Merge remote-tracking branch 'goog/master-chromium' into 'goog/master'

bug: 6906025
diff --git a/Android.mk b/Android.mk
index 018f7d2..26ad2bd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -16,7 +16,8 @@
 #       needed since your sources and libskia have been built with SK_DEBUG.
 # (2) You're building libskia in debug mode.
 #   (a) RECOMMENDED: You can build the entire system in debug mode. Do this by
-#       updating your buildspec.mk to include TARGET_BUILD_TYPE=debug
+#       updating your build/config.mk to include -DSK_DEBUG on the line that
+#       defines COMMON_GLOBAL_CFLAGS
 #   (b) You can update all the users of libskia to define SK_DEBUG when they are
 #       building their sources.
 #
@@ -51,20 +52,36 @@
 	LOCAL_CFLAGS += -D__ARM_HAVE_NEON
 endif
 
-# special checks for alpha == 0 and alpha == 255 in S32A_Opaque_BlitRow32
-# procedures (C and assembly) seriously improve skia performance
-LOCAL_CFLAGS += -DTEST_SRC_ALPHA
-
 # using freetype's embolden allows us to adjust fake bold settings at
 # draw-time, at which point we know which SkTypeface is being drawn
 LOCAL_CFLAGS += -DSK_USE_FREETYPE_EMBOLDEN
 
+#Android provides at least FreeType 2.4.0 at runtime.
+LOCAL_CFLAGS += -DSK_FONTHOST_FREETYPE_RUNTIME_VERSION=0x020400
+
+#Skia should not use dlopen on Android.
+LOCAL_CFLAGS += -DSK_CAN_USE_DLOPEN=0
+
+# used for testing
+#LOCAL_CFLAGS += -g -O0
+
+ifeq ($(NO_FALLBACK_FONT),true)
+	LOCAL_CFLAGS += -DNO_FALLBACK_FONT
+endif
+
+
+
 LOCAL_SRC_FILES:= \
 	src/core/Sk64.cpp \
+	src/core/SkAnnotation.cpp \
 	src/core/SkAAClip.cpp \
 	src/core/SkAdvancedTypefaceMetrics.cpp \
 	src/core/SkAlphaRuns.cpp \
+	src/core/SkBBoxHierarchy.cpp \
+	src/core/SkBBoxRecord.cpp \
+	src/core/SkBBoxHierarchyRecord.cpp \
 	src/core/SkBitmap.cpp \
+	src/core/SkBitmapHeap.cpp \
 	src/core/SkBitmapProcShader.cpp \
 	src/core/SkBitmapProcState.cpp \
 	src/core/SkBitmapProcState_matrixProcs.cpp \
@@ -84,13 +101,11 @@
 	src/core/SkBuffer.cpp \
 	src/core/SkCanvas.cpp \
 	src/core/SkChunkAlloc.cpp \
-	src/core/SkClampRange.cpp \
 	src/core/SkClipStack.cpp \
 	src/core/SkColor.cpp \
 	src/core/SkColorFilter.cpp \
 	src/core/SkColorTable.cpp \
 	src/core/SkComposeShader.cpp \
-	src/core/SkConcaveToTriangles.cpp \
 	src/core/SkConfig8888.cpp \
 	src/core/SkCordic.cpp \
 	src/core/SkCubicClipper.cpp \
@@ -101,28 +116,36 @@
 	src/core/SkDeviceProfile.cpp \
 	src/core/SkDither.cpp \
 	src/core/SkDraw.cpp \
-  src/core/SkEdgeBuilder.cpp \
+	src/core/SkEdgeBuilder.cpp \
 	src/core/SkEdgeClipper.cpp \
-  src/core/SkEdge.cpp \
+	src/core/SkEdge.cpp \
 	src/core/SkFilterProc.cpp \
 	src/core/SkFlattenable.cpp \
+	src/core/SkFlattenableBuffers.cpp \
 	src/core/SkFloat.cpp \
 	src/core/SkFloatBits.cpp \
+	src/core/SkFontDescriptor.cpp \
 	src/core/SkFontHost.cpp \
 	src/core/SkGeometry.cpp \
 	src/core/SkGlyphCache.cpp \
 	src/core/SkGraphics.cpp \
+	src/core/SkInstCnt.cpp \
+	src/core/SkImageFilter.cpp \
 	src/core/SkLanguage.cpp \
 	src/core/SkLineClipper.cpp \
 	src/core/SkMallocPixelRef.cpp \
 	src/core/SkMask.cpp \
 	src/core/SkMaskFilter.cpp \
+	src/core/SkMaskGamma.cpp \
 	src/core/SkMath.cpp \
 	src/core/SkMatrix.cpp \
 	src/core/SkMetaData.cpp \
 	src/core/SkMMapStream.cpp \
+	src/core/SkOrderedReadBuffer.cpp \
+	src/core/SkOrderedWriteBuffer.cpp \
 	src/core/SkPackBits.cpp \
 	src/core/SkPaint.cpp \
+	src/core/SkPaintPriv.cpp \
 	src/core/SkPath.cpp \
 	src/core/SkPathEffect.cpp \
 	src/core/SkPathHeap.cpp \
@@ -131,6 +154,7 @@
 	src/core/SkPictureFlat.cpp \
 	src/core/SkPicturePlayback.cpp \
 	src/core/SkPictureRecord.cpp \
+	src/core/SkPictureStateTree.cpp \
 	src/core/SkPixelRef.cpp \
 	src/core/SkPoint.cpp \
 	src/core/SkProcSpriteBlitter.cpp \
@@ -139,24 +163,31 @@
 	src/core/SkRasterClip.cpp \
 	src/core/SkRasterizer.cpp \
 	src/core/SkRect.cpp \
+	src/core/SkRefCnt.cpp \
 	src/core/SkRefDict.cpp \
 	src/core/SkRegion.cpp \
 	src/core/SkRegion_path.cpp \
-  src/core/SkScalar.cpp \
-  src/core/SkScalerContext.cpp \
+	src/core/SkRRect.cpp \
+	src/core/SkRTree.cpp \
+	src/core/SkScalar.cpp \
+	src/core/SkScalerContext.cpp \
 	src/core/SkScan.cpp \
 	src/core/SkScan_AntiPath.cpp \
 	src/core/SkScan_Antihair.cpp \
 	src/core/SkScan_Hairline.cpp \
 	src/core/SkScan_Path.cpp \
 	src/core/SkShader.cpp \
-	src/core/SkShape.cpp \
 	src/core/SkSpriteBlitter_ARGB32.cpp \
 	src/core/SkSpriteBlitter_RGB16.cpp \
 	src/core/SkStream.cpp \
 	src/core/SkString.cpp \
+	src/core/SkStringUtils.cpp \
 	src/core/SkStroke.cpp \
+	src/core/SkStrokeRec.cpp \
 	src/core/SkStrokerPriv.cpp \
+	src/core/SkTileGrid.cpp \
+	src/core/SkTileGridPicture.cpp \
+	src/core/SkTLS.cpp \
 	src/core/SkTSearch.cpp \
 	src/core/SkTypeface.cpp \
 	src/core/SkTypefaceCache.cpp \
@@ -169,38 +200,65 @@
 	src/effects/Sk2DPathEffect.cpp \
 	src/effects/SkAvoidXfermode.cpp \
 	src/effects/SkArithmeticMode.cpp \
-	src/effects/SkBitmapCache.cpp \
+	src/effects/SkBicubicImageFilter.cpp \
+	src/effects/SkBitmapSource.cpp \
+	src/effects/SkBlendImageFilter.cpp \
 	src/effects/SkBlurDrawLooper.cpp \
 	src/effects/SkBlurImageFilter.cpp \
 	src/effects/SkBlurMask.cpp \
 	src/effects/SkBlurMaskFilter.cpp \
+	src/effects/SkColorFilterImageFilter.cpp \
 	src/effects/SkColorFilters.cpp \
+	src/effects/SkColorMatrix.cpp \
 	src/effects/SkColorMatrixFilter.cpp \
 	src/effects/SkCornerPathEffect.cpp \
 	src/effects/SkDashPathEffect.cpp \
 	src/effects/SkDiscretePathEffect.cpp \
-	src/effects/SkEffects.cpp \
+	src/effects/SkDisplacementMapEffect.cpp \
 	src/effects/SkEmbossMask.cpp \
 	src/effects/SkEmbossMaskFilter.cpp \
-	src/effects/SkGradientShader.cpp \
-	src/effects/SkGroupShape.cpp \
+	src/effects/SkImageFilterUtils.cpp \
 	src/effects/SkKernel33MaskFilter.cpp \
 	src/effects/SkLayerDrawLooper.cpp \
 	src/effects/SkLayerRasterizer.cpp \
+	src/effects/SkLightingImageFilter.cpp \
+	src/effects/SkMagnifierImageFilter.cpp \
+	src/effects/SkMatrixConvolutionImageFilter.cpp \
+	src/effects/SkMergeImageFilter.cpp \
 	src/effects/SkMorphologyImageFilter.cpp \
+	src/effects/SkOffsetImageFilter.cpp \
 	src/effects/SkPaintFlagsDrawFilter.cpp \
 	src/effects/SkPixelXorXfermode.cpp \
 	src/effects/SkPorterDuff.cpp \
-	src/effects/SkRectShape.cpp \
+	src/effects/SkSingleInputImageFilter.cpp \
+	src/effects/SkStippleMaskFilter.cpp \
 	src/effects/SkTableColorFilter.cpp \
-  src/effects/SkTableMaskFilter.cpp \
-  src/effects/SkTestImageFilters.cpp \
+	src/effects/SkTableMaskFilter.cpp \
+	src/effects/SkTestImageFilters.cpp \
 	src/effects/SkTransparentShader.cpp \
+	src/effects/gradients/SkBitmapCache.cpp \
+	src/effects/gradients/SkClampRange.cpp \
+	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/SkSweepGradient.cpp \
+	src/image/SkDataPixelRef.cpp \
+	src/image/SkImage.cpp \
+	src/image/SkImagePriv.cpp \
+	src/image/SkImage_Codec.cpp \
+	src/image/SkImage_Picture.cpp \
+	src/image/SkImage_Raster.cpp \
+	src/image/SkSurface.cpp \
+	src/image/SkSurface_Picture.cpp \
+	src/image/SkSurface_Raster.cpp \
 	src/images/bmpdecoderhelper.cpp \
+	src/images/SkBitmapFactory.cpp \
 	src/images/SkBitmapRegionDecoder.cpp \
-	src/images/SkCreateRLEPixelRef.cpp \
 	src/images/SkFDStream.cpp \
 	src/images/SkFlipPixelRef.cpp \
+	src/images/SkImages.cpp \
 	src/images/SkImageDecoder.cpp \
 	src/images/SkImageDecoder_Factory.cpp \
 	src/images/SkImageDecoder_libbmp.cpp \
@@ -214,36 +272,40 @@
 	src/images/SkImageEncoder_Factory.cpp \
 	src/images/SkImageRef.cpp \
 	src/images/SkImageRefPool.cpp \
+	src/images/SkImageRef_ashmem.cpp \
 	src/images/SkImageRef_GlobalPool.cpp \
 	src/images/SkJpegUtility.cpp \
 	src/images/SkMovie.cpp \
 	src/images/SkMovie_gif.cpp \
 	src/images/SkPageFlipper.cpp \
 	src/images/SkScaledBitmapSampler.cpp \
+	src/pipe/SkGPipeRead.cpp \
+	src/pipe/SkGPipeWrite.cpp \
 	src/ports/FontHostConfiguration_android.cpp \
 	src/ports/SkDebug_android.cpp \
 	src/ports/SkGlobalInitialization_default.cpp \
 	src/ports/SkFontHost_FreeType.cpp \
+	src/ports/SkFontHost_FreeType_common.cpp \
 	src/ports/SkFontHost_sandbox_none.cpp	\
 	src/ports/SkFontHost_android.cpp \
-	src/ports/SkFontHost_gamma.cpp \
 	src/ports/SkFontHost_tables.cpp \
-	src/ports/SkImageRef_ashmem.cpp \
 	src/ports/SkMemory_malloc.cpp \
 	src/ports/SkOSFile_stdio.cpp \
 	src/ports/SkThread_pthread.cpp \
 	src/ports/SkTime_Unix.cpp \
 	src/utils/SkBase64.cpp \
+	src/utils/SkBitmapTransformer.cpp \
+	src/utils/SkBitSet.cpp \
 	src/utils/SkBoundaryPatch.cpp \
 	src/utils/SkCamera.cpp \
-	src/utils/SkColorMatrix.cpp \
-  src/utils/SkCubicInterval.cpp \
+	src/utils/SkCubicInterval.cpp \
 	src/utils/SkCullPoints.cpp \
 	src/utils/SkDeferredCanvas.cpp \
 	src/utils/SkDumpCanvas.cpp \
 	src/utils/SkInterpolator.cpp \
 	src/utils/SkLayer.cpp \
 	src/utils/SkMatrix44.cpp \
+	src/utils/SkMD5.cpp \
 	src/utils/SkMeshUtils.cpp \
 	src/utils/SkNinePatch.cpp \
 	src/utils/SkNWayCanvas.cpp \
@@ -251,23 +313,128 @@
 	src/utils/SkParse.cpp \
 	src/utils/SkParseColor.cpp \
 	src/utils/SkParsePath.cpp \
+	src/utils/SkPictureUtils.cpp \
 	src/utils/SkProxyCanvas.cpp \
-	src/utils/SkSfntUtils.cpp \
+	src/utils/SkSHA1.cpp \
+	src/utils/SkRTConf.cpp \
+	src/utils/SkThreadUtils_pthread.cpp \
+	src/utils/SkThreadUtils_pthread_other.cpp \
 	src/utils/SkUnitMappers.cpp
 
+#	src/utils/SkBitmapChecksummer.cpp \
+#	src/utils/SkCityHash.cpp \
+
+# maps to the 'skgr' gyp target
+LOCAL_SRC_FILES += \
+	src/gpu/SkGpuDevice.cpp \
+	src/gpu/SkGr.cpp \
+	src/gpu/SkGrFontScaler.cpp \
+	src/gpu/SkGrPixelRef.cpp \
+	src/gpu/SkGrTexturePixelRef.cpp \
+	src/gpu/gl/SkGLContext.cpp \
+	src/gpu/gl/SkNullGLContext.cpp \
+	src/gpu/gl/debug/SkDebugGLContext.cpp \
+	src/gpu/gl/android/SkNativeGLContext_android.cpp
+
+# maps to the 'gr' gyp target
+LOCAL_SRC_FILES += \
+	src/gpu/GrAAHairLinePathRenderer.cpp \
+	src/gpu/GrAAConvexPathRenderer.cpp \
+	src/gpu/GrAARectRenderer.cpp \
+	src/gpu/GrAddPathRenderers_default.cpp \
+	src/gpu/GrAllocPool.cpp \
+	src/gpu/GrAtlas.cpp \
+	src/gpu/GrBufferAllocPool.cpp \
+	src/gpu/GrCacheID.cpp \
+	src/gpu/GrClipData.cpp \
+	src/gpu/GrContext.cpp \
+	src/gpu/GrDefaultPathRenderer.cpp \
+	src/gpu/GrDrawState.cpp \
+	src/gpu/GrDrawTarget.cpp \
+	src/gpu/GrEffect.cpp \
+	src/gpu/GrGeometryBuffer.cpp \
+	src/gpu/GrClipMaskCache.cpp \
+	src/gpu/GrClipMaskManager.cpp \
+	src/gpu/GrGpu.cpp \
+	src/gpu/GrGpuFactory.cpp \
+	src/gpu/GrInOrderDrawBuffer.cpp \
+	src/gpu/GrMemory.cpp \
+	src/gpu/GrMemoryPool.cpp \
+	src/gpu/GrPath.cpp \
+	src/gpu/GrPathRendererChain.cpp \
+	src/gpu/GrPathRenderer.cpp \
+	src/gpu/GrPathUtils.cpp \
+	src/gpu/GrRectanizer.cpp \
+	src/gpu/GrReducedClip.cpp \
+	src/gpu/GrRenderTarget.cpp \
+	src/gpu/GrResource.cpp \
+	src/gpu/GrResourceCache.cpp \
+	src/gpu/GrStencil.cpp \
+	src/gpu/GrStencilAndCoverPathRenderer.cpp \
+	src/gpu/GrStencilBuffer.cpp \
+	src/gpu/GrSWMaskHelper.cpp \
+	src/gpu/GrSoftwarePathRenderer.cpp \
+	src/gpu/GrSurface.cpp \
+	src/gpu/GrTextContext.cpp \
+	src/gpu/GrTextStrike.cpp \
+	src/gpu/GrTexture.cpp \
+	src/gpu/GrTextureAccess.cpp \
+	src/gpu/gr_unittests.cpp \
+	src/gpu/effects/GrTextureStripAtlas.cpp \
+	src/gpu/effects/GrConfigConversionEffect.cpp \
+	src/gpu/effects/GrConvolutionEffect.cpp \
+	src/gpu/effects/GrSimpleTextureEffect.cpp \
+	src/gpu/effects/GrSingleTextureEffect.cpp \
+	src/gpu/effects/GrTextureDomainEffect.cpp \
+	src/gpu/gl/GrGLCaps.cpp \
+	src/gpu/gl/GrGLContextInfo.cpp \
+	src/gpu/gl/GrGLCreateNullInterface.cpp \
+	src/gpu/gl/GrGLDefaultInterface_native.cpp \
+	src/gpu/gl/GrGLEffect.cpp \
+	src/gpu/gl/GrGLEffectMatrix.cpp \
+	src/gpu/gl/GrGLIndexBuffer.cpp \
+	src/gpu/gl/GrGLInterface.cpp \
+	src/gpu/gl/GrGLPath.cpp \
+	src/gpu/gl/GrGLProgram.cpp \
+	src/gpu/gl/GrGLRenderTarget.cpp \
+	src/gpu/gl/GrGLShaderBuilder.cpp \
+	src/gpu/gl/GrGLSL.cpp \
+	src/gpu/gl/GrGLStencilBuffer.cpp \
+	src/gpu/gl/GrGLTexture.cpp \
+	src/gpu/gl/GrGLUtil.cpp \
+	src/gpu/gl/GrGLUniformManager.cpp \
+	src/gpu/gl/GrGLVertexBuffer.cpp \
+	src/gpu/gl/GrGpuGL.cpp \
+	src/gpu/gl/GrGpuGL_program.cpp \
+	src/gpu/gl/debug/GrGLCreateDebugInterface.cpp \
+	src/gpu/gl/debug/GrBufferObj.cpp \
+	src/gpu/gl/debug/GrTextureObj.cpp \
+	src/gpu/gl/debug/GrTextureUnitObj.cpp \
+	src/gpu/gl/debug/GrFrameBufferObj.cpp \
+	src/gpu/gl/debug/GrShaderObj.cpp \
+	src/gpu/gl/debug/GrProgramObj.cpp \
+	src/gpu/gl/debug/GrDebugGL.cpp \
+	src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
+
+
 ifeq ($(TARGET_ARCH),arm)
 
 ifeq ($(ARCH_ARM_HAVE_NEON),true)
 LOCAL_SRC_FILES += \
 	src/opts/memset16_neon.S \
-	src/opts/memset32_neon.S
+	src/opts/memset32_neon.S \
+    src/opts/SkBitmapProcState_arm_neon.cpp \
+    src/opts/SkBitmapProcState_matrixProcs_neon.cpp \
+    src/opts/SkBlitRow_opts_arm_neon.cpp
 endif
 
 LOCAL_SRC_FILES += \
+    src/core/SkUtilsArm.cpp \
 	src/opts/opts_check_arm.cpp \
 	src/opts/memset.arm.S \
 	src/opts/SkBitmapProcState_opts_arm.cpp \
 	src/opts/SkBlitRow_opts_arm.cpp
+
 else
 LOCAL_SRC_FILES += \
 	src/opts/SkBlitRow_opts_none.cpp \
@@ -275,6 +442,7 @@
 	src/opts/SkUtils_opts_none.cpp
 endif
 
+
 # these are for emoji support, needed by webkit
 LOCAL_SRC_FILES += \
 	emoji/EmojiFont.cpp
@@ -285,7 +453,10 @@
 	libjpeg \
 	libutils \
 	libz \
-	libexpat
+	libexpat \
+	libutils \
+	libEGL \
+	libGLESv2
 
 LOCAL_STATIC_LIBRARIES := \
 	libft2 \
@@ -294,27 +465,38 @@
 	libwebp-decode \
 	libwebp-encode
 
-LOCAL_C_INCLUDES += \
-	$(LOCAL_PATH)/src/core \
+LOCAL_C_INCLUDES := \
 	$(LOCAL_PATH)/include/core \
 	$(LOCAL_PATH)/include/config \
 	$(LOCAL_PATH)/include/effects \
+	$(LOCAL_PATH)/include/gpu \
 	$(LOCAL_PATH)/include/images \
+	$(LOCAL_PATH)/include/pipe \
 	$(LOCAL_PATH)/include/ports \
 	$(LOCAL_PATH)/include/utils \
 	$(LOCAL_PATH)/include/xml \
+	$(LOCAL_PATH)/src/core \
+	$(LOCAL_PATH)/src/gpu \
+	$(LOCAL_PATH)/src/image \
+	$(LOCAL_PATH)/src/utils \
 	external/freetype/include \
 	external/zlib \
 	external/libpng \
 	external/giflib \
 	external/jpeg \
 	external/webp/include \
+	frameworks/base/opengl/include \
 	frameworks/opt/emoji \
 	external/expat/lib
 
-ifeq ($(NO_FALLBACK_FONT),true)
-	LOCAL_CFLAGS += -DNO_FALLBACK_FONT
-endif
+LOCAL_EXPORT_C_INCLUDES := \
+	$(LOCAL_PATH)/include/core \
+	$(LOCAL_PATH)/include/effects \
+	$(LOCAL_PATH)/include/gpu \
+	$(LOCAL_PATH)/include/images \
+	$(LOCAL_PATH)/include/pipe \
+	$(LOCAL_PATH)/include/ports \
+	$(LOCAL_PATH)/include/utils
 
 LOCAL_LDLIBS += -lpthread
 
@@ -323,144 +505,6 @@
 include $(BUILD_SHARED_LIBRARY)
 
 #############################################################
-# Build the skia gpu (ganesh) library
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_ARM_MODE := arm
-
-ifneq ($(ARCH_ARM_HAVE_VFP),true)
-       LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT
-endif
-
-ifeq ($(ARCH_ARM_HAVE_NEON),true)
-       LOCAL_CFLAGS += -DGR_ANDROID_BUILD=1
-endif
-
-LOCAL_SRC_FILES:= \
-  src/gpu/GrPrintf_skia.cpp \
-  src/gpu/SkGpuCanvas.cpp \
-  src/gpu/SkGpuDevice.cpp \
-  src/gpu/SkGr.cpp \
-  src/gpu/SkGrFontScaler.cpp \
-  src/gpu/SkGrTexturePixelRef.cpp \
-  src/gpu/android/SkNativeGLContext_android.cpp \
-  src/gpu/gl/SkGLContext.cpp \
-  src/gpu/gl/SkNullGLContext.cpp
-
-LOCAL_SRC_FILES += \
-  src/gpu/GrAAHairLinePathRenderer.cpp \
-  src/gpu/GrAAConvexPathRenderer.cpp \
-  src/gpu/GrAddPathRenderers_default.cpp \
-  src/gpu/GrAllocPool.cpp \
-  src/gpu/GrAtlas.cpp \
-  src/gpu/GrBufferAllocPool.cpp \
-  src/gpu/GrClip.cpp \
-  src/gpu/GrContext.cpp \
-  src/gpu/GrDefaultPathRenderer.cpp \
-  src/gpu/GrDrawTarget.cpp \
-  src/gpu/GrGpu.cpp \
-  src/gpu/GrGpuFactory.cpp \
-  src/gpu/GrInOrderDrawBuffer.cpp \
-  src/gpu/GrMatrix.cpp \
-  src/gpu/GrMemory.cpp \
-  src/gpu/GrPathRendererChain.cpp \
-  src/gpu/GrPathRenderer.cpp \
-  src/gpu/GrPathUtils.cpp \
-  src/gpu/GrRectanizer.cpp \
-  src/gpu/GrRenderTarget.cpp \
-  src/gpu/GrResource.cpp \
-  src/gpu/GrResourceCache.cpp \
-  src/gpu/GrStencil.cpp \
-  src/gpu/GrStencilBuffer.cpp \
-  src/gpu/GrTesselatedPathRenderer.cpp \
-  src/gpu/GrTextContext.cpp \
-  src/gpu/GrTextStrike.cpp \
-  src/gpu/GrTexture.cpp \
-  src/gpu/gr_unittests.cpp \
-  src/gpu/android/GrGLCreateNativeInterface_android.cpp
-
-LOCAL_SRC_FILES += \
-  src/gpu/gl/GrGLCaps.cpp \
-  src/gpu/gl/GrGLContextInfo.cpp \
-  src/gpu/gl/GrGLCreateNullInterface.cpp \
-  src/gpu/gl/GrGLDefaultInterface_native.cpp \
-  src/gpu/gl/GrGLIndexBuffer.cpp \
-  src/gpu/gl/GrGLInterface.cpp \
-  src/gpu/gl/GrGLProgram.cpp \
-  src/gpu/gl/GrGLRenderTarget.cpp \
-  src/gpu/gl/GrGLSL.cpp \
-  src/gpu/gl/GrGLStencilBuffer.cpp \
-  src/gpu/gl/GrGLTexture.cpp \
-  src/gpu/gl/GrGLUtil.cpp \
-  src/gpu/gl/GrGLVertexBuffer.cpp \
-  src/gpu/gl/GrGpuGL.cpp \
-  src/gpu/gl/GrGpuGLShaders.cpp
-  
-LOCAL_STATIC_LIBRARIES := libskiatess
-LOCAL_SHARED_LIBRARIES := \
-  libcutils \
-  libutils \
-  libskia \
-  libEGL \
-  libGLESv2
-
-LOCAL_C_INCLUDES += \
-  $(LOCAL_PATH)/include/core \
-  $(LOCAL_PATH)/include/config \
-  $(LOCAL_PATH)/include/gpu \
-  $(LOCAL_PATH)/src/core \
-  $(LOCAL_PATH)/src/gpu \
-  $(LOCAL_PATH)/third_party/glu \
-  frameworks/base/opengl/include
-
-LOCAL_LDLIBS += -lpthread
-
-LOCAL_MODULE:= libskiagpu
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_STATIC_LIBRARY)
-
-#############################################################
-# Build the skia gpu (ganesh) library
-#
-
-include $(CLEAR_VARS)
-
-LOCAL_ARM_MODE := arm
-
-LOCAL_SRC_FILES := \
-  third_party/glu/libtess/dict.c \
-  third_party/glu/libtess/geom.c \
-  third_party/glu/libtess/memalloc.c \
-  third_party/glu/libtess/mesh.c \
-  third_party/glu/libtess/normal.c \
-  third_party/glu/libtess/priorityq.c \
-  third_party/glu/libtess/render.c \
-  third_party/glu/libtess/sweep.c \
-  third_party/glu/libtess/tess.c \
-  third_party/glu/libtess/tessmono.c
-
-LOCAL_SHARED_LIBRARIES := \
-  libcutils \
-  libutils \
-  libEGL \
-  libGLESv2
-
-LOCAL_C_INCLUDES += \
-  $(LOCAL_PATH)/third_party/glu \
-  $(LOCAL_PATH)/third_party/glu/libtess \
-  frameworks/base/opengl/include
-
-LOCAL_LDLIBS += -lpthread
-
-LOCAL_MODULE:= libskiatess
-LOCAL_MODULE_TAGS := optional
-
-include $(BUILD_STATIC_LIBRARY)
-
-#############################################################
 # Build the skia tools
 #
 
@@ -468,7 +512,7 @@
 include $(BASE_PATH)/bench/Android.mk
 
 # golden-master (fidelity / regression test)
-include $(BASE_PATH)/gm/Android.mk
+#include $(BASE_PATH)/gm/Android.mk
 
 # unit-tests
 include $(BASE_PATH)/tests/Android.mk
diff --git a/DEPS b/DEPS
new file mode 100644
index 0000000..408aab1
--- /dev/null
+++ b/DEPS
@@ -0,0 +1,26 @@
+use_relative_paths = True
+
+# Dependencies on outside packages, as needed for developers/bots that use
+# "gclient" instead of raw SVN access.
+#
+# For now, this must be maintained in parallel with "SVN externals"
+# dependencies for developers who use raw SVN instead of "gclient".
+# See third_party/externals/README
+#
+deps = {
+  "third_party/externals/angle" : "http://angleproject.googlecode.com/svn/trunk@1268",
+  "third_party/externals/cityhash" : "http://cityhash.googlecode.com/svn/trunk@11",
+  "third_party/externals/freetype" : "https://android.googlesource.com/platform/external/freetype.git",
+  "third_party/externals/gyp" : "http://gyp.googlecode.com/svn/trunk@1563",
+  "third_party/externals/libjpeg" : "http://src.chromium.org/svn/trunk/src/third_party/libjpeg@125399",
+  "third_party/externals/jsoncpp" : "http://jsoncpp.svn.sourceforge.net/svnroot/jsoncpp/trunk/jsoncpp@248",
+  "third_party/externals/jsoncpp-chromium" : "http://src.chromium.org/svn/trunk/src/third_party/jsoncpp@125399",
+}
+
+#hooks = [
+#  {
+#    # A change to a .gyp, .gypi, or to GYP itself should run the generator.
+#    "pattern": ".",
+#    "action": ["python", "trunk/gyp_skia"],
+#  },
+#]
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
new file mode 100644
index 0000000..ddac74a
--- /dev/null
+++ b/PRESUBMIT.py
@@ -0,0 +1,93 @@
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""Top-level presubmit script for Skia.
+
+See http://dev.chromium.org/developers/how-tos/depottools/presubmit-scripts
+for more details about the presubmit API built into gcl.
+"""
+
+
+def _CheckChangeHasEol(input_api, output_api, source_file_filter=None):
+  """Checks that files end with atleast one \n (LF)."""
+  eof_files = []
+  for f in input_api.AffectedSourceFiles(source_file_filter):
+    contents = input_api.ReadFile(f, 'rb')
+    # Check that the file ends in atleast one newline character.
+    if len(contents) > 1 and contents[-1:] != '\n':
+      eof_files.append(f.LocalPath())
+
+  if eof_files:
+    return [output_api.PresubmitPromptWarning(
+      'These files should end in a newline character:',
+      items=eof_files)]
+  return []
+
+
+def _CommonChecks(input_api, output_api):
+  """Presubmit checks common to upload and commit."""
+  results = []
+  sources = lambda x: (x.LocalPath().endswith('.h') or
+                       x.LocalPath().endswith('.gypi') or
+                       x.LocalPath().endswith('.gyp') or
+                       x.LocalPath().endswith('.py') or
+                       x.LocalPath().endswith('.sh') or
+                       x.LocalPath().endswith('.cpp'))
+  results.extend(
+      _CheckChangeHasEol(
+          input_api, output_api, source_file_filter=sources))
+  return results
+
+
+def CheckChangeOnUpload(input_api, output_api):
+  """Presubmit checks for the change on upload.
+
+  The following are the presubmit checks:
+  * Check change has one and only one EOL.
+  """
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  return results
+
+
+def _CheckTreeStatus(input_api, output_api, json_url):
+  """Check whether to allow commit.
+
+  Args:
+    input_api: input related apis.
+    output_api: output related apis.
+    json_url: url to download json style status.
+  """
+  tree_status_results = input_api.canned_checks.CheckTreeIsOpen(
+      input_api, output_api, json_url=json_url)
+  if not tree_status_results:
+    # Check for caution state only if tree is not closed.
+    connection = input_api.urllib2.urlopen(json_url)
+    status = input_api.json.loads(connection.read())
+    connection.close()
+    if 'caution' in status['message'].lower():
+      short_text = 'Tree state is: ' + status['general_state']
+      long_text = status['message'] + '\n' + json_url
+      tree_status_results.append(
+          output_api.PresubmitPromptWarning(
+              message=short_text, long_text=long_text))
+  return tree_status_results
+
+
+def CheckChangeOnCommit(input_api, output_api):
+  """Presubmit checks for the change on commit.
+
+  The following are the presubmit checks:
+  * Check change has one and only one EOL.
+  * Ensures that the Skia tree is open in
+    http://skia-tree-status.appspot.com/. Shows a warning if it is in 'Caution'
+    state and an error if it is in 'Closed' state.
+  """
+  results = []
+  results.extend(_CommonChecks(input_api, output_api))
+  results.extend(
+      _CheckTreeStatus(input_api, output_api, json_url=(
+          'http://skia-tree-status.appspot.com/banner-status?format=json')))
+  return results
diff --git a/android_sample/SampleApp/Android.mk b/android_sample/SampleApp/Android.mk
deleted file mode 100644
index 6d62c35..0000000
--- a/android_sample/SampleApp/Android.mk
+++ /dev/null
@@ -1,71 +0,0 @@
-######################################
-# Build the app.
-######################################
-
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := \
-        $(call all-java-files-under, src)
-
-LOCAL_PACKAGE_NAME := SampleApp
-
-LOCAL_JNI_SHARED_LIBRARIES := libskia-sample
-
-include $(BUILD_PACKAGE)
-
-######################################
-# Build the shared library.
-######################################
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_C_INCLUDES += \
-    external/skia/include/core \
-    external/skia/include/config \
-    external/skia/include/effects \
-    external/skia/include/images \
-    external/skia/include/utils \
-    external/skia/include/utils/android \
-    external/skia/include/views \
-    external/skia/samplecode \
-    external/skia/include/xml \
-    external/skia/include/gpu \
-    external/skia/src/core \
-    external/skia/gpu/include \
-    frameworks/base/core/jni/android/graphics \
-    frameworks/base/native/include/android \
-    $(LOCAL_PATH)/jni
-
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    libutils \
-    libskia \
-    libandroid_runtime \
-    libGLESv2
-
-LOCAL_STATIC_LIBRARIES := \
-    libskiagpu
-
-LOCAL_PRELINK_MODULE := false
-
-LOCAL_MODULE := libskia-sample
-
-LOCAL_SRC_FILES := \
-    ../../src/ports/SkXMLParser_empty.cpp \
-    jni/sample-jni.cpp
-
-include external/skia/src/views/views_files.mk
-LOCAL_SRC_FILES += $(addprefix ../../src/views/, $(SOURCE))
-
-include external/skia/src/xml/xml_files.mk
-LOCAL_SRC_FILES += $(addprefix ../../src/xml/, $(SOURCE))
-
-include external/skia/samplecode/samplecode_files.mk
-LOCAL_SRC_FILES += $(addprefix ../../samplecode/, $(SOURCE))
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/android_sample/SampleApp/AndroidManifest.xml b/android_sample/SampleApp/AndroidManifest.xml
deleted file mode 100644
index e323246..0000000
--- a/android_sample/SampleApp/AndroidManifest.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 Skia
-
-     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.
--->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="com.skia.sampleapp"
-      android:versionCode="1"
-      android:versionName="1.0">
-    <uses-sdk android:minSdkVersion="3" />
-    <application android:label="@string/app_name"
-                 android:debuggable="true">
-        <activity android:name=".SampleApp"
-                  android:theme="@android:style/Theme.Holo.NoActionBar"
-                  android:label="@string/app_name">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
-            </intent-filter>
-        </activity>
-    </application>
-</manifest> 
diff --git a/android_sample/SampleApp/README.txt b/android_sample/SampleApp/README.txt
deleted file mode 100644
index a7d8e54..0000000
--- a/android_sample/SampleApp/README.txt
+++ /dev/null
@@ -1,25 +0,0 @@
-Building the sample app for Android using an Android tree:
-
-Copy this folder into an Android tree in packages/apps. In addition to jni,
-res, and src, there needs to be a fourth folder named "skia_extra".  This
-will include the skia files which are not part of an Android checkout. It
-should have three folders: include, samplecode, and src.
-
-skia/trunk/include/views -> skia_extra/include/views
-skia/trunk/include/xml -> skia_extra/include/xml
-
-skia/trunk/samplecode -> skia_extra/samplecode
-
-skia/trunk/src/views -> skia_extra/src/views
-skia/trunk/src/ports/SkXMLParser_empty.cpp -> skia_extra/src/ports/
-skia/trunk/src/xml -> skia_extra/src/xml
-
-skia/trunk/include/utils/android/AndroidKeyToSkKey.h -> jni/
-
-From packages/apps/SampleApp, type "mm" to build, and install the
-resulting apk.
-
-(It may be necessary to remove samples that do not build from
-skia_extra/samplecode/samplecode_files.mk)
-
-TODO: Instructions for building from SDK/NDK
diff --git a/android_sample/SampleApp/jni/sample-jni.cpp b/android_sample/SampleApp/jni/sample-jni.cpp
deleted file mode 100644
index 529047c..0000000
--- a/android_sample/SampleApp/jni/sample-jni.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2011 Skia
- *
- * 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.
- *
- */
-
-#include <jni.h>
-
-#include "SkCanvas.h"
-#include "GraphicsJNI.h"
-#include "SkEvent.h"
-#include "SkWindow.h"
-#include "SkApplication.h"
-#include "utils/android/AndroidKeyToSkKey.h"
-
-///////////////////////////////////////////
-///////////////// Globals /////////////////
-///////////////////////////////////////////
-
-struct ActivityGlue {
-    JNIEnv* m_env;
-    jweak m_obj;
-    jmethodID m_setTitle;
-    ActivityGlue() {
-        m_env = NULL;
-        m_obj = NULL;
-        m_setTitle = NULL;
-    }
-} gActivityGlue;
-
-struct WindowGlue {
-    jweak m_obj;
-    jmethodID m_inval;
-    WindowGlue() {
-        m_obj = NULL;
-        m_inval = NULL;
-    }
-} gWindowGlue;
-
-SkOSWindow* gWindow;
-
-///////////////////////////////////////////
-///////////// SkOSWindow impl /////////////
-///////////////////////////////////////////
-
-void SkOSWindow::onSetTitle(const char title[])
-{
-    if (gActivityGlue.m_env) {
-        JNIEnv* env = gActivityGlue.m_env;
-        jstring string = env->NewStringUTF(title);
-        env->CallVoidMethod(gActivityGlue.m_obj, gActivityGlue.m_setTitle,
-                string);
-        env->DeleteLocalRef(string);
-    }
-}
-
-void SkOSWindow::onHandleInval(const SkIRect& rect)
-{
-    if (!gActivityGlue.m_env || !gWindowGlue.m_inval || !gWindowGlue.m_obj) {
-        return;
-    }
-    gActivityGlue.m_env->CallVoidMethod(gWindowGlue.m_obj, gWindowGlue.m_inval,
-            rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
-}
-
-///////////////////////////////////////////
-/////////////// SkEvent impl //////////////
-///////////////////////////////////////////
-
-void SkEvent::SignalQueueTimer(SkMSec) {}
-
-void SkEvent::SignalNonEmptyQueue() {}
-
-///////////////////////////////////////////
-////////////////// JNI ////////////////////
-///////////////////////////////////////////
-
-static jmethodID GetJMethod(JNIEnv* env, jclass clazz, const char name[],
-        const char signature[])
-{
-    jmethodID m = env->GetMethodID(clazz, name, signature);
-    if (!m) SkDebugf("Could not find Java method %s\n", name);
-    return m;
-}
-
-extern "C" {
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas(
-        JNIEnv* env, jobject thiz, jobject jcanvas);
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(
-        JNIEnv* env, jobject thiz);
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(
-        JNIEnv* env, jobject thiz);
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(
-        JNIEnv* env, jobject thiz, jint w, jint h);
-JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown(
-        JNIEnv* env, jobject thiz, jint keyCode, jint uni);
-JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(
-        JNIEnv* env, jobject thiz, jint keyCode);
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(
-        JNIEnv* env, jobject thiz, jint x, jint y, jint state);
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow(
-        JNIEnv* env, jobject thiz, jobject jsampleView);
-};
-
-JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyDown(
-        JNIEnv* env, jobject thiz, jint keyCode, jint uni)
-{
-    bool handled = gWindow->handleKey(AndroidKeycodeToSkKey(keyCode));
-    handled |= gWindow->handleChar((SkUnichar) uni);
-    return handled;
-}
-
-JNIEXPORT bool JNICALL Java_com_skia_sampleapp_SampleApp_handleKeyUp(JNIEnv* env,
-        jobject thiz, jint keyCode)
-{
-    return gWindow->handleKeyUp(AndroidKeycodeToSkKey(keyCode));
-}
-
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_handleClick(JNIEnv* env,
-        jobject thiz, jint x, jint y, jint jstate)
-{
-    SkView::Click::State state;
-    switch(jstate) {
-        case 0:     // MotionEvent.ACTION_DOWN
-            state = SkView::Click::kDown_State;
-            break;
-        case 1:     // MotionEvent.ACTION_UP
-        case 3:     // MotionEvent.ACTION_CANCEL
-            state = SkView::Click::kUp_State;
-            break;
-        case 2:     // MotionEvent.ACTION_MOVE
-            state = SkView::Click::kMoved_State;
-            break;
-        default:
-            SkDebugf("motion event ignored\n");
-            return;
-    }
-    gWindow->handleClick(x, y, state);
-}
-
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_updateSize(JNIEnv* env,
-        jobject thiz, jint w, jint h)
-{
-    gWindow->resize(w, h);
-}
-
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_createOSWindow(
-        JNIEnv* env, jobject thiz, jobject jsampleView)
-{
-    gWindow = create_sk_window(NULL);
-    // Only using a method on View.
-    jclass clazz = gActivityGlue.m_env->FindClass("android/view/View");
-    gWindowGlue.m_obj = gActivityGlue.m_env->NewWeakGlobalRef(jsampleView);
-    gWindowGlue.m_inval = GetJMethod(gActivityGlue.m_env, clazz, "invalidate",
-            "(IIII)V");
-    gActivityGlue.m_env->DeleteLocalRef(clazz);
-}
-
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_init(JNIEnv* env,
-        jobject thiz)
-{
-    gActivityGlue.m_env = env;
-    // Only using a method on Activity.
-    jclass clazz = env->FindClass("android/app/Activity");
-    gActivityGlue.m_obj = env->NewWeakGlobalRef(thiz);
-    gActivityGlue.m_setTitle = GetJMethod(env, clazz, "setTitle",
-            "(Ljava/lang/CharSequence;)V");
-    env->DeleteLocalRef(clazz);
-
-    application_init();
-}
-
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_term(JNIEnv* env,
-        jobject thiz)
-{
-    application_term();
-    if (gWindowGlue.m_obj) {
-        env->DeleteWeakGlobalRef(gWindowGlue.m_obj);
-        gWindowGlue.m_obj = NULL;
-    }
-    if (gActivityGlue.m_obj) {
-        env->DeleteWeakGlobalRef(gActivityGlue.m_obj);
-        gActivityGlue.m_obj = NULL;
-    }
-    delete gWindow;
-    gWindow = NULL;
-}
-
-
-JNIEXPORT void JNICALL Java_com_skia_sampleapp_SampleApp_drawToCanvas(
-        JNIEnv* env, jobject thiz, jobject jcanvas)
-{
-    if (!gWindow) return;
-    gWindow->update(NULL);
-    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
-    canvas->drawBitmap(gWindow->getBitmap(), 0, 0);
-}
diff --git a/android_sample/SampleApp/res/layout/layout.xml b/android_sample/SampleApp/res/layout/layout.xml
deleted file mode 100644
index d71116b..0000000
--- a/android_sample/SampleApp/res/layout/layout.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 Skia
-
-     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.
--->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/holder"
-    android:orientation="vertical"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content">
-    <TextView android:id="@+id/title_view"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        />
-</LinearLayout>
-
diff --git a/android_sample/SampleApp/res/values/strings.xml b/android_sample/SampleApp/res/values/strings.xml
deleted file mode 100644
index 72d3bc8..0000000
--- a/android_sample/SampleApp/res/values/strings.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 Skia
-
-     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.
--->
-<resources>
-    <string name="app_name">SampleApp</string>
-</resources>
diff --git a/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java b/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java
deleted file mode 100644
index b02c4d7..0000000
--- a/android_sample/SampleApp/src/com/skia/sampleapp/SampleApp.java
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (C) 2011 Skia
- *
- * 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.
- */
-
-package com.skia.sampleapp;
-
-import android.app.Activity;
-import android.content.Context;
-import android.graphics.Canvas;
-import android.os.Bundle;
-import android.util.AttributeSet;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class SampleApp extends Activity
-{
-    private TextView mTitle;
-
-    public class SampleView extends View {
-        public SampleView(Context context) {
-            super(context);
-            createOSWindow(this);
-        }
-
-        @Override
-        protected void onDraw(Canvas canvas) {
-            drawToCanvas(canvas);
-        }
-
-        @Override
-        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-            updateSize(w, h);
-        }
-
-        @Override
-        public boolean onTouchEvent(MotionEvent event) {
-            final int x = (int) event.getX();
-            final int y = (int) event.getY();
-            final int action = event.getAction();
-            handleClick(x, y, action);
-            return true;
-        }
-    }
-
-    @Override
-    public void onCreate(Bundle savedInstanceState)
-    {
-        super.onCreate(savedInstanceState);
-
-        init();
-        setContentView(R.layout.layout);
-        mTitle = (TextView) findViewById(R.id.title_view);
-        LinearLayout holder = (LinearLayout) findViewById(R.id.holder);
-        View view = new SampleView(this);
-        holder.addView(view, new LinearLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.MATCH_PARENT));
-    }
-
-    @Override
-    public void onDestroy()
-    {
-        term();
-        super.onDestroy();
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        switch (event.getAction()) {
-            case KeyEvent.ACTION_DOWN:
-                int uni = event.getUnicodeChar(event.getMetaState());
-                return handleKeyDown(event.getKeyCode(), uni);
-            case KeyEvent.ACTION_UP:
-                return handleKeyUp(event.getKeyCode());
-            default:
-                return false;
-        }
-    }
-
-    @Override
-    public void setTitle(CharSequence title) {
-        mTitle.setText(title);
-    }
-
-    private native void drawToCanvas(Canvas canvas);
-    private native void init();
-    private native void term();
-    // Currently depends on init having already been called.
-    private native void createOSWindow(SampleView view);
-    private native void updateSize(int w, int h);
-    private native void handleClick(int x, int y, int state);
-    private native boolean handleKeyDown(int key, int uni);
-    private native boolean handleKeyUp(int key);
-
-    static {
-        System.loadLibrary("skia-sample");
-    }
-}
diff --git a/bench/AAClipBench.cpp b/bench/AAClipBench.cpp
index 39088c1..f85af68 100644
--- a/bench/AAClipBench.cpp
+++ b/bench/AAClipBench.cpp
@@ -10,7 +10,176 @@
 #include "SkPath.h"
 #include "SkRegion.h"
 #include "SkString.h"
+#include "SkCanvas.h"
+#include "SkRandom.h"
 
+////////////////////////////////////////////////////////////////////////////////
+// This bench tests out AA/BW clipping via canvas' clipPath and clipRect calls
+class AAClipBench : public SkBenchmark {
+    SkString fName;
+    SkPath   fClipPath;
+    SkRect   fClipRect;
+    SkRect   fDrawRect;
+    bool     fDoPath;
+    bool     fDoAA;
+
+    enum {
+        N = SkBENCHLOOP(200),
+    };
+
+public:
+    AAClipBench(void* param, bool doPath, bool doAA)
+        : INHERITED(param)
+        , fDoPath(doPath)
+        , fDoAA(doAA) {
+
+        fName.printf("aaclip_%s_%s",
+                     doPath ? "path" : "rect",
+                     doAA ? "AA" : "BW");
+
+        fClipRect.set(SkFloatToScalar(10.5f), SkFloatToScalar(10.5f),
+                      SkFloatToScalar(50.5f), SkFloatToScalar(50.5f));
+        fClipPath.addRoundRect(fClipRect, SkIntToScalar(10), SkIntToScalar(10));
+        fDrawRect.set(SkIntToScalar(0), SkIntToScalar(0),
+                      SkIntToScalar(100), SkIntToScalar(100));
+
+        SkASSERT(fClipPath.isConvex());
+    }
+
+protected:
+    virtual const char* onGetName() { return fName.c_str(); }
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPaint paint;
+        this->setupPaint(&paint);
+
+        for (int i = 0; i < N; ++i) {
+            // jostle the clip regions each time to prevent caching
+            fClipRect.offset((i % 2) == 0 ? SkIntToScalar(10) : SkIntToScalar(-10), 0);
+            fClipPath.reset();
+            fClipPath.addRoundRect(fClipRect,
+                                   SkIntToScalar(5), SkIntToScalar(5));
+            SkASSERT(fClipPath.isConvex());
+
+            canvas->save();
+#if 1
+            if (fDoPath) {
+                canvas->clipPath(fClipPath, SkRegion::kReplace_Op, fDoAA);
+            } else {
+                canvas->clipRect(fClipRect, SkRegion::kReplace_Op, fDoAA);
+            }
+
+            canvas->drawRect(fDrawRect, paint);
+#else
+            // this path tests out directly draw the clip primitive
+            // use it to comparing just drawing the clip vs. drawing using
+            // the clip
+            if (fDoPath) {
+                canvas->drawPath(fClipPath, paint);
+            } else {
+                canvas->drawRect(fClipRect, paint);
+            }
+#endif
+            canvas->restore();
+        }
+    }
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// This bench tests out nested clip stacks. It is intended to simulate
+// how WebKit nests clips.
+class NestedAAClipBench : public SkBenchmark {
+    SkString fName;
+    bool     fDoAA;
+    SkRect   fDrawRect;
+    SkRandom fRandom;
+
+    static const int kNumDraws = SkBENCHLOOP(2);
+    static const int kNestingDepth = 3;
+    static const int kImageSize = 400;
+
+    SkPoint fSizes[kNestingDepth+1];
+
+public:
+    NestedAAClipBench(void* param, bool doAA)
+        : INHERITED(param)
+        , fDoAA(doAA) {
+
+        fName.printf("nested_aaclip_%s", doAA ? "AA" : "BW");
+
+        fDrawRect = SkRect::MakeLTRB(0, 0,
+                                     SkIntToScalar(kImageSize),
+                                     SkIntToScalar(kImageSize));
+
+        fSizes[0].set(SkIntToScalar(kImageSize), SkIntToScalar(kImageSize));
+
+        for (int i = 1; i < kNestingDepth+1; ++i) {
+            fSizes[i].set(fSizes[i-1].fX/2, fSizes[i-1].fY/2);
+        }
+    }
+
+protected:
+    virtual const char* onGetName() { return fName.c_str(); }
+
+
+    void recurse(SkCanvas* canvas,
+                 int depth,
+                 const SkPoint& offset) {
+
+            canvas->save();
+
+            SkRect temp = SkRect::MakeLTRB(0, 0,
+                                           fSizes[depth].fX, fSizes[depth].fY);
+            temp.offset(offset);
+
+            SkPath path;
+            path.addRoundRect(temp, SkIntToScalar(3), SkIntToScalar(3));
+            SkASSERT(path.isConvex());
+
+            canvas->clipPath(path,
+                             0 == depth ? SkRegion::kReplace_Op :
+                                          SkRegion::kIntersect_Op,
+                             fDoAA);
+
+            if (kNestingDepth == depth) {
+                // we only draw the draw rect at the lowest nesting level
+                SkPaint paint;
+                paint.setColor(0xff000000 | fRandom.nextU());
+                canvas->drawRect(fDrawRect, paint);
+            } else {
+                SkPoint childOffset = offset;
+                this->recurse(canvas, depth+1, childOffset);
+
+                childOffset += fSizes[depth+1];
+                this->recurse(canvas, depth+1, childOffset);
+
+                childOffset.fX = offset.fX + fSizes[depth+1].fX;
+                childOffset.fY = offset.fY;
+                this->recurse(canvas, depth+1, childOffset);
+
+                childOffset.fX = offset.fX;
+                childOffset.fY = offset.fY + fSizes[depth+1].fY;
+                this->recurse(canvas, depth+1, childOffset);
+            }
+
+            canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        for (int i = 0; i < kNumDraws; ++i) {
+            SkPoint offset = SkPoint::Make(0, 0);
+            this->recurse(canvas, 0, offset);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
 class AAClipBuilderBench : public SkBenchmark {
     SkString fName;
     SkPath   fPath;
@@ -56,6 +225,7 @@
     typedef SkBenchmark INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
 class AAClipRegionBench : public SkBenchmark {
 public:
     AAClipRegionBench(void* param) : INHERITED(param) {
@@ -88,7 +258,7 @@
     typedef SkBenchmark INHERITED;
 };
 
-///////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
 
 static SkBenchmark* Fact0(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, false)); }
 static SkBenchmark* Fact1(void* p) { return SkNEW_ARGS(AAClipBuilderBench, (p, false, true)); }
@@ -102,3 +272,19 @@
 
 static SkBenchmark* Fact01(void* p) { return SkNEW_ARGS(AAClipRegionBench, (p)); }
 static BenchRegistry gReg01(Fact01);
+
+static SkBenchmark* Fact000(void* p) { return SkNEW_ARGS(AAClipBench, (p, false, false)); }
+static SkBenchmark* Fact001(void* p) { return SkNEW_ARGS(AAClipBench, (p, false, true)); }
+static SkBenchmark* Fact002(void* p) { return SkNEW_ARGS(AAClipBench, (p, true, false)); }
+static SkBenchmark* Fact003(void* p) { return SkNEW_ARGS(AAClipBench, (p, true, true)); }
+
+static BenchRegistry gReg000(Fact000);
+static BenchRegistry gReg001(Fact001);
+static BenchRegistry gReg002(Fact002);
+static BenchRegistry gReg003(Fact003);
+
+static SkBenchmark* Fact004(void* p) { return SkNEW_ARGS(NestedAAClipBench, (p, false)); }
+static SkBenchmark* Fact005(void* p) { return SkNEW_ARGS(NestedAAClipBench, (p, true)); }
+
+static BenchRegistry gReg004(Fact004);
+static BenchRegistry gReg005(Fact005);
diff --git a/bench/Android.mk b/bench/Android.mk
index 71a44b9..11481de 100644
--- a/bench/Android.mk
+++ b/bench/Android.mk
@@ -4,44 +4,78 @@
 
 LOCAL_SRC_FILES := \
   benchmain.cpp \
+  SkBenchmark.cpp \
   BenchTimer.cpp \
   BenchSysTimer_posix.cpp \
   BenchGpuTimer_gl.cpp \
-  SkBenchmark.cpp
+  SkBenchLogger.cpp \
+  TimerData.cpp
 
 LOCAL_SRC_FILES += \
   AAClipBench.cpp \
   BitmapBench.cpp \
+  BitmapRectBench.cpp \
   BlurBench.cpp \
+  BlurRectBench.cpp \
   ChromeBench.cpp \
+  DashBench.cpp \
   DecodeBench.cpp \
+  DeferredCanvasBench.cpp \
   FontScalerBench.cpp \
   GradientBench.cpp \
+  GrMemoryPoolBench.cpp \
+  InterpBench.cpp \
+  LineBench.cpp \
   MathBench.cpp \
+  Matrix44Bench.cpp \
   MatrixBench.cpp \
+  MatrixConvolutionBench.cpp \
+  MemoryBench.cpp \
+  MorphologyBench.cpp \
   MutexBench.cpp \
   PathBench.cpp \
+  PathIterBench.cpp \
   PicturePlaybackBench.cpp \
+  PictureRecordBench.cpp \
+  ReadPixBench.cpp \
   RectBench.cpp \
+  RefCntBench.cpp \
+  RegionBench.cpp \
+  RegionContainBench.cpp \
   RepeatTileBench.cpp \
+  RTreeBench.cpp \
   ScalarBench.cpp \
   ShaderMaskBench.cpp \
+  SortBench.cpp \
+  TableBench.cpp \
   TextBench.cpp \
-  VertBench.cpp
+  TileBench.cpp \
+  VertBench.cpp \
+  WriterBench.cpp
 
-LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2 libEGL
-LOCAL_STATIC_LIBRARIES := libskiagpu
+# Files that are missing dependencies
+#LOCAL_SRC_FILES += \
+#  ChecksumBench.cpp
+#
+
+LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2 libEGL 
+
+LOCAL_STATIC_LIBRARIES := libstlport_static
+
 LOCAL_C_INCLUDES := \
-    external/skia/include/core \
-    external/skia/include/config \
-    external/skia/include/effects \
-    external/skia/include/gpu \
-    external/skia/include/images \
-    external/skia/include/utils \
-    external/skia/src/core \
-    external/skia/src/gpu
-
-#LOCAL_CFLAGS := 
+  external/skia/include/core \
+  external/skia/include/config \
+  external/skia/include/effects \
+  external/skia/include/gpu \
+  external/skia/include/images \
+  external/skia/include/pipe \
+  external/skia/include/utils \
+  external/skia/src/core \
+  external/skia/src/effects \
+  external/skia/src/utils \
+  external/skia/src/gpu \
+  external/stlport/stlport \
+  bionic
 
 LOCAL_MODULE := skia_bench
 
diff --git a/bench/BenchGpuTimer_gl.cpp b/bench/BenchGpuTimer_gl.cpp
index b7bd88b..699f5e5 100644
--- a/bench/BenchGpuTimer_gl.cpp
+++ b/bench/BenchGpuTimer_gl.cpp
@@ -7,6 +7,7 @@
  */
 #include "BenchGpuTimer_gl.h"
 #include "gl/SkGLContext.h"
+#include "gl/GrGLUtil.h"
 
 BenchGpuTimer::BenchGpuTimer(const SkGLContext* glctx) {
     fContext = glctx;
@@ -16,7 +17,7 @@
     fSupported = GrGLGetVersion(glctx->gl()) > GR_GL_VER(3,3) ||
                  GrGLHasExtension(glctx->gl(), "GL_ARB_timer_query") ||
                  GrGLHasExtension(glctx->gl(), "GL_EXT_timer_query");
-    
+
     if (fSupported) {
         SK_GL(*glctx, GenQueries(1, &fQuery));
     }
@@ -47,7 +48,7 @@
         fStarted = false;
         fContext->makeCurrent();
         SK_GL(*fContext, EndQuery(GR_GL_TIME_ELAPSED));
-        
+
         GrGLint available = 0;
         while (!available) {
             SK_GL(*fContext, GetQueryObjectiv(fQuery,
@@ -58,7 +59,7 @@
         SK_GL(*fContext, GetQueryObjectui64v(fQuery,
                                              GR_GL_QUERY_RESULT,
                                              &totalGPUTimeElapsed));
-        
+
         return totalGPUTimeElapsed / 1000000.0;
     } else {
         return 0;
diff --git a/bench/BenchSysTimer_mach.cpp b/bench/BenchSysTimer_mach.cpp
index c837ca3..cf3f1c1 100644
--- a/bench/BenchSysTimer_mach.cpp
+++ b/bench/BenchSysTimer_mach.cpp
@@ -17,7 +17,7 @@
         time_value_t none = {0, 0};
         return none;
     }
-    
+
     task_thread_times_info thread_info_data;
     mach_msg_type_number_t thread_info_count = TASK_THREAD_TIMES_INFO_COUNT;
     if (KERN_SUCCESS != task_info(task,
@@ -28,7 +28,7 @@
         time_value_t none = {0, 0};
         return none;
     }
-    
+
     time_value_add(&thread_info_data.user_time, &thread_info_data.system_time)
     return thread_info_data.user_time;
 }
@@ -63,7 +63,7 @@
 }
 double BenchSysTimer::endWall() {
     uint64_t end_wall = mach_absolute_time();
-    
+
     uint64_t elapsed = end_wall - this->fStartWall;
     mach_timebase_info_data_t sTimebaseInfo;
     if (KERN_SUCCESS != mach_timebase_info(&sTimebaseInfo)) {
diff --git a/bench/BenchSysTimer_posix.h b/bench/BenchSysTimer_posix.h
index de793f3..8fd9706 100644
--- a/bench/BenchSysTimer_posix.h
+++ b/bench/BenchSysTimer_posix.h
@@ -23,4 +23,3 @@
 };
 
 #endif
-
diff --git a/bench/BenchSysTimer_windows.cpp b/bench/BenchSysTimer_windows.cpp
index 3635ec5..1c4e404 100644
--- a/bench/BenchSysTimer_windows.cpp
+++ b/bench/BenchSysTimer_windows.cpp
@@ -49,10 +49,10 @@
     if (0 == ::QueryPerformanceCounter(&end_wall)) {
         end_wall.QuadPart = 0;
     }
-    
+
     LARGE_INTEGER ticks_elapsed;
     ticks_elapsed.QuadPart = end_wall.QuadPart - this->fStartWall.QuadPart;
-    
+
     LARGE_INTEGER frequency;
     if (0 == ::QueryPerformanceFrequency(&frequency)) {
         return 0.0L;
diff --git a/bench/BenchTimer.cpp b/bench/BenchTimer.cpp
index c3a1190..4e04844 100644
--- a/bench/BenchTimer.cpp
+++ b/bench/BenchTimer.cpp
@@ -16,40 +16,61 @@
     #include "BenchSysTimer_c.h"
 #endif
 
+#if SK_SUPPORT_GPU
 #include "BenchGpuTimer_gl.h"
+#endif
 
 BenchTimer::BenchTimer(SkGLContext* gl)
         : fCpu(-1.0)
         , fWall(-1.0)
+        , fTruncatedCpu(-1.0)
+        , fTruncatedWall(-1.0)
         , fGpu(-1.0)
 {
     fSysTimer = new BenchSysTimer();
+    fTruncatedSysTimer = new BenchSysTimer();
+#if SK_SUPPORT_GPU
     if (gl) {
         fGpuTimer = new BenchGpuTimer(gl);
     } else {
         fGpuTimer = NULL;
     }
+#endif
 }
 
 BenchTimer::~BenchTimer() {
     delete fSysTimer;
+    delete fTruncatedSysTimer;
+#if SK_SUPPORT_GPU
     delete fGpuTimer;
+#endif
 }
 
 void BenchTimer::start() {
     fSysTimer->startWall();
+    fTruncatedSysTimer->startWall();
+#if SK_SUPPORT_GPU
     if (fGpuTimer) {
         fGpuTimer->startGpu();
     }
+#endif
     fSysTimer->startCpu();
+    fTruncatedSysTimer->startCpu();
 }
 
 void BenchTimer::end() {
     fCpu = fSysTimer->endCpu();
+#if SK_SUPPORT_GPU
     //It is important to stop the cpu clocks first,
     //as the following will cpu wait for the gpu to finish.
     if (fGpuTimer) {
         fGpu = fGpuTimer->endGpu();
     }
+#endif
     fWall = fSysTimer->endWall();
 }
+
+void BenchTimer::truncatedEnd() {
+    fTruncatedCpu = fTruncatedSysTimer->endCpu();
+    fTruncatedWall = fTruncatedSysTimer->endWall();
+}
diff --git a/bench/BenchTimer.h b/bench/BenchTimer.h
index 080bc6d..58773d4 100644
--- a/bench/BenchTimer.h
+++ b/bench/BenchTimer.h
@@ -18,8 +18,12 @@
 
 /**
  * SysTimers and GpuTimers are implemented orthogonally.
- * This class combines a SysTimer and a GpuTimer into one single,
- * platform specific, Timer with a simple interface.
+ * This class combines 2 SysTimers and a GpuTimer into one single,
+ * platform specific Timer with a simple interface. The truncated
+ * timer doesn't include the time required for the GPU to finish
+ * its rendering. It should always be <= the un-truncated system
+ * times and (for GPU configurations) can be used to roughly (very
+ * roughly) gauge the GPU load/backlog.
  */
 class BenchTimer {
 public:
@@ -27,13 +31,19 @@
     ~BenchTimer();
     void start();
     void end();
+    void truncatedEnd();
     double fCpu;
     double fWall;
+    double fTruncatedCpu;
+    double fTruncatedWall;
     double fGpu;
-    
+
 private:
     BenchSysTimer *fSysTimer;
+    BenchSysTimer *fTruncatedSysTimer;
+#if SK_SUPPORT_GPU
     BenchGpuTimer *fGpuTimer;
+#endif
 };
 
 #endif
diff --git a/bench/BitmapBench.cpp b/bench/BitmapBench.cpp
index 3b16925..0efdde3 100644
--- a/bench/BitmapBench.cpp
+++ b/bench/BitmapBench.cpp
@@ -31,7 +31,7 @@
     p.setColor(SK_ColorRED);
     canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
                       SkIntToScalar(SkMin32(w, h))*3/8, p);
-    
+
     SkRect r;
     r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
     p.setStyle(SkPaint::kStroke_Style);
@@ -52,7 +52,7 @@
     int r = SkGetPackedR32(c);
     int g = SkGetPackedG32(c);
     int b = SkGetPackedB32(c);
-    
+
     return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b);
 }
 
@@ -74,7 +74,7 @@
     dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height());
     dst->allocPixels(ctable);
     ctable->unref();
-    
+
     SkAutoLockPixels alps(src);
     SkAutoLockPixels alpd(*dst);
 
@@ -88,12 +88,12 @@
 }
 
 /*  Variants for bitmaps
- 
+
     - src depth (32 w+w/o alpha), 565, 4444, index, a8
     - paint options: filtering, dither, alpha
     - matrix options: translate, scale, rotate, persp
     - tiling: none, repeat, mirror, clamp
-    
+
  */
 
 class BitmapBench : public SkBenchmark {
@@ -106,7 +106,7 @@
     enum { N = SkBENCHLOOP(300) };
 public:
     BitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
-                bool forceUpdate = false, bool bitmapVolatile = false, 
+                bool forceUpdate = false, bool bitmapVolatile = false,
                 int tx = -1, int ty = -1)
         : INHERITED(param), fIsOpaque(isOpaque), fForceUpdate(forceUpdate), fTileX(tx), fTileY(ty) {
         const int w = 128;
@@ -120,7 +120,7 @@
         }
         bm.allocPixels();
         bm.eraseColor(isOpaque ? SK_ColorBLACK : 0);
-        
+
         drawIntoBitmap(bm);
 
         if (SkBitmap::kIndex8_Config == c) {
@@ -147,7 +147,7 @@
         }
         fName.appendf("_%s%s", gConfigName[fBitmap.config()],
                       fIsOpaque ? "" : "_A");
-        if (fForceUpdate) 
+        if (fForceUpdate)
             fName.append("_update");
         if (fBitmap.isVolatile())
             fName.append("_volatile");
@@ -165,7 +165,7 @@
         const SkBitmap& bitmap = fBitmap;
         const SkScalar x0 = SkIntToScalar(-bitmap.width() / 2);
         const SkScalar y0 = SkIntToScalar(-bitmap.height() / 2);
-        
+
         for (int i = 0; i < N; i++) {
             SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
             SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
@@ -181,6 +181,66 @@
     typedef SkBenchmark INHERITED;
 };
 
+/** Explicitly invoke some filter types to improve coverage of acceleration
+    procs. */
+
+class FilterBitmapBench : public BitmapBench {
+    bool        fScale;
+    bool        fRotate;
+    bool        fFilter;
+    SkString    fFullName;
+    enum { N = SkBENCHLOOP(300) };
+public:
+    FilterBitmapBench(void* param, bool isOpaque, SkBitmap::Config c,
+                bool forceUpdate = false, bool bitmapVolatile = false,
+                int tx = -1, int ty = -1, bool addScale = false,
+                bool addRotate = false, bool addFilter = false)
+        : INHERITED(param, isOpaque, c, forceUpdate, bitmapVolatile, tx, ty)
+        , fScale(addScale), fRotate(addRotate), fFilter(addFilter) {
+
+    }
+
+protected:
+    virtual const char* onGetName() {
+        fFullName.set(INHERITED::onGetName());
+        if (fScale)
+            fFullName.append("_scale");
+        if (fRotate)
+            fFullName.append("_rotate");
+        if (fFilter)
+            fFullName.append("_filter");
+
+        return fFullName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkISize dim = canvas->getDeviceSize();
+        if (fScale) {
+            const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
+            const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
+
+            canvas->translate(x, y);
+            // just enough so we can't take the sprite case
+            canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
+            canvas->translate(-x, -y);
+        }
+        if (fRotate) {
+            const SkScalar x = SkIntToScalar(dim.fWidth) / 2;
+            const SkScalar y = SkIntToScalar(dim.fHeight) / 2;
+
+            canvas->translate(x, y);
+            canvas->rotate(SkIntToScalar(35));
+            canvas->translate(-x, -y);
+        }
+
+        this->setForceFilter(fFilter);
+        INHERITED::onDraw(canvas);
+    }
+
+private:
+    typedef BitmapBench INHERITED;
+};
+
 static SkBenchmark* Fact0(void* p) { return new BitmapBench(p, false, SkBitmap::kARGB_8888_Config); }
 static SkBenchmark* Fact1(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config); }
 static SkBenchmark* Fact2(void* p) { return new BitmapBench(p, true, SkBitmap::kRGB_565_Config); }
@@ -191,6 +251,18 @@
 static SkBenchmark* Fact7(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true); }
 static SkBenchmark* Fact8(void* p) { return new BitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false); }
 
+// scale filter -> S32_opaque_D32_filter_DX_{SSE2,SSSE3} and Fact9 is also for S32_D16_filter_DX_SSE2
+static SkBenchmark* Fact9(void* p) { return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, -1, -1, true, false, true); }
+static SkBenchmark* Fact10(void* p) { return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, -1, -1, true, false, true); }
+static SkBenchmark* Fact11(void* p) { return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, -1, -1, true, false, true); }
+static SkBenchmark* Fact12(void* p) { return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, -1, -1, true, false, true); }
+
+// scale rotate filter -> S32_opaque_D32_filter_DXDY_{SSE2,SSSE3}
+static SkBenchmark* Fact13(void* p) { return new FilterBitmapBench(p, false, SkBitmap::kARGB_8888_Config, false, false, -1, -1, true, true, true); }
+static SkBenchmark* Fact14(void* p) { return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, false, false, -1, -1, true, true, true); }
+static SkBenchmark* Fact15(void* p) { return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, true, -1, -1, true, true, true); }
+static SkBenchmark* Fact16(void* p) { return new FilterBitmapBench(p, true, SkBitmap::kARGB_8888_Config, true, false, -1, -1, true, true, true); }
+
 static BenchRegistry gReg0(Fact0);
 static BenchRegistry gReg1(Fact1);
 static BenchRegistry gReg2(Fact2);
@@ -200,3 +272,13 @@
 static BenchRegistry gReg6(Fact6);
 static BenchRegistry gReg7(Fact7);
 static BenchRegistry gReg8(Fact8);
+
+static BenchRegistry gReg9(Fact9);
+static BenchRegistry gReg10(Fact10);
+static BenchRegistry gReg11(Fact11);
+static BenchRegistry gReg12(Fact12);
+
+static BenchRegistry gReg13(Fact13);
+static BenchRegistry gReg14(Fact14);
+static BenchRegistry gReg15(Fact15);
+static BenchRegistry gReg16(Fact16);
diff --git a/bench/BitmapRectBench.cpp b/bench/BitmapRectBench.cpp
new file mode 100644
index 0000000..f9f46b8
--- /dev/null
+++ b/bench/BitmapRectBench.cpp
@@ -0,0 +1,108 @@
+
+/*
+ * 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 "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkPaint.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+static void drawIntoBitmap(const SkBitmap& bm) {
+    const int w = bm.width();
+    const int h = bm.height();
+
+    SkCanvas canvas(bm);
+    SkPaint p;
+    p.setAntiAlias(true);
+    p.setColor(SK_ColorRED);
+    canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
+                      SkIntToScalar(SkMin32(w, h))*3/8, p);
+
+    SkRect r;
+    r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SkIntToScalar(4));
+    p.setColor(SK_ColorBLUE);
+    canvas.drawRect(r, p);
+}
+
+/*  Variants for bitmaprect
+    src : entire bitmap, subset, fractional subset
+    dst : same size as src, diff size
+    paint : filter-p
+ */
+
+class BitmapRectBench : public SkBenchmark {
+    SkBitmap    fBitmap;
+    bool        fDoFilter;
+    bool        fSlightMatrix;
+    uint8_t     fAlpha;
+    SkString    fName;
+    SkRect      fSrcR, fDstR;
+    enum { N = SkBENCHLOOP(300) };
+public:
+    BitmapRectBench(void* param, U8CPU alpha, bool doFilter, bool slightMatrix) : INHERITED(param) {
+        fAlpha = SkToU8(alpha);
+        fDoFilter = doFilter;
+        fSlightMatrix = slightMatrix;
+
+        const int w = 128;
+        const int h = 128;
+
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+        fBitmap.allocPixels();
+        fBitmap.setIsOpaque(true);
+        fBitmap.eraseColor(SK_ColorBLACK);
+        drawIntoBitmap(fBitmap);
+
+        fSrcR.iset(0, 0, w, h);
+        fDstR.iset(0, 0, w, h);
+
+        if (slightMatrix) {
+            // want fractional translate
+            fDstR.offset(SK_Scalar1 / 3, SK_Scalar1 * 5 / 7);
+            // want enough to create a scale matrix, but not enough to scare
+            // off our sniffer which tries to see if the matrix is "effectively"
+            // translate-only.
+            fDstR.fRight += SK_Scalar1 / (w * 60);
+        }
+    }
+
+protected:
+    virtual const char* onGetName() {
+        fName.printf("bitmaprect_%02X_%sfilter_%s",
+                     fAlpha, fDoFilter ? "" : "no",
+                     fSlightMatrix ? "trans" : "identity");
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkRandom rand;
+
+        SkPaint paint;
+        this->setupPaint(&paint);
+        paint.setFilterBitmap(fDoFilter);
+        paint.setAlpha(fAlpha);
+
+        for (int i = 0; i < N; i++) {
+            canvas->drawBitmapRectToRect(fBitmap, &fSrcR, fDstR, &paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, false, false))
+DEF_BENCH(return new BitmapRectBench(p, 0x80, false, false))
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, true, false))
+DEF_BENCH(return new BitmapRectBench(p, 0x80, true, false))
+
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, false, true))
+DEF_BENCH(return new BitmapRectBench(p, 0xFF, true, true))
diff --git a/bench/BlurBench.cpp b/bench/BlurBench.cpp
index de78fe1..5f2f425 100644
--- a/bench/BlurBench.cpp
+++ b/bench/BlurBench.cpp
@@ -16,6 +16,7 @@
 #define SMALL   SkIntToScalar(2)
 #define REAL    SkFloatToScalar(1.5f)
 #define BIG     SkIntToScalar(10)
+#define REALBIG SkFloatToScalar(100.5f)
 
 static const char* gStyleName[] = {
     "normal",
@@ -27,25 +28,28 @@
 class BlurBench : public SkBenchmark {
     SkScalar    fRadius;
     SkBlurMaskFilter::BlurStyle fStyle;
+    uint32_t                    fFlags;
     SkString    fName;
 
 public:
-    BlurBench(void* param, SkScalar rad, SkBlurMaskFilter::BlurStyle bs) : INHERITED(param) {
+    BlurBench(void* param, SkScalar rad, SkBlurMaskFilter::BlurStyle bs, uint32_t flags = 0) : INHERITED(param) {
         fRadius = rad;
         fStyle = bs;
+        fFlags = flags;
         const char* name = rad > 0 ? gStyleName[bs] : "none";
+        const char* quality = flags & SkBlurMaskFilter::kHighQuality_BlurFlag ? "high_quality" : "low_quality";
         if (SkScalarFraction(rad) != 0) {
-            fName.printf("blur_%.2f_%s", SkScalarToFloat(rad), name);
+            fName.printf("blur_%.2f_%s_%s", SkScalarToFloat(rad), name, quality);
         } else {
-            fName.printf("blur_%d_%s", SkScalarRound(rad), name);
+            fName.printf("blur_%d_%s_%s", SkScalarRound(rad), name, quality);
         }
     }
-    
+
 protected:
     virtual const char* onGetName() {
         return fName.c_str();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         SkPaint paint;
         this->setupPaint(&paint);
@@ -59,48 +63,43 @@
             r.offset(fRadius, fRadius);
 
             if (fRadius > 0) {
-                SkMaskFilter* mf = SkBlurMaskFilter::Create(fRadius, fStyle, 0);
+                SkMaskFilter* mf = SkBlurMaskFilter::Create(fRadius, fStyle, fFlags);
                 paint.setMaskFilter(mf)->unref();
             }
             canvas->drawOval(r, paint);
         }
     }
-    
+
 private:
     typedef SkBenchmark INHERITED;
 };
 
-static SkBenchmark* Fact00(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kNormal_BlurStyle); }
-static SkBenchmark* Fact01(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kSolid_BlurStyle); }
-static SkBenchmark* Fact02(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kOuter_BlurStyle); }
-static SkBenchmark* Fact03(void* p) { return new BlurBench(p, SMALL, SkBlurMaskFilter::kInner_BlurStyle); }
+DEF_BENCH(return new BlurBench(p, SMALL, SkBlurMaskFilter::kNormal_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, SMALL, SkBlurMaskFilter::kSolid_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, SMALL, SkBlurMaskFilter::kOuter_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, SMALL, SkBlurMaskFilter::kInner_BlurStyle);)
 
-static SkBenchmark* Fact10(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kNormal_BlurStyle); }
-static SkBenchmark* Fact11(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kSolid_BlurStyle); }
-static SkBenchmark* Fact12(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kOuter_BlurStyle); }
-static SkBenchmark* Fact13(void* p) { return new BlurBench(p, BIG, SkBlurMaskFilter::kInner_BlurStyle); }
+DEF_BENCH(return new BlurBench(p, BIG, SkBlurMaskFilter::kNormal_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, BIG, SkBlurMaskFilter::kSolid_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, BIG, SkBlurMaskFilter::kOuter_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, BIG, SkBlurMaskFilter::kInner_BlurStyle);)
 
-static SkBenchmark* Fact20(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kNormal_BlurStyle); }
-static SkBenchmark* Fact21(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kSolid_BlurStyle); }
-static SkBenchmark* Fact22(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kOuter_BlurStyle); }
-static SkBenchmark* Fact23(void* p) { return new BlurBench(p, REAL, SkBlurMaskFilter::kInner_BlurStyle); }
+DEF_BENCH(return new BlurBench(p, REALBIG, SkBlurMaskFilter::kNormal_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, REALBIG, SkBlurMaskFilter::kSolid_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, REALBIG, SkBlurMaskFilter::kOuter_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, REALBIG, SkBlurMaskFilter::kInner_BlurStyle);)
 
-static SkBenchmark* FactNone(void* p) { return new BlurBench(p, 0, SkBlurMaskFilter::kNormal_BlurStyle); }
+DEF_BENCH(return new BlurBench(p, REAL, SkBlurMaskFilter::kNormal_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, REAL, SkBlurMaskFilter::kSolid_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, REAL, SkBlurMaskFilter::kOuter_BlurStyle);)
+DEF_BENCH(return new BlurBench(p, REAL, SkBlurMaskFilter::kInner_BlurStyle);)
 
-static BenchRegistry gReg00(Fact00);
-static BenchRegistry gReg01(Fact01);
-static BenchRegistry gReg02(Fact02);
-static BenchRegistry gReg03(Fact03);
+DEF_BENCH(return new BlurBench(p, SMALL, SkBlurMaskFilter::kNormal_BlurStyle, SkBlurMaskFilter::kHighQuality_BlurFlag);)
 
-static BenchRegistry gReg10(Fact10);
-static BenchRegistry gReg11(Fact11);
-static BenchRegistry gReg12(Fact12);
-static BenchRegistry gReg13(Fact13);
+DEF_BENCH(return new BlurBench(p, BIG, SkBlurMaskFilter::kNormal_BlurStyle, SkBlurMaskFilter::kHighQuality_BlurFlag);)
 
-static BenchRegistry gReg20(Fact20);
-static BenchRegistry gReg21(Fact21);
-static BenchRegistry gReg22(Fact22);
-static BenchRegistry gReg23(Fact23);
+DEF_BENCH(return new BlurBench(p, REALBIG, SkBlurMaskFilter::kNormal_BlurStyle, SkBlurMaskFilter::kHighQuality_BlurFlag);)
 
-static BenchRegistry gRegNone(FactNone);
+DEF_BENCH(return new BlurBench(p, REAL, SkBlurMaskFilter::kNormal_BlurStyle, SkBlurMaskFilter::kHighQuality_BlurFlag);)
 
+DEF_BENCH(return new BlurBench(p, 0, SkBlurMaskFilter::kNormal_BlurStyle);)
diff --git a/bench/BlurRectBench.cpp b/bench/BlurRectBench.cpp
new file mode 100644
index 0000000..b0b06e6
--- /dev/null
+++ b/bench/BlurRectBench.cpp
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkBlurMask.h"
+
+#define SMALL   SkIntToScalar(2)
+#define REAL    SkFloatToScalar(1.5f)
+#define BIG     SkIntToScalar(10)
+#define REALBIG SkFloatToScalar(30.5f)
+
+class BlurRectBench: public SkBenchmark {
+    SkScalar    fRadius;
+    SkString    fName;
+
+public:
+    BlurRectBench(void *param, SkScalar rad) : INHERITED(param) {
+        fRadius = rad;
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    SkScalar radius() const {
+        return fRadius;
+    }
+
+    void setName(const SkString& name) {
+        fName = name;
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        this->setupPaint(&paint);
+
+        paint.setAntiAlias(true);
+
+        SkScalar pad = fRadius*3/2 + SK_Scalar1;
+        SkRect r = SkRect::MakeWH(2 * pad + SK_Scalar1, 2 * pad + SK_Scalar1);
+
+        int loop_count;
+
+        if (fRadius > SkIntToScalar(25)) {
+          loop_count = 100;
+        } else if (fRadius > SkIntToScalar(5)) {
+          loop_count = 1000;
+        } else {
+          loop_count = 10000;
+        }
+
+        preBenchSetup(r);
+
+        for (int i = 0; i < SkBENCHLOOP(loop_count); i++) {
+            makeBlurryRect(r);
+        }
+    }
+
+    virtual void makeBlurryRect(const SkRect&) = 0;
+    virtual void preBenchSetup(const SkRect&) {}
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+
+class BlurRectDirectBench: public BlurRectBench {
+ public:
+    BlurRectDirectBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
+        SkString name;
+
+        if (SkScalarFraction(rad) != 0) {
+            name.printf("blurrect_direct_%.2f", SkScalarToFloat(rad));
+        } else {
+            name.printf("blurrect_direct_%d", SkScalarRound(rad));
+        }
+
+        setName(name);
+    }
+protected:
+    virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
+        SkMask mask;
+        SkBlurMask::BlurRect(&mask, r, radius(), SkBlurMask::kNormal_Style,
+                             SkBlurMask::kHigh_Quality);
+        SkMask::FreeImage(mask.fImage);
+    }
+};
+
+class BlurRectSeparableBench: public BlurRectBench {
+    SkMask fSrcMask;
+public:
+    BlurRectSeparableBench(void *param, SkScalar rad) : BlurRectBench(param, rad) {
+        SkString name;
+        if (SkScalarFraction(rad) != 0) {
+            name.printf("blurrect_separable_%.2f", SkScalarToFloat(rad));
+        } else {
+            name.printf("blurrect_separable_%d", SkScalarRound(rad));
+        }
+        setName(name);
+        fSrcMask.fImage = NULL;
+    }
+
+    ~BlurRectSeparableBench() {
+        SkMask::FreeImage(fSrcMask.fImage);
+    }
+
+protected:
+    virtual void preBenchSetup(const SkRect& r) SK_OVERRIDE {
+        SkMask::FreeImage(fSrcMask.fImage);
+
+        r.roundOut(&fSrcMask.fBounds);
+        fSrcMask.fFormat = SkMask::kA8_Format;
+        fSrcMask.fRowBytes = fSrcMask.fBounds.width();
+        fSrcMask.fImage = SkMask::AllocImage(fSrcMask.computeTotalImageSize());
+
+        memset(fSrcMask.fImage, 0xff, fSrcMask.computeTotalImageSize());
+    }
+
+    virtual void makeBlurryRect(const SkRect& r) SK_OVERRIDE {
+        SkMask mask;
+        SkBlurMask::BlurSeparable(&mask, fSrcMask, radius(),
+                                  SkBlurMask::kNormal_Style,
+                                  SkBlurMask::kHigh_Quality);
+        SkMask::FreeImage(mask.fImage);
+    }
+};
+
+DEF_BENCH(return new BlurRectSeparableBench(p, SMALL);)
+DEF_BENCH(return new BlurRectSeparableBench(p, BIG);)
+DEF_BENCH(return new BlurRectSeparableBench(p, REALBIG);)
+DEF_BENCH(return new BlurRectSeparableBench(p, REAL);)
+DEF_BENCH(return new BlurRectDirectBench(p, SMALL);)
+DEF_BENCH(return new BlurRectDirectBench(p, BIG);)
+DEF_BENCH(return new BlurRectDirectBench(p, REALBIG);)
+DEF_BENCH(return new BlurRectDirectBench(p, REAL);)
diff --git a/bench/ChecksumBench.cpp b/bench/ChecksumBench.cpp
new file mode 100644
index 0000000..4158d94
--- /dev/null
+++ b/bench/ChecksumBench.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkChecksum.h"
+#include "SkCityHash.h"
+#include "SkMD5.h"
+#include "SkRandom.h"
+#include "SkSHA1.h"
+
+template<typename T> inline void sk_ignore_unused(const T&) { }
+
+enum ChecksumType {
+    kChecksum_ChecksumType,
+    kMD5_ChecksumType,
+    kSHA1_ChecksumType,
+    kCityHash32,
+    kCityHash64
+};
+
+class ComputeChecksumBench : public SkBenchmark {
+    enum {
+        U32COUNT  = 256,
+        SIZE      = U32COUNT * 4,
+        N         = SkBENCHLOOP(100000),
+    };
+    uint32_t    fData[U32COUNT];
+    ChecksumType fType;
+
+public:
+    ComputeChecksumBench(void* param, ChecksumType type) : INHERITED(param), fType(type) {
+        SkRandom rand;
+        for (int i = 0; i < U32COUNT; ++i) {
+            fData[i] = rand.nextU();
+        }
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() {
+        switch (fType) {
+            case kChecksum_ChecksumType: return "compute_checksum";
+            case kMD5_ChecksumType: return "compute_md5";
+            case kSHA1_ChecksumType: return "compute_sha1";
+            case kCityHash32: return "compute_cityhash32";
+            case kCityHash64: return "compute_cityhash64";
+            default: SK_CRASH(); return "";
+        }
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        switch (fType) {
+            case kChecksum_ChecksumType: {
+                for (int i = 0; i < N; i++) {
+                    volatile uint32_t result = SkChecksum::Compute(fData, sizeof(fData));
+                    sk_ignore_unused(result);
+                }
+            } break;
+            case kMD5_ChecksumType: {
+                for (int i = 0; i < N; i++) {
+                    SkMD5 md5;
+                    md5.update(reinterpret_cast<uint8_t*>(fData), sizeof(fData));
+                    SkMD5::Digest digest;
+                    md5.finish(digest);
+                    sk_ignore_unused(digest);
+                }
+            } break;
+            case kSHA1_ChecksumType: {
+                for (int i = 0; i < N; i++) {
+                    SkSHA1 sha1;
+                    sha1.update(reinterpret_cast<uint8_t*>(fData), sizeof(fData));
+                    SkSHA1::Digest digest;
+                    sha1.finish(digest);
+                    sk_ignore_unused(digest);
+                }
+            } break;
+            case kCityHash32: {
+                for (int i = 0; i < N; i++) {
+                    volatile uint32_t result = SkCityHash::Compute32(reinterpret_cast<char*>(fData), sizeof(fData));
+                    sk_ignore_unused(result);
+                }
+            } break;
+            case kCityHash64: {
+                for (int i = 0; i < N; i++) {
+                    volatile uint64_t result = SkCityHash::Compute64(reinterpret_cast<char*>(fData), sizeof(fData));
+                    sk_ignore_unused(result);
+                }
+            } break;
+        }
+
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return new ComputeChecksumBench(p, kChecksum_ChecksumType); }
+static SkBenchmark* Fact1(void* p) { return new ComputeChecksumBench(p, kMD5_ChecksumType); }
+static SkBenchmark* Fact2(void* p) { return new ComputeChecksumBench(p, kSHA1_ChecksumType); }
+static SkBenchmark* Fact3(void* p) { return new ComputeChecksumBench(p, kCityHash32); }
+static SkBenchmark* Fact4(void* p) { return new ComputeChecksumBench(p, kCityHash64); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
+static BenchRegistry gReg3(Fact3);
+static BenchRegistry gReg4(Fact4);
diff --git a/bench/ChromeBench.cpp b/bench/ChromeBench.cpp
index fc73d53..8021f92 100644
--- a/bench/ChromeBench.cpp
+++ b/bench/ChromeBench.cpp
@@ -491,8 +491,10 @@
     typedef SkBenchmark INHERITED;
 };
 
-static SkBenchmark* ScrollGmailFactory(void* p) {
+static inline SkBenchmark* ScrollGmailFactory(void* p) {
     return SkNEW_ARGS(ScrollGmailBench, (p));
 }
 
-static BenchRegistry gScrollGmailReg(ScrollGmailFactory);
+// Disabled this benchmark: it takes 15x longer than any other benchmark
+// and is probably not giving us important information.
+//static BenchRegistry gScrollGmailReg(ScrollGmailFactory);
diff --git a/bench/DashBench.cpp b/bench/DashBench.cpp
new file mode 100644
index 0000000..e62fb53
--- /dev/null
+++ b/bench/DashBench.cpp
@@ -0,0 +1,424 @@
+
+/*
+ * 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 "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDashPathEffect.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+
+
+/*
+ *  Cases to consider:
+ *
+ *  1. antialiasing on/off (esp. width <= 1)
+ *  2. strokewidth == 0, 1, 2
+ *  3. hline, vline, diagonal, rect, oval
+ *  4. dots [1,1] ([N,N] where N=strokeWidth?) or arbitrary (e.g. [2,1] or [1,2,3,2])
+ */
+static void path_hline(SkPath* path) {
+    path->moveTo(SkIntToScalar(10), SkIntToScalar(10));
+    path->lineTo(SkIntToScalar(600), SkIntToScalar(10));
+}
+
+class DashBench : public SkBenchmark {
+protected:
+    SkString            fName;
+    SkTDArray<SkScalar> fIntervals;
+    int                 fWidth;
+    SkPoint             fPts[2];
+    bool                fDoClip;
+
+    enum {
+        N = SkBENCHLOOP(100)
+    };
+public:
+    DashBench(void* param, const SkScalar intervals[], int count, int width,
+              bool doClip = false) : INHERITED(param) {
+        fIntervals.append(count, intervals);
+        for (int i = 0; i < count; ++i) {
+            fIntervals[i] *= width;
+        }
+        fWidth = width;
+        fName.printf("dash_%d_%s", width, doClip ? "clipped" : "noclip");
+        fDoClip = doClip;
+
+        fPts[0].set(SkIntToScalar(10), SkIntToScalar(10));
+        fPts[1].set(SkIntToScalar(600), SkIntToScalar(10));
+    }
+
+    virtual void makePath(SkPath* path) {
+        path_hline(path);
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint paint;
+        this->setupPaint(&paint);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SkIntToScalar(fWidth));
+        paint.setAntiAlias(false);
+
+        SkPath path;
+        this->makePath(&path);
+
+        paint.setPathEffect(new SkDashPathEffect(fIntervals.begin(),
+                                                 fIntervals.count(), 0))->unref();
+
+        if (fDoClip) {
+            SkRect r = path.getBounds();
+            r.inset(-SkIntToScalar(20), -SkIntToScalar(20));
+            // now move it so we don't intersect
+            r.offset(0, r.height() * 3 / 2);
+            canvas->clipRect(r);
+        }
+
+        this->handlePath(canvas, path, paint, N);
+    }
+
+    virtual void handlePath(SkCanvas* canvas, const SkPath& path,
+                            const SkPaint& paint, int N) {
+        for (int i = 0; i < N; ++i) {
+//            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, paint);
+            canvas->drawPath(path, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class RectDashBench : public DashBench {
+public:
+    RectDashBench(void* param, const SkScalar intervals[], int count, int width, bool doClip = false)
+    : INHERITED(param, intervals, count, width) {
+        fName.append("_rect");
+    }
+
+protected:
+    virtual void handlePath(SkCanvas* canvas, const SkPath& path,
+                            const SkPaint& paint, int N) SK_OVERRIDE {
+        SkPoint pts[2];
+        if (!path.isLine(pts) || pts[0].fY != pts[1].fY) {
+            this->INHERITED::handlePath(canvas, path, paint, N);
+        } else {
+            SkRect rect;
+            rect.fLeft = pts[0].fX;
+            rect.fTop = pts[0].fY - paint.getStrokeWidth() / 2;
+            rect.fRight = rect.fLeft + SkIntToScalar(fWidth);
+            rect.fBottom = rect.fTop + paint.getStrokeWidth();
+
+            SkPaint p(paint);
+            p.setStyle(SkPaint::kFill_Style);
+            p.setPathEffect(NULL);
+
+            int count = SkScalarRoundToInt((pts[1].fX - pts[0].fX) / (2*fWidth));
+            SkScalar dx = SkIntToScalar(2 * fWidth);
+
+            for (int i = 0; i < N*10; ++i) {
+                SkRect r = rect;
+                for (int j = 0; j < count; ++j) {
+                    canvas->drawRect(r, p);
+                    r.offset(dx, 0);
+                }
+            }
+        }
+    }
+
+private:
+    typedef DashBench INHERITED;
+};
+
+static void make_unit_star(SkPath* path, int n) {
+    SkScalar rad = -SK_ScalarPI / 2;
+    const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
+
+    path->moveTo(0, -SK_Scalar1);
+    for (int i = 1; i < n; i++) {
+        rad += drad;
+        SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
+        path->lineTo(cosV, sinV);
+    }
+    path->close();
+}
+
+static void make_poly(SkPath* path) {
+    make_unit_star(path, 9);
+    SkMatrix matrix;
+    matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
+    path->transform(matrix);
+}
+
+static void make_quad(SkPath* path) {
+    SkScalar x0 = SkIntToScalar(10);
+    SkScalar y0 = SkIntToScalar(10);
+    path->moveTo(x0, y0);
+    path->quadTo(x0,                    y0 + 400 * SK_Scalar1,
+                 x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1);
+}
+
+static void make_cubic(SkPath* path) {
+    SkScalar x0 = SkIntToScalar(10);
+    SkScalar y0 = SkIntToScalar(10);
+    path->moveTo(x0, y0);
+    path->cubicTo(x0,                    y0 + 400 * SK_Scalar1,
+                  x0 + 600 * SK_Scalar1, y0 + 400 * SK_Scalar1,
+                  x0 + 600 * SK_Scalar1, y0);
+}
+
+class MakeDashBench : public SkBenchmark {
+    SkString fName;
+    SkPath   fPath;
+    SkAutoTUnref<SkPathEffect> fPE;
+
+    enum {
+        N = SkBENCHLOOP(400)
+    };
+
+public:
+    MakeDashBench(void* param, void (*proc)(SkPath*), const char name[]) : INHERITED(param) {
+        fName.printf("makedash_%s", name);
+        proc(&fPath);
+
+        SkScalar vals[] = { SkIntToScalar(4), SkIntToScalar(4) };
+        fPE.reset(new SkDashPathEffect(vals, 2, 0));
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPath dst;
+        for (int i = 0; i < N; ++i) {
+            SkStrokeRec rec(SkStrokeRec::kHairline_InitStyle);
+
+            fPE->filterPath(&dst, fPath, &rec, NULL);
+            dst.rewind();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+/*
+ *  We try to special case square dashes (intervals are equal to strokewidth).
+ */
+class DashLineBench : public SkBenchmark {
+    SkString fName;
+    SkScalar fStrokeWidth;
+    bool     fIsRound;
+    SkAutoTUnref<SkPathEffect> fPE;
+
+    enum {
+        N = SkBENCHLOOP(200)
+    };
+
+public:
+    DashLineBench(void* param, SkScalar width, bool isRound) : INHERITED(param) {
+        fName.printf("dashline_%g_%s", SkScalarToFloat(width), isRound ? "circle" : "square");
+        fStrokeWidth = width;
+        fIsRound = isRound;
+
+        SkScalar vals[] = { SK_Scalar1, SK_Scalar1 };
+        fPE.reset(new SkDashPathEffect(vals, 2, 0));
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint paint;
+        this->setupPaint(&paint);
+        paint.setStrokeWidth(fStrokeWidth);
+        paint.setStrokeCap(fIsRound ? SkPaint::kRound_Cap : SkPaint::kSquare_Cap);
+        paint.setPathEffect(fPE);
+        for (int i = 0; i < N; ++i) {
+            canvas->drawLine(10 * SK_Scalar1, 10 * SK_Scalar1,
+                             640 * SK_Scalar1, 10 * SK_Scalar1, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class DrawPointsDashingBench : public SkBenchmark {
+    SkString fName;
+    int      fStrokeWidth;
+    bool     fDoAA;
+
+    SkAutoTUnref<SkPathEffect> fPathEffect;
+
+    enum {
+        N = SkBENCHLOOP(480)
+    };
+
+public:
+    DrawPointsDashingBench(void* param, int dashLength, int strokeWidth, bool doAA)
+        : INHERITED(param) {
+        fName.printf("drawpointsdash_%d_%d%s", dashLength, strokeWidth, doAA ? "_aa" : "_bw");
+        fStrokeWidth = strokeWidth;
+        fDoAA = doAA;
+
+        SkScalar vals[] = { SkIntToScalar(dashLength), SkIntToScalar(dashLength) };
+        fPathEffect.reset(new SkDashPathEffect(vals, 2, SK_Scalar1, false));
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+
+        SkPaint p;
+        this->setupPaint(&p);
+        p.setColor(SK_ColorBLACK);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(SkIntToScalar(fStrokeWidth));
+        p.setPathEffect(fPathEffect);
+        p.setAntiAlias(fDoAA);
+
+        SkPoint pts[2] = {
+            { SkIntToScalar(10), 0 },
+            { SkIntToScalar(640), 0 }
+        };
+
+        for (int i = 0; i < N; ++i) {
+
+            pts[0].fY = pts[1].fY = SkIntToScalar(i % 480);
+            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+// Want to test how we handle dashing when 99% of the dash is clipped out
+class GiantDashBench : public SkBenchmark {
+    SkString fName;
+    SkScalar fStrokeWidth;
+    SkPoint  fPts[2];
+    SkAutoTUnref<SkPathEffect> fPathEffect;
+
+public:
+    enum LineType {
+        kHori_LineType,
+        kVert_LineType,
+        kDiag_LineType
+    };
+
+    static const char* LineTypeName(LineType lt) {
+        static const char* gNames[] = { "hori", "vert", "diag" };
+        SkASSERT((size_t)lt <= SK_ARRAY_COUNT(gNames));
+        return gNames[lt];
+    }
+
+    GiantDashBench(void* param, LineType lt, SkScalar width) : INHERITED(param) {
+        fName.printf("giantdashline_%s_%g", LineTypeName(lt), width);
+        fStrokeWidth = width;
+
+        // deliberately pick intervals that won't be caught by asPoints(), so
+        // we can test the filterPath code-path.
+        const SkScalar intervals[] = { 2, 1, 1, 1 };
+        fPathEffect.reset(new SkDashPathEffect(intervals,
+                                               SK_ARRAY_COUNT(intervals), 0));
+
+        SkScalar cx = 640 / 2;  // center X
+        SkScalar cy = 480 / 2;  // center Y
+        SkMatrix matrix;
+
+        switch (lt) {
+            case kHori_LineType:
+                matrix.setIdentity();
+                break;
+            case kVert_LineType:
+                matrix.setRotate(90, cx, cy);
+                break;
+            case kDiag_LineType:
+                matrix.setRotate(45, cx, cy);
+                break;
+        }
+
+        const SkScalar overshoot = 100*1000;
+        const SkPoint pts[2] = {
+            { -overshoot, cy }, { 640 + overshoot, cy }
+        };
+        matrix.mapPoints(fPts, pts, 2);
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint p;
+        this->setupPaint(&p);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(fStrokeWidth);
+        p.setPathEffect(fPathEffect);
+
+        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, fPts, p);
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkScalar gDots[] = { SK_Scalar1, SK_Scalar1 };
+
+#define PARAM(array)    array, SK_ARRAY_COUNT(array)
+
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 0); )
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 1); )
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 1, true); )
+DEF_BENCH( return new DashBench(p, PARAM(gDots), 4); )
+DEF_BENCH( return new MakeDashBench(p, make_poly, "poly"); )
+DEF_BENCH( return new MakeDashBench(p, make_quad, "quad"); )
+DEF_BENCH( return new MakeDashBench(p, make_cubic, "cubic"); )
+DEF_BENCH( return new DashLineBench(p, 0, false); )
+DEF_BENCH( return new DashLineBench(p, SK_Scalar1, false); )
+DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, false); )
+DEF_BENCH( return new DashLineBench(p, 0, true); )
+DEF_BENCH( return new DashLineBench(p, SK_Scalar1, true); )
+DEF_BENCH( return new DashLineBench(p, 2 * SK_Scalar1, true); )
+
+DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, false); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 1, 1, true); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, false); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 3, 1, true); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, false); )
+DEF_BENCH( return new DrawPointsDashingBench(p, 5, 5, true); )
+
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 0); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 0); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 0); )
+
+// pass 2 to explicitly avoid any 1-is-the-same-as-hairline special casing
+
+// hori_2 is just too slow to enable at the moment
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kHori_LineType, 2); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kVert_LineType, 2); )
+DEF_BENCH( return new GiantDashBench(p, GiantDashBench::kDiag_LineType, 2); )
diff --git a/bench/DecodeBench.cpp b/bench/DecodeBench.cpp
index 6761690..6593d10 100644
--- a/bench/DecodeBench.cpp
+++ b/bench/DecodeBench.cpp
@@ -23,7 +23,7 @@
     DecodeBench(void* param, SkBitmap::Config c) : SkBenchmark(param) {
         fFilename = this->findDefine("decode-filename");
         fPrefConfig = c;
-        
+
         const char* fname = NULL;
         if (fFilename) {
             fname = strrchr(fFilename, '/');
@@ -32,6 +32,7 @@
             }
         }
         fName.printf("decode_%s_%s", gConfigName[c], fname);
+        fIsRendering = false;
     }
 
 protected:
diff --git a/bench/DeferredCanvasBench.cpp b/bench/DeferredCanvasBench.cpp
new file mode 100644
index 0000000..a1a3901
--- /dev/null
+++ b/bench/DeferredCanvasBench.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
+#include "SkString.h"
+
+class DeferredCanvasBench : public SkBenchmark {
+public:
+    DeferredCanvasBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("deferred_canvas_%s", name);
+    }
+
+    enum {
+        N = SkBENCHLOOP(25), // number of times to create the picture
+        CANVAS_WIDTH = 200,
+        CANVAS_HEIGHT = 200,
+    };
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkDevice *device = canvas->getDevice()->createCompatibleDevice(
+            SkBitmap::kARGB_8888_Config, CANVAS_WIDTH, CANVAS_HEIGHT, false);
+
+        SkDeferredCanvas deferredCanvas(device);
+
+        device->unref();
+
+        initDeferredCanvas(deferredCanvas);
+
+        for (int i = 0; i < N; i++) {
+            drawInDeferredCanvas(deferredCanvas);
+        }
+
+        finalizeDeferredCanvas(deferredCanvas);
+        deferredCanvas.flush();
+    }
+
+    virtual void initDeferredCanvas(SkDeferredCanvas& canvas) = 0;
+    virtual void drawInDeferredCanvas(SkDeferredCanvas& canvas) = 0;
+    virtual void finalizeDeferredCanvas(SkDeferredCanvas& canvas) = 0;
+
+    SkString fName;
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class SimpleNotificationClient : public SkDeferredCanvas::NotificationClient {
+public:
+    SimpleNotificationClient() : fDummy(false) {}
+
+    //bogus virtual implementations that just do something small
+    virtual void prepareForDraw() SK_OVERRIDE {fDummy = true;}
+    virtual void storageAllocatedForRecordingChanged(size_t) SK_OVERRIDE {fDummy = false;}
+    virtual void flushedDrawCommands() SK_OVERRIDE {fDummy = !fDummy;}
+private:
+    bool fDummy;
+};
+
+// Test that records very simple draw operations.
+// This benchmark aims to capture performance fluctuations in the recording
+// overhead of SkDeferredCanvas
+class DeferredRecordBench : public DeferredCanvasBench {
+public:
+    DeferredRecordBench(void* param)
+        : INHERITED(param, "record") {
+    }
+
+    enum {
+        M = SkBENCHLOOP(700),   // number of individual draws in each loop
+    };
+protected:
+
+    virtual void initDeferredCanvas(SkDeferredCanvas& canvas) SK_OVERRIDE {
+        canvas.setNotificationClient(&fNotificationClient);
+    }
+
+    virtual void drawInDeferredCanvas(SkDeferredCanvas& canvas) SK_OVERRIDE {
+        SkRect rect;
+        rect.setXYWH(0, 0, 10, 10);
+        SkPaint paint;
+        for (int i = 0; i < M; i++) {
+            canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+            canvas.translate(SkIntToScalar(i * 27 % CANVAS_WIDTH), SkIntToScalar(i * 13 % CANVAS_HEIGHT));
+            canvas.drawRect(rect, paint);
+            canvas.restore();
+        }
+    }
+
+    virtual void finalizeDeferredCanvas(SkDeferredCanvas& canvas) SK_OVERRIDE {
+        canvas.clear(0x0);
+        canvas.setNotificationClient(NULL);
+    }
+
+private:
+    typedef DeferredCanvasBench INHERITED;
+    SimpleNotificationClient fNotificationClient;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return new DeferredRecordBench(p); }
+
+static BenchRegistry gReg0(Fact0);
diff --git a/bench/GrMemoryPoolBench.cpp b/bench/GrMemoryPoolBench.cpp
new file mode 100644
index 0000000..d6c4b77
--- /dev/null
+++ b/bench/GrMemoryPoolBench.cpp
@@ -0,0 +1,171 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This tests a Gr class
+#if SK_SUPPORT_GPU
+
+#include "GrMemoryPool.h"
+#include "SkBenchmark.h"
+#include "SkRandom.h"
+#include "SkTScopedPtr.h"
+#include "SkTDArray.h"
+
+// change this to 0 to compare GrMemoryPool to default new / delete
+#define OVERRIDE_NEW    1
+
+namespace {
+struct A {
+    int gStuff[10];
+#if OVERRIDE_NEW
+    void* operator new (size_t size) { return gPool.allocate(size); }
+    void operator delete (void* mem) { if (mem) { return gPool.release(mem); } }
+#endif
+    static GrMemoryPool gPool;
+};
+GrMemoryPool A::gPool(10 * (1 << 10), 10 * (1 << 10));
+}
+
+
+/**
+ * This benchmark creates and deletes objects in stack order
+ */
+class GrMemoryPoolBenchStack : public SkBenchmark {
+    enum {
+        N = SkBENCHLOOP(1 * (1 << 20)),
+    };
+public:
+    GrMemoryPoolBenchStack(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "grmemorypool_stack";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkRandom r;
+        enum {
+            kMaxObjects = 4 * (1 << 10),
+        };
+        A* objects[kMaxObjects];
+
+        // We delete if a random [-1, 1] fixed pt is < the thresh. Otherwise,
+        // we allocate. We start allocate-biased and ping-pong to delete-biased
+        SkFixed delThresh = -SK_FixedHalf;
+        enum {
+            kSwitchThreshPeriod = N / (2 * kMaxObjects),
+        };
+        int s = 0;
+
+        int count = 0;
+        for (int i = 0; i < N; i++, ++s) {
+            if (kSwitchThreshPeriod == s) {
+                delThresh = -delThresh;
+                s = 0;
+            }
+            SkFixed del = r.nextSFixed1();
+            if (count &&
+                (kMaxObjects == count || del < delThresh)) {
+                delete objects[count-1];
+                --count;
+            } else {
+                objects[count] = new A;
+                ++count;
+            }
+        }
+        for (int i = 0; i < count; ++i) {
+            delete objects[i];
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+/**
+ * This benchmark creates objects and deletes them in random order
+ */
+class GrMemoryPoolBenchRandom : public SkBenchmark {
+    enum {
+        N = SkBENCHLOOP(1 * (1 << 20)),
+    };
+public:
+    GrMemoryPoolBenchRandom(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "grmemorypool_random";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkRandom r;
+        enum {
+            kMaxObjects = 4 * (1 << 10),
+        };
+        SkTScopedPtr<A> objects[kMaxObjects];
+
+        for (int i = 0; i < N; i++) {
+            uint32_t idx = r.nextRangeU(0, kMaxObjects-1);
+            if (NULL == objects[idx].get()) {
+                objects[idx].reset(new A);
+            } else {
+                objects[idx].reset(NULL);
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+/**
+ * This benchmark creates objects and deletes them in queue order
+ */
+class GrMemoryPoolBenchQueue : public SkBenchmark {
+    enum {
+        N = SkBENCHLOOP((1 << 8)),
+        M = SkBENCHLOOP(4 * (1 << 10)),
+    };
+public:
+    GrMemoryPoolBenchQueue(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "grmemorypool_queue";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkRandom r;
+        A* objects[M];
+        for (int i = 0; i < N; i++) {
+            uint32_t count = r.nextRangeU(0, M-1);
+            for (uint32_t i = 0; i < count; i++) {
+                objects[i] = new A;
+            }
+            for (uint32_t i = 0; i < count; i++) {
+                delete objects[i];
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact1(void* p) { return new GrMemoryPoolBenchStack(p); }
+static SkBenchmark* Fact2(void* p) { return new GrMemoryPoolBenchRandom(p); }
+static SkBenchmark* Fact3(void* p) { return new GrMemoryPoolBenchQueue(p); }
+
+static BenchRegistry gReg01(Fact1);
+static BenchRegistry gReg02(Fact2);
+static BenchRegistry gReg03(Fact3);
+
+#endif
diff --git a/bench/GradientBench.cpp b/bench/GradientBench.cpp
index a5032ea..2f1b825 100644
--- a/bench/GradientBench.cpp
+++ b/bench/GradientBench.cpp
@@ -78,9 +78,23 @@
     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, mapper);
+                                                  center1, (pts[1].fX - pts[0].fX) / 7,
+                                                  center0, (pts[1].fX - pts[0].fX) / 2,
+                                                  data.fColors, data.fPos, data.fCount, tm, mapper);
+}
+
+/// Ignores scale
+static SkShader* MakeConical(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper,
+                             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::CreateTwoPointConical(center1, (pts[1].fX - pts[0].fX) / 7,
+                                                   center0, (pts[1].fX - pts[0].fX) / 2,
+                                                   data.fColors, data.fPos, data.fCount, tm, mapper);
 }
 
 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
@@ -96,13 +110,15 @@
     { MakeRadial,   "radial1", 10 },
     { MakeSweep,    "sweep",    1 },
     { Make2Radial,  "radial2",  5 },
+    { MakeConical,  "conical",  5 },
 };
 
 enum GradType { // these must match the order in gGrads
     kLinear_GradType,
     kRadial_GradType,
     kSweep_GradType,
-    kRadial2_GradType
+    kRadial2_GradType,
+    kConical_GradType
 };
 
 enum GeomType {
@@ -164,7 +180,7 @@
             { 0, 0 },
             { SkIntToScalar(W), SkIntToScalar(H) }
         };
-        
+
         fCount = SkBENCHLOOP(N * gGrads[gradType].fRepeat);
         fShader = gGrads[gradType].fMaker(pts, gGradData[0], tm, NULL, scale);
         fGeomType = geomType;
@@ -207,16 +223,16 @@
 class Gradient2Bench : public SkBenchmark {
 public:
     Gradient2Bench(void* param) : INHERITED(param) {}
-    
+
 protected:
     virtual const char* onGetName() {
         return "gradient_create";
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         SkPaint paint;
         this->setupPaint(&paint);
-        
+
         const SkRect r = { 0, 0, SkIntToScalar(4), SkIntToScalar(4) };
         const SkPoint pts[] = {
             { 0, 0 },
@@ -236,7 +252,7 @@
             canvas->drawRect(r, paint);
         }
     }
-    
+
 private:
     typedef SkBenchmark INHERITED;
 };
@@ -256,6 +272,7 @@
 static SkBenchmark* Fact2(void* p) { return new GradientBench(p, kSweep_GradType); }
 static SkBenchmark* Fact3(void* p) { return new GradientBench(p, kRadial2_GradType); }
 static SkBenchmark* Fact31(void* p) { return new GradientBench(p, kRadial2_GradType, SkShader::kMirror_TileMode); }
+static SkBenchmark* Fact5(void* p) { return new GradientBench(p, kConical_GradType); }
 
 static SkBenchmark* Fact4(void* p) { return new Gradient2Bench(p); }
 
@@ -267,6 +284,6 @@
 static BenchRegistry gReg2(Fact2);
 static BenchRegistry gReg3(Fact3);
 static BenchRegistry gReg31(Fact31);
+static BenchRegistry gReg5(Fact5);
 
 static BenchRegistry gReg4(Fact4);
-
diff --git a/bench/InterpBench.cpp b/bench/InterpBench.cpp
new file mode 100644
index 0000000..086cc3b
--- /dev/null
+++ b/bench/InterpBench.cpp
@@ -0,0 +1,166 @@
+#include "SkBenchmark.h"
+#include "SkColorPriv.h"
+#include "SkMatrix.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "SkPaint.h"
+
+#define TILE(x, width)  (((x) & 0xFFFF) * width >> 16)
+
+class InterpBench : public SkBenchmark {
+    enum {
+        kBuffer = 128,
+        kLoop   = 20000
+    };
+    SkString    fName;
+    int16_t     fDst[kBuffer];
+    float       fFx, fDx;
+public:
+    InterpBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("interp_%s", name);
+        fFx = 3.3f;
+        fDx = 0.1257f;
+        fIsRendering = false;
+    }
+
+    virtual void performTest(int16_t dst[], float x, float dx, int count) = 0;
+
+protected:
+    virtual int mulLoopCount() const { return 1; }
+
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        int n = SkBENCHLOOP(kLoop * this->mulLoopCount());
+        for (int i = 0; i < n; i++) {
+            this->performTest(fDst, fFx, fDx, kBuffer);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class Fixed16D16Interp : public InterpBench {
+public:
+    Fixed16D16Interp(void* param) : INHERITED(param, "16.16") {}
+
+protected:
+    virtual void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+        SkFixed curr = SkFloatToFixed(fx);
+        SkFixed step = SkFloatToFixed(dx);
+        for (int i = 0; i < count; i += 4) {
+            dst[i + 0] = TILE(curr, count); curr += step;
+            dst[i + 1] = TILE(curr, count); curr += step;
+            dst[i + 2] = TILE(curr, count); curr += step;
+            dst[i + 3] = TILE(curr, count); curr += step;
+        }
+    }
+private:
+    typedef InterpBench INHERITED;
+};
+
+class Fixed32D32Interp : public InterpBench {
+public:
+    Fixed32D32Interp(void* param) : INHERITED(param, "32.32") {}
+
+protected:
+    virtual void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+        int64_t curr = (int64_t)(fx * 65536 * 655536);
+        int64_t step = (int64_t)(dx * 65536 * 655536);
+        SkFixed tmp;
+        for (int i = 0; i < count; i += 4) {
+            tmp = (SkFixed)(curr >> 16);
+            dst[i + 0] = TILE(tmp, count);
+            curr += step;
+
+            tmp = (SkFixed)(curr >> 16);
+            dst[i + 1] = TILE(tmp, count);
+            curr += step;
+
+            tmp = (SkFixed)(curr >> 16);
+            dst[i + 2] = TILE(tmp, count);
+            curr += step;
+
+            tmp = (SkFixed)(curr >> 16);
+            dst[i + 3] = TILE(tmp, count);
+            curr += step;
+        }
+    }
+private:
+    typedef InterpBench INHERITED;
+};
+
+class Fixed16D48Interp : public InterpBench {
+public:
+    Fixed16D48Interp(void* param) : INHERITED(param, "16.48") {}
+
+protected:
+    virtual void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+        int64_t curr = (int64_t)(fx * 65536 * 655536 * 65536);
+        int64_t step = (int64_t)(dx * 65536 * 655536 * 65536);
+        SkFixed tmp;
+        for (int i = 0; i < count; i += 4) {
+            tmp = (SkFixed) (curr >> 32); dst[i + 0] = TILE(tmp, count); curr += step;
+            tmp = (SkFixed) (curr >> 32); dst[i + 1] = TILE(tmp, count); curr += step;
+            tmp = (SkFixed) (curr >> 32); dst[i + 2] = TILE(tmp, count); curr += step;
+            tmp = (SkFixed) (curr >> 32); dst[i + 3] = TILE(tmp, count); curr += step;
+        }
+    }
+private:
+    typedef InterpBench INHERITED;
+};
+
+class FloatInterp : public InterpBench {
+public:
+    FloatInterp(void* param) : INHERITED(param, "float") {}
+
+protected:
+    virtual void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+        SkFixed tmp;
+        for (int i = 0; i < count; i += 4) {
+            tmp = SkFloatToFixed(fx); dst[i + 0] = TILE(tmp, count); fx += dx;
+            tmp = SkFloatToFixed(fx); dst[i + 1] = TILE(tmp, count); fx += dx;
+            tmp = SkFloatToFixed(fx); dst[i + 2] = TILE(tmp, count); fx += dx;
+            tmp = SkFloatToFixed(fx); dst[i + 3] = TILE(tmp, count); fx += dx;
+        }
+    }
+private:
+    typedef InterpBench INHERITED;
+};
+
+class DoubleInterp : public InterpBench {
+public:
+    DoubleInterp(void* param) : INHERITED(param, "double") {}
+
+protected:
+    virtual void performTest(int16_t dst[], float fx, float dx, int count) SK_OVERRIDE {
+        double ffx = fx;
+        double ddx = dx;
+        SkFixed tmp;
+        for (int i = 0; i < count; i += 4) {
+            tmp = SkDoubleToFixed(ffx); dst[i + 0] = TILE(tmp, count); ffx += ddx;
+            tmp = SkDoubleToFixed(ffx); dst[i + 1] = TILE(tmp, count); ffx += ddx;
+            tmp = SkDoubleToFixed(ffx); dst[i + 2] = TILE(tmp, count); ffx += ddx;
+            tmp = SkDoubleToFixed(ffx); dst[i + 3] = TILE(tmp, count); ffx += ddx;
+        }
+    }
+private:
+    typedef InterpBench INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* M0(void* p) { return new Fixed16D16Interp(p); }
+static SkBenchmark* M1(void* p) { return new Fixed32D32Interp(p); }
+static SkBenchmark* M2(void* p) { return new Fixed16D48Interp(p); }
+static SkBenchmark* M3(void* p) { return new FloatInterp(p); }
+static SkBenchmark* M4(void* p) { return new DoubleInterp(p); }
+
+static BenchRegistry gReg0(M0);
+static BenchRegistry gReg1(M1);
+static BenchRegistry gReg2(M2);
+static BenchRegistry gReg3(M3);
+static BenchRegistry gReg4(M4);
diff --git a/bench/LineBench.cpp b/bench/LineBench.cpp
new file mode 100644
index 0000000..feaae2b
--- /dev/null
+++ b/bench/LineBench.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkTArray.h"
+
+
+class LineBench : public SkBenchmark {
+    SkScalar    fStrokeWidth;
+    bool        fDoAA;
+    SkString    fName;
+    enum {
+        PTS = 500,
+        N = SkBENCHLOOP(10)
+    };
+    SkPoint fPts[PTS];
+
+public:
+    LineBench(void* param, SkScalar width, bool doAA) : INHERITED(param) {
+        fStrokeWidth = width;
+        fDoAA = doAA;
+        fName.printf("lines_%g_%s", width, doAA ? "AA" : "BW");
+
+        SkRandom rand;
+        for (int i = 0; i < PTS; ++i) {
+            fPts[i].set(rand.nextUScalar1() * 640, rand.nextUScalar1() * 480);
+        }
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint paint;
+        this->setupPaint(&paint);
+
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setAntiAlias(fDoAA);
+        paint.setStrokeWidth(fStrokeWidth);
+
+        for (int i = 0; i < N; i++) {
+            canvas->drawPoints(SkCanvas::kLines_PointMode, PTS, fPts, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+DEF_BENCH(return new LineBench(p, 0,            false);)
+DEF_BENCH(return new LineBench(p, SK_Scalar1,   false);)
+DEF_BENCH(return new LineBench(p, 0,            true);)
+DEF_BENCH(return new LineBench(p, SK_Scalar1/2, true);)
+DEF_BENCH(return new LineBench(p, SK_Scalar1,   true);)
diff --git a/bench/MathBench.cpp b/bench/MathBench.cpp
index 9feb5af..bdb89ca 100644
--- a/bench/MathBench.cpp
+++ b/bench/MathBench.cpp
@@ -5,6 +5,16 @@
 #include "SkString.h"
 #include "SkPaint.h"
 
+static float sk_fsel(float pred, float result_ge, float result_lt) {
+    return pred >= 0 ? result_ge : result_lt;
+}
+
+static float fast_floor(float x) {
+//    float big = sk_fsel(x, 0x1.0p+23, -0x1.0p+23);
+    float big = sk_fsel(x, (float)(1 << 23), -(float)(1 << 23));
+    return (x + big) - big;
+}
+
 class MathBench : public SkBenchmark {
     enum {
         kBuffer = 100,
@@ -20,9 +30,13 @@
         for (int i = 0; i < kBuffer; ++i) {
             fSrc[i] = rand.nextSScalar1();
         }
+
+        fIsRendering = false;
     }
 
-    virtual void performTest(float dst[], const float src[], int count) = 0;
+    virtual void performTest(float* SK_RESTRICT dst,
+                              const float* SK_RESTRICT src,
+                              int count) = 0;
 
 protected:
     virtual int mulLoopCount() const { return 1; }
@@ -47,10 +61,13 @@
     MathBenchU32(void* param, const char name[]) : INHERITED(param, name) {}
 
 protected:
-    virtual void performITest(uint32_t* dst, const uint32_t* src, int count) = 0;
-    
-    virtual void performTest(float* SK_RESTRICT dst, const float* SK_RESTRICT src,
-                             int count) SK_OVERRIDE {
+    virtual void performITest(uint32_t* SK_RESTRICT dst,
+                              const uint32_t* SK_RESTRICT src,
+                              int count) = 0;
+
+    virtual void performTest(float* SK_RESTRICT dst,
+                              const float* SK_RESTRICT src,
+                              int count) SK_OVERRIDE {
         uint32_t* d = SkTCast<uint32_t*>(dst);
         const uint32_t* s = SkTCast<const uint32_t*>(src);
         this->performITest(d, s, count);
@@ -65,7 +82,9 @@
 public:
     NoOpMathBench(void* param) : INHERITED(param, "noOp") {}
 protected:
-    virtual void performTest(float dst[], const float src[], int count) {
+    virtual void performTest(float* SK_RESTRICT dst,
+                              const float* SK_RESTRICT src,
+                              int count) {
         for (int i = 0; i < count; ++i) {
             dst[i] = src[i] + 1;
         }
@@ -78,7 +97,9 @@
 public:
     SlowISqrtMathBench(void* param) : INHERITED(param, "slowIsqrt") {}
 protected:
-    virtual void performTest(float dst[], const float src[], int count) {
+    virtual void performTest(float* SK_RESTRICT dst,
+                              const float* SK_RESTRICT src,
+                              int count) {
         for (int i = 0; i < count; ++i) {
             dst[i] = 1.0f / sk_float_sqrt(src[i]);
         }
@@ -89,9 +110,9 @@
 
 static inline float SkFastInvSqrt(float x) {
     float xhalf = 0.5f*x;
-    int i = *(int*)&x;
+    int i = *SkTCast<int*>(&x);
     i = 0x5f3759df - (i>>1);
-    x = *(float*)&i;
+    x = *SkTCast<float*>(&i);
     x = x*(1.5f-xhalf*x*x);
 //    x = x*(1.5f-xhalf*x*x); // this line takes err from 10^-3 to 10^-6
     return x;
@@ -101,7 +122,9 @@
 public:
     FastISqrtMathBench(void* param) : INHERITED(param, "fastIsqrt") {}
 protected:
-    virtual void performTest(float dst[], const float src[], int count) {
+    virtual void performTest(float* SK_RESTRICT dst,
+                              const float* SK_RESTRICT src,
+                              int count) {
         for (int i = 0; i < count; ++i) {
             dst[i] = SkFastInvSqrt(src[i]);
         }
@@ -117,7 +140,7 @@
     uint64_t tmp = value;
     tmp = (tmp & mask) | ((tmp & ~mask) << 24);
     tmp *= alpha;
-    return ((tmp >> 8) & mask) | ((tmp >> 32) & ~mask);
+    return (uint32_t) (((tmp >> 8) & mask) | ((tmp >> 32) & ~mask));
 }
 
 class QMul64Bench : public MathBenchU32 {
@@ -159,7 +182,7 @@
 }
 
 static bool isFinite_float(float x) {
-    return sk_float_isfinite(x);
+    return SkToBool(sk_float_isfinite(x));
 }
 
 static bool isFinite_mulzero(float x) {
@@ -216,7 +239,7 @@
     // x * 0 will be NaN iff x is infinity or NaN.
     // a + b will be NaN iff either a or b is NaN.
     float value = r.fLeft * 0 + r.fTop * 0 + r.fRight * 0 + r.fBottom * 0;
-    
+
     // value is either NaN or it is finite (zero).
     // value==value will be true iff value is not NaN
     return value == value;
@@ -244,6 +267,7 @@
             fProc = gRec[index].fProc;
             fName = gRec[index].fName;
         }
+        fIsRendering = false;
     }
 
 protected:
@@ -263,11 +287,14 @@
             for (int j = 0; j < NN; ++j) {
                 for (int i = 0; i < N - 4; ++i) {
                     const SkRect* r = reinterpret_cast<const SkRect*>(&data[i]);
+                    if (false) { // avoid bit rot, suppress warning
+                        isFinite(*r);
+                    }
                     counter += r->isFinite();
                 }
             }
         }
-        
+
         SkPaint paint;
         if (paint.getAlpha() == 0) {
             SkDebugf("%d\n", counter);
@@ -277,7 +304,7 @@
     virtual const char* onGetName() {
         return fName;
     }
-        
+
 private:
     IsFiniteProc    fProc;
     const char*     fName;
@@ -285,6 +312,65 @@
     typedef SkBenchmark INHERITED;
 };
 
+class FloorBench : public SkBenchmark {
+    enum {
+        ARRAY = SkBENCHLOOP(1000),
+        LOOP = SkBENCHLOOP(1000),
+    };
+    float fData[ARRAY];
+    bool fFast;
+public:
+
+    FloorBench(void* param, bool fast) : INHERITED(param), fFast(fast) {
+        SkRandom rand;
+
+        for (int i = 0; i < ARRAY; ++i) {
+            fData[i] = rand.nextSScalar1();
+        }
+
+        if (fast) {
+            fName = "floor_fast";
+        } else {
+            fName = "floor_std";
+        }
+        fIsRendering = false;
+    }
+
+    virtual void process(float) {}
+
+protected:
+    virtual void onDraw(SkCanvas* canvas) {
+        SkRandom rand;
+        float accum = 0;
+        const float* data = fData;
+
+        if (fFast) {
+            for (int j = 0; j < LOOP; ++j) {
+                for (int i = 0; i < ARRAY; ++i) {
+                    accum += fast_floor(data[i]);
+                }
+                this->process(accum);
+            }
+        } else {
+            for (int j = 0; j < LOOP; ++j) {
+                for (int i = 0; i < ARRAY; ++i) {
+                    accum += sk_float_floor(data[i]);
+                }
+                this->process(accum);
+            }
+        }
+    }
+
+    virtual const char* onGetName() {
+        return fName;
+    }
+
+private:
+    const char*     fName;
+
+    typedef SkBenchmark INHERITED;
+};
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static SkBenchmark* M0(void* p) { return new NoOpMathBench(p); }
@@ -301,6 +387,9 @@
 static SkBenchmark* M54(void* p) { return new IsFiniteBench(p, 4); }
 static SkBenchmark* M55(void* p) { return new IsFiniteBench(p, 5); }
 
+static SkBenchmark* F0(void* p) { return new FloorBench(p, false); }
+static SkBenchmark* F1(void* p) { return new FloorBench(p, true); }
+
 static BenchRegistry gReg0(M0);
 static BenchRegistry gReg1(M1);
 static BenchRegistry gReg2(M2);
@@ -314,3 +403,6 @@
 static BenchRegistry gReg53(M53);
 static BenchRegistry gReg54(M54);
 static BenchRegistry gReg55(M55);
+
+static BenchRegistry gRF0(F0);
+static BenchRegistry gRF1(F1);
diff --git a/bench/Matrix44Bench.cpp b/bench/Matrix44Bench.cpp
new file mode 100644
index 0000000..0d9d572
--- /dev/null
+++ b/bench/Matrix44Bench.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkMatrix44.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+class Matrix44Bench : public SkBenchmark {
+    SkString    fName;
+    enum { N = 10000 };
+public:
+    Matrix44Bench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("matrix44_%s", name);
+        fIsRendering = false;
+    }
+
+    virtual void performTest() = 0;
+
+protected:
+    virtual int mulLoopCount() const { return 1; }
+
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        int n = SkBENCHLOOP(N * this->mulLoopCount());
+        for (int i = 0; i < n; i++) {
+            this->performTest();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class EqualsMatrix44Bench : public Matrix44Bench {
+public:
+    EqualsMatrix44Bench(void* param) : INHERITED(param, "equals") {
+        fM1.set(0, 0, 0);
+        fM2.set(3, 3, 0);
+    }
+protected:
+    virtual void performTest() {
+        for (int i = 0; i < 10; ++i) {
+            (void) (fM0 == fM1);
+            (void) (fM1 == fM2);
+            (void) (fM2 == fM0);
+        }
+    }
+private:
+    SkMatrix44 fM0, fM1, fM2;
+    typedef Matrix44Bench INHERITED;
+};
+
+class PreScaleMatrix44Bench : public Matrix44Bench {
+public:
+    PreScaleMatrix44Bench(void* param) : INHERITED(param, "prescale") {
+        fX = fY = fZ = SkDoubleToMScalar(1.5);
+    }
+protected:
+    virtual void performTest() {
+        fM0.reset();
+        for (int i = 0; i < 10; ++i) {
+            fM0.preScale(fX, fY, fZ);
+        }
+    }
+private:
+    SkMatrix44 fM0;
+    SkMScalar  fX, fY, fZ;
+    typedef Matrix44Bench INHERITED;
+};
+
+class InvertMatrix44Bench : public Matrix44Bench {
+public:
+    InvertMatrix44Bench(void* param) : INHERITED(param, "invert") {
+    fM0.set(0, 0, -1.1);
+    fM0.set(0, 1, 2.1);
+    fM0.set(0, 2, -3.1);
+    fM0.set(0, 3, 4.1);
+    fM0.set(1, 0, 5.1);
+    fM0.set(1, 1, -6.1);
+    fM0.set(1, 2, 7.1);
+    fM0.set(1, 3, 8.1);
+    fM0.set(2, 0, -9.1);
+    fM0.set(2, 1, 10.1);
+    fM0.set(2, 2, 11.1);
+    fM0.set(2, 3, -12.1);
+    fM0.set(3, 0, -13.1);
+    fM0.set(3, 1, 14.1);
+    fM0.set(3, 2, -15.1);
+    fM0.set(3, 3, 16.1);
+    }
+protected:
+    virtual void performTest() {
+        for (int i = 0; i < 10; ++i) {
+            fM0.invert(&fM1);
+        }
+    }
+private:
+    SkMatrix44 fM0, fM1;
+    typedef Matrix44Bench INHERITED;
+};
+
+class PostScaleMatrix44Bench : public Matrix44Bench {
+public:
+    PostScaleMatrix44Bench(void* param) : INHERITED(param, "postscale") {
+        fX = fY = fZ = SkDoubleToMScalar(1.5);
+    }
+protected:
+    virtual void performTest() {
+        fM0.reset();
+        for (int i = 0; i < 10; ++i) {
+            fM0.postScale(fX, fY, fZ);
+        }
+    }
+private:
+    SkMatrix44 fM0;
+    SkMScalar  fX, fY, fZ;
+    typedef Matrix44Bench INHERITED;
+};
+
+class SetConcatMatrix44Bench : public Matrix44Bench {
+public:
+    SetConcatMatrix44Bench(void* param) : INHERITED(param, "setconcat") {
+        fX = fY = fZ = SkDoubleToMScalar(1.5);
+        fM1.setScale(fX, fY, fZ);
+        fM2.setTranslate(fX, fY, fZ);
+    }
+protected:
+    virtual void performTest() {
+        fM0.reset();    // just to normalize this test with prescale/postscale
+        for (int i = 0; i < 10; ++i) {
+            fM0.setConcat(fM1, fM2);
+        }
+    }
+private:
+    SkMatrix44 fM0, fM1, fM2;
+    SkMScalar  fX, fY, fZ;
+    typedef Matrix44Bench INHERITED;
+};
+
+class GetTypeMatrix44Bench : public Matrix44Bench {
+public:
+    GetTypeMatrix44Bench(void* param) : INHERITED(param, "gettype") {}
+protected:
+    // Putting random generation of the matrix inside performTest()
+    // would help us avoid anomalous runs, but takes up 25% or
+    // more of the function time.
+    virtual void performTest() {
+        for (int i = 0; i < 20; ++i) {
+            fMatrix.set(1, 2, 1);   // to invalidate the type-cache
+            fMatrix.getType();
+        }
+    }
+private:
+    SkMatrix44 fMatrix;
+    typedef Matrix44Bench INHERITED;
+};
+
+DEF_BENCH( return new EqualsMatrix44Bench(p); )
+DEF_BENCH( return new PreScaleMatrix44Bench(p); )
+DEF_BENCH( return new PostScaleMatrix44Bench(p); )
+DEF_BENCH( return new InvertMatrix44Bench(p); )
+DEF_BENCH( return new SetConcatMatrix44Bench(p); )
+DEF_BENCH( return new GetTypeMatrix44Bench(p); )
diff --git a/bench/MatrixBench.cpp b/bench/MatrixBench.cpp
index a2e459a..f739715 100644
--- a/bench/MatrixBench.cpp
+++ b/bench/MatrixBench.cpp
@@ -16,6 +16,7 @@
 public:
     MatrixBench(void* param, const char name[]) : INHERITED(param) {
         fName.printf("matrix_%s", name);
+        fIsRendering = false;
     }
 
     virtual void performTest() = 0;
@@ -263,7 +264,6 @@
     typedef MatrixBench INHERITED;
 };
 
-#ifdef SK_SCALAR_IS_FLOAT
 class ScaleTransMixedMatrixBench : public MatrixBench {
  public:
     ScaleTransMixedMatrixBench(void* p) : INHERITED(p, "scaletrans_mixed"), fCount (16) {
@@ -340,29 +340,103 @@
     SkRandom fRandom;
     typedef MatrixBench INHERITED;
 };
-#endif
 
+class InvertMapRectMatrixBench : public MatrixBench {
+public:
+    InvertMapRectMatrixBench(void* param, const char* name, int flags)
+        : INHERITED(param, name)
+        , fFlags(flags) {
+        fMatrix.reset();
+        fIteration = 0;
+        if (flags & kScale_Flag) {
+            fMatrix.postScale(SkFloatToScalar(1.5f), SkFloatToScalar(2.5f));
+        }
+        if (flags & kTranslate_Flag) {
+            fMatrix.postTranslate(SkFloatToScalar(1.5f), SkFloatToScalar(2.5f));
+        }
+        if (flags & kRotate_Flag) {
+            fMatrix.postRotate(SkFloatToScalar(45.0f));
+        }
+        if (flags & kPerspective_Flag) {
+            fMatrix.setPerspX(SkFloatToScalar(1.5f));
+            fMatrix.setPerspY(SkFloatToScalar(2.5f));
+        }
+        if (0 == (flags & kUncachedTypeMask_Flag)) {
+            fMatrix.getType();
+        }
+    }
+    enum Flag {
+        kScale_Flag             = 0x01,
+        kTranslate_Flag         = 0x02,
+        kRotate_Flag            = 0x04,
+        kPerspective_Flag       = 0x08,
+        kUncachedTypeMask_Flag  = 0x10,
+    };
+protected:
+    virtual void performTest() {
+        if (fFlags & kUncachedTypeMask_Flag) {
+            // This will invalidate the typemask without
+            // changing the matrix.
+            fMatrix.setPerspX(fMatrix.getPerspX());
+        }
+        SkMatrix inv;
+        bool invertible = fMatrix.invert(&inv);
+        SkASSERT(invertible);
+        SkRect transformedRect;
+        // an arbitrary, small, non-zero rect to transform
+        SkRect srcRect = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10));
+        if (invertible) {
+            inv.mapRect(&transformedRect, srcRect);
+        }
+    }
+private:
+    SkMatrix fMatrix;
+    int fFlags;
+    unsigned fIteration;
+    typedef MatrixBench INHERITED;
+};
 
+///////////////////////////////////////////////////////////////////////////////
 
+DEF_BENCH( return new EqualsMatrixBench(p); )
+DEF_BENCH( return new ScaleMatrixBench(p); )
+DEF_BENCH( return new FloatConcatMatrixBench(p); )
+DEF_BENCH( return new FloatDoubleConcatMatrixBench(p); )
+DEF_BENCH( return new DoubleConcatMatrixBench(p); )
+DEF_BENCH( return new GetTypeMatrixBench(p); )
+DEF_BENCH( return new InvertMapRectMatrixBench(p, "invert_maprect_identity", 0); )
 
+DEF_BENCH(return new InvertMapRectMatrixBench(p,
+                                  "invert_maprect_rectstaysrect",
+                                  InvertMapRectMatrixBench::kScale_Flag |
+                                  InvertMapRectMatrixBench::kTranslate_Flag); )
 
-static SkBenchmark* M0(void* p) { return new EqualsMatrixBench(p); }
-static SkBenchmark* M1(void* p) { return new ScaleMatrixBench(p); }
-static SkBenchmark* M2(void* p) { return new FloatConcatMatrixBench(p); }
-static SkBenchmark* M3(void* p) { return new FloatDoubleConcatMatrixBench(p); }
-static SkBenchmark* M4(void* p) { return new DoubleConcatMatrixBench(p); }
-static SkBenchmark* M5(void* p) { return new GetTypeMatrixBench(p); }
+DEF_BENCH(return new InvertMapRectMatrixBench(p,
+                                  "invert_maprect_translate",
+                                  InvertMapRectMatrixBench::kTranslate_Flag); )
 
-static BenchRegistry gReg0(M0);
-static BenchRegistry gReg1(M1);
-static BenchRegistry gReg2(M2);
-static BenchRegistry gReg3(M3);
-static BenchRegistry gReg4(M4);
-static BenchRegistry gReg5(M5);
+DEF_BENCH(return new InvertMapRectMatrixBench(p,
+                                  "invert_maprect_nonpersp",
+                                  InvertMapRectMatrixBench::kScale_Flag |
+                                  InvertMapRectMatrixBench::kRotate_Flag |
+                                  InvertMapRectMatrixBench::kTranslate_Flag); )
 
-#ifdef SK_SCALAR_IS_FLOAT
-static SkBenchmark* FlM0(void* p) { return new ScaleTransMixedMatrixBench(p); }
-static SkBenchmark* FlM1(void* p) { return new ScaleTransDoubleMatrixBench(p); }
-static BenchRegistry gFlReg5(FlM0);
-static BenchRegistry gFlReg6(FlM1);
-#endif
+DEF_BENCH( return new InvertMapRectMatrixBench(p,
+                               "invert_maprect_persp",
+                               InvertMapRectMatrixBench::kPerspective_Flag); )
+
+DEF_BENCH( return new InvertMapRectMatrixBench(p,
+                           "invert_maprect_typemask_rectstaysrect",
+                           InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
+                           InvertMapRectMatrixBench::kScale_Flag |
+                           InvertMapRectMatrixBench::kTranslate_Flag); )
+
+DEF_BENCH( return new InvertMapRectMatrixBench(p,
+                           "invert_maprect_typemask_nonpersp",
+                           InvertMapRectMatrixBench::kUncachedTypeMask_Flag |
+                           InvertMapRectMatrixBench::kScale_Flag |
+                           InvertMapRectMatrixBench::kRotate_Flag |
+                           InvertMapRectMatrixBench::kTranslate_Flag); )
+
+DEF_BENCH( return new ScaleTransMixedMatrixBench(p); )
+DEF_BENCH( return new ScaleTransDoubleMatrixBench(p); )
diff --git a/bench/MatrixConvolutionBench.cpp b/bench/MatrixConvolutionBench.cpp
new file mode 100644
index 0000000..dfa05db
--- /dev/null
+++ b/bench/MatrixConvolutionBench.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkString.h"
+#include "SkMatrixConvolutionImageFilter.h"
+
+class MatrixConvolutionBench : public SkBenchmark {
+    SkMatrixConvolutionImageFilter::TileMode  fTileMode;
+
+public:
+    MatrixConvolutionBench(void* param, SkMatrixConvolutionImageFilter::TileMode tileMode, bool convolveAlpha)
+        : INHERITED(param), fName("matrixconvolution") {
+        SkISize kernelSize = SkISize::Make(3, 3);
+        SkScalar kernel[9] = {
+            SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
+            SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
+            SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
+        };
+        SkScalar gain = SkFloatToScalar(0.3f), bias = SkIntToScalar(100);
+        SkIPoint target = SkIPoint::Make(1, 1);
+        fFilter = new SkMatrixConvolutionImageFilter(kernelSize, kernel, gain, bias, target, tileMode, convolveAlpha);
+    }
+
+    ~MatrixConvolutionBench() {
+        fFilter->unref();
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        this->setupPaint(&paint);
+        paint.setAntiAlias(true);
+        SkRandom rand;
+        for (int i = 0; i < SkBENCHLOOP(3); i++) {
+            SkRect r = SkRect::MakeWH(rand.nextUScalar1() * 400,
+                                      rand.nextUScalar1() * 400);
+            paint.setImageFilter(fFilter);
+            canvas->drawOval(r, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+    SkMatrixConvolutionImageFilter* fFilter;
+    SkString fName;
+};
+
+static SkBenchmark* Fact00(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClamp_TileMode, true); }
+static SkBenchmark* Fact01(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kRepeat_TileMode, true); }
+static SkBenchmark* Fact02(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, true); }
+static SkBenchmark* Fact03(void* p) { return new MatrixConvolutionBench(p, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, false); }
+
+static BenchRegistry gReg00(Fact00);
+static BenchRegistry gReg01(Fact01);
+static BenchRegistry gReg02(Fact02);
+static BenchRegistry gReg03(Fact03);
diff --git a/bench/MemoryBench.cpp b/bench/MemoryBench.cpp
new file mode 100644
index 0000000..ab9c07d
--- /dev/null
+++ b/bench/MemoryBench.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkChunkAlloc.h"
+#include "SkString.h"
+
+class ChunkAllocBench : public SkBenchmark {
+    SkString    fName;
+    size_t      fMinSize;
+
+    enum {
+        N = SkBENCHLOOP(1000)
+    };
+public:
+    ChunkAllocBench(void* param, size_t minSize) : INHERITED(param) {
+        fMinSize = minSize;
+        fName.printf("chunkalloc_" SK_SIZE_T_SPECIFIER, minSize);
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        size_t inc = fMinSize >> 4;
+        SkASSERT(inc > 0);
+        size_t total = fMinSize * 64;
+
+        SkChunkAlloc alloc(fMinSize);
+
+        for (int i = 0; i < N; ++i) {
+            size_t size = 0;
+            int calls = 0;
+            while (size < total) {
+                alloc.allocThrow(inc);
+                size += inc;
+                calls += 1;
+            }
+            alloc.reset();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* F0(void* p) { return new ChunkAllocBench(p, 64); }
+static SkBenchmark* F1(void* p) { return new ChunkAllocBench(p, 8*1024); }
+
+static BenchRegistry gR0(F0);
+static BenchRegistry gR1(F1);
diff --git a/bench/MorphologyBench.cpp b/bench/MorphologyBench.cpp
new file mode 100644
index 0000000..f9657ba
--- /dev/null
+++ b/bench/MorphologyBench.cpp
@@ -0,0 +1,116 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkMorphologyImageFilter.h"
+
+#define SMALL   SkIntToScalar(2)
+#define REAL    SkFloatToScalar(1.5f)
+#define BIG     SkIntToScalar(10)
+
+namespace {
+
+enum MorphologyType {
+    kErode_MT,
+    kDilate_MT
+};
+
+}
+
+static const char* gStyleName[] = {
+    "erode",
+    "dilate"
+};
+
+class MorphologyBench : public SkBenchmark {
+    SkScalar       fRadius;
+    MorphologyType fStyle;
+    SkString       fName;
+
+public:
+    MorphologyBench(void* param, SkScalar rad, MorphologyType style)
+        :  INHERITED(param) {
+        fRadius = rad;
+        fStyle = style;
+        const char* name = rad > 0 ? gStyleName[style] : "none";
+        if (SkScalarFraction(rad) != 0) {
+            fName.printf("morph_%.2f_%s", SkScalarToFloat(rad), name);
+        } else {
+            fName.printf("morph_%d_%s", SkScalarRound(rad), name);
+        }
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        this->setupPaint(&paint);
+
+        paint.setAntiAlias(true);
+
+        SkRandom rand;
+        for (int i = 0; i < SkBENCHLOOP(3); i++) {
+            SkRect r = SkRect::MakeWH(rand.nextUScalar1() * 400,
+                                      rand.nextUScalar1() * 400);
+            r.offset(fRadius, fRadius);
+
+            if (fRadius > 0) {
+                SkMorphologyImageFilter* mf = NULL;
+                switch (fStyle) {
+                case kDilate_MT:
+                    mf = new SkDilateImageFilter(SkScalarFloorToInt(fRadius),
+                                                 SkScalarFloorToInt(fRadius));
+                    break;
+                case kErode_MT:
+                    mf = new SkErodeImageFilter(SkScalarFloorToInt(fRadius),
+                                                SkScalarFloorToInt(fRadius));
+                    break;
+                }
+                paint.setImageFilter(mf)->unref();
+            }
+            canvas->drawOval(r, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* Fact00(void* p) { return new MorphologyBench(p, SMALL, kErode_MT); }
+static SkBenchmark* Fact01(void* p) { return new MorphologyBench(p, SMALL, kDilate_MT); }
+
+static SkBenchmark* Fact10(void* p) { return new MorphologyBench(p, BIG, kErode_MT); }
+static SkBenchmark* Fact11(void* p) { return new MorphologyBench(p, BIG, kDilate_MT); }
+
+static SkBenchmark* Fact20(void* p) { return new MorphologyBench(p, REAL, kErode_MT); }
+static SkBenchmark* Fact21(void* p) { return new MorphologyBench(p, REAL, kDilate_MT); }
+
+static SkBenchmark* FactNone(void* p) { return new MorphologyBench(p, 0, kErode_MT); }
+
+// Fixed point can be 100x slower than float on these tests, causing
+// bench to timeout.
+#ifndef SK_SCALAR_IS_FIXED
+
+static BenchRegistry gReg00(Fact00);
+static BenchRegistry gReg01(Fact01);
+
+static BenchRegistry gReg10(Fact10);
+static BenchRegistry gReg11(Fact11);
+
+static BenchRegistry gReg20(Fact20);
+static BenchRegistry gReg21(Fact21);
+
+static BenchRegistry gRegNone(FactNone);
+
+#endif
diff --git a/bench/MutexBench.cpp b/bench/MutexBench.cpp
index af8a840..6c152a0 100644
--- a/bench/MutexBench.cpp
+++ b/bench/MutexBench.cpp
@@ -14,7 +14,7 @@
     };
 public:
     MutexBench(void* param) : INHERITED(param) {
-
+        fIsRendering = false;
     }
 protected:
     virtual const char* onGetName() {
@@ -40,4 +40,3 @@
 static SkBenchmark* Fact(void* p) { return new MutexBench(p); }
 
 static BenchRegistry gReg01(Fact);
-
diff --git a/bench/PathBench.cpp b/bench/PathBench.cpp
index f9de53c..41a6922 100644
--- a/bench/PathBench.cpp
+++ b/bench/PathBench.cpp
@@ -13,6 +13,8 @@
 #include "SkRandom.h"
 #include "SkShader.h"
 #include "SkString.h"
+#include "SkTArray.h"
+
 
 enum Flags {
     kStroke_Flag = 1 << 0,
@@ -42,7 +44,7 @@
     virtual int complexity() { return 0; }
 
 protected:
-    virtual const char* onGetName() {
+    virtual const char* onGetName() SK_OVERRIDE {
         fName.printf("path_%s_%s_",
                      fFlags & kStroke_Flag ? "stroke" : "fill",
                      fFlags & kBig_Flag ? "big" : "small");
@@ -50,7 +52,7 @@
         return fName.c_str();
     }
 
-    virtual void onDraw(SkCanvas* canvas) {
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
         SkPaint paint(fPaint);
         this->setupPaint(&paint);
 
@@ -80,11 +82,11 @@
 class TrianglePathBench : public PathBench {
 public:
     TrianglePathBench(void* param, Flags flags) : INHERITED(param, flags) {}
-    
-    virtual void appendName(SkString* name) {
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
         name->append("triangle");
     }
-    virtual void makePath(SkPath* path) {
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
         static const int gCoord[] = {
             10, 10, 15, 5, 20, 20
         };
@@ -100,11 +102,11 @@
 class RectPathBench : public PathBench {
 public:
     RectPathBench(void* param, Flags flags) : INHERITED(param, flags) {}
-    
-    virtual void appendName(SkString* name) {
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
         name->append("rect");
     }
-    virtual void makePath(SkPath* path) {
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
         SkRect r = { 10, 10, 20, 20 };
         path->addRect(r);
     }
@@ -115,23 +117,38 @@
 class OvalPathBench : public PathBench {
 public:
     OvalPathBench(void* param, Flags flags) : INHERITED(param, flags) {}
-    
-    virtual void appendName(SkString* name) {
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
         name->append("oval");
     }
-    virtual void makePath(SkPath* path) {
-        SkRect r = { 10, 10, 20, 20 };
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
+        SkRect r = { 10, 10, 23, 20 };
         path->addOval(r);
     }
 private:
     typedef PathBench INHERITED;
 };
 
+class CirclePathBench: public PathBench {
+public:
+    CirclePathBench(void* param, Flags flags) : INHERITED(param, flags) {}
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
+        name->append("circle");
+    }
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
+        path->addCircle(SkIntToScalar(20), SkIntToScalar(20),
+                        SkIntToScalar(10));
+    }
+private:
+    typedef PathBench INHERITED;
+};
+
 class SawToothPathBench : public PathBench {
 public:
     SawToothPathBench(void* param, Flags flags) : INHERITED(param, flags) {}
-    
-    virtual void appendName(SkString* name) {
+
+    virtual void appendName(SkString* name) SK_OVERRIDE {
         name->append("sawtooth");
     }
     virtual void makePath(SkPath* path) {
@@ -152,7 +169,7 @@
         path->lineTo(x0, y + 2 * dy);
         path->close();
     }
-    virtual int complexity() { return 1; }
+    virtual int complexity() SK_OVERRIDE { return 1; }
 private:
     typedef PathBench INHERITED;
 };
@@ -163,10 +180,10 @@
         : INHERITED(param, flags) {
     }
 
-    virtual void appendName(SkString* name) {
+    virtual void appendName(SkString* name) SK_OVERRIDE {
         name->append("long_curved");
     }
-    virtual void makePath(SkPath* path) {
+    virtual void makePath(SkPath* path) SK_OVERRIDE {
         SkRandom rand (12);
         int i;
         for (i = 0; i < 100; i++) {
@@ -177,7 +194,7 @@
         }
         path->close();
     }
-    virtual int complexity() { return 2; }
+    virtual int complexity() SK_OVERRIDE { return 2; }
 private:
     typedef PathBench INHERITED;
 };
@@ -188,21 +205,672 @@
         : INHERITED(param, flags) {
     }
 
-    virtual void appendName(SkString* name) {
+    virtual void appendName(SkString* name) SK_OVERRIDE {
         name->append("long_line");
     }
-    virtual void makePath(SkPath* path) {
+    virtual void makePath(SkPath* path) SK_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);
         }
     }
-    virtual int complexity() { return 2; }
+    virtual int complexity() SK_OVERRIDE { return 2; }
 private:
     typedef PathBench INHERITED;
 };
 
+class RandomPathBench : public SkBenchmark {
+public:
+    RandomPathBench(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+
+protected:
+    void createData(int minVerbs,
+                    int maxVerbs,
+                    bool allowMoves = true,
+                    SkRect* bounds = NULL) {
+        SkRect tempBounds;
+        if (NULL == bounds) {
+            tempBounds.setXYWH(0, 0, SK_Scalar1, SK_Scalar1);
+            bounds = &tempBounds;
+        }
+        fVerbCnts.reset(kNumVerbCnts);
+        for (int i = 0; i < kNumVerbCnts; ++i) {
+            fVerbCnts[i] = fRandom.nextRangeU(minVerbs, maxVerbs + 1);
+        }
+        fVerbs.reset(kNumVerbs);
+        for (int i = 0; i < kNumVerbs; ++i) {
+            do {
+                fVerbs[i] = static_cast<SkPath::Verb>(fRandom.nextULessThan(SkPath::kDone_Verb));
+            } while (!allowMoves && SkPath::kMove_Verb == fVerbs[i]);
+        }
+        fPoints.reset(kNumPoints);
+        for (int i = 0; i < kNumPoints; ++i) {
+            fPoints[i].set(fRandom.nextRangeScalar(bounds->fLeft, bounds->fRight),
+                           fRandom.nextRangeScalar(bounds->fTop, bounds->fBottom));
+        }
+        this->restartMakingPaths();
+    }
+
+    void restartMakingPaths() {
+        fCurrPath = 0;
+        fCurrVerb = 0;
+        fCurrPoint = 0;
+    }
+
+    void makePath(SkPath* path) {
+        int vCount = fVerbCnts[(fCurrPath++) & (kNumVerbCnts - 1)];
+        for (int v = 0; v < vCount; ++v) {
+            int verb = fVerbs[(fCurrVerb++) & (kNumVerbs - 1)];
+            switch (verb) {
+                case SkPath::kMove_Verb:
+                    path->moveTo(fPoints[(fCurrPoint++) & (kNumPoints - 1)]);
+                    break;
+                case SkPath::kLine_Verb:
+                    path->lineTo(fPoints[(fCurrPoint++) & (kNumPoints - 1)]);
+                    break;
+                case SkPath::kQuad_Verb:
+                    path->quadTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
+                                 fPoints[(fCurrPoint + 1) & (kNumPoints - 1)]);
+                    fCurrPoint += 2;
+                    break;
+                case SkPath::kCubic_Verb:
+                    path->cubicTo(fPoints[(fCurrPoint + 0) & (kNumPoints - 1)],
+                                  fPoints[(fCurrPoint + 1) & (kNumPoints - 1)],
+                                  fPoints[(fCurrPoint + 2) & (kNumPoints - 1)]);
+                    fCurrPoint += 3;
+                    break;
+                case SkPath::kClose_Verb:
+                    path->close();
+                    break;
+                default:
+                    SkDEBUGFAIL("Unexpected path verb");
+                    break;
+            }
+        }
+    }
+
+    void finishedMakingPaths() {
+        fVerbCnts.reset(0);
+        fVerbs.reset(0);
+        fPoints.reset(0);
+    }
+
+private:
+    enum {
+        // these should all be pow 2
+        kNumVerbCnts = 1 << 5,
+        kNumVerbs    = 1 << 5,
+        kNumPoints   = 1 << 5,
+    };
+    SkAutoTArray<int>           fVerbCnts;
+    SkAutoTArray<SkPath::Verb>  fVerbs;
+    SkAutoTArray<SkPoint>       fPoints;
+    int                         fCurrPath;
+    int                         fCurrVerb;
+    int                         fCurrPoint;
+    SkRandom                    fRandom;
+    typedef SkBenchmark INHERITED;
+};
+
+class PathCreateBench : public RandomPathBench {
+public:
+    PathCreateBench(void* param) : INHERITED(param) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(5000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "path_create";
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        this->createData(10, 100);
+        fPaths.reset(kPathCnt);
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        for (int i = 0; i < N; ++i) {
+            this->makePath(&fPaths[i & (kPathCnt - 1)]);
+        }
+        this->restartMakingPaths();
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        this->finishedMakingPaths();
+        fPaths.reset(0);
+    }
+
+private:
+    enum {
+        // must be a pow 2
+        kPathCnt = 1 << 5,
+    };
+    SkAutoTArray<SkPath> fPaths;
+
+    typedef RandomPathBench INHERITED;
+};
+
+class PathCopyBench : public RandomPathBench {
+public:
+    PathCopyBench(void* param) : INHERITED(param) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(30000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "path_copy";
+    }
+    virtual void onPreDraw() SK_OVERRIDE {
+        this->createData(10, 100);
+        fPaths.reset(kPathCnt);
+        fCopies.reset(kPathCnt);
+        for (int i = 0; i < kPathCnt; ++i) {
+            this->makePath(&fPaths[i]);
+        }
+        this->finishedMakingPaths();
+    }
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        for (int i = 0; i < N; ++i) {
+            int idx = i & (kPathCnt - 1);
+            fCopies[idx] = fPaths[idx];
+        }
+    }
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths.reset(0);
+        fCopies.reset(0);
+    }
+
+private:
+    enum {
+        // must be a pow 2
+        kPathCnt = 1 << 5,
+    };
+    SkAutoTArray<SkPath> fPaths;
+    SkAutoTArray<SkPath> fCopies;
+
+    typedef RandomPathBench INHERITED;
+};
+
+class PathTransformBench : public RandomPathBench {
+public:
+    PathTransformBench(bool inPlace, void* param)
+        : INHERITED(param)
+        , fInPlace(inPlace) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(30000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fInPlace ? "path_transform_in_place" : "path_transform_copy";
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        fMatrix.setScale(5 * SK_Scalar1, 6 * SK_Scalar1);
+        this->createData(10, 100);
+        fPaths.reset(kPathCnt);
+        for (int i = 0; i < kPathCnt; ++i) {
+            this->makePath(&fPaths[i]);
+        }
+        this->finishedMakingPaths();
+        if (!fInPlace) {
+            fTransformed.reset(kPathCnt);
+        }
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        if (fInPlace) {
+            for (int i = 0; i < N; ++i) {
+                fPaths[i & (kPathCnt - 1)].transform(fMatrix);
+            }
+        } else {
+            for (int i = 0; i < N; ++i) {
+                int idx = i & (kPathCnt - 1);
+                fPaths[idx].transform(fMatrix, &fTransformed[idx]);
+            }
+        }
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths.reset(0);
+        fTransformed.reset(0);
+    }
+
+private:
+    enum {
+        // must be a pow 2
+        kPathCnt = 1 << 5,
+    };
+    SkAutoTArray<SkPath> fPaths;
+    SkAutoTArray<SkPath> fTransformed;
+
+    SkMatrix fMatrix;
+    bool fInPlace;
+    typedef RandomPathBench INHERITED;
+};
+
+class PathEqualityBench : public RandomPathBench {
+public:
+    PathEqualityBench(void* param)
+        : INHERITED(param) {
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(40000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "path_equality_50%";
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        fParity = 0;
+        this->createData(10, 100);
+        fPaths.reset(kPathCnt);
+        fCopies.reset(kPathCnt);
+        for (int i = 0; i < kPathCnt; ++i) {
+            this->makePath(&fPaths[i]);
+            fCopies[i] = fPaths[i];
+        }
+        this->finishedMakingPaths();
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        for (int i = 0; i < N; ++i) {
+            int idx = i & (kPathCnt - 1);
+            fParity ^= (fPaths[idx] == fCopies[idx & ~0x1]);
+        }
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths.reset(0);
+        fCopies.reset(0);
+    }
+
+private:
+    bool fParity; // attempt to keep compiler from optimizing out the ==
+    enum {
+        // must be a pow 2
+        kPathCnt = 1 << 5,
+    };
+    SkAutoTArray<SkPath> fPaths;
+    SkAutoTArray<SkPath> fCopies;
+    typedef RandomPathBench INHERITED;
+};
+
+class SkBench_AddPathTest : public RandomPathBench {
+public:
+    enum AddType {
+        kAdd_AddType,
+        kAddTrans_AddType,
+        kAddMatrix_AddType,
+        kPathTo_AddType,
+        kReverseAdd_AddType,
+        kReversePathTo_AddType,
+    };
+
+    SkBench_AddPathTest(AddType type, void* param)
+        : INHERITED(param)
+        , fType(type) {
+        fMatrix.setRotate(60 * SK_Scalar1);
+    }
+
+protected:
+    enum { N = SkBENCHLOOP(15000) };
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        switch (fType) {
+            case kAdd_AddType:
+                return "path_add_path";
+            case kAddTrans_AddType:
+                return "path_add_path_trans";
+            case kAddMatrix_AddType:
+                return "path_add_path_matrix";
+            case kPathTo_AddType:
+                return "path_path_to";
+            case kReverseAdd_AddType:
+                return "path_reverse_add_path";
+            case kReversePathTo_AddType:
+                return "path_reverse_path_to";
+            default:
+                SkDEBUGFAIL("Bad add type");
+                return "";
+        }
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        // pathTo and reversePathTo assume a single contour path.
+        bool allowMoves = kPathTo_AddType != fType &&
+                          kReversePathTo_AddType != fType;
+        this->createData(10, 100, allowMoves);
+        fPaths0.reset(kPathCnt);
+        fPaths1.reset(kPathCnt);
+        for (int i = 0; i < kPathCnt; ++i) {
+            this->makePath(&fPaths0[i]);
+            this->makePath(&fPaths1[i]);
+        }
+        this->finishedMakingPaths();
+    }
+
+    virtual void onDraw(SkCanvas*) SK_OVERRIDE {
+        switch (fType) {
+            case kAdd_AddType:
+                for (int i = 0; i < N; ++i) {
+                    int idx = i & (kPathCnt - 1);
+                    SkPath result = fPaths0[idx];
+                    result.addPath(fPaths1[idx]);
+                }
+                break;
+            case kAddTrans_AddType:
+                for (int i = 0; i < N; ++i) {
+                    int idx = i & (kPathCnt - 1);
+                    SkPath result = fPaths0[idx];
+                    result.addPath(fPaths1[idx], 2 * SK_Scalar1, 5 * SK_Scalar1);
+                }
+                break;
+            case kAddMatrix_AddType:
+                for (int i = 0; i < N; ++i) {
+                    int idx = i & (kPathCnt - 1);
+                    SkPath result = fPaths0[idx];
+                    result.addPath(fPaths1[idx], fMatrix);
+                }
+                break;
+            case kPathTo_AddType:
+                for (int i = 0; i < N; ++i) {
+                    int idx = i & (kPathCnt - 1);
+                    SkPath result = fPaths0[idx];
+                    result.pathTo(fPaths1[idx]);
+                }
+                break;
+            case kReverseAdd_AddType:
+                for (int i = 0; i < N; ++i) {
+                    int idx = i & (kPathCnt - 1);
+                    SkPath result = fPaths0[idx];
+                    result.reverseAddPath(fPaths1[idx]);
+                }
+                break;
+            case kReversePathTo_AddType:
+                for (int i = 0; i < N; ++i) {
+                    int idx = i & (kPathCnt - 1);
+                    SkPath result = fPaths0[idx];
+                    result.reversePathTo(fPaths1[idx]);
+                }
+                break;
+        }
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        fPaths0.reset(0);
+        fPaths1.reset(0);
+    }
+
+private:
+    AddType fType; // or reverseAddPath
+    enum {
+        // must be a pow 2
+        kPathCnt = 1 << 5,
+    };
+    SkAutoTArray<SkPath> fPaths0;
+    SkAutoTArray<SkPath> fPaths1;
+    SkMatrix         fMatrix;
+    typedef RandomPathBench INHERITED;
+};
+
+
+class CirclesBench : public SkBenchmark {
+protected:
+    SkString            fName;
+    Flags               fFlags;
+
+    enum {
+        N = SkBENCHLOOP(100)
+    };
+public:
+    CirclesBench(void* param, Flags flags) : INHERITED(param), fFlags(flags) {
+        fName.printf("circles_%s", fFlags & kStroke_Flag ? "stroke" : "fill");
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint paint;
+
+        paint.setColor(SK_ColorBLACK);
+        paint.setAntiAlias(true);
+        if (fFlags & kStroke_Flag) {
+            paint.setStyle(SkPaint::kStroke_Style);
+        }
+
+        SkRandom rand;
+
+        SkRect r;
+
+        for (int i = 0; i < 5000; ++i) {
+            SkScalar radius = rand.nextUScalar1() * 3;
+            r.fLeft = rand.nextUScalar1() * 300;
+            r.fTop =  rand.nextUScalar1() * 300;
+            r.fRight =  r.fLeft + 2 * radius;
+            r.fBottom = r.fTop + 2 * radius;
+
+            if (fFlags & kStroke_Flag) {
+                paint.setStrokeWidth(rand.nextUScalar1() * 5.0f);
+            }
+
+            SkPath temp;
+
+            // mimic how Chrome does circles
+            temp.arcTo(r, 0, 0, false);
+            temp.addOval(r, SkPath::kCCW_Direction);
+            temp.arcTo(r, 360, 0, true);
+            temp.close();
+
+            canvas->drawPath(temp, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+
+// Chrome creates its own round rects with each corner possibly being different.
+// In its "zero radius" incarnation it creates degenerate round rects.
+// Note: PathTest::test_arb_round_rect_is_convex and
+// test_arb_zero_rad_round_rect_is_rect perform almost exactly
+// the same test (but with no drawing)
+class ArbRoundRectBench : public SkBenchmark {
+protected:
+    SkString            fName;
+
+    enum {
+        N = SkBENCHLOOP(100)
+    };
+public:
+    ArbRoundRectBench(void* param, bool zeroRad) : INHERITED(param), fZeroRad(zeroRad) {
+        if (zeroRad) {
+            fName.printf("zeroradroundrect");
+        } else {
+            fName.printf("arbroundrect");
+        }
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    static void add_corner_arc(SkPath* path, const SkRect& rect,
+                               SkScalar xIn, SkScalar yIn,
+                               int startAngle)
+    {
+
+        SkScalar rx = SkMinScalar(rect.width(), xIn);
+        SkScalar ry = SkMinScalar(rect.height(), yIn);
+
+        SkRect arcRect;
+        arcRect.set(-rx, -ry, rx, ry);
+        switch (startAngle) {
+        case 0:
+            arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
+            break;
+        case 90:
+            arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
+            break;
+        case 180:
+            arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
+            break;
+        case 270:
+            arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
+            break;
+        default:
+            break;
+        }
+
+        path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
+    }
+
+    static void make_arb_round_rect(SkPath* path, const SkRect& r,
+                                    SkScalar xCorner, SkScalar yCorner) {
+        // we are lazy here and use the same x & y for each corner
+        add_corner_arc(path, r, xCorner, yCorner, 270);
+        add_corner_arc(path, r, xCorner, yCorner, 0);
+        add_corner_arc(path, r, xCorner, yCorner, 90);
+        add_corner_arc(path, r, xCorner, yCorner, 180);
+        path->close();
+
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
+        SkASSERT(path->isConvex());
+#endif
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkRandom rand;
+        SkRect r;
+
+        for (int i = 0; i < 5000; ++i) {
+            SkPaint paint;
+            paint.setColor(0xff000000 | rand.nextU());
+            paint.setAntiAlias(true);
+
+            SkScalar size = rand.nextUScalar1() * 30;
+            if (size < SK_Scalar1) {
+                continue;
+            }
+            r.fLeft = rand.nextUScalar1() * 300;
+            r.fTop =  rand.nextUScalar1() * 300;
+            r.fRight =  r.fLeft + 2 * size;
+            r.fBottom = r.fTop + 2 * size;
+
+            SkPath temp;
+
+            if (fZeroRad) {
+                make_arb_round_rect(&temp, r, 0, 0);
+
+                SkASSERT(temp.isRect(NULL));
+            } else {
+                make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
+            }
+
+            canvas->drawPath(temp, paint);
+        }
+    }
+
+private:
+    bool fZeroRad;      // should 0 radius rounds rects be tested?
+
+    typedef SkBenchmark INHERITED;
+};
+
+class ConservativelyContainsBench : public SkBenchmark {
+public:
+    enum Type {
+        kRect_Type,
+        kRoundRect_Type,
+        kOval_Type,
+    };
+
+    ConservativelyContainsBench(void* param, Type type) : INHERITED(param) {
+        fIsRendering = false;
+        fParity = false;
+        fName = "conservatively_contains_";
+        switch (type) {
+            case kRect_Type:
+                fName.append("rect");
+                fPath.addRect(kBaseRect);
+                break;
+            case kRoundRect_Type:
+                fName.append("round_rect");
+                fPath.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1]);
+                break;
+            case kOval_Type:
+                fName.append("oval");
+                fPath.addOval(kBaseRect);
+                break;
+        }
+    }
+
+private:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        for (int i = 0; i < N; ++i) {
+            const SkRect& rect = fQueryRects[i % kQueryRectCnt];
+            fParity = fParity != fPath.conservativelyContainsRect(rect);
+        }
+    }
+
+    virtual void onPreDraw() SK_OVERRIDE {
+        fQueryRects.setCount(kQueryRectCnt);
+
+        SkRandom rand;
+        for (int i = 0; i < kQueryRectCnt; ++i) {
+            SkSize size;
+            SkPoint xy;
+            size.fWidth = rand.nextRangeScalar(kQueryMin.fWidth,  kQueryMax.fWidth);
+            size.fHeight = rand.nextRangeScalar(kQueryMin.fHeight, kQueryMax.fHeight);
+            xy.fX = rand.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth);
+            xy.fY = rand.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight);
+
+            fQueryRects[i] = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
+        }
+    }
+
+    virtual void onPostDraw() SK_OVERRIDE {
+        fQueryRects.setCount(0);
+    }
+
+    enum {
+        N = SkBENCHLOOP(100000),
+        kQueryRectCnt = 400,
+    };
+    static const SkRect kBounds;   // bounds for all random query rects
+    static const SkSize kQueryMin; // minimum query rect size, should be <= kQueryMax
+    static const SkSize kQueryMax; // max query rect size, should < kBounds
+    static const SkRect kBaseRect; // rect that is used to construct the path
+    static const SkScalar kRRRadii[2]; // x and y radii for round rect
+
+    SkString            fName;
+    SkPath              fPath;
+    bool                fParity;
+    SkTDArray<SkRect>   fQueryRects;
+
+    typedef SkBenchmark INHERITED;
+};
+
+const SkRect ConservativelyContainsBench::kBounds = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
+const SkSize ConservativelyContainsBench::kQueryMin = SkSize::Make(SkIntToScalar(1), SkIntToScalar(1));
+const SkSize ConservativelyContainsBench::kQueryMax = SkSize::Make(SkIntToScalar(40), SkIntToScalar(40));
+const SkRect ConservativelyContainsBench::kBaseRect = SkRect::MakeXYWH(SkIntToScalar(25), SkIntToScalar(25), SkIntToScalar(50), SkIntToScalar(50));
+const SkScalar ConservativelyContainsBench::kRRRadii[2] = {SkIntToScalar(5), SkIntToScalar(10)};
 
 static SkBenchmark* FactT00(void* p) { return new TrianglePathBench(p, FLAGS00); }
 static SkBenchmark* FactT01(void* p) { return new TrianglePathBench(p, FLAGS01); }
@@ -219,6 +887,11 @@
 static SkBenchmark* FactO10(void* p) { return new OvalPathBench(p, FLAGS10); }
 static SkBenchmark* FactO11(void* p) { return new OvalPathBench(p, FLAGS11); }
 
+static SkBenchmark* FactC00(void* p) { return new CirclePathBench(p, FLAGS00); }
+static SkBenchmark* FactC01(void* p) { return new CirclePathBench(p, FLAGS01); }
+static SkBenchmark* FactC10(void* p) { return new CirclePathBench(p, FLAGS10); }
+static SkBenchmark* FactC11(void* p) { return new CirclePathBench(p, FLAGS11); }
+
 static SkBenchmark* FactS00(void* p) { return new SawToothPathBench(p, FLAGS00); }
 static SkBenchmark* FactS01(void* p) { return new SawToothPathBench(p, FLAGS01); }
 
@@ -252,6 +925,11 @@
 static BenchRegistry gRegO10(FactO10);
 static BenchRegistry gRegO11(FactO11);
 
+static BenchRegistry gRegC00(FactC00);
+static BenchRegistry gRegC01(FactC01);
+static BenchRegistry gRegC10(FactC10);
+static BenchRegistry gRegC11(FactC11);
+
 static BenchRegistry gRegS00(FactS00);
 static BenchRegistry gRegS01(FactS01);
 
@@ -261,3 +939,52 @@
 static BenchRegistry gRegLL00(FactLL00);
 static BenchRegistry gRegLL01(FactLL01);
 
+static SkBenchmark* FactCreate(void* p) { return new PathCreateBench(p); }
+static BenchRegistry gRegCreate(FactCreate);
+
+static SkBenchmark* FactCopy(void* p) { return new PathCopyBench(p); }
+static BenchRegistry gRegCopy(FactCopy);
+
+static SkBenchmark* FactPathTransformInPlace(void* p) { return new PathTransformBench(true, p); }
+static BenchRegistry gRegPathTransformInPlace(FactPathTransformInPlace);
+
+static SkBenchmark* FactPathTransformCopy(void* p) { return new PathTransformBench(false, p); }
+static BenchRegistry gRegPathTransformCopy(FactPathTransformCopy);
+
+static SkBenchmark* FactEquality(void* p) { return new PathEqualityBench(p); }
+static BenchRegistry gRegEquality(FactEquality);
+
+static SkBenchmark* FactAdd(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kAdd_AddType, p); }
+static SkBenchmark* FactAddTrans(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kAddTrans_AddType, p); }
+static SkBenchmark* FactAddMatrix(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kAddMatrix_AddType, p); }
+static SkBenchmark* FactPathTo(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kPathTo_AddType, p); }
+static SkBenchmark* FactReverseAdd(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kReverseAdd_AddType, p); }
+static SkBenchmark* FactReverseTo(void* p) { return new SkBench_AddPathTest(SkBench_AddPathTest::kReversePathTo_AddType, p); }
+
+static BenchRegistry gRegAdd(FactAdd);
+static BenchRegistry gRegAddTrans(FactAddTrans);
+static BenchRegistry gRegAddMatrix(FactAddMatrix);
+static BenchRegistry gRegPathTo(FactPathTo);
+static BenchRegistry gRegReverseAdd(FactReverseAdd);
+static BenchRegistry gRegReverseTo(FactReverseTo);
+
+static SkBenchmark* CirclesTest(void* p) { return new CirclesBench(p, FLAGS00); }
+static BenchRegistry gRegCirclesTest(CirclesTest);
+
+static SkBenchmark* CirclesStrokeTest(void* p) { return new CirclesBench(p, FLAGS01); }
+static BenchRegistry gRegCirclesStrokeTest(CirclesStrokeTest);
+
+static SkBenchmark* ArbRoundRectTest(void* p) { return new ArbRoundRectBench(p, false); }
+static BenchRegistry gRegArbRoundRectTest(ArbRoundRectTest);
+
+static SkBenchmark* ZeroRadRoundRectTest(void* p) { return new ArbRoundRectBench(p, true); }
+static BenchRegistry gRegZeroRadRoundRectTest(ZeroRadRoundRectTest);
+
+static SkBenchmark* RectConservativelyContainsTest(void* p) { return new ConservativelyContainsBench(p, ConservativelyContainsBench::kRect_Type); }
+static BenchRegistry gRegRectConservativelyContainsTest(RectConservativelyContainsTest);
+
+static SkBenchmark* RoundRectConservativelyContainsTest(void* p) { return new ConservativelyContainsBench(p, ConservativelyContainsBench::kRoundRect_Type); }
+static BenchRegistry gRegRoundRectConservativelyContainsTest(RoundRectConservativelyContainsTest);
+
+static SkBenchmark* OvalConservativelyContainsTest(void* p) { return new ConservativelyContainsBench(p, ConservativelyContainsBench::kOval_Type); }
+static BenchRegistry gRegOvalConservativelyContainsTest(OvalConservativelyContainsTest);
diff --git a/bench/PathIterBench.cpp b/bench/PathIterBench.cpp
new file mode 100644
index 0000000..ed99f0a
--- /dev/null
+++ b/bench/PathIterBench.cpp
@@ -0,0 +1,98 @@
+
+/*
+ * 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 "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkPaint.h"
+#include "SkRandom.h"
+#include "SkShader.h"
+#include "SkString.h"
+
+static int rand_pts(SkRandom& rand, SkPoint pts[4]) {
+    int n = rand.nextU() & 3;
+    n += 1;
+
+    for (int i = 0; i < n; ++i) {
+        pts[i].fX = rand.nextSScalar1();
+        pts[i].fY = rand.nextSScalar1();
+    }
+    return n;
+}
+
+class PathIterBench : public SkBenchmark {
+    SkString    fName;
+    SkPath      fPath;
+    bool        fRaw;
+
+    enum { N = SkBENCHLOOP(500) };
+
+public:
+    PathIterBench(void* param, bool raw) : INHERITED(param) {
+        fName.printf("pathiter_%s", raw ? "raw" : "consume");
+        fRaw = raw;
+
+        SkRandom rand;
+        for (int i = 0; i < 1000; ++i) {
+            SkPoint pts[4];
+            int n = rand_pts(rand, pts);
+            switch (n) {
+                case 1:
+                    fPath.moveTo(pts[0]);
+                    break;
+                case 2:
+                    fPath.lineTo(pts[1]);
+                    break;
+                case 3:
+                    fPath.quadTo(pts[1], pts[2]);
+                    break;
+                case 4:
+                    fPath.cubicTo(pts[1], pts[2], pts[3]);
+                    break;
+            }
+        }
+
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        if (fRaw) {
+            for (int i = 0; i < N; ++i) {
+                SkPath::RawIter iter(fPath);
+                SkPath::Verb verb;
+                SkPoint      pts[4];
+
+                while ((verb = iter.next(pts)) != SkPath::kDone_Verb);
+            }
+        } else {
+            for (int i = 0; i < N; ++i) {
+                SkPath::Iter iter(fPath, false);
+                SkPath::Verb verb;
+                SkPoint      pts[4];
+
+                while ((verb = iter.next(pts)) != SkPath::kDone_Verb);
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* F0(void* p) { return new PathIterBench(p, false); }
+static SkBenchmark* F1(void* p) { return new PathIterBench(p, true); }
+
+static BenchRegistry gR0(F0);
+static BenchRegistry gR1(F1);
diff --git a/bench/PicturePlaybackBench.cpp b/bench/PicturePlaybackBench.cpp
index 6a07fb5..bd84baf 100644
--- a/bench/PicturePlaybackBench.cpp
+++ b/bench/PicturePlaybackBench.cpp
@@ -26,7 +26,7 @@
     }
 
     enum {
-        N = SkBENCHLOOP(1000),   // number of times to playback the picture
+        N = SkBENCHLOOP(200),   // number of times to playback the picture
         PICTURE_WIDTH = 1000,
         PICTURE_HEIGHT = 4000,
         TEXT_SIZE = 10
@@ -119,7 +119,7 @@
                     if (fDrawPosH)
                         pos[i].set(x + advX, y);
                     else
-                        pos[i].set(x + advX, y + SkIntToScalar(i));
+                        pos[i].set(x + advX, y + i);
                     advX += adv[i];
                 }
 
@@ -144,4 +144,3 @@
 static BenchRegistry gReg0(Fact0);
 static BenchRegistry gReg1(Fact1);
 static BenchRegistry gReg2(Fact2);
-
diff --git a/bench/PictureRecordBench.cpp b/bench/PictureRecordBench.cpp
new file mode 100644
index 0000000..0f82fda
--- /dev/null
+++ b/bench/PictureRecordBench.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkPoint.h"
+#include "SkRandom.h"
+#include "SkRect.h"
+#include "SkString.h"
+
+class PictureRecordBench : public SkBenchmark {
+public:
+    PictureRecordBench(void* param, const char name[]) : INHERITED(param) {
+        fName.printf("picture_record_%s", name);
+        fPictureWidth = SkIntToScalar(PICTURE_WIDTH);
+        fPictureHeight = SkIntToScalar(PICTURE_HEIGHT);
+    }
+
+    enum {
+        N = SkBENCHLOOP(25), // number of times to create the picture
+        PICTURE_WIDTH = 1000,
+        PICTURE_HEIGHT = 4000,
+    };
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        int n = (int)(N * this->innerLoopScale());
+        n = SkMax32(1, n);
+
+        for (int i = 0; i < n; i++) {
+
+            SkPicture picture;
+
+            SkCanvas* pCanvas = picture.beginRecording(PICTURE_WIDTH, PICTURE_HEIGHT);
+            recordCanvas(pCanvas);
+
+            // we don't need to draw the picture as the endRecording step will
+            // do the work of transferring the recorded content into a playback
+            // object.
+            picture.endRecording();
+        }
+    }
+
+    virtual void recordCanvas(SkCanvas* canvas) = 0;
+    virtual float innerLoopScale() const { return 1; }
+
+    SkString fName;
+    SkScalar fPictureWidth;
+    SkScalar fPictureHeight;
+    SkScalar fTextSize;
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+/*
+ *  An SkPicture has internal dictionaries to store bitmaps, matrices, paints,
+ *  and regions.  This bench populates those dictionaries to test the speed of
+ *  reading and writing to those particular dictionary data structures.
+ */
+class DictionaryRecordBench : public PictureRecordBench {
+public:
+    DictionaryRecordBench(void* param)
+        : INHERITED(param, "dictionaries") { }
+
+    enum {
+        M = SkBENCHLOOP(100),   // number of elements in each dictionary
+    };
+protected:
+    virtual void recordCanvas(SkCanvas* canvas) {
+
+        const SkPoint translateDelta = getTranslateDelta();
+
+        for (int i = 0; i < M; i++) {
+
+            SkColor color = SK_ColorYELLOW + (i % 255);
+            SkIRect rect = SkIRect::MakeWH(i,i);
+
+            canvas->save();
+
+            // set the clip to the given region
+            SkRegion region;
+            region.setRect(rect);
+            canvas->clipRegion(region);
+
+            // fill the clip with a color
+            SkPaint paint;
+            paint.setColor(color);
+            canvas->drawPaint(paint);
+
+            // set a matrix on the canvas
+            SkMatrix matrix;
+            matrix.setRotate(SkIntToScalar(i % 360));
+            canvas->setMatrix(matrix);
+
+            // create a simple bitmap
+            SkBitmap bitmap;
+            bitmap.setConfig(SkBitmap::kRGB_565_Config, 10, 10);
+            bitmap.allocPixels();
+
+            // draw a single color into the bitmap
+            SkCanvas bitmapCanvas(bitmap);
+            bitmapCanvas.drawColor(SkColorSetA(color, i % 255));
+
+            // draw the bitmap onto the canvas
+            canvas->drawBitmapMatrix(bitmap, matrix);
+
+            canvas->restore();
+            canvas->translate(translateDelta.fX, translateDelta.fY);
+        }
+    }
+
+    SkPoint getTranslateDelta() {
+        SkIPoint canvasSize = onGetSize();
+        return SkPoint::Make(SkIntToScalar((PICTURE_WIDTH - canvasSize.fX)/M),
+                             SkIntToScalar((PICTURE_HEIGHT- canvasSize.fY)/M));
+    }
+private:
+    typedef PictureRecordBench INHERITED;
+};
+
+/*
+ *  Populates the SkPaint dictionary with a large number of unique paint
+ *  objects that differ only by color
+ */
+class UniquePaintDictionaryRecordBench : public PictureRecordBench {
+public:
+    UniquePaintDictionaryRecordBench(void* param)
+        : INHERITED(param, "unique_paint_dictionary") { }
+
+    enum {
+        M = SkBENCHLOOP(15000),   // number of unique paint objects
+    };
+protected:
+    virtual float innerLoopScale() const SK_OVERRIDE { return 0.1f; }
+    virtual void recordCanvas(SkCanvas* canvas) {
+        SkRandom rand;
+        for (int i = 0; i < M; i++) {
+            SkPaint paint;
+            paint.setColor(rand.nextU());
+            canvas->drawPaint(paint);
+        }
+    }
+
+private:
+    typedef PictureRecordBench INHERITED;
+};
+
+/*
+ *  Populates the SkPaint dictionary with a number of unique paint
+ *  objects that get reused repeatedly
+ */
+class RecurringPaintDictionaryRecordBench : public PictureRecordBench {
+public:
+    RecurringPaintDictionaryRecordBench(void* param)
+        : INHERITED(param, "recurring_paint_dictionary") { }
+
+    enum {
+        ObjCount = 100,           // number of unique paint objects
+        M = SkBENCHLOOP(50000),   // number of draw iterations
+    };
+protected:
+    virtual float innerLoopScale() const SK_OVERRIDE { return 0.1f; }
+    virtual void recordCanvas(SkCanvas* canvas) {
+
+        for (int i = 0; i < M; i++) {
+            SkPaint paint;
+            paint.setColor(i % ObjCount);
+            canvas->drawPaint(paint);
+        }
+    }
+
+private:
+    typedef PictureRecordBench INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact0(void* p) { return new DictionaryRecordBench(p); }
+static SkBenchmark* Fact1(void* p) { return new UniquePaintDictionaryRecordBench(p); }
+static SkBenchmark* Fact2(void* p) { return new RecurringPaintDictionaryRecordBench(p); }
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
diff --git a/bench/RTreeBench.cpp b/bench/RTreeBench.cpp
new file mode 100644
index 0000000..53dbe28
--- /dev/null
+++ b/bench/RTreeBench.cpp
@@ -0,0 +1,217 @@
+
+/*
+ * 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkRTree.h"
+#include "SkRandom.h"
+#include "SkString.h"
+
+// confine rectangles to a smallish area, so queries generally hit something, and overlap occurs:
+static const int GENERATE_EXTENTS = 1000;
+static const int NUM_BUILD_RECTS = 500;
+static const int NUM_QUERY_RECTS = 5000;
+static const int NUM_QUERIES = 1000;
+
+typedef SkIRect (*MakeRectProc)(SkRandom&, int, int);
+
+// Time how long it takes to build an R-Tree either bulk-loaded or not
+class BBoxBuildBench : public SkBenchmark {
+public:
+    BBoxBuildBench(void* param, const char* name, MakeRectProc proc, bool bulkLoad,
+                    SkBBoxHierarchy* tree)
+        : INHERITED(param)
+        , fTree(tree)
+        , fProc(proc)
+        , fBulkLoad(bulkLoad) {
+        fName.append("rtree_");
+        fName.append(name);
+        fName.append("_build");
+        if (fBulkLoad) {
+            fName.append("_bulk");
+        }
+        fIsRendering = false;
+    }
+    virtual ~BBoxBuildBench() {
+        fTree->unref();
+    }
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkRandom rand;
+        for (int i = 0; i < SkBENCHLOOP(100); ++i) {
+            for (int j = 0; j < NUM_BUILD_RECTS; ++j) {
+                fTree->insert(reinterpret_cast<void*>(j), fProc(rand, j, NUM_BUILD_RECTS),
+                              fBulkLoad);
+            }
+            fTree->flushDeferredInserts();
+            fTree->clear();
+        }
+    }
+private:
+    SkBBoxHierarchy* fTree;
+    MakeRectProc fProc;
+    SkString fName;
+    bool fBulkLoad;
+    typedef SkBenchmark INHERITED;
+};
+
+// Time how long it takes to perform queries on an R-Tree, bulk-loaded or not
+class BBoxQueryBench : public SkBenchmark {
+public:
+    enum QueryType {
+        kSmall_QueryType, // small queries
+        kLarge_QueryType, // large queries
+        kRandom_QueryType,// randomly sized queries
+        kFull_QueryType   // queries that cover everything
+    };
+
+    BBoxQueryBench(void* param, const char* name, MakeRectProc proc, bool bulkLoad,
+                    QueryType q, SkBBoxHierarchy* tree)
+        : INHERITED(param)
+        , fTree(tree)
+        , fProc(proc)
+        , fBulkLoad(bulkLoad)
+        , fQuery(q) {
+        fName.append("rtree_");
+        fName.append(name);
+        fName.append("_query");
+        if (fBulkLoad) {
+            fName.append("_bulk");
+        }
+        SkRandom rand;
+        for (int j = 0; j < SkBENCHLOOP(NUM_QUERY_RECTS); ++j) {
+            fTree->insert(reinterpret_cast<void*>(j), fProc(rand, j,
+                           SkBENCHLOOP(NUM_QUERY_RECTS)), fBulkLoad);
+        }
+        fTree->flushDeferredInserts();
+        fIsRendering = false;
+    }
+    virtual ~BBoxQueryBench() {
+        fTree->unref();
+    }
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkRandom rand;
+        for (int i = 0; i < SkBENCHLOOP(NUM_QUERIES); ++i) {
+            SkTDArray<void*> hits;
+            SkIRect query;
+            switch(fQuery) {
+                case kSmall_QueryType:
+                    query.fLeft = rand.nextU() % GENERATE_EXTENTS;
+                    query.fTop = rand.nextU() % GENERATE_EXTENTS;
+                    query.fRight = query.fLeft + (GENERATE_EXTENTS / 20);
+                    query.fBottom = query.fTop + (GENERATE_EXTENTS / 20);
+                    break;
+                case kLarge_QueryType:
+                    query.fLeft = rand.nextU() % GENERATE_EXTENTS;
+                    query.fTop = rand.nextU() % GENERATE_EXTENTS;
+                    query.fRight = query.fLeft + (GENERATE_EXTENTS / 2);
+                    query.fBottom = query.fTop + (GENERATE_EXTENTS / 2);
+                    break;
+                case kFull_QueryType:
+                    query.fLeft = -GENERATE_EXTENTS;
+                    query.fTop = -GENERATE_EXTENTS;
+                    query.fRight = 2 * GENERATE_EXTENTS;
+                    query.fBottom = 2 * GENERATE_EXTENTS;
+                    break;
+                default: // fallthrough
+                case kRandom_QueryType:
+                    query.fLeft = rand.nextU() % GENERATE_EXTENTS;
+                    query.fTop = rand.nextU() % GENERATE_EXTENTS;
+                    query.fRight = query.fLeft + 1 + rand.nextU() % (GENERATE_EXTENTS / 2);
+                    query.fBottom = query.fTop + 1 + rand.nextU() % (GENERATE_EXTENTS / 2);
+                    break;
+            };
+            fTree->search(query, &hits);
+        }
+    }
+private:
+    SkBBoxHierarchy* fTree;
+    MakeRectProc fProc;
+    SkString fName;
+    bool fBulkLoad;
+    QueryType fQuery;
+    typedef SkBenchmark INHERITED;
+};
+
+static inline SkIRect make_simple_rect(SkRandom&, int index, int numRects) {
+    SkIRect out = {0, 0, GENERATE_EXTENTS, GENERATE_EXTENTS};
+    return out;
+}
+
+static inline SkIRect make_concentric_rects_increasing(SkRandom&, int index, int numRects) {
+    SkIRect out = {0, 0, index + 1, index + 1};
+    return out;
+}
+
+static inline SkIRect make_concentric_rects_decreasing(SkRandom&, int index, int numRects) {
+    SkIRect out = {0, 0, numRects - index, numRects - index};
+    return out;
+}
+
+static inline SkIRect make_point_rects(SkRandom& rand, int index, int numRects) {
+    SkIRect out;
+    out.fLeft   = rand.nextU() % GENERATE_EXTENTS;
+    out.fTop    = rand.nextU() % GENERATE_EXTENTS;
+    out.fRight  = out.fLeft + (GENERATE_EXTENTS / 200);
+    out.fBottom = out.fTop + (GENERATE_EXTENTS / 200);
+    return out;
+}
+
+static inline SkIRect make_random_rects(SkRandom& rand, int index, int numRects) {
+    SkIRect out;
+    out.fLeft   = rand.nextS() % GENERATE_EXTENTS;
+    out.fTop    = rand.nextS() % GENERATE_EXTENTS;
+    out.fRight  = out.fLeft + 1 + rand.nextU() % (GENERATE_EXTENTS / 5);
+    out.fBottom = out.fTop  + 1 + rand.nextU() % (GENERATE_EXTENTS / 5);
+    return out;
+}
+
+static inline SkIRect make_large_rects(SkRandom& rand, int index, int numRects) {
+    SkIRect out;
+    out.fLeft   = rand.nextU() % GENERATE_EXTENTS;
+    out.fTop    = rand.nextU() % GENERATE_EXTENTS;
+    out.fRight  = out.fLeft + (GENERATE_EXTENTS / 3);
+    out.fBottom = out.fTop  + (GENERATE_EXTENTS / 3);
+    return out;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static inline SkBenchmark* Fact0(void* p) {
+    return SkNEW_ARGS(BBoxBuildBench, (p, "random", &make_random_rects, true,
+                      SkRTree::Create(5, 16)));
+}
+static inline SkBenchmark* Fact1(void* p) {
+    return SkNEW_ARGS(BBoxBuildBench, (p, "random", &make_random_rects, false,
+                      SkRTree::Create(5, 16)));
+}
+static inline SkBenchmark* Fact2(void* p) {
+    return SkNEW_ARGS(BBoxBuildBench, (p, "concentric",
+                      &make_concentric_rects_increasing, true, SkRTree::Create(5, 16)));
+}
+static inline SkBenchmark* Fact3(void* p) {
+    return SkNEW_ARGS(BBoxQueryBench, (p, "random", &make_random_rects, true,
+                      BBoxQueryBench::kRandom_QueryType, SkRTree::Create(5, 16)));
+}
+static inline SkBenchmark* Fact4(void* p) {
+    return SkNEW_ARGS(BBoxQueryBench, (p, "random", &make_random_rects, false,
+                      BBoxQueryBench::kRandom_QueryType, SkRTree::Create(5, 16)));
+}
+
+static BenchRegistry gReg0(Fact0);
+static BenchRegistry gReg1(Fact1);
+static BenchRegistry gReg2(Fact2);
+static BenchRegistry gReg3(Fact3);
+static BenchRegistry gReg4(Fact4);
diff --git a/bench/ReadPixBench.cpp b/bench/ReadPixBench.cpp
new file mode 100644
index 0000000..66db2eb
--- /dev/null
+++ b/bench/ReadPixBench.cpp
@@ -0,0 +1,66 @@
+
+/*
+ * Copyright 2012 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+
+
+/**
+ * This bench mark tests the use case where the user writes the a canvas
+ * and then reads small chunks from it repeatedly. This can cause trouble
+ * for the GPU as readbacks are very expensive.
+ */
+class ReadPixBench : public SkBenchmark {
+public:
+    ReadPixBench(void* param) : INHERITED(param) {}
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "readpix";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        canvas->clear(SK_ColorBLACK);
+
+        SkISize size = canvas->getDeviceSize();
+
+        int offX = (size.width() - kWindowSize) / kNumStepsX;
+        int offY = (size.height() - kWindowSize) / kNumStepsY;
+
+        SkPaint paint;
+
+        paint.setColor(SK_ColorBLUE);
+
+        canvas->drawCircle(SkIntToScalar(size.width()/2),
+                           SkIntToScalar(size.height()/2),
+                           SkIntToScalar(size.width()/2),
+                           paint);
+
+        SkBitmap bitmap;
+
+        bitmap.setConfig(SkBitmap::kARGB_8888_Config, kWindowSize, kWindowSize);
+
+        for (int x = 0; x < kNumStepsX; ++x) {
+            for (int y = 0; y < kNumStepsY; ++y) {
+                canvas->readPixels(&bitmap, x * offX, y * offY);
+            }
+        }
+    }
+
+private:
+    static const int kNumStepsX = 30;
+    static const int kNumStepsY = 30;
+    static const int kWindowSize = 5;
+
+    typedef SkBenchmark INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* fact(void* p) { return new ReadPixBench(p); }
+static BenchRegistry gReg(fact);
diff --git a/bench/RectBench.cpp b/bench/RectBench.cpp
index cef2e22..645f8a8 100644
--- a/bench/RectBench.cpp
+++ b/bench/RectBench.cpp
@@ -23,7 +23,10 @@
     SkRect  fRects[N];
     SkColor fColors[N];
 
-    RectBench(void* param, int shift, int stroke = 0) : INHERITED(param), fShift(shift), fStroke(stroke) {
+    RectBench(void* param, int shift, int stroke = 0)
+        : INHERITED(param)
+        , fShift(shift)
+        , fStroke(stroke) {
         SkRandom rand;
         const SkScalar offset = SK_Scalar1/3;
         for (int i = 0; i < N; i++) {
@@ -73,6 +76,37 @@
     typedef SkBenchmark INHERITED;
 };
 
+class SrcModeRectBench : public RectBench {
+public:
+    SrcModeRectBench(void* param) : INHERITED(param, 1, 0) {
+        fMode = SkXfermode::Create(SkXfermode::kSrc_Mode);
+    }
+
+    virtual ~SrcModeRectBench() {
+        SkSafeUnref(fMode);
+    }
+
+protected:
+    virtual void setupPaint(SkPaint* paint) SK_OVERRIDE {
+        this->INHERITED::setupPaint(paint);
+        // srcmode is most interesting when we're not opaque
+        paint->setAlpha(0x80);
+        paint->setXfermode(fMode);
+    }
+
+    virtual const char* onGetName() SK_OVERRIDE {
+        fName.set(this->INHERITED::onGetName());
+        fName.prepend("srcmode_");
+        return fName.c_str();
+    }
+
+private:
+    SkString fName;
+    SkXfermode* fMode;
+
+    typedef RectBench INHERITED;
+};
+
 class OvalBench : public RectBench {
 public:
     OvalBench(void* param, int shift) : RectBench(param, shift) {}
@@ -128,6 +162,40 @@
     virtual const char* onGetName() { return fName; }
 };
 
+class AARectBench : public SkBenchmark {
+public:
+    enum {
+        W = 640,
+        H = 480,
+    };
+
+    AARectBench(void* param) : INHERITED(param) {}
+
+protected:
+
+    virtual const char* onGetName() { return "aarects"; }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        this->setupPaint(&paint);
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorBLACK);
+        SkRect r;
+
+        // Draw small aa rects in a grid across the screen
+        for (SkScalar y = SK_ScalarHalf; y < H; y += SkIntToScalar(2)) {
+            for (SkScalar x = SK_ScalarHalf; x < W; x += SkIntToScalar(2)) {
+                r.set(x, y,
+                      x+SkFloatToScalar(1.5f), y+SkFloatToScalar(1.5f));
+                canvas->drawRect(r, paint);
+            }
+        }
+
+    }
+private:
+    typedef SkBenchmark INHERITED;
+};
+
 /*******************************************************************************
  * to bench BlitMask [Opaque, Black, color, shader]
  *******************************************************************************/
@@ -143,7 +211,7 @@
     SkCanvas::PointMode fMode;
     const char* fName;
 
-    BlitMaskBench(void* param, SkCanvas::PointMode mode, 
+    BlitMaskBench(void* param, SkCanvas::PointMode mode,
                   BlitMaskBench::kMaskType type, const char* name) :
                   RectBench(param, 2), fMode(mode), _type(type) {
         fName = name;
@@ -177,17 +245,17 @@
             paint.setShader(s)->unref();
         }
         for (size_t i = 0; i < sizes; i++) {
-            switch (_type) {	
-                case kMaskOpaque: 
-                    color = fColors[i]; 
-                    alpha = 0xFF; 
+            switch (_type) {
+                case kMaskOpaque:
+                    color = fColors[i];
+                    alpha = 0xFF;
                     break;
-                case kMaskBlack: 
+                case kMaskBlack:
                     alpha = 0xFF;
                     color = 0xFF000000;
                     break;
                 case kMaskColor:
-                    color = fColors[i]; 
+                    color = fColors[i];
                     alpha = rand.nextU() & 255;
                     break;
                 case KMaskShader:
@@ -202,68 +270,41 @@
     }
     virtual const char* onGetName() { return fName; }
 private:
-	typedef RectBench INHERITED;
-	kMaskType _type;
+    typedef RectBench INHERITED;
+    kMaskType _type;
 };
 
 
-static SkBenchmark* RectFactory1F(void* p) { return SkNEW_ARGS(RectBench, (p, 1)); }
-static SkBenchmark* RectFactory1S(void* p) { return SkNEW_ARGS(RectBench, (p, 1, 4)); }
-static SkBenchmark* RectFactory2F(void* p) { return SkNEW_ARGS(RectBench, (p, 3)); }
-static SkBenchmark* RectFactory2S(void* p) { return SkNEW_ARGS(RectBench, (p, 3, 4)); }
-static SkBenchmark* OvalFactory1(void* p) { return SkNEW_ARGS(OvalBench, (p, 1)); }
-static SkBenchmark* OvalFactory2(void* p) { return SkNEW_ARGS(OvalBench, (p, 3)); }
-static SkBenchmark* RRectFactory1(void* p) { return SkNEW_ARGS(RRectBench, (p, 1)); }
-static SkBenchmark* RRectFactory2(void* p) { return SkNEW_ARGS(RRectBench, (p, 3)); }
-static SkBenchmark* PointsFactory(void* p) {
-    return SkNEW_ARGS(PointsBench, (p, SkCanvas::kPoints_PointMode, "points"));
-}
-static SkBenchmark* LinesFactory(void* p) {
-    return SkNEW_ARGS(PointsBench, (p, SkCanvas::kLines_PointMode, "lines"));
-}
-static SkBenchmark* PolygonFactory(void* p) {
-    return SkNEW_ARGS(PointsBench, (p, SkCanvas::kPolygon_PointMode, "polygon"));
-}
+DEF_BENCH( return SkNEW_ARGS(RectBench, (p, 1)); )
+DEF_BENCH( return SkNEW_ARGS(RectBench, (p, 1, 4)); )
+DEF_BENCH( return SkNEW_ARGS(RectBench, (p, 3)); )
+DEF_BENCH( return SkNEW_ARGS(RectBench, (p, 3, 4)); )
+DEF_BENCH( return SkNEW_ARGS(OvalBench, (p, 1)); )
+DEF_BENCH( return SkNEW_ARGS(OvalBench, (p, 3)); )
+DEF_BENCH( return SkNEW_ARGS(RRectBench, (p, 1)); )
+DEF_BENCH( return SkNEW_ARGS(RRectBench, (p, 3)); )
+DEF_BENCH( return SkNEW_ARGS(PointsBench, (p, SkCanvas::kPoints_PointMode, "points")); )
+DEF_BENCH( return SkNEW_ARGS(PointsBench, (p, SkCanvas::kLines_PointMode, "lines")); )
+DEF_BENCH( return SkNEW_ARGS(PointsBench, (p, SkCanvas::kPolygon_PointMode, "polygon")); )
+
+DEF_BENCH( return SkNEW_ARGS(SrcModeRectBench, (p)); )
+DEF_BENCH( return SkNEW_ARGS(AARectBench, (p)); )
 
 /* init the blitmask bench
  */
-static SkBenchmark* BlitMaskOpaqueFactory(void* p) {
-    return SkNEW_ARGS(BlitMaskBench,
+DEF_BENCH( return SkNEW_ARGS(BlitMaskBench,
                       (p, SkCanvas::kPoints_PointMode,
                       BlitMaskBench::kMaskOpaque, "maskopaque")
-                      );
-}
-static SkBenchmark* BlitMaskBlackFactory(void* p) {
-    return SkNEW_ARGS(BlitMaskBench,
+                      ); )
+DEF_BENCH( return SkNEW_ARGS(BlitMaskBench,
                       (p, SkCanvas::kPoints_PointMode,
                       BlitMaskBench::kMaskBlack, "maskblack")
-                      );
-}
-static SkBenchmark* BlitMaskColorFactory(void* p) {
-    return SkNEW_ARGS(BlitMaskBench,
+                      ); )
+DEF_BENCH( return SkNEW_ARGS(BlitMaskBench,
                       (p, SkCanvas::kPoints_PointMode,
                       BlitMaskBench::kMaskColor, "maskcolor")
-                      );
-}
-static SkBenchmark* BlitMaskShaderFactory(void* p) {
-    return SkNEW_ARGS(BlitMaskBench,
+                      ); )
+DEF_BENCH( return SkNEW_ARGS(BlitMaskBench,
                      (p, SkCanvas::kPoints_PointMode,
                      BlitMaskBench::KMaskShader, "maskshader")
-                     );
-}
-
-static BenchRegistry gRectReg1F(RectFactory1F);
-static BenchRegistry gRectReg1S(RectFactory1S);
-static BenchRegistry gRectReg2F(RectFactory2F);
-static BenchRegistry gRectReg2S(RectFactory2S);
-static BenchRegistry gOvalReg1(OvalFactory1);
-static BenchRegistry gOvalReg2(OvalFactory2);
-static BenchRegistry gRRectReg1(RRectFactory1);
-static BenchRegistry gRRectReg2(RRectFactory2);
-static BenchRegistry gPointsReg(PointsFactory);
-static BenchRegistry gLinesReg(LinesFactory);
-static BenchRegistry gPolygonReg(PolygonFactory);
-static BenchRegistry gRectRegOpaque(BlitMaskOpaqueFactory);
-static BenchRegistry gRectRegBlack(BlitMaskBlackFactory);
-static BenchRegistry gRectRegColor(BlitMaskColorFactory);
-static BenchRegistry gRectRegShader(BlitMaskShaderFactory);
+                     ); )
diff --git a/bench/RefCntBench.cpp b/bench/RefCntBench.cpp
new file mode 100644
index 0000000..c8a98e4
--- /dev/null
+++ b/bench/RefCntBench.cpp
@@ -0,0 +1,205 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkRefCnt.h"
+#include "SkThread.h"
+#include "SkWeakRefCnt.h"
+#include <memory>
+
+enum {
+    N = SkBENCHLOOP(100000),
+    M = SkBENCHLOOP(2)
+};
+
+class RefCntBench_Stack : public SkBenchmark {
+public:
+    RefCntBench_Stack(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "ref_cnt_stack";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        for (int i = 0; i < N; ++i) {
+            SkRefCnt ref;
+            for (int j = 0; j < M; ++j) {
+                ref.ref();
+                ref.unref();
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class PlacedRefCnt : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(PlacedRefCnt)
+
+    PlacedRefCnt() : SkRefCnt() { }
+    void operator delete(void *p) { }
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+SK_DEFINE_INST_COUNT(PlacedRefCnt)
+
+class RefCntBench_Heap : public SkBenchmark {
+public:
+    RefCntBench_Heap(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "ref_cnt_heap";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        char memory[sizeof(PlacedRefCnt)];
+        for (int i = 0; i < N; ++i) {
+            PlacedRefCnt* ref = new (memory) PlacedRefCnt();
+            for (int j = 0; j < M; ++j) {
+                ref->ref();
+                ref->unref();
+            }
+            ref->unref();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class RefCntBench_New : public SkBenchmark {
+public:
+    RefCntBench_New(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "ref_cnt_new";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        for (int i = 0; i < N; ++i) {
+            SkRefCnt* ref = new SkRefCnt();
+            for (int j = 0; j < M; ++j) {
+                ref->ref();
+                ref->unref();
+            }
+            ref->unref();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class WeakRefCntBench_Stack : public SkBenchmark {
+public:
+    WeakRefCntBench_Stack(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "ref_cnt_stack_weak";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        for (int i = 0; i < N; ++i) {
+            SkWeakRefCnt ref;
+            for (int j = 0; j < M; ++j) {
+                ref.ref();
+                ref.unref();
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class PlacedWeakRefCnt : public SkWeakRefCnt {
+public:
+    PlacedWeakRefCnt() : SkWeakRefCnt() { }
+    void operator delete(void *p) { }
+};
+
+class WeakRefCntBench_Heap : public SkBenchmark {
+public:
+    WeakRefCntBench_Heap(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "ref_cnt_heap_weak";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        char memory[sizeof(PlacedWeakRefCnt)];
+        for (int i = 0; i < N; ++i) {
+            PlacedWeakRefCnt* ref = new (memory) PlacedWeakRefCnt();
+            for (int j = 0; j < M; ++j) {
+                ref->ref();
+                ref->unref();
+            }
+            ref->unref();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+class WeakRefCntBench_New : public SkBenchmark {
+public:
+    WeakRefCntBench_New(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+protected:
+    virtual const char* onGetName() {
+        return "ref_cnt_new_weak";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        for (int i = 0; i < N; ++i) {
+            SkWeakRefCnt* ref = new SkWeakRefCnt();
+            for (int j = 0; j < M; ++j) {
+                ref->ref();
+                ref->unref();
+            }
+            ref->unref();
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* Fact00(void* p) { return new RefCntBench_Stack(p); }
+static SkBenchmark* Fact01(void* p) { return new RefCntBench_Heap(p); }
+static SkBenchmark* Fact02(void* p) { return new RefCntBench_New(p); }
+
+static SkBenchmark* Fact10(void* p) { return new WeakRefCntBench_Stack(p); }
+static SkBenchmark* Fact11(void* p) { return new WeakRefCntBench_Heap(p); }
+static SkBenchmark* Fact12(void* p) { return new WeakRefCntBench_New(p); }
+
+static BenchRegistry gReg00(Fact00);
+static BenchRegistry gReg01(Fact01);
+static BenchRegistry gReg02(Fact02);
+
+static BenchRegistry gReg10(Fact10);
+static BenchRegistry gReg11(Fact11);
+static BenchRegistry gReg12(Fact12);
diff --git a/bench/RegionBench.cpp b/bench/RegionBench.cpp
new file mode 100644
index 0000000..7a306e9
--- /dev/null
+++ b/bench/RegionBench.cpp
@@ -0,0 +1,141 @@
+
+/*
+ * 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 "SkBenchmark.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkString.h"
+
+static bool union_proc(SkRegion& a, SkRegion& b) {
+    SkRegion result;
+    return result.op(a, b, SkRegion::kUnion_Op);
+}
+
+static bool sect_proc(SkRegion& a, SkRegion& b) {
+    SkRegion result;
+    return result.op(a, b, SkRegion::kIntersect_Op);
+}
+
+static bool diff_proc(SkRegion& a, SkRegion& b) {
+    SkRegion result;
+    return result.op(a, b, SkRegion::kDifference_Op);
+}
+
+static bool diffrect_proc(SkRegion& a, SkRegion& b) {
+    SkRegion result;
+    return result.op(a, b.getBounds(), SkRegion::kDifference_Op);
+}
+
+static bool diffrectbig_proc(SkRegion& a, SkRegion& b) {
+    SkRegion result;
+    return result.op(a, a.getBounds(), SkRegion::kDifference_Op);
+}
+
+static bool containsrect_proc(SkRegion& a, SkRegion& b) {
+    SkIRect r = a.getBounds();
+    r.inset(r.width()/4, r.height()/4);
+    (void)a.contains(r);
+
+    r = b.getBounds();
+    r.inset(r.width()/4, r.height()/4);
+    return b.contains(r);
+}
+
+static bool sectsrgn_proc(SkRegion& a, SkRegion& b) {
+    return a.intersects(b);
+}
+
+static bool sectsrect_proc(SkRegion& a, SkRegion& b) {
+    SkIRect r = a.getBounds();
+    r.inset(r.width()/4, r.height()/4);
+    return a.intersects(r);
+}
+
+static bool containsxy_proc(SkRegion& a, SkRegion& b) {
+    const SkIRect& r = a.getBounds();
+    const int dx = r.width() / 8;
+    const int dy = r.height() / 8;
+    for (int y = r.fTop; y < r.fBottom; y += dy) {
+        for (int x = r.fLeft; x < r.fRight; x += dx) {
+            (void)a.contains(x, y);
+        }
+    }
+    return true;
+}
+
+class RegionBench : public SkBenchmark {
+public:
+    typedef bool (*Proc)(SkRegion& a, SkRegion& b);
+
+    SkRegion fA, fB;
+    Proc     fProc;
+    SkString fName;
+    int      fLoopMul;
+
+    enum {
+        W = 1024,
+        H = 768,
+        N = SkBENCHLOOP(2000)
+    };
+
+    SkIRect randrect(SkRandom& rand) {
+        int x = rand.nextU() % W;
+        int y = rand.nextU() % H;
+        int w = rand.nextU() % W;
+        int h = rand.nextU() % H;
+        return SkIRect::MakeXYWH(x, y, w >> 1, h >> 1);
+    }
+
+    RegionBench(void* param, int count, Proc proc, const char name[], int mul = 1) : INHERITED(param) {
+        fProc = proc;
+        fName.printf("region_%s_%d", name, count);
+        fLoopMul = mul;
+
+        SkRandom rand;
+        for (int i = 0; i < count; i++) {
+            fA.op(randrect(rand), SkRegion::kXOR_Op);
+            fB.op(randrect(rand), SkRegion::kXOR_Op);
+        }
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() { return fName.c_str(); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        Proc proc = fProc;
+        int n = fLoopMul * N;
+        for (int i = 0; i < n; ++i) {
+            proc(fA, fB);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+#define SMALL   16
+
+static SkBenchmark* gF0(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, union_proc, "union")); }
+static SkBenchmark* gF1(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, sect_proc, "intersect")); }
+static SkBenchmark* gF2(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, diff_proc, "difference")); }
+static SkBenchmark* gF3(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, diffrect_proc, "differencerect")); }
+static SkBenchmark* gF4(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, diffrectbig_proc, "differencerectbig")); }
+static SkBenchmark* gF5(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, containsrect_proc, "containsrect", 100)); }
+static SkBenchmark* gF6(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, sectsrgn_proc, "intersectsrgn", 10)); }
+static SkBenchmark* gF7(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, sectsrect_proc, "intersectsrect", 200)); }
+static SkBenchmark* gF8(void* p) { return SkNEW_ARGS(RegionBench, (p, SMALL, containsxy_proc, "containsxy")); }
+
+static BenchRegistry gR0(gF0);
+static BenchRegistry gR1(gF1);
+static BenchRegistry gR2(gF2);
+static BenchRegistry gR3(gF3);
+static BenchRegistry gR4(gF4);
+static BenchRegistry gR5(gF5);
+static BenchRegistry gR6(gF6);
+static BenchRegistry gR7(gF7);
+static BenchRegistry gR8(gF8);
diff --git a/bench/RegionContainBench.cpp b/bench/RegionContainBench.cpp
new file mode 100644
index 0000000..1aae264
--- /dev/null
+++ b/bench/RegionContainBench.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkRandom.h"
+#include "SkRegion.h"
+#include "SkString.h"
+
+static bool sect_proc(SkRegion& a, SkRegion& b) {
+    SkRegion result;
+    return result.op(a, b, SkRegion::kIntersect_Op);
+}
+
+class RegionContainBench : public SkBenchmark {
+public:
+    typedef bool (*Proc)(SkRegion& a, SkRegion& b);
+    SkRegion fA, fB;
+    Proc     fProc;
+    SkString fName;
+
+    enum {
+        W = 200,
+        H = 200,
+        COUNT = 10,
+        N = SkBENCHLOOP(20000)
+    };
+
+    SkIRect randrect(SkRandom& rand, int i) {
+        int w = rand.nextU() % W;
+        return SkIRect::MakeXYWH(0, i*H/COUNT, w, H/COUNT);
+    }
+
+    RegionContainBench(void* param, Proc proc, const char name[]) : INHERITED(param) {
+        fProc = proc;
+        fName.printf("region_contains_%s", name);
+
+        SkRandom rand;
+        for (int i = 0; i < COUNT; i++) {
+            fA.op(randrect(rand, i), SkRegion::kXOR_Op);
+        }
+
+        fB.setRect(0, 0, H, W);
+
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() { return fName.c_str(); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        Proc proc = fProc;
+
+        for (int i = 0; i < N; ++i) {
+           proc(fA, fB);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* gF0(void* p) { return SkNEW_ARGS(RegionContainBench, (p, sect_proc, "sect")); }
+
+static BenchRegistry gR0(gF0);
diff --git a/bench/RepeatTileBench.cpp b/bench/RepeatTileBench.cpp
index 8470bed..5235696 100644
--- a/bench/RepeatTileBench.cpp
+++ b/bench/RepeatTileBench.cpp
@@ -27,7 +27,7 @@
     p.setColor(SK_ColorRED);
     canvas.drawCircle(SkIntToScalar(w)/2, SkIntToScalar(h)/2,
                       SkIntToScalar(SkMin32(w, h))*3/8, p);
-    
+
     SkRect r;
     r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
     p.setStyle(SkPaint::kStroke_Style);
@@ -48,7 +48,7 @@
     int r = SkGetPackedR32(c);
     int g = SkGetPackedG32(c);
     int b = SkGetPackedB32(c);
-    
+
     return convByteTo6(r) * 36 + convByteTo6(g) * 6 + convByteTo6(b);
 }
 
@@ -70,7 +70,7 @@
     dst->setConfig(SkBitmap::kIndex8_Config, src.width(), src.height());
     dst->allocPixels(ctable);
     ctable->unref();
-    
+
     SkAutoLockPixels alps(src);
     SkAutoLockPixels alpd(*dst);
 
@@ -88,7 +88,7 @@
     SkString    fName;
     enum { N = SkBENCHLOOP(20) };
 public:
-    RepeatTileBench(void* param, SkBitmap::Config c) : INHERITED(param) {
+    RepeatTileBench(void* param, SkBitmap::Config c, bool isOpaque = false) : INHERITED(param) {
         const int w = 50;
         const int h = 50;
         SkBitmap bm;
@@ -99,8 +99,9 @@
             bm.setConfig(c, w, h);
         }
         bm.allocPixels();
-        bm.eraseColor(0);
-        
+        bm.eraseColor(isOpaque ? SK_ColorWHITE : 0);
+        bm.setIsOpaque(isOpaque);
+
         drawIntoBitmap(bm);
 
         if (SkBitmap::kIndex8_Config == c) {
@@ -113,7 +114,7 @@
                                                    SkShader::kRepeat_TileMode,
                                                    SkShader::kRepeat_TileMode);
         fPaint.setShader(s)->unref();
-        fName.printf("repeatTile_%s", gConfigName[bm.config()]);
+        fName.printf("repeatTile_%s_%c", gConfigName[bm.config()], isOpaque ? 'X' : 'A');
     }
 
 protected:
@@ -134,12 +135,8 @@
     typedef SkBenchmark INHERITED;
 };
 
-static SkBenchmark* Fact0(void* p) { return new RepeatTileBench(p, SkBitmap::kARGB_8888_Config); }
-static SkBenchmark* Fact1(void* p) { return new RepeatTileBench(p, SkBitmap::kRGB_565_Config); }
-static SkBenchmark* Fact2(void* p) { return new RepeatTileBench(p, SkBitmap::kARGB_4444_Config); }
-static SkBenchmark* Fact3(void* p) { return new RepeatTileBench(p, SkBitmap::kIndex8_Config); }
-
-static BenchRegistry gReg0(Fact0);
-static BenchRegistry gReg1(Fact1);
-static BenchRegistry gReg2(Fact2);
-static BenchRegistry gReg3(Fact3);
+DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kARGB_8888_Config, true))
+DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kARGB_8888_Config, false))
+DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kRGB_565_Config))
+DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kARGB_4444_Config))
+DEF_BENCH(return new RepeatTileBench(p, SkBitmap::kIndex8_Config))
diff --git a/bench/ScalarBench.cpp b/bench/ScalarBench.cpp
index 8bedfbd..333dd22 100644
--- a/bench/ScalarBench.cpp
+++ b/bench/ScalarBench.cpp
@@ -8,6 +8,7 @@
 #include "SkBenchmark.h"
 #include "SkFloatBits.h"
 #include "SkRandom.h"
+#include "SkRect.h"
 #include "SkString.h"
 
 class ScalarBench : public SkBenchmark {
@@ -16,6 +17,7 @@
 public:
     ScalarBench(void* param, const char name[]) : INHERITED(param) {
         fName.printf("scalar_%s", name);
+        fIsRendering = false;
     }
 
     virtual void performTest() = 0;
@@ -23,7 +25,7 @@
 protected:
     virtual int mulLoopCount() const { return 1; }
 
-    virtual const char* onGetName() {
+    virtual const char* onGetName() SK_OVERRIDE {
         return fName.c_str();
     }
 
@@ -79,7 +81,7 @@
 class ForcedIntComparisonBench : public ScalarBench {
 public:
     ForcedIntComparisonBench(void* param)
-        : INHERITED(param, "compare_forced_int") {
+    : INHERITED(param, "compare_forced_int") {
         init9(fArray);
     }
 protected:
@@ -97,8 +99,80 @@
     typedef ScalarBench INHERITED;
 };
 
+class IsFiniteScalarBench : public ScalarBench {
+public:
+    IsFiniteScalarBench(void* param) : INHERITED(param, "isfinite") {
+        SkRandom rand;
+        for (size_t i = 0; i < ARRAY_N; ++i) {
+            fArray[i] = rand.nextSScalar1();
+        }
+    }
+protected:
+    virtual int mulLoopCount() const { return 1; }
+    virtual void performTest() SK_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
+            // value as an int (even though we tell it that it is a float)
+            sum += SkScalarIsFinite(-fArray[i]);
+        }
+        // we do this so the compiler won't optimize our loop away...
+        this->doSomething(fArray, sum);
+    }
+
+    virtual void doSomething(SkScalar array[], int sum) {}
+private:
+    enum {
+        ARRAY_N = 64
+    };
+    SkScalar fArray[ARRAY_N];
+
+    typedef ScalarBench INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class RectBoundsBench : public SkBenchmark {
+    enum {
+        PTS = 100,
+        N = SkBENCHLOOP(10000)
+    };
+    SkPoint fPts[PTS];
+
+public:
+    RectBoundsBench(void* param) : INHERITED(param) {
+        SkRandom rand;
+        for (int i = 0; i < PTS; ++i) {
+            fPts[i].fX = rand.nextSScalar1();
+            fPts[i].fY = rand.nextSScalar1();
+        }
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "rect_bounds";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkRect r;
+        for (int i = 0; i < N; ++i) {
+            r.set(fPts, PTS);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 static SkBenchmark* S0(void* p) { return new FloatComparisonBench(p); }
 static SkBenchmark* S1(void* p) { return new ForcedIntComparisonBench(p); }
+static SkBenchmark* S2(void* p) { return new RectBoundsBench(p); }
+static SkBenchmark* S3(void* p) { return new IsFiniteScalarBench(p); }
 
 static BenchRegistry gReg0(S0);
 static BenchRegistry gReg1(S1);
+static BenchRegistry gReg2(S2);
+static BenchRegistry gReg3(S3);
diff --git a/bench/ShaderMaskBench.cpp b/bench/ShaderMaskBench.cpp
index 62be6c5..0e8e4bb 100644
--- a/bench/ShaderMaskBench.cpp
+++ b/bench/ShaderMaskBench.cpp
@@ -11,7 +11,6 @@
 #include "SkFontHost.h"
 #include "SkPaint.h"
 #include "SkRandom.h"
-#include "SkSfntUtils.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 
@@ -52,7 +51,7 @@
 
 protected:
     virtual const char* onGetName() {
-        fName.printf("shadermask", SkScalarToFloat(fPaint.getTextSize()));
+        fName.printf("shadermask");
         fName.appendf("_%s", fontQualityName(fPaint));
         fName.appendf("_%02X", fPaint.getAlpha());
         return fName.c_str();
@@ -71,14 +70,14 @@
 
         const SkScalar x0 = SkIntToScalar(-10);
         const SkScalar y0 = SkIntToScalar(-10);
-        
+
         paint.setTextSize(SkIntToScalar(12));
         for (int i = 0; i < N; i++) {
             SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
             SkScalar y = y0 + rand.nextUScalar1() * dim.fY;
             canvas->drawText(fText.c_str(), fText.size(), x, y, paint);
         }
-        
+
         paint.setTextSize(SkIntToScalar(48));
         for (int i = 0; i < N/4; i++) {
             SkScalar x = x0 + rand.nextUScalar1() * dim.fX;
@@ -106,4 +105,3 @@
 static BenchRegistry gReg11(Fact11);
 static BenchRegistry gReg20(Fact20);
 static BenchRegistry gReg21(Fact21);
-
diff --git a/bench/SkBenchLogger.cpp b/bench/SkBenchLogger.cpp
new file mode 100644
index 0000000..7cda327
--- /dev/null
+++ b/bench/SkBenchLogger.cpp
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchLogger.h"
+#include "SkStream.h"
+
+SkBenchLogger::SkBenchLogger()
+: fFileStream(NULL) {}
+
+SkBenchLogger::~SkBenchLogger() {
+    if (fFileStream) {
+        SkDELETE(fFileStream);
+    }
+}
+
+bool SkBenchLogger::SetLogFile(const char *file) {
+    fFileStream = SkNEW_ARGS(SkFILEWStream, (file));
+    return fFileStream->isValid();
+}
+
+void SkBenchLogger::fileWrite(const char msg[], size_t size) {
+    if (fFileStream && fFileStream->isValid()) {
+        fFileStream->write(msg, size);
+    }
+}
diff --git a/bench/SkBenchLogger.h b/bench/SkBenchLogger.h
new file mode 100644
index 0000000..5eed9d3
--- /dev/null
+++ b/bench/SkBenchLogger.h
@@ -0,0 +1,77 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkBenchLogger_DEFINED
+#define SkBenchLogger_DEFINED
+
+#include "SkTypes.h"
+#include "SkString.h"
+
+class SkFILEWStream;
+
+/**
+ * Class that allows logging to a file while simultaneously logging to stdout/stderr.
+ */
+class SkBenchLogger {
+public:
+    SkBenchLogger();
+
+    /**
+     * Not virtual, since this class is not intended to be subclassed.
+     */
+    ~SkBenchLogger();
+
+    /**
+     * Specify a file to write progress logs to. Unless this is called with a valid file path,
+     * SkBenchLogger will only write to stdout/stderr.
+     */
+    bool SetLogFile(const char file[]);
+
+    /**
+     * Log an error to stderr, taking a C style string as input.
+     */
+    void logError(const char msg[]) { this->nativeLogError(msg); }
+
+    /**
+     * Log an error to stderr, taking an SkString as input.
+     */
+    void logError(const SkString& str) { this->nativeLogError(str.c_str()); }
+
+    /**
+     * Log the progress of the bench tool to both stdout and the log file specified by SetLogFile,
+     * if any, taking a C style string as input.
+     */
+    void logProgress(const char msg[]) {
+        this->nativeLogProgress(msg);
+        this->fileWrite(msg, strlen(msg));
+    }
+
+    /**
+     * Log the progress of the bench tool to both stdout and the log file specified by SetLogFile,
+     * if any, taking an SkString as input.
+     */
+    void logProgress(const SkString& str) {
+        this->nativeLogProgress(str.c_str());
+        this->fileWrite(str.c_str(), str.size());
+    }
+
+private:
+#ifdef SK_BUILD_FOR_ANDROID
+    void nativeLogError(const char msg[]) { SkDebugf("%s", msg); }
+    void nativeLogProgress(const char msg[]) { SkDebugf("%s", msg); }
+#else
+    void nativeLogError(const char msg[]) { fprintf(stderr, "%s", msg); }
+    void nativeLogProgress(const char msg[]) { printf("%s", msg); }
+#endif
+
+    void fileWrite(const char msg[], size_t size);
+
+    SkFILEWStream* fFileStream;
+};
+
+#endif // SkBenchLogger_DEFINED
diff --git a/bench/SkBenchmark.cpp b/bench/SkBenchmark.cpp
index a69402b..6afcd8e 100644
--- a/bench/SkBenchmark.cpp
+++ b/bench/SkBenchmark.cpp
@@ -9,6 +9,8 @@
 #include "SkPaint.h"
 #include "SkParse.h"
 
+SK_DEFINE_INST_COUNT(SkBenchmark)
+
 template BenchRegistry* BenchRegistry::gHead;
 
 SkBenchmark::SkBenchmark(void* defineDict) {
@@ -17,6 +19,7 @@
     fForceAA = true;
     fDither = SkTriState::kDefault;
     fHasStrokeWidth = false;
+    fIsRendering = true;
 }
 
 const char* SkBenchmark::getName() {
@@ -27,10 +30,18 @@
     return this->onGetSize();
 }
 
+void SkBenchmark::preDraw() {
+    this->onPreDraw();
+}
+
 void SkBenchmark::draw(SkCanvas* canvas) {
     this->onDraw(canvas);
 }
 
+void SkBenchmark::postDraw() {
+    this->onPostDraw();
+}
+
 void SkBenchmark::setupPaint(SkPaint* paint) {
     paint->setAlpha(fForceAlpha);
     paint->setAntiAlias(fForceAA);
diff --git a/bench/SkBenchmark.h b/bench/SkBenchmark.h
index 5019b23..001b3ab 100644
--- a/bench/SkBenchmark.h
+++ b/bench/SkBenchmark.h
@@ -13,6 +13,20 @@
 #include "SkTDict.h"
 #include "SkTRegistry.h"
 
+#define DEF_BENCH(code) \
+static SkBenchmark* SK_MACRO_APPEND_LINE(F_)(void* p) { code; } \
+static BenchRegistry SK_MACRO_APPEND_LINE(R_)(SK_MACRO_APPEND_LINE(F_));
+
+/*
+ *  With the above macros, you can register benches as follows (at the bottom
+ *  of your .cpp)
+ *
+ *  DEF_BENCH(new MyBenchmark(p, ...))
+ *  DEF_BENCH(new MyBenchmark(p, ...))
+ *  DEF_BENCH(new MyBenchmark(p, ...))
+ */
+
+
 #ifdef SK_DEBUG
     #define SkBENCHLOOP(n) 1
 #else
@@ -33,24 +47,37 @@
 
 class SkBenchmark : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkBenchmark)
+
     SkBenchmark(void* defineDict);
 
     const char* getName();
     SkIPoint getSize();
+
+    // Call before draw, allows the benchmark to do setup work outside of the
+    // timer. When a benchmark is repeatedly drawn, this should be called once
+    // before the initial draw.
+    void preDraw();
+
     void draw(SkCanvas*);
-    
+
+    // Call after draw, allows the benchmark to do cleanup work outside of the
+    // timer. When a benchmark is repeatedly drawn, this is only called once
+    // after the last draw.
+    void postDraw();
+
     void setForceAlpha(int alpha) {
         fForceAlpha = alpha;
     }
-    
+
     void setForceAA(bool aa) {
         fForceAA = aa;
     }
-    
+
     void setForceFilter(bool filter) {
         fForceFilter = filter;
     }
-    
+
     void setDither(SkTriState::State state) {
         fDither = state;
     }
@@ -68,17 +95,28 @@
       return fHasStrokeWidth;
     }
 
+    /** If true; the benchmark does rendering; if false, the benchmark
+        doesn't, and so need not be re-run in every different rendering
+        mode. */
+    bool isRendering() {
+        return fIsRendering;
+    }
+
     const char* findDefine(const char* key) const;
     bool findDefine32(const char* key, int32_t* value) const;
     bool findDefineScalar(const char* key, SkScalar* value) const;
 
 protected:
-    void setupPaint(SkPaint* paint);
+    virtual void setupPaint(SkPaint* paint);
 
     virtual const char* onGetName() = 0;
+    virtual void onPreDraw() {}
     virtual void onDraw(SkCanvas*) = 0;
+    virtual void onPostDraw() {}
 
     virtual SkIPoint onGetSize();
+    /// Defaults to true.
+    bool    fIsRendering;
 
 private:
     const SkTDict<const char*>* fDict;
@@ -88,6 +126,8 @@
     SkTriState::State  fDither;
     bool    fHasStrokeWidth;
     SkScalar strokeWidth;
+
+    typedef SkRefCnt INHERITED;
 };
 
 typedef SkTRegistry<SkBenchmark*, void*> BenchRegistry;
diff --git a/bench/SortBench.cpp b/bench/SortBench.cpp
new file mode 100644
index 0000000..8d6a716
--- /dev/null
+++ b/bench/SortBench.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkRandom.h"
+#include "SkTSort.h"
+#include "SkString.h"
+
+#ifdef SK_DEBUG
+// Windows-debug builds (at least) don't implement tail-recursion, and we have
+// a bench that triggers a worst-case behavior in SkTQSort (w/ repeated keys)
+// which can overflow the stack if N is too big. So we reduce it for debug
+// builds (for which we don't care about sorting performance anyways).
+static const int N = 100;
+#else
+static const int N = 1000;
+#endif
+
+static void rand_proc(int array[], int count) {
+    SkRandom rand;
+    for (int i = 0; i < count; ++i) {
+        array[i] = rand.nextS();
+    }
+}
+
+static void randN_proc(int array[], int count) {
+    SkRandom rand;
+    int mod = N / 10;
+    for (int i = 0; i < count; ++i) {
+        array[i] = rand.nextU() % mod;
+    }
+}
+
+static void forward_proc(int array[], int count) {
+    for (int i = 0; i < count; ++i) {
+        array[i] = i;
+    }
+}
+
+static void backward_proc(int array[], int count) {
+    for (int i = 0; i < count; ++i) {
+        array[i] = -i;
+    }
+}
+
+static void same_proc(int array[], int count) {
+    for (int i = 0; i < count; ++i) {
+        array[i] = count;
+    }
+}
+
+typedef void (*SortProc)(int array[], int count);
+
+enum Type {
+    kRand, kRandN, kFore, kBack, kSame
+};
+
+static const struct {
+    const char* fName;
+    SortProc    fProc;
+} gRec[] = {
+    { "rand", rand_proc },
+    { "rand10", randN_proc },
+    { "forward", forward_proc },
+    { "backward", backward_proc },
+    { "repeated", same_proc },
+};
+
+static void skqsort_sort(int array[], int count) {
+    SkTQSort<int>(array, array + count);
+}
+
+static void skheap_sort(int array[], int count) {
+    SkTHeapSort<int>(array, count);
+}
+
+extern "C" {
+    static int int_compare(const void* a, const void* b) {
+        const int ai = *(const int*)a;
+        const int bi = *(const int*)b;
+        return ai < bi ? -1 : (ai > bi);
+    }
+}
+
+static void qsort_sort(int array[], int count) {
+    qsort(array, count, sizeof(int), int_compare);
+}
+
+enum SortType {
+    kSKQSort, kSKHeap, kQSort
+};
+
+static const struct {
+    const char* fName;
+    SortProc    fProc;
+} gSorts[] = {
+    { "skqsort", skqsort_sort },
+    { "skheap", skheap_sort },
+    { "qsort", qsort_sort },
+};
+
+class SortBench : public SkBenchmark {
+    SkString    fName;
+    enum { MAX = 100000 };
+    int         fUnsorted[MAX];
+    int         fSorted[MAX];
+    int         fCount;
+    SortProc    fSortProc;
+
+public:
+    SortBench(void* param, Type t, int n, SortType s) : INHERITED(param) {
+        if (n > MAX) {
+            n = MAX;
+        }
+        fName.printf("sort_%s_%s", gSorts[s].fName, gRec[t].fName);
+        fCount = n;
+        gRec[t].fProc(fUnsorted, n);
+        fSortProc = gSorts[s].fProc;
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        int n = SkBENCHLOOP(200);
+        for (int i = 0; i < n; i++) {
+            memcpy(fSorted, fUnsorted, fCount * sizeof(int));
+            fSortProc(fSorted, fCount);
+#ifdef SK_DEBUG
+            for (int j = 1; j < fCount; ++j) {
+                SkASSERT(fSorted[j - 1] <= fSorted[j]);
+            }
+#endif
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* NewSkQSort(void* param, Type t) {
+    return new SortBench(param, t, N, kSKQSort);
+}
+static SkBenchmark* NewSkHeap(void* param, Type t) {
+    return new SortBench(param, t, N, kSKHeap);
+}
+static SkBenchmark* NewQSort(void* param, Type t) {
+    return new SortBench(param, t, N, kQSort);
+}
+
+DEF_BENCH( return NewSkQSort(p, kRand); )
+DEF_BENCH( return NewSkHeap(p, kRand); )
+DEF_BENCH( return NewQSort(p, kRand); )
+
+DEF_BENCH( return NewSkQSort(p, kRandN); )
+DEF_BENCH( return NewSkHeap(p, kRandN); )
+DEF_BENCH( return NewQSort(p, kRandN); )
+
+DEF_BENCH( return NewSkQSort(p, kFore); )
+DEF_BENCH( return NewSkHeap(p, kFore); )
+DEF_BENCH( return NewQSort(p, kFore); )
+
+DEF_BENCH( return NewSkQSort(p, kBack); )
+DEF_BENCH( return NewSkHeap(p, kBack); )
+DEF_BENCH( return NewQSort(p, kBack); )
+
+DEF_BENCH( return NewSkQSort(p, kSame); )
+DEF_BENCH( return NewSkHeap(p, kSame); )
+DEF_BENCH( return NewQSort(p, kSame); )
diff --git a/bench/TableBench.cpp b/bench/TableBench.cpp
new file mode 100644
index 0000000..e6efe25
--- /dev/null
+++ b/bench/TableBench.cpp
@@ -0,0 +1,81 @@
+/*
+ * 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkRect.h"
+
+static const SkScalar kCellWidth = SkIntToScalar(20);
+static const SkScalar kCellHeight = SkIntToScalar(10);
+
+// This bench draws a table in the manner of Google spreadsheet and sahadan.com.
+//           ____________ ___
+//          |     1      | 2 |
+//          |____________|___|
+//          |     3      | 4 |
+//          |____________|___|
+//
+// Areas 1-4 are first all draw white. Areas 3&4 are then drawn grey. Areas
+// 2&4 are then drawn grey. Areas 2&3 are thus double drawn while area 4 is
+// triple drawn.
+// This trio of drawRects is then repeat for the next cell.
+class TableBench : public SkBenchmark {
+public:
+
+    static const int kNumIterations = SkBENCHLOOP(10);
+    static const int kNumRows = 48;
+    static const int kNumCols = 32;
+
+    TableBench(void* param)
+        : INHERITED(param) {
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return "tablebench";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPaint cellPaint;
+        cellPaint.setColor(0xFFFFFFF);
+
+        SkPaint borderPaint;
+        borderPaint.setColor(0xFFCCCCCC);
+
+        for (int i = 0; i < kNumIterations; ++i) {
+            for (int row = 0; row < kNumRows; ++row) {
+                for (int col = 0; col < kNumCols; ++col) {
+                    SkRect cell = SkRect::MakeLTRB(col * kCellWidth,
+                                                   row * kCellHeight,
+                                                   (col+1) * kCellWidth,
+                                                   (row+1) * kCellHeight);
+                    canvas->drawRect(cell, cellPaint);
+
+                    SkRect bottom = SkRect::MakeLTRB(col * kCellWidth,
+                                                     row * kCellHeight + (kCellHeight-SK_Scalar1),
+                                                     (col+1) * kCellWidth,
+                                                     (row+1) * kCellHeight);
+                    canvas->drawRect(bottom, borderPaint);
+
+                    SkRect right = SkRect::MakeLTRB(col * kCellWidth + (kCellWidth-SK_Scalar1),
+                                                    row * kCellHeight,
+                                                    (col+1) * kCellWidth,
+                                                    (row+1) * kCellHeight);
+                    canvas->drawRect(right, borderPaint);
+                }
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+static SkBenchmark* gFactory(void* p) { return new TableBench(p); }
+
+static BenchRegistry gRegistry(gFactory);
diff --git a/bench/TextBench.cpp b/bench/TextBench.cpp
index ed8fb0e..9334c33 100644
--- a/bench/TextBench.cpp
+++ b/bench/TextBench.cpp
@@ -10,7 +10,6 @@
 #include "SkFontHost.h"
 #include "SkPaint.h"
 #include "SkRandom.h"
-#include "SkSfntUtils.h"
 #include "SkString.h"
 #include "SkTemplates.h"
 
@@ -50,6 +49,7 @@
 public:
     TextBench(void* param, const char text[], int ps,
               SkColor color, FontQuality fq, bool doPos = false) : INHERITED(param) {
+        fPos = NULL;
         fFQ = fq;
         fDoPos = doPos;
         fText.set(text);
diff --git a/bench/TileBench.cpp b/bench/TileBench.cpp
new file mode 100644
index 0000000..7f2e7a8
--- /dev/null
+++ b/bench/TileBench.cpp
@@ -0,0 +1,136 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBenchmark.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkString.h"
+
+static void create_gradient(SkBitmap* bm) {
+    SkASSERT(1 == bm->width());
+    const int height = bm->height();
+
+    float deltaB = 255.0f / height;
+    float blue = 255.0f;
+
+    SkAutoLockPixels lock(*bm);
+    for (int y = 0; y < height; y++) {
+        *bm->getAddr32(0, y) = SkColorSetRGB(0, 0, (U8CPU) blue);
+        blue -= deltaB;
+    }
+}
+
+// Test out the special case of a tiled 1xN texture. Test out opacity,
+// filtering and the different tiling modes
+class ConstXTileBench : public SkBenchmark {
+    SkPaint             fPaint;
+    SkString            fName;
+    bool                fDoFilter;
+    bool                fDoTrans;
+    bool                fDoScale;
+    enum { N = SkBENCHLOOP(20) };
+    static const int kWidth = 1;
+    static const int kHeight = 300;
+
+public:
+    ConstXTileBench(void* param,
+                    SkShader::TileMode xTile,
+                    SkShader::TileMode yTile,
+                    bool doFilter,
+                    bool doTrans,
+                    bool doScale)
+        : INHERITED(param)
+        , fDoFilter(doFilter)
+        , fDoTrans(doTrans)
+        , fDoScale(doScale) {
+        SkBitmap bm;
+
+        bm.setConfig(SkBitmap::kARGB_8888_Config, kWidth, kHeight);
+
+        bm.allocPixels();
+        bm.eraseColor(SK_ColorWHITE);
+        bm.setIsOpaque(true);
+
+        create_gradient(&bm);
+
+        SkShader* s = SkShader::CreateBitmapShader(bm, xTile, yTile);
+        fPaint.setShader(s)->unref();
+
+        fName.printf("constXTile_");
+
+        static const char* gTileModeStr[SkShader::kTileModeCount] = { "C", "R", "M" };
+        fName.append(gTileModeStr[xTile]);
+        fName.append(gTileModeStr[yTile]);
+
+        if (doFilter) {
+            fName.append("_filter");
+        }
+
+        if (doTrans) {
+            fName.append("_trans");
+        }
+
+        if (doScale) {
+            fName.append("_scale");
+        }
+    }
+
+protected:
+    virtual const char* onGetName() {
+        return fName.c_str();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint(fPaint);
+        this->setupPaint(&paint);
+        paint.setFilterBitmap(fDoFilter);
+        if (fDoTrans) {
+            paint.setColor(SkColorSetARGBMacro(0x80, 0xFF, 0xFF, 0xFF));
+        }
+
+        SkRect r;
+
+        if (fDoScale) {
+            r = SkRect::MakeWH(SkIntToScalar(2 * 640), SkIntToScalar(2 * 480));
+            canvas->scale(SK_ScalarHalf, SK_ScalarHalf);
+        } else {
+            r = SkRect::MakeWH(SkIntToScalar(640), SkIntToScalar(480));
+        }
+
+        SkPaint bgPaint;
+        bgPaint.setColor(SK_ColorWHITE);
+
+        for (int i = 0; i < N; i++) {
+            if (fDoTrans) {
+                canvas->drawRect(r, bgPaint);
+            }
+
+            canvas->drawRect(r, paint);
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, false, false, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, false, false, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, false, false, true))
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, true, false, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, true, false, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, true, false, false))
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, false, true, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, false, true, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, false, true, true))
+
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode, true, true, false))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode, true, true, true))
+DEF_BENCH(return new ConstXTileBench(p, SkShader::kMirror_TileMode, SkShader::kMirror_TileMode, true, true, false))
diff --git a/bench/TimerData.cpp b/bench/TimerData.cpp
new file mode 100644
index 0000000..18d41e4
--- /dev/null
+++ b/bench/TimerData.cpp
@@ -0,0 +1,109 @@
+
+/*
+ * 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 "TimerData.h"
+
+#include "BenchTimer.h"
+#include <limits>
+
+using namespace std;
+
+TimerData::TimerData(const SkString& perIterTimeFormat, const SkString& normalTimeFormat)
+: fWallStr(" msecs = ")
+, fTruncatedWallStr(" Wmsecs = ")
+, fCpuStr(" cmsecs = ")
+, fTruncatedCpuStr(" Cmsecs = ")
+, fGpuStr(" gmsecs = ")
+, fWallSum(0.0)
+, fWallMin((numeric_limits<double>::max)()) // Extra parens to make the windows build work, due to
+                                            // 'max' macro
+, fTruncatedWallSum(0.0)
+, fTruncatedWallMin((numeric_limits<double>::max)())
+, fCpuSum(0.0)
+, fCpuMin((numeric_limits<double>::max)())
+, fTruncatedCpuSum(0.0)
+, fTruncatedCpuMin((numeric_limits<double>::max)())
+, fGpuSum(0.0)
+, fGpuMin((numeric_limits<double>::max)())
+, fPerIterTimeFormat(perIterTimeFormat)
+, fNormalTimeFormat(normalTimeFormat)
+{}
+
+static double Min(double a, double b) {
+    return (a < b) ? a : b;
+}
+
+void TimerData::appendTimes(BenchTimer* timer, bool last) {
+    SkASSERT(timer != NULL);
+    SkString formatString(fPerIterTimeFormat);
+    if (!last) {
+        formatString.append(",");
+    }
+    const char* format = formatString.c_str();
+    fWallStr.appendf(format, timer->fWall);
+    fCpuStr.appendf(format, timer->fCpu);
+    fTruncatedWallStr.appendf(format, timer->fTruncatedWall);
+    fTruncatedCpuStr.appendf(format, timer->fTruncatedCpu);
+    fGpuStr.appendf(format, timer->fGpu);
+
+    // Store the minimum values. We do not need to special case the first time since we initialized
+    // to max double.
+    fWallMin = Min(fWallMin, timer->fWall);
+    fCpuMin  = Min(fCpuMin,  timer->fCpu);
+    fTruncatedWallMin = Min(fTruncatedWallMin, timer->fTruncatedWall);
+    fTruncatedCpuMin  = Min(fTruncatedCpuMin,  timer->fTruncatedCpu);
+    fGpuMin  = Min(fGpuMin,  timer->fGpu);
+
+    // Tally the sum of each timer type.
+    fWallSum += timer->fWall;
+    fCpuSum += timer->fCpu;
+    fTruncatedWallSum += timer->fTruncatedWall;
+    fTruncatedCpuSum += timer->fTruncatedCpu;
+    fGpuSum += timer->fGpu;
+
+}
+
+SkString TimerData::getResult(bool logPerIter, bool printMin, int repeatDraw,
+                              const char *configName, bool showWallTime, bool showTruncatedWallTime,
+                              bool showCpuTime, bool showTruncatedCpuTime, bool showGpuTime) {
+    // output each repeat (no average) if logPerIter is set,
+    // otherwise output only the average
+    if (!logPerIter) {
+        const char* format = fNormalTimeFormat.c_str();
+        fWallStr.set(" msecs = ");
+        fWallStr.appendf(format, printMin ? fWallMin : fWallSum / repeatDraw);
+        fCpuStr.set(" cmsecs = ");
+        fCpuStr.appendf(format, printMin ? fCpuMin : fCpuSum / repeatDraw);
+        fTruncatedWallStr.set(" Wmsecs = ");
+        fTruncatedWallStr.appendf(format,
+                                  printMin ? fTruncatedWallMin : fTruncatedWallSum / repeatDraw);
+        fTruncatedCpuStr.set(" Cmsecs = ");
+        fTruncatedCpuStr.appendf(format,
+                                 printMin ? fTruncatedCpuMin : fTruncatedCpuSum / repeatDraw);
+        fGpuStr.set(" gmsecs = ");
+        fGpuStr.appendf(format, printMin ? fGpuMin : fGpuSum / repeatDraw);
+    }
+    SkString str;
+    str.printf("  %4s:", configName);
+    if (showWallTime) {
+        str += fWallStr;
+    }
+    if (showTruncatedWallTime) {
+        str += fTruncatedWallStr;
+    }
+    if (showCpuTime) {
+        str += fCpuStr;
+    }
+    if (showTruncatedCpuTime) {
+        str += fTruncatedCpuStr;
+    }
+    if (showGpuTime && fGpuSum > 0) {
+        str += fGpuStr;
+    }
+    return str;
+}
diff --git a/bench/TimerData.h b/bench/TimerData.h
new file mode 100644
index 0000000..d97a063
--- /dev/null
+++ b/bench/TimerData.h
@@ -0,0 +1,46 @@
+
+/*
+ * 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 TimerData_DEFINED
+#define TimerData_DEFINED
+
+#include "SkString.h"
+
+class BenchTimer;
+
+class TimerData {
+public:
+    TimerData(const SkString& perIterTimeFormat, const SkString& normalTimeFormat);
+
+    /**
+     * Append the value from each timer in BenchTimer to our various strings, and update the
+     * minimum and sum times.
+     * @param BenchTimer Must not be null.
+     * @param last True if this is the last set of times to add.
+     */
+    void appendTimes(BenchTimer*, bool last);
+    SkString getResult(bool logPerIter, bool printMin, int repeatDraw, const char* configName,
+                       bool showWallTime, bool showTruncatedWallTime, bool showCpuTime,
+                       bool showTruncatedCpuTime, bool showGpuTime);
+private:
+    SkString fWallStr;
+    SkString fTruncatedWallStr;
+    SkString fCpuStr;
+    SkString fTruncatedCpuStr;
+    SkString fGpuStr;
+    double fWallSum, fWallMin;
+    double fTruncatedWallSum, fTruncatedWallMin;
+    double fCpuSum, fCpuMin;
+    double fTruncatedCpuSum, fTruncatedCpuMin;
+    double fGpuSum, fGpuMin;
+
+    SkString fPerIterTimeFormat;
+    SkString fNormalTimeFormat;
+};
+
+#endif // TimerData_DEFINED
diff --git a/bench/VertBench.cpp b/bench/VertBench.cpp
index 98df449..e5053d9 100644
--- a/bench/VertBench.cpp
+++ b/bench/VertBench.cpp
@@ -39,7 +39,7 @@
         idx[0] = n; idx[1] = n + 1; idx[2] = rb + n + 1;
         idx[3] = n; idx[4] = rb + n + 1; idx[5] = n + rb;
     }
-    
+
 public:
     VertBench(void* param) : INHERITED(param) {
         const SkScalar dx = SkIntToScalar(W) / COL;
@@ -55,7 +55,7 @@
                 pts->set(xx, yy);
                 pts += 1;
                 xx += dx;
-                
+
                 if (x < COL && y < ROW) {
                     load_2_tris(idx, x, y, COL + 1);
                     for (int i = 0; i < 6; i++) {
diff --git a/bench/WriterBench.cpp b/bench/WriterBench.cpp
new file mode 100644
index 0000000..faf17b5
--- /dev/null
+++ b/bench/WriterBench.cpp
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright 2012 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 "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkWriter32.h"
+
+class WriterBench : public SkBenchmark {
+public:
+    WriterBench(void* param) : INHERITED(param) {
+        fIsRendering = false;
+    }
+
+protected:
+    virtual const char* onGetName() SK_OVERRIDE {
+        return "writer";
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        static const char gStr[] = "abcdefghimjklmnopqrstuvwxyz";
+        static const size_t gLen = strlen(gStr);
+        SkWriter32 writer(256 * 4);
+        for (int i = 0; i < SkBENCHLOOP(800); i++) {
+            for (size_t j = 0; j <= gLen; j++) {
+                writer.writeString(gStr, j);
+            }
+        }
+    }
+
+private:
+    typedef SkBenchmark INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static SkBenchmark* fact(void* p) { return new WriterBench(p); }
+static BenchRegistry gReg(fact);
diff --git a/bench/bench_analyze.py b/bench/bench_analyze.py
new file mode 100755
index 0000000..74751cb
--- /dev/null
+++ b/bench/bench_analyze.py
@@ -0,0 +1,247 @@
+#!/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.
+
+""" Analyze recent bench data from graphs, and output suggested ranges.
+
+This script reads and parses Skia benchmark values from the xhtml files
+generated by bench_graph_svg.py, and outputs an html file containing suggested
+bench ranges to use in bench_expectations.txt, with analytical plots.
+"""
+
+__author__ = 'bensong@google.com (Ben Chen)'
+
+import getopt
+import math
+import re
+import sys
+import urllib
+from datetime import datetime
+
+
+# Constants for calculating suggested bench ranges.
+WINDOW = 5  # Moving average sliding window size.
+# We use moving average as expected bench value, and calculate average Variance
+# of bench from the moving average. Set range to be [X_UB * Variance above
+# moving average, X_LB * Variance below moving average] of latest revision.
+X_UB = 4.0
+X_LB = 5.0
+
+# List of platforms.
+PLATFORMS = ['GalaxyNexus_4-1_Float_Release',
+             'Mac_Float_NoDebug_32',
+             'Mac_Float_NoDebug_64',
+             'MacMiniLion_Float_NoDebug_32',
+             'MacMiniLion_Float_NoDebug_64',
+             'Nexus7_4-1_Float_Release',
+             'Shuttle_Ubuntu12_ATI5770_Float_Release_64',
+             'Shuttle_Win7_Intel_Float_Release_32',
+             'Shuttle_Win7_Intel_Float_Release_64',
+             'Xoom_4-1_Float_Release'
+            ]
+
+# List of bench representation algorithms. Flag "-a" is chosen from the list.
+ALGS = ['25th', 'avg', 'med', 'min']
+
+# Regular expressions for parsing bench/revision values.
+HEIGHT_RE = 'height (\d+\.\d+) corresponds to bench value (\d+\.\d+).-->'
+REV_RE = '<rect id="(\d+)" x="(\d+\.\d+)" y="'  # Revision corresponding x.
+LINE_RE = '<polyline id="(.*)".*points="(.*)"/>'  # Bench value lines.
+
+# Bench graph url pattern.
+INPUT_URL_TEMPLATE = ('http://chromium-skia-gm.commondatastorage.googleapis.com'
+                      '/graph-Skia_%s-2.xhtml')
+
+# Output HTML elements and templates.
+HTML_HEAD = ('<html><head><title>Skia Bench Expected Ranges</title>'
+             '<script type="text/javascript" src="https://skia.googlecode.com/'
+             'svn/buildbot/dygraph-combined.js"></script></head><body>Please '
+             'adjust values as appropriate and update benches to monitor in '
+             'bench/bench_expectations.txt.<br><br>')
+HTML_SUFFIX = '</body></html>'
+GRAPH_PREFIX = ('<br>%s<br><div id="%s" style="width:400px;height:200px"></div>'
+                '<script type="text/javascript">g%s=new Dygraph('
+                'document.getElementById("%s"),"rev,bench,alert\\n')
+GRAPH_SUFFIX = ('",{customBars: true,"alert":{strokeWidth:0.0,drawPoints:true,'
+                'pointSize:4,highlightCircleSize:6}});</script>')
+
+
+def Usage():
+  """Prints flag usage information."""
+  print '-a <representation-algorithm>: defaults to "25th".'
+  print '  If set, must be one of the list element in ALGS defined above.'
+  print '-b <bench-prefix>: prefix of matching bench names to analyze.'
+  print '  Only include benchmarks whose names start with this string.'
+  print '  Cannot be empty, because there are too many benches overall.'
+  print '-o <file>: html output filename. Output to STDOUT if not set.'
+  print '-p <platform-prefix>: prefix of platform names to analyze.'
+  print '  PLATFORMS has list of matching candidates. Matches all if not set.'
+
+def GetBenchValues(page, bench_prefix):
+  """Returns a dict of matching bench values from the given xhtml page.
+  Args:
+    page: substring used to construct the specific bench graph URL to fetch.
+    bench_prefix: only benches starting with this string will be included.
+
+  Returns:
+    a dict mapping benchmark name and revision combinations to bench values.
+  """
+  height = None
+  max_bench = None
+  height_scale = None
+  revisions = []
+  x_axes = []  # For calculating corresponding revisions.
+  val_dic = {}  # dict[bench_name][revision] -> bench_value
+
+  lines = urllib.urlopen(INPUT_URL_TEMPLATE % page).readlines()
+  for line in lines:
+    height_match = re.search(HEIGHT_RE, line)
+    if height_match:
+      height = float(height_match.group(1))
+      max_bench = float(height_match.group(2))
+      height_scale = max_bench / height
+
+    rev_match = re.search(REV_RE, line)
+    if rev_match:
+      revisions.append(int(rev_match.group(1)))
+      x_axes.append(float(rev_match.group(2)))
+
+    line_match = re.search(LINE_RE, line)
+    if not line_match:
+      continue
+    bench = line_match.group(1)
+    bench = bench[:bench.find('_{')]
+    if not bench.startswith(bench_prefix):
+      continue
+    if bench not in val_dic:
+      val_dic[bench] = {}
+
+    vals = line_match.group(2).strip().split(' ')
+    if len(vals) < WINDOW:  # Too few bench data points; skip.
+      continue
+    for val in vals:
+      x, y = [float(i) for i in val.split(',')]
+      for i in range(len(x_axes)):
+        if x <= x_axes[i]:  # Found corresponding bench revision.
+          break
+      val_dic[bench][revisions[i]] = float(
+          '%.3f' % ((height - y) * height_scale))
+
+  return val_dic
+
+def CreateBenchOutput(page, bench, val_dic):
+  """Returns output for the given page and bench data in dict.
+  Args:
+    page: substring of bench graph webpage, to indicate the bench platform.
+    bench: name of the benchmark to process.
+    val_dic: dict[bench_name][revision] -> bench_value.
+
+  Returns:
+    string of html/javascript as part of the whole script output for the bench.
+  """
+  revs = val_dic[bench].keys()
+  revs.sort()
+  # Uses moving average to calculate expected bench variance, then sets
+  # expectations and ranges accordingly.
+  variances = []
+  moving_avgs = []
+  points = []
+  for rev in revs:
+    points.append(val_dic[bench][rev])
+    if len(points) >= WINDOW:
+      moving_avgs.append(sum(points[-WINDOW:]) / WINDOW)
+      variances.append(abs(points[-1] - moving_avgs[-1]))
+    else:  # For the first WINDOW-1 points, cannot calculate moving average.
+      moving_avgs.append(points[-1])  # Uses actual value as estimates.
+      variances.append(0)
+  if len(variances) >= WINDOW:
+    for i in range(WINDOW - 1):
+      # Backfills estimated variances for the first WINDOW-1 points.
+      variances[i] = variances[WINDOW - 1]
+
+  avg_var = sum(variances) / len(variances)
+  for val in variances:  # Removes outlier variances. Only does one iter.
+    if val > min(X_LB, X_UB) * avg_var:
+      variances.remove(val)
+  avg_var = sum(variances) / len(variances)
+
+  graph_id = '%s_%s' % (bench, page.replace('-', '_'))
+  expectations = '%s,%s,%.2f,%.2f,%.2f' % (bench, page, moving_avgs[-1],
+                                           moving_avgs[-1] - X_LB * avg_var,
+                                           moving_avgs[-1] + X_UB * avg_var)
+  out = GRAPH_PREFIX % (expectations, graph_id, graph_id, graph_id)
+  for i in range(len(revs)):
+    out += '%s,%.2f;%.2f;%.2f,' % (revs[i], moving_avgs[i] - X_LB * avg_var,
+                                   points[i], moving_avgs[i] + X_UB * avg_var)
+    if (points[i] > moving_avgs[i] + X_UB * avg_var or
+        points[i] < moving_avgs[i] - X_LB * avg_var):  # Mark as alert point.
+      out += '%.2f;%.2f;%.2f\\n' % (points[i], points[i], points[i])
+    else:
+      out += 'NaN;NaN;NaN\\n'
+
+  return out
+
+def main():
+  """Parses flags and outputs analysis results."""
+  try:
+    opts, _ = getopt.getopt(sys.argv[1:], 'a:b:o:p:')
+  except getopt.GetoptError, err:
+    Usage()
+    sys.exit(2)
+
+  alg = '25th'
+  bench_prefix = None
+  out_file = None
+  platform_prefix = ''
+  for option, value in opts:
+    if option == '-a':
+      if value not in ALGS:
+        raise Exception('Invalid flag -a (%s): must be set to one of %s.' %
+                        (value, str(ALGS)))
+      alg = value
+    elif option == '-b':
+      bench_prefix = value
+    elif option == '-o':
+      out_file = value
+    elif option == '-p':
+      platform_prefix = value
+    else:
+      Usage()
+      raise Exception('Error handling flags.')
+
+  if not bench_prefix:
+    raise Exception('Must provide nonempty Flag -b (bench name prefix).')
+
+  pages = []
+  for platform in PLATFORMS:
+    if not platform.startswith(platform_prefix):
+      continue
+    pages.append('%s-%s' % (platform, alg))
+
+  if not pages:  # No matching platform found.
+    raise Exception('Flag -p (platform prefix: %s) does not match any of %s.' %
+                    (platform_prefix, str(PLATFORMS)))
+
+  body = ''
+  # Iterates through bench graph xhtml pages for oututting matching benches.
+  for page in pages:
+    bench_value_dict = GetBenchValues(page, bench_prefix)
+    for bench in bench_value_dict:
+      body += CreateBenchOutput(page, bench, bench_value_dict) + GRAPH_SUFFIX
+
+  if not body:
+    raise Exception('No bench outputs. Most likely there are no matching bench'
+                    ' prefix (%s) in Flags -b for platforms %s.\nPlease also '
+                    'check if the bench graph URLs are valid at %s.' % (
+                        bench_prefix, str(PLATFORMS), INPUT_URL_TEMPLATE))
+  if out_file:
+    f = open(out_file, 'w+')
+    f.write(HTML_HEAD + body + HTML_SUFFIX)
+    f.close()
+  else:
+    print HTML_HEAD + body + HTML_SUFFIX
+
+
+if '__main__' == __name__:
+  main()
diff --git a/bench/bench_compare.py b/bench/bench_compare.py
index c1a1ff9..f4f7734 100644
--- a/bench/bench_compare.py
+++ b/bench/bench_compare.py
@@ -1,3 +1,4 @@
+#!/usr/bin/env python
 '''
 Created on May 16, 2011
 
@@ -9,10 +10,16 @@
 
 def usage():
     """Prints simple usage information."""
-    
+
     print '-o <file> the old bench output file.'
     print '-n <file> the new bench output file.'
     print '-h causes headers to be output.'
+    print '-s <stat> the type of statistical analysis used'
+    print '   Not specifying is the same as -s "avg".'
+    print '  avg: average of all data points'
+    print '  min: minimum of all data points'
+    print '  med: median of all data points'
+    print '  25th: twenty-fifth percentile for all data points'
     print '-f <fieldSpec> which fields to output and in what order.'
     print '   Not specifying is the same as -f "bctondp".'
     print '  b: bench'
@@ -22,10 +29,12 @@
     print '  n: new time'
     print '  d: diff'
     print '  p: percent diff'
-    
+    print '-t use tab delimited format for output.'
+    print '--match <bench> only matches benches which begin with <bench>.'
+
 class BenchDiff:
     """A compare between data points produced by bench.
-    
+
     (BenchDataPoint, BenchDataPoint)"""
     def __init__(self, old, new):
         self.old = old
@@ -35,49 +44,33 @@
         if old.time != 0:
             diffp = self.diff / old.time
         self.diffp = diffp
-    
+
     def __repr__(self):
         return "BenchDiff(%s, %s)" % (
                    str(self.new),
                    str(self.old),
                )
-        
+
 def main():
     """Parses command line and writes output."""
-    
+
     try:
-        opts, _ = getopt.getopt(sys.argv[1:], "f:o:n:h")
+        opts, _ = getopt.getopt(sys.argv[1:], "f:o:n:s:ht", ['match='])
     except getopt.GetoptError, err:
         print str(err) 
         usage()
         sys.exit(2)
-    
-    column_formats = {
-        'b' : '{bench: >28} ',
-        'c' : '{config: <4} ',
-        't' : '{time_type: <4} ',
-        'o' : '{old_time: >10.2f} ',
-        'n' : '{new_time: >10.2f} ',
-        'd' : '{diff: >+10.2f} ',
-        'p' : '{diffp: >+8.1%} ',
-    }
-    header_formats = {
-        'b' : '{bench: >28} ',
-        'c' : '{config: <4} ',
-        't' : '{time_type: <4} ',
-        'o' : '{old_time: >10} ',
-        'n' : '{new_time: >10} ',
-        'd' : '{diff: >10} ',
-        'p' : '{diffp: >8} ',
-    }
-    
+
     old = None
     new = None
     column_format = ""
     header_format = ""
     columns = 'bctondp'
     header = False
-    
+    stat_type = "avg"
+    use_tabs = False
+    match_bench = None;
+
     for option, value in opts:
         if option == "-o":
             old = value
@@ -87,14 +80,80 @@
             header = True
         elif option == "-f":
             columns = value
+        elif option == "-s":
+            stat_type = value
+        elif option == "-t":
+            use_tabs = True
+        elif option == "--match":
+            match_bench = value
         else:
             usage()
             assert False, "unhandled option"
-    
+
     if old is None or new is None:
         usage()
         sys.exit(2)
-    
+
+    old_benches = bench_util.parse({}, open(old, 'r'), stat_type)
+    new_benches = bench_util.parse({}, open(new, 'r'), stat_type)
+
+    bench_diffs = []
+    for old_bench in old_benches:
+        #filter benches by the match criteria
+        if match_bench and not old_bench.bench.startswith(match_bench):
+            continue
+
+        #filter new_benches for benches that match old_bench
+        new_bench_match = [bench for bench in new_benches
+            if old_bench.bench == bench.bench and
+               old_bench.config == bench.config and
+               old_bench.time_type == bench.time_type
+        ]
+        if (len(new_bench_match) < 1):
+            continue
+        bench_diffs.append(BenchDiff(old_bench, new_bench_match[0]))
+
+    if use_tabs:
+        column_formats = {
+            'b' : '{bench}\t',
+            'c' : '{config}\t',
+            't' : '{time_type}\t',
+            'o' : '{old_time: 0.2f}\t',
+            'n' : '{new_time: 0.2f}\t',
+            'd' : '{diff: 0.2f}\t',
+            'p' : '{diffp: 0.1%}\t',
+        }
+        header_formats = {
+            'b' : '{bench}\t',
+            'c' : '{config}\t',
+            't' : '{time_type}\t',
+            'o' : '{old_time}\t',
+            'n' : '{new_time}\t',
+            'd' : '{diff}\t',
+            'p' : '{diffp}\t',
+        }
+    else:
+        bench_max_len = max(map(lambda b: len(b.old.bench), bench_diffs))
+        config_max_len = max(map(lambda b: len(b.old.config), bench_diffs))
+        column_formats = {
+            'b' : '{bench: >%d} ' % (bench_max_len),
+            'c' : '{config: <%d} ' % (config_max_len),
+            't' : '{time_type: <4} ',
+            'o' : '{old_time: >10.2f} ',
+            'n' : '{new_time: >10.2f} ',
+            'd' : '{diff: >+10.2f} ',
+            'p' : '{diffp: >+8.1%} ',
+        }
+        header_formats = {
+            'b' : '{bench: >%d} ' % (bench_max_len),
+            'c' : '{config: <%d} ' % (config_max_len),
+            't' : '{time_type: <4} ',
+            'o' : '{old_time: >10} ',
+            'n' : '{new_time: >10} ',
+            'd' : '{diff: >10} ',
+            'p' : '{diffp: >8} ',
+        }
+
     for column_char in columns:
         if column_formats[column_char]:
             column_format += column_formats[column_char]
@@ -102,7 +161,7 @@
         else:
             usage()
             sys.exit(2)
-    
+
     if header:
         print header_format.format(
             bench='bench'
@@ -113,22 +172,7 @@
             , diff='diff'
             , diffp='diffP'
         )
-    
-    old_benches = bench_util.parse({}, open(old, 'r'))
-    new_benches = bench_util.parse({}, open(new, 'r'))
-    
-    bench_diffs = []
-    for old_bench in old_benches:
-        #filter new_benches for benches that match old_bench
-        new_bench_match = [bench for bench in new_benches
-            if old_bench.bench == bench.bench and
-               old_bench.config == bench.config and
-               old_bench.time_type == bench.time_type
-        ]
-        if (len(new_bench_match) < 1):
-            continue
-        bench_diffs.append(BenchDiff(old_bench, new_bench_match[0]))
-    
+
     bench_diffs.sort(key=lambda d : [d.diffp,
                                      d.old.bench,
                                      d.old.config,
@@ -144,6 +188,6 @@
             , diff=bench_diff.diff
             , diffp=bench_diff.diffp
         )
-    
+
 if __name__ == "__main__":
     main()
diff --git a/bench/bench_expectations.txt b/bench/bench_expectations.txt
new file mode 100644
index 0000000..0bba96f
--- /dev/null
+++ b/bench/bench_expectations.txt
@@ -0,0 +1,2076 @@
+# File for specifying expected Skia benchmark values and ranges.
+# Currently the file is manually created/edited for adapting to bench changes.
+#
+# The format of each data line (non-empty, not started with "#") is:
+# <BenchType_BitmapConfig_TimerType>,<Platform-Alg>,<Expected>,<LB>,<UB>
+# Where Alg is the bench value representation algorithm: min, avg, 25th, etc.
+# LB and UB are lower and upper bounds for the range of the bench value.
+#
+# Note: unrecognized data lines (not exactly 4 commas, unparsable bench values)
+# will likely raise exceptions.
+
+# Microbench expectations.
+# Currently none.
+
+# SkPicture bench expectations. Default is generated by gen_skp_ranges.py on a
+# given revision. Prepend "#" to have alerting ignore the corresponding row.
+# The default was obtained with command:
+# bench/gen_skp_ranges.py --rev-range=6580:6585
+androidpolice.skp_copy_tiles_,Mac_Float_Bench_32-25th,656.000,547.600,731.600
+androidpolice.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,1031.970,867.174,1145.167
+androidpolice.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,232.440,187.574,265.684
+androidpolice.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+androidpolice.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.399,-9.661,10.439
+androidpolice.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.098,-9.916,10.108
+androidpolice.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,2.000,-8.300,12.200
+androidpolice.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,2.377,-7.980,12.614
+androidpolice.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.804,-9.316,10.885
+androidpolice.skp_record_,Mac_Float_Bench_32-25th,22.000,8.700,34.200
+androidpolice.skp_record_,Nexus10_4-1_Float_Bench_32-25th,24.244,10.607,36.668
+androidpolice.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.233,-6.402,14.657
+androidpolice.skp_record_grid_,Mac_Float_Bench_32-25th,25.000,11.250,37.500
+androidpolice.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,26.864,12.834,39.550
+androidpolice.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.255,-5.534,15.780
+androidpolice.skp_record_rtree_,Mac_Float_Bench_32-25th,26.000,12.100,38.600
+androidpolice.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,27.249,13.161,39.974
+androidpolice.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.923,-4.966,16.515
+androidpolice.skp_tile_1024x256_,Mac_Float_Bench_32-25th,630.000,525.500,703.000
+androidpolice.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,1008.890,847.556,1119.779
+androidpolice.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,243.830,197.256,278.213
+androidpolice.skp_tile_1024x64_,Mac_Float_Bench_32-25th,824.000,690.400,916.400
+androidpolice.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,1330.430,1120.866,1473.473
+androidpolice.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,333.880,273.798,377.268
+androidpolice.skp_tile_256x1024_,Mac_Float_Bench_32-25th,658.000,549.300,733.800
+androidpolice.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,970.030,814.525,1077.033
+androidpolice.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,260.460,211.391,296.506
+androidpolice.skp_tile_256x256_,Mac_Float_Bench_32-25th,741.000,619.850,825.100
+androidpolice.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,1152.620,969.727,1277.882
+androidpolice.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,307.510,251.383,348.261
+androidpolice.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,394.000,324.900,443.400
+androidpolice.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,2411.340,2039.639,2662.474
+androidpolice.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,333.680,273.628,377.048
+androidpolice.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,332.530,272.650,375.783
+androidpolice.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,655.000,546.750,730.500
+androidpolice.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,931.980,782.183,1035.178
+androidpolice.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,258.800,209.980,294.680
+androidpolice.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,439.000,363.150,492.900
+androidpolice.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,759.560,635.626,845.516
+androidpolice.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,195.370,156.065,224.907
+androidpolice.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,398.000,328.300,447.800
+androidpolice.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,759.110,635.244,845.021
+androidpolice.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,140.330,109.281,164.363
+androidpolice.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,400.000,330.000,450.000
+androidpolice.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,710.970,594.325,792.067
+androidpolice.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,133.100,103.135,156.410
+androidpolice.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,654.000,545.900,729.400
+androidpolice.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,897.650,753.002,997.415
+androidpolice.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,259.120,210.252,295.032
+androidpolice.skp_tile_512x512_,Mac_Float_Bench_32-25th,620.000,517.000,692.000
+androidpolice.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,962.260,807.921,1068.486
+androidpolice.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,244.730,198.020,279.203
+androidpolice.skp_tile_64x1024_,Mac_Float_Bench_32-25th,936.000,785.600,1039.600
+androidpolice.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,1401.770,1181.505,1551.947
+androidpolice.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,387.160,319.086,435.876
+br.337.skp_copy_tiles_,Mac_Float_Bench_32-25th,38.000,22.300,51.800
+br.337.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,56.810,38.288,72.491
+br.337.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.820,-3.353,18.602
+br.337.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+br.337.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.242,-9.794,10.266
+br.337.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.058,-9.951,10.063
+br.337.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+br.337.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.102,-9.063,11.213
+br.337.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.389,-9.670,10.427
+br.337.skp_record_,Mac_Float_Bench_32-25th,32.000,17.200,45.200
+br.337.skp_record_,Nexus10_4-1_Float_Bench_32-25th,35.067,19.807,48.574
+br.337.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.637,-1.808,20.601
+br.337.skp_record_grid_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+br.337.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,36.050,20.642,49.655
+br.337.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.136,-1.384,21.150
+br.337.skp_record_rtree_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+br.337.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,36.131,20.712,49.744
+br.337.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.299,-1.246,21.329
+br.337.skp_tile_1024x256_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+br.337.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,56.950,38.407,72.645
+br.337.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.840,-1.636,20.824
+br.337.skp_tile_1024x64_,Mac_Float_Bench_32-25th,32.000,17.200,45.200
+br.337.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,81.400,59.190,99.540
+br.337.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.840,4.314,28.524
+br.337.skp_tile_256x1024_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+br.337.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,76.520,55.042,94.172
+br.337.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.540,1.509,24.894
+br.337.skp_tile_256x256_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+br.337.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,85.020,62.267,103.522
+br.337.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.720,2.512,26.192
+br.337.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,65.000,45.250,81.500
+br.337.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,285.300,232.505,323.830
+br.337.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,42.290,25.947,56.519
+br.337.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,41.530,25.300,55.683
+br.337.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+br.337.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,49.400,31.990,64.340
+br.337.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.910,-3.277,18.701
+br.337.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+br.337.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,55.820,37.447,71.402
+br.337.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.250,-2.987,19.075
+br.337.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+br.337.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,45.990,29.092,60.589
+br.337.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.670,-3.481,18.437
+br.337.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+br.337.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,56.210,37.779,71.831
+br.337.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.980,-4.917,16.578
+br.337.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+br.337.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,55.130,36.861,70.643
+br.337.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.560,-2.724,19.416
+br.337.skp_tile_512x512_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+br.337.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,63.160,43.686,79.476
+br.337.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.220,-1.313,21.242
+br.337.skp_tile_64x1024_,Mac_Float_Bench_32-25th,64.000,44.400,80.400
+br.337.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,169.320,133.922,196.252
+br.337.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.880,17.098,45.068
+cnn-michelle-obama-debate.skp_copy_tiles_,Mac_Float_Bench_32-25th,112.000,85.200,133.200
+cnn-michelle-obama-debate.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,127.490,98.366,150.239
+cnn-michelle-obama-debate.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,58.610,39.819,74.471
+cnn-michelle-obama-debate.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+cnn-michelle-obama-debate.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.337,-9.714,10.370
+cnn-michelle-obama-debate.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.063,-9.946,10.070
+cnn-michelle-obama-debate.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+cnn-michelle-obama-debate.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.246,-8.941,11.370
+cnn-michelle-obama-debate.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.399,-9.661,10.439
+cnn-michelle-obama-debate.skp_record_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+cnn-michelle-obama-debate.skp_record_,Nexus10_4-1_Float_Bench_32-25th,11.457,-0.262,22.603
+cnn-michelle-obama-debate.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.659,-7.740,12.925
+cnn-michelle-obama-debate.skp_record_grid_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+cnn-michelle-obama-debate.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,14.239,2.103,25.663
+cnn-michelle-obama-debate.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.227,-7.257,13.550
+cnn-michelle-obama-debate.skp_record_rtree_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+cnn-michelle-obama-debate.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,15.477,3.156,27.025
+cnn-michelle-obama-debate.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.549,-6.983,13.904
+cnn-michelle-obama-debate.skp_tile_1024x256_,Mac_Float_Bench_32-25th,69.000,48.650,85.900
+cnn-michelle-obama-debate.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,141.460,110.241,165.606
+cnn-michelle-obama-debate.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,59.600,40.660,75.560
+cnn-michelle-obama-debate.skp_tile_1024x64_,Mac_Float_Bench_32-25th,128.000,98.800,150.800
+cnn-michelle-obama-debate.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,242.740,196.329,277.014
+cnn-michelle-obama-debate.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,94.340,70.189,113.774
+cnn-michelle-obama-debate.skp_tile_256x1024_,Mac_Float_Bench_32-25th,72.000,51.200,89.200
+cnn-michelle-obama-debate.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,175.180,138.903,202.698
+cnn-michelle-obama-debate.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,60.440,41.374,76.484
+cnn-michelle-obama-debate.skp_tile_256x256_,Mac_Float_Bench_32-25th,108.000,81.800,128.800
+cnn-michelle-obama-debate.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,222.590,179.202,254.849
+cnn-michelle-obama-debate.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,82.100,59.785,100.310
+cnn-michelle-obama-debate.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,305.000,249.250,345.500
+cnn-michelle-obama-debate.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,1483.870,1251.289,1642.257
+cnn-michelle-obama-debate.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,287.640,234.494,326.404
+cnn-michelle-obama-debate.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,286.430,233.465,325.073
+cnn-michelle-obama-debate.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,56.000,37.600,71.600
+cnn-michelle-obama-debate.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,91.350,67.647,110.485
+cnn-michelle-obama-debate.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,53.440,35.424,68.784
+cnn-michelle-obama-debate.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,61.000,41.850,77.100
+cnn-michelle-obama-debate.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,186.320,148.372,214.952
+cnn-michelle-obama-debate.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,55.130,36.861,70.643
+cnn-michelle-obama-debate.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,62.000,42.700,78.200
+cnn-michelle-obama-debate.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,172.930,136.990,200.223
+cnn-michelle-obama-debate.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,50.040,32.534,65.044
+cnn-michelle-obama-debate.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,61.000,41.850,77.100
+cnn-michelle-obama-debate.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,169.990,134.492,196.989
+cnn-michelle-obama-debate.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,55.900,37.515,71.490
+cnn-michelle-obama-debate.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,57.000,38.450,72.700
+cnn-michelle-obama-debate.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,93.740,69.679,113.114
+cnn-michelle-obama-debate.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,54.960,36.716,70.456
+cnn-michelle-obama-debate.skp_tile_512x512_,Mac_Float_Bench_32-25th,68.000,47.800,84.800
+cnn-michelle-obama-debate.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,150.050,117.543,175.055
+cnn-michelle-obama-debate.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,60.190,41.161,76.209
+cnn-michelle-obama-debate.skp_tile_64x1024_,Mac_Float_Bench_32-25th,146.000,114.100,170.600
+cnn-michelle-obama-debate.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,265.480,215.658,302.028
+cnn-michelle-obama-debate.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,101.770,76.504,121.947
+culturalsolutions.co.uk.skp_copy_tiles_,Mac_Float_Bench_32-25th,133.000,103.050,156.300
+culturalsolutions.co.uk.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,188.980,150.633,217.878
+culturalsolutions.co.uk.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,54.820,36.597,70.302
+culturalsolutions.co.uk.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+culturalsolutions.co.uk.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.174,-9.852,10.192
+culturalsolutions.co.uk.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.038,-9.968,10.042
+culturalsolutions.co.uk.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+culturalsolutions.co.uk.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.458,-9.611,10.503
+culturalsolutions.co.uk.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.146,-9.876,10.161
+culturalsolutions.co.uk.skp_record_,Mac_Float_Bench_32-25th,80.000,58.000,98.000
+culturalsolutions.co.uk.skp_record_,Nexus10_4-1_Float_Bench_32-25th,73.226,52.242,90.549
+culturalsolutions.co.uk.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.663,8.413,33.829
+culturalsolutions.co.uk.skp_record_grid_,Mac_Float_Bench_32-25th,80.000,58.000,98.000
+culturalsolutions.co.uk.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,77.016,55.463,94.717
+culturalsolutions.co.uk.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.095,8.781,34.305
+culturalsolutions.co.uk.skp_record_rtree_,Mac_Float_Bench_32-25th,81.000,58.850,99.100
+culturalsolutions.co.uk.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,72.397,51.538,89.637
+culturalsolutions.co.uk.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.067,8.757,34.273
+culturalsolutions.co.uk.skp_tile_1024x256_,Mac_Float_Bench_32-25th,86.000,63.100,104.600
+culturalsolutions.co.uk.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,194.710,155.504,224.181
+culturalsolutions.co.uk.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,50.050,32.542,65.055
+culturalsolutions.co.uk.skp_tile_1024x64_,Mac_Float_Bench_32-25th,88.000,64.800,106.800
+culturalsolutions.co.uk.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,223.590,180.052,255.949
+culturalsolutions.co.uk.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,55.140,36.869,70.654
+culturalsolutions.co.uk.skp_tile_256x1024_,Mac_Float_Bench_32-25th,102.000,76.700,122.200
+culturalsolutions.co.uk.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,227.780,183.613,260.558
+culturalsolutions.co.uk.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,58.560,39.776,74.416
+culturalsolutions.co.uk.skp_tile_256x256_,Mac_Float_Bench_32-25th,97.000,72.450,116.700
+culturalsolutions.co.uk.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,234.420,189.257,267.862
+culturalsolutions.co.uk.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,58.940,40.099,74.834
+culturalsolutions.co.uk.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,81.000,58.850,99.100
+culturalsolutions.co.uk.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,400.630,330.536,450.693
+culturalsolutions.co.uk.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,104.810,79.088,125.291
+culturalsolutions.co.uk.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,103.870,78.290,124.257
+culturalsolutions.co.uk.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,88.000,64.800,106.800
+culturalsolutions.co.uk.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,194.560,155.376,224.016
+culturalsolutions.co.uk.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,53.140,35.169,68.454
+culturalsolutions.co.uk.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,62.000,42.700,78.200
+culturalsolutions.co.uk.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,198.430,158.666,228.273
+culturalsolutions.co.uk.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,43.550,27.017,57.905
+culturalsolutions.co.uk.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,60.000,41.000,76.000
+culturalsolutions.co.uk.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,168.450,133.182,195.295
+culturalsolutions.co.uk.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,40.650,24.552,54.715
+culturalsolutions.co.uk.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,57.000,38.450,72.700
+culturalsolutions.co.uk.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,167.520,132.392,194.272
+culturalsolutions.co.uk.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,39.130,23.261,53.043
+culturalsolutions.co.uk.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,88.000,64.800,106.800
+culturalsolutions.co.uk.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,198.900,159.065,228.790
+culturalsolutions.co.uk.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,54.360,36.206,69.796
+culturalsolutions.co.uk.skp_tile_512x512_,Mac_Float_Bench_32-25th,91.000,67.350,110.100
+culturalsolutions.co.uk.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,205.140,164.369,235.654
+culturalsolutions.co.uk.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,52.120,34.302,67.332
+culturalsolutions.co.uk.skp_tile_64x1024_,Mac_Float_Bench_32-25th,148.000,115.800,172.800
+culturalsolutions.co.uk.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,319.060,261.201,360.966
+culturalsolutions.co.uk.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,93.380,69.373,112.718
+cuteoverload.skp_copy_tiles_,Mac_Float_Bench_32-25th,143.000,111.550,167.300
+cuteoverload.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,140.640,109.544,164.704
+cuteoverload.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,35.880,20.498,49.468
+cuteoverload.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+cuteoverload.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.270,-9.771,10.296
+cuteoverload.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.056,-9.952,10.062
+cuteoverload.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+cuteoverload.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.906,-9.230,10.996
+cuteoverload.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.294,-9.750,10.324
+cuteoverload.skp_record_,Mac_Float_Bench_32-25th,59.000,40.150,74.900
+cuteoverload.skp_record_,Nexus10_4-1_Float_Bench_32-25th,58.400,39.640,74.240
+cuteoverload.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.308,3.862,27.939
+cuteoverload.skp_record_grid_,Mac_Float_Bench_32-25th,61.000,41.850,77.100
+cuteoverload.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,58.403,39.642,74.243
+cuteoverload.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.829,4.305,28.512
+cuteoverload.skp_record_rtree_,Mac_Float_Bench_32-25th,61.000,41.850,77.100
+cuteoverload.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,60.728,41.619,76.801
+cuteoverload.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.775,4.259,28.453
+cuteoverload.skp_tile_1024x256_,Mac_Float_Bench_32-25th,64.000,44.400,80.400
+cuteoverload.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,145.490,113.666,170.039
+cuteoverload.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,48.090,30.877,62.899
+cuteoverload.skp_tile_1024x64_,Mac_Float_Bench_32-25th,129.000,99.650,151.900
+cuteoverload.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,251.300,203.605,286.430
+cuteoverload.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,81.050,58.892,99.155
+cuteoverload.skp_tile_256x1024_,Mac_Float_Bench_32-25th,68.000,47.800,84.800
+cuteoverload.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,188.650,150.352,217.515
+cuteoverload.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,48.420,31.157,63.262
+cuteoverload.skp_tile_256x256_,Mac_Float_Bench_32-25th,104.000,78.400,124.400
+cuteoverload.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,217.220,174.637,248.942
+cuteoverload.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,66.490,46.516,83.139
+cuteoverload.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,206.000,165.100,236.600
+cuteoverload.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,935.160,784.886,1038.676
+cuteoverload.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,191.090,152.427,220.199
+cuteoverload.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,190.340,151.789,219.374
+cuteoverload.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,48.000,30.800,62.800
+cuteoverload.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,83.220,60.737,101.542
+cuteoverload.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,42.160,25.836,56.376
+cuteoverload.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,61.000,41.850,77.100
+cuteoverload.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,187.840,149.664,216.624
+cuteoverload.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,46.020,29.117,60.622
+cuteoverload.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,65.000,45.250,81.500
+cuteoverload.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,216.200,173.770,247.820
+cuteoverload.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,38.720,22.912,52.592
+cuteoverload.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,64.000,44.400,80.400
+cuteoverload.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,206.680,165.678,237.348
+cuteoverload.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,37.140,21.569,50.854
+cuteoverload.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,49.000,31.650,63.900
+cuteoverload.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,87.230,64.145,105.953
+cuteoverload.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,42.580,26.193,56.838
+cuteoverload.skp_tile_512x512_,Mac_Float_Bench_32-25th,63.000,43.550,79.300
+cuteoverload.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,157.990,124.292,183.789
+cuteoverload.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,48.010,30.808,62.811
+cuteoverload.skp_tile_64x1024_,Mac_Float_Bench_32-25th,126.000,97.100,148.600
+cuteoverload.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,256.080,207.668,291.688
+cuteoverload.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,83.140,60.669,101.454
+dashed-spreadsheet.skp_copy_tiles_,Mac_Float_Bench_32-25th,52.000,34.200,67.200
+dashed-spreadsheet.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,150.570,117.984,175.627
+dashed-spreadsheet.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.770,4.254,28.447
+dashed-spreadsheet.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+dashed-spreadsheet.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.028,-9.976,10.031
+dashed-spreadsheet.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+dashed-spreadsheet.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.402,-7.958,12.642
+dashed-spreadsheet.skp_record_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+dashed-spreadsheet.skp_record_,Nexus10_4-1_Float_Bench_32-25th,22.808,9.387,35.089
+dashed-spreadsheet.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.256,-5.532,15.782
+dashed-spreadsheet.skp_record_grid_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+dashed-spreadsheet.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,27.851,13.673,40.636
+dashed-spreadsheet.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.450,-4.518,17.095
+dashed-spreadsheet.skp_record_rtree_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+dashed-spreadsheet.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,34.214,19.082,47.636
+dashed-spreadsheet.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.937,-2.404,19.830
+dashed-spreadsheet.skp_tile_1024x256_,Mac_Float_Bench_32-25th,56.000,37.600,71.600
+dashed-spreadsheet.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,186.950,148.907,215.645
+dashed-spreadsheet.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.680,10.978,37.148
+dashed-spreadsheet.skp_tile_1024x64_,Mac_Float_Bench_32-25th,120.000,92.000,142.000
+dashed-spreadsheet.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,321.380,263.173,363.518
+dashed-spreadsheet.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,53.540,35.509,68.894
+dashed-spreadsheet.skp_tile_256x256_,Mac_Float_Bench_32-25th,94.000,69.900,113.400
+dashed-spreadsheet.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,310.210,253.678,351.231
+dashed-spreadsheet.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,40.010,24.008,54.011
+dashed-spreadsheet.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,416.000,343.600,467.600
+dashed-spreadsheet.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,1899.460,1604.541,2099.406
+dashed-spreadsheet.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,266.710,216.703,303.381
+dashed-spreadsheet.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,264.800,215.080,301.280
+dashed-spreadsheet.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,32.000,17.200,45.200
+dashed-spreadsheet.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,151.670,118.919,176.837
+dashed-spreadsheet.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.100,3.685,27.710
+dashed-spreadsheet.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,49.000,31.650,63.900
+dashed-spreadsheet.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,244.940,198.199,279.434
+dashed-spreadsheet.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.890,9.456,35.179
+dashed-spreadsheet.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,51.000,33.350,66.100
+dashed-spreadsheet.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,252.640,204.744,287.904
+dashed-spreadsheet.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.870,11.140,37.357
+dashed-spreadsheet.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,50.000,32.500,65.000
+dashed-spreadsheet.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,256.850,208.323,292.535
+dashed-spreadsheet.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,19.150,6.277,31.065
+dashed-spreadsheet.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+dashed-spreadsheet.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,152.200,119.370,177.420
+dashed-spreadsheet.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.530,4.051,28.183
+dashed-spreadsheet.skp_tile_512x512_,Mac_Float_Bench_32-25th,50.000,32.500,65.000
+dashed-spreadsheet.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,177.470,140.850,205.217
+dashed-spreadsheet.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.820,9.397,35.102
+digg.skp_copy_tiles_,Mac_Float_Bench_32-25th,87.000,63.950,105.700
+digg.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,135.450,105.132,158.995
+digg.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,20.590,7.502,32.649
+digg.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+digg.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.168,-9.857,10.185
+digg.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.036,-9.969,10.040
+digg.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+digg.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.376,-8.831,11.513
+digg.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.482,-9.590,10.530
+digg.skp_record_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+digg.skp_record_,Nexus10_4-1_Float_Bench_32-25th,7.281,-3.811,18.010
+digg.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.226,-8.108,12.449
+digg.skp_record_grid_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+digg.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,10.440,-1.126,21.484
+digg.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.687,-7.716,12.956
+digg.skp_record_rtree_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+digg.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,11.165,-0.510,22.282
+digg.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.126,-7.343,13.439
+digg.skp_tile_1024x256_,Mac_Float_Bench_32-25th,47.000,29.950,61.700
+digg.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,121.470,93.249,143.617
+digg.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,23.010,9.559,35.311
+digg.skp_tile_1024x64_,Mac_Float_Bench_32-25th,63.000,43.550,79.300
+digg.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,134.110,103.994,157.521
+digg.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,33.290,18.296,46.619
+digg.skp_tile_256x1024_,Mac_Float_Bench_32-25th,54.000,35.900,69.400
+digg.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,145.940,114.049,170.534
+digg.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,25.740,11.879,38.314
+digg.skp_tile_256x256_,Mac_Float_Bench_32-25th,63.000,43.550,79.300
+digg.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,144.420,112.757,168.862
+digg.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,32.780,17.863,46.058
+digg.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,105.000,79.250,125.500
+digg.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,467.870,387.690,524.657
+digg.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,83.660,61.111,102.026
+digg.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,82.930,60.490,101.223
+digg.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,43.000,26.550,57.300
+digg.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,83.130,60.660,101.443
+digg.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.500,8.275,33.650
+digg.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,39.000,23.150,52.900
+digg.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,102.920,77.482,123.212
+digg.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,29.420,15.007,42.362
+digg.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,37.000,21.450,50.700
+digg.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,97.060,72.501,116.766
+digg.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,18.870,6.040,30.757
+digg.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,37.000,21.450,50.700
+digg.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,131.480,101.758,154.628
+digg.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.570,8.334,33.727
+digg.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,44.000,27.400,58.400
+digg.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,92.780,68.863,112.058
+digg.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.190,8.861,34.409
+digg.skp_tile_512x512_,Mac_Float_Bench_32-25th,50.000,32.500,65.000
+digg.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,135.800,105.430,159.380
+digg.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,23.740,10.179,36.114
+digg.skp_tile_64x1024_,Mac_Float_Bench_32-25th,103.000,77.550,123.300
+digg.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,226.040,182.134,258.644
+digg.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,51.380,33.673,66.518
+frantzen-lindeberg.skp_copy_tiles_,Mac_Float_Bench_32-25th,191.000,152.350,220.100
+frantzen-lindeberg.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,228.590,184.302,261.449
+frantzen-lindeberg.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,41.790,25.521,55.969
+frantzen-lindeberg.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+frantzen-lindeberg.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.021,-9.983,10.023
+frantzen-lindeberg.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+frantzen-lindeberg.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.046,-9.961,10.051
+frantzen-lindeberg.skp_record_,Mac_Float_Bench_32-25th,53.000,35.050,68.300
+frantzen-lindeberg.skp_record_,Nexus10_4-1_Float_Bench_32-25th,37.175,21.599,50.892
+frantzen-lindeberg.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.364,-1.190,21.401
+frantzen-lindeberg.skp_record_grid_,Mac_Float_Bench_32-25th,53.000,35.050,68.300
+frantzen-lindeberg.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,47.134,30.064,61.847
+frantzen-lindeberg.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.676,0.775,23.944
+frantzen-lindeberg.skp_record_rtree_,Mac_Float_Bench_32-25th,53.000,35.050,68.300
+frantzen-lindeberg.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,41.331,25.131,55.464
+frantzen-lindeberg.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.524,0.645,23.776
+frantzen-lindeberg.skp_tile_1024x256_,Mac_Float_Bench_32-25th,98.000,73.300,117.800
+frantzen-lindeberg.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,223.970,180.374,256.367
+frantzen-lindeberg.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,39.590,23.651,53.549
+frantzen-lindeberg.skp_tile_1024x64_,Mac_Float_Bench_32-25th,102.000,76.700,122.200
+frantzen-lindeberg.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,220.920,177.782,253.012
+frantzen-lindeberg.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,43.230,26.745,57.553
+frantzen-lindeberg.skp_tile_256x256_,Mac_Float_Bench_32-25th,106.000,80.100,126.600
+frantzen-lindeberg.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,241.830,195.555,276.013
+frantzen-lindeberg.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,44.500,27.825,58.950
+frantzen-lindeberg.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,84.000,61.400,102.400
+frantzen-lindeberg.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,252.970,205.024,288.267
+frantzen-lindeberg.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.080,10.468,36.488
+frantzen-lindeberg.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.970,9.524,35.267
+frantzen-lindeberg.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,102.000,76.700,122.200
+frantzen-lindeberg.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,240.280,194.238,274.308
+frantzen-lindeberg.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,42.890,26.456,57.179
+frantzen-lindeberg.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,72.000,51.200,89.200
+frantzen-lindeberg.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,186.040,148.134,214.644
+frantzen-lindeberg.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,29.940,15.449,42.934
+frantzen-lindeberg.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,60.000,41.000,76.000
+frantzen-lindeberg.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,178.620,141.827,206.482
+frantzen-lindeberg.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.560,9.176,34.816
+frantzen-lindeberg.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,60.000,41.000,76.000
+frantzen-lindeberg.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,186.730,148.720,215.403
+frantzen-lindeberg.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,17.450,4.832,29.195
+frantzen-lindeberg.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,101.000,75.850,121.100
+frantzen-lindeberg.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,220.680,177.578,252.748
+frantzen-lindeberg.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,41.810,25.538,55.991
+frantzen-lindeberg.skp_tile_512x512_,Mac_Float_Bench_32-25th,106.000,80.100,126.600
+frantzen-lindeberg.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,234.290,189.146,267.719
+frantzen-lindeberg.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,42.450,26.083,56.695
+game.deksiam.in.th.skp_copy_tiles_,Mac_Float_Bench_32-25th,57.000,38.450,72.700
+game.deksiam.in.th.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,65.770,45.904,82.347
+game.deksiam.in.th.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.420,2.257,25.862
+game.deksiam.in.th.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+game.deksiam.in.th.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.208,-9.823,10.229
+game.deksiam.in.th.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.034,-9.971,10.038
+game.deksiam.in.th.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+game.deksiam.in.th.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,2.279,-8.063,12.507
+game.deksiam.in.th.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.800,-9.320,10.880
+game.deksiam.in.th.skp_record_,Mac_Float_Bench_32-25th,6.000,-4.900,16.600
+game.deksiam.in.th.skp_record_,Nexus10_4-1_Float_Bench_32-25th,7.624,-3.520,18.386
+game.deksiam.in.th.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.935,-8.355,12.129
+game.deksiam.in.th.skp_record_grid_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+game.deksiam.in.th.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,10.636,-0.960,21.699
+game.deksiam.in.th.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.630,-7.764,12.893
+game.deksiam.in.th.skp_record_rtree_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+game.deksiam.in.th.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,12.203,0.373,23.424
+game.deksiam.in.th.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.326,-7.173,13.659
+game.deksiam.in.th.skp_tile_1024x256_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+game.deksiam.in.th.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,66.110,46.194,82.721
+game.deksiam.in.th.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.500,4.025,28.150
+game.deksiam.in.th.skp_tile_1024x64_,Mac_Float_Bench_32-25th,47.000,29.950,61.700
+game.deksiam.in.th.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,87.710,64.553,106.481
+game.deksiam.in.th.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.880,11.148,37.368
+game.deksiam.in.th.skp_tile_256x1024_,Mac_Float_Bench_32-25th,31.000,16.350,44.100
+game.deksiam.in.th.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,65.570,45.734,82.127
+game.deksiam.in.th.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.340,3.039,26.874
+game.deksiam.in.th.skp_tile_256x256_,Mac_Float_Bench_32-25th,37.000,21.450,50.700
+game.deksiam.in.th.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,66.940,46.899,83.634
+game.deksiam.in.th.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,19.740,6.779,31.714
+game.deksiam.in.th.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,138.000,107.300,161.800
+game.deksiam.in.th.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,977.920,821.232,1085.712
+game.deksiam.in.th.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,115.210,87.928,136.731
+game.deksiam.in.th.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,114.000,86.900,135.400
+game.deksiam.in.th.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+game.deksiam.in.th.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,52.880,34.948,68.168
+game.deksiam.in.th.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.910,3.524,27.501
+game.deksiam.in.th.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+game.deksiam.in.th.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,58.270,39.529,74.097
+game.deksiam.in.th.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.150,7.977,33.265
+game.deksiam.in.th.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,24.000,10.400,36.400
+game.deksiam.in.th.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,50.880,33.248,65.968
+game.deksiam.in.th.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.930,4.390,28.623
+game.deksiam.in.th.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+game.deksiam.in.th.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,60.660,41.561,76.726
+game.deksiam.in.th.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.750,2.537,26.225
+game.deksiam.in.th.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,31.000,16.350,44.100
+game.deksiam.in.th.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,50.470,32.899,65.517
+game.deksiam.in.th.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.700,3.345,27.270
+game.deksiam.in.th.skp_tile_512x512_,Mac_Float_Bench_32-25th,32.000,17.200,45.200
+game.deksiam.in.th.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,65.700,45.845,82.270
+game.deksiam.in.th.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.530,4.051,28.183
+game.deksiam.in.th.skp_tile_64x1024_,Mac_Float_Bench_32-25th,54.000,35.900,69.400
+game.deksiam.in.th.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,100.420,75.357,120.462
+game.deksiam.in.th.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,28.970,14.624,41.867
+gmail-thread.skp_copy_tiles_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+gmail-thread.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,25.820,11.947,38.402
+gmail-thread.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.110,-5.656,15.621
+gmail-thread.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+gmail-thread.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.158,-9.866,10.174
+gmail-thread.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.026,-9.978,10.028
+gmail-thread.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+gmail-thread.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.417,-9.645,10.459
+gmail-thread.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.124,-9.894,10.137
+gmail-thread.skp_record_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+gmail-thread.skp_record_,Nexus10_4-1_Float_Bench_32-25th,2.465,-7.905,12.712
+gmail-thread.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.538,-9.542,10.592
+gmail-thread.skp_record_grid_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+gmail-thread.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,3.257,-7.231,13.583
+gmail-thread.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.693,-9.411,10.762
+gmail-thread.skp_record_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+gmail-thread.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,3.383,-7.125,13.721
+gmail-thread.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.780,-9.337,10.858
+gmail-thread.skp_tile_1024x256_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+gmail-thread.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,21.880,8.598,34.068
+gmail-thread.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.850,-5.028,16.435
+gmail-thread.skp_tile_1024x64_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+gmail-thread.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,30.330,15.780,43.363
+gmail-thread.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.690,-2.614,19.559
+gmail-thread.skp_tile_256x1024_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+gmail-thread.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,27.900,13.715,40.690
+gmail-thread.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.520,-4.458,17.172
+gmail-thread.skp_tile_256x256_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+gmail-thread.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,28.480,14.208,41.328
+gmail-thread.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.550,-3.583,18.305
+gmail-thread.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,48.000,30.800,62.800
+gmail-thread.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,168.320,133.072,195.152
+gmail-thread.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,28.890,14.556,41.779
+gmail-thread.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,28.000,13.800,40.800
+gmail-thread.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+gmail-thread.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,15.930,3.540,27.523
+gmail-thread.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.560,-5.274,16.116
+gmail-thread.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+gmail-thread.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,22.250,8.912,34.475
+gmail-thread.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.970,-2.375,19.867
+gmail-thread.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+gmail-thread.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,24.560,10.876,37.016
+gmail-thread.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.870,-3.311,18.657
+gmail-thread.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+gmail-thread.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,20.110,7.093,32.121
+gmail-thread.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.780,-5.087,16.358
+gmail-thread.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+gmail-thread.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,17.120,4.552,28.832
+gmail-thread.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.580,-5.257,16.138
+gmail-thread.skp_tile_512x512_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+gmail-thread.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,22.060,8.751,34.266
+gmail-thread.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.730,-5.130,16.303
+gmail-thread.skp_tile_64x1024_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+gmail-thread.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,36.660,21.161,50.326
+gmail-thread.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.140,-1.381,21.154
+gmail.skp_copy_tiles_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+gmail.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,36.540,21.059,50.194
+gmail.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.150,-3.922,17.865
+gmail.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+gmail.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.168,-9.858,10.184
+gmail.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.026,-9.978,10.029
+gmail.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+gmail.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.671,-8.580,11.838
+gmail.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.569,-9.516,10.626
+gmail.skp_record_,Mac_Float_Bench_32-25th,2.000,-8.300,12.200
+gmail.skp_record_,Nexus10_4-1_Float_Bench_32-25th,4.681,-6.021,15.149
+gmail.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.040,-9.116,11.144
+gmail.skp_record_grid_,Mac_Float_Bench_32-25th,3.000,-7.450,13.300
+gmail.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,6.575,-4.411,17.233
+gmail.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.494,-8.730,11.644
+gmail.skp_record_rtree_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+gmail.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,8.280,-2.962,19.108
+gmail.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.019,-8.284,12.220
+gmail.skp_tile_1024x256_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+gmail.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,37.440,21.824,51.184
+gmail.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.060,-2.299,19.966
+gmail.skp_tile_1024x64_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+gmail.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,51.870,34.089,67.057
+gmail.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.410,3.098,26.951
+gmail.skp_tile_256x1024_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+gmail.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,43.580,27.043,57.938
+gmail.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.620,-1.823,20.582
+gmail.skp_tile_256x256_,Mac_Float_Bench_32-25th,24.000,10.400,36.400
+gmail.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,53.910,35.823,69.301
+gmail.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.310,1.313,24.641
+gmail.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,68.000,47.800,84.800
+gmail.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,260.850,211.723,296.935
+gmail.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,46.710,29.703,61.381
+gmail.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,45.860,28.981,60.446
+gmail.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+gmail.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,28.020,13.817,40.822
+gmail.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.810,-3.362,18.591
+gmail.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+gmail.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,36.010,20.608,49.611
+gmail.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.670,-3.481,18.437
+gmail.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+gmail.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,28.660,14.361,41.526
+gmail.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.610,-5.231,16.171
+gmail.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+gmail.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,31.270,16.579,44.397
+gmail.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.940,-4.951,16.534
+gmail.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+gmail.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,26.160,12.236,38.776
+gmail.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.100,-3.115,18.910
+gmail.skp_tile_512x512_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+gmail.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,37.140,21.569,50.854
+gmail.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.960,-2.384,19.856
+gmail.skp_tile_64x1024_,Mac_Float_Bench_32-25th,32.000,17.200,45.200
+gmail.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,62.450,43.083,78.695
+gmail.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,17.060,4.501,28.766
+googleblog.blogspot.co.uk.skp_copy_tiles_,Mac_Float_Bench_32-25th,150.000,117.500,175.000
+googleblog.blogspot.co.uk.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,181.360,144.156,209.496
+googleblog.blogspot.co.uk.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,32.820,17.897,46.102
+googleblog.blogspot.co.uk.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+googleblog.blogspot.co.uk.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.298,-9.747,10.328
+googleblog.blogspot.co.uk.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.061,-9.948,10.067
+googleblog.blogspot.co.uk.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+googleblog.blogspot.co.uk.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.480,-8.742,11.628
+googleblog.blogspot.co.uk.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.516,-9.561,10.568
+googleblog.blogspot.co.uk.skp_record_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+googleblog.blogspot.co.uk.skp_record_,Nexus10_4-1_Float_Bench_32-25th,20.461,7.392,32.507
+googleblog.blogspot.co.uk.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.399,-7.111,13.739
+googleblog.blogspot.co.uk.skp_record_grid_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+googleblog.blogspot.co.uk.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,22.654,9.256,34.920
+googleblog.blogspot.co.uk.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.462,-6.207,14.909
+googleblog.blogspot.co.uk.skp_record_rtree_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+googleblog.blogspot.co.uk.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,23.206,9.725,35.526
+googleblog.blogspot.co.uk.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.516,-6.162,14.967
+googleblog.blogspot.co.uk.skp_tile_1024x256_,Mac_Float_Bench_32-25th,75.000,53.750,92.500
+googleblog.blogspot.co.uk.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,160.440,126.374,186.484
+googleblog.blogspot.co.uk.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,37.110,21.543,50.821
+googleblog.blogspot.co.uk.skp_tile_1024x64_,Mac_Float_Bench_32-25th,109.000,82.650,129.900
+googleblog.blogspot.co.uk.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,203.380,162.873,233.718
+googleblog.blogspot.co.uk.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,53.350,35.347,68.685
+googleblog.blogspot.co.uk.skp_tile_256x1024_,Mac_Float_Bench_32-25th,84.000,61.400,102.400
+googleblog.blogspot.co.uk.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,192.470,153.600,221.717
+googleblog.blogspot.co.uk.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,40.840,24.714,54.924
+googleblog.blogspot.co.uk.skp_tile_256x256_,Mac_Float_Bench_32-25th,108.000,81.800,128.800
+googleblog.blogspot.co.uk.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,194.690,155.487,224.159
+googleblog.blogspot.co.uk.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,54.280,36.138,69.708
+googleblog.blogspot.co.uk.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,214.000,171.900,245.400
+googleblog.blogspot.co.uk.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,914.710,767.504,1016.181
+googleblog.blogspot.co.uk.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,157.520,123.892,183.272
+googleblog.blogspot.co.uk.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,156.980,123.433,182.678
+googleblog.blogspot.co.uk.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,75.000,53.750,92.500
+googleblog.blogspot.co.uk.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,118.810,90.989,140.691
+googleblog.blogspot.co.uk.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,38.200,22.470,52.020
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,57.000,38.450,72.700
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,150.040,117.534,175.044
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,33.440,18.424,46.784
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,57.000,38.450,72.700
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,167.970,132.774,194.767
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.610,10.918,37.071
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,58.000,39.300,73.800
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,153.620,120.577,178.982
+googleblog.blogspot.co.uk.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.690,12.686,39.359
+googleblog.blogspot.co.uk.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,75.000,53.750,92.500
+googleblog.blogspot.co.uk.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,120.990,92.841,143.089
+googleblog.blogspot.co.uk.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,38.700,22.895,52.570
+googleblog.blogspot.co.uk.skp_tile_512x512_,Mac_Float_Bench_32-25th,81.000,58.850,99.100
+googleblog.blogspot.co.uk.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,177.970,141.274,205.767
+googleblog.blogspot.co.uk.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,39.660,23.711,53.626
+googleblog.blogspot.co.uk.skp_tile_64x1024_,Mac_Float_Bench_32-25th,143.000,111.550,167.300
+googleblog.blogspot.co.uk.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,260.040,211.034,296.044
+googleblog.blogspot.co.uk.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,70.740,50.129,87.814
+gsp.ro.skp_copy_tiles_,Mac_Float_Bench_32-25th,261.000,211.850,297.100
+gsp.ro.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,369.630,304.185,416.593
+gsp.ro.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,164.970,130.225,191.467
+gsp.ro.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+gsp.ro.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,1.259,-8.930,11.385
+gsp.ro.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.252,-9.786,10.277
+gsp.ro.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,2.000,-8.300,12.200
+gsp.ro.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,3.995,-6.604,14.395
+gsp.ro.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.205,-8.976,11.325
+gsp.ro.skp_record_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+gsp.ro.skp_record_,Nexus10_4-1_Float_Bench_32-25th,36.040,20.634,49.644
+gsp.ro.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.760,-4.254,17.436
+gsp.ro.skp_record_grid_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+gsp.ro.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,47.731,30.571,62.504
+gsp.ro.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.594,-2.695,19.454
+gsp.ro.skp_record_rtree_,Mac_Float_Bench_32-25th,35.000,19.750,48.500
+gsp.ro.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,49.328,31.929,64.260
+gsp.ro.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.569,-1.866,20.526
+gsp.ro.skp_tile_1024x256_,Mac_Float_Bench_32-25th,160.000,126.000,186.000
+gsp.ro.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,383.550,316.017,431.905
+gsp.ro.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,144.710,113.004,169.181
+gsp.ro.skp_tile_1024x64_,Mac_Float_Bench_32-25th,236.000,190.600,269.600
+gsp.ro.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,519.210,431.329,581.131
+gsp.ro.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,195.950,156.557,225.545
+gsp.ro.skp_tile_256x1024_,Mac_Float_Bench_32-25th,211.000,169.350,242.100
+gsp.ro.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,508.760,422.446,569.636
+gsp.ro.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,158.950,125.107,184.845
+gsp.ro.skp_tile_256x256_,Mac_Float_Bench_32-25th,210.000,168.500,241.000
+gsp.ro.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,502.390,417.031,562.629
+gsp.ro.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,181.640,144.394,209.804
+gsp.ro.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,344.000,282.400,388.400
+gsp.ro.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,1592.890,1343.957,1762.179
+gsp.ro.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,445.790,368.921,500.369
+gsp.ro.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,443.950,367.357,498.345
+gsp.ro.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,131.000,101.350,154.100
+gsp.ro.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,253.080,205.118,288.388
+gsp.ro.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,134.720,104.512,158.192
+gsp.ro.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,140.000,109.000,164.000
+gsp.ro.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,442.790,366.372,497.069
+gsp.ro.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,220.910,177.773,253.001
+gsp.ro.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,134.000,103.900,157.400
+gsp.ro.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,458.090,379.376,513.899
+gsp.ro.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,203.970,163.374,234.367
+gsp.ro.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,134.000,103.900,157.400
+gsp.ro.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,479.920,397.932,537.912
+gsp.ro.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,212.340,170.489,243.574
+gsp.ro.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,131.000,101.350,154.100
+gsp.ro.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,254.910,206.673,290.401
+gsp.ro.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,135.930,105.541,159.523
+gsp.ro.skp_tile_512x512_,Mac_Float_Bench_32-25th,162.000,127.700,188.200
+gsp.ro.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,381.780,314.513,429.958
+gsp.ro.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,146.280,114.338,170.908
+gsp.ro.skp_tile_64x1024_,Mac_Float_Bench_32-25th,343.000,281.550,387.300
+gsp.ro.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,792.570,663.685,881.827
+gsp.ro.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,236.100,190.685,269.710
+hs.fi.skp_copy_tiles_,Mac_Float_Bench_32-25th,128.000,98.800,150.800
+hs.fi.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,141.280,110.088,165.408
+hs.fi.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,62.480,43.108,78.728
+hs.fi.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+hs.fi.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.248,-9.789,10.272
+hs.fi.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.084,-9.929,10.092
+hs.fi.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+hs.fi.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.343,-8.859,11.477
+hs.fi.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.507,-9.569,10.557
+hs.fi.skp_record_,Mac_Float_Bench_32-25th,22.000,8.700,34.200
+hs.fi.skp_record_,Nexus10_4-1_Float_Bench_32-25th,21.437,8.221,33.580
+hs.fi.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.220,-5.563,15.742
+hs.fi.skp_record_grid_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+hs.fi.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,24.913,11.176,37.404
+hs.fi.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.842,-5.034,16.426
+hs.fi.skp_record_rtree_,Mac_Float_Bench_32-25th,24.000,10.400,36.400
+hs.fi.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,25.107,11.341,37.618
+hs.fi.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.079,-4.833,16.687
+hs.fi.skp_tile_1024x256_,Mac_Float_Bench_32-25th,58.000,39.300,73.800
+hs.fi.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,138.460,107.691,162.306
+hs.fi.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,57.660,39.011,73.426
+hs.fi.skp_tile_1024x64_,Mac_Float_Bench_32-25th,85.000,62.250,103.500
+hs.fi.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,166.570,131.584,193.227
+hs.fi.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,73.980,52.883,91.378
+hs.fi.skp_tile_256x1024_,Mac_Float_Bench_32-25th,69.000,48.650,85.900
+hs.fi.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,162.190,127.862,188.409
+hs.fi.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,64.200,44.570,80.620
+hs.fi.skp_tile_256x256_,Mac_Float_Bench_32-25th,79.000,57.150,96.900
+hs.fi.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,165.390,130.581,191.929
+hs.fi.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,70.730,50.120,87.803
+hs.fi.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,131.000,101.350,154.100
+hs.fi.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,564.110,469.493,630.521
+hs.fi.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,151.530,118.800,176.683
+hs.fi.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,150.070,117.559,175.077
+hs.fi.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,52.000,34.200,67.200
+hs.fi.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,91.760,67.996,110.936
+hs.fi.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,53.930,35.840,69.323
+hs.fi.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,46.000,29.100,60.600
+hs.fi.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,122.160,93.836,144.376
+hs.fi.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,58.450,39.683,74.295
+hs.fi.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,47.000,29.950,61.700
+hs.fi.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,146.320,114.372,170.952
+hs.fi.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,55.240,36.954,70.764
+hs.fi.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,46.000,29.100,60.600
+hs.fi.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,134.350,104.197,157.785
+hs.fi.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,60.630,41.535,76.693
+hs.fi.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,53.000,35.050,68.300
+hs.fi.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,92.710,68.803,111.981
+hs.fi.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,54.350,36.197,69.785
+hs.fi.skp_tile_512x512_,Mac_Float_Bench_32-25th,60.000,41.000,76.000
+hs.fi.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,142.360,111.006,166.596
+hs.fi.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,59.380,40.473,75.318
+hs.fi.skp_tile_64x1024_,Mac_Float_Bench_32-25th,125.000,96.250,147.500
+hs.fi.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,250.700,203.095,285.770
+hs.fi.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,98.130,73.410,117.943
+iphone.capitolvolkswagen.skp_copy_tiles_,Mac_Float_Bench_32-25th,100.000,75.000,120.000
+iphone.capitolvolkswagen.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,81.290,59.097,99.419
+iphone.capitolvolkswagen.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.140,2.019,25.554
+iphone.capitolvolkswagen.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+iphone.capitolvolkswagen.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.115,-9.903,10.126
+iphone.capitolvolkswagen.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.019,-9.984,10.021
+iphone.capitolvolkswagen.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+iphone.capitolvolkswagen.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.244,-9.793,10.268
+iphone.capitolvolkswagen.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.066,-9.944,10.073
+iphone.capitolvolkswagen.skp_record_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+iphone.capitolvolkswagen.skp_record_,Nexus10_4-1_Float_Bench_32-25th,2.530,-7.850,12.783
+iphone.capitolvolkswagen.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.536,-9.545,10.589
+iphone.capitolvolkswagen.skp_record_grid_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+iphone.capitolvolkswagen.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,3.764,-6.800,14.141
+iphone.capitolvolkswagen.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.780,-9.337,10.858
+iphone.capitolvolkswagen.skp_record_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+iphone.capitolvolkswagen.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,3.520,-7.008,13.872
+iphone.capitolvolkswagen.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.807,-9.314,10.887
+iphone.capitolvolkswagen.skp_tile_1024x256_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+iphone.capitolvolkswagen.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,51.060,33.401,66.166
+iphone.capitolvolkswagen.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.770,2.554,26.247
+iphone.capitolvolkswagen.skp_tile_1024x64_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+iphone.capitolvolkswagen.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,60.320,41.272,76.352
+iphone.capitolvolkswagen.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,17.140,4.569,28.854
+iphone.capitolvolkswagen.skp_tile_256x1024_,Mac_Float_Bench_32-25th,60.000,41.000,76.000
+iphone.capitolvolkswagen.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,108.620,82.327,129.482
+iphone.capitolvolkswagen.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,30.400,15.840,43.440
+iphone.capitolvolkswagen.skp_tile_256x256_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+iphone.capitolvolkswagen.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,64.180,44.553,80.598
+iphone.capitolvolkswagen.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,17.040,4.484,28.744
+iphone.capitolvolkswagen.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,49.000,31.650,63.900
+iphone.capitolvolkswagen.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,183.200,145.720,211.520
+iphone.capitolvolkswagen.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.830,12.805,39.513
+iphone.capitolvolkswagen.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.160,12.236,38.776
+iphone.capitolvolkswagen.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+iphone.capitolvolkswagen.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,42.600,26.210,56.860
+iphone.capitolvolkswagen.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.630,1.585,24.993
+iphone.capitolvolkswagen.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+iphone.capitolvolkswagen.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,42.790,26.371,57.069
+iphone.capitolvolkswagen.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.300,-1.245,21.330
+iphone.capitolvolkswagen.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+iphone.capitolvolkswagen.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,38.150,22.427,51.965
+iphone.capitolvolkswagen.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.690,-1.764,20.659
+iphone.capitolvolkswagen.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+iphone.capitolvolkswagen.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,36.380,20.923,50.018
+iphone.capitolvolkswagen.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.180,-2.197,20.098
+iphone.capitolvolkswagen.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+iphone.capitolvolkswagen.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,40.350,24.297,54.385
+iphone.capitolvolkswagen.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.570,3.235,27.127
+iphone.capitolvolkswagen.skp_tile_512x512_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+iphone.capitolvolkswagen.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,57.210,38.629,72.931
+iphone.capitolvolkswagen.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.460,3.141,27.006
+iphone.capitolvolkswagen.skp_tile_64x1024_,Mac_Float_Bench_32-25th,89.000,65.650,107.900
+iphone.capitolvolkswagen.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,145.970,114.075,170.567
+iphone.capitolvolkswagen.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,45.280,28.488,59.808
+milwaukeepolicenews.skp_copy_tiles_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+milwaukeepolicenews.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,25.340,11.539,37.874
+milwaukeepolicenews.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.690,-6.013,15.159
+milwaukeepolicenews.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+milwaukeepolicenews.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.125,-9.893,10.138
+milwaukeepolicenews.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.019,-9.984,10.021
+milwaukeepolicenews.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+milwaukeepolicenews.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.216,-9.816,10.238
+milwaukeepolicenews.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.050,-9.957,10.055
+milwaukeepolicenews.skp_record_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+milwaukeepolicenews.skp_record_,Nexus10_4-1_Float_Bench_32-25th,4.044,-6.563,14.448
+milwaukeepolicenews.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.964,-9.181,11.060
+milwaukeepolicenews.skp_record_grid_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+milwaukeepolicenews.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,4.360,-6.294,14.796
+milwaukeepolicenews.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.034,-9.121,11.137
+milwaukeepolicenews.skp_record_rtree_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+milwaukeepolicenews.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,4.493,-6.181,14.943
+milwaukeepolicenews.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.042,-9.114,11.147
+milwaukeepolicenews.skp_tile_1024x256_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+milwaukeepolicenews.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,23.100,9.635,35.410
+milwaukeepolicenews.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.760,-5.954,15.236
+milwaukeepolicenews.skp_tile_1024x64_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+milwaukeepolicenews.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,22.800,9.380,35.080
+milwaukeepolicenews.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.820,-5.053,16.402
+milwaukeepolicenews.skp_tile_256x1024_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+milwaukeepolicenews.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,24.540,10.859,36.994
+milwaukeepolicenews.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.190,-5.588,15.709
+milwaukeepolicenews.skp_tile_256x256_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+milwaukeepolicenews.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,25.070,11.309,37.577
+milwaukeepolicenews.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.600,-5.240,16.160
+milwaukeepolicenews.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+milwaukeepolicenews.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,42.920,26.482,57.212
+milwaukeepolicenews.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.710,-4.296,17.381
+milwaukeepolicenews.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.170,-4.756,16.787
+milwaukeepolicenews.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+milwaukeepolicenews.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,18.220,5.487,30.042
+milwaukeepolicenews.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.990,-5.758,15.489
+milwaukeepolicenews.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+milwaukeepolicenews.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,15.360,3.056,26.896
+milwaukeepolicenews.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.540,-6.991,13.894
+milwaukeepolicenews.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+milwaukeepolicenews.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,14.780,2.563,26.258
+milwaukeepolicenews.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.470,-7.050,13.817
+milwaukeepolicenews.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+milwaukeepolicenews.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,16.070,3.659,27.677
+milwaukeepolicenews.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.070,-7.391,13.377
+milwaukeepolicenews.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+milwaukeepolicenews.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,20.150,7.127,32.165
+milwaukeepolicenews.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.800,-5.920,15.280
+milwaukeepolicenews.skp_tile_512x512_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+milwaukeepolicenews.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,22.620,9.227,34.882
+milwaukeepolicenews.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.770,-5.946,15.247
+milwaukeepolicenews.skp_tile_64x1024_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+milwaukeepolicenews.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,30.140,15.619,43.154
+milwaukeepolicenews.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.730,-3.429,18.503
+mlb.skp_copy_tiles_,Mac_Float_Bench_32-25th,89.000,65.650,107.900
+mlb.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,101.780,76.513,121.958
+mlb.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.950,8.657,34.145
+mlb.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+mlb.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.230,-9.804,10.253
+mlb.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.047,-9.960,10.052
+mlb.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+mlb.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.659,-9.439,10.725
+mlb.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.208,-9.823,10.228
+mlb.skp_record_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+mlb.skp_record_,Nexus10_4-1_Float_Bench_32-25th,14.661,2.462,26.127
+mlb.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.964,-8.331,12.160
+mlb.skp_record_grid_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+mlb.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,16.058,3.649,27.663
+mlb.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.194,-8.135,12.413
+mlb.skp_record_rtree_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+mlb.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,15.757,3.393,27.332
+mlb.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.291,-6.352,14.721
+mlb.skp_tile_1024x256_,Mac_Float_Bench_32-25th,61.000,41.850,77.100
+mlb.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,100.950,75.808,121.045
+mlb.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.980,12.933,39.678
+mlb.skp_tile_1024x64_,Mac_Float_Bench_32-25th,127.000,97.950,149.700
+mlb.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,186.620,148.627,215.282
+mlb.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,49.220,31.837,64.142
+mlb.skp_tile_256x1024_,Mac_Float_Bench_32-25th,286.000,233.100,324.600
+mlb.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,425.600,351.760,478.160
+mlb.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,104.140,78.519,124.554
+mlb.skp_tile_256x256_,Mac_Float_Bench_32-25th,93.000,69.050,112.300
+mlb.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,155.680,122.328,181.248
+mlb.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,39.150,23.277,53.065
+mlb.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,83.000,60.550,101.300
+mlb.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,545.480,453.658,610.028
+mlb.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,70.780,50.163,87.858
+mlb.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,69.820,49.347,86.802
+mlb.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,87.000,63.950,105.700
+mlb.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,132.870,102.939,156.157
+mlb.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,36.020,20.617,49.622
+mlb.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,59.000,40.150,74.900
+mlb.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,137.720,107.062,161.492
+mlb.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,32.510,17.633,45.761
+mlb.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,53.000,35.050,68.300
+mlb.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,118.250,90.513,140.075
+mlb.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.390,10.732,36.829
+mlb.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,51.000,33.350,66.100
+mlb.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,96.950,72.407,116.645
+mlb.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,23.020,9.567,35.322
+mlb.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,88.000,64.800,106.800
+mlb.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,132.580,102.693,155.838
+mlb.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,35.770,20.405,49.347
+mlb.skp_tile_512x512_,Mac_Float_Bench_32-25th,53.000,35.050,68.300
+mlb.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,106.090,80.177,126.699
+mlb.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,25.440,11.624,37.984
+mlb.skp_tile_64x1024_,Mac_Float_Bench_32-25th,511.000,424.350,572.100
+mlb.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,740.530,619.450,824.583
+mlb.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,172.720,136.812,199.992
+nofolo.skp_copy_tiles_,Mac_Float_Bench_32-25th,45.000,28.250,59.500
+nofolo.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,45.460,28.641,60.006
+nofolo.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.310,-2.087,20.241
+nofolo.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+nofolo.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.038,-9.968,10.042
+nofolo.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+nofolo.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.065,-9.945,10.072
+nofolo.skp_record_,Mac_Float_Bench_32-25th,22.000,8.700,34.200
+nofolo.skp_record_,Nexus10_4-1_Float_Bench_32-25th,15.988,3.590,27.587
+nofolo.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.196,-7.284,13.515
+nofolo.skp_record_grid_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+nofolo.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,16.928,4.389,28.621
+nofolo.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.331,-7.169,13.664
+nofolo.skp_record_rtree_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+nofolo.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,16.352,3.899,27.987
+nofolo.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.480,-6.192,14.928
+nofolo.skp_tile_1024x256_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+nofolo.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,40.550,24.467,54.605
+nofolo.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.780,-1.687,20.758
+nofolo.skp_tile_1024x64_,Mac_Float_Bench_32-25th,25.000,11.250,37.500
+nofolo.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,47.100,30.035,61.810
+nofolo.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.290,1.296,24.619
+nofolo.skp_tile_256x256_,Mac_Float_Bench_32-25th,25.000,11.250,37.500
+nofolo.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,47.490,30.367,62.239
+nofolo.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.880,0.948,24.168
+nofolo.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+nofolo.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,113.070,86.109,134.377
+nofolo.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,17.350,4.748,29.085
+nofolo.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.400,3.940,28.040
+nofolo.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+nofolo.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,36.290,20.846,49.919
+nofolo.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.690,-0.914,21.759
+nofolo.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+nofolo.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,37.220,21.637,50.942
+nofolo.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.960,-3.234,18.756
+nofolo.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+nofolo.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,35.720,20.362,49.292
+nofolo.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.640,-5.206,16.204
+nofolo.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+nofolo.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,34.690,19.486,48.159
+nofolo.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.280,-4.662,16.908
+nofolo.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+nofolo.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,38.590,22.802,52.449
+nofolo.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.200,-1.330,21.220
+nofolo.skp_tile_512x512_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+nofolo.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,45.800,28.930,60.380
+nofolo.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.430,-1.135,21.473
+nytimes.skp_copy_tiles_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+nytimes.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,38.920,23.082,52.812
+nytimes.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,19.870,6.890,31.857
+nytimes.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+nytimes.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.162,-9.862,10.178
+nytimes.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.023,-9.980,10.026
+nytimes.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+nytimes.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.840,-9.286,10.924
+nytimes.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.275,-9.766,10.303
+nytimes.skp_record_,Mac_Float_Bench_32-25th,2.000,-8.300,12.200
+nytimes.skp_record_,Nexus10_4-1_Float_Bench_32-25th,3.360,-7.144,13.696
+nytimes.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.705,-9.401,10.775
+nytimes.skp_record_grid_,Mac_Float_Bench_32-25th,3.000,-7.450,13.300
+nytimes.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,5.184,-5.594,15.702
+nytimes.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.064,-9.096,11.170
+nytimes.skp_record_rtree_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+nytimes.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,5.625,-5.219,16.187
+nytimes.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.270,-8.920,11.397
+nytimes.skp_tile_1024x256_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+nytimes.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,34.460,19.291,47.906
+nytimes.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.690,4.187,28.359
+nytimes.skp_tile_1024x64_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+nytimes.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,41.320,25.122,55.452
+nytimes.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,19.830,6.855,31.813
+nytimes.skp_tile_256x1024_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+nytimes.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,48.930,31.590,63.823
+nytimes.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.850,4.323,28.535
+nytimes.skp_tile_256x256_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+nytimes.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,41.470,25.249,55.617
+nytimes.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,20.150,7.127,32.165
+nytimes.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,47.000,29.950,61.700
+nytimes.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,166.020,131.117,192.622
+nytimes.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,58.080,39.368,73.888
+nytimes.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,56.930,38.390,72.623
+nytimes.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+nytimes.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,24.370,10.715,36.807
+nytimes.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.750,3.387,27.325
+nytimes.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+nytimes.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,37.520,21.892,51.272
+nytimes.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.720,16.962,44.892
+nytimes.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+nytimes.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,31.550,16.817,44.705
+nytimes.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,33.730,18.670,47.103
+nytimes.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+nytimes.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,28.790,14.471,41.669
+nytimes.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,34.270,19.130,47.697
+nytimes.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+nytimes.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,23.360,9.856,35.696
+nytimes.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.050,3.643,27.655
+nytimes.skp_tile_512x512_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+nytimes.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,36.190,20.761,49.809
+nytimes.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.520,3.192,27.072
+nytimes.skp_tile_64x1024_,Mac_Float_Bench_32-25th,38.000,22.300,51.800
+nytimes.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,79.140,57.269,97.054
+nytimes.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.640,12.644,39.304
+planet.mozilla.skp_copy_tiles_,Mac_Float_Bench_32-25th,844.000,707.400,938.400
+planet.mozilla.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,1004.040,843.434,1114.444
+planet.mozilla.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,182.340,144.989,210.574
+planet.mozilla.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,2.000,-8.300,12.200
+planet.mozilla.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,2.026,-8.278,12.229
+planet.mozilla.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.474,-9.597,10.521
+planet.mozilla.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+planet.mozilla.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,10.223,-1.310,21.246
+planet.mozilla.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.577,-6.960,13.934
+planet.mozilla.skp_record_,Mac_Float_Bench_32-25th,67.000,46.950,83.700
+planet.mozilla.skp_record_,Nexus10_4-1_Float_Bench_32-25th,73.897,52.812,91.287
+planet.mozilla.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,19.070,6.210,30.977
+planet.mozilla.skp_record_grid_,Mac_Float_Bench_32-25th,75.000,53.750,92.500
+planet.mozilla.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,85.647,62.800,104.212
+planet.mozilla.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.336,8.136,33.470
+planet.mozilla.skp_record_rtree_,Mac_Float_Bench_32-25th,80.000,58.000,98.000
+planet.mozilla.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,95.906,71.520,115.497
+planet.mozilla.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,25.167,11.392,37.684
+planet.mozilla.skp_tile_1024x256_,Mac_Float_Bench_32-25th,572.000,476.200,639.200
+planet.mozilla.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,1498.400,1263.640,1658.240
+planet.mozilla.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,298.450,243.682,338.295
+planet.mozilla.skp_tile_1024x64_,Mac_Float_Bench_32-25th,1293.000,1089.050,1432.300
+planet.mozilla.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,3302.270,2796.929,3642.497
+planet.mozilla.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,776.010,649.608,863.611
+planet.mozilla.skp_tile_256x1024_,Mac_Float_Bench_32-25th,611.000,509.350,682.100
+planet.mozilla.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,1855.320,1567.022,2050.852
+planet.mozilla.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,324.310,265.663,366.741
+planet.mozilla.skp_tile_256x256_,Mac_Float_Bench_32-25th,1229.000,1034.650,1361.900
+planet.mozilla.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,3318.050,2810.343,3659.855
+planet.mozilla.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,730.250,610.712,813.275
+planet.mozilla.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,1405.000,1184.250,1555.500
+planet.mozilla.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,6054.900,5136.665,6670.390
+planet.mozilla.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,927.820,778.647,1030.602
+planet.mozilla.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,924.200,775.570,1026.620
+planet.mozilla.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,292.000,238.200,331.200
+planet.mozilla.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,395.940,326.549,445.534
+planet.mozilla.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,146.690,114.686,171.359
+planet.mozilla.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,674.000,562.900,751.400
+planet.mozilla.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,2163.670,1829.120,2390.037
+planet.mozilla.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,410.270,338.729,461.297
+planet.mozilla.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,670.000,559.500,747.000
+planet.mozilla.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,2243.750,1897.188,2478.125
+planet.mozilla.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,283.920,231.332,322.312
+planet.mozilla.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,671.000,560.350,748.100
+planet.mozilla.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,2352.270,1989.429,2597.497
+planet.mozilla.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,298.310,243.564,338.141
+planet.mozilla.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,294.000,239.900,333.400
+planet.mozilla.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,402.420,332.057,452.662
+planet.mozilla.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,148.560,116.276,173.416
+planet.mozilla.skp_tile_512x512_,Mac_Float_Bench_32-25th,600.000,500.000,670.000
+planet.mozilla.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,1675.880,1414.498,1853.468
+planet.mozilla.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,318.030,260.325,359.833
+planet.mozilla.skp_tile_64x1024_,Mac_Float_Bench_32-25th,1561.000,1316.850,1727.100
+planet.mozilla.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,3974.390,3368.231,4381.829
+planet.mozilla.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,889.910,746.423,988.901
+pokemon-table.skp_copy_tiles_,Mac_Float_Bench_32-25th,289.000,235.650,327.900
+pokemon-table.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,391.090,322.426,440.199
+pokemon-table.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,84.960,62.216,103.456
+pokemon-table.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+pokemon-table.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.638,-9.458,10.702
+pokemon-table.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.086,-9.927,10.095
+pokemon-table.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+pokemon-table.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,36.055,20.647,49.661
+pokemon-table.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.831,0.906,24.114
+pokemon-table.skp_record_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+pokemon-table.skp_record_,Nexus10_4-1_Float_Bench_32-25th,32.023,17.219,45.225
+pokemon-table.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.178,-3.899,17.896
+pokemon-table.skp_record_grid_,Mac_Float_Bench_32-25th,31.000,16.350,44.100
+pokemon-table.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,53.682,35.630,69.050
+pokemon-table.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.932,1.842,25.325
+pokemon-table.skp_record_rtree_,Mac_Float_Bench_32-25th,59.000,40.150,74.900
+pokemon-table.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,84.947,62.205,103.442
+pokemon-table.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,25.865,11.986,38.452
+pokemon-table.skp_tile_1024x256_,Mac_Float_Bench_32-25th,274.000,222.900,311.400
+pokemon-table.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,675.570,564.235,753.127
+pokemon-table.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,153.120,120.152,178.432
+pokemon-table.skp_tile_1024x64_,Mac_Float_Bench_32-25th,828.000,693.800,920.800
+pokemon-table.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,2007.490,1696.366,2218.239
+pokemon-table.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,448.280,371.038,503.108
+pokemon-table.skp_tile_256x1024_,Mac_Float_Bench_32-25th,249.000,201.650,283.900
+pokemon-table.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,603.740,503.179,674.114
+pokemon-table.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,142.680,111.278,166.948
+pokemon-table.skp_tile_256x256_,Mac_Float_Bench_32-25th,583.000,485.550,651.300
+pokemon-table.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,1383.760,1166.196,1532.136
+pokemon-table.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,315.300,258.005,356.830
+pokemon-table.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,1013.000,851.050,1124.300
+pokemon-table.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,3270.490,2769.916,3607.539
+pokemon-table.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,532.020,442.217,595.222
+pokemon-table.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,529.410,439.998,592.351
+pokemon-table.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,95.000,70.750,114.500
+pokemon-table.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,184.380,146.723,212.818
+pokemon-table.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,61.260,42.071,77.386
+pokemon-table.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,314.000,256.900,355.400
+pokemon-table.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,782.560,655.176,870.816
+pokemon-table.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,175.170,138.894,202.687
+pokemon-table.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,316.000,258.600,357.600
+pokemon-table.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,781.240,654.054,869.364
+pokemon-table.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,134.990,104.742,158.489
+pokemon-table.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,314.000,256.900,355.400
+pokemon-table.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,767.970,642.774,854.767
+pokemon-table.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,124.200,95.570,146.620
+pokemon-table.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,99.000,74.150,118.900
+pokemon-table.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,190.190,151.661,219.209
+pokemon-table.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,63.580,44.043,79.938
+pokemon-table.skp_tile_512x512_,Mac_Float_Bench_32-25th,242.000,195.700,276.200
+pokemon-table.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,606.330,505.380,676.963
+pokemon-table.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,138.160,107.436,161.976
+pokemon-table.skp_tile_64x1024_,Mac_Float_Bench_32-25th,720.000,602.000,802.000
+pokemon-table.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,1649.970,1392.475,1824.967
+pokemon-table.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,372.520,306.642,419.772
+pravda.skp_copy_tiles_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+pravda.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,35.450,20.133,48.995
+pravda.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.010,-4.892,16.611
+pravda.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+pravda.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.136,-9.884,10.149
+pravda.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.021,-9.982,10.024
+pravda.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+pravda.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.708,-9.398,10.779
+pravda.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.233,-9.802,10.257
+pravda.skp_record_,Mac_Float_Bench_32-25th,3.000,-7.450,13.300
+pravda.skp_record_,Nexus10_4-1_Float_Bench_32-25th,3.732,-6.828,14.105
+pravda.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.884,-9.249,10.972
+pravda.skp_record_grid_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+pravda.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,5.216,-5.567,15.737
+pravda.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.192,-8.987,11.311
+pravda.skp_record_rtree_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+pravda.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,5.399,-5.411,15.939
+pravda.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.333,-8.867,11.467
+pravda.skp_tile_1024x256_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+pravda.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,30.170,15.645,43.187
+pravda.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.570,-4.415,17.227
+pravda.skp_tile_1024x64_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+pravda.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,33.710,18.654,47.081
+pravda.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.900,-2.435,19.790
+pravda.skp_tile_256x1024_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+pravda.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,38.600,22.810,52.460
+pravda.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.990,-3.208,18.789
+pravda.skp_tile_256x256_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+pravda.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,34.940,19.699,48.434
+pravda.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.380,-2.027,20.318
+pravda.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,43.000,26.550,57.300
+pravda.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,105.920,80.032,126.512
+pravda.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,35.650,20.302,49.215
+pravda.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,34.760,19.546,48.236
+pravda.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+pravda.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,23.200,9.720,35.520
+pravda.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.640,-4.356,17.304
+pravda.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+pravda.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,24.390,10.732,36.829
+pravda.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.830,1.755,25.213
+pravda.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+pravda.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,23.920,10.332,36.312
+pravda.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.090,-2.274,19.999
+pravda.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+pravda.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,26.750,12.738,39.425
+pravda.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.110,1.143,24.421
+pravda.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+pravda.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,24.820,11.097,37.302
+pravda.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.770,-4.246,17.447
+pravda.skp_tile_512x512_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+pravda.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,31.990,17.191,45.189
+pravda.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.120,-3.948,17.832
+pravda.skp_tile_64x1024_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+pravda.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,56.010,37.608,71.611
+pravda.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.860,1.781,25.246
+sahadan.skp_copy_tiles_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+sahadan.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,39.980,23.983,53.978
+sahadan.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.170,-3.056,18.987
+sahadan.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sahadan.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.329,-9.720,10.362
+sahadan.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.044,-9.963,10.048
+sahadan.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+sahadan.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,5.099,-5.666,15.609
+sahadan.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.851,-8.427,12.036
+sahadan.skp_record_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+sahadan.skp_record_,Nexus10_4-1_Float_Bench_32-25th,7.533,-3.597,18.286
+sahadan.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.542,-8.690,11.696
+sahadan.skp_record_grid_,Mac_Float_Bench_32-25th,6.000,-4.900,16.600
+sahadan.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,10.754,-0.860,21.829
+sahadan.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.428,-7.937,12.670
+sahadan.skp_record_rtree_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+sahadan.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,15.259,2.970,26.785
+sahadan.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.160,-6.464,14.576
+sahadan.skp_tile_1024x256_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+sahadan.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,34.690,19.486,48.159
+sahadan.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.730,-2.579,19.603
+sahadan.skp_tile_1024x64_,Mac_Float_Bench_32-25th,24.000,10.400,36.400
+sahadan.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,44.220,27.587,58.642
+sahadan.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,11.970,0.175,23.167
+sahadan.skp_tile_256x1024_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+sahadan.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,45.160,28.386,59.676
+sahadan.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.700,-0.905,21.770
+sahadan.skp_tile_256x256_,Mac_Float_Bench_32-25th,24.000,10.400,36.400
+sahadan.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,49.470,32.049,64.417
+sahadan.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,11.820,0.047,23.002
+sahadan.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,48.000,30.800,62.800
+sahadan.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,139.970,108.974,163.967
+sahadan.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,29.570,15.134,42.527
+sahadan.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,28.750,14.438,41.625
+sahadan.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+sahadan.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,30.550,15.968,43.605
+sahadan.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.260,-2.979,19.086
+sahadan.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+sahadan.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,35.790,20.421,49.369
+sahadan.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.840,0.914,24.124
+sahadan.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+sahadan.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,27.060,13.001,39.766
+sahadan.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.620,-1.823,20.582
+sahadan.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+sahadan.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,29.240,14.854,42.164
+sahadan.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.790,-5.079,16.369
+sahadan.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+sahadan.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,30.350,15.797,43.385
+sahadan.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.400,-2.860,19.240
+sahadan.skp_tile_512x512_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+sahadan.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,40.660,24.561,54.726
+sahadan.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.240,-2.146,20.164
+sahadan.skp_tile_64x1024_,Mac_Float_Bench_32-25th,43.000,26.550,57.300
+sahadan.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,87.320,64.222,106.052
+sahadan.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.110,7.944,33.221
+sfgate.skp_copy_tiles_,Mac_Float_Bench_32-25th,48.000,30.800,62.800
+sfgate.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,54.200,36.070,69.620
+sfgate.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,32.520,17.642,45.772
+sfgate.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sfgate.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.209,-9.822,10.230
+sfgate.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.065,-9.945,10.071
+sfgate.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sfgate.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.022,-9.131,11.125
+sfgate.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.352,-9.701,10.387
+sfgate.skp_record_,Mac_Float_Bench_32-25th,25.000,11.250,37.500
+sfgate.skp_record_,Nexus10_4-1_Float_Bench_32-25th,22.113,8.796,34.325
+sfgate.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.909,-7.527,13.200
+sfgate.skp_record_grid_,Mac_Float_Bench_32-25th,26.000,12.100,38.600
+sfgate.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,23.636,10.090,35.999
+sfgate.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.369,-4.587,17.005
+sfgate.skp_record_rtree_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+sfgate.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,25.046,11.289,37.551
+sfgate.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.565,-4.419,17.222
+sfgate.skp_tile_1024x256_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+sfgate.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,51.490,33.767,66.639
+sfgate.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,28.940,14.599,41.834
+sfgate.skp_tile_1024x64_,Mac_Float_Bench_32-25th,37.000,21.450,50.700
+sfgate.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,67.050,46.992,83.755
+sfgate.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,35.830,20.455,49.413
+sfgate.skp_tile_256x1024_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+sfgate.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,60.500,41.425,76.550
+sfgate.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.170,16.495,44.287
+sfgate.skp_tile_256x256_,Mac_Float_Bench_32-25th,35.000,19.750,48.500
+sfgate.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,68.520,48.242,85.372
+sfgate.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,33.830,18.755,47.213
+sfgate.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,58.000,39.300,73.800
+sfgate.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,180.130,143.111,208.143
+sfgate.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,84.330,61.680,102.763
+sfgate.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,83.170,60.695,101.487
+sfgate.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,24.000,10.400,36.400
+sfgate.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,35.080,19.818,48.588
+sfgate.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.890,13.706,40.679
+sfgate.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+sfgate.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,59.440,40.524,75.384
+sfgate.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,36.350,20.898,49.985
+sfgate.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+sfgate.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,54.020,35.917,69.422
+sfgate.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,35.260,19.971,48.786
+sfgate.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+sfgate.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,49.500,32.075,64.450
+sfgate.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,35.750,20.387,49.325
+sfgate.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,24.000,10.400,36.400
+sfgate.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,32.630,17.736,45.893
+sfgate.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.480,13.358,40.228
+sfgate.skp_tile_512x512_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+sfgate.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,54.810,36.589,70.291
+sfgate.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,29.430,15.015,42.373
+sfgate.skp_tile_64x1024_,Mac_Float_Bench_32-25th,53.000,35.050,68.300
+sfgate.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,104.750,79.037,125.225
+sfgate.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,43.540,27.009,57.894
+simsimi.skp_copy_tiles_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+simsimi.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,19.240,6.354,31.164
+simsimi.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.170,-7.306,13.487
+simsimi.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+simsimi.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.078,-9.934,10.085
+simsimi.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.010,-9.991,10.011
+simsimi.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+simsimi.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.123,-9.895,10.135
+simsimi.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.025,-9.979,10.027
+simsimi.skp_record_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+simsimi.skp_record_,Nexus10_4-1_Float_Bench_32-25th,1.485,-8.738,11.634
+simsimi.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.299,-9.745,10.329
+simsimi.skp_record_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+simsimi.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,1.625,-8.619,11.787
+simsimi.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.336,-9.714,10.369
+simsimi.skp_record_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+simsimi.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.675,-8.577,11.842
+simsimi.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.342,-9.709,10.376
+simsimi.skp_tile_1024x256_,Mac_Float_Bench_32-25th,6.000,-4.900,16.600
+simsimi.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,20.610,7.518,32.671
+simsimi.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.250,-7.238,13.575
+simsimi.skp_tile_1024x64_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+simsimi.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,16.750,4.237,28.425
+simsimi.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.750,-6.812,14.125
+simsimi.skp_tile_256x1024_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+simsimi.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,27.500,13.375,40.250
+simsimi.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.660,-6.039,15.126
+simsimi.skp_tile_256x256_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+simsimi.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,18.580,5.793,30.438
+simsimi.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.870,-6.710,14.257
+simsimi.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+simsimi.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,61.930,42.640,78.123
+simsimi.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.620,-0.973,21.682
+simsimi.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.390,-2.018,20.329
+simsimi.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+simsimi.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,18.260,5.521,30.086
+simsimi.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.860,-6.719,14.246
+simsimi.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+simsimi.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,14.720,2.512,26.192
+simsimi.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.670,-7.731,12.937
+simsimi.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+simsimi.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,13.700,1.645,25.070
+simsimi.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.190,-8.139,12.409
+simsimi.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+simsimi.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,15.790,3.421,27.369
+simsimi.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.140,-8.181,12.354
+simsimi.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+simsimi.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,19.180,6.303,31.098
+simsimi.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.510,-7.017,13.861
+simsimi.skp_tile_512x512_,Mac_Float_Bench_32-25th,7.000,-4.050,17.700
+simsimi.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,24.220,10.587,36.642
+simsimi.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.330,-7.170,13.663
+simsimi.skp_tile_64x1024_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+simsimi.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,24.330,10.680,36.763
+simsimi.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.100,-4.815,16.710
+slashdot.skp_copy_tiles_,Mac_Float_Bench_32-25th,44.000,27.400,58.400
+slashdot.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,55.600,37.260,71.160
+slashdot.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.340,0.489,23.574
+slashdot.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+slashdot.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.158,-9.866,10.173
+slashdot.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.025,-9.979,10.027
+slashdot.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+slashdot.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.732,-9.378,10.805
+slashdot.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.241,-9.795,10.266
+slashdot.skp_record_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+slashdot.skp_record_,Nexus10_4-1_Float_Bench_32-25th,14.645,2.448,26.109
+slashdot.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.077,-6.535,14.485
+slashdot.skp_record_grid_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+slashdot.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,17.325,4.726,29.058
+slashdot.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.430,-6.235,14.873
+slashdot.skp_record_rtree_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+slashdot.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,18.561,5.777,30.417
+slashdot.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.604,-6.086,15.065
+slashdot.skp_tile_1024x256_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+slashdot.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,57.220,38.637,72.942
+slashdot.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.940,0.999,24.234
+slashdot.skp_tile_1024x64_,Mac_Float_Bench_32-25th,36.000,20.600,49.600
+slashdot.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,63.900,44.315,80.290
+slashdot.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.960,4.416,28.656
+slashdot.skp_tile_256x1024_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+slashdot.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,68.910,48.573,85.801
+slashdot.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.790,3.421,27.369
+slashdot.skp_tile_256x256_,Mac_Float_Bench_32-25th,36.000,20.600,49.600
+slashdot.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,64.040,44.434,80.444
+slashdot.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,17.550,4.918,29.305
+slashdot.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,115.000,87.750,136.500
+slashdot.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,438.630,362.835,492.493
+slashdot.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,79.930,57.941,97.923
+slashdot.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,79.330,57.430,97.263
+slashdot.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,26.000,12.100,38.600
+slashdot.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,41.050,24.892,55.155
+slashdot.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.640,0.744,23.904
+slashdot.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,22.000,8.700,34.200
+slashdot.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,48.440,31.174,63.284
+slashdot.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.860,2.631,26.346
+slashdot.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+slashdot.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,56.600,38.110,72.260
+slashdot.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.450,-3.668,18.195
+slashdot.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+slashdot.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,61.760,42.496,77.936
+slashdot.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.630,-2.664,19.493
+slashdot.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,26.000,12.100,38.600
+slashdot.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,44.080,27.468,58.488
+slashdot.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.770,0.854,24.047
+slashdot.skp_tile_512x512_,Mac_Float_Bench_32-25th,31.000,16.350,44.100
+slashdot.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,61.430,42.215,77.573
+slashdot.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.090,1.976,25.499
+slashdot.skp_tile_64x1024_,Mac_Float_Bench_32-25th,56.000,37.600,71.600
+slashdot.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,103.190,77.712,123.509
+slashdot.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.740,13.579,40.514
+sms.personal.skp_copy_tiles_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+sms.personal.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,15.030,2.775,26.533
+sms.personal.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.250,-8.088,12.475
+sms.personal.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sms.personal.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.060,-9.949,10.066
+sms.personal.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.009,-9.993,10.009
+sms.personal.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sms.personal.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.190,-9.839,10.209
+sms.personal.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.057,-9.952,10.062
+sms.personal.skp_record_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sms.personal.skp_record_,Nexus10_4-1_Float_Bench_32-25th,0.654,-9.444,10.720
+sms.personal.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.143,-9.879,10.157
+sms.personal.skp_record_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sms.personal.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,0.987,-9.161,11.085
+sms.personal.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.210,-9.821,10.231
+sms.personal.skp_record_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+sms.personal.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.129,-9.040,11.242
+sms.personal.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.251,-9.787,10.276
+sms.personal.skp_tile_1024x256_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+sms.personal.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,9.500,-1.925,20.450
+sms.personal.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.510,-7.867,12.761
+sms.personal.skp_tile_1024x64_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+sms.personal.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,8.240,-2.996,19.064
+sms.personal.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.470,-7.901,12.717
+sms.personal.skp_tile_256x1024_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+sms.personal.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,11.580,-0.157,22.738
+sms.personal.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.250,-7.238,13.575
+sms.personal.skp_tile_256x256_,Mac_Float_Bench_32-25th,5.000,-5.750,15.500
+sms.personal.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,10.010,-1.492,21.011
+sms.personal.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.800,-7.620,13.080
+sms.personal.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+sms.personal.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,35.300,20.005,48.830
+sms.personal.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.410,-6.252,14.851
+sms.personal.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.660,-6.889,14.026
+sms.personal.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+sms.personal.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,7.210,-3.872,17.931
+sms.personal.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.220,-8.113,12.442
+sms.personal.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+sms.personal.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,9.190,-2.189,20.109
+sms.personal.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.080,-8.232,12.288
+sms.personal.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,3.000,-7.450,13.300
+sms.personal.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,6.670,-4.330,17.337
+sms.personal.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.730,-8.530,11.903
+sms.personal.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,3.000,-7.450,13.300
+sms.personal.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,6.180,-4.747,16.798
+sms.personal.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.390,-8.819,11.529
+sms.personal.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+sms.personal.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,7.650,-3.497,18.415
+sms.personal.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.450,-7.918,12.695
+sms.personal.skp_tile_512x512_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+sms.personal.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,10.410,-1.152,21.451
+sms.personal.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.700,-7.705,12.970
+sms.personal.skp_tile_64x1024_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+sms.personal.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,16.450,3.982,28.095
+sms.personal.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.030,-5.724,15.533
+spreadsheet.skp_copy_tiles_,Mac_Float_Bench_32-25th,25.000,11.250,37.500
+spreadsheet.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,38.550,22.767,52.405
+spreadsheet.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.600,-5.240,16.160
+spreadsheet.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+spreadsheet.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.109,-9.907,10.120
+spreadsheet.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.016,-9.987,10.017
+spreadsheet.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,2.000,-8.300,12.200
+spreadsheet.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,2.906,-7.530,13.197
+spreadsheet.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.006,-9.145,11.106
+spreadsheet.skp_record_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+spreadsheet.skp_record_,Nexus10_4-1_Float_Bench_32-25th,3.216,-7.266,13.538
+spreadsheet.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.692,-9.412,10.761
+spreadsheet.skp_record_grid_,Mac_Float_Bench_32-25th,2.000,-8.300,12.200
+spreadsheet.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,5.020,-5.733,15.522
+spreadsheet.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.254,-8.934,11.379
+spreadsheet.skp_record_rtree_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+spreadsheet.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,7.217,-3.865,17.939
+spreadsheet.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.180,-8.147,12.398
+spreadsheet.skp_tile_1024x256_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+spreadsheet.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,37.510,21.883,51.261
+spreadsheet.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.780,-4.237,17.458
+spreadsheet.skp_tile_1024x64_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+spreadsheet.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,39.140,23.269,53.054
+spreadsheet.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.970,-1.525,20.967
+spreadsheet.skp_tile_256x1024_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+spreadsheet.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,41.260,25.071,55.386
+spreadsheet.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.810,-4.212,17.491
+spreadsheet.skp_tile_256x256_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+spreadsheet.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,37.600,21.960,51.360
+spreadsheet.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.220,-3.013,19.042
+spreadsheet.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,43.000,26.550,57.300
+spreadsheet.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,153.320,120.322,178.652
+spreadsheet.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,20.040,7.034,32.044
+spreadsheet.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,19.310,6.413,31.241
+spreadsheet.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+spreadsheet.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,18.000,5.300,29.800
+spreadsheet.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.580,-6.107,15.038
+spreadsheet.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+spreadsheet.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,29.260,14.871,42.186
+spreadsheet.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.900,-5.835,15.390
+spreadsheet.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+spreadsheet.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,28.360,14.106,41.196
+spreadsheet.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.800,-6.770,14.180
+spreadsheet.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+spreadsheet.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,21.560,8.326,33.716
+spreadsheet.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.660,-6.889,14.026
+spreadsheet.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+spreadsheet.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,17.400,4.790,29.140
+spreadsheet.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.850,-5.878,15.335
+spreadsheet.skp_tile_512x512_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+spreadsheet.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,32.700,17.795,45.970
+spreadsheet.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.560,-4.424,17.216
+spreadsheet.skp_tile_64x1024_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+spreadsheet.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,43.880,27.298,58.268
+spreadsheet.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,11.100,-0.565,22.210
+techcrunch.skp_copy_tiles_,Mac_Float_Bench_32-25th,94.000,69.900,113.400
+techcrunch.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,99.870,74.889,119.857
+techcrunch.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.290,13.197,40.019
+techcrunch.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+techcrunch.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.193,-9.836,10.212
+techcrunch.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.071,-9.939,10.078
+techcrunch.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+techcrunch.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.902,-9.233,10.993
+techcrunch.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.340,-9.711,10.373
+techcrunch.skp_record_,Mac_Float_Bench_32-25th,8.000,-3.200,18.800
+techcrunch.skp_record_,Nexus10_4-1_Float_Bench_32-25th,11.632,-0.113,22.795
+techcrunch.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.096,-7.368,13.406
+techcrunch.skp_record_grid_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+techcrunch.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,12.972,1.026,24.269
+techcrunch.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.429,-7.085,13.772
+techcrunch.skp_record_rtree_,Mac_Float_Bench_32-25th,9.000,-2.350,19.900
+techcrunch.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,12.786,0.868,24.064
+techcrunch.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.681,-6.871,14.050
+techcrunch.skp_tile_1024x256_,Mac_Float_Bench_32-25th,35.000,19.750,48.500
+techcrunch.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,72.850,51.922,90.135
+techcrunch.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,29.250,14.863,42.175
+techcrunch.skp_tile_1024x64_,Mac_Float_Bench_32-25th,45.000,28.250,59.500
+techcrunch.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,67.580,47.443,84.338
+techcrunch.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,35.650,20.302,49.215
+techcrunch.skp_tile_256x1024_,Mac_Float_Bench_32-25th,40.000,24.000,54.000
+techcrunch.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,102.470,77.099,122.717
+techcrunch.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,32.900,17.965,46.190
+techcrunch.skp_tile_256x256_,Mac_Float_Bench_32-25th,44.000,27.400,58.400
+techcrunch.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,67.050,46.992,83.755
+techcrunch.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.870,17.090,45.057
+techcrunch.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,55.000,36.750,70.500
+techcrunch.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,307.070,251.010,347.777
+techcrunch.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,65.240,45.454,81.764
+techcrunch.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,64.220,44.587,80.642
+techcrunch.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+techcrunch.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,44.690,27.986,59.159
+techcrunch.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.170,16.495,44.287
+techcrunch.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,32.000,17.200,45.200
+techcrunch.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,57.190,38.611,72.909
+techcrunch.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.770,17.005,44.947
+techcrunch.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+techcrunch.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,63.030,43.575,79.333
+techcrunch.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.770,17.005,44.947
+techcrunch.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,31.000,16.350,44.100
+techcrunch.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,58.490,39.717,74.339
+techcrunch.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.270,13.179,39.997
+techcrunch.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+techcrunch.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,45.580,28.743,60.138
+techcrunch.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.780,17.013,44.958
+techcrunch.skp_tile_512x512_,Mac_Float_Bench_32-25th,36.000,20.600,49.600
+techcrunch.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,76.380,54.923,94.018
+techcrunch.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,30.060,15.551,43.066
+techcrunch.skp_tile_64x1024_,Mac_Float_Bench_32-25th,70.000,49.500,87.000
+techcrunch.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,113.100,86.135,134.410
+techcrunch.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,45.220,28.437,59.742
+theverge.skp_copy_tiles_,Mac_Float_Bench_32-25th,274.000,222.900,311.400
+theverge.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,391.770,323.004,440.947
+theverge.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,111.010,84.359,132.111
+theverge.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+theverge.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.434,-9.631,10.478
+theverge.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.088,-9.926,10.096
+theverge.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+theverge.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.182,-8.995,11.300
+theverge.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.349,-9.703,10.384
+theverge.skp_record_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+theverge.skp_record_,Nexus10_4-1_Float_Bench_32-25th,8.657,-2.642,19.522
+theverge.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.810,-7.611,13.091
+theverge.skp_record_grid_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+theverge.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,10.805,-0.816,21.885
+theverge.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.401,-7.110,13.741
+theverge.skp_record_rtree_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+theverge.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,11.480,-0.242,22.628
+theverge.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.598,-6.942,13.958
+theverge.skp_tile_1024x256_,Mac_Float_Bench_32-25th,297.000,242.450,336.700
+theverge.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,457.580,378.943,513.338
+theverge.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,126.950,97.907,149.645
+theverge.skp_tile_1024x64_,Mac_Float_Bench_32-25th,648.000,540.800,722.800
+theverge.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,946.900,794.865,1051.590
+theverge.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,253.000,205.050,288.300
+theverge.skp_tile_256x1024_,Mac_Float_Bench_32-25th,510.000,423.500,571.000
+theverge.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,742.240,620.904,826.464
+theverge.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,196.350,156.897,225.985
+theverge.skp_tile_256x256_,Mac_Float_Bench_32-25th,463.000,383.550,519.300
+theverge.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,648.840,541.514,723.724
+theverge.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,187.510,149.383,216.261
+theverge.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,212.000,170.200,243.200
+theverge.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,965.380,810.573,1071.918
+theverge.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,206.760,165.746,237.436
+theverge.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,206.410,165.448,237.051
+theverge.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,436.000,360.600,489.600
+theverge.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,679.850,567.873,757.835
+theverge.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,174.690,138.487,202.159
+theverge.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,244.000,197.400,278.400
+theverge.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,417.970,345.274,469.767
+theverge.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,118.930,91.091,140.823
+theverge.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,247.000,199.950,281.700
+theverge.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,477.590,395.951,535.349
+theverge.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,94.900,70.665,114.390
+theverge.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,261.000,211.850,297.100
+theverge.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,477.010,395.458,534.711
+theverge.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,91.400,67.690,110.540
+theverge.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,438.000,362.300,491.800
+theverge.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,665.620,555.777,742.182
+theverge.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,175.470,139.149,203.017
+theverge.skp_tile_512x512_,Mac_Float_Bench_32-25th,322.000,263.700,364.200
+theverge.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,500.210,415.178,560.231
+theverge.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,136.050,105.643,159.655
+theverge.skp_tile_64x1024_,Mac_Float_Bench_32-25th,886.000,743.100,984.600
+theverge.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,1221.320,1028.122,1353.452
+theverge.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,327.360,268.256,370.096
+transformice.skp_copy_tiles_,Mac_Float_Bench_32-25th,55.000,36.750,70.500
+transformice.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,75.730,54.371,93.303
+transformice.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.490,0.617,23.739
+transformice.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+transformice.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.109,-9.907,10.120
+transformice.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.023,-9.980,10.026
+transformice.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+transformice.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.402,-9.658,10.443
+transformice.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.131,-9.888,10.145
+transformice.skp_record_,Mac_Float_Bench_32-25th,13.000,1.050,24.300
+transformice.skp_record_,Nexus10_4-1_Float_Bench_32-25th,5.155,-5.619,15.670
+transformice.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.516,-8.712,11.667
+transformice.skp_record_grid_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+transformice.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,6.207,-4.724,16.828
+transformice.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.775,-8.491,11.952
+transformice.skp_record_rtree_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+transformice.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,6.435,-4.530,17.078
+transformice.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.752,-8.511,11.927
+transformice.skp_tile_1024x256_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+transformice.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,74.540,53.359,91.994
+transformice.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.660,0.761,23.926
+transformice.skp_tile_1024x64_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+transformice.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,64.520,44.842,80.972
+transformice.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.780,2.563,26.258
+transformice.skp_tile_256x1024_,Mac_Float_Bench_32-25th,31.000,16.350,44.100
+transformice.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,85.340,62.539,103.874
+transformice.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.380,3.073,26.918
+transformice.skp_tile_256x256_,Mac_Float_Bench_32-25th,31.000,16.350,44.100
+transformice.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,64.810,45.089,81.291
+transformice.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.970,3.575,27.567
+transformice.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,55.000,36.750,70.500
+transformice.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,226.040,182.134,258.644
+transformice.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,41.350,25.148,55.485
+transformice.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,40.390,24.331,54.429
+transformice.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+transformice.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,54.600,36.410,70.060
+transformice.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.250,1.262,24.575
+transformice.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+transformice.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,56.260,37.821,71.886
+transformice.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,17.700,5.045,29.470
+transformice.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+transformice.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,59.970,40.974,75.967
+transformice.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.320,1.322,24.652
+transformice.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+transformice.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,46.170,29.245,60.787
+transformice.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.780,-0.837,21.858
+transformice.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+transformice.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,57.730,39.070,73.503
+transformice.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.340,1.339,24.674
+transformice.skp_tile_512x512_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+transformice.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,83.230,60.746,101.553
+transformice.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.590,1.551,24.949
+transformice.skp_tile_64x1024_,Mac_Float_Bench_32-25th,47.000,29.950,61.700
+transformice.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,94.550,70.367,114.005
+transformice.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,23.030,9.576,35.333
+uk.wsj.skp_copy_tiles_,Mac_Float_Bench_32-25th,55.000,36.750,70.500
+uk.wsj.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,90.040,66.534,109.044
+uk.wsj.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,47.140,30.069,61.854
+uk.wsj.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+uk.wsj.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.419,-9.644,10.461
+uk.wsj.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.105,-9.911,10.116
+uk.wsj.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+uk.wsj.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.244,-8.943,11.368
+uk.wsj.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.423,-9.641,10.465
+uk.wsj.skp_record_,Mac_Float_Bench_32-25th,47.000,29.950,61.700
+uk.wsj.skp_record_,Nexus10_4-1_Float_Bench_32-25th,43.680,27.128,58.048
+uk.wsj.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.444,0.578,23.689
+uk.wsj.skp_record_grid_,Mac_Float_Bench_32-25th,48.000,30.800,62.800
+uk.wsj.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,45.131,28.361,59.644
+uk.wsj.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.387,0.529,23.626
+uk.wsj.skp_record_rtree_,Mac_Float_Bench_32-25th,48.000,30.800,62.800
+uk.wsj.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,46.551,29.568,61.206
+uk.wsj.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.687,0.784,23.955
+uk.wsj.skp_tile_1024x256_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+uk.wsj.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,78.930,57.091,96.823
+uk.wsj.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,37.400,21.790,51.140
+uk.wsj.skp_tile_1024x64_,Mac_Float_Bench_32-25th,44.000,27.400,58.400
+uk.wsj.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,93.740,69.679,113.114
+uk.wsj.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,45.410,28.598,59.951
+uk.wsj.skp_tile_256x1024_,Mac_Float_Bench_32-25th,42.000,25.700,56.200
+uk.wsj.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,101.360,76.156,121.496
+uk.wsj.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,42.980,26.533,57.278
+uk.wsj.skp_tile_256x256_,Mac_Float_Bench_32-25th,42.000,25.700,56.200
+uk.wsj.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,110.150,83.627,131.165
+uk.wsj.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,43.560,27.026,57.916
+uk.wsj.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,86.000,63.100,104.600
+uk.wsj.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,390.730,322.120,439.803
+uk.wsj.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,118.200,90.470,140.020
+uk.wsj.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,116.960,89.416,138.656
+uk.wsj.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+uk.wsj.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,67.180,47.103,83.898
+uk.wsj.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,37.870,22.189,51.657
+uk.wsj.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+uk.wsj.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,79.810,57.838,97.791
+uk.wsj.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,45.800,28.930,60.380
+uk.wsj.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+uk.wsj.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,99.910,74.923,119.901
+uk.wsj.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,46.980,29.933,61.678
+uk.wsj.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,25.000,11.250,37.500
+uk.wsj.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,93.620,69.577,112.982
+uk.wsj.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,43.280,26.788,57.608
+uk.wsj.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+uk.wsj.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,68.590,48.302,85.449
+uk.wsj.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,38.570,22.785,52.427
+uk.wsj.skp_tile_512x512_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+uk.wsj.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,85.980,63.083,104.578
+uk.wsj.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,37.600,21.960,51.360
+uk.wsj.skp_tile_64x1024_,Mac_Float_Bench_32-25th,71.000,50.350,88.100
+uk.wsj.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,158.620,124.827,184.482
+uk.wsj.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,58.660,39.861,74.526
+vnexpress.skp_copy_tiles_,Mac_Float_Bench_32-25th,36.000,20.600,49.600
+vnexpress.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,44.920,28.182,59.412
+vnexpress.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.100,-1.415,21.110
+vnexpress.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+vnexpress.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.297,-9.748,10.327
+vnexpress.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.077,-9.935,10.085
+vnexpress.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+vnexpress.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.426,-8.788,11.568
+vnexpress.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.476,-9.595,10.524
+vnexpress.skp_record_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+vnexpress.skp_record_,Nexus10_4-1_Float_Bench_32-25th,7.647,-3.500,18.412
+vnexpress.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.284,-8.058,12.513
+vnexpress.skp_record_grid_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+vnexpress.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,9.806,-1.665,20.787
+vnexpress.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,2.700,-7.705,12.970
+vnexpress.skp_record_rtree_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+vnexpress.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,10.827,-0.797,21.909
+vnexpress.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.119,-7.349,13.430
+vnexpress.skp_tile_1024x256_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+vnexpress.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,43.380,26.873,57.718
+vnexpress.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,11.220,-0.463,22.342
+vnexpress.skp_tile_1024x64_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+vnexpress.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,54.370,36.214,69.807
+vnexpress.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.640,4.144,28.304
+vnexpress.skp_tile_256x1024_,Mac_Float_Bench_32-25th,23.000,9.550,35.300
+vnexpress.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,52.070,34.259,67.277
+vnexpress.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,12.370,0.514,23.607
+vnexpress.skp_tile_256x256_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+vnexpress.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,50.070,32.559,65.077
+vnexpress.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,15.260,2.971,26.786
+vnexpress.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,50.000,32.500,65.000
+vnexpress.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,125.220,96.437,147.742
+vnexpress.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,47.190,30.111,61.909
+vnexpress.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,46.230,29.295,60.853
+vnexpress.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+vnexpress.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,37.570,21.934,51.327
+vnexpress.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,11.380,-0.327,22.518
+vnexpress.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+vnexpress.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,46.210,29.279,60.831
+vnexpress.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,29.440,15.024,42.384
+vnexpress.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+vnexpress.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,37.000,21.450,50.700
+vnexpress.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.400,9.040,34.640
+vnexpress.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+vnexpress.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,42.950,26.508,57.245
+vnexpress.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.280,8.088,33.408
+vnexpress.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,20.000,7.000,32.000
+vnexpress.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,38.030,22.325,51.833
+vnexpress.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,11.280,-0.412,22.408
+vnexpress.skp_tile_512x512_,Mac_Float_Bench_32-25th,21.000,7.850,33.100
+vnexpress.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,48.630,31.336,63.493
+vnexpress.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,11.210,-0.471,22.331
+vnexpress.skp_tile_64x1024_,Mac_Float_Bench_32-25th,38.000,22.300,51.800
+vnexpress.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,74.780,53.563,92.258
+vnexpress.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,20.130,7.110,32.143
+worldjournal.skp_copy_tiles_,Mac_Float_Bench_32-25th,55.000,36.750,70.500
+worldjournal.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,78.080,56.368,95.888
+worldjournal.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,23.710,10.154,36.081
+worldjournal.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+worldjournal.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.144,-9.877,10.159
+worldjournal.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.026,-9.978,10.028
+worldjournal.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+worldjournal.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.978,-9.168,11.076
+worldjournal.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.332,-9.718,10.366
+worldjournal.skp_record_,Mac_Float_Bench_32-25th,17.000,4.450,28.700
+worldjournal.skp_record_,Nexus10_4-1_Float_Bench_32-25th,20.476,7.405,32.524
+worldjournal.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.391,-5.418,15.930
+worldjournal.skp_record_grid_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+worldjournal.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,23.009,9.558,35.310
+worldjournal.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.806,-5.065,16.387
+worldjournal.skp_record_rtree_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+worldjournal.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,23.611,10.070,35.973
+worldjournal.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.068,-4.842,16.675
+worldjournal.skp_tile_1024x256_,Mac_Float_Bench_32-25th,36.000,20.600,49.600
+worldjournal.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,69.960,49.466,86.956
+worldjournal.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,21.490,8.266,33.639
+worldjournal.skp_tile_1024x64_,Mac_Float_Bench_32-25th,45.000,28.250,59.500
+worldjournal.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,80.740,58.629,98.814
+worldjournal.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.150,12.227,38.765
+worldjournal.skp_tile_256x1024_,Mac_Float_Bench_32-25th,44.000,27.400,58.400
+worldjournal.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,93.080,69.118,112.388
+worldjournal.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.580,10.893,37.038
+worldjournal.skp_tile_256x256_,Mac_Float_Bench_32-25th,48.000,30.800,62.800
+worldjournal.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,96.530,72.050,116.183
+worldjournal.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,28.150,13.927,40.965
+worldjournal.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,364.000,299.400,410.400
+worldjournal.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,2322.570,1964.185,2564.827
+worldjournal.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,280.620,228.527,318.682
+worldjournal.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,279.230,227.346,317.153
+worldjournal.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,41.000,24.850,55.100
+worldjournal.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,73.900,52.815,91.290
+worldjournal.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.270,12.329,38.897
+worldjournal.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+worldjournal.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,78.190,56.462,96.009
+worldjournal.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,19.570,6.634,31.527
+worldjournal.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+worldjournal.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,83.360,60.856,101.696
+worldjournal.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,18.920,6.082,30.812
+worldjournal.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+worldjournal.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,83.140,60.669,101.454
+worldjournal.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,16.920,4.382,28.612
+worldjournal.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,41.000,24.850,55.100
+worldjournal.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,71.580,50.843,88.738
+worldjournal.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.550,12.567,39.205
+worldjournal.skp_tile_512x512_,Mac_Float_Bench_32-25th,39.000,23.150,52.900
+worldjournal.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,76.370,54.915,94.007
+worldjournal.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,22.960,9.516,35.256
+worldjournal.skp_tile_64x1024_,Mac_Float_Bench_32-25th,80.000,58.000,98.000
+worldjournal.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,147.060,115.001,171.766
+worldjournal.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,40.890,24.757,54.979
+wowwiki-pandaria.skp_copy_tiles_,Mac_Float_Bench_32-25th,264.000,214.400,300.400
+wowwiki-pandaria.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,307.090,251.026,347.799
+wowwiki-pandaria.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,98.200,73.470,118.020
+wowwiki-pandaria.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+wowwiki-pandaria.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.087,-9.926,10.096
+wowwiki-pandaria.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,4.000,-6.600,14.400
+wowwiki-pandaria.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,1.745,-8.517,11.920
+wowwiki-pandaria.skp_record_,Mac_Float_Bench_32-25th,22.000,8.700,34.200
+wowwiki-pandaria.skp_record_,Nexus10_4-1_Float_Bench_32-25th,26.216,12.284,38.838
+wowwiki-pandaria.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.506,-4.470,17.157
+wowwiki-pandaria.skp_record_grid_,Mac_Float_Bench_32-25th,26.000,12.100,38.600
+wowwiki-pandaria.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,34.199,19.069,47.618
+wowwiki-pandaria.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,8.397,-2.863,19.237
+wowwiki-pandaria.skp_record_rtree_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+wowwiki-pandaria.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,38.059,22.350,51.865
+wowwiki-pandaria.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.115,-1.402,21.127
+wowwiki-pandaria.skp_tile_1024x256_,Mac_Float_Bench_32-25th,147.000,114.950,171.700
+wowwiki-pandaria.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,322.730,264.320,365.003
+wowwiki-pandaria.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,106.170,80.245,126.787
+wowwiki-pandaria.skp_tile_1024x64_,Mac_Float_Bench_32-25th,288.000,234.800,326.800
+wowwiki-pandaria.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,531.600,441.860,594.760
+wowwiki-pandaria.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,174.580,138.393,202.038
+wowwiki-pandaria.skp_tile_256x256_,Mac_Float_Bench_32-25th,232.000,187.200,265.200
+wowwiki-pandaria.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,442.340,365.989,496.574
+wowwiki-pandaria.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,152.600,119.710,177.860
+wowwiki-pandaria.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,294.000,239.900,333.400
+wowwiki-pandaria.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,1163.330,978.830,1289.663
+wowwiki-pandaria.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,258.360,209.606,294.196
+wowwiki-pandaria.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,256.680,208.178,292.348
+wowwiki-pandaria.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,102.000,76.700,122.200
+wowwiki-pandaria.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,152.400,119.540,177.640
+wowwiki-pandaria.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,81.410,59.198,99.551
+wowwiki-pandaria.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,127.000,97.950,149.700
+wowwiki-pandaria.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,313.180,256.203,354.498
+wowwiki-pandaria.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,101.520,76.292,121.672
+wowwiki-pandaria.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,131.000,101.350,154.100
+wowwiki-pandaria.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,382.000,314.700,430.200
+wowwiki-pandaria.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,87.510,64.383,106.261
+wowwiki-pandaria.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,129.000,99.650,151.900
+wowwiki-pandaria.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,329.600,270.160,372.560
+wowwiki-pandaria.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,85.300,62.505,103.830
+wowwiki-pandaria.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,103.000,77.550,123.300
+wowwiki-pandaria.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,165.470,130.649,192.017
+wowwiki-pandaria.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,82.860,60.431,101.146
+wowwiki-pandaria.skp_tile_512x512_,Mac_Float_Bench_32-25th,140.000,109.000,164.000
+wowwiki-pandaria.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,327.710,268.553,370.481
+wowwiki-pandaria.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,101.990,76.691,122.189
+wsj.skp_copy_tiles_,Mac_Float_Bench_32-25th,66.000,46.100,82.600
+wsj.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,99.460,74.541,119.406
+wsj.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,52.960,35.016,68.256
+wsj.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+wsj.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.495,-9.579,10.544
+wsj.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.100,-9.915,10.110
+wsj.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,1.000,-9.150,11.100
+wsj.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,1.399,-8.811,11.539
+wsj.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.428,-9.636,10.471
+wsj.skp_record_,Mac_Float_Bench_32-25th,39.000,23.150,52.900
+wsj.skp_record_,Nexus10_4-1_Float_Bench_32-25th,33.695,18.641,47.065
+wsj.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,9.947,-1.545,20.942
+wsj.skp_record_grid_,Mac_Float_Bench_32-25th,40.000,24.000,54.000
+wsj.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,36.760,21.246,50.435
+wsj.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.543,-1.038,21.597
+wsj.skp_record_rtree_,Mac_Float_Bench_32-25th,40.000,24.000,54.000
+wsj.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,37.198,21.618,50.918
+wsj.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,10.668,-0.933,21.734
+wsj.skp_tile_1024x256_,Mac_Float_Bench_32-25th,38.000,22.300,51.800
+wsj.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,91.690,67.936,110.859
+wsj.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,44.900,28.165,59.390
+wsj.skp_tile_1024x64_,Mac_Float_Bench_32-25th,52.000,34.200,67.200
+wsj.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,120.550,92.468,142.605
+wsj.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,53.800,35.730,69.180
+wsj.skp_tile_256x1024_,Mac_Float_Bench_32-25th,47.000,29.950,61.700
+wsj.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,111.260,84.571,132.386
+wsj.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,51.230,33.545,66.353
+wsj.skp_tile_256x256_,Mac_Float_Bench_32-25th,49.000,31.650,63.900
+wsj.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,124.410,95.748,146.851
+wsj.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,52.020,34.217,67.222
+wsj.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,98.000,73.300,117.800
+wsj.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,431.190,356.512,484.309
+wsj.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,132.120,102.302,155.332
+wsj.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,130.880,101.248,153.968
+wsj.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+wsj.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,83.900,61.315,102.290
+wsj.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,44.200,27.570,58.620
+wsj.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+wsj.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,100.870,75.740,120.957
+wsj.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,54.740,36.529,70.214
+wsj.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,30.000,15.500,43.000
+wsj.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,101.430,76.216,121.573
+wsj.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,52.530,34.651,67.783
+wsj.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+wsj.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,130.560,100.976,153.616
+wsj.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,57.430,38.816,73.173
+wsj.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,34.000,18.900,47.400
+wsj.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,79.550,57.617,97.505
+wsj.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,43.740,27.179,58.114
+wsj.skp_tile_512x512_,Mac_Float_Bench_32-25th,38.000,22.300,51.800
+wsj.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,106.350,80.397,126.985
+wsj.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,45.290,28.496,59.819
+wsj.skp_tile_64x1024_,Mac_Float_Bench_32-25th,83.000,60.550,101.300
+wsj.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,179.240,142.354,207.164
+wsj.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,68.060,47.851,84.866
+xkcd.skp_copy_tiles_,Mac_Float_Bench_32-25th,25.000,11.250,37.500
+xkcd.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,35.890,20.506,49.479
+xkcd.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.760,-5.104,16.336
+xkcd.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+xkcd.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.095,-9.919,10.104
+xkcd.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.021,-9.982,10.023
+xkcd.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+xkcd.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.381,-9.677,10.419
+xkcd.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.120,-9.898,10.132
+xkcd.skp_record_,Mac_Float_Bench_32-25th,57.000,38.450,72.700
+xkcd.skp_record_,Nexus10_4-1_Float_Bench_32-25th,48.923,31.585,63.816
+xkcd.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.475,2.304,25.922
+xkcd.skp_record_grid_,Mac_Float_Bench_32-25th,56.000,37.600,71.600
+xkcd.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,48.224,30.991,63.047
+xkcd.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.123,2.005,25.536
+xkcd.skp_record_rtree_,Mac_Float_Bench_32-25th,55.000,36.750,70.500
+xkcd.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,48.520,31.242,63.373
+xkcd.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,14.220,2.087,25.642
+xkcd.skp_tile_1024x256_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+xkcd.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,30.960,16.316,44.056
+xkcd.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.920,-4.968,16.512
+xkcd.skp_tile_1024x64_,Mac_Float_Bench_32-25th,15.000,2.750,26.500
+xkcd.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,30.200,15.670,43.220
+xkcd.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.840,-4.186,17.524
+xkcd.skp_tile_256x1024_,Mac_Float_Bench_32-25th,19.000,6.150,30.900
+xkcd.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,40.440,24.374,54.484
+xkcd.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.310,-3.787,18.041
+xkcd.skp_tile_256x256_,Mac_Float_Bench_32-25th,18.000,5.300,29.800
+xkcd.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,39.910,23.923,53.901
+xkcd.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,7.970,-3.226,18.767
+xkcd.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,52.000,34.200,67.200
+xkcd.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,999.830,839.856,1109.813
+xkcd.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,33.440,18.424,46.784
+xkcd.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,32.500,17.625,45.750
+xkcd.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+xkcd.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,29.620,15.177,42.582
+xkcd.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.640,-4.356,17.304
+xkcd.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+xkcd.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,26.260,12.321,38.886
+xkcd.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,5.410,-5.402,15.951
+xkcd.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,10.000,-1.500,21.000
+xkcd.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,34.230,19.095,47.653
+xkcd.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.480,-6.192,14.928
+xkcd.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+xkcd.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,27.820,13.647,40.602
+xkcd.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.350,-6.303,14.785
+xkcd.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,14.000,1.900,25.400
+xkcd.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,29.790,15.322,42.769
+xkcd.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.870,-4.160,17.557
+xkcd.skp_tile_512x512_,Mac_Float_Bench_32-25th,16.000,3.600,27.600
+xkcd.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,34.310,19.164,47.741
+xkcd.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,6.720,-4.288,17.392
+xkcd.skp_tile_64x1024_,Mac_Float_Bench_32-25th,29.000,14.650,41.900
+xkcd.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,56.490,38.017,72.139
+xkcd.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,13.770,1.704,25.147
+youtube.skp_copy_tiles_,Mac_Float_Bench_32-25th,58.000,39.300,73.800
+youtube.skp_copy_tiles_,Nexus10_4-1_Float_Bench_32-25th,71.410,50.698,88.551
+youtube.skp_copy_tiles_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,24.720,11.012,37.192
+youtube.skp_playback_creation_grid_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+youtube.skp_playback_creation_grid_,Nexus10_4-1_Float_Bench_32-25th,0.316,-9.731,10.348
+youtube.skp_playback_creation_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.056,-9.952,10.061
+youtube.skp_playback_creation_rtree_,Mac_Float_Bench_32-25th,0.000,-10.000,10.000
+youtube.skp_playback_creation_rtree_,Nexus10_4-1_Float_Bench_32-25th,0.785,-9.332,10.864
+youtube.skp_playback_creation_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,0.227,-9.807,10.250
+youtube.skp_record_,Mac_Float_Bench_32-25th,11.000,-0.650,22.100
+youtube.skp_record_,Nexus10_4-1_Float_Bench_32-25th,14.957,2.714,26.453
+youtube.skp_record_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,3.792,-6.777,14.171
+youtube.skp_record_grid_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+youtube.skp_record_grid_,Nexus10_4-1_Float_Bench_32-25th,17.481,4.859,29.229
+youtube.skp_record_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.093,-6.521,14.503
+youtube.skp_record_rtree_,Mac_Float_Bench_32-25th,12.000,0.200,23.200
+youtube.skp_record_rtree_,Nexus10_4-1_Float_Bench_32-25th,18.196,5.467,30.016
+youtube.skp_record_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,4.325,-6.323,14.758
+youtube.skp_tile_1024x256_,Mac_Float_Bench_32-25th,35.000,19.750,48.500
+youtube.skp_tile_1024x256_,Nexus10_4-1_Float_Bench_32-25th,65.250,45.462,81.775
+youtube.skp_tile_1024x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.510,13.384,40.261
+youtube.skp_tile_1024x64_,Mac_Float_Bench_32-25th,46.000,29.100,60.600
+youtube.skp_tile_1024x64_,Nexus10_4-1_Float_Bench_32-25th,81.900,59.615,100.090
+youtube.skp_tile_1024x64_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,34.270,19.130,47.697
+youtube.skp_tile_256x1024_,Mac_Float_Bench_32-25th,39.000,23.150,52.900
+youtube.skp_tile_256x1024_,Nexus10_4-1_Float_Bench_32-25th,78.450,56.683,96.295
+youtube.skp_tile_256x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.140,12.219,38.754
+youtube.skp_tile_256x256_,Mac_Float_Bench_32-25th,42.000,25.700,56.200
+youtube.skp_tile_256x256_,Nexus10_4-1_Float_Bench_32-25th,72.740,51.829,90.014
+youtube.skp_tile_256x256_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,31.450,16.732,44.595
+youtube.skp_tile_256x256_gpu_,Mac_Float_Bench_32-25th,158.000,124.300,183.800
+youtube.skp_tile_256x256_gpu_,Nexus10_4-1_Float_Bench_32-25th,780.130,653.111,868.143
+youtube.skp_tile_256x256_gpu_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,154.690,121.487,180.159
+youtube.skp_tile_256x256_gpu_c,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,153.650,120.602,179.015
+youtube.skp_tile_256x256_grid_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+youtube.skp_tile_256x256_grid_,Nexus10_4-1_Float_Bench_32-25th,56.710,38.203,72.381
+youtube.skp_tile_256x256_grid_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.340,13.239,40.074
+youtube.skp_tile_256x256_multi_2_threads_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+youtube.skp_tile_256x256_multi_2_threads_,Nexus10_4-1_Float_Bench_32-25th,66.700,46.695,83.370
+youtube.skp_tile_256x256_multi_2_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,26.270,12.329,38.897
+youtube.skp_tile_256x256_multi_3_threads_,Mac_Float_Bench_32-25th,28.000,13.800,40.800
+youtube.skp_tile_256x256_multi_3_threads_,Nexus10_4-1_Float_Bench_32-25th,73.320,52.322,90.652
+youtube.skp_tile_256x256_multi_3_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.210,13.128,39.931
+youtube.skp_tile_256x256_multi_4_threads_,Mac_Float_Bench_32-25th,27.000,12.950,39.700
+youtube.skp_tile_256x256_multi_4_threads_,Nexus10_4-1_Float_Bench_32-25th,70.320,49.772,87.352
+youtube.skp_tile_256x256_multi_4_threads_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,28.800,14.480,41.680
+youtube.skp_tile_256x256_rtree_,Mac_Float_Bench_32-25th,33.000,18.050,46.300
+youtube.skp_tile_256x256_rtree_,Nexus10_4-1_Float_Bench_32-25th,56.230,37.795,71.853
+youtube.skp_tile_256x256_rtree_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.900,13.715,40.690
+youtube.skp_tile_512x512_,Mac_Float_Bench_32-25th,35.000,19.750,48.500
+youtube.skp_tile_512x512_,Nexus10_4-1_Float_Bench_32-25th,70.020,49.517,87.022
+youtube.skp_tile_512x512_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,27.660,13.511,40.426
+youtube.skp_tile_64x1024_,Mac_Float_Bench_32-25th,62.000,42.700,78.200
+youtube.skp_tile_64x1024_,Nexus10_4-1_Float_Bench_32-25th,116.520,89.042,138.172
+youtube.skp_tile_64x1024_,Shuttle_Ubuntu12_ATI5770_Float_Bench_32-25th,38.080,22.368,51.888
diff --git a/bench/bench_graph_svg.py b/bench/bench_graph_svg.py
index d8cd54b..5a9978a 100644
--- a/bench/bench_graph_svg.py
+++ b/bench/bench_graph_svg.py
@@ -11,21 +11,38 @@
 import json
 import xml.sax.saxutils
 
+# We throw out any measurement outside this range, and log a warning.
+MIN_REASONABLE_TIME = 0
+MAX_REASONABLE_TIME = 99999
+
+# Constants for prefixes in output title used in buildbot.
+TITLE_PREAMBLE = 'Bench_Performance_for_Skia_'
+TITLE_PREAMBLE_LENGTH = len(TITLE_PREAMBLE)
+
 def usage():
     """Prints simple usage information."""
     
-    print '-d <dir> a directory containing bench_r<revision>_<scalar> files.'
     print '-b <bench> the bench to show.'
     print '-c <config> the config to show (GPU, 8888, 565, etc).'
-    print '-t <time> the time to show (w, c, g, etc).'
-    print '-s <setting>[=<value>] a setting to show (alpha, scalar, etc).'
-    print '-r <revision>[:<revision>] the revisions to show.'
-    print '   Negative <revision> is taken as offset from most recent revision.'
+    print '-d <dir> a directory containing bench_r<revision>_<scalar> files.'
+    print '-e <file> file containing expected bench values/ranges.'
+    print '   Will raise exception if actual bench values are out of range.'
+    print '   See bench_expectations.txt for data format and examples.'
     print '-f <revision>[:<revision>] the revisions to use for fitting.'
     print '   Negative <revision> is taken as offset from most recent revision.'
+    print '-i <time> the time to ignore (w, c, g, etc).'
+    print '   The flag is ignored when -t is set; otherwise we plot all the'
+    print '   times except the one specified here.'
+    print '-l <title> title to use for the output graph'
+    print '-m <representation> representation of bench value.'
+    print '   See _ListAlgorithm class in bench_util.py.'
+    print '-o <path> path to which to write output; writes to stdout if not specified'
+    print '-r <revision>[:<revision>] the revisions to show.'
+    print '   Negative <revision> is taken as offset from most recent revision.'
+    print '-s <setting>[=<value>] a setting to show (alpha, scalar, etc).'
+    print '-t <time> the time to show (w, c, g, etc).'
     print '-x <int> the desired width of the svg.'
     print '-y <int> the desired height of the svg.'
-    print '-l <title> title to use for the output graph'
     print '--default-setting <setting>[=<value>] setting for those without.'
     
 
@@ -83,7 +100,8 @@
     else:
         return latest_revision_found
 
-def parse_dir(directory, default_settings, oldest_revision, newest_revision):
+def parse_dir(directory, default_settings, oldest_revision, newest_revision,
+              rep):
     """Parses bench data from files like bench_r<revision>_<scalar>.
     
     (str, {str, str}, Number, Number) -> {int:[BenchDataPoints]}"""
@@ -92,25 +110,83 @@
         file_name_match = re.match('bench_r(\d+)_(\S+)', bench_file)
         if (file_name_match is None):
             continue
-        
+
         revision = int(file_name_match.group(1))
         scalar_type = file_name_match.group(2)
-        
+
         if (revision < oldest_revision or revision > newest_revision):
             continue
-        
+
         file_handle = open(directory + '/' + bench_file, 'r')
-        
+
         if (revision not in revision_data_points):
             revision_data_points[revision] = []
         default_settings['scalar'] = scalar_type
         revision_data_points[revision].extend(
-                        bench_util.parse(default_settings, file_handle))
+                        bench_util.parse(default_settings, file_handle, rep))
         file_handle.close()
     return revision_data_points
 
+def add_to_revision_data_points(new_point, revision, revision_data_points):
+    """Add new_point to set of revision_data_points we are building up.
+    """
+    if (revision not in revision_data_points):
+        revision_data_points[revision] = []
+    revision_data_points[revision].append(new_point)
+
+def filter_data_points(unfiltered_revision_data_points):
+    """Filter out any data points that are utterly bogus.
+
+    Returns (allowed_revision_data_points, ignored_revision_data_points):
+        allowed_revision_data_points: points that survived the filter
+        ignored_revision_data_points: points that did NOT survive the filter
+    """
+    allowed_revision_data_points = {} # {revision : [BenchDataPoints]}
+    ignored_revision_data_points = {} # {revision : [BenchDataPoints]}
+    revisions = unfiltered_revision_data_points.keys()
+    revisions.sort()
+    for revision in revisions:
+        for point in unfiltered_revision_data_points[revision]:
+            if point.time < MIN_REASONABLE_TIME or point.time > MAX_REASONABLE_TIME:
+                add_to_revision_data_points(point, revision, ignored_revision_data_points)
+            else:
+                add_to_revision_data_points(point, revision, allowed_revision_data_points)
+    return (allowed_revision_data_points, ignored_revision_data_points)
+
+def get_abs_path(relative_path):
+    """My own implementation of os.path.abspath() that better handles paths
+    which approach Window's 260-character limit.
+    See https://code.google.com/p/skia/issues/detail?id=674
+
+    This implementation adds path components one at a time, resolving the
+    absolute path each time, to take advantage of any chdirs into outer
+    directories that will shorten the total path length.
+
+    TODO: share a single implementation with upload_to_bucket.py, instead
+    of pasting this same code into both files."""
+    if os.path.isabs(relative_path):
+        return relative_path
+    path_parts = relative_path.split(os.sep)
+    abs_path = os.path.abspath('.')
+    for path_part in path_parts:
+        abs_path = os.path.abspath(os.path.join(abs_path, path_part))
+    return abs_path
+
+def redirect_stdout(output_path):
+    """Redirect all following stdout to a file.
+
+    You may be asking yourself, why redirect stdout within Python rather than
+    redirecting the script's output in the calling shell?
+    The answer lies in https://code.google.com/p/skia/issues/detail?id=674
+    ('buildbot: windows GenerateBenchGraphs step fails due to filename length'):
+    On Windows, we need to generate the absolute path within Python to avoid
+    the operating system's 260-character pathname limit, including chdirs."""
+    abs_path = get_abs_path(output_path)
+    sys.stdout = open(abs_path, 'w')
+
 def create_lines(revision_data_points, settings
-               , bench_of_interest, config_of_interest, time_of_interest):
+               , bench_of_interest, config_of_interest, time_of_interest
+               , time_to_ignore):
     """Convert revision data into sorted line data.
     
     ({int:[BenchDataPoints]}, {str:str}, str?, str?, str?)
@@ -131,6 +207,9 @@
             if (time_of_interest is not None and
                 not time_of_interest == point.time_type):
                 continue
+            elif (time_to_ignore is not None and
+                  time_to_ignore == point.time_type):
+                continue
             
             skip = False
             for key, value in settings.items():
@@ -205,7 +284,7 @@
     
     try:
         opts, _ = getopt.getopt(sys.argv[1:]
-                                 , "d:b:c:l:t:s:r:f:x:y:"
+                                 , "b:c:d:e:f:i:l:m:o:r:s:t:x:y:"
                                  , "default-setting=")
     except getopt.GetoptError, err:
         print str(err) 
@@ -216,6 +295,9 @@
     config_of_interest = None
     bench_of_interest = None
     time_of_interest = None
+    time_to_ignore = None
+    bench_expectations = {}
+    rep = None  # bench representation algorithm
     revision_range = '0:'
     regression_range = '0:'
     latest_revision = None
@@ -248,29 +330,74 @@
             settings[name] = True
         else:
             settings[name] = value
-        
+
+    def read_expectations(expectations, filename):
+        """Reads expectations data from file and put in expectations dict."""
+        for expectation in open(filename).readlines():
+            elements = expectation.strip().split(',')
+            if not elements[0] or elements[0].startswith('#'):
+                continue
+            if len(elements) != 5:
+                raise Exception("Invalid expectation line format: %s" %
+                                expectation)
+            bench_entry = elements[0] + ',' + elements[1]
+            if bench_entry in expectations:
+                raise Exception("Dup entries for bench expectation %s" %
+                                bench_entry)
+            # [<Bench_BmpConfig_TimeType>,<Platform-Alg>] -> (LB, UB)
+            expectations[bench_entry] = (float(elements[-2]),
+                                         float(elements[-1]))
+
+    def check_expectations(lines, expectations, newest_revision, key_suffix):
+        """Check if there are benches in latest rev outside expected range."""
+        exceptions = []
+        for line in lines:
+            line_str = str(line)
+            bench_platform_key = (line_str[ : line_str.find('_{')] + ',' +
+                key_suffix)
+            this_revision, this_bench_value = lines[line][-1]
+            if (this_revision != newest_revision or
+                bench_platform_key not in expectations):
+                # Skip benches without value for latest revision.
+                continue
+            this_min, this_max = expectations[bench_platform_key]
+            if this_bench_value < this_min or this_bench_value > this_max:
+                exceptions.append('Bench %s value %s out of range [%s, %s].' %
+                    (bench_platform_key, this_bench_value, this_min, this_max))
+        if exceptions:
+            raise Exception('Bench values out of range:\n' +
+                            '\n'.join(exceptions))
+
     try:
         for option, value in opts:
-            if option == "-d":
-                directory = value
-            elif option == "-b":
+            if option == "-b":
                 bench_of_interest = value
             elif option == "-c":
                 config_of_interest = value
-            elif option == "-t":
-                time_of_interest = value
-            elif option == "-s":
-                add_setting(settings, value)
-            elif option == "-r":
-                revision_range = value
+            elif option == "-d":
+                directory = value
+            elif option == "-e":
+                read_expectations(bench_expectations, value)
             elif option == "-f":
                 regression_range = value
+            elif option == "-i":
+                time_to_ignore = value
+            elif option == "-l":
+                title = value
+            elif option == "-m":
+                rep = value
+            elif option == "-o":
+                redirect_stdout(value)
+            elif option == "-r":
+                revision_range = value
+            elif option == "-s":
+                add_setting(settings, value)
+            elif option == "-t":
+                time_of_interest = value
             elif option == "-x":
                 requested_width = int(value)
             elif option == "-y":
                 requested_height = int(value)
-            elif option == "-l":
-                title = value
             elif option == "--default-setting":
                 add_setting(default_settings, value)
             else:
@@ -284,33 +411,57 @@
         usage()
         sys.exit(2)
 
+    if time_of_interest:
+        time_to_ignore = None
+
+    # The title flag (-l) provided in buildbot slave is in the format
+    # Bench_Performance_for_Skia_<platform>, and we want to extract <platform>
+    # for use in platform_and_alg to track matching benches later. If title flag
+    # is not in this format, there may be no matching benches in the file
+    # provided by the expectation_file flag (-e).
+    platform_and_alg = title
+    if platform_and_alg.startswith(TITLE_PREAMBLE):
+        platform_and_alg = (
+            platform_and_alg[TITLE_PREAMBLE_LENGTH:] + '-' + rep)
+    title += ' [representation: %s]' % rep
+
     latest_revision = get_latest_revision(directory)
     oldest_revision, newest_revision = parse_range(revision_range)
     oldest_regression, newest_regression = parse_range(regression_range)
 
-    revision_data_points = parse_dir(directory
+    unfiltered_revision_data_points = parse_dir(directory
                                    , default_settings
                                    , oldest_revision
-                                   , newest_revision)
+                                   , newest_revision
+                                   , rep)
+
+    # Filter out any data points that are utterly bogus... make sure to report
+    # that we did so later!
+    (allowed_revision_data_points, ignored_revision_data_points) = filter_data_points(
+        unfiltered_revision_data_points)
 
     # Update oldest_revision and newest_revision based on the data we could find
-    all_revision_numbers = revision_data_points.keys()
+    all_revision_numbers = allowed_revision_data_points.keys()
     oldest_revision = min(all_revision_numbers)
     newest_revision = max(all_revision_numbers)
 
-    lines = create_lines(revision_data_points
+    lines = create_lines(allowed_revision_data_points
                    , settings
                    , bench_of_interest
                    , config_of_interest
-                   , time_of_interest)
+                   , time_of_interest
+                   , time_to_ignore)
 
     regressions = create_regressions(lines
                                    , oldest_regression
                                    , newest_regression)
 
-    output_xhtml(lines, oldest_revision, newest_revision,
+    output_xhtml(lines, oldest_revision, newest_revision, ignored_revision_data_points,
                  regressions, requested_width, requested_height, title)
 
+    check_expectations(lines, bench_expectations, newest_revision,
+                       platform_and_alg)
+
 def qa(out):
     """Stringify input and quote as an xml attribute."""
     return xml.sax.saxutils.quoteattr(str(out))
@@ -340,19 +491,47 @@
         + ']') + '>'+qe(option)+'</option>'
     print '</select>'
 
-def output_xhtml(lines, oldest_revision, newest_revision,
+def output_ignored_data_points_warning(ignored_revision_data_points):
+    """Write description of ignored_revision_data_points to stdout as xhtml.
+    """
+    num_ignored_points = 0
+    description = ''
+    revisions = ignored_revision_data_points.keys()
+    if revisions:
+        revisions.sort()
+        revisions.reverse()
+        for revision in revisions:
+            num_ignored_points += len(ignored_revision_data_points[revision])
+            points_at_this_revision = []
+            for point in ignored_revision_data_points[revision]:
+                points_at_this_revision.append(point.bench)
+            points_at_this_revision.sort()
+            description += 'r%d: %s\n' % (revision, points_at_this_revision)
+    if num_ignored_points == 0:
+        print 'Did not discard any data points; all were within the range [%d-%d]' % (
+            MIN_REASONABLE_TIME, MAX_REASONABLE_TIME)
+    else:
+        print '<table width="100%" bgcolor="ff0000"><tr><td align="center">'
+        print 'Discarded %d data points outside of range [%d-%d]' % (
+            num_ignored_points, MIN_REASONABLE_TIME, MAX_REASONABLE_TIME)
+        print '</td></tr><tr><td width="100%" align="center">'
+        print ('<textarea rows="4" style="width:97%" readonly="true" wrap="off">'
+            + qe(description) + '</textarea>')
+        print '</td></tr></table>'
+
+def output_xhtml(lines, oldest_revision, newest_revision, ignored_revision_data_points,
                  regressions, requested_width, requested_height, title):
     """Outputs an svg/xhtml view of the data."""
     print '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"',
     print '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
     print '<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">'
     print '<head>'
-    print '<title>%s</title>' % title
+    print '<title>%s</title>' % qe(title)
     print '</head>'
     print '<body>'
     
     output_svg(lines, regressions, requested_width, requested_height)
-    
+
     #output the manipulation controls
     print """
 <script type="text/javascript">//<![CDATA[
@@ -436,28 +615,6 @@
     }
 //]]></script>"""
 
-    print '<table border="0" width="%s">' % requested_width
-    print """
-<form>
-<tr valign="bottom" align="center">
-<td width="1">Bench&nbsp;Type</td>
-<td width="1">Bitmap Config</td>
-<td width="1">Timer&nbsp;Type (Cpu/Gpu/wall)</td>
-<td width="1"><!--buttons--></td>
-<td width="10%"><!--spacing--></td>"""
-
-    print '<td>%s<br></br>revisions r%s - r%s</td>' % (
-        title,
-        bench_util.CreateRevisionLink(oldest_revision),
-        bench_util.CreateRevisionLink(newest_revision))
-    print '</tr><tr valign="top" align="center">'
-    print '<td width="1">'
-    create_select(lambda l: l.bench, lines, 'benchSelect')
-    print '</td><td width="1">'
-    create_select(lambda l: l.config, lines)
-    print '</td><td width="1">'
-    create_select(lambda l: l.time_type, lines)
-
     all_settings = {}
     variant_settings = set()
     for label in lines.keys():
@@ -467,18 +624,61 @@
             elif all_settings[key] != value:
                 variant_settings.add(key)
 
+    print '<table border="0" width="%s">' % requested_width
+    #output column headers
+    print """
+<tr valign="top"><td width="50%">
+<table border="0" width="100%">
+<tr><td align="center"><table border="0">
+<form>
+<tr valign="bottom" align="center">
+<td width="1">Bench&nbsp;Type</td>
+<td width="1">Bitmap Config</td>
+<td width="1">Timer&nbsp;Type (Cpu/Gpu/wall)</td>
+"""
+
     for k in variant_settings:
-        create_select(lambda l: l.settings[k], lines)
+        print '<td width="1">%s</td>' % qe(k)
+
+    print '<td width="1"><!--buttons--></td></tr>'
+
+    #output column contents
+    print '<tr valign="top" align="center">'
+    print '<td width="1">'
+    create_select(lambda l: l.bench, lines, 'benchSelect')
+    print '</td><td width="1">'
+    create_select(lambda l: l.config, lines)
+    print '</td><td width="1">'
+    create_select(lambda l: l.time_type, lines)
+
+    for k in variant_settings:
+        print '</td><td width="1">'
+        create_select(lambda l: l.settings.get(k, " "), lines)
 
     print '</td><td width="1"><button type="button"',
     print 'onclick=%s' % qa("mark('url(#circleMark)'); return false;"),
     print '>Mark Points</button>'
     print '<button type="button" onclick="mark(null);">Clear Points</button>'
-
+    print '</td>'
     print """
-</td>
-<td width="10%"></td>
-<td align="left">
+</tr>
+</form>
+</table></td></tr>
+<tr><td align="center">
+<hr />
+"""
+
+    output_ignored_data_points_warning(ignored_revision_data_points)
+    print '</td></tr></table>'
+    print '</td><td width="2%"><!--gutter--></td>'
+
+    print '<td><table border="0">'
+    print '<tr><td align="center">%s<br></br>revisions r%s - r%s</td></tr>' % (
+        qe(title),
+        bench_util.CreateRevisionLink(oldest_revision),
+        bench_util.CreateRevisionLink(newest_revision))
+    print """
+<tr><td align="left">
 <p>Brighter red indicates tests that have gotten worse; brighter green
 indicates tests that have gotten better.</p>
 <p>To highlight individual tests, hold down CONTROL and mouse over
@@ -489,9 +689,11 @@
 tests in the selectors at left.  (To show all, select all.)</p>
 <p>Use buttons at left to mark/clear points on the lines for selected
 benchmarks.</p>
+</td></tr>
+</table>
+
 </td>
 </tr>
-</form>
 </table>
 </body>
 </html>"""
@@ -553,6 +755,8 @@
         """Converts a time to a vertical display position."""
         return pic_height + ch(y - global_min_y)
     
+    print '<!--Picture height %.2f corresponds to bench value %.2f.-->' % (
+        pic_height, h)
     print '<svg',
     print 'width=%s' % qa(str(pic_width)+'px')
     print 'height=%s' % qa(str(pic_height)+'px')
diff --git a/bench/bench_util.py b/bench/bench_util.py
index e36e6c6..6d6839d 100644
--- a/bench/bench_util.py
+++ b/bench/bench_util.py
@@ -7,9 +7,15 @@
 import re
 import math
 
+# bench representation algorithm constant names
+ALGORITHM_AVERAGE = 'avg'
+ALGORITHM_MEDIAN = 'med'
+ALGORITHM_MINIMUM = 'min'
+ALGORITHM_25TH_PERCENTILE = '25th'
+
 class BenchDataPoint:
     """A single data point produced by bench.
-    
+
     (str, str, str, float, {str:str})"""
     def __init__(self, bench, config, time_type, time, settings):
         self.bench = bench
@@ -17,7 +23,7 @@
         self.time_type = time_type
         self.time = time
         self.settings = settings
-    
+
     def __repr__(self):
         return "BenchDataPoint(%s, %s, %s, %s, %s)" % (
                    str(self.bench),
@@ -26,41 +32,101 @@
                    str(self.time),
                    str(self.settings),
                )
-    
+
 class _ExtremeType(object):
     """Instances of this class compare greater or less than other objects."""
     def __init__(self, cmpr, rep):
         object.__init__(self)
         self._cmpr = cmpr
         self._rep = rep
-    
+
     def __cmp__(self, other):
         if isinstance(other, self.__class__) and other._cmpr == self._cmpr:
             return 0
         return self._cmpr
-    
+
     def __repr__(self):
         return self._rep
 
 Max = _ExtremeType(1, "Max")
 Min = _ExtremeType(-1, "Min")
 
-def parse(settings, lines):
+class _ListAlgorithm(object):
+    """Algorithm for selecting the representation value from a given list.
+    representation is one of the ALGORITHM_XXX representation types."""
+    def __init__(self, data, representation=None):
+        if not representation:
+            representation = ALGORITHM_AVERAGE  # default algorithm
+        self._data = data
+        self._len = len(data)
+        if representation == ALGORITHM_AVERAGE:
+            self._rep = sum(self._data) / self._len
+        else:
+            self._data.sort()
+            if representation == ALGORITHM_MINIMUM:
+                self._rep = self._data[0]
+            else:
+                # for percentiles, we use the value below which x% of values are
+                # found, which allows for better detection of quantum behaviors.
+                if representation == ALGORITHM_MEDIAN:
+                    x = int(round(0.5 * self._len + 0.5))
+                elif representation == ALGORITHM_25TH_PERCENTILE:
+                    x = int(round(0.25 * self._len + 0.5))
+                else:
+                    raise Exception("invalid representation algorithm %s!" %
+                                    representation)
+                self._rep = self._data[x - 1]
+
+    def compute(self):
+        return self._rep
+
+def _ParseAndStoreTimes(config_re, time_re, line, bench, dic,
+                        representation=None):
+    """Parses given bench time line with regex and adds data to the given dic.
+    config_re: regular expression for parsing the config line.
+    time_re: regular expression for parsing bench time.
+    line: input string line to parse.
+    bench: name of bench for the time values.
+    dic: dictionary to store bench values. See bench_dic in parse() below.
+    representation: should match one of the ALGORITHM_XXX types."""
+
+    for config in re.finditer(config_re, line):
+        current_config = config.group(1)
+        if config_re.startswith('  tile_'):  # per-tile bench, add name prefix
+            current_config = 'tile_' + current_config
+        times = config.group(2)
+        for new_time in re.finditer(time_re, times):
+            current_time_type = new_time.group(1)
+            iters = [float(i) for i in
+                     new_time.group(2).strip().split(',')]
+            dic.setdefault(bench, {}).setdefault(current_config, {}).setdefault(
+                current_time_type, []).append(_ListAlgorithm(
+                    iters, representation).compute())
+
+def parse(settings, lines, representation=None):
     """Parses bench output into a useful data structure.
-    
-    ({str:str}, __iter__ -> str) -> [BenchDataPoint]"""
-    
+
+    ({str:str}, __iter__ -> str) -> [BenchDataPoint]
+    representation is one of the ALGORITHM_XXX types."""
+
     benches = []
     current_bench = None
+    bench_dic = {}  # [bench][config][time_type] -> [list of bench values]
     setting_re = '([^\s=]+)(?:=(\S+))?'
     settings_re = 'skia bench:((?:\s+' + setting_re + ')*)'
     bench_re = 'running bench (?:\[\d+ \d+\] )?\s*(\S+)'
-    time_re = '(?:(\w*)msecs = )?\s*(\d+\.\d+)'
-    config_re = '(\S+): ((?:' + time_re + '\s+)+)'
-    
+    time_re = '(?:(\w*)msecs = )?\s*((?:\d+\.\d+)(?:,\d+\.\d+)*)'
+    # non-per-tile benches have configs that don't end with ']'
+    config_re = '(\S+[^\]]): ((?:' + time_re + '\s+)+)'
+    # per-tile bench lines are in the following format. Note that there are
+    # non-averaged bench numbers in separate lines, which we ignore now due to
+    # their inaccuracy.
+    tile_re = ('  tile_(\S+): tile \[\d+,\d+\] out of \[\d+,\d+\] <averaged>:'
+               ' ((?:' + time_re + '\s+)+)')
+
     for line in lines:
-        
-        #see if this line is a settings line
+
+        # see if this line is a settings line
         settingsMatch = re.search(settings_re, line)
         if (settingsMatch):
             settings = dict(settings)
@@ -69,39 +135,41 @@
                     settings[settingMatch.group(1)] = settingMatch.group(2)
                 else:
                     settings[settingMatch.group(1)] = True
-                
-        #see if this line starts a new bench
+
+        # see if this line starts a new bench
         new_bench = re.search(bench_re, line)
         if new_bench:
             current_bench = new_bench.group(1)
-        
-        #add configs on this line to the current bench
+
+        # add configs on this line to the bench_dic
         if current_bench:
-            for new_config in re.finditer(config_re, line):
-                current_config = new_config.group(1)
-                times = new_config.group(2)
-                for new_time in re.finditer(time_re, times):
-                    current_time_type = new_time.group(1)
-                    current_time = float(new_time.group(2))
-                    benches.append(BenchDataPoint(
-                            current_bench
-                            , current_config
-                            , current_time_type
-                            , current_time
-                            , settings))
-    
+            for regex in [config_re, tile_re]:
+                 _ParseAndStoreTimes(regex, time_re, line, current_bench,
+                                     bench_dic, representation)
+
+    # append benches to list, use the total time as final bench value.
+    for bench in bench_dic:
+        for config in bench_dic[bench]:
+            for time_type in bench_dic[bench][config]:
+                benches.append(BenchDataPoint(
+                    bench,
+                    config,
+                    time_type,
+                    sum(bench_dic[bench][config][time_type]),
+                    settings))
+
     return benches
-    
+
 class LinearRegression:
     """Linear regression data based on a set of data points.
-    
+
     ([(Number,Number)])
     There must be at least two points for this to make sense."""
     def __init__(self, points):
         n = len(points)
         max_x = Min
         min_x = Max
-        
+
         Sx = 0.0
         Sy = 0.0
         Sxx = 0.0
@@ -112,25 +180,29 @@
             y = point[1]
             max_x = max(max_x, x)
             min_x = min(min_x, x)
-            
+
             Sx += x
             Sy += y
             Sxx += x*x
             Sxy += x*y
             Syy += y*y
-        
-        B = (n*Sxy - Sx*Sy) / (n*Sxx - Sx*Sx)
+
+        denom = n*Sxx - Sx*Sx
+        if (denom != 0.0):
+            B = (n*Sxy - Sx*Sy) / denom
+        else:
+            B = 0.0
         a = (1.0/n)*(Sy - B*Sx)
-        
+
         se2 = 0
         sB2 = 0
         sa2 = 0
-        if (n >= 3):
-            se2 = (1.0/(n*(n-2)) * (n*Syy - Sy*Sy - B*B*(n*Sxx - Sx*Sx)))
-            sB2 = (n*se2) / (n*Sxx - Sx*Sx)
+        if (n >= 3 and denom != 0.0):
+            se2 = (1.0/(n*(n-2)) * (n*Syy - Sy*Sy - B*B*denom))
+            sB2 = (n*se2) / denom
             sa2 = sB2 * (1.0/n) * Sxx
-        
-        
+
+
         self.slope = B
         self.intercept = a
         self.serror = math.sqrt(max(0, se2))
@@ -138,7 +210,7 @@
         self.serror_intercept = math.sqrt(max(0, sa2))
         self.max_x = max_x
         self.min_x = min_x
-        
+
     def __repr__(self):
         return "LinearRegression(%s, %s, %s, %s, %s)" % (
                    str(self.slope),
@@ -147,7 +219,7 @@
                    str(self.serror_slope),
                    str(self.serror_intercept),
                )
-    
+
     def find_min_slope(self):
         """Finds the minimal slope given one standard deviation."""
         slope = self.slope
@@ -156,17 +228,17 @@
         regr_start = self.min_x
         regr_end = self.max_x
         regr_width = regr_end - regr_start
-        
+
         if slope < 0:
             lower_left_y = slope*regr_start + intercept - error
             upper_right_y = slope*regr_end + intercept + error
             return min(0, (upper_right_y - lower_left_y) / regr_width)
-        
+
         elif slope > 0:
             upper_left_y = slope*regr_start + intercept + error
             lower_right_y = slope*regr_end + intercept - error
             return max(0, (lower_right_y - upper_left_y) / regr_width)
-        
+
         return 0
 
 def CreateRevisionLink(revision_number):
@@ -176,3 +248,10 @@
     """
     return '<a href="http://code.google.com/p/skia/source/detail?r=%s">%s</a>'%(
         revision_number, revision_number)
+
+def main():
+    foo = [[0.0, 0.0], [0.0, 1.0], [0.0, 2.0], [0.0, 3.0]]
+    LinearRegression(foo)
+
+if __name__ == "__main__":
+    main()
diff --git a/bench/benchmain.cpp b/bench/benchmain.cpp
index 7732268..370530d 100644
--- a/bench/benchmain.cpp
+++ b/bench/benchmain.cpp
@@ -9,37 +9,44 @@
 
 #include "BenchTimer.h"
 
+#if SK_SUPPORT_GPU
 #include "GrContext.h"
 #include "GrRenderTarget.h"
-
-#include "SkBenchmark.h"
-#include "SkCanvas.h"
-#include "SkColorPriv.h"
-#include "SkGpuDevice.h"
-#include "SkGraphics.h"
-#include "SkImageEncoder.h"
+#if SK_ANGLE
+#include "gl/SkANGLEGLContext.h"
+#endif // SK_ANGLE
 #include "gl/SkNativeGLContext.h"
 #include "gl/SkNullGLContext.h"
+#include "gl/SkDebugGLContext.h"
+#include "SkGpuDevice.h"
+#endif // SK_SUPPORT_GPU
+
+#include "SkBenchLogger.h"
+#include "SkBenchmark.h"
+#include "SkCanvas.h"
+#include "SkDeferredCanvas.h"
+#include "SkDevice.h"
+#include "SkColorPriv.h"
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
 #include "SkNWayCanvas.h"
 #include "SkPicture.h"
 #include "SkString.h"
+#include "TimerData.h"
 
-#ifdef SK_BUILD_FOR_ANDROID
-static void log_error(const char msg[]) { SkDebugf("%s", msg); }
-static void log_progress(const char msg[]) { SkDebugf("%s", msg); }
-#else
-static void log_error(const char msg[]) { fprintf(stderr, "%s", msg); }
-static void log_progress(const char msg[]) { printf("%s", msg); }
-#endif
-
-static void log_error(const SkString& str) { log_error(str.c_str()); }
-static void log_progress(const SkString& str) { log_progress(str.c_str()); }
+enum benchModes {
+    kNormal_benchModes,
+    kDeferred_benchModes,
+    kDeferredSilent_benchModes,
+    kRecord_benchModes,
+    kPictureRecord_benchModes
+};
 
 ///////////////////////////////////////////////////////////////////////////////
 
 static void erase(SkBitmap& bm) {
     if (bm.config() == SkBitmap::kA8_Config) {
-        bm.eraseColor(0);
+        bm.eraseColor(SK_ColorTRANSPARENT);
     } else {
         bm.eraseColor(SK_ColorWHITE);
     }
@@ -52,7 +59,7 @@
         bm1.config() != bm2.config()) {
         return false;
     }
-    
+
     size_t pixelBytes = bm1.width() * bm1.bytesPerPixel();
     for (int y = 0; y < bm1.height(); y++) {
         if (memcmp(bm1.getAddr(0, y), bm2.getAddr(0, y), pixelBytes)) {
@@ -69,7 +76,7 @@
         fBench = BenchRegistry::Head();
         fParam = param;
     }
-    
+
     SkBenchmark* next() {
         if (fBench) {
             BenchRegistry::Factory f = fBench->factory();
@@ -84,6 +91,18 @@
     void* fParam;
 };
 
+class AutoPrePostDraw {
+public:
+    AutoPrePostDraw(SkBenchmark* bench) : fBench(bench) {
+        fBench->preDraw();
+    }
+    ~AutoPrePostDraw() {
+        fBench->postDraw();
+    }
+private:
+    SkBenchmark* fBench;
+};
+
 static void make_filename(const char name[], SkString* path) {
     path->set(name);
     for (int i = 0; name[i]; i++) {
@@ -106,7 +125,7 @@
     if (!bm.copyTo(&copy, SkBitmap::kARGB_8888_Config)) {
         return;
     }
-    
+
     if (bm.config() == SkBitmap::kA8_Config) {
         // turn alpha into gray-scale
         size_t size = copy.getSize() >> 2;
@@ -118,7 +137,7 @@
             *p++ = c | (SK_A32_MASK << SK_A32_SHIFT);
         }
     }
-    
+
     SkString str;
     make_filename(name, &str);
     str.appendf("_%s.png", config);
@@ -130,11 +149,11 @@
 
 static void performClip(SkCanvas* canvas, int w, int h) {
     SkRect r;
-    
+
     r.set(SkIntToScalar(10), SkIntToScalar(10),
           SkIntToScalar(w*2/3), SkIntToScalar(h*2/3));
     canvas->clipRect(r, SkRegion::kIntersect_Op);
-    
+
     r.set(SkIntToScalar(w/3), SkIntToScalar(h/3),
           SkIntToScalar(w-10), SkIntToScalar(h-10));
     canvas->clipRect(r, SkRegion::kXOR_Op);
@@ -143,7 +162,7 @@
 static void performRotate(SkCanvas* canvas, int w, int h) {
     const SkScalar x = SkIntToScalar(w) / 2;
     const SkScalar y = SkIntToScalar(h) / 2;
-    
+
     canvas->translate(x, y);
     canvas->rotate(SkIntToScalar(35));
     canvas->translate(-x, -y);
@@ -152,7 +171,7 @@
 static void performScale(SkCanvas* canvas, int w, int h) {
     const SkScalar x = SkIntToScalar(w) / 2;
     const SkScalar y = SkIntToScalar(h) / 2;
-    
+
     canvas->translate(x, y);
     // just enough so we can't take the sprite case
     canvas->scale(SK_Scalar1 * 99/100, SK_Scalar1 * 99/100);
@@ -173,6 +192,7 @@
     kPDF_Backend,
 };
 
+#if SK_SUPPORT_GPU
 class GLHelper {
 public:
     GLHelper() {
@@ -180,34 +200,37 @@
 
     bool init(SkGLContext* glCtx, int width, int height) {
         GrContext* grCtx;
-        GrRenderTarget* rt;
-        if (glCtx->init(width, height)) {
-            GrPlatform3DContext ctx =
-                reinterpret_cast<GrPlatform3DContext>(glCtx->gl());
-            grCtx = GrContext::Create(kOpenGL_Shaders_GrEngine, ctx);
-            if (NULL != grCtx) {
-                GrPlatformRenderTargetDesc desc;
-                desc.fConfig = kSkia8888_PM_GrPixelConfig;
-                desc.fWidth = width;
-                desc.fHeight = height;
-                desc.fStencilBits = 8;
-                desc.fRenderTargetHandle = glCtx->getFBOID();
-                rt = grCtx->createPlatformRenderTarget(desc);
-                if (NULL == rt) {
-                    grCtx->unref();
-                    return false;
-                }
-            }
-        } else {
+        if (!glCtx->init(width, height)) {
             return false;
         }
-        glCtx->ref();
-        fGLContext.reset(glCtx);
-        fGrContext.reset(grCtx);
-        fRenderTarget.reset(rt);
+        GrBackendContext ctx = reinterpret_cast<GrBackendContext>(glCtx->gl());
+        grCtx = GrContext::Create(kOpenGL_GrBackend, ctx);
+        if (NULL != grCtx) {
+            GrBackendRenderTargetDesc desc;
+            desc.fConfig = kSkia8888_PM_GrPixelConfig;
+            desc.fWidth = width;
+            desc.fHeight = height;
+            desc.fStencilBits = 8;
+            desc.fRenderTargetHandle = glCtx->getFBOID();
+            GrRenderTarget* rt = grCtx->wrapBackendRenderTarget(desc);
+            if (NULL == rt) {
+                grCtx->unref();
+                return false;
+            }
+            glCtx->ref();
+            fGLContext.reset(glCtx);
+            fGrContext.reset(grCtx);
+            fRenderTarget.reset(rt);
+        }
         return true;
     }
 
+    void cleanup() {
+        fGLContext.reset(NULL);
+        fGrContext.reset(NULL);
+        fRenderTarget.reset(NULL);
+    }
+
     bool isValid() {
         return NULL != fGLContext.get();
     }
@@ -231,23 +254,32 @@
 
 static GLHelper gRealGLHelper;
 static GLHelper gNullGLHelper;
-
+static GLHelper gDebugGLHelper;
+#if SK_ANGLE
+static GLHelper gANGLEGLHelper;
+#endif // SK_ANGLE
+#else  // !SK_SUPPORT_GPU
+class GLHelper;
+class SkGLContext;
+#endif // !SK_SUPPORT_GPU
 static SkDevice* make_device(SkBitmap::Config config, const SkIPoint& size,
                              Backend backend, GLHelper* glHelper) {
     SkDevice* device = NULL;
     SkBitmap bitmap;
     bitmap.setConfig(config, size.fX, size.fY);
-    
+
     switch (backend) {
         case kRaster_Backend:
             bitmap.allocPixels();
             erase(bitmap);
             device = new SkDevice(bitmap);
             break;
+#if SK_SUPPORT_GPU
         case kGPU_Backend:
             device = new SkGpuDevice(glHelper->grContext(),
                                      glHelper->renderTarget());
             break;
+#endif
         case kPDF_Backend:
         default:
             SkASSERT(!"unsupported");
@@ -263,8 +295,16 @@
 } gConfigs[] = {
     { SkBitmap::kARGB_8888_Config,  "8888",     kRaster_Backend, NULL },
     { SkBitmap::kRGB_565_Config,    "565",      kRaster_Backend, NULL },
+#if SK_SUPPORT_GPU
     { SkBitmap::kARGB_8888_Config,  "GPU",      kGPU_Backend, &gRealGLHelper },
+#if SK_ANGLE
+    { SkBitmap::kARGB_8888_Config,  "ANGLE",    kGPU_Backend, &gANGLEGLHelper },
+#endif // SK_ANGLE
+#ifdef SK_DEBUG
+    { SkBitmap::kARGB_8888_Config,  "Debug",    kGPU_Backend, &gDebugGLHelper },
+#endif // SK_DEBUG
     { SkBitmap::kARGB_8888_Config,  "NULLGPU",  kGPU_Backend, &gNullGLHelper },
+#endif // SK_SUPPORT_GPU
 };
 
 static int findConfig(const char config[]) {
@@ -289,6 +329,7 @@
         if (*contextHeight < dim.fY) {
             *contextHeight = dim.fY;
         }
+        bench->unref();
     }
 }
 
@@ -306,32 +347,95 @@
     return true;
 }
 
-int main (int argc, char * const argv[]) {
+static void help() {
+    SkDebugf("Usage: bench [-o outDir] [--repeat nr] [--logPerIter 1|0] "
+                          "[--timers [wcgWC]*] [--rotate]\n"
+             "    [--scale] [--clip] [--min] [--forceAA 1|0] [--forceFilter 1|0]\n"
+             "    [--forceDither 1|0] [--forceBlend 1|0] [--strokeWidth width]\n"
+             "    [--match name] [--mode normal|deferred|deferredSilent|record|picturerecord]\n"
+             "    [--config 8888|565|GPU|ANGLE|NULLGPU] [-Dfoo bar] [--logFile filename]\n"
+             "    [-h|--help]");
+    SkDebugf("\n\n");
+    SkDebugf("    -o outDir : Image of each bench will be put in outDir.\n");
+    SkDebugf("    --repeat nr : Each bench repeats for nr times.\n");
+    SkDebugf("    --logPerIter 1|0 : "
+             "Log each repeat timer instead of mean, default is disabled.\n");
+    SkDebugf("    --timers [wcgWC]* : "
+             "Display wall, cpu, gpu, truncated wall or truncated cpu time for each bench.\n");
+    SkDebugf("    --rotate : Rotate before each bench runs.\n");
+    SkDebugf("    --scale : Scale before each bench runs.\n");
+    SkDebugf("    --clip : Clip before each bench runs.\n");
+    SkDebugf("    --min : Print the minimum times (instead of average).\n");
+    SkDebugf("    --forceAA 1|0 : "
+             "Enable/disable anti-aliased, default is enabled.\n");
+    SkDebugf("    --forceFilter 1|0 : "
+             "Enable/disable bitmap filtering, default is disabled.\n");
+    SkDebugf("    --forceDither 1|0 : "
+             "Enable/disable dithering, default is disabled.\n");
+    SkDebugf("    --forceBlend 1|0 : "
+             "Enable/disable dithering, default is disabled.\n");
+    SkDebugf("    --strokeWidth width : The width for path stroke.\n");
+    SkDebugf("    --match name : Only run bench whose name is matched.\n");
+    SkDebugf("    --mode normal|deferred|deferredSilent|record|picturerecord :\n"
+             "             Run in the corresponding mode\n"
+             "                 normal, Use a normal canvas to draw to;\n"
+             "                 deferred, Use a deferrred canvas when drawing;\n"
+             "                 deferredSilent, deferred with silent playback;\n"
+             "                 record, Benchmark the time to record to an SkPicture;\n"
+             "                 picturerecord, Benchmark the time to do record from a \n"
+             "                                SkPicture to a SkPicture.\n");
+    SkDebugf("    --logFile filename : destination for writing log output, in addition to stdout.\n");
+#if SK_SUPPORT_GPU
+    SkDebugf("    --config 8888|565|GPU|ANGLE|NULLGPU : "
+             "Run bench in corresponding config mode.\n");
+#else
+    SkDebugf("    --config 8888|565: "
+             "Run bench in corresponding config mode.\n");
+#endif
+    SkDebugf("    -Dfoo bar : Add extra definition to bench.\n");
+    SkDebugf("    -h|--help : Show this help message.\n");
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+#if SK_ENABLE_INST_COUNT
+    gPrintInstCount = true;
+#endif
     SkAutoGraphics ag;
-    
+
     SkTDict<const char*> defineDict(1024);
     int repeatDraw = 1;
+    bool logPerIter = false;
     int forceAlpha = 0xFF;
     bool forceAA = true;
     bool forceFilter = false;
     SkTriState::State forceDither = SkTriState::kDefault;
     bool timerWall = false;
+    bool truncatedTimerWall = false;
     bool timerCpu = true;
+    bool truncatedTimerCpu = false;
     bool timerGpu = true;
     bool doScale = false;
     bool doRotate = false;
     bool doClip = false;
+    bool printMin = false;
     bool hasStrokeWidth = false;
     float strokeWidth;
     SkTDArray<const char*> fMatches;
-    
+    benchModes benchMode = kNormal_benchModes;
+    SkString perIterTimeformat("%.2f");
+    SkString normalTimeFormat("%6.2f");
+
     SkString outDir;
     SkBitmap::Config outConfig = SkBitmap::kNo_Config;
     GLHelper* glHelper = NULL;
     const char* configName = "";
     Backend backend = kRaster_Backend;  // for warning
-    int configCount = SK_ARRAY_COUNT(gConfigs);
-    
+    SkTDArray<int> configs;
+    bool userConfig = false;
+
+    SkBenchLogger logger;
+
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
         if (strcmp(*argv, "-o") == 0) {
@@ -342,7 +446,7 @@
                     outDir.append("/");
                 }
             }
-        } else if (strcmp(*argv, "-repeat") == 0) {
+        } else if (strcmp(*argv, "--repeat") == 0) {
             argv++;
             if (argv < stop) {
                 repeatDraw = atoi(*argv);
@@ -350,95 +454,152 @@
                     repeatDraw = 1;
                 }
             } else {
-                log_error("missing arg for -repeat\n");
+                logger.logError("missing arg for --repeat\n");
+                help();
                 return -1;
             }
-        } else if (strcmp(*argv, "-timers") == 0) {
+        } else if (strcmp(*argv, "--logPerIter") == 0) {
+            if (!parse_bool_arg(++argv, stop, &logPerIter)) {
+                logger.logError("missing arg for --logPerIter\n");
+                help();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--timers") == 0) {
             argv++;
             if (argv < stop) {
                 timerWall = false;
+                truncatedTimerWall = false;
                 timerCpu = false;
+                truncatedTimerCpu = false;
                 timerGpu = false;
                 for (char* t = *argv; *t; ++t) {
                     switch (*t) {
                     case 'w': timerWall = true; break;
                     case 'c': timerCpu = true; break;
+                    case 'W': truncatedTimerWall = true; break;
+                    case 'C': truncatedTimerCpu = true; break;
                     case 'g': timerGpu = true; break;
                     }
                 }
             } else {
-                log_error("missing arg for -timers\n");
+                logger.logError("missing arg for --timers\n");
+                help();
                 return -1;
             }
-        } else if (!strcmp(*argv, "-rotate")) {
+        } else if (!strcmp(*argv, "--rotate")) {
             doRotate = true;
-        } else if (!strcmp(*argv, "-scale")) {
+        } else if (!strcmp(*argv, "--scale")) {
             doScale = true;
-        } else if (!strcmp(*argv, "-clip")) {
+        } else if (!strcmp(*argv, "--clip")) {
             doClip = true;
-        } else if (strcmp(*argv, "-forceAA") == 0) {
+        } else if (!strcmp(*argv, "--min")) {
+            printMin = true;
+        } else if (strcmp(*argv, "--forceAA") == 0) {
             if (!parse_bool_arg(++argv, stop, &forceAA)) {
-                log_error("missing arg for -forceAA\n");
+                logger.logError("missing arg for --forceAA\n");
+                help();
                 return -1;
             }
-        } else if (strcmp(*argv, "-forceFilter") == 0) {
+        } else if (strcmp(*argv, "--forceFilter") == 0) {
             if (!parse_bool_arg(++argv, stop, &forceFilter)) {
-                log_error("missing arg for -forceFilter\n");
+                logger.logError("missing arg for --forceFilter\n");
+                help();
                 return -1;
             }
-        } else if (strcmp(*argv, "-forceDither") == 0) {
+        } else if (strcmp(*argv, "--forceDither") == 0) {
             bool tmp;
             if (!parse_bool_arg(++argv, stop, &tmp)) {
-                log_error("missing arg for -forceDither\n");
+                logger.logError("missing arg for --forceDither\n");
+                help();
                 return -1;
             }
             forceDither = tmp ? SkTriState::kTrue : SkTriState::kFalse;
-        } else if (strcmp(*argv, "-forceBlend") == 0) {
+        } else if (strcmp(*argv, "--forceBlend") == 0) {
             bool wantAlpha = false;
             if (!parse_bool_arg(++argv, stop, &wantAlpha)) {
-                log_error("missing arg for -forceBlend\n");
+                logger.logError("missing arg for --forceBlend\n");
+                help();
                 return -1;
             }
             forceAlpha = wantAlpha ? 0x80 : 0xFF;
-        } else if (strcmp(*argv, "-strokeWidth") == 0) {
+        } else if (strcmp(*argv, "--mode") == 0) {
+            argv++;
+            if (argv < stop) {
+                if (strcmp(*argv, "normal") == 0) {
+                    benchMode = kNormal_benchModes;
+                } else if (strcmp(*argv, "deferred") == 0) {
+                    benchMode = kDeferred_benchModes;
+                } else if (strcmp(*argv, "deferredSilent") == 0) {
+                    benchMode = kDeferredSilent_benchModes;
+                } else if (strcmp(*argv, "record") == 0) {
+                    benchMode = kRecord_benchModes;
+                } else if (strcmp(*argv, "picturerecord") == 0) {
+                    benchMode = kPictureRecord_benchModes;
+                } else {
+                    logger.logError("bad arg for --mode\n");
+                    help();
+                    return -1;
+                }
+            } else {
+                logger.logError("missing arg for --mode\n");
+                help();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--strokeWidth") == 0) {
             argv++;
             if (argv < stop) {
                 const char *strokeWidthStr = *argv;
                 if (sscanf(strokeWidthStr, "%f", &strokeWidth) != 1) {
-                  log_error("bad arg for -strokeWidth\n");
+                  logger.logError("bad arg for --strokeWidth\n");
+                  help();
                   return -1;
                 }
                 hasStrokeWidth = true;
             } else {
-                log_error("missing arg for -strokeWidth\n");
+                logger.logError("missing arg for --strokeWidth\n");
+                help();
                 return -1;
             }
-        } else if (strcmp(*argv, "-match") == 0) {
+        } else if (strcmp(*argv, "--match") == 0) {
             argv++;
             if (argv < stop) {
                 *fMatches.append() = *argv;
             } else {
-                log_error("missing arg for -match\n");
+                logger.logError("missing arg for --match\n");
+                help();
                 return -1;
             }
-        } else if (strcmp(*argv, "-config") == 0) {
+        } else if (strcmp(*argv, "--config") == 0) {
             argv++;
             if (argv < stop) {
                 int index = findConfig(*argv);
                 if (index >= 0) {
-                    outConfig = gConfigs[index].fConfig;
-                    configName = gConfigs[index].fName;
-                    backend = gConfigs[index].fBackend;
-                    glHelper = gConfigs[index].fGLHelper;
-                    configCount = 1;
+                    *configs.append() = index;
+                    userConfig = true;
                 } else {
                     SkString str;
                     str.printf("unrecognized config %s\n", *argv);
-                    log_error(str);
+                    logger.logError(str);
+                    help();
                     return -1;
                 }
             } else {
-                log_error("missing arg for -config\n");
+                logger.logError("missing arg for --config\n");
+                help();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--logFile") == 0) {
+            argv++;
+            if (argv < stop) {
+                if (!logger.SetLogFile(*argv)) {
+                    SkString str;
+                    str.printf("Could not open %s for writing.", *argv);
+                    logger.logError(str);
+                    return -1;
+                }
+            } else {
+                logger.logError("missing arg for --logFile\n");
+                help();
                 return -1;
             }
         } else if (strlen(*argv) > 2 && strncmp(*argv, "-D", 2) == 0) {
@@ -446,25 +607,52 @@
             if (argv < stop) {
                 defineDict.set(argv[-1] + 2, *argv);
             } else {
-                log_error("incomplete '-Dfoo bar' definition\n");
+                logger.logError("incomplete '-Dfoo bar' definition\n");
+                help();
                 return -1;
             }
+        } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
+            help();
+            return 0;
         } else {
             SkString str;
             str.printf("unrecognized arg %s\n", *argv);
-            log_error(str);
+            logger.logError(str);
+            help();
             return -1;
         }
     }
-    
+    if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)
+            && !outDir.isEmpty()) {
+        logger.logError("'--mode record' and '--mode picturerecord' are not"
+                  " compatible with -o.\n");
+        return -1;
+    }
+    if ((benchMode == kRecord_benchModes || benchMode == kPictureRecord_benchModes)) {
+        perIterTimeformat.set("%.4f");
+        normalTimeFormat.set("%6.4f");
+    }
+    if (!userConfig) {
+        // if no config is specified by user, we add them all.
+        for (unsigned int i = 0; i < SK_ARRAY_COUNT(gConfigs); ++i) {
+            *configs.append() = i;
+        }
+    }
+
     // report our current settings
     {
         SkString str;
-        str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d",
-                   forceAlpha, forceAA, forceFilter);
-        str.appendf(" rotate=%d scale=%d clip=%d",
-                   doRotate, doScale, doClip);
-                   
+        const char* deferredMode = benchMode == kDeferred_benchModes ? "yes" :
+            (benchMode == kDeferredSilent_benchModes ? "silent" : "no");
+        str.printf("skia bench: alpha=0x%02X antialias=%d filter=%d "
+                   "deferred=%s logperiter=%d",
+                   forceAlpha, forceAA, forceFilter, deferredMode,
+                   logPerIter);
+        str.appendf(" rotate=%d scale=%d clip=%d min=%d",
+                   doRotate, doScale, doClip, printMin);
+        str.appendf(" record=%d picturerecord=%d",
+                    benchMode == kRecord_benchModes,
+                    benchMode == kPictureRecord_benchModes);
         const char * ditherName;
         switch (forceDither) {
             case SkTriState::kDefault: ditherName = "default"; break;
@@ -473,13 +661,13 @@
             default: ditherName = "<invalid>"; break;
         }
         str.appendf(" dither=%s", ditherName);
-        
+
         if (hasStrokeWidth) {
             str.appendf(" strokeWidth=%f", strokeWidth);
         } else {
             str.append(" strokeWidth=none");
         }
-        
+
 #if defined(SK_SCALAR_IS_FLOAT)
         str.append(" scalar=float");
 #elif defined(SK_SCALAR_IS_FIXED)
@@ -502,29 +690,39 @@
         str.append(" DEBUG");
 #endif
         str.append("\n");
-        log_progress(str);
+        logger.logProgress(str);
     }
 
+    SkGLContext* timerCtx = NULL;
     //Don't do GL when fixed.
-#if !defined(SK_SCALAR_IS_FIXED)
+#if !defined(SK_SCALAR_IS_FIXED) && SK_SUPPORT_GPU
     int contextWidth = 1024;
     int contextHeight = 1024;
     determine_gpu_context_size(defineDict, &contextWidth, &contextHeight);
     SkAutoTUnref<SkGLContext> realGLCtx(new SkNativeGLContext);
     SkAutoTUnref<SkGLContext> nullGLCtx(new SkNullGLContext);
+    SkAutoTUnref<SkGLContext> debugGLCtx(new SkDebugGLContext);
     gRealGLHelper.init(realGLCtx.get(), contextWidth, contextHeight);
     gNullGLHelper.init(nullGLCtx.get(), contextWidth, contextHeight);
-#endif
-    BenchTimer timer = BenchTimer(gRealGLHelper.glContext());
+    gDebugGLHelper.init(debugGLCtx.get(), contextWidth, contextHeight);
+#if SK_ANGLE
+    SkAutoTUnref<SkGLContext> angleGLCtx(new SkANGLEGLContext);
+    gANGLEGLHelper.init(angleGLCtx.get(), contextWidth, contextHeight);
+#endif // SK_ANGLE
+    timerCtx = gRealGLHelper.glContext();
+#endif // !defined(SK_SCALAR_IS_FIXED) && SK_SUPPORT_GPU
 
+    BenchTimer timer = BenchTimer(timerCtx);
     Iter iter(&defineDict);
     SkBenchmark* bench;
     while ((bench = iter.next()) != NULL) {
+        SkAutoTUnref<SkBenchmark> benchUnref(bench);
+
         SkIPoint dim = bench->getSize();
         if (dim.fX <= 0 || dim.fY <= 0) {
             continue;
         }
-        
+
         bench->setForceAlpha(forceAlpha);
         bench->setForceAA(forceAA);
         bench->setForceFilter(forceFilter);
@@ -532,90 +730,191 @@
         if (hasStrokeWidth) {
             bench->setStrokeWidth(strokeWidth);
         }
-        
+
         // only run benchmarks if their name contains matchStr
         if (skip_name(fMatches, bench->getName())) {
             continue;
         }
-        
+
         {
             SkString str;
             str.printf("running bench [%d %d] %28s", dim.fX, dim.fY,
                        bench->getName());
-            log_progress(str);
+            logger.logProgress(str);
         }
-        
-        for (int configIndex = 0; configIndex < configCount; configIndex++) {
-            if (configCount > 1) {
-                outConfig = gConfigs[configIndex].fConfig;
-                configName = gConfigs[configIndex].fName;
-                backend = gConfigs[configIndex].fBackend;
-                glHelper = gConfigs[configIndex].fGLHelper;
-            }
 
+        AutoPrePostDraw appd(bench);
+
+        bool runOnce = false;
+        for (int x = 0; x < configs.count(); ++x) {
+            if (!bench->isRendering() && runOnce) {
+                continue;
+            }
+            runOnce = true;
+
+            int configIndex = configs[x];
+
+            outConfig = gConfigs[configIndex].fConfig;
+            configName = gConfigs[configIndex].fName;
+            backend = gConfigs[configIndex].fBackend;
+            glHelper = gConfigs[configIndex].fGLHelper;
+
+#if SK_SUPPORT_GPU
             if (kGPU_Backend == backend &&
                 (NULL == glHelper || !glHelper->isValid())) {
                 continue;
             }
-            
+#endif
             SkDevice* device = make_device(outConfig, dim, backend, glHelper);
-            SkCanvas canvas(device);
+            SkCanvas* canvas = NULL;
+            SkPicture pictureRecordFrom;
+            SkPicture pictureRecordTo;
+            switch(benchMode) {
+                case kDeferredSilent_benchModes:
+                case kDeferred_benchModes:
+                    canvas = new SkDeferredCanvas(device);
+                    break;
+                case kRecord_benchModes:
+                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
+                    canvas->ref();
+                    break;
+                case kPictureRecord_benchModes: {
+                    // This sets up picture-to-picture recording.
+                    // The C++ drawing calls for the benchmark are recorded into
+                    // pictureRecordFrom. As the benchmark, we will time how
+                    // long it takes to playback pictureRecordFrom into
+                    // pictureRecordTo.
+                    SkCanvas* tempCanvas = pictureRecordFrom.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
+                    bench->draw(tempCanvas);
+                    pictureRecordFrom.endRecording();
+                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
+                    canvas->ref();
+                    break;
+                }
+                case kNormal_benchModes:
+                    canvas = new SkCanvas(device);
+                    break;
+                default:
+                    SkASSERT(0);
+            }
             device->unref();
-            
+            SkAutoUnref canvasUnref(canvas);
+
             if (doClip) {
-                performClip(&canvas, dim.fX, dim.fY);
+                performClip(canvas, dim.fX, dim.fY);
             }
             if (doScale) {
-                performScale(&canvas, dim.fX, dim.fY);
+                performScale(canvas, dim.fX, dim.fY);
             }
             if (doRotate) {
-                performRotate(&canvas, dim.fX, dim.fY);
+                performRotate(canvas, dim.fX, dim.fY);
             }
 
-            //warm up caches if needed
+            // warm up caches if needed
             if (repeatDraw > 1) {
-                SkAutoCanvasRestore acr(&canvas, true);
-                bench->draw(&canvas);
+#if SK_SUPPORT_GPU
+                if (glHelper) {
+                    // purge the GPU resources to reduce variance
+                    glHelper->grContext()->freeGpuResources();
+                }
+#endif
+                SkAutoCanvasRestore acr(canvas, true);
+                if (benchMode == kPictureRecord_benchModes) {
+                    pictureRecordFrom.draw(canvas);
+                } else {
+                    bench->draw(canvas);
+                }
+
+                if (kDeferredSilent_benchModes == benchMode) {
+                    static_cast<SkDeferredCanvas*>(canvas)->silentFlush();
+                } else {
+                    canvas->flush();
+                }
+#if SK_SUPPORT_GPU
                 if (glHelper) {
                     glHelper->grContext()->flush();
                     SK_GL(*glHelper->glContext(), Finish());
                 }
+#endif
             }
-            
-            timer.start();
+
+            // record timer values for each repeat, and their sum
+            TimerData timerData(perIterTimeformat, normalTimeFormat);
             for (int i = 0; i < repeatDraw; i++) {
-                SkAutoCanvasRestore acr(&canvas, true);
-                bench->draw(&canvas);
+                if ((benchMode == kRecord_benchModes
+                     || benchMode == kPictureRecord_benchModes)) {
+                    // This will clear the recorded commands so that they do not
+                    // acculmulate.
+                    canvas = pictureRecordTo.beginRecording(dim.fX, dim.fY,
+                        SkPicture::kUsePathBoundsForClip_RecordingFlag);
+                }
+
+                timer.start();
+                SkAutoCanvasRestore acr(canvas, true);
+                if (benchMode == kPictureRecord_benchModes) {
+                    pictureRecordFrom.draw(canvas);
+                } else {
+                    bench->draw(canvas);
+                }
+
+                if (kDeferredSilent_benchModes == benchMode) {
+                    static_cast<SkDeferredCanvas*>(canvas)->silentFlush();
+                } else {
+                    canvas->flush();
+                }
+
+                // stop the truncated timer after the last canvas call but
+                // don't wait for all the GL calls to complete
+                timer.truncatedEnd();
+#if SK_SUPPORT_GPU
                 if (glHelper) {
                     glHelper->grContext()->flush();
+                    SK_GL(*glHelper->glContext(), Finish());
                 }
+#endif
+                // stop the inclusive and gpu timers once all the GL calls
+                // have completed
+                timer.end();
+
+                timerData.appendTimes(&timer, repeatDraw - 1 == i);
+
             }
-           if (glHelper) {
-                SK_GL(*glHelper->glContext(), Finish());
-           }
-           timer.end();
-            
             if (repeatDraw > 1) {
-                SkString str;
-                str.printf("  %4s:", configName);
-                if (timerWall) {
-                    str.appendf(" msecs = %6.2f", timer.fWall / repeatDraw);
-                }
-                if (timerCpu) {
-                    str.appendf(" cmsecs = %6.2f", timer.fCpu / repeatDraw);
-                }
-                if (timerGpu && glHelper && timer.fGpu > 0) {
-                    str.appendf(" gmsecs = %6.2f", timer.fGpu / repeatDraw);
-                }
-                log_progress(str);
+                SkString result = timerData.getResult(logPerIter, printMin, repeatDraw, configName,
+                                                      timerWall, truncatedTimerWall, timerCpu,
+                                                      truncatedTimerCpu, timerGpu && glHelper);
+                logger.logProgress(result);
             }
             if (outDir.size() > 0) {
                 saveFile(bench->getName(), configName, outDir.c_str(),
                          device->accessBitmap(false));
+                canvas->clear(SK_ColorWHITE);
             }
         }
-        log_progress("\n");
+        logger.logProgress(SkString("\n"));
     }
+#if SK_SUPPORT_GPU
+#if GR_CACHE_STATS
+    gRealGLHelper.grContext()->printCacheStats();
+#endif
+
+    // need to clean up here rather than post-main to allow leak detection to work
+    gRealGLHelper.cleanup();
+    gDebugGLHelper.cleanup();
+    gNullGLHelper.cleanup();
+#if SK_ANGLE
+    gANGLEGLHelper.cleanup();
+#endif // SK_ANGLE
+#endif
 
     return 0;
 }
+
+#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/bench/gen_skp_ranges.py b/bench/gen_skp_ranges.py
new file mode 100755
index 0000000..575db37
--- /dev/null
+++ b/bench/gen_skp_ranges.py
@@ -0,0 +1,159 @@
+#!/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.
+
+""" Analyze recent SkPicture bench data, and output suggested ranges.
+
+The outputs can be edited and pasted to bench_expectations.txt to trigger
+buildbot alerts if the actual benches are out of range. Details are documented
+in the .txt file.
+
+Currently the easiest way to update bench_expectations.txt is to delete all skp
+bench lines, run this script, and redirect outputs (">>") to be added to the
+.txt file.
+TODO(bensong): find a better way for updating the bench lines in place.
+
+Note: since input data are stored in Google Storage, you will need to set up
+the corresponding library.
+See http://developers.google.com/storage/docs/gspythonlibrary for details.
+"""
+
+__author__ = 'bensong@google.com (Ben Chen)'
+
+import bench_util
+import boto
+import cStringIO
+import optparse
+import re
+import shutil
+
+from oauth2_plugin import oauth2_plugin
+
+
+# Ratios for calculating suggested picture bench upper and lower bounds.
+BENCH_UB = 1.1  # Allow for 10% room for normal variance on the up side.
+BENCH_LB = 0.85
+
+# Further allow for a fixed amount of noise. This is especially useful for
+# benches of smaller absolute value. Keeping this value small will not affect
+# performance tunings.
+BENCH_ALLOWED_NOISE = 10
+
+# List of platforms to track.
+PLATFORMS = ['Mac_Float_Bench_32',
+             'Nexus10_4-1_Float_Bench_32',
+             'Shuttle_Ubuntu12_ATI5770_Float_Bench_32',
+            ]
+
+# Filter for configs of no interest. They are old config names replaced by more
+# specific ones.
+CONFIGS_TO_FILTER = ['gpu', 'raster']
+
+# Template for gsutil uri.
+GOOGLE_STORAGE_URI_SCHEME = 'gs'
+URI_BUCKET = 'chromium-skia-gm'
+
+# Constants for optparse.
+USAGE_STRING = 'USAGE: %s [options]'
+HOWTO_STRING = """
+Feel free to revise PLATFORMS for your own needs. The default is the most common
+combination that we care most about. Platforms that did not run bench_pictures
+in the given revision range will not have corresponding outputs.
+Please check http://go/skpbench to choose a range that fits your needs.
+BENCH_UB, BENCH_LB and BENCH_ALLOWED_NOISE can be changed to expand or narrow
+the permitted bench ranges without triggering buidbot alerts.
+"""
+HELP_STRING = """
+Outputs expectation picture bench ranges for the latest revisions for the given
+revision range. For instance, --rev_range=6000:6000 will return only bench
+ranges for the bots that ran bench_pictures at rev 6000; --rev-range=6000:7000
+may have multiple bench data points for each bench configuration, and the code
+returns bench data for the latest revision of all available (closer to 7000).
+""" + HOWTO_STRING
+
+OPTION_REVISION_RANGE = '--rev-range'
+OPTION_REVISION_RANGE_SHORT = '-r'
+# Bench bench representation algorithm flag.
+OPTION_REPRESENTATION_ALG = '--algorithm'
+OPTION_REPRESENTATION_ALG_SHORT = '-a'
+
+# List of valid representation algorithms.
+REPRESENTATION_ALGS = ['avg', 'min', 'med', '25th']
+
+def OutputSkpBenchExpectations(rev_min, rev_max, representation_alg):
+  """Reads skp bench data from google storage, and outputs expectations.
+
+  Ignores data with revisions outside [rev_min, rev_max] integer range. For
+  bench data with multiple revisions, we use higher revisions to calculate
+  expected bench values.
+  Uses the provided representation_alg for calculating bench representations.
+  """
+  expectation_dic = {}
+  uri = boto.storage_uri(URI_BUCKET, GOOGLE_STORAGE_URI_SCHEME)
+  for obj in uri.get_bucket():
+    # Filters out non-skp-bench files.
+    if (not obj.name.startswith('perfdata/Skia_') or
+        obj.name.find('_data_skp_') < 0):
+      continue
+    # Ignores uninterested platforms.
+    platform = obj.name.split('/')[1][5:]  # Removes "Skia_" prefix.
+    if platform not in PLATFORMS:
+      continue
+    # Filters by revision.
+    for rev in range(rev_min, rev_max + 1):
+      if '_r%s_' % rev not in obj.name:
+        continue
+
+    contents = cStringIO.StringIO()
+    obj.get_file(contents)
+    for point in bench_util.parse('', contents.getvalue().split('\n'),
+                                  representation_alg):
+      if point.config in CONFIGS_TO_FILTER:
+        continue
+      # TODO(bensong): the filtering below is only needed during skp generation
+      # system transitioning. Change it once the new system (bench name starts
+      # with http) is stable for the switch-over, and delete it once we
+      # deprecate the old ones.
+      if point.bench.startswith('http'):
+        continue
+
+      key = '%s_%s_%s,%s-%s' % (point.bench, point.config, point.time_type,
+                                platform, representation_alg)
+      # It is fine to have later revisions overwrite earlier benches, since we
+      # only use the latest bench within revision range to set expectations.
+      expectation_dic[key] = point.time
+  keys = expectation_dic.keys()
+  keys.sort()
+  for key in keys:
+    bench_val = expectation_dic[key]
+    # Prints out expectation lines.
+    print '%s,%.3f,%.3f,%.3f' % (key, bench_val,
+                                 bench_val * BENCH_LB - BENCH_ALLOWED_NOISE,
+                                 bench_val * BENCH_UB + BENCH_ALLOWED_NOISE)
+
+def main():
+  """Parses flags and outputs expected Skia picture bench results."""
+  parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
+  parser.add_option(OPTION_REVISION_RANGE_SHORT, OPTION_REVISION_RANGE,
+      dest='rev_range',
+      help='(Mandatory) revision range separated by ":", e.g., 6000:6005')
+  parser.add_option(OPTION_REPRESENTATION_ALG_SHORT, OPTION_REPRESENTATION_ALG,
+      dest='alg', default='25th',
+      help=('Bench representation algorithm. One of '
+            '%s. Default to "25th".' % str(REPRESENTATION_ALGS)))
+  (options, args) = parser.parse_args()
+  if options.rev_range:
+    range_match = re.search('(\d+)\:(\d+)', options.rev_range)
+    if not range_match:
+      parser.error('Wrong format for rev-range [%s]' % options.rev_range)
+    else:
+      rev_min = int(range_match.group(1))
+      rev_max = int(range_match.group(2))
+      OutputSkpBenchExpectations(rev_min, rev_max, options.alg)
+  else:
+    parser.error('Please provide mandatory flag %s' % OPTION_REVISION_RANGE)
+
+
+if '__main__' == __name__:
+  main()
diff --git a/emoji/EmojiFont.cpp b/emoji/EmojiFont.cpp
index 9436f26..7590382 100644
--- a/emoji/EmojiFont.cpp
+++ b/emoji/EmojiFont.cpp
@@ -28,6 +28,7 @@
 
 #include "EmojiFactory.h"
 #include "EmojiFont.h"
+#include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkImageDecoder.h"
 #include "SkPaint.h"
diff --git a/gm/Android.mk b/gm/Android.mk
index 8dcb0ef..9f944ec 100644
--- a/gm/Android.mk
+++ b/gm/Android.mk
@@ -5,60 +5,101 @@
 LOCAL_SRC_FILES := \
   gm.cpp \
   gmmain.cpp \
-  system_preferences_default.cpp
+  system_preferences_default.cpp \
+  ../src/pipe/utils/SamplePipeControllers.cpp
 
 # Slides
 LOCAL_SRC_FILES += \
   aaclip.cpp \
   aarectmodes.cpp \
   arithmode.cpp \
+  bicubicfilter.cpp \
+  bigmatrix.cpp \
   bitmapcopy.cpp \
+  bitmapmatrix.cpp \
   bitmapfilters.cpp \
+  bitmaprect.cpp \
   bitmapscroll.cpp \
+  blend.cpp \
   blurs.cpp \
+  blurrect.cpp \
+  circles.cpp \
+  colorfilterimagefilter.cpp \
   colormatrix.cpp \
   complexclip.cpp \
   complexclip2.cpp \
+  composeshader.cpp \
   convexpaths.cpp \
   cubicpaths.cpp \
+  cmykjpeg.cpp \
   degeneratesegments.cpp \
+  dashcubics.cpp \
+  dashing.cpp \
+  distantclip.cpp \
+  displacement.cpp \
   drawbitmaprect.cpp \
+  drawlooper.cpp \
+  extractbitmap.cpp \
   emptypath.cpp \
+  fatpathfill.cpp \
+  factory.cpp \
   filltypes.cpp \
   filltypespersp.cpp \
   fontscaler.cpp \
   gammatext.cpp \
+  getpostextpath.cpp \
+  giantbitmap.cpp \
   gradients.cpp \
   gradtext.cpp \
   hairmodes.cpp \
+  hittestpath.cpp \
   imageblur.cpp \
+  imagemagnifier.cpp \
+  lighting.cpp \
+  image.cpp \
+  imagefiltersbase.cpp \
+  imagefiltersgraph.cpp \
   lcdtext.cpp \
   linepaths.cpp \
+  matrixconvolution.cpp \
+  modecolorfilters.cpp \
   morphology.cpp \
   ninepatchstretch.cpp \
   nocolorbleed.cpp \
   patheffects.cpp \
   pathfill.cpp \
+  pathinterior.cpp \
   pathreverse.cpp \
   points.cpp \
   poly2poly.cpp \
   quadpaths.cpp \
+  rrect.cpp \
+  rrects.cpp \
+  samplerstress.cpp \
+  shaderbounds.cpp \
   shadertext.cpp \
+  shadertext2.cpp \
+  shadertext3.cpp \
   shadows.cpp \
-  shapes.cpp \
+  simpleaaclip.cpp \
+  spritebitmap.cpp \
+  srcmode.cpp \
   strokefill.cpp \
-  strokerects.cpp \
+  strokerect.cpp \
   strokes.cpp \
   tablecolorfilter.cpp \
+  texteffects.cpp \
   testimagefilters.cpp \
   texdata.cpp \
   tilemodes.cpp \
   tinybitmap.cpp \
+  twopointradial.cpp \
+  typeface.cpp \
   verttext.cpp \
   verttext2.cpp \
+  verylargebitmap.cpp \
   xfermodes.cpp
 
-LOCAL_STATIC_LIBRARIES := libskiagpu
 LOCAL_SHARED_LIBRARIES := \
   libcutils \
   libutils \
@@ -72,10 +113,14 @@
   external/skia/include/effects \
   external/skia/include/gpu \
   external/skia/include/images \
+  external/skia/include/pipe \
   external/skia/include/utils \
-  external/skia/gm
-
-#LOCAL_CFLAGS := 
+  external/skia/gm \
+  external/skia/src/core \
+  external/skia/src/effects \
+  external/skia/src/gpu \
+  external/skia/src/pipe/utils \
+  external/skia/src/utils
 
 LOCAL_MODULE := skia_gm
 
diff --git a/gm/aaclip.cpp b/gm/aaclip.cpp
index 2976560..88e1cd4 100644
--- a/gm/aaclip.cpp
+++ b/gm/aaclip.cpp
@@ -9,13 +9,137 @@
 #include "SkCanvas.h"
 #include "SkPath.h"
 
+#include "SkDashPathEffect.h"
+static void test_giant_dash(SkCanvas* canvas) {
+    SkPaint paint;
+    const SkScalar intervals[] = { SK_Scalar1, SK_Scalar1 };
+
+    paint.setStrokeWidth(2);
+    paint.setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
+
+    SkScalar big = 500 * 1000;
+
+    canvas->drawLine(10, 10, big, 10, paint);
+    canvas->drawLine(-big, 20, 500, 20, paint);
+    canvas->drawLine(-big, 30, big, 30, paint);
+
+    const SkScalar intervals2[] = { 20, 5, 10, 5 };
+    paint.setPathEffect(new SkDashPathEffect(intervals2, 4, 17))->unref();
+
+    canvas->translate(0, 40);
+    SkScalar x = -500;
+    SkScalar width = 3173;
+    for (int i = 0; i < 40; ++i) {
+        if (i > 10)
+        canvas->drawLine(x, 0, x + width, 0, paint);
+        x += 1;
+        canvas->translate(0, 4);
+    }
+}
+
+// Reproduces bug found here: http://jsfiddle.net/R8Cu5/1/
+//
+#include "SkGradientShader.h"
+static void test_grad(SkCanvas* canvas) {
+    SkPoint pts[] = {
+        { 478.544067f, -84.2041016f },
+        { 602.455933f, 625.204102f },
+    };
+    SkColor colors[] = { SK_ColorBLACK, SK_ColorBLACK, SK_ColorRED, SK_ColorRED };
+    SkScalar pos[] = { 0, 0.3f, 0.3f, 1.0f };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, pos, 4, SkShader::kClamp_TileMode);
+    SkPaint p;
+    p.setShader(s)->unref();
+    canvas->drawPaint(p);
+}
+
+static SkCanvas* MakeCanvas(const SkIRect& bounds) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas* canvas = new SkCanvas(bm);
+    canvas->translate(-SkIntToScalar(bounds.fLeft), -SkIntToScalar(bounds.fTop));
+    return canvas;
+}
+
+#ifdef SK_DEBUG
+static void GetBitmap(const SkCanvas* canvas, SkBitmap* bm) {
+    *bm = canvas->getDevice()->accessBitmap(false);
+}
+#endif
+
+static void compare_canvas(const SkCanvas* a, const SkCanvas* b) {
+#ifdef SK_DEBUG
+    SkBitmap bma, bmb;
+    GetBitmap(a, &bma);
+    GetBitmap(b, &bmb);
+
+    SkASSERT(bma.width() == bmb.width());
+    SkASSERT(bma.height() == bmb.height());
+
+    bma.lockPixels();
+    bmb.lockPixels();
+    for (int y = 0; y < bma.height(); ++y) {
+        const SkPMColor* rowa = bma.getAddr32(0, y);
+        const SkPMColor* rowb = bmb.getAddr32(0, y);
+        SkASSERT(!memcmp(rowa, rowb, bma.width() << 2));
+
+        for (int x = 1; x < bma.width() - 1; ++x) {
+            SkASSERT(0xFF000000 == rowa[x]);
+            SkASSERT(0xFF000000 == rowb[x]);
+        }
+    }
+#endif
+}
+
+static void drawRectAsPath(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+    SkPath path;
+    path.addRect(r);
+    canvas->drawPath(path, p);
+}
+
+static void test_maskFromPath(const SkPath& path) {
+    SkIRect bounds;
+    path.getBounds().roundOut(&bounds);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkAutoTUnref<SkCanvas> path_canvas(MakeCanvas(bounds));
+    path_canvas->drawPath(path, paint);
+
+    SkAutoTUnref<SkCanvas> rect_canvas(MakeCanvas(bounds));
+    drawRectAsPath(rect_canvas, path.getBounds(), paint);
+
+    compare_canvas(path_canvas, rect_canvas);
+}
+
+static void test_mask() {
+    for (int i = 1; i <= 20; ++i) {
+        const SkScalar dx = SK_Scalar1 / i;
+        const SkRect constr = SkRect::MakeWH(dx, SkIntToScalar(2));
+        for (int n = 2; n < 20; ++n) {
+            SkPath path;
+            path.setFillType(SkPath::kEvenOdd_FillType);
+            SkRect r = constr;
+            while (r.fRight < SkIntToScalar(4)) {
+                path.addRect(r);
+                r.offset(dx, 0);
+            }
+            test_maskFromPath(path);
+        }
+    }
+}
+
 namespace skiagm {
 
 /** Draw a 2px border around the target, then red behind the target;
     set the clip to match the target, then draw >> the target in blue.
 */
 
-void draw (SkCanvas* canvas, SkRect& target, int x, int y) {
+static void draw (SkCanvas* canvas, SkRect& target, int x, int y) {
     SkPaint borderPaint;
     borderPaint.setColor(SkColorSetRGB(0x0, 0xDD, 0x0));
     borderPaint.setAntiAlias(true);
@@ -38,22 +162,22 @@
     canvas->restore();
 }
 
-void draw_square (SkCanvas* canvas, int x, int y) {
+static void draw_square (SkCanvas* canvas, int x, int y) {
     SkRect target (SkRect::MakeWH(10 * SK_Scalar1, 10 * SK_Scalar1));
     draw(canvas, target, x, y);
 }
 
-void draw_column (SkCanvas* canvas, int x, int y) {
+static void draw_column (SkCanvas* canvas, int x, int y) {
     SkRect target (SkRect::MakeWH(1 * SK_Scalar1, 10 * SK_Scalar1));
     draw(canvas, target, x, y);
 }
 
-void draw_bar (SkCanvas* canvas, int x, int y) {
+static void draw_bar (SkCanvas* canvas, int x, int y) {
     SkRect target (SkRect::MakeWH(10 * SK_Scalar1, 1 * SK_Scalar1));
     draw(canvas, target, x, y);
 }
 
-void draw_rect_tests (SkCanvas* canvas) {
+static void draw_rect_tests (SkCanvas* canvas) {
     draw_square(canvas, 10, 10);
     draw_column(canvas, 30, 10);
     draw_bar(canvas, 10, 30);
@@ -81,7 +205,17 @@
         return make_isize(640, 480);
     }
 
-    virtual void onDraw(SkCanvas* canvas) {
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        if (false) {
+            test_giant_dash(canvas); return;
+        }
+        if (false) {
+            test_grad(canvas); return;
+        }
+        if (false) { // avoid bit rot, suppress warning
+            test_mask();
+        }
+
         // Initial pixel-boundary-aligned draw
         draw_rect_tests(canvas);
 
@@ -103,6 +237,8 @@
         draw_rect_tests(canvas);
     }
 
+    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/aarectmodes.cpp b/gm/aarectmodes.cpp
index f518cae..704ddcb 100644
--- a/gm/aarectmodes.cpp
+++ b/gm/aarectmodes.cpp
@@ -16,18 +16,18 @@
     SkPoint pts[] = {
         {10, 160}, {610, 160},
         {610, 160}, {10, 160},
-        
+
         {610, 160}, {610, 160},
         {610, 199}, {610, 199},
-        
+
         {10, 198}, {610, 198},
         {610, 199}, {10, 199},
-        
+
         {10, 160}, {10, 160},
         {10, 199}, {10, 199}
     };
     char verbs[] = {
-        0, 1, 1, 1, 4, 
+        0, 1, 1, 1, 4,
         0, 1, 1, 1, 4,
         0, 1, 1, 1, 4,
         0, 1, 1, 1, 4
@@ -57,18 +57,6 @@
     canvas->drawPath(path, paint);
 }
 
-static SkCanvas* create_canvas(int w, int h) {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
-    bm.allocPixels();
-    bm.eraseColor(0);
-    return new SkCanvas(bm);
-}
-
-static const SkBitmap& extract_bitmap(SkCanvas* canvas) {
-    return canvas->getDevice()->accessBitmap(false);
-}
-
 static const struct {
     SkXfermode::Mode  fMode;
     const char*         fLabel;
@@ -114,7 +102,7 @@
                                    H / 4 + offset,
                                    W / 2, H / 2);
     canvas->drawRect(rect, paint);
-    
+
     return H;
 }
 
@@ -129,7 +117,7 @@
     SkShader* s = SkShader::CreateBitmapShader(bm,
                                                SkShader::kRepeat_TileMode,
                                                SkShader::kRepeat_TileMode);
-    
+
     SkMatrix m;
     m.setScale(SkIntToScalar(6), SkIntToScalar(6));
     s->setLocalMatrix(m);
@@ -137,7 +125,7 @@
 }
 
 namespace skiagm {
-    
+
     class AARectModesGM : public GM {
         SkPaint fBGPaint;
     public:
@@ -154,7 +142,9 @@
         virtual SkISize onISize() { return make_isize(640, 480); }
 
         virtual void onDraw(SkCanvas* canvas) {
-//            test4(canvas);
+            if (false) { // avoid bit rot, suppress warning
+                test4(canvas);
+            }
             const SkRect bounds = SkRect::MakeWH(W, H);
             static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 };
 
@@ -170,7 +160,7 @@
                         canvas->save();
                     }
                     SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
-                
+
                     canvas->drawRect(bounds, fBGPaint);
                     canvas->saveLayer(&bounds, NULL);
                     SkScalar dy = drawCell(canvas, mode,
@@ -187,9 +177,6 @@
             }
         }
 
-        // disable pdf for now, since it crashes on mac
-        virtual uint32_t onGetFlags() const { return kSkipPDF_Flag; }
-
     private:
         typedef GM INHERITED;
     };
@@ -200,4 +187,3 @@
     static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/arithmode.cpp b/gm/arithmode.cpp
index ea015c6..f5e1091 100644
--- a/gm/arithmode.cpp
+++ b/gm/arithmode.cpp
@@ -19,7 +19,7 @@
     SkBitmap bm;
     bm.setConfig(SkBitmap::kARGB_8888_Config, WW, HH);
     bm.allocPixels();
-    bm.eraseColor(0);
+    bm.eraseColor(SK_ColorTRANSPARENT);
     return bm;
 }
 
@@ -94,7 +94,7 @@
     virtual void onDraw(SkCanvas* canvas) {
         SkBitmap src = make_src();
         SkBitmap dst = make_dst();
-        
+
         const SkScalar one = SK_Scalar1;
         static const SkScalar K[] = {
             0, 0, 0, 0,
@@ -109,11 +109,10 @@
             one/4, one/2, one/2, 0,
             -one/4, one/2, one/2, 0,
         };
-        
+
         const SkScalar* k = K;
         const SkScalar* stop = k + SK_ARRAY_COUNT(K);
         SkScalar y = 0;
-        SkScalar x = 0;
         SkScalar gap = SkIntToScalar(src.width() + 20);
         while (k < stop) {
             SkScalar x = 0;
diff --git a/gm/bicubicfilter.cpp b/gm/bicubicfilter.cpp
new file mode 100644
index 0000000..c3f80df
--- /dev/null
+++ b/gm/bicubicfilter.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkColor.h"
+#include "SkBicubicImageFilter.h"
+
+namespace skiagm {
+
+class BicubicGM : public GM {
+public:
+    BicubicGM() : fInitialized(false) {
+        this->setBGColor(0x00000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bicubicfilter");
+    }
+
+    void make_checkerboard(int width, int height) {
+        SkASSERT(width % 2 == 0);
+        SkASSERT(height % 2 == 0);
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+        fCheckerboard.allocPixels();
+        SkAutoLockPixels lock(fCheckerboard);
+        for (int y = 0; y < height; y += 2) {
+            SkPMColor* s = fCheckerboard.getAddr32(0, y);
+            for (int x = 0; x < width; x += 2) {
+                *s++ = 0xFFFFFFFF;
+                *s++ = 0xFF000000;
+            }
+            s = fCheckerboard.getAddr32(0, y + 1);
+            for (int x = 0; x < width; x += 2) {
+                *s++ = 0xFF000000;
+                *s++ = 0xFFFFFFFF;
+            }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(400, 300);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_checkerboard(4, 4);
+            fInitialized = true;
+        }
+        SkScalar sk32 = SkIntToScalar(32);
+        canvas->clear(0x00000000);
+        SkPaint bilinearPaint, bicubicPaint;
+        SkSize scale = SkSize::Make(sk32, sk32);
+        canvas->save();
+        canvas->scale(sk32, sk32);
+        bilinearPaint.setFilterBitmap(true);
+        canvas->drawBitmap(fCheckerboard, 0, 0, &bilinearPaint);
+        canvas->restore();
+        SkAutoTUnref<SkImageFilter> bicubic(SkBicubicImageFilter::CreateMitchell(scale));
+        bicubicPaint.setImageFilter(bicubic);
+        SkRect srcBounds;
+        fCheckerboard.getBounds(&srcBounds);
+        canvas->translate(SkIntToScalar(140), 0);
+        canvas->saveLayer(&srcBounds, &bicubicPaint);
+        canvas->drawBitmap(fCheckerboard, 0, 0);
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new BicubicGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/bigmatrix.cpp b/gm/bigmatrix.cpp
new file mode 100644
index 0000000..d414486
--- /dev/null
+++ b/gm/bigmatrix.cpp
@@ -0,0 +1,98 @@
+
+/*
+ * 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 "SkColorPriv.h"
+#include "SkShader.h"
+
+namespace skiagm {
+
+class BigMatrixGM : public GM {
+public:
+    BigMatrixGM() {
+        this->setBGColor(0xFF66AA99);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("bigmatrix");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(50, 50);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkMatrix m;
+        m.reset();
+        m.setRotate(33 * SK_Scalar1);
+        m.postScale(3000 * SK_Scalar1, 3000 * SK_Scalar1);
+        m.postTranslate(6000 * SK_Scalar1, -5000 * SK_Scalar1);
+        canvas->concat(m);
+
+        SkPaint paint;
+        paint.setColor(SK_ColorRED);
+        paint.setAntiAlias(true);
+
+        bool success = m.invert(&m);
+        SkASSERT(success);
+        (void) success; // silence compiler :(
+
+        SkPath path;
+
+        SkPoint pt = {10 * SK_Scalar1, 10 * SK_Scalar1};
+        SkScalar small = 1 / (500 * SK_Scalar1);
+
+        m.mapPoints(&pt, 1);
+        path.addCircle(pt.fX, pt.fY, small);
+        canvas->drawPath(path, paint);
+
+        pt.set(30 * SK_Scalar1, 10 * SK_Scalar1);
+        m.mapPoints(&pt, 1);
+        SkRect rect = {pt.fX - small, pt.fY - small,
+                       pt.fX + small, pt.fY + small};
+        canvas->drawRect(rect, paint);
+
+        SkBitmap bmp;
+        bmp.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+        bmp.allocPixels();
+        bmp.lockPixels();
+        uint32_t* pixels = reinterpret_cast<uint32_t*>(bmp.getPixels());
+        pixels[0] = SkPackARGB32(0xFF, 0xFF, 0x00, 0x00);
+        pixels[1] = SkPackARGB32(0xFF, 0x00, 0xFF, 0x00);
+        pixels[2] = SkPackARGB32(0x80, 0x00, 0x00, 0x00);
+        pixels[3] = SkPackARGB32(0xFF, 0x00, 0x00, 0xFF);
+        bmp.unlockPixels();
+        pt.set(30 * SK_Scalar1, 30 * SK_Scalar1);
+        m.mapPoints(&pt, 1);
+        SkShader* shader = SkShader::CreateBitmapShader(
+                                            bmp,
+                                            SkShader::kRepeat_TileMode,
+                                            SkShader::kRepeat_TileMode);
+        SkMatrix s;
+        s.reset();
+        s.setScale(SK_Scalar1 / 1000, SK_Scalar1 / 1000);
+        shader->setLocalMatrix(s);
+        paint.setShader(shader)->unref();
+        paint.setAntiAlias(false);
+        paint.setFilterBitmap(true);
+        rect.setLTRB(pt.fX - small, pt.fY - small,
+                     pt.fX + small, pt.fY + small);
+        canvas->drawRect(rect, paint);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new BigMatrixGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/bitmapcopy.cpp b/gm/bitmapcopy.cpp
index 249ec43..6080918 100644
--- a/gm/bitmapcopy.cpp
+++ b/gm/bitmapcopy.cpp
@@ -30,13 +30,17 @@
 static void draw_checks(SkCanvas* canvas, int width, int height) {
     SkPaint paint;
     paint.setColor(SK_ColorRED);
-    canvas->drawRectCoords(0, 0, width / 2, height / 2, paint);
+    canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(0),
+        SkIntToScalar(width / 2), SkIntToScalar(height / 2), paint);
     paint.setColor(SK_ColorGREEN);
-    canvas->drawRectCoords(width / 2, 0, width, height / 2, paint);
+    canvas->drawRectCoords(SkIntToScalar(width / 2), SkIntToScalar(0),
+        SkIntToScalar(width), SkIntToScalar(height / 2), paint);
     paint.setColor(SK_ColorBLUE);
-    canvas->drawRectCoords(0, height / 2, width / 2, height, paint);
+    canvas->drawRectCoords(SkIntToScalar(0), SkIntToScalar(height / 2),
+        SkIntToScalar(width / 2), SkIntToScalar(height), paint);
     paint.setColor(SK_ColorYELLOW);
-    canvas->drawRectCoords(width / 2, height / 2, width, height, paint);
+    canvas->drawRectCoords(SkIntToScalar(width / 2), SkIntToScalar(height / 2),
+        SkIntToScalar(width), SkIntToScalar(height), paint);
 }
 
 class BitmapCopyGM : public GM {
@@ -107,7 +111,8 @@
         }
     }
 
-    virtual uint32_t onGetFlags() const { return kSkipPicture_Flag; }
+    virtual uint32_t onGetFlags() const { return kSkipPicture_Flag
+                                               | kSkipPipe_Flag; }
 
 private:
     typedef GM INHERITED;
@@ -115,7 +120,8 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
+#ifndef SK_BUILD_FOR_ANDROID
 static GM* MyFactory(void*) { return new BitmapCopyGM; }
 static GMRegistry reg(MyFactory);
-
+#endif
 }
diff --git a/gm/bitmapfilters.cpp b/gm/bitmapfilters.cpp
index cf98dcd..d74dbf5 100644
--- a/gm/bitmapfilters.cpp
+++ b/gm/bitmapfilters.cpp
@@ -78,14 +78,21 @@
 }
 
 class FilterGM : public GM {
-public:
-    SkBitmap    fBM8, fBM4444, fBM16, fBM32;
-
-	FilterGM() {
+    bool fOnce;
+    void init() {
+        if (fOnce) {
+            return;
+        }
+        fOnce = true;
         make_bm(&fBM8);
         fBM8.copyTo(&fBM4444, SkBitmap::kARGB_4444_Config);
         fBM8.copyTo(&fBM16, SkBitmap::kRGB_565_Config);
         fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
+    }
+public:
+    SkBitmap    fBM8, fBM4444, fBM16, fBM32;
+
+    FilterGM() : fOnce(false) {
         this->setBGColor(0xFFDDDDDD);
     }
 
@@ -94,11 +101,12 @@
         return SkString("bitmapfilters");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(540, 330);
     }
 
     virtual void onDraw(SkCanvas* canvas) {
+        this->init();
 
         SkScalar x = SkIntToScalar(10);
         SkScalar y = SkIntToScalar(10);
@@ -123,6 +131,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
-
-
diff --git a/gm/bitmapmatrix.cpp b/gm/bitmapmatrix.cpp
new file mode 100644
index 0000000..ad1329b
--- /dev/null
+++ b/gm/bitmapmatrix.cpp
@@ -0,0 +1,131 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkBlurMaskFilter.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkRect.h"
+#include "SkSize.h"
+#include "SkString.h"
+
+namespace skiagm {
+
+class DrawBitmapMatrixGM : public GM {
+public:
+    DrawBitmapMatrixGM() {}
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("drawbitmapmatrix");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE { return make_isize(1024, 256); }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkBitmap bm;
+        this->setupBitmap(&bm);
+
+        // Draw normally.
+        SkMatrix matrix;
+        matrix.reset();
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setDither(true);
+        canvas->drawBitmapMatrix(bm, matrix, &paint);
+
+        // Draw stretched horizontally and squished vertically.
+        canvas->translate(SkIntToScalar(bm.width() + 5), 0);
+        matrix.setScale(SkIntToScalar(2), SK_ScalarHalf);
+        canvas->drawBitmapMatrix(bm, matrix, &paint);
+
+        // Draw rotated
+        canvas->translate(SkIntToScalar(bm.width()*2 + 5), 0);
+        matrix.reset();
+        matrix.setRotate(SkIntToScalar(45), SkIntToScalar(bm.width() / 2),
+                         SkIntToScalar(bm.height() / 2));
+        canvas->save();
+        canvas->translate(0, SkIntToScalar(10));
+        canvas->drawBitmapMatrix(bm, matrix, &paint);
+        canvas->restore();
+
+        // Draw with perspective
+        canvas->translate(SkIntToScalar(bm.width() + 15), 0);
+        matrix.reset();
+        matrix.setPerspX(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
+        matrix.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
+        canvas->drawBitmapMatrix(bm, matrix, &paint);
+
+        // Draw with skew
+        canvas->translate(SkIntToScalar(bm.width() + 5), 0);
+        matrix.reset();
+        matrix.setSkew(SkIntToScalar(2), SkIntToScalar(2));
+        canvas->drawBitmapMatrix(bm, matrix, &paint);
+
+        // Draw with sin/cos
+        canvas->translate(SkIntToScalar(bm.width() * 4), 0);
+        matrix.reset();
+        matrix.setSinCos(SK_ScalarHalf, SkIntToScalar(2));
+        canvas->drawBitmapMatrix(bm, matrix, &paint);
+
+        {
+            // test the following code path:
+            // SkGpuDevice::drawPath() -> SkGpuDevice::drawWithMaskFilter()
+            SkPaint paint;
+
+            paint.setFilterBitmap(true);
+
+            SkMaskFilter* mf = SkBlurMaskFilter::Create(
+                5,
+                SkBlurMaskFilter::kNormal_BlurStyle,
+                SkBlurMaskFilter::kHighQuality_BlurFlag |
+                SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
+            paint.setMaskFilter(mf)->unref();
+
+            canvas->translate(SkIntToScalar(bm.width()*2 + 20), 0);
+
+            matrix.reset();
+            matrix.setRotate(SkIntToScalar(45), SkIntToScalar(bm.width() / 2),
+                             SkIntToScalar(bm.height() / 2));
+
+            canvas->save();
+            canvas->translate(0, SkIntToScalar(20));
+            canvas->drawBitmapMatrix(bm, matrix, &paint);
+            canvas->restore();
+        }
+
+    }
+private:
+    void setupBitmap(SkBitmap* bm) {
+        SkASSERT(bm);
+        static const int SIZE = 64;
+        bm->setConfig(SkBitmap::kARGB_8888_Config, SIZE, SIZE);
+        bm->allocPixels();
+        SkCanvas canvas(*bm);
+
+        SkPaint paint;
+        paint.setColor(SK_ColorGREEN);
+        canvas.drawPaint(paint);
+
+        paint.setColor(SK_ColorBLUE);
+        paint.setAntiAlias(true);
+        SkRect rect = SkRect::MakeWH(SkIntToScalar(SIZE), SkIntToScalar(SIZE));
+        SkPath path;
+        path.addOval(rect);
+        canvas.drawPath(path, paint);
+    }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DrawBitmapMatrixGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/bitmaprect.cpp b/gm/bitmaprect.cpp
new file mode 100644
index 0000000..9c6d472
--- /dev/null
+++ b/gm/bitmaprect.cpp
@@ -0,0 +1,253 @@
+
+/*
+ * 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 "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkShader.h"
+
+static void make_bitmap(SkBitmap* bitmap) {
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
+    bitmap->allocPixels();
+
+    SkCanvas canvas(*bitmap);
+
+    canvas.drawColor(SK_ColorRED);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    const SkPoint pts[] = { { 0, 0 }, { 64, 64 } };
+    const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE };
+    paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                                   SkShader::kClamp_TileMode))->unref();
+    canvas.drawCircle(32, 32, 32, paint);
+}
+
+class DrawBitmapRect2 : public skiagm::GM {
+    bool fUseIRect;
+public:
+    DrawBitmapRect2(bool useIRect) : fUseIRect(useIRect) {
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        SkString str;
+        str.printf("bitmaprect_%s", fUseIRect ? "i" : "s");
+        return str;
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        canvas->drawColor(0xFFCCCCCC);
+
+        const SkIRect src[] = {
+            { 0, 0, 32, 32 },
+            { 0, 0, 80, 80 },
+            { 32, 32, 96, 96 },
+            { -32, -32, 32, 32, }
+        };
+
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+
+        SkBitmap bitmap;
+        make_bitmap(&bitmap);
+
+        SkRect dstR = { 0, 200, 128, 380 };
+
+        canvas->translate(16, 40);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
+            SkRect srcR;
+            srcR.set(src[i]);
+
+            canvas->drawBitmap(bitmap, 0, 0, &paint);
+            if (!fUseIRect) {
+                canvas->drawBitmapRectToRect(bitmap, &srcR, dstR, &paint);
+            } else {
+                canvas->drawBitmapRect(bitmap, &src[i], dstR, &paint);
+            }
+
+            canvas->drawRect(dstR, paint);
+            canvas->drawRect(srcR, paint);
+
+            canvas->translate(160, 0);
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+static void make_3x3_bitmap(SkBitmap* bitmap) {
+
+    static const int gXSize = 3;
+    static const int gYSize = 3;
+
+    SkColor textureData[gXSize][gYSize] = {
+        { SK_ColorRED,    SK_ColorWHITE, SK_ColorBLUE },
+        { SK_ColorGREEN,  SK_ColorBLACK, SK_ColorCYAN },
+        { SK_ColorYELLOW, SK_ColorGRAY,  SK_ColorMAGENTA }
+    };
+
+
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, gXSize, gYSize);
+    bitmap->allocPixels();
+
+    SkAutoLockPixels lock(*bitmap);
+    for (int y = 0; y < gYSize; y++) {
+        for (int x = 0; x < gXSize; x++) {
+            *bitmap->getAddr32(x, y) = textureData[x][y];
+        }
+    }
+}
+
+// This GM attempts to make visible any issues drawBitmapRectToRect may have
+// with partial source rects. In this case the eight pixels on the border
+// should be half the width/height of the central pixel, i.e.:
+//                         __|____|__
+//                           |    |
+//                         __|____|__
+//                           |    |
+class DrawBitmapRect3 : public skiagm::GM {
+public:
+    DrawBitmapRect3() {
+        this->setBGColor(SK_ColorBLACK);
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        SkString str;
+        str.printf("3x3bitmaprect");
+        return str;
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+
+        SkBitmap bitmap;
+        make_3x3_bitmap(&bitmap);
+
+        SkRect srcR = { 0.5f, 0.5f, 2.5f, 2.5f };
+        SkRect dstR = { 100, 100, 300, 200 };
+
+        canvas->drawBitmapRectToRect(bitmap, &srcR, dstR, NULL);
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+static void make_big_bitmap(SkBitmap* bitmap) {
+
+    static const int gXSize = 4096;
+    static const int gYSize = 4096;
+    static const int gBorderWidth = 10;
+
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, gXSize, gYSize);
+    bitmap->allocPixels();
+
+    SkAutoLockPixels lock(*bitmap);
+    for (int y = 0; y < gYSize; ++y) {
+        for (int x = 0; x < gXSize; ++x) {
+            if (x <= gBorderWidth || x >= gXSize-gBorderWidth ||
+                y <= gBorderWidth || y >= gYSize-gBorderWidth) {
+                *bitmap->getAddr32(x, y) = 0x88FFFFFF;
+            } else {
+                *bitmap->getAddr32(x, y) = 0x88FF0000;
+            }
+        }
+    }
+}
+
+// This GM attempts to reveal any issues we may have when the GPU has to
+// break up a large texture in order to draw it. The XOR transfer mode will
+// create stripes in the image if there is imprecision in the destination
+// tile placement.
+class DrawBitmapRect4 : public skiagm::GM {
+    bool fUseIRect;
+public:
+    DrawBitmapRect4(bool useIRect) : fUseIRect(useIRect) {
+        this->setBGColor(0x88444444);
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        SkString str;
+        str.printf("bigbitmaprect_%s", fUseIRect ? "i" : "s");
+        return str;
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+
+        SkXfermode* mode = SkXfermode::Create(SkXfermode::kXor_Mode);
+
+        SkPaint paint;
+        paint.setAlpha(128);
+        paint.setXfermode(mode)->unref();
+
+        SkBitmap bitmap;
+        make_big_bitmap(&bitmap);
+
+        SkRect srcR1 = { 0.0f, 0.0f, 4096.0f, 2040.0f };
+        SkRect dstR1 = { 10.1f, 10.1f, 629.9f, 400.9f };
+
+        SkRect srcR2 = { 4085.0f, 10.0f, 4087.0f, 12.0f };
+        SkRect dstR2 = { 10, 410, 30, 430 };
+
+        if (!fUseIRect) {
+            canvas->drawBitmapRectToRect(bitmap, &srcR1, dstR1, &paint);
+            canvas->drawBitmapRectToRect(bitmap, &srcR2, dstR2, &paint);
+        } else {
+            SkIRect iSrcR1, iSrcR2;
+
+            srcR1.roundOut(&iSrcR1);
+            srcR2.roundOut(&iSrcR2);
+
+            canvas->drawBitmapRect(bitmap, &iSrcR1, dstR1, &paint);
+            canvas->drawBitmapRect(bitmap, &iSrcR2, dstR2, &paint);
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory0(void*) { return new DrawBitmapRect2(false); }
+static skiagm::GM* MyFactory1(void*) { return new DrawBitmapRect2(true); }
+
+static skiagm::GM* MyFactory2(void*) { return new DrawBitmapRect3(); }
+
+#ifndef SK_BUILD_FOR_ANDROID
+static skiagm::GM* MyFactory3(void*) { return new DrawBitmapRect4(false); }
+static skiagm::GM* MyFactory4(void*) { return new DrawBitmapRect4(true); }
+#endif
+
+static skiagm::GMRegistry reg0(MyFactory0);
+static skiagm::GMRegistry reg1(MyFactory1);
+
+static skiagm::GMRegistry reg2(MyFactory2);
+
+#ifndef SK_BUILD_FOR_ANDROID
+static skiagm::GMRegistry reg3(MyFactory3);
+static skiagm::GMRegistry reg4(MyFactory4);
+#endif
diff --git a/gm/bitmapscroll.cpp b/gm/bitmapscroll.cpp
index 70d1052..a1c0ce4 100644
--- a/gm/bitmapscroll.cpp
+++ b/gm/bitmapscroll.cpp
@@ -45,10 +45,19 @@
 }
 
 class BitmapScrollGM : public GM {
-public:
-    BitmapScrollGM() {
+    bool fInited;
+    void init() {
+        if (fInited) {
+            return;
+        }
+        fInited = true;
         // Create the original bitmap.
         make_bitmap(quarterWidth, quarterHeight, &origBitmap);
+    }
+
+public:
+    BitmapScrollGM() {
+        fInited = false;
         this->setBGColor(0xFFDDDDDD);
     }
 
@@ -62,6 +71,7 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
+        this->init();
         SkIRect scrollCenterRegion = SkIRect::MakeXYWH(
             quarterWidth, quarterHeight, quarterWidth*2+1, quarterHeight*2+1);
         int x = quarterWidth;
@@ -121,10 +131,10 @@
                 // Scroll a new copy of the bitmap, and then draw it.
                 // scrollRect() should always return true, even if it's a no-op
                 SkBitmap scrolledBitmap;
-                bool copyToReturnValue = origBitmap.copyTo(
+                SkDEBUGCODE(bool copyToReturnValue = )origBitmap.copyTo(
                     &scrolledBitmap, origBitmap.config());
                 SkASSERT(copyToReturnValue);
-                bool scrollRectReturnValue = scrolledBitmap.scrollRect(
+                SkDEBUGCODE(bool scrollRectReturnValue = )scrolledBitmap.scrollRect(
                     subset, scrollX * xMult, scrollY * yMult);
                 SkASSERT(scrollRectReturnValue);
                 canvas->drawBitmap(scrolledBitmap, bitmapX, bitmapY);
diff --git a/gm/blend.cpp b/gm/blend.cpp
new file mode 100644
index 0000000..452eb49
--- /dev/null
+++ b/gm/blend.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "SkBlendImageFilter.h"
+#include "SkBitmapSource.h"
+
+namespace skiagm {
+
+class ImageBlendGM : public GM {
+public:
+    ImageBlendGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("blend");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xD000D000);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(65), paint);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fCheckerboard.allocPixels();
+        SkDevice device(fCheckerboard);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint darkPaint;
+        darkPaint.setColor(0xFF404040);
+        SkPaint lightPaint;
+        lightPaint.setColor(0xFFA0A0A0);
+        for (int y = 0; y < 80; y += 16) {
+          for (int x = 0; x < 80; x += 16) {
+            canvas.save();
+            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint);
+            canvas.restore();
+          }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(500, 100);
+    }
+
+    void drawClippedBitmap(SkCanvas* canvas, const SkPaint& paint, int x) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), 0,
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), 0, &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            make_checkerboard();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+        SkAutoTUnref<SkImageFilter> background(SkNEW_ARGS(SkBitmapSource, (fCheckerboard)));
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kNormal_Mode, background)))->unref();
+        drawClippedBitmap(canvas, paint, 0);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kMultiply_Mode, background)))->unref();
+        drawClippedBitmap(canvas, paint, 100);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kScreen_Mode, background)))->unref();
+        drawClippedBitmap(canvas, paint, 200);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kDarken_Mode, background)))->unref();
+        drawClippedBitmap(canvas, paint, 300);
+        paint.setImageFilter(SkNEW_ARGS(SkBlendImageFilter, (SkBlendImageFilter::kLighten_Mode, background)))->unref();
+        drawClippedBitmap(canvas, paint, 400);
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ImageBlendGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/blurrect.cpp b/gm/blurrect.cpp
new file mode 100644
index 0000000..a5e8cf0
--- /dev/null
+++ b/gm/blurrect.cpp
@@ -0,0 +1,234 @@
+/*
+ * 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 "SkBlurMaskFilter.h"
+#include "SkBlurMask.h"
+#include "SkCanvas.h"
+#include "SkPath.h"
+
+#define STROKE_WIDTH    SkIntToScalar(10)
+
+typedef void (*Proc)(SkCanvas*, const SkRect&, const SkPaint&);
+
+static void fill_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+    canvas->drawRect(r, p);
+}
+
+static void draw_donut(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+    SkRect  rect;
+    SkPath  path;
+
+    rect = r;
+    rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
+    path.addRect(rect);
+    rect = r;
+    rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
+
+    path.addRect(rect);
+    path.setFillType(SkPath::kEvenOdd_FillType);
+
+    canvas->drawPath(path, p);
+}
+
+static void draw_donut_skewed(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
+    SkRect  rect;
+    SkPath  path;
+
+    rect = r;
+    rect.outset(STROKE_WIDTH/2, STROKE_WIDTH/2);
+    path.addRect(rect);
+    rect = r;
+    rect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
+
+    rect.offset(7, -7);
+
+    path.addRect(rect);
+    path.setFillType(SkPath::kEvenOdd_FillType);
+
+    canvas->drawPath(path, p);
+}
+
+#include "SkGradientShader.h"
+
+typedef void (*PaintProc)(SkPaint*, SkScalar width);
+
+static const char* gBlurStyle2Name[] = {
+    "normal",
+    "solid",
+    "outer",
+    "inner"
+};
+
+class BlurRectGM : public skiagm::GM {
+    SkAutoTUnref<SkMaskFilter> fMaskFilter;
+    SkString  fName;
+    PaintProc fPProc;
+    SkAlpha   fAlpha;
+public:
+    BlurRectGM(const char name[], PaintProc pproc, U8CPU alpha,
+               SkBlurMaskFilter::BlurStyle bs) :
+        fMaskFilter(SkBlurMaskFilter::Create(STROKE_WIDTH/2, bs,
+                                       SkBlurMaskFilter::kHighQuality_BlurFlag))
+        , fName(name)
+        , fPProc(pproc)
+        , fAlpha(SkToU8(alpha))
+    {
+        fName.appendf("_%s", gBlurStyle2Name[bs]);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return fName;
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
+
+        SkRect  r = { 0, 0, 250, 120 };
+
+        SkPaint paint;
+        paint.setMaskFilter(fMaskFilter);
+        if (fPProc) {
+            fPProc(&paint, r.width());
+        }
+        paint.setAlpha(fAlpha);
+
+        static const Proc procs[] = {
+            fill_rect, draw_donut, draw_donut_skewed
+        };
+
+        this->drawProcs(canvas, r, paint, false, procs, SK_ARRAY_COUNT(procs));
+        canvas->translate(r.width() * 4/3, 0);
+        this->drawProcs(canvas, r, paint, true, procs, SK_ARRAY_COUNT(procs));
+    }
+
+    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
+
+private:
+    void drawProcs(SkCanvas* canvas, const SkRect& r, const SkPaint& paint,
+                   bool doClip, const Proc procs[], size_t procsCount) {
+        SkAutoCanvasRestore acr(canvas, true);
+        for (size_t i = 0; i < procsCount; ++i) {
+            if (doClip) {
+                SkRect clipRect(r);
+                clipRect.inset(STROKE_WIDTH/2, STROKE_WIDTH/2);
+                canvas->save();
+                canvas->clipRect(r);
+            }
+            procs[i](canvas, r, paint);
+            if (doClip) {
+                canvas->restore();
+            }
+            canvas->translate(0, r.height() * 4/3);
+        }
+    }
+
+    typedef GM INHERITED;
+};
+
+class BlurRectCompareGM : public skiagm::GM {
+    SkString  fName;
+    unsigned int fRectWidth, fRectHeight;
+    SkScalar fRadius;
+public:
+    BlurRectCompareGM(const char name[], unsigned int rectWidth, unsigned int rectHeight, float radius)
+        : fName(name)
+        , fRectWidth(rectWidth)
+        , fRectHeight(rectHeight)
+        , fRadius(radius)
+    {}
+
+  int width() const { return fRectWidth; }
+  int height() const { return fRectHeight; }
+  SkScalar radius() const { return fRadius; }
+
+protected:
+    virtual SkString onShortName() {
+        return fName;
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void makeMask(SkMask *m, const SkRect&) = 0;
+
+    virtual void onDraw(SkCanvas* canvas) {
+      SkRect r;
+      r.setWH(SkIntToScalar(fRectWidth), SkIntToScalar(fRectHeight));
+
+      SkMask mask;
+
+      this->makeMask(&mask, r);
+      SkAutoMaskFreeImage amfi(mask.fImage);
+
+      SkBitmap bm;
+      bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(), mask.fBounds.height());
+      bm.setPixels(mask.fImage);
+      canvas->drawBitmap(bm, 50, 50, NULL);
+    }
+
+    virtual uint32_t onGetFlags() const { return kSkipPipe_Flag; }
+
+private:
+    typedef GM INHERITED;
+};
+
+class BlurRectFastGM: public BlurRectCompareGM {
+public:
+    BlurRectFastGM(const char name[], unsigned int rect_width,
+                   unsigned int rect_height, float blur_radius) :
+        BlurRectCompareGM(name, rect_width, rect_height, blur_radius) {}
+protected:
+    virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+        SkBlurMask::BlurRect(m, r, radius(), SkBlurMask::kNormal_Style,
+                             SkBlurMask::kHigh_Quality );
+    }
+};
+
+class BlurRectSlowGM: public BlurRectCompareGM {
+public:
+  BlurRectSlowGM(const char name[], unsigned int rect_width, unsigned int rect_height, float blur_radius) :
+    BlurRectCompareGM( name, rect_width, rect_height, blur_radius ) {}
+protected:
+    virtual void makeMask(SkMask *m, const SkRect& r) SK_OVERRIDE {
+        SkMask src;
+        r.roundOut(&src.fBounds);
+        src.fBounds.offset(-src.fBounds.fLeft, -src.fBounds.fTop);  // move to origin
+        src.fFormat = SkMask::kA8_Format;
+        src.fRowBytes = src.fBounds.width();
+        src.fImage = SkMask::AllocImage( src.computeTotalImageSize() );
+        SkAutoMaskFreeImage amfi(src.fImage);
+
+        memset(src.fImage, 0xff, src.computeTotalImageSize());
+
+        SkBlurMask::BlurSeparable(m, src, radius()/2, SkBlurMask::kNormal_Style, SkBlurMask::kHigh_Quality);
+    }
+};
+
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kNormal_BlurStyle);)
+DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kSolid_BlurStyle);)
+DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kOuter_BlurStyle);)
+DEF_GM(return new BlurRectGM("blurrect", NULL, 0xFF, SkBlurMaskFilter::kInner_BlurStyle);)
+
+DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_10", 100, 100, 10);)
+DEF_GM(return new BlurRectFastGM("blurrect_fast_100_100_2", 100, 100, 2);)
+DEF_GM(return new BlurRectFastGM("blurrect_fast_10_10_100", 10, 10, 100);)
+DEF_GM(return new BlurRectFastGM("blurrect_fast_10_100_10", 10, 100, 10);)
+
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_10", 100, 100, 10);)
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_100_100_2", 100, 100, 2);)
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_10_100", 10, 10, 100);)
+DEF_GM(return new BlurRectSlowGM("blurrect_slow_10_100_10", 10, 100, 10);)
diff --git a/gm/blurs.cpp b/gm/blurs.cpp
index 69504f7..22804c7 100644
--- a/gm/blurs.cpp
+++ b/gm/blurs.cpp
@@ -17,6 +17,16 @@
     }
 
 protected:
+#ifdef SK_SCALAR_IS_FIXED
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        // SkCanvas::drawCircle, used by this test, performs a quick reject.
+        // The large size given to the device used by SkGPipeCanvas means that
+        // the device clip will not be set properly and circles will be
+        // rejected when in FIXED.
+        return this->INHERITED::onGetFlags() | GM::kSkipPipe_Flag;
+    }
+#endif
+
     virtual SkString onShortName() {
         return SkString("blurs");
     }
@@ -97,4 +107,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/circles.cpp b/gm/circles.cpp
new file mode 100644
index 0000000..4513934
--- /dev/null
+++ b/gm/circles.cpp
@@ -0,0 +1,205 @@
+
+/*
+ * Copyright 2012 Intel 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 "SkTArray.h"
+#include "SkRandom.h"
+#include "SkMatrix.h"
+#include "SkBlurMaskFilter.h"
+#include "SkGradientShader.h"
+#include "SkBlurDrawLooper.h"
+
+namespace skiagm {
+
+class CircleGM : public GM {
+public:
+    CircleGM() {
+        this->setBGColor(0xFF000000);
+        this->makePaints();
+        this->makeMatrices();
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("circles");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return make_isize(1200, 900);
+    }
+
+    void makePaints() {
+        {
+        // no AA
+        SkPaint p;
+        fPaints.push_back(p);
+        }
+
+        {
+        // AA
+        SkPaint p;
+        p.setAntiAlias(true);
+        fPaints.push_back(p);
+        }
+
+        {
+        // AA with mask filter
+        SkPaint p;
+        p.setAntiAlias(true);
+        SkMaskFilter* mf = SkBlurMaskFilter::Create(SkIntToScalar(5),
+                               SkBlurMaskFilter::kNormal_BlurStyle,
+                               SkBlurMaskFilter::kHighQuality_BlurFlag);
+        p.setMaskFilter(mf)->unref();
+        fPaints.push_back(p);
+        }
+
+        {
+        // AA with radial shader
+        SkPaint p;
+        p.setAntiAlias(true);
+        SkPoint center = SkPoint::Make(SkIntToScalar(40), SkIntToScalar(40));
+        SkColor colors[] = { SK_ColorBLUE, SK_ColorRED, SK_ColorGREEN };
+        SkScalar pos[] = { 0, SK_ScalarHalf, SK_Scalar1 };
+        SkShader* s = SkGradientShader::CreateRadial(center,
+                                                     SkIntToScalar(20),
+                                                     colors,
+                                                     pos,
+                                                     SK_ARRAY_COUNT(colors),
+                                                     SkShader::kClamp_TileMode);
+        p.setShader(s)->unref();
+        fPaints.push_back(p);
+        }
+
+        {
+        // AA with blur
+        SkPaint p;
+        p.setAntiAlias(true);
+        SkBlurDrawLooper* shadowLooper =
+            new SkBlurDrawLooper (SkIntToScalar(10), SkIntToScalar(5),
+                                  SkIntToScalar(10), 0xFF0000FF,
+                                  SkBlurDrawLooper::kIgnoreTransform_BlurFlag |
+                                  SkBlurDrawLooper::kOverrideColor_BlurFlag |
+                                  SkBlurDrawLooper::kHighQuality_BlurFlag );
+        SkAutoUnref aurL0(shadowLooper);
+        p.setLooper(shadowLooper);
+        fPaints.push_back(p);
+        }
+
+        {
+        // AA with stroke style
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(SkIntToScalar(3));
+        fPaints.push_back(p);
+        }
+
+        {
+        // AA with stroke style, width = 0
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setStyle(SkPaint::kStroke_Style);
+        fPaints.push_back(p);
+        }
+
+        {
+        // AA with stroke and fill style
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setStyle(SkPaint::kStrokeAndFill_Style);
+        p.setStrokeWidth(SkIntToScalar(2));
+        fPaints.push_back(p);
+        }
+    }
+
+    void makeMatrices() {
+        {
+        SkMatrix m;
+        m.setScale(SkIntToScalar(2), SkIntToScalar(3));
+        fMatrices.push_back(m);
+        }
+
+        {
+        SkMatrix m;
+        m.setScale(SkIntToScalar(2), SkIntToScalar(2));
+        fMatrices.push_back(m);
+        }
+
+        {
+        SkMatrix m;
+        m.setSkew(SkIntToScalar(2), SkIntToScalar(3));
+        fMatrices.push_back(m);
+        }
+
+        {
+        SkMatrix m;
+        m.setSkew(SkIntToScalar(2), SkIntToScalar(2));
+        fMatrices.push_back(m);
+        }
+
+        {
+        SkMatrix m;
+        m.setRotate(SkIntToScalar(30));
+        fMatrices.push_back(m);
+        }
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkRandom rand;
+        canvas->translate(20 * SK_Scalar1, 20 * SK_Scalar1);
+
+        int i;
+        for (i = 0; i < fPaints.count(); ++i) {
+            canvas->save();
+            // position the path, and make it at off-integer coords.
+            canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 4,
+                              SK_Scalar1 * 200 * (i / 5) + 3 * SK_Scalar1 / 4);
+            SkColor color = rand.nextU();
+            color |= 0xff000000;
+            fPaints[i].setColor(color);
+
+            canvas->drawCircle(SkIntToScalar(40), SkIntToScalar(40),
+                               SkIntToScalar(20),
+                               fPaints[i]);
+            canvas->restore();
+        }
+
+        for (int j = 0; j < fMatrices.count(); ++j, ++i) {
+            canvas->save();
+
+            canvas->translate(SK_Scalar1 * 200 * (i % 5) + SK_Scalar1 / 4,
+                              SK_Scalar1 * 200 * (i / 5) + 3 * SK_Scalar1 / 4);
+
+            canvas->concat(fMatrices[j]);
+
+            SkPaint paint;
+            paint.setAntiAlias(true);
+
+            SkColor color = rand.nextU();
+            color |= 0xff000000;
+            paint.setColor(color);
+
+            canvas->drawCircle(SkIntToScalar(40), SkIntToScalar(40),
+                               SkIntToScalar(20),
+                               paint);
+
+            canvas->restore();
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+    SkTArray<SkPaint> fPaints;
+    SkTArray<SkMatrix> fMatrices;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new CircleGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/cmykjpeg.cpp b/gm/cmykjpeg.cpp
new file mode 100644
index 0000000..7ac4259
--- /dev/null
+++ b/gm/cmykjpeg.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "SkImageDecoder.h"
+#include "SkStream.h"
+
+namespace skiagm {
+
+/** Draw a CMYK encoded jpeg - libjpeg doesn't support CMYK->RGB
+    conversion so this tests Skia's internal processing
+*/
+class CMYKJpegGM : public GM {
+public:
+    CMYKJpegGM() {}
+
+protected:
+    virtual void onOnceBeforeDraw() SK_OVERRIDE {
+
+        // parameters to the "decode" call
+        bool dither = false;
+        SkBitmap::Config prefConfig = SkBitmap::kARGB_8888_Config;
+
+        SkString filename(INHERITED::gResourcePath);
+        if (!filename.endsWith("/") && !filename.endsWith("\\")) {
+            filename.append("/");
+        }
+
+        filename.append("CMYK.jpg");
+
+        SkFILEStream stream(filename.c_str());
+        SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+        if (codec) {
+            stream.rewind();
+            codec->setDitherImage(dither);
+            codec->decode(&stream, &fBitmap, prefConfig,
+                          SkImageDecoder::kDecodePixels_Mode);
+            SkDELETE(codec);
+        }
+    }
+
+    virtual SkString onShortName() {
+        return SkString("cmykjpeg");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        canvas->translate(20*SK_Scalar1, 20*SK_Scalar1);
+        canvas->drawBitmap(fBitmap, 0, 0);
+    }
+
+private:
+    SkBitmap fBitmap;
+
+    typedef GM INHERITED;
+};
+
+void forceLinking();
+
+void forceLinking() {
+    SkDEBUGCODE(SkImageDecoder *creator = ) CreateJPEGImageDecoder();
+    SkASSERT(creator);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new CMYKJpegGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/colorfilterimagefilter.cpp b/gm/colorfilterimagefilter.cpp
new file mode 100644
index 0000000..6323d33
--- /dev/null
+++ b/gm/colorfilterimagefilter.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "SkColorMatrixFilter.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+#include "SkBlurImageFilter.h"
+#include "SkColorFilterImageFilter.h"
+
+#define FILTER_WIDTH    SkIntToScalar(30)
+#define FILTER_HEIGHT   SkIntToScalar(30)
+#define MARGIN          SkIntToScalar(10)
+
+static SkImageFilter* make_blur(float amount, SkImageFilter* input = NULL) {
+    return new SkBlurImageFilter(amount, amount, input);
+}
+
+static SkImageFilter* make_brightness(float amount, SkImageFilter* input = NULL) {
+    SkScalar amount255 = SkScalarMul(SkFloatToScalar(amount), SkIntToScalar(255));
+    SkScalar matrix[20] = { 1, 0, 0, 0, amount255,
+                            0, 1, 0, 0, amount255,
+                            0, 0, 1, 0, amount255,
+                            0, 0, 0, 1, 0 };
+    SkAutoTUnref<SkColorFilter> filter(new SkColorMatrixFilter(matrix));
+    return SkColorFilterImageFilter::Create(filter, input);
+}
+
+static SkImageFilter* make_grayscale(SkImageFilter* input = NULL) {
+    SkScalar matrix[20];
+    memset(matrix, 0, 20 * sizeof(SkScalar));
+    matrix[0] = matrix[5] = matrix[10] = SkFloatToScalar(0.2126f);
+    matrix[1] = matrix[6] = matrix[11] = SkFloatToScalar(0.7152f);
+    matrix[2] = matrix[7] = matrix[12] = SkFloatToScalar(0.0722f);
+    matrix[18] = SkFloatToScalar(1.0f);
+    SkAutoTUnref<SkColorFilter> filter(new SkColorMatrixFilter(matrix));
+    return SkColorFilterImageFilter::Create(filter, input);
+}
+
+static SkImageFilter* make_mode_blue(SkImageFilter* input = NULL) {
+    SkAutoTUnref<SkColorFilter> filter(
+        SkColorFilter::CreateModeFilter(SK_ColorBLUE, SkXfermode::kSrcIn_Mode));
+    return SkColorFilterImageFilter::Create(filter, input);
+}
+
+class ColorFilterImageFilterGM : public skiagm::GM {
+public:
+    ColorFilterImageFilterGM () {}
+
+protected:
+
+    virtual SkString onShortName() {
+        return SkString("colorfilterimagefilter");
+    }
+
+    void drawClippedRect(SkCanvas* canvas, const SkRect& r, const SkPaint& paint, float outset = 0.0f) {
+        canvas->save();
+        SkRect clip(r);
+        clip.outset(SkFloatToScalar(outset), SkFloatToScalar(outset));
+        canvas->clipRect(clip);
+        canvas->drawRect(r, paint);
+        canvas->restore();
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(400, 100); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkRect r = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
+        SkPaint paint;
+        paint.setColor(SK_ColorRED);
+        canvas->save();
+        for (float brightness = -1.0f; brightness <= 1.0f; brightness += 0.2f) {
+            SkAutoTUnref<SkImageFilter> dim(make_brightness(-brightness));
+            SkAutoTUnref<SkImageFilter> bright(make_brightness(brightness, dim));
+            paint.setImageFilter(bright);
+            drawClippedRect(canvas, r, paint);
+            canvas->translate(FILTER_WIDTH + MARGIN, 0);
+        }
+        canvas->restore();
+        canvas->translate(0, FILTER_HEIGHT + MARGIN);
+        {
+            SkAutoTUnref<SkImageFilter> brightness(make_brightness(0.9f));
+            SkAutoTUnref<SkImageFilter> grayscale(make_grayscale(brightness));
+            paint.setImageFilter(grayscale);
+            drawClippedRect(canvas, r, paint);
+            canvas->translate(FILTER_WIDTH + MARGIN, 0);
+        }
+        {
+            SkAutoTUnref<SkImageFilter> grayscale(make_grayscale());
+            SkAutoTUnref<SkImageFilter> brightness(make_brightness(0.9f, grayscale));
+            paint.setImageFilter(brightness);
+            drawClippedRect(canvas, r, paint);
+            canvas->translate(FILTER_WIDTH + MARGIN, 0);
+        }
+        {
+            SkAutoTUnref<SkImageFilter> blue(make_mode_blue());
+            SkAutoTUnref<SkImageFilter> brightness(make_brightness(1.0f, blue));
+            paint.setImageFilter(brightness);
+            drawClippedRect(canvas, r, paint);
+            canvas->translate(FILTER_WIDTH + MARGIN, 0);
+        }
+        {
+            SkAutoTUnref<SkImageFilter> brightness(make_brightness(1.0f));
+            SkAutoTUnref<SkImageFilter> blue(make_mode_blue(brightness));
+            paint.setImageFilter(blue);
+            drawClippedRect(canvas, r, paint);
+            canvas->translate(FILTER_WIDTH + MARGIN, 0);
+        }
+        {
+            SkAutoTUnref<SkImageFilter> blur(make_blur(3.0f));
+            SkAutoTUnref<SkImageFilter> brightness(make_brightness(0.5f, blur));
+            paint.setImageFilter(brightness);
+            drawClippedRect(canvas, r, paint, 3);
+            canvas->translate(FILTER_WIDTH + MARGIN, 0);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new ColorFilterImageFilterGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/colormatrix.cpp b/gm/colormatrix.cpp
index 0a4acfd..16086f0 100644
--- a/gm/colormatrix.cpp
+++ b/gm/colormatrix.cpp
@@ -7,19 +7,51 @@
 
 #include "gm.h"
 #include "SkColorMatrixFilter.h"
+#include "SkGradientShader.h"
 
 #define WIDTH 500
 #define HEIGHT 500
 
+class SkOnce {
+public:
+    SkOnce() : fOnce(false) {};
+
+    bool once() const {
+        if (fOnce) {
+            return false;
+        }
+        fOnce = true;
+        return true;
+    }
+
+private:
+    mutable bool fOnce;
+};
+
+static void setColorMatrix(SkPaint* paint, const SkColorMatrix& matrix) {
+    paint->setColorFilter(SkNEW_ARGS(SkColorMatrixFilter, (matrix)))->unref();
+}
+
+static void setArray(SkPaint* paint, const SkScalar array[]) {
+    paint->setColorFilter(SkNEW_ARGS(SkColorMatrixFilter, (array)))->unref();
+}
+
 namespace skiagm {
 
 class ColorMatrixGM : public GM {
+    SkOnce fOnce;
+    void init() {
+        if (fOnce.once()) {
+            fSolidBitmap = this->createSolidBitmap(64, 64);
+            fTransparentBitmap = this->createTransparentBitmap(64, 64);
+        }
+    }
+
 public:
     ColorMatrixGM() {
         this->setBGColor(0xFF808080);
-        fBitmap = createBitmap(64, 64);
     }
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("colormatrix");
@@ -29,7 +61,7 @@
         return make_isize(WIDTH, HEIGHT);
     }
 
-    SkBitmap createBitmap(int width, int height) {
+    SkBitmap createSolidBitmap(int width, int height) {
         SkBitmap bm;
         bm.setConfig(SkBitmap::kARGB_8888_Config, width, height);
         bm.allocPixels();
@@ -39,74 +71,101 @@
             for (int x = 0; x < width; ++x) {
                 SkPaint paint;
                 paint.setColor(SkColorSetARGB(255, x * 255 / width, y * 255 / height, 0));
-                canvas.drawRect(SkRect::MakeXYWH(x, y, 1, 1), paint);
+                canvas.drawRect(SkRect::MakeXYWH(SkIntToScalar(x),
+                    SkIntToScalar(y), SK_Scalar1, SK_Scalar1), paint);
             }
         }
         return bm;
     }
+
+    // creates a bitmap with shades of transparent gray.
+    SkBitmap createTransparentBitmap(int width, int height) {
+        SkBitmap bm;
+        bm.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+        bm.allocPixels();
+        SkCanvas canvas(bm);
+        canvas.clear(0x0);
+
+        SkPoint pts[] = {{0, 0}, {SkIntToScalar(width), SkIntToScalar(height)}};
+        SkColor colors[] = {0x00000000, 0xFFFFFFFF};
+        SkPaint paint;
+        paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                                       SkShader::kClamp_TileMode))->unref();
+        canvas.drawRect(SkRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)), paint);
+        return bm;
+    }
+
     virtual void onDraw(SkCanvas* canvas) {
+        this->init();
 
         SkPaint paint;
         SkColorMatrix matrix;
-        SkColorMatrixFilter* filter = new SkColorMatrixFilter();
-        paint.setColorFilter(filter)->unref();
 
-        matrix.setIdentity();
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 0, 0, &paint);
+        paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+        const SkBitmap bmps[] = { fSolidBitmap, fTransparentBitmap };
 
-        matrix.setRotate(SkColorMatrix::kR_Axis, 90);
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 80, 0, &paint);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(bmps); ++i) {
 
-        matrix.setRotate(SkColorMatrix::kG_Axis, 90);
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 160, 0, &paint);
+            matrix.setIdentity();
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 0, 0, &paint);
 
-        matrix.setRotate(SkColorMatrix::kB_Axis, 90);
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 240, 0, &paint);
+            matrix.setRotate(SkColorMatrix::kR_Axis, 90);
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 80, 0, &paint);
 
-        matrix.setSaturation(SkFloatToScalar(0.0f));
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 0, 80, &paint);
+            matrix.setRotate(SkColorMatrix::kG_Axis, 90);
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 160, 0, &paint);
 
-        matrix.setSaturation(SkFloatToScalar(0.5f));
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 80, 80, &paint);
+            matrix.setRotate(SkColorMatrix::kB_Axis, 90);
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 240, 0, &paint);
 
-        matrix.setSaturation(SkFloatToScalar(1.0f));
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 160, 80, &paint);
+            matrix.setSaturation(SkFloatToScalar(0.0f));
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 0, 80, &paint);
 
-        matrix.setSaturation(SkFloatToScalar(2.0f));
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 240, 80, &paint);
+            matrix.setSaturation(SkFloatToScalar(0.5f));
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 80, 80, &paint);
 
-        matrix.setRGB2YUV();
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 0, 160, &paint);
+            matrix.setSaturation(SkFloatToScalar(1.0f));
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 160, 80, &paint);
 
-        matrix.setYUV2RGB();
-        filter->setMatrix(matrix);
-        canvas->drawBitmap(fBitmap, 80, 160, &paint);
+            matrix.setSaturation(SkFloatToScalar(2.0f));
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 240, 80, &paint);
 
-        SkScalar s1 = SK_Scalar1;
-        SkScalar s255 = SkIntToScalar(255);
-        // Move red into alpha, set color to white
-        SkScalar data[20] = {
-            0,  0, 0, 0, s255,
-            0,  0, 0, 0, s255,
-            0,  0, 0, 0, s255,
-            s1, 0, 0, 0, 0,
-        };
+            matrix.setRGB2YUV();
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 0, 160, &paint);
 
-        filter->setArray(data);
-        canvas->drawBitmap(fBitmap, 160, 160, &paint);
+            matrix.setYUV2RGB();
+            setColorMatrix(&paint, matrix);
+            canvas->drawBitmap(bmps[i], 80, 160, &paint);
+
+            SkScalar s1 = SK_Scalar1;
+            SkScalar s255 = SkIntToScalar(255);
+            // Move red into alpha, set color to white
+            SkScalar data[20] = {
+                0,  0, 0, 0, s255,
+                0,  0, 0, 0, s255,
+                0,  0, 0, 0, s255,
+                s1, 0, 0, 0, 0,
+            };
+
+            setArray(&paint, data);
+            canvas->drawBitmap(bmps[i], 160, 160, &paint);
+
+            canvas->translate(0, 240);
+        }
     }
-    
+
 private:
-    SkBitmap fBitmap;
+    SkBitmap fSolidBitmap;
+    SkBitmap fTransparentBitmap;
     typedef GM INHERITED;
 };
 
diff --git a/gm/complexclip.cpp b/gm/complexclip.cpp
index 30bb50b..86386b3 100644
--- a/gm/complexclip.cpp
+++ b/gm/complexclip.cpp
@@ -19,8 +19,11 @@
 
 class ComplexClipGM : public GM {
     bool fDoAAClip;
+    bool fDoSaveLayer;
 public:
-	ComplexClipGM(bool aaclip) : fDoAAClip(aaclip) {
+    ComplexClipGM(bool aaclip, bool saveLayer)
+    : fDoAAClip(aaclip)
+    , fDoSaveLayer(saveLayer) {
         this->setBGColor(0xFFDDDDDD);
 //        this->setBGColor(SkColorSetRGB(0xB0,0xDD,0xB0));
     }
@@ -29,7 +32,9 @@
 
     SkString onShortName() {
         SkString str;
-        str.printf("complexclip_%s", fDoAAClip ? "aa" : "bw");
+        str.printf("complexclip_%s%s",
+                   fDoAAClip ? "aa" : "bw",
+                   fDoSaveLayer ? "_layer" : "");
         return str;
     }
 
@@ -90,6 +95,24 @@
         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
         canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4);
 
+        if (fDoSaveLayer) {
+            // We want the layer to appear symmetric relative to actual
+            // device boundaries so we need to "undo" the effect of the
+            // scale and translate
+            SkRect bounds = SkRect::MakeLTRB(
+              SkFloatToScalar(4.0f/3.0f * -20),
+              SkFloatToScalar(4.0f/3.0f * -20),
+              SkFloatToScalar(4.0f/3.0f * (this->getISize().fWidth - 20)),
+              SkFloatToScalar(4.0f/3.0f * (this->getISize().fHeight - 20)));
+
+            bounds.inset(SkIntToScalar(100), SkIntToScalar(100));
+            SkPaint boundPaint;
+            boundPaint.setColor(SK_ColorRED);
+            boundPaint.setStyle(SkPaint::kStroke_Style);
+            canvas->drawRect(bounds, boundPaint);
+            canvas->saveLayer(&bounds, NULL);
+        }
+
         for (int invBits = 0; invBits < 4; ++invBits) {
             canvas->save();
             for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
@@ -129,6 +152,10 @@
             canvas->restore();
             canvas->translate(0, SkIntToScalar(250));
         }
+
+        if (fDoSaveLayer) {
+            canvas->restore();
+        }
     }
 private:
     void drawHairlines(SkCanvas* canvas, const SkPath& path,
@@ -141,7 +168,7 @@
         // draw path in hairline
         paint.setColor(gPathColor); paint.setAlpha(fade);
         canvas->drawPath(path, paint);
-        
+
         // draw clips in hair line
         paint.setColor(gClipAColor); paint.setAlpha(fade);
         canvas->drawPath(clipA, paint);
@@ -154,10 +181,17 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-static GM* gFact0(void*) { return new ComplexClipGM(false); }
-static GM* gFact1(void*) { return new ComplexClipGM(true); }
+// aliased and anti-aliased w/o a layer
+static GM* gFact0(void*) { return new ComplexClipGM(false, false); }
+static GM* gFact1(void*) { return new ComplexClipGM(true, false); }
+
+// aliased and anti-aliased w/ a layer
+static GM* gFact2(void*) { return new ComplexClipGM(false, true); }
+static GM* gFact3(void*) { return new ComplexClipGM(true, true); }
 
 static GMRegistry gReg0(gFact0);
 static GMRegistry gReg1(gFact1);
+static GMRegistry gReg2(gFact2);
+static GMRegistry gReg3(gFact3);
 
 }
diff --git a/gm/complexclip2.cpp b/gm/complexclip2.cpp
index d6b653e..ce1f0e0 100644
--- a/gm/complexclip2.cpp
+++ b/gm/complexclip2.cpp
@@ -15,39 +15,47 @@
 
 class ComplexClip2GM : public GM {
 public:
-    ComplexClip2GM() {
+    ComplexClip2GM(bool doPaths, bool antiAlias)
+    : fDoPaths(doPaths)
+    , fAntiAlias(antiAlias) {
         this->setBGColor(SkColorSetRGB(0xDD,0xA0,0xDD));
-        
-        SkScalar xA =  0 * SK_Scalar1;
-        SkScalar xB = 10 * SK_Scalar1;
-        SkScalar xC = 20 * SK_Scalar1;
-        SkScalar xD = 30 * SK_Scalar1;
-        SkScalar xE = 40 * SK_Scalar1;
-        SkScalar xF = 50 * SK_Scalar1;
 
-        SkScalar yA =  0 * SK_Scalar1;
-        SkScalar yB = 10 * SK_Scalar1;
-        SkScalar yC = 20 * SK_Scalar1;
-        SkScalar yD = 30 * SK_Scalar1;
-        SkScalar yE = 40 * SK_Scalar1;
-        SkScalar yF = 50 * SK_Scalar1;
+        // offset the rects a bit so we get antialiasing even in the rect case
+        SkScalar xA = SkFloatToScalar(0.65f);
+        SkScalar xB = SkFloatToScalar(10.65f);
+        SkScalar xC = SkFloatToScalar(20.65f);
+        SkScalar xD = SkFloatToScalar(30.65f);
+        SkScalar xE = SkFloatToScalar(40.65f);
+        SkScalar xF = SkFloatToScalar(50.65f);
+
+        SkScalar yA = SkFloatToScalar(0.65f);
+        SkScalar yB = SkFloatToScalar(10.65f);
+        SkScalar yC = SkFloatToScalar(20.65f);
+        SkScalar yD = SkFloatToScalar(30.65f);
+        SkScalar yE = SkFloatToScalar(40.65f);
+        SkScalar yF = SkFloatToScalar(50.65f);
 
         fWidth = xF - xA;
         fHeight = yF - yA;
 
         fRects[0].set(xB, yB, xE, yE);
+        fPaths[0].addRoundRect(fRects[0], SkIntToScalar(5), SkIntToScalar(5));
         fRectColors[0] = SK_ColorRED;
 
         fRects[1].set(xA, yA, xD, yD);
+        fPaths[1].addRoundRect(fRects[1], SkIntToScalar(5), SkIntToScalar(5));
         fRectColors[1] = SK_ColorGREEN;
 
         fRects[2].set(xC, yA, xF, yD);
+        fPaths[2].addRoundRect(fRects[2], SkIntToScalar(5), SkIntToScalar(5));
         fRectColors[2] = SK_ColorBLUE;
 
         fRects[3].set(xA, yC, xD, yF);
+        fPaths[3].addRoundRect(fRects[3], SkIntToScalar(5), SkIntToScalar(5));
         fRectColors[3] = SK_ColorYELLOW;
 
         fRects[4].set(xC, yC, xF, yF);
+        fPaths[4].addRoundRect(fRects[4], SkIntToScalar(5), SkIntToScalar(5));
         fRectColors[4] = SK_ColorCYAN;
 
         fTotalWidth = kCols * fWidth + SK_Scalar1 * (kCols + 1) * kPadX;
@@ -80,7 +88,15 @@
     static const int kPadY = 20;
 
     virtual SkString onShortName() {
-        return SkString("complexclip2");
+        if (!fDoPaths && !fAntiAlias) {
+            return SkString("complexclip2");
+        }
+
+        SkString str;
+        str.printf("complexclip2_%s_%s",
+                    fDoPaths ? "path" : "rect",
+                    fAntiAlias ? "aa" : "bw");
+        return str;
     }
 
     virtual SkISize onISize() {
@@ -99,24 +115,42 @@
         for (int i = 0; i < kRows; ++i) {
             for (int j = 0; j < kCols; ++j) {
                 canvas->save();
+
                 canvas->translate(kPadX * SK_Scalar1 + (fWidth + kPadX * SK_Scalar1)*j,
                                   kPadY * SK_Scalar1 + (fHeight + kPadY * SK_Scalar1)*i);
-                canvas->save();
-                for (int k = 0; k < 5; ++k) {
-                    canvas->clipRect(fRects[k], fOps[j*kRows+i][k]);
-                }
-                canvas->drawRect(SkRect::MakeWH(fWidth, fHeight), fillPaint);
-                canvas->restore();
+
+                // draw the original shapes first so we can see the
+                // antialiasing on the clipped draw
                 for (int k = 0; k < 5; ++k) {
                     rectPaint.setColor(fRectColors[k]);
-                    canvas->drawRect(fRects[k], rectPaint);
+                    if (fDoPaths) {
+                        canvas->drawPath(fPaths[k], rectPaint);
+                    } else {
+                        canvas->drawRect(fRects[k], rectPaint);
+                    }
                 }
+
+                for (int k = 0; k < 5; ++k) {
+                    if (fDoPaths) {
+                        canvas->clipPath(fPaths[k],
+                                         fOps[j*kRows+i][k],
+                                         fAntiAlias);
+                    } else {
+                        canvas->clipRect(fRects[k],
+                                         fOps[j*kRows+i][k],
+                                         fAntiAlias);
+                    }
+                }
+                canvas->drawRect(SkRect::MakeWH(fWidth, fHeight), fillPaint);
                 canvas->restore();
             }
         }
     }
 private:
+    bool fDoPaths;
+    bool fAntiAlias;
     SkRect fRects[5];
+    SkPath fPaths[5];
     SkColor fRectColors[5];
     SkRegion::Op fOps[kRows * kCols][5];
     SkScalar fWidth;
@@ -129,7 +163,20 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
-static GM* MyFactory(void*) { return new ComplexClip2GM; }
+// bw rects
+static GM* MyFactory(void*) { return new ComplexClip2GM(false, false); }
 static GMRegistry reg(MyFactory);
 
+// bw paths
+static GM* MyFactory2(void*) { return new ComplexClip2GM(true, false); }
+static GMRegistry reg2(MyFactory2);
+
+// aa rects
+static GM* MyFactory3(void*) { return new ComplexClip2GM(false, true); }
+static GMRegistry reg3(MyFactory3);
+
+// aa paths
+static GM* MyFactory4(void*) { return new ComplexClip2GM(true, true); }
+static GMRegistry reg4(MyFactory4);
+
 }
diff --git a/gm/composeshader.cpp b/gm/composeshader.cpp
new file mode 100644
index 0000000..a6643a6
--- /dev/null
+++ b/gm/composeshader.cpp
@@ -0,0 +1,82 @@
+
+/*
+ * 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 "SkComposeShader.h"
+#include "SkGradientShader.h"
+#include "SkGraphics.h"
+#include "SkShader.h"
+#include "SkString.h"
+#include "SkXfermode.h"
+
+namespace skiagm {
+
+class ShaderGM : public GM {
+public:
+    ShaderGM() {
+        SkPoint pts[2];
+        SkColor colors[2];
+
+        pts[0].set(0, 0);
+        pts[1].set(SkIntToScalar(100), 0);
+        colors[0] = SK_ColorRED;
+        colors[1] = SK_ColorBLUE;
+        SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                                           SkShader::kClamp_TileMode);
+
+        pts[0].set(0, 0);
+        pts[1].set(0, SkIntToScalar(100));
+        colors[0] = SK_ColorBLACK;
+        colors[1] = SkColorSetARGB(0x80, 0, 0, 0);
+        SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                                           SkShader::kClamp_TileMode);
+
+        SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
+
+        fShader = new SkComposeShader(shaderA, shaderB, mode);
+        shaderA->unref();
+        shaderB->unref();
+        mode->unref();
+    }
+
+    virtual ~ShaderGM() {
+        SkSafeUnref(fShader);
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("composeshader");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return make_isize(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+
+        SkPaint paint;
+
+        paint.setColor(SK_ColorGREEN);
+        canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
+        paint.setShader(fShader);
+        canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
+    }
+
+private:
+    SkShader*   fShader;
+    typedef GM INHERITED ;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ShaderGM; }
+static GMRegistry reg(MyFactory);
+
+} // namespace
diff --git a/gm/convexpaths.cpp b/gm/convexpaths.cpp
index 2c719e8..2c4753d 100644
--- a/gm/convexpaths.cpp
+++ b/gm/convexpaths.cpp
@@ -9,13 +9,28 @@
 #include "SkRandom.h"
 #include "SkTArray.h"
 
+class SkOnce : SkNoncopyable {
+public:
+    SkOnce() { fDidOnce = false; }
+
+    bool needToDo() const { return !fDidOnce; }
+    bool alreadyDone() const { return fDidOnce; }
+    void accomplished() {
+        SkASSERT(!fDidOnce);
+        fDidOnce = true;
+    }
+
+private:
+    bool fDidOnce;
+};
+
 namespace skiagm {
 
 class ConvexPathsGM : public GM {
+    SkOnce fOnce;
 public:
     ConvexPathsGM() {
         this->setBGColor(0xFF000000);
-        this->makePaths();
     }
 
 protected:
@@ -25,10 +40,14 @@
 
 
     virtual SkISize onISize() {
-        return make_isize(1200, 900);
+        return make_isize(1200, 1100);
     }
 
     void makePaths() {
+        if (fOnce.alreadyDone()) {
+            return;
+        }
+        fOnce.accomplished();
         // CW
         fPaths.push_back().moveTo(0, 0);
         fPaths.back().quadTo(50 * SK_Scalar1, 100 * SK_Scalar1,
@@ -106,14 +125,13 @@
         fPaths.back().lineTo(98 * SK_Scalar1, 100 * SK_Scalar1);
         fPaths.back().lineTo(3 * SK_Scalar1, 96 * SK_Scalar1);
 
-        /*
-        It turns out arcTos are not automatically marked as convex and they
-        may in fact be ever so slightly concave.
-        fPaths.push_back().arcTo(SkRect::MakeXYWH(0, 0,
-                                                  50 * SK_Scalar1,
-                                                  100 * SK_Scalar1),
-                                 25 * SK_Scalar1,  130 * SK_Scalar1, false);
-        */
+
+        //It turns out arcTos are not automatically marked as convex and they
+        //may in fact be ever so slightly concave.
+        //fPaths.push_back().arcTo(SkRect::MakeXYWH(0, 0,
+        //                                          50 * SK_Scalar1,
+        //                                          100 * SK_Scalar1),
+        //                         25 * SK_Scalar1,  130 * SK_Scalar1, false);
 
         // cubics
         fPaths.push_back().cubicTo( 1 * SK_Scalar1,  1 * SK_Scalar1,
@@ -123,6 +141,51 @@
                                     20 * SK_Scalar1, 100 * SK_Scalar1,
                                      0 * SK_Scalar1,   0 * SK_Scalar1);
 
+        // path that has a cubic with a repeated first control point and
+        // a repeated last control point.
+        fPaths.push_back().moveTo(SK_Scalar1 * 10, SK_Scalar1 * 10);
+        fPaths.back().cubicTo(10 * SK_Scalar1, 10 * SK_Scalar1,
+                              10 * SK_Scalar1, 0,
+                              20 * SK_Scalar1, 0);
+        fPaths.back().lineTo(40 * SK_Scalar1, 0);
+        fPaths.back().cubicTo(40 * SK_Scalar1, 0,
+                              50 * SK_Scalar1, 0,
+                              50 * SK_Scalar1, 10 * SK_Scalar1);
+
+        // path that has two cubics with repeated middle control points.
+        fPaths.push_back().moveTo(SK_Scalar1 * 10, SK_Scalar1 * 10);
+        fPaths.back().cubicTo(10 * SK_Scalar1, 0,
+                              10 * SK_Scalar1, 0,
+                              20 * SK_Scalar1, 0);
+        fPaths.back().lineTo(40 * SK_Scalar1, 0);
+        fPaths.back().cubicTo(50 * SK_Scalar1, 0,
+                              50 * SK_Scalar1, 0,
+                              50 * SK_Scalar1, 10 * SK_Scalar1);
+
+        // cubic where last three points are almost a line
+        fPaths.push_back().moveTo(0, 228 * SK_Scalar1 / 8);
+        fPaths.back().cubicTo(628 * SK_Scalar1 / 8, 82 * SK_Scalar1 / 8,
+                              1255 * SK_Scalar1 / 8, 141 * SK_Scalar1 / 8,
+                              1883 * SK_Scalar1 / 8, 202 * SK_Scalar1 / 8);
+
+        // flat cubic where the at end point tangents both point outward.
+        fPaths.push_back().moveTo(10 * SK_Scalar1, 0);
+        fPaths.back().cubicTo(0, SK_Scalar1,
+                              30 * SK_Scalar1, SK_Scalar1,
+                              20 * SK_Scalar1, 0);
+
+        // flat cubic where initial tangent is in, end tangent out
+        fPaths.push_back().moveTo(0, 0 * SK_Scalar1);
+        fPaths.back().cubicTo(10 * SK_Scalar1, SK_Scalar1,
+                              30 * SK_Scalar1, SK_Scalar1,
+                              20 * SK_Scalar1, 0);
+
+        // flat cubic where initial tangent is out, end tangent in
+        fPaths.push_back().moveTo(10 * SK_Scalar1, 0);
+        fPaths.back().cubicTo(0, SK_Scalar1,
+                              20 * SK_Scalar1, SK_Scalar1,
+                              30 * SK_Scalar1, 0);
+
         // triangle where one edge is a degenerate quad
         fPaths.push_back().moveTo(SkFloatToScalar(8.59375f), 45 * SK_Scalar1);
         fPaths.back().quadTo(SkFloatToScalar(16.9921875f),   45 * SK_Scalar1,
@@ -133,7 +196,7 @@
         // point degenerate
         fPaths.push_back().moveTo(50 * SK_Scalar1, 50 * SK_Scalar1);
         fPaths.back().lineTo(50 * SK_Scalar1, 50 * SK_Scalar1);
-        
+
         fPaths.push_back().moveTo(50 * SK_Scalar1, 50 * SK_Scalar1);
         fPaths.back().quadTo(50 * SK_Scalar1, 50 * SK_Scalar1,
                              50 * SK_Scalar1, 50 * SK_Scalar1);
@@ -169,6 +232,7 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
+        this->makePaths();
 
     SkPaint paint;
     paint.setAntiAlias(true);
@@ -187,7 +251,7 @@
         canvas->restore();
     }
     }
-    
+
 private:
     typedef GM INHERITED;
     SkTArray<SkPath> fPaths;
@@ -199,4 +263,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/cubicpaths.cpp b/gm/cubicpaths.cpp
index 7cd3df1..1fc13c0 100644
--- a/gm/cubicpaths.cpp
+++ b/gm/cubicpaths.cpp
@@ -19,9 +19,9 @@
     SkString onShortName() {
         return SkString("cubicpath");
     }
-        
+
     SkISize onISize() { return make_isize(1240, 390); }
-    
+
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
                   SkPaint::Style style, SkPath::FillType fill,
@@ -38,7 +38,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         struct FillAndName {
             SkPath::FillType fFill;
@@ -111,19 +111,19 @@
                     if (0 < style) {
                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                     }
-        
+
                     SkColor color = 0xff007000;
                     this->drawPath(path.fPath, canvas, color, rect,
                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                     gFills[fill].fFill, SK_Scalar1*10);
-        
+
                     SkPaint rectPaint;
                     rectPaint.setColor(SK_ColorBLACK);
                     rectPaint.setStyle(SkPaint::kStroke_Style);
                     rectPaint.setStrokeWidth(-1);
                     rectPaint.setAntiAlias(true);
                     canvas->drawRect(rect, rectPaint);
-        
+
                     SkPaint labelPaint;
                     labelPaint.setColor(color);
                     labelPaint.setAntiAlias(true);
@@ -149,7 +149,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -162,9 +162,9 @@
     SkString onShortName() {
         return SkString("cubicclosepath");
     }
-        
+
     SkISize onISize() { return make_isize(1240, 390); }
-    
+
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
                   SkPaint::Style style, SkPath::FillType fill,
@@ -181,7 +181,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         struct FillAndName {
             SkPath::FillType fFill;
@@ -255,19 +255,19 @@
                     if (0 < style) {
                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                     }
-        
+
                     SkColor color = 0xff007000;
                     this->drawPath(path.fPath, canvas, color, rect,
                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                     gFills[fill].fFill, SK_Scalar1*10);
-        
+
                     SkPaint rectPaint;
                     rectPaint.setColor(SK_ColorBLACK);
                     rectPaint.setStyle(SkPaint::kStroke_Style);
                     rectPaint.setStrokeWidth(-1);
                     rectPaint.setAntiAlias(true);
                     canvas->drawRect(rect, rectPaint);
-        
+
                     SkPaint labelPaint;
                     labelPaint.setColor(color);
                     labelPaint.setAntiAlias(true);
@@ -293,7 +293,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/dashcubics.cpp b/gm/dashcubics.cpp
new file mode 100644
index 0000000..5d874d4
--- /dev/null
+++ b/gm/dashcubics.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "SkPath.h"
+#include "SkParsePath.h"
+#include "SkDashPathEffect.h"
+
+/*
+ *  Inspired by http://code.google.com/p/chromium/issues/detail?id=112145
+ */
+
+class DashCubicsGM : public skiagm::GM {
+public:
+    DashCubicsGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("dashcubics");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPath path;
+        const char* d = "M 337,98 C 250,141 250,212 250,212 C 250,212 250,212 250,212"
+        "C 250,212 250,212 250,212 C 250,212 250,141 163,98 C 156,195 217,231 217,231"
+        "C 217,231 217,231 217,231 C 217,231 217,231 217,231 C 217,231 156,195 75,250"
+        "C 156,305 217,269 217,269 C 217,269 217,269 217,269 C 217,269 217,269 217,269"
+        "C 217,269 156,305 163,402 C 250,359 250,288 250,288 C 250,288 250,288 250,288"
+        "C 250,288 250,288 250,288 C 250,288 250,359 338,402 C 345,305 283,269 283,269"
+        "C 283,269 283,269 283,269 C 283,269 283,269 283,269 C 283,269 345,305 425,250"
+        "C 344,195 283,231 283,231 C 283,231 283,231 283,231 C 283,231 283,231 283,231"
+        "C 283,231 344,195 338,98";
+
+        SkParsePath::FromSVGString(d, &path);
+
+        SkScalar intervals[] = { 5, 10 };
+        SkPathEffect* pe = new SkDashPathEffect(intervals, 2, 0);
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+
+        paint.setStrokeWidth(42);
+        canvas->drawPath(path, paint);
+
+        paint.setColor(SK_ColorRED);
+        paint.setStrokeWidth(21);
+        paint.setPathEffect(pe)->unref();
+        canvas->drawPath(path, paint);
+
+        paint.setColor(SK_ColorGREEN);
+        paint.setPathEffect(NULL);
+        paint.setStrokeWidth(0);
+        canvas->drawPath(path, paint);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new DashCubicsGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/dashing.cpp b/gm/dashing.cpp
new file mode 100644
index 0000000..7fe3029
--- /dev/null
+++ b/gm/dashing.cpp
@@ -0,0 +1,310 @@
+/*
+ * 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 "SkDashPathEffect.h"
+
+static void drawline(SkCanvas* canvas, int on, int off, const SkPaint& paint,
+                     SkScalar finalX = SkIntToScalar(600)) {
+    SkPaint p(paint);
+
+    const SkScalar intervals[] = {
+        SkIntToScalar(on),
+        SkIntToScalar(off),
+    };
+
+    p.setPathEffect(new SkDashPathEffect(intervals, 2, 0))->unref();
+    canvas->drawLine(0, 0, finalX, 0, p);
+}
+
+// earlier bug stopped us from drawing very long single-segment dashes, because
+// SkPathMeasure was skipping very small delta-T values (nearlyzero). This is
+// now fixes, so this giant dash should appear.
+static void show_giant_dash(SkCanvas* canvas) {
+    SkPaint paint;
+
+    drawline(canvas, 1, 1, paint, SkIntToScalar(20 * 1000));
+}
+
+class DashingGM : public skiagm::GM {
+public:
+    DashingGM() {}
+
+protected:
+    SkString onShortName() {
+        return SkString("dashing");
+    }
+
+    SkISize onISize() { return skiagm::make_isize(640, 300); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        static const struct {
+            int fOnInterval;
+            int fOffInterval;
+        } gData[] = {
+            { 1, 1 },
+            { 4, 1 },
+        };
+
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        canvas->translate(0, SK_ScalarHalf);
+
+        for (int width = 0; width <= 2; ++width) {
+            for (size_t data = 0; data < SK_ARRAY_COUNT(gData); ++data) {
+                for (int aa = 0; aa <= 1; ++aa) {
+                    int w = width * width * width;
+                    paint.setAntiAlias(SkToBool(aa));
+                    paint.setStrokeWidth(SkIntToScalar(w));
+
+                    int scale = w ? w : 1;
+
+                    drawline(canvas, gData[data].fOnInterval * scale,
+                             gData[data].fOffInterval * scale,
+                             paint);
+                    canvas->translate(0, SkIntToScalar(20));
+                }
+            }
+        }
+
+        show_giant_dash(canvas);
+    }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void make_unit_star(SkPath* path, int n) {
+    SkScalar rad = -SK_ScalarPI / 2;
+    const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
+
+    path->moveTo(0, -SK_Scalar1);
+    for (int i = 1; i < n; i++) {
+        rad += drad;
+        SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
+        path->lineTo(cosV, sinV);
+    }
+    path->close();
+}
+
+static void make_path_line(SkPath* path, const SkRect& bounds) {
+    path->moveTo(bounds.left(), bounds.top());
+    path->lineTo(bounds.right(), bounds.bottom());
+}
+
+static void make_path_rect(SkPath* path, const SkRect& bounds) {
+    path->addRect(bounds);
+}
+
+static void make_path_oval(SkPath* path, const SkRect& bounds) {
+    path->addOval(bounds);
+}
+
+static void make_path_star(SkPath* path, const SkRect& bounds) {
+    make_unit_star(path, 5);
+    SkMatrix matrix;
+    matrix.setRectToRect(path->getBounds(), bounds, SkMatrix::kCenter_ScaleToFit);
+    path->transform(matrix);
+}
+
+class Dashing2GM : public skiagm::GM {
+public:
+    Dashing2GM() {}
+
+protected:
+    SkString onShortName() {
+        return SkString("dashing2");
+    }
+
+    SkISize onISize() { return skiagm::make_isize(640, 480); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        static const int gIntervals[] = {
+            3,  // 3 dashes: each count [0] followed by intervals [1..count]
+            2,  10, 10,
+            4,  20, 5, 5, 5,
+            2,  2, 2
+        };
+
+        void (*gProc[])(SkPath*, const SkRect&) = {
+            make_path_line, make_path_rect, make_path_oval, make_path_star,
+        };
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SkIntToScalar(6));
+
+        SkRect bounds = SkRect::MakeWH(SkIntToScalar(120), SkIntToScalar(120));
+        bounds.offset(SkIntToScalar(20), SkIntToScalar(20));
+        SkScalar dx = bounds.width() * 4 / 3;
+        SkScalar dy = bounds.height() * 4 / 3;
+
+        const int* intervals = &gIntervals[1];
+        for (int y = 0; y < gIntervals[0]; ++y) {
+            SkScalar vals[SK_ARRAY_COUNT(gIntervals)];  // more than enough
+            int count = *intervals++;
+            for (int i = 0; i < count; ++i) {
+                vals[i] = SkIntToScalar(*intervals++);
+            }
+            SkScalar phase = vals[0] / 2;
+            paint.setPathEffect(new SkDashPathEffect(vals, count, phase))->unref();
+
+            for (size_t x = 0; x < SK_ARRAY_COUNT(gProc); ++x) {
+                SkPath path;
+                SkRect r = bounds;
+                r.offset(x * dx, y * dy);
+                gProc[x](&path, r);
+
+                canvas->drawPath(path, paint);
+            }
+        }
+    }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+// Test out the on/off line dashing Chrome if fond of
+class Dashing3GM : public skiagm::GM {
+public:
+    Dashing3GM() {}
+
+protected:
+    SkString onShortName() {
+        return SkString("dashing3");
+    }
+
+    SkISize onISize() { return skiagm::make_isize(640, 480); }
+
+    // Draw a 100x100 block of dashed lines. The horizontal ones are BW
+    // while the vertical ones are AA.
+    void drawDashedLines(SkCanvas* canvas,
+                         SkScalar lineLength,
+                         SkScalar phase,
+                         SkScalar dashLength,
+                         int strokeWidth,
+                         bool circles) {
+        SkPaint p;
+        p.setColor(SK_ColorBLACK);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setStrokeWidth(SkIntToScalar(strokeWidth));
+
+        if (circles) {
+            p.setStrokeCap(SkPaint::kRound_Cap);
+        }
+
+        SkScalar intervals[2] = { dashLength, dashLength };
+
+        p.setPathEffect(new SkDashPathEffect(intervals, 2, phase, false));
+
+        SkPoint pts[2];
+
+        for (int y = 0; y < 100; y += 10*strokeWidth) {
+            pts[0].set(0, SkIntToScalar(y));
+            pts[1].set(lineLength, SkIntToScalar(y));
+
+            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
+        }
+
+        p.setAntiAlias(true);
+
+        for (int x = 0; x < 100; x += 14*strokeWidth) {
+            pts[0].set(SkIntToScalar(x), 0);
+            pts[1].set(SkIntToScalar(x), lineLength);
+
+            canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p);
+        }
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        // 1on/1off 1x1 squares with phase of 0 - points fastpath
+        canvas->save();
+            canvas->translate(2, 0);
+            this->drawDashedLines(canvas, 100, 0, SK_Scalar1, 1, false);
+        canvas->restore();
+
+        // 1on/1off 1x1 squares with phase of .5 - rects fastpath (due to partial squares)
+        canvas->save();
+            canvas->translate(112, 0);
+            this->drawDashedLines(canvas, 100, SK_ScalarHalf, SK_Scalar1, 1, false);
+        canvas->restore();
+
+        // 1on/1off 1x1 squares with phase of 1 - points fastpath
+        canvas->save();
+            canvas->translate(222, 0);
+            this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false);
+        canvas->restore();
+
+        // 1on/1off 1x1 squares with phase of 1 and non-integer length - rects fastpath
+        canvas->save();
+            canvas->translate(332, 0);
+            this->drawDashedLines(canvas, 99.5f, SK_ScalarHalf, SK_Scalar1, 1, false);
+        canvas->restore();
+
+        // 255on/255off 1x1 squares with phase of 0 - rects fast path
+        canvas->save();
+            canvas->translate(446, 0);
+            this->drawDashedLines(canvas, 100, 0, SkIntToScalar(255), 1, false);
+        canvas->restore();
+
+        // 1on/1off 3x3 squares with phase of 0 - points fast path
+        canvas->save();
+            canvas->translate(2, 110);
+            this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, false);
+        canvas->restore();
+
+        // 1on/1off 3x3 squares with phase of 1.5 - rects fast path
+        canvas->save();
+            canvas->translate(112, 110);
+            this->drawDashedLines(canvas, 100, SkFloatToScalar(1.5f), SkIntToScalar(3), 3, false);
+        canvas->restore();
+
+        // 1on/1off 1x1 circles with phase of 1 - no fast path yet
+        canvas->save();
+            canvas->translate(2, 220);
+            this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, true);
+        canvas->restore();
+
+        // 1on/1off 3x3 circles with phase of 1 - no fast path yet
+        canvas->save();
+            canvas->translate(112, 220);
+            this->drawDashedLines(canvas, 100, 0, SkIntToScalar(3), 3, true);
+        canvas->restore();
+
+        // 1on/1off 1x1 squares with rotation - should break fast path
+        canvas->save();
+            canvas->translate(332+SK_ScalarRoot2Over2*100, 110+SK_ScalarRoot2Over2*100);
+            canvas->rotate(45);
+            canvas->translate(-50, -50);
+
+            this->drawDashedLines(canvas, 100, SK_Scalar1, SK_Scalar1, 1, false);
+        canvas->restore();
+
+        // 3on/3off 3x1 rects - should use rect fast path regardless of phase
+        for (int phase = 0; phase <= 3; ++phase) {
+            canvas->save();
+                canvas->translate(SkIntToScalar(phase*110+2),
+                                  SkIntToScalar(330));
+                this->drawDashedLines(canvas, 100, SkIntToScalar(phase), SkIntToScalar(3), 1, false);
+            canvas->restore();
+        }
+    }
+
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* F0(void*) { return new DashingGM; }
+static skiagm::GM* F1(void*) { return new Dashing2GM; }
+static skiagm::GM* F2(void*) { return new Dashing3GM; }
+
+static skiagm::GMRegistry gR0(F0);
+static skiagm::GMRegistry gR1(F1);
+static skiagm::GMRegistry gR2(F2);
diff --git a/gm/degeneratesegments.cpp b/gm/degeneratesegments.cpp
index 63d9dba..2e19d05 100644
--- a/gm/degeneratesegments.cpp
+++ b/gm/degeneratesegments.cpp
@@ -25,11 +25,11 @@
     SkString onShortName() {
         return SkString("degeneratesegments");
     }
-        
+
     SkISize onISize() { return make_isize(896, 930); }
 
     typedef SkPoint (*AddSegmentFunc)(SkPath&, SkPoint&);
-    
+
     // We need to use explicit commands here, instead of addPath, because we
     // do not want the moveTo that is added at the beginning of a path to
     // appear in the appended path.
@@ -205,7 +205,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
     static const AddSegmentFunc gSegmentFunctions[] = {
         AddMove,
@@ -315,7 +315,7 @@
                 if (0 < column) {
                     canvas->translate(rect.width() + 4*SK_Scalar1, 0);
                 }
-        
+
                 SkColor color = 0xff007000;
                 StyleAndName style = gStyles[(rand.nextU() >> 16) % numStyles];
                 CapAndName cap = gCaps[(rand.nextU() >> 16) % numCaps];
@@ -387,7 +387,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/displacement.cpp b/gm/displacement.cpp
new file mode 100644
index 0000000..5317a31
--- /dev/null
+++ b/gm/displacement.cpp
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkDisplacementMapEffect.h"
+#include "SkBitmapSource.h"
+
+namespace skiagm {
+
+class DisplacementMapGM : public GM {
+public:
+    DisplacementMapGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("displacement");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFF884422);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "g";
+        canvas.drawText(str, strlen(str), SkIntToScalar(15), SkIntToScalar(55), paint);
+    }
+
+    void make_checkerboard() {
+        fCheckerboard.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fCheckerboard.allocPixels();
+        SkDevice device(fCheckerboard);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint darkPaint;
+        darkPaint.setColor(0xFF804020);
+        SkPaint lightPaint;
+        lightPaint.setColor(0xFF244484);
+        for (int y = 0; y < 80; y += 16) {
+          for (int x = 0; x < 80; x += 16) {
+            canvas.save();
+            canvas.translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas.drawRect(SkRect::MakeXYWH(0, 0, 8, 8), darkPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 0, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(0, 8, 8, 8), lightPaint);
+            canvas.drawRect(SkRect::MakeXYWH(8, 8, 8, 8), darkPaint);
+            canvas.restore();
+          }
+        }
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(500, 200);
+    }
+
+    void drawClippedBitmap(SkCanvas* canvas, int x, int y, const SkPaint& paint) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            make_checkerboard();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkPaint paint;
+        SkAutoTUnref<SkImageFilter> displ(SkNEW_ARGS(SkBitmapSource, (fCheckerboard)));
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kG_ChannelSelectorType, 0.0f, displ)))->unref();
+        drawClippedBitmap(canvas, 0, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kB_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.2f, displ)))->unref();
+        drawClippedBitmap(canvas, 100, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kB_ChannelSelectorType, 0.4f, displ)))->unref();
+        drawClippedBitmap(canvas, 200, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kG_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.6f, displ)))->unref();
+        drawClippedBitmap(canvas, 300, 0, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.8f, displ)))->unref();
+        drawClippedBitmap(canvas, 400, 0, paint);
+
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kG_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 0, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kB_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 100, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kB_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 200, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kG_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 300, 100, paint);
+        paint.setImageFilter(SkNEW_ARGS(SkDisplacementMapEffect,
+            (SkDisplacementMapEffect::kR_ChannelSelectorType,
+             SkDisplacementMapEffect::kA_ChannelSelectorType, 0.5f, displ)))->unref();
+        drawClippedBitmap(canvas, 400, 100, paint);
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap, fCheckerboard;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DisplacementMapGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/distantclip.cpp b/gm/distantclip.cpp
new file mode 100644
index 0000000..6fd1420
--- /dev/null
+++ b/gm/distantclip.cpp
@@ -0,0 +1,77 @@
+
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+
+namespace skiagm {
+
+class DistantClipGM : public GM {
+public:
+    DistantClipGM() { }
+
+protected:
+
+    SkString onShortName() {
+        return SkString("distantclip");
+    }
+
+    SkISize onISize() { return make_isize(100, 100); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        int offset = 35000;
+        int extents = 1000;
+
+        // We record a picture of huge vertical extents in which we clear the canvas to red, create
+        // a 'extents' by 'extents' round rect clip at a vertical offset of 'offset', then draw
+        // green into that.
+        SkPicture pict;
+        SkCanvas* rec = pict.beginRecording(100, offset + extents);
+        rec->drawColor(0xffff0000);
+        rec->save();
+        SkRect r = {
+            SkIntToScalar(-extents),
+            SkIntToScalar(offset - extents),
+            SkIntToScalar(extents),
+            SkIntToScalar(offset + extents)
+        };
+        SkPath p;
+        p.addRoundRect(r, 5, 5);
+        rec->clipPath(p, SkRegion::kIntersect_Op, true);
+        rec->drawColor(0xff00ff00);
+        rec->restore();
+        pict.endRecording();
+
+        // Next we play that picture into another picture of the same size.
+        SkPicture pict2;
+        pict.draw(pict2.beginRecording(100, offset + extents));
+        pict2.endRecording();
+
+        // Finally we play the part of that second picture that should be green into the canvas.
+        canvas->save();
+        canvas->translate(SkIntToScalar(extents / 2),
+                          SkIntToScalar(-(offset - extents / 2)));
+        pict2.draw(canvas);
+        canvas->restore();
+
+        // If the image is red, we erroneously decided the clipPath was empty and didn't record
+        // the green drawColor, if it's green we're all good.
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new DistantClipGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/drawbitmaprect.cpp b/gm/drawbitmaprect.cpp
index 5832a9c..16cd6e4 100644
--- a/gm/drawbitmaprect.cpp
+++ b/gm/drawbitmaprect.cpp
@@ -8,6 +8,7 @@
 #include "gm.h"
 #include "SkShader.h"
 #include "SkColorPriv.h"
+#include "SkBlurMaskFilter.h"
 
 // effects
 #include "SkGradientShader.h"
@@ -15,10 +16,25 @@
 
 namespace skiagm {
 
+static SkBitmap make_chessbm(int w, int h) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config , w, h);
+    bm.allocPixels();
+
+    for (int y = 0; y < bm.height(); y++) {
+        uint32_t* p = bm.getAddr32(0, y);
+        for (int x = 0; x < bm.width(); x++) {
+            p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
+        }
+    }
+    bm.unlockPixels();
+    return bm;
+}
+
 static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
     bm->setConfig(config, w, h);
     bm->allocPixels();
-    bm->eraseColor(0);
+    bm->eraseColor(SK_ColorTRANSPARENT);
 
     SkCanvas    canvas(*bm);
 
@@ -71,9 +87,9 @@
     SkString onShortName() {
         return SkString("drawbitmaprect");
     }
-    
+
     SkISize onISize() { return make_isize(gSize, gSize); }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         static const int kBmpSize = 2048;
         if (fLargeBitmap.isNull()) {
@@ -140,6 +156,26 @@
                 }
             }
         }
+
+        {
+            // test the following code path:
+            // SkGpuDevice::drawPath() -> SkGpuDevice::drawWithMaskFilter()
+            SkIRect srcRect;
+            SkPaint paint;
+            SkBitmap bm;
+
+            bm = make_chessbm(5, 5);
+            paint.setFilterBitmap(true);
+
+            srcRect.setXYWH(1, 1, 3, 3);
+            SkMaskFilter* mf = SkBlurMaskFilter::Create(
+                5,
+                SkBlurMaskFilter::kNormal_BlurStyle,
+                SkBlurMaskFilter::kHighQuality_BlurFlag |
+                SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
+            paint.setMaskFilter(mf)->unref();
+            canvas->drawBitmapRect(bm, &srcRect, dstRect, &paint);
+        }
     }
 
 private:
@@ -148,8 +184,8 @@
 
 //////////////////////////////////////////////////////////////////////////////
 
+#ifndef SK_BUILD_FOR_ANDROID
 static GM* MyFactory(void*) { return new DrawBitmapRectGM; }
 static GMRegistry reg(MyFactory);
-
+#endif
 }
-
diff --git a/gm/drawlooper.cpp b/gm/drawlooper.cpp
new file mode 100644
index 0000000..58c691c
--- /dev/null
+++ b/gm/drawlooper.cpp
@@ -0,0 +1,101 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkLayerDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+
+#define WIDTH   200
+#define HEIGHT  200
+
+class DrawLooperGM : public skiagm::GM {
+public:
+    DrawLooperGM() : fLooper(NULL) {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+    virtual ~DrawLooperGM() {
+        SkSafeUnref(fLooper);
+    }
+
+protected:
+    virtual SkISize onISize() {
+        return SkISize::Make(520, 160);
+    }
+
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("drawlooper");
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        this->init();
+
+        SkPaint  paint;
+        paint.setTextSize(SkIntToScalar(72));
+        paint.setLooper(fLooper);
+
+        canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
+                           SkIntToScalar(30), paint);
+
+        canvas->drawRectCoords(SkIntToScalar(150), SkIntToScalar(50),
+                               SkIntToScalar(200), SkIntToScalar(100), paint);
+
+        canvas->drawText("Looper", 6, SkIntToScalar(230), SkIntToScalar(100),
+                         paint);
+    }
+
+private:
+    SkLayerDrawLooper*   fLooper;
+
+    void init() {
+        if (fLooper) return;
+
+        static const struct {
+            SkColor         fColor;
+            SkPaint::Style  fStyle;
+            SkScalar        fWidth;
+            SkScalar        fOffset;
+            int             fBlur;
+        } gParams[] = {
+            { SK_ColorWHITE, SkPaint::kStroke_Style, SkIntToScalar(1)*3/4, 0, 0 },
+            { SK_ColorRED, SkPaint::kStroke_Style, SkIntToScalar(4), 0, 0 },
+            { SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0 },
+            { 0x88000000, SkPaint::kFill_Style, 0, SkIntToScalar(10), 3 }
+        };
+
+        fLooper = new SkLayerDrawLooper;
+
+        SkLayerDrawLooper::LayerInfo info;
+        info.fFlagsMask = SkPaint::kAntiAlias_Flag;
+        info.fPaintBits = SkLayerDrawLooper::kStyle_Bit | SkLayerDrawLooper::kMaskFilter_Bit;
+        info.fColorMode = SkXfermode::kSrc_Mode;
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gParams); i++) {
+            info.fOffset.set(gParams[i].fOffset, gParams[i].fOffset);
+            SkPaint* paint = fLooper->addLayer(info);
+            paint->setAntiAlias(true);
+            paint->setColor(gParams[i].fColor);
+            paint->setStyle(gParams[i].fStyle);
+            paint->setStrokeWidth(gParams[i].fWidth);
+            if (gParams[i].fBlur > 0) {
+                SkMaskFilter* mf = SkBlurMaskFilter::Create(SkIntToScalar(gParams[i].fBlur),
+                                                            SkBlurMaskFilter::kNormal_BlurStyle);
+                paint->setMaskFilter(mf)->unref();
+            }
+        }
+    }
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new DrawLooperGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/emptypath.cpp b/gm/emptypath.cpp
index c52932e..df314a3 100644
--- a/gm/emptypath.cpp
+++ b/gm/emptypath.cpp
@@ -20,7 +20,7 @@
     SkString onShortName() {
         return SkString("emptypath");
     }
-        
+
     SkISize onISize() { return make_isize(600, 280); }
 
     void drawEmpty(SkCanvas* canvas,
@@ -38,7 +38,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         struct FillAndName {
             SkPath::FillType fFill;
@@ -120,7 +120,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/extractbitmap.cpp b/gm/extractbitmap.cpp
new file mode 100644
index 0000000..59f8a93
--- /dev/null
+++ b/gm/extractbitmap.cpp
@@ -0,0 +1,104 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkString.h"
+
+namespace skiagm {
+
+static void create_bitmap(SkBitmap* bitmap) {
+    const int W = 100;
+    const int H = 100;
+    bitmap->setConfig(SkBitmap::kARGB_8888_Config, W, H);
+    bitmap->allocPixels();
+
+    SkCanvas canvas(*bitmap);
+    canvas.drawColor(SK_ColorRED);
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    canvas.drawCircle(SkIntToScalar(W)/2, SkIntToScalar(H)/2, SkIntToScalar(W)/2, paint);
+}
+
+class ExtractBitmapGM : public GM {
+public:
+    ExtractBitmapGM() {}
+
+protected:
+    // overrides from SkEventSink
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("extractbitmap");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return make_isize(600, 600);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkBitmap bitmap;
+        create_bitmap(&bitmap);
+        int x = bitmap.width() / 2;
+        int y = bitmap.height() / 2;
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+        canvas->drawBitmap(bitmap, 0, 0);
+
+        {
+            // Do some subset drawing. This will test that an SkGPipe properly
+            // handles the case where bitmaps share a pixelref
+            // Draw the bottom right fourth of the bitmap over the top left
+            SkBitmap subset;
+            bitmap.extractSubset(&subset, SkIRect::MakeXYWH(x, y, x, y));
+            canvas->drawBitmap(subset, 0, 0);
+            // Draw the top left corner over the bottom right
+            bitmap.extractSubset(&subset, SkIRect::MakeWH(x, y));
+            canvas->drawBitmap(subset, SkIntToScalar(x), SkIntToScalar(y));
+            // Draw a subset which has the same height and pixelref offset but a
+            // different width
+            bitmap.extractSubset(&subset, SkIRect::MakeWH(x, bitmap.height()));
+            SkAutoCanvasRestore autoRestore(canvas, true);
+            canvas->translate(0, SkIntToScalar(bitmap.height() + 20));
+            canvas->drawBitmap(subset, 0, 0);
+            // Now draw a subet which has the same width and pixelref offset but
+            // a different height
+            bitmap.extractSubset(&subset, SkIRect::MakeWH(bitmap.height(), y));
+            canvas->translate(0, SkIntToScalar(bitmap.height() + 20));
+            canvas->drawBitmap(subset, 0, 0);
+        }
+
+        // Now do the same but with a device bitmap as source image
+        SkAutoTUnref<SkDevice> secondDevice(canvas->createCompatibleDevice(
+            SkBitmap::kARGB_8888_Config, bitmap.width(),
+            bitmap.height(), true));
+        SkCanvas secondCanvas(secondDevice.get());
+        secondCanvas.writePixels(bitmap, 0, 0);
+
+        SkBitmap deviceBitmap = secondDevice->accessBitmap(false);
+        SkBitmap deviceSubset;
+        deviceBitmap.extractSubset(&deviceSubset,
+             SkIRect::MakeXYWH(x, y, x, y));
+
+        canvas->translate(SkIntToScalar(120), SkIntToScalar(0));
+
+        canvas->drawBitmap(deviceBitmap, 0, 0);
+        canvas->drawBitmap(deviceSubset, 0, 0);
+
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ExtractBitmapGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/factory.cpp b/gm/factory.cpp
new file mode 100644
index 0000000..8893158
--- /dev/null
+++ b/gm/factory.cpp
@@ -0,0 +1,67 @@
+/*
+ * 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 "SkBitmapFactory.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkStream.h"
+
+namespace skiagm {
+
+/**
+ *  Draw a PNG created by SkBitmapFactory.
+ */
+class FactoryGM : public GM {
+public:
+    FactoryGM() {}
+
+protected:
+    virtual void onOnceBeforeDraw() SK_OVERRIDE {
+        SkString filename(INHERITED::gResourcePath);
+        if (!filename.endsWith("/") && !filename.endsWith("\\")) {
+            filename.append("/");
+        }
+
+        // Copyright-free file from http://openclipart.org/detail/29213/paper-plane-by-ddoo
+        filename.append("plane.png");
+
+        SkFILEStream stream(filename.c_str());
+        if (stream.isValid()) {
+            stream.rewind();
+            size_t length = stream.getLength();
+            void* buffer = sk_malloc_throw(length);
+            stream.read(buffer, length);
+            SkAutoDataUnref data(SkData::NewFromMalloc(buffer, length));
+            SkBitmapFactory::DecodeBitmap(&fBitmap, data);
+        }
+    }
+
+    virtual SkString onShortName() {
+        return SkString("factory");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->drawBitmap(fBitmap, 0, 0);
+    }
+
+private:
+    SkBitmap fBitmap;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new FactoryGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/fatpathfill.cpp b/gm/fatpathfill.cpp
new file mode 100644
index 0000000..4498879
--- /dev/null
+++ b/gm/fatpathfill.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 "SkPath.h"
+#include "SkSurface.h"
+
+#define ZOOM    32
+#define SMALL_W 9
+#define SMALL_H 3
+#define REPEAT_LOOP 5
+
+static SkSurface* new_surface(int width, int height) {
+    SkImage::Info info = {
+        width,
+        height,
+        SkImage::kPMColor_ColorType,
+        SkImage::kPremul_AlphaType
+    };
+    return SkSurface::NewRaster(info);
+}
+
+static void draw_pixel_centers(SkCanvas* canvas) {
+    SkPaint paint;
+    paint.setColor(0xFF0088FF);
+    paint.setAntiAlias(true);
+
+    for (int y = 0; y < SMALL_H; ++y) {
+        for (int x = 0; x < SMALL_W; ++x) {
+            canvas->drawCircle(x + 0.5f, y + 0.5f, 1.5f / ZOOM, paint);
+        }
+    }
+}
+
+static void draw_fatpath(SkCanvas* canvas, SkSurface* surface,
+                         const SkPath paths[], int count) {
+    SkPaint paint;
+
+    surface->getCanvas()->clear(SK_ColorTRANSPARENT);
+    for (int i = 0; i < count; ++i) {
+        surface->getCanvas()->drawPath(paths[i], paint);
+    }
+    surface->draw(canvas, 0, 0, NULL);
+
+    paint.setAntiAlias(true);
+    paint.setColor(SK_ColorRED);
+    paint.setStyle(SkPaint::kStroke_Style);
+    for (int j = 0; j < count; ++j) {
+        canvas->drawPath(paths[j], paint);
+    }
+
+    draw_pixel_centers(canvas);
+}
+
+class FatPathFillGM : public skiagm::GM {
+public:
+    FatPathFillGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("fatpathfill");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(SMALL_W * ZOOM, SMALL_H * ZOOM * REPEAT_LOOP);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkAutoTUnref<SkSurface> surface(new_surface(SMALL_W, SMALL_H));
+
+        canvas->scale(ZOOM, ZOOM);
+
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(SK_Scalar1);
+
+        for (int i = 0; i < REPEAT_LOOP; ++i) {
+            SkPath line, path;
+            line.moveTo(SkIntToScalar(1), SkIntToScalar(2));
+            line.lineTo(SkIntToScalar(4 + i), SkIntToScalar(1));
+            paint.getFillPath(line, &path);
+            draw_fatpath(canvas, surface, &path, 1);
+
+            canvas->translate(0, SMALL_H);
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return new FatPathFillGM;)
diff --git a/gm/filltypes.cpp b/gm/filltypes.cpp
index 73c718e..7df02ae 100644
--- a/gm/filltypes.cpp
+++ b/gm/filltypes.cpp
@@ -12,19 +12,24 @@
 class FillTypeGM : public GM {
     SkPath fPath;
 public:
-	FillTypeGM() {
+    FillTypeGM() {
         this->setBGColor(0xFFDDDDDD);
-        const SkScalar radius = SkIntToScalar(45);
-        fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
-        fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
     }
-    
+
+    void makePath() {
+        if (fPath.isEmpty()) {
+            const SkScalar radius = SkIntToScalar(45);
+            fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
+            fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
+        }
+    }
+
 protected:
     virtual SkString onShortName() {
         return SkString("filltypes");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(835, 840);
     }
 
@@ -57,8 +62,10 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
+        this->makePath();
+
         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
-        
+
         SkPaint paint;
         const SkScalar scale = SkIntToScalar(5)/4;
 
@@ -75,7 +82,7 @@
         canvas->translate(SkIntToScalar(450), 0);
         showFour(canvas, scale, paint);
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -86,4 +93,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/filltypespersp.cpp b/gm/filltypespersp.cpp
index 33f3242..cbd966c 100644
--- a/gm/filltypespersp.cpp
+++ b/gm/filltypespersp.cpp
@@ -13,12 +13,16 @@
 class FillTypePerspGM : public GM {
     SkPath fPath;
 public:
-    FillTypePerspGM() {
-        const SkScalar radius = SkIntToScalar(45);
-        fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
-        fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
+    FillTypePerspGM() {}
+
+    void makePath() {
+        if (fPath.isEmpty()) {
+            const SkScalar radius = SkIntToScalar(45);
+            fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
+            fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
+        }
     }
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("filltypespersp");
@@ -71,6 +75,8 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
+        this->makePath();
+
         // do perspective drawPaint as the background;
         SkPaint bkgnrd;
         SkPoint center = SkPoint::Make(SkIntToScalar(100),
@@ -114,7 +120,7 @@
         canvas->translate(SkIntToScalar(450), 0);
         showFour(canvas, scale, true);
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -125,4 +131,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/fontscaler.cpp b/gm/fontscaler.cpp
index b331bde..59b1a43 100644
--- a/gm/fontscaler.cpp
+++ b/gm/fontscaler.cpp
@@ -90,4 +90,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/gammatext.cpp b/gm/gammatext.cpp
index 6f1c298..99642f6 100644
--- a/gm/gammatext.cpp
+++ b/gm/gammatext.cpp
@@ -12,10 +12,12 @@
 #include "SkTypeface.h"
 
 static SkShader* make_heatGradient(const SkPoint pts[2]) {
+#if 0 // UNUSED
     const SkColor colors[] = {
         SK_ColorBLACK, SK_ColorBLUE, SK_ColorCYAN, SK_ColorGREEN,
         SK_ColorYELLOW, SK_ColorRED, SK_ColorWHITE
     };
+#endif
     const SkColor bw[] = { SK_ColorBLACK, SK_ColorWHITE };
 
     return SkGradientShader::CreateLinear(pts, bw, NULL,
@@ -45,10 +47,10 @@
     CGContextRef cg = CGBitmapContextCreate(bm.getPixels(), bm.width(), bm.height(),
                                             8, bm.rowBytes(), space, BITMAP_INFO_RGB);
     CFRelease(space);
-    
+
     CGContextSetAllowsFontSubpixelQuantization(cg, false);
     CGContextSetShouldSubpixelQuantizeFonts(cg, false);
-    
+
     return cg;
 }
 
@@ -66,10 +68,10 @@
 static void cgSetPaintForText(CGContextRef cg, const SkPaint& paint) {
     SkColor c = paint.getColor();
     CGFloat rgba[] = {
-        SkColorGetB(c) / 255.0,
-        SkColorGetG(c) / 255.0,
-        SkColorGetR(c) / 255.0,
-        SkColorGetA(c) / 255.0,
+        SkColorGetB(c) / 255.0f,
+        SkColorGetG(c) / 255.0f,
+        SkColorGetR(c) / 255.0f,
+        SkColorGetA(c) / 255.0f,
     };
     CGContextSetRGBFillColor(cg, rgba[0], rgba[1], rgba[2], rgba[3]);
 
@@ -79,7 +81,7 @@
 
     CGContextSetAllowsFontSubpixelPositioning(cg, paint.isSubpixelText());
     CGContextSetShouldSubpixelPositionFonts(cg, paint.isSubpixelText());
-    
+
     CGContextSetShouldAntialias(cg, paint.isAntiAlias());
     CGContextSetShouldSmoothFonts(cg, paint.isLCDRenderText());
 }
@@ -105,7 +107,7 @@
    Each region should show as a blue center surrounded by a 2px green
    border, with no red.
 */
-    
+
 #define HEIGHT 480
 
 class GammaTextGM : public GM {
@@ -124,18 +126,18 @@
     }
 
     static void drawGrad(SkCanvas* canvas) {
-        SkPoint pts[] = { { 0, 0 }, { 0, HEIGHT } };
+        SkPoint pts[] = { { 0, 0 }, { 0, SkIntToScalar(HEIGHT) } };
 #if 0
         const SkColor colors[] = { SK_ColorBLACK, SK_ColorWHITE };
         SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
 #else
         SkShader* s = make_heatGradient(pts);
 #endif
-        
+
         canvas->clear(SK_ColorRED);
         SkPaint paint;
         paint.setShader(s)->unref();
-        SkRect r = { 0, 0, 1024, HEIGHT };
+        SkRect r = { 0, 0, SkIntToScalar(1024), SkIntToScalar(HEIGHT) };
         canvas->drawRect(r, paint);
     }
 
@@ -143,7 +145,7 @@
 #ifdef SK_BUILD_FOR_MAC
         CGContextRef cg = makeCG(canvas->getDevice()->accessBitmap(false));
 #endif
-        
+
         drawGrad(canvas);
 
         const SkColor fg[] = {
@@ -152,7 +154,7 @@
             0xFFFF0000, 0xFF00FF00, 0xFF0000FF,
             0xFF000000,
         };
-        
+
         const char* text = "Hamburgefons";
         size_t len = strlen(text);
 
@@ -162,22 +164,30 @@
         paint.setAntiAlias(true);
         paint.setLCDRenderText(true);
 
-        SkScalar x = 10;
+        SkScalar x = SkIntToScalar(10);
         for (size_t i = 0; i < SK_ARRAY_COUNT(fg); ++i) {
             paint.setColor(fg[i]);
-            
-            SkScalar y = 40;
-            SkScalar stopy = HEIGHT;
+
+            SkScalar y = SkIntToScalar(40);
+            SkScalar stopy = SkIntToScalar(HEIGHT);
             while (y < stopy) {
-#if 1
-                canvas->drawText(text, len, x, y, paint);
-#else
-                cgDrawText(cg, text, len, x, HEIGHT - y, paint);
+                if (true) {
+                    canvas->drawText(text, len, x, y, paint);
+                }
+#ifdef SK_BUILD_FOR_MAC
+                else {
+                    cgDrawText(cg, text, len, SkScalarToFloat(x),
+                               static_cast<float>(HEIGHT) - SkScalarToFloat(y),
+                               paint);
+                }
 #endif
                 y += paint.getTextSize() * 2;
             }
             x += SkIntToScalar(1024) / SK_ARRAY_COUNT(fg);
         }
+#ifdef SK_BUILD_FOR_MAC
+        CGContextRelease(cg);
+#endif
     }
 
 private:
diff --git a/gm/getpostextpath.cpp b/gm/getpostextpath.cpp
new file mode 100644
index 0000000..39a3fc1
--- /dev/null
+++ b/gm/getpostextpath.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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 "SkRandom.h"
+#include "SkTemplates.h"
+
+class GetPosTextPathGM : public skiagm::GM {
+public:
+    GetPosTextPathGM() {}
+
+protected:
+    SkString onShortName() {
+        return SkString("getpostextpath");
+    }
+
+    SkISize onISize() { return skiagm::make_isize(480, 780); }
+
+    static void strokePath(SkCanvas* canvas, const SkPath& path) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(SK_ColorRED);
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas->drawPath(path, paint);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        // explicitly add spaces, to test a prev. bug
+        const char* text = "Ham bur ge fons";
+        size_t len = strlen(text);
+        SkPath path;
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(48));
+
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(64));
+
+        canvas->drawText(text, len, 0, 0, paint);
+        paint.getTextPath(text, len, 0, 0, &path);
+        strokePath(canvas, path);
+        path.reset();
+
+        SkAutoTArray<SkPoint>  pos(len);
+        SkAutoTArray<SkScalar> widths(len);
+        paint.getTextWidths(text, len, &widths[0]);
+
+        SkRandom rand;
+        SkScalar x = SkIntToScalar(20);
+        SkScalar y = SkIntToScalar(100);
+        for (size_t i = 0; i < len; ++i) {
+            pos[i].set(x, y + rand.nextSScalar1() * 24);
+            x += widths[i];
+        }
+
+        canvas->translate(0, SkIntToScalar(64));
+
+        canvas->drawPosText(text, len, &pos[0], paint);
+        paint.getPosTextPath(text, len, &pos[0], &path);
+        strokePath(canvas, path);
+    }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* F(void*) { return new GetPosTextPathGM; }
+static skiagm::GMRegistry gR(F);
diff --git a/gm/giantbitmap.cpp b/gm/giantbitmap.cpp
new file mode 100644
index 0000000..0d342b8
--- /dev/null
+++ b/gm/giantbitmap.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+/*
+ *  Want to ensure that our bitmap sampler (in bitmap shader) keeps plenty of
+ *  precision when scaling very large images (where the dx might get very small.
+ */
+
+#define W   257
+#define H   161
+
+class GiantBitmapGM : public skiagm::GM {
+    SkBitmap* fBM;
+    SkShader::TileMode fMode;
+    bool fDoFilter;
+    bool fDoRotate;
+
+    const SkBitmap& getBitmap() {
+        if (NULL == fBM) {
+            fBM = new SkBitmap;
+            fBM->setConfig(SkBitmap::kARGB_8888_Config, W, H);
+            fBM->allocPixels();
+            fBM->eraseColor(SK_ColorWHITE);
+
+            const SkColor colors[] = {
+                SK_ColorBLUE, SK_ColorRED, SK_ColorBLACK, SK_ColorGREEN
+            };
+
+            SkCanvas canvas(*fBM);
+            SkPaint paint;
+            paint.setAntiAlias(true);
+            paint.setStrokeWidth(SkIntToScalar(20));
+
+#if 0
+            for (int y = -H*2; y < H; y += 50) {
+                SkScalar yy = SkIntToScalar(y);
+                paint.setColor(colors[y/50 & 0x3]);
+                canvas.drawLine(0, yy, SkIntToScalar(W), yy + SkIntToScalar(W),
+                                paint);
+            }
+#else
+            for (int x = -W; x < W; x += 60) {
+                paint.setColor(colors[x/60 & 0x3]);
+
+                SkScalar xx = SkIntToScalar(x);
+                canvas.drawLine(xx, 0, xx, SkIntToScalar(H),
+                                paint);
+            }
+#endif
+        }
+        return *fBM;
+    }
+
+public:
+    GiantBitmapGM(SkShader::TileMode mode, bool doFilter, bool doRotate) : fBM(NULL) {
+        fMode = mode;
+        fDoFilter = doFilter;
+        fDoRotate = doRotate;
+    }
+
+    virtual ~GiantBitmapGM() {
+        SkDELETE(fBM);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        SkString str("giantbitmap_");
+        switch (fMode) {
+            case SkShader::kClamp_TileMode:
+                str.append("clamp");
+                break;
+            case SkShader::kRepeat_TileMode:
+                str.append("repeat");
+                break;
+            case SkShader::kMirror_TileMode:
+                str.append("mirror");
+                break;
+            default:
+                break;
+        }
+        str.append(fDoFilter ? "_bilerp" : "_point");
+        str.append(fDoRotate ? "_rotate" : "_scale");
+        return str;
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(640, 480); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        SkShader* s = SkShader::CreateBitmapShader(getBitmap(), fMode, fMode);
+
+        SkMatrix m;
+        if (fDoRotate) {
+//            m.setRotate(SkIntToScalar(30), 0, 0);
+            m.setSkew(SK_Scalar1, 0, 0, 0);
+//            m.postScale(2*SK_Scalar1/3, 2*SK_Scalar1/3);
+        } else {
+            SkScalar scale = 11*SK_Scalar1/12;
+            m.setScale(scale, scale);
+        }
+        s->setLocalMatrix(m);
+
+        paint.setShader(s)->unref();
+        paint.setFilterBitmap(fDoFilter);
+
+        canvas->translate(SkIntToScalar(50), SkIntToScalar(50));
+
+//        SkRect r = SkRect::MakeXYWH(-50, -50, 32, 16);
+//        canvas->drawRect(r, paint); return;
+        canvas->drawPaint(paint);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* G000(void*) { return new GiantBitmapGM(SkShader::kClamp_TileMode, false, false); }
+static skiagm::GM* G100(void*) { return new GiantBitmapGM(SkShader::kRepeat_TileMode, false, false); }
+static skiagm::GM* G200(void*) { return new GiantBitmapGM(SkShader::kMirror_TileMode, false, false); }
+static skiagm::GM* G010(void*) { return new GiantBitmapGM(SkShader::kClamp_TileMode, true, false); }
+static skiagm::GM* G110(void*) { return new GiantBitmapGM(SkShader::kRepeat_TileMode, true, false); }
+static skiagm::GM* G210(void*) { return new GiantBitmapGM(SkShader::kMirror_TileMode, true, false); }
+
+static skiagm::GM* G001(void*) { return new GiantBitmapGM(SkShader::kClamp_TileMode, false, true); }
+static skiagm::GM* G101(void*) { return new GiantBitmapGM(SkShader::kRepeat_TileMode, false, true); }
+static skiagm::GM* G201(void*) { return new GiantBitmapGM(SkShader::kMirror_TileMode, false, true); }
+static skiagm::GM* G011(void*) { return new GiantBitmapGM(SkShader::kClamp_TileMode, true, true); }
+static skiagm::GM* G111(void*) { return new GiantBitmapGM(SkShader::kRepeat_TileMode, true, true); }
+static skiagm::GM* G211(void*) { return new GiantBitmapGM(SkShader::kMirror_TileMode, true, true); }
+
+static skiagm::GMRegistry reg000(G000);
+static skiagm::GMRegistry reg100(G100);
+static skiagm::GMRegistry reg200(G200);
+static skiagm::GMRegistry reg010(G010);
+static skiagm::GMRegistry reg110(G110);
+static skiagm::GMRegistry reg210(G210);
+
+static skiagm::GMRegistry reg001(G001);
+static skiagm::GMRegistry reg101(G101);
+static skiagm::GMRegistry reg201(G201);
+static skiagm::GMRegistry reg011(G011);
+static skiagm::GMRegistry reg111(G111);
+static skiagm::GMRegistry reg211(G211);
diff --git a/gm/gm.cpp b/gm/gm.cpp
index f9c0e66..c1e75e9 100644
--- a/gm/gm.cpp
+++ b/gm/gm.cpp
@@ -8,8 +8,12 @@
 #include "gm.h"
 using namespace skiagm;
 
+SkString GM::gResourcePath;
+
 GM::GM() {
     fBGColor = SK_ColorWHITE;
+    fCanvasIsDeferred = false;
+    fHaveCalledOnceBeforeDraw = false;
 }
 GM::~GM() {}
 
@@ -19,10 +23,18 @@
 }
 
 void GM::drawContent(SkCanvas* canvas) {
+    if (!fHaveCalledOnceBeforeDraw) {
+        fHaveCalledOnceBeforeDraw = true;
+        this->onOnceBeforeDraw();
+    }
     this->onDraw(canvas);
 }
 
 void GM::drawBackground(SkCanvas* canvas) {
+    if (!fHaveCalledOnceBeforeDraw) {
+        fHaveCalledOnceBeforeDraw = true;
+        this->onOnceBeforeDraw();
+    }
     this->onDrawBackground(canvas);
 }
 
@@ -38,7 +50,7 @@
 }
 
 void GM::onDrawBackground(SkCanvas* canvas) {
-    canvas->drawColor(fBGColor);
+    canvas->drawColor(fBGColor, SkXfermode::kSrc_Mode);
 }
 
 void GM::drawSizeBounds(SkCanvas* canvas, SkColor color) {
diff --git a/gm/gm.h b/gm/gm.h
index b0de922..6ffb029 100644
--- a/gm/gm.h
+++ b/gm/gm.h
@@ -12,40 +12,62 @@
 #include "SkCanvas.h"
 #include "SkDevice.h"
 #include "SkPaint.h"
-#include "SkRefCnt.h"
 #include "SkSize.h"
 #include "SkString.h"
 #include "SkTRegistry.h"
 
+#define DEF_GM(code) \
+    static skiagm::GM*          SK_MACRO_APPEND_LINE(F_)(void* p) { code; } \
+    static skiagm::GMRegistry   SK_MACRO_APPEND_LINE(R_)(SK_MACRO_APPEND_LINE(F_));
+
 namespace skiagm {
-	
-	static inline SkISize make_isize(int w, int h) {
-		SkISize sz;
-		sz.set(w, h);
-		return sz;
-	}
+
+        static inline SkISize make_isize(int w, int h) {
+                SkISize sz;
+                sz.set(w, h);
+                return sz;
+        }
 
     class GM {
     public:
         GM();
         virtual ~GM();
-		
+
         enum Flags {
-            kSkipPDF_Flag       = 1 << 0,
-            kSkipPicture_Flag   = 1 << 1
+            kSkipPDF_Flag           = 1 << 0,
+            kSkipPicture_Flag       = 1 << 1,
+            kSkipPipe_Flag          = 1 << 2,
+            kSkipTiled_Flag         = 1 << 3,
+            kSkip565_Flag           = 1 << 4,
+            kSkipScaledReplay_Flag  = 1 << 5,
         };
 
         void draw(SkCanvas*);
         void drawBackground(SkCanvas*);
         void drawContent(SkCanvas*);
-        
-		SkISize getISize() { return this->onISize(); }
+
+        SkISize getISize() { return this->onISize(); }
         const char* shortName();
 
         uint32_t getFlags() const {
             return this->onGetFlags();
         }
-        
+
+        SkScalar width() {
+            return SkIntToScalar(this->getISize().width());
+        }
+        SkScalar height() {
+            return SkIntToScalar(this->getISize().width());
+        }
+
+        // TODO(vandebo) Instead of exposing this, we should run all the GMs
+        // with and without an initial transform.
+        // Most GMs will return the identity matrix, but some PDFs tests
+        // require setting the initial transform.
+        SkMatrix getInitialTransform() const {
+            return this->onGetInitialTransform();
+        }
+
         SkColor getBGColor() const { return fBGColor; }
         void setBGColor(SkColor);
 
@@ -53,16 +75,31 @@
         // GM's getISize bounds.
         void drawSizeBounds(SkCanvas*, SkColor);
 
-	protected:
-		virtual void onDraw(SkCanvas*) = 0;
-		virtual void onDrawBackground(SkCanvas*);
-		virtual SkISize onISize() = 0;
+        static void SetResourcePath(const char* resourcePath) {
+            gResourcePath = resourcePath;
+        }
+
+        bool isCanvasDeferred() const { return fCanvasIsDeferred; }
+        void setCanvasIsDeferred(bool isDeferred) {
+            fCanvasIsDeferred = isDeferred;
+        }
+
+    protected:
+        static SkString gResourcePath;
+
+        virtual void onOnceBeforeDraw() {}
+        virtual void onDraw(SkCanvas*) = 0;
+        virtual void onDrawBackground(SkCanvas*);
+        virtual SkISize onISize() = 0;
         virtual SkString onShortName() = 0;
         virtual uint32_t onGetFlags() const { return 0; }
-        
+        virtual SkMatrix onGetInitialTransform() const { return SkMatrix::I(); }
+
     private:
         SkString fShortName;
         SkColor  fBGColor;
+        bool     fCanvasIsDeferred; // work-around problem in srcmode.cpp
+        bool     fHaveCalledOnceBeforeDraw;
     };
 
     typedef SkTRegistry<GM*, void*> GMRegistry;
diff --git a/gm/gm_expectations.h b/gm/gm_expectations.h
new file mode 100644
index 0000000..07f0e60
--- /dev/null
+++ b/gm/gm_expectations.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef gm_expectations_DEFINED
+#define gm_expectations_DEFINED
+
+#include "gm.h"
+#include "SkBitmap.h"
+#include "SkBitmapChecksummer.h"
+#include "SkImageDecoder.h"
+#include "SkOSFile.h"
+#include "SkRefCnt.h"
+#include "SkTArray.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    // json includes xlocale which generates warning 4530 because we're compiling without
+    // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067
+    #pragma warning(push)
+    #pragma warning(disable : 4530)
+#endif
+#include "json/value.h"
+#ifdef SK_BUILD_FOR_WIN
+    #pragma warning(pop)
+#endif
+
+namespace skiagm {
+
+    // The actual type we use to represent a checksum is hidden in here.
+    typedef Json::UInt64 Checksum;
+    static inline Json::Value asJsonValue(Checksum checksum) {
+        return checksum;
+    }
+
+    static SkString make_filename(const char path[],
+                                  const char renderModeDescriptor[],
+                                  const char *name,
+                                  const char suffix[]) {
+        SkString filename(path);
+        if (filename.endsWith(SkPATH_SEPARATOR)) {
+            filename.remove(filename.size() - 1, 1);
+        }
+        filename.appendf("%c%s%s.%s", SkPATH_SEPARATOR,
+                         name, renderModeDescriptor, suffix);
+        return filename;
+    }
+
+    /**
+     * Test expectations (allowed image checksums, etc.)
+     */
+    class Expectations {
+    public:
+        /**
+         * No expectations at all.
+         *
+         * We set ignoreFailure to false by default, but it doesn't really
+         * matter... the result will always be "no-comparison" anyway.
+         */
+        Expectations(bool ignoreFailure=false) {
+            fIgnoreFailure = ignoreFailure;
+        }
+
+        /**
+         * Expect exactly one image (appropriate for the case when we
+         * are comparing against a single PNG file).
+         *
+         * By default, DO NOT ignore failures.
+         */
+        Expectations(const SkBitmap& bitmap, bool ignoreFailure=false) {
+            fBitmap = bitmap;
+            fIgnoreFailure = ignoreFailure;
+            fAllowedChecksums.push_back() = SkBitmapChecksummer::Compute64(bitmap);
+        }
+
+        /**
+         * Returns true iff we want to ignore failed expectations.
+         */
+        bool ignoreFailure() const { return this->fIgnoreFailure; }
+
+        /**
+         * Returns true iff there are no allowed checksums.
+         */
+        bool empty() const { return this->fAllowedChecksums.empty(); }
+
+        /**
+         * Returns true iff actualChecksum matches any allowedChecksum,
+         * regardless of fIgnoreFailure.  (The caller can check
+         * that separately.)
+         */
+        bool match(Checksum actualChecksum) const {
+            for (int i=0; i < this->fAllowedChecksums.count(); i++) {
+                Checksum allowedChecksum = this->fAllowedChecksums[i];
+                if (allowedChecksum == actualChecksum) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        /**
+         * If this Expectation is based on a single SkBitmap, return a
+         * pointer to that SkBitmap. Otherwise (if the Expectation is
+         * empty, or if it was based on a list of checksums rather
+         * than a single bitmap), returns NULL.
+         */
+        const SkBitmap *asBitmap() const {
+            return (SkBitmap::kNo_Config == fBitmap.config()) ? NULL : &fBitmap;
+        }
+
+        /**
+         * Return a JSON representation of the allowed checksums.
+         * This does NOT include any information about whether to
+         * ignore failures.
+         */
+        Json::Value allowedChecksumsAsJson() const {
+            Json::Value allowedChecksumArray;
+            if (!this->fAllowedChecksums.empty()) {
+                for (int i=0; i < this->fAllowedChecksums.count(); i++) {
+                    Checksum allowedChecksum = this->fAllowedChecksums[i];
+                    allowedChecksumArray.append(asJsonValue(allowedChecksum));
+                }
+            }
+            return allowedChecksumArray;
+        }
+
+    private:
+        SkTArray<Checksum> fAllowedChecksums;
+        bool fIgnoreFailure;
+        SkBitmap fBitmap;
+    };
+
+    /**
+     * Abstract source of Expectations objects for individual tests.
+     */
+    class ExpectationsSource : public SkRefCnt {
+    public:
+        virtual Expectations get(const char *testName) = 0;
+    };
+
+    /**
+     * Return Expectations based on individual image files on disk.
+     */
+    class IndividualImageExpectationsSource : public ExpectationsSource {
+    public:
+        /**
+         * Create an ExpectationsSource that will return Expectations based on
+         * image files found within rootDir.
+         *
+         * rootDir: directory under which to look for image files
+         *          (this string will be copied to storage within this object)
+         * notifyOfMissingFiles: whether to log a message to stderr if an image
+         *                       file cannot be found
+         */
+        IndividualImageExpectationsSource(const char *rootDir,
+                                          bool notifyOfMissingFiles) :
+            fRootDir(rootDir), fNotifyOfMissingFiles(notifyOfMissingFiles) {}
+
+        Expectations get(const char *testName) SK_OVERRIDE {
+            SkString path = make_filename(fRootDir.c_str(), "", testName,
+                                          "png");
+            SkBitmap referenceBitmap;
+            bool decodedReferenceBitmap =
+                SkImageDecoder::DecodeFile(path.c_str(), &referenceBitmap,
+                                           SkBitmap::kARGB_8888_Config,
+                                           SkImageDecoder::kDecodePixels_Mode,
+                                           NULL);
+            if (decodedReferenceBitmap) {
+                return Expectations(referenceBitmap);
+            } else {
+                if (fNotifyOfMissingFiles) {
+                    fprintf(stderr, "FAILED to read %s\n", path.c_str());
+                }
+                return Expectations();
+            }
+        }
+
+    private:
+        const SkString fRootDir;
+        const bool fNotifyOfMissingFiles;
+    };
+
+}
+#endif
diff --git a/gm/gmmain.cpp b/gm/gmmain.cpp
index 523faf8..bbeb443 100644
--- a/gm/gmmain.cpp
+++ b/gm/gmmain.cpp
@@ -5,25 +5,57 @@
  * found in the LICENSE file.
  */
 
-#include "gm.h"
-#include "system_preferences.h"
-#include "GrContext.h"
-#include "GrRenderTarget.h"
+/*
+ * Code for the "gm" (Golden Master) rendering comparison tool.
+ *
+ * If you make changes to this, re-run the self-tests at gm/tests/run.sh
+ * to make sure they still pass... you may need to change the expected
+ * results of the self-test.
+ */
 
+#include "gm.h"
+#include "gm_expectations.h"
+#include "system_preferences.h"
+#include "SkBitmap.h"
+#include "SkBitmapChecksummer.h"
 #include "SkColorPriv.h"
 #include "SkData.h"
 #include "SkDeferredCanvas.h"
 #include "SkDevice.h"
-#include "SkGpuCanvas.h"
-#include "SkGpuDevice.h"
+#include "SkDrawFilter.h"
+#include "SkGPipe.h"
 #include "SkGraphics.h"
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
-#include "gl/SkNativeGLContext.h"
-#include "gl/SkMesaGLContext.h"
+#include "SkOSFile.h"
 #include "SkPicture.h"
-#include "SkStream.h"
 #include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTArray.h"
+#include "SkTileGridPicture.h"
+#include "SamplePipeControllers.h"
+
+#ifdef SK_BUILD_FOR_WIN
+    // json includes xlocale which generates warning 4530 because we're compiling without
+    // exceptions; see https://code.google.com/p/skia/issues/detail?id=1067
+    #pragma warning(push)
+    #pragma warning(disable : 4530)
+#endif
+#include "json/value.h"
+#ifdef SK_BUILD_FOR_WIN
+    #pragma warning(pop)
+#endif
+
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+#include "GrRenderTarget.h"
+#include "SkGpuDevice.h"
+typedef GrContextFactory::GLContextType GLContextType;
+#else
+class GrContext;
+class GrRenderTarget;
+typedef int GLContextType;
+#endif
 
 static bool gForceBWtext;
 
@@ -48,16 +80,41 @@
     #define CAN_IMAGE_PDF   0
 #endif
 
+// TODO(epoger): We created this ErrorBitfield so that we could record
+// multiple error types for the same comparison. But in practice, we
+// process its final value in switch() statements, which inherently
+// assume that only one error type will be set.
+// I think we should probably change this to be an enum, and thus
+// constrain ourselves to a single error type per comparison.
 typedef int ErrorBitfield;
 const static ErrorBitfield ERROR_NONE                    = 0x00;
 const static ErrorBitfield ERROR_NO_GPU_CONTEXT          = 0x01;
-const static ErrorBitfield ERROR_PIXEL_MISMATCH          = 0x02;
-const static ErrorBitfield ERROR_DIMENSION_MISMATCH      = 0x04;
+const static ErrorBitfield ERROR_IMAGE_MISMATCH          = 0x02;
+// const static ErrorBitfield ERROR_DIMENSION_MISMATCH      = 0x04; DEPRECATED in https://codereview.appspot.com/7064047
 const static ErrorBitfield ERROR_READING_REFERENCE_IMAGE = 0x08;
 const static ErrorBitfield ERROR_WRITING_REFERENCE_IMAGE = 0x10;
 
+const static char kJsonKey_ActualResults[]   = "actual-results";
+const static char kJsonKey_ActualResults_Failed[]        = "failed";
+const static char kJsonKey_ActualResults_FailureIgnored[]= "failure-ignored";
+const static char kJsonKey_ActualResults_NoComparison[]  = "no-comparison";
+const static char kJsonKey_ActualResults_Succeeded[]     = "succeeded";
+const static char kJsonKey_ActualResults_AnyStatus_Checksum[]    = "checksum";
+
+const static char kJsonKey_ExpectedResults[] = "expected-results";
+const static char kJsonKey_ExpectedResults_Checksums[]     = "checksums";
+const static char kJsonKey_ExpectedResults_IgnoreFailure[] = "ignore-failure";
+
 using namespace skiagm;
 
+struct FailRec {
+    SkString    fName;
+    bool        fIsPixelError;
+
+    FailRec() : fIsPixelError(false) {}
+    FailRec(const SkString& name) : fName(name), fIsPixelError(false) {}
+};
+
 class Iter {
 public:
     Iter() {
@@ -91,544 +148,947 @@
     const GMRegistry* fReg;
 };
 
-static SkString make_name(const char shortName[], const char configName[]) {
-    SkString name(shortName);
-    name.appendf("_%s", configName);
-    return name;
-}
-
-static SkString make_filename(const char path[],
-                              const char pathSuffix[],
-                              const SkString& name,
-                              const char suffix[]) {
-    SkString filename(path);
-    if (filename.endsWith("/")) {
-        filename.remove(filename.size() - 1, 1);
-    }
-    filename.append(pathSuffix);
-    filename.append("/");
-    filename.appendf("%s.%s", name.c_str(), suffix);
-    return filename;
-}
-
-/* since PNG insists on unpremultiplying our alpha, we take no precision chances
-    and force all pixels to be 100% opaque, otherwise on compare we may not get
-    a perfect match.
- */
-static void force_all_opaque(const SkBitmap& bitmap) {
-    SkAutoLockPixels lock(bitmap);
-    for (int y = 0; y < bitmap.height(); y++) {
-        for (int x = 0; x < bitmap.width(); x++) {
-            *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
-        }
-    }
-}
-
-static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
-    SkBitmap copy;
-    bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
-    force_all_opaque(copy);
-    return SkImageEncoder::EncodeFile(path.c_str(), copy,
-                                      SkImageEncoder::kPNG_Type, 100);
-}
-
-static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
-    int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
-    int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
-    int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
-    return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
-}
-
-static void compute_diff(const SkBitmap& target, const SkBitmap& base,
-                         SkBitmap* diff) {
-    SkAutoLockPixels alp(*diff);
-
-    const int w = target.width();
-    const int h = target.height();
-    for (int y = 0; y < h; y++) {
-        for (int x = 0; x < w; x++) {
-            SkPMColor c0 = *base.getAddr32(x, y);
-            SkPMColor c1 = *target.getAddr32(x, y);
-            SkPMColor d = 0;
-            if (c0 != c1) {
-                d = compute_diff_pmcolor(c0, c1);
-            }
-            *diff->getAddr32(x, y) = d;
-        }
-    }
-}
-
-static ErrorBitfield compare(const SkBitmap& target, const SkBitmap& base,
-                             const SkString& name,
-                             const char* renderModeDescriptor,
-                             SkBitmap* diff) {
-    SkBitmap copy;
-    const SkBitmap* bm = &target;
-    if (target.config() != SkBitmap::kARGB_8888_Config) {
-        target.copyTo(&copy, SkBitmap::kARGB_8888_Config);
-        bm = &copy;
-    }
-    SkBitmap baseCopy;
-    const SkBitmap* bp = &base;
-    if (base.config() != SkBitmap::kARGB_8888_Config) {
-        base.copyTo(&baseCopy, SkBitmap::kARGB_8888_Config);
-        bp = &baseCopy;
-    }
-
-    force_all_opaque(*bm);
-    force_all_opaque(*bp);
-
-    const int w = bm->width();
-    const int h = bm->height();
-    if (w != bp->width() || h != bp->height()) {
-        SkDebugf(
-"---- %s dimensions mismatch for %s base [%d %d] current [%d %d]\n",
-                 renderModeDescriptor, name.c_str(),
-                 bp->width(), bp->height(), w, h);
-        return ERROR_DIMENSION_MISMATCH;
-    }
-
-    SkAutoLockPixels bmLock(*bm);
-    SkAutoLockPixels baseLock(*bp);
-
-    for (int y = 0; y < h; y++) {
-        for (int x = 0; x < w; x++) {
-            SkPMColor c0 = *bp->getAddr32(x, y);
-            SkPMColor c1 = *bm->getAddr32(x, y);
-            if (c0 != c1) {
-                SkDebugf(
-"----- %s pixel mismatch for %s at [%d %d] base 0x%08X current 0x%08X\n",
-                         renderModeDescriptor, name.c_str(), x, y, c0, c1);
-
-                if (diff) {
-                    diff->setConfig(SkBitmap::kARGB_8888_Config, w, h);
-                    diff->allocPixels();
-                    compute_diff(*bm, *bp, diff);
-                }
-                return ERROR_PIXEL_MISMATCH;
-            }
-        }
-    }
-
-    // they're equal
-    return ERROR_NONE;
-}
-
-static bool write_document(const SkString& path,
-                           const SkDynamicMemoryWStream& document) {
-    SkFILEWStream stream(path.c_str());
-    SkAutoDataUnref data(document.copyToData());
-    return stream.writeData(data.get());
-}
-
 enum Backend {
-  kRaster_Backend,
-  kGPU_Backend,
-  kPDF_Backend,
-  kXPS_Backend,
+    kRaster_Backend,
+    kGPU_Backend,
+    kPDF_Backend,
+    kXPS_Backend,
+};
+
+enum BbhType {
+    kNone_BbhType,
+    kRTree_BbhType,
+    kTileGrid_BbhType,
+};
+
+enum ConfigFlags {
+    kNone_ConfigFlag  = 0x0,
+    /* Write GM images if a write path is provided. */
+    kWrite_ConfigFlag = 0x1,
+    /* Read reference GM images if a read path is provided. */
+    kRead_ConfigFlag  = 0x2,
+    kRW_ConfigFlag    = (kWrite_ConfigFlag | kRead_ConfigFlag),
 };
 
 struct ConfigData {
-    SkBitmap::Config    fConfig;
-    Backend             fBackend;
-    const char*         fName;
+    SkBitmap::Config                fConfig;
+    Backend                         fBackend;
+    GLContextType                   fGLContextType; // GPU backend only
+    int                             fSampleCnt;     // GPU backend only
+    ConfigFlags                     fFlags;
+    const char*                     fName;
 };
 
-/// Returns true if processing should continue, false to skip the
-/// remainder of this config for this GM.
-//@todo thudson 22 April 2011 - could refactor this to take in
-// a factory to generate the context, always call readPixels()
-// (logically a noop for rasters, if wasted time), and thus collapse the
-// GPU special case and also let this be used for SkPicture testing.
-static void setup_bitmap(const ConfigData& gRec, SkISize& size,
-                         SkBitmap* bitmap) {
-    bitmap->setConfig(gRec.fConfig, size.width(), size.height());
-    bitmap->allocPixels();
-    bitmap->eraseColor(0);
-}
-
-#include "SkDrawFilter.h"
 class BWTextDrawFilter : public SkDrawFilter {
 public:
-    virtual void filter(SkPaint*, Type) SK_OVERRIDE;
+    virtual bool filter(SkPaint*, Type) SK_OVERRIDE;
 };
-void BWTextDrawFilter::filter(SkPaint* p, Type t) {
+bool BWTextDrawFilter::filter(SkPaint* p, Type t) {
     if (kText_Type == t) {
         p->setAntiAlias(false);
     }
+    return true;
 }
 
-static void installFilter(SkCanvas* canvas) {
-    if (gForceBWtext) {
-        canvas->setDrawFilter(new BWTextDrawFilter)->unref();
+struct PipeFlagComboData {
+    const char* name;
+    uint32_t flags;
+};
+
+static PipeFlagComboData gPipeWritingFlagCombos[] = {
+    { "", 0 },
+    { " cross-process", SkGPipeWriter::kCrossProcess_Flag },
+    { " cross-process, shared address", SkGPipeWriter::kCrossProcess_Flag
+        | SkGPipeWriter::kSharedAddressSpace_Flag }
+};
+
+class GMMain {
+public:
+    GMMain() {
+        // Set default values of member variables, which tool_main()
+        // may override.
+        fUseFileHierarchy = false;
+        fMismatchPath = NULL;
     }
-}
 
-static void invokeGM(GM* gm, SkCanvas* canvas) {
-    installFilter(canvas);
-    gm->draw(canvas);
-    canvas->setDrawFilter(NULL);
-}
-
-static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec,
-                                    GrContext* context,
-                                    GrRenderTarget* rt,
-                                    SkBitmap* bitmap,
-                                    bool deferred) {
-    SkISize size (gm->getISize());
-    setup_bitmap(gRec, size, bitmap);
-
-    if (gRec.fBackend == kRaster_Backend) {
-        SkCanvas* canvas;
-        if (deferred) {
-            canvas = new SkDeferredCanvas;
-            canvas->setDevice(new SkDevice(*bitmap))->unref();
+    SkString make_name(const char shortName[], const char configName[]) {
+        SkString name;
+        if (0 == strlen(configName)) {
+            name.append(shortName);
+        } else if (fUseFileHierarchy) {
+            name.appendf("%s%c%s", configName, SkPATH_SEPARATOR, shortName);
         } else {
-            canvas = new SkCanvas(*bitmap);
+            name.appendf("%s_%s", shortName, configName);
         }
-        SkAutoUnref canvasUnref(canvas);
-        invokeGM(gm, canvas);
-        canvas->flush();
-    } else {  // GPU
-        if (NULL == context) {
-            return ERROR_NO_GPU_CONTEXT;
-        }
-        SkCanvas* gc;
-        if (deferred) {
-            gc = new SkDeferredCanvas;
-        } else {
-            gc = new SkGpuCanvas(context, rt);
-        }
-        SkAutoUnref gcUnref(gc);
-        gc->setDevice(new SkGpuDevice(context, rt))->unref();
-        invokeGM(gm, gc);
-        // the device is as large as the current rendertarget, so we explicitly
-        // only readback the amount we expect (in size)
-        // overwrite our previous allocation
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
-                                                       size.fHeight);
-        gc->readPixels(bitmap, 0, 0);
+        return name;
     }
-    return ERROR_NONE;
-}
 
-static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
-                                        SkPicture* pict, SkBitmap* bitmap) {
-    SkISize size = gm->getISize();
-    setup_bitmap(gRec, size, bitmap);
-    SkCanvas canvas(*bitmap);
-    installFilter(&canvas);
-    canvas.drawPicture(*pict);
-}
-
-static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
-#ifdef SK_SUPPORT_PDF
-    SkISize size = gm->getISize();
-    SkMatrix identity;
-    identity.reset();
-    SkPDFDevice* dev = new SkPDFDevice(size, size, identity);
-    SkAutoUnref aur(dev);
-
-    SkCanvas c(dev);
-    invokeGM(gm, &c);
-
-    SkPDFDocument doc;
-    doc.appendPage(dev);
-    doc.emitPDF(&pdf);
-#endif
-}
-
-static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) {
-#ifdef SK_SUPPORT_XPS
-    SkISize size = gm->getISize();
-    
-    SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()),
-                                   SkIntToScalar(size.height()));
-    static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254);
-    static const SkScalar upm = 72 * inchesPerMeter;
-    SkVector unitsPerMeter = SkPoint::Make(upm, upm);
-    static const SkScalar ppm = 200 * inchesPerMeter;
-    SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm);
-
-    SkXPSDevice* dev = new SkXPSDevice();
-    SkAutoUnref aur(dev);
-
-    SkCanvas c(dev);
-    dev->beginPortfolio(&xps);
-    dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize);
-    invokeGM(gm, &c);
-    dev->endSheet();
-    dev->endPortfolio();
-
-#endif
-}
-
-static ErrorBitfield write_reference_image(const ConfigData& gRec,
-                                           const char writePath [],
-                                           const char renderModeDescriptor [],
-                                           const SkString& name,
-                                           SkBitmap& bitmap,
-                                           SkDynamicMemoryWStream* document) {
-    SkString path;
-    bool success = false;
-    if (gRec.fBackend == kRaster_Backend ||
-        gRec.fBackend == kGPU_Backend ||
-        (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) {
-    
-        path = make_filename(writePath, renderModeDescriptor, name, "png");
-        success = write_bitmap(path, bitmap);
-    }
-    if (kPDF_Backend == gRec.fBackend) {
-        path = make_filename(writePath, renderModeDescriptor, name, "pdf");
-        success = write_document(path, *document);
-    }
-    if (kXPS_Backend == gRec.fBackend) {
-        path = make_filename(writePath, renderModeDescriptor, name, "xps");
-        success = write_document(path, *document);
-    }
-    if (success) {
-        return ERROR_NONE;
-    } else {
-        fprintf(stderr, "FAILED to write %s\n", path.c_str());
-        return ERROR_WRITING_REFERENCE_IMAGE;
-    }
-}
-
-static ErrorBitfield compare_to_reference_image(const SkString& name,
-                                                SkBitmap &bitmap,
-                                                const SkBitmap& comparisonBitmap,
-                                                const char diffPath [],
-                                                const char renderModeDescriptor []) {
-    ErrorBitfield errors;
-    SkBitmap diffBitmap;
-    errors = compare(bitmap, comparisonBitmap, name, renderModeDescriptor,
-                     diffPath ? &diffBitmap : NULL);
-    if ((ERROR_NONE == errors) && diffPath) {
-        SkString diffName = make_filename(diffPath, "", name, ".diff.png");
-        if (!write_bitmap(diffName, diffBitmap)) {
-            errors |= ERROR_WRITING_REFERENCE_IMAGE;
+    /* since PNG insists on unpremultiplying our alpha, we take no
+       precision chances and force all pixels to be 100% opaque,
+       otherwise on compare we may not get a perfect match.
+    */
+    static void force_all_opaque(const SkBitmap& bitmap) {
+        SkBitmap::Config config = bitmap.config();
+        switch (config) {
+        case SkBitmap::kARGB_8888_Config:
+            force_all_opaque_8888(bitmap);
+            break;
+        case SkBitmap::kRGB_565_Config:
+            // nothing to do here; 565 bitmaps are inherently opaque
+            break;
+        default:
+            fprintf(stderr, "unsupported bitmap config %d\n", config);
+            SkDEBUGFAIL("unsupported bitmap config");
         }
     }
-    return errors;
-}
 
-static ErrorBitfield compare_to_reference_image(const char readPath [],
-                                                const SkString& name,
-                                                SkBitmap &bitmap,
-                                                const char diffPath [],
-                                                const char renderModeDescriptor []) {
-    SkString path = make_filename(readPath, "", name, "png");
-    SkBitmap orig;
-    if (SkImageDecoder::DecodeFile(path.c_str(), &orig,
-                                   SkBitmap::kARGB_8888_Config,
-                                   SkImageDecoder::kDecodePixels_Mode, NULL)) {
-        return compare_to_reference_image(name, bitmap,
-                                          orig, diffPath,
-                                          renderModeDescriptor);
-    } else {
-        fprintf(stderr, "FAILED to read %s\n", path.c_str());
-        return ERROR_READING_REFERENCE_IMAGE;
-    }
-}
-
-static ErrorBitfield handle_test_results(GM* gm,
-                                         const ConfigData& gRec,
-                                         const char writePath [],
-                                         const char readPath [],
-                                         const char diffPath [],
-                                         const char renderModeDescriptor [],
-                                         SkBitmap& bitmap,
-                                         SkDynamicMemoryWStream* pdf,
-                                         const SkBitmap* comparisonBitmap) {
-    SkString name = make_name(gm->shortName(), gRec.fName);
-
-    if (writePath) {
-        return write_reference_image(gRec, writePath, renderModeDescriptor,
-                                     name, bitmap, pdf);
-    } else if (readPath && (
-                   gRec.fBackend == kRaster_Backend ||
-                   gRec.fBackend == kGPU_Backend ||
-                   (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF))) {
-        return compare_to_reference_image(readPath, name, bitmap,
-                                   diffPath, renderModeDescriptor);
-    } else if (comparisonBitmap) {
-        return compare_to_reference_image(name, bitmap,
-                                   *comparisonBitmap, diffPath,
-                                   renderModeDescriptor);
-    } else {
-        return ERROR_NONE;
-    }
-}
-
-static SkPicture* generate_new_picture(GM* gm) {
-    // Pictures are refcounted so must be on heap
-    SkPicture* pict = new SkPicture;
-    SkCanvas* cv = pict->beginRecording(1000, 1000);
-    invokeGM(gm, cv);
-    pict->endRecording();
-
-    return pict;
-}
-
-static SkPicture* stream_to_new_picture(const SkPicture& src) {
-
-    // To do in-memory commiunications with a stream, we need to:
-    // * create a dynamic memory stream
-    // * copy it into a buffer
-    // * create a read stream from it
-    // ?!?!
-
-    SkDynamicMemoryWStream storage;
-    src.serialize(&storage);
-
-    int streamSize = storage.getOffset();
-    SkAutoMalloc dstStorage(streamSize);
-    void* dst = dstStorage.get();
-    //char* dst = new char [streamSize];
-    //@todo thudson 22 April 2011 when can we safely delete [] dst?
-    storage.copyTo(dst);
-    SkMemoryStream pictReadback(dst, streamSize);
-    SkPicture* retval = new SkPicture (&pictReadback);
-    return retval;
-}
-
-// Test: draw into a bitmap or pdf.
-// Depending on flags, possibly compare to an expected image
-// and possibly output a diff image if it fails to match.
-static ErrorBitfield test_drawing(GM* gm,
-                                  const ConfigData& gRec,
-                                  const char writePath [],
-                                  const char readPath [],
-                                  const char diffPath [],
-                                  GrContext* context,
-                                  GrRenderTarget* rt,
-                                  SkBitmap* bitmap) {
-    SkDynamicMemoryWStream document;
-
-    if (gRec.fBackend == kRaster_Backend ||
-        gRec.fBackend == kGPU_Backend) {
-        // Early exit if we can't generate the image.
-        ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
-            false);
-        if (ERROR_NONE != errors) {
-            return errors;
+    static void force_all_opaque_8888(const SkBitmap& bitmap) {
+        SkAutoLockPixels lock(bitmap);
+        for (int y = 0; y < bitmap.height(); y++) {
+            for (int x = 0; x < bitmap.width(); x++) {
+                *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
+            }
         }
-    } else if (gRec.fBackend == kPDF_Backend) {
-        generate_pdf(gm, document);
-#if CAN_IMAGE_PDF
+    }
+
+    static bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
+        // TODO(epoger): Now that we have removed force_all_opaque()
+        // from this method, we should be able to get rid of the
+        // transformation to 8888 format also.
+        SkBitmap copy;
+        bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
+        return SkImageEncoder::EncodeFile(path.c_str(), copy,
+                                          SkImageEncoder::kPNG_Type, 100);
+    }
+
+    // Records an error in fFailedTests, if we want to record errors
+    // of this type.
+    void RecordError(ErrorBitfield errorType, const SkString& name,
+                     const char renderModeDescriptor []) {
+        bool isPixelError;
+        switch (errorType) {
+        case ERROR_NONE:
+            return;
+        case ERROR_READING_REFERENCE_IMAGE:
+            return;
+        case ERROR_IMAGE_MISMATCH:
+            isPixelError = true;
+            break;
+        default:
+            isPixelError = false;
+            break;
+        }
+
+        FailRec& rec = fFailedTests.push_back(make_name(
+            name.c_str(), renderModeDescriptor));
+        rec.fIsPixelError = isPixelError;
+    }
+
+    // List contents of fFailedTests via SkDebug.
+    void ListErrors() {
+        for (int i = 0; i < fFailedTests.count(); ++i) {
+            if (fFailedTests[i].fIsPixelError) {
+                SkDebugf("\t\t%s pixel_error\n", fFailedTests[i].fName.c_str());
+            } else {
+                SkDebugf("\t\t%s\n", fFailedTests[i].fName.c_str());
+            }
+        }
+    }
+
+    static bool write_document(const SkString& path,
+                               const SkDynamicMemoryWStream& document) {
+        SkFILEWStream stream(path.c_str());
         SkAutoDataUnref data(document.copyToData());
-        SkMemoryStream stream(data.data(), data.size());
-        SkPDFDocumentToBitmap(&stream, bitmap);
-#endif
-    } else if (gRec.fBackend == kXPS_Backend) {
-        generate_xps(gm, document);
+        return stream.writeData(data.get());
     }
-    return handle_test_results(gm, gRec, writePath, readPath, diffPath,
-                               "", *bitmap, &document, NULL);
-}
 
-static ErrorBitfield test_deferred_drawing(GM* gm,
-                         const ConfigData& gRec,
-                         const SkBitmap& comparisonBitmap,
-                         const char diffPath [],
-                         GrContext* context,
-                         GrRenderTarget* rt) {
-    SkDynamicMemoryWStream document;
+    /**
+     * Prepare an SkBitmap to render a GM into.
+     *
+     * After you've rendered the GM into the SkBitmap, you must call
+     * complete_bitmap()!
+     *
+     * @todo thudson 22 April 2011 - could refactor this to take in
+     * a factory to generate the context, always call readPixels()
+     * (logically a noop for rasters, if wasted time), and thus collapse the
+     * GPU special case and also let this be used for SkPicture testing.
+     */
+    static void setup_bitmap(const ConfigData& gRec, SkISize& size,
+                             SkBitmap* bitmap) {
+        bitmap->setConfig(gRec.fConfig, size.width(), size.height());
+        bitmap->allocPixels();
+        bitmap->eraseColor(SK_ColorTRANSPARENT);
+    }
 
-    if (gRec.fBackend == kRaster_Backend ||
-        gRec.fBackend == kGPU_Backend) {
-        SkBitmap bitmap;
-        // Early exit if we can't generate the image, but this is
-        // expected in some cases, so don't report a test failure.
-        if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
-            return ERROR_NONE;
+    /**
+     * Any finalization steps we need to perform on the SkBitmap after
+     * we have rendered the GM into it.
+     *
+     * It's too bad that we are throwing away alpha channel data
+     * we could otherwise be examining, but this had always been happening
+     * before... it was buried within the compare() method at
+     * https://code.google.com/p/skia/source/browse/trunk/gm/gmmain.cpp?r=7289#305 .
+     *
+     * Apparently we need this, at least for bitmaps that are either:
+     * (a) destined to be written out as PNG files, or
+     * (b) compared against bitmaps read in from PNG files
+     * for the reasons described just above the force_all_opaque() method.
+     *
+     * Neglecting to do this led to the difficult-to-diagnose
+     * http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating
+     * spurious pixel_error messages as of r7258')
+     *
+     * TODO(epoger): Come up with a better solution that allows us to
+     * compare full pixel data, including alpha channel, while still being
+     * robust in the face of transformations to/from PNG files.
+     * Options include:
+     *
+     * 1. Continue to call force_all_opaque(), but ONLY for bitmaps that
+     *    will be written to, or compared against, PNG files.
+     *    PRO: Preserve/compare alpha channel info for the non-PNG cases
+     *         (comparing different renderModes in-memory)
+     *    CON: The bitmaps (and checksums) for these non-PNG cases would be
+     *         different than those for the PNG-compared cases, and in the
+     *         case of a failed renderMode comparison, how would we write the
+     *         image to disk for examination?
+     *
+     * 2. Always compute image checksums from PNG format (either
+     *    directly from the the bytes of a PNG file, or capturing the
+     *    bytes we would have written to disk if we were writing the
+     *    bitmap out as a PNG).
+     *    PRO: I think this would allow us to never force opaque, and to
+     *         the extent that alpha channel data can be preserved in a PNG
+     *         file, we could observe it.
+     *    CON: If we read a bitmap from disk, we need to take its checksum
+     *         from the source PNG (we can't compute it from the bitmap we
+     *         read out of the PNG, because we will have already premultiplied
+     *         the alpha).
+     *    CON: Seems wasteful to convert a bitmap to PNG format just to take
+     *         its checksum. (Although we're wasting lots of effort already
+     *         calling force_all_opaque().)
+     *
+     * 3. Make the alpha premultiply/unpremultiply routines 100% consistent,
+     *    so we can transform images back and forth without fear of off-by-one
+     *    errors.
+     *    CON: Math is hard.
+     *
+     * 4. Perform a "close enough" comparison of bitmaps (+/- 1 bit in each
+     *    channel), rather than demanding absolute equality.
+     *    CON: Can't do this with checksums.
+     */
+    static void complete_bitmap(SkBitmap* bitmap) {
+        force_all_opaque(*bitmap);
+    }
+
+    static void installFilter(SkCanvas* canvas) {
+        if (gForceBWtext) {
+            canvas->setDrawFilter(new BWTextDrawFilter)->unref();
         }
-        return handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                                   "-deferred", bitmap, NULL, &comparisonBitmap);
     }
-    return ERROR_NONE;
-}
 
-static ErrorBitfield test_picture_playback(GM* gm,
-                                           const ConfigData& gRec,
-                                           const SkBitmap& comparisonBitmap,
-                                           const char readPath [],
-                                           const char diffPath []) {
-    SkPicture* pict = generate_new_picture(gm);
-    SkAutoUnref aur(pict);
+    static void invokeGM(GM* gm, SkCanvas* canvas, bool isPDF, bool isDeferred) {
+        SkAutoCanvasRestore acr(canvas, true);
 
-    if (kRaster_Backend == gRec.fBackend) {
-        SkBitmap bitmap;
-        generate_image_from_picture(gm, gRec, pict, &bitmap);
-        return handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                            "-replay", bitmap, NULL, &comparisonBitmap);
-    } else {
+        if (!isPDF) {
+            canvas->concat(gm->getInitialTransform());
+        }
+        installFilter(canvas);
+        gm->setCanvasIsDeferred(isDeferred);
+        gm->draw(canvas);
+        canvas->setDrawFilter(NULL);
+    }
+
+    static ErrorBitfield generate_image(GM* gm, const ConfigData& gRec,
+                                        GrContext* context,
+                                        GrRenderTarget* rt,
+                                        SkBitmap* bitmap,
+                                        bool deferred) {
+        SkISize size (gm->getISize());
+        setup_bitmap(gRec, size, bitmap);
+
+        SkAutoTUnref<SkCanvas> canvas;
+
+        if (gRec.fBackend == kRaster_Backend) {
+            SkAutoTUnref<SkDevice> device(new SkDevice(*bitmap));
+            if (deferred) {
+                canvas.reset(new SkDeferredCanvas(device));
+            } else {
+                canvas.reset(new SkCanvas(device));
+            }
+            invokeGM(gm, canvas, false, deferred);
+            canvas->flush();
+        }
+#if SK_SUPPORT_GPU
+        else {  // GPU
+            if (NULL == context) {
+                return ERROR_NO_GPU_CONTEXT;
+            }
+            SkAutoTUnref<SkDevice> device(new SkGpuDevice(context, rt));
+            if (deferred) {
+                canvas.reset(new SkDeferredCanvas(device));
+            } else {
+                canvas.reset(new SkCanvas(device));
+            }
+            invokeGM(gm, canvas, false, deferred);
+            // the device is as large as the current rendertarget, so
+            // we explicitly only readback the amount we expect (in
+            // size) overwrite our previous allocation
+            bitmap->setConfig(SkBitmap::kARGB_8888_Config, size.fWidth,
+                              size.fHeight);
+            canvas->readPixels(bitmap, 0, 0);
+        }
+#endif
+        complete_bitmap(bitmap);
         return ERROR_NONE;
     }
-}
 
-static ErrorBitfield test_picture_serialization(GM* gm,
-                                                const ConfigData& gRec,
-                                                const SkBitmap& comparisonBitmap,
-                                                const char readPath [],
-                                                const char diffPath []) {
-    SkPicture* pict = generate_new_picture(gm);
-    SkAutoUnref aurp(pict);
-    SkPicture* repict = stream_to_new_picture(*pict);
-    SkAutoUnref aurr(repict);
+    static void generate_image_from_picture(GM* gm, const ConfigData& gRec,
+                                            SkPicture* pict, SkBitmap* bitmap,
+                                            SkScalar scale = SK_Scalar1) {
+        SkISize size = gm->getISize();
+        setup_bitmap(gRec, size, bitmap);
+        SkCanvas canvas(*bitmap);
+        installFilter(&canvas);
+        canvas.scale(scale, scale);
+        canvas.drawPicture(*pict);
+        complete_bitmap(bitmap);
+    }
 
-    if (kRaster_Backend == gRec.fBackend) {
-        SkBitmap bitmap;
-        generate_image_from_picture(gm, gRec, repict, &bitmap);
-        return handle_test_results(gm, gRec, NULL, NULL, diffPath,
-                            "-serialize", bitmap, NULL, &comparisonBitmap);
-    } else {
+    static void generate_pdf(GM* gm, SkDynamicMemoryWStream& pdf) {
+#ifdef SK_SUPPORT_PDF
+        SkMatrix initialTransform = gm->getInitialTransform();
+        SkISize pageSize = gm->getISize();
+        SkPDFDevice* dev = NULL;
+        if (initialTransform.isIdentity()) {
+            dev = new SkPDFDevice(pageSize, pageSize, initialTransform);
+        } else {
+            SkRect content = SkRect::MakeWH(SkIntToScalar(pageSize.width()),
+                                            SkIntToScalar(pageSize.height()));
+            initialTransform.mapRect(&content);
+            content.intersect(0, 0, SkIntToScalar(pageSize.width()),
+                              SkIntToScalar(pageSize.height()));
+            SkISize contentSize =
+                SkISize::Make(SkScalarRoundToInt(content.width()),
+                              SkScalarRoundToInt(content.height()));
+            dev = new SkPDFDevice(pageSize, contentSize, initialTransform);
+        }
+        SkAutoUnref aur(dev);
+
+        SkCanvas c(dev);
+        invokeGM(gm, &c, true, false);
+
+        SkPDFDocument doc;
+        doc.appendPage(dev);
+        doc.emitPDF(&pdf);
+#endif
+    }
+
+    static void generate_xps(GM* gm, SkDynamicMemoryWStream& xps) {
+#ifdef SK_SUPPORT_XPS
+        SkISize size = gm->getISize();
+
+        SkSize trimSize = SkSize::Make(SkIntToScalar(size.width()),
+                                       SkIntToScalar(size.height()));
+        static const SkScalar inchesPerMeter = SkScalarDiv(10000, 254);
+        static const SkScalar upm = 72 * inchesPerMeter;
+        SkVector unitsPerMeter = SkPoint::Make(upm, upm);
+        static const SkScalar ppm = 200 * inchesPerMeter;
+        SkVector pixelsPerMeter = SkPoint::Make(ppm, ppm);
+
+        SkXPSDevice* dev = new SkXPSDevice();
+        SkAutoUnref aur(dev);
+
+        SkCanvas c(dev);
+        dev->beginPortfolio(&xps);
+        dev->beginSheet(unitsPerMeter, pixelsPerMeter, trimSize);
+        invokeGM(gm, &c, false, false);
+        dev->endSheet();
+        dev->endPortfolio();
+
+#endif
+    }
+
+    ErrorBitfield write_reference_image(
+      const ConfigData& gRec, const char writePath [],
+      const char renderModeDescriptor [], const SkString& name,
+        SkBitmap& bitmap, SkDynamicMemoryWStream* document) {
+        SkString path;
+        bool success = false;
+        if (gRec.fBackend == kRaster_Backend ||
+            gRec.fBackend == kGPU_Backend ||
+            (gRec.fBackend == kPDF_Backend && CAN_IMAGE_PDF)) {
+
+            path = make_filename(writePath, renderModeDescriptor, name.c_str(),
+                                 "png");
+            success = write_bitmap(path, bitmap);
+        }
+        if (kPDF_Backend == gRec.fBackend) {
+            path = make_filename(writePath, renderModeDescriptor, name.c_str(),
+                                 "pdf");
+            success = write_document(path, *document);
+        }
+        if (kXPS_Backend == gRec.fBackend) {
+            path = make_filename(writePath, renderModeDescriptor, name.c_str(),
+                                 "xps");
+            success = write_document(path, *document);
+        }
+        if (success) {
+            return ERROR_NONE;
+        } else {
+            fprintf(stderr, "FAILED to write %s\n", path.c_str());
+            RecordError(ERROR_WRITING_REFERENCE_IMAGE, name,
+                        renderModeDescriptor);
+            return ERROR_WRITING_REFERENCE_IMAGE;
+        }
+    }
+
+    /**
+     * Log more detail about the mistmatch between expectedBitmap and
+     * actualBitmap.
+     */
+    void report_bitmap_diffs(const SkBitmap& expectedBitmap, const SkBitmap& actualBitmap,
+                             const char *testName) {
+        const int expectedWidth = expectedBitmap.width();
+        const int expectedHeight = expectedBitmap.height();
+        const int width = actualBitmap.width();
+        const int height = actualBitmap.height();
+        if ((expectedWidth != width) || (expectedHeight != height)) {
+            SkDebugf("---- %s: dimension mismatch -- expected [%d %d], actual [%d %d]\n",
+                     testName, expectedWidth, expectedHeight, width, height);
+            return;
+        }
+
+        if ((SkBitmap::kARGB_8888_Config != expectedBitmap.config()) ||
+            (SkBitmap::kARGB_8888_Config != actualBitmap.config())) {
+            SkDebugf("---- %s: not computing max per-channel pixel mismatch because non-8888\n",
+                     testName);
+            return;
+        }
+
+        SkAutoLockPixels alp0(expectedBitmap);
+        SkAutoLockPixels alp1(actualBitmap);
+        int errR = 0;
+        int errG = 0;
+        int errB = 0;
+        int errA = 0;
+        int differingPixels = 0;
+
+        for (int y = 0; y < height; ++y) {
+            const SkPMColor* expectedPixelPtr = expectedBitmap.getAddr32(0, y);
+            const SkPMColor* actualPixelPtr = actualBitmap.getAddr32(0, y);
+            for (int x = 0; x < width; ++x) {
+                SkPMColor expectedPixel = *expectedPixelPtr++;
+                SkPMColor actualPixel = *actualPixelPtr++;
+                if (expectedPixel != actualPixel) {
+                    differingPixels++;
+                    errR = SkMax32(errR, SkAbs32((int)SkGetPackedR32(expectedPixel) -
+                                                 (int)SkGetPackedR32(actualPixel)));
+                    errG = SkMax32(errG, SkAbs32((int)SkGetPackedG32(expectedPixel) -
+                                                 (int)SkGetPackedG32(actualPixel)));
+                    errB = SkMax32(errB, SkAbs32((int)SkGetPackedB32(expectedPixel) -
+                                                 (int)SkGetPackedB32(actualPixel)));
+                    errA = SkMax32(errA, SkAbs32((int)SkGetPackedA32(expectedPixel) -
+                                                 (int)SkGetPackedA32(actualPixel)));
+                }
+            }
+        }
+        SkDebugf("---- %s: %d (of %d) differing pixels, max per-channel mismatch"
+                 " R=%d G=%d B=%d A=%d\n",
+                 testName, differingPixels, width*height, errR, errG, errB, errA);
+    }
+
+    /**
+     * Compares actual checksum to expectations.
+     * Returns ERROR_NONE if they match, or some particular error code otherwise
+     *
+     * If fMismatchPath has been set, and there are pixel diffs, then the
+     * actual bitmap will be written out to a file within fMismatchPath.
+     *
+     * @param expectations what expectations to compare actualBitmap against
+     * @param actualBitmap the image we actually generated
+     * @param baseNameString name of test without renderModeDescriptor added
+     * @param renderModeDescriptor e.g., "-rtree", "-deferred"
+     * @param addToJsonSummary whether to add these results (both actual and
+     *        expected) to the JSON summary
+     *
+     * TODO: For now, addToJsonSummary is only set to true within
+     * compare_test_results_to_stored_expectations(), so results of our
+     * in-memory comparisons (Rtree vs regular, etc.) are not written to the
+     * JSON summary.  We may wish to change that.
+     */
+    ErrorBitfield compare_to_expectations(Expectations expectations,
+                                          const SkBitmap& actualBitmap,
+                                          const SkString& baseNameString,
+                                          const char renderModeDescriptor[],
+                                          bool addToJsonSummary=false) {
+        ErrorBitfield retval;
+        Checksum actualChecksum = SkBitmapChecksummer::Compute64(actualBitmap);
+        SkString completeNameString = baseNameString;
+        completeNameString.append(renderModeDescriptor);
+        const char* completeName = completeNameString.c_str();
+
+        if (expectations.empty()) {
+            retval = ERROR_READING_REFERENCE_IMAGE;
+        } else if (expectations.match(actualChecksum)) {
+            retval = ERROR_NONE;
+        } else {
+            retval = ERROR_IMAGE_MISMATCH;
+
+            // Write out the "actuals" for any mismatches, if we have
+            // been directed to do so.
+            if (fMismatchPath) {
+                SkString path =
+                    make_filename(fMismatchPath, renderModeDescriptor,
+                                  baseNameString.c_str(), "png");
+                write_bitmap(path, actualBitmap);
+            }
+
+            // If we have access to a single expected bitmap, log more
+            // detail about the mismatch.
+            const SkBitmap *expectedBitmapPtr = expectations.asBitmap();
+            if (NULL != expectedBitmapPtr) {
+                report_bitmap_diffs(*expectedBitmapPtr, actualBitmap, completeName);
+            }
+        }
+        RecordError(retval, baseNameString, renderModeDescriptor);
+
+        if (addToJsonSummary) {
+            add_actual_results_to_json_summary(completeName, actualChecksum,
+                                               retval,
+                                               expectations.ignoreFailure());
+            add_expected_results_to_json_summary(completeName, expectations);
+        }
+
+        return retval;
+    }
+
+    /**
+     * Add this result to the appropriate JSON collection of actual results,
+     * depending on status.
+     */
+    void add_actual_results_to_json_summary(const char testName[],
+                                            Checksum actualChecksum,
+                                            ErrorBitfield result,
+                                            bool ignoreFailure) {
+        Json::Value actualResults;
+        actualResults[kJsonKey_ActualResults_AnyStatus_Checksum] =
+            asJsonValue(actualChecksum);
+        if (ERROR_NONE == result) {
+            this->fJsonActualResults_Succeeded[testName] = actualResults;
+        } else {
+            if (ignoreFailure) {
+                // TODO: Once we have added the ability to compare
+                // actual results against expectations in a JSON file
+                // (where we can set ignore-failure to either true or
+                // false), add test cases that exercise ignored
+                // failures (both for ERROR_READING_REFERENCE_IMAGE
+                // and ERROR_IMAGE_MISMATCH).
+                this->fJsonActualResults_FailureIgnored[testName] =
+                    actualResults;
+            } else {
+                switch(result) {
+                case ERROR_READING_REFERENCE_IMAGE:
+                    // TODO: What about the case where there IS an
+                    // expected image checksum, but that gm test
+                    // doesn't actually run?  For now, those cases
+                    // will always be ignored, because gm only looks
+                    // at expectations that correspond to gm tests
+                    // that were actually run.
+                    //
+                    // Once we have the ability to express
+                    // expectations as a JSON file, we should fix this
+                    // (and add a test case for which an expectation
+                    // is given but the test is never run).
+                    this->fJsonActualResults_NoComparison[testName] =
+                        actualResults;
+                    break;
+                case ERROR_IMAGE_MISMATCH:
+                    this->fJsonActualResults_Failed[testName] = actualResults;
+                    break;
+                default:
+                    fprintf(stderr, "encountered unexpected result %d\n",
+                            result);
+                    SkDEBUGFAIL("encountered unexpected result");
+                    break;
+                }
+            }
+        }
+    }
+
+    /**
+     * Add this test to the JSON collection of expected results.
+     */
+    void add_expected_results_to_json_summary(const char testName[],
+                                              Expectations expectations) {
+        // For now, we assume that this collection starts out empty and we
+        // just fill it in as we go; once gm accepts a JSON file as input,
+        // we'll have to change that.
+        Json::Value expectedResults;
+        expectedResults[kJsonKey_ExpectedResults_Checksums] =
+            expectations.allowedChecksumsAsJson();
+        expectedResults[kJsonKey_ExpectedResults_IgnoreFailure] =
+            expectations.ignoreFailure();
+        this->fJsonExpectedResults[testName] = expectedResults;
+    }
+
+    /**
+     * Compare actualBitmap to expectations stored in this->fExpectationsSource.
+     *
+     * @param gm which test generated the actualBitmap
+     * @param gRec
+     * @param writePath unless this is NULL, write out actual images into this
+     *        directory
+     * @param actualBitmap bitmap generated by this run
+     * @param pdf
+     */
+    ErrorBitfield compare_test_results_to_stored_expectations(
+        GM* gm, const ConfigData& gRec, const char writePath[],
+        SkBitmap& actualBitmap, SkDynamicMemoryWStream* pdf) {
+
+        SkString name = make_name(gm->shortName(), gRec.fName);
+        ErrorBitfield retval = ERROR_NONE;
+
+        ExpectationsSource *expectationsSource =
+            this->fExpectationsSource.get();
+        if (expectationsSource && (gRec.fFlags & kRead_ConfigFlag)) {
+            /*
+             * Get the expected results for this test, as one or more allowed
+             * checksums. The current implementation of expectationsSource
+             * get this by computing the checksum of a single PNG file on disk.
+             *
+             * TODO(epoger): This relies on the fact that
+             * force_all_opaque() was called on the bitmap before it
+             * was written to disk as a PNG in the first place. If
+             * not, the checksum returned here may not match the
+             * checksum of actualBitmap, which *has* been run through
+             * force_all_opaque().
+             * See comments above complete_bitmap() for more detail.
+             */
+            Expectations expectations = expectationsSource->get(name.c_str());
+            retval |= compare_to_expectations(expectations, actualBitmap,
+                                              name, "", true);
+        } else {
+            // If we are running without expectations, we still want to
+            // record the actual results.
+            Checksum actualChecksum =
+                SkBitmapChecksummer::Compute64(actualBitmap);
+            add_actual_results_to_json_summary(name.c_str(), actualChecksum,
+                                               ERROR_READING_REFERENCE_IMAGE,
+                                               false);
+        }
+
+        // TODO: Consider moving this into compare_to_expectations(),
+        // similar to fMismatchPath... for now, we don't do that, because
+        // we don't want to write out the actual bitmaps for all
+        // renderModes of all tests!  That would be a lot of files.
+        if (writePath && (gRec.fFlags & kWrite_ConfigFlag)) {
+            retval |= write_reference_image(gRec, writePath, "",
+                                            name, actualBitmap, pdf);
+        }
+
+        return retval;
+    }
+
+    /**
+     * Compare actualBitmap to referenceBitmap.
+     *
+     * @param gm which test generated the bitmap
+     * @param gRec
+     * @param renderModeDescriptor
+     * @param actualBitmap actual bitmap generated by this run
+     * @param referenceBitmap bitmap we expected to be generated
+     */
+    ErrorBitfield compare_test_results_to_reference_bitmap(
+        GM* gm, const ConfigData& gRec, const char renderModeDescriptor [],
+        SkBitmap& actualBitmap, const SkBitmap* referenceBitmap) {
+
+        SkASSERT(referenceBitmap);
+        SkString name = make_name(gm->shortName(), gRec.fName);
+        Expectations expectations(*referenceBitmap);
+        return compare_to_expectations(expectations, actualBitmap,
+                                       name, renderModeDescriptor);
+    }
+
+    static SkPicture* generate_new_picture(GM* gm, BbhType bbhType, uint32_t recordFlags,
+                                           SkScalar scale = SK_Scalar1) {
+        // Pictures are refcounted so must be on heap
+        SkPicture* pict;
+        int width = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().width()), scale));
+        int height = SkScalarCeilToInt(SkScalarMul(SkIntToScalar(gm->getISize().height()), scale));
+
+        if (kTileGrid_BbhType == bbhType) {
+            pict = new SkTileGridPicture(16, 16, width, height);
+        } else {
+            pict = new SkPicture;
+        }
+        if (kNone_BbhType != bbhType) {
+            recordFlags |= SkPicture::kOptimizeForClippedPlayback_RecordingFlag;
+        }
+        SkCanvas* cv = pict->beginRecording(width, height, recordFlags);
+        cv->scale(scale, scale);
+        invokeGM(gm, cv, false, false);
+        pict->endRecording();
+
+        return pict;
+    }
+
+    static SkPicture* stream_to_new_picture(const SkPicture& src) {
+
+        // To do in-memory commiunications with a stream, we need to:
+        // * create a dynamic memory stream
+        // * copy it into a buffer
+        // * create a read stream from it
+        // ?!?!
+
+        SkDynamicMemoryWStream storage;
+        src.serialize(&storage);
+
+        int streamSize = storage.getOffset();
+        SkAutoMalloc dstStorage(streamSize);
+        void* dst = dstStorage.get();
+        //char* dst = new char [streamSize];
+        //@todo thudson 22 April 2011 when can we safely delete [] dst?
+        storage.copyTo(dst);
+        SkMemoryStream pictReadback(dst, streamSize);
+        SkPicture* retval = new SkPicture (&pictReadback);
+        return retval;
+    }
+
+    // Test: draw into a bitmap or pdf.
+    // Depending on flags, possibly compare to an expected image.
+    ErrorBitfield test_drawing(GM* gm,
+                               const ConfigData& gRec,
+                               const char writePath [],
+                               GrContext* context,
+                               GrRenderTarget* rt,
+                               SkBitmap* bitmap) {
+        SkDynamicMemoryWStream document;
+
+        if (gRec.fBackend == kRaster_Backend ||
+            gRec.fBackend == kGPU_Backend) {
+            // Early exit if we can't generate the image.
+            ErrorBitfield errors = generate_image(gm, gRec, context, rt, bitmap,
+                                                  false);
+            if (ERROR_NONE != errors) {
+                // TODO: Add a test to exercise what the stdout and
+                // JSON look like if we get an "early error" while
+                // trying to generate the image.
+                return errors;
+            }
+        } else if (gRec.fBackend == kPDF_Backend) {
+            generate_pdf(gm, document);
+#if CAN_IMAGE_PDF
+            SkAutoDataUnref data(document.copyToData());
+            SkMemoryStream stream(data->data(), data->size());
+            SkPDFDocumentToBitmap(&stream, bitmap);
+#endif
+        } else if (gRec.fBackend == kXPS_Backend) {
+            generate_xps(gm, document);
+        }
+        return compare_test_results_to_stored_expectations(
+            gm, gRec, writePath, *bitmap, &document);
+    }
+
+    ErrorBitfield test_deferred_drawing(GM* gm,
+                                        const ConfigData& gRec,
+                                        const SkBitmap& referenceBitmap,
+                                        GrContext* context,
+                                        GrRenderTarget* rt) {
+        SkDynamicMemoryWStream document;
+
+        if (gRec.fBackend == kRaster_Backend ||
+            gRec.fBackend == kGPU_Backend) {
+            SkBitmap bitmap;
+            // Early exit if we can't generate the image, but this is
+            // expected in some cases, so don't report a test failure.
+            if (!generate_image(gm, gRec, context, rt, &bitmap, true)) {
+                return ERROR_NONE;
+            }
+            return compare_test_results_to_reference_bitmap(
+                gm, gRec, "-deferred", bitmap, &referenceBitmap);
+        }
         return ERROR_NONE;
     }
-}
 
-static void usage(const char * argv0) {
-    SkDebugf(
-        "%s [-w writePath] [-r readPath] [-d diffPath] [--noreplay]\n"
-        "    [--serialize] [--forceBWtext] [--nopdf] [--nodeferred]\n"
-        "    [--match substring] [--notexturecache]"
-#if SK_MESA
-        " [--mesagl]"
+    ErrorBitfield test_pipe_playback(GM* gm,
+                                     const ConfigData& gRec,
+                                     const SkBitmap& referenceBitmap) {
+        ErrorBitfield errors = ERROR_NONE;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
+            SkBitmap bitmap;
+            SkISize size = gm->getISize();
+            setup_bitmap(gRec, size, &bitmap);
+            SkCanvas canvas(bitmap);
+            PipeController pipeController(&canvas);
+            SkGPipeWriter writer;
+            SkCanvas* pipeCanvas = writer.startRecording(
+              &pipeController, gPipeWritingFlagCombos[i].flags);
+            invokeGM(gm, pipeCanvas, false, false);
+            complete_bitmap(&bitmap);
+            writer.endRecording();
+            SkString string("-pipe");
+            string.append(gPipeWritingFlagCombos[i].name);
+            errors |= compare_test_results_to_reference_bitmap(
+                gm, gRec, string.c_str(), bitmap, &referenceBitmap);
+            if (errors != ERROR_NONE) {
+                break;
+            }
+        }
+        return errors;
+    }
+
+    ErrorBitfield test_tiled_pipe_playback(
+      GM* gm, const ConfigData& gRec, const SkBitmap& referenceBitmap) {
+        ErrorBitfield errors = ERROR_NONE;
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gPipeWritingFlagCombos); ++i) {
+            SkBitmap bitmap;
+            SkISize size = gm->getISize();
+            setup_bitmap(gRec, size, &bitmap);
+            SkCanvas canvas(bitmap);
+            TiledPipeController pipeController(bitmap);
+            SkGPipeWriter writer;
+            SkCanvas* pipeCanvas = writer.startRecording(
+              &pipeController, gPipeWritingFlagCombos[i].flags);
+            invokeGM(gm, pipeCanvas, false, false);
+            complete_bitmap(&bitmap);
+            writer.endRecording();
+            SkString string("-tiled pipe");
+            string.append(gPipeWritingFlagCombos[i].name);
+            errors |= compare_test_results_to_reference_bitmap(
+                gm, gRec, string.c_str(), bitmap, &referenceBitmap);
+            if (errors != ERROR_NONE) {
+                break;
+            }
+        }
+        return errors;
+    }
+
+    //
+    // member variables.
+    // They are public for now, to allow easier setting by tool_main().
+    //
+
+    bool fUseFileHierarchy;
+
+    const char* fMismatchPath;
+
+    // information about all failed tests we have encountered so far
+    SkTArray<FailRec> fFailedTests;
+
+    // Where to read expectations (expected image checksums, etc.) from.
+    // If unset, we don't do comparisons.
+    SkAutoTUnref<ExpectationsSource> fExpectationsSource;
+
+    // JSON summaries that we generate as we go (just for output).
+    Json::Value fJsonExpectedResults;
+    Json::Value fJsonActualResults_Failed;
+    Json::Value fJsonActualResults_FailureIgnored;
+    Json::Value fJsonActualResults_NoComparison;
+    Json::Value fJsonActualResults_Succeeded;
+
+}; // end of GMMain class definition
+
+#if SK_SUPPORT_GPU
+static const GLContextType kDontCare_GLContextType = GrContextFactory::kNative_GLContextType;
+#else
+static const GLContextType kDontCare_GLContextType = 0;
 #endif
-        "\n\n", argv0);
-    SkDebugf("    writePath: directory to write rendered images in.\n");
-    SkDebugf(
-"    readPath: directory to read reference images from;\n"
-"        reports if any pixels mismatch between reference and new images\n");
-    SkDebugf("    diffPath: directory to write difference images in.\n");
-    SkDebugf("    --noreplay: do not exercise SkPicture replay.\n");
-    SkDebugf(
-"    --serialize: exercise SkPicture serialization & deserialization.\n");
-    SkDebugf("    --forceBWtext: disable text anti-aliasing.\n");
-    SkDebugf("    --nopdf: skip the pdf rendering test pass.\n");
-    SkDebugf("    --nodeferred: skip the deferred rendering test pass.\n");
-    SkDebugf("    --match foo will only run tests that substring match foo.\n");
-#if SK_MESA
-    SkDebugf("    --mesagl will run using the osmesa sw gl rasterizer.\n");
-#endif
-    SkDebugf("    --notexturecache: disable the gpu texture cache.\n");
-}
+
+// If the platform does not support writing PNGs of PDFs then there will be no
+// reference images to read. However, we can always write the .pdf files
+static const ConfigFlags kPDFConfigFlags = CAN_IMAGE_PDF ? kRW_ConfigFlag :
+                                                           kWrite_ConfigFlag;
 
 static const ConfigData gRec[] = {
-    { SkBitmap::kARGB_8888_Config, kRaster_Backend, "8888" },
-    { SkBitmap::kARGB_4444_Config, kRaster_Backend, "4444" },
-    { SkBitmap::kRGB_565_Config,   kRaster_Backend, "565" },
-#ifdef SK_SCALAR_IS_FLOAT
-    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    "gpu" },
+    { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "8888" },
+#if 0   // stop testing this (for now at least) since we want to remove support for it (soon please!!!)
+    { SkBitmap::kARGB_4444_Config, kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "4444" },
 #endif
-#ifdef SK_SUPPORT_PDF
-    { SkBitmap::kARGB_8888_Config, kPDF_Backend,    "pdf" },
+    { SkBitmap::kRGB_565_Config,   kRaster_Backend, kDontCare_GLContextType,                  0, kRW_ConfigFlag,    "565" },
+#if defined(SK_SCALAR_IS_FLOAT) && SK_SUPPORT_GPU
+    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNative_GLContextType,  0, kRW_ConfigFlag,    "gpu" },
+#ifndef SK_BUILD_FOR_ANDROID
+    // currently we don't want to run MSAA tests on Android
+    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kNative_GLContextType, 16, kRW_ConfigFlag,    "msaa16" },
 #endif
+    /* The debug context does not generate images */
+    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kDebug_GLContextType,   0, kNone_ConfigFlag,  "debug" },
+#if SK_ANGLE
+    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,   0, kRW_ConfigFlag,    "angle" },
+    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kANGLE_GLContextType,  16, kRW_ConfigFlag,    "anglemsaa16" },
+#endif // SK_ANGLE
+#ifdef SK_MESA
+    { SkBitmap::kARGB_8888_Config, kGPU_Backend,    GrContextFactory::kMESA_GLContextType,    0, kRW_ConfigFlag,    "mesa" },
+#endif // SK_MESA
+#endif // defined(SK_SCALAR_IS_FLOAT) && SK_SUPPORT_GPU
 #ifdef SK_SUPPORT_XPS
-    { SkBitmap::kARGB_8888_Config, kXPS_Backend,    "xps" },
-#endif
+    /* At present we have no way of comparing XPS files (either natively or by converting to PNG). */
+    { SkBitmap::kARGB_8888_Config, kXPS_Backend,    kDontCare_GLContextType,                  0, kWrite_ConfigFlag, "xps" },
+#endif // SK_SUPPORT_XPS
+#ifdef SK_SUPPORT_PDF
+    { SkBitmap::kARGB_8888_Config, kPDF_Backend,    kDontCare_GLContextType,                  0, kPDFConfigFlags,   "pdf" },
+#endif // SK_SUPPORT_PDF
 };
 
+static void usage(const char * argv0) {
+    SkDebugf("%s\n", argv0);
+    SkDebugf("    [--config ");
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+        if (i > 0) {
+            SkDebugf("|");
+        }
+        SkDebugf(gRec[i].fName);
+    }
+    SkDebugf("]:\n        run these configurations\n");
+    SkDebugf(
+// Alphabetized ignoring "no" prefix ("readPath", "noreplay", "resourcePath").
+// It would probably be better if we allowed both yes-and-no settings for each
+// one, e.g.:
+// [--replay|--noreplay]: whether to exercise SkPicture replay; default is yes
+"    [--nodeferred]: skip the deferred rendering test pass\n"
+"    [--disable-missing-warning]: don't print a message to stderr if\n"
+"        unable to read a reference image for any tests (NOT default behavior)\n"
+"    [--enable-missing-warning]: print message to stderr (but don't fail) if\n"
+"        unable to read a reference image for any tests (default behavior)\n"
+"    [--exclude-config]: disable this config (may be used multiple times)\n"
+"    [--forceBWtext]: disable text anti-aliasing\n"
+"    [--help|-h]: show this help message\n"
+"    [--hierarchy|--nohierarchy]: whether to use multilevel directory structure\n"
+"        when reading/writing files; default is no\n"
+"    [--match <substring>]: only run tests whose name includes this substring\n"
+"    [--mismatchPath <path>]: write images for tests that failed due to\n"
+"        pixel mismatched into this directory"
+"    [--modulo <remainder> <divisor>]: only run tests for which \n"
+"        testIndex %% divisor == remainder\n"
+"    [--nopdf]: skip the pdf rendering test pass\n"
+"    [--nopipe]: Skip SkGPipe replay\n"
+"    [--readPath|-r <path>]: read reference images from this dir, and report\n"
+"        any differences between those and the newly generated ones\n"
+"    [--noreplay]: do not exercise SkPicture replay\n"
+"    [--resourcePath|-i <path>]: directory that stores image resources\n"
+"    [--nortree]: Do not exercise the R-Tree variant of SkPicture\n"
+"    [--noserialize]: do not exercise SkPicture serialization & deserialization\n"
+"    [--notexturecache]: disable the gpu texture cache\n"
+"    [--tiledPipe]: Exercise tiled SkGPipe replay\n"
+"    [--notileGrid]: Do not exercise the tile grid variant of SkPicture\n"
+"    [--tileGridReplayScales <scales>]: Comma separated list of floating-point scale\n"
+"        factors to be used for tileGrid playback testing. Default value: 1.0\n"
+"    [--writeJsonSummary <path>]: write a JSON-formatted result summary to this file\n"
+"    [--verbose] print diagnostics (e.g. list each config to be tested)\n"
+"    [--writePath|-w <path>]: write rendered images into this directory\n"
+"    [--writePicturePath|-wp <path>]: write .skp files into this directory\n"
+             );
+}
+
+static int findConfig(const char config[]) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        if (!strcmp(config, gRec[i].fName)) {
+            return i;
+        }
+    }
+    return -1;
+}
+
 static bool skip_name(const SkTDArray<const char*> array, const char name[]) {
     if (0 == array.count()) {
         // no names, so don't skip anything
@@ -644,121 +1104,316 @@
 }
 
 namespace skiagm {
-static GrContext* gGrContext;
-GrContext* GetGr() {
-    return gGrContext;
-}
+#if SK_SUPPORT_GPU
+SkAutoTUnref<GrContext> gGrContext;
+/**
+ * Sets the global GrContext, accessible by individual GMs
+ */
+static void SetGr(GrContext* grContext) {
+    SkSafeRef(grContext);
+    gGrContext.reset(grContext);
 }
 
-int main(int argc, char * const argv[]) {
-    SkAutoGraphics ag;
+/**
+ * Gets the global GrContext, can be called by GM tests.
+ */
+GrContext* GetGr();
+GrContext* GetGr() {
+    return gGrContext.get();
+}
+
+/**
+ * Sets the global GrContext and then resets it to its previous value at
+ * destruction.
+ */
+class AutoResetGr : SkNoncopyable {
+public:
+    AutoResetGr() : fOld(NULL) {}
+    void set(GrContext* context) {
+        SkASSERT(NULL == fOld);
+        fOld = GetGr();
+        SkSafeRef(fOld);
+        SetGr(context);
+    }
+    ~AutoResetGr() { SetGr(fOld); SkSafeUnref(fOld); }
+private:
+    GrContext* fOld;
+};
+#else
+GrContext* GetGr() { return NULL; }
+#endif
+}
+
+template <typename T> void appendUnique(SkTDArray<T>* array, const T& value) {
+    int index = array->find(value);
+    if (index < 0) {
+        *array->append() = value;
+    }
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+
+#if SK_ENABLE_INST_COUNT
+    gPrintInstCount = true;
+#endif
+
+    SkGraphics::Init();
     // we don't need to see this during a run
     gSkSuppressFontCachePurgeSpew = true;
 
     setSystemPreferences();
+    GMMain gmmain;
 
+    const char* writeJsonSummaryPath = NULL;// if non-null, where we write the JSON summary
     const char* writePath = NULL;   // if non-null, where we write the originals
+    const char* writePicturePath = NULL;    // if non-null, where we write serialized pictures
     const char* readPath = NULL;    // if non-null, were we read from to compare
-    const char* diffPath = NULL;    // if non-null, where we write our diffs (from compare)
+    const char* resourcePath = NULL;// if non-null, where we read from for image resources
+
+    // if true, emit a message when we can't find a reference image to compare
+    bool notifyMissingReadReference = true;
 
     SkTDArray<const char*> fMatches;
 
     bool doPDF = true;
     bool doReplay = true;
-    bool doSerialize = false;
-    bool useMesa = false;
+    bool doPipe = true;
+    bool doTiledPipe = false;
+    bool doSerialize = true;
     bool doDeferred = true;
+    bool doRTree = true;
+    bool doTileGrid = true;
+    bool doVerbose = false;
     bool disableTextureCache = false;
+    SkTDArray<size_t> configs;
+    SkTDArray<size_t> excludeConfigs;
+    SkTDArray<SkScalar> tileGridReplayScales;
+    *tileGridReplayScales.append() = SK_Scalar1; // By default only test at scale 1.0
+    bool userConfig = false;
+
+    int moduloRemainder = -1;
+    int moduloDivisor = -1;
 
     const char* const commandName = argv[0];
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
-        if (strcmp(*argv, "-w") == 0) {
+        if (strcmp(*argv, "--config") == 0) {
             argv++;
-            if (argv < stop && **argv) {
-                writePath = *argv;
+            if (argv < stop) {
+                int index = findConfig(*argv);
+                if (index >= 0) {
+                    appendUnique<size_t>(&configs, index);
+                    userConfig = true;
+                } else {
+                    SkString str;
+                    str.printf("unrecognized config %s\n", *argv);
+                    SkDebugf(str.c_str());
+                    usage(commandName);
+                    return -1;
+                }
+            } else {
+                SkDebugf("missing arg for --config\n");
+                usage(commandName);
+                return -1;
             }
-        } else if (strcmp(*argv, "-r") == 0) {
+        } else if (strcmp(*argv, "--exclude-config") == 0) {
             argv++;
-            if (argv < stop && **argv) {
-                readPath = *argv;
+            if (argv < stop) {
+                int index = findConfig(*argv);
+                if (index >= 0) {
+                    *excludeConfigs.append() = index;
+                } else {
+                    SkString str;
+                    str.printf("unrecognized exclude-config %s\n", *argv);
+                    SkDebugf(str.c_str());
+                    usage(commandName);
+                    return -1;
+                }
+            } else {
+                SkDebugf("missing arg for --exclude-config\n");
+                usage(commandName);
+                return -1;
             }
-        } else if (strcmp(*argv, "-d") == 0) {
-            argv++;
-            if (argv < stop && **argv) {
-                diffPath = *argv;
-            }
-        } else if (strcmp(*argv, "--forceBWtext") == 0) {
-            gForceBWtext = true;
-        } else if (strcmp(*argv, "--noreplay") == 0) {
-            doReplay = false;
-        } else if (strcmp(*argv, "--nopdf") == 0) {
-            doPDF = false;
         } else if (strcmp(*argv, "--nodeferred") == 0) {
             doDeferred = false;
-        } else if (strcmp(*argv, "--serialize") == 0) {
-            doSerialize = true;
+        } else if (strcmp(*argv, "--disable-missing-warning") == 0) {
+            notifyMissingReadReference = false;
+        } else if (strcmp(*argv, "--mismatchPath") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                gmmain.fMismatchPath = *argv;
+            }
+        } else if (strcmp(*argv, "--nortree") == 0) {
+            doRTree = false;
+        } else if (strcmp(*argv, "--notileGrid") == 0) {
+            doTileGrid = false;
+        } else if (strcmp(*argv, "--tileGridReplayScales") == 0) {
+            tileGridReplayScales.reset();
+            ++argv;
+            if (argv < stop) {
+                char* token = strtok(*argv, ",");
+                while (NULL != token) {
+                    double val = atof(token);
+                    if (0 < val) {
+                        *tileGridReplayScales.append() = SkDoubleToScalar(val);
+                    }
+                    token = strtok(NULL, ",");
+                }
+            }
+            if (0 == tileGridReplayScales.count()) {
+                // Should have at least one scale
+                usage(commandName);
+                return -1;
+            }
+        } else if (strcmp(*argv, "--enable-missing-warning") == 0) {
+            notifyMissingReadReference = true;
+        } else if (strcmp(*argv, "--forceBWtext") == 0) {
+            gForceBWtext = true;
+        } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
+            usage(commandName);
+            return -1;
+        } else if (strcmp(*argv, "--hierarchy") == 0) {
+            gmmain.fUseFileHierarchy = true;
+        } else if (strcmp(*argv, "--nohierarchy") == 0) {
+            gmmain.fUseFileHierarchy = false;
         } else if (strcmp(*argv, "--match") == 0) {
             ++argv;
             if (argv < stop && **argv) {
                 // just record the ptr, no need for a deep copy
                 *fMatches.append() = *argv;
             }
-#if SK_MESA
-        } else if (strcmp(*argv, "--mesagl") == 0) {
-            useMesa = true;
-#endif
+        } else if (strcmp(*argv, "--modulo") == 0) {
+            ++argv;
+            if (argv >= stop) {
+                continue;
+            }
+            moduloRemainder = atoi(*argv);
+
+            ++argv;
+            if (argv >= stop) {
+                continue;
+            }
+            moduloDivisor = atoi(*argv);
+            if (moduloRemainder < 0 || moduloDivisor <= 0 || moduloRemainder >= moduloDivisor) {
+                SkDebugf("invalid modulo values.");
+                return -1;
+            }
+        } else if (strcmp(*argv, "--nopdf") == 0) {
+            doPDF = false;
+        } else if (strcmp(*argv, "--nopipe") == 0) {
+            doPipe = false;
+        } else if ((0 == strcmp(*argv, "--readPath")) ||
+                   (0 == strcmp(*argv, "-r"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                readPath = *argv;
+            }
+        } else if (strcmp(*argv, "--noreplay") == 0) {
+            doReplay = false;
+        } else if ((0 == strcmp(*argv, "--resourcePath")) ||
+                   (0 == strcmp(*argv, "-i"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                resourcePath = *argv;
+            }
+        } else if (strcmp(*argv, "--serialize") == 0) {
+            doSerialize = true;
+        } else if (strcmp(*argv, "--noserialize") == 0) {
+            doSerialize = false;
         } else if (strcmp(*argv, "--notexturecache") == 0) {
-          disableTextureCache = true;
+            disableTextureCache = true;
+        } else if (strcmp(*argv, "--tiledPipe") == 0) {
+            doTiledPipe = true;
+        } else if (!strcmp(*argv, "--verbose") || !strcmp(*argv, "-v")) {
+            doVerbose = true;
+        } else if ((0 == strcmp(*argv, "--writePath")) ||
+            (0 == strcmp(*argv, "-w"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                writePath = *argv;
+            }
+        } else if (0 == strcmp(*argv, "--writeJsonSummary")) {
+            argv++;
+            if (argv < stop && **argv) {
+                writeJsonSummaryPath = *argv;
+            }
+        } else if ((0 == strcmp(*argv, "--writePicturePath")) ||
+                   (0 == strcmp(*argv, "-wp"))) {
+            argv++;
+            if (argv < stop && **argv) {
+                writePicturePath = *argv;
+            }
         } else {
-          usage(commandName);
-          return -1;
+            usage(commandName);
+            return -1;
         }
     }
     if (argv != stop) {
-      usage(commandName);
-      return -1;
+        usage(commandName);
+        return -1;
     }
 
-    int maxW = -1;
-    int maxH = -1;
-    Iter iter;
-    GM* gm;
-    while ((gm = iter.next()) != NULL) {
-        SkISize size = gm->getISize();
-        maxW = SkMax32(size.width(), maxW);
-        maxH = SkMax32(size.height(), maxH);
-    }
-    // setup a GL context for drawing offscreen
-    SkAutoTUnref<SkGLContext> glContext;
-#if SK_MESA
-    if (useMesa) {
-        glContext.reset(new SkMesaGLContext());
-    } else
-#endif
-    {
-        glContext.reset(new SkNativeGLContext());
-    }
-
-    GrPlatformRenderTargetDesc rtDesc;
-    if (glContext.get()->init(maxW, maxH)) {
-        GrPlatform3DContext ctx =
-            reinterpret_cast<GrPlatform3DContext>(glContext.get()->gl());
-        gGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine, ctx);
-        if (NULL != gGrContext) {
-            rtDesc.fConfig = kSkia8888_PM_GrPixelConfig;
-            rtDesc.fStencilBits = 8;
-            rtDesc.fRenderTargetHandle = glContext.get()->getFBOID();
+    if (!userConfig) {
+        // if no config is specified by user, we add them all.
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
+            *configs.append() = i;
         }
-    } else {
-        fprintf(stderr, "could not create GL context.\n");
     }
+    // now remove any explicitly excluded configs
+    for (int i = 0; i < excludeConfigs.count(); ++i) {
+        int index = configs.find(excludeConfigs[i]);
+        if (index >= 0) {
+            configs.remove(index);
+            // now assert that there was only one copy in configs[]
+            SkASSERT(configs.find(excludeConfigs[i]) < 0);
+        }
+    }
+
+    if (doVerbose) {
+        SkString str;
+        str.printf("gm: %d configs:", configs.count());
+        for (int i = 0; i < configs.count(); ++i) {
+            str.appendf(" %s", gRec[configs[i]].fName);
+        }
+        SkDebugf("%s\n", str.c_str());
+    }
+
+    GM::SetResourcePath(resourcePath);
 
     if (readPath) {
-        fprintf(stderr, "reading from %s\n", readPath);
-    } else if (writePath) {
+        if (!sk_exists(readPath)) {
+            fprintf(stderr, "readPath %s does not exist!\n", readPath);
+            return -1;
+        }
+        if (sk_isdir(readPath)) {
+            fprintf(stderr, "reading from %s\n", readPath);
+            gmmain.fExpectationsSource.reset(SkNEW_ARGS(
+                IndividualImageExpectationsSource,
+                (readPath, notifyMissingReadReference)));
+        } else {
+            fprintf(stderr, "reading expectations from JSON summary file %s ",
+                    readPath);
+            fprintf(stderr, "BUT WE DON'T KNOW HOW TO DO THIS YET!\n");
+            return -1;
+        }
+    }
+    if (writePath) {
         fprintf(stderr, "writing to %s\n", writePath);
     }
+    if (writePicturePath) {
+        fprintf(stderr, "writing pictures to %s\n", writePicturePath);
+    }
+    if (resourcePath) {
+        fprintf(stderr, "reading resources from %s\n", resourcePath);
+    }
+
+    if (moduloDivisor <= 0) {
+        moduloRemainder = -1;
+    }
+    if (moduloRemainder < 0 || moduloRemainder >= moduloDivisor) {
+        moduloRemainder = -1;
+    }
 
     // Accumulate success of all tests.
     int testsRun = 0;
@@ -766,12 +1421,46 @@
     int testsFailed = 0;
     int testsMissingReferenceImages = 0;
 
+#if SK_SUPPORT_GPU
+    GrContextFactory* grFactory = new GrContextFactory;
     if (disableTextureCache) {
         skiagm::GetGr()->setTextureCacheLimits(0, 0);
     }
+#endif
 
-    iter.reset();
+    int gmIndex = -1;
+    SkString moduloStr;
+
+    // If we will be writing out files, prepare subdirectories.
+    if (writePath) {
+        if (!sk_mkdir(writePath)) {
+            return -1;
+        }
+        if (gmmain.fUseFileHierarchy) {
+            for (int i = 0; i < configs.count(); i++) {
+                ConfigData config = gRec[configs[i]];
+                SkString subdir;
+                subdir.appendf("%s%c%s", writePath, SkPATH_SEPARATOR,
+                               config.fName);
+                if (!sk_mkdir(subdir.c_str())) {
+                    return -1;
+                }
+            }
+        }
+    }
+
+    Iter iter;
+    GM* gm;
     while ((gm = iter.next()) != NULL) {
+
+        ++gmIndex;
+        if (moduloRemainder >= 0) {
+            if ((gmIndex % moduloDivisor) != moduloRemainder) {
+                continue;
+            }
+            moduloStr.printf("[%d.%d] ", gmIndex, moduloDivisor);
+        }
+
         const char* shortName = gm->shortName();
         if (skip_name(fMatches, shortName)) {
             SkDELETE(gm);
@@ -779,88 +1468,253 @@
         }
 
         SkISize size = gm->getISize();
-        SkDebugf("drawing... %s [%d %d]\n", shortName,
+        SkDebugf("%sdrawing... %s [%d %d]\n", moduloStr.c_str(), shortName,
                  size.width(), size.height());
-        SkBitmap forwardRenderedBitmap;
 
-        // Above we created an fbo for the context at maxW x maxH size.
-        // Here we lie about the size of the rt. We claim it is the size
-        // desired by the test. The reason is that rasterization may change
-        // slightly when the viewport dimensions change. Previously, whenever
-        // a new test was checked in that bumped maxW or maxH several images
-        // would slightly change.
-        rtDesc.fWidth = size.width();
-        rtDesc.fHeight = size.height();
-        SkAutoTUnref<GrRenderTarget> rt;
-        if (gGrContext) {
-            rt.reset(gGrContext->createPlatformRenderTarget(rtDesc));
-        }
+        ErrorBitfield testErrors = ERROR_NONE;
+        uint32_t gmFlags = gm->getFlags();
 
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        for (int i = 0; i < configs.count(); i++) {
+            ConfigData config = gRec[configs[i]];
+
             // Skip any tests that we don't even need to try.
-            uint32_t gmFlags = gm->getFlags();
-            if ((kPDF_Backend == gRec[i].fBackend) &&
+            if ((kPDF_Backend == config.fBackend) &&
                 (!doPDF || (gmFlags & GM::kSkipPDF_Flag)))
-            {
+                {
+                    continue;
+                }
+            if ((gmFlags & GM::kSkip565_Flag) &&
+                (kRaster_Backend == config.fBackend) &&
+                (SkBitmap::kRGB_565_Config == config.fConfig)) {
                 continue;
             }
 
             // Now we know that we want to run this test and record its
             // success or failure.
-            ErrorBitfield testErrors = ERROR_NONE;
+            ErrorBitfield renderErrors = ERROR_NONE;
+            GrRenderTarget* renderTarget = NULL;
+#if SK_SUPPORT_GPU
+            SkAutoTUnref<GrRenderTarget> rt;
+            AutoResetGr autogr;
+            if ((ERROR_NONE == renderErrors) &&
+                kGPU_Backend == config.fBackend) {
+                GrContext* gr = grFactory->get(config.fGLContextType);
+                bool grSuccess = false;
+                if (gr) {
+                    // create a render target to back the device
+                    GrTextureDesc desc;
+                    desc.fConfig = kSkia8888_PM_GrPixelConfig;
+                    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+                    desc.fWidth = gm->getISize().width();
+                    desc.fHeight = gm->getISize().height();
+                    desc.fSampleCnt = config.fSampleCnt;
+                    GrTexture* tex = gr->createUncachedTexture(desc, NULL, 0);
+                    if (tex) {
+                        rt.reset(tex->asRenderTarget());
+                        rt.get()->ref();
+                        tex->unref();
+                        autogr.set(gr);
+                        renderTarget = rt.get();
+                        grSuccess = NULL != renderTarget;
+                    }
+                }
+                if (!grSuccess) {
+                    renderErrors |= ERROR_NO_GPU_CONTEXT;
+                }
+            }
+#endif
+
+            SkBitmap comparisonBitmap;
+
+            if (ERROR_NONE == renderErrors) {
+                renderErrors |= gmmain.test_drawing(gm, config, writePath,
+                                                    GetGr(),
+                                                    renderTarget,
+                                                    &comparisonBitmap);
+            }
+
+            if (doDeferred && !renderErrors &&
+                (kGPU_Backend == config.fBackend ||
+                 kRaster_Backend == config.fBackend)) {
+                renderErrors |= gmmain.test_deferred_drawing(gm, config,
+                                                             comparisonBitmap,
+                                                             GetGr(),
+                                                             renderTarget);
+            }
+
+            testErrors |= renderErrors;
+        }
+
+        SkBitmap comparisonBitmap;
+        const ConfigData compareConfig =
+            { SkBitmap::kARGB_8888_Config, kRaster_Backend, kDontCare_GLContextType, 0, kRW_ConfigFlag, "comparison" };
+        testErrors |= gmmain.generate_image(gm, compareConfig, NULL, NULL, &comparisonBitmap, false);
+
+        // run the picture centric GM steps
+        if (!(gmFlags & GM::kSkipPicture_Flag)) {
+
+            ErrorBitfield pictErrors = ERROR_NONE;
+
+            //SkAutoTUnref<SkPicture> pict(generate_new_picture(gm));
+            SkPicture* pict = gmmain.generate_new_picture(gm, kNone_BbhType, 0);
+            SkAutoUnref aur(pict);
+
+            if ((ERROR_NONE == testErrors) && doReplay) {
+                SkBitmap bitmap;
+                gmmain.generate_image_from_picture(gm, compareConfig, pict,
+                                                   &bitmap);
+                pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                    gm, compareConfig, "-replay", bitmap, &comparisonBitmap);
+            }
 
             if ((ERROR_NONE == testErrors) &&
-                (kGPU_Backend == gRec[i].fBackend) &&
-                (NULL == rt.get())) {
-                fprintf(stderr, "Could not create render target for gpu.\n");
-                testErrors |= ERROR_NO_GPU_CONTEXT;
+                (ERROR_NONE == pictErrors) &&
+                doSerialize) {
+                SkPicture* repict = gmmain.stream_to_new_picture(*pict);
+                SkAutoUnref aurr(repict);
+
+                SkBitmap bitmap;
+                gmmain.generate_image_from_picture(gm, compareConfig, repict,
+                                                   &bitmap);
+                pictErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                    gm, compareConfig, "-serialize", bitmap, &comparisonBitmap);
             }
 
-            if (ERROR_NONE == testErrors) {
-                testErrors |= test_drawing(gm, gRec[i],
-                                           writePath, readPath, diffPath,
-                                           gGrContext,
-                                           rt.get(), &forwardRenderedBitmap);
+            if (writePicturePath) {
+                const char* pictureSuffix = "skp";
+                SkString path = make_filename(writePicturePath, "",
+                                              gm->shortName(),
+                                              pictureSuffix);
+                SkFILEWStream stream(path.c_str());
+                pict->serialize(&stream);
             }
 
-            if (doDeferred && !testErrors &&
-                (kGPU_Backend == gRec[i].fBackend ||
-                kRaster_Backend == gRec[i].fBackend)) {
-                testErrors |= test_deferred_drawing(gm, gRec[i],
-                                    forwardRenderedBitmap,
-                                    diffPath, gGrContext, rt.get());
-            }
+            testErrors |= pictErrors;
+        }
 
-            if ((ERROR_NONE == testErrors) && doReplay &&
-                !(gmFlags & GM::kSkipPicture_Flag)) {
-                testErrors |= test_picture_playback(gm, gRec[i],
-                                                    forwardRenderedBitmap,
-                                                    readPath, diffPath);
-            }
+        // TODO: add a test in which the RTree rendering results in a
+        // different bitmap than the standard rendering.  It should
+        // show up as failed in the JSON summary, and should be listed
+        // in the stdout also.
+        if (!(gmFlags & GM::kSkipPicture_Flag) && doRTree) {
+            SkPicture* pict = gmmain.generate_new_picture(
+                gm, kRTree_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag);
+            SkAutoUnref aur(pict);
+            SkBitmap bitmap;
+            gmmain.generate_image_from_picture(gm, compareConfig, pict,
+                                               &bitmap);
+            testErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                gm, compareConfig, "-rtree", bitmap, &comparisonBitmap);
+        }
 
-            if ((ERROR_NONE == testErrors) && doSerialize) {
-                testErrors |= test_picture_serialization(gm, gRec[i],
-                                                         forwardRenderedBitmap,
-                                                         readPath, diffPath);
-            }
-
-            // Update overall results.
-            // We only tabulate the particular error types that we currently
-            // care about (e.g., missing reference images). Later on, if we
-            // want to also tabulate pixel mismatches vs dimension mistmatches
-            // (or whatever else), we can do so.
-            testsRun++;
-            if (ERROR_NONE == testErrors) {
-                testsPassed++;
-            } else if (ERROR_READING_REFERENCE_IMAGE & testErrors) {
-                testsMissingReferenceImages++;
-            } else {
-                testsFailed++;
+        if (!(gmFlags & GM::kSkipPicture_Flag) && doTileGrid) {
+            for(int scaleIndex = 0; scaleIndex < tileGridReplayScales.count(); ++scaleIndex) {
+                SkScalar replayScale = tileGridReplayScales[scaleIndex];
+                if ((gmFlags & GM::kSkipScaledReplay_Flag) && replayScale != 1)
+                    continue;
+                // We record with the reciprocal scale to obtain a replay
+                // result that can be validated against comparisonBitmap.
+                SkScalar recordScale = SkScalarInvert(replayScale);
+                SkPicture* pict = gmmain.generate_new_picture(
+                    gm, kTileGrid_BbhType, SkPicture::kUsePathBoundsForClip_RecordingFlag,
+                    recordScale);
+                SkAutoUnref aur(pict);
+                SkBitmap bitmap;
+                gmmain.generate_image_from_picture(gm, compareConfig, pict,
+                                                   &bitmap, replayScale);
+                SkString suffix("-tilegrid");
+                if (SK_Scalar1 != replayScale) {
+                    suffix += "-scale-";
+                    suffix.appendScalar(replayScale);
+                }
+                testErrors |= gmmain.compare_test_results_to_reference_bitmap(
+                    gm, compareConfig, suffix.c_str(), bitmap,
+                    &comparisonBitmap);
             }
         }
+
+        // run the pipe centric GM steps
+        if (!(gmFlags & GM::kSkipPipe_Flag)) {
+
+            ErrorBitfield pipeErrors = ERROR_NONE;
+
+            if ((ERROR_NONE == testErrors) && doPipe) {
+                pipeErrors |= gmmain.test_pipe_playback(gm, compareConfig,
+                                                        comparisonBitmap);
+            }
+
+            if ((ERROR_NONE == testErrors) &&
+                (ERROR_NONE == pipeErrors) &&
+                doTiledPipe && !(gmFlags & GM::kSkipTiled_Flag)) {
+                pipeErrors |= gmmain.test_tiled_pipe_playback(gm, compareConfig,
+                                                              comparisonBitmap);
+            }
+
+            testErrors |= pipeErrors;
+        }
+
+        // Update overall results.
+        // We only tabulate the particular error types that we currently
+        // care about (e.g., missing reference images). Later on, if we
+        // want to also tabulate other error types, we can do so.
+        testsRun++;
+        if (!gmmain.fExpectationsSource.get() ||
+            (ERROR_READING_REFERENCE_IMAGE & testErrors)) {
+            testsMissingReferenceImages++;
+        } else if (ERROR_NONE == testErrors) {
+            testsPassed++;
+        } else {
+            testsFailed++;
+        }
+
         SkDELETE(gm);
     }
-    printf("Ran %d tests: %d passed, %d failed, %d missing reference images\n",
-           testsRun, testsPassed, testsFailed, testsMissingReferenceImages);
+    SkDebugf("Ran %d tests: %d passed, %d failed, %d missing reference images\n",
+             testsRun, testsPassed, testsFailed, testsMissingReferenceImages);
+    gmmain.ListErrors();
+
+    if (NULL != writeJsonSummaryPath) {
+        Json::Value actualResults;
+        actualResults[kJsonKey_ActualResults_Failed] =
+            gmmain.fJsonActualResults_Failed;
+        actualResults[kJsonKey_ActualResults_FailureIgnored] =
+            gmmain.fJsonActualResults_FailureIgnored;
+        actualResults[kJsonKey_ActualResults_NoComparison] =
+            gmmain.fJsonActualResults_NoComparison;
+        actualResults[kJsonKey_ActualResults_Succeeded] =
+            gmmain.fJsonActualResults_Succeeded;
+        Json::Value root;
+        root[kJsonKey_ActualResults] = actualResults;
+        root[kJsonKey_ExpectedResults] = gmmain.fJsonExpectedResults;
+        std::string jsonStdString = root.toStyledString();
+        SkFILEWStream stream(writeJsonSummaryPath);
+        stream.write(jsonStdString.c_str(), jsonStdString.length());
+    }
+
+#if SK_SUPPORT_GPU
+
+#if GR_CACHE_STATS
+    for (int i = 0; i < configs.count(); i++) {
+        ConfigData config = gRec[configs[i]];
+
+        if (kGPU_Backend == config.fBackend) {
+            GrContext* gr = grFactory->get(config.fGLContextType);
+
+            SkDebugf("config: %s %x\n", config.fName, gr);
+            gr->printCacheStats();
+        }
+    }
+#endif
+
+    delete grFactory;
+#endif
+    SkGraphics::Term();
+
     return (0 == testsFailed) ? 0 : -1;
 }
+
+#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/gm/gradients.cpp b/gm/gradients.cpp
index 3eb5633..004d991 100644
--- a/gm/gradients.cpp
+++ b/gm/gradients.cpp
@@ -70,29 +70,42 @@
                                                   data.fColors, data.fPos, data.fCount, tm, mapper);
 }
 
+static SkShader* Make2Conical(const SkPoint pts[2], const GradData& data,
+                             SkShader::TileMode tm, SkUnitMapper* mapper) {
+    SkPoint center0, center1;
+    SkScalar radius0 = SkScalarDiv(pts[1].fX - pts[0].fX, 10);
+    SkScalar radius1 = SkScalarDiv(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,
+                                                   center0, radius0,
+                                                   data.fColors, data.fPos,
+                                                   data.fCount, tm, mapper);
+}
+
 typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
                                SkShader::TileMode tm, SkUnitMapper* mapper);
 static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial
+    MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2Conical
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 class GradientsGM : public GM {
 public:
-	GradientsGM() {
+    GradientsGM() {
         this->setBGColor(0xFFDDDDDD);
     }
-    
+
 protected:
     SkString onShortName() {
         return SkString("gradients");
     }
-    
-    virtual SkISize onISize() { return make_isize(640, 510); }
-        
+
+    virtual SkISize onISize() { return make_isize(640, 615); }
+
     virtual void onDraw(SkCanvas* canvas) {
-        
+
         SkPoint pts[2] = {
             { 0, 0 },
             { SkIntToScalar(100), SkIntToScalar(100) }
@@ -101,7 +114,7 @@
         SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
         SkPaint paint;
         paint.setAntiAlias(true);
-        
+
         canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
         for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
             canvas->save();
@@ -116,11 +129,89 @@
             canvas->translate(SkIntToScalar(120), 0);
         }
     }
-    
+
 private:
     typedef GM INHERITED;
 };
 
+// Based on the original gradient slide, but with perspective applied to the
+// gradient shaders' local matrices
+class GradientsLocalPerspectiveGM : public GM {
+public:
+    GradientsLocalPerspectiveGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    SkString onShortName() {
+        return SkString("gradients_local_perspective");
+    }
+
+    virtual SkISize onISize() { return make_isize(640, 615); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPoint pts[2] = {
+            { 0, 0 },
+            { SkIntToScalar(100), SkIntToScalar(100) }
+        };
+        SkShader::TileMode tm = SkShader::kClamp_TileMode;
+        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
+            canvas->save();
+            for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
+                SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL);
+
+                // 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)));
+                shader->setLocalMatrix(perspective);
+
+                paint.setShader(shader);
+                canvas->drawRect(r, paint);
+                shader->unref();
+                canvas->translate(0, SkIntToScalar(120));
+            }
+            canvas->restore();
+            canvas->translate(SkIntToScalar(120), 0);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+// Based on the original gradient slide, but with perspective applied to
+// the view matrix
+class GradientsViewPerspectiveGM : public GradientsGM {
+protected:
+    SkString onShortName() {
+        return SkString("gradients_view_perspective");
+    }
+
+    virtual SkISize onISize() { return make_isize(640, 400); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkMatrix perspective;
+        perspective.setIdentity();
+        perspective.setPerspY(SkScalarDiv(SK_Scalar1, SkIntToScalar(1000)));
+        perspective.setSkewX(SkScalarDiv(SkIntToScalar(8), SkIntToScalar(25)));
+        canvas->concat(perspective);
+        INHERITED::onDraw(canvas);
+    }
+
+private:
+    typedef GradientsGM INHERITED;
+};
+
 /*
  Inspired by this <canvas> javascript, where we need to detect that we are not
  solving a quadratic equation, but must instead solve a linear (since our X^2
@@ -128,7 +219,7 @@
 
  ctx.fillStyle = '#f00';
  ctx.fillRect(0, 0, 100, 50);
- 
+
  var g = ctx.createRadialGradient(-80, 25, 70, 0, 25, 150);
  g.addColorStop(0, '#f00');
  g.addColorStop(0.01, '#0f0');
@@ -140,21 +231,21 @@
 class GradientsDegenrate2PointGM : public GM {
 public:
     GradientsDegenrate2PointGM() {}
-    
+
 protected:
     SkString onShortName() {
         return SkString("gradients_degenerate_2pt");
     }
-    
-	virtual SkISize onISize() { return make_isize(320, 320); }
-    
+
+    virtual SkISize onISize() { return make_isize(320, 320); }
+
     void drawBG(SkCanvas* canvas) {
         canvas->drawColor(SK_ColorBLUE);
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         this->drawBG(canvas);
-        
+
         SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorGREEN, SK_ColorRED };
         SkScalar pos[] = { 0, SkFloatToScalar(0.01f), SkFloatToScalar(0.99f), SK_Scalar1 };
         SkPoint c0;
@@ -170,7 +261,7 @@
         paint.setShader(s)->unref();
         canvas->drawPaint(paint);
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -230,16 +321,16 @@
         const SkISize dim = this->getISize();
 
         this->drawBG(canvas);
- 
+
         SkPaint paint;
         paint.setDither(true);
         SkPoint center;
         center.set(SkIntToScalar(dim.width())/2, SkIntToScalar(dim.height())/2);
         SkScalar radius = SkIntToScalar(dim.width())/2;
         const SkColor colors[] = { 0x7f7f7f7f, 0x7f7f7f7f, 0xb2000000 };
-        const SkScalar pos[] = { SkFloatToScalar(0.0),
-                             SkFloatToScalar(0.35),
-                             SkFloatToScalar(1.0) };
+        const SkScalar pos[] = { SkFloatToScalar(0.0f),
+                             SkFloatToScalar(0.35f),
+                             SkFloatToScalar(1.0f) };
         SkShader* shader =
             SkGradientShader::CreateRadial(center, radius, colors,
                                            pos, SK_ARRAY_COUNT(pos),
@@ -269,5 +360,10 @@
 
 static GM* MyFactory4(void*) { return new RadialGradientGM; }
 static GMRegistry reg4(MyFactory4);
-}
 
+static GM* MyFactory5(void*) { return new GradientsLocalPerspectiveGM; }
+static GMRegistry reg5(MyFactory5);
+
+static GM* MyFactory6(void*) { return new GradientsViewPerspectiveGM; }
+static GMRegistry reg6(MyFactory6);
+}
diff --git a/gm/gradtext.cpp b/gm/gradtext.cpp
index a3769d2..d782338 100644
--- a/gm/gradtext.cpp
+++ b/gm/gradtext.cpp
@@ -8,6 +8,7 @@
 #include "gm.h"
 #include "SkCanvas.h"
 #include "SkGradientShader.h"
+#include "SkTypeface.h"
 
 // test shader w/ transparency
 static SkShader* make_grad(SkScalar width) {
@@ -27,8 +28,73 @@
                                           SkShader::kMirror_TileMode);
 }
 
+static SkShader* make_chrome_solid() {
+    SkColor colors[] = { SK_ColorGREEN, SK_ColorGREEN };
+    SkPoint pts[] = { { 0, 0 }, { 1, 0 } };
+    return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                          SkShader::kClamp_TileMode);
+}
+
 namespace skiagm {
-    
+
+// Replicate chrome layout test - clipped pathed gradient-shaded text
+class ChromeGradTextGM1 : public GM {
+public:
+    ChromeGradTextGM1() { }
+protected:
+
+    virtual SkString onShortName() { return SkString("chrome_gradtext1"); }
+    virtual SkISize onISize() { return make_isize(500, 480); }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+        SkRect r = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
+
+        canvas->clipRect(r);
+
+        paint.setColor(SK_ColorRED);
+        canvas->drawRect(r, paint);
+
+        // Minimal repro doesn't require AA, LCD, or a nondefault typeface
+        paint.setShader(make_chrome_solid())->unref();
+        paint.setTextSize(SkIntToScalar(500));
+
+        canvas->drawText("I", 1, 0, 100, paint);
+    }
+private:
+    typedef GM INHERITED;
+};
+
+
+// Replicate chrome layout test - switching between solid & gadient text
+class ChromeGradTextGM2 : public GM {
+public:
+    ChromeGradTextGM2() { }
+protected:
+
+    virtual SkString onShortName() { return SkString("chrome_gradtext2"); }
+    virtual SkISize onISize() { return make_isize(500, 480); }
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint paint;
+
+        paint.setStyle(SkPaint::kFill_Style);
+        canvas->drawText("Normal Fill Text", 16, 0, 50, paint);
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas->drawText("Normal Stroke Text", 18, 0, 100, paint);
+
+        // Minimal repro doesn't require AA, LCD, or a nondefault typeface
+        paint.setShader(make_chrome_solid())->unref();
+
+        paint.setStyle(SkPaint::kFill_Style);
+        canvas->drawText("Gradient Fill Text", 18, 0, 150, paint);
+        paint.setStyle(SkPaint::kStroke_Style);
+        canvas->drawText("Gradient Stroke Text", 20, 0, 200, paint);
+    }
+private:
+    typedef GM INHERITED;
+};
+
+
+
 class GradTextGM : public GM {
 public:
     GradTextGM () {}
@@ -79,7 +145,7 @@
 
             paint.setShader(make_grad2(SkIntToScalar(80)))->unref();
             draw_text3(canvas, paint);
-            
+
             canvas->translate(0, paint.getTextSize() * 2);
         }
     }
@@ -91,7 +157,10 @@
 //////////////////////////////////////////////////////////////////////////////
 
 static GM* MyFactory(void*) { return new GradTextGM; }
+static GM* CMyFactory(void*) { return new ChromeGradTextGM1; }
+static GM* CMyFactory2(void*) { return new ChromeGradTextGM2; }
+
 static GMRegistry reg(MyFactory);
-
+static GMRegistry Creg(CMyFactory);
+static GMRegistry Creg2(CMyFactory2);
 }
-
diff --git a/gm/hairmodes.cpp b/gm/hairmodes.cpp
index 8bfa186..6fd72fc 100644
--- a/gm/hairmodes.cpp
+++ b/gm/hairmodes.cpp
@@ -10,18 +10,6 @@
 #include "SkColorPriv.h"
 #include "SkShader.h"
 
-static SkCanvas* create_canvas(int w, int h) {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
-    bm.allocPixels();
-    bm.eraseColor(0);
-    return new SkCanvas(bm);
-}
-
-static const SkBitmap& extract_bitmap(SkCanvas* canvas) {
-    return canvas->getDevice()->accessBitmap(false);
-}
-
 static const struct {
     SkXfermode::Mode  fMode;
     const char*         fLabel;
@@ -66,7 +54,7 @@
         paint.setStrokeWidth(SK_Scalar1 * angle * 2 / 24);
         canvas->drawLine(W/2, H/2, W/2 + x, H/2 + y, paint);
     }
-    
+
     return H;
 }
 
@@ -80,7 +68,7 @@
     SkShader* s = SkShader::CreateBitmapShader(bm,
                                                SkShader::kRepeat_TileMode,
                                                SkShader::kRepeat_TileMode);
-    
+
     SkMatrix m;
     m.setScale(SkIntToScalar(6), SkIntToScalar(6));
     s->setLocalMatrix(m);
@@ -88,28 +76,28 @@
 }
 
 namespace skiagm {
-    
+
     class HairModesGM : public GM {
         SkPaint fBGPaint;
-    public:
-        HairModesGM() {
-            fBGPaint.setShader(make_bg_shader())->unref();
-        }
-        
+
     protected:
-        
-        virtual SkString onShortName() {
+
+        virtual SkString onShortName() SK_OVERRIDE {
             return SkString("hairmodes");
         }
-        
+
         virtual SkISize onISize() { return make_isize(640, 480); }
-        
-        virtual void onDraw(SkCanvas* canvas) {
+
+        virtual void onOnceBeforeDraw() SK_OVERRIDE {
+            fBGPaint.setShader(make_bg_shader())->unref();
+        }
+
+        virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
             const SkRect bounds = SkRect::MakeWH(W, H);
             static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 };
-            
+
             canvas->translate(SkIntToScalar(4), SkIntToScalar(4));
-            
+
             for (int alpha = 0; alpha < 4; ++alpha) {
                 canvas->save();
                 canvas->save();
@@ -120,14 +108,14 @@
                         canvas->save();
                     }
                     SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
-                    
+
                     canvas->drawRect(bounds, fBGPaint);
                     canvas->saveLayer(&bounds, NULL);
                     SkScalar dy = drawCell(canvas, mode,
                                            gAlphaValue[alpha & 1],
                                            gAlphaValue[alpha & 2]);
                     canvas->restore();
-                    
+
                     canvas->translate(0, dy * 5 / 4);
                     SkSafeUnref(mode);
                 }
@@ -143,10 +131,10 @@
     private:
         typedef GM INHERITED;
     };
-    
+
     //////////////////////////////////////////////////////////////////////////////
-    
+
     static GM* MyFactory(void*) { return new HairModesGM; }
     static GMRegistry reg(MyFactory);
-    
+
 }
diff --git a/gm/hittestpath.cpp b/gm/hittestpath.cpp
new file mode 100644
index 0000000..0327571
--- /dev/null
+++ b/gm/hittestpath.cpp
@@ -0,0 +1,75 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkCullPoints.h"
+#include "SkRandom.h"
+
+static void test_hittest(SkCanvas* canvas, const SkPath& path) {
+    SkPaint paint;
+    SkRect r = path.getBounds();
+
+    paint.setColor(SK_ColorRED);
+    canvas->drawPath(path, paint);
+
+    const SkScalar MARGIN = SkIntToScalar(4);
+
+    paint.setColor(0x800000FF);
+    for (SkScalar y = r.fTop + SK_ScalarHalf - MARGIN; y < r.fBottom + MARGIN; y += SK_Scalar1) {
+        for (SkScalar x = r.fLeft + SK_ScalarHalf - MARGIN; x < r.fRight + MARGIN; x += SK_Scalar1) {
+            if (path.contains(x, y)) {
+                canvas->drawPoint(x, y, paint);
+            }
+        }
+    }
+}
+
+class HitTestPathGM : public skiagm::GM {
+public:
+    HitTestPathGM () {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("hittestpath");
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(700, 460); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPath path;
+        SkRandom rand;
+
+        int scale = 300;
+        for (int i = 0; i < 4; ++i) {
+            path.lineTo(rand.nextUScalar1() * scale, rand.nextUScalar1() * scale);
+            path.quadTo(rand.nextUScalar1() * scale, rand.nextUScalar1() * scale,
+                        rand.nextUScalar1() * scale, rand.nextUScalar1() * scale);
+            path.cubicTo(rand.nextUScalar1() * scale, rand.nextUScalar1() * scale,
+                         rand.nextUScalar1() * scale, rand.nextUScalar1() * scale,
+                         rand.nextUScalar1() * scale, rand.nextUScalar1() * scale);
+        }
+
+        path.setFillType(SkPath::kEvenOdd_FillType);
+        path.offset(SkIntToScalar(20), SkIntToScalar(20));
+
+        test_hittest(canvas, path);
+
+        canvas->translate(SkIntToScalar(scale), 0);
+        path.setFillType(SkPath::kWinding_FillType);
+
+        test_hittest(canvas, path);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new HitTestPathGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/image.cpp b/gm/image.cpp
new file mode 100644
index 0000000..974f5f9
--- /dev/null
+++ b/gm/image.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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 "SkSurface.h"
+#include "SkCanvas.h"
+#include "SkStream.h"
+#include "SkData.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+
+namespace skiagm {
+extern GrContext* GetGr();
+};
+#endif
+
+static SkData* fileToData(const char path[]) {
+    SkFILEStream stream(path);
+    if (!stream.isValid()) {
+        return SkData::NewEmpty();
+    }
+    size_t size = stream.getLength();
+    void* mem = sk_malloc_throw(size);
+    stream.read(mem, size);
+    return SkData::NewFromMalloc(mem, size);
+}
+
+static void drawJpeg(SkCanvas* canvas, const SkISize& size) {
+    SkAutoDataUnref data(fileToData("/Users/mike/Downloads/skia.google.jpeg"));
+    SkImage* image = SkImage::NewEncodedData(data);
+    if (image) {
+        SkAutoCanvasRestore acr(canvas, true);
+        canvas->scale(size.width() * 1.0f / image->width(),
+                      size.height() * 1.0f / image->height());
+        image->draw(canvas,0, 0, NULL);
+        image->unref();
+    }
+}
+
+static void drawContents(SkSurface* surface, SkColor fillC) {
+    SkSize size = SkSize::Make(SkIntToScalar(surface->width()),
+                               SkIntToScalar(surface->height()));
+    SkCanvas* canvas = surface->getCanvas();
+
+    SkScalar stroke = size.fWidth / 10;
+    SkScalar radius = (size.fWidth - stroke) / 2;
+
+    SkPaint paint;
+
+    paint.setAntiAlias(true);
+    paint.setColor(fillC);
+    canvas->drawCircle(size.fWidth/2, size.fHeight/2, radius, paint);
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(stroke);
+    paint.setColor(SK_ColorBLACK);
+    canvas->drawCircle(size.fWidth/2, size.fHeight/2, radius, paint);
+}
+
+static void test_surface(SkCanvas* canvas, SkSurface* surf) {
+    drawContents(surf, SK_ColorRED);
+    SkImage* imgR = surf->newImageShapshot();
+
+    if (true) {
+        SkImage* imgR2 = surf->newImageShapshot();
+        SkASSERT(imgR == imgR2);
+        imgR2->unref();
+    }
+
+    drawContents(surf, SK_ColorGREEN);
+    SkImage* imgG = surf->newImageShapshot();
+
+    // since we've drawn after we snapped imgR, imgG will be a different obj
+    SkASSERT(imgR != imgG);
+
+    drawContents(surf, SK_ColorBLUE);
+
+    SkPaint paint;
+//    paint.setFilterBitmap(true);
+//    paint.setAlpha(0x80);
+
+    imgR->draw(canvas, 0, 0, &paint);
+    imgG->draw(canvas, 0, 80, &paint);
+    surf->draw(canvas, 0, 160, &paint);
+
+    imgG->unref();
+    imgR->unref();
+}
+
+class ImageGM : public skiagm::GM {
+    void*   fBuffer;
+    size_t  fBufferSize;
+    SkSize  fSize;
+    enum {
+        W = 64,
+        H = 64,
+        RB = W * 4 + 8,
+    };
+public:
+    ImageGM() {
+        fBufferSize = RB * H;
+        fBuffer = sk_malloc_throw(fBufferSize);
+        fSize.set(SkIntToScalar(W), SkIntToScalar(H));
+    }
+
+    virtual ~ImageGM() {
+        sk_free(fBuffer);
+    }
+
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("image-surface");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(800, 500);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        drawJpeg(canvas, this->getISize());
+
+        canvas->scale(2, 2);
+
+        static const char* kLabel1 = "Original Img";
+        static const char* kLabel2 = "Modified Img";
+        static const char* kLabel3 = "Cur Surface";
+
+        static const char* kLabel4 = "Pre-Alloc Img";
+        static const char* kLabel5 = "New Alloc Img";
+        static const char* kLabel6 = "SkPicture";
+        static const char* kLabel7 = "GPU";
+
+        SkPaint textPaint;
+
+        canvas->drawText(kLabel1, strlen(kLabel1), 10,  60, textPaint);
+        canvas->drawText(kLabel2, strlen(kLabel2), 10, 140, textPaint);
+        canvas->drawText(kLabel3, strlen(kLabel3), 10, 220, textPaint);
+
+        canvas->drawText(kLabel4, strlen(kLabel4),  80, 10, textPaint);
+        canvas->drawText(kLabel5, strlen(kLabel5), 160, 10, textPaint);
+        canvas->drawText(kLabel6, strlen(kLabel6), 250, 10, textPaint);
+        canvas->drawText(kLabel7, strlen(kLabel7), 340, 10, textPaint);
+
+        canvas->translate(80, 20);
+
+        // since we draw into this directly, we need to start fresh
+        sk_bzero(fBuffer, fBufferSize);
+
+        SkImage::Info info;
+
+        info.fWidth = W;
+        info.fHeight = H;
+        info.fColorType = SkImage::kPMColor_ColorType;
+        info.fAlphaType = SkImage::kPremul_AlphaType;
+        SkAutoTUnref<SkSurface> surf0(SkSurface::NewRasterDirect(info, fBuffer, RB));
+        SkAutoTUnref<SkSurface> surf1(SkSurface::NewRaster(info));
+        SkAutoTUnref<SkSurface> surf2(SkSurface::NewPicture(info.fWidth, info.fHeight));
+#if SK_SUPPORT_GPU
+        GrContext* ctx = skiagm::GetGr();
+
+        SkAutoTUnref<SkSurface> surf3(SkSurface::NewRenderTarget(ctx, info, 0));
+#endif
+
+        test_surface(canvas, surf0);
+        canvas->translate(80, 0);
+        test_surface(canvas, surf1);
+        canvas->translate(80, 0);
+        test_surface(canvas, surf2);
+#if SK_SUPPORT_GPU
+        if (NULL != ctx) {
+            canvas->translate(80, 0);
+            test_surface(canvas, surf3);
+        }
+#endif
+    }
+
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        return GM::kSkipPicture_Flag | GM::kSkipPipe_Flag;
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new ImageGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/image.h b/gm/image.h
new file mode 100644
index 0000000..25711be
--- /dev/null
+++ b/gm/image.h
@@ -0,0 +1,14 @@
+//
+//  image.h
+//  SampleApp
+//
+//  Created by Mike Reed on 7/27/12.
+//
+//
+
+#ifndef __SampleApp__image__
+#define __SampleApp__image__
+
+#include <iostream>
+
+#endif /* defined(__SampleApp__image__) */
diff --git a/gm/imageblur.cpp b/gm/imageblur.cpp
index 841441e..406f9ef 100644
--- a/gm/imageblur.cpp
+++ b/gm/imageblur.cpp
@@ -18,7 +18,7 @@
     ImageBlurGM() {
         this->setBGColor(0xFF000000);
     }
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("imageblur");
@@ -32,19 +32,21 @@
         SkPaint paint;
         paint.setImageFilter(new SkBlurImageFilter(24.0f, 0.0f))->unref();
         canvas->saveLayer(NULL, &paint);
-        paint.setAntiAlias(true);
         const char* str = "The quick brown fox jumped over the lazy dog.";
         srand(1234);
+        SkPaint textPaint;
+        textPaint.setAntiAlias(true);
         for (int i = 0; i < 25; ++i) {
             int x = rand() % WIDTH;
             int y = rand() % HEIGHT;
-            paint.setColor(rand() % 0x1000000 | 0xFF000000);
-            paint.setTextSize(rand() % 300);
-            canvas->drawText(str, strlen(str), x, y, paint);
+            textPaint.setColor(rand() % 0x1000000 | 0xFF000000);
+            textPaint.setTextSize(SkIntToScalar(rand() % 300));
+            canvas->drawText(str, strlen(str), SkIntToScalar(x),
+                             SkIntToScalar(y), textPaint);
         }
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/imagefiltersbase.cpp b/gm/imagefiltersbase.cpp
new file mode 100644
index 0000000..6879e79
--- /dev/null
+++ b/gm/imagefiltersbase.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkShader.h"
+
+#include "SkBlurImageFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkTestImageFilters.h"
+
+class FailImageFilter : public SkImageFilter {
+public:
+    FailImageFilter() : INHERITED(0) {}
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(FailImageFilter)
+protected:
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) {
+        return false;
+    }
+
+    FailImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    typedef SkImageFilter INHERITED;
+};
+
+// register the filter with the flattenable registry
+static SkFlattenable::Registrar gFailImageFilterReg("FailImageFilter",
+                                                    FailImageFilter::CreateProc);
+
+class IdentityImageFilter : public SkImageFilter {
+public:
+    IdentityImageFilter() : INHERITED(0) {}
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(IdentityImageFilter)
+protected:
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) {
+        *result = src;
+        return true;
+    }
+
+    IdentityImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
+    typedef SkImageFilter INHERITED;
+};
+
+// register the filter with the flattenable registry
+static SkFlattenable::Registrar gIdentityImageFilterReg("IdentityImageFilter",
+                                                        IdentityImageFilter::CreateProc);
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void draw_paint(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+    paint.setColor(SK_ColorGREEN);
+    canvas->save();
+    canvas->clipRect(r);
+    canvas->drawPaint(paint);
+    canvas->restore();
+}
+
+static void draw_line(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    paint.setImageFilter(imf);
+    paint.setStrokeWidth(r.width()/10);
+    canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+}
+
+static void draw_rect(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setColor(SK_ColorYELLOW);
+    paint.setImageFilter(imf);
+    SkRect rr(r);
+    rr.inset(r.width()/10, r.height()/10);
+    canvas->drawRect(rr, paint);
+}
+
+static void draw_path(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setColor(SK_ColorMAGENTA);
+    paint.setImageFilter(imf);
+    paint.setAntiAlias(true);
+    canvas->drawCircle(r.centerX(), r.centerY(), r.width()*2/5, paint);
+}
+
+static void draw_text(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+    paint.setColor(SK_ColorCYAN);
+    paint.setAntiAlias(true);
+    paint.setTextSize(r.height()/2);
+    paint.setTextAlign(SkPaint::kCenter_Align);
+    canvas->drawText("Text", 4, r.centerX(), r.centerY(), paint);
+}
+
+static void draw_bitmap(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+
+    SkIRect bounds;
+    r.roundOut(&bounds);
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorTRANSPARENT);
+    SkCanvas c(bm);
+    draw_path(&c, r, NULL);
+
+    canvas->drawBitmap(bm, 0, 0, &paint);
+}
+
+static void draw_sprite(SkCanvas* canvas, const SkRect& r, SkImageFilter* imf) {
+    SkPaint paint;
+    paint.setImageFilter(imf);
+
+    SkIRect bounds;
+    r.roundOut(&bounds);
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, bounds.width(), bounds.height());
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorTRANSPARENT);
+    SkCanvas c(bm);
+    draw_path(&c, r, NULL);
+
+    SkPoint loc = { r.fLeft, r.fTop };
+    canvas->getTotalMatrix().mapPoints(&loc, 1);
+    canvas->drawSprite(bm,
+                       SkScalarRoundToInt(loc.fX), SkScalarRoundToInt(loc.fY),
+                       &paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ImageFiltersBaseGM : public skiagm::GM {
+public:
+    ImageFiltersBaseGM () {}
+
+protected:
+
+    virtual SkString onShortName() {
+        return SkString("imagefiltersbase");
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(700, 460); }
+
+    void draw_frame(SkCanvas* canvas, const SkRect& r) {
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorRED);
+        canvas->drawRect(r, paint);
+    }
+
+    virtual uint32_t onGetFlags() const {
+        // Because of the use of drawSprite, this test is excluded
+        // from scaled replay tests because drawSprite ignores the
+        // reciprocal scale that is applied at record time, which is
+        // the intended behavior of drawSprite.
+        return kSkipScaledReplay_Flag;
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        void (*drawProc[])(SkCanvas*, const SkRect&, SkImageFilter*) = {
+            draw_paint,
+            draw_line, draw_rect, draw_path, draw_text,
+            draw_bitmap,
+            draw_sprite
+        };
+
+        SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorRED,
+                                                     SkXfermode::kSrcIn_Mode);
+        SkImageFilter* filters[] = {
+            NULL,
+            new IdentityImageFilter,
+            new FailImageFilter,
+            SkColorFilterImageFilter::Create(cf),
+            new SkBlurImageFilter(12.0f, 0.0f),
+        };
+        cf->unref();
+
+        SkRect r = SkRect::MakeWH(SkIntToScalar(64), SkIntToScalar(64));
+        SkScalar MARGIN = SkIntToScalar(16);
+        SkScalar DX = r.width() + MARGIN;
+        SkScalar DY = r.height() + MARGIN;
+
+        canvas->translate(MARGIN, MARGIN);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(drawProc); ++i) {
+            canvas->save();
+            for (size_t j = 0; j < SK_ARRAY_COUNT(filters); ++j) {
+                drawProc[i](canvas, r, filters[j]);
+
+                draw_frame(canvas, r);
+                canvas->translate(0, DY);
+            }
+            canvas->restore();
+            canvas->translate(DX, 0);
+        }
+
+        for(size_t j = 0; j < SK_ARRAY_COUNT(filters); ++j) {
+            SkSafeUnref(filters[j]);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new ImageFiltersBaseGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/imagefiltersgraph.cpp b/gm/imagefiltersgraph.cpp
new file mode 100644
index 0000000..739a18f
--- /dev/null
+++ b/gm/imagefiltersgraph.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+
+#include "SkBitmapSource.h"
+#include "SkBlendImageFilter.h"
+#include "SkBlurImageFilter.h"
+#include "SkColorFilter.h"
+#include "SkColorMatrixFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkMergeImageFilter.h"
+#include "SkMorphologyImageFilter.h"
+
+#include "SkTestImageFilters.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ImageFiltersGraphGM : public skiagm::GM {
+public:
+    ImageFiltersGraphGM() : fInitialized(false) {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("imagefiltersgraph");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFFFFFFFF);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
+    }
+
+    virtual SkISize onISize() { return SkISize::Make(200, 100); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            this->make_bitmap();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        {
+            SkAutoTUnref<SkImageFilter> bitmapSource(new SkBitmapSource(fBitmap));
+            SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(SK_ColorRED,
+                                                         SkXfermode::kSrcIn_Mode));
+            SkAutoTUnref<SkImageFilter> blur(new SkBlurImageFilter(4.0f, 4.0f, bitmapSource));
+            SkAutoTUnref<SkImageFilter> erode(new SkErodeImageFilter(4, 4, blur));
+            SkAutoTUnref<SkImageFilter> color(SkColorFilterImageFilter::Create(cf, erode));
+            SkAutoTUnref<SkImageFilter> merge(new SkMergeImageFilter(blur, color));
+
+            SkPaint paint;
+            paint.setImageFilter(merge);
+            canvas->drawPaint(paint);
+        }
+        {
+            SkAutoTUnref<SkImageFilter> morph(new SkDilateImageFilter(5, 5));
+
+            SkScalar matrix[20] = { SK_Scalar1, 0, 0, 0, 0,
+                                    0, SK_Scalar1, 0, 0, 0,
+                                    0, 0, SK_Scalar1, 0, 0,
+                                    0, 0, 0, SkFloatToScalar(0.5f), 0 };
+
+            SkAutoTUnref<SkColorFilter> matrixFilter(new SkColorMatrixFilter(matrix));
+            SkAutoTUnref<SkImageFilter> colorMorph(SkColorFilterImageFilter::Create(matrixFilter, morph));
+            SkAutoTUnref<SkImageFilter> blendColor(new SkBlendImageFilter(SkBlendImageFilter::kNormal_Mode, colorMorph));
+
+            SkPaint paint;
+            paint.setImageFilter(blendColor);
+            canvas->drawBitmap(fBitmap, 100, 0, &paint);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap;
+    bool fInitialized;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new ImageFiltersGraphGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/imagemagnifier.cpp b/gm/imagemagnifier.cpp
new file mode 100644
index 0000000..0966121
--- /dev/null
+++ b/gm/imagemagnifier.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "SkMagnifierImageFilter.h"
+
+#define WIDTH 500
+#define HEIGHT 500
+
+namespace skiagm {
+
+class ImageMagnifierGM : public GM {
+public:
+    ImageMagnifierGM() {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        // Skip tiled drawing until https://code.google.com/p/skia/issues/detail?id=781 is fixed.
+        return this->INHERITED::onGetFlags() | GM::kSkipTiled_Flag;
+    }
+
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("imagemagnifier");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkPaint paint;
+        paint.setImageFilter(
+            new SkMagnifierImageFilter(
+                SkRect::MakeXYWH(SkIntToScalar(125), SkIntToScalar(125),
+                                 SkIntToScalar(WIDTH / 2),
+                                 SkIntToScalar(HEIGHT / 2)),
+                100))->unref();
+        canvas->saveLayer(NULL, &paint);
+        paint.setAntiAlias(true);
+        const char* str = "The quick brown fox jumped over the lazy dog.";
+        srand(1234);
+        for (int i = 0; i < 25; ++i) {
+            int x = rand() % WIDTH;
+            int y = rand() % HEIGHT;
+            paint.setColor(rand() % 0x1000000 | 0xFF000000);
+            paint.setTextSize(SkIntToScalar(rand() % 300));
+            canvas->drawText(str, strlen(str), SkIntToScalar(x),
+                             SkIntToScalar(y), paint);
+        }
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ImageMagnifierGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/lcdtext.cpp b/gm/lcdtext.cpp
index ab8b8e4..706d4fd 100644
--- a/gm/lcdtext.cpp
+++ b/gm/lcdtext.cpp
@@ -31,7 +31,7 @@
     SkISize onISize() { return make_isize(640, 480); }
 
     virtual void onDraw(SkCanvas* canvas) {
-        
+
         y = textHeight;
         drawText(canvas, SkString("TEXT: SubpixelTrue LCDRenderTrue"),
                  true,  true);
diff --git a/gm/lighting.cpp b/gm/lighting.cpp
new file mode 100644
index 0000000..de5e330
--- /dev/null
+++ b/gm/lighting.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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 "SkLightingImageFilter.h"
+
+#define WIDTH 330
+#define HEIGHT 220
+
+namespace skiagm {
+
+class ImageLightingGM : public GM {
+public:
+    ImageLightingGM() : fInitialized(false) {
+        this->setBGColor(0xFF000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("lighting");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFFFFFFFF);
+        paint.setTextSize(SkIntToScalar(96));
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(20), SkIntToScalar(70), paint);
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    void drawClippedBitmap(SkCanvas* canvas, const SkPaint& paint, int x, int y) {
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            fInitialized = true;
+        }
+        canvas->clear(0xFF101010);
+        SkPaint checkPaint;
+        checkPaint.setColor(0xFF202020);
+        for (int y = 0; y < HEIGHT; y += 16) {
+          for (int x = 0; x < WIDTH; x += 16) {
+            canvas->save();
+            canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+            canvas->drawRect(SkRect::MakeXYWH(8, 0, 8, 8), checkPaint);
+            canvas->drawRect(SkRect::MakeXYWH(0, 8, 8, 8), checkPaint);
+            canvas->restore();
+          }
+        }
+        SkPoint3 pointLocation(0, 0, SkIntToScalar(10));
+        SkScalar azimuthRad = SkDegreesToRadians(SkIntToScalar(225));
+        SkScalar elevationRad = SkDegreesToRadians(SkIntToScalar(5));
+        SkPoint3 distantDirection(SkScalarMul(SkScalarCos(azimuthRad), SkScalarCos(elevationRad)),
+                                  SkScalarMul(SkScalarSin(azimuthRad), SkScalarCos(elevationRad)),
+                                  SkScalarSin(elevationRad));
+        SkPoint3 spotLocation(SkIntToScalar(-10), SkIntToScalar(-10), SkIntToScalar(20));
+        SkPoint3 spotTarget(SkIntToScalar(40), SkIntToScalar(40), 0);
+        SkScalar spotExponent = SK_Scalar1;
+        SkScalar cutoffAngle = SkIntToScalar(15);
+        SkScalar kd = SkIntToScalar(2);
+        SkScalar ks = SkIntToScalar(1);
+        SkScalar shininess = SkIntToScalar(8);
+        SkScalar surfaceScale = SkIntToScalar(1);
+        SkColor white(0xFFFFFFFF);
+        SkPaint paint;
+        paint.setImageFilter(SkLightingImageFilter::CreatePointLitDiffuse(pointLocation, white, surfaceScale, kd))->unref();
+        drawClippedBitmap(canvas, paint, 0, 0);
+        paint.setImageFilter(SkLightingImageFilter::CreateDistantLitDiffuse(distantDirection, white, surfaceScale, kd))->unref();
+        drawClippedBitmap(canvas, paint, 110, 0);
+        paint.setImageFilter(SkLightingImageFilter::CreateSpotLitDiffuse(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, kd))->unref();
+        drawClippedBitmap(canvas, paint, 220, 0);
+        paint.setImageFilter(SkLightingImageFilter::CreatePointLitSpecular(pointLocation, white, surfaceScale, ks, shininess))->unref();
+        drawClippedBitmap(canvas, paint, 0, 110);
+        paint.setImageFilter(SkLightingImageFilter::CreateDistantLitSpecular(distantDirection, white, surfaceScale, ks, shininess))->unref();
+        drawClippedBitmap(canvas, paint, 110, 110);
+        paint.setImageFilter(SkLightingImageFilter::CreateSpotLitSpecular(spotLocation, spotTarget, spotExponent, cutoffAngle, white, surfaceScale, ks, shininess))->unref();
+        drawClippedBitmap(canvas, paint, 220, 110);
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ImageLightingGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/linepaths.cpp b/gm/linepaths.cpp
index 3d84c37..fbf3faf 100644
--- a/gm/linepaths.cpp
+++ b/gm/linepaths.cpp
@@ -19,9 +19,9 @@
     SkString onShortName() {
         return SkString("linepath");
     }
-        
+
     SkISize onISize() { return make_isize(1240, 390); }
-    
+
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
                   SkPaint::Style style, SkPath::FillType fill,
@@ -38,7 +38,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         struct FillAndName {
             SkPath::FillType fFill;
@@ -109,19 +109,19 @@
                     if (0 < style) {
                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                     }
-        
+
                     SkColor color = 0xff007000;
                     this->drawPath(path.fPath, canvas, color, rect,
                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                     gFills[fill].fFill, SK_Scalar1*10);
-        
+
                     SkPaint rectPaint;
                     rectPaint.setColor(SK_ColorBLACK);
                     rectPaint.setStyle(SkPaint::kStroke_Style);
                     rectPaint.setStrokeWidth(-1);
                     rectPaint.setAntiAlias(true);
                     canvas->drawRect(rect, rectPaint);
-        
+
                     SkPaint labelPaint;
                     labelPaint.setColor(color);
                     labelPaint.setAntiAlias(true);
@@ -147,7 +147,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -160,9 +160,9 @@
     SkString onShortName() {
         return SkString("lineclosepath");
     }
-        
+
     SkISize onISize() { return make_isize(1240, 390); }
-    
+
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
                   SkPaint::Style style, SkPath::FillType fill,
@@ -179,7 +179,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         struct FillAndName {
             SkPath::FillType fFill;
@@ -251,19 +251,19 @@
                     if (0 < style) {
                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                     }
-        
+
                     SkColor color = 0xff007000;
                     this->drawPath(path.fPath, canvas, color, rect,
                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                     gFills[fill].fFill, SK_Scalar1*10);
-        
+
                     SkPaint rectPaint;
                     rectPaint.setColor(SK_ColorBLACK);
                     rectPaint.setStyle(SkPaint::kStroke_Style);
                     rectPaint.setStrokeWidth(-1);
                     rectPaint.setAntiAlias(true);
                     canvas->drawRect(rect, rectPaint);
-        
+
                     SkPaint labelPaint;
                     labelPaint.setColor(color);
                     labelPaint.setAntiAlias(true);
@@ -289,7 +289,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/matrixconvolution.cpp b/gm/matrixconvolution.cpp
new file mode 100644
index 0000000..59bd380
--- /dev/null
+++ b/gm/matrixconvolution.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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 "SkColor.h"
+#include "SkMatrixConvolutionImageFilter.h"
+#include "SkGradientShader.h"
+
+namespace skiagm {
+
+class MatrixConvolutionGM : public GM {
+public:
+    MatrixConvolutionGM() : fInitialized(false) {
+        this->setBGColor(0x00000000);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("matrixconvolution");
+    }
+
+    void make_bitmap() {
+        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, 80, 80);
+        fBitmap.allocPixels();
+        SkDevice device(fBitmap);
+        SkCanvas canvas(&device);
+        canvas.clear(0x00000000);
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setColor(0xFFFFFFFF);
+        paint.setTextSize(SkIntToScalar(180));
+        SkPoint pts[2] = { SkPoint::Make(0, 0),
+                           SkPoint::Make(0, SkIntToScalar(80)) };
+        SkColor colors[2] = { 0xFFFFFFFF, 0x40404040 };
+        SkScalar pos[2] = { 0, SkIntToScalar(80) };
+        paint.setShader(SkGradientShader::CreateLinear(
+            pts, colors, pos, 2, SkShader::kClamp_TileMode))->unref();
+        const char* str = "e";
+        canvas.drawText(str, strlen(str), SkIntToScalar(-10), SkIntToScalar(80), paint);
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(400, 300);
+    }
+
+    void draw(SkCanvas* canvas, int x, int y, const SkIPoint& target, SkMatrixConvolutionImageFilter::TileMode tileMode, bool convolveAlpha) {
+        SkScalar kernel[9] = {
+            SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
+            SkIntToScalar( 1), SkIntToScalar(-7), SkIntToScalar( 1),
+            SkIntToScalar( 1), SkIntToScalar( 1), SkIntToScalar( 1),
+        };
+        SkISize kernelSize = SkISize::Make(3, 3);
+        SkScalar gain = SkFloatToScalar(0.3f), bias = SkIntToScalar(100);
+        SkPaint paint;
+        SkAutoTUnref<SkImageFilter> filter(SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias, target, tileMode, convolveAlpha)));
+        paint.setImageFilter(filter);
+        canvas->save();
+        canvas->clipRect(SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+            SkIntToScalar(fBitmap.width()), SkIntToScalar(fBitmap.height())));
+        canvas->drawBitmap(fBitmap, SkIntToScalar(x), SkIntToScalar(y), &paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        if (!fInitialized) {
+            make_bitmap();
+            fInitialized = true;
+        }
+        canvas->clear(0x00000000);
+        SkIPoint target = SkIPoint::Make(1, 0);
+        for (int x = 10; x < 310; x += 100) {
+            draw(canvas, x, 10, target, SkMatrixConvolutionImageFilter::kClamp_TileMode, true);
+            draw(canvas, x, 110, target, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, true);
+            draw(canvas, x, 210, target, SkMatrixConvolutionImageFilter::kRepeat_TileMode, true);
+            target.fY++;
+        }
+        target.fY = 1;
+        draw(canvas, 310, 10, target, SkMatrixConvolutionImageFilter::kClamp_TileMode, false);
+        draw(canvas, 310, 110, target, SkMatrixConvolutionImageFilter::kClampToBlack_TileMode, false);
+        draw(canvas, 310, 210, target, SkMatrixConvolutionImageFilter::kRepeat_TileMode, false);
+    }
+
+private:
+    typedef GM INHERITED;
+    SkBitmap fBitmap;
+    bool fInitialized;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new MatrixConvolutionGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/modecolorfilters.cpp b/gm/modecolorfilters.cpp
new file mode 100644
index 0000000..3e1ccb8
--- /dev/null
+++ b/gm/modecolorfilters.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 "SkBitmapProcShader.h"
+#include "SkColorFilter.h"
+#include "SkGradientShader.h"
+
+#define WIDTH 512
+#define HEIGHT 1024
+
+namespace skiagm {
+
+// Using gradients because GPU doesn't currently have an implementation of SkColorShader (duh!)
+static SkShader* make_color_shader(SkColor color) {
+    static const SkPoint kPts[] = {{0, 0}, {1, 1}};
+    SkColor colors[] = {color, color};
+
+    return SkGradientShader::CreateLinear(kPts, colors, NULL, 2, SkShader::kClamp_TileMode);
+}
+
+static SkShader* make_solid_shader() {
+    return make_color_shader(SkColorSetARGB(0xFF, 0x40, 0x80, 0x20));
+}
+
+static SkShader* make_transparent_shader() {
+    return make_color_shader(SkColorSetARGB(0x80, 0x10, 0x70, 0x20));
+}
+
+static SkShader* make_trans_black_shader() {
+    return make_color_shader(0x0);
+}
+
+// draws a background behind each test rect to see transparency
+static SkShader* make_bg_shader(int checkSize) {
+    SkBitmap bmp;
+    bmp.setConfig(SkBitmap::kARGB_8888_Config, 2 * checkSize, 2 * checkSize);
+    bmp.allocPixels();
+    SkCanvas canvas(bmp);
+    canvas.clear(0xFF800000);
+    SkPaint paint;
+    paint.setColor(0xFF000080);
+    SkRect rect0 = SkRect::MakeXYWH(0, 0,
+                                    SkIntToScalar(checkSize), SkIntToScalar(checkSize));
+    SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(checkSize), SkIntToScalar(checkSize),
+                                    SkIntToScalar(checkSize), SkIntToScalar(checkSize));
+    canvas.drawRect(rect1, paint);
+    canvas.drawRect(rect0, paint);
+    return SkNEW_ARGS(SkBitmapProcShader, (bmp, SkShader::kRepeat_TileMode,
+                                                SkShader::kRepeat_TileMode));
+}
+
+class ModeColorFilterGM : public GM {
+public:
+    ModeColorFilterGM() {
+        this->setBGColor(0xFF303030);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("modecolorfilters");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(WIDTH, HEIGHT);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        // size of rect for each test case
+        static const int kRectWidth  = 20;
+        static const int kRectHeight = 20;
+
+        static const int kCheckSize  = 10;
+
+        if (!fBmpShader) {
+            fBmpShader.reset(make_bg_shader(kCheckSize));
+        }
+        SkPaint bgPaint;
+        bgPaint.setShader(fBmpShader);
+        bgPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
+
+        SkShader* shaders[] = {
+            NULL,                                   // use a paint color instead of a shader
+            make_solid_shader(),
+            make_transparent_shader(),
+            make_trans_black_shader(),
+        };
+
+        // used without shader
+        SkColor colors[] = {
+            SkColorSetARGB(0xFF, 0xFF, 0xFF, 0xFF),
+            SkColorSetARGB(0xFF, 0x00, 0x00, 0x00),
+            SkColorSetARGB(0x00, 0x00, 0x00, 0x00),
+            SkColorSetARGB(0xFF, 0x10, 0x20, 0x40),
+            SkColorSetARGB(0xA0, 0x20, 0x30, 0x90),
+        };
+
+        // used with shaders
+        SkColor alphas[] = {0xFFFFFFFF, 0x80808080};
+
+        SkXfermode::Mode modes[]  = { // currently just doing the Modes expressible as Coeffs
+            SkXfermode::kClear_Mode,
+            SkXfermode::kSrc_Mode,
+            SkXfermode::kDst_Mode,
+            SkXfermode::kSrcOver_Mode,
+            SkXfermode::kDstOver_Mode,
+            SkXfermode::kSrcIn_Mode,
+            SkXfermode::kDstIn_Mode,
+            SkXfermode::kSrcOut_Mode,
+            SkXfermode::kDstOut_Mode,
+            SkXfermode::kSrcATop_Mode,
+            SkXfermode::kDstATop_Mode,
+            SkXfermode::kXor_Mode,
+            SkXfermode::kPlus_Mode,
+            SkXfermode::kModulate_Mode,
+        };
+
+        SkPaint paint;
+        int idx = 0;
+        static const int kRectsPerRow = SkMax32(this->getISize().fWidth / kRectWidth, 1);
+        for (size_t cfm = 0; cfm < SK_ARRAY_COUNT(modes); ++cfm) {
+            for (size_t cfc = 0; cfc < SK_ARRAY_COUNT(colors); ++cfc) {
+                SkAutoTUnref<SkColorFilter> cf(SkColorFilter::CreateModeFilter(colors[cfc],
+                                                                               modes[cfm]));
+                paint.setColorFilter(cf);
+                for (size_t s = 0; s < SK_ARRAY_COUNT(shaders); ++s) {
+                    paint.setShader(shaders[s]);
+                    bool hasShader = NULL == paint.getShader();
+                    int paintColorCnt = hasShader ? SK_ARRAY_COUNT(alphas) : SK_ARRAY_COUNT(colors);
+                    SkColor* paintColors = hasShader ? alphas : colors;
+                    for (int pc = 0; pc < paintColorCnt; ++pc) {
+                        paint.setColor(paintColors[pc]);
+                        SkScalar x = SkIntToScalar(idx % kRectsPerRow);
+                        SkScalar y = SkIntToScalar(idx / kRectsPerRow);
+                        SkRect rect = SkRect::MakeXYWH(x * kRectWidth, y * kRectHeight,
+                                                       SkIntToScalar(kRectWidth),
+                                                       SkIntToScalar(kRectHeight));
+                        canvas->drawRect(rect, bgPaint);
+                        canvas->drawRect(rect, paint);
+                        ++idx;
+                    }
+                }
+            }
+        }
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(shaders); ++i) {
+            SkSafeUnref(shaders[i]);
+        }
+    }
+
+private:
+    SkAutoTUnref<SkShader> fBmpShader;
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new ModeColorFilterGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/morphology.cpp b/gm/morphology.cpp
index bfaa406..83de758 100644
--- a/gm/morphology.cpp
+++ b/gm/morphology.cpp
@@ -19,7 +19,7 @@
         this->setBGColor(0xFF000000);
         fOnce = false;
     }
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("morphology");
@@ -50,40 +50,40 @@
             fOnce = true;
         }
         struct {
+            int fWidth, fHeight;
             int fRadiusX, fRadiusY;
-            bool erode;
-            SkScalar fX, fY;
         } samples[] = {
-            { 0, 0, false, 0,   0 },
-            { 0, 2, false, 140, 0 },
-            { 2, 0, false, 280, 0 },
-            { 2, 2, false, 420, 0 },
-            { 0, 0, true,  0,   140 },
-            { 0, 2, true,  140, 140 },
-            { 2, 0, true,  280, 140 },
-            { 2, 2, true,  420, 140 },
+            { 140, 140,   0,   0 },
+            { 140, 140,   0,   2 },
+            { 140, 140,   2,   0 },
+            { 140, 140,   2,   2 },
+            {  24,  24,  25,  25 },
         };
-        const char* str = "The quick brown fox jumped over the lazy dog.";
         SkPaint paint;
-        for (unsigned i = 0; i < SK_ARRAY_COUNT(samples); ++i) {
-            if (samples[i].erode) {
-                paint.setImageFilter(new SkErodeImageFilter(
-                    samples[i].fRadiusX,
-                    samples[i].fRadiusY))->unref();
-            } else {
-                paint.setImageFilter(new SkDilateImageFilter(
-                    samples[i].fRadiusX,
-                    samples[i].fRadiusY))->unref();
+        for (unsigned j = 0; j < 2; ++j) {
+            for (unsigned i = 0; i < SK_ARRAY_COUNT(samples); ++i) {
+                SkScalar x = SkIntToScalar(i * 140), y = SkIntToScalar(j * 140);
+                if (j) {
+                    paint.setImageFilter(new SkErodeImageFilter(
+                        samples[i].fRadiusX,
+                        samples[i].fRadiusY))->unref();
+                } else {
+                    paint.setImageFilter(new SkDilateImageFilter(
+                        samples[i].fRadiusX,
+                        samples[i].fRadiusY))->unref();
+                }
+                SkRect bounds = SkRect::MakeXYWH(
+                    x,
+                    y,
+                    SkIntToScalar(samples[i].fWidth),
+                    SkIntToScalar(samples[i].fHeight));
+                canvas->saveLayer(&bounds, &paint);
+                canvas->drawBitmap(fBitmap, x, y);
+                canvas->restore();
             }
-            SkRect bounds = SkRect::MakeXYWH(samples[i].fX,
-                                             samples[i].fY,
-                                             140, 140);
-            canvas->saveLayer(&bounds, &paint);
-            canvas->drawBitmap(fBitmap, samples[i].fX, samples[i].fY);
-            canvas->restore();
         }
     }
-    
+
 private:
     typedef GM INHERITED;
     SkBitmap fBitmap;
diff --git a/gm/ninepatchstretch.cpp b/gm/ninepatchstretch.cpp
index d1d4dda..c4c5291 100644
--- a/gm/ninepatchstretch.cpp
+++ b/gm/ninepatchstretch.cpp
@@ -7,37 +7,44 @@
 
 #include "gm.h"
 
+#if SK_SUPPORT_GPU
 #include "SkGpuDevice.h"
+#else
+class GrContext;
+#endif
 
 static void make_bitmap(SkBitmap* bitmap, GrContext* ctx, SkIRect* center) {
     SkDevice* dev;
-    SkCanvas canvas;
-    
+
     const int kFixed = 28;
     const int kStretchy = 8;
     const int kSize = 2*kFixed + kStretchy;
-    
+
+#if SK_SUPPORT_GPU
     if (ctx) {
         dev = new SkGpuDevice(ctx, SkBitmap::kARGB_8888_Config, kSize, kSize);
         *bitmap = dev->accessBitmap(false);
-    } else {
+    } else
+#endif
+    {
         bitmap->setConfig(SkBitmap::kARGB_8888_Config, kSize, kSize);
         bitmap->allocPixels();
         dev = new SkDevice(*bitmap);
     }
-    
-    canvas.setDevice(dev)->unref();
-    canvas.clear(0);
-    
+
+    SkCanvas canvas(dev);
+    dev->unref();
+    canvas.clear(SK_ColorTRANSPARENT);
+
     SkRect r = SkRect::MakeWH(SkIntToScalar(kSize), SkIntToScalar(kSize));
     const SkScalar strokeWidth = SkIntToScalar(6);
     const SkScalar radius = SkIntToScalar(kFixed) - strokeWidth/2;
-    
+
     center->setXYWH(kFixed, kFixed, kStretchy, kStretchy);
-    
+
     SkPaint paint;
     paint.setAntiAlias(true);
-    
+
     paint.setColor(0xFFFF0000);
     canvas.drawRoundRect(r, radius, radius, paint);
     r.setXYWH(SkIntToScalar(kFixed), 0, SkIntToScalar(kStretchy), SkIntToScalar(kSize));
@@ -49,19 +56,19 @@
 }
 
 namespace skiagm {
-    
+
 class NinePatchStretchGM : public GM {
 public:
     SkBitmap fBM;
 
-	NinePatchStretchGM() {}
+    NinePatchStretchGM() {}
 
 protected:
     virtual SkString onShortName() {
         return SkString("ninepatch-stretch");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(400, 400);
     }
 
@@ -69,25 +76,25 @@
         SkBitmap bm;
         SkIRect center;
         make_bitmap(&bm, NULL /*SampleCode::GetGr()*/, &center);
-        
+
         // amount of bm that should not be stretched (unless we have to)
         const SkScalar fixed = SkIntToScalar(bm.width() - center.width());
-        
+
         const SkTSize<SkScalar> size[] = {
             { fixed * 4 / 5, fixed * 4 / 5 },   // shrink in both axes
             { fixed * 4 / 5, fixed * 4 },       // shrink in X
             { fixed * 4,     fixed * 4 / 5 },   // shrink in Y
             { fixed * 4,     fixed * 4 }
         };
-        
+
         canvas->drawBitmap(bm, SkIntToScalar(10), SkIntToScalar(10), NULL);
-        
+
         SkScalar x = SkIntToScalar(100);
         SkScalar y = SkIntToScalar(100);
-        
+
         SkPaint paint;
         paint.setFilterBitmap(true);
-        
+
         for (int iy = 0; iy < 2; ++iy) {
             for (int ix = 0; ix < 2; ++ix) {
                 int i = ix * 2 + iy;
@@ -97,7 +104,7 @@
             }
         }
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -108,6 +115,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
-
-
diff --git a/gm/patheffects.cpp b/gm/patheffects.cpp
index c606116..a1fb675 100644
--- a/gm/patheffects.cpp
+++ b/gm/patheffects.cpp
@@ -67,8 +67,8 @@
         path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
     path.close();
     path.offset(SkIntToScalar(-6), 0);
-    scale(&path, 1.5);
-    
+    scale(&path, 1.5f);
+
     paint->setPathEffect(new SkPath1DPathEffect(path, SkIntToScalar(21), 0,
                                 SkPath1DPathEffect::kRotate_Style))->unref();
     compose_pe(paint);
@@ -92,7 +92,7 @@
 
     SkPath path;
     path.addCircle(0, 0, SkIntToScalar(5));
-    
+
     return new SkPath2DPathEffect(m, path);
 }
 
@@ -146,6 +146,16 @@
             canvas->drawPath(path, paint);
             canvas->translate(0, 160);
         }
+
+        SkIRect rect = SkIRect::MakeXYWH(20, 20, 60, 60);
+        for (i = 0; i < SK_ARRAY_COUNT(gPE); i++) {
+            SkPaint p;
+            p.setAntiAlias(true);
+            p.setStyle(SkPaint::kFill_Style);
+            gPE[i](&p);
+            canvas->drawIRect(rect, p);
+            canvas->translate(75, 0);
+        }
     }
 
 private:
diff --git a/gm/pathfill.cpp b/gm/pathfill.cpp
index 9d4b3c8..993917a 100644
--- a/gm/pathfill.cpp
+++ b/gm/pathfill.cpp
@@ -6,9 +6,6 @@
  * found in the LICENSE file.
  */
 #include "gm.h"
-#include "SkPicture.h"
-#include "SkRectShape.h"
-#include "SkGroupShape.h"
 
 typedef SkScalar (*MakePathProc)(SkPath*);
 
@@ -16,7 +13,7 @@
     SkRect r = { SkIntToScalar(10), SkIntToScalar(10),
                  SkIntToScalar(630), SkIntToScalar(470) };
     path->addRoundRect(r, SkIntToScalar(15), SkIntToScalar(15));
-    
+
     SkPaint paint;
     paint.setStyle(SkPaint::kStroke_Style);
     paint.setStrokeWidth(SkIntToScalar(5));
@@ -58,7 +55,7 @@
     const SkScalar x0 = x;
     const SkScalar dx = SK_Scalar1 * 5;
     const SkScalar dy = SK_Scalar1 * 10;
-    
+
     path->moveTo(x, y);
     for (int i = 0; i < 32; i++) {
         x += dx;
@@ -75,10 +72,10 @@
 static SkScalar make_star(SkPath* path, int n) {
     const SkScalar c = SkIntToScalar(45);
     const SkScalar r = SkIntToScalar(20);
-    
+
     SkScalar rad = -SK_ScalarPI / 2;
     const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
-    
+
     path->moveTo(c, c - r);
     for (int i = 1; i < n; i++) {
         rad += drad;
@@ -92,6 +89,18 @@
 static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); }
 static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); }
 
+// We don't expect any output from this path.
+static SkScalar make_line(SkPath* path) {
+    path->moveTo(SkIntToScalar(30), SkIntToScalar(30));
+    path->lineTo(SkIntToScalar(120), SkIntToScalar(40));
+    path->close();
+    path->moveTo(SkIntToScalar(150), SkIntToScalar(30));
+    path->lineTo(SkIntToScalar(150), SkIntToScalar(30));
+    path->lineTo(SkIntToScalar(300), SkIntToScalar(40));
+    path->close();
+    return SkIntToScalar(40);
+}
+
 static const MakePathProc gProcs[] = {
     make_frame,
     make_triangle,
@@ -99,14 +108,13 @@
     make_oval,
     make_sawtooth,
     make_star_5,
-    make_star_13
+    make_star_13,
+    make_line,
 };
 
 #define N   SK_ARRAY_COUNT(gProcs)
 
-namespace skiagm {
-
-class PathFillGM : public GM {
+class PathFillGM : public skiagm::GM {
     SkPath  fPath[N];
     SkScalar fDY[N];
 public:
@@ -122,13 +130,13 @@
     }
 
     virtual SkISize onISize() {
-        return make_isize(640, 480);
+        return SkISize::Make(640, 480);
     }
 
     virtual void onDraw(SkCanvas* canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
-        
+
         for (size_t i = 0; i < N; i++) {
             canvas->drawPath(fPath[i], paint);
             canvas->translate(SkIntToScalar(0), fDY[i]);
@@ -136,12 +144,79 @@
     }
 
 private:
-    typedef GM INHERITED;
+    typedef skiagm::GM INHERITED;
+};
+
+// test inverse-fill w/ a clip that completely excludes the geometry
+class PathInverseFillGM : public skiagm::GM {
+    SkPath  fPath[N];
+    SkScalar fDY[N];
+public:
+    PathInverseFillGM() {
+        for (size_t i = 0; i < N; i++) {
+            fDY[i] = gProcs[i](&fPath[i]);
+        }
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("pathinvfill");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(450, 220);
+    }
+
+    static void show(SkCanvas* canvas, const SkPath& path, const SkPaint& paint,
+                     const SkRect* clip, SkScalar top, const SkScalar bottom) {
+        canvas->save();
+        if (clip) {
+            SkRect r = *clip;
+            r.fTop = top;
+            r.fBottom = bottom;
+            canvas->clipRect(r);
+        }
+        canvas->drawPath(path, paint);
+        canvas->restore();
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPath path;
+
+        path.addCircle(SkIntToScalar(50), SkIntToScalar(50), SkIntToScalar(40));
+        path.toggleInverseFillType();
+
+        SkRect clipR = { 0, 0, SkIntToScalar(100), SkIntToScalar(200) };
+
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+
+        for (int doclip = 0; doclip <= 1; ++doclip) {
+            for (int aa = 0; aa <= 1; ++aa) {
+                SkPaint paint;
+                paint.setAntiAlias(SkToBool(aa));
+
+                canvas->save();
+                canvas->clipRect(clipR);
+
+                const SkRect* clipPtr = doclip ? &clipR : NULL;
+
+                show(canvas, path, paint, clipPtr, clipR.fTop, clipR.centerY());
+                show(canvas, path, paint, clipPtr, clipR.centerY(), clipR.fBottom);
+
+                canvas->restore();
+                canvas->translate(SkIntToScalar(110), 0);
+            }
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static GM* MyFactory(void*) { return new PathFillGM; }
-static GMRegistry reg(MyFactory);
+static skiagm::GM* MyFactory(void*) { return new PathFillGM; }
+static skiagm::GMRegistry reg(MyFactory);
 
-}
+static skiagm::GM* F1(void*) { return new PathInverseFillGM; }
+static skiagm::GMRegistry gR1(F1);
diff --git a/gm/pathinterior.cpp b/gm/pathinterior.cpp
new file mode 100644
index 0000000..559fb89
--- /dev/null
+++ b/gm/pathinterior.cpp
@@ -0,0 +1,113 @@
+/*
+ * 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 "SkGraphics.h"
+#include "SkRandom.h"
+#include "SkLayerDrawLooper.h"
+#include "SkBlurMaskFilter.h"
+
+static SkRect inset(const SkRect& r) {
+    SkRect rect = r;
+    rect.inset(r.width() / 8, r.height() / 8);
+    return rect;
+}
+
+class PathInteriorGM : public skiagm::GM {
+public:
+    PathInteriorGM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    virtual SkISize onISize() {
+        return SkISize::Make(770, 770);
+    }
+
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("pathinterior");
+    }
+
+    void show(SkCanvas* canvas, const SkPath& path) {
+        SkPaint paint;
+        paint.setAntiAlias(true);
+
+        SkRect rect;
+#if 0
+        bool hasInterior = path.hasRectangularInterior(&rect);
+#else
+        bool hasInterior = false;
+#endif
+
+        paint.setColor(hasInterior ? 0xFF8888FF : SK_ColorGRAY);
+        canvas->drawPath(path, paint);
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorRED);
+        canvas->drawPath(path, paint);
+
+        if (hasInterior) {
+            paint.setStyle(SkPaint::kFill_Style);
+            paint.setColor(0x8800FF00);
+            canvas->drawRect(rect, paint);
+        }
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        canvas->translate(8.5f, 8.5f);
+
+        const SkRect rect = { 0, 0, 80, 80 };
+        const SkScalar RAD = rect.width()/8;
+
+        int i = 0;
+        for (int insetFirst = 0; insetFirst <= 1; ++insetFirst) {
+            for (int doEvenOdd = 0; doEvenOdd <= 1; ++doEvenOdd) {
+                for (int outerRR = 0; outerRR <= 1; ++outerRR) {
+                    for (int innerRR = 0; innerRR <= 1; ++innerRR) {
+                        for (int outerCW = 0; outerCW <= 1; ++outerCW) {
+                            for (int innerCW = 0; innerCW <= 1; ++innerCW) {
+                                SkPath path;
+                                path.setFillType(doEvenOdd ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType);
+                                SkPath::Direction outerDir = outerCW ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+                                SkPath::Direction innerDir = innerCW ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+
+                                SkRect r = insetFirst ? inset(rect) : rect;
+                                if (outerRR) {
+                                    path.addRoundRect(r, RAD, RAD, outerDir);
+                                } else {
+                                    path.addRect(r, outerDir);
+                                }
+                                r = insetFirst ? rect : inset(rect);
+                                if (innerRR) {
+                                    path.addRoundRect(r, RAD, RAD, innerDir);
+                                } else {
+                                    path.addRect(r, innerDir);
+                                }
+
+                                SkScalar dx = (i / 8) * rect.width() * 6 / 5;
+                                SkScalar dy = (i % 8) * rect.height() * 6 / 5;
+                                i++;
+                                path.offset(dx, dy);
+
+                                this->show(canvas, path);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+private:
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new PathInteriorGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/pathreverse.cpp b/gm/pathreverse.cpp
index fc8028e..b605a4c 100644
--- a/gm/pathreverse.cpp
+++ b/gm/pathreverse.cpp
@@ -14,7 +14,7 @@
     SkPaint paint;
     paint.setAntiAlias(true);
     canvas->drawPath(path, paint);
-    
+
     paint.setStyle(SkPaint::kStroke_Style);
     paint.setColor(SK_ColorRED);
     canvas->drawPath(path, paint);
@@ -78,16 +78,17 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
+        if (false) test_rev(canvas); // avoid bit rot, suppress warning
         SkRect r = { 10, 10, 100, 60 };
-        
+
         SkPath path;
-        
+
         path.addRect(r); test_rev(canvas, path);
-        
+
         canvas->translate(0, 100);
         path.offset(20, 20);
         path.addRect(r); test_rev(canvas, path);
-        
+
         canvas->translate(0, 100);
         path.reset();
         path.moveTo(10, 10); path.lineTo(30, 30);
@@ -95,7 +96,7 @@
         r.offset(50, 20);
         path.addOval(r);
         test_rev(canvas, path);
-        
+
         SkPaint paint;
         paint.setTextSize(SkIntToScalar(100));
         SkTypeface* hira = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro", SkTypeface::kNormal);
diff --git a/gm/points.cpp b/gm/points.cpp
index e4f3c2e..0a83acf 100644
--- a/gm/points.cpp
+++ b/gm/points.cpp
@@ -12,14 +12,14 @@
 
 class PointsGM : public GM {
 public:
-	PointsGM() {}
+    PointsGM() {}
 
 protected:
     virtual SkString onShortName() {
         return SkString("points");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(640, 490);
     }
 
@@ -71,4 +71,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/poly2poly.cpp b/gm/poly2poly.cpp
index df824b5..20c117f 100644
--- a/gm/poly2poly.cpp
+++ b/gm/poly2poly.cpp
@@ -11,38 +11,38 @@
 
 class Poly2PolyGM : public GM {
 public:
-	Poly2PolyGM() {}
-    
+    Poly2PolyGM() {}
+
 protected:
     virtual SkString onShortName() {
         return SkString("poly2poly");
     }
 
-	virtual SkISize onISize() {
+    virtual SkISize onISize() {
         return make_isize(835, 840);
     }
-	
+
     static void doDraw(SkCanvas* canvas, SkPaint* paint, const int isrc[],
                        const int idst[], int count) {
         SkMatrix matrix;
         SkPoint src[4], dst[4];
-        
+
         for (int i = 0; i < count; i++) {
             src[i].set(SkIntToScalar(isrc[2*i+0]), SkIntToScalar(isrc[2*i+1]));
             dst[i].set(SkIntToScalar(idst[2*i+0]), SkIntToScalar(idst[2*i+1]));
         }
-        
+
         canvas->save();
         matrix.setPolyToPoly(src, dst, count);
         canvas->concat(matrix);
-        
+
         paint->setColor(SK_ColorGRAY);
         paint->setStyle(SkPaint::kStroke_Style);
         const SkScalar D = SkIntToScalar(64);
         canvas->drawRectCoords(0, 0, D, D, *paint);
         canvas->drawLine(0, 0, D, D, *paint);
         canvas->drawLine(0, D, D, 0, *paint);
-        
+
         SkPaint::FontMetrics fm;
         paint->getFontMetrics(&fm);
         paint->setColor(SK_ColorRED);
@@ -52,17 +52,17 @@
         SkString str;
         str.appendS32(count);
         canvas->drawText(str.c_str(), str.size(), x, y, *paint);
-        
+
         canvas->restore();
     }
-    
-    virtual void onDraw(SkCanvas* canvas) {        
+
+    virtual void onDraw(SkCanvas* canvas) {
         SkPaint paint;
         paint.setAntiAlias(true);
         paint.setStrokeWidth(SkIntToScalar(4));
         paint.setTextSize(SkIntToScalar(40));
         paint.setTextAlign(SkPaint::kCenter_Align);
-		
+
         canvas->save();
         canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
         // translate (1 point)
@@ -70,7 +70,7 @@
         const int dst1[] = { 5, 5 };
         doDraw(canvas, &paint, src1, dst1, 1);
         canvas->restore();
-        
+
         canvas->save();
         canvas->translate(SkIntToScalar(160), SkIntToScalar(10));
         // rotate/uniform-scale (2 points)
@@ -78,7 +78,7 @@
         const int dst2[] = { 32, 32, 64, 48 };
         doDraw(canvas, &paint, src2, dst2, 2);
         canvas->restore();
-        
+
         canvas->save();
         canvas->translate(SkIntToScalar(10), SkIntToScalar(110));
         // rotate/skew (3 points)
@@ -86,7 +86,7 @@
         const int dst3[] = { 0, 0, 96, 0, 24, 64 };
         doDraw(canvas, &paint, src3, dst3, 3);
         canvas->restore();
-        
+
         canvas->save();
         canvas->translate(SkIntToScalar(160), SkIntToScalar(110));
         // perspective (4 points)
@@ -95,7 +95,7 @@
         doDraw(canvas, &paint, src4, dst4, 4);
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -106,4 +106,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/quadpaths.cpp b/gm/quadpaths.cpp
index f8b172b..ac91c62 100644
--- a/gm/quadpaths.cpp
+++ b/gm/quadpaths.cpp
@@ -19,9 +19,9 @@
     SkString onShortName() {
         return SkString("quadpath");
     }
-        
+
     SkISize onISize() { return make_isize(1240, 390); }
-    
+
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
                   SkPaint::Style style, SkPath::FillType fill,
@@ -38,7 +38,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         struct FillAndName {
             SkPath::FillType fFill;
@@ -110,19 +110,19 @@
                     if (0 < style) {
                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                     }
-        
+
                     SkColor color = 0xff007000;
                     this->drawPath(path.fPath, canvas, color, rect,
                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                     gFills[fill].fFill, SK_Scalar1*10);
-        
+
                     SkPaint rectPaint;
                     rectPaint.setColor(SK_ColorBLACK);
                     rectPaint.setStyle(SkPaint::kStroke_Style);
                     rectPaint.setStrokeWidth(-1);
                     rectPaint.setAntiAlias(true);
                     canvas->drawRect(rect, rectPaint);
-        
+
                     SkPaint labelPaint;
                     labelPaint.setColor(color);
                     labelPaint.setAntiAlias(true);
@@ -148,7 +148,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -161,9 +161,9 @@
     SkString onShortName() {
         return SkString("quadclosepath");
     }
-        
+
     SkISize onISize() { return make_isize(1240, 390); }
-    
+
     void drawPath(SkPath& path,SkCanvas* canvas,SkColor color,
                   const SkRect& clip,SkPaint::Cap cap, SkPaint::Join join,
                   SkPaint::Style style, SkPath::FillType fill,
@@ -180,7 +180,7 @@
         canvas->drawPath(path, paint);
         canvas->restore();
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         struct FillAndName {
             SkPath::FillType fFill;
@@ -253,19 +253,19 @@
                     if (0 < style) {
                         canvas->translate(rect.width() + 40 * SK_Scalar1, 0);
                     }
-        
+
                     SkColor color = 0xff007000;
                     this->drawPath(path.fPath, canvas, color, rect,
                                     gCaps[cap].fCap, gCaps[cap].fJoin, gStyles[style].fStyle,
                                     gFills[fill].fFill, SK_Scalar1*10);
-        
+
                     SkPaint rectPaint;
                     rectPaint.setColor(SK_ColorBLACK);
                     rectPaint.setStyle(SkPaint::kStroke_Style);
                     rectPaint.setStrokeWidth(-1);
                     rectPaint.setAntiAlias(true);
                     canvas->drawRect(rect, rectPaint);
-        
+
                     SkPaint labelPaint;
                     labelPaint.setColor(color);
                     labelPaint.setAntiAlias(true);
@@ -291,7 +291,7 @@
         canvas->restore();
         canvas->restore();
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/resources/CMYK.jpg b/gm/resources/CMYK.jpg
new file mode 100644
index 0000000..04ed985
--- /dev/null
+++ b/gm/resources/CMYK.jpg
Binary files differ
diff --git a/gm/resources/plane.png b/gm/resources/plane.png
new file mode 100644
index 0000000..03585b5
--- /dev/null
+++ b/gm/resources/plane.png
Binary files differ
diff --git a/gm/rrect.cpp b/gm/rrect.cpp
new file mode 100644
index 0000000..29d0300
--- /dev/null
+++ b/gm/rrect.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 "SkRRect.h"
+#include "SkPath.h"
+
+typedef void (*InsetProc)(const SkRRect&, SkScalar dx, SkScalar dy, SkRRect*);
+
+static void inset0(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
+    SkRect r = src.rect();
+
+    r.inset(dx, dy);
+    if (r.isEmpty()) {
+        dst->setEmpty();
+        return;
+    }
+
+    SkVector radii[4];
+    for (int i = 0; i < 4; ++i) {
+        radii[i] = src.radii((SkRRect::Corner)i);
+    }
+    for (int i = 0; i < 4; ++i) {
+        radii[i].fX -= dx;
+        radii[i].fY -= dy;
+    }
+    dst->setRectRadii(r, radii);
+}
+
+static void inset1(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
+    SkRect r = src.rect();
+
+    r.inset(dx, dy);
+    if (r.isEmpty()) {
+        dst->setEmpty();
+        return;
+    }
+
+    SkVector radii[4];
+    for (int i = 0; i < 4; ++i) {
+        radii[i] = src.radii((SkRRect::Corner)i);
+    }
+    dst->setRectRadii(r, radii);
+}
+
+static void inset2(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
+    SkRect r = src.rect();
+
+    r.inset(dx, dy);
+    if (r.isEmpty()) {
+        dst->setEmpty();
+        return;
+    }
+
+    SkVector radii[4];
+    for (int i = 0; i < 4; ++i) {
+        radii[i] = src.radii((SkRRect::Corner)i);
+    }
+    for (int i = 0; i < 4; ++i) {
+        if (radii[i].fX) {
+            radii[i].fX -= dx;
+        }
+        if (radii[i].fY) {
+            radii[i].fY -= dy;
+        }
+    }
+    dst->setRectRadii(r, radii);
+}
+
+static SkScalar prop(SkScalar radius, SkScalar delta, SkScalar newSize, SkScalar oldSize) {
+    return newSize * radius / oldSize;
+}
+
+static void inset3(const SkRRect& src, SkScalar dx, SkScalar dy, SkRRect* dst) {
+    SkRect r = src.rect();
+
+    r.inset(dx, dy);
+    if (r.isEmpty()) {
+        dst->setEmpty();
+        return;
+    }
+
+    SkVector radii[4];
+    for (int i = 0; i < 4; ++i) {
+        radii[i] = src.radii((SkRRect::Corner)i);
+    }
+    for (int i = 0; i < 4; ++i) {
+        radii[i].fX = prop(radii[i].fX, dx, r.width(), src.rect().width());
+        radii[i].fY = prop(radii[i].fY, dy, r.height(), src.rect().height());
+    }
+    dst->setRectRadii(r, radii);
+}
+
+static void draw_rrect_color(SkCanvas* canvas, const SkRRect& rrect) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    if (rrect.isRect()) {
+        paint.setColor(SK_ColorRED);
+    } else if (rrect.isOval()) {
+        paint.setColor(0xFF008800);
+    } else if (rrect.isSimple()) {
+        paint.setColor(SK_ColorBLUE);
+    } else {
+        paint.setColor(SK_ColorBLACK);
+    }
+    canvas->drawRRect(rrect, paint);
+}
+
+static void drawrr(SkCanvas* canvas, const SkRRect& rrect, InsetProc proc) {
+    SkRRect rr;
+    for (SkScalar d = -30; d <= 30; d += 5) {
+        proc(rrect, d, d, &rr);
+        draw_rrect_color(canvas, rr);
+    }
+}
+
+class RRectGM : public skiagm::GM {
+public:
+    RRectGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("rrect");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(820, 710);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        static const InsetProc insetProcs[] = {
+            inset0, inset1, inset2, inset3
+        };
+
+        SkRRect rrect[4];
+        SkRect r = { 0, 0, 120, 100 };
+        SkVector radii[4] = {
+            { 0, 0 }, { 30, 1 }, { 10, 40 }, { 40, 40 }
+        };
+
+        rrect[0].setRect(r);
+        rrect[1].setOval(r);
+        rrect[2].setRectXY(r, 20, 20);
+        rrect[3].setRectRadii(r, radii);
+
+        canvas->translate(50.5f, 50.5f);
+        for (size_t j = 0; j < SK_ARRAY_COUNT(insetProcs); ++j) {
+            canvas->save();
+            for (size_t i = 0; i < SK_ARRAY_COUNT(rrect); ++i) {
+                drawrr(canvas, rrect[i], insetProcs[j]);
+                canvas->translate(200, 0);
+            }
+            canvas->restore();
+            canvas->translate(0, 170);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+DEF_GM( return new RRectGM; )
diff --git a/gm/rrects.cpp b/gm/rrects.cpp
new file mode 100644
index 0000000..299e8d8
--- /dev/null
+++ b/gm/rrects.cpp
@@ -0,0 +1,148 @@
+/*
+ * 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 "SkRRect.h"
+
+namespace skiagm {
+
+///////////////////////////////////////////////////////////////////////////////
+
+class RRectGM : public GM {
+public:
+    RRectGM(bool doAA, bool doClip) : fDoAA(doAA), fDoClip(doClip) {
+        this->setBGColor(0xFFDDDDDD);
+        this->setUpRRects();
+    }
+
+protected:
+    SkString onShortName() {
+        SkString name("rrect");
+        if (fDoClip) {
+            name.append("_clip");
+        }
+        if (fDoAA) {
+            name.append("_aa");
+        } else {
+            name.append("_bw");
+        }
+
+        return name;
+    }
+
+    virtual SkISize onISize() { return make_isize(kImageWidth, kImageHeight); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        SkPaint paint;
+        // when clipping the AA is pushed into the clip operation
+        paint.setAntiAlias(fDoClip ? false : fDoAA);
+
+        static const SkRect kMaxTileBound = SkRect::MakeWH(SkIntToScalar(kTileX), SkIntToScalar(kTileY));
+
+        int curRRect = 0;
+        for (int y = 1; y < kImageHeight; y += kTileY) {
+            for (int x = 1; x < kImageWidth; x += kTileX) {
+                if (curRRect >= kNumRRects) {
+                    break;
+                }
+                SkASSERT(kMaxTileBound.contains(fRRects[curRRect].getBounds()));
+
+                canvas->save();
+                    canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
+                    if (fDoClip) {
+                        canvas->clipRRect(fRRects[curRRect], SkRegion::kReplace_Op, fDoAA);
+                        canvas->drawRect(kMaxTileBound, paint);
+                    } else {
+                        canvas->drawRRect(fRRects[curRRect], paint);
+                    }
+                    ++curRRect;
+                canvas->restore();
+            }
+        }
+    }
+
+    void setUpRRects() {
+        // each RRect must fit in a 0x0 -> (kTileX-2)x(kTileY-2) block. These will be tiled across
+        // the screen in kTileX x kTileY tiles. The extra empty pixels on each side are for AA.
+
+        // simple cases
+        fRRects[0].setRect(SkRect::MakeWH(kTileX-2, kTileY-2));
+        fRRects[1].setOval(SkRect::MakeWH(kTileX-2, kTileY-2));
+        fRRects[2].setRectXY(SkRect::MakeWH(kTileX-2, kTileY-2), 10, 10);
+
+        // The first complex case needs special handling since it is a square
+        fRRects[kNumSimpleCases].setRectRadii(SkRect::MakeWH(kTileY-2, kTileY-2), gRadii[0]);
+        for (size_t i = 1; i < SK_ARRAY_COUNT(gRadii); ++i) {
+            fRRects[kNumSimpleCases+i].setRectRadii(SkRect::MakeWH(kTileX-2, kTileY-2), gRadii[i]);
+        }
+    }
+
+private:
+    bool fDoAA;
+    bool fDoClip;   // use clipRRect & drawRect instead of drawRRect
+
+    static const int kImageWidth = 640;
+    static const int kImageHeight = 480;
+
+    static const int kTileX = 80;
+    static const int kTileY = 40;
+
+    static const int kNumSimpleCases = 3;
+    static const int kNumComplexCases = 19;
+    static const SkVector gRadii[kNumComplexCases][4];
+
+    static const int kNumRRects = kNumSimpleCases + kNumComplexCases;
+    SkRRect fRRects[kNumRRects];
+
+    typedef GM INHERITED;
+};
+
+// Radii for the various test cases. Order is UL, UR, LR, LL
+const SkVector RRectGM::gRadii[kNumComplexCases][4] = {
+    // a circle
+    { { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY }, { kTileY, kTileY } },
+
+    // odd ball cases
+    { { 8, 8 }, { 32, 32 }, { 8, 8 }, { 32, 32 } },
+    { { 16, 8 }, { 8, 16 }, { 16, 8 }, { 8, 16 } },
+    { { 0, 0 }, { 16, 16 }, { 8, 8 }, { 32, 32 } },
+
+    // UL
+    { { 30, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+    { { 30, 15 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+    { { 15, 30 }, { 0, 0 }, { 0, 0 }, { 0, 0 } },
+
+    // UR
+    { { 0, 0 }, { 30, 30 }, { 0, 0 }, { 0, 0 } },
+    { { 0, 0 }, { 30, 15 }, { 0, 0 }, { 0, 0 } },
+    { { 0, 0 }, { 15, 30 }, { 0, 0 }, { 0, 0 } },
+
+    // LR
+    { { 0, 0 }, { 0, 0 }, { 30, 30 }, { 0, 0 } },
+    { { 0, 0 }, { 0, 0 }, { 30, 15 }, { 0, 0 } },
+    { { 0, 0 }, { 0, 0 }, { 15, 30 }, { 0, 0 } },
+
+    // LL
+    { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 30 } },
+    { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 30, 15 } },
+    { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 15, 30 } },
+
+    // over-sized radii
+    { { 0, 0 }, { 100, 400 }, { 0, 0 }, { 0, 0 } },
+    { { 0, 0 }, { 400, 400 }, { 0, 0 }, { 0, 0 } },
+    { { 400, 400 }, { 400, 400 }, { 400, 400 }, { 400, 400 } },
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new RRectGM(false, false); )
+DEF_GM( return new RRectGM(true, false); )
+DEF_GM( return new RRectGM(false, true); )
+DEF_GM( return new RRectGM(true, true); )
+
+}
diff --git a/gm/samplerstress.cpp b/gm/samplerstress.cpp
new file mode 100644
index 0000000..93a26dd
--- /dev/null
+++ b/gm/samplerstress.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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 "SkShader.h"
+#include "SkStippleMaskFilter.h"
+
+namespace skiagm {
+
+/**
+ * Stress test the samplers by rendering a textured glyph with a mask and
+ * an AA clip
+ */
+class SamplerStressGM : public GM {
+public:
+    SamplerStressGM()
+    : fTextureCreated(false)
+    , fShader(NULL)
+    , fMaskFilter(NULL) {
+    }
+
+    virtual ~SamplerStressGM() {
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("samplerstress");
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 480);
+    }
+
+    /**
+     * Create a red & green stripes on black texture
+     */
+    void createTexture() {
+        if (fTextureCreated) {
+            return;
+        }
+
+        static const int xSize = 16;
+        static const int ySize = 16;
+
+        fTexture.setConfig(SkBitmap::kARGB_8888_Config,
+                           xSize,
+                           ySize,
+                           xSize*sizeof(SkColor));
+
+        fTexture.allocPixels();
+        fTexture.lockPixels();
+        SkPMColor* addr = fTexture.getAddr32(0, 0);
+
+        for (int y = 0; y < ySize; ++y) {
+            for (int x = 0; x < xSize; ++x) {
+                addr[y*xSize+x] = SkPreMultiplyColor(SK_ColorBLACK);
+
+                if ((y % 5) == 0) {
+                    addr[y*xSize+x] = SkPreMultiplyColor(SK_ColorRED);
+                }
+                if ((x % 7) == 0) {
+                    addr[y*xSize+x] = SkPreMultiplyColor(SK_ColorGREEN);
+                }
+            }
+        }
+
+        fTexture.unlockPixels();
+
+        fTextureCreated = true;
+    }
+
+    void createShader() {
+        if (NULL != fShader.get()) {
+            return;
+        }
+
+        createTexture();
+
+        fShader.reset(SkShader::CreateBitmapShader(fTexture,
+                                                   SkShader::kRepeat_TileMode,
+                                                   SkShader::kRepeat_TileMode));
+    }
+
+    void createMaskFilter() {
+        if (NULL != fMaskFilter.get()) {
+            return;
+        }
+
+        fMaskFilter.reset(SkNEW(SkStippleMaskFilter));
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        createShader();
+        createMaskFilter();
+
+        canvas->save();
+
+        // draw a letter "M" with a green & red striped texture and a
+        // stipple mask with a round rect soft clip
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(72);
+        paint.setShader(fShader.get());
+        paint.setMaskFilter(fMaskFilter.get());
+
+        SkRect temp;
+        temp.set(SkIntToScalar(115),
+                 SkIntToScalar(75),
+                 SkIntToScalar(144),
+                 SkIntToScalar(110));
+
+        SkPath path;
+        path.addRoundRect(temp, SkIntToScalar(5), SkIntToScalar(5));
+
+        canvas->clipPath(path, SkRegion::kReplace_Op, true); // AA is on
+
+        canvas->drawText("M", 1,
+                         SkIntToScalar(100), SkIntToScalar(100),
+                         paint);
+
+        canvas->restore();
+
+        // Now draw stroked versions of the "M" and the round rect so we can
+        // see what is going on
+        SkPaint paint2;
+        paint2.setColor(SK_ColorBLACK);
+        paint2.setAntiAlias(true);
+        paint2.setTextSize(72);
+        paint2.setStyle(SkPaint::kStroke_Style);
+        paint2.setStrokeWidth(1);
+        canvas->drawText("M", 1,
+                         SkIntToScalar(100), SkIntToScalar(100),
+                         paint2);
+
+        paint2.setColor(SK_ColorGRAY);
+
+        canvas->drawPath(path, paint2);
+    }
+
+private:
+    SkBitmap      fTexture;
+    bool          fTextureCreated;
+    SkAutoTUnref<SkShader>     fShader;
+    SkAutoTUnref<SkMaskFilter> fMaskFilter;
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new SamplerStressGM; }
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/shaderbounds.cpp b/gm/shaderbounds.cpp
new file mode 100644
index 0000000..9529151
--- /dev/null
+++ b/gm/shaderbounds.cpp
@@ -0,0 +1,106 @@
+/*
+ * 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 "SkGradientShader.h"
+
+namespace skiagm {
+
+static SkShader* MakeLinear(SkScalar width, SkScalar height, bool alternate) {
+  SkPoint pts[2] = { {0, 0}, {width, height}};
+  SkColor colors[2] = {SK_ColorRED, SK_ColorGREEN};
+  if (alternate) {
+    pts[1].fY = 0;
+    colors[0] = SK_ColorBLUE;
+    colors[1] = SK_ColorYELLOW;
+  }
+  return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
+                                        SkShader::kClamp_TileMode, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class ShaderBoundsGM : public GM {
+public:
+    typedef SkShader* (*ShaderGenFunc)(SkScalar width, SkScalar height,
+                                       bool alternate);
+    ShaderBoundsGM(ShaderGenFunc maker, const SkString& name)
+        : fShaderMaker(maker),
+          fName(name) {
+    }
+
+protected:
+    SkString onShortName() {
+        return fName;
+    }
+
+    virtual SkISize onISize() { return make_isize(320, 240); }
+
+    virtual SkMatrix onGetInitialTransform() const SK_OVERRIDE {
+        SkMatrix result;
+        SkScalar scale = SkFloatToScalar(0.8f);
+        result.setScale(scale, scale);
+        result.postTranslate(SkIntToScalar(7), SkIntToScalar(23));
+        return result;
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        // 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),
+                                        SkIntToScalar(240)));
+
+        SkMatrix canvasScale;
+        SkScalar scale = SkFloatToScalar(0.7f);
+        canvasScale.setScale(scale, scale);
+        canvas->concat(canvasScale);
+
+        // Background shader.
+        SkPaint paint;
+        paint.setShader(MakeShader(559, 387, false))->unref();
+        SkRect r = SkRect::MakeXYWH(SkIntToScalar(-12), SkIntToScalar(-41),
+                                    SkIntToScalar(571), SkIntToScalar(428));
+        canvas->drawRect(r, paint);
+
+        // Constrained shader.
+        paint.setShader(MakeShader(101, 151, true))->unref();
+        r = SkRect::MakeXYWH(SkIntToScalar(43), SkIntToScalar(71),
+                             SkIntToScalar(101), SkIntToScalar(151));
+        canvas->clipRect(r);
+        canvas->drawRect(r, paint);
+    }
+
+    SkShader* MakeShader(int width, int height, bool background) {
+        SkScalar scale = SkFloatToScalar(0.5f);
+        if (background) {
+            scale = SkFloatToScalar(0.6f);
+        }
+        SkScalar shaderWidth = SkScalarDiv(SkIntToScalar(width), scale);
+        SkScalar shaderHeight = SkScalarDiv(SkIntToScalar(height), scale);
+        SkShader* shader = fShaderMaker(shaderWidth, shaderHeight, background);
+        SkMatrix shaderScale;
+        shaderScale.setScale(scale, scale);
+        shader->setLocalMatrix(shaderScale);
+        return shader;
+    }
+
+private:
+    typedef GM INHERITED;
+
+    ShaderGenFunc fShaderMaker;
+    SkString fName;
+
+    SkShader* MakeShader(bool background);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) {
+    return new ShaderBoundsGM(MakeLinear, SkString("shaderbounds_linear"));
+}
+static GMRegistry reg(MyFactory);
+
+}
diff --git a/gm/shadertext.cpp b/gm/shadertext.cpp
index b574ab6..0e092dc 100644
--- a/gm/shadertext.cpp
+++ b/gm/shadertext.cpp
@@ -15,7 +15,7 @@
 static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
     bm->setConfig(config, w, h);
     bm->allocPixels();
-    bm->eraseColor(0);
+    bm->eraseColor(SK_ColorTRANSPARENT);
 
     SkCanvas    canvas(*bm);
     SkScalar    s = SkIntToScalar(SkMin32(w, h));
@@ -36,7 +36,7 @@
     canvas.drawPaint(paint);
 }
 
-SkShader* MakeBitmapShader(SkShader::TileMode tx, SkShader::TileMode ty,
+static SkShader* MakeBitmapShader(SkShader::TileMode tx, SkShader::TileMode ty,
                            int w, int h) {
     static SkBitmap bmp;
     if (bmp.isNull()) {
@@ -109,7 +109,7 @@
 
 class ShaderTextGM : public GM {
 public:
-	ShaderTextGM() {
+    ShaderTextGM() {
         this->setBGColor(0xFFDDDDDD);
     }
 
@@ -119,7 +119,7 @@
         return SkString("shadertext");
     }
 
-	SkISize onISize() { return make_isize(1450, 500); }
+    SkISize onISize() { return make_isize(1450, 500); }
 
     virtual void onDraw(SkCanvas* canvas) {
         const char text[] = "Shaded Text";
@@ -174,8 +174,8 @@
 
         SkPath path;
         path.arcTo(SkRect::MakeXYWH(SkIntToScalar(-40), SkIntToScalar(15),
-                                    SkIntToScalar(300), SkIntToScalar(90)), 
-                                    SkIntToScalar(225), SkIntToScalar(90), 
+                                    SkIntToScalar(300), SkIntToScalar(90)),
+                                    SkIntToScalar(225), SkIntToScalar(90),
                                     false);
         path.close();
 
@@ -208,7 +208,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#ifndef SK_BUILD_FOR_ANDROID
 static GM* MyFactory(void*) { return new ShaderTextGM; }
 static GMRegistry reg(MyFactory);
-
+#endif
 }
diff --git a/gm/shadertext2.cpp b/gm/shadertext2.cpp
new file mode 100644
index 0000000..e760e54
--- /dev/null
+++ b/gm/shadertext2.cpp
@@ -0,0 +1,222 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkUnitMappers.h"
+
+namespace skiagm {
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+    bm->setConfig(config, w, h);
+    bm->allocPixels();
+    bm->eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas    canvas(*bm);
+    SkScalar    s = SkIntToScalar(SkMin32(w, h));
+    static const SkPoint     kPts0[] = { { 0, 0 }, { s, s } };
+    static const SkPoint     kPts1[] = { { s, 0 }, { 0, s } };
+    static const SkScalar    kPos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+    static const SkColor kColors0[] = {0x40FF00FF, 0xF0FFFF00, 0x4000FFFF };
+    static const SkColor kColors1[] = {0xF0FF00FF, 0x80FFFF00, 0xF000FFFF };
+
+
+    SkPaint     paint;
+
+    SkUnitMapper*   um = NULL;
+
+    um = new SkCosineMapper;
+
+    SkAutoUnref au(um);
+
+    paint.setShader(SkGradientShader::CreateLinear(kPts0, kColors0, kPos,
+                    SK_ARRAY_COUNT(kColors0), SkShader::kClamp_TileMode, um))->unref();
+    canvas.drawPaint(paint);
+    paint.setShader(SkGradientShader::CreateLinear(kPts1, kColors1, kPos,
+                    SK_ARRAY_COUNT(kColors1), SkShader::kClamp_TileMode))->unref();
+    canvas.drawPaint(paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct LabeledMatrix {
+    SkMatrix    fMatrix;
+    const char* fLabel;
+};
+
+class ShaderText2GM : public GM {
+public:
+    ShaderText2GM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+
+    SkString onShortName() {
+        return SkString("shadertext2");
+    }
+
+    SkISize onISize() { return make_isize(1800, 900); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        static const char kText[] = "SKIA";
+        static const int kTextLen = SK_ARRAY_COUNT(kText) - 1;
+        static const int kPointSize = 55;
+
+        SkTDArray<LabeledMatrix> matrices;
+        matrices.append()->fMatrix.reset();
+        matrices.top().fLabel = "Identity";
+        matrices.append()->fMatrix.setScale(1.2f, 0.8f);
+        matrices.top().fLabel = "Scale";
+        matrices.append()->fMatrix.setRotate(10.f);
+        matrices.top().fLabel = "Rotate";
+        matrices.append()->fMatrix.reset();
+        matrices.top().fMatrix.setPerspX(-0.0015f);
+        matrices.top().fMatrix.setPerspY(+0.0015f);
+        matrices.top().fLabel = "Persp";
+
+        SkTDArray<LabeledMatrix> localMatrices;
+        localMatrices.append()->fMatrix.reset();
+        localMatrices.top().fLabel = "Identity";
+        localMatrices.append()->fMatrix.setScale(2.5f, 0.2f);
+        localMatrices.top().fLabel = "Scale";
+        localMatrices.append()->fMatrix.setRotate(45.f);
+        localMatrices.top().fLabel = "Rotate";
+        localMatrices.append()->fMatrix.reset();
+        localMatrices.top().fMatrix.setPerspX(-0.007f);
+        localMatrices.top().fMatrix.setPerspY(+0.008f);
+        localMatrices.top().fLabel = "Persp";
+
+        static SkBitmap bmp;
+        if (bmp.isNull()) {
+            makebm(&bmp, SkBitmap::kARGB_8888_Config, kPointSize / 2, kPointSize / 2);
+        }
+
+        SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bmp,
+                                                                   SkShader::kMirror_TileMode,
+                                                                   SkShader::kRepeat_TileMode));
+        SkPaint fillPaint;
+        fillPaint.setAntiAlias(true);
+        fillPaint.setTextSize(SkIntToScalar(kPointSize));
+        fillPaint.setFilterBitmap(true);
+        fillPaint.setShader(shader);
+
+        SkPaint outlinePaint;
+        outlinePaint.setAntiAlias(true);
+        outlinePaint.setTextSize(SkIntToScalar(kPointSize));
+        outlinePaint.setStyle(SkPaint::kStroke_Style);
+        outlinePaint.setStrokeWidth(0.f);
+
+        SkScalar w = fillPaint.measureText(kText, kTextLen);
+        static SkScalar kPadY = 0.5f * kPointSize;
+        static SkScalar kPadX = 1.5f * kPointSize;
+
+        SkPaint strokePaint(fillPaint);
+        strokePaint.setStyle(SkPaint::kStroke_Style);
+        strokePaint.setStrokeWidth(kPointSize * 0.1f);
+
+        SkPaint labelPaint;
+        labelPaint.setColor(0xff000000);
+        labelPaint.setAntiAlias(true);
+        labelPaint.setTextSize(12.f);
+
+        canvas->translate(15.f, 15.f);
+        canvas->drawBitmap(bmp, 0, 0);
+        canvas->translate(0, bmp.height() + labelPaint.getTextSize() + 15.f);
+
+        static const char kLabelLabel[] = "localM / canvasM";
+        canvas->drawText(kLabelLabel, strlen(kLabelLabel), 0, 0, labelPaint);
+        canvas->translate(0, 15.f);
+
+        canvas->save();
+        SkScalar maxLabelW = 0;
+        canvas->translate(0, kPadY / 2 + kPointSize);
+        for (int lm = 0; lm < localMatrices.count(); ++lm) {
+            canvas->drawText(matrices[lm].fLabel, strlen(matrices[lm].fLabel),
+                             0, labelPaint.getTextSize() - 1, labelPaint);
+            SkScalar labelW = labelPaint.measureText(matrices[lm].fLabel,
+                                                     strlen(matrices[lm].fLabel));
+            maxLabelW = SkMaxScalar(maxLabelW, labelW);
+            canvas->translate(0.f, 2 * kPointSize + 2.5f * kPadY);
+        }
+        canvas->restore();
+
+        canvas->translate(maxLabelW + kPadX / 2.f, 0.f);
+
+        for (int s = 0; s < 2; ++s) {
+            SkPaint& paint = s ? strokePaint : fillPaint;
+
+            SkScalar columnH;
+            for (int m = 0; m < matrices.count(); ++m) {
+                columnH = 0;
+                canvas->save();
+                canvas->drawText(matrices[m].fLabel, strlen(matrices[m].fLabel),
+                                 0, labelPaint.getTextSize() - 1, labelPaint);
+                canvas->translate(0, kPadY / 2 + kPointSize);
+                columnH += kPadY / 2 + kPointSize;
+                for (int lm = 0; lm < localMatrices.count(); ++lm) {
+                    shader->setLocalMatrix(localMatrices[lm].fMatrix);
+
+                    canvas->save();
+                        canvas->concat(matrices[m].fMatrix);
+                        canvas->drawText(kText, kTextLen, 0, 0, paint);
+                        canvas->drawText(kText, kTextLen, 0, 0, outlinePaint);
+                    canvas->restore();
+
+                    SkPath path;
+                    path.arcTo(SkRect::MakeXYWH(-0.1f * w, 0.f,
+                                                1.2f * w, 2.f * kPointSize),
+                                                225.f, 359.f,
+                                                false);
+                    path.close();
+
+                    canvas->translate(0.f, kPointSize + kPadY);
+                    columnH += kPointSize + kPadY;
+
+                    canvas->save();
+                        canvas->concat(matrices[m].fMatrix);
+                        canvas->drawTextOnPath(kText, kTextLen, path, NULL, paint);
+                        canvas->drawTextOnPath(kText, kTextLen, path, NULL, outlinePaint);
+                    canvas->restore();
+                    SkPaint stroke;
+                    stroke.setStyle(SkPaint::kStroke_Style);
+                    canvas->translate(0.f, kPointSize + kPadY);
+                    columnH += kPointSize + kPadY;
+                }
+                canvas->restore();
+                canvas->translate(w + kPadX, 0.f);
+            }
+            if (0 == s) {
+                canvas->drawLine(0.f, -kPadY, 0.f, columnH + kPadY, outlinePaint);
+                canvas->translate(kPadX / 2, 0.f);
+                static const char kFillLabel[] = "Filled";
+                static const char kStrokeLabel[] = "Stroked";
+                SkScalar y = columnH + kPadY / 2;
+                SkScalar fillX = -outlinePaint.measureText(kFillLabel, strlen(kFillLabel)) - kPadX;
+                SkScalar strokeX = kPadX;
+                canvas->drawText(kFillLabel, strlen(kFillLabel), fillX, y, labelPaint);
+                canvas->drawText(kStrokeLabel, strlen(kStrokeLabel), strokeX, y, labelPaint);
+            }
+        }
+    }
+
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        // disable 565 for now, til mike fixes the debug assert
+        return this->INHERITED::onGetFlags() | kSkip565_Flag;
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef SK_BUILD_FOR_ANDROID
+static GM* MyFactory(void*) { return new ShaderText2GM; }
+static GMRegistry reg(MyFactory);
+#endif
+}
diff --git a/gm/shadertext3.cpp b/gm/shadertext3.cpp
new file mode 100644
index 0000000..fa327c8
--- /dev/null
+++ b/gm/shadertext3.cpp
@@ -0,0 +1,144 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkGradientShader.h"
+#include "SkUnitMappers.h"
+
+namespace skiagm {
+
+static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
+    bm->setConfig(config, w, h);
+    bm->allocPixels();
+    bm->eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas    canvas(*bm);
+    SkScalar    s = SkIntToScalar(SkMin32(w, h));
+    static const SkPoint     kPts0[] = { { 0, 0 }, { s, s } };
+    static const SkPoint     kPts1[] = { { s/2, 0 }, { s/2, s } };
+    static const SkScalar    kPos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
+    static const SkColor kColors0[] = {0x80F00080, 0xF0F08000, 0x800080F0 };
+    static const SkColor kColors1[] = {0xF08000F0, 0x8080F000, 0xF000F080 };
+
+
+    SkPaint     paint;
+
+    SkUnitMapper*   um = NULL;
+
+    um = new SkCosineMapper;
+
+    SkAutoUnref au(um);
+
+    paint.setShader(SkGradientShader::CreateLinear(kPts0, kColors0, kPos,
+                    SK_ARRAY_COUNT(kColors0), SkShader::kClamp_TileMode, um))->unref();
+    canvas.drawPaint(paint);
+    paint.setShader(SkGradientShader::CreateLinear(kPts1, kColors1, kPos,
+                    SK_ARRAY_COUNT(kColors1), SkShader::kClamp_TileMode))->unref();
+    canvas.drawPaint(paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+struct LabeledMatrix {
+    SkMatrix    fMatrix;
+    const char* fLabel;
+};
+
+class ShaderText3GM : public GM {
+public:
+    ShaderText3GM() {
+        this->setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+
+    SkString onShortName() {
+        return SkString("shadertext3");
+    }
+
+    SkISize onISize() { return make_isize(800, 1000); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        static const char kText[] = "B";
+        static const int kTextLen = SK_ARRAY_COUNT(kText) - 1;
+        static const int kPointSize = 300;
+
+        static SkBitmap bmp;
+        if (bmp.isNull()) {
+            makebm(&bmp, SkBitmap::kARGB_8888_Config, kPointSize / 4, kPointSize / 4);
+        }
+
+        SkPaint bmpPaint;
+        bmpPaint.setAntiAlias(true);
+        bmpPaint.setFilterBitmap(true);
+        bmpPaint.setAlpha(0x80);
+        canvas->drawBitmap(bmp, 5.f, 5.f, &bmpPaint);
+
+        SkPaint outlinePaint;
+        outlinePaint.setAntiAlias(true);
+        outlinePaint.setTextSize(SkIntToScalar(kPointSize));
+        outlinePaint.setStyle(SkPaint::kStroke_Style);
+        outlinePaint.setStrokeWidth(0.f);
+
+        canvas->translate(15.f, 15.f);
+
+        // draw glyphs scaled up
+        canvas->scale(2.f, 2.f);
+
+        static const SkShader::TileMode kTileModes[] = {
+            SkShader::kRepeat_TileMode,
+            SkShader::kMirror_TileMode,
+        };
+
+        // position the baseline of the first run
+        canvas->translate(0.f, 0.75f * kPointSize);
+
+        canvas->save();
+        int i = 0;
+        for (size_t tm0 = 0; tm0 < SK_ARRAY_COUNT(kTileModes); ++tm0) {
+            for (size_t tm1 = 0; tm1 < SK_ARRAY_COUNT(kTileModes); ++tm1) {
+                SkAutoTUnref<SkShader> shader(SkShader::CreateBitmapShader(bmp,
+                                                                           kTileModes[tm0],
+                                                                           kTileModes[tm1]));
+                SkMatrix localM;
+                localM.setTranslate(5.f, 5.f);
+                localM.postRotate(20);
+                localM.postScale(1.15f, .85f);
+                shader->setLocalMatrix(localM);
+
+                SkPaint fillPaint;
+                fillPaint.setAntiAlias(true);
+                fillPaint.setTextSize(SkIntToScalar(kPointSize));
+                fillPaint.setFilterBitmap(true);
+                fillPaint.setShader(shader);
+
+                canvas->drawText(kText, kTextLen, 0, 0, fillPaint);
+                canvas->drawText(kText, kTextLen, 0, 0, outlinePaint);
+                SkScalar w = fillPaint.measureText(kText, kTextLen);
+                canvas->translate(w + 10.f, 0.f);
+                ++i;
+                if (!(i % 2)) {
+                    canvas->restore();
+                    canvas->translate(0, 0.75f * kPointSize);
+                    canvas->save();
+                }
+            }
+        }
+        canvas->restore();
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef SK_BUILD_FOR_ANDROID
+static GM* MyFactory(void*) { return new ShaderText3GM; }
+static GMRegistry reg(MyFactory);
+#endif
+}
diff --git a/gm/shadows.cpp b/gm/shadows.cpp
index d4dd72f..2fb1615 100644
--- a/gm/shadows.cpp
+++ b/gm/shadows.cpp
@@ -40,7 +40,7 @@
     }
 
     virtual SkISize onISize() {
-        return make_isize(200, 80);
+        return make_isize(200, 120);
     }
 
     virtual void onDraw(SkCanvas* canvas) {
@@ -85,6 +85,7 @@
     } gRec[] = {
         { SK_ColorRED,      -SK_Scalar1 },
         { SK_ColorGREEN,    SkIntToScalar(4) },
+        { SK_ColorBLUE,     SkIntToScalar(0)},
     };
 
     SkPaint paint;
@@ -94,13 +95,17 @@
 
         paint.setLooper(shadowLoopers[i]);
 
-        canvas->translate(SkIntToScalar(i*40), SkIntToScalar(0));
+        canvas->translate(SkIntToScalar((unsigned int)i*40), SkIntToScalar(0));
         setup(&paint, gRec[0].fColor, gRec[0].fStrokeWidth);
         canvas->drawRect(fRect, paint);
 
         canvas->translate(SkIntToScalar(0), SkIntToScalar(40));
         setup(&paint, gRec[1].fColor, gRec[1].fStrokeWidth);
         canvas->drawPath(fCirclePath, paint);
+
+        canvas->translate(SkIntToScalar(0), SkIntToScalar(40));
+        setup(&paint, gRec[2].fColor, gRec[2].fStrokeWidth);
+        canvas->drawPath(fCirclePath, paint);
     }
 }
 
diff --git a/gm/shapes.cpp b/gm/shapes.cpp
deleted file mode 100644
index 0f46355..0000000
--- a/gm/shapes.cpp
+++ /dev/null
@@ -1,124 +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 "gm.h"
-#include "SkPicture.h"
-#include "SkRectShape.h"
-#include "SkGroupShape.h"
-
-namespace skiagm {
-
-static SkRect make_rect(int l, int t, int r, int b) {
-    SkRect rect;
-    rect.set(SkIntToScalar(l), SkIntToScalar(t),
-             SkIntToScalar(r), SkIntToScalar(b));
-    return rect;
-}
-
-static SkShape* make_shape0(bool red) {
-    SkRectShape* s = new SkRectShape;
-    s->setRect(make_rect(10, 10, 90, 90));
-    if (red) {
-        s->paint().setColor(SK_ColorRED);
-    }
-    return s;
-}
-
-static SkShape* make_shape1() {
-    SkRectShape* s = new SkRectShape;
-    s->setOval(make_rect(10, 10, 90, 90));
-    s->paint().setColor(SK_ColorBLUE);
-    return s;
-}
-
-static SkShape* make_shape2() {
-    SkRectShape* s = new SkRectShape;
-    s->setRRect(make_rect(10, 10, 90, 90),
-                SkIntToScalar(20), SkIntToScalar(20));
-    s->paint().setColor(SK_ColorGREEN);
-    return s;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class ShapesGM : public GM {
-    SkGroupShape fGroup;
-    SkMatrixRef*    fMatrixRefs[4];
-public:
-	ShapesGM() {
-        this->setBGColor(0xFFDDDDDD);
-        
-        SkMatrix m;
-        fGroup.appendShape(make_shape0(false))->unref();
-        m.setRotate(SkIntToScalar(30), SkIntToScalar(50), SkIntToScalar(50));
-        m.postTranslate(0, SkIntToScalar(120));
-        fGroup.appendShape(make_shape0(true), m)->unref();
-
-        m.setTranslate(SkIntToScalar(120), 0);
-        fGroup.appendShape(make_shape1(), m)->unref();
-        m.postTranslate(0, SkIntToScalar(120));
-        fGroup.appendShape(make_shape2(), m)->unref();
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
-            SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i));
-        }
-        SkScalar c = SkIntToScalar(50);
-        fMatrixRefs[3]->preRotate(SkIntToScalar(30), c, c);
-    }
-
-    virtual ~ShapesGM() {
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
-            SkSafeUnref(fMatrixRefs[i]);
-        }
-    }
-
-protected:
-    virtual SkString onShortName() {
-        return SkString("shapes");
-    }
-
-	virtual SkISize onISize() {
-        return make_isize(380, 480);
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        SkMatrix matrix;
-
-        SkGroupShape* gs = new SkGroupShape;
-        SkAutoUnref aur(gs);
-        gs->appendShape(&fGroup);
-        matrix.setScale(-SK_Scalar1, SK_Scalar1);
-        matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
-        gs->appendShape(&fGroup, matrix);
-        matrix.setTranslate(SkIntToScalar(240), 0);
-        matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
-        gs->appendShape(&fGroup, matrix);
-
-#if 1
-        SkPicture* pict = new SkPicture;
-        SkCanvas* cv = pict->beginRecording(1000, 1000);
-        cv->scale(SK_ScalarHalf, SK_ScalarHalf);
-        gs->draw(cv);
-        cv->translate(SkIntToScalar(680), SkIntToScalar(480));
-        cv->scale(-SK_Scalar1, SK_Scalar1);
-        gs->draw(cv);
-        pict->endRecording();
-        canvas->drawPicture(*pict);
-        pict->unref();
-#endif
-}
-
-private:
-    typedef GM INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static GM* MyFactory(void*) { return new ShapesGM; }
-static GMRegistry reg(MyFactory);
-
-}
diff --git a/gm/simpleaaclip.cpp b/gm/simpleaaclip.cpp
new file mode 100644
index 0000000..885226b
--- /dev/null
+++ b/gm/simpleaaclip.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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 "SkAAClip.h"
+
+namespace skiagm {
+
+static void paint_rgn(SkCanvas* canvas, const SkAAClip& clip,
+                      const SkPaint& paint) {
+    SkMask mask;
+    SkBitmap bm;
+
+    clip.copyToMask(&mask);
+
+    SkAutoMaskFreeImage amfi(mask.fImage);
+
+    bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
+                 mask.fBounds.height(), mask.fRowBytes);
+    bm.setPixels(mask.fImage);
+
+    // need to copy for deferred drawing test to work
+    SkBitmap bm2;
+
+    bm.deepCopyTo(&bm2, SkBitmap::kA8_Config);
+
+    canvas->drawBitmap(bm2,
+                       SK_Scalar1 * mask.fBounds.fLeft,
+                       SK_Scalar1 * mask.fBounds.fTop,
+                       &paint);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/*
+ * This GM tests anti aliased single operation booleans with SkAAClips,
+ * SkRect and SkPaths.
+ */
+class SimpleClipGM : public GM {
+public:
+    enum SkGeomTypes {
+        kRect_GeomType,
+        kPath_GeomType,
+        kAAClip_GeomType
+    };
+
+    SimpleClipGM(SkGeomTypes geomType)
+    : fGeomType(geomType) {
+
+        // offset the rects a bit so we get anti-aliasing in the rect case
+        fBase.set(SkFloatToScalar(100.65f),
+                  SkFloatToScalar(100.65f),
+                  SkFloatToScalar(150.65f),
+                  SkFloatToScalar(150.65f));
+        fRect = fBase;
+        fRect.inset(5, 5);
+        fRect.offset(25, 25);
+
+        fBasePath.addRoundRect(fBase, SkIntToScalar(5), SkIntToScalar(5));
+        fRectPath.addRoundRect(fRect, SkIntToScalar(5), SkIntToScalar(5));
+        INHERITED::setBGColor(0xFFDDDDDD);
+    }
+
+protected:
+    void buildRgn(SkAAClip* clip, SkRegion::Op op) {
+        clip->setPath(fBasePath, NULL, true);
+
+        SkAAClip clip2;
+        clip2.setPath(fRectPath, NULL, true);
+        clip->op(clip2, op);
+    }
+
+    void drawOrig(SkCanvas* canvas) {
+        SkPaint     paint;
+
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setColor(SK_ColorBLACK);
+
+        canvas->drawRect(fBase, paint);
+        canvas->drawRect(fRect, paint);
+    }
+
+    void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
+
+        SkAAClip clip;
+
+        this->buildRgn(&clip, op);
+        this->drawOrig(canvas);
+
+        SkPaint paint;
+        paint.setColor(color);
+        paint_rgn(canvas, clip, paint);
+    }
+
+    void drawPathsOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
+
+        this->drawOrig(canvas);
+
+        canvas->save();
+
+        // create the clip mask with the supplied boolean op
+        if (kPath_GeomType == fGeomType) {
+            // path-based case
+            canvas->clipPath(fBasePath, SkRegion::kReplace_Op, true);
+            canvas->clipPath(fRectPath, op, true);
+        } else {
+            // rect-based case
+            canvas->clipRect(fBase, SkRegion::kReplace_Op, true);
+            canvas->clipRect(fRect, op, true);
+        }
+
+        // draw a rect that will entirely cover the clip mask area
+        SkPaint paint;
+        paint.setColor(color);
+
+        SkRect r = SkRect::MakeLTRB(SkIntToScalar(90),  SkIntToScalar(90),
+                                    SkIntToScalar(180), SkIntToScalar(180));
+
+        canvas->drawRect(r, paint);
+
+        canvas->restore();
+    }
+
+    virtual SkString onShortName() {
+        SkString str;
+        str.printf("simpleaaclip_%s",
+                    kRect_GeomType == fGeomType ? "rect" :
+                    (kPath_GeomType == fGeomType ? "path" :
+                    "aaclip"));
+        return str;
+    }
+
+    virtual SkISize onISize() {
+        return make_isize(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+
+        static const struct {
+            SkColor         fColor;
+            const char*     fName;
+            SkRegion::Op    fOp;
+        } gOps[] = {
+            { SK_ColorBLACK,    "Difference", SkRegion::kDifference_Op    },
+            { SK_ColorRED,      "Intersect",  SkRegion::kIntersect_Op     },
+            { 0xFF008800,       "Union",      SkRegion::kUnion_Op         },
+            { SK_ColorGREEN,    "Rev Diff",   SkRegion::kReverseDifference_Op },
+            { SK_ColorYELLOW,   "Replace",    SkRegion::kReplace_Op       },
+            { SK_ColorBLUE,     "XOR",        SkRegion::kXOR_Op           },
+        };
+
+        SkPaint textPaint;
+        textPaint.setAntiAlias(true);
+        textPaint.setTextSize(SK_Scalar1*24);
+        int xOff = 0;
+
+        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
+            canvas->drawText(gOps[op].fName, strlen(gOps[op].fName),
+                             SkIntToScalar(75), SkIntToScalar(50),
+                             textPaint);
+
+            if (kAAClip_GeomType == fGeomType) {
+                this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
+            } else {
+                this->drawPathsOped(canvas, gOps[op].fOp, gOps[op].fColor);
+            }
+
+            if (xOff >= 400) {
+                canvas->translate(SkIntToScalar(-400), SkIntToScalar(250));
+                xOff = 0;
+            } else {
+                canvas->translate(SkIntToScalar(200), 0);
+                xOff += 200;
+            }
+        }
+    }
+private:
+
+    SkGeomTypes fGeomType;
+
+    SkRect fBase;
+    SkRect fRect;
+
+    SkPath fBasePath;       // fBase as a round rect
+    SkPath fRectPath;       // fRect as a round rect
+
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+// rects
+DEF_GM( return new SimpleClipGM(SimpleClipGM::kRect_GeomType); )
+DEF_GM( return new SimpleClipGM(SimpleClipGM::kPath_GeomType); )
+DEF_GM( return new SimpleClipGM(SimpleClipGM::kAAClip_GeomType); )
+
+}
diff --git a/gm/spritebitmap.cpp b/gm/spritebitmap.cpp
new file mode 100644
index 0000000..8aaa6ea
--- /dev/null
+++ b/gm/spritebitmap.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gm.h"
+#include "SkCanvas.h"
+#include "SkBlurImageFilter.h"
+
+static void make_bm(SkBitmap* bm) {
+    bm->setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    bm->allocPixels();
+    bm->eraseColor(SK_ColorBLUE);
+
+    SkCanvas canvas(*bm);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setColor(SK_ColorRED);
+    canvas.drawCircle(50, 50, 50, paint);
+}
+
+static void draw_2_bitmaps(SkCanvas* canvas, const SkBitmap& bm, bool doClip,
+                           int dx, int dy, SkImageFilter* filter = NULL) {
+    SkAutoCanvasRestore acr(canvas, true);
+    SkPaint paint;
+
+    SkRect clipR = SkRect::MakeXYWH(SkIntToScalar(dx),
+                                    SkIntToScalar(dy),
+                                    SkIntToScalar(bm.width()),
+                                    SkIntToScalar(bm.height()));
+
+    paint.setImageFilter(filter);
+    clipR.inset(5, 5);
+
+    if (doClip) {
+        canvas->save();
+        canvas->clipRect(clipR);
+    }
+    canvas->drawSprite(bm, dx, dy, &paint);
+    if (doClip) {
+        canvas->restore();
+    }
+
+    canvas->translate(SkIntToScalar(bm.width() + 20), 0);
+
+    if (doClip) {
+        canvas->save();
+        canvas->clipRect(clipR);
+    }
+    canvas->drawBitmap(bm, SkIntToScalar(dx), SkIntToScalar(dy), &paint);
+    if (doClip) {
+        canvas->restore();
+    }
+}
+
+/**
+ *  Compare output of drawSprite and drawBitmap (esp. clipping and imagefilters)
+ */
+class SpriteBitmapGM : public skiagm::GM {
+public:
+    SpriteBitmapGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("spritebitmap");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkBitmap bm;
+        make_bm(&bm);
+
+        int dx = 10;
+        int dy = 10;
+
+        SkScalar sigma = 8;
+        SkAutoTUnref<SkImageFilter> filter(new SkBlurImageFilter(sigma, sigma));
+
+        draw_2_bitmaps(canvas, bm, false, dx, dy);
+        dy += bm.height() + 20;
+        draw_2_bitmaps(canvas, bm, false, dx, dy, filter);
+        dy += bm.height() + 20;
+        draw_2_bitmaps(canvas, bm, true, dx, dy);
+        dy += bm.height() + 20;
+        draw_2_bitmaps(canvas, bm, true, dx, dy, filter);
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+DEF_GM( return new SpriteBitmapGM; )
diff --git a/gm/srcmode.cpp b/gm/srcmode.cpp
new file mode 100644
index 0000000..66729e2
--- /dev/null
+++ b/gm/srcmode.cpp
@@ -0,0 +1,151 @@
+/*
+ * 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 "SkGradientShader.h"
+#include "SkSurface.h"
+
+#if SK_SUPPORT_GPU
+    #include "SkGpuDevice.h"
+#endif
+
+#define W   SkIntToScalar(80)
+#define H   SkIntToScalar(60)
+
+typedef void (*PaintProc)(SkPaint*);
+
+static void identity_paintproc(SkPaint* paint) {
+    paint->setShader(NULL);
+}
+
+static void gradient_paintproc(SkPaint* paint) {
+    const SkColor colors[] = { SK_ColorGREEN, SK_ColorBLUE };
+    const SkPoint pts[] = { { 0, 0 }, { W, H } };
+    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL,
+                                                 SK_ARRAY_COUNT(colors),
+                                                 SkShader::kClamp_TileMode);
+    paint->setShader(s)->unref();
+}
+
+typedef void (*Proc)(SkCanvas*, const SkPaint&);
+
+static void draw_hair(SkCanvas* canvas, const SkPaint& paint) {
+    SkPaint p(paint);
+    p.setStrokeWidth(0);
+    canvas->drawLine(0, 0, W, H, p);
+}
+
+static void draw_thick(SkCanvas* canvas, const SkPaint& paint) {
+    SkPaint p(paint);
+    p.setStrokeWidth(H/5);
+    canvas->drawLine(0, 0, W, H, p);
+}
+
+static void draw_rect(SkCanvas* canvas, const SkPaint& paint) {
+    canvas->drawRect(SkRect::MakeWH(W, H), paint);
+}
+
+static void draw_oval(SkCanvas* canvas, const SkPaint& paint) {
+    canvas->drawOval(SkRect::MakeWH(W, H), paint);
+}
+
+static void draw_text(SkCanvas* canvas, const SkPaint& paint) {
+    SkPaint p(paint);
+    p.setTextSize(H/4);
+    canvas->drawText("Hamburge", 8, 0, H*2/3, p);
+}
+
+class SrcModeGM : public skiagm::GM {
+    SkPath fPath;
+public:
+    SrcModeGM() {
+        this->setBGColor(SK_ColorBLACK);
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("srcmode");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 760);
+    }
+
+    void drawContent(SkCanvas* canvas) {
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+        SkPaint paint;
+        paint.setColor(0x80FF0000);
+
+        const Proc procs[] = {
+            draw_hair, draw_thick, draw_rect, draw_oval, draw_text
+        };
+
+        const SkXfermode::Mode modes[] = {
+            SkXfermode::kSrcOver_Mode, SkXfermode::kSrc_Mode, SkXfermode::kClear_Mode
+        };
+
+        const PaintProc paintProcs[] = {
+            identity_paintproc, gradient_paintproc
+        };
+
+        for (int aa = 0; aa <= 1; ++aa) {
+            paint.setAntiAlias(SkToBool(aa));
+            canvas->save();
+            for (size_t i = 0; i < SK_ARRAY_COUNT(paintProcs); ++i) {
+                paintProcs[i](&paint);
+                for (size_t x = 0; x < SK_ARRAY_COUNT(modes); ++x) {
+                    paint.setXfermodeMode(modes[x]);
+                    canvas->save();
+                    for (size_t y = 0; y < SK_ARRAY_COUNT(procs); ++y) {
+                        procs[y](canvas, paint);
+                        canvas->translate(0, H * 5 / 4);
+                    }
+                    canvas->restore();
+                    canvas->translate(W * 5 / 4, 0);
+                }
+            }
+            canvas->restore();
+            canvas->translate(0, (H * 5 / 4) * SK_ARRAY_COUNT(procs));
+        }
+    }
+
+    static SkSurface* compat_surface(SkCanvas* canvas, const SkISize& size,
+                                     bool skipGPU) {
+        SkImage::Info info = {
+            size.width(),
+            size.height(),
+            SkImage::kPMColor_ColorType,
+            SkImage::kPremul_AlphaType
+        };
+#if SK_SUPPORT_GPU
+        SkDevice* dev = canvas->getDevice();
+        if (!skipGPU && dev->accessRenderTarget()) {
+            SkGpuDevice* gd = (SkGpuDevice*)dev;
+            GrContext* ctx = gd->context();
+            return SkSurface::NewRenderTarget(ctx, info, 0);
+        }
+#endif
+        return SkSurface::NewRaster(info);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkAutoTUnref<SkSurface> surf(compat_surface(canvas, this->getISize(),
+                                                    this->isCanvasDeferred()));
+        surf->getCanvas()->drawColor(SK_ColorWHITE);
+        this->drawContent(surf->getCanvas());
+        surf->draw(canvas, 0, 0, NULL);
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return new SrcModeGM;)
diff --git a/gm/strokefill.cpp b/gm/strokefill.cpp
index 75fa008..445de81 100644
--- a/gm/strokefill.cpp
+++ b/gm/strokefill.cpp
@@ -27,9 +27,8 @@
         return make_isize(640, 480);
     }
 
-    static void show_bold(SkCanvas* canvas, const char text[], SkScalar x,
-                          SkScalar y, const SkPaint& paint) {
-        size_t len = strlen(text);
+    static void show_bold(SkCanvas* canvas, const void* text, int len,
+                          SkScalar x, SkScalar y, const SkPaint& paint) {
         SkPaint p(paint);
         canvas->drawText(text, len, x, y, p);
         p.setFakeBoldText(true);
@@ -44,15 +43,15 @@
         paint.setAntiAlias(true);
         paint.setTextSize(SkIntToScalar(100));
         paint.setStrokeWidth(SkIntToScalar(5));
-        
+
         SkTypeface* face = SkTypeface::CreateFromName("Papyrus", SkTypeface::kNormal);
         SkSafeUnref(paint.setTypeface(face));
-        show_bold(canvas, "Hello", x, y, paint);
+        show_bold(canvas, "Hello", 5, x, y, paint);
 
         face = SkTypeface::CreateFromName("Hiragino Maru Gothic Pro", SkTypeface::kNormal);
         SkSafeUnref(paint.setTypeface(face));
-        const char hyphen[] = { 0xE3, 0x83, 0xBC, 0 };
-        show_bold(canvas, hyphen, x + SkIntToScalar(300), y, paint);
+        const unsigned char hyphen[] = { 0xE3, 0x83, 0xBC };
+        show_bold(canvas, hyphen, SK_ARRAY_COUNT(hyphen), x + SkIntToScalar(300), y, paint);
 
         paint.setStyle(SkPaint::kStrokeAndFill_Style);
 
@@ -61,18 +60,18 @@
         path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction);
         path.addCircle(x, y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCCW_Direction);
         canvas->drawPath(path, paint);
-        
+
         SkPath path2;
         path2.setFillType(SkPath::kWinding_FillType);
         path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction);
         path2.addCircle(x + SkIntToScalar(120), y + SkIntToScalar(200), SkIntToScalar(40), SkPath::kCW_Direction);
         canvas->drawPath(path2, paint);
-        
+
         path2.reset();
         path2.addCircle(x + SkIntToScalar(240), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCCW_Direction);
         canvas->drawPath(path2, paint);
         SkASSERT(path2.cheapIsDirection(SkPath::kCCW_Direction));
-        
+
         path2.reset();
         SkASSERT(!path2.cheapComputeDirection(NULL));
         path2.addCircle(x + SkIntToScalar(360), y + SkIntToScalar(200), SkIntToScalar(50), SkPath::kCW_Direction);
diff --git a/gm/strokerect.cpp b/gm/strokerect.cpp
new file mode 100644
index 0000000..489f7b4
--- /dev/null
+++ b/gm/strokerect.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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 "SkPath.h"
+
+#define STROKE_WIDTH    SkIntToScalar(20)
+
+static void draw_path(SkCanvas* canvas, const SkPath& path, const SkRect& rect,
+                      SkPaint::Join join, int doFill) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setStyle(doFill ? SkPaint::kStrokeAndFill_Style : SkPaint::kStroke_Style);
+
+    paint.setColor(SK_ColorGRAY);
+    paint.setStrokeWidth(STROKE_WIDTH);
+    paint.setStrokeJoin(join);
+    canvas->drawRect(rect, paint);
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(0);
+    paint.setColor(SK_ColorRED);
+    canvas->drawPath(path, paint);
+
+    paint.setStrokeWidth(3);
+    paint.setStrokeJoin(SkPaint::kMiter_Join);
+    int n = path.countPoints();
+    SkAutoTArray<SkPoint> points(n);
+    path.getPoints(points.get(), n);
+    canvas->drawPoints(SkCanvas::kPoints_PointMode, n, points.get(), paint);
+}
+
+/*
+ *  Test calling SkStroker for rectangles. Cases to cover:
+ *
+ *  geometry: normal, small (smaller than stroke-width), empty, inverted
+ *  joint-type for the corners
+ */
+class StrokeRectGM : public skiagm::GM {
+public:
+    StrokeRectGM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("strokerect");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(1024, 740);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->drawColor(SK_ColorWHITE);
+        canvas->translate(STROKE_WIDTH*3/2, STROKE_WIDTH*3/2);
+
+        SkPaint paint;
+        paint.setStyle(SkPaint::kStroke_Style);
+        paint.setStrokeWidth(STROKE_WIDTH);
+
+        static const SkPaint::Join gJoins[] = {
+            SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
+        };
+
+        static const SkScalar W = 80;
+        static const SkScalar H = 80;
+        static const SkRect gRects[] = {
+            { 0, 0, W, H },
+            { W, 0, 0, H },
+            { 0, H, W, 0 },
+            { 0, 0, STROKE_WIDTH, H },
+            { 0, 0, W, STROKE_WIDTH },
+            { 0, 0, STROKE_WIDTH/2, STROKE_WIDTH/2 },
+            { 0, 0, W, 0 },
+            { 0, 0, 0, H },
+            { 0, 0, 0, 0 },
+        };
+
+        for (int doFill = 0; doFill <= 1; ++doFill) {
+            for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); ++i) {
+                SkPaint::Join join = gJoins[i];
+                paint.setStrokeJoin(join);
+
+                SkAutoCanvasRestore acr(canvas, true);
+                for (size_t j = 0; j < SK_ARRAY_COUNT(gRects); ++j) {
+                    const SkRect& r = gRects[j];
+
+                    SkPath path, fillPath;
+                    path.addRect(r);
+                    paint.getFillPath(path, &fillPath);
+                    draw_path(canvas, fillPath, r, join, doFill);
+
+                    canvas->translate(W + 2 * STROKE_WIDTH, 0);
+                }
+                acr.restore();
+                canvas->translate(0, H + 2 * STROKE_WIDTH);
+            }
+            paint.setStyle(SkPaint::kStrokeAndFill_Style);
+        }
+    }
+
+private:
+    typedef GM INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+DEF_GM(return new StrokeRectGM;)
diff --git a/gm/strokerects.cpp b/gm/strokerects.cpp
index 92f2a18..61ee9e0 100644
--- a/gm/strokerects.cpp
+++ b/gm/strokerects.cpp
@@ -23,7 +23,7 @@
 class StrokeRectGM : public GM {
 public:
     StrokeRectGM() {}
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("strokerects");
@@ -40,7 +40,7 @@
         SkScalar h = rand.nextUScalar1() * (H >> 2);
         SkScalar hoffset = rand.nextSScalar1();
         SkScalar woffset = rand.nextSScalar1();
-        
+
         r->set(x, y, x + w, y + h);
         r->offset(-w/2 + woffset, -h/2 + hoffset);
     }
@@ -70,7 +70,7 @@
             }
         }
     }
-    
+
 private:
     typedef GM INHERITED;
 };
@@ -81,4 +81,3 @@
 static GMRegistry reg(MyFactory);
 
 }
-
diff --git a/gm/strokes.cpp b/gm/strokes.cpp
index ec265ad..49a7fc6 100644
--- a/gm/strokes.cpp
+++ b/gm/strokes.cpp
@@ -11,8 +11,6 @@
 #include "gm.h"
 #include "SkRandom.h"
 
-namespace skiagm {
-
 #define W   400
 #define H   400
 #define N   50
@@ -27,33 +25,33 @@
     SkScalar h = rand.nextUScalar1() * (H >> 2);
     SkScalar hoffset = rand.nextSScalar1();
     SkScalar woffset = rand.nextSScalar1();
-    
+
     r->set(x, y, x + w, y + h);
     r->offset(-w/2 + woffset, -h/2 + hoffset);
-    
+
     paint->setColor(rand.nextU());
     paint->setAlpha(0xFF);
 }
 
-    
-class StrokesGM : public GM {
+
+class StrokesGM : public skiagm::GM {
 public:
     StrokesGM() {}
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("strokes_round");
     }
-    
+
     virtual SkISize onISize() {
-        return make_isize(W, H*2);
+        return SkISize::Make(W, H*2);
     }
-    
-    virtual void onDraw(SkCanvas* canvas) {        
+
+    virtual void onDraw(SkCanvas* canvas) {
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setStrokeWidth(SkIntToScalar(9)/2);
-        
+
         for (int y = 0; y < 2; y++) {
             paint.setAntiAlias(!!y);
             SkAutoCanvasRestore acr(canvas, true);
@@ -62,7 +60,7 @@
                                               SkIntToScalar(2), SkIntToScalar(2)
                                               , SW - SkIntToScalar(2), SH - SkIntToScalar(2)
                                               ));
-            
+
             SkRandom rand;
             for (int i = 0; i < N; i++) {
                 SkRect r;
@@ -74,12 +72,12 @@
             }
         }
     }
-    
+
 private:
-    typedef GM INHERITED;
+    typedef skiagm::GM INHERITED;
 };
 
-class Strokes2GM : public GM {
+class Strokes2GM : public skiagm::GM {
     SkPath fPath;
 public:
     Strokes2GM() {
@@ -91,16 +89,16 @@
             fPath.lineTo(x, y);
         }
     }
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("strokes_poly");
     }
-    
+
     virtual SkISize onISize() {
-        return make_isize(W, H*2);
+        return SkISize::Make(W, H*2);
     }
-    
+
     static void rotate(SkScalar angle, SkScalar px, SkScalar py, SkCanvas* canvas) {
         SkMatrix matrix;
         matrix.setRotate(angle, px, py);
@@ -109,11 +107,11 @@
 
     virtual void onDraw(SkCanvas* canvas) {
         canvas->drawColor(SK_ColorWHITE);
-        
+
         SkPaint paint;
         paint.setStyle(SkPaint::kStroke_Style);
         paint.setStrokeWidth(SkIntToScalar(9)/2);
-        
+
         for (int y = 0; y < 2; y++) {
             paint.setAntiAlias(!!y);
             SkAutoCanvasRestore acr(canvas, true);
@@ -122,7 +120,7 @@
                                               SkIntToScalar(2),
                                               SW - SkIntToScalar(2),
                                               SH - SkIntToScalar(2)));
-            
+
             SkRandom rand;
             for (int i = 0; i < N/2; i++) {
                 SkRect r;
@@ -132,18 +130,121 @@
             }
         }
     }
-    
+
 private:
-    typedef GM INHERITED;
+    typedef skiagm::GM INHERITED;
 };
 
 //////////////////////////////////////////////////////////////////////////////
 
-static GM* MyFactory(void*) { return new StrokesGM; }
-static GMRegistry reg(MyFactory);
-
-static GM* MyFactory2(void*) { return new Strokes2GM; }
-static GMRegistry reg2(MyFactory2);
-
+static SkRect inset(const SkRect& r) {
+    SkRect rr(r);
+    rr.inset(r.width()/10, r.height()/10);
+    return rr;
 }
 
+class Strokes3GM : public skiagm::GM {
+    static void make0(SkPath* path, const SkRect& bounds, SkString* title) {
+        path->addRect(bounds, SkPath::kCW_Direction);
+        path->addRect(inset(bounds), SkPath::kCW_Direction);
+        title->set("CW CW");
+    }
+
+    static void make1(SkPath* path, const SkRect& bounds, SkString* title) {
+        path->addRect(bounds, SkPath::kCW_Direction);
+        path->addRect(inset(bounds), SkPath::kCCW_Direction);
+        title->set("CW CCW");
+    }
+
+    static void make2(SkPath* path, const SkRect& bounds, SkString* title) {
+        path->addOval(bounds, SkPath::kCW_Direction);
+        path->addOval(inset(bounds), SkPath::kCW_Direction);
+        title->set("CW CW");
+    }
+
+    static void make3(SkPath* path, const SkRect& bounds, SkString* title) {
+        path->addOval(bounds, SkPath::kCW_Direction);
+        path->addOval(inset(bounds), SkPath::kCCW_Direction);
+        title->set("CW CCW");
+    }
+
+    static void make4(SkPath* path, const SkRect& bounds, SkString* title) {
+        path->addRect(bounds, SkPath::kCW_Direction);
+        SkRect r = bounds;
+        r.inset(bounds.width() / 10, -bounds.height() / 10);
+        path->addOval(r, SkPath::kCW_Direction);
+        title->set("CW CW");
+    }
+
+    static void make5(SkPath* path, const SkRect& bounds, SkString* title) {
+        path->addRect(bounds, SkPath::kCW_Direction);
+        SkRect r = bounds;
+        r.inset(bounds.width() / 10, -bounds.height() / 10);
+        path->addOval(r, SkPath::kCCW_Direction);
+        title->set("CW CCW");
+    }
+
+public:
+    Strokes3GM() {}
+
+protected:
+    virtual SkString onShortName() {
+        return SkString("strokes3");
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(W, H*2);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkPaint origPaint;
+        origPaint.setAntiAlias(true);
+        origPaint.setStyle(SkPaint::kStroke_Style);
+        SkPaint fillPaint(origPaint);
+        fillPaint.setColor(SK_ColorRED);
+        SkPaint strokePaint(origPaint);
+        strokePaint.setColor(0xFF4444FF);
+
+        void (*procs[])(SkPath*, const SkRect&, SkString*) = {
+            make0, make1, make2, make3, make4, make5
+        };
+
+        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
+
+        SkRect bounds = SkRect::MakeWH(SkIntToScalar(50), SkIntToScalar(50));
+        SkScalar dx = bounds.width() * 4/3;
+        SkScalar dy = bounds.height() * 5;
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(procs); ++i) {
+            SkPath orig;
+            SkString str;
+            procs[i](&orig, bounds, &str);
+
+            canvas->save();
+            for (int j = 0; j < 13; ++j) {
+                strokePaint.setStrokeWidth(SK_Scalar1 * j * j);
+                canvas->drawPath(orig, strokePaint);
+                canvas->drawPath(orig, origPaint);
+                SkPath fill;
+                strokePaint.getFillPath(orig, &fill);
+                canvas->drawPath(fill, fillPaint);
+                canvas->translate(dx + strokePaint.getStrokeWidth(), 0);
+            }
+            canvas->restore();
+            canvas->translate(0, dy);
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* F0(void*) { return new StrokesGM; }
+static skiagm::GM* F1(void*) { return new Strokes2GM; }
+static skiagm::GM* F2(void*) { return new Strokes3GM; }
+
+static skiagm::GMRegistry R0(F0);
+static skiagm::GMRegistry R1(F1);
+static skiagm::GMRegistry R2(F2);
diff --git a/gm/system_preferences_mac.mm b/gm/system_preferences_mac.mm
index 2c772a1..ee6ab19 100644
--- a/gm/system_preferences_mac.mm
+++ b/gm/system_preferences_mac.mm
@@ -5,6 +5,8 @@
  * found in the LICENSE file.
  */
 
+#include "system_preferences.h"
+
 #import <Cocoa/Cocoa.h>
 
 void setSystemPreferences() {
@@ -13,10 +15,12 @@
     // Set LCD font smoothing level for this application (does not affect other
     // applications). Based on resetDefaultsToConsistentValues() in
     // http://trac.webkit.org/browser/trunk/Tools/DumpRenderTree/mac/DumpRenderTree.mm
-    static const int NoFontSmoothing     = 0;
-    static const int LightFontSmoothing  = 1;
-    static const int MediumFontSmoothing = 2;
-    static const int StrongFontSmoothing = 3;
+    enum {
+        NoFontSmoothing     = 0,
+        LightFontSmoothing  = 1,
+        MediumFontSmoothing = 2,
+        StrongFontSmoothing = 3,
+    };
     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
     [defaults setInteger:MediumFontSmoothing forKey:@"AppleFontSmoothing"];
 
diff --git a/gm/tablecolorfilter.cpp b/gm/tablecolorfilter.cpp
index df33337..49e342d 100644
--- a/gm/tablecolorfilter.cpp
+++ b/gm/tablecolorfilter.cpp
@@ -15,8 +15,8 @@
     int H = 120;
     bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
     bm->allocPixels();
-    bm->eraseColor(0);
-    
+    bm->eraseColor(SK_ColorTRANSPARENT);
+
     SkCanvas canvas(*bm);
     SkPaint paint;
     SkPoint pts[] = { {0, 0}, {SkIntToScalar(W), SkIntToScalar(H)} };
@@ -34,8 +34,8 @@
     int H = 120;
     bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
     bm->allocPixels();
-    bm->eraseColor(0);
-    
+    bm->eraseColor(SK_ColorTRANSPARENT);
+
     SkCanvas canvas(*bm);
     SkPaint paint;
     SkScalar cx = SkIntToScalar(W)/2;
@@ -66,7 +66,7 @@
 static void make_table2(uint8_t table[]) {
     for (int i = 0; i < 256; ++i) {
         float fi = i / 255.0f;
-        table[i] = sqrtf(fi) * 255;
+        table[i] = static_cast<uint8_t>(sqrtf(fi) * 255);
     }
 }
 
@@ -92,27 +92,27 @@
 class TableColorFilterGM : public skiagm::GM {
 public:
     TableColorFilterGM() {}
-    
+
 protected:
     virtual SkString onShortName() {
         return SkString("tablecolorfilter");
     }
-    
+
     virtual SkISize onISize() {
-        return SkISize::Make(640, 480);
+        return SkISize::Make(700, 300);
     }
-    
+
     virtual void onDraw(SkCanvas* canvas) {
         canvas->drawColor(0xFFDDDDDD);
         canvas->translate(20, 20);
-        
+
         SkScalar x = 0, y = 0;
-        
+
         static void (*gMakers[])(SkBitmap*) = { make_bm0, make_bm1 };
         for (size_t maker = 0; maker < SK_ARRAY_COUNT(gMakers); ++maker) {
             SkBitmap bm;
             gMakers[maker](&bm);
-            
+
             SkPaint paint;
             x = 0;
             canvas->drawBitmap(bm, x, y, &paint);
@@ -124,11 +124,11 @@
             canvas->drawBitmap(bm, x, y, &paint);
             paint.setColorFilter(make_cf3())->unref();  x += bm.width() * 9 / 8;
             canvas->drawBitmap(bm, x, y, &paint);
-            
+
             y += bm.height() * 9 / 8;
         }
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/techtalk1.cpp b/gm/techtalk1.cpp
new file mode 100644
index 0000000..442d73b
--- /dev/null
+++ b/gm/techtalk1.cpp
@@ -0,0 +1,397 @@
+
+/*
+ * 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 "SkColorPriv.h"
+#include "SkGeometry.h"
+#include "SkShader.h"
+
+#define WIRE_FRAME_WIDTH    1.1f
+
+static void tesselate(const SkPath& src, SkPath* dst) {
+    SkPath::Iter iter(src, true);
+    SkPoint pts[4];
+    SkPath::Verb verb;
+    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                dst->moveTo(pts[0]);
+                break;
+            case SkPath::kLine_Verb:
+                dst->lineTo(pts[1]);
+                break;
+            case SkPath::kQuad_Verb: {
+                SkPoint p;
+                for (int i = 1; i <= 8; ++i) {
+                    SkEvalQuadAt(pts, i / 8.0f, &p, NULL);
+                    dst->lineTo(p);
+                }
+            } break;
+            case SkPath::kCubic_Verb: {
+                SkPoint p;
+                for (int i = 1; i <= 8; ++i) {
+                    SkEvalCubicAt(pts, i / 8.0f, &p, NULL, NULL);
+                    dst->lineTo(p);
+                }
+            } break;
+        }
+    }
+}
+
+static void setFade(SkPaint* paint, bool showGL) {
+    paint->setAlpha(showGL ? 0x66 : 0xFF);
+}
+
+static void setGLFrame(SkPaint* paint) {
+    paint->setColor(0xFFFF0000);
+    paint->setStyle(SkPaint::kStroke_Style);
+    paint->setAntiAlias(true);
+    paint->setStrokeWidth(WIRE_FRAME_WIDTH);
+}
+
+static void show_mesh(SkCanvas* canvas, const SkRect& r) {
+    SkPaint paint;
+    setGLFrame(&paint);
+
+    canvas->drawRect(r, paint);
+    canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, paint);
+}
+
+static void drawLine(SkCanvas* canvas, const SkPoint& p0, const SkPoint& p1,
+                     const SkPaint& paint) {
+    canvas->drawLine(p0.fX, p0.fY, p1.fX, p1.fY, paint);
+}
+
+static void show_mesh(SkCanvas* canvas, const SkPoint pts[],
+                      const uint16_t indices[], int count) {
+    SkPaint paint;
+    setGLFrame(&paint);
+
+    for (int i = 0; i < count - 2; ++i) {
+        drawLine(canvas, pts[indices[i]], pts[indices[i+1]], paint);
+        drawLine(canvas, pts[indices[i+1]], pts[indices[i+2]], paint);
+        drawLine(canvas, pts[indices[i+2]], pts[indices[i]], paint);
+    }
+}
+
+static void show_glframe(SkCanvas* canvas, const SkPath& path) {
+    SkPaint paint;
+    setGLFrame(&paint);
+    canvas->drawPath(path, paint);
+}
+
+static void show_mesh_between(SkCanvas* canvas, const SkPath& p0, const SkPath& p1) {
+    SkPath d0, d1;
+    tesselate(p0, &d0);
+    tesselate(p1, &d1);
+
+    SkPoint pts0[256*2], pts1[256];
+    int count = d0.getPoints(pts0, SK_ARRAY_COUNT(pts0));
+    int count1 = d1.getPoints(pts1, SK_ARRAY_COUNT(pts1));
+    SkASSERT(count == count1);
+    memcpy(&pts0[count], pts1, count * sizeof(SkPoint));
+
+    uint16_t indices[256*6];
+    uint16_t* ndx = indices;
+    for (int i = 0; i < count; ++i) {
+        *ndx++ = i;
+        *ndx++ = i + count;
+    }
+    *ndx++ = 0;
+
+    show_mesh(canvas, pts0, indices, ndx - indices);
+}
+
+static void show_fan(SkCanvas* canvas, const SkPath& path, SkScalar cx, SkScalar cy) {
+    SkPaint paint;
+    setGLFrame(&paint);
+
+    canvas->drawPath(path, paint);
+
+    SkPoint pts[256];
+    int count = path.getPoints(pts, SK_ARRAY_COUNT(pts));
+    for (int i = 0; i < count; ++i) {
+        canvas->drawLine(pts[i].fX, pts[i].fY, cx, cy, paint);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef void (*DrawProc)(SkCanvas* canvas, bool showGL, int flags);
+
+static void draw_line(SkCanvas* canvas, bool showGL, int flags) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    if (showGL) {
+        setGLFrame(&paint);
+    }
+    canvas->drawLine(50, 50, 400, 100, paint);
+    paint.setColor(SK_ColorBLACK);
+
+    canvas->rotate(40);
+    setFade(&paint, showGL);
+    paint.setStrokeWidth(40);
+    canvas->drawLine(100, 50, 450, 50, paint);
+    if (showGL) {
+        show_mesh(canvas, SkRect::MakeLTRB(100, 50-20, 450, 50+20));
+    }
+}
+
+static void draw_rect(SkCanvas* canvas, bool showGL, int flags) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);
+
+    setFade(&paint, showGL);
+    canvas->drawRect(r, paint);
+    if (showGL) {
+        show_mesh(canvas, r);
+    }
+
+    canvas->translate(320, 0);
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(25);
+    canvas->drawRect(r, paint);
+    if (showGL) {
+        SkScalar rad = paint.getStrokeWidth() / 2;
+        SkPoint pts[8];
+        r.outset(rad, rad);
+        r.toQuad(&pts[0]);
+        r.inset(rad*2, rad*2);
+        r.toQuad(&pts[4]);
+
+        const uint16_t indices[] = {
+            0, 4, 1, 5, 2, 6, 3, 7, 0, 4
+        };
+        show_mesh(canvas, pts, indices, SK_ARRAY_COUNT(indices));
+    }
+}
+
+static void draw_oval(SkCanvas* canvas, bool showGL, int flags) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkRect r = SkRect::MakeLTRB(50, 70, 250, 370);
+
+    setFade(&paint, showGL);
+    canvas->drawOval(r, paint);
+    if (showGL) {
+        switch (flags) {
+            case 0: {
+                SkPath path;
+                path.addOval(r);
+                show_glframe(canvas, path);
+            } break;
+            case 1:
+            case 3: {
+                SkPath src, dst;
+                src.addOval(r);
+                tesselate(src, &dst);
+                show_fan(canvas, dst, r.centerX(), r.centerY());
+            } break;
+            case 2: {
+                SkPaint p(paint);
+                show_mesh(canvas, r);
+                setGLFrame(&p);
+                paint.setStyle(SkPaint::kFill_Style);
+                canvas->drawCircle(r.centerX(), r.centerY(), 3, p);
+            } break;
+        }
+    }
+
+    canvas->translate(320, 0);
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(25);
+    canvas->drawOval(r, paint);
+    if (showGL) {
+        switch (flags) {
+            case 0: {
+                SkPath path;
+                SkScalar rad = paint.getStrokeWidth() / 2;
+                r.outset(rad, rad);
+                path.addOval(r);
+                r.inset(rad*2, rad*2);
+                path.addOval(r);
+                show_glframe(canvas, path);
+            } break;
+            case 1: {
+                SkPath path0, path1;
+                SkScalar rad = paint.getStrokeWidth() / 2;
+                r.outset(rad, rad);
+                path0.addOval(r);
+                r.inset(rad*2, rad*2);
+                path1.addOval(r);
+                show_mesh_between(canvas, path0, path1);
+            } break;
+            case 2: {
+                SkPath path;
+                path.addOval(r);
+                show_glframe(canvas, path);
+                SkScalar rad = paint.getStrokeWidth() / 2;
+                r.outset(rad, rad);
+                show_mesh(canvas, r);
+            } break;
+            case 3: {
+                SkScalar rad = paint.getStrokeWidth() / 2;
+                r.outset(rad, rad);
+                SkPaint paint;
+                paint.setAlpha(0x33);
+                canvas->drawRect(r, paint);
+                show_mesh(canvas, r);
+            } break;
+        }
+    }
+}
+
+#include "SkImageDecoder.h"
+
+static void draw_image(SkCanvas* canvas, bool showGL, int flags) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setFilterBitmap(true);
+    setFade(&paint, showGL);
+
+    static SkBitmap* gBM;
+    if (NULL == gBM) {
+        gBM = new SkBitmap;
+        SkImageDecoder::DecodeFile("/skimages/startrek.png", gBM);
+    }
+    SkRect r = SkRect::MakeWH(gBM->width(), gBM->height());
+
+    canvas->save();
+    canvas->translate(30, 30);
+    canvas->scale(0.8f, 0.8f);
+    canvas->drawBitmap(*gBM, 0, 0, &paint);
+    if (showGL) {
+        show_mesh(canvas, r);
+    }
+    canvas->restore();
+
+    canvas->translate(210, 290);
+    canvas->rotate(-35);
+    canvas->drawBitmap(*gBM, 0, 0, &paint);
+    if (showGL) {
+        show_mesh(canvas, r);
+    }
+}
+
+static void draw_text(SkCanvas* canvas, bool showGL, int flags) {
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setLCDRenderText(true);
+    const char text[] = "Graphics at Google";
+    size_t len = strlen(text);
+    setFade(&paint, showGL);
+
+    canvas->translate(40, 50);
+    for (int i = 0; i < 10; ++i) {
+        paint.setTextSize(12 + i * 3);
+        canvas->drawText(text, len, 0, 0, paint);
+        if (showGL) {
+            SkRect bounds[256];
+            SkScalar widths[256];
+            int count = paint.getTextWidths(text, len, widths, bounds);
+            SkScalar adv = 0;
+            for (int j = 0; j < count; ++j) {
+                bounds[j].offset(adv, 0);
+                show_mesh(canvas, bounds[j]);
+                adv += widths[j];
+            }
+        }
+        canvas->translate(0, paint.getTextSize() * 3 / 2);
+    }
+}
+
+static const struct {
+    DrawProc    fProc;
+    const char* fName;
+} gRec[] = {
+    {   draw_line,  "Lines" },
+    {   draw_rect,  "Rects" },
+    {   draw_oval,  "Ovals" },
+    {   draw_image, "Images" },
+    {   draw_text,  "Text" },
+};
+
+class TalkGM : public skiagm::GM {
+    DrawProc fProc;
+    SkString fName;
+    bool     fShowGL;
+    int      fFlags;
+
+public:
+    TalkGM(int index, bool showGL, int flags = 0) {
+        fProc = gRec[index].fProc;
+        fName.set(gRec[index].fName);
+        if (showGL) {
+            fName.append("-gl");
+        }
+        fShowGL = showGL;
+        fFlags = flags;
+    }
+
+protected:
+    virtual SkString onShortName() {
+        return fName;
+    }
+
+    virtual SkISize onISize() {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        SkISize size = canvas->getDeviceSize();
+        SkRect dst = SkRect::MakeWH(size.width(), size.height());
+        SkRect src = SkRect::MakeWH(640, 480);
+        SkMatrix matrix;
+        matrix.setRectToRect(src, dst, SkMatrix::kCenter_ScaleToFit);
+
+        canvas->concat(matrix);
+        fProc(canvas, fShowGL, fFlags);
+    }
+
+    virtual uint32_t onGetFlags() const SK_OVERRIDE {
+        return  kSkipPDF_Flag | kSkipPicture_Flag | kSkipPipe_Flag | kSkipTiled_Flag;
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+#define GM_CONCAT(X,Y) GM_CONCAT_IMPL(X,Y)
+#define GM_CONCAT_IMPL(X,Y) X##Y
+
+#define FACTORY_NAME  GM_CONCAT(Factory, __LINE__)
+#define REGISTRY_NAME  GM_CONCAT(gReg, __LINE__)
+
+#define ADD_GM(Class, args)    \
+    static skiagm::GM* FACTORY_NAME(void*) { return new Class args; } \
+    static skiagm::GMRegistry REGISTRY_NAME(FACTORY_NAME);
+
+ADD_GM(TalkGM, (0, false))
+ADD_GM(TalkGM, (0, true))
+ADD_GM(TalkGM, (1, false))
+ADD_GM(TalkGM, (1, true))
+ADD_GM(TalkGM, (2, false))
+ADD_GM(TalkGM, (2, true))
+ADD_GM(TalkGM, (2, true, 1))
+ADD_GM(TalkGM, (2, true, 2))
+ADD_GM(TalkGM, (2, true, 3))
+ADD_GM(TalkGM, (3, false))
+ADD_GM(TalkGM, (3, true))
+ADD_GM(TalkGM, (4, false))
+ADD_GM(TalkGM, (4, true))
+
+//static GM* MyFactory(void*) { return new TalkGM(0, false); }
+//static GMRegistry reg(MyFactory);
diff --git a/gm/testimagefilters.cpp b/gm/testimagefilters.cpp
index fc71d30..73581ba 100644
--- a/gm/testimagefilters.cpp
+++ b/gm/testimagefilters.cpp
@@ -12,6 +12,9 @@
 #include "SkShader.h"
 
 #include "SkBlurImageFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkMergeImageFilter.h"
+#include "SkOffsetImageFilter.h"
 #include "SkTestImageFilters.h"
 
 #define FILTER_WIDTH    SkIntToScalar(150)
@@ -23,7 +26,7 @@
     SkColorFilter* cf = SkColorFilter::CreateModeFilter(SK_ColorBLUE,
                                                         SkXfermode::kSrcIn_Mode);
     SkAutoUnref aur(cf);
-    return new SkColorFilterImageFilter(cf);
+    return SkColorFilterImageFilter::Create(cf);
 }
 static SkImageFilter* make3() {
     return new SkBlurImageFilter(8, 0);
@@ -51,13 +54,13 @@
     SkAutoUnref aur1(inner);
     SkImageFilter* compose = new SkComposeImageFilter(outer, inner);
     SkAutoUnref aur2(compose);
-    
+
     SkColorFilter* cf = SkColorFilter::CreateModeFilter(0x880000FF,
                                                         SkXfermode::kSrcIn_Mode);
     SkAutoUnref aur3(cf);
-    SkImageFilter* blue = new SkColorFilterImageFilter(cf);
+    SkImageFilter* blue = SkColorFilterImageFilter::Create(cf);
     SkAutoUnref aur4(blue);
-    
+
     return new SkMergeImageFilter(compose, blue);
 }
 
@@ -68,13 +71,13 @@
     SkAutoUnref aur1(inner);
     SkImageFilter* compose = new SkComposeImageFilter(outer, inner);
     SkAutoUnref aur2(compose);
-    
+
     SkColorFilter* cf = SkColorFilter::CreateModeFilter(0x880000FF,
                                                         SkXfermode::kSrcIn_Mode);
     SkAutoUnref aur3(cf);
-    SkImageFilter* blue = new SkColorFilterImageFilter(cf);
+    SkImageFilter* blue = SkColorFilterImageFilter::Create(cf);
     SkAutoUnref aur4(blue);
-    
+
     return new SkMergeImageFilter(compose, blue);
 }
 
@@ -105,9 +108,9 @@
         static SkImageFilter* (*gFilterProc[])() = {
             make0, make1, make2, make3, make4, make5, make6, make7
         };
-        
+
         const SkRect bounds = SkRect::MakeWH(FILTER_WIDTH, FILTER_HEIGHT);
-        
+
         const SkScalar dx = bounds.width() * 8 / 7;
         const SkScalar dy = bounds.height() * 8 / 7;
 
@@ -123,7 +126,7 @@
             SkPaint p;
             p.setStyle(SkPaint::kStroke_Style);
             canvas->drawRect(bounds, p);
-            
+
             SkPaint paint;
             paint.setImageFilter(gFilterProc[i]())->unref();
             canvas->saveLayer(&bounds, &paint);
@@ -139,5 +142,3 @@
 
 static skiagm::GM* MyFactory(void*) { return new TestImageFiltersGM; }
 static skiagm::GMRegistry reg(MyFactory);
-
-
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/command_line b/gm/tests/outputs/aaclip-readback/output-expected/command_line
new file mode 100644
index 0000000..db66e2e
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --match simpleaaclip_path --config 8888 --config 565 -r gm/tests/tempfiles/aaclip-images --writeJsonSummary gm/tests/outputs/aaclip-readback/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/json-summary.txt b/gm/tests/outputs/aaclip-readback/output-expected/json-summary.txt
new file mode 100644
index 0000000..83d2e77
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/json-summary.txt
@@ -0,0 +1,25 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : null,
+      "succeeded" : {
+         "simpleaaclip_path_565" : {
+            "checksum" : FAKE
+         },
+         "simpleaaclip_path_8888" : {
+            "checksum" : FAKE
+         }
+      }
+   },
+   "expected-results" : {
+      "simpleaaclip_path_565" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
+      "simpleaaclip_path_8888" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      }
+   }
+}
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/return_value b/gm/tests/outputs/aaclip-readback/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/aaclip-readback/output-expected/stdout b/gm/tests/outputs/aaclip-readback/output-expected/stdout
new file mode 100644
index 0000000..52931ca
--- /dev/null
+++ b/gm/tests/outputs/aaclip-readback/output-expected/stdout
@@ -0,0 +1,3 @@
+reading from gm/tests/tempfiles/aaclip-images
+drawing... simpleaaclip_path [640 480]
+Ran 1 tests: 1 passed, 0 failed, 0 missing reference images
diff --git a/gm/tests/outputs/aaclip-write/output-expected/command_line b/gm/tests/outputs/aaclip-write/output-expected/command_line
new file mode 100644
index 0000000..db030e1
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --match simpleaaclip_path --config 8888 --config 565 -w gm/tests/tempfiles/aaclip-images --writeJsonSummary gm/tests/outputs/aaclip-write/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/aaclip-write/output-expected/json-summary.txt b/gm/tests/outputs/aaclip-write/output-expected/json-summary.txt
new file mode 100644
index 0000000..832f828
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/json-summary.txt
@@ -0,0 +1,16 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : {
+         "simpleaaclip_path_565" : {
+            "checksum" : FAKE
+         },
+         "simpleaaclip_path_8888" : {
+            "checksum" : FAKE
+         }
+      },
+      "succeeded" : null
+   },
+   "expected-results" : null
+}
diff --git a/gm/tests/outputs/aaclip-write/output-expected/return_value b/gm/tests/outputs/aaclip-write/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/aaclip-write/output-expected/stdout b/gm/tests/outputs/aaclip-write/output-expected/stdout
new file mode 100644
index 0000000..5211664
--- /dev/null
+++ b/gm/tests/outputs/aaclip-write/output-expected/stdout
@@ -0,0 +1,3 @@
+writing to gm/tests/tempfiles/aaclip-images
+drawing... simpleaaclip_path [640 480]
+Ran 1 tests: 0 passed, 0 failed, 1 missing reference images
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/command_line b/gm/tests/outputs/compared-against-different-pixels/output-expected/command_line
new file mode 100644
index 0000000..d887baa
--- /dev/null
+++ b/gm/tests/outputs/compared-against-different-pixels/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/different-pixels --writeJsonSummary gm/tests/outputs/compared-against-different-pixels/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-different-pixels/output-expected/json-summary.txt
new file mode 100644
index 0000000..37709e1
--- /dev/null
+++ b/gm/tests/outputs/compared-against-different-pixels/output-expected/json-summary.txt
@@ -0,0 +1,25 @@
+{
+   "actual-results" : {
+      "failed" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
+         "8888/dashing2" : {
+            "checksum" : FAKE
+         }
+      },
+      "failure-ignored" : null,
+      "no-comparison" : null,
+      "succeeded" : null
+   },
+   "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
+      "8888/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      }
+   }
+}
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/return_value b/gm/tests/outputs/compared-against-different-pixels/output-expected/return_value
new file mode 100644
index 0000000..ace9d03
--- /dev/null
+++ b/gm/tests/outputs/compared-against-different-pixels/output-expected/return_value
@@ -0,0 +1 @@
+255
diff --git a/gm/tests/outputs/compared-against-different-pixels/output-expected/stdout b/gm/tests/outputs/compared-against-different-pixels/output-expected/stdout
new file mode 100644
index 0000000..885dfd7
--- /dev/null
+++ b/gm/tests/outputs/compared-against-different-pixels/output-expected/stdout
@@ -0,0 +1,3 @@
+reading from gm/tests/inputs/different-pixels
+drawing... dashing2 [640 480]
+Ran 1 tests: 0 passed, 1 failed, 0 missing reference images
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/command_line b/gm/tests/outputs/compared-against-empty-dir/output-expected/command_line
new file mode 100644
index 0000000..d00ec60
--- /dev/null
+++ b/gm/tests/outputs/compared-against-empty-dir/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/empty-dir --writeJsonSummary gm/tests/outputs/compared-against-empty-dir/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-empty-dir/output-expected/json-summary.txt
new file mode 100644
index 0000000..6ffaf35
--- /dev/null
+++ b/gm/tests/outputs/compared-against-empty-dir/output-expected/json-summary.txt
@@ -0,0 +1,25 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
+         "8888/dashing2" : {
+            "checksum" : FAKE
+         }
+      },
+      "succeeded" : null
+   },
+   "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : null,
+         "ignore-failure" : false
+      },
+      "8888/dashing2" : {
+         "checksums" : null,
+         "ignore-failure" : false
+      }
+   }
+}
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/return_value b/gm/tests/outputs/compared-against-empty-dir/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/compared-against-empty-dir/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/compared-against-empty-dir/output-expected/stdout b/gm/tests/outputs/compared-against-empty-dir/output-expected/stdout
new file mode 100644
index 0000000..246c05b
--- /dev/null
+++ b/gm/tests/outputs/compared-against-empty-dir/output-expected/stdout
@@ -0,0 +1,5 @@
+reading from gm/tests/inputs/empty-dir
+drawing... dashing2 [640 480]
+FAILED to read gm/tests/inputs/empty-dir/8888/dashing2.png
+FAILED to read gm/tests/inputs/empty-dir/565/dashing2.png
+Ran 1 tests: 0 passed, 0 failed, 1 missing reference images
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/command_line b/gm/tests/outputs/compared-against-identical-bytes/output-expected/command_line
new file mode 100644
index 0000000..d065b13
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-bytes/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/identical-bytes --writeJsonSummary gm/tests/outputs/compared-against-identical-bytes/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-identical-bytes/output-expected/json-summary.txt
new file mode 100644
index 0000000..bed3b2d
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-bytes/output-expected/json-summary.txt
@@ -0,0 +1,25 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : null,
+      "succeeded" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
+         "8888/dashing2" : {
+            "checksum" : FAKE
+         }
+      }
+   },
+   "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
+      "8888/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      }
+   }
+}
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/return_value b/gm/tests/outputs/compared-against-identical-bytes/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-bytes/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/compared-against-identical-bytes/output-expected/stdout b/gm/tests/outputs/compared-against-identical-bytes/output-expected/stdout
new file mode 100644
index 0000000..03153e6
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-bytes/output-expected/stdout
@@ -0,0 +1,3 @@
+reading from gm/tests/inputs/identical-bytes
+drawing... dashing2 [640 480]
+Ran 1 tests: 1 passed, 0 failed, 0 missing reference images
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/command_line b/gm/tests/outputs/compared-against-identical-pixels/output-expected/command_line
new file mode 100644
index 0000000..086f984
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-pixels/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 -r gm/tests/inputs/identical-pixels --writeJsonSummary gm/tests/outputs/compared-against-identical-pixels/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/json-summary.txt b/gm/tests/outputs/compared-against-identical-pixels/output-expected/json-summary.txt
new file mode 100644
index 0000000..bed3b2d
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-pixels/output-expected/json-summary.txt
@@ -0,0 +1,25 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : null,
+      "succeeded" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
+         "8888/dashing2" : {
+            "checksum" : FAKE
+         }
+      }
+   },
+   "expected-results" : {
+      "565/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      },
+      "8888/dashing2" : {
+         "checksums" : [ FAKE ],
+         "ignore-failure" : false
+      }
+   }
+}
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/return_value b/gm/tests/outputs/compared-against-identical-pixels/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-pixels/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/compared-against-identical-pixels/output-expected/stdout b/gm/tests/outputs/compared-against-identical-pixels/output-expected/stdout
new file mode 100644
index 0000000..a60b4ef
--- /dev/null
+++ b/gm/tests/outputs/compared-against-identical-pixels/output-expected/stdout
@@ -0,0 +1,3 @@
+reading from gm/tests/inputs/identical-pixels
+drawing... dashing2 [640 480]
+Ran 1 tests: 1 passed, 0 failed, 0 missing reference images
diff --git a/gm/tests/outputs/no-readpath/output-expected/command_line b/gm/tests/outputs/no-readpath/output-expected/command_line
new file mode 100644
index 0000000..1609465
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/gm --hierarchy --match dashing2 --config 8888 --config 565 --writeJsonSummary gm/tests/outputs/no-readpath/output-actual/json-summary.txt
diff --git a/gm/tests/outputs/no-readpath/output-expected/json-summary.txt b/gm/tests/outputs/no-readpath/output-expected/json-summary.txt
new file mode 100644
index 0000000..81db3a2
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/json-summary.txt
@@ -0,0 +1,16 @@
+{
+   "actual-results" : {
+      "failed" : null,
+      "failure-ignored" : null,
+      "no-comparison" : {
+         "565/dashing2" : {
+            "checksum" : FAKE
+         },
+         "8888/dashing2" : {
+            "checksum" : FAKE
+         }
+      },
+      "succeeded" : null
+   },
+   "expected-results" : null
+}
diff --git a/gm/tests/outputs/no-readpath/output-expected/return_value b/gm/tests/outputs/no-readpath/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/gm/tests/outputs/no-readpath/output-expected/stdout b/gm/tests/outputs/no-readpath/output-expected/stdout
new file mode 100644
index 0000000..effa9e7
--- /dev/null
+++ b/gm/tests/outputs/no-readpath/output-expected/stdout
@@ -0,0 +1,2 @@
+drawing... dashing2 [640 480]
+Ran 1 tests: 0 passed, 0 failed, 1 missing reference images
diff --git a/gm/tests/rebaseline.sh b/gm/tests/rebaseline.sh
new file mode 100755
index 0000000..71af20f
--- /dev/null
+++ b/gm/tests/rebaseline.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+# Rebaseline the outputs/*/output-expected/ subdirectories used by the
+# gm self-tests.
+# Use with caution: are you sure the new results are actually correct?
+#
+# YOU MUST RE-RUN THIS UNTIL THE SELF-TESTS SUCCEED!
+# (It takes one run for each failing call to gm_test in run.sh)
+#
+# TODO: currently, this must be run on Linux to generate baselines that match
+# the ones on the housekeeper bot (which runs on Linux... see
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1417/steps/RunGmSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
+
+function replace_expected_with_actual {
+  # Delete all the expected output files
+  EXPECTED_FILES=$(find outputs/*/output-expected -type f | grep -v /\.svn/)
+  for EXPECTED_FILE in $EXPECTED_FILES; do
+    rm $EXPECTED_FILE
+  done
+
+  # Copy all the actual output files into the "expected" directories,
+  # creating new subdirs as we go.
+  ACTUAL_FILES=$(find outputs/*/output-actual -type f | grep -v /\.svn/)
+  for ACTUAL_FILE in $ACTUAL_FILES; do
+    EXPECTED_FILE=${ACTUAL_FILE//actual/expected}
+    mkdir -p $(dirname $EXPECTED_FILE)
+    cp $ACTUAL_FILE $EXPECTED_FILE
+  done
+}
+
+function svn_add_new_files {
+  # Delete all the "actual" directories, so we can svn-add any new "expected"
+  # directories without adding the "actual" ones.
+  rm -rf outputs/*/output-actual
+  FILES=$(svn stat outputs/* | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+  FILES=$(svn stat outputs/*/output-expected | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+}
+
+function svn_delete_old_files {
+  FILES=$(svn stat outputs/*/output-expected | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+  FILES=$(svn stat outputs/* | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+}
+
+
+# cd into the gm self-test dir
+cd $(dirname $0)
+
+./run.sh
+SELFTEST_RESULT=$?
+echo
+if [ "$SELFTEST_RESULT" != "0" ]; then
+  replace_expected_with_actual
+  echo "Self-tests still failing, you should probably run this again..."
+else
+  svn_add_new_files
+  svn_delete_old_files
+  echo "Self-tests succeeded this time, you should be done!"
+fi
+exit $SELFTEST_RESULT
+
diff --git a/gm/tests/run.sh b/gm/tests/run.sh
new file mode 100755
index 0000000..717262e
--- /dev/null
+++ b/gm/tests/run.sh
@@ -0,0 +1,162 @@
+#!/bin/bash
+
+# Self-tests for gm, based on tools/tests/run.sh
+#
+# These tests are run by the Skia_PerCommit_House_Keeping bot at every commit,
+# so make sure that they still pass when you make changes to gm!
+#
+# TODO: currently, this only passes on Linux (which is the platform that
+# the housekeeper bot runs on, e.g.
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1417/steps/RunGmSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
+# Ideally, these tests should pass on all development platforms...
+# otherwise, how can developers be expected to test them before committing a
+# change?
+
+# cd into .../trunk so all the paths will work
+cd $(dirname $0)/../..
+
+# TODO(epoger): make it look in Release and/or Debug
+GM_BINARY=out/Debug/gm
+
+OUTPUT_ACTUAL_SUBDIR=output-actual
+OUTPUT_EXPECTED_SUBDIR=output-expected
+CONFIGS="--config 8888 --config 565"
+
+# Compare contents of all files within directories $1 and $2,
+# EXCEPT for any dotfiles.
+# If there are any differences, a description is written to stdout and
+# we exit with a nonzero return value.
+# Otherwise, we write nothing to stdout and return.
+function compare_directories {
+  if [ $# != 2 ]; then
+    echo "compare_directories requires exactly 2 parameters, got $#"
+    exit 1
+  fi
+  diff -r --exclude=.* $1 $2
+  if [ $? != 0 ]; then
+    echo "failed in: compare_directories $1 $2"
+    exit 1
+  fi
+}
+
+# Run gm...
+# - with the arguments in $1
+# - writing stdout into $2/$OUTPUT_ACTUAL_SUBDIR/stdout
+# - writing json summary into $2/$OUTPUT_ACTUAL_SUBDIR/json-summary.txt
+# - writing return value into $2/$OUTPUT_ACTUAL_SUBDIR/return_value
+# Then compare all of those against $2/$OUTPUT_EXPECTED_SUBDIR .
+function gm_test {
+  if [ $# != 2 ]; then
+    echo "gm_test requires exactly 2 parameters, got $#"
+    exit 1
+  fi
+  GM_ARGS="$1"
+  ACTUAL_OUTPUT_DIR="$2/$OUTPUT_ACTUAL_SUBDIR"
+  EXPECTED_OUTPUT_DIR="$2/$OUTPUT_EXPECTED_SUBDIR"
+  JSON_SUMMARY_FILE="$ACTUAL_OUTPUT_DIR/json-summary.txt"
+
+  rm -rf $ACTUAL_OUTPUT_DIR
+  mkdir -p $ACTUAL_OUTPUT_DIR
+  COMMAND="$GM_BINARY $GM_ARGS --writeJsonSummary $JSON_SUMMARY_FILE"
+  echo "$COMMAND" >$ACTUAL_OUTPUT_DIR/command_line
+  $COMMAND &>$ACTUAL_OUTPUT_DIR/stdout
+  echo $? >$ACTUAL_OUTPUT_DIR/return_value
+
+  # Only compare selected lines in the stdout, to ignore any spurious lines
+  # as noted in http://code.google.com/p/skia/issues/detail?id=1068 .
+  #
+  # TODO(epoger): This is still hacky... we need to rewrite this script in
+  # Python soon, and make stuff like this more maintainable.
+  grep --regexp=^reading --regexp=^writing --regexp=^drawing \
+    --regexp=^FAILED --regexp=^Ran $ACTUAL_OUTPUT_DIR/stdout \
+    >$ACTUAL_OUTPUT_DIR/stdout-tmp
+  mv $ACTUAL_OUTPUT_DIR/stdout-tmp $ACTUAL_OUTPUT_DIR/stdout
+
+  # Replace particular checksums in json summary with a placeholder, so
+  # we don't need to rebaseline these json files when our drawing routines
+  # change.
+  sed -e 's/"checksum" : [0-9]*/"checksum" : FAKE/g' \
+    --in-place $JSON_SUMMARY_FILE
+  sed -e 's/"checksums" : \[ [0-9]* \]/"checksums" : [ FAKE ]/g' \
+    --in-place $JSON_SUMMARY_FILE
+
+  compare_directories $EXPECTED_OUTPUT_DIR $ACTUAL_OUTPUT_DIR
+}
+
+# Create input dir (at path $1) with images that match or mismatch
+# as appropriate.
+#
+# We used to check these files into SVN, but then we needed to rebasline them
+# when our drawing changed at all... so, as proposed in
+# http://code.google.com/p/skia/issues/detail?id=1068 , we generate them
+# new each time.
+function create_inputs_dir {
+  if [ $# != 1 ]; then
+    echo "create_inputs_dir requires exactly 1 parameter, got $#"
+    exit 1
+  fi
+  INPUTS_DIR="$1"
+  mkdir -p $INPUTS_DIR
+
+  mkdir -p $INPUTS_DIR/identical-bytes
+  $GM_BINARY --hierarchy --match dashing2 $CONFIGS \
+    -w $INPUTS_DIR/identical-bytes
+
+  mkdir -p $INPUTS_DIR/identical-pixels
+  $GM_BINARY --hierarchy --match dashing2 $CONFIGS \
+    -w $INPUTS_DIR/identical-pixels
+  echo "more bytes that do not change the image pixels" \
+    >> $INPUTS_DIR/identical-pixels/8888/dashing2.png
+  echo "more bytes that do not change the image pixels" \
+    >> $INPUTS_DIR/identical-pixels/565/dashing2.png
+
+  mkdir -p $INPUTS_DIR/different-pixels
+  $GM_BINARY --hierarchy --match dashing3 $CONFIGS \
+    -w $INPUTS_DIR/different-pixels
+  mv $INPUTS_DIR/different-pixels/8888/dashing3.png \
+    $INPUTS_DIR/different-pixels/8888/dashing2.png
+  mv $INPUTS_DIR/different-pixels/565/dashing3.png \
+    $INPUTS_DIR/different-pixels/565/dashing2.png
+
+  mkdir -p $INPUTS_DIR/empty-dir
+}
+
+GM_TESTDIR=gm/tests
+GM_INPUTS=$GM_TESTDIR/inputs
+GM_OUTPUTS=$GM_TESTDIR/outputs
+GM_TEMPFILES=$GM_TESTDIR/tempfiles
+
+create_inputs_dir $GM_INPUTS
+
+# Compare generated image against an input image file with identical bytes.
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/identical-bytes" "$GM_OUTPUTS/compared-against-identical-bytes"
+
+# Compare generated image against an input image file with identical pixels but different PNG encoding.
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/identical-pixels" "$GM_OUTPUTS/compared-against-identical-pixels"
+
+# Compare generated image against an input image file with different pixels.
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/different-pixels" "$GM_OUTPUTS/compared-against-different-pixels"
+
+# Compare generated image against an empty "expected image" dir.
+gm_test "--hierarchy --match dashing2 $CONFIGS -r $GM_INPUTS/empty-dir" "$GM_OUTPUTS/compared-against-empty-dir"
+
+# If run without "-r", the JSON's "actual-results" section should contain
+# actual checksums marked as "failure-ignored", but the "expected-results"
+# section should be empty.
+gm_test "--hierarchy --match dashing2 $CONFIGS" "$GM_OUTPUTS/no-readpath"
+
+# Run a test which generates partially transparent images, write out those
+# images, and read them back in.
+#
+# This test would have caught
+# http://code.google.com/p/skia/issues/detail?id=1079 ('gm generating
+# spurious pixel_error messages as of r7258').
+IMAGEDIR=$GM_TEMPFILES/aaclip-images
+rm -rf $IMAGEDIR
+mkdir -p $IMAGEDIR
+gm_test "--match simpleaaclip_path $CONFIGS -w $IMAGEDIR" "$GM_OUTPUTS/aaclip-write"
+gm_test "--match simpleaaclip_path $CONFIGS -r $IMAGEDIR" "$GM_OUTPUTS/aaclip-readback"
+
+echo "All tests passed."
diff --git a/gm/texdata.cpp b/gm/texdata.cpp
index 1fdae69..da52aad 100644
--- a/gm/texdata.cpp
+++ b/gm/texdata.cpp
@@ -5,8 +5,14 @@
  * 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 "effects/GrSimpleTextureEffect.h"
 #include "SkColorPriv.h"
 #include "SkDevice.h"
 
@@ -81,8 +87,7 @@
                 desc.fConfig    = kSkia8888_PM_GrPixelConfig;
                 desc.fWidth     = 2 * S;
                 desc.fHeight    = 2 * S;
-                desc.fSampleCnt = 0;
-                GrTexture* texture = 
+                GrTexture* texture =
                     ctx->createUncachedTexture(desc, gTextureData, 0);
 
                 if (!texture) {
@@ -90,15 +95,13 @@
                 }
                 GrAutoUnref au(texture);
 
-                ctx->setClip(GrRect::MakeWH(2*S, 2*S));
+                GrContext::AutoClip acs(ctx, GrRect::MakeWH(2*S, 2*S));
+
                 ctx->setRenderTarget(target);
 
                 GrPaint paint;
-                paint.reset();
-                paint.fColor = 0xffffffff;
-                paint.fSrcBlendCoeff = kOne_BlendCoeff;
-                paint.fDstBlendCoeff = kISA_BlendCoeff;
-                GrMatrix vm;
+                paint.setBlendFunc(kOne_GrBlendCoeff, kISA_GrBlendCoeff);
+                SkMatrix vm;
                 if (i) {
                     vm.setRotate(90 * SK_Scalar1,
                                  S * SK_Scalar1,
@@ -107,12 +110,10 @@
                     vm.reset();
                 }
                 ctx->setMatrix(vm);
-                GrMatrix tm;
+                SkMatrix tm;
                 tm = vm;
-                GrMatrix* sampleMat = paint.textureSampler(0)->matrix();
-                *sampleMat = vm;
-                sampleMat->postIDiv(2*S, 2*S);
-                paint.setTexture(0, texture);
+                tm.postIDiv(2*S, 2*S);
+                paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(texture, tm))->unref();
 
                 ctx->drawRect(paint, GrRect::MakeWH(2*S, 2*S));
 
@@ -121,7 +122,7 @@
                 offset = 0;
                 for (int y = 0; y < S; ++y) {
                     for (int x = 0; x < S; ++x) {
-                        gTextureData[offset + y * stride + x] = 
+                        gTextureData[offset + y * stride + x] =
                             ((x + y) % 2) ? (i ? green : red) : blue;
                     }
                 }
@@ -144,3 +145,4 @@
 
 }
 
+#endif
diff --git a/gm/texteffects.cpp b/gm/texteffects.cpp
new file mode 100644
index 0000000..782850e
--- /dev/null
+++ b/gm/texteffects.cpp
@@ -0,0 +1,217 @@
+/*
+ * 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 "SkFlattenableBuffers.h"
+#include "SkLayerRasterizer.h"
+#include "SkBlurMaskFilter.h"
+
+static void r0(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
+                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+    p.setMaskFilter(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+
+    p.setAlpha(0x11);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r1(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setAlpha(0x40);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*2);
+    rast->addLayer(p);
+}
+
+static void r2(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setStyle(SkPaint::kStrokeAndFill_Style);
+    p.setStrokeWidth(SK_Scalar1*4);
+    rast->addLayer(p);
+
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3/2);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+static void r3(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1*3);
+    rast->addLayer(p);
+
+    p.setAlpha(0x20);
+    p.setStyle(SkPaint::kFill_Style);
+    p.setXfermodeMode(SkXfermode::kSrc_Mode);
+    rast->addLayer(p);
+}
+
+static void r4(SkLayerRasterizer* rast, SkPaint& p) {
+    p.setAlpha(0x60);
+    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
+
+    p.setAlpha(0xFF);
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
+
+    p.setXfermode(NULL);
+    rast->addLayer(p);
+}
+
+#include "SkDiscretePathEffect.h"
+
+static void r5(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
+    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
+    rast->addLayer(p);
+}
+
+static void r6(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    p.setAntiAlias(false);
+    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
+    r5(rast2, p);
+    p.setRasterizer(rast2)->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+}
+
+#include "Sk2DPathEffect.h"
+
+static SkPathEffect* MakeDotEffect(SkScalar radius, const SkMatrix& matrix) {
+    SkPath path;
+    path.addCircle(0, 0, radius);
+    return new SkPath2DPathEffect(matrix, path);
+}
+
+static void r7(SkLayerRasterizer* rast, SkPaint& p) {
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(MakeDotEffect(SK_Scalar1*4, lattice))->unref();
+    rast->addLayer(p);
+}
+
+static void r8(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
+    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
+    p.setPathEffect(MakeDotEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+static void r9(SkLayerRasterizer* rast, SkPaint& p) {
+    rast->addLayer(p);
+
+    SkMatrix    lattice;
+    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
+    lattice.postRotate(SkIntToScalar(30), 0, 0);
+    p.setPathEffect(new SkLine2DPathEffect(SK_Scalar1*2, lattice))->unref();
+    p.setXfermodeMode(SkXfermode::kClear_Mode);
+    rast->addLayer(p);
+
+    p.setPathEffect(NULL);
+    p.setXfermode(NULL);
+    p.setStyle(SkPaint::kStroke_Style);
+    p.setStrokeWidth(SK_Scalar1);
+    rast->addLayer(p);
+}
+
+typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
+
+static const raster_proc gRastProcs[] = {
+    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
+};
+
+#include "SkXfermode.h"
+
+static void apply_shader(SkPaint* paint, int index) {
+    raster_proc proc = gRastProcs[index];
+    if (proc)
+    {
+        SkPaint p;
+        SkLayerRasterizer*  rast = new SkLayerRasterizer;
+
+        p.setAntiAlias(true);
+        proc(rast, p);
+        paint->setRasterizer(rast)->unref();
+    }
+
+#if 0
+    SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
+    paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();
+#endif
+    paint->setColor(SK_ColorBLUE);
+}
+
+class TextEffectsGM : public skiagm::GM {
+public:
+    TextEffectsGM() {}
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("texteffects");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(460, 680);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        canvas->save();
+
+        SkPaint     paint;
+        paint.setAntiAlias(true);
+        paint.setTextSize(SkIntToScalar(56));
+
+        SkScalar    x = SkIntToScalar(20);
+        SkScalar    y = paint.getTextSize();
+
+        SkString str("Hamburgefons");
+
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
+            apply_shader(&paint, i);
+
+            //  paint.setMaskFilter(NULL);
+            //  paint.setColor(SK_ColorBLACK);
+
+            canvas->drawText(str.c_str(), str.size(), x, y, paint);
+
+            y += paint.getFontSpacing();
+        }
+
+        canvas->restore();
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+static skiagm::GM* MyFactory(void*) { return new TextEffectsGM; }
+static skiagm::GMRegistry reg(MyFactory);
diff --git a/gm/tilemodes.cpp b/gm/tilemodes.cpp
index 289caae..b0594e4 100644
--- a/gm/tilemodes.cpp
+++ b/gm/tilemodes.cpp
@@ -19,20 +19,18 @@
 #include "SkUnitMappers.h"
 #include "SkBlurDrawLooper.h"
 
-namespace skiagm {
-
 static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
     bm->setConfig(config, w, h);
     bm->allocPixels();
-    bm->eraseColor(0);
-    
+    bm->eraseColor(SK_ColorTRANSPARENT);
+
     SkCanvas    canvas(*bm);
     SkPoint     pts[] = { { 0, 0 }, { SkIntToScalar(w), SkIntToScalar(h)} };
     SkColor     colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
     SkScalar    pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
     SkPaint     paint;
-    
-    SkUnitMapper*   um = NULL;    
+
+    SkUnitMapper*   um = NULL;
 
     um = new SkCosineMapper;
 //    um = new SkDiscreteMapper(12);
@@ -60,35 +58,38 @@
 static const int gWidth = 32;
 static const int gHeight = 32;
 
-class TilingGM : public GM {
+class TilingGM : public skiagm::GM {
     SkBlurDrawLooper    fLooper;
 public:
-	TilingGM()
+    TilingGM()
             : fLooper(SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(2),
                       0x88000000) {
+    }
+
+    SkBitmap    fTexture[SK_ARRAY_COUNT(gConfigs)];
+
+protected:
+    SkString onShortName() {
+        return SkString("tilemodes");
+    }
+
+    SkISize onISize() { return SkISize::Make(880, 560); }
+
+    virtual void onOnceBeforeDraw() SK_OVERRIDE {
         for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
             makebm(&fTexture[i], gConfigs[i], gWidth, gHeight);
         }
     }
 
-    SkBitmap    fTexture[SK_ARRAY_COUNT(gConfigs)];
-	
-protected:
-    SkString onShortName() {
-        return SkString("tilemodes");
-    }
-    
-	SkISize onISize() { return make_isize(880, 560); }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+
         SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) };
 
         static const char* gConfigNames[] = { "8888", "565", "4444" };
-    
+
         static const bool           gFilters[] = { false, true };
         static const char*          gFilterNames[] = {     "point",                     "bilinear" };
-    
+
         static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
         static const char*          gModeNames[] = {    "C",                    "R",                   "M" };
 
@@ -106,11 +107,11 @@
 
                 p.setTextAlign(SkPaint::kCenter_Align);
                 canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
-                
+
                 x += r.width() * 4 / 3;
             }
         }
-        
+
         y += SkIntToScalar(16);
 
         for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
@@ -121,12 +122,12 @@
                         SkPaint paint;
                         setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
                         paint.setDither(true);
-                        
+
                         canvas->save();
                         canvas->translate(x, y);
                         canvas->drawRect(r, paint);
                         canvas->restore();
-                        
+
                         x += r.width() * 4 / 3;
                     }
                 }
@@ -143,15 +144,116 @@
             }
         }
     }
-    
+
 private:
-    typedef GM INHERITED;
+    typedef skiagm::GM INHERITED;
+};
+
+static SkShader* make_bm(SkShader::TileMode tx, SkShader::TileMode ty) {
+    SkBitmap bm;
+    makebm(&bm, SkBitmap::kARGB_8888_Config, gWidth, gHeight);
+    return SkShader::CreateBitmapShader(bm, tx, ty);
+}
+
+static SkShader* make_grad(SkShader::TileMode tx, SkShader::TileMode ty) {
+    SkPoint pts[] = { { 0, 0 }, { SkIntToScalar(gWidth), SkIntToScalar(gHeight)} };
+    SkPoint center = { SkIntToScalar(gWidth)/2, SkIntToScalar(gHeight)/2 };
+    SkScalar rad = SkIntToScalar(gWidth)/2;
+    SkColor colors[] = { 0xFFFF0000, 0xFF0044FF };
+
+    int index = (int)ty;
+    switch (index % 3) {
+        case 0:
+            return SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors), tx);
+        case 1:
+            return SkGradientShader::CreateRadial(center, rad, colors, NULL, SK_ARRAY_COUNT(colors), tx);
+        case 2:
+            return SkGradientShader::CreateSweep(center.fX, center.fY, colors, NULL, SK_ARRAY_COUNT(colors));
+    }
+
+    return NULL;
+}
+
+typedef SkShader* (*ShaderProc)(SkShader::TileMode, SkShader::TileMode);
+
+class Tiling2GM : public skiagm::GM {
+    ShaderProc fProc;
+    SkString   fName;
+public:
+    Tiling2GM(ShaderProc proc, const char name[]) : fProc(proc) {
+        fName.printf("tilemode_%s", name);
+    }
+
+protected:
+    SkString onShortName() {
+        return fName;
+    }
+
+    SkISize onISize() { return SkISize::Make(880, 560); }
+
+    virtual void onDraw(SkCanvas* canvas) {
+        canvas->scale(SkIntToScalar(3)/2, SkIntToScalar(3)/2);
+
+        const SkScalar w = SkIntToScalar(gWidth);
+        const SkScalar h = SkIntToScalar(gHeight);
+        SkRect r = { -w, -h, w*2, h*2 };
+
+        static const SkShader::TileMode gModes[] = {
+            SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode
+        };
+        static const char* gModeNames[] = {
+            "Clamp", "Repeat", "Mirror"
+        };
+
+        SkScalar y = SkIntToScalar(24);
+        SkScalar x = SkIntToScalar(66);
+
+        SkPaint p;
+        p.setAntiAlias(true);
+        p.setTextAlign(SkPaint::kCenter_Align);
+
+        for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+            SkString str(gModeNames[kx]);
+            canvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
+            x += r.width() * 4 / 3;
+        }
+
+        y += SkIntToScalar(16) + h;
+        p.setTextAlign(SkPaint::kRight_Align);
+
+        for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
+            x = SkIntToScalar(16) + w;
+
+            SkString str(gModeNames[ky]);
+            canvas->drawText(str.c_str(), str.size(), x, y + h/2, p);
+
+            x += SkIntToScalar(50);
+            for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
+                SkPaint paint;
+                paint.setShader(fProc(gModes[kx], gModes[ky]))->unref();
+
+                canvas->save();
+                canvas->translate(x, y);
+                canvas->drawRect(r, paint);
+                canvas->restore();
+
+                x += r.width() * 4 / 3;
+            }
+            y += r.height() * 4 / 3;
+        }
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
 };
 
 //////////////////////////////////////////////////////////////////////////////
 
-static GM* MyFactory(void*) { return new TilingGM; }
-static GMRegistry reg(MyFactory);
+static skiagm::GM* MyFactory(void*) { return new TilingGM; }
+static skiagm::GMRegistry reg(MyFactory);
 
-}
+static skiagm::GM* MyFactory2(void*) { return new Tiling2GM(make_bm, "bitmap"); }
+static skiagm::GMRegistry reg2(MyFactory2);
 
+static skiagm::GM* MyFactory3(void*) { return new Tiling2GM(make_grad, "gradient"); }
+static skiagm::GMRegistry reg3(MyFactory3);
diff --git a/gm/tinybitmap.cpp b/gm/tinybitmap.cpp
index d532b5b..26bf25e 100644
--- a/gm/tinybitmap.cpp
+++ b/gm/tinybitmap.cpp
@@ -32,13 +32,11 @@
 }
 
 class TinyBitmapGM : public GM {
-    SkBitmap    fBM;
 public:
     TinyBitmapGM() {
         this->setBGColor(0xFFDDDDDD);
-        fBM = make_bitmap();
     }
-    
+
 protected:
     SkString onShortName() {
         return SkString("tinybitmap");
@@ -47,15 +45,16 @@
     virtual SkISize onISize() { return make_isize(100, 100); }
 
     virtual void onDraw(SkCanvas* canvas) {
-        SkShader* s = 
-            SkShader::CreateBitmapShader(fBM, SkShader::kRepeat_TileMode,
+        SkBitmap bm = make_bitmap();
+        SkShader* s =
+            SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
                                          SkShader::kMirror_TileMode);
         SkPaint paint;
         paint.setAlpha(0x80);
         paint.setShader(s)->unref();
         canvas->drawPaint(paint);
     }
-    
+
 private:
     typedef GM INHERITED;
 };
diff --git a/gm/twopointradial.cpp b/gm/twopointradial.cpp
new file mode 100644
index 0000000..a10f5af
--- /dev/null
+++ b/gm/twopointradial.cpp
@@ -0,0 +1,111 @@
+/*
+ * 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);
+
+    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 skiagm::make_isize(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
new file mode 100644
index 0000000..2217863
--- /dev/null
+++ b/gm/typeface.cpp
@@ -0,0 +1,83 @@
+
+/*
+ * 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 "SkString.h"
+#include "SkTypeface.h"
+#include "SkTypes.h"
+
+namespace skiagm {
+
+const char* gFaces[] = {
+    "Times Roman",
+    "Hiragino Maru Gothic Pro",
+    "Papyrus",
+    "Helvetica",
+    "Courier New"
+};
+
+class TypefaceGM : public GM {
+public:
+    TypefaceGM() {
+        fFaces = new SkTypeface*[SK_ARRAY_COUNT(gFaces)];
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gFaces); i++) {
+            fFaces[i] = SkTypeface::CreateFromName(gFaces[i], SkTypeface::kNormal);
+        }
+    }
+
+    virtual ~TypefaceGM() {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gFaces); i++) {
+            fFaces[i]->unref();
+        }
+        delete [] fFaces;
+    }
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("typeface");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        SkString text("Typefaces are fun!");
+        SkScalar y = 0;
+
+        SkPaint paint;
+        paint.setAntiAlias(true);
+        for (size_t i = 0; i < SK_ARRAY_COUNT(gFaces); i++) {
+            this->drawWithFace(text, i, y, paint, canvas);
+        }
+        // Now go backwards
+        for (int i = SK_ARRAY_COUNT(gFaces) - 1; i >= 0; i--) {
+            this->drawWithFace(text, i, y, paint, canvas);
+        }
+    }
+
+private:
+    void drawWithFace(const SkString& text, int i, SkScalar& y, SkPaint& paint,
+                      SkCanvas* canvas) {
+        paint.setTypeface(fFaces[i]);
+        y += paint.getFontMetrics(NULL);
+        canvas->drawText(text.c_str(), text.size(), 0, y, paint);
+    }
+
+    SkTypeface** fFaces;
+
+    typedef GM INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+static GM* MyFactory(void*) { return new TypefaceGM; }
+static GMRegistry reg(MyFactory);
+
+}   // skiagm
diff --git a/gm/valgrind.supp b/gm/valgrind.supp
new file mode 100644
index 0000000..f13fa69
--- /dev/null
+++ b/gm/valgrind.supp
@@ -0,0 +1,16 @@
+# Pass this file to Valgrind with "--suppressions=gm/valgrind.supp"
+# to avoid reporting errors inside the driver.
+
+{
+    nVidiaDriverUninitializedJump001
+    Memcheck:Cond
+    obj:/usr/lib/nvidia-current/libnvidia-glcore.so.280.13
+}
+{
+   nVidiaDriverLeak001
+   Memcheck:Leak
+   fun:malloc
+   obj:/usr/lib/nvidia-current/libGL.so.280.13
+}
+
+
diff --git a/gm/verttext.cpp b/gm/verttext.cpp
index 0f3cb3b..1471cbc 100644
--- a/gm/verttext.cpp
+++ b/gm/verttext.cpp
@@ -8,16 +8,32 @@
 
 #include "gm.h"
 #include "SkCanvas.h"
+#include "SkTypeface.h"
 
 namespace skiagm {
 
 #define TEXT_SIZE   48
 static const char gText[] = "Hello";
+
+//Before shaping
+//static const char gText[] = "「テスト。」";
+//static const char gText[] = {0xE3,0x80,0x8C, 0xE3,0x83,0x86, 0xE3,0x82,0xB9, 0xE3,0x83,0x88, 0xE3,0x80,0x82, 0xE3,0x80,0x8D, 0x0};
+
+//After shaping
+//static const char gText[] = "﹁テスト︒﹂";
+//static const char gText[] = {0xEF,0xB9,0x81, 0xE3,0x83,0x86, 0xE3,0x82,0xB9, 0xE3,0x83,0x88, 0xEF,0xB8,0x92, 0xEF,0xB9,0x82, 0x0};
+
 static const size_t gLen = sizeof(gText) - 1;
 
 class VertTextGM : public GM {
 public:
-    VertTextGM() {}
+    VertTextGM()
+        // : fFace(SkTypeface::CreateFromName("unifont", SkTypeface::kNormal))
+        // : fFace(SkTypeface::CreateFromFile("MotoyaL04Mincho_3.ttf"))
+    {
+    }
+
+    //SkAutoTUnref<SkTypeface> fFace;
 
 protected:
 
@@ -56,12 +72,13 @@
     virtual void onDraw(SkCanvas* canvas) {
         SkScalar x = SkIntToScalar(100);
         SkScalar y = SkIntToScalar(50);
-        
+
         for (int i = 0; i < 4; ++i) {
-            SkPaint     paint;
+            SkPaint paint;
             paint.setAntiAlias(true);
             paint.setTextSize(SkIntToScalar(TEXT_SIZE));
-
+            //paint.setTypeface(fFace);
+            //paint.setFakeBoldText(true);
 
             paint.setVerticalText(false);
             drawBaseline(canvas, paint, x, y);
@@ -70,7 +87,7 @@
             paint.setVerticalText(true);
             drawBaseline(canvas, paint, x, y);
             canvas->drawText(gText, gLen, x, y, paint);
-            
+
             x += SkIntToScalar(40);
             y += SkIntToScalar(120);
 
diff --git a/gm/verttext2.cpp b/gm/verttext2.cpp
index 3bfb471..33380e0 100644
--- a/gm/verttext2.cpp
+++ b/gm/verttext2.cpp
@@ -21,8 +21,13 @@
     VertText2GM() {
         const int pointSize = 24;
         textHeight = SkIntToScalar(pointSize);
-        prop = SkTypeface::CreateFromName("Helvetica", SkTypeface::kNormal);
-        mono = SkTypeface::CreateFromName("Courier New", SkTypeface::kNormal);
+        fProp = SkTypeface::CreateFromName("Helvetica", SkTypeface::kNormal);
+        fMono = SkTypeface::CreateFromName("Courier New", SkTypeface::kNormal);
+    }
+
+    virtual ~VertText2GM() {
+        SkSafeUnref(fProp);
+        SkSafeUnref(fMono);
     }
 
 protected:
@@ -34,7 +39,7 @@
     SkISize onISize() { return make_isize(640, 480); }
 
     virtual void onDraw(SkCanvas* canvas) {
-        
+
         for (int i = 0; i < 3; ++i) {
             SkPaint paint;
             paint.setColor(SK_ColorRED);
@@ -47,13 +52,13 @@
             canvas->drawLine(0, SkIntToScalar(470),
                     SkIntToScalar(110), SkIntToScalar(470), paint);
             drawText(canvas, SkString("Proportional / Top Aligned"),
-                     prop,  SkPaint::kLeft_Align);
+                     fProp,  SkPaint::kLeft_Align);
             drawText(canvas, SkString("<   Proportional / Centered   >"),
-                     prop,  SkPaint::kCenter_Align);
+                     fProp,  SkPaint::kCenter_Align);
             drawText(canvas, SkString("Monospaced / Top Aligned"),
-                     mono, SkPaint::kLeft_Align);
+                     fMono, SkPaint::kLeft_Align);
             drawText(canvas, SkString("<    Monospaced / Centered    >"),
-                     mono, SkPaint::kCenter_Align);
+                     fMono, SkPaint::kCenter_Align);
             canvas->rotate(SkIntToScalar(-15));
             canvas->translate(textHeight * 4, SkIntToScalar(50));
             if (i > 0) {
@@ -72,16 +77,17 @@
         paint.setTypeface(family);
         paint.setTextSize(textHeight);
 
-        canvas->drawText(string.c_str(), string.size(), y, 
-                alignment == SkPaint::kLeft_Align ? 10 : 240, paint);
+        canvas->drawText(string.c_str(), string.size(), y,
+                SkIntToScalar(alignment == SkPaint::kLeft_Align ? 10 : 240),
+                paint);
         y += textHeight;
     }
 
 private:
     typedef GM INHERITED;
     SkScalar y, textHeight;
-    SkTypeface* prop;
-    SkTypeface* mono;
+    SkTypeface* fProp;
+    SkTypeface* fMono;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/gm/verylargebitmap.cpp b/gm/verylargebitmap.cpp
new file mode 100644
index 0000000..4704a86
--- /dev/null
+++ b/gm/verylargebitmap.cpp
@@ -0,0 +1,89 @@
+/*
+ * 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 "SkPath.h"
+
+static void make_bm(SkBitmap* bm, int width, int height, SkColor color) {
+    bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    bm->allocPixels();
+    bm->eraseColor(color);
+    bm->setImmutable();
+}
+
+static void show_bm(SkCanvas* canvas, int width, int height, SkColor color) {
+    SkBitmap bm;
+    make_bm(&bm, width, height, color);
+
+    SkPaint paint;
+    SkRect r;
+    SkIRect ir;
+
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    ir.set(0, 0, 128, 128);
+    r.set(ir);
+
+    canvas->save();
+    canvas->clipRect(r);
+    canvas->drawBitmap(bm, 0, 0, NULL);
+    canvas->restore();
+    canvas->drawRect(r, paint);
+
+    r.offset(SkIntToScalar(150), 0);
+    // exercises extract bitmap, but not shader
+    canvas->drawBitmapRect(bm, &ir, r, NULL);
+    canvas->drawRect(r, paint);
+
+    r.offset(SkIntToScalar(150), 0);
+    // exercises bitmapshader
+    canvas->drawBitmapRect(bm, NULL, r, NULL);
+    canvas->drawRect(r, paint);
+}
+
+class VeryLargeBitmapGM : public skiagm::GM {
+public:
+    VeryLargeBitmapGM() {}
+
+protected:
+    virtual SkString onShortName() SK_OVERRIDE {
+        return SkString("verylargebitmap");
+    }
+
+    virtual SkISize onISize() SK_OVERRIDE {
+        return SkISize::Make(640, 480);
+    }
+
+    virtual void onDraw(SkCanvas* canvas) SK_OVERRIDE {
+        int veryBig = 100*1024; // 64K < size
+        int big = 60*1024;      // 32K < size < 64K
+        int small = 300;
+
+        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
+        show_bm(canvas, small, small, SK_ColorRED);
+        canvas->translate(0, SkIntToScalar(150));
+
+        show_bm(canvas, big, small, SK_ColorBLUE);
+        canvas->translate(0, SkIntToScalar(150));
+
+        // as of this writing, the raster code will fail to draw the scaled version
+        // since it has a 64K limit on x,y coordinates... (but gpu should succeed)
+        show_bm(canvas, veryBig, small, SK_ColorGREEN);
+    }
+
+private:
+    typedef skiagm::GM INHERITED;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+
+// This GM allocates more memory than Android devices are capable of fulfilling.
+#ifndef SK_BUILD_FOR_ANDROID
+static skiagm::GM* MyFactory(void*) { return new VeryLargeBitmapGM; }
+static skiagm::GMRegistry reg(MyFactory);
+#endif
diff --git a/gm/xfermodes.cpp b/gm/xfermodes.cpp
index 42b8a6a..9eb2f87 100644
--- a/gm/xfermodes.cpp
+++ b/gm/xfermodes.cpp
@@ -15,33 +15,39 @@
 static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
     src->setConfig(SkBitmap::kARGB_8888_Config, w, h);
     src->allocPixels();
-    src->eraseColor(0);
+    src->eraseColor(SK_ColorTRANSPARENT);
 
-    SkCanvas c(*src);
     SkPaint p;
+    p.setAntiAlias(true);
+
     SkRect r;
     SkScalar ww = SkIntToScalar(w);
     SkScalar hh = SkIntToScalar(h);
 
-    p.setAntiAlias(true);
-    p.setColor(0xFFFFCC44);
-    r.set(0, 0, ww*3/4, hh*3/4);
-    c.drawOval(r, p);
+    {
+        SkCanvas c(*src);
+        p.setColor(0xFFFFCC44);
+        r.set(0, 0, ww*3/4, hh*3/4);
+        c.drawOval(r, p);
+    }
 
     dst->setConfig(SkBitmap::kARGB_8888_Config, w, h);
     dst->allocPixels();
-    dst->eraseColor(0);
-    c.setBitmapDevice(*dst);
+    dst->eraseColor(SK_ColorTRANSPARENT);
 
-    p.setColor(0xFF66AAFF);
-    r.set(ww/3, hh/3, ww*19/20, hh*19/20);
-    c.drawRect(r, p);
+    {
+        SkCanvas c(*dst);
+        p.setColor(0xFF66AAFF);
+        r.set(ww/3, hh/3, ww*19/20, hh*19/20);
+        c.drawRect(r, p);
+    }
 }
 
+static uint16_t gData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
+
 class XfermodesGM : public GM {
     SkBitmap    fBG;
     SkBitmap    fSrcB, fDstB;
-    bool        fOnce;
 
     void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
                    SkScalar x, SkScalar y) {
@@ -53,25 +59,18 @@
         canvas->drawBitmap(fDstB, x, y, &p);
     }
 
-    void init() {
-        if (!fOnce) {
-            // Do all this work in a temporary so we get a deep copy
-            uint16_t localData[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
-            SkBitmap scratchBitmap;
-            scratchBitmap.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
-            scratchBitmap.setPixels(localData);
-            scratchBitmap.setIsOpaque(true);
-            scratchBitmap.copyTo(&fBG, SkBitmap::kARGB_4444_Config);
-            
-            make_bitmaps(W, H, &fSrcB, &fDstB);
-            fOnce = true;
-        }
+    virtual void onOnceBeforeDraw() SK_OVERRIDE {
+        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
+        fBG.setPixels(gData);
+        fBG.setIsOpaque(true);
+
+        make_bitmaps(W, H, &fSrcB, &fDstB);
     }
 
 public:
     const static int W = 64;
     const static int H = 64;
-    XfermodesGM() : fOnce(false) {}
+    XfermodesGM() {}
 
 protected:
     virtual SkString onShortName() {
@@ -83,8 +82,6 @@
     }
 
     virtual void onDraw(SkCanvas* canvas) {
-        this->init();
-
         canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
 
         const struct {
@@ -105,7 +102,7 @@
             { SkXfermode::kXor_Mode,      "Xor"       },
 
             { SkXfermode::kPlus_Mode,         "Plus"          },
-            { SkXfermode::kMultiply_Mode,     "Multiply"      },
+            { SkXfermode::kModulate_Mode,     "Modulate"      },
             { SkXfermode::kScreen_Mode,       "Screen"        },
             { SkXfermode::kOverlay_Mode,      "Overlay"       },
             { SkXfermode::kDarken_Mode,       "Darken"        },
diff --git a/gyp/FileReaderApp.gyp b/gyp/FileReaderApp.gyp
index c5651c7..bcd0f0d 100644
--- a/gyp/FileReaderApp.gyp
+++ b/gyp/FileReaderApp.gyp
@@ -1,13 +1,10 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'FileReaderApp',
       'type': 'executable',
       'mac_bundle' : 1,
-      
+
       'include_dirs' : [
         '../include/pipe',
         '../experimental/FileReaderApp',
@@ -21,11 +18,8 @@
         '../src/utils/mac/SkOSWindow_Mac.cpp',
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
         'effects.gyp:effects',
-        'opts.gyp:opts',
-        'ports.gyp:ports',
-        'utils.gyp:utils',
         'views.gyp:views',
         'xml.gyp:xml',
       ],
diff --git a/gyp/SampleApp.gyp b/gyp/SampleApp.gyp
index 1f97dc6..8d833dd 100644
--- a/gyp/SampleApp.gyp
+++ b/gyp/SampleApp.gyp
@@ -1,18 +1,15 @@
-
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'SampleApp',
       'type': 'executable',
       'mac_bundle' : 1,
       'include_dirs' : [
-        '../src/core', # needed to get SkConcaveToTriangle, maybe this should be moved to include dir?
+        '../src/core',
+        '../src/effects', #needed for BlurMask.h
         '../gm',       # needed to pull gm.h
-        '../include/pipe', # To pull in SkGPipe.h for pipe reader/writer
         '../samplecode', # To pull SampleApp.h and SampleCode.h
+        '../src/pipe/utils', # For TiledPipeController
       ],
       'includes': [
         'gmslides.gypi',
@@ -24,13 +21,14 @@
         '../samplecode/GMSampleView.h',
         '../samplecode/ClockFaceView.cpp',
         '../samplecode/OverView.cpp',
+        '../samplecode/OverView.h',
         '../samplecode/Sample2PtRadial.cpp',
         '../samplecode/SampleAAClip.cpp',
-        '../samplecode/SampleAAClip2.cpp',
         '../samplecode/SampleAARects.cpp',
         '../samplecode/SampleAARectModes.cpp',
         '../samplecode/SampleAll.cpp',
         '../samplecode/SampleAnimator.cpp',
+        '../samplecode/SampleAnimBlur.cpp',
         '../samplecode/SampleApp.cpp',
         '../samplecode/SampleArc.cpp',
         '../samplecode/SampleAvoid.cpp',
@@ -50,12 +48,11 @@
         '../samplecode/SampleDegenerateTwoPtRadials.cpp',
         '../samplecode/SampleDither.cpp',
         '../samplecode/SampleDitherBitmap.cpp',
-        '../samplecode/SampleDrawBitmap.cpp',
-        '../samplecode/SampleDrawLooper.cpp',
         '../samplecode/SampleEffects.cpp',
         '../samplecode/SampleEmboss.cpp',
         '../samplecode/SampleEmptyPath.cpp',
         '../samplecode/SampleEncode.cpp',
+        '../samplecode/SampleFatBits.cpp',
         '../samplecode/SampleFillType.cpp',
         '../samplecode/SampleFilter.cpp',
         '../samplecode/SampleFilter2.cpp',
@@ -66,32 +63,29 @@
         '../samplecode/SampleHairCurves.cpp',
         '../samplecode/SampleHairline.cpp',
         '../samplecode/SampleHairModes.cpp',
-        '../samplecode/SampleImage.cpp',
-        '../samplecode/SampleImageDir.cpp',
         '../samplecode/SampleLayerMask.cpp',
         '../samplecode/SampleLayers.cpp',
         '../samplecode/SampleLCD.cpp',
-        '../samplecode/SampleLineClipper.cpp',
         '../samplecode/SampleLines.cpp',
+        '../samplecode/SampleManyRects.cpp',
         '../samplecode/SampleMeasure.cpp',
         '../samplecode/SampleMipMap.cpp',
         '../samplecode/SampleMovie.cpp',
-        '../samplecode/SampleNinePatch.cpp',
         '../samplecode/SampleOvalTest.cpp',
         '../samplecode/SampleOverflow.cpp',
-        '../samplecode/SamplePageFlip.cpp',
         '../samplecode/SamplePatch.cpp',
         '../samplecode/SamplePath.cpp',
         '../samplecode/SamplePathClip.cpp',
         '../samplecode/SamplePathEffects.cpp',
         '../samplecode/SamplePicture.cpp',
+        '../samplecode/SamplePictFile.cpp',
         '../samplecode/SamplePoints.cpp',
         '../samplecode/SamplePolyToPoly.cpp',
         '../samplecode/SampleRegion.cpp',
         '../samplecode/SampleRepeatTile.cpp',
+        '../samplecode/SampleRotateCircles.cpp',
         '../samplecode/SampleShaders.cpp',
         '../samplecode/SampleShaderText.cpp',
-        '../samplecode/SampleShapes.cpp',
         '../samplecode/SampleSkLayer.cpp',
         '../samplecode/SampleSlides.cpp',
         '../samplecode/SampleStrokePath.cpp',
@@ -100,23 +94,17 @@
         '../samplecode/SampleText.cpp',
         '../samplecode/SampleTextAlpha.cpp',
         '../samplecode/SampleTextBox.cpp',
-        '../samplecode/SampleTextEffects.cpp',
         '../samplecode/SampleTextOnPath.cpp',
         '../samplecode/SampleTextureDomain.cpp',
         '../samplecode/SampleTiling.cpp',
         '../samplecode/SampleTinyBitmap.cpp',
-        '../samplecode/SampleTriangles.cpp',
         '../samplecode/SampleTypeface.cpp',
         '../samplecode/SampleUnitMapper.cpp',
         '../samplecode/SampleVertices.cpp',
-        '../samplecode/SampleXfermodes.cpp',
         '../samplecode/SampleXfermodesBlur.cpp',
         '../samplecode/TransitionView.cpp',
-        
-        # Dependencies for the pipe code in SampleApp
-        '../src/pipe/SkGPipeRead.cpp',
-        '../src/pipe/SkGPipeWrite.cpp',
-        
+        '../samplecode/TransitionView.h',
+
         # DrawingBoard
         #'../experimental/DrawingBoard/SkColorPalette.h',
         #'../experimental/DrawingBoard/SkColorPalette.cpp',
@@ -124,19 +112,15 @@
         #'../experimental/DrawingBoard/SkNetPipeController.cpp',
         #'../experimental/DrawingBoard/SampleDrawingClient.cpp',
         #'../experimental/DrawingBoard/SampleDrawingServer.cpp',
-    
+
         # Networking
         #'../experimental/Networking/SampleNetPipeReader.cpp',
         #'../experimental/Networking/SkSockets.cpp',
         #'../experimental/Networking/SkSockets.h',
-        
-        # Debugger
-        '../experimental/Debugger/DebuggerViews.h',
-        '../experimental/Debugger/DebuggerContentView.cpp',
-        '../experimental/Debugger/DebuggerCommandsView.cpp',
-        '../experimental/Debugger/DebuggerStateView.cpp',
-        '../experimental/Debugger/SkDebugDumper.cpp',
-        '../experimental/Debugger/SkDebugDumper.h',
+
+        # TiledPipeController
+        '../src/pipe/utils/SamplePipeControllers.h',
+        '../src/pipe/utils/SamplePipeControllers.cpp',
       ],
       'sources!': [
         '../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile
@@ -145,18 +129,15 @@
         '../samplecode/SampleFontCache.cpp',
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
         'effects.gyp:effects',
         'images.gyp:images',
-        'ports.gyp:ports',
         'views.gyp:views',
-        'utils.gyp:utils',
         'animator.gyp:animator',
         'xml.gyp:xml',
         'experimental.gyp:experimental',
-        'gpu.gyp:gr',
-        'gpu.gyp:skgr',
         'pdf.gyp:pdf',
+        'views_animated.gyp:views_animated',
       ],
       'conditions' : [
        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
@@ -168,7 +149,6 @@
           'sources!': [
             # require UNIX functions
             '../samplecode/SampleEncode.cpp',
-            '../samplecode/SamplePageFlip.cpp',
           ],
         }],
         [ 'skia_os == "mac"', {
@@ -177,33 +157,33 @@
           ],
           'sources': [
             # Sample App specific files
-            '../src/utils/mac/SampleApp-Info.plist',
-            '../src/utils/mac/SampleAppDelegate.h',
-            '../src/utils/mac/SampleAppDelegate.mm',
-            '../src/utils/mac/SkSampleNSView.h',
-            '../src/utils/mac/SkSampleNSView.mm',
-            
+            '../src/views/mac/SampleApp-Info.plist',
+            '../src/views/mac/SampleAppDelegate.h',
+            '../src/views/mac/SampleAppDelegate.mm',
+            '../src/views/mac/SkSampleNSView.h',
+            '../src/views/mac/SkSampleNSView.mm',
+
             # Mac files
-            '../src/utils/mac/SkEventNotifier.h',
-            '../src/utils/mac/SkEventNotifier.mm',
-            '../src/utils/mac/skia_mac.mm',
-            '../src/utils/mac/SkNSView.h',
-            '../src/utils/mac/SkNSView.mm',
-            '../src/utils/mac/SkOptionsTableView.h',
-            '../src/utils/mac/SkOptionsTableView.mm',
-            '../src/utils/mac/SkOSWindow_Mac.mm',
-            '../src/utils/mac/SkTextFieldCell.h',
-            '../src/utils/mac/SkTextFieldCell.m',
+            '../src/views/mac/SkEventNotifier.h',
+            '../src/views/mac/SkEventNotifier.mm',
+            '../src/views/mac/skia_mac.mm',
+            '../src/views/mac/SkNSView.h',
+            '../src/views/mac/SkNSView.mm',
+            '../src/views/mac/SkOptionsTableView.h',
+            '../src/views/mac/SkOptionsTableView.mm',
+            '../src/views/mac/SkOSWindow_Mac.mm',
+            '../src/views/mac/SkTextFieldCell.h',
+            '../src/views/mac/SkTextFieldCell.m',
           ],
           'libraries': [
             '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
             '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
           ],
           'xcode_settings' : {
-            'INFOPLIST_FILE' : '../src/utils/mac/SampleApp-Info.plist',
+            'INFOPLIST_FILE' : '../src/views/mac/SampleApp-Info.plist',
           },
           'mac_bundle_resources' : [
-            '../src/utils/mac/SampleApp.xib',
+            '../src/views/mac/SampleApp.xib',
           ],
         }],
         [ 'skia_os == "ios"', {
@@ -212,35 +192,72 @@
             '../samplecode/SampleDecode.cpp',
           ],
           'sources': [
-            '../experimental/iOSSampleApp/SkIOSNotifier.mm',
-            '../experimental/iOSSampleApp/SkTime_iOS.mm',
-            '../experimental/iOSSampleApp/SkUIDetailViewController.mm',
-            '../experimental/iOSSampleApp/SkUIRootViewController.mm',
-            '../experimental/iOSSampleApp/SkUIView_shell.mm',
+            '../src/views/mac/SkEventNotifier.h',
+            '../src/views/mac/SkEventNotifier.mm',
+            '../experimental/iOSSampleApp/SkSampleUIView.h',
+            '../experimental/iOSSampleApp/SkSampleUIView.mm',
+            '../experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig',
+            '../experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig',
+            '../experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig',
+            '../experimental/iOSSampleApp/iOSSampleApp-Info.plist',
+            '../experimental/iOSSampleApp/Shared/SkOptionListController.h',
+            '../experimental/iOSSampleApp/Shared/SkOptionListController.mm',
+            '../experimental/iOSSampleApp/Shared/SkUIRootViewController.h',
+            '../experimental/iOSSampleApp/Shared/SkUIRootViewController.mm',
+            '../experimental/iOSSampleApp/Shared/SkOptionsTableViewController.h',
+            '../experimental/iOSSampleApp/Shared/SkOptionsTableViewController.mm',
+            '../experimental/iOSSampleApp/Shared/SkUIView.h',
+            '../experimental/iOSSampleApp/Shared/SkUIView.mm',
+            '../experimental/iOSSampleApp/Shared/SkUIDetailViewController.h',
+            '../experimental/iOSSampleApp/Shared/SkUIDetailViewController.mm',
+            '../experimental/iOSSampleApp/Shared/skia_ios.mm',
 
-            '../experimental/iOSSampleApp/iOSSampleApp_Prefix.pch',
-            '../experimental/iOSSampleApp/Shared/main.m',
+            # iPad
+            '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.h',
             '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.mm',
+            '../experimental/iOSSampleApp/iPad/SkUISplitViewController.h',
             '../experimental/iOSSampleApp/iPad/SkUISplitViewController.mm',
-            '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm',
-            '../experimental/iOSSampleApp/iPhone/SkUINavigationController.mm',
+            '../experimental/iOSSampleApp/iPad/MainWindow_iPad.xib',
 
-            '../src/utils/ios/SkOSWindow_iOS.mm',
+            # iPhone
+            '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h',
+            '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm',
+            '../experimental/iOSSampleApp/iPhone/SkUINavigationController.h',
+            '../experimental/iOSSampleApp/iPhone/SkUINavigationController.mm',
+            '../experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib',
+
+            '../src/views/ios/SkOSWindow_iOS.mm',
             '../src/utils/ios/SkImageDecoder_iOS.mm',
             '../src/utils/ios/SkStream_NSData.mm',
             '../src/utils/ios/SkOSFile_iOS.mm',
 
+            '../include/utils/mac/SkCGUtils.h',
             '../src/utils/mac/SkCreateCGImageRef.cpp',
             '../experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig',
             '../experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig',
           ],
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+              '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+              '$(SDKROOT)/System/Library/Frameworks/CoreText.framework',
+              '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+              '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+              '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
+              '$(SDKROOT)/System/Library/Frameworks/OpenGLES.framework',
+              '$(SDKROOT)/System/Library/Frameworks/ImageIO.framework',
+              '$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
+            ],
+          },
           'include_dirs' : [
             '../experimental/iOSSampleApp',
             '../experimental/iOSSampleApp/iPad',
             '../experimental/iOSSampleApp/iPhone',
             '../include/utils/ios',
-            '../../include/gpu',
           ],
+          'xcode_settings' : {
+            'INFOPLIST_FILE' : '../experimental/iOSSampleApp/iOSSampleApp-Info.plist',
+          },
           'xcode_config_file': '../experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig',
           'mac_bundle_resources' : [
             '../experimental/iOSSampleApp/iPad/MainWindow_iPad.xib',
@@ -257,16 +274,23 @@
             'experimental.gyp:experimental',
           ],
           'dependencies': [
-            'android_system.gyp:sampleApp',
+            'android_deps.gyp:Android_SampleApp',
+          ],
+        }],
+        [ 'skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu', # To pull gl/GrGLUtil.h
+          ],
+        }],
+        [ 'skia_os == "nacl"', {
+          'sources': [
+            '../../nacl/src/nacl_sample.cpp',
           ],
         }],
       ],
       'msvs_settings': {
         'VCLinkerTool': {
           'SubSystem': '2',
-          'AdditionalDependencies': [
-            'd3d9.lib',
-          ],
         },
       },
     },
diff --git a/gyp/SimpleCocoaApp.gyp b/gyp/SimpleCocoaApp.gyp
index ed35d54..3e17ad5 100644
--- a/gyp/SimpleCocoaApp.gyp
+++ b/gyp/SimpleCocoaApp.gyp
@@ -1,7 +1,4 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'SimpleCocoaApp',
@@ -11,26 +8,24 @@
         '../experimental/SimpleCocoaApp/',
       ],
       'sources': [
-        '../src/utils/mac/SkEventNotifier.h',
-        '../src/utils/mac/SkEventNotifier.mm',
-        '../src/utils/mac/skia_mac.mm',
-        '../src/utils/mac/SkNSView.h',
-        '../src/utils/mac/SkNSView.mm',
-        '../src/utils/mac/SkOptionsTableView.h',
-        '../src/utils/mac/SkOptionsTableView.mm',
-        '../src/utils/mac/SkOSWindow_Mac.mm',
-        '../src/utils/mac/SkTextFieldCell.h',
-        '../src/utils/mac/SkTextFieldCell.m',
-        
+        '../src/views/mac/SkEventNotifier.h',
+        '../src/views/mac/SkEventNotifier.mm',
+        '../src/views/mac/skia_mac.mm',
+        '../src/views/mac/SkNSView.h',
+        '../src/views/mac/SkNSView.mm',
+        '../src/views/mac/SkOptionsTableView.h',
+        '../src/views/mac/SkOptionsTableView.mm',
+        '../src/views/mac/SkOSWindow_Mac.mm',
+        '../src/views/mac/SkTextFieldCell.h',
+        '../src/views/mac/SkTextFieldCell.m',
+
         '../experimental/SimpleCocoaApp/SimpleApp-Info.plist',
         '../experimental/SimpleCocoaApp/SimpleApp.h',
         '../experimental/SimpleCocoaApp/SimpleApp.mm',
-        
+
       ],
       'dependencies': [
-        'core.gyp:core',
-        'opts.gyp:opts',
-        'utils.gyp:utils',
+        'skia_base_libs.gyp:skia_base_libs',
         'views.gyp:views',
         'xml.gyp:xml',
       ],
diff --git a/gyp/SimpleiOSApp.gyp b/gyp/SimpleiOSApp.gyp
new file mode 100644
index 0000000..3678e7c
--- /dev/null
+++ b/gyp/SimpleiOSApp.gyp
@@ -0,0 +1,71 @@
+{
+  'conditions' : [
+    [ 'skia_os != "ios"', {
+      'error': '<!(set GYP_DEFINES=\"skia_os=\'ios\'\")'
+    }],
+  ],
+  'targets': [
+    {
+      'target_name': 'SimpleiOSApp',
+      'type': 'executable',
+      'mac_bundle' : 1,
+      'include_dirs' : [
+        '../experimental/iOSSampleApp/Shared',
+      ],
+      'sources': [
+        '../src/views/ios/SkOSWindow_iOS.mm',
+        '../src/views/mac/SkEventNotifier.h',
+        '../src/views/mac/SkEventNotifier.mm',
+        '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.h',
+        '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.mm',
+        '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h',
+        '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm',
+        '../experimental/iOSSampleApp/Shared/SkUIView.h',
+        '../experimental/iOSSampleApp/Shared/SkUIView.mm',
+        '../experimental/iOSSampleApp/Shared/skia_ios.mm',
+        '../experimental/SimpleiOSApp/SimpleApp.h',
+        '../experimental/SimpleiOSApp/SimpleApp.mm',
+        '../experimental/SimpleiOSApp/SimpleiOSApp-Info.plist',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        'views.gyp:views',
+        'xml.gyp:xml',
+      ],
+      'link_settings': {
+        'libraries': [
+          '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+          '$(SDKROOT)/System/Library/Frameworks/CoreText.framework',
+          '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+          '$(SDKROOT)/System/Library/Frameworks/ImageIO.framework',
+          '$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
+          '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+        ],
+        'libraries!': [
+          #remove mac dependencies
+          '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+          '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+          '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
+          '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
+          '$(SDKROOT)/System/Library/Frameworks/ApplicationServices.framework',
+        ],
+      },
+      'xcode_settings' : {
+        'INFOPLIST_FILE' : '../experimental/SimpleiOSApp/SimpleiOSApp-Info.plist',
+      },
+      'xcode_config_file': '../experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig',
+      'mac_bundle_resources' : [
+        '../experimental/SimpleiOSApp/iPad/MainWindow_iPad.xib',
+        '../experimental/SimpleiOSApp/iPhone/MainWindow_iPhone.xib',
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/all.gyp b/gyp/all.gyp
deleted file mode 100644
index 37844bb..0000000
--- a/gyp/all.gyp
+++ /dev/null
@@ -1,41 +0,0 @@
-# Creates a Makefile that is capable of building all executable targets.
-#
-# To build on Linux:
-#  ./gyp_skia && make all
-#
-# Building on other platforms not tested yet.
-#
-
-#
-#
-#
-#
-#
-# THIS IS DEPRECATED IN FAVOR OF trunk/skia.gyp !!!
-# Questions? Contact epoger@google.com
-#
-#
-#
-#
-
-{
-  'targets': [
-    {
-      'target_name': 'all',
-      'type': 'none',
-      'dependencies': [
-        'bench.gyp:bench',
-        'gm.gyp:gm',
-        'SampleApp.gyp:SampleApp',
-        'tests.gyp:tests',
-        'tools.gyp:tools',
-      ],
-    },
-  ],
-}
-
-# Local Variables:
-# tab-width:2
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/android_deps.gyp b/gyp/android_deps.gyp
new file mode 100644
index 0000000..ea7bbf2
--- /dev/null
+++ b/gyp/android_deps.gyp
@@ -0,0 +1,20 @@
+# 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.
+#
+# We tried adding this gyp file to the android directory at the root of
+# the Skia repo, but that resulted in the generated makefiles being created
+# outside of the intended output directory. So to avoid this we created a simple
+# shim here that includes the android_deps.gypi file.  The actual dependencies
+# are defined and maintained in that gypi file.
+#
+# Also this assumes that the android directory is a sibling to the directory
+# that contains your primary Skia checkout. If it is not then you must manually
+# edit the includes below to specify the actual location of the android.gypi.
+# This is due to the fact that we cannot use variables in an includes as the
+# variable expansion step for gyp happens after the includes are processed.
+{
+  'includes': [
+    '../../android/gyp/dependencies.gypi',
+  ],
+}
diff --git a/gyp/android_system.gyp b/gyp/android_system.gyp
index 27a82f3..57f973a 100644
--- a/gyp/android_system.gyp
+++ b/gyp/android_system.gyp
@@ -1,21 +1,20 @@
 # 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.  
+# distribution so we have to build a few of them ourselves.
 #
-# We tried adding this gyp file to the android directory at the root of 
+# We tried adding this gyp file to the android directory at the root of
 # the Skia repo, but that resulted in the generated makefiles being created
 # outside of the intended output directory. So to avoid this we created a simple
 # shim here that includes the android_system.gypi file.  The actual dependencies
-# are defined and maintained in that gypi file.  
-# 
+# are defined and maintained in that gypi file.
+#
 # Also this assumes that the android directory is a sibling to the directory
 # that contains your primary Skia checkout. If it is not then you must manually
 # edit the includes below to specify the actual location of the android.gypi.
 # This is due to the fact that we cannot use variables in an includes as the
-# variable expansion step for gyp happens after the includes are processed. 
+# variable expansion step for gyp happens after the includes are processed.
 {
   'includes': [
-    'common.gypi',
-    '../../android/gyp/android.gypi',
+    '../../android/gyp/skia_android.gypi',
   ],
-}
\ No newline at end of file
+}
diff --git a/gyp/angle.gyp b/gyp/angle.gyp
new file mode 100644
index 0000000..088895b
--- /dev/null
+++ b/gyp/angle.gyp
@@ -0,0 +1,41 @@
+# ANGLE is the Windows-specific translator from OGL ES 2.0 to D3D 9
+
+{
+  'conditions': [
+    [ 'skia_angle', {
+      'target_defaults': {
+        'defines': [
+          'NOMINMAX',
+        ],
+      },
+      'variables': {
+        'component': 'static_library',
+            'skia_building_angle': 1, # See comment in common_conditions.gypi.
+      },
+      'includes': [
+        '../third_party/externals/angle/src/build_angle.gypi',
+      ],
+    }],
+  ],
+  'targets': [
+    {
+      'target_name': 'angle',
+      'type': 'none',
+      'conditions': [
+        [ 'skia_angle', {
+          'direct_dependent_settings': {
+            'include_dirs': [
+              '../third_party/externals/angle/include',
+            ],
+          },
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/animator.gyp b/gyp/animator.gyp
index f713f3f..942e849 100644
--- a/gyp/animator.gyp
+++ b/gyp/animator.gyp
@@ -1,11 +1,14 @@
+#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.
+
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'animator',
+      'product_name': 'skia_animator',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'include_dirs': [
         '../include/config',
         '../include/core',
diff --git a/gyp/apptype_console.gypi b/gyp/apptype_console.gypi
index f2f6e1c..a306193 100644
--- a/gyp/apptype_console.gypi
+++ b/gyp/apptype_console.gypi
@@ -11,6 +11,67 @@
         'EntryPointSymbol': 'mainCRTStartup',
       },
     },
+    'conditions': [
+      [ 'skia_os == "android"', {
+        'dependencies': [
+          'android_deps.gyp:Android_EntryPoint',
+        ],
+      }],
+      [ 'skia_os == "nacl"', {
+        'dependencies': [
+          'nacl.gyp:nacl_interface',
+        ],
+      }],
+      ['skia_os == "ios"', {
+        'target_conditions': [
+          ['_type == "executable"', {
+            'mac_bundle' : 1,
+          }],
+        ],
+        'include_dirs' : [
+          '../experimental/iOSSampleApp/Shared',
+          '../include/views',
+          '../include/xml',
+          '../include/utils/mac',
+        ],
+        'sources': [
+          '../src/views/ios/SkOSWindow_iOS.mm',
+          '../src/views/mac/SkEventNotifier.h',
+          '../src/views/mac/SkEventNotifier.mm',
+          '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.h',
+          '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.mm',
+          '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h',
+          '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm',
+          '../experimental/iOSSampleApp/Shared/SkUIView.h',
+          '../experimental/iOSSampleApp/Shared/SkUIView.mm',
+          '../experimental/iOSSampleApp/Shared/skia_ios.mm',
+          '../experimental/SimpleiOSApp/SimpleApp.h',
+          '../experimental/SimpleiOSApp/SimpleApp.mm',
+        ],
+        'dependencies': [
+          'views.gyp:views',
+          'xml.gyp:xml',
+        ],
+        'link_settings': {
+          'libraries': [
+            '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+            '$(SDKROOT)/System/Library/Frameworks/CoreText.framework',
+            '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+            '$(SDKROOT)/System/Library/Frameworks/ImageIO.framework',
+            '$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
+            '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+          ],
+        },
+        'xcode_config_file': '../experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig',
+        'mac_bundle_resources' : [
+          '../experimental/SimpleiOSApp/iPad/MainWindow_iPad.xib',
+          '../experimental/SimpleiOSApp/iPhone/MainWindow_iPhone.xib',
+        ],
+        'xcode_settings' : {
+          'INFOPLIST_FILE' : '../experimental/SimpleiOSApp/tool-Info.plist',
+        },
+      }],
+    ],
   },
 }
 
diff --git a/gyp/bench.gyp b/gyp/bench.gyp
index 0c5e2ea..be768f0 100644
--- a/gyp/bench.gyp
+++ b/gyp/bench.gyp
@@ -3,7 +3,6 @@
 {
   'includes': [
     'apptype_console.gypi',
-    'common.gypi',
   ],
   'targets': [
     {
@@ -11,21 +10,82 @@
       'type': 'executable',
       'include_dirs' : [
         '../src/core',
-        '../src/gpu',
+        '../src/effects',
+        '../src/utils',
       ],
       'includes': [
         'bench.gypi'
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
         'effects.gyp:effects',
-        'gpu.gyp:gr',
-        'gpu.gyp:skgr',
         'images.gyp:images',
-        'ports.gyp:ports',
-        'utils.gyp:utils',
+        'bench_timer',
+      ],
+      'conditions': [
+        ['skia_gpu == 1',
+          {
+            'include_dirs' : [
+              '../src/gpu',
+            ],
+          },
+        ],
       ],
     },
+    {
+      'target_name' : 'bench_timer',
+      'type': 'static_library',
+      'sources': [
+        '../bench/BenchTimer.h',
+        '../bench/BenchTimer.cpp',
+        '../bench/BenchSysTimer_mach.h',
+        '../bench/BenchSysTimer_mach.cpp',
+        '../bench/BenchSysTimer_posix.h',
+        '../bench/BenchSysTimer_posix.cpp',
+        '../bench/BenchSysTimer_windows.h',
+        '../bench/BenchSysTimer_windows.cpp',
+      ],
+        'include_dirs': [
+        '../src/core',
+        '../src/gpu',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+      ],
+      'conditions': [
+        [ 'skia_os not in ["mac", "ios"]', {
+          'sources!': [
+            '../bench/BenchSysTimer_mach.h',
+            '../bench/BenchSysTimer_mach.cpp',
+          ],
+        }],
+        [ 'skia_os not in ["linux", "freebsd", "openbsd", "solaris", "android"]', {
+          'sources!': [
+            '../bench/BenchSysTimer_posix.h',
+            '../bench/BenchSysTimer_posix.cpp',
+          ],
+        }],
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+          'link_settings': {
+            'libraries': [
+              '-lrt',
+            ],
+          },
+        }],
+        [ 'skia_os != "win"', {
+          'sources!': [
+            '../bench/BenchSysTimer_windows.h',
+            '../bench/BenchSysTimer_windows.cpp',
+          ],
+        }],
+        ['skia_gpu == 1', {
+          'sources': [
+            '../bench/BenchGpuTimer_gl.h',
+            '../bench/BenchGpuTimer_gl.cpp',
+          ],
+        }],
+      ],
+    }
   ],
 }
 
diff --git a/gyp/bench.gypi b/gyp/bench.gypi
index 2ea3660..cf5c02e 100644
--- a/gyp/bench.gypi
+++ b/gyp/bench.gypi
@@ -3,65 +3,55 @@
 {
   'sources': [
     '../bench/benchmain.cpp',
-    '../bench/BenchTimer.h',
-    '../bench/BenchTimer.cpp',
-    '../bench/BenchSysTimer_mach.h',
-    '../bench/BenchSysTimer_mach.cpp',
-    '../bench/BenchSysTimer_posix.h',
-    '../bench/BenchSysTimer_posix.cpp',
-    '../bench/BenchSysTimer_windows.h',
-    '../bench/BenchSysTimer_windows.cpp',
-    '../bench/BenchGpuTimer_gl.h',
-    '../bench/BenchGpuTimer_gl.cpp',
-
     '../bench/SkBenchmark.h',
     '../bench/SkBenchmark.cpp',
 
     '../bench/AAClipBench.cpp',
     '../bench/BitmapBench.cpp',
+    '../bench/BitmapRectBench.cpp',
     '../bench/BlurBench.cpp',
+    '../bench/BlurRectBench.cpp',
+    '../bench/ChecksumBench.cpp',
     '../bench/ChromeBench.cpp',
+    '../bench/DashBench.cpp',
     '../bench/DecodeBench.cpp',
+    '../bench/DeferredCanvasBench.cpp',
     '../bench/FontScalerBench.cpp',
     '../bench/GradientBench.cpp',
+    '../bench/GrMemoryPoolBench.cpp',
+    '../bench/InterpBench.cpp',
+    '../bench/LineBench.cpp',
     '../bench/MathBench.cpp',
+    '../bench/Matrix44Bench.cpp',
     '../bench/MatrixBench.cpp',
+    '../bench/MatrixConvolutionBench.cpp',
+    '../bench/MemoryBench.cpp',
+    '../bench/MorphologyBench.cpp',
     '../bench/MutexBench.cpp',
     '../bench/PathBench.cpp',
+    '../bench/PathIterBench.cpp',
     '../bench/PicturePlaybackBench.cpp',
+    '../bench/PictureRecordBench.cpp',
+    '../bench/ReadPixBench.cpp',
     '../bench/RectBench.cpp',
+    '../bench/RefCntBench.cpp',
+    '../bench/RegionBench.cpp',
+    '../bench/RegionContainBench.cpp',
     '../bench/RepeatTileBench.cpp',
+    '../bench/RTreeBench.cpp',
     '../bench/ScalarBench.cpp',
     '../bench/ShaderMaskBench.cpp',
+    '../bench/SortBench.cpp',
+    '../bench/TableBench.cpp',
     '../bench/TextBench.cpp',
+    '../bench/TileBench.cpp',
     '../bench/VertBench.cpp',
-  ],
-  'conditions': [
-    [ 'skia_os != "mac"', {
-      'sources!': [
-        '../bench/BenchSysTimer_mach.h',
-        '../bench/BenchSysTimer_mach.cpp',
-      ],
-    }],
-    [ 'skia_os not in ["linux", "freebsd", "openbsd", "solaris", "android"]', {
-      'sources!': [
-        '../bench/BenchSysTimer_posix.h',
-        '../bench/BenchSysTimer_posix.cpp',
-      ],
-    }],
-    [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
-      'link_settings': {
-        'libraries': [
-          '-lrt',
-        ],
-      },
-    }],
-    [ 'skia_os != "win"', {
-      'sources!': [
-        '../bench/BenchSysTimer_windows.h',
-        '../bench/BenchSysTimer_windows.cpp',
-      ],
-    }],
+    '../bench/WriterBench.cpp',
+
+    '../bench/SkBenchLogger.h',
+    '../bench/SkBenchLogger.cpp',
+    '../bench/TimerData.h',
+    '../bench/TimerData.cpp',
   ],
 }
 
diff --git a/gyp/common.gypi b/gyp/common.gypi
index 30424de..8be06e3 100644
--- a/gyp/common.gypi
+++ b/gyp/common.gypi
@@ -2,10 +2,14 @@
 #
 # Use of this source code is governed by a BSD-style license that can be
 # found in the LICENSE file.
+#
+# This file is automatically included by gyp_skia when building any target.
+
 {
   'includes': [
     'common_variables.gypi',
   ],
+
   'target_defaults': {
 
     # Validate the 'skia_os' setting against 'OS', because only certain
@@ -13,14 +17,26 @@
     # situations, like building for iOS on a Mac.
     'variables': {
       'conditions': [
-        ['skia_os != OS and not (skia_os == "ios" and OS == "mac")',
-          {'error': '<!(Cannot build with skia_os=<(skia_os) on OS=<(OS))'}],
-        ['skia_mesa and skia_os not in ["mac", "linux"]',
-          {'error': '<!(skia_mesa=1 only supported with skia_os="mac" or "linux".)'}],
+        [ 'skia_os != OS and not ((skia_os == "ios" and OS == "mac") or \
+                                  (skia_os == "nacl" and OS == "linux"))', {
+          'error': '<!(Cannot build with skia_os=<(skia_os) on OS=<(OS))',
+        }],
+        [ 'skia_mesa and skia_os not in ["mac", "linux"]', {
+          'error': '<!(skia_mesa=1 only supported with skia_os="mac" or "linux".)',
+        }],
+        [ 'skia_angle and not skia_os == "win"', {
+          'error': '<!(skia_angle=1 only supported with skia_os="win".)',
+        }],
+        [ '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.)',
+        }],
       ],
     },
     'includes': [
-      'common_conditions.gypi'
+      'common_conditions.gypi',
     ],
     'conditions': [
       [ 'skia_scalar == "float"',
@@ -46,12 +62,23 @@
           ],
         },
       }],
+      [ 'skia_angle', {
+        'defines': [
+          'SK_ANGLE',
+        ],
+        'direct_dependent_settings': {
+          'defines': [
+            'SK_ANGLE',
+          ],
+        },
+      }],
     ],
     'configurations': {
       'Debug': {
         'defines': [
           'SK_DEBUG',
           'GR_DEBUG=1',
+          'SK_DEVELOPER=1',
         ],
       },
       'Release': {
@@ -60,6 +87,12 @@
           'GR_RELEASE=1',
         ],
       },
+      'Release_Developer': {
+        'inherit_from': ['Release'],
+        'defines': [
+          'SK_DEVELOPER=1',
+        ],
+      },
     },
   }, # end 'target_defaults'
 }
diff --git a/gyp/common_conditions.gypi b/gyp/common_conditions.gypi
index 7152181..d61cc94 100644
--- a/gyp/common_conditions.gypi
+++ b/gyp/common_conditions.gypi
@@ -1,20 +1,34 @@
 # conditions used in both common.gypi and skia.gyp in chromium
 #
 {
+  'defines': [
+    'SK_ALLOW_STATIC_GLOBAL_INITIALIZERS=<(skia_static_initializers)',
+#    'SK_SUPPORT_HINTING_SCALE_FACTOR',
+  ],
   'conditions' : [
-
+    ['skia_gpu == 1',
+      {
+        'defines': [
+          'SK_SUPPORT_GPU=1',
+        ],
+      }, {
+        'defines': [
+          'SK_SUPPORT_GPU=0',
+        ],
+      },
+    ],
     ['skia_os == "win"',
       {
         'defines': [
           'SK_BUILD_FOR_WIN32',
           'SK_IGNORE_STDINT_DOT_H',
           '_CRT_SECURE_NO_WARNINGS',
+          'GR_GL_FUNCTION_TYPE=__stdcall',
         ],
         'msvs_cygwin_shell': 0,
         'msvs_settings': {
           'VCCLCompilerTool': {
             'WarningLevel': '1',
-            'WarnAsError': 'false',
             'DebugInformationFormat': '3',
             'AdditionalOptions': [ '/MP' ],
           },
@@ -47,18 +61,19 @@
           'Release': {
             'msvs_settings': {
               'VCCLCompilerTool': {
-                'DebugInformationFormat': '3',   # programDatabase (/Zi)
+                'DebugInformationFormat': '3',      # programDatabase (/Zi)
                 'ProgramDataBaseFileName': '$(OutDir)\\$(ProjectName).pdb',
-                'Optimization': '3',             # full (/Ox)
+                'Optimization': '3',                # full (/Ox)
                 'WholeProgramOptimization': 'true', #/GL
                # Changing the floating point model requires rebaseling gm images
-               #'FloatingPointModel': '2',       # fast (/fp:fast)
-                'FavorSizeOrSpeed': '1',         # speed (/Ot)
+               #'FloatingPointModel': '2',          # fast (/fp:fast)
+                'FavorSizeOrSpeed': '1',            # speed (/Ot)
                 'PreprocessorDefinitions': ['NDEBUG'],
-                'RuntimeLibrary': '2',           # rtMultiThreadedDLL (/MD)
+                'RuntimeLibrary': '2',              # rtMultiThreadedDLL (/MD)
                 'ExceptionHandling': '0',
-                'RuntimeTypeInfo': 'false',      # /GR-
-                'WarningLevel': '3',             # level3 (/W3)
+                'EnableEnhancedInstructionSet': '2',# /arch:SSE2
+                'RuntimeTypeInfo': 'false',         # /GR-
+                'WarningLevel': '3',                # level3 (/W3)
               },
               'VCLinkerTool': {
                 'GenerateDebugInformation': 'true', # /DEBUG
@@ -70,23 +85,57 @@
             },
           },
         },
+        'conditions' : [
+          ['skia_arch_width == 64', {
+            'msvs_configuration_platform': 'x64',
+            'msvs_settings': {
+              'VCCLCompilerTool': {
+                'WarnAsError': 'false',
+              },
+            },
+          }],
+          ['skia_arch_width == 32', {
+            # This gypi file will be included directly into the gyp(i) files in the angle repo by
+            # our gyp_skia script. We don't want force WarnAsError on angle. So angle.gyp defines
+            # skia_building_angle=1 and here we select whether to enable WarnAsError based on that
+            # var's value. Here it defaults to 0.
+            'variables' : {
+              'skia_building_angle%': 0,
+            },
+            'conditions' : [
+              ['skia_building_angle', {
+                'msvs_configuration_platform': 'Win32',
+                'msvs_settings': {
+                  'VCCLCompilerTool': {
+                    'WarnAsError': 'false',
+                  },
+                },
+              },{ # not angle
+                'msvs_configuration_platform': 'Win32',
+                'msvs_settings': {
+                  'VCCLCompilerTool': {
+                    'WarnAsError': 'true',
+                  },
+                },
+              }],
+            ],
+          }],
+        ],
       },
     ],
 
-    ['skia_os in ["linux", "freebsd", "openbsd", "solaris"]',
+    ['skia_os in ["linux", "freebsd", "openbsd", "solaris", "nacl"]',
       {
         'defines': [
           'SK_SAMPLES_FOR_X',
           'SK_BUILD_FOR_UNIX',
-          'SK_USE_COLOR_LUMINANCE',
-          'SK_GAMMA_APPLY_TO_A8',
         ],
         'configurations': {
           'Debug': {
             'cflags': ['-g']
           },
           'Release': {
-            'cflags': ['-O2'],
+            'cflags': ['-O3 -g'],
             'defines': [ 'NDEBUG' ],
           },
         },
@@ -101,17 +150,69 @@
           '-Wno-unused-parameter',
           '-Wno-c++11-extensions'
         ],
-        'include_dirs' : [
-          '/usr/include/freetype2',
+        'conditions' : [
+          ['skia_warnings_as_errors == 1', {
+            'cflags': [
+              '-Werror',
+            ],
+          }],
+          ['skia_arch_width == 64', {
+            'cflags': [
+              '-m64',
+            ],
+            'ldflags': [
+              '-m64',
+            ],
+          }],
+          ['skia_arch_width == 32', {
+            'cflags': [
+              '-m32',
+            ],
+            'ldflags': [
+              '-m32',
+            ],
+          }],
+          [ 'skia_os == "nacl"', {
+            'defines': [
+              'SK_BUILD_FOR_NACL',
+            ],
+            'link_settings': {
+              'libraries': [
+                '-lppapi',
+                '-lppapi_cpp',
+                '-lnosys',
+                '-pthread',
+              ],
+            },
+          }, { # skia_os != "nacl"
+            'include_dirs' : [
+              '/usr/include/freetype2',
+            ],
+          }],
         ],
       },
     ],
 
-    ['skia_os == "mac"', 
+    ['skia_os == "mac"',
       {
         'defines': [
           'SK_BUILD_FOR_MAC',
         ],
+        'conditions' : [
+          ['skia_arch_width == 64', {
+            'xcode_settings': {
+              'ARCHS': 'x86_64',
+            },
+          }],
+          ['skia_arch_width == 32', {
+            'xcode_settings': {
+              'ARCHS': 'i386',
+              'OTHER_CPLUSPLUSFLAGS': [
+                '-Werror',
+              ],
+            },
+          }],
+        ],
         'configurations': {
           'Debug': {
             'xcode_settings': {
@@ -126,16 +227,39 @@
           },
         },
         'xcode_settings': {
-          'SYMROOT': '<(DEPTH)/xcodebuild',
-          'SDKROOT': 'macosx10.6',
+          'GCC_SYMBOLS_PRIVATE_EXTERN': 'NO',
+          'SDKROOT': '<(skia_osx_sdkroot)',
 # trying to get this to work, but it needs clang I think...
 #          'WARNING_CFLAGS': '-Wexit-time-destructors',
           'CLANG_WARN_CXX0X_EXTENSIONS': 'NO',
+          'GCC_WARN_64_TO_32_BIT_CONVERSION': 'YES',
+          'GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS': 'YES',
+          'GCC_WARN_ABOUT_INVALID_OFFSETOF_MACRO': 'YES',
+          'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES',
+          'GCC_WARN_ABOUT_MISSING_PROTOTYPES': 'YES',
+          'GCC_WARN_ABOUT_POINTER_SIGNEDNESS': 'YES',
+          'GCC_WARN_ABOUT_RETURN_TYPE': 'YES',
+          'GCC_WARN_ALLOW_INCOMPLETE_PROTOCOL': 'YES',
+          'GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED': 'YES',
+          'GCC_WARN_MISSING_PARENTHESES': 'YES',
+          'GCC_WARN_PROTOTYPE_CONVERSION': 'YES',
+          'GCC_WARN_SIGN_COMPARE': 'YES',
+          'GCC_WARN_TYPECHECK_CALLS_TO_PRINTF': 'YES',
+          'GCC_WARN_UNKNOWN_PRAGMAS': 'YES',
+          'GCC_WARN_UNUSED_FUNCTION': 'YES',
+          'GCC_WARN_UNUSED_LABEL': 'YES',
+          'GCC_WARN_UNUSED_VALUE': 'YES',
+          'GCC_WARN_UNUSED_VARIABLE': 'YES',
+          'OTHER_CPLUSPLUSFLAGS': [
+            '-mssse3',
+            '-fvisibility=hidden',
+            '-fvisibility-inlines-hidden',
+          ],
         },
       },
     ],
 
-    ['skia_os == "ios"', 
+    ['skia_os == "ios"',
       {
         'defines': [
           'SK_BUILD_FOR_IOS',
@@ -146,19 +270,31 @@
               'GCC_OPTIMIZATION_LEVEL': '0',
             },
           },
+          'Release': {
+            'xcode_settings': {
+              'GCC_OPTIMIZATION_LEVEL': '3',
+            },
+            'defines': [ 'NDEBUG' ],
+          },
         },
         'xcode_settings': {
-          'SYMROOT': '<(DEPTH)/xcodebuild',
+          'ARCHS': 'armv6 armv7',
+          'CODE_SIGNING_REQUIRED': 'NO',
+          'CODE_SIGN_IDENTITY[sdk=iphoneos*]': '',
+          'IPHONEOS_DEPLOYMENT_TARGET': '<(ios_sdk_version)',
+          'SDKROOT': 'iphoneos',
+          'TARGETED_DEVICE_FAMILY': '1,2',
+          'OTHER_CPLUSPLUSFLAGS': '-fvisibility=hidden -fvisibility-inlines-hidden',
+          'GCC_THUMB_SUPPORT': 'NO',
         },
       },
     ],
-    
-    ['skia_os == "android"', 
+
+    ['skia_os == "android"',
       {
         'defines': [
           'SK_BUILD_FOR_ANDROID',
           'SK_BUILD_FOR_ANDROID_NDK',
-          'SK_ALLOW_STATIC_GLOBAL_INITIALIZERS=0',
         ],
         'configurations': {
           'Debug': {
@@ -177,20 +313,26 @@
         'cflags': [
           '-fno-exceptions',
           '-fno-rtti',
+          '-fuse-ld=gold',
         ],
         'conditions': [
-          [ 'skia_target_arch == "arm"', {
-            'ldflags': [
-              '-Wl',
-              '--fix-cortex-a8',
+          [ 'skia_warnings_as_errors == 1', {
+            'cflags': [
+              '-Werror',
             ],
           }],
-          [ 'skia_target_arch == "arm" and arm_thumb == 1', {
+          [ 'skia_profile_enabled == 1', {
+            'cflags': ['-g', '-fno-omit-frame-pointer', '-marm', '-mapcs'],
+          }],
+          [ 'skia_arch_type == "arm" and arm_thumb == 1', {
             'cflags': [
               '-mthumb',
             ],
           }],
-          [ 'skia_target_arch == "arm" and armv7 == 1', {
+          [ 'skia_arch_type == "arm" and armv7 == 1', {
+            'variables': {
+              'arm_neon_optional%': 0,
+            },
             'defines': [
               '__ARM_ARCH__=7',
             ],
@@ -206,10 +348,19 @@
                 'cflags': [
                   '-mfpu=neon',
                 ],
-             }],
+                'ldflags': [
+                  '-march=armv7-a',
+                  '-Wl,--fix-cortex-a8',
+                ],
+              }],
+              [ 'arm_neon_optional == 1', {
+                'defines': [
+                  '__ARM_HAVE_OPTIONAL_NEON_SUPPORT',
+                ],
+              }],
             ],
          }],
-        ], 
+        ],
       },
     ],
 
@@ -217,11 +368,14 @@
     # static initializers if we're using a pthread-compatible thread interface.
     [ 'skia_os != "win"', {
       'defines': [
-        'SK_USE_POSIX_THREADS'
+        'SK_USE_POSIX_THREADS',
       ],
     }],
-
   ], # end 'conditions'
+  # The Xcode SYMROOT must be at the root. See build/common.gypi in chromium for more details
+  'xcode_settings': {
+    'SYMROOT': '<(DEPTH)/xcodebuild',
+  },
 }
 
 # Local Variables:
diff --git a/gyp/common_variables.gypi b/gyp/common_variables.gypi
index 94e2b9b..5aca19c 100644
--- a/gyp/common_variables.gypi
+++ b/gyp/common_variables.gypi
@@ -1,23 +1,128 @@
-# variables used in both common.gypi and skia.gyp in chromium
+# Copyright 2012 The Android Open Source Project
 #
-{
-  # Define all variables, allowing for override in GYP_DEFINES.
-  #
-  # One such variable is 'skia_os', which we use instead of 'OS' throughout
-  # our gyp files.  We set it automatically based on 'OS', but allow the
-  # user to override it via GYP_DEFINES if they like.
-  'variables': {
-    'skia_scalar%': 'float',
-    'skia_os%': '<(OS)',
-    'skia_mesa%': 0,
-    'skia_target_arch%': '',
-  },
-  'skia_scalar%': '<(skia_scalar)',
-  'skia_os': '<(skia_os)',
-  'skia_mesa': '<(skia_mesa)',
-  'skia_target_arch': '<(skia_target_arch)',
-}
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
 
+{
+  # Get ready for the ugly...
+  #
+  # - We have to nest our variables dictionaries multiple levels deep, so that
+  #   this and other gyp files can rely on previously-set variable values in
+  #   their 'variables': { 'conditions': [] } clauses.
+  #
+  #   Example 1:
+  #   Within this file, we use the value of variable 'skia_os' to set the
+  #   value of variable 'os_posix', so 'skia_os' must be defined within a
+  #   "more inner" (enclosed) scope than 'os_posix'.
+  #
+  #   Example 2:
+  #   http://src.chromium.org/viewvc/chrome/trunk/src/third_party/libjpeg/libjpeg.gyp?revision=102306 ,
+  #   which we currently import into our build, uses the value of 'os_posix'
+  #   within the 'conditions' list in its 'variables' dict.
+  #   In order for that to work, it needs the value of 'os_posix' to have been
+  #   set within a "more inner" (enclosed) scope than its own 'variables' dict.
+  #
+  # - On the other hand, key/value pairs of a given 'variable' dict are only
+  #   inherited by:
+  #   1. directly enclosing 'variable' dicts, and
+  #   2. "sibling" 'variable' dicts (which, I guess, are combined into a single
+  #      'variable' dict during gyp processing)
+  #   and NOT inherited by "uncles" (siblings of directly enclosing 'variable'
+  #   dicts), so we have to re-define every variable at every enclosure level
+  #   within our ridiculous matryoshka doll of 'variable' dicts.  That's why
+  #   we have variable definitions like this:  'skia_os%': '<(skia_os)',
+  #
+  # See http://src.chromium.org/viewvc/chrome/trunk/src/build/common.gypi?revision=127004 ,
+  # which deals with these same constraints in a similar manner.
+  #
+  'variables': {  # level 1
+    'variables': {  # level 2
+
+      # Variables needed by conditions list within the level-2 variables dict.
+      'variables': {  # level 3
+        # We use 'skia_os' instead of 'OS' throughout our gyp files, to allow
+        # for cross-compilation (e.g. building for either MacOS or iOS on Mac).
+        # We set it automatically based on 'OS' (the host OS), but allow the
+        # user to override it via GYP_DEFINES if they like.
+        'skia_os%': '<(OS)',
+      },
+
+      # Re-define all variables defined within the level-3 'variables' dict,
+      # so that siblings of the level-2 'variables' dict can see them.
+      'skia_os%': '<(skia_os)',
+
+      'conditions': [
+        [ 'skia_os == "win"', {
+          'os_posix%': 0,
+        }, {
+          'os_posix%': 1,
+        }],
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+          'skia_arch_width%': 64,
+        }, {
+          'skia_arch_width%': 32,
+        }],
+        [ 'skia_os == "android"', {
+          'skia_static_initializers%': 0,
+        }, {
+          'skia_static_initializers%': 1,
+        }],
+        [ 'skia_os == "ios"', {
+          'skia_arch_type%': 'arm',
+          'armv7%': 1,
+          'arm_neon%': 0, # neon asm files known not to work with the ios build
+        },{ # skia_os is not ios
+          'skia_arch_type%': 'x86',
+          'armv7%': 0,
+          'arm_neon%': 0,
+        }],
+      ],
+
+      'skia_scalar%': 'float',
+      'skia_mesa%': 0,
+      'skia_nv_path_rendering%': 0,
+      'skia_stroke_path_rendering%': 0,
+      'skia_android_path_rendering%': 0,
+      'skia_texture_cache_mb_limit%': 0,
+      'skia_angle%': 0,
+      'skia_directwrite%': 0,
+      'skia_gpu%': 1,
+      'skia_osx_sdkroot%': 'macosx',
+      'skia_profile_enabled%': 0,
+      # Note: This is currently only turned on for linux and android.
+      # TODO: Turn on for Win and Mac as well.
+      'skia_warnings_as_errors%': 0,
+    },
+
+    # Re-define all variables defined within the level-2 'variables' dict,
+    # so that siblings of the level-1 'variables' dict can see them.
+    'armv7%': '<(armv7)',
+    'arm_neon%': '<(arm_neon)',
+    'skia_os%': '<(skia_os)',
+    'os_posix%': '<(os_posix)',
+    'skia_scalar%': '<(skia_scalar)',
+    'skia_mesa%': '<(skia_mesa)',
+    'skia_nv_path_rendering%': '<(skia_nv_path_rendering)',
+    'skia_stroke_path_rendering%': '<(skia_stroke_path_rendering)',
+    'skia_android_path_rendering%': '<(skia_android_path_rendering)',
+    'skia_texture_cache_mb_limit%': '<(skia_texture_cache_mb_limit)',
+    'skia_angle%': '<(skia_angle)',
+    'skia_arch_width%': '<(skia_arch_width)',
+    'skia_arch_type%': '<(skia_arch_type)',
+    'skia_directwrite%': '<(skia_directwrite)',
+    'skia_gpu%': '<(skia_gpu)',
+    'skia_osx_sdkroot%': '<(skia_osx_sdkroot)',
+    'skia_profile_enabled%': '<(skia_profile_enabled)',
+    'skia_warnings_as_errors%': '<(skia_warnings_as_errors)',
+    'skia_static_initializers%': '<(skia_static_initializers)',
+    'ios_sdk_version%': '6.0',
+
+    # These are referenced by our .gypi files that list files (e.g. core.gypi)
+    #
+    'skia_src_path%': '../src',
+    'skia_include_path%': '../include',
+  },
+}
 # Local Variables:
 # tab-width:2
 # indent-tabs-mode:nil
diff --git a/gyp/core.gyp b/gyp/core.gyp
index 077d395..7e7640c 100644
--- a/gyp/core.gyp
+++ b/gyp/core.gyp
@@ -1,249 +1,29 @@
 # Core Skia library code.
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'core',
+      'product_name': 'skia_core',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'msvs_guid': 'B7760B5E-BFA8-486B-ACFD-49E3A6DE8E76',
-      'sources': [
-        '../src/core/ARGB32_Clamp_Bilinear_BitmapShader.h',
-        '../src/core/Sk64.cpp',
-        '../src/core/SkAAClip.cpp',
-        '../src/core/SkAdvancedTypefaceMetrics.cpp',
-        '../src/core/SkAlphaRuns.cpp',
-        '../src/core/SkAntiRun.h',
-        '../src/core/SkBitmap.cpp',
-        '../src/core/SkBitmapProcShader.cpp',
-        '../src/core/SkBitmapProcShader.h',
-        '../src/core/SkBitmapProcState.cpp',
-        '../src/core/SkBitmapProcState.h',
-        '../src/core/SkBitmapProcState_matrix.h',
-        '../src/core/SkBitmapProcState_matrixProcs.cpp',
-        '../src/core/SkBitmapProcState_sample.h',
-        '../src/core/SkBitmapSampler.cpp',
-        '../src/core/SkBitmapSampler.h',
-        '../src/core/SkBitmapSamplerTemplate.h',
-        '../src/core/SkBitmapShader16BilerpTemplate.h',
-        '../src/core/SkBitmapShaderTemplate.h',
-        '../src/core/SkBitmap_scroll.cpp',
-        '../src/core/SkBlitBWMaskTemplate.h',
-        '../src/core/SkBlitMask_D32.cpp',
-        '../src/core/SkBlitRow_D16.cpp',
-        '../src/core/SkBlitRow_D32.cpp',
-        '../src/core/SkBlitRow_D4444.cpp',
-        '../src/core/SkBlitter.cpp',
-        '../src/core/SkBlitter_4444.cpp',
-        '../src/core/SkBlitter_A1.cpp',
-        '../src/core/SkBlitter_A8.cpp',
-        '../src/core/SkBlitter_ARGB32.cpp',
-        '../src/core/SkBlitter_RGB16.cpp',
-        '../src/core/SkBlitter_Sprite.cpp',
-        '../src/core/SkBuffer.cpp',
-        '../src/core/SkCanvas.cpp',
-        '../src/core/SkChunkAlloc.cpp',
-        '../src/core/SkClampRange.cpp',
-        '../src/core/SkClipStack.cpp',
-        '../src/core/SkColor.cpp',
-        '../src/core/SkColorFilter.cpp',
-        '../src/core/SkColorTable.cpp',
-        '../src/core/SkComposeShader.cpp',
-        '../src/core/SkConcaveToTriangles.cpp',
-        '../src/core/SkConcaveToTriangles.h',
-        '../src/core/SkConfig8888.cpp',
-        '../src/core/SkConfig8888.h',
-        '../src/core/SkCordic.cpp',
-        '../src/core/SkCordic.h',
-        '../src/core/SkCoreBlitters.h',
-        '../src/core/SkCubicClipper.cpp',
-        '../src/core/SkCubicClipper.h',
-        '../src/core/SkData.cpp',
-        '../src/core/SkDebug.cpp',
-        '../src/core/SkDeque.cpp',
-        '../src/core/SkDevice.cpp',
-        '../src/core/SkDeviceProfile.cpp',
-        '../src/core/SkDither.cpp',
-        '../src/core/SkDraw.cpp',
-        '../src/core/SkDrawProcs.h',
-        '../src/core/SkEdgeBuilder.cpp',
-        '../src/core/SkEdgeClipper.cpp',
-        '../src/core/SkEdge.cpp',
-        '../src/core/SkEdge.h',
-        '../src/core/SkFP.h',
-        '../src/core/SkFilterProc.cpp',
-        '../src/core/SkFilterProc.h',
-        '../src/core/SkFlattenable.cpp',
-        '../src/core/SkFloat.cpp',
-        '../src/core/SkFloat.h',
-        '../src/core/SkFloatBits.cpp',
-        '../src/core/SkFontHost.cpp',
-        '../src/core/SkGeometry.cpp',
-        '../src/core/SkGlyphCache.cpp',
-        '../src/core/SkGlyphCache.h',
-        '../src/core/SkGraphics.cpp',
-        '../src/core/SkLineClipper.cpp',
-        '../src/core/SkMallocPixelRef.cpp',
-        '../src/core/SkMask.cpp',
-        '../src/core/SkMaskFilter.cpp',
-        '../src/core/SkMath.cpp',
-        '../src/core/SkMatrix.cpp',
-        '../src/core/SkMetaData.cpp',
-        '../src/core/SkMMapStream.cpp',
-        '../src/core/SkPackBits.cpp',
-        '../src/core/SkPaint.cpp',
-        '../src/core/SkPath.cpp',
-        '../src/core/SkPathEffect.cpp',
-        '../src/core/SkPathHeap.cpp',
-        '../src/core/SkPathHeap.h',
-        '../src/core/SkPathMeasure.cpp',
-        '../src/core/SkPicture.cpp',
-        '../src/core/SkPictureFlat.cpp',
-        '../src/core/SkPictureFlat.h',
-        '../src/core/SkPicturePlayback.cpp',
-        '../src/core/SkPicturePlayback.h',
-        '../src/core/SkPictureRecord.cpp',
-        '../src/core/SkPictureRecord.h',
-        '../src/core/SkPixelRef.cpp',
-        '../src/core/SkPoint.cpp',
-        '../src/core/SkProcSpriteBlitter.cpp',
-        '../src/core/SkPtrRecorder.cpp',
-        '../src/core/SkQuadClipper.cpp',
-        '../src/core/SkQuadClipper.h',
-        '../src/core/SkRasterClip.cpp',
-        '../src/core/SkRasterizer.cpp',
-        '../src/core/SkRect.cpp',
-        '../src/core/SkRefDict.cpp',
-        '../src/core/SkRegion.cpp',
-        '../src/core/SkRegionPriv.h',
-        '../src/core/SkRegion_path.cpp',
-        '../src/core/SkScalar.cpp',
-        '../src/core/SkScalerContext.cpp',
-        '../src/core/SkScan.cpp',
-        '../src/core/SkScanPriv.h',
-        '../src/core/SkScan_AntiPath.cpp',
-        '../src/core/SkScan_Antihair.cpp',
-        '../src/core/SkScan_Hairline.cpp',
-        '../src/core/SkScan_Path.cpp',
-        '../src/core/SkShader.cpp',
-        '../src/core/SkShape.cpp',
-        '../src/core/SkSpriteBlitter_ARGB32.cpp',
-        '../src/core/SkSpriteBlitter_RGB16.cpp',
-        '../src/core/SkSinTable.h',
-        '../src/core/SkSpriteBlitter.h',
-        '../src/core/SkSpriteBlitterTemplate.h',
-        '../src/core/SkStream.cpp',
-        '../src/core/SkString.cpp',
-        '../src/core/SkStroke.cpp',
-        '../src/core/SkStrokerPriv.cpp',
-        '../src/core/SkStrokerPriv.h',
-        '../src/core/SkTextFormatParams.h',
-        '../src/core/SkTSearch.cpp',
-        '../src/core/SkTSort.h',
-        '../src/core/SkTemplatesPriv.h',
-        '../src/core/SkTypeface.cpp',
-        '../src/core/SkTypefaceCache.cpp',
-        '../src/core/SkTypefaceCache.h',
-        '../src/core/SkUnPreMultiply.cpp',
-        '../src/core/SkUtils.cpp',
-        '../src/core/SkWriter32.cpp',
-        '../src/core/SkXfermode.cpp',
 
-        '../include/core/Sk64.h',
-        '../include/core/SkAdvancedTypefaceMetrics.h',
-        '../include/core/SkAutoKern.h',
-        '../include/core/SkBitmap.h',
-        '../include/core/SkBlitRow.h',
-        '../include/core/SkBlitter.h',
-        '../include/core/SkBounder.h',
-        '../include/core/SkBuffer.h',
-        '../include/core/SkCanvas.h',
-        '../include/core/SkChunkAlloc.h',
-        '../include/core/SkClampRange.h',
-        '../include/core/SkClipStack.h',
-        '../include/core/SkColor.h',
-        '../include/core/SkColorFilter.h',
-        '../include/core/SkColorPriv.h',
-        '../include/core/SkColorShader.h',
-        '../include/core/SkComposeShader.h',
-        '../include/core/SkData.h',
-        '../include/core/SkDeque.h',
-        '../include/core/SkDescriptor.h',
-        '../include/core/SkDevice.h',
-        '../include/core/SkDither.h',
-        '../include/core/SkDraw.h',
-        '../include/core/SkDrawFilter.h',
-        '../include/core/SkDrawLooper.h',
-        '../include/core/SkEndian.h',
-        '../include/core/SkFDot6.h',
-        '../include/core/SkFixed.h',
-        '../include/core/SkFlattenable.h',
-        '../include/core/SkFloatBits.h',
-        '../include/core/SkFloatingPoint.h',
-        '../include/core/SkFontHost.h',
-        '../include/core/SkGeometry.h',
-        '../include/core/SkGraphics.h',
-        '../include/core/SkMallocPixelRef.h',
-        '../include/core/SkMask.h',
-        '../include/core/SkMaskFilter.h',
-        '../include/core/SkMath.h',
-        '../include/core/SkMatrix.h',
-        '../include/core/SkMetaData.h',
-        '../include/core/SkMMapStream.h',
-        '../include/core/SkOSFile.h',
-        '../include/core/SkPackBits.h',
-        '../include/core/SkPaint.h',
-        '../include/core/SkPath.h',
-        '../include/core/SkPathEffect.h',
-        '../include/core/SkPathMeasure.h',
-        '../include/core/SkPerspIter.h',
-        '../include/core/SkPicture.h',
-        '../include/core/SkPixelRef.h',
-        '../include/core/SkPoint.h',
-        '../include/core/SkPtrRecorder.h',
-        '../include/core/SkRandom.h',
-        '../include/core/SkRasterizer.h',
-        '../include/core/SkReader32.h',
-        '../include/core/SkRect.h',
-        '../include/core/SkRefCnt.h',
-        '../include/core/SkRefDict.h',
-        '../include/core/SkRegion.h',
-        '../include/core/SkScalar.h',
-        '../include/core/SkScalarCompare.h',
-        '../include/core/SkScalerContext.h',
-        '../include/core/SkScan.h',
-        '../include/core/SkShader.h',
-        '../include/core/SkStream.h',
-        '../include/core/SkString.h',
-        '../include/core/SkStroke.h',
-        '../include/core/SkTArray.h',
-        '../include/core/SkTDArray.h',
-        '../include/core/SkTDStack.h',
-        '../include/core/SkTDict.h',
-        '../include/core/SkTRegistry.h',
-        '../include/core/SkTScopedPtr.h',
-        '../include/core/SkTSearch.h',
-        '../include/core/SkTemplates.h',
-        '../include/core/SkThread.h',
-        '../include/core/SkThread_platform.h',
-        '../include/core/SkTime.h',
-        '../include/core/SkTLazy.h',
-        '../include/core/SkTrace.h',
-        '../include/core/SkTypeface.h',
-        '../include/core/SkTypes.h',
-        '../include/core/SkUnPreMultiply.h',
-        '../include/core/SkUnitMapper.h',
-        '../include/core/SkUtils.h',
-        '../include/core/SkWriter32.h',
-        '../include/core/SkXfermode.h',
+      'includes': [
+        'core.gypi',
       ],
+
       'include_dirs': [
         '../include/config',
         '../include/core',
+        '../include/pipe',
         '../include/ports',
+        '../include/utils',
         '../include/xml',
         '../src/core',
+        '../src/image',
+      ],
+      'sources': [
+        'core.gypi', # Makes the gypi appear in IDEs (but does not modify the build).
       ],
       'msvs_disabled_warnings': [4244, 4267,4345, 4390, 4554, 4800],
       'conditions': [
@@ -254,7 +34,6 @@
           ],
           'link_settings': {
             'libraries': [
-              '-lfreetype',
               '-lpthread',
             ],
           },
@@ -282,13 +61,15 @@
           ],
           'link_settings': {
             'libraries': [
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CoreFoundation.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CoreGraphics.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CoreText.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/UIKit.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/Foundation.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/QuartzCore.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/OpenGLES.framework',
+              '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework',
+              '$(SDKROOT)/System/Library/Frameworks/CoreGraphics.framework',
+              '$(SDKROOT)/System/Library/Frameworks/CoreText.framework',
+              '$(SDKROOT)/System/Library/Frameworks/UIKit.framework',
+              '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+              '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
+              '$(SDKROOT)/System/Library/Frameworks/OpenGLES.framework',
+              '$(SDKROOT)/System/Library/Frameworks/ImageIO.framework',
+              '$(SDKROOT)/System/Library/Frameworks/MobileCoreServices.framework',
             ],
           },
         }],
@@ -303,17 +84,49 @@
         }],
         [ 'skia_os == "android"', {
           'dependencies': [
-             'android_system.gyp:ft2',
+             'freetype.gyp:freetype',
           ],
-        }],        
+        }],
+        [ 'skia_os == "android" and skia_arch_type == "arm" and armv7 == 1', {
+          # The code in SkUtilsArm.cpp can be used on an ARM-based Linux system, not only Android.
+          'sources': [
+            '../src/core/SkUtilsArm.cpp',
+            '../src/core/SkUtilsArm.h',
+          ],
+        }],
+        ['skia_gpu == 1', {
+          'include_dirs': [
+              '../include/gpu',
+              '../src/gpu',
+          ],
+        }],
       ],
       'direct_dependent_settings': {
         'include_dirs': [
           'config',
           '../include/config',
           '../include/core',
+          '../include/pipe',
           'ext',
         ],
+        'conditions': [
+          [ 'skia_os == "mac"', {
+            'include_dirs': [
+              '../include/utils/mac',
+              '../third_party/freetype/include/**',
+            ],
+          }],
+          [ 'skia_os == "ios"', {
+            'include_dirs': [
+              '../include/utils/ios',
+            ],
+          }],
+          [ 'skia_os == "win"', {
+            'include_dirs': [
+              'config/win',
+            ],
+          }],
+        ],
       },
       'dependencies': [
         'opts.gyp:opts'
diff --git a/gyp/core.gypi b/gyp/core.gypi
new file mode 100644
index 0000000..d5f76a3
--- /dev/null
+++ b/gyp/core.gypi
@@ -0,0 +1,291 @@
+# Include this gypi to include all 'core' files
+# The parent gyp/gypi file must define
+#       'skia_src_path'     e.g. skia/trunk/src
+#       'skia_include_path' e.g. skia/trunk/include
+#
+# The skia build defines these in common_variables.gypi
+#
+{
+    'sources': [
+        '<(skia_src_path)/core/ARGB32_Clamp_Bilinear_BitmapShader.h',
+        '<(skia_src_path)/core/Sk64.cpp',
+        '<(skia_src_path)/core/SkAAClip.cpp',
+        '<(skia_src_path)/core/SkAnnotation.cpp',
+        '<(skia_src_path)/core/SkAdvancedTypefaceMetrics.cpp',
+        '<(skia_src_path)/core/SkAlphaRuns.cpp',
+        '<(skia_src_path)/core/SkAntiRun.h',
+        '<(skia_src_path)/core/SkBBoxHierarchy.cpp',
+        '<(skia_src_path)/core/SkBBoxHierarchy.h',
+        '<(skia_src_path)/core/SkBBoxRecord.cpp',
+        '<(skia_src_path)/core/SkBBoxRecord.h',
+        '<(skia_src_path)/core/SkBBoxHierarchyRecord.cpp',
+        '<(skia_src_path)/core/SkBBoxHierarchyRecord.h',
+        '<(skia_src_path)/core/SkBitmap.cpp',
+        '<(skia_src_path)/core/SkBitmapHeap.cpp',
+        '<(skia_src_path)/core/SkBitmapHeap.h',
+        '<(skia_src_path)/core/SkBitmapProcShader.cpp',
+        '<(skia_src_path)/core/SkBitmapProcShader.h',
+        '<(skia_src_path)/core/SkBitmapProcState.cpp',
+        '<(skia_src_path)/core/SkBitmapProcState.h',
+        '<(skia_src_path)/core/SkBitmapProcState_matrix.h',
+        '<(skia_src_path)/core/SkBitmapProcState_matrixProcs.cpp',
+        '<(skia_src_path)/core/SkBitmapProcState_sample.h',
+        '<(skia_src_path)/core/SkBitmapSampler.cpp',
+        '<(skia_src_path)/core/SkBitmapSampler.h',
+        '<(skia_src_path)/core/SkBitmapSamplerTemplate.h',
+        '<(skia_src_path)/core/SkBitmapShader16BilerpTemplate.h',
+        '<(skia_src_path)/core/SkBitmapShaderTemplate.h',
+        '<(skia_src_path)/core/SkBitmap_scroll.cpp',
+        '<(skia_src_path)/core/SkBlitBWMaskTemplate.h',
+        '<(skia_src_path)/core/SkBlitMask_D32.cpp',
+        '<(skia_src_path)/core/SkBlitRow_D16.cpp',
+        '<(skia_src_path)/core/SkBlitRow_D32.cpp',
+        '<(skia_src_path)/core/SkBlitRow_D4444.cpp',
+        '<(skia_src_path)/core/SkBlitter.h',
+        '<(skia_src_path)/core/SkBlitter.cpp',
+        '<(skia_src_path)/core/SkBlitter_4444.cpp',
+        '<(skia_src_path)/core/SkBlitter_A1.cpp',
+        '<(skia_src_path)/core/SkBlitter_A8.cpp',
+        '<(skia_src_path)/core/SkBlitter_ARGB32.cpp',
+        '<(skia_src_path)/core/SkBlitter_RGB16.cpp',
+        '<(skia_src_path)/core/SkBlitter_Sprite.cpp',
+        '<(skia_src_path)/core/SkBuffer.cpp',
+        '<(skia_src_path)/core/SkCanvas.cpp',
+        '<(skia_src_path)/core/SkChunkAlloc.cpp',
+        '<(skia_src_path)/core/SkClipStack.cpp',
+        '<(skia_src_path)/core/SkColor.cpp',
+        '<(skia_src_path)/core/SkColorFilter.cpp',
+        '<(skia_src_path)/core/SkColorTable.cpp',
+        '<(skia_src_path)/core/SkComposeShader.cpp',
+        '<(skia_src_path)/core/SkConfig8888.cpp',
+        '<(skia_src_path)/core/SkConfig8888.h',
+        '<(skia_src_path)/core/SkCordic.cpp',
+        '<(skia_src_path)/core/SkCordic.h',
+        '<(skia_src_path)/core/SkCoreBlitters.h',
+        '<(skia_src_path)/core/SkCubicClipper.cpp',
+        '<(skia_src_path)/core/SkCubicClipper.h',
+        '<(skia_src_path)/core/SkData.cpp',
+        '<(skia_src_path)/core/SkDebug.cpp',
+        '<(skia_src_path)/core/SkDeque.cpp',
+        '<(skia_src_path)/core/SkDevice.cpp',
+        '<(skia_src_path)/core/SkDeviceProfile.cpp',
+        '<(skia_src_path)/core/SkDither.cpp',
+        '<(skia_src_path)/core/SkDraw.cpp',
+        '<(skia_src_path)/core/SkDrawProcs.h',
+        '<(skia_src_path)/core/SkEdgeBuilder.cpp',
+        '<(skia_src_path)/core/SkEdgeClipper.cpp',
+        '<(skia_src_path)/core/SkEdge.cpp',
+        '<(skia_src_path)/core/SkEdge.h',
+        '<(skia_src_path)/core/SkFP.h',
+        '<(skia_src_path)/core/SkFilterProc.cpp',
+        '<(skia_src_path)/core/SkFilterProc.h',
+        '<(skia_src_path)/core/SkFlattenable.cpp',
+        '<(skia_src_path)/core/SkFlattenableBuffers.cpp',
+        '<(skia_src_path)/core/SkFloat.cpp',
+        '<(skia_src_path)/core/SkFloat.h',
+        '<(skia_src_path)/core/SkFloatBits.cpp',
+        '<(skia_src_path)/core/SkFontHost.cpp',
+        '<(skia_src_path)/core/SkFontDescriptor.cpp',
+        '<(skia_src_path)/core/SkFontDescriptor.h',
+        '<(skia_src_path)/core/SkGeometry.cpp',
+        '<(skia_src_path)/core/SkGlyphCache.cpp',
+        '<(skia_src_path)/core/SkGlyphCache.h',
+        '<(skia_src_path)/core/SkGraphics.cpp',
+        '<(skia_src_path)/core/SkInstCnt.cpp',
+        '<(skia_src_path)/core/SkImageFilter.cpp',
+        '<(skia_src_path)/core/SkLineClipper.cpp',
+        '<(skia_src_path)/core/SkMallocPixelRef.cpp',
+        '<(skia_src_path)/core/SkMask.cpp',
+        '<(skia_src_path)/core/SkMaskFilter.cpp',
+        '<(skia_src_path)/core/SkMaskGamma.cpp',
+        '<(skia_src_path)/core/SkMaskGamma.h',
+        '<(skia_src_path)/core/SkMath.cpp',
+        '<(skia_src_path)/core/SkMatrix.cpp',
+        '<(skia_src_path)/core/SkMetaData.cpp',
+        '<(skia_src_path)/core/SkMMapStream.cpp',
+        '<(skia_src_path)/core/SkOrderedReadBuffer.cpp',
+        '<(skia_src_path)/core/SkOrderedWriteBuffer.cpp',
+        '<(skia_src_path)/core/SkPackBits.cpp',
+        '<(skia_src_path)/core/SkPaint.cpp',
+        '<(skia_src_path)/core/SkPaintPriv.cpp',
+        '<(skia_src_path)/core/SkPaintPriv.h',
+        '<(skia_src_path)/core/SkPath.cpp',
+        '<(skia_src_path)/core/SkPathEffect.cpp',
+        '<(skia_src_path)/core/SkPathHeap.cpp',
+        '<(skia_src_path)/core/SkPathHeap.h',
+        '<(skia_src_path)/core/SkPathMeasure.cpp',
+        '<(skia_src_path)/core/SkPathRef.h',
+        '<(skia_src_path)/core/SkPicture.cpp',
+        '<(skia_src_path)/core/SkPictureFlat.cpp',
+        '<(skia_src_path)/core/SkPictureFlat.h',
+        '<(skia_src_path)/core/SkPicturePlayback.cpp',
+        '<(skia_src_path)/core/SkPicturePlayback.h',
+        '<(skia_src_path)/core/SkPictureRecord.cpp',
+        '<(skia_src_path)/core/SkPictureRecord.h',
+        '<(skia_src_path)/core/SkPictureStateTree.cpp',
+        '<(skia_src_path)/core/SkPictureStateTree.h',
+        '<(skia_src_path)/core/SkPixelRef.cpp',
+        '<(skia_src_path)/core/SkPoint.cpp',
+        '<(skia_src_path)/core/SkProcSpriteBlitter.cpp',
+        '<(skia_src_path)/core/SkPtrRecorder.cpp',
+        '<(skia_src_path)/core/SkQuadClipper.cpp',
+        '<(skia_src_path)/core/SkQuadClipper.h',
+        '<(skia_src_path)/core/SkRasterClip.cpp',
+        '<(skia_src_path)/core/SkRasterizer.cpp',
+        '<(skia_src_path)/core/SkRect.cpp',
+        '<(skia_src_path)/core/SkRefCnt.cpp',
+        '<(skia_src_path)/core/SkRefDict.cpp',
+        '<(skia_src_path)/core/SkRegion.cpp',
+        '<(skia_src_path)/core/SkRegionPriv.h',
+        '<(skia_src_path)/core/SkRegion_path.cpp',
+        '<(skia_src_path)/core/SkRRect.cpp',
+        '<(skia_src_path)/core/SkRTree.h',
+        '<(skia_src_path)/core/SkRTree.cpp',
+        '<(skia_src_path)/core/SkScalar.cpp',
+        '<(skia_src_path)/core/SkScalerContext.cpp',
+        '<(skia_src_path)/core/SkScalerContext.h',
+        '<(skia_src_path)/core/SkScan.cpp',
+        '<(skia_src_path)/core/SkScan.h',
+        '<(skia_src_path)/core/SkScanPriv.h',
+        '<(skia_src_path)/core/SkScan_AntiPath.cpp',
+        '<(skia_src_path)/core/SkScan_Antihair.cpp',
+        '<(skia_src_path)/core/SkScan_Hairline.cpp',
+        '<(skia_src_path)/core/SkScan_Path.cpp',
+        '<(skia_src_path)/core/SkShader.cpp',
+        '<(skia_src_path)/core/SkSpriteBlitter_ARGB32.cpp',
+        '<(skia_src_path)/core/SkSpriteBlitter_RGB16.cpp',
+        '<(skia_src_path)/core/SkSinTable.h',
+        '<(skia_src_path)/core/SkSpriteBlitter.h',
+        '<(skia_src_path)/core/SkSpriteBlitterTemplate.h',
+        '<(skia_src_path)/core/SkStream.cpp',
+        '<(skia_src_path)/core/SkString.cpp',
+        '<(skia_src_path)/core/SkStringUtils.cpp',
+        '<(skia_src_path)/core/SkStroke.h',
+        '<(skia_src_path)/core/SkStroke.cpp',
+        '<(skia_src_path)/core/SkStrokeRec.cpp',
+        '<(skia_src_path)/core/SkStrokerPriv.cpp',
+        '<(skia_src_path)/core/SkStrokerPriv.h',
+        '<(skia_src_path)/core/SkTemplatesPriv.h',
+        '<(skia_src_path)/core/SkTextFormatParams.h',
+        '<(skia_src_path)/core/SkTileGrid.cpp',
+        '<(skia_src_path)/core/SkTileGrid.h',
+        '<(skia_src_path)/core/SkTileGridPicture.cpp',
+        '<(skia_src_path)/core/SkTLList.h',
+        '<(skia_src_path)/core/SkTLS.cpp',
+        '<(skia_src_path)/core/SkTSearch.cpp',
+        '<(skia_src_path)/core/SkTSort.h',
+        '<(skia_src_path)/core/SkTypeface.cpp',
+        '<(skia_src_path)/core/SkTypefaceCache.cpp',
+        '<(skia_src_path)/core/SkTypefaceCache.h',
+        '<(skia_src_path)/core/SkUnPreMultiply.cpp',
+        '<(skia_src_path)/core/SkUtils.cpp',
+        '<(skia_src_path)/core/SkWriter32.cpp',
+        '<(skia_src_path)/core/SkXfermode.cpp',
+
+        '<(skia_src_path)/image/SkDataPixelRef.cpp',
+        '<(skia_src_path)/image/SkImage.cpp',
+        '<(skia_src_path)/image/SkImagePriv.cpp',
+        '<(skia_src_path)/image/SkImage_Codec.cpp',
+#        '<(skia_src_path)/image/SkImage_Gpu.cpp',
+        '<(skia_src_path)/image/SkImage_Picture.cpp',
+        '<(skia_src_path)/image/SkImage_Raster.cpp',
+        '<(skia_src_path)/image/SkSurface.cpp',
+#        '<(skia_src_path)/image/SkSurface_Gpu.cpp',
+        '<(skia_src_path)/image/SkSurface_Picture.cpp',
+        '<(skia_src_path)/image/SkSurface_Raster.cpp',
+
+        '<(skia_src_path)/pipe/SkGPipeRead.cpp',
+        '<(skia_src_path)/pipe/SkGPipeWrite.cpp',
+
+        '<(skia_include_path)/core/Sk64.h',
+        '<(skia_include_path)/core/SkAdvancedTypefaceMetrics.h',
+        '<(skia_include_path)/core/SkBitmap.h',
+        '<(skia_include_path)/core/SkBlitRow.h',
+        '<(skia_include_path)/core/SkBounder.h',
+        '<(skia_include_path)/core/SkCanvas.h',
+        '<(skia_include_path)/core/SkChecksum.h',
+        '<(skia_include_path)/core/SkChunkAlloc.h',
+        '<(skia_include_path)/core/SkClipStack.h',
+        '<(skia_include_path)/core/SkColor.h',
+        '<(skia_include_path)/core/SkColorFilter.h',
+        '<(skia_include_path)/core/SkColorPriv.h',
+        '<(skia_include_path)/core/SkColorShader.h',
+        '<(skia_include_path)/core/SkComposeShader.h',
+        '<(skia_include_path)/core/SkData.h',
+        '<(skia_include_path)/core/SkDeque.h',
+        '<(skia_include_path)/core/SkDevice.h',
+        '<(skia_include_path)/core/SkDeviceProperties.h',
+        '<(skia_include_path)/core/SkDither.h',
+        '<(skia_include_path)/core/SkDraw.h',
+        '<(skia_include_path)/core/SkDrawFilter.h',
+        '<(skia_include_path)/core/SkDrawLooper.h',
+        '<(skia_include_path)/core/SkEndian.h',
+        '<(skia_include_path)/core/SkFixed.h',
+        '<(skia_include_path)/core/SkFlattenable.h',
+        '<(skia_include_path)/core/SkFloatBits.h',
+        '<(skia_include_path)/core/SkFloatingPoint.h',
+        '<(skia_include_path)/core/SkFontHost.h',
+        '<(skia_include_path)/core/SkGeometry.h',
+        '<(skia_include_path)/core/SkGraphics.h',
+        '<(skia_include_path)/core/SkImageFilter.h',
+        '<(skia_include_path)/core/SkInstCnt.h',
+        '<(skia_include_path)/core/SkMallocPixelRef.h',
+        '<(skia_include_path)/core/SkMask.h',
+        '<(skia_include_path)/core/SkMaskFilter.h',
+        '<(skia_include_path)/core/SkMath.h',
+        '<(skia_include_path)/core/SkMatrix.h',
+        '<(skia_include_path)/core/SkMetaData.h',
+        '<(skia_include_path)/core/SkMMapStream.h',
+        '<(skia_include_path)/core/SkOSFile.h',
+        '<(skia_include_path)/core/SkPackBits.h',
+        '<(skia_include_path)/core/SkPaint.h',
+        '<(skia_include_path)/core/SkPath.h',
+        '<(skia_include_path)/core/SkPathEffect.h',
+        '<(skia_include_path)/core/SkPathMeasure.h',
+        '<(skia_include_path)/core/SkPicture.h',
+        '<(skia_include_path)/core/SkPixelRef.h',
+        '<(skia_include_path)/core/SkPoint.h',
+        '<(skia_include_path)/core/SkRasterizer.h',
+        '<(skia_include_path)/core/SkReader32.h',
+        '<(skia_include_path)/core/SkRect.h',
+        '<(skia_include_path)/core/SkRefCnt.h',
+        '<(skia_include_path)/core/SkRegion.h',
+        '<(skia_include_path)/core/SkRRect.h',
+        '<(skia_include_path)/core/SkScalar.h',
+        '<(skia_include_path)/core/SkScalarCompare.h',
+        '<(skia_include_path)/core/SkShader.h',
+        '<(skia_include_path)/core/SkStream.h',
+        '<(skia_include_path)/core/SkString.h',
+        '<(skia_include_path)/core/SkStringUtils.h',
+        '<(skia_include_path)/core/SkStrokeRec.h',
+        '<(skia_include_path)/core/SkTArray.h',
+        '<(skia_include_path)/core/SkTDArray.h',
+        '<(skia_include_path)/core/SkTDStack.h',
+        '<(skia_include_path)/core/SkTDict.h',
+        '<(skia_include_path)/core/SkTInternalLList.h',
+        '<(skia_include_path)/core/SkTileGridPicture.h',
+        '<(skia_include_path)/core/SkTRegistry.h',
+        '<(skia_include_path)/core/SkTScopedPtr.h',
+        '<(skia_include_path)/core/SkTSearch.h',
+        '<(skia_include_path)/core/SkTemplates.h',
+        '<(skia_include_path)/core/SkThread.h',
+        '<(skia_include_path)/core/SkThread_platform.h',
+        '<(skia_include_path)/core/SkTime.h',
+        '<(skia_include_path)/core/SkTLazy.h',
+        '<(skia_include_path)/core/SkTrace.h',
+        '<(skia_include_path)/core/SkTypeface.h',
+        '<(skia_include_path)/core/SkTypes.h',
+        '<(skia_include_path)/core/SkUnPreMultiply.h',
+        '<(skia_include_path)/core/SkUnitMapper.h',
+        '<(skia_include_path)/core/SkUtils.h',
+        '<(skia_include_path)/core/SkWeakRefCnt.h',
+        '<(skia_include_path)/core/SkWriter32.h',
+        '<(skia_include_path)/core/SkXfermode.h',
+    ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/debugger.gyp b/gyp/debugger.gyp
new file mode 100644
index 0000000..40ce090
--- /dev/null
+++ b/gyp/debugger.gyp
@@ -0,0 +1,170 @@
+{
+  'variables': {
+    'conditions': [
+      [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+        # Use the systemwide Qt libs by default
+        'variables': {
+          'qt_sdk%': '/usr',
+        },
+        'qt_sdk': '<(qt_sdk)',
+        'qt_moc%': 'moc',
+        'qt_includes': [
+          '<(qt_sdk)/include',
+          '<(qt_sdk)/include/QtCore',
+          '<(qt_sdk)/include/QtGui',
+          '<(qt_sdk)/include/QtOpenGL',
+
+          # Systemwide Qt libs are not contained under a single tree,
+          # so we're adding package-qualified paths as a fallback.
+          '<(qt_sdk)/include/qt4',
+          '<(qt_sdk)/include/qt4/QtCore',
+          '<(qt_sdk)/include/qt4/QtGui',
+          '<(qt_sdk)/include/qt4/QtOpenGL',
+        ],
+        'qt_libs': [
+          '-lQtCore',
+          '-lQtGui',
+          '-lQtOpenGL'
+        ],
+      }],
+      [ 'skia_os == "mac"', {
+        # Use the systemwide Qt libs by default
+        'variables': {
+          'qt_sdk%': '/Library/Frameworks',
+        },
+        'qt_sdk': '<(qt_sdk)',
+        'qt_moc%': 'moc',
+        'qt_includes': [
+          '<(qt_sdk)/QtCore.framework/Headers/',
+          '<(qt_sdk)/QtGui.framework/Headers/',
+          '<(qt_sdk)/QtOpenGL.framework/Headers/',
+        ],
+        'qt_libs': [
+          '<(qt_sdk)/QtCore.framework',
+          '<(qt_sdk)/QtGui.framework',
+          '<(qt_sdk)/QtOpenGL.framework',
+        ],
+      }],
+      [ 'skia_os == "win"', {
+        'variables': {
+          # This is the default location for the version of Qt current on 10/11/12
+          'qt_sdk%': 'C:/Qt/4.8.3/',
+        },
+        'qt_sdk': '<(qt_sdk)',
+        'qt_moc%': '<(qt_sdk)/bin/moc',
+        'qt_includes': [
+          '<(qt_sdk)/include',
+          '<(qt_sdk)/include/QtCore',
+          '<(qt_sdk)/include/QtGui',
+          '<(qt_sdk)/include/QtOpenGL',
+        ],
+        'qt_libs': [
+          '<(qt_sdk)/lib/QtCore4.lib',
+          '<(qt_sdk)/lib/QtGui4.lib',
+          '<(qt_sdk)/lib/QtOpenGL4.lib',
+        ],
+      }],
+    ],
+    'moc_src_dir': '../debugger/QT',
+    'moc_gen_dir': '<(SHARED_INTERMEDIATE_DIR)/debugger/QT',
+  },
+  'targets': [
+    {
+      'target_name': 'debugger',
+      'type': 'executable',
+      'mac_bundle': 1,
+      'mac_framework_dirs': [
+        '/Library/Frameworks',
+      ],
+      'include_dirs' : [
+        '../src/core',
+        '../debugger',      # To pull SkDebugger.h
+        '../debugger/QT',   # For all the QT UI Goodies
+        '../src/gpu',       # To pull gl/GrGLUtil.h
+        '../src/ports',     # To pull SkFontDescriptor.h
+        '../bench',
+        '../tools',
+        '<@(qt_includes)',
+      ],
+      'sources': [
+        '../debugger/debuggermain.cpp',
+        '../debugger/SkDebugCanvas.h',
+        '../debugger/SkDebugCanvas.cpp',
+        '../debugger/SkDebugger.cpp',
+        '../debugger/SkDrawCommand.h',
+        '../debugger/SkDrawCommand.cpp',
+        '../debugger/SkObjectParser.h',
+        '../debugger/SkObjectParser.cpp',
+        '../debugger/QT/SkDebuggerGUI.cpp',
+        '../debugger/QT/SkDebuggerGUI.h',
+        '../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',
+        '../debugger/QT/SkImageWidget.h',
+        '../debugger/QT/SkImageWidget.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_SkInspectorWidget.cpp',
+        '<(moc_gen_dir)/moc_SkSettingsWidget.cpp',
+        '<(moc_gen_dir)/moc_SkRasterWidget.cpp',
+        '<(moc_gen_dir)/moc_SkImageWidget.cpp',
+        '<(moc_gen_dir)/moc_SkGLWidget.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'images.gyp:images',
+        'effects.gyp:effects',
+        'bench.gyp:bench_timer',
+        'tools.gyp:picture_renderer',
+        'debugger_mocs',
+      ],
+      'link_settings': {
+        'libraries': [
+          '<@(qt_libs)',
+        ],
+      },
+    },
+    {
+      'target_name': 'debugger_mocs',
+      'type': 'none',
+      'sources': [
+        '<(moc_src_dir)/SkCanvasWidget.h',
+        '<(moc_src_dir)/SkDebuggerGUI.h',
+        '<(moc_src_dir)/SkInspectorWidget.h',
+        '<(moc_src_dir)/SkSettingsWidget.h',
+        '<(moc_src_dir)/SkRasterWidget.h',
+        '<(moc_src_dir)/SkImageWidget.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)', '<(RULE_INPUT_PATH)', '-o', '<(moc_gen_dir)/moc_<(RULE_INPUT_ROOT).cpp' ],
+          'message': 'Generating <(RULE_INPUT_ROOT).cpp.',
+        },
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/effects.gyp b/gyp/effects.gyp
index ac56510..e110c9f 100644
--- a/gyp/effects.gyp
+++ b/gyp/effects.gyp
@@ -1,88 +1,35 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'effects',
+      'product_name': 'skia_effects',
       'type': 'static_library',
-      'include_dirs': [
-        '../include/config',
-        '../include/core',
-        '../include/effects',
+      'standalone_static_library': 1,
+      'includes': [
+        'effects.gypi',
       ],
-      'sources': [
-        '../include/effects/Sk1DPathEffect.h',
-        '../include/effects/Sk2DPathEffect.h',
-        '../include/effects/SkAvoidXfermode.h',
-        '../include/effects/SkArithmeticMode.h',
-        '../include/effects/SkBlurDrawLooper.h',
-        '../include/effects/SkBlurImageFilter.h',
-        '../include/effects/SkBlurMaskFilter.h',
-        '../include/effects/SkColorMatrix.h',
-        '../include/effects/SkColorMatrixFilter.h',
-        '../include/effects/SkCornerPathEffect.h',
-        '../include/effects/SkDashPathEffect.h',
-        '../include/effects/SkDiscretePathEffect.h',
-        '../include/effects/SkDrawExtraPathEffect.h',
-        '../include/effects/SkEffects.h',
-        '../include/effects/SkEmbossMaskFilter.h',
-        '../include/effects/SkGradientShader.h',
-        '../include/effects/SkGroupShape.h',
-        '../include/effects/SkKernel33MaskFilter.h',
-        '../include/effects/SkLayerDrawLooper.h',
-        '../include/effects/SkLayerRasterizer.h',
-        '../include/effects/SkMorphologyImageFilter.h',
-        '../include/effects/SkPaintFlagsDrawFilter.h',
-        '../include/effects/SkPixelXorXfermode.h',
-        '../include/effects/SkPorterDuff.h',
-        '../include/effects/SkRectShape.h',
-        '../include/effects/SkTableColorFilter.h',
-        '../include/effects/SkTableMaskFilter.h',
-        '../include/effects/SkTransparentShader.h',
-
-        '../src/effects/Sk1DPathEffect.cpp',
-        '../src/effects/Sk2DPathEffect.cpp',
-        '../src/effects/SkAvoidXfermode.cpp',
-        '../src/effects/SkArithmeticMode.cpp',
-        '../src/effects/SkBitmapCache.cpp',
-        '../src/effects/SkBitmapCache.h',
-        '../src/effects/SkBlurDrawLooper.cpp',
-        '../src/effects/SkBlurMask.cpp',
-        '../src/effects/SkBlurMask.h',
-        '../src/effects/SkBlurImageFilter.cpp',
-        '../src/effects/SkBlurMaskFilter.cpp',
-        '../src/effects/SkColorFilters.cpp',
-        '../src/effects/SkColorMatrixFilter.cpp',
-        '../src/effects/SkCornerPathEffect.cpp',
-        '../src/effects/SkDashPathEffect.cpp',
-        '../src/effects/SkDiscretePathEffect.cpp',
-        '../src/effects/SkEffects.cpp',
-        '../src/effects/SkEmbossMask.cpp',
-        '../src/effects/SkEmbossMask.h',
-        '../src/effects/SkEmbossMask_Table.h',
-        '../src/effects/SkEmbossMaskFilter.cpp',
-        '../src/effects/SkGradientShader.cpp',
-        '../src/effects/SkGroupShape.cpp',
-        '../src/effects/SkKernel33MaskFilter.cpp',
-        '../src/effects/SkLayerDrawLooper.cpp',
-        '../src/effects/SkLayerRasterizer.cpp',
-        '../src/effects/SkMorphologyImageFilter.cpp',
-        '../src/effects/SkPaintFlagsDrawFilter.cpp',
-        '../src/effects/SkPixelXorXfermode.cpp',
-        '../src/effects/SkPorterDuff.cpp',
-        '../src/effects/SkRadialGradient_Table.h',
-        '../src/effects/SkRectShape.cpp',
-        '../src/effects/SkTableColorFilter.cpp',
-        '../src/effects/SkTableMaskFilter.cpp',
-        '../src/effects/SkTestImageFilters.cpp',
-        '../src/effects/SkTransparentShader.cpp',
+      'include_dirs': [
+        '../include/effects',
+        '../src/core',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
           '../include/effects',
         ],
       },
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+      ],
+      'sources': [
+        'effects.gypi', # Makes the gypi appear in IDEs (but does not modify the build).
+      ],
+      'conditions': [
+        ['skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu',
+          ],
+        }],
+      ],
     },
   ],
 }
diff --git a/gyp/effects.gypi b/gyp/effects.gypi
new file mode 100644
index 0000000..44d846a
--- /dev/null
+++ b/gyp/effects.gypi
@@ -0,0 +1,114 @@
+# Include this gypi to include all 'effects' files
+# The parent gyp/gypi file must define:
+#       'skia_src_path'     e.g. skia/trunk/src
+#       'skia_include_path' e.g. skia/trunk/include
+#
+# The skia build defines these in common_variables.gypi.
+#
+{
+  'sources': [
+    '<(skia_src_path)/effects/Sk1DPathEffect.cpp',
+    '<(skia_src_path)/effects/Sk2DPathEffect.cpp',
+    '<(skia_src_path)/effects/SkAvoidXfermode.cpp',
+    '<(skia_src_path)/effects/SkArithmeticMode.cpp',
+    '<(skia_src_path)/effects/SkBicubicImageFilter.cpp',
+    '<(skia_src_path)/effects/SkBitmapSource.cpp',
+    '<(skia_src_path)/effects/SkBlendImageFilter.cpp',
+    '<(skia_src_path)/effects/SkBlurDrawLooper.cpp',
+    '<(skia_src_path)/effects/SkBlurMask.cpp',
+    '<(skia_src_path)/effects/SkBlurMask.h',
+    '<(skia_src_path)/effects/SkBlurImageFilter.cpp',
+    '<(skia_src_path)/effects/SkBlurMaskFilter.cpp',
+    '<(skia_src_path)/effects/SkColorFilters.cpp',
+    '<(skia_src_path)/effects/SkColorFilterImageFilter.cpp',
+    '<(skia_src_path)/effects/SkColorMatrix.cpp',
+    '<(skia_src_path)/effects/SkColorMatrixFilter.cpp',
+    '<(skia_src_path)/effects/SkCornerPathEffect.cpp',
+    '<(skia_src_path)/effects/SkDashPathEffect.cpp',
+    '<(skia_src_path)/effects/SkDiscretePathEffect.cpp',
+    '<(skia_src_path)/effects/SkDisplacementMapEffect.cpp',
+    '<(skia_src_path)/effects/SkEmbossMask.cpp',
+    '<(skia_src_path)/effects/SkEmbossMask.h',
+    '<(skia_src_path)/effects/SkEmbossMask_Table.h',
+    '<(skia_src_path)/effects/SkEmbossMaskFilter.cpp',
+    '<(skia_src_path)/effects/SkImageFilterUtils.cpp',
+    '<(skia_src_path)/effects/SkKernel33MaskFilter.cpp',
+    '<(skia_src_path)/effects/SkLayerDrawLooper.cpp',
+    '<(skia_src_path)/effects/SkLayerRasterizer.cpp',
+    '<(skia_src_path)/effects/SkLightingImageFilter.cpp',
+    '<(skia_src_path)/effects/SkMatrixConvolutionImageFilter.cpp',
+    '<(skia_src_path)/effects/SkMergeImageFilter.cpp',
+    '<(skia_src_path)/effects/SkMorphologyImageFilter.cpp',
+    '<(skia_src_path)/effects/SkOffsetImageFilter.cpp',
+    '<(skia_src_path)/effects/SkPaintFlagsDrawFilter.cpp',
+    '<(skia_src_path)/effects/SkPixelXorXfermode.cpp',
+    '<(skia_src_path)/effects/SkPorterDuff.cpp',
+    '<(skia_src_path)/effects/SkSingleInputImageFilter.cpp',
+    '<(skia_src_path)/effects/SkStippleMaskFilter.cpp',
+    '<(skia_src_path)/effects/SkTableColorFilter.cpp',
+    '<(skia_src_path)/effects/SkTableMaskFilter.cpp',
+    '<(skia_src_path)/effects/SkTestImageFilters.cpp',
+    '<(skia_src_path)/effects/SkTransparentShader.cpp',
+    '<(skia_src_path)/effects/SkMagnifierImageFilter.cpp',
+
+    '<(skia_src_path)/effects/gradients/SkBitmapCache.cpp',
+    '<(skia_src_path)/effects/gradients/SkBitmapCache.h',
+    '<(skia_src_path)/effects/gradients/SkClampRange.cpp',
+    '<(skia_src_path)/effects/gradients/SkClampRange.h',
+    '<(skia_src_path)/effects/gradients/SkRadialGradient_Table.h',
+    '<(skia_src_path)/effects/gradients/SkGradientShader.cpp',
+    '<(skia_src_path)/effects/gradients/SkGradientShaderPriv.h',
+    '<(skia_src_path)/effects/gradients/SkLinearGradient.cpp',
+    '<(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/SkSweepGradient.cpp',
+    '<(skia_src_path)/effects/gradients/SkSweepGradient.h',
+
+    '<(skia_include_path)/effects/Sk1DPathEffect.h',
+    '<(skia_include_path)/effects/Sk2DPathEffect.h',
+    '<(skia_include_path)/effects/SkAvoidXfermode.h',
+    '<(skia_include_path)/effects/SkArithmeticMode.h',
+    '<(skia_include_path)/effects/SkBitmapSource.h',
+    '<(skia_include_path)/effects/SkBlendImageFilter.h',
+    '<(skia_include_path)/effects/SkBlurDrawLooper.h',
+    '<(skia_include_path)/effects/SkBlurImageFilter.h',
+    '<(skia_include_path)/effects/SkBlurMaskFilter.h',
+    '<(skia_include_path)/effects/SkColorMatrix.h',
+    '<(skia_include_path)/effects/SkColorMatrixFilter.h',
+    '<(skia_include_path)/effects/SkColorFilterImageFilter.h',
+    '<(skia_include_path)/effects/SkCornerPathEffect.h',
+    '<(skia_include_path)/effects/SkDashPathEffect.h',
+    '<(skia_include_path)/effects/SkDiscretePathEffect.h',
+    '<(skia_include_path)/effects/SkDisplacementMapEffect.h',
+    '<(skia_include_path)/effects/SkDrawExtraPathEffect.h',
+    '<(skia_include_path)/effects/SkEmbossMaskFilter.h',
+    '<(skia_include_path)/effects/SkGradientShader.h',
+    '<(skia_include_path)/effects/SkImageFilterUtils.h',
+    '<(skia_include_path)/effects/SkKernel33MaskFilter.h',
+    '<(skia_include_path)/effects/SkLayerDrawLooper.h',
+    '<(skia_include_path)/effects/SkLayerRasterizer.h',
+    '<(skia_include_path)/effects/SkLightingImageFilter.h',
+    '<(skia_include_path)/effects/SkOffsetImageFilter.h',
+    '<(skia_include_path)/effects/SkMorphologyImageFilter.h',
+    '<(skia_include_path)/effects/SkPaintFlagsDrawFilter.h',
+    '<(skia_include_path)/effects/SkPixelXorXfermode.h',
+    '<(skia_include_path)/effects/SkPorterDuff.h',
+    '<(skia_include_path)/effects/SkSingleInputImageFilter.h',
+    '<(skia_include_path)/effects/SkStippleMaskFilter.h',
+    '<(skia_include_path)/effects/SkTableColorFilter.h',
+    '<(skia_include_path)/effects/SkTableMaskFilter.h',
+    '<(skia_include_path)/effects/SkTransparentShader.h',
+    '<(skia_include_path)/effects/SkMagnifierImageFilter.h',
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/everything.gyp b/gyp/everything.gyp
new file mode 100644
index 0000000..e1d516d
--- /dev/null
+++ b/gyp/everything.gyp
@@ -0,0 +1,31 @@
+# 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
+# left out of "most".)
+#
+# We used to call this the 'all' target, but in SOME cases that
+# conflicted with an automatically-generated 'all' target.
+# See https://code.google.com/p/skia/issues/detail?id=932
+#
+{
+  'targets': [
+    {
+      'target_name': 'everything',
+      'type': 'none',
+      'dependencies': ['most.gyp:most'],
+      'conditions': [
+        ['skia_os in ("ios", "android", "nacl") or (skia_os == "mac" and skia_arch_width == 32)', {
+          # debugger is not supported on this platform
+        }, {
+          'dependencies': [ 'debugger.gyp:debugger' ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/experimental.gyp b/gyp/experimental.gyp
index c5b8c5e..09711ac 100644
--- a/gyp/experimental.gyp
+++ b/gyp/experimental.gyp
@@ -1,7 +1,4 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'experimental',
diff --git a/gyp/freetype.gyp b/gyp/freetype.gyp
index e86a338..db61742 100644
--- a/gyp/freetype.gyp
+++ b/gyp/freetype.gyp
@@ -1,63 +1,82 @@
 {
-#  'includes': [
-#    'common.gypi',
-#  ],
   'targets': [
     {
-      'target_name': 'skfreetype',
+      'target_name': 'freetype',
       'type': 'static_library',
       'sources': [
-        '../third_party/freetype/src/base/ftbbox.c',
-        '../third_party/freetype/src/base/ftbitmap.c',
-        '../third_party/freetype/src/base/ftglyph.c',
-        '../third_party/freetype/src/base/ftlcdfil.c',
-        '../third_party/freetype/src/base/ftstroke.c',
-        '../third_party/freetype/src/base/ftxf86.c',
-        '../third_party/freetype/src/base/ftbase.c',
-        '../third_party/freetype/src/base/ftsystem.c',
-        '../third_party/freetype/src/base/ftinit.c',
-        '../third_party/freetype/src/base/ftgasp.c',
-        '../third_party/freetype/src/base/ftfstype.c',
-        '../third_party/freetype/src/raster/raster.c',
-        '../third_party/freetype/src/sfnt/sfnt.c',
-        '../third_party/freetype/src/smooth/smooth.c',
-        '../third_party/freetype/src/autofit/autofit.c',
-        '../third_party/freetype/src/truetype/truetype.c',
-        '../third_party/freetype/src/cff/cff.c',
-        '../third_party/freetype/src/psnames/psnames.c',
-        '../third_party/freetype/src/pshinter/pshinter.c',
+        # base components (required)
+        '../third_party/externals/freetype/src/base/ftsystem.c',
+        '../third_party/externals/freetype/src/base/ftinit.c',
+        '../third_party/externals/freetype/src/base/ftdebug.c',
+        '../third_party/externals/freetype/src/base/ftbase.c',
 
-# added for linker
-        '../third_party/freetype/src/lzw/ftlzw.c',
-        '../third_party/freetype/src/gzip/ftgzip.c',
-        '../third_party/freetype/src/cid/type1cid.c',
-        '../third_party/freetype/src/bdf/bdf.c',
-        '../third_party/freetype/src/psaux/psaux.c',
-        '../third_party/freetype/src/pcf/pcf.c',
-        '../third_party/freetype/src/pfr/pfr.c',
-        '../third_party/freetype/src/type1/type1.c',
-        '../third_party/freetype/src/type42/type42.c',
-        '../third_party/freetype/src/winfonts/winfnt.c',
+        '../third_party/externals/freetype/src/base/ftbbox.c',       # recommended, see <freetype/ftbbox.h>
+        '../third_party/externals/freetype/src/base/ftglyph.c',      # recommended, see <freetype/ftglyph.h>
+
+        '../third_party/externals/freetype/src/base/ftbitmap.c',     # optional, see <freetype/ftbitmap.h>
+        '../third_party/externals/freetype/src/base/ftfstype.c',     # optional
+        '../third_party/externals/freetype/src/base/ftgasp.c',       # optional, see <freetype/ftgasp.h>
+        '../third_party/externals/freetype/src/base/ftlcdfil.c',     # optional, see <freetype/ftlcdfil.h>
+        '../third_party/externals/freetype/src/base/ftmm.c',         # optional, see <freetype/ftmm.h>
+        '../third_party/externals/freetype/src/base/ftpatent.c',     # optional
+        '../third_party/externals/freetype/src/base/ftstroke.c',     # optional, see <freetype/ftstroke.h>
+        '../third_party/externals/freetype/src/base/ftsynth.c',      # optional, see <freetype/ftsynth.h>
+        '../third_party/externals/freetype/src/base/fttype1.c',      # optional, see <freetype/t1tables.h>
+        '../third_party/externals/freetype/src/base/ftwinfnt.c',     # optional, see <freetype/ftwinfnt.h>
+        '../third_party/externals/freetype/src/base/ftxf86.c',       # optional, see <freetype/ftxf86.h>
+
+        # font drivers (optional; at least one is needed)
+        '../third_party/externals/freetype/src/cff/cff.c',           # CFF/OpenType font driver
+        '../third_party/externals/freetype/src/sfnt/sfnt.c',         # SFNT files support (TrueType & OpenType)
+        '../third_party/externals/freetype/src/truetype/truetype.c', # TrueType font driver
+
+        # rasterizers (optional; at least one is needed for vector formats)
+        '../third_party/externals/freetype/src/raster/raster.c',     # monochrome rasterizer
+        '../third_party/externals/freetype/src/smooth/smooth.c',     # anti-aliasing rasterizer
+
+        # auxiliary modules (optional)
+        '../third_party/externals/freetype/src/autofit/autofit.c',   # auto hinting module
+        '../third_party/externals/freetype/src/psaux/psaux.c',       # PostScript Type 1 parsing
+        '../third_party/externals/freetype/src/pshinter/pshinter.c', # PS hinting module
+        '../third_party/externals/freetype/src/psnames/psnames.c',   # PostScript glyph names support
       ],
       'include_dirs': [
-        '../third_party/freetype/internal',
-        '../third_party/freetype/builds',
-        '../third_party/freetype/include',
-        '../third_party/freetype',
+        '../third_party/externals/freetype/internal',
+        '../third_party/externals/freetype/builds',
+        '../third_party/externals/freetype/include',
+        '../third_party/externals/freetype',
       ],
       'cflags': [
-        '-W',
-        '-Wall',
-        '-fPIC',
-        '-DPIC',
-        '-DDARWIN_NO_CARBON',
         '-DFT2_BUILD_LIBRARY',
       ],
       'direct_dependent_settings': {
         'include_dirs': [
-          '../third_party/freetype/include',  # For ft2build.h
+          '../third_party/externals/freetype/include',
         ],
       },
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'sources': [
+            '../third_party/externals/freetype/src/base/ftmac.c',        # only on the Macintosh
+          ],
+        }],
+        [ 'skia_os == "android"', {
+          # These flags are used by the Android OS.  They are probably overkill
+          # for Skia, but we add them for consistency.
+          'cflags': [
+            '-W',
+            '-Wall',
+            '-fPIC',
+            '-DPIC',
+            '-DDARWIN_NO_CARBON',
+            '-DFT2_BUILD_LIBRARY',
+            '-O2',
+          ],
+          'cflags!': [
+            '-fno-rtti', # supress warnings about invalid option of non-C++ code
+          ],
+        }],
+      ],
     },
   ],
 }
diff --git a/gyp/gm.gyp b/gyp/gm.gyp
index 3c5e12e..4b2b1c4 100644
--- a/gyp/gm.gyp
+++ b/gyp/gm.gyp
@@ -2,12 +2,17 @@
 {
   'includes': [
     'apptype_console.gypi',
-    'common.gypi',
   ],
   'targets': [
     {
       'target_name': 'gm',
       'type': 'executable',
+      'include_dirs' : [
+        '../src/core',
+        '../src/effects',
+        '../src/pipe/utils/',
+        '../src/utils/',
+      ],
       'includes': [
         'gmslides.gypi',
       ],
@@ -15,16 +20,16 @@
         '../gm/gm.cpp',
         '../gm/gmmain.cpp',
         '../gm/system_preferences_default.cpp',
+        '../src/pipe/utils/SamplePipeControllers.h',
+        '../src/pipe/utils/SamplePipeControllers.cpp',
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
         'effects.gyp:effects',
-        'gpu.gyp:gr',
-        'gpu.gyp:skgr',
         'images.gyp:images',
-        'ports.gyp:ports',
+        'jsoncpp.gyp:jsoncpp',
         'pdf.gyp:pdf',
-        'utils.gyp:utils',        
+        'utils.gyp:utils',
       ],
       'conditions': [
         ['skia_os == "mac"', {
@@ -46,7 +51,12 @@
             'xps.gyp:xps',
           ],
         }],
-      ],  
+        ['skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu',
+          ],
+        }],
+      ],
     },
   ],
 }
diff --git a/gyp/gmslides.gypi b/gyp/gmslides.gypi
index 2c96e45..8d772f3 100644
--- a/gyp/gmslides.gypi
+++ b/gyp/gmslides.gypi
@@ -4,50 +4,92 @@
     '../gm/aaclip.cpp',
     '../gm/aarectmodes.cpp',
     '../gm/arithmode.cpp',
+    '../gm/bicubicfilter.cpp',
+    '../gm/bigmatrix.cpp',
     '../gm/bitmapcopy.cpp',
+    '../gm/bitmapmatrix.cpp',
     '../gm/bitmapfilters.cpp',
+    '../gm/bitmaprect.cpp',
     '../gm/bitmapscroll.cpp',
+    '../gm/blend.cpp',
     '../gm/blurs.cpp',
+    '../gm/blurrect.cpp',
+    '../gm/circles.cpp',
+    '../gm/colorfilterimagefilter.cpp',
     '../gm/colormatrix.cpp',
     '../gm/complexclip.cpp',
     '../gm/complexclip2.cpp',
+    '../gm/composeshader.cpp',
     '../gm/convexpaths.cpp',
     '../gm/cubicpaths.cpp',
+    '../gm/cmykjpeg.cpp',
     '../gm/degeneratesegments.cpp',
+    '../gm/dashcubics.cpp',
+    '../gm/dashing.cpp',
+    '../gm/distantclip.cpp',
+    '../gm/displacement.cpp',
     '../gm/drawbitmaprect.cpp',
+    '../gm/drawlooper.cpp',
+    '../gm/extractbitmap.cpp',
     '../gm/emptypath.cpp',
+    '../gm/fatpathfill.cpp',
+    '../gm/factory.cpp',
     '../gm/filltypes.cpp',
     '../gm/filltypespersp.cpp',
     '../gm/fontscaler.cpp',
     '../gm/gammatext.cpp',
+    '../gm/getpostextpath.cpp',
+    '../gm/giantbitmap.cpp',
     '../gm/gradients.cpp',
     '../gm/gradtext.cpp',
     '../gm/hairmodes.cpp',
+    '../gm/hittestpath.cpp',
     '../gm/imageblur.cpp',
+    '../gm/imagemagnifier.cpp',
+    '../gm/lighting.cpp',
+    '../gm/image.cpp',
+    '../gm/imagefiltersbase.cpp',
+    '../gm/imagefiltersgraph.cpp',
     '../gm/lcdtext.cpp',
     '../gm/linepaths.cpp',
+    '../gm/matrixconvolution.cpp',
+    '../gm/modecolorfilters.cpp',
     '../gm/morphology.cpp',
     '../gm/ninepatchstretch.cpp',
     '../gm/nocolorbleed.cpp',
     '../gm/patheffects.cpp',
     '../gm/pathfill.cpp',
+    '../gm/pathinterior.cpp',
     '../gm/pathreverse.cpp',
     '../gm/points.cpp',
     '../gm/poly2poly.cpp',
     '../gm/quadpaths.cpp',
+    '../gm/rrect.cpp',
+    '../gm/rrects.cpp',
+    '../gm/samplerstress.cpp',
+    '../gm/shaderbounds.cpp',
     '../gm/shadertext.cpp',
+    '../gm/shadertext2.cpp',
+    '../gm/shadertext3.cpp',
     '../gm/shadows.cpp',
-    '../gm/shapes.cpp',
+    '../gm/simpleaaclip.cpp',
+    '../gm/spritebitmap.cpp',
+    '../gm/srcmode.cpp',
     '../gm/strokefill.cpp',
+    '../gm/strokerect.cpp',
     '../gm/strokerects.cpp',
     '../gm/strokes.cpp',
     '../gm/tablecolorfilter.cpp',
+    '../gm/texteffects.cpp',
     '../gm/testimagefilters.cpp',
     '../gm/texdata.cpp',
     '../gm/tilemodes.cpp',
     '../gm/tinybitmap.cpp',
+    '../gm/twopointradial.cpp',
+    '../gm/typeface.cpp',
     '../gm/verttext.cpp',
     '../gm/verttext2.cpp',
+    '../gm/verylargebitmap.cpp',
     '../gm/xfermodes.cpp',
   ],
 }
diff --git a/gyp/gpu.gyp b/gyp/gpu.gyp
index bb13bbd..6c599e1 100644
--- a/gyp/gpu.gyp
+++ b/gyp/gpu.gyp
@@ -1,7 +1,4 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'target_defaults': {
     'conditions': [
       ['skia_os != "win"', {
@@ -9,7 +6,7 @@
         ],
       }],
       ['skia_os != "mac"', {
-        'sources/': [ ['exclude', '_mac.(h|cpp)$'],
+        'sources/': [ ['exclude', '_mac.(h|cpp|m|mm)$'],
         ],
       }],
       ['skia_os != "linux"', {
@@ -17,13 +14,17 @@
         ],
       }],
       ['skia_os != "ios"', {
-        'sources/': [ ['exclude', '_iOS.(h|cpp)$'],
+        'sources/': [ ['exclude', '_iOS.(h|cpp|m|mm)$'],
         ],
       }],
       ['skia_os != "android"', {
         'sources/': [ ['exclude', '_android.(h|cpp)$'],
         ],
       }],
+      ['skia_os != "nacl"', {
+        'sources/': [ ['exclude', '_nacl.(h|cpp)$'],
+        ],
+      }],
       [ 'skia_os == "android"', {
         'defines': [
           'GR_ANDROID_BUILD=1',
@@ -47,7 +48,36 @@
       [ 'skia_os == "win"', {
         'defines': [
           'GR_WIN32_BUILD=1',
-          'GR_GL_FUNCTION_TYPE=__stdcall',
+        ],
+      }],
+      # nullify the targets in this gyp file if skia_gpu is 0
+      [ 'skia_gpu == 0', {
+        'sources/': [
+          ['exclude', '.*'],
+        ],
+        'defines/': [
+          ['exclude', '.*'],
+        ],
+        'include_dirs/': [
+           ['exclude', '.*'],
+        ],
+        'link_settings': {
+          'libraries/': [
+            ['exclude', '.*'],
+          ],
+        },
+        'direct_dependent_settings': {
+          'defines/': [
+            ['exclude', '.*'],
+          ],
+          'include_dirs/': [
+            ['exclude', '.*'],
+          ],
+        },
+      }],
+      [ 'skia_texture_cache_mb_limit != 0', {
+        'defines': [
+          'GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT=<(skia_texture_cache_mb_limit)',
         ],
       }],
     ],
@@ -88,48 +118,39 @@
   'targets': [
     {
       'target_name': 'skgr',
+      'product_name': 'skia_skgr',
       'type': 'static_library',
+      'standalone_static_library': 1,
+      'includes': [
+        'gpu.gypi',
+      ],
       'include_dirs': [
         '../include/config',
         '../include/core',
+        '../include/utils',
         '../src/core',
         '../include/gpu',
+        '../src/gpu',
+      ],
+      'dependencies': [
+        'angle.gyp:*',
+      ],
+      'export_dependent_settings': [
+        'angle.gyp:*',
       ],
       'sources': [
-        '../include/gpu/SkGpuCanvas.h',
-        '../include/gpu/SkGpuDevice.h',
-        '../include/gpu/SkGr.h',
-        '../include/gpu/SkGrTexturePixelRef.h',
-
-        '../include/gpu/gl/SkGLContext.h',
-        '../include/gpu/gl/SkMesaGLContext.h',
-        '../include/gpu/gl/SkNativeGLContext.h',
-        '../include/gpu/gl/SkNullGLContext.h',
-
-        '../src/gpu/GrPrintf_skia.cpp',
-        '../src/gpu/SkGpuCanvas.cpp',
-        '../src/gpu/SkGpuDevice.cpp',
-        '../src/gpu/SkGr.cpp',
-        '../src/gpu/SkGrFontScaler.cpp',
-        '../src/gpu/SkGrTexturePixelRef.cpp',
-
-        '../src/gpu/gl/SkGLContext.cpp',
-        '../src/gpu/gl/SkNullGLContext.cpp',
-
-        '../src/gpu/android/SkNativeGLContext_android.cpp',
-
-        '../src/gpu/mac/SkNativeGLContext_mac.cpp',
-
-        '../src/gpu/win/SkNativeGLContext_win.cpp',
-
-        '../src/gpu/unix/SkNativeGLContext_unix.cpp',
-
-        '../src/gpu/mesa/SkMesaGLContext.cpp',
+        '<@(skgr_sources)',
+        '<@(skgr_native_gl_sources)',
+        '<@(skgr_angle_gl_sources)',
+        '<@(skgr_mesa_gl_sources)',
+        '<@(skgr_debug_gl_sources)',
+        '<@(skgr_null_gl_sources)',
+        'gpu.gypi', # Makes the gypi appear in IDEs (but does not modify the build).
       ],
       'conditions': [
         [ 'not skia_mesa', {
           'sources!': [
-            '../src/gpu/mesa/SkMesaGLContext.cpp',
+            '../src/gpu/gl/mesa/SkMesaGLContext.cpp',
           ],
         }],
         [ 'skia_mesa and skia_os == "mac"', {
@@ -137,160 +158,76 @@
              '$(SDKROOT)/usr/X11/include/',
           ],
         }],
+        [ 'not skia_angle', {
+          'sources!': [
+            '../include/gpu/gl/SkANGLEGLContext.h',
+            '../src/gpu/gl/angle/SkANGLEGLContext.cpp',
+            '../src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp',
+          ],
+        }],
       ],
     },
     {
       'target_name': 'gr',
+      'product_name': 'skia_gr',
       'type': 'static_library',
-      'include_dirs': [
+      'standalone_static_library': 1,
+      'includes': [
+        'gpu.gypi',
+      ],
+     'include_dirs': [
         '../include/core',
         '../include/config',
+        '../include/utils',
         '../include/gpu',
         '../src/core', # SkRasterClip.h
+        '../src/gpu'
       ],
       'dependencies': [
-        'libtess.gyp:libtess',
+        'angle.gyp:*',
+      ],
+      'export_dependent_settings': [
+        'angle.gyp:*',
       ],
       'sources': [
-        '../include/gpu/GrClip.h',
-        '../include/gpu/GrClipIterator.h',
-        '../include/gpu/GrColor.h',
-        '../include/gpu/GrConfig.h',
-        '../include/gpu/GrContext.h',
-        '../include/gpu/GrFontScaler.h',
-        '../include/gpu/GrGlyph.h',
-        '../include/gpu/GrInstanceCounter.h',
-        '../include/gpu/GrKey.h',
-        '../include/gpu/GrMatrix.h',
-        '../include/gpu/GrNoncopyable.h',
-        '../include/gpu/GrPaint.h',
-        '../include/gpu/GrPath.h',
-        '../include/gpu/GrPoint.h',
-        '../include/gpu/GrRect.h',
-        '../include/gpu/GrRefCnt.h',
-        '../include/gpu/GrRenderTarget.h',
-        '../include/gpu/GrResource.h',
-        '../include/gpu/GrSamplerState.h',
-        '../include/gpu/GrScalar.h',
-        '../include/gpu/GrTextContext.h',
-        '../include/gpu/GrTexture.h',
-        '../include/gpu/GrTypes.h',
-        '../include/gpu/GrUserConfig.h',
-
-        '../include/gpu/gl/GrGLConfig.h',
-        '../include/gpu/gl/GrGLConfig_chrome.h',
-        '../include/gpu/gl/GrGLDefines.h',
-        '../include/gpu/gl/GrGLInterface.h',
-
-        '../src/gpu/GrAAHairLinePathRenderer.cpp',
-        '../src/gpu/GrAAHairLinePathRenderer.h',
-        '../src/gpu/GrAAConvexPathRenderer.cpp',
-        '../src/gpu/GrAAConvexPathRenderer.h',
-        '../src/gpu/GrAddPathRenderers_default.cpp',
-        '../src/gpu/GrAllocator.h',
-        '../src/gpu/GrAllocPool.h',
-        '../src/gpu/GrAllocPool.cpp',
-        '../src/gpu/GrAtlas.cpp',
-        '../src/gpu/GrAtlas.h',
-        '../src/gpu/GrBinHashKey.h',
-        '../src/gpu/GrBufferAllocPool.cpp',
-        '../src/gpu/GrBufferAllocPool.h',
-        '../src/gpu/GrClip.cpp',
-        '../src/gpu/GrContext.cpp',
-        '../src/gpu/GrDefaultPathRenderer.cpp',
-        '../src/gpu/GrDefaultPathRenderer.h',
-        '../src/gpu/GrDrawState.h',
-        '../src/gpu/GrDrawTarget.cpp',
-        '../src/gpu/GrDrawTarget.h',
-        '../src/gpu/GrGeometryBuffer.h',
-        '../src/gpu/GrGpu.cpp',
-        '../src/gpu/GrGpu.h',
-        '../src/gpu/GrGpuFactory.cpp',
-        '../src/gpu/GrGpuVertex.h',
-        '../src/gpu/GrIndexBuffer.h',
-        '../src/gpu/GrInOrderDrawBuffer.cpp',
-        '../src/gpu/GrInOrderDrawBuffer.h',
-        '../src/gpu/GrMatrix.cpp',
-        '../src/gpu/GrMemory.cpp',
-        '../src/gpu/GrPathRendererChain.cpp',
-        '../src/gpu/GrPathRendererChain.h',
-        '../src/gpu/GrPathRenderer.cpp',
-        '../src/gpu/GrPathRenderer.h',
-        '../src/gpu/GrPathUtils.cpp',
-        '../src/gpu/GrPathUtils.h',
-        '../src/gpu/GrPlotMgr.h',
-        '../src/gpu/GrRandom.h',
-        '../src/gpu/GrRectanizer.cpp',
-        '../src/gpu/GrRectanizer.h',
-        '../src/gpu/GrRedBlackTree.h',
-        '../src/gpu/GrRenderTarget.cpp',
-        '../src/gpu/GrResource.cpp',
-        '../src/gpu/GrResourceCache.cpp',
-        '../src/gpu/GrResourceCache.h',
-        '../src/gpu/GrStencil.cpp',
-        '../src/gpu/GrStencil.h',
-        '../src/gpu/GrStencilBuffer.cpp',
-        '../src/gpu/GrStencilBuffer.h',
-        '../src/gpu/GrStringBuilder.h',
-        '../src/gpu/GrTBSearch.h',
-        '../src/gpu/GrTDArray.h',
-        '../src/gpu/GrTesselatedPathRenderer.cpp',
-        '../src/gpu/GrTesselatedPathRenderer.h',
-        '../src/gpu/GrTextContext.cpp',
-        '../src/gpu/GrTextStrike.cpp',
-        '../src/gpu/GrTextStrike.h',
-        '../src/gpu/GrTextStrike_impl.h',
-        '../src/gpu/GrTexture.cpp',
-        '../src/gpu/GrTHashCache.h',
-        '../src/gpu/GrTLList.h',
-        '../src/gpu/GrVertexBuffer.h',
-        '../src/gpu/gr_unittests.cpp',
-
-        '../src/gpu/gl/GrGLCaps.cpp',
-        '../src/gpu/gl/GrGLCaps.h',
-        '../src/gpu/gl/GrGLContextInfo.cpp',
-        '../src/gpu/gl/GrGLContextInfo.h',
-        '../src/gpu/gl/GrGLCreateNativeInterface_none.cpp',
-        '../src/gpu/gl/GrGLCreateNullInterface.cpp',
-        '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
-        '../src/gpu/gl/GrGLDefaultInterface_native.cpp',
-        '../src/gpu/gl/GrGLIndexBuffer.cpp',
-        '../src/gpu/gl/GrGLIndexBuffer.h',
-        '../src/gpu/gl/GrGLInterface.cpp',
-        '../src/gpu/gl/GrGLIRect.h',
-        '../src/gpu/gl/GrGLProgram.cpp',
-        '../src/gpu/gl/GrGLProgram.h',
-        '../src/gpu/gl/GrGLRenderTarget.cpp',
-        '../src/gpu/gl/GrGLRenderTarget.h',
-        '../src/gpu/gl/GrGLShaderVar.h',
-        '../src/gpu/gl/GrGLSL.cpp',
-        '../src/gpu/gl/GrGLSL.h',
-        '../src/gpu/gl/GrGLStencilBuffer.cpp',
-        '../src/gpu/gl/GrGLStencilBuffer.h',
-        '../src/gpu/gl/GrGLTexture.cpp',
-        '../src/gpu/gl/GrGLTexture.h',
-        '../src/gpu/gl/GrGLUtil.cpp',
-        '../src/gpu/gl/GrGLVertexBuffer.cpp',
-        '../src/gpu/gl/GrGLVertexBuffer.h',
-        '../src/gpu/gl/GrGpuGL.cpp',
-        '../src/gpu/gl/GrGpuGL.h',
-        '../src/gpu/gl/GrGpuGLShaders.cpp',
-        '../src/gpu/gl/GrGpuGLShaders.h',
-
-        '../src/gpu/mac/GrGLCreateNativeInterface_mac.cpp',
-
-        '../src/gpu/win/GrGLCreateNativeInterface_win.cpp',
-
-        '../src/gpu/unix/GrGLCreateNativeInterface_unix.cpp',
-
-        '../src/gpu/android/GrGLCreateNativeInterface_android.cpp',
-
-        '../src/gpu/mesa/GrGLCreateMesaInterface.cpp',
+        '<@(gr_sources)',
+        '<@(gr_native_gl_sources)',
+        '<@(gr_angle_gl_sources)',
+        '<@(gr_mesa_gl_sources)',
+        '<@(gr_debug_gl_sources)',
+        '<@(gr_null_gl_sources)',
+        'gpu.gypi', # Makes the gypi appear in IDEs (but does not modify the build).
       ],
       'defines': [
         'GR_IMPLEMENTATION=1',
       ],
       'conditions': [
+        [ 'skia_nv_path_rendering', {
+          'defines': [
+            'GR_GL_USE_NV_PATH_RENDERING=1',
+          ],
+        }],
+        [ 'skia_stroke_path_rendering', {
+          'sources': [
+            '../experimental/StrokePathRenderer/GrStrokePathRenderer.h',
+            '../experimental/StrokePathRenderer/GrStrokePathRenderer.cpp',
+          ],
+          'defines': [
+            'GR_STROKE_PATH_RENDERING=1',
+          ],
+        }],
+        [ 'skia_android_path_rendering', {
+          'sources': [
+            '../experimental/AndroidPathRenderer/GrAndroidPathRenderer.cpp',
+            '../experimental/AndroidPathRenderer/GrAndroidPathRenderer.h',
+            '../experimental/AndroidPathRenderer/AndroidPathRenderer.cpp',
+            '../experimental/AndroidPathRenderer/AndroidPathRenderer.h',
+            '../experimental/AndroidPathRenderer/Vertex.h',
+          ],
+          'defines': [
+            'GR_ANDROID_PATH_RENDERING=1',
+          ],
+        }],
         [ 'skia_os == "linux"', {
           'sources!': [
             '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
@@ -299,10 +236,18 @@
           'link_settings': {
             'libraries': [
               '-lGL',
+              '-lGLU',
               '-lX11',
             ],
           },
         }],
+        [ 'skia_os == "nacl"', {
+          'link_settings': {
+            'libraries': [
+              '-lppapi_gles2',
+            ],
+          },
+        }],
         [ 'skia_mesa and skia_os == "linux"', {
           'link_settings': {
             'libraries': [
@@ -333,15 +278,23 @@
         }],
         [ 'not skia_mesa', {
           'sources!': [
-            '../src/gpu/mesa/GrGLCreateMesaInterface.cpp',
+            '../src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp',
           ],
         }],
-        [ 'skia_os == "win"', {
+        [ 'skia_os in ["win", "ios"]', {
           'sources!': [
             '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
             '../src/gpu/gl/GrGLCreateNativeInterface_none.cpp',
           ],
         }],
+        [ 'not skia_angle', {
+          'sources!': [
+            '../include/gpu/gl/SkANGLEGLContext.h',
+
+            '../src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp',
+            '../src/gpu/gl/angle/SkANGLEGLContext.cpp',
+          ],
+        }],
         [ 'skia_os == "android"', {
           'sources!': [
             '../src/gpu/gl/GrGLDefaultInterface_none.cpp',
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi
new file mode 100644
index 0000000..bf301bb
--- /dev/null
+++ b/gyp/gpu.gypi
@@ -0,0 +1,268 @@
+# Include this gypi to include all 'gr' and 'skgr' files
+# The parent gyp/gypi file must define
+#       'skia_src_path'     e.g. skia/trunk/src
+#       'skia_include_path' e.g. skia/trunk/include
+#
+# The skia build defines these in common_variables.gypi
+#
+{
+  'variables': {
+    'gr_sources': [
+      '<(skia_include_path)/gpu/GrAARectRenderer.h',
+      '<(skia_include_path)/gpu/GrBackendEffectFactory.h',
+      '<(skia_include_path)/gpu/GrClipData.h',
+      '<(skia_include_path)/gpu/GrColor.h',
+      '<(skia_include_path)/gpu/GrConfig.h',
+      '<(skia_include_path)/gpu/GrContext.h',
+      '<(skia_include_path)/gpu/GrContextFactory.h',
+      '<(skia_include_path)/gpu/GrEffect.h',
+      '<(skia_include_path)/gpu/GrEffectStage.h',
+      '<(skia_include_path)/gpu/GrEffectUnitTest.h',
+      '<(skia_include_path)/gpu/GrFontScaler.h',
+      '<(skia_include_path)/gpu/GrGlyph.h',
+      '<(skia_include_path)/gpu/GrKey.h',
+      '<(skia_include_path)/gpu/GrNoncopyable.h',
+      '<(skia_include_path)/gpu/GrPaint.h',
+      '<(skia_include_path)/gpu/GrPathRendererChain.h',
+      '<(skia_include_path)/gpu/GrPoint.h',
+      '<(skia_include_path)/gpu/GrRect.h',
+      '<(skia_include_path)/gpu/GrRefCnt.h',
+      '<(skia_include_path)/gpu/GrRenderTarget.h',
+      '<(skia_include_path)/gpu/GrResource.h',
+      '<(skia_include_path)/gpu/GrSurface.h',
+      '<(skia_include_path)/gpu/GrTBackendEffectFactory.h',
+      '<(skia_include_path)/gpu/GrTextContext.h',
+      '<(skia_include_path)/gpu/GrTexture.h',
+      '<(skia_include_path)/gpu/GrTextureAccess.h',
+      '<(skia_include_path)/gpu/GrTypes.h',
+      '<(skia_include_path)/gpu/GrUserConfig.h',
+
+      '<(skia_include_path)/gpu/gl/GrGLConfig.h',
+      '<(skia_include_path)/gpu/gl/GrGLFunctions.h',
+      '<(skia_include_path)/gpu/gl/GrGLInterface.h',
+
+      '<(skia_src_path)/gpu/GrAAHairLinePathRenderer.cpp',
+      '<(skia_src_path)/gpu/GrAAHairLinePathRenderer.h',
+      '<(skia_src_path)/gpu/GrAAConvexPathRenderer.cpp',
+      '<(skia_src_path)/gpu/GrAAConvexPathRenderer.h',
+      '<(skia_src_path)/gpu/GrAARectRenderer.cpp',
+      '<(skia_src_path)/gpu/GrAddPathRenderers_default.cpp',
+      '<(skia_src_path)/gpu/GrAllocator.h',
+      '<(skia_src_path)/gpu/GrAllocPool.h',
+      '<(skia_src_path)/gpu/GrAllocPool.cpp',
+      '<(skia_src_path)/gpu/GrAtlas.cpp',
+      '<(skia_src_path)/gpu/GrAtlas.h',
+      '<(skia_src_path)/gpu/GrBinHashKey.h',
+      '<(skia_src_path)/gpu/GrBufferAllocPool.cpp',
+      '<(skia_src_path)/gpu/GrBufferAllocPool.h',
+      '<(skia_src_path)/gpu/GrCacheID.cpp',
+      '<(skia_src_path)/gpu/GrClipData.cpp',
+      '<(skia_src_path)/gpu/GrContext.cpp',
+      '<(skia_src_path)/gpu/GrDefaultPathRenderer.cpp',
+      '<(skia_src_path)/gpu/GrDefaultPathRenderer.h',
+      '<(skia_src_path)/gpu/GrDrawState.cpp',
+      '<(skia_src_path)/gpu/GrDrawState.h',
+      '<(skia_src_path)/gpu/GrDrawTarget.cpp',
+      '<(skia_src_path)/gpu/GrDrawTarget.h',
+      '<(skia_src_path)/gpu/GrEffect.cpp',
+      '<(skia_src_path)/gpu/GrGeometryBuffer.cpp',
+      '<(skia_src_path)/gpu/GrGeometryBuffer.h',
+      '<(skia_src_path)/gpu/GrClipMaskCache.h',
+      '<(skia_src_path)/gpu/GrClipMaskCache.cpp',
+      '<(skia_src_path)/gpu/GrClipMaskManager.h',
+      '<(skia_src_path)/gpu/GrClipMaskManager.cpp',
+      '<(skia_src_path)/gpu/GrGpu.cpp',
+      '<(skia_src_path)/gpu/GrGpu.h',
+      '<(skia_src_path)/gpu/GrGpuFactory.cpp',
+      '<(skia_src_path)/gpu/GrGpuVertex.h',
+      '<(skia_src_path)/gpu/GrIndexBuffer.h',
+      '<(skia_src_path)/gpu/GrInOrderDrawBuffer.cpp',
+      '<(skia_src_path)/gpu/GrInOrderDrawBuffer.h',
+      '<(skia_src_path)/gpu/GrMemory.cpp',
+      '<(skia_src_path)/gpu/GrMemoryPool.cpp',
+      '<(skia_src_path)/gpu/GrMemoryPool.h',
+      '<(skia_src_path)/gpu/GrPath.cpp',
+      '<(skia_src_path)/gpu/GrPath.h',
+      '<(skia_src_path)/gpu/GrPathRendererChain.cpp',
+      '<(skia_src_path)/gpu/GrPathRenderer.cpp',
+      '<(skia_src_path)/gpu/GrPathRenderer.h',
+      '<(skia_src_path)/gpu/GrPathUtils.cpp',
+      '<(skia_src_path)/gpu/GrPathUtils.h',
+      '<(skia_src_path)/gpu/GrPlotMgr.h',
+      '<(skia_src_path)/gpu/GrRectanizer.cpp',
+      '<(skia_src_path)/gpu/GrRectanizer.h',
+      '<(skia_src_path)/gpu/GrRedBlackTree.h',
+      '<(skia_src_path)/gpu/GrRenderTarget.cpp',
+      '<(skia_src_path)/gpu/GrReducedClip.cpp',
+      '<(skia_src_path)/gpu/GrReducedClip.h',
+      '<(skia_src_path)/gpu/GrResource.cpp',
+      '<(skia_src_path)/gpu/GrResourceCache.cpp',
+      '<(skia_src_path)/gpu/GrResourceCache.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/GrStencilBuffer.cpp',
+      '<(skia_src_path)/gpu/GrStencilBuffer.h',
+      '<(skia_src_path)/gpu/GrTBSearch.h',
+      '<(skia_src_path)/gpu/GrSWMaskHelper.cpp',
+      '<(skia_src_path)/gpu/GrSWMaskHelper.h',
+      '<(skia_src_path)/gpu/GrSoftwarePathRenderer.cpp',
+      '<(skia_src_path)/gpu/GrSoftwarePathRenderer.h',
+      '<(skia_src_path)/gpu/GrSurface.cpp',
+      '<(skia_src_path)/gpu/GrTemplates.h',
+      '<(skia_src_path)/gpu/GrTextContext.cpp',
+      '<(skia_src_path)/gpu/GrTextStrike.cpp',
+      '<(skia_src_path)/gpu/GrTextStrike.h',
+      '<(skia_src_path)/gpu/GrTextStrike_impl.h',
+      '<(skia_src_path)/gpu/GrTexture.cpp',
+      '<(skia_src_path)/gpu/GrTextureAccess.cpp',
+      '<(skia_src_path)/gpu/GrTHashCache.h',
+      '<(skia_src_path)/gpu/GrVertexBuffer.h',
+      '<(skia_src_path)/gpu/gr_unittests.cpp',
+
+      '<(skia_src_path)/gpu/effects/Gr1DKernelEffect.h',
+      '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.h',
+      '<(skia_src_path)/gpu/effects/GrTextureStripAtlas.cpp',
+      '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrConfigConversionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrConvolutionEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrConvolutionEffect.h',
+      '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrSimpleTextureEffect.h',
+      '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrSingleTextureEffect.h',
+      '<(skia_src_path)/gpu/effects/GrTextureDomainEffect.cpp',
+      '<(skia_src_path)/gpu/effects/GrTextureDomainEffect.h',
+
+      '<(skia_src_path)/gpu/gl/GrGLCaps.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLCaps.h',
+      '<(skia_src_path)/gpu/gl/GrGLContextInfo.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLContextInfo.h',
+      '<(skia_src_path)/gpu/gl/GrGLCreateNativeInterface_none.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLDefaultInterface_none.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLDefines.h',
+      '<(skia_src_path)/gpu/gl/GrGLEffect.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLEffect.h',
+      '<(skia_src_path)/gpu/gl/GrGLEffectMatrix.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLEffectMatrix.h',
+      '<(skia_src_path)/gpu/gl/GrGLIndexBuffer.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLIndexBuffer.h',
+      '<(skia_src_path)/gpu/gl/GrGLInterface.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLIRect.h',
+      '<(skia_src_path)/gpu/gl/GrGLPath.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLPath.h',
+      '<(skia_src_path)/gpu/gl/GrGLProgram.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLProgram.h',
+      '<(skia_src_path)/gpu/gl/GrGLRenderTarget.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLRenderTarget.h',
+      '<(skia_src_path)/gpu/gl/GrGLShaderBuilder.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLShaderBuilder.h',
+      '<(skia_src_path)/gpu/gl/GrGLShaderVar.h',
+      '<(skia_src_path)/gpu/gl/GrGLSL.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLSL.h',
+      '<(skia_src_path)/gpu/gl/GrGLStencilBuffer.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLStencilBuffer.h',
+      '<(skia_src_path)/gpu/gl/GrGLTexture.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLTexture.h',
+      '<(skia_src_path)/gpu/gl/GrGLUtil.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLUtil.h',
+      '<(skia_src_path)/gpu/gl/GrGLUniformManager.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLUniformManager.h',
+      '<(skia_src_path)/gpu/gl/GrGLUniformHandle.h',
+      '<(skia_src_path)/gpu/gl/GrGLVertexBuffer.cpp',
+      '<(skia_src_path)/gpu/gl/GrGLVertexBuffer.h',
+      '<(skia_src_path)/gpu/gl/GrGpuGL.cpp',
+      '<(skia_src_path)/gpu/gl/GrGpuGL.h',
+      '<(skia_src_path)/gpu/gl/GrGpuGL_program.cpp',
+    ],
+    'gr_native_gl_sources': [
+      '<(skia_src_path)/gpu/gl/GrGLDefaultInterface_native.cpp',
+      '<(skia_src_path)/gpu/gl/mac/GrGLCreateNativeInterface_mac.cpp',
+      '<(skia_src_path)/gpu/gl/win/GrGLCreateNativeInterface_win.cpp',
+      '<(skia_src_path)/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp',
+      '<(skia_src_path)/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp',
+      '<(skia_src_path)/gpu/gl/android/GrGLCreateNativeInterface_android.cpp',
+    ],
+    'gr_mesa_gl_sources': [
+      '<(skia_src_path)/gpu/gl/mesa/GrGLCreateMesaInterface.cpp',
+    ],
+    'gr_angle_gl_sources': [
+      '<(skia_src_path)/gpu/gl/angle/GrGLCreateANGLEInterface.cpp',
+    ],
+    'gr_debug_gl_sources': [
+      '<(skia_src_path)/gpu/gl/debug/GrGLCreateDebugInterface.cpp',
+      '<(skia_src_path)/gpu/gl/debug/GrFakeRefObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrBufferObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrBufferObj.cpp',
+      '<(skia_src_path)/gpu/gl/debug/GrFBBindableObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrRenderBufferObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrTextureObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrTextureObj.cpp',
+      '<(skia_src_path)/gpu/gl/debug/GrTextureUnitObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrTextureUnitObj.cpp',
+      '<(skia_src_path)/gpu/gl/debug/GrFrameBufferObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrFrameBufferObj.cpp',
+      '<(skia_src_path)/gpu/gl/debug/GrShaderObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrShaderObj.cpp',
+      '<(skia_src_path)/gpu/gl/debug/GrProgramObj.h',
+      '<(skia_src_path)/gpu/gl/debug/GrProgramObj.cpp',
+      '<(skia_src_path)/gpu/gl/debug/GrDebugGL.h',
+      '<(skia_src_path)/gpu/gl/debug/GrDebugGL.cpp',
+    ],
+    'gr_null_gl_sources': [
+      '<(skia_src_path)/gpu/gl/GrGLCreateNullInterface.cpp',
+    ],
+
+    'skgr_sources': [
+      '<(skia_include_path)/gpu/SkGpuDevice.h',
+      '<(skia_include_path)/gpu/SkGr.h',
+      '<(skia_include_path)/gpu/SkGrPixelRef.h',
+      '<(skia_include_path)/gpu/SkGrTexturePixelRef.h',
+
+      '<(skia_include_path)/gpu/gl/SkGLContext.h',
+
+      '<(skia_src_path)/gpu/SkGpuDevice.cpp',
+      '<(skia_src_path)/gpu/SkGr.cpp',
+      '<(skia_src_path)/gpu/SkGrFontScaler.cpp',
+      '<(skia_src_path)/gpu/SkGrPixelRef.cpp',
+      '<(skia_src_path)/gpu/SkGrTexturePixelRef.cpp',
+
+      '<(skia_src_path)/image/SkImage_Gpu.cpp',
+      '<(skia_src_path)/image/SkSurface_Gpu.cpp',
+
+      '<(skia_src_path)/gpu/gl/SkGLContext.cpp'
+    ],
+    'skgr_native_gl_sources': [
+      '<(skia_include_path)/gpu/gl/SkNativeGLContext.h',
+      '<(skia_src_path)/gpu/gl/mac/SkNativeGLContext_mac.cpp',
+      '<(skia_src_path)/gpu/gl/nacl/SkNativeGLContext_nacl.cpp',
+      '<(skia_src_path)/gpu/gl/win/SkNativeGLContext_win.cpp',
+      '<(skia_src_path)/gpu/gl/unix/SkNativeGLContext_unix.cpp',
+      '<(skia_src_path)/gpu/gl/android/SkNativeGLContext_android.cpp',
+      '<(skia_src_path)/gpu/gl/iOS/SkNativeGLContext_iOS.mm',
+    ],
+    'skgr_angle_gl_sources': [
+      '<(skia_include_path)/gpu/gl/SkANGLEGLContext.h',
+      '<(skia_src_path)/gpu/gl/angle/SkANGLEGLContext.cpp',
+    ],
+    'skgr_mesa_gl_sources': [
+      '<(skia_include_path)/gpu/gl/SkMesaGLContext.h',
+      '<(skia_src_path)/gpu/gl/mesa/SkMesaGLContext.cpp',
+    ],
+    'skgr_debug_gl_sources': [
+      '<(skia_include_path)/gpu/gl/SkDebugGLContext.h',
+      '<(skia_src_path)/gpu/gl/debug/SkDebugGLContext.cpp',
+    ],
+    'skgr_null_gl_sources': [
+      '<(skia_include_path)/gpu/gl/SkNullGLContext.h',
+      '<(skia_src_path)/gpu/gl/SkNullGLContext.cpp',
+    ],
+  },
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/iOSSampleApp.gyp b/gyp/iOSSampleApp.gyp
deleted file mode 100644
index b864ca6..0000000
--- a/gyp/iOSSampleApp.gyp
+++ /dev/null
@@ -1,278 +0,0 @@
-{
-  'includes': [
-    'target_defaults.gypi',
-  ],
-  'defines!': [
-    'SK_BUILD_FOR_MAC',
-  ],
-  'targets': [
-    {
-      'target_name': 'iOSSampleApp',
-      'type': 'executable',
-      'mac_bundle' : 1,
-      'include_dirs' : [
-        '../src/core', # needed to get SkConcaveToTriangle, maybe this should be moved to include dir?
-        '../gm',       # SampleGM.cpp pulls gm.h
-        '../include/pipe', # To pull in SkGPipe.h for pipe reader/writer
-      ],
-      'sources': [
-        # gm files needed for SampleGM.cpp
-        '../gm/bitmapfilters.cpp',
-        '../gm/blurs.cpp',
-        '../gm/complexclip.cpp',
-        '../gm/filltypes.cpp',
-        '../gm/gm.h',
-        '../gm/gradients.cpp',
-        '../gm/nocolorbleed.cpp',
-        '../gm/points.cpp',
-        '../gm/poly2poly.cpp',
-        '../gm/shadertext.cpp',
-        '../gm/shadows.cpp',
-        '../gm/shapes.cpp',
-        '../gm/tilemodes.cpp',
-        '../gm/xfermodes.cpp',
-
-        '../samplecode/ClockFaceView.cpp',
-        '../samplecode/OverView.cpp',
-        '../samplecode/Sample2PtRadial.cpp',
-        '../samplecode/SampleAll.cpp',
-        '../samplecode/SampleAnimator.cpp',
-        '../samplecode/SampleApp.cpp',
-        '../samplecode/SampleArc.cpp',
-        '../samplecode/SampleAvoid.cpp',
-        '../samplecode/SampleBigBlur.cpp',
-        '../samplecode/SampleBigGradient.cpp',
-        '../samplecode/SampleBitmapRect.cpp',
-        '../samplecode/SampleBlur.cpp',
-        '../samplecode/SampleCamera.cpp',
-        '../samplecode/SampleCircle.cpp',
-        '../samplecode/SampleCode.h',
-        '../samplecode/SampleColorFilter.cpp',
-        '../samplecode/SampleComplexClip.cpp',
-        '../samplecode/SampleConcavePaths.cpp',
-        '../samplecode/SampleCull.cpp',
-        '../samplecode/SampleDecode.cpp',
-        '../samplecode/SampleDegenerateTwoPtRadials.cpp',
-        '../samplecode/SampleDither.cpp',
-        '../samplecode/SampleDitherBitmap.cpp',
-        '../samplecode/SampleDrawBitmap.cpp',
-        '../samplecode/SampleDrawLooper.cpp',
-        '../samplecode/SampleEffects.cpp',
-        '../samplecode/SampleEmboss.cpp',
-        '../samplecode/SampleEncode.cpp',
-        '../samplecode/SampleExtractAlpha.cpp',
-        '../samplecode/SampleFillType.cpp',
-        '../samplecode/SampleFilter.cpp',
-        '../samplecode/SampleFilter2.cpp',
-        '../samplecode/SampleFontCache.cpp',
-        '../samplecode/SampleFontScalerTest.cpp',
-        '../samplecode/SampleFuzz.cpp',
-        '../samplecode/SampleGM.cpp',
-        '../samplecode/SampleGradients.cpp',
-        '../samplecode/SampleHairline.cpp',
-        '../samplecode/SampleImage.cpp',
-        '../samplecode/SampleImageDir.cpp',
-        '../samplecode/SampleLayerMask.cpp',
-        '../samplecode/SampleLayers.cpp',
-        '../samplecode/SampleLCD.cpp',
-        '../samplecode/SampleLineClipper.cpp',
-        '../samplecode/SampleLines.cpp',
-        '../samplecode/SampleMeasure.cpp',
-        '../samplecode/SampleMipMap.cpp',
-        '../samplecode/SampleMovie.cpp',
-        '../samplecode/SampleNinePatch.cpp',
-        '../samplecode/SampleOvalTest.cpp',
-        '../samplecode/SampleOverflow.cpp',
-        '../samplecode/SamplePageFlip.cpp',
-        '../samplecode/SamplePatch.cpp',
-        '../samplecode/SamplePath.cpp',
-        '../samplecode/SamplePathClip.cpp',
-        '../samplecode/SamplePathEffects.cpp',
-        '../samplecode/SamplePicture.cpp',
-        '../samplecode/SamplePoints.cpp',
-        '../samplecode/SamplePolyToPoly.cpp',
-        '../samplecode/SampleAARects.cpp',
-        '../samplecode/SampleRegion.cpp',
-        '../samplecode/SampleRepeatTile.cpp',
-        '../samplecode/SampleShaders.cpp',
-        '../samplecode/SampleShaderText.cpp',
-        '../samplecode/SampleShapes.cpp',
-        '../samplecode/SampleSkLayer.cpp',
-        '../samplecode/SampleSlides.cpp',
-        '../samplecode/SampleStrokePath.cpp',
-        '../samplecode/SampleStrokeText.cpp',
-        '../samplecode/SampleTests.cpp',
-        '../samplecode/SampleText.cpp',
-        '../samplecode/SampleTextAlpha.cpp',
-        '../samplecode/SampleTextBox.cpp',
-        '../samplecode/SampleTextEffects.cpp',
-        '../samplecode/SampleTextOnPath.cpp',
-        '../samplecode/SampleTextureDomain.cpp',
-        '../samplecode/SampleTiling.cpp',
-        '../samplecode/SampleTinyBitmap.cpp',
-        '../samplecode/SampleTriangles.cpp',
-        '../samplecode/SampleTypeface.cpp',
-        '../samplecode/SampleUnitMapper.cpp',
-        '../samplecode/SampleVertices.cpp',
-        '../samplecode/SampleXfermodes.cpp',
-        '../samplecode/SampleXfermodesBlur.cpp',
-        
-        # Dependencies for the pipe code in SampleApp
-        '../src/pipe/SkGPipeRead.cpp',
-        '../src/pipe/SkGPipeWrite.cpp',
-        
-        # DrawingBoard
-        '../experimental/DrawingBoard/SkColorPalette.h',
-        '../experimental/DrawingBoard/SkColorPalette.cpp',
-        '../experimental/DrawingBoard/SkNetPipeController.h',
-        '../experimental/DrawingBoard/SkNetPipeController.cpp',
-        '../experimental/DrawingBoard/SampleDrawingClient.cpp',
-        '../experimental/DrawingBoard/SampleDrawingServer.cpp',
-    
-        # Networking
-        '../experimental/Networking/SampleNetPipeReader.cpp',
-        '../experimental/Networking/SkSockets.cpp',
-        '../experimental/Networking/SkSockets.h',
-        
-        # Transition
-        '../src/utils/SkInterpolator.cpp',
-        '../include/utils/SkInterpolator.h',
-        '../samplecode/TransitionView.cpp',
-      ],
-      'sources!': [
-        '../samplecode/SampleSkLayer.cpp', #relies on SkMatrix44 which doesn't compile
-        '../samplecode/SampleTests.cpp',   #includes unknown file SkShaderExtras.h
-        '../samplecode/SampleWarp.cpp',
-        '../samplecode/SampleFontCache.cpp',
-      ],
-      'dependencies': [
-        'core.gyp:core',
-        'effects.gyp:effects',
-        'images.gyp:images',
-        'ports.gyp:ports',
-        'views.gyp:views',
-        'utils.gyp:utils',
-        'animator.gyp:animator',
-        'xml.gyp:xml',
-        'experimental.gyp:experimental',
-        'gpu.gyp:gr',
-        'gpu.gyp:skgr',
-        'pdf.gyp:pdf',
-      ],
-      'conditions' : [
-       [ 'OS == "linux" or OS == "freebsd" or OS == "openbsd" or OS == "solaris"', {
-         'sources!': [
-            '../samplecode/SampleDecode.cpp',
-         ],
-        }],
-        [ 'OS == "win"', {
-          'sources!': [
-            # require UNIX functions
-            '../samplecode/SampleEncode.cpp',
-            '../samplecode/SamplePageFlip.cpp',
-          ],
-        }],
-        [ 'OS == "mac"', {
-          'sources!': [
-            '../samplecode/SampleDecode.cpp',
-            '../src/gpu/mac/GrGLDefaultInterface_mac.cpp',
-          ],
-          'sources': [
-            # Shared resources
-            '../experimental/SkEventNotifier.h',
-            '../experimental/SkEventNotifier.mm',
-            '../experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig',
-            '../experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig',
-            '../experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig',
-            '../experimental/iOSSampleApp/iOSSampleApp-Info.plist',
-            '../experimental/iOSSampleApp/iOSSampleApp_Prefix.pch',
-            '../experimental/iOSSampleApp/Shared/SkOptionListController.h',
-            '../experimental/iOSSampleApp/Shared/SkOptionListController.mm',
-            '../experimental/iOSSampleApp/Shared/SkUIRootViewController.h',
-            '../experimental/iOSSampleApp/Shared/SkUIRootViewController.mm',
-            '../experimental/iOSSampleApp/Shared/SkOptionsTableViewController.h',
-            '../experimental/iOSSampleApp/Shared/SkOptionsTableViewController.mm',
-            '../experimental/iOSSampleApp/Shared/SkUIView.h',
-            '../experimental/iOSSampleApp/Shared/SkUIView.mm',
-            '../experimental/iOSSampleApp/Shared/SkUIDetailViewController.h',
-            '../experimental/iOSSampleApp/Shared/SkUIDetailViewController.mm',
-            '../experimental/iOSSampleApp/Shared/main.m',
-            
-            # iPad
-            '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.h',
-            '../experimental/iOSSampleApp/iPad/AppDelegate_iPad.mm',
-            '../experimental/iOSSampleApp/iPad/SkUISplitViewController.h',
-            '../experimental/iOSSampleApp/iPad/SkUISplitViewController.mm',
-            '../experimental/iOSSampleApp/iPad/MainWindow_iPad.xib',
-            
-            # iPhone
-            '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.h',
-            '../experimental/iOSSampleApp/iPhone/AppDelegate_iPhone.mm',
-            '../experimental/iOSSampleApp/iPhone/SkUINavigationController.h',
-            '../experimental/iOSSampleApp/iPhone/SkUINavigationController.mm',
-            '../experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib',
-
-            '../src/utils/ios/SkOSWindow_iOS.mm',
-            '../src/utils/ios/SkImageDecoder_iOS.mm',
-            '../src/utils/ios/SkStream_NSData.mm',
-            '../src/utils/ios/SkOSFile_iOS.mm',
-
-            '../include/utils/mac/SkCGUtils.h',
-            '../src/utils/mac/SkCreateCGImageRef.cpp',
-            '../experimental/iOSSampleApp/SkiOSSampleApp-Debug.xcconfig',
-            '../experimental/iOSSampleApp/SkiOSSampleApp-Release.xcconfig',
-          ],
-          'link_settings': {
-            'libraries': [
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CoreFoundation.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CoreGraphics.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/CoreText.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/UIKit.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/Foundation.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/QuartzCore.framework',
-              '/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS4.3.sdk/System/Library/Frameworks/OpenGLES.framework',
-            ],
-            'libraries!': [
-              #remove mac dependencies
-              '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
-              '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
-              '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
-              '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
-              '$(SDKROOT)/System/Library/Frameworks/ApplicationServices.framework',
-            ],
-          },
-          'include_dirs' : [
-            '../experimental/iOSSampleApp',
-            '../experimental/iOSSampleApp/iPad',
-            '../experimental/iOSSampleApp/iPhone',
-            '../include/utils/ios',
-            '../../include/gpu',
-          ],
-          #'xcode_settings' : {
-          #  'INFOPLIST_FILE' : '../experimental/iOSSampleApp/iOSSampleApp-Info.plist',
-          #  'ARCHS' : 'armv6 armv7',
-          #  'IPHONEOS_DEPLOYMENT_TARGET' : '4.2',
-          #  'SDKROOT' : 'iphoneos',
-          #  'TARGETED_DEVICE_FAMILY' : '1,2',
-          #  'USER_HEADER_SEARCH_PATHS' : '../../gpu/include/** ../../include/**',
-          #  'CODE_SIGN_IDENTITY' : 'iPhone Developer',
-          #  'GCC_PREPROCESSOR_DEFINITIONS' : 'SK_BUILD_FOR_IOS',
-          #  'GCC_OPTIMIZATION_LEVEL' : '0',
-          #},
-          'xcode_config_file': '../experimental/iOSSampleApp/SkiOSSampleApp-Base.xcconfig',
-          'mac_bundle_resources' : [
-            '../experimental/iOSSampleApp/iPad/MainWindow_iPad.xib',
-            '../experimental/iOSSampleApp/iPhone/MainWindow_iPhone.xib',
-          ],
-        }],
-
-      ],
-    },
-  ],
-}
-
-# Local Variables:
-# tab-width:2
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/images.gyp b/gyp/images.gyp
index 0732351..b979641 100644
--- a/gyp/images.gyp
+++ b/gyp/images.gyp
@@ -1,21 +1,24 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'images',
+      'product_name': 'skia_images',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'dependencies': [
+        'libjpeg.gyp:*',
         'utils.gyp:utils',
       ],
+      'export_dependent_settings': [
+        'libjpeg.gyp:*',
+      ],
       'include_dirs': [
         '../include/config',
         '../include/core',
         '../include/images',
       ],
       'sources': [
-        '../include/images/SkFlipPixelRef.h',
+        '../include/images/SkBitmapFactory.h',
         '../include/images/SkImageDecoder.h',
         '../include/images/SkImageEncoder.h',
         '../include/images/SkImageRef.h',
@@ -29,8 +32,8 @@
         '../src/images/SkBitmapRegionDecoder.cpp',
         '../src/images/SkBitmap_RLEPixels.h',
         '../src/images/SkCreateRLEPixelRef.cpp',
+        '../src/images/SkBitmapFactory.cpp',
         '../src/images/SkFDStream.cpp',
-        '../src/images/SkFlipPixelRef.cpp',
         '../src/images/SkImageDecoder.cpp',
         '../src/images/SkImageDecoder_Factory.cpp',
         '../src/images/SkImageDecoder_libbmp.cpp',
@@ -44,7 +47,10 @@
         '../src/images/SkImageRef.cpp',
         '../src/images/SkImageRefPool.cpp',
         '../src/images/SkImageRefPool.h',
+        '../src/images/SkImageRef_ashmem.h',
+        '../src/images/SkImageRef_ashmem.cpp',
         '../src/images/SkImageRef_GlobalPool.cpp',
+        '../src/images/SkImages.cpp',
         '../src/images/SkJpegUtility.cpp',
         '../src/images/SkMovie.cpp',
         '../src/images/SkMovie_gif.cpp',
@@ -58,16 +64,11 @@
       'conditions': [
         [ 'skia_os == "win"', {
           'sources!': [
-            '../include/images/SkJpegUtility.h',
-
             '../src/images/SkFDStream.cpp',
             '../src/images/SkImageDecoder_Factory.cpp',
             '../src/images/SkImageDecoder_libgif.cpp',
-            '../src/images/SkImageDecoder_libjpeg.cpp',
             '../src/images/SkImageDecoder_libpng.cpp',
-            '../src/images/SkImageDecoder_libpvjpeg.c',
             '../src/images/SkImageEncoder_Factory.cpp',
-            '../src/images/SkJpegUtility.cpp',
             '../src/images/SkMovie_gif.cpp',
           ],
           'link_settings': {
@@ -80,17 +81,12 @@
             '../src/ports/SkImageDecoder_WIC.cpp',
           ],
         }],
-        [ 'skia_os == "mac"', {
+        [ 'skia_os in ["mac", "ios"]', {
           'sources!': [
-            '../include/images/SkJpegUtility.h',
-
             '../src/images/SkImageDecoder_Factory.cpp',
             '../src/images/SkImageDecoder_libpng.cpp',
             '../src/images/SkImageDecoder_libgif.cpp',
-            '../src/images/SkImageDecoder_libjpeg.cpp',
-            '../src/images/SkImageDecoder_libpvjpeg.c',
             '../src/images/SkImageEncoder_Factory.cpp',
-            '../src/images/SkJpegUtility.cpp',
             '../src/images/SkMovie_gif.cpp',
           ],
         },{ #else if skia_os != mac
@@ -98,14 +94,9 @@
             '../src/ports/SkImageDecoder_CG.cpp',
           ],
         }],
-        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "nacl"]', {
           'sources!': [
-            '../include/images/SkJpegUtility.h',
-
-            '../src/images/SkImageDecoder_libjpeg.cpp',
             '../src/images/SkImageDecoder_libgif.cpp',
-            '../src/images/SkImageDecoder_libpvjpeg.c',
-            '../src/images/SkJpegUtility.cpp',
             '../src/images/SkMovie_gif.cpp',
           ],
           # libpng stuff:
@@ -123,14 +114,26 @@
           # end libpng stuff
         }],
         [ 'skia_os == "android"', {
-          'sources!': [
-            '../src/images/SkImageDecoder_libjpeg.cpp',
-            '../src/images/SkJpegUtility.cpp',
+          'include_dirs': [
+             '../src/utils',
           ],
           'dependencies': [
-             'android_system.gyp:gif',
-             'android_system.gyp:png',
+             'android_deps.gyp:gif',
+             'android_deps.gyp:png',
           ],
+          'defines': [
+            'SK_ENABLE_LIBPNG',
+          ],
+        },{ #else if skia_os != android
+          'sources!': [
+            '../src/images/SkImageRef_ashmem.h',
+            '../src/images/SkImageRef_ashmem.cpp',
+          ],
+        }],
+        [ 'skia_os == "ios"', {
+           'include_dirs': [
+             '../include/utils/mac',
+           ],
         }],
       ],
       'direct_dependent_settings': {
diff --git a/gyp/jsoncpp.gyp b/gyp/jsoncpp.gyp
new file mode 100644
index 0000000..1ee33fd
--- /dev/null
+++ b/gyp/jsoncpp.gyp
@@ -0,0 +1,73 @@
+# 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.
+
+# TODO: This file was copied from the external dependency
+# third_party/externals/jsoncpp-chromium/jsoncpp.gyp , at revision 125399,
+# with directory paths modified to work at this level.
+#
+# It would be better for us to depend on that gypfile within the external
+# dependency, but so far we have been unable to make that work reliably.
+# See https://code.google.com/p/skia/issues/detail?id=1023
+# and https://code.google.com/p/skia/source/detail?r=7115
+
+{
+  'targets': [
+    {
+      'target_name': 'jsoncpp',
+      'type': 'static_library',
+      'defines': [
+        'JSON_USE_EXCEPTION=0',
+      ],
+      'sources': [
+        '../third_party/externals/jsoncpp/include/json/assertions.h',
+        '../third_party/externals/jsoncpp/include/json/autolink.h',
+        '../third_party/externals/jsoncpp/include/json/config.h',
+        '../third_party/externals/jsoncpp/include/json/features.h',
+        '../third_party/externals/jsoncpp/include/json/forwards.h',
+        '../third_party/externals/jsoncpp/include/json/json.h',
+        '../third_party/externals/jsoncpp/include/json/reader.h',
+        '../third_party/externals/jsoncpp-chromium/overrides/include/json/value.h',
+        '../third_party/externals/jsoncpp/include/json/writer.h',
+        '../third_party/externals/jsoncpp/src/lib_json/json_batchallocator.h',
+        '../third_party/externals/jsoncpp/src/lib_json/json_reader.cpp',
+        '../third_party/externals/jsoncpp/src/lib_json/json_tool.h',
+        '../third_party/externals/jsoncpp-chromium/overrides/src/lib_json/json_value.cpp',
+        '../third_party/externals/jsoncpp/src/lib_json/json_writer.cpp',
+      ],
+      'include_dirs': [
+        '../third_party/externals/jsoncpp-chromium/overrides/include/',
+        '../third_party/externals/jsoncpp/include/',
+        '../third_party/externals/jsoncpp/src/lib_json/',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../third_party/externals/jsoncpp-chromium/overrides/include/',
+          '../third_party/externals/jsoncpp/include/',
+        ],
+      },
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'xcode_settings': {
+            'OTHER_CPLUSPLUSFLAGS!': [
+              '-Werror',
+            ]
+          },
+        }],
+        [ 'skia_os == "win"', {
+          'msvs_settings': {
+            'VCCLCompilerTool': {
+              'WarnAsError': 'false',
+            },
+          },
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/libjpeg.gyp b/gyp/libjpeg.gyp
new file mode 100644
index 0000000..5780b27
--- /dev/null
+++ b/gyp/libjpeg.gyp
@@ -0,0 +1,122 @@
+# Copyright 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.
+
+# This is a copy of ../third_party/externals/libjpeg/libjpeg.gyp , modified
+# such that all source paths point into that directory.
+# See http://code.google.com/p/skia/issues/detail?id=543 ('wrap libjpeg.gyp
+# from Chrome's libjpeg port, rather than making our own copy') for a better
+# long-term solution.
+
+{
+  'variables': {
+    'use_system_libjpeg%': 0,
+  },
+  'conditions': [
+    ['use_system_libjpeg==0', {
+      'targets': [
+        {
+          'target_name': 'libjpeg',
+          'type': 'static_library',
+          'sources': [
+            '../third_party/externals/libjpeg/jcapimin.c',
+            '../third_party/externals/libjpeg/jcapistd.c',
+            '../third_party/externals/libjpeg/jccoefct.c',
+            '../third_party/externals/libjpeg/jccolor.c',
+            '../third_party/externals/libjpeg/jcdctmgr.c',
+            '../third_party/externals/libjpeg/jchuff.c',
+            '../third_party/externals/libjpeg/jchuff.h',
+            '../third_party/externals/libjpeg/jcinit.c',
+            '../third_party/externals/libjpeg/jcmainct.c',
+            '../third_party/externals/libjpeg/jcmarker.c',
+            '../third_party/externals/libjpeg/jcmaster.c',
+            '../third_party/externals/libjpeg/jcomapi.c',
+            '../third_party/externals/libjpeg/jconfig.h',
+            '../third_party/externals/libjpeg/jcparam.c',
+            '../third_party/externals/libjpeg/jcphuff.c',
+            '../third_party/externals/libjpeg/jcprepct.c',
+            '../third_party/externals/libjpeg/jcsample.c',
+            '../third_party/externals/libjpeg/jdapimin.c',
+            '../third_party/externals/libjpeg/jdapistd.c',
+            '../third_party/externals/libjpeg/jdatadst.c',
+            '../third_party/externals/libjpeg/jdatasrc.c',
+            '../third_party/externals/libjpeg/jdcoefct.c',
+            '../third_party/externals/libjpeg/jdcolor.c',
+            '../third_party/externals/libjpeg/jdct.h',
+            '../third_party/externals/libjpeg/jddctmgr.c',
+            '../third_party/externals/libjpeg/jdhuff.c',
+            '../third_party/externals/libjpeg/jdhuff.h',
+            '../third_party/externals/libjpeg/jdinput.c',
+            '../third_party/externals/libjpeg/jdmainct.c',
+            '../third_party/externals/libjpeg/jdmarker.c',
+            '../third_party/externals/libjpeg/jdmaster.c',
+            '../third_party/externals/libjpeg/jdmerge.c',
+            '../third_party/externals/libjpeg/jdphuff.c',
+            '../third_party/externals/libjpeg/jdpostct.c',
+            '../third_party/externals/libjpeg/jdsample.c',
+            '../third_party/externals/libjpeg/jerror.c',
+            '../third_party/externals/libjpeg/jerror.h',
+            '../third_party/externals/libjpeg/jfdctflt.c',
+            '../third_party/externals/libjpeg/jfdctfst.c',
+            '../third_party/externals/libjpeg/jfdctint.c',
+            '../third_party/externals/libjpeg/jidctflt.c',
+            '../third_party/externals/libjpeg/jidctfst.c',
+            '../third_party/externals/libjpeg/jidctint.c',
+            '../third_party/externals/libjpeg/jinclude.h',
+            '../third_party/externals/libjpeg/jmemmgr.c',
+            '../third_party/externals/libjpeg/jmemnobs.c',
+            '../third_party/externals/libjpeg/jmemsys.h',
+            '../third_party/externals/libjpeg/jmorecfg.h',
+            '../third_party/externals/libjpeg/jpegint.h',
+            '../third_party/externals/libjpeg/jpeglib.h',
+            '../third_party/externals/libjpeg/jquant1.c',
+            '../third_party/externals/libjpeg/jquant2.c',
+            '../third_party/externals/libjpeg/jutils.c',
+            '../third_party/externals/libjpeg/jversion.h',
+          ],
+          'direct_dependent_settings': {
+            'include_dirs': [
+              '../third_party/externals/libjpeg',
+            ],
+          },
+          'conditions': [
+            ['OS!="win"', {
+              'product_name': 'jpeg',
+              'cflags': [
+               '-Wno-main', # supresses warnings about naming things "main"
+              ],
+            }],
+            ['OS=="android"', {
+              'cflags!': [
+               '-fno-rtti', # supresses warnings about invalid option of non-C++ code
+              ],
+            }],
+          ],
+        },
+      ],
+    }, {
+      'targets': [
+        {
+          'target_name': 'libjpeg',
+          'type': 'none',
+          'direct_dependent_settings': {
+            'defines': [
+              'USE_SYSTEM_LIBJPEG',
+            ],
+          },
+          'link_settings': {
+            'libraries': [
+              '-ljpeg',
+            ],
+          },
+        }
+      ],
+    }],
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/libtess.gyp b/gyp/libtess.gyp
deleted file mode 100644
index d74bcc7..0000000
--- a/gyp/libtess.gyp
+++ /dev/null
@@ -1,59 +0,0 @@
-{
-  'includes': [
-    'common.gypi',
-  ],
-  'targets': [
-    {
-      'target_name': 'libtess',
-      'type': 'static_library',
-      'include_dirs': [
-        '../third_party/glu',
-      ],
-      'sources': [
-        '../third_party/glu/sk_glu.h',
-        '../third_party/glu/gluos.h',
-        '../third_party/glu/libtess/dict-list.h',
-        '../third_party/glu/libtess/dict.c',
-        '../third_party/glu/libtess/dict.h',
-        '../third_party/glu/libtess/geom.c',
-        '../third_party/glu/libtess/geom.h',
-        '../third_party/glu/libtess/memalloc.c',
-        '../third_party/glu/libtess/memalloc.h',
-        '../third_party/glu/libtess/mesh.c',
-        '../third_party/glu/libtess/mesh.h',
-        '../third_party/glu/libtess/normal.c',
-        '../third_party/glu/libtess/normal.h',
-        '../third_party/glu/libtess/priorityq-heap.h',
-        '../third_party/glu/libtess/priorityq-sort.h',
-        '../third_party/glu/libtess/priorityq.c',
-        '../third_party/glu/libtess/priorityq.h',
-        '../third_party/glu/libtess/render.c',
-        '../third_party/glu/libtess/render.h',
-        '../third_party/glu/libtess/sweep.c',
-        '../third_party/glu/libtess/sweep.h',
-        '../third_party/glu/libtess/tess.c',
-        '../third_party/glu/libtess/tess.h',
-        '../third_party/glu/libtess/tessmono.c',
-        '../third_party/glu/libtess/tessmono.h',
-      ],
-      'direct_dependent_settings': {
-        'include_dirs': [
-          '../third_party/glu',
-        ],
-      },
-      'conditions': [
-        [ 'skia_os == "android"', {
-          'cflags!': [
-            '-fno-rtti', # supresses warnings about invalid option of non-C++ code
-          ],
-        }],
-      ],
-    },
-  ],
-}
-
-# Local Variables:
-# tab-width:2
-# indent-tabs-mode:nil
-# End:
-# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/most.gyp b/gyp/most.gyp
new file mode 100644
index 0000000..319547e
--- /dev/null
+++ b/gyp/most.gyp
@@ -0,0 +1,34 @@
+# 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:
+# - debugger: this requires QT to build
+#
+{
+  'targets': [
+    {
+      'target_name': 'most',
+      'type': 'none',
+      'dependencies': [
+        # The minimal set of static libraries for basic Skia functionality.
+        'skia_base_libs.gyp:skia_base_libs',
+
+        'bench.gyp:bench',
+        'gm.gyp:gm',
+        'SampleApp.gyp:SampleApp',
+        'tests.gyp:tests',
+        'tools.gyp:tools',
+      ],
+      'conditions': [
+        ['skia_os == "android"', {
+          'dependencies': [ 'android_system.gyp:SkiaAndroidApp' ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/nacl.gyp b/gyp/nacl.gyp
new file mode 100644
index 0000000..5edb445
--- /dev/null
+++ b/gyp/nacl.gyp
@@ -0,0 +1,19 @@
+# Common entry point for all Skia executables running in NaCl
+{
+  'targets': [
+    {
+      'target_name': 'nacl_interface',
+      'type': 'static_library',
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+      ],
+      'include_dirs': [
+        # For SkThreadUtils.h
+        '../src/utils',
+      ],
+      'sources': [
+        '../../nacl/src/nacl_interface.cpp',
+      ],
+    },
+  ],
+}
diff --git a/gyp/opts.gyp b/gyp/opts.gyp
index 11391a8..30f464f 100644
--- a/gyp/opts.gyp
+++ b/gyp/opts.gyp
@@ -1,7 +1,4 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     # Due to an unfortunate intersection of lameness between gcc and gyp,
     # we have to build the *_SSE2.cpp files in a separate target.  The
@@ -23,7 +20,9 @@
     # separately as well.
     {
       'target_name': 'opts',
+      'product_name': 'skia_opts',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'include_dirs': [
         '../include/config',
         '../include/core',
@@ -31,43 +30,66 @@
         '../src/opts',
       ],
       'conditions': [
-        [ 'skia_target_arch != "arm"', {
+        [ 'skia_arch_type == "x86" and skia_os != "ios"', {
           'conditions': [
-            [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+            [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "nacl"]', {
               'cflags': [
                 '-msse2',
               ],
             }],
+            [ 'skia_os != "android"', {
+              'dependencies': [
+                'opts_ssse3',
+              ],
+            }],
           ],
           'sources': [
             '../src/opts/opts_check_SSE2.cpp',
             '../src/opts/SkBitmapProcState_opts_SSE2.cpp',
             '../src/opts/SkBlitRow_opts_SSE2.cpp',
+            '../src/opts/SkBlitRect_opts_SSE2.cpp',
             '../src/opts/SkUtils_opts_SSE2.cpp',
           ],
-          'dependencies': [
-            'opts_ssse3',
-          ],
         }],
-        [ 'skia_target_arch == "arm" and armv7 == 1', {
+        [ 'skia_arch_type == "arm" and armv7 == 1', {
           # The assembly uses the frame pointer register (r7 in Thumb/r11 in
           # ARM), the compiler doesn't like that.
           'cflags!': [
             '-fno-omit-frame-pointer',
+            '-mapcs-frame',
+            '-mapcs',
           ],
           'cflags': [
             '-fomit-frame-pointer',
+            '-mno-apcs-frame',
           ],
+          'variables': {
+            'arm_neon_optional%': '<(arm_neon_optional>',
+          },
           'sources': [
             '../src/opts/opts_check_arm.cpp',
             '../src/opts/memset.arm.S',
-            '../src/opts/memset16_neon.S',
-            '../src/opts/memset32_neon.S',
             '../src/opts/SkBitmapProcState_opts_arm.cpp',
             '../src/opts/SkBlitRow_opts_arm.cpp',
+            '../src/opts/SkBlitRow_opts_arm.h',
+          ],
+          'conditions': [
+            [ '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',
+              ],
+            }],
           ],
         }],
-        [ 'skia_target_arch == "arm" and armv7 != 1', {
+        [ '(skia_arch_type == "arm" and armv7 == 0) or (skia_os == "ios")', {
           'sources': [
             '../src/opts/SkBitmapProcState_opts_none.cpp',
             '../src/opts/SkBlitRow_opts_none.cpp',
@@ -82,14 +104,16 @@
     # gcc to generate SSSE3 code.
     {
       'target_name': 'opts_ssse3',
+      'product_name': 'skia_opts_ssse3',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'include_dirs': [
         '../include/config',
         '../include/core',
         '../src/core',
       ],
       'conditions': [
-        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "nacl"]', {
           'cflags': [
             '-mssse3',
           ],
@@ -102,13 +126,52 @@
             'OTHER_CFLAGS': ['-mssse3',],
           },
         }],
-        [ 'skia_target_arch != "arm"', {
+        [ 'skia_arch_type == "x86"', {
           'sources': [
             '../src/opts/SkBitmapProcState_opts_SSSE3.cpp',
           ],
         }],
       ],
     },
+    # NEON code must be compiled with -mfpu=neon which also affects scalar
+    # code. To support dynamic NEON code paths, we need to build all
+    # NEON-specific sources in a separate static library. The situation
+    # is very similar to the SSSE3 one.
+    {
+      'target_name': 'opts_neon',
+      'product_name': 'skia_opts_neon',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../src/core',
+        '../src/opts',
+      ],
+      'cflags!': [
+        '-fno-omit-frame-pointer',
+        '-mfpu=vfp',  # remove them all, just in case.
+        '-mfpu=vfpv3',
+        '-mfpu=vfpv3-d16',
+      ],
+      'cflags': [
+        '-mfpu=neon',
+        '-fomit-frame-pointer',
+      ],
+      'ldflags': [
+        '-march=armv7-a',
+        '-Wl,--fix-cortex-a8',
+      ],
+      'sources': [
+        '../src/opts/memset16_neon.S',
+        '../src/opts/memset32_neon.S',
+        '../src/opts/SkBitmapProcState_arm_neon.cpp',
+        '../src/opts/SkBitmapProcState_matrixProcs_neon.cpp',
+        '../src/opts/SkBitmapProcState_matrix_clamp_neon.h',
+        '../src/opts/SkBitmapProcState_matrix_repeat_neon.h',
+        '../src/opts/SkBlitRow_opts_arm_neon.cpp',
+      ],
+    },
   ],
 }
 
diff --git a/gyp/pdf.gyp b/gyp/pdf.gyp
index f0c6b47..7b3b9ea 100644
--- a/gyp/pdf.gyp
+++ b/gyp/pdf.gyp
@@ -1,46 +1,48 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'pdf',
+      'product_name': 'skia_pdf',
       'type': 'static_library',
+      'standalone_static_library': 1,
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'zlib.gyp:zlib',
+      ],
       'include_dirs': [
         '../include/config',
         '../include/core',
         '../include/pdf',
         '../src/core', # needed to get SkGlyphCache.h and SkTextFormatParams.h
+        '../src/utils', # needed to get SkBitSet.h
       ],
       'sources': [
-        '../include/pdf/SkBitSet.h',
-        '../include/pdf/SkPDFCatalog.h',
         '../include/pdf/SkPDFDevice.h',
         '../include/pdf/SkPDFDocument.h',
-        '../include/pdf/SkPDFFont.h',
-        '../include/pdf/SkPDFFormXObject.h',
-        '../include/pdf/SkPDFGraphicState.h',
-        '../include/pdf/SkPDFImage.h',
-        '../include/pdf/SkPDFPage.h',
-        '../include/pdf/SkPDFShader.h',
-        '../include/pdf/SkPDFStream.h',
-        '../include/pdf/SkPDFTypes.h',
-        '../include/pdf/SkPDFUtils.h',
 
-        '../src/pdf/SkBitSet.cpp',
         '../src/pdf/SkPDFCatalog.cpp',
+        '../src/pdf/SkPDFCatalog.h',
         '../src/pdf/SkPDFDevice.cpp',
         '../src/pdf/SkPDFDocument.cpp',
         '../src/pdf/SkPDFFont.cpp',
+        '../src/pdf/SkPDFFont.h',
         '../src/pdf/SkPDFFontImpl.h',
         '../src/pdf/SkPDFFormXObject.cpp',
+        '../src/pdf/SkPDFFormXObject.h',
         '../src/pdf/SkPDFGraphicState.cpp',
+        '../src/pdf/SkPDFGraphicState.h',
         '../src/pdf/SkPDFImage.cpp',
+        '../src/pdf/SkPDFImage.h',
         '../src/pdf/SkPDFPage.cpp',
+        '../src/pdf/SkPDFPage.h',
         '../src/pdf/SkPDFShader.cpp',
+        '../src/pdf/SkPDFShader.h',
         '../src/pdf/SkPDFStream.cpp',
+        '../src/pdf/SkPDFStream.h',
         '../src/pdf/SkPDFTypes.cpp',
+        '../src/pdf/SkPDFTypes.h',
         '../src/pdf/SkPDFUtils.cpp',
+        '../src/pdf/SkPDFUtils.h',
       ],
       # This section makes all targets that depend on this target
       # #define SK_SUPPORT_PDF and have access to the pdf header files.
@@ -52,9 +54,6 @@
           '../include/pdf',
         ],
       },
-      'dependencies': [
-        'zlib.gyp:zlib',
-      ],
     },
   ],
 }
diff --git a/gyp/pixman_test.gyp b/gyp/pixman_test.gyp
new file mode 100644
index 0000000..64c5a9c
--- /dev/null
+++ b/gyp/pixman_test.gyp
@@ -0,0 +1,177 @@
+# GYP file to build unit tests.
+{
+  'includes': [
+    'apptype_console.gypi',
+    'common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'pixman_test',
+      'type': 'executable',
+      'mac_bundle' : 1,
+      'defines': [
+        'HAVE_CONFIG_H',
+      ],
+      'include_dirs' : [
+        '../src/core',
+        '../experimental/pixman',
+        '../experimental/SimpleCocoaApp', # needed to get SimpleApp.h
+      ],
+      'sources': [
+        '../experimental/pixman/config.h',
+        '../experimental/pixman/junk.cpp',
+#        '../../../pixman/demos/alpha-test.c',
+        '../../../pixman/demos/checkerboard.c',
+#        '../../../pixman/demos/clip-in.c',
+#        '../../../pixman/demos/clip-test.c',
+#        '../../../pixman/demos/composite-test.c',
+#        '../../../pixman/demos/convolution-test.c',
+#        '../../../pixman/demos/gradient-test.c',
+#        '../../../pixman/demos/gtk-utils.c',
+#        '../../../pixman/demos/parrot.c',
+#        '../../../pixman/demos/quad2quad.c',
+#        '../../../pixman/demos/radial-test.c',
+#        '../../../pixman/demos/screen-test.c',
+#        '../../../pixman/demos/srgb-test.c',
+#        '../../../pixman/demos/srgb-trap-test.c',
+#        '../../../pixman/demos/trap-test.c',
+#        '../../../pixman/demos/tri-test.c',
+        '../../../pixman/demos/gtk-utils.h',
+#        '../../../pixman/test/a1-trap-test.c',
+#        '../../../pixman/test/affine-test.c',
+#        '../../../pixman/test/alpha-loop.c',
+#        '../../../pixman/test/alphamap.c',
+#        '../../../pixman/test/blitters-test.c',
+#        '../../../pixman/test/combiner-test.c',
+#        '../../../pixman/test/composite-traps-test.c',
+#        '../../../pixman/test/composite.c',
+#        '../../../pixman/test/fetch-test.c',
+#        '../../../pixman/test/glyph-test.c',
+#        '../../../pixman/test/gradient-crash-test.c',
+#        '../../../pixman/test/infinite-loop.c',
+#        '../../../pixman/test/lowlevel-blt-bench.c',
+#        '../../../pixman/test/oob-test.c',
+#        '../../../pixman/test/pdf-op-test.c',
+#        '../../../pixman/test/region-contains-test.c',
+#        '../../../pixman/test/region-test.c',
+#        '../../../pixman/test/region-translate-test.c',
+#        '../../../pixman/test/rotate-test.c',
+#        '../../../pixman/test/scaling-crash-test.c',
+#        '../../../pixman/test/scaling-helpers-test.c',
+#        '../../../pixman/test/scaling-test.c',
+#        '../../../pixman/test/stress-test.c',
+#        '../../../pixman/test/trap-crasher.c',
+        '../../../pixman/test/utils.c',
+        '../../../pixman/test/utils.h',
+        '../../../pixman/pixman/pixman-access-accessors.c',
+        '../../../pixman/pixman/pixman-access.c',
+#        '../../../pixman/pixman/pixman-arm-neon.c',
+#        '../../../pixman/pixman/pixman-arm-simd.c',
+        '../../../pixman/pixman/pixman-arm.c',
+        '../../../pixman/pixman/pixman-bits-image.c',
+        '../../../pixman/pixman/pixman-combine-float.c',
+        '../../../pixman/pixman/pixman-combine32.c',
+        '../../../pixman/pixman/pixman-conical-gradient.c',
+        '../../../pixman/pixman/pixman-edge-accessors.c',
+        '../../../pixman/pixman/pixman-edge.c',
+        '../../../pixman/pixman/pixman-fast-path.c',
+        '../../../pixman/pixman/pixman-general.c',
+        '../../../pixman/pixman/pixman-glyph.c',
+        '../../../pixman/pixman/pixman-gradient-walker.c',
+        '../../../pixman/pixman/pixman-image.c',
+        '../../../pixman/pixman/pixman-implementation.c',
+        '../../../pixman/pixman/pixman-linear-gradient.c',
+        '../../../pixman/pixman/pixman-matrix.c',
+#        '../../../pixman/pixman/pixman-mips-dspr2.c',
+        '../../../pixman/pixman/pixman-mips.c',
+        '../../../pixman/pixman/pixman-mmx.c',
+        '../../../pixman/pixman/pixman-noop.c',
+        '../../../pixman/pixman/pixman-ppc.c',
+        '../../../pixman/pixman/pixman-radial-gradient.c',
+#        '../../../pixman/pixman/pixman-region.c',
+        '../../../pixman/pixman/pixman-region16.c',
+        '../../../pixman/pixman/pixman-region32.c',
+        '../../../pixman/pixman/pixman-solid-fill.c',
+        '../../../pixman/pixman/pixman-sse2.c',
+        '../../../pixman/pixman/pixman-timer.c',
+        '../../../pixman/pixman/pixman-trap.c',
+        '../../../pixman/pixman/pixman-utils.c',
+#        '../../../pixman/pixman/pixman-vmx.c',
+        '../../../pixman/pixman/pixman-x86.c',
+        '../../../pixman/pixman/pixman.c',
+#        '../../../pixman/pixman/pixman-arm-neon-asm-bilinear.S',
+#        '../../../pixman/pixman/pixman-arm-neon-asm.S',
+#        '../../../pixman/pixman/pixman-arm-simd-asm.S',
+#        '../../../pixman/pixman/pixman-mips-dspr2-asm.S',
+#        '../../../pixman/pixman/pixman-mips-memcpy-asm.S',
+        '../../../pixman/pixman/loongson-mmintrin.h',
+        '../../../pixman/pixman/pixman-accessor.h',
+        '../../../pixman/pixman/pixman-arm-common.h',
+        '../../../pixman/pixman/pixman-arm-neon-asm.h',
+        '../../../pixman/pixman/pixman-combine32.h',
+        '../../../pixman/pixman/pixman-compiler.h',
+        '../../../pixman/pixman/pixman-edge-imp.h',
+        '../../../pixman/pixman/pixman-inlines.h',
+        '../../../pixman/pixman/pixman-mips-dspr2-asm.h',
+        '../../../pixman/pixman/pixman-mips-dspr2.h',
+        '../../../pixman/pixman/pixman-private.h',
+        '../../../pixman/pixman/pixman.h',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'experimental.gyp:experimental',
+        'images.gyp:images',
+        'pdf.gyp:pdf',
+        'views.gyp:views',
+        'xml.gyp:xml',
+      ],
+      'conditions': [
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+        }],
+        [ 'skia_os == "win"', {
+        }],
+        [ 'skia_os == "mac"', {
+          'sources': [
+
+            # Mac files
+            '../src/views/mac/SkEventNotifier.h',
+            '../src/views/mac/SkEventNotifier.mm',
+            '../src/views/mac/skia_mac.mm',
+            '../src/views/mac/SkNSView.h',
+            '../src/views/mac/SkNSView.mm',
+            '../src/views/mac/SkOptionsTableView.h',
+            '../src/views/mac/SkOptionsTableView.mm',
+            '../src/views/mac/SkOSWindow_Mac.mm',
+            '../src/views/mac/SkTextFieldCell.h',
+            '../src/views/mac/SkTextFieldCell.m',
+          ],
+          'libraries': [
+            '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
+            '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
+          ],
+          'xcode_settings' : {
+            'INFOPLIST_FILE' : '../experimental/Intersection/EdgeDemoApp-Info.plist',
+          },
+          'mac_bundle_resources' : [
+            '../experimental/Intersection/EdgeDemoApp.xib',
+          ],
+        }],
+      ],
+      'msvs_settings': {
+        'VCLinkerTool': {
+          'SubSystem': '2',
+          'AdditionalDependencies': [
+            'd3d9.lib',
+          ],
+        },
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/ports.gyp b/gyp/ports.gyp
index 5dc88d5..219bf3e 100644
--- a/gyp/ports.gyp
+++ b/gyp/ports.gyp
@@ -1,26 +1,31 @@
 # Port-specific Skia library code.
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'ports',
+      'product_name': 'skia_ports',
       'type': 'static_library',
+      'standalone_static_library': 1,
+      'dependencies': [
+        'core.gyp:core',
+        'sfnt.gyp:sfnt',
+        'utils.gyp:utils',
+      ],
       'include_dirs': [
-        '../include/config',
-        '../include/core',
         '../include/images',
         '../include/effects',
         '../include/ports',
         '../include/xml',
         '../src/core',
+        '../src/utils',
       ],
       'sources': [
+        '../src/ports/SkDebug_nacl.cpp',
         '../src/ports/SkDebug_stdio.cpp',
         '../src/ports/SkDebug_win.cpp',
         '../src/ports/SkFontHost_sandbox_none.cpp',
         '../src/ports/SkFontHost_win.cpp',
+        '../src/ports/SkFontHost_win_dw.cpp',
         '../src/ports/SkGlobalInitialization_default.cpp',
         '../src/ports/SkThread_win.cpp',
 
@@ -30,15 +35,53 @@
         '../src/ports/SkTime_Unix.cpp',
         '../src/ports/SkTime_win.cpp',
         '../src/ports/SkXMLParser_empty.cpp',
-        '../src/ports/sk_predefined_gamma.h',
       ],
       'conditions': [
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+          'defines': [
+            #The font host requires at least FreeType 2.3.0 at runtime.
+            'SK_FONTHOST_FREETYPE_RUNTIME_VERSION=0x020300',\
+            'SK_CAN_USE_DLOPEN=1',
+          ],
+          'link_settings': {
+            'libraries': [
+              '-lfreetype',
+              '-ldl',
+            ],
+          },
           'sources': [
-            '../src/ports/SkThread_pthread.cpp',
             '../src/ports/SkFontHost_FreeType.cpp',
-            '../src/ports/SkFontHost_gamma_none.cpp',
+            '../src/ports/SkFontHost_FreeType_common.cpp',
             '../src/ports/SkFontHost_linux.cpp',
+            '../src/ports/SkThread_pthread.cpp',
+          ],
+        }],
+        [ 'skia_os == "nacl"', {
+          'dependencies': [
+            # On other OS, we can dynamically link against freetype.  For nacl,
+            # we have to include our own version since the naclports version is
+            # too old (<0x020300) to provide the functionality we need.
+            'freetype.gyp:freetype',
+          ],
+          'export_dependent_settings': [
+            'freetype.gyp:freetype',
+          ],
+          'defines': [
+            # We use Android's repo, which provides at least FreeType 2.4.0
+            'SK_FONTHOST_FREETYPE_RUNTIME_VERSION=0x020400',\
+          ],
+          'sources': [
+            '../src/ports/SkFontHost_FreeType.cpp',
+            '../src/ports/SkFontHost_FreeType_common.cpp',
+            '../src/ports/SkFontHost_linux.cpp',
+            '../src/ports/SkThread_pthread.cpp',
+          ],
+          'sources!': [
+            '../src/ports/SkDebug_stdio.cpp',
+          ],
+        }, {
+          'sources!': [
+            '../src/ports/SkDebug_nacl.cpp',
           ],
         }],
         [ 'skia_os == "mac"', {
@@ -50,8 +93,8 @@
             '../src/ports/SkFontHost_mac_coretext.cpp',
             '../src/utils/mac/SkStream_mac.cpp',
 #            '../src/ports/SkFontHost_FreeType.cpp',
+#            '../src/ports/SkFontHost_FreeType_common.cpp',
 #            '../src/ports/SkFontHost_freetype_mac.cpp',
-#            '../src/ports/SkFontHost_gamma_none.cpp',
             '../src/ports/SkThread_pthread.cpp',
           ],
           'sources!': [
@@ -61,15 +104,32 @@
         [ 'skia_os == "ios"', {
           'include_dirs': [
             '../include/utils/ios',
+            '../include/utils/mac',
           ],
           'sources': [
             '../src/ports/SkFontHost_mac_coretext.cpp',
+            '../src/utils/mac/SkStream_mac.cpp',
             '../src/ports/SkThread_pthread.cpp',
           ],
+          'sources!': [
+            '../src/ports/SkFontHost_tables.cpp',
+          ],
         }],
         [ 'skia_os == "win"', {
           'include_dirs': [
             'config/win',
+            '../src/utils/win',
+          ],
+          'conditions': [
+            [ 'skia_directwrite', {
+                'sources!': [
+                  '../src/ports/SkFontHost_win.cpp',
+                ],
+              }, { # else !skia_directwrite
+                'sources!': [
+                  '../src/ports/SkFontHost_win_dw.cpp',
+                ],
+              }],
           ],
           'sources!': [ # these are used everywhere but windows
             '../src/ports/SkDebug_stdio.cpp',
@@ -79,11 +139,18 @@
           'sources!': [
             '../src/ports/SkDebug_win.cpp',
             '../src/ports/SkFontHost_win.cpp',
+            '../src/ports/SkFontHost_win_dw.cpp',
             '../src/ports/SkThread_win.cpp',
             '../src/ports/SkTime_win.cpp',
           ],
         }],
         [ 'skia_os == "android"', {
+          'defines': [
+            #Android provides at least FreeType 2.4.0 at runtime.
+            'SK_FONTHOST_FREETYPE_RUNTIME_VERSION=0x020400',
+            #Skia should not use dlopen on Android.
+            'SK_CAN_USE_DLOPEN=0',
+          ],
           'sources!': [
             '../src/ports/SkDebug_stdio.cpp',
           ],
@@ -91,16 +158,15 @@
             '../src/ports/SkDebug_android.cpp',
             '../src/ports/SkThread_pthread.cpp',
             '../src/ports/SkFontHost_android.cpp',
-            '../src/ports/SkFontHost_gamma.cpp',
             '../src/ports/SkFontHost_FreeType.cpp',
+            '../src/ports/SkFontHost_FreeType_common.cpp',
             '../src/ports/FontHostConfiguration_android.cpp',
-            #TODO: include the ports/SkImageRef_ashmem.cpp for non-NDK builds
           ],
           'dependencies': [
-             'android_system.gyp:ft2',
-             'android_system.gyp:expat',
+             'freetype.gyp:freetype',
+             'android_deps.gyp:expat',
           ],
-        }],        
+        }],
       ],
       'direct_dependent_settings': {
         'include_dirs': [
diff --git a/gyp/sfnt.gyp b/gyp/sfnt.gyp
index 2f2fc76..66089e3 100644
--- a/gyp/sfnt.gyp
+++ b/gyp/sfnt.gyp
@@ -1,11 +1,10 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'sfnt',
+      'product_name': 'skia_sfnt',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'dependencies': [
         'core.gyp:core',
       ],
@@ -15,8 +14,13 @@
       'sources': [
         '../src/sfnt/SkIBMFamilyClass.h',
         '../src/sfnt/SkOTTableTypes.h',
+        '../src/sfnt/SkOTTable_glyf.h',
         '../src/sfnt/SkOTTable_head.h',
         '../src/sfnt/SkOTTable_hhea.h',
+        '../src/sfnt/SkOTTable_loca.h',
+        '../src/sfnt/SkOTTable_maxp.h',
+        '../src/sfnt/SkOTTable_maxp_CFF.h',
+        '../src/sfnt/SkOTTable_maxp_TT.h',
         '../src/sfnt/SkOTTable_name.h',
         '../src/sfnt/SkOTTable_OS_2.h',
         '../src/sfnt/SkOTTable_OS_2_V0.h',
diff --git a/gyp/shapeops_demo.gyp b/gyp/shapeops_demo.gyp
new file mode 100644
index 0000000..6a06f86
--- /dev/null
+++ b/gyp/shapeops_demo.gyp
@@ -0,0 +1,124 @@
+
+{
+  'includes': [
+    'common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'shapeops_demo',
+      'type': 'executable',
+      'mac_bundle' : 1,
+      'include_dirs' : [
+        '../experimental/SimpleCocoaApp', # needed to get SimpleApp.h
+      ],
+      'sources': [
+        '../experimental/Intersection/ConvexHull.cpp',
+        '../experimental/Intersection/CubeRoot.cpp',
+        '../experimental/Intersection/CubicBezierClip.cpp',
+        '../experimental/Intersection/CubicBounds.cpp',
+        '../experimental/Intersection/CubicIntersection.cpp',
+        '../experimental/Intersection/CubicReduceOrder.cpp',
+        '../experimental/Intersection/CubicSubDivide.cpp',
+        '../experimental/Intersection/CubicToQuadratics.cpp',
+        '../experimental/Intersection/CubicUtilities.cpp',
+        '../experimental/Intersection/DataTypes.cpp',
+        '../experimental/Intersection/EdgeDemo.cpp',
+        '../experimental/Intersection/EdgeDemoApp.mm',
+        '../experimental/Intersection/EdgeWalker.cpp',
+        '../experimental/Intersection/EdgeWalker_TestUtility.cpp',
+        '../experimental/Intersection/Extrema.cpp',
+        '../experimental/Intersection/Intersections.cpp',
+        '../experimental/Intersection/LineCubicIntersection.cpp',
+        '../experimental/Intersection/LineIntersection.cpp',
+        '../experimental/Intersection/LineParameterization.cpp',
+        '../experimental/Intersection/LineQuadraticIntersection.cpp',
+        '../experimental/Intersection/LineUtilities.cpp',
+        '../experimental/Intersection/QuadraticBezierClip.cpp',
+        '../experimental/Intersection/QuadraticBounds.cpp',
+        '../experimental/Intersection/QuadraticIntersection.cpp',
+        '../experimental/Intersection/QuadraticImplicit.cpp',
+        '../experimental/Intersection/QuadraticLineSegments.cpp',
+        '../experimental/Intersection/QuadraticParameterization.cpp',
+        '../experimental/Intersection/QuadraticReduceOrder.cpp',
+        '../experimental/Intersection/QuadraticSubDivide.cpp',
+        '../experimental/Intersection/QuadraticUtilities.cpp',
+        '../experimental/Intersection/QuarticRoot.cpp',
+        '../experimental/Intersection/ShapeOps.cpp',
+        '../experimental/Intersection/Simplify.cpp',
+        '../experimental/Intersection/CubicParameterization.cpp',
+        '../experimental/Intersection/CubicReduceOrder.cpp',
+        '../experimental/Intersection/CubicSubDivide.cpp',
+        '../experimental/Intersection/CubicUtilities.h',
+        '../experimental/Intersection/CurveIntersection.h',
+        '../experimental/Intersection/DataTypes.h',
+        '../experimental/Intersection/EdgeDemo.h',
+        '../experimental/Intersection/Extrema.h',
+        '../experimental/Intersection/Intersections.h',
+        '../experimental/Intersection/IntersectionUtilities.h',
+        '../experimental/Intersection/LineIntersection.h',
+        '../experimental/Intersection/LineParameters.h',
+        '../experimental/Intersection/LineUtilities.h',
+        '../experimental/Intersection/QuadraticLineSegments.h',
+        '../experimental/Intersection/QuadraticParameterization.h',
+        '../experimental/Intersection/QuadraticUtilities.h',
+        '../experimental/Intersection/QuarticRoot.h',
+        '../experimental/Intersection/ShapeOps.h',
+        '../experimental/Intersection/Simplify.h',
+        '../experimental/Intersection/TSearch.h',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        'views.gyp:views',
+        'xml.gyp:xml',
+      ],
+      'conditions' : [
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
+        }],
+        [ 'skia_os == "win"', {
+        }],
+        [ 'skia_os == "mac"', {
+          'sources': [
+
+            # Mac files
+            '../src/views/mac/SkEventNotifier.h',
+            '../src/views/mac/SkEventNotifier.mm',
+            '../src/views/mac/skia_mac.mm',
+            '../src/views/mac/SkNSView.h',
+            '../src/views/mac/SkNSView.mm',
+            '../src/views/mac/SkOptionsTableView.h',
+            '../src/views/mac/SkOptionsTableView.mm',
+            '../src/views/mac/SkOSWindow_Mac.mm',
+            '../src/views/mac/SkTextFieldCell.h',
+            '../src/views/mac/SkTextFieldCell.m',
+          ],
+          'libraries': [
+            '$(SDKROOT)/System/Library/Frameworks/QuartzCore.framework',
+            '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework',
+          ],
+          'xcode_settings' : {
+            'INFOPLIST_FILE' : '../experimental/Intersection/EdgeDemoApp-Info.plist',
+          },
+          'mac_bundle_resources' : [
+            '../experimental/Intersection/EdgeDemoApp.xib',
+          ],
+        }],
+      ],
+      'msvs_settings': {
+        'VCLinkerTool': {
+          'SubSystem': '2',
+          'AdditionalDependencies': [
+            'd3d9.lib',
+          ],
+        },
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/shapeops_edge.gyp b/gyp/shapeops_edge.gyp
new file mode 100644
index 0000000..34d60f4
--- /dev/null
+++ b/gyp/shapeops_edge.gyp
@@ -0,0 +1,134 @@
+# GYP file to build unit tests.
+{
+  'includes': [
+    'apptype_console.gypi',
+    'common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'edge',
+      'type': 'executable',
+      'include_dirs' : [
+        '../src/core',
+      ],
+      'sources': [
+        '../experimental/Intersection/ActiveEdge_Test.cpp',
+        '../experimental/Intersection/ConvexHull.cpp',
+        '../experimental/Intersection/ConvexHull_Test.cpp',
+        '../experimental/Intersection/CubeRoot.cpp',
+        '../experimental/Intersection/CubicBezierClip.cpp',
+        '../experimental/Intersection/CubicBezierClip_Test.cpp',
+        '../experimental/Intersection/CubicBounds.cpp',
+        '../experimental/Intersection/CubicIntersection.cpp',
+        '../experimental/Intersection/CubicIntersection_Test.cpp',
+        '../experimental/Intersection/CubicIntersection_TestData.cpp',
+        '../experimental/Intersection/CubicLineSegments.cpp',
+        '../experimental/Intersection/CubicParameterization.cpp',
+        '../experimental/Intersection/CubicParameterization_Test.cpp',
+        '../experimental/Intersection/CubicParameterizationCode.cpp',
+        '../experimental/Intersection/CubicReduceOrder.cpp',
+        '../experimental/Intersection/CubicReduceOrder_Test.cpp',
+        '../experimental/Intersection/CubicSubDivide.cpp',
+        '../experimental/Intersection/CubicToQuadratics.cpp',
+        '../experimental/Intersection/CubicToQuadratics_Test.cpp',
+        '../experimental/Intersection/CubicUtilities.cpp',
+        '../experimental/Intersection/DataTypes.cpp',
+        '../experimental/Intersection/EdgeMain.cpp',
+        '../experimental/Intersection/EdgeWalker.cpp',
+        '../experimental/Intersection/EdgeWalker_TestUtility.cpp',
+        '../experimental/Intersection/EdgeWalkerPolygon4x4_Test.cpp',
+        '../experimental/Intersection/EdgeWalkerPolygons_Mismatches.cpp',
+        '../experimental/Intersection/EdgeWalkerPolygons_Test.cpp',
+        '../experimental/Intersection/EdgeWalkerQuadralaterals_Test.cpp',
+        '../experimental/Intersection/EdgeWalkerQuadratic4x4_Test.cpp',
+        '../experimental/Intersection/EdgeWalkerQuadratics_Test.cpp',
+        '../experimental/Intersection/EdgeWalkerRectangles_Test.cpp',
+        '../experimental/Intersection/Extrema.cpp',
+        '../experimental/Intersection/Inline_Tests.cpp',
+        '../experimental/Intersection/Intersection_Tests.cpp',
+        '../experimental/Intersection/Intersections.cpp',
+        '../experimental/Intersection/IntersectionUtilities.cpp',
+        '../experimental/Intersection/LineCubicIntersection.cpp',
+        '../experimental/Intersection/LineCubicIntersection_Test.cpp',
+        '../experimental/Intersection/LineIntersection.cpp',
+        '../experimental/Intersection/LineIntersection_Test.cpp',
+        '../experimental/Intersection/LineParameterization.cpp',
+        '../experimental/Intersection/LineParameteters_Test.cpp',
+        '../experimental/Intersection/LineQuadraticIntersection.cpp',
+        '../experimental/Intersection/LineQuadraticIntersection_Test.cpp',
+        '../experimental/Intersection/LineUtilities.cpp',
+        '../experimental/Intersection/MiniSimplify_Test.cpp',
+        '../experimental/Intersection/QuadraticBezierClip.cpp',
+        '../experimental/Intersection/QuadraticBezierClip_Test.cpp',
+        '../experimental/Intersection/QuadraticBounds.cpp',
+        '../experimental/Intersection/QuadraticImplicit.cpp',
+        '../experimental/Intersection/QuadraticIntersection.cpp',
+        '../experimental/Intersection/QuadraticIntersection_Test.cpp',
+        '../experimental/Intersection/QuadraticIntersection_TestData.cpp',
+        '../experimental/Intersection/QuadraticLineSegments.cpp',
+        '../experimental/Intersection/QuadraticParameterization.cpp',
+        '../experimental/Intersection/QuadraticParameterization_Test.cpp',
+        '../experimental/Intersection/QuadraticReduceOrder.cpp',
+        '../experimental/Intersection/QuadraticReduceOrder_Test.cpp',
+        '../experimental/Intersection/QuadraticSubDivide.cpp',
+        '../experimental/Intersection/QuadraticUtilities.cpp',
+        '../experimental/Intersection/QuarticRoot.cpp',
+        '../experimental/Intersection/QuarticRoot_Test.cpp',
+        '../experimental/Intersection/ShapeOps.cpp',
+        '../experimental/Intersection/ShapeOpCubic4x4_Test.cpp',
+        '../experimental/Intersection/ShapeOpRect4x4_Test.cpp',
+        '../experimental/Intersection/Simplify.cpp',
+        '../experimental/Intersection/SimplifyAddIntersectingTs_Test.cpp',
+        '../experimental/Intersection/SimplifyAngle_Test.cpp',
+        '../experimental/Intersection/SimplifyFindNext_Test.cpp',
+        '../experimental/Intersection/SimplifyFindTop_Test.cpp',
+        '../experimental/Intersection/SimplifyNew_Test.cpp',
+        '../experimental/Intersection/SimplifyRect4x4_Test.cpp',
+        '../experimental/Intersection/TestUtilities.cpp',
+        '../experimental/Intersection/CubicIntersection_TestData.h',
+        '../experimental/Intersection/CubicLineSegments.h',
+        '../experimental/Intersection/CubicUtilities.h',
+        '../experimental/Intersection/CurveIntersection.h',
+        '../experimental/Intersection/CurveUtilities.h',
+        '../experimental/Intersection/DataTypes.h',
+        '../experimental/Intersection/EdgeWalker_Test.h',
+        '../experimental/Intersection/Extrema.h',
+        '../experimental/Intersection/Intersection_Tests.h',
+        '../experimental/Intersection/Intersections.h',
+        '../experimental/Intersection/IntersectionUtilities.h',
+        '../experimental/Intersection/LineIntersection.h',
+        '../experimental/Intersection/LineParameters.h',
+        '../experimental/Intersection/LineUtilities.h',
+        '../experimental/Intersection/Parameterization_Test.h',
+        '../experimental/Intersection/QuadraticIntersection_TestData.h',
+        '../experimental/Intersection/QuadraticLineSegments.h',
+        '../experimental/Intersection/QuadraticParameterization.h',
+        '../experimental/Intersection/QuadraticUtilities.h',
+        '../experimental/Intersection/QuarticRoot.h',
+        '../experimental/Intersection/ShapeOps.h',
+        '../experimental/Intersection/Simplify.h',
+        '../experimental/Intersection/TestUtilities.h',
+        '../experimental/Intersection/TSearch.h',
+        '../experimental/Intersection/thingsToDo.txt',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+      ],
+      'conditions': [
+        [ 'skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu',
+          ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/shapeops_tool.gyp b/gyp/shapeops_tool.gyp
new file mode 100644
index 0000000..3b1408a
--- /dev/null
+++ b/gyp/shapeops_tool.gyp
@@ -0,0 +1,45 @@
+# GYP file to build unit tests.
+{
+  'includes': [
+    'apptype_console.gypi',
+    'common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'addTest',
+      'type': 'executable',
+      'include_dirs' : [
+        '../src/core',
+      ],
+      'sources': [
+        '../experimental/Intersection/AddTestOutput/main.cpp',
+      ],
+      'dependencies': [
+        'core.gyp:core',
+        'effects.gyp:effects',
+        'experimental.gyp:experimental',
+        'images.gyp:images',
+        'ports.gyp:ports',
+        'pdf.gyp:pdf',
+        'utils.gyp:utils',
+      ],
+      'conditions': [
+        [ 'skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu',
+          ],
+          'dependencies': [
+            'gpu.gyp:gr',
+            'gpu.gyp:skgr',
+          ],
+        }],
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/skia_base_libs.gyp b/gyp/skia_base_libs.gyp
new file mode 100644
index 0000000..245babb
--- /dev/null
+++ b/gyp/skia_base_libs.gyp
@@ -0,0 +1,47 @@
+# The minimal set of static libraries for basic Skia functionality.
+{
+  'variables': {
+    'component_libs': [
+      'core.gyp:core',
+      'opts.gyp:opts',
+      'ports.gyp:ports',
+      'utils.gyp:utils',
+    ],
+    'conditions': [
+      [ 'skia_arch_type == "x86" and skia_os != "android"', {
+        'component_libs': [
+          'opts.gyp:opts_ssse3',
+        ],
+      }],
+      [ 'arm_neon == 1', {
+        'component_libs': [
+          'opts.gyp:opts_neon',
+        ],
+      }],
+      [ 'skia_gpu', {
+        'component_libs': [
+          'gpu.gyp:gr',
+          'gpu.gyp:skgr',
+        ],
+      }],
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'skia_base_libs',
+      'type': 'none',
+      'dependencies': [
+        '<@(component_libs)',
+      ],
+      'export_dependent_settings': [
+        '<@(component_libs)',
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/svg.gyp b/gyp/svg.gyp
index c72b99f..3952fd7 100644
--- a/gyp/svg.gyp
+++ b/gyp/svg.gyp
@@ -1,11 +1,10 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'svg',
+      'product_name': 'skia_svg',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'include_dirs': [
         '../include/config',
         '../include/core',
diff --git a/gyp/tests.gyp b/gyp/tests.gyp
index cf793ab..10e99d2 100644
--- a/gyp/tests.gyp
+++ b/gyp/tests.gyp
@@ -2,7 +2,6 @@
 {
   'includes': [
     'apptype_console.gypi',
-    'common.gypi',
   ],
   'targets': [
     {
@@ -10,17 +9,28 @@
       'type': 'executable',
       'include_dirs' : [
         '../src/core',
-        '../src/gpu',
+        '../src/effects',
+        '../src/pdf',
+        '../src/pipe/utils',
+        '../src/utils',
+        '../tools/',
       ],
       'sources': [
         '../tests/AAClipTest.cpp',
+        '../tests/AnnotationTest.cpp',
+        '../tests/AtomicTest.cpp',
         '../tests/BitmapCopyTest.cpp',
+        '../tests/BitmapFactoryTest.cpp',
         '../tests/BitmapGetColorTest.cpp',
+        '../tests/BitmapHeapTest.cpp',
+        '../tests/BitmapTransformerTest.cpp',
         '../tests/BitSetTest.cpp',
         '../tests/BlitRowTest.cpp',
         '../tests/BlurTest.cpp',
         '../tests/CanvasTest.cpp',
+        '../tests/ChecksumTest.cpp',
         '../tests/ClampRangeTest.cpp',
+        '../tests/ClipCacheTest.cpp',
         '../tests/ClipCubicTest.cpp',
         '../tests/ClipStackTest.cpp',
         '../tests/ClipperTest.cpp',
@@ -30,15 +40,25 @@
         '../tests/DeferredCanvasTest.cpp',
         '../tests/DequeTest.cpp',
         '../tests/DrawBitmapRectTest.cpp',
+        '../tests/DrawPathTest.cpp',
         '../tests/DrawTextTest.cpp',
         '../tests/EmptyPathTest.cpp',
         '../tests/FillPathTest.cpp',
+        '../tests/FlatDataTest.cpp',
         '../tests/FlateTest.cpp',
+        '../tests/FontHostStreamTest.cpp',
         '../tests/FontHostTest.cpp',
         '../tests/GeometryTest.cpp',
         '../tests/GLInterfaceValidation.cpp',
         '../tests/GLProgramsTest.cpp',
+        '../tests/GpuBitmapCopyTest.cpp',
+        '../tests/GrContextFactoryTest.cpp',
+        '../tests/GradientTest.cpp',
+        '../tests/GrMemoryPoolTest.cpp',
+        '../tests/HashCacheTest.cpp',
         '../tests/InfRectTest.cpp',
+        '../tests/LListTest.cpp',
+        '../tests/MD5Test.cpp',
         '../tests/MathTest.cpp',
         '../tests/MatrixTest.cpp',
         '../tests/Matrix44Test.cpp',
@@ -51,13 +71,21 @@
         '../tests/PathMeasureTest.cpp',
         '../tests/PathTest.cpp',
         '../tests/PDFPrimitivesTest.cpp',
+        '../tests/PictureTest.cpp',
+        '../tests/PictureUtilsTest.cpp',
+        '../tests/PipeTest.cpp',
         '../tests/PointTest.cpp',
         '../tests/PremulAlphaRoundTripTest.cpp',
         '../tests/QuickRejectTest.cpp',
         '../tests/Reader32Test.cpp',
         '../tests/ReadPixelsTest.cpp',
+        '../tests/ReadWriteAlphaTest.cpp',
+        '../tests/RefCntTest.cpp',
         '../tests/RefDictTest.cpp',
         '../tests/RegionTest.cpp',
+        '../tests/RoundRectTest.cpp',
+        '../tests/RTreeTest.cpp',
+        '../tests/SHA1Test.cpp',
         '../tests/ScalarTest.cpp',
         '../tests/ShaderOpacityTest.cpp',
         '../tests/Sk64Test.cpp',
@@ -66,9 +94,12 @@
         '../tests/SrcOverTest.cpp',
         '../tests/StreamTest.cpp',
         '../tests/StringTest.cpp',
+        '../tests/StrokeTest.cpp',
         '../tests/Test.cpp',
         '../tests/Test.h',
         '../tests/TestSize.cpp',
+        '../tests/TileGridTest.cpp',
+        '../tests/TLSTest.cpp',
         '../tests/ToUnicode.cpp',
         '../tests/UnicodeTest.cpp',
         '../tests/UtilsTest.cpp',
@@ -76,18 +107,26 @@
         '../tests/WritePixelsTest.cpp',
         '../tests/Writer32Test.cpp',
         '../tests/XfermodeTest.cpp',
+
+        # Needed for PipeTest.
+        '../src/pipe/utils/SamplePipeControllers.cpp',
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
         'effects.gyp:effects',
         'experimental.gyp:experimental',
-        'gpu.gyp:gr',
-        'gpu.gyp:skgr',
         'images.gyp:images',
-        'ports.gyp:ports',
         'pdf.gyp:pdf',
+        'tools.gyp:picture_utils',
         'utils.gyp:utils',
       ],
+      'conditions': [
+        [ 'skia_gpu == 1', {
+          'include_dirs': [
+            '../src/gpu',
+          ],
+        }],
+      ],
     },
   ],
 }
diff --git a/gyp/tools.gyp b/gyp/tools.gyp
index 1e419cd..8148358 100644
--- a/gyp/tools.gyp
+++ b/gyp/tools.gyp
@@ -8,7 +8,6 @@
 {
   'includes': [
     'apptype_console.gypi',
-    'common.gypi',
   ],
   'targets': [
     {
@@ -16,6 +15,11 @@
       'target_name': 'tools',
       'type': 'none',
       'dependencies': [
+        'bench_pictures',
+        'filter',
+        'pinspect',
+        'render_pdfs',
+        'render_pictures',
         'skdiff',
         'skhello',
         'skimage',
@@ -25,42 +29,192 @@
       'target_name': 'skdiff',
       'type': 'executable',
       'sources': [
-        '../src/effects/SkEffects_none.cpp',
+        '../tools/skdiff.cpp',
+        '../tools/skdiff.h',
+        '../tools/skdiff_html.cpp',
+        '../tools/skdiff_html.h',
         '../tools/skdiff_main.cpp',
+        '../tools/skdiff_utils.cpp',
+        '../tools/skdiff_utils.h',
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
         'images.gyp:images',
-        'ports.gyp:ports',
-        'utils.gyp:utils',
+      ],
+    },
+    {
+      'target_name': 'skimagediff',
+      'type': 'executable',
+      'sources': [
+        '../tools/skdiff.cpp',
+        '../tools/skdiff.h',
+        '../tools/skdiff_html.cpp',
+        '../tools/skdiff_html.h',
+        '../tools/skdiff_image.cpp',
+        '../tools/skdiff_utils.cpp',
+        '../tools/skdiff_utils.h',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
       ],
     },
     {
       'target_name': 'skhello',
       'type': 'executable',
       'sources': [
-        '../src/effects/SkEffects_none.cpp',
         '../tools/skhello.cpp',
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
         'images.gyp:images',
-        'ports.gyp:ports',
-        'utils.gyp:utils',
       ],
     },
     {
       'target_name': 'skimage',
       'type': 'executable',
       'sources': [
-        '../src/effects/SkEffects_none.cpp',
         '../tools/skimage_main.cpp',
       ],
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
         'images.gyp:images',
+      ],
+    },
+    {
+      'target_name': 'render_pictures',
+      'type': 'executable',
+      'sources': [
+        '../tools/render_pictures_main.cpp',
+      ],
+      'include_dirs': [
+        '../src/pipe/utils/',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'tools.gyp:picture_renderer',
+        'tools.gyp:picture_utils',
+      ],
+    },
+    {
+      'target_name': 'bench_pictures',
+      'type': 'executable',
+      'sources': [
+        '../bench/SkBenchLogger.h',
+        '../bench/SkBenchLogger.cpp',
+        '../bench/TimerData.h',
+        '../bench/TimerData.cpp',
+        '../tools/bench_pictures_main.cpp',
+        '../tools/PictureBenchmark.cpp',
+      ],
+      'include_dirs': [
+        '../bench',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'tools.gyp:picture_utils',
+        'tools.gyp:picture_renderer',
+        'bench.gyp:bench_timer',
+      ],
+    },
+    {
+      'target_name': 'picture_renderer',
+      'type': 'static_library',
+      'sources': [
+        '../tools/PictureRenderer.h',
+        '../tools/PictureRenderer.cpp',
+        '../tools/CopyTilesRenderer.h',
+        '../tools/CopyTilesRenderer.cpp',
+        '../src/pipe/utils/SamplePipeControllers.h',
+        '../src/pipe/utils/SamplePipeControllers.cpp',
+      ],
+      'include_dirs': [
+        '../src/core/',
+        '../src/pipe/utils/',
+        '../src/utils/',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        'tools.gyp:picture_utils',
+      ],
+      'export_dependent_settings': [
+        'images.gyp:images',
+      ],
+    },
+    {
+      'target_name': 'render_pdfs',
+      'type': 'executable',
+      'sources': [
+        '../tools/render_pdfs_main.cpp',
+        '../tools/PdfRenderer.cpp',
+        '../tools/PdfRenderer.h',
+      ],
+      'include_dirs': [
+        '../src/pipe/utils/',
+        '../src/utils/',
+      ],
+      'dependencies': [
+        'core.gyp:core',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        'pdf.gyp:pdf',
         'ports.gyp:ports',
-        'utils.gyp:utils',
+        'tools.gyp:picture_utils',
+      ],
+    },
+    {
+      'target_name': 'picture_utils',
+      'type': 'static_library',
+      'sources': [
+        '../tools/picture_utils.cpp',
+        '../tools/picture_utils.h',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+      ],
+    },
+    {
+      'target_name': 'pinspect',
+      'type': 'executable',
+      'sources': [
+        '../tools/pinspect.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+      ],
+    },
+    {
+      'target_name': 'filter',
+      'type': 'executable',
+      'include_dirs' : [
+        '../src/core',
+        '../debugger',
+      ],
+      'sources': [
+        '../tools/filtermain.cpp',
+        '../tools/path_utils.h',
+        '../tools/path_utils.cpp',
+        '../debugger/SkDrawCommand.h',
+        '../debugger/SkDrawCommand.cpp',
+        '../debugger/SkDebugCanvas.h',
+        '../debugger/SkDebugCanvas.cpp',
+        '../debugger/SkObjectParser.h',
+        '../debugger/SkObjectParser.cpp',
+      ],
+      'dependencies': [
+        'skia_base_libs.gyp:skia_base_libs',
+        'effects.gyp:effects',
+        'images.gyp:images',
+        'tools.gyp:picture_utils',
       ],
     },
   ],
diff --git a/gyp/utils.gyp b/gyp/utils.gyp
index 8d2415a..c62657d 100644
--- a/gyp/utils.gyp
+++ b/gyp/utils.gyp
@@ -1,23 +1,36 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'utils',
+      'product_name': 'skia_utils',
       'type': 'static_library',
+      'standalone_static_library': 1,
+      'dependencies': [
+        'cityhash',
+      ],
       'include_dirs': [
         '../include/config',
         '../include/core',
+        '../include/effects',
+        '../include/pipe',
         '../include/utils',
         '../include/utils/mac',
         '../include/utils/unix',
         '../include/utils/win',
-        '../include/views',
-        '../include/effects',
         '../include/xml',
+        '../src/core',
+        '../src/utils',
       ],
       'sources': [
+        # Classes for a threadpool.
+        '../include/utils/SkCondVar.h',
+        '../include/utils/SkCountdown.h',
+        '../include/utils/SkRunnable.h',
+        '../include/utils/SkThreadPool.h',
+        '../src/utils/SkCondVar.cpp',
+        '../src/utils/SkCountdown.cpp',
+        '../src/utils/SkThreadPool.cpp',
+
         '../include/utils/SkBoundaryPatch.h',
         '../include/utils/SkCamera.h',
         '../include/utils/SkCubicInterval.h',
@@ -30,59 +43,78 @@
         '../include/utils/SkMeshUtils.h',
         '../include/utils/SkNinePatch.h',
         '../include/utils/SkNWayCanvas.h',
+        '../include/utils/SkNullCanvas.h',
         '../include/utils/SkParse.h',
         '../include/utils/SkParsePaint.h',
         '../include/utils/SkParsePath.h',
+        '../include/utils/SkPictureUtils.h',
+        '../include/utils/SkRandom.h',
+        '../include/utils/SkRTConf.h',
         '../include/utils/SkProxyCanvas.h',
-        '../include/utils/SkSfntUtils.h',
-        '../include/utils/SkTextBox.h',
         '../include/utils/SkUnitMappers.h',
         '../include/utils/SkWGL.h',
 
         '../src/utils/SkBase64.cpp',
         '../src/utils/SkBase64.h',
+        '../src/utils/SkBitmapChecksummer.cpp',
+        '../src/utils/SkBitmapChecksummer.h',
+        '../src/utils/SkBitmapTransformer.cpp',
+        '../src/utils/SkBitmapTransformer.h',
+        '../src/utils/SkBitSet.cpp',
+        '../src/utils/SkBitSet.h',
         '../src/utils/SkBoundaryPatch.cpp',
         '../src/utils/SkCamera.cpp',
-        '../src/utils/SkColorMatrix.cpp',
+        '../src/utils/SkCityHash.cpp',
+        '../src/utils/SkCityHash.h',
         '../src/utils/SkCubicInterval.cpp',
         '../src/utils/SkCullPoints.cpp',
         '../src/utils/SkDeferredCanvas.cpp',
         '../src/utils/SkDumpCanvas.cpp',
+        '../src/utils/SkFloatUtils.h',
         '../src/utils/SkInterpolator.cpp',
         '../src/utils/SkLayer.cpp',
         '../src/utils/SkMatrix44.cpp',
+        '../src/utils/SkMD5.cpp',
+        '../src/utils/SkMD5.h',
         '../src/utils/SkMeshUtils.cpp',
         '../src/utils/SkNinePatch.cpp',
         '../src/utils/SkNWayCanvas.cpp',
+        '../src/utils/SkNullCanvas.cpp',
         '../src/utils/SkOSFile.cpp',
         '../src/utils/SkParse.cpp',
         '../src/utils/SkParseColor.cpp',
         '../src/utils/SkParsePath.cpp',
+        '../src/utils/SkPictureUtils.cpp',
         '../src/utils/SkProxyCanvas.cpp',
-        '../src/utils/SkSfntUtils.cpp',
+        '../src/utils/SkSHA1.cpp',
+        '../src/utils/SkSHA1.h',
+        '../src/utils/SkRTConf.cpp',
+        '../src/utils/SkThreadUtils.h',
+        '../src/utils/SkThreadUtils_pthread.cpp',
+        '../src/utils/SkThreadUtils_pthread.h',
+        '../src/utils/SkThreadUtils_pthread_linux.cpp',
+        '../src/utils/SkThreadUtils_pthread_mach.cpp',
+        '../src/utils/SkThreadUtils_pthread_other.cpp',
+        '../src/utils/SkThreadUtils_win.cpp',
+        '../src/utils/SkThreadUtils_win.h',
         '../src/utils/SkUnitMappers.cpp',
 
         #mac
         '../include/utils/mac/SkCGUtils.h',
         '../src/utils/mac/SkCreateCGImageRef.cpp',
 
-        #sdl
-        '../src/utils/SDL/SkOSWindow_SDL.cpp',
-
-        #*nix
-        '../src/utils/unix/keysym2ucs.c',
-        '../src/utils/unix/SkOSWindow_Unix.cpp',
-        
         #windows
         '../include/utils/win/SkAutoCoInitialize.h',
         '../include/utils/win/SkHRESULT.h',
         '../include/utils/win/SkIStream.h',
         '../include/utils/win/SkTScopedComPtr.h',
         '../src/utils/win/SkAutoCoInitialize.cpp',
-        '../src/utils/win/skia_win.cpp',
+        '../src/utils/win/SkDWriteFontFileStream.cpp',
+        '../src/utils/win/SkDWriteFontFileStream.h',
+        '../src/utils/win/SkDWriteGeometrySink.cpp',
+        '../src/utils/win/SkDWriteGeometrySink.h',
         '../src/utils/win/SkHRESULT.cpp',
         '../src/utils/win/SkIStream.cpp',
-        '../src/utils/win/SkOSWindow_win.cpp',
         '../src/utils/win/SkWGL_win.cpp',
       ],
       'sources!': [
@@ -95,11 +127,16 @@
               '$(SDKROOT)/System/Library/Frameworks/AGL.framework',
             ],
           },
+        }],
+        [ 'skia_os in ["mac", "ios"]', {
           'direct_dependent_settings': {
             'include_dirs': [
               '../include/utils/mac',
             ],
           },
+          'sources!': [
+            '../src/utils/SkThreadUtils_pthread_other.cpp',
+          ],
         },{ #else if 'skia_os != "mac"'
           'include_dirs!': [
             '../include/utils/mac',
@@ -107,24 +144,19 @@
           'sources!': [
             '../include/utils/mac/SkCGUtils.h',
             '../src/utils/mac/SkCreateCGImageRef.cpp',
-            '../src/utils/mac/skia_mac.mm',
-            '../src/utils/mac/SkOSWindow_Mac.mm',
+            '../src/utils/SkThreadUtils_pthread_mach.cpp',
           ],
         }],
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
-          'link_settings': {
-            'libraries': [
-              '-lGL',
-              '-lGLU',
-            ],
-          },
+          'sources!': [
+            '../src/utils/SkThreadUtils_pthread_other.cpp',
+          ],
         },{ #else if 'skia_os not in ["linux", "freebsd", "openbsd", "solaris"]'
           'include_dirs!': [
             '../include/utils/unix',
           ],
           'sources!': [
-            '../src/utils/unix/keysym2ucs.c',
-            '../src/utils/unix/SkOSWindow_Unix.cpp',
+            '../src/utils/SkThreadUtils_pthread_linux.cpp',
           ],
         }],
         [ 'skia_os == "win"', {
@@ -133,6 +165,11 @@
               '../include/utils/win',
             ],
           },
+          'sources!': [
+            '../src/utils/SkThreadUtils_pthread.cpp',
+            '../src/utils/SkThreadUtils_pthread.h',
+            '../src/utils/SkThreadUtils_pthread_other.cpp',
+          ],
         },{ #else if 'skia_os != "win"'
           'include_dirs!': [
             '../include/utils/win',
@@ -144,10 +181,27 @@
             '../include/utils/win/SkIStream.h',
             '../include/utils/win/SkTScopedComPtr.h',
             '../src/utils/win/SkAutoCoInitialize.cpp',
+            '../src/utils/win/SkDWriteFontFileStream.cpp',
+            '../src/utils/win/SkDWriteFontFileStream.h',
+            '../src/utils/win/SkDWriteGeometrySink.cpp',
+            '../src/utils/win/SkDWriteGeometrySink.h',
             '../src/utils/win/SkHRESULT.cpp',
             '../src/utils/win/SkIStream.cpp',
           ],
         }],
+        [ 'skia_os == "nacl"', {
+          'sources': [
+            '../src/utils/SkThreadUtils_pthread_other.cpp',
+          ],
+          'sources!': [
+            '../src/utils/SkThreadUtils_pthread_linux.cpp',
+          ],
+        }],
+        [ 'skia_os == "android"', {
+          'sources': [
+            '../src/utils/android/ashmem.c',
+          ],
+        }],
       ],
       'direct_dependent_settings': {
         'include_dirs': [
@@ -155,6 +209,41 @@
         ],
       },
     },
+    {
+      'target_name': 'cityhash',
+      'type': 'static_library',
+      'standalone_static_library': 1,
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../src/utils/cityhash',
+        '../third_party/externals/cityhash/src',
+      ],
+      'sources': [
+        '../third_party/externals/cityhash/src/city.cc',
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../third_party/externals/cityhash/src',
+        ],
+      },
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'xcode_settings': {
+            'OTHER_CPLUSPLUSFLAGS!': [
+              '-Werror',
+            ]
+          },
+        }],
+        [ 'skia_os == "win"', {
+          'msvs_settings': {
+            'VCCLCompilerTool': {
+              'WarnAsError': 'false',
+            },
+          },
+        }],
+      ],
+    },
   ],
 }
 
diff --git a/gyp/views.gyp b/gyp/views.gyp
index e91b28e..b0697a0 100644
--- a/gyp/views.gyp
+++ b/gyp/views.gyp
@@ -1,11 +1,16 @@
+# Views is the Skia windowing toolkit.
+# It provides:
+#  * A portable means of creating native windows.
+#  * Events.
+#  * Basic widgets and controls.
+
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'views',
+      'product_name': 'skia_views',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'include_dirs': [
         '../include/config',
         '../include/core',
@@ -13,48 +18,40 @@
         '../include/xml',
         '../include/utils',
         '../include/images',
-        '../include/animator',
         '../include/effects',
+        '../include/views/unix',
+      ],
+      'dependencies': [
+        'angle.gyp:*',
       ],
       'sources': [
         '../include/views/SkApplication.h',
         '../include/views/SkBGViewArtist.h',
-        '../include/views/SkBorderView.h',
         '../include/views/SkEvent.h',
         '../include/views/SkEventSink.h',
-        '../include/views/SkImageView.h',
         '../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',
-        #'../include/views/SkOSWindow_wxwidgets.h',
-        '../include/views/SkProgressBarView.h',
-        '../include/views/SkScrollBarView.h',
         '../include/views/SkStackViewLayout.h',
         '../include/views/SkSystemEventTypes.h',
+        '../include/views/SkTextBox.h',
         '../include/views/SkTouchGesture.h',
         '../include/views/SkView.h',
         '../include/views/SkViewInflate.h',
         '../include/views/SkWidget.h',
-        '../include/views/SkWidgetViews.h',
         '../include/views/SkWindow.h',
 
         '../src/views/SkBGViewArtist.cpp',
-        '../src/views/SkBorderView.cpp',
         '../src/views/SkEvent.cpp',
         '../src/views/SkEventSink.cpp',
-        '../src/views/SkImageView.cpp',
-        '../src/views/SkListView.cpp',
-        '../src/views/SkListWidget.cpp',
         '../src/views/SkOSMenu.cpp',
         '../src/views/SkParsePaint.cpp',
-        '../src/views/SkProgressBarView.cpp',
         '../src/views/SkProgressView.cpp',
-        '../src/views/SkScrollBarView.cpp',
         '../src/views/SkStackViewLayout.cpp',
-        '../src/views/SkStaticTextView.cpp',
         '../src/views/SkTagList.cpp',
         '../src/views/SkTagList.h',
         '../src/views/SkTextBox.cpp',
@@ -63,14 +60,28 @@
         '../src/views/SkViewInflate.cpp',
         '../src/views/SkViewPriv.cpp',
         '../src/views/SkViewPriv.h',
-        '../src/views/SkWidget.cpp',
         '../src/views/SkWidgets.cpp',
-        '../src/views/SkWidgetViews.cpp',
         '../src/views/SkWindow.cpp',
+
+        # Mac
+        '../src/views/mac/SkOSWindow_Mac.mm',
+        '../src/views/mac/skia_mac.mm',
+
+        # SDL
+        '../src/views/SDL/SkOSWindow_SDL.cpp',
+
+        # *nix
+        '../src/views/unix/SkOSWindow_Unix.cpp',
+        '../src/views/unix/keysym2ucs.c',
+        '../src/views/unix/skia_unix.cpp',
+
+        # Windows
+        '../src/views/win/SkOSWindow_win.cpp',
+        '../src/views/win/skia_win.cpp',
+
       ],
       'sources!' : [
-        '../src/views/SkListView.cpp',   #depends on missing SkListSource implementation
-        '../src/views/SkListWidget.cpp', #depends on missing SkListSource implementation
+        '../src/views/SDL/SkOSWindow_SDL.cpp',
       ],
       'conditions': [
         [ 'skia_os == "mac"', {
@@ -80,25 +91,48 @@
               '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
             ],
           },
+        },{
+          'sources!': [
+            '../src/views/mac/SkOSWindow_Mac.mm',
+            '../src/views/mac/skia_mac.mm',
+          ],
         }],
         [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris"]', {
-          'sources': [
-            '../unix_test_app/main.cpp',
+          'link_settings': {
+            'libraries': [
+              '-lGL',
+              '-lGLU',
+              '-lX11',
+            ],
+          },
+        },{
+          'sources!': [
+            '../src/views/unix/SkOSWindow_Unix.cpp',
+            '../src/views/unix/keysym2ucs.c',
+            '../src/views/unix/skia_unix.cpp',
           ],
         }],
-        [ 'skia_os == "android"', {
-          # Android does not support animator so we need to remove all files
-          # that have references to it.
-          'include_dirs!': [
-            '../include/animator',
-          ],
+        [ 'skia_os == "win"', {
+        },{
           'sources!': [
-            '../src/views/SkBorderView.cpp',
-            '../src/views/SkImageView.cpp',
-            '../src/views/SkProgressBarView.cpp',
-            '../src/views/SkScrollBarView.cpp',
-            '../src/views/SkStaticTextView.cpp',
-            '../src/views/SkWidgetViews.cpp',
+            '../src/views/win/SkOSWindow_win.cpp',
+            '../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
new file mode 100644
index 0000000..1767fe1
--- /dev/null
+++ b/gyp/views_animated.gyp
@@ -0,0 +1,72 @@
+#Animated widgets are views which use animator.
+
+{
+  'targets': [
+    {
+      'target_name': 'views_animated',
+      'type': 'static_library',
+      'include_dirs': [
+        '../include/config',
+        '../include/core',
+        '../include/views',
+        '../include/xml',
+        '../include/utils',
+        '../include/images',
+        '../include/animator',
+        '../include/effects',
+        '../include/views/unix',
+        '../include/views/animated',
+      ],
+      'sources': [
+        '../include/views/animated/SkBorderView.h',
+        '../include/views/animated/SkImageView.h',
+        '../include/views/animated/SkProgressBarView.h',
+        '../include/views/animated/SkScrollBarView.h',
+        '../include/views/animated/SkWidgetViews.h',
+
+        '../src/views/animated/SkBorderView.cpp',
+        '../src/views/animated/SkImageView.cpp',
+        '../src/views/animated/SkProgressBarView.cpp',
+        '../src/views/animated/SkScrollBarView.cpp',
+        '../src/views/animated/SkStaticTextView.cpp',
+        '../src/views/animated/SkWidgetViews.cpp',
+      ],
+      'conditions': [
+        [ 'skia_os == "mac"', {
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/System/Library/Frameworks/Cocoa.framework',
+              '$(SDKROOT)/System/Library/Frameworks/Foundation.framework',
+            ],
+          },
+        }],
+        [ 'skia_os == "android"', {
+          # Android does not support animator so we need to remove all files
+          # that have references to it.
+          'include_dirs!': [
+            '../include/animator',
+          ],
+          'sources!': [
+            '../src/views/animated/SkBorderView.cpp',
+            '../src/views/animated/SkImageView.cpp',
+            '../src/views/animated/SkProgressBarView.cpp',
+            '../src/views/animated/SkScrollBarView.cpp',
+            '../src/views/animated/SkStaticTextView.cpp',
+            '../src/views/animated/SkWidgetViews.cpp',
+          ],
+        }],
+      ],
+      'direct_dependent_settings': {
+        'include_dirs': [
+          '../include/views/animated',
+        ],
+      },
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/gyp/xml.gyp b/gyp/xml.gyp
index 7da8666..5cf0659 100644
--- a/gyp/xml.gyp
+++ b/gyp/xml.gyp
@@ -1,11 +1,10 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'xml',
+      'product_name': 'skia_xml',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'include_dirs': [
         '../include/config',
         '../include/core',
@@ -33,7 +32,7 @@
           '../src/xml/SkXMLPullParser.cpp', #if 0 around class decl in header
       ],
       'conditions': [
-        [ 'skia_os in ["win", "mac", "linux", "freebsd", "openbsd", "solaris", "android"]', {
+        [ 'skia_os in ["win", "mac", "linux", "freebsd", "openbsd", "solaris", "android", "ios", "nacl"]', {
           'sources!': [
             # no jsapi.h by default on system
             '../include/xml/SkJS.h',
diff --git a/gyp/xps.gyp b/gyp/xps.gyp
index ff68d6f..e5b7c0d 100644
--- a/gyp/xps.gyp
+++ b/gyp/xps.gyp
@@ -1,21 +1,19 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'xps',
+      'product_name': 'skia_xps',
       'type': 'static_library',
+      'standalone_static_library': 1,
       'dependencies': [
-        'core.gyp:core',
+        'skia_base_libs.gyp:skia_base_libs',
         'images.gyp:images',
-        'utils.gyp:utils',
-        'pdf.gyp:pdf', # needed to get SkBitSet
       ],
       'include_dirs': [
         '../include/device/xps',
         '../include/utils/win',
         '../src/core', # needed to get SkGlyphCache.h
+        '../src/utils', # needed to get SkBitSet.h
       ],
       'sources': [
         '../include/device/xps/SkConstexprMath.h',
@@ -54,6 +52,7 @@
         ],
         'include_dirs': [
           '../include/device/xps',
+          '../src/utils', # needed to get SkBitSet.h
         ],
       },
     },
diff --git a/gyp/zlib.gyp b/gyp/zlib.gyp
index 5ed9d14..fcd0ac3 100644
--- a/gyp/zlib.gyp
+++ b/gyp/zlib.gyp
@@ -1,7 +1,4 @@
 {
-  'includes': [
-    'common.gypi',
-  ],
   'targets': [
     {
       'target_name': 'zlib',
@@ -24,7 +21,15 @@
           },
           'defines': [ 'SK_ZLIB_INCLUDE=<zlib.h>', ],
         }],
-        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "android"]', {
+        [ 'skia_os == "ios"', {
+          'link_settings': {
+            'libraries': [
+              '$(SDKROOT)/usr/lib/libz.dylib',
+            ],
+          },
+          'defines': [ 'SK_ZLIB_INCLUDE=<zlib.h>', ],
+        }],
+        [ 'skia_os in ["linux", "freebsd", "openbsd", "solaris", "android", "nacl"]', {
           'link_settings': { 'libraries': [ '-lz', ], },
           'defines': [ 'SK_ZLIB_INCLUDE=<zlib.h>', ],
         }],
diff --git a/gyp_skia b/gyp_skia
new file mode 100755
index 0000000..8032589
--- /dev/null
+++ b/gyp_skia
@@ -0,0 +1,127 @@
+#!/usr/bin/python
+
+# Copyright 2011 The Android Open Source Project
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+# This script is a wrapper which invokes gyp with the correct --depth argument,
+# and supports the automatic regeneration of build files if all.gyp is
+# changed (Linux-only).
+
+import glob
+import os
+import platform
+import shlex
+import sys
+
+script_dir = os.path.dirname(__file__)
+
+# Directory within which we can find the gyp source.
+gyp_source_dir = os.path.join(script_dir, 'third_party', 'externals', 'gyp')
+
+# Directory within which we can find most of Skia's gyp configuration files.
+gyp_config_dir = os.path.join(script_dir, 'gyp')
+
+# Ensure we import our current gyp source's module, not any version
+# pre-installed in your PYTHONPATH.
+sys.path.insert(0, os.path.join(gyp_source_dir, 'pylib'))
+import gyp
+
+def additional_include_files(args=[]):
+  # Determine the include files specified on the command line.
+  # This doesn't cover all the different option formats you can use,
+  # but it's mainly intended to avoid duplicating flags on the automatic
+  # makefile regeneration which only uses this format.
+  specified_includes = set()
+  for arg in args:
+    if arg.startswith('-I') and len(arg) > 2:
+      specified_includes.add(os.path.realpath(arg[2:]))
+
+  result = []
+  def AddInclude(path):
+    if os.path.realpath(path) not in specified_includes:
+      result.append(path)
+
+  # Always include common.gypi.
+  # We do this, rather than including common.gypi explicitly in all our gyp
+  # files, so that gyp files we use but do not maintain (e.g.,
+  # third_party/externals/libjpeg/libjpeg.gyp) will include common.gypi too.
+  AddInclude(os.path.join(gyp_config_dir, 'common.gypi'))
+
+  return result
+
+# Return the directory where all generated files (including Makefiles) are to
+# be written.
+def get_output_dir():
+
+  # SKIA_OUT can be any directory either as a child of the standard out/
+  # directory or any given location on the file system (e.g. /tmp/skia)
+  output_dir = os.getenv('SKIA_OUT')
+
+  if not output_dir:
+    return os.path.join(os.path.abspath(script_dir), 'out')
+
+  if (os.name != 'posix' or
+     (sys.platform.startswith('darwin') and 
+      (not os.getenv('GYP_GENERATORS') or 
+       'make' not in os.getenv('GYP_GENERATORS')))):
+    print 'ERROR: variable SKIA_OUT is not valid on Mac (using xcodebuild)' \
+          ' or Windows'
+    sys.exit(-1);
+
+  if os.path.isabs(output_dir):
+    return output_dir
+  else:
+    return os.path.join(os.path.abspath(script_dir), output_dir)
+
+
+if __name__ == '__main__':
+  args = sys.argv[1:]
+
+  # Set CWD to the directory containing this script.
+  # This allows us to launch it from other directories, in spite of gyp's
+  # finickyness about the current working directory.
+  # See http://b.corp.google.com/issue?id=5019517 ('Linux make build
+  # (from out dir) no longer runs skia_gyp correctly')
+  os.chdir(os.path.abspath(script_dir))
+
+  # This could give false positives since it doesn't actually do real option
+  # parsing.  Oh well.
+  gyp_file_specified = False
+  for arg in args:
+    if arg.endswith('.gyp'):
+      gyp_file_specified = True
+      break
+
+  # If we didn't get a file, then fall back to assuming 'skia.gyp' from the
+  # same directory as the script.
+  # The gypfile must be passed as a relative path, not an absolute path,
+  # or else the gyp code doesn't write into the proper output dir.
+  if not gyp_file_specified:
+    args.append('skia.gyp')
+
+  args.extend(['-I' + i for i in additional_include_files(args)])
+  args.extend(['--depth', '.'])
+
+  # Tell gyp to write the Makefiles into output_dir
+  args.extend(['--generator-output', os.path.abspath(get_output_dir())])
+
+  # Tell make to write its output into the same dir
+  args.extend(['-Goutput_dir=.'])
+
+  # By default, we build 'most' instead of 'all' or 'everything'. See skia.gyp.
+  args.extend(['-Gdefault_target=most'])
+
+  # Special arguments for generating Visual Studio projects:
+  # - msvs_version forces generation of Visual Studio 2010 project so that we
+  #   can use msbuild.exe
+  # - msvs_abspath_output is a workaround for
+  #   http://code.google.com/p/gyp/issues/detail?id=201
+  args.extend(['-Gmsvs_version=2010'])
+
+  print 'Updating projects from gyp files...'
+  sys.stdout.flush()
+
+  # Off we go...
+  sys.exit(gyp.main(args))
diff --git a/include/animator/SkAnimator.h b/include/animator/SkAnimator.h
index e6c5583..9bb3642 100644
--- a/include/animator/SkAnimator.h
+++ b/include/animator/SkAnimator.h
@@ -42,15 +42,15 @@
 /** \class SkAnimator
 
     The SkAnimator class decodes an XML stream into a display list. The
-    display list can be drawn statically as a picture, or can drawn 
+    display list can be drawn statically as a picture, or can drawn
     different elements at different times to form a moving animation.
 
     SkAnimator does not read the system time on its own; it relies on the
     caller to pass the current time. The caller can pause, speed up, or
     reverse the animation by varying the time passed in.
 
-    The XML describing the display list must conform to the schema 
-    described by SkAnimateSchema.xsd. 
+    The XML describing the display list must conform to the schema
+    described by SkAnimateSchema.xsd.
 
     The XML must contain an <event> element to draw. Usually, it contains
     an <event kind="onload" /> block to add some drawing elements to the
@@ -84,7 +84,7 @@
     SkAnimator();
     virtual ~SkAnimator();
 
-    /** Add a drawable extension to the graphics engine. Experimental. 
+    /** Add a drawable extension to the graphics engine. Experimental.
         @param extras A derived class that implements methods that identify and instantiate the class
     */
     void addExtras(SkExtras* extras);
@@ -97,7 +97,7 @@
     */
     bool appendStream(SkStream* stream);
 
-    /** Read in XML from memory. Returns true if the file can be 
+    /** Read in XML from memory. Returns true if the file can be
         read without error. Returns false if an error was encountered.
         Error diagnostics are stored in fErrorCode and fLineNumber.
         @param buffer  The XML text as UTF-8 characters.
@@ -106,7 +106,7 @@
     */
     bool decodeMemory(const void* buffer, size_t size);
 
-    /** Read in XML from a stream. Returns true if the file can be 
+    /** Read in XML from a stream. Returns true if the file can be
         read without error. Returns false if an error was encountered.
         Error diagnostics are stored in fErrorCode and fLineNumber.
         @param stream  The stream containg the XML text as UTF-8 characters.
@@ -114,14 +114,14 @@
     */
     virtual bool decodeStream(SkStream* stream);
 
-    /** Parse the DOM tree starting at the specified node. Returns true if it can be 
+    /** Parse the DOM tree starting at the specified node. Returns true if it can be
         parsed without error. Returns false if an error was encountered.
         Error diagnostics are stored in fErrorCode and fLineNumber.
         @return true if the DOM was parsed successfully.
     */
     virtual bool decodeDOM(const SkDOM&, const SkDOMNode*);
 
-    /** Read in XML from a URI. Returns true if the file can be 
+    /** Read in XML from a URI. Returns true if the file can be
         read without error. Returns false if an error was encountered.
         Error diagnostics are stored in fErrorCode and fLineNumber.
         @param uri The complete url path to be read (either ftp, http or https).
@@ -131,14 +131,14 @@
 
     /** Pass a char event, usually a keyboard symbol, to the animator.
         This triggers events of the form <event kind="keyChar" key="... />
-        @param ch  The character to match against <event> element "key" 
+        @param ch  The character to match against <event> element "key"
             attributes.
         @return true if the event was dispatched successfully.
     */
     bool doCharEvent(SkUnichar ch);
 
     /** Experimental:
-        Pass a mouse click event along with the mouse coordinates to 
+        Pass a mouse click event along with the mouse coordinates to
         the animator. This triggers events of the form <event kind="mouseDown" ... />
         and other mouse events.
         @param state The mouse state, described by SkView::Click::State : values are
@@ -151,48 +151,48 @@
 
     /** Pass a meta-key event, such as an arrow , to the animator.
         This triggers events of the form <event kind="keyPress" code="... />
-        @param code  The key to match against <event> element "code" 
+        @param code  The key to match against <event> element "code"
             attributes.
         @return true if the event was dispatched successfully.
     */
     bool doKeyEvent(SkKey code);
     bool doKeyUpEvent(SkKey code);
-    
-    /** Send an event to the animator. The animator's clock is set 
+
+    /** Send an event to the animator. The animator's clock is set
         relative to the current time.
         @return true if the event was dispatched successfully.
     */
     bool doUserEvent(const SkEvent& evt);
 
-    /** The possible results from the draw function. 
+    /** The possible results from the draw function.
     */
     enum DifferenceType {
         kNotDifferent,
         kDifferent,
         kPartiallyDifferent
     };
-    /** Draws one frame of the animation. The first call to draw always 
-        draws the initial frame of the animation. Subsequent calls draw 
-        the offset into the animation by 
+    /** Draws one frame of the animation. The first call to draw always
+        draws the initial frame of the animation. Subsequent calls draw
+        the offset into the animation by
         subtracting the initial time from the current time.
         @param canvas  The canvas to draw into.
         @param paint     The paint to draw with.
         @param time  The offset into the current animation.
         @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and
-        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal 
+        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal
         redraw area.
     */
     DifferenceType draw(SkCanvas* canvas, SkPaint* paint, SkMSec time);
 
     /** Draws one frame of the animation, using a new Paint each time.
-        The first call to draw always 
-        draws the initial frame of the animation. Subsequent calls draw 
-        the offset into the animation by 
+        The first call to draw always
+        draws the initial frame of the animation. Subsequent calls draw
+        the offset into the animation by
         subtracting the initial time from the current time.
         @param canvas  The canvas to draw into.
         @param time  The offset into the current animation.
         @return kNotDifferent if there are no active animations; kDifferent if there are active animations; and
-        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal 
+        kPartiallyDifferent if the document contains an active <bounds> element that specifies a minimal
         redraw area.
     */
     DifferenceType draw(SkCanvas* canvas, SkMSec time);
@@ -203,7 +203,7 @@
         @param y ignored
         @return true if a mouseDown event handler is enabled.
     */
-    bool findClickEvent(SkScalar x, SkScalar y); 
+    bool findClickEvent(SkScalar x, SkScalar y);
 
 
     /** Get the nested animator associated with this element, if any.
@@ -223,7 +223,7 @@
 
     /** Returns the scalar value of the specified element's attribute[index]
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param index the array entry
         @return the integer value to retrieve, or SK_NaN32 if unsuccessful
     */
@@ -239,7 +239,7 @@
 
     /** Returns the scalar value of the specified element's attribute[index]
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param index the array entry
         @return the scalar value to retrieve, or SK_ScalarNaN if unsuccessful
     */
@@ -247,7 +247,7 @@
 
     /** Returns the string value of the specified element's attribute[index]
         @param element is a value returned by getElement
-        @param field is a value returned by getField  
+        @param field is a value returned by getField
         @param index the array entry
         @return the string value to retrieve, or null if unsuccessful
     */
@@ -255,55 +255,55 @@
 
     /** Returns the string value of the specified element's attribute[index]
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param index the array entry
         @return the string value to retrieve, or null if unsuccessful
     */
     const char* getArrayString(const char* elementID, const char* fieldName, int index);
 
     /** Returns the XML element corresponding to the given ID.
-        @param elementID is the value of the id attribute in the XML of this element 
+        @param elementID is the value of the id attribute in the XML of this element
         @return the element matching the ID, or null if the element can't be found
     */
     const SkDisplayable* getElement(const char* elementID);
 
     /** Returns the element type corresponding to the XML element.
         The element type matches the element name; for instance, <line> returns kElement_LineType
-        @param element is a value returned by getElement  
+        @param element is a value returned by getElement
         @return element type, or 0 if the element can't be found
     */
     SkElementType getElementType(const SkDisplayable* element);
 
     /** Returns the element type corresponding to the given ID.
-        @param elementID is the value of the id attribute in the XML of this element 
+        @param elementID is the value of the id attribute in the XML of this element
         @return element type, or 0 if the element can't be found
     */
     SkElementType getElementType(const char* elementID);
 
     /** Returns the XML field of the named attribute in the XML element.
         @param element is a value returned by getElement
-        @param fieldName is the attribute to return  
+        @param fieldName is the attribute to return
         @return the attribute matching the fieldName, or null if the element can't be found
     */
     const SkMemberInfo* getField(const SkDisplayable* element, const char* fieldName);
 
     /** Returns the XML field of the named attribute in the XML element matching the elementID.
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName is the attribute to return  
+        @param fieldName is the attribute to return
         @return the attribute matching the fieldName, or null if the element can't be found
     */
     const SkMemberInfo* getField(const char* elementID, const char* fieldName);
 
     /** Returns the value type coresponding to the element's attribute.
         The value type matches the XML schema: and may be kField_BooleanType, kField_ScalarType, etc.
-        @param field is a value returned by getField  
+        @param field is a value returned by getField
         @return the attribute type, or 0 if the element can't be found
     */
     SkFieldType getFieldType(const SkMemberInfo* field);
 
     /** Returns the value type coresponding to the element's attribute.
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @return the attribute type, or 0 if the element can't be found
     */
     SkFieldType getFieldType(const char* elementID, const char* fieldName);
@@ -315,54 +315,54 @@
 
     /** Returns the partial rectangle to invalidate after drawing. Call after draw() returns
     kIsPartiallyDifferent to do a mimimal inval(). */
-    void getInvalBounds(SkRect* inval); 
+    void getInvalBounds(SkRect* inval);
 
-    /** Returns the details of any error encountered while parsing the XML. 
+    /** Returns the details of any error encountered while parsing the XML.
     */
     const SkXMLParserError* getParserError();
-    
-    /** Returns the details of any error encountered while parsing the XML as string. 
+
+    /** Returns the details of any error encountered while parsing the XML as string.
     */
     const char* getParserErrorString();
-    
+
     /** Returns the scalar value of the specified element's attribute
         @param element is a value returned by getElement
-        @param field is a value returned by getField  
+        @param field is a value returned by getField
         @return the integer value to retrieve, or SK_NaN32 if not found
     */
     int32_t getInt(const SkDisplayable* element, const SkMemberInfo* field);
 
     /** Returns the scalar value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @return the integer value to retrieve, or SK_NaN32 if not found
     */
     int32_t getInt(const char* elementID, const char* fieldName);
 
     /** Returns the scalar value of the specified element's attribute
         @param element is a value returned by getElement
-        @param field is a value returned by getField  
+        @param field is a value returned by getField
         @return the scalar value to retrieve, or SK_ScalarNaN if not found
     */
     SkScalar getScalar(const SkDisplayable* element, const SkMemberInfo* field);
 
     /** Returns the scalar value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @return the scalar value to retrieve, or SK_ScalarNaN if not found
     */
     SkScalar getScalar(const char* elementID, const char* fieldName);
 
     /** Returns the string value of the specified element's attribute
         @param element is a value returned by getElement
-        @param field is a value returned by getField  
+        @param field is a value returned by getField
         @return the string value to retrieve, or null if not found
     */
     const char* getString(const SkDisplayable* element, const SkMemberInfo* field);
 
     /** Returns the string value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @return the string value to retrieve, or null if not found
     */
     const char* getString(const char* elementID, const char* fieldName);
@@ -373,31 +373,31 @@
     /** Resets the animator to a newly created state with no animation data. */
     void initialize();
 
-    /** Experimental. Resets any active animations so that the next time passed is treated as 
+    /** Experimental. Resets any active animations so that the next time passed is treated as
         time zero. */
     void reset();
-    
+
     /** Sets the scalar value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param array is the c-style array of integers
         @param count is the length of the array
         @return true if the value was set successfully
     */
     bool setArrayInt(const char* elementID, const char* fieldName, const int* array, int count);
-    
+
     /** Sets the scalar value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param array is the c-style array of strings
         @param count is the length of the array
         @return true if the value was set successfully
     */
     bool setArrayString(const char* elementID, const char* fieldName, const char** array, int count);
-    
+
     /** Sets the scalar value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param data the integer value to set
         @return true if the value was set successfully
     */
@@ -405,7 +405,7 @@
 
     /** Sets the scalar value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param data the scalar value to set
         @return true if the value was set successfully
     */
@@ -413,14 +413,14 @@
 
     /** Sets the string value of the specified element's attribute
         @param elementID is the value of the id attribute in the XML of this element
-        @param fieldName specifies the name of the attribute  
+        @param fieldName specifies the name of the attribute
         @param data the string value to set
         @return true if the value was set successfully
     */
     bool setString(const char* elementID, const char* fieldName, const char* data);
 
-    /** Sets the file default directory of the URL base path 
-        @param path the directory path 
+    /** Sets the file default directory of the URL base path
+        @param path the directory path
     */
     void setURIBase(const char* path);
 
@@ -442,7 +442,7 @@
         virtual SkMSec getMSecs() const = 0;
     };
 
-    /** Sets a user class to return the current time to the animator. 
+    /** Sets a user class to return the current time to the animator.
         Optional; if not called, the system clock will be used by calling SkTime::GetMSecs instead.
         @param callBack the time function
     */
@@ -450,28 +450,28 @@
 
     static void Init(bool runUnitTests);
     static void Term();
-    
-    /** The event sink events generated by the animation are posted to. 
+
+    /** The event sink events generated by the animation are posted to.
         Screenplay also posts an inval event to this event sink after processing an
         event to force a redraw.
         @param target the event sink id
     */
     void setHostEventSinkID(SkEventSinkID hostID);
     SkEventSinkID getHostEventSinkID() const;
-    
+
     // helper
     void setHostEventSink(SkEventSink* sink) {
         this->setHostEventSinkID(sink ? sink->getSinkID() : 0);
     }
-    
+
     virtual void setJavaOwner(Handler owner);
-    
+
 #ifdef SK_DEBUG
     virtual void eventDone(const SkEvent& evt);
     virtual bool isTrackingEvents();
     static bool NoLeaks();
-#endif  
-    
+#endif
+
 protected:
     virtual void onSetHostHandler(Handler handler);
     virtual void onEventPost(SkEvent*, SkEventSinkID);
@@ -484,7 +484,7 @@
     bool setInt(SkDisplayable* element, const SkMemberInfo* field, int32_t data);
     bool setScalar(SkDisplayable* element, const SkMemberInfo* field, SkScalar data);
     bool setString(SkDisplayable* element, const SkMemberInfo* field, const char* data);
-    
+
     virtual bool onEvent(const SkEvent&);
     SkAnimateMaker* fMaker;
     friend class SkAnimateMaker;
@@ -498,4 +498,3 @@
 };
 
 #endif
-
diff --git a/include/animator/SkAnimatorView.h b/include/animator/SkAnimatorView.h
index 940dd26..2b2c61b 100644
--- a/include/animator/SkAnimatorView.h
+++ b/include/animator/SkAnimatorView.h
@@ -37,4 +37,3 @@
 };
 
 #endif
-
diff --git a/include/core/Sk64.h b/include/core/Sk64.h
index b86e0be..6db3001 100644
--- a/include/core/Sk64.h
+++ b/include/core/Sk64.h
@@ -134,7 +134,7 @@
         fHi = fHi + hi + (sum < fLo);
         fLo = sum;
     }
-    
+
     /** Add the specified Sk64 to the number */
     void add(int32_t hi, uint32_t lo) {
         uint32_t sum = fLo + lo;
@@ -142,18 +142,18 @@
         fHi = fHi + hi + (sum < fLo);
         fLo = sum;
     }
-    
+
     /** Add the specified Sk64 to the number */
     void    add(const Sk64& other) { this->add(other.fHi, other.fLo); }
-    
+
     /** Subtract the specified Sk64 from the number. (*this) = (*this) - num
     */
     void    sub(const Sk64& num);
-    
+
     /** Subtract the number from the specified Sk64. (*this) = num - (*this)
     */
     void    rsub(const Sk64& num);
-    
+
     /** Multiply the number by the specified 32 bit integer
     */
     void    mul(int32_t);
@@ -162,7 +162,7 @@
         kTrunc_DivOption,   //!< truncate the result when calling div()
         kRound_DivOption    //!< round the result when calling div()
     };
-    
+
     /** Divide the number by the specified 32 bit integer, using the specified
         divide option (either truncate or round).
     */
@@ -205,19 +205,19 @@
     friend bool operator!=(const Sk64& a, const Sk64& b) {
         return a.fHi != b.fHi || a.fLo != b.fLo;
     }
-    
+
     friend bool operator<(const Sk64& a, const Sk64& b) {
         return a.fHi < b.fHi || (a.fHi == b.fHi && a.fLo < b.fLo);
     }
-    
+
     friend bool operator<=(const Sk64& a, const Sk64& b) {
         return a.fHi < b.fHi || (a.fHi == b.fHi && a.fLo <= b.fLo);
     }
-    
+
     friend bool operator>(const Sk64& a, const Sk64& b) {
         return a.fHi > b.fHi || (a.fHi == b.fHi && a.fLo > b.fLo);
     }
-    
+
     friend bool operator>=(const Sk64& a, const Sk64& b) {
         return a.fHi > b.fHi || (a.fHi == b.fHi && a.fLo >= b.fLo);
     }
@@ -228,4 +228,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkAdvancedTypefaceMetrics.h b/include/core/SkAdvancedTypefaceMetrics.h
index 09fc9a9..d326379 100755
--- a/include/core/SkAdvancedTypefaceMetrics.h
+++ b/include/core/SkAdvancedTypefaceMetrics.h
@@ -26,6 +26,8 @@
 
 class SkAdvancedTypefaceMetrics : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkAdvancedTypefaceMetrics)
+
     SkString fFontName;
 
     enum FontType {
@@ -34,7 +36,7 @@
         kCFF_Font,
         kTrueType_Font,
         kOther_Font,
-        kNotEmbeddable_Font,
+        kNotEmbeddable_Font
     };
     // The type of the underlying font program.  This field determines which
     // of the following fields are valid.  If it is kOther_Font or
@@ -56,7 +58,7 @@
         kItalic_Style      = 0x00040,
         kAllCaps_Style     = 0x10000,
         kSmallCaps_Style   = 0x20000,
-        kForceBold_Style   = 0x40000,
+        kForceBold_Style   = 0x40000
     };
     uint16_t fStyle;        // Font style characteristics.
     int16_t fItalicAngle;   // Counterclockwise degrees from vertical of the
@@ -75,7 +77,7 @@
       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
+      kToUnicode_PerGlyphInfo  = 0x8  // Populate ToUnicode table, ignored
                                       // for Type 1 fonts
     };
 
@@ -84,7 +86,7 @@
         enum MetricType {
             kDefault,  // Default advance: fAdvance.count = 1
             kRange,    // Advances for a range: fAdvance.count = fEndID-fStartID
-            kRun,      // fStartID-fEndID have same advance: fAdvance.count = 1
+            kRun       // fStartID-fEndID have same advance: fAdvance.count = 1
         };
         MetricType fType;
         uint16_t fStartId;
@@ -112,6 +114,9 @@
     // The mapping from glyph to Unicode, only populated if
     // kToUnicode_PerGlyphInfo is passed to GetAdvancedTypefaceMetrics.
     SkTDArray<SkUnichar> fGlyphToUnicode;
+
+private:
+    typedef SkRefCnt INHERITED;
 };
 
 namespace skia_advanced_typeface_metrics_utils {
diff --git a/include/core/SkAnnotation.h b/include/core/SkAnnotation.h
new file mode 100644
index 0000000..e1ecf6c
--- /dev/null
+++ b/include/core/SkAnnotation.h
@@ -0,0 +1,89 @@
+/*
+ * 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 SkAnnotation_DEFINED
+#define SkAnnotation_DEFINED
+
+#include "SkFlattenable.h"
+
+class SkData;
+class SkDataSet;
+class SkStream;
+class SkWStream;
+
+/**
+ *  Experimental class for annotating draws. Do not use directly yet.
+ *  Use helper functions at the bottom of this file for now.
+ */
+class SkAnnotation : public SkFlattenable {
+public:
+    enum Flags {
+        // If set, the associated drawing primitive should not be drawn
+        kNoDraw_Flag  = 1 << 0,
+    };
+
+    SkAnnotation(SkDataSet*, uint32_t flags);
+    virtual ~SkAnnotation();
+
+    uint32_t getFlags() const { return fFlags; }
+    SkDataSet* getDataSet() const { return fDataSet; }
+
+    bool isNoDraw() const { return SkToBool(fFlags & kNoDraw_Flag); }
+
+    /**
+     *  Helper for search the annotation's dataset.
+     */
+    SkData* find(const char name[]) const;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAnnotation)
+
+protected:
+    SkAnnotation(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    SkDataSet*  fDataSet;
+    uint32_t    fFlags;
+
+    void writeToStream(SkWStream*) const;
+    void readFromStream(SkStream*);
+
+    typedef SkFlattenable INHERITED;
+};
+
+/**
+ *  Experimental collection of predefined Keys into the Annotation dictionary
+ */
+class SkAnnotationKeys {
+public:
+    /**
+     *  Returns the canonical key whose payload is a URL
+     */
+    static const char* URL_Key();
+};
+
+///////////////////////////////////////////////////////////////////////////////
+//
+// Experimental helper functions to use Annotations
+//
+
+struct SkRect;
+class SkCanvas;
+
+/**
+ *  Experimental!
+ *
+ *  Annotate the canvas by associating the specified URL with the
+ *  specified rectangle (in local coordinates, just like drawRect). If the
+ *  backend of this canvas does not support annotations, this call is
+ *  safely ignored.
+ *
+ *  The caller is responsible for managing its ownership of the SkData.
+ */
+SK_API void SkAnnotateRectWithURL(SkCanvas*, const SkRect&, SkData*);
+
+#endif
diff --git a/include/core/SkAutoKern.h b/include/core/SkAutoKern.h
deleted file mode 100644
index 7a5cdef..0000000
--- a/include/core/SkAutoKern.h
+++ /dev/null
@@ -1,54 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkAutoKern_DEFINED
-#define SkAutoKern_DEFINED
-
-#include "SkScalerContext.h"
-
-#define SkAutoKern_AdjustF(prev, next)    (((next) - (prev) + 32) >> 6 << 16)
-#define SkAutoKern_AdjustS(prev, next)    SkIntToScalar(((next) - (prev) + 32) >> 6)
-
-/* this is a helper class to perform auto-kerning
- * the adjust() method returns a SkFixed corresponding
- * to a +1/0/-1 pixel adjustment
- */
-
-class SkAutoKern {
-public:
-    SkAutoKern() : fPrevRsbDelta(0) {}
-
-    SkFixed  adjust(const SkGlyph&  glyph) 
-    {
-//        if (SkAbs32(glyph.fLsbDelta) > 47 || SkAbs32(glyph.fRsbDelta) > 47)
-//            printf("------- %d> L %d R %d\n", glyph.f_GlyphID, glyph.fLsbDelta, glyph.fRsbDelta);
-
-#if 0
-        int  distort = fPrevRsbDelta - glyph.fLsbDelta;
-
-        fPrevRsbDelta = glyph.fRsbDelta;
-
-        if (distort >= 32)
-            return -SK_Fixed1;
-        else if (distort < -32)
-            return +SK_Fixed1;
-        else
-            return 0;
-#else
-        SkFixed adjust = SkAutoKern_AdjustF(fPrevRsbDelta, glyph.fLsbDelta);
-        fPrevRsbDelta = glyph.fRsbDelta;
-        return adjust;
-#endif
-    }
-private:
-    int   fPrevRsbDelta;
-};
-
-#endif
-
diff --git a/include/core/SkBitmap.h b/include/core/SkBitmap.h
index 2d5fc41..3fb35dc 100644
--- a/include/core/SkBitmap.h
+++ b/include/core/SkBitmap.h
@@ -12,16 +12,16 @@
 
 #include "Sk64.h"
 #include "SkColor.h"
+#include "SkColorTable.h"
 #include "SkPoint.h"
 #include "SkRefCnt.h"
 
 struct SkIRect;
-class SkColorTable;
+struct SkRect;
 class SkPaint;
 class SkPixelRef;
 class SkRegion;
-class SkFlattenableReadBuffer;
-class SkFlattenableWriteBuffer;
+class SkString;
 
 // This is an opaque class, not interpreted by skia
 class SkGpuTexture;
@@ -63,16 +63,21 @@
         kConfigCount
     };
 
-    /** Default construct creates a bitmap with zero width and height, and no pixels.
-        Its config is set to kNo_Config.
-    */
+    /**
+     *  Default construct creates a bitmap with zero width and height, and no pixels.
+     *  Its config is set to kNo_Config.
+     */
     SkBitmap();
-    /** Constructor initializes the new bitmap by copying the src bitmap. All fields are copied,
-        but ownership of the pixels remains with the src bitmap.
-    */
+
+    /**
+     *  Copy the settings from the src into this bitmap. If the src has pixels
+     *  allocated, they will be shared, not copied, so that the two bitmaps will
+     *  reference the same memory for the pixels. If a deep copy is needed,
+     *  where the new bitmap has its own separate copy of the pixels, use
+     *  deepCopyTo().
+     */
     SkBitmap(const SkBitmap& src);
-    /** Decrements our (shared) pixel ownership if needed.
-    */
+
     ~SkBitmap();
 
     /** Copies the src bitmap into this bitmap. Ownership of the src bitmap's pixels remains
@@ -88,10 +93,10 @@
     */
     bool empty() const { return 0 == fWidth || 0 == fHeight; }
 
-    /** Return true iff the bitmap has no pixels nor a pixelref. Note: this can
-        return true even if the dimensions of the bitmap are > 0 (see empty()).
+    /** Return true iff the bitmap has no pixelref. Note: this can return true even if the
+        dimensions of the bitmap are > 0 (see empty()).
     */
-    bool isNull() const { return NULL == fPixels && NULL == fPixelRef; }
+    bool isNull() const { return NULL == fPixelRef; }
 
     /** Return the config for the bitmap.
     */
@@ -140,7 +145,7 @@
 
     /** Return the number of bytes from the pointer returned by getPixels()
         to the end of the allocated space in the buffer. Required in
-        cases where extractBitmap has been called.
+        cases where extractSubset has been called.
     */
     size_t getSafeSize() const ;
 
@@ -165,7 +170,7 @@
 
     /** Marks this bitmap as immutable, meaning that the contents of its
         pixels will not change for the lifetime of the bitmap and of the
-        underlying pixelref. This state can be set, but it cannot be 
+        underlying pixelref. This state can be set, but it cannot be
         cleared once it is set. This state propagates to all other bitmaps
         that share the same pixelref.
     */
@@ -184,11 +189,11 @@
     */
     bool isVolatile() const;
 
-    /** Specify whether this bitmap is volatile. Bitmaps are not volatile by 
+    /** Specify whether this bitmap is volatile. Bitmaps are not volatile by
         default. Temporary bitmaps that are discarded after use should be
         marked as volatile. This provides a hint to the device that the bitmap
-        should not be cached. Providing this hint when appropriate can  
-        improve performance by avoiding unnecessary overhead and resource 
+        should not be cached. Providing this hint when appropriate can
+        improve performance by avoiding unnecessary overhead and resource
         consumption on the device.
     */
     void setIsVolatile(bool);
@@ -218,6 +223,30 @@
     static Sk64 ComputeSize64(Config, int width, int height);
     static size_t ComputeSize(Config, int width, int height);
 
+    /**
+     *  This will brute-force return true if all of the pixels in the bitmap
+     *  are opaque. If it fails to read the pixels, or encounters an error,
+     *  it will return false.
+     *
+     *  Since this can be an expensive operation, the bitmap stores a flag for
+     *  this (isOpaque, setIsOpaque). Only call this if you need to compute this
+     *  value from "unknown" pixels.
+     */
+    static bool ComputeIsOpaque(const SkBitmap&);
+
+    /**
+     *  Calls ComputeIsOpaque, and passes its result to setIsOpaque().
+     */
+    void computeAndSetOpaquePredicate() {
+        this->setIsOpaque(ComputeIsOpaque(*this));
+    }
+
+    /**
+     *  Return the bitmap's bounds [0, 0, width, height] as an SkRect
+     */
+    void getBounds(SkRect* bounds) const;
+    void getBounds(SkIRect* bounds) const;
+
     /** Set the bitmap's config and dimensions. If rowBytes is 0, then
         ComputeRowBytes() is called to compute the optimal value. This resets
         any pixel/colortable ownership, just like reset().
@@ -349,9 +378,9 @@
     SkColorTable* getColorTable() const { return fColorTable; }
 
     /** Returns a non-zero, unique value corresponding to the pixels in our
-        pixelref (or raw pixels set via setPixels). Each time the pixels are
-        changed (and notifyPixelsChanged is called), a different generation ID
-        will be returned.
+        pixelref. Each time the pixels are changed (and notifyPixelsChanged
+        is called), a different generation ID will be returned. Finally, if
+        their is no pixelRef then zero is returned.
     */
     uint32_t getGenerationID() const;
 
@@ -385,7 +414,8 @@
 
     /** Scroll (a subset of) the contents of this bitmap by dx/dy. If there are
         no pixels allocated (i.e. getPixels() returns null) the method will
-        still update the inval region (if present).
+        still update the inval region (if present). If the bitmap is immutable,
+        do nothing and return false.
 
         @param subset The subset of the bitmap to scroll/move. To scroll the
                       entire contents, specify [0, 0, width, height] or just
@@ -413,7 +443,7 @@
      *  does not have any pixels (or has not be locked with lockPixels()).
      */
     SkColor getColor(int x, int y) const;
-    
+
     /** Returns the address of the specified pixel. This performs a runtime
         check to know the size of the pixels, and will return the same answer
         as the corresponding size-specific method (e.g. getAddr16). Since the
@@ -548,6 +578,13 @@
     bool extractAlpha(SkBitmap* dst, const SkPaint* paint, Allocator* allocator,
                       SkIPoint* offset) const;
 
+    /** The following two functions provide the means to both flatten and
+        unflatten the bitmap AND its pixels into the provided buffer.
+        It is recommended that you do not call these functions directly,
+        but instead call the write/readBitmap functions on the respective
+        buffers as they can optimize the recording process and avoid recording
+        duplicate bitmaps and pixelRefs.
+     */
     void flatten(SkFlattenableWriteBuffer&) const;
     void unflatten(SkFlattenableReadBuffer&);
 
@@ -555,6 +592,8 @@
 
     class Allocator : public SkRefCnt {
     public:
+        SK_DECLARE_INST_COUNT(Allocator)
+
         /** Allocate the pixel memory for the bitmap, given its dimensions and
             config. Return true on success, where success means either setPixels
             or setPixelRef was called. The pixels need not be locked when this
@@ -563,6 +602,8 @@
             colortable should be left unchanged.
         */
         virtual bool allocPixelRef(SkBitmap*, SkColorTable*) = 0;
+    private:
+        typedef SkRefCnt INHERITED;
     };
 
     /** Subclass of Allocator that returns a pixelref that allocates its pixel
@@ -595,6 +636,8 @@
         int       fHeight;
     };
 
+    SkDEVCODE(void toString(SkString* str) const;)
+
 private:
     struct MipMap;
     mutable MipMap* fMipMap;
@@ -606,10 +649,6 @@
     // or a cache of the returned value from fPixelRef->lockPixels()
     mutable void*       fPixels;
     mutable SkColorTable* fColorTable;    // only meaningful for kIndex8
-    // When there is no pixel ref (setPixels was called) we still need a
-    // gen id for SkDevice implementations that may cache a copy of the
-    // pixels (e.g. as a gpu texture)
-    mutable int         fRawPixelGenerationID;
 
     enum Flags {
         kImageIsOpaque_Flag     = 0x01,
@@ -647,95 +686,6 @@
     static SkFixed ComputeMipLevel(SkFixed sx, SkFixed dy);
 };
 
-/** \class SkColorTable
-
-    SkColorTable holds an array SkPMColors (premultiplied 32-bit colors) used by
-    8-bit bitmaps, where the bitmap bytes are interpreted as indices into the colortable.
-*/
-class SkColorTable : public SkRefCnt {
-public:
-    /** Makes a deep copy of colors.
-     */
-    SkColorTable(const SkColorTable& src);
-    /** Preallocates the colortable to have 'count' colors, which
-     *  are initially set to 0.
-    */
-    explicit SkColorTable(int count);
-    explicit SkColorTable(SkFlattenableReadBuffer&);
-    SkColorTable(const SkPMColor colors[], int count);
-    virtual ~SkColorTable();
-
-    enum Flags {
-        kColorsAreOpaque_Flag   = 0x01  //!< if set, all of the colors in the table are opaque (alpha==0xFF)
-    };
-    /** Returns the flag bits for the color table. These can be changed with setFlags().
-    */
-    unsigned getFlags() const { return fFlags; }
-    /** Set the flags for the color table. See the Flags enum for possible values.
-    */
-    void    setFlags(unsigned flags);
-
-    bool isOpaque() const { return (fFlags & kColorsAreOpaque_Flag) != 0; }
-    void setIsOpaque(bool isOpaque);
-
-    /** Returns the number of colors in the table.
-    */
-    int count() const { return fCount; }
-
-    /** Returns the specified color from the table. In the debug build, this asserts that
-        the index is in range (0 <= index < count).
-    */
-    SkPMColor operator[](int index) const {
-        SkASSERT(fColors != NULL && (unsigned)index < fCount);
-        return fColors[index];
-    }
-
-    /** Specify the number of colors in the color table. This does not initialize the colors
-        to any value, just allocates memory for them. To initialize the values, either call
-        setColors(array, count), or follow setCount(count) with a call to
-        lockColors()/{set the values}/unlockColors(true).
-    */
-//    void    setColors(int count) { this->setColors(NULL, count); }
-//    void    setColors(const SkPMColor[], int count);
-
-    /** Return the array of colors for reading and/or writing. This must be
-        balanced by a call to unlockColors(changed?), telling the colortable if
-        the colors were changed during the lock.
-    */
-    SkPMColor* lockColors() {
-        SkDEBUGCODE(fColorLockCount += 1;)
-        return fColors;
-    }
-    /** Balancing call to lockColors(). If the colors have been changed, pass true.
-    */
-    void unlockColors(bool changed);
-
-    /** Similar to lockColors(), lock16BitCache() returns the array of
-        RGB16 colors that mirror the 32bit colors. However, this function
-        will return null if kColorsAreOpaque_Flag is not set.
-        Also, unlike lockColors(), the returned array here cannot be modified.
-    */
-    const uint16_t* lock16BitCache();
-    /** Balancing call to lock16BitCache().
-    */
-    void unlock16BitCache() {
-        SkASSERT(f16BitCacheLockCount > 0);
-        SkDEBUGCODE(f16BitCacheLockCount -= 1);
-    }
-
-    void flatten(SkFlattenableWriteBuffer&) const;
-
-private:
-    SkPMColor*  fColors;
-    uint16_t*   f16BitCache;
-    uint16_t    fCount;
-    uint8_t     fFlags;
-    SkDEBUGCODE(int fColorLockCount;)
-    SkDEBUGCODE(int f16BitCacheLockCount;)
-
-    void inval16BitCache();
-};
-
 class SkAutoLockPixels : public SkNoncopyable {
 public:
     SkAutoLockPixels(const SkBitmap& bm, bool doLock = true) : fBitmap(bm) {
diff --git a/include/core/SkBlitRow.h b/include/core/SkBlitRow.h
index fb62f5a..febc405 100644
--- a/include/core/SkBlitRow.h
+++ b/include/core/SkBlitRow.h
@@ -36,20 +36,13 @@
                          const SkPMColor* src,
                          int count, U8CPU alpha, int x, int y);
 
-   /** 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);
-
-    //! Public entry-point to return a blit function ptr
     static Proc Factory(unsigned flags, SkBitmap::Config);
 
     ///////////// D32 version
 
     enum Flags32 {
         kGlobalAlpha_Flag32     = 1 << 0,
-        kSrcPixelAlpha_Flag32   = 1 << 1,
+        kSrcPixelAlpha_Flag32   = 1 << 1
     };
 
     /** Function pointer that blends 32bit colors onto a 32bit destination.
@@ -64,6 +57,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.
@@ -71,8 +70,20 @@
     static void Color32(SkPMColor dst[], const SkPMColor src[],
                         int count, SkPMColor color);
 
+    //! Public entry-point to return a blit function ptr
     static ColorProc ColorProcFactory();
 
+    /** Function pointer that blends a single color onto a 32-bit rectangle.  */
+    typedef void (*ColorRectProc)(SkPMColor* dst, int width, int height,
+                                  size_t rowBytes, SkPMColor color);
+
+    /** Blend a single color into a rectangle of D32 pixels. */
+    static void ColorRect32(SkPMColor* dst, int width, int height,
+                            size_t rowBytes, SkPMColor color);
+
+    //! Public entry-point to return a blit function ptr
+    static ColorRectProc ColorRectProcFactory();
+
     /** 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
diff --git a/include/core/SkBlitter.h b/include/core/SkBlitter.h
deleted file mode 100644
index ce74a28..0000000
--- a/include/core/SkBlitter.h
+++ /dev/null
@@ -1,166 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkBlitter_DEFINED
-#define SkBlitter_DEFINED
-
-#include "SkBitmap.h"
-#include "SkMatrix.h"
-#include "SkPaint.h"
-#include "SkRefCnt.h"
-#include "SkRegion.h"
-#include "SkMask.h"
-
-/** SkBlitter and its subclasses are responsible for actually writing pixels
-    into memory. Besides efficiency, they handle clipping and antialiasing.
-*/
-class SkBlitter {
-public:
-    virtual ~SkBlitter();
-
-    /// Blit a horizontal run of one or more pixels.
-    virtual void blitH(int x, int y, int width);
-    /// 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[]);
-    /// Blit a vertical run of pixels with a constant alpha value.
-    virtual void blitV(int x, int y, int height, SkAlpha alpha);
-    /// Blit a solid rectangle one or more pixels wide.
-    virtual void blitRect(int x, int y, int width, int height);
-    /** 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);
-    /// Blit a pattern of pixels defined by a rectangle-clipped mask;
-    /// typically used for text.
-    virtual void blitMask(const SkMask&, const SkIRect& clip);
-
-    /** 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.
-    */
-    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
-
-    ///@name non-virtual helpers
-    void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
-    void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
-    void blitRegion(const SkRegion& clip);
-    ///@}
-
-    /** @name Factories
-        Return the correct blitter to use given the specified context.
-     */
-    static SkBlitter* Choose(const SkBitmap& device,
-                             const SkMatrix& matrix,
-                             const SkPaint& paint) {
-        return Choose(device, matrix, paint, NULL, 0);
-    }
-
-    static SkBlitter* Choose(const SkBitmap& device,
-                             const SkMatrix& matrix,
-                             const SkPaint& paint,
-                             void* storage, size_t storageSize);
-
-    static SkBlitter* ChooseSprite(const SkBitmap& device,
-                                   const SkPaint&,
-                                   const SkBitmap& src,
-                                   int left, int top,
-                                   void* storage, size_t storageSize);
-    ///@}
-
-private:
-};
-
-/** This blitter silently never draws anything.
-*/
-class SkNullBlitter : public SkBlitter {
-public:
-    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
-    virtual void blitAntiH(int x, int y, const SkAlpha[],
-                           const int16_t runs[]) SK_OVERRIDE;
-    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
-    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
-    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
-};
-
-/** Wraps another (real) blitter, and ensures that the real blitter is only
-    called with coordinates that have been clipped by the specified clipRect.
-    This means the caller need not perform the clipping ahead of time.
-*/
-class SkRectClipBlitter : public SkBlitter {
-public:
-    void init(SkBlitter* blitter, const SkIRect& clipRect) {
-        SkASSERT(!clipRect.isEmpty());
-        fBlitter = blitter;
-        fClipRect = clipRect;
-    }
-
-    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
-    virtual void blitAntiH(int x, int y, const SkAlpha[],
-                           const int16_t runs[]) SK_OVERRIDE;
-    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
-    virtual void blitAntiRect(int x, int y, int width, int height,
-                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
-    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
-    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
-
-private:
-    SkBlitter*  fBlitter;
-    SkIRect     fClipRect;
-};
-
-/** Wraps another (real) blitter, and ensures that the real blitter is only
-    called with coordinates that have been clipped by the specified clipRgn.
-    This means the caller need not perform the clipping ahead of time.
-*/
-class SkRgnClipBlitter : public SkBlitter {
-public:
-    void init(SkBlitter* blitter, const SkRegion* clipRgn) {
-        SkASSERT(clipRgn && !clipRgn->isEmpty());
-        fBlitter = blitter;
-        fRgn = clipRgn;
-    }
-
-    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
-    virtual void blitAntiH(int x, int y, const SkAlpha[],
-                           const int16_t runs[]) SK_OVERRIDE;
-    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
-    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
-    virtual void blitAntiRect(int x, int y, int width, int height,
-                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
-    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
-    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
-
-private:
-    SkBlitter*      fBlitter;
-    const SkRegion* fRgn;
-};
-
-/** Factory to set up the appropriate most-efficient wrapper blitter
-    to apply a clip. Returns a pointer to a member, so lifetime must
-    be managed carefully.
-*/
-class SkBlitterClipper {
-public:
-    SkBlitter*  apply(SkBlitter* blitter, const SkRegion* clip,
-                      const SkIRect* bounds = NULL);
-
-private:
-    SkNullBlitter       fNullBlitter;
-    SkRectClipBlitter   fRectBlitter;
-    SkRgnClipBlitter    fRgnBlitter;
-};
-
-#endif
diff --git a/include/core/SkBounder.h b/include/core/SkBounder.h
index 5bac358..7368d09 100644
--- a/include/core/SkBounder.h
+++ b/include/core/SkBounder.h
@@ -29,9 +29,11 @@
 */
 class SkBounder : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkBounder)
+
     SkBounder();
 
-    /* Call to perform a clip test before calling onIRect. 
+    /* Call to perform a clip test before calling onIRect.
        Returns the result from onIRect.
     */
     bool doIRect(const SkIRect&);
@@ -84,7 +86,8 @@
     friend class SkDrawIter;
     friend struct Draw1Glyph;
     friend class SkMaskFilter;
+
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
-
diff --git a/include/core/SkBuffer.h b/include/core/SkBuffer.h
deleted file mode 100644
index 7a63d3a..0000000
--- a/include/core/SkBuffer.h
+++ /dev/null
@@ -1,139 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkBuffer_DEFINED
-#define SkBuffer_DEFINED
-
-#include "SkScalar.h"
-
-/** \class SkRBuffer
-
-    Light weight class for reading data from a memory block.
-    The RBuffer is given the buffer to read from, with either a specified size
-    or no size (in which case no range checking is performed). It is iillegal
-    to attempt to read a value from an empty RBuffer (data == null). 
-*/
-class SkRBuffer : SkNoncopyable {
-public:
-    SkRBuffer() : fData(0), fPos(0), fStop(0) {}
-    /** Initialize RBuffer with a data pointer, but no specified length.
-        This signals the RBuffer to not perform range checks during reading.
-    */
-    SkRBuffer(const void* data) {
-        fData = (const char*)data;
-        fPos = (const char*)data;
-        fStop = 0;  // no bounds checking
-    }
-    /** Initialize RBuffer with a data point and length.
-    */
-    SkRBuffer(const void* data, size_t size) {
-        SkASSERT(data != 0 || size == 0);
-        fData = (const char*)data;
-        fPos = (const char*)data;
-        fStop = (const char*)data + size;
-    }
-    
-    /** Return the number of bytes that have been read from the beginning
-        of the data pointer.
-    */
-    size_t  pos() const { return fPos - fData; }
-    /** Return the total size of the data pointer. Only defined if the length was
-        specified in the constructor or in a call to reset().
-    */
-    size_t  size() const { return fStop - fData; }
-    /** Return true if the buffer has read to the end of the data pointer.
-        Only defined if the length was specified in the constructor or in a call
-        to reset(). Always returns true if the length was not specified.
-    */
-    bool    eof() const { return fPos >= fStop; }
-
-    /** Read the specified number of bytes from the data pointer. If buffer is not
-        null, copy those bytes into buffer.
-    */
-    void read(void* buffer, size_t size) {
-        if (size) {
-            this->readNoSizeCheck(buffer, size);
-        }
-    }
-
-    const void* skip(size_t size); // return start of skipped data
-    size_t  skipToAlign4();
-
-    void*       readPtr() { void* ptr; read(&ptr, sizeof(ptr)); return ptr; }
-    SkScalar    readScalar() { SkScalar x; read(&x, 4); return x; }
-    uint32_t    readU32() { uint32_t x; read(&x, 4); return x; }
-    int32_t     readS32() { int32_t x; read(&x, 4); return x; }
-    uint16_t    readU16() { uint16_t x; read(&x, 2); return x; }
-    int16_t     readS16() { int16_t x; read(&x, 2); return x; }
-    uint8_t     readU8() { uint8_t x; read(&x, 1); return x; }
-    bool        readBool() { return this->readU8() != 0; }
-
-protected:
-    void    readNoSizeCheck(void* buffer, size_t size);
-
-    const char* fData;
-    const char* fPos;
-    const char* fStop;
-};
-
-/** \class SkWBuffer
-
-    Light weight class for writing data to a memory block.
-    The WBuffer is given the buffer to write into, with either a specified size
-    or no size, in which case no range checking is performed. An empty WBuffer
-    is legal, in which case no data is ever written, but the relative pos()
-    is updated.
-*/
-class SkWBuffer : SkNoncopyable {
-public:
-    SkWBuffer() : fData(0), fPos(0), fStop(0) {}
-    SkWBuffer(void* data) { reset(data); }
-    SkWBuffer(void* data, size_t size) { reset(data, size); }
-
-    void reset(void* data) {
-        fData = (char*)data;
-        fPos = (char*)data;
-        fStop = 0;  // no bounds checking
-    }
-
-    void reset(void* data, size_t size) {
-        SkASSERT(data != 0 || size == 0);
-        fData = (char*)data;
-        fPos = (char*)data;
-        fStop = (char*)data + size;
-    }
-    
-    size_t  pos() const { return fPos - fData; }
-    void*   skip(size_t size); // return start of skipped data
-
-    void write(const void* buffer, size_t size) {
-        if (size) {
-            this->writeNoSizeCheck(buffer, size);
-        }
-    }
-
-    size_t  padToAlign4();
-
-    void    writePtr(const void* x) { this->writeNoSizeCheck(&x, sizeof(x)); }
-    void    writeScalar(SkScalar x) { this->writeNoSizeCheck(&x, 4); }
-    void    write32(int32_t x) { this->writeNoSizeCheck(&x, 4); }
-    void    write16(int16_t x) { this->writeNoSizeCheck(&x, 2); }
-    void    write8(int8_t x) { this->writeNoSizeCheck(&x, 1); }
-    void    writeBool(bool x) { this->write8(x); }
-
-protected:
-    void    writeNoSizeCheck(const void* buffer, size_t size);
-
-    char* fData;
-    char* fPos;
-    char* fStop;
-};
-
-#endif
-
diff --git a/include/core/SkCanvas.h b/include/core/SkCanvas.h
index 1b9f055..b908bd6 100644
--- a/include/core/SkCanvas.h
+++ b/include/core/SkCanvas.h
@@ -25,7 +25,17 @@
 class SkDevice;
 class SkDraw;
 class SkDrawFilter;
+class SkMetaData;
 class SkPicture;
+class SkRRect;
+class SkSurface_Base;
+
+#ifdef SK_BUILD_FOR_ANDROID
+namespace WebCore {
+  class RasterRenderer;
+  class GaneshRenderer;
+}
+#endif
 
 /** \class SkCanvas
 
@@ -44,6 +54,8 @@
 */
 class SK_API SkCanvas : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkCanvas)
+
     SkCanvas();
 
     /** Construct a canvas with the specified device to draw into.
@@ -59,6 +71,8 @@
     explicit SkCanvas(const SkBitmap& bitmap);
     virtual ~SkCanvas();
 
+    SkMetaData& getMetaData();
+
     ///////////////////////////////////////////////////////////////////////////
 
     /**
@@ -79,26 +93,20 @@
     */
     SkDevice* getDevice() const;
 
-    /** Specify a device for this canvas to draw into. If it is not null, its
-        reference count is incremented. If the canvas was already holding a
-        device, its reference count is decremented. The new device is returned.
-    */
-    virtual SkDevice* setDevice(SkDevice* device);
-
     /**
      *  saveLayer() can create another device (which is later drawn onto
      *  the previous device). getTopDevice() returns the top-most device current
      *  installed. Note that this can change on other calls like save/restore,
      *  so do not access this device after subsequent canvas calls.
      *  The reference count of the device is not changed.
+     *
+     * @param updateMatrixClip If this is true, then before the device is
+     *        returned, we ensure that its has been notified about the current
+     *        matrix and clip. Note: this happens automatically when the device
+     *        is drawn to, but is optional here, as there is a small perf hit
+     *        sometimes.
      */
-    SkDevice* getTopDevice() const;
-
-    /**
-     *  Create a new raster device and make it current. This also returns
-     *  the new device.
-     */
-    SkDevice* setBitmapDevice(const SkBitmap& bitmap);
+    SkDevice* getTopDevice(bool updateMatrixClip = false) const;
 
     /**
      *  Shortcut for getDevice()->createCompatibleDevice(...).
@@ -142,7 +150,7 @@
          * low byte to high byte: R, G, B, A.
          */
         kRGBA_Premul_Config8888,
-        kRGBA_Unpremul_Config8888,
+        kRGBA_Unpremul_Config8888
     };
 
     /**
@@ -339,24 +347,47 @@
     */
     void resetMatrix();
 
-    /** Modify the current clip with the specified rectangle.
-        @param rect The rect to intersect with the current clip
-        @param op The region op to apply to the current clip
-        @return true if the canvas' clip is non-empty
-    */
+    /**
+     *  Modify the current clip with the specified rectangle.
+     *  @param rect The rect to combine with the current clip
+     *  @param op The region op to apply to the current clip
+     *  @param doAntiAlias true if the clip should be antialiased
+     *  @return true if the canvas' clip is non-empty
+     */
     virtual bool clipRect(const SkRect& rect,
                           SkRegion::Op op = SkRegion::kIntersect_Op,
                           bool doAntiAlias = false);
 
-    /** Modify the current clip with the specified path.
-        @param path The path to apply to the current clip
-        @param op The region op to apply to the current clip
-        @return true if the canvas' new clip is non-empty
-    */
+    /**
+     *  Modify the current clip with the specified SkRRect.
+     *  @param rrect The rrect to combine with the current clip
+     *  @param op The region op to apply to the current clip
+     *  @param doAntiAlias true if the clip should be antialiased
+     *  @return true if the canvas' clip is non-empty
+     */
+    virtual bool clipRRect(const SkRRect& rrect,
+                           SkRegion::Op op = SkRegion::kIntersect_Op,
+                           bool doAntiAlias = false);
+
+    /**
+     *  Modify the current clip with the specified path.
+     *  @param path The path to combine with the current clip
+     *  @param op The region op to apply to the current clip
+     *  @param doAntiAlias true if the clip should be antialiased
+     *  @return true if the canvas' new clip is non-empty
+     */
     virtual bool clipPath(const SkPath& path,
                           SkRegion::Op op = SkRegion::kIntersect_Op,
                           bool doAntiAlias = false);
 
+    /** EXPERIMENTAL -- only used for testing
+        Set to false to force clips to be hard, even if doAntiAlias=true is
+        passed to clipRect or clipPath.
+     */
+    void setAllowSoftClip(bool allow) {
+        fAllowSoftClip = allow;
+    }
+
     /** Modify the current clip with the specified region. Note that unlike
         clipRect() and clipPath() which transform their arguments by the current
         matrix, clipRegion() assumes its argument is already in device
@@ -378,33 +409,15 @@
         return this->clipRegion(deviceRgn, SkRegion::kReplace_Op);
     }
 
-    /** Enum describing how to treat edges when performing quick-reject tests
-        of a geometry against the current clip. Treating them as antialiased
-        (kAA_EdgeType) will take into account the extra pixels that may be drawn
-        if the edge does not lie exactly on a device pixel boundary (after being
-        transformed by the current matrix).
-    */
-    enum EdgeType {
-        /** Treat the edges as B&W (not antialiased) for the purposes of testing
-            against the current clip
-        */
-        kBW_EdgeType,
-        /** Treat the edges as antialiased for the purposes of testing
-            against the current clip
-        */
-        kAA_EdgeType
-    };
-
     /** Return true if the specified rectangle, after being transformed by the
         current matrix, would lie completely outside of the current clip. Call
         this to check if an area you intend to draw into is clipped out (and
         therefore you can skip making the draw calls).
         @param rect the rect to compare with the current clip
-        @param et  specifies how to treat the edges (see EdgeType)
         @return true if the rect (transformed by the canvas' matrix) does not
                      intersect with the canvas' clip
     */
-    bool quickReject(const SkRect& rect, EdgeType et) const;
+    bool quickReject(const SkRect& rect) const;
 
     /** Return true if the specified path, after being transformed by the
         current matrix, would lie completely outside of the current clip. Call
@@ -413,11 +426,10 @@
         return false even if the path itself might not intersect the clip
         (i.e. the bounds of the path intersects, but the path does not).
         @param path The path to compare with the current clip
-        @param et  specifies how to treat the edges (see EdgeType)
         @return true if the path (transformed by the canvas' matrix) does not
                      intersect with the canvas' clip
     */
-    bool quickReject(const SkPath& path, EdgeType et) const;
+    bool quickReject(const SkPath& path) const;
 
     /** Return true if the horizontal band specified by top and bottom is
         completely clipped out. This is a conservative calculation, meaning
@@ -429,9 +441,9 @@
         @return true if the horizontal band is completely clipped out (i.e. does
                      not intersect the current clip)
     */
-    bool quickRejectY(SkScalar top, SkScalar bottom, EdgeType et) const {
+    bool quickRejectY(SkScalar top, SkScalar bottom) const {
         SkASSERT(SkScalarToCompareType(top) <= SkScalarToCompareType(bottom));
-        const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
+        const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
         // In the case where the clip is empty and we are provided with a
         // negative top and positive bottom parameter then this test will return
         // false even though it will be clipped. We have chosen to exclude that
@@ -445,7 +457,7 @@
         in a way similar to quickReject, in that it tells you that drawing
         outside of these bounds will be clipped out.
     */
-    bool getClipBounds(SkRect* bounds, EdgeType et = kAA_EdgeType) const;
+    bool getClipBounds(SkRect* bounds) const;
 
     /** Return the bounds of the current clip, in device coordinates; returns
         true if non-empty. Maybe faster than getting the clip explicitly and
@@ -588,7 +600,16 @@
         @param oval     The rectangle bounds of the oval to be drawn
         @param paint    The paint used to draw the oval
     */
-    void drawOval(const SkRect& oval, const SkPaint&);
+    virtual void drawOval(const SkRect& oval, const SkPaint&);
+
+    /**
+     *  Draw the specified RRect using the specified paint The rrect will be filled or stroked
+     *  based on the Style in the paint.
+     *
+     *  @param rrect    The round-rect to draw
+     *  @param paint    The paint used to draw the round-rect
+     */
+    virtual void drawRRect(const SkRRect& rrect, const SkPaint& paint);
 
     /** Draw the specified circle using the specified paint. If radius is <= 0,
         then nothing will be drawn. The circle will be filled
@@ -654,8 +675,25 @@
                         image will be drawn
         @param paint    The paint used to draw the bitmap, or NULL
     */
-    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
-                                const SkRect& dst, const SkPaint* paint = NULL);
+    virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                                      const SkRect& dst,
+                                      const SkPaint* paint);
+
+    void drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst,
+                        const SkPaint* paint) {
+        this->drawBitmapRectToRect(bitmap, NULL, dst, paint);
+    }
+
+    void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* isrc,
+                        const SkRect& dst, const SkPaint* paint = NULL) {
+        SkRect realSrcStorage;
+        SkRect* realSrcPtr = NULL;
+        if (isrc) {
+            realSrcStorage.set(*isrc);
+            realSrcPtr = &realSrcStorage;
+        }
+        this->drawBitmapRectToRect(bitmap, realSrcPtr, dst, paint);
+    }
 
     virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
                                   const SkPaint* paint = NULL);
@@ -772,10 +810,7 @@
 
     /** Draw the picture into this canvas. This method effective brackets the
         playback of the picture's draw calls with save/restore, so the state
-        of this canvas will be unchanged after this call. This contrasts with
-        the more immediate method SkPicture::draw(), which does not bracket
-        the canvas with save/restore, thus the canvas may be left in a changed
-        state after the call.
+        of this canvas will be unchanged after this call.
         @param picture The recorded drawing commands to playback into this
                        canvas.
     */
@@ -799,7 +834,7 @@
         @param xmode Used if both texs and colors are present. In this
                     case the colors are combined with the texture using mode,
                     before being drawn using the paint. If mode is null, then
-                    kMultiply_Mode is used.
+                    kModulate_Mode is used.
         @param indices If not null, array of indices to reference into the
                     vertex (texs, colors) array.
         @param indexCount number of entries in the indices array (if not null)
@@ -873,26 +908,35 @@
     ClipType getClipType() const;
 
     /** Return the current device clip (concatenation of all clip calls).
-        This does not account for the translate in any of the devices.
-        @return the current device clip (concatenation of all clip calls).
-    */
+     *  This does not account for the translate in any of the devices.
+     *  @return the current device clip (concatenation of all clip calls).
+     *
+     *  DEPRECATED -- call getClipDeviceBounds() instead.
+     */
     const SkRegion& getTotalClip() const;
 
-    /**
-     *  Return true if the current clip is non-empty.
-     *
-     *  If bounds is not NULL, set it to the bounds of the current clip
-     *  in global coordinates.
+    /** Return the clip stack. The clip stack stores all the individual
+     *  clips organized by the save/restore frame in which they were
+     *  added.
+     *  @return the current clip stack ("list" of individual clip elements)
      */
-    bool getTotalClipBounds(SkIRect* bounds) const;
+    const SkClipStack* getClipStack() const {
+        return &fClipStack;
+    }
+
+    class ClipVisitor {
+    public:
+        virtual ~ClipVisitor();
+        virtual void clipRect(const SkRect&, SkRegion::Op, bool antialias) = 0;
+        virtual void clipPath(const SkPath&, SkRegion::Op, bool antialias) = 0;
+    };
 
     /**
-     *  Return the current clipstack. This mirrors the result in getTotalClip()
-     *  but is represented as a stack of geometric clips + region-ops.
+     *  Replays the clip operations, back to front, that have been applied to
+     *  the canvas, calling the appropriate method on the visitor for each
+     *  clip. All clips have already been transformed into device space.
      */
-    const SkClipStack& getTotalClipStack() const;
-
-    void setExternalMatrix(const SkMatrix* = NULL);
+    void replayClips(ClipVisitor*) const;
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -942,13 +986,33 @@
     virtual SkCanvas* canvasForDrawIter();
 
     // all of the drawBitmap variants call this guy
-    virtual void commonDrawBitmap(const SkBitmap&, const SkIRect*,
-                                  const SkMatrix&, const SkPaint& paint);
+    void commonDrawBitmap(const SkBitmap&, const SkIRect*, const SkMatrix&,
+                          const SkPaint& paint);
 
     // Clip rectangle bounds. Called internally by saveLayer.
     // returns false if the entire rectangle is entirely clipped out
     bool clipRectBounds(const SkRect* bounds, SaveFlags flags,
-                         SkIRect* intersection);
+                        SkIRect* intersection);
+
+    // notify our surface (if we have one) that we are about to draw, so it
+    // can perform copy-on-write or invalidate any cached images
+    void predrawNotify();
+
+    /** DEPRECATED -- use constructor(device)
+
+     Marked as 'protected' to avoid new clients using this before we can
+     completely remove it.
+
+     Specify a device for this canvas to draw into. If it is not null, its
+     reference count is incremented. If the canvas was already holding a
+     device, its reference count is decremented. The new device is returned.
+     */
+    virtual SkDevice* setDevice(SkDevice* device);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    friend class WebCore::GaneshRenderer;
+    friend class WebCore::RasterRenderer;
+#endif
 
 private:
     class MCRec;
@@ -961,16 +1025,22 @@
     uint32_t    fMCRecStorage[32];
 
     SkBounder*  fBounder;
-    SkDevice*   fLastDeviceToGainFocus;
-    int         fLayerCount;    // number of successful saveLayer calls
+    int         fSaveLayerCount;    // number of successful saveLayer calls
 
-    void prepareForDeviceDraw(SkDevice*, const SkMatrix&, const SkRegion&,
-                              const SkClipStack& clipStack);
+    SkMetaData* fMetaData;
+
+    SkSurface_Base*  fSurfaceBase;
+    SkSurface_Base* getSurfaceBase() const { return fSurfaceBase; }
+    void setSurfaceBase(SkSurface_Base* sb) {
+        fSurfaceBase = sb;
+    }
+    friend class SkSurface_Base;
 
     bool fDeviceCMDirty;            // cleared by updateDeviceCMCache()
     void updateDeviceCMCache();
 
     friend class SkDrawIter;    // needs setupDrawForLayerDevice()
+    friend class AutoDrawLooper;
 
     SkDevice* createLayerDevice(SkBitmap::Config, int width, int height,
                                 bool isOpaque);
@@ -981,14 +1051,15 @@
     // canvas apis, without confusing subclasses (like SkPictureRecording)
     void internalDrawBitmap(const SkBitmap&, const SkIRect*, const SkMatrix& m,
                                   const SkPaint* paint);
-    void internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+    void internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
                                 const SkRect& dst, const SkPaint* paint);
     void internalDrawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
                                 const SkRect& dst, const SkPaint* paint);
     void internalDrawPaint(const SkPaint& paint);
+    int internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags, bool justForImageFilter);
+    void internalDrawDevice(SkDevice*, int x, int y, const SkPaint*);
 
-
-    void drawDevice(SkDevice*, int x, int y, const SkPaint*);
     // shared by save() and saveLayer()
     int internalSave(SaveFlags flags);
     void internalRestore();
@@ -1003,35 +1074,16 @@
      */
     mutable SkRectCompareType fLocalBoundsCompareType;
     mutable bool              fLocalBoundsCompareTypeDirty;
+    bool fAllowSoftClip;
 
-    mutable SkRectCompareType fLocalBoundsCompareTypeBW;
-    mutable bool              fLocalBoundsCompareTypeDirtyBW;
-
-    /* Get the local clip bounds with an anti-aliased edge.
-     */
     const SkRectCompareType& getLocalClipBoundsCompareType() const {
-        return getLocalClipBoundsCompareType(kAA_EdgeType);
-    }
-
-    const SkRectCompareType& getLocalClipBoundsCompareType(EdgeType et) const {
-        if (et == kAA_EdgeType) {
-            if (fLocalBoundsCompareTypeDirty) {
-                this->computeLocalClipBoundsCompareType(et);
-                fLocalBoundsCompareTypeDirty = false;
-            }
-            return fLocalBoundsCompareType;
-        } else {
-            if (fLocalBoundsCompareTypeDirtyBW) {
-                this->computeLocalClipBoundsCompareType(et);
-                fLocalBoundsCompareTypeDirtyBW = false;
-            }
-            return fLocalBoundsCompareTypeBW;
+        if (fLocalBoundsCompareTypeDirty) {
+            this->computeLocalClipBoundsCompareType();
+            fLocalBoundsCompareTypeDirty = false;
         }
+        return fLocalBoundsCompareType;
     }
-    void computeLocalClipBoundsCompareType(EdgeType et) const;
-
-    SkMatrix    fExternalMatrix, fExternalInverse;
-    bool        fUseExternalMatrix;
+    void computeLocalClipBoundsCompareType() const;
 
     class AutoValidateClip : ::SkNoncopyable {
     public:
@@ -1049,6 +1101,8 @@
 #else
     void validateClip() const {}
 #endif
+
+    typedef SkRefCnt INHERITED;
 };
 
 /** Stack helper class to automatically call restoreToCount() on the canvas
@@ -1065,7 +1119,20 @@
         }
     }
     ~SkAutoCanvasRestore() {
-        fCanvas->restoreToCount(fSaveCount);
+        if (fCanvas) {
+            fCanvas->restoreToCount(fSaveCount);
+        }
+    }
+
+    /**
+     *  Perform the restore now, instead of waiting for the destructor. Will
+     *  only do this once.
+     */
+    void restore() {
+        if (fCanvas) {
+            fCanvas->restoreToCount(fSaveCount);
+            fCanvas = NULL;
+        }
     }
 
 private:
diff --git a/include/core/SkChecksum.h b/include/core/SkChecksum.h
new file mode 100644
index 0000000..9cb45c3
--- /dev/null
+++ b/include/core/SkChecksum.h
@@ -0,0 +1,90 @@
+/*
+ * 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 SkChecksum_DEFINED
+#define SkChecksum_DEFINED
+
+#include "SkTypes.h"
+
+class SkChecksum : SkNoncopyable {
+private:
+    /*
+     *  Our Rotate and Mash helpers are meant to automatically do the right
+     *  thing depending if sizeof(uintptr_t) is 4 or 8.
+     */
+    enum {
+        ROTR = 17,
+        ROTL = sizeof(uintptr_t) * 8 - ROTR,
+        HALFBITS = sizeof(uintptr_t) * 4
+    };
+
+    static inline uintptr_t Mash(uintptr_t total, uintptr_t value) {
+        return ((total >> ROTR) | (total << ROTL)) ^ value;
+    }
+
+public:
+    /**
+     *  Compute a 32-bit checksum for a given data block
+     *
+     *  WARNING: this algorithm is tuned for efficiency, not backward/forward
+     *  compatibility.  It may change at any time, so a checksum generated with
+     *  one version of the Skia code may not match a checksum generated with
+     *  a different version of the Skia code.
+     *
+     *  @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.
+     *  @return checksum result
+     */
+    static uint32_t Compute(const uint32_t* data, size_t size) {
+        SkASSERT(SkIsAlign4(size));
+
+        /*
+         *  We want to let the compiler use 32bit or 64bit addressing and math
+         *  so we use uintptr_t as our magic type. This makes the code a little
+         *  more obscure (we can't hard-code 32 or 64 anywhere, but have to use
+         *  sizeof()).
+         */
+        uintptr_t result = 0;
+        const uintptr_t* ptr = reinterpret_cast<const uintptr_t*>(data);
+
+        /*
+         *  count the number of quad element chunks. This takes into account
+         *  if we're on a 32bit or 64bit arch, since we use sizeof(uintptr_t)
+         *  to compute how much to shift-down the size.
+         */
+        size_t n4 = size / (sizeof(uintptr_t) << 2);
+        for (size_t i = 0; i < n4; ++i) {
+            result = Mash(result, *ptr++);
+            result = Mash(result, *ptr++);
+            result = Mash(result, *ptr++);
+            result = Mash(result, *ptr++);
+        }
+        size &= ((sizeof(uintptr_t) << 2) - 1);
+
+        data = reinterpret_cast<const uint32_t*>(ptr);
+        const uint32_t* stop = data + (size >> 2);
+        while (data < stop) {
+            result = Mash(result, *data++);
+        }
+
+        /*
+         *  smash us down to 32bits if we were 64. Note that when uintptr_t is
+         *  32bits, this code-path should go away, but I still got a warning
+         *  when I wrote
+         *      result ^= result >> 32;
+         *  since >>32 is undefined for 32bit ints, hence the wacky HALFBITS
+         *  define.
+         */
+        if (8 == sizeof(result)) {
+            result ^= result >> HALFBITS;
+        }
+        return static_cast<uint32_t>(result);
+    }
+};
+
+#endif
diff --git a/include/core/SkChunkAlloc.h b/include/core/SkChunkAlloc.h
index 28d3c7e..1b52a35 100644
--- a/include/core/SkChunkAlloc.h
+++ b/include/core/SkChunkAlloc.h
@@ -17,28 +17,22 @@
     SkChunkAlloc(size_t minSize);
     ~SkChunkAlloc();
 
-    /** Free up all allocated blocks. This invalidates all returned
-        pointers.
-    */
+    /**
+     *  Free up all allocated blocks. This invalidates all returned
+     *  pointers.
+     */
     void reset();
 
-    /** Reuse all allocated blocks. This invalidates all returned
-        pointers (like reset) but doesn't necessarily free up all
-        of the privately allocated blocks. This is more efficient
-        if you plan to reuse the allocator multiple times.
-    */
-    void reuse();
-
     enum AllocFailType {
         kReturnNil_AllocFailType,
         kThrow_AllocFailType
     };
-    
+
     void* alloc(size_t bytes, AllocFailType);
     void* allocThrow(size_t bytes) {
         return this->alloc(bytes, kThrow_AllocFailType);
     }
-    
+
     /** Call this to unalloc the most-recently allocated ptr by alloc(). On
         success, the number of bytes freed is returned, or 0 if the block could
         not be unallocated. This is a hint to the underlying allocator that
@@ -46,8 +40,9 @@
         to ignore this call (and return 0).
      */
     size_t unalloc(void* ptr);
-    
+
     size_t totalCapacity() const { return fTotalCapacity; }
+    int blockCount() const { return fBlockCount; }
 
     /**
      *  Returns true if the specified address is within one of the chunks, and
@@ -58,10 +53,12 @@
 
 private:
     struct Block;
+
     Block*  fBlock;
     size_t  fMinSize;
-    Block*  fPool;
+    size_t  fChunkSize;
     size_t  fTotalCapacity;
+    int     fBlockCount;
 
     Block* newBlock(size_t bytes, AllocFailType ftype);
 };
diff --git a/include/core/SkClampRange.h b/include/core/SkClampRange.h
deleted file mode 100644
index 68a27e9..0000000
--- a/include/core/SkClampRange.h
+++ /dev/null
@@ -1,39 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkClampRange_DEFINED
-#define SkClampRange_DEFINED
-
-#include "SkFixed.h"
-
-/**
- *  Iteration fixed fx by dx, clamping as you go to [0..0xFFFF], this class
- *  computes the (up to) 3 spans there are:
- *
- *  range0: use constant value V0
- *  range1: iterate as usual fx += dx
- *  range2: use constant value V1
- */
-struct SkClampRange {
-    int fCount0;    // count for fV0
-    int fCount1;    // count for interpolating (fV0...fV1)
-    int fCount2;    // count for fV1
-    SkFixed fFx1;   // initial fx value for the fCount1 range.
-                    // only valid if fCount1 > 0
-    int fV0, fV1;
-    bool fOverflowed;   // true if we had to clamp due to numerical overflow
-
-    void init(SkFixed fx, SkFixed dx, int count, int v0, int v1);
-
-private:
-    void initFor1(SkFixed fx);
-};
-
-#endif
-
diff --git a/include/core/SkClipStack.h b/include/core/SkClipStack.h
index fc96f03..256f603 100644
--- a/include/core/SkClipStack.h
+++ b/include/core/SkClipStack.h
@@ -9,16 +9,258 @@
 #define SkClipStack_DEFINED
 
 #include "SkDeque.h"
+#include "SkPath.h"
+#include "SkRect.h"
 #include "SkRegion.h"
+#include "SkTDArray.h"
 
-struct SkRect;
-class SkPath;
 
+// Because a single save/restore state can have multiple clips, this class
+// stores the stack depth (fSaveCount) and clips (fDeque) separately.
+// Each clip in fDeque stores the stack state to which it belongs
+// (i.e., the fSaveCount in force when it was added). Restores are thus
+// implemented by removing clips from fDeque that have an fSaveCount larger
+// then the freshly decremented count.
 class SK_API SkClipStack {
 public:
+    enum BoundsType {
+        // The bounding box contains all the pixels that can be written to
+        kNormal_BoundsType,
+        // The bounding box contains all the pixels that cannot be written to.
+        // The real bound extends out to infinity and all the pixels outside
+        // of the bound can be written to. Note that some of the pixels inside
+        // the bound may also be writeable but all pixels that cannot be
+        // written to are guaranteed to be inside.
+        kInsideOut_BoundsType
+    };
+
+    class Element {
+    public:
+        enum Type {
+            //!< This element makes the clip empty (regardless of previous elements).
+            kEmpty_Type,
+            //!< This element combines a rect with the current clip using a set operation
+            kRect_Type,
+            //!< This element combines a path with the current clip using a set operation
+            kPath_Type,
+        };
+
+        Element() {
+            this->initCommon(0, SkRegion::kReplace_Op, false);
+            this->setEmpty();
+        }
+
+        Element(const SkRect& rect, SkRegion::Op op, bool doAA) {
+            this->initRect(0, rect, op, doAA);
+        }
+
+        Element(const SkPath& path, SkRegion::Op op, bool doAA) {
+            this->initPath(0, path, op, doAA);
+        }
+
+        bool operator== (const Element& element) const {
+            if (this == &element) {
+                return true;
+            }
+            if (fOp != element.fOp ||
+                fType != element.fType ||
+                fDoAA != element.fDoAA ||
+                fSaveCount != element.fSaveCount) {
+                return false;
+            }
+            switch (fType) {
+                case kPath_Type:
+                    return fPath == element.fPath;
+                case kRect_Type:
+                    return fRect == element.fRect;
+                case kEmpty_Type:
+                    return true;
+                default:
+                    SkDEBUGFAIL("Unexpected type.");
+                    return false;
+            }
+        }
+        bool operator!= (const Element& element) const { return !(*this == element); }
+
+        //!< Call to get the type of the clip element.
+        Type getType() const { return fType; }
+
+        //!< Call if getType() is kPath to get the path.
+        const SkPath& getPath() const { return fPath; }
+
+        //!< Call if getType() is kRect to get the rect.
+        const SkRect& getRect() const { return fRect; }
+
+        //!< Call if getType() is not kEmpty to get the set operation used to combine this element.
+        SkRegion::Op getOp() const { return fOp; }
+
+        /** If getType() is not kEmpty this indicates whether the clip shape should be anti-aliased
+            when it is rasterized. */
+        bool isAA() const { return fDoAA; }
+
+        //!< Inverts the fill of the clip shape. Note that a kEmpty element remains kEmpty.
+        void invertShapeFillType();
+
+        //!< Sets the set operation represented by the element.
+        void setOp(SkRegion::Op op) { fOp = op; }
+
+        /** The GenID can be used by clip stack clients to cache representations of the clip. The
+            ID corresponds to the set of clip elements up to and including this element within the
+            stack not to the element itself. That is the same clip path in different stacks will
+            have a different ID since the elements produce different clip result in the context of
+            their stacks. */
+        int32_t getGenID() const { return fGenID; }
+
+        /**
+         * Gets the bounds of the clip element, either the rect or path bounds. (Whether the shape
+         * is inverse filled is not considered.)
+         */
+        const SkRect& getBounds() const {
+            static const SkRect kEmpty = { 0, 0, 0, 0 };
+            switch (fType) {
+                case kRect_Type:
+                    return fRect;
+                case kPath_Type:
+                    return fPath.getBounds();
+                case kEmpty_Type:
+                    return kEmpty;
+                default:
+                    SkDEBUGFAIL("Unexpected type.");
+                    return kEmpty;
+            }
+        }
+
+        /**
+         * Conservatively checks whether the clip shape contains the rect param. (Whether the shape
+         * is inverse filled is not considered.)
+         */
+        bool contains(const SkRect& rect) const {
+            switch (fType) {
+                case kRect_Type:
+                    return fRect.contains(rect);
+                case kPath_Type:
+                    return fPath.conservativelyContainsRect(rect);
+                case kEmpty_Type:
+                    return false;
+                default:
+                    SkDEBUGFAIL("Unexpected type.");
+                    return false;
+            }
+        }
+
+        /**
+         * Is the clip shape inverse filled.
+         */
+        bool isInverseFilled() const {
+            return kPath_Type == fType && fPath.isInverseFillType();
+        }
+
+    private:
+        friend class SkClipStack;
+
+        SkPath          fPath;
+        SkRect          fRect;
+        int             fSaveCount; // save count of stack when this element was added.
+        SkRegion::Op    fOp;
+        Type            fType;
+        bool            fDoAA;
+
+        /* fFiniteBoundType and fFiniteBound are used to incrementally update the clip stack's
+           bound. When fFiniteBoundType is kNormal_BoundsType, fFiniteBound represents the
+           conservative bounding box of the pixels that aren't clipped (i.e., any pixels that can be
+           drawn to are inside the bound). When fFiniteBoundType is kInsideOut_BoundsType (which
+           occurs when a clip is inverse filled), fFiniteBound represents the conservative bounding
+           box of the pixels that _are_ clipped (i.e., any pixels that cannot be drawn to are inside
+           the bound). When fFiniteBoundType is kInsideOut_BoundsType the actual bound is the
+           infinite plane. This behavior of fFiniteBoundType and fFiniteBound is required so that we
+           can capture the cancelling out of the extensions to infinity when two inverse filled
+           clips are Booleaned together. */
+        SkClipStack::BoundsType fFiniteBoundType;
+        SkRect                  fFiniteBound;
+
+        // When element is applied to the previous elements in the stack is the result known to be
+        // equivalent to a single rect intersection? IIOW, is the clip effectively a rectangle.
+        bool                    fIsIntersectionOfRects;
+
+        int                     fGenID;
+
+        Element(int saveCount) {
+            this->initCommon(saveCount, SkRegion::kReplace_Op, false);
+            this->setEmpty();
+        }
+
+        Element(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
+            this->initRect(saveCount, rect, op, doAA);
+        }
+
+        Element(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) {
+            this->initPath(saveCount, path, op, doAA);
+        }
+
+        void initCommon(int saveCount, SkRegion::Op op, bool doAA) {
+            fSaveCount = saveCount;
+            fOp = op;
+            fDoAA = doAA;
+            // A default of inside-out and empty bounds means the bounds are effectively void as it
+            // indicates that nothing is known to be outside the clip.
+            fFiniteBoundType = kInsideOut_BoundsType;
+            fFiniteBound.setEmpty();
+            fIsIntersectionOfRects = false;
+            fGenID = kInvalidGenID;
+        }
+
+        void initRect(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) {
+            fRect = rect;
+            fType = kRect_Type;
+            this->initCommon(saveCount, op, doAA);
+        }
+
+        void initPath(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) {
+            fPath = path;
+            fType = kPath_Type;
+            this->initCommon(saveCount, op, doAA);
+        }
+
+        void setEmpty() {
+            fType = kEmpty_Type;
+            fFiniteBound.setEmpty();
+            fFiniteBoundType = kNormal_BoundsType;
+            fIsIntersectionOfRects = false;
+            fRect.setEmpty();
+            fPath.reset();
+            fGenID = kEmptyGenID;
+        }
+
+        // All Element methods below are only used within SkClipStack.cpp
+        inline void checkEmpty() const;
+        inline bool canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const;
+        /* This method checks to see if two rect clips can be safely merged into one. The issue here
+          is that to be strictly correct all the edges of the resulting rect must have the same
+          anti-aliasing. */
+        bool rectRectIntersectAllowed(const SkRect& newR, bool newAA) const;
+        /** Determines possible finite bounds for the Element given the previous element of the
+            stack */
+        void updateBoundAndGenID(const Element* prior);
+        // The different combination of fill & inverse fill when combining bounding boxes
+        enum FillCombo {
+            kPrev_Cur_FillCombo,
+            kPrev_InvCur_FillCombo,
+            kInvPrev_Cur_FillCombo,
+            kInvPrev_InvCur_FillCombo
+        };
+        // per-set operation functions used by updateBoundAndGenID().
+        inline void combineBoundsDiff(FillCombo combination, const SkRect& prevFinite);
+        inline void combineBoundsXOR(int combination, const SkRect& prevFinite);
+        inline void combineBoundsUnion(int combination, const SkRect& prevFinite);
+        inline void combineBoundsIntersection(int combination, const SkRect& prevFinite);
+        inline void combineBoundsRevDiff(int combination, const SkRect& prevFinite);
+    };
+
     SkClipStack();
     SkClipStack(const SkClipStack& b);
-    ~SkClipStack() {}
+    explicit SkClipStack(const SkRect& r);
+    explicit SkClipStack(const SkIRect& r);
+    ~SkClipStack();
 
     SkClipStack& operator=(const SkClipStack& b);
     bool operator==(const SkClipStack& b) const;
@@ -30,62 +272,195 @@
     void save();
     void restore();
 
-    void clipDevRect(const SkIRect& ir,
-                     SkRegion::Op op = SkRegion::kIntersect_Op) {
+    /**
+     * getBounds places the current finite bound in its first parameter. In its
+     * second, it indicates which kind of bound is being returned. If
+     * 'canvFiniteBound' is a normal bounding box then it encloses all writeable
+     * pixels. If 'canvFiniteBound' is an inside out bounding box then it
+     * encloses all the un-writeable pixels and the true/normal bound is the
+     * infinite plane. isIntersectionOfRects is an optional parameter
+     * that is true if 'canvFiniteBound' resulted from an intersection of rects.
+     */
+    void getBounds(SkRect* canvFiniteBound,
+                   BoundsType* boundType,
+                   bool* isIntersectionOfRects = NULL) const;
+
+    /**
+     * Takes an input rect in device space and conservatively clips it to the
+     * clip-stack. If false is returned then the rect does not intersect the
+     * clip and is unmodified.
+     */
+    bool intersectRectWithClip(SkRect* devRect) const;
+
+    /**
+     * Returns true if the input rect in device space is entirely contained
+     * by the clip. A return value of false does not guarantee that the rect
+     * is not contained by the clip.
+     */
+    bool quickContains(const SkRect& devRect) const;
+
+    void clipDevRect(const SkIRect& ir, SkRegion::Op op) {
         SkRect r;
         r.set(ir);
         this->clipDevRect(r, op, false);
     }
     void clipDevRect(const SkRect&, SkRegion::Op, bool doAA);
     void clipDevPath(const SkPath&, SkRegion::Op, bool doAA);
+    // An optimized version of clipDevRect(emptyRect, kIntersect, ...)
+    void clipEmpty();
 
-    class B2FIter {
+    /**
+     * isWideOpen returns true if the clip state corresponds to the infinite
+     * plane (i.e., draws are not limited at all)
+     */
+    bool isWideOpen() const;
+
+    /**
+     * Add a callback function that will be called whenever a clip state
+     * is no longer viable. This will occur whenever restore
+     * is called or when a clipDevRect or clipDevPath call updates the
+     * clip within an existing save/restore state. Each clip state is
+     * represented by a unique generation ID.
+     */
+    typedef void (*PFPurgeClipCB)(int genID, void* data);
+    void addPurgeClipCallback(PFPurgeClipCB callback, void* data) const;
+
+    /**
+     * Remove a callback added earlier via addPurgeClipCallback
+     */
+    void removePurgeClipCallback(PFPurgeClipCB callback, void* data) const;
+
+    /**
+     * The generation ID has three reserved values to indicate special
+     * (potentially ignorable) cases
+     */
+    static const int32_t kInvalidGenID = 0;
+    static const int32_t kEmptyGenID = 1;       // no pixels writeable
+    static const int32_t kWideOpenGenID = 2;    // all pixels writeable
+
+    int32_t getTopmostGenID() const;
+
+public:
+    class Iter {
     public:
-        /**
-         * Creates an uninitialized iterator. Must be reset()
-         */
-        B2FIter();
-
-        B2FIter(const SkClipStack& stack);
-
-        struct Clip {
-            Clip() : fRect(NULL), fPath(NULL), fOp(SkRegion::kIntersect_Op) {}
-            friend bool operator==(const Clip& a, const Clip& b);
-            friend bool operator!=(const Clip& a, const Clip& b);
-            const SkRect*   fRect;  // if non-null, this is a rect clip
-            const SkPath*   fPath;  // if non-null, this is a path clip
-            SkRegion::Op    fOp;
-            bool            fDoAA;
+        enum IterStart {
+            kBottom_IterStart = SkDeque::Iter::kFront_IterStart,
+            kTop_IterStart = SkDeque::Iter::kBack_IterStart
         };
 
         /**
-         *  Return the clip for this element in the iterator. If next() returns
-         *  NULL, then the iterator is done. The type of clip is determined by
-         *  the pointers fRect and fPath:
-         *
-         *  fRect==NULL  fPath!=NULL    path clip
-         *  fRect!=NULL  fPath==NULL    rect clip
-         *  fRect==NULL  fPath==NULL    empty clip
+         * Creates an uninitialized iterator. Must be reset()
          */
-        const Clip* next();
+        Iter();
+
+        Iter(const SkClipStack& stack, IterStart startLoc);
+
+        /**
+         *  Return the clip element for this iterator. If next()/prev() returns NULL, then the
+         *  iterator is done.
+         */
+        const Element* next();
+        const Element* prev();
+
+        /**
+         * Moves the iterator to the topmost element with the specified RegionOp and returns that
+         * element. If no clip element with that op is found, the first element is returned.
+         */
+        const Element* skipToTopmost(SkRegion::Op op);
 
         /**
          * Restarts the iterator on a clip stack.
          */
-        void reset(const SkClipStack& stack);
+        void reset(const SkClipStack& stack, IterStart startLoc);
 
     private:
-        Clip             fClip;
-        SkDeque::F2BIter fIter;
+        const SkClipStack* fStack;
+        SkDeque::Iter      fIter;
     };
 
+    /**
+     * The B2TIter iterates from the bottom of the stack to the top.
+     * It inherits privately from Iter to prevent access to reverse iteration.
+     */
+    class B2TIter : private Iter {
+    public:
+        B2TIter() {}
+
+        /**
+         * Wrap Iter's 2 parameter ctor to force initialization to the
+         * beginning of the deque/bottom of the stack
+         */
+        B2TIter(const SkClipStack& stack)
+        : INHERITED(stack, kBottom_IterStart) {
+        }
+
+        using Iter::next;
+
+        /**
+         * Wrap Iter::reset to force initialization to the
+         * beginning of the deque/bottom of the stack
+         */
+        void reset(const SkClipStack& stack) {
+            this->INHERITED::reset(stack, kBottom_IterStart);
+        }
+
+    private:
+
+        typedef Iter INHERITED;
+    };
+
+    /**
+     * GetConservativeBounds returns a conservative bound of the current clip.
+     * Since this could be the infinite plane (if inverse fills were involved) the
+     * maxWidth and maxHeight parameters can be used to limit the returned bound
+     * to the expected drawing area. Similarly, the offsetX and offsetY parameters
+     * allow the caller to offset the returned bound to account for translated
+     * drawing areas (i.e., those resulting from a saveLayer). For finite bounds,
+     * the translation (+offsetX, +offsetY) is applied before the clamp to the
+     * maximum rectangle: [0,maxWidth) x [0,maxHeight).
+     * isIntersectionOfRects is an optional parameter that is true when
+     * 'devBounds' is the result of an intersection of rects. In this case
+     * 'devBounds' is the exact answer/clip.
+     */
+    void getConservativeBounds(int offsetX,
+                               int offsetY,
+                               int maxWidth,
+                               int maxHeight,
+                               SkRect* devBounds,
+                               bool* isIntersectionOfRects = NULL) const;
+
 private:
-    friend class B2FIter;
-    struct Rec;
+    friend class Iter;
 
     SkDeque fDeque;
     int     fSaveCount;
+
+    // Generation ID for the clip stack. This is incremented for each
+    // clipDevRect and clipDevPath call. 0 is reserved to indicate an
+    // invalid ID.
+    static int32_t     gGenID;
+
+    struct ClipCallbackData {
+        PFPurgeClipCB   fCallback;
+        void*           fData;
+
+        friend bool operator==(const ClipCallbackData& a,
+                               const ClipCallbackData& b) {
+            return a.fCallback == b.fCallback && a.fData == b.fData;
+        }
+    };
+
+    mutable SkTDArray<ClipCallbackData> fCallbackData;
+
+    /**
+     * Invoke all the purge callbacks passing in element's generation ID.
+     */
+    void purgeClip(Element* element);
+
+    /**
+     * Return the next unique generation ID.
+     */
+    static int32_t GetNextGenID();
 };
 
 #endif
-
diff --git a/include/core/SkColor.h b/include/core/SkColor.h
index e6d4352..dafc596 100644
--- a/include/core/SkColor.h
+++ b/include/core/SkColor.h
@@ -73,18 +73,20 @@
 
 // common colors
 
-#define SK_ColorBLACK   0xFF000000  //!< black SkColor value
-#define SK_ColorDKGRAY  0xFF444444  //!< dark gray SkColor value
-#define SK_ColorGRAY    0xFF888888  //!< gray SkColor value
-#define SK_ColorLTGRAY  0xFFCCCCCC  //!< light gray SkColor value
-#define SK_ColorWHITE   0xFFFFFFFF  //!< white SkColor value
+#define SK_ColorTRANSPARENT 0x00000000  //!< transparent SkColor value
 
-#define SK_ColorRED     0xFFFF0000  //!< red SkColor value
-#define SK_ColorGREEN   0xFF00FF00  //!< green SkColor value
-#define SK_ColorBLUE    0xFF0000FF  //!< blue SkColor value
-#define SK_ColorYELLOW  0xFFFFFF00  //!< yellow SkColor value
-#define SK_ColorCYAN    0xFF00FFFF  //!< cyan SkColor value
-#define SK_ColorMAGENTA 0xFFFF00FF  //!< magenta SkColor value
+#define SK_ColorBLACK       0xFF000000  //!< black SkColor value
+#define SK_ColorDKGRAY      0xFF444444  //!< dark gray SkColor value
+#define SK_ColorGRAY        0xFF888888  //!< gray SkColor value
+#define SK_ColorLTGRAY      0xFFCCCCCC  //!< light gray SkColor value
+#define SK_ColorWHITE       0xFFFFFFFF  //!< white SkColor value
+
+#define SK_ColorRED         0xFFFF0000  //!< red SkColor value
+#define SK_ColorGREEN       0xFF00FF00  //!< green SkColor value
+#define SK_ColorBLUE        0xFF0000FF  //!< blue SkColor value
+#define SK_ColorYELLOW      0xFFFFFF00  //!< yellow SkColor value
+#define SK_ColorCYAN        0xFF00FFFF  //!< cyan SkColor value
+#define SK_ColorMAGENTA     0xFFFF00FF  //!< magenta SkColor value
 
 ////////////////////////////////////////////////////////////////////////
 
@@ -162,4 +164,3 @@
 typedef uint16_t (*SkXfermodeProc16)(SkPMColor src, uint16_t dst);
 
 #endif
-
diff --git a/include/core/SkColorFilter.h b/include/core/SkColorFilter.h
index 97db5cc..65a8cf3 100644
--- a/include/core/SkColorFilter.h
+++ b/include/core/SkColorFilter.h
@@ -14,21 +14,27 @@
 #include "SkFlattenable.h"
 #include "SkXfermode.h"
 
+class SkBitmap;
+class GrEffectRef;
+class GrContext;
+
 class SK_API SkColorFilter : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkColorFilter)
+
     /**
      *  If the filter can be represented by a source color plus Mode, this
      *  returns true, and sets (if not NULL) the color and mode appropriately.
      *  If not, this returns false and ignores the parameters.
      */
-    virtual bool asColorMode(SkColor* color, SkXfermode::Mode* mode);
+    virtual bool asColorMode(SkColor* color, SkXfermode::Mode* mode) const;
 
     /**
      *  If the filter can be represented by a 5x4 matrix, this
      *  returns true, and sets the matrix appropriately.
      *  If not, this returns false and ignores the parameter.
      */
-    virtual bool asColorMatrix(SkScalar matrix[20]);
+    virtual bool asColorMatrix(SkScalar matrix[20]) const;
 
     /**
      *  If the filter can be represented by per-component table, return true,
@@ -46,7 +52,7 @@
      *  The original component value is the horizontal index for a given row,
      *  and the stored value at that index is the new value for that component.
      */
-    virtual bool asComponentTable(SkBitmap* table);
+    virtual bool asComponentTable(SkBitmap* table) const;
 
     /** Called with a scanline of colors, as if there was a shader installed.
         The implementation writes out its filtered version into result[].
@@ -56,7 +62,7 @@
         @param result   written by the filter
     */
     virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) = 0;
+                            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.
@@ -65,7 +71,7 @@
         @param result   written by the filter
     */
     virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]);
+                              uint16_t result[]) const;
 
     enum Flags {
         /** If set the filter methods will not change the alpha channel of the
@@ -81,7 +87,7 @@
     /** Returns the flags for this filter. Override in subclasses to return
         custom flags.
     */
-    virtual uint32_t getFlags() { return 0; }
+    virtual uint32_t getFlags() const { return 0; }
 
     /**
      *  Apply this colorfilter to the specified SkColor. This routine handles
@@ -89,8 +95,7 @@
      *  to SkColor. This method is not virtual, but will call filterSpan()
      *   which is virtual.
      */
-    SkColor filterColor(SkColor);
-
+    SkColor filterColor(SkColor) const;
 
     /** Create a colorfilter that uses the specified color and mode.
         If the Mode is DST, this function will return NULL (since that
@@ -103,22 +108,19 @@
     */
     static SkColorFilter* CreateModeFilter(SkColor c, SkXfermode::Mode mode);
 
-    /** Create a colorfilter that calls through to the specified procs to
-        filter the colors. The SkXfermodeProc parameter must be non-null, but
-        the SkXfermodeProc16 is optional, and may be null.
-    */
-    static SkColorFilter* CreateProcFilter(SkColor srcColor,
-                                           SkXfermodeProc proc,
-                                           SkXfermodeProc16 proc16 = NULL);
-
     /** Create a colorfilter that multiplies the RGB channels by one color, and
         then adds a second color, pinning the result for each component to
         [0..255]. The alpha components of the mul and add arguments
         are ignored.
     */
     static SkColorFilter* CreateLightingFilter(SkColor mul, SkColor add);
-    
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+
+    /** A subclass may implement this factory function to work with the GPU backend. If the return
+        is non-NULL then the caller owns a ref on the returned object.
+     */
+    virtual GrEffectRef* asNewEffect(GrContext*) const;
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 protected:
     SkColorFilter() {}
     SkColorFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
@@ -127,33 +129,4 @@
     typedef SkFlattenable INHERITED;
 };
 
-#include "SkShader.h"
-
-class SkFilterShader : public SkShader {
-public:
-    SkFilterShader(SkShader* shader, SkColorFilter* filter);
-    virtual ~SkFilterShader();
-
-    // override
-    virtual uint32_t getFlags();
-    virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
-                            const SkMatrix& matrix);
-    virtual void shadeSpan(int x, int y, SkPMColor result[], int count);
-    virtual void shadeSpan16(int x, int y, uint16_t result[], int count);
-    virtual void beginSession();
-    virtual void endSession();
-
-protected:
-    SkFilterShader(SkFlattenableReadBuffer& );
-    virtual void flatten(SkFlattenableWriteBuffer& ) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-private:
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkFilterShader, (buffer)); }
-    SkShader*       fShader;
-    SkColorFilter*  fFilter;
-
-    typedef SkShader INHERITED;
-};
-
 #endif
diff --git a/include/core/SkColorPriv.h b/include/core/SkColorPriv.h
index e51b0b9..46f7d27 100644
--- a/include/core/SkColorPriv.h
+++ b/include/core/SkColorPriv.h
@@ -18,6 +18,30 @@
 #include "SkColor.h"
 #include "SkMath.h"
 
+///@{
+/** See ITU-R Recommendation BT.709 at http://www.itu.int/rec/R-REC-BT.709/ .*/
+#define SK_ITU_BT709_LUM_COEFF_R (0.2126f)
+#define SK_ITU_BT709_LUM_COEFF_G (0.7152f)
+#define SK_ITU_BT709_LUM_COEFF_B (0.0722f)
+///@}
+
+///@{
+/** A float value which specifies this channel's contribution to luminance. */
+#define SK_LUM_COEFF_R SK_ITU_BT709_LUM_COEFF_R
+#define SK_LUM_COEFF_G SK_ITU_BT709_LUM_COEFF_G
+#define SK_LUM_COEFF_B SK_ITU_BT709_LUM_COEFF_B
+///@}
+
+/** Computes the luminance from the given r, g, and b in accordance with
+    SK_LUM_COEFF_X. For correct results, r, g, and b should be in linear space.
+*/
+static inline U8CPU SkComputeLuminance(U8CPU r, U8CPU g, U8CPU b) {
+    //The following is
+    //r * SK_LUM_COEFF_R + g * SK_LUM_COEFF_G + b * SK_LUM_COEFF_B
+    //with SK_LUM_COEFF_X in 1.8 fixed point (rounding adjusted to sum to 256).
+    return (r * 54 + g * 183 + b * 19) >> 8;
+}
+
 /** Turn 0..255 into 0..256 by adding 1 at the half-way point. Used to turn a
     byte into a scale value, so that we can say scale * value >> 8 instead of
     alpha * value / 255.
@@ -53,7 +77,7 @@
     SkASSERT((int16_t)src == src);
     SkASSERT((int16_t)dst == dst);
     SkASSERT((uint8_t)alpha == alpha);
-    
+
     int prod = SkMulS16(src - dst, alpha) + 128;
     prod = (prod + (prod >> 8)) >> 8;
     return dst + prod;
@@ -220,11 +244,10 @@
  * utility functions. Third parameter controls blending of the first two:
  *   (src, dst, 0) returns dst
  *   (src, dst, 0xFF) returns src
+ *   srcWeight is [0..256], unlike SkFourByteInterp which takes [0..255]
  */
-static inline SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst,
-                                         U8CPU srcWeight) {
-    unsigned scale = SkAlpha255To256(srcWeight);
-
+static inline SkPMColor SkFourByteInterp256(SkPMColor src, SkPMColor dst,
+                                         unsigned scale) {
     unsigned a = SkAlphaBlend(SkGetPackedA32(src), SkGetPackedA32(dst), scale);
     unsigned r = SkAlphaBlend(SkGetPackedR32(src), SkGetPackedR32(dst), scale);
     unsigned g = SkAlphaBlend(SkGetPackedG32(src), SkGetPackedG32(dst), scale);
@@ -234,6 +257,18 @@
 }
 
 /**
+ * Abstract 4-byte interpolation, implemented on top of SkPMColor
+ * utility functions. Third parameter controls blending of the first two:
+ *   (src, dst, 0) returns dst
+ *   (src, dst, 0xFF) returns src
+ */
+static inline SkPMColor SkFourByteInterp(SkPMColor src, SkPMColor dst,
+                                         U8CPU srcWeight) {
+    unsigned scale = SkAlpha255To256(srcWeight);
+    return SkFourByteInterp256(src, dst, scale);
+}
+
+/**
  * 32b optimized version; currently appears to be 10% faster even on 64b
  * architectures than an equivalent 64b version and 30% faster than
  * SkFourByteInterp(). Third parameter controls blending of the first two:
@@ -729,32 +764,32 @@
 }
 
 static inline SkPMColor SkBlendLCD16(int srcA, int srcR, int srcG, int srcB,
-                                     SkPMColor dst, uint16_t mask) { 
+                                     SkPMColor dst, uint16_t mask) {
     if (mask == 0) {
         return dst;
     }
-        
+
     /*  We want all of these in 5bits, hence the shifts in case one of them
      *  (green) is 6bits.
      */
     int maskR = SkGetPackedR16(mask) >> (SK_R16_BITS - 5);
     int maskG = SkGetPackedG16(mask) >> (SK_G16_BITS - 5);
     int maskB = SkGetPackedB16(mask) >> (SK_B16_BITS - 5);
-        
+
     // Now upscale them to 0..32, so we can use blend32
     maskR = SkUpscale31To32(maskR);
     maskG = SkUpscale31To32(maskG);
     maskB = SkUpscale31To32(maskB);
-     
+
     // srcA has been upscaled to 256 before passed into this function
     maskR = maskR * srcA >> 8;
     maskG = maskG * srcA >> 8;
     maskB = maskB * srcA >> 8;
-        
+
     int dstR = SkGetPackedR32(dst);
     int dstG = SkGetPackedG32(dst);
     int dstB = SkGetPackedB32(dst);
-        
+
     // LCD blitting is only supported if the dst is known/required
     // to be opaque
     return SkPackARGB32(0xFF,
@@ -765,7 +800,7 @@
 
 static inline SkPMColor SkBlendLCD16Opaque(int srcR, int srcG, int srcB,
                                            SkPMColor dst, uint16_t mask,
-                                           SkPMColor opaqueDst) { 
+                                           SkPMColor opaqueDst) {
     if (mask == 0) {
         return dst;
     }
@@ -773,23 +808,23 @@
     if (0xFFFF == mask) {
         return opaqueDst;
     }
-        
+
     /*  We want all of these in 5bits, hence the shifts in case one of them
      *  (green) is 6bits.
      */
     int maskR = SkGetPackedR16(mask) >> (SK_R16_BITS - 5);
     int maskG = SkGetPackedG16(mask) >> (SK_G16_BITS - 5);
     int maskB = SkGetPackedB16(mask) >> (SK_B16_BITS - 5);
-        
+
     // Now upscale them to 0..32, so we can use blend32
     maskR = SkUpscale31To32(maskR);
     maskG = SkUpscale31To32(maskG);
     maskB = SkUpscale31To32(maskB);
-        
+
     int dstR = SkGetPackedR32(dst);
     int dstG = SkGetPackedG32(dst);
     int dstB = SkGetPackedB32(dst);
-        
+
     // LCD blitting is only supported if the dst is known/required
     // to be opaque
     return SkPackARGB32(0xFF,
@@ -804,26 +839,25 @@
     int srcR = SkColorGetR(color);
     int srcG = SkColorGetG(color);
     int srcB = SkColorGetB(color);
-    
+
     srcA = SkAlpha255To256(srcA);
-    
+
     for (int i = 0; i < width; i++) {
         dst[i] = SkBlendLCD16(srcA, srcR, srcG, srcB, dst[i], src[i]);
     }
 }
 
 static inline void SkBlitLCD16OpaqueRow(SkPMColor dst[], const uint16_t src[],
-                                        SkColor color, int width, 
+                                        SkColor color, int width,
                                         SkPMColor opaqueDst) {
     int srcR = SkColorGetR(color);
     int srcG = SkColorGetG(color);
     int srcB = SkColorGetB(color);
-    
+
     for (int i = 0; i < width; i++) {
         dst[i] = SkBlendLCD16Opaque(srcR, srcG, srcB, dst[i], src[i],
-                                    opaqueDst); 
+                                    opaqueDst);
     }
 }
 
 #endif
-
diff --git a/include/core/SkColorShader.h b/include/core/SkColorShader.h
index 9b1fed3..c379068 100644
--- a/include/core/SkColorShader.h
+++ b/include/core/SkColorShader.h
@@ -44,19 +44,18 @@
     // we return false for this, use asAGradient
     virtual BitmapType asABitmap(SkBitmap* outTexture,
                                  SkMatrix* outMatrix,
-                                 TileMode xy[2],
-                                 SkScalar* twoPointRadialParams) const SK_OVERRIDE;
+                                 TileMode xy[2]) const SK_OVERRIDE;
 
     virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
 
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorShader)
+
 protected:
     SkColorShader(SkFlattenableReadBuffer&);
-
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
 
     SkColor     fColor;         // ignored if fInheritColor is true
     SkPMColor   fPMColor;       // cached after setContext()
diff --git a/include/core/SkColorTable.h b/include/core/SkColorTable.h
new file mode 100644
index 0000000..f8d1ccf
--- /dev/null
+++ b/include/core/SkColorTable.h
@@ -0,0 +1,112 @@
+
+/*
+ * 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 SkColorTable_DEFINED
+#define SkColorTable_DEFINED
+
+#include "SkColor.h"
+#include "SkFlattenable.h"
+
+/** \class SkColorTable
+
+    SkColorTable holds an array SkPMColors (premultiplied 32-bit colors) used by
+    8-bit bitmaps, where the bitmap bytes are interpreted as indices into the colortable.
+*/
+class SkColorTable : public SkFlattenable {
+public:
+    SK_DECLARE_INST_COUNT(SkColorTable)
+
+    /** Makes a deep copy of colors.
+     */
+    SkColorTable(const SkColorTable& src);
+    /** Preallocates the colortable to have 'count' colors, which
+     *  are initially set to 0.
+    */
+    explicit SkColorTable(int count);
+    SkColorTable(const SkPMColor colors[], int count);
+    virtual ~SkColorTable();
+
+    enum Flags {
+        kColorsAreOpaque_Flag   = 0x01  //!< if set, all of the colors in the table are opaque (alpha==0xFF)
+    };
+    /** Returns the flag bits for the color table. These can be changed with setFlags().
+    */
+    unsigned getFlags() const { return fFlags; }
+    /** Set the flags for the color table. See the Flags enum for possible values.
+    */
+    void    setFlags(unsigned flags);
+
+    bool isOpaque() const { return (fFlags & kColorsAreOpaque_Flag) != 0; }
+    void setIsOpaque(bool isOpaque);
+
+    /** Returns the number of colors in the table.
+    */
+    int count() const { return fCount; }
+
+    /** Returns the specified color from the table. In the debug build, this asserts that
+        the index is in range (0 <= index < count).
+    */
+    SkPMColor operator[](int index) const {
+        SkASSERT(fColors != NULL && (unsigned)index < fCount);
+        return fColors[index];
+    }
+
+    /** Specify the number of colors in the color table. This does not initialize the colors
+        to any value, just allocates memory for them. To initialize the values, either call
+        setColors(array, count), or follow setCount(count) with a call to
+        lockColors()/{set the values}/unlockColors(true).
+    */
+//    void    setColors(int count) { this->setColors(NULL, count); }
+//    void    setColors(const SkPMColor[], int count);
+
+    /** Return the array of colors for reading and/or writing. This must be
+        balanced by a call to unlockColors(changed?), telling the colortable if
+        the colors were changed during the lock.
+    */
+    SkPMColor* lockColors() {
+        SkDEBUGCODE(sk_atomic_inc(&fColorLockCount);)
+        return fColors;
+    }
+    /** Balancing call to lockColors(). If the colors have been changed, pass true.
+    */
+    void unlockColors(bool changed);
+
+    /** Similar to lockColors(), lock16BitCache() returns the array of
+        RGB16 colors that mirror the 32bit colors. However, this function
+        will return null if kColorsAreOpaque_Flag is not set.
+        Also, unlike lockColors(), the returned array here cannot be modified.
+    */
+    const uint16_t* lock16BitCache();
+    /** Balancing call to lock16BitCache().
+    */
+    void unlock16BitCache() {
+        SkASSERT(f16BitCacheLockCount > 0);
+        SkDEBUGCODE(f16BitCacheLockCount -= 1);
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorTable)
+
+protected:
+    explicit SkColorTable(SkFlattenableReadBuffer&);
+    void flatten(SkFlattenableWriteBuffer&) const;
+
+private:
+    SkPMColor*  fColors;
+    uint16_t*   f16BitCache;
+    uint16_t    fCount;
+    uint8_t     fFlags;
+    SkDEBUGCODE(int fColorLockCount;)
+    SkDEBUGCODE(int f16BitCacheLockCount;)
+
+    void inval16BitCache();
+
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkComposeShader.h b/include/core/SkComposeShader.h
index f243954..524161b 100644
--- a/include/core/SkComposeShader.h
+++ b/include/core/SkComposeShader.h
@@ -33,21 +33,20 @@
     */
     SkComposeShader(SkShader* sA, SkShader* sB, SkXfermode* mode = NULL);
     virtual ~SkComposeShader();
-    
-    // override
-    virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix);
-    virtual void shadeSpan(int x, int y, SkPMColor result[], int count);
-    virtual void beginSession();
-    virtual void endSession();
+
+    virtual bool setContext(const SkBitmap&, const SkPaint&,
+                            const SkMatrix&) SK_OVERRIDE;
+    virtual void endContext() SK_OVERRIDE;
+    virtual void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeShader)
 
 protected:
     SkComposeShader(SkFlattenableReadBuffer& );
-    virtual void flatten(SkFlattenableWriteBuffer& );
-    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { 
-        return SkNEW_ARGS(SkComposeShader, (buffer)); }
 
     SkShader*   fShaderA;
     SkShader*   fShaderB;
diff --git a/include/core/SkData.h b/include/core/SkData.h
index a134536..9a0cb09 100644
--- a/include/core/SkData.h
+++ b/include/core/SkData.h
@@ -11,20 +11,24 @@
 #ifndef SkData_DEFINED
 #define SkData_DEFINED
 
-#include "SkRefCnt.h"
+#include "SkFlattenable.h"
 
 /**
  *  SkData holds an immutable data buffer. Not only is the data immutable,
  *  but the actual ptr that is returned (by data() or bytes()) is guaranteed
  *  to always be the same for the life of this instance.
  */
-class SkData : public SkRefCnt {
+class SK_API SkData : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkData)
+
     /**
      *  Returns the number of bytes stored.
      */
     size_t size() const { return fSize; }
 
+    bool isEmpty() const { return 0 == fSize; }
+
     /**
      *  Returns the ptr to the data.
      */
@@ -47,17 +51,31 @@
     size_t copyRange(size_t offset, size_t length, void* buffer) const;
 
     /**
+     *  Returns true if these two objects have the same length and contents,
+     *  effectively returning 0 == memcmp(...)
+     */
+    bool equals(const SkData* other) const;
+
+    /**
      *  Function that, if provided, will be called when the SkData goes out
      *  of scope, allowing for custom allocation/freeing of the data.
      */
     typedef void (*ReleaseProc)(const void* ptr, size_t length, void* context);
-    
+
     /**
      *  Create a new dataref by copying the specified data
      */
     static SkData* NewWithCopy(const void* data, size_t length);
 
     /**
+     *  Create a new dataref by copying the specified c-string
+     *  (a null-terminated array of bytes). The returned SkData will have size()
+     *  equal to strlen(cstr) + 1. If cstr is NULL, it will be treated the same
+     *  as "".
+     */
+    static SkData* NewWithCString(const char cstr[]);
+
+    /**
      *  Create a new dataref, taking the data ptr as is, and using the
      *  releaseproc to free it. The proc may be NULL.
      */
@@ -65,8 +83,8 @@
                                ReleaseProc proc, void* context);
 
     /**
-     *  Create a new dataref, reference the data ptr as is, and calling
-     *  sk_free to delete it.
+     *  Create a new dataref from a pointer allocated by malloc. The Data object
+     *  takes ownership of that allocation, and will handling calling sk_free.
      */
     static SkData* NewFromMalloc(const void* data, size_t length);
 
@@ -82,6 +100,12 @@
      */
     static SkData* NewEmpty();
 
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkData)
+
+protected:
+    SkData(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
 private:
     ReleaseProc fReleaseProc;
     void*       fReleaseProcContext;
@@ -90,48 +114,43 @@
     size_t      fSize;
 
     SkData(const void* ptr, size_t size, ReleaseProc, void* context);
-    ~SkData();
+    virtual ~SkData();
+
+    // This is here because SkAutoTUnref creates an internal helper class
+    // that derives from SkData (i.e., BlockRef) to prevent refs\unrefs.
+    // This helper class generates a compiler warning on Windows since the
+    // SkData's destructor is private. This friending gives the helper class
+    // access to the destructor.
+    friend class SkAutoTUnref<SkData>::BlockRef<SkData>;
+
+    typedef SkFlattenable INHERITED;
 };
 
 /**
  *  Specialized version of SkAutoTUnref<SkData> for automatically unref-ing a
- *  SkData. If the SkData is null, data(), bytes() and size() will return 0.
+ *  SkData.
  */
 class SkAutoDataUnref : SkNoncopyable {
 public:
-    SkAutoDataUnref(SkData* data) : fRef(data) {
-        if (data) {
-            fData = data->data();
-            fSize = data->size();
-        } else {
-            fData = NULL;
-            fSize = 0;
-        }
-    }
+    SkAutoDataUnref(SkData* data) : fRef(data) {}
     ~SkAutoDataUnref() {
         SkSafeUnref(fRef);
     }
 
-    const void* data() const { return fData; }
-    const uint8_t* bytes() const {
-        return reinterpret_cast<const uint8_t*> (fData);
-    }
-    size_t size() const { return fSize; }
     SkData* get() const { return fRef; }
 
     void release() {
         if (fRef) {
             fRef->unref();
             fRef = NULL;
-            fData = NULL;
-            fSize = 0;
         }
     }
 
+    SkData *operator->() const { return fRef; }
+    operator SkData*() { return fRef; }
+
 private:
     SkData*     fRef;
-    const void* fData;
-    size_t      fSize;
 };
 
 #endif
diff --git a/include/core/SkDataSet.h b/include/core/SkDataSet.h
new file mode 100644
index 0000000..2e5d96e
--- /dev/null
+++ b/include/core/SkDataSet.h
@@ -0,0 +1,88 @@
+/*
+ * 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 SkDataSet_DEFINED
+#define SkDataSet_DEFINED
+
+#include "SkData.h"
+#include "SkFlattenable.h"
+
+class SkStream;
+class SkWStream;
+
+class SkDataSet : public SkFlattenable {
+public:
+    /**
+     *  Returns a new empty dataset. Note: since SkDataSet is immutable, this
+     *  "new" set may be the same one that was returned before, but each
+     *  returned object must have its reference-count balanced regardless.
+     *
+     *  SkDataSet* empty = SkDataSet::NewEmpty();
+     *  ...
+     *  empty->unref();
+     */
+    static SkDataSet* NewEmpty();
+
+    struct Pair {
+        const char* fKey;
+        SkData*     fValue;
+    };
+
+    SkDataSet(const char key[], SkData* value);
+    SkDataSet(const Pair[], int count);
+    virtual ~SkDataSet();
+
+    bool isEmpty() const { return 0 == fCount; }
+    int count() const { return fCount; }
+    SkData* find(const char name[]) const;
+
+    class Iter {
+    public:
+        Iter(const SkDataSet& ds) {
+            fPair = ds.fPairs;
+            fStop = ds.fPairs + ds.fCount;
+        }
+
+        const char* key() const {
+            SkASSERT(!this->done());
+            return fPair->fKey;
+        }
+
+        SkData* value() const {
+            SkASSERT(!this->done());
+            return fPair->fValue;
+        }
+
+        bool done() const { return fPair >= fStop; }
+        void next() {
+            SkASSERT(!this->done());
+            fPair += 1;
+        }
+
+    private:
+        const SkDataSet::Pair* fPair;
+        const SkDataSet::Pair* fStop;
+    };
+
+    explicit SkDataSet(SkStream*);
+    void writeToStream(SkWStream*) const;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDataSet)
+
+protected:
+    SkDataSet(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    int32_t     fCount;
+    uint32_t    fKeySize;
+    Pair*       fPairs;
+
+    typedef SkFlattenable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkDeque.h b/include/core/SkDeque.h
index b4f420d..eef335d 100644
--- a/include/core/SkDeque.h
+++ b/include/core/SkDeque.h
@@ -12,18 +12,33 @@
 
 #include "SkTypes.h"
 
+/*
+ * The deque class works by blindly creating memory space of a specified element
+ * size. It manages the memory as a doubly linked list of blocks each of which
+ * can contain multiple elements. Pushes and pops add/remove blocks from the
+ * beginning/end of the list as necessary while each block tracks the used
+ * portion of its memory.
+ * One behavior to be aware of is that the pops do not immediately remove an
+ * empty block from the beginning/end of the list (Presumably so push/pop pairs
+ * on the block boundaries don't cause thrashing). This can result in the first/
+ * last element not residing in the first/last block.
+ */
 class SK_API SkDeque : SkNoncopyable {
 public:
-    explicit SkDeque(size_t elemSize);
-    SkDeque(size_t elemSize, void* storage, size_t storageSize);
+    /**
+     * elemSize specifies the size of each individual element in the deque
+     * allocCount specifies how many elements are to be allocated as a block
+     */
+    explicit SkDeque(size_t elemSize, int allocCount = 1);
+    SkDeque(size_t elemSize, void* storage, size_t storageSize, int allocCount = 1);
     ~SkDeque();
 
     bool    empty() const { return 0 == fCount; }
     int     count() const { return fCount; }
     size_t  elemSize() const { return fElemSize; }
 
-    const void* front() const;
-    const void* back() const;
+    const void* front() const { return fFront; }
+    const void* back() const  { return fBack; }
 
     void* front() {
         return (void*)((const SkDeque*)this)->front();
@@ -33,6 +48,10 @@
         return (void*)((const SkDeque*)this)->back();
     }
 
+    /**
+     * push_front and push_back return a pointer to the memory space
+     * for the new element
+     */
     void* push_front();
     void* push_back();
 
@@ -40,35 +59,80 @@
     void pop_back();
 
 private:
-    struct Head;
+    struct Block;
 
 public:
-    class F2BIter {
+    class Iter {
     public:
+        enum IterStart {
+            kFront_IterStart,
+            kBack_IterStart
+        };
+
         /**
          * Creates an uninitialized iterator. Must be reset()
          */
-        F2BIter();
+        Iter();
 
-        F2BIter(const SkDeque& d);
+        Iter(const SkDeque& d, IterStart startLoc);
         void* next();
+        void* prev();
 
-        void reset(const SkDeque& d);
+        void reset(const SkDeque& d, IterStart startLoc);
 
     private:
-        SkDeque::Head*  fHead;
+        SkDeque::Block*  fCurBlock;
         char*           fPos;
         size_t          fElemSize;
     };
 
+    // Inherit privately from Iter to prevent access to reverse iteration
+    class F2BIter : private Iter {
+    public:
+        F2BIter() {}
+
+        /**
+         * Wrap Iter's 2 parameter ctor to force initialization to the
+         * beginning of the deque
+         */
+        F2BIter(const SkDeque& d) : INHERITED(d, kFront_IterStart) {}
+
+        using Iter::next;
+
+        /**
+         * Wrap Iter::reset to force initialization to the beginning of the
+         * deque
+         */
+        void reset(const SkDeque& d) {
+            this->INHERITED::reset(d, kFront_IterStart);
+        }
+
+    private:
+        typedef Iter INHERITED;
+    };
+
 private:
-    Head*   fFront;
-    Head*   fBack;
+    // allow unit test to call numBlocksAllocated
+    friend class DequeUnitTestHelper;
+
+    void*   fFront;
+    void*   fBack;
+
+    Block*  fFrontBlock;
+    Block*  fBackBlock;
     size_t  fElemSize;
     void*   fInitialStorage;
-    int     fCount;
+    int     fCount;             // number of elements in the deque
+    int     fAllocCount;        // number of elements to allocate per block
 
-    friend class Iter;
+    Block*  allocateBlock(int allocCount);
+    void    freeBlock(Block* block);
+
+    /**
+     * This returns the number of chunk blocks allocated by the deque. It
+     * can be used to gauge the effectiveness of the selected allocCount.
+     */
+    int  numBlocksAllocated() const;
 };
 
 #endif
diff --git a/include/core/SkDescriptor.h b/include/core/SkDescriptor.h
deleted file mode 100644
index b97b75f..0000000
--- a/include/core/SkDescriptor.h
+++ /dev/null
@@ -1,181 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkDescriptor_DEFINED
-#define SkDescriptor_DEFINED
-
-#include "SkTypes.h"
-
-class SkDescriptor : SkNoncopyable {
-public:
-    static size_t ComputeOverhead(int entryCount)
-    {
-        SkASSERT(entryCount >= 0);
-        return sizeof(SkDescriptor) + entryCount * sizeof(Entry);
-    }
-
-    static SkDescriptor* Alloc(size_t length)
-    {
-        SkASSERT(SkAlign4(length) == length);
-        SkDescriptor* desc = (SkDescriptor*)sk_malloc_throw(length);
-        return desc;
-    }
-
-    static void Free(SkDescriptor* desc)
-    {
-        sk_free(desc);
-    }
-
-    void init()
-    {
-        fLength = sizeof(SkDescriptor);
-        fCount  = 0;
-    }
-
-    uint32_t getLength() const { return fLength; }
-
-    void* addEntry(uint32_t tag, uint32_t length, const void* data = NULL)
-    {
-        SkASSERT(tag);
-        SkASSERT(SkAlign4(length) == length);
-        SkASSERT(this->findEntry(tag, NULL) == NULL);
-
-        Entry*  entry = (Entry*)((char*)this + fLength);
-        entry->fTag = tag;
-        entry->fLen = length;
-        if (data)
-            memcpy(entry + 1, data, length);
-
-        fCount += 1;
-        fLength += sizeof(Entry) + length;
-        return (entry + 1); // return its data
-    }
-
-    void computeChecksum()
-    {
-        fChecksum = SkDescriptor::ComputeChecksum(this);
-    }
-
-#ifdef SK_DEBUG
-    void assertChecksum() const
-    {
-        SkASSERT(fChecksum == SkDescriptor::ComputeChecksum(this));
-    }
-#endif
-
-    const void* findEntry(uint32_t tag, uint32_t* length) const
-    {
-        const Entry* entry = (const Entry*)(this + 1);
-        int          count = fCount;
-
-        while (--count >= 0)
-        {
-            if (entry->fTag == tag)
-            {
-                if (length)
-                    *length = entry->fLen;
-                return entry + 1;
-            }
-            entry = (const Entry*)((const char*)(entry + 1) + entry->fLen);
-        }
-        return NULL;
-    }
-
-    SkDescriptor* copy() const
-    {
-        SkDescriptor* desc = SkDescriptor::Alloc(fLength);
-        memcpy(desc, this, fLength);
-        return desc;
-    }
-
-    bool equals(const SkDescriptor& other) const
-    {
-        // probe to see if we have a good checksum algo
-//        SkASSERT(a.fChecksum != b.fChecksum || memcmp(&a, &b, a.fLength) == 0);
-
-        // the first value we should look at is the checksum, so this loop
-        // should terminate early if they descriptors are different.
-        // NOTE: if we wrote a sentinel value at the end of each, we chould
-        //       remove the aa < stop test in the loop...
-        const uint32_t* aa = (const uint32_t*)this;
-        const uint32_t* bb = (const uint32_t*)&other;
-        const uint32_t* stop = (const uint32_t*)((const char*)aa + fLength);
-        do {
-            if (*aa++ != *bb++)
-                return false;
-        } while (aa < stop);
-        return true;
-    }
-
-    uint32_t getChecksum() const { return fChecksum; }
-
-    struct Entry {
-        uint32_t fTag;
-        uint32_t fLen;
-    };
-
-#ifdef SK_DEBUG
-    uint32_t getCount() const { return fCount; }
-#endif
-
-private:
-    uint32_t fChecksum;  // must be first
-    uint32_t fLength;    // must be second
-    uint32_t fCount;
-
-    static uint32_t ComputeChecksum(const SkDescriptor* desc)
-    {
-        const uint32_t*  ptr = (const uint32_t*)desc + 1; // skip the checksum field
-        const uint32_t*  stop = (const uint32_t*)((const char*)desc + desc->fLength);
-        uint32_t         sum = 0;
-
-        SkASSERT(ptr < stop);
-        do {
-            sum = (sum << 1) | (sum >> 31);
-            sum ^= *ptr++;
-        } while (ptr < stop);
-
-        return sum;
-    }
-    
-    // private so no one can create one except our factories
-    SkDescriptor() {}
-};
-
-#include "SkScalerContext.h"
-
-class SkAutoDescriptor : SkNoncopyable {
-public:
-    SkAutoDescriptor(size_t size)
-    {
-        if (size <= sizeof(fStorage))
-            fDesc = (SkDescriptor*)(void*)fStorage;
-        else
-            fDesc = SkDescriptor::Alloc(size);
-    }
-    ~SkAutoDescriptor()
-    {
-        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
-                        + sizeof(SkDescriptor::Entry) + sizeof(void*)                   // for typeface
-                        + 32   // slop for occational small extras
-    };
-    SkDescriptor*   fDesc;
-    uint32_t        fStorage[(kStorageSize + 3) >> 2];
-};
-
-
-#endif
-
diff --git a/include/core/SkDevice.h b/include/core/SkDevice.h
index 3303981..81bbf43 100644
--- a/include/core/SkDevice.h
+++ b/include/core/SkDevice.h
@@ -14,6 +14,7 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColor.h"
+#include "SkDeviceProperties.h"
 
 class SkClipStack;
 class SkDraw;
@@ -27,6 +28,8 @@
 
 class SK_API SkDevice : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkDevice)
+
     /**
      *  Construct a new device with the specified bitmap as its backend. It is
      *  valid for the bitmap to have no pixels associated with it. In that case,
@@ -35,6 +38,13 @@
     SkDevice(const SkBitmap& bitmap);
 
     /**
+     *  Construct a new device with the specified bitmap as its backend. It is
+     *  valid for the bitmap to have no pixels associated with it. In that case,
+     *  any drawing to this device will have no effect.
+    */
+    SkDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties);
+
+    /**
      *  Create a new raster device and have the pixels be automatically
      *  allocated. The rowBytes of the device will be computed automatically
      *  based on the config and the width.
@@ -49,6 +59,23 @@
      */
     SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque = false);
 
+    /**
+     *  Create a new raster device and have the pixels be automatically
+     *  allocated. The rowBytes of the device will be computed automatically
+     *  based on the config and the width.
+     *
+     *  @param config   The desired config for the pixels. If the request cannot
+     *                  be met, the closest matching support config will be used.
+     *  @param width    width (in pixels) of the device
+     *  @param height   height (in pixels) of the device
+     *  @param isOpaque Set to true if it is known that all of the pixels will
+     *                  be drawn to opaquely. Used as an accelerator when drawing
+     *                  these pixels to another device.
+     *  @param deviceProperties Properties which affect compositing.
+     */
+    SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
+             const SkDeviceProperties& deviceProperties);
+
     virtual ~SkDevice();
 
     /**
@@ -82,6 +109,12 @@
     */
     virtual int height() const { return fBitmap.height(); }
 
+    /** Return the image properties of the device. */
+    virtual const SkDeviceProperties& getDeviceProperties() const {
+        //Currently, all the properties are leaky.
+        return fLeakyProperties;
+    }
+
     /**
      *  Return the bounds of the device in the coordinate space of the root
      *  canvas. The root device will have its top-left at 0,0, but other devices
@@ -136,10 +169,38 @@
      */
     const SkIPoint& getOrigin() const { return fOrigin; }
 
+    /**
+     * onAttachToCanvas is invoked whenever a device is installed in a canvas
+     * (i.e., setDevice, saveLayer (for the new device created by the save),
+     * and SkCanvas' SkDevice & SkBitmap -taking ctors). It allows the
+     * devices to prepare for drawing (e.g., locking their pixels, etc.)
+     */
+    virtual void onAttachToCanvas(SkCanvas* canvas) {
+        SkASSERT(!fAttachedToCanvas);
+        this->lockPixels();
+#ifdef SK_DEBUG
+        fAttachedToCanvas = true;
+#endif
+    };
+
+    /**
+     * onDetachFromCanvas notifies a device that it will no longer be drawn to.
+     * It gives the device a chance to clean up (e.g., unlock its pixels). It
+     * is invoked from setDevice (for the displaced device), restore and
+     * possibly from SkCanvas' dtor.
+     */
+    virtual void onDetachFromCanvas() {
+        SkASSERT(fAttachedToCanvas);
+        this->unlockPixels();
+#ifdef SK_DEBUG
+        fAttachedToCanvas = false;
+#endif
+    };
+
 protected:
     enum Usage {
        kGeneral_Usage,
-       kSaveLayer_Usage, // <! internal use only
+       kSaveLayer_Usage  // <! internal use only
     };
 
     struct TextFlags {
@@ -158,6 +219,10 @@
     virtual bool filterTextFlags(const SkPaint& paint, TextFlags*);
 
     /**
+     *
+     *  DEPRECATED: This will be removed in a future change. Device subclasses
+     *  should use the matrix and clip from the SkDraw passed to draw functions.
+     *
      *  Called with the correct matrix and clip before this device is drawn
      *  to using those settings. If your subclass overrides this, be sure to
      *  call through to the base class as well.
@@ -168,15 +233,9 @@
      *  picture of the current clip. (i.e. if you regionize all of the geometry
      *  in the clipstack, you will arrive at an equivalent region to the one
      *  passed in).
-    */
-    virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
-                               const SkClipStack&);
-
-    /** Called when this device gains focus (i.e becomes the current device
-        for drawing).
-    */
-    virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&,
-                           const SkClipStack&) {}
+     */
+     virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
+                                const SkClipStack&);
 
     /** Clears the entire device to the specified color (including alpha).
      *  Ignores the clip.
@@ -198,6 +257,8 @@
                             const SkPoint[], const SkPaint& paint);
     virtual void drawRect(const SkDraw&, const SkRect& r,
                           const SkPaint& paint);
+    virtual void drawOval(const SkDraw&, const SkRect& oval,
+                          const SkPaint& paint);
     /**
      *  If pathIsMutable, then the implementation is allowed to cast path to a
      *  non-const pointer and modify it in place (as an optimization). Canvas
@@ -218,6 +279,15 @@
                             const SkMatrix& matrix, const SkPaint& paint);
     virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
                             int x, int y, const SkPaint& paint);
+
+    /**
+     *  The default impl. will create a bitmap-shader from the bitmap,
+     *  and call drawRect with it.
+     */
+    virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint& paint);
+
     /**
      *  Does not handle text decoration.
      *  Decorations (underline and stike-thru) will be handled by SkCanvas.
@@ -283,7 +353,7 @@
         access the pixels directly. Note: only the pixels field should be
         altered. The config/width/height/rowbytes must remain unchanged.
         @param bitmap The device's bitmap
-        @return Echo the bitmap parameter, or an alternate (shadow) bitmap 
+        @return Echo the bitmap parameter, or an alternate (shadow) bitmap
             maintained by the subclass.
     */
     virtual const SkBitmap& onAccessBitmap(SkBitmap*);
@@ -321,13 +391,22 @@
     virtual bool allowImageFilter(SkImageFilter*);
 
     /**
-     *  Override and return true for filters that the device handles
-     *  intrinsically. Returning false means call the filter.
-     *  Default impl returns false. This will only be called if allowImageFilter()
-     *  returned 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).
+     *  Returning false means the SkCanvas will have apply the filter itself,
+     *  and just pass the resulting image to the device.
      */
-    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
-                             const SkMatrix& ctm,
+    virtual bool canHandleImageFilter(SkImageFilter*);
+
+    /**
+     *  Related (but not required) to canHandleImageFilter, this method returns
+     *  true if the device could apply the filter to the src bitmap and return
+     *  the result (and updates offset as needed).
+     *  If the device does not recognize or support this filter,
+     *  it just returns false and leaves result and offset unchanged.
+     */
+    virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&,
                              SkBitmap* result, SkIPoint* offset);
 
     // This is equal kBGRA_Premul_Config8888 or kRGBA_Premul_Config8888 if
@@ -340,7 +419,13 @@
     friend class SkDraw;
     friend class SkDrawIter;
     friend class SkDeviceFilteredPaint;
-    friend class DeviceImageFilterProxy;
+    friend class SkDeviceImageFilterProxy;
+
+    friend class SkSurface_Raster;
+    // 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&);
 
     // just called by SkCanvas when built as a layer
     void setOrigin(int x, int y) { fOrigin.set(x, y); }
@@ -364,6 +449,19 @@
     SkBitmap    fBitmap;
     SkIPoint    fOrigin;
     SkMetaData* fMetaData;
+    /**
+     *  Leaky properties are those which the device should be applying but it isn't.
+     *  These properties will be applied by the draw, when and as it can.
+     *  If the device does handle a property, that property should be set to the identity value
+     *  for that property, effectively making it non-leaky.
+     */
+    SkDeviceProperties fLeakyProperties;
+
+#ifdef SK_DEBUG
+    bool        fAttachedToCanvas;
+#endif
+
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
diff --git a/include/core/SkDeviceProfile.h b/include/core/SkDeviceProfile.h
deleted file mode 100644
index 46b9781..0000000
--- a/include/core/SkDeviceProfile.h
+++ /dev/null
@@ -1,95 +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 SkDeviceProfile_DEFINED
-#define SkDeviceProfile_DEFINED
-
-#include "SkRefCnt.h"
-
-class SkDeviceProfile : public SkRefCnt {
-public:
-    enum LCDConfig {
-        kNone_LCDConfig,   // disables LCD text rendering, uses A8 instead
-        kRGB_Horizontal_LCDConfig,
-        kBGR_Horizontal_LCDConfig,
-        kRGB_Vertical_LCDConfig,
-        kBGR_Vertical_LCDConfig,
-    };
-
-    enum FontHintLevel {
-        kNone_FontHintLevel,
-        kSlight_FontHintLevel,
-        kNormal_FontHintLevel,
-        kFull_FontHintLevel,
-        kAuto_FontHintLevel,
-    };
-
-    /**
-     *  gammaExp is typically between 1.0 and 2.2. For no gamma adjustment,
-     *  specify 1.0
-     *
-     *  contrastScale will be pinned between 0.0 and 1.0. For no contrast
-     *  adjustment, specify 0.0
-     *
-     *  @param config   Describes the LCD layout for this device. If this is set
-     *                  to kNone, then all requests for LCD text will be
-     *                  devolved to A8 antialiasing.
-     *
-     *  @param level    The hinting level to be used, IF the paint specifies
-     *                  "default". Otherwise the paint's hinting level will be
-     *                  respected.
-     */
-    static SkDeviceProfile* Create(float gammaExp,
-                                   float contrastScale,
-                                   LCDConfig,
-                                   FontHintLevel);
-
-    /**
-     *  Returns the global default profile, that is used if no global profile is
-     *  specified with SetGlobal(), or if NULL is specified to SetGlobal().
-     *  The references count is *not* incremented, and the caller should not
-     *  call unref().
-     */
-    static SkDeviceProfile* GetDefault();
-
-    /**
-     *  Return the current global profile (or the default if no global had yet
-     *  been set) and increment its reference count. The call *must* call unref()
-     *  when it is done using it.
-     */
-    static SkDeviceProfile* RefGlobal();
-
-    /**
-     *  Make the specified profile be the global value for all subsequently
-     *  instantiated devices. Does not affect any existing devices.
-     *  Increments the reference count on the profile.
-     *  Specify NULL for the "identity" profile (where there is no gamma or
-     *  contrast correction).
-     */
-    static void SetGlobal(SkDeviceProfile*);
-
-    float getFontGammaExponent() const { return fGammaExponent; }
-    float getFontContrastScale() const { return fContrastScale; }
-
-    /**
-     *  Given a luminance byte (0 for black, 0xFF for white), generate a table
-     *  that applies the gamma/contrast settings to linear coverage values.
-     */
-    void generateTableForLuminanceByte(U8CPU lumByte, uint8_t table[256]) const;
-
-private:
-    SkDeviceProfile(float gammaExp, float contrastScale, LCDConfig,
-                    FontHintLevel);
-
-    float           fGammaExponent;
-    float           fContrastScale;
-    LCDConfig       fLCDConfig;
-    FontHintLevel   fFontHintLevel;
-};
-
-#endif
-
diff --git a/include/core/SkDeviceProperties.h b/include/core/SkDeviceProperties.h
new file mode 100644
index 0000000..2c2e952
--- /dev/null
+++ b/include/core/SkDeviceProperties.h
@@ -0,0 +1,112 @@
+#ifndef SkDeviceProperties_DEFINED
+#define SkDeviceProperties_DEFINED
+
+#ifndef SK_GAMMA_EXPONENT
+    #define SK_GAMMA_EXPONENT (2.2f)
+#endif
+
+#ifdef SK_GAMMA_SRGB
+    #undef SK_GAMMA_EXPONENT
+    #define SK_GAMMA_EXPONENT (0.0f)
+#endif
+
+//TODO: get everyone to stop using SkFontHost::SetSubpixel* and remove this import.
+#include "SkFontHost.h"
+
+struct SkDeviceProperties {
+    struct Geometry {
+        /** The orientation of the pixel specifies the interpretation of the
+        *  layout. If the orientation is horizontal, the layout is interpreted as
+        *  left to right. It the orientation is vertical, the layout is
+        *  interpreted top to bottom (rotated 90deg cw from horizontal).
+        */
+        enum Orientation {
+            kUnknown_Orientation      = 0x0,
+            kKnown_Orientation        = 0x2,
+
+            kHorizontal_Orientation   = 0x2,  //!< this is the default
+            kVertical_Orientation     = 0x3,
+
+            kOrientationMask          = 0x3,
+        };
+
+        /** The layout of the pixel specifies its subpixel geometry.
+        *
+        *  kUnknown_Layout means that the subpixel elements are not spatially
+        *  separated in any known or usable fashion.
+        */
+        enum Layout {
+            kUnknown_Layout   = 0x0,
+            kKnown_Layout     = 0x8,
+
+            kRGB_Layout       = 0x8,  //!< this is the default
+            kBGR_Layout       = 0xC,
+
+            kLayoutMask       = 0xC,
+        };
+
+        Orientation getOrientation() {
+            return static_cast<Orientation>(fGeometry | kOrientationMask);
+        }
+        Layout getLayout() {
+            return static_cast<Layout>(fGeometry | kLayoutMask);
+        }
+
+        bool isOrientationKnown() {
+            return SkToBool(fGeometry & kKnown_Orientation);
+        }
+        bool isLayoutKnown() {
+            return SkToBool(fGeometry & kKnown_Layout);
+        }
+
+    private:
+        //TODO: get everyone to stop using SkFontHost::SetSubpixel* and replace these calls with constants.
+        static Orientation fromOldOrientation(SkFontHost::LCDOrientation orientation) {
+            switch (orientation) {
+            case SkFontHost::kHorizontal_LCDOrientation: return kHorizontal_Orientation;
+            case SkFontHost::kVertical_LCDOrientation: return kVertical_Orientation;
+            default: return kUnknown_Orientation;
+            }
+        }
+        static Layout fromOldLayout(SkFontHost::LCDOrder order) {
+            switch (order) {
+            case SkFontHost::kRGB_LCDOrder: return kRGB_Layout;
+            case SkFontHost::kBGR_LCDOrder: return kBGR_Layout;
+            default: return kUnknown_Layout;
+            }
+        }
+    public:
+        static Geometry MakeDefault() {
+            Orientation orientation = fromOldOrientation(SkFontHost::GetSubpixelOrientation()); //kHorizontal_Orientation
+            Layout layout = fromOldLayout(SkFontHost::GetSubpixelOrder()); //kRGB_Layout
+            Geometry ret = { SkToU8(orientation | layout) };
+            return ret;
+        }
+
+        static Geometry Make(Orientation orientation, Layout layout) {
+            Geometry ret = { SkToU8(orientation | layout) };
+            return ret;
+        }
+
+        uint8_t fGeometry;
+    };
+
+    static SkDeviceProperties MakeDefault() {
+        SkDeviceProperties ret = { Geometry::MakeDefault(), SK_GAMMA_EXPONENT };
+        return ret;
+    }
+
+    static SkDeviceProperties Make(Geometry geometry, SkScalar gamma) {
+        SkDeviceProperties ret = { geometry, gamma };
+        return ret;
+    }
+
+    /** Each pixel of an image will have some number of channels.
+     *  Can the layout of those channels be exploited? */
+    Geometry fGeometry;
+
+    /** Represents the color space of the image. This is a woefully inadequate beginning. */
+    SkScalar fGamma;
+};
+
+#endif
diff --git a/include/core/SkDither.h b/include/core/SkDither.h
index 692c7e4..d82b416 100644
--- a/include/core/SkDither.h
+++ b/include/core/SkDither.h
@@ -71,7 +71,7 @@
 static inline SkPMColor SkDitherARGB32For565(SkPMColor c, unsigned dither)
 {
     SkASSERT(dither <= SK_DitherValueMax565);
-    
+
     unsigned sa = SkGetPackedA32(c);
     dither = SkAlphaMul(dither, SkAlpha255To256(sa));
 
@@ -81,21 +81,21 @@
     sr = SkDITHER_R32_FOR_565(sr, dither);
     sg = SkDITHER_G32_FOR_565(sg, dither);
     sb = SkDITHER_B32_FOR_565(sb, dither);
-    
+
     return SkPackARGB32(sa, sr, sg, sb);
 }
 
 static inline SkPMColor SkDitherRGB32For565(SkPMColor c, unsigned dither)
 {
     SkASSERT(dither <= SK_DitherValueMax565);
-    
+
     unsigned sr = SkGetPackedR32(c);
     unsigned sg = SkGetPackedG32(c);
     unsigned sb = SkGetPackedB32(c);
     sr = SkDITHER_R32_FOR_565(sr, dither);
     sg = SkDITHER_G32_FOR_565(sg, dither);
     sb = SkDITHER_B32_FOR_565(sb, dither);
-    
+
     return SkPackARGB32(0xFF, sr, sg, sb);
 }
 
@@ -112,29 +112,29 @@
 static inline uint16_t SkDitherRGB32To565(SkPMColor c, unsigned dither)
 {
     SkASSERT(dither <= SK_DitherValueMax565);
-    
+
     unsigned sr = SkGetPackedR32(c);
     unsigned sg = SkGetPackedG32(c);
     unsigned sb = SkGetPackedB32(c);
     sr = SkDITHER_R32To565(sr, dither);
     sg = SkDITHER_G32To565(sg, dither);
     sb = SkDITHER_B32To565(sb, dither);
-    
+
     return SkPackRGB16(sr, sg, sb);
 }
 
 static inline uint16_t SkDitherARGB32To565(U8CPU sa, SkPMColor c, unsigned dither)
 {
-    SkASSERT(dither <= SK_DitherValueMax565);    
+    SkASSERT(dither <= SK_DitherValueMax565);
     dither = SkAlphaMul(dither, SkAlpha255To256(sa));
-    
+
     unsigned sr = SkGetPackedR32(c);
     unsigned sg = SkGetPackedG32(c);
     unsigned sb = SkGetPackedB32(c);
     sr = SkDITHER_R32To565(sr, dither);
     sg = SkDITHER_G32To565(sg, dither);
     sb = SkDITHER_B32To565(sb, dither);
-    
+
     return SkPackRGB16(sr, sg, sb);
 }
 
@@ -149,7 +149,7 @@
     r = SkDITHER_R32To4444(r, dither);
     g = SkDITHER_G32To4444(g, dither);
     b = SkDITHER_B32To4444(b, dither);
-    
+
     return SkPackARGB4444(a, r, g, b);
 }
 
@@ -166,7 +166,7 @@
     r = SkDITHER_R32To4444(r, dither);
     g = SkDITHER_G32To4444(g, dither);
     b = SkDITHER_B32To4444(b, dither);
-    
+
     return SkPackARGB4444(a, r, g, b);
 }
 
diff --git a/include/core/SkDraw.h b/include/core/SkDraw.h
index 8c659c2..6829f8e 100644
--- a/include/core/SkDraw.h
+++ b/include/core/SkDraw.h
@@ -76,8 +76,9 @@
         solely to assist in computing the mask's bounds (if the mode requests that).
     */
     static bool DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
-                           SkMaskFilter* filter, const SkMatrix* filterMatrix,
-                           SkMask* mask, SkMask::CreateMode mode);
+                           const SkMaskFilter*, const SkMatrix* filterMatrix,
+                           SkMask* mask, SkMask::CreateMode mode,
+                           SkPaint::Style style);
 
     enum RectType {
         kHair_RectType,
@@ -103,6 +104,17 @@
     void    drawDevMask(const SkMask& mask, const SkPaint&) const;
     void    drawBitmapAsMask(const SkBitmap&, const SkPaint&) const;
 
+    /**
+     *  Return the current clip bounds, in local coordinates, with slop to account
+     *  for antialiasing or hairlines (i.e. device-bounds outset by 1, and then
+     *  run through the inverse of the matrix).
+     *
+     *  If the matrix cannot be inverted, or the current clip is empty, return
+     *  false and ignore bounds parameter.
+     */
+    bool SK_WARN_UNUSED_RESULT
+    computeConservativeLocalClipBounds(SkRect* bounds) const;
+
 public:
     const SkBitmap* fBitmap;        // required
     const SkMatrix* fMatrix;        // required
@@ -114,9 +126,6 @@
     SkBounder*      fBounder;       // optional
     SkDrawProcs*    fProcs;         // optional
 
-    const SkMatrix* fMVMatrix;      // optional
-    const SkMatrix* fExtMatrix;     // optional
-
 #ifdef SK_DEBUG
     void validate() const;
 #else
@@ -124,34 +133,4 @@
 #endif
 };
 
-class SkGlyphCache;
-
-class SkTextToPathIter {
-public:
-    SkTextToPathIter(const char text[], size_t length, const SkPaint&,
-                     bool applyStrokeAndPathEffects, bool forceLinearTextOn);
-    ~SkTextToPathIter();
-
-    const SkPaint&  getPaint() const { return fPaint; }
-    SkScalar        getPathScale() const { return fScale; }
-
-    const SkPath*   next(SkScalar* xpos);   //!< returns nil when there are no more paths
-
-private:
-    SkGlyphCache*   fCache;
-    SkPaint         fPaint;
-    SkScalar        fScale;
-    SkFixed         fPrevAdvance;
-    const char*     fText;
-    const char*     fStop;
-    SkMeasureCacheProc fGlyphCacheProc;
-
-    const SkPath*   fPath;      // returned in next
-    SkScalar        fXPos;      // accumulated xpos, returned in next
-    SkAutoKern      fAutoKern;
-    int             fXYIndex;   // cache for horizontal -vs- vertical text
-};
-
 #endif
-
-
diff --git a/include/core/SkDrawFilter.h b/include/core/SkDrawFilter.h
index 303b80e..6a50ca7 100644
--- a/include/core/SkDrawFilter.h
+++ b/include/core/SkDrawFilter.h
@@ -21,23 +21,34 @@
  *  used for the actual drawing. Note: this modification only lasts for the
  *  current draw, as a temporary copy of the paint is used.
  */
-class SkDrawFilter : public SkRefCnt {
+class SK_API SkDrawFilter : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkDrawFilter)
+
     enum Type {
         kPaint_Type,
         kPoint_Type,
         kLine_Type,
         kBitmap_Type,
         kRect_Type,
+        kOval_Type,
         kPath_Type,
-        kText_Type
+        kText_Type,
+    };
+
+    enum {
+        kTypeCount = kText_Type + 1
     };
 
     /**
      *  Called with the paint that will be used to draw the specified type.
-     *  The implementation may modify the paint as they wish.
+     *  The implementation may modify the paint as they wish. If filter()
+     *  returns false, the draw will be skipped.
      */
-    virtual void filter(SkPaint*, Type) = 0;
+    virtual bool filter(SkPaint*, Type) = 0;
+
+private:
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
diff --git a/include/core/SkDrawLooper.h b/include/core/SkDrawLooper.h
index e8265db..2faf28b 100644
--- a/include/core/SkDrawLooper.h
+++ b/include/core/SkDrawLooper.h
@@ -14,6 +14,8 @@
 
 class SkCanvas;
 class SkPaint;
+struct SkRect;
+class SkString;
 
 /** \class SkDrawLooper
     Subclasses of SkDrawLooper can be attached to a SkPaint. Where they are,
@@ -25,6 +27,8 @@
 */
 class SK_API SkDrawLooper : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkDrawLooper)
+
     /**
      *  Called right before something is being drawn. This will be followed by
      *  calls to next() until next() returns false.
@@ -44,7 +48,7 @@
      *  init() was first called.
      */
     virtual bool next(SkCanvas*, SkPaint* paint) = 0;
-    
+
     /**
      * The fast bounds functions are used to enable the paint to be culled early
      * in the drawing pipeline. If a subclass can support this feature it must
@@ -59,6 +63,8 @@
     virtual void computeFastBounds(const SkPaint& paint,
                                    const SkRect& src, SkRect* dst);
 
+    SkDEVCODE(virtual void toString(SkString* str) const = 0;)
+
 protected:
     SkDrawLooper() {}
     SkDrawLooper(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
diff --git a/include/core/SkEdgeClipper.h b/include/core/SkEdgeClipper.h
deleted file mode 100644
index a7eedd5..0000000
--- a/include/core/SkEdgeClipper.h
+++ /dev/null
@@ -1,51 +0,0 @@
-
-/*
- * Copyright 2009 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkEdgeClipper_DEFINED
-#define SkEdgeClipper_DEFINED
-
-#include "SkPath.h"
-
-/** This is basically an iterator. It is initialized with an edge and a clip,
-    and then next() is called until it returns kDone_Verb.
- */
-class SkEdgeClipper {
-public:
-    bool clipQuad(const SkPoint pts[3], const SkRect& clip);
-    bool clipCubic(const SkPoint pts[4], const SkRect& clip);
-
-    SkPath::Verb next(SkPoint pts[]);
-    
-private:
-    SkPoint*        fCurrPoint;
-    SkPath::Verb*   fCurrVerb;
-    
-    enum {
-        kMaxVerbs = 13,
-        kMaxPoints = 32
-    };
-    SkPoint         fPoints[kMaxPoints];
-    SkPath::Verb    fVerbs[kMaxVerbs];
-
-    void clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip);
-    void clipMonoCubic(const SkPoint srcPts[4], const SkRect& clip);
-    void appendVLine(SkScalar x, SkScalar y0, SkScalar y1, bool reverse);
-    void appendQuad(const SkPoint pts[3], bool reverse);
-    void appendCubic(const SkPoint pts[4], bool reverse);
-};
-
-#ifdef SK_DEBUG
-    void sk_assert_monotonic_x(const SkPoint pts[], int count);
-    void sk_assert_monotonic_y(const SkPoint pts[], int count);
-#else
-    #define sk_assert_monotonic_x(pts, count)
-    #define sk_assert_monotonic_y(pts, count)
-#endif
-
-#endif
diff --git a/include/core/SkEmptyShader.h b/include/core/SkEmptyShader.h
index bf270bf..08c131d 100644
--- a/include/core/SkEmptyShader.h
+++ b/include/core/SkEmptyShader.h
@@ -30,11 +30,11 @@
     virtual void shadeSpan16(int x, int y, uint16_t span[], int count) SK_OVERRIDE;
     virtual void shadeSpanAlpha(int x, int y, uint8_t alpha[], int count) SK_OVERRIDE;
 
-protected:
-    SkEmptyShader(SkFlattenableReadBuffer&);
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmptyShader)
 
-    virtual Factory getFactory() SK_OVERRIDE;
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+protected:
+    SkEmptyShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
 
 private:
     typedef SkShader INHERITED;
diff --git a/include/core/SkEndian.h b/include/core/SkEndian.h
index 910cf1e..01d2195 100644
--- a/include/core/SkEndian.h
+++ b/include/core/SkEndian.h
@@ -149,4 +149,3 @@
 #endif
 
 #endif
-
diff --git a/include/core/SkFDot6.h b/include/core/SkFDot6.h
deleted file mode 100644
index aa58857..0000000
--- a/include/core/SkFDot6.h
+++ /dev/null
@@ -1,58 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkFDot6_DEFINED
-#define SkFDot6_DEFINED
-
-#include "SkMath.h"
-
-typedef int32_t SkFDot6;
-
-#define SK_FDot6One         (64)
-#define SK_FDot6Half        (32)
-
-#ifdef SK_DEBUG
-    inline SkFDot6 SkIntToFDot6(S16CPU x) {
-        SkASSERT(SkToS16(x) == x);
-        return x << 6;
-    }
-#else
-    #define SkIntToFDot6(x) ((x) << 6)
-#endif
-
-#define SkFDot6Floor(x)     ((x) >> 6)
-#define SkFDot6Ceil(x)      (((x) + 63) >> 6)
-#define SkFDot6Round(x)     (((x) + 32) >> 6)
-
-#define SkFixedToFDot6(x)   ((x) >> 10)
-
-inline SkFixed SkFDot6ToFixed(SkFDot6 x) {
-    SkASSERT((x << 10 >> 10) == x);
-
-    return x << 10;
-}
-
-#ifdef SK_SCALAR_IS_FLOAT
-    #define SkScalarToFDot6(x)  (SkFDot6)((x) * 64)
-#else
-    #define SkScalarToFDot6(x)  ((x) >> 10)
-#endif
-
-inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b) {
-    SkASSERT(b != 0);
-
-    if (a == (int16_t)a) {
-        return (a << 16) / b;
-    } else {
-        return SkFixedDiv(a, b);
-    }
-}
-
-#endif
-
diff --git a/include/core/SkFixed.h b/include/core/SkFixed.h
index 0af5d9d..0f8f758 100644
--- a/include/core/SkFixed.h
+++ b/include/core/SkFixed.h
@@ -30,8 +30,7 @@
 #define SK_FixedTanPIOver8  (0x6A0A)
 #define SK_FixedRoot2Over2  (0xB505)
 
-#ifdef SK_CAN_USE_FLOAT
-    #define SkFixedToFloat(x)   ((x) * 1.5258789e-5f)
+#define SkFixedToFloat(x)   ((x) * 1.5258789e-5f)
 #if 1
     #define SkFloatToFixed(x)   ((SkFixed)((x) * SK_Fixed1))
 #else
@@ -42,10 +41,20 @@
     }
 #endif
 
-    #define SkFixedToDouble(x)  ((x) * 1.5258789e-5)
-    #define SkDoubleToFixed(x)  ((SkFixed)((x) * SK_Fixed1))
+#ifdef SK_DEBUG
+    static inline SkFixed SkFloatToFixed_Check(float x) {
+        int64_t n64 = (int64_t)(x * SK_Fixed1);
+        SkFixed n32 = (SkFixed)n64;
+        SkASSERT(n64 == n32);
+        return n32;
+    }
+#else
+    #define SkFloatToFixed_Check(x) SkFloatToFixed(x)
 #endif
 
+#define SkFixedToDouble(x)  ((x) * 1.5258789e-5)
+#define SkDoubleToFixed(x)  ((SkFixed)((x) * SK_Fixed1))
+
 /** 32 bit signed integer used to represent fractions values with 30 bits to the right of the decimal point
 */
 typedef int32_t             SkFract;
@@ -53,10 +62,8 @@
 #define Sk_FracHalf         (1 << 29)
 #define SK_FractPIOver180   (0x11DF46A)
 
-#ifdef SK_CAN_USE_FLOAT
-    #define SkFractToFloat(x)   ((float)(x) * 0.00000000093132257f)
-    #define SkFloatToFract(x)   ((SkFract)((x) * SK_Fract1))
-#endif
+#define SkFractToFloat(x)   ((float)(x) * 0.00000000093132257f)
+#define SkFloatToFract(x)   ((SkFract)((x) * SK_Fract1))
 
 /** Converts an integer to a SkFixed, asserting that the result does not overflow
     a 32 bit signed integer
@@ -183,7 +190,7 @@
     #define SkFixedSquare(a)    SkFixedSquare_longlong(a)
 #endif
 
-#if defined(__arm__) && !defined(__thumb__)
+#if defined(SK_CPU_ARM) && !defined(__thumb__)
     /* This guy does not handle NaN or other obscurities, but is faster than
        than (int)(x*65536) when we only have software floats
     */
@@ -264,4 +271,20 @@
     #define SkFixedMulAdd(x, y, a)  (SkFixedMul(x, y) + (a))
 #endif
 
+///////////////////////////////////////////////////////////////////////////////
+
+typedef int64_t SkFixed48;
+
+#define SkIntToFixed48(x)       ((SkFixed48)(x) << 48)
+#define SkFixed48ToInt(x)       ((int)((x) >> 48))
+#define SkFixedToFixed48(x)     ((SkFixed48)(x) << 32)
+#define SkFixed48ToFixed(x)     ((SkFixed)((x) >> 32))
+#define SkFloatToFixed48(x)     ((SkFixed48)((x) * (65536.0f * 65536.0f * 65536.0f)))
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkScalarToFixed48(x)    SkFloatToFixed48(x)
+#else
+    #define SkScalarToFixed48(x)    SkFixedToFixed48(x)
+#endif
+
 #endif
diff --git a/include/core/SkFlate.h b/include/core/SkFlate.h
index c111de0..e4c1417 100644
--- a/include/core/SkFlate.h
+++ b/include/core/SkFlate.h
@@ -30,19 +30,19 @@
      *  putting the result into dst.  Returns false if an error occurs.
      */
     static bool Deflate(SkStream* src, SkWStream* dst);
-    
+
     /**
      *  Use the flate compression algorithm to compress the data in src,
      *  putting the result into dst.  Returns false if an error occurs.
      */
     static bool Deflate(const void* src, size_t len, SkWStream* dst);
-    
+
     /**
      *  Use the flate compression algorithm to compress the data,
      *  putting the result into dst.  Returns false if an error occurs.
      */
     static bool Deflate(const SkData*, SkWStream* dst);
-    
+
     /** Use the flate compression algorithm to decompress the data in src,
         putting the result into dst.  Returns false if an error occurs.
      */
diff --git a/include/core/SkFlattenable.h b/include/core/SkFlattenable.h
index 34ca34e..0b21abc 100644
--- a/include/core/SkFlattenable.h
+++ b/include/core/SkFlattenable.h
@@ -11,247 +11,76 @@
 #define SkFlattenable_DEFINED
 
 #include "SkRefCnt.h"
-#include "SkBitmap.h"
-#include "SkReader32.h"
-#include "SkTDArray.h"
-#include "SkWriter32.h"
 
 class SkFlattenableReadBuffer;
 class SkFlattenableWriteBuffer;
-class SkString;
 
-#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-
-#define SK_DECLARE_FLATTENABLE_REGISTRAR() 
-
-#define SK_DEFINE_FLATTENABLE_REGISTRAR(flattenable) \
-    static SkFlattenable::Registrar g##flattenable##Reg(#flattenable, \
-                                                      flattenable::CreateProc);
-                                                      
-#define SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(flattenable)
-#define SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(flattenable) \
-    static SkFlattenable::Registrar g##flattenable##Reg(#flattenable, \
-                                                      flattenable::CreateProc);
-#define SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
-#else
-
-#define SK_DECLARE_FLATTENABLE_REGISTRAR() static void Init();
-
-#define SK_DEFINE_FLATTENABLE_REGISTRAR(flattenable) \
-    void flattenable::Init() { \
-        SkFlattenable::Registrar(#flattenable, CreateProc); \
-    }
-
-#define SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(flattenable) \
-    void flattenable::Init() {
-    
 #define SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(flattenable) \
         SkFlattenable::Registrar(#flattenable, flattenable::CreateProc);
-    
+
+#define SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP() static void InitializeFlattenables();
+
+#define SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(flattenable) \
+    void flattenable::InitializeFlattenables() {
+
 #define SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END \
     }
 
-#endif
+#define SK_DECLARE_UNFLATTENABLE_OBJECT() \
+    virtual Factory getFactory() SK_OVERRIDE { return NULL; }; \
+
+#define SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(flattenable) \
+    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; } \
+    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { \
+        return SkNEW_ARGS(flattenable, (buffer)); \
+    }
 
 /** \class SkFlattenable
- 
+
  SkFlattenable is the base class for objects that need to be flattened
  into a data stream for either transport or as part of the key to the
  font cache.
  */
 class SK_API SkFlattenable : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkFlattenable)
+
     typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&);
-    
+
     SkFlattenable() {}
-    
+
     /** Implement this to return a factory function pointer that can be called
      to recreate your class given a buffer (previously written to by your
      override of flatten().
      */
     virtual Factory getFactory() = 0;
-    /** Override this to write data specific to your subclass into the buffer,
-     being sure to call your super-class' version first. This data will later
-     be passed to your Factory function, returned by getFactory().
-     */
-    virtual void flatten(SkFlattenableWriteBuffer&);
-    
-    /** Set the string to describe the sublass and return true. If this is not
-        overridden, ignore the string param and return false.
-     */
-    virtual bool toDumpString(SkString*) const;
 
     static Factory NameToFactory(const char name[]);
     static const char* FactoryToName(Factory);
     static void Register(const char name[], Factory);
-    
+
     class Registrar {
     public:
         Registrar(const char name[], Factory factory) {
             SkFlattenable::Register(name, factory);
         }
     };
-    
+
 protected:
     SkFlattenable(SkFlattenableReadBuffer&) {}
+    /** Override this to write data specific to your subclass into the buffer,
+     being sure to call your super-class' version first. This data will later
+     be passed to your Factory function, returned by getFactory().
+     */
+    virtual void flatten(SkFlattenableWriteBuffer&) const;
 
 private:
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
     static void InitializeFlattenables();
-#endif
 
     friend class SkGraphics;
-};
+    friend class SkFlattenableWriteBuffer;
 
-// helpers for matrix and region
-
-class SkMatrix;
-extern void SkReadMatrix(SkReader32*, SkMatrix*);
-extern void SkWriteMatrix(SkWriter32*, const SkMatrix&);
-
-class SkRegion;
-extern void SkReadRegion(SkReader32*, SkRegion*);
-extern void SkWriteRegion(SkWriter32*, const SkRegion&);
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-class SkTypeface;
-
-class SkFlattenableReadBuffer : public SkReader32 {
-public:
-    SkFlattenableReadBuffer();
-    explicit SkFlattenableReadBuffer(const void* data);
-    SkFlattenableReadBuffer(const void* data, size_t size);
-    
-    void setRefCntArray(SkRefCnt* array[], int count) {
-        fRCArray = array;
-        fRCCount = count;
-    }
-    
-    void setTypefaceArray(SkTypeface* array[], int count) {
-        fTFArray = array;
-        fTFCount = count;
-    }
-
-    /**
-     *  Call this with a pre-loaded array of Factories, in the same order as
-     *  were created/written by the writer. SkPicture uses this.
-     */
-    void setFactoryPlayback(SkFlattenable::Factory array[], int count) {
-        fFactoryTDArray = NULL;
-        fFactoryArray = array;
-        fFactoryCount = count;
-    }
-
-    /**
-     *  Call this with an initially empty array, so the reader can cache each
-     *  factory it sees by name. Used by the pipe code in conjunction with
-     *  the writer's kInlineFactoryNames_Flag.
-     */
-    void setFactoryArray(SkTDArray<SkFlattenable::Factory>* array) {
-        fFactoryTDArray = array;
-        fFactoryArray = NULL;
-        fFactoryCount = 0;
-    }
-    
-    SkTypeface* readTypeface();
-    SkRefCnt* readRefCnt();
-    void* readFunctionPtr();
-    SkFlattenable* readFlattenable();
-    
-    void setPictureVersion(uint32_t version) { fPictureVersion = version; }
-    uint32_t getPictureVersion() { return fPictureVersion; }
-
-private:
-    SkRefCnt** fRCArray;
-    int        fRCCount;
-    
-    SkTypeface** fTFArray;
-    int        fTFCount;
-    
-    SkTDArray<SkFlattenable::Factory>* fFactoryTDArray;
-    SkFlattenable::Factory* fFactoryArray;
-    int                     fFactoryCount;
-    
-    uint32_t fPictureVersion;
-
-    typedef SkReader32 INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkPtrRecorder.h"
-
-/**
- *  Subclass of SkTPtrSet specialed to call ref() and unref() when the
- *  base class's incPtr() and decPtr() are called. This makes it a valid owner
- *  of each ptr, which is released when the set is reset or destroyed.
- */
-class SkRefCntSet : public SkTPtrSet<SkRefCnt*> {
-public:
-    virtual ~SkRefCntSet();
-    
-protected:
-    // overrides
-    virtual void incPtr(void*);
-    virtual void decPtr(void*);
-};
-
-class SkFactorySet : public SkTPtrSet<SkFlattenable::Factory> {};
-
-class SkFlattenableWriteBuffer : public SkWriter32 {
-public:
-    SkFlattenableWriteBuffer(size_t minSize);
-    virtual ~SkFlattenableWriteBuffer();
-
-    void writeTypeface(SkTypeface*);
-    void writeRefCnt(SkRefCnt*);
-    void writeFunctionPtr(void*);
-    void writeFlattenable(SkFlattenable* flattenable);
-    
-    SkRefCntSet* getTypefaceRecorder() const { return fTFSet; }
-    SkRefCntSet* setTypefaceRecorder(SkRefCntSet*);
-    
-    SkRefCntSet* getRefCntRecorder() const { return fRCSet; }
-    SkRefCntSet* setRefCntRecorder(SkRefCntSet*);
-    
-    SkFactorySet* getFactoryRecorder() const { return fFactorySet; }
-    SkFactorySet* setFactoryRecorder(SkFactorySet*);
-
-    enum Flags {
-        kCrossProcess_Flag       = 0x01,
-        /**
-         *  Instructs the writer to inline Factory names as there are seen the
-         *  first time (after that we store an index). The pipe code uses this.
-         */
-        kInlineFactoryNames_Flag = 0x02,
-    };
-    Flags getFlags() const { return (Flags)fFlags; }
-    void setFlags(Flags flags) { fFlags = flags; }
-    
-    bool isCrossProcess() const {
-        return SkToBool(fFlags & kCrossProcess_Flag);
-    }
-    bool inlineFactoryNames() const {
-        return SkToBool(fFlags & kInlineFactoryNames_Flag);
-    }
-
-    bool persistBitmapPixels() const {
-        return (fFlags & kCrossProcess_Flag) != 0;
-    }
-    
-    bool persistTypeface() const { return (fFlags & kCrossProcess_Flag) != 0; }
-
-private:
-    uint32_t        fFlags;
-    SkRefCntSet*    fTFSet;
-    SkRefCntSet*    fRCSet;
-    SkFactorySet*   fFactorySet;
-    
-    typedef SkWriter32 INHERITED;
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
-
diff --git a/include/core/SkFlattenableBuffers.h b/include/core/SkFlattenableBuffers.h
new file mode 100644
index 0000000..f5b853c
--- /dev/null
+++ b/include/core/SkFlattenableBuffers.h
@@ -0,0 +1,175 @@
+
+/*
+ * 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 SkFlattenableBuffers_DEFINED
+#define SkFlattenableBuffers_DEFINED
+
+#include "SkColor.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+
+class SkBitmap;
+class SkFlattenable;
+struct SkIRect;
+class SkMatrix;
+class SkOrderedReadBuffer;
+class SkOrderedWriteBuffer;
+class SkPath;
+class SkPixelRef;
+struct SkRect;
+class SkRefCnt;
+class SkRegion;
+class SkStream;
+class SkString;
+class SkTypeface;
+class SkWStream;
+
+class SkFlattenableReadBuffer {
+public:
+    SkFlattenableReadBuffer();
+    virtual ~SkFlattenableReadBuffer();
+
+    bool isOrderedBinaryBuffer() { return NULL != getOrderedBinaryBuffer(); }
+    virtual SkOrderedReadBuffer* getOrderedBinaryBuffer() { return NULL; }
+
+    enum Flags {
+        kCrossProcess_Flag      = 1 << 0,
+        kScalarIsFloat_Flag     = 1 << 1,
+        kPtrIs64Bit_Flag        = 1 << 2,
+    };
+
+    void setFlags(uint32_t flags) { fFlags = flags; }
+    uint32_t getFlags() const { return fFlags; }
+
+    bool isCrossProcess() const { return SkToBool(fFlags & kCrossProcess_Flag); }
+    bool isScalarFloat() const { return SkToBool(fFlags & kScalarIsFloat_Flag); }
+    bool isPtr64Bit() const { return SkToBool(fFlags & kPtrIs64Bit_Flag); }
+
+    // primitives
+    virtual bool readBool() = 0;
+    virtual SkColor readColor() = 0;
+    virtual SkFixed readFixed() = 0;
+    virtual int32_t readInt() = 0;
+    virtual SkScalar readScalar() = 0;
+    virtual uint32_t readUInt() = 0;
+    virtual int32_t read32() = 0;
+
+    // strings -- the caller is responsible for freeing the string contents
+    virtual char* readString() = 0;
+    virtual void* readEncodedString(size_t* length, SkPaint::TextEncoding encoding) = 0;
+
+    // common data structures
+    virtual SkFlattenable* readFlattenable() = 0;
+    virtual void readPoint(SkPoint* point) = 0;
+    virtual void readMatrix(SkMatrix* matrix) = 0;
+    virtual void readIRect(SkIRect* rect) = 0;
+    virtual void readRect(SkRect* rect) = 0;
+    virtual void readRegion(SkRegion* region) = 0;
+    virtual void readPath(SkPath* path) = 0;
+
+    // binary data and arrays
+    virtual uint32_t readByteArray(void* value) = 0;
+    virtual uint32_t readColorArray(SkColor* colors) = 0;
+    virtual uint32_t readIntArray(int32_t* values) = 0;
+    virtual uint32_t readPointArray(SkPoint* points) = 0;
+    virtual uint32_t readScalarArray(SkScalar* values) = 0;
+
+    /** This helper peeks into the buffer and reports back the length of the next array in
+     *  the buffer but does not change the state of the buffer.
+     */
+    virtual uint32_t getArrayCount() = 0;
+
+    // helper functions
+    virtual void* readFunctionPtr();
+    virtual void readPaint(SkPaint* paint);
+
+    virtual void readBitmap(SkBitmap* bitmap) = 0;
+    virtual SkTypeface* readTypeface() = 0;
+
+    // helper function for classes with const SkPoint members
+    SkPoint readPoint() {
+        SkPoint point;
+        this->readPoint(&point);
+        return point;
+    }
+
+    template <typename T> T* readFlattenableT() {
+        return static_cast<T*>(this->readFlattenable());
+    }
+
+private:
+    uint32_t fFlags;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkFlattenableWriteBuffer {
+public:
+    SkFlattenableWriteBuffer();
+    virtual ~SkFlattenableWriteBuffer();
+
+    virtual bool isOrderedBinaryBuffer() { return false; }
+    virtual SkOrderedWriteBuffer* getOrderedBinaryBuffer() { sk_throw(); return NULL; }
+
+    // primitives
+    virtual void writeByteArray(const void* data, size_t size) = 0;
+    virtual void writeBool(bool value) = 0;
+    virtual void writeFixed(SkFixed value) = 0;
+    virtual void writeScalar(SkScalar value) = 0;
+    virtual void writeScalarArray(const SkScalar* value, uint32_t count) = 0;
+    virtual void writeInt(int32_t value) = 0;
+    virtual void writeIntArray(const int32_t* value, uint32_t count) = 0;
+    virtual void writeUInt(uint32_t value) = 0;
+    virtual void write32(int32_t value) = 0; // printf in hex
+    virtual void writeString(const char* value) = 0;
+    virtual void writeEncodedString(const void* value, size_t byteLength,
+                                    SkPaint::TextEncoding encoding) = 0;
+
+    // common data structures
+    virtual void writeFlattenable(SkFlattenable* flattenable) = 0;
+    virtual void writeColor(const SkColor& color) = 0;
+    virtual void writeColorArray(const SkColor* color, uint32_t count) = 0;
+    virtual void writePoint(const SkPoint& point) = 0;
+    virtual void writePointArray(const SkPoint* points, uint32_t count) = 0;
+    virtual void writeMatrix(const SkMatrix& matrix) = 0;
+    virtual void writeIRect(const SkIRect& rect) = 0;
+    virtual void writeRect(const SkRect& rect) = 0;
+    virtual void writeRegion(const SkRegion& region) = 0;
+    virtual void writePath(const SkPath& path) = 0;
+    virtual size_t writeStream(SkStream* stream, size_t length) = 0;
+
+    // helper functions
+    virtual void writeFunctionPtr(void* ptr);
+    virtual void writePaint(const SkPaint& paint);
+
+    virtual void writeBitmap(const SkBitmap& bitmap) = 0;
+    virtual void writeTypeface(SkTypeface* typeface) = 0;
+
+    virtual bool writeToStream(SkWStream*) = 0;
+
+    enum Flags {
+        kCrossProcess_Flag               = 0x01,
+    };
+
+    uint32_t getFlags() const { return fFlags; }
+    void setFlags(uint32_t flags) { fFlags = flags; }
+
+    bool isCrossProcess() const {
+        return SkToBool(fFlags & kCrossProcess_Flag);
+    }
+
+    bool persistTypeface() const { return (fFlags & kCrossProcess_Flag) != 0; }
+
+protected:
+    // A helper function so that each subclass does not have to be a friend of SkFlattenable
+    void flattenObject(SkFlattenable* obj, SkFlattenableWriteBuffer& buffer);
+
+    uint32_t fFlags;
+};
+
+#endif
diff --git a/include/core/SkFloatBits.h b/include/core/SkFloatBits.h
index e6fc5b5..a1196ca 100644
--- a/include/core/SkFloatBits.h
+++ b/include/core/SkFloatBits.h
@@ -57,8 +57,6 @@
 SK_API int32_t SkFloatBits_toIntCeil(int32_t floatBits);
 
 
-#ifdef SK_CAN_USE_FLOAT
-
 union SkFloatIntUnion {
     float   fFloat;
     int32_t fSignBitInt;
@@ -127,8 +125,6 @@
     return SkFloatBits_toIntCeil(SkFloat2Bits(x));
 }
 
-#endif
-
 //  Scalar wrappers for float-bit routines
 
 #ifdef SK_SCALAR_IS_FLOAT
@@ -140,4 +136,3 @@
 #endif
 
 #endif
-
diff --git a/include/core/SkFloatingPoint.h b/include/core/SkFloatingPoint.h
index ee91cd9..96270b0 100644
--- a/include/core/SkFloatingPoint.h
+++ b/include/core/SkFloatingPoint.h
@@ -12,16 +12,15 @@
 
 #include "SkTypes.h"
 
-#ifdef SK_CAN_USE_FLOAT
-
 #include <math.h>
 #include <float.h>
 #include "SkFloatBits.h"
 
-// If math.h had powf(float, float), I could remove this wrapper
+// C++98 cmath std::pow seems to be the earliest portable way to get float pow.
+// However, on Linux including cmath undefines isfinite.
+// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=14608
 static inline float sk_float_pow(float base, float exp) {
-    return static_cast<float>(pow(static_cast<double>(base),
-                                  static_cast<double>(exp)));
+    return powf(base, exp);
 }
 
 static inline float sk_float_copysign(float x, float y) {
@@ -88,5 +87,11 @@
     #define sk_float_ceil2int(x)    (int)sk_float_ceil(x)
 #endif
 
-#endif
+extern const uint32_t gIEEENotANumber;
+extern const uint32_t gIEEEInfinity;
+extern const uint32_t gIEEENegativeInfinity;
+
+#define SK_FloatNaN                 (*reinterpret_cast<const float*>(&gIEEENotANumber))
+#define SK_FloatInfinity            (*reinterpret_cast<const float*>(&gIEEEInfinity))
+#define SK_FloatNegativeInfinity    (*reinterpret_cast<const float*>(&gIEEENegativeInfinity))
 #endif
diff --git a/include/core/SkFontHost.h b/include/core/SkFontHost.h
index ace08d8..050ea9f 100644
--- a/include/core/SkFontHost.h
+++ b/include/core/SkFontHost.h
@@ -10,15 +10,14 @@
 #ifndef SkFontHost_DEFINED
 #define SkFontHost_DEFINED
 
-#include "SkScalerContext.h"
 #include "SkTypeface.h"
 
 class SkDescriptor;
+class SkScalerContext;
+struct SkScalerContextRec;
 class SkStream;
 class SkWStream;
 
-typedef uint32_t SkFontTableTag;
-
 /** \class SkFontHost
 
     This class is ported to each environment. It is responsible for bridging
@@ -26,7 +25,7 @@
     platform-specific implementation that provides access to font files.
 
     One basic task is for each create (subclass of) SkTypeface, the FontHost is
-    resonsible for assigning a uniqueID. The ID should be unique for the
+    responsible for assigning a uniqueID. The ID should be unique for the
     underlying font file/data, not unique per typeface instance. Thus it is
     possible/common to request a typeface for the same font more than once
     (e.g. asking for the same font by name several times). The FontHost may
@@ -52,15 +51,13 @@
 public:
     /** Return a new, closest matching typeface given either an existing family
         (specified by a typeface in that family) or by a familyName and a
-        requested style, or by a set of Unicode codepoitns to cover in a given
-        style.
+        requested style.
         1) If familyFace is null, use familyName.
         2) If familyName is null, use data (UTF-16 to cover).
         3) If all are null, return the default font that best matches style
      */
     static SkTypeface* CreateTypeface(const SkTypeface* familyFace,
                                       const char familyName[],
-                                      const void* data, size_t bytelength,
                                       SkTypeface::Style style);
 
     /** Return a new typeface given the data buffer. If the data does not
@@ -122,12 +119,17 @@
     ///////////////////////////////////////////////////////////////////////////
 
     /** Write a unique identifier to the stream, so that the same typeface can
-        be retrieved with Deserialize().
+        be retrieved with Deserialize(). The standard format is to serialize
+        a SkFontDescriptor followed by a uint32_t length value. If the length
+        is non-zero then the following bytes (of that length) represent a
+        serialized copy of the font which can be recreated from a stream.
     */
     static void Serialize(const SkTypeface*, SkWStream*);
 
     /** Given a stream created by Serialize(), return a new typeface (like
-        CreateTypeface) or return NULL if no match is found.
+        CreateTypeface) which is either an exact match to the one serialized
+        or the best available typeface based on the data in the deserialized
+        SkFontDescriptor.
      */
     static SkTypeface* Deserialize(SkStream*);
 
@@ -159,7 +161,7 @@
      * This Android-only version of NextLogicalFont allows us to pass in an
      * entire Rec structure so that a caller can change fallback behavior
      */
-    static SkFontID NextLogicalFont(const SkScalerContext::Rec& rec);
+    static SkFontID NextLogicalFont(const SkScalerContextRec& rec);
 #endif
 
 
@@ -174,8 +176,10 @@
         the same output.
 
         A lazy (but valid) fonthost can do nothing in its FilterRec routine.
+
+        The provided typeface corresponds to the fFontID field.
      */
-    static void FilterRec(SkScalerContext::Rec* rec);
+    static void FilterRec(SkScalerContextRec* rec, SkTypeface* typeface);
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -235,28 +239,23 @@
 
     ///////////////////////////////////////////////////////////////////////////
 
-    /** DEPRECATED -- only called by SkFontHost_FreeType internally
-
-        Return NULL or a pointer to 256 bytes for the black (table[0]) and
-        white (table[1]) gamma tables.
-    */
-    static void GetGammaTables(const uint8_t* tables[2]);
-
-    ///////////////////////////////////////////////////////////////////////////
-
     /** LCDs either have their color elements arranged horizontally or
         vertically. When rendering subpixel glyphs we need to know which way
         round they are.
 
         Note, if you change this after startup, you'll need to flush the glyph
         cache because it'll have the wrong type of masks cached.
+
+        @deprecated use SkPixelGeometry instead.
     */
     enum LCDOrientation {
         kHorizontal_LCDOrientation = 0,    //!< this is the default
-        kVertical_LCDOrientation   = 1,
+        kVertical_LCDOrientation   = 1
     };
 
+    /** @deprecated set on Device creation. */
     static void SetSubpixelOrientation(LCDOrientation orientation);
+    /** @deprecated get from Device. */
     static LCDOrientation GetSubpixelOrientation();
 
     /** LCD color elements can vary in order. For subpixel text we need to know
@@ -268,14 +267,18 @@
 
         kNONE_LCDOrder means that the subpixel elements are not spatially
         separated in any usable fashion.
+
+        @deprecated use SkPixelGeometry instead.
      */
     enum LCDOrder {
         kRGB_LCDOrder = 0,    //!< this is the default
         kBGR_LCDOrder = 1,
-        kNONE_LCDOrder = 2,
+        kNONE_LCDOrder = 2
     };
 
+    /** @deprecated set on Device creation. */
     static void SetSubpixelOrder(LCDOrder order);
+    /** @deprecated get from Device. */
     static LCDOrder GetSubpixelOrder();
 
 #ifdef SK_BUILD_FOR_ANDROID
diff --git a/include/core/SkGraphics.h b/include/core/SkGraphics.h
index c89758b..87d66ad 100644
--- a/include/core/SkGraphics.h
+++ b/include/core/SkGraphics.h
@@ -12,11 +12,11 @@
 
 #include "SkTypes.h"
 
-class SkGraphics {
+class SK_API SkGraphics {
 public:
     /**
      *  Call this at process initialization time if your environment does not
-     *  permit static global initializers that execute code. Note that 
+     *  permit static global initializers that execute code. Note that
      *  Init() is not thread-safe.
      */
     static void Init();
@@ -38,7 +38,7 @@
      *  This max can be changed by calling SetFontCacheLimit().
      */
     static size_t GetFontCacheLimit();
-    
+
     /**
      *  Specify the max number of bytes that should be used by the font cache.
      *  If the cache needs to allocate more, it will purge previous entries.
@@ -49,12 +49,17 @@
     static size_t SetFontCacheLimit(size_t bytes);
 
     /**
+     *  Return the number of bytes currently used by the font cache.
+     */
+    static size_t GetFontCacheUsed();
+
+    /**
      *  For debugging purposes, this will attempt to purge the font cache. It
      *  does not change the limit, but will cause subsequent font measures and
      *  draws to be recreated, since they will no longer be in the cache.
      */
     static void PurgeFontCache();
-    
+
     /**
      *  Applications with command line options may pass optional state, such
      *  as cache sizes, here, for instance:
@@ -64,7 +69,27 @@
      *  This format is subject to change.
      */
     static void SetFlags(const char* flags);
-    
+
+    /**
+     *  Return the max number of bytes that should be used by the thread-local
+     *  font cache.
+     *  If the cache needs to allocate more, it will purge previous entries.
+     *  This max can be changed by calling SetFontCacheLimit().
+     *
+     *  If this thread has never called SetTLSFontCacheLimit, or has called it
+     *  with 0, then this thread is using the shared font cache. In that case,
+     *  this function will always return 0, and the caller may want to call
+     *  GetFontCacheLimit.
+     */
+    static size_t GetTLSFontCacheLimit();
+
+    /**
+     *  Specify the max number of bytes that should be used by the thread-local
+     *  font cache. If this value is 0, then this thread will use the shared
+     *  global font cache.
+     */
+    static void SetTLSFontCacheLimit(size_t bytes);
+
 private:
     /** This is automatically called by SkGraphics::Init(), and must be
         implemented by the host OS. This allows the host OS to register a callback
@@ -84,4 +109,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkImage.h b/include/core/SkImage.h
new file mode 100644
index 0000000..b8ebc3a
--- /dev/null
+++ b/include/core/SkImage.h
@@ -0,0 +1,100 @@
+/*
+ * 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 SkImage_DEFINED
+#define SkImage_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+
+class SkData;
+class SkCanvas;
+class SkPaint;
+class SkShader;
+class GrContext;
+class GrTexture;
+
+// need for TileMode
+#include "SkShader.h"
+
+////// EXPERIMENTAL
+
+/**
+ *  SkImage is an abstraction for drawing a rectagle of pixels, though the
+ *  particular type of image could be actually storing its data on the GPU, or
+ *  as drawing commands (picture or PDF or otherwise), ready to be played back
+ *  into another canvas.
+ *
+ *  The content of SkImage is always immutable, though the actual storage may
+ *  change, if for example that image can be re-created via encoded data or
+ *  other means.
+ */
+class SkImage : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkImage)
+
+    enum ColorType {
+        kAlpha_8_ColorType,
+        kRGB_565_ColorType,
+        kRGBA_8888_ColorType,
+        kBGRA_8888_ColorType,
+        kPMColor_ColorType,
+
+        kLastEnum_ColorType = kPMColor_ColorType
+    };
+
+    enum AlphaType {
+        kIgnore_AlphaType,
+        kOpaque_AlphaType,
+        kPremul_AlphaType,
+        kUnpremul_AlphaType,
+
+        kLastEnum_AlphaType = kUnpremul_AlphaType
+    };
+
+    struct Info {
+        int         fWidth;
+        int         fHeight;
+        ColorType   fColorType;
+        AlphaType   fAlphaType;
+    };
+
+    static SkImage* NewRasterCopy(const Info&, const void* pixels, size_t rowBytes);
+    static SkImage* NewRasterData(const Info&, SkData* pixels, size_t rowBytes);
+    static SkImage* NewEncodedData(SkData*);
+    static SkImage* NewTexture(GrTexture*);
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+    uint32_t uniqueID() const { return fUniqueID; }
+
+    SkShader*   newShaderClamp() const;
+    SkShader*   newShader(SkShader::TileMode, SkShader::TileMode) const;
+
+    void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
+
+protected:
+    SkImage(int width, int height) :
+        fWidth(width),
+        fHeight(height),
+        fUniqueID(NextUniqueID()) {
+
+        SkASSERT(width >= 0);
+        SkASSERT(height >= 0);
+    }
+
+private:
+    const int       fWidth;
+    const int       fHeight;
+    const uint32_t  fUniqueID;
+
+    static uint32_t NextUniqueID();
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkImageFilter.h b/include/core/SkImageFilter.h
index 7d7c140..5066c7b 100644
--- a/include/core/SkImageFilter.h
+++ b/include/core/SkImageFilter.h
@@ -11,9 +11,13 @@
 #include "SkFlattenable.h"
 
 class SkBitmap;
+class SkColorFilter;
 class SkDevice;
 class SkMatrix;
-struct SkPoint;
+struct SkIPoint;
+struct SkIRect;
+class GrEffectRef;
+class GrTexture;
 
 /**
  *  Experimental.
@@ -38,16 +42,20 @@
  */
 class SK_API SkImageFilter : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkImageFilter)
+
     class Proxy {
     public:
+        virtual ~Proxy() {};
+
         virtual SkDevice* createDevice(int width, int height) = 0;
-        
+        // returns true if the proxy can handle this filter natively
+        virtual bool canHandleImageFilter(SkImageFilter*) = 0;
         // returns true if the proxy handled the filter itself. if this returns
         // false then the filter's code will be called.
         virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
                                  const SkMatrix& ctm,
                                  SkBitmap* result, SkIPoint* offset) = 0;
-        virtual ~Proxy() {};
     };
 
     /**
@@ -58,7 +66,7 @@
      *  The matrix is the current matrix on the canvas.
      *
      *  Offset is the amount to translate the resulting image relative to the
-     *  src when it is drawn. 
+     *  src when it is drawn.
      *
      *  If the result image cannot be created, return false, in which case both
      *  the result and offset parameters will be ignored by the caller.
@@ -73,32 +81,71 @@
     bool filterBounds(const SkIRect& src, const SkMatrix& ctm, SkIRect* dst);
 
     /**
-     *  Experimental.
+     *  Returns true if the filter can be expressed a single-pass
+     *  GrEffect, used to process this filter on the GPU, or false if
+     *  not.
      *
-     *  If the filter can be expressed as a gaussian-blur, return true and
-     *  set the sigma to the values for horizontal and vertical.
+     *  If effect is non-NULL, a new GrEffect instance is stored
+     *  in it.  The caller assumes ownership of the stage, and it is up to the
+     *  caller to unref it.
+     *
+     *  The effect can assume its vertexCoords space maps 1-to-1 with texels
+     *  in the texture.
      */
-    virtual bool asABlur(SkSize* sigma) const;
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture*) const;
 
     /**
-     *  Experimental.
-     *
-     *  If the filter can be expressed as an erode, return true and
-     *  set the radius in X and Y.
+     *  Returns true if the filter can be processed on the GPU.  This is most
+     *  often used for multi-pass effects, where intermediate results must be
+     *  rendered to textures.  For single-pass effects, use asNewEffect().
+     *  The default implementation returns false.
      */
-    virtual bool asAnErode(SkISize* radius) const;
+    virtual bool canFilterImageGPU() const;
 
     /**
-     *  Experimental.
-     *
-     *  If the filter can be expressed as a dilation, return true and
-     *  set the radius in X and Y.
+     *  Process this image filter on the GPU.  src is the source image for
+     *  processing, as a texture-backed bitmap.  result is the destination
+     *  bitmap, which should contain a texture-backed pixelref on success.
+     *  The default implementation returns returns false and ignores the
+     *  result parameter.
      */
-    virtual bool asADilate(SkISize* radius) const;
+    virtual bool filterImageGPU(Proxy*, const SkBitmap& src, SkBitmap* result);
+
+    /**
+     *  Returns this image filter as a color filter if possible,
+     *  NULL otherwise.
+     */
+    virtual SkColorFilter* asColorFilter() const;
+
+    /**
+     *  Returns the number of inputs this filter will accept (some inputs can
+     *  be NULL).
+     */
+    int countInputs() const { return fInputCount; }
+
+    /**
+     *  Returns the input filter at a given index, or NULL if no input is
+     *  connected.  The indices used are filter-specific.
+     */
+    SkImageFilter* getInput(int i) const {
+        SkASSERT(i < fInputCount);
+        return fInputs[i];
+    }
 
 protected:
-    SkImageFilter() {}
-    explicit SkImageFilter(SkFlattenableReadBuffer& rb) : INHERITED(rb) {}
+    SkImageFilter(int inputCount, SkImageFilter** inputs);
+
+    // Convenience constructor for 1-input filters.
+    explicit SkImageFilter(SkImageFilter* input);
+
+    // Convenience constructor for 2-input filters.
+    SkImageFilter(SkImageFilter* input1, SkImageFilter* input2);
+
+    virtual ~SkImageFilter();
+
+    explicit SkImageFilter(SkFlattenableReadBuffer& rb);
+
+    virtual void flatten(SkFlattenableWriteBuffer& wb) const SK_OVERRIDE;
 
     // Default impl returns false
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
@@ -106,8 +153,15 @@
     // Default impl copies src into dst and returns true
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*);
 
+    // Return the result of processing the given input, or the source bitmap
+    // if we have no connected input at that index.
+    SkBitmap getInputResult(int index, Proxy*, const SkBitmap& src, const SkMatrix&,
+                            SkIPoint*);
+
 private:
     typedef SkFlattenable INHERITED;
+    int fInputCount;
+    SkImageFilter** fInputs;
 };
 
 #endif
diff --git a/include/core/SkInstCnt.h b/include/core/SkInstCnt.h
new file mode 100644
index 0000000..e7f752e
--- /dev/null
+++ b/include/core/SkInstCnt.h
@@ -0,0 +1,133 @@
+/*
+ * 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 SkInstCnt_DEFINED
+#define SkInstCnt_DEFINED
+
+/*
+ * The instance counting system consists of three macros that create the
+ * instance counting machinery. A class is added to the system by adding:
+ *   SK_DECLARE_INST_COUNT at the top of its declaration for derived classes
+ *   SK_DECLARE_INST_COUNT_ROOT at the top of its declaration for a root class
+ *   SK_DEFINE_INST_COUNT at the top of its .cpp file (for both kinds).
+ * At the end of an application a call to all the "root" objects'
+ * CheckInstanceCount methods should be made
+ */
+#include "SkTypes.h"
+
+#if SK_ENABLE_INST_COUNT
+#include "SkTArray.h"
+#include "SkThread_platform.h"
+
+extern bool gPrintInstCount;
+
+// The non-root classes just register themselves with their parent
+#define SK_DECLARE_INST_COUNT(className)                                    \
+    SK_DECLARE_INST_COUNT_INTERNAL(className,                               \
+                               INHERITED::AddInstChild(CheckInstanceCount);,\
+                               /**/)
+
+#define SK_DECLARE_INST_COUNT_TEMPLATE(className)                           \
+    SK_DECLARE_INST_COUNT_INTERNAL(className,                               \
+                              INHERITED::AddInstChild(CheckInstanceCount);, \
+                              typename)
+
+// The root classes registers a function to print out the memory stats when
+// the app ends
+#define SK_DECLARE_INST_COUNT_ROOT(className)                               \
+    SK_DECLARE_INST_COUNT_INTERNAL(className, atexit(exitPrint);, /**/)
+
+#define SK_DECLARE_INST_COUNT_INTERNAL(className, initStep, templateType)   \
+    class SkInstanceCountHelper {                                           \
+    public:                                                                 \
+        typedef int (*PFCheckInstCnt)(int level, bool cleanUp);             \
+        SkInstanceCountHelper() {                                           \
+            if (!gInited) {                                                 \
+                initStep                                                    \
+                gChildren = new SkTArray<PFCheckInstCnt>;                   \
+                gInited = true;                                             \
+            }                                                               \
+            sk_atomic_inc(&gInstanceCount);                                 \
+        }                                                                   \
+                                                                            \
+        SkInstanceCountHelper(const SkInstanceCountHelper& other) {         \
+            sk_atomic_inc(&gInstanceCount);                                 \
+        }                                                                   \
+                                                                            \
+        ~SkInstanceCountHelper() {                                          \
+            sk_atomic_dec(&gInstanceCount);                                 \
+        }                                                                   \
+                                                                            \
+        static int32_t gInstanceCount;                                      \
+        static bool gInited;                                                \
+        static SkTArray<PFCheckInstCnt>* gChildren;                         \
+    } fInstanceCountHelper;                                                 \
+                                                                            \
+    static int32_t GetInstanceCount() {                                     \
+        return SkInstanceCountHelper::gInstanceCount;                       \
+    }                                                                       \
+                                                                            \
+    static void exitPrint() {                                               \
+        CheckInstanceCount(0, true);                                        \
+    }                                                                       \
+                                                                            \
+    static int CheckInstanceCount(int level = 0, bool cleanUp = false) {    \
+        if (gPrintInstCount && 0 != SkInstanceCountHelper::gInstanceCount) {\
+            SkDebugf("%*c Leaked %s: %d\n",                                 \
+                     4*level, ' ', #className,                              \
+                     SkInstanceCountHelper::gInstanceCount);                \
+        }                                                                   \
+        if (NULL == SkInstanceCountHelper::gChildren) {                     \
+            return SkInstanceCountHelper::gInstanceCount;                   \
+        }                                                                   \
+        int childCount = SkInstanceCountHelper::gChildren->count();         \
+        int count = SkInstanceCountHelper::gInstanceCount;                  \
+        for (int i = 0; i < childCount; ++i) {                              \
+            count -= (*(*SkInstanceCountHelper::gChildren)[i])(level+1, cleanUp); \
+        }                                                                   \
+        SkASSERT(count >= 0);                                               \
+        if (gPrintInstCount && childCount > 0 && count > 0) {               \
+            SkDebugf("%*c Leaked ???: %d\n", 4*(level + 1), ' ', count);    \
+        }                                                                   \
+        if (cleanUp) {                                                      \
+            delete SkInstanceCountHelper::gChildren;                        \
+            SkInstanceCountHelper::gChildren = NULL;                        \
+        }                                                                   \
+        return SkInstanceCountHelper::gInstanceCount;                       \
+    }                                                                       \
+                                                                            \
+    static void AddInstChild(templateType SkInstanceCountHelper::PFCheckInstCnt \
+                                                       childCheckInstCnt) { \
+        if (CheckInstanceCount != childCheckInstCnt &&                      \
+            NULL != SkInstanceCountHelper::gChildren) {                     \
+            SkInstanceCountHelper::gChildren->push_back(childCheckInstCnt); \
+        }                                                                   \
+    }
+
+#define SK_DEFINE_INST_COUNT(className)                                     \
+    int32_t className::SkInstanceCountHelper::gInstanceCount = 0;           \
+    bool className::SkInstanceCountHelper::gInited = false;                 \
+    SkTArray<className::SkInstanceCountHelper::PFCheckInstCnt>*             \
+                        className::SkInstanceCountHelper::gChildren = NULL;
+
+#define SK_DEFINE_INST_COUNT_TEMPLATE(templateInfo, className)              \
+    templateInfo int32_t className::SkInstanceCountHelper::gInstanceCount = 0;\
+    templateInfo bool className::SkInstanceCountHelper::gInited = false;    \
+    templateInfo                                                            \
+        SkTArray<typename className::SkInstanceCountHelper::PFCheckInstCnt>*\
+                      className::SkInstanceCountHelper::gChildren = NULL;
+
+#else
+#define SK_DECLARE_INST_COUNT(className)
+#define SK_DECLARE_INST_COUNT_TEMPLATE(className)
+#define SK_DECLARE_INST_COUNT_ROOT(className)
+#define SK_DEFINE_INST_COUNT(className)
+#define SK_DEFINE_INST_COUNT_TEMPLATE(templateInfo, className)
+#endif
+
+#endif // SkInstCnt_DEFINED
diff --git a/include/core/SkLineClipper.h b/include/core/SkLineClipper.h
index 1958395..8026890 100644
--- a/include/core/SkLineClipper.h
+++ b/include/core/SkLineClipper.h
@@ -14,14 +14,15 @@
 class SkLineClipper {
 public:
     enum {
-        kMaxPoints = 4
+        kMaxPoints = 4,
+        kMaxClippedLineSegments = kMaxPoints - 1
     };
 
     /*  Clip the line pts[0]...pts[1] against clip, ignoring segments that
         lie completely above or below the clip. For portions to the left or
         right, turn those into vertical line segments that are aligned to the
         edge of the clip.
-         
+
         Return the number of line segments that result, and store the end-points
         of those segments sequentially in lines as follows:
             1st segment: lines[0]..lines[1]
@@ -34,7 +35,7 @@
     /*  Intersect the line segment against the rect. If there is a non-empty
         resulting segment, return true and set dst[] to that segment. If not,
         return false and ignore dst[].
-     
+
         ClipLine is specialized for scan-conversion, as it adds vertical
         segments on the sides to show where the line extended beyond the
         left or right sides. IntersectLine does not.
@@ -44,4 +45,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkMMapStream.h b/include/core/SkMMapStream.h
index 19ba634..a3b35f2 100644
--- a/include/core/SkMMapStream.h
+++ b/include/core/SkMMapStream.h
@@ -21,9 +21,9 @@
 private:
     void*   fAddr;
     size_t  fSize;
-    
+
     void closeMMap();
-    
+
     typedef SkMemoryStream INHERITED;
 };
 
diff --git a/include/core/SkMallocPixelRef.h b/include/core/SkMallocPixelRef.h
index 2ceb826..d35909e 100644
--- a/include/core/SkMallocPixelRef.h
+++ b/include/core/SkMallocPixelRef.h
@@ -21,36 +21,29 @@
         last owner of this pixelref is gone. If addr is NULL, sk_malloc_throw()
         is called to allocate it.
      */
-    SkMallocPixelRef(void* addr, size_t size, SkColorTable* ctable);
+    SkMallocPixelRef(void* addr, size_t size, SkColorTable* ctable, bool ownPixels = true);
     virtual ~SkMallocPixelRef();
-    
+
     //! Return the allocation size for the pixels
     size_t getSize() const { return fSize; }
     void* getAddr() const { return fStorage; }
 
-    // overrides from SkPixelRef
-    virtual void flatten(SkFlattenableWriteBuffer&) const;
-    virtual Factory getFactory() const {
-        return Create;
-    }
-    static SkPixelRef* Create(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkMallocPixelRef, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMallocPixelRef)
 
-    SK_DECLARE_PIXEL_REF_REGISTRAR()
 protected:
     // overrides from SkPixelRef
     virtual void* onLockPixels(SkColorTable**);
     virtual void onUnlockPixels();
 
     SkMallocPixelRef(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     void*           fStorage;
 
 private:
-
     size_t          fSize;
     SkColorTable*   fCTable;
+    bool            fOwnPixels;
 
     typedef SkPixelRef INHERITED;
 };
diff --git a/include/core/SkMask.h b/include/core/SkMask.h
index 3f9a114..4265595 100644
--- a/include/core/SkMask.h
+++ b/include/core/SkMask.h
@@ -136,7 +136,7 @@
     SkAutoMaskFreeImage(uint8_t* maskImage) {
         fImage = maskImage;
     }
-    
+
     ~SkAutoMaskFreeImage() {
         SkMask::FreeImage(fImage);
     }
@@ -146,4 +146,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkMaskFilter.h b/include/core/SkMaskFilter.h
index 2808de7..a874e62 100644
--- a/include/core/SkMaskFilter.h
+++ b/include/core/SkMaskFilter.h
@@ -12,6 +12,7 @@
 
 #include "SkFlattenable.h"
 #include "SkMask.h"
+#include "SkPaint.h"
 
 class SkBlitter;
 class SkBounder;
@@ -30,14 +31,16 @@
 
     Blur and emboss are implemented as subclasses of SkMaskFilter.
 */
-class SkMaskFilter : public SkFlattenable {
+class SK_API SkMaskFilter : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkMaskFilter)
+
     SkMaskFilter() {}
 
     /** Returns the format of the resulting mask that this subclass will return
         when its filterMask() method is called.
     */
-    virtual SkMask::Format getFormat() = 0;
+    virtual SkMask::Format getFormat() const = 0;
 
     /** Create a new mask by filter the src mask.
         If src.fImage == null, then do not allocate or create the dst image
@@ -53,16 +56,14 @@
         @return true if the dst mask was correctly created.
     */
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                            SkIPoint* margin);
-
-    virtual void flatten(SkFlattenableWriteBuffer& ) {}
+                            SkIPoint* margin) const;
 
     enum BlurType {
         kNone_BlurType,    //!< this maskfilter is not a blur
         kNormal_BlurType,  //!< fuzzy inside and outside
         kSolid_BlurType,   //!< solid inside, fuzzy outside
         kOuter_BlurType,   //!< nothing inside, fuzzy outside
-        kInner_BlurType,   //!< fuzzy inside, nothing outside
+        kInner_BlurType    //!< fuzzy inside, nothing outside
     };
 
     struct BlurInfo {
@@ -90,11 +91,43 @@
      *  The default impl calls filterMask with the src mask having no image,
      *  but subclasses may override this if they can compute the rect faster.
      */
-    virtual void computeFastBounds(const SkRect& src, SkRect* dest);
+    virtual void computeFastBounds(const SkRect& src, SkRect* dest) const;
 
 protected:
     // empty for now, but lets get our subclass to remember to init us for the future
-    SkMaskFilter(SkFlattenableReadBuffer&) {}
+    SkMaskFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+    enum FilterReturn {
+        kFalse_FilterReturn,
+        kTrue_FilterReturn,
+        kUnimplemented_FilterReturn
+    };
+
+    struct NinePatch {
+        SkMask      fMask;      // fBounds must have [0,0] in its top-left
+        SkIRect     fOuterRect; // width/height must be >= fMask.fBounds'
+        SkIPoint    fCenter;    // identifies center row/col for stretching
+    };
+
+    /**
+     *  Override if your subclass can filter a rect, and return the answer as
+     *  a ninepatch mask to be stretched over the returned outerRect. On success
+     *  return kTrue_FilterReturn. On failure (e.g. out of memory) return
+     *  kFalse_FilterReturn. If the normal filterMask() entry-point should be
+     *  called (the default) return kUnimplemented_FilterReturn.
+     *
+     *  By convention, the caller will take the center rol/col from the returned
+     *  mask as the slice it can replicate horizontally and vertically as we
+     *  stretch the mask to fit inside outerRect. It is an error for outerRect
+     *  to be smaller than the mask's bounds. This would imply that the width
+     *  and height of the mask should be odd. This is not required, just that
+     *  the caller will call mask.fBounds.centerX() and centerY() to find the
+     *  strips that will be replicated.
+     */
+    virtual FilterReturn filterRectsToNine(const SkRect[], int count,
+                                           const SkMatrix&,
+                                           const SkIRect& clipBounds,
+                                           NinePatch*) const;
 
 private:
     friend class SkDraw;
@@ -105,8 +138,10 @@
      This method is not exported to java.
      */
     bool filterPath(const SkPath& devPath, const SkMatrix& devMatrix,
-                    const SkRasterClip&, SkBounder*, SkBlitter* blitter);
+                    const SkRasterClip&, SkBounder*, SkBlitter* blitter,
+                    SkPaint::Style style) const;
+
+    typedef SkFlattenable INHERITED;
 };
 
 #endif
-
diff --git a/include/core/SkMath.h b/include/core/SkMath.h
index 5889103..314311f 100644
--- a/include/core/SkMath.h
+++ b/include/core/SkMath.h
@@ -12,68 +12,55 @@
 
 #include "SkTypes.h"
 
-//! Returns the number of leading zero bits (0...32)
-int SkCLZ_portable(uint32_t);
-
-/** Computes the 64bit product of a * b, and then shifts the answer down by
-    shift bits, returning the low 32bits. shift must be [0..63]
-    e.g. to perform a fixedmul, call SkMulShift(a, b, 16)
-*/
-int32_t SkMulShift(int32_t a, int32_t b, unsigned shift);
-
-/** Computes numer1 * numer2 / denom in full 64 intermediate precision.
-    It is an error for denom to be 0. There is no special handling if
-    the result overflows 32bits.
-*/
+/**
+ *  Computes numer1 * numer2 / denom in full 64 intermediate precision.
+ *  It is an error for denom to be 0. There is no special handling if
+ *  the result overflows 32bits.
+ */
 int32_t SkMulDiv(int32_t numer1, int32_t numer2, int32_t denom);
 
-/** Computes (numer1 << shift) / denom in full 64 intermediate precision.
-    It is an error for denom to be 0. There is no special handling if
-    the result overflows 32bits.
-*/
+/**
+ *  Computes (numer1 << shift) / denom in full 64 intermediate precision.
+ *  It is an error for denom to be 0. There is no special handling if
+ *  the result overflows 32bits.
+ */
 int32_t SkDivBits(int32_t numer, int32_t denom, int shift);
 
-/** Return the integer square root of value, with a bias of bitBias
-*/
+/**
+ *  Return the integer square root of value, with a bias of bitBias
+ */
 int32_t SkSqrtBits(int32_t value, int bitBias);
 
 /** Return the integer square root of n, treated as a SkFixed (16.16)
-*/
+ */
 #define SkSqrt32(n)         SkSqrtBits(n, 15)
 
-/** Return the integer cube root of value, with a bias of bitBias
+///////////////////////////////////////////////////////////////////////////////
+
+//! Returns the number of leading zero bits (0...32)
+int SkCLZ_portable(uint32_t);
+
+#if defined(SK_CPU_ARM)
+    #define SkCLZ(x)    __builtin_clz(x)
+#endif
+
+#ifndef SkCLZ
+    #define SkCLZ(x)    SkCLZ_portable(x)
+#endif
+
+/**
+ *  Returns (value < 0 ? 0 : value) efficiently (i.e. no compares or branches)
  */
-int32_t SkCubeRootBits(int32_t value, int bitBias);
-
-/** Returns -1 if n < 0, else returns 0
-*/
-#define SkExtractSign(n)    ((int32_t)(n) >> 31)
-
-/** If sign == -1, returns -n, else sign must be 0, and returns n.
-    Typically used in conjunction with SkExtractSign().
-*/
-static inline int32_t SkApplySign(int32_t n, int32_t sign) {
-    SkASSERT(sign == 0 || sign == -1);
-    return (n ^ sign) - sign;
-}
-
-/** Return x with the sign of y */
-static inline int32_t SkCopySign32(int32_t x, int32_t y) {
-    return SkApplySign(x, SkExtractSign(x ^ y));
-}
-
-/** Returns (value < 0 ? 0 : value) efficiently (i.e. no compares or branches)
-*/
 static inline int SkClampPos(int value) {
     return value & ~(value >> 31);
 }
 
 /** Given an integer and a positive (max) integer, return the value
-    pinned against 0 and max, inclusive.
-    @param value    The value we want returned pinned between [0...max]
-    @param max      The positive max value
-    @return 0 if value < 0, max if value > max, else value
-*/
+ *  pinned against 0 and max, inclusive.
+ *  @param value    The value we want returned pinned between [0...max]
+ *  @param max      The positive max value
+ *  @return 0 if value < 0, max if value > max, else value
+ */
 static inline int SkClampMax(int value, int max) {
     // ensure that max is positive
     SkASSERT(max >= 0);
@@ -86,62 +73,33 @@
     return value;
 }
 
-/** Given a positive value and a positive max, return the value
-    pinned against max.
-    Note: only works as long as max - value doesn't wrap around
-    @return max if value >= max, else value
-*/
-static inline unsigned SkClampUMax(unsigned value, unsigned max) {
-#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
-    if (value > max) {
-        value = max;
-    }
-    return value;
-#else
-    int diff = max - value;
-    // clear diff if diff is positive
-    diff &= diff >> 31;
-
-    return value + diff;
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if defined(__arm__)
-    #define SkCLZ(x)    __builtin_clz(x)
-#endif
-
-#ifndef SkCLZ
-    #define SkCLZ(x)    SkCLZ_portable(x)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-/** Returns the smallest power-of-2 that is >= the specified value. If value
-    is already a power of 2, then it is returned unchanged. It is undefined
-    if value is <= 0.
-*/
+/**
+ *  Returns the smallest power-of-2 that is >= the specified value. If value
+ *  is already a power of 2, then it is returned unchanged. It is undefined
+ *  if value is <= 0.
+ */
 static inline int SkNextPow2(int value) {
     SkASSERT(value > 0);
     return 1 << (32 - SkCLZ(value - 1));
 }
 
-/** Returns the log2 of the specified value, were that value to be rounded up
-    to the next power of 2. It is undefined to pass 0. Examples:
-         SkNextLog2(1) -> 0
-         SkNextLog2(2) -> 1
-         SkNextLog2(3) -> 2
-         SkNextLog2(4) -> 2
-         SkNextLog2(5) -> 3
-*/
+/**
+ *  Returns the log2 of the specified value, were that value to be rounded up
+ *  to the next power of 2. It is undefined to pass 0. Examples:
+ *  SkNextLog2(1) -> 0
+ *  SkNextLog2(2) -> 1
+ *  SkNextLog2(3) -> 2
+ *  SkNextLog2(4) -> 2
+ *  SkNextLog2(5) -> 3
+ */
 static inline int SkNextLog2(uint32_t value) {
     SkASSERT(value != 0);
     return 32 - SkCLZ(value - 1);
 }
 
-/** Returns true if value is a power of 2. Does not explicitly check for
-    value <= 0.
+/**
+ *  Returns true if value is a power of 2. Does not explicitly check for
+ *  value <= 0.
  */
 static inline bool SkIsPow2(int value) {
     return (value & (value - 1)) == 0;
@@ -149,14 +107,12 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-/** SkMulS16(a, b) multiplies a * b, but requires that a and b are both int16_t.
-    With this requirement, we can generate faster instructions on some
-    architectures.
-*/
-#if defined(__arm__) \
-  && !defined(__thumb__) \
-  && !defined(__ARM_ARCH_4T__) \
-  && !defined(__ARM_ARCH_5T__)
+/**
+ *  SkMulS16(a, b) multiplies a * b, but requires that a and b are both int16_t.
+ *  With this requirement, we can generate faster instructions on some
+ *  architectures.
+ */
+#ifdef SK_ARM_HAS_EDSP
     static inline int32_t SkMulS16(S16CPU x, S16CPU y) {
         SkASSERT((int16_t)x == x);
         SkASSERT((int16_t)y == y);
@@ -179,39 +135,10 @@
     #endif
 #endif
 
-/** Return a*b/255, truncating away any fractional bits. Only valid if both
-    a and b are 0..255
-*/
-static inline U8CPU SkMulDiv255Trunc(U8CPU a, U8CPU b) {
-    SkASSERT((uint8_t)a == a);
-    SkASSERT((uint8_t)b == b);
-    unsigned prod = SkMulS16(a, b) + 1;
-    return (prod + (prod >> 8)) >> 8;
-}
-
-/** Return a*b/255, rounding any fractional bits. Only valid if both
-    a and b are 0..255
+/**
+ *  Return a*b/((1 << shift) - 1), rounding any fractional bits.
+ *  Only valid if a and b are unsigned and <= 32767 and shift is > 0 and <= 8
  */
-static inline U8CPU SkMulDiv255Round(U8CPU a, U8CPU b) {
-    SkASSERT((uint8_t)a == a);
-    SkASSERT((uint8_t)b == b);
-    unsigned prod = SkMulS16(a, b) + 128;
-    return (prod + (prod >> 8)) >> 8;
-}
-
-/** Return (a*b)/255, taking the ceiling of any fractional bits. Only valid if
-    both a and b are 0..255. The expected result equals (a * b + 254) / 255.
- */
-static inline U8CPU SkMulDiv255Ceiling(U8CPU a, U8CPU b) {
-    SkASSERT((uint8_t)a == a);
-    SkASSERT((uint8_t)b == b);
-    unsigned prod = SkMulS16(a, b) + 255;
-    return (prod + (prod >> 8)) >> 8;
-}
-
-/** Return a*b/((1 << shift) - 1), rounding any fractional bits.
-    Only valid if a and b are unsigned and <= 32767 and shift is > 0 and <= 8
-*/
 static inline unsigned SkMul16ShiftRound(unsigned a, unsigned b, int shift) {
     SkASSERT(a <= 32767);
     SkASSERT(b <= 32767);
@@ -220,12 +147,15 @@
     return (prod + (prod >> shift)) >> shift;
 }
 
-/** Just the rounding step in SkDiv255Round: round(value / 255)
+/**
+ *  Return a*b/255, rounding any fractional bits. Only valid if both
+ *  a and b are 0..255
  */
-static inline unsigned SkDiv255Round(unsigned prod) {
-    prod += 128;
+static inline U8CPU SkMulDiv255Round(U8CPU a, U8CPU b) {
+    SkASSERT((uint8_t)a == a);
+    SkASSERT((uint8_t)b == b);
+    unsigned prod = SkMulS16(a, b) + 128;
     return (prod + (prod >> 8)) >> 8;
 }
 
 #endif
-
diff --git a/include/core/SkMatrix.h b/include/core/SkMatrix.h
index 8fdb818..87599d4 100644
--- a/include/core/SkMatrix.h
+++ b/include/core/SkMatrix.h
@@ -44,11 +44,12 @@
         kPerspective_Mask   = 0x08   //!< set if the matrix is in perspective
     };
 
-    /** Returns a mask bitfield describing the types of transformations
-        that the matrix will perform. This information is used by routines
-        like mapPoints, to optimize its inner loops to only perform as much
-        arithmetic as is necessary.
-    */
+    /** Returns a bitfield describing the transformations the matrix may
+        perform. The bitfield is computed conservatively, so it may include
+        false positives. For example, when kPerspective_Mask is true, all
+        other bits may be set to true even in the case of a pure perspective
+        transform.
+   */
     TypeMask getType() const {
         if (fTypeMask & kUnknown_Mask) {
             fTypeMask = this->computeTypeMask();
@@ -84,6 +85,11 @@
                         kPerspective_Mask);
     }
 
+    /** Returns true if the matrix contains only translation, rotation or uniform scale
+        Returns false if other transformation types are included or is degenerate
+     */
+    bool isSimilarity(SkScalar tol = SK_ScalarNearlyZero) const;
+
     enum {
         kMScaleX,
         kMSkewX,
@@ -95,7 +101,7 @@
         kMPersp1,
         kMPersp2
     };
-    
+
     /** Affine arrays are in column major order
         because that's how PDF and XPS like it.
      */
@@ -112,12 +118,12 @@
         SkASSERT((unsigned)index < 9);
         return fMat[index];
     }
-    
+
     SkScalar get(int index) const {
         SkASSERT((unsigned)index < 9);
         return fMat[index];
     }
-    
+
     SkScalar getScaleX() const { return fMat[kMScaleX]; }
     SkScalar getScaleY() const { return fMat[kMScaleY]; }
     SkScalar getSkewY() const { return fMat[kMSkewY]; }
@@ -162,7 +168,7 @@
         fMat[kMPersp2] = persp2;
         this->setTypeMask(kUnknown_Mask);
     }
-        
+
     /** Set the matrix to identity
     */
     void reset();
@@ -172,6 +178,8 @@
     /** Set the matrix to translate by (dx, dy).
     */
     void setTranslate(SkScalar dx, SkScalar dy);
+    void setTranslate(const SkVector& v) { this->setTranslate(v.fX, v.fY); }
+
     /** Set the matrix to scale by sx and sy, with a pivot point at (px, py).
         The pivot point is the coordinate that should remain unchanged by the
         specified transformation.
@@ -322,7 +330,7 @@
         @return true if the matrix can be represented by the rectangle mapping.
     */
     bool setRectToRect(const SkRect& src, const SkRect& dst, ScaleToFit stf);
-    
+
     /** Set the matrix such that the specified src points would map to the
         specified dst points. count must be within [0..4].
         @param src  The array of src points
@@ -336,7 +344,16 @@
         set inverse to be the inverse of this matrix. If this matrix cannot be
         inverted, ignore inverse and return false
     */
-    bool invert(SkMatrix* inverse) const;
+    bool SK_WARN_UNUSED_RESULT invert(SkMatrix* inverse) const {
+        // Allow the trivial case to be inlined.
+        if (this->isIdentity()) {
+            if (NULL != inverse) {
+                inverse->reset();
+            }
+            return true;
+        }
+        return this->invertNonIdentity(inverse);
+    }
 
     /** Fills the passed array with affine identity values
         in column major order.
@@ -374,7 +391,7 @@
     void mapPoints(SkPoint pts[], int count) const {
         this->mapPoints(pts, pts, count);
     }
-    
+
     /** Like mapPoints but with custom byte stride between the points. Stride
      *  should be a multiple of sizeof(SkScalar).
      */
@@ -460,7 +477,7 @@
         SkASSERT((mask & ~kAllMasks) == 0);
         return gMapXYProcs[mask & kAllMasks];
     }
-    
+
     MapXYProc getMapXYProc() const {
         return GetMapXYProc(this->getType());
     }
@@ -472,7 +489,7 @@
         SkASSERT((mask & ~kAllMasks) == 0);
         return gMapPtsProcs[mask & kAllMasks];
     }
-    
+
     MapPtsProc getMapPtsProc() const {
         return GetMapPtsProc(this->getType());
     }
@@ -483,32 +500,40 @@
     */
     bool fixedStepInX(SkScalar y, SkFixed* stepX, SkFixed* stepY) const;
 
-#ifdef SK_SCALAR_IS_FIXED
-    friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
-        return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) == 0;
+    /** Efficient comparison of two matrices. It distinguishes between zero and
+     *  negative zero. It will return false when the sign of zero values is the
+     *  only difference between the two matrices. It considers NaN values to be
+     *  equal to themselves. So a matrix full of NaNs is "cheap equal" to
+     *  another matrix full of NaNs iff the NaN values are bitwise identical
+     *  while according to strict the strict == test a matrix with a NaN value
+     *  is equal to nothing, including itself.
+     */
+    bool cheapEqualTo(const SkMatrix& m) const {
+        return 0 == memcmp(fMat, m.fMat, sizeof(fMat));
     }
 
-    friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
-        return memcmp(a.fMat, b.fMat, sizeof(a.fMat)) != 0;
+#ifdef SK_SCALAR_IS_FIXED
+    friend bool operator==(const SkMatrix& a, const SkMatrix& b) {
+        return a.cheapEqualTo(b);
     }
 #else
-    friend bool operator==(const SkMatrix& a, const SkMatrix& b);    
+    friend bool operator==(const SkMatrix& a, const SkMatrix& b);
+#endif
     friend bool operator!=(const SkMatrix& a, const SkMatrix& b) {
         return !(a == b);
     }
-#endif
 
     enum {
-        // flatten/unflatten will never return a value larger than this
+        // writeTo/readFromMemory will never return a value larger than this
         kMaxFlattenSize = 9 * sizeof(SkScalar) + sizeof(uint32_t)
     };
     // return the number of bytes written, whether or not buffer is null
-    uint32_t flatten(void* buffer) const;
+    uint32_t writeToMemory(void* buffer) const;
     // return the number of bytes read
-    uint32_t unflatten(const void* buffer);
-    
-    void dump() const;
-    void toDumpString(SkString*) const;
+    uint32_t readFromMemory(const void* buffer);
+
+    SkDEVCODE(void dump() const;)
+    SkDEVCODE(void toString(SkString*) const;)
 
     /**
      * Calculates the maximum stretching factor of the matrix. If the matrix has
@@ -541,8 +566,8 @@
     enum {
         /** Set if the matrix will map a rectangle to another rectangle. This
             can be true if the matrix is scale-only, or rotates a multiple of
-            90 degrees. This bit is not set if the matrix is identity.
-             
+            90 degrees.
+
             This bit will be set on identity matrices
         */
         kRectStaysRect_Mask = 0x10,
@@ -566,8 +591,8 @@
                     kRectStaysRect_Mask
     };
 
-    SkScalar        fMat[9];
-    mutable uint8_t fTypeMask;
+    SkScalar         fMat[9];
+    mutable uint32_t fTypeMask;
 
     uint8_t computeTypeMask() const;
     uint8_t computePerspectiveTypeMask() const;
@@ -575,8 +600,8 @@
     void setTypeMask(int mask) {
         // allow kUnknown or a valid mask
         SkASSERT(kUnknown_Mask == mask || (mask & kAllMasks) == mask ||
-                 ((kUnknown_Mask | kOnlyPerspectiveValid_Mask | kPerspective_Mask) & mask)
-                 == mask);
+                 ((kUnknown_Mask | kOnlyPerspectiveValid_Mask) & mask)
+                 == (kUnknown_Mask | kOnlyPerspectiveValid_Mask));
         fTypeMask = SkToU8(mask);
     }
 
@@ -608,7 +633,9 @@
         }
         return ((fTypeMask & 0xF) == 0);
     }
-    
+
+    bool SK_WARN_UNUSED_RESULT invertNonIdentity(SkMatrix* inverse) const;
+
     static bool Poly2Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
     static bool Poly3Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
     static bool Poly4Proc(const SkPoint[], SkMatrix*, const SkPoint& scale);
@@ -620,9 +647,9 @@
     static void Rot_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
     static void RotTrans_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
     static void Persp_xy(const SkMatrix&, SkScalar, SkScalar, SkPoint*);
-    
+
     static const MapXYProc gMapXYProcs[];
-    
+
     static void Identity_pts(const SkMatrix&, SkPoint[], const SkPoint[], int);
     static void Trans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
     static void Scale_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
@@ -632,7 +659,7 @@
     static void RotTrans_pts(const SkMatrix&, SkPoint dst[], const SkPoint[],
                              int count);
     static void Persp_pts(const SkMatrix&, SkPoint dst[], const SkPoint[], int);
-    
+
     static const MapPtsProc gMapPtsProcs[];
 
     friend class SkPerspIter;
diff --git a/include/core/SkMetaData.h b/include/core/SkMetaData.h
index 86f186f..5db437c 100644
--- a/include/core/SkMetaData.h
+++ b/include/core/SkMetaData.h
@@ -173,4 +173,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkOSFile.h b/include/core/SkOSFile.h
index 9f01ead..257b66a 100644
--- a/include/core/SkOSFile.h
+++ b/include/core/SkOSFile.h
@@ -7,16 +7,19 @@
  */
 
 
-// 
+// TODO: add unittests for all these operations
+
 #ifndef SkOSFile_DEFINED
 #define SkOSFile_DEFINED
 
 #include "SkString.h"
 
-#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
     #include <dirent.h>
 #endif
 
+#include <stddef.h> // ptrdiff_t
+
 struct SkFILE;
 
 enum SkFILE_Flags {
@@ -24,6 +27,12 @@
     kWrite_SkFILE_Flag  = 0x02
 };
 
+#ifdef _WIN32
+const static char SkPATH_SEPARATOR = '\\';
+#else
+const static char SkPATH_SEPARATOR = '/';
+#endif
+
 SkFILE* sk_fopen(const char path[], SkFILE_Flags);
 void    sk_fclose(SkFILE*);
 
@@ -34,10 +43,28 @@
 
 size_t  sk_fread(void* buffer, size_t byteCount, SkFILE*);
 size_t  sk_fwrite(const void* buffer, size_t byteCount, SkFILE*);
+
+char*   sk_fgets(char* str, int size, SkFILE* f);
+
 void    sk_fflush(SkFILE*);
 
-int     sk_fseek( SkFILE*, size_t, int );
-size_t  sk_ftell( SkFILE* );
+int     sk_fseek(SkFILE*, size_t, int);
+size_t  sk_ftell(SkFILE*);
+
+// Returns true if something (file, directory, ???) exists at this path.
+bool    sk_exists(const char *path);
+
+// Returns true if a directory exists at this path.
+bool    sk_isdir(const char *path);
+
+// Have we reached the end of the file?
+int sk_feof(SkFILE *);
+
+
+// Create a new directory at this path; returns true if successful.
+// If the directory already existed, this will return true.
+// Description of the error, if any, will be written to stderr.
+bool    sk_mkdir(const char* path);
 
 class SkOSFile {
 public:
@@ -58,7 +85,7 @@
 #ifdef SK_BUILD_FOR_WIN
         HANDLE      fHandle;
         uint16_t*   fPath16;
-#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
         DIR*        fDIR;
         SkString    fPath, fSuffix;
 #endif
@@ -79,4 +106,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkPaint.h b/include/core/SkPaint.h
index 8e46deb..b8ee203 100644
--- a/include/core/SkPaint.h
+++ b/include/core/SkPaint.h
@@ -1,4 +1,5 @@
 
+
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -10,19 +11,18 @@
 #ifndef SkPaint_DEFINED
 #define SkPaint_DEFINED
 
-#include "SkTypes.h"
 #include "SkColor.h"
 #include "SkDrawLooper.h"
 #include "SkXfermode.h"
-#include "SkString.h"
-
 #ifdef SK_BUILD_FOR_ANDROID
 #include "SkLanguage.h"
 #endif
 
+class SkAnnotation;
 class SkAutoGlyphCache;
 class SkColorFilter;
 class SkDescriptor;
+struct SkDeviceProperties;
 class SkFlattenableReadBuffer;
 class SkFlattenableWriteBuffer;
 struct SkGlyph;
@@ -33,6 +33,7 @@
 class SkMatrix;
 class SkPath;
 class SkPathEffect;
+struct SkPoint;
 class SkRasterizer;
 class SkShader;
 class SkTypeface;
@@ -81,7 +82,7 @@
         kNo_Hinting            = 0,
         kSlight_Hinting        = 1,
         kNormal_Hinting        = 2,     //!< this is the default
-        kFull_Hinting          = 3,
+        kFull_Hinting          = 3
     };
 
     Hinting getHinting() const {
@@ -211,7 +212,7 @@
     bool isVerticalText() const {
         return SkToBool(this->getFlags() & kVerticalText_Flag);
     }
-    
+
     /**
      *  Helper for setting or clearing the kVerticalText_Flag bit in
      *  setFlags(...).
@@ -294,7 +295,7 @@
         kStroke_Style,          //!< stroke the geometry
         kStrokeAndFill_Style,   //!< fill and stroke the geometry
 
-        kStyleCount,
+        kStyleCount
     };
 
     /** Return the paint's style, used for controlling how primitives'
@@ -428,59 +429,20 @@
     */
     void setStrokeJoin(Join join);
 
-    /** Applies any/all effects (patheffect, stroking) to src, returning the
-        result in dst. The result is that drawing src with this paint will be
-        the same as drawing dst with a default paint (at least from the
-        geometric perspective).
-        @param src  input path
-        @param dst  output path (may be the same as src)
-        @return     true if the path should be filled, or false if it should be
-                    drawn with a hairline (width == 0)
-    */
-    bool getFillPath(const SkPath& src, SkPath* dst) const;
-
-    /** Returns true if the current paint settings allow for fast computation of
-        bounds (i.e. there is nothing complex like a patheffect that would make
-        the bounds computation expensive.
-    */
-    bool canComputeFastBounds() const {
-        if (this->getLooper()) {
-            return this->getLooper()->canComputeFastBounds(*this);
-        }
-        // use bit-or since no need for early exit
-        return (reinterpret_cast<uintptr_t>(this->getRasterizer()) |
-                reinterpret_cast<uintptr_t>(this->getPathEffect())) == 0;
-    }
-
-    /** Only call this if canComputeFastBounds() returned true. This takes a
-        raw rectangle (the raw bounds of a shape), and adjusts it for stylistic
-        effects in the paint (e.g. stroking). If needed, it uses the storage
-        rect parameter. It returns the adjusted bounds that can then be used
-        for quickReject tests.
-
-        The returned rect will either be orig or storage, thus the caller
-        should not rely on storage being set to the result, but should always
-        use the retured value. It is legal for orig and storage to be the same
-        rect.
-
-        e.g.
-        if (paint.canComputeFastBounds()) {
-            SkRect r, storage;
-            path.computeBounds(&r, SkPath::kFast_BoundsType);
-            const SkRect& fastR = paint.computeFastBounds(r, &storage);
-            if (canvas->quickReject(fastR, ...)) {
-                // don't draw the path
-            }
-        }
-    */
-    const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const {
-        if (this->getStyle() == kFill_Style &&
-                !this->getLooper() && !this->getMaskFilter()) {
-            return orig;
-        }
-
-        return this->doComputeFastBounds(orig, storage);
-    }
+    /**
+     *  Applies any/all effects (patheffect, stroking) to src, returning the
+     *  result in dst. The result is that drawing src with this paint will be
+     *  the same as drawing dst with a default paint (at least from the
+     *  geometric perspective).
+     *
+     *  @param src  input path
+     *  @param dst  output path (may be the same as src)
+     *  @param cullRect If not null, the dst path may be culled to this rect.
+     *  @return     true if the path should be filled, or false if it should be
+     *              drawn with a hairline (width == 0)
+     */
+    bool getFillPath(const SkPath& src, SkPath* dst,
+                     const SkRect* cullRect = NULL) const;
 
     /** Get the paint's shader object.
         <p />
@@ -490,14 +452,21 @@
     SkShader* getShader() const { return fShader; }
 
     /** Set or clear the shader object.
-        <p />
-        Pass NULL to clear any previous shader.
-        As a convenience, the parameter passed is also returned.
-        If a previous shader exists, its reference count is decremented.
-        If shader is not NULL, its reference count is incremented.
-        @param shader   May be NULL. The shader to be installed in the paint
-        @return         shader
-    */
+     *  Shaders specify the source color(s) for what is being drawn. If a paint
+     *  has no shader, then the paint's color is used. If the paint has a
+     *  shader, then the shader's color(s) are use instead, but they are
+     *  modulated by the paint's alpha. This makes it easy to create a shader
+     *  once (e.g. bitmap tiling or gradient) and then change its transparency
+     *  w/o having to modify the original shader... only the paint's alpha needs
+     *  to be modified.
+     *  <p />
+     *  Pass NULL to clear any previous shader.
+     *  As a convenience, the parameter passed is also returned.
+     *  If a previous shader exists, its reference count is decremented.
+     *  If shader is not NULL, its reference count is incremented.
+     *  @param shader   May be NULL. The shader to be installed in the paint
+     *  @return         shader
+     */
     SkShader* setShader(SkShader* shader);
 
     /** Get the paint's colorfilter. If there is a colorfilter, its reference
@@ -623,6 +592,17 @@
     SkImageFilter* getImageFilter() const { return fImageFilter; }
     SkImageFilter* setImageFilter(SkImageFilter*);
 
+    SkAnnotation* getAnnotation() const { return fAnnotation; }
+    SkAnnotation* setAnnotation(SkAnnotation*);
+
+    /**
+     *  Returns true if there is an annotation installed on this paint, and
+     *  the annotation specifics no-drawing.
+     */
+    bool isNoDrawAnnotation() const {
+        return SkToBool(fPrivFlags & kNoDrawAnnotation_PrivFlag);
+    }
+
     /**
      *  Return the paint's SkDrawLooper (if any). Does not affect the looper's
      *  reference count.
@@ -727,12 +707,27 @@
     */
     void setTextSkewX(SkScalar skewX);
 
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+    /** Return the paint's scale factor used for correctly rendering
+        glyphs in high DPI mode without text subpixel positioning.
+        @return the scale factor used for rendering glyphs in high DPI mode.
+    */
+    SkScalar getHintingScaleFactor() const { return fHintingScaleFactor; }
+
+    /** Set the paint's scale factor used for correctly rendering
+        glyphs in high DPI mode without text subpixel positioning.
+        @param the scale factor used for rendering glyphs in high DPI mode.
+    */
+    void setHintingScaleFactor(SkScalar hintingScaleFactor);
+#endif
+
     /** Describes how to interpret the text parameters that are passed to paint
         methods like measureText() and getTextWidths().
     */
     enum TextEncoding {
         kUTF8_TextEncoding,     //!< the text parameters are UTF8
         kUTF16_TextEncoding,    //!< the text parameters are UTF16
+        kUTF32_TextEncoding,    //!< the text parameters are UTF32
         kGlyphID_TextEncoding   //!< the text parameters are glyph indices
     };
 
@@ -823,7 +818,7 @@
      *
      *  @param text     Address of the text
      *  @param length   Number of bytes of text to measure
-     *  @return         The width of the text
+     *  @return         The advance width of the text
      */
     SkScalar measureText(const void* text, size_t length) const {
         return this->measureText(text, length, NULL, 0);
@@ -845,7 +840,7 @@
     /** Return the number of bytes of text that were measured. If
      *  isVerticalText() is true, then the vertical advances are used for
      *  the measurement.
-     *  
+     *
      *  @param text     The text to be measured
      *  @param length   Number of bytes of text to measure
      *  @param maxWidth Maximum width. Only the subset of text whose accumulated
@@ -884,6 +879,9 @@
     void getTextPath(const void* text, size_t length, SkScalar x, SkScalar y,
                      SkPath* path) const;
 
+    void getPosTextPath(const void* text, size_t length,
+                        const SkPoint pos[], SkPath* path) const;
+
 #ifdef SK_BUILD_FOR_ANDROID
     const SkGlyph& getUnicharMetrics(SkUnichar, const SkMatrix*);
     const SkGlyph& getGlyphMetrics(uint16_t, const SkMatrix*);
@@ -894,20 +892,80 @@
     /** Returns the base glyph count for the strike associated with this paint
     */
     unsigned getBaseGlyphCount(SkUnichar text) const;
-    
-    int utfToGlyphs(const void* text, TextEncoding encoding,
-            size_t byteLength, uint16_t glyphs[]) const;
 #endif
 
     // returns true if the paint's settings (e.g. xfermode + alpha) resolve to
     // mean that we need not draw at all (e.g. SrcOver + 0-alpha)
     bool nothingToDraw() const;
 
+    ///////////////////////////////////////////////////////////////////////////
+    // would prefer to make these private...
+
+    /** Returns true if the current paint settings allow for fast computation of
+     bounds (i.e. there is nothing complex like a patheffect that would make
+     the bounds computation expensive.
+     */
+    bool canComputeFastBounds() const {
+        if (this->getLooper()) {
+            return this->getLooper()->canComputeFastBounds(*this);
+        }
+        return !this->getRasterizer();
+    }
+
+    /** Only call this if canComputeFastBounds() returned true. This takes a
+     raw rectangle (the raw bounds of a shape), and adjusts it for stylistic
+     effects in the paint (e.g. stroking). If needed, it uses the storage
+     rect parameter. It returns the adjusted bounds that can then be used
+     for quickReject tests.
+
+     The returned rect will either be orig or storage, thus the caller
+     should not rely on storage being set to the result, but should always
+     use the retured value. It is legal for orig and storage to be the same
+     rect.
+
+     e.g.
+     if (paint.canComputeFastBounds()) {
+     SkRect r, storage;
+     path.computeBounds(&r, SkPath::kFast_BoundsType);
+     const SkRect& fastR = paint.computeFastBounds(r, &storage);
+     if (canvas->quickReject(fastR, ...)) {
+     // don't draw the path
+     }
+     }
+     */
+    const SkRect& computeFastBounds(const SkRect& orig, SkRect* storage) const {
+        SkPaint::Style style = this->getStyle();
+        // ultra fast-case: filling with no effects that affect geometry
+        if (kFill_Style == style) {
+            uintptr_t effects = reinterpret_cast<uintptr_t>(this->getLooper());
+            effects |= reinterpret_cast<uintptr_t>(this->getMaskFilter());
+            effects |= reinterpret_cast<uintptr_t>(this->getPathEffect());
+            if (!effects) {
+                return orig;
+            }
+        }
+
+        return this->doComputeFastBounds(orig, storage, style);
+    }
+
+    const SkRect& computeFastStrokeBounds(const SkRect& orig,
+                                          SkRect* storage) const {
+        return this->doComputeFastBounds(orig, storage, kStroke_Style);
+    }
+
+    // Take the style explicitly, so the caller can force us to be stroked
+    // without having to make a copy of the paint just to change that field.
+    const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage,
+                                      Style) const;
+
 private:
     SkTypeface*     fTypeface;
     SkScalar        fTextSize;
     SkScalar        fTextScaleX;
     SkScalar        fTextSkewX;
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+    SkScalar        fHintingScaleFactor;
+#endif
 
     SkPathEffect*   fPathEffect;
     SkShader*       fShader;
@@ -917,17 +975,24 @@
     SkRasterizer*   fRasterizer;
     SkDrawLooper*   fLooper;
     SkImageFilter*  fImageFilter;
+    SkAnnotation*   fAnnotation;
 
     SkColor         fColor;
     SkScalar        fWidth;
     SkScalar        fMiterLimit;
-    unsigned        fFlags : 15;
+    // all of these bitfields should add up to 32
+    unsigned        fFlags : 16;
     unsigned        fTextAlign : 2;
     unsigned        fCapType : 2;
     unsigned        fJoinType : 2;
     unsigned        fStyle : 2;
     unsigned        fTextEncoding : 2;  // 3 values
     unsigned        fHinting : 2;
+    unsigned        fPrivFlags : 4; // these are not flattened/unflattened
+
+    enum PrivFlags {
+        kNoDrawAnnotation_PrivFlag  = 1 << 0,
+    };
 #ifdef SK_BUILD_FOR_ANDROID
     SkLanguage      fLanguage;
     FontVariant     fFontVariant;
@@ -940,13 +1005,13 @@
     SkScalar measure_text(SkGlyphCache*, const char* text, size_t length,
                           int* count, SkRect* bounds) const;
 
-    SkGlyphCache*   detachCache(const SkMatrix*) const;
+    SkGlyphCache* detachCache(const SkDeviceProperties* deviceProperties, const SkMatrix*) const;
 
-    void descriptorProc(const SkMatrix* deviceMatrix,
+    void descriptorProc(const SkDeviceProperties* deviceProperties, const SkMatrix* deviceMatrix,
                         void (*proc)(const SkDescriptor*, void*),
                         void* context, bool ignoreGamma = false) const;
 
-    const SkRect& doComputeFastBounds(const SkRect& orig, SkRect* storage) const;
+    static void Term();
 
     enum {
         kCanonicalTextSizeForPaths = 64
@@ -954,6 +1019,7 @@
     friend class SkAutoGlyphCache;
     friend class SkCanvas;
     friend class SkDraw;
+    friend class SkGraphics; // So Term() can be called.
     friend class SkPDFDevice;
     friend class SkTextToPathIter;
 
@@ -964,44 +1030,4 @@
 #endif
 };
 
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkPathEffect.h"
-
-/** \class SkStrokePathEffect
-
-    SkStrokePathEffect simulates stroking inside a patheffect, allowing the
-    caller to have explicit control of when to stroke a path. Typically this is
-    used if the caller wants to stroke before another patheffect is applied
-    (using SkComposePathEffect or SkSumPathEffect).
-*/
-class SkStrokePathEffect : public SkPathEffect {
-public:
-    SkStrokePathEffect(const SkPaint&);
-    SkStrokePathEffect(SkScalar width, SkPaint::Style, SkPaint::Join,
-                       SkPaint::Cap, SkScalar miterLimit = -1);
-
-    // overrides
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
-
-    // overrides for SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&);
-    virtual Factory getFactory();
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
-private:
-    SkScalar    fWidth, fMiter;
-    uint8_t     fStyle, fJoin, fCap;
-
-    SkStrokePathEffect(SkFlattenableReadBuffer&);
-
-    typedef SkPathEffect INHERITED;
-
-    // illegal
-    SkStrokePathEffect(const SkStrokePathEffect&);
-    SkStrokePathEffect& operator=(const SkStrokePathEffect&);
-};
-
 #endif
-
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 957d50e..3b5424f 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -10,8 +10,10 @@
 #ifndef SkPath_DEFINED
 #define SkPath_DEFINED
 
+#include "SkInstCnt.h"
 #include "SkMatrix.h"
 #include "SkTDArray.h"
+#include "SkRefCnt.h"
 
 #ifdef SK_BUILD_FOR_ANDROID
 #define GEN_ID_INC              fGenerationID++
@@ -25,6 +27,12 @@
 class SkWriter32;
 class SkAutoPathBoundsUpdate;
 class SkString;
+class SkPathRef;
+class SkRRect;
+
+#ifndef SK_DEBUG_PATH_REF
+    #define SK_DEBUG_PATH_REF 0
+#endif
 
 /** \class SkPath
 
@@ -33,13 +41,15 @@
 */
 class SK_API SkPath {
 public:
+    SK_DECLARE_INST_COUNT_ROOT(SkPath);
+
     SkPath();
     SkPath(const SkPath&);
     ~SkPath();
 
     SkPath& operator=(const SkPath&);
 
-    friend bool operator==(const SkPath&, const SkPath&);
+    friend  SK_API bool operator==(const SkPath&, const SkPath&);
     friend bool operator!=(const SkPath& a, const SkPath& b) {
         return !(a == b);
     }
@@ -79,7 +89,7 @@
     }
 
     /** Returns true if the filltype is one of the Inverse variants */
-    bool isInverseFillType() const { return (fFillType & 2) != 0; }
+    bool isInverseFillType() const { return IsInverseFillType((FillType)fFillType); }
 
     /**
      *  Toggle between inverse and normal filltypes. This reverse the return
@@ -97,15 +107,15 @@
     };
 
     /**
-     *  Return the path's convexity, as stored in the path. If it is currently
-     *  unknown, and the computeIfUnknown bool is true, then this will first
-     *  call ComputeConvexity() and then return that (cached) value.
+     *  Return the path's convexity, as stored in the path. If it is currently unknown,
+     *  then this function will attempt to compute the convexity (and cache the result).
      */
     Convexity getConvexity() const {
-        if (kUnknown_Convexity == fConvexity) {
-            fConvexity = (uint8_t)ComputeConvexity(*this);
+        if (kUnknown_Convexity != fConvexity) {
+            return static_cast<Convexity>(fConvexity);
+        } else {
+            return this->internalGetConvexity();
         }
-        return (Convexity)fConvexity;
     }
 
     /**
@@ -118,8 +128,8 @@
 
     /**
      *  Store a convexity setting in the path. There is no automatic check to
-     *  see if this value actually agress with the return value from
-     *  ComputeConvexity().
+     *  see if this value actually agrees with the return value that would be
+     *  computed by getConvexity().
      *
      *  Note: even if this is set to a "known" value, if the path is later
      *  changed (e.g. lineTo(), addRect(), etc.) then the cached value will be
@@ -128,20 +138,6 @@
     void setConvexity(Convexity);
 
     /**
-     *  Compute the convexity of the specified path. This does not look at the
-     *  value stored in the path, but computes it directly from the path's data.
-     *
-     *  This never returns kUnknown_Convexity.
-     *
-     *  If there is more than one contour, this returns kConcave_Convexity.
-     *  If the contour is degenerate (e.g. there are fewer than 3 non-degenerate
-     *  segments), then this returns kConvex_Convexity.
-     *  The contour is treated as if it were closed, even if there is no kClose
-     *  verb.
-     */
-    static Convexity ComputeConvexity(const SkPath&);
-
-    /**
      *  DEPRECATED: use getConvexity()
      *  Returns true if the path is flagged as being convex. This is not a
      *  confirmed by any analysis, it is just the value set earlier.
@@ -161,12 +157,24 @@
         this->setConvexity(isConvex ? kConvex_Convexity : kConcave_Convexity);
     }
 
+    /** Returns true if the path is an oval.
+     *
+     * @param rect      returns the bounding rect of this oval. It's a circle
+     *                  if the height and width are the same.
+     *
+     * @return true if this path is an oval.
+     *              Tracking whether a path is an oval is considered an
+     *              optimization for performance and so some paths that are in
+     *              fact ovals can report false.
+     */
+    bool isOval(SkRect* rect) const;
+
     /** Clear any lines and curves from the path, making it empty. This frees up
         internal storage associated with those segments.
         This does NOT change the fill-type setting nor isConvex
     */
     void reset();
-    
+
     /** Similar to reset(), in that all lines and curves are removed from the
         path. However, any internal storage for those lines/curves is retained,
         making reuse of the path potentially faster.
@@ -180,12 +188,23 @@
     */
     bool isEmpty() const;
 
+    /**
+     *  Returns true if all of the points in this path are finite, meaning there
+     *  are no infinities and no NaNs.
+     */
+    bool isFinite() const {
+        if (fBoundsIsDirty) {
+            this->computeBounds();
+        }
+        return SkToBool(fIsFinite);
+    }
+
     /** Test a line for zero length
 
         @return true if the line is of zero length; otherwise false.
     */
     static bool IsLineDegenerate(const SkPoint& p1, const SkPoint& p2) {
-        return p1.equalsWithinTolerance(p2, SK_ScalarNearlyZero);
+        return p1.equalsWithinTolerance(p2);
     }
 
     /** Test a quad for zero length
@@ -194,8 +213,8 @@
     */
     static bool IsQuadDegenerate(const SkPoint& p1, const SkPoint& p2,
                                  const SkPoint& p3) {
-        return p1.equalsWithinTolerance(p2, SK_ScalarNearlyZero) &&
-               p2.equalsWithinTolerance(p3, SK_ScalarNearlyZero);
+        return p1.equalsWithinTolerance(p2) &&
+               p2.equalsWithinTolerance(p3);
     }
 
     /** Test a cubic curve for zero length
@@ -204,26 +223,42 @@
     */
     static bool IsCubicDegenerate(const SkPoint& p1, const SkPoint& p2,
                                   const SkPoint& p3, const SkPoint& p4) {
-        return p1.equalsWithinTolerance(p2, SK_ScalarNearlyZero) &&
-               p2.equalsWithinTolerance(p3, SK_ScalarNearlyZero) &&
-               p3.equalsWithinTolerance(p4, SK_ScalarNearlyZero);
+        return p1.equalsWithinTolerance(p2) &&
+               p2.equalsWithinTolerance(p3) &&
+               p3.equalsWithinTolerance(p4);
     }
 
+    /**
+     *  Returns true if the path specifies a single line (i.e. it contains just
+     *  a moveTo and a lineTo). If so, and line[] is not null, it sets the 2
+     *  points in line[] to the end-points of the line. If the path is not a
+     *  line, returns false and ignores line[].
+     */
+    bool isLine(SkPoint line[2]) const;
+
     /** Returns true if the path specifies a rectangle. If so, and if rect is
         not null, set rect to the bounds of the path. If the path does not
         specify a rectangle, return false and ignore rect.
-     
+
         @param rect If not null, returns the bounds of the path if it specifies
                     a rectangle
         @return true if the path specifies a rectangle
     */
     bool isRect(SkRect* rect) const;
 
+    /** Returns true if the path specifies a pair of nested rectangles. If so, and if
+        rect is not null, set rect[0] to the outer rectangle and rect[1] to the inner
+        rectangle. If the path does not specify a pair of nested rectangles, return
+        false and ignore rect.
+
+        @param rect If not null, returns the path as a pair of nested rectangles
+        @return true if the path describes a pair of nested rectangles
+    */
+    bool isNestedRects(SkRect rect[2]) const;
+
     /** Return the number of points in the path
      */
-    int countPoints() const {
-        return this->getPoints(NULL, 0);
-    }
+    int countPoints() const;
 
     /** Return the point at the specified index. If the index is out of range
          (i.e. is not 0 <= index < countPoints()) then the returned coordinates
@@ -232,13 +267,26 @@
     SkPoint getPoint(int index) const;
 
     /** Returns the number of points in the path. Up to max points are copied.
-     
+
         @param points If not null, receives up to max points
         @param max The maximum number of points to copy into points
         @return the actual number of points in the path
     */
     int getPoints(SkPoint points[], int max) const;
 
+    /** Return the number of verbs in the path
+     */
+    int countVerbs() const;
+
+    /** Returns the number of verbs in the path. Up to max verbs are copied. The
+        verbs are copied as one byte per verb.
+
+        @param verbs If not null, receives up to max verbs
+        @param max The maximum number of verbs to copy into verbs
+        @return the actual number of verbs in the path
+    */
+    int getVerbs(uint8_t verbs[], int max) const;
+
     //! Swap contents of this and other. Guaranteed not to throw
     void swap(SkPath& other);
 
@@ -255,7 +303,7 @@
     }
 
     /** Calling this will, if the internal cache of the bounds is out of date,
-        update it so that subsequent calls to getBounds will be instanteous.
+        update it so that subsequent calls to getBounds will be instantaneous.
         This also means that any copies or simple transformations of the path
         will inherit the cached bounds.
      */
@@ -264,25 +312,33 @@
         this->getBounds();
     }
 
+    /**
+     * Does a conservative test to see whether a rectangle is inside a path. Currently it only
+     * will ever return true for single convex contour paths. The empty-status of the rect is not
+     * considered (e.g. a rect that is a point can be inside a path). Points or line segments where
+     * the rect edge touches the path border are not considered containment violations.
+     */
+    bool conservativelyContainsRect(const SkRect& rect) const;
+
     //  Construction methods
 
     /** Hint to the path to prepare for adding more points. This can allow the
         path to more efficiently grow its storage.
-    
+
         @param extraPtCount The number of extra points the path should
                             preallocate for.
     */
     void incReserve(unsigned extraPtCount);
 
     /** Set the beginning of the next contour to the point (x,y).
-     
+
         @param x    The x-coordinate of the start of a new contour
         @param y    The y-coordinate of the start of a new contour
     */
     void moveTo(SkScalar x, SkScalar y);
 
     /** Set the beginning of the next contour to the point
-     
+
         @param p    The start of a new contour
     */
     void moveTo(const SkPoint& p) {
@@ -292,7 +348,7 @@
     /** Set the beginning of the next contour relative to the last point on the
         previous contour. If there is no previous contour, this is treated the
         same as moveTo().
-     
+
         @param dx   The amount to add to the x-coordinate of the end of the
                     previous contour, to specify the start of a new contour
         @param dy   The amount to add to the y-coordinate of the end of the
@@ -322,7 +378,7 @@
     /** Same as lineTo, but the coordinates are considered relative to the last
         point on this contour. If there is no previous point, then a moveTo(0,0)
         is inserted automatically.
-     
+
         @param dx   The amount to add to the x-coordinate of the previous point
                     on this contour, to specify a line
         @param dy   The amount to add to the y-coordinate of the previous point
@@ -333,7 +389,7 @@
     /** Add a quadratic bezier from the last point, approaching control point
         (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
         this contour, the first point is automatically set to (0,0).
-     
+
         @param x1   The x-coordinate of the control point on a quadratic curve
         @param y1   The y-coordinate of the control point on a quadratic curve
         @param x2   The x-coordinate of the end point on a quadratic curve
@@ -344,7 +400,7 @@
     /** Add a quadratic bezier from the last point, approaching control point
         p1, and ending at p2. If no moveTo() call has been made for this
         contour, the first point is automatically set to (0,0).
-     
+
         @param p1   The control point on a quadratic curve
         @param p2   The end point on a quadratic curve
     */
@@ -370,7 +426,7 @@
     /** Add a cubic bezier from the last point, approaching control points
         (x1,y1) and (x2,y2), and ending at (x3,y3). If no moveTo() call has been
         made for this contour, the first point is automatically set to (0,0).
-     
+
         @param x1   The x-coordinate of the 1st control point on a cubic curve
         @param y1   The y-coordinate of the 1st control point on a cubic curve
         @param x2   The x-coordinate of the 2nd control point on a cubic curve
@@ -384,7 +440,7 @@
     /** Add a cubic bezier from the last point, approaching control points p1
         and p2, and ending at p3. If no moveTo() call has been made for this
         contour, the first point is automatically set to (0,0).
-     
+
         @param p1   The 1st control point on a cubic curve
         @param p2   The 2nd control point on a cubic curve
         @param p3   The end point on a cubic curve
@@ -396,7 +452,7 @@
     /** Same as cubicTo, but the coordinates are considered relative to the
         current point on this contour. If there is no previous point, then a
         moveTo(0,0) is inserted automatically.
-     
+
         @param dx1   The amount to add to the x-coordinate of the last point on
                 this contour, to specify the 1st control point of a cubic curve
         @param dy1   The amount to add to the y-coordinate of the last point on
@@ -418,7 +474,7 @@
         automatic lineTo() is added to connect the current contour to the start
         of the arc. However, if the path is empty, then we call moveTo() with
         the first point of the arc. The sweep angle is treated mod 360.
-     
+
         @param oval The bounding oval defining the shape and size of the arc
         @param startAngle Starting angle (in degrees) where the arc begins
         @param sweepAngle Sweep angle (in degrees) measured clockwise. This is
@@ -447,98 +503,193 @@
     void close();
 
     enum Direction {
+        /** Direction either has not been or could not be computed */
+        kUnknown_Direction,
         /** clockwise direction for adding closed contours */
         kCW_Direction,
         /** counter-clockwise direction for adding closed contours */
-        kCCW_Direction
+        kCCW_Direction,
     };
 
     /**
+     *  Return the opposite of the specified direction. kUnknown is its own
+     *  opposite.
+     */
+    static Direction OppositeDirection(Direction dir) {
+        static const Direction gOppositeDir[] = {
+            kUnknown_Direction, kCCW_Direction, kCW_Direction
+        };
+        return gOppositeDir[dir];
+    }
+
+    /**
+     *  Returns whether or not a fill type is inverted
+     *
+     *  kWinding_FillType        -> false
+     *  kEvenOdd_FillType        -> false
+     *  kInverseWinding_FillType -> true
+     *  kInverseEvenOdd_FillType -> true
+     */
+    static bool IsInverseFillType(FillType fill) {
+        SK_COMPILE_ASSERT(0 == kWinding_FillType, fill_type_mismatch);
+        SK_COMPILE_ASSERT(1 == kEvenOdd_FillType, fill_type_mismatch);
+        SK_COMPILE_ASSERT(2 == kInverseWinding_FillType, fill_type_mismatch);
+        SK_COMPILE_ASSERT(3 == kInverseEvenOdd_FillType, fill_type_mismatch);
+        return (fill & 2) != 0;
+    }
+
+    /**
+     *  Returns the equivalent non-inverted fill type to the given fill type
+     *
+     *  kWinding_FillType        -> kWinding_FillType
+     *  kEvenOdd_FillType        -> kEvenOdd_FillType
+     *  kInverseWinding_FillType -> kWinding_FillType
+     *  kInverseEvenOdd_FillType -> kEvenOdd_FillType
+     */
+    static FillType ConvertToNonInverseFillType(FillType fill) {
+        SK_COMPILE_ASSERT(0 == kWinding_FillType, fill_type_mismatch);
+        SK_COMPILE_ASSERT(1 == kEvenOdd_FillType, fill_type_mismatch);
+        SK_COMPILE_ASSERT(2 == kInverseWinding_FillType, fill_type_mismatch);
+        SK_COMPILE_ASSERT(3 == kInverseEvenOdd_FillType, fill_type_mismatch);
+        return (FillType)(fill & 1);
+    }
+
+    /**
      *  Tries to quickly compute the direction of the first non-degenerate
      *  contour. If it can be computed, return true and set dir to that
      *  direction. If it cannot be (quickly) determined, return false and ignore
-     *  the dir parameter.
+     *  the dir parameter. If the direction was determined, it is cached to make
+     *  subsequent calls return quickly.
      */
     bool cheapComputeDirection(Direction* dir) const;
 
     /**
      *  Returns true if the path's direction can be computed via
      *  cheapComputDirection() and if that computed direction matches the
-     *  specified direction.
+     *  specified direction. If dir is kUnknown, returns true if the direction
+     *  cannot be computed.
      */
     bool cheapIsDirection(Direction dir) const {
-        Direction computedDir;
-        return this->cheapComputeDirection(&computedDir) && computedDir == dir;
+        Direction computedDir = kUnknown_Direction;
+        (void)this->cheapComputeDirection(&computedDir);
+        return computedDir == dir;
     }
 
-    /** Add a closed rectangle contour to the path
-        @param rect The rectangle to add as a closed contour to the path
-        @param dir  The direction to wind the rectangle's contour
+    /** Returns true if the path specifies a rectangle. If so, and if isClosed is
+        not null, set isClosed to true if the path is closed. Also, if returning true
+        and direction is not null, return the rect direction. If the path does not
+        specify a rectangle, return false and ignore isClosed and direction.
+
+        @param isClosed If not null, set to true if the path is closed
+        @param direction If not null, set to the rectangle's direction
+        @return true if the path specifies a rectangle
     */
+    bool isRect(bool* isClosed, Direction* direction) const;
+
+    /**
+     *  Add a closed rectangle contour to the path
+     *  @param rect The rectangle to add as a closed contour to the path
+     *  @param dir  The direction to wind the rectangle's contour. Cannot be
+     *              kUnknown_Direction.
+     */
     void    addRect(const SkRect& rect, Direction dir = kCW_Direction);
 
-    /** Add a closed rectangle contour to the path
-
-        @param left     The left side of a rectangle to add as a closed contour
-                        to the path
-        @param top      The top of a rectangle to add as a closed contour to the
-                        path
-        @param right    The right side of a rectangle to add as a closed contour
-                        to the path
-        @param bottom   The bottom of a rectangle to add as a closed contour to
-                        the path
-        @param dir      The direction to wind the rectangle's contour
-    */
+    /**
+     *  Add a closed rectangle contour to the path
+     *
+     *  @param left     The left side of a rectangle to add as a closed contour
+     *                  to the path
+     *  @param top      The top of a rectangle to add as a closed contour to the
+     *                  path
+     *  @param right    The right side of a rectangle to add as a closed contour
+     *                  to the path
+     *  @param bottom   The bottom of a rectangle to add as a closed contour to
+     *                  the path
+     *  @param dir  The direction to wind the rectangle's contour. Cannot be
+     *              kUnknown_Direction.
+     */
     void addRect(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom,
                  Direction dir = kCW_Direction);
 
-    /** Add a closed oval contour to the path
-
-        @param oval The bounding oval to add as a closed contour to the path
-        @param dir  The direction to wind the oval's contour
-    */
+    /**
+     *  Add a closed oval contour to the path
+     *
+     *  @param oval The bounding oval to add as a closed contour to the path
+     *  @param dir  The direction to wind the oval's contour. Cannot be
+     *              kUnknown_Direction.
+     */
     void addOval(const SkRect& oval, Direction dir = kCW_Direction);
 
-    /** Add a closed circle contour to the path
-
-        @param x        The x-coordinate of the center of a circle to add as a
-                        closed contour to the path
-        @param y        The y-coordinate of the center of a circle to add as a
-                        closed contour to the path
-        @param radius   The radius of a circle to add as a closed contour to the
-                        path
-        @param dir      The direction to wind the circle's contour
-    */
+    /**
+     *  Add a closed circle contour to the path
+     *
+     *  @param x        The x-coordinate of the center of a circle to add as a
+     *                  closed contour to the path
+     *  @param y        The y-coordinate of the center of a circle to add as a
+     *                  closed contour to the path
+     *  @param radius   The radius of a circle to add as a closed contour to the
+     *                  path
+     *  @param dir  The direction to wind the circle's contour. Cannot be
+     *              kUnknown_Direction.
+     */
     void addCircle(SkScalar x, SkScalar y, SkScalar radius,
                    Direction dir = kCW_Direction);
 
     /** Add the specified arc to the path as a new contour.
-     
+
         @param oval The bounds of oval used to define the size of the arc
         @param startAngle Starting angle (in degrees) where the arc begins
         @param sweepAngle Sweep angle (in degrees) measured clockwise
     */
     void addArc(const SkRect& oval, SkScalar startAngle, SkScalar sweepAngle);
 
-    /** Add a closed round-rectangle contour to the path
-        @param rect The bounds of a round-rectangle to add as a closed contour
-        @param rx   The x-radius of the rounded corners on the round-rectangle
-        @param ry   The y-radius of the rounded corners on the round-rectangle
-        @param dir  The direction to wind the round-rectangle's contour
-    */
+    /**
+     *  Add a closed round-rectangle contour to the path
+     *  @param rect The bounds of a round-rectangle to add as a closed contour
+     *  @param rx   The x-radius of the rounded corners on the round-rectangle
+     *  @param ry   The y-radius of the rounded corners on the round-rectangle
+     *  @param dir  The direction to wind the rectangle's contour. Cannot be
+     *              kUnknown_Direction.
+     */
     void    addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
                          Direction dir = kCW_Direction);
 
-    /** Add a closed round-rectangle contour to the path. Each corner receives
-        two radius values [X, Y]. The corners are ordered top-left, top-right,
-        bottom-right, bottom-left.
-        @param rect The bounds of a round-rectangle to add as a closed contour
-        @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
-        @param dir  The direction to wind the round-rectangle's contour
-        */
+    /**
+     *  Add a closed round-rectangle contour to the path. Each corner receives
+     *  two radius values [X, Y]. The corners are ordered top-left, top-right,
+     *  bottom-right, bottom-left.
+     *  @param rect The bounds of a round-rectangle to add as a closed contour
+     *  @param radii Array of 8 scalars, 4 [X,Y] pairs for each corner
+     *  @param dir  The direction to wind the rectangle's contour. Cannot be
+     *              kUnknown_Direction.
+     * Note: The radii here now go through the same constraint handling as the
+     *       SkRRect radii (i.e., either radii at a corner being 0 implies a
+     *       sqaure corner and oversized radii are proportionally scaled down).
+     */
     void addRoundRect(const SkRect& rect, const SkScalar radii[],
                       Direction dir = kCW_Direction);
 
+    /**
+     *  Add an SkRRect contour to the path
+     *  @param rrect The rounded rect to add as a closed contour
+     *  @param dir   The winding direction for the new contour. Cannot be
+     *               kUnknown_Direction.
+     */
+    void addRRect(const SkRRect& rrect, Direction dir = kCW_Direction);
+
+    /**
+     *  Add a new contour made of just lines. This is just a fast version of
+     *  the following:
+     *      this->moveTo(pts[0]);
+     *      for (int i = 1; i < count; ++i) {
+     *          this->lineTo(pts[i]);
+     *      }
+     *      if (close) {
+     *          this->close();
+     *      }
+     */
+    void addPoly(const SkPoint pts[], int count, bool close);
+
     /** Add a copy of src to the path, offset by (dx,dy)
         @param src  The path to add as a new contour
         @param dx   The amount to translate the path in X as it is added
@@ -565,17 +716,17 @@
     void reverseAddPath(const SkPath& src);
 
     /** Offset the path by (dx,dy), returning true on success
-     
-        @param dx   The amount in the X direction to offset the entire path 
-        @param dy   The amount in the Y direction to offset the entire path 
+
+        @param dx   The amount in the X direction to offset the entire path
+        @param dy   The amount in the Y direction to offset the entire path
         @param dst  The translated path is written here
     */
     void offset(SkScalar dx, SkScalar dy, SkPath* dst) const;
 
     /** Offset the path by (dx,dy), returning true on success
-     
-        @param dx   The amount in the X direction to offset the entire path 
-        @param dy   The amount in the Y direction to offset the entire path 
+
+        @param dx   The amount in the X direction to offset the entire path
+        @param dy   The amount in the Y direction to offset the entire path
     */
     void offset(SkScalar dx, SkScalar dy) {
         this->offset(dx, dy, this);
@@ -583,7 +734,7 @@
 
     /** Transform the points in this path by matrix, and write the answer into
         dst.
-     
+
         @param matrix   The matrix to apply to the path
         @param dst      The transformed path is written here
     */
@@ -600,14 +751,14 @@
     /** Return the last point on the path. If no points have been added, (0,0)
         is returned. If there are no points, this returns false, otherwise it
         returns true.
-     
+
         @param lastPt   The last point on the path is returned here
     */
     bool getLastPt(SkPoint* lastPt) const;
 
     /** Set the last point on the path. If no points have been added,
         moveTo(x,y) is automatically called.
-     
+
         @param x    The new x-coordinate for the last point
         @param y    The new y-coordinate for the last point
     */
@@ -664,9 +815,16 @@
             segments have been visited, return kDone_Verb.
 
             @param  pts The points representing the current verb and/or segment
+            @param doConsumeDegerates If true, first scan for segments that are
+                   deemed degenerate (too short) and skip those.
             @return The verb for the current segment
         */
-        Verb next(SkPoint pts[4]);
+        Verb next(SkPoint pts[4], bool doConsumeDegerates = true) {
+            if (doConsumeDegerates) {
+                this->consumeDegenerateSegments();
+            }
+            return this->doNext(pts);
+        }
 
         /** If next() returns kLine_Verb, then this query returns true if the
             line was the result of a close() command (i.e. the end point is the
@@ -694,9 +852,10 @@
         SkBool8         fCloseLine;
         SkBool8         fSegmentState;
 
-        bool cons_moveTo(SkPoint pts[1]);
+        inline const SkPoint& cons_moveTo();
         Verb autoClose(SkPoint pts[2]);
         void consumeDegenerateSegments();
+        Verb doNext(SkPoint pts[4]);
     };
 
     /** Iterate through the verbs in the path, providing the associated points.
@@ -710,8 +869,9 @@
 
         /** Return the next verb in this iteration of the path. When all
             segments have been visited, return kDone_Verb.
-         
+
             @param  pts The points representing the current verb and/or segment
+                        This must not be NULL.
             @return The verb for the current segment
         */
         Verb next(SkPoint pts[4]);
@@ -724,11 +884,25 @@
         SkPoint         fLastPt;
     };
 
+    /**
+     *  Returns true if the point { x, y } is contained by the path, taking into
+     *  account the FillType.
+     */
+    bool contains(SkScalar x, SkScalar y) const;
+
     void dump(bool forceClose, const char title[] = NULL) const;
     void dump() const;
 
-    void flatten(SkWriter32&) const;
-    void unflatten(SkReader32&);
+    /**
+     *  Write the region to the buffer, and return the number of bytes written.
+     *  If buffer is NULL, it still returns the number of bytes.
+     */
+    uint32_t writeToMemory(void* buffer) const;
+    /**
+     *  Initialized the region from the buffer, returning the number
+     *  of bytes actually read.
+     */
+    uint32_t readFromMemory(const void* buffer);
 
 #ifdef SK_BUILD_FOR_ANDROID
     uint32_t getGenerationID() const;
@@ -739,14 +913,49 @@
     SkDEBUGCODE(void validate() const;)
 
 private:
-    SkTDArray<SkPoint>  fPts;
-    SkTDArray<uint8_t>  fVerbs;
+    enum SerializationOffsets {
+        kDirection_SerializationShift = 26, // requires 2 bits
+        kIsFinite_SerializationShift = 25,  // requires 1 bit
+        kIsOval_SerializationShift = 24,    // requires 1 bit
+        kConvexity_SerializationShift = 16, // requires 2 bits
+        kFillType_SerializationShift = 8,   // requires 2 bits
+        kSegmentMask_SerializationShift = 0 // requires 3 bits
+    };
+
+#if SK_DEBUG_PATH_REF
+public:
+    /** Debugging wrapper for SkAutoTUnref<SkPathRef> used to track owners (SkPaths)
+        of SkPathRefs */
+    class PathRefDebugRef {
+    public:
+        PathRefDebugRef(SkPath* owner);
+        PathRefDebugRef(SkPathRef* pr, SkPath* owner);
+        ~PathRefDebugRef();
+        void reset(SkPathRef* ref);
+        void swap(PathRefDebugRef* other);
+        SkPathRef* get() const;
+        SkAutoTUnref<SkPathRef>::BlockRefType *operator->() const;
+        operator SkPathRef*();
+    private:
+        SkAutoTUnref<SkPathRef>   fPathRef;
+        SkPath*                   fOwner;
+    };
+
+private:
+    PathRefDebugRef     fPathRef;
+#else
+    SkAutoTUnref<SkPathRef> fPathRef;
+#endif
+
     mutable SkRect      fBounds;
     int                 fLastMoveToIndex;
     uint8_t             fFillType;
     uint8_t             fSegmentMask;
     mutable uint8_t     fBoundsIsDirty;
     mutable uint8_t     fConvexity;
+    mutable uint8_t     fDirection;
+    mutable SkBool8     fIsFinite;    // only meaningful if bounds are valid
+    mutable SkBool8     fIsOval;
 #ifdef SK_BUILD_FOR_ANDROID
     uint32_t            fGenerationID;
     const SkPath*       fSourcePath;
@@ -778,7 +987,17 @@
     //
     inline void injectMoveToIfNeeded();
 
+    inline bool hasOnlyMoveTos() const;
+
+    Convexity internalGetConvexity() const;
+
+    bool isRectContour(bool allowPartial, int* currVerb, const SkPoint** pts,
+                       bool* isClosed, Direction* direction) const;
+
     friend class SkAutoPathBoundsUpdate;
+    friend class SkAutoDisableOvalCheck;
+    friend class SkAutoDisableDirectionCheck;
+    friend class SkBench_AddPathTest; // perf test pathTo/reversePathTo
 };
 
 #endif
diff --git a/include/core/SkPathEffect.h b/include/core/SkPathEffect.h
index 1b4cd5f..3b4541d 100644
--- a/include/core/SkPathEffect.h
+++ b/include/core/SkPathEffect.h
@@ -11,6 +11,11 @@
 #define SkPathEffect_DEFINED
 
 #include "SkFlattenable.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkStrokeRec.h"
+#include "SkTDArray.h"
 
 class SkPath;
 
@@ -24,21 +29,92 @@
 */
 class SK_API SkPathEffect : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkPathEffect)
+
     SkPathEffect() {}
 
-    /** Given a src path and a width value, return true if the patheffect
-        has produced a new path (dst) and a new width value. If false is returned,
-        ignore dst and width.
-        On input, width >= 0 means the src should be stroked
-        On output, width >= 0 means the dst should be stroked
-    */
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) = 0;
+    /**
+     *  Given a src path (input) and a stroke-rec (input and output), apply
+     *  this effect to the src path, returning the new path in dst, and return
+     *  true. If this effect cannot be applied, return false and ignore dst
+     *  and stroke-rec.
+     *
+     *  The stroke-rec specifies the initial request for stroking (if any).
+     *  The effect can treat this as input only, or it can choose to change
+     *  the rec as well. For example, the effect can decide to change the
+     *  stroke's width or join, or the effect can change the rec from stroke
+     *  to fill (or fill to stroke) in addition to returning a new (dst) path.
+     *
+     *  If this method returns true, the caller will apply (as needed) the
+     *  resulting stroke-rec to dst and then draw.
+     */
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect* cullR) const = 0;
 
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    /**
+     *  Compute a conservative bounds for its effect, given the src bounds.
+     *  The baseline implementation just assigns src to dst.
+     */
+    virtual void computeFastBounds(SkRect* dst, const SkRect& src) const;
+
+    /** \class PointData
+
+        PointData aggregates all the information needed to draw the point
+        primitives returned by an 'asPoints' call.
+    */
+    class PointData {
+    public:
+        PointData()
+            : fFlags(0)
+            , fPoints(NULL)
+            , fNumPoints(0) {
+            fSize.set(SK_Scalar1, SK_Scalar1);
+            // 'asPoints' needs to initialize/fill-in 'fClipRect' if it sets
+            // the kUseClip flag
+        };
+        ~PointData() {
+            delete [] fPoints;
+        }
+
+        // TODO: consider using passed-in flags to limit the work asPoints does.
+        // For example, a kNoPath flag could indicate don't bother generating
+        // stamped solutions.
+
+        // Currently none of these flags are supported.
+        enum PointFlags {
+            kCircles_PointFlag            = 0x01,   // draw points as circles (instead of rects)
+            kUsePath_PointFlag            = 0x02,   // draw points as stamps of the returned path
+            kUseClip_PointFlag            = 0x04,   // apply 'fClipRect' before drawing the points
+        };
+
+        uint32_t           fFlags;      // flags that impact the drawing of the points
+        SkPoint*           fPoints;     // the center point of each generated point
+        int                fNumPoints;  // number of points in fPoints
+        SkVector           fSize;       // the size to draw the points
+        SkRect             fClipRect;   // clip required to draw the points (if kUseClip is set)
+        SkPath             fPath;       // 'stamp' to be used at each point (if kUsePath is set)
+
+        SkPath             fFirst;      // If not empty, contains geometry for first point
+        SkPath             fLast;       // If not empty, contains geometry for last point
+    };
+
+    /**
+     *  Does applying this path effect to 'src' yield a set of points? If so,
+     *  optionally return the points in 'results'.
+     */
+    virtual bool asPoints(PointData* results, const SkPath& src,
+                          const SkStrokeRec&, const SkMatrix&,
+                          const SkRect* cullR) const;
+
+protected:
+    SkPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
 private:
     // illegal
     SkPathEffect(const SkPathEffect&);
     SkPathEffect& operator=(const SkPathEffect&);
+
+    typedef SkFlattenable INHERITED;
 };
 
 /** \class SkPairPathEffect
@@ -54,10 +130,11 @@
 
 protected:
     SkPairPathEffect(SkFlattenableReadBuffer&);
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
     // these are visible to our subclasses
     SkPathEffect* fPE0, *fPE1;
-    
+
 private:
     typedef SkPathEffect INHERITED;
 };
@@ -77,24 +154,19 @@
     SkComposePathEffect(SkPathEffect* outer, SkPathEffect* inner)
         : INHERITED(outer, inner) {}
 
-    // overrides
-    
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkComposePathEffect, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposePathEffect)
 
 protected:
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-
-private:
     SkComposePathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
 
+private:
     // illegal
     SkComposePathEffect(const SkComposePathEffect&);
     SkComposePathEffect& operator=(const SkComposePathEffect&);
-    
+
     typedef SkPairPathEffect INHERITED;
 };
 
@@ -113,19 +185,15 @@
     SkSumPathEffect(SkPathEffect* first, SkPathEffect* second)
         : INHERITED(first, second) {}
 
-    // overrides
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)  {
-        return SkNEW_ARGS(SkSumPathEffect, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSumPathEffect)
 
 protected:
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-
-private:
     SkSumPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
 
+private:
     // illegal
     SkSumPathEffect(const SkSumPathEffect&);
     SkSumPathEffect& operator=(const SkSumPathEffect&);
@@ -134,4 +202,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkPathMeasure.h b/include/core/SkPathMeasure.h
index 6fb4482..6866cce 100644
--- a/include/core/SkPathMeasure.h
+++ b/include/core/SkPathMeasure.h
@@ -29,7 +29,7 @@
         a different path (or null), since the measure object keeps a pointer to the
         path object (does not copy its data).
     */
-    void    setPath(const SkPath*, bool forceClosed);
+    void setPath(const SkPath*, bool forceClosed);
 
     /** Return the total length of the current contour, or 0 if no path
         is associated (e.g. resetPath(null))
@@ -41,19 +41,23 @@
         Returns false if there is no path, or a zero-length path was specified, in which case
         position and tangent are unchanged.
     */
-    bool getPosTan(SkScalar distance, SkPoint* position, SkVector* tangent);
+    bool SK_WARN_UNUSED_RESULT getPosTan(SkScalar distance, SkPoint* position,
+                                         SkVector* tangent);
 
     enum MatrixFlags {
         kGetPosition_MatrixFlag     = 0x01,
         kGetTangent_MatrixFlag      = 0x02,
         kGetPosAndTan_MatrixFlag    = kGetPosition_MatrixFlag | kGetTangent_MatrixFlag
     };
+
     /** Pins distance to 0 <= distance <= getLength(), and then computes
         the corresponding matrix (by calling getPosTan).
         Returns false if there is no path, or a zero-length path was specified, in which case
         matrix is unchanged.
     */
-    bool getMatrix(SkScalar distance, SkMatrix* matrix, MatrixFlags flags = kGetPosAndTan_MatrixFlag);
+    bool SK_WARN_UNUSED_RESULT getMatrix(SkScalar distance, SkMatrix* matrix,
+                                  MatrixFlags flags = kGetPosAndTan_MatrixFlag);
+
     /** Given a start and stop distance, return in dst the intervening segment(s).
         If the segment is zero-length, return false, else return true.
         startD and stopD are pinned to legal values (0..getLength()). If startD <= stopD
diff --git a/include/core/SkPerspIter.h b/include/core/SkPerspIter.h
deleted file mode 100644
index 7f6add2..0000000
--- a/include/core/SkPerspIter.h
+++ /dev/null
@@ -1,48 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPerspIter_DEFINED
-#define SkPerspIter_DEFINED
-
-#include "SkMatrix.h"
-
-class SkPerspIter {
-public:
-    /** Iterate a line through the matrix [x,y] ... [x+count-1, y].
-        @param m    The matrix we will be iterating a line through
-        @param x    The initial X coordinate to be mapped through the matrix
-        @param y    The initial Y coordinate to be mapped through the matrix
-        @param count The number of points (x,y) (x+1,y) (x+2,y) ... we will eventually map
-    */
-    SkPerspIter(const SkMatrix& m, SkScalar x, SkScalar y, int count);
-    
-    /** Return the buffer of [x,y] fixed point values we will be filling.
-        This always returns the same value, so it can be saved across calls to
-        next().
-    */
-    const SkFixed* getXY() const { return fStorage; }
-
-    /** Return the number of [x,y] pairs that have been filled in the getXY() buffer.
-        When this returns 0, the iterator is finished.
-    */
-    int next();
-    
-private:
-    enum {
-        kShift  = 4,
-        kCount  = (1 << kShift)
-    };
-    const SkMatrix& fMatrix;
-    SkFixed         fStorage[kCount * 2];
-    SkFixed         fX, fY;
-    SkScalar        fSX, fSY;
-    int             fCount;
-};
-
-#endif
diff --git a/include/core/SkPicture.h b/include/core/SkPicture.h
index 47a2b95..44e4f86 100644
--- a/include/core/SkPicture.h
+++ b/include/core/SkPicture.h
@@ -11,7 +11,10 @@
 #define SkPicture_DEFINED
 
 #include "SkRefCnt.h"
+#include "SkSerializationHelpers.h"
 
+class SkBBoxHierarchy;
+class SkBitmap;
 class SkCanvas;
 class SkPicturePlayback;
 class SkPictureRecord;
@@ -25,6 +28,8 @@
 */
 class SK_API SkPicture : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkPicture)
+
     /** The constructor prepares the picture to record.
         @param width the width of the virtual device the picture records.
         @param height the height of the virtual device the picture records.
@@ -34,14 +39,32 @@
         this call, those elements will not appear in this picture.
     */
     SkPicture(const SkPicture& src);
-    explicit SkPicture(SkStream*);
+    /**
+     *  Recreate a picture that was serialized into a stream. *success is set to
+     *  true if the picture was deserialized successfully and false otherwise.
+     *  decoder is used to decode any SkBitmaps that were encoded into the stream.
+     */
+    explicit SkPicture(SkStream*, bool* success = NULL,
+                       SkSerializationHelpers::DecodeBitmap decoder = NULL);
     virtual ~SkPicture();
-    
+
     /**
      *  Swap the contents of the two pictures. Guaranteed to succeed.
      */
     void swap(SkPicture& other);
-    
+
+    /**
+     *  Creates a thread-safe clone of the picture that is ready for playback.
+     */
+    SkPicture* clone() const;
+
+    /**
+     * Creates multiple thread-safe clones of this picture that are ready for
+     * playback. The resulting clones are stored in the provided array of
+     * SkPictures.
+     */
+    void clone(SkPicture* pictures, int count) const;
+
     enum RecordingFlags {
         /*  This flag specifies that when clipPath() is called, the path will
             be faithfully recorded, but the recording canvas' current clip will
@@ -51,7 +74,25 @@
             clip-query calls will reflect the path's bounds, not the actual
             path.
          */
-        kUsePathBoundsForClip_RecordingFlag = 0x01
+        kUsePathBoundsForClip_RecordingFlag = 0x01,
+        /*  This flag causes the picture to compute bounding boxes and build
+            up a spatial hierarchy (currently an R-Tree), plus a tree of Canvas'
+            usually stack-based clip/etc state. This requires an increase in
+            recording time (often ~2x; likely more for very complex pictures),
+            but allows us to perform much faster culling at playback time, and
+            completely avoid some unnecessary clips and other operations. This
+            is ideal for tiled rendering, or any other situation where you're
+            drawing a fraction of a large scene into a smaller viewport.
+
+            In most cases the record cost is offset by the playback improvement
+            after a frame or two of tiled rendering (and complex pictures that
+            induce the worst record times will generally get the largest
+            speedups at playback time).
+
+            Note: Currently this is not serializable, the bounding data will be
+            discarded if you serialize into a stream and then deserialize.
+        */
+        kOptimizeForClippedPlayback_RecordingFlag = 0x02
     };
 
     /** Returns the canvas that records the drawing commands.
@@ -74,13 +115,13 @@
         is drawn.
     */
     void endRecording();
-    
+
     /** Replays the drawing commands on the specified canvas. This internally
         calls endRecording() if that has not already been called.
         @param surface the canvas receiving the drawing commands.
     */
     void draw(SkCanvas* surface);
-    
+
     /** Return the width of the picture's recording canvas. This
         value reflects what was passed to setSize(), and does not necessarily
         reflect the bounds of what has been recorded into the picture.
@@ -95,21 +136,51 @@
     */
     int height() const { return fHeight; }
 
-    void serialize(SkWStream*) const;
+    /**
+     *  Serialize to a stream. If non NULL, encoder will be used to encode
+     *  any bitmaps in the picture.
+     */
+    void serialize(SkWStream*, SkSerializationHelpers::EncodeBitmap encoder = NULL) const;
 
+#ifdef SK_BUILD_FOR_ANDROID
     /** Signals that the caller is prematurely done replaying the drawing
         commands. This can be called from a canvas virtual while the picture
-        is drawing. Has no effect if the picture is not drawing. 
+        is drawing. Has no effect if the picture is not drawing.
+        @deprecated preserving for legacy purposes
     */
     void abortPlayback();
-    
-private:
-    int fWidth, fHeight;
-    SkPictureRecord* fRecord;
+#endif
+
+protected:
+    // V2 : adds SkPixelRef's generation ID.
+    // V3 : PictInfo tag at beginning, and EOF tag at the end
+    // V4 : move SkPictInfo to be the header
+    // V5 : don't read/write FunctionPtr on cross-process (we can detect that)
+    // V6 : added serialization of SkPath's bounds (and packed its flags tighter)
+    // V7 : changed drawBitmapRect(IRect) to drawBitmapRectToRect(Rect)
+    // V8 : Add an option for encoding bitmaps
+    // V9 : Allow the reader and writer of an SKP disagree on whether to support
+    //      SK_SUPPORT_HINTING_SCALE_FACTOR
+    // V10: add drawRRect, drawOval, clipRRect
+    static const uint32_t PICTURE_VERSION = 10;
+
+    // fPlayback, fRecord, fWidth & fHeight are protected to allow derived classes to
+    // install their own SkPicturePlayback-derived players,SkPictureRecord-derived
+    // recorders and set the picture size
     SkPicturePlayback* fPlayback;
+    SkPictureRecord* fRecord;
+    int fWidth, fHeight;
+
+    // For testing. Derived classes may instantiate an alternate
+    // SkBBoxHierarchy implementation
+    virtual SkBBoxHierarchy* createBBoxHierarchy() const;
+
+private:
 
     friend class SkFlatPicture;
     friend class SkPicturePlayback;
+
+    typedef SkRefCnt INHERITED;
 };
 
 class SkAutoPictureRecord : SkNoncopyable {
@@ -122,11 +193,11 @@
     ~SkAutoPictureRecord() {
         fPicture->endRecording();
     }
-    
+
     /** Return the canvas to draw into for recording into the picture.
     */
     SkCanvas* getRecordingCanvas() const { return fCanvas; }
-    
+
 private:
     SkPicture*  fPicture;
     SkCanvas*   fCanvas;
diff --git a/include/core/SkPixelRef.h b/include/core/SkPixelRef.h
index f01ba15..89ce69a 100644
--- a/include/core/SkPixelRef.h
+++ b/include/core/SkPixelRef.h
@@ -13,35 +13,30 @@
 #include "SkBitmap.h"
 #include "SkRefCnt.h"
 #include "SkString.h"
+#include "SkFlattenable.h"
+
+#ifdef SK_DEBUG
+    /**
+     *  Defining SK_IGNORE_PIXELREF_SETPRELOCKED will force all pixelref
+     *  subclasses to correctly handle lock/unlock pixels. For performance
+     *  reasons, simple malloc-based subclasses call setPreLocked() to skip
+     *  the overhead of implementing these calls.
+     *
+     *  This build-flag disables that optimization, to add in debugging our
+     *  call-sites, to ensure that they correctly balance their calls of
+     *  lock and unlock.
+     */
+//    #define SK_IGNORE_PIXELREF_SETPRELOCKED
+#endif
 
 class SkColorTable;
+class SkData;
 struct SkIRect;
 class SkMutex;
-class SkFlattenableReadBuffer;
-class SkFlattenableWriteBuffer;
 
 // this is an opaque class, not interpreted by skia
 class SkGpuTexture;
 
-#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-
-#define SK_DECLARE_PIXEL_REF_REGISTRAR() 
-
-#define SK_DEFINE_PIXEL_REF_REGISTRAR(pixelRef) \
-    static SkPixelRef::Registrar g##pixelRef##Reg(#pixelRef, \
-                                                  pixelRef::Create);
-                                                      
-#else
-
-#define SK_DECLARE_PIXEL_REF_REGISTRAR() static void Init();
-
-#define SK_DEFINE_PIXEL_REF_REGISTRAR(pixelRef) \
-    void pixelRef::Init() { \
-        SkPixelRef::Registrar(#pixelRef, Create); \
-    }
-
-#endif
-
 /** \class SkPixelRef
 
     This class is the smart container for pixel memory, and is used with
@@ -50,8 +45,10 @@
 
     This class can be shared/accessed between multiple threads.
 */
-class SK_API SkPixelRef : public SkRefCnt {
+class SK_API SkPixelRef : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkPixelRef)
+
     explicit SkPixelRef(SkBaseMutex* mutex = NULL);
 
     /** Return the pixel memory returned from lockPixels, or null if the
@@ -68,6 +65,8 @@
      */
     bool isLocked() const { return fLockCount > 0; }
 
+    SkDEBUGCODE(int getLockCount() const { return fLockCount; })
+
     /** Call to access the pixel memory, which is returned. Balance with a call
         to unlockPixels().
     */
@@ -131,24 +130,36 @@
     */
     void setURI(const SkString& uri) { fURI = uri; }
 
+    /**
+     *  If the pixelRef has an encoded (i.e. compressed) representation,
+     *  return a ref to its data. If the pixelRef
+     *  is uncompressed or otherwise does not have this form, return NULL.
+     *
+     *  If non-null is returned, the caller is responsible for calling unref()
+     *  on the data when it is finished.
+     */
+    SkData* refEncodedData() {
+        return this->onRefEncodedData();
+    }
+
     /** Are we really wrapping a texture instead of a bitmap?
      */
     virtual SkGpuTexture* getTexture() { return NULL; }
 
     bool readPixels(SkBitmap* dst, const SkIRect* subset = NULL);
 
-    /** Makes a deep copy of this PixelRef, respecting the requested config.
-        Returns NULL if either there is an error (e.g. the destination could
-        not be created with the given config), or this PixelRef does not 
-        support deep copies.  */
-    virtual SkPixelRef* deepCopy(SkBitmap::Config config) { return NULL; }
-
-    // serialization
-
-    typedef SkPixelRef* (*Factory)(SkFlattenableReadBuffer&);
-
-    virtual Factory getFactory() const { return NULL; }
-    virtual void flatten(SkFlattenableWriteBuffer&) const;
+    /**
+     *  Makes a deep copy of this PixelRef, respecting the requested config.
+     *  @param config Desired config.
+     *  @param subset Subset of this PixelRef to copy. Must be fully contained within the bounds of
+     *         of this PixelRef.
+     *  @return A new SkPixelRef, or NULL if either there is an error (e.g. the destination could
+     *          not be created with the given config), or this PixelRef does not support deep
+     *          copies.
+     */
+    virtual SkPixelRef* deepCopy(SkBitmap::Config config, const SkIRect* subset = NULL) {
+        return NULL;
+    }
 
 #ifdef SK_BUILD_FOR_ANDROID
     /**
@@ -166,17 +177,6 @@
     virtual void globalUnref();
 #endif
 
-    static Factory NameToFactory(const char name[]);
-    static const char* FactoryToName(Factory);
-    static void Register(const char name[], Factory);
-
-    class Registrar {
-    public:
-        Registrar(const char name[], Factory factory) {
-            SkPixelRef::Register(name, factory);
-        }
-    };
-
 protected:
     /** Called when the lockCount goes from 0 to 1. The caller will have already
         acquire a mutex for thread safety, so this method need not do that.
@@ -199,29 +199,24 @@
      */
     virtual bool onReadPixels(SkBitmap* dst, const SkIRect* subsetOrNull);
 
+    // default impl returns NULL.
+    virtual SkData* onRefEncodedData();
+
     /** Return the mutex associated with this pixelref. This value is assigned
         in the constructor, and cannot change during the lifetime of the object.
     */
     SkBaseMutex* mutex() const { return fMutex; }
 
+    // serialization
     SkPixelRef(SkFlattenableReadBuffer&, SkBaseMutex*);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     // only call from constructor. Flags this to always be locked, removing
     // the need to grab the mutex and call onLockPixels/onUnlockPixels.
     // Performance tweak to avoid those calls (esp. in multi-thread use case).
     void setPreLocked(void* pixels, SkColorTable* ctable);
 
-    // only call from constructor. Specify a (possibly) different mutex, or
-    // null to use the default. Use with caution.
-    // The default logic is to provide a mutex, but possibly one that is
-    // shared with other instances, though this sharing is implementation
-    // specific, and it is legal for each instance to have its own mutex.
-    void useDefaultMutex() { this->setMutex(NULL); }
-
 private:
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-    static void InitializeFlattenables();
-#endif
 
     SkBaseMutex*    fMutex; // must remain in scope for the life of this object
     void*           fPixels;
@@ -230,6 +225,10 @@
 
     mutable uint32_t fGenerationID;
 
+    // SkBitmap is only a friend so that when copying, it can modify the new SkPixelRef to have the
+    // same fGenerationID as the original.
+    friend class SkBitmap;
+
     SkString    fURI;
 
     // can go from false to true, but never from true to false
@@ -239,7 +238,7 @@
 
     void setMutex(SkBaseMutex* mutex);
 
-    friend class SkGraphics;
+    typedef SkFlattenable INHERITED;
 };
 
 #endif
diff --git a/include/core/SkPoint.h b/include/core/SkPoint.h
index d371e64..a6a8e73 100644
--- a/include/core/SkPoint.h
+++ b/include/core/SkPoint.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkPoint_DEFINED
 #define SkPoint_DEFINED
 
@@ -145,6 +143,11 @@
     SkScalar x() const { return fX; }
     SkScalar y() const { return fY; }
 
+    /**
+     *  Returns true iff fX and fY are both zero.
+     */
+    bool isZero() const { return (0 == fX) & (0 == fY); }
+
     /** Set the point's X and Y coordinates */
     void set(SkScalar x, SkScalar y) { fX = x; fY = y; }
 
@@ -168,7 +171,7 @@
         fX = SkScalarAbs(pt.fX);
         fY = SkScalarAbs(pt.fY);
     }
-    
+
     // counter-clockwise fan
     void setIRectFan(int l, int t, int r, int b) {
         SkPoint* v = this;
@@ -213,7 +216,13 @@
      *  Return true if the computed length of the vector is >= the internal
      *  tolerance (used to avoid dividing by tiny values).
      */
-    static bool CanNormalize(SkScalar dx, SkScalar dy);
+    static bool CanNormalize(SkScalar dx, SkScalar dy)
+#ifdef SK_SCALAR_IS_FLOAT
+    // Simple enough (and performance critical sometimes) so we inline it.
+    { return (dx*dx + dy*dy) > (SK_ScalarNearlyZero * SK_ScalarNearlyZero); }
+#else
+    ;
+#endif
 
     bool canNormalize() const {
         return CanNormalize(fX, fY);
@@ -303,9 +312,35 @@
         fY -= v.fY;
     }
 
-    /** Returns true if the point's coordinates equal (x,y)
-    */
-    bool equals(SkScalar x, SkScalar y) const { return fX == x && fY == y; }
+    /**
+     *  Returns true if both X and Y are finite (not infinity or NaN)
+     */
+    bool isFinite() const {
+#ifdef SK_SCALAR_IS_FLOAT
+        SkScalar accum = 0;
+        accum *= fX;
+        accum *= fY;
+
+        // accum is either NaN or it is finite (zero).
+        SkASSERT(0 == accum || !(accum == accum));
+
+        // value==value will be true iff value is not NaN
+        // TODO: is it faster to say !accum or accum==accum?
+        return accum == accum;
+#else
+        // use bit-or for speed, since we don't care about short-circuting the
+        // tests, and we expect the common case will be that we need to check all.
+        int isNaN = (SK_FixedNaN == fX) | (SK_FixedNaN == fX));
+        return !isNaN;
+#endif
+    }
+
+    /**
+     *  Returns true if the point's coordinates equal (x,y)
+     */
+    bool equals(SkScalar x, SkScalar y) const {
+        return fX == x && fY == y;
+    }
 
     friend bool operator==(const SkPoint& a, const SkPoint& b) {
         return a.fX == b.fX && a.fY == b.fY;
@@ -315,11 +350,29 @@
         return a.fX != b.fX || a.fY != b.fY;
     }
 
-    /** Return true if this and the given point are componentwise within tol.
+    /** Return true if this point and the given point are far enough apart
+        such that a vector between them would be non-degenerate.
+
+        WARNING: Unlike the deprecated version of equalsWithinTolerance(),
+        this method does not use componentwise comparison.  Instead, it
+        uses a comparison designed to match judgments elsewhere regarding
+        degeneracy ("points A and B are so close that the vector between them
+        is essentially zero").
     */
-    bool equalsWithinTolerance(const SkPoint& v, SkScalar tol) const {
-        return SkScalarNearlyZero(fX - v.fX, tol)
-               && SkScalarNearlyZero(fY - v.fY, tol);
+    bool equalsWithinTolerance(const SkPoint& p) const {
+        return !CanNormalize(fX - p.fX, fY - p.fY);
+    }
+
+    /** DEPRECATED: Return true if this and the given point are componentwise
+        within tolerance "tol".
+
+        WARNING: There is no guarantee that the result will reflect judgments
+        elsewhere regarding degeneracy ("points A and B are so close that the
+        vector between them is essentially zero").
+    */
+    bool equalsWithinTolerance(const SkPoint& p, SkScalar tol) const {
+        return SkScalarNearlyZero(fX - p.fX, tol)
+               && SkScalarNearlyZero(fY - p.fY, tol);
     }
 
     /** Returns a new point whose coordinates are the difference between
@@ -379,11 +432,11 @@
     SkScalar dot(const SkPoint& vec) const {
         return DotProduct(*this, vec);
     }
-    
+
     SkScalar lengthSqd() const {
         return DotProduct(*this, *this);
     }
-    
+
     SkScalar distanceToSqd(const SkPoint& pt) const {
         SkScalar dx = fX - pt.fX;
         SkScalar dy = fY - pt.fY;
diff --git a/include/core/SkPostConfig.h b/include/core/SkPostConfig.h
index edce334..14a4a45 100644
--- a/include/core/SkPostConfig.h
+++ b/include/core/SkPostConfig.h
@@ -27,16 +27,15 @@
 #if defined(SK_SCALAR_IS_FIXED) && defined(SK_SCALAR_IS_FLOAT)
     #error "cannot define both SK_SCALAR_IS_FIXED and SK_SCALAR_IS_FLOAT"
 #elif !defined(SK_SCALAR_IS_FIXED) && !defined(SK_SCALAR_IS_FLOAT)
-    #ifdef SK_CAN_USE_FLOAT
-        #define SK_SCALAR_IS_FLOAT
-    #else
-        #define SK_SCALAR_IS_FIXED
-    #endif
+    #define SK_SCALAR_IS_FLOAT
 #endif
 
-#if defined(SK_SCALAR_IS_FLOAT) && !defined(SK_CAN_USE_FLOAT)
-    #define SK_CAN_USE_FLOAT
-    // we do nothing in the else case: fixed-scalars can have floats or not
+#if defined(SK_MSCALAR_IS_DOUBLE) && defined(SK_MSCALAR_IS_FLOAT)
+    #error "cannot define both SK_MSCALAR_IS_DOUBLE and SK_MSCALAR_IS_FLOAT"
+#elif !defined(SK_MSCALAR_IS_DOUBLE) && !defined(SK_MSCALAR_IS_FLOAT)
+    // default is double, as that is faster given our impl uses doubles
+    // for intermediate calculations.
+    #define SK_MSCALAR_IS_DOUBLE
 #endif
 
 #if defined(SK_CPU_LENDIAN) && defined(SK_CPU_BENDIAN)
@@ -64,6 +63,10 @@
     #endif
 #endif
 
+#if !defined(SK_SUPPORT_GPU)
+    #define SK_SUPPORT_GPU 1
+#endif
+
 /**
  * The clang static analyzer likes to know that when the program is not
  * expected to continue (crash, assertion failure, etc). It will notice that
@@ -73,23 +76,30 @@
  */
 #if !defined(SkNO_RETURN_HINT)
     #if SK_HAS_COMPILER_FEATURE(attribute_analyzer_noreturn)
-        namespace {
-            inline void SkNO_RETURN_HINT() __attribute__((analyzer_noreturn));
-            void SkNO_RETURN_HINT() {}
-        }
+        static inline void SkNO_RETURN_HINT() __attribute__((analyzer_noreturn));
+        static inline void SkNO_RETURN_HINT() {}
     #else
         #define SkNO_RETURN_HINT() do {} while (false)
     #endif
 #endif
 
+#if defined(SK_ZLIB_INCLUDE) && defined(SK_SYSTEM_ZLIB)
+    #error "cannot define both SK_ZLIB_INCLUDE and SK_SYSTEM_ZLIB"
+#elif defined(SK_ZLIB_INCLUDE) || defined(SK_SYSTEM_ZLIB)
+    #define SK_HAS_ZLIB
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifndef SkNEW
-    #define SkNEW(type_name)                new type_name
-    #define SkNEW_ARGS(type_name, args)     new type_name args
-    #define SkNEW_ARRAY(type_name, count)   new type_name[count]
-    #define SkDELETE(obj)                   delete obj
-    #define SkDELETE_ARRAY(array)           delete[] array
+    #define SkNEW(type_name)                (new type_name)
+    #define SkNEW_ARGS(type_name, args)     (new type_name args)
+    #define SkNEW_ARRAY(type_name, count)   (new type_name[(count)])
+    #define SkNEW_PLACEMENT(buf, type_name) (new (buf) type_name)
+    #define SkNEW_PLACEMENT_ARGS(buf, type_name, args) \
+                                            (new (buf) type_name args)
+    #define SkDELETE(obj)                   (delete (obj))
+    #define SkDELETE_ARRAY(array)           (delete[] (array))
 #endif
 
 #ifndef SK_CRASH
@@ -102,6 +112,17 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// SK_ENABLE_INST_COUNT defaults to 1 in DEBUG and 0 in RELEASE
+#ifndef SK_ENABLE_INST_COUNT
+    #ifdef SK_DEBUG
+        #define SK_ENABLE_INST_COUNT 1
+    #else
+        #define SK_ENABLE_INST_COUNT 0
+    #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
 #if defined(SK_SOFTWARE_FLOAT) && defined(SK_SCALAR_IS_FLOAT)
     // if this is defined, we convert floats to 2scompliment ints for compares
     #ifndef SK_SCALAR_SLOW_COMPARES
@@ -279,15 +300,41 @@
 //////////////////////////////////////////////////////////////////////
 
 #ifndef SK_OVERRIDE
-#if defined(_MSC_VER)
-#define SK_OVERRIDE override
-#elif defined(__clang__)
-// Some documentation suggests we should be using __attribute__((override)),
-// but it doesn't work.
-#define SK_OVERRIDE override
+    #if defined(_MSC_VER)
+        #define SK_OVERRIDE override
+    #elif defined(__clang__) && !defined(SK_BUILD_FOR_IOS)
+        #if __has_feature(cxx_override_control)
+            // Some documentation suggests we should be using __attribute__((override)),
+            // but it doesn't work.
+            #define SK_OVERRIDE override
+        #elif defined(__has_extension)
+            #if __has_extension(cxx_override_control)
+                #define SK_OVERRIDE override
+            #endif
+        #endif
+    #else
+        // Linux GCC ignores "__attribute__((override))" and rejects "override".
+        #define SK_OVERRIDE
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#ifndef SK_PRINTF_LIKE
+#if defined(__clang__) || defined(__GNUC__)
+#define SK_PRINTF_LIKE(A, B) __attribute__((format(printf, (A), (B))))
 #else
-// Linux GCC ignores "__attribute__((override))" and rejects "override".
-#define SK_OVERRIDE
+#define SK_PRINTF_LIKE(A, B)
+#endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+#ifndef SK_SIZE_T_SPECIFIER
+#if defined(_MSC_VER)
+#define SK_SIZE_T_SPECIFIER "%Iu"
+#else
+#define SK_SIZE_T_SPECIFIER "%zu"
 #endif
 #endif
 
diff --git a/include/core/SkPreConfig.h b/include/core/SkPreConfig.h
index 4485f1d..4f977ea 100644
--- a/include/core/SkPreConfig.h
+++ b/include/core/SkPreConfig.h
@@ -16,7 +16,7 @@
 
 //////////////////////////////////////////////////////////////////////
 
-#if !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) && !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_PALM) && !defined(SK_BUILD_FOR_WINCE) && !defined(SK_BUILD_FOR_WIN32) && !defined(SK_BUILD_FOR_SYMBIAN) && !defined(SK_BUILD_FOR_UNIX) && !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_SDL) && !defined(SK_BUILD_FOR_BREW)
+#if !defined(SK_BUILD_FOR_ANDROID) && !defined(SK_BUILD_FOR_ANDROID_NDK) && !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)
 
     #ifdef __APPLE__
         #include "TargetConditionals.h"
@@ -35,7 +35,8 @@
     #elif defined(ANDROID)
         #define SK_BUILD_FOR_ANDROID
     #elif defined(linux) || defined(__FreeBSD__) || defined(__OpenBSD__) || \
-          defined(__sun) || defined(__NetBSD__) || defined(__DragonFly__)
+          defined(__sun) || defined(__NetBSD__) || defined(__DragonFly__) || \
+          defined(__GLIBC__) || defined(__GNU__)
         #define SK_BUILD_FOR_UNIX
     #elif TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
         #define SK_BUILD_FOR_IOS
@@ -47,13 +48,19 @@
 
 /* Even if the user only defined the NDK variant we still need to build
  * the default Android code. Therefore, when attempting to include/exclude
- * something from the NDK variant check first that we are building for 
+ * something from the NDK variant check first that we are building for
  * Android then check the status of the NDK define.
  */
 #if defined(SK_BUILD_FOR_ANDROID_NDK) && !defined(SK_BUILD_FOR_ANDROID)
     #define SK_BUILD_FOR_ANDROID
 #endif
 
+// USE_CHROMIUM_SKIA is defined when building Skia for the Chromium
+// browser.
+#if defined(USE_CHROMIUM_SKIA)
+    #define SK_BUILD_FOR_CHROMIUM
+#endif
+
 //////////////////////////////////////////////////////////////////////
 
 #if !defined(SK_DEBUG) && !defined(SK_RELEASE)
@@ -68,6 +75,9 @@
     #if !defined(SK_RESTRICT)
         #define SK_RESTRICT __restrict
     #endif
+    #if !defined(SK_WARN_UNUSED_RESULT)
+        #define SK_WARN_UNUSED_RESULT
+    #endif
     #include "sk_stdint.h"
 #endif
 
@@ -77,11 +87,14 @@
     #define SK_RESTRICT __restrict__
 #endif
 
+#if !defined(SK_WARN_UNUSED_RESULT)
+    #define SK_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#endif
+
 //////////////////////////////////////////////////////////////////////
 
 #if !defined(SK_SCALAR_IS_FLOAT) && !defined(SK_SCALAR_IS_FIXED)
     #define SK_SCALAR_IS_FLOAT
-    #define SK_CAN_USE_FLOAT
 #endif
 
 //////////////////////////////////////////////////////////////////////
@@ -96,8 +109,87 @@
 
 //////////////////////////////////////////////////////////////////////
 
-#if (defined(__arm__) && !defined(__thumb__)) || defined(SK_BUILD_FOR_WINCE) || (defined(SK_BUILD_FOR_SYMBIAN) && !defined(__MARM_THUMB__))
-    /* e.g. the ARM instructions have conditional execution, making tiny branches cheap */
+/**
+ *  SK_CPU_SSE_LEVEL
+ *
+ *  If defined, SK_CPU_SSE_LEVEL should be set to the highest supported level.
+ *  On non-intel CPU this should be undefined.
+ */
+
+#define SK_CPU_SSE_LEVEL_SSE1     10
+#define SK_CPU_SSE_LEVEL_SSE2     20
+#define SK_CPU_SSE_LEVEL_SSE3     30
+#define SK_CPU_SSE_LEVEL_SSSE3    31
+
+// Are we in GCC?
+#ifndef SK_CPU_SSE_LEVEL
+    #if defined(__SSE2__)
+        #define SK_CPU_SSE_LEVEL    SK_CPU_SSE_LEVEL_SSE2
+    #elif defined(__SSE3__)
+        #define SK_CPU_SSE_LEVEL    SK_CPU_SSE_LEVEL_SSE3
+    #elif defined(__SSSE3__)
+        #define SK_CPU_SSE_LEVEL    SK_CPU_SSE_LEVEL_SSSE3
+    #endif
+#endif
+
+// Are we in VisualStudio?
+#ifndef SK_CPU_SSE_LEVEL
+    #if _M_IX86_FP == 1
+        #define SK_CPU_SSE_LEVEL    SK_CPU_SSE_LEVEL_SSE1
+    #elif _M_IX86_FP >= 2
+        #define SK_CPU_SSE_LEVEL    SK_CPU_SSE_LEVEL_SSE2
+    #endif
+#endif
+
+// 64bit intel guarantees at least SSE2
+#if defined(__x86_64__) || defined(_WIN64)
+    #if !defined(SK_CPU_SSE_LEVEL) || (SK_CPU_SSE_LEVEL < SK_CPU_SSE_LEVEL_SSE2)
+        #undef SK_CPU_SSE_LEVEL
+        #define SK_CPU_SSE_LEVEL    SK_CPU_SSE_LEVEL_SSE2
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// ARM defines
+
+#if defined(__arm__) && (!defined(__APPLE__) || !TARGET_IPHONE_SIMULATOR)
+    #define SK_CPU_ARM
+
+    #if defined(__GNUC__)
+        #if defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \
+                || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \
+                || defined(__ARM_ARCH_7EM__) || defined(_ARM_ARCH_7)
+            #define SK_ARM_ARCH 7
+        #elif defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \
+                || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \
+                || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) \
+                || defined(__ARM_ARCH_6M__) || defined(_ARM_ARCH_6)
+            #define SK_ARM_ARCH 6
+        #elif defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \
+                || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \
+                || defined(__ARM_ARCH_5TEJ__) || defined(_ARM_ARCH_5)
+            #define SK_ARM_ARCH 5
+        #elif defined(__ARM_ARCH_4__) || defined(__ARM_ARCH_4T__) || defined(_ARM_ARCH_4)
+            #define SK_ARM_ARCH 4
+        #else
+            #define SK_ARM_ARCH 3
+        #endif
+
+        #if defined(__thumb2__) && (SK_ARM_ARCH >= 6) \
+                || !defined(__thumb__) && ((SK_ARM_ARCH > 5) || defined(__ARM_ARCH_5E__) \
+                || defined(__ARM_ARCH_5TE__) || defined(__ARM_ARCH_5TEJ__))
+            #define SK_ARM_HAS_EDSP
+        #endif
+    #endif
+#endif
+
+//////////////////////////////////////////////////////////////////////
+
+/**
+ *  THUMB is the only known config where we avoid small branches in
+ *  favor of more complex math.
+ */
+#if !(defined(__arm__) && defined(__thumb__))
     #define SK_CPU_HAS_CONDITIONAL_INSTR
 #endif
 
@@ -121,5 +213,48 @@
     #define SK_API
 #endif
 
+//////////////////////////////////////////////////////////////////////
+
+/**
+ * Use SK_PURE_FUNC as an attribute to indicate that a function's
+ * return value only depends on the value of its parameters. This
+ * can help the compiler optimize out successive calls.
+ *
+ * Usage:
+ *      void  function(int params)  SK_PURE_FUNC;
+ */
+#if defined(__GNUC__)
+#  define  SK_PURE_FUNC  __attribute__((pure))
+#else
+#  define  SK_PURE_FUNC  /* nothing */
 #endif
 
+//////////////////////////////////////////////////////////////////////
+
+/**
+ * SK_HAS_ATTRIBUTE(<name>) should return true iff the compiler
+ * supports __attribute__((<name>)). Mostly important because
+ * Clang doesn't support all of GCC attributes.
+ */
+#if defined(__has_attribute)
+#   define SK_HAS_ATTRIBUTE(x) __has_attribute(x)
+#elif defined(__GNUC__)
+#   define SK_HAS_ATTRIBUTE(x) 1
+#else
+#   define SK_HAS_ATTRIBUTE(x) 0
+#endif
+
+/**
+ * SK_ATTRIBUTE_OPTIMIZE_O1 can be used as a function attribute
+ * to specify individual optimization level of -O1, if the compiler
+ * supports it.
+ *
+ * NOTE: Clang/ARM (r161757) does not support the 'optimize' attribute.
+ */
+#if SK_HAS_ATTRIBUTE(optimize)
+#   define SK_ATTRIBUTE_OPTIMIZE_O1 __attribute__((optimize("O1")))
+#else
+#   define SK_ATTRIBUTE_OPTIMIZE_O1 /* nothing */
+#endif
+
+#endif
diff --git a/include/core/SkPtrRecorder.h b/include/core/SkPtrRecorder.h
deleted file mode 100644
index 11fd170..0000000
--- a/include/core/SkPtrRecorder.h
+++ /dev/null
@@ -1,100 +0,0 @@
-
-/*
- * Copyright 2008 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 SkPtrSet_DEFINED
-#define SkPtrSet_DEFINED
-
-#include "SkRefCnt.h"
-#include "SkTDArray.h"
-
-/**
- *  Maintains a set of ptrs, assigning each a unique ID [1...N]. Duplicate ptrs
- *  return the same ID (since its a set). Subclasses can override inPtr()
- *  and decPtr(). incPtr() is called each time a unique ptr is added ot the
- *  set. decPtr() is called on each ptr when the set is destroyed or reset.
- */
-class SkPtrSet : public SkRefCnt {
-public:
-    /**
-     *  Search for the specified ptr in the set. If it is found, return its
-     *  32bit ID [1..N], or if not found, return 0. Always returns 0 for NULL.
-     */
-    uint32_t find(void*) const;
-
-    /**
-     *  Add the specified ptr to the set, returning a unique 32bit ID for it
-     *  [1...N]. Duplicate ptrs will return the same ID.
-     *
-     *  If the ptr is NULL, it is not added, and 0 is returned.
-     */
-    uint32_t add(void*);
-    
-    /**
-     *  Return the number of (non-null) ptrs in the set.
-     */
-    int count() const { return fList.count(); }
-
-    /**
-     *  Copy the ptrs in the set into the specified array (allocated by the
-     *  caller). The ptrs are assgined to the array based on their corresponding
-     *  ID. e.g. array[ptr.ID - 1] = ptr.
-     *
-     *  incPtr() and decPtr() are not called during this operation.
-     */
-    void copyToArray(void* array[]) const;
-
-    /**
-     *  Call decPtr() on each ptr in the set, and the reset the size of the set
-     *  to 0.
-     */
-    void reset();
-
-protected:
-    virtual void incPtr(void* ptr) {}
-    virtual void decPtr(void* ptr) {}
-
-private:
-    struct Pair {
-        void*       fPtr;   // never NULL
-        uint32_t    fIndex; // 1...N
-    };
-
-    // we store the ptrs in sorted-order (using Cmp) so that we can efficiently
-    // detect duplicates when add() is called. Hence we need to store the
-    // ptr and its ID/fIndex explicitly, since the ptr's position in the array
-    // is not related to its "index".
-    SkTDArray<Pair>  fList;
-    
-    static int Cmp(const Pair& a, const Pair& b);
-    
-    typedef SkRefCnt INHERITED;
-};
-
-/**
- *  Templated wrapper for SkPtrSet, just meant to automate typecasting
- *  parameters to and from void* (which the base class expects).
- */
-template <typename T> class SkTPtrSet : public SkPtrSet {
-public:
-    uint32_t find(T ptr) {
-        return this->INHERITED::find((void*)ptr);
-    }
-    uint32_t add(T ptr) {
-        return this->INHERITED::add((void*)ptr);
-    }
-    
-    void copyToArray(T* array) const {
-        this->INHERITED::copyToArray((void**)array);
-    }
-
-private:
-    typedef SkPtrSet INHERITED;
-};
-
-#endif
diff --git a/include/core/SkRRect.h b/include/core/SkRRect.h
new file mode 100644
index 0000000..16b2dc1
--- /dev/null
+++ b/include/core/SkRRect.h
@@ -0,0 +1,286 @@
+/*
+ * 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 SkRRect_DEFINED
+#define SkRRect_DEFINED
+
+#include "SkRect.h"
+#include "SkPoint.h"
+
+class SkPath;
+
+// Path forward:
+//   core work
+//      add validate method (all radii positive, all radii sums < rect size, etc.)
+//      add contains(SkRect&)  - for clip stack
+//      add contains(SkRRect&) - for clip stack
+//      add heart rect computation (max rect inside RR)
+//      add 9patch rect computation
+//      add growToInclude(SkPath&)
+//   analysis
+//      use growToInclude to fit skp round rects & generate stats (RRs vs. real paths)
+//      check on # of rectorus's the RRs could handle
+//   rendering work
+//      add entry points (clipRRect, drawRRect) - plumb down to SkDevice
+//      update SkPath.addRRect() to take an SkRRect - only use quads
+//          -- alternatively add addRRectToPath here
+//      add GM and bench
+//   clipping opt
+//      update SkClipStack to perform logic with RRs
+//   further out
+//      add RR rendering shader to Ganesh (akin to cicle drawing code)
+//          - only for simple RRs
+//      detect and triangulate RRectorii rather than falling back to SW in Ganesh
+//
+
+/** \class SkRRect
+
+    The SkRRect class represents a rounded rect with a potentially different
+    radii for each corner. It does not have a constructor so must be
+    initialized with one of the initialization functions (e.g., setEmpty,
+    setRectRadii, etc.)
+
+    This class is intended to roughly match CSS' border-*-*-radius capabilities.
+    This means:
+        If either of a corner's radii are 0 the corner will be square.
+        Negative radii are not allowed (they are clamped to zero).
+        If the corner curves overlap they will be proportionally reduced to fit.
+*/
+class SK_API SkRRect {
+public:
+    /**
+     * Enum to capture the various possible subtypes of RR. Accessed
+     * by type(). The subtypes become progressively less restrictive.
+     */
+    enum Type {
+        // !< Internal indicator that the sub type must be computed.
+        kUnknown_Type = -1,
+
+        // !< The RR is empty
+        kEmpty_Type,
+
+        //!< The RR is actually a (non-empty) rect (i.e., at least one radius
+        //!< at each corner is zero)
+        kRect_Type,
+
+        //!< The RR is actually a (non-empty) oval (i.e., all x radii are equal
+        //!< and >= width/2 and all the y radii are equal and >= height/2
+        kOval_Type,
+
+        //!< The RR is non-empty and all the x radii are equal & all y radii
+        //!< are equal but it is not an oval (i.e., there are lines between
+        //!< the curves) nor a rect (i.e., both radii are non-zero)
+        kSimple_Type,
+
+        //!< A fully general (non-empty) RR. Some of the x and/or y radii are
+        //!< different from the others and there must be one corner where
+        //!< both radii are non-zero.
+        kComplex_Type,
+    };
+
+    /**
+     * Returns the RR's sub type.
+     */
+    Type getType() const {
+        SkDEBUGCODE(this->validate();)
+
+        if (kUnknown_Type == fType) {
+            this->computeType();
+        }
+        SkASSERT(kUnknown_Type != fType);
+        return fType;
+    }
+
+    Type type() const { return this->getType(); }
+
+    inline bool isEmpty() const { return kEmpty_Type == this->getType(); }
+    inline bool isRect() const { return kRect_Type == this->getType(); }
+    inline bool isOval() const { return kOval_Type == this->getType(); }
+    inline bool isSimple() const { return kSimple_Type == this->getType(); }
+    inline bool isComplex() const { return kComplex_Type == this->getType(); }
+
+    SkScalar width() const { return fRect.width(); }
+    SkScalar height() const { return fRect.height(); }
+
+    /**
+     * Set this RR to the empty rectangle (0,0,0,0) with 0 x & y radii.
+     */
+    void setEmpty() {
+        fRect.setEmpty();
+        memset(fRadii, 0, sizeof(fRadii));
+        fType = kEmpty_Type;
+
+        SkDEBUGCODE(this->validate();)
+    }
+
+    /**
+     * Set this RR to match the supplied rect. All radii will be 0.
+     */
+    void setRect(const SkRect& rect) {
+        if (rect.isEmpty()) {
+            this->setEmpty();
+            return;
+        }
+
+        fRect = rect;
+        memset(fRadii, 0, sizeof(fRadii));
+        fType = kRect_Type;
+
+        SkDEBUGCODE(this->validate();)
+    }
+
+    /**
+     * Set this RR to match the supplied oval. All x radii will equal half the
+     * width and all y radii will equal half the height.
+     */
+    void setOval(const SkRect& oval) {
+        if (oval.isEmpty()) {
+            this->setEmpty();
+            return;
+        }
+
+        SkScalar xRad = SkScalarHalf(oval.width());
+        SkScalar yRad = SkScalarHalf(oval.height());
+
+        fRect = oval;
+        for (int i = 0; i < 4; ++i) {
+            fRadii[i].set(xRad, yRad);
+        }
+        fType = kOval_Type;
+
+        SkDEBUGCODE(this->validate();)
+    }
+
+    /**
+     * Initialize the RR with the same radii for all four corners.
+     */
+    void setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad);
+
+    /**
+     * Initialize the RR with potentially different radii for all four corners.
+     */
+    void setRectRadii(const SkRect& rect, const SkVector radii[4]);
+
+    // The radii are stored in UL, UR, LR, LL order.
+    enum Corner {
+        kUpperLeft_Corner,
+        kUpperRight_Corner,
+        kLowerRight_Corner,
+        kLowerLeft_Corner
+    };
+
+    const SkRect& rect() const { return fRect; }
+    const SkVector& radii(Corner corner) const { return fRadii[corner]; }
+    const SkRect& getBounds() const { return fRect; }
+
+    /**
+     *  When a rrect is simple, all of its radii are equal. This returns one
+     *  of those radii. This call requires the rrect to be non-complex.
+     */
+    const SkVector& getSimpleRadii() const {
+        SkASSERT(!this->isComplex());
+        return fRadii[0];
+    }
+
+    friend bool operator==(const SkRRect& a, const SkRRect& b) {
+        return a.fRect == b.fRect &&
+               SkScalarsEqual(SkTCast<const SkScalar*>(a.fRadii), SkTCast<const SkScalar*>(b.fRadii), 8);
+    }
+
+    friend bool operator!=(const SkRRect& a, const SkRRect& b) {
+        return a.fRect != b.fRect ||
+               !SkScalarsEqual(SkTCast<const SkScalar*>(a.fRadii), SkTCast<const SkScalar*>(b.fRadii), 8);
+    }
+
+    /**
+     *  Returns true if (p.fX,p.fY) is inside the RR, and the RR
+     *  is not empty.
+     *
+     *  Contains treats the left and top differently from the right and bottom.
+     *  The left and top coordinates of the RR are themselves considered
+     *  to be inside, while the right and bottom are not. All the points on the
+     *  edges of the corners are considered to be inside.
+     */
+    bool contains(const SkPoint& p) const {
+        return contains(p.fX, p.fY);
+    }
+
+    /**
+     *  Returns true if (x,y) is inside the RR, and the RR
+     *  is not empty.
+     *
+     *  Contains treats the left and top differently from the right and bottom.
+     *  The left and top coordinates of the RR are themselves considered
+     *  to be inside, while the right and bottom are not. All the points on the
+     *  edges of the corners are considered to be inside.
+     */
+    bool contains(SkScalar x, SkScalar y) const;
+
+    /**
+     *  Call inset on the bounds, and adjust the radii to reflect what happens
+     *  in stroking: If the corner is sharp (no curvature), leave it alone,
+     *  otherwise we grow/shrink the radii by the amount of the inset. If a
+     *  given radius becomes negative, it is pinned to 0.
+     *
+     *  It is valid for dst == this.
+     */
+    void inset(SkScalar dx, SkScalar dy, SkRRect* dst) const;
+
+    void inset(SkScalar dx, SkScalar dy) {
+        this->inset(dx, dy, this);
+    }
+
+    /**
+     *  Call outset on the bounds, and adjust the radii to reflect what happens
+     *  in stroking: If the corner is sharp (no curvature), leave it alone,
+     *  otherwise we grow/shrink the radii by the amount of the inset. If a
+     *  given radius becomes negative, it is pinned to 0.
+     *
+     *  It is valid for dst == this.
+     */
+    void outset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
+        this->inset(-dx, -dy, dst);
+    }
+    void outset(SkScalar dx, SkScalar dy) {
+        this->inset(-dx, -dy, this);
+    }
+
+    SkDEBUGCODE(void validate() const;)
+
+    enum {
+        kSizeInMemory = 12 * sizeof(SkScalar)
+    };
+
+    /**
+     *  Write the rrect into the specified buffer. This is guaranteed to always
+     *  write kSizeInMemory bytes, and that value is guaranteed to always be
+     *  a multiple of 4. Return kSizeInMemory.
+     */
+    uint32_t writeToMemory(void* buffer) const;
+
+    /**
+     *  Read the rrect from the specified buffer. This is guaranteed to always
+     *  read kSizeInMemory bytes, and that value is guaranteed to always be
+     *  a multiple of 4. Return kSizeInMemory.
+     */
+    uint32_t readFromMemory(const void* buffer);
+
+private:
+    SkRect fRect;
+    // Radii order is UL, UR, LR, LL. Use Corner enum to index into fRadii[]
+    SkVector fRadii[4];
+    mutable Type fType;
+    // TODO: add padding so we can use memcpy for flattening and not copy
+    // uninitialized data
+
+    void computeType() const;
+
+    // to access fRadii directly
+    friend class SkPath;
+};
+
+#endif
diff --git a/include/core/SkRandom.h b/include/core/SkRandom.h
deleted file mode 100644
index b850df3..0000000
--- a/include/core/SkRandom.h
+++ /dev/null
@@ -1,103 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkRandom_DEFINED
-#define SkRandom_DEFINED
-
-#include "Sk64.h"
-#include "SkScalar.h"
-
-/** \class SkRandom
-
-    Utility class that implements pseudo random 32bit numbers using a fast
-    linear equation. Unlike rand(), this class holds its own seed (initially
-    set to 0), so that multiple instances can be used with no side-effects.
-*/
-class SkRandom {
-public:
-    SkRandom() : fSeed(0) {}
-    SkRandom(uint32_t seed) : fSeed(seed) {}
-
-    /** Return the next pseudo random number as an unsigned 32bit value.
-    */
-    uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
-
-    /** Return the next pseudo random number as a signed 32bit value.
-    */
-    int32_t nextS() { return (int32_t)this->nextU(); }
-
-    /** Return the next pseudo random number as an unsigned 16bit value.
-    */
-    U16CPU nextU16() { return this->nextU() >> 16; }
-
-    /** Return the next pseudo random number as a signed 16bit value.
-    */
-    S16CPU nextS16() { return this->nextS() >> 16; }
-
-    /** Return the next pseudo random number, as an unsigned value of
-        at most bitCount bits.
-        @param bitCount The maximum number of bits to be returned
-    */
-    uint32_t nextBits(unsigned bitCount) {
-        SkASSERT(bitCount > 0 && bitCount <= 32);
-        return this->nextU() >> (32 - bitCount);
-    }
-
-    /** Return the next pseudo random unsigned number, mapped to lie within
-        [min, max] inclusive.
-    */
-    uint32_t nextRangeU(uint32_t min, uint32_t max) {
-        SkASSERT(min <= max);
-        return min + this->nextU() % (max - min + 1);
-    }
-
-    /** Return the next pseudo random number expressed as an unsigned SkFixed
-        in the range [0..SK_Fixed1).
-    */
-    SkFixed nextUFixed1() { return this->nextU() >> 16; }
-
-    /** Return the next pseudo random number expressed as a signed SkFixed
-        in the range (-SK_Fixed1..SK_Fixed1).
-    */
-    SkFixed nextSFixed1() { return this->nextS() >> 15; }
-
-    /** Return the next pseudo random number expressed as a SkScalar
-        in the range [0..SK_Scalar1).
-    */
-    SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); }
-
-    /** Return the next pseudo random number expressed as a SkScalar
-        in the range (-SK_Scalar1..SK_Scalar1).
-    */
-    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
-
-    /** Return the next pseudo random number as a signed 64bit value.
-    */
-    void next64(Sk64* a) {
-        SkASSERT(a);
-        a->set(this->nextS(), this->nextU());
-    }
-
-    /** Set the seed of the random object. The seed is initialized to 0 when the
-        object is first created, and is updated each time the next pseudo random
-        number is requested.
-    */
-    void setSeed(int32_t seed) { fSeed = (uint32_t)seed; }
-
-private:
-    //  See "Numerical Recipes in C", 1992 page 284 for these constants
-    enum {
-        kMul = 1664525,
-        kAdd = 1013904223
-    };
-    uint32_t fSeed;
-};
-
-#endif
-
diff --git a/include/core/SkRasterizer.h b/include/core/SkRasterizer.h
index d249898..3e662ab 100644
--- a/include/core/SkRasterizer.h
+++ b/include/core/SkRasterizer.h
@@ -18,23 +18,24 @@
 class SkPath;
 struct SkIRect;
 
-class SkRasterizer : public SkFlattenable {
+class SK_API SkRasterizer : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkRasterizer)
+
     SkRasterizer() {}
 
     /** Turn the path into a mask, respecting the specified local->device matrix.
     */
     bool rasterize(const SkPath& path, const SkMatrix& matrix,
                    const SkIRect* clipBounds, SkMaskFilter* filter,
-                   SkMask* mask, SkMask::CreateMode mode);
+                   SkMask* mask, SkMask::CreateMode mode) const;
 
-    virtual void flatten(SkFlattenableWriteBuffer& ) SK_OVERRIDE {}
 protected:
-    SkRasterizer(SkFlattenableReadBuffer&);
+    SkRasterizer(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
 
     virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix,
                              const SkIRect* clipBounds,
-                             SkMask* mask, SkMask::CreateMode mode);
+                             SkMask* mask, SkMask::CreateMode mode) const;
 
 private:
     typedef SkFlattenable INHERITED;
diff --git a/include/core/SkReader32.h b/include/core/SkReader32.h
index 37ace5c..7a8d22a 100644
--- a/include/core/SkReader32.h
+++ b/include/core/SkReader32.h
@@ -10,6 +10,10 @@
 #ifndef SkReader32_DEFINED
 #define SkReader32_DEFINED
 
+#include "SkMatrix.h"
+#include "SkPath.h"
+#include "SkRegion.h"
+#include "SkRRect.h"
 #include "SkScalar.h"
 
 class SkString;
@@ -24,20 +28,20 @@
     void setMemory(const void* data, size_t size) {
         SkASSERT(ptr_align_4(data));
         SkASSERT(SkAlign4(size) == size);
-        
+
         fBase = fCurr = (const char*)data;
         fStop = (const char*)data + size;
     }
-    
-    uint32_t size() const { return fStop - fBase; }
-    uint32_t offset() const { return fCurr - fBase; }
+
+    uint32_t size() const { return SkToU32(fStop - fBase); }
+    uint32_t offset() const { return SkToU32(fCurr - fBase); }
     bool eof() const { return fCurr >= fStop; }
     const void* base() const { return fBase; }
     const void* peek() const { return fCurr; }
 
-    uint32_t available() const { return fStop - fCurr; }
+    uint32_t available() const { return SkToU32(fStop - fCurr); }
     bool isAvailable(uint32_t size) const { return fCurr + size <= fStop; }
-    
+
     void rewind() { fCurr = fBase; }
 
     void setOffset(size_t offset) {
@@ -45,9 +49,9 @@
         SkASSERT(offset <= this->size());
         fCurr = fBase + offset;
     }
-    
+
     bool readBool() { return this->readInt() != 0; }
-    
+
     int32_t readInt() {
         SkASSERT(ptr_align_4(fCurr));
         int32_t value = *(const int32_t*)fCurr;
@@ -55,7 +59,19 @@
         SkASSERT(fCurr <= fStop);
         return value;
     }
-    
+
+    void* readPtr() {
+        void* ptr;
+        // we presume this "if" is resolved at compile-time
+        if (4 == sizeof(void*)) {
+            ptr = *(void**)fCurr;
+        } else {
+            memcpy(&ptr, fCurr, sizeof(void*));
+        }
+        fCurr += sizeof(void*);
+        return ptr;
+    }
+
     SkScalar readScalar() {
         SkASSERT(ptr_align_4(fCurr));
         SkScalar value = *(const SkScalar*)fCurr;
@@ -63,7 +79,7 @@
         SkASSERT(fCurr <= fStop);
         return value;
     }
-    
+
     const void* skip(size_t size) {
         SkASSERT(ptr_align_4(fCurr));
         const void* addr = fCurr;
@@ -71,7 +87,7 @@
         SkASSERT(fCurr <= fStop);
         return addr;
     }
-    
+
     template <typename T> const T& skipT() {
         SkASSERT(SkAlign4(sizeof(T)) == sizeof(T));
         return *(const T*)this->skip(sizeof(T));
@@ -84,12 +100,35 @@
         fCurr += SkAlign4(size);
         SkASSERT(fCurr <= fStop);
     }
-    
+
     uint8_t readU8() { return (uint8_t)this->readInt(); }
     uint16_t readU16() { return (uint16_t)this->readInt(); }
     int32_t readS32() { return this->readInt(); }
     uint32_t readU32() { return this->readInt(); }
 
+    void readPath(SkPath* path) {
+        size_t size = path->readFromMemory(this->peek());
+        SkASSERT(SkAlign4(size) == size);
+        (void)this->skip(size);
+    }
+
+    void readMatrix(SkMatrix* matrix) {
+        size_t size = matrix->readFromMemory(this->peek());
+        SkASSERT(SkAlign4(size) == size);
+        (void)this->skip(size);
+    }
+
+    SkRRect* readRRect(SkRRect* rrect) {
+        rrect->readFromMemory(this->skip(SkRRect::kSizeInMemory));
+        return rrect;
+    }
+
+    void readRegion(SkRegion* rgn) {
+        size_t size = rgn->readFromMemory(this->peek());
+        SkASSERT(SkAlign4(size) == size);
+        (void)this->skip(size);
+    }
+
     /**
      *  Read the length of a string (written by SkWriter32::writeString) into
      *  len (if len is not NULL) and return the null-ternimated address of the
@@ -108,7 +147,7 @@
     const char* fCurr;  // current position within buffer
     const char* fStop;  // end of buffer
     const char* fBase;  // beginning of buffer
-    
+
 #ifdef SK_DEBUG
     static bool ptr_align_4(const void* ptr) {
         return (((const char*)ptr - (const char*)NULL) & 3) == 0;
diff --git a/include/core/SkRect.h b/include/core/SkRect.h
index 65e7611..e6efed5 100644
--- a/include/core/SkRect.h
+++ b/include/core/SkRect.h
@@ -20,31 +20,31 @@
 struct SK_API SkIRect {
     int32_t fLeft, fTop, fRight, fBottom;
 
-    static SkIRect MakeEmpty() {
+    static SkIRect SK_WARN_UNUSED_RESULT MakeEmpty() {
         SkIRect r;
         r.setEmpty();
         return r;
     }
-    
-    static SkIRect MakeWH(int32_t w, int32_t h) {
+
+    static SkIRect SK_WARN_UNUSED_RESULT MakeWH(int32_t w, int32_t h) {
         SkIRect r;
         r.set(0, 0, w, h);
         return r;
     }
-    
-    static SkIRect MakeSize(const SkISize& size) {
+
+    static SkIRect SK_WARN_UNUSED_RESULT MakeSize(const SkISize& size) {
         SkIRect r;
         r.set(0, 0, size.width(), size.height());
         return r;
     }
-    
-    static SkIRect MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
+
+    static SkIRect SK_WARN_UNUSED_RESULT MakeLTRB(int32_t l, int32_t t, int32_t r, int32_t b) {
         SkIRect rect;
         rect.set(l, t, r, b);
         return rect;
     }
-    
-    static SkIRect MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
+
+    static SkIRect SK_WARN_UNUSED_RESULT MakeXYWH(int32_t x, int32_t y, int32_t w, int32_t h) {
         SkIRect r;
         r.set(x, y, x + w, y + h);
         return r;
@@ -54,7 +54,7 @@
     int top() const { return fTop; }
     int right() const { return fRight; }
     int bottom() const { return fBottom; }
-    
+
     /** return the left edge of the rect */
     int x() const { return fLeft; }
     /** return the top edge of the rect */
@@ -64,13 +64,31 @@
      *  (i.e. left <= right) so the result may be negative.
      */
     int width() const { return fRight - fLeft; }
-    
+
     /**
      *  Returns the rectangle's height. This does not check for a valid rect
      *  (i.e. top <= bottom) so the result may be negative.
      */
     int height() const { return fBottom - fTop; }
-    
+
+    /**
+     *  Since the center of an integer rect may fall on a factional value, this
+     *  method is defined to return (right + left) >> 1.
+     *
+     *  This is a specific "truncation" of the average, which is different than
+     *  (right + left) / 2 when the sum is negative.
+     */
+    int centerX() const { return (fRight + fLeft) >> 1; }
+
+    /**
+     *  Since the center of an integer rect may fall on a factional value, this
+     *  method is defined to return (bottom + top) >> 1
+     *
+     *  This is a specific "truncation" of the average, which is different than
+     *  (bottom + top) / 2 when the sum is negative.
+     */
+    int centerY() const { return (fBottom + fTop) >> 1; }
+
     /**
      *  Return true if the rectangle's width or height are <= 0
      */
@@ -118,7 +136,7 @@
         fLeft = fTop = SK_MinS32;
         fRight = fBottom = SK_MaxS32;
     }
-    
+
     /**
      *  Make the largest representable rectangle, but inverted (e.g. fLeft will
      *  be max 32bit and right will be min 32bit).
@@ -127,7 +145,7 @@
         fLeft = fTop = SK_MaxS32;
         fRight = fBottom = SK_MinS32;
     }
-    
+
     /** Offset set the rectangle by adding dx to its left and right,
         and adding dy to its top and bottom.
     */
@@ -142,9 +160,19 @@
         this->offset(delta.fX, delta.fY);
     }
 
+    /**
+     *  Offset this rect such its new x() and y() will equal newX and newY.
+     */
+    void offsetTo(int32_t newX, int32_t newY) {
+        fRight += newX - fLeft;
+        fBottom += newY - fTop;
+        fLeft = newX;
+        fTop = newY;
+    }
+
     /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are moved inwards,
         making the rectangle narrower. If dx is negative, then the sides are moved outwards,
-        making the rectangle wider. The same hods true for dy and the top and bottom.
+        making the rectangle wider. The same holds true for dy and the top and bottom.
     */
     void inset(int32_t dx, int32_t dy) {
         fLeft   += dx;
@@ -153,10 +181,17 @@
         fBottom -= dy;
     }
 
+   /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
+       moved outwards, making the rectangle wider. If dx is negative, then the
+       sides are moved inwards, making the rectangle narrower. The same holds
+       true for dy and the top and bottom.
+    */
+    void outset(int32_t dx, int32_t dy)  { this->inset(-dx, -dy); }
+
     bool quickReject(int l, int t, int r, int b) const {
         return l >= fRight || fLeft >= r || t >= fBottom || fTop >= b;
     }
-    
+
     /** Returns true if (x,y) is inside the rectangle and the rectangle is not
         empty. The left and top are considered to be inside, while the right
         and bottom are not. Thus for the rectangle (0, 0, 5, 10), the
@@ -185,20 +220,24 @@
     }
 
     /** Return true if this rectangle contains the specified rectangle.
-		For speed, this method does not check if either this or the specified
-		rectangles are empty, and if either is, its return value is undefined.
-		In the debugging build however, we assert that both this and the
-		specified rectangles are non-empty.
+        For speed, this method does not check if either this or the specified
+        rectangles are empty, and if either is, its return value is undefined.
+        In the debugging build however, we assert that both this and the
+        specified rectangles are non-empty.
     */
     bool containsNoEmptyCheck(int32_t left, int32_t top,
-							  int32_t right, int32_t bottom) const {
-		SkASSERT(fLeft < fRight && fTop < fBottom);
+                              int32_t right, int32_t bottom) const {
+        SkASSERT(fLeft < fRight && fTop < fBottom);
         SkASSERT(left < right && top < bottom);
 
         return fLeft <= left && fTop <= top &&
-			   fRight >= right && fBottom >= bottom;
+               fRight >= right && fBottom >= bottom;
     }
-    
+
+    bool containsNoEmptyCheck(const SkIRect& r) const {
+        return containsNoEmptyCheck(r.fLeft, r.fTop, r.fRight, r.fBottom);
+    }
+
     /** If r intersects this rectangle, return true and set this rectangle to that
         intersection, otherwise return false and do not change this rectangle.
         If either rectangle is empty, do nothing and return false.
@@ -214,7 +253,7 @@
     */
     bool intersect(const SkIRect& a, const SkIRect& b) {
         SkASSERT(&a && &b);
-        
+
         if (!a.isEmpty() && !b.isEmpty() &&
                 a.fLeft < b.fRight && b.fLeft < a.fRight &&
                 a.fTop < b.fBottom && b.fTop < a.fBottom) {
@@ -226,7 +265,7 @@
         }
         return false;
     }
-    
+
     /** If rectangles a and b intersect, return true and set this rectangle to
         that intersection, otherwise return false and do not change this
         rectangle. For speed, no check to see if a or b are empty is performed.
@@ -236,7 +275,7 @@
     bool intersectNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
         SkASSERT(&a && &b);
         SkASSERT(!a.isEmpty() && !b.isEmpty());
-        
+
         if (a.fLeft < b.fRight && b.fLeft < a.fRight &&
                 a.fTop < b.fBottom && b.fTop < a.fBottom) {
             fLeft   = SkMax32(a.fLeft,   b.fLeft);
@@ -264,15 +303,25 @@
         }
         return false;
     }
-    
+
     /** Returns true if a and b are not empty, and they intersect
-    */
+     */
     static bool Intersects(const SkIRect& a, const SkIRect& b) {
         return  !a.isEmpty() && !b.isEmpty() &&              // check for empties
-                a.fLeft < b.fRight && b.fLeft < a.fRight &&
+        a.fLeft < b.fRight && b.fLeft < a.fRight &&
+        a.fTop < b.fBottom && b.fTop < a.fBottom;
+    }
+
+    /**
+     *  Returns true if a and b intersect. debug-asserts that neither are empty.
+     */
+    static bool IntersectsNoEmptyCheck(const SkIRect& a, const SkIRect& b) {
+        SkASSERT(!a.isEmpty());
+        SkASSERT(!b.isEmpty());
+        return  a.fLeft < b.fRight && b.fLeft < a.fRight &&
                 a.fTop < b.fBottom && b.fTop < a.fBottom;
     }
-    
+
     /** Update this rectangle to enclose itself and the specified rectangle.
         If this rectangle is empty, just set it to the specified rectangle. If the specified
         rectangle is empty, do nothing.
@@ -294,7 +343,7 @@
     */
     void sort();
 
-    static const SkIRect& EmptyIRect() {
+    static const SkIRect& SK_WARN_UNUSED_RESULT EmptyIRect() {
         static const SkIRect gEmpty = { 0, 0, 0, 0 };
         return gEmpty;
     }
@@ -305,41 +354,60 @@
 struct SK_API SkRect {
     SkScalar    fLeft, fTop, fRight, fBottom;
 
-    static SkRect MakeEmpty() {
+    static SkRect SK_WARN_UNUSED_RESULT MakeEmpty() {
         SkRect r;
         r.setEmpty();
         return r;
     }
 
-    static SkRect MakeWH(SkScalar w, SkScalar h) {
+    static SkRect SK_WARN_UNUSED_RESULT MakeWH(SkScalar w, SkScalar h) {
         SkRect r;
         r.set(0, 0, w, h);
         return r;
     }
 
-    static SkRect MakeSize(const SkSize& size) {
+    static SkRect SK_WARN_UNUSED_RESULT MakeSize(const SkSize& size) {
         SkRect r;
         r.set(0, 0, size.width(), size.height());
         return r;
     }
 
-    static SkRect MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
+    static SkRect SK_WARN_UNUSED_RESULT MakeLTRB(SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
         SkRect rect;
         rect.set(l, t, r, b);
         return rect;
     }
 
-    static SkRect MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
+    static SkRect SK_WARN_UNUSED_RESULT MakeXYWH(SkScalar x, SkScalar y, SkScalar w, SkScalar h) {
         SkRect r;
         r.set(x, y, x + w, y + h);
         return r;
     }
 
+    // DEPRECATED: call Make(r)
+    static SkRect SK_WARN_UNUSED_RESULT MakeFromIRect(const SkIRect& irect) {
+        SkRect r;
+        r.set(SkIntToScalar(irect.fLeft),
+              SkIntToScalar(irect.fTop),
+              SkIntToScalar(irect.fRight),
+              SkIntToScalar(irect.fBottom));
+        return r;
+    }
+
+    static SkRect SK_WARN_UNUSED_RESULT Make(const SkIRect& irect) {
+        SkRect r;
+        r.set(SkIntToScalar(irect.fLeft),
+              SkIntToScalar(irect.fTop),
+              SkIntToScalar(irect.fRight),
+              SkIntToScalar(irect.fBottom));
+        return r;
+    }
+
     /**
      *  Return true if the rectangle's width or height are <= 0
      */
     bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
-    
+
     /**
      *  Returns true iff all values in the rect are finite. If any are
      *  infinite or NaN (or SK_FixedNaN when SkScalar is fixed) then this
@@ -347,13 +415,18 @@
      */
     bool isFinite() const {
 #ifdef SK_SCALAR_IS_FLOAT
-        // x * 0 will be NaN iff x is infinity or NaN.
-        // a + b will be NaN iff either a or b is NaN.
-        float value = fLeft * 0 + fTop * 0 + fRight * 0 + fBottom * 0;
-        
-        // value is either NaN or it is finite (zero).
+        float accum = 0;
+        accum *= fLeft;
+        accum *= fTop;
+        accum *= fRight;
+        accum *= fBottom;
+
+        // accum is either NaN or it is finite (zero).
+        SkASSERT(0 == accum || !(accum == accum));
+
         // value==value will be true iff value is not NaN
-        return value == value;
+        // TODO: is it faster to say !accum or accum==accum?
+        return accum == accum;
 #else
         // use bit-or for speed, since we don't care about short-circuting the
         // tests, and we expect the common case will be that we need to check all.
@@ -363,6 +436,8 @@
 #endif
     }
 
+    SkScalar    x() const { return fLeft; }
+    SkScalar    y() const { return fTop; }
     SkScalar    left() const { return fLeft; }
     SkScalar    top() const { return fTop; }
     SkScalar    right() const { return fRight; }
@@ -373,11 +448,11 @@
     SkScalar    centerY() const { return SkScalarHalf(fTop + fBottom); }
 
     friend bool operator==(const SkRect& a, const SkRect& b) {
-        return 0 == memcmp(&a, &b, sizeof(a));
+        return SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
     }
 
     friend bool operator!=(const SkRect& a, const SkRect& b) {
-        return 0 != memcmp(&a, &b, sizeof(a));
+        return !SkScalarsEqual((SkScalar*)&a, (SkScalar*)&b, 4);
     }
 
     /** return the 4 points that enclose the rectangle
@@ -416,15 +491,44 @@
         fBottom = SkIntToScalar(bottom);
     }
 
+    /**
+     *  Set this rectangle to be left/top at 0,0, and have the specified width
+     *  and height (automatically converted to SkScalar).
+     */
+    void isetWH(int width, int height) {
+        fLeft = fTop = 0;
+        fRight = SkIntToScalar(width);
+        fBottom = SkIntToScalar(height);
+    }
+
     /** Set this rectangle to be the bounds of the array of points.
         If the array is empty (count == 0), then set this rectangle
         to the empty rectangle (0,0,0,0)
     */
-    void set(const SkPoint pts[], int count);
+    void set(const SkPoint pts[], int count) {
+        // set() had been checking for non-finite values, so keep that behavior
+        // for now. Now that we have setBoundsCheck(), we may decide to make
+        // set() be simpler/faster, and not check for those.
+        (void)this->setBoundsCheck(pts, count);
+    }
 
     // alias for set(pts, count)
     void setBounds(const SkPoint pts[], int count) {
-        this->set(pts, count);
+        (void)this->setBoundsCheck(pts, count);
+    }
+
+    /**
+     *  Compute the bounds of the array of points, and set this rect to that
+     *  bounds and return true... unless a non-finite value is encountered,
+     *  in which case this rect is set to empty and false is returned.
+     */
+    bool setBoundsCheck(const SkPoint pts[], int count);
+
+    void set(const SkPoint& p0, const SkPoint& p1) {
+        fLeft =   SkMinScalar(p0.fX, p1.fX);
+        fRight =  SkMaxScalar(p0.fX, p1.fX);
+        fTop =    SkMinScalar(p0.fY, p1.fY);
+        fBottom = SkMaxScalar(p0.fY, p1.fY);
     }
 
     void setXYWH(SkScalar x, SkScalar y, SkScalar width, SkScalar height) {
@@ -434,6 +538,13 @@
         fBottom = y + height;
     }
 
+    void setWH(SkScalar width, SkScalar height) {
+        fLeft = 0;
+        fTop = 0;
+        fRight = width;
+        fBottom = height;
+    }
+
     /**
      *  Make the largest representable rectangle
      */
@@ -441,7 +552,7 @@
         fLeft = fTop = SK_ScalarMin;
         fRight = fBottom = SK_ScalarMax;
     }
-    
+
     /**
      *  Make the largest representable rectangle, but inverted (e.g. fLeft will
      *  be max and right will be min).
@@ -459,12 +570,22 @@
         fTop    += dy;
         fRight  += dx;
         fBottom += dy;
-    }   
+    }
 
     void offset(const SkPoint& delta) {
         this->offset(delta.fX, delta.fY);
     }
 
+    /**
+     *  Offset this rect such its new x() and y() will equal newX and newY.
+     */
+    void offsetTo(SkScalar newX, SkScalar newY) {
+        fRight += newX - fLeft;
+        fBottom += newY - fTop;
+        fLeft = newX;
+        fTop = newY;
+    }
+
     /** Inset the rectangle by (dx,dy). If dx is positive, then the sides are
         moved inwards, making the rectangle narrower. If dx is negative, then
         the sides are moved outwards, making the rectangle wider. The same holds
@@ -479,7 +600,7 @@
 
    /** Outset the rectangle by (dx,dy). If dx is positive, then the sides are
        moved outwards, making the rectangle wider. If dx is negative, then the
-       sides are moved inwards, making the rectangle narrower. The same hods
+       sides are moved inwards, making the rectangle narrower. The same holds
        true for dy and the top and bottom.
     */
     void outset(SkScalar dx, SkScalar dy)  { this->inset(-dx, -dy); }
@@ -515,7 +636,7 @@
      *  rectangle. If either rectangle is empty, do nothing and return false.
      */
     bool intersect(const SkRect& a, const SkRect& b);
-    
+
     /**
      *  Return true if rectangles a and b are not empty and intersect.
      */
@@ -524,7 +645,7 @@
                 a.fLeft < b.fRight && b.fLeft < a.fRight &&
                 a.fTop < b.fBottom && b.fTop < a.fBottom;
     }
-    
+
     /**
      *  Update this rectangle to enclose itself and the specified rectangle.
      *  If this rectangle is empty, just set it to the specified rectangle.
@@ -557,7 +678,7 @@
         fTop    = SkMinScalar(y, fTop);
         fBottom = SkMaxScalar(y, fBottom);
     }
-    
+
     /**
      *  Returns true if (p.fX,p.fY) is inside the rectangle, and the rectangle
      *  is not empty.
@@ -602,8 +723,8 @@
      */
     void round(SkIRect* dst) const {
         SkASSERT(dst);
-        dst->set(SkScalarRound(fLeft), SkScalarRound(fTop),
-                 SkScalarRound(fRight), SkScalarRound(fBottom));
+        dst->set(SkScalarRoundToInt(fLeft), SkScalarRoundToInt(fTop),
+                 SkScalarRoundToInt(fRight), SkScalarRoundToInt(fBottom));
     }
 
     /**
@@ -612,8 +733,8 @@
      */
     void roundOut(SkIRect* dst) const {
         SkASSERT(dst);
-        dst->set(SkScalarFloor(fLeft), SkScalarFloor(fTop),
-                 SkScalarCeil(fRight), SkScalarCeil(fBottom));
+        dst->set(SkScalarFloorToInt(fLeft), SkScalarFloorToInt(fTop),
+                 SkScalarCeilToInt(fRight), SkScalarCeilToInt(fBottom));
     }
 
     /**
@@ -629,6 +750,19 @@
     }
 
     /**
+     *  Set the dst rectangle by rounding "in" this rectangle, choosing the
+     *  ceil of top and left, and the floor of right and bottom. This does *not*
+     *  call sort(), so it is possible that the resulting rect is inverted...
+     *  e.g. left >= right or top >= bottom. Call isEmpty() to detect that.
+     */
+    void roundIn(SkIRect* dst) const {
+        SkASSERT(dst);
+        dst->set(SkScalarCeilToInt(fLeft), SkScalarCeilToInt(fTop),
+                 SkScalarFloorToInt(fRight), SkScalarFloorToInt(fBottom));
+    }
+
+
+    /**
      *  Swap top/bottom or left/right if there are flipped (i.e. if width()
      *  or height() would have returned a negative value.) This should be called
      *  if the edges are computed separately, and may have crossed over each
@@ -638,4 +772,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkRefCnt.h b/include/core/SkRefCnt.h
index 7af0017..0330133 100644
--- a/include/core/SkRefCnt.h
+++ b/include/core/SkRefCnt.h
@@ -11,24 +11,28 @@
 #define SkRefCnt_DEFINED
 
 #include "SkThread.h"
+#include "SkInstCnt.h"
+#include "SkTemplates.h"
 
 /** \class SkRefCnt
 
     SkRefCnt is the base class for objects that may be shared by multiple
-    objects. When a new owner wants a reference, it calls ref(). When an owner
-    wants to release its reference, it calls unref(). When the shared object's
-    reference count goes to zero as the result of an unref() call, its (virtual)
-    destructor is called. It is an error for the destructor to be called
-    explicitly (or via the object going out of scope on the stack or calling
-    delete) if getRefCnt() > 1.
+    objects. When an existing owner wants to share a reference, it calls ref().
+    When an owner wants to release its reference, it calls unref(). When the
+    shared object's reference count goes to zero as the result of an unref()
+    call, its (virtual) destructor is called. It is an error for the
+    destructor to be called explicitly (or via the object going out of scope on
+    the stack or calling delete) if getRefCnt() > 1.
 */
 class SK_API SkRefCnt : SkNoncopyable {
 public:
+    SK_DECLARE_INST_COUNT_ROOT(SkRefCnt)
+
     /** Default construct, initializing the reference count to 1.
     */
     SkRefCnt() : fRefCnt(1) {}
 
-    /**  Destruct, asserting that the reference count is 1.
+    /** Destruct, asserting that the reference count is 1.
     */
     virtual ~SkRefCnt() {
 #ifdef SK_DEBUG
@@ -45,19 +49,21 @@
     */
     void ref() const {
         SkASSERT(fRefCnt > 0);
-        sk_atomic_inc(&fRefCnt);
+        sk_atomic_inc(&fRefCnt);  // No barrier required.
     }
 
     /** Decrement the reference count. If the reference count is 1 before the
-        decrement, then call delete on the object. Note that if this is the
-        case, then the object needs to have been allocated via new, and not on
-        the stack.
+        decrement, then delete the object. Note that if this is the case, then
+        the object needs to have been allocated via new, and not on the stack.
     */
     void unref() const {
         SkASSERT(fRefCnt > 0);
+        // Release barrier (SL/S), if not provided below.
         if (sk_atomic_dec(&fRefCnt) == 1) {
-            fRefCnt = 1;    // so our destructor won't complain
-            SkDELETE(this);
+            // Aquire barrier (L/SL), if not provided above.
+            // Prevents code in dispose from happening before the decrement.
+            sk_membar_aquire__after_atomic_dec();
+            internal_dispose();
         }
     }
 
@@ -65,8 +71,46 @@
         SkASSERT(fRefCnt > 0);
     }
 
+    /**
+     *  Alias for ref(), for compatibility with scoped_refptr.
+     */
+    void AddRef() { this->ref(); }
+
+    /**
+     *  Alias for unref(), for compatibility with scoped_refptr.
+     */
+    void Release() { this->unref(); }
+
+protected:
+    /**
+     *  Allow subclasses to call this if they've overridden internal_dispose
+     *  so they can reset fRefCnt before the destructor is called. Should only
+     *  be called right before calling through to inherited internal_dispose()
+     *  or before calling the destructor.
+     */
+    void internal_dispose_restore_refcnt_to_1() const {
+#ifdef SK_DEBUG
+        SkASSERT(0 == fRefCnt);
+        fRefCnt = 1;
+#endif
+    }
+
 private:
+    /**
+     *  Called when the ref count goes to 0.
+     */
+    virtual void internal_dispose() const {
+        this->internal_dispose_restore_refcnt_to_1();
+        SkDELETE(this);
+    }
+
+    friend class SkWeakRefCnt;
+    friend class GrTexture;     // to allow GrTexture's internal_dispose to
+                                // call SkRefCnt's & directly set fRefCnt (to 1)
+
     mutable int32_t fRefCnt;
+
+    typedef SkNoncopyable INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -83,12 +127,21 @@
     } while (0)
 
 
-/** Check if the argument is non-null, and if so, call obj->ref()
+/** Call obj->ref() and return obj. The obj must not be NULL.
  */
-template <typename T> static inline void SkSafeRef(T* obj) {
+template <typename T> static inline T* SkRef(T* obj) {
+    SkASSERT(obj);
+    obj->ref();
+    return obj;
+}
+
+/** Check if the argument is non-null, and if so, call obj->ref() and return obj.
+ */
+template <typename T> static inline T* SkSafeRef(T* obj) {
     if (obj) {
         obj->ref();
     }
+    return obj;
 }
 
 /** Check if the argument is non-null, and if so, call obj->unref()
@@ -116,6 +169,12 @@
         fObj = obj;
     }
 
+    void swap(SkAutoTUnref* other) {
+        T* tmp = fObj;
+        fObj = other->fObj;
+        other->fObj = tmp;
+    }
+
     /**
      *  Return the hosted object (which may be null), transferring ownership.
      *  The reference count is not modified, and the internal ptr is set to NULL
@@ -128,6 +187,31 @@
         return obj;
     }
 
+    /**
+     * BlockRef<B> is a type which inherits from B, cannot be created,
+     * and makes ref and unref private.
+     */
+    template<typename B> class BlockRef : public B {
+    private:
+        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);
+    }
+    operator T*() { return fObj; }
+
 private:
     T*  fObj;
 };
@@ -178,4 +262,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkRefDict.h b/include/core/SkRefDict.h
deleted file mode 100644
index 6751121..0000000
--- a/include/core/SkRefDict.h
+++ /dev/null
@@ -1,54 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkRefDict_DEFINED
-#define SkRefDict_DEFINED
-
-#include "SkRefCnt.h"
-
-/**
- *  A dictionary of string,refcnt pairs. The dictionary is also an owner of the
- *  refcnt objects while they are contained.
- */
-class SK_API SkRefDict : SkNoncopyable {
-public:
-    SkRefDict();
-    ~SkRefDict();
-
-    /**
-     *  Return the data associated with name[], or NULL if no matching entry
-     *  is found. The reference-count of the entry is not affected.
-     */
-    SkRefCnt* find(const char name[]) const;
-    
-    /**
-     *  If data is NULL, remove (if present) the entry matching name and call
-     *  prev_data->unref() on the data for the matching entry.
-     *  If data is not-NULL, replace the existing entry matching name and
-     *  call (prev_data->unref()), or add a new one. In either case,
-     *  data->ref() is called.
-     */
-    void set(const char name[], SkRefCnt* data);
-
-    /**
-     *  Remove the matching entry (if found) and unref its data.
-     */
-    void remove(const char name[]) { this->set(name, NULL); }
-
-    /**
-     *  Remove all entries, and unref() their associated data.
-     */
-    void removeAll();
-
-private:
-    struct Impl;
-    Impl* fImpl;
-};
-
-#endif
diff --git a/include/core/SkRegion.h b/include/core/SkRegion.h
index 7623b82..ab8f220 100644
--- a/include/core/SkRegion.h
+++ b/include/core/SkRegion.h
@@ -33,7 +33,7 @@
     enum {
         kRunTypeSentinel = 0x7FFFFFFF
     };
-    
+
     SkRegion();
     SkRegion(const SkRegion&);
     explicit SkRegion(const SkIRect&);
@@ -53,7 +53,7 @@
     bool operator!=(const SkRegion& other) const {
         return !(*this == other);
     }
-    
+
     /**
      *  Replace this region with the specified region, and return true if the
      *  resulting region is non-empty.
@@ -117,7 +117,7 @@
      *  @return true if the resulting region is non-empty
      */
     bool setRects(const SkIRect rects[], int count);
-    
+
     /**
      *  Set this region to the specified region, and return true if it is
      *  non-empty.
@@ -131,13 +131,13 @@
      *  drawn by the path (with no antialiasing) with the specified clip.
      */
     bool setPath(const SkPath&, const SkRegion& clip);
-    
+
     /**
      *  Returns true if the specified rectangle has a non-empty intersection
      *  with this region.
      */
     bool intersects(const SkIRect&) const;
-    
+
     /**
      *  Returns true if the specified region has a non-empty intersection
      *  with this region.
@@ -185,14 +185,14 @@
     bool quickContains(int32_t left, int32_t top, int32_t right,
                        int32_t bottom) const {
         SkASSERT(this->isEmpty() == fBounds.isEmpty()); // valid region
-        
+
         return left < right && top < bottom &&
                fRunHead == SkRegion_gRectRunHeadPtr &&  // this->isRect()
                /* fBounds.contains(left, top, right, bottom); */
                fBounds.fLeft <= left && fBounds.fTop <= top &&
                fBounds.fRight >= right && fBounds.fBottom >= bottom;
     }
-    
+
     /**
      *  Return true if this region is empty, or if the specified rectangle does
      *  not intersect the region. Returning false is not a guarantee that they
@@ -236,14 +236,14 @@
         kReverseDifference_Op,
         kReplace_Op     //!< replace the dst region with the op region
     };
-    
+
     /**
      *  Set this region to the result of applying the Op to this region and the
      *  specified rectangle: this = (this op rect).
      *  Return true if the resulting region is non-empty.
      */
     bool op(const SkIRect& rect, Op op) { return this->op(*this, rect, op); }
-    
+
     /**
      *  Set this region to the result of applying the Op to this region and the
      *  specified rectangle: this = (this op rect).
@@ -254,7 +254,7 @@
         rect.set(left, top, right, bottom);
         return this->op(*this, rect, op);
     }
-    
+
     /**
      *  Set this region to the result of applying the Op to this region and the
      *  specified region: this = (this op rgn).
@@ -351,13 +351,13 @@
      *  Write the region to the buffer, and return the number of bytes written.
      *  If buffer is NULL, it still returns the number of bytes.
      */
-    uint32_t flatten(void* buffer) const;
+    uint32_t writeToMemory(void* buffer) const;
 
     /**
      *  Initialized the region from the buffer, returning the number
      *  of bytes actually read.
      */
-    uint32_t unflatten(const void* buffer);
+    uint32_t readFromMemory(const void* buffer);
 
     /**
      *  Returns a reference to a global empty region. Just a convenience for
@@ -378,28 +378,53 @@
     };
 
     enum {
-        kRectRegionRuns = 6 // need to store a region of a rect [T B L R S S]        
+        // T
+        // [B N L R S]
+        // S
+        kRectRegionRuns = 7
     };
 
     friend class android::Region;    // needed for marshalling efficiently
-    void allocateRuns(int count); // allocate space for count runs
 
     struct RunHead;
 
+    // allocate space for count runs
+    void allocateRuns(int count);
+    void allocateRuns(int count, int ySpanCount, int intervalCount);
+    void allocateRuns(const RunHead& src);
+
     SkIRect     fBounds;
     RunHead*    fRunHead;
 
-    void            freeRuns();
-    const RunType*  getRuns(RunType tmpStorage[], int* count) const;
-    bool            setRuns(RunType runs[], int count);
+    void freeRuns();
+
+    /**
+     *  Return the runs from this region, consing up fake runs if the region
+     *  is empty or a rect. In those 2 cases, we use tmpStorage to hold the
+     *  run data.
+     */
+    const RunType*  getRuns(RunType tmpStorage[], int* intervals) const;
+
+    // This is called with runs[] that do not yet have their interval-count
+    // field set on each scanline. That is computed as part of this call
+    // (inside ComputeRunBounds).
+    bool setRuns(RunType runs[], int count);
 
     int count_runtype_values(int* itop, int* ibot) const;
-    
+
     static void BuildRectRuns(const SkIRect& bounds,
                               RunType runs[kRectRegionRuns]);
-    // returns true if runs are just a rect
-    static bool ComputeRunBounds(const RunType runs[], int count,
-                                 SkIRect* bounds);
+
+    // If the runs define a simple rect, return true and set bounds to that
+    // rect. If not, return false and ignore bounds.
+    static bool RunsAreARect(const SkRegion::RunType runs[], int count,
+                             SkIRect* bounds);
+
+    /**
+     *  If the last arg is null, just return if the result is non-empty,
+     *  else store the result in the last arg.
+     */
+    static bool Oper(const SkRegion&, const SkRegion&, SkRegion::Op, SkRegion*);
 
     friend struct RunHead;
     friend class Iterator;
diff --git a/include/core/SkRelay.h b/include/core/SkRelay.h
deleted file mode 100644
index ef09ce4..0000000
--- a/include/core/SkRelay.h
+++ /dev/null
@@ -1,43 +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 SkTRelay_DEFINED
-#define SkTRelay_DEFINED
-
-#include "SkRefCnt.h"
-
-/**
- *  Similar to a weakptr in java, a Relay allows for a back-ptr to an
- *  object to be "safe", without using a hard reference-count.
- *
- *  Typically, the target creates a Relay with a pointer to itself. Whenever it
- *  wants to have another object maintain a safe-ptr to it, it gives them a
- *  Relay, which they ref()/unref(). Through the Relay each external object can
- *  retrieve a pointer to the Target. However, when the Target goes away, it
- *  clears the Relay pointer to it (relay->set(NULL)) and then unref()s the
- *  Relay. The other objects still have a ref on the Relay, but now when they
- *  call get() the receive a NULL.
- */
-template <template T> class SkTRelay : public SkRefCnt {
-public:
-    SkTRelay(T* ptr) : fPtr(ptr) {}
-
-    // consumers call this
-    T* get() const { return fPtr; }
-
-    // producer calls this
-    void set(T* ptr) { fPtr = ptr; }
-
-    void clear() { this->set(NULL); }
-
-private:
-    T* fPtr;
-};
-
-#endif
diff --git a/include/core/SkScalar.h b/include/core/SkScalar.h
index 71aad98..02d9790 100644
--- a/include/core/SkScalar.h
+++ b/include/core/SkScalar.h
@@ -29,8 +29,6 @@
         as a 16.16 fixed point integer.
     */
     typedef float   SkScalar;
-    extern const uint32_t gIEEENotANumber;
-    extern const uint32_t gIEEEInfinity;
 
     /** SK_Scalar1 is defined to be 1.0 represented as an SkScalar
     */
@@ -40,7 +38,10 @@
     #define SK_ScalarHalf           (0.5f)
     /** SK_ScalarInfinity is defined to be infinity as an SkScalar
     */
-    #define SK_ScalarInfinity           (*(const float*)&gIEEEInfinity)
+    #define SK_ScalarInfinity       SK_FloatInfinity
+    /** SK_ScalarNegativeInfinity is defined to be negative infinity as an SkScalar
+    */
+    #define SK_ScalarNegativeInfinity       SK_FloatNegativeInfinity
     /** SK_ScalarMax is defined to be the largest value representable as an SkScalar
     */
     #define SK_ScalarMax            (3.402823466e+38f)
@@ -49,17 +50,24 @@
     #define SK_ScalarMin            (-SK_ScalarMax)
     /** SK_ScalarNaN is defined to be 'Not a Number' as an SkScalar
     */
-    #define SK_ScalarNaN      (*(const float*)(const void*)&gIEEENotANumber)
+    #define SK_ScalarNaN            SK_FloatNaN
     /** SkScalarIsNaN(n) returns true if argument is not a number
     */
     static inline bool SkScalarIsNaN(float x) { return x != x; }
+
     /** Returns true if x is not NaN and not infinite */
     static inline bool SkScalarIsFinite(float x) {
-        uint32_t bits = SkFloat2Bits(x);    // need unsigned for our shifts
-        int exponent = bits << 1 >> 24;
-        return exponent != 0xFF;
+        // We rely on the following behavior of infinities and nans
+        // 0 * finite --> 0
+        // 0 * infinity --> NaN
+        // 0 * NaN --> NaN
+        float prod = x * 0;
+        // At this point, prod will either be NaN or 0
+        // Therefore we can return (prod == prod) or (0 == prod).
+        return prod == prod;
     }
-#ifdef SK_DEBUG
+
+#if defined(SK_DEBUG) && !defined(SK_BUILD_FOR_ANDROID)
     /** SkIntToScalar(n) returns its integer argument as an SkScalar
      *
      * If we're compiling in DEBUG mode, and can thus afford some extra runtime
@@ -84,7 +92,7 @@
     static inline float SkIntToScalar(unsigned long param) {
         return (float)param;
     }
-    static inline float SkIntToScalar(float param) {
+    static inline float SkIntToScalar(float /* param */) {
         /* If the parameter passed into SkIntToScalar is a float,
          * one of two things has happened:
          * 1. the parameter was an SkScalar (which is typedef'd to float)
@@ -175,6 +183,9 @@
     /** Returns the square root of the SkScalar
     */
     #define SkScalarSqrt(x)         sk_float_sqrt(x)
+    /** Returns b to the e
+    */
+    #define SkScalarPow(b, e)       sk_float_pow(b, e)
     /** Returns the average of two SkScalars (a+b)/2
     */
     #define SkScalarAve(a, b)       (((a) + (b)) * 0.5f)
@@ -212,7 +223,8 @@
 
     #define SK_Scalar1              SK_Fixed1
     #define SK_ScalarHalf           SK_FixedHalf
-    #define SK_ScalarInfinity   SK_FixedMax
+    #define SK_ScalarInfinity           SK_FixedMax
+    #define SK_ScalarNegativeInfinity   SK_FixedMin
     #define SK_ScalarMax            SK_FixedMax
     #define SK_ScalarMin            SK_FixedMin
     #define SK_ScalarNaN            SK_FixedNaN
@@ -222,13 +234,11 @@
     #define SkIntToScalar(n)        SkIntToFixed(n)
     #define SkFixedToScalar(x)      (x)
     #define SkScalarToFixed(x)      (x)
-    #ifdef SK_CAN_USE_FLOAT
-        #define SkScalarToFloat(n)  SkFixedToFloat(n)
-        #define SkFloatToScalar(n)  SkFloatToFixed(n)
+    #define SkScalarToFloat(n)  SkFixedToFloat(n)
+    #define SkFloatToScalar(n)  SkFloatToFixed(n)
 
-        #define SkScalarToDouble(n) SkFixedToDouble(n)
-        #define SkDoubleToScalar(n) SkDoubleToFixed(n)
-    #endif
+    #define SkScalarToDouble(n) SkFixedToDouble(n)
+    #define SkDoubleToScalar(n) SkDoubleToFixed(n)
     #define SkScalarFraction(x)     SkFixedFraction(x)
 
     #define SkScalarFloorToScalar(x)    SkFixedFloorToFixed(x)
@@ -305,19 +315,16 @@
 
 #define SK_ScalarNearlyZero         (SK_Scalar1 / (1 << 12))
 
-/*  <= is slower than < for floats, so we use < for our tolerance test
-*/
-
 static inline bool SkScalarNearlyZero(SkScalar x,
                                     SkScalar tolerance = SK_ScalarNearlyZero) {
-    SkASSERT(tolerance > 0);
-    return SkScalarAbs(x) < tolerance;
+    SkASSERT(tolerance >= 0);
+    return SkScalarAbs(x) <= tolerance;
 }
 
 static inline bool SkScalarNearlyEqual(SkScalar x, SkScalar y,
                                      SkScalar tolerance = SK_ScalarNearlyZero) {
-    SkASSERT(tolerance > 0);
-    return SkScalarAbs(x-y) < tolerance;
+    SkASSERT(tolerance >= 0);
+    return SkScalarAbs(x-y) <= tolerance;
 }
 
 /** Linearly interpolate between A and B, based on t.
@@ -331,6 +338,12 @@
     return A + SkScalarMul(B - A, t);
 }
 
+static inline SkScalar SkScalarLog2(SkScalar x) {
+    static const SkScalar log2_conversion_factor = SkScalarDiv(1, SkScalarLog(2));
+
+    return SkScalarMul(SkScalarLog(x), log2_conversion_factor);
+}
+
 /** Interpolate along the function described by (keys[length], values[length])
     for the passed searchKey.  SearchKeys outside the range keys[0]-keys[Length]
     clamp to the min or max value.  This function was inspired by a desire
@@ -344,4 +357,21 @@
 SkScalar SkScalarInterpFunc(SkScalar searchKey, const SkScalar keys[],
                             const SkScalar values[], int length);
 
+/*
+ *  Helper to compare an array of scalars.
+ */
+static inline bool SkScalarsEqual(const SkScalar a[], const SkScalar b[], int n) {
+#ifdef SK_SCALAR_IS_FLOAT
+    SkASSERT(n >= 0);
+    for (int i = 0; i < n; ++i) {
+        if (a[i] != b[i]) {
+            return false;
+        }
+    }
+    return true;
+#else
+    return 0 == memcmp(a, b, n * sizeof(SkScalar));
+#endif
+}
+
 #endif
diff --git a/include/core/SkScalarCompare.h b/include/core/SkScalarCompare.h
index 0842fdd..5361294 100644
--- a/include/core/SkScalarCompare.h
+++ b/include/core/SkScalarCompare.h
@@ -36,4 +36,3 @@
 #endif
 
 #endif
-
diff --git a/include/core/SkScalerContext.h b/include/core/SkScalerContext.h
deleted file mode 100644
index 4a0c70d..0000000
--- a/include/core/SkScalerContext.h
+++ /dev/null
@@ -1,387 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkScalerContext_DEFINED
-#define SkScalerContext_DEFINED
-
-#include "SkMask.h"
-#include "SkMatrix.h"
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkPoint.h"
-#include "SkTypeface.h"
-
-#ifdef SK_BUILD_FOR_ANDROID
-#include "SkLanguage.h"
-#endif
-
-//#define SK_USE_COLOR_LUMINANCE
-
-class SkDescriptor;
-class SkMaskFilter;
-class SkPathEffect;
-class SkRasterizer;
-
-// needs to be != to any valid SkMask::Format
-#define MASK_FORMAT_UNKNOWN         (0xFF)
-#define MASK_FORMAT_JUST_ADVANCE    MASK_FORMAT_UNKNOWN
-
-#define kMaxGlyphWidth (1<<13)
-
-struct SkGlyph {
-    void*       fImage;
-    SkPath*     fPath;
-    SkFixed     fAdvanceX, fAdvanceY;
-
-    uint32_t    fID;
-    uint16_t    fWidth, fHeight;
-    int16_t     fTop, fLeft;
-
-    uint8_t     fMaskFormat;
-    int8_t      fRsbDelta, fLsbDelta;  // used by auto-kerning
-
-    void init(uint32_t id) {
-        fID             = id;
-        fImage          = NULL;
-        fPath           = NULL;
-        fMaskFormat     = MASK_FORMAT_UNKNOWN;
-    }
-
-    /**
-     *  Compute the rowbytes for the specified width and mask-format.
-     */
-    static unsigned ComputeRowBytes(unsigned width, SkMask::Format format) {
-        unsigned rb = width;
-        if (SkMask::kBW_Format == format) {
-            rb = (rb + 7) >> 3;
-		} else if (SkMask::kARGB32_Format == format ||
-                   SkMask::kLCD32_Format == format)
-        {
-			rb <<= 2;
-		} else if (SkMask::kLCD16_Format == format) {
-			rb = SkAlign4(rb << 1);
-        } else {
-            rb = SkAlign4(rb);
-        }
-        return rb;
-    }
-
-    unsigned rowBytes() const {
-        return ComputeRowBytes(fWidth, (SkMask::Format)fMaskFormat);
-    }
-
-    bool isJustAdvance() const {
-        return MASK_FORMAT_JUST_ADVANCE == fMaskFormat;
-    }
-
-    bool isFullMetrics() const {
-        return MASK_FORMAT_JUST_ADVANCE != fMaskFormat;
-    }
-
-    uint16_t getGlyphID() const {
-        return ID2Code(fID);
-    }
-
-    unsigned getGlyphID(unsigned baseGlyphCount) const {
-        unsigned code = ID2Code(fID);
-        SkASSERT(code >= baseGlyphCount);
-        return code - baseGlyphCount;
-    }
-
-    unsigned getSubX() const {
-        return ID2SubX(fID);
-    }
-
-    SkFixed getSubXFixed() const {
-        return SubToFixed(ID2SubX(fID));
-    }
-
-    SkFixed getSubYFixed() const {
-        return SubToFixed(ID2SubY(fID));
-    }
-
-    size_t computeImageSize() const;
-
-    /** Call this to set all of the metrics fields to 0 (e.g. if the scaler
-        encounters an error measuring a glyph). Note: this does not alter the
-        fImage, fPath, fID, fMaskFormat fields.
-     */
-    void zeroMetrics();
-
-    enum {
-        kSubBits = 2,
-        kSubMask = ((1 << kSubBits) - 1),
-        kSubShift = 24, // must be large enough for glyphs and unichars
-        kCodeMask = ((1 << kSubShift) - 1),
-        // relative offsets for X and Y subpixel bits
-        kSubShiftX = kSubBits,
-        kSubShiftY = 0
-    };
-
-    static unsigned ID2Code(uint32_t id) {
-        return id & kCodeMask;
-    }
-
-    static unsigned ID2SubX(uint32_t id) {
-        return id >> (kSubShift + kSubShiftX);
-    }
-
-    static unsigned ID2SubY(uint32_t id) {
-        return (id >> (kSubShift + kSubShiftY)) & kSubMask;
-    }
-
-    static unsigned FixedToSub(SkFixed n) {
-        return (n >> (16 - kSubBits)) & kSubMask;
-    }
-
-    static SkFixed SubToFixed(unsigned sub) {
-        SkASSERT(sub <= kSubMask);
-        return sub << (16 - kSubBits);
-    }
-
-    static uint32_t MakeID(unsigned code) {
-        return code;
-    }
-
-    static uint32_t MakeID(unsigned code, SkFixed x, SkFixed y) {
-        SkASSERT(code <= kCodeMask);
-        x = FixedToSub(x);
-        y = FixedToSub(y);
-        return (x << (kSubShift + kSubShiftX)) |
-               (y << (kSubShift + kSubShiftY)) |
-               code;
-    }
-
-    void toMask(SkMask* mask) const;
-};
-
-class SkScalerContext {
-public:
-    enum Flags {
-        kFrameAndFill_Flag        = 0x0001,
-        kDevKernText_Flag         = 0x0002,
-        kEmbeddedBitmapText_Flag  = 0x0004,
-        kEmbolden_Flag            = 0x0008,
-        kSubpixelPositioning_Flag = 0x0010,
-        kAutohinting_Flag         = 0x0020,
-        kVertical_Flag            = 0x0040,
-
-        // together, these two flags resulting in a two bit value which matches
-        // up with the SkPaint::Hinting enum.
-        kHinting_Shift            = 7, // to shift into the other flags above
-        kHintingBit1_Flag         = 0x0080,
-        kHintingBit2_Flag         = 0x0100,
-
-        // these should only ever be set if fMaskFormat is LCD16 or LCD32
-        kLCD_Vertical_Flag        = 0x0200,    // else Horizontal
-        kLCD_BGROrder_Flag        = 0x0400,    // else RGB order
-
-        // Generate A8 from LCD source (for GDI), only meaningful if fMaskFormat is kA8
-        // Perhaps we can store this (instead) in fMaskFormat, in hight bit?
-        kGenA8FromLCD_Flag        = 0x0800,
-
-#ifdef SK_USE_COLOR_LUMINANCE
-        kLuminance_Bits           = 3,
-#else
-        // luminance : 0 for black text, kLuminance_Max for white text
-        kLuminance_Shift          = 13, // shift to land in the high 3-bits of Flags
-        kLuminance_Bits           = 3,  // ensure Flags doesn't exceed 16bits
-#endif
-    };
-    
-    // computed values
-    enum {
-        kHinting_Mask   = kHintingBit1_Flag | kHintingBit2_Flag,
-#ifdef SK_USE_COLOR_LUMINANCE
-#else
-        kLuminance_Max  = (1 << kLuminance_Bits) - 1,
-        kLuminance_Mask = kLuminance_Max << kLuminance_Shift,
-#endif
-    };
-
-    struct Rec {
-        uint32_t    fOrigFontID;
-        uint32_t    fFontID;
-        SkScalar    fTextSize, fPreScaleX, fPreSkewX;
-        SkScalar    fPost2x2[2][2];
-        SkScalar    fFrameWidth, fMiterLimit;
-#ifdef SK_USE_COLOR_LUMINANCE
-        uint32_t    fLumBits;
-#endif
-#ifdef SK_BUILD_FOR_ANDROID
-        SkLanguage fLanguage;
-        SkPaint::FontVariant fFontVariant;
-#endif
-        uint8_t     fMaskFormat;
-        uint8_t     fStrokeJoin;
-        uint16_t    fFlags;
-        // Warning: when adding members note that the size of this structure
-        // must be a multiple of 4. SkDescriptor requires that its arguments be
-        // multiples of four and this structure is put in an SkDescriptor in
-        // SkPaint::MakeRec.
-
-        void    getMatrixFrom2x2(SkMatrix*) const;
-        void    getLocalMatrix(SkMatrix*) const;
-        void    getSingleMatrix(SkMatrix*) const;
-
-        SkPaint::Hinting getHinting() const {
-            unsigned hint = (fFlags & kHinting_Mask) >> kHinting_Shift;
-            return static_cast<SkPaint::Hinting>(hint);
-        }
-
-        void setHinting(SkPaint::Hinting hinting) {
-            fFlags = (fFlags & ~kHinting_Mask) | (hinting << kHinting_Shift);
-        }
-        
-        SkMask::Format getFormat() const {
-            return static_cast<SkMask::Format>(fMaskFormat);
-        }
-#ifdef SK_USE_COLOR_LUMINANCE
-        SkColor getLuminanceColor() const {
-            return fLumBits;
-        }
-        
-        void setLuminanceColor(SkColor c) {
-            fLumBits = c;
-        }
-#else
-        unsigned getLuminanceBits() const {
-            return (fFlags & kLuminance_Mask) >> kLuminance_Shift;
-        }
-        
-        void setLuminanceBits(unsigned lum) {
-            SkASSERT(lum <= kLuminance_Max);
-            fFlags = (fFlags & ~kLuminance_Mask) | (lum << kLuminance_Shift);
-        }
-
-        U8CPU getLuminanceByte() const {
-            SkASSERT(3 == kLuminance_Bits);
-            unsigned lum = this->getLuminanceBits();
-            lum |= (lum << kLuminance_Bits);
-            lum |= (lum << kLuminance_Bits*2);
-            return lum >> (4*kLuminance_Bits - 8);
-        }
-#endif
-    };
-
-    SkScalerContext(const SkDescriptor* desc);
-    virtual ~SkScalerContext();
-
-    SkMask::Format getMaskFormat() const {
-        return (SkMask::Format)fRec.fMaskFormat;
-    }
-
-    bool isSubpixel() const {
-        return SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
-    }
-    
-    // remember our glyph offset/base
-    void setBaseGlyphCount(unsigned baseGlyphCount) {
-        fBaseGlyphCount = baseGlyphCount;
-    }
-
-    /** Return the corresponding glyph for the specified unichar. Since contexts
-        may be chained (under the hood), the glyphID that is returned may in
-        fact correspond to a different font/context. In that case, we use the
-        base-glyph-count to know how to translate back into local glyph space.
-     */
-    uint16_t charToGlyphID(SkUnichar uni);
-
-    /** Map the glyphID to its glyph index, and then to its char code. Unmapped
-        glyphs return zero.
-    */
-    SkUnichar glyphIDToChar(uint16_t glyphID);
-
-    unsigned    getGlyphCount() { return this->generateGlyphCount(); }
-    void        getAdvance(SkGlyph*);
-    void        getMetrics(SkGlyph*);
-    void        getImage(const SkGlyph&);
-    void        getPath(const SkGlyph&, SkPath*);
-    void        getFontMetrics(SkPaint::FontMetrics* mX,
-                               SkPaint::FontMetrics* mY);
-
-#ifdef SK_BUILD_FOR_ANDROID
-    // This function must be public for SkTypeface_android.h, but should not be
-    // called by other callers
-    SkFontID findTypefaceIdForChar(SkUnichar uni);
-
-    unsigned getBaseGlyphCount(SkUnichar charCode);
-#endif
-
-    static inline void MakeRec(const SkPaint&, const SkMatrix*, Rec* rec);
-    static inline void PostMakeRec(Rec*);
-
-    static SkScalerContext* Create(const SkDescriptor*);
-
-protected:
-    Rec         fRec;
-    unsigned    fBaseGlyphCount;
-
-    virtual unsigned generateGlyphCount() = 0;
-    virtual uint16_t generateCharToGlyph(SkUnichar) = 0;
-    virtual void generateAdvance(SkGlyph*) = 0;
-    virtual void generateMetrics(SkGlyph*) = 0;
-    virtual void generateImage(const SkGlyph&) = 0;
-    virtual void generatePath(const SkGlyph&, SkPath*) = 0;
-    virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
-                                     SkPaint::FontMetrics* mY) = 0;
-    // default impl returns 0, indicating failure.
-    virtual SkUnichar generateGlyphToChar(uint16_t);
-
-    void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
-
-private:
-    SkScalerContext* getContextFromChar(SkUnichar uni, unsigned& glyphID);
-
-    SkPathEffect*   fPathEffect;
-    SkMaskFilter*   fMaskFilter;
-    SkRasterizer*   fRasterizer;
-    SkScalar        fDevFrameWidth;
-
-    // if this is set, we draw the image from a path, rather than
-    // calling generateImage.
-    bool fGenerateImageFromPath;
-
-    void internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
-                         SkPath* devPath, SkMatrix* fillToDevMatrix);
-
-    // return the next context, treating fNextContext as a cache of the answer
-    SkScalerContext* getNextContext();
-
-    // returns the right context from our link-list for this glyph. If no match
-    // is found, just returns the original context (this)
-    SkScalerContext* getGlyphContext(const SkGlyph& glyph);
-
-    // link-list of context, to handle missing chars. null-terminated.
-    SkScalerContext* fNextContext;
-};
-
-#define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
-#define kPathEffect_SkDescriptorTag     SkSetFourByteTag('p', 't', 'h', 'e')
-#define kMaskFilter_SkDescriptorTag     SkSetFourByteTag('m', 's', 'k', 'f')
-#define kRasterizer_SkDescriptorTag     SkSetFourByteTag('r', 'a', 's', 't')
-
-///////////////////////////////////////////////////////////////////////////////
-
-enum SkAxisAlignment {
-    kNone_SkAxisAlignment,
-    kX_SkAxisAlignment,
-    kY_SkAxisAlignment
-};
-
-/**
- *  Return the axis (if any) that the baseline for horizontal text will land on
- *  after running through the specified matrix.
- *
- *  As an example, the identity matrix will return kX_SkAxisAlignment
- */
-SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix);
-
-#endif
-
diff --git a/include/core/SkScan.h b/include/core/SkScan.h
deleted file mode 100644
index 61ba1b0..0000000
--- a/include/core/SkScan.h
+++ /dev/null
@@ -1,138 +0,0 @@
-
-/*
- * Copyright 2011 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 SkScan_DEFINED
-#define SkScan_DEFINED
-
-#include "SkRect.h"
-
-class SkRasterClip;
-class SkRegion;
-class SkBlitter;
-class SkPath;
-
-/** Defines a fixed-point rectangle, identical to the integer SkIRect, but its
-    coordinates are treated as SkFixed rather than int32_t.
-*/
-typedef SkIRect SkXRect;
-
-class SkScan {
-public:
-    static void FillPath(const SkPath&, const SkIRect&, SkBlitter*);
-
-    ///////////////////////////////////////////////////////////////////////////
-    // rasterclip
-
-    static void FillIRect(const SkIRect&, const SkRasterClip&, SkBlitter*);
-    static void FillXRect(const SkXRect&, const SkRasterClip&, SkBlitter*);
-#ifdef SK_SCALAR_IS_FIXED
-    static void FillRect(const SkRect& rect, const SkRasterClip& clip,
-                         SkBlitter* blitter) {
-        SkScan::FillXRect(*(const SkXRect*)&rect, clip, blitter);
-    }
-    static void AntiFillRect(const SkRect& rect, const SkRasterClip& clip,
-                             SkBlitter* blitter) {
-        SkScan::AntiFillXRect(*(const SkXRect*)&rect, clip, blitter);
-    }
-#else
-    static void FillRect(const SkRect&, const SkRasterClip&, SkBlitter*);
-    static void AntiFillRect(const SkRect&, const SkRasterClip&, SkBlitter*);
-#endif
-    static void AntiFillXRect(const SkXRect&, const SkRasterClip&, SkBlitter*);
-    static void FillPath(const SkPath&, const SkRasterClip&, SkBlitter*);
-    static void AntiFillPath(const SkPath&, const SkRasterClip&, SkBlitter*);
-    static void FrameRect(const SkRect&, const SkPoint& strokeSize,
-                          const SkRasterClip&, SkBlitter*);
-    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 HairRect(const SkRect&, const SkRasterClip&, SkBlitter*);
-    static void AntiHairRect(const SkRect&, const SkRasterClip&, SkBlitter*);
-    static void HairPath(const SkPath&, const SkRasterClip&, SkBlitter*);
-    static void AntiHairPath(const SkPath&, const SkRasterClip&, SkBlitter*);
-
-private:
-    friend class SkAAClip;
-    friend class SkRegion;
-
-    static void FillIRect(const SkIRect&, const SkRegion* clip, SkBlitter*);
-    static void FillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
-#ifdef SK_SCALAR_IS_FIXED
-    static void FillRect(const SkRect& rect, const SkRegion* clip,
-                         SkBlitter* blitter) {
-        SkScan::FillXRect(*(const SkXRect*)&rect, clip, blitter);
-    }
-    static void AntiFillRect(const SkRect& rect, const SkRegion* clip,
-                             SkBlitter* blitter) {
-        SkScan::AntiFillXRect(*(const SkXRect*)&rect, clip, blitter);
-    }
-#else
-    static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
-    static void AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
-#endif
-    static void AntiFillXRect(const SkXRect&, const SkRegion*, SkBlitter*);
-    static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
-    static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*,
-                             bool forceRLE = false);
-    static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
-    
-    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*);
-};
-
-/** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates
-    from int to SkFixed. Does not check for overflow if the src coordinates
-    exceed 32K
-*/
-static inline void XRect_set(SkXRect* xr, const SkIRect& src) {
-    xr->fLeft = SkIntToFixed(src.fLeft);
-    xr->fTop = SkIntToFixed(src.fTop);
-    xr->fRight = SkIntToFixed(src.fRight);
-    xr->fBottom = SkIntToFixed(src.fBottom);
-}
-
-/** Assign an SkXRect from a SkRect, by promoting the src rect's coordinates
-    from SkScalar to SkFixed. Does not check for overflow if the src coordinates
-    exceed 32K
-*/
-static inline void XRect_set(SkXRect* xr, const SkRect& src) {
-    xr->fLeft = SkScalarToFixed(src.fLeft);
-    xr->fTop = SkScalarToFixed(src.fTop);
-    xr->fRight = SkScalarToFixed(src.fRight);
-    xr->fBottom = SkScalarToFixed(src.fBottom);
-}
-
-/** Round the SkXRect coordinates, and store the result in the SkIRect.
-*/
-static inline void XRect_round(const SkXRect& xr, SkIRect* dst) {
-    dst->fLeft = SkFixedRound(xr.fLeft);
-    dst->fTop = SkFixedRound(xr.fTop);
-    dst->fRight = SkFixedRound(xr.fRight);
-    dst->fBottom = SkFixedRound(xr.fBottom);
-}
-
-/** Round the SkXRect coordinates out (i.e. use floor for left/top, and ceiling
-    for right/bottom), and store the result in the SkIRect.
-*/
-static inline void XRect_roundOut(const SkXRect& xr, SkIRect* dst) {
-    dst->fLeft = SkFixedFloor(xr.fLeft);
-    dst->fTop = SkFixedFloor(xr.fTop);
-    dst->fRight = SkFixedCeil(xr.fRight);
-    dst->fBottom = SkFixedCeil(xr.fBottom);
-}
-
-#endif
diff --git a/include/core/SkSerializationHelpers.h b/include/core/SkSerializationHelpers.h
new file mode 100644
index 0000000..8bb2a41
--- /dev/null
+++ b/include/core/SkSerializationHelpers.h
@@ -0,0 +1,35 @@
+
+/*
+ * 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 SkSerializationHelpers_DEFINED
+#define SkSerializationHelpers_DEFINED
+
+class SkBitmap;
+class SkStream;
+class SkWStream;
+
+namespace SkSerializationHelpers {
+    /**
+     *  Function to encode an SkBitmap to an SkWStream. A function with this signature can be passed
+     *  to SkPicture::serialize() and SkOrderedWriteBuffer. The function should return true if it
+     *  succeeds. Otherwise it should return false so that SkOrderedWriteBuffer can switch to
+     *  another method of storing SkBitmaps.
+     */
+    typedef bool (*EncodeBitmap)(SkWStream*, const SkBitmap&);
+
+    /**
+     *  Function to decode an SkBitmap from an SkStream. A function with this signature can be
+     *  passed to the SkStream constructor for SkPicture and SkOrderedReadBuffer to decode SkBitmaps
+     *  which were previously encoded. The function should return true if it succeeds. Otherwise it
+     *  should return false so that SkOrderedReadBuffer can skip the data and provide a dummy
+     *  SkBitmap.
+     */
+    typedef bool (*DecodeBitmap)(SkStream*, SkBitmap*);
+}
+
+#endif // SkSerializationHelpers_DEFINED
diff --git a/include/core/SkShader.h b/include/core/SkShader.h
index 7c5be06..126602d 100644
--- a/include/core/SkShader.h
+++ b/include/core/SkShader.h
@@ -17,41 +17,65 @@
 #include "SkPaint.h"
 
 class SkPath;
+class GrContext;
+class GrEffectRef;
 
 /** \class SkShader
  *
- *  SkShader is the based class for objects that return horizontal spans of
- *  colors during drawing. A subclass of SkShader is installed in a SkPaint
- *  calling paint.setShader(shader). After that any object (other than a bitmap)
- *  that is drawn with that paint will get its color(s) from the shader.
+ *  Shaders specify the source color(s) for what is being drawn. If a paint
+ *  has no shader, then the paint's color is used. If the paint has a
+ *  shader, then the shader's color(s) are use instead, but they are
+ *  modulated by the paint's alpha. This makes it easy to create a shader
+ *  once (e.g. bitmap tiling or gradient) and then change its transparency
+ *  w/o having to modify the original shader... only the paint's alpha needs
+ *  to be modified.
  */
 class SK_API SkShader : public SkFlattenable {
 public:
-            SkShader();
+    SK_DECLARE_INST_COUNT(SkShader)
+
+    SkShader();
     virtual ~SkShader();
 
     /**
-     *  Return true if the shader has a non-identity local matrix.
-     *  @param localM   Optional: If not null, return the shader's local matrix
-     *  @return true if the shader has a non-identity local matrix.
+     * Returns true if the local matrix is not an identity matrix.
      */
-    bool getLocalMatrix(SkMatrix* localM) const;
+    bool hasLocalMatrix() const { return !fLocalMatrix.isIdentity(); }
+
+    /**
+     *  Returns the local matrix.
+     */
+    const SkMatrix& getLocalMatrix() const { return fLocalMatrix; }
 
     /**
      *  Set the shader's local matrix.
      *  @param localM   The shader's new local matrix.
      */
-    void setLocalMatrix(const SkMatrix& localM);
+    void setLocalMatrix(const SkMatrix& localM) { fLocalMatrix = localM; }
 
     /**
      *  Reset the shader's local matrix to identity.
      */
-    void resetLocalMatrix();
+    void resetLocalMatrix() { fLocalMatrix.reset(); }
 
     enum TileMode {
-        kClamp_TileMode,    //!< replicate the edge color if the shader draws outside of its original bounds
-        kRepeat_TileMode,   //!< repeat the shader's image horizontally and vertically
-        kMirror_TileMode,   //!< repeat the shader's image horizontally and vertically, alternating mirror images so that adjacent images always seam
+        /** replicate the edge color if the shader draws outside of its
+         *  original bounds
+         */
+        kClamp_TileMode,
+
+        /** repeat the shader's image horizontally and vertically */
+        kRepeat_TileMode,
+
+        /** repeat the shader's image horizontally and vertically, alternating
+         *  mirror images so that adjacent images always seam
+         */
+        kMirror_TileMode,
+
+#if 0
+        /** only draw within the original domain, return 0 everywhere else */
+        kDecal_TileMode,
+#endif
 
         kTileModeCount
     };
@@ -114,18 +138,38 @@
     /**
      *  Called once before drawing, with the current paint and device matrix.
      *  Return true if your shader supports these parameters, or false if not.
-     *  If false is returned, nothing will be drawn.
+     *  If false is returned, nothing will be drawn. If true is returned, then
+     *  a balancing call to endContext() will be made before the next call to
+     *  setContext.
+     *
+     *  Subclasses should be sure to call their INHERITED::setContext() if they
+     *  override this method.
      */
     virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
                             const SkMatrix& matrix);
 
     /**
+     *  Assuming setContext returned true, endContext() will be called when
+     *  the draw using the shader has completed. It is an error for setContext
+     *  to be called twice w/o an intervening call to endContext().
+     *
+     *  Subclasses should be sure to call their INHERITED::endContext() if they
+     *  override this method.
+     */
+    virtual void endContext();
+
+    SkDEBUGCODE(bool setContextHasBeenCalled() const { return SkToBool(fInSetContext); })
+
+    /**
      *  Called for each span of the object being drawn. Your subclass should
      *  set the appropriate colors (with premultiplied alpha) that correspond
      *  to the specified device coordinates.
      */
     virtual void shadeSpan(int x, int y, SkPMColor[], int count) = 0;
 
+    typedef void (*ShadeProc)(void* ctx, int x, int y, SkPMColor[], int count);
+    virtual ShadeProc asAShadeProc(void** ctx);
+
     /**
      *  Called only for 16bit devices when getFlags() returns
      *  kOpaqueAlphaFlag | kHasSpan16_Flag
@@ -155,14 +199,6 @@
     }
 
     /**
-     *  Called before a session using the shader begins. Some shaders override
-     *  this to defer some of their work (like calling bitmap.lockPixels()).
-     *  Must be balanced by a call to endSession.
-     */
-    virtual void beginSession();
-    virtual void endSession();
-
-    /**
      Gives method bitmap should be read to implement a shader.
      Also determines number and interpretation of "extra" parameters returned
      by asABitmap
@@ -193,8 +229,20 @@
                             //         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
+                            //   circle will be centered (x, 0) where x  may be
+                            //   0.
+                            //   Three extra parameters are returned:
+                            //      0: x-offset of second circle center
+                            //         to first.
+                            //      1: radius of first circle
+                            //      2: the second radius minus the first radius
+        kLinear_BitmapType, //<! Access bitmap using local coords transformed
+                            //   by matrix. No extras
 
-       kLast_BitmapType = kTwoPointRadial_BitmapType
+       kLast_BitmapType = kLinear_BitmapType
     };
     /** Optional methods for shaders that can pretend to be a bitmap/texture
         to play along with opengl. Default just returns kNone_BitmapType and
@@ -212,7 +260,7 @@
                                     about the first point.
     */
     virtual BitmapType asABitmap(SkBitmap* outTexture, SkMatrix* outMatrix,
-                         TileMode xy[2], SkScalar* twoPointRadialParams) const;
+                         TileMode xy[2]) const;
 
     /**
      *  If the shader subclass can be represented as a gradient, asAGradient
@@ -250,7 +298,8 @@
         kRadial_GradientType,
         kRadial2_GradientType,
         kSweep_GradientType,
-        kLast_GradientType = kSweep_GradientType
+        kConical_GradientType,
+        kLast_GradientType = kConical_GradientType
     };
 
     struct GradientInfo {
@@ -267,19 +316,32 @@
 
     virtual GradientType asAGradient(GradientInfo* info) const;
 
+    /**
+     *  If the shader subclass has a GrEffect implementation, this installs an effect on the stage.
+     *  The GrContext may be used by the effect to create textures. The GPU device does not call
+     *  setContext. Instead we pass the paint here in case the shader needs paint info.
+     */
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint& paint) const;
+
     //////////////////////////////////////////////////////////////////////////
     //  Factory methods for stock shaders
 
     /** Call this to create a new shader that will draw with the specified bitmap.
-        @param src  The bitmap to use inside the shader
-        @param tmx  The tiling mode to use when sampling the bitmap in the x-direction.
-        @param tmy  The tiling mode to use when sampling the bitmap in the y-direction.
-        @return     Returns a new shader object. Note: this function never returns null.
+     *
+     *  If the bitmap cannot be used (e.g. has no pixels, or its dimensions
+     *  exceed implementation limits (currently at 64K - 1)) then SkEmptyShader
+     *  may be returned.
+     *
+     *  @param src  The bitmap to use inside the shader
+     *  @param tmx  The tiling mode to use when sampling the bitmap in the x-direction.
+     *  @param tmy  The tiling mode to use when sampling the bitmap in the y-direction.
+     *  @return     Returns a new shader object. Note: this function never returns null.
     */
     static SkShader* CreateBitmapShader(const SkBitmap& src,
                                         TileMode tmx, TileMode tmy);
 
-    virtual void flatten(SkFlattenableWriteBuffer& ) SK_OVERRIDE;
+    SkDEVCODE(virtual void toString(SkString* str) const;)
+
 protected:
     enum MatrixClass {
         kLinear_MatrixClass,            // no perspective
@@ -295,13 +357,14 @@
     MatrixClass         getInverseClass() const { return (MatrixClass)fTotalInverseClass; }
 
     SkShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 private:
-    SkMatrix*           fLocalMatrix;
+    SkMatrix            fLocalMatrix;
     SkMatrix            fTotalInverse;
     uint8_t             fPaintAlpha;
     uint8_t             fDeviceConfig;
     uint8_t             fTotalInverseClass;
-    SkDEBUGCODE(SkBool8 fInSession;)
+    SkDEBUGCODE(SkBool8 fInSetContext;)
 
     static SkShader* CreateBitmapShader(const SkBitmap& src,
                                         TileMode, TileMode,
@@ -311,4 +374,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkShape.h b/include/core/SkShape.h
deleted file mode 100644
index c7cf9ec..0000000
--- a/include/core/SkShape.h
+++ /dev/null
@@ -1,53 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkShape_DEFINED
-#define SkShape_DEFINED
-
-#include "SkFlattenable.h"
-
-class SkCanvas;
-class SkMatrix;
-class SkWStream;
-
-class SkShape : public SkFlattenable {
-public:
-    SkShape();
-    virtual ~SkShape();
-
-    void draw(SkCanvas*);
-
-    /** Draw the shape translated by (dx,dy), which is applied before the
-        shape's matrix (if any).
-     */
-    void drawXY(SkCanvas*, SkScalar dx, SkScalar dy);
-
-    /** Draw the shape with the specified matrix, applied before the shape's
-        matrix (if any).
-     */
-    void drawMatrix(SkCanvas*, const SkMatrix&);
-
-    // overrides
-    virtual Factory getFactory();
-    virtual void flatten(SkFlattenableWriteBuffer&);
-
-    // public for Registrar
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
-
-protected:
-    virtual void onDraw(SkCanvas*);
-
-    SkShape(SkFlattenableReadBuffer&);
-
-private:
-
-    typedef SkFlattenable INHERITED;
-};
-
-#endif
diff --git a/include/core/SkSize.h b/include/core/SkSize.h
index 12dbeae..01c6e35 100644
--- a/include/core/SkSize.h
+++ b/include/core/SkSize.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,10 +5,11 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkSize_DEFINED
 #define SkSize_DEFINED
 
+#include "SkScalar.h"
+
 template <typename T> struct SkTSize {
     T fWidth;
     T fHeight;
@@ -36,15 +36,15 @@
     bool isEmpty() const {
         return fWidth <= 0 || fHeight <= 0;
     }
-    
+
     /** Set the width and height to 0 */
     void setEmpty() {
         fWidth = fHeight = 0;
     }
-	
-	T width() const { return fWidth; }
-	T height() const { return fHeight; }
-    
+
+    T width() const { return fWidth; }
+    T height() const { return fHeight; }
+
     /** If width or height is < 0, it is set to 0 */
     void clampNegToZero() {
         if (fWidth < 0) {
@@ -54,7 +54,7 @@
             fHeight = 0;
         }
     }
-    
+
     bool equals(T w, T h) const {
         return fWidth == w && fHeight == h;
     }
@@ -74,8 +74,6 @@
 
 typedef SkTSize<int32_t> SkISize;
 
-#include "SkScalar.h"
-
 struct SkSize : public SkTSize<SkScalar> {
     static SkSize Make(SkScalar w, SkScalar h) {
         SkSize s;
@@ -83,8 +81,8 @@
         s.fHeight = h;
         return s;
     }
-    
-    
+
+
     SkSize& operator=(const SkISize& src) {
         this->set(SkIntToScalar(src.fWidth), SkIntToScalar(src.fHeight));
         return *this;
diff --git a/include/core/SkStream.h b/include/core/SkStream.h
index 67512d7..7ec3117 100644
--- a/include/core/SkStream.h
+++ b/include/core/SkStream.h
@@ -17,7 +17,8 @@
 
 class SK_API SkStream : public SkRefCnt {
 public:
-    virtual ~SkStream();
+    SK_DECLARE_INST_COUNT(SkStream)
+
     /** Called to rewind to the beginning of the stream. If this cannot be
         done, return false.
     */
@@ -39,7 +40,7 @@
     /** Return the total length of the stream.
     */
     size_t getLength() { return this->read(NULL, 0); }
-    
+
     /** Skip the specified number of bytes, returning the actual number
         of bytes that could be skipped.
     */
@@ -63,10 +64,21 @@
     bool     readBool() { return this->readU8() != 0; }
     SkScalar readScalar();
     size_t   readPackedUInt();
+
+    /**
+     *  Create a new SkData from the stream contents. This balances the call
+     *  SkWStream::writeData().
+     */
+    SkData* readData();
+
+private:
+    typedef SkRefCnt INHERITED;
 };
 
 class SK_API SkWStream : SkNoncopyable {
 public:
+    SK_DECLARE_INST_COUNT_ROOT(SkWStream)
+
     virtual ~SkWStream();
 
     /** Called to write bytes to a SkWStream. Returns true on success
@@ -79,7 +91,7 @@
     virtual void flush();
 
     // helpers
-    
+
     bool    write8(U8CPU);
     bool    write16(U16CPU);
     bool    write32(uint32_t);
@@ -89,11 +101,11 @@
     bool    writeBigDecAsText(int64_t, int minDigits = 0);
     bool    writeHexAsText(uint32_t, int minDigits = 0);
     bool    writeScalarAsText(SkScalar);
-    
+
     bool    writeBool(bool v) { return this->write8(v); }
     bool    writeScalar(SkScalar);
     bool    writePackedUInt(size_t);
-    
+
     bool writeStream(SkStream* input, size_t length);
 
     bool writeData(const SkData*);
@@ -110,6 +122,8 @@
  */
 class SkFILEStream : public SkStream {
 public:
+    SK_DECLARE_INST_COUNT(SkFILEStream)
+
     /** Initialize the stream by calling fopen on the specified path. Will be
         closed in the destructor.
      */
@@ -131,34 +145,42 @@
 private:
     SkFILE*     fFILE;
     SkString    fName;
+
+    typedef SkStream INHERITED;
 };
 
 /** A stream that reads from a file descriptor
  */
 class SkFDStream : public SkStream {
 public:
+    SK_DECLARE_INST_COUNT(SkFDStream)
+
     /** Initialize the stream with a dup() of the specified file descriptor.
         If closeWhenDone is true, then the descriptor will be closed in the
         destructor.
      */
     SkFDStream(int fileDesc, bool closeWhenDone);
     virtual ~SkFDStream();
-    
+
     /** Returns true if the current path could be opened.
      */
     bool isValid() const { return fFD >= 0; }
-    
+
     virtual bool rewind() SK_OVERRIDE;
     virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
     virtual const char* getFileName() SK_OVERRIDE { return NULL; }
-    
+
 private:
     int     fFD;
     bool    fCloseWhenDone;
+
+    typedef SkStream INHERITED;
 };
 
 class SkMemoryStream : public SkStream {
 public:
+    SK_DECLARE_INST_COUNT(SkMemoryStream)
+
     SkMemoryStream();
     /** We allocate (and free) the memory. Write to it via getMemoryBase()
     */
@@ -166,6 +188,13 @@
     /** if copyData is true, the stream makes a private copy of the data
     */
     SkMemoryStream(const void* data, size_t length, bool copyData = false);
+
+    /**
+     *  Use the specified data as the memory for this stream. The stream will
+     *  call ref() on the data (assuming it is not null).
+     */
+    SkMemoryStream(SkData*);
+
     virtual ~SkMemoryStream();
 
     /** Resets the stream to the specified data and length,
@@ -200,10 +229,12 @@
     const void* getAtPos();
     size_t seek(size_t offset);
     size_t peek() const { return fOffset; }
-    
+
 private:
     SkData* fData;
     size_t  fOffset;
+
+    typedef SkStream INHERITED;
 };
 
 /** \class SkBufferStream
@@ -213,6 +244,8 @@
 */
 class SkBufferStream : public SkStream {
 public:
+    SK_DECLARE_INST_COUNT(SkBufferStream)
+
     /** Provide the stream to be buffered (proxy), and the size of the buffer that
         should be used. This will be allocated and freed automatically. If bufferSize is 0,
         a default buffer size will be used.
@@ -249,13 +282,17 @@
     bool        fWeOwnTheBuffer;
 
     void    init(void*, size_t);
+
+    typedef SkStream INHERITED;
 };
 
 /////////////////////////////////////////////////////////////////////////////////////////////
 
-class SkFILEWStream : public SkWStream {
+class SK_API SkFILEWStream : public SkWStream {
 public:
-            SkFILEWStream(const char path[]);
+    SK_DECLARE_INST_COUNT(SkFILEWStream)
+
+    SkFILEWStream(const char path[]);
     virtual ~SkFILEWStream();
 
     /** Returns true if the current path could be opened.
@@ -264,12 +301,17 @@
 
     virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
     virtual void flush() SK_OVERRIDE;
+
 private:
     SkFILE* fFILE;
+
+    typedef SkWStream INHERITED;
 };
 
 class SkMemoryWStream : public SkWStream {
 public:
+    SK_DECLARE_INST_COUNT(SkMemoryWStream)
+
     SkMemoryWStream(void* buffer, size_t size);
     virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
     size_t bytesWritten() const { return fBytesWritten; }
@@ -278,10 +320,14 @@
     char*   fBuffer;
     size_t  fMaxLength;
     size_t  fBytesWritten;
+
+    typedef SkWStream INHERITED;
 };
 
 class SK_API SkDynamicMemoryWStream : public SkWStream {
 public:
+    SK_DECLARE_INST_COUNT(SkDynamicMemoryWStream)
+
     SkDynamicMemoryWStream();
     virtual ~SkDynamicMemoryWStream();
 
@@ -313,14 +359,21 @@
     mutable SkData* fCopy;  // is invalidated if we write after it is created
 
     void invalidateCopy();
+
+    typedef SkWStream INHERITED;
 };
 
 
 class SkDebugWStream : public SkWStream {
 public:
+    SK_DECLARE_INST_COUNT(SkDebugWStream)
+
     // overrides
     virtual bool write(const void* buffer, size_t size) SK_OVERRIDE;
     virtual void newline() SK_OVERRIDE;
+
+private:
+    typedef SkWStream INHERITED;
 };
 
 // for now
diff --git a/include/core/SkString.h b/include/core/SkString.h
index 3fa367b..0049402 100644
--- a/include/core/SkString.h
+++ b/include/core/SkString.h
@@ -15,10 +15,48 @@
 /*  Some helper functions for C strings
 */
 
-bool SkStrStartsWith(const char string[], const char prefix[]);
-bool SkStrEndsWith(const char string[], const char suffix[]);
+static bool SkStrStartsWith(const char string[], const char prefixStr[]) {
+    SkASSERT(string);
+    SkASSERT(prefixStr);
+    return !strncmp(string, prefixStr, strlen(prefixStr));
+}
+static bool SkStrStartsWith(const char string[], const char prefixChar) {
+    SkASSERT(string);
+    return (prefixChar == *string);
+}
+
+bool SkStrEndsWith(const char string[], const char suffixStr[]);
+bool SkStrEndsWith(const char string[], const char suffixChar);
+
 int SkStrStartsWithOneOf(const char string[], const char prefixes[]);
 
+static int SkStrFind(const char string[], const char substring[]) {
+    const char *first = strstr(string, substring);
+    if (NULL == first) return -1;
+    return first - &(string[0]);
+}
+
+static bool SkStrContains(const char string[], const char substring[]) {
+    SkASSERT(string);
+    SkASSERT(substring);
+    return (-1 != SkStrFind(string, substring));
+}
+static bool SkStrContains(const char string[], const char subchar) {
+    SkASSERT(string);
+    char tmp[2];
+    tmp[0] = subchar;
+    tmp[1] = '\0';
+    return (-1 != SkStrFind(string, tmp));
+}
+
+static inline char *SkStrDup(const char string[]) {
+    char *ret = (char *) sk_malloc_throw(strlen(string)+1);
+    memcpy(ret,string,strlen(string));
+    return ret;
+}
+
+
+
 #define SkStrAppendS32_MaxSize  11
 char*   SkStrAppendS32(char buffer[], int32_t);
 #define SkStrAppendS64_MaxSize  20
@@ -46,9 +84,7 @@
     #define SkStrAppendScalar SkStrAppendFixed
 #endif
 
-#ifdef SK_CAN_USE_FLOAT
 char* SkStrAppendFloat(char buffer[], float);
-#endif
 char* SkStrAppendFixed(char buffer[], SkFixed);
 
 /** \class SkString
@@ -57,7 +93,7 @@
     counting to make string assignments and copies very fast
     with no extra RAM cost. Assumes UTF8 encoding.
 */
-class SkString {
+class SK_API SkString {
 public:
                 SkString();
     explicit    SkString(size_t len);
@@ -75,11 +111,26 @@
     bool equals(const char text[]) const;
     bool equals(const char text[], size_t len) const;
 
-    bool startsWith(const char prefix[]) const {
-        return SkStrStartsWith(fRec->data(), prefix);
+    bool startsWith(const char prefixStr[]) const {
+        return SkStrStartsWith(fRec->data(), prefixStr);
     }
-    bool endsWith(const char suffix[]) const {
-        return SkStrEndsWith(fRec->data(), suffix);
+    bool startsWith(const char prefixChar) const {
+        return SkStrStartsWith(fRec->data(), prefixChar);
+    }
+    bool endsWith(const char suffixStr[]) const {
+        return SkStrEndsWith(fRec->data(), suffixStr);
+    }
+    bool endsWith(const char suffixChar) const {
+        return SkStrEndsWith(fRec->data(), suffixChar);
+    }
+    bool contains(const char substring[]) const {
+        return SkStrContains(fRec->data(), substring);
+    }
+    bool contains(const char subchar) const {
+        return SkStrContains(fRec->data(), subchar);
+    }
+    int find(const char substring[]) const {
+        return SkStrFind(fRec->data(), substring);
     }
 
     friend bool operator==(const SkString& a, const SkString& b) {
@@ -132,9 +183,9 @@
     void prependHex(uint32_t value, int minDigits = 0) { this->insertHex(0, value, minDigits); }
     void prependScalar(SkScalar value) { this->insertScalar((size_t)-1, value); }
 
-    void printf(const char format[], ...);
-    void appendf(const char format[], ...);
-    void prependf(const char format[], ...);
+    void printf(const char format[], ...) SK_PRINTF_LIKE(2, 3);
+    void appendf(const char format[], ...) SK_PRINTF_LIKE(2, 3);
+    void prependf(const char format[], ...) SK_PRINTF_LIKE(2, 3);
 
     void remove(size_t offset, size_t length);
 
diff --git a/include/core/SkStringUtils.h b/include/core/SkStringUtils.h
new file mode 100644
index 0000000..aa5c809
--- /dev/null
+++ b/include/core/SkStringUtils.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkStringUtils_DEFINED
+#define SkStringUtils_DEFINED
+
+class SkString;
+
+/**
+ * Add 'flagStr' to 'string' and set 'needSeparator' to true only if 'flag' is
+ * true. If 'needSeparator' is true append a '|' before 'flagStr'. This method
+ * is used to streamline the creation of ASCII flag strings within the toString
+ * methods.
+ */
+void SkAddFlagToString(SkString* string, bool flag,
+                       const char* flagStr, bool* needSeparator);
+
+
+#endif
diff --git a/include/core/SkStroke.h b/include/core/SkStroke.h
deleted file mode 100644
index e5d69c4..0000000
--- a/include/core/SkStroke.h
+++ /dev/null
@@ -1,60 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkStroke_DEFINED
-#define SkStroke_DEFINED
-
-#include "SkPoint.h"
-#include "SkPaint.h"
-
-struct SkRect;
-class SkPath;
-
-/** \class SkStroke
-    SkStroke is the utility class that constructs paths by stroking
-    geometries (lines, rects, ovals, roundrects, paths). This is
-    invoked when a geometry or text is drawn in a canvas with the
-    kStroke_Mask bit set in the paint.
-*/
-class SkStroke {
-public:
-    SkStroke();
-    SkStroke(const SkPaint&);
-    SkStroke(const SkPaint&, SkScalar width);   // width overrides paint.getStrokeWidth()
-
-    SkPaint::Cap    getCap() const { return (SkPaint::Cap)fCap; }
-    void        setCap(SkPaint::Cap);
-
-    SkPaint::Join   getJoin() const { return (SkPaint::Join)fJoin; }
-    void        setJoin(SkPaint::Join);
-
-    void    setMiterLimit(SkScalar);
-    void    setWidth(SkScalar);
-
-    bool    getDoFill() const { return SkToBool(fDoFill); }
-    void    setDoFill(bool doFill) { fDoFill = SkToU8(doFill); }
-
-    void    strokeLine(const SkPoint& start, const SkPoint& end, SkPath*) const;
-    void    strokeRect(const SkRect& rect, SkPath*) const;
-    void    strokeOval(const SkRect& oval, SkPath*) const;
-    void    strokeRRect(const SkRect& rect, SkScalar rx, SkScalar ry, SkPath*) const;
-    void    strokePath(const SkPath& path, SkPath*) const;
-
-    ////////////////////////////////////////////////////////////////
-
-private:
-    SkScalar    fWidth, fMiterLimit;
-    uint8_t     fCap, fJoin;
-    SkBool8     fDoFill;
-
-    friend class SkPaint;
-};
-
-#endif
-
diff --git a/include/core/SkStrokeRec.h b/include/core/SkStrokeRec.h
new file mode 100644
index 0000000..c5b47c2
--- /dev/null
+++ b/include/core/SkStrokeRec.h
@@ -0,0 +1,92 @@
+/*
+ * 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 SkStrokeRec_DEFINED
+#define SkStrokeRec_DEFINED
+
+#include "SkPaint.h"
+
+class SkPath;
+
+class SkStrokeRec {
+public:
+    enum InitStyle {
+        kHairline_InitStyle,
+        kFill_InitStyle
+    };
+    SkStrokeRec(InitStyle style);
+
+    SkStrokeRec(const SkStrokeRec&);
+    explicit SkStrokeRec(const SkPaint&);
+
+    enum Style {
+        kHairline_Style,
+        kFill_Style,
+        kStroke_Style,
+        kStrokeAndFill_Style
+    };
+
+    Style getStyle() const;
+    SkScalar getWidth() const { return fWidth; }
+    SkScalar getMiter() const { return fMiterLimit; }
+    SkPaint::Cap getCap() const { return fCap; }
+    SkPaint::Join getJoin() const { return fJoin; }
+
+    bool isHairlineStyle() const {
+        return kHairline_Style == this->getStyle();
+    }
+
+    bool isFillStyle() const {
+        return kFill_Style == this->getStyle();
+    }
+
+    void setFillStyle();
+    void setHairlineStyle();
+    /**
+     *  Specify the strokewidth, and optionally if you want stroke + fill.
+     *  Note, if width==0, then this request is taken to mean:
+     *      strokeAndFill==true -> new style will be Fill
+     *      strokeAndFill==false -> new style will be Hairline
+     */
+    void setStrokeStyle(SkScalar width, bool strokeAndFill = false);
+
+    void setStrokeParams(SkPaint::Cap cap, SkPaint::Join join, SkScalar miterLimit) {
+        fCap = cap;
+        fJoin = join;
+        fMiterLimit = miterLimit;
+    }
+
+    /**
+     *  Returns true if this specifes any thick stroking, i.e. applyToPath()
+     *  will return true.
+     */
+    bool needToApply() const {
+        Style style = this->getStyle();
+        return (kStroke_Style == style) || (kStrokeAndFill_Style == style);
+    }
+
+    /**
+     *  Apply these stroke parameters to the src path, returning the result
+     *  in dst.
+     *
+     *  If there was no change (i.e. style == hairline or fill) this returns
+     *  false and dst is unchanged. Otherwise returns true and the result is
+     *  stored in dst.
+     *
+     *  src and dst may be the same path.
+     */
+    bool applyToPath(SkPath* dst, const SkPath& src) const;
+
+private:
+    SkScalar        fWidth;
+    SkScalar        fMiterLimit;
+    SkPaint::Cap    fCap;
+    SkPaint::Join   fJoin;
+    bool            fStrokeAndFill;
+};
+
+#endif
diff --git a/include/core/SkSurface.h b/include/core/SkSurface.h
new file mode 100644
index 0000000..7001299
--- /dev/null
+++ b/include/core/SkSurface.h
@@ -0,0 +1,142 @@
+/*
+ * 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 SkSurface_DEFINED
+#define SkSurface_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkImage.h"
+
+class SkCanvas;
+class SkPaint;
+class GrContext;
+class GrRenderTarget;
+
+/**
+ *  SkSurface represents the backend/results of drawing to a canvas. For raster
+ *  drawing, the surface will be pixels, but (for example) when drawing into
+ *  a PDF or Picture canvas, the surface stores the recorded commands.
+ *
+ *  To draw into a canvas, first create the appropriate type of Surface, and
+ *  then request the canvas from the surface.
+ */
+class SkSurface : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkSurface)
+
+    /**
+     *  Create a new surface, using the specified pixels/rowbytes as its
+     *  backend.
+     *
+     *  If the requested surface cannot be created, or the request is not a
+     *  supported configuration, NULL will be returned.
+     */
+    static SkSurface* NewRasterDirect(const SkImage::Info&, void* pixels, size_t rowBytes);
+
+    /**
+     *  Return a new surface, with the memory for the pixels automatically
+     *  allocated.
+     *
+     *  If the requested surface cannot be created, or the request is not a
+     *  supported configuration, NULL will be returned.
+     */
+    static SkSurface* NewRaster(const SkImage::Info&);
+
+    /**
+     *  Return a new surface whose contents will be recorded into a picture.
+     *  When this surface is drawn into another canvas, its contents will be
+     *  "replayed" into that canvas.
+     */
+    static SkSurface* NewPicture(int width, int height);
+
+    /**
+     *  Return a new surface using the specified render target.
+     */
+    static SkSurface* NewRenderTargetDirect(GrContext*, GrRenderTarget*);
+
+    /**
+     *  Return a new surface whose contents will be drawn to an offscreen
+     *  render target, allocated by the surface.
+     */
+    static SkSurface* NewRenderTarget(GrContext*, const SkImage::Info&, int sampleCount = 0);
+
+    int width() const { return fWidth; }
+    int height() const { return fHeight; }
+
+    /**
+     *  Returns a unique non-zero, unique value identifying the content of this
+     *  surface. Each time the content is changed changed, either by drawing
+     *  into this surface, or explicitly calling notifyContentChanged()) this
+     *  method will return a new value.
+     *
+     *  If this surface is empty (i.e. has a zero-dimention), this will return
+     *  0.
+     */
+    uint32_t generationID();
+
+    /**
+     *  Call this if the contents have changed. This will (lazily) force a new
+     *  value to be returned from generationID() when it is called next.
+     */
+    void notifyContentChanged();
+
+    /**
+     *  Return a canvas that will draw into this surface. This will always
+     *  return the same canvas for a given surface, and is manged/owned by the
+     *  surface. It should not be used when its parent surface has gone out of
+     *  scope.
+     */
+    SkCanvas* getCanvas();
+
+    /**
+     *  Return a new surface that is "compatible" with this one, in that it will
+     *  efficiently be able to be drawn into this surface. Typical calling
+     *  pattern:
+     *
+     *  SkSurface* A = SkSurface::New...();
+     *  SkCanvas* canvasA = surfaceA->newCanvas();
+     *  ...
+     *  SkSurface* surfaceB = surfaceA->newSurface(...);
+     *  SkCanvas* canvasB = surfaceB->newCanvas();
+     *  ... // draw using canvasB
+     *  canvasA->drawSurface(surfaceB); // <--- this will always be optimal!
+     */
+    SkSurface* newSurface(const SkImage::Info&);
+
+    /**
+     *  Returns an image of the current state of the surface pixels up to this
+     *  point. Subsequent changes to the surface (by drawing into its canvas)
+     *  will not be reflected in this image.
+     */
+    SkImage* newImageShapshot();
+
+    /**
+     *  Thought the caller could get a snapshot image explicitly, and draw that,
+     *  it seems that directly drawing a surface into another canvas might be
+     *  a common pattern, and that we could possibly be more efficient, since
+     *  we'd know that the "snapshot" need only live until we've handed it off
+     *  to the canvas.
+     */
+    void draw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
+
+protected:
+    SkSurface(int width, int height);
+
+    // called by subclass if their contents have changed
+    void dirtyGenerationID() {
+        fGenerationID = 0;
+    }
+
+private:
+    const int   fWidth;
+    const int   fHeight;
+    uint32_t    fGenerationID;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkTArray.h b/include/core/SkTArray.h
index 577e860..b264482 100644
--- a/include/core/SkTArray.h
+++ b/include/core/SkTArray.h
@@ -60,13 +60,13 @@
     }
 
     /**
-     * Creates an empty array that will preallocate space for reserveCount 
+     * Creates an empty array that will preallocate space for reserveCount
      * elements.
      */
     explicit SkTArray(int reserveCount) {
         this->init(NULL, 0, NULL, reserveCount);
     }
-  
+
     /**
      * Copies one array to another. The new array will be heap allocated.
      */
@@ -75,7 +75,7 @@
     }
 
     /**
-     * Creates a SkTArray by copying contents of a standard C array. The new 
+     * Creates a SkTArray by copying contents of a standard C array. The new
      * array will be heap allocated. Be careful not to use this constructor
      * when you really want the (void*, int) version.
      */
@@ -331,13 +331,11 @@
         int newCount = fCount + delta;
         int newAllocCount = fAllocCount;
 
-        if (newCount > fAllocCount) {
-            newAllocCount = SkMax32(newCount + ((newCount + 1) >> 1),
-                                   fReserveCount);
-        } else if (newCount < fAllocCount / 3) {
-            newAllocCount = SkMax32(fAllocCount / 2, fReserveCount);
+        if (newCount > fAllocCount || newCount < (fAllocCount / 3)) {
+            // whether we're growing or shrinking, we leave at least 50% extra space for future
+            // growth (clamped to the reserve count).
+            newAllocCount = SkMax32(newCount + ((newCount + 1) >> 1), fReserveCount);
         }
-
         if (newAllocCount != fAllocCount) {
 
             fAllocCount = newAllocCount;
@@ -412,4 +410,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTDArray.h b/include/core/SkTDArray.h
index 5f62203..ee77889 100644
--- a/include/core/SkTDArray.h
+++ b/include/core/SkTDArray.h
@@ -95,7 +95,7 @@
     /**
      *  Return the number of elements in the array
      */
-    int count() const { return fCount; }
+    int count() const { return (int)fCount; }
 
     /**
      *  return the number of bytes in the array: count * sizeof(T)
@@ -109,6 +109,10 @@
         return fArray[index];
     }
 
+    T&  getAt(int index) const {
+        return (*this)[index];
+    }
+
     void reset() {
         if (fArray) {
             sk_free(fArray);
@@ -121,7 +125,7 @@
             SkASSERT(fReserve == 0 && fCount == 0);
         }
     }
-    
+
     void rewind() {
         // same as setCount(0)
         fCount = 0;
@@ -154,7 +158,7 @@
         return this->append(1, NULL);
     }
     T* append(size_t count, const T* src = NULL) {
-        unsigned oldCount = fCount;
+        size_t oldCount = fCount;
         if (count)  {
             SkASSERT(src == NULL || fArray == NULL ||
                     src + count <= fArray || fArray + oldCount <= src);
@@ -166,9 +170,9 @@
         }
         return fArray + oldCount;
     }
-    
+
     T* appendClear() {
-        T* result = this->append(); 
+        T* result = this->append();
         *result = 0;
         return result;
     }
@@ -179,7 +183,7 @@
     T* insert(size_t index, size_t count, const T* src = NULL) {
         SkASSERT(count);
         SkASSERT(index <= fCount);
-        int oldCount = fCount;
+        size_t oldCount = fCount;
         this->growBy(count);
         T* dst = fArray + index;
         memmove(dst + count, dst, sizeof(T) * (oldCount - index));
@@ -197,7 +201,7 @@
 
     void removeShuffle(size_t index) {
         SkASSERT(index < fCount);
-        unsigned newCount = fCount - 1;
+        size_t newCount = fCount - 1;
         fCount = newCount;
         if (index != newCount) {
             memcpy(fArray + index, fArray + newCount, sizeof(T));
@@ -228,6 +232,32 @@
         return -1;
     }
 
+    /**
+     * Returns true iff the array contains this element.
+     */
+    bool contains(const T& elem) const {
+        return (this->find(elem) >= 0);
+    }
+
+    /**
+     * Copies up to max elements into dst. The number of items copied is
+     * capped by count - index. The actual number copied is returned.
+     */
+    int copyRange(T* dst, size_t index, int max) const {
+        SkASSERT(max >= 0);
+        SkASSERT(!max || dst);
+        if (index >= fCount) {
+            return 0;
+        }
+        int count = SkMin32(max, fCount - index);
+        memcpy(dst, fArray + index, sizeof(T) * count);
+        return count;
+    }
+
+    void copy(T* dst) const {
+        this->copyRange(0, fCount, dst);
+    }
+
     // routines to treat the array like a stack
     T*          push() { return this->append(); }
     void        push(const T& elem) { *this->append() = elem; }
@@ -240,7 +270,7 @@
         T*  iter = fArray;
         T*  stop = fArray + fCount;
         while (iter < stop) {
-            delete (*iter);
+            SkDELETE (*iter);
             iter += 1;
         }
         this->reset();
@@ -276,6 +306,15 @@
         this->reset();
     }
 
+    void visitAll(void visitor(T&)) const {
+        T* stop = this->end();
+        for (T* curr = this->begin(); curr < stop; curr++) {
+            if (*curr) {
+                visitor(*curr);
+            }
+        }
+    }
+
 #ifdef SK_DEBUG
     void validate() const {
         SkASSERT((fReserve == 0 && fArray == NULL) ||
@@ -314,4 +353,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTDStack.h b/include/core/SkTDStack.h
index be34e01..e286e4a 100644
--- a/include/core/SkTDStack.h
+++ b/include/core/SkTDStack.h
@@ -108,4 +108,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTDict.h b/include/core/SkTDict.h
index 3620899..b704a48 100644
--- a/include/core/SkTDict.h
+++ b/include/core/SkTDict.h
@@ -159,4 +159,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTInternalLList.h b/include/core/SkTInternalLList.h
new file mode 100644
index 0000000..a6b6f15
--- /dev/null
+++ b/include/core/SkTInternalLList.h
@@ -0,0 +1,272 @@
+/*
+ * 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 SkTInternalLList_DEFINED
+#define SkTInternalLList_DEFINED
+
+#include "SkTypes.h"
+
+/**
+ * Helper class to automatically initialize the doubly linked list created pointers.
+ */
+template <typename T> class SkPtrWrapper {
+  public:
+      SkPtrWrapper() : fPtr(NULL) {}
+      SkPtrWrapper& operator =(T* ptr) { fPtr = ptr; return *this; }
+      operator T*() const { return fPtr; }
+      T* operator->() { return fPtr; }
+  private:
+      T* fPtr;
+};
+
+
+/**
+ * This macro creates the member variables required by the SkTInternalLList class. It should be
+ * placed in the private section of any class that will be stored in a double linked list.
+ */
+#define SK_DECLARE_INTERNAL_LLIST_INTERFACE(ClassName)              \
+    friend class SkTInternalLList<ClassName>;                       \
+    /* back pointer to the owning list - for debugging */           \
+    SkDEBUGCODE(SkPtrWrapper<SkTInternalLList<ClassName> > fList;)  \
+    SkPtrWrapper<ClassName> fPrev;                                  \
+    SkPtrWrapper<ClassName> fNext
+
+/**
+ * This class implements a templated internal doubly linked list data structure.
+ */
+template <class T> class SkTInternalLList : public SkNoncopyable {
+public:
+    SkTInternalLList()
+        : fHead(NULL)
+        , fTail(NULL) {
+    }
+
+    void remove(T* entry) {
+        SkASSERT(NULL != fHead && NULL != fTail);
+        SkASSERT(this->isInList(entry));
+
+        T* prev = entry->fPrev;
+        T* next = entry->fNext;
+
+        if (NULL != prev) {
+            prev->fNext = next;
+        } else {
+            fHead = next;
+        }
+        if (NULL != next) {
+            next->fPrev = prev;
+        } else {
+            fTail = prev;
+        }
+
+        entry->fPrev = NULL;
+        entry->fNext = NULL;
+
+#ifdef SK_DEBUG
+        entry->fList = NULL;
+#endif
+    }
+
+    void addToHead(T* entry) {
+        SkASSERT(NULL == entry->fPrev && NULL == entry->fNext);
+        SkASSERT(NULL == entry->fList);
+
+        entry->fPrev = NULL;
+        entry->fNext = fHead;
+        if (NULL != fHead) {
+            fHead->fPrev = entry;
+        }
+        fHead = entry;
+        if (NULL == fTail) {
+            fTail = entry;
+        }
+
+#ifdef SK_DEBUG
+        entry->fList = this;
+#endif
+    }
+
+    void addToTail(T* entry) {
+        SkASSERT(NULL == entry->fPrev && NULL == entry->fNext);
+        SkASSERT(NULL == entry->fList);
+
+        entry->fPrev = fTail;
+        entry->fNext = NULL;
+        if (NULL != fTail) {
+            fTail->fNext = entry;
+        }
+        fTail = entry;
+        if (NULL == fHead) {
+            fHead = entry;
+        }
+
+#ifdef SK_DEBUG
+        entry->fList = this;
+#endif
+    }
+
+    /**
+     * Inserts a new list entry before an existing list entry. The new entry must not already be
+     * a member of this or any other list. If existingEntry is NULL then the new entry is added
+     * at the tail.
+     */
+    void addBefore(T* newEntry, T* existingEntry) {
+        SkASSERT(NULL != newEntry);
+
+        if (NULL == existingEntry) {
+            this->addToTail(newEntry);
+            return;
+        }
+
+        SkASSERT(this->isInList(existingEntry));
+        newEntry->fNext = existingEntry;
+        T* prev = existingEntry->fPrev;
+        existingEntry->fPrev = newEntry;
+        newEntry->fPrev = prev;
+        if (NULL == prev) {
+            SkASSERT(fHead == existingEntry);
+            fHead = newEntry;
+        } else {
+            prev->fNext = newEntry;
+        }
+#ifdef SK_DEBUG
+        newEntry->fList = this;
+#endif
+    }
+
+    /**
+     * Inserts a new list entry after an existing list entry. The new entry must not already be
+     * a member of this or any other list. If existingEntry is NULL then the new entry is added
+     * at the head.
+     */
+    void addAfter(T* newEntry, T* existingEntry) {
+        SkASSERT(NULL != newEntry);
+
+        if (NULL == existingEntry) {
+            this->addToHead(newEntry);
+            return;
+        }
+
+        SkASSERT(this->isInList(existingEntry));
+        newEntry->fPrev = existingEntry;
+        T* next = existingEntry->fNext;
+        existingEntry->fNext = newEntry;
+        newEntry->fNext = next;
+        if (NULL == next) {
+            SkASSERT(fTail == existingEntry);
+            fTail = newEntry;
+        } else {
+            next->fPrev = newEntry;
+        }
+#ifdef SK_DEBUG
+        newEntry->fList = this;
+#endif
+    }
+
+    bool isEmpty() const {
+        return NULL == fHead && NULL == fTail;
+    }
+
+    T* head() { return fHead; }
+    T* tail() { return fTail; }
+
+    class Iter {
+    public:
+        enum IterStart {
+            kHead_IterStart,
+            kTail_IterStart
+        };
+
+        Iter() : fCurr(NULL) {}
+        Iter(const Iter& iter) : fCurr(iter.fCurr) {}
+        Iter& operator= (const Iter& iter) { fCurr = iter.fCurr; return *this; }
+
+        T* init(const SkTInternalLList& list, IterStart startLoc) {
+            if (kHead_IterStart == startLoc) {
+                fCurr = list.fHead;
+            } else {
+                SkASSERT(kTail_IterStart == startLoc);
+                fCurr = list.fTail;
+            }
+
+            return fCurr;
+        }
+
+        T* get() { return fCurr; }
+
+        /**
+         * Return the next/previous element in the list or NULL if at the end.
+         */
+        T* next() {
+            if (NULL == fCurr) {
+                return NULL;
+            }
+
+            fCurr = fCurr->fNext;
+            return fCurr;
+        }
+
+        T* prev() {
+            if (NULL == fCurr) {
+                return NULL;
+            }
+
+            fCurr = fCurr->fPrev;
+            return fCurr;
+        }
+
+    private:
+        T* fCurr;
+    };
+
+#ifdef SK_DEBUG
+    void validate() const {
+        SkASSERT(!fHead == !fTail);
+        Iter iter;
+        for (T* item = iter.init(*this, Iter::kHead_IterStart); NULL != (item = iter.next()); ) {
+            SkASSERT(this->isInList(item));
+            if (NULL == item->fPrev) {
+                SkASSERT(fHead == item);
+            } else {
+                SkASSERT(item->fPrev->fNext == item);
+            }
+            if (NULL == item->fNext) {
+                SkASSERT(fTail == item);
+            } else {
+                SkASSERT(item->fNext->fPrev == item);
+            }
+        }
+    }
+
+    /**
+     * Debugging-only method that uses the list back pointer to check if 'entry' is indeed in 'this'
+     * list.
+     */
+    bool isInList(const T* entry) const {
+        return entry->fList == this;
+    }
+
+    /**
+     * Debugging-only method that laboriously counts the list entries.
+     */
+    int countEntries() const {
+        int count = 0;
+        for (T* entry = fHead; NULL != entry; entry = entry->fNext) {
+            ++count;
+        }
+        return count;
+    }
+#endif // SK_DEBUG
+
+private:
+    T* fHead;
+    T* fTail;
+
+    typedef SkNoncopyable INHERITED;
+};
+
+#endif
diff --git a/include/core/SkTLazy.h b/include/core/SkTLazy.h
index 9cfaccb..b9052f0 100644
--- a/include/core/SkTLazy.h
+++ b/include/core/SkTLazy.h
@@ -76,17 +76,81 @@
      *  false otherwise.
      */
     bool isValid() const { return NULL != fPtr; }
-    
+
     /**
      *  Returns either NULL, or a copy of the object that was passed to
      *  set() or the constructor.
      */
     T* get() const { SkASSERT(this->isValid()); return fPtr; }
-    
+
 private:
     T*   fPtr; // NULL or fStorage
     char fStorage[sizeof(T)];
 };
 
-#endif
+/**
+ * A helper built on top of SkTLazy to do copy-on-first-write. The object is initialized
+ * with a const pointer but provides a non-const pointer accessor. The first time the
+ * accessor is called (if ever) the object is cloned.
+ *
+ * In the following example at most one copy of constThing is made:
+ *
+ * SkTCopyOnFirstWrite<Thing> thing(&constThing);
+ * ...
+ * function_that_takes_a_const_thing_ptr(thing); // constThing is passed
+ * ...
+ * if (need_to_modify_thing()) {
+ *    thing.writable()->modifyMe(); // makes a copy of constThing
+ * }
+ * ...
+ * x = thing->readSomething();
+ * ...
+ * if (need_to_modify_thing_now()) {
+ *    thing.writable()->changeMe(); // makes a copy of constThing if we didn't call modifyMe()
+ * }
+ *
+ * consume_a_thing(thing); // could be constThing or a modified copy.
+ */
+template <typename T>
+class SkTCopyOnFirstWrite {
+public:
+    SkTCopyOnFirstWrite(const T& initial) : fObj(&initial) {}
 
+    // Constructor for delayed initialization.
+    SkTCopyOnFirstWrite() : fObj(NULL) {}
+
+    // Should only be called once, and only if the default constructor was used.
+    void init(const T& initial) {
+        SkASSERT(NULL == fObj);
+        SkASSERT(!fLazy.isValid());
+        fObj = &initial;
+    }
+
+    /**
+     * Returns a writable T*. The first time this is called the initial object is cloned.
+     */
+    T* writable() {
+        SkASSERT(NULL != fObj);
+        if (!fLazy.isValid()) {
+            fLazy.set(*fObj);
+            fObj = fLazy.get();
+        }
+        return const_cast<T*>(fObj);
+    }
+
+    /**
+     * Operators for treating this as though it were a const pointer.
+     */
+
+    const T *operator->() const { return fObj; }
+
+    operator const T*() const { return fObj; }
+
+    const T& operator *() const { return *fObj; }
+
+private:
+    const T*    fObj;
+    SkTLazy<T>  fLazy;
+};
+
+#endif
diff --git a/include/core/SkTSearch.h b/include/core/SkTSearch.h
index d5ab18c..eaf5ee5 100644
--- a/include/core/SkTSearch.h
+++ b/include/core/SkTSearch.h
@@ -12,6 +12,24 @@
 
 #include "SkTypes.h"
 
+/**
+ *  All of the SkTSearch variants want to return the index (0...N-1) of the
+ *  found element, or the bit-not of where to insert the element.
+ *
+ *  At a simple level, if the return value is negative, it was not found.
+ *
+ *  For clients that want to insert the new element if it was not found, use
+ *  the following logic:
+ *
+ *  int index = SkTSearch(...);
+ *  if (index >= 0) {
+ *      // found at index
+ *  } else {
+ *      index = ~index; // now we are positive
+ *      // insert at index
+ *  }
+ */
+
 template <typename T>
 int SkTSearch(const T* base, int count, const T& target, size_t elemSize)
 {
@@ -45,9 +63,8 @@
     return hi;
 }
 
-template <typename T>
-int SkTSearch(const T* base, int count, const T& target, size_t elemSize,
-              int (*compare)(const T&, const T&))
+template <typename T, int (COMPARE)(const T*, const T*)>
+int SkTSearch(const T* base, int count, const T& target, size_t elemSize)
 {
     SkASSERT(count >= 0);
     if (count <= 0) {
@@ -63,14 +80,48 @@
         int mid = (hi + lo) >> 1;
         const T* elem = (const T*)((const char*)base + mid * elemSize);
 
-        if ((*compare)(*elem, target) < 0)
+        if (COMPARE(elem, &target) < 0)
             lo = mid + 1;
         else
             hi = mid;
     }
 
     const T* elem = (const T*)((const char*)base + hi * elemSize);
-    int pred = (*compare)(*elem, target);
+    int pred = COMPARE(elem, &target);
+    if (pred != 0) {
+        if (pred < 0)
+            hi += 1;
+        hi = ~hi;
+    }
+    return hi;
+}
+
+template <typename T>
+int SkTSearch(const T* base, int count, const T& target, size_t elemSize,
+              int (*compare)(const T*, const T*))
+{
+    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;
+        const T* elem = (const T*)((const char*)base + mid * elemSize);
+
+        if ((*compare)(elem, &target) < 0)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+
+    const T* elem = (const T*)((const char*)base + hi * elemSize);
+    int pred = (*compare)(elem, &target);
     if (pred != 0) {
         if (pred < 0)
             hi += 1;
@@ -114,6 +165,39 @@
     return hi;
 }
 
+template <typename T,  int (COMPARE)(const T*, const T*)>
+int SkTSearch(const T** base, int count, const T* target, size_t elemSize)
+{
+    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;
+        const T* elem = *(const T**)((const char*)base + mid * elemSize);
+
+        if (COMPARE(elem, target) < 0)
+            lo = mid + 1;
+        else
+            hi = mid;
+    }
+
+    const T* elem = *(const T**)((const char*)base + hi * elemSize);
+    int pred = COMPARE(elem, target);
+    if (pred != 0)
+    {
+        if (pred < 0)
+            hi += 1;
+        hi = ~hi;
+    }
+    return hi;
+}
 int SkStrSearch(const char*const* base, int count, const char target[],
                 size_t target_len, size_t elemSize);
 int SkStrSearch(const char*const* base, int count, const char target[],
@@ -136,7 +220,7 @@
 public:
     SkAutoAsciiToLC(const char str[], size_t len = (size_t)-1);
     ~SkAutoAsciiToLC();
-    
+
     const char* lc() const { return fLC; }
     size_t      length() const { return fLength; }
 
@@ -149,10 +233,7 @@
     char    fStorage[STORAGE+1];
 };
 
-extern "C" {
-    typedef int (*SkQSortCompareProc)(const void*, const void*);
-    void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc);
-}
+// Helper when calling qsort with a compare proc that has typed its arguments
+#define SkCastForQSort(compare) reinterpret_cast<int (*)(const void*, const void*)>(compare)
 
 #endif
-
diff --git a/include/core/SkTemplates.h b/include/core/SkTemplates.h
index 03f0892..42e9943 100644
--- a/include/core/SkTemplates.h
+++ b/include/core/SkTemplates.h
@@ -11,6 +11,7 @@
 #define SkTemplates_DEFINED
 
 #include "SkTypes.h"
+#include <new>
 
 /** \file SkTemplates.h
 
@@ -18,6 +19,27 @@
     resource management.
 */
 
+/**
+ *  SkTIsConst<T>::value is true if the type T is const.
+ *  The type T is constrained not to be an array or reference type.
+ */
+template <typename T> struct SkTIsConst {
+    static T* t;
+    static uint16_t test(const volatile void*);
+    static uint32_t test(volatile void *);
+    static const bool value = (sizeof(uint16_t) == sizeof(test(t)));
+};
+
+///@{
+/** SkTConstType<T, CONST>::type will be 'const T' if CONST is true, 'T' otherwise. */
+template <typename T, bool CONST> struct SkTConstType {
+    typedef T type;
+};
+template <typename T> struct SkTConstType<T, true> {
+    typedef const T type;
+};
+///@}
+
 /** \class SkAutoTCallVProc
 
     Call a function when this goes out of scope. The template uses two
@@ -55,27 +77,43 @@
 // See also SkTScopedPtr.
 template <typename T> class SkAutoTDelete : SkNoncopyable {
 public:
-    SkAutoTDelete(T* obj, bool deleteWhenDone = true) : fObj(obj) {
-        fDeleteWhenDone = deleteWhenDone;
-    }
-    ~SkAutoTDelete() { if (fDeleteWhenDone) delete fObj; }
+    SkAutoTDelete(T* obj) : fObj(obj) {}
+    ~SkAutoTDelete() { delete fObj; }
 
-    T*      get() const { return fObj; }
-    void    free() { delete fObj; fObj = NULL; }
-    T*      detach() { T* obj = fObj; fObj = NULL; return obj; }
+    T* get() const { return fObj; }
+    T& operator*() const { SkASSERT(fObj); return *fObj; }
+    T* operator->() const { SkASSERT(fObj); return fObj; }
+
+    /**
+     *  Delete the owned object, setting the internal pointer to NULL.
+     */
+    void free() {
+        delete fObj;
+        fObj = NULL;
+    }
+
+    /**
+     *  Transfer ownership of the object to the caller, setting the internal
+     *  pointer to NULL. Note that this differs from get(), which also returns
+     *  the pointer, but it does not transfer ownership.
+     */
+    T* detach() {
+        T* obj = fObj;
+        fObj = NULL;
+        return obj;
+    }
 
 private:
     T*  fObj;
-    bool fDeleteWhenDone;
 };
 
 template <typename T> class SkAutoTDeleteArray : SkNoncopyable {
 public:
     SkAutoTDeleteArray(T array[]) : fArray(array) {}
-    ~SkAutoTDeleteArray() { delete[] fArray; }
+    ~SkAutoTDeleteArray() { SkDELETE_ARRAY(fArray); }
 
     T*      get() const { return fArray; }
-    void    free() { delete[] fArray; fArray = NULL; }
+    void    free() { SkDELETE_ARRAY(fArray); fArray = NULL; }
     T*      detach() { T* array = fArray; fArray = NULL; return array; }
 
 private:
@@ -86,9 +124,26 @@
  */
 template <typename T> class SkAutoTArray : SkNoncopyable {
 public:
+    SkAutoTArray() {
+        fArray = NULL;
+        SkDEBUGCODE(fCount = 0;)
+    }
     /** Allocate count number of T elements
      */
-    SkAutoTArray(size_t count) {
+    explicit SkAutoTArray(int count) {
+        SkASSERT(count >= 0);
+        fArray = NULL;
+        if (count) {
+            fArray = new T[count];
+        }
+        SkDEBUGCODE(fCount = count;)
+    }
+
+    /** Reallocates given a new count. Reallocation occurs even if new count equals old count.
+     */
+    void reset(int count) {
+        delete[] fArray;
+        SkASSERT(count >= 0);
         fArray = NULL;
         if (count) {
             fArray = new T[count];
@@ -103,17 +158,17 @@
     /** Return the array of T elements. Will be NULL if count == 0
      */
     T* get() const { return fArray; }
-    
+
     /** Return the nth element in the array
      */
     T&  operator[](int index) const {
-        SkASSERT((unsigned)index < fCount);
+        SkASSERT((unsigned)index < (unsigned)fCount);
         return fArray[index];
     }
 
 private:
     T*  fArray;
-    SkDEBUGCODE(size_t fCount;)
+    SkDEBUGCODE(int fCount;)
 };
 
 /** Wraps SkAutoTArray, with room for up to N elements preallocated
@@ -132,7 +187,7 @@
         }
         fCount = count;
     }
-    
+
     ~SkAutoSTArray() {
         if (fCount > N) {
             delete[] fArray;
@@ -144,22 +199,22 @@
             }
         }
     }
-    
+
     /** Return the number of T elements in the array
      */
     size_t count() const { return fCount; }
-    
+
     /** Return the array of T elements. Will be NULL if count == 0
      */
     T* get() const { return fArray; }
-    
+
     /** Return the nth element in the array
      */
     T&  operator[](int index) const {
         SkASSERT((unsigned)index < fCount);
         return fArray[index];
     }
-    
+
 private:
     size_t  fCount;
     T*      fArray;
@@ -295,4 +350,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkThread.h b/include/core/SkThread.h
index 1495a16..4a2499a 100644
--- a/include/core/SkThread.h
+++ b/include/core/SkThread.h
@@ -16,7 +16,9 @@
 /****** SkThread_platform needs to define the following...
 
 int32_t sk_atomic_inc(int32_t*);
+int32_t sk_atomic_add(int32_t*, int32_t);
 int32_t sk_atomic_dec(int32_t*);
+int32_t sk_atomic_conditional_inc(int32_t*);
 
 class SkMutex {
 public:
@@ -31,29 +33,34 @@
 
 class SkAutoMutexAcquire : SkNoncopyable {
 public:
-    explicit SkAutoMutexAcquire(SkBaseMutex& mutex) : fMutex(&mutex)
-    {
+    explicit SkAutoMutexAcquire(SkBaseMutex& mutex) : fMutex(&mutex) {
         SkASSERT(fMutex != NULL);
         mutex.acquire();
     }
-    /** If the mutex has not been release, release it now.
-    */
-    ~SkAutoMutexAcquire()
-    {
-        if (fMutex)
-            fMutex->release();
+
+    SkAutoMutexAcquire(SkBaseMutex* mutex) : fMutex(mutex) {
+        if (mutex) {
+            mutex->acquire();
+        }
     }
+
     /** If the mutex has not been release, release it now.
     */
-    void release()
-    {
-        if (fMutex)
-        {
+    ~SkAutoMutexAcquire() {
+        if (fMutex) {
+            fMutex->release();
+        }
+    }
+
+    /** If the mutex has not been release, release it now.
+    */
+    void release() {
+        if (fMutex) {
             fMutex->release();
             fMutex = NULL;
         }
     }
-        
+
 private:
     SkBaseMutex* fMutex;
 };
diff --git a/include/core/SkThread_platform.h b/include/core/SkThread_platform.h
index 58311e1..298e9cb 100644
--- a/include/core/SkThread_platform.h
+++ b/include/core/SkThread_platform.h
@@ -19,13 +19,37 @@
 /* Just use the GCC atomic intrinsics. They're supported by the NDK toolchain,
  * have reasonable performance, and provide full memory barriers
  */
-static __attribute__((always_inline)) int32_t sk_atomic_inc(int32_t *addr) {
+static inline __attribute__((always_inline)) int32_t sk_atomic_inc(int32_t *addr) {
     return __sync_fetch_and_add(addr, 1);
 }
 
-static __attribute__((always_inline)) int32_t sk_atomic_dec(int32_t *addr) {
+static inline __attribute__((always_inline)) int32_t sk_atomic_add(int32_t *addr, int32_t inc) {
+    return __sync_fetch_and_add(addr, inc);
+}
+
+static inline __attribute__((always_inline)) int32_t sk_atomic_dec(int32_t *addr) {
     return __sync_fetch_and_add(addr, -1);
 }
+static inline __attribute__((always_inline)) void sk_membar_aquire__after_atomic_dec() { }
+
+static inline __attribute__((always_inline)) int32_t sk_atomic_conditional_inc(int32_t* addr) {
+    int32_t value = *addr;
+
+    while (true) {
+        if (value == 0) {
+            return 0;
+        }
+
+        int32_t before = __sync_val_compare_and_swap(addr, value, value + 1);
+
+        if (before == value) {
+            return value;
+        } else {
+            value = before;
+        }
+    }
+}
+static inline __attribute__((always_inline)) void sk_membar_aquire__after_atomic_conditional_inc() { }
 
 #else // !SK_BUILD_FOR_ANDROID_NDK
 
@@ -34,22 +58,77 @@
  */
 #include <utils/Atomic.h>
 
-#define sk_atomic_inc(addr)     android_atomic_inc(addr)
-#define sk_atomic_dec(addr)     android_atomic_dec(addr)
+#define sk_atomic_inc(addr)         android_atomic_inc(addr)
+#define sk_atomic_add(addr, inc)    android_atomic_add(inc, addr)
+#define sk_atomic_dec(addr)         android_atomic_dec(addr)
+
+static inline __attribute__((always_inline)) void sk_membar_aquire__after_atomic_dec() {
+    //HACK: Android is actually using full memory barriers.
+    //      Should this change, uncomment below.
+    //int dummy;
+    //android_atomic_aquire_store(0, &dummy);
+}
+static inline __attribute__((always_inline)) int32_t sk_atomic_conditional_inc(int32_t* addr) {
+    while (true) {
+        int32_t value = *addr;
+        if (value == 0) {
+            return 0;
+        }
+        if (0 == android_atomic_release_cas(value, value + 1, addr)) {
+            return value;
+        }
+    }
+}
+static inline __attribute__((always_inline)) void sk_membar_aquire__after_atomic_conditional_inc() {
+    //HACK: Android is actually using full memory barriers.
+    //      Should this change, uncomment below.
+    //int dummy;
+    //android_atomic_aquire_store(0, &dummy);
+}
 
 #endif // !SK_BUILD_FOR_ANDROID_NDK
 
 #else  // !SK_BUILD_FOR_ANDROID
 
-/** Implemented by the porting layer, this function adds 1 to the int specified
-    by the address (in a thread-safe manner), and returns the previous value.
+/** Implemented by the porting layer, this function adds one to the int
+    specified by the address (in a thread-safe manner), and returns the
+    previous value.
+    No additional memory barrier is required.
+    This must act as a compiler barrier.
 */
 SK_API int32_t sk_atomic_inc(int32_t* addr);
-/** Implemented by the porting layer, this function subtracts 1 to the int
-    specified by the address (in a thread-safe manner), and returns the previous
-    value.
+
+/** Implemented by the porting layer, this function adds inc to the int
+    specified by the address (in a thread-safe manner), and returns the
+    previous value.
+    No additional memory barrier is required.
+    This must act as a compiler barrier.
+ */
+SK_API int32_t sk_atomic_add(int32_t* addr, int32_t inc);
+
+/** Implemented by the porting layer, this function subtracts one from the int
+    specified by the address (in a thread-safe manner), and returns the
+    previous value.
+    Expected to act as a release (SL/S) memory barrier and a compiler barrier.
 */
 SK_API int32_t sk_atomic_dec(int32_t* addr);
+/** If sk_atomic_dec does not act as an aquire (L/SL) barrier, this is expected
+    to act as an aquire (L/SL) memory barrier and as a compiler barrier.
+*/
+SK_API void sk_membar_aquire__after_atomic_dec();
+
+/** Implemented by the porting layer, this function adds one to the int
+    specified by the address iff the int specified by the address is not zero
+    (in a thread-safe manner), and returns the previous value.
+    No additional memory barrier is required.
+    This must act as a compiler barrier.
+*/
+SK_API int32_t sk_atomic_conditional_inc(int32_t*);
+/** If sk_atomic_conditional_inc does not act as an aquire (L/SL) barrier, this
+    is expected to act as an aquire (L/SL) memory barrier and as a compiler
+    barrier.
+*/
+SK_API void sk_membar_aquire__after_atomic_conditional_inc();
 
 #endif // !SK_BUILD_FOR_ANDROID
 
@@ -75,11 +154,11 @@
 // Special case used when the static mutex must be available globally.
 #define SK_DECLARE_GLOBAL_MUTEX(name)   SkBaseMutex  name = { PTHREAD_MUTEX_INITIALIZER }
 
-#define SK_DECLARE_MUTEX_ARRAY(name, count)    SkBaseMutex name[count] = { PTHREAD_MUTEX_INITIALIZER }
+#define SK_DECLARE_MUTEX_ARRAY(name, count)    SkBaseMutex name[count] = { { PTHREAD_MUTEX_INITIALIZER } }
 
 // A normal mutex that requires to be initialized through normal C++ construction,
 // i.e. when it's a member of another class, or allocated on the heap.
-class SkMutex : public SkBaseMutex, SkNoncopyable {
+class SK_API SkMutex : public SkBaseMutex, SkNoncopyable {
 public:
     SkMutex();
     ~SkMutex();
@@ -90,7 +169,7 @@
 // In the generic case, SkBaseMutex and SkMutex are the same thing, and we
 // can't easily get rid of static initializers.
 //
-class SkMutex : SkNoncopyable {
+class SK_API SkMutex : SkNoncopyable {
 public:
     SkMutex();
     ~SkMutex();
diff --git a/include/core/SkTileGridPicture.h b/include/core/SkTileGridPicture.h
new file mode 100644
index 0000000..6263ecb
--- /dev/null
+++ b/include/core/SkTileGridPicture.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkTileGridPicture_DEFINED
+#define SkTileGridPicture_DEFINED
+
+#include "SkPicture.h"
+
+/**
+ * Subclass of SkPicture that override the behavior of the
+ * kOptimizeForClippedPlayback_RecordingFlag by creating an SkTileGrid
+ * structure rather than an R-Tree. The tile grid has lower recording
+ * and playback costs, but is less effective at eliminating extraneous
+ * primitives for arbitrary query rectangles. It is most effective for
+ * tiled playback when the tile structure is known at record time.
+ */
+class SK_API SkTileGridPicture : public SkPicture {
+public:
+    SkTileGridPicture(int tileWidth, int tileHeight, int width, int height);
+    virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE;
+private:
+    int fTileWidth, fTileHeight, fXTileCount, fYTileCount;
+};
+
+#endif
diff --git a/include/core/SkTime.h b/include/core/SkTime.h
index ede1fd9..7f3c270 100644
--- a/include/core/SkTime.h
+++ b/include/core/SkTime.h
@@ -62,4 +62,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkTrace.h b/include/core/SkTrace.h
index 2d48799..e6e7e2c 100644
--- a/include/core/SkTrace.h
+++ b/include/core/SkTrace.h
@@ -43,5 +43,3 @@
 #endif
 
 #endif
-
-
diff --git a/include/core/SkTypeface.h b/include/core/SkTypeface.h
index 03ed11c..7fb6b81 100644
--- a/include/core/SkTypeface.h
+++ b/include/core/SkTypeface.h
@@ -11,13 +11,14 @@
 #define SkTypeface_DEFINED
 
 #include "SkAdvancedTypefaceMetrics.h"
-#include "SkRefCnt.h"
+#include "SkWeakRefCnt.h"
 
 class SkStream;
 class SkAdvancedTypefaceMetrics;
 class SkWStream;
 
 typedef uint32_t SkFontID;
+typedef uint32_t SkFontTableTag;
 
 /** \class SkTypeface
 
@@ -28,8 +29,10 @@
 
     Typeface objects are immutable, and so they can be shared between threads.
 */
-class SK_API SkTypeface : public SkRefCnt {
+class SK_API SkTypeface : public SkWeakRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkTypeface)
+
     /** Style specifies the intrinsic style attributes of a given typeface
     */
     enum Style {
@@ -84,18 +87,6 @@
     */
     static SkTypeface* CreateFromName(const char familyName[], Style style);
 
-    /** Return a new reference to the typeface that covers a set of Unicode
-        code points with the specified Style. Use this call if you want to
-        pick any font that covers a given string of text.
-
-        @param data        UTF-16 characters
-        @param bytelength  length of data, in bytes
-        @return reference to the closest-matching typeface. Call must call
-                unref() when they are done.
-    */
-    static SkTypeface* CreateForChars(const void* data, size_t bytelength,
-                                      Style s);
-
     /** Return a new reference to the typeface that most closely matches the
         requested typeface and specified Style. Use this call if you want to
         pick a new style from the same family of the existing typeface.
@@ -146,18 +137,67 @@
             const uint32_t* glyphIDs = NULL,
             uint32_t glyphIDsCount = 0) const;
 
+    // Table getters -- may fail if the underlying font format is not organized
+    // as 4-byte tables.
+
+    /** Return the number of tables in the font. */
+    int countTables() const;
+
+    /** Copy into tags[] (allocated by the caller) the list of table tags in
+     *  the font, and return the number. This will be the same as CountTables()
+     *  or 0 if an error occured. If tags == NULL, this only returns the count
+     *  (the same as calling countTables()).
+     */
+    int getTableTags(SkFontTableTag tags[]) const;
+
+    /** Given a table tag, return the size of its contents, or 0 if not present
+     */
+    size_t getTableSize(SkFontTableTag) const;
+
+    /** Copy the contents of a table into data (allocated by the caller). Note
+     *  that the contents of the table will be in their native endian order
+     *  (which for most truetype tables is big endian). If the table tag is
+     *  not found, or there is an error copying the data, then 0 is returned.
+     *  If this happens, it is possible that some or all of the memory pointed
+     *  to by data may have been written to, even though an error has occured.
+     *
+     *  @param fontID the font to copy the table from
+     *  @param tag  The table tag whose contents are to be copied
+     *  @param offset The offset in bytes into the table's contents where the
+     *  copy should start from.
+     *  @param length The number of bytes, starting at offset, of table data
+     *  to copy.
+     *  @param data storage address where the table contents are copied to
+     *  @return the number of bytes actually copied into data. If offset+length
+     *  exceeds the table's size, then only the bytes up to the table's
+     *  size are actually copied, and this is the value returned. If
+     *  offset > the table's size, or tag is not a valid table,
+     *  then 0 is returned.
+     */
+    size_t getTableData(SkFontTableTag tag, size_t offset, size_t length,
+                        void* data) const;
+
+    /**
+     *  Return the units-per-em value for this typeface, or zero if there is an
+     *  error.
+     */
+    int getUnitsPerEm() const;
+
 protected:
     /** uniqueID must be unique (please!) and non-zero
     */
     SkTypeface(Style style, SkFontID uniqueID, bool isFixedWidth = false);
     virtual ~SkTypeface();
 
+    friend class SkScalerContext;
+    static SkTypeface* GetDefaultTypeface();
+
 private:
     SkFontID    fUniqueID;
     Style       fStyle;
     bool        fIsFixedWidth;
 
-    typedef SkRefCnt INHERITED;
+    typedef SkWeakRefCnt INHERITED;
 };
 
 #endif
diff --git a/include/core/SkTypes.h b/include/core/SkTypes.h
index a584421..cbba50c 100644
--- a/include/core/SkTypes.h
+++ b/include/core/SkTypes.h
@@ -112,17 +112,40 @@
     #define SkAssertResult(cond)        cond
 #endif
 
-namespace {
+#ifdef SK_DEVELOPER
+    #define SkDEVCODE(code)             code
+    // the 'toString' helper functions convert Sk* objects to human-readable
+    // form in developer mode
+    #define SK_DEVELOPER_TO_STRING()    virtual void toString(SkString* str) const SK_OVERRIDE;
+#else
+    #define SkDEVCODE(code)
+    #define SK_DEVELOPER_TO_STRING()
+#endif
 
 template <bool>
 struct SkCompileAssert {
 };
 
-}  // namespace
-
 #define SK_COMPILE_ASSERT(expr, msg) \
     typedef SkCompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
 
+/*
+ *  Usage:  SK_MACRO_CONCAT(a, b)   to construct the symbol ab
+ *
+ *  SK_MACRO_CONCAT_IMPL_PRIV just exists to make this work. Do not use directly
+ *
+ */
+#define SK_MACRO_CONCAT(X, Y)           SK_MACRO_CONCAT_IMPL_PRIV(X, Y)
+#define SK_MACRO_CONCAT_IMPL_PRIV(X, Y)  X ## Y
+
+/*
+ *  Usage: SK_MACRO_APPEND_LINE(foo)    to make foo123, where 123 is the current
+ *                                      line number. Easy way to construct
+ *                                      unique names for local functions or
+ *                                      variables.
+ */
+#define SK_MACRO_APPEND_LINE(name)  SK_MACRO_CONCAT(name, __LINE__)
+
 ///////////////////////////////////////////////////////////////////////
 
 /**
@@ -185,10 +208,10 @@
 #define SK_MaxU16   0xFFFF
 #define SK_MinU16   0
 #define SK_MaxS32   0x7FFFFFFF
-#define SK_MinS32   0x80000001
+#define SK_MinS32   -SK_MaxS32
 #define SK_MaxU32   0xFFFFFFFF
 #define SK_MinU32   0
-#define SK_NaN32    0x80000000
+#define SK_NaN32    (1 << 31)
 
 /** Returns true if the value can be represented with signed 16bits
  */
@@ -204,21 +227,21 @@
 
 //////////////////////////////////////////////////////////////////////////////
 #ifndef SK_OFFSETOF
-    #define SK_OFFSETOF(type, field)    ((char*)&(((type*)1)->field) - (char*)1)
+    #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 x rounded up to a multiple of 2
-*/
 #define SkAlign2(x)     (((x) + 1) >> 1 << 1)
-/** Returns x rounded up to a multiple of 4
-*/
-#define SkAlign4(x)     (((x) + 3) >> 2 << 2)
+#define SkIsAlign2(x)   (0 == ((x) & 1))
 
-#define SkIsAlign4(x) (((x) & 3) == 0)
+#define SkAlign4(x)     (((x) + 3) >> 2 << 2)
+#define SkIsAlign4(x)   (0 == ((x) & 3))
+
+#define SkAlign8(x)     (((x) + 7) >> 3 << 3)
+#define SkIsAlign8(x)   (0 == ((x) & 7))
 
 typedef uint32_t SkFourByteTag;
 #define SkSetFourByteTag(a, b, c, d)    (((a) << 24) | ((b) << 16) | ((c) << 8) | (d))
@@ -270,6 +293,13 @@
 #endif
 }
 
+template <typename T> inline T SkTAbs(T value) {
+    if (value < 0) {
+        value = -value;
+    }
+    return value;
+}
+
 static inline int32_t SkMax32(int32_t a, int32_t b) {
     if (a < b)
         a = b;
@@ -432,13 +462,13 @@
          *  malloc a new block of the smaller size.
          */
         kAlloc_OnShrink,
-        
+
         /**
          *  If the requested size is smaller than the current size, and the
          *  current block is dynamically allocated, just return the old
          *  block.
          */
-        kReuse_OnShrink,
+        kReuse_OnShrink
     };
 
     /**
diff --git a/include/core/SkUnPreMultiply.h b/include/core/SkUnPreMultiply.h
index 7088918..4fa5d57 100644
--- a/include/core/SkUnPreMultiply.h
+++ b/include/core/SkUnPreMultiply.h
@@ -18,7 +18,7 @@
 class SK_API SkUnPreMultiply {
 public:
     typedef uint32_t Scale;
-    
+
     // index this table with alpha [0..255]
     static const Scale* GetScaleTable() {
         return gTable;
@@ -28,15 +28,15 @@
         SkASSERT(alpha <= 255);
         return gTable[alpha];
     }
-    
+
     /** Usage:
-     
+
         const Scale* table = SkUnPreMultiply::GetScaleTable();
-     
+
         for (...) {
             unsigned a = ...
             SkUnPreMultiply::Scale scale = table[a];
-     
+
             red = SkUnPreMultiply::ApplyScale(scale, red);
             ...
             // now red is unpremultiplied
@@ -46,9 +46,9 @@
         SkASSERT(component <= 255);
         return (scale * component + (1 << 23)) >> 24;
     }
-    
+
     static SkColor PMColorToColor(SkPMColor c);
-    
+
 private:
     static const uint32_t gTable[256];
 };
diff --git a/include/core/SkUnitMapper.h b/include/core/SkUnitMapper.h
index 2bd482b..754be26 100644
--- a/include/core/SkUnitMapper.h
+++ b/include/core/SkUnitMapper.h
@@ -17,15 +17,19 @@
 
 class SkUnitMapper : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkUnitMapper)
+
     SkUnitMapper() {}
 
     /** Given a value in [0..0xFFFF], return a value in the same range.
     */
     virtual uint16_t mapUnit16(uint16_t x) = 0;
-    
+
 protected:
     SkUnitMapper(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {}
+
+private:
+    typedef SkFlattenable INHERITED;
 };
 
 #endif
-
diff --git a/include/core/SkUserConfig.h b/include/core/SkUserConfig.h
index 91f8948..edda6b5 100644
--- a/include/core/SkUserConfig.h
+++ b/include/core/SkUserConfig.h
@@ -41,8 +41,7 @@
 // ANDROID Specific changes - NO NOT CHECK BACK INTO code.google.com/p/skia
 //
 
-#define PICTURE_VERSION_ICS 1 // r1562 of Skia
-#define PICTURE_VERSION_JB  2
+#define SK_BUILD_FOR_ANDROID
 
 // do this build check for other tools that still read this header
 #ifdef ANDROID
@@ -59,13 +58,6 @@
 #undef SK_SCALAR_IS_FIXED
 
 
-/*  Somewhat independent of how SkScalar is implemented, Skia also wants to know
-    if it can use floats at all. Naturally, if SK_SCALAR_IS_FLOAT is defined,
-    SK_CAN_USE_FLOAT must be too; but if scalars are fixed, SK_CAN_USE_FLOAT
-    can go either way.
- */
-#define SK_CAN_USE_FLOAT
-
 /*  For some performance-critical scalar operations, skia will optionally work
     around the standard float operators if it knows that the CPU does not have
     native support for floats. If your environment uses software floating point,
@@ -86,6 +78,21 @@
 //#define SK_DEBUG
 //#define SK_RELEASE
 
+/*  Skia has certain debug-only code that is extremely intensive even for debug
+    builds.  This code is useful for diagnosing specific issues, but is not
+    generally applicable, therefore it must be explicitly enabled to avoid
+    the performance impact. By default these flags are undefined, but can be
+    enabled by uncommenting them below.
+ */
+//#define SK_DEBUG_GLYPH_CACHE
+//#define SK_DEBUG_PATH
+
+/*  To assist debugging, Skia provides an instance counting utility in
+    include/core/SkInstCount.h. This flag turns on and off that utility to
+    allow instance count tracking in either debug or release builds. By
+    default it is enabled in debug but disabled in release.
+ */
+//#define SK_ENABLE_INST_COUNT 1
 
 /*  If, in debugging mode, Skia needs to stop (presumably to invoke a debugger)
     it will call SK_CRASH(). If this is not defined it, it is defined in
@@ -105,6 +112,15 @@
     #undef  SK_CPU_BENDIAN
 #endif
 
+/*  Most compilers use the same bit endianness for bit flags in a byte as the
+    system byte endianness, and this is the default. If for some reason this
+    needs to be overridden, specify which of the mutually exclusive flags to
+    use. For example, some atom processors in certain configurations have big
+    endian byte order but little endian bit orders.
+*/
+//#define SK_UINT8_BITFIELD_BENDIAN
+//#define SK_UINT8_BITFIELD_LENDIAN
+
 
 /*  Some compilers don't support long long for 64bit integers. If yours does
     not, define this to the appropriate type.
@@ -131,9 +147,11 @@
 
 /*  If zlib is available and you want to support the flate compression
     algorithm (used in PDF generation), define SK_ZLIB_INCLUDE to be the
-    include path.
+    include path. Alternatively, define SK_SYSTEM_ZLIB to use the system zlib
+    library specified as "#include <zlib.h>".
  */
 //#define SK_ZLIB_INCLUDE <zlib.h>
+//#define SK_SYSTEM_ZLIB
 
 /*  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.
@@ -149,6 +167,12 @@
  */
 //#define SK_ALLOW_OVER_32K_BITMAPS
 
+/**
+ *  To revert to int-only srcrect behavior in drawBitmapRect(ToRect),
+ *  define this symbol.
+ */
+//#define SK_SUPPORT_INT_SRCRECT_DRAWBITMAPRECT
+
 /*  Define this to set the upper limit for text to support LCD. Values that
     are very large increase the cost in the font cache and draw slower, without
     improving readability. If this is undefined, Skia will use its default
@@ -161,7 +185,7 @@
     so this flag is optional.
  */
 #ifdef SK_DEBUG
-    #define SK_SUPPORT_UNITTEST
+//#define SK_SUPPORT_UNITTEST
 #endif
 
 /* If your system embeds skia and has complex event logging, define this
@@ -185,4 +209,14 @@
         #define SK_A32_SHIFT    24
 #endif
 
+
+/* Determines whether to build code that supports the GPU backend. Some classes
+   that are not GPU-specific, such as SkShader subclasses, have optional code
+   that is used allows them to interact with the GPU backend. If you'd like to
+   omit this code set SK_SUPPORT_GPU to 0. This also allows you to omit the gpu
+   directories from your include search path when you're not building the GPU
+   backend. Defaults to 1 (build the GPU code).
+ */
+//#define SK_SUPPORT_GPU 1
+
 #endif
diff --git a/include/core/SkUtils.h b/include/core/SkUtils.h
index b700b96..c0ce51f 100644
--- a/include/core/SkUtils.h
+++ b/include/core/SkUtils.h
@@ -133,4 +133,3 @@
 };
 
 #endif
-
diff --git a/include/core/SkWeakRefCnt.h b/include/core/SkWeakRefCnt.h
new file mode 100644
index 0000000..e6871fe
--- /dev/null
+++ b/include/core/SkWeakRefCnt.h
@@ -0,0 +1,159 @@
+/*
+ * 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 SkWeakRefCnt_DEFINED
+#define SkWeakRefCnt_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkThread.h"
+
+/** \class SkWeakRefCnt
+
+    SkWeakRefCnt is the base class for objects that may be shared by multiple
+    objects. When an existing strong owner wants to share a reference, it calls
+    ref(). When a strong owner wants to release its reference, it calls
+    unref(). When the shared object's strong reference count goes to zero as
+    the result of an unref() call, its (virtual) weak_dispose method is called.
+    It is an error for the destructor to be called explicitly (or via the
+    object going out of scope on the stack or calling delete) if
+    getRefCnt() > 1.
+
+    In addition to strong ownership, an owner may instead obtain a weak
+    reference by calling weak_ref(). A call to weak_ref() must be balanced my a
+    call to weak_unref(). To obtain a strong reference from a weak reference,
+    call try_ref(). If try_ref() returns true, the owner's pointer is now also
+    a strong reference on which unref() must be called. Note that this does not
+    affect the original weak reference, weak_unref() must still be called. When
+    the weak reference count goes to zero, the object is deleted. While the
+    weak reference count is positive and the strong reference count is zero the
+    object still exists, but will be in the disposed state. It is up to the
+    object to define what this means.
+
+    Note that a strong reference implicitly implies a weak reference. As a
+    result, it is allowable for the owner of a strong ref to call try_ref().
+    This will have the same effect as calling ref(), but may be more expensive.
+
+    Example:
+
+    SkWeakRefCnt myRef = strongRef.weak_ref();
+    ... // strongRef.unref() may or may not be called
+    if (myRef.try_ref()) {
+        ... // use myRef
+        myRef.unref();
+    } else {
+        // myRef is in the disposed state
+    }
+    myRef.weak_unref();
+*/
+class SK_API SkWeakRefCnt : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkWeakRefCnt)
+
+    /** Default construct, initializing the reference counts to 1.
+        The strong references collectively hold one weak reference. When the
+        strong reference count goes to zero, the collectively held weak
+        reference is released.
+    */
+    SkWeakRefCnt() : SkRefCnt(), fWeakCnt(1) {}
+
+    /** Destruct, asserting that the weak reference count is 1.
+    */
+    virtual ~SkWeakRefCnt() {
+#ifdef SK_DEBUG
+        SkASSERT(fWeakCnt == 1);
+        fWeakCnt = 0;
+#endif
+    }
+
+    /** Return the weak reference count.
+    */
+    int32_t getWeakCnt() const { return fWeakCnt; }
+
+    void validate() const {
+        SkRefCnt::validate();
+        SkASSERT(fWeakCnt > 0);
+    }
+
+    /** Creates a strong reference from a weak reference, if possible. The
+        caller must already be an owner. If try_ref() returns true the owner
+        is in posession of an additional strong reference. Both the original
+        reference and new reference must be properly unreferenced. If try_ref()
+        returns false, no strong reference could be created and the owner's
+        reference is in the same state as before the call.
+    */
+    bool SK_WARN_UNUSED_RESULT try_ref() const {
+        if (sk_atomic_conditional_inc(&fRefCnt) != 0) {
+            // Aquire barrier (L/SL), if not provided above.
+            // Prevents subsequent code from happening before the increment.
+            sk_membar_aquire__after_atomic_conditional_inc();
+            return true;
+        }
+        return false;
+    }
+
+    /** Increment the weak reference count. Must be balanced by a call to
+        weak_unref().
+    */
+    void weak_ref() const {
+        SkASSERT(fRefCnt > 0);
+        SkASSERT(fWeakCnt > 0);
+        sk_atomic_inc(&fWeakCnt);  // No barrier required.
+    }
+
+    /** Decrement the weak reference count. If the weak reference count is 1
+        before the decrement, then call delete on the object. Note that if this
+        is the case, then the object needs to have been allocated via new, and
+        not on the stack.
+    */
+    void weak_unref() const {
+        SkASSERT(fWeakCnt > 0);
+        // Release barrier (SL/S), if not provided below.
+        if (sk_atomic_dec(&fWeakCnt) == 1) {
+            // Aquire barrier (L/SL), if not provided above.
+            // Prevents code in destructor from happening before the decrement.
+            sk_membar_aquire__after_atomic_dec();
+#ifdef SK_DEBUG
+            // so our destructor won't complain
+            fWeakCnt = 1;
+#endif
+            SkRefCnt::internal_dispose();
+        }
+    }
+
+    /** Returns true if there are no strong references to the object. When this
+        is the case all future calls to try_ref() will return false.
+    */
+    bool weak_expired() const {
+        return fRefCnt == 0;
+    }
+
+protected:
+    /** Called when the strong reference count goes to zero. This allows the
+        object to free any resources it may be holding. Weak references may
+        still exist and their level of allowed access to the object is defined
+        by the object's class.
+    */
+    virtual void weak_dispose() const {
+    }
+
+private:
+    /** Called when the strong reference count goes to zero. Calls weak_dispose
+        on the object and releases the implicit weak reference held
+        collectively by the strong references.
+    */
+    virtual void internal_dispose() const SK_OVERRIDE {
+        weak_dispose();
+        weak_unref();
+    }
+
+    /* Invariant: fWeakCnt = #weak + (fRefCnt > 0 ? 1 : 0) */
+    mutable int32_t fWeakCnt;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/core/SkWriter32.h b/include/core/SkWriter32.h
index d7159ff..51044ab 100644
--- a/include/core/SkWriter32.h
+++ b/include/core/SkWriter32.h
@@ -13,70 +13,124 @@
 #include "SkTypes.h"
 
 #include "SkScalar.h"
+#include "SkPath.h"
 #include "SkPoint.h"
 #include "SkRect.h"
+#include "SkRRect.h"
+#include "SkMatrix.h"
+#include "SkRegion.h"
 
 class SkStream;
 class SkWStream;
 
 class SkWriter32 : SkNoncopyable {
+    struct BlockHeader;
 public:
+    /**
+     *  The caller can specify an initial block of storage, which the caller manages.
+     *  SkWriter32 will not attempt to free this in its destructor. It is up to the
+     *  implementation to decide if, and how much, of the storage to utilize, and it
+     *  is possible that it may be ignored entirely.
+     */
+    SkWriter32(size_t minSize, void* initialStorage, size_t storageSize);
+
     SkWriter32(size_t minSize)
-        : fMinSize(minSize),
-          fSize(0),
-          fSingleBlock(NULL),
-          fSingleBlockSize(0),
-          fHead(NULL),
-          fTail(NULL) {
-    }
+        : fHead(NULL)
+        , fTail(NULL)
+        , fMinSize(minSize)
+        , fSize(0)
+        , fWrittenBeforeLastBlock(0)
+        {}
+
     ~SkWriter32();
 
-    /**
-     *  Returns the single block backing the writer, or NULL if the memory is
-     *  to be dynamically allocated.
-     */
-    void* getSingleBlock() const { return fSingleBlock; }
+    // return the current offset (will always be a multiple of 4)
+    uint32_t bytesWritten() const { return fSize; }
+    // DEPRECATED: use byetsWritten instead
+    uint32_t  size() const { return this->bytesWritten(); }
 
-    /**
-     *  Specify the single block to back the writer, rathern than dynamically
-     *  allocating the memory. If block == NULL, then the writer reverts to
-     *  dynamic allocation (and resets).
-     */
-    void reset(void* block, size_t size);
-                    
+    void      reset();
+
+    // size MUST be multiple of 4
+    uint32_t* reserve(size_t size) {
+        SkASSERT(SkAlign4(size) == size);
+
+        Block* block = fTail;
+        if (NULL == block || block->available() < size) {
+            block = this->doReserve(size);
+        }
+        fSize += size;
+        return block->alloc(size);
+    }
+
+    void reset(void* storage, size_t size);
+
     bool writeBool(bool value) {
         this->writeInt(value);
         return value;
     }
-    
+
     void writeInt(int32_t value) {
         *(int32_t*)this->reserve(sizeof(value)) = value;
     }
-    
+
     void write8(int32_t value) {
         *(int32_t*)this->reserve(sizeof(value)) = value & 0xFF;
     }
-    
+
     void write16(int32_t value) {
         *(int32_t*)this->reserve(sizeof(value)) = value & 0xFFFF;
     }
-    
+
     void write32(int32_t value) {
         *(int32_t*)this->reserve(sizeof(value)) = value;
     }
-    
+
+    void writePtr(void* ptr) {
+        // Since we "know" that we're always 4-byte aligned, we can tell the
+        // compiler that here, by assigning to an int32 ptr.
+        int32_t* addr = (int32_t*)this->reserve(sizeof(void*));
+        if (4 == sizeof(void*)) {
+            *(void**)addr = ptr;
+        } else {
+            memcpy(addr, &ptr, sizeof(void*));
+        }
+    }
+
     void writeScalar(SkScalar value) {
         *(SkScalar*)this->reserve(sizeof(value)) = value;
     }
-    
+
     void writePoint(const SkPoint& pt) {
         *(SkPoint*)this->reserve(sizeof(pt)) = pt;
     }
-    
+
     void writeRect(const SkRect& rect) {
         *(SkRect*)this->reserve(sizeof(rect)) = rect;
     }
-    
+
+    void writeRRect(const SkRRect& rrect) {
+        rrect.writeToMemory(this->reserve(SkRRect::kSizeInMemory));
+    }
+
+    void writePath(const SkPath& path) {
+        size_t size = path.writeToMemory(NULL);
+        SkASSERT(SkAlign4(size) == size);
+        path.writeToMemory(this->reserve(size));
+    }
+
+    void writeMatrix(const SkMatrix& matrix) {
+        size_t size = matrix.writeToMemory(NULL);
+        SkASSERT(SkAlign4(size) == size);
+        matrix.writeToMemory(this->reserve(size));
+    }
+
+    void writeRegion(const SkRegion& rgn) {
+        size_t size = rgn.writeToMemory(NULL);
+        SkASSERT(SkAlign4(size) == size);
+        rgn.writeToMemory(this->reserve(size));
+    }
+
     // write count bytes (must be a multiple of 4)
     void writeMul4(const void* values, size_t size) {
         this->write(values, size);
@@ -93,7 +147,16 @@
         // in the current block
         memcpy(this->reserve(size), values, size);
     }
-    
+
+    /**
+     *  Reserve size bytes. Does not need to be 4 byte aligned. The remaining space (if any) will be
+     *  filled in with zeroes.
+     */
+    uint32_t* reservePad(size_t size);
+
+    /**
+     *  Write size bytes from src, and pad to 4 byte alignment with zeroes.
+     */
     void writePad(const void* src, size_t size);
 
     /**
@@ -111,37 +174,121 @@
      */
     static size_t WriteStringSize(const char* str, size_t len = (size_t)-1);
 
-    // return the current offset (will always be a multiple of 4)
-    uint32_t  size() const { return fSize; }
-    void      reset();
-    uint32_t* reserve(size_t size); // size MUST be multiple of 4
-
     // return the address of the 4byte int at the specified offset (which must
     // be a multiple of 4. This does not allocate any new space, so the returned
     // address is only valid for 1 int.
     uint32_t* peek32(size_t offset);
-    
+
+    /**
+     *  Move the cursor back to offset bytes from the beginning.
+     *  This has the same restrictions as peek32: offset must be <= size() and
+     *  offset must be a multiple of 4.
+     */
+    void rewindToOffset(size_t offset);
+
     // copy into a single buffer (allocated by caller). Must be at least size()
     void flatten(void* dst) const;
-    
+
     // read from the stream, and write up to length bytes. Return the actual
     // number of bytes written.
     size_t readFromStream(SkStream*, size_t length);
-    
+
     bool writeToStream(SkWStream*);
 
 private:
+    struct Block {
+        Block*  fNext;
+        char*   fBasePtr;
+        size_t  fSizeOfBlock;      // total space allocated (after this)
+        size_t  fAllocatedSoFar;    // space used so far
+
+        size_t  available() const { return fSizeOfBlock - fAllocatedSoFar; }
+        char*   base() { return fBasePtr; }
+        const char* base() const { return fBasePtr; }
+
+        uint32_t* alloc(size_t size) {
+            SkASSERT(SkAlign4(size) == size);
+            SkASSERT(this->available() >= size);
+            void* ptr = this->base() + fAllocatedSoFar;
+            fAllocatedSoFar += size;
+            SkASSERT(fAllocatedSoFar <= fSizeOfBlock);
+            return (uint32_t*)ptr;
+        }
+
+        uint32_t* peek32(size_t offset) {
+            SkASSERT(offset <= fAllocatedSoFar + 4);
+            void* ptr = this->base() + offset;
+            return (uint32_t*)ptr;
+        }
+
+        void rewind() {
+            fNext = NULL;
+            fAllocatedSoFar = 0;
+            // keep fSizeOfBlock as is
+        }
+
+        static Block* Create(size_t size) {
+            SkASSERT(SkIsAlign4(size));
+            Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
+            block->fNext = NULL;
+            block->fBasePtr = (char*)(block + 1);
+            block->fSizeOfBlock = size;
+            block->fAllocatedSoFar = 0;
+            return block;
+        }
+
+        Block* initFromStorage(void* storage, size_t size) {
+            SkASSERT(SkIsAlign4((intptr_t)storage));
+            SkASSERT(SkIsAlign4(size));
+            Block* block = this;
+            block->fNext = NULL;
+            block->fBasePtr = (char*)storage;
+            block->fSizeOfBlock = size;
+            block->fAllocatedSoFar = 0;
+            return block;
+        }
+    };
+
+    enum {
+        MIN_BLOCKSIZE = sizeof(SkWriter32::Block) + sizeof(intptr_t)
+    };
+
+    Block       fExternalBlock;
+    Block*      fHead;
+    Block*      fTail;
     size_t      fMinSize;
     uint32_t    fSize;
+    // sum of bytes written in all blocks *before* fTail
+    uint32_t    fWrittenBeforeLastBlock;
 
-    char*       fSingleBlock;
-    uint32_t    fSingleBlockSize;
-    
-    struct Block;
-    Block*  fHead;
-    Block*  fTail;
+    bool isHeadExternallyAllocated() const {
+        return fHead == &fExternalBlock;
+    }
 
     Block* newBlock(size_t bytes);
+
+    // only call from reserve()
+    Block* doReserve(size_t bytes);
+
+    SkDEBUGCODE(void validate() const;)
+};
+
+/**
+ *  Helper class to allocated SIZE bytes as part of the writer, and to provide
+ *  that storage to the constructor as its initial storage buffer.
+ *
+ *  This wrapper ensures proper alignment rules are met for the storage.
+ */
+template <size_t SIZE> class SkSWriter32 : public SkWriter32 {
+public:
+    SkSWriter32(size_t minSize) : SkWriter32(minSize, fData.fStorage, SIZE) {}
+
+private:
+    union {
+        void*   fPtrAlignment;
+        double  fDoubleAlignment;
+        char    fStorage[SIZE];
+    } fData;
 };
 
 #endif
diff --git a/include/core/SkXfermode.h b/include/core/SkXfermode.h
index 0d1c207..d396529 100644
--- a/include/core/SkXfermode.h
+++ b/include/core/SkXfermode.h
@@ -13,6 +13,8 @@
 #include "SkFlattenable.h"
 #include "SkColor.h"
 
+class SkString;
+
 /** \class SkXfermode
 
     SkXfermode is the base class for objects that are called to implement custom
@@ -23,16 +25,18 @@
 */
 class SK_API SkXfermode : public SkFlattenable {
 public:
+    SK_DECLARE_INST_COUNT(SkXfermode)
+
     SkXfermode() {}
 
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]);
+                        const SkAlpha aa[]) const;
     virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]);
+                        const SkAlpha aa[]) const;
     virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
-                          const SkAlpha aa[]);
+                          const SkAlpha aa[]) const;
     virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]);
+                        const SkAlpha aa[]) const;
 
     /** Enum of possible coefficients to describe some xfermodes
      */
@@ -66,13 +70,13 @@
         srcover     one             isa
         dstover     ida             one
      */
-    virtual bool asCoeff(Coeff* src, Coeff* dst);
+    virtual bool asCoeff(Coeff* src, Coeff* dst) const;
 
     /**
      *  The same as calling xfermode->asCoeff(..), except that this also checks
      *  if the xfermode is NULL, and if so, treats its as kSrcOver_Mode.
      */
-    static bool AsCoeff(SkXfermode*, Coeff* src, Coeff* dst);
+    static bool AsCoeff(const SkXfermode*, Coeff* src, Coeff* dst);
 
     /** List of predefined xfermodes.
         The algebra for the modes uses the following symbols:
@@ -99,11 +103,11 @@
         // all remaining modes are defined in the SVG Compositing standard
         // http://www.w3.org/TR/2009/WD-SVGCompositing-20090430/
         kPlus_Mode,
-        kMultiply_Mode, 
-        
+        kModulate_Mode, // multiplies all components (= alpha and color)
+
         // all above modes can be expressed as pair of src/dst Coeffs
-        kCoeffModesCnt, 
-        
+        kCoeffModesCnt,
+
         kScreen_Mode = kCoeffModesCnt,
         kOverlay_Mode,
         kDarken_Mode,
@@ -123,13 +127,13 @@
      *  returns true and sets (if not null) mode accordingly. Otherwise it
      *  returns false and ignores the mode parameter.
      */
-    virtual bool asMode(Mode* mode);
+    virtual bool asMode(Mode* mode) const;
 
     /**
      *  The same as calling xfermode->asMode(mode), except that this also checks
      *  if the xfermode is NULL, and if so, treats its as kSrcOver_Mode.
      */
-    static bool AsMode(SkXfermode*, Mode* mode);
+    static bool AsMode(const SkXfermode*, Mode* mode);
 
     /**
      *  Returns true if the xfermode claims to be the specified Mode. This works
@@ -141,7 +145,7 @@
      *      ...
      *  }
      */
-    static bool IsMode(SkXfermode* xfer, Mode mode);
+    static bool IsMode(const SkXfermode* xfer, Mode mode);
 
     /** Return an SkXfermode object for the specified mode.
      */
@@ -168,11 +172,12 @@
     static bool ModeAsCoeff(Mode mode, Coeff* src, Coeff* dst);
 
     // DEPRECATED: call AsMode(...)
-    static bool IsMode(SkXfermode* xfer, Mode* mode) {
+    static bool IsMode(const SkXfermode* xfer, Mode* mode) {
         return AsMode(xfer, mode);
     }
 
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SkDEVCODE(virtual void toString(SkString* str) const = 0;)
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 protected:
     SkXfermode(SkFlattenableReadBuffer& rb) : SkFlattenable(rb) {}
 
@@ -184,7 +189,7 @@
         This method will not be called directly by the client, so it need not
         be implemented if your subclass has overridden xfer32/xfer16/xferA8
     */
-    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst);
+    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const;
 
 private:
     enum {
@@ -206,20 +211,20 @@
 
     // overrides from SkXfermode
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) SK_OVERRIDE;
+                        const SkAlpha aa[]) const SK_OVERRIDE;
     virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) SK_OVERRIDE;
+                        const SkAlpha aa[]) const SK_OVERRIDE;
     virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
-                          const SkAlpha aa[]) SK_OVERRIDE;
+                          const SkAlpha aa[]) const SK_OVERRIDE;
     virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) SK_OVERRIDE;
+                        const SkAlpha aa[]) const SK_OVERRIDE;
 
-    // overrides from SkFlattenable
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-    virtual void    flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcXfermode)
 
 protected:
     SkProcXfermode(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     // allow subclasses to update this after we unflatten
     void setProc(SkXfermodeProc proc) {
@@ -229,9 +234,6 @@
 private:
     SkXfermodeProc  fProc;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkProcXfermode, (buffer)); }
-
     typedef SkXfermode INHERITED;
 };
 
diff --git a/include/device/xps/SkConstexprMath.h b/include/device/xps/SkConstexprMath.h
new file mode 100644
index 0000000..65b70b6
--- /dev/null
+++ b/include/device/xps/SkConstexprMath.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkConstexprMath_DEFINED
+#define SkConstexprMath_DEFINED
+
+#include "SkTypes.h"
+#include <limits.h>
+
+template <uintmax_t N, uintmax_t B>
+struct SK_LOG {
+    //! Compile-time constant ceiling(logB(N)).
+    static const uintmax_t value = 1 + SK_LOG<N/B, B>::value;
+};
+template <uintmax_t B>
+struct SK_LOG<1, B> {
+    static const uintmax_t value = 0;
+};
+template <uintmax_t B>
+struct SK_LOG<0, B> {
+    static const uintmax_t value = 0;
+};
+
+template<uintmax_t N>
+struct SK_2N1 {
+    //! Compile-time constant (2^N)-1.
+    static const uintmax_t value = (SK_2N1<N-1>::value << 1) + 1;
+};
+template<>
+struct SK_2N1<1> {
+    static const uintmax_t value = 1;
+};
+
+/** Compile-time constant number of base n digits in type t
+    if the bits of type t are considered as unsigned base two.
+*/
+#define SK_BASE_N_DIGITS_IN(n, t) (\
+    SK_LOG<SK_2N1<(sizeof(t) * CHAR_BIT)>::value, n>::value\
+)
+/** Compile-time constant number of base 10 digits in type t
+    if the bits of type t are considered as unsigned base two.
+*/
+#define SK_DIGITS_IN(t) SK_BASE_N_DIGITS_IN(10, (t))
+
+//! a > b ? a : b
+#define SK_MAX(a,b) (((a) > (b)) ? (a) : (b))
+
+#endif
diff --git a/include/device/xps/SkXPSDevice.h b/include/device/xps/SkXPSDevice.h
new file mode 100644
index 0000000..228e905
--- /dev/null
+++ b/include/device/xps/SkXPSDevice.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkXPSDevice_DEFINED
+#define SkXPSDevice_DEFINED
+
+#include "SkTypes.h"
+#include <ObjBase.h>
+#include <XpsObjectModel.h>
+
+#include "SkAutoCoInitialize.h"
+#include "SkBitSet.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkDevice.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkShader.h"
+#include "SkSize.h"
+#include "SkTArray.h"
+#include "SkTScopedComPtr.h"
+#include "SkTypeface.h"
+
+/** \class SkXPSDevice
+
+    The drawing context for the XPS backend.
+*/
+class SkXPSDevice : public SkDevice {
+public:
+    SK_API SkXPSDevice();
+    SK_API virtual ~SkXPSDevice();
+
+    virtual bool beginPortfolio(SkWStream* outputStream);
+    /**
+      @param unitsPerMeter converts geometry units into physical units.
+      @param pixelsPerMeter resolution to use when geometry must be rasterized.
+      @param trimSize final page size in physical units.
+                      The top left of the trim is the origin of physical space.
+      @param mediaBox The size of the physical media in physical units.
+                      The top and left must be less than zero.
+                      The bottom and right must be greater than the trimSize.
+                      The default is to coincide with the trimSize.
+      @param bleedBox The size of the bleed box in physical units.
+                      Must be contained within the mediaBox.
+                      The default is to coincide with the mediaBox.
+      @param artBox The size of the content box in physical units.
+                    Must be contained within the trimSize.
+                    The default is to coincide with the trimSize.
+      @param cropBox The size of the recommended view port in physical units.
+                     Must be contained within the mediaBox.
+                     The default is to coincide with the mediaBox.
+     */
+    virtual bool beginSheet(
+        const SkVector& unitsPerMeter,
+        const SkVector& pixelsPerMeter,
+        const SkSize& trimSize,
+        const SkRect* mediaBox = NULL,
+        const SkRect* bleedBox = NULL,
+        const SkRect* artBox = NULL,
+        const SkRect* cropBox = NULL);
+
+    virtual bool endSheet();
+    virtual bool endPortfolio();
+
+    virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
+
+protected:
+    virtual void clear(SkColor color) SK_OVERRIDE;
+
+    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 drawPath(
+        const SkDraw&,
+        const SkPath& platonicPath,
+        const SkPaint& paint,
+        const SkMatrix* prePathMatrix,
+        bool pathIsMutable) SK_OVERRIDE;
+
+    virtual void drawBitmap(
+        const SkDraw&,
+        const SkBitmap& bitmap,
+        const SkIRect* srcRectOrNull,
+        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 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[], SkScalar constY, int scalarsPerPos,
+        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;
+
+    virtual void drawDevice(
+        const SkDraw&,
+        SkDevice* device,
+        int x, int y,
+        const SkPaint& paint) SK_OVERRIDE;
+
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                              int x,
+                              int y,
+                              SkCanvas::Config8888) SK_OVERRIDE;
+
+    virtual bool allowImageFilter(SkImageFilter*) SK_OVERRIDE;
+
+private:
+    class TypefaceUse : ::SkNoncopyable {
+    public:
+        SkFontID typefaceId;
+        SkStream* fontData;
+        IXpsOMFontResource* xpsFont;
+        SkBitSet* glyphsUsed;
+
+        explicit TypefaceUse();
+        ~TypefaceUse();
+    };
+    friend static HRESULT subset_typeface(TypefaceUse* current);
+
+    SkXPSDevice(IXpsOMObjectFactory* xpsFactory);
+
+    SkAutoCoInitialize fAutoCo;
+    SkTScopedComPtr<IXpsOMObjectFactory> fXpsFactory;
+    SkTScopedComPtr<IStream> fOutputStream;
+    SkTScopedComPtr<IXpsOMPackageWriter> fPackageWriter;
+
+    unsigned int fCurrentPage;
+    SkTScopedComPtr<IXpsOMCanvas> fCurrentXpsCanvas;
+    SkSize fCurrentCanvasSize;
+    SkVector fCurrentUnitsPerMeter;
+    SkVector fCurrentPixelsPerMeter;
+
+    SkTArray<TypefaceUse, true> fTypefaces;
+
+    HRESULT initXpsDocumentWriter(IXpsOMImageResource* image);
+
+    HRESULT createXpsPage(
+        const XPS_SIZE& pageSize,
+        IXpsOMPage** page);
+
+    HRESULT createXpsThumbnail(
+        IXpsOMPage* page, const unsigned int pageNumber,
+        IXpsOMImageResource** image);
+
+    void internalDrawRect(
+        const SkDraw&,
+        const SkRect& r,
+        bool transformRect,
+        const SkPaint& paint);
+
+    HRESULT createXpsBrush(
+        const SkPaint& skPaint,
+        IXpsOMBrush** xpsBrush,
+        const SkMatrix* parentTransform = NULL);
+
+    HRESULT createXpsSolidColorBrush(
+        const SkColor skColor, const SkAlpha alpha,
+        IXpsOMBrush** xpsBrush);
+
+    HRESULT createXpsImageBrush(
+        const SkBitmap& bitmap,
+        const SkMatrix& localMatrix,
+        const SkShader::TileMode (&xy)[2],
+        const SkAlpha alpha,
+        IXpsOMTileBrush** xpsBrush);
+
+    HRESULT createXpsLinearGradient(
+        SkShader::GradientInfo info,
+        const SkAlpha alpha,
+        const SkMatrix& localMatrix,
+        IXpsOMMatrixTransform* xpsMatrixToUse,
+        IXpsOMBrush** xpsBrush);
+
+    HRESULT createXpsRadialGradient(
+        SkShader::GradientInfo info,
+        const SkAlpha alpha,
+        const SkMatrix& localMatrix,
+        IXpsOMMatrixTransform* xpsMatrixToUse,
+        IXpsOMBrush** xpsBrush);
+
+    HRESULT createXpsGradientStop(
+        const SkColor skColor,
+        const SkScalar offset,
+        IXpsOMGradientStop** xpsGradStop);
+
+    HRESULT createXpsTransform(
+        const SkMatrix& matrix,
+        IXpsOMMatrixTransform ** xpsTransform);
+
+    HRESULT createXpsRect(
+        const SkRect& rect,
+        BOOL stroke, BOOL fill,
+        IXpsOMGeometryFigure** xpsRect);
+
+    HRESULT createXpsQuad(
+        const SkPoint (&points)[4],
+        BOOL stroke, BOOL fill,
+        IXpsOMGeometryFigure** xpsQuad);
+
+    HRESULT CreateTypefaceUse(
+        const SkPaint& paint,
+        TypefaceUse** fontResource);
+
+    HRESULT AddGlyphs(
+        const SkDraw& d,
+        IXpsOMObjectFactory* xpsFactory,
+        IXpsOMCanvas* canvas,
+        IXpsOMFontResource* font,
+        LPCWSTR text,
+        XPS_GLYPH_INDEX* xpsGlyphs,
+        UINT32 xpsGlyphsLen,
+        XPS_POINT *origin,
+        FLOAT fontSize,
+        XPS_STYLE_SIMULATION sims,
+        const SkMatrix& transform,
+        const SkPaint& paint);
+
+    HRESULT addXpsPathGeometry(
+        IXpsOMGeometryFigureCollection* figures,
+        BOOL stroke, BOOL fill, const SkPath& path);
+
+    HRESULT createPath(
+        IXpsOMGeometryFigure* figure,
+        IXpsOMVisualCollection* visuals,
+        IXpsOMPath** path);
+
+    HRESULT sideOfClamp(
+        const SkRect& leftPoints, const XPS_RECT& left,
+        IXpsOMImageResource* imageResource,
+        IXpsOMVisualCollection* visuals);
+
+    HRESULT cornerOfClamp(
+        const SkRect& tlPoints,
+        const SkColor color,
+        IXpsOMVisualCollection* visuals);
+
+    HRESULT clip(
+        IXpsOMVisual* xpsVisual,
+        const SkDraw& d);
+    HRESULT clipToPath(
+        IXpsOMVisual* xpsVisual,
+        const SkPath& clipPath,
+        XPS_FILL_RULE fillRule);
+
+    HRESULT drawInverseWindingPath(
+        const SkDraw& d,
+        const SkPath& devicePath,
+        IXpsOMPath* xpsPath);
+
+    HRESULT shadePath(
+        IXpsOMPath* shadedPath,
+        const SkPaint& shaderPaint,
+        const SkMatrix& matrix,
+        BOOL* fill, BOOL* stroke);
+
+    void convertToPpm(
+        const SkMaskFilter* filter,
+        SkMatrix* matrix,
+        SkVector* ppuScale,
+        const SkIRect& clip, SkIRect* clipIRect);
+
+    HRESULT applyMask(
+        const SkDraw& d,
+        const SkMask& mask,
+        const SkVector& ppuScale,
+        IXpsOMPath* shadedPath);
+
+    // override from SkDevice
+    virtual SkDevice* onCreateCompatibleDevice(
+        SkBitmap::Config config,
+        int width, int height,
+        bool isOpaque,
+        Usage usage) SK_OVERRIDE;
+
+    // Disable the default copy and assign implementation.
+    SkXPSDevice(const SkXPSDevice&);
+    void operator=(const SkXPSDevice&);
+
+    typedef SkDevice INHERITED;
+};
+
+#endif
diff --git a/include/effects/Sk1DPathEffect.h b/include/effects/Sk1DPathEffect.h
index 814e547..4599276 100644
--- a/include/effects/Sk1DPathEffect.h
+++ b/include/effects/Sk1DPathEffect.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef Sk1DPathEffect_DEFINED
 #define Sk1DPathEffect_DEFINED
 
@@ -15,38 +13,38 @@
 
 class SkPathMeasure;
 
-//  This class is not exported to java.
-class Sk1DPathEffect : public SkPathEffect {
+// This class is not exported to java.
+class SK_API Sk1DPathEffect : public SkPathEffect {
 public:
-    //  override from SkPathEffect
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
 protected:
     /** Called at the start of each contour, returns the initial offset
         into that contour.
     */
-    virtual SkScalar begin(SkScalar contourLength) = 0;
+    virtual SkScalar begin(SkScalar contourLength) const = 0;
     /** Called with the current distance along the path, with the current matrix
         for the point/tangent at the specified distance.
         Return the distance to travel for the next call. If return <= 0, then that
         contour is done.
     */
-    virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&) = 0;
+    virtual SkScalar next(SkPath* dst, SkScalar dist, SkPathMeasure&) const = 0;
 
 private:
     typedef SkPathEffect INHERITED;
 };
 
-class SkPath1DPathEffect : public Sk1DPathEffect {
+class SK_API SkPath1DPathEffect : public Sk1DPathEffect {
 public:
     enum Style {
         kTranslate_Style,   // translate the shape to each position
         kRotate_Style,      // rotate the shape about its center
         kMorph_Style,       // transform each point, and turn lines into curves
-        
+
         kStyleCount
     };
-    
+
     /** Dash by replicating the specified path.
         @param path The path to replicate (dash)
         @param advance The space between instances of path
@@ -56,25 +54,19 @@
     */
     SkPath1DPathEffect(const SkPath& path, SkScalar advance, SkScalar phase, Style);
 
-    // override from SkPathEffect
-    virtual bool filterPath(SkPath*, const SkPath&, SkScalar* width) SK_OVERRIDE;
+    virtual bool filterPath(SkPath*, const SkPath&,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkPath1DPathEffect, (buffer));
-    }
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPath1DPathEffect)
 
 protected:
     SkPath1DPathEffect(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     // overrides from Sk1DPathEffect
-    virtual SkScalar begin(SkScalar contourLength) SK_OVERRIDE;
-    virtual SkScalar next(SkPath*, SkScalar distance, SkPathMeasure&) SK_OVERRIDE;
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-    
+    virtual SkScalar begin(SkScalar contourLength) const SK_OVERRIDE;
+    virtual SkScalar next(SkPath*, SkScalar, SkPathMeasure&) const SK_OVERRIDE;
+
 private:
     SkPath      fPath;          // copied from constructor
     SkScalar    fAdvance;       // copied from constructor
@@ -84,5 +76,4 @@
     typedef Sk1DPathEffect INHERITED;
 };
 
-
 #endif
diff --git a/include/effects/Sk2DPathEffect.h b/include/effects/Sk2DPathEffect.h
index b5d7fbb..ed7f674 100644
--- a/include/effects/Sk2DPathEffect.h
+++ b/include/effects/Sk2DPathEffect.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef Sk2DPathEffect_DEFINED
 #define Sk2DPathEffect_DEFINED
 
@@ -14,16 +12,14 @@
 #include "SkPathEffect.h"
 #include "SkMatrix.h"
 
-class Sk2DPathEffect : public SkPathEffect {
+class SK_API Sk2DPathEffect : public SkPathEffect {
 public:
     Sk2DPathEffect(const SkMatrix& mat);
 
-    // overrides
-    virtual bool filterPath(SkPath*, const SkPath&, SkScalar* width) SK_OVERRIDE;
+    virtual bool filterPath(SkPath*, const SkPath&,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sk2DPathEffect)
 
 protected:
     /** New virtual, to be overridden by subclasses.
@@ -32,53 +28,72 @@
         next() will receive u and v values within these bounds,
         and then a call to end() will signal the end of processing.
     */
-    virtual void begin(const SkIRect& uvBounds, SkPath* dst);
-    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst);
-    virtual void end(SkPath* dst);
+    virtual void begin(const SkIRect& uvBounds, SkPath* dst) const;
+    virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) const;
+    virtual void end(SkPath* dst) const;
 
     /** Low-level virtual called per span of locations in the u-direction.
         The default implementation calls next() repeatedly with each
         location.
     */
-    virtual void nextSpan(int u, int v, int ucount, SkPath* dst);
+    virtual void nextSpan(int u, int v, int ucount, SkPath* dst) const;
 
     const SkMatrix& getMatrix() const { return fMatrix; }
 
     // protected so that subclasses can call this during unflattening
     Sk2DPathEffect(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     SkMatrix    fMatrix, fInverse;
+    bool        fMatrixIsInvertible;
+
     // illegal
     Sk2DPathEffect(const Sk2DPathEffect&);
     Sk2DPathEffect& operator=(const Sk2DPathEffect&);
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
     friend class Sk2DPathEffectBlitter;
     typedef SkPathEffect INHERITED;
 };
 
-class SkPath2DPathEffect : public Sk2DPathEffect {
+class SK_API SkLine2DPathEffect : public Sk2DPathEffect {
+public:
+    SkLine2DPathEffect(SkScalar width, const SkMatrix& matrix)
+    : Sk2DPathEffect(matrix), fWidth(width) {}
+
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLine2DPathEffect)
+
+protected:
+    virtual void nextSpan(int u, int v, int ucount, SkPath*) const SK_OVERRIDE;
+
+    SkLine2DPathEffect(SkFlattenableReadBuffer&);
+
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    SkScalar fWidth;
+
+    typedef Sk2DPathEffect INHERITED;
+};
+
+class SK_API SkPath2DPathEffect : public Sk2DPathEffect {
 public:
     /**
      *  Stamp the specified path to fill the shape, using the matrix to define
      *  the latice.
      */
     SkPath2DPathEffect(const SkMatrix&, const SkPath&);
-    
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
 
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPath2DPathEffect)
 
 protected:
     SkPath2DPathEffect(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
-    virtual void next(const SkPoint&, int u, int v, SkPath* dst) SK_OVERRIDE;
+    virtual void next(const SkPoint&, int u, int v, SkPath*) const SK_OVERRIDE;
 
 private:
     SkPath  fPath;
@@ -86,5 +101,4 @@
     typedef Sk2DPathEffect INHERITED;
 };
 
-
 #endif
diff --git a/include/effects/SkArithmeticMode.h b/include/effects/SkArithmeticMode.h
index 70d660f..dc5493f 100644
--- a/include/effects/SkArithmeticMode.h
+++ b/include/effects/SkArithmeticMode.h
@@ -10,7 +10,7 @@
 
 #include "SkXfermode.h"
 
-class SkArithmeticMode : public SkXfermode {
+class SK_API SkArithmeticMode : public SkXfermode {
 public:
     /**
      *  result = clamp[k1 * src * dst + k2 * src + k3 * dst + k4]
@@ -24,7 +24,9 @@
      */
     static SkXfermode* Create(SkScalar k1, SkScalar k2,
                               SkScalar k3, SkScalar k4);
+
+private:
+    typedef SkXfermode INHERITED;
 };
 
 #endif
-
diff --git a/include/effects/SkAvoidXfermode.h b/include/effects/SkAvoidXfermode.h
index 398eaea..e354391 100644
--- a/include/effects/SkAvoidXfermode.h
+++ b/include/effects/SkAvoidXfermode.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkAvoidXfermode_DEFINED
 #define SkAvoidXfermode_DEFINED
 
@@ -17,7 +15,7 @@
     This xfermode will draw the src everywhere except on top of the specified
     color.
 */
-class SkAvoidXfermode : public SkXfermode {
+class SK_API SkAvoidXfermode : public SkXfermode {
 public:
     enum Mode {
         kAvoidColor_Mode,   //!< draw everywhere except on the opColor
@@ -43,34 +41,26 @@
 
     // overrides from SkXfermode
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) SK_OVERRIDE;
+                        const SkAlpha aa[]) const SK_OVERRIDE;
     virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) SK_OVERRIDE;
+                        const SkAlpha aa[]) const SK_OVERRIDE;
     virtual void xfer4444(uint16_t dst[], const SkPMColor src[], int count,
-                          const SkAlpha aa[]) SK_OVERRIDE;
+                          const SkAlpha aa[]) const SK_OVERRIDE;
     virtual void xferA8(SkAlpha dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) SK_OVERRIDE;
+                        const SkAlpha aa[]) const SK_OVERRIDE;
 
-    // overrides from SkFlattenable
-    virtual Factory getFactory() SK_OVERRIDE;
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkAvoidXfermode, (buffer));
-    }
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkAvoidXfermode)
 
 protected:
     SkAvoidXfermode(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     SkColor     fOpColor;
     uint32_t    fDistMul;   // x.14
     Mode        fMode;
 
-    static SkFlattenable* Create(SkFlattenableReadBuffer&);
-
     typedef SkXfermode INHERITED;
 };
 
diff --git a/include/effects/SkBicubicImageFilter.h b/include/effects/SkBicubicImageFilter.h
new file mode 100644
index 0000000..cb856fb
--- /dev/null
+++ b/include/effects/SkBicubicImageFilter.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkBicubicImageFilter_DEFINED
+#define SkBicubicImageFilter_DEFINED
+
+#include "SkSingleInputImageFilter.h"
+#include "SkScalar.h"
+#include "SkSize.h"
+#include "SkPoint.h"
+
+/*! \class SkBicubicImageFilter
+    Bicubic resampling image filter.  This filter does a 16-tap bicubic
+    filter using the given matrix.
+ */
+
+class SK_API SkBicubicImageFilter : public SkSingleInputImageFilter {
+public:
+    /** Construct a (scaling-only) bicubic resampling image filter.
+        @param scale        How much to scale the image.
+        @param coefficients The 16 coefficients of the bicubic matrix.
+        @param input        The input image filter.  If NULL, the src bitmap
+                            passed to filterImage() is used instead.
+    */
+
+    SkBicubicImageFilter(const SkSize& scale, const SkScalar coefficients[16], SkImageFilter* input = NULL);
+    static SkBicubicImageFilter* CreateMitchell(const SkSize& scale, SkImageFilter* input = NULL);
+    virtual ~SkBicubicImageFilter();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBicubicImageFilter)
+
+protected:
+    SkBicubicImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
+
+private:
+    SkSize    fScale;
+    SkScalar  fCoefficients[16];
+    typedef SkSingleInputImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkBitmapSource.h b/include/effects/SkBitmapSource.h
new file mode 100644
index 0000000..138987e
--- /dev/null
+++ b/include/effects/SkBitmapSource.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2012 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 SkBitmapSource_DEFINED
+#define SkBitmapSource_DEFINED
+
+#include "SkImageFilter.h"
+#include "SkBitmap.h"
+
+class SK_API SkBitmapSource : public SkImageFilter {
+public:
+    explicit SkBitmapSource(const SkBitmap& bitmap);
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapSource)
+
+protected:
+    explicit SkBitmapSource(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+
+private:
+    SkBitmap fBitmap;
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkBlendImageFilter.h b/include/effects/SkBlendImageFilter.h
new file mode 100644
index 0000000..a2dc847
--- /dev/null
+++ b/include/effects/SkBlendImageFilter.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2012 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 SkBlendImageFilter_DEFINED
+#define SkBlendImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+#include "SkBitmap.h"
+
+class SK_API SkBlendImageFilter : public SkImageFilter {
+public:
+    enum Mode {
+      kNormal_Mode,
+      kMultiply_Mode,
+      kScreen_Mode,
+      kDarken_Mode,
+      kLighten_Mode,
+    };
+    SkBlendImageFilter(Mode mode, SkImageFilter* background, SkImageFilter* foreground = NULL);
+
+    ~SkBlendImageFilter();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlendImageFilter)
+
+    virtual bool onFilterImage(Proxy* proxy,
+                               const SkBitmap& src,
+                               const SkMatrix& ctm,
+                               SkBitmap* dst,
+                               SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
+
+protected:
+    explicit SkBlendImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    Mode fMode;
+    typedef SkImageFilter INHERITED;
+    SkImageFilter* getBackgroundInput() { return getInput(0); }
+    SkImageFilter* getForegroundInput() { return getInput(1); }
+};
+
+#endif
diff --git a/include/effects/SkBlurDrawLooper.h b/include/effects/SkBlurDrawLooper.h
index be1a78e..e968857 100644
--- a/include/effects/SkBlurDrawLooper.h
+++ b/include/effects/SkBlurDrawLooper.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 The Android Open Source Project
  *
@@ -25,18 +24,18 @@
 public:
     enum BlurFlags {
         kNone_BlurFlag = 0x00,
-        /** 
-            The blur layer's dx/dy/radius aren't affected by the canvas 
+        /**
+            The blur layer's dx/dy/radius aren't affected by the canvas
             transform.
         */
         kIgnoreTransform_BlurFlag   = 0x01,
         kOverrideColor_BlurFlag     = 0x02,
         kHighQuality_BlurFlag       = 0x04,
         /** mask for all blur flags */
-        kAll_BlurFlag = 0x07
+        kAll_BlurFlag               = 0x07
     };
 
-    SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color, 
+    SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy, SkColor color,
                      uint32_t flags = kNone_BlurFlag);
     virtual ~SkBlurDrawLooper();
 
@@ -44,24 +43,19 @@
     virtual void init(SkCanvas*);
     virtual bool next(SkCanvas*, SkPaint* paint);
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkBlurDrawLooper, (buffer));
-    }
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurDrawLooper)
 
 protected:
     SkBlurDrawLooper(SkFlattenableReadBuffer&);
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer& );
-    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     SkMaskFilter*   fBlur;
     SkColorFilter*  fColorFilter;
     SkScalar        fDx, fDy;
     SkColor         fBlurColor;
-    uint32_t        fBlurFlags;  
+    uint32_t        fBlurFlags;
 
     enum State {
         kBeforeEdge,
@@ -69,7 +63,7 @@
         kDone
     };
     State   fState;
-    
+
     typedef SkDrawLooper INHERITED;
 };
 
diff --git a/include/effects/SkBlurImageFilter.h b/include/effects/SkBlurImageFilter.h
index 4c9798b..018718f 100644
--- a/include/effects/SkBlurImageFilter.h
+++ b/include/effects/SkBlurImageFilter.h
@@ -5,36 +5,31 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkBlurImageFilter_DEFINED
 #define SkBlurImageFilter_DEFINED
 
-#include "SkImageFilter.h"
+#include "SkSingleInputImageFilter.h"
+#include "SkSize.h"
 
-class SK_API SkBlurImageFilter : public SkImageFilter {
+class SK_API SkBlurImageFilter : public SkSingleInputImageFilter {
 public:
-    SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY);
+    SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input = NULL);
 
-    virtual bool asABlur(SkSize* sigma) const SK_OVERRIDE;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkBlurImageFilter, (buffer));
-    }
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurImageFilter)
 
 protected:
     explicit SkBlurImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+
+    bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
 
 private:
     SkSize   fSigma;
-    typedef SkImageFilter INHERITED;
+    typedef SkSingleInputImageFilter INHERITED;
 };
 
 #endif
-
diff --git a/include/effects/SkBlurMaskFilter.h b/include/effects/SkBlurMaskFilter.h
index 9e85d87..2ab321a 100644
--- a/include/effects/SkBlurMaskFilter.h
+++ b/include/effects/SkBlurMaskFilter.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkBlurMaskFilter_DEFINED
 #define SkBlurMaskFilter_DEFINED
 
@@ -41,7 +39,7 @@
         @param flags    Flags to use - defaults to none
         @return The new blur maskfilter
     */
-    static SkMaskFilter* Create(SkScalar radius, BlurStyle style, 
+    static SkMaskFilter* Create(SkScalar radius, BlurStyle style,
                                 uint32_t flags = kNone_BlurFlag);
 
     /** Create an emboss maskfilter
@@ -55,11 +53,9 @@
                                         SkScalar ambient, SkScalar specular,
                                         SkScalar blurRadius);
 
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
-
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 private:
     SkBlurMaskFilter(); // can't be instantiated
 };
 
 #endif
-
diff --git a/include/effects/SkColorFilterImageFilter.h b/include/effects/SkColorFilterImageFilter.h
new file mode 100755
index 0000000..bff9293
--- /dev/null
+++ b/include/effects/SkColorFilterImageFilter.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2012 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 SkColorFilterImageFilter_DEFINED
+#define SkColorFilterImageFilter_DEFINED
+
+#include "SkSingleInputImageFilter.h"
+
+class SkColorFilter;
+
+class SK_API SkColorFilterImageFilter : public SkSingleInputImageFilter {
+public:
+    static SkColorFilterImageFilter* Create(SkColorFilter* cf, SkImageFilter* input = NULL);
+    virtual ~SkColorFilterImageFilter();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorFilterImageFilter)
+
+protected:
+    SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+
+    virtual SkColorFilter* asColorFilter() const SK_OVERRIDE;
+
+private:
+    SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input);
+    SkColorFilter*  fColorFilter;
+
+    typedef SkSingleInputImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkColorMatrix.h b/include/effects/SkColorMatrix.h
index ee383db..84a3b7c 100644
--- a/include/effects/SkColorMatrix.h
+++ b/include/effects/SkColorMatrix.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2007 The Android Open Source Project
  *
@@ -6,16 +5,15 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkColorMatrix_DEFINED
 #define SkColorMatrix_DEFINED
 
 #include "SkScalar.h"
 
-class SkColorMatrix {
+class SK_API SkColorMatrix {
 public:
     SkScalar    fMat[20];
-    
+
     void setIdentity();
     void setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
                   SkScalar aScale = SK_Scalar1);
@@ -41,6 +39,12 @@
     void setSaturation(SkScalar sat);
     void setRGB2YUV();
     void setYUV2RGB();
+
+    bool operator==(const SkColorMatrix& other) const {
+        return 0 == memcmp(fMat, other.fMat, sizeof(fMat));
+    }
+
+    bool operator!=(const SkColorMatrix& other) const { return !((*this) == other); }
 };
 
 #endif
diff --git a/include/effects/SkColorMatrixFilter.h b/include/effects/SkColorMatrixFilter.h
index 1475258..e44a7cd 100644
--- a/include/effects/SkColorMatrixFilter.h
+++ b/include/effects/SkColorMatrixFilter.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2007 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkColorMatrixFilter_DEFINED
 #define SkColorMatrixFilter_DEFINED
 
@@ -15,48 +13,40 @@
 
 class SK_API SkColorMatrixFilter : public SkColorFilter {
 public:
-    SkColorMatrixFilter();
     explicit SkColorMatrixFilter(const SkColorMatrix&);
     SkColorMatrixFilter(const SkScalar array[20]);
 
-    void setMatrix(const SkColorMatrix&);
-    void setArray(const SkScalar array[20]);
-
     // overrides from SkColorFilter
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor[]) SK_OVERRIDE;
-    virtual void filterSpan16(const uint16_t src[], int count, uint16_t[]) SK_OVERRIDE;
-    virtual uint32_t getFlags() SK_OVERRIDE;
-    virtual bool asColorMatrix(SkScalar matrix[20]) SK_OVERRIDE;
-
-    // overrides for SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE;
+    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor[]) const SK_OVERRIDE;
+    virtual void filterSpan16(const uint16_t src[], int count, uint16_t[]) const SK_OVERRIDE;
+    virtual uint32_t getFlags() const SK_OVERRIDE;
+    virtual bool asColorMatrix(SkScalar matrix[20]) const SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual GrEffectRef* asNewEffect(GrContext*) const SK_OVERRIDE;
+#endif
 
     struct State {
         int32_t fArray[20];
         int     fShift;
-        int32_t fResult[4];
     };
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkColorMatrixFilter)
 
 protected:
-    // overrides for SkFlattenable
-    virtual Factory getFactory();
-
     SkColorMatrixFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
+    SkColorMatrix fMatrix;
 
-    typedef void (*Proc)(State*, unsigned r, unsigned g, unsigned b,
-                         unsigned a);
+    typedef void (*Proc)(const State&, unsigned r, unsigned g, unsigned b,
+                         unsigned a, int32_t result[4]);
 
     Proc        fProc;
     State       fState;
     uint32_t    fFlags;
 
-    void setup(const SkScalar array[20]);
+    void initState(const SkScalar array[20]);
 
     typedef SkColorFilter INHERITED;
 };
diff --git a/include/effects/SkCornerPathEffect.h b/include/effects/SkCornerPathEffect.h
index 990bad0..704b7fb 100644
--- a/include/effects/SkCornerPathEffect.h
+++ b/include/effects/SkCornerPathEffect.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkCornerPathEffect_DEFINED
 #define SkCornerPathEffect_DEFINED
 
@@ -25,28 +23,19 @@
     SkCornerPathEffect(SkScalar radius);
     virtual ~SkCornerPathEffect();
 
-    // overrides for SkPathEffect
-    //  This method is not exported to java.
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
-    // overrides for SkFlattenable
-    //  This method is not exported to java.
-    virtual Factory getFactory();
-    //  This method is not exported to java.
-    virtual void flatten(SkFlattenableWriteBuffer&);
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkCornerPathEffect)
 
 protected:
     SkCornerPathEffect(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     SkScalar    fRadius;
-    
+
     typedef SkPathEffect INHERITED;
 };
 
 #endif
-
diff --git a/include/effects/SkDashPathEffect.h b/include/effects/SkDashPathEffect.h
index 6d34910..9c0775d 100644
--- a/include/effects/SkDashPathEffect.h
+++ b/include/effects/SkDashPathEffect.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkDashPathEffect_DEFINED
 #define SkDashPathEffect_DEFINED
 
@@ -18,32 +16,45 @@
 */
 class SK_API SkDashPathEffect : public SkPathEffect {
 public:
-    /** The intervals array must contain an even number of entries (>=2), with the even
-        indices specifying the "on" intervals, and the odd indices specifying the "off"
-        intervals. phase is an offset into the intervals array (mod the sum of all of the
-        intervals).
-        Note: only affects framed paths
+    /** intervals: array containing an even number of entries (>=2), with
+         the even indices specifying the length of "on" intervals, and the odd
+         indices specifying the length of "off" intervals.
+        count: number of elements in the intervals array
+        phase: offset into the intervals array (mod the sum of all of the
+         intervals).
+
+        For example: if intervals[] = {10, 20}, count = 2, and phase = 25,
+         this will set up a dashed path like so:
+         5 pixels off
+         10 pixels on
+         20 pixels off
+         10 pixels on
+         20 pixels off
+         ...
+        A phase of -5, 25, 55, 85, etc. would all result in the same path,
+         because the sum of all the intervals is 30.
+
+        Note: only affects stroked paths.
     */
-    SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase, bool scaleToFit = false);
+    SkDashPathEffect(const SkScalar intervals[], int count, SkScalar phase,
+                     bool scaleToFit = false);
     virtual ~SkDashPathEffect();
 
-    // overrides for SkPathEffect
-    //  This method is not exported to java.
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
-    // overrides for SkFlattenable
-    //  This method is not exported to java.
-    virtual Factory getFactory();
-    //  This method is not exported to java.
-    virtual void flatten(SkFlattenableWriteBuffer&);
+    virtual bool asPoints(PointData* results, const SkPath& src,
+                          const SkStrokeRec&, const SkMatrix&,
+                          const SkRect*) const SK_OVERRIDE;
+
+    virtual Factory getFactory() SK_OVERRIDE;
 
     static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
 
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
-
 protected:
     SkDashPathEffect(SkFlattenableReadBuffer&);
-    
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
 private:
     SkScalar*   fIntervals;
     int32_t     fCount;
@@ -57,4 +68,3 @@
 };
 
 #endif
-
diff --git a/include/effects/SkDiscretePathEffect.h b/include/effects/SkDiscretePathEffect.h
index 5369ddb..999ea04 100644
--- a/include/effects/SkDiscretePathEffect.h
+++ b/include/effects/SkDiscretePathEffect.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkDiscretePathEffect_DEFINED
 #define SkDiscretePathEffect_DEFINED
 
@@ -16,7 +14,7 @@
 
     This path effect chops a path into discrete segments, and randomly displaces them.
 */
-class SkDiscretePathEffect : public SkPathEffect {
+class SK_API SkDiscretePathEffect : public SkPathEffect {
 public:
     /** Break the path into segments of segLength length, and randomly move the endpoints
         away from the original path by a maximum of deviation.
@@ -24,28 +22,19 @@
     */
     SkDiscretePathEffect(SkScalar segLength, SkScalar deviation);
 
-    // overrides for SkPathEffect
-    //  This method is not exported to java.
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width);
+    virtual bool filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec*, const SkRect*) const SK_OVERRIDE;
 
-    // overrides for SkFlattenable
-    //  This method is not exported to java.
-    virtual Factory getFactory();
-    //  This method is not exported to java.
-    virtual void flatten(SkFlattenableWriteBuffer&);
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiscretePathEffect)
 
 protected:
     SkDiscretePathEffect(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     SkScalar fSegLength, fPerterb;
-    
+
     typedef SkPathEffect INHERITED;
 };
 
 #endif
-
diff --git a/include/effects/SkDisplacementMapEffect.h b/include/effects/SkDisplacementMapEffect.h
new file mode 100644
index 0000000..18f9df8
--- /dev/null
+++ b/include/effects/SkDisplacementMapEffect.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkDisplacementMapEffect_DEFINED
+#define SkDisplacementMapEffect_DEFINED
+
+#include "SkImageFilter.h"
+#include "SkBitmap.h"
+
+class SK_API SkDisplacementMapEffect : public SkImageFilter {
+public:
+    enum ChannelSelectorType {
+        kUnknown_ChannelSelectorType,
+        kR_ChannelSelectorType,
+        kG_ChannelSelectorType,
+        kB_ChannelSelectorType,
+        kA_ChannelSelectorType,
+        kKeyBits = 3 // Max value is 4, so 3 bits are required at most
+    };
+
+    SkDisplacementMapEffect(ChannelSelectorType xChannelSelector, ChannelSelectorType yChannelSelector, SkScalar scale, SkImageFilter* displacement, SkImageFilter* color = NULL);
+
+    ~SkDisplacementMapEffect();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDisplacementMapEffect)
+
+    virtual bool onFilterImage(Proxy* proxy,
+                               const SkBitmap& src,
+                               const SkMatrix& ctm,
+                               SkBitmap* dst,
+                               SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
+
+protected:
+    explicit SkDisplacementMapEffect(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    ChannelSelectorType fXChannelSelector;
+    ChannelSelectorType fYChannelSelector;
+    SkScalar fScale;
+    typedef SkImageFilter INHERITED;
+    SkImageFilter* getDisplacementInput() { return getInput(0); }
+    SkImageFilter* getColorInput() { return getInput(1); }
+};
+
+#endif
diff --git a/include/effects/SkDrawExtraPathEffect.h b/include/effects/SkDrawExtraPathEffect.h
index c7611f0..392a46b 100644
--- a/include/effects/SkDrawExtraPathEffect.h
+++ b/include/effects/SkDrawExtraPathEffect.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 The Android Open Source Project
  *
@@ -6,10 +5,11 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SK_DRAW_EXTRA_PATH_EFFECT_H
 #define SK_DRAW_EXTRA_PATH_EFFECT_H
-class SkAnimator;
-void InitializeSkExtraPathEffects(SkAnimator* animator);
-#endif
 
+class SkAnimator;
+
+void InitializeSkExtraPathEffects(SkAnimator* animator);
+
+#endif
diff --git a/include/effects/SkEffects.h b/include/effects/SkEffects.h
deleted file mode 100644
index 04091de..0000000
--- a/include/effects/SkEffects.h
+++ /dev/null
@@ -1,16 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkEffects_DEFINED
-#define SkEffects_DEFINED
-
-class SkEffects {
-public:
-    static void Init();
-};
-
-#endif
diff --git a/include/effects/SkEmbossMaskFilter.h b/include/effects/SkEmbossMaskFilter.h
index 257e19a..ce64473 100644
--- a/include/effects/SkEmbossMaskFilter.h
+++ b/include/effects/SkEmbossMaskFilter.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkEmbossMaskFilter_DEFINED
 #define SkEmbossMaskFilter_DEFINED
 
@@ -16,7 +14,7 @@
 
     This mask filter creates a 3D emboss look, by specifying a light and blur amount.
 */
-class SkEmbossMaskFilter : public SkMaskFilter {
+class SK_API SkEmbossMaskFilter : public SkMaskFilter {
 public:
     struct Light {
         SkScalar    fDirection[3];  // x,y,z
@@ -29,31 +27,22 @@
 
     // overrides from SkMaskFilter
     //  This method is not exported to java.
-    virtual SkMask::Format getFormat();
+    virtual SkMask::Format getFormat() const SK_OVERRIDE;
     //  This method is not exported to java.
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                            SkIPoint* margin);
+                            SkIPoint* margin) const SK_OVERRIDE;
 
-    // overrides from SkFlattenable
-
-    //  This method is not exported to java.
-    virtual Factory getFactory();
-    //  This method is not exported to java.
-    virtual void flatten(SkFlattenableWriteBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkEmbossMaskFilter)
 
 protected:
     SkEmbossMaskFilter(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     Light       fLight;
     SkScalar    fBlurRadius;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-    
     typedef SkMaskFilter INHERITED;
 };
 
 #endif
-
diff --git a/include/effects/SkGradientShader.h b/include/effects/SkGradientShader.h
index 3232703..dbeb2fb 100644
--- a/include/effects/SkGradientShader.h
+++ b/include/effects/SkGradientShader.h
@@ -34,7 +34,7 @@
                         the the colors are distributed evenly between the start and end point.
                         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  count   Must be >=2. The number of colors (and pos if not NULL) entries.
         @param  mode    The tiling mode
         @param  mapper  May be NULL. Callback to modify the spread of the colors.
     */
@@ -93,6 +93,22 @@
                                           const SkScalar pos[], int count,
                                           SkShader::TileMode mode,
                                           SkUnitMapper* mapper = NULL);
+
+    /**
+     *  Returns a shader that generates a conical gradient given two circles, or
+     *  returns NULL if the inputs are invalid. The gradient interprets the
+     *  two circles according to the following HTML spec.
+     *  http://dev.w3.org/html5/2dcontext/#dom-context-2d-createradialgradient
+     */
+    static SkShader* CreateTwoPointConical(const SkPoint& start,
+                                          SkScalar startRadius,
+                                          const SkPoint& end,
+                                          SkScalar endRadius,
+                                          const SkColor colors[],
+                                          const SkScalar pos[], int count,
+                                          SkShader::TileMode mode,
+                                          SkUnitMapper* mapper = NULL);
+
     /** Returns a shader that generates a sweep gradient given a center.
         <p />
         CreateSweep returns a shader with a reference count of 1.
@@ -113,8 +129,7 @@
                                  const SkColor colors[], const SkScalar pos[],
                                  int count, SkUnitMapper* mapper = NULL);
 
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 };
 
 #endif
-
diff --git a/include/effects/SkGroupShape.h b/include/effects/SkGroupShape.h
deleted file mode 100644
index 7764003..0000000
--- a/include/effects/SkGroupShape.h
+++ /dev/null
@@ -1,159 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkGroupShape_DEFINED
-#define SkGroupShape_DEFINED
-
-#include "SkMatrix.h"
-#include "SkShape.h"
-#include "SkTDArray.h"
-#include "SkThread.h"
-
-template <typename T> class SkTRefCnt : public T {
-public:
-    SkTRefCnt() : fRefCnt(1) {}
-    ~SkTRefCnt() { SkASSERT(1 == fRefCnt); }
-
-    int32_t getRefCnt() const { return fRefCnt; }
-    
-    /** Increment the reference count. Must be balanced by a call to unref().
-     */
-    void ref() const {
-        SkASSERT(fRefCnt > 0);
-        sk_atomic_inc(&fRefCnt);
-    }
-    
-    /** Decrement the reference count. If the reference count is 1 before the
-        decrement, then call delete on the object. Note that if this is the
-        case, then the object needs to have been allocated via new, and not on
-        the stack.
-     */
-    void unref() const {
-        SkASSERT(fRefCnt > 0);
-        if (sk_atomic_dec(&fRefCnt) == 1) {
-            fRefCnt = 1;    // so our destructor won't complain
-            SkDELETE(this);
-        }
-    }
-    
-    static void SafeRef(const SkTRefCnt* obj) {
-        if (obj) {
-            obj->ref();
-        }
-    }
-    
-    static void SafeUnref(const SkTRefCnt* obj) {
-        if (obj) {
-            obj->unref();
-        }
-    }
-    
-private:
-    mutable int32_t fRefCnt;
-};
-
-class SkMatrixRef : public SkTRefCnt<SkMatrix> {
-public:
-    SkMatrixRef() { this->reset(); }
-    explicit SkMatrixRef(const SkMatrix& matrix) {
-        SkMatrix& m = *this;
-        m = matrix;
-    }
-    
-    SkMatrix& operator=(const SkMatrix& matrix) {
-        SkMatrix& m = *this;
-        m = matrix;
-        return m;
-    }
-};
-
-class SkGroupShape : public SkShape {
-public:
-            SkGroupShape();
-    virtual ~SkGroupShape();
-
-    /** Return the number of child shapes in this group
-     */
-    int countShapes() const;
-
-    /** Return the shape at the specified index. Note this does not affect the
-        owner count of the index'd shape. If index is out of range, returns NULL
-     */
-    SkShape* getShape(int index, SkMatrixRef** = NULL) const;
-    
-    /** Helper function to return the matrixref of the specified shape.
-     */
-    SkMatrixRef* getShapeMatrixRef(int index) const {
-        SkMatrixRef* mr = NULL;
-        (void)this->getShape(index, &mr);
-        return mr;
-    }
-
-    /** Ref the specified shape, and insert it into the child list at the
-        specified index. If index == countShapes(), then the shape will be
-        appended to the child list, otherwise if index is out of range, the
-        shape is not added. Either way, the shape parameter is returned.
-     
-        Child shapes are drawn in order, after the parent, so the shape at index
-        0 will be drawn first, and the shape at index countShapes() - 1 will be
-        drawn last.
-     */
-    void addShape(int index, SkShape*, SkMatrixRef* = NULL);
-
-    void addShape(int index, SkShape* shape, const SkMatrix& matrix) {
-        SkMatrixRef* mr = SkNEW_ARGS(SkMatrixRef, (matrix));
-        this->addShape(index, shape, mr);
-        mr->unref();
-    }
-
-    /** Helper method to append a shape, passing countShapes() for the index
-     */
-    SkShape* appendShape(SkShape* shape, SkMatrixRef* mr = NULL) {
-        this->addShape(this->countShapes(), shape, mr);
-        return shape;
-    }
-    
-    SkShape* appendShape(SkShape* shape, const SkMatrix& matrix) {
-        this->addShape(this->countShapes(), shape, matrix);
-        return shape;
-    }
-    
-    /** Unref the specified index, and remove it from the child list. If index
-        is out of range, does nothing.
-     */
-    void removeShape(int index);
-
-    /** Unrefs and removes all of the child shapes
-     */
-    void removeAllShapes();
-
-    // overrides
-    virtual Factory getFactory();
-    virtual void flatten(SkFlattenableWriteBuffer&);
-
-    // public for Registrar
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
-
-protected:
-    // overrides
-    virtual void onDraw(SkCanvas*);
-
-    SkGroupShape(SkFlattenableReadBuffer&);
-
-private:
-    struct Rec {
-        SkShape*     fShape;
-        SkMatrixRef* fMatrixRef;
-    };
-    SkTDArray<Rec> fList;
-
-    typedef SkShape INHERITED;
-};
-
-#endif
diff --git a/include/effects/SkImageFilterUtils.h b/include/effects/SkImageFilterUtils.h
new file mode 100644
index 0000000..72a25fe
--- /dev/null
+++ b/include/effects/SkImageFilterUtils.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkImageFilterUtils_DEFINED
+#define SkImageFilterUtils_DEFINED
+
+#if SK_SUPPORT_GPU
+
+#include "SkImageFilter.h"
+
+class SkBitmap;
+class GrTexture;
+class SkImageFilter;
+
+class SK_API SkImageFilterUtils {
+public:
+    /**
+     * Wrap the given texture in a texture-backed SkBitmap.
+     */
+    static bool WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result);
+
+    /**
+     * Recursively evaluate the given filter on the GPU.  If filter is NULL,
+     * this function returns src.  If the filter has no GPU implementation, it
+     * will be processed in software and uploaded to the GPU.
+     */
+    static bool GetInputResultGPU(SkImageFilter* filter, SkImageFilter::Proxy* proxy, const SkBitmap& src, SkBitmap* result);
+};
+
+#endif
+
+#endif
diff --git a/include/effects/SkKernel33MaskFilter.h b/include/effects/SkKernel33MaskFilter.h
index 2b9e6d4..9fcbb6a 100644
--- a/include/effects/SkKernel33MaskFilter.h
+++ b/include/effects/SkKernel33MaskFilter.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 The Android Open Source Project
  *
@@ -6,59 +5,54 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkKernel33MaskFilter_DEFINED
 #define SkKernel33MaskFilter_DEFINED
 
 #include "SkMaskFilter.h"
 
-class SkKernel33ProcMaskFilter : public SkMaskFilter {
+class SK_API SkKernel33ProcMaskFilter : public SkMaskFilter {
 public:
     SkKernel33ProcMaskFilter(unsigned percent256 = 256)
         : fPercent256(percent256) {}
 
-    virtual uint8_t computeValue(uint8_t* const* srcRows) = 0;
-    
-    // overrides from SkMaskFilter
-    virtual SkMask::Format getFormat();
-    virtual bool filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*);
+    virtual uint8_t computeValue(uint8_t* const* srcRows) const = 0;
 
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer& wb);
+    virtual SkMask::Format getFormat() const SK_OVERRIDE;
+    virtual bool filterMask(SkMask*, const SkMask&, const SkMatrix&,
+                            SkIPoint*) const SK_OVERRIDE;
 
 protected:
     SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     int fPercent256;
-    
+
     typedef SkMaskFilter INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class SkKernel33MaskFilter : public SkKernel33ProcMaskFilter {
+class SK_API SkKernel33MaskFilter : public SkKernel33ProcMaskFilter {
 public:
     SkKernel33MaskFilter(const int coeff[3][3], int shift, int percent256 = 256)
             : SkKernel33ProcMaskFilter(percent256) {
         memcpy(fKernel, coeff, 9 * sizeof(int));
         fShift = shift;
     }
-    
+
     // override from SkKernel33ProcMaskFilter
-    virtual uint8_t computeValue(uint8_t* const* srcRows);
-    
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer& wb);
-    virtual Factory getFactory();
-    
+    virtual uint8_t computeValue(uint8_t* const* srcRows) const SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkKernel33MaskFilter)
+
 private:
     int fKernel[3][3];
     int fShift;
 
     SkKernel33MaskFilter(SkFlattenableReadBuffer& rb);
-    static SkFlattenable* Create(SkFlattenableReadBuffer& rb);
-    
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
     typedef SkKernel33ProcMaskFilter INHERITED;
 };
 
diff --git a/include/effects/SkLayerDrawLooper.h b/include/effects/SkLayerDrawLooper.h
index 697d7b2..cec78b7 100644
--- a/include/effects/SkLayerDrawLooper.h
+++ b/include/effects/SkLayerDrawLooper.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.
  */
+
 #ifndef SkLayerDrawLooper_DEFINED
 #define SkLayerDrawLooper_DEFINED
 
@@ -15,6 +15,8 @@
 
 class SK_API SkLayerDrawLooper : public SkDrawLooper {
 public:
+    SK_DECLARE_INST_COUNT(SkLayerDrawLooper)
+
             SkLayerDrawLooper();
     virtual ~SkLayerDrawLooper();
 
@@ -33,7 +35,7 @@
         kShader_Bit     = 1 << 4,   //!< use this layer's shader
         kColorFilter_Bit = 1 << 5,  //!< use this layer's colorfilter
         kXfermode_Bit   = 1 << 6,   //!< use this layer's xfermode
-        
+
         /**
          *  Use the layer's paint entirely, with these exceptions:
          *  - We never override the draw's paint's text_encoding, since that is
@@ -41,8 +43,8 @@
          *  - Flags and Color are always computed using the LayerInfo's
          *    fFlagsMask and fColorMode.
          */
-        kEntirePaint_Bits = -1,
-        
+        kEntirePaint_Bits = -1
+
     };
     typedef int32_t BitFlags;
 
@@ -88,33 +90,26 @@
     SkPaint* addLayer(const LayerInfo&);
 
     /**
-     *  This layer will draw with the original paint, ad the specified offset
+     *  This layer will draw with the original paint, at the specified offset
      */
     void addLayer(SkScalar dx, SkScalar dy);
-    
+
     /**
      *  This layer will with the original paint and no offset.
      */
     void addLayer() { this->addLayer(0, 0); }
-    
+
     // overrides from SkDrawLooper
     virtual void init(SkCanvas*);
     virtual bool next(SkCanvas*, SkPaint* paint);
 
-    // must be public for Registrar :(
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkLayerDrawLooper, (buffer));
-    }
-    
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLayerDrawLooper)
 
 protected:
     SkLayerDrawLooper(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer& );
-    virtual Factory getFactory() { return CreateProc; }
-    
 private:
     struct Rec {
         Rec*    fNext;
@@ -135,9 +130,8 @@
     public:
         MyRegistrar();
     };
-    
+
     typedef SkDrawLooper INHERITED;
 };
 
-
 #endif
diff --git a/include/effects/SkLayerRasterizer.h b/include/effects/SkLayerRasterizer.h
index 91deb61..65d1be0 100644
--- a/include/effects/SkLayerRasterizer.h
+++ b/include/effects/SkLayerRasterizer.h
@@ -16,37 +16,32 @@
 
 class SkPaint;
 
-class SkLayerRasterizer : public SkRasterizer {
+class SK_API SkLayerRasterizer : public SkRasterizer {
 public:
             SkLayerRasterizer();
     virtual ~SkLayerRasterizer();
-    
+
     void addLayer(const SkPaint& paint) {
         this->addLayer(paint, 0, 0);
     }
 
-	/**	Add a new layer (above any previous layers) to the rasterizer.
-		The layer will extract those fields that affect the mask from
-		the specified paint, but will not retain a reference to the paint
-		object itself, so it may be reused without danger of side-effects.
-	*/
+    /**    Add a new layer (above any previous layers) to the rasterizer.
+        The layer will extract those fields that affect the mask from
+        the specified paint, but will not retain a reference to the paint
+        object itself, so it may be reused without danger of side-effects.
+    */
     void addLayer(const SkPaint& paint, SkScalar dx, SkScalar dy);
 
-    // overrides from SkFlattenable
-    virtual Factory getFactory();
-    virtual void    flatten(SkFlattenableWriteBuffer&);
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLayerRasterizer)
 
 protected:
     SkLayerRasterizer(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     // override from SkRasterizer
     virtual bool onRasterize(const SkPath& path, const SkMatrix& matrix,
                              const SkIRect* clipBounds,
-                             SkMask* mask, SkMask::CreateMode mode);
+                             SkMask* mask, SkMask::CreateMode mode) const;
 
 private:
     SkDeque fLayers;
diff --git a/include/effects/SkLightingImageFilter.h b/include/effects/SkLightingImageFilter.h
new file mode 100644
index 0000000..da95b7b
--- /dev/null
+++ b/include/effects/SkLightingImageFilter.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2012 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 SkLightingImageFilter_DEFINED
+#define SkLightingImageFilter_DEFINED
+
+#include "SkSingleInputImageFilter.h"
+#include "SkColor.h"
+
+class SK_API SkPoint3 {
+public:
+    SkPoint3() {}
+    SkPoint3(SkScalar x, SkScalar y, SkScalar z)
+      : fX(x), fY(y), fZ(z) {}
+    SkScalar dot(const SkPoint3& other) const {
+        return SkScalarMul(fX, other.fX)
+             + SkScalarMul(fY, other.fY)
+             + SkScalarMul(fZ, other.fZ);
+    }
+    SkScalar maxComponent() const {
+        return fX > fY ? (fX > fZ ? fX : fZ) : (fY > fZ ? fY : fZ);
+    }
+    void normalize() {
+        SkScalar scale = SkScalarInvert(SkScalarSqrt(dot(*this)));
+        fX = SkScalarMul(fX, scale);
+        fY = SkScalarMul(fY, scale);
+        fZ = SkScalarMul(fZ, scale);
+    }
+    SkPoint3 operator*(SkScalar scalar) const {
+        return SkPoint3(SkScalarMul(fX, scalar),
+                        SkScalarMul(fY, scalar),
+                        SkScalarMul(fZ, scalar));
+    }
+    SkPoint3 operator-(const SkPoint3& other) const {
+        return SkPoint3(fX - other.fX, fY - other.fY, fZ - other.fZ);
+    }
+    bool operator==(const SkPoint3& other) const {
+        return fX == other.fX && fY == other.fY && fZ == other.fZ;
+    }
+    SkScalar fX, fY, fZ;
+};
+
+class SkLight;
+
+class SK_API SkLightingImageFilter : public SkSingleInputImageFilter {
+public:
+    static SkImageFilter* CreateDistantLitDiffuse(const SkPoint3& direction,
+        SkColor lightColor, SkScalar surfaceScale, SkScalar kd,
+        SkImageFilter* input = NULL);
+    static SkImageFilter* CreatePointLitDiffuse(const SkPoint3& location,
+        SkColor lightColor, SkScalar surfaceScale, SkScalar kd,
+        SkImageFilter* input = NULL);
+    static SkImageFilter* CreateSpotLitDiffuse(const SkPoint3& location,
+        const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle,
+        SkColor lightColor, SkScalar surfaceScale, SkScalar kd,
+        SkImageFilter* input = NULL);
+    static SkImageFilter* CreateDistantLitSpecular(const SkPoint3& direction,
+        SkColor lightColor, SkScalar surfaceScale, SkScalar ks,
+        SkScalar shininess, SkImageFilter* input = NULL);
+    static SkImageFilter* CreatePointLitSpecular(const SkPoint3& location,
+        SkColor lightColor, SkScalar surfaceScale, SkScalar ks,
+        SkScalar shininess, SkImageFilter* input = NULL);
+    static SkImageFilter* CreateSpotLitSpecular(const SkPoint3& location,
+        const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle,
+        SkColor lightColor, SkScalar surfaceScale, SkScalar ks,
+        SkScalar shininess, SkImageFilter* input = NULL);
+    ~SkLightingImageFilter();
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
+
+protected:
+    SkLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkImageFilter* input);
+    explicit SkLightingImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+    const SkLight* light() const { return fLight; }
+    SkScalar surfaceScale() const { return fSurfaceScale; }
+
+private:
+    typedef SkSingleInputImageFilter INHERITED;
+    SkLight* fLight;
+    SkScalar fSurfaceScale;
+};
+
+#endif
diff --git a/include/effects/SkMagnifierImageFilter.h b/include/effects/SkMagnifierImageFilter.h
new file mode 100644
index 0000000..e013426
--- /dev/null
+++ b/include/effects/SkMagnifierImageFilter.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2012 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 SkMagnifierImageFilter_DEFINED
+#define SkMagnifierImageFilter_DEFINED
+
+#include "SkRect.h"
+#include "SkImageFilter.h"
+
+class SK_API SkMagnifierImageFilter : public SkImageFilter {
+public:
+    SkMagnifierImageFilter(SkRect srcRect, SkScalar inset);
+
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture* texture) const SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMagnifierImageFilter)
+
+protected:
+    explicit SkMagnifierImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+
+private:
+    SkRect fSrcRect;
+    SkScalar fInset;
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkMatrixConvolutionImageFilter.h b/include/effects/SkMatrixConvolutionImageFilter.h
new file mode 100644
index 0000000..09cb5ef
--- /dev/null
+++ b/include/effects/SkMatrixConvolutionImageFilter.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2012 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 SkMatrixConvolutionImageFilter_DEFINED
+#define SkMatrixConvolutionImageFilter_DEFINED
+
+#include "SkSingleInputImageFilter.h"
+#include "SkScalar.h"
+#include "SkSize.h"
+#include "SkPoint.h"
+
+/*! \class SkMatrixConvolutionImageFilter
+    Matrix convolution image filter.  This filter applies an NxM image
+    processing kernel to a given input image.  This can be used to produce
+    effects such as sharpening, blurring, edge detection, etc.
+ */
+
+class SK_API SkMatrixConvolutionImageFilter : public SkSingleInputImageFilter {
+public:
+    /*! \enum TileMode */
+    enum TileMode {
+      kClamp_TileMode,         /*!< Clamp to the image's edge pixels. */
+      kRepeat_TileMode,        /*!< Wrap around to the image's opposite edge. */
+      kClampToBlack_TileMode,  /*!< Fill with transparent black. */
+    };
+
+    /** Construct a matrix convolution image filter.
+        @param kernelSize  The kernel size in pixels, in each dimension (N by M).
+        @param kernel      The image processing kernel.  Must contain N * M
+                           elements, in row order.
+        @param gain        A scale factor applied to each pixel after
+                           convolution.  This can be used to normalize the
+                           kernel, if it does not sum to 1.
+        @param bias        A bias factor added to each pixel after convolution.
+        @param target      An offset applied to each pixel coordinate before
+                           convolution.  This can be used to center the kernel
+                           over the image (e.g., a 3x3 kernel should have a
+                           target of {1, 1}).
+        @param tileMode    How accesses outside the image are treated.  (@see
+                           TileMode).
+        @param convolveAlpha  If true, all channels are convolved.  If false,
+                           only the RGB channels are convolved, and
+                           alpha is copied from the source image.
+        @param input       The input image filter.  If NULL, the src bitmap
+                           passed to filterImage() is used instead.
+    */
+
+    SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, bool convolveAlpha, SkImageFilter* input = NULL);
+    virtual ~SkMatrixConvolutionImageFilter();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMatrixConvolutionImageFilter)
+
+protected:
+    SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+
+#if SK_SUPPORT_GPU
+    virtual bool asNewEffect(GrEffectRef**, GrTexture*) const SK_OVERRIDE;
+#endif
+
+private:
+    SkISize   fKernelSize;
+    SkScalar* fKernel;
+    SkScalar  fGain;
+    SkScalar  fBias;
+    SkIPoint  fTarget;
+    TileMode  fTileMode;
+    bool      fConvolveAlpha;
+    typedef SkSingleInputImageFilter INHERITED;
+
+    template <class PixelFetcher, bool convolveAlpha>
+    void filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect);
+    template <class PixelFetcher>
+    void filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect);
+    void filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect);
+    void filterBorderPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect);
+};
+
+#endif
diff --git a/include/effects/SkMergeImageFilter.h b/include/effects/SkMergeImageFilter.h
new file mode 100755
index 0000000..8c4313d
--- /dev/null
+++ b/include/effects/SkMergeImageFilter.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012 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 SkMergeImageFilter_DEFINED
+#define SkMergeImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+
+#include "SkXfermode.h"
+
+class SK_API SkMergeImageFilter : public SkImageFilter {
+public:
+    SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
+                       SkXfermode::Mode = SkXfermode::kSrcOver_Mode);
+    SkMergeImageFilter(SkImageFilter* filters[], int count,
+                       const SkXfermode::Mode modes[] = NULL);
+    virtual ~SkMergeImageFilter();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkMergeImageFilter)
+
+protected:
+    SkMergeImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+    virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
+
+private:
+    uint8_t*            fModes; // SkXfermode::Mode
+
+    // private storage, to avoid dynamically allocating storage for our copy
+    // of the modes (unless the count is so large we can't fit).
+    intptr_t    fStorage[16];
+
+    void initAllocModes();
+    void initModes(const SkXfermode::Mode []);
+
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkMorphologyImageFilter.h b/include/effects/SkMorphologyImageFilter.h
index 2297938..b4bc676 100644
--- a/include/effects/SkMorphologyImageFilter.h
+++ b/include/effects/SkMorphologyImageFilter.h
@@ -9,57 +9,65 @@
 #ifndef SkMorphologyImageFilter_DEFINED
 #define SkMorphologyImageFilter_DEFINED
 
-#include "SkImageFilter.h"
+#include "SkSingleInputImageFilter.h"
+#include "SkSize.h"
 
-class SK_API SkMorphologyImageFilter : public SkImageFilter {
+class SK_API SkMorphologyImageFilter : public SkSingleInputImageFilter {
 public:
-    explicit SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer);
-    SkMorphologyImageFilter(int radiusX, int radiusY);
+    SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input);
 
 protected:
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE;
+    SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual bool canFilterImageGPU() const SK_OVERRIDE { return true; }
+#endif
+
     SkISize    radius() const { return fRadius; }
 
 private:
     SkISize    fRadius;
-    typedef SkImageFilter INHERITED;
+    typedef SkSingleInputImageFilter INHERITED;
 };
 
 class SK_API SkDilateImageFilter : public SkMorphologyImageFilter {
 public:
-    SkDilateImageFilter(int radiusX, int radiusY) : INHERITED(radiusX, radiusY) {}
-    explicit SkDilateImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+    SkDilateImageFilter(int radiusX, int radiusY, SkImageFilter* input = NULL)
+    : INHERITED(radiusX, radiusY, input) {}
 
-    virtual bool asADilate(SkISize* radius) const SK_OVERRIDE;
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkDilateImageFilter, (buffer));
-    }
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+#if SK_SUPPORT_GPU
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
 
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDilateImageFilter)
+
+protected:
+    SkDilateImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+
+private:
     typedef SkMorphologyImageFilter INHERITED;
 };
 
 class SK_API SkErodeImageFilter : public SkMorphologyImageFilter {
 public:
-    SkErodeImageFilter(int radiusX, int radiusY) : INHERITED(radiusX, radiusY) {}
-    explicit SkErodeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
+    SkErodeImageFilter(int radiusX, int radiusY, SkImageFilter* input = NULL)
+    : INHERITED(radiusX, radiusY, input) {}
 
-    virtual bool asAnErode(SkISize* radius) const SK_OVERRIDE;
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+#if SK_SUPPORT_GPU
+    virtual bool filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) SK_OVERRIDE;
+#endif
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkErodeImageFilter, (buffer));
-    }
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkErodeImageFilter)
+
+protected:
+    SkErodeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
 
 private:
     typedef SkMorphologyImageFilter INHERITED;
 };
 
 #endif
-
diff --git a/include/effects/SkOffsetImageFilter.h b/include/effects/SkOffsetImageFilter.h
new file mode 100644
index 0000000..74d6ebc
--- /dev/null
+++ b/include/effects/SkOffsetImageFilter.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2012 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 SkOffsetImageFilter_DEFINED
+#define SkOffsetImageFilter_DEFINED
+
+#include "SkSingleInputImageFilter.h"
+#include "SkPoint.h"
+
+class SK_API SkOffsetImageFilter : public SkSingleInputImageFilter {
+public:
+    SkOffsetImageFilter(SkScalar dx, SkScalar dy, SkImageFilter* input = NULL);
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkOffsetImageFilter)
+
+protected:
+    SkOffsetImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
+    virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
+
+private:
+    SkVector fOffset;
+
+    typedef SkSingleInputImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkPaintFlagsDrawFilter.h b/include/effects/SkPaintFlagsDrawFilter.h
index 66a43cc..cb2a8b7 100644
--- a/include/effects/SkPaintFlagsDrawFilter.h
+++ b/include/effects/SkPaintFlagsDrawFilter.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 The Android Open Source Project
  *
@@ -6,24 +5,20 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkPaintFlagsDrawFilter_DEFINED
 #define SkPaintFlagsDrawFilter_DEFINED
 
 #include "SkDrawFilter.h"
 
-class SkPaintFlagsDrawFilter : public SkDrawFilter {
+class SK_API SkPaintFlagsDrawFilter : public SkDrawFilter {
 public:
     SkPaintFlagsDrawFilter(uint32_t clearFlags, uint32_t setFlags);
-    
-    // overrides
-    virtual void filter(SkPaint*, Type);
-    
+
+    virtual bool filter(SkPaint*, Type) SK_OVERRIDE;
+
 private:
-    uint32_t    fPrevFlags;     // local cache for filter/restore
     uint16_t    fClearFlags;    // user specified
     uint16_t    fSetFlags;      // user specified
 };
 
 #endif
-
diff --git a/include/effects/SkPixelXorXfermode.h b/include/effects/SkPixelXorXfermode.h
index a7197ab..5411b12 100644
--- a/include/effects/SkPixelXorXfermode.h
+++ b/include/effects/SkPixelXorXfermode.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2007 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkPixelXorXfermode_DEFINED
 #define SkPixelXorXfermode_DEFINED
 
@@ -17,31 +15,23 @@
     this proc *always* returns an opaque color (alpha == 255). Thus it is
     not really usefull for operating on blended colors.
 */
-class SkPixelXorXfermode : public SkXfermode {
+class SK_API SkPixelXorXfermode : public SkXfermode {
 public:
     SkPixelXorXfermode(SkColor opColor) : fOpColor(opColor) {}
 
-    // override from SkFlattenable
-    virtual Factory getFactory();
-    virtual void flatten(SkFlattenableWriteBuffer&);
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkPixelXorXfermode, (buffer));
-    }
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPixelXorXfermode)
 
 protected:
+    SkPixelXorXfermode(SkFlattenableReadBuffer& rb);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
     // override from SkXfermode
-    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst);
+    virtual SkPMColor xferColor(SkPMColor src, SkPMColor dst) const;
 
 private:
     SkColor fOpColor;
 
-    SkPixelXorXfermode(SkFlattenableReadBuffer& rb);
-    // our private factory
-    static SkFlattenable* Create(SkFlattenableReadBuffer&);
-
     typedef SkXfermode INHERITED;
 };
 
diff --git a/include/effects/SkPorterDuff.h b/include/effects/SkPorterDuff.h
index 44d94f8..c5f5492 100644
--- a/include/effects/SkPorterDuff.h
+++ b/include/effects/SkPorterDuff.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkPorterDuff_DEFINED
 #define SkPorterDuff_DEFINED
 
@@ -17,7 +15,7 @@
 
 /** DEPRECATED - use SkXfermode::Mode instead
  */
-class SkPorterDuff {
+class SK_API SkPorterDuff {
 public:
     /** List of predefined xfermodes. In general, the algebra for the modes
         uses the following symbols:
@@ -42,7 +40,7 @@
         kXor_Mode,      //!< [Sa + Da - 2 * Sa * Da, Sc * (1 - Da) + (1 - Sa) * Dc]
         kDarken_Mode,   //!< [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + min(Sc, Dc)]
         kLighten_Mode,  //!< [Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)]
-        kMultiply_Mode, //!< [Sa * Da, Sc * Dc]
+        kModulate_Mode, //!< [Sa * Da, Sc * Dc] multiplies all components
         kScreen_Mode,   //!< [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc]
         kAdd_Mode,      //!< Saturate(S + D)
 #ifdef SK_BUILD_FOR_ANDROID
@@ -55,19 +53,19 @@
     /** Return an SkXfermode object for the specified mode.
     */
     static SkXfermode* CreateXfermode(Mode mode);
-    
+
     /** Return a function pointer to a routine that applies the specified
         porter-duff transfer mode.
     */
     static SkXfermodeProc GetXfermodeProc(Mode mode);
-    
+
     /** Return a function pointer to a routine that applies the specified
         porter-duff transfer mode and srcColor to a 16bit device color. Note,
         if the mode+srcColor might return a non-opaque color, then there is not
         16bit proc, and this will return NULL.
     */
     static SkXfermodeProc16 GetXfermodeProc16(Mode mode, SkColor srcColor);
-    
+
     /** If the specified xfermode advertises itself as one of the porterduff
         modes (via SkXfermode::Coeff), return true and if not null, set mode
         to the corresponding porterduff mode. If it is not recognized as a one,
@@ -81,4 +79,3 @@
 };
 
 #endif
-
diff --git a/include/effects/SkRectShape.h b/include/effects/SkRectShape.h
deleted file mode 100644
index 519398c..0000000
--- a/include/effects/SkRectShape.h
+++ /dev/null
@@ -1,65 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkRectShape_DEFINED
-#define SkRectShape_DEFINED
-
-#include "SkShape.h"
-#include "SkPaint.h"
-#include "SkSize.h"
-
-class SkPaintShape : public SkShape {
-public:
-    SkPaintShape();
-
-    SkPaint& paint() { return fPaint; }
-    const SkPaint& paint() const { return fPaint; }
-
-    // overrides
-    virtual void flatten(SkFlattenableWriteBuffer&);
-    
-protected:
-    SkPaintShape(SkFlattenableReadBuffer& buffer);
-    
-private:
-    SkPaint fPaint;
-    
-    typedef SkShape INHERITED;
-};
-
-class SkRectShape : public SkPaintShape {
-public:
-    SkRectShape();
-    
-    void setRect(const SkRect&);
-    void setOval(const SkRect&);
-    void setCircle(SkScalar x, SkScalar y, SkScalar radius);
-    void setRRect(const SkRect&, SkScalar rx, SkScalar ry);
-
-    // overrides
-    virtual Factory getFactory();
-    virtual void flatten(SkFlattenableWriteBuffer&);
-
-    // public for Registrar
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
-
-protected:
-    SkRectShape(SkFlattenableReadBuffer&);
-
-    // overrides
-    virtual void onDraw(SkCanvas*);
-
-private:
-    SkRect  fBounds;
-    SkSize  fRadii;
-
-    typedef SkPaintShape INHERITED;
-};
-
-#endif
diff --git a/include/effects/SkSingleInputImageFilter.h b/include/effects/SkSingleInputImageFilter.h
new file mode 100644
index 0000000..3df718f
--- /dev/null
+++ b/include/effects/SkSingleInputImageFilter.h
@@ -0,0 +1,37 @@
+/*
+ * 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 SkSingleInputImageFilter_DEFINED
+#define SkSingleInputImageFilter_DEFINED
+
+#include "SkImageFilter.h"
+
+class SkMatrix;
+struct SkIPoint;
+class GrTexture;
+
+class SK_API SkSingleInputImageFilter : public SkImageFilter {
+public:
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSingleInputImageFilter)
+
+protected:
+    explicit SkSingleInputImageFilter(SkImageFilter* input);
+    ~SkSingleInputImageFilter();
+    explicit SkSingleInputImageFilter(SkFlattenableReadBuffer& rb);
+    virtual void flatten(SkFlattenableWriteBuffer& wb) const SK_OVERRIDE;
+
+    // Recurses on input (if non-NULL), and returns the processed result,
+    // otherwise returns src.
+    SkBitmap getInputResult(Proxy*, const SkBitmap& src, const SkMatrix&,
+                            SkIPoint* offset);
+
+    SkImageFilter* input() const { return getInput(0); }
+private:
+    typedef SkImageFilter INHERITED;
+};
+
+#endif
diff --git a/include/effects/SkStippleMaskFilter.h b/include/effects/SkStippleMaskFilter.h
new file mode 100644
index 0000000..be03c45
--- /dev/null
+++ b/include/effects/SkStippleMaskFilter.h
@@ -0,0 +1,41 @@
+/*
+ * 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 SkStippleMaskFilter_DEFINED
+#define SkStippleMaskFilter_DEFINED
+
+#include "SkMaskFilter.h"
+
+/**
+ * Simple MaskFilter that creates a screen door stipple pattern.
+ */
+class SK_API SkStippleMaskFilter : public SkMaskFilter {
+public:
+    SkStippleMaskFilter() : INHERITED() {
+    }
+
+    virtual bool filterMask(SkMask* dst, const SkMask& src,
+                            const SkMatrix& matrix,
+                            SkIPoint* margin) const SK_OVERRIDE;
+
+    // getFormat is from SkMaskFilter
+    virtual SkMask::Format getFormat() const SK_OVERRIDE {
+        return SkMask::kA8_Format;
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkStippleMaskFilter);
+
+protected:
+    SkStippleMaskFilter(SkFlattenableReadBuffer& buffer)
+    : SkMaskFilter(buffer) {
+    }
+
+private:
+    typedef SkMaskFilter INHERITED;
+};
+
+#endif // SkStippleMaskFilter_DEFINED
diff --git a/include/effects/SkTableColorFilter.h b/include/effects/SkTableColorFilter.h
index b442197..5714d07 100644
--- a/include/effects/SkTableColorFilter.h
+++ b/include/effects/SkTableColorFilter.h
@@ -18,7 +18,7 @@
      *  the table is applied, and then the result is remultiplied.
      */
     static SkColorFilter* Create(const uint8_t table[256]);
-    
+
     /**
      *  Create a table colorfilter, with a different table for each
      *  component [A, R, G, B]. If a given table is NULL, then it is
@@ -29,6 +29,8 @@
                                      const uint8_t tableR[256],
                                      const uint8_t tableG[256],
                                      const uint8_t tableB[256]);
+
+    SK_DECLARE_FLATTENABLE_REGISTRAR_GROUP()
 };
 
 #endif
diff --git a/include/effects/SkTableMaskFilter.h b/include/effects/SkTableMaskFilter.h
index f213de7..feb3b13 100644
--- a/include/effects/SkTableMaskFilter.h
+++ b/include/effects/SkTableMaskFilter.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkTableMaskFilter_DEFINED
 #define SkTableMaskFilter_DEFINED
 
@@ -14,18 +12,16 @@
 #include "SkScalar.h"
 
 /** \class SkTableMaskFilter
- 
+
     Applies a table lookup on each of the alpha values in the mask.
     Helper methods create some common tables (e.g. gamma, clipping)
  */
-class SkTableMaskFilter : public SkMaskFilter {
+class SK_API SkTableMaskFilter : public SkMaskFilter {
 public:
     SkTableMaskFilter();
     SkTableMaskFilter(const uint8_t table[256]);
     virtual ~SkTableMaskFilter();
 
-    void setTable(const uint8_t table[256]);
-
     /** Utility that sets the gamma table
      */
     static void MakeGammaTable(uint8_t table[256], SkScalar gamma);
@@ -47,23 +43,20 @@
         return SkNEW_ARGS(SkTableMaskFilter, (table));
     }
 
-    // overrides from SkMaskFilter
-    virtual SkMask::Format getFormat();
-    virtual bool filterMask(SkMask*, const SkMask&, const SkMatrix&, SkIPoint*);
-    
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer& wb);
-    virtual Factory getFactory();
+    virtual SkMask::Format getFormat() const SK_OVERRIDE;
+    virtual bool filterMask(SkMask*, const SkMask&, const SkMatrix&,
+                            SkIPoint*) const SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTableMaskFilter)
 
 protected:
     SkTableMaskFilter(SkFlattenableReadBuffer& rb);
-    static SkFlattenable* Factory(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
 private:
     uint8_t fTable[256];
-    
+
     typedef SkMaskFilter INHERITED;
 };
 
 #endif
-
diff --git a/include/effects/SkTestImageFilters.h b/include/effects/SkTestImageFilters.h
index 55522c1..c38c037 100755
--- a/include/effects/SkTestImageFilters.h
+++ b/include/effects/SkTestImageFilters.h
@@ -1,155 +1,46 @@
-
 #ifndef _SkTestImageFilters_h
 #define _SkTestImageFilters_h
 
 #include "SkImageFilter.h"
-#include "SkColorFilter.h"
+#include "SkPoint.h"
 
-class SkOffsetImageFilter : public SkImageFilter {
+class SK_API SkComposeImageFilter : public SkImageFilter {
 public:
-    SkOffsetImageFilter(SkScalar dx, SkScalar dy) {
-        fOffset.set(dx, dy);
-    }
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkOffsetImageFilter, (buffer));
-    }
-
-protected:
-    SkOffsetImageFilter(SkFlattenableReadBuffer& buffer);
-
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
-                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
-    virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
-
-private:
-    SkVector fOffset;
-
-    typedef SkImageFilter INHERITED;
-};
-
-class SkComposeImageFilter : public SkImageFilter {
-public:
-    SkComposeImageFilter(SkImageFilter* outer, SkImageFilter* inner) {
-        fOuter = outer;
-        fInner = inner;
-        SkSafeRef(outer);
-        SkSafeRef(inner);
-    }
+    SkComposeImageFilter(SkImageFilter* outer, SkImageFilter* inner) : INHERITED(outer, inner) {}
     virtual ~SkComposeImageFilter();
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkComposeImageFilter, (buffer));
-    }
-    
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkComposeImageFilter)
+
 protected:
     SkComposeImageFilter(SkFlattenableReadBuffer& buffer);
-    
+
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
     virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
-    
+
 private:
-    SkImageFilter*  fOuter;
-    SkImageFilter*  fInner;
-    
-    typedef SkImageFilter INHERITED;
-};
-
-#include "SkXfermode.h"
-
-class SkMergeImageFilter : public SkImageFilter {
-public:
-    SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
-                       SkXfermode::Mode = SkXfermode::kSrcOver_Mode);
-    SkMergeImageFilter(SkImageFilter* const filters[], int count,
-                       const SkXfermode::Mode modes[] = NULL);
-    virtual ~SkMergeImageFilter();
-    
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkMergeImageFilter, (buffer));
-    }
-    
-protected:
-    SkMergeImageFilter(SkFlattenableReadBuffer& buffer);
-    
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
-                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
-    virtual bool onFilterBounds(const SkIRect&, const SkMatrix&, SkIRect*) SK_OVERRIDE;
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
-    
-private:
-    SkImageFilter**     fFilters;
-    uint8_t*            fModes; // SkXfermode::Mode
-    int                 fCount;
-
-    // private storage, to avoid dynamically allocating storage for our copy
-    // of the filters and modes (unless fCount is so large we can't fit).
-    intptr_t    fStorage[16];
-
-    void initAlloc(int count, bool hasModes);
-    void init(SkImageFilter* const [], int count, const SkXfermode::Mode []);
-    
-    typedef SkImageFilter INHERITED;
-};
-
-class SkColorFilterImageFilter : public SkImageFilter {
-public:
-    SkColorFilterImageFilter(SkColorFilter* cf) : fColorFilter(cf) {
-        SkSafeRef(cf);
-    }
-    virtual ~SkColorFilterImageFilter();
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkColorFilterImageFilter, (buffer));
-    }
-    
-protected:
-    SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer);
-    
-    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
-                               SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
-    
-private:
-    SkColorFilter*  fColorFilter;
-    
     typedef SkImageFilter INHERITED;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
 // Fun mode that scales down (only) and then scales back up to look pixelated
-class SkDownSampleImageFilter : public SkImageFilter {
+class SK_API SkDownSampleImageFilter : public SkImageFilter {
 public:
-    SkDownSampleImageFilter(SkScalar scale) : fScale(scale) {}
-    
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkDownSampleImageFilter, (buffer));
-    }
-    
+    SkDownSampleImageFilter(SkScalar scale) : INHERITED(0), fScale(scale) {}
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDownSampleImageFilter)
+
 protected:
     SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer);
-    
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
     virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
                                SkBitmap* result, SkIPoint* loc) SK_OVERRIDE;
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory()  SK_OVERRIDE;
-    
+
 private:
     SkScalar fScale;
-    
+
     typedef SkImageFilter INHERITED;
 };
 
diff --git a/include/effects/SkTransparentShader.h b/include/effects/SkTransparentShader.h
index e951bba..bee9a02 100644
--- a/include/effects/SkTransparentShader.h
+++ b/include/effects/SkTransparentShader.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,26 +5,24 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkTransparentShader_DEFINED
 #define SkTransparentShader_DEFINED
 
 #include "SkShader.h"
 
-class SkTransparentShader : public SkShader {
+class SK_API SkTransparentShader : public SkShader {
 public:
     SkTransparentShader() {}
 
     virtual uint32_t getFlags() SK_OVERRIDE;
-    virtual bool    setContext( const SkBitmap& device,
-                                const SkPaint& paint,
-                                const SkMatrix& matrix) SK_OVERRIDE;
+    virtual bool    setContext(const SkBitmap& device,
+                               const SkPaint& paint,
+                               const SkMatrix& matrix) SK_OVERRIDE;
     virtual void    shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
     virtual void    shadeSpan16(int x, int y, uint16_t span[], int count) SK_OVERRIDE;
 
-    // overrides for SkFlattenable
-    virtual Factory getFactory() SK_OVERRIDE;
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTransparentShader)
 
 private:
     // these are a cache from the call to setContext()
@@ -33,13 +30,8 @@
     uint8_t         fAlpha;
 
     SkTransparentShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
-    
-    static SkFlattenable* Create(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkTransparentShader, (buffer));
-    }
 
     typedef SkShader INHERITED;
 };
 
 #endif
-
diff --git a/include/gpu/GrAARectRenderer.h b/include/gpu/GrAARectRenderer.h
new file mode 100644
index 0000000..0cf7faa
--- /dev/null
+++ b/include/gpu/GrAARectRenderer.h
@@ -0,0 +1,65 @@
+/*
+ * 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 GrAARectRenderer_DEFINED
+#define GrAARectRenderer_DEFINED
+
+#include "GrRect.h"
+#include "GrRefCnt.h"
+
+class GrGpu;
+class GrDrawTarget;
+class GrIndexBuffer;
+
+/*
+ * This class wraps helper functions that draw AA rects (filled & stroked)
+ */
+class GrAARectRenderer : public GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrAARectRenderer)
+
+    GrAARectRenderer()
+    : fAAFillRectIndexBuffer(NULL)
+    , fAAStrokeRectIndexBuffer(NULL) {
+    }
+
+    void reset();
+
+    ~GrAARectRenderer() {
+        this->reset();
+    }
+
+    // TODO: potentialy fuse the fill & stroke methods and differentiate
+    // btween them by passing in strokeWidth (<0 means fill).
+
+    // TODO: Remove the useVertexCoverage boolean. Just use it all the time
+    // since we now have a coverage vertex attribute
+    void fillAARect(GrGpu* gpu,
+                    GrDrawTarget* target,
+                    const GrRect& devRect,
+                    bool useVertexCoverage);
+
+    void strokeAARect(GrGpu* gpu,
+                      GrDrawTarget* target,
+                      const GrRect& devRect,
+                      const GrVec& devStrokeSize,
+                      bool useVertexCoverage);
+
+private:
+    GrIndexBuffer*              fAAFillRectIndexBuffer;
+    GrIndexBuffer*              fAAStrokeRectIndexBuffer;
+
+    GrIndexBuffer* aaFillRectIndexBuffer(GrGpu* gpu);
+
+    static int aaStrokeRectIndexCount();
+    GrIndexBuffer* aaStrokeRectIndexBuffer(GrGpu* gpu);
+
+    typedef GrRefCnt INHERITED;
+};
+
+#endif // GrAARectRenderer_DEFINED
diff --git a/include/gpu/GrBackendEffectFactory.h b/include/gpu/GrBackendEffectFactory.h
new file mode 100644
index 0000000..a291387
--- /dev/null
+++ b/include/gpu/GrBackendEffectFactory.h
@@ -0,0 +1,86 @@
+/*
+ * 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 GrBackendEffectFactory_DEFINED
+#define GrBackendEffectFactory_DEFINED
+
+#include "GrTypes.h"
+#include "SkTemplates.h"
+#include "SkThread_platform.h"
+#include "GrNoncopyable.h"
+
+/** Given a GrEffect of a particular type, creates the corresponding graphics-backend-specific
+    effect object. Also tracks equivalence of shaders generated via a key. Each factory instance
+    is assigned a generation ID at construction. The ID of the return of GrEffect::getFactory()
+    is used as a type identifier. Thus a GrEffect subclass must return a singleton from
+    getFactory(). GrEffect subclasses should use the derived class GrTBackendEffectFactory that is
+    templated on the GrEffect subclass as their factory object. It requires that the GrEffect
+    subclass has a nested class (or typedef) GLEffect which is its GL implementation and a subclass
+    of GrGLEffect.
+ */
+
+class GrEffectRef;
+class GrEffectStage;
+class GrGLEffect;
+class GrGLCaps;
+
+class GrBackendEffectFactory : public GrNoncopyable {
+public:
+    typedef uint32_t EffectKey;
+    enum {
+        kNoEffectKey = 0,
+        kEffectKeyBits = 12,
+        /**
+         * Some aspects of the generated code may be determined by the particular textures that are
+         * associated with the effect. These manipulations are performed by GrGLShaderBuilder beyond
+         * GrGLEffects' control. So there is a dedicated part of the key which is combined
+         * automatically with the bits produced by GrGLEffect::GenKey().
+         */
+        kTextureKeyBits = 6
+    };
+
+    virtual EffectKey glEffectKey(const GrEffectStage&, const GrGLCaps&) const = 0;
+    virtual GrGLEffect* createGLInstance(const GrEffectRef&) const = 0;
+
+    bool operator ==(const GrBackendEffectFactory& b) const {
+        return fEffectClassID == b.fEffectClassID;
+    }
+    bool operator !=(const GrBackendEffectFactory& b) const {
+        return !(*this == b);
+    }
+
+    virtual const char* name() const = 0;
+
+protected:
+    enum {
+        kIllegalEffectClassID = 0,
+    };
+
+    GrBackendEffectFactory() {
+        fEffectClassID = kIllegalEffectClassID;
+    }
+    virtual ~GrBackendEffectFactory() {}
+
+    static EffectKey GenID() {
+        GR_DEBUGCODE(static const int32_t kClassIDBits = 8 * sizeof(EffectKey) -
+                                            kTextureKeyBits -
+                                    kEffectKeyBits);
+        // fCurrEffectClassID has been initialized to kIllegalEffectClassID. The
+        // atomic inc returns the old value not the incremented value. So we add
+        // 1 to the returned value.
+        int32_t id = sk_atomic_inc(&fCurrEffectClassID) + 1;
+        GrAssert(id < (1 << kClassIDBits));
+        return static_cast<EffectKey>(id);
+    }
+
+    EffectKey fEffectClassID;
+
+private:
+    static int32_t fCurrEffectClassID;
+};
+
+#endif
diff --git a/include/gpu/GrClip.h b/include/gpu/GrClip.h
deleted file mode 100644
index d86eb97..0000000
--- a/include/gpu/GrClip.h
+++ /dev/null
@@ -1,141 +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 GrClip_DEFINED
-#define GrClip_DEFINED
-
-#include "GrClipIterator.h"
-#include "GrRect.h"
-#include "GrPath.h"
-#include "GrTemplates.h"
-
-#include "SkTArray.h"
-
-class GrClip {
-public:
-    GrClip();
-    GrClip(const GrClip& src);
-    /**
-     *  If specified, the conservativeBounds parameter already takes (tx,ty)
-     *  into account.
-     */
-    GrClip(GrClipIterator* iter, GrScalar tx, GrScalar ty,
-           const GrRect* conservativeBounds = NULL);
-    GrClip(const GrIRect& rect);
-    GrClip(const GrRect& rect);
-
-    ~GrClip();
-
-    GrClip& operator=(const GrClip& src);
-
-    bool hasConservativeBounds() const { return fConservativeBoundsValid; }
-
-    const GrRect& getConservativeBounds() const { return fConservativeBounds; }
-
-    int getElementCount() const { return fList.count(); }
-
-    GrClipType getElementType(int i) const { return fList[i].fType; }
-
-    const GrPath& getPath(int i) const {
-        GrAssert(kPath_ClipType == fList[i].fType);
-        return fList[i].fPath;
-    }
-
-    GrPathFill getPathFill(int i) const {
-        GrAssert(kPath_ClipType == fList[i].fType);
-        return fList[i].fPathFill;
-    }
-
-    const GrRect& getRect(int i) const {
-        GrAssert(kRect_ClipType == fList[i].fType);
-        return fList[i].fRect;
-    }
-
-    GrSetOp getOp(int i) const { return fList[i].fOp; }
-
-    bool isRect() const {
-        if (1 == fList.count() && kRect_ClipType == fList[0].fType && 
-            (kIntersect_SetOp == fList[0].fOp ||
-             kReplace_SetOp == fList[0].fOp)) {
-            // if we determined that the clip is a single rect
-            // we ought to have also used that rect as the bounds.
-            GrAssert(fConservativeBoundsValid);
-            GrAssert(fConservativeBounds == fList[0].fRect);
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    bool isEmpty() const { return 0 == fList.count(); }
-
-    /**
-     *  Resets this clip to be empty
-     */
-    void setEmpty();
-
-    /**
-     *  If specified, the bounds parameter already takes (tx,ty) into account.
-     */
-    void setFromIterator(GrClipIterator* iter, GrScalar tx, GrScalar ty,
-                         const GrRect* conservativeBounds = NULL);
-    void setFromRect(const GrRect& rect);
-    void setFromIRect(const GrIRect& rect);
-
-    friend bool operator==(const GrClip& a, const GrClip& b) {
-        if (a.fList.count() != b.fList.count()) {
-            return false;
-        }
-        int count = a.fList.count();
-        for (int i = 0; i < count; ++i) {
-            if (a.fList[i] != b.fList[i]) {
-                return false;
-            }
-        }
-        return true;
-    }
-    friend bool operator!=(const GrClip& a, const GrClip& b) {
-        return !(a == b);
-    }
-
-private:
-    struct Element {
-        GrClipType  fType;
-        GrRect      fRect;
-        GrPath      fPath;
-        GrPathFill  fPathFill;
-        GrSetOp     fOp;
-        bool operator ==(const Element& e) const {
-            if (e.fType != fType || e.fOp != fOp) {
-                return false;
-            }
-            switch (fType) {
-                case kRect_ClipType:
-                    return fRect == e.fRect;
-                case kPath_ClipType:
-                    return fPath == e.fPath;
-                default:
-                    GrCrash("Unknown clip element type.");
-                    return false; // suppress warning
-            }
-        }
-        bool operator !=(const Element& e) const { return !(*this == e); }
-    };
-
-    GrRect              fConservativeBounds;
-    bool                fConservativeBoundsValid;
-
-    enum {
-        kPreAllocElements = 4,
-    };
-    SkSTArray<kPreAllocElements, Element>   fList;
-};
-#endif
-
diff --git a/include/gpu/GrClipData.h b/include/gpu/GrClipData.h
new file mode 100644
index 0000000..078b61e
--- /dev/null
+++ b/include/gpu/GrClipData.h
@@ -0,0 +1,57 @@
+
+/*
+ * 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 GrClip_DEFINED
+#define GrClip_DEFINED
+
+#include "GrRect.h"
+#include "SkClipStack.h"
+
+class GrSurface;
+
+/**
+ * GrClipData encapsulates the information required to construct the clip
+ * masks. 'fOrigin' is only non-zero when saveLayer has been called
+ * with an offset bounding box. The clips in 'fClipStack' are in
+ * device coordinates (i.e., they have been translated by -fOrigin w.r.t.
+ * the canvas' device coordinates).
+ */
+class GrClipData : public SkNoncopyable {
+public:
+    const SkClipStack*  fClipStack;
+    SkIPoint            fOrigin;
+
+    GrClipData()
+        : fClipStack(NULL) {
+        fOrigin.setZero();
+    }
+
+    bool operator==(const GrClipData& other) const {
+        if (fOrigin != other.fOrigin) {
+            return false;
+        }
+
+        if (NULL != fClipStack && NULL != other.fClipStack) {
+            return *fClipStack == *other.fClipStack;
+        }
+
+        return fClipStack == other.fClipStack;
+    }
+
+    bool operator!=(const GrClipData& other) const {
+        return !(*this == other);
+    }
+
+    void getConservativeBounds(const GrSurface* surface,
+                               GrIRect* devResult,
+                               bool* isIntersectionOfRects = NULL) const;
+};
+
+#endif
diff --git a/include/gpu/GrClipIterator.h b/include/gpu/GrClipIterator.h
deleted file mode 100644
index 4a5cc71..0000000
--- a/include/gpu/GrClipIterator.h
+++ /dev/null
@@ -1,80 +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 GrClipIterator_DEFINED
-#define GrClipIterator_DEFINED
-
-#include "GrPath.h"
-#include "GrRect.h"
-
-/**
- * A clip is a list of paths and/or rects with set operations to combine them.
- */
-class GrClipIterator {
-public:
-    virtual ~GrClipIterator() {}
-
-    /**
-     *  Returns true if there are no more rects to process
-     */
-    virtual bool isDone() const = 0;
-
-    /**
-     *  Rewind the iterator to replay the set of clip elements again
-     */
-    virtual void rewind() = 0;
-
-    /**
-     * Get the type of the current clip element
-     */
-    virtual GrClipType getType() const = 0;
-
-    /**
-     * Return the current path. It is an error to call this when isDone() is
-     * true or when getType() is kRect_Type.
-     */
-    virtual const GrPath* getPath() = 0;
-
-    /**
-     * Return the fill rule for the path. It is an error to call this when
-     * isDone() is true or when getType is kRect_Type.
-     */
-    virtual GrPathFill getPathFill() const = 0;
-
-    /**
-    * Return the current rect. It is an error to call this when isDone is true
-    * or when getType() is kPath_Type.
-    */
-    virtual void getRect(GrRect* rect) const = 0;
-
-    /**
-     * Gets the operation used to apply the current item to previously iterated
-     * items. Iterators should not produce a Replace op.
-     */
-    virtual GrSetOp getOp() const = 0;
-
-    /**
-     *  Call to move to the next element in the list, previous path iter can be
-     *  made invalid.
-     */
-    virtual void next() = 0;
-};
-
-/**
- *  Call to rewind iter, first checking to see if iter is NULL
- */
-static inline void GrSafeRewind(GrClipIterator* iter) {
-    if (iter) {
-        iter->rewind();
-    }
-}
-
-#endif
-
diff --git a/include/gpu/GrColor.h b/include/gpu/GrColor.h
index ed666de..af12ac6 100644
--- a/include/gpu/GrColor.h
+++ b/include/gpu/GrColor.h
@@ -58,5 +58,13 @@
  */
 #define GrColor_ILLEGAL     (~(0xFF << GrColor_SHIFT_A))
 
-#endif
+/** Converts a GrColor to an rgba array of GrGLfloat */
+static inline void GrColorToRGBAFloat(GrColor color, float rgba[4]) {
+    static const float ONE_OVER_255 = 1.f / 255.f;
+    rgba[0] = GrColorUnpackR(color) * ONE_OVER_255;
+    rgba[1] = GrColorUnpackG(color) * ONE_OVER_255;
+    rgba[2] = GrColorUnpackB(color) * ONE_OVER_255;
+    rgba[3] = GrColorUnpackA(color) * ONE_OVER_255;
+}
 
+#endif
diff --git a/include/gpu/GrConfig.h b/include/gpu/GrConfig.h
index 1dfe199..d423a2a 100644
--- a/include/gpu/GrConfig.h
+++ b/include/gpu/GrConfig.h
@@ -11,6 +11,8 @@
 #ifndef GrConfig_DEFINED
 #define GrConfig_DEFINED
 
+#include "SkTypes.h"
+
 ///////////////////////////////////////////////////////////////////////////////
 // preconfig section:
 //
@@ -48,6 +50,9 @@
 #if !defined(GR_QNX_BUILD)
     #define GR_QNX_BUILD        0
 #endif
+#if !defined(GR_CACHE_STATS)
+    #define GR_CACHE_STATS      0
+#endif
 
 /**
  *  If no build target has been defined, attempt to infer.
@@ -118,11 +123,15 @@
 #else
 /*
  *  Include stdint.h with defines that trigger declaration of C99 limit/const
- *  macros here before anyone else has a chance to include stdint.h without 
+ *  macros here before anyone else has a chance to include stdint.h without
  *  these.
  */
+#ifndef __STDC_LIMIT_MACROS
 #define __STDC_LIMIT_MACROS
+#endif
+#ifndef __STDC_CONSTANT_MACROS
 #define __STDC_CONSTANT_MACROS
+#endif
 #include <stdint.h>
 #endif
 
@@ -134,7 +143,7 @@
  *  GR_USER_CONFIG_FILE. It should be defined relative to GrConfig.h
  *
  *  e.g. it can specify GR_DEBUG/GR_RELEASE as it please, change the BUILD
- *  target, or supply its own defines for anything else (e.g. GR_SCALAR)
+ *  target, or supply its own defines for anything else (e.g. GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT)
  */
 #if !defined(GR_USER_CONFIG_FILE)
     #include "GrUserConfig.h"
@@ -182,7 +191,7 @@
 // debug -vs- release
 //
 
-extern GR_API void GrPrintf(const char format[], ...);
+#define GrPrintf SkDebugf
 
 /**
  *  GR_STRING makes a string of X where X is expanded before conversion to a string
@@ -306,13 +315,6 @@
     #endif
 #endif
 
-#if !defined(GR_SCALAR_IS_FLOAT)
-    #define GR_SCALAR_IS_FLOAT   0
-#endif
-#if !defined(GR_SCALAR_IS_FIXED)
-    #define GR_SCALAR_IS_FIXED   0
-#endif
-
 #if !defined(GR_TEXT_SCALAR_TYPE_IS_USHORT)
     #define GR_TEXT_SCALAR_TYPE_IS_USHORT  0
 #endif
@@ -328,14 +330,6 @@
 #endif
 
 /**
- *  GR_COLLECT_STATS controls whether the GrGpu class collects stats.
- *  If not already defined then collect in debug build but not release.
- */
-#if !defined(GR_COLLECT_STATS)
-    #define GR_COLLECT_STATS GR_DEBUG
-#endif
-
-/**
  *  GR_STATIC_RECT_VB controls whether rects are drawn by issuing a vertex
  *  for each corner or using a static vb that is positioned by modifying the
  *  view / texture matrix.
@@ -345,6 +339,14 @@
 #endif
 
 /**
+ *  GR_DISABLE_DRAW_BUFFERING prevents GrContext from queueing draws in a
+ *  GrInOrderDrawBuffer.
+ */
+#if !defined(GR_DISABLE_DRAW_BUFFERING)
+    #define GR_DISABLE_DRAW_BUFFERING 0
+#endif
+
+/**
  *  GR_AGGRESSIVE_SHADER_OPTS controls how aggressively shaders are optimized
  *  for special cases. On systems where program changes are expensive this
  *  may not be advantageous. Consecutive draws may no longer use the same
@@ -364,6 +366,32 @@
     #define GR_GEOM_BUFFER_LOCK_THRESHOLD (1 << 15)
 #endif
 
+/**
+ * GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT gives a threshold (in megabytes) for the
+ * maximum size of the texture cache in vram. The value is only a default and
+ * can be overridden at runtime.
+ */
+#if !defined(GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT)
+    #define GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT 96
+#endif
+
+/**
+ * GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE is for compatibility with the new version
+ * of the OpenGLES2.0 headers from Khronos.  glShaderSource now takes a const char * const *,
+ * instead of a const char **.
+ */
+#if !defined(GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE)
+    #define GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE 0
+#endif
+
+/**
+ * GR_STROKE_PATH_RENDERING controls whether or not the GrStrokePathRenderer can be selected
+ * as a path renderer. GrStrokePathRenderer is currently an experimental path renderer.
+ */
+#if !defined(GR_STROKE_PATH_RENDERING)
+    #define GR_STROKE_PATH_RENDERING                 0
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 // tail section:
 //
@@ -383,12 +411,6 @@
 #endif
 
 
-#if !GR_SCALAR_IS_FLOAT && !GR_SCALAR_IS_FIXED
-    #undef  GR_SCALAR_IS_FLOAT
-    #define GR_SCALAR_IS_FLOAT              1
-    #pragma message GR_WARN("Scalar type not defined, defaulting to float")
-#endif
-
 #if !GR_TEXT_SCALAR_IS_FLOAT && \
     !GR_TEXT_SCALAR_IS_FIXED && \
     !GR_TEXT_SCALAR_IS_USHORT
@@ -419,4 +441,3 @@
 #endif
 
 #endif
-
diff --git a/include/gpu/GrContext.h b/include/gpu/GrContext.h
index 37160b1..dc69766 100644
--- a/include/gpu/GrContext.h
+++ b/include/gpu/GrContext.h
@@ -10,34 +10,50 @@
 #ifndef GrContext_DEFINED
 #define GrContext_DEFINED
 
-#include "GrClip.h"
+#include "GrColor.h"
+#include "GrAARectRenderer.h"
+#include "GrClipData.h"
+#include "SkMatrix.h"
 #include "GrPaint.h"
+#include "GrPathRendererChain.h"
 // not strictly needed but requires WK change in LayerTextureUpdaterCanvas to
 // remove.
-#include "GrRenderTarget.h" 
+#include "GrRenderTarget.h"
+#include "GrRefCnt.h"
+#include "GrTexture.h"
 
+class GrAutoScratchTexture;
+class GrDrawState;
 class GrDrawTarget;
+class GrEffect;
 class GrFontCache;
 class GrGpu;
-struct GrGpuStats;
 class GrIndexBuffer;
 class GrIndexBufferAllocPool;
 class GrInOrderDrawBuffer;
 class GrPathRenderer;
-class GrPathRendererChain;
 class GrResourceEntry;
 class GrResourceCache;
 class GrStencilBuffer;
+class GrTextureParams;
 class GrVertexBuffer;
 class GrVertexBufferAllocPool;
+class GrSoftwarePathRenderer;
+class SkStrokeRec;
 
 class GR_API GrContext : public GrRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(GrContext)
+
     /**
-     * Creates a GrContext from within a 3D context.
+     * Creates a GrContext for a backend context.
      */
-    static GrContext* Create(GrEngine engine,
-                             GrPlatform3DContext context3D);
+    static GrContext* Create(GrBackend, GrBackendContext);
+
+    /**
+     * Returns the number of GrContext instances for the current thread.
+     */
+    static int GetThreadInstanceCount();
 
     virtual ~GrContext();
 
@@ -50,7 +66,25 @@
     void resetContext();
 
     /**
-     * Abandons all gpu resources, assumes 3D API state is unknown. Call this
+     * Callback function to allow classes to cleanup on GrContext destruction.
+     * The 'info' field is filled in with the 'info' passed to addCleanUp.
+     */
+    typedef void (*PFCleanUpFunc)(const GrContext* context, void* info);
+
+    /**
+     * Add a function to be called from within GrContext's destructor.
+     * This gives classes a chance to free resources held on a per context basis.
+     * The 'info' parameter will be stored and passed to the callback function.
+     */
+    void addCleanUp(PFCleanUpFunc cleanUp, void* info) {
+        CleanUpData* entry = fCleanUpData.push();
+
+        entry->fFunc = cleanUp;
+        entry->fInfo = info;
+    }
+
+    /**
+     * Abandons all GPU resources, assumes 3D API state is unknown. Call this
      * if you have lost the associated GPU context, and thus internal texture,
      * buffer, etc. references/IDs are now invalid. Should be called even when
      * GrContext is no longer going to be used for two reasons:
@@ -70,7 +104,7 @@
     void contextDestroyed();
 
     /**
-     * Frees gpu created by the context. Can be called to reduce GPU memory
+     * Frees GPU created by the context. Can be called to reduce GPU memory
      * pressure.
      */
     void freeGpuResources();
@@ -84,90 +118,46 @@
     // Textures
 
     /**
-     * Token that refers to an entry in the texture cache. Returned by
-     * functions that lock textures. Passed to unlockTexture.
-     */
-    class SK_API TextureCacheEntry {
-    public:
-        TextureCacheEntry() : fEntry(NULL) {}
-        TextureCacheEntry(const TextureCacheEntry& e) : fEntry(e.fEntry) {}
-        TextureCacheEntry& operator= (const TextureCacheEntry& e) {
-            fEntry = e.fEntry;
-            return *this;
-        }
-        GrTexture* texture() const;
-        void reset() { fEntry = NULL; }
-    private:
-        explicit TextureCacheEntry(GrResourceEntry* entry) { fEntry = entry; }
-        void set(GrResourceEntry* entry) { fEntry = entry; }
-        GrResourceEntry* cacheEntry() { return fEntry; }
-        GrResourceEntry* fEntry;
-        friend class GrContext;
-    };
-
-    /**
-     * Key generated by client. Should be a unique key on the texture data.
-     * Does not need to consider that width and height of the texture. Two
-     * textures with the same TextureKey but different bounds will not collide.
-     */
-    typedef uint64_t TextureKey;
-
-    /**
-     *  Create a new entry, based on the specified key and texture, and return
-     *  its "locked" entry. Must call be balanced with an unlockTexture() call.
+     * Creates a new entry, based on the specified key and texture and returns it. The caller owns a
+     * ref on the returned texture which must be balanced by a call to unref.
      *
-     *  @param key      A client-generated key that identifies the contents
-     *                  of the texture. Respecified to findAndLockTexture
-     *                  for subsequent uses of the texture.
-     *  @param sampler  The sampler state used to draw a texture may be used
-     *                  to determine how to store the pixel data in the texture
-     *                  cache. (e.g. different versions may exist for different
-     *                  wrap modes on GPUs with limited or no NPOT texture
-     *                  support). Only the wrap and filter fields are used. NULL
-     *                  implies clamp wrap modes and nearest filtering.
+     * @param params    The texture params used to draw a texture may help determine
+     *                  the cache entry used. (e.g. different versions may exist
+     *                  for different wrap modes on GPUs with limited NPOT
+     *                  texture support). NULL implies clamp wrap modes.
      * @param desc      Description of the texture properties.
+     * @param cacheID Cache-specific properties (e.g., texture gen ID)
      * @param srcData   Pointer to the pixel values.
      * @param rowBytes  The number of bytes between rows of the texture. Zero
      *                  implies tightly packed rows.
      */
-    TextureCacheEntry createAndLockTexture(TextureKey key,
-                                           const GrSamplerState* sampler,
-                                           const GrTextureDesc& desc,
-                                           void* srcData, size_t rowBytes);
+    GrTexture* createTexture(const GrTextureParams* params,
+                             const GrTextureDesc& desc,
+                             const GrCacheID& cacheID,
+                             void* srcData, size_t rowBytes);
 
     /**
-     *  Search for an entry based on key and dimensions. If found, "lock" it and
-     *  return it. The entry's texture() function will return NULL if not found.
-     *  Must be balanced with an unlockTexture() call.
+     * Search for an entry based on key and dimensions. If found, ref it and return it. The return
+     * value will be NULL if not found. The caller must balance with a call to unref.
      *
-     *  @param key      A client-generated key that identifies the contents
-     *                  of the texture.
-     *  @param width    The width of the texture in pixels as specifed in
-     *                  the GrTextureDesc originally passed to
-     *                  createAndLockTexture
-     *  @param width    The height of the texture in pixels as specifed in
-     *                  the GrTextureDesc originally passed to
-     *                  createAndLockTexture
-     *  @param sampler  The sampler state used to draw a texture may be used
-     *                  to determine the cache entry used. (e.g. different
-     *                  versions may exist for different wrap modes on GPUs with
-     *                  limited or no NPOT texture support). Only the wrap and 
-     *                  filter fields are used. NULL implies clamp wrap modes
-     *                  and nearest filtering.
+     *  @param desc     Description of the texture properties.
+     *  @param cacheID Cache-specific properties (e.g., texture gen ID)
+     *  @param params   The texture params used to draw a texture may help determine
+     *                  the cache entry used. (e.g. different versions may exist
+     *                  for different wrap modes on GPUs with limited NPOT
+     *                  texture support). NULL implies clamp wrap modes.
      */
-    TextureCacheEntry findAndLockTexture(TextureKey key,
-                                         int width,
-                                         int height,
-                                         const GrSamplerState* sampler);
+    GrTexture* findAndRefTexture(const GrTextureDesc& desc,
+                                 const GrCacheID& cacheID,
+                                 const GrTextureParams* params);
     /**
      * Determines whether a texture is in the cache. If the texture is found it
      * will not be locked or returned. This call does not affect the priority of
      * the texture for deletion.
      */
-    bool isTextureInCache(TextureKey key,
-                          int width,
-                          int height,
-                          const GrSamplerState*) const;
+    bool isTextureInCache(const GrTextureDesc& desc,
+                          const GrCacheID& cacheID,
+                          const GrTextureParams* params) const;
 
     /**
      * Enum that determines how closely a returned scratch texture must match
@@ -193,34 +183,47 @@
      * Returns a texture matching the desc. It's contents are unknown. Subsequent
      * requests with the same descriptor are not guaranteed to return the same
      * texture. The same texture is guaranteed not be returned again until it is
-     * unlocked. Must call be balanced with an unlockTexture() call.
+     * unlocked. Call must be balanced with an unlockTexture() call. The caller
+     * owns a ref on the returned texture and must balance with a call to unref.
      *
      * Textures created by createAndLockTexture() hide the complications of
-     * tiling non-power-of-two textures on APIs that don't support this (e.g. 
-     * unextended GLES2). Tiling a npot texture created by lockScratchTexture on
+     * tiling non-power-of-two textures on APIs that don't support this (e.g.
+     * unextended GLES2). Tiling a NPOT texture created by lockScratchTexture on
      * such an API will create gaps in the tiling pattern. This includes clamp
      * mode. (This may be addressed in a future update.)
      */
-    TextureCacheEntry lockScratchTexture(const GrTextureDesc& desc, ScratchTexMatch match);
+    GrTexture* lockAndRefScratchTexture(const GrTextureDesc&, ScratchTexMatch match);
 
     /**
-     *  When done with an entry, call unlockTexture(entry) on it, which returns
-     *  it to the cache, where it may be purged.
+     *  When done with an entry, call unlockScratchTexture(entry) on it, which returns
+     *  it to the cache, where it may be purged. This does not unref the texture.
      */
-    void unlockTexture(TextureCacheEntry entry);
+    void unlockScratchTexture(GrTexture* texture);
+
+    /**
+     * This method should be called whenever a GrTexture is unreffed or
+     * switched from exclusive to non-exclusive. This
+     * gives the resource cache a chance to discard unneeded textures.
+     * Note: this entry point will be removed once totally ref-driven
+     * cache maintenance is implemented
+     */
+    void purgeCache();
 
     /**
      * Creates a texture that is outside the cache. Does not count against
      * cache's budget.
      */
-    GrTexture* createUncachedTexture(const GrTextureDesc&,
+    GrTexture* createUncachedTexture(const GrTextureDesc& desc,
                                      void* srcData,
                                      size_t rowBytes);
 
     /**
-     *  Returns true if the specified use of an indexed texture is supported.
+     * Returns true if the specified use of an indexed texture is supported.
+     * Support may depend upon whether the texture params indicate that the
+     * texture will be tiled. Passing NULL for the texture params indicates
+     * clamp mode.
      */
-    bool supportsIndex8PixelConfig(const GrSamplerState*,
+    bool supportsIndex8PixelConfig(const GrTextureParams*,
                                    int width,
                                    int height) const;
 
@@ -246,13 +249,13 @@
     void setTextureCacheLimits(int maxTextures, size_t maxTextureBytes);
 
     /**
-     *  Return the max width or height of a texture supported by the current gpu
+     *  Return the max width or height of a texture supported by the current GPU.
      */
     int getMaxTextureSize() const;
 
     /**
-     * Return the max width or height of a render target supported by the 
-     * current gpu
+     * Return the max width or height of a render target supported by the
+     * current GPU.
      */
     int getMaxRenderTargetSize() const;
 
@@ -272,8 +275,15 @@
     const GrRenderTarget* getRenderTarget() const;
     GrRenderTarget* getRenderTarget();
 
+    GrAARectRenderer* getAARectRenderer() { return fAARectRenderer; }
+
+    /**
+     * Can the provided configuration act as a color render target?
+     */
+    bool isConfigRenderable(GrPixelConfig config) const;
+
     ///////////////////////////////////////////////////////////////////////////
-    // Platform Surfaces
+    // Backend Surfaces
 
     /**
      * Wraps an existing texture with a GrTexture object.
@@ -285,11 +295,11 @@
      *
      * @return GrTexture object or NULL on failure.
      */
-    GrTexture* createPlatformTexture(const GrPlatformTextureDesc& desc);
+    GrTexture* wrapBackendTexture(const GrBackendTextureDesc& desc);
 
     /**
      * Wraps an existing render target with a GrRenderTarget object. It is
-     * similar to createPlatformTexture but can be used to draw into surfaces
+     * 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).
      *
@@ -297,8 +307,7 @@
      *
      * @return GrTexture object or NULL on failure.
      */
-     GrRenderTarget* createPlatformRenderTarget(
-                                    const GrPlatformRenderTargetDesc& desc);
+     GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc);
 
     ///////////////////////////////////////////////////////////////////////////
     // Matrix state
@@ -307,20 +316,25 @@
      * Gets the current transformation matrix.
      * @return the current matrix.
      */
-    const GrMatrix& getMatrix() const;
+    const SkMatrix& getMatrix() const;
 
     /**
      * Sets the transformation matrix.
      * @param m the matrix to set.
      */
-    void setMatrix(const GrMatrix& m);
+    void setMatrix(const SkMatrix& m);
+
+    /**
+     * Sets the current transformation matrix to identity.
+     */
+    void setIdentityMatrix();
 
     /**
      * Concats the current matrix. The passed matrix is applied before the
      * current matrix.
      * @param m the matrix to concat.
      */
-    void concatMatrix(const GrMatrix& m) const;
+    void concatMatrix(const SkMatrix& m) const;
 
 
     ///////////////////////////////////////////////////////////////////////////
@@ -329,19 +343,13 @@
      * Gets the current clip.
      * @return the current clip.
      */
-    const GrClip& getClip() const;
+    const GrClipData* getClip() const;
 
     /**
      * Sets the clip.
-     * @param clip  the clip to set.
+     * @param clipData  the clip to set.
      */
-    void setClip(const GrClip& clip);
-
-    /**
-     * Convenience method for setting the clip to a rect.
-     * @param rect  the rect to set as the new clip.
-     */
-    void setClip(const GrIRect& rect);
+    void setClip(const GrClipData* clipData);
 
     ///////////////////////////////////////////////////////////////////////////
     // Draws
@@ -350,8 +358,11 @@
      * Clear the entire or rect of the render target, ignoring any clips.
      * @param rect  the rect to clear or the whole thing if rect is NULL.
      * @param color the color to clear to.
+     * @param target if non-NULL, the render target to clear otherwise clear
+     *               the current render target
      */
-    void clear(const GrIRect* rect, GrColor color);
+    void clear(const GrIRect* rect, GrColor color,
+               GrRenderTarget* target = NULL);
 
     /**
      *  Draw everywhere (respecting the clip) with the paint.
@@ -371,8 +382,8 @@
      */
     void drawRect(const GrPaint& paint,
                   const GrRect&,
-                  GrScalar strokeWidth = -1,
-                  const GrMatrix* matrix = NULL);
+                  SkScalar strokeWidth = -1,
+                  const SkMatrix* matrix = NULL);
 
     /**
      * Maps a rect of paint coordinates onto the a rect of destination
@@ -392,20 +403,17 @@
     void drawRectToRect(const GrPaint& paint,
                         const GrRect& dstRect,
                         const GrRect& srcRect,
-                        const GrMatrix* dstMatrix = NULL,
-                        const GrMatrix* srcMatrix = NULL);
+                        const SkMatrix* dstMatrix = NULL,
+                        const SkMatrix* srcMatrix = NULL);
 
     /**
      * Draws a path.
      *
      * @param paint         describes how to color pixels.
      * @param path          the path to draw
-     * @param fill          the path filling rule to use.
-     * @param translate     optional additional translation applied to the
-     *                      path.
+     * @param stroke        the stroke information (width, join, cap)
      */
-    void drawPath(const GrPaint& paint, const GrPath& path, GrPathFill fill,
-                  const GrPoint* translate = NULL);
+    void drawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke);
 
     /**
      * Draws vertices with a paint.
@@ -432,6 +440,17 @@
                       const uint16_t indices[],
                       int indexCount);
 
+    /**
+     * Draws an oval.
+     *
+     * @param paint         describes how to color pixels.
+     * @param oval          the bounding rect of the oval.
+     * @param stroke        the stroke information (width, style)
+     */
+    void drawOval(const GrPaint& paint,
+                  const GrRect& oval,
+                  const SkStrokeRec& stroke);
+
     ///////////////////////////////////////////////////////////////////////////
     // Misc.
 
@@ -464,54 +483,60 @@
      */
     void flush(int flagsBitfield = 0);
 
+   /**
+    * These flags can be used with the read/write pixels functions below.
+    */
+    enum PixelOpsFlags {
+        /** The GrContext will not be flushed. This means that the read or write may occur before
+            previous draws have executed. */
+        kDontFlush_PixelOpsFlag = 0x1,
+        /** The src for write or dst read is unpremultiplied. This is only respected if both the
+            config src and dst configs are an RGBA/BGRA 8888 format. */
+        kUnpremul_PixelOpsFlag  = 0x2,
+    };
+
     /**
      * Reads a rectangle of pixels from a render target.
-     * @param target        the render target to read from. NULL means the
-     *                      current render target.
+     * @param target        the render target to read from. NULL means the current render target.
      * @param left          left edge of the rectangle to read (inclusive)
      * @param top           top edge of the rectangle to read (inclusive)
      * @param width         width of rectangle to read in pixels.
      * @param height        height of rectangle to read in pixels.
      * @param config        the pixel config of the destination buffer
      * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
-     *                      means rows are tightly packed.
+     * @param rowBytes      number of bytes bewtween consecutive rows. Zero means rows are tightly
+     *                      packed.
+     * @param pixelOpsFlags see PixelOpsFlags enum above.
      *
-     * @return true if the read succeeded, false if not. The read can fail
-     *              because of an unsupported pixel config or because no render
-     *              target is currently set.
+     * @return true if the read succeeded, false if not. The read can fail because of an unsupported
+     *         pixel config or because no render target is currently set and NULL was passed for
+     *         target.
      */
     bool readRenderTargetPixels(GrRenderTarget* target,
                                 int left, int top, int width, int height,
-                                GrPixelConfig config, void* buffer, 
-                                size_t rowBytes) {
-        return this->internalReadRenderTargetPixels(target, left, top,
-                                                    width, height,
-                                                    config, buffer,
-                                                    rowBytes, 0);
-    }
+                                GrPixelConfig config, void* buffer,
+                                size_t rowBytes = 0,
+                                uint32_t pixelOpsFlags = 0);
 
     /**
-     * Copy the src pixels [buffer, rowbytes, pixelconfig] into a render target
-     * at the specified rectangle.
-     * @param target        the render target to write into. NULL means the
-     *                      current render target.
+     * Copy the src pixels [buffer, row bytes, pixel config] into a render target at the specified
+     * rectangle.
+     * @param target        the render target to write into. NULL means the current render target.
      * @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 the rectangle from.
-     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
-     *                      means rows are tightly packed.
+     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
+     *                      packed.
+     * @param pixelOpsFlags see PixelOpsFlags enum above.
      */
     void writeRenderTargetPixels(GrRenderTarget* target,
                                  int left, int top, int width, int height,
                                  GrPixelConfig config, const void* buffer,
-                                 size_t rowBytes) {
-        this->internalWriteRenderTargetPixels(target, left, top, width, height,
-                                              config, buffer, rowBytes, 0);
-    }
+                                 size_t rowBytes = 0,
+                                 uint32_t pixelOpsFlags = 0);
 
     /**
      * Reads a rectangle of pixels from a texture.
@@ -522,20 +547,18 @@
      * @param height        height of rectangle to read in pixels.
      * @param config        the pixel config of the destination buffer
      * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
-     *                      means rows are tightly packed.
+     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
+     *                      packed.
+     * @param pixelOpsFlags see PixelOpsFlags enum above.
      *
-     * @return true if the read succeeded, false if not. The read can fail
-     *              because of an unsupported pixel config.
+     * @return true if the read succeeded, false if not. The read can fail because of an unsupported
+     *         pixel config.
      */
     bool readTexturePixels(GrTexture* texture,
                            int left, int top, int width, int height,
                            GrPixelConfig config, void* buffer,
-                           size_t rowBytes) {
-        return this->internalReadTexturePixels(texture, left, top,
-                                               width, height,
-                                               config, buffer, rowBytes, 0);
-    }
+                           size_t rowBytes = 0,
+                           uint32_t pixelOpsFlags = 0);
 
     /**
      * Writes a rectangle of pixels to a texture.
@@ -546,87 +569,246 @@
      * @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 bewtween consecutive rows. Zero
+     * @param rowBytes      number of bytes between consecutive rows. Zero
      *                      means rows are tightly packed.
+     * @param pixelOpsFlags see PixelOpsFlags enum above.
      */
     void writeTexturePixels(GrTexture* texture,
                             int left, int top, int width, int height,
                             GrPixelConfig config, const void* buffer,
-                            size_t rowBytes) {
-        this->internalWriteTexturePixels(texture, left, top, width, height, 
-                                         config, buffer, rowBytes, 0);
-    }
+                            size_t rowBytes,
+                            uint32_t pixelOpsFlags = 0);
+
+
     /**
-     * Copies all texels from one texture to another.
+     * Copies a rectangle of texels from src to dst. The size of dst is the size of the rectangle
+     * copied and topLeft is the position of the rect in src. The rectangle is clipped to src's
+     * bounds.
      * @param src           the texture to copy from.
      * @param dst           the render target to copy to.
+     * @param topLeft       the point in src that will be copied to the top-left of dst. If NULL,
+     *                      (0, 0) will be used.
      */
-    void copyTexture(GrTexture* src, GrRenderTarget* dst);
+    void copyTexture(GrTexture* src, GrRenderTarget* dst, const SkIPoint* topLeft = NULL);
 
     /**
      * Resolves a render target that has MSAA. The intermediate MSAA buffer is
-     * downsampled to the associated GrTexture (accessible via
+     * down-sampled to the associated GrTexture (accessible via
      * GrRenderTarget::asTexture()). Any pending draws to the render target will
      * be executed before the resolve.
      *
      * This is only necessary when a client wants to access the object directly
-     * using the underlying graphics API. GrContext will detect when it must
+     * using the backend API directly. GrContext will detect when it must
      * perform a resolve to a GrTexture used as the source of a draw or before
      * reading pixels back from a GrTexture or GrRenderTarget.
      */
     void resolveRenderTarget(GrRenderTarget* target);
 
     /**
-     * Applies a 1D convolution kernel in the given direction to a rectangle of
-     * pixels from a given texture.
-     * @param texture         the texture to read from
-     * @param rect            the destination rectangle
-     * @param kernel          the convolution kernel (kernelWidth elements)
-     * @param kernelWidth     the width of the convolution kernel
-     * @param direction       the direction in which to apply the kernel
+     * Applies a 2D Gaussian blur to a given texture.
+     * @param srcTexture      The source texture to be blurred.
+     * @param canClobberSrc   If true, srcTexture may be overwritten, and
+     *                        may be returned as the result.
+     * @param rect            The destination rectangle.
+     * @param sigmaX          The blur's standard deviation in X.
+     * @param sigmaY          The blur's standard deviation in Y.
+     * @return the blurred texture, which may be srcTexture reffed, or a
+     * new texture.  It is the caller's responsibility to unref this texture.
      */
-    void convolve(GrTexture* texture,
-                  const SkRect& rect,
-                  const float* kernel,
-                  int kernelWidth,
-                  GrSamplerState::FilterDirection direction);
-    /**
-     * Applies a 1D morphology in the given direction to a rectangle of
-     * pixels from a given texture.
-     * @param texture         the texture to read from
-     * @param rect            the destination rectangle
-     * @param radius          the radius of the morphological operator
-     * @param filter          the filter kernel (must be kDilate or kErode)
-     * @param direction       the direction in which to apply the morphology
-     */
-    void applyMorphology(GrTexture* texture,
-                         const SkRect& rect,
-                         int radius,
-                         GrSamplerState::Filter filter,
-                         GrSamplerState::FilterDirection direction);
+     GrTexture* gaussianBlur(GrTexture* srcTexture,
+                             bool canClobberSrc,
+                             const SkRect& rect,
+                             float sigmaX, float sigmaY);
+
     ///////////////////////////////////////////////////////////////////////////
     // Helpers
 
-    class AutoRenderTarget : ::GrNoncopyable {
+    class AutoRenderTarget : public ::GrNoncopyable {
     public:
         AutoRenderTarget(GrContext* context, GrRenderTarget* target) {
-            fContext = NULL;
             fPrevTarget = context->getRenderTarget();
-            if (fPrevTarget != target) {
-                context->setRenderTarget(target);
-                fContext = context;
-            }
+            GrSafeRef(fPrevTarget);
+            context->setRenderTarget(target);
+            fContext = context;
+        }
+        AutoRenderTarget(GrContext* context) {
+            fPrevTarget = context->getRenderTarget();
+            GrSafeRef(fPrevTarget);
+            fContext = context;
         }
         ~AutoRenderTarget() {
-            if (fContext) {
+            if (NULL != fContext) {
                 fContext->setRenderTarget(fPrevTarget);
             }
+            GrSafeUnref(fPrevTarget);
         }
     private:
         GrContext*      fContext;
         GrRenderTarget* fPrevTarget;
     };
 
+    /**
+     * Save/restore the view-matrix in the context. It can optionally adjust a paint to account
+     * for a coordinate system change. Here is an example of how the paint param can be used:
+     *
+     * A GrPaint is setup with GrEffects. The stages will have access to the pre-matrix source
+     * geometry positions when the draw is executed. Later on a decision is made to transform the
+     * geometry to device space on the CPU. The effects now need to know that the space in which
+     * the geometry will be specified has changed.
+     *
+     * Note that when restore is called (or in the destructor) the context's matrix will be
+     * restored. However, the paint will not be restored. The caller must make a copy of the
+     * paint if necessary. Hint: use SkTCopyOnFirstWrite if the AutoMatrix is conditionally
+     * initialized.
+     */
+    class AutoMatrix : GrNoncopyable {
+    public:
+        AutoMatrix() : fContext(NULL) {}
+
+        ~AutoMatrix() { this->restore(); }
+
+        /**
+         * Initializes by pre-concat'ing the context's current matrix with the preConcat param.
+         */
+        void setPreConcat(GrContext* context, const SkMatrix& preConcat, GrPaint* paint = NULL) {
+            GrAssert(NULL != context);
+
+            this->restore();
+
+            fContext = context;
+            fMatrix = context->getMatrix();
+            this->preConcat(preConcat, paint);
+        }
+
+        /**
+         * Sets the context's matrix to identity. Returns false if the inverse matrix is required to
+         * update a paint but the matrix cannot be inverted.
+         */
+        bool setIdentity(GrContext* context, GrPaint* paint = NULL) {
+            GrAssert(NULL != context);
+
+            this->restore();
+
+            if (NULL != paint) {
+                if (!paint->sourceCoordChangeByInverse(context->getMatrix())) {
+                    return false;
+                }
+            }
+            fMatrix = context->getMatrix();
+            fContext = context;
+            context->setIdentityMatrix();
+            return true;
+        }
+
+        /**
+         * Replaces the context's matrix with a new matrix. Returns false if the inverse matrix is
+         * required to update a paint but the matrix cannot be inverted.
+         */
+        bool set(GrContext* context, const SkMatrix& newMatrix, GrPaint* paint = NULL) {
+            if (NULL != paint) {
+                if (!this->setIdentity(context, paint)) {
+                    return false;
+                }
+                this->preConcat(newMatrix, paint);
+            } else {
+                this->restore();
+                fContext = context;
+                fMatrix = context->getMatrix();
+                context->setMatrix(newMatrix);
+            }
+            return true;
+        }
+
+        /**
+         * If this has been initialized then the context's matrix will be further updated by
+         * pre-concat'ing the preConcat param. The matrix that will be restored remains unchanged.
+         * The paint is assumed to be relative to the context's matrix at the time this call is
+         * made, not the matrix at the time AutoMatrix was first initialized. In other words, this
+         * performs an incremental update of the paint.
+         */
+        void preConcat(const SkMatrix& preConcat, GrPaint* paint = NULL) {
+            if (NULL != paint) {
+                paint->sourceCoordChange(preConcat);
+            }
+            fContext->concatMatrix(preConcat);
+        }
+
+        /**
+         * Returns false if never initialized or the inverse matrix was required to update a paint
+         * but the matrix could not be inverted.
+         */
+        bool succeeded() const { return NULL != fContext; }
+
+        /**
+         * If this has been initialized then the context's original matrix is restored.
+         */
+        void restore() {
+            if (NULL != fContext) {
+                fContext->setMatrix(fMatrix);
+                fContext = NULL;
+            }
+        }
+
+    private:
+        GrContext*  fContext;
+        SkMatrix    fMatrix;
+    };
+
+    class AutoClip : GrNoncopyable {
+    public:
+        // This enum exists to require a caller of the constructor to acknowledge that the clip will
+        // initially be wide open. It also could be extended if there are other desirable initial
+        // clip states.
+        enum InitialClip {
+            kWideOpen_InitialClip,
+        };
+
+        AutoClip(GrContext* context, InitialClip initialState)
+        : fContext(context) {
+            GrAssert(kWideOpen_InitialClip == initialState);
+            fNewClipData.fClipStack = &fNewClipStack;
+
+            fOldClip = context->getClip();
+            context->setClip(&fNewClipData);
+        }
+
+        AutoClip(GrContext* context, const GrRect& newClipRect)
+        : fContext(context)
+        , fNewClipStack(newClipRect) {
+            fNewClipData.fClipStack = &fNewClipStack;
+
+            fOldClip = fContext->getClip();
+            fContext->setClip(&fNewClipData);
+        }
+
+        ~AutoClip() {
+            if (NULL != fContext) {
+                fContext->setClip(fOldClip);
+            }
+        }
+    private:
+        GrContext*        fContext;
+        const GrClipData* fOldClip;
+
+        SkClipStack       fNewClipStack;
+        GrClipData        fNewClipData;
+    };
+
+    class AutoWideOpenIdentityDraw {
+    public:
+        AutoWideOpenIdentityDraw(GrContext* ctx, GrRenderTarget* rt)
+            : fAutoClip(ctx, AutoClip::kWideOpen_InitialClip)
+            , fAutoRT(ctx, rt) {
+            fAutoMatrix.setIdentity(ctx);
+            // should never fail with no paint param.
+            GrAssert(fAutoMatrix.succeeded());
+        }
+
+    private:
+        AutoClip fAutoClip;
+        AutoRenderTarget fAutoRT;
+        AutoMatrix fAutoMatrix;
+    };
 
     ///////////////////////////////////////////////////////////////////////////
     // Functions intended for internal use only.
@@ -634,210 +816,196 @@
     const GrGpu* getGpu() const { return fGpu; }
     GrFontCache* getFontCache() { return fFontCache; }
     GrDrawTarget* getTextTarget(const GrPaint& paint);
-    void flushText();
     const GrIndexBuffer* getQuadIndexBuffer() const;
-    void resetStats();
-    const GrGpuStats& getStats() const;
-    void printStats() const;
+
     /**
-     * Stencil buffers add themselves to the cache using
-     * addAndLockStencilBuffer. When a SB's RT-attachment count
-     * reaches zero the SB unlocks itself using unlockStencilBuffer and is
-     * eligible for purging. findStencilBuffer is called to check the cache for
-     * a SB that matching an RT's criteria. If a match is found that has been
-     * unlocked (its attachment count has reached 0) then it will be relocked.
+     * Stencil buffers add themselves to the cache using addStencilBuffer. findStencilBuffer is
+     * called to check the cache for a SB that matches an RT's criteria.
      */
-    GrResourceEntry* addAndLockStencilBuffer(GrStencilBuffer* sb);
-    void unlockStencilBuffer(GrResourceEntry* sbEntry);
+    void addStencilBuffer(GrStencilBuffer* sb);
     GrStencilBuffer* findStencilBuffer(int width, int height, int sampleCnt);
 
+    GrPathRenderer* getPathRenderer(
+                    const SkPath& path,
+                    const SkStrokeRec& stroke,
+                    const GrDrawTarget* target,
+                    bool allowSW,
+                    GrPathRendererChain::DrawType drawType = GrPathRendererChain::kColor_DrawType,
+                    GrPathRendererChain::StencilSupport* stencilSupport = NULL);
+
+#if GR_CACHE_STATS
+    void printCacheStats() const;
+#endif
+
+    ///////////////////////////////////////////////////////////////////////////
+    // Legacy names that will be kept until WebKit can be updated.
+    GrTexture* createPlatformTexture(const GrPlatformTextureDesc& desc) {
+        return this->wrapBackendTexture(desc);
+    }
+
+    GrRenderTarget* createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
+        return wrapBackendRenderTarget(desc);
+    }
+
 private:
-    // used to keep track of when we need to flush the draw buffer
-    enum DrawCategory {
-        kBuffered_DrawCategory,      // last draw was inserted in draw buffer
-        kUnbuffered_DrawCategory,    // last draw was not inserted in the draw buffer
-        kText_DrawCategory           // text context was last to draw
+    // Used to indicate whether a draw should be performed immediately or queued in fDrawBuffer.
+    enum BufferedDraw {
+        kYes_BufferedDraw,
+        kNo_BufferedDraw,
     };
-    DrawCategory fLastDrawCategory;
+    BufferedDraw fLastDrawWasBuffered;
 
     GrGpu*              fGpu;
+    GrDrawState*        fDrawState;
+
     GrResourceCache*    fTextureCache;
     GrFontCache*        fFontCache;
 
     GrPathRendererChain*        fPathRendererChain;
+    GrSoftwarePathRenderer*     fSoftwarePathRenderer;
 
     GrVertexBufferAllocPool*    fDrawBufferVBAllocPool;
     GrIndexBufferAllocPool*     fDrawBufferIBAllocPool;
     GrInOrderDrawBuffer*        fDrawBuffer;
 
-    GrIndexBuffer*              fAAFillRectIndexBuffer;
-    GrIndexBuffer*              fAAStrokeRectIndexBuffer;
+    GrAARectRenderer*           fAARectRenderer;
+
+    bool                        fDidTestPMConversions;
+    int                         fPMToUPMConversion;
+    int                         fUPMToPMConversion;
+
+    struct CleanUpData {
+        PFCleanUpFunc fFunc;
+        void*         fInfo;
+    };
+
+    SkTDArray<CleanUpData>      fCleanUpData;
 
     GrContext(GrGpu* gpu);
 
-    void fillAARect(GrDrawTarget* target,
-                    const GrRect& devRect,
-                    bool useVertexCoverage);
-
-    void strokeAARect(GrDrawTarget* target,
-                      const GrRect& devRect,
-                      const GrVec& devStrokeSize,
-                      bool useVertexCoverage);
-
-    inline int aaFillRectIndexCount() const;
-    GrIndexBuffer* aaFillRectIndexBuffer();
-
-    inline int aaStrokeRectIndexCount() const;
-    GrIndexBuffer* aaStrokeRectIndexBuffer();
-
     void setupDrawBuffer();
 
     void flushDrawBuffer();
 
-    void setPaint(const GrPaint& paint, GrDrawTarget* target);
+    /// Sets the paint and returns the target to draw into. The paint can be NULL in which case the
+    /// draw state is left unmodified.
+    GrDrawTarget* prepareToDraw(const GrPaint*, BufferedDraw);
 
-    GrDrawTarget* prepareToDraw(const GrPaint& paint, DrawCategory drawType);
+    void internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke);
 
-    GrPathRenderer* getPathRenderer(const GrPath& path,
-                                    GrPathFill fill,
-                                    const GrDrawTarget* target,
-                                    bool antiAlias);
+    void internalDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke);
+    bool canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const;
+
+    GrTexture* createResizedTexture(const GrTextureDesc& desc,
+                                    const GrCacheID& cacheID,
+                                    void* srcData,
+                                    size_t rowBytes,
+                                    bool needsFiltering);
+
+    // Needed so GrTexture's returnToCache helper function can call
+    // addExistingTextureToCache
+    friend class GrTexture;
+
+    // Add an existing texture to the texture cache. This is intended solely
+    // for use with textures released from an GrAutoScratchTexture.
+    void addExistingTextureToCache(GrTexture* texture);
 
     /**
-     * Flags to the internal read/write pixels funcs
+     * These functions create premul <-> unpremul effects if it is possible to generate a pair
+     * of effects that make a readToUPM->writeToPM->readToUPM cycle invariant. Otherwise, they
+     * return NULL.
      */
-    enum PixelOpsFlags {
-        kDontFlush_PixelOpsFlag = 0x1,
-    };
+    const GrEffectRef* createPMToUPMEffect(GrTexture* texture,
+                                           bool swapRAndB,
+                                           const SkMatrix& matrix);
+    const GrEffectRef* createUPMToPMEffect(GrTexture* texture,
+                                           bool swapRAndB,
+                                           const SkMatrix& matrix);
 
-    bool internalReadRenderTargetPixels(GrRenderTarget* target,
-                                        int left, int top,
-                                        int width, int height,
-                                        GrPixelConfig config, void* buffer, 
-                                        size_t rowBytes, uint32_t flags);
-
-    void internalWriteRenderTargetPixels(GrRenderTarget* target,
-                                        int left, int top,
-                                        int width, int height,
-                                        GrPixelConfig, const void* buffer,
-                                        size_t rowBytes, uint32_t flags);
-
-    bool internalReadTexturePixels(GrTexture* texture,
-                                   int left, int top,
-                                   int width, int height,
-                                   GrPixelConfig config, void* buffer,
-                                   size_t rowBytes, uint32_t flags);
-
-    void internalWriteTexturePixels(GrTexture* texture,
-                                    int left, int top,
-                                    int width, int height,
-                                    GrPixelConfig config, const void* buffer,
-                                    size_t rowBytes, uint32_t flags);
-    // needed for access to internalWriteTexturePixels. TODO: make GrContext
-    // be a facade for an internal class. Then functions that are public on the 
-    // internal class would have only be callable in src/gpu. The facade would
-    // only have to functions necessary for clients.
-    friend class GrAtlas;
-
-    // computes vertex layout bits based on the paint. If paint expresses
-    // a texture for a stage, the stage coords will be bound to postitions
-    // unless hasTexCoords[s]==true in which case stage s's input coords
-    // are bound to tex coord index s. hasTexCoords == NULL is a shortcut
-    // for an array where all the values are false.
-    static int PaintStageVertexLayoutBits(
-                                    const GrPaint& paint,
-                                    const bool hasTexCoords[GrPaint::kTotalStages]);
-    
+    typedef GrRefCnt INHERITED;
 };
 
 /**
- *  Save/restore the view-matrix in the context.
- */
-class GrAutoMatrix : GrNoncopyable {
-public:
-    GrAutoMatrix() : fContext(NULL) {}
-    GrAutoMatrix(GrContext* ctx) : fContext(ctx) {
-        fMatrix = ctx->getMatrix();
-    }
-    GrAutoMatrix(GrContext* ctx, const GrMatrix& matrix) : fContext(ctx) {
-        fMatrix = ctx->getMatrix();
-        ctx->setMatrix(matrix);
-    }
-    void set(GrContext* ctx) {
-        if (NULL != fContext) {
-            fContext->setMatrix(fMatrix);
-        }
-        fMatrix = ctx->getMatrix();
-        fContext = ctx;
-    }
-    void set(GrContext* ctx, const GrMatrix& matrix) {
-        if (NULL != fContext) {
-            fContext->setMatrix(fMatrix);
-        }
-        fMatrix = ctx->getMatrix();
-        ctx->setMatrix(matrix);
-        fContext = ctx;
-    }
-    ~GrAutoMatrix() {
-        if (NULL != fContext) {
-            fContext->setMatrix(fMatrix);
-        }
-    }
-
-private:
-    GrContext*  fContext;
-    GrMatrix    fMatrix;
-};
-
-/**
- * Gets and locks a scratch texture from a descriptor using
- * either exact or approximate criteria. Unlocks texture in
- * the destructor.
+ * Gets and locks a scratch texture from a descriptor using either exact or approximate criteria.
+ * Unlocks texture in the destructor.
  */
 class GrAutoScratchTexture : ::GrNoncopyable {
 public:
     GrAutoScratchTexture()
-        : fContext(NULL) {
+        : fContext(NULL)
+        , fTexture(NULL) {
     }
 
     GrAutoScratchTexture(GrContext* context,
                          const GrTextureDesc& desc,
-                         GrContext::ScratchTexMatch match =
-                            GrContext::kApprox_ScratchTexMatch)
-      : fContext(NULL) {
+                         GrContext::ScratchTexMatch match = GrContext::kApprox_ScratchTexMatch)
+      : fContext(NULL)
+      , fTexture(NULL) {
       this->set(context, desc, match);
     }
-    
+
     ~GrAutoScratchTexture() {
-        if (NULL != fContext) {
-            fContext->unlockTexture(fEntry);
+        this->reset();
+    }
+
+    void reset() {
+        if (NULL != fContext && NULL != fTexture) {
+            fContext->unlockScratchTexture(fTexture);
+            fTexture->unref();
+            fTexture = NULL;
         }
     }
 
+    /*
+     * When detaching a texture we do not unlock it in the texture cache but
+     * we do set the returnToCache flag. In this way the texture remains
+     * "locked" in the texture cache until it is freed and recycled in
+     * GrTexture::internal_dispose. In reality, the texture has been removed
+     * from the cache (because this is in AutoScratchTexture) and by not
+     * calling unlockScratchTexture we simply don't re-add it. It will be
+     * reattached in GrTexture::internal_dispose.
+     *
+     * Note that the caller is assumed to accept and manage the ref to the
+     * returned texture.
+     */
+    GrTexture* detach() {
+        GrTexture* texture = fTexture;
+        fTexture = NULL;
+
+        // This GrAutoScratchTexture has a ref from lockAndRefScratchTexture, which we give up now.
+        // The cache also has a ref which we are lending to the caller of detach(). When the caller
+        // lets go of the ref and the ref count goes to 0 internal_dispose will see this flag is
+        // set and re-ref the texture, thereby restoring the cache's ref.
+        GrAssert(texture->getRefCnt() > 1);
+        texture->setFlag((GrTextureFlags) GrTexture::kReturnToCache_FlagBit);
+        texture->unref();
+        GrAssert(NULL != texture->getCacheEntry());
+
+        return texture;
+    }
+
     GrTexture* set(GrContext* context,
                    const GrTextureDesc& desc,
-                   GrContext::ScratchTexMatch match =
-                        GrContext::kApprox_ScratchTexMatch) {
-        if (NULL != fContext) {
-            fContext->unlockTexture(fEntry);
-        }
+                   GrContext::ScratchTexMatch match = GrContext::kApprox_ScratchTexMatch) {
+        this->reset();
+
         fContext = context;
         if (NULL != fContext) {
-            fEntry = fContext->lockScratchTexture(desc, match);
-            GrTexture* ret = fEntry.texture();
-            if (NULL == ret) {
+            fTexture = fContext->lockAndRefScratchTexture(desc, match);
+            if (NULL == fTexture) {
                 fContext = NULL;
             }
-            return ret;
+            return fTexture;
         } else {
             return NULL;
         }
     }
 
-    GrTexture* texture() { return fEntry.texture(); }
+    GrTexture* texture() { return fTexture; }
+
 private:
     GrContext*                    fContext;
-    GrContext::TextureCacheEntry  fEntry;
+    GrTexture*                    fTexture;
 };
 
 #endif
diff --git a/include/gpu/GrContextFactory.h b/include/gpu/GrContextFactory.h
new file mode 100644
index 0000000..95d02a2
--- /dev/null
+++ b/include/gpu/GrContextFactory.h
@@ -0,0 +1,133 @@
+/*
+ * 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 GrContextFactory_DEFINED
+#define GrContextFactory_DEFINED
+
+#if SK_ANGLE
+    #include "gl/SkANGLEGLContext.h"
+#endif
+#include "gl/SkDebugGLContext.h"
+#if SK_MESA
+    #include "gl/SkMesaGLContext.h"
+#endif
+#include "gl/SkNativeGLContext.h"
+#include "gl/SkNullGLContext.h"
+
+#include "GrContext.h"
+#include "SkTArray.h"
+
+/**
+ * This is a simple class that is useful in test apps that use different
+ * GrContexts backed by different types of GL contexts. It manages creating the
+ * GL context and a GrContext that uses it. The GL/Gr contexts persist until the
+ * factory is destroyed (though the caller can always grab a ref on the returned
+ * GrContext to make it outlive the factory).
+ */
+class GrContextFactory  : GrNoncopyable {
+public:
+    /**
+     * Types of GL contexts supported.
+     */
+    enum GLContextType {
+      kNative_GLContextType,
+#if SK_ANGLE
+      kANGLE_GLContextType,
+#endif
+#if SK_MESA
+      kMESA_GLContextType,
+#endif
+      kNull_GLContextType,
+      kDebug_GLContextType,
+    };
+
+    GrContextFactory() {
+    }
+
+    ~GrContextFactory() {
+        for (int i = 0; i < fContexts.count(); ++i) {
+            fContexts[i].fGrContext->unref();
+            fContexts[i].fGLContext->unref();
+        }
+    }
+
+    /**
+     * Get a GrContext initalized with a type of GL context.
+     */
+    GrContext* get(GLContextType type) {
+
+        for (int i = 0; i < fContexts.count(); ++i) {
+            if (fContexts[i].fType == type) {
+                return fContexts[i].fGrContext;
+            }
+        }
+        SkAutoTUnref<SkGLContext> glCtx;
+        SkAutoTUnref<GrContext> grCtx;
+        switch (type) {
+            case kNative_GLContextType:
+                glCtx.reset(SkNEW(SkNativeGLContext));
+                break;
+#ifdef SK_ANGLE
+            case kANGLE_GLContextType:
+                glCtx.reset(SkNEW(SkANGLEGLContext));
+                break;
+#endif
+#ifdef SK_MESA
+            case kMESA_GLContextType:
+                glCtx.reset(SkNEW(SkMesaGLContext));
+                break;
+#endif
+            case kNull_GLContextType:
+                glCtx.reset(SkNEW(SkNullGLContext));
+                break;
+            case kDebug_GLContextType:
+                glCtx.reset(SkNEW(SkDebugGLContext));
+                break;
+        }
+        static const int kBogusSize = 1;
+        if (!glCtx.get()) {
+            return NULL;
+        }
+        if (!glCtx.get()->init(kBogusSize, kBogusSize)) {
+            return NULL;
+        }
+        GrBackendContext p3dctx = reinterpret_cast<GrBackendContext>(glCtx.get()->gl());
+        grCtx.reset(GrContext::Create(kOpenGL_GrBackend, p3dctx));
+        if (!grCtx.get()) {
+            return NULL;
+        }
+        GPUContext& ctx = fContexts.push_back();
+        ctx.fGLContext = glCtx.get();
+        ctx.fGLContext->ref();
+        ctx.fGrContext = grCtx.get();
+        ctx.fGrContext->ref();
+        ctx.fType = type;
+        return ctx.fGrContext;
+    }
+
+    // Returns the GLContext of the given type. If it has not been created yet,
+    // NULL is returned instead.
+    SkGLContext* getGLContext(GLContextType type) {
+        for (int i = 0; i < fContexts.count(); ++i) {
+            if (fContexts[i].fType == type) {
+                return fContexts[i].fGLContext;
+            }
+        }
+
+        return NULL;
+    }
+
+private:
+    struct GPUContext {
+        GLContextType             fType;
+        SkGLContext*              fGLContext;
+        GrContext*                fGrContext;
+    };
+    SkTArray<GPUContext, true> fContexts;
+};
+
+#endif
diff --git a/include/gpu/GrEffect.h b/include/gpu/GrEffect.h
new file mode 100644
index 0000000..7b7cd33
--- /dev/null
+++ b/include/gpu/GrEffect.h
@@ -0,0 +1,261 @@
+/*
+ * 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 GrEffect_DEFINED
+#define GrEffect_DEFINED
+
+#include "GrColor.h"
+#include "GrEffectUnitTest.h"
+#include "GrNoncopyable.h"
+#include "GrRefCnt.h"
+#include "GrTexture.h"
+#include "GrTextureAccess.h"
+
+class GrBackendEffectFactory;
+class GrContext;
+class GrEffect;
+class SkString;
+
+/**
+ * A Wrapper class for GrEffect. Its ref-count will track owners that may use effects to enqueue
+ * new draw operations separately from ownership within a deferred drawing queue. When the
+ * GrEffectRef ref count reaches zero the scratch GrResources owned by the effect can be recycled
+ * in service of later draws. However, the deferred draw queue may still own direct references to
+ * the underlying GrEffect.
+ */
+class GrEffectRef : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrEffectRef);
+
+    GrEffect* get() { return fEffect; }
+    const GrEffect* get() const { return fEffect; }
+
+    const GrEffect* operator-> () { return fEffect; }
+    const GrEffect* operator-> () const { return fEffect; }
+
+    void* operator new(size_t size);
+    void operator delete(void* target);
+
+private:
+    friend class GrEffect; // to construct these
+
+    explicit GrEffectRef(GrEffect* effect);
+
+    virtual ~GrEffectRef();
+
+    GrEffect* fEffect;
+
+    typedef SkRefCnt INHERITED;
+};
+
+/** Provides custom vertex shader, fragment shader, uniform data for a particular stage of the
+    Ganesh shading pipeline.
+    Subclasses must have a function that produces a human-readable name:
+        static const char* Name();
+    GrEffect objects *must* be immutable: after being constructed, their fields may not change.
+
+    GrEffect subclass objects should be created by factory functions that return GrEffectRef.
+    There is no public way to wrap a GrEffect in a GrEffectRef. Thus, a factory should be a static
+    member function of a GrEffect subclass.
+
+    Because almost no code should ever handle a GrEffect outside of a GrEffectRef, we privately
+    inherit from GrRefCnt to help prevent accidental direct ref'ing/unref'ing of effects.
+  */
+class GrEffect : private GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrEffect)
+
+    virtual ~GrEffect();
+
+    /**
+     * Flags for getConstantColorComponents. They are defined so that the bit order reflects the
+     * GrColor shift order.
+     */
+    enum ValidComponentFlags {
+        kR_ValidComponentFlag = 1 << (GrColor_SHIFT_R / 8),
+        kG_ValidComponentFlag = 1 << (GrColor_SHIFT_G / 8),
+        kB_ValidComponentFlag = 1 << (GrColor_SHIFT_B / 8),
+        kA_ValidComponentFlag = 1 << (GrColor_SHIFT_A / 8),
+
+        kAll_ValidComponentFlags = (kR_ValidComponentFlag | kG_ValidComponentFlag |
+                                    kB_ValidComponentFlag | kA_ValidComponentFlag)
+    };
+
+    /**
+     * This function is used to perform optimizations. When called the color and validFlags params
+     * indicate whether the input components to this effect in the FS will have known values. The
+     * function updates both params to indicate known values of its output. A component of the color
+     * param only has meaning if the corresponding bit in validFlags is set.
+     */
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const = 0;
+
+    /** This object, besides creating back-end-specific helper objects, is used for run-time-type-
+        identification. The factory should be an instance of templated class,
+        GrTBackendEffectFactory. It is templated on the subclass of GrEffect. The subclass must have
+        a nested type (or typedef) named GLEffect which will be the subclass of GrGLEffect created
+        by the factory.
+
+        Example:
+        class MyCustomEffect : public GrEffect {
+        ...
+            virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+                return GrTBackendEffectFactory<MyCustomEffect>::getInstance();
+            }
+        ...
+        };
+     */
+    virtual const GrBackendEffectFactory& getFactory() const = 0;
+
+    /** Returns true if this and other effect conservatively draw identically. It can only return
+        true when the two effects are of the same subclass (i.e. they return the same object from
+        from getFactory()).
+
+        A return value of true from isEqual() should not be used to test whether the effects would
+        generate the same shader code. To test for identical code generation use the EffectKey
+        computed by the GrBackendEffectFactory:
+            effectA.getFactory().glEffectKey(effectA) == effectB.getFactory().glEffectKey(effectB).
+     */
+    bool isEqual(const GrEffectRef& other) const {
+        return this->isEqual(*other.get());
+    }
+
+    /** Human-meaningful string to identify this effect; may be embedded
+        in generated shader code. */
+    const char* name() const;
+
+    int numTextures() const { return fTextureAccesses.count(); }
+
+    /** Returns the access pattern for the texture at index. index must be valid according to
+        numTextures(). */
+    const GrTextureAccess& textureAccess(int index) const { return *fTextureAccesses[index]; }
+
+    /** Shortcut for textureAccess(index).texture(); */
+    GrTexture* texture(int index) const { return this->textureAccess(index).getTexture(); }
+
+    /** Useful for effects that want to insert a texture matrix that is implied by the texture
+        dimensions */
+    static inline SkMatrix MakeDivByTextureWHMatrix(const GrTexture* texture) {
+        GrAssert(NULL != texture);
+        SkMatrix mat;
+        mat.setIDiv(texture->width(), texture->height());
+        return mat;
+    }
+
+    void* operator new(size_t size);
+    void operator delete(void* target);
+
+    /** These functions are used when recording effects into a deferred drawing queue. The inc call
+        keeps the effect alive outside of GrEffectRef while allowing any resources owned by the
+        effect to be returned to the cache for reuse. The dec call must balance the inc call. */
+    void incDeferredRefCounts() const {
+        this->ref();
+        int count = fTextureAccesses.count();
+        for (int t = 0; t < count; ++t) {
+            fTextureAccesses[t]->getTexture()->incDeferredRefCount();
+        }
+    }
+    void decDeferredRefCounts() const {
+        int count = fTextureAccesses.count();
+        for (int t = 0; t < count; ++t) {
+            fTextureAccesses[t]->getTexture()->decDeferredRefCount();
+        }
+        this->unref();
+    }
+
+protected:
+    /**
+     * Subclasses call this from their constructor to register GrTextureAcceses. The effect subclass
+     * manages the lifetime of the accesses (this function only stores a pointer). This must only be
+     * called from the constructor because GrEffects are supposed to be immutable.
+     */
+    void addTextureAccess(const GrTextureAccess* textureAccess);
+
+    GrEffect() : fEffectRef(NULL) {};
+
+    /** This should be called by GrEffect subclass factories. See the comment on AutoEffectUnref for
+        an example factory function. */
+    static GrEffectRef* CreateEffectRef(GrEffect* effect) {
+        if (NULL == effect->fEffectRef) {
+            effect->fEffectRef = SkNEW_ARGS(GrEffectRef, (effect));
+        } else {
+            effect->fEffectRef->ref();
+        }
+        return effect->fEffectRef;
+    }
+
+    static const GrEffectRef* CreateEffectRef(const GrEffect* effect) {
+        return CreateEffectRef(const_cast<GrEffect*>(effect));
+    }
+
+    /** Helper used in subclass factory functions to unref the effect after it has been wrapped in a
+        GrEffectRef. E.g.:
+
+        class EffectSubclass : public GrEffect {
+        public:
+            GrEffectRef* Create(ParamType1 param1, ParamType2 param2, ...) {
+                AutoEffectUnref effect(SkNEW_ARGS(EffectSubclass, (param1, param2, ...)));
+                return CreateEffectRef(effect);
+            }
+     */
+    class AutoEffectUnref {
+    public:
+        AutoEffectUnref(GrEffect* effect) : fEffect(effect) { }
+        ~AutoEffectUnref() { fEffect->unref(); }
+        operator GrEffect*() { return fEffect; }
+    private:
+        GrEffect* fEffect;
+    };
+
+    /** Helper for getting the GrEffect out of a GrEffectRef and down-casting to a GrEffect subclass
+      */
+    template <typename T>
+    static const T& CastEffect(const GrEffect& effectRef) {
+        return *static_cast<const T*>(&effectRef);
+    }
+
+private:
+    bool isEqual(const GrEffect& other) const {
+        if (&this->getFactory() != &other.getFactory()) {
+            return false;
+        }
+        bool result = this->onIsEqual(other);
+#if GR_DEBUG
+        if (result) {
+            GrAssert(this->numTextures() == other.numTextures());
+            for (int i = 0; i < this->numTextures(); ++i) {
+                GrAssert(*fTextureAccesses[i] == *other.fTextureAccesses[i]);
+            }
+        }
+#endif
+        return result;
+    }
+
+    /** Subclass implements this to support isEqual(). It will only be called if it is known that
+        the two effects are of the same subclass (i.e. they return the same object from
+        getFactory()).*/
+    virtual bool onIsEqual(const GrEffect& other) const = 0;
+
+    void EffectRefDestroyed() { fEffectRef = NULL; }
+
+    friend class GrEffectRef;   // to call EffectRefDestroyed()
+    friend class GrEffectStage; // to rewrap GrEffect in GrEffectRef when restoring an effect-stage
+                                // from deferred state, to call isEqual on naked GrEffects, and
+                                // to inc/dec deferred ref counts.
+
+    SkSTArray<4, const GrTextureAccess*, true>  fTextureAccesses;
+    GrEffectRef*                                fEffectRef;
+
+    typedef GrRefCnt INHERITED;
+};
+
+inline GrEffectRef::GrEffectRef(GrEffect* effect) {
+    GrAssert(NULL != effect);
+    effect->ref();
+    fEffect = effect;
+}
+
+#endif
diff --git a/include/gpu/GrEffectStage.h b/include/gpu/GrEffectStage.h
new file mode 100644
index 0000000..05bc313
--- /dev/null
+++ b/include/gpu/GrEffectStage.h
@@ -0,0 +1,181 @@
+
+/*
+ * 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 GrEffectStage_DEFINED
+#define GrEffectStage_DEFINED
+
+#include "GrBackendEffectFactory.h"
+#include "GrEffect.h"
+#include "SkMatrix.h"
+#include "GrTypes.h"
+
+#include "SkShader.h"
+
+class GrEffectStage {
+public:
+    GrEffectStage()
+    : fEffectRef (NULL) {
+        GR_DEBUGCODE(fSavedCoordChangeCnt = 0;)
+    }
+
+    ~GrEffectStage() {
+        GrSafeUnref(fEffectRef);
+        GrAssert(0 == fSavedCoordChangeCnt);
+    }
+
+    bool operator ==(const GrEffectStage& other) const {
+        // first handle cases where one or the other has no effect
+        if (NULL == fEffectRef) {
+            return NULL == other.fEffectRef;
+        } else if (NULL == other.fEffectRef) {
+            return false;
+        }
+
+        if (!(*this->getEffect())->isEqual(*other.getEffect())) {
+            return false;
+        }
+
+        return fCoordChangeMatrix == other.fCoordChangeMatrix;
+    }
+
+    bool operator !=(const GrEffectStage& s) const { return !(*this == s); }
+
+    GrEffectStage& operator =(const GrEffectStage& other) {
+        GrSafeAssign(fEffectRef, other.fEffectRef);
+        if (NULL != fEffectRef) {
+            fCoordChangeMatrix = other.fCoordChangeMatrix;
+        }
+        return *this;
+    }
+
+    /**
+     * This is called when the coordinate system in which the geometry is specified will change.
+     *
+     * @param matrix    The transformation from the old coord system to the new one.
+     */
+    void preConcatCoordChange(const SkMatrix& matrix) { fCoordChangeMatrix.preConcat(matrix); }
+
+    class SavedCoordChange {
+    private:
+        SkMatrix fCoordChangeMatrix;
+        GR_DEBUGCODE(mutable SkAutoTUnref<const GrEffectRef> fEffectRef;)
+
+        friend class GrEffectStage;
+    };
+
+    /**
+     * This gets the current coordinate system change. It is the accumulation of
+     * preConcatCoordChange calls since the effect was installed. It is used when then caller
+     * wants to temporarily change the source geometry coord system, draw something, and then
+     * restore the previous coord system (e.g. temporarily draw in device coords).
+     */
+    void saveCoordChange(SavedCoordChange* savedCoordChange) const {
+        savedCoordChange->fCoordChangeMatrix = fCoordChangeMatrix;
+        GrAssert(NULL == savedCoordChange->fEffectRef.get());
+        GR_DEBUGCODE(GrSafeRef(fEffectRef);)
+        GR_DEBUGCODE(savedCoordChange->fEffectRef.reset(fEffectRef);)
+        GR_DEBUGCODE(++fSavedCoordChangeCnt);
+    }
+
+    /**
+     * This balances the saveCoordChange call.
+     */
+    void restoreCoordChange(const SavedCoordChange& savedCoordChange) {
+        fCoordChangeMatrix = savedCoordChange.fCoordChangeMatrix;
+        GrAssert(savedCoordChange.fEffectRef.get() == fEffectRef);
+        GR_DEBUGCODE(--fSavedCoordChangeCnt);
+        GR_DEBUGCODE(savedCoordChange.fEffectRef.reset(NULL);)
+    }
+
+    /**
+     * Used when storing a deferred GrDrawState. The DeferredStage allows resources owned by its
+     * GrEffect to be recycled through the cache.
+     */
+    class DeferredStage {
+    public:
+        DeferredStage() : fEffect(NULL) {
+            SkDEBUGCODE(fInitialized = false;)
+        }
+
+        ~DeferredStage() {
+            if (NULL != fEffect) {
+                fEffect->decDeferredRefCounts();
+            }
+        }
+
+        void saveFrom(const GrEffectStage& stage) {
+            GrAssert(!fInitialized);
+            if (NULL != stage.fEffectRef) {
+                stage.fEffectRef->get()->incDeferredRefCounts();
+                fEffect = stage.fEffectRef->get();
+                fCoordChangeMatrix = stage.fCoordChangeMatrix;
+            }
+            SkDEBUGCODE(fInitialized = true;)
+        }
+
+        void restoreTo(GrEffectStage* stage) {
+            GrAssert(fInitialized);
+            const GrEffectRef* oldEffectRef = stage->fEffectRef;
+            if (NULL != fEffect) {
+                stage->fEffectRef = GrEffect::CreateEffectRef(fEffect);
+                stage->fCoordChangeMatrix = fCoordChangeMatrix;
+            } else {
+                stage->fEffectRef = NULL;
+            }
+            SkSafeUnref(oldEffectRef);
+        }
+
+        bool isEqual(const GrEffectStage& stage) const {
+            if (NULL == stage.fEffectRef) {
+                return NULL == fEffect;
+            } else if (NULL == fEffect) {
+                return false;
+            }
+
+            if (!(*stage.getEffect())->isEqual(*fEffect)) {
+                return false;
+            }
+
+            return fCoordChangeMatrix == stage.fCoordChangeMatrix;
+        }
+
+    private:
+        const GrEffect*               fEffect;
+        SkMatrix                      fCoordChangeMatrix;
+        SkDEBUGCODE(bool fInitialized;)
+    };
+
+    /**
+     * Gets the matrix representing all changes of coordinate system since the GrEffect was
+     * installed in the stage.
+     */
+    const SkMatrix& getCoordChangeMatrix() const { return fCoordChangeMatrix; }
+
+    void reset() {
+        GrSafeSetNull(fEffectRef);
+    }
+
+    const GrEffectRef* setEffect(const GrEffectRef* EffectRef) {
+        GrAssert(0 == fSavedCoordChangeCnt);
+        GrSafeAssign(fEffectRef, EffectRef);
+        fCoordChangeMatrix.reset();
+        return EffectRef;
+    }
+
+    const GrEffectRef* getEffect() const { return fEffectRef; }
+
+private:
+    SkMatrix            fCoordChangeMatrix;
+    const GrEffectRef*  fEffectRef;
+
+    GR_DEBUGCODE(mutable int fSavedCoordChangeCnt;)
+};
+
+#endif
diff --git a/include/gpu/GrEffectUnitTest.h b/include/gpu/GrEffectUnitTest.h
new file mode 100644
index 0000000..8b65e97
--- /dev/null
+++ b/include/gpu/GrEffectUnitTest.h
@@ -0,0 +1,87 @@
+/*
+ * 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 GrEffectUnitTest_DEFINED
+#define GrEffectUnitTest_DEFINED
+
+#include "SkRandom.h"
+#include "GrNoncopyable.h"
+#include "SkTArray.h"
+
+class SkMatrix;
+
+namespace GrEffectUnitTest {
+// Used to access the dummy textures in TestCreate procs.
+enum {
+    kSkiaPMTextureIdx = 0,
+    kAlphaTextureIdx = 1,
+};
+
+/**
+ * A helper for use in GrEffect::TestCreate functions.
+ */
+const SkMatrix& TestMatrix(SkRandom*);
+
+}
+
+#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+class GrContext;
+class GrEffectRef;
+class GrTexture;
+
+class GrEffectTestFactory : GrNoncopyable {
+public:
+
+    typedef GrEffectRef* (*CreateProc)(SkRandom*, GrContext*, GrTexture* dummyTextures[]);
+
+    GrEffectTestFactory(CreateProc createProc) {
+        fCreateProc = createProc;
+        GetFactories()->push_back(this);
+    }
+
+    static GrEffectRef* CreateStage(SkRandom* random,
+                                    GrContext* context,
+                                    GrTexture* dummyTextures[]) {
+        uint32_t idx = random->nextRangeU(0, GetFactories()->count() - 1);
+        GrEffectTestFactory* factory = (*GetFactories())[idx];
+        return factory->fCreateProc(random, context, dummyTextures);
+    }
+
+private:
+    CreateProc fCreateProc;
+    static SkTArray<GrEffectTestFactory*, true>* GetFactories();
+};
+
+/** GrEffect subclasses should insert this macro in their declaration to be included in the
+ *  program generation unit test.
+ */
+#define GR_DECLARE_EFFECT_TEST                                                      \
+    static GrEffectTestFactory gTestFactory;                                        \
+    static GrEffectRef* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
+
+/** GrEffect subclasses should insert this macro in their implementation file. They must then
+ *  also implement this static function:
+ *      GrEffect* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2]);
+ * dummyTextures[] are valid textures that can optionally be used to construct GrTextureAccesses.
+ * The first texture has config kSkia8888_PM_GrPixelConfig and the second has
+ * kAlpha_8_GrPixelConfig. TestCreate functions are also free to create additional textures using
+ * the GrContext.
+ */
+#define GR_DEFINE_EFFECT_TEST(Effect)                                               \
+    GrEffectTestFactory Effect :: gTestFactory(Effect :: TestCreate)
+
+#else // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+// The unit test relies on static initializers. Just declare the TestCreate function so that
+// its definitions will compile.
+#define GR_DECLARE_EFFECT_TEST \
+    static GrEffectRef* TestCreate(SkRandom*, GrContext*, GrTexture* dummyTextures[2])
+#define GR_DEFINE_EFFECT_TEST(X)
+
+#endif // !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+#endif
diff --git a/include/gpu/GrFontScaler.h b/include/gpu/GrFontScaler.h
index 657647d..e3a7a2a 100644
--- a/include/gpu/GrFontScaler.h
+++ b/include/gpu/GrFontScaler.h
@@ -25,13 +25,17 @@
  */
 class GrFontScaler : public GrRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(GrFontScaler)
+
     virtual const GrKey* getKey() = 0;
     virtual GrMaskFormat getMaskFormat() = 0;
     virtual bool getPackedGlyphBounds(GrGlyph::PackedID, GrIRect* bounds) = 0;
     virtual bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height,
                                      int rowBytes, void* image) = 0;
     virtual bool getGlyphPath(uint16_t glyphID, SkPath*) = 0;
+
+private:
+    typedef GrRefCnt INHERITED;
 };
 
 #endif
-
diff --git a/include/gpu/GrGlyph.h b/include/gpu/GrGlyph.h
index a0d81a1..4945f2a 100644
--- a/include/gpu/GrGlyph.h
+++ b/include/gpu/GrGlyph.h
@@ -11,8 +11,8 @@
 #ifndef GrGlyph_DEFINED
 #define GrGlyph_DEFINED
 
-#include "GrPath.h"
 #include "GrRect.h"
+#include "SkPath.h"
 
 class GrAtlas;
 
@@ -26,7 +26,7 @@
     typedef uint32_t PackedID;
 
     GrAtlas*    fAtlas;
-    GrPath*     fPath;
+    SkPath*     fPath;
     PackedID    fPackedID;
     GrIRect16   fBounds;
     GrIPoint16  fAtlasLocation;
@@ -38,40 +38,40 @@
         fBounds.set(bounds);
         fAtlasLocation.set(0, 0);
     }
-    
+
     void free() {
         if (fPath) {
             delete fPath;
             fPath = NULL;
         }
     }
-    
+
     int width() const { return fBounds.width(); }
     int height() const { return fBounds.height(); }
     bool isEmpty() const { return fBounds.isEmpty(); }
     uint16_t glyphID() const { return UnpackID(fPackedID); }
 
     ///////////////////////////////////////////////////////////////////////////
-    
+
     static inline unsigned ExtractSubPixelBitsFromFixed(GrFixed pos) {
         // two most significant fraction bits from fixed-point
         return (pos >> 14) & 3;
     }
-    
+
     static inline PackedID Pack(uint16_t glyphID, GrFixed x, GrFixed y) {
         x = ExtractSubPixelBitsFromFixed(x);
         y = ExtractSubPixelBitsFromFixed(y);
         return (x << 18) | (y << 16) | glyphID;
     }
-    
+
     static inline GrFixed UnpackFixedX(PackedID packed) {
         return ((packed >> 18) & 3) << 14;
     }
-    
+
     static inline GrFixed UnpackFixedY(PackedID packed) {
         return ((packed >> 16) & 3) << 14;
     }
-    
+
     static inline uint16_t UnpackID(PackedID packed) {
         return (uint16_t)packed;
     }
@@ -79,4 +79,3 @@
 
 
 #endif
-
diff --git a/include/gpu/GrInstanceCounter.h b/include/gpu/GrInstanceCounter.h
deleted file mode 100644
index b3e21d2..0000000
--- a/include/gpu/GrInstanceCounter.h
+++ /dev/null
@@ -1,40 +0,0 @@
-
-/*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-
-#ifndef GrInstanceCounter_DEFINED
-#define GrInstanceCounter_DEFINED
-
-#include "GrTypes.h"
-
-template <typename T> class GrInstanceCounter {
-public:
-    GrInstanceCounter() {
-        ++gCounter;
-        GrPrintf("+ %s %d\n", T::InstanceCounterClassName(), gCounter);
-    }
-
-    ~GrInstanceCounter() {
-        --gCounter;
-        GrPrintf("- %s %d\n", T::InstanceCounterClassName(), gCounter);
-    }
-
-private:
-    static int gCounter;
-};
-
-template <typename T> int GrInstanceCounter<T>::gCounter;
-
-#define DECLARE_INSTANCE_COUNTER(T)                                 \
-    static const char* InstanceCounterClassName() { return #T; }    \
-    friend class GrInstanceCounter<T>;                              \
-    GrInstanceCounter<T> fInstanceCounter
-
-#endif
-
diff --git a/include/gpu/GrKey.h b/include/gpu/GrKey.h
index 813d82d..80eb537 100644
--- a/include/gpu/GrKey.h
+++ b/include/gpu/GrKey.h
@@ -15,6 +15,8 @@
 
 class GrKey : public GrRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(GrKey)
+
     typedef intptr_t Hash;
 
     explicit GrKey(Hash hash) : fHash(hash) {}
@@ -34,7 +36,8 @@
 
 private:
     const Hash fHash;
+
+    typedef GrRefCnt INHERITED;
 };
 
 #endif
-
diff --git a/include/gpu/GrMatrix.h b/include/gpu/GrMatrix.h
deleted file mode 100644
index 055680a..0000000
--- a/include/gpu/GrMatrix.h
+++ /dev/null
@@ -1,19 +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 GrMatrix_DEFINED
-#define GrMatrix_DEFINED
-
-#include "GrRect.h"
-#include "SkMatrix.h"
-
-typedef SkMatrix GrMatrix;
-
-#endif
diff --git a/include/gpu/GrNoncopyable.h b/include/gpu/GrNoncopyable.h
index cad722f..3d47170 100644
--- a/include/gpu/GrNoncopyable.h
+++ b/include/gpu/GrNoncopyable.h
@@ -28,4 +28,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrPaint.h b/include/gpu/GrPaint.h
index 9f220e0..0fd42f9 100644
--- a/include/gpu/GrPaint.h
+++ b/include/gpu/GrPaint.h
@@ -10,114 +10,211 @@
 #ifndef GrPaint_DEFINED
 #define GrPaint_DEFINED
 
-#include "GrTexture.h"
 #include "GrColor.h"
-#include "GrSamplerState.h"
+#include "GrEffectStage.h"
 
 #include "SkXfermode.h"
 
 /**
- * The paint describes how pixels are colored when the context draws to
- * them. TODO: Make this a "real" class with getters and setters, default
- * values, and documentation.
+ * The paint describes how color and coverage are computed at each pixel by GrContext draw
+ * functions and the how color is blended with the destination pixel.
+ *
+ * The paint allows installation of custom color and coverage stages. New types of stages are
+ * created by subclassing GrEffect.
+ *
+ * The primitive color computation starts with the color specified by setColor(). This color is the
+ * input to the first color stage. Each color stage feeds its output to the next color stage. The
+ * final color stage's output color is input to the color filter specified by
+ * setXfermodeColorFilter which produces the final source color, S.
+ *
+ * Fractional pixel coverage follows a similar flow. The coverage is initially the value specified
+ * by setCoverage(). This is input to the first coverage stage. Coverage stages are chained
+ * together in the same manner as color stages. The output of the last stage is modulated by any
+ * fractional coverage produced by anti-aliasing. This last step produces the final coverage, C.
+ *
+ * setBlendFunc() specifies blending coefficients for S (described above) and D, the initial value
+ * of the destination pixel, labeled Bs and Bd respectively. The final value of the destination
+ * pixel is then D' = (1-C)*D + C*(Bd*D + Bs*S).
+ *
+ * Note that the coverage is applied after the blend. This is why they are computed as distinct
+ * values.
+ *
+ * TODO: Encapsulate setXfermodeColorFilter in a GrEffect and remove from GrPaint.
  */
 class GrPaint {
 public:
     enum {
-        kMaxTextures = 1,
-        kMaxMasks    = 1,
+        kMaxColorStages     = 2,
+        kMaxCoverageStages  = 1,
     };
 
-    // All the paint fields are public except textures/samplers
-    GrBlendCoeff                fSrcBlendCoeff;
-    GrBlendCoeff                fDstBlendCoeff;
-    bool                        fAntiAlias;
-    bool                        fDither;
-    bool                        fColorMatrixEnabled;
+    GrPaint() { this->reset(); }
 
-    GrColor                     fColor;
-    uint8_t                     fCoverage;
+    GrPaint(const GrPaint& paint) { *this = paint; }
 
-    GrColor                     fColorFilterColor;
-    SkXfermode::Mode            fColorFilterXfermode;
-    float                       fColorMatrix[20];
+    ~GrPaint() {}
 
-    void setTexture(int i, GrTexture* texture) {
-        GrAssert((unsigned)i < kMaxTextures);
-        GrSafeRef(texture);
-        GrSafeUnref(fTextures[i]);
-        fTextures[i] = texture;
+    /**
+     * Sets the blending coefficients to use to blend the final primitive color with the
+     * destination color. Defaults to kOne for src and kZero for dst (i.e. src mode).
+     */
+    void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
+        fSrcBlendCoeff = srcCoeff;
+        fDstBlendCoeff = dstCoeff;
+    }
+    GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlendCoeff; }
+    GrBlendCoeff getDstBlendCoeff() const { return fDstBlendCoeff; }
+
+    /**
+     * The initial color of the drawn primitive. Defaults to solid white.
+     */
+    void setColor(GrColor color) { fColor = color; }
+    GrColor getColor() const { return fColor; }
+
+    /**
+     * Applies fractional coverage to the entire drawn primitive. Defaults to 0xff.
+     */
+    void setCoverage(uint8_t coverage) { fCoverage = coverage; }
+    uint8_t getCoverage() const { return fCoverage; }
+
+    /**
+     * Should primitives be anti-aliased or not. Defaults to false.
+     */
+    void setAntiAlias(bool aa) { fAntiAlias = aa; }
+    bool isAntiAlias() const { return fAntiAlias; }
+
+    /**
+     * Should dithering be applied. Defaults to false.
+     */
+    void setDither(bool dither) { fDither = dither; }
+    bool isDither() const { return fDither; }
+
+    /**
+     * Enables a SkXfermode::Mode-based color filter applied to the primitive color. The constant
+     * color passed to this function is considered the "src" color and the primitive's color is
+     * considered the "dst" color. Defaults to kDst_Mode which equates to simply passing through
+     * the primitive color unmodified.
+     */
+    void setXfermodeColorFilter(SkXfermode::Mode mode, GrColor color) {
+        fColorFilterColor = color;
+        fColorFilterXfermode = mode;
+    }
+    SkXfermode::Mode getColorFilterMode() const { return fColorFilterXfermode; }
+    GrColor getColorFilterColor() const { return fColorFilterColor; }
+
+    /**
+     * Disables the SkXfermode::Mode color filter.
+     */
+    void resetColorFilter() {
+        fColorFilterXfermode = SkXfermode::kDst_Mode;
+        fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
     }
 
-    GrTexture* getTexture(int i) const { 
-        GrAssert((unsigned)i < kMaxTextures);
-        return fTextures[i]; 
+    /**
+     * Specifies a stage of the color pipeline. Usually the texture matrices of color stages apply
+     * to the primitive's positions. Some GrContext calls take explicit coords as an array or a
+     * rect. In this case these are the pre-matrix coords to colorStage(0).
+     */
+    GrEffectStage* colorStage(int i) {
+        GrAssert((unsigned)i < kMaxColorStages);
+        return fColorStages + i;
     }
 
-    GrSamplerState* textureSampler(int i) {
-        GrAssert((unsigned)i < kMaxTextures);
-        return fTextureSamplers + i;
+    const GrEffectStage& getColorStage(int i) const {
+        GrAssert((unsigned)i < kMaxColorStages);
+        return fColorStages[i];
     }
 
-    const GrSamplerState& getTextureSampler(int i) const {
-        GrAssert((unsigned)i < kMaxTextures);
-        return fTextureSamplers[i];
+    bool isColorStageEnabled(int i) const {
+        GrAssert((unsigned)i < kMaxColorStages);
+        return (NULL != fColorStages[i].getEffect());
     }
 
-    // The mask can be alpha-only or per channel. It is applied
-    // after the colorfilter
-    void setMask(int i, GrTexture* mask) {
-        GrAssert((unsigned)i < kMaxMasks);
-        GrSafeRef(mask);
-        GrSafeUnref(fMaskTextures[i]);
-        fMaskTextures[i] = mask;
+    /**
+     * Specifies a stage of the coverage pipeline. Coverage stages' texture matrices are always
+     * applied to the primitive's position, never to explicit texture coords.
+     */
+    GrEffectStage* coverageStage(int i) {
+        GrAssert((unsigned)i < kMaxCoverageStages);
+        return fCoverageStages + i;
     }
 
-    GrTexture* getMask(int i) const { 
-        GrAssert((unsigned)i < kMaxMasks);
-        return fMaskTextures[i]; 
+    const GrEffectStage& getCoverageStage(int i) const {
+        GrAssert((unsigned)i < kMaxCoverageStages);
+        return fCoverageStages[i];
     }
 
-    // mask's sampler matrix is always applied to the positions
-    // (i.e. no explicit texture coordinates)
-    GrSamplerState* maskSampler(int i) {
-        GrAssert((unsigned)i < kMaxMasks);
-        return fMaskSamplers + i;
+    bool isCoverageStageEnabled(int i) const {
+        GrAssert((unsigned)i < kMaxCoverageStages);
+        return (NULL != fCoverageStages[i].getEffect());
     }
 
-    const GrSamplerState& getMaskSampler(int i) const {
-        GrAssert((unsigned)i < kMaxMasks);
-        return fMaskSamplers[i];
-    }
-
-    // pre-concats sampler matrices for non-NULL textures and masks
-    void preConcatActiveSamplerMatrices(const GrMatrix& matrix) {
-        for (int i = 0; i < kMaxTextures; ++i) {
-            fTextureSamplers[i].preConcatMatrix(matrix);
+    bool hasCoverageStage() const {
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (this->isCoverageStageEnabled(i)) {
+                return true;
+            }
         }
-        for (int i = 0; i < kMaxMasks; ++i) {
-            fMaskSamplers[i].preConcatMatrix(matrix);
-        }
+        return false;
     }
 
-    // uninitialized
-    GrPaint() {
-        for (int i = 0; i < kMaxTextures; ++i) {
-            fTextures[i] = NULL;
+    bool hasColorStage() const {
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (this->isColorStageEnabled(i)) {
+                return true;
+            }
         }
-        for (int i = 0; i < kMaxMasks; ++i) {
-            fMaskTextures[i] = NULL;
-        }
+        return false;
     }
 
-    GrPaint(const GrPaint& paint) {
-        for (int i = 0; i < kMaxTextures; ++i) {
-            fTextures[i] = NULL;
+    bool hasStage() const { return this->hasColorStage() || this->hasCoverageStage(); }
+
+    /**
+     * Called when the source coord system is changing. preConcatInverse is the inverse of the
+     * transformation from the old coord system to the new coord system. Returns false if the matrix
+     * cannot be inverted.
+     */
+    bool sourceCoordChangeByInverse(const SkMatrix& preConcatInverse) {
+        SkMatrix inv;
+        bool computed = false;
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (this->isColorStageEnabled(i)) {
+                if (!computed && !preConcatInverse.invert(&inv)) {
+                    return false;
+                } else {
+                    computed = true;
+                }
+                fColorStages[i].preConcatCoordChange(inv);
+            }
         }
-        for (int i = 0; i < kMaxMasks; ++i) {
-            fMaskTextures[i] = NULL;
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (this->isCoverageStageEnabled(i)) {
+                if (!computed && !preConcatInverse.invert(&inv)) {
+                    return false;
+                } else {
+                    computed = true;
+                }
+                fCoverageStages[i].preConcatCoordChange(inv);
+            }
         }
-        *this = paint;
+        return true;
+    }
+
+    /**
+     * Called when the source coord system is changing. preConcat gives the transformation from the
+     * old coord system to the new coord system.
+     */
+    void sourceCoordChange(const SkMatrix& preConcat) {
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (this->isColorStageEnabled(i)) {
+                fColorStages[i].preConcatCoordChange(preConcat);
+            }
+        }
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (this->isCoverageStageEnabled(i)) {
+                fCoverageStages[i].preConcatCoordChange(preConcat);
+            }
+        }
     }
 
     GrPaint& operator=(const GrPaint& paint) {
@@ -131,109 +228,60 @@
 
         fColorFilterColor = paint.fColorFilterColor;
         fColorFilterXfermode = paint.fColorFilterXfermode;
-        memcpy(fColorMatrix, paint.fColorMatrix, sizeof(fColorMatrix));
-        fColorMatrixEnabled = paint.fColorMatrixEnabled;
 
-        for (int i = 0; i < kMaxTextures; ++i) {
-            GrSafeUnref(fTextures[i]);
-            fTextureSamplers[i] = paint.fTextureSamplers[i];
-            fTextures[i] = paint.fTextures[i];
-            GrSafeRef(fTextures[i]);
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            if (paint.isColorStageEnabled(i)) {
+                fColorStages[i] = paint.fColorStages[i];
+            }
         }
-        for (int i = 0; i < kMaxMasks; ++i) {
-            GrSafeUnref(fMaskTextures[i]);
-            fMaskSamplers[i] = paint.fMaskSamplers[i];
-            fMaskTextures[i] = paint.fMaskTextures[i];
-            GrSafeRef(fMaskTextures[i]);
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            if (paint.isCoverageStageEnabled(i)) {
+                fCoverageStages[i] = paint.fCoverageStages[i];
+            }
         }
         return *this;
     }
 
-    ~GrPaint() {
-        for (int i = 0; i < kMaxTextures; ++i) {
-            GrSafeUnref(fTextures[i]);
-        }
-        for (int i = 0; i < kMaxMasks; ++i) {
-            GrSafeUnref(fMaskTextures[i]);
-        }
-    }
-
-    // sets paint to src-over, solid white, no texture, no mask
+    /**
+     * Resets the paint to the defaults.
+     */
     void reset() {
         this->resetBlend();
         this->resetOptions();
         this->resetColor();
         this->resetCoverage();
-        this->resetTextures();
+        this->resetStages();
         this->resetColorFilter();
-        this->resetMasks();
-    }
-
-    void resetColorFilter() {
-        fColorFilterXfermode = SkXfermode::kDst_Mode;
-        fColorFilterColor = GrColorPackRGBA(0xff, 0xff, 0xff, 0xff);
-        memset(fColorMatrix, 0, sizeof(fColorMatrix));
-        fColorMatrixEnabled = false;
-    }
-
-    bool hasTexture() const {
-        return 0 != this->getActiveTextureStageMask();
-    }
-
-    bool hasMask() const {
-        return 0 != this->getActiveMaskStageMask();
-    }
-
-    bool hasTextureOrMask() const {
-        return this->hasTexture() || this->hasMask();
-    }
-
-    // helpers for GrContext, GrTextContext
-    int getActiveTextureStageMask() const {
-        int mask = 0;
-        for (int i = 0; i < kMaxTextures; ++i) {
-            if (NULL != fTextures[i]) {
-                mask |= 1 << (i + kFirstTextureStage);
-            }
-        }
-        return mask;
-    }
-
-    int getActiveMaskStageMask() const {
-        int mask = 0;
-        for (int i = 0; i < kMaxMasks; ++i) {
-            if (NULL != fMaskTextures[i]) {
-                mask |= 1 << (i + kFirstMaskStage);
-            }
-        }
-        return mask;
-    }
-    
-    int getActiveStageMask() const {
-        return this->getActiveTextureStageMask() |
-                this->getActiveMaskStageMask();
     }
 
     // internal use
     // GrPaint's textures and masks map to the first N stages
     // of GrDrawTarget in that order (textures followed by masks)
     enum {
-        kFirstTextureStage = 0,
-        kFirstMaskStage = kMaxTextures,
-        kTotalStages = kMaxTextures + kMaxMasks,
+        kFirstColorStage = 0,
+        kFirstCoverageStage = kMaxColorStages,
+        kTotalStages = kFirstColorStage + kMaxColorStages + kMaxCoverageStages,
     };
 
 private:
 
-    GrSamplerState              fTextureSamplers[kMaxTextures];
-    GrSamplerState              fMaskSamplers[kMaxMasks];
+    GrEffectStage               fColorStages[kMaxColorStages];
+    GrEffectStage               fCoverageStages[kMaxCoverageStages];
 
-    GrTexture*      fTextures[kMaxTextures];
-    GrTexture*      fMaskTextures[kMaxMasks];
+    GrBlendCoeff                fSrcBlendCoeff;
+    GrBlendCoeff                fDstBlendCoeff;
+    bool                        fAntiAlias;
+    bool                        fDither;
+
+    GrColor                     fColor;
+    uint8_t                     fCoverage;
+
+    GrColor                     fColorFilterColor;
+    SkXfermode::Mode            fColorFilterXfermode;
 
     void resetBlend() {
-        fSrcBlendCoeff = kOne_BlendCoeff;
-        fDstBlendCoeff = kZero_BlendCoeff;
+        fSrcBlendCoeff = kOne_GrBlendCoeff;
+        fDstBlendCoeff = kZero_GrBlendCoeff;
     }
 
     void resetOptions() {
@@ -249,17 +297,12 @@
         fCoverage = 0xff;
     }
 
-    void resetTextures() {
-        for (int i = 0; i < kMaxTextures; ++i) {
-            this->setTexture(i, NULL);
-            fTextureSamplers[i].reset();
+    void resetStages() {
+        for (int i = 0; i < kMaxColorStages; ++i) {
+            fColorStages[i].reset();
         }
-    }
-
-    void resetMasks() {
-        for (int i = 0; i < kMaxMasks; ++i) {
-            this->setMask(i, NULL);
-            fMaskSamplers[i].reset();
+        for (int i = 0; i < kMaxCoverageStages; ++i) {
+            fCoverageStages[i].reset();
         }
     }
 };
diff --git a/include/gpu/GrPath.h b/include/gpu/GrPath.h
deleted file mode 100644
index 7da0d48..0000000
--- a/include/gpu/GrPath.h
+++ /dev/null
@@ -1,20 +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 GrPath_DEFINED
-#define GrPath_DEFINED
-
-#include "GrTypes.h"
-#include "SkPath.h"
-
-typedef SkPath GrPath;
-
-#endif
-
diff --git a/include/gpu/GrPathRendererChain.h b/include/gpu/GrPathRendererChain.h
new file mode 100644
index 0000000..d51a4bb
--- /dev/null
+++ b/include/gpu/GrPathRendererChain.h
@@ -0,0 +1,83 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef GrPathRendererChain_DEFINED
+#define GrPathRendererChain_DEFINED
+
+#include "GrRefCnt.h"
+#include "SkTArray.h"
+
+class GrContext;
+class GrDrawTarget;
+class GrPathRenderer;
+class SkPath;
+class SkStrokeRec;
+
+/**
+ * Keeps track of an ordered list of path renderers. When a path needs to be
+ * drawn this list is scanned to find the most preferred renderer. To add your
+ * path renderer to the list implement the GrPathRenderer::AddPathRenderers
+ * function.
+ */
+class GrPathRendererChain : public SkRefCnt {
+public:
+    // See comments in GrPathRenderer.h
+    enum StencilSupport {
+        kNoSupport_StencilSupport,
+        kStencilOnly_StencilSupport,
+        kNoRestriction_StencilSupport,
+    };
+
+    SK_DECLARE_INST_COUNT(GrPathRendererChain)
+
+    GrPathRendererChain(GrContext* context);
+
+    ~GrPathRendererChain();
+
+    // takes a ref and unrefs in destructor
+    GrPathRenderer* addPathRenderer(GrPathRenderer* pr);
+
+    /** Documents how the caller plans to use a GrPathRenderer to draw a path. It affects the PR
+        returned by getPathRenderer */
+    enum DrawType {
+        kColor_DrawType,                    // draw to the color buffer, no AA
+        kColorAntiAlias_DrawType,           // draw to color buffer, with partial coverage AA
+        kStencilOnly_DrawType,              // draw just to the stencil buffer
+        kStencilAndColor_DrawType,          // draw the stencil and color buffer, no AA
+        kStencilAndColorAntiAlias_DrawType  // draw the stencil and color buffer, with partial
+                                            // coverage AA.
+    };
+    /** Returns a GrPathRenderer compatible with the request if one is available. If the caller
+        is drawing the path to the stencil buffer then stencilSupport can be used to determine
+        whether the path can be rendered with arbitrary stencil rules or not. See comments on
+        StencilSupport in GrPathRenderer.h. */
+    GrPathRenderer* getPathRenderer(const SkPath& path,
+                                    const SkStrokeRec& rec,
+                                    const GrDrawTarget* target,
+                                    DrawType drawType,
+                                    StencilSupport* stencilSupport);
+
+private:
+
+    GrPathRendererChain();
+
+    void init();
+
+    enum {
+        kPreAllocCount = 8,
+    };
+    bool fInit;
+    GrContext*                                          fOwner;
+    SkSTArray<kPreAllocCount, GrPathRenderer*, true>    fChain;
+
+    typedef SkRefCnt INHERITED;
+};
+
+
+#endif
diff --git a/include/gpu/GrPoint.h b/include/gpu/GrPoint.h
index b32123b..c987b01 100644
--- a/include/gpu/GrPoint.h
+++ b/include/gpu/GrPoint.h
@@ -12,7 +12,7 @@
 #define GrPoint_DEFINED
 
 #include "GrTypes.h"
-#include "GrScalar.h"
+#include "SkScalar.h"
 #include "SkPoint.h"
 
 #define GrPoint     SkPoint
@@ -20,7 +20,7 @@
 
 struct GrIPoint16 {
     int16_t fX, fY;
-    
+
     void set(intptr_t x, intptr_t y) {
         fX = GrToS16(x);
         fY = GrToS16(y);
@@ -28,4 +28,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrRect.h b/include/gpu/GrRect.h
index 60a4773..821e137 100644
--- a/include/gpu/GrRect.h
+++ b/include/gpu/GrRect.h
@@ -19,12 +19,12 @@
 
 struct GrIRect16 {
     int16_t fLeft, fTop, fRight, fBottom;
-    
+
     int width() const { return fRight - fLeft; }
     int height() const { return fBottom - fTop; }
     int area() const { return this->width() * this->height(); }
     bool isEmpty() const { return fLeft >= fRight || fTop >= fBottom; }
-    
+
     void set(const GrIRect& r) {
         fLeft   = SkToS16(r.fLeft);
         fTop    = SkToS16(r.fTop);
@@ -34,4 +34,3 @@
 };
 
 #endif
-
diff --git a/include/gpu/GrRefCnt.h b/include/gpu/GrRefCnt.h
index 50c32c6..31aa80d 100644
--- a/include/gpu/GrRefCnt.h
+++ b/include/gpu/GrRefCnt.h
@@ -31,4 +31,3 @@
 }
 
 #endif
-
diff --git a/include/gpu/GrRenderTarget.h b/include/gpu/GrRenderTarget.h
index 909adb3..19a37a5 100644
--- a/include/gpu/GrRenderTarget.h
+++ b/include/gpu/GrRenderTarget.h
@@ -1,25 +1,15 @@
 /*
-    Copyright 2011 Google Inc.
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
  */
 
-
 #ifndef GrRenderTarget_DEFINED
 #define GrRenderTarget_DEFINED
 
 #include "GrRect.h"
-#include "GrResource.h"
+#include "GrSurface.h"
 
 class GrStencilBuffer;
 class GrTexture;
@@ -31,61 +21,72 @@
  * Additionally, GrContext provides methods for creating GrRenderTargets
  * that wrap externally created render targets.
  */
-class GrRenderTarget : public GrResource {
+class GrRenderTarget : public GrSurface {
 public:
+    SK_DECLARE_INST_COUNT(GrRenderTarget)
+
+    // GrResource overrides
+    virtual size_t sizeInBytes() const SK_OVERRIDE;
+
+    // GrSurface overrides
+    /**
+     * @return the texture associated with the render target, may be NULL.
+     */
+    virtual GrTexture* asTexture() SK_OVERRIDE { return fTexture; }
+    virtual const GrTexture* asTexture() const SK_OVERRIDE { return fTexture; }
 
     /**
-     * @return the width of the rendertarget
+     * @return this render target.
      */
-    int width() const { return fWidth; }
-    /**
-     * @return the height of the rendertarget
-     */
-    int height() const { return fHeight; }
+    virtual GrRenderTarget* asRenderTarget() SK_OVERRIDE { return this; }
+    virtual const GrRenderTarget* asRenderTarget() const  SK_OVERRIDE {
+        return this;
+    }
 
-    /**
-     * @return the pixel config. Can be kUnknown_GrPixelConfig
-     * if client asked us to render to a target that has a pixel
-     * config that isn't equivalent with one of our configs.
-     */
-    GrPixelConfig config() const { return fConfig; }
+    virtual bool readPixels(int left, int top, int width, int height,
+                            GrPixelConfig config,
+                            void* buffer,
+                            size_t rowBytes = 0,
+                            uint32_t pixelOpsFlags = 0) SK_OVERRIDE;
 
-    /**
-     * @return the texture associated with the rendertarget, may be NULL.
-     */
-    GrTexture* asTexture() {return fTexture;}
+    virtual void writePixels(int left, int top, int width, int height,
+                             GrPixelConfig config,
+                             const void* buffer,
+                             size_t rowBytes = 0,
+                             uint32_t pixelOpsFlags = 0) SK_OVERRIDE;
 
+    // GrRenderTarget
     /**
      * If this RT is multisampled, this is the multisample buffer
      * @return the 3D API's handle to this object (e.g. FBO ID in OpenGL)
      */
-    virtual intptr_t getRenderTargetHandle() const = 0;
+    virtual GrBackendObject getRenderTargetHandle() const = 0;
 
     /**
      * If this RT is multisampled, this is the buffer it is resolved to.
      * Otherwise, same as getRenderTargetHandle().
-     * (In GL a separate FBO ID is used for the msaa and resolved buffers)
+     * (In GL a separate FBO ID is used for the MSAA and resolved buffers)
      * @return the 3D API's handle to this object (e.g. FBO ID in OpenGL)
      */
-    virtual intptr_t getRenderTargetResolvedHandle() const = 0;
+    virtual GrBackendObject getRenderTargetResolvedHandle() const = 0;
 
     /**
-     * @return true if the render target is multisampled, false otherwise
+     * @return true if the surface is multisampled, false otherwise
      */
-    bool isMultisampled() const { return 0 != fSampleCnt; }
+    bool isMultisampled() const { return 0 != fDesc.fSampleCnt; }
 
     /**
      * @return the number of samples-per-pixel or zero if non-MSAA.
      */
-    int numSamples() const { return fSampleCnt; }
+    int numSamples() const { return fDesc.fSampleCnt; }
 
     /**
      * Call to indicate the multisample contents were modified such that the
      * render target needs to be resolved before it can be used as texture. Gr
      * tracks this for its own drawing and thus this only needs to be called
-     * when the render target has been modified outside of Gr. Only meaningful
-     * for Gr-created RT/Textures and Platform RT/Textures created with the
-     * kGrCanResolve flag.
+     * when the render target has been modified outside of Gr. This has no
+     * effect on wrapped backend render targets.
+     *
      * @param rect  a rect bounding the area needing resolve. NULL indicates
      *              the whole RT needs resolving.
      */
@@ -120,43 +121,8 @@
      */
     void resolve();
 
-    // GrResource overrides
-    virtual size_t sizeInBytes() const;
-
-    /**
-     * Reads a rectangle of pixels from the render target.
-     * @param left          left edge of the rectangle to read (inclusive)
-     * @param top           top edge of the rectangle to read (inclusive)
-     * @param width         width of rectangle to read in pixels.
-     * @param height        height of rectangle to read in pixels.
-     * @param config        the pixel config of the destination buffer
-     * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
-     *                      means rows are tightly packed.
-     *
-     * @return true if the read succeeded, false if not. The read can fail
-     *              because of an unsupported pixel config.
-     */
-    bool readPixels(int left, int top, int width, int height,
-                    GrPixelConfig config, void* buffer, size_t rowBytes);
-
-    /**
-     * Copy the src pixels [buffer, rowbytes, pixelconfig] into the render
-     * target at the specified rectangle.
-     * @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 the rectangle from.
-     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
-     *                      means rows are tightly packed.
-     */
-    void writePixels(int left, int top, int width, int height,
-                     GrPixelConfig config, const void* buffer, size_t rowBytes);
-
     // a MSAA RT may require explicit resolving , it may auto-resolve (e.g. FBO
-    // 0 in GL), or be unresolvable because the client didn't give us the 
+    // 0 in GL), or be unresolvable because the client didn't give us the
     // resolve destination.
     enum ResolveType {
         kCanResolve_ResolveType,
@@ -173,42 +139,38 @@
 
 protected:
     GrRenderTarget(GrGpu* gpu,
+                   bool isWrapped,
                    GrTexture* texture,
-                   int width,
-                   int height,
-                   GrPixelConfig config,
-                   int sampleCnt)
-        : INHERITED(gpu)
+                   const GrTextureDesc& desc,
+                   GrSurfaceOrigin origin)
+        : INHERITED(gpu, isWrapped, desc, origin)
         , fStencilBuffer(NULL)
-        , fTexture(texture)
-        , fWidth(width)
-        , fHeight(height)
-        , fConfig(config)
-        , fSampleCnt(sampleCnt) {
+        , fTexture(texture) {
         fResolveRect.setLargestInverted();
     }
 
     friend class GrTexture;
-    // When a texture unrefs an owned rendertarget this func
-    // removes the back pointer. This could be done called from 
+    // When a texture unrefs an owned render target this func
+    // removes the back pointer. This could be called from
     // texture's destructor but would have to be done in derived
-    // class. By the time of texture base destructor it has already
+    // classes. By the time of texture base destructor it has already
     // lost its pointer to the rt.
     void onTextureReleaseRenderTarget() {
         GrAssert(NULL != fTexture);
         fTexture = NULL;
     }
 
+    // override of GrResource
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
+
 private:
     GrStencilBuffer*  fStencilBuffer;
     GrTexture*        fTexture; // not ref'ed
-    int               fWidth;
-    int               fHeight;
-    GrPixelConfig     fConfig;
-    int               fSampleCnt;
+
     GrIRect           fResolveRect;
 
-    typedef GrResource INHERITED;
+    typedef GrSurface INHERITED;
 };
 
 #endif
diff --git a/include/gpu/GrResource.h b/include/gpu/GrResource.h
index e003353..72e6928 100644
--- a/include/gpu/GrResource.h
+++ b/include/gpu/GrResource.h
@@ -12,17 +12,18 @@
 
 #include "GrRefCnt.h"
 
+#include "SkTInternalLList.h"
+
 class GrGpu;
 class GrContext;
+class GrResourceEntry;
 
+/**
+ * Base class for the GPU resources created by a GrContext.
+ */
 class GrResource : public GrRefCnt {
 public:
-    explicit GrResource(GrGpu* gpu);
-
-    virtual ~GrResource() {
-        // subclass should have released this.
-        GrAssert(!isValid());
-    }
+    SK_DECLARE_INST_COUNT(GrResource)
 
     /**
      * Frees the resource in the underlying 3D API. It must be safe to call this
@@ -54,33 +55,61 @@
      *
      * @return the size of the buffer in bytes
      */
-     virtual size_t sizeInBytes() const = 0;
+    virtual size_t sizeInBytes() const = 0;
 
-     /**
-      * Retrieves the context that owns the resource. Note that it is possible
-      * for this to return NULL. When resources have been release()ed or
-      * abandon()ed they no longer have an unknowning context. Destroying a
-      * GrContext automatically releases all its resources.
-      */
-     const GrContext* getContext() const;
-     GrContext* getContext();
+    /**
+     * Retrieves the context that owns the resource. Note that it is possible
+     * for this to return NULL. When resources have been release()ed or
+     * abandon()ed they no longer have an owning context. Destroying a
+     * GrContext automatically releases all its resources.
+     */
+    const GrContext* getContext() const;
+    GrContext* getContext();
+
+    void setCacheEntry(GrResourceEntry* cacheEntry) { fCacheEntry = cacheEntry; }
+    GrResourceEntry* getCacheEntry() { return fCacheEntry; }
+
+    void incDeferredRefCount() const { GrAssert(fDeferredRefCount >= 0); ++fDeferredRefCount; }
+    void decDeferredRefCount() const { GrAssert(fDeferredRefCount > 0); --fDeferredRefCount; }
 
 protected:
-
-    virtual void onRelease() = 0;
-    virtual void onAbandon() = 0;
+    /**
+     * isWrapped indicates we have wrapped a client-created backend resource in a GrResource. If it
+     * is true then the client is responsible for the lifetime of the underlying backend resource.
+     * Otherwise, our onRelease() should free the resource.
+     */
+    GrResource(GrGpu* gpu, bool isWrapped);
+    virtual ~GrResource();
 
     GrGpu* getGpu() const { return fGpu; }
 
+    // Derived classes should always call their parent class' onRelease
+    // and onAbandon methods in their overrides.
+    virtual void onRelease() {};
+    virtual void onAbandon() {};
+
+    bool isInCache() const { return NULL != fCacheEntry; }
+    bool isWrapped() const { return kWrapped_Flag & fFlags; }
+
 private:
-    GrResource(); // unimpl
+#if GR_DEBUG
+    friend class GrGpu; // for assert in GrGpu to access getGpu
+#endif
 
-    GrGpu* fGpu; // not reffed. This can outlive the GrGpu.
+    // We're in an internal doubly linked list
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResource);
 
-    friend class GrGpu; // GrGpu manages list of resources.
+    GrGpu*              fGpu;               // not reffed. The GrGpu can be deleted while there
+                                            // are still live GrResources. It will call
+                                            // release() on all such resources in its
+                                            // destructor.
+    GrResourceEntry*    fCacheEntry;        // NULL if not in cache
+    mutable int         fDeferredRefCount;  // How many references in deferred drawing buffers.
 
-    GrResource* fNext;      // dl-list of resources per-GrGpu
-    GrResource* fPrevious;
+    enum Flags {
+        kWrapped_Flag = 0x1,
+    };
+    uint32_t         fFlags;
 
     typedef GrRefCnt INHERITED;
 };
diff --git a/include/gpu/GrSamplerState.h b/include/gpu/GrSamplerState.h
deleted file mode 100644
index 50a6cc9..0000000
--- a/include/gpu/GrSamplerState.h
+++ /dev/null
@@ -1,260 +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 GrSamplerState_DEFINED
-#define GrSamplerState_DEFINED
-
-#include "GrTypes.h"
-#include "GrMatrix.h"
-
-#define MAX_KERNEL_WIDTH 25
-
-class GrSamplerState {
-public:
-    enum Filter {
-        /**
-         * Read the closest src texel to the sample position
-         */
-        kNearest_Filter,
-        /**
-         * Blend between closest 4 src texels to sample position (tent filter)
-         */
-        kBilinear_Filter,
-        /**
-         * Average of 4 bilinear filterings spaced +/- 1 texel from sample
-         * position in x and y. Intended for averaging 16 texels in a downsample
-         * pass. (rasterizing such that texture samples fall exactly halfway
-         * between texels in x and y spaced 4 texels apart.) Only supported
-         * on shader backends.
-         */
-        k4x4Downsample_Filter,
-        /**
-         * Apply a separable convolution kernel.
-         */
-        kConvolution_Filter,
-        /**
-         * Apply a dilate filter (max over a 1D radius).
-         */
-        kDilate_Filter,
-        /**
-         * Apply an erode filter (min over a 1D radius).
-         */
-        kErode_Filter,
-
-        kDefault_Filter = kNearest_Filter
-    };
-
-    /**
-     * The intepretation of the texture matrix depends on the sample mode. The
-     * texture matrix is applied both when the texture coordinates are explicit
-     * and  when vertex positions are used as texture  coordinates. In the latter
-     * case the texture matrix is applied to the pre-view-matrix position 
-     * values.
-     *
-     * kNormal_SampleMode
-     *  The post-matrix texture coordinates are in normalize space with (0,0) at
-     *  the top-left and (1,1) at the bottom right.
-     * kRadial_SampleMode
-     *  The matrix specifies the radial gradient parameters.
-     *  (0,0) in the post-matrix space is center of the radial gradient.
-     * kRadial2_SampleMode
-     *   Matrix transforms to space where first circle is centered at the
-     *   origin. The second circle will be centered (x, 0) where x may be 
-     *   0 and is provided by setRadial2Params. The post-matrix space is 
-     *   normalized such that 1 is the second radius - first radius.
-     * kSweepSampleMode
-     *  The angle from the origin of texture coordinates in post-matrix space
-     *  determines the gradient value.
-     */
-    enum SampleMode {
-        kNormal_SampleMode,     //!< sample color directly
-        kRadial_SampleMode,     //!< treat as radial gradient
-        kRadial2_SampleMode,    //!< treat as 2-point radial gradient
-        kSweep_SampleMode,      //!< treat as sweep gradient
-
-        kDefault_SampleMode = kNormal_SampleMode
-    };
-
-    /**
-     * Describes how a texture is sampled when coordinates are outside the
-     * texture border
-     */
-    enum WrapMode {
-        kClamp_WrapMode,
-        kRepeat_WrapMode,
-        kMirror_WrapMode,
-
-        kDefault_WrapMode = kClamp_WrapMode
-    };
-
-    /**
-     * For the filters which perform more than one texture sample (convolution,
-     * erode, dilate), this determines the direction in which the texture
-     * coordinates will be incremented.
-     */
-    enum FilterDirection {
-        kX_FilterDirection,
-        kY_FilterDirection,
-
-        kDefault_FilterDirection = kX_FilterDirection,
-    };
-    /**
-     * Default sampler state is set to clamp, use normal sampling mode, be
-     * unfiltered, and use identity matrix.
-     */
-    GrSamplerState()
-    : fRadial2CenterX1()
-    , fRadial2Radius0()
-    , fRadial2PosRoot() {
-        this->reset();
-    }
-
-    WrapMode getWrapX() const { return fWrapX; }
-    WrapMode getWrapY() const { return fWrapY; }
-    FilterDirection getFilterDirection() const { return fFilterDirection; }
-    SampleMode getSampleMode() const { return fSampleMode; }
-    const GrMatrix& getMatrix() const { return fMatrix; }
-    const GrRect& getTextureDomain() const { return fTextureDomain; }
-    bool hasTextureDomain() const {return SkIntToScalar(0) != fTextureDomain.right();}
-    Filter getFilter() const { return fFilter; }
-    int getKernelWidth() const { return fKernelWidth; }
-    const float* getKernel() const { return fKernel; }
-    bool swapsRAndB() const { return fSwapRAndB; }
-
-    bool isGradient() const {
-        return  kRadial_SampleMode == fSampleMode ||
-                kRadial2_SampleMode == fSampleMode ||
-                kSweep_SampleMode == fSampleMode;
-    }
-
-    void setWrapX(WrapMode mode) { fWrapX = mode; }
-    void setWrapY(WrapMode mode) { fWrapY = mode; }
-    void setSampleMode(SampleMode mode) { fSampleMode = mode; }
-    void setFilterDirection(FilterDirection mode) { fFilterDirection = mode; }
-    
-    /**
-     * Access the sampler's matrix. See SampleMode for explanation of
-     * relationship between the matrix and sample mode.
-     */
-    GrMatrix* matrix() { return &fMatrix; }
-
-    /**
-     * Sets the sampler's texture coordinate domain to a 
-     * custom rectangle, rather than the default (0,1).
-     * This option is currently only supported with kClamp_WrapMode
-     */
-    void setTextureDomain(const GrRect& textureDomain) { fTextureDomain = textureDomain; }
-
-    /**
-     * Swaps the R and B components when reading from the texture. Has no effect
-     * if the texture is alpha only.
-     */
-    void setRAndBSwap(bool swap) { fSwapRAndB = swap; }
-
-    /**
-     *  Multiplies the current sampler matrix  a matrix
-     *
-     *  After this call M' = M*m where M is the old matrix, m is the parameter
-     *  to this function, and M' is the new matrix. (We consider points to
-     *  be column vectors so tex cood vector t is transformed by matrix X as 
-     *  t' = X*t.)
-     *
-     *  @param matrix   the matrix used to modify the matrix.
-     */
-    void preConcatMatrix(const GrMatrix& matrix) { fMatrix.preConcat(matrix); }
-
-    /**
-     * Sets filtering type.
-     * @param filter    type of filtering to apply
-     */
-    void setFilter(Filter filter) { fFilter = filter; }
-
-    void reset(WrapMode wrapXAndY,
-               Filter filter,
-               FilterDirection direction,
-               const GrMatrix& matrix) {
-        fWrapX = wrapXAndY;
-        fWrapY = wrapXAndY;
-        fSampleMode = kDefault_SampleMode;
-        fFilter = filter;
-        fFilterDirection = direction;
-        fMatrix = matrix;
-        fTextureDomain.setEmpty();
-        fSwapRAndB = false;
-    }
-    void reset(WrapMode wrapXAndY, Filter filter, const GrMatrix& matrix) {
-        this->reset(wrapXAndY, filter, kDefault_FilterDirection, matrix);
-    }
-    void reset(WrapMode wrapXAndY,
-               Filter filter) {
-        this->reset(wrapXAndY, filter, kDefault_FilterDirection, GrMatrix::I());
-    }
-    void reset(const GrMatrix& matrix) {
-        this->reset(kDefault_WrapMode, kDefault_Filter, kDefault_FilterDirection, matrix);
-    }
-    void reset() {
-        this->reset(kDefault_WrapMode, kDefault_Filter, kDefault_FilterDirection, GrMatrix::I());
-    }
-
-    GrScalar getRadial2CenterX1() const { return fRadial2CenterX1; }
-    GrScalar getRadial2Radius0() const { return fRadial2Radius0; }
-    bool     isRadial2PosRoot() const { return SkToBool(fRadial2PosRoot); }
-    // do the radial gradient params lead to a linear (rather than quadratic)
-    // equation.
-    bool radial2IsDegenerate() const { return GR_Scalar1 == fRadial2CenterX1; }
-
-    /**
-     * Sets the parameters for kRadial2_SampleMode. The texture
-     * matrix must be set so that the first point is at (0,0) and the second
-     * point lies on the x-axis. The second radius minus the first is 1 unit.
-     * The additional parameters to define the gradient are specified by this
-     * function.
-     */
-    void setRadial2Params(GrScalar centerX1, GrScalar radius0, bool posRoot) {
-        fRadial2CenterX1 = centerX1;
-        fRadial2Radius0 = radius0;
-        fRadial2PosRoot = posRoot;
-    }
-
-    void setConvolutionParams(int kernelWidth, const float* kernel) {
-        GrAssert(kernelWidth >= 0 && kernelWidth <= MAX_KERNEL_WIDTH);
-        fKernelWidth = kernelWidth;
-        if (NULL != kernel) {
-            memcpy(fKernel, kernel, kernelWidth * sizeof(float));
-        }
-    }
-
-    void setMorphologyRadius(int radius) {
-        GrAssert(radius >= 0 && radius <= MAX_KERNEL_WIDTH);
-        fKernelWidth = radius;
-    }
-
-private:
-    WrapMode            fWrapX : 8;
-    WrapMode            fWrapY : 8;
-    FilterDirection     fFilterDirection : 8;
-    SampleMode          fSampleMode : 8;
-    Filter              fFilter : 8;
-    GrMatrix            fMatrix;
-    bool                fSwapRAndB;
-    GrRect              fTextureDomain;
-
-    // these are undefined unless fSampleMode == kRadial2_SampleMode
-    GrScalar            fRadial2CenterX1;
-    GrScalar            fRadial2Radius0;
-    SkBool8             fRadial2PosRoot;
-
-    // These are undefined unless fFilter == kConvolution_Filter
-    uint8_t             fKernelWidth;
-    float               fKernel[MAX_KERNEL_WIDTH];
-};
-
-#endif
-
diff --git a/include/gpu/GrScalar.h b/include/gpu/GrScalar.h
deleted file mode 100644
index 4ffc4ca..0000000
--- a/include/gpu/GrScalar.h
+++ /dev/null
@@ -1,49 +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 GrScalar_DEFINED
-#define GrScalar_DEFINED
-
-#include "GrTypes.h"
-#include "SkScalar.h"
-
-#define GR_Int32Min         SK_NaN32
-#define GR_Int32Max         SK_MaxS32
-
-#define GR_Fixed1           SK_Fixed1
-#define GR_FixedHalf        SK_FixedHalf
-#define GrIntToFixed(a)     SkIntToFixed(a)
-#define GrFixedToFloat(a)   SkFixedToFloat(a)
-#define GrFixedFloorToInt(a)    SkFixedFloor(a)
-
-#define GrScalar            SkScalar
-#define GR_Scalar1          SK_Scalar1
-#define GR_ScalarHalf       SK_ScalarHalf
-#define GR_ScalarMin        SK_ScalarMin
-#define GR_ScalarMax        SK_ScalarMax
-
-#define GrIntToScalar(a)    SkIntToScalar(a)
-#define GrScalarHalf(a)     SkScalarHalf(a)
-#define GrScalarAve(a,b)    SkScalarAve(a,b)
-#define GrMul(a,b)          SkScalarMul(a,b) // deprecated, prefer GrScalarMul
-#define GrScalarMul(a,b)    SkScalarMul(a,b)
-#define GrScalarDiv(a,b)    SkScalarDiv(a, b)
-#define GrScalarToFloat(a)  SkScalarToFloat(a)
-#define GrFloatToScalar(a)  SkScalarToFloat(a)
-#define GrIntToScalar(a)    SkIntToScalar(a)
-#define GrScalarAbs(a)      SkScalarAbs(a)
-#define GrScalarIsInt(a)    SkScalarIsInt(a)
-#define GrScalarMax(a,b)    SkScalarMax(a,b)
-#define GrScalarFloorToInt(a)   SkScalarFloor(a)
-#define GrScalarCeilToInt(a)    SkScalarCeil(a)
-#define GrFixedToScalar(a)  SkFixedToScalar(a)
-
-#endif
-
diff --git a/include/gpu/GrSurface.h b/include/gpu/GrSurface.h
new file mode 100644
index 0000000..3429cc6
--- /dev/null
+++ b/include/gpu/GrSurface.h
@@ -0,0 +1,121 @@
+/*
+ * 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 GrSurface_DEFINED
+#define GrSurface_DEFINED
+
+#include "GrTypes.h"
+#include "GrResource.h"
+
+class GrTexture;
+class GrRenderTarget;
+
+class GrSurface : public GrResource {
+public:
+    SK_DECLARE_INST_COUNT(GrSurface);
+
+    /**
+     * Retrieves the width of the surface.
+     *
+     * @return the width in texels
+     */
+    int width() const { return fDesc.fWidth; }
+
+    /**
+     * Retrieves the height of the surface.
+     *
+     * @return the height in texels
+     */
+    int height() const { return fDesc.fHeight; }
+
+    GrSurfaceOrigin origin() const {
+        GrAssert(kTopLeft_GrSurfaceOrigin == fOrigin || kBottomLeft_GrSurfaceOrigin == fOrigin);
+        return fOrigin;
+    }
+
+    /**
+     * Retrieves the pixel config specified when the surface was created.
+     * For render targets this can be kUnknown_GrPixelConfig
+     * if client asked us to render to a target that has a pixel
+     * config that isn't equivalent with one of our configs.
+     */
+    GrPixelConfig config() const { return fDesc.fConfig; }
+
+    /**
+     * Return the descriptor describing the surface
+     */
+    const GrTextureDesc& desc() const { return fDesc; }
+
+    /**
+     * @return the texture associated with the surface, may be NULL.
+     */
+    virtual GrTexture* asTexture() = 0;
+    virtual const GrTexture* asTexture() const = 0;
+
+    /**
+     * @return the render target underlying this surface, may be NULL.
+     */
+    virtual GrRenderTarget* asRenderTarget() = 0;
+    virtual const GrRenderTarget* asRenderTarget() const = 0;
+
+    /**
+     * Reads a rectangle of pixels from the surface.
+     * @param left          left edge of the rectangle to read (inclusive)
+     * @param top           top edge of the rectangle to read (inclusive)
+     * @param width         width of rectangle to read in pixels.
+     * @param height        height of rectangle to read in pixels.
+     * @param config        the pixel config of the destination buffer
+     * @param buffer        memory to read the rectangle into.
+     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
+     *                      packed.
+     * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum.
+     *
+     * @return true if the read succeeded, false if not. The read can fail because of an unsupported
+     *              pixel config.
+     */
+    virtual bool readPixels(int left, int top, int width, int height,
+                            GrPixelConfig config,
+                            void* buffer,
+                            size_t rowBytes = 0,
+                            uint32_t pixelOpsFlags = 0) = 0;
+
+    /**
+     * Copy the src pixels [buffer, rowbytes, pixelconfig] into the surface at the specified
+     * rectangle.
+     * @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 the rectangle from.
+     * @param rowBytes      number of bytes between consecutive rows. Zero means rows are tightly
+     *                      packed.
+     * @param pixelOpsFlags See the GrContext::PixelOpsFlags enum.
+     */
+    virtual void writePixels(int left, int top, int width, int height,
+                             GrPixelConfig config,
+                             const void* buffer,
+                             size_t rowBytes = 0,
+                             uint32_t pixelOpsFlags = 0) = 0;
+
+protected:
+    GrSurface(GrGpu* gpu, bool isWrapped, const GrTextureDesc& desc, GrSurfaceOrigin origin)
+    : INHERITED(gpu, isWrapped)
+    , fDesc(desc)
+    , fOrigin(origin) {
+    }
+
+    GrTextureDesc fDesc;
+
+private:
+    GrSurfaceOrigin fOrigin;
+
+    typedef GrResource INHERITED;
+};
+
+#endif // GrSurface_DEFINED
diff --git a/include/gpu/GrTBackendEffectFactory.h b/include/gpu/GrTBackendEffectFactory.h
new file mode 100644
index 0000000..7ea7e39
--- /dev/null
+++ b/include/gpu/GrTBackendEffectFactory.h
@@ -0,0 +1,73 @@
+/*
+ * 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 GrTBackendEffectFactory_DEFINED
+#define GrTBackendEffectFactory_DEFINED
+
+#include "GrBackendEffectFactory.h"
+#include "GrEffectStage.h"
+
+/**
+ * Implements GrBackendEffectFactory for a GrEffect subclass as a singleton.
+ */
+template <typename EffectClass>
+class GrTBackendEffectFactory : public GrBackendEffectFactory {
+
+public:
+    typedef typename EffectClass::GLEffect GLEffect;
+
+    /** Returns a human-readable name that is accessible via GrEffect or
+        GrGLEffect and is consistent between the two of them.
+     */
+    virtual const char* name() const SK_OVERRIDE { return EffectClass::Name(); }
+
+    /** Returns a value that identifies the GLSL shader code generated by
+        a GrEffect. This enables caching of generated shaders. Part of the
+        id identifies the GrEffect subclass. The remainder is based
+        on the aspects of the GrEffect object's configuration that affect
+        GLSL code generation. */
+    virtual EffectKey glEffectKey(const GrEffectStage& stage,
+                                  const GrGLCaps& caps) const SK_OVERRIDE {
+        GrAssert(kIllegalEffectClassID != fEffectClassID);
+        EffectKey effectKey = GLEffect::GenKey(stage, caps);
+        EffectKey textureKey = GLEffect::GenTextureKey(stage.getEffect(), caps);
+#if GR_DEBUG
+        static const EffectKey kIllegalIDMask = (uint16_t) (~((1U << kEffectKeyBits) - 1));
+        GrAssert(!(kIllegalIDMask & effectKey));
+
+        static const EffectKey kIllegalTextureKeyMask = (uint16_t) (~((1U << kTextureKeyBits) - 1));
+        GrAssert(!(kIllegalTextureKeyMask & textureKey));
+#endif
+        return fEffectClassID | (textureKey << kEffectKeyBits) | effectKey;
+    }
+
+    /** Returns a new instance of the appropriate *GL* implementation class
+        for the given GrEffect; caller is responsible for deleting
+        the object. */
+    virtual GLEffect* createGLInstance(const GrEffectRef& effect) const SK_OVERRIDE {
+        return SkNEW_ARGS(GLEffect, (*this, effect));
+    }
+
+    /** This class is a singleton. This function returns the single instance.
+     */
+    static const GrBackendEffectFactory& getInstance() {
+        static SkAlignedSTStorage<1, GrTBackendEffectFactory> gInstanceMem;
+        static const GrTBackendEffectFactory* gInstance;
+        if (!gInstance) {
+            gInstance = SkNEW_PLACEMENT(gInstanceMem.get(),
+                                        GrTBackendEffectFactory);
+        }
+        return *gInstance;
+    }
+
+protected:
+    GrTBackendEffectFactory() {
+        fEffectClassID = GenID() << (kEffectKeyBits + kTextureKeyBits) ;
+    }
+};
+
+#endif
diff --git a/include/gpu/GrTemplates.h b/include/gpu/GrTemplates.h
deleted file mode 100644
index 63e43ee..0000000
--- a/include/gpu/GrTemplates.h
+++ /dev/null
@@ -1,69 +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 GrTemplates_DEFINED
-#define GrTemplates_DEFINED
-
-#include "GrNoncopyable.h"
-
-/**
- *  Use to cast a ptr to a different type, and maintain strict-aliasing
- */
-template <typename Dst, typename Src> Dst GrTCast(Src src) {
-    union {
-        Src src;
-        Dst dst;
-    } data;
-    data.src = src;
-    return data.dst;
-}
-
-/**
- * saves value of T* in and restores in destructor
- * e.g.:
- * {
- *      GrAutoTPtrValueRestore<int*> autoCountRestore;
- *      if (useExtra) {
- *          autoCountRestore.save(&fCount);
- *          fCount += fExtraCount;
- *      }
- *      ...
- * }  // fCount is restored
- */
-template <typename T> class GrAutoTPtrValueRestore : public GrNoncopyable {
-public:
-    GrAutoTPtrValueRestore() : fPtr(NULL), fVal() {}
-    
-    GrAutoTPtrValueRestore(T* ptr) {
-        fPtr = ptr;
-        if (NULL != ptr) {
-            fVal = *ptr;
-        }
-    }
-    
-    ~GrAutoTPtrValueRestore() {
-        if (NULL != fPtr) {
-            *fPtr = fVal;
-        }
-    }
-    
-    // restores previously saved value (if any) and saves value for passed T*
-    void save(T* ptr) {
-        if (NULL != fPtr) {
-            *fPtr = fVal;
-        }
-        fPtr = ptr;
-        fVal = *ptr;
-    }
-private:
-    T* fPtr;
-    T  fVal;
-};
-
-#endif
diff --git a/include/gpu/GrTextContext.h b/include/gpu/GrTextContext.h
index 5983e35..fc7735d 100644
--- a/include/gpu/GrTextContext.h
+++ b/include/gpu/GrTextContext.h
@@ -11,9 +11,9 @@
 #ifndef GrTextContext_DEFINED
 #define GrTextContext_DEFINED
 
+#include "GrContext.h"
 #include "GrGlyph.h"
 #include "GrPaint.h"
-#include "GrMatrix.h"
 
 struct GrGpuTextVertex;
 class GrContext;
@@ -23,9 +23,7 @@
 
 class GrTextContext {
 public:
-    GrTextContext(GrContext*,
-                  const GrPaint& paint,
-                  const GrMatrix* extMatrix = NULL);
+    GrTextContext(GrContext*, const GrPaint&);
     ~GrTextContext();
 
     void drawPackedGlyph(GrGlyph::PackedID, GrFixed left, GrFixed top,
@@ -39,7 +37,6 @@
     GrContext*      fContext;
     GrDrawTarget*   fDrawTarget;
 
-    GrMatrix        fExtMatrix;
     GrFontScaler*   fScaler;
     GrTextStrike*   fStrike;
 
@@ -53,16 +50,14 @@
         kDefaultRequestedVerts   = kDefaultRequestedGlyphs * 4,
     };
 
-    GrGpuTextVertex* fVertices;
+    GrGpuTextVertex*        fVertices;
 
-    int32_t     fMaxVertices;
-    GrTexture*  fCurrTexture;
-    int         fCurrVertex;
+    int32_t                 fMaxVertices;
+    GrTexture*              fCurrTexture;
+    int                     fCurrVertex;
 
-    GrIRect     fClipRect;
-    GrMatrix    fOrigViewMatrix;    // restore previous viewmatrix
+    GrIRect                 fClipRect;
+    GrContext::AutoMatrix   fAutoMatrix;
 };
 
 #endif
-
-
diff --git a/include/gpu/GrTexture.h b/include/gpu/GrTexture.h
index 8274140..94d5788 100644
--- a/include/gpu/GrTexture.h
+++ b/include/gpu/GrTexture.h
@@ -6,85 +6,70 @@
  * found in the LICENSE file.
  */
 
-
-
 #ifndef GrTexture_DEFINED
 #define GrTexture_DEFINED
 
-#include "GrResource.h"
+#include "GrSurface.h"
 
 class GrRenderTarget;
+class GrResourceKey;
+class GrTextureParams;
 
-class GrTexture : public GrResource {
+class GrTexture : public GrSurface {
 
 public:
+    SK_DECLARE_INST_COUNT(GrTexture)
+    // from GrResource
     /**
-     * Retrieves the width of the texture.
-     *
-     * @return the width in texels
+     * Informational texture flags
      */
-    int width() const { return fWidth; }
+    enum FlagBits {
+        kFirstBit = (kLastPublic_GrTextureFlagBit << 1),
 
-    /**
-     * Retrieves the height of the texture.
-     *
-     * @return the height in texels
-     */
-    int height() const { return fHeight; }
+        /**
+         * This texture should be returned to the texture cache when
+         * it is no longer reffed
+         */
+        kReturnToCache_FlagBit        = kFirstBit,
+    };
 
-    /**
-     * Convert from texels to normalized texture coords for POT textures
-     * only.
-     */
-    GrFixed normalizeFixedX(GrFixed x) const { GrAssert(GrIsPow2(fWidth));
-                                               return x >> fShiftFixedX; }
-    GrFixed normalizeFixedY(GrFixed y) const { GrAssert(GrIsPow2(fHeight));
-                                               return y >> fShiftFixedY; }
-
-    /**
-     * Retrieves the pixel config specified when the texture was created.
-     */
-    GrPixelConfig config() const { return fConfig; }
+    void setFlag(GrTextureFlags flags) {
+        fDesc.fFlags = fDesc.fFlags | flags;
+    }
+    void resetFlag(GrTextureFlags flags) {
+        fDesc.fFlags = fDesc.fFlags & ~flags;
+    }
+    bool isSetFlag(GrTextureFlags flags) const {
+        return 0 != (fDesc.fFlags & flags);
+    }
 
     /**
      *  Approximate number of bytes used by the texture
      */
-    virtual size_t sizeInBytes() const {
-        return (size_t) fWidth * fHeight * GrBytesPerPixel(fConfig);
+    virtual size_t sizeInBytes() const SK_OVERRIDE {
+        return (size_t) fDesc.fWidth *
+                        fDesc.fHeight *
+                        GrBytesPerPixel(fDesc.fConfig);
     }
 
-    /**
-     * Read a rectangle of pixels from the texture.
-     * @param left          left edge of the rectangle to read (inclusive)
-     * @param top           top edge of the rectangle to read (inclusive)
-     * @param width         width of rectangle to read in pixels.
-     * @param height        height of rectangle to read in pixels.
-     * @param config        the pixel config of the destination buffer
-     * @param buffer        memory to read the rectangle into.
-     * @param rowBytes      number of bytes bewtween consecutive rows. Zero
-     *                      means rows are tightly packed.
-     *
-     * @return true if the read succeeded, false if not. The read can fail
-     *              because of a unsupported pixel config.
-     */
-    bool readPixels(int left, int top, int width, int height,
-                    GrPixelConfig config, void* buffer,
-                    size_t rowBytes);
+    // GrSurface overrides
+    virtual bool readPixels(int left, int top, int width, int height,
+                            GrPixelConfig config,
+                            void* buffer,
+                            size_t rowBytes = 0,
+                            uint32_t pixelOpsFlags = 0) SK_OVERRIDE;
+
+    virtual void writePixels(int left, int top, int width, int height,
+                             GrPixelConfig config,
+                             const void* buffer,
+                             size_t rowBytes = 0,
+                             uint32_t pixelOpsFlags = 0) SK_OVERRIDE;
 
     /**
-     * Writes a rectangle of pixels to the 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 bewtween consecutive rows. Zero
-     *                      means rows are tightly packed.
+     * @return this texture
      */
-    void writePixels(int left, int top, int width, int height,
-                     GrPixelConfig config, const void* buffer,
-                     size_t rowBytes);
+    virtual GrTexture* asTexture() SK_OVERRIDE { return this; }
+    virtual const GrTexture* asTexture() const SK_OVERRIDE { return this; }
 
     /**
      * Retrieves the render target underlying this texture that can be passed to
@@ -93,7 +78,26 @@
      * @return    handle to render target or NULL if the texture is not a
      *            render target
      */
-    GrRenderTarget* asRenderTarget() { return fRenderTarget; }
+    virtual GrRenderTarget* asRenderTarget() SK_OVERRIDE {
+        return fRenderTarget;
+    }
+    virtual const GrRenderTarget* asRenderTarget() const SK_OVERRIDE {
+        return fRenderTarget;
+    }
+
+    // GrTexture
+    /**
+     * Convert from texels to normalized texture coords for POT textures
+     * only.
+     */
+    GrFixed normalizeFixedX(GrFixed x) const {
+        GrAssert(GrIsPow2(fDesc.fWidth));
+        return x >> fShiftFixedX;
+    }
+    GrFixed normalizeFixedY(GrFixed y) const {
+        GrAssert(GrIsPow2(fDesc.fHeight));
+        return y >> fShiftFixedY;
+    }
 
     /**
      * Removes the reference on the associated GrRenderTarget held by this
@@ -104,57 +108,62 @@
 
     /**
      *  Return the native ID or handle to the texture, depending on the
-     *  platform. e.g. on opengl, return the texture ID.
+     *  platform. e.g. on OpenGL, return the texture ID.
      */
-    virtual intptr_t getTextureHandle() const = 0;
+    virtual GrBackendObject getTextureHandle() const = 0;
+
+    /**
+     *  Call this when the state of the native API texture object is
+     *  altered directly, without being tracked by skia.
+     */
+    virtual void invalidateCachedState() = 0;
 
 #if GR_DEBUG
     void validate() const {
         this->INHERITED::validate();
+
+        this->validateDesc();
     }
 #else
     void validate() const {}
 #endif
+    static GrResourceKey ComputeKey(const GrGpu* gpu,
+                                    const GrTextureParams* params,
+                                    const GrTextureDesc& desc,
+                                    const GrCacheID& cacheID);
+    static GrResourceKey ComputeScratchKey(const GrTextureDesc& desc);
+    static bool NeedsResizing(const GrResourceKey& key);
+    static bool NeedsFiltering(const GrResourceKey& key);
 
 protected:
     GrRenderTarget* fRenderTarget; // texture refs its rt representation
                                    // base class cons sets to NULL
                                    // subclass cons can create and set
 
-    GrTexture(GrGpu* gpu,
-              int width,
-              int height,
-              GrPixelConfig config)
-    : INHERITED(gpu)
-    , fRenderTarget(NULL)
-    , fWidth(width)
-    , fHeight(height)
-    , fConfig(config) {
+    GrTexture(GrGpu* gpu, bool isWrapped, const GrTextureDesc& desc, GrSurfaceOrigin origin)
+    : INHERITED(gpu, isWrapped, desc, origin)
+    , fRenderTarget(NULL) {
+
         // only make sense if alloc size is pow2
-        fShiftFixedX = 31 - Gr_clz(fWidth);
-        fShiftFixedY = 31 - Gr_clz(fHeight);
+        fShiftFixedX = 31 - SkCLZ(fDesc.fWidth);
+        fShiftFixedY = 31 - SkCLZ(fDesc.fHeight);
     }
 
     // GrResource overrides
-    virtual void onRelease() {
-        this->releaseRenderTarget();
-    }
+    virtual void onRelease() SK_OVERRIDE;
+    virtual void onAbandon() SK_OVERRIDE;
 
-    virtual void onAbandon();
+    void validateDesc() const;
 
 private:
-    int fWidth;
-    int fHeight;
-
     // these two shift a fixed-point value into normalized coordinates
     // for this texture if the texture is power of two sized.
-    int      fShiftFixedX;
-    int      fShiftFixedY;
+    int                 fShiftFixedX;
+    int                 fShiftFixedY;
 
-    GrPixelConfig fConfig;
+    virtual void internal_dispose() const SK_OVERRIDE;
 
-    typedef GrResource INHERITED;
+    typedef GrSurface INHERITED;
 };
 
 #endif
-
diff --git a/include/gpu/GrTextureAccess.h b/include/gpu/GrTextureAccess.h
new file mode 100644
index 0000000..b03e6e6
--- /dev/null
+++ b/include/gpu/GrTextureAccess.h
@@ -0,0 +1,190 @@
+/*
+ * 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 GrTextureAccess_DEFINED
+#define GrTextureAccess_DEFINED
+
+#include "GrNoncopyable.h"
+#include "SkRefCnt.h"
+#include "SkShader.h"
+
+class GrTexture;
+
+/**
+ * Represents the filtering and tile modes used to access a texture. It is mostly used with
+ * GrTextureAccess (defined below). Also, some of the texture cache methods require knowledge about
+ * filtering and tiling to perform a cache lookup. If it wasn't for this latter usage this would
+ * be folded into GrTextureAccess.
+ */
+class GrTextureParams {
+public:
+    GrTextureParams() {
+        this->reset();
+    }
+
+    GrTextureParams(SkShader::TileMode tileXAndY, bool bilerp) {
+        this->reset(tileXAndY, bilerp);
+    }
+
+    GrTextureParams(SkShader::TileMode tileModes[2], bool bilerp) {
+        this->reset(tileModes, bilerp);
+    }
+
+    GrTextureParams(const GrTextureParams& params) {
+        *this = params;
+    }
+
+    GrTextureParams& operator= (const GrTextureParams& params) {
+        fTileModes[0] = params.fTileModes[0];
+        fTileModes[1] = params.fTileModes[1];
+        fBilerp = params.fBilerp;
+        return *this;
+    }
+
+    void reset() {
+        this->reset(SkShader::kClamp_TileMode, false);
+    }
+
+    void reset(SkShader::TileMode tileXAndY, bool bilerp) {
+        fTileModes[0] = fTileModes[1] = tileXAndY;
+        fBilerp = bilerp;
+    }
+
+    void reset(SkShader::TileMode tileModes[2], bool bilerp) {
+        fTileModes[0] = tileModes[0];
+        fTileModes[1] = tileModes[1];
+        fBilerp = bilerp;
+    }
+
+    void setClampNoFilter() {
+        fTileModes[0] = fTileModes[1] = SkShader::kClamp_TileMode;
+        fBilerp = false;
+    }
+
+    void setClamp() {
+        fTileModes[0] = fTileModes[1] = SkShader::kClamp_TileMode;
+    }
+
+    void setBilerp(bool bilerp) { fBilerp = bilerp; }
+
+    void setTileModeX(const SkShader::TileMode tm) { fTileModes[0] = tm; }
+    void setTileModeY(const SkShader::TileMode tm) { fTileModes[1] = tm; }
+    void setTileModeXAndY(const SkShader::TileMode tm) { fTileModes[0] = fTileModes[1] = tm; }
+
+    SkShader::TileMode getTileModeX() const { return fTileModes[0]; }
+
+    SkShader::TileMode getTileModeY() const { return fTileModes[1]; }
+
+    bool isTiled() const {
+        return SkShader::kClamp_TileMode != fTileModes[0] ||
+               SkShader::kClamp_TileMode != fTileModes[1];
+    }
+
+    bool isBilerp() const { return fBilerp; }
+
+    bool operator== (const GrTextureParams& other) const {
+        return fTileModes[0] == other.fTileModes[0] &&
+               fTileModes[1] == other.fTileModes[1] &&
+               fBilerp == other.fBilerp;
+    }
+
+    bool operator!= (const GrTextureParams& other) const { return !(*this == other); }
+
+private:
+
+    SkShader::TileMode fTileModes[2];
+    bool               fBilerp;
+};
+
+/** A class representing the swizzle access pattern for a texture. Note that if the texture is
+ *  an alpha-only texture then the alpha channel is substituted for other components. Any mangling
+ *  to handle the r,g,b->a conversions for alpha textures is automatically included in the stage
+ *  key. However, if a GrEffect uses different swizzles based on its input then it must
+ *  consider that variation in its key-generation.
+ */
+class GrTextureAccess : GrNoncopyable {
+public:
+    /**
+     * A default GrTextureAccess must have reset() called on it in a GrEffect subclass's
+     * constructor if it will be accessible via GrEffect::textureAccess().
+     */
+    GrTextureAccess();
+
+    /**
+     * Uses the default swizzle, "rgba".
+     */
+    GrTextureAccess(GrTexture*, const GrTextureParams&);
+    explicit GrTextureAccess(GrTexture*,
+                             bool bilerp = false,
+                             SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode);
+
+    /**
+     * swizzle must be a string between one and four (inclusive) characters containing only 'r',
+     * 'g', 'b',  and/or 'a'.
+     */
+    GrTextureAccess(GrTexture*, const char* swizzle, const GrTextureParams&);
+    GrTextureAccess(GrTexture*,
+                    const char* swizzle,
+                    bool bilerp = false,
+                    SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode);
+
+    void reset(GrTexture*, const GrTextureParams&);
+    void reset(GrTexture*,
+               bool bilerp = false,
+               SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode);
+    void reset(GrTexture*, const char* swizzle, const GrTextureParams&);
+    void reset(GrTexture*,
+               const char* swizzle,
+               bool bilerp = false,
+               SkShader::TileMode tileXAndY = SkShader::kClamp_TileMode);
+
+    bool operator== (const GrTextureAccess& other) const {
+#if GR_DEBUG
+        // below assumes all chars in fSwizzle are initialized even if string is < 4 chars long.
+        GrAssert(memcmp(fSwizzle, other.fSwizzle, sizeof(fSwizzle)-1) ==
+                 strcmp(fSwizzle, other.fSwizzle));
+#endif
+        return fParams == other.fParams &&
+               (fTexture.get() == other.fTexture.get()) &&
+               (0 == memcmp(fSwizzle, other.fSwizzle, sizeof(fSwizzle)-1));
+    }
+
+    bool operator!= (const GrTextureAccess& other) const { return !(*this == other); }
+
+    GrTexture* getTexture() const { return fTexture.get(); }
+
+    /**
+     * Returns a string representing the swizzle. The string is is null-terminated.
+     */
+    const char* getSwizzle() const { return fSwizzle; }
+
+    enum {
+        kR_SwizzleFlag = 0x1,
+        kG_SwizzleFlag = 0x2,
+        kB_SwizzleFlag = 0x4,
+        kA_SwizzleFlag = 0x8,
+
+        kRGB_SwizzleMask = (kR_SwizzleFlag |  kG_SwizzleFlag | kB_SwizzleFlag),
+    };
+
+    /** Returns a mask indicating which components are referenced in the swizzle. */
+    uint32_t swizzleMask() const { return fSwizzleMask; }
+
+    const GrTextureParams& getParams() const { return fParams; }
+
+private:
+    void setSwizzle(const char*);
+
+    GrTextureParams         fParams;
+    SkAutoTUnref<GrTexture> fTexture;
+    uint32_t                fSwizzleMask;
+    char                    fSwizzle[5];
+
+    typedef GrNoncopyable INHERITED;
+};
+
+#endif
diff --git a/include/gpu/GrTypes.h b/include/gpu/GrTypes.h
index f6809d6..055750d 100644
--- a/include/gpu/GrTypes.h
+++ b/include/gpu/GrTypes.h
@@ -13,6 +13,7 @@
 
 #include "SkTypes.h"
 #include "GrConfig.h"
+#include "SkMath.h"
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -58,11 +59,11 @@
 #define GrIsALIGN4(n)   SkIsAlign4(n)
 
 template <typename T> const T& GrMin(const T& a, const T& b) {
-	return (a < b) ? a : b;
+    return (a < b) ? a : b;
 }
 
 template <typename T> const T& GrMax(const T& a, const T& b) {
-	return (b < a) ? a : b;
+    return (b < a) ? a : b;
 }
 
 // compile time versions of min/max
@@ -79,27 +80,33 @@
 static inline uint32_t GrUIDivRoundUp(uint32_t x, uint32_t y) {
     return (x + (y-1)) / y;
 }
-static inline size_t GrSizeDivRoundUp(size_t x, uint32_t y) {
+static inline size_t GrSizeDivRoundUp(size_t x, size_t y) {
     return (x + (y-1)) / y;
 }
 
+// compile time, evaluates Y multiple times
+#define GR_CT_DIV_ROUND_UP(X, Y) (((X) + ((Y)-1)) / (Y))
+
 /**
  *  align up
  */
 static inline uint32_t GrUIAlignUp(uint32_t x, uint32_t alignment) {
     return GrUIDivRoundUp(x, alignment) * alignment;
 }
-static inline uint32_t GrSizeAlignUp(size_t x, uint32_t alignment) {
+static inline size_t GrSizeAlignUp(size_t x, size_t alignment) {
     return GrSizeDivRoundUp(x, alignment) * alignment;
 }
 
+// compile time, evaluates A multiple times
+#define GR_CT_ALIGN_UP(X, A) (GR_CT_DIV_ROUND_UP((X),(A)) * (A))
+
 /**
  * amount of pad needed to align up
  */
 static inline uint32_t GrUIAlignUpPad(uint32_t x, uint32_t alignment) {
     return (alignment - x % alignment) % alignment;
 }
-static inline size_t GrSizeAlignUpPad(size_t x, uint32_t alignment) {
+static inline size_t GrSizeAlignUpPad(size_t x, size_t alignment) {
     return (alignment - x % alignment) % alignment;
 }
 
@@ -109,7 +116,7 @@
 static inline uint32_t GrUIAlignDown(uint32_t x, uint32_t alignment) {
     return (x / alignment) * alignment;
 }
-static inline uint32_t GrSizeAlignDown(size_t x, uint32_t alignment) {
+static inline size_t GrSizeAlignDown(size_t x, uint32_t alignment) {
     return (x / alignment) * alignment;
 }
 
@@ -131,11 +138,6 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
- *  Return the number of leading zeros in n
- */
-extern int Gr_clz(uint32_t n);
-
-/**
  *  Return true if n is a power of 2
  */
 static inline bool GrIsPow2(unsigned n) {
@@ -146,12 +148,12 @@
  *  Return the next power of 2 >= n.
  */
 static inline uint32_t GrNextPow2(uint32_t n) {
-    return n ? (1 << (32 - Gr_clz(n - 1))) : 1;
+    return n ? (1 << (32 - SkCLZ(n - 1))) : 1;
 }
 
 static inline int GrNextPow2(int n) {
     GrAssert(n >= 0); // this impl only works for non-neg.
-    return n ? (1 << (32 - Gr_clz(n - 1))) : 1;
+    return n ? (1 << (32 - SkCLZ(n - 1))) : 1;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -180,16 +182,15 @@
 /**
  * Possible 3D APIs that may be used by Ganesh.
  */
-enum GrEngine {
-    kOpenGL_Shaders_GrEngine,
-    kOpenGL_Fixed_GrEngine,
+enum GrBackend {
+    kOpenGL_GrBackend,
 };
 
 /**
- * Engine-specific 3D context handle
+ * Backend-specific 3D context handle
  *      GrGLInterface* for OpenGL. If NULL will use the default GL interface.
  */
-typedef intptr_t GrPlatform3DContext;
+typedef intptr_t GrBackendContext;
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -203,44 +204,46 @@
 * Geometric primitives used for drawing.
 */
 enum GrPrimitiveType {
-    kTriangles_PrimitiveType,
-    kTriangleStrip_PrimitiveType,
-    kTriangleFan_PrimitiveType,
-    kPoints_PrimitiveType,
-    kLines_PrimitiveType,     // 1 pix wide only
-    kLineStrip_PrimitiveType  // 1 pix wide only
+    kTriangles_GrPrimitiveType,
+    kTriangleStrip_GrPrimitiveType,
+    kTriangleFan_GrPrimitiveType,
+    kPoints_GrPrimitiveType,
+    kLines_GrPrimitiveType,     // 1 pix wide only
+    kLineStrip_GrPrimitiveType  // 1 pix wide only
 };
 
 static inline bool GrIsPrimTypeLines(GrPrimitiveType type) {
-    return kLines_PrimitiveType == type || kLineStrip_PrimitiveType == type;
+    return kLines_GrPrimitiveType == type || kLineStrip_GrPrimitiveType == type;
 }
 
 static inline bool GrIsPrimTypeTris(GrPrimitiveType type) {
-    return kTriangles_PrimitiveType == type     ||
-           kTriangleStrip_PrimitiveType == type ||
-           kTriangleFan_PrimitiveType == type;
+    return kTriangles_GrPrimitiveType == type     ||
+           kTriangleStrip_GrPrimitiveType == type ||
+           kTriangleFan_GrPrimitiveType == type;
 }
 
 /**
  * Coeffecients for alpha-blending.
  */
 enum GrBlendCoeff {
-    kZero_BlendCoeff,    //<! 0
-    kOne_BlendCoeff,     //<! 1
-    kSC_BlendCoeff,      //<! src color
-    kISC_BlendCoeff,     //<! one minus src color
-    kDC_BlendCoeff,      //<! dst color
-    kIDC_BlendCoeff,     //<! one minus dst color
-    kSA_BlendCoeff,      //<! src alpha
-    kISA_BlendCoeff,     //<! one minus src alpha
-    kDA_BlendCoeff,      //<! dst alpha
-    kIDA_BlendCoeff,     //<! one minus dst alpha
-    kConstC_BlendCoeff,  //<! constant color
-    kIConstC_BlendCoeff, //<! one minus constant color
-    kConstA_BlendCoeff,  //<! constant color alpha
-    kIConstA_BlendCoeff, //<! one minus constant color alpha
+    kInvalid_GrBlendCoeff = -1,
 
-    kPublicBlendCoeffCount
+    kZero_GrBlendCoeff,    //<! 0
+    kOne_GrBlendCoeff,     //<! 1
+    kSC_GrBlendCoeff,      //<! src color
+    kISC_GrBlendCoeff,     //<! one minus src color
+    kDC_GrBlendCoeff,      //<! dst color
+    kIDC_GrBlendCoeff,     //<! one minus dst color
+    kSA_GrBlendCoeff,      //<! src alpha
+    kISA_GrBlendCoeff,     //<! one minus src alpha
+    kDA_GrBlendCoeff,      //<! dst alpha
+    kIDA_GrBlendCoeff,     //<! one minus dst alpha
+    kConstC_GrBlendCoeff,  //<! constant color
+    kIConstC_GrBlendCoeff, //<! one minus constant color
+    kConstA_GrBlendCoeff,  //<! constant color alpha
+    kIConstA_GrBlendCoeff, //<! one minus constant color alpha
+
+    kPublicGrBlendCoeffCount
 };
 
 /**
@@ -268,13 +271,6 @@
 
 /**
  * Pixel configurations.
- *
- * Unpremultiplied configs are intended for converting pixel data in and out
- * from skia. Surfaces with these configs have limited support. As an input
- * (GrPaint texture) the corresponding GrSamplerState must have its filter set
- * to kNearest_Filter. Otherwise, the draw will fail. When the render target
- * has an unpremultiplied config draws must use blend coeffs 1,0 (AKA src-mode).
- * Other coeffs will cause the draw to fail.
  */
 enum GrPixelConfig {
     kUnknown_GrPixelConfig,
@@ -286,52 +282,39 @@
      */
     kRGBA_4444_GrPixelConfig,
     /**
-     * Premultiplied. Byte order is r,g,b,a
+     * Premultiplied. Byte order is r,g,b,a.
      */
-    kRGBA_8888_PM_GrPixelConfig,
+    kRGBA_8888_GrPixelConfig,
     /**
-     * Unpremultiplied. Byte order is r,g,b,a
+     * Premultiplied. Byte order is b,g,r,a.
      */
-    kRGBA_8888_UPM_GrPixelConfig,
-    /**
-     * Premultiplied. Byte order is b,g,r,a
-     */
-    kBGRA_8888_PM_GrPixelConfig,
-    /**
-     * Unpremultiplied. Byte order is b,g,r,a
-     */
-    kBGRA_8888_UPM_GrPixelConfig,
+    kBGRA_8888_GrPixelConfig,
 
     kGrPixelConfigCount
 };
 
-// Aliases for pixel configs that match skia's byte order
+// Aliases for pixel configs that match skia's byte order.
 #ifndef SK_CPU_LENDIAN
     #error "Skia gpu currently assumes little endian"
 #endif
 #if 24 == SK_A32_SHIFT && 16 == SK_R32_SHIFT && \
      8 == SK_G32_SHIFT &&  0 == SK_B32_SHIFT
-    static const GrPixelConfig kSkia8888_PM_GrPixelConfig = kBGRA_8888_PM_GrPixelConfig;
-    static const GrPixelConfig kSkia8888_UPM_GrPixelConfig = kBGRA_8888_UPM_GrPixelConfig;
+    static const GrPixelConfig kSkia8888_GrPixelConfig = kBGRA_8888_GrPixelConfig;
 #elif 24 == SK_A32_SHIFT && 16 == SK_B32_SHIFT && \
        8 == SK_G32_SHIFT &&  0 == SK_R32_SHIFT
-    static const GrPixelConfig kSkia8888_PM_GrPixelConfig = kRGBA_8888_PM_GrPixelConfig;
-    static const GrPixelConfig kSkia8888_UPM_GrPixelConfig = kRGBA_8888_UPM_GrPixelConfig;
+    static const GrPixelConfig kSkia8888_GrPixelConfig = kRGBA_8888_GrPixelConfig;
 #else
     #error "SK_*32_SHIFT values must correspond to GL_BGRA or GL_RGBA format."
 #endif
 
-// WebKit is relying on this old name for the native skia PM config. This will
-// be deleted ASAP because it is so similar to kRGBA_PM_8888_GrPixelConfig but
-// has a different interpretation when skia is compiled BGRA.
-static const GrPixelConfig kRGBA_8888_GrPixelConfig = kSkia8888_PM_GrPixelConfig;
+// This alias is deprecated and will be removed.
+static const GrPixelConfig kSkia8888_PM_GrPixelConfig = kSkia8888_GrPixelConfig;
 
 // Returns true if the pixel config has 8bit r,g,b,a components in that byte
 // order
 static inline bool GrPixelConfigIsRGBA8888(GrPixelConfig config) {
     switch (config) {
-        case kRGBA_8888_PM_GrPixelConfig:
-        case kRGBA_8888_UPM_GrPixelConfig:
+        case kRGBA_8888_GrPixelConfig:
             return true;
         default:
             return false;
@@ -342,8 +325,7 @@
 // order
 static inline bool GrPixelConfigIsBGRA8888(GrPixelConfig config) {
     switch (config) {
-        case kBGRA_8888_PM_GrPixelConfig:
-        case kBGRA_8888_UPM_GrPixelConfig:
+        case kBGRA_8888_GrPixelConfig:
             return true;
         default:
             return false;
@@ -353,10 +335,8 @@
 // Returns true if the pixel config is 32 bits per pixel
 static inline bool GrPixelConfigIs32Bit(GrPixelConfig config) {
     switch (config) {
-        case kRGBA_8888_PM_GrPixelConfig:
-        case kRGBA_8888_UPM_GrPixelConfig:
-        case kBGRA_8888_PM_GrPixelConfig:
-        case kBGRA_8888_UPM_GrPixelConfig:
+        case kRGBA_8888_GrPixelConfig:
+        case kBGRA_8888_GrPixelConfig:
             return true;
         default:
             return false;
@@ -367,14 +347,10 @@
 // swapped if such a config exists. Otherwise, kUnknown_GrPixelConfig
 static inline GrPixelConfig GrPixelConfigSwapRAndB(GrPixelConfig config) {
     switch (config) {
-        case kBGRA_8888_PM_GrPixelConfig:
-            return kRGBA_8888_PM_GrPixelConfig;
-        case kBGRA_8888_UPM_GrPixelConfig:
-            return kRGBA_8888_UPM_GrPixelConfig;
-        case kRGBA_8888_PM_GrPixelConfig:
-            return kBGRA_8888_PM_GrPixelConfig;
-        case kRGBA_8888_UPM_GrPixelConfig:
-            return kBGRA_8888_UPM_GrPixelConfig;
+        case kBGRA_8888_GrPixelConfig:
+            return kRGBA_8888_GrPixelConfig;
+        case kRGBA_8888_GrPixelConfig:
+            return kBGRA_8888_GrPixelConfig;
         default:
             return kUnknown_GrPixelConfig;
     }
@@ -388,10 +364,8 @@
         case kRGB_565_GrPixelConfig:
         case kRGBA_4444_GrPixelConfig:
             return 2;
-        case kRGBA_8888_PM_GrPixelConfig:
-        case kRGBA_8888_UPM_GrPixelConfig:
-        case kBGRA_8888_PM_GrPixelConfig:
-        case kBGRA_8888_UPM_GrPixelConfig:
+        case kRGBA_8888_GrPixelConfig:
+        case kBGRA_8888_GrPixelConfig:
             return 4;
         default:
             return 0;
@@ -407,20 +381,6 @@
     }
 }
 
-/**
- * Premultiplied alpha is the usual for skia. Therefore, configs that are
- * ambiguous (alpha-only or color-only) are considered premultiplied.
- */
-static inline bool GrPixelConfigIsUnpremultiplied(GrPixelConfig config) {
-    switch (config) {
-        case kRGBA_8888_UPM_GrPixelConfig:
-        case kBGRA_8888_UPM_GrPixelConfig:
-            return true;
-        default:
-            return false;
-    }
-}
-
 static inline bool GrPixelConfigIsAlphaOnly(GrPixelConfig config) {
     switch (config) {
         case kAlpha_8_GrPixelConfig:
@@ -431,12 +391,6 @@
 }
 
 /**
- * DEPRECATED: This will be removed as soon as WebKit no longer references
- * this (former) enum value.
- */
-static const int kNone_GrAALevel = 0;
-
-/**
  * Optional bitfield flags that can be passed to createTexture.
  */
 enum GrTextureFlags {
@@ -445,7 +399,7 @@
      * Creates a texture that can be rendered to as a GrRenderTarget. Use
      * GrTexture::asRenderTarget() to access.
      */
-    kRenderTarget_GrTextureFlagBit  = 0x1,  
+    kRenderTarget_GrTextureFlagBit  = 0x1,
     /**
      * By default all render targets have an associated stencil buffer that
      * may be required for path filling. This flag overrides stencil buffer
@@ -457,6 +411,9 @@
      * Hint that the CPU may modify this texture after creation.
      */
     kDynamicUpdate_GrTextureFlagBit = 0x4,
+
+    kDummy_GrTextureFlagBit,
+    kLastPublic_GrTextureFlagBit = kDummy_GrTextureFlagBit-1,
 };
 
 GR_MAKE_BITFIELD_OPS(GrTextureFlags)
@@ -469,47 +426,99 @@
 };
 
 /**
+ * Some textures will be stored such that the upper and left edges of the content meet at the
+ * the origin (in texture coord space) and for other textures the lower and left edges meet at
+ * the origin. Render-targets are always consistent with the convention of the underlying
+ * backend API to make it easier to mix native backend rendering with Skia rendering.
+ */
+
+enum GrSurfaceOrigin {
+    kBottomLeft_GrSurfaceOrigin,
+    kTopLeft_GrSurfaceOrigin,
+};
+
+/**
  * Describes a texture to be created.
  */
 struct GrTextureDesc {
+    GrTextureDesc()
+    : fFlags(kNone_GrTextureFlags)
+    , fWidth(0)
+    , fHeight(0)
+    , fConfig(kUnknown_GrPixelConfig)
+    , fSampleCnt(0) {
+    }
+
     GrTextureFlags         fFlags;  //!< bitfield of TextureFlags
     int                    fWidth;  //!< Width of the texture
     int                    fHeight; //!< Height of the texture
 
     /**
-     * Format of source data of the texture. Not guaraunteed to be the same as
+     * Format of source data of the texture. Not guaranteed to be the same as
      * internal format used by 3D API.
      */
     GrPixelConfig          fConfig;
-    
+
     /**
      * The number of samples per pixel or 0 to disable full scene AA. This only
      * applies if the kRenderTarget_GrTextureFlagBit is set. The actual number
      * of samples may not exactly match the request. The request will be rounded
      * up to the next supported sample count, or down if it is larger than the
-     * max supportex count.
+     * max supported count.
      */
-    union {
-        /**
-         * This field has two names for legacy reasons. Use the fSampleCnt name.
-         * fAALevel is deprecated and will be removed as soon as WebKit no
-         * longer uses it.
-         */
-        int fSampleCnt;
-        int fAALevel;
-    };
+    int                    fSampleCnt;
 };
 
 /**
- * Set Operations used to construct clips.
+ * GrCacheID is used create and find cached GrResources (e.g. GrTextures). The ID has two parts:
+ * the domain and the key. Domains simply allow multiple clients to use 0-based indices as their
+ * cache key without colliding. The key uniquely identifies a GrResource within the domain.
+ * Users of the cache must obtain a domain via GenerateDomain().
  */
-enum GrSetOp {
-    kReplace_SetOp,
-    kIntersect_SetOp,
-    kUnion_SetOp,
-    kXor_SetOp,
-    kDifference_SetOp,
-    kReverseDifference_SetOp,
+struct GrCacheID {
+public:
+    typedef uint8_t  Domain;
+
+    struct Key {
+        union {
+            uint8_t  fData8[16];
+            uint32_t fData32[4];
+            uint64_t fData64[2];
+        };
+    };
+
+    /**
+     * A default cache ID is invalid; a set method must be called before the object is used.
+     */
+    GrCacheID() { fDomain = kInvalid_Domain; }
+
+    /**
+     * Initialize the cache ID to a domain and key.
+     */
+    GrCacheID(Domain domain, const Key& key) {
+        GrAssert(kInvalid_Domain != domain);
+        this->reset(domain, key);
+    }
+
+    void reset(Domain domain, const Key& key) {
+        fDomain = domain;
+        memcpy(&fKey, &key, sizeof(Key));
+    }
+
+    /** Has this been initialized to a valid domain */
+    bool isValid() const { return kInvalid_Domain != fDomain; }
+
+    const Key& getKey() const { GrAssert(this->isValid()); return fKey; }
+    Domain getDomain() const { GrAssert(this->isValid()); return fDomain; }
+
+    /** Creates a new unique ID domain. */
+    static Domain GenerateDomain();
+
+private:
+    Key             fKey;
+    Domain          fDomain;
+
+    static const Domain kInvalid_Domain = 0;
 };
 
 /**
@@ -553,63 +562,16 @@
     return gNumPoints[cmd];
 }
 
-/**
- * Path filling rules
- */
-enum GrPathFill {
-    kWinding_PathFill,
-    kEvenOdd_PathFill,
-    kInverseWinding_PathFill,
-    kInverseEvenOdd_PathFill,
-    kHairLine_PathFill,
-
-    kPathFillCount
-};
-
-static inline GrPathFill GrNonInvertedFill(GrPathFill fill) {
-    static const GrPathFill gNonInvertedFills[] = {
-        kWinding_PathFill, // kWinding_PathFill
-        kEvenOdd_PathFill, // kEvenOdd_PathFill
-        kWinding_PathFill, // kInverseWinding_PathFill
-        kEvenOdd_PathFill, // kInverseEvenOdd_PathFill
-        kHairLine_PathFill,// kHairLine_PathFill
-    };
-    GR_STATIC_ASSERT(0 == kWinding_PathFill);
-    GR_STATIC_ASSERT(1 == kEvenOdd_PathFill);
-    GR_STATIC_ASSERT(2 == kInverseWinding_PathFill);
-    GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill);
-    GR_STATIC_ASSERT(4 == kHairLine_PathFill);
-    GR_STATIC_ASSERT(5 == kPathFillCount);
-    return gNonInvertedFills[fill];
-}
-
-static inline bool GrIsFillInverted(GrPathFill fill) {
-    static const bool gIsFillInverted[] = {
-        false, // kWinding_PathFill
-        false, // kEvenOdd_PathFill
-        true,  // kInverseWinding_PathFill
-        true,  // kInverseEvenOdd_PathFill
-        false, // kHairLine_PathFill
-    };
-    GR_STATIC_ASSERT(0 == kWinding_PathFill);
-    GR_STATIC_ASSERT(1 == kEvenOdd_PathFill);
-    GR_STATIC_ASSERT(2 == kInverseWinding_PathFill);
-    GR_STATIC_ASSERT(3 == kInverseEvenOdd_PathFill);
-    GR_STATIC_ASSERT(4 == kHairLine_PathFill);
-    GR_STATIC_ASSERT(5 == kPathFillCount);
-    return gIsFillInverted[fill];
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 // opaque type for 3D API object handles
-typedef intptr_t GrPlatform3DObject;
+typedef intptr_t GrBackendObject;
 
 /**
  * Gr can wrap an existing texture created by the client with a GrTexture
  * object. The client is responsible for ensuring that the texture lives at
- * least as long as the GrTexture object wrapping it. We require the client to 
- * explicitly provide information about the texture, such as width, height, 
+ * least as long as the GrTexture object wrapping it. We require the client to
+ * explicitly provide information about the texture, such as width, height,
  * and pixel config, rather than querying the 3D APIfor these values. We expect
  * these to be immutable even if the 3D API doesn't require this (OpenGL).
  *
@@ -622,13 +584,15 @@
  * count Gr will create an MSAA buffer that resolves into the texture. Gr auto-
  * resolves when it reads from the texture. The client can explictly resolve
  * using the GrRenderTarget interface.
+ *
+ * Note: These flags currently form a subset of GrTexture's flags.
  */
 
-enum GrPlatformTextureFlags {
+enum GrBackendTextureFlags {
     /**
      * No flags enabled
      */
-    kNone_GrPlatformTextureFlag              = 0x0,
+    kNone_GrBackendTextureFlag             = kNone_GrTextureFlags,
     /**
      * Indicates that the texture is also a render target, and thus should have
      * a GrRenderTarget object.
@@ -636,13 +600,14 @@
      * D3D (future): client must have created the texture with flags that allow
      * it to be used as a render target.
      */
-    kRenderTarget_GrPlatformTextureFlag      = 0x1,
+    kRenderTarget_GrBackendTextureFlag     = kRenderTarget_GrTextureFlagBit,
 };
-GR_MAKE_BITFIELD_OPS(GrPlatformTextureFlags)
+GR_MAKE_BITFIELD_OPS(GrBackendTextureFlags)
 
-struct GrPlatformTextureDesc {
-    GrPlatformTextureDesc() { memset(this, 0, sizeof(*this)); }
-    GrPlatformTextureFlags          fFlags;
+struct GrBackendTextureDesc {
+    GrBackendTextureDesc() { memset(this, 0, sizeof(*this)); }
+    GrBackendTextureFlags           fFlags;
+    GrSurfaceOrigin                 fOrigin;
     int                             fWidth;         //<! width in pixels
     int                             fHeight;        //<! height in pixels
     GrPixelConfig                   fConfig;        //<! color format
@@ -655,7 +620,7 @@
      * Handle to the 3D API object.
      * OpenGL: Texture ID.
      */
-    GrPlatform3DObject              fTextureHandle;
+    GrBackendObject                 fTextureHandle;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -664,20 +629,20 @@
  * Gr can wrap an existing render target created by the client in the 3D API
  * with a GrRenderTarget object. The client is responsible for ensuring that the
  * underlying 3D API object lives at least as long as the GrRenderTarget object
- * wrapping it. We require the client to explicitly provide information about 
+ * wrapping it. We require the client to explicitly provide information about
  * the target, such as width, height, and pixel config rather than querying the
  * 3D API for these values. We expect these properties to be immutable even if
  * the 3D API doesn't require this (OpenGL).
  */
 
-struct GrPlatformRenderTargetDesc {
-    GrPlatformRenderTargetDesc() { memset(this, 0, sizeof(*this)); }
+struct GrBackendRenderTargetDesc {
+    GrBackendRenderTargetDesc() { memset(this, 0, sizeof(*this)); }
     int                             fWidth;         //<! width in pixels
     int                             fHeight;        //<! height in pixels
     GrPixelConfig                   fConfig;        //<! color format
     /**
      * The number of samples per pixel. Gr uses this to influence decisions
-     * about applying other forms of antialiasing.
+     * about applying other forms of anti-aliasing.
      */
     int                             fSampleCnt;
     /**
@@ -688,13 +653,27 @@
      * Handle to the 3D API object.
      * OpenGL: FBO ID
      */
-    GrPlatform3DObject              fRenderTargetHandle;
+    GrBackendObject                 fRenderTargetHandle;
 };
 
+///////////////////////////////////////////////////////////////////////////////
+// Legacy names that will be kept until WebKit can be updated.
+
+typedef GrBackend GrEngine;
+static const GrBackend kOpenGL_Shaders_GrEngine = kOpenGL_GrBackend;
+
+typedef GrBackendContext GrPlatform3DContext;
+
+typedef GrBackendObject GrPlatform3DObject;
+
+typedef GrBackendTextureFlags GrPlatformTextureFlags;
+static const GrBackendTextureFlags kNone_GrPlatformTextureFlag = kNone_GrBackendTextureFlag;
+static const GrBackendTextureFlags kRenderTarget_GrPlatformTextureFlag = kRenderTarget_GrBackendTextureFlag;
+
+typedef GrBackendTextureDesc GrPlatformTextureDesc;
+
+typedef GrBackendRenderTargetDesc GrPlatformRenderTargetDesc;
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// this is included only to make it easy to use this debugging facility
-#include "GrInstanceCounter.h"
-
 #endif
diff --git a/include/gpu/GrUserConfig.h b/include/gpu/GrUserConfig.h
index 657f74f..ff10044 100644
--- a/include/gpu/GrUserConfig.h
+++ b/include/gpu/GrUserConfig.h
@@ -22,8 +22,8 @@
 #endif
 
 /*
- *  To diagnose texture cache performance, define this to 1 if you want to see
- *  a log statement everytime we upload an image to create a texture.
+ * To diagnose texture cache performance, define this to 1 if you want to see
+ * a log statement everytime we upload an image to create a texture.
  */
 //#define GR_DUMP_TEXTURE_UPLOAD    1
 
@@ -36,6 +36,12 @@
 //#define GR_STATIC_RECT_VB 1
 
 /*
+ * This causes the GrContext to execute all draws immediately in the 3D API
+ * rather than internally queuing draws.
+ */
+//#define GR_DISABLE_DRAW_BUFFERING 1
+
+/*
  * This causes more aggressive shader optimization. May hurt performance if
  * switching shaders is expensive.
  */
@@ -48,16 +54,17 @@
  */
 //#define GR_GEOM_BUFFER_LOCK_THRESHOLD (1<<15)
 
+/**
+ * This gives a threshold in megabytes for the maximum size of the texture cache
+ * in vram. The value is only a default and can be overridden at runtime.
+ */
+//#define GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT 96
+
 ///////////////////////////////////////////////////////////////////////////////
 // Decide Ganesh types
 
-#define GR_SCALAR_IS_FIXED          0
-#define GR_SCALAR_IS_FLOAT          1
-
 #define GR_TEXT_SCALAR_IS_USHORT    0
 #define GR_TEXT_SCALAR_IS_FIXED     0
 #define GR_TEXT_SCALAR_IS_FLOAT     1
 
 #endif
-
-
diff --git a/include/gpu/SkGpuCanvas.h b/include/gpu/SkGpuCanvas.h
deleted file mode 100644
index 825c567..0000000
--- a/include/gpu/SkGpuCanvas.h
+++ /dev/null
@@ -1,57 +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 SkGpuCanvas_DEFINED
-#define SkGpuCanvas_DEFINED
-
-#include "SkCanvas.h"
-
-class GrContext;
-class GrRenderTarget;
-
-/**
- *  Subclass of canvas that creates devices compatible with the GrContext pass
- *  to the canvas' constructor.
- */
-class SkGpuCanvas : public SkCanvas {
-public:
-    /**
-     *  The GrContext object is reference counted. When passed to our
-     *  constructor, its reference count is incremented. In our destructor, the
-     *  GrGpu's reference count will be decremented.
-     *  GrRenderTarget represents the rendering destination in the underlying
-     *  3D API. Its reference count is incremented in the constructor and
-     *  decremented in the destructor.
-     */
-    explicit SkGpuCanvas(GrContext*, GrRenderTarget*);
-    virtual ~SkGpuCanvas();
-
-    /**
-     *  Override from SkCanvas. Returns true, and if not-null, sets size to
-     *  be the width/height of our viewport.
-     */
-    virtual bool getViewport(SkIPoint* size) const;
-
-#if 0
-    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
-                          SaveFlags flags = kARGB_ClipLayer_SaveFlag) {
-        return this->save(flags);
-    }
-#endif
-
-private:
-    GrContext* fContext;
-
-    typedef SkCanvas INHERITED;
-};
-
-#endif
-
-
diff --git a/include/gpu/SkGpuDevice.h b/include/gpu/SkGpuDevice.h
index 6663ed5..a6087c8 100644
--- a/include/gpu/SkGpuDevice.h
+++ b/include/gpu/SkGpuDevice.h
@@ -29,14 +29,11 @@
 public:
     /**
      *  New device that will create an offscreen renderTarget based on the
-     *  config, width, height.
-     *
-     *  usage is a special flag that should only be set by SkCanvas
-     *  internally.
+     *  config, width, height, and sampleCount. The device's storage will not
+     *  count against the GrContext's texture cache budget. The device's pixels
+     *  will be uninitialized.
      */
-    SkGpuDevice(GrContext*, SkBitmap::Config,
-                int width, int height,
-                SkDevice::Usage usage = SkDevice::kGeneral_Usage);
+    SkGpuDevice(GrContext*, SkBitmap::Config, int width, int height, int sampleCount = 0);
 
     /**
      *  New device that will render to the specified renderTarget.
@@ -54,13 +51,6 @@
 
     GrContext* context() const { return fContext; }
 
-    /**
-     *  Override from SkGpuDevice, so we can set our FBO to be the render target
-     *  The canvas parameter must be a SkGpuCanvas
-     */
-    virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&,
-                           const SkClipStack& clipStack) SK_OVERRIDE;
-
     virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE;
 
     // overrides from SkDevice
@@ -69,20 +59,22 @@
     virtual void writePixels(const SkBitmap& bitmap, int x, int y,
                              SkCanvas::Config8888 config8888) SK_OVERRIDE;
 
-    virtual void setMatrixClip(const SkMatrix& matrix, const SkRegion& clip,
-                               const SkClipStack&) SK_OVERRIDE;
-
     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 drawPath(const SkDraw&, const SkPath& path,
                           const SkPaint& paint, const SkMatrix* prePathMatrix,
                           bool pathIsMutable) SK_OVERRIDE;
     virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
                             const SkIRect* srcRectOrNull,
                             const SkMatrix&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawBitmapRect(const SkDraw&, const SkBitmap&,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint& paint) SK_OVERRIDE;
     virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
                             int x, int y, const SkPaint& paint);
     virtual void drawText(const SkDraw&, const void* text, size_t len,
@@ -104,111 +96,90 @@
 
     virtual void flush();
 
+    virtual void onAttachToCanvas(SkCanvas* canvas) SK_OVERRIDE;
+    virtual void onDetachFromCanvas() SK_OVERRIDE;
+
     /**
      * Make's this device's rendertarget current in the underlying 3D API.
      * Also implicitly flushes.
      */
     virtual void makeRenderTargetCurrent();
 
-    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
-                             const SkMatrix& ctm,
-                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+    virtual bool canHandleImageFilter(SkImageFilter*) SK_OVERRIDE;
+    virtual bool filterImage(SkImageFilter*, const SkBitmap&, const SkMatrix&,
+                             SkBitmap*, SkIPoint*) SK_OVERRIDE;
+
+    class SkAutoCachedTexture; // used internally
 
 protected:
-    typedef GrContext::TextureCacheEntry TexCache;
-    enum TexType {
-        kBitmap_TexType,
-        kDeviceRenderTarget_TexType,
-        kSaveLayerDeviceRenderTarget_TexType
-    };
-    TexCache lockCachedTexture(const SkBitmap& bitmap,
-                               const GrSamplerState* sampler,
-                               TexType type = kBitmap_TexType);
-    bool isBitmapInTextureCache(const SkBitmap& bitmap,
-                                const GrSamplerState& sampler) const;
-    void unlockCachedTexture(TexCache);
-
-    class SkAutoCachedTexture {
-    public:
-        SkAutoCachedTexture();
-        SkAutoCachedTexture(SkGpuDevice* device,
-                            const SkBitmap& bitmap,
-                            const GrSamplerState* sampler,
-                            GrTexture** texture);
-        ~SkAutoCachedTexture();
-
-        GrTexture* set(SkGpuDevice*, const SkBitmap&, const GrSamplerState*);
-
-    private:
-        SkGpuDevice*    fDevice;
-        TexCache        fTex;
-    };
-    friend class SkAutoTexCache;
-
     // overrides from SkDevice
     virtual bool onReadPixels(const SkBitmap& bitmap,
                               int x, int y,
                               SkCanvas::Config8888 config8888) SK_OVERRIDE;
 
-
 private:
     GrContext*      fContext;
 
     GrSkDrawProcs*  fDrawProcs;
 
-    // state for our offscreen render-target
-    TexCache            fCache;
-    GrTexture*          fTexture;
+    GrClipData      fClipData;
+
+    // state for our render-target
     GrRenderTarget*     fRenderTarget;
     bool                fNeedClear;
-    bool                fNeedPrepareRenderTarget;
 
     // called from rt and tex cons
-    void initFromRenderTarget(GrContext*, GrRenderTarget*);
+    void initFromRenderTarget(GrContext*, GrRenderTarget*, bool cached);
 
-    // doesn't set the texture/sampler/matrix state
-    // caller needs to null out GrPaint's texture if
-    // non-textured drawing is desired.
-    // Set constantColor to true if a constant color
-    // will be used.  This is an optimization, and can
-    // always be set to false. constantColor should
-    // never be true if justAlpha is true.
-    bool skPaint2GrPaintNoShader(const SkPaint& skPaint,
-                                 bool justAlpha,
-                                 GrPaint* grPaint,
-                                 bool constantColor);
-
-    // uses the SkShader to setup paint, act used to
-    // hold lock on cached texture and free it when
-    // destroyed.
-    // If there is no shader, constantColor will
-    // be passed to skPaint2GrPaintNoShader.  Otherwise
-    // it is ignored.
-    bool skPaint2GrPaintShader(const SkPaint& skPaint,
-                               SkAutoCachedTexture* act,
-                               const SkMatrix& ctm,
-                               GrPaint* grPaint,
-                               bool constantColor);
+    // used by createCompatibleDevice
+    SkGpuDevice(GrContext*, GrTexture* texture, bool needClear);
 
     // override from SkDevice
     virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
                                                int width, int height,
                                                bool isOpaque,
-                                               Usage usage);
+                                               Usage usage) SK_OVERRIDE;
 
     SkDrawProcs* initDrawForText(GrTextContext*);
     bool bindDeviceAsTexture(GrPaint* paint);
 
-    void prepareRenderTarget(const SkDraw&);
+    // sets the render target, clip, and matrix on GrContext. Use forceIdenity to override
+    // SkDraw's matrix and draw in device coords.
+    void prepareDraw(const SkDraw&, bool forceIdentity);
+
+    /**
+     * Implementation for both drawBitmap and drawBitmapRect.
+     */
+    void drawBitmapCommon(const SkDraw&,
+                          const SkBitmap& bitmap,
+                          const SkRect* srcRectPtr,
+                          const SkMatrix&,
+                          const SkPaint&);
+
+    /**
+     * Helper functions called by drawBitmapCommon. By the time these are called the SkDraw's
+     * matrix has already been set on GrContext
+     */
     bool shouldTileBitmap(const SkBitmap& bitmap,
-                          const GrSamplerState& sampler,
-                          const SkIRect* srcRectPtr,
-                          int* tileSize) const;
-    void internalDrawBitmap(const SkDraw&, const SkBitmap&,
-                            const SkIRect&, const SkMatrix&, GrPaint* grPaint);
+                          const GrTextureParams& sampler,
+                          const SkRect* srcRectPtr) const;
+    void internalDrawBitmap(const SkBitmap&,
+                            const SkRect&,
+                            const SkMatrix&,
+                            const GrTextureParams& params,
+                            GrPaint* grPaint);
+    void drawTiledBitmap(const SkBitmap& bitmap,
+                         const SkRect& srcRect,
+                         const SkMatrix& m,
+                         const GrTextureParams& params,
+                         GrPaint* grPaint);
+
+    /**
+     * Returns non-initialized instance.
+     */
+    GrTextContext* getTextContext();
 
     typedef SkDevice INHERITED;
 };
 
 #endif
-
diff --git a/include/gpu/SkGr.h b/include/gpu/SkGr.h
index f4dab53..cf37f47 100644
--- a/include/gpu/SkGr.h
+++ b/include/gpu/SkGr.h
@@ -17,15 +17,12 @@
 #include "GrTypes.h"
 #include "GrContext.h"
 #include "GrFontScaler.h"
-#include "GrClipIterator.h"
-#include "GrPath.h"
 
 // skia headers
 #include "SkBitmap.h"
 #include "SkPath.h"
 #include "SkPoint.h"
 #include "SkRegion.h"
-#include "SkShader.h"
 #include "SkClipStack.h"
 
 #if (GR_DEBUG && defined(SK_RELEASE)) || (GR_RELEASE && defined(SK_DEBUG))
@@ -35,24 +32,16 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Sk to Gr Type conversions
 
-GR_STATIC_ASSERT((int)GrSamplerState::kClamp_WrapMode == (int)SkShader::kClamp_TileMode);
-GR_STATIC_ASSERT((int)GrSamplerState::kRepeat_WrapMode ==(
-                 int)SkShader::kRepeat_TileMode);
-GR_STATIC_ASSERT((int)GrSamplerState::kMirror_WrapMode ==
-                 (int)SkShader::kMirror_TileMode);
-
-#define sk_tile_mode_to_grwrap(X) ((GrSamplerState::WrapMode)(X))
-
-GR_STATIC_ASSERT((int)kZero_BlendCoeff == (int)SkXfermode::kZero_Coeff);
-GR_STATIC_ASSERT((int)kOne_BlendCoeff  == (int)SkXfermode::kOne_Coeff);
-GR_STATIC_ASSERT((int)kSC_BlendCoeff   == (int)SkXfermode::kSC_Coeff);
-GR_STATIC_ASSERT((int)kISC_BlendCoeff  == (int)SkXfermode::kISC_Coeff);
-GR_STATIC_ASSERT((int)kDC_BlendCoeff   == (int)SkXfermode::kDC_Coeff);
-GR_STATIC_ASSERT((int)kIDC_BlendCoeff  == (int)SkXfermode::kIDC_Coeff);
-GR_STATIC_ASSERT((int)kSA_BlendCoeff   == (int)SkXfermode::kSA_Coeff);
-GR_STATIC_ASSERT((int)kISA_BlendCoeff  == (int)SkXfermode::kISA_Coeff);
-GR_STATIC_ASSERT((int)kDA_BlendCoeff   == (int)SkXfermode::kDA_Coeff);
-GR_STATIC_ASSERT((int)kIDA_BlendCoeff  == (int)SkXfermode::kIDA_Coeff);
+GR_STATIC_ASSERT((int)kZero_GrBlendCoeff == (int)SkXfermode::kZero_Coeff);
+GR_STATIC_ASSERT((int)kOne_GrBlendCoeff  == (int)SkXfermode::kOne_Coeff);
+GR_STATIC_ASSERT((int)kSC_GrBlendCoeff   == (int)SkXfermode::kSC_Coeff);
+GR_STATIC_ASSERT((int)kISC_GrBlendCoeff  == (int)SkXfermode::kISC_Coeff);
+GR_STATIC_ASSERT((int)kDC_GrBlendCoeff   == (int)SkXfermode::kDC_Coeff);
+GR_STATIC_ASSERT((int)kIDC_GrBlendCoeff  == (int)SkXfermode::kIDC_Coeff);
+GR_STATIC_ASSERT((int)kSA_GrBlendCoeff   == (int)SkXfermode::kSA_Coeff);
+GR_STATIC_ASSERT((int)kISA_GrBlendCoeff  == (int)SkXfermode::kISA_Coeff);
+GR_STATIC_ASSERT((int)kDA_GrBlendCoeff   == (int)SkXfermode::kDA_Coeff);
+GR_STATIC_ASSERT((int)kIDA_GrBlendCoeff  == (int)SkXfermode::kIDA_Coeff);
 
 #define sk_blend_to_grblend(X) ((GrBlendCoeff)(X))
 
@@ -69,109 +58,32 @@
 
 #include "SkColorPriv.h"
 
-class SkGr {
-public:
-    /**
-     *  Convert the SkBitmap::Config to the corresponding PixelConfig, or
-     *  kUnknown_PixelConfig if the conversion cannot be done.
-     */
-    static GrPixelConfig BitmapConfig2PixelConfig(SkBitmap::Config,
-                                                  bool isOpaque);
+/**
+ *  Convert the SkBitmap::Config to the corresponding PixelConfig, or
+ *  kUnknown_PixelConfig if the conversion cannot be done.
+ */
+GrPixelConfig SkBitmapConfig2GrPixelConfig(SkBitmap::Config);
 
-    static GrPixelConfig Bitmap2PixelConfig(const SkBitmap& bm) {
-        return BitmapConfig2PixelConfig(bm.config(), bm.isOpaque());
-    }
+static inline GrColor SkColor2GrColor(SkColor c) {
+    SkPMColor pm = SkPreMultiplyColor(c);
+    unsigned r = SkGetPackedR32(pm);
+    unsigned g = SkGetPackedG32(pm);
+    unsigned b = SkGetPackedB32(pm);
+    unsigned a = SkGetPackedA32(pm);
+    return GrColorPackRGBA(r, g, b, a);
+}
 
-    static GrColor SkColor2GrColor(SkColor c) {
-        SkPMColor pm = SkPreMultiplyColor(c);
-        unsigned r = SkGetPackedR32(pm);
-        unsigned g = SkGetPackedG32(pm);
-        unsigned b = SkGetPackedB32(pm);
-        unsigned a = SkGetPackedA32(pm);
-        return GrColorPackRGBA(r, g, b, a);
-    }
-};
+////////////////////////////////////////////////////////////////////////////////
+
+bool GrIsBitmapInCache(const GrContext*, const SkBitmap&, const GrTextureParams*);
+
+GrTexture* GrLockAndRefCachedBitmapTexture(GrContext*, const SkBitmap&, const GrTextureParams*);
+
+void GrUnlockAndUnrefCachedBitmapTexture(GrTexture*);
 
 ////////////////////////////////////////////////////////////////////////////////
 // Classes
 
-class SkGrClipIterator : public GrClipIterator {
-public:
-    SkGrClipIterator() { fClipStack = NULL;  fCurr = NULL; }
-    SkGrClipIterator(const SkClipStack& clipStack) { this->reset(clipStack); }
-
-    void reset(const SkClipStack& clipStack);
-
-    // overrides
-    virtual bool isDone() const { return NULL == fCurr; }
-    virtual void next() { fCurr = fIter.next(); }
-    virtual void rewind() { this->reset(*fClipStack); }
-    virtual GrClipType getType() const;
-
-    virtual GrSetOp getOp() const;
-
-    virtual void getRect(GrRect* rect) const {
-        if (!fCurr->fRect) {
-            rect->setEmpty();
-        } else {
-            *rect = *fCurr->fRect;
-        }
-    }
-
-    virtual const GrPath* getPath() {
-        return fCurr->fPath;
-    }
-
-    virtual GrPathFill getPathFill() const;
-
-private:
-    const SkClipStack*                  fClipStack;
-    SkClipStack::B2FIter                fIter;
-    // SkClipStack's auto advances on each get
-    // so we store the current pos here.
-    const SkClipStack::B2FIter::Clip*   fCurr;
-};
-
-class SkGrRegionIterator : public GrClipIterator {
-public:
-    SkGrRegionIterator() {}
-    SkGrRegionIterator(const SkRegion& region) { this->reset(region); }
-
-    void reset(const SkRegion& region) {
-        fRegion = &region;
-        fIter.reset(region);
-    }
-
-    // overrides
-    virtual bool isDone() const { return fIter.done(); }
-    virtual void next() { fIter.next(); }
-    virtual void rewind() { this->reset(*fRegion); }
-    virtual GrClipType getType() const { return kRect_ClipType; }
-
-    virtual GrSetOp getOp() const { return kUnion_SetOp; }
-
-    virtual void getRect(GrRect* rect) const {
-        const SkIRect& r = fIter.rect();
-        rect->fLeft   = GrIntToScalar(r.fLeft);
-        rect->fTop    = GrIntToScalar(r.fTop);
-        rect->fRight  = GrIntToScalar(r.fRight);
-        rect->fBottom = GrIntToScalar(r.fBottom);
-    }
-
-    virtual const GrPath* getPath() {
-        SkASSERT(0);
-        return NULL;
-    }
-
-    virtual GrPathFill getPathFill() const {
-        SkASSERT(0);
-        return kWinding_PathFill;
-    }
-private:
-    const SkRegion*     fRegion;
-    SkRegion::Iterator  fIter;
-};
-
 class SkGlyphCache;
 
 class SkGrFontScaler : public GrFontScaler {
@@ -185,7 +97,7 @@
     virtual bool getPackedGlyphBounds(GrGlyph::PackedID, GrIRect* bounds);
     virtual bool getPackedGlyphImage(GrGlyph::PackedID, int width, int height,
                                      int rowBytes, void* image);
-    virtual bool getGlyphPath(uint16_t glyphID, GrPath*);
+    virtual bool getGlyphPath(uint16_t glyphID, SkPath*);
 
 private:
     SkGlyphCache* fStrike;
@@ -194,13 +106,5 @@
 };
 
 ////////////////////////////////////////////////////////////////////////////////
-// Helper functions
-
-static const GrContext::TextureKey gUNCACHED_KEY = ~0;
-GrContext::TextureCacheEntry sk_gr_create_bitmap_texture(GrContext* ctx,
-                                                GrContext::TextureKey key,
-                                                const GrSamplerState* sampler,
-                                                const SkBitmap& bitmap);
-
 
 #endif
diff --git a/include/gpu/SkGrPixelRef.h b/include/gpu/SkGrPixelRef.h
new file mode 100644
index 0000000..ec1e22e
--- /dev/null
+++ b/include/gpu/SkGrPixelRef.h
@@ -0,0 +1,70 @@
+
+/*
+ * 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 SkGrPixelRef_DEFINED
+#define SkGrPixelRef_DEFINED
+
+#include "SkBitmap.h"
+#include "SkPixelRef.h"
+#include "GrTexture.h"
+#include "GrRenderTarget.h"
+
+
+/**
+ *  Common baseclass that implements onLockPixels() by calling onReadPixels().
+ *  Since it has a copy, it always returns false for onLockPixelsAreWritable().
+ */
+class SK_API SkROLockPixelsPixelRef : public SkPixelRef {
+public:
+    SkROLockPixelsPixelRef();
+    virtual ~SkROLockPixelsPixelRef();
+
+protected:
+    // override from SkPixelRef
+    virtual void* onLockPixels(SkColorTable** ptr);
+    virtual void onUnlockPixels();
+    virtual bool onLockPixelsAreWritable() const;   // return false;
+
+private:
+    SkBitmap    fBitmap;
+    typedef SkPixelRef INHERITED;
+};
+
+/**
+ *  PixelRef that wraps a GrSurface
+ */
+class SK_API SkGrPixelRef : public SkROLockPixelsPixelRef {
+public:
+    /**
+     * Constructs a pixel ref around a GrSurface. If the caller has locked the GrSurface in the
+     * cache and would like the pixel ref to unlock it in its destructor then transferCacheLock
+     * should be set to true.
+     */
+    SkGrPixelRef(GrSurface* surface, bool transferCacheLock = false);
+    virtual ~SkGrPixelRef();
+
+    // override from SkPixelRef
+    virtual SkGpuTexture* getTexture() SK_OVERRIDE;
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
+protected:
+    // overrides from SkPixelRef
+    virtual bool onReadPixels(SkBitmap* dst, const SkIRect* subset) SK_OVERRIDE;
+    virtual SkPixelRef* deepCopy(SkBitmap::Config dstConfig, const SkIRect* subset) SK_OVERRIDE;
+
+private:
+    GrSurface*  fSurface;
+    bool        fUnlock;   // if true the pixel ref owns a texture cache lock on fSurface
+
+    typedef SkROLockPixelsPixelRef INHERITED;
+};
+
+#endif
diff --git a/include/gpu/SkGrTexturePixelRef.h b/include/gpu/SkGrTexturePixelRef.h
index ab92eff..7129512 100644
--- a/include/gpu/SkGrTexturePixelRef.h
+++ b/include/gpu/SkGrTexturePixelRef.h
@@ -11,77 +11,9 @@
 #ifndef SkGrTexturePixelRef_DEFINED
 #define SkGrTexturePixelRef_DEFINED
 
-#include "SkBitmap.h"
-#include "SkPixelRef.h"
-#include "GrTexture.h"
-#include "GrRenderTarget.h"
+#include "SkGrPixelRef.h"
 
-
-/**
- *  Common baseclass that implements onLockPixels() by calling onReadPixels().
- *  Since it has a copy, it always returns false for onLockPixelsAreWritable().
- */
-class SK_API SkROLockPixelsPixelRef : public SkPixelRef {
-public:
-    SkROLockPixelsPixelRef();
-    virtual ~SkROLockPixelsPixelRef();
-
-protected:
-    // override from SkPixelRef
-    virtual void* onLockPixels(SkColorTable** ptr);
-    virtual void onUnlockPixels();
-    virtual bool onLockPixelsAreWritable() const;   // return false;
-
-private:
-    SkBitmap    fBitmap;
-    typedef SkPixelRef INHERITED;
-};
-
-/**
- *  PixelRef that wraps a GrTexture
- */
-class SK_API SkGrTexturePixelRef : public SkROLockPixelsPixelRef {
-public:
-            SkGrTexturePixelRef(GrTexture*);
-    virtual ~SkGrTexturePixelRef();
-
-    // override from SkPixelRef
-    virtual SkGpuTexture* getTexture();
-
-protected:
-    // override from SkPixelRef
-    virtual bool onReadPixels(SkBitmap* dst, const SkIRect* subset);
-
-    // override from SkPixelRef
-    virtual SkPixelRef* deepCopy(SkBitmap::Config dstConfig) SK_OVERRIDE;
-
-private:
-    GrTexture*  fTexture;
-    typedef SkROLockPixelsPixelRef INHERITED;
-};
-
-/**
- *  PixelRef that wraps a GrRenderTarget
- */
-class SK_API SkGrRenderTargetPixelRef : public SkROLockPixelsPixelRef {
-public:
-            SkGrRenderTargetPixelRef(GrRenderTarget* rt);
-    virtual ~SkGrRenderTargetPixelRef();
-
-    // override from SkPixelRef
-    virtual SkGpuTexture* getTexture();
-
-protected:
-    // override from SkPixelRef
-    virtual bool onReadPixels(SkBitmap* dst, const SkIRect* subset);
-
-    // override from SkPixelRef
-    virtual SkPixelRef* deepCopy(SkBitmap::Config dstConfig) SK_OVERRIDE;
-
-private:
-    GrRenderTarget*  fRenderTarget;
-    typedef SkROLockPixelsPixelRef INHERITED;
-};
+typedef SkGrPixelRef SkGrTexturePixelRef;
+typedef SkGrPixelRef SkGrRenderTargetPixelRef;
 
 #endif
-
diff --git a/include/gpu/gl/GrGLConfig.h b/include/gpu/gl/GrGLConfig.h
index 806f055..20d9031 100644
--- a/include/gpu/gl/GrGLConfig.h
+++ b/include/gpu/gl/GrGLConfig.h
@@ -12,7 +12,6 @@
 #define GrGLConfig_DEFINED
 
 #include "GrTypes.h"
-#include "GrGLDefines.h"
 
 /**
  * Optional GL config file.
@@ -50,15 +49,9 @@
  * GR_GL_NO_CONSTANT_ATTRIBUTES: if this evaluates to true then the GL backend
  * will use uniforms instead of attributes in all cases when there is not
  * per-vertex data. This is important when the underlying GL implementation
- * doesn't actually support immediate style attribute values (e.g. when 
+ * doesn't actually support immediate style attribute values (e.g. when
  * the GL stream is converted to DX as in ANGLE on Chrome). Defaults to 0.
  *
- * GR_GL_ATTRIBUTE_MATRICES: If changing uniforms is very expensive it may be
- * faster to use vertex attributes for matrices (set via glVertexAttrib3fv). 
- * Setting this build flag enables this behavior. GR_GL_NO_CONSTANT_ATTRIBUTES
- * must not be set since this uses constant attributes for the matrices. 
- * Defaults to 0.
- *
  * GR_GL_USE_BUFFER_DATA_NULL_HINT: When specifing new data for a vertex/index
  * buffer that replaces old data Ganesh can give a hint to the driver that the
  * previous data will not be used in future draws like this:
@@ -99,6 +92,10 @@
  * check the first time we use a color format or a combination of color /
  * stencil formats as attachments. If the FBO is complete we will assume
  * subsequent attachments with the same formats are complete as well.
+ *
+ * GR_GL_USE_NV_PATH_RENDERING: Enable experimental support for
+ * GL_NV_path_rendering. There are known issues with clipping, non-AA paths, and
+ * perspective.
  */
 
 #if !defined(GR_GL_LOG_CALLS)
@@ -121,10 +118,6 @@
     #define GR_GL_NO_CONSTANT_ATTRIBUTES                0
 #endif
 
-#if !defined(GR_GL_ATTRIBUTE_MATRICES)
-    #define GR_GL_ATTRIBUTE_MATRICES                    0
-#endif
-
 #if !defined(GR_GL_USE_BUFFER_DATA_NULL_HINT)
     #define GR_GL_USE_BUFFER_DATA_NULL_HINT             1
 #endif
@@ -149,6 +142,10 @@
     #define GR_GL_CHECK_FBO_STATUS_ONCE_PER_FORMAT      0
 #endif
 
+#if !defined(GR_GL_USE_NV_PATH_RENDERING)
+    #define GR_GL_USE_NV_PATH_RENDERING                 0
+#endif
+
 /**
  * There is a strange bug that occurs on Macs with NVIDIA GPUs. We don't
  * fully understand it. When (element) array buffers are continually
@@ -178,125 +175,4 @@
     (GR_MAC_BUILD &&                                    \
      !GR_GL_USE_BUFFER_DATA_NULL_HINT)
 
-#if(GR_GL_NO_CONSTANT_ATTRIBUTES) && (GR_GL_ATTRIBUTE_MATRICES)
-    #error "Cannot combine GR_GL_NO_CONSTANT_ATTRIBUTES and GR_GL_ATTRIBUTE_MATRICES"
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-#if GR_SCALAR_IS_FIXED
-    #define GrGLType   GL_FIXED
-#elif GR_SCALAR_IS_FLOAT
-    #define GrGLType   GR_GL_FLOAT
-#else
-    #error "unknown GR_SCALAR type"
-#endif
-
-#if GR_TEXT_SCALAR_IS_USHORT
-    #define GrGLTextType                    GR_GL_UNSIGNED_SHORT
-    #define GR_GL_TEXT_TEXTURE_NORMALIZED   1
-#elif GR_TEXT_SCALAR_IS_FLOAT
-    #define GrGLTextType                    GR_GL_FLOAT
-    #define GR_GL_TEXT_TEXTURE_NORMALIZED   0
-#elif GR_TEXT_SCALAR_IS_FIXED
-    #define GrGLTextType                    GR_GL_FIXED
-    #define GR_GL_TEXT_TEXTURE_NORMALIZED   0
-#else
-    #error "unknown GR_TEXT_SCALAR type"
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-struct GrGLInterface;
-
-extern void GrGLCheckErr(const GrGLInterface* gl,
-                         const char* location,
-                         const char* call);
-
-extern void GrGLClearErr(const GrGLInterface* gl);
-
-#if GR_GL_CHECK_ERROR
-    extern bool gCheckErrorGL;
-    #define GR_GL_CHECK_ERROR_IMPL(IFACE, X)                    \
-        if (gCheckErrorGL)                                      \
-            GrGLCheckErr(IFACE, GR_FILE_AND_LINE_STR, #X)
-#else
-    #define GR_GL_CHECK_ERROR_IMPL(IFACE, X)
-#endif
-
-#if GR_GL_LOG_CALLS
-    extern bool gLogCallsGL;
-    #define GR_GL_LOG_CALLS_IMPL(X)                             \
-        if (gLogCallsGL)                                        \
-            GrPrintf(GR_FILE_AND_LINE_STR "GL: " #X "\n")
-#else
-    #define GR_GL_LOG_CALLS_IMPL(X)
-#endif
-
-#if GR_GL_PER_GL_FUNC_CALLBACK
-    #define GR_GL_CALLBACK_IMPL(IFACE) (IFACE)->fCallback(IFACE)
-#else
-    #define GR_GL_CALLBACK_IMPL(IFACE)
-#endif
-
-#define GR_GL_CALL(IFACE, X)                                    \
-    do {                                                        \
-        GR_GL_CALL_NOERRCHECK(IFACE, X);                        \
-        GR_GL_CHECK_ERROR_IMPL(IFACE, X);                       \
-    } while (false)
-
-#define GR_GL_CALL_NOERRCHECK(IFACE, X)                         \
-    do {                                                        \
-        GR_GL_CALLBACK_IMPL(IFACE);                             \
-        (IFACE)->f##X;                                          \
-        GR_GL_LOG_CALLS_IMPL(X);                                \
-    } while (false)
-
-#define GR_GL_CALL_RET(IFACE, RET, X)                           \
-    do {                                                        \
-        GR_GL_CALL_RET_NOERRCHECK(IFACE, RET, X);               \
-        GR_GL_CHECK_ERROR_IMPL(IFACE, X);                       \
-    } while (false)
-
-#define GR_GL_CALL_RET_NOERRCHECK(IFACE, RET, X)                \
-    do {                                                        \
-        GR_GL_CALLBACK_IMPL(IFACE);                             \
-        (RET) = (IFACE)->f##X;                                  \
-        GR_GL_LOG_CALLS_IMPL(X);                                \
-    } while (false)
-
-#define GR_GL_GET_ERROR(IFACE) (IFACE)->fGetError()
-
-////////////////////////////////////////////////////////////////////////////////
-
-/**
- *  Some drivers want the var-int arg to be zero-initialized on input.
- */
-#define GR_GL_INIT_ZERO     0
-#define GR_GL_GetIntegerv(gl, e, p)     \
-    do {                            \
-        *(p) = GR_GL_INIT_ZERO;     \
-        GR_GL_CALL(gl, GetIntegerv(e, p));   \
-    } while (0)
-
-#define GR_GL_GetFramebufferAttachmentParameteriv(gl, t, a, pname, p)           \
-    do {                                                                        \
-        *(p) = GR_GL_INIT_ZERO;                                                 \
-        GR_GL_CALL(gl, GetFramebufferAttachmentParameteriv(t, a, pname, p));    \
-    } while (0)
-
-#define GR_GL_GetRenderbufferParameteriv(gl, t, pname, p)                       \
-    do {                                                                        \
-        *(p) = GR_GL_INIT_ZERO;                                                 \
-        GR_GL_CALL(gl, GetRenderbufferParameteriv(t, pname, p));                \
-    } while (0)
-
-#define GR_GL_GetTexLevelParameteriv(gl, t, l, pname, p)                        \
-    do {                                                                        \
-        *(p) = GR_GL_INIT_ZERO;                                                 \
-        GR_GL_CALL(gl, GetTexLevelParameteriv(t, l, pname, p));                 \
-    } while (0)
-
-////////////////////////////////////////////////////////////////////////////////
-
 #endif
diff --git a/include/gpu/gl/GrGLDefines.h b/include/gpu/gl/GrGLDefines.h
deleted file mode 100644
index e66eec4..0000000
--- a/include/gpu/gl/GrGLDefines.h
+++ /dev/null
@@ -1,692 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-
-#ifndef GrGLDefines_DEFINED
-#define GrGLDefines_DEFINED
-
-// The following constants consist of the intersection of GL constants
-// exported by GLES 1.0, GLES 2.0, and desktop GL required by the system.
-
-#define GR_GL_DEPTH_BUFFER_BIT               0x00000100
-#define GR_GL_STENCIL_BUFFER_BIT             0x00000400
-#define GR_GL_COLOR_BUFFER_BIT               0x00004000
-
-/* Boolean */
-#define GR_GL_FALSE                          0
-#define GR_GL_TRUE                           1
-
-/* BeginMode */
-#define GR_GL_POINTS                         0x0000
-#define GR_GL_LINES                          0x0001
-#define GR_GL_LINE_LOOP                      0x0002
-#define GR_GL_LINE_STRIP                     0x0003
-#define GR_GL_TRIANGLES                      0x0004
-#define GR_GL_TRIANGLE_STRIP                 0x0005
-#define GR_GL_TRIANGLE_FAN                   0x0006
-
-/* AlphaFunction (not supported in ES20) */
-/*      GL_NEVER */
-/*      GL_LESS */
-/*      GL_EQUAL */
-/*      GL_LEQUAL */
-/*      GL_GREATER */
-/*      GL_NOTEQUAL */
-/*      GL_GEQUAL */
-/*      GL_ALWAYS */
-
-/* BlendingFactorDest */
-#define GR_GL_ZERO                           0
-#define GR_GL_ONE                            1
-#define GR_GL_SRC_COLOR                      0x0300
-#define GR_GL_ONE_MINUS_SRC_COLOR            0x0301
-#define GR_GL_SRC_ALPHA                      0x0302
-#define GR_GL_ONE_MINUS_SRC_ALPHA            0x0303
-#define GR_GL_DST_ALPHA                      0x0304
-#define GR_GL_ONE_MINUS_DST_ALPHA            0x0305
-
-/* BlendingFactorSrc */
-/*      GL_ZERO */
-/*      GL_ONE */
-#define GR_GL_DST_COLOR                      0x0306
-#define GR_GL_ONE_MINUS_DST_COLOR            0x0307
-#define GR_GL_SRC_ALPHA_SATURATE             0x0308
-/*      GL_SRC_ALPHA */
-/*      GL_ONE_MINUS_SRC_ALPHA */
-/*      GL_DST_ALPHA */
-/*      GL_ONE_MINUS_DST_ALPHA */
-
-/* ExtendedBlendFactors */
-#define GR_GL_SRC1_COLOR                     0x88F9
-#define GR_GL_ONE_MINUS_SRC1_COLOR           0x88FA
-/*      GL_SRC1_ALPHA */
-#define GR_GL_ONE_MINUS_SRC1_ALPHA           0x88FB
-
-/* BlendEquationSeparate */
-#define GR_GL_FUNC_ADD                       0x8006
-#define GR_GL_BLEND_EQUATION                 0x8009
-#define GR_GL_BLEND_EQUATION_RGB             0x8009    /* same as BLEND_EQUATION */
-#define GR_GL_BLEND_EQUATION_ALPHA           0x883D
-
-/* BlendSubtract */
-#define GR_GL_FUNC_SUBTRACT                  0x800A
-#define GR_GL_FUNC_REVERSE_SUBTRACT          0x800B
-
-/* Separate Blend Functions */
-#define GR_GL_BLEND_DST_RGB                  0x80C8
-#define GR_GL_BLEND_SRC_RGB                  0x80C9
-#define GR_GL_BLEND_DST_ALPHA                0x80CA
-#define GR_GL_BLEND_SRC_ALPHA                0x80CB
-#define GR_GL_CONSTANT_COLOR                 0x8001
-#define GR_GL_ONE_MINUS_CONSTANT_COLOR       0x8002
-#define GR_GL_CONSTANT_ALPHA                 0x8003
-#define GR_GL_ONE_MINUS_CONSTANT_ALPHA       0x8004
-#define GR_GL_BLEND_COLOR                    0x8005
-
-/* Buffer Objects */
-#define GR_GL_ARRAY_BUFFER                   0x8892
-#define GR_GL_ELEMENT_ARRAY_BUFFER           0x8893
-#define GR_GL_ARRAY_BUFFER_BINDING           0x8894
-#define GR_GL_ELEMENT_ARRAY_BUFFER_BINDING   0x8895
-
-#define GR_GL_STREAM_DRAW                    0x88E0
-#define GR_GL_STATIC_DRAW                    0x88E4
-#define GR_GL_DYNAMIC_DRAW                   0x88E8
-
-#define GR_GL_BUFFER_SIZE                    0x8764
-#define GR_GL_BUFFER_USAGE                   0x8765
-
-#define GR_GL_CURRENT_VERTEX_ATTRIB          0x8626
-
-/* CullFaceMode */
-#define GR_GL_FRONT                          0x0404
-#define GR_GL_BACK                           0x0405
-#define GR_GL_FRONT_AND_BACK                 0x0408
-
-/* DepthFunction */
-/*      GL_NEVER */
-/*      GL_LESS */
-/*      GL_EQUAL */
-/*      GL_LEQUAL */
-/*      GL_GREATER */
-/*      GL_NOTEQUAL */
-/*      GL_GEQUAL */
-/*      GL_ALWAYS */
-
-/* EnableCap */
-#define GR_GL_TEXTURE_2D                     0x0DE1
-#define GR_GL_CULL_FACE                      0x0B44
-#define GR_GL_BLEND                          0x0BE2
-#define GR_GL_DITHER                         0x0BD0
-#define GR_GL_STENCIL_TEST                   0x0B90
-#define GR_GL_DEPTH_TEST                     0x0B71
-#define GR_GL_SCISSOR_TEST                   0x0C11
-#define GR_GL_POLYGON_OFFSET_FILL            0x8037
-#define GR_GL_SAMPLE_ALPHA_TO_COVERAGE       0x809E
-#define GR_GL_SAMPLE_COVERAGE                0x80A0
-
-/* ErrorCode */
-#define GR_GL_NO_ERROR                       0
-#define GR_GL_INVALID_ENUM                   0x0500
-#define GR_GL_INVALID_VALUE                  0x0501
-#define GR_GL_INVALID_OPERATION              0x0502
-#define GR_GL_OUT_OF_MEMORY                  0x0505
-#define GR_GL_CONTEXT_LOST                   0x300E  // TODO(gman): What value?
-
-/* FrontFaceDirection */
-#define GR_GL_CW                             0x0900
-#define GR_GL_CCW                            0x0901
-
-/* GetPName */
-#define GR_GL_LINE_WIDTH                     0x0B21
-#define GR_GL_ALIASED_POINT_SIZE_RANGE       0x846D
-#define GR_GL_ALIASED_LINE_WIDTH_RANGE       0x846E
-#define GR_GL_CULL_FACE_MODE                 0x0B45
-#define GR_GL_FRONT_FACE                     0x0B46
-#define GR_GL_DEPTH_RANGE                    0x0B70
-#define GR_GL_DEPTH_WRITEMASK                0x0B72
-#define GR_GL_DEPTH_CLEAR_VALUE              0x0B73
-#define GR_GL_DEPTH_FUNC                     0x0B74
-#define GR_GL_STENCIL_CLEAR_VALUE            0x0B91
-#define GR_GL_STENCIL_FUNC                   0x0B92
-#define GR_GL_STENCIL_FAIL                   0x0B94
-#define GR_GL_STENCIL_PASS_DEPTH_FAIL        0x0B95
-#define GR_GL_STENCIL_PASS_DEPTH_PASS        0x0B96
-#define GR_GL_STENCIL_REF                    0x0B97
-#define GR_GL_STENCIL_VALUE_MASK             0x0B93
-#define GR_GL_STENCIL_WRITEMASK              0x0B98
-#define GR_GL_STENCIL_BACK_FUNC              0x8800
-#define GR_GL_STENCIL_BACK_FAIL              0x8801
-#define GR_GL_STENCIL_BACK_PASS_DEPTH_FAIL   0x8802
-#define GR_GL_STENCIL_BACK_PASS_DEPTH_PASS   0x8803
-#define GR_GL_STENCIL_BACK_REF               0x8CA3
-#define GR_GL_STENCIL_BACK_VALUE_MASK        0x8CA4
-#define GR_GL_STENCIL_BACK_WRITEMASK         0x8CA5
-#define GR_GL_VIEWPORT                       0x0BA2
-#define GR_GL_SCISSOR_BOX                    0x0C10
-/*      GL_SCISSOR_TEST */
-#define GR_GL_COLOR_CLEAR_VALUE              0x0C22
-#define GR_GL_COLOR_WRITEMASK                0x0C23
-#define GR_GL_UNPACK_ALIGNMENT               0x0CF5
-#define GR_GL_UNPACK_FLIP_Y                  0x9240
-#define GR_GL_PACK_ALIGNMENT                 0x0D05
-#define GR_GL_PACK_REVERSE_ROW_ORDER         0x93A4
-#define GR_GL_MAX_TEXTURE_SIZE               0x0D33
-#define GR_GL_MAX_VIEWPORT_DIMS              0x0D3A
-#define GR_GL_SUBPIXEL_BITS                  0x0D50
-#define GR_GL_RED_BITS                       0x0D52
-#define GR_GL_GREEN_BITS                     0x0D53
-#define GR_GL_BLUE_BITS                      0x0D54
-#define GR_GL_ALPHA_BITS                     0x0D55
-#define GR_GL_DEPTH_BITS                     0x0D56
-#define GR_GL_STENCIL_BITS                   0x0D57
-#define GR_GL_POLYGON_OFFSET_UNITS           0x2A00
-/*      GL_POLYGON_OFFSET_FILL */
-#define GR_GL_POLYGON_OFFSET_FACTOR          0x8038
-#define GR_GL_TEXTURE_BINDING_2D             0x8069
-#define GR_GL_SAMPLE_BUFFERS                 0x80A8
-#define GR_GL_SAMPLES                        0x80A9
-#define GR_GL_SAMPLE_COVERAGE_VALUE          0x80AA
-#define GR_GL_SAMPLE_COVERAGE_INVERT         0x80AB
-
-/* GetTextureParameter */
-/*      GL_TEXTURE_MAG_FILTER */
-/*      GL_TEXTURE_MIN_FILTER */
-/*      GL_TEXTURE_WRAP_S */
-/*      GL_TEXTURE_WRAP_T */
-
-#define GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
-#define GR_GL_COMPRESSED_TEXTURE_FORMATS     0x86A3
-
-/* HintMode */
-#define GR_GL_DONT_CARE                      0x1100
-#define GR_GL_FASTEST                        0x1101
-#define GR_GL_NICEST                         0x1102
-
-/* HintTarget */
-#define GR_GL_GENERATE_MIPMAP_HINT            0x8192
-
-/* DataType */
-#define GR_GL_BYTE                           0x1400
-#define GR_GL_UNSIGNED_BYTE                  0x1401
-#define GR_GL_SHORT                          0x1402
-#define GR_GL_UNSIGNED_SHORT                 0x1403
-#define GR_GL_INT                            0x1404
-#define GR_GL_UNSIGNED_INT                   0x1405
-#define GR_GL_FLOAT                          0x1406
-#define GR_GL_FIXED                          0x140C
-
-/* Lighting */
-#define GR_GL_LIGHTING                       0x0B50
-#define GR_GL_LIGHT0                         0x4000
-#define GR_GL_LIGHT1                         0x4001
-#define GR_GL_LIGHT2                         0x4002
-#define GR_GL_LIGHT3                         0x4003
-#define GR_GL_LIGHT4                         0x4004
-#define GR_GL_LIGHT5                         0x4005
-#define GR_GL_LIGHT6                         0x4006
-#define GR_GL_LIGHT7                         0x4007
-#define GR_GL_SPOT_EXPONENT                  0x1205
-#define GR_GL_SPOT_CUTOFF                    0x1206
-#define GR_GL_CONSTANT_ATTENUATION           0x1207
-#define GR_GL_LINEAR_ATTENUATION             0x1208
-#define GR_GL_QUADRATIC_ATTENUATION          0x1209
-#define GR_GL_AMBIENT                        0x1200
-#define GR_GL_DIFFUSE                        0x1201
-#define GR_GL_SPECULAR                       0x1202
-#define GR_GL_SHININESS                      0x1601
-#define GR_GL_EMISSION                       0x1600
-#define GR_GL_POSITION                       0x1203
-#define GR_GL_SPOT_DIRECTION                 0x1204
-#define GR_GL_AMBIENT_AND_DIFFUSE            0x1602
-#define GR_GL_COLOR_INDEXES                  0x1603
-#define GR_GL_LIGHT_MODEL_TWO_SIDE           0x0B52
-#define GR_GL_LIGHT_MODEL_LOCAL_VIEWER       0x0B51
-#define GR_GL_LIGHT_MODEL_AMBIENT            0x0B53
-#define GR_GL_FRONT_AND_BACK                 0x0408
-#define GR_GL_SHADE_MODEL                    0x0B54
-#define GR_GL_FLAT                           0x1D00
-#define GR_GL_SMOOTH                         0x1D01
-#define GR_GL_COLOR_MATERIAL                 0x0B57
-#define GR_GL_COLOR_MATERIAL_FACE            0x0B55
-#define GR_GL_COLOR_MATERIAL_PARAMETER       0x0B56
-#define GR_GL_NORMALIZE                      0x0BA1
-
-/* Matrix Mode */
-#define GR_GL_MATRIX_MODE                    0x0BA0
-#define GR_GL_MODELVIEW                      0x1700
-#define GR_GL_PROJECTION                     0x1701
-#define GR_GL_TEXTURE                        0x1702
-
-/* multisample */
-#define GR_GL_MULTISAMPLE                    0x809D
-
-/* Points */
-#define GR_GL_POINT_SMOOTH                   0x0B10
-#define GR_GL_POINT_SIZE                     0x0B11
-#define GR_GL_POINT_SIZE_GRANULARITY         0x0B13
-#define GR_GL_POINT_SIZE_RANGE               0x0B12
-
-/* Lines */
-#define GR_GL_LINE_SMOOTH                    0x0B20
-#define GR_GL_LINE_STIPPLE                   0x0B24
-#define GR_GL_LINE_STIPPLE_PATTERN           0x0B25
-#define GR_GL_LINE_STIPPLE_REPEAT            0x0B26
-#define GR_GL_LINE_WIDTH                     0x0B21
-#define GR_GL_LINE_WIDTH_GRANULARITY         0x0B23
-#define GR_GL_LINE_WIDTH_RANGE               0x0B22
-
-/* PixelFormat */
-#define GR_GL_DEPTH_COMPONENT                0x1902
-#define GR_GL_RED                            0x1903
-#define GR_GL_GREEN                          0x1904
-#define GR_GL_BLUE                           0x1905
-#define GR_GL_ALPHA                          0x1906
-#define GR_GL_RGB                            0x1907
-#define GR_GL_RGBA                           0x1908
-#define GR_GL_BGRA                           0x80E1
-#define GR_GL_LUMINANCE                      0x1909
-#define GR_GL_LUMINANCE_ALPHA                0x190A
-#define GR_GL_PALETTE8_RGBA8                 0x8B96
-#define GR_GL_ALPHA8                         0x803C
-
-/* PixelType */
-/*      GL_UNSIGNED_BYTE */
-#define GR_GL_UNSIGNED_SHORT_4_4_4_4         0x8033
-#define GR_GL_UNSIGNED_SHORT_5_5_5_1         0x8034
-#define GR_GL_UNSIGNED_SHORT_5_6_5           0x8363
-
-/* Shaders */
-#define GR_GL_FRAGMENT_SHADER                  0x8B30
-#define GR_GL_VERTEX_SHADER                    0x8B31
-#define GR_GL_GEOMETRY_SHADER                  0x8DD9
-#define GR_GL_MAX_VERTEX_ATTRIBS               0x8869
-#define GR_GL_MAX_VERTEX_UNIFORM_VECTORS       0x8DFB
-#define GR_GL_MAX_VARYING_VECTORS              0x8DFC
-#define GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
-#define GR_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS   0x8B4C
-#define GR_GL_MAX_TEXTURE_IMAGE_UNITS          0x8872
-#define GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS     0x8DFD
-#define GR_GL_SHADER_TYPE                      0x8B4F
-#define GR_GL_DELETE_STATUS                    0x8B80
-#define GR_GL_LINK_STATUS                      0x8B82
-#define GR_GL_VALIDATE_STATUS                  0x8B83
-#define GR_GL_ATTACHED_SHADERS                 0x8B85
-#define GR_GL_ACTIVE_UNIFORMS                  0x8B86
-#define GR_GL_ACTIVE_UNIFORM_MAX_LENGTH        0x8B87
-#define GR_GL_ACTIVE_ATTRIBUTES                0x8B89
-#define GR_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH      0x8B8A
-#define GR_GL_SHADING_LANGUAGE_VERSION         0x8B8C
-#define GR_GL_CURRENT_PROGRAM                  0x8B8D
-#define GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS  0x8B49
-#define GR_GL_MAX_VERTEX_UNIFORM_COMPONENTS    0x8B4A
-
-/* StencilFunction */
-#define GR_GL_NEVER                          0x0200
-#define GR_GL_LESS                           0x0201
-#define GR_GL_EQUAL                          0x0202
-#define GR_GL_LEQUAL                         0x0203
-#define GR_GL_GREATER                        0x0204
-#define GR_GL_NOTEQUAL                       0x0205
-#define GR_GL_GEQUAL                         0x0206
-#define GR_GL_ALWAYS                         0x0207
-
-/* StencilOp */
-/*      GL_ZERO */
-#define GR_GL_KEEP                           0x1E00
-#define GR_GL_REPLACE                        0x1E01
-#define GR_GL_INCR                           0x1E02
-#define GR_GL_DECR                           0x1E03
-#define GR_GL_INVERT                         0x150A
-#define GR_GL_INCR_WRAP                      0x8507
-#define GR_GL_DECR_WRAP                      0x8508
-
-/* StringName */
-#define GR_GL_VENDOR                         0x1F00
-#define GR_GL_RENDERER                       0x1F01
-#define GR_GL_VERSION                        0x1F02
-#define GR_GL_EXTENSIONS                     0x1F03
-
-/* Pixel Mode / Transfer */
-#define GR_GL_UNPACK_ROW_LENGTH              0x0CF2
-#define GR_GL_PACK_ROW_LENGTH                0x0D02
-
-
-/* TextureMagFilter */
-#define GR_GL_NEAREST                        0x2600
-#define GR_GL_LINEAR                         0x2601
-
-/* TextureMinFilter */
-/*      GL_NEAREST */
-/*      GL_LINEAR */
-#define GR_GL_NEAREST_MIPMAP_NEAREST         0x2700
-#define GR_GL_LINEAR_MIPMAP_NEAREST          0x2701
-#define GR_GL_NEAREST_MIPMAP_LINEAR          0x2702
-#define GR_GL_LINEAR_MIPMAP_LINEAR           0x2703
-
-/* TextureUsage */
-#define GR_GL_FRAMEBUFFER_ATTACHMENT         0x93A3
-
-/* TextureParameterName */
-#define GR_GL_TEXTURE_MAG_FILTER             0x2800
-#define GR_GL_TEXTURE_MIN_FILTER             0x2801
-#define GR_GL_TEXTURE_WRAP_S                 0x2802
-#define GR_GL_TEXTURE_WRAP_T                 0x2803
-#define GR_GL_TEXTURE_USAGE                  0x93A2
-
-/* TextureTarget */
-/*      GL_TEXTURE_2D */
-#define GR_GL_TEXTURE                        0x1702
-#define GR_GL_TEXTURE_CUBE_MAP               0x8513
-#define GR_GL_TEXTURE_BINDING_CUBE_MAP       0x8514
-#define GR_GL_TEXTURE_CUBE_MAP_POSITIVE_X    0x8515
-#define GR_GL_TEXTURE_CUBE_MAP_NEGATIVE_X    0x8516
-#define GR_GL_TEXTURE_CUBE_MAP_POSITIVE_Y    0x8517
-#define GR_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y    0x8518
-#define GR_GL_TEXTURE_CUBE_MAP_POSITIVE_Z    0x8519
-#define GR_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z    0x851A
-#define GR_GL_MAX_CUBE_MAP_TEXTURE_SIZE      0x851C
-
-/* TextureUnit */
-#define GR_GL_TEXTURE0                       0x84C0
-#define GR_GL_TEXTURE1                       0x84C1
-#define GR_GL_TEXTURE2                       0x84C2
-#define GR_GL_TEXTURE3                       0x84C3
-#define GR_GL_TEXTURE4                       0x84C4
-#define GR_GL_TEXTURE5                       0x84C5
-#define GR_GL_TEXTURE6                       0x84C6
-#define GR_GL_TEXTURE7                       0x84C7
-#define GR_GL_TEXTURE8                       0x84C8
-#define GR_GL_TEXTURE9                       0x84C9
-#define GR_GL_TEXTURE10                      0x84CA
-#define GR_GL_TEXTURE11                      0x84CB
-#define GR_GL_TEXTURE12                      0x84CC
-#define GR_GL_TEXTURE13                      0x84CD
-#define GR_GL_TEXTURE14                      0x84CE
-#define GR_GL_TEXTURE15                      0x84CF
-#define GR_GL_TEXTURE16                      0x84D0
-#define GR_GL_TEXTURE17                      0x84D1
-#define GR_GL_TEXTURE18                      0x84D2
-#define GR_GL_TEXTURE19                      0x84D3
-#define GR_GL_TEXTURE20                      0x84D4
-#define GR_GL_TEXTURE21                      0x84D5
-#define GR_GL_TEXTURE22                      0x84D6
-#define GR_GL_TEXTURE23                      0x84D7
-#define GR_GL_TEXTURE24                      0x84D8
-#define GR_GL_TEXTURE25                      0x84D9
-#define GR_GL_TEXTURE26                      0x84DA
-#define GR_GL_TEXTURE27                      0x84DB
-#define GR_GL_TEXTURE28                      0x84DC
-#define GR_GL_TEXTURE29                      0x84DD
-#define GR_GL_TEXTURE30                      0x84DE
-#define GR_GL_TEXTURE31                      0x84DF
-#define GR_GL_ACTIVE_TEXTURE                 0x84E0
-#define GR_GL_MAX_TEXTURE_UNITS              0x84E2
-
-/* TextureWrapMode */
-#define GR_GL_REPEAT                         0x2901
-#define GR_GL_CLAMP_TO_EDGE                  0x812F
-#define GR_GL_MIRRORED_REPEAT                0x8370
-
-/* Texture Swizzle */
-#define GR_GL_TEXTURE_SWIZZLE_R              0x8E42
-#define GR_GL_TEXTURE_SWIZZLE_G              0x8E43
-#define GR_GL_TEXTURE_SWIZZLE_B              0x8E44
-#define GR_GL_TEXTURE_SWIZZLE_A              0x8E45
-#define GR_GL_TEXTURE_SWIZZLE_RGBA           0x8E46
-
-/* Texture mapping */
-#define GR_GL_TEXTURE_ENV                    0x2300
-#define GR_GL_TEXTURE_ENV_MODE               0x2200
-#define GR_GL_TEXTURE_1D                     0x0DE0
-/* GL_TEXTURE_2D */
-/* GL_TEXTURE_WRAP_S */
-/* GL_TEXTURE_WRAP_T */
-/* GL_TEXTURE_MAG_FILTER */
-/* GL_TEXTURE_MIN_FILTER */
-#define GR_GL_TEXTURE_ENV_COLOR             0x2201
-#define GR_GL_TEXTURE_GEN_S                 0x0C60
-#define GR_GL_TEXTURE_GEN_T                 0x0C61
-#define GR_GL_TEXTURE_GEN_MODE              0x2500
-#define GR_GL_TEXTURE_BORDER_COLOR          0x1004
-#define GR_GL_TEXTURE_WIDTH                 0x1000
-#define GR_GL_TEXTURE_HEIGHT                0x1001
-#define GR_GL_TEXTURE_BORDER                0x1005
-#define GR_GL_TEXTURE_COMPONENTS            0x1003
-#define GR_GL_TEXTURE_RED_SIZE              0x805C
-#define GR_GL_TEXTURE_GREEN_SIZE            0x805D
-#define GR_GL_TEXTURE_BLUE_SIZE             0x805E
-#define GR_GL_TEXTURE_ALPHA_SIZE            0x805F
-#define GR_GL_TEXTURE_LUMINANCE_SIZE        0x8060
-#define GR_GL_TEXTURE_INTENSITY_SIZE        0x8061
-#define GR_GL_TEXTURE_INTERNAL_FORMAT       0x1003
-/* GL_NEAREST_MIPMAP_NEAREST */
-/* GL_NEAREST_MIPMAP_LINEAR */
-/* GL_LINEAR_MIPMAP_NEAREST */
-/* GL_LINEAR_MIPMAP_LINEAR */
-#define GR_GL_OBJECT_LINEAR                 0x2401
-#define GR_GL_OBJECT_PLANE                  0x2501
-#define GR_GL_EYE_LINEAR                    0x2400
-#define GR_GL_EYE_PLANE                     0x2502
-#define GR_GL_SPHERE_MAP                    0x2402
-#define GR_GL_DECAL                         0x2101
-#define GR_GL_MODULATE                      0x2100
-/*	GL_NEAREST */
-/*	GL_REPEAT */
-#define GR_GL_CLAMP                         0x2900
-#define GR_GL_S                             0x2000
-#define GR_GL_T                             0x2001
-#define GR_GL_R                             0x2002
-#define GR_GL_Q                             0x2003
-#define GR_GL_TEXTURE_GEN_R                 0x0C62
-#define GR_GL_TEXTURE_GEN_Q                 0x0C63
-
-/* texture_env_combine */
-#define GR_GL_COMBINE                       0x8570
-#define GR_GL_COMBINE_RGB                   0x8571
-#define GR_GL_COMBINE_ALPHA                 0x8572
-#define GR_GL_SOURCE0_RGB                   0x8580
-#define GR_GL_SOURCE1_RGB                   0x8581
-#define GR_GL_SOURCE2_RGB                   0x8582
-#define GR_GL_SOURCE0_ALPHA                 0x8588
-#define GR_GL_SOURCE1_ALPHA                 0x8589
-#define GR_GL_SOURCE2_ALPHA                 0x858A
-#define GR_GL_OPERAND0_RGB                  0x8590
-#define GR_GL_OPERAND1_RGB                  0x8591
-#define GR_GL_OPERAND2_RGB                  0x8592
-#define GR_GL_OPERAND0_ALPHA                0x8598
-#define GR_GL_OPERAND1_ALPHA                0x8599
-#define GR_GL_OPERAND2_ALPHA                0x859A
-#define GR_GL_RGB_SCALE                     0x8573
-#define GR_GL_ADD_SIGNED                    0x8574
-#define GR_GL_INTERPOLATE                   0x8575
-#define GR_GL_SUBTRACT                      0x84E7
-#define GR_GL_CONSTANT                      0x8576
-#define GR_GL_PRIMARY_COLOR                 0x8577
-#define GR_GL_PREVIOUS                      0x8578
-#define GR_GL_SRC0_RGB                      0x8580
-#define GR_GL_SRC1_RGB                      0x8581
-#define GR_GL_SRC2_RGB                      0x8582
-#define GR_GL_SRC0_ALPHA                    0x8588
-#define GR_GL_SRC1_ALPHA                    0x8589
-#define GR_GL_SRC2_ALPHA                    0x858A
-
-/* Uniform Types */
-#define GR_GL_FLOAT_VEC2                     0x8B50
-#define GR_GL_FLOAT_VEC3                     0x8B51
-#define GR_GL_FLOAT_VEC4                     0x8B52
-#define GR_GL_INT_VEC2                       0x8B53
-#define GR_GL_INT_VEC3                       0x8B54
-#define GR_GL_INT_VEC4                       0x8B55
-#define GR_GL_BOOL                           0x8B56
-#define GR_GL_BOOL_VEC2                      0x8B57
-#define GR_GL_BOOL_VEC3                      0x8B58
-#define GR_GL_BOOL_VEC4                      0x8B59
-#define GR_GL_FLOAT_MAT2                     0x8B5A
-#define GR_GL_FLOAT_MAT3                     0x8B5B
-#define GR_GL_FLOAT_MAT4                     0x8B5C
-#define GR_GL_SAMPLER_2D                     0x8B5E
-#define GR_GL_SAMPLER_CUBE                   0x8B60
-
-/* Vertex Arrays */
-#define GR_GL_VERTEX_ATTRIB_ARRAY_ENABLED        0x8622
-#define GR_GL_VERTEX_ATTRIB_ARRAY_SIZE           0x8623
-#define GR_GL_VERTEX_ATTRIB_ARRAY_STRIDE         0x8624
-#define GR_GL_VERTEX_ATTRIB_ARRAY_TYPE           0x8625
-#define GR_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED     0x886A
-#define GR_GL_VERTEX_ATTRIB_ARRAY_POINTER        0x8645
-#define GR_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
-#define GR_GL_VERTEX_ARRAY                       0x8074
-#define GR_GL_NORMAL_ARRAY                       0x8075
-#define GR_GL_COLOR_ARRAY                        0x8076
-#define GR_GL_INDEX_ARRAY                        0x8077
-#define GR_GL_TEXTURE_COORD_ARRAY                0x8078
-#define GR_GL_EDGE_FLAG_ARRAY                    0x8079
-#define GR_GL_VERTEX_ARRAY_SIZE                  0x807A
-#define GR_GL_VERTEX_ARRAY_TYPE                  0x807B
-#define GR_GL_VERTEX_ARRAY_STRIDE                0x807C
-#define GR_GL_NORMAL_ARRAY_TYPE                  0x807E
-#define GR_GL_NORMAL_ARRAY_STRIDE                0x807F
-#define GR_GL_COLOR_ARRAY_SIZE                   0x8081
-#define GR_GL_COLOR_ARRAY_TYPE                   0x8082
-#define GR_GL_COLOR_ARRAY_STRIDE                 0x8083
-#define GR_GL_INDEX_ARRAY_TYPE                   0x8085
-#define GR_GL_INDEX_ARRAY_STRIDE                 0x8086
-#define GR_GL_TEXTURE_COORD_ARRAY_SIZE           0x8088
-#define GR_GL_TEXTURE_COORD_ARRAY_TYPE           0x8089
-#define GR_GL_TEXTURE_COORD_ARRAY_STRIDE         0x808A
-#define GR_GL_EDGE_FLAG_ARRAY_STRIDE             0x808C
-#define GR_GL_VERTEX_ARRAY_POINTER               0x808E
-#define GR_GL_NORMAL_ARRAY_POINTER               0x808F
-#define GR_GL_COLOR_ARRAY_POINTER                0x8090
-#define GR_GL_INDEX_ARRAY_POINTER                0x8091
-#define GR_GL_TEXTURE_COORD_ARRAY_POINTER        0x8092
-#define GR_GL_EDGE_FLAG_ARRAY_POINTER            0x8093
-#define GR_GL_V2F                                0x2A20
-#define GR_GL_V3F                                0x2A21
-#define GR_GL_C4UB_V2F                           0x2A22
-#define GR_GL_C4UB_V3F                           0x2A23
-#define GR_GL_C3F_V3F                            0x2A24
-#define GR_GL_N3F_V3F                            0x2A25
-#define GR_GL_C4F_N3F_V3F                        0x2A26
-#define GR_GL_T2F_V3F                            0x2A27
-#define GR_GL_T4F_V4F                            0x2A28
-#define GR_GL_T2F_C4UB_V3F                       0x2A29
-#define GR_GL_T2F_C3F_V3F                        0x2A2A
-#define GR_GL_T2F_N3F_V3F                        0x2A2B
-#define GR_GL_T2F_C4F_N3F_V3F                    0x2A2C
-#define GR_GL_T4F_C4F_N3F_V4F                    0x2A2D
-
-/* Vertex Buffer Object */
-#define GR_GL_WRITE_ONLY                         0x88B9
-#define GR_GL_BUFFER_MAPPED                      0x88BC
-/* Read Format */
-#define GR_GL_IMPLEMENTATION_COLOR_READ_TYPE   0x8B9A
-#define GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
-
-/* Shader Source */
-#define GR_GL_COMPILE_STATUS                 0x8B81
-#define GR_GL_INFO_LOG_LENGTH                0x8B84
-#define GR_GL_SHADER_SOURCE_LENGTH           0x8B88
-#define GR_GL_SHADER_COMPILER                0x8DFA
-
-/* Shader Binary */
-#define GR_GL_SHADER_BINARY_FORMATS          0x8DF8
-#define GR_GL_NUM_SHADER_BINARY_FORMATS      0x8DF9
-
-/* Shader Precision-Specified Types */
-#define GR_GL_LOW_FLOAT                      0x8DF0
-#define GR_GL_MEDIUM_FLOAT                   0x8DF1
-#define GR_GL_HIGH_FLOAT                     0x8DF2
-#define GR_GL_LOW_INT                        0x8DF3
-#define GR_GL_MEDIUM_INT                     0x8DF4
-#define GR_GL_HIGH_INT                       0x8DF5
-
-/* Queries */
-#define GR_GL_QUERY_COUNTER_BITS             0x8864
-#define GR_GL_CURRENT_QUERY                  0x8865
-#define GR_GL_QUERY_RESULT                   0x8866
-#define GR_GL_QUERY_RESULT_AVAILABLE         0x8867
-#define GR_GL_SAMPLES_PASSED                 0x8914
-#define GR_GL_ANY_SAMPLES_PASSED             0x8C2F
-#define GR_GL_TIME_ELAPSED                   0x88BF
-#define GR_GL_TIMESTAMP                      0x8E28
-#define GR_GL_PRIMITIVES_GENERATED           0x8C87
-#define GR_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88
-
-
-/* Framebuffer Object. */
-#define GR_GL_FRAMEBUFFER                    0x8D40
-#define GR_GL_READ_FRAMEBUFFER               0x8CA8
-#define GR_GL_DRAW_FRAMEBUFFER               0x8CA9
-
-#define GR_GL_RENDERBUFFER                   0x8D41
-
-#define GR_GL_RGBA4                          0x8056
-#define GR_GL_RGB5_A1                        0x8057
-#define GR_GL_RGB565                         0x8D62
-#define GR_GL_RGBA8                          0x8058
-#define GR_GL_RGB8                           0x8051
-#define GR_GL_BGRA8                          0x93A1
-#define GR_GL_SRGB                           0x8C40
-#define GR_GL_SRGB8                          0x8C41
-#define GR_GL_SRGB_ALPHA                     0x8C42
-#define GR_GL_SRGB8_ALPHA8                   0x8C43
-#define GR_GL_DEPTH_COMPONENT16              0x81A5
-#define GR_GL_STENCIL_INDEX                  0x1901
-#define GR_GL_STENCIL_INDEX4                 0x8D47
-#define GR_GL_STENCIL_INDEX8                 0x8D48
-#define GR_GL_STENCIL_INDEX16                0x8D49
-#define GR_GL_DEPTH_STENCIL                  0x84F9
-#define GR_GL_DEPTH24_STENCIL8               0x88F0
-
-#define GR_GL_MAX_SAMPLES                    0x8D57
-
-#define GR_GL_RENDERBUFFER_WIDTH             0x8D42
-#define GR_GL_RENDERBUFFER_HEIGHT            0x8D43
-#define GR_GL_RENDERBUFFER_INTERNAL_FORMAT   0x8D44
-#define GR_GL_RENDERBUFFER_RED_SIZE          0x8D50
-#define GR_GL_RENDERBUFFER_GREEN_SIZE        0x8D51
-#define GR_GL_RENDERBUFFER_BLUE_SIZE         0x8D52
-#define GR_GL_RENDERBUFFER_ALPHA_SIZE        0x8D53
-#define GR_GL_RENDERBUFFER_DEPTH_SIZE        0x8D54
-#define GR_GL_RENDERBUFFER_STENCIL_SIZE      0x8D55
-
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE           0x8CD0
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME           0x8CD1
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL         0x8CD2
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER         0x8CD4
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE              0x8212
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE            0x8213
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE             0x8214
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE            0x8215
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE            0x8216
-#define GR_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE          0x8217
-
-#define GR_GL_COLOR_ATTACHMENT0              0x8CE0
-#define GR_GL_DEPTH_ATTACHMENT               0x8D00
-#define GR_GL_STENCIL_ATTACHMENT             0x8D20
-#define GR_GL_DEPTH_STENCIL_ATTACHMENT       0x821A
-
-#define GR_GL_NONE                           0
-
-#define GR_GL_FRAMEBUFFER_COMPLETE                      0x8CD5
-#define GR_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT         0x8CD6
-#define GR_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
-#define GR_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS         0x8CD9
-#define GR_GL_FRAMEBUFFER_UNSUPPORTED                   0x8CDD
-
-#define GR_GL_FRAMEBUFFER_BINDING            0x8CA6
-#define GR_GL_RENDERBUFFER_BINDING           0x8CA7
-#define GR_GL_MAX_RENDERBUFFER_SIZE          0x84E8
-
-#define GR_GL_INVALID_FRAMEBUFFER_OPERATION  0x0506
-
-#endif
diff --git a/include/gpu/gl/GrGLFunctions.h b/include/gpu/gl/GrGLFunctions.h
new file mode 100644
index 0000000..ceaecc6
--- /dev/null
+++ b/include/gpu/gl/GrGLFunctions.h
@@ -0,0 +1,223 @@
+
+/*
+ * 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 GrGLFunctions_DEFINED
+#define GrGLFunctions_DEFINED
+
+#include "GrGLConfig.h"
+
+/**
+ * Declares typedefs for all the GL functions used in GrGLInterface
+ */
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef unsigned int GrGLenum;
+typedef unsigned char GrGLboolean;
+typedef unsigned int GrGLbitfield;
+typedef signed char GrGLbyte;
+typedef char GrGLchar;
+typedef short GrGLshort;
+typedef int GrGLint;
+typedef int GrGLsizei;
+typedef int64_t GrGLint64;
+typedef unsigned char GrGLubyte;
+typedef unsigned short GrGLushort;
+typedef unsigned int GrGLuint;
+typedef uint64_t GrGLuint64;
+typedef float GrGLfloat;
+typedef float GrGLclampf;
+typedef double GrGLdouble;
+typedef double GrGLclampd;
+typedef void GrGLvoid;
+typedef long GrGLintptr;
+typedef long GrGLsizeiptr;
+
+///////////////////////////////////////////////////////////////////////////////
+
+extern "C" {
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLActiveTextureProc)(GrGLenum texture);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLAttachShaderProc)(GrGLuint program, GrGLuint shader);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBeginQueryProc)(GrGLenum target, GrGLuint id);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindAttribLocationProc)(GrGLuint program, GrGLuint index, const char* name);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBindBufferProc)(GrGLenum target, GrGLuint buffer);
+    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* 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);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLBufferSubDataProc)(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid* data);
+    typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLCheckFramebufferStatusProc)(GrGLenum target);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearProc)(GrGLbitfield mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearColorProc)(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLClearStencilProc)(GrGLint s);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLColorMaskProc)(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompileShaderProc)(GrGLuint shader);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCompressedTexImage2DProc)(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data);
+    typedef GrGLuint (GR_GL_FUNCTION_TYPE* GrGLCreateProgramProc)(void);
+    typedef GrGLuint (GR_GL_FUNCTION_TYPE* GrGLCreateShaderProc)(GrGLenum type);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCullFaceProc)(GrGLenum mode);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteBuffersProc)(GrGLsizei n, const GrGLuint* buffers);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteFramebuffersProc)(GrGLsizei n, const GrGLuint *framebuffers);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteProgramProc)(GrGLuint program);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteQueriesProc)(GrGLsizei n, const GrGLuint *ids);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteRenderbuffersProc)(GrGLsizei n, const GrGLuint *renderbuffers);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteShaderProc)(GrGLuint shader);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteTexturesProc)(GrGLsizei n, const GrGLuint* textures);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDepthMaskProc)(GrGLboolean flag);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDisableProc)(GrGLenum cap);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDisableVertexAttribArrayProc)(GrGLuint index);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawArraysProc)(GrGLenum mode, GrGLint first, GrGLsizei count);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawBufferProc)(GrGLenum mode);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawBuffersProc)(GrGLsizei n, const GrGLenum* bufs);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDrawElementsProc)(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEnableProc)(GrGLenum cap);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEnableVertexAttribArrayProc)(GrGLuint index);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLEndQueryProc)(GrGLenum target);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLFinishProc)();
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLFlushProc)();
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLFramebufferRenderbufferProc)(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLFramebufferTexture2DProc)(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLFrontFaceProc)(GrGLenum mode);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGenBuffersProc)(GrGLsizei n, GrGLuint* buffers);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGenFramebuffersProc)(GrGLsizei n, GrGLuint *framebuffers);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGenQueriesProc)(GrGLsizei n, GrGLuint *ids);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGenRenderbuffersProc)(GrGLsizei n, GrGLuint *renderbuffers);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGenTexturesProc)(GrGLsizei n, GrGLuint* textures);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetBufferParameterivProc)(GrGLenum target, GrGLenum pname, GrGLint* params);
+    typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLGetErrorProc)();
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetFramebufferAttachmentParameterivProc)(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetIntegervProc)(GrGLenum pname, GrGLint* params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetProgramInfoLogProc)(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetProgramivProc)(GrGLuint program, GrGLenum pname, GrGLint* params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetQueryivProc)(GrGLenum GLtarget, GrGLenum pname, GrGLint *params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetQueryObjecti64vProc)(GrGLuint id, GrGLenum pname, GrGLint64 *params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetQueryObjectivProc)(GrGLuint id, GrGLenum pname, GrGLint *params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetQueryObjectui64vProc)(GrGLuint id, GrGLenum pname, GrGLuint64 *params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetQueryObjectuivProc)(GrGLuint id, GrGLenum pname, GrGLuint *params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetRenderbufferParameterivProc)(GrGLenum target, GrGLenum pname, GrGLint* params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetShaderInfoLogProc)(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, char* infolog);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetShaderivProc)(GrGLuint shader, GrGLenum pname, GrGLint* params);
+    typedef const GrGLubyte* (GR_GL_FUNCTION_TYPE* GrGLGetStringProc)(GrGLenum name);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetTexLevelParameterivProc)(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint* params);
+    typedef GrGLint (GR_GL_FUNCTION_TYPE* GrGLGetUniformLocationProc)(GrGLuint program, const char* name);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLLineWidthProc)(GrGLfloat width);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLLinkProgramProc)(GrGLuint program);
+    typedef GrGLvoid* (GR_GL_FUNCTION_TYPE* GrGLMapBufferProc)(GrGLenum target, GrGLenum access);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPixelStoreiProc)(GrGLenum pname, GrGLint param);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLQueryCounterProc)(GrGLuint id, GrGLenum target);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLReadBufferProc)(GrGLenum src);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLReadPixelsProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLRenderbufferStorageProc)(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLRenderbufferStorageMultisampleProc)(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLRenderbufferStorageMultisampleCoverageProc)(GrGLenum target, GrGLsizei coverageSamples, GrGLsizei colorSamples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLResolveMultisampleFramebufferProc)();
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLScissorProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height);
+#if GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLShaderSourceProc)(GrGLuint shader, GrGLsizei count, const char* const * str, const GrGLint* length);
+#else
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLShaderSourceProc)(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length);
+#endif
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilFuncProc)(GrGLenum func, GrGLint ref, GrGLuint mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilFuncSeparateProc)(GrGLenum face, GrGLenum func, GrGLint ref, GrGLuint mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilMaskProc)(GrGLuint mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilMaskSeparateProc)(GrGLenum face, GrGLuint mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilOpProc)(GrGLenum fail, GrGLenum zfail, GrGLenum zpass);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilOpSeparateProc)(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexImage2DProc)(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexParameteriProc)(GrGLenum target, GrGLenum pname, GrGLint param);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexParameterivProc)(GrGLenum target, GrGLenum pname, const GrGLint* params);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTexStorage2DProc)(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
+    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* 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);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform1ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform2fProc)(GrGLint location, GrGLfloat v0, GrGLfloat v1);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform2iProc)(GrGLint location, GrGLint v0, GrGLint v1);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform2fvProc)(GrGLint location, GrGLsizei count, const GrGLfloat* v);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform2ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform3fProc)(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform3iProc)(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform3fvProc)(GrGLint location, GrGLsizei count, const GrGLfloat* v);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform3ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform4fProc)(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform4iProc)(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform4fvProc)(GrGLint location, GrGLsizei count, const GrGLfloat* v);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniform4ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniformMatrix2fvProc)(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniformMatrix3fvProc)(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUniformMatrix4fvProc)(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value);
+    typedef GrGLboolean (GR_GL_FUNCTION_TYPE* GrGLUnmapBufferProc)(GrGLenum target);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLUseProgramProc)(GrGLuint program);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttrib4fvProc)(GrGLuint indx, const GrGLfloat* values);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLVertexAttribPointerProc)(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLViewportProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height);
+
+    // Experimental: Functions for GL_NV_path_rendering. These will be
+    // alphabetized with the above functions once this is fully supported
+    // (and functions we are unlikely to use will possibly be omitted).
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLMatrixModeProc)(GrGLenum);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLLoadIdentityProc)();
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLLoadMatrixfProc)(const GrGLfloat* m);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathCommandsProc)(GrGLuint path, GrGLsizei numCommands, const GrGLubyte *commands, GrGLsizei numCoords, GrGLenum coordType, const GrGLvoid *coords);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathCoordsProc)(GrGLuint path, GrGLsizei numCoords, GrGLenum coordType, const GrGLvoid *coords);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathSubCommandsProc)(GrGLuint path, GrGLsizei commandStart, GrGLsizei commandsToDelete, GrGLsizei numCommands, const GrGLubyte *commands, GrGLsizei numCoords, GrGLenum coordType, const GrGLvoid *coords);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathSubCoordsProc)(GrGLuint path, GrGLsizei coordStart, GrGLsizei numCoords, GrGLenum coordType, const GrGLvoid *coords);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathStringProc)(GrGLuint path, GrGLenum format, GrGLsizei length, const GrGLvoid *pathString);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathGlyphsProc)(GrGLuint firstPathName, GrGLenum fontTarget, const GrGLvoid *fontName, GrGLbitfield fontStyle, GrGLsizei numGlyphs, GrGLenum type, const GrGLvoid *charcodes, GrGLenum handleMissingGlyphs, GrGLuint pathParameterTemplate, GrGLfloat emScale);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathGlyphRangeProc)(GrGLuint firstPathName, GrGLenum fontTarget, const GrGLvoid *fontName, GrGLbitfield fontStyle, GrGLuint firstGlyph, GrGLsizei numGlyphs, GrGLenum handleMissingGlyphs, GrGLuint pathParameterTemplate, GrGLfloat emScale);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLWeightPathsProc)(GrGLuint resultPath, GrGLsizei numPaths, const GrGLuint paths[], const GrGLfloat weights[]);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCopyPathProc)(GrGLuint resultPath, GrGLuint srcPath);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLInterpolatePathsProc)(GrGLuint resultPath, GrGLuint pathA, GrGLuint pathB, GrGLfloat weight);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLTransformPathProc)(GrGLuint resultPath, GrGLuint srcPath, GrGLenum transformType, const GrGLfloat *transformValues);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathParameterivProc)(GrGLuint path, GrGLenum pname, const GrGLint *value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathParameteriProc)(GrGLuint path, GrGLenum pname, GrGLint value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathParameterfvProc)(GrGLuint path, GrGLenum pname, const GrGLfloat *value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathParameterfProc)(GrGLuint path, GrGLenum pname, GrGLfloat value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathDashArrayProc)(GrGLuint path, GrGLsizei dashCount, const GrGLfloat *dashArray);
+    typedef GrGLuint (GR_GL_FUNCTION_TYPE* GrGLGenPathsProc)(GrGLsizei range);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeletePathsProc)(GrGLuint path, GrGLsizei range);
+    typedef GrGLboolean (GR_GL_FUNCTION_TYPE* GrGLIsPathProc)(GrGLuint path);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathStencilFuncProc)(GrGLenum func, GrGLint ref, GrGLuint mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathStencilDepthOffsetProc)(GrGLfloat factor, GrGLfloat units);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilFillPathProc)(GrGLuint path, GrGLenum fillMode, GrGLuint mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilStrokePathProc)(GrGLuint path, GrGLint reference, GrGLuint mask);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilFillPathInstancedProc)(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum fillMode, GrGLuint mask, GrGLenum transformType, const GrGLfloat *transformValues);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLStencilStrokePathInstancedProc)(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLint reference, GrGLuint mask, GrGLenum transformType, const GrGLfloat *transformValues);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathCoverDepthFuncProc)(GrGLenum zfunc);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathColorGenProc)(GrGLenum color, GrGLenum genMode, GrGLenum colorFormat, const GrGLfloat *coeffs);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathTexGenProc)(GrGLenum texCoordSet, GrGLenum genMode, GrGLint components, const GrGLfloat *coeffs);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLPathFogGenProc)(GrGLenum genMode);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCoverFillPathProc)(GrGLuint path, GrGLenum coverMode);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCoverStrokePathProc)(GrGLuint name, GrGLenum coverMode);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCoverFillPathInstancedProc)(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat *transformValues);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLCoverStrokePathInstancedProc)(GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLenum coverMode, GrGLenum transformType, const GrGLfloat* transformValues);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathParameterivProc)(GrGLuint name, GrGLenum param, GrGLint *value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathParameterfvProc)(GrGLuint name, GrGLenum param, GrGLfloat *value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathCommandsProc)(GrGLuint name, GrGLubyte *commands);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathCoordsProc)(GrGLuint name, GrGLfloat *coords);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathDashArrayProc)(GrGLuint name, GrGLfloat *dashArray);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathMetricsProc)(GrGLbitfield metricQueryMask, GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLsizei stride, GrGLfloat *metrics);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathMetricRangeProc)(GrGLbitfield metricQueryMask, GrGLuint fistPathName, GrGLsizei numPaths, GrGLsizei stride, GrGLfloat *metrics);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathSpacingProc)(GrGLenum pathListMode, GrGLsizei numPaths, GrGLenum pathNameType, const GrGLvoid *paths, GrGLuint pathBase, GrGLfloat advanceScale, GrGLfloat kerningScale, GrGLenum transformType, GrGLfloat *returnedSpacing);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathColorGenivProc)(GrGLenum color, GrGLenum pname, GrGLint *value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathColorGenfvProc)(GrGLenum color, GrGLenum pname, GrGLfloat *value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathTexGenivProc)(GrGLenum texCoordSet, GrGLenum pname, GrGLint *value);
+    typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLGetPathTexGenfvProc)(GrGLenum texCoordSet, GrGLenum pname, GrGLfloat *value);
+    typedef GrGLboolean (GR_GL_FUNCTION_TYPE* GrGLIsPointInFillPathProc)(GrGLuint path, GrGLuint mask, GrGLfloat x, GrGLfloat y);
+    typedef GrGLboolean (GR_GL_FUNCTION_TYPE* GrGLIsPointInStrokePathProc)(GrGLuint path, GrGLfloat x, GrGLfloat y);
+    typedef GrGLfloat (GR_GL_FUNCTION_TYPE* GrGLGetPathLengthProc)(GrGLuint path, GrGLsizei startSegment, GrGLsizei numSegments);
+    typedef GrGLboolean (GR_GL_FUNCTION_TYPE* GrGLPointAlongPathProc)(GrGLuint path, GrGLsizei startSegment, GrGLsizei numSegments, GrGLfloat distance, GrGLfloat *x, GrGLfloat *y, GrGLfloat *tangentX, GrGLfloat *tangentY);
+}  // extern "C"
+
+#endif
diff --git a/include/gpu/gl/GrGLInterface.h b/include/gpu/gl/GrGLInterface.h
index 968afab..e1ca40e 100644
--- a/include/gpu/gl/GrGLInterface.h
+++ b/include/gpu/gl/GrGLInterface.h
@@ -7,17 +7,12 @@
  */
 
 
-
 #ifndef GrGLInterface_DEFINED
 #define GrGLInterface_DEFINED
 
-#include "GrGLConfig.h"
+#include "GrGLFunctions.h"
 #include "GrRefCnt.h"
 
-#if !defined(GR_GL_FUNCTION_TYPE)
-    #define GR_GL_FUNCTION_TYPE
-#endif
-
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -38,32 +33,6 @@
 ////////////////////////////////////////////////////////////////////////////////
 
 /**
- * Helpers for glGetString()
- */
-
-typedef uint32_t GrGLVersion;
-typedef uint32_t GrGLSLVersion;
-
-#define GR_GL_VER(major, minor) ((static_cast<int>(major) << 16) | \
-                                 static_cast<int>(minor))
-#define GR_GLSL_VER(major, minor) ((static_cast<int>(major) << 16) | \
-                                   static_cast<int>(minor))
-
-// these variants assume caller already has a string from glGetString()
-GrGLVersion GrGLGetVersionFromString(const char* versionString);
-GrGLBinding GrGLGetBindingInUseFromString(const char* versionString);
-GrGLSLVersion GrGLGetGLSLVersionFromString(const char* versionString);
-bool GrGLHasExtensionFromString(const char* ext, const char* extensionString);
-
-// these variants call glGetString()
-bool GrGLHasExtension(const GrGLInterface*, const char* ext);
-GrGLBinding GrGLGetBindingInUse(const GrGLInterface*);
-GrGLVersion GrGLGetVersion(const GrGLInterface*);
-GrGLSLVersion GrGLGetGLSLVersion(const GrGLInterface*);
-
-////////////////////////////////////////////////////////////////////////////////
-
-/**
  * Rather than depend on platform-specific GL headers and libraries, we require
  * the client to provide a struct of GL function pointers. This struct can be
  * specified per-GrContext as a parameter to GrContext::Create. If NULL is
@@ -71,12 +40,12 @@
  * also NULL GrContext creation will fail.
  *
  * The default interface is returned by GrGLDefaultInterface. This function's
- * implementation is platform-specifc. Several have been provided, along with an
- * implementation that simply returns NULL. It is implementation-specific
+ * implementation is platform-specific. Several have been provided, along with
+ * an implementation that simply returns NULL. It is implementation-specific
  * whether the same GrGLInterface is returned or whether a new one is created
  * at each call. Some platforms may not be able to use a single GrGLInterface
  * because extension function ptrs vary across contexts. Note that GrGLInterface
- * is ref-counted. So if the same object is returned by multiple calls to 
+ * is ref-counted. So if the same object is returned by multiple calls to
  * GrGLDefaultInterface, each should bump the ref count.
  *
  * By defining GR_GL_PER_GL_CALL_IFACE_CALLBACK to 1 the client can specify a
@@ -91,15 +60,24 @@
 /**
  * Creates a GrGLInterface for a "native" GL context (e.g. WGL on windows,
  * GLX on linux, AGL on Mac). On platforms that have context-specific function
- * pointers for GL extensions (e.g. windows) the returned interface is only 
+ * pointers for GL extensions (e.g. windows) the returned interface is only
  * valid for the context that was current at creation.
  */
 const GrGLInterface* GrGLCreateNativeInterface();
 
+#if SK_MESA
 /**
  * Creates a GrGLInterface for an OSMesa context.
  */
 const GrGLInterface* GrGLCreateMesaInterface();
+#endif
+
+#if SK_ANGLE
+/**
+ * Creates a GrGLInterface for an ANGLE context.
+ */
+const GrGLInterface* GrGLCreateANGLEInterface();
+#endif
 
 /**
  * Creates a null GrGLInterface that doesn't draw anything. Used for measuring
@@ -107,316 +85,230 @@
  */
 const GrGLInterface* GrGLCreateNullInterface();
 
-typedef unsigned int GrGLenum;
-typedef unsigned char GrGLboolean;
-typedef unsigned int GrGLbitfield;
-typedef signed char GrGLbyte;
-typedef char GrGLchar;
-typedef short GrGLshort;
-typedef int GrGLint;
-typedef int GrGLsizei;
-typedef int64_t GrGLint64;
-typedef unsigned char GrGLubyte;
-typedef unsigned short GrGLushort;
-typedef unsigned int GrGLuint;
-typedef uint64_t GrGLuint64;
-typedef float GrGLfloat;
-typedef float GrGLclampf;
-typedef double GrGLdouble;
-typedef double GrGLclampd;
-typedef void GrGLvoid;
-typedef long GrGLintptr;
-typedef long GrGLsizeiptr;
-
-extern "C" {
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLActiveTextureProc)(GrGLenum texture);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLAttachShaderProc)(GrGLuint program, GrGLuint shader);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBeginQueryProc)(GrGLenum target, GrGLuint id);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindAttribLocationProc)(GrGLuint program, GrGLuint index, const char* name);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindBufferProc)(GrGLenum target, GrGLuint buffer);
-    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 *GrGLBlendFuncProc)(GrGLenum sfactor, GrGLenum dfactor);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBufferDataProc)(GrGLenum target, GrGLsizeiptr size, const GrGLvoid* data, GrGLenum usage);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBufferSubDataProc)(GrGLenum target, GrGLintptr offset, GrGLsizeiptr size, const GrGLvoid* data);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLClearProc)(GrGLbitfield mask);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLClearColorProc)(GrGLclampf red, GrGLclampf green, GrGLclampf blue, GrGLclampf alpha);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLClearStencilProc)(GrGLint s);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLColorMaskProc)(GrGLboolean red, GrGLboolean green, GrGLboolean blue, GrGLboolean alpha);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLColorPointerProc)(GrGLint size, GrGLenum type, GrGLsizei stride, const GrGLvoid* pointer);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLCompileShaderProc)(GrGLuint shader);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLCompressedTexImage2DProc)(GrGLenum target, GrGLint level, GrGLenum internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLsizei imageSize, const GrGLvoid* data);
-    typedef GrGLuint (GR_GL_FUNCTION_TYPE *GrGLCreateProgramProc)(void);
-    typedef GrGLuint (GR_GL_FUNCTION_TYPE *GrGLCreateShaderProc)(GrGLenum type);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLCullFaceProc)(GrGLenum mode);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDeleteBuffersProc)(GrGLsizei n, const GrGLuint* buffers);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDeleteProgramProc)(GrGLuint program);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDeleteQueriesProc)(GrGLsizei n, const GrGLuint *ids);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDeleteShaderProc)(GrGLuint shader);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDeleteTexturesProc)(GrGLsizei n, const GrGLuint* textures);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDepthMaskProc)(GrGLboolean flag);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDisableProc)(GrGLenum cap);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDisableVertexAttribArrayProc)(GrGLuint index);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDrawArraysProc)(GrGLenum mode, GrGLint first, GrGLsizei count);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDrawBufferProc)(GrGLenum mode);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDrawBuffersProc)(GrGLsizei n, const GrGLenum* bufs);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDrawElementsProc)(GrGLenum mode, GrGLsizei count, GrGLenum type, const GrGLvoid* indices);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLEnableProc)(GrGLenum cap);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLEnableVertexAttribArrayProc)(GrGLuint index);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLEndQueryProc)(GrGLenum target);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLFinishProc)();
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLFlushProc)();
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLFrontFaceProc)(GrGLenum mode);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGenBuffersProc)(GrGLsizei n, GrGLuint* buffers);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGenQueriesProc)(GrGLsizei n, GrGLuint *ids);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGenTexturesProc)(GrGLsizei n, GrGLuint* textures);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetBufferParameterivProc)(GrGLenum target, GrGLenum pname, GrGLint* params);
-    typedef GrGLenum (GR_GL_FUNCTION_TYPE *GrGLGetErrorProc)();
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetIntegervProc)(GrGLenum pname, GrGLint* params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetProgramInfoLogProc)(GrGLuint program, GrGLsizei bufsize, GrGLsizei* length, char* infolog);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetProgramivProc)(GrGLuint program, GrGLenum pname, GrGLint* params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetQueryivProc)(GrGLenum GLtarget, GrGLenum pname, GrGLint *params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetQueryObjecti64vProc)(GrGLuint id, GrGLenum pname, GrGLint64 *params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetQueryObjectivProc)(GrGLuint id, GrGLenum pname, GrGLint *params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetQueryObjectui64vProc)(GrGLuint id, GrGLenum pname, GrGLuint64 *params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetQueryObjectuivProc)(GrGLuint id, GrGLenum pname, GrGLuint *params);    
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetShaderInfoLogProc)(GrGLuint shader, GrGLsizei bufsize, GrGLsizei* length, char* infolog);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetShaderivProc)(GrGLuint shader, GrGLenum pname, GrGLint* params);
-    typedef const GrGLubyte* (GR_GL_FUNCTION_TYPE *GrGLGetStringProc)(GrGLenum name);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetTexLevelParameterivProc)(GrGLenum target, GrGLint level, GrGLenum pname, GrGLint* params);
-    typedef GrGLint (GR_GL_FUNCTION_TYPE *GrGLGetUniformLocationProc)(GrGLuint program, const char* name);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLLineWidthProc)(GrGLfloat width);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLLinkProgramProc)(GrGLuint program);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLPixelStoreiProc)(GrGLenum pname, GrGLint param);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLQueryCounterProc)(GrGLuint id, GrGLenum target);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLReadBufferProc)(GrGLenum src);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLReadPixelsProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLScissorProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLShaderSourceProc)(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLStencilFuncProc)(GrGLenum func, GrGLint ref, GrGLuint mask);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLStencilFuncSeparateProc)(GrGLenum face, GrGLenum func, GrGLint ref, GrGLuint mask);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLStencilMaskProc)(GrGLuint mask);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLStencilMaskSeparateProc)(GrGLenum face, GrGLuint mask);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLStencilOpProc)(GrGLenum fail, GrGLenum zfail, GrGLenum zpass);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLStencilOpSeparateProc)(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLTexImage2DProc)(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLTexParameteriProc)(GrGLenum target, GrGLenum pname, GrGLint param);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLTexStorage2DProc)(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
-    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 *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);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform1ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform2fProc)(GrGLint location, GrGLfloat v0, GrGLfloat v1);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform2iProc)(GrGLint location, GrGLint v0, GrGLint v1);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform2fvProc)(GrGLint location, GrGLsizei count, const GrGLfloat* v);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform2ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform3fProc)(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform3iProc)(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform3fvProc)(GrGLint location, GrGLsizei count, const GrGLfloat* v);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform3ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform4fProc)(GrGLint location, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2, GrGLfloat v3);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform4iProc)(GrGLint location, GrGLint v0, GrGLint v1, GrGLint v2, GrGLint v3);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform4fvProc)(GrGLint location, GrGLsizei count, const GrGLfloat* v);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniform4ivProc)(GrGLint location, GrGLsizei count, const GrGLint* v);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniformMatrix2fvProc)(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniformMatrix3fvProc)(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUniformMatrix4fvProc)(GrGLint location, GrGLsizei count, GrGLboolean transpose, const GrGLfloat* value);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLUseProgramProc)(GrGLuint program);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLVertexAttrib4fvProc)(GrGLuint indx, const GrGLfloat* values);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLVertexAttribPointerProc)(GrGLuint indx, GrGLint size, GrGLenum type, GrGLboolean normalized, GrGLsizei stride, const GrGLvoid* ptr);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLViewportProc)(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height);
-
-    // FBO Extension Functions
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindFramebufferProc)(GrGLenum target, GrGLuint framebuffer);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindRenderbufferProc)(GrGLenum target, GrGLuint renderbuffer);
-    typedef GrGLenum (GR_GL_FUNCTION_TYPE *GrGLCheckFramebufferStatusProc)(GrGLenum target);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDeleteFramebuffersProc)(GrGLsizei n, const GrGLuint *framebuffers);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLDeleteRenderbuffersProc)(GrGLsizei n, const GrGLuint *renderbuffers);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLFramebufferRenderbufferProc)(GrGLenum target, GrGLenum attachment, GrGLenum renderbuffertarget, GrGLuint renderbuffer);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLFramebufferTexture2DProc)(GrGLenum target, GrGLenum attachment, GrGLenum textarget, GrGLuint texture, GrGLint level);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGenFramebuffersProc)(GrGLsizei n, GrGLuint *framebuffers);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGenRenderbuffersProc)(GrGLsizei n, GrGLuint *renderbuffers);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetFramebufferAttachmentParameterivProc)(GrGLenum target, GrGLenum attachment, GrGLenum pname, GrGLint* params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLGetRenderbufferParameterivProc)(GrGLenum target, GrGLenum pname, GrGLint* params);
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLRenderbufferStorageProc)(GrGLenum target, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
-
-    // Multisampling Extension Functions
-    // same prototype for ARB_FBO, EXT_FBO, GL 3.0, & Apple ES extension
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLRenderbufferStorageMultisampleProc)(GrGLenum target, GrGLsizei samples, GrGLenum internalformat, GrGLsizei width, GrGLsizei height);
-    // desktop: ext_fbo_blit, arb_fbo, gl 3.0
-    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);
-    // apple's es extension
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLResolveMultisampleFramebufferProc)();
-
-    // Buffer mapping (extension in ES).
-    typedef GrGLvoid* (GR_GL_FUNCTION_TYPE *GrGLMapBufferProc)(GrGLenum target, GrGLenum access);
-    typedef GrGLboolean (GR_GL_FUNCTION_TYPE *GrGLUnmapBufferProc)(GrGLenum target);
-
-    // Dual source blending
-    typedef GrGLvoid (GR_GL_FUNCTION_TYPE *GrGLBindFragDataLocationIndexedProc)(GrGLuint program, GrGLuint colorNumber, GrGLuint index, const GrGLchar * name);
-}  // extern "C"
+/**
+ * Creates a debugging GrGLInterface that doesn't draw anything. Used for
+ * finding memory leaks and invalid memory accesses.
+ */
+const GrGLInterface* GrGLCreateDebugInterface();
 
 #if GR_GL_PER_GL_FUNC_CALLBACK
 typedef void (*GrGLInterfaceCallbackProc)(const GrGLInterface*);
 typedef intptr_t GrGLInterfaceCallbackData;
 #endif
 
-
-enum GrGLCapability {
-    kProbe_GrGLCapability = -1
-};
-
 /*
- * The following interface exports the OpenGL entry points used by the system.
- * Use of OpenGL calls is disallowed.  All calls should be invoked through
- * the global instance of this struct, defined above.
- *
- * IMPORTANT NOTE: The OpenGL entry points exposed here include both core GL
- * functions, and extensions.  The system assumes that the address of the
- * extension pointer will be valid across contexts.
+ * GrContext uses the following interface to make all calls into OpenGL. When a
+ * GrContext is created it is given a GrGLInterface. The interface's function
+ * pointers must be valid for the OpenGL context associated with the GrContext.
+ * On some platforms, such as Windows, function pointers for OpenGL extensions
+ * may vary between OpenGL contexts. So the caller must be careful to use a
+ * GrGLInterface initialized for the correct context. All functions that should
+ * be available based on the OpenGL's version and extension string must be
+ * non-NULL or GrContext creation will fail. This can be tested with the
+ * validate() method when the OpenGL context has been made current.
  */
 struct GR_API GrGLInterface : public GrRefCnt {
+private:
+    // simple wrapper class that exists only to initialize a pointer to NULL
+    template <typename FNPTR_TYPE> class GLPtr {
+    public:
+        GLPtr() : fPtr(NULL) {}
+        GLPtr operator =(FNPTR_TYPE ptr) { fPtr = ptr; return *this; }
+        operator FNPTR_TYPE() const { return fPtr; }
+    private:
+        FNPTR_TYPE fPtr;
+    };
+
+    typedef GrRefCnt INHERITED;
+
+public:
+    SK_DECLARE_INST_COUNT(GrGLInterface)
 
     GrGLInterface();
 
     // Validates that the GrGLInterface supports a binding. This means that
     // the GrGLinterface advertises the binding in fBindingsExported and all
-    // the necessary function pointers have been initialized.
+    // the necessary function pointers have been initialized. The interface is
+    // validated for the current OpenGL context.
     bool validate(GrGLBinding binding) const;
 
     // Indicator variable specifying the type of GL implementation
-    // exported:  GLES{1|2} or Desktop.
+    // exported:  GLES2 and/or Desktop.
     GrGLBinding fBindingsExported;
 
-    GrGLActiveTextureProc fActiveTexture;
-    GrGLAttachShaderProc fAttachShader;
-    GrGLBeginQueryProc fBeginQuery;
-    GrGLBindAttribLocationProc fBindAttribLocation;
-    GrGLBindBufferProc fBindBuffer;
-    GrGLBindFragDataLocationProc fBindFragDataLocation;
-    GrGLBindTextureProc fBindTexture;
-    GrGLBlendColorProc fBlendColor;
-    GrGLBlendFuncProc fBlendFunc;
-    GrGLBufferDataProc fBufferData;
-    GrGLBufferSubDataProc fBufferSubData;
-    GrGLClearProc fClear;
-    GrGLClearColorProc fClearColor;
-    GrGLClearStencilProc fClearStencil;
-    GrGLColorMaskProc fColorMask;
-    GrGLColorPointerProc fColorPointer;
-    GrGLCompileShaderProc fCompileShader;
-    GrGLCompressedTexImage2DProc fCompressedTexImage2D;
-    GrGLCreateProgramProc fCreateProgram;
-    GrGLCreateShaderProc fCreateShader;
-    GrGLCullFaceProc fCullFace;
-    GrGLDeleteBuffersProc fDeleteBuffers;
-    GrGLDeleteProgramProc fDeleteProgram;
-    GrGLDeleteQueriesProc fDeleteQueries;
-    GrGLDeleteShaderProc fDeleteShader;
-    GrGLDeleteTexturesProc fDeleteTextures;
-    GrGLDepthMaskProc fDepthMask;
-    GrGLDisableProc fDisable;
-    GrGLDisableVertexAttribArrayProc fDisableVertexAttribArray;
-    GrGLDrawArraysProc fDrawArrays;
-    GrGLDrawBufferProc fDrawBuffer;
-    GrGLDrawBuffersProc fDrawBuffers;
-    GrGLDrawElementsProc fDrawElements;
-    GrGLEnableProc fEnable;
-    GrGLEnableVertexAttribArrayProc fEnableVertexAttribArray;
-    GrGLEndQueryProc fEndQuery;
-    GrGLFinishProc fFinish;
-    GrGLFlushProc fFlush;
-    GrGLFrontFaceProc fFrontFace;
-    GrGLGenBuffersProc fGenBuffers;
-    GrGLGenQueriesProc fGenQueries;
-    GrGLGenTexturesProc fGenTextures;
-    GrGLGetBufferParameterivProc fGetBufferParameteriv;
-    GrGLGetErrorProc fGetError;
-    GrGLGetIntegervProc fGetIntegerv;
-    GrGLGetQueryObjecti64vProc fGetQueryObjecti64v;
-    GrGLGetQueryObjectivProc fGetQueryObjectiv;
-    GrGLGetQueryObjectui64vProc fGetQueryObjectui64v;
-    GrGLGetQueryObjectuivProc fGetQueryObjectuiv;
-    GrGLGetQueryivProc fGetQueryiv;
-    GrGLGetProgramInfoLogProc fGetProgramInfoLog;
-    GrGLGetProgramivProc fGetProgramiv;
-    GrGLGetShaderInfoLogProc fGetShaderInfoLog;
-    GrGLGetShaderivProc fGetShaderiv;
-    GrGLGetStringProc fGetString;
-    GrGLGetTexLevelParameterivProc fGetTexLevelParameteriv;
-    GrGLGetUniformLocationProc fGetUniformLocation;
-    GrGLLineWidthProc fLineWidth;
-    GrGLLinkProgramProc fLinkProgram;
-    GrGLPixelStoreiProc fPixelStorei;
-    GrGLQueryCounterProc fQueryCounter;
-    GrGLReadBufferProc fReadBuffer;
-    GrGLReadPixelsProc fReadPixels;
-    GrGLScissorProc fScissor;
-    GrGLShaderSourceProc fShaderSource;
-    GrGLStencilFuncProc fStencilFunc;
-    GrGLStencilFuncSeparateProc fStencilFuncSeparate;
-    GrGLStencilMaskProc fStencilMask;
-    GrGLStencilMaskSeparateProc fStencilMaskSeparate;
-    GrGLStencilOpProc fStencilOp;
-    GrGLStencilOpSeparateProc fStencilOpSeparate;
-    GrGLTexImage2DProc fTexImage2D;
-    GrGLTexParameteriProc fTexParameteri;
-    GrGLTexSubImage2DProc fTexSubImage2D;
-    GrGLTexStorage2DProc fTexStorage2D;
-    GrGLUniform1fProc fUniform1f;
-    GrGLUniform1iProc fUniform1i;
-    GrGLUniform1fvProc fUniform1fv;
-    GrGLUniform1ivProc fUniform1iv;
-    GrGLUniform2fProc fUniform2f;
-    GrGLUniform2iProc fUniform2i;
-    GrGLUniform2fvProc  fUniform2fv;
-    GrGLUniform2ivProc fUniform2iv;
-    GrGLUniform3fProc fUniform3f;
-    GrGLUniform3iProc fUniform3i;
-    GrGLUniform3fvProc fUniform3fv;
-    GrGLUniform3ivProc fUniform3iv;
-    GrGLUniform4fProc fUniform4f;
-    GrGLUniform4iProc fUniform4i;
-    GrGLUniform4fvProc fUniform4fv;
-    GrGLUniform4ivProc fUniform4iv;
-    GrGLUniformMatrix2fvProc fUniformMatrix2fv;
-    GrGLUniformMatrix3fvProc fUniformMatrix3fv;
-    GrGLUniformMatrix4fvProc fUniformMatrix4fv;
-    GrGLUseProgramProc fUseProgram;
-    GrGLVertexAttrib4fvProc fVertexAttrib4fv;
-    GrGLVertexAttribPointerProc fVertexAttribPointer;
-    GrGLViewportProc fViewport;
+    GLPtr<GrGLActiveTextureProc> fActiveTexture;
+    GLPtr<GrGLAttachShaderProc> fAttachShader;
+    GLPtr<GrGLBeginQueryProc> fBeginQuery;
+    GLPtr<GrGLBindAttribLocationProc> fBindAttribLocation;
+    GLPtr<GrGLBindBufferProc> fBindBuffer;
+    GLPtr<GrGLBindFragDataLocationProc> fBindFragDataLocation;
+    GLPtr<GrGLBindFragDataLocationIndexedProc> fBindFragDataLocationIndexed;
+    GLPtr<GrGLBindFramebufferProc> fBindFramebuffer;
+    GLPtr<GrGLBindRenderbufferProc> fBindRenderbuffer;
+    GLPtr<GrGLBindTextureProc> fBindTexture;
+    GLPtr<GrGLBlendColorProc> fBlendColor;
+    GLPtr<GrGLBlendFuncProc> fBlendFunc;
+    GLPtr<GrGLBlitFramebufferProc> fBlitFramebuffer;
+    GLPtr<GrGLBufferDataProc> fBufferData;
+    GLPtr<GrGLBufferSubDataProc> fBufferSubData;
+    GLPtr<GrGLCheckFramebufferStatusProc> fCheckFramebufferStatus;
+    GLPtr<GrGLClearProc> fClear;
+    GLPtr<GrGLClearColorProc> fClearColor;
+    GLPtr<GrGLClearStencilProc> fClearStencil;
+    GLPtr<GrGLColorMaskProc> fColorMask;
+    GLPtr<GrGLCompileShaderProc> fCompileShader;
+    GLPtr<GrGLCompressedTexImage2DProc> fCompressedTexImage2D;
+    GLPtr<GrGLCreateProgramProc> fCreateProgram;
+    GLPtr<GrGLCreateShaderProc> fCreateShader;
+    GLPtr<GrGLCullFaceProc> fCullFace;
+    GLPtr<GrGLDeleteBuffersProc> fDeleteBuffers;
+    GLPtr<GrGLDeleteFramebuffersProc> fDeleteFramebuffers;
+    GLPtr<GrGLDeleteProgramProc> fDeleteProgram;
+    GLPtr<GrGLDeleteQueriesProc> fDeleteQueries;
+    GLPtr<GrGLDeleteRenderbuffersProc> fDeleteRenderbuffers;
+    GLPtr<GrGLDeleteShaderProc> fDeleteShader;
+    GLPtr<GrGLDeleteTexturesProc> fDeleteTextures;
+    GLPtr<GrGLDepthMaskProc> fDepthMask;
+    GLPtr<GrGLDisableProc> fDisable;
+    GLPtr<GrGLDisableVertexAttribArrayProc> fDisableVertexAttribArray;
+    GLPtr<GrGLDrawArraysProc> fDrawArrays;
+    GLPtr<GrGLDrawBufferProc> fDrawBuffer;
+    GLPtr<GrGLDrawBuffersProc> fDrawBuffers;
+    GLPtr<GrGLDrawElementsProc> fDrawElements;
+    GLPtr<GrGLEnableProc> fEnable;
+    GLPtr<GrGLEnableVertexAttribArrayProc> fEnableVertexAttribArray;
+    GLPtr<GrGLEndQueryProc> fEndQuery;
+    GLPtr<GrGLFinishProc> fFinish;
+    GLPtr<GrGLFlushProc> fFlush;
+    GLPtr<GrGLFramebufferRenderbufferProc> fFramebufferRenderbuffer;
+    GLPtr<GrGLFramebufferTexture2DProc> fFramebufferTexture2D;
+    GLPtr<GrGLFrontFaceProc> fFrontFace;
+    GLPtr<GrGLGenBuffersProc> fGenBuffers;
+    GLPtr<GrGLGenFramebuffersProc> fGenFramebuffers;
+    GLPtr<GrGLGenQueriesProc> fGenQueries;
+    GLPtr<GrGLGenRenderbuffersProc> fGenRenderbuffers;
+    GLPtr<GrGLGenTexturesProc> fGenTextures;
+    GLPtr<GrGLGetBufferParameterivProc> fGetBufferParameteriv;
+    GLPtr<GrGLGetErrorProc> fGetError;
+    GLPtr<GrGLGetFramebufferAttachmentParameterivProc> fGetFramebufferAttachmentParameteriv;
+    GLPtr<GrGLGetIntegervProc> fGetIntegerv;
+    GLPtr<GrGLGetQueryObjecti64vProc> fGetQueryObjecti64v;
+    GLPtr<GrGLGetQueryObjectivProc> fGetQueryObjectiv;
+    GLPtr<GrGLGetQueryObjectui64vProc> fGetQueryObjectui64v;
+    GLPtr<GrGLGetQueryObjectuivProc> fGetQueryObjectuiv;
+    GLPtr<GrGLGetQueryivProc> fGetQueryiv;
+    GLPtr<GrGLGetProgramInfoLogProc> fGetProgramInfoLog;
+    GLPtr<GrGLGetProgramivProc> fGetProgramiv;
+    GLPtr<GrGLGetRenderbufferParameterivProc> fGetRenderbufferParameteriv;
+    GLPtr<GrGLGetShaderInfoLogProc> fGetShaderInfoLog;
+    GLPtr<GrGLGetShaderivProc> fGetShaderiv;
+    GLPtr<GrGLGetStringProc> fGetString;
+    GLPtr<GrGLGetTexLevelParameterivProc> fGetTexLevelParameteriv;
+    GLPtr<GrGLGetUniformLocationProc> fGetUniformLocation;
+    GLPtr<GrGLLineWidthProc> fLineWidth;
+    GLPtr<GrGLLinkProgramProc> fLinkProgram;
+    GLPtr<GrGLMapBufferProc> fMapBuffer;
+    GLPtr<GrGLPixelStoreiProc> fPixelStorei;
+    GLPtr<GrGLQueryCounterProc> fQueryCounter;
+    GLPtr<GrGLReadBufferProc> fReadBuffer;
+    GLPtr<GrGLReadPixelsProc> fReadPixels;
+    GLPtr<GrGLRenderbufferStorageProc> fRenderbufferStorage;
+    GLPtr<GrGLRenderbufferStorageMultisampleProc> fRenderbufferStorageMultisample;
+    GLPtr<GrGLRenderbufferStorageMultisampleCoverageProc> fRenderbufferStorageMultisampleCoverage;
+    GLPtr<GrGLResolveMultisampleFramebufferProc> fResolveMultisampleFramebuffer;
+    GLPtr<GrGLScissorProc> fScissor;
+    GLPtr<GrGLShaderSourceProc> fShaderSource;
+    GLPtr<GrGLStencilFuncProc> fStencilFunc;
+    GLPtr<GrGLStencilFuncSeparateProc> fStencilFuncSeparate;
+    GLPtr<GrGLStencilMaskProc> fStencilMask;
+    GLPtr<GrGLStencilMaskSeparateProc> fStencilMaskSeparate;
+    GLPtr<GrGLStencilOpProc> fStencilOp;
+    GLPtr<GrGLStencilOpSeparateProc> fStencilOpSeparate;
+    GLPtr<GrGLTexImage2DProc> fTexImage2D;
+    GLPtr<GrGLTexParameteriProc> fTexParameteri;
+    GLPtr<GrGLTexParameterivProc> fTexParameteriv;
+    GLPtr<GrGLTexSubImage2DProc> fTexSubImage2D;
+    GLPtr<GrGLTexStorage2DProc> fTexStorage2D;
+    GLPtr<GrGLUniform1fProc> fUniform1f;
+    GLPtr<GrGLUniform1iProc> fUniform1i;
+    GLPtr<GrGLUniform1fvProc> fUniform1fv;
+    GLPtr<GrGLUniform1ivProc> fUniform1iv;
+    GLPtr<GrGLUniform2fProc> fUniform2f;
+    GLPtr<GrGLUniform2iProc> fUniform2i;
+    GLPtr<GrGLUniform2fvProc> fUniform2fv;
+    GLPtr<GrGLUniform2ivProc> fUniform2iv;
+    GLPtr<GrGLUniform3fProc> fUniform3f;
+    GLPtr<GrGLUniform3iProc> fUniform3i;
+    GLPtr<GrGLUniform3fvProc> fUniform3fv;
+    GLPtr<GrGLUniform3ivProc> fUniform3iv;
+    GLPtr<GrGLUniform4fProc> fUniform4f;
+    GLPtr<GrGLUniform4iProc> fUniform4i;
+    GLPtr<GrGLUniform4fvProc> fUniform4fv;
+    GLPtr<GrGLUniform4ivProc> fUniform4iv;
+    GLPtr<GrGLUniformMatrix2fvProc> fUniformMatrix2fv;
+    GLPtr<GrGLUniformMatrix3fvProc> fUniformMatrix3fv;
+    GLPtr<GrGLUniformMatrix4fvProc> fUniformMatrix4fv;
+    GLPtr<GrGLUnmapBufferProc> fUnmapBuffer;
+    GLPtr<GrGLUseProgramProc> fUseProgram;
+    GLPtr<GrGLVertexAttrib4fvProc> fVertexAttrib4fv;
+    GLPtr<GrGLVertexAttribPointerProc> fVertexAttribPointer;
+    GLPtr<GrGLViewportProc> fViewport;
 
-    // FBO Extension Functions
-    GrGLBindFramebufferProc fBindFramebuffer;
-    GrGLBindRenderbufferProc fBindRenderbuffer;
-    GrGLCheckFramebufferStatusProc fCheckFramebufferStatus;
-    GrGLDeleteFramebuffersProc fDeleteFramebuffers;
-    GrGLDeleteRenderbuffersProc fDeleteRenderbuffers;
-    GrGLFramebufferRenderbufferProc fFramebufferRenderbuffer;
-    GrGLFramebufferTexture2DProc fFramebufferTexture2D;
-    GrGLGenFramebuffersProc fGenFramebuffers;
-    GrGLGenRenderbuffersProc fGenRenderbuffers;
-    GrGLGetFramebufferAttachmentParameterivProc fGetFramebufferAttachmentParameteriv;
-    GrGLGetRenderbufferParameterivProc fGetRenderbufferParameteriv;
-    GrGLRenderbufferStorageProc fRenderbufferStorage;
-
-    // Multisampling Extension Functions
-    // same prototype for ARB_FBO, EXT_FBO, GL 3.0, & Apple ES extension
-    GrGLRenderbufferStorageMultisampleProc fRenderbufferStorageMultisample;
-    // desktop: ext_fbo_blit, arb_fbo, gl 3.0
-    GrGLBlitFramebufferProc fBlitFramebuffer;
-    // apple's es extension
-    GrGLResolveMultisampleFramebufferProc fResolveMultisampleFramebuffer;
-
-    // Buffer mapping (extension in ES).
-    GrGLMapBufferProc fMapBuffer;
-    GrGLUnmapBufferProc fUnmapBuffer;
-
-    // Dual Source Blending
-    GrGLBindFragDataLocationIndexedProc fBindFragDataLocationIndexed;
+    // Experimental: Functions for GL_NV_path_rendering. These will be
+    // alphabetized with the above functions once this is fully supported
+    // (and functions we are unlikely to use will possibly be omitted).
+    GLPtr<GrGLMatrixModeProc> fMatrixMode;
+    GLPtr<GrGLLoadIdentityProc> fLoadIdentity;
+    GLPtr<GrGLLoadMatrixfProc> fLoadMatrixf;
+    GLPtr<GrGLPathCommandsProc> fPathCommands;
+    GLPtr<GrGLPathCoordsProc> fPathCoords;
+    GLPtr<GrGLPathSubCommandsProc> fPathSubCommands;
+    GLPtr<GrGLPathSubCoordsProc> fPathSubCoords;
+    GLPtr<GrGLPathStringProc> fPathString;
+    GLPtr<GrGLPathGlyphsProc> fPathGlyphs;
+    GLPtr<GrGLPathGlyphRangeProc> fPathGlyphRange;
+    GLPtr<GrGLWeightPathsProc> fWeightPaths;
+    GLPtr<GrGLCopyPathProc> fCopyPath;
+    GLPtr<GrGLInterpolatePathsProc> fInterpolatePaths;
+    GLPtr<GrGLTransformPathProc> fTransformPath;
+    GLPtr<GrGLPathParameterivProc> fPathParameteriv;
+    GLPtr<GrGLPathParameteriProc> fPathParameteri;
+    GLPtr<GrGLPathParameterfvProc> fPathParameterfv;
+    GLPtr<GrGLPathParameterfProc> fPathParameterf;
+    GLPtr<GrGLPathDashArrayProc> fPathDashArray;
+    GLPtr<GrGLGenPathsProc> fGenPaths;
+    GLPtr<GrGLDeletePathsProc> fDeletePaths;
+    GLPtr<GrGLIsPathProc> fIsPath;
+    GLPtr<GrGLPathStencilFuncProc> fPathStencilFunc;
+    GLPtr<GrGLPathStencilDepthOffsetProc> fPathStencilDepthOffset;
+    GLPtr<GrGLStencilFillPathProc> fStencilFillPath;
+    GLPtr<GrGLStencilStrokePathProc> fStencilStrokePath;
+    GLPtr<GrGLStencilFillPathInstancedProc> fStencilFillPathInstanced;
+    GLPtr<GrGLStencilStrokePathInstancedProc> fStencilStrokePathInstanced;
+    GLPtr<GrGLPathCoverDepthFuncProc> fPathCoverDepthFunc;
+    GLPtr<GrGLPathColorGenProc> fPathColorGen;
+    GLPtr<GrGLPathTexGenProc> fPathTexGen;
+    GLPtr<GrGLPathFogGenProc> fPathFogGen;
+    GLPtr<GrGLCoverFillPathProc> fCoverFillPath;
+    GLPtr<GrGLCoverStrokePathProc> fCoverStrokePath;
+    GLPtr<GrGLCoverFillPathInstancedProc> fCoverFillPathInstanced;
+    GLPtr<GrGLCoverStrokePathInstancedProc> fCoverStrokePathInstanced;
+    GLPtr<GrGLGetPathParameterivProc> fGetPathParameteriv;
+    GLPtr<GrGLGetPathParameterfvProc> fGetPathParameterfv;
+    GLPtr<GrGLGetPathCommandsProc> fGetPathCommands;
+    GLPtr<GrGLGetPathCoordsProc> fGetPathCoords;
+    GLPtr<GrGLGetPathDashArrayProc> fGetPathDashArray;
+    GLPtr<GrGLGetPathMetricsProc> fGetPathMetrics;
+    GLPtr<GrGLGetPathMetricRangeProc> fGetPathMetricRange;
+    GLPtr<GrGLGetPathSpacingProc> fGetPathSpacing;
+    GLPtr<GrGLGetPathColorGenivProc> fGetPathColorGeniv;
+    GLPtr<GrGLGetPathColorGenfvProc> fGetPathColorGenfv;
+    GLPtr<GrGLGetPathTexGenivProc> fGetPathTexGeniv;
+    GLPtr<GrGLGetPathTexGenfvProc> fGetPathTexGenfv;
+    GLPtr<GrGLIsPointInFillPathProc> fIsPointInFillPath;
+    GLPtr<GrGLIsPointInStrokePathProc> fIsPointInStrokePath;
+    GLPtr<GrGLGetPathLengthProc> fGetPathLength;
+    GLPtr<GrGLPointAlongPathProc> fPointAlongPath;
 
     // Per-GL func callback
 #if GR_GL_PER_GL_FUNC_CALLBACK
diff --git a/include/gpu/gl/SkANGLEGLContext.h b/include/gpu/gl/SkANGLEGLContext.h
new file mode 100644
index 0000000..7bcf07a
--- /dev/null
+++ b/include/gpu/gl/SkANGLEGLContext.h
@@ -0,0 +1,49 @@
+
+/*
+ * 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 SkANGLEGLContext_DEFINED
+#define SkANGLEGLContext_DEFINED
+
+#if SK_ANGLE
+
+#include "SkGLContext.h"
+
+#include <GLES2/gl2.h>
+#include <EGL/egl.h>
+
+class SkANGLEGLContext : public SkGLContext {
+public:
+    SkANGLEGLContext();
+
+    virtual ~SkANGLEGLContext();
+
+    virtual void makeCurrent() const SK_OVERRIDE;
+
+    class AutoContextRestore {
+    public:
+        AutoContextRestore();
+        ~AutoContextRestore();
+
+    private:
+        EGLContext fOldEGLContext;
+        EGLDisplay fOldDisplay;
+        EGLSurface fOldSurface;
+    };
+
+protected:
+    virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
+    virtual void destroyGLContext() SK_OVERRIDE;
+
+private:
+    EGLContext fContext;
+    EGLDisplay fDisplay;
+    EGLSurface fSurface;
+};
+
+#endif
+
+#endif
diff --git a/include/gpu/gl/SkDebugGLContext.h b/include/gpu/gl/SkDebugGLContext.h
new file mode 100644
index 0000000..6e29b67
--- /dev/null
+++ b/include/gpu/gl/SkDebugGLContext.h
@@ -0,0 +1,26 @@
+
+/*
+ * 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 SkDebugGLContext_DEFINED
+#define SkDebugGLContext_DEFINED
+
+#include "SkGLContext.h"
+
+class SkDebugGLContext : public SkGLContext {
+
+public:
+    SkDebugGLContext() {};
+
+    virtual void makeCurrent() const SK_OVERRIDE {};
+
+protected:
+    virtual const GrGLInterface* createGLContext() SK_OVERRIDE;
+
+    virtual void destroyGLContext() SK_OVERRIDE {};
+};
+
+#endif
diff --git a/include/gpu/gl/SkGLContext.h b/include/gpu/gl/SkGLContext.h
index 542d1bb..3f99eae 100644
--- a/include/gpu/gl/SkGLContext.h
+++ b/include/gpu/gl/SkGLContext.h
@@ -18,6 +18,8 @@
 
 class SkGLContext : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkGLContext)
+
     SkGLContext();
     virtual ~SkGLContext();
 
@@ -36,8 +38,8 @@
 
 protected:
     /**
-     * Subclass implements this to make a GL context. The returned GrGLInterface 
-     * should be populated with functions compatible with the context. The 
+     * Subclass implements this to make a GL context. The returned GrGLInterface
+     * should be populated with functions compatible with the context. The
      * format and size of backbuffers does not matter since an FBO will be
      * created.
      */
@@ -51,7 +53,11 @@
 private:
     SkString fExtensionString;
     GrGLuint fFBO;
+    GrGLuint fColorBufferID;
+    GrGLuint fDepthStencilBufferID;
     const GrGLInterface* fGL;
+
+    typedef SkRefCnt INHERITED;
 };
 
 /**
diff --git a/include/gpu/gl/SkMesaGLContext.h b/include/gpu/gl/SkMesaGLContext.h
index 5c329ff..14a3ca7 100644
--- a/include/gpu/gl/SkMesaGLContext.h
+++ b/include/gpu/gl/SkMesaGLContext.h
@@ -30,9 +30,9 @@
 
     private:
         Context fOldContext;
-        GLint   fOldWidth;
-        GLint   fOldHeight;
-        GLint   fOldFormat;
+        GrGLint fOldWidth;
+        GrGLint fOldHeight;
+        GrGLint fOldFormat;
         void*   fOldImage;
     };
 
diff --git a/include/gpu/gl/SkNativeGLContext.h b/include/gpu/gl/SkNativeGLContext.h
index 36461ba..93744e7 100644
--- a/include/gpu/gl/SkNativeGLContext.h
+++ b/include/gpu/gl/SkNativeGLContext.h
@@ -12,8 +12,7 @@
 
 #if defined(SK_BUILD_FOR_MAC)
     #include <AGL/agl.h>
-
-#elif defined(SK_BUILD_FOR_ANDROID)
+#elif defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_NACL)
     #include <GLES2/gl2.h>
     #include <EGL/egl.h>
 #elif defined(SK_BUILD_FOR_UNIX)
@@ -40,6 +39,10 @@
     private:
     #if defined(SK_BUILD_FOR_MAC)
         AGLContext fOldAGLContext;
+    #elif defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_NACL)
+        EGLContext fOldEGLContext;
+        EGLDisplay fOldDisplay;
+        EGLSurface fOldSurface;
     #elif defined(SK_BUILD_FOR_UNIX)
         GLXContext fOldGLXContext;
         Display* fOldDisplay;
@@ -47,10 +50,9 @@
     #elif defined(SK_BUILD_FOR_WIN32)
         HDC fOldHDC;
         HGLRC fOldHGLRC;
-    #elif defined(SK_BUILD_FOR_ANDROID)
-        EGLContext fOldEGLContext;
-        EGLDisplay fOldDisplay;
-        EGLSurface fOldSurface;
+
+    #elif defined(SK_BUILD_FOR_IOS)
+        void* fEAGLContext;
     #endif
     };
 
@@ -61,6 +63,10 @@
 private:
 #if defined(SK_BUILD_FOR_MAC)
     AGLContext fContext;
+#elif defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_NACL)
+    EGLContext fContext;
+    EGLDisplay fDisplay;
+    EGLSurface fSurface;
 #elif defined(SK_BUILD_FOR_UNIX)
     GLXContext fContext;
     Display* fDisplay;
@@ -71,10 +77,8 @@
     HDC fDeviceContext;
     HGLRC fGlRenderContext;
     static ATOM gWC;
-#elif defined(SK_BUILD_FOR_ANDROID)
-    EGLContext fContext;
-    EGLDisplay fDisplay;
-    EGLSurface fSurface;
+#elif defined(SK_BUILD_FOR_IOS)
+    void* fEAGLContext;
 #endif
 };
 
diff --git a/include/gpu/gl/SkNullGLContext.h b/include/gpu/gl/SkNullGLContext.h
index 9e16cee..83213a3 100644
--- a/include/gpu/gl/SkNullGLContext.h
+++ b/include/gpu/gl/SkNullGLContext.h
@@ -24,4 +24,3 @@
 };
 
 #endif
-
diff --git a/include/images/SkBitmapFactory.h b/include/images/SkBitmapFactory.h
new file mode 100644
index 0000000..6779dc2
--- /dev/null
+++ b/include/images/SkBitmapFactory.h
@@ -0,0 +1,46 @@
+/*
+ * 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 SkBitmapFactory_DEFINED
+#define SkBitmapFactory_DEFINED
+
+class SkBitmap;
+class SkData;
+
+/**
+ *  General purpose factory for decoding bitmaps.
+ *
+ *  Currently only provides a way to decode a bitmap or its dimensions from an SkData. Future plans
+ *  include options to provide a bitmap which caches the pixel data.
+ */
+class SkBitmapFactory {
+
+public:
+    enum Constraints {
+        /**
+         *  Only decode the bounds of the bitmap. No pixels will be allocated.
+         */
+        kDecodeBoundsOnly_Constraint,
+
+        /**
+         *  Decode the bounds and pixels of the bitmap.
+         */
+        kDecodePixels_Constraint,
+    };
+
+    /**
+     *  Decodes an SkData into an SkBitmap.
+     *  @param SkBitmap Already created bitmap to encode into.
+     *  @param SkData Encoded SkBitmap data.
+     *  @param constraint Specifications for how to do the decoding.
+     *  @return True on success. If false, passed in SkBitmap is unmodified.
+     */
+    static bool DecodeBitmap(SkBitmap*, const SkData*,
+                             Constraints constraint = kDecodePixels_Constraint);
+};
+
+#endif // SkBitmapFactory_DEFINED
diff --git a/include/images/SkFlipPixelRef.h b/include/images/SkFlipPixelRef.h
index 455a3da..3443e3d 100644
--- a/include/images/SkFlipPixelRef.h
+++ b/include/images/SkFlipPixelRef.h
@@ -33,6 +33,7 @@
     const SkRegion& beginUpdate(SkBitmap* device);
     void endUpdate();
     
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
 private:
     void getFrontBack(const void** front, void** back) const {
         if (front) {
@@ -50,21 +51,10 @@
     static void CopyBitsFromAddr(const SkBitmap& dst, const SkRegion& clip,
                                  const void* srcAddr);
 
-    // serialization
-
-public:
-    virtual Factory getFactory() const { return Create; }
-    virtual void flatten(SkFlattenableWriteBuffer&) const;
-    static SkPixelRef* Create(SkFlattenableReadBuffer& buffer);
-
-    SK_DECLARE_PIXEL_REF_REGISTRAR()
-
 protected:
     virtual void* onLockPixels(SkColorTable**);
     virtual void onUnlockPixels();
 
-    SkFlipPixelRef(SkFlattenableReadBuffer&);
-
 private:
     SkMutex         fMutex;
     SkPageFlipper   fFlipper;
diff --git a/include/images/SkImageDecoder.h b/include/images/SkImageDecoder.h
index 361e1a0..f13674a 100644
--- a/include/images/SkImageDecoder.h
+++ b/include/images/SkImageDecoder.h
@@ -90,10 +90,14 @@
     */
     class Peeker : public SkRefCnt {
     public:
+        SK_DECLARE_INST_COUNT(Peeker)
+
         /** Return true to continue decoding, or false to indicate an error, which
             will cause the decoder to not return the image.
         */
         virtual bool peek(const char tag[], const void* data, size_t length) = 0;
+    private:
+        typedef SkRefCnt INHERITED;
     };
 
     Peeker* getPeeker() const { return fPeeker; }
@@ -106,11 +110,16 @@
     */
     class Chooser : public SkRefCnt {
     public:
+        SK_DECLARE_INST_COUNT(Chooser)
+
         virtual void begin(int count) {}
         virtual void inspect(int index, SkBitmap::Config config, int width, int height) {}
         /** Return the index of the subimage you want, or -1 to choose none of them.
         */
         virtual int choose() = 0;
+
+    private:
+        typedef SkRefCnt INHERITED;
     };
 
     Chooser* getChooser() const { return fChooser; }
@@ -412,7 +421,12 @@
  */
 class SkImageDecoderFactory : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkImageDecoderFactory)
+
     virtual SkImageDecoder* newDecoder(SkStream*) = 0;
+
+private:
+    typedef SkRefCnt INHERITED;
 };
 
 class SkDefaultImageDecoderFactory : SkImageDecoderFactory {
@@ -423,5 +437,26 @@
     }
 };
 
+// This macro declares a global (i.e., non-class owned) creation entry point
+// for each decoder (e.g., CreateJPEGImageDecoder)
+#define DECLARE_DECODER_CREATOR(codec)          \
+    SkImageDecoder *Create ## codec ();
+
+// This macro defines the global creation entry point for each decoder. Each
+// decoder implementation that registers with the decoder factory must call it.
+#define DEFINE_DECODER_CREATOR(codec)           \
+    SkImageDecoder *Create ## codec () {        \
+        return SkNEW( Sk ## codec );            \
+    }
+
+// All the decoders known by Skia. Note that, depending on the compiler settings,
+// not all of these will be available
+DECLARE_DECODER_CREATOR(BMPImageDecoder);
+DECLARE_DECODER_CREATOR(GIFImageDecoder);
+DECLARE_DECODER_CREATOR(ICOImageDecoder);
+DECLARE_DECODER_CREATOR(JPEGImageDecoder);
+DECLARE_DECODER_CREATOR(PNGImageDecoder);
+DECLARE_DECODER_CREATOR(WBMPImageDecoder);
+DECLARE_DECODER_CREATOR(WEBPImageDecoder);
 
 #endif
diff --git a/include/images/SkImageEncoder.h b/include/images/SkImageEncoder.h
index a4831f1..d4d7169 100644
--- a/include/images/SkImageEncoder.h
+++ b/include/images/SkImageEncoder.h
@@ -23,14 +23,31 @@
     static SkImageEncoder* Create(Type);
 
     virtual ~SkImageEncoder();
-    
+
     /*  Quality ranges from 0..100 */
     enum {
         kDefaultQuality = 80
     };
 
-    bool encodeFile(const char file[], const SkBitmap&, int quality);
-    bool encodeStream(SkWStream*, const SkBitmap&, int quality);
+    /**
+     * Encode bitmap 'bm' in the desired format, writing results to
+     * file 'file', at quality level 'quality' (which can be in range
+     * 0-100).
+     *
+     * Calls the particular implementation's onEncode() method to
+     * actually do the encoding.
+     */
+    bool encodeFile(const char file[], const SkBitmap& bm, int quality);
+
+    /**
+     * Encode bitmap 'bm' in the desired format, writing results to
+     * stream 'stream', at quality level 'quality' (which can be in
+     * range 0-100).
+     *
+     * Calls the particular implementation's onEncode() method to
+     * actually do the encoding.
+     */
+    bool encodeStream(SkWStream* stream, const SkBitmap& bm, int quality);
 
     static bool EncodeFile(const char file[], const SkBitmap&, Type,
                            int quality);
@@ -38,7 +55,32 @@
                            int quality);
 
 protected:
-    virtual bool onEncode(SkWStream*, const SkBitmap&, int quality) = 0;
+    /**
+     * Encode bitmap 'bm' in the desired format, writing results to
+     * stream 'stream', at quality level 'quality' (which can be in
+     * range 0-100).
+     *
+     * This must be overridden by each SkImageEncoder implementation.
+     */
+    virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) = 0;
 };
 
+// This macro declares a global (i.e., non-class owned) creation entry point
+// for each encoder (e.g., CreateJPEGImageEncoder)
+#define DECLARE_ENCODER_CREATOR(codec)          \
+    SkImageEncoder *Create ## codec ();
+
+// This macro defines the global creation entry point for each encoder. Each
+// encoder implementation that registers with the encoder factory must call it.
+#define DEFINE_ENCODER_CREATOR(codec)           \
+    SkImageEncoder *Create ## codec () {        \
+        return SkNEW( Sk ## codec );            \
+    }
+
+// All the encoders known by Skia. Note that, depending on the compiler settings,
+// not all of these will be available
+DECLARE_ENCODER_CREATOR(JPEGImageEncoder);
+DECLARE_ENCODER_CREATOR(PNGImageEncoder);
+DECLARE_ENCODER_CREATOR(WEBPImageEncoder);
+
 #endif
diff --git a/include/images/SkImageRef.h b/include/images/SkImageRef.h
index f0f06b6..bca4305 100644
--- a/include/images/SkImageRef.h
+++ b/include/images/SkImageRef.h
@@ -27,42 +27,39 @@
         since it may be accessed from another thread, the caller must ensure
         that this imageref is the only owner of the stream. i.e. - sole
         ownership of the stream object is transferred to this imageref object.
-    
+
         @param stream The stream containing the encoded image data. This may be
                       retained (by calling ref()), so the caller should not
                       explicitly delete it.
         @param config The preferred config of the decoded bitmap.
         @param sampleSize Requested sampleSize for decoding. Defaults to 1.
     */
-    SkImageRef(SkStream*, SkBitmap::Config config, int sampleSize = 1);
+    SkImageRef(SkStream*, SkBitmap::Config config, int sampleSize = 1, SkBaseMutex* mutex = NULL);
     virtual ~SkImageRef();
 
     /** this value is passed onto the decoder. Default is true
      */
     void setDitherImage(bool dither) { fDoDither = dither; }
-    
+
     /** Return true if the image can be decoded. If so, and bitmap is non-null,
         call its setConfig() with the corresponding values, but explicitly will
         not set its pixels or colortable. Use SkPixelRef::lockPixels() for that.
-     
+
         If there has been an error decoding the bitmap, this will return false
         and ignore the bitmap parameter.
     */
     bool getInfo(SkBitmap* bm);
-    
+
     /** Return true if the image can be decoded and is opaque. Calling this
         method will decode and set the pixels in the specified bitmap and
         sets the isOpaque flag.
      */
     bool isOpaque(SkBitmap* bm);
-    
+
     SkImageDecoderFactory* getDecoderFactory() const { return fFactory; }
     // returns the factory parameter
     SkImageDecoderFactory* setDecoderFactory(SkImageDecoderFactory*);
 
-    // overrides
-    virtual void flatten(SkFlattenableWriteBuffer&) const;
-
 protected:
     /** Override if you want to install a custom allocator.
         When this is called we will have already acquired the mutex!
@@ -76,13 +73,14 @@
 
     virtual void* onLockPixels(SkColorTable**);
     // override this in your subclass to clean up when we're unlocking pixels
-    virtual void onUnlockPixels();
-    
-    SkImageRef(SkFlattenableReadBuffer&);
+    virtual void onUnlockPixels() {}
+
+    SkImageRef(SkFlattenableReadBuffer&, SkBaseMutex* mutex = NULL);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     SkBitmap fBitmap;
 
-private:    
+private:
     SkStream* setStream(SkStream*);
     // called with mutex already held. returns true if the bitmap is in the
     // requested state (or further, i.e. has pixels)
@@ -94,12 +92,12 @@
     int                     fSampleSize;
     bool                    fDoDither;
     bool                    fErrorInDecoding;
-    
+
     friend class SkImageRefPool;
-    
-    SkImageRef*  fPrev, *fNext;    
+
+    SkImageRef*  fPrev, *fNext;
     size_t ramUsed() const;
-    
+
     typedef SkPixelRef INHERITED;
 };
 
diff --git a/include/images/SkImageRef_GlobalPool.h b/include/images/SkImageRef_GlobalPool.h
index 909ee71..914b0eb 100644
--- a/include/images/SkImageRef_GlobalPool.h
+++ b/include/images/SkImageRef_GlobalPool.h
@@ -17,29 +17,23 @@
     // if pool is null, use the global pool
     SkImageRef_GlobalPool(SkStream*, SkBitmap::Config, int sampleSize = 1);
     virtual ~SkImageRef_GlobalPool();
-    
-    // overrides
-    virtual Factory getFactory() const {
-        return Create;
-    }
-    static SkPixelRef* Create(SkFlattenableReadBuffer&);
-    
-    SK_DECLARE_PIXEL_REF_REGISTRAR()
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageRef_GlobalPool)
 
     // API to control the global pool
 
     /** Return the amount specified as the budget for the cache (in bytes).
      */
     static size_t GetRAMBudget();
-    
+
     /** Set a new budget value for the cache.
      */
     static void SetRAMBudget(size_t);
-    
+
     /** Return how much ram is currently in use by the global cache.
      */
     static size_t GetRAMUsed();
-    
+
     /** Free up (approximately) enough such that the amount used by the cache
      is <= the specified amount. Since some images may be "in use", the
      amount actually freed may not always result in a ram usage value <=
@@ -48,16 +42,16 @@
      amount.
      */
     static void SetRAMUsed(size_t usageInBytes);
-    
+
     static void DumpPool();
 
 protected:
     virtual bool onDecode(SkImageDecoder* codec, SkStream* stream,
                           SkBitmap* bitmap, SkBitmap::Config config,
                           SkImageDecoder::Mode mode);
-    
+
     virtual void onUnlockPixels();
-    
+
     SkImageRef_GlobalPool(SkFlattenableReadBuffer&);
 
 private:
diff --git a/include/images/SkImages.h b/include/images/SkImages.h
new file mode 100644
index 0000000..abe10da
--- /dev/null
+++ b/include/images/SkImages.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+class SkImages {
+public:
+    /**
+     * Initializes flattenables in the images project.
+     */
+    static void InitializeFlattenables();
+};
diff --git a/include/images/SkMovie.h b/include/images/SkMovie.h
index f52a786..f32d609 100644
--- a/include/images/SkMovie.h
+++ b/include/images/SkMovie.h
@@ -17,6 +17,8 @@
 
 class SkMovie : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkMovie)
+
     /** Try to create a movie from the stream. If the stream format is not
         supported, return NULL.
     */
@@ -38,7 +40,7 @@
     int     width();
     int     height();
     int     isOpaque();
-    
+
     /** Specify the time code (between 0...duration) to sample a bitmap
         from the movie. Returns true if this time code generated a different
         bitmap/frame from the previous state (i.e. true means you need to
@@ -48,7 +50,7 @@
 
     // return the right bitmap for the current time code
     const SkBitmap& bitmap();
-    
+
 protected:
     struct Info {
         SkMSec  fDuration;
@@ -69,8 +71,10 @@
     SkMSec      fCurrTime;
     SkBitmap    fBitmap;
     bool        fNeedBitmap;
-    
+
     void ensureInfo();
+
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
diff --git a/include/images/SkPageFlipper.h b/include/images/SkPageFlipper.h
index 1a18856..7779c30 100644
--- a/include/images/SkPageFlipper.h
+++ b/include/images/SkPageFlipper.h
@@ -25,7 +25,7 @@
 public:
     SkPageFlipper();
     SkPageFlipper(int width, int height);
-    
+
     int width() const { return fWidth; }
     int height() const { return fHeight; }
 
@@ -44,7 +44,7 @@
         region (provided by the caller) is the area that should be copied from
         the front page to the back page (will not intersect with the returned
         inval region.
-     
+
         Once this is called, the two internal regions are swapped, so the *new*
         back inval region is ready to receive new inval calls.
      */
@@ -60,4 +60,3 @@
 };
 
 #endif
-
diff --git a/include/pdf/SkPDFCatalog.h b/include/pdf/SkPDFCatalog.h
deleted file mode 100644
index 68a244f..0000000
--- a/include/pdf/SkPDFCatalog.h
+++ /dev/null
@@ -1,137 +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 "SkPDFDocument.h"
-#include "SkPDFTypes.h"
-#include "SkRefCnt.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 SK_API SkPDFCatalog {
-public:
-    /** Create a PDF catalog.
-     */
-    explicit SkPDFCatalog(SkPDFDocument::Flags flags);
-    ~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.  Returns
-     *  the object's size.
-     *  @param obj         The object to add.
-     *  @param offset      The byte offset in the output stream of this object.
-     */
-    size_t setFileOffset(SkPDFObject* obj, size_t offset);
-
-    /** Output the object number for the passed object.
-     *  @param obj         The object of interest.
-     *  @param stream      The writable output stream to send the output to.
-     */
-    void emitObjectNumber(SkWStream* stream, SkPDFObject* obj);
-
-    /** Return the number of bytes that would be emitted for the passed
-     *  object's object number.
-     *  @param obj         The object of interest
-     */
-    size_t getObjectNumberSize(SkPDFObject* obj);
-
-    /** Return the document flags in effect for this catalog/document.
-     */
-    SkPDFDocument::Flags getDocumentFlags() const { return fDocumentFlags; }
-
-    /** 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);
-
-    /** Set file offsets for the resources of substitute objects.
-     *  @param fileOffset Accumulated offset of current document.
-     *  @param firstPage  Indicate whether this is for the first page only.
-     *  @return           Total size of resources of substitute objects.
-     */
-    off_t setSubstituteResourcesOffsets(off_t fileOffset, bool firstPage);
-
-    /** Emit the resources of substitute objects.
-     */
-    void emitSubstituteResources(SkWStream* stream, bool firstPage);
-
-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<struct Rec> fCatalog;
-
-    // TODO(arthurhsu): Make this a hash if it's a performance problem.
-    SkTDArray<SubstituteMapping> fSubstituteMap;
-    SkTDArray<SkPDFObject*> fSubstituteResourcesFirstPage;
-    SkTDArray<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;
-
-    SkPDFDocument::Flags fDocumentFlags;
-
-    int findObjectIndex(SkPDFObject* obj) const;
-
-    int assignObjNum(SkPDFObject* obj);
-
-    SkTDArray<SkPDFObject*>* getSubstituteList(bool firstPage);
-};
-
-#endif
diff --git a/include/pdf/SkPDFDevice.h b/include/pdf/SkPDFDevice.h
index 9d6b54c..6d86226 100644
--- a/include/pdf/SkPDFDevice.h
+++ b/include/pdf/SkPDFDevice.h
@@ -14,6 +14,7 @@
 #include "SkDevice.h"
 #include "SkPaint.h"
 #include "SkPath.h"
+#include "SkRect.h"
 #include "SkRefCnt.h"
 #include "SkStream.h"
 #include "SkTScopedPtr.h"
@@ -100,6 +101,9 @@
     virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
                             const SkPaint&) SK_OVERRIDE;
 
+    virtual void onAttachToCanvas(SkCanvas* canvas) SK_OVERRIDE;
+    virtual void onDetachFromCanvas() SK_OVERRIDE;
+
     enum DrawingArea {
         kContent_DrawingArea,  // Drawing area for the page content.
         kMargin_DrawingArea,   // Drawing area for the margin content.
@@ -124,16 +128,25 @@
 
     /** Get the list of resources (PDF objects) used on this page.
      *  @param resourceList A list to append the resources to.
+     *  @param recursive    If recursive is true, get the resources of the
+     *                      device's resources recursively. (Useful for adding
+     *                      objects to the catalog.)
      */
-    SK_API void getResources(SkTDArray<SkPDFObject*>* resourceList) const;
+    SK_API void getResources(SkTDArray<SkPDFObject*>* resourceList,
+                             bool recursive) const;
 
     /** Get the fonts used on this device.
      */
     SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
 
-    /** Returns the media box for this device.
+    /** Returns a copy of the media box for this device. The caller is required
+     *  to unref() this when it is finished.
      */
-    SK_API SkRefPtr<SkPDFArray> getMediaBox() const;
+    SK_API SkPDFArray* copyMediaBox() const;
+
+    /** Get the annotations from this page, or NULL if there are none.
+     */
+    SK_API SkPDFArray* getAnnotations() const { return fAnnotations; }
 
     /** Returns a SkStream with the page contents.  The caller is responsible
         for a reference to the returned value.
@@ -173,7 +186,8 @@
     SkMatrix fInitialTransform;
     SkClipStack fExistingClipStack;
     SkRegion fExistingClipRegion;
-    SkRefPtr<SkPDFDict> fResourceDict;
+    SkPDFArray* fAnnotations;
+    SkPDFDict* fResourceDict;
 
     SkTDArray<SkPDFGraphicState*> fGraphicStateResources;
     SkTDArray<SkPDFObject*> fXObjectResources;
@@ -186,6 +200,8 @@
     ContentEntry* fLastMarginContentEntry;
     DrawingArea fDrawingArea;
 
+    const SkClipStack* fClipStack;
+
     // Accessor and setter functions based on the current DrawingArea.
     SkTScopedPtr<ContentEntry>* getContentEntries();
     ContentEntry* getLastContentEntry();
@@ -205,7 +221,7 @@
 
     void init();
     void cleanUp(bool clearFontUsage);
-    void createFormXObjectFromDevice(SkRefPtr<SkPDFFormXObject>* xobject);
+    SkPDFFormXObject* createFormXObjectFromDevice();
 
     // Clear the passed clip from all existing content entries.
     void clearClipFromContent(const SkClipStack* clipStack,
@@ -224,7 +240,7 @@
                                     const SkMatrix& matrix,
                                     const SkPaint& paint,
                                     bool hasText,
-                                    SkRefPtr<SkPDFFormXObject>* dst);
+                                    SkPDFFormXObject** dst);
     void finishContentEntry(SkXfermode::Mode xfermode,
                             SkPDFFormXObject* dst);
     bool isContentEmpty();
@@ -254,9 +270,10 @@
      */
     void copyContentEntriesToData(ContentEntry* entry, SkWStream* data) const;
 
-    // Disable the default copy and assign implementation.
-    SkPDFDevice(const SkPDFDevice&);
-    void operator=(const SkPDFDevice&);
+    bool handleAnnotations(const SkRect& r, const SkMatrix& matrix,
+                           const SkPaint& paint);
+
+    typedef SkDevice INHERITED;
 };
 
 #endif
diff --git a/include/pdf/SkPDFDocument.h b/include/pdf/SkPDFDocument.h
index c1c6fb4..167634e 100644
--- a/include/pdf/SkPDFDocument.h
+++ b/include/pdf/SkPDFDocument.h
@@ -10,15 +10,17 @@
 #ifndef SkPDFDocument_DEFINED
 #define SkPDFDocument_DEFINED
 
-#include "SkPDFTypes.h"
+#include "SkAdvancedTypefaceMetrics.h"
 #include "SkRefCnt.h"
 #include "SkTDArray.h"
 #include "SkTScopedPtr.h"
 
 class SkPDFCatalog;
 class SkPDFDevice;
+class SkPDFDict;
 class SkPDFPage;
-class SkWSteam;
+class SkPDFObject;
+class SkWStream;
 
 /** \class SkPDFDocument
 
@@ -27,10 +29,10 @@
 class SkPDFDocument {
 public:
     enum Flags {
-        kNoCompression_Flag = 0x01,  //!< mask disable stream compression.
-        kNoEmbedding_Flag   = 0x02,  //!< mask do not embed fonts.
+        kNoCompression_Flags = 0x01,  //!< mask disable stream compression.
+        kNoLinks_Flags       = 0x02,  //!< do not honor link annotations.
 
-        kDraftMode_Flags    = 0x03,
+        kDraftMode_Flags     = 0x01,
     };
     /** Create a PDF document.
      */
@@ -62,9 +64,10 @@
      */
     SK_API bool appendPage(SkPDFDevice* pdfDevice);
 
-    /** Get the list of pages in this document.
+    /** Get the count of unique font types used in the document.
      */
-    SK_API const SkTDArray<SkPDFPage*>& getPages();
+    SK_API void getCountOfFontTypes(
+        int counts[SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1]) const;
 
 private:
     SkTScopedPtr<SkPDFCatalog> fCatalog;
@@ -72,12 +75,12 @@
 
     SkTDArray<SkPDFPage*> fPages;
     SkTDArray<SkPDFDict*> fPageTree;
-    SkRefPtr<SkPDFDict> fDocCatalog;
+    SkPDFDict* fDocCatalog;
     SkTDArray<SkPDFObject*> fPageResources;
     SkTDArray<SkPDFObject*> fSubstitutes;
     int fSecondPageFirstResourceIndex;
 
-    SkRefPtr<SkPDFDict> fTrailerDict;
+    SkPDFDict* fTrailerDict;
 
     /** Output the PDF header to the passed stream.
      *  @param stream    The writable output stream to send the header to.
diff --git a/include/pdf/SkPDFFont.h b/include/pdf/SkPDFFont.h
deleted file mode 100644
index 2ebdec7..0000000
--- a/include/pdf/SkPDFFont.h
+++ /dev/null
@@ -1,202 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFFont_DEFINED
-#define SkPDFFont_DEFINED
-
-#include "SkAdvancedTypefaceMetrics.h"
-#include "SkBitSet.h"
-#include "SkPDFTypes.h"
-#include "SkTDArray.h"
-#include "SkThread.h"
-#include "SkTypeface.h"
-
-class SkPaint;
-class SkPDFCatalog;
-class SkPDFFont;
-
-class SkPDFGlyphSet : public SkNoncopyable {
-public:
-    SkPDFGlyphSet();
-
-    void set(const uint16_t* glyphIDs, int numGlyphs);
-    bool has(uint16_t glyphID) const;
-    void merge(const SkPDFGlyphSet& usage);
-    void exportTo(SkTDArray<uint32_t>* glyphIDs) const;
-
-private:
-    SkBitSet fBitSet;
-};
-
-class SkPDFGlyphSetMap : public SkNoncopyable {
-public:
-    struct FontGlyphSetPair {
-        FontGlyphSetPair(SkPDFFont* font, SkPDFGlyphSet* glyphSet);
-
-        SkPDFFont* fFont;
-        SkPDFGlyphSet* fGlyphSet;
-    };
-
-    SkPDFGlyphSetMap();
-    ~SkPDFGlyphSetMap();
-
-    class F2BIter {
-    public:
-        explicit F2BIter(const SkPDFGlyphSetMap& map);
-        FontGlyphSetPair* next() const;
-        void reset(const SkPDFGlyphSetMap& map);
-
-    private:
-        const SkTDArray<FontGlyphSetPair>* fMap;
-        mutable int fIndex;
-    };
-
-    void merge(const SkPDFGlyphSetMap& usage);
-    void reset();
-
-    void noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
-                        int numGlyphs);
-
-private:
-    SkPDFGlyphSet* getGlyphSetForFont(SkPDFFont* font);
-
-    SkTDArray<FontGlyphSetPair> fMap;
-};
-
-
-/** \class SkPDFFont
-    A PDF Object class representing a font.  The font may have resources
-    attached to it in order to embed the font.  SkPDFFonts are canonicalized
-    so that resource deduplication will only include one copy of a font.
-    This class uses the same pattern as SkPDFGraphicState, a static weak
-    reference to each instantiated class.
-*/
-class SkPDFFont : public SkPDFDict {
-public:
-    SK_API virtual ~SkPDFFont();
-
-    SK_API virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
-
-    /** Returns the typeface represented by this class. Returns NULL for the
-     *  default typeface.
-     */
-    SK_API SkTypeface* typeface();
-
-    /** Returns the font type represented in this font.  For Type0 fonts,
-     *  returns the type of the decendant font.
-     */
-    SK_API virtual SkAdvancedTypefaceMetrics::FontType getType();
-
-    /** Returns true if this font encoding supports glyph IDs above 255.
-     */
-    SK_API virtual bool multiByteGlyphs() const = 0;
-
-    /** Return true if this font has an encoding for the passed glyph id.
-     */
-    SK_API bool hasGlyph(uint16_t glyphID);
-
-    /** Convert (in place) the input glyph IDs into the font encoding.  If the
-     *  font has more glyphs than can be encoded (like a type 1 font with more
-     *  than 255 glyphs) this method only converts up to the first out of range
-     *  glyph ID.
-     *  @param glyphIDs       The input text as glyph IDs.
-     *  @param numGlyphs      The number of input glyphs.
-     *  @return               Returns the number of glyphs consumed.
-     */
-    SK_API size_t glyphsToPDFFontEncoding(uint16_t* glyphIDs, size_t numGlyphs);
-
-    /** Get the font resource for the passed typeface and glyphID. 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.
-     *  @param typeface  The typeface to find.
-     *  @param glyphID   Specify which section of a large font is of interest.
-     */
-    SK_API static SkPDFFont* GetFontResource(SkTypeface* typeface,
-                                             uint16_t glyphID);
-
-    /** Subset the font based on usage set. Returns a SkPDFFont instance with
-     *  subset.
-     *  @param usage  Glyph subset requested.
-     *  @return       NULL if font does not support subsetting, a new instance
-     *                of SkPDFFont otherwise.
-     */
-    SK_API virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage);
-
-protected:
-    // Common constructor to handle common members.
-    SkPDFFont(SkAdvancedTypefaceMetrics* fontInfo, SkTypeface* typeface,
-              uint16_t glyphID, bool descendantFont);
-
-    // Accessors for subclass.
-    SkAdvancedTypefaceMetrics* fontInfo();
-    void setFontInfo(SkAdvancedTypefaceMetrics* info);
-    uint16_t firstGlyphID() const;
-    uint16_t lastGlyphID() const;
-    void setLastGlyphID(uint16_t glyphID);
-
-    // Add object to resource list.
-    void addResource(SkPDFObject* object);
-
-    // Accessors for FontDescriptor associated with this object.
-    SkPDFDict* getFontDescriptor();
-    void setFontDescriptor(SkPDFDict* descriptor);
-
-    // Add common entries to FontDescriptor.
-    bool addCommonFontDescriptorEntries(int16_t defaultWidth);
-
-    /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
-     *  including the passed glyphID.
-     */
-    void adjustGlyphRangeForSingleByteEncoding(int16_t glyphID);
-
-    // Generate ToUnicode table according to glyph usage subset.
-    // If subset is NULL, all available glyph ids will be used.
-    void populateToUnicodeTable(const SkPDFGlyphSet* subset);
-
-    // Create instances of derived types based on fontInfo.
-    static SkPDFFont* Create(SkAdvancedTypefaceMetrics* fontInfo,
-                             SkTypeface* typeface, uint16_t glyphID,
-                             SkPDFDict* fontDescriptor);
-
-    static bool Find(uint32_t fontID, uint16_t glyphID, int* index);
-
-private:
-    class FontRec {
-    public:
-        SkPDFFont* fFont;
-        uint32_t fFontID;
-        uint16_t fGlyphID;
-
-        // A fGlyphID of 0 with no fFont always matches.
-        bool operator==(const FontRec& b) const;
-        FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID);
-    };
-
-    SkRefPtr<SkTypeface> fTypeface;
-
-    // The glyph IDs accessible with this font.  For Type1 (non CID) fonts,
-    // this will be a subset if the font has more than 255 glyphs.
-    uint16_t fFirstGlyphID;
-    uint16_t fLastGlyphID;
-    // The font info is only kept around after construction for large
-    // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs.
-    SkRefPtr<SkAdvancedTypefaceMetrics> fFontInfo;
-    SkTDArray<SkPDFObject*> fResources;
-    SkRefPtr<SkPDFDict> fDescriptor;
-
-    SkAdvancedTypefaceMetrics::FontType fFontType;
-
-    // This should be made a hash table if performance is a problem.
-    static SkTDArray<FontRec>& CanonicalFonts();
-    static SkBaseMutex& CanonicalFontsMutex();
-};
-
-#endif
diff --git a/include/pdf/SkPDFPage.h b/include/pdf/SkPDFPage.h
deleted file mode 100644
index 38a371a..0000000
--- a/include/pdf/SkPDFPage.h
+++ /dev/null
@@ -1,99 +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 {
-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 resourceObjects The resource objects used on the page are added
-     *                         to this array.  This gives the caller a chance
-     *                         to deduplicate resources across pages.
-     */
-    void finalizePage(SkPDFCatalog* catalog, bool firstPage,
-                      SkTDArray<SkPDFObject*>* resourceObjects);
-
-    /** Determine the size of the page content and store to the catalog
-     *  the offsets of all nonresource-indirect objects that make up the page
-     *  content.  This must be called before emitPage(), but after finalizePage.
-     *  @param catalog    The catalog to add the object offsets to.
-     *  @param fileOffset The file offset where the page content will be
-     *                    emitted.
-     */
-    off_t getPageSize(SkPDFCatalog* catalog, off_t fileOffset);
-
-    /** Output the page content to the passed stream.
-     *  @param stream     The writable output stream to send the content to.
-     *  @param catalog    The active object catalog.
-     */
-    void emitPage(SkWStream* stream, SkPDFCatalog* catalog);
-
-    /** 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.
-     */
-    SK_API const SkTDArray<SkPDFFont*>& getFontResources() const;
-
-    /** Returns a SkPDFGlyphSetMap which represents glyph usage of every font
-     *  that shows on this page.
-     */
-    const SkPDFGlyphSetMap& getFontGlyphUsage() const;
-
-private:
-    // Multiple pages may reference the content.
-    SkRefPtr<SkPDFDevice> fDevice;
-
-    // Once the content is finalized, put it into a stream for output.
-    SkRefPtr<SkPDFStream> fContentStream;
-};
-
-#endif
diff --git a/include/pdf/SkPDFShader.h b/include/pdf/SkPDFShader.h
deleted file mode 100644
index 439d83b..0000000
--- a/include/pdf/SkPDFShader.h
+++ /dev/null
@@ -1,65 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFShader_DEFINED
-#define SkPDFShader_DEFINED
-
-#include "SkPDFStream.h"
-#include "SkPDFTypes.h"
-#include "SkMatrix.h"
-#include "SkRefCnt.h"
-#include "SkShader.h"
-
-class SkObjRef;
-class SkPDFCatalog;
-
-/** \class SkPDFShader
-
-    In PDF parlance, this is a pattern, used in place of a color when the
-    pattern color space is selected.
-*/
-
-class SkPDFShader {
-public:
-    /** Get the PDF shader for the passed SkShader. If the SkShader is
-     *  invalid in some way, returns NULL. 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.
-     *  @param shader     The SkShader to emulate.
-     *  @param matrix     The current transform. (PDF shaders are absolutely
-     *                    positioned, relative to where the page is drawn.)
-     *  @param surfceBBox The bounding box of the drawing surface (with matrix
-     *                    already applied).
-     */
-    static SkPDFObject* GetPDFShader(const SkShader& shader,
-                                     const SkMatrix& matrix,
-                                     const SkIRect& surfaceBBox);
-
-protected:
-    class State;
-
-    class ShaderCanonicalEntry {
-    public:
-        ShaderCanonicalEntry(SkPDFObject* pdfShader, const State* state);
-        bool operator==(const ShaderCanonicalEntry& b) const;
-
-        SkPDFObject* fPDFShader;
-        const State* fState;
-    };
-    // This should be made a hash table if performance is a problem.
-    static SkTDArray<ShaderCanonicalEntry>& CanonicalShaders();
-    static SkBaseMutex& CanonicalShadersMutex();
-    static void RemoveShader(SkPDFObject* shader);
-
-    SkPDFShader();
-};
-
-#endif
diff --git a/include/pdf/SkPDFStream.h b/include/pdf/SkPDFStream.h
deleted file mode 100644
index b3a7ad3..0000000
--- a/include/pdf/SkPDFStream.h
+++ /dev/null
@@ -1,75 +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 SkPDFStream_DEFINED
-#define SkPDFStream_DEFINED
-
-#include "SkPDFTypes.h"
-#include "SkRefCnt.h"
-#include "SkStream.h"
-#include "SkTemplates.h"
-
-class SkPDFCatalog;
-
-/** \class SkPDFStream
-
-    A stream object in a PDF.  Note, all streams must be indirect objects (via
-    SkObjRef).
-*/
-class SkPDFStream : public SkPDFDict {
-public:
-    /** Create a PDF stream. A Length entry is automatically added to the
-     *  stream dictionary. The stream may be retained (stream->ref() may be
-     *  called) so its contents must not be changed after calling this.
-     *  @param data  The data part of the stream.
-     */
-    explicit SkPDFStream(SkData* data);
-    /** Deprecated constructor. */
-    explicit SkPDFStream(SkStream* stream);
-    /** Create a PDF stream with the same content and dictionary entries
-     *  as the passed one.
-     */
-    explicit SkPDFStream(const SkPDFStream& pdfStream);
-    virtual ~SkPDFStream();
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
-                            bool indirect);
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-protected:
-    /* Create a PDF stream with no data.  The setData method must be called to
-     * set the data.
-     */
-    SkPDFStream();
-
-    void setData(SkStream* stream);
-
-private:
-    enum State {
-        kUnused_State,         //!< The stream hasn't been requested yet.
-        kNoCompression_State,  //!< The stream's been requested in an
-                               //   uncompressed form.
-        kCompressed_State,     //!< The stream's already been compressed.
-    };
-    // Indicates what form (or if) the stream has been requested.
-    State fState;
-
-    // TODO(vandebo): Use SkData (after removing deprecated constructor).
-    SkRefPtr<SkStream> fData;
-    SkRefPtr<SkPDFStream> fSubstitute;
-
-    typedef SkPDFDict INHERITED;
-
-    // Populate the stream dictionary.  This method returns false if
-    // fSubstitute should be used.
-    bool populate(SkPDFCatalog* catalog);
-};
-
-#endif
diff --git a/include/pdf/SkPDFTypes.h b/include/pdf/SkPDFTypes.h
deleted file mode 100644
index 155b826..0000000
--- a/include/pdf/SkPDFTypes.h
+++ /dev/null
@@ -1,403 +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 SkPDFTypes_DEFINED
-#define SkPDFTypes_DEFINED
-
-#include "SkRefCnt.h"
-#include "SkScalar.h"
-#include "SkString.h"
-#include "SkTDArray.h"
-#include "SkTypes.h"
-
-class SkPDFCatalog;
-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:
-    /** Create a PDF object.
-     */
-    SkPDFObject();
-    virtual ~SkPDFObject();
-
-    /** Return the size (number of bytes) of this object in the final output
-     *  file. Compound objects or objects that are computationally intensive
-     *  to output should override this method.
-     *  @param catalog  The object catalog to use.
-     *  @param indirect If true, output an object identifier with the object.
-     */
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-    /** If this object explicitly depends on other objects, add them to the
-     *  end of the list.  This only applies to higher level object, where
-     *  the depenency is explicit and introduced by the class.  i.e. an
-     *  SkPDFImage added to an SkPDFDevice, but not an SkPDFObjRef added to
-     *  an SkPDFArray.
-     *  @param resourceList  The list to append dependant resources to.
-     */
-    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
-
-    /** Emit this object unless the catalog has a substitute object, in which
-     *  case emit that.
-     *  @see emitObject
-     */
-    void emit(SkWStream* stream, SkPDFCatalog* catalog, bool indirect);
-
-    /** Helper function to output an indirect object.
-     *  @param catalog The object catalog to use.
-     *  @param stream  The writable output stream to send the output to.
-     */
-    void emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog);
-
-    /** Helper function to find the size of an indirect object.
-     *  @param catalog The object catalog to use.
-     */
-    size_t getIndirectOutputSize(SkPDFCatalog* catalog);
-
-    /** Static helper function to add a resource to a list.  The list takes
-     *  a reference.
-     * @param resource  The resource to add.
-     * @param list      The list to add the resource to.
-     */
-    static void AddResourceHelper(SkPDFObject* resource,
-                                  SkTDArray<SkPDFObject*>* list);
-
-    /** Static helper function to copy and reference the resources (and all
-     *   their subresources) into a new list.
-     * @param resources The resource list.
-     * @param result    The list to add to.
-     */
-    static void GetResourcesHelper(SkTDArray<SkPDFObject*>* resources,
-                                   SkTDArray<SkPDFObject*>* result);
-
-protected:
-    /** Subclasses must implement this method to print the object to the
-     *  PDF file.
-     *  @param catalog  The object catalog to use.
-     *  @param indirect If true, output an object identifier with the object.
-     *  @param stream   The writable output stream to send the output to.
-     */
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
-                            bool indirect) = 0;
-};
-
-/** \class SkPDFObjRef
-
-    An indirect reference to a PDF object.
-*/
-class SkPDFObjRef : public SkPDFObject {
-public:
-    /** Create a reference to an existing SkPDFObject.
-     *  @param obj The object to reference.
-     */
-    explicit SkPDFObjRef(SkPDFObject* obj);
-    virtual ~SkPDFObjRef();
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
-                            bool indirect);
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-private:
-    SkRefPtr<SkPDFObject> fObj;
-};
-
-/** \class SkPDFInt
-
-    An integer object in a PDF.
-*/
-class SkPDFInt : public SkPDFObject {
-public:
-    /** 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,
-                            bool indirect);
-
-private:
-    int32_t fValue;
-};
-
-/** \class SkPDFBool
-
-    An boolean value in a PDF.
-*/
-class SkPDFBool : public SkPDFObject {
-public:
-    /** 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,
-                            bool indirect);
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-private:
-    bool fValue;
-};
-
-/** \class SkPDFScalar
-
-    A real number object in a PDF.
-*/
-class SkPDFScalar : public SkPDFObject {
-public:
-    /** 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,
-                            bool indirect);
-
-private:
-    SkScalar fValue;
-};
-
-/** \class SkPDFString
-
-    A string object in a PDF.
-*/
-class SkPDFString : public SkPDFObject {
-public:
-    /** 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,
-                            bool indirect);
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-    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);
-};
-
-/** \class SkPDFName
-
-    A name object in a PDF.
-*/
-class SkPDFName : public SkPDFObject {
-public:
-    /** 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,
-                            bool indirect);
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-private:
-    static const size_t kMaxLen = 127;
-
-    const SkString fValue;
-
-    static SkString FormatName(const SkString& input);
-};
-
-/** \class SkPDFArray
-
-    An array object in a PDF.
-*/
-class SkPDFArray : public SkPDFObject {
-public:
-    /** Create a PDF array. Maximum length is 8191.
-     */
-    SkPDFArray();
-    virtual ~SkPDFArray();
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
-                            bool indirect);
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-    /** The size of the array.
-     */
-    int size() { return fValue.count(); }
-
-    /** 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.
-     *  @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[]);
-
-private:
-    static const int kMaxLen = 8191;
-    SkTDArray<SkPDFObject*> fValue;
-};
-
-/** \class SkPDFDict
-
-    A dictionary object in a PDF.
-*/
-class SkPDFDict : public SkPDFObject {
-public:
-    /** Create a PDF dictionary. Maximum number of entries is 4095.
-     */
-    SkPDFDict();
-
-    /** Create a PDF dictionary with a Type entry.
-     *  @param type   The value of the Type entry.
-     */
-    explicit SkPDFDict(const char type[]);
-
-    virtual ~SkPDFDict();
-
-    // The SkPDFObject interface.
-    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
-                            bool indirect);
-    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
-
-    /** The size of the dictionary.
-     */
-    int size() { return fValue.count(); }
-
-    /** 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.
-     *  @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);
-
-    /** Add the int 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.
-     */
-    void insertInt(const char key[], int32_t 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 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());
-    }
-
-    /** Remove all entries from the dictionary.
-     */
-    void clear();
-
-private:
-    struct Rec {
-      SkPDFName* key;
-      SkPDFObject* value;
-    };
-
-public:
-    class Iter {
-    public:
-        explicit Iter(const SkPDFDict& dict);
-        SkPDFName* next(SkPDFObject** value);
-
-    private:
-        Rec* fIter;
-        Rec* fStop;
-    };
-
-private:
-    static const int kMaxLen = 4095;
-
-    SkTDArray<struct Rec> fValue;
-};
-
-#endif
diff --git a/include/pdf/SkPDFUtils.h b/include/pdf/SkPDFUtils.h
deleted file mode 100644
index 5b9d74e..0000000
--- a/include/pdf/SkPDFUtils.h
+++ /dev/null
@@ -1,54 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkPDFUtils_DEFINED
-#define SkPDFUtils_DEFINED
-
-#include "SkPath.h"
-
-class SkMatrix;
-class SkPath;
-class SkPDFArray;
-struct SkRect;
-
-#if 0
-#define PRINT_NOT_IMPL(str) fprintf(stderr, str)
-#else
-#define PRINT_NOT_IMPL(str)
-#endif
-
-#define NOT_IMPLEMENTED(condition, assert)                         \
-    do {                                                           \
-        if (condition) {                                           \
-            PRINT_NOT_IMPL("NOT_IMPLEMENTED: " #condition "\n");   \
-            SkDEBUGCODE(SkASSERT(!assert);)                        \
-        }                                                          \
-    } while (0)
-
-class SkPDFUtils {
-public:
-    static SkPDFArray* MatrixToArray(const SkMatrix& matrix);
-    static void AppendTransform(const SkMatrix& matrix, SkWStream* content);
-
-    static void MoveTo(SkScalar x, SkScalar y, SkWStream* content);
-    static void AppendLine(SkScalar x, SkScalar y, SkWStream* content);
-    static void AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
-                            SkScalar ctl2X, SkScalar ctl2Y,
-                            SkScalar dstX, SkScalar dstY, SkWStream* content);
-    static void AppendRectangle(const SkRect& rect, SkWStream* content);
-    static void EmitPath(const SkPath& path, SkWStream* content);
-    static void ClosePath(SkWStream* content);
-    static void PaintPath(SkPaint::Style style, SkPath::FillType fill,
-                          SkWStream* content);
-    static void StrokePath(SkWStream* content);
-    static void DrawFormXObject(int objectIndex, SkWStream* content);
-    static void ApplyGraphicState(int objectIndex, SkWStream* content);
-};
-
-#endif
diff --git a/include/pipe/SkGPipe.h b/include/pipe/SkGPipe.h
index 29b058e..27af16d 100644
--- a/include/pipe/SkGPipe.h
+++ b/include/pipe/SkGPipe.h
@@ -23,6 +23,7 @@
 
 class SkGPipeReader {
 public:
+    SkGPipeReader();
     SkGPipeReader(SkCanvas* target);
     ~SkGPipeReader();
 
@@ -33,10 +34,16 @@
         kReadAtom_Status//!< finished reading an atom
     };
 
+    enum PlaybackFlags {
+        kReadAtom_PlaybackFlag = 0x1, //!< playback a single command from the stream
+        kSilent_PlaybackFlag   = 0x2, //!< playback without drawing
+    };
+
+    void setCanvas(SkCanvas*);
     // data must be 4-byte aligned
     // length must be a multiple of 4
-    Status playback(const void* data, size_t length, size_t* bytesRead = NULL,
-                    bool readAtom = false);
+    Status playback(const void* data, size_t length, uint32_t playbackFlags = 0,
+                    size_t* bytesRead = NULL);
 private:
     SkCanvas*           fCanvas;
     class SkGPipeState* fState;
@@ -44,8 +51,13 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+class SkGPipeCanvas;
+
 class SkGPipeController {
 public:
+    SkGPipeController() : fCanvas(NULL) {}
+    virtual ~SkGPipeController();
+
     /**
      *  Called periodically by the writer, to get a working buffer of RAM to
      *  write into. The actual size of the block is also returned, and must be
@@ -66,6 +78,13 @@
      *  bytes will always be a multiple of 4.
      */
     virtual void notifyWritten(size_t bytes) = 0;
+    virtual int numberOfReaders() const { return 1; }
+
+private:
+    friend class SkGPipeWriter;
+    void setCanvas(SkGPipeCanvas*);
+
+    SkGPipeCanvas* fCanvas;
 };
 
 class SkGPipeWriter {
@@ -76,20 +95,67 @@
     bool isRecording() const { return NULL != fCanvas; }
 
     enum Flags {
-        kCrossProcess_Flag = 1 << 0,
+        /**
+         *  Tells the writer that the reader will be in a different process, so
+         *  (for example) we cannot put function pointers in the stream.
+         */
+        kCrossProcess_Flag              = 1 << 0,
+
+        /**
+         *  Only meaningful if kCrossProcess_Flag is set. Tells the writer that
+         *  in spite of being cross process, it will have shared address space
+         *  with the reader, so the two can share large objects (like SkBitmaps).
+         */
+        kSharedAddressSpace_Flag        = 1 << 1,
+
+        /**
+         *  Tells the writer that there will be multiple threads reading the stream
+         *  simultaneously.
+         */
+        kSimultaneousReaders_Flag       = 1 << 2,
     };
 
-    SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0);
+    SkCanvas* startRecording(SkGPipeController*, uint32_t flags = 0,
+        uint32_t width = kDefaultRecordingCanvasSize,
+        uint32_t height = kDefaultRecordingCanvasSize);
 
     // called in destructor, but can be called sooner once you know there
     // should be no more drawing calls made into the recording canvas.
     void endRecording();
 
+    /**
+     *  Tells the writer to commit all recorded draw commands to the
+     *  controller immediately.
+     *  @param detachCurrentBlock Set to true to request that the next draw
+     *      command be recorded in a new block.
+     */
+    void flushRecording(bool detachCurrentBlock);
+
+    /**
+     * Return the amount of bytes being used for recording. Note that this
+     * does not include the amount of storage written to the stream, which is
+     * controlled by the SkGPipeController.
+     * Currently only returns the amount used for SkBitmaps, since they are
+     * potentially unbounded (if the client is not calling playback).
+     */
+    size_t storageAllocatedForRecording() const;
+
+    /**
+     * Attempt to reduce the storage allocated for recording by evicting
+     * cache resources.
+     * @param bytesToFree minimum number of bytes that should be attempted to
+     *   be freed.
+     * @return number of bytes actually freed.
+     */
+    size_t freeMemoryIfPossible(size_t bytesToFree);
+
 private:
-    class SkGPipeCanvas* fCanvas;
-    SkGPipeController*   fController;
-    SkFactorySet         fFactorySet;
-    SkWriter32 fWriter;
+    enum {
+        kDefaultRecordingCanvasSize = 32767,
+    };
+
+    SkGPipeCanvas* fCanvas;
+    SkWriter32     fWriter;
 };
 
 #endif
diff --git a/include/ports/SkHarfBuzzFont.h b/include/ports/SkHarfBuzzFont.h
index 66c5534..22749af 100644
--- a/include/ports/SkHarfBuzzFont.h
+++ b/include/ports/SkHarfBuzzFont.h
@@ -29,7 +29,7 @@
         Color information (e.g. color, xfermode, shader, etc.) are not required.
      */
     virtual void setupPaint(SkPaint*) const = 0;
-    
+
     /** Implementation of HB_GetFontTableFunc, using SkHarfBuzzFont* as
         the first parameter.
      */
diff --git a/include/ports/SkStream_Win.h b/include/ports/SkStream_Win.h
deleted file mode 100644
index 8496360..0000000
--- a/include/ports/SkStream_Win.h
+++ /dev/null
@@ -1,46 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkStream_Win_DEFINED
-#define SkStream_Win_DEFINED
-
-#ifndef SK_BUILD_FOR_WIN
-#error "only valid for windows and wince builds"
-#endif
-
-#ifndef SkStream_DEFINED
-#include "SkStream.h"
-#endif
-#include "SkString.h"
-#include "Wininet.h"
-
-/** \cond ZERO */
-class SkURLStream : public SkStream {
-public:
-    SkURLStream(const char url[] = NULL);
-    virtual ~SkURLStream();
-
-    /** Close the current URL, and open a new URL.
-        If URL is null, just close the current URL.
-    */
-    void setURL(const char url[]);
-
-    // overrides
-    virtual bool rewind();
-    virtual size_t read(void* buffer, size_t size);
-    
-private:
-    SkString fURL;
-    HINTERNET fConnection;
-    HINTERNET fURLStream;
-};
-
-/** \endcond */
-#endif // SkStream_Win_DEFINED
-
diff --git a/include/ports/SkTypeface_mac.h b/include/ports/SkTypeface_mac.h
index eb9d25f..0166ee5 100644
--- a/include/ports/SkTypeface_mac.h
+++ b/include/ports/SkTypeface_mac.h
@@ -27,4 +27,3 @@
 SK_API extern SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef);
 
 #endif
-
diff --git a/include/ports/SkTypeface_win.h b/include/ports/SkTypeface_win.h
index 6da843f..fdf38e2 100644
--- a/include/ports/SkTypeface_win.h
+++ b/include/ports/SkTypeface_win.h
@@ -29,4 +29,3 @@
 SK_API void SkLOGFONTFromTypeface(const SkTypeface* typeface, LOGFONT* lf);
 
 #endif
-
diff --git a/include/svg/SkSVGAttribute.h b/include/svg/SkSVGAttribute.h
new file mode 100644
index 0000000..940ca5a
--- /dev/null
+++ b/include/svg/SkSVGAttribute.h
@@ -0,0 +1,42 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkSVGAttribute_DEFINED
+#define SkSVGAttribute_DEFINED
+
+#include "SkTypes.h"
+
+struct SkSVGAttribute {
+    const char* fName;
+#ifdef SK_DEBUG
+    size_t fOffset;
+#endif
+};
+
+#ifndef SK_OFFSETOF
+#define SK_OFFSETOF(a, b) (((size_t) (&(((a*) 1)->b)))-1)
+#endif
+
+#ifdef SK_DEBUG
+#define SVG_ATTRIBUTE(attr) { #attr, SK_OFFSETOF(BASE_CLASS, f_##attr) }
+#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr, SK_OFFSETOF(BASE_CLASS, cAttr) }
+#else
+#define SVG_ATTRIBUTE(attr) { #attr }
+#define SVG_LITERAL_ATTRIBUTE(svgAttr, cAttr) { #svgAttr }
+#endif
+
+#define SVG_ADD_ATTRIBUTE(attr) \
+    if (f_##attr.size() > 0) \
+        parser._addAttributeLen(#attr, f_##attr.c_str(), f_##attr.size())
+
+#define SVG_ADD_ATTRIBUTE_ALIAS(attr, alias) \
+    if (f_##alias.size() > 0) \
+        parser._addAttributeLen(#attr, f_##alias.c_str(), f_##alias.size())
+
+#endif // SkSVGAttribute_DEFINED
diff --git a/include/svg/SkSVGBase.h b/include/svg/SkSVGBase.h
new file mode 100644
index 0000000..6bfc39d
--- /dev/null
+++ b/include/svg/SkSVGBase.h
@@ -0,0 +1,25 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkSVGBase_DEFINED
+#define SkSVGBase_DEFINED
+
+#include "SkSVGAttribute.h"
+
+class SkSVGParser;
+
+class SkSVGBase {
+public:
+    virtual ~SkSVGBase();
+    virtual void addAttribute(SkSVGParser& parser, int attrIndex,
+        const char* attrValue, size_t attrLength);
+    virtual int getAttributes(const SkSVGAttribute** attrPtr) = 0;
+};
+
+#endif // SkSVGBase_DEFINEDes(const SkSVGAttribute** attrPtr) = 0;
diff --git a/include/svg/SkSVGPaintState.h b/include/svg/SkSVGPaintState.h
new file mode 100644
index 0000000..211e9cf
--- /dev/null
+++ b/include/svg/SkSVGPaintState.h
@@ -0,0 +1,89 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkSVGPaintState_DEFINED
+#define SkSVGPaintState_DEFINED
+
+#include "SkSVGBase.h"
+#include "SkString.h"
+
+class SkSVGPaint : public SkSVGBase {
+public:
+    enum Field {
+        kInitial = -1,
+        kClipPath,
+        kClipRule,
+        kEnableBackground,
+        kFill,
+        kFillRule,
+        kFilter,
+        kFontFamily,
+        kFontSize,
+        kLetterSpacing,
+        kMask,
+        kOpacity,
+        kStopColor,
+        kStopOpacity,
+        kStroke,
+        kStroke_Dasharray,
+        kStroke_Linecap,
+        kStroke_Linejoin,
+        kStroke_Miterlimit,
+        kStroke_Width,
+        kStyle,
+        kTransform,
+        kTerminal
+    };
+
+    SkSVGPaint();
+    virtual void addAttribute(SkSVGParser& parser, int attrIndex,
+        const char* attrValue, size_t attrLength);
+    bool flush(SkSVGParser& , bool isFlushable, bool isDef);
+    virtual int getAttributes(const SkSVGAttribute** attrPtr);
+    static void Push(SkSVGPaint** head, SkSVGPaint* add);
+    static void Pop(SkSVGPaint** head);
+    SkString* operator[](int index);
+    SkString fInitial;
+    SkString f_clipPath;
+    SkString f_clipRule;
+    SkString f_enableBackground;
+    SkString f_fill;
+    SkString f_fillRule;
+    SkString f_filter;
+    SkString f_fontFamily;
+    SkString f_fontSize;
+    SkString f_letterSpacing;
+    SkString f_mask;
+    SkString f_opacity;
+    SkString f_stopColor;
+    SkString f_stopOpacity;
+    SkString f_stroke;
+    SkString f_strokeDasharray;
+    SkString f_strokeLinecap;
+    SkString f_strokeLinejoin;
+    SkString f_strokeMiterlimit;
+    SkString f_strokeWidth;
+    SkString f_style; // unused, but allows array access to the rest
+    SkString f_transform;
+#ifdef SK_DEBUG
+    SkString fTerminal;
+#endif
+    SkString fTransformID;
+    static SkSVGAttribute gAttributes[];
+    static const int kAttributesSize;
+private:
+    void setSave(SkSVGParser& );
+    bool writeChangedAttributes(SkSVGParser& , SkSVGPaint& , bool* changed);
+    bool writeChangedElements(SkSVGParser& , SkSVGPaint& , bool* changed);
+    SkSVGPaint* fNext;
+    friend class SkSVGParser;
+    typedef SkSVGPaint BASE_CLASS;
+};
+
+#endif // SkSVGPaintState_DEFINED
diff --git a/include/svg/SkSVGParser.h b/include/svg/SkSVGParser.h
new file mode 100644
index 0000000..c2f9112
--- /dev/null
+++ b/include/svg/SkSVGParser.h
@@ -0,0 +1,74 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkSVGParser_DEFINED
+#define SkSVGParser_DEFINED
+
+#include "SkMatrix.h"
+#include "SkTDict.h"
+#include "SkTDStack.h"
+#include "SkSVGPaintState.h"
+#include "SkSVGTypes.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkXMLParser.h"
+#include "SkXMLWriter.h"
+
+class SkSVGBase;
+class SkSVGElement;
+
+class SkSVGParser : public SkXMLParser {
+public:
+    SkSVGParser(SkXMLParserError* err = NULL);
+    virtual ~SkSVGParser();
+    void _addAttribute(const char* attrName, const char* attrValue) {
+        fXMLWriter.addAttribute(attrName, attrValue); }
+    void _addAttribute(const char* attrName, SkString& attrValue) {
+        fXMLWriter.addAttribute(attrName, attrValue.c_str()); }
+    void _addAttributeLen(const char* attrName, const char* attrValue, size_t len) {
+        fXMLWriter.addAttributeLen(attrName, attrValue, len); }
+    void _endElement() { fXMLWriter.endElement(); }
+    int findAttribute(SkSVGBase* , const char* attrValue, size_t len, bool isPaint);
+//    const char* getFinal();
+    SkTDict<SkSVGElement*>& getIDs() { return fIDs; }
+    SkString& getPaintLast(SkSVGPaint::Field field);
+    void _startElement(const char name[]) { fXMLWriter.startElement(name); }
+    void translate(SkSVGElement*, bool isDef);
+    void translateMatrix(SkString& , SkString* id);
+    static void ConvertToArray(SkString& vals);
+protected:
+    virtual bool onAddAttribute(const char name[], const char value[]);
+    bool onAddAttributeLen(const char name[], const char value[], size_t len);
+    virtual bool onEndElement(const char elem[]);
+    virtual bool onStartElement(const char elem[]);
+    bool onStartElementLen(const char elem[], size_t len);
+    virtual bool onText(const char text[], int len);
+private:
+    bool isStrokeAndFill(SkSVGPaint** stroke, SkSVGPaint** fill);
+    static SkSVGElement* CreateElement(SkSVGTypes type, SkSVGElement* parent);
+    static void Delete(SkTDArray<SkSVGElement*>& fChildren);
+    static SkSVGTypes GetType(const char name[], size_t len);
+    SkSVGPaint* fHead;
+    SkSVGPaint fEmptyPaint;
+    SkSVGPaint fLastFlush;
+    SkString fLastColor;
+    SkMatrix fLastTransform;
+    SkTDArray<SkSVGElement*> fChildren;
+    SkTDict<SkSVGElement*> fIDs;
+    SkTDArray<SkSVGElement*> fParents;
+    SkDynamicMemoryWStream fStream;
+    SkXMLStreamWriter fXMLWriter;
+    SkSVGElement*   fCurrElement;
+    SkBool8 fInSVG;
+    SkBool8 fSuppressPaint;
+    friend class SkSVGPaint;
+    friend class SkSVGGradient;
+};
+
+#endif // SkSVGParser_DEFINED
diff --git a/include/svg/SkSVGTypes.h b/include/svg/SkSVGTypes.h
new file mode 100644
index 0000000..b13bc6e
--- /dev/null
+++ b/include/svg/SkSVGTypes.h
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkSVGTypes_DEFINED
+#define SkSVGTypes_DEFINED
+
+enum SkSVGTypes {
+    SkSVGType_Circle,
+    SkSVGType_ClipPath,
+    SkSVGType_Defs,
+    SkSVGType_Ellipse,
+    SkSVGType_FeColorMatrix,
+    SkSVGType_Filter,
+    SkSVGType_G,
+    SkSVGType_Image,
+    SkSVGType_Line,
+    SkSVGType_LinearGradient,
+    SkSVGType_Mask,
+    SkSVGType_Metadata,
+    SkSVGType_Path,
+    SkSVGType_Polygon,
+    SkSVGType_Polyline,
+    SkSVGType_RadialGradient,
+    SkSVGType_Rect,
+    SkSVGType_SVG,
+    SkSVGType_Stop,
+    SkSVGType_Symbol,
+    SkSVGType_Text,
+    SkSVGType_Tspan,
+    SkSVGType_Unknown,
+    SkSVGType_Use
+};
+
+#endif // SkSVGTypes_DEFINED
diff --git a/include/text/SkTextLayout.h b/include/text/SkTextLayout.h
index e1e3e56..718acdb 100644
--- a/include/text/SkTextLayout.h
+++ b/include/text/SkTextLayout.h
@@ -13,6 +13,8 @@
 
 class SkTextStyle : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkTextStyle)
+
     SkTextStyle();
     SkTextStyle(const SkTextStyle&);
     explicit SkTextStyle(const SkPaint&);
@@ -20,11 +22,13 @@
 
     const SkPaint& paint() const { return fPaint; }
     SkPaint& paint() { return fPaint; }
-    
+
     // todo: bidi-override, language
 
 private:
     SkPaint fPaint;
+
+    typedef SkRefCnt INHERITED;
 };
 
 class SkTextLayout {
@@ -54,4 +58,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkBoundaryPatch.h b/include/utils/SkBoundaryPatch.h
index 9d4b5ad..cf001d5 100644
--- a/include/utils/SkBoundaryPatch.h
+++ b/include/utils/SkBoundaryPatch.h
@@ -13,6 +13,8 @@
 
 class SkBoundary : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(SkBoundary)
+
     // These must be 0, 1, 2, 3 for efficiency in the subclass implementations
     enum Edge {
         kTop    = 0,
@@ -22,6 +24,9 @@
     };
     // Edge index goes clockwise around the boundary, beginning at the "top"
     virtual SkPoint eval(Edge, SkScalar unitInterval) = 0;
+
+private:
+    typedef SkRefCnt INHERITED;
 };
 
 class SkBoundaryPatch {
@@ -44,7 +49,7 @@
 class SkLineBoundary : public SkBoundary {
 public:
     SkPoint fPts[4];
-    
+
     // override
     virtual SkPoint eval(Edge, SkScalar);
 };
@@ -53,10 +58,9 @@
 public:
     // the caller sets the first 12 entries. The 13th is used by the impl.
     SkPoint fPts[13];
-    
+
     // override
     virtual SkPoint eval(Edge, SkScalar);
 };
 
 #endif
-
diff --git a/include/utils/SkCamera.h b/include/utils/SkCamera.h
index 1c4c1fb..eafacbc 100644
--- a/include/utils/SkCamera.h
+++ b/include/utils/SkCamera.h
@@ -55,7 +55,7 @@
 
 struct SkMatrix3D {
     SkScalar    fMat[3][4];
-    
+
     void reset();
 
     void setRow(int row, SkScalar a, SkScalar b, SkScalar c, SkScalar d = 0)
@@ -71,7 +71,7 @@
     void setRotateY(SkScalar deg);
     void setRotateZ(SkScalar deg);
     void setTranslate(SkScalar x, SkScalar y, SkScalar z);
-    
+
     void preRotateX(SkScalar deg);
     void preRotateY(SkScalar deg);
     void preRotateZ(SkScalar deg);
@@ -105,7 +105,7 @@
         return this->dotWith(v.fX, v.fY, v.fZ);
     }
 
-    // depreicated, but still here for animator (for now)
+    // deprecated, but still here for animator (for now)
     void rotate(SkScalar x, SkScalar y, SkScalar z) {}
     void rotateDegrees(SkScalar x, SkScalar y, SkScalar z) {}
 
@@ -113,7 +113,7 @@
 public: // make public for SkDraw3D for now
     SkVector3D  fU, fV;
     SkPoint3D   fOrigin;
-    
+
     friend class SkCamera3D;
 };
 
@@ -161,7 +161,7 @@
     void applyToCanvas(SkCanvas*) const;
 
     SkScalar dotWithNormal(SkScalar dx, SkScalar dy, SkScalar dz) const;
-    
+
 private:
     struct Rec {
         Rec*        fNext;
@@ -173,4 +173,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkCondVar.h b/include/utils/SkCondVar.h
new file mode 100644
index 0000000..15f16e6
--- /dev/null
+++ b/include/utils/SkCondVar.h
@@ -0,0 +1,68 @@
+/*
+ * 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 SkCondVar_DEFINED
+#define SkCondVar_DEFINED
+
+#ifdef SK_USE_POSIX_THREADS
+#include <pthread.h>
+#elif defined(SK_BUILD_FOR_WIN32)
+#include <Windows.h>
+#endif
+
+/**
+ * Condition variable for blocking access to shared data from other threads and
+ * controlling which threads are awake.
+ *
+ * Currently only supported on platforms with posix threads and Windows Vista and
+ * above.
+ */
+class SkCondVar {
+public:
+    SkCondVar();
+    ~SkCondVar();
+
+    /**
+     * Lock a mutex. Must be done before calling the other functions on this object.
+     */
+    void lock();
+
+    /**
+     * Unlock the mutex.
+     */
+    void unlock();
+
+    /**
+     * Pause the calling thread. Will be awoken when signal() or broadcast() is called.
+     * Must be called while lock() is held (but gives it up while waiting). Once awoken,
+     * the calling thread will hold the lock once again.
+     */
+    void wait();
+
+    /**
+     * Wake one thread waiting on this condition. Must be called while lock()
+     * is held.
+     */
+    void signal();
+
+    /**
+     * Wake all threads waiting on this condition. Must be called while lock()
+     * is held.
+     */
+    void broadcast();
+
+private:
+#ifdef SK_USE_POSIX_THREADS
+    pthread_mutex_t  fMutex;
+    pthread_cond_t   fCond;
+#elif defined(SK_BUILD_FOR_WIN32)
+    CRITICAL_SECTION   fCriticalSection;
+    CONDITION_VARIABLE fCondition;
+#endif
+};
+
+#endif
diff --git a/include/utils/SkCountdown.h b/include/utils/SkCountdown.h
new file mode 100644
index 0000000..6bcec7d
--- /dev/null
+++ b/include/utils/SkCountdown.h
@@ -0,0 +1,36 @@
+/*
+ * 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 SkCountdown_DEFINED
+#define SkCountdown_DEFINED
+
+#include "SkCondVar.h"
+#include "SkRunnable.h"
+#include "SkTypes.h"
+
+class SkCountdown : public SkRunnable {
+public:
+    explicit SkCountdown(int32_t count);
+
+    /**
+     * Resets the countdown to the count provided.
+     */
+    void reset(int32_t count);
+
+    virtual void run() SK_OVERRIDE;
+
+    /**
+     * Blocks until run() has been called count times.
+     */
+    void wait();
+
+private:
+    SkCondVar fReady;
+    int32_t   fCount;
+};
+
+#endif
diff --git a/include/utils/SkCubicInterval.h b/include/utils/SkCubicInterval.h
index bd1f9b9..64d63cf 100644
--- a/include/utils/SkCubicInterval.h
+++ b/include/utils/SkCubicInterval.h
@@ -20,4 +20,3 @@
 }
 
 #endif
-
diff --git a/include/utils/SkCullPoints.h b/include/utils/SkCullPoints.h
index 9e2c01a..fafa0fc 100644
--- a/include/utils/SkCullPoints.h
+++ b/include/utils/SkCullPoints.h
@@ -16,13 +16,13 @@
 public:
     SkCullPoints();
     SkCullPoints(const SkIRect& r);
-    
+
     void    reset(const SkIRect& r);
-    
+
     /** Start a contour at (x,y). Follow this with call(s) to lineTo(...)
     */
     void    moveTo(int x, int y);
-    
+
     enum LineToResult {
         kNo_Result,             //!< line segment was completely clipped out
         kLineTo_Result,         //!< path.lineTo(pts[1]);
@@ -37,7 +37,7 @@
     SkIPoint     fAsQuad[4];     // cache of fR as 4 points
     SkIPoint     fPrevPt;        // private state
     LineToResult fPrevResult;   // private state
-    
+
     bool sect_test(int x0, int y0, int x1, int y1) const;
 };
 
@@ -56,7 +56,7 @@
     SkCullPointsPath(const SkIRect& r, SkPath* dst);
 
     void reset(const SkIRect& r, SkPath* dst);
-    
+
     void    moveTo(int x, int y);
     void    lineTo(int x, int y);
 
@@ -65,4 +65,7 @@
     SkPath*         fPath;
 };
 
+bool SkHitTestPath(const SkPath&, SkRect& target, bool hires);
+bool SkHitTestPath(const SkPath&, SkScalar x, SkScalar y, bool hires);
+
 #endif
diff --git a/include/utils/SkDeferredCanvas.h b/include/utils/SkDeferredCanvas.h
index 87797ac..1d15c6f 100644
--- a/include/utils/SkDeferredCanvas.h
+++ b/include/utils/SkDeferredCanvas.h
@@ -1,5 +1,5 @@
 /*
- * Copyright 2011 Google Inc.
+ * Copyright 2012 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -9,21 +9,21 @@
 #define SkDeferredCanvas_DEFINED
 
 #include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPicture.h"
 #include "SkPixelRef.h"
 
+class DeferredDevice;
+
 /** \class SkDeferredCanvas
-    Subclass of SkCanvas that encapsulates an SkPicture for deferred drawing.
-    The main difference between this class and SkPictureRecord (the canvas
-    provided by SkPicture) is that this is a full drop-in replacement for
-    SkCanvas, while SkPictureRecord only supports draw operations.
+    Subclass of SkCanvas that encapsulates an SkPicture or SkGPipe for deferred
+    drawing. The main difference between this class and SkPictureRecord (the
+    canvas provided by SkPicture) is that this is a full drop-in replacement
+    for SkCanvas, while SkPictureRecord only supports draw operations.
     SkDeferredCanvas will transparently trigger the flushing of deferred
     draw operations when an attempt is made to access the pixel data.
 */
 class SK_API SkDeferredCanvas : public SkCanvas {
 public:
-    class DeviceContext;
+    class NotificationClient;
 
     SkDeferredCanvas();
 
@@ -33,19 +33,12 @@
     */
     explicit SkDeferredCanvas(SkDevice* device);
 
-    /** Construct a canvas with the specified device to draw into, and
-     *  a device context. Equivalent to calling default constructor, then
-     *  setDevice.
-     *  @param device Specifies a device for the canvas to draw into.
-     *  @param deviceContext interface for the device's the graphics context
-     */
-    explicit SkDeferredCanvas(SkDevice* device, DeviceContext* deviceContext);
-
     virtual ~SkDeferredCanvas();
 
     /**
      *  Specify a device to be used by this canvas. Calling setDevice will
-     *  release the previously set device, if any.
+     *  release the previously set device, if any. Takes a reference on the
+     *  device.
      *
      *  @param device The device that the canvas will raw into
      *  @return The device argument, for convenience.
@@ -53,18 +46,19 @@
     virtual SkDevice* setDevice(SkDevice* device);
 
     /**
-     *  Specify a deviceContext to be used by this canvas. Calling
-     *  setDeviceContext will release the previously set deviceContext, if any.
-     *  A deviceContext must be specified if the device uses a graphics context
-     *  that requires some form of state initialization prior to drawing
-     *  and/or explicit flushing to synchronize the execution of rendering
-     *  operations.
+     *  Specify a NotificationClient to be used by this canvas. Calling
+     *  setNotificationClient will release the previously set
+     *  NotificationClient, if any. SkDeferredCanvas does not take ownership
+     *  of the notification client.  Therefore user code is resposible
+     *  for its destruction.  The notification client must be unregistered
+     *  by calling setNotificationClient(NULL) if it is destroyed before
+     *  this canvas.
      *  Note: Must be called after the device is set with setDevice.
      *
-     *  @deviceContext interface for the device's the graphics context
-     *  @return The deviceContext argument, for convenience.
+     *  @param notificationClient interface for dispatching notifications
+     *  @return The notificationClient argument, for convenience.
      */
-    DeviceContext* setDeviceContext(DeviceContext* deviceContext);
+    NotificationClient* setNotificationClient(NotificationClient* notificationClient);
 
     /**
      *  Enable or disable deferred drawing. When deferral is disabled,
@@ -75,6 +69,63 @@
      */
     void setDeferredDrawing(bool deferred);
 
+    /**
+     *  Returns true if deferred drawing is currenlty enabled.
+     */
+    bool isDeferredDrawing() const;
+
+    /**
+     *  Returns true if the canvas contains a fresh frame.  A frame is
+     *  considered fresh when its content do not depend on the contents
+     *  of the previous frame. For example, if a canvas is cleared before
+     *  drawing each frame, the frames will all be considered fresh.
+     *  A frame is defined as the graphics image produced by as a result
+     *  of all the canvas draws operation executed between two successive
+     *  calls to isFreshFrame.  The result of isFreshFrame is computed
+     *  conservatively, so it may report false negatives.
+     */
+    bool isFreshFrame() const;
+
+    /**
+     *  Returns true if the canvas has recorded draw commands that have
+     *  not yet been played back.
+     */
+    bool hasPendingCommands() const;
+
+    /**
+     *  Specify the maximum number of bytes to be allocated for the purpose
+     *  of recording draw commands to this canvas.  The default limit, is
+     *  64MB.
+     *  @param maxStorage The maximum number of bytes to be allocated.
+     */
+    void setMaxRecordingStorage(size_t maxStorage);
+
+    /**
+     *  Returns the number of bytes currently allocated for the purpose of
+     *  recording draw commands.
+     */
+    size_t storageAllocatedForRecording() const;
+
+    /**
+     * Attempt to reduce the storage allocated for recording by evicting
+     * cache resources.
+     * @param bytesToFree minimum number of bytes that should be attempted to
+     *   be freed.
+     * @return number of bytes actually freed.
+     */
+    size_t freeMemoryIfPossible(size_t bytesToFree);
+
+    /**
+     * Specifies the maximum size (in bytes) allowed for a given image to be
+     * rendered using the deferred canvas.
+     */
+    void setBitmapSizeThreshold(size_t sizeThreshold);
+
+    /**
+     * Executes all pending commands without drawing
+     */
+    void silentFlush();
+
     // Overrides of the SkCanvas interface
     virtual int save(SaveFlags flags) SK_OVERRIDE;
     virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
@@ -89,6 +140,8 @@
     virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
     virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
                           bool doAntiAlias) SK_OVERRIDE;
+    virtual bool clipRRect(const SkRRect& rect, SkRegion::Op op,
+                           bool doAntiAlias) SK_OVERRIDE;
     virtual bool clipPath(const SkPath& path, SkRegion::Op op,
                           bool doAntiAlias) SK_OVERRIDE;
     virtual bool clipRegion(const SkRegion& deviceRgn,
@@ -97,14 +150,15 @@
     virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
                             const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawRect(const SkRect& rect, const SkPaint& paint)
-                          SK_OVERRIDE;
+    virtual void drawOval(const SkRect&, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRRect(const SkRRect&, const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPath(const SkPath& path, const SkPaint& paint)
                           SK_OVERRIDE;
     virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left,
                             SkScalar top, const SkPaint* paint)
                             SK_OVERRIDE;
-    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+    virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                                 const SkRect& dst, const SkPaint* paint)
                                 SK_OVERRIDE;
 
@@ -135,166 +189,60 @@
     virtual SkBounder* setBounder(SkBounder* bounder) SK_OVERRIDE;
     virtual SkDrawFilter* setDrawFilter(SkDrawFilter* filter) SK_OVERRIDE;
 
-private:
-    void flushIfNeeded(const SkBitmap& bitmap);
-
 public:
-    class DeviceContext : public SkRefCnt {
+    class NotificationClient {
     public:
+        /**
+         * All classes with virtual methods should have a virtual destructor
+         * to avoid compiler warnings.
+         */
+        virtual ~NotificationClient() {}
+
+        /**
+         *  Called before executing one or several draw commands, which means
+         *  once per flush when deferred rendering is enabled.
+         */
         virtual void prepareForDraw() {}
-    };
-
-public:
-    class DeferredDevice : public SkDevice {
-    public:
-        /**
-         *  Constructor
-         *  @param immediateDevice device to be drawn to when flushing
-         *      deferred operations
-         *  @param deviceContext callback interface for managing graphics
-         *      context state, can be NULL.
-         */
-        DeferredDevice(SkDevice* immediateDevice,
-            DeviceContext* deviceContext = NULL);
-        ~DeferredDevice();
 
         /**
-         *  Sets the device context to be use with the device.
-         *  @param deviceContext callback interface for managing graphics
-         *      context state, can be NULL.
+         *  Called after a recording a draw command if additional memory
+         *  had to be allocated for recording.
+         *  @param newAllocatedStorage same value as would be returned by
+         *      storageAllocatedForRecording(), for convenience.
          */
-        void setDeviceContext(DeviceContext* deviceContext);
+        virtual void storageAllocatedForRecordingChanged(
+            size_t newAllocatedStorage) {}
 
         /**
-         *  Returns the recording canvas.
+         *  Called after pending draw commands have been flushed
          */
-        SkCanvas* recordingCanvas() const {return fRecordingCanvas;}
+        virtual void flushedDrawCommands() {}
 
         /**
-         *  Returns the immediate (non deferred) canvas.
+         *  Called after pending draw commands have been skipped, meaning
+         *  that they were optimized-out because the canvas is cleared
+         *  or completely overwritten by the command currently being recorded.
          */
-        SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
+        virtual void skippedPendingDrawCommands() {}
 
-        /**
-         *  Returns the immediate (non deferred) device.
-         */
-        SkDevice* immediateDevice() const {return fImmediateDevice;}
-
-        /**
-         *  Returns true if an opaque draw operation covering the entire canvas
-         *  was performed since the last call to isFreshFrame().
-         */
-        bool isFreshFrame();
-
-        void flushPending();
-        void contentsCleared();
-        void flushIfNeeded(const SkBitmap& bitmap);
-
-        virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
-        virtual int width() const SK_OVERRIDE;
-        virtual int height() const SK_OVERRIDE;
-        virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE;
-
-        virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
-                                                   int width, int height,
-                                                   bool isOpaque,
-                                                   Usage usage) SK_OVERRIDE;
-
-        virtual void writePixels(const SkBitmap& bitmap, int x, int y,
-                                 SkCanvas::Config8888 config8888) SK_OVERRIDE;
-
-    protected:
-        virtual const SkBitmap& onAccessBitmap(SkBitmap*) SK_OVERRIDE;
-        virtual bool onReadPixels(const SkBitmap& bitmap,
-                                  int x, int y,
-                                  SkCanvas::Config8888 config8888) SK_OVERRIDE;
-
-        // The following methods are no-ops on a deferred device
-        virtual bool filterTextFlags(const SkPaint& paint, TextFlags*)
-            SK_OVERRIDE
-            {return false;}
-        virtual void setMatrixClip(const SkMatrix&, const SkRegion&,
-                                   const SkClipStack&) SK_OVERRIDE
-            {}
-        virtual void gainFocus(SkCanvas*, const SkMatrix&, const SkRegion&,
-                               const SkClipStack&) SK_OVERRIDE
-            {}
-
-        // None of the following drawing methods should ever get called on the
-        // deferred device
-        virtual void clear(SkColor color)
-            {SkASSERT(0);}
-        virtual void drawPaint(const SkDraw&, const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
-                                size_t count, const SkPoint[],
-                                const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawRect(const SkDraw&, const SkRect& r,
-                              const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawPath(const SkDraw&, const SkPath& path,
-                              const SkPaint& paint,
-                              const SkMatrix* prePathMatrix = NULL,
-                              bool pathIsMutable = false)
-            {SkASSERT(0);}
-        virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
-                                const SkIRect* srcRectOrNull,
-                                const SkMatrix& matrix, const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
-                                int x, int y, const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawText(const SkDraw&, const void* text, size_t len,
-                              SkScalar x, SkScalar y, const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawPosText(const SkDraw&, const void* text, size_t len,
-                                 const SkScalar pos[], SkScalar constY,
-                                 int scalarsPerPos, const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawTextOnPath(const SkDraw&, const void* text,
-                                    size_t len, const SkPath& path,
-                                    const SkMatrix* matrix,
-                                    const SkPaint& paint)
-            {SkASSERT(0);}
-        virtual void drawPosTextOnPath(const SkDraw& draw, const void* text,
-                                       size_t len, const SkPoint pos[],
-                                       const SkPaint& paint,
-                                       const SkPath& path,
-                                       const SkMatrix* matrix)
-            {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)
-            {SkASSERT(0);}
-        virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
-                                const SkPaint&)
-            {SkASSERT(0);}
     private:
-        virtual void flush();
-
-        SkPicture fPicture;
-        SkDevice* fImmediateDevice;
-        SkCanvas* fImmediateCanvas;
-        SkCanvas* fRecordingCanvas;
-        DeviceContext* fDeviceContext;
-        bool fFreshFrame;
+        typedef SkRefCnt INHERITED;
     };
 
-    DeferredDevice* getDeferredDevice() const;
-
 protected:
     virtual SkCanvas* canvasForDrawIter();
+    DeferredDevice* getDeferredDevice() const;
 
 private:
+    void recordedDrawCommand();
     SkCanvas* drawingCanvas() const;
+    SkCanvas* immediateCanvas() const;
     bool isFullFrame(const SkRect*, const SkPaint*) const;
     void validate() const;
     void init();
     bool            fDeferredDrawing;
 
+    friend class SkDeferredCanvasTester; // for unit testing
     typedef SkCanvas INHERITED;
 };
 
diff --git a/include/utils/SkDumpCanvas.h b/include/utils/SkDumpCanvas.h
index de2af04..8d9c67d 100644
--- a/include/utils/SkDumpCanvas.h
+++ b/include/utils/SkDumpCanvas.h
@@ -10,6 +10,8 @@
 
 #include "SkCanvas.h"
 
+#ifdef SK_DEVELOPER
+
 /** This class overrides all the draw methods on SkCanvas, and formats them
     as text, and then sends that to a Dumper helper object.
 
@@ -35,7 +37,9 @@
 
         kDrawPaint_Verb,
         kDrawPoints_Verb,
+        kDrawOval_Verb,
         kDrawRect_Verb,
+        kDrawRRect_Verb,
         kDrawPath_Verb,
         kDrawBitmap_Verb,
         kDrawText_Verb,
@@ -49,8 +53,13 @@
      */
     class Dumper : public SkRefCnt {
     public:
+        SK_DECLARE_INST_COUNT(Dumper)
+
         virtual void dump(SkDumpCanvas*, SkDumpCanvas::Verb, const char str[],
                           const SkPaint*) = 0;
+
+    private:
+        typedef SkRefCnt INHERITED;
     };
 
     Dumper* getDumper() const { return fDumper; }
@@ -71,6 +80,7 @@
     virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
 
     virtual bool clipRect(const SkRect&, SkRegion::Op, bool) SK_OVERRIDE;
+    virtual bool clipRRect(const SkRRect&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipPath(const SkPath&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipRegion(const SkRegion& deviceRgn,
                             SkRegion::Op) SK_OVERRIDE;
@@ -78,11 +88,13 @@
     virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
                             const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawOval(const SkRect&, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRect(const SkRect&, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRRect(const SkRRect&, const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE;
     virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
                             const SkPaint* paint) SK_OVERRIDE;
-    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+    virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                                 const SkRect& dst, const SkPaint* paint) SK_OVERRIDE;
     virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
                                   const SkPaint* paint) SK_OVERRIDE;
@@ -145,3 +157,5 @@
 };
 
 #endif
+
+#endif
diff --git a/include/utils/SkInterpolator.h b/include/utils/SkInterpolator.h
index 289e886..74789c8 100644
--- a/include/utils/SkInterpolator.h
+++ b/include/utils/SkInterpolator.h
@@ -129,4 +129,3 @@
                            SkScalar cx, SkScalar cy);
 
 #endif
-
diff --git a/include/utils/SkJSON.h b/include/utils/SkJSON.h
index 5268af5..c601fa8 100644
--- a/include/utils/SkJSON.h
+++ b/include/utils/SkJSON.h
@@ -23,9 +23,9 @@
         kFloat,
         kBool,
     };
-    
+
     class Array;
-    
+
     class Object {
     private:
         struct Slot;
@@ -42,7 +42,7 @@
          *  not be null.
          */
         void addObject(const char name[], Object* value);
-        
+
         /**
          *  Create a new slot with the specified name and value. The name
          *  parameter is copied, but ownership of the Array parameter is
@@ -50,26 +50,26 @@
          *  not be null.
          */
         void addArray(const char name[], Array* value);
-        
+
         /**
          *  Create a new slot with the specified name and value. Both parameters
          *  are copied. The value parameter may be null, but the name must
          *  not be null.
          */
         void addString(const char name[], const char value[]);
-        
+
         /**
          *  Create a new slot with the specified name and value. The name
          *  parameter is copied, and must not be null.
          */
         void addInt(const char name[], int32_t value);
-        
+
         /**
          *  Create a new slot with the specified name and value. The name
          *  parameter is copied, and must not be null.
          */
         void addFloat(const char name[], float value);
-        
+
         /**
          *  Create a new slot with the specified name and value. The name
          *  parameter is copied, and must not be null.
@@ -108,7 +108,7 @@
         class Iter {
         public:
             Iter(const Object&);
-            
+
             /**
              *  Returns true when there are no more entries in the iterator.
              *  In this case, no other methods should be called.
@@ -126,43 +126,43 @@
              *  if done() returns false.
              */
             Type type() const;
-            
+
             /**
              *  Returns the name of the current element. Should only be called
              *  if done() returns false.
              */
             const char* name() const;
-            
+
             /**
              *  Returns the type of the current element. Should only be called
              *  if done() returns false and type() returns kObject.
              */
             Object* objectValue() const;
-            
+
             /**
              *  Returns the type of the current element. Should only be called
              *  if done() returns false and type() returns kArray.
              */
             Array* arrayValue() const;
-            
+
             /**
              *  Returns the type of the current element. Should only be called
              *  if done() returns false and type() returns kString.
              */
             const char* stringValue() const;
-            
+
             /**
              *  Returns the type of the current element. Should only be called
              *  if done() returns false and type() returns kInt.
              */
             int32_t intValue() const;
-            
+
             /**
              *  Returns the type of the current element. Should only be called
              *  if done() returns false and type() returns kFloat.
              */
             float floatValue() const;
-            
+
             /**
              *  Returns the type of the current element. Should only be called
              *  if done() returns false and type() returns kBool.
@@ -176,14 +176,14 @@
     private:
         Slot* fHead;
         Slot* fTail;
-        
+
         const Slot* findSlot(const char name[], Type) const;
         Slot* addSlot(Slot*);
         void dumpLevel(int level) const;
-        
+
         friend class Array;
     };
-    
+
     class Array {
     public:
         /**
@@ -197,22 +197,22 @@
          *  values.
          */
         Array(const int32_t values[], int count);
-        
+
         /**
          *  Creates an array of floats, initialized by copying the specified
          *  values.
          */
         Array(const float values[], int count);
-        
+
         /**
          *  Creates an array of bools, initialized by copying the specified
          *  values.
          */
         Array(const bool values[], int count);
-        
+
         Array(const Array&);
         ~Array();
-        
+
         int count() const { return fCount; }
         Type type() const { return fType; }
 
@@ -222,7 +222,7 @@
          *  Should only be called if the Array's type is kObject.
          */
         void setObject(int index, Object*);
-        
+
         /**
          *  Replace the element at the specified index with the specified
          *  Array (which may be null). Ownership of the Array is transferred.
@@ -274,10 +274,10 @@
             float*   fFloats;
             bool*    fBools;
         } fArray;
-        
+
         void init(Type, int count, const void* src);
         void dumpLevel(int level) const;
-        
+
         friend class Object;
     };
 };
diff --git a/include/utils/SkLayer.h b/include/utils/SkLayer.h
index 9cba463..70fa01b 100644
--- a/include/utils/SkLayer.h
+++ b/include/utils/SkLayer.h
@@ -23,6 +23,8 @@
 class SkLayer : public SkRefCnt {
 
 public:
+    SK_DECLARE_INST_COUNT(SkLayer)
+
     SkLayer();
     SkLayer(const SkLayer&);
     virtual ~SkLayer();
diff --git a/include/utils/SkMatrix44.h b/include/utils/SkMatrix44.h
index 93140b0..41f1a30 100644
--- a/include/utils/SkMatrix44.h
+++ b/include/utils/SkMatrix44.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,8 +5,6 @@
  * found in the LICENSE file.
  */
 
-
-
 #ifndef SkMatrix44_DEFINED
 #define SkMatrix44_DEFINED
 
@@ -15,7 +12,11 @@
 #include "SkScalar.h"
 
 #ifdef SK_MSCALAR_IS_DOUBLE
+#ifdef SK_MSCALAR_IS_FLOAT
+    #error "can't define MSCALAR both as DOUBLE and FLOAT"
+#endif
     typedef double SkMScalar;
+
     static inline double SkFloatToMScalar(float x) {
         return static_cast<double>(x);
     }
@@ -29,8 +30,12 @@
         return x;
     }
     static const SkMScalar SK_MScalarPI = 3.141592653589793;
-#else
+#elif defined SK_MSCALAR_IS_FLOAT
+#ifdef SK_MSCALAR_IS_DOUBLE
+    #error "can't define MSCALAR both as DOUBLE and FLOAT"
+#endif
     typedef float SkMScalar;
+
     static inline float SkFloatToMScalar(float x) {
         return x;
     }
@@ -46,19 +51,8 @@
     static const SkMScalar SK_MScalarPI = 3.14159265f;
 #endif
 
-#ifdef SK_SCALAR_IS_FLOAT
-    #define SkMScalarToScalar SkMScalarToFloat
-    #define SkScalarToMScalar SkFloatToMScalar
-#else
-    #if SK_MSCALAR_IS_DOUBLE
-        // we don't have fixed <-> double macros, use double<->scalar macros
-        #define SkMScalarToScalar SkDoubleToScalar
-        #define SkScalarToMScalar SkScalarToDouble
-    #else
-        #define SkMScalarToScalar SkFloatToFixed
-        #define SkScalarToMScalar SkFixedToFloat
-    #endif
-#endif
+#define SkMScalarToScalar SkMScalarToFloat
+#define SkScalarToMScalar SkFloatToMScalar
 
 static const SkMScalar SK_MScalar1 = 1;
 
@@ -107,37 +101,155 @@
 
 class SK_API SkMatrix44 {
 public:
-    SkMatrix44();
-    SkMatrix44(const SkMatrix44&);
-    SkMatrix44(const SkMatrix44& a, const SkMatrix44& b);
+
+    enum Uninitialized_Constructor {
+        kUninitialized_Constructor
+    };
+    enum Identity_Constructor {
+        kIdentity_Constructor
+    };
+
+    SkMatrix44(Uninitialized_Constructor) { }
+    SkMatrix44(Identity_Constructor) { this->setIdentity(); }
+
+    // DEPRECATED: use the constructors that take an enum
+    SkMatrix44() { this->setIdentity(); }
+
+    SkMatrix44(const SkMatrix44& src) {
+        memcpy(fMat, src.fMat, sizeof(fMat));
+        fTypeMask = src.fTypeMask;
+    }
+
+    SkMatrix44(const SkMatrix44& a, const SkMatrix44& b) {
+        this->setConcat(a, b);
+    }
 
     SkMatrix44& operator=(const SkMatrix44& src) {
-        memcpy(this, &src, sizeof(*this));
+        memcpy(fMat, src.fMat, sizeof(fMat));
+        fTypeMask = src.fTypeMask;
         return *this;
     }
 
-    bool operator==(const SkMatrix44& other) const {
-        return !memcmp(this, &other, sizeof(*this));
-    }
+    bool operator==(const SkMatrix44& other) const;
     bool operator!=(const SkMatrix44& other) const {
-        return !!memcmp(this, &other, sizeof(*this));
+        return !(other == *this);
     }
 
     SkMatrix44(const SkMatrix&);
     SkMatrix44& operator=(const SkMatrix& src);
     operator SkMatrix() const;
 
-    SkMScalar get(int row, int col) const;
-    void set(int row, int col, const SkMScalar& value);
+    /**
+     *  Return a reference to a const identity matrix
+     */
+    static const SkMatrix44& I();
 
+    enum TypeMask {
+        kIdentity_Mask      = 0,
+        kTranslate_Mask     = 0x01,  //!< set if the matrix has translation
+        kScale_Mask         = 0x02,  //!< set if the matrix has any scale != 1
+        kAffine_Mask        = 0x04,  //!< set if the matrix skews or rotates
+        kPerspective_Mask   = 0x08   //!< set if the matrix is in perspective
+    };
+
+    /**
+     *  Returns a bitfield describing the transformations the matrix may
+     *  perform. The bitfield is computed conservatively, so it may include
+     *  false positives. For example, when kPerspective_Mask is true, all
+     *  other bits may be set to true even in the case of a pure perspective
+     *  transform.
+     */
+    inline TypeMask getType() const {
+        if (fTypeMask & kUnknown_Mask) {
+            fTypeMask = this->computeTypeMask();
+        }
+        SkASSERT(!(fTypeMask & kUnknown_Mask));
+        return (TypeMask)fTypeMask;
+    }
+
+    /**
+     *  Return true if the matrix is identity.
+     */
+    inline bool isIdentity() const {
+        return kIdentity_Mask == this->getType();
+    }
+
+    /**
+     *  Return true if the matrix contains translate or is identity.
+     */
+    inline bool isTranslate() const {
+        return !(this->getType() & ~kTranslate_Mask);
+    }
+
+    /**
+     *  Return true if the matrix only contains scale or translate or is identity.
+     */
+    inline bool isScaleTranslate() const {
+        return !(this->getType() & ~(kScale_Mask | kTranslate_Mask));
+    }
+
+    void setIdentity();
+    inline void reset() { this->setIdentity();}
+
+    /**
+     *  get a value from the matrix. The row,col parameters work as follows:
+     *  (0, 0)  scale-x
+     *  (0, 3)  translate-x
+     *  (3, 0)  perspective-x
+     */
+    inline SkMScalar get(int row, int col) const {
+        SkASSERT((unsigned)row <= 3);
+        SkASSERT((unsigned)col <= 3);
+        return fMat[col][row];
+    }
+
+    /**
+     *  set a value in the matrix. The row,col parameters work as follows:
+     *  (0, 0)  scale-x
+     *  (0, 3)  translate-x
+     *  (3, 0)  perspective-x
+     */
+    inline void set(int row, int col, SkMScalar value) {
+        SkASSERT((unsigned)row <= 3);
+        SkASSERT((unsigned)col <= 3);
+        fMat[col][row] = value;
+        this->dirtyTypeMask();
+    }
+
+    inline double getDouble(int row, int col) const {
+        return SkMScalarToDouble(this->get(row, col));
+    }
+    inline void setDouble(int row, int col, double value) {
+        this->set(row, col, SkDoubleToMScalar(value));
+    }
+
+    /** These methods allow one to efficiently read matrix entries into an
+     *  array. The given array must have room for exactly 16 entries. Whenever
+     *  possible, they will try to use memcpy rather than an entry-by-entry
+     *  copy.
+     */
     void asColMajorf(float[]) const;
     void asColMajord(double[]) const;
     void asRowMajorf(float[]) const;
     void asRowMajord(double[]) const;
 
-    bool isIdentity() const;
-    void setIdentity();
-    void reset() { this->setIdentity();}
+    /** These methods allow one to efficiently set all matrix entries from an
+     *  array. The given array must have room for exactly 16 entries. Whenever
+     *  possible, they will try to use memcpy rather than an entry-by-entry
+     *  copy.
+     */
+    void setColMajorf(const float[]);
+    void setColMajord(const double[]);
+    void setRowMajorf(const float[]);
+    void setRowMajord(const double[]);
+
+#ifdef SK_MSCALAR_IS_FLOAT
+    void setColMajor(const SkMScalar data[]) { this->setColMajorf(data); }
+    void setRowMajor(const SkMScalar data[]) { this->setRowMajorf(data); }
+#else
+    void setColMajor(const SkMScalar data[]) { this->setColMajord(data); }
+    void setRowMajor(const SkMScalar data[]) { this->setRowMajord(data); }
+#endif
 
     void set3x3(SkMScalar m00, SkMScalar m01, SkMScalar m02,
                 SkMScalar m10, SkMScalar m11, SkMScalar m12,
@@ -151,13 +263,13 @@
     void preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
     void postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz);
 
-    void setScale(SkMScalar scale) {
+    inline void setScale(SkMScalar scale) {
         this->setScale(scale, scale, scale);
     }
-    void preScale(SkMScalar scale) {
+    inline void preScale(SkMScalar scale) {
         this->preScale(scale, scale, scale);
     }
-    void postScale(SkMScalar scale) {
+    inline void postScale(SkMScalar scale) {
         this->postScale(scale, scale, scale);
     }
 
@@ -178,10 +290,10 @@
                             SkMScalar radians);
 
     void setConcat(const SkMatrix44& a, const SkMatrix44& b);
-    void preConcat(const SkMatrix44& m) {
+    inline void preConcat(const SkMatrix44& m) {
         this->setConcat(*this, m);
     }
-    void postConcat(const SkMatrix44& m) {
+    inline void postConcat(const SkMatrix44& m) {
         this->setConcat(m, *this);
     }
 
@@ -194,12 +306,35 @@
      */
     bool invert(SkMatrix44* inverse) const;
 
+    /** Transpose this matrix in place. */
+    void transpose();
+
     /** Apply the matrix to the src vector, returning the new vector in dst.
         It is legal for src and dst to point to the same memory.
      */
-    void map(const SkScalar src[4], SkScalar dst[4]) const;
+    void mapScalars(const SkScalar src[4], SkScalar dst[4]) const;
+    inline void mapScalars(SkScalar vec[4]) const {
+        this->mapScalars(vec, vec);
+    }
+
+    // DEPRECATED: call mapScalars()
+    void map(const SkScalar src[4], SkScalar dst[4]) const {
+        this->mapScalars(src, dst);
+    }
+    // DEPRECATED: call mapScalars()
     void map(SkScalar vec[4]) const {
-        this->map(vec, vec);
+        this->mapScalars(vec, vec);
+    }
+
+#ifdef SK_MSCALAR_IS_DOUBLE
+    void mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const;
+#elif defined SK_MSCALAR_IS_FLOAT
+    inline void mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const {
+        this->mapScalars(src, dst);
+    }
+#endif
+    inline void mapMScalars(SkMScalar vec[4]) const {
+        this->mapMScalars(vec, vec);
     }
 
     friend SkVector4 operator*(const SkMatrix44& m, const SkVector4& src) {
@@ -208,17 +343,61 @@
         return dst;
     }
 
+    /**
+     *  map an array of [x, y, 0, 1] through the matrix, returning an array
+     *  of [x', y', z', w'].
+     *
+     *  @param src2     array of [x, y] pairs, with implied z=0 and w=1
+     *  @param count    number of [x, y] pairs in src2
+     *  @param dst4     array of [x', y', z', w'] quads as the output.
+     */
+    void map2(const float src2[], int count, float dst4[]) const;
+    void map2(const double src2[], int count, double dst4[]) const;
+
     void dump() const;
 
-private:
-    /*  Stored in the same order as opengl:
-         [3][0] = tx
-         [3][1] = ty
-         [3][2] = tz
-     */
-    SkMScalar fMat[4][4];
-
     double determinant() const;
+
+private:
+    SkMScalar           fMat[4][4];
+    mutable unsigned    fTypeMask;
+
+    enum {
+        kUnknown_Mask = 0x80,
+
+        kAllPublic_Masks = 0xF
+    };
+
+    SkMScalar transX() const { return fMat[3][0]; }
+    SkMScalar transY() const { return fMat[3][1]; }
+    SkMScalar transZ() const { return fMat[3][2]; }
+
+    SkMScalar scaleX() const { return fMat[0][0]; }
+    SkMScalar scaleY() const { return fMat[1][1]; }
+    SkMScalar scaleZ() const { return fMat[2][2]; }
+
+    SkMScalar perspX() const { return fMat[0][3]; }
+    SkMScalar perspY() const { return fMat[1][3]; }
+    SkMScalar perspZ() const { return fMat[2][3]; }
+
+    int computeTypeMask() const;
+
+    inline void dirtyTypeMask() {
+        fTypeMask = kUnknown_Mask;
+    }
+
+    inline void setTypeMask(int mask) {
+        SkASSERT(0 == (~(kAllPublic_Masks | kUnknown_Mask) & mask));
+        fTypeMask = mask;
+    }
+
+    /**
+     *  Does not take the time to 'compute' the typemask. Only returns true if
+     *  we already know that this matrix is identity.
+     */
+    inline bool isTriviallyIdentity() const {
+        return 0 == fTypeMask;
+    }
 };
 
 #endif
diff --git a/include/utils/SkMeshUtils.h b/include/utils/SkMeshUtils.h
index c7cdeca..564df67 100644
--- a/include/utils/SkMeshUtils.h
+++ b/include/utils/SkMeshUtils.h
@@ -19,7 +19,7 @@
 public:
     SkMeshIndices();
     ~SkMeshIndices();
-    
+
     bool init(int texW, int texH, int rows, int cols) {
         return this->init(NULL, NULL, texW, texH, rows, cols);
     }
diff --git a/include/utils/SkNWayCanvas.h b/include/utils/SkNWayCanvas.h
index dbf4a58..d833455 100644
--- a/include/utils/SkNWayCanvas.h
+++ b/include/utils/SkNWayCanvas.h
@@ -34,6 +34,7 @@
     virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
     virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
     virtual bool clipRect(const SkRect&, SkRegion::Op, bool) SK_OVERRIDE;
+    virtual bool clipRRect(const SkRRect&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipPath(const SkPath&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipRegion(const SkRegion& deviceRgn,
                             SkRegion::Op) SK_OVERRIDE;
@@ -41,11 +42,13 @@
     virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
                             const SkPaint&) SK_OVERRIDE;
-    virtual void drawRect(const SkRect& rect, const SkPaint&) SK_OVERRIDE;
+    virtual void drawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
     virtual void drawPath(const SkPath& path, const SkPaint&) SK_OVERRIDE;
     virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
                             const SkPaint*) SK_OVERRIDE;
-    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+    virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                                 const SkRect& dst, const SkPaint*) SK_OVERRIDE;
     virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
                                   const SkPaint*) SK_OVERRIDE;
@@ -81,4 +84,3 @@
 
 
 #endif
-
diff --git a/include/utils/SkNinePatch.h b/include/utils/SkNinePatch.h
index b0ea46b..4d8788b 100644
--- a/include/utils/SkNinePatch.h
+++ b/include/utils/SkNinePatch.h
@@ -22,7 +22,7 @@
     static void DrawNine(SkCanvas* canvas, const SkRect& dst,
                      const SkBitmap& bitmap, const SkIRect& margins,
                      const SkPaint* paint = NULL);
-    
+
     static void DrawMesh(SkCanvas* canvas, const SkRect& dst,
                          const SkBitmap& bitmap,
                          const int32_t xDivs[], int numXDivs,
diff --git a/include/utils/SkNullCanvas.h b/include/utils/SkNullCanvas.h
new file mode 100644
index 0000000..99a26da
--- /dev/null
+++ b/include/utils/SkNullCanvas.h
@@ -0,0 +1,20 @@
+/*
+ * 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 SkNullCanvas_DEFINED
+#define SkNullCanvas_DEFINED
+
+#include "SkBitmap.h"
+
+class SkCanvas;
+
+/**
+ * Creates a canvas that draws nothing. This is useful for performance testing.
+ */
+SK_API SkCanvas* SkCreateNullCanvas();
+
+#endif
diff --git a/include/utils/SkParse.h b/include/utils/SkParse.h
index 7491cd6..c5aac7d 100644
--- a/include/utils/SkParse.h
+++ b/include/utils/SkParse.h
@@ -34,4 +34,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkParsePaint.h b/include/utils/SkParsePaint.h
index b35fcf1..066292f 100644
--- a/include/utils/SkParsePaint.h
+++ b/include/utils/SkParsePaint.h
@@ -24,4 +24,3 @@
 void SkPaint_Inflate(SkPaint*, const SkDOM&, const SkDOM::Node*);
 
 #endif
-
diff --git a/include/utils/SkParsePath.h b/include/utils/SkParsePath.h
index b48c2c5..c52b3c0 100644
--- a/include/utils/SkParsePath.h
+++ b/include/utils/SkParsePath.h
@@ -21,4 +21,3 @@
 };
 
 #endif
-
diff --git a/include/utils/SkPictureUtils.h b/include/utils/SkPictureUtils.h
new file mode 100644
index 0000000..5e6b051
--- /dev/null
+++ b/include/utils/SkPictureUtils.h
@@ -0,0 +1,31 @@
+/*
+ * 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 SkPictureUtils_DEFINED
+#define SkPictureUtils_DEFINED
+
+#include "SkPicture.h"
+
+class SkData;
+struct SkRect;
+
+class SK_API SkPictureUtils {
+public:
+    /**
+     *  Given a rectangular visible "window" into the picture, return an array
+     *  of SkPixelRefs that might intersect that area. To keep the call fast,
+     *  the returned list is not guaranteed to be exact, so it may miss some,
+     *  and it may return false positives.
+     *
+     *  The pixelrefs returned in the SkData are already owned by the picture,
+     *  so the returned pointers are only valid while the picture is in scope
+     *  and remains unchanged.
+     */
+    static SkData* GatherPixelRefs(SkPicture* pict, const SkRect& area);
+};
+
+#endif
diff --git a/include/utils/SkProxyCanvas.h b/include/utils/SkProxyCanvas.h
index 720436b..bd260c7 100644
--- a/include/utils/SkProxyCanvas.h
+++ b/include/utils/SkProxyCanvas.h
@@ -39,6 +39,7 @@
     virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
 
     virtual bool clipRect(const SkRect&, SkRegion::Op, bool) SK_OVERRIDE;
+    virtual bool clipRRect(const SkRRect&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipPath(const SkPath&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipRegion(const SkRegion& deviceRgn,
                             SkRegion::Op op = SkRegion::kIntersect_Op) SK_OVERRIDE;
@@ -46,11 +47,13 @@
     virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
                             const SkPaint& paint) SK_OVERRIDE;
-    virtual void drawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawOval(const SkRect&, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRect(const SkRect&, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRRect(const SkRRect&, const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE;
     virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
                             const SkPaint* paint = NULL) SK_OVERRIDE;
-    virtual void drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+    virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                                 const SkRect& dst, const SkPaint* paint = NULL) SK_OVERRIDE;
     virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
                                   const SkPaint* paint = NULL) SK_OVERRIDE;
diff --git a/include/utils/SkRTConf.h b/include/utils/SkRTConf.h
new file mode 100644
index 0000000..c34dafb
--- /dev/null
+++ b/include/utils/SkRTConf.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2013 Google, Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkRTConf_DEFINED
+#define SkRTConf_DEFINED
+
+#include "SkString.h"
+#include "SkStream.h"
+
+#include "SkTDict.h"
+#include "SkTArray.h"
+
+/** \class SkRTConfBase
+    Non-templated base class for the runtime configs
+*/
+
+class SkRTConfBase {
+public:
+    SkRTConfBase(const char *name) : fName(name) {}
+    virtual ~SkRTConfBase() {}
+    virtual const char *getName() const { return fName.c_str(); }
+    virtual bool isDefault() const = 0;
+    virtual void print(SkWStream *o) const = 0;
+    virtual bool equals(const SkRTConfBase *conf) const = 0;
+protected:
+    SkString fName;
+};
+
+/** \class SkRTConf
+    A class to provide runtime configurability.
+*/
+template<typename T> class SkRTConf: public SkRTConfBase {
+public:
+    SkRTConf(const char *name, const T &defaultValue, const char *description);
+    operator const T&() const { return fValue; }
+    void print(SkWStream *o) const;
+    bool equals(const SkRTConfBase *conf) const;
+    bool isDefault() const { return fDefault == fValue; }
+    void set(const T& value) { fValue = value; }
+protected:
+    void doPrint(char *s) const;
+
+    T        fValue;
+    T        fDefault;
+    SkString fDescription;
+};
+
+#ifdef SK_DEVELOPER
+#define SK_CONF_DECLARE(confType, varName, confName, defaultValue, description) static SkRTConf<confType> varName(confName, defaultValue, description)
+#define SK_CONF_SET(confname, value) skRTConfRegistry().set(confname, value)
+#else
+#define SK_CONF_DECLARE(confType, varName, confName, defaultValue, description) static const confType varName = defaultValue
+#define SK_CONF_SET(confname, value) (void) confname, (void) value
+#endif
+
+/** \class SkRTConfRegistry
+    A class that maintains a systemwide registry of all runtime configuration
+    parameters.  Mainly used for printing them out and handling multiply-defined
+    knobs.
+*/
+
+class SkRTConfRegistry {
+public:
+    SkRTConfRegistry();
+    void printAll(const char *fname = NULL) const;
+    void printNonDefault(const char *fname = NULL) const;
+    const char *configFileLocation() const;
+    void possiblyDumpFile() const;
+    void validate() const;
+    template <typename T> void set(const char *confname, T value);
+private:
+    template<typename T> friend class SkRTConf;
+
+    void registerConf(SkRTConfBase *conf);
+    template <typename T> bool parse(const char *name, T* value);
+
+    SkTDArray<SkString *> fConfigFileKeys, fConfigFileValues;
+    typedef SkTDict< SkTDArray<SkRTConfBase *> * > ConfMap;
+    ConfMap fConfs;
+};
+
+// our singleton registry
+
+SkRTConfRegistry &skRTConfRegistry();
+
+template<typename T>
+SkRTConf<T>::SkRTConf(const char *name, const T &defaultValue, const char *description)
+    : SkRTConfBase(name)
+    , fValue(defaultValue)
+    , fDefault(defaultValue)
+    , fDescription(description) {
+
+    T value;
+    if (skRTConfRegistry().parse(fName.c_str(), &value)) {
+        fValue = value;
+    }
+    skRTConfRegistry().registerConf(this);
+}
+
+template<typename T>
+void SkRTConf<T>::print(SkWStream *o) const {
+    char outline[200]; // should be ok because we specify a max. width for everything here.
+
+    sprintf(outline, "%-30.30s", getName());
+    doPrint(&(outline[30]));
+    sprintf(&(outline[60]), " %.128s", fDescription.c_str());
+    if (' ' == outline[strlen(outline)-1]) {
+        for (int i = strlen(outline)-1 ; ' ' == outline[i] ; i--) {
+            outline[i] = '\0';
+        }
+    }
+    o->writeText(outline);
+}
+
+template<typename T>
+void SkRTConf<T>::doPrint(char *s) const {
+    sprintf(s, "%-30.30s", "How do I print myself??");
+}
+
+template<> inline void SkRTConf<bool>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%s # [%s]", fValue ? "true" : "false", fDefault ? "true" : "false");
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<int>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%d # [%d]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<unsigned int>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%u # [%u]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<float>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%6.6f # [%6.6f]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<double>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%6.6f # [%6.6f]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<> inline void SkRTConf<const char *>::doPrint(char *s) const {
+    char tmp[30];
+    sprintf(tmp, "%s # [%s]", fValue, fDefault);
+    sprintf(s, "%-30.30s", tmp);
+}
+
+template<typename T>
+bool SkRTConf<T>::equals(const SkRTConfBase *conf) const {
+    // static_cast here is okay because there's only one kind of child class.
+    const SkRTConf<T> *child_pointer = static_cast<const SkRTConf<T> *>(conf);
+    return child_pointer &&
+           fName == child_pointer->fName &&
+           fDescription == child_pointer->fDescription &&
+           fValue == child_pointer->fValue &&
+           fDefault == child_pointer->fDefault;
+}
+
+#endif
diff --git a/include/utils/SkRandom.h b/include/utils/SkRandom.h
new file mode 100644
index 0000000..150384d
--- /dev/null
+++ b/include/utils/SkRandom.h
@@ -0,0 +1,323 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkRandom_DEFINED
+#define SkRandom_DEFINED
+
+#include "Sk64.h"
+#include "SkScalar.h"
+
+/** \class SkRandom
+
+    Utility class that implements pseudo random 32bit numbers using a fast
+    linear equation. Unlike rand(), this class holds its own seed (initially
+    set to 0), so that multiple instances can be used with no side-effects.
+*/
+class SkRandom {
+public:
+    SkRandom() : fSeed(0) {}
+    SkRandom(uint32_t seed) : fSeed(seed) {}
+
+    /** Return the next pseudo random number as an unsigned 32bit value.
+    */
+    uint32_t nextU() { uint32_t r = fSeed * kMul + kAdd; fSeed = r; return r; }
+
+    /** Return the next pseudo random number as a signed 32bit value.
+    */
+    int32_t nextS() { return (int32_t)this->nextU(); }
+
+    /** Return the next pseudo random number as an unsigned 16bit value.
+    */
+    U16CPU nextU16() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number as a signed 16bit value.
+    */
+    S16CPU nextS16() { return this->nextS() >> 16; }
+
+    /**
+     *  Returns value [0...1) as a float
+     */
+    float nextF() {
+        // const is 1 / (2^32 - 1)
+        return (float)(this->nextU() * 2.32830644e-10);
+    }
+
+    /**
+     *  Returns value [min...max) as a float
+     */
+    float nextRangeF(float min, float max) {
+        return min + this->nextF() * (max - min);
+    }
+
+    /** Return the next pseudo random number, as an unsigned value of
+        at most bitCount bits.
+        @param bitCount The maximum number of bits to be returned
+    */
+    uint32_t nextBits(unsigned bitCount) {
+        SkASSERT(bitCount > 0 && bitCount <= 32);
+        return this->nextU() >> (32 - bitCount);
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+        [min, max] inclusive.
+    */
+    uint32_t nextRangeU(uint32_t min, uint32_t max) {
+        SkASSERT(min <= max);
+        uint32_t range = max - min + 1;
+        if (0 == range) {
+            return this->nextU();
+        } else {
+            return min + this->nextU() % range;
+        }
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+        [0, count).
+     */
+    uint32_t nextULessThan(uint32_t count) {
+        SkASSERT(count > 0);
+        return this->nextRangeU(0, count - 1);
+    }
+
+    /** Return the next pseudo random number expressed as an unsigned SkFixed
+        in the range [0..SK_Fixed1).
+    */
+    SkFixed nextUFixed1() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number expressed as a signed SkFixed
+        in the range (-SK_Fixed1..SK_Fixed1).
+    */
+    SkFixed nextSFixed1() { return this->nextS() >> 15; }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range [0..SK_Scalar1).
+    */
+    SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range [min..max).
+    */
+    SkScalar nextRangeScalar(SkScalar min, SkScalar max) {
+        return SkScalarMul(this->nextUScalar1(), (max - min)) + min;
+    }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+        in the range (-SK_Scalar1..SK_Scalar1).
+    */
+    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
+
+    /** Return the next pseudo random number as a bool.
+    */
+    bool nextBool() { return this->nextU() >= 0x80000000; }
+
+    /** A biased version of nextBool().
+     */
+    bool nextBiasedBool(SkScalar fractionTrue) {
+        SkASSERT(fractionTrue >= 0 && fractionTrue <= SK_Scalar1);
+        return this->nextUScalar1() <= fractionTrue;
+    }
+
+    /** Return the next pseudo random number as a signed 64bit value.
+    */
+    void next64(Sk64* a) {
+        SkASSERT(a);
+        a->set(this->nextS(), this->nextU());
+    }
+
+    /**
+     *  Return the current seed. This allows the caller to later reset to the
+     *  same seed (using setSeed) so it can generate the same sequence.
+     */
+    int32_t getSeed() const { return fSeed; }
+
+    /** Set the seed of the random object. The seed is initialized to 0 when the
+        object is first created, and is updated each time the next pseudo random
+        number is requested.
+    */
+    void setSeed(int32_t seed) { fSeed = (uint32_t)seed; }
+
+private:
+    //  See "Numerical Recipes in C", 1992 page 284 for these constants
+    enum {
+        kMul = 1664525,
+        kAdd = 1013904223
+    };
+    uint32_t fSeed;
+};
+
+/** \class SkMWCRandom
+
+ Utility class that implements pseudo random 32bit numbers using Marsaglia's
+ multiply-with-carry "mother of all" algorithm. Unlike rand(), this class holds
+ its own state, so that multiple instances can be used with no side-effects.
+
+ Has a large period and all bits are well-randomized.
+ */
+class SkMWCRandom {
+public:
+    SkMWCRandom() { init(0); }
+    SkMWCRandom(uint32_t seed) { init(seed); }
+    SkMWCRandom(const SkMWCRandom& rand) : fK(rand.fK), fJ(rand.fJ) {}
+
+    SkMWCRandom& operator=(const SkMWCRandom& rand) {
+        fK = rand.fK;
+        fJ = rand.fJ;
+
+        return *this;
+    }
+
+    /** Return the next pseudo random number as an unsigned 32bit value.
+     */
+    uint32_t nextU() {
+        fK = kKMul*(fK & 0xffff) + (fK >> 16);
+        fJ = kJMul*(fJ & 0xffff) + (fJ >> 16);
+        return (((fK << 16) | (fK >> 16)) + fJ);
+    }
+
+    /** Return the next pseudo random number as a signed 32bit value.
+     */
+    int32_t nextS() { return (int32_t)this->nextU(); }
+
+    /** Return the next pseudo random number as an unsigned 16bit value.
+     */
+    U16CPU nextU16() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number as a signed 16bit value.
+     */
+    S16CPU nextS16() { return this->nextS() >> 16; }
+
+    /**
+     *  Returns value [0...1) as an IEEE float
+     */
+    float nextF() {
+        unsigned int floatint = 0x3f800000 | (this->nextU() >> 9);
+        float f = *(float*)(&floatint) - 1.0f;
+        return f;
+    }
+
+    /**
+     *  Returns value [min...max) as a float
+     */
+    float nextRangeF(float min, float max) {
+        return min + this->nextF() * (max - min);
+    }
+
+    /** Return the next pseudo random number, as an unsigned value of
+     at most bitCount bits.
+     @param bitCount The maximum number of bits to be returned
+     */
+    uint32_t nextBits(unsigned bitCount) {
+        SkASSERT(bitCount > 0 && bitCount <= 32);
+        return this->nextU() >> (32 - bitCount);
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+     [min, max] inclusive.
+     */
+    uint32_t nextRangeU(uint32_t min, uint32_t max) {
+        SkASSERT(min <= max);
+        uint32_t range = max - min + 1;
+        if (0 == range) {
+            return this->nextU();
+        } else {
+            return min + this->nextU() % range;
+        }
+    }
+
+    /** Return the next pseudo random unsigned number, mapped to lie within
+     [0, count).
+     */
+    uint32_t nextULessThan(uint32_t count) {
+        SkASSERT(count > 0);
+        return this->nextRangeU(0, count - 1);
+    }
+
+    /** Return the next pseudo random number expressed as an unsigned SkFixed
+     in the range [0..SK_Fixed1).
+     */
+    SkFixed nextUFixed1() { return this->nextU() >> 16; }
+
+    /** Return the next pseudo random number expressed as a signed SkFixed
+     in the range (-SK_Fixed1..SK_Fixed1).
+     */
+    SkFixed nextSFixed1() { return this->nextS() >> 15; }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+     in the range [0..SK_Scalar1).
+     */
+    SkScalar nextUScalar1() { return SkFixedToScalar(this->nextUFixed1()); }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+     in the range [min..max).
+     */
+    SkScalar nextRangeScalar(SkScalar min, SkScalar max) {
+        return SkScalarMul(this->nextUScalar1(), (max - min)) + min;
+    }
+
+    /** Return the next pseudo random number expressed as a SkScalar
+     in the range (-SK_Scalar1..SK_Scalar1).
+     */
+    SkScalar nextSScalar1() { return SkFixedToScalar(this->nextSFixed1()); }
+
+    /** Return the next pseudo random number as a bool.
+     */
+    bool nextBool() { return this->nextU() >= 0x80000000; }
+
+    /** A biased version of nextBool().
+     */
+    bool nextBiasedBool(SkScalar fractionTrue) {
+        SkASSERT(fractionTrue >= 0 && fractionTrue <= SK_Scalar1);
+        return this->nextUScalar1() <= fractionTrue;
+    }
+
+    /** Return the next pseudo random number as a signed 64bit value.
+     */
+    void next64(Sk64* a) {
+        SkASSERT(a);
+        a->set(this->nextS(), this->nextU());
+    }
+
+    /** Reset the random object.
+     */
+    void setSeed(uint32_t seed) { init(seed); }
+
+private:
+    // Initialize state variables with LCG.
+    // We must ensure that both J and K are non-zero, otherwise the
+    // multiply-with-carry step will forevermore return zero.
+    void init(uint32_t seed) {
+        fK = NextLCG(seed);
+        if (0 == fK) {
+            fK = NextLCG(fK);
+        }
+        fJ = NextLCG(fK);
+        if (0 == fJ) {
+            fJ = NextLCG(fJ);
+        }
+        SkASSERT(0 != fK && 0 != fJ);
+    }
+    static uint32_t NextLCG(uint32_t seed) { return kMul*seed + kAdd; }
+
+    //  See "Numerical Recipes in C", 1992 page 284 for these constants
+    //  For the LCG that sets the initial state from a seed
+    enum {
+        kMul = 1664525,
+        kAdd = 1013904223
+    };
+    // Constants for the multiply-with-carry steps
+    enum {
+        kKMul = 30345,
+        kJMul = 18000,
+    };
+
+    uint32_t fK;
+    uint32_t fJ;
+};
+
+#endif
diff --git a/include/utils/SkRunnable.h b/include/utils/SkRunnable.h
new file mode 100644
index 0000000..84e4375
--- /dev/null
+++ b/include/utils/SkRunnable.h
@@ -0,0 +1,17 @@
+/*
+ * 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 SkRunnable_DEFINED
+#define SkRunnable_DEFINED
+
+class SkRunnable {
+public:
+    virtual ~SkRunnable() {};
+    virtual void run() = 0;
+};
+
+#endif
diff --git a/include/utils/SkSfntUtils.h b/include/utils/SkSfntUtils.h
deleted file mode 100644
index 69c9c03..0000000
--- a/include/utils/SkSfntUtils.h
+++ /dev/null
@@ -1,59 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkSfntUtils_DEFINED
-#define SkSfntUtils_DEFINED
-
-#include "SkFontHost.h"
-
-struct SkSfntTable_head {
-    SkFixed     fVersion;
-    SkFixed     fRevision;
-    uint32_t    fCheckSumAdjustment;
-    uint32_t    fMagicNumber;
-    uint16_t    fFlags;
-    uint16_t    fUnitsPerEm;
-    Sk64        fDateCreated;
-    Sk64        fDateModified;
-    int16_t     fXMin;
-    int16_t     fYMin;
-    int16_t     fXMax;
-    int16_t     fYMax;
-    uint16_t    fMacStyle;
-    uint16_t    fLowestPPEM;
-    int16_t     fFontDirectionHint;
-    int16_t     fIndexToLocFormat;
-    int16_t     fGlyphDataFormat;
-};
-
-struct SkSfntTable_maxp {
-    SkFixed     fVersion;
-    uint16_t    fNumGlyphs;
-    uint16_t    fMaxPoints;
-    uint16_t    fMaxContours;
-    uint16_t    fMaxComponentPoints;
-    uint16_t    fMaxComponentContours;
-    uint16_t    fMaxZones;
-    uint16_t    fMaxTwilightPoints;
-    uint16_t    fMaxStorage;
-    uint16_t    fMaxFunctionDefs;
-    uint16_t    fMaxInstructionDefs;
-    uint16_t    fMaxStackElements;
-    uint16_t    fMaxSizeOfInstructions;
-    uint16_t    fMaxComponentElements;
-    uint16_t    fMaxComponentDepth;
-};
-
-class SkSfntUtils {
-public:
-    static bool ReadTable_head(SkFontID, SkSfntTable_head*);
-    static bool ReadTable_maxp(SkFontID, SkSfntTable_maxp*);
-};
-
-#endif
-
diff --git a/include/utils/SkTextBox.h b/include/utils/SkTextBox.h
deleted file mode 100644
index e3b365d..0000000
--- a/include/utils/SkTextBox.h
+++ /dev/null
@@ -1,78 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkTextBox_DEFINED
-#define SkTextBox_DEFINED
-
-#include "SkCanvas.h"
-
-/** \class SkTextBox
-
-    SkTextBox is a helper class for drawing 1 or more lines of text
-    within a rectangle. The textbox is positioned and clipped by its Frame.
-    The Margin rectangle controls where the text is drawn relative to
-    the Frame. Line-breaks occur inside the Margin rectangle.
-
-    Spacing is a linear equation used to compute the distance between lines
-    of text. Spacing consists of two scalars: mul and add, and the spacing
-    between lines is computed as: spacing = paint.getTextSize() * mul + add
-*/
-class SkTextBox {
-public:
-    SkTextBox();
-
-    enum Mode {
-        kOneLine_Mode,
-        kLineBreak_Mode,
-
-        kModeCount
-    };
-    Mode    getMode() const { return (Mode)fMode; }
-    void    setMode(Mode);
-
-    enum SpacingAlign {
-        kStart_SpacingAlign,
-        kCenter_SpacingAlign,
-        kEnd_SpacingAlign,
-
-        kSpacingAlignCount
-    };
-    SpacingAlign    getSpacingAlign() const { return (SpacingAlign)fSpacingAlign; }
-    void            setSpacingAlign(SpacingAlign);
-
-    void    getBox(SkRect*) const;
-    void    setBox(const SkRect&);
-    void    setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
-
-    void    getSpacing(SkScalar* mul, SkScalar* add) const;
-    void    setSpacing(SkScalar mul, SkScalar add);
-
-    void    draw(SkCanvas*, const char text[], size_t len, const SkPaint&);
-
-    void    setText(const char text[], size_t len, const SkPaint&);
-    void    draw(SkCanvas*);
-    int     countLines() const;
-    SkScalar getTextHeight() const;
-
-private:
-    SkRect      fBox;
-    SkScalar    fSpacingMul, fSpacingAdd;
-    uint8_t     fMode, fSpacingAlign;
-    const char* fText;
-    size_t      fLen;
-    const SkPaint* fPaint;
-};
-
-class SkTextLineBreaker {
-public:
-    static int CountLines(const char text[], size_t len, const SkPaint&, SkScalar width);
-};
-
-#endif
-
diff --git a/include/utils/SkThreadPool.h b/include/utils/SkThreadPool.h
new file mode 100644
index 0000000..cc45fc2
--- /dev/null
+++ b/include/utils/SkThreadPool.h
@@ -0,0 +1,50 @@
+/*
+ * 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 SkThreadPool_DEFINED
+#define SkThreadPool_DEFINED
+
+#include "SkCondVar.h"
+#include "SkTDArray.h"
+#include "SkTInternalLList.h"
+
+class SkRunnable;
+class SkThread;
+
+class SkThreadPool {
+
+public:
+    /**
+     * Create a threadpool with exactly count (>=0) threads.
+     */
+    explicit SkThreadPool(int count);
+    ~SkThreadPool();
+
+    /**
+     * Queues up an SkRunnable to run when a thread is available, or immediately if
+     * count is 0.  NULL is a safe no-op.  Does not take ownership.
+     */
+    void add(SkRunnable*);
+
+ private:
+    struct LinkedRunnable {
+        // Unowned pointer.
+        SkRunnable* fRunnable;
+
+    private:
+        SK_DECLARE_INTERNAL_LLIST_INTERFACE(LinkedRunnable);
+    };
+
+    SkTInternalLList<LinkedRunnable>    fQueue;
+    SkCondVar                           fReady;
+    SkTDArray<SkThread*>                fThreads;
+    bool                            fDone;
+
+    static void Loop(void*);  // Static because we pass in this.
+};
+
+#endif
diff --git a/include/utils/SkUnitMappers.h b/include/utils/SkUnitMappers.h
index a14f1af..64aab5d 100644
--- a/include/utils/SkUnitMappers.h
+++ b/include/utils/SkUnitMappers.h
@@ -20,17 +20,16 @@
     // override from SkUnitMapper
     virtual uint16_t mapUnit16(uint16_t x);
 
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiscreteMapper)
+
 protected:
     SkDiscreteMapper(SkFlattenableReadBuffer& );
-    // overrides from SkFlattenable
-    virtual void flatten(SkFlattenableWriteBuffer& );
-    virtual Factory getFactory();
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
 private:
     int     fSegments;
     SkFract fScale;    // computed from fSegments
 
-    static SkFlattenable* Create(SkFlattenableReadBuffer& buffer);
-    
     typedef SkUnitMapper INHERITED;
 };
 
@@ -43,16 +42,14 @@
     // override from SkUnitMapper
     virtual uint16_t mapUnit16(uint16_t x);
 
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkCosineMapper)
+
 protected:
     SkCosineMapper(SkFlattenableReadBuffer&);
-    // overrides from SkFlattenable
-    virtual Factory getFactory();
 
 private:
-    static SkFlattenable* Create(SkFlattenableReadBuffer&);
 
     typedef SkUnitMapper INHERITED;
 };
 
 #endif
-
diff --git a/include/utils/SkWGL.h b/include/utils/SkWGL.h
index 8eae5af..619b8e0 100644
--- a/include/utils/SkWGL.h
+++ b/include/utils/SkWGL.h
@@ -33,28 +33,30 @@
     #undef SK_LOCAL_LEAN_AND_MEAN
 #endif
 
-#define SK_WGL_DRAW_TO_WINDOW                                       0x2001
-#define SK_WGL_ACCELERATION                                         0x2003
-#define SK_WGL_SUPPORT_OPENGL                                       0x2010
-#define SK_WGL_DOUBLE_BUFFER                                        0x2011
-#define SK_WGL_COLOR_BITS                                           0x2014
-#define SK_WGL_ALPHA_BITS                                           0x201B
-#define SK_WGL_STENCIL_BITS                                         0x2023
-#define SK_WGL_FULL_ACCELERATION                                    0x2027
-#define SK_WGL_SAMPLE_BUFFERS                                       0x2041
-#define SK_WGL_SAMPLES                                              0x2042
-#define SK_WGL_CONTEXT_MAJOR_VERSION                                0x2091
-#define SK_WGL_CONTEXT_MINOR_VERSION                                0x2092
-#define SK_WGL_CONTEXT_LAYER_PLANE                                  0x2093
-#define SK_WGL_CONTEXT_FLAGS                                        0x2094
-#define SK_WGL_CONTEXT_PROFILE_MASK                                 0x9126
-#define SK_WGL_CONTEXT_DEBUG_BIT                                    0x0001
-#define SK_WGL_CONTEXT_FORWARD_COMPATIBLE_BIT                       0x0002
-#define SK_WGL_CONTEXT_CORE_PROFILE_BIT                             0x00000001
-#define SK_WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT                    0x00000002
-#define SK_WGL_CONTEXT_ES2_PROFILE_BIT                              0x00000004
-#define SK_ERROR_INVALID_VERSION                                    0x2095
-#define SK_ERROR_INVALID_PROFILE                                    0x2096
+#define SK_WGL_DRAW_TO_WINDOW                       0x2001
+#define SK_WGL_ACCELERATION                         0x2003
+#define SK_WGL_SUPPORT_OPENGL                       0x2010
+#define SK_WGL_DOUBLE_BUFFER                        0x2011
+#define SK_WGL_COLOR_BITS                           0x2014
+#define SK_WGL_ALPHA_BITS                           0x201B
+#define SK_WGL_STENCIL_BITS                         0x2023
+#define SK_WGL_FULL_ACCELERATION                    0x2027
+#define SK_WGL_SAMPLE_BUFFERS                       0x2041
+#define SK_WGL_SAMPLES                              0x2042
+#define SK_WGL_COVERAGE_SAMPLES                     0x2042 /* same as SAMPLES */
+#define SK_WGL_COLOR_SAMPLES                        0x20B9
+#define SK_WGL_CONTEXT_MAJOR_VERSION                0x2091
+#define SK_WGL_CONTEXT_MINOR_VERSION                0x2092
+#define SK_WGL_CONTEXT_LAYER_PLANE                  0x2093
+#define SK_WGL_CONTEXT_FLAGS                        0x2094
+#define SK_WGL_CONTEXT_PROFILE_MASK                 0x9126
+#define SK_WGL_CONTEXT_DEBUG_BIT                    0x0001
+#define SK_WGL_CONTEXT_FORWARD_COMPATIBLE_BIT       0x0002
+#define SK_WGL_CONTEXT_CORE_PROFILE_BIT             0x00000001
+#define SK_WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT    0x00000002
+#define SK_WGL_CONTEXT_ES2_PROFILE_BIT              0x00000004
+#define SK_ERROR_INVALID_VERSION                    0x2095
+#define SK_ERROR_INVALID_PROFILE                    0x2096
 
 class SkWGLExtensions {
 public:
@@ -73,6 +75,23 @@
     BOOL getPixelFormatAttribfv(HDC hdc, int, int, UINT, const int*, FLOAT*) const;
     HGLRC createContextAttribs(HDC, HGLRC, const int *) const;
 
+    /**
+     * WGL doesn't have precise rules for the ordering of formats returned
+     * by wglChoosePixelFormat. This function helps choose among the set of
+     * formats returned by wglChoosePixelFormat. The rules in decreasing
+     * priority are:
+     *     * Choose formats with the smallest sample count that is >=
+     *       desiredSampleCount (or the largest sample count if all formats have
+     *       fewer samples than desiredSampleCount.)
+     *     * Choose formats with the fewest color samples when coverage sampling
+     *       is available.
+     *     * If the above rules leave multiple formats, choose the one that
+     *       appears first in the formats array parameter.
+     */
+    int selectFormat(const int formats[],
+                     int formatCount,
+                     HDC dc,
+                     int desiredSampleCount);
 private:
     typedef const char* (WINAPI *GetExtensionsStringProc)(HDC hdc);
     typedef BOOL (WINAPI *ChoosePixelFormatProc)(HDC hdc, const int *, const FLOAT *, UINT, int *, UINT *);
diff --git a/include/utils/ios/SkStream_NSData.h b/include/utils/ios/SkStream_NSData.h
index 0829a4f..8e6f064 100755
--- a/include/utils/ios/SkStream_NSData.h
+++ b/include/utils/ios/SkStream_NSData.h
@@ -30,7 +30,7 @@
 public:
             SkStream_NSData(NSData* data);
     virtual ~SkStream_NSData();
-    
+
     static SkStream_NSData* CreateFromResource(const char name[],
                                                const char suffix[]);
 
diff --git a/include/utils/win/SkHRESULT.h b/include/utils/win/SkHRESULT.h
index ff596c7..9738f74 100644
--- a/include/utils/win/SkHRESULT.h
+++ b/include/utils/win/SkHRESULT.h
@@ -36,6 +36,7 @@
 In variants ending with 'M' the given message will be traced when FAILED.
 The HR variants will return the HRESULT when FAILED.
 The HRB variants will return false when FAILED.
+The HRN variants will return NULL when FAILED.
 The HRV variants will simply return when FAILED.
 */
 #define HR(ex) HR_GENERAL(ex, NULL, _hr)
@@ -44,6 +45,9 @@
 #define HRB(ex) HR_GENERAL(ex, NULL, false)
 #define HRBM(ex, msg) HR_GENERAL(ex, msg, false)
 
+#define HRN(ex) HR_GENERAL(ex, NULL, NULL)
+#define HRNM(ex, msg) HR_GENERAL(ex, msg, NULL)
+
 #define HRV(ex) HR_GENERAL(ex, NULL, )
 #define HRVM(ex, msg) HR_GENERAL(ex, msg, )
 //@}
diff --git a/include/utils/win/SkIStream.h b/include/utils/win/SkIStream.h
index b7d0949..e4e045c 100644
--- a/include/utils/win/SkIStream.h
+++ b/include/utils/win/SkIStream.h
@@ -46,24 +46,24 @@
     // IStream Interface
 public:
     virtual HRESULT STDMETHODCALLTYPE SetSize(ULARGE_INTEGER);
-    
+
     virtual HRESULT STDMETHODCALLTYPE CopyTo(IStream*
                                            , ULARGE_INTEGER
                                            , ULARGE_INTEGER*
                                            , ULARGE_INTEGER*);
-    
+
     virtual HRESULT STDMETHODCALLTYPE Commit(DWORD);
-    
+
     virtual HRESULT STDMETHODCALLTYPE Revert(void);
-    
+
     virtual HRESULT STDMETHODCALLTYPE LockRegion(ULARGE_INTEGER
                                                , ULARGE_INTEGER
                                                , DWORD);
-    
+
     virtual HRESULT STDMETHODCALLTYPE UnlockRegion(ULARGE_INTEGER
                                                  , ULARGE_INTEGER
                                                  , DWORD);
-    
+
     virtual HRESULT STDMETHODCALLTYPE Clone(IStream **);
 
     virtual HRESULT STDMETHODCALLTYPE Seek(LARGE_INTEGER liDistanceToMove
diff --git a/include/utils/win/SkTScopedComPtr.h b/include/utils/win/SkTScopedComPtr.h
index b9be037..85c314a 100644
--- a/include/utils/win/SkTScopedComPtr.h
+++ b/include/utils/win/SkTScopedComPtr.h
@@ -29,7 +29,7 @@
     ~SkTScopedComPtr() {
         this->reset();
     }
-    T &operator*() const { return *fPtr; }
+    T &operator*() const { SkASSERT(fPtr != NULL); return *fPtr; }
     SkBlockComRef<T> *operator->() const {
         return static_cast<SkBlockComRef<T>*>(fPtr);
     }
@@ -47,13 +47,13 @@
             this->fPtr = NULL;
         }
     }
-    
+
     void swap(SkTScopedComPtr<T>& that) {
         T* temp = this->fPtr;
         this->fPtr = that.fPtr;
         that.fPtr = temp;
     }
-    
+
     T* release() {
         T* temp = this->fPtr;
         this->fPtr = NULL;
diff --git a/include/views/SkBGViewArtist.h b/include/views/SkBGViewArtist.h
index 869beab..33054c8 100644
--- a/include/views/SkBGViewArtist.h
+++ b/include/views/SkBGViewArtist.h
@@ -31,4 +31,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkBorderView.h b/include/views/SkBorderView.h
deleted file mode 100644
index 02f8725..0000000
--- a/include/views/SkBorderView.h
+++ /dev/null
@@ -1,41 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkBorderView_DEFINED
-#define SkBorderView_DEFINED
-
-#include "SkView.h"
-#include "SkWidgetViews.h"
-#include "SkAnimator.h"
-
-class SkBorderView : public SkWidgetView {
-public:
-    SkBorderView();
-    ~SkBorderView();
-    void setSkin(const char skin[]);
-    SkScalar getLeft() const { return fLeft; }
-    SkScalar getRight() const { return fRight; }
-    SkScalar getTop() const { return fTop; }
-    SkScalar getBottom() const { return fBottom; }
-protected:
-    //overrides
-    virtual void onInflate(const SkDOM& dom,  const SkDOM::Node* node);
-    virtual void onSizeChange();
-    virtual void onDraw(SkCanvas* canvas);
-    virtual bool onEvent(const SkEvent& evt);
-private:
-    SkAnimator fAnim;
-    SkScalar fLeft, fRight, fTop, fBottom;  //margin on each side
-    SkRect fMargin;
-
-    typedef SkWidgetView INHERITED;
-};
-
-#endif
-
diff --git a/include/views/SkEvent.h b/include/views/SkEvent.h
index b3a07e9..f4df448 100644
--- a/include/views/SkEvent.h
+++ b/include/views/SkEvent.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkEvent_DEFINED
 #define SkEvent_DEFINED
 
@@ -105,7 +103,7 @@
         fTargetProc = proc;
         return this;
     }
-    
+
     /**
      *  Return the event's unnamed 32bit field. Default value is 0
      */
@@ -195,7 +193,7 @@
     void post() {
         return this->postDelay(0);
     }
-    
+
     /**
      *  Post to the event queue using the event's targetID or target-proc and
      *  the specifed millisecond delay.
@@ -204,7 +202,7 @@
      *  the event queue. It cannot be allocated on the stack or in a global.
      */
     void postDelay(SkMSec delay);
-    
+
     /**
      *  Post to the event queue using the event's targetID or target-proc.
      *  The event will be delivered no sooner than the specified millisecond
@@ -258,16 +256,8 @@
     */
     static void SignalQueueTimer(SkMSec delay);
 
-#ifndef SK_USE_WXWIDGETS
-#ifdef SK_BUILD_FOR_WIN
+#if defined(SK_BUILD_FOR_WIN)
     static bool WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
-#elif defined(SK_BUILD_FOR_UNIXx)
-  static uint32_t HandleTimer(uint32_t, void*);
-  static bool WndProc(Display*, Window, XEvent&);
-#endif
-#else
-    // Don't know yet what this will be
-    //static bool CustomEvent();
 #endif
 
 private:
@@ -292,4 +282,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkEventSink.h b/include/views/SkEventSink.h
index 69981fa..01d4f7a 100644
--- a/include/views/SkEventSink.h
+++ b/include/views/SkEventSink.h
@@ -21,7 +21,9 @@
 */
 class SkEventSink : public SkRefCnt {
 public:
-            SkEventSink();
+    SK_DECLARE_INST_COUNT(SkEventSink)
+
+             SkEventSink();
     virtual ~SkEventSink();
 
     /**
@@ -103,7 +105,8 @@
 
     // for our private link-list
     SkEventSink*    fNextSink;
+
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
-
diff --git a/include/views/SkImageView.h b/include/views/SkImageView.h
deleted file mode 100644
index e179141..0000000
--- a/include/views/SkImageView.h
+++ /dev/null
@@ -1,68 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkImageView_DEFINED
-#define SkImageView_DEFINED
-
-#include "SkView.h"
-#include "SkString.h"
-
-class SkAnimator;
-class SkBitmap;
-class SkMatrix;
-
-class SkImageView : public SkView {
-public:
-            SkImageView();
-    virtual ~SkImageView();
-
-    void    getUri(SkString*) const;
-    void    setUri(const char []);
-    void    setUri(const SkString&);
-    
-
-    enum ScaleType {
-        kMatrix_ScaleType,
-        kFitXY_ScaleType,
-        kFitStart_ScaleType,
-        kFitCenter_ScaleType,
-        kFitEnd_ScaleType
-    };
-    ScaleType   getScaleType() const { return (ScaleType)fScaleType; }
-    void        setScaleType(ScaleType);
-    
-    bool    getImageMatrix(SkMatrix*) const;
-    void    setImageMatrix(const SkMatrix*);
-
-protected:
-    // overrides
-    virtual bool    onEvent(const SkEvent&);
-    virtual void    onDraw(SkCanvas*);
-    virtual void    onInflate(const SkDOM&, const SkDOMNode*);
-    
-private:
-    SkString    fUri;
-    SkMatrix*   fMatrix;    // null or copy of caller's matrix ,,,,,
-    union {
-        SkAnimator* fAnim;
-        SkBitmap* fBitmap;
-    } fData;
-    uint8_t     fScaleType;
-    SkBool8     fDataIsAnim;    // as opposed to bitmap
-    SkBool8     fUriIsValid;
-    
-    void    onUriChange();
-    bool    getDataBounds(SkRect* bounds);
-    bool    freeData();
-    bool    ensureUriIsLoaded();
-
-    typedef SkView INHERITED;
-};
-
-#endif
diff --git a/include/views/SkKey.h b/include/views/SkKey.h
index 4db3108..036e2c3 100644
--- a/include/views/SkKey.h
+++ b/include/views/SkKey.h
@@ -13,9 +13,9 @@
 #include "SkTypes.h"
 
 enum SkKey {
-    //reordering these to match android.app.KeyEvent 
+    //reordering these to match android.app.KeyEvent
     kNONE_SkKey,    //corresponds to android's UNKNOWN
-    
+
     kLeftSoftKey_SkKey,
     kRightSoftKey_SkKey,
 
@@ -23,7 +23,7 @@
     kBack_SkKey,    //!< (CLR)
     kSend_SkKey,    //!< the green (talk) key
     kEnd_SkKey,     //!< the red key
-    
+
     k0_SkKey,
     k1_SkKey,
     k2_SkKey,
@@ -52,5 +52,11 @@
     kSkKeyCount
 };
 
-#endif
+enum SkModifierKeys {
+    kShift_SkModifierKey    = 1 << 0,
+    kControl_SkModifierKey  = 1 << 1,
+    kOption_SkModifierKey   = 1 << 2,   // same as ALT
+    kCommand_SkModifierKey  = 1 << 3,
+};
 
+#endif
diff --git a/include/views/SkOSMenu.h b/include/views/SkOSMenu.h
index 0c4a619..8801a52 100644
--- a/include/views/SkOSMenu.h
+++ b/include/views/SkOSMenu.h
@@ -17,9 +17,9 @@
 public:
     explicit SkOSMenu(const char title[] = "");
     ~SkOSMenu();
-    
+
     /**
-     * Each of these (except action) has an associated value, which is stored in 
+     * Each of these (except action) has an associated value, which is stored in
      * the event payload for the item.
      * Each type has a specific type for its value...
      *     Action : none
@@ -40,23 +40,23 @@
         kTextField_Type,
         kCustom_Type
     };
-    
+
     enum TriState {
         kMixedState = -1,
         kOffState = 0,
         kOnState = 1
     };
-    
+
     class Item {
     public:
         /**
          * Auto increments a global to generate an unique ID for each new item
          * Note: Thread safe
          */
-        Item(const char label[], SkOSMenu::Type type, const char slotName[], 
+        Item(const char label[], SkOSMenu::Type type, const char slotName[],
              SkEvent* evt);
         ~Item() { delete fEvent; }
-        
+
         SkEvent*    getEvent() const { return fEvent; }
         int         getID() const { return fID; }
         const char* getLabel() const { return fLabel.c_str(); }
@@ -64,18 +64,18 @@
         Type        getType() const { return fType; }
         void        setKeyEquivalent(SkUnichar key) { fKey = key; }
         SkUnichar   getKeyEquivalent() const { return fKey; }
-        
+
         /**
          * Helper functions for predefined types
          */
         void setBool(bool value) const;             //For Switch
-        void setScalar(SkScalar value) const;       //For Slider     
+        void setScalar(SkScalar value) const;       //For Slider
         void setInt(int value) const;               //For List
         void setTriState(TriState value) const;     //For Tristate
         void setString(const char value[]) const;   //For TextField
-        
+
         /**
-         * Post event associated with the menu item to target, any changes to 
+         * Post event associated with the menu item to target, any changes to
          * the associated event must be made prior to calling this method
          */
         void postEvent() const { (new SkEvent(*(fEvent)))->post(); }
@@ -88,80 +88,80 @@
         Type            fType;
         SkUnichar       fKey;
     };
-    
+
     void        reset();
     const char* getTitle() const { return fTitle.c_str(); }
     void        setTitle (const char title[]) { fTitle.set(title); }
     int         getCount() const { return fItems.count(); }
     const Item* getItemByID(int itemID) const;
     void        getItems(const Item* items[]) const;
-    
+
     /**
-     * Assign key to the menu item with itemID, will do nothing if there's no 
+     * Assign key to the menu item with itemID, will do nothing if there's no
      * item with the id given
      */
     void        assignKeyEquivalentToItem(int itemID, SkUnichar key);
     /**
-     * Call this in a SkView's onHandleChar to trigger any menu items with the 
-     * given key equivalent. If such an item is found, the method will return 
-     * true and its corresponding event will be triggered (default behavior 
+     * Call this in a SkView's onHandleChar to trigger any menu items with the
+     * given key equivalent. If such an item is found, the method will return
+     * true and its corresponding event will be triggered (default behavior
      * defined for switches(toggling), tristates(cycle), and lists(cycle),
-     * for anything else, the event attached is posted without state changes) 
+     * for anything else, the event attached is posted without state changes)
      * If no menu item can be matched with the key, false will be returned
      */
     bool        handleKeyEquivalent(SkUnichar key);
-    
+
     /**
-     * The following functions append new items to the menu and returns their 
-     * associated unique id, which can be used to by the client to refer to 
+     * The following functions append new items to the menu and returns their
+     * associated unique id, which can be used to by the client to refer to
      * the menu item created and change its state. slotName specifies the string
      * identifier of any state/value to be returned in the item's SkEvent object
      * NOTE: evt must be dynamically allocated
      */
-    int appendItem(const char label[], Type type, const char slotName[], 
-                   SkEvent* evt); 
-    
+    int appendItem(const char label[], Type type, const char slotName[],
+                   SkEvent* evt);
+
     /**
-     * Create predefined items with the given parameters. To be used with the 
+     * Create predefined items with the given parameters. To be used with the
      * other helper functions below to retrive/update state information.
-     * Note: the helper functions below assume that slotName is UNIQUE for all 
+     * Note: the helper functions below assume that slotName is UNIQUE for all
      * menu items of the same type since it's used to identify the event
      */
     int appendAction(const char label[], SkEventSinkID target);
-    int appendList(const char label[], const char slotName[], 
+    int appendList(const char label[], const char slotName[],
                    SkEventSinkID target, int defaultIndex, const char[] ...);
-    int appendSlider(const char label[], const char slotName[], 
-                     SkEventSinkID target, SkScalar min, SkScalar max, 
+    int appendSlider(const char label[], const char slotName[],
+                     SkEventSinkID target, SkScalar min, SkScalar max,
                      SkScalar defaultValue);
-    int appendSwitch(const char label[], const char slotName[], 
+    int appendSwitch(const char label[], const char slotName[],
                      SkEventSinkID target, bool defaultState = false);
     int appendTriState(const char label[], const char slotName[],
                        SkEventSinkID target, TriState defaultState = kOffState);
     int appendTextField(const char label[], const char slotName[],
                         SkEventSinkID target, const char placeholder[] = "");
-    
-    
+
+
     /**
      * Helper functions to retrieve information other than the stored value for
      * some predefined types
      */
     static bool FindListItemCount(const SkEvent& evt, int* count);
     /**
-     * Ensure that the items array can store n SkStrings where n is the count 
+     * Ensure that the items array can store n SkStrings where n is the count
      * extracted using FindListItemCount
      */
     static bool FindListItems(const SkEvent& evt, SkString items[]);
     static bool FindSliderMin(const SkEvent& evt, SkScalar* min);
     static bool FindSliderMax(const SkEvent& evt, SkScalar* max);
-    
+
     /**
      * Returns true if an action with the given label is found, false otherwise
      */
     static bool FindAction(const SkEvent& evt, const char label[]);
     /**
-     * The following helper functions will return true if evt is generated from 
-     * a predefined item type and retrieve the corresponding state information. 
-     * They will return false and leave value unchanged if there's a type 
+     * The following helper functions will return true if evt is generated from
+     * a predefined item type and retrieve the corresponding state information.
+     * They will return false and leave value unchanged if there's a type
      * mismatch or slotName is incorrect
      */
     static bool FindListIndex(const SkEvent& evt, const char slotName[], int* value);
@@ -169,15 +169,14 @@
     static bool FindSwitchState(const SkEvent& evt, const char slotName[], bool* value);
     static bool FindTriState(const SkEvent& evt, const char slotName[], TriState* value);
     static bool FindText(const SkEvent& evt, const char slotName[], SkString* value);
-    
+
 private:
     SkString fTitle;
     SkTDArray<Item*> fItems;
-    
+
     // illegal
     SkOSMenu(const SkOSMenu&);
     SkOSMenu& operator=(const SkOSMenu&);
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_Android.h b/include/views/SkOSWindow_Android.h
index bdce5d0..ca9b770 100644
--- a/include/views/SkOSWindow_Android.h
+++ b/include/views/SkOSWindow_Android.h
@@ -18,9 +18,17 @@
 public:
     SkOSWindow(void*) {}
     ~SkOSWindow() {}
-    bool attachGL() { return true; }
-    void detachGL() {}
-    void presentGL() {}
+
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+        kNativeGL_BackEndType,
+    };
+
+    bool attach(SkBackEndTypes /* attachType */, int /* msaaSampleCount */) {
+        return true;
+    }
+    void detach() {}
+    void present() {}
 
     virtual void onPDFSaved(const char title[], const char desc[],
         const char path[]);
@@ -35,4 +43,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_Mac.h b/include/views/SkOSWindow_Mac.h
index b09f1dd..aa52021 100644
--- a/include/views/SkOSWindow_Mac.h
+++ b/include/views/SkOSWindow_Mac.h
@@ -16,13 +16,20 @@
     SkOSWindow(void* hwnd);
     ~SkOSWindow();
     void*   getHWND() const { return fHWND; }
-    
-    virtual bool onDispatchClick(int x, int y, Click::State state, 
-                                 void* owner);
-    void    detachGL();
-    bool    attachGL();
-    void    presentGL();
-    
+
+    virtual bool onDispatchClick(int x, int y, Click::State state,
+                                 void* owner, unsigned modi);
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+#if SK_SUPPORT_GPU
+        kNativeGL_BackEndType,
+#endif
+    };
+
+    void    detach();
+    bool    attach(SkBackEndTypes attachType, int msaaSampleCount);
+    void    present();
+
 protected:
     // overrides from SkEventSink
     virtual bool onEvent(const SkEvent& evt);
@@ -32,12 +39,14 @@
     virtual void onAddMenu(const SkOSMenu*);
     virtual void onUpdateMenu(const SkOSMenu*);
     virtual void onSetTitle(const char[]);
-    
+
 private:
     void*   fHWND;
     bool    fInvalEventIsPending;
     void*   fNotifier;
+#if SK_SUPPORT_GPU
     void*   fGLContext;
+#endif
     typedef SkWindow INHERITED;
 };
 
diff --git a/include/views/SkOSWindow_NaCl.h b/include/views/SkOSWindow_NaCl.h
new file mode 100644
index 0000000..52514ca
--- /dev/null
+++ b/include/views/SkOSWindow_NaCl.h
@@ -0,0 +1,45 @@
+
+/*
+ * 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 SkIRect;
+
+class SkOSWindow : public SkWindow {
+public:
+    SkOSWindow(void*) {}
+    ~SkOSWindow() {}
+
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+        kNativeGL_BackEndType,
+    };
+
+    bool attach(SkBackEndTypes /* attachType */, int /* msaaSampleCount */) {
+        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_SDL.h b/include/views/SkOSWindow_SDL.h
index 037de3b..e6b59e6 100644
--- a/include/views/SkOSWindow_SDL.h
+++ b/include/views/SkOSWindow_SDL.h
@@ -42,4 +42,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_Unix.h b/include/views/SkOSWindow_Unix.h
index d688fb5..f1a4698 100644
--- a/include/views/SkOSWindow_Unix.h
+++ b/include/views/SkOSWindow_Unix.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,15 +5,14 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkOSWindow_Unix_DEFINED
 #define SkOSWindow_Unix_DEFINED
 
-#include "SkWindow.h"
-#include <X11/Xlib.h>
 #include <GL/glx.h>
+#include <X11/Xlib.h>
 
-class SkBitmap;
+#include "SkWindow.h"
+
 class SkEvent;
 
 struct SkUnixWindow {
@@ -23,7 +21,6 @@
   size_t fOSWin;
   GC fGc;
   GLXContext fGLContext;
-  bool fGLCreated;
 };
 
 class SkOSWindow : public SkWindow {
@@ -36,35 +33,44 @@
     void* getUnixWindow() const { return (void*)&fUnixWindow; }
     void loop();
     void post_linuxevent();
-    bool attachGL();
-    void detachGL();
-    void presentGL();
+
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+        kNativeGL_BackEndType,
+    };
+
+    bool attach(SkBackEndTypes attachType, int msaaSampleCount);
+    void detach();
+    void present();
+
+    int getMSAASampleCount() const { return fMSAASampleCount; }
 
     //static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
 
-    //static bool WndProc(SkUnixWindow* w,  XEvent &e);
-
 protected:
-    // overrides from SkWindow
-    virtual bool onEvent(const SkEvent&);
-    virtual void onHandleInval(const SkIRect&);
-    virtual bool onHandleChar(SkUnichar);
-    virtual bool onHandleKey(SkKey);
-    virtual bool onHandleKeyUp(SkKey);
-    virtual void onSetTitle(const char title[]);
+    // Overridden from from SkWindow:
+    virtual bool onEvent(const SkEvent&) SK_OVERRIDE;
+    virtual void onHandleInval(const SkIRect&) SK_OVERRIDE;
+    virtual bool onHandleChar(SkUnichar) SK_OVERRIDE;
+    virtual bool onHandleKey(SkKey) SK_OVERRIDE;
+    virtual bool onHandleKeyUp(SkKey) SK_OVERRIDE;
+    virtual void onSetTitle(const char title[]) SK_OVERRIDE;
 
 private:
-    SkUnixWindow  fUnixWindow;
-    bool fGLAttached;
+    void doPaint();
+    void mapWindowAndWait();
+
+    void closeWindow();
+    void initWindow(int newMSAASampleCount);
+
+    SkUnixWindow fUnixWindow;
 
     // Needed for GL
     XVisualInfo* fVi;
-
-    void    doPaint();
-    void    mapWindowAndWait();
+    // we recreate the underlying xwindow if this changes
+    int fMSAASampleCount;
 
     typedef SkWindow INHERITED;
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_Win.h b/include/views/SkOSWindow_Win.h
index bd6b4bd..ff289bd 100644
--- a/include/views/SkOSWindow_Win.h
+++ b/include/views/SkOSWindow_Win.h
@@ -12,6 +12,10 @@
 
 #include "SkWindow.h"
 
+#if SK_ANGLE
+#include "EGL/egl.h"
+#endif
+
 class SkOSWindow : public SkWindow {
 public:
     SkOSWindow(void* hwnd);
@@ -22,16 +26,20 @@
     void    updateSize();
 
     static bool PostEvent(SkEvent* evt, SkEventSinkID, SkMSec delay);
-    
-    bool attachGL();
-    void detachGL();
-    void presentGL();
 
-    bool attachD3D9();
-    void detachD3D9();
-    void presentD3D9();
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+#if SK_SUPPORT_GPU
+        kNativeGL_BackEndType,
+#if SK_ANGLE
+        kANGLE_BackEndType,
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+    };
 
-    void* d3d9Device() { return fD3D9Device; }
+    bool attach(SkBackEndTypes attachType, int msaaSampleCount);
+    void detach();
+    void present();
 
     bool wndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
     static bool QuitOnDeactivate(HWND hWnd);
@@ -53,20 +61,35 @@
 
 private:
     void*               fHWND;
-    
+
     void                doPaint(void* ctx);
 
+#if SK_SUPPORT_GPU
     void*               fHGLRC;
-
-    bool                fGLAttached;
-
-    void*               fD3D9Device;
-    bool                fD3D9Attached;
+#if SK_ANGLE
+    EGLDisplay          fDisplay;
+    EGLContext          fContext;
+    EGLSurface          fSurface;
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
 
     HMENU               fMBar;
 
-    typedef SkWindow INHERITED; 
+    SkBackEndTypes      fAttached;
+
+#if SK_SUPPORT_GPU
+    bool attachGL(int msaaSampleCount);
+    void detachGL();
+    void presentGL();
+
+#if SK_ANGLE
+    bool attachANGLE(int msaaSampleCount);
+    void detachANGLE();
+    void presentANGLE();
+#endif // SK_ANGLE
+#endif // SK_SUPPORT_GPU
+
+    typedef SkWindow INHERITED;
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_iOS.h b/include/views/SkOSWindow_iOS.h
index ff28484..1dd3997 100755
--- a/include/views/SkOSWindow_iOS.h
+++ b/include/views/SkOSWindow_iOS.h
@@ -16,11 +16,14 @@
     ~SkOSWindow();
     void*   getHWND() const { return fHWND; }
 
-    virtual bool onDispatchClick(int x, int y, Click::State state, 
-                                 void* owner);
-    void    detachGL();
-    bool    attachGL();
-    void    presentGL();
+    enum SkBackEndTypes {
+        kNone_BackEndType,
+        kNativeGL_BackEndType,
+    };
+
+    void    detach();
+    bool    attach(SkBackEndTypes attachType, int msaaSampleCount);
+    void    present();
 
 protected:
     // overrides from SkEventSink
@@ -29,9 +32,9 @@
     virtual void onHandleInval(const SkIRect&);
     // overrides from SkView
     virtual void onAddMenu(const SkOSMenu*);
-    virtual void onUpdateMenu(const SkOSMenu*);
+    virtual void onUpdateMenu(SkOSMenu*);
     virtual void onSetTitle(const char[]);
-    
+
 private:
     void*   fHWND;
     bool    fInvalEventIsPending;
@@ -40,4 +43,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkOSWindow_wxwidgets.h b/include/views/SkOSWindow_wxwidgets.h
deleted file mode 100644
index a662b40..0000000
--- a/include/views/SkOSWindow_wxwidgets.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SkOSWindow_wxwidgets_DEFINED
-#define SkOSWindow_wxwidgets_DEFINED
-
-#include "SkWindow.h"
-#include "wx/frame.h"
-
-class SkOSWindow: public SkWindow
-{
-public:
-    SkOSWindow();
-    SkOSWindow(const wxString& title, int x, int y, int width, int height);
-    ~SkOSWindow();
-    
-    wxFrame* getWXFrame() const { return fFrame; }
-    
-    void updateSize();
-    
-protected:
-    virtual void onHandleInval(const SkIRect&);
-    virtual void onAddMenu(const SkOSMenu*);
-    
-private:
-    wxFrame* fFrame;
-    typedef SkWindow INHERITED;
-    
-};
-
-#endifpedef SkWindow INHERITED;
diff --git a/include/views/SkProgressBarView.h b/include/views/SkProgressBarView.h
deleted file mode 100644
index 0e39d1e..0000000
--- a/include/views/SkProgressBarView.h
+++ /dev/null
@@ -1,50 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkProgressBarView_DEFINED
-#define SkProgressBarView_DEFINED
-
-#include "SkView.h"
-#include "SkWidgetViews.h"
-#include "SkAnimator.h"
-
-class SkProgressBarView : public SkWidgetView {
-    public:
-        SkProgressBarView();
-        //SkProgressBarView(int max);
-                
-        //inflate: "sk-progress"
-    
-        void reset();   //reset progress to zero
-        void setProgress(int progress);
-        void changeProgress(int diff);
-        void setMax(int max);
-        
-        int getProgress() const { return fProgress; }
-        int getMax() const { return fMax; }
-    
-    protected:
-        //overrides
-        virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
-        virtual void onSizeChange();
-        virtual void onDraw(SkCanvas* canvas);
-        virtual bool onEvent(const SkEvent& evt);
-    
-    private:
-        SkAnimator  fAnim;
-        int         fProgress;
-        int         fMax;
-        
-        typedef SkWidgetView INHERITED;
-};
-
-
-
-
-#endif
diff --git a/include/views/SkScrollBarView.h b/include/views/SkScrollBarView.h
deleted file mode 100644
index 110d0e1..0000000
--- a/include/views/SkScrollBarView.h
+++ /dev/null
@@ -1,45 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkScrollBarView_DEFINED
-#define SkScrollBarView_DEFINED
-
-#include "SkView.h"
-#include "SkWidgetViews.h"
-#include "SkAnimator.h"
-
-class SkScrollBarView : public SkWidgetView {
-public:
-    SkScrollBarView();
-
-    unsigned getStart() const { return fStartPoint; }
-    unsigned getShown() const { return fShownLength; }
-    unsigned getTotal() const { return fTotalLength; }
-
-    void setStart(unsigned start);  
-    void setShown(unsigned shown);
-    void setTotal(unsigned total);
-    
-protected:
-    //overrides
-    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
-    virtual void onSizeChange();
-    virtual void onDraw(SkCanvas* canvas);
-    virtual bool onEvent(const SkEvent& evt);
-
-private:
-    SkAnimator  fAnim;
-    unsigned    fTotalLength, fStartPoint, fShownLength;
-    
-    void adjust();
-    
-    typedef SkWidgetView INHERITED;
-};
-#endif
-
diff --git a/include/views/SkStackViewLayout.h b/include/views/SkStackViewLayout.h
index 705ff74..f2a7cf0 100644
--- a/include/views/SkStackViewLayout.h
+++ b/include/views/SkStackViewLayout.h
@@ -37,7 +37,7 @@
         kStart_Pack,
         kCenter_Pack,
         kEnd_Pack,
-        
+
         kPackCount
     };
     Pack    getPack() const { return (Pack)fPack; }
@@ -86,4 +86,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkSystemEventTypes.h b/include/views/SkSystemEventTypes.h
index f0f2952..bb2b5d5 100644
--- a/include/views/SkSystemEventTypes.h
+++ b/include/views/SkSystemEventTypes.h
@@ -17,7 +17,7 @@
 */
 #define SK_EventType_Delay      "\xd" "lay"
 #define SK_EventType_Inval      "nv" "\xa" "l"
-#define SK_EventType_Key        "key" "\x1" 
+#define SK_EventType_Key        "key" "\x1"
 #define SK_EventType_OnEnd "on" "\xe" "n"
 #define SK_EventType_Unichar    "\xc" "har"
 #define SK_EventType_KeyUp      "key" "\xf"
diff --git a/include/views/SkTextBox.h b/include/views/SkTextBox.h
new file mode 100644
index 0000000..e217076
--- /dev/null
+++ b/include/views/SkTextBox.h
@@ -0,0 +1,77 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkTextBox_DEFINED
+#define SkTextBox_DEFINED
+
+#include "SkCanvas.h"
+
+/** \class SkTextBox
+
+    SkTextBox is a helper class for drawing 1 or more lines of text
+    within a rectangle. The textbox is positioned and clipped by its Frame.
+    The Margin rectangle controls where the text is drawn relative to
+    the Frame. Line-breaks occur inside the Margin rectangle.
+
+    Spacing is a linear equation used to compute the distance between lines
+    of text. Spacing consists of two scalars: mul and add, and the spacing
+    between lines is computed as: spacing = paint.getTextSize() * mul + add
+*/
+class SkTextBox {
+public:
+    SkTextBox();
+
+    enum Mode {
+        kOneLine_Mode,
+        kLineBreak_Mode,
+
+        kModeCount
+    };
+    Mode    getMode() const { return (Mode)fMode; }
+    void    setMode(Mode);
+
+    enum SpacingAlign {
+        kStart_SpacingAlign,
+        kCenter_SpacingAlign,
+        kEnd_SpacingAlign,
+
+        kSpacingAlignCount
+    };
+    SpacingAlign    getSpacingAlign() const { return (SpacingAlign)fSpacingAlign; }
+    void            setSpacingAlign(SpacingAlign);
+
+    void    getBox(SkRect*) const;
+    void    setBox(const SkRect&);
+    void    setBox(SkScalar left, SkScalar top, SkScalar right, SkScalar bottom);
+
+    void    getSpacing(SkScalar* mul, SkScalar* add) const;
+    void    setSpacing(SkScalar mul, SkScalar add);
+
+    void    draw(SkCanvas*, const char text[], size_t len, const SkPaint&);
+
+    void    setText(const char text[], size_t len, const SkPaint&);
+    void    draw(SkCanvas*);
+    int     countLines() const;
+    SkScalar getTextHeight() const;
+
+private:
+    SkRect      fBox;
+    SkScalar    fSpacingMul, fSpacingAdd;
+    uint8_t     fMode, fSpacingAlign;
+    const char* fText;
+    size_t      fLen;
+    const SkPaint* fPaint;
+};
+
+class SkTextLineBreaker {
+public:
+    static int CountLines(const char text[], size_t len, const SkPaint&, SkScalar width);
+};
+
+#endif
diff --git a/include/views/SkTouchGesture.h b/include/views/SkTouchGesture.h
index 527065e..3a0cfce 100644
--- a/include/views/SkTouchGesture.h
+++ b/include/views/SkTouchGesture.h
@@ -13,13 +13,13 @@
 
 struct SkFlingState {
     SkFlingState() : fActive(false) {}
-    
+
     bool isActive() const { return fActive; }
     void stop() { fActive = false; }
-    
+
     void reset(float sx, float sy);
     bool evaluateMatrix(SkMatrix* matrix);
-    
+
 private:
     SkPoint     fDirection;
     SkScalar    fSpeed0;
@@ -75,5 +75,3 @@
 };
 
 #endif
-
-
diff --git a/include/views/SkView.h b/include/views/SkView.h
index a5349c2..786d363 100644
--- a/include/views/SkView.h
+++ b/include/views/SkView.h
@@ -15,6 +15,7 @@
 #include "SkDOM.h"
 #include "SkTDict.h"
 #include "SkMatrix.h"
+#include "SkMetaData.h"
 
 class SkCanvas;
 class SkLayerView;
@@ -82,9 +83,9 @@
     void        getLocalBounds(SkRect* bounds) const;
 
     /** Loc - the view's offset with respect to its parent in its view hiearchy.
-        NOTE: For more complex transforms, use Local Matrix. The tranformations 
+        NOTE: For more complex transforms, use Local Matrix. The tranformations
         are applied in the following order:
-             canvas->translate(fLoc.fX, fLoc.fY);		
+             canvas->translate(fLoc.fX, fLoc.fY);
              canvas->concat(fMatrix);
     */
     /** Return the view's left edge */
@@ -96,13 +97,13 @@
     void        setLoc(const SkPoint& loc) { this->setLoc(loc.fX, loc.fY); }
     void        setLocX(SkScalar x) { this->setLoc(x, fLoc.fY); }
     void        setLocY(SkScalar y) { this->setLoc(fLoc.fX, y); }
-    
-    /** Local Matrix - matrix used to tranform the view with respect to its 
-        parent in its view hiearchy. Use setLocalMatrix to apply matrix 
+
+    /** Local Matrix - matrix used to tranform the view with respect to its
+        parent in its view hiearchy. Use setLocalMatrix to apply matrix
         transformations to the current view and in turn affect its children.
         NOTE: For simple offsets, use Loc. The transformations are applied in
         the following order:
-             canvas->translate(fLoc.fX, fLoc.fY);		
+             canvas->translate(fLoc.fX, fLoc.fY);
              canvas->concat(fMatrix);
     */
     const SkMatrix& getLocalMatrix() const { return fMatrix; }
@@ -155,6 +156,9 @@
         SkIPoint    fIOrig, fIPrev, fICurr;
         State       fState;
         void*       fOwner;
+        unsigned    fModifierKeys;
+
+        SkMetaData  fMeta;
     private:
         SkEventSinkID   fTargetID;
         char*           fType;
@@ -164,11 +168,11 @@
 
         friend class SkView;
     };
-    Click*  findClickHandler(SkScalar x, SkScalar y);
+    Click*  findClickHandler(SkScalar x, SkScalar y, unsigned modifierKeys);
 
-    static void DoClickDown(Click*, int x, int y);
-    static void DoClickMoved(Click*, int x, int y);
-    static void DoClickUp(Click*, int x, int y);
+    static void DoClickDown(Click*, int x, int y, unsigned modi);
+    static void DoClickMoved(Click*, int x, int y, unsigned modi);
+    static void DoClickUp(Click*, int x, int y, unsigned modi);
 
     /** Send the event to the view's parent, and its parent etc. until one of them
         returns true from its onEvent call. This view is returned. If no parent handles
@@ -203,15 +207,21 @@
     void        detachAllChildren();
 
     /** Convert the specified point from global coordinates into view-local coordinates
-    */
-    void        globalToLocal(SkPoint* pt) const { if (pt) this->globalToLocal(pt->fX, pt->fY, pt); }
+     *  Return true on success; false on failure
+     */
+    bool        globalToLocal(SkPoint* pt) const {
+        if (NULL != pt) {
+            return this->globalToLocal(pt->fX, pt->fY, pt);
+        }
+        return true;  // nothing to do so return true
+    }
     /** Convert the specified x,y from global coordinates into view-local coordinates, returning
         the answer in the local parameter.
     */
-    void        globalToLocal(SkScalar globalX, SkScalar globalY, SkPoint* local) const;
+    bool        globalToLocal(SkScalar globalX, SkScalar globalY, SkPoint* local) const;
 
     /** \class F2BIter
-    
+
         Iterator that will return each of this view's children, in
         front-to-back order (the order used for clicking). The first
         call to next() returns the front-most child view. When
@@ -226,7 +236,7 @@
     };
 
     /** \class B2FIter
-    
+
         Iterator that will return each of this view's children, in
         back-to-front order (the order they are drawn). The first
         call to next() returns the back-most child view. When
@@ -241,18 +251,22 @@
     };
 
     /** \class Artist
-    
+
         Install a subclass of this in a view (calling setArtist()), and then the
         default implementation of that view's onDraw() will invoke this object
         automatically.
     */
     class Artist : public SkRefCnt {
     public:
+        SK_DECLARE_INST_COUNT(Artist)
+
         void draw(SkView*, SkCanvas*);
         void inflate(const SkDOM&, const SkDOM::Node*);
     protected:
         virtual void onDraw(SkView*, SkCanvas*) = 0;
         virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+    private:
+        typedef SkRefCnt INHERITED;
     };
     /** Return the artist attached to this view (or null). The artist's reference
         count is not affected.
@@ -265,18 +279,22 @@
     Artist* setArtist(Artist* artist);
 
     /** \class Layout
-    
+
         Install a subclass of this in a view (calling setLayout()), and then the
         default implementation of that view's onLayoutChildren() will invoke
         this object automatically.
     */
     class Layout : public SkRefCnt {
     public:
+        SK_DECLARE_INST_COUNT(Layout)
+
         void layoutChildren(SkView* parent);
         void inflate(const SkDOM&, const SkDOM::Node*);
     protected:
         virtual void onLayoutChildren(SkView* parent) = 0;
         virtual void onInflate(const SkDOM&, const SkDOM::Node*);
+    private:
+        typedef SkRefCnt INHERITED;
     };
 
     /** Return the layout attached to this view (or null). The layout's reference
@@ -328,13 +346,13 @@
 
     /** Override this if you might handle the click
     */
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
+    virtual Click* onFindClickHandler(SkScalar x, SkScalar y, unsigned modi);
     /** Override this to decide if your children are targets for a click.
         The default returns true, in which case your children views will be
         candidates for onFindClickHandler. Returning false wil skip the children
         and just call your onFindClickHandler.
      */
-    virtual bool onSendClickToChildren(SkScalar x, SkScalar y);
+    virtual bool onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi);
     /** Override this to track clicks, returning true as long as you want to track
         the pen/mouse.
     */
@@ -368,7 +386,7 @@
 
     friend class B2FIter;
     friend class F2BIter;
-    
+
     friend class SkLayerView;
 
     bool    setFocusView(SkView* fvOrNull);
@@ -379,4 +397,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkViewInflate.h b/include/views/SkViewInflate.h
index 7282091..db3689a 100644
--- a/include/views/SkViewInflate.h
+++ b/include/views/SkViewInflate.h
@@ -17,7 +17,7 @@
 class SkView;
 
 class SkViewInflate {
-public: 
+public:
             SkViewInflate();
     virtual ~SkViewInflate();
 
@@ -33,7 +33,7 @@
         if no match is found.
     */
     SkView* findViewByID(const char id[]) const;
-    
+
     SkDEBUGCODE(void dump() const;)
 
 protected:
@@ -69,4 +69,3 @@
 };
 
 #endif
-
diff --git a/include/views/SkWidget.h b/include/views/SkWidget.h
index c3b4530..3b68e7b 100644
--- a/include/views/SkWidget.h
+++ b/include/views/SkWidget.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,18 +5,22 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkWidget_DEFINED
 #define SkWidget_DEFINED
 
-#include "SkView.h"
 #include "SkBitmap.h"
 #include "SkDOM.h"
 #include "SkPaint.h"
 #include "SkString.h"
 #include "SkTDArray.h"
+#include "SkTextBox.h"
+#include "SkView.h"
 
-//////////////////////////////////////////////////////////////////////////////
+class SkEvent;
+class SkInterpolator;
+class SkShader;
+
+////////////////////////////////////////////////////////////////////////////////
 
 class SkWidget : public SkView {
 public:
@@ -41,6 +44,8 @@
     typedef SkView INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkHasLabelWidget : public SkWidget {
 public:
     SkHasLabelWidget(uint32_t flags = 0) : SkWidget(flags) {}
@@ -63,6 +68,8 @@
     typedef SkWidget INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkButtonWidget : public SkHasLabelWidget {
 public:
     SkButtonWidget(uint32_t flags = 0) : SkHasLabelWidget(flags), fState(kOff_State) {}
@@ -87,6 +94,8 @@
     typedef SkHasLabelWidget INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkPushButtonWidget : public SkButtonWidget {
 public:
     SkPushButtonWidget(uint32_t flags = 0) : SkButtonWidget(flags) {}
@@ -101,6 +110,8 @@
     typedef SkButtonWidget INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkCheckBoxWidget : public SkButtonWidget {
 public:
     SkCheckBoxWidget(uint32_t flags = 0);
@@ -114,7 +125,7 @@
     typedef SkButtonWidget INHERITED;
 };
 
-#include "SkTextBox.h"
+////////////////////////////////////////////////////////////////////////////////
 
 class SkStaticTextView : public SkView {
 public:
@@ -163,6 +174,8 @@
     typedef SkView INHERITED;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkBitmapView : public SkView {
 public:
             SkBitmapView(uint32_t flags = 0);
@@ -181,41 +194,7 @@
     typedef SkView INHERITED;
 };
 
-/////////////////////////////////////////////////////////////////////////////
-
-class SkShader;
-class SkInterpolator;
-
-class SkWidgetView : public SkView {
-public:
-            SkWidgetView(uint32_t flags = 0);
-    virtual ~SkWidgetView();
-
-    static const char*  GetEventType();
-};
-
-class SkSliderView : public SkWidgetView {
-public:
-    SkSliderView(uint32_t flags = 0);
-
-    uint16_t    getValue() const { return fValue; }
-    uint16_t    getMax() const { return fMax; }
-
-    void    setMax(U16CPU max);
-    void    setValue(U16CPU value);
-
-protected:
-    virtual void    onDraw(SkCanvas*);
-    virtual Click*  onFindClickHandler(SkScalar x, SkScalar y);
-    virtual bool    onClick(Click*);
-
-private:
-    uint16_t fValue, fMax;
-
-    typedef SkWidgetView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
 
 class SkHasLabelView : public SkView {
 public:
@@ -233,6 +212,8 @@
     virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkPushButtonView : public SkHasLabelView {
 public:
     SkPushButtonView(uint32_t flags = 0);
@@ -242,6 +223,8 @@
     virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkCheckBoxView : public SkHasLabelView {
 public:
     SkCheckBoxView(uint32_t flags = 0);
@@ -262,6 +245,8 @@
     State   fState;
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
 class SkProgressView : public SkView {
 public:
     SkProgressView(uint32_t flags = 0);
@@ -286,50 +271,7 @@
     typedef SkView INHERITED;
 };
 
-class SkTextView : public SkView {
-public:
-            SkTextView(uint32_t flags = 0);
-    virtual ~SkTextView();
-
-    enum AnimaDir {
-        kNeutral_AnimDir,
-        kForward_AnimDir,
-        kBackward_AnimDir,
-        kAnimDirCount
-    };
-
-    void    getText(SkString*) const;
-    void    setText(const SkString&, AnimaDir dir = kNeutral_AnimDir);
-    void    setText(const char text[], AnimaDir dir = kNeutral_AnimDir);
-    void    setText(const char text[], size_t len, AnimaDir dir = kNeutral_AnimDir);
-
-    void    getMargin(SkPoint* margin) const;
-    void    setMargin(const SkPoint&);
-
-    SkPaint&    paint() { return fPaint; }
-
-protected:
-    virtual void onDraw(SkCanvas*);
-    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
-
-private:
-    SkString fText;
-    SkPaint  fPaint;
-    SkPoint  fMargin;
-
-    class Interp;
-    Interp* fInterp;
-    bool    fDoInterp;
-    // called by the other setText methods. This guy does not check for !=
-    // before doing the assign, so the caller must check for us
-    void privSetText(const SkString&, AnimaDir dir);
-
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////
-
-class SkEvent;
+////////////////////////////////////////////////////////////////////////////////
 
 class SkListSource : public SkEventSink {
 public:
@@ -342,7 +284,9 @@
     static SkListSource* CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node);
 };
 
-class SkListView : public SkWidgetView {
+////////////////////////////////////////////////////////////////////////////////
+
+class SkListView : public SkView {
 public:
             SkListView(uint32_t flags = 0);
     virtual ~SkListView();
@@ -409,12 +353,12 @@
     bool    getRowRect(int index, SkRect*) const;
     void    ensureSelectionIsVisible();
 
-    typedef SkWidgetView INHERITED;
+    typedef SkView INHERITED;
 };
 
-//////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
 
-class SkGridView : public SkWidgetView {
+class SkGridView : public SkView {
 public:
             SkGridView(uint32_t flags = 0);
     virtual ~SkGridView();
@@ -462,8 +406,7 @@
     bool    getCellRect(int index, SkRect*) const;
     void    ensureSelectionIsVisible();
 
-    typedef SkWidgetView INHERITED;
+    typedef SkView INHERITED;
 };
 
 #endif
-
diff --git a/include/views/SkWidgetViews.h b/include/views/SkWidgetViews.h
deleted file mode 100644
index 295c101..0000000
--- a/include/views/SkWidgetViews.h
+++ /dev/null
@@ -1,305 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkWidgetViews_DEFINED
-#define SkWidgetViews_DEFINED
-
-#include "SkView.h"
-
-
-enum SkWidgetEnum {
-    kBorder_WidgetEnum,         //!< <sk-border>
-    kButton_WidgetEnum,         //!< <sk-button>
-    kImage_WidgetEnum,          //!< <sk-image>
-    kList_WidgetEnum,           //!< <sk-list>
-    kProgress_WidgetEnum,       //!< <sk-progress>
-    kScroll_WidgetEnum,         //!< <sk-scroll>
-    kText_WidgetEnum,           //!< <sk-text>
-    
-    kWidgetEnumCount
-};
-
-//determines which skin to use
-enum SkinEnum {
-    kBorder_SkinEnum,
-    kButton_SkinEnum,
-    kProgress_SkinEnum,
-    kScroll_SkinEnum,
-    kStaticText_SkinEnum,
-    
-    kSkinEnumCount
-};
-
-#include "SkAnimator.h"
-//used for inflates
-const char* get_skin_enum_path(SkinEnum se);
-void init_skin_anim(const char path[], SkAnimator* anim);
-void init_skin_anim(SkinEnum se, SkAnimator* anim);
-void init_skin_paint(SkinEnum se, SkPaint* paint);
-void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint);
-
-/** Given an enum value, return an instance of the specified widget.
-    If the enum is out of range, returns null
-*/
-SkView* SkWidgetFactory(SkWidgetEnum);
-/** Given the inflate/element name of a widget, return an instance of
-    the specified widget, or null if name does not match any known
-    widget type.
-*/
-SkView* SkWidgetFactory(const char name[]);
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-
-class SkWidgetView : public SkView {
-public:
-    SkWidgetView();
-
-    const char* getLabel() const;
-    void        getLabel(SkString* label) const;
-
-    void        setLabel(const char[]);
-    void        setLabel(const char[], size_t len);
-    void        setLabel(const SkString&);
-
-    SkEvent&        event() { return fEvent; }
-    const SkEvent&  event() const { return fEvent; }
-
-    /** Returns true if the widget can post its event to its listeners.
-    */
-    bool    postWidgetEvent();
-    
-    /** Returns the sinkID of the widgetview that posted the event, or 0
-    */
-    static SkEventSinkID GetWidgetEventSinkID(const SkEvent&);
-
-protected:
-    /** called when the label changes. override in subclasses. default action invals the view's bounds.
-        called with the old and new labels, before the label has actually changed.
-    */
-    virtual void onLabelChange(const char oldLabel[], const char newLabel[]);
-    /** called before posting the event to our listeners. Override to add slots to the event
-        before posting. Return true to proceed with posting, or false to not post the event to any
-        listener. Note: the event passed in may not be the same as calling this->event().
-        Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot
-        at modifying the event (and possibly returning false to abort).
-    */
-    virtual bool onPrepareWidgetEvent(SkEvent* evt);
-
-    // overrides
-    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
-    
-private:
-    SkString    fLabel;
-    SkEvent     fEvent;
-    
-    typedef SkView INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-
-class SkButtonView : public SkWidgetView {
-public:
-    // inflate: "sk-button"
-    
-protected:
-    // overrides
-    virtual bool onEvent(const SkEvent&);
-private:
-    typedef SkWidgetView INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-
-class SkCheckButtonView : public SkWidgetView {
-public:
-    SkCheckButtonView();
-
-    // inflate: "sk-checkbutton"
-    
-    enum CheckState {
-        kOff_CheckState,        //!< inflate: check-state="off"
-        kOn_CheckState,         //!< inflate: check-state="on"
-        kUnknown_CheckState     //!< inflate: check-state="unknown"
-    };
-    CheckState  getCheckState() const { return (CheckState)fCheckState; }
-    void        setCheckState(CheckState);
-
-    /** use this to extract the CheckState from an event (i.e. one that as posted
-        by a SkCheckButtonView). Returns true if the proper slot was present in the event,
-        and sets state to that value. If no proper slot is found, returns false and does not
-        modify state.
-    */
-    static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state);
-
-protected:
-    // called when the check-state is about to change, but before it actually has
-    virtual void onCheckStateChange(CheckState oldState, CheckState newState);
-
-    // overrides
-    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
-    virtual bool onPrepareWidgetEvent(SkEvent* evt);
-    
-private:
-    uint8_t  fCheckState;
-    
-    typedef SkWidgetView INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-#include "SkTextBox.h"
-
-class SkStaticTextView : public SkView {
-public:
-            SkStaticTextView();
-    virtual ~SkStaticTextView();
-
-    enum Mode {
-        kFixedSize_Mode,
-        kAutoWidth_Mode,
-        kAutoHeight_Mode,
-
-        kModeCount
-    };
-    Mode    getMode() const { return (Mode)fMode; }
-    void    setMode(Mode);
-
-    SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; }
-    void    setSpacingAlign(SkTextBox::SpacingAlign);
-
-    void    getMargin(SkPoint* margin) const;
-    void    setMargin(SkScalar dx, SkScalar dy);
-
-    size_t  getText(SkString* text = NULL) const;
-    size_t  getText(char text[] = NULL) const;
-    void    setText(const SkString&);
-    void    setText(const char text[]);
-    void    setText(const char text[], size_t len);
-
-    void    getPaint(SkPaint*) const;
-    void    setPaint(const SkPaint&);
-
-protected:
-    // overrides
-    virtual void onDraw(SkCanvas*);
-    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
-
-private:
-    SkPoint     fMargin;
-    SkString    fText;
-    SkPaint     fPaint;
-    uint8_t     fMode;
-    uint8_t     fSpacingAlign;
-
-    void computeSize();
-
-    typedef SkView INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////////
-
-class SkAnimator;
-class SkListSource;
-class SkScrollBarView;
-
-class SkListView : public SkWidgetView {
-public:
-            SkListView();
-    virtual ~SkListView();
-
-    bool    hasScrollBar() const { return fScrollBar != NULL; }
-    void    setHasScrollBar(bool);
-    
-    /** Return the number of visible rows
-    */
-    int     getVisibleRowCount() const { return fVisibleRowCount; }
-    /** Return the index of the selected row, or -1 if none
-    */
-    int     getSelection() const { return fCurrIndex; }
-    /** Set the index of the selected row, or -1 for none
-    */
-    void    setSelection(int);
-    /** If possible, move the selection up and return true,
-        else do nothing and return false
-        If nothing is selected, select the last item (unless there are no items).
-    */
-    bool    moveSelectionUp();
-    /** If possible, move the selection down and return true,
-        else do nothing and return false.
-        If nothing is selected, select the first item (unless there are no items).
-    */
-    bool    moveSelectionDown();
-
-    SkListSource*   getListSource() const { return fSource; }
-    SkListSource*   setListSource(SkListSource*);
-
-    /** Call this in your event handler. If the specified event is from a SkListView,
-        then it returns the index of the selected item in this list, otherwise it
-        returns -1
-    */
-    static int GetWidgetEventListIndex(const SkEvent&);
-
-protected:
-    // overrides
-    virtual void onDraw(SkCanvas*);
-    virtual void onSizeChange();
-    virtual bool onEvent(const SkEvent&);
-    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
-    virtual bool onPrepareWidgetEvent(SkEvent*);
-
-private:
-    enum DirtyFlags {
-        kAnimCount_DirtyFlag    = 0x01,
-        kAnimContent_DirtyFlag  = 0x02
-    };
-    void    dirtyCache(unsigned dirtyFlags);
-    bool    ensureCache();
-
-    int     logicalToVisualIndex(int index) const { return index - fScrollIndex; }
-    void    invalSelection();
-    SkScalar getContentWidth() const;
-    bool    getRowRect(int index, SkRect*) const;
-    void    ensureSelectionIsVisible();
-    void    ensureVisibleRowCount();
-
-    struct BindingRec;
-
-    enum Heights {
-        kNormal_Height,
-        kSelected_Height
-    };
-    SkListSource*   fSource;
-    SkScrollBarView*    fScrollBar;
-    SkAnimator*     fAnims;
-    BindingRec*     fBindings;
-    SkString        fSkinName;
-    SkScalar        fHeights[2];
-    int16_t         fScrollIndex, fCurrIndex;
-    uint16_t        fVisibleRowCount, fBindingCount;
-    SkBool8         fAnimContentDirty;
-    SkBool8         fAnimFocusDirty;
-
-    typedef SkWidgetView INHERITED;
-};
-
-class SkListSource : public SkRefCnt {
-public:
-    virtual int countFields();
-    virtual void getFieldName(int index, SkString* field);
-    /** Return the index of the named field, or -1 if not found */
-    virtual int findFieldIndex(const char field[]);
-
-    virtual int countRecords();
-    virtual void getRecord(int rowIndex, int fieldIndex, SkString* data);
-
-    virtual bool prepareWidgetEvent(SkEvent*, int rowIndex);
-    
-    static SkListSource* Factory(const char name[]);
-};
-
-#endif
diff --git a/include/views/SkWindow.h b/include/views/SkWindow.h
index c72163c..ca68e75 100644
--- a/include/views/SkWindow.h
+++ b/include/views/SkWindow.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #ifndef SkWindow_DEFINED
 #define SkWindow_DEFINED
 
@@ -40,21 +38,21 @@
     void    eraseRGB(U8CPU r, U8CPU g, U8CPU b);
 
     bool    isDirty() const { return !fDirtyRgn.isEmpty(); }
-    bool    update(SkIRect* updateArea, SkCanvas* = NULL);
+    bool    update(SkIRect* updateArea);
     // does not call through to onHandleInval(), but does force the fDirtyRgn
     // to be wide open. Call before update() to ensure we redraw everything.
     void    forceInvalAll();
     // return the bounds of the dirty/inval rgn, or [0,0,0,0] if none
     const SkIRect& getDirtyBounds() const { return fDirtyRgn.getBounds(); }
 
-    bool    handleClick(int x, int y, Click::State, void* owner = NULL);
+    bool    handleClick(int x, int y, Click::State, void* owner, unsigned modi = 0);
     bool    handleChar(SkUnichar);
     bool    handleKey(SkKey);
     bool    handleKeyUp(SkKey);
 
     void    addMenu(SkOSMenu*);
     const SkTDArray<SkOSMenu*>* getMenus() { return &fMenus; }
-    
+
     const char* getTitle() const { return fTitle.c_str(); }
     void    setTitle(const char title[]);
 
@@ -63,11 +61,13 @@
     void    preConcat(const SkMatrix&);
     void    postConcat(const SkMatrix&);
 
+    virtual SkCanvas* createCanvas();
+
     virtual void onPDFSaved(const char title[], const char desc[],
         const char path[]) {}
 protected:
     virtual bool onEvent(const SkEvent&);
-    virtual bool onDispatchClick(int x, int y, Click::State, void* owner);
+    virtual bool onDispatchClick(int x, int y, Click::State, void* owner, unsigned modi);
     // called if part of our bitmap is invalidated
     virtual void onHandleInval(const SkIRect&);
     virtual bool onHandleChar(SkUnichar);
@@ -93,17 +93,17 @@
 
     SkView* fFocusView;
     bool    fWaitingOnInval;
-    
+
     SkString    fTitle;
     SkMatrix    fMatrix;
 
     typedef SkView INHERITED;
 };
 
-///////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////////////
 
-#ifdef SK_USE_WXWIDGETS
-    #include "SkOSWindow_wxwidgets.h"
+#if defined(SK_BUILD_FOR_NACL)
+    #include "SkOSWindow_NaCl.h"
 #elif defined(SK_BUILD_FOR_MAC)
     #include "SkOSWindow_Mac.h"
 #elif defined(SK_BUILD_FOR_WIN)
@@ -119,4 +119,3 @@
 #endif
 
 #endif
-
diff --git a/include/utils/android/AndroidKeyToSkKey.h b/include/views/android/AndroidKeyToSkKey.h
similarity index 100%
rename from include/utils/android/AndroidKeyToSkKey.h
rename to include/views/android/AndroidKeyToSkKey.h
diff --git a/include/views/animated/SkBorderView.h b/include/views/animated/SkBorderView.h
new file mode 100644
index 0000000..8b1e537
--- /dev/null
+++ b/include/views/animated/SkBorderView.h
@@ -0,0 +1,40 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkBorderView_DEFINED
+#define SkBorderView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkBorderView : public SkWidgetView {
+public:
+    SkBorderView();
+    ~SkBorderView();
+    void setSkin(const char skin[]);
+    SkScalar getLeft() const { return fLeft; }
+    SkScalar getRight() const { return fRight; }
+    SkScalar getTop() const { return fTop; }
+    SkScalar getBottom() const { return fBottom; }
+protected:
+    //overrides
+    virtual void onInflate(const SkDOM& dom,  const SkDOM::Node* node);
+    virtual void onSizeChange();
+    virtual void onDraw(SkCanvas* canvas);
+    virtual bool onEvent(const SkEvent& evt);
+private:
+    SkAnimator fAnim;
+    SkScalar fLeft, fRight, fTop, fBottom;  //margin on each side
+    SkRect fMargin;
+
+    typedef SkWidgetView INHERITED;
+};
+
+#endif
diff --git a/include/views/animated/SkImageView.h b/include/views/animated/SkImageView.h
new file mode 100644
index 0000000..a21da0b
--- /dev/null
+++ b/include/views/animated/SkImageView.h
@@ -0,0 +1,68 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkImageView_DEFINED
+#define SkImageView_DEFINED
+
+#include "SkView.h"
+#include "SkString.h"
+
+class SkAnimator;
+class SkBitmap;
+class SkMatrix;
+
+class SkImageView : public SkView {
+public:
+            SkImageView();
+    virtual ~SkImageView();
+
+    void    getUri(SkString*) const;
+    void    setUri(const char []);
+    void    setUri(const SkString&);
+
+
+    enum ScaleType {
+        kMatrix_ScaleType,
+        kFitXY_ScaleType,
+        kFitStart_ScaleType,
+        kFitCenter_ScaleType,
+        kFitEnd_ScaleType
+    };
+    ScaleType   getScaleType() const { return (ScaleType)fScaleType; }
+    void        setScaleType(ScaleType);
+
+    bool    getImageMatrix(SkMatrix*) const;
+    void    setImageMatrix(const SkMatrix*);
+
+protected:
+    // overrides
+    virtual bool    onEvent(const SkEvent&);
+    virtual void    onDraw(SkCanvas*);
+    virtual void    onInflate(const SkDOM&, const SkDOMNode*);
+
+private:
+    SkString    fUri;
+    SkMatrix*   fMatrix;    // null or copy of caller's matrix ,,,,,
+    union {
+        SkAnimator* fAnim;
+        SkBitmap* fBitmap;
+    } fData;
+    uint8_t     fScaleType;
+    SkBool8     fDataIsAnim;    // as opposed to bitmap
+    SkBool8     fUriIsValid;
+
+    void    onUriChange();
+    bool    getDataBounds(SkRect* bounds);
+    bool    freeData();
+    bool    ensureUriIsLoaded();
+
+    typedef SkView INHERITED;
+};
+
+#endif
diff --git a/include/views/animated/SkProgressBarView.h b/include/views/animated/SkProgressBarView.h
new file mode 100644
index 0000000..7e670a9
--- /dev/null
+++ b/include/views/animated/SkProgressBarView.h
@@ -0,0 +1,50 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkProgressBarView_DEFINED
+#define SkProgressBarView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkProgressBarView : public SkWidgetView {
+    public:
+        SkProgressBarView();
+        //SkProgressBarView(int max);
+
+        //inflate: "sk-progress"
+
+        void reset();   //reset progress to zero
+        void setProgress(int progress);
+        void changeProgress(int diff);
+        void setMax(int max);
+
+        int getProgress() const { return fProgress; }
+        int getMax() const { return fMax; }
+
+    protected:
+        //overrides
+        virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+        virtual void onSizeChange();
+        virtual void onDraw(SkCanvas* canvas);
+        virtual bool onEvent(const SkEvent& evt);
+
+    private:
+        SkAnimator  fAnim;
+        int         fProgress;
+        int         fMax;
+
+        typedef SkWidgetView INHERITED;
+};
+
+
+
+
+#endif
diff --git a/include/views/animated/SkScrollBarView.h b/include/views/animated/SkScrollBarView.h
new file mode 100644
index 0000000..05042f0
--- /dev/null
+++ b/include/views/animated/SkScrollBarView.h
@@ -0,0 +1,44 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkScrollBarView_DEFINED
+#define SkScrollBarView_DEFINED
+
+#include "SkView.h"
+#include "SkWidgetViews.h"
+#include "SkAnimator.h"
+
+class SkScrollBarView : public SkWidgetView {
+public:
+    SkScrollBarView();
+
+    unsigned getStart() const { return fStartPoint; }
+    unsigned getShown() const { return fShownLength; }
+    unsigned getTotal() const { return fTotalLength; }
+
+    void setStart(unsigned start);
+    void setShown(unsigned shown);
+    void setTotal(unsigned total);
+
+protected:
+    //overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+    virtual void onSizeChange();
+    virtual void onDraw(SkCanvas* canvas);
+    virtual bool onEvent(const SkEvent& evt);
+
+private:
+    SkAnimator  fAnim;
+    unsigned    fTotalLength, fStartPoint, fShownLength;
+
+    void adjust();
+
+    typedef SkWidgetView INHERITED;
+};
+#endif
diff --git a/include/views/animated/SkWidgetViews.h b/include/views/animated/SkWidgetViews.h
new file mode 100644
index 0000000..4034660
--- /dev/null
+++ b/include/views/animated/SkWidgetViews.h
@@ -0,0 +1,309 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkWidgetViews_DEFINED
+#define SkWidgetViews_DEFINED
+
+#include "SkView.h"
+
+
+enum SkWidgetEnum {
+    kBorder_WidgetEnum,         //!< <sk-border>
+    kButton_WidgetEnum,         //!< <sk-button>
+    kImage_WidgetEnum,          //!< <sk-image>
+    kList_WidgetEnum,           //!< <sk-list>
+    kProgress_WidgetEnum,       //!< <sk-progress>
+    kScroll_WidgetEnum,         //!< <sk-scroll>
+    kText_WidgetEnum,           //!< <sk-text>
+
+    kWidgetEnumCount
+};
+
+//determines which skin to use
+enum SkinEnum {
+    kBorder_SkinEnum,
+    kButton_SkinEnum,
+    kProgress_SkinEnum,
+    kScroll_SkinEnum,
+    kStaticText_SkinEnum,
+
+    kSkinEnumCount
+};
+
+#include "SkAnimator.h"
+//used for inflates
+const char* get_skin_enum_path(SkinEnum se);
+void init_skin_anim(const char path[], SkAnimator* anim);
+void init_skin_anim(SkinEnum se, SkAnimator* anim);
+void init_skin_paint(SkinEnum se, SkPaint* paint);
+void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint);
+
+/** Given an enum value, return an instance of the specified widget.
+    If the enum is out of range, returns null
+*/
+SkView* SkWidgetFactory(SkWidgetEnum);
+/** Given the inflate/element name of a widget, return an instance of
+    the specified widget, or null if name does not match any known
+    widget type.
+*/
+SkView* SkWidgetFactory(const char name[]);
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkWidgetView : public SkView {
+public:
+    SkWidgetView();
+
+    const char* getLabel() const;
+    void        getLabel(SkString* label) const;
+
+    void        setLabel(const char[]);
+    void        setLabel(const char[], size_t len);
+    void        setLabel(const SkString&);
+
+    SkEvent&        event() { return fEvent; }
+    const SkEvent&  event() const { return fEvent; }
+
+    /** Returns true if the widget can post its event to its listeners.
+    */
+    bool    postWidgetEvent();
+
+    /** Returns the sinkID of the widgetview that posted the event, or 0
+    */
+    static SkEventSinkID GetWidgetEventSinkID(const SkEvent&);
+
+protected:
+    /** called when the label changes. override in subclasses. default action invals the view's bounds.
+        called with the old and new labels, before the label has actually changed.
+    */
+    virtual void onLabelChange(const char oldLabel[], const char newLabel[]);
+    /** called before posting the event to our listeners. Override to add slots to the event
+        before posting. Return true to proceed with posting, or false to not post the event to any
+        listener. Note: the event passed in may not be the same as calling this->event().
+        Be sure to call your INHERITED method as well, so that all classes in the hierarchy get a shot
+        at modifying the event (and possibly returning false to abort).
+    */
+    virtual bool onPrepareWidgetEvent(SkEvent* evt);
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    SkString    fLabel;
+    SkEvent     fEvent;
+
+    typedef SkView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkButtonView : public SkWidgetView {
+public:
+    // inflate: "sk-button"
+
+protected:
+    // overrides
+    virtual bool onEvent(const SkEvent&);
+private:
+    typedef SkWidgetView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkCheckButtonView : public SkWidgetView {
+public:
+    SkCheckButtonView();
+
+    // inflate: "sk-checkbutton"
+
+    enum CheckState {
+        kOff_CheckState,        //!< inflate: check-state="off"
+        kOn_CheckState,         //!< inflate: check-state="on"
+        kUnknown_CheckState     //!< inflate: check-state="unknown"
+    };
+    CheckState  getCheckState() const { return (CheckState)fCheckState; }
+    void        setCheckState(CheckState);
+
+    /** use this to extract the CheckState from an event (i.e. one that as posted
+        by a SkCheckButtonView). Returns true if the proper slot was present in the event,
+        and sets state to that value. If no proper slot is found, returns false and does not
+        modify state.
+    */
+    static bool GetWidgetEventCheckState(const SkEvent&, CheckState* state);
+
+protected:
+    // called when the check-state is about to change, but before it actually has
+    virtual void onCheckStateChange(CheckState oldState, CheckState newState);
+
+    // overrides
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+    virtual bool onPrepareWidgetEvent(SkEvent* evt);
+
+private:
+    uint8_t  fCheckState;
+
+    typedef SkWidgetView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+#include "SkTextBox.h"
+
+class SkStaticTextView : public SkView {
+public:
+            SkStaticTextView();
+    virtual ~SkStaticTextView();
+
+    enum Mode {
+        kFixedSize_Mode,
+        kAutoWidth_Mode,
+        kAutoHeight_Mode,
+
+        kModeCount
+    };
+    Mode    getMode() const { return (Mode)fMode; }
+    void    setMode(Mode);
+
+    SkTextBox::SpacingAlign getSpacingAlign() const { return (SkTextBox::SpacingAlign)fSpacingAlign; }
+    void    setSpacingAlign(SkTextBox::SpacingAlign);
+
+    void    getMargin(SkPoint* margin) const;
+    void    setMargin(SkScalar dx, SkScalar dy);
+
+    size_t  getText(SkString* text = NULL) const;
+    size_t  getText(char text[] = NULL) const;
+    void    setText(const SkString&);
+    void    setText(const char text[]);
+    void    setText(const char text[], size_t len);
+
+    void    getPaint(SkPaint*) const;
+    void    setPaint(const SkPaint&);
+
+protected:
+    // overrides
+    virtual void onDraw(SkCanvas*);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node*);
+
+private:
+    SkPoint     fMargin;
+    SkString    fText;
+    SkPaint     fPaint;
+    uint8_t     fMode;
+    uint8_t     fSpacingAlign;
+
+    void computeSize();
+
+    typedef SkView INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////////////////////
+
+class SkAnimator;
+class SkListSource;
+class SkScrollBarView;
+
+class SkListView : public SkWidgetView {
+public:
+            SkListView();
+    virtual ~SkListView();
+
+    bool    hasScrollBar() const { return fScrollBar != NULL; }
+    void    setHasScrollBar(bool);
+
+    /** Return the number of visible rows
+    */
+    int     getVisibleRowCount() const { return fVisibleRowCount; }
+    /** Return the index of the selected row, or -1 if none
+    */
+    int     getSelection() const { return fCurrIndex; }
+    /** Set the index of the selected row, or -1 for none
+    */
+    void    setSelection(int);
+    /** If possible, move the selection up and return true,
+        else do nothing and return false
+        If nothing is selected, select the last item (unless there are no items).
+    */
+    bool    moveSelectionUp();
+    /** If possible, move the selection down and return true,
+        else do nothing and return false.
+        If nothing is selected, select the first item (unless there are no items).
+    */
+    bool    moveSelectionDown();
+
+    SkListSource*   getListSource() const { return fSource; }
+    SkListSource*   setListSource(SkListSource*);
+
+    /** Call this in your event handler. If the specified event is from a SkListView,
+        then it returns the index of the selected item in this list, otherwise it
+        returns -1
+    */
+    static int GetWidgetEventListIndex(const SkEvent&);
+
+protected:
+    // overrides
+    virtual void onDraw(SkCanvas*);
+    virtual void onSizeChange();
+    virtual bool onEvent(const SkEvent&);
+    virtual void onInflate(const SkDOM& dom, const SkDOM::Node* node);
+    virtual bool onPrepareWidgetEvent(SkEvent*);
+
+private:
+    enum DirtyFlags {
+        kAnimCount_DirtyFlag    = 0x01,
+        kAnimContent_DirtyFlag  = 0x02
+    };
+    void    dirtyCache(unsigned dirtyFlags);
+    bool    ensureCache();
+
+    int     logicalToVisualIndex(int index) const { return index - fScrollIndex; }
+    void    invalSelection();
+    SkScalar getContentWidth() const;
+    bool    getRowRect(int index, SkRect*) const;
+    void    ensureSelectionIsVisible();
+    void    ensureVisibleRowCount();
+
+    struct BindingRec;
+
+    enum Heights {
+        kNormal_Height,
+        kSelected_Height
+    };
+    SkListSource*   fSource;
+    SkScrollBarView*    fScrollBar;
+    SkAnimator*     fAnims;
+    BindingRec*     fBindings;
+    SkString        fSkinName;
+    SkScalar        fHeights[2];
+    int16_t         fScrollIndex, fCurrIndex;
+    uint16_t        fVisibleRowCount, fBindingCount;
+    SkBool8         fAnimContentDirty;
+    SkBool8         fAnimFocusDirty;
+
+    typedef SkWidgetView INHERITED;
+};
+
+class SkListSource : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkListSource)
+
+    virtual int countFields();
+    virtual void getFieldName(int index, SkString* field);
+    /** Return the index of the named field, or -1 if not found */
+    virtual int findFieldIndex(const char field[]);
+
+    virtual int countRecords();
+    virtual void getRecord(int rowIndex, int fieldIndex, SkString* data);
+
+    virtual bool prepareWidgetEvent(SkEvent*, int rowIndex);
+
+    static SkListSource* Factory(const char name[]);
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/include/utils/unix/XkeysToSkKeys.h b/include/views/unix/XkeysToSkKeys.h
similarity index 100%
rename from include/utils/unix/XkeysToSkKeys.h
rename to include/views/unix/XkeysToSkKeys.h
diff --git a/include/utils/unix/keysym2ucs.h b/include/views/unix/keysym2ucs.h
similarity index 100%
rename from include/utils/unix/keysym2ucs.h
rename to include/views/unix/keysym2ucs.h
diff --git a/include/xml/SkBML_WXMLParser.h b/include/xml/SkBML_WXMLParser.h
index e16b95c..74f164c 100644
--- a/include/xml/SkBML_WXMLParser.h
+++ b/include/xml/SkBML_WXMLParser.h
@@ -21,10 +21,10 @@
     BML_WXMLParser(SkWStream& writer);
     virtual ~BML_WXMLParser();
     static void Write(SkStream& s, const char filename[]);
-  
+
   /** @cond UNIT_TEST */
   SkDEBUGCODE(static void UnitTest();)
-  /** @endcond */  
+  /** @endcond */
 private:
     virtual bool onAddAttribute(const char name[], const char value[]);
     virtual bool onEndElement(const char name[]);
@@ -44,4 +44,3 @@
 };
 
 #endif // SkBML_WXMLParser_DEFINED
-
diff --git a/include/xml/SkBML_XMLParser.h b/include/xml/SkBML_XMLParser.h
index 7a8d6a1..9bdbf51 100644
--- a/include/xml/SkBML_XMLParser.h
+++ b/include/xml/SkBML_XMLParser.h
@@ -29,4 +29,3 @@
 };
 
 #endif // SkBML_XMLParser_DEFINED
-
diff --git a/include/xml/SkDOM.h b/include/xml/SkDOM.h
index 60145c8..e0bb744 100644
--- a/include/xml/SkDOM.h
+++ b/include/xml/SkDOM.h
@@ -47,7 +47,7 @@
     const Attr* getNextAttr(const Node*, const Attr*) const;
     const char* getAttrName(const Node*, const Attr*) const;
     const char* getAttrValue(const Node*, const Attr*) const;
-    
+
     // helpers for walking children
     int countChildren(const Node* node, const char elem[] = NULL) const;
 
@@ -89,4 +89,3 @@
 };
 
 #endif
-
diff --git a/include/xml/SkJS.h b/include/xml/SkJS.h
index 645e03b..8a11097 100644
--- a/include/xml/SkJS.h
+++ b/include/xml/SkJS.h
@@ -37,4 +37,3 @@
     JSContext *fContext;
     JSObject *fGlobal;
 };
-
diff --git a/include/xml/SkXMLParser.h b/include/xml/SkXMLParser.h
index bd8c2f1..1a90bf7 100644
--- a/include/xml/SkXMLParser.h
+++ b/include/xml/SkXMLParser.h
@@ -76,7 +76,7 @@
     virtual bool startElement(const char elem[]);
     virtual bool addAttribute(const char name[], const char value[]);
     virtual bool endElement(const char elem[]);
-    virtual bool text(const char text[], int len); 
+    virtual bool text(const char text[], int len);
     void* fParser;
 protected:
     SkXMLParserError* fError;
@@ -116,14 +116,14 @@
         const char* fName;
         const char* fValue;
     };
-    
+
     int         getDepth() const { return fDepth; }
     const char* getName();
     int         getAttributeCount();
     void        getAttributeInfo(int, AttrInfo*);
     const char* getText();
     bool        isWhitespace();
-    
+
 protected:
     virtual bool onEntityReplacement(const char name[],
                                      SkString* replacement);
@@ -142,11 +142,11 @@
     bool        onInit();   // return false on failure
     EventType   onNextToken();
     void        onExit();
-    
+
     SkStream*   fStream;
     Curr        fCurr;
     int         fDepth;
-    
+
     struct Impl;
     Impl*   fImpl;
 };
diff --git a/include/xml/SkXMLWriter.h b/include/xml/SkXMLWriter.h
index 4e0eda1..214fefe 100644
--- a/include/xml/SkXMLWriter.h
+++ b/include/xml/SkXMLWriter.h
@@ -83,4 +83,3 @@
 
 
 #endif
-
diff --git a/samplecode/ClockFaceView.cpp b/samplecode/ClockFaceView.cpp
deleted file mode 100644
index a479d9c..0000000
--- a/samplecode/ClockFaceView.cpp
+++ /dev/null
@@ -1,262 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTypeface.h"
-#include "SkAvoidXfermode.h"
-
-static inline SkPMColor rgb2gray(SkPMColor c)
-{
-    unsigned r = SkGetPackedR32(c);
-    unsigned g = SkGetPackedG32(c);
-    unsigned b = SkGetPackedB32(c);
-
-    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
-
-    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
-}
-
-class SkGrayScaleColorFilter : public SkColorFilter {
-public:
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
-    {
-        for (int i = 0; i < count; i++)
-            result[i] = rgb2gray(src[i]);
-    }
-};
-
-class SkChannelMaskColorFilter : public SkColorFilter {
-public:
-    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask)
-    {
-        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
-    }
-
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[])
-    {
-        SkPMColor mask = fMask;
-        for (int i = 0; i < count; i++)
-            result[i] = src[i] & mask;
-    }
-
-private:
-    SkPMColor   fMask;
-};
-
-///////////////////////////////////////////////////////////
-
-#include "SkGradientShader.h"
-#include "SkLayerRasterizer.h"
-#include "SkBlurMaskFilter.h"
-
-#include "Sk2DPathEffect.h"
-
-class Dot2DPathEffect : public Sk2DPathEffect {
-public:
-    Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix,
-                    SkTDArray<SkPoint>* pts)
-    : Sk2DPathEffect(matrix), fRadius(radius), fPts(pts) {}
-
-    virtual void flatten(SkFlattenableWriteBuffer& buffer)
-    {
-        this->INHERITED::flatten(buffer);
-
-        buffer.writeScalar(fRadius);
-    }
-    virtual Factory getFactory() { return CreateProc; }
-
-protected:
-    virtual void begin(const SkIRect& uvBounds, SkPath* dst) {
-        if (fPts) {
-            fPts->reset();
-        }
-        this->INHERITED::begin(uvBounds, dst);
-    }
-//    virtual void end(SkPath* dst) {}
-	virtual void next(const SkPoint& loc, int u, int v, SkPath* dst)
-    {
-        if (fPts) {
-            *fPts->append() = loc;
-        }
-        dst->addCircle(loc.fX, loc.fY, fRadius);
-    }
-
-    Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
-    {
-        fRadius = buffer.readScalar();
-        fPts = NULL;
-    }
-private:
-    SkScalar fRadius;
-    SkTDArray<SkPoint>* fPts;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
-    {
-        return new Dot2DPathEffect(buffer);
-    }
-
-    typedef Sk2DPathEffect INHERITED;
-};
-
-class InverseFillPE : public SkPathEffect {
-public:
-    InverseFillPE() {}
-    virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
-        *dst = src;
-        dst->setFillType(SkPath::kInverseWinding_FillType);
-        return true;
-    }
-    virtual Factory getFactory() { return Factory; }
-protected:
-//    InverseFillPE(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
-private:
-    static SkFlattenable* Factory(SkFlattenableReadBuffer& buffer) {
-        return new InverseFillPE;
-    }
-    typedef SkPathEffect INHERITED;
-};
-
-static SkPathEffect* makepe(float interp, SkTDArray<SkPoint>* pts) {
-    SkMatrix    lattice;
-    SkScalar    rad = 3 + SkIntToScalar(4) * (1 - interp);
-    lattice.setScale(rad*2, rad*2, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    return new Dot2DPathEffect(rad, lattice, pts);
-}
-
-static void r7(SkLayerRasterizer* rast, SkPaint& p, SkScalar interp) {
-    p.setPathEffect(makepe(interp, NULL))->unref();
-    rast->addLayer(p);
-#if 0
-    p.setPathEffect(new InverseFillPE())->unref();
-    p.setXfermodeMode(SkXfermode::kSrcIn_Mode);
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    p.setAlpha((1 - interp) * 255);
-    rast->addLayer(p);
-#endif
-}
-
-typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
-
-#include "SkXfermode.h"
-
-static void apply_shader(SkPaint* paint, float scale)
-{
-    SkPaint p;
-    SkLayerRasterizer*  rast = new SkLayerRasterizer;
-
-    p.setAntiAlias(true);
-    r7(rast, p, scale);
-    paint->setRasterizer(rast)->unref();
-
-    paint->setColor(SK_ColorBLUE);
-}
-
-class ClockFaceView : public SkView {
-    SkTypeface* fFace;
-    SkScalar fInterp;
-    SkScalar fDx;
-public:
-	ClockFaceView()
-    {
-        fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
-        fInterp = 0;
-        fDx = SK_Scalar1/64;
-    }
-
-    virtual ~ClockFaceView()
-    {
-        SkSafeUnref(fFace);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)
-    {
-        if (SampleCode::TitleQ(*evt))
-        {
-            SampleCode::TitleR(evt, "Text Effects");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawBG(SkCanvas* canvas)
-    {
-//        canvas->drawColor(0xFFDDDDDD);
-        canvas->drawColor(SK_ColorWHITE);
-    }
-
-    static void drawdots(SkCanvas* canvas, const SkPaint& orig) {
-        SkTDArray<SkPoint> pts;
-        SkPathEffect* pe = makepe(0, &pts);
-
-        SkScalar width = -1;
-        SkPath path, dstPath;
-        orig.getTextPath("9", 1, 0, 0, &path);
-        pe->filterPath(&dstPath, path, &width);
-
-        SkPaint p;
-        p.setAntiAlias(true);
-        p.setStrokeWidth(10);
-        p.setColor(SK_ColorRED);
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, pts.count(), pts.begin(),
-                           p);
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-
-        SkScalar    x = SkIntToScalar(20);
-        SkScalar    y = SkIntToScalar(300);
-        SkPaint     paint;
-
-        paint.setAntiAlias(true);
-        paint.setTextSize(SkIntToScalar(240));
-        paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
-                                                     SkTypeface::kBold));
-
-        SkString str("9");
-
-        paint.setTypeface(fFace);
-
-        apply_shader(&paint, fInterp);
-        canvas->drawText(str.c_str(), str.size(), x, y, paint);
-
-    //    drawdots(canvas, paint);
-
-        if (false) {
-            fInterp += fDx;
-            if (fInterp > 1) {
-                fInterp = 1;
-                fDx = -fDx;
-            } else if (fInterp < 0) {
-                fInterp = 0;
-                fDx = -fDx;
-            }
-            this->inval(NULL);
-        }
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ClockFaceView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/GMSampleView.h b/samplecode/GMSampleView.h
deleted file mode 100644
index 7decfcb..0000000
--- a/samplecode/GMSampleView.h
+++ /dev/null
@@ -1,52 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef GMSampleView_DEFINED
-#define GMSampleView_DEFINED
-
-#include "SampleCode.h"
-#include "gm.h"
-
-class GMSampleView : public SampleView {
-private:
-    typedef skiagm::GM GM;
-
-public:
-    GMSampleView(GM* gm)
-    : fGM(gm) {}
-    
-    virtual ~GMSampleView() {
-        delete fGM;
-    }
-    
-protected:
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString name("GM:");
-            name.append(fGM->shortName());
-            SampleCode::TitleR(evt, name.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        fGM->drawContent(canvas);
-    }
-
-    virtual void onDrawBackground(SkCanvas* canvas) {
-        fGM->drawBackground(canvas);
-    }
-
-private:
-    GM* fGM;
-    typedef SampleView INHERITED;
-};
-
-#endif
diff --git a/samplecode/OverView.cpp b/samplecode/OverView.cpp
deleted file mode 100644
index fc4a9ef..0000000
--- a/samplecode/OverView.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkView.h"
-
-static const int N = 8;
-const SkScalar W = SkIntToScalar(640);
-const SkScalar H = SkIntToScalar(480); 
-
-static const char gIsOverview[] = "is-overview";
-bool is_overview(SkView* view) {
-    SkEvent isOverview(gIsOverview);
-    return view->doQuery(&isOverview); 
-}
-class OverView : public SkView {
-public:
-    OverView(int count, const SkViewFactory* factories[]);
-    virtual ~OverView();
-    
-protected:
-    virtual bool onEvent(const SkEvent&);
-    virtual void onSizeChange();
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorLTGRAY);
-    }
-
-    virtual SkCanvas* beforeChildren(SkCanvas*);
-
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Overview");
-            return true;
-        }
-        if (evt->isType(gIsOverview)) {
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual bool onSendClickToChildren(SkScalar x, SkScalar y) {
-        return false;
-    }
-
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        int ix = (int)(SkScalarDiv(x * N, W));
-        int iy = (int)(SkScalarDiv(y * N, H));
-        if (ix >= 0 && iy >= 0) {
-            SkEvent evt("set-curr-index");
-            evt.setFast32(iy * N + ix);
-            this->sendEventToParents(evt);
-        }
-        return NULL;
-    }
-
-private:
-    int             fCount;
-    const SkViewFactory**  fFactories;
-
-    typedef SkView INHERITED;
-};
-
-SkView* create_overview(int count, const SkViewFactory* factories[]) {
-    return SkNEW_ARGS(OverView, (count, factories));
-};
-
-OverView::OverView(int count, const SkViewFactory* factories[]) {
-    fCount = count;
-    fFactories = factories;
-}
-
-OverView::~OverView() {
-}
-
-bool OverView::onEvent(const SkEvent& evt) {
-    return this->INHERITED::onEvent(evt);
-}
-
-void OverView::onSizeChange() {
-    this->detachAllChildren();
-    
-    SkScalar locX = 0;
-    SkScalar locY = 0;
-    for (int i = 0; i < fCount; i++) {
-        SkView* view = (*fFactories[i])();
-        view->setVisibleP(true);
-        this->attachChildToBack(view)->unref();
-        view->setLoc(locX, locY);
-        view->setSize(W, H);
-        locX += W;
-        if ((i % N) == N - 1) {
-            locY += H;
-            locX = 0;
-        }
-    }
-}
-
-SkCanvas* OverView::beforeChildren(SkCanvas* canvas) {
-    canvas->scale(SK_Scalar1 / N, SK_Scalar1 / N);
-    return canvas;
-}
-
diff --git a/samplecode/Sample2PtRadial.cpp b/samplecode/Sample2PtRadial.cpp
deleted file mode 100644
index 0c44fd6..0000000
--- a/samplecode/Sample2PtRadial.cpp
+++ /dev/null
@@ -1,52 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-
-
-class TwoPtRadialView : public SampleView {
-public:
-    TwoPtRadialView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "2PtRadial");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
-
-        SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
-        SkPoint c0 = { 0, 0 };
-        SkScalar r0 = 100;
-        SkPoint c1 = { 100, 100 };
-        SkScalar r1 = 100;
-        SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
-                                                             NULL, 2,
-                                                             SkShader::kClamp_TileMode);
-        
-        SkPaint paint;
-        paint.setShader(s)->unref();
-        canvas->drawPaint(paint);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TwoPtRadialView; }
-static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleAAClip.cpp b/samplecode/SampleAAClip.cpp
deleted file mode 100644
index d2931fa..0000000
--- a/samplecode/SampleAAClip.cpp
+++ /dev/null
@@ -1,128 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkAAClip.h"
-
-static void testop(const SkIRect& r0, const SkIRect& r1, SkRegion::Op op,
-                   const SkIRect& expectedR) {
-    SkAAClip c0, c1, c2;
-    c0.setRect(r0);
-    c1.setRect(r1);
-    c2.op(c0, c1, op);
-    
-    SkIRect r2 = c2.getBounds();
-    SkASSERT(r2 == expectedR);
-}
-
-static const struct {
-    SkIRect r0;
-    SkIRect r1;
-    SkRegion::Op op;
-    SkIRect expectedR;
-} gRec[] = {
-    {{ 1, 2, 9, 3 }, { -3, 2, 5, 11 }, SkRegion::kDifference_Op, { 5, 2, 9, 3 }},
-    {{ 1, 10, 5, 13 }, { 1, 2, 5, 11 }, SkRegion::kDifference_Op, { 1, 11, 5, 13 }},
-    {{ 1, 10, 5, 13 }, { 1, 2, 5, 11 }, SkRegion::kReverseDifference_Op, { 1, 2, 5, 10 }},
-};
-
-static void testop() {
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
-        testop(gRec[i].r0, gRec[i].r1, gRec[i].op, gRec[i].expectedR);
-    }
-}
-
-static void drawClip(SkCanvas* canvas, const SkAAClip& clip) {
-    SkMask mask;
-    SkBitmap bm;
-    
-    clip.copyToMask(&mask);
-    SkAutoMaskFreeImage amfi(mask.fImage);
-
-    bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
-                 mask.fBounds.height(), mask.fRowBytes);
-    bm.setPixels(mask.fImage);
-
-    SkPaint paint;
-    canvas->drawBitmap(bm,
-                       SK_Scalar1 * mask.fBounds.fLeft,
-                       SK_Scalar1 * mask.fBounds.fTop,
-                       &paint);
-}
-
-class AAClipView : public SampleView {
-public:
-    AAClipView() {
-        testop();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "AAClip");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-#if 1
-        SkAAClip aaclip;
-        SkPath path;
-        SkRect bounds;
-        
-        bounds.set(0, 0, 20, 20);
-        bounds.inset(SK_ScalarHalf, SK_ScalarHalf);
-
-//        path.addRect(bounds);
-//        path.addOval(bounds);
-        path.addRoundRect(bounds, 4, 4);
-        aaclip.setPath(path);
-        canvas->translate(30, 30);
-        drawClip(canvas, aaclip);
-
-        SkAAClip aaclip2;
-        path.offset(10, 10);
-        aaclip2.setPath(path);
-        canvas->translate(30, 0);
-        drawClip(canvas, aaclip2);
-
-        SkAAClip aaclip3;
-        aaclip3.op(aaclip, aaclip2, SkRegion::kIntersect_Op);
-        canvas->translate(30, 0);
-        drawClip(canvas, aaclip3);
-        
-#endif
-        
-#if 0
-        SkRect r;
-        r.set(0, 0, this->width(), this->height());
-        r.inset(20, 20);
-        canvas->clipRect(r);
-        
-        SkPath path;
-        path.addRect(r);
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(SK_ColorRED);
-        canvas->drawPath(path, paint);
-#endif
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new AAClipView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAAClip2.cpp b/samplecode/SampleAAClip2.cpp
deleted file mode 100644
index d12e13c..0000000
--- a/samplecode/SampleAAClip2.cpp
+++ /dev/null
@@ -1,194 +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 "SkAAClip.h"
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkImageDecoder.h"
-
-#ifdef SK_BUILD_FOR_WIN
-// windows doesn't have roundf
-inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
-#endif
-
-static void drawClip(SkCanvas* canvas, const SkAAClip& clip) {
-    SkMask mask;
-    SkBitmap bm;
-    
-    clip.copyToMask(&mask);
-    SkAutoMaskFreeImage amfi(mask.fImage);
-
-    bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
-                 mask.fBounds.height(), mask.fRowBytes);
-    bm.setPixels(mask.fImage);
-    
-    SkPaint paint;
-    canvas->drawBitmap(bm,
-                       SK_Scalar1 * mask.fBounds.fLeft,
-                       SK_Scalar1 * mask.fBounds.fTop,
-                       &paint);
-}
-
-static void paint_rgn(SkCanvas* canvas, const SkAAClip& clip,
-                      const SkPaint& paint) {
-    SkMask mask;
-    SkBitmap bm;
-    
-    clip.copyToMask(&mask);
-    SkAutoMaskFreeImage amfi(mask.fImage);
-
-    bm.setConfig(SkBitmap::kA8_Config, mask.fBounds.width(),
-                 mask.fBounds.height(), mask.fRowBytes);
-    bm.setPixels(mask.fImage);
-    
-    canvas->drawBitmap(bm,
-                       SK_Scalar1 * mask.fBounds.fLeft,
-                       SK_Scalar1 * mask.fBounds.fTop,
-                       &paint);
-}
-
-class AAClipView2 : public SampleView {
-public:
-	AAClipView2() {
-        fBase.set(100, 100, 150, 150);
-        fRect = fBase;
-        fRect.inset(5, 5);
-        fRect.offset(25, 25);
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-    static void setAAClip(SkAAClip* clip, const SkIRect& rect) {
-        SkRect r;
-        r.set(rect);
-        SkPath path;
-        path.addRoundRect(r, SkIntToScalar(5), SkIntToScalar(5));
-        clip->setPath(path);
-    }
-
-    void build_rgn(SkAAClip* clip, SkRegion::Op op) {
-        setAAClip(clip, fBase);
-
-        SkAAClip clip2;
-        setAAClip(&clip2, fRect);
-        clip->op(clip2, op);
-    }
-
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "AAClips");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawOrig(SkCanvas* canvas, bool bg) {
-        SkRect      r;
-        SkPaint     paint;
-        
-        paint.setStyle(SkPaint::kStroke_Style);
-        if (bg)
-            paint.setColor(0xFFBBBBBB);
-        
-        r.set(fBase);
-        canvas->drawRect(r, paint);
-        r.set(fRect);
-        canvas->drawRect(r, paint);
-    }
-    
-    static void outer_frame(SkCanvas* canvas, const SkIRect& rect) {
-        SkRect r;
-        r.set(rect);
-        r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
-        
-        SkPaint paint;
-        paint.setColor(SK_ColorGRAY);
-        paint.setStyle(SkPaint::kStroke_Style);
-        canvas->drawRect(r, paint);
-    }
-
-    void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
-        SkAAClip clip;
-
-        this->build_rgn(&clip, op);
-        
-        this->drawOrig(canvas, true);
-
-        SkPaint paint;
-        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
-        paint_rgn(canvas, clip, paint);
-
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setColor(color);
-        paint_rgn(canvas, clip, paint);
-        
-        SkAAClip clip2(clip);
-        clip2.translate(0, 80);
-        outer_frame(canvas, clip2.getBounds());
-        paint_rgn(canvas, clip2, paint);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-
-        static const struct {
-            SkColor         fColor;
-            const char*     fName;
-            SkRegion::Op    fOp;
-        } gOps[] = {
-            { SK_ColorBLACK,    "Difference",   SkRegion::kDifference_Op    },
-            { SK_ColorRED,      "Intersect",    SkRegion::kIntersect_Op     },
-            { 0xFF008800,       "Union",        SkRegion::kUnion_Op         },
-            { SK_ColorBLUE,     "XOR",          SkRegion::kXOR_Op           }
-        };
-
-        SkPaint textPaint;
-        textPaint.setAntiAlias(true);
-        textPaint.setTextSize(SK_Scalar1*24);
-
-        this->drawOrig(canvas, false);
-        
-        canvas->translate(0, SkIntToScalar(200));
-
-        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
-            canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
-
-            this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
-            
-            canvas->translate(SkIntToScalar(200), 0);
-        }
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
-    }
-    
-    virtual bool onClick(Click* click) {
-        fRect.offset(click->fICurr.fX - click->fIPrev.fX,
-                     click->fICurr.fY - click->fIPrev.fY);
-        this->inval(NULL);
-        return true;
-    }
-    
-private:
-    SkIRect    fBase, fRect;
-    
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new AAClipView2; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAARectModes.cpp b/samplecode/SampleAARectModes.cpp
deleted file mode 100644
index 7b9e7d5..0000000
--- a/samplecode/SampleAARectModes.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkColorPriv.h"
-#include "SkShader.h"
-
-static SkCanvas* create_canvas(int w, int h) {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
-    bm.allocPixels();
-    bm.eraseColor(0);
-    return new SkCanvas(bm);
-}
-
-static const SkBitmap& extract_bitmap(SkCanvas* canvas) {
-    return canvas->getDevice()->accessBitmap(false);
-}
-
-static const struct {
-    SkXfermode::Mode  fMode;
-    const char*         fLabel;
-} gModes[] = {
-    { SkXfermode::kClear_Mode,    "Clear"     },
-    { SkXfermode::kSrc_Mode,      "Src"       },
-    { SkXfermode::kDst_Mode,      "Dst"       },
-    { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
-    { SkXfermode::kDstOver_Mode,  "DstOver"   },
-    { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
-    { SkXfermode::kDstIn_Mode,    "DstIn"     },
-    { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
-    { SkXfermode::kDstOut_Mode,   "DstOut"    },
-    { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
-    { SkXfermode::kDstATop_Mode,  "DstATop"   },
-    { SkXfermode::kXor_Mode,      "Xor"       },
-};
-
-const int gWidth = 64;
-const int gHeight = 64;
-const SkScalar W = SkIntToScalar(gWidth);
-const SkScalar H = SkIntToScalar(gHeight);
-
-static SkScalar drawCell(SkCanvas* canvas, SkXfermode* mode,
-                         SkAlpha a0, SkAlpha a1) {
-
-    SkPaint paint;
-    paint.setAntiAlias(true);
-
-    SkRect r = SkRect::MakeWH(W, H);
-    r.inset(W/10, H/10);
-
-    paint.setColor(SK_ColorBLUE);
-    paint.setAlpha(a0);
-    canvas->drawOval(r, paint);
-
-    paint.setColor(SK_ColorRED);
-    paint.setAlpha(a1);
-    paint.setXfermode(mode);
-
-    SkScalar offset = SK_Scalar1 / 3;
-    SkRect rect = SkRect::MakeXYWH(W / 4 + offset,
-                                   H / 4 + offset,
-                                   W / 2, H / 2);
-    canvas->drawRect(rect, paint);
-    
-    return H;
-}
-
-static SkShader* make_bg_shader() {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
-    bm.allocPixels();
-    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF;
-    *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC,
-                                                             0xCC, 0xCC);
-
-    SkShader* s = SkShader::CreateBitmapShader(bm,
-                                               SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode);
-    
-    SkMatrix m;
-    m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-    s->setLocalMatrix(m);
-    return s;
-}
-
-class AARectsModesView : public SampleView {
-    SkPaint fBGPaint;
-public:
-    AARectsModesView () {
-        fBGPaint.setShader(make_bg_shader())->unref();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "AARectsModes");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        const SkRect bounds = SkRect::MakeWH(W, H);
-        static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 };
-
-        canvas->translate(SkIntToScalar(4), SkIntToScalar(4));
-
-        for (int alpha = 0; alpha < 4; ++alpha) {
-            canvas->save();
-            canvas->save();
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); ++i) {
-                if (6 == i) {
-                    canvas->restore();
-                    canvas->translate(W * 5, 0);
-                    canvas->save();
-                }
-                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
-                
-                canvas->drawRect(bounds, fBGPaint);
-                canvas->saveLayer(&bounds, NULL);
-                SkScalar dy = drawCell(canvas, mode,
-                                       gAlphaValue[alpha & 1],
-                                       gAlphaValue[alpha & 2]);
-                canvas->restore();
-
-                canvas->translate(0, dy * 5 / 4);
-                SkSafeUnref(mode);
-            }
-            canvas->restore();
-            canvas->restore();
-            canvas->translate(W * 5 / 4, 0);
-        }
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new AARectsModesView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAARects.cpp b/samplecode/SampleAARects.cpp
deleted file mode 100644
index 0490552..0000000
--- a/samplecode/SampleAARects.cpp
+++ /dev/null
@@ -1,198 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-#include "SkShader.h"
-
-static SkBitmap createBitmap(int n) {
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
-    bitmap.allocPixels();
-    bitmap.eraseColor(SK_ColorGREEN);
-    
-    SkCanvas canvas(bitmap);
-    SkRect r;
-    r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    
-    paint.setColor(SK_ColorRED);
-    canvas.drawOval(r, paint);
-    paint.setColor(SK_ColorBLUE);
-    paint.setStrokeWidth(SkIntToScalar(n)/15);
-    paint.setStyle(SkPaint::kStroke_Style);
-    canvas.drawLine(0, 0, r.fRight, r.fBottom, paint);
-    canvas.drawLine(0, r.fBottom, r.fRight, 0, paint);
-    
-    return bitmap;
-}
-
-class AARectView : public SampleView {
-    SkBitmap fBitmap;
-    enum {
-        N = 64
-    };
-public:
-    AARectView() {
-        fBitmap = createBitmap(N);
-        
-        fWidth = N;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "AA Rects");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-
-        SkPaint bluePaint;
-        bluePaint.setARGB(0xff, 0x0, 0x0, 0xff);
-        SkPaint bmpPaint;
-        SkShader* bmpShader = SkShader::CreateBitmapShader(fBitmap, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode);
-        bmpPaint.setShader(bmpShader);
-        bmpShader->unref();
-
-        bluePaint.setStrokeWidth(3);
-        bmpPaint.setStrokeWidth(3);
-
-        SkPaint paints[] = { bluePaint, bmpPaint };
-
-        SkRect rect;
-
-        SkScalar dx = SkIntToScalar(80);
-        SkScalar dy = SkIntToScalar(100);
-        SkMatrix matrix;
-        for (size_t p = 0; p < SK_ARRAY_COUNT(paints); ++p) {
-            for (int stroke = 0; stroke < 2; ++stroke) {
-                paints[p].setStyle(stroke ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
-                for (int a = 0; a < 3; ++ a) {
-                    paints[p].setAntiAlias(a > 0);
-                    paints[p].setAlpha(a > 1 ? 0x80 : 0xff);
-
-                    canvas->save();
-                        rect = SkRect::MakeLTRB(SkFloatToScalar(0.f),
-                                                SkFloatToScalar(0.f),
-                                                SkFloatToScalar(40.f),
-                                                SkFloatToScalar(40.f));
-                        canvas->drawRect(rect, paints[p]);
-                        canvas->translate(dx, 0);
-
-                        rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
-                                                SkFloatToScalar(0.5f),
-                                                SkFloatToScalar(40.5f),
-                                                SkFloatToScalar(40.5f));
-                        canvas->drawRect(rect, paints[p]);
-                        canvas->translate(dx, 0);
-
-                        rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
-                                                SkFloatToScalar(0.5f),
-                                                SkFloatToScalar(40.f),
-                                                SkFloatToScalar(40.f));
-                        canvas->drawRect(rect, paints[p]);
-                        canvas->translate(dx, 0);
-
-                        rect = SkRect::MakeLTRB(SkFloatToScalar(0.75f),
-                                                SkFloatToScalar(0.75f),
-                                                SkFloatToScalar(40.75f),
-                                                SkFloatToScalar(40.75f));
-                        canvas->drawRect(rect, paints[p]);
-                        canvas->translate(dx, 0);
-
-                        canvas->save();
-                            canvas->translate(SkFloatToScalar(.33f), SkFloatToScalar(.67f));
-                            rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f),
-                                                    SkFloatToScalar(0.0f),
-                                                    SkFloatToScalar(40.0f),
-                                                    SkFloatToScalar(40.0f));
-                            canvas->drawRect(rect, paints[p]);
-                        canvas->restore();
-                        canvas->translate(dx, 0);
-
-                        canvas->save();
-                            matrix.setRotate(SkFloatToScalar(45.f));
-                            canvas->concat(matrix);
-                            canvas->translate(SkFloatToScalar(20.0f / sqrtf(2.f)),
-                                                SkFloatToScalar(20.0f / sqrtf(2.f)));
-                            rect = SkRect::MakeLTRB(SkFloatToScalar(-20.0f),
-                                                    SkFloatToScalar(-20.0f),
-                                                    SkFloatToScalar(20.0f),
-                                                    SkFloatToScalar(20.0f));
-                            canvas->drawRect(rect, paints[p]);
-                        canvas->restore();
-                        canvas->translate(dx, 0);
-
-                        canvas->save();
-                            canvas->rotate(SkFloatToScalar(90.f));
-                            rect = SkRect::MakeLTRB(SkFloatToScalar(0.0f),
-                                                    SkFloatToScalar(0.0f),
-                                                    SkFloatToScalar(40.0f),
-                                                    SkFloatToScalar(-40.0f));
-                            canvas->drawRect(rect, paints[p]);
-                        canvas->restore();
-                        canvas->translate(dx, 0);
-
-                        canvas->save();
-                            canvas->rotate(SkFloatToScalar(90.f));
-                            rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
-                                                    SkFloatToScalar(0.5f),
-                                                    SkFloatToScalar(40.5f),
-                                                    SkFloatToScalar(-40.5f));
-                            canvas->drawRect(rect, paints[p]);
-                        canvas->restore();
-                        canvas->translate(dx, 0);
-
-                        canvas->save();
-                            matrix.setScale(SkFloatToScalar(-1.f), SkFloatToScalar(-1.f));
-                            canvas->concat(matrix);
-                            rect = SkRect::MakeLTRB(SkFloatToScalar(0.5f),
-                                                    SkFloatToScalar(0.5f),
-                                                    SkFloatToScalar(-40.5f),
-                                                    SkFloatToScalar(-40.5f));
-                            canvas->drawRect(rect, paints[p]);
-                        canvas->restore();
-                        canvas->translate(dx, 0);
-
-                        canvas->save();
-                            matrix.setScale(SkFloatToScalar(2.1f), SkFloatToScalar(4.1f));
-                            canvas->concat(matrix);
-                            rect = SkRect::MakeLTRB(SkFloatToScalar(0.1f),
-                                                    SkFloatToScalar(0.1f),
-                                                    SkFloatToScalar(19.1f),
-                                                    SkFloatToScalar(9.1f));
-                            canvas->drawRect(rect, paints[p]);
-                        canvas->restore();
-                        canvas->translate(dx, 0);
-
-                    canvas->restore();
-                    canvas->translate(0, dy);
-                }
-            }
-        }
-    }
-    
-private:
-    int fWidth;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new AARectView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAll.cpp b/samplecode/SampleAll.cpp
deleted file mode 100644
index 4321532..0000000
--- a/samplecode/SampleAll.cpp
+++ /dev/null
@@ -1,722 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkView.h"
-#include "Sk1DPathEffect.h"
-#include "Sk2DPathEffect.h"
-#include "SkAvoidXfermode.h"
-#include "SkBlurMaskFilter.h"
-#include "SkColorFilter.h"
-#include "SkColorPriv.h"
-#include "SkCornerPathEffect.h"
-#include "SkDashPathEffect.h"
-#include "SkDiscretePathEffect.h"
-#include "SkEmbossMaskFilter.h"
-#include "SkGradientShader.h"
-#include "SkImageDecoder.h"
-#include "SkLayerRasterizer.h"
-#include "SkMath.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkComposeShader.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkPicture.h"
-#include "SkRandom.h"
-#include "SkTransparentShader.h"
-#include "SkTypeface.h"
-#include "SkUnitMappers.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-
-#include <math.h>
-    
-static inline SkPMColor rgb2gray(SkPMColor c) {
-    unsigned r = SkGetPackedR32(c);
-    unsigned g = SkGetPackedG32(c);
-    unsigned b = SkGetPackedB32(c);
-    
-    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
-    
-    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
-}
-
-class SkGrayScaleColorFilter : public SkColorFilter {
-public:
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
-        for (int i = 0; i < count; i++)
-            result[i] = rgb2gray(src[i]);
-    }
-};
-
-class SkChannelMaskColorFilter : public SkColorFilter {
-public:
-    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
-        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
-    }
-
-    virtual void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
-        SkPMColor mask = fMask;
-        for (int i = 0; i < count; i++) {
-            result[i] = src[i] & mask;
-        }
-    }
-    
-private:
-    SkPMColor   fMask;
-};
-
-///////////////////////////////////////////////////////////
-
-static void r0(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
-                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
-    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
-
-    p.setMaskFilter(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-
-    p.setAlpha(0x11);
-    p.setStyle(SkPaint::kFill_Style);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    rast->addLayer(p);
-}
-
-static void r1(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-
-    p.setAlpha(0x40);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*2);
-    rast->addLayer(p);
-}
-    
-static void r2(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setStyle(SkPaint::kStrokeAndFill_Style);
-    p.setStrokeWidth(SK_Scalar1*4);
-    rast->addLayer(p);
-
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*3/2);
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-}
-
-static void r3(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*3);
-    rast->addLayer(p);
-
-    p.setAlpha(0x20);
-    p.setStyle(SkPaint::kFill_Style);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    rast->addLayer(p);
-}
-
-static void r4(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setAlpha(0x60);
-    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
-
-    p.setAlpha(0xFF);
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
-
-    p.setXfermode(NULL);
-    rast->addLayer(p);
-}
-
-static void r5(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-
-    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
-    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
-    rast->addLayer(p);
-}
-
-static void r6(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-    
-    p.setAntiAlias(false);
-    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
-    r5(rast2, p);
-    p.setRasterizer(rast2)->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-}
-
-class Dot2DPathEffect : public Sk2DPathEffect {
-public:
-    Dot2DPathEffect(SkScalar radius, const SkMatrix& matrix)
-        : Sk2DPathEffect(matrix), fRadius(radius) {}
-
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
-        this->INHERITED::flatten(buffer);
-        
-        buffer.writeScalar(fRadius);
-    }
-    virtual Factory getFactory() { return CreateProc; }
-
-protected:
-	virtual void next(const SkPoint& loc, int u, int v, SkPath* dst) {
-        dst->addCircle(loc.fX, loc.fY, fRadius);
-    }
-    
-    Dot2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
-        fRadius = buffer.readScalar();
-    }
-private:
-    SkScalar fRadius;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return new Dot2DPathEffect(buffer);
-    }
-
-    typedef Sk2DPathEffect INHERITED;
-};
-
-static void r7(SkLayerRasterizer* rast, SkPaint& p) {
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*4, lattice))->unref();
-    rast->addLayer(p);
-}
-
-static void r8(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-    
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    p.setPathEffect(new Dot2DPathEffect(SK_Scalar1*2, lattice))->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-
-    p.setPathEffect(NULL);
-    p.setXfermode(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-}
-
-class Line2DPathEffect : public Sk2DPathEffect {
-public:
-    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
-        : Sk2DPathEffect(matrix), fWidth(width) {}
-
-	virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
-        if (this->INHERITED::filterPath(dst, src, width)) {
-            *width = fWidth;
-            return true;
-        }
-        return false;
-    }
-    
-    virtual Factory getFactory() { return CreateProc; }
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
-        this->INHERITED::flatten(buffer);
-        buffer.writeScalar(fWidth);
-    }
-protected:
-	virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
-        if (ucount > 1) {
-            SkPoint	src[2], dstP[2];
-
-            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
-                       SkIntToScalar(v) + SK_ScalarHalf);
-            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
-                       SkIntToScalar(v) + SK_ScalarHalf);
-            this->getMatrix().mapPoints(dstP, src, 2);
-            
-            dst->moveTo(dstP[0]);
-            dst->lineTo(dstP[1]);
-        }
-    }
-    
-    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
-        fWidth = buffer.readScalar();
-    }
-    
-private:
-    SkScalar fWidth;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) { return new Line2DPathEffect(buffer); }
-
-    typedef Sk2DPathEffect INHERITED;
-};
-
-static void r9(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-    
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
-    lattice.postRotate(SkIntToScalar(30), 0, 0);
-    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-
-    p.setPathEffect(NULL);
-    p.setXfermode(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-}
-
-typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
-
-static const raster_proc gRastProcs[] = {
-    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
-};
-
-static const struct {
-    SkColor fMul, fAdd;
-} gLightingColors[] = {
-    { 0x808080, 0x800000 }, // general case
-    { 0x707070, 0x707070 }, // no-pin case
-    { 0xFFFFFF, 0x800000 }, // just-add case
-    { 0x808080, 0x000000 }, // just-mul case
-    { 0xFFFFFF, 0x000000 }  // identity case
-};
-
-static unsigned color_dist16(uint16_t a, uint16_t b) {
-    unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b));
-    unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b));
-    unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b));
-    
-    return SkMax32(dr, SkMax32(dg, db));
-}
-
-static unsigned scale_dist(unsigned dist, unsigned scale) {
-    dist >>= 6;
-    dist = (dist << 2) | dist;
-    dist = (dist << 4) | dist;
-    return dist;
-
-//    return SkAlphaMul(dist, scale);
-}
-
-static void apply_shader(SkPaint* paint, int index) {    
-    raster_proc proc = gRastProcs[index];
-    if (proc) {
-        SkPaint p;
-        SkLayerRasterizer*  rast = new SkLayerRasterizer;
-
-        p.setAntiAlias(true);
-        proc(rast, p);
-        paint->setRasterizer(rast)->unref();
-    }
-
-#if 1
-    SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
-    paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();    
-    paint->setColor(SK_ColorBLUE);
-#endif
-}
-
-class DemoView : public SampleView {
-public:
-    DemoView() {}
-	
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Demo");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-    
-    void makePath(SkPath& path) {
-        path.addCircle(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(20),
-            SkPath::kCCW_Direction);
-        for (int index = 0; index < 10; index++) {
-            SkScalar x = SkFloatToScalar(cos(index / 10.0f * 2 * 3.1415925358f));
-            SkScalar y = SkFloatToScalar(sin(index / 10.0f * 2 * 3.1415925358f));
-            x *= index & 1 ? 7 : 14;
-            y *= index & 1 ? 7 : 14;
-            x += SkIntToScalar(20);
-            y += SkIntToScalar(20);
-            if (index == 0)
-                path.moveTo(x, y);
-            else
-                path.lineTo(x, y);
-        }
-        path.close();
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->save();
-        drawPicture(canvas, 0);
-        canvas->restore();
-
-        {
-            SkPicture picture;
-            SkCanvas* record = picture.beginRecording(320, 480);
-            drawPicture(record, 120);
-            canvas->translate(0, SkIntToScalar(120));
-
-            SkRect clip;
-            clip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));
-            do {
-                canvas->save();
-                canvas->clipRect(clip);
-                picture.draw(canvas);
-                canvas->restore();
-                if (clip.fRight < SkIntToScalar(320))
-                    clip.offset(SkIntToScalar(160), 0);
-                else if (clip.fBottom < SkIntToScalar(480))
-                    clip.offset(-SkIntToScalar(320), SkIntToScalar(160));
-                else
-                    break;
-            } while (true);
-        }
-    }
-    
-    void drawPicture(SkCanvas* canvas, int spriteOffset) {
-	    SkMatrix matrix; matrix.reset();
-		SkPaint paint;
-		SkPath path;
-        SkPoint start = {0, 0};
-        SkPoint stop = { SkIntToScalar(40), SkIntToScalar(40) };
-		SkRect rect = {0, 0, SkIntToScalar(40), SkIntToScalar(40) };
-		SkRect rect2 = {0, 0, SkIntToScalar(65), SkIntToScalar(20) };
-		SkScalar left = 0, top = 0, x = 0, y = 0;
-		size_t index;
-		
-		char ascii[] = "ascii...";
-		size_t asciiLength = sizeof(ascii) - 1;
-		char utf8[] = "utf8" "\xe2\x80\xa6";
-		short utf16[] = {'u', 't', 'f', '1', '6', 0x2026 };
-		short utf16simple[] = {'u', 't', 'f', '1', '6', '!' };
-		
-        makePath(path);
-        SkTDArray<SkPoint>(pos);
-		pos.setCount(asciiLength);
-		for (index = 0;  index < asciiLength; index++)
-			pos[index].set(SkIntToScalar(index * 10), SkIntToScalar(index * 2));
-        SkTDArray<SkPoint>(pos2);
-		pos2.setCount(asciiLength);
-		for (index = 0;  index < asciiLength; index++)
-			pos2[index].set(SkIntToScalar(index * 10), SkIntToScalar(20));
-		
-        // shaders
-        SkPoint linearPoints[] = { { 0, 0, }, { SkIntToScalar(40), SkIntToScalar(40) } };
-        SkColor linearColors[] = { SK_ColorRED, SK_ColorBLUE };
-        SkScalar* linearPos = NULL;
-        int linearCount = 2;
-        SkShader::TileMode linearMode = SkShader::kMirror_TileMode;
-        SkUnitMapper* linearMapper = new SkDiscreteMapper(3);
-        SkAutoUnref unmapLinearMapper(linearMapper);
-        SkShader* linear = SkGradientShader::CreateLinear(linearPoints,
-            linearColors, linearPos, linearCount, linearMode, linearMapper);
-
-        SkPoint radialCenter = { SkIntToScalar(25), SkIntToScalar(25) };
-        SkScalar radialRadius = SkIntToScalar(25);
-        SkColor radialColors[] = { SK_ColorGREEN, SK_ColorGRAY, SK_ColorRED };
-        SkScalar radialPos[] = { 0, SkIntToScalar(3) / 5, SkIntToScalar(1)};
-        int radialCount = 3;
-        SkShader::TileMode radialMode = SkShader::kRepeat_TileMode;
-        SkUnitMapper* radialMapper = new SkCosineMapper();
-        SkAutoUnref unmapRadialMapper(radialMapper);
-        SkShader* radial = SkGradientShader::CreateRadial(radialCenter, 
-            radialRadius, radialColors, radialPos, radialCount,
-            radialMode, radialMapper);
-        
-        SkTransparentShader* transparentShader = new SkTransparentShader();
-        SkEmbossMaskFilter::Light light;
-        light.fDirection[0] = SK_Scalar1/2;
-        light.fDirection[1] = SK_Scalar1/2;
-        light.fDirection[2] = SK_Scalar1/3;
-        light.fAmbient		= 0x48;
-        light.fSpecular		= 0x80;
-        SkScalar radius = SkIntToScalar(12)/5;
-        SkEmbossMaskFilter* embossFilter = new SkEmbossMaskFilter(light, 
-            radius);
-            
-        SkXfermode* xfermode = SkXfermode::Create(SkXfermode::kXor_Mode);
-        SkColorFilter* lightingFilter = SkColorFilter::CreateLightingFilter(
-            0xff89bc45, 0xff112233);
-        
-        canvas->save();
-		canvas->translate(SkIntToScalar(0), SkIntToScalar(5));
-		paint.setFlags(SkPaint::kAntiAlias_Flag | SkPaint::kFilterBitmap_Flag);
-		// !!! draw through a clip
-		paint.setColor(SK_ColorLTGRAY);
-		paint.setStyle(SkPaint::kFill_Style);
-        SkRect clip = {0, 0, SkIntToScalar(320), SkIntToScalar(120)};
-        canvas->clipRect(clip);
-        paint.setShader(SkShader::CreateBitmapShader(fTx, 
-            SkShader::kMirror_TileMode, SkShader::kRepeat_TileMode))->unref();
-		canvas->drawPaint(paint);
-		canvas->save();
-        
-        // line (exercises xfermode, colorShader, colorFilter, filterShader)
-		paint.setColor(SK_ColorGREEN);
-		paint.setStrokeWidth(SkIntToScalar(10));
-		paint.setStyle(SkPaint::kStroke_Style);
-        paint.setXfermode(xfermode)->unref();
-        paint.setColorFilter(lightingFilter)->unref();
-		canvas->drawLine(start.fX, start.fY, stop.fX, stop.fY, paint); // should not be green
-		paint.setXfermode(NULL);
-        paint.setColorFilter(NULL);
-        
-        // rectangle
-		paint.setStyle(SkPaint::kFill_Style);
-		canvas->translate(SkIntToScalar(50), 0);
-		paint.setColor(SK_ColorYELLOW);
-        paint.setShader(linear)->unref();
-        paint.setPathEffect(pathEffectTest())->unref();
-		canvas->drawRect(rect, paint); 
-        paint.setPathEffect(NULL);
-        
-        // circle w/ emboss & transparent (exercises 3dshader)
-		canvas->translate(SkIntToScalar(50), 0);
-        paint.setMaskFilter(embossFilter)->unref();
-        canvas->drawOval(rect, paint);
-		canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-        paint.setShader(transparentShader)->unref();
-        canvas->drawOval(rect, paint);
-		canvas->translate(0, SkIntToScalar(-10));
-        
-        // path
-		canvas->translate(SkIntToScalar(50), 0);
-		paint.setColor(SK_ColorRED);
-		paint.setStyle(SkPaint::kStroke_Style);
-		paint.setStrokeWidth(SkIntToScalar(5));
-        paint.setShader(radial)->unref();
-        paint.setMaskFilter(NULL);
-		canvas->drawPath(path, paint);
-		
-        paint.setShader(NULL);
-        // bitmap, sprite
-		canvas->translate(SkIntToScalar(50), 0);
-		paint.setStyle(SkPaint::kFill_Style);
-		canvas->drawBitmap(fBug, left, top, &paint);
-		canvas->translate(SkIntToScalar(30), 0);
-		canvas->drawSprite(fTb, 
-			SkScalarRound(canvas->getTotalMatrix().getTranslateX()), 
-            spriteOffset + 10, &paint);
-
-		canvas->translate(-SkIntToScalar(30), SkIntToScalar(30));
-        paint.setShader(shaderTest())->unref(); // test compose shader
-		canvas->drawRect(rect2, paint); 
-        paint.setShader(NULL);
-		
-        canvas->restore();
-        // text
-		canvas->translate(0, SkIntToScalar(60));
-        canvas->save();
-		paint.setColor(SK_ColorGRAY);
-		canvas->drawPosText(ascii, asciiLength, pos.begin(), paint);
-		canvas->drawPosText(ascii, asciiLength, pos2.begin(), paint);
-
-		canvas->translate(SkIntToScalar(50), 0);
-		paint.setColor(SK_ColorCYAN);
-		canvas->drawText(utf8, sizeof(utf8) - 1, x, y, paint);
-       
-		canvas->translate(SkIntToScalar(30), 0);
-		paint.setColor(SK_ColorMAGENTA);
-		paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-        matrix.setTranslate(SkIntToScalar(10), SkIntToScalar(10));
-		canvas->drawTextOnPath((void*) utf16, sizeof(utf16), path, &matrix, paint);
-		canvas->translate(0, SkIntToScalar(20));
-		canvas->drawTextOnPath((void*) utf16simple, sizeof(utf16simple), path, &matrix, paint);
-        canvas->restore();
-        
-        canvas->translate(0, SkIntToScalar(60));
-		paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
-        canvas->restore();
-    }
-    
-    /*
-./SkColorFilter.h:25:class SkColorFilter : public SkFlattenable { -- abstract
-    static SkColorFilter* CreatXfermodeFilter() *** untested ***
-    static SkColorFilter* CreatePorterDuffFilter() *** untested ***
-    static SkColorFilter* CreateLightingFilter() -- tested
-./SkDrawLooper.h:9:class SkDrawLooper : public SkFlattenable { -- virtually abstract
-    ./SkBlurDrawLooper.h:9:class SkBlurDrawLooper : public SkDrawLooper { *** untested ***
-./SkMaskFilter.h:41:class SkMaskFilter : public SkFlattenable { -- abstract chmod +w .h
-    ./SkEmbossMaskFilter.h:27:class SkEmbossMaskFilter : public SkMaskFilter { -- tested
-./SkPathEffect.h:33:class SkPathEffect : public SkFlattenable { -- abstract
-    ./Sk1DPathEffect.h:27:class Sk1DPathEffect : public SkPathEffect { -- abstract
-        ./Sk1DPathEffect.h:48:class SkPath1DPathEffect : public Sk1DPathEffect { -- tested
-    ./Sk2DPathEffect.h:25:class Sk2DPathEffect : public SkPathEffect { *** untested ***
-    ./SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect { *** untested ***
-    ./SkDashPathEffect.h:27:class SkDashPathEffect : public SkPathEffect {
-    ./SkDiscretePathEffect.h:27:class SkDiscretePathEffect : public SkPathEffect {
-    ./SkPaint.h:760:class SkStrokePathEffect : public SkPathEffect {
-    ./SkPathEffect.h:58:class SkPairPathEffect : public SkPathEffect {
-        ./SkPathEffect.h:78:class SkComposePathEffect : public SkPairPathEffect {
-        ./SkPathEffect.h:114:class SkSumPathEffect : public SkPairPathEffect {
-./SkRasterizer.h:29:class SkRasterizer : public SkFlattenable {
-    ./SkLayerRasterizer.h:27:class SkLayerRasterizer : public SkRasterizer {
-./SkShader.h:36:class SkShader : public SkFlattenable {
-    ./SkColorFilter.h:59:class SkFilterShader : public SkShader {
-    ./SkColorShader.h:26:class SkColorShader : public SkShader {
-    ./SkShaderExtras.h:31:class SkComposeShader : public SkShader {
-    ./SkTransparentShader.h:23:class SkTransparentShader : public SkShader {
-./SkUnitMapper.h:24:class SkUnitMapper : public SkFlattenable {
-    ./SkUnitMapper.h:33:class SkDiscreteMapper : public SkUnitMapper {
-    ./SkUnitMapper.h:51:class SkFlipCosineMapper : public SkUnitMapper {
-./SkXfermode.h:32:class SkXfermode : public SkFlattenable {
-    ./SkAvoidXfermode.h:28:class SkAvoidXfermode : public SkXfermode { *** not done *** chmod +w .h .cpp
-    ./SkXfermode.h:54:class SkProcXfermode : public SkXfermode {
-    */
-    
-    /*
-./SkBlurMaskFilter.h:25:class SkBlurMaskFilter {
-    chmod +w SkBlurMaskFilter.cpp
-./SkGradientShader.h:30:class SkGradientShader {
-    */
-        // save layer, bounder, looper
-        // matrix
-        // clip /path/region
-        // bitmap proc shader ?
-
-/* untested:
-SkCornerPathEffect.h:28:class SkCornerPathEffect : public SkPathEffect {
-*/
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fClickPt.set(x, y);
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    SkPathEffect* pathEffectTest() {
-        static const int gXY[] = { 1, 0, 0, -1, 2, -1, 3, 0, 2, 1, 0, 1 };
-        SkScalar gPhase = 0;
-        SkPath path;
-        path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
-        for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
-            path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
-        path.close();
-        path.offset(SkIntToScalar(-6), 0);
-        SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), 
-            gPhase, SkPath1DPathEffect::kRotate_Style);
-        SkPathEffect* inner = new SkDiscretePathEffect(SkIntToScalar(2), 
-            SkIntToScalar(1)/10); // SkCornerPathEffect(SkIntToScalar(2));
-        SkPathEffect* result = new SkComposePathEffect(outer, inner);
-        outer->unref();
-        inner->unref();
-        return result;
-    }
-    
-    SkPathEffect* pathEffectTest2() { // unsure this works (has no visible effect)
-        SkPathEffect* outer = new SkStrokePathEffect(SkIntToScalar(4), 
-            SkPaint::kStroke_Style, SkPaint::kMiter_Join, SkPaint::kButt_Cap);
-        static const SkScalar intervals[] = {SkIntToScalar(1), SkIntToScalar(2),
-            SkIntToScalar(2), SkIntToScalar(1)};
-        SkPathEffect* inner = new SkDashPathEffect(intervals, 
-            sizeof(intervals) / sizeof(intervals[0]), 0);
-        SkPathEffect* result = new SkSumPathEffect(outer, inner);
-        outer->unref();
-        inner->unref();
-        return result;
-    }
-    
-    SkShader* shaderTest() {
-        SkPoint pts[] = { { 0, 0, }, { SkIntToScalar(100), 0 } };
-        SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
-        SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 
-            2, SkShader::kClamp_TileMode);
-        pts[1].set(0, SkIntToScalar(100));
-        SkColor colors2[] = {SK_ColorBLACK,  SkColorSetARGB(0x80, 0, 0, 0)};
-        SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors2, NULL, 
-            2, SkShader::kClamp_TileMode);
-        SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
-        SkShader* result = new SkComposeShader(shaderA, shaderB, mode);
-        shaderA->unref();
-        shaderB->unref();
-        mode->unref();
-        return result;
-    }
-
-    virtual void startTest() {
-		SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/bugcirc.gif", &fBug);
-		SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/tbcirc.gif", &fTb);
-		SkImageDecoder::DecodeFile("/Users/caryclark/Desktop/05psp04.gif", &fTx);
-	}
-
-    void drawRaster(SkCanvas* canvas)  {
-        for (size_t index = 0; index < SK_ARRAY_COUNT(gRastProcs); index++)
-            drawOneRaster(canvas);
-    }
-    
-    void drawOneRaster(SkCanvas* canvas) {
-        canvas->save();
-
-        SkScalar    x = SkIntToScalar(20);
-        SkScalar    y = SkIntToScalar(40);
-        SkPaint     paint;
-        
-        paint.setAntiAlias(true);
-        paint.setTextSize(SkIntToScalar(48));
-        paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
-                                                     SkTypeface::kBold));
-
-        SkString str("GOOGLE");
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
-            apply_shader(&paint, i);
-            
-          //  paint.setMaskFilter(NULL);
-          //  paint.setColor(SK_ColorBLACK);
-
-#if 01
-            int index = i % SK_ARRAY_COUNT(gLightingColors);
-            paint.setColorFilter(SkColorFilter::CreateLightingFilter(
-                                    gLightingColors[index].fMul,
-                                    gLightingColors[index].fAdd))->unref();
-#endif
-            
-            canvas->drawText(str.c_str(), str.size(), x, y, paint);
-            SkRect  oval = { x, y - SkIntToScalar(40), x + SkIntToScalar(40), y };
-            paint.setStyle(SkPaint::kStroke_Style);
-            canvas->drawOval(oval, paint);
-            paint.setStyle(SkPaint::kFill_Style);
-
-            y += paint.getFontSpacing();
-        }
-
-        canvas->restore();
-        
-        if (1) {
-            SkAvoidXfermode   mode(SK_ColorWHITE, 0xFF,
-                                   SkAvoidXfermode::kTargetColor_Mode);
-            SkPaint paint;
-            x += SkIntToScalar(20);
-            SkRect  r = { x, 0, x + SkIntToScalar(360), SkIntToScalar(700) };
-            paint.setXfermode(&mode);
-            paint.setColor(SK_ColorGREEN);
-            paint.setAntiAlias(true);
-            canvas->drawOval(r, paint);
-        }
-    }
-
-private:
-    SkPoint fClickPt;
-    SkBitmap fBug, fTb, fTx;
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DemoView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAnimatedGradient.cpp b/samplecode/SampleAnimatedGradient.cpp
deleted file mode 100644
index 3caff1c..0000000
--- a/samplecode/SampleAnimatedGradient.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-
-class GradientView : public SampleView {
-public:
-	GradientView() {
-        this->setBGColor(0xFFDDDDDD);
-	}
-	
-protected:
-	struct GradData {
-        int             fCount;
-        const SkColor*  fColors;
-        const SkScalar* fPos;
-    };
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Gradients");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
-        paint.setStyle(SkPaint::kFill_Style);
-        
-        SkPoint p = SkPoint::Make(0,0);
-        SkPoint q = SkPoint::Make(100,100);
-        SkPoint pts[] = {p, q};
-        
-        SkScalar t, temp, x, y;
-        SkColor gColors[] = {
-            SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
-        };
-        t =    SampleCode::GetAnimScalar(SkIntToScalar(2), SkIntToScalar(20));
-        temp = SampleCode::GetAnimScalar(SkIntToScalar(1), SkIntToScalar(8));
-        SkScalar step = SK_ScalarPI / (10);
-        SkScalar angle = t * step;
-        x =  SkScalarSinCos(angle, &y);
-        SkScalar colorPositions[] = { 0, 0.1 + x, 0.4 + y, 0.9 - x + y, 1.0};
-        GradData data = { 5, gColors, colorPositions };
-        
-        
-        SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(200) };
-        SkShader* shader1 = SkGradientShader::CreateLinear(
-                           pts, data.fColors, data.fPos,data.fCount, 
-                           SkShader::kMirror_TileMode);
-        paint.setShader(shader1)->unref();
-        
-        canvas->drawRect(r, paint);
-        
-        
-        SkPoint s = SkPoint::Make(100,100);
-        SkShader* shader2 = SkGradientShader::CreateRadial(
-                           s, 100, data.fColors, data.fPos, data.fCount, 
-                           SkShader::kMirror_TileMode);
-        paint.setShader(shader2)->unref();
-        canvas->translate(250, 0);
-        canvas->drawRect(r, paint);
-        
-        SkShader* shader3 = SkGradientShader::CreateTwoPointRadial(
-                           p, 0, q, 100, data.fColors, data.fPos, data.fCount,
-                           SkShader::kMirror_TileMode);
-        paint.setShader(shader3)->unref();
-        canvas->translate(0, 250);
-        canvas->drawRect(r, paint);
-        
-        SkShader* shader4 = SkGradientShader::CreateSweep(
-                            100, 100, data.fColors, data.fPos, data.fCount);
- 
-        paint.setShader(shader4)->unref();
-        canvas->translate(-250, 0);
-        canvas->drawRect(r, paint);
-        
-        this->inval(NULL);		
-    }
-	
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new GradientView; }
-static SkViewRegister reg(MyFactory);
\ No newline at end of file
diff --git a/samplecode/SampleAnimator.cpp b/samplecode/SampleAnimator.cpp
deleted file mode 100644
index 09742a0..0000000
--- a/samplecode/SampleAnimator.cpp
+++ /dev/null
@@ -1,175 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-
-#include "SkAnimator.h"
-#include "SkStream.h"
-#include "SkDOM.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SkAnimatorView : public SkView {
-public:
-    SkAnimatorView();
-    virtual ~SkAnimatorView();
-
-    void setURIBase(const char dir[]);
-
-    SkAnimator* getAnimator() const { return fAnimator; }
-    
-    bool    decodeFile(const char path[]);
-    bool    decodeMemory(const void* buffer, size_t size);
-    bool    decodeStream(SkStream* stream);
-    
-protected:
-    // overrides
-    virtual void onDraw(SkCanvas*);
-    virtual bool onQuery(SkEvent* evt);
-    
-private:
-    SkString fBaseURI;
-    SkAnimator* fAnimator;
-    
-    typedef SkView INHERITED;
-};
-
-SkAnimatorView::SkAnimatorView() : fAnimator(NULL) {}
-
-SkAnimatorView::~SkAnimatorView() {
-    delete fAnimator;
-}
-
-void SkAnimatorView::setURIBase(const char dir[]) {
-    fBaseURI.set(dir);
-}
-
-bool SkAnimatorView::decodeFile(const char path[]) {
-    SkFILEStream* is = new SkFILEStream(path);
-    SkAutoUnref aur(is);
-    return is->isValid() && this->decodeStream(is);
-}
-
-bool SkAnimatorView::decodeMemory(const void* buffer, size_t size) {
-    SkMemoryStream* is = new SkMemoryStream(buffer, size);
-    SkAutoUnref aur(is);
-    return this->decodeStream(is);
-}
-
-static const SkDOMNode* find_nodeID(const SkDOM& dom,
-						const SkDOMNode* node, const char name[]) {
-	if (NULL == node) {
-		node = dom.getRootNode();
-	}
-	do {
-		const char* idval = dom.findAttr(node, "id");
-		if (idval && !strcmp(idval, name)) {
-			return node;
-		}
-		const SkDOMNode* child = dom.getFirstChild(node);
-		if (child) {
-			const SkDOMNode* found = find_nodeID(dom, child, name);
-			if (found) {
-				return found;
-			}
-		}
-	} while ((node = dom.getNextSibling(node)) != NULL);
-	return NULL;
-}
-
-bool SkAnimatorView::decodeStream(SkStream* stream) {
-    delete fAnimator;
-    fAnimator = new SkAnimator;
-    fAnimator->setURIBase(fBaseURI.c_str());
-#if 0
-    if (!fAnimator->decodeStream(stream)) {
-        delete fAnimator;
-        fAnimator = NULL;
-        return false;
-    }
-#else
-	size_t len = stream->getLength();
-	char* text = (char*)sk_malloc_throw(len);
-	stream->read(text, len);
-	SkDOM dom;
-	const SkDOM::Node* root = dom.build(text, len);
-	if (NULL == root) {
-		return false;
-	}
-	if (!fAnimator->decodeDOM(dom, root)) {
-		delete fAnimator;
-		fAnimator = NULL;
-		return false;
-	}
-	for (int i = 0; i <= 10; i++) {
-		SkString name("glyph");
-		name.appendS32(i);
-		const SkDOM::Node* node = find_nodeID(dom, NULL, name.c_str());
-		SkASSERT(node);
-		SkRect r;
-		dom.findScalar(node, "left", &r.fLeft);
-		dom.findScalar(node, "top", &r.fTop);
-		dom.findScalar(node, "width", &r.fRight); r.fRight += r.fLeft;
-		dom.findScalar(node, "height", &r.fBottom); r.fBottom += r.fTop;
-		SkDebugf("--- %s [%g %g %g %g]\n", name.c_str(),
-				 r.fLeft, r.fTop, r.fRight, r.fBottom);
-	}
-#endif
-    return true;
-}
-
-#include "SkTime.h"
-
-void SkAnimatorView::onDraw(SkCanvas* canvas) {
-    canvas->drawColor(SK_ColorWHITE);
-    if (fAnimator) {
-        fAnimator->draw(canvas, 0);
-#if 0
-        canvas->save();
-        canvas->translate(120, 30);
-        canvas->scale(0.5, 0.5);
-        fAnimator->draw(canvas, 0);
-        canvas->restore();
-        
-        canvas->save();
-        canvas->translate(190, 40);
-        canvas->scale(0.25, 0.25);
-        fAnimator->draw(canvas, 0);
-        canvas->restore();
-        
-        this->inval(NULL);
-#endif
-    }
-}
-
-bool SkAnimatorView::onQuery(SkEvent* evt) {
-    if (SampleCode::TitleQ(*evt)) {
-        SampleCode::TitleR(evt, "Animator");
-        return true;
-    }
-    return this->INHERITED::onQuery(evt);
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() {
-    SkAnimatorView* av = new SkAnimatorView;
-//    av->decodeFile("/skimages/test.xml");
-#if 0
-    av->setURIBase("/skia/trunk/animations/");
-    av->decodeFile("/skia/trunk/animations/checkbox.xml");
-#else
-	av->setURIBase("/");
-	av->decodeFile("/testanim.txt");
-#endif
-    return av;
-}
-
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleApp.cpp b/samplecode/SampleApp.cpp
deleted file mode 100644
index 51d42b6..0000000
--- a/samplecode/SampleApp.cpp
+++ /dev/null
@@ -1,2163 +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 "SampleApp.h"
-
-#include "SkData.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkGpuDevice.h"
-#include "SkGraphics.h"
-#include "SkImageEncoder.h"
-#include "SkPaint.h"
-#include "SkPicture.h"
-#include "SkStream.h"
-#include "SkTime.h"
-#include "SkWindow.h"
-
-#include "SampleCode.h"
-#include "GrContext.h"
-#include "SkTypeface.h"
-
-#include "gl/GrGLInterface.h"
-#include "GrRenderTarget.h"
-
-#include "SkPDFDevice.h"
-#include "SkPDFDocument.h"
-#include "SkStream.h"
-
-#define TEST_GPIPE
-
-#ifdef  TEST_GPIPE
-#define PIPE_FILEx
-#ifdef  PIPE_FILE
-#define FILE_PATH "/path/to/drawing.data"
-#endif
-
-#define PIPE_NETx
-#ifdef  PIPE_NET
-#include "SkSockets.h"
-SkTCPServer gServer;
-#endif
-
-#define DEBUGGERx
-#ifdef  DEBUGGER
-extern SkView* create_debugger(const char* data, size_t size);
-extern bool is_debugger(SkView* view);
-SkTDArray<char> gTempDataStore;
-#endif
-
-#endif
-
-#define USE_ARROWS_FOR_ZOOM true
-//#define DEFAULT_TO_GPU
-
-extern SkView* create_overview(int, const SkViewFactory*[]);
-extern bool is_overview(SkView* view);
-//extern SkView* create_transition(SkView*, SkView*, int);
-//extern bool is_transition(SkView* view);
-
-
-#define ANIMATING_EVENTTYPE "nextSample"
-#define ANIMATING_DELAY     750
-
-#ifdef SK_DEBUG
-    #define FPS_REPEAT_MULTIPLIER   1
-#else
-    #define FPS_REPEAT_MULTIPLIER   10
-#endif
-#define FPS_REPEAT_COUNT    (10 * FPS_REPEAT_MULTIPLIER)
-
-static SampleWindow* gSampleWindow;
-
-static void postEventToSink(SkEvent* evt, SkEventSink* sink) {
-    evt->setTargetID(sink->getSinkID())->post();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const char* skip_until(const char* str, const char* skip) {
-    if (!str) {
-        return NULL;
-    }
-    return strstr(str, skip);
-}
-
-static const char* skip_past(const char* str, const char* skip) {
-    const char* found = skip_until(str, skip);
-    if (!found) {
-        return NULL;
-    }
-    return found + strlen(skip);
-}
-
-static const char* gPrefFileName = "sampleapp_prefs.txt";
-
-static bool readTitleFromPrefs(SkString* title) {
-    SkFILEStream stream(gPrefFileName);
-    if (!stream.isValid()) {
-        return false;
-    }
-
-    int len = stream.getLength();
-    SkString data(len);
-    stream.read(data.writable_str(), len);
-    const char* s = data.c_str();
-
-    s = skip_past(s, "curr-slide-title");
-    s = skip_past(s, "=");
-    s = skip_past(s, "\"");
-    const char* stop = skip_until(s, "\"");
-    if (stop > s) {
-        title->set(s, stop - s);
-        return true;
-    }
-    return false;
-}
-
-static void writeTitleToPrefs(const char* title) {
-    SkFILEWStream stream(gPrefFileName);
-    SkString data;
-    data.printf("curr-slide-title = \"%s\"\n", title);
-    stream.write(data.c_str(), data.size());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SampleWindow::DefaultDeviceManager : public SampleWindow::DeviceManager {
-public:
-
-    DefaultDeviceManager() {
-        fGrRenderTarget = NULL;
-        fGrContext = NULL;
-        fGL = NULL;
-        fNullGrContext = NULL;
-        fNullGrRenderTarget = NULL;
-    }
-
-    virtual ~DefaultDeviceManager() {
-        SkSafeUnref(fGrRenderTarget);
-        SkSafeUnref(fGrContext);
-        SkSafeUnref(fGL);
-        SkSafeUnref(fNullGrContext);
-        SkSafeUnref(fNullGrRenderTarget);
-    }
-
-    virtual void init(SampleWindow* win) {
-        if (!win->attachGL()) {
-            SkDebugf("Failed to initialize GL");
-        }
-        if (NULL == fGL) {
-            fGL = GrGLCreateNativeInterface();
-            GrAssert(NULL == fGrContext);
-            fGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine,
-                                           (GrPlatform3DContext) fGL);
-        }
-        if (NULL == fGrContext || NULL == fGL) {
-            SkSafeUnref(fGrContext);
-            SkSafeUnref(fGL);
-            SkDebugf("Failed to setup 3D");
-            win->detachGL();
-        }
-        if (NULL == fNullGrContext) {
-            const GrGLInterface* nullGL = GrGLCreateNullInterface();
-            fNullGrContext = GrContext::Create(kOpenGL_Shaders_GrEngine,
-                                               (GrPlatform3DContext) nullGL);
-            nullGL->unref();
-        }
-    }
-
-    virtual bool supportsDeviceType(SampleWindow::DeviceType dType) {
-        switch (dType) {
-            case kRaster_DeviceType:
-            case kPicture_DeviceType: // fallthru
-                return true;
-            case kGPU_DeviceType:
-                return NULL != fGrContext && NULL != fGrRenderTarget;
-            case kNullGPU_DeviceType:
-                return NULL != fNullGrContext && NULL != fNullGrRenderTarget;
-            default:
-                return false;
-        }
-    }
-
-    virtual bool prepareCanvas(SampleWindow::DeviceType dType,
-                               SkCanvas* canvas,
-                               SampleWindow* win) {
-        switch (dType) {
-            case kGPU_DeviceType:
-                if (fGrContext) {
-                    canvas->setDevice(new SkGpuDevice(fGrContext,
-                                                    fGrRenderTarget))->unref();
-                } else {
-                    return false;
-                }
-                break;
-            case kNullGPU_DeviceType:
-                if (fNullGrContext) {
-                    canvas->setDevice(new SkGpuDevice(fNullGrContext,
-                                                      fNullGrRenderTarget))->unref();
-                } else {
-                    return false;
-                }
-                break;
-            case kRaster_DeviceType:
-            case kPicture_DeviceType:
-                break;
-        }
-        return true;
-    }
-
-    virtual void publishCanvas(SampleWindow::DeviceType dType,
-                               SkCanvas* canvas,
-                               SampleWindow* win) {
-        if (fGrContext) {
-            // in case we have queued drawing calls
-            fGrContext->flush();
-            if (NULL != fNullGrContext) {
-                fNullGrContext->flush();
-            }
-            if (dType != kGPU_DeviceType &&
-                dType != kNullGPU_DeviceType) {
-                // need to send the raster bits to the (gpu) window
-                fGrContext->setRenderTarget(fGrRenderTarget);
-                const SkBitmap& bm = win->getBitmap();
-                fGrRenderTarget->writePixels(0, 0, bm.width(), bm.height(),
-                                             kSkia8888_PM_GrPixelConfig,
-                                             bm.getPixels(),
-                                             bm.rowBytes());
-            }
-        }
-        win->presentGL();
-    }
-
-    virtual void windowSizeChanged(SampleWindow* win) {
-        if (fGrContext) {
-            win->attachGL();
-
-            GrPlatformRenderTargetDesc desc;
-            desc.fWidth = SkScalarRound(win->width());
-            desc.fHeight = SkScalarRound(win->height());
-            desc.fConfig = kSkia8888_PM_GrPixelConfig;
-            GR_GL_GetIntegerv(fGL, GR_GL_SAMPLES, &desc.fSampleCnt);
-            GR_GL_GetIntegerv(fGL, GR_GL_STENCIL_BITS, &desc.fStencilBits);
-            GrGLint buffer;
-            GR_GL_GetIntegerv(fGL, GR_GL_FRAMEBUFFER_BINDING, &buffer);
-            desc.fRenderTargetHandle = buffer;
-
-            SkSafeUnref(fGrRenderTarget);
-            fGrRenderTarget = fGrContext->createPlatformRenderTarget(desc);
-        }
-        if (NULL != fNullGrContext) {
-            GrPlatformRenderTargetDesc desc;
-            desc.fWidth = SkScalarRound(win->width());
-            desc.fHeight = SkScalarRound(win->height());
-            desc.fConfig = kSkia8888_PM_GrPixelConfig;
-            desc.fStencilBits = 8;
-            desc.fSampleCnt = 0;
-            desc.fRenderTargetHandle = 0;
-            fNullGrRenderTarget = fNullGrContext->createPlatformRenderTarget(desc);
-        }
-    }
-
-    virtual GrContext* getGrContext(SampleWindow::DeviceType dType) {
-        if (kNullGPU_DeviceType == dType) {
-            return fNullGrContext;
-        } else {
-            return fGrContext;
-        }
-    }
-private:
-    GrContext* fGrContext;
-    const GrGLInterface* fGL;
-    GrRenderTarget* fGrRenderTarget;
-    GrContext* fNullGrContext;
-    GrRenderTarget* fNullGrRenderTarget;
-};
-
-///////////////
-static const char view_inval_msg[] = "view-inval-msg";
-
-void SampleWindow::postInvalDelay() {
-    (new SkEvent(view_inval_msg, this->getSinkID()))->postDelay(1);
-}
-
-static bool isInvalEvent(const SkEvent& evt) {
-    return evt.isType(view_inval_msg);
-}
-//////////////////
-
-SkFuncViewFactory::SkFuncViewFactory(SkViewCreateFunc func)
-    : fCreateFunc(func) {
-}
-
-SkView* SkFuncViewFactory::operator() () const {
-    return (*fCreateFunc)();
-}
-
-#include "GMSampleView.h"
-
-SkGMSampleViewFactory::SkGMSampleViewFactory(GMFactoryFunc func)
-    : fFunc(func) {
-}
-
-SkView* SkGMSampleViewFactory::operator() () const {
-    return new GMSampleView(fFunc(NULL));
-}
-
-SkViewRegister* SkViewRegister::gHead;
-SkViewRegister::SkViewRegister(SkViewFactory* fact) : fFact(fact) {
-    fFact->ref();
-    fChain = gHead;
-    gHead = this;
-}
-
-SkViewRegister::SkViewRegister(SkViewCreateFunc func) {
-    fFact = new SkFuncViewFactory(func);
-    fChain = gHead;
-    gHead = this;
-}
-
-SkViewRegister::SkViewRegister(GMFactoryFunc func) {
-    fFact = new SkGMSampleViewFactory(func);
-    fChain = gHead;
-    gHead = this;
-}
-
-class AutoUnrefArray {
-public:
-    AutoUnrefArray() {}
-    ~AutoUnrefArray() {
-        int count = fObjs.count();
-        for (int i = 0; i < count; ++i) {
-            fObjs[i]->unref();
-        }
-    }
-    SkRefCnt*& push_back() { return *fObjs.append(); }
-    
-private:
-    SkTDArray<SkRefCnt*> fObjs;
-};
-
-// registers GMs as Samples
-// This can't be performed during static initialization because it could be
-// run before GMRegistry has been fully built.
-void SkGMRegistyToSampleRegistry() {
-    static bool gOnce;
-    static AutoUnrefArray fRegisters; 
-
-    if (!gOnce) {
-        const skiagm::GMRegistry* gmreg = skiagm::GMRegistry::Head();
-        while (gmreg) {
-            fRegisters.push_back() = new SkViewRegister(gmreg->factory());
-            gmreg = gmreg->next();
-        }
-        gOnce = true;
-    }
-}
-
-#if 0
-#include <CoreFoundation/CoreFoundation.h>
-#include <CoreFoundation/CFURLAccess.h>
-
-static void testpdf() {
-    CFStringRef path = CFStringCreateWithCString(NULL, "/test.pdf",
-                                                 kCFStringEncodingUTF8);
-    CFURLRef url = CFURLCreateWithFileSystemPath(NULL, path,
-                                              kCFURLPOSIXPathStyle,
-                                              false);
-    CFRelease(path);
-    CGRect box = CGRectMake(0, 0, 8*72, 10*72);
-    CGContextRef cg = CGPDFContextCreateWithURL(url, &box, NULL);
-    CFRelease(url);
-
-    CGContextBeginPage(cg, &box);
-    CGRect r = CGRectMake(10, 10, 40 + 0.5, 50 + 0.5);
-    CGContextFillEllipseInRect(cg, r);
-    CGContextEndPage(cg);
-    CGContextRelease(cg);
-
-    if (false) {
-        SkBitmap bm;
-        bm.setConfig(SkBitmap::kA8_Config, 64, 64);
-        bm.allocPixels();
-        bm.eraseColor(0);
-
-        SkCanvas canvas(bm);
-
-    }
-}
-#endif
-
-//////////////////////////////////////////////////////////////////////////////
-
-enum FlipAxisEnum {
-    kFlipAxis_X = (1 << 0),
-    kFlipAxis_Y = (1 << 1)
-};
-
-#include "SkDrawFilter.h"
-
-class FlagsDrawFilter : public SkDrawFilter {
-public:
-    FlagsDrawFilter(SkOSMenu::TriState lcd, SkOSMenu::TriState aa, SkOSMenu::TriState filter,
-                    SkOSMenu::TriState hinting) :
-        fLCDState(lcd), fAAState(aa), fFilterState(filter), fHintingState(hinting) {}
-
-    virtual void filter(SkPaint* paint, Type t) {
-        if (kText_Type == t && SkOSMenu::kMixedState != fLCDState) {
-            paint->setLCDRenderText(SkOSMenu::kOnState == fLCDState);
-        }
-        if (SkOSMenu::kMixedState != fAAState) {
-            paint->setAntiAlias(SkOSMenu::kOnState == fAAState);
-        }
-        if (SkOSMenu::kMixedState != fFilterState) {
-            paint->setFilterBitmap(SkOSMenu::kOnState == fFilterState);
-        }
-        if (SkOSMenu::kMixedState != fHintingState) {
-            paint->setHinting(SkOSMenu::kOnState == fHintingState ?
-                              SkPaint::kNormal_Hinting :
-                              SkPaint::kSlight_Hinting);
-        }
-    }
-
-private:
-    SkOSMenu::TriState  fLCDState;
-    SkOSMenu::TriState  fAAState;
-    SkOSMenu::TriState  fFilterState;
-    SkOSMenu::TriState  fHintingState;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-#define MAX_ZOOM_LEVEL  8
-#define MIN_ZOOM_LEVEL  -8
-
-static const char gCharEvtName[] = "SampleCode_Char_Event";
-static const char gKeyEvtName[] = "SampleCode_Key_Event";
-static const char gTitleEvtName[] = "SampleCode_Title_Event";
-static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
-static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
-static const char gUpdateWindowTitleEvtName[] = "SampleCode_UpdateWindowTitle";
-
-bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) {
-    if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) {
-        if (outUni) {
-            *outUni = evt.getFast32();
-        }
-        return true;
-    }
-    return false;
-}
-
-bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) {
-    if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) {
-        if (outKey) {
-            *outKey = (SkKey)evt.getFast32();
-        }
-        return true;
-    }
-    return false;
-}
-
-bool SampleCode::TitleQ(const SkEvent& evt) {
-    return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1);
-}
-
-void SampleCode::TitleR(SkEvent* evt, const char title[]) {
-    SkASSERT(evt && TitleQ(*evt));
-    evt->setString(gTitleEvtName, title);
-}
-
-bool SampleCode::RequestTitle(SkView* view, SkString* title) {
-    SkEvent evt(gTitleEvtName);
-    if (view->doQuery(&evt)) {
-        title->set(evt.findString(gTitleEvtName));
-        return true;
-    }
-    return false;
-}
-
-bool SampleCode::PrefSizeQ(const SkEvent& evt) {
-    return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1);
-}
-
-void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) {
-    SkASSERT(evt && PrefSizeQ(*evt));
-    SkScalar size[2];
-    size[0] = width;
-    size[1] = height;
-    evt->setScalars(gPrefSizeEvtName, 2, size);
-}
-
-bool SampleCode::FastTextQ(const SkEvent& evt) {
-    return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkMSec gAnimTime;
-static SkMSec gAnimTimePrev;
-
-SkMSec SampleCode::GetAnimTime() { return gAnimTime; }
-SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; }
-SkScalar SampleCode::GetAnimSecondsDelta() {
-    return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0);
-}
-
-SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) {
-    // since gAnimTime can be up to 32 bits, we can't convert it to a float
-    // or we'll lose the low bits. Hence we use doubles for the intermediate
-    // calculations
-    double seconds = (double)gAnimTime / 1000.0;
-    double value = SkScalarToDouble(speed) * seconds;
-    if (period) {
-        value = ::fmod(value, SkScalarToDouble(period));
-    }
-    return SkDoubleToScalar(value);
-}
-
-GrContext* SampleCode::GetGr() {
-    return gSampleWindow ? gSampleWindow->getGrContext() : NULL;
-}
-
-// some GMs rely on having a skiagm::GetGr function defined
-namespace skiagm {
-    GrContext* GetGr() { return SampleCode::GetGr(); }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* curr_view(SkWindow* wind) {
-    SkView::F2BIter iter(wind);
-    return iter.next();
-}
-
-static bool curr_title(SkWindow* wind, SkString* title) {
-    SkView* view = curr_view(wind);
-    if (view) {
-        SkEvent evt(gTitleEvtName);
-        if (view->doQuery(&evt)) {
-            title->set(evt.findString(gTitleEvtName));
-            return true;
-        }
-    }
-    return false;
-}
-
-void SampleWindow::setZoomCenter(float x, float y)
-{
-    fZoomCenterX = SkFloatToScalar(x);
-    fZoomCenterY = SkFloatToScalar(y);
-}
-
-bool SampleWindow::zoomIn()
-{
-    // Arbitrarily decided
-    if (fFatBitsScale == 25) return false;
-    fFatBitsScale++;
-    this->inval(NULL);
-    return true;
-}
-
-bool SampleWindow::zoomOut()
-{
-    if (fFatBitsScale == 1) return false;
-    fFatBitsScale--;
-    this->inval(NULL);
-    return true;
-}
-
-void SampleWindow::updatePointer(int x, int y)
-{
-    fMouseX = x;
-    fMouseY = y;
-    if (fShowZoomer) {
-        this->inval(NULL);
-    }
-}
-
-static inline SampleWindow::DeviceType cycle_devicetype(SampleWindow::DeviceType ct) {
-    static const SampleWindow::DeviceType gCT[] = {
-        SampleWindow::kPicture_DeviceType,
-        SampleWindow::kGPU_DeviceType,
-        SampleWindow::kRaster_DeviceType, // skip the null gpu device in normal cycling
-        SampleWindow::kRaster_DeviceType
-    };
-    return gCT[ct];
-}
-
-SampleWindow::SampleWindow(void* hwnd, int argc, char** argv, DeviceManager* devManager) : INHERITED(hwnd) {
-    gSampleWindow = this;
-
-#ifdef  PIPE_FILE
-    //Clear existing file or create file if it doesn't exist
-    FILE* f = fopen(FILE_PATH, "wb");
-    fclose(f);
-#endif
-     
-    fPicture = NULL;
-    
-#ifdef DEFAULT_TO_GPU
-    fDeviceType = kGPU_DeviceType;
-#else
-    fDeviceType = kRaster_DeviceType;
-#endif
-    fUseClip = false;
-    fNClip = false;
-    fAnimating = false;
-    fRotate = false;
-    fPerspAnim = false;
-    fPerspAnimTime = 0;
-    fScale = false;
-    fRequestGrabImage = false;
-    fUsePipe = false;
-    fMeasureFPS = false;
-    fLCDState = SkOSMenu::kMixedState;
-    fAAState = SkOSMenu::kMixedState;
-    fFilterState = SkOSMenu::kMixedState;
-    fHintingState = SkOSMenu::kMixedState;
-    fFlipAxis = 0;
-    fScrollTestX = fScrollTestY = 0;
-
-    fMouseX = fMouseY = 0;
-    fFatBitsScale = 8;
-    fTypeface = SkTypeface::CreateFromTypeface(NULL, SkTypeface::kBold);
-    fShowZoomer = false;
-    
-    fZoomLevel = 0;
-    fZoomScale = SK_Scalar1;
-    
-    fMagnify = false;
-    fDebugger = false;
-    
-    fSaveToPdf = false;
-    fPdfCanvas = NULL;
-
-    fTransitionNext = 6;
-    fTransitionPrev = 2;
-    
-    int sinkID = this->getSinkID();
-    fAppMenu.setTitle("Global Settings");
-    int itemID;
-    
-    itemID =fAppMenu.appendList("Device Type", "Device Type", sinkID, 0, 
-                                "Raster", "Picture", "OpenGL", NULL);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'd');
-    itemID = fAppMenu.appendTriState("AA", "AA", sinkID, fAAState);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'b');
-    itemID = fAppMenu.appendTriState("LCD", "LCD", sinkID, fLCDState);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'l');
-    itemID = fAppMenu.appendTriState("Filter", "Filter", sinkID, fFilterState);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'n');
-    itemID = fAppMenu.appendTriState("Hinting", "Hinting", sinkID, fHintingState);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'h');
-    fUsePipeMenuItemID = fAppMenu.appendSwitch("Pipe", "Pipe" , sinkID, fUsePipe);    
-    fAppMenu.assignKeyEquivalentToItem(fUsePipeMenuItemID, 'p');
-#ifdef DEBUGGER
-    itemID = fAppMenu.appendSwitch("Debugger", "Debugger", sinkID, fDebugger);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'q');
-#endif
-    itemID = fAppMenu.appendSwitch("Slide Show", "Slide Show" , sinkID, false);    
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'a');    
-    itemID = fAppMenu.appendSwitch("Clip", "Clip" , sinkID, fUseClip);    
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'c');
-    itemID = fAppMenu.appendSwitch("Flip X", "Flip X" , sinkID, false); 
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'x');
-    itemID = fAppMenu.appendSwitch("Flip Y", "Flip Y" , sinkID, false);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'y');
-    itemID = fAppMenu.appendSwitch("Zoomer", "Zoomer" , sinkID, fShowZoomer);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'z');
-    itemID = fAppMenu.appendSwitch("Magnify", "Magnify" , sinkID, fMagnify);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'm');
-    itemID =fAppMenu.appendList("Transition-Next", "Transition-Next", sinkID, 
-                                fTransitionNext, "Up", "Up and Right", "Right", 
-                                "Down and Right", "Down", "Down and Left", 
-                                "Left", "Up and Left", NULL);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'j');
-    itemID =fAppMenu.appendList("Transition-Prev", "Transition-Prev", sinkID, 
-                                fTransitionPrev, "Up", "Up and Right", "Right", 
-                                "Down and Right", "Down", "Down and Left", 
-                                "Left", "Up and Left", NULL);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'k');
-    itemID = fAppMenu.appendAction("Save to PDF", sinkID);
-    fAppMenu.assignKeyEquivalentToItem(itemID, 'e');
-    
-    this->addMenu(&fAppMenu);
-    this->addMenu(&fSlideMenu);
-    
-//    this->setConfig(SkBitmap::kRGB_565_Config);
-    this->setConfig(SkBitmap::kARGB_8888_Config);
-    this->setVisibleP(true);
-    this->setClipToBounds(false);
-
-    SkGMRegistyToSampleRegistry();
-    {
-        const SkViewRegister* reg = SkViewRegister::Head();
-        while (reg) {
-            *fSamples.append() = reg->factory();
-            reg = reg->next();
-        }
-    }
-    fCurrIndex = 0;
-    if (argc > 1) {
-        fCurrIndex = findByTitle(argv[1]);
-        if (fCurrIndex < 0) {
-            fprintf(stderr, "Unknown sample \"%s\"\n", argv[1]);
-        }
-    } else {
-        SkString title;
-        if (readTitleFromPrefs(&title)) {
-            fCurrIndex = findByTitle(title.c_str());
-        }
-    }
-
-    if (fCurrIndex < 0) {
-        fCurrIndex = 0;
-    }
-    this->loadView((*fSamples[fCurrIndex])());
-    
-    fPDFData = NULL;
-
-    if (NULL == devManager) {
-        fDevManager = new DefaultDeviceManager();
-    } else {
-        devManager->ref();
-        fDevManager = devManager;
-    }
-    fDevManager->init(this);
-
-    // If another constructor set our dimensions, ensure that our
-    // onSizeChange gets called.
-    if (this->height() && this->width()) {
-        this->onSizeChange();
-    }
-
-    // can't call this synchronously, since it may require a subclass to
-    // to implement, or the caller may need us to have returned from the
-    // constructor first. Hence we post an event to ourselves.
-//    this->updateTitle();
-    postEventToSink(new SkEvent(gUpdateWindowTitleEvtName), this);
-}
-
-SampleWindow::~SampleWindow() {
-    delete fPicture;
-    delete fPdfCanvas;
-    fTypeface->unref();
-
-    SkSafeUnref(fDevManager);
-}
-
-int SampleWindow::findByTitle(const char title[]) {
-    int i, count = fSamples.count();
-    for (i = 0; i < count; i++) {
-        if (getSampleTitle(i).equals(title)) {
-            return i;
-        }
-    }
-    return -1;
-}
-
-static SkBitmap capture_bitmap(SkCanvas* canvas) {
-    SkBitmap bm;
-    const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
-    src.copyTo(&bm, src.config());
-    return bm;
-}
-
-static bool bitmap_diff(SkCanvas* canvas, const SkBitmap& orig,
-                        SkBitmap* diff) {
-    const SkBitmap& src = canvas->getDevice()->accessBitmap(false);
-
-    SkAutoLockPixels alp0(src);
-    SkAutoLockPixels alp1(orig);
-    for (int y = 0; y < src.height(); y++) {
-        const void* srcP = src.getAddr(0, y);
-        const void* origP = orig.getAddr(0, y);
-        size_t bytes = src.width() * src.bytesPerPixel();
-        if (memcmp(srcP, origP, bytes)) {
-            SkDebugf("---------- difference on line %d\n", y);
-            return true;
-        }
-    }
-    return false;
-}
-
-static void drawText(SkCanvas* canvas, SkString string, SkScalar left, SkScalar top, SkPaint& paint)
-{
-    SkColor desiredColor = paint.getColor();
-    paint.setColor(SK_ColorWHITE);
-    const char* c_str = string.c_str();
-    size_t size = string.size();
-    SkRect bounds;
-    paint.measureText(c_str, size, &bounds);
-    bounds.offset(left, top);
-    SkScalar inset = SkIntToScalar(-2);
-    bounds.inset(inset, inset);
-    canvas->drawRect(bounds, paint);
-    if (desiredColor != SK_ColorBLACK) {
-        paint.setColor(SK_ColorBLACK);
-        canvas->drawText(c_str, size, left + SK_Scalar1, top + SK_Scalar1, paint);
-    }
-    paint.setColor(desiredColor);
-    canvas->drawText(c_str, size, left, top, paint);
-}
-
-#define XCLIP_N  8
-#define YCLIP_N  8
-
-void SampleWindow::draw(SkCanvas* canvas) {
-    if (!fDevManager->prepareCanvas(fDeviceType, canvas, this)) {
-        return;
-    }
-    // update the animation time
-    if (!gAnimTimePrev && !gAnimTime) {
-        // first time make delta be 0
-        gAnimTime = SkTime::GetMSecs();
-        gAnimTimePrev = gAnimTime;
-    } else {
-        gAnimTimePrev = gAnimTime;
-        gAnimTime = SkTime::GetMSecs();
-    }
-    
-    const SkMatrix& localM = fGesture.localM();
-    if (localM.getType() & SkMatrix::kScale_Mask) {
-        canvas->setExternalMatrix(&localM);
-    }
-    if (fGesture.isActive()) {
-        this->updateMatrix();
-    }
-    
-    if (fNClip) {
-        this->INHERITED::draw(canvas);
-        SkBitmap orig = capture_bitmap(canvas);
-
-        const SkScalar w = this->width();
-        const SkScalar h = this->height();
-        const SkScalar cw = w / XCLIP_N;
-        const SkScalar ch = h / YCLIP_N;
-        for (int y = 0; y < YCLIP_N; y++) {
-            SkRect r;
-            r.fTop = y * ch;
-            r.fBottom = (y + 1) * ch;
-            if (y == YCLIP_N - 1) {
-                r.fBottom = h;
-            }
-            for (int x = 0; x < XCLIP_N; x++) {
-                SkAutoCanvasRestore acr(canvas, true);
-                r.fLeft = x * cw;
-                r.fRight = (x + 1) * cw;
-                if (x == XCLIP_N - 1) {
-                    r.fRight = w;
-                }
-                canvas->clipRect(r);
-                this->INHERITED::draw(canvas);
-            }
-        }
-
-        SkBitmap diff;
-        if (bitmap_diff(canvas, orig, &diff)) {
-        }
-    } else {
-        this->INHERITED::draw(canvas);
-    }
-    if (fShowZoomer && !fSaveToPdf) {
-        showZoomer(canvas);
-    }
-    if (fMagnify && !fSaveToPdf) {
-        magnify(canvas);
-    }
-    
-    // do this last
-    fDevManager->publishCanvas(fDeviceType, canvas, this);
-}
-
-static float clipW = 200;
-static float clipH = 200;
-void SampleWindow::magnify(SkCanvas* canvas) {
-    SkRect r;
-    int count = canvas->save();
-    
-    SkMatrix m = canvas->getTotalMatrix();
-    m.invert(&m);
-    SkPoint offset, center;
-    SkScalar mouseX = fMouseX * SK_Scalar1;
-    SkScalar mouseY = fMouseY * SK_Scalar1;
-    m.mapXY(mouseX - clipW/2, mouseY - clipH/2, &offset);
-    m.mapXY(mouseX, mouseY, &center);
-    
-    r.set(0, 0, clipW * m.getScaleX(), clipH * m.getScaleX());
-    r.offset(offset.fX, offset.fY);
-    
-    SkPaint paint;
-    paint.setColor(0xFF66AAEE);
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeWidth(10.f * m.getScaleX());
-    //lense offset
-    //canvas->translate(0, -250);
-    canvas->drawRect(r, paint);
-    canvas->clipRect(r);
-    
-    m = canvas->getTotalMatrix();
-    m.setTranslate(-center.fX, -center.fY);
-    m.postScale(0.5f * fFatBitsScale, 0.5f * fFatBitsScale);
-    m.postTranslate(center.fX, center.fY);
-    canvas->concat(m);
-    
-    this->INHERITED::draw(canvas);
-    
-    canvas->restoreToCount(count);
-}
-
-void SampleWindow::showZoomer(SkCanvas* canvas) {
-        int count = canvas->save();
-        canvas->resetMatrix();
-        // Ensure the mouse position is on screen.
-        int width = SkScalarRound(this->width());
-        int height = SkScalarRound(this->height());
-        if (fMouseX >= width) fMouseX = width - 1;
-        else if (fMouseX < 0) fMouseX = 0;
-        if (fMouseY >= height) fMouseY = height - 1;
-        else if (fMouseY < 0) fMouseY = 0;
-
-        SkBitmap bitmap = capture_bitmap(canvas);
-        bitmap.lockPixels();
-
-        // Find the size of the zoomed in view, forced to be odd, so the examined pixel is in the middle.
-        int zoomedWidth = (width >> 1) | 1;
-        int zoomedHeight = (height >> 1) | 1;
-        SkIRect src;
-        src.set(0, 0, zoomedWidth / fFatBitsScale, zoomedHeight / fFatBitsScale);
-        src.offset(fMouseX - (src.width()>>1), fMouseY - (src.height()>>1));
-        SkRect dest;
-        dest.set(0, 0, SkIntToScalar(zoomedWidth), SkIntToScalar(zoomedHeight));
-        dest.offset(SkIntToScalar(width - zoomedWidth), SkIntToScalar(height - zoomedHeight));
-        SkPaint paint;
-        // Clear the background behind our zoomed in view
-        paint.setColor(SK_ColorWHITE);
-        canvas->drawRect(dest, paint);
-        canvas->drawBitmapRect(bitmap, &src, dest);
-        paint.setColor(SK_ColorBLACK);
-        paint.setStyle(SkPaint::kStroke_Style);
-        // Draw a border around the pixel in the middle
-        SkRect originalPixel;
-        originalPixel.set(SkIntToScalar(fMouseX), SkIntToScalar(fMouseY), SkIntToScalar(fMouseX + 1), SkIntToScalar(fMouseY + 1));
-        SkMatrix matrix;
-        SkRect scalarSrc;
-        scalarSrc.set(src);
-        SkColor color = bitmap.getColor(fMouseX, fMouseY);
-        if (matrix.setRectToRect(scalarSrc, dest, SkMatrix::kFill_ScaleToFit)) {
-            SkRect pixel;
-            matrix.mapRect(&pixel, originalPixel);
-            // TODO Perhaps measure the values and make the outline white if it's "dark"
-            if (color == SK_ColorBLACK) {
-                paint.setColor(SK_ColorWHITE);
-            }
-            canvas->drawRect(pixel, paint);
-        }
-        paint.setColor(SK_ColorBLACK);
-        // Draw a border around the destination rectangle
-        canvas->drawRect(dest, paint);
-        paint.setStyle(SkPaint::kStrokeAndFill_Style);
-        // Identify the pixel and its color on screen
-        paint.setTypeface(fTypeface);
-        paint.setAntiAlias(true);
-        SkScalar lineHeight = paint.getFontMetrics(NULL);
-        SkString string;
-        string.appendf("(%i, %i)", fMouseX, fMouseY);
-        SkScalar left = dest.fLeft + SkIntToScalar(3);
-        SkScalar i = SK_Scalar1;
-        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
-        // Alpha
-        i += SK_Scalar1;
-        string.reset();
-        string.appendf("A: %X", SkColorGetA(color));
-        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
-        // Red
-        i += SK_Scalar1;
-        string.reset();
-        string.appendf("R: %X", SkColorGetR(color));
-        paint.setColor(SK_ColorRED);
-        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
-        // Green
-        i += SK_Scalar1;
-        string.reset();
-        string.appendf("G: %X", SkColorGetG(color));
-        paint.setColor(SK_ColorGREEN);
-        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
-        // Blue
-        i += SK_Scalar1;
-        string.reset();
-        string.appendf("B: %X", SkColorGetB(color));
-        paint.setColor(SK_ColorBLUE);
-        drawText(canvas, string, left, SkScalarMulAdd(lineHeight, i, dest.fTop), paint);
-        canvas->restoreToCount(count);
-}
-
-void SampleWindow::onDraw(SkCanvas* canvas) {
-}
-
-#include "SkColorPriv.h"
-
-static void reverseRedAndBlue(const SkBitmap& bm) {
-    SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
-    uint8_t* p = (uint8_t*)bm.getPixels();
-    uint8_t* stop = p + bm.getSize();
-    while (p < stop) {
-        // swap red/blue (to go from ARGB(int) to RGBA(memory) and premultiply
-        unsigned scale = SkAlpha255To256(p[3]);
-        unsigned r = p[2];
-        unsigned b = p[0];
-        p[0] = SkAlphaMul(r, scale);
-        p[1] = SkAlphaMul(p[1], scale);
-        p[2] = SkAlphaMul(b, scale);
-        p += 4;
-    }
-}
-
-void SampleWindow::saveToPdf()
-{
-    fSaveToPdf = true;
-    this->inval(NULL);
-}
-
-SkCanvas* SampleWindow::beforeChildren(SkCanvas* canvas) {
-    if (fSaveToPdf) {
-        const SkBitmap& bmp = canvas->getDevice()->accessBitmap(false);
-        SkISize size = SkISize::Make(bmp.width(), bmp.height());
-        SkPDFDevice* pdfDevice = new SkPDFDevice(size, size,
-                canvas->getTotalMatrix());
-        fPdfCanvas = new SkCanvas(pdfDevice);
-        pdfDevice->unref();
-        canvas = fPdfCanvas;
-    } else {
-        switch (fDeviceType) {
-            case kRaster_DeviceType:
-            case kGPU_DeviceType:
-                canvas = this->INHERITED::beforeChildren(canvas);
-                break;
-            case kPicture_DeviceType:
-                fPicture = new SkPicture;
-                canvas = fPicture->beginRecording(9999, 9999);
-                break;
-            case kNullGPU_DeviceType:
-                break;
-        }
-    }
-
-    if (fUseClip) {
-        canvas->drawColor(0xFFFF88FF);
-        canvas->clipPath(fClipPath, SkRegion::kIntersect_Op, true);
-    }
-
-    return canvas;
-}
-
-static void paint_rgn(const SkBitmap& bm, const SkIRect& r,
-                      const SkRegion& rgn) {
-    SkCanvas    canvas(bm);
-    SkRegion    inval(rgn);
-
-    inval.translate(r.fLeft, r.fTop);
-    canvas.clipRegion(inval);
-    canvas.drawColor(0xFFFF8080);
-}
-#include "SkData.h"
-void SampleWindow::afterChildren(SkCanvas* orig) {
-    if (fSaveToPdf) {
-        fSaveToPdf = false;
-        if (fShowZoomer) {
-            showZoomer(fPdfCanvas);
-        }
-        SkString name;
-        name.printf("%s.pdf", this->getTitle());
-        SkPDFDocument doc;
-        SkPDFDevice* device = static_cast<SkPDFDevice*>(fPdfCanvas->getDevice());
-        doc.appendPage(device);
-#ifdef SK_BUILD_FOR_ANDROID
-        name.prepend("/sdcard/");
-#endif
-        
-#ifdef SK_BUILD_FOR_IOS
-        SkDynamicMemoryWStream mstream;
-        doc.emitPDF(&mstream);
-        fPDFData = mstream.copyToData();
-#endif
-        SkFILEWStream stream(name.c_str());
-        if (stream.isValid()) {
-            doc.emitPDF(&stream);
-            const char* desc = "File saved from Skia SampleApp";
-            this->onPDFSaved(this->getTitle(), desc, name.c_str());
-        }
-        
-        delete fPdfCanvas;
-        fPdfCanvas = NULL;
-
-        // We took over the draw calls in order to create the PDF, so we need
-        // to redraw.
-        this->inval(NULL);
-        return;
-    }
-    
-    if (fRequestGrabImage) {
-        fRequestGrabImage = false;
-
-        SkDevice* device = orig->getDevice();
-        SkBitmap bmp;
-        if (device->accessBitmap(false).copyTo(&bmp, SkBitmap::kARGB_8888_Config)) {
-            static int gSampleGrabCounter;
-            SkString name;
-            name.printf("sample_grab_%d", gSampleGrabCounter++);
-            SkImageEncoder::EncodeFile(name.c_str(), bmp,
-                                       SkImageEncoder::kPNG_Type, 100);
-        }
-    }
-
-    if (kPicture_DeviceType == fDeviceType) {
-        if (true) {
-            SkPicture* pict = new SkPicture(*fPicture);
-            fPicture->unref();
-            this->installDrawFilter(orig);
-            orig->drawPicture(*pict);
-            pict->unref();
-        } else if (true) {
-            SkDynamicMemoryWStream ostream;
-            fPicture->serialize(&ostream);
-            fPicture->unref();
-
-            SkAutoDataUnref data(ostream.copyToData());
-            SkMemoryStream istream(data.data(), data.size());
-            SkPicture pict(&istream);
-            orig->drawPicture(pict);
-        } else {
-            fPicture->draw(orig);
-            fPicture->unref();
-        }
-        fPicture = NULL;
-    }
-
-    // Do this after presentGL and other finishing, rather than in afterChild
-    if (fMeasureFPS && fMeasureFPS_Time) {
-        fMeasureFPS_Time = SkTime::GetMSecs() - fMeasureFPS_Time;
-        this->updateTitle();
-        this->postInvalDelay();
-    }
-
-    //    if ((fScrollTestX | fScrollTestY) != 0)
-    if (false) {
-        const SkBitmap& bm = orig->getDevice()->accessBitmap(true);
-        int dx = fScrollTestX * 7;
-        int dy = fScrollTestY * 7;
-        SkIRect r;
-        SkRegion inval;
-
-        r.set(50, 50, 50+100, 50+100);
-        bm.scrollRect(&r, dx, dy, &inval);
-        paint_rgn(bm, r, inval);
-    }
-#ifdef DEBUGGER
-    SkView* curr = curr_view(this);
-    if (fDebugger && !is_debugger(curr) && !is_transition(curr) && !is_overview(curr)) {
-        //Stop Pipe when fDebugger is active
-        fUsePipe = false;
-        (void)SampleView::SetUsePipe(curr, false);
-        fAppMenu.getItemByID(fUsePipeMenuItemID)->setBool(fUsePipe);
-        this->onUpdateMenu(&fAppMenu);
-        
-        //Reset any transformations
-        fGesture.stop();
-        fGesture.reset();
-        
-        this->loadView(create_debugger(gTempDataStore.begin(), 
-                                       gTempDataStore.count()));
-    }
-#endif
-}
-
-void SampleWindow::beforeChild(SkView* child, SkCanvas* canvas) {
-    if (fScale) {
-        SkScalar scale = SK_Scalar1 * 7 / 10;
-        SkScalar cx = this->width() / 2;
-        SkScalar cy = this->height() / 2;
-        canvas->translate(cx, cy);
-        canvas->scale(scale, scale);
-        canvas->translate(-cx, -cy);
-    }
-    if (fRotate) {
-        SkScalar cx = this->width() / 2;
-        SkScalar cy = this->height() / 2;
-        canvas->translate(cx, cy);
-        canvas->rotate(SkIntToScalar(30));
-        canvas->translate(-cx, -cy);
-    }
-    if (fPerspAnim) {
-        fPerspAnimTime += SampleCode::GetAnimSecondsDelta();
-
-        static const SkScalar gAnimPeriod = 10 * SK_Scalar1;
-        static const SkScalar gAnimMag = SK_Scalar1 / 1000;
-        SkScalar t = SkScalarMod(fPerspAnimTime, gAnimPeriod);
-        if (SkScalarFloorToInt(SkScalarDiv(fPerspAnimTime, gAnimPeriod)) & 0x1) {
-            t = gAnimPeriod - t;
-        }
-        t = 2 * t - gAnimPeriod;
-        t = SkScalarMul(SkScalarDiv(t, gAnimPeriod), gAnimMag);
-        SkMatrix m;
-        m.reset();
-        m.setPerspY(t);
-        canvas->concat(m);
-    }
-
-    this->installDrawFilter(canvas);
-
-    if (fMeasureFPS) {
-        fMeasureFPS_Time = 0;   // 0 means the child is not aware of repeat-draw
-        if (SampleView::SetRepeatDraw(child, FPS_REPEAT_COUNT)) {
-            fMeasureFPS_Time = SkTime::GetMSecs();
-        }
-    } else {
-        (void)SampleView::SetRepeatDraw(child, 1);
-    }
-    if (fPerspAnim) {
-        this->inval(NULL);
-    }
-    //(void)SampleView::SetUsePipe(child, fUsePipe);
-}
-
-void SampleWindow::afterChild(SkView* child, SkCanvas* canvas) {
-    canvas->setDrawFilter(NULL);
-}
-
-static SkBitmap::Config gConfigCycle[] = {
-    SkBitmap::kNo_Config,           // none -> none
-    SkBitmap::kNo_Config,           // a1 -> none
-    SkBitmap::kNo_Config,           // a8 -> none
-    SkBitmap::kNo_Config,           // index8 -> none
-    SkBitmap::kARGB_4444_Config,    // 565 -> 4444
-    SkBitmap::kARGB_8888_Config,    // 4444 -> 8888
-    SkBitmap::kRGB_565_Config       // 8888 -> 565
-};
-
-static SkBitmap::Config cycle_configs(SkBitmap::Config c) {
-    return gConfigCycle[c];
-}
-
-void SampleWindow::changeZoomLevel(float delta) {
-    fZoomLevel += SkFloatToScalar(delta);
-    if (fZoomLevel > 0) {
-        fZoomLevel = SkMinScalar(fZoomLevel, MAX_ZOOM_LEVEL);
-        fZoomScale = fZoomLevel + SK_Scalar1;
-    } else if (fZoomLevel < 0) {
-        fZoomLevel = SkMaxScalar(fZoomLevel, MIN_ZOOM_LEVEL);
-        fZoomScale = SK_Scalar1 / (SK_Scalar1 - fZoomLevel);
-    } else {
-        fZoomScale = SK_Scalar1;
-    }
-    this->updateMatrix();
-}
-
-void SampleWindow::updateMatrix(){
-    SkMatrix m;
-    m.reset();
-    if (fZoomLevel) {
-        SkPoint center;
-        //m = this->getLocalMatrix();//.invert(&m);
-        m.mapXY(fZoomCenterX, fZoomCenterY, &center);
-        SkScalar cx = center.fX;
-        SkScalar cy = center.fY;
-        
-        m.setTranslate(-cx, -cy);
-        m.postScale(fZoomScale, fZoomScale);
-        m.postTranslate(cx, cy);
-    }
-    
-    if (fFlipAxis) {
-        m.preTranslate(fZoomCenterX, fZoomCenterY);
-        if (fFlipAxis & kFlipAxis_X) {
-            m.preScale(-SK_Scalar1, SK_Scalar1);
-        }
-        if (fFlipAxis & kFlipAxis_Y) {
-            m.preScale(SK_Scalar1, -SK_Scalar1);
-        }
-        m.preTranslate(-fZoomCenterX, -fZoomCenterY);
-        //canvas->concat(m);
-    }
-    // Apply any gesture matrix
-    m.preConcat(fGesture.localM());
-    m.preConcat(fGesture.globalM());
-    
-    this->setLocalMatrix(m);
-    
-    this->updateTitle();
-    this->inval(NULL);
-}
-bool SampleWindow::previousSample() {
-    fCurrIndex = (fCurrIndex - 1 + fSamples.count()) % fSamples.count();
-    SkView* view = (*fSamples[fCurrIndex])();
-    this->loadView(view);
-//    this->loadView(create_transition(curr_view(this), (*fSamples[fCurrIndex])(),
-//                                     fTransitionPrev));
-    return true;
-}
-
-bool SampleWindow::nextSample() {
-    fCurrIndex = (fCurrIndex + 1) % fSamples.count();
-    SkView* view = (*fSamples[fCurrIndex])();
-    this->loadView(view);
-//    this->loadView(create_transition(curr_view(this), (*fSamples[fCurrIndex])(),
-//                                     fTransitionNext));
-    return true;
-}
-
-bool SampleWindow::goToSample(int i) {
-    fCurrIndex = (i) % fSamples.count();
-    SkView* view = (*fSamples[fCurrIndex])();
-    this->loadView(view);
-//    this->loadView(create_transition(curr_view(this),(*fSamples[fCurrIndex])(), 6));
-    return true;
-}
-
-SkString SampleWindow::getSampleTitle(int i) {
-    SkView* view = (*fSamples[i])();
-    SkString title;
-    SampleCode::RequestTitle(view, &title);
-    view->unref();
-    return title;
-}
-
-int SampleWindow::sampleCount() {
-    return fSamples.count();
-}
-
-void SampleWindow::showOverview() {
-    this->loadView(create_overview(fSamples.count(), fSamples.begin()));
-//    this->loadView(create_transition(curr_view(this),
-//                                     create_overview(fSamples.count(), fSamples.begin()),
-//                                     4));
-}
-
-void SampleWindow::installDrawFilter(SkCanvas* canvas) {
-    canvas->setDrawFilter(new FlagsDrawFilter(fLCDState, fAAState,
-                                              fFilterState, fHintingState))->unref();
-}
-
-void SampleWindow::postAnimatingEvent() {
-    if (fAnimating) {
-        (new SkEvent(ANIMATING_EVENTTYPE, this->getSinkID()))->postDelay(ANIMATING_DELAY);
-    }
-}
-
-bool SampleWindow::onEvent(const SkEvent& evt) {
-    if (evt.isType(gUpdateWindowTitleEvtName)) {
-        this->updateTitle();
-        return true;
-    }
-    if (evt.isType(ANIMATING_EVENTTYPE)) {
-        if (fAnimating) {
-            this->nextSample();
-            this->postAnimatingEvent();
-        }
-        return true;
-    }
-    if (evt.isType("replace-transition-view")) {
-        this->loadView((SkView*)SkEventSink::FindSink(evt.getFast32()));
-        return true;
-    }
-    if (evt.isType("set-curr-index")) {
-        this->goToSample(evt.getFast32());
-        return true;
-    }
-    if (isInvalEvent(evt)) {
-        this->inval(NULL);
-        return true;
-    }
-    int selected = -1;
-    if (SkOSMenu::FindListIndex(evt, "Device Type", &selected)) {
-        this->setDeviceType((DeviceType)selected);
-        return true; 
-    }
-    if (SkOSMenu::FindSwitchState(evt, "Pipe", &fUsePipe)) {
-#ifdef PIPE_NET
-        if (!fUsePipe)
-            gServer.disconnectAll();
-#endif
-        (void)SampleView::SetUsePipe(curr_view(this), fUsePipe);
-        this->updateTitle();
-        this->inval(NULL);
-        return true;
-    }
-    if (SkOSMenu::FindSwitchState(evt, "Slide Show", NULL)) {
-        this->toggleSlideshow();
-        return true;
-    }
-    if (SkOSMenu::FindTriState(evt, "AA", &fAAState) ||
-        SkOSMenu::FindTriState(evt, "LCD", &fLCDState) ||
-        SkOSMenu::FindTriState(evt, "Filter", &fFilterState) ||
-        SkOSMenu::FindTriState(evt, "Hinting", &fHintingState) ||
-        SkOSMenu::FindSwitchState(evt, "Clip", &fUseClip) ||
-        SkOSMenu::FindSwitchState(evt, "Zoomer", &fShowZoomer) ||
-        SkOSMenu::FindSwitchState(evt, "Magnify", &fMagnify) ||
-        SkOSMenu::FindListIndex(evt, "Transition-Next", &fTransitionNext) ||
-        SkOSMenu::FindListIndex(evt, "Transition-Prev", &fTransitionPrev)) {
-        this->inval(NULL);
-        this->updateTitle();
-        return true;
-    }
-    if (SkOSMenu::FindSwitchState(evt, "Flip X", NULL)) {
-        fFlipAxis ^= kFlipAxis_X;
-        this->updateMatrix();
-        return true;
-    }
-    if (SkOSMenu::FindSwitchState(evt, "Flip Y", NULL)) {
-        fFlipAxis ^= kFlipAxis_Y;
-        this->updateMatrix();
-        return true;
-    }
-    if (SkOSMenu::FindAction(evt,"Save to PDF")) {
-        this->saveToPdf();
-        return true;
-    } 
-#ifdef DEBUGGER
-    if (SkOSMenu::FindSwitchState(evt, "Debugger", &fDebugger)) {
-        if (fDebugger) {
-            fUsePipe = true;
-            (void)SampleView::SetUsePipe(curr_view(this), true);
-        } else {
-            this->loadView(fSamples[fCurrIndex]());
-        }
-        this->inval(NULL);
-        return true;
-    }
-#endif
-    return this->INHERITED::onEvent(evt);
-}
-
-bool SampleWindow::onQuery(SkEvent* query) {
-    if (query->isType("get-slide-count")) {
-        query->setFast32(fSamples.count());
-        return true;
-    }
-    if (query->isType("get-slide-title")) {
-        SkView* view = (*fSamples[query->getFast32()])();
-        SkEvent evt(gTitleEvtName);
-        if (view->doQuery(&evt)) {
-            query->setString("title", evt.findString(gTitleEvtName));
-        }
-        SkSafeUnref(view);
-        return true;
-    }
-    if (query->isType("use-fast-text")) {
-        SkEvent evt(gFastTextEvtName);
-        return curr_view(this)->doQuery(&evt);
-    }
-    if (query->isType("ignore-window-bitmap")) {
-        query->setFast32(this->getGrContext() != NULL);
-        return true;
-    }
-    return this->INHERITED::onQuery(query);
-}
-
-static void cleanup_for_filename(SkString* name) {
-    char* str = name->writable_str();
-    for (size_t i = 0; i < name->size(); i++) {
-        switch (str[i]) {
-            case ':': str[i] = '-'; break;
-            case '/': str[i] = '-'; break;
-            case ' ': str[i] = '_'; break;
-            default: break;
-        }
-    }
-}
-
-bool SampleWindow::onHandleChar(SkUnichar uni) {
-    {
-        SkView* view = curr_view(this);
-        if (view) {
-            SkEvent evt(gCharEvtName);
-            evt.setFast32(uni);
-            if (view->doQuery(&evt)) {
-                return true;
-            }
-        }
-    }
-    
-    int dx = 0xFF;
-    int dy = 0xFF;
-
-    switch (uni) {
-        case '5': dx =  0; dy =  0; break;
-        case '8': dx =  0; dy = -1; break;
-        case '6': dx =  1; dy =  0; break;
-        case '2': dx =  0; dy =  1; break;
-        case '4': dx = -1; dy =  0; break;
-        case '7': dx = -1; dy = -1; break;
-        case '9': dx =  1; dy = -1; break;
-        case '3': dx =  1; dy =  1; break;
-        case '1': dx = -1; dy =  1; break;
-
-        default:
-            break;
-    }
-
-    if (0xFF != dx && 0xFF != dy) {
-        if ((dx | dy) == 0) {
-            fScrollTestX = fScrollTestY = 0;
-        } else {
-            fScrollTestX += dx;
-            fScrollTestY += dy;
-        }
-        this->inval(NULL);
-        return true;
-    }
-
-    switch (uni) {
-        case 'f':
-            // only 
-            toggleFPS();
-            break;
-        case 'g':
-            fRequestGrabImage = true;
-            this->inval(NULL);
-            break;
-        case 'i':
-            this->zoomIn();
-            break;
-        case 'o':
-            this->zoomOut();
-            break;
-        case 'r':
-            fRotate = !fRotate;
-            this->inval(NULL);
-            this->updateTitle();
-            return true;
-        case 'k':
-            fPerspAnim = !fPerspAnim;
-            this->inval(NULL);
-            this->updateTitle();
-            return true;
-        case '\\':
-            if (fDevManager->supportsDeviceType(kNullGPU_DeviceType)) {
-                fDeviceType=  kNullGPU_DeviceType;
-                this->inval(NULL);
-                this->updateTitle();
-            }
-            return true;
-        case 'p':
-            {
-                GrContext* grContext = this->getGrContext();
-                if (grContext) {
-                    size_t cacheBytes = grContext->getGpuTextureCacheBytes();
-                    grContext->freeGpuResources();
-                    SkDebugf("Purged %d bytes from the GPU resource cache.\n",
-                             cacheBytes);
-                }
-            }
-            return true;
-        case 's':
-            fScale = !fScale;
-            this->inval(NULL);
-            this->updateTitle();
-            return true;
-        default:
-            break;
-    }
-    
-    if (fAppMenu.handleKeyEquivalent(uni)|| fSlideMenu.handleKeyEquivalent(uni)) {
-        this->onUpdateMenu(&fAppMenu);
-        this->onUpdateMenu(&fSlideMenu);
-        return true;
-    }
-    return this->INHERITED::onHandleChar(uni);
-}
-
-void SampleWindow::setDeviceType(DeviceType type) {
-    if (type != fDeviceType && fDevManager->supportsDeviceType(fDeviceType))
-        fDeviceType = type;
-    this->updateTitle();
-    this->inval(NULL);
-}
-
-void SampleWindow::toggleSlideshow() {
-    fAnimating = !fAnimating;
-    this->postAnimatingEvent();
-    this->updateTitle();
-}
-
-void SampleWindow::toggleRendering() {
-    DeviceType origDevType = fDeviceType;
-    do {
-        fDeviceType = cycle_devicetype(fDeviceType);
-    } while (origDevType != fDeviceType &&
-             !fDevManager->supportsDeviceType(fDeviceType));
-    this->updateTitle();
-    this->inval(NULL);
-}
-
-void SampleWindow::toggleFPS() {
-    fMeasureFPS = !fMeasureFPS;
-    this->updateTitle();
-    this->inval(NULL);
-}
-
-#include "SkDumpCanvas.h"
-
-bool SampleWindow::onHandleKey(SkKey key) {
-    {
-        SkView* view = curr_view(this);
-        if (view) {
-            SkEvent evt(gKeyEvtName);
-            evt.setFast32(key);
-            if (view->doQuery(&evt)) {
-                return true;
-            }
-        }
-    }
-    switch (key) {
-        case kRight_SkKey:
-            if (this->nextSample()) {
-                return true;
-            }
-            break;
-        case kLeft_SkKey:
-            toggleRendering();
-            return true;
-        case kUp_SkKey:
-            if (USE_ARROWS_FOR_ZOOM) {
-                this->changeZoomLevel(1.f);
-            } else {
-                fNClip = !fNClip;
-                this->inval(NULL);
-                this->updateTitle();
-            }
-            return true;
-        case kDown_SkKey:
-            if (USE_ARROWS_FOR_ZOOM) {
-                this->changeZoomLevel(-1.f);
-            } else {
-                this->setConfig(cycle_configs(this->getBitmap().config()));
-                this->updateTitle();
-            }
-            return true;
-        case kOK_SkKey: {
-            SkString title;
-            if (curr_title(this, &title)) {
-                writeTitleToPrefs(title.c_str());
-            }
-            return true;
-        }
-        case kBack_SkKey:
-            this->showOverview();
-            return true;
-        default:
-            break;
-    }
-    return this->INHERITED::onHandleKey(key);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const char gGestureClickType[] = "GestureClickType";
-
-bool SampleWindow::onDispatchClick(int x, int y, Click::State state,
-        void* owner) {
-    if (Click::kMoved_State == state) {
-        updatePointer(x, y);
-    }
-    int w = SkScalarRound(this->width());
-    int h = SkScalarRound(this->height());
-
-    // check for the resize-box
-    if (w - x < 16 && h - y < 16) {
-        return false;   // let the OS handle the click
-    } 
-    else if (fMagnify) {
-        //it's only necessary to update the drawing if there's a click
-        this->inval(NULL);
-        return false; //prevent dragging while magnify is enabled
-    }
-    else {
-        return this->INHERITED::onDispatchClick(x, y, state, owner);
-    }
-}
-
-class GestureClick : public SkView::Click {
-public:
-    GestureClick(SkView* target) : SkView::Click(target) {
-        this->setType(gGestureClickType);
-    }
-
-    static bool IsGesture(Click* click) {
-        return click->isType(gGestureClickType);
-    }
-};
-
-SkView::Click* SampleWindow::onFindClickHandler(SkScalar x, SkScalar y) {
-    return new GestureClick(this);
-}
-
-bool SampleWindow::onClick(Click* click) {
-    if (GestureClick::IsGesture(click)) {
-        float x = static_cast<float>(click->fICurr.fX);
-        float y = static_cast<float>(click->fICurr.fY);
-        
-        switch (click->fState) {
-            case SkView::Click::kDown_State:
-                fGesture.touchBegin(click->fOwner, x, y);
-                break;
-            case SkView::Click::kMoved_State:
-                fGesture.touchMoved(click->fOwner, x, y);
-                this->updateMatrix();
-                break;
-            case SkView::Click::kUp_State:
-                fGesture.touchEnd(click->fOwner);
-                this->updateMatrix();
-                break;
-        }
-        return true;
-    }
-    return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SampleWindow::loadView(SkView* view) {
-    SkView::F2BIter iter(this);
-    SkView* prev = iter.next();
-    if (prev) {
-        prev->detachFromParent();
-    }
-    
-    view->setVisibleP(true);
-    view->setClipToBounds(false);
-    this->attachChildToFront(view)->unref();
-    view->setSize(this->width(), this->height());
-
-    //repopulate the slide menu when a view is loaded
-    fSlideMenu.reset();
-#ifdef DEBUGGER
-    if (!is_debugger(view) && !is_overview(view) && !is_transition(view) && fDebugger) {
-        //Force Pipe to be on if using debugger
-        fUsePipe = true;
-    }
-#endif
-    (void)SampleView::SetUsePipe(view, fUsePipe);
-    if (SampleView::IsSampleView(view))
-        ((SampleView*)view)->requestMenu(&fSlideMenu);
-    this->onUpdateMenu(&fSlideMenu);
-    this->updateTitle();
-}
-
-static const char* gConfigNames[] = {
-    "unknown config",
-    "A1",
-    "A8",
-    "Index8",
-    "565",
-    "4444",
-    "8888"
-};
-
-static const char* configToString(SkBitmap::Config c) {
-    return gConfigNames[c];
-}
-
-static const char* gDeviceTypePrefix[] = {
-    "raster: ",
-    "picture: ",
-    "opengl: ",
-    "null-gl: "
-};
-
-static const char* trystate_str(SkOSMenu::TriState state,
-                                const char trueStr[], const char falseStr[]) {
-    if (SkOSMenu::kOnState == state) {
-        return trueStr;
-    } else if (SkOSMenu::kOffState == state) {
-        return falseStr;
-    }
-    return NULL;
-}
-
-void SampleWindow::updateTitle() {
-    SkView* view = curr_view(this);
-
-    SkString title;
-    if (!curr_title(this, &title)) {
-        title.set("<unknown>");
-    }
-
-    title.prepend(gDeviceTypePrefix[fDeviceType]);
-
-    title.prepend(" ");
-    title.prepend(configToString(this->getBitmap().config()));
-
-    if (fAnimating) {
-        title.prepend("<A> ");
-    }
-    if (fScale) {
-        title.prepend("<S> ");
-    }
-    if (fRotate) {
-        title.prepend("<R> ");
-    }
-    if (fNClip) {
-        title.prepend("<C> ");
-    }
-    if (fPerspAnim) {
-        title.prepend("<K> ");
-    }
-
-    title.prepend(trystate_str(fLCDState, "LCD ", "lcd "));
-    title.prepend(trystate_str(fAAState, "AA ", "aa "));
-    title.prepend(trystate_str(fFilterState, "H ", "h "));
-    title.prepend(fFlipAxis & kFlipAxis_X ? "X " : NULL);
-    title.prepend(fFlipAxis & kFlipAxis_Y ? "Y " : NULL);
-
-    if (fZoomLevel) {
-        title.prependf("{%.2f} ", SkScalarToFloat(fZoomLevel));
-    }
-    
-    if (fMeasureFPS) {
-        title.appendf(" %6.1f ms", fMeasureFPS_Time / (float)FPS_REPEAT_MULTIPLIER);
-    }
-    if (fUsePipe && SampleView::IsSampleView(view)) {
-        title.prepend("<P> ");
-    }
-    if (SampleView::IsSampleView(view)) {
-        title.prepend("! ");
-    }
-
-    this->setTitle(title.c_str());
-}
-
-void SampleWindow::onSizeChange() {
-    this->INHERITED::onSizeChange();
-    
-    SkView::F2BIter iter(this);
-    SkView* view = iter.next();
-    view->setSize(this->width(), this->height());
-
-    // rebuild our clippath
-    {
-        const SkScalar W = this->width();
-        const SkScalar H = this->height();
-
-        fClipPath.reset();
-#if 0
-        for (SkScalar y = SK_Scalar1; y < H; y += SkIntToScalar(32)) {
-            SkRect r;
-            r.set(SK_Scalar1, y, SkIntToScalar(30), y + SkIntToScalar(30));
-            for (; r.fLeft < W; r.offset(SkIntToScalar(32), 0))
-                fClipPath.addRect(r);
-        }
-#else
-        SkRect r;
-        r.set(0, 0, W, H);
-        fClipPath.addRect(r, SkPath::kCCW_Direction);
-        r.set(W/4, H/4, W*3/4, H*3/4);
-        fClipPath.addRect(r, SkPath::kCW_Direction);
-#endif
-    }
-
-    fZoomCenterX = SkScalarHalf(this->width());
-    fZoomCenterY = SkScalarHalf(this->height());
-
-#ifdef SK_BUILD_FOR_ANDROID
-    // FIXME: The first draw after a size change does not work on Android, so
-    // we post an invalidate.
-    this->postInvalDelay();
-#endif
-    this->updateTitle();    // to refresh our config
-    fDevManager->windowSizeChanged(this);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const char is_sample_view_tag[] = "sample-is-sample-view";
-static const char repeat_count_tag[] = "sample-set-repeat-count";
-static const char set_use_pipe_tag[] = "sample-set-use-pipe";
-
-bool SampleView::IsSampleView(SkView* view) {
-    SkEvent evt(is_sample_view_tag);
-    return view->doQuery(&evt);
-}
-
-bool SampleView::SetRepeatDraw(SkView* view, int count) {
-    SkEvent evt(repeat_count_tag);
-    evt.setFast32(count);
-    return view->doEvent(evt);
-}
-
-bool SampleView::SetUsePipe(SkView* view, bool pred) {
-    SkEvent evt(set_use_pipe_tag);
-    evt.setFast32(pred);
-    return view->doEvent(evt);
-}
-
-bool SampleView::onEvent(const SkEvent& evt) {
-    if (evt.isType(repeat_count_tag)) {
-        fRepeatCount = evt.getFast32();
-        return true;
-    }
-    if (evt.isType(set_use_pipe_tag)) {
-        fUsePipe = !!evt.getFast32();
-        return true;
-    }
-    return this->INHERITED::onEvent(evt);
-}
-
-bool SampleView::onQuery(SkEvent* evt) {
-    if (evt->isType(is_sample_view_tag)) {
-        return true;
-    }
-    return this->INHERITED::onQuery(evt);
-}
-
-#ifdef TEST_GPIPE
-    #include "SkGPipe.h"
-
-class SimplePC : public SkGPipeController {
-public:
-    SimplePC(SkCanvas* target);
-    ~SimplePC();
-    
-    /**
-     * User this method to halt/restart pipe
-     */
-    void setWriteToPipe(bool writeToPipe) { fWriteToPipe = writeToPipe; }
-    virtual void* requestBlock(size_t minRequest, size_t* actual);
-    virtual void notifyWritten(size_t bytes);
-
-private:
-    SkGPipeReader   fReader;
-    void*           fBlock;
-    size_t          fBlockSize;
-    size_t          fBytesWritten;
-    int             fAtomsWritten;
-    SkGPipeReader::Status   fStatus;
-    bool            fWriteToPipe;
-
-    size_t        fTotalWritten;
-};
-
-SimplePC::SimplePC(SkCanvas* target) : fReader(target) {
-    fBlock = NULL;
-    fBlockSize = fBytesWritten = 0;
-    fStatus = SkGPipeReader::kDone_Status;
-    fTotalWritten = 0;
-    fAtomsWritten = 0;
-    fWriteToPipe = true;
-}
-
-SimplePC::~SimplePC() {
-//    SkASSERT(SkGPipeReader::kDone_Status == fStatus);
-    if (fTotalWritten) {
-        if (fWriteToPipe) {
-            SkDebugf("--- %d bytes %d atoms, status %d\n", fTotalWritten,
-                     fAtomsWritten, fStatus);
-#ifdef  PIPE_FILE
-            //File is open in append mode
-            FILE* f = fopen(FILE_PATH, "ab");
-            SkASSERT(f != NULL);
-            fwrite((const char*)fBlock + fBytesWritten, 1, bytes, f);
-            fclose(f);
-#endif
-#ifdef PIPE_NET
-            if (fAtomsWritten > 1 && fTotalWritten > 4) { //ignore done
-                gServer.acceptConnections();
-                gServer.writePacket(fBlock, fTotalWritten);
-            }
-#endif
-#ifdef  DEBUGGER
-            gTempDataStore.reset();
-            gTempDataStore.append(fTotalWritten, (const char*)fBlock);
-#endif
-        }
-    }
-    sk_free(fBlock);
-}
-
-void* SimplePC::requestBlock(size_t minRequest, size_t* actual) {
-    sk_free(fBlock);
-
-    fBlockSize = minRequest * 4;
-    fBlock = sk_malloc_throw(fBlockSize);
-    fBytesWritten = 0;
-    *actual = fBlockSize;
-    return fBlock;
-}
-
-void SimplePC::notifyWritten(size_t bytes) {
-    SkASSERT(fBytesWritten + bytes <= fBlockSize);
-    fStatus = fReader.playback((const char*)fBlock + fBytesWritten, bytes);
-    SkASSERT(SkGPipeReader::kError_Status != fStatus);
-    fBytesWritten += bytes;
-    fTotalWritten += bytes;
-    
-    fAtomsWritten += 1;
-}
-
-#endif
-
-void SampleView::draw(SkCanvas* canvas) {
-#ifdef TEST_GPIPE
-    if (fUsePipe) {
-        SkGPipeWriter writer;
-        SimplePC controller(canvas);
-        uint32_t flags = SkGPipeWriter::kCrossProcess_Flag;
-        canvas = writer.startRecording(&controller, flags);
-        //Must draw before controller goes out of scope and sends data
-        this->INHERITED::draw(canvas);
-        //explicitly end recording to ensure writer is flushed before the memory
-        //is freed in the deconstructor of the controller
-        writer.endRecording();
-        controller.setWriteToPipe(fUsePipe);
-    }
-    else
-        this->INHERITED::draw(canvas);
-#else
-    this->INHERITED::draw(canvas);
-#endif
-}
-void SampleView::onDraw(SkCanvas* canvas) {
-    this->onDrawBackground(canvas);
-
-    for (int i = 0; i < fRepeatCount; i++) {
-        SkAutoCanvasRestore acr(canvas, true);
-        this->onDrawContent(canvas);
-    }
-}
-
-void SampleView::onDrawBackground(SkCanvas* canvas) {
-    canvas->drawColor(fBGColor);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-template <typename T> void SkTBSort(T array[], int count) {
-    for (int i = 1; i < count - 1; i++) {
-        bool didSwap = false;
-        for (int j = count - 1; j > i; --j) {
-            if (array[j] < array[j-1]) {
-                T tmp(array[j-1]);
-                array[j-1] = array[j];
-                array[j] = tmp;
-                didSwap = true;
-            }
-        }
-        if (!didSwap) {
-            break;
-        }
-    }
-
-    for (int k = 0; k < count - 1; k++) {
-        SkASSERT(!(array[k+1] < array[k]));
-    }
-}
-
-#include "SkRandom.h"
-
-static void rand_rect(SkIRect* rect, SkRandom& rand) {
-    int bits = 8;
-    int shift = 32 - bits;
-    rect->set(rand.nextU() >> shift, rand.nextU() >> shift,
-              rand.nextU() >> shift, rand.nextU() >> shift);
-    rect->sort();
-}
-
-static void dumpRect(const SkIRect& r) {
-    SkDebugf(" { %d, %d, %d, %d },\n",
-             r.fLeft, r.fTop,
-             r.fRight, r.fBottom);
-}
-
-static void test_rects(const SkIRect rect[], int count) {
-    SkRegion rgn0, rgn1;
-
-    for (int i = 0; i < count; i++) {
-        rgn0.op(rect[i], SkRegion::kUnion_Op);
-     //   dumpRect(rect[i]);
-    }
-    rgn1.setRects(rect, count);
-
-    if (rgn0 != rgn1) {
-        SkDebugf("\n");
-        for (int i = 0; i < count; i++) {
-            dumpRect(rect[i]);
-        }
-        SkDebugf("\n");
-    }
-}
-
-static void test() {
-    size_t i;
-
-    const SkIRect r0[] = {
-        { 0, 0, 1, 1 },
-        { 2, 2, 3, 3 },
-    };
-    const SkIRect r1[] = {
-        { 0, 0, 1, 3 },
-        { 1, 1, 2, 2 },
-        { 2, 0, 3, 3 },
-    };
-    const SkIRect r2[] = {
-        { 0, 0, 1, 2 },
-        { 2, 1, 3, 3 },
-        { 4, 0, 5, 1 },
-        { 6, 0, 7, 4 },
-    };
-
-    static const struct {
-        const SkIRect* fRects;
-        int            fCount;
-    } gRecs[] = {
-        { r0, SK_ARRAY_COUNT(r0) },
-        { r1, SK_ARRAY_COUNT(r1) },
-        { r2, SK_ARRAY_COUNT(r2) },
-    };
-
-    for (i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
-        test_rects(gRecs[i].fRects, gRecs[i].fCount);
-    }
-
-    SkRandom rand;
-    for (i = 0; i < 10000; i++) {
-        SkRegion rgn0, rgn1;
-
-        const int N = 8;
-        SkIRect rect[N];
-        for (int j = 0; j < N; j++) {
-            rand_rect(&rect[j], rand);
-        }
-        test_rects(rect, N);
-    }
-}
-
-SkOSWindow* create_sk_window(void* hwnd, int argc, char** argv) {
-//    test();
-    return new SampleWindow(hwnd, argc, argv, NULL);
-}
-
-void get_preferred_size(int* x, int* y, int* width, int* height) {
-    *x = 10;
-    *y = 50;
-    *width = 640;
-    *height = 480;
-}
-
-void application_init() {
-//    setenv("ANDROID_ROOT", "../../../data", 0);
-#ifdef SK_BUILD_FOR_MAC
-    setenv("ANDROID_ROOT", "/android/device/data", 0);
-#endif
-    SkGraphics::Init();
-    SkEvent::Init();
-}
-
-void application_term() {
-    SkEvent::Term();
-    SkGraphics::Term();
-}
diff --git a/samplecode/SampleApp.h b/samplecode/SampleApp.h
deleted file mode 100644
index 871d47e..0000000
--- a/samplecode/SampleApp.h
+++ /dev/null
@@ -1,197 +0,0 @@
-
-/*
- * Copyright 2011 Skia
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SampleWindow_DEFINED
-#define SampleWindow_DEFINED
-
-#include "SkWindow.h"
-
-#include "SampleCode.h"
-#include "SkPath.h"
-#include "SkScalar.h"
-#include "SkTDArray.h"
-#include "SkTouchGesture.h"
-#include "SkWindow.h"
-#include "SkOSMenu.h"
-
-class GrContext;
-class GrRenderTarget;
-
-class SkEvent;
-class SkCanvas;
-class SkPicture;
-class SkTypeface;
-class SkData;
-
-class SampleWindow : public SkOSWindow {
-    SkTDArray<const SkViewFactory*> fSamples;
-public:
-    enum DeviceType {
-        kRaster_DeviceType,
-        kPicture_DeviceType,
-        kGPU_DeviceType,
-        kNullGPU_DeviceType
-    };
-    /**
-     * SampleApp ports can subclass this manager class if they want to:
-     *      * filter the types of devices supported
-     *      * customize plugging of SkDevice objects into an SkCanvas
-     *      * customize publishing the results of draw to the OS window
-     *      * manage GrContext / GrRenderTarget lifetimes
-     */
-    class DeviceManager : public SkRefCnt {
-    public:
-        // called at end of SampleWindow cons
-        virtual void init(SampleWindow* win) = 0;
-
-        // called when selecting a new device type
-        // can disallow a device type by returning false.
-        virtual bool supportsDeviceType(DeviceType dType) = 0;
-
-        // called before drawing. should install correct device
-        // type on the canvas. Will skip drawing if returns false.
-        virtual bool prepareCanvas(DeviceType dType,
-                                   SkCanvas* canvas,
-                                   SampleWindow* win) = 0;
-
-        // called after drawing, should get the results onto the
-        // screen.
-        virtual void publishCanvas(DeviceType dType,
-                                   SkCanvas* canvas,
-                                   SampleWindow* win) = 0;
-
-        // called when window changes size, guaranteed to be called
-        // at least once before first draw (after init)
-        virtual void windowSizeChanged(SampleWindow* win) = 0;
-
-        // return the GrContext backing gpu devices
-        virtual GrContext* getGrContext(DeviceType dType) = 0;
-    };
-
-    SampleWindow(void* hwnd, int argc, char** argv, DeviceManager*);
-    virtual ~SampleWindow();
-
-    virtual void draw(SkCanvas* canvas);
-
-    void setDeviceType(DeviceType type);
-    void toggleRendering();
-    void toggleSlideshow();
-    void toggleFPS();
-    void showOverview();
-
-    GrContext* getGrContext() const { return fDevManager->getGrContext(fDeviceType); }
-
-    void setZoomCenter(float x, float y);
-    void changeZoomLevel(float delta);
-    bool nextSample();
-    bool previousSample();
-    bool goToSample(int i);
-    SkString getSampleTitle(int i);
-    int  sampleCount();
-    bool handleTouch(int ownerId, float x, float y,
-            SkView::Click::State state);
-    void saveToPdf();
-    SkData* getPDFData() { return fPDFData; }
-    void postInvalDelay();
-
-protected:
-    virtual void onDraw(SkCanvas* canvas);
-    virtual bool onHandleKey(SkKey key);
-    virtual bool onHandleChar(SkUnichar);
-    virtual void onSizeChange();
-
-    virtual SkCanvas* beforeChildren(SkCanvas*);
-    virtual void afterChildren(SkCanvas*);
-    virtual void beforeChild(SkView* child, SkCanvas* canvas);
-    virtual void afterChild(SkView* child, SkCanvas* canvas);
-
-    virtual bool onEvent(const SkEvent& evt);
-    virtual bool onQuery(SkEvent* evt);
-
-    virtual bool onDispatchClick(int x, int y, Click::State, void* owner);
-    virtual bool onClick(Click* click);
-    virtual Click* onFindClickHandler(SkScalar x, SkScalar y);
-
-private:
-    class DefaultDeviceManager;
-
-    int fCurrIndex;
-
-    SkPicture* fPicture;
-    SkPath fClipPath;
-
-    SkTouchGesture fGesture;
-    SkScalar fZoomLevel;
-    SkScalar fZoomScale;
-
-    DeviceType fDeviceType;
-    DeviceManager* fDevManager;
-
-    bool fSaveToPdf;
-    SkCanvas* fPdfCanvas;
-    SkData* fPDFData;
-
-    bool fUseClip;
-    bool fNClip;
-    bool fAnimating;
-    bool fRotate;
-    bool fPerspAnim;
-    SkScalar fPerspAnimTime;
-    bool fScale;
-    bool fRequestGrabImage;
-    bool fMeasureFPS;
-    SkMSec fMeasureFPS_Time;
-    bool fMagnify;
-    
-    
-    bool fUsePipe;
-    int  fUsePipeMenuItemID;
-    bool fDebugger;
-    
-    // The following are for the 'fatbits' drawing
-    // Latest position of the mouse.
-    int fMouseX, fMouseY;
-    int fFatBitsScale;
-    // Used by the text showing position and color values.
-    SkTypeface* fTypeface;
-    bool fShowZoomer;
-    
-    SkOSMenu::TriState fLCDState;
-    SkOSMenu::TriState fAAState;
-    SkOSMenu::TriState fFilterState;
-    SkOSMenu::TriState fHintingState;
-    unsigned   fFlipAxis;
-
-    int fScrollTestX, fScrollTestY;
-    SkScalar fZoomCenterX, fZoomCenterY;
-
-    //Stores global settings
-    SkOSMenu fAppMenu;
-    //Stores slide specific settings
-    SkOSMenu fSlideMenu;
-    int fTransitionNext;
-    int fTransitionPrev;
-    
-    void loadView(SkView*);
-    void updateTitle();
-
-    bool zoomIn();
-    bool zoomOut();
-    void updatePointer(int x, int y);
-    void magnify(SkCanvas* canvas);
-    void showZoomer(SkCanvas* canvas);
-    void updateMatrix();
-    void postAnimatingEvent();
-    void installDrawFilter(SkCanvas*);
-    int findByTitle(const char*);
-
-    typedef SkOSWindow INHERITED;
-};
-
-#endif
diff --git a/samplecode/SampleArc.cpp b/samplecode/SampleArc.cpp
deleted file mode 100644
index d418a4b..0000000
--- a/samplecode/SampleArc.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkComposeShader.h"
-#include "Sk1DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkLayerRasterizer.h"
-
-#include "SkParsePath.h"
-static void testparse() {
-    SkRect r;
-    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5));
-    SkPath p, p2;
-    SkString str, str2;
-
-    p.addRect(r);
-    SkParsePath::ToSVGString(p, &str);
-    SkParsePath::FromSVGString(str.c_str(), &p2);
-    SkParsePath::ToSVGString(p2, &str2);
-}
-
-class ArcsView : public SampleView {
-public:
-	ArcsView() {
-        testparse();
-        fSweep = SkIntToScalar(100);
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Arcs");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    static void drawRectWithLines(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
-        canvas->drawRect(r, p);
-        canvas->drawLine(r.fLeft, r.fTop, r.fRight, r.fBottom, p);
-        canvas->drawLine(r.fLeft, r.fBottom, r.fRight, r.fTop, p);
-        canvas->drawLine(r.fLeft, r.centerY(), r.fRight, r.centerY(), p);
-        canvas->drawLine(r.centerX(), r.fTop, r.centerX(), r.fBottom, p);
-    }
-    
-    static void draw_label(SkCanvas* canvas, const SkRect& rect,
-                            int start, int sweep) {
-        SkPaint paint;
-        
-        paint.setAntiAlias(true);
-        paint.setTextAlign(SkPaint::kCenter_Align);
-        
-        SkString    str;
-        
-        str.appendS32(start);
-        str.append(", ");
-        str.appendS32(sweep);
-        canvas->drawText(str.c_str(), str.size(), rect.centerX(),
-                         rect.fBottom + paint.getTextSize() * 5/4, paint);
-    }
-    
-    static void drawArcs(SkCanvas* canvas) {
-        SkPaint paint;
-        SkRect  r;
-        SkScalar w = SkIntToScalar(75);
-        SkScalar h = SkIntToScalar(50);
-
-        r.set(0, 0, w, h);
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        
-        canvas->save();
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(300));
-        
-        paint.setStrokeWidth(SkIntToScalar(1));
-        
-        static const int gAngles[] = {
-            0, 360,
-            0, 45,
-            0, -45,
-            720, 135,
-            -90, 269,
-            -90, 270,
-            -90, 271,
-            -180, -270,
-            225, 90
-        };
-        
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gAngles); i += 2) {
-            paint.setColor(SK_ColorBLACK);
-            drawRectWithLines(canvas, r, paint);
-
-            paint.setColor(SK_ColorRED);
-            canvas->drawArc(r, SkIntToScalar(gAngles[i]),
-                            SkIntToScalar(gAngles[i+1]), false, paint);
-            
-            draw_label(canvas, r, gAngles[i], gAngles[i+1]);
-
-            canvas->translate(w * 8 / 7, 0);
-        }
-        
-        canvas->restore();
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        fSweep = SampleCode::GetAnimScalar(SkIntToScalar(360)/24,
-                                           SkIntToScalar(360));
-//        fSweep = SkFloatToScalar(359.99f);
-
-        SkRect  r;
-        SkPaint paint;
-        
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(SkIntToScalar(2));
-        paint.setStyle(SkPaint::kStroke_Style);
-        
-        r.set(0, 0, SkIntToScalar(200), SkIntToScalar(200));
-        r.offset(SkIntToScalar(20), SkIntToScalar(20));
-        
-        if (false) {
-            const SkScalar d = SkIntToScalar(3);
-            const SkScalar rad[] = { d, d, d, d, d, d, d, d };
-            SkPath path;
-            path.addRoundRect(r, rad);
-            canvas->drawPath(path, paint);
-            return;
-        }
-
-        drawRectWithLines(canvas, r, paint);
-        
-   //     printf("----- sweep %g %X\n", SkScalarToFloat(fSweep), SkDegreesToRadians(fSweep));
-        
-        
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setColor(0x800000FF);
-        canvas->drawArc(r, 0, fSweep, true, paint);
-
-        paint.setColor(0x800FF000);
-        canvas->drawArc(r, 0, fSweep, false, paint);
-
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setColor(SK_ColorRED);
-        canvas->drawArc(r, 0, fSweep, true, paint);
-        
-        paint.setStrokeWidth(0);
-        paint.setColor(SK_ColorBLUE);
-        canvas->drawArc(r, 0, fSweep, false, paint);
-        
-        drawArcs(canvas);
-        this->inval(NULL);
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-     //   fSweep += SK_Scalar1;
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-    
-private:
-    SkScalar fSweep;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ArcsView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleAvoid.cpp b/samplecode/SampleAvoid.cpp
deleted file mode 100644
index 81050a3..0000000
--- a/samplecode/SampleAvoid.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkAvoidXfermode.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-class AvoidView : public SampleView {
-    SkShader* fShader;
-
-    enum {
-        W = 480,
-        H = 320
-    };
-public:
-    AvoidView() {
-        SkColor colors[] = { SK_ColorRED, SK_ColorYELLOW, SK_ColorGREEN, SK_ColorCYAN, SK_ColorBLUE };
-
-#if 0
-        SkPoint pts[] = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
-        fShader = SkGradientShader::CreateLinear(pts, colors, NULL,
-                                                 SK_ARRAY_COUNT(colors),
-                                                 SkShader::kMirror_TileMode);
-#else
-        SkPoint pts[] = { { SkIntToScalar(W)/2, SkIntToScalar(H)/2 } };
-        fShader = SkGradientShader::CreateRadial(pts[0], SkIntToScalar(H)/5,
-                                                 colors, NULL,
-                                                 SK_ARRAY_COUNT(colors),
-                                                 SkShader::kMirror_TileMode);
-#endif
-    }
-    
-    virtual ~AvoidView() {
-        fShader->unref();
-    }
-
-protected:
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "AvoidXfermode");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
-        
-        canvas->translate(r.width() / 6, r.height() / 6);
-
-        paint.setShader(fShader);
-        canvas->drawRect(r, paint);
-
-        static const struct {
-            int                     fTolerance;
-            SkAvoidXfermode::Mode   fMode;
-            float                   fDX, fDY;
-        } gData[] = {
-            { 16,       SkAvoidXfermode::kAvoidColor_Mode, 0, 0 },
-            { 255-16,   SkAvoidXfermode::kAvoidColor_Mode, 1, 0 },
-            { 16,       SkAvoidXfermode::kTargetColor_Mode, 0, 1 },
-            { 255-16,   SkAvoidXfermode::kTargetColor_Mode, 1, 1 },
-        };
-
-        paint.setShader(NULL);
-        paint.setColor(SK_ColorMAGENTA);
-        
-        SkPaint frameP;
-        frameP.setStyle(SkPaint::kStroke_Style);
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gData); i++) {
-            SkAvoidXfermode mode(SK_ColorGREEN, gData[i].fTolerance,
-                                 gData[i].fMode);
-            paint.setXfermode(&mode);
-            int div = 3;
-            SkRect rr = { 0, 0, r.width()/div, r.height()/div };
-            rr.offset(r.width()/4 - rr.width()/2, r.height()/4 - rr.height()/2);
-            rr.offset(r.width() * gData[i].fDX/2, r.height() * gData[i].fDY/2);
-            canvas->drawRect(rr, paint);
-            paint.setXfermode(NULL);
-
-            canvas->drawRect(rr, frameP);
-        }
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() {
-    return new AvoidView;
-}
-
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBigBlur.cpp b/samplecode/SampleBigBlur.cpp
deleted file mode 100644
index f5e632c..0000000
--- a/samplecode/SampleBigBlur.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkBlurMaskFilter.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-
-class BigBlurView : public SampleView {
-public:
-    BigBlurView() {
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "BigBlur");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        canvas->save();
-        paint.setColor(SK_ColorBLUE);
-        SkMaskFilter* mf = SkBlurMaskFilter::Create(
-            128,
-            SkBlurMaskFilter::kNormal_BlurStyle,
-            SkBlurMaskFilter::kHighQuality_BlurFlag);
-        paint.setMaskFilter(mf)->unref();
-        canvas->translate(200, 200);
-        canvas->drawCircle(100, 100, 200, paint);
-        canvas->restore();
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new BigBlurView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBigGradient.cpp b/samplecode/SampleBigGradient.cpp
deleted file mode 100644
index 8e08e15..0000000
--- a/samplecode/SampleBigGradient.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-
-static SkShader* make_grad(SkScalar w, SkScalar h) {
-    SkColor colors[] = { 0xFF000000, 0xFF333333 };
-    SkPoint pts[] = { { 0, 0 }, { w, h } };
-    return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
-                                          SkShader::kClamp_TileMode);
-}
-
-class BigGradientView : public SampleView {
-public:
-	BigGradientView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "BigGradient");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkRect r;
-        r.set(0, 0, this->width(), this->height());
-        SkPaint p;
-        p.setShader(make_grad(this->width(), this->height()))->unref();
-        canvas->drawRect(r, p);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new BigGradientView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBitmapRect.cpp b/samplecode/SampleBitmapRect.cpp
deleted file mode 100644
index 95ea1a7..0000000
--- a/samplecode/SampleBitmapRect.cpp
+++ /dev/null
@@ -1,110 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkOSFile.h"
-#include "SkStream.h"
-
-#include "SkGpuDevice.h"
-
-static void make_bitmap(SkBitmap* bitmap, GrContext* ctx) {
-    SkCanvas canvas;
-
-    if (ctx) {
-        SkDevice* dev = new SkGpuDevice(ctx, SkBitmap::kARGB_8888_Config, 64, 64);
-        canvas.setDevice(dev)->unref();
-        *bitmap = dev->accessBitmap(false);
-    } else {
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
-        bitmap->allocPixels();
-        canvas.setBitmapDevice(*bitmap);
-    }
-
-    canvas.drawColor(SK_ColorRED);
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    const SkPoint pts[] = { { 0, 0 }, { 64, 64 } };
-    const SkColor colors[] = { SK_ColorWHITE, SK_ColorBLUE };
-    paint.setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2,
-                                                   SkShader::kClamp_TileMode))->unref();
-    canvas.drawCircle(32, 32, 32, paint);
-}
-
-class BitmapRectView : public SampleView {
-public:
-	BitmapRectView() {
-        this->setBGColor(SK_ColorGRAY);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "BitmapRect");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        GrContext* ctx = SampleCode::GetGr();
-
-        const SkIRect src[] = {
-            { 0, 0, 32, 32 },
-            { 0, 0, 80, 80 },
-            { 32, 32, 96, 96 },
-            { -32, -32, 32, 32, }
-        };
-
-        SkPaint paint;
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setColor(ctx ? SK_ColorGREEN : SK_ColorYELLOW);
-
-        SkBitmap bitmap;
-        make_bitmap(&bitmap, ctx);
-
-        SkRect dstR = { 0, 200, 128, 380 };
-
-        canvas->translate(16, 40);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(src); i++) {
-            SkRect srcR;
-            srcR.set(src[i]);
-
-            canvas->drawBitmap(bitmap, 0, 0, &paint);
-            canvas->drawBitmapRect(bitmap, &src[i], dstR, &paint);
-
-            canvas->drawRect(dstR, paint);
-            canvas->drawRect(srcR, paint);
-            
-            canvas->translate(160, 0);
-        }
-    }
-    
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new BitmapRectView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBlur.cpp b/samplecode/SampleBlur.cpp
deleted file mode 100644
index aa92343..0000000
--- a/samplecode/SampleBlur.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkBlurMaskFilter.h"
-#include "SkColorPriv.h"
-#include "SkGradientShader.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkUtils.h"
-
-static SkBitmap make_bitmap() {
-    SkBitmap bm;
-    SkColorTable* ctable = new SkColorTable(256);
-
-    SkPMColor* c = ctable->lockColors();
-    for (int i = 0; i < 256; i++) {
-        c[i] = SkPackARGB32(255 - i, 0, 0, 0);
-    }
-    ctable->unlockColors(true);
-    bm.setConfig(SkBitmap::kIndex8_Config, 256, 256);
-    bm.allocPixels(ctable);
-    ctable->unref();
-
-    bm.lockPixels();
-    const float cx = bm.width() * 0.5f;
-    const float cy = bm.height() * 0.5f;
-    for (int y = 0; y < bm.height(); y++) {
-        float dy = y - cy;
-        dy *= dy;
-        uint8_t* p = bm.getAddr8(0, y);
-        for (int x = 0; x < 256; x++) {
-            float dx = x - cx;
-            dx *= dx;
-            float d = (dx + dy) / (cx/2);
-            int id = (int)d;
-            if (id > 255) {
-                id = 255;
-            }
-            p[x] = id;
-        }
-    }
-    bm.unlockPixels();
-    return bm;
-}
-
-class BlurView : public SampleView {
-    SkBitmap    fBM;
-public:
-	BlurView() {
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Blur");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(0xFFDDDDDD);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        drawBG(canvas);
-
-        SkBlurMaskFilter::BlurStyle NONE = SkBlurMaskFilter::BlurStyle(-999);
-        static const struct {
-            SkBlurMaskFilter::BlurStyle fStyle;
-            int                         fCx, fCy;
-        } gRecs[] = {
-            { NONE,                                 0,  0 },
-            { SkBlurMaskFilter::kInner_BlurStyle,  -1,  0 },
-            { SkBlurMaskFilter::kNormal_BlurStyle,  0,  1 },
-            { SkBlurMaskFilter::kSolid_BlurStyle,   0, -1 },
-            { SkBlurMaskFilter::kOuter_BlurStyle,   1,  0 },
-        };
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setTextSize(25);
-        canvas->translate(-40, 0);
-
-        SkBlurMaskFilter::BlurFlags flags = SkBlurMaskFilter::kNone_BlurFlag;
-        for (int j = 0; j < 2; j++) {
-            canvas->save();
-            paint.setColor(SK_ColorBLUE);
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gRecs); i++) {
-                if (gRecs[i].fStyle != NONE) {
-                    SkMaskFilter* mf = SkBlurMaskFilter::Create(20,
-                                                                gRecs[i].fStyle,
-                                                                flags);
-                    paint.setMaskFilter(mf)->unref();
-                } else {
-                    paint.setMaskFilter(NULL);
-                }
-                canvas->drawCircle(200 + gRecs[i].fCx*100.f,
-                                   200 + gRecs[i].fCy*100.f, 50, paint);
-            }
-            // draw text
-            {
-                SkMaskFilter* mf = SkBlurMaskFilter::Create(4,
-                                                            SkBlurMaskFilter::kNormal_BlurStyle,
-                                                            flags);
-                paint.setMaskFilter(mf)->unref();
-                SkScalar x = SkIntToScalar(70);
-                SkScalar y = SkIntToScalar(400);
-                paint.setColor(SK_ColorBLACK);
-                canvas->drawText("Hamburgefons Style", 18, x, y, paint);
-                canvas->drawText("Hamburgefons Style", 18, x, y + SkIntToScalar(50), paint);
-                paint.setMaskFilter(NULL);
-                paint.setColor(SK_ColorWHITE);
-                x -= SkIntToScalar(2);
-                y -= SkIntToScalar(2);
-                canvas->drawText("Hamburgefons Style", 18, x, y, paint);
-            }
-            canvas->restore();
-            flags = SkBlurMaskFilter::kHighQuality_BlurFlag;
-            canvas->translate(350, 0);
-        }
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new BlurView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleBox.cpp b/samplecode/SampleBox.cpp
deleted file mode 100644
index 0b1da07..0000000
--- a/samplecode/SampleBox.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-
-class SimpleView : public SampleView {
-public:
-	SimpleView() {
-        this->setBGColor(0xFFDDDDDD);
-	}
-	
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Box Gradient");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-	
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
-        paint.setStyle(SkPaint::kFill_Style);
-        
-        SkRect  r;
-        SkScalar x,y;
-        x = 10;
-        y = 10;
-
-        r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100));
-        for (int i = 0; i < 256; ++i) {
-            canvas->translate(1, 1);
-            paint.setColor(0xFF000000 + i * 0x00010000);
-            canvas->drawRect(r, paint);
-        }
-    }
-	
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new SimpleView; }
-static SkViewRegister reg(MyFactory);
\ No newline at end of file
diff --git a/samplecode/SampleCamera.cpp b/samplecode/SampleCamera.cpp
deleted file mode 100644
index a4af2de..0000000
--- a/samplecode/SampleCamera.cpp
+++ /dev/null
@@ -1,106 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkCamera.h"
-#include "SkEmbossMaskFilter.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkRandom.h"
-#include "SkImageDecoder.h"
-
-class CameraView : public SampleView {
-    SkTDArray<SkShader*> fShaders;
-    int     fShaderIndex;
-    bool    fFrontFace;
-public:
-	CameraView() {
-        fRX = fRY = fRZ = 0;
-        fShaderIndex = 0;
-        fFrontFace = false;
-
-        for (int i = 0;; i++) {
-            SkString str;
-            str.printf("/skimages/elephant%d.jpeg", i);
-            SkBitmap bm;
-            if (SkImageDecoder::DecodeFile(str.c_str(), &bm)) {
-                SkShader* s = SkShader::CreateBitmapShader(bm,
-                                                           SkShader::kClamp_TileMode,
-                                                           SkShader::kClamp_TileMode);
-                
-                SkRect src = { 0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()) };
-                SkRect dst = { -150, -150, 150, 150 };
-                SkMatrix matrix;
-                matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
-                s->setLocalMatrix(matrix);
-                *fShaders.append() = s;
-            } else {
-                break;
-            }
-        }
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-    virtual ~CameraView() {
-        fShaders.unrefAll();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt))
-        {
-            SampleCode::TitleR(evt, "Camera");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(this->width()/2, this->height()/2);
-
-        Sk3DView    view;
-        view.rotateX(fRX);
-        view.rotateY(fRY);
-        view.applyToCanvas(canvas);
-        
-        SkPaint paint;
-        if (fShaders.count() > 0) {
-            bool frontFace = view.dotWithNormal(0, 0, SK_Scalar1) < 0;
-            if (frontFace != fFrontFace) {
-                fFrontFace = frontFace;
-                fShaderIndex = (fShaderIndex + 1) % fShaders.count();
-            }
-        
-            paint.setAntiAlias(true);
-            paint.setShader(fShaders[fShaderIndex]);
-            SkRect r = { -150, -150, 150, 150 };
-            canvas->drawRoundRect(r, 30, 30, paint);
-        }
-        
-        fRY += SampleCode::GetAnimSecondsDelta() * 90;
-        if (fRY >= SkIntToScalar(360)) {
-            fRY = 0;
-        }
-        this->inval(NULL);
-    }
-
-private:
-    SkScalar fRX, fRY, fRZ;
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new CameraView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleCircle.cpp b/samplecode/SampleCircle.cpp
deleted file mode 100644
index c3f986d..0000000
--- a/samplecode/SampleCircle.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-
-// ensure that we don't accidentally screw up the bounds when the oval is
-// fractional, and the impl computes the center and radii, and uses them to
-// reconstruct the edges of the circle.
-// see bug# 1504910
-static void test_circlebounds(SkCanvas* canvas) {
-#ifdef SK_SCALAR_IS_FLOAT
-    SkRect r = { 1.39999998f, 1, 21.3999996f, 21 };
-    SkPath p;
-    p.addOval(r);
-    SkASSERT(r == p.getBounds());
-#endif
-}
-
-class CircleView : public SampleView {
-public:
-    static const SkScalar ANIM_DX;
-    static const SkScalar ANIM_DY;
-    static const SkScalar ANIM_RAD;
-    SkScalar fDX, fDY, fRAD;
-
-    CircleView() {
-        fDX = fDY = fRAD = 0;
-        fN = 3;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Circles");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void circle(SkCanvas* canvas, int width, bool aa) {
-        SkPaint paint;
-        
-        paint.setAntiAlias(aa);
-        if (width < 0) {
-            paint.setStyle(SkPaint::kFill_Style);
-        } else {
-            paint.setStyle(SkPaint::kStroke_Style);
-            paint.setStrokeWidth(SkIntToScalar(width));
-        }
-        canvas->drawCircle(0, 0, SkIntToScalar(9) + fRAD, paint);
-    }
-    
-    void drawSix(SkCanvas* canvas, SkScalar dx, SkScalar dy) {
-        for (int width = -1; width <= 1; width++) {
-            canvas->save();
-            circle(canvas, width, false);
-            canvas->translate(0, dy);
-            circle(canvas, width, true);
-            canvas->restore();
-            canvas->translate(dx, 0);
-        }
-    }
-    
-    static void blowup(SkCanvas* canvas, const SkIRect& src, const SkRect& dst) {
-        SkDevice* device = canvas->getDevice();
-        const SkBitmap& bm = device->accessBitmap(false);
-        canvas->drawBitmapRect(bm, &src, dst, NULL);
-    }
-    
-    static void make_poly(SkPath* path, int n) {
-        if (n <= 0) {
-            return;
-        }
-        path->incReserve(n + 1);
-        path->moveTo(SK_Scalar1, 0);
-        SkScalar step = SK_ScalarPI * 2 / n;
-        SkScalar angle = 0;
-        for (int i = 1; i < n; i++) {
-            angle += step;
-            SkScalar c, s = SkScalarSinCos(angle, &c);
-            path->lineTo(c, s);
-        }
-        path->close();
-    }
-    
-    static void rotate(SkCanvas* canvas, SkScalar angle, SkScalar px, SkScalar py) {
-        canvas->translate(-px, -py);
-        canvas->rotate(angle);
-        canvas->translate(px, py);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-//        canvas->drawCircle(250, 250, 220, paint);
-        SkMatrix matrix;
-        matrix.setScale(SkIntToScalar(100), SkIntToScalar(100));
-        matrix.postTranslate(SkIntToScalar(200), SkIntToScalar(200));
-        canvas->concat(matrix);
-        for (int n = 3; n < 20; n++) {
-            SkPath path;
-            make_poly(&path, n);
-            SkAutoCanvasRestore acr(canvas, true);
-            canvas->rotate(SkIntToScalar(10) * (n - 3));
-            canvas->translate(-SK_Scalar1, 0);
-            canvas->drawPath(path, paint);
-        }
-    }
-    
-private:
-    int fN;
-    typedef SampleView INHERITED;
-};
-
-const SkScalar CircleView::ANIM_DX(SK_Scalar1 / 67);
-const SkScalar CircleView::ANIM_DY(SK_Scalar1 / 29);
-const SkScalar CircleView::ANIM_RAD(SK_Scalar1 / 19);
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new CircleView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleClamp.cpp b/samplecode/SampleClamp.cpp
deleted file mode 100644
index c521f81..0000000
--- a/samplecode/SampleClamp.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkRandom.h"
-#include "SkGradientShader.h"
-#include "SkPicture.h"
-
-static SkShader* make_linear() {
-    SkPoint pts[] = { 0, 0, SK_Scalar1/500, SK_Scalar1/500 };
-    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE };
-    return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
-                                          SkShader::kClamp_TileMode);
-}
-
-class ClampView : public SampleView {
-    SkShader*   fGrad;
-
-public:
-    ClampView() {
-        fGrad = make_linear();
-    }
-
-    virtual ~ClampView() {
-        fGrad->unref();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Clamp");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setShader(fGrad);
-
-//        canvas->translate(this->width()/2, this->height()/2);
-        canvas->translate(64, 64);
-        canvas->drawPaint(paint);
-
-        SkPicture pic;
-        SkCanvas* c = pic.beginRecording(100, 100, 0);
-        SkCanvas::LayerIter layerIterator(c, false);
-        layerIterator.next();
-        layerIterator.done();
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ClampView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleClip.cpp b/samplecode/SampleClip.cpp
deleted file mode 100644
index 570f0b9..0000000
--- a/samplecode/SampleClip.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkColorPriv.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-#include "SkRandom.h"
-
-#define W   150
-#define H   200
-
-static void show_text(SkCanvas* canvas, bool doAA) {
-    SkRandom rand;
-    SkPaint paint;
-    paint.setAntiAlias(doAA);
-    paint.setLCDRenderText(true);
-    paint.setTextSize(SkIntToScalar(20));
-    
-    for (int i = 0; i < 200; ++i) {
-        paint.setColor((SK_A32_MASK << SK_A32_SHIFT) | rand.nextU());
-        canvas->drawText("Hamburgefons", 12,
-                         rand.nextSScalar1() * W, rand.nextSScalar1() * H + 20,
-                         paint);
-    }
-}
-
-static bool valid(int i) {
-    return i < 15 && i > 7;
-}
-
-static void show_fill(SkCanvas* canvas, bool doAA) {
-    SkRandom rand;
-    SkPaint paint;
-    paint.setAntiAlias(doAA);
-    
-    for (int i = 0; i < 50; ++i) {
-        SkRect r;
-        SkPath p;
-        
-        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
-                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
-        paint.setColor(rand.nextU());
-        canvas->drawRect(r, paint);
-        
-        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
-                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
-        paint.setColor(rand.nextU());
-        p.addOval(r);
-        canvas->drawPath(p, paint);
-    }
-}
-
-static SkScalar randRange(SkRandom& rand, SkScalar min, SkScalar max) {
-    SkASSERT(min <= max);
-    return min + SkScalarMul(rand.nextUScalar1(), max - min);
-}
-
-static void show_stroke(SkCanvas* canvas, bool doAA, SkScalar strokeWidth, int n) {
-    SkRandom rand;
-    SkPaint paint;
-    paint.setAntiAlias(doAA);
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeWidth(strokeWidth);
-    
-    for (int i = 0; i < n; ++i) {
-        SkRect r;
-        SkPath p;
-        
-        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
-                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
-        paint.setColor(rand.nextU());
-        canvas->drawRect(r, paint);
-        
-        r.setXYWH(rand.nextSScalar1() * W, rand.nextSScalar1() * H,
-                  rand.nextUScalar1() * W, rand.nextUScalar1() * H);
-        paint.setColor(rand.nextU());
-        p.addOval(r);
-        canvas->drawPath(p, paint);
-
-        const SkScalar minx = -SkIntToScalar(W)/4;
-        const SkScalar maxx = 5*SkIntToScalar(W)/4;
-        const SkScalar miny = -SkIntToScalar(H)/4;
-        const SkScalar maxy = 5*SkIntToScalar(H)/4;
-        paint.setColor(rand.nextU());
-        canvas->drawLine(randRange(rand, minx, maxx), randRange(rand, miny, maxy),
-                         randRange(rand, minx, maxx), randRange(rand, miny, maxy),
-                         paint);
-    }
-}
-
-static void show_hair(SkCanvas* canvas, bool doAA) {
-    show_stroke(canvas, doAA, 0, 150);
-}
-
-static void show_thick(SkCanvas* canvas, bool doAA) {
-    show_stroke(canvas, doAA, SkIntToScalar(5), 50);
-}
-
-typedef void (*CanvasProc)(SkCanvas*, bool);
-
-#include "SkAAClip.h"
-
-class ClipView : public SampleView {
-public:
-    ClipView() {
-        SkAAClip clip;
-        SkIRect r = { -2, -3, 842, 18 };
-        clip.setRect(r);
-    }
-
-    virtual ~ClipView() {
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Clip");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorWHITE);
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
-
-        static const CanvasProc gProc[] = {
-            show_text, show_thick, show_hair, show_fill
-        };
-        
-        SkRect r = { 0, 0, SkIntToScalar(W), SkIntToScalar(H) };
-        SkPath clipPath;
-        r.inset(SK_Scalar1 / 4, SK_Scalar1 / 4);
-        clipPath.addRoundRect(r, SkIntToScalar(20), SkIntToScalar(20));
-
-//        clipPath.toggleInverseFillType();
-
-        for (int aa = 0; aa <= 1; ++aa) {
-            canvas->save();
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); ++i) {
-                canvas->save();
-                canvas->clipPath(clipPath, SkRegion::kIntersect_Op, true);
-//                canvas->drawColor(SK_ColorWHITE);
-                gProc[i](canvas, SkToBool(aa));
-                canvas->restore();
-                canvas->translate(W * SK_Scalar1 * 8 / 7, 0);
-            }
-            canvas->restore();
-            canvas->translate(0, H * SK_Scalar1 * 8 / 7);
-        }
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ClipView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleCode.h b/samplecode/SampleCode.h
deleted file mode 100644
index 197e0f1..0000000
--- a/samplecode/SampleCode.h
+++ /dev/null
@@ -1,145 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SampleCode_DEFINED
-#define SampleCode_DEFINED
-
-#include "SkColor.h"
-#include "SkEvent.h"
-#include "SkKey.h"
-#include "SkView.h"
-class SkOSMenu;
-class GrContext;
-
-class SampleCode {
-public:
-    static bool KeyQ(const SkEvent&, SkKey* outKey);
-    static bool CharQ(const SkEvent&, SkUnichar* outUni);
-
-    static bool TitleQ(const SkEvent&);
-    static void TitleR(SkEvent*, const char title[]);
-    static bool RequestTitle(SkView* view, SkString* title);
-    
-    static bool PrefSizeQ(const SkEvent&);
-    static void PrefSizeR(SkEvent*, SkScalar width, SkScalar height);
-
-    static bool FastTextQ(const SkEvent&);
-
-    static SkMSec GetAnimTime();
-    static SkMSec GetAnimTimeDelta();
-    static SkScalar GetAnimSecondsDelta();
-    static SkScalar GetAnimScalar(SkScalar speedPerSec, SkScalar period = 0);
-
-    static GrContext* GetGr();
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-// interface that constructs SkViews
-class SkViewFactory : public SkRefCnt {
-public:
-    virtual SkView* operator() () const = 0;
-};
-
-typedef SkView* (*SkViewCreateFunc)();
-
-// wraps SkViewCreateFunc in SkViewFactory interface
-class SkFuncViewFactory : public SkViewFactory {
-public:
-    SkFuncViewFactory(SkViewCreateFunc func);
-    virtual SkView* operator() () const SK_OVERRIDE;
-    
-private:
-    SkViewCreateFunc fCreateFunc;
-};
-
-namespace skiagm {
-class GM;
-}
-
-// factory function that creates a skiagm::GM
-typedef skiagm::GM* (*GMFactoryFunc)(void*);
-
-// Takes a GM factory function and implements the SkViewFactory interface 
-// by making the GM and wrapping it in a GMSampleView. GMSampleView bridges
-// the SampleView interface to skiagm::GM.
-class SkGMSampleViewFactory : public SkViewFactory {
-public:
-    SkGMSampleViewFactory(GMFactoryFunc func);
-    virtual SkView* operator() () const SK_OVERRIDE;
-private:
-    GMFactoryFunc fFunc;
-};
-
-class SkViewRegister : public SkRefCnt {
-public:
-    explicit SkViewRegister(SkViewFactory*);
-    explicit SkViewRegister(SkViewCreateFunc);
-    explicit SkViewRegister(GMFactoryFunc);
-
-    ~SkViewRegister() {
-        fFact->unref();
-    }
-    
-    static const SkViewRegister* Head() { return gHead; }
-    
-    SkViewRegister* next() const { return fChain; }
-    const SkViewFactory*   factory() const { return fFact; }
-    
-private:
-    SkViewFactory*  fFact;
-    SkViewRegister* fChain;
-    
-    static SkViewRegister* gHead;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class SampleView : public SkView {
-public:
-    SampleView() : fBGColor(SK_ColorWHITE), fRepeatCount(1) {
-        fUsePipe = false;
-    }
-
-    void setBGColor(SkColor color) { fBGColor = color; }
-
-    static bool IsSampleView(SkView*);
-    static bool SetRepeatDraw(SkView*, int count);
-    static bool SetUsePipe(SkView*, bool);
-    
-    /**
-     *  Call this to request menu items from a SampleView.
-     *  Subclassing notes: A subclass of SampleView can overwrite this method 
-     *  to add new items of various types to the menu and change its title.
-     *  The events attached to any new menu items must be handled in its onEvent
-     *  method. See SkOSMenu.h for helper functions.   
-     */
-    virtual void requestMenu(SkOSMenu* menu) {}
-
-protected:
-    virtual void onDrawBackground(SkCanvas*);
-    virtual void onDrawContent(SkCanvas*) = 0;
-    
-    // overrides
-    virtual bool onEvent(const SkEvent& evt);
-    virtual bool onQuery(SkEvent* evt);
-    virtual void draw(SkCanvas*);
-    virtual void onDraw(SkCanvas*);
-
-    bool fUsePipe;
-    SkColor fBGColor;
-    
-private:
-    int fRepeatCount;
-
-    typedef SkView INHERITED;
-};
-
-#endif
-
diff --git a/samplecode/SampleColorFilter.cpp b/samplecode/SampleColorFilter.cpp
deleted file mode 100644
index 52bf950..0000000
--- a/samplecode/SampleColorFilter.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkColorFilter.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-#include "SkShader.h"
-
-static int inflate5To8(int x) {
-    return (x << 3) | (x >> 2);
-}
-
-static int trunc5(int x) {
-    return x >> 3;
-}
-
-#define SK_R16_BITS 5
-
-static int round5_slow(int x) {    
-    int orig = x & 7;
-    int fake = x >> 5;
-    int trunc = x >> 3;
-
-    int diff = fake - orig;
-
-    int bias = 0;
-    if (diff > 4) {
-        bias = -1;
-    } else if (diff < -4) {
-        bias = 1;
-    }
-    return trunc + bias;
-}
-
-static int round5_fast(int x) {
-    int result = x + 3 - (x >> 5) + (x >> 7);
-    result >>= 3;
-
-    {
-        int r2 = round5_slow(x);
-        SkASSERT(r2 == result);
-    }
-    return result;
-}
-
-static void test_5bits() {
-    int e0 = 0;
-    int e1 = 0;
-    int e2 = 0;
-    int ae0 = 0;
-    int ae1 = 0;
-    int ae2 = 0;
-    for (int i = 0; i < 256; i++) {
-        int t0 = trunc5(i);
-        int t1 = round5_fast(i);
-        int t2 = trunc5(i);
-        int v0 = inflate5To8(t0);
-        int v1 = inflate5To8(t1);
-        int v2 = inflate5To8(t2);
-        int err0 = i - v0;
-        int err1 = i - v1;
-        int err2 = i - v2;
-        SkDebugf("--- %3d : trunc=%3d (%2d) round:%3d (%2d) \n"/*new:%d (%2d)\n"*/, i,
-                 v0, err0, v1, err1, v2, err2);
-        
-
-        e0 += err0;
-        e1 += err1;
-        e2 += err2;
-        ae0 += SkAbs32(err0);
-        ae1 += SkAbs32(err1);
-        ae2 += SkAbs32(err2);
-    }
-    SkDebugf("--- trunc: %d %d  round: %d %d new: %d %d\n", e0, ae0, e1, ae1, e2, ae2);
-}
-
-static SkShader* createChecker() {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
-    bm.allocPixels();
-    bm.lockPixels();
-    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = SkPreMultiplyColor(0xFFFFFFFF);
-    *bm.getAddr32(0, 1) = *bm.getAddr32(1, 0) = SkPreMultiplyColor(0xFFCCCCCC);
-    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode);
-
-    SkMatrix m;
-    m.setScale(12, 12);
-    s->setLocalMatrix(m);
-    return s;
-}
-
-static SkBitmap createBitmap(int n) {
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
-    bitmap.allocPixels();
-    bitmap.eraseColor(0);
-    
-    SkCanvas canvas(bitmap);
-    SkRect r;
-    r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
-    r.inset(SK_Scalar1, SK_Scalar1);
-
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    
-    paint.setColor(SK_ColorRED);
-    canvas.drawOval(r, paint);
-
-    r.inset(SK_Scalar1*n/4, SK_Scalar1*n/4);
-    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    paint.setColor(0x800000FF);
-    canvas.drawOval(r, paint);
-    
-    return bitmap;
-}
-
-class ColorFilterView : public SampleView {
-    SkBitmap fBitmap;
-    SkShader* fShader;
-    enum {
-        N = 64
-    };
-public:
-    ColorFilterView() {
-        fBitmap = createBitmap(N);
-        fShader = createChecker();
-      
-//        test_5bits();
-    }
-
-    virtual ~ColorFilterView() {
-        fShader->unref();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "ColorFilter");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawBackground(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setShader(fShader);
-        canvas->drawPaint(paint);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        if (false) {
-            SkPaint p;
-            p.setAntiAlias(true);
-            SkRect r = { 20.4f, 10, 20.6f, 20 };
-            canvas->drawRect(r, p);
-            r.set(30.9f, 10, 31.1f, 20);
-            canvas->drawRect(r, p);
-            return;
-        }
-        
-        static const SkXfermode::Mode gModes[] = {
-            SkXfermode::kClear_Mode,
-            SkXfermode::kSrc_Mode,
-            SkXfermode::kDst_Mode,
-            SkXfermode::kSrcOver_Mode,
-            SkXfermode::kDstOver_Mode,
-            SkXfermode::kSrcIn_Mode,
-            SkXfermode::kDstIn_Mode,
-            SkXfermode::kSrcOut_Mode,
-            SkXfermode::kDstOut_Mode,
-            SkXfermode::kSrcATop_Mode,
-            SkXfermode::kDstATop_Mode,
-            SkXfermode::kXor_Mode,
-            SkXfermode::kPlus_Mode,
-            SkXfermode::kMultiply_Mode,
-        };
-    
-        static const SkColor gColors[] = {
-            0xFF000000,
-            0x80000000,
-            0xFF00FF00,
-            0x8000FF00,
-            0x00000000,
-        };
-
-        float scale = 1.5f;
-        SkPaint paint;
-        canvas->translate(N / 8, N / 8);
-
-        for (size_t y = 0; y < SK_ARRAY_COUNT(gColors); y++) {
-            for (size_t x = 0; x < SK_ARRAY_COUNT(gModes); x++) {
-                SkColorFilter* cf = SkColorFilter::CreateModeFilter(gColors[y], gModes[x]);
-                SkSafeUnref(paint.setColorFilter(cf));
-                canvas->drawBitmap(fBitmap, x * N * 1.25f, y * N * scale, &paint);
-            }
-        }
-        
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ColorFilterView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleComplexClip.cpp b/samplecode/SampleComplexClip.cpp
deleted file mode 100644
index e7f9a01..0000000
--- a/samplecode/SampleComplexClip.cpp
+++ /dev/null
@@ -1,154 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkPath.h"
-#include "SkView.h"
-
-class ComplexClipView : public SampleView {
-public:
-	ComplexClipView() {
-        this->setBGColor(0xFFA0DDA0);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "ComplexClip");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPath path;
-        path.moveTo(SkIntToScalar(0),   SkIntToScalar(50));
-        path.quadTo(SkIntToScalar(0),   SkIntToScalar(0),   SkIntToScalar(50),  SkIntToScalar(0));
-        path.lineTo(SkIntToScalar(175), SkIntToScalar(0));
-        path.quadTo(SkIntToScalar(200), SkIntToScalar(0),   SkIntToScalar(200), SkIntToScalar(25));
-        path.lineTo(SkIntToScalar(200), SkIntToScalar(150));
-        path.quadTo(SkIntToScalar(200), SkIntToScalar(200), SkIntToScalar(150), SkIntToScalar(200));
-        path.lineTo(SkIntToScalar(0),   SkIntToScalar(200));
-        path.close();
-        path.moveTo(SkIntToScalar(50),  SkIntToScalar(50));
-        path.lineTo(SkIntToScalar(150), SkIntToScalar(50));
-        path.lineTo(SkIntToScalar(150), SkIntToScalar(125));
-        path.quadTo(SkIntToScalar(150), SkIntToScalar(150), SkIntToScalar(125), SkIntToScalar(150));
-        path.lineTo(SkIntToScalar(50),  SkIntToScalar(150));
-        path.close();
-        path.setFillType(SkPath::kEvenOdd_FillType);
-        SkColor pathColor = SK_ColorBLACK;
-        SkPaint pathPaint;
-        pathPaint.setAntiAlias(true);
-        pathPaint.setColor(pathColor);
-
-        SkPath clipA;
-        clipA.moveTo(SkIntToScalar(10),  SkIntToScalar(20));
-        clipA.lineTo(SkIntToScalar(165), SkIntToScalar(22));
-        clipA.lineTo(SkIntToScalar(70),  SkIntToScalar(105));
-        clipA.lineTo(SkIntToScalar(165), SkIntToScalar(177));
-        clipA.lineTo(SkIntToScalar(-5),  SkIntToScalar(180));
-        clipA.close();
-        SkColor colorA = SK_ColorCYAN;
-
-        SkPath clipB;
-        clipB.moveTo(SkIntToScalar(40),  SkIntToScalar(10));
-        clipB.lineTo(SkIntToScalar(190), SkIntToScalar(15));
-        clipB.lineTo(SkIntToScalar(195), SkIntToScalar(190));
-        clipB.lineTo(SkIntToScalar(40),  SkIntToScalar(185));
-        clipB.lineTo(SkIntToScalar(155), SkIntToScalar(100));
-        clipB.close();
-        SkColor colorB = SK_ColorRED;
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(0);
-
-        canvas->translate(SkIntToScalar(10),SkIntToScalar(10));
-        canvas->drawPath(path, pathPaint);
-        paint.setColor(colorA);
-        canvas->drawPath(clipA, paint);
-        paint.setColor(colorB);
-        canvas->drawPath(clipB, paint);
-
-        static const struct {
-            SkRegion::Op fOp;
-            const char*  fName;
-        } gOps[] = { //extra spaces in names for measureText
-            {SkRegion::kIntersect_Op,         "Isect "},
-            {SkRegion::kDifference_Op,        "Diff " },
-            {SkRegion::kUnion_Op,             "Union "},
-            {SkRegion::kXOR_Op,               "Xor "  },
-            {SkRegion::kReverseDifference_Op, "RDiff "}
-        };
-
-        canvas->translate(0, SkIntToScalar(40));
-        canvas->scale(3 * SK_Scalar1 / 4, 3 * SK_Scalar1 / 4);
-        canvas->save();
-
-        for (int invA = 0; invA < 2; ++invA) {
-            for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
-                int idx = invA * SK_ARRAY_COUNT(gOps) + op;
-                if (!(idx % 3)) {
-                    canvas->restore();
-                    canvas->translate(0, SkIntToScalar(250));
-                    canvas->save();
-                }
-                canvas->save();
-                    // set clip
-                    clipA.setFillType(invA ? SkPath::kInverseEvenOdd_FillType :
-                                             SkPath::kEvenOdd_FillType);
-                    canvas->clipPath(clipA);
-                    canvas->clipPath(clipB, gOps[op].fOp);
-
-                    // draw path clipped
-                    canvas->drawPath(path, pathPaint);
-                canvas->restore();
-
-                // draw path in hairline
-                paint.setColor(pathColor);
-                canvas->drawPath(path, paint);
-
-                // draw clips in hair line
-                paint.setColor(colorA);
-                canvas->drawPath(clipA, paint);
-                paint.setColor(colorB);
-                canvas->drawPath(clipB, paint);
-
-                paint.setTextSize(SkIntToScalar(20));
-
-                SkScalar txtX = SkIntToScalar(55);
-                paint.setColor(colorA);
-                const char* aTxt = invA ? "InverseA " : "A ";
-                canvas->drawText(aTxt, strlen(aTxt), txtX, SkIntToScalar(220), paint);
-                txtX += paint.measureText(aTxt, strlen(aTxt));
-                paint.setColor(SK_ColorBLACK);
-                canvas->drawText(gOps[op].fName, strlen(gOps[op].fName),
-                                    txtX, SkIntToScalar(220), paint);
-                txtX += paint.measureText(gOps[op].fName, strlen(gOps[op].fName));
-                paint.setColor(colorB);
-                canvas->drawText("B", 1, txtX, SkIntToScalar(220), paint);
-
-                canvas->translate(SkIntToScalar(250),0);
-            }
-        }
-        canvas->restore();
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ComplexClipView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleConcavePaths.cpp b/samplecode/SampleConcavePaths.cpp
deleted file mode 100644
index 1659ec0..0000000
--- a/samplecode/SampleConcavePaths.cpp
+++ /dev/null
@@ -1,153 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkParsePath.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkGeometry.h"
-
-class ConcavePathView : public SampleView {
-public:
-	ConcavePathView() {}
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "ConcavePaths");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {        
-        SkPaint paint;
-        
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kFill_Style);
-
-        // Concave test
-        if (1) {
-            SkPath path;
-            canvas->translate(0, 0);
-            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
-            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
-            canvas->drawPath(path, paint);
-        }
-        // Reverse concave test
-        if (1) {
-            SkPath path;
-            canvas->save();
-            canvas->translate(100, 0);
-            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
-            path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
-            canvas->drawPath(path, paint);
-            canvas->restore();
-        }
-        // Bowtie (intersection)
-        if (1) {
-            SkPath path;
-            canvas->save();
-            canvas->translate(200, 0);
-            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
-            canvas->drawPath(path, paint);
-            canvas->restore();
-        }
-        // "fake" bowtie (concave, but no intersection)
-        if (1) {
-            SkPath path;
-            canvas->save();
-            canvas->translate(300, 0);
-            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(50), SkIntToScalar(40));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
-            path.lineTo(SkIntToScalar(50), SkIntToScalar(60));
-            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
-            canvas->drawPath(path, paint);
-            canvas->restore();
-        }
-        // Fish test (intersection/concave)
-        if (1) {
-            SkPath path;
-            canvas->save();
-            canvas->translate(0, 100);
-            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
-            path.lineTo(SkIntToScalar(70), SkIntToScalar(50));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
-            path.lineTo(SkIntToScalar(0), SkIntToScalar(50));
-            canvas->drawPath(path, paint);
-            canvas->restore();
-        }
-        // Collinear test
-        if (1) {
-            SkPath path;
-            canvas->save();
-            canvas->translate(100, 100);
-            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(50), SkIntToScalar(80));
-            canvas->drawPath(path, paint);
-            canvas->restore();
-        }
-        // Hole test
-        if (1) {
-            SkPath path;
-            canvas->save();
-            canvas->translate(200, 100);
-            path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
-            path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
-            path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
-            path.moveTo(SkIntToScalar(30), SkIntToScalar(30));
-            path.lineTo(SkIntToScalar(30), SkIntToScalar(70));
-            path.lineTo(SkIntToScalar(70), SkIntToScalar(70));
-            path.lineTo(SkIntToScalar(70), SkIntToScalar(30));
-            canvas->drawPath(path, paint);
-            canvas->restore();
-        }
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ConcavePathView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleCull.cpp b/samplecode/SampleCull.cpp
deleted file mode 100644
index 778d0fc..0000000
--- a/samplecode/SampleCull.cpp
+++ /dev/null
@@ -1,196 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkCornerPathEffect.h"
-#include "SkCullPoints.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkRandom.h"
-
-static void addbump(SkPath* path, const SkPoint pts[2], SkScalar bump) {
-    SkVector    tang;
-    
-    tang.setLength(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY, bump);
-
-    path->lineTo(SkScalarHalf(pts[0].fX + pts[1].fX) - tang.fY,
-                 SkScalarHalf(pts[0].fY + pts[1].fY) + tang.fX);
-    path->lineTo(pts[1]);
-}
-
-static void subdivide(SkPath* path, SkScalar bump) {
-    SkPath::Iter    iter(*path, false);
-    SkPoint         pts[4];
-    SkPath          tmp;
-    
-    for (;;)
-        switch (iter.next(pts)) {
-        case SkPath::kMove_Verb:
-            tmp.moveTo(pts[0]);
-            break;
-        case SkPath::kLine_Verb:
-            addbump(&tmp, pts, bump);
-            bump = -bump;
-            break;
-        case SkPath::kDone_Verb:
-            goto FINISH;
-        default:
-            break;
-        }
-
-FINISH:
-    path->swap(tmp);
-}
-
-static SkIPoint* getpts(const SkPath& path, int* count) {
-    SkPoint     pts[4];
-    int         n = 1;
-    SkIPoint*   array;
-
-    {
-        SkPath::Iter    iter(path, false);
-        for (;;)
-            switch (iter.next(pts)) {
-            case SkPath::kLine_Verb:
-                n += 1;
-                break;
-            case SkPath::kDone_Verb:
-                goto FINISHED;
-            default:
-                break;
-            }
-    }
-
-FINISHED:
-    array = new SkIPoint[n];
-    n = 0;
-
-    {
-        SkPath::Iter    iter(path, false);
-        for (;;)
-            switch (iter.next(pts)) {
-            case SkPath::kMove_Verb:
-                array[n++].set(SkScalarRound(pts[0].fX), SkScalarRound(pts[0].fY));
-                break;
-            case SkPath::kLine_Verb:
-                array[n++].set(SkScalarRound(pts[1].fX), SkScalarRound(pts[1].fY));
-                break;
-            case SkPath::kDone_Verb:
-                goto FINISHED2;
-            default:
-                break;
-            }
-    }
-    
-FINISHED2:
-    *count = n;
-    return array;
-}
-
-static SkScalar nextScalarRange(SkRandom& rand, SkScalar min, SkScalar max) {
-    return min + SkScalarMul(rand.nextUScalar1(), max - min);
-}
-
-class CullView : public SampleView {
-public:
-	CullView() {
-        fClip.set(0, 0, SkIntToScalar(160), SkIntToScalar(160));
-        
-        SkRandom    rand;
-        
-        for (int i = 0; i < 50; i++) {
-            SkScalar x = nextScalarRange(rand, -fClip.width()*1, fClip.width()*2);
-            SkScalar y = nextScalarRange(rand, -fClip.height()*1, fClip.height()*2);
-            if (i == 0)
-                fPath.moveTo(x, y);
-            else
-                fPath.lineTo(x, y);
-        }
-        
-        SkScalar bump = fClip.width()/8;
-        subdivide(&fPath, bump);
-        subdivide(&fPath, bump);
-        subdivide(&fPath, bump);
-        fPoints = getpts(fPath, &fPtCount);
-        
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-    virtual ~CullView() {
-        delete[] fPoints;
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Culling");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkAutoCanvasRestore ar(canvas, true);
-
-        canvas->translate(  SkScalarHalf(this->width() - fClip.width()),
-                            SkScalarHalf(this->height() - fClip.height()));
-
-   //     canvas->scale(SK_Scalar1*3, SK_Scalar1*3, 0, 0);
-
-        SkPaint paint;
-        
-    //    paint.setAntiAliasOn(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-
-        canvas->drawRect(fClip, paint);
-
-#if 1
-        paint.setColor(0xFF555555);
-        paint.setStrokeWidth(SkIntToScalar(2));
-//        paint.setPathEffect(new SkCornerPathEffect(SkIntToScalar(30)))->unref();
-        canvas->drawPath(fPath, paint);
-//        paint.setPathEffect(NULL);
-#endif
-
-        SkPath  tmp;
-        SkIRect iclip;
-        fClip.round(&iclip);
-        
-        SkCullPointsPath    cpp(iclip, &tmp);
-        
-        cpp.moveTo(fPoints[0].fX, fPoints[0].fY);
-        for (int i = 0; i < fPtCount; i++)
-            cpp.lineTo(fPoints[i].fX, fPoints[i].fY);
-        
-        paint.setColor(SK_ColorRED);
-        paint.setStrokeWidth(SkIntToScalar(3));
-        paint.setStrokeJoin(SkPaint::kRound_Join);
-        canvas->drawPath(tmp, paint);
-        
-        this->inval(NULL);
-    }
-    
-private:
-    SkRect      fClip;
-    SkIPoint*   fPoints;
-    SkPath      fPath;
-    int         fPtCount;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new CullView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDash.cpp b/samplecode/SampleDash.cpp
deleted file mode 100644
index fc0d678..0000000
--- a/samplecode/SampleDash.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkRandom.h"
-#include "SkDashPathEffect.h"
-#include "SkShader.h"
-
-static void setBitmapDash(SkPaint* paint, int width) {
-    SkColor c = paint->getColor();
-
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 1);
-    bm.allocPixels();
-    bm.lockPixels();
-    *bm.getAddr32(0, 0) = SkPreMultiplyARGB(0xFF, SkColorGetR(c),
-                                            SkColorGetG(c), SkColorGetB(c));
-    *bm.getAddr32(1, 0) = 0;
-    bm.unlockPixels();
-
-    SkMatrix matrix;
-    matrix.setScale(SkIntToScalar(width), SK_Scalar1);
-
-    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
-                                               SkShader::kClamp_TileMode);
-    s->setLocalMatrix(matrix);
-
-    paint->setShader(s)->unref();
-}
-
-class DashView : public SampleView {
-public:
-    DashView() {
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Dash");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        static const char* gStr[] = {
-            "11",
-            "44",
-            "112233",
-            "411327463524",
-        };
-
-        SkPaint paint;
-        paint.setStrokeWidth(SkIntToScalar(1));
-
-        SkScalar x0 = SkIntToScalar(10);
-        SkScalar y0 = SkIntToScalar(10);
-        SkScalar x1 = x0 + SkIntToScalar(1000);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gStr); i++) {
-            SkScalar interval[12];
-            size_t len = SkMin32(strlen(gStr[i]), SK_ARRAY_COUNT(interval));
-            for (size_t j = 0; j < len; j++) {
-                interval[j] = SkIntToScalar(gStr[i][j] - '0');
-            }
-            
-            SkDashPathEffect dash(interval, len, 0);
-            paint.setPathEffect(&dash);
-            canvas->drawLine(x0, y0, x1, y0, paint);
-            paint.setPathEffect(NULL);
-
-            y0 += paint.getStrokeWidth() * 3;
-        }
-
-        setBitmapDash(&paint, 3);
-        canvas->drawLine(x0, y0, x1, y0, paint);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DashView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDecode.cpp b/samplecode/SampleDecode.cpp
deleted file mode 100644
index 9188257..0000000
--- a/samplecode/SampleDecode.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkImageDecoder.h"
-#include "SkStream.h"
-
-static const struct {
-    SkBitmap::Config    fPrefConfig;
-    bool                fDither;
-} gRec[] = {
-    { SkBitmap::kIndex8_Config,     false },
-    { SkBitmap::kARGB_8888_Config,  false },
-    { SkBitmap::kARGB_4444_Config,  false },
-    { SkBitmap::kARGB_4444_Config,  true },
-    { SkBitmap::kRGB_565_Config,    false },
-    { SkBitmap::kRGB_565_Config,    true },
-};
-
-class DecodeView : public SkView {
-public:
-    SkBitmap fBitmap[SK_ARRAY_COUNT(gRec)];
-
-	DecodeView() {
-        SkFILEStream stream("/skimages/index.png");
-        SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
-        if (codec) {
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
-                stream.rewind();
-                codec->setDitherImage(gRec[i].fDither);
-                codec->decode(&stream, &fBitmap[i], gRec[i].fPrefConfig,
-                              SkImageDecoder::kDecodePixels_Mode);
-            }
-        }
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "ImageDecoder");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(0xFFDDDDDD);
-//        canvas->drawColor(SK_ColorWHITE);
-    }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-        
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
-        
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fBitmap); i++) {
-            canvas->drawBitmap(fBitmap[i], 0, 0);
-            canvas->translate(SkIntToScalar(fBitmap[i].width()), 0);
-        }
-    }
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DecodeView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDegenerateTwoPtRadials.cpp b/samplecode/SampleDegenerateTwoPtRadials.cpp
deleted file mode 100644
index 5b8f46d..0000000
--- a/samplecode/SampleDegenerateTwoPtRadials.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkGradientShader.h"
-
-static void draw_gradient2(SkCanvas* canvas, const SkRect& rect, SkScalar delta) {
-    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorMAGENTA };
-    SkScalar pos[] = { 0, SkFloatToScalar(0.25f), SkFloatToScalar(0.75f), SK_Scalar1 };
-
-    SkScalar l = rect.fLeft;
-    SkScalar t = rect.fTop;
-    SkScalar w = rect.width();
-    SkScalar h = rect.height();
-
-    SkASSERT(0 == SkScalarMod(w, SK_Scalar1 * 5));
-
-    SkPoint c0 = { l + 2 * w / 5 + delta, t + h / 2 };
-    SkPoint c1 = { l + 3 * w / 5, t + h / 2 };
-    SkScalar r0 = w / 5;
-    SkScalar r1 = 2 * w / 5;
-    SkShader* s = SkGradientShader::CreateTwoPointRadial(c0, r0, c1, r1, colors,
-                                                         pos, SK_ARRAY_COUNT(pos),
-                                                         SkShader::kClamp_TileMode);
-    SkPaint paint;
-    paint.setShader(s)->unref();
-
-    canvas->drawRect(rect, paint);
-}
-
-
-class DegenerateTwoPtRadialsView : public SampleView {
-
-public:
-    DegenerateTwoPtRadialsView() {
-        fTime = 0;
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "DegenerateTwoPtRadials");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        fTime += SampleCode::GetAnimSecondsDelta();
-        SkScalar delta = fTime / 15.f;
-        int intPart = SkScalarFloor(delta);
-        delta = delta - SK_Scalar1 * intPart;
-        if (intPart % 2) {
-            delta = SK_Scalar1 - delta;
-        }
-        delta -= SK_ScalarHalf;
-        static const int DELTA_SCALE = 500;
-        delta /= DELTA_SCALE;
-
-        SkRect rect;
-        SkScalar w = SK_Scalar1 * 500;
-        SkScalar h = SK_Scalar1 * 500;
-        SkScalar l = SK_Scalar1 * 100;
-        SkScalar t = SK_Scalar1 * 100;
-        draw_gradient2(canvas, SkRect::MakeXYWH(l, t, w, h), delta);
-        char txt[512];
-        sprintf(txt, "gap at \"tangent\" pt = %f", SkScalarToFloat(delta));
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(SK_ColorBLACK);
-        canvas->drawText(txt, strlen(txt), l + w/2 + w*DELTA_SCALE*delta, t + h + SK_Scalar1 * 10, paint);
-        this->inval(NULL);
-    }
-
-private:
-    SkScalar           fTime;
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DegenerateTwoPtRadialsView; }
-static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleDither.cpp b/samplecode/SampleDither.cpp
deleted file mode 100644
index 85ac2b1..0000000
--- a/samplecode/SampleDither.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "Sk1DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkDither.h"
-
-static void draw_sweep(SkCanvas* c, int width, int height, SkScalar angle) {
-    SkRect  r;
-    SkPaint p;
-    
-    p.setAntiAlias(true);
-//    p.setDither(true);
-    p.setStrokeWidth(SkIntToScalar(width/10));
-    p.setStyle(SkPaint::kStroke_Style);
-
-    r.set(0, 0, SkIntToScalar(width), SkIntToScalar(height));
-    
-    //    SkColor colors[] = { SK_ColorRED, SK_ColorBLUE, SK_ColorGREEN, SK_ColorCYAN };
-    SkColor colors[] = { 0x4c737373, 0x4c737373, 0xffffd300 };
-    SkShader* s = SkGradientShader::CreateSweep(r.centerX(), r.centerY(),
-                                                colors, NULL, SK_ARRAY_COUNT(colors));
-    p.setShader(s)->unref();
-    
-    SkAutoCanvasRestore acr(c, true);
-
-    c->translate(r.centerX(), r.centerY());
-    c->rotate(angle);
-    c->translate(-r.centerX(), -r.centerY());
-
-    SkRect bounds = r;
-    r.inset(p.getStrokeWidth(), p.getStrokeWidth());
-    SkRect innerBounds = r;
-
-    if (true) {
-        c->drawOval(r, p);
-    } else {
-        SkScalar x = r.centerX();
-        SkScalar y = r.centerY();
-        SkScalar radius = r.width() / 2;
-        SkScalar thickness = p.getStrokeWidth();
-        SkScalar sweep = SkFloatToScalar(360.0f);
-        SkPath path;
-        
-        path.moveTo(x + radius, y);
-        // outer top
-        path.lineTo(x + radius + thickness, y);
-        // outer arc
-        path.arcTo(bounds, 0, sweep, false);
-        // inner arc
-        path.arcTo(innerBounds, sweep, -sweep, false);
-        path.close();
-    }
-}
-
-static void make_bm(SkBitmap* bm) {
-    bm->setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
-    bm->allocPixels();
-#if 0
-    bm->eraseColor(SK_ColorBLUE);
-    return;
-#else
-    bm->eraseColor(0);
-#endif
-    
-    SkCanvas c(*bm);    
-    draw_sweep(&c, bm->width(), bm->height(), 0);
-}
-
-static void pre_dither(const SkBitmap& bm) {
-    SkAutoLockPixels alp(bm);
-    
-    for (int y = 0; y < bm.height(); y++) {
-        DITHER_4444_SCAN(y);
-        
-        SkPMColor* p = bm.getAddr32(0, y);
-        for (int x = 0; x < bm.width(); x++) {
-            SkPMColor c = *p;
-            
-            unsigned a = SkGetPackedA32(c);
-            unsigned r = SkGetPackedR32(c);
-            unsigned g = SkGetPackedG32(c);
-            unsigned b = SkGetPackedB32(c);
-            
-            unsigned d = DITHER_VALUE(x);
-
-            a = SkDITHER_A32To4444(a, d);
-            r = SkDITHER_R32To4444(r, d);
-            g = SkDITHER_G32To4444(g, d);
-            b = SkDITHER_B32To4444(b, d);
-            
-            a = SkA4444ToA32(a);
-            r = SkR4444ToR32(r);
-            g = SkG4444ToG32(g);
-            b = SkB4444ToB32(b);
-            
-            *p++ = SkPackARGB32(a, r, g, b);
-        }
-    }
-}
-
-class DitherView : public SampleView {
-public:
-    SkBitmap    fBM, fBMPreDither, fBM16;
-    SkScalar fAngle;
-
-	DitherView() {
-        make_bm(&fBM);
-        make_bm(&fBMPreDither);
-        pre_dither(fBMPreDither);
-        fBM.copyTo(&fBM16, SkBitmap::kARGB_4444_Config);
-        
-        fAngle = 0;
-        
-        this->setBGColor(0xFF181818);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Dither");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        SkScalar x = SkIntToScalar(10);
-        SkScalar y = SkIntToScalar(10);
-        const SkScalar DX = SkIntToScalar(fBM.width() + 10);
-        
-        paint.setAntiAlias(true);
-        
-        if (true) {
-            canvas->drawBitmap(fBM, x, y, &paint);
-            x += DX;
-            paint.setDither(true);
-            canvas->drawBitmap(fBM, x, y, &paint);
-            
-            x += DX;
-            paint.setDither(false);
-            canvas->drawBitmap(fBMPreDither, x, y, &paint);
-            
-            x += DX;
-            canvas->drawBitmap(fBM16, x, y, &paint);
-        }
-        
-        canvas->translate(DX, DX*2);
-        draw_sweep(canvas, fBM.width(), fBM.height(), fAngle);
-        canvas->translate(DX, 0);
-        draw_sweep(canvas, fBM.width()>>1, fBM.height()>>1, fAngle);
-        canvas->translate(DX, 0);
-        draw_sweep(canvas, fBM.width()>>2, fBM.height()>>2, fAngle);
-
-        fAngle += SK_Scalar1/2;
-        this->inval(NULL);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DitherView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDitherBitmap.cpp b/samplecode/SampleDitherBitmap.cpp
deleted file mode 100644
index c243782..0000000
--- a/samplecode/SampleDitherBitmap.cpp
+++ /dev/null
@@ -1,148 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkColorPriv.h"
-#include "SkGradientShader.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkUtils.h"
-
-static void draw_rect(SkCanvas* canvas, const SkRect& r, const SkPaint& p) {
-    canvas->drawRect(r, p);
-
-    SkPaint frame(p);
-    frame.setShader(NULL);
-    frame.setStyle(SkPaint::kStroke_Style);
-    canvas->drawRect(r, frame);
-}
-
-static void draw_gradient(SkCanvas* canvas) {
-    SkRect r = { 0, 0, SkIntToScalar(256), SkIntToScalar(32) };
-    SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fTop } };
-    SkColor colors[] = { 0xFF000000, 0xFFFF0000 };
-    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2,
-                                                 SkShader::kClamp_TileMode);
-
-    SkPaint p;
-    p.setShader(s)->unref();
-    draw_rect(canvas, r, p);
-
-    canvas->translate(0, SkIntToScalar(40));
-    p.setDither(true);
-    draw_rect(canvas, r, p);
-}
-
-static void test_pathregion() {
-    SkPath path;
-    SkRegion region;
-    path.moveTo(25071800.f, -141823808.f); 
-    path.lineTo(25075500.f, -141824000.f);
-    path.lineTo(25075400.f, -141827712.f);
-    path.lineTo(25071810.f, -141827600.f);
-    path.close();
-
-    SkIRect bounds;
-    path.getBounds().round(&bounds);
-    SkRegion clip(bounds);
-    bool result = region.setPath(path, clip); // <-- !! DOWN !!
-    SkDebugf("----- result %d\n", result);
-}
-
-static SkBitmap make_bitmap() {
-    SkBitmap bm;
-    SkColorTable* ctable = new SkColorTable(256);
-
-    SkPMColor* c = ctable->lockColors();
-    for (int i = 0; i < 256; i++) {
-        c[i] = SkPackARGB32(0xFF, i, 0, 0);
-    }
-    ctable->unlockColors(true);
-    bm.setConfig(SkBitmap::kIndex8_Config, 256, 32);
-    bm.allocPixels(ctable);
-    ctable->unref();
-
-    bm.lockPixels();
-    for (int y = 0; y < bm.height(); y++) {
-        uint8_t* p = bm.getAddr8(0, y);
-        for (int x = 0; x < 256; x++) {
-            p[x] = x;
-        }
-    }
-    bm.unlockPixels();
-    return bm;
-}
-
-class DitherBitmapView : public SampleView {
-    SkBitmap    fBM8;
-    SkBitmap    fBM32;
-public:
-	DitherBitmapView() {
-        test_pathregion();
-        fBM8 = make_bitmap();
-        fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
-        
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "DitherBitmap");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) {
-        SkAutoLockPixels alp(*bm);  // needed for ctable
-        bm->setIsOpaque(isOpaque);
-        SkColorTable* ctable = bm->getColorTable();
-        if (ctable) {
-            ctable->setIsOpaque(isOpaque);
-        }
-    }
-    
-    static void draw2(SkCanvas* canvas, const SkBitmap& bm) {
-        SkPaint paint;
-        SkBitmap bitmap(bm);
-
-        setBitmapOpaque(&bitmap, false);
-        paint.setDither(false);
-        canvas->drawBitmap(bitmap, 0, 0, &paint);
-        paint.setDither(true);
-        canvas->drawBitmap(bitmap, 0, SkIntToScalar(bm.height() + 10), &paint);
-
-        setBitmapOpaque(&bitmap, true);
-        SkScalar x = SkIntToScalar(bm.width() + 10);
-        paint.setDither(false);
-        canvas->drawBitmap(bitmap, x, 0, &paint);
-        paint.setDither(true);
-        canvas->drawBitmap(bitmap, x, SkIntToScalar(bm.height() + 10), &paint);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
-
-        draw2(canvas, fBM8);
-        canvas->translate(0, SkIntToScalar(fBM8.height() *3));
-        draw2(canvas, fBM32);
-
-        canvas->translate(0, SkIntToScalar(fBM8.height() *3));
-        draw_gradient(canvas);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DitherBitmapView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDraw.cpp b/samplecode/SampleDraw.cpp
deleted file mode 100644
index c418585..0000000
--- a/samplecode/SampleDraw.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkRandom.h"
-
-static void test_clearonlayers(SkCanvas* canvas) {
-    SkCanvas& c = *canvas;
-    
-    SkPaint paint;
-    paint.setColor(SK_ColorBLUE);
-    paint.setStyle(SkPaint::kStrokeAndFill_Style);
-    SkRect rect = SkRect::MakeXYWH(25, 25, 50, 50);
-    c.drawRect(rect, paint);
-    
-    c.clipRect(rect);
-    
-    c.saveLayer(NULL, NULL);
-    rect = SkRect::MakeXYWH(50, 10, 40, 80);
-    c.clipRect(rect, SkRegion::kUnion_Op);
-    
-    rect = SkRect::MakeXYWH(50, 0, 50, 100);
-    // You might draw something here, but it's not necessary.
-    // paint.setColor(SK_ColorRED);
-    // c.drawRect(rect, paint);
-    paint.setXfermodeMode(SkXfermode::kClear_Mode);
-    c.drawRect(rect, paint);
-    c.restore();
-}
-
-static void test_strokerect(SkCanvas* canvas, const SkRect& r) {
-    SkPaint p;
-    
-    p.setAntiAlias(true);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(4);
-    
-    canvas->drawRect(r, p);
-
-    SkPath path;
-    SkRect r2(r);
-    r2.offset(18, 0);
-    path.addRect(r2);
-
-    canvas->drawPath(path, p);
-}
-
-static void test_strokerect(SkCanvas* canvas) {
-    canvas->drawColor(SK_ColorWHITE);
-
-    SkRect r;
-
-    r.set(10, 10, 14, 14);
-    r.offset(0.25f, 0.3333f);
-    test_strokerect(canvas, r);
-    canvas->translate(0, 20);
-    
-    r.set(10, 10, 14.5f, 14.5f);
-    r.offset(0.25f, 0.3333f);
-    test_strokerect(canvas, r);
-    canvas->translate(0, 20);
-    
-    r.set(10, 10, 14.5f, 20);
-    r.offset(0.25f, 0.3333f);
-    test_strokerect(canvas, r);
-    canvas->translate(0, 20);
-    
-    r.set(10, 10, 20, 14.5f);
-    r.offset(0.25f, 0.3333f);
-    test_strokerect(canvas, r);
-    canvas->translate(0, 20);
-    
-    r.set(10, 10, 20, 20);
-    r.offset(0.25f, 0.3333f);
-    test_strokerect(canvas, r);
-    canvas->translate(0, 20);
-    
-}
-
-class Draw : public SkRefCnt {
-public:
-    Draw() : fFlags(0) {}
-
-    enum Flags {
-        kSelected_Flag  = 1 << 0
-    };
-    int getFlags() const { return fFlags; }
-    void setFlags(int flags);
-
-    bool isSelected() const { return SkToBool(fFlags & kSelected_Flag); }
-    void setSelected(bool pred) {
-        if (pred) {
-            fFlags |= kSelected_Flag;
-        } else {
-            fFlags &= ~kSelected_Flag;
-        }
-    }
-
-    void draw(SkCanvas* canvas) {
-        int sc = canvas->save();
-        this->onDraw(canvas);
-        canvas->restoreToCount(sc);
-
-        if (this->isSelected()) {
-            this->drawSelection(canvas);
-        }
-    }
-
-    void drawSelection(SkCanvas* canvas) {
-        int sc = canvas->save();
-        this->onDrawSelection(canvas);
-        canvas->restoreToCount(sc);
-    }
-
-    void getBounds(SkRect* bounds) {
-        this->onGetBounds(bounds);
-    }
-
-    bool hitTest(SkScalar x, SkScalar y) {
-        return this->onHitTest(x, y);
-    }
-
-    void offset(SkScalar dx, SkScalar dy) {
-        if (dx || dy) {
-            this->onOffset(dx, dy);
-        }
-    }
-
-protected:
-    virtual void onDraw(SkCanvas*) = 0;
-    virtual void onGetBounds(SkRect*) = 0;
-    virtual void onOffset(SkScalar dx, SkScalar dy) = 0;
-    virtual void onDrawSelection(SkCanvas* canvas) {
-        SkRect r;
-        this->getBounds(&r);
-        SkPaint paint;
-        SkPoint pts[4];
-        r.toQuad(pts);
-        paint.setStrokeWidth(SkIntToScalar(10));
-        paint.setColor(0x80FF8844);
-        paint.setStrokeCap(SkPaint::kRound_Cap);
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, 4, pts, paint);
-    }
-    virtual bool onHitTest(SkScalar x, SkScalar y) {
-        SkRect bounds;
-        this->getBounds(&bounds);
-        return bounds.contains(x, y);
-    }
-
-private:
-    int fFlags;
-};
-
-class RDraw : public Draw {
-public:
-    enum Style {
-        kRect_Style,
-        kOval_Style,
-        kRRect_Style,
-        kFrame_Style
-    };
-
-    RDraw(const SkRect& r, Style s) : fRect(r), fStyle(s) {}
-
-    void setRect(const SkRect& r) {
-        fRect = r;
-    }
-
-    void setPaint(const SkPaint& p) {
-        fPaint = p;
-    }
-
-protected:
-    virtual void onDraw(SkCanvas* canvas) {
-        switch (fStyle) {
-            case kRect_Style:
-                canvas->drawRect(fRect, fPaint);
-                break;
-            case kOval_Style:
-                canvas->drawOval(fRect, fPaint);
-                break;
-            case kRRect_Style: {
-                SkScalar rx = fRect.width() / 5;
-                SkScalar ry = fRect.height() / 5;
-                if (rx < ry) {
-                    ry = rx;
-                } else {
-                    rx = ry;
-                }
-                canvas->drawRoundRect(fRect, rx, ry, fPaint);
-                break;
-            }
-            case kFrame_Style: {
-                SkPath path;
-                path.addOval(fRect, SkPath::kCW_Direction);
-                SkRect r = fRect;
-                r.inset(fRect.width()/6, 0);
-                path.addOval(r, SkPath::kCCW_Direction);
-                canvas->drawPath(path, fPaint);
-                break;
-            }
-        }
-    }
-
-    virtual void onGetBounds(SkRect* bounds) {
-        *bounds = fRect;
-    }
-
-    virtual void onOffset(SkScalar dx, SkScalar dy) {
-        fRect.offset(dx, dy);
-    }
-
-private:
-    SkRect  fRect;
-    SkPaint fPaint;
-    Style   fStyle;
-};
-
-class DrawFactory {
-public:
-    DrawFactory() {
-        fPaint.setAntiAlias(true);
-    }
-
-    const SkPaint& getPaint() const { return fPaint; }
-
-    void setPaint(const SkPaint& p) {
-        fPaint = p;
-    }
-
-    virtual Draw* create(const SkPoint&, const SkPoint&) = 0;
-    
-private:
-    SkPaint fPaint;
-};
-
-class RectFactory : public DrawFactory {
-public:
-    virtual Draw* create(const SkPoint& p0, const SkPoint& p1) {
-        SkRect r;
-        r.set(p0.x(), p0.y(), p1.x(), p1.y());
-        r.sort();
-
-//        RDraw* d = new RDraw(r, RDraw::kRRect_Style);
-        RDraw* d = new RDraw(r, RDraw::kFrame_Style);
-        d->setPaint(this->getPaint());
-        return d;
-    }
-};
-
-class DrawView : public SkView {
-    Draw*           fDraw;
-    DrawFactory*    fFactory;
-    SkRandom        fRand;
-    SkTDArray<Draw*> fList;
-
-public:
-    DrawView() : fDraw(NULL) {
-        fFactory = new RectFactory;
-    }
-
-    virtual ~DrawView() {
-        fList.unrefAll();
-        SkSafeUnref(fDraw);
-        delete fFactory;
-    }
-
-    Draw* setDraw(Draw* d) {
-        SkRefCnt_SafeAssign(fDraw, d);
-        return d;
-    }
-
-    SkColor randColor() {
-        return (SkColor)fRand.nextU() | 0xFF000000;
-    }
-
-    Draw* hitTestList(SkScalar x, SkScalar y) const {
-        Draw** first = fList.begin();
-        for (Draw** iter = fList.end(); iter > first;) {
-            --iter;
-            if ((*iter)->hitTest(x, y)) {
-                return *iter;
-            }
-        }
-        return NULL;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Draw");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(0xFFDDDDDD);
-//        canvas->drawColor(SK_ColorWHITE);
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-        test_clearonlayers(canvas); return;
-     //   test_strokerect(canvas); return;
-
-        for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
-            (*iter)->draw(canvas);
-        }
-        if (fDraw) {
-            fDraw->draw(canvas);
-        }
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
-            (*iter)->setSelected(false);
-        }
-
-        Click* c = new Click(this);
-        Draw* d = this->hitTestList(x, y);
-        if (d) {
-            d->setSelected(true);
-            c->setType("dragger");
-        } else {
-            c->setType("maker");
-        }
-        return c;
-    }
-
-    virtual bool onClick(Click* click) {
-        if (Click::kUp_State == click->fState) {
-            if (click->isType("maker")) {
-                if (SkPoint::Distance(click->fOrig, click->fCurr) > SkIntToScalar(3)) {
-                    *fList.append() = fDraw;
-                } else {
-                    fDraw->unref();
-                }
-                fDraw = NULL;
-            }
-            return true;
-        }
-
-        if (Click::kDown_State == click->fState) {
-            SkPaint p = fFactory->getPaint();
-            p.setColor(this->randColor());
-            fFactory->setPaint(p);
-        }
-
-        if (click->isType("maker")) {
-            this->setDraw(fFactory->create(click->fOrig, click->fCurr))->unref();
-        } else if (click->isType("dragger")) {
-            for (Draw** iter = fList.begin(); iter < fList.end(); iter++) {
-                if ((*iter)->isSelected()) {
-                    (*iter)->offset(click->fCurr.x() - click->fPrev.x(),
-                                    click->fCurr.y() - click->fPrev.y());
-                }
-            }
-        }
-        this->inval(NULL);
-        return true;
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DrawView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleDrawBitmap.cpp b/samplecode/SampleDrawBitmap.cpp
deleted file mode 100644
index aaf1123..0000000
--- a/samplecode/SampleDrawBitmap.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkDevice.h"
-
-static void create_bitmap(SkBitmap* bitmap) {
-    const int W = 100;
-    const int H = 100;
-    bitmap->setConfig(SkBitmap::kARGB_8888_Config, W, H);
-    bitmap->allocPixels();
-
-    SkCanvas canvas(*bitmap);
-    canvas.drawColor(SK_ColorRED);
-    SkPaint paint;
-    paint.setColor(SK_ColorBLUE);
-    canvas.drawCircle(SkIntToScalar(W)/2, SkIntToScalar(H)/2, SkIntToScalar(W)/2, paint);
-}
-
-class DrawBitmapView : public SampleView {
-    SkPath fPath;
-public:
-	DrawBitmapView() {}
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "DrawBitmap");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkBitmap bitmap;
-        create_bitmap(&bitmap);
-        int x = bitmap.width() / 2;
-        int y = bitmap.height() / 2;
-        SkBitmap subset;
-        bitmap.extractSubset(&subset, SkIRect::MakeXYWH(x, y, x, y));
-
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
-
-        canvas->drawBitmap(bitmap, 0, 0);
-        canvas->drawBitmap(subset, 0, 0);
-
-        // Now do the same but with a device bitmap as source image
-        SkRefPtr<SkDevice> primaryDevice(canvas->getDevice());
-        SkRefPtr<SkDevice> secondDevice(canvas->createCompatibleDevice(
-            SkBitmap::kARGB_8888_Config, bitmap.width(), 
-            bitmap.height(), true));
-        secondDevice->unref();
-        SkCanvas secondCanvas(secondDevice.get());
-        secondCanvas.writePixels(bitmap, 0, 0);
-
-        SkBitmap deviceBitmap = secondDevice->accessBitmap(false);
-        SkBitmap deviceSubset;
-        deviceBitmap.extractSubset(&deviceSubset, 
-             SkIRect::MakeXYWH(x, y, x, y));
-
-        canvas->translate(SkIntToScalar(120), SkIntToScalar(0));
-
-        canvas->drawBitmap(deviceBitmap, 0, 0);
-        canvas->drawBitmap(deviceSubset, 0, 0);
-
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new DrawBitmapView; }
-static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleDrawLooper.cpp b/samplecode/SampleDrawLooper.cpp
deleted file mode 100644
index 7e317d7..0000000
--- a/samplecode/SampleDrawLooper.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkRandom.h"
-#include "SkLayerDrawLooper.h"
-#include "SkBlurMaskFilter.h"
-
-#define WIDTH   200
-#define HEIGHT  200
-
-class LooperView : public SampleView {
-public:
-
-    SkLayerDrawLooper*   fLooper;
-
-	LooperView() {
-        static const struct {
-            SkColor         fColor;
-            SkPaint::Style  fStyle;
-            SkScalar        fWidth;
-            SkScalar        fOffset;
-            int             fBlur;
-        } gParams[] = {
-            { SK_ColorWHITE, SkPaint::kStroke_Style, SkIntToScalar(1)*3/4, 0, 0 },
-            { SK_ColorRED, SkPaint::kStroke_Style, SkIntToScalar(4), 0, 0 },
-            { SK_ColorBLUE, SkPaint::kFill_Style, 0, 0, 0 },
-            { 0x88000000, SkPaint::kFill_Style, 0, SkIntToScalar(10), 3 }
-        };
-
-        fLooper = new SkLayerDrawLooper;
-
-        SkLayerDrawLooper::LayerInfo info;
-        info.fFlagsMask = SkPaint::kAntiAlias_Flag;
-        info.fPaintBits = SkLayerDrawLooper::kStyle_Bit | SkLayerDrawLooper::kMaskFilter_Bit;
-        info.fColorMode = SkXfermode::kSrc_Mode;
-        
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gParams); i++) {
-            info.fOffset.set(gParams[i].fOffset, gParams[i].fOffset);
-            SkPaint* paint = fLooper->addLayer(info);
-            paint->setAntiAlias(true);
-            paint->setColor(gParams[i].fColor);
-            paint->setStyle(gParams[i].fStyle);
-            paint->setStrokeWidth(gParams[i].fWidth);
-            if (gParams[i].fBlur > 0) {
-                SkMaskFilter* mf = SkBlurMaskFilter::Create(SkIntToScalar(gParams[i].fBlur),
-                                                            SkBlurMaskFilter::kNormal_BlurStyle);
-                paint->setMaskFilter(mf)->unref();
-            }
-        }
-        
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-    virtual ~LooperView() {
-        SkSafeUnref(fLooper);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "DrawLooper");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint  paint;
-        paint.setTextSize(SkIntToScalar(72));
-        paint.setLooper(fLooper);
-
-        canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
-                           SkIntToScalar(30), paint);
-
-        canvas->drawRectCoords(SkIntToScalar(150), SkIntToScalar(50),
-                               SkIntToScalar(200), SkIntToScalar(100), paint);
-
-        canvas->drawText("Looper", 6, SkIntToScalar(230), SkIntToScalar(100),
-                         paint);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new LooperView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleEffects.cpp b/samplecode/SampleEffects.cpp
deleted file mode 100644
index bf83bae..0000000
--- a/samplecode/SampleEffects.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkView.h"
-
-#include "SkBlurMaskFilter.h"
-#include "SkColorMatrixFilter.h"
-#include "SkDiscretePathEffect.h"
-#include "SkGradientShader.h"
-
-#include "SkEdgeClipper.h"
-
-static void test_edgeclipper() {
-    SkPoint pts[] = {
-        { -8.38822452e+21f, -7.69721471e+19f },
-        { 1.57645875e+23f, 1.44634003e+21f },
-        { 1.61519691e+23f, 1.48208059e+21f },
-        { 3.13963584e+23f, 2.88057438e+21f }
-    };
-    SkRect clip = { 0, 0, 300, 200 };
-    
-    SkEdgeClipper clipper;
-    clipper.clipCubic(pts, clip);
-}
-
-///////////
-
-//#define COLOR 0xFFFF8844
-#define COLOR 0xFF888888
-
-static void paint_proc0(SkPaint* paint) {
-}
-
-static void paint_proc1(SkPaint* paint) {
-    paint->setMaskFilter(SkBlurMaskFilter::Create(2,
-                                SkBlurMaskFilter::kNormal_BlurStyle))->unref();
-}
-
-static void paint_proc2(SkPaint* paint) {
-    SkScalar dir[3] = { 1, 1, 1};
-    paint->setMaskFilter(
-                     SkBlurMaskFilter::CreateEmboss(dir, 0.1f, 0.05f, 1))->unref();
-}
-
-static void paint_proc3(SkPaint* paint) {
-    SkColor colors[] = { SK_ColorRED, COLOR, SK_ColorBLUE };
-    SkPoint pts[] = { { 3, 0 }, { 7, 5 } };
-    paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, SK_ARRAY_COUNT(colors),
-                                        SkShader::kMirror_TileMode))->unref();
-}
-
-static void paint_proc5(SkPaint* paint) {
-    paint_proc3(paint);
-    paint_proc2(paint);
-}
-
-typedef void (*PaintProc)(SkPaint*);
-const PaintProc gPaintProcs[] = {
-    paint_proc0,
-    paint_proc1,
-    paint_proc2,
-    paint_proc3,
-    paint_proc5,
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class EffectsView : public SampleView {
-public:
-    SkPath fPath;
-    SkPaint fPaint[SK_ARRAY_COUNT(gPaintProcs)];
-
-	EffectsView() {
-        size_t i;
-        const float pts[] = {
-            0, 0,
-            10, 0,
-            10, 5,
-            20, -5,
-            10, -15,
-            10, -10,
-            0, -10
-        };
-        fPath.moveTo(pts[0], pts[1]);
-        for (i = 2; i < SK_ARRAY_COUNT(pts); i += 2) {
-            fPath.lineTo(pts[i], pts[i+1]);
-        }
-        
-        for (i = 0; i < SK_ARRAY_COUNT(gPaintProcs); i++) {
-            fPaint[i].setAntiAlias(true);
-            fPaint[i].setColor(COLOR);
-            gPaintProcs[i](&fPaint[i]);
-        }
-
-        test_edgeclipper();
-        SkColorMatrix cm;
-        cm.setRotate(SkColorMatrix::kG_Axis, 180);
-        cm.setIdentity();
-        
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Effects");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->scale(3, 3);
-        canvas->translate(10, 30);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fPaint); i++) {
-            canvas->drawPath(fPath, fPaint[i]);
-            canvas->translate(32, 0);
-        }
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new EffectsView; }
-static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleEmboss.cpp b/samplecode/SampleEmboss.cpp
deleted file mode 100644
index bf12636..0000000
--- a/samplecode/SampleEmboss.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkColorShader.h"
-#include "SkEmbossMaskFilter.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-class EmbossView : public SampleView {
-    SkEmbossMaskFilter::Light   fLight;
-public:
-	EmbossView() {
-        fLight.fDirection[0] = SK_Scalar1;
-        fLight.fDirection[1] = SK_Scalar1;
-        fLight.fDirection[2] = SK_Scalar1;
-        fLight.fAmbient = 128;
-        fLight.fSpecular = 16*2;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Emboss");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkIntToScalar(10));
-        paint.setMaskFilter(new SkEmbossMaskFilter(fLight, SkIntToScalar(4)))->unref();
-        paint.setShader(new SkColorShader(SK_ColorBLUE))->unref();
-        paint.setDither(true);
-        
-        canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
-                           SkIntToScalar(30), paint);
-    }
-    
-private:
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new EmbossView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleEmptyPath.cpp b/samplecode/SampleEmptyPath.cpp
deleted file mode 100644
index cffe6cf..0000000
--- a/samplecode/SampleEmptyPath.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkRandom.h"
-
-class EmptyPathView : public SampleView {
-public:
-    EmptyPathView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "EmptyPath");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawEmpty(SkCanvas* canvas,
-                   SkColor color,
-                   const SkRect& clip,
-                   SkPaint::Style style,
-                   SkPath::FillType fill) {
-        SkPath path;
-        path.setFillType(fill);
-        SkPaint paint;
-        paint.setColor(color);
-        paint.setStyle(style);
-        canvas->save();
-        canvas->clipRect(clip);
-        canvas->drawPath(path, paint);
-        canvas->restore();
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        struct FillAndName {
-            SkPath::FillType fFill;
-            const char*      fName;
-        };
-        static const FillAndName gFills[] = {
-            {SkPath::kWinding_FillType, "Winding"},
-            {SkPath::kEvenOdd_FillType, "Even / Odd"},
-            {SkPath::kInverseWinding_FillType, "Inverse Winding"},
-            {SkPath::kInverseEvenOdd_FillType, "Inverse Even / Odd"},
-        };
-        struct StyleAndName {
-            SkPaint::Style fStyle;
-            const char*    fName;
-        };
-        static const StyleAndName gStyles[] = {
-            {SkPaint::kFill_Style, "Fill"},
-            {SkPaint::kStroke_Style, "Stroke"},
-            {SkPaint::kStrokeAndFill_Style, "Stroke And Fill"},
-        };
-
-        SkPaint titlePaint;
-        titlePaint.setColor(SK_ColorBLACK);
-        titlePaint.setAntiAlias(true);
-        titlePaint.setLCDRenderText(true);
-        titlePaint.setTextSize(24 * SK_Scalar1);
-        const char title[] = "Empty Paths Drawn Into Rectangle Clips With Indicated Style and Fill";
-        canvas->drawText(title, strlen(title),
-                         40 * SK_Scalar1,
-                         100*SK_Scalar1,
-                         titlePaint);
-
-        SkRandom rand;
-        SkRect rect = SkRect::MakeWH(125*SK_Scalar1, 100*SK_Scalar1);
-        int i = 0;
-        canvas->save();
-        canvas->translate(80 * SK_Scalar1, 0);
-        canvas->save();
-        for (size_t style = 0; style < SK_ARRAY_COUNT(gStyles); ++style) {
-            for (size_t fill = 0; fill < SK_ARRAY_COUNT(gFills); ++fill) {
-                if (0 == i % 4) {
-                    canvas->restore();
-                    canvas->translate(0, rect.height() + 50 * SK_Scalar1);
-                    canvas->save();
-                } else {
-                    canvas->translate(rect.width() + 100 * SK_Scalar1, 0);
-                }
-                ++i;
-
-
-                SkColor color = rand.nextU();
-                color = 0xff000000| color; // force solid
-                this->drawEmpty(canvas, color, rect,
-                                gStyles[style].fStyle, gFills[fill].fFill);
-
-                SkPaint rectPaint;
-                rectPaint.setColor(SK_ColorBLACK);
-                rectPaint.setStyle(SkPaint::kStroke_Style);
-                rectPaint.setStrokeWidth(-1);
-                rectPaint.setAntiAlias(true);
-                canvas->drawRect(rect, rectPaint);
-
-                char label[1024];
-                sprintf(label, "%s, %s", gStyles[style].fName,
-                                         gFills[fill].fName);
-                SkPaint labelPaint;
-                labelPaint.setColor(color);
-                labelPaint.setAntiAlias(true);
-                labelPaint.setLCDRenderText(true);
-                canvas->drawText(label, strlen(label), 
-                                 0, rect.height() + 15 * SK_Scalar1,
-                                 labelPaint);
-            }
-        }
-        canvas->restore();
-        canvas->restore();
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new EmptyPathView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleEncode.cpp b/samplecode/SampleEncode.cpp
deleted file mode 100644
index 6999a06..0000000
--- a/samplecode/SampleEncode.cpp
+++ /dev/null
@@ -1,231 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkImageEncoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkStream.h"
-
-static void make_image(SkBitmap* bm, SkBitmap::Config config, int configIndex) {
-    const int   width = 98;
-    const int   height = 100;
-    SkBitmap    device;
-    
-    device.setConfig(SkBitmap::kARGB_8888_Config, width, height);
-    device.allocPixels();
-
-    SkCanvas    canvas(device);
-    SkPaint     paint;
-    
-    paint.setAntiAlias(true);
-    canvas.drawColor(SK_ColorRED);
-    paint.setColor(SK_ColorBLUE);
-    canvas.drawCircle(SkIntToScalar(width)/2, SkIntToScalar(height)/2,
-                      SkIntToScalar(width)/2, paint);
-
-    bm->setConfig(config, width, height);
-    switch (config) {
-        case SkBitmap::kARGB_8888_Config:
-            bm->swap(device);
-            break;
-        case SkBitmap::kRGB_565_Config: {
-            bm->allocPixels();
-            for (int y = 0; y < height; y++) {
-                for (int x = 0; x < width; x++) {
-                    *bm->getAddr16(x, y) = SkPixel32ToPixel16(*device.getAddr32(x, y));
-                }
-            }
-            break;
-        }
-        case SkBitmap::kIndex8_Config: {
-            SkPMColor colors[256];
-            for (int i = 0; i < 256; i++) {
-                if (configIndex & 1) {
-                    colors[i] = SkPackARGB32(255-i, 0, 0, 255-i);
-                } else {
-                    colors[i] = SkPackARGB32(0xFF, i, 0, 255-i);
-                }
-            }
-            SkColorTable* ctable = new SkColorTable(colors, 256);
-            bm->allocPixels(ctable);
-            ctable->unref();
-            
-            for (int y = 0; y < height; y++) {
-                for (int x = 0; x < width; x++) {
-                    *bm->getAddr8(x, y) = SkGetPackedR32(*device.getAddr32(x, y));
-                }
-            }
-            break;
-        }
-        default:
-            break;
-    }
-}
-
-// configs to build the original bitmap in. Can be at most these 3
-static const SkBitmap::Config gConfigs[] = {
-    SkBitmap::kARGB_8888_Config,
-    SkBitmap::kRGB_565_Config,
-    SkBitmap::kIndex8_Config,   // opaque
-    SkBitmap::kIndex8_Config    // alpha
-};
-
-static const char* const gConfigLabels[] = {
-    "8888", "565", "Index8",  "Index8 alpha"
-};
-
-// types to encode into. Can be at most these 3. Must match up with gExt[]
-static const SkImageEncoder::Type gTypes[] = {
-    SkImageEncoder::kJPEG_Type,
-    SkImageEncoder::kPNG_Type
-};
-
-// must match up with gTypes[]
-static const char* const gExt[] = {
-    ".jpg", ".png"
-};
-
-static const char* gPath = "/encoded/";
-
-static void make_name(SkString* name, int config, int ext) {
-    name->set(gPath);
-    name->append(gConfigLabels[config]);
-    name->append(gExt[ext]);
-}
-
-#include <sys/stat.h>
-
-class EncodeView : public SampleView {
-public:
-    SkBitmap*   fBitmaps;
-    size_t      fBitmapCount;
-
-	EncodeView() {
-    #if 1
-        (void)mkdir(gPath, S_IRWXU | S_IRWXG | S_IRWXO);
-        
-        fBitmapCount = SK_ARRAY_COUNT(gConfigs);
-        fBitmaps = new SkBitmap[fBitmapCount];
-        for (size_t i = 0; i < fBitmapCount; i++) {
-            make_image(&fBitmaps[i], gConfigs[i], i);
-            
-            for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) {
-                SkString path;
-                make_name(&path, i, j);
-                
-                // remove any previous run of this file
-                remove(path.c_str());
-                
-                SkImageEncoder* codec = SkImageEncoder::Create(gTypes[j]);
-                if (NULL == codec ||
-                        !codec->encodeFile(path.c_str(), fBitmaps[i], 100)) {
-                    SkDebugf("------ failed to encode %s\n", path.c_str());
-                    remove(path.c_str());   // remove any partial file
-                }
-                delete codec;
-            }
-        }
-    #else
-        fBitmaps = NULL;
-        fBitmapCount = 0;
-    #endif
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-    virtual ~EncodeView() {
-        delete[] fBitmaps;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "ImageEncoder");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        if (fBitmapCount == 0) {
-            return;
-        }
-        
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setTextAlign(SkPaint::kCenter_Align);
-        
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
-        
-        SkScalar x = 0, y = 0, maxX = 0;
-        const int SPACER = 10;
-        
-        for (size_t i = 0; i < fBitmapCount; i++) {
-            canvas->drawText(gConfigLabels[i], strlen(gConfigLabels[i]),
-                             x + SkIntToScalar(fBitmaps[i].width()) / 2, 0,
-                             paint);
-            y = paint.getTextSize();
-
-            canvas->drawBitmap(fBitmaps[i], x, y);
-            
-            SkScalar yy = y;
-            for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) {
-                yy += SkIntToScalar(fBitmaps[i].height() + 10);
-
-                SkBitmap bm;
-                SkString name;
-                
-                make_name(&name, i, j);
-                
-                SkImageDecoder::DecodeFile(name.c_str(), &bm);
-                canvas->drawBitmap(bm, x, yy);
-            }
-            
-            x += SkIntToScalar(fBitmaps[i].width() + SPACER);
-            if (x > maxX) {
-                maxX = x;
-            }
-        }
-
-        y = (paint.getTextSize() + SkIntToScalar(fBitmaps[0].height())) * 3 / 2;
-        x = maxX + SkIntToScalar(10);
-        paint.setTextAlign(SkPaint::kLeft_Align);
-
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gExt); j++) {
-            canvas->drawText(gExt[j], strlen(gExt[j]), x, y, paint);
-            y += SkIntToScalar(fBitmaps[0].height() + SPACER);
-        }
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new EncodeView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFillType.cpp b/samplecode/SampleFillType.cpp
deleted file mode 100644
index ae97720..0000000
--- a/samplecode/SampleFillType.cpp
+++ /dev/null
@@ -1,97 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkCornerPathEffect.h"
-#include "SkCullPoints.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-
-class FillTypeView : public SampleView {
-    SkPath fPath;
-public:
-	FillTypeView() {
-        const SkScalar radius = SkIntToScalar(45);
-        fPath.addCircle(SkIntToScalar(50), SkIntToScalar(50), radius);
-        fPath.addCircle(SkIntToScalar(100), SkIntToScalar(100), radius);
-
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "FillType");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void showPath(SkCanvas* canvas, int x, int y, SkPath::FillType ft,
-                  SkScalar scale, const SkPaint& paint) {
-
-        const SkRect r = { 0, 0, SkIntToScalar(150), SkIntToScalar(150) };
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(x), SkIntToScalar(y));
-        canvas->clipRect(r);
-        canvas->drawColor(SK_ColorWHITE);
-        fPath.setFillType(ft);
-        canvas->translate(r.centerX(), r.centerY());
-        canvas->scale(scale, scale);
-        canvas->translate(-r.centerX(), -r.centerY());
-        canvas->drawPath(fPath, paint);
-        canvas->restore();
-    }
-    
-    void showFour(SkCanvas* canvas, SkScalar scale, const SkPaint& paint) {
-        showPath(canvas,   0,   0, SkPath::kWinding_FillType,
-                 scale, paint);
-        showPath(canvas, 200,   0, SkPath::kEvenOdd_FillType,
-                 scale, paint);
-        showPath(canvas,  00, 200, SkPath::kInverseWinding_FillType,
-                 scale, paint);
-        showPath(canvas, 200, 200, SkPath::kInverseEvenOdd_FillType,
-                 scale, paint);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
-        
-        SkPaint paint;
-        const SkScalar scale = SkIntToScalar(5)/4;
-
-        paint.setAntiAlias(false);
-        paint.setColor(0x8000FF00);
-
-        showFour(canvas, SK_Scalar1, paint);
-        canvas->translate(SkIntToScalar(450), 0);
-        showFour(canvas, scale, paint);
-
-        paint.setAntiAlias(true);
-
-        canvas->translate(SkIntToScalar(-450), SkIntToScalar(450));
-        showFour(canvas, SK_Scalar1, paint);
-        canvas->translate(SkIntToScalar(450), 0);
-        showFour(canvas, scale, paint);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new FillTypeView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFilter.cpp b/samplecode/SampleFilter.cpp
deleted file mode 100644
index 350523d..0000000
--- a/samplecode/SampleFilter.cpp
+++ /dev/null
@@ -1,145 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "Sk1DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkDither.h"
-
-static void make_bm(SkBitmap* bm) {
-    const SkColor colors[] = {
-        SK_ColorRED, SK_ColorGREEN,
-        SK_ColorBLUE, SK_ColorWHITE
-    };
-    SkColorTable* ctable = new SkColorTable(colors, 4);
-    bm->setConfig(SkBitmap::kIndex8_Config, 2, 2);
-    bm->allocPixels(ctable);
-    ctable->unref();
-    
-    *bm->getAddr8(0, 0) = 0;
-    *bm->getAddr8(1, 0) = 1;
-    *bm->getAddr8(0, 1) = 2;
-    *bm->getAddr8(1, 1) = 3;
-}
-
-static SkScalar draw_bm(SkCanvas* canvas, const SkBitmap& bm,
-                        SkScalar x, SkScalar y, SkPaint* paint) {
-#if 1
-    canvas->drawBitmap(bm, x, y, paint);
-    return SkIntToScalar(bm.width()) * 5/4;
-#else
-    SkAutoCanvasRestore acr(canvas, true);
-    canvas->translate(x, y);
-
-    SkScalar w = SkIntToScalar(bm.width());
-    SkScalar h = SkIntToScalar(bm.height());
-    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode);
-    paint->setShader(s)->unref();
-    canvas->drawRect(SkRect::MakeWH(w, h), *paint);
-    paint->setShader(NULL);
-    return w * 5/4;
-#endif
-}
-
-static SkScalar draw_set(SkCanvas* c, const SkBitmap& bm, SkScalar x, SkPaint* p) {
-    x += draw_bm(c, bm, x, 0, p);
-    p->setFilterBitmap(true);
-    x += draw_bm(c, bm, x, 0, p);
-    p->setDither(true);
-    return x + draw_bm(c, bm, x, 0, p);
-}
-
-static const char* gConfigNames[] = {
-    "unknown config",
-    "A1",
-    "A8",
-    "Index8",
-    "565",
-    "4444",
-    "8888"
-};
-
-static SkScalar draw_row(SkCanvas* canvas, const SkBitmap& bm) {
-    SkAutoCanvasRestore acr(canvas, true);
-
-    SkPaint paint;
-    SkScalar x = 0;
-    const int scale = 32;
-
-    paint.setAntiAlias(true);
-    const char* name = gConfigNames[bm.config()];
-    canvas->drawText(name, strlen(name), x, SkIntToScalar(bm.height())*scale*5/8,
-                     paint);
-    canvas->translate(SkIntToScalar(48), 0);
-
-    canvas->scale(SkIntToScalar(scale), SkIntToScalar(scale));
-    
-    x += draw_set(canvas, bm, 0, &paint);
-    paint.reset();
-    paint.setAlpha(0x80);
-    draw_set(canvas, bm, x, &paint);
-    return x * scale / 3;
-}
-
-class FilterView : public SampleView {
-public:
-    SkBitmap    fBM8, fBM4444, fBM16, fBM32;
-
-	FilterView() {
-        make_bm(&fBM8);
-        fBM8.copyTo(&fBM4444, SkBitmap::kARGB_4444_Config);
-        fBM8.copyTo(&fBM16, SkBitmap::kRGB_565_Config);
-        fBM8.copyTo(&fBM32, SkBitmap::kARGB_8888_Config);
-        
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Filter");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkScalar x = SkIntToScalar(10);
-        SkScalar y = SkIntToScalar(10);
-        
-        canvas->translate(x, y);
-        y = draw_row(canvas, fBM8);
-        canvas->translate(0, y);
-        y = draw_row(canvas, fBM4444);
-        canvas->translate(0, y);
-        y = draw_row(canvas, fBM16);
-        canvas->translate(0, y);
-        draw_row(canvas, fBM32);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new FilterView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFilter2.cpp b/samplecode/SampleFilter2.cpp
deleted file mode 100644
index da4147e..0000000
--- a/samplecode/SampleFilter2.cpp
+++ /dev/null
@@ -1,123 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-
-static const char* gNames[] = {
-    "/skimages/background_01.png"
-};
-
-class Filter2View : public SampleView {
-public:
-    SkBitmap*   fBitmaps;
-    int         fBitmapCount;
-    int         fCurrIndex;
-
-	Filter2View() {
-        fBitmapCount = SK_ARRAY_COUNT(gNames)*2;
-        fBitmaps = new SkBitmap[fBitmapCount];
-        
-        for (int i = 0; i < fBitmapCount/2; i++) {
-            SkImageDecoder::DecodeFile(gNames[i], &fBitmaps[i],
-                                       SkBitmap::kARGB_8888_Config,
-                                   SkImageDecoder::kDecodePixels_Mode, NULL);
-        }
-        for (int i = fBitmapCount/2; i < fBitmapCount; i++) {
-            SkImageDecoder::DecodeFile(gNames[i-fBitmapCount/2], &fBitmaps[i],
-                                       SkBitmap::kRGB_565_Config,
-                                   SkImageDecoder::kDecodePixels_Mode, NULL);
-        }
-        fCurrIndex = 0;
-        
-        this->setBGColor(SK_ColorGRAY);
-    }
-    
-    virtual ~Filter2View() {
-        delete[] fBitmaps;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString str("Filter/Dither ");
-            str.append(gNames[fCurrIndex]);
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(50));
-        
-        const SkScalar W = SkIntToScalar(fBitmaps[0].width() + 1);
-        const SkScalar H = SkIntToScalar(fBitmaps[0].height() + 1);
-        SkPaint paint;
-        
-        const SkScalar scale = SkFloatToScalar(0.897917f);
-        canvas->scale(SK_Scalar1, scale);
-
-        for (int k = 0; k < 2; k++) {
-            paint.setFilterBitmap(k == 1);
-            for (int j = 0; j < 2; j++) {
-                paint.setDither(j == 1);
-                for (int i = 0; i < fBitmapCount; i++) {
-                    SkScalar x = (k * fBitmapCount + j) * W;
-                    SkScalar y = i * H;
-                    x = SkIntToScalar(SkScalarRound(x));
-                    y = SkIntToScalar(SkScalarRound(y));
-                    canvas->drawBitmap(fBitmaps[i], x, y, &paint);
-                    if (i == 0) {
-                        SkPaint p;
-                        p.setAntiAlias(true);
-                        p.setTextAlign(SkPaint::kCenter_Align);
-                        p.setTextSize(SkIntToScalar(18));
-                        SkString s("dither=");
-                        s.appendS32(paint.isDither());
-                        s.append(" filter=");
-                        s.appendS32(paint.isFilterBitmap());
-                        canvas->drawText(s.c_str(), s.size(), x + W/2,
-                                         y - p.getTextSize(), p);
-                    }
-                    if (k+j == 2) {
-                        SkPaint p;
-                        p.setAntiAlias(true);
-                        p.setTextSize(SkIntToScalar(18));
-                        SkString s;
-                        s.append(" depth=");
-                        s.appendS32(fBitmaps[i].config() == SkBitmap::kRGB_565_Config ? 16 : 32);
-                        canvas->drawText(s.c_str(), s.size(), x + W + SkIntToScalar(4),
-                                         y + H/2, p);
-                    }
-                }
-            }
-        }
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new Filter2View; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFontCache.cpp b/samplecode/SampleFontCache.cpp
deleted file mode 100644
index 2546890..0000000
--- a/samplecode/SampleFontCache.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkRandom.h"
-
-#include <pthread.h>
-
-static void call_measure() {
-    SkPaint paint;
-    uint16_t text[32];
-    SkRandom rand;
-    
-    paint.setAntiAlias(true);
-    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-    for (int j = 0; j < SK_ARRAY_COUNT(text); j++)
-        text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32);
-    
-    for (int i = 9; i < 36; i++) {
-        SkPaint::FontMetrics m;
-        
-        paint.setTextSize(SkIntToScalar(i));
-        paint.getFontMetrics(&m);
-        paint.measureText(text, sizeof(text));
-    }
-}
-
-static void call_draw(SkCanvas* canvas) {
-    SkPaint paint;
-    uint16_t text[32];
-    SkRandom rand;
-    
-    paint.setAntiAlias(true);
-    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
-    for (int j = 0; j < SK_ARRAY_COUNT(text); j++)
-        text[j] = (uint16_t)((rand.nextU() & 0xFF) + 32);
-    
-    SkScalar x = SkIntToScalar(10);
-    SkScalar y = SkIntToScalar(20);
-    
-    canvas->drawColor(SK_ColorWHITE);
-    for (int i = 9; i < 36; i++)
-    {
-        SkPaint::FontMetrics m;
-        
-        paint.setTextSize(SkIntToScalar(i));
-        paint.getFontMetrics(&m);
-        canvas->drawText(text, sizeof(text), x, y, paint);
-        y += m.fDescent - m.fAscent;
-    }
-}
-
-static bool gDone;
-
-static void* measure_proc(void* context) {
-    while (!gDone) {
-        call_measure();
-    }
-    return NULL;
-}
-
-static void* draw_proc(void* context) {
-    SkBitmap* bm = (SkBitmap*)context;
-    SkCanvas    canvas(*bm);
-
-    while (!gDone) {
-        call_draw(&canvas);
-    }
-    return NULL;
-}
-
-class FontCacheView : public SampleView {
-public:
-    enum { N = 4 };
-    
-    pthread_t   fMThreads[N];
-    pthread_t   fDThreads[N];
-    SkBitmap    fBitmaps[N];
-
-	FontCacheView() {
-        gDone = false;
-        for (int i = 0; i < N; i++) {
-            int status;
-            
-            status = pthread_create(&fMThreads[i], NULL,  measure_proc, NULL);
-            SkASSERT(0 == status);
-
-            fBitmaps[i].setConfig(SkBitmap::kRGB_565_Config, 320, 240);
-            fBitmaps[i].allocPixels();
-            status = pthread_create(&fDThreads[i], NULL,  draw_proc, &fBitmaps[i]);
-            SkASSERT(0 == status);
-        }
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-    virtual ~FontCacheView() {
-        gDone = true;
-        for (int i = 0; i < N; i++) {
-            void* ret;
-            int status = pthread_join(fMThreads[i], &ret);
-            SkASSERT(0 == status);
-            status = pthread_join(fDThreads[i], &ret);
-            SkASSERT(0 == status);
-        }
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "FontCache");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkScalar x = 0;
-        SkScalar y = 0;
-        for (int i = 0; i < N; i++) {
-            canvas->drawBitmap(fBitmaps[i], x, y);
-            x += SkIntToScalar(fBitmaps[i].width());
-        }
-        this->inval(NULL);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new FontCacheView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFontScalerTest.cpp b/samplecode/SampleFontScalerTest.cpp
deleted file mode 100644
index 062eca3..0000000
--- a/samplecode/SampleFontScalerTest.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkTypeface.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "Sk1DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkDither.h"
-
-static const struct {
-    const char* fName;
-    SkTypeface::Style   fStyle;
-} gFaces[] = {
-    { NULL, SkTypeface::kNormal },
-    { NULL, SkTypeface::kBold },
-    { "serif", SkTypeface::kNormal },
-    { "serif", SkTypeface::kBold },
-    { "serif", SkTypeface::kItalic },
-    { "serif", SkTypeface::kBoldItalic },
-    { "monospace", SkTypeface::kNormal }
-};
-
-static const int gFaceCount = SK_ARRAY_COUNT(gFaces);
-
-class FontScalerTestView : public SampleView {
-    SkTypeface* fFaces[gFaceCount];
-
-public:
-	FontScalerTestView() {
-        for (int i = 0; i < gFaceCount; i++) {
-            fFaces[i] = SkTypeface::CreateFromName(gFaces[i].fName,
-                                                   gFaces[i].fStyle);
-        }
-//        this->setBGColor(0xFFDDDDDD);
-    }
-
-    virtual ~FontScalerTestView() {
-        for (int i = 0; i < gFaceCount; i++) {
-            SkSafeUnref(fFaces[i]);
-        }
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "FontScaler Test");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    static void rotate_about(SkCanvas* canvas, SkScalar degrees, SkScalar px, SkScalar py) {
-        canvas->translate(px, py);
-        canvas->rotate(degrees);
-        canvas->translate(-px, -py);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-
-        // test handling of obscene cubic values (currently broken)
-        if (false) {
-            SkPoint pts[4];
-            pts[0].set(1.61061274e+09f, 6291456);
-            pts[1].set(-7.18397061e+15f, -1.53091184e+13f);
-            pts[2].set(-1.30077315e+16f, -2.77196141e+13f);
-            pts[3].set(-1.30077315e+16f, -2.77196162e+13f);
-
-            SkPath path;
-            path.moveTo(pts[0]);
-            path.cubicTo(pts[1], pts[2], pts[3]);
-            canvas->drawPath(path, paint);
-        }
-
-//        paint.setSubpixelText(true);
-        paint.setAntiAlias(true);
-        paint.setLCDRenderText(true);
-        SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromName("Times Roman", SkTypeface::kNormal)));
-
-//        const char* text = "abcdefghijklmnopqrstuvwxyz";
-        const char* text = "Hamburgefons ooo mmm";
-        const size_t textLen = strlen(text);
-
-        for (int j = 0; j < 2; ++j) {
-            for (int i = 0; i < 6; ++i) {
-                SkScalar x = SkIntToScalar(10);
-                SkScalar y = SkIntToScalar(20);
-
-                SkAutoCanvasRestore acr(canvas, true);
-                canvas->translate(SkIntToScalar(50 + i * 230),
-                                  SkIntToScalar(20));
-                rotate_about(canvas, i * 5, x, y * 10);
-
-                {
-                    SkPaint p;
-                    p.setAntiAlias(true);
-                    SkRect r;
-                    r.set(x-3, 15, x-1, 280);
-                    canvas->drawRect(r, p);
-                }
-
-                int index = 0;
-                for (int ps = 6; ps <= 22; ps++) {
-                    paint.setTextSize(SkIntToScalar(ps));
-                    canvas->drawText(text, textLen, x, y, paint);
-                    y += paint.getFontMetrics(NULL);
-                    index += 1;
-                }
-            }
-            canvas->translate(0, 400);
-            paint.setSubpixelText(true);
-        }
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new FontScalerTestView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleFuzz.cpp b/samplecode/SampleFuzz.cpp
deleted file mode 100644
index 36149c3..0000000
--- a/samplecode/SampleFuzz.cpp
+++ /dev/null
@@ -1,371 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkBlurMaskFilter.h"
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkXfermode.h"
-#include "SkMatrix.h"
-#include "SkColor.h"
-#include "SkRandom.h"
-
-static void set2x3(SkMatrix* m, float a, float b, float c, float d, float e, float f) {
-    m->reset();
-    m->set(0, a);
-    m->set(1, b);
-    m->set(2, c);
-    m->set(3, d);
-    m->set(4, e);
-    m->set(5, f);
-}
-
-static SkRandom gRand;
-static bool return_large;
-static bool return_undef;
-static bool quick;
-static bool scale_large;
-static int scval = 1;
-static float transval = 0;
-
-static int R(float x) {
-  return (int)floor(SkScalarToFloat(gRand.nextUScalar1()) * x);
-}
-
-static float huge() {
-    double d = 1e100;
-    float f = (float)d;
-    return f;
-}
-
-static float make_number() {
-  float v = 0;
-  int sel;
-
-  if (return_large == true && R(3) == 1) sel = R(6); else  sel = R(4);
-  if (return_undef == false && sel == 0) sel = 1;
-
-  if (R(2) == 1) v = (float)R(100); else
-
-  switch (sel) {
-    case 0: break; 
-    case 1: v = 0; break;
-    case 2: v = 0.000001f; break;
-    case 3: v = 10000; break;
-    case 4: v = 2000000000; break;
-    case 5: v = huge(); break;
-  }
-
-  if (R(4) == 1) v = -v;
-  return v;
-}
-
-static SkColor make_color() {
-  if (R(2) == 1) return 0xFFC0F0A0; else return 0xFF000090;
-}
-
-
-static SkColor make_fill() {
-#if 0
-  int sel;
-
-  if (quick == true) sel = 0; else sel = R(6);
-
-  switch (sel) {
- 
-    case 0:
-    case 1:
-    case 2:
-      return make_color();
-      break;
-
-    case 3:
-      var r = ctx.createLinearGradient(make_number(),make_number(),make_number(),make_number());
-      for (i=0;i<4;i++)
-        r.addColorStop(make_number(),make_color());
-      return r;
-      break;
-
-    case 4:
-      var r = ctx.createRadialGradient(make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
-      for (i=0;i<4;i++)
-        r.addColorStop(make_number(),make_color());
-      return r;
-      break;
-
-    case 5:
-      var r = ctx.createPattern(imgObj,"repeat");
-      if (R(6) == 0)
-        r.addColorStop(make_number(),make_color());
-      return r;
-      break;
-  }
-#else
-    return make_color();
-#endif
-}
-    
-
-static void do_fuzz(SkCanvas* canvas) {
-    SkPath path;
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    
-  for (int i=0;i<100;i++) {
-  switch (R(33)) {
-
-    case 0:
-          paint.setColor(make_fill());
-      break;
-
-    case 1:
-      paint.setAlpha(gRand.nextU() & 0xFF);
-      break;
-
-      case 2: {
-          SkXfermode::Mode mode;
-          switch (R(3)) {
-            case 0: mode = SkXfermode::kSrc_Mode; break;
-            case 1: mode = SkXfermode::kXor_Mode; break;
-            case 2:
-            default:  // silence warning
-              mode = SkXfermode::kSrcOver_Mode; break;
-          }
-          paint.setXfermodeMode(mode);
-      }
-      break;
-
-    case 3:
-      switch (R(2)) {
-          case 0: paint.setStrokeCap(SkPaint::kRound_Cap); break;
-        case 1: paint.setStrokeCap(SkPaint::kButt_Cap); break;
-      }      
-      break;
-
-    case 4:
-      switch (R(2)) {
-          case 0: paint.setStrokeJoin(SkPaint::kRound_Join); break;
-        case 1: paint.setStrokeJoin(SkPaint::kMiter_Join); break;
-      }      
-      break;
-
-    case 5: 
-      paint.setStrokeWidth(make_number()); 
-      break;
-
-    case 6: 
-      paint.setStrokeMiter(make_number());
-      break;
-
-    case 7: 
-      if (quick == true) break;
-          SkSafeUnref(paint.setMaskFilter(SkBlurMaskFilter::Create(make_number(), SkBlurMaskFilter::kNormal_BlurStyle)));
-      break;
-
-    case 8: 
-      if (quick == true) break;
-      //ctx.shadowColor = make_fill(); 
-      break;
-
-    case 9: 
-      if (quick == true) break;
-      //ctx.shadowOffsetX = make_number();
-      //ctx.shadowOffsetY = make_number();
-      break;
-
-    case 10:
-      canvas->restore();
-      break;
-
-    case 11:
-      canvas->rotate(make_number());
-      break;
-
-    case 12:
-      canvas->save();
-      break;
-
-    case 13:
-      canvas->scale(-1,-1);
-      break;
-
-    case 14:
-
-      if (quick == true) break;
-
-      if (transval == 0) {
-        transval = make_number();
-        canvas->translate(transval,0);
-      } else {
-        canvas->translate(-transval,0);
-        transval = 0;
-      }
-
-      break;
-
-          case 15: {
-              SkRect r;
-              r.set(make_number(),make_number(),make_number(),make_number());
-              SkPaint::Style s = paint.getStyle();
-              paint.setStyle(SkPaint::kFill_Style);
-              canvas->drawRect(r, paint);
-              paint.setStyle(s);
-              // clearrect
-          } break;
-
-    case 16:
-      if (quick == true) break;
-//      ctx.drawImage(imgObj,make_number(),make_number(),make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
-      break;
-
-          case 17: {
-          SkRect r;
-          r.set(make_number(),make_number(),make_number(),make_number());
-              SkPaint::Style s = paint.getStyle();
-              paint.setStyle(SkPaint::kFill_Style);
-          canvas->drawRect(r, paint);
-              paint.setStyle(s);
-          } break;
-
-    case 18:
-          path.reset();
-      break;
-
-    case 19:
-      // ctx.clip() is evil.
-      break;
-
-    case 20:
-          path.close();
-      break;
-
-          case 21: {
-          SkPaint::Style s = paint.getStyle();
-          paint.setStyle(SkPaint::kFill_Style);
-          canvas->drawPath(path, paint);
-          paint.setStyle(s);
-          } break;
-
-          case 22: {
-              SkPaint::Style s = paint.getStyle();
-              paint.setStyle(SkPaint::kFill_Style);
-              canvas->drawPath(path, paint);
-              paint.setStyle(s);
-          } break;
-          
-          case 23: {
-              SkRect r;
-              r.set(make_number(),make_number(),make_number(),make_number());
-              SkPaint::Style s = paint.getStyle();
-              paint.setStyle(SkPaint::kStroke_Style);
-              canvas->drawRect(r, paint);
-              paint.setStyle(s);
-          } break;
-          
-    case 24:
-      if (quick == true) break;
-      //ctx.arc(make_number(),make_number(),make_number(),make_number(),make_number(),true);
-      break;
-
-    case 25:
-      if (quick == true) break;
-      //ctx.arcTo(make_number(),make_number(),make_number(),make_number(),make_number());
-      break;
-
-    case 26:
-      if (quick == true) break;
-      //ctx.bezierCurveTo(make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
-      break;
-
-    case 27:
-      path.lineTo(make_number(),make_number());
-      break;
-
-    case 28:
-      path.moveTo(make_number(),make_number());
-      break;
-
-    case 29:
-      if (quick == true) break;
-      path.quadTo(make_number(),make_number(),make_number(),make_number());
-      break;
-
-          case 30: {
-      if (quick == true) break;
-              SkMatrix matrix;
-      set2x3(&matrix, make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
-              canvas->concat(matrix);
-          } break;
-
-          case 31: {
-      if (quick == true) break;
-          SkMatrix matrix;
-          set2x3(&matrix, make_number(),make_number(),make_number(),make_number(),make_number(),make_number());
-          canvas->setMatrix(matrix);
-          } break;
-
-    case 32:
-
-      if (scale_large == true) {
-
-        switch (scval) {
-          case 0: canvas->scale(-1000000000,1); 
-                  canvas->scale(-1000000000,1);
-                  scval = 1; break;
-          case 1: canvas->scale(-.000000001f,1); scval = 2; break;
-          case 2: canvas->scale(-.000000001f,1); scval = 0; break;
-        }
-
-      }
-
-      break;
-
-
-
-  }
-  }
-
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-class FuzzView : public SampleView {
-public:
-	FuzzView() {
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Fuzzer");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(0xFFDDDDDD);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        do_fuzz(canvas);
-        this->inval(NULL);
-    }
-    
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new FuzzView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleGradients.cpp b/samplecode/SampleGradients.cpp
deleted file mode 100644
index 18536cf..0000000
--- a/samplecode/SampleGradients.cpp
+++ /dev/null
@@ -1,183 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-
-static SkShader* setgrad(const SkRect& r, SkColor c0, SkColor c1) {
-    SkColor colors[] = { c0, c1 };
-    SkPoint pts[] = { { r.fLeft, r.fTop }, { r.fRight, r.fTop } };
-    return SkGradientShader::CreateLinear(pts, colors, NULL, 2,
-                                          SkShader::kClamp_TileMode, NULL);
-}
-
-static void test_alphagradients(SkCanvas* canvas) {
-    SkRect r;
-    r.set(SkIntToScalar(10), SkIntToScalar(10),
-          SkIntToScalar(410), SkIntToScalar(30));
-    SkPaint p, p2;
-    p2.setStyle(SkPaint::kStroke_Style);
-    
-    p.setShader(setgrad(r, 0xFF00FF00, 0x0000FF00))->unref();
-    canvas->drawRect(r, p);
-    canvas->drawRect(r, p2);
-    
-    r.offset(0, r.height() + SkIntToScalar(4));
-    p.setShader(setgrad(r, 0xFF00FF00, 0x00000000))->unref();
-    canvas->drawRect(r, p);
-    canvas->drawRect(r, p2);
-    
-    r.offset(0, r.height() + SkIntToScalar(4));
-    p.setShader(setgrad(r, 0xFF00FF00, 0x00FF0000))->unref();
-    canvas->drawRect(r, p);
-    canvas->drawRect(r, p2);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct GradData {
-    int             fCount;
-    const SkColor*  fColors;
-    const SkScalar* fPos;
-};
-
-static const SkColor gColors[] = {
-    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
-};
-static const SkScalar gPos0[] = { 0, SK_Scalar1 };
-static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
-static const SkScalar gPos2[] = {
-    0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
-};
-
-static const GradData gGradData[] = {
-    { 2, gColors, NULL },
-    { 2, gColors, gPos0 },
-    { 2, gColors, gPos1 },
-    { 5, gColors, NULL },
-    { 5, gColors, gPos2 }
-};
-
-static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
-    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
-                                          data.fCount, tm, mapper);
-}
-                                          
-static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
-    SkPoint center;
-    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
-               SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
-                                          data.fPos, data.fCount, tm, mapper);
-}
-
-static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
-                           SkShader::TileMode tm, SkUnitMapper* mapper) {
-    SkPoint center;
-    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
-               SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
-                                         data.fPos, data.fCount, mapper);
-}
-
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
-                           SkShader::TileMode tm, SkUnitMapper* mapper) {
-    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, mapper);
-}
-
-static SkShader* Make2RadialConcentric(const SkPoint pts[2], const GradData& data,
-                                       SkShader::TileMode tm, SkUnitMapper* mapper) {
-    SkPoint center;
-    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
-               SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateTwoPointRadial(
-                            center, (pts[1].fX - pts[0].fX) / 7,
-                            center, (pts[1].fX - pts[0].fX) / 2,
-                            data.fColors, data.fPos, data.fCount, tm, mapper);
-}
-
-typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
-                     SkShader::TileMode tm, SkUnitMapper* mapper);
-static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial, Make2RadialConcentric
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class GradientsView : public SampleView {
-public:
-	GradientsView() {
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Gradients");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPoint pts[2] = {
-            { 0, 0 },
-            { SkIntToScalar(100), SkIntToScalar(100) }
-        };
-        SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
-        SkPaint paint;
-        paint.setDither(true);
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(10));
-
-        for (int tm = 0; tm < SkShader::kTileModeCount; ++tm) {
-            canvas->save();
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
-                canvas->save();
-                for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
-                    SkShader* shader;
-                    shader = gGradMakers[j](pts, gGradData[i], (SkShader::TileMode)tm, NULL);
-                    paint.setShader(shader)->unref();
-                    canvas->drawRect(r, paint);
-                    canvas->translate(0, SkIntToScalar(120));
-                }
-                canvas->restore();
-                canvas->translate(SkIntToScalar(120), 0);
-            }
-            canvas->restore();
-            canvas->translate(SK_ARRAY_COUNT(gGradData)*SkIntToScalar(120), 0);
-        }
-        canvas->restore();
-        
-        canvas->translate(0, SkIntToScalar(370));
-     //   test_alphagradients(canvas);
-        this->inval(NULL);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new GradientsView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleHairCurves.cpp b/samplecode/SampleHairCurves.cpp
deleted file mode 100644
index 152cbeb..0000000
--- a/samplecode/SampleHairCurves.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-
-class HairCurvesView : public SampleView {
-public:
-    HairCurvesView() {
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "HairCurves");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(-1);
-        canvas->save();
-        canvas->scale(1000 * SK_Scalar1, 1000 * SK_Scalar1);
-        SkRandom rand;
-        SkPath curves;
-        SkPath hulls;
-        SkPath ctrlPts;
-        for (int i = 0; i < 100; ++i) {
-            SkScalar pts[] = {
-                rand.nextUScalar1(), rand.nextUScalar1(),
-                rand.nextUScalar1(), rand.nextUScalar1(),
-                rand.nextUScalar1(), rand.nextUScalar1(),
-                rand.nextUScalar1(), rand.nextUScalar1()
-            };
-            curves.moveTo(pts[0], pts[1]);
-            curves.cubicTo(pts[2], pts[3],
-                         pts[4], pts[5],
-                         pts[6], pts[7]);
-
-            hulls.moveTo(pts[0], pts[1]);
-            hulls.lineTo(pts[2], pts[3]);
-            hulls.lineTo(pts[4], pts[5]);
-            hulls.lineTo(pts[6], pts[7]);
-
-            ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
-            ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
-            ctrlPts.addCircle(pts[4], pts[5], SK_Scalar1 / 200);
-            ctrlPts.addCircle(pts[6], pts[7], SK_Scalar1 / 200);
-        }
-        for (int i = 0; i < 100; ++i) {
-            SkScalar pts[] = {
-                rand.nextUScalar1(), rand.nextUScalar1(),
-                rand.nextUScalar1(), rand.nextUScalar1(),
-                rand.nextUScalar1(), rand.nextUScalar1(),
-            };
-            curves.moveTo(pts[0], pts[1]);
-            curves.quadTo(pts[2], pts[3],
-                          pts[4], pts[5]);
-
-            hulls.moveTo(pts[0], pts[1]);
-            hulls.lineTo(pts[2], pts[3]);
-            hulls.lineTo(pts[4], pts[5]);
-
-            ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
-            ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
-            ctrlPts.addCircle(pts[4], pts[5], SK_Scalar1 / 200);
-        }
-        for (int i = 0; i < 100; ++i) {
-            SkScalar pts[] = {
-                rand.nextUScalar1(), rand.nextUScalar1(),
-                rand.nextUScalar1(), rand.nextUScalar1(),
-            };
-            curves.moveTo(pts[0], pts[1]);
-            curves.lineTo(pts[2], pts[3]);
-
-            ctrlPts.addCircle(pts[0], pts[1], SK_Scalar1 / 200);
-            ctrlPts.addCircle(pts[2], pts[3], SK_Scalar1 / 200);
-        }
-
-        paint.setColor(SK_ColorBLACK);
-        canvas->drawPath(curves, paint);
-        paint.setColor(SK_ColorRED);
-        //canvas->drawPath(hulls, paint);
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setColor(SK_ColorBLUE);
-        //canvas->drawPath(ctrlPts, paint);
-
-        canvas->restore();
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new HairCurvesView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleHairModes.cpp b/samplecode/SampleHairModes.cpp
deleted file mode 100644
index cc22b17..0000000
--- a/samplecode/SampleHairModes.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkColorPriv.h"
-#include "SkShader.h"
-
-static SkCanvas* create_canvas(int w, int h) {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, w, h);
-    bm.allocPixels();
-    bm.eraseColor(0);
-    return new SkCanvas(bm);
-}
-
-static const SkBitmap& extract_bitmap(SkCanvas* canvas) {
-    return canvas->getDevice()->accessBitmap(false);
-}
-
-static const struct {
-    SkXfermode::Mode  fMode;
-    const char*         fLabel;
-} gModes[] = {
-    { SkXfermode::kClear_Mode,    "Clear"     },
-    { SkXfermode::kSrc_Mode,      "Src"       },
-    { SkXfermode::kDst_Mode,      "Dst"       },
-    { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
-    { SkXfermode::kDstOver_Mode,  "DstOver"   },
-    { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
-    { SkXfermode::kDstIn_Mode,    "DstIn"     },
-    { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
-    { SkXfermode::kDstOut_Mode,   "DstOut"    },
-    { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
-    { SkXfermode::kDstATop_Mode,  "DstATop"   },
-    { SkXfermode::kXor_Mode,      "Xor"       },
-};
-
-const int gWidth = 64;
-const int gHeight = 64;
-const SkScalar W = SkIntToScalar(gWidth);
-const SkScalar H = SkIntToScalar(gHeight);
-
-static SkScalar drawCell(SkCanvas* canvas, SkXfermode* mode, SkAlpha a0, SkAlpha a1) {
-
-    SkPaint paint;
-    paint.setAntiAlias(true);
-
-    SkRect r = SkRect::MakeWH(W, H);
-    r.inset(W/10, H/10);
-
-    paint.setColor(SK_ColorBLUE);
-    paint.setAlpha(a0);
-    canvas->drawOval(r, paint);
-
-    paint.setColor(SK_ColorRED);
-    paint.setAlpha(a1);
-    paint.setXfermode(mode);
-    for (int angle = 0; angle < 24; ++angle) {
-        SkScalar x = SkScalarCos(SkIntToScalar(angle) * (SK_ScalarPI * 2) / 24) * gWidth;
-        SkScalar y = SkScalarSin(SkIntToScalar(angle) * (SK_ScalarPI * 2) / 24) * gHeight;
-        paint.setStrokeWidth(SK_Scalar1 * angle * 2 / 24);
-        canvas->drawLine(W/2, H/2, W/2 + x, H/2 + y, paint);
-    }
-    
-    return H;
-}
-
-static SkShader* make_bg_shader() {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
-    bm.allocPixels();
-    *bm.getAddr32(0, 0) = *bm.getAddr32(1, 1) = 0xFFFFFFFF;
-    *bm.getAddr32(1, 0) = *bm.getAddr32(0, 1) = SkPackARGB32(0xFF, 0xCC, 0xCC, 0xCC);
-
-    SkShader* s = SkShader::CreateBitmapShader(bm,
-                                               SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode);
-    
-    SkMatrix m;
-    m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-    s->setLocalMatrix(m);
-    return s;
-}
-
-class HairModesView : public SampleView {
-    SkPaint fBGPaint;
-public:
-	HairModesView() {
-        fBGPaint.setShader(make_bg_shader())->unref();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "HairlineModes");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        const SkRect bounds = SkRect::MakeWH(W, H);
-        static const SkAlpha gAlphaValue[] = { 0xFF, 0x88, 0x88 };
-
-        canvas->translate(SkIntToScalar(4), SkIntToScalar(4));
-
-        for (int alpha = 0; alpha < 4; ++alpha) {
-            canvas->save();
-            canvas->save();
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); ++i) {
-                if (6 == i) {
-                    canvas->restore();
-                    canvas->translate(W * 5, 0);
-                    canvas->save();
-                }
-                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
-                
-                canvas->drawRect(bounds, fBGPaint);
-                canvas->saveLayer(&bounds, NULL);
-                SkScalar dy = drawCell(canvas, mode,
-                                       gAlphaValue[alpha & 1],
-                                       gAlphaValue[alpha & 2]);
-                canvas->restore();
-
-                canvas->translate(0, dy * 5 / 4);
-                SkSafeUnref(mode);
-            }
-            canvas->restore();
-            canvas->restore();
-            canvas->translate(W * 5 / 4, 0);
-        }
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new HairModesView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleHairline.cpp b/samplecode/SampleHairline.cpp
deleted file mode 100644
index 065087e..0000000
--- a/samplecode/SampleHairline.cpp
+++ /dev/null
@@ -1,279 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkCornerPathEffect.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-#include "SkStream.h"
-#include "SkXMLParser.h"
-#include "SkColorPriv.h"
-#include "SkImageDecoder.h"
-
-static SkRandom gRand;
-
-static void test_chromium_9005() {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config, 800, 600);
-    bm.allocPixels();
-
-    SkCanvas canvas(bm);
-
-    SkPoint pt0 = { SkFloatToScalar(799.33374f), SkFloatToScalar(1.2360189f) };
-    SkPoint pt1 = { SkFloatToScalar(808.49969f), SkFloatToScalar(-7.4338055f) };
-    
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    canvas.drawLine(pt0.fX, pt0.fY, pt1.fX, pt1.fY, paint);
-}
-
-static void generate_pts(SkPoint pts[], int count, int w, int h) {
-    for (int i = 0; i < count; i++) {
-        pts[i].set(gRand.nextUScalar1() * 3 * w - SkIntToScalar(w),
-                   gRand.nextUScalar1() * 3 * h - SkIntToScalar(h));
-    }
-}
-
-static bool check_zeros(const SkPMColor pixels[], int count, int skip) {
-    for (int i = 0; i < count; i++) {
-        if (*pixels) {
-            return false;
-        }
-        pixels += skip;
-    }
-    return true;
-}
-
-static bool check_bitmap_margin(const SkBitmap& bm, int margin) {
-    size_t rb = bm.rowBytes();
-    for (int i = 0; i < margin; i++) {
-        if (!check_zeros(bm.getAddr32(0, i), bm.width(), 1)) {
-            return false;
-        }
-        int bottom = bm.height() - i - 1;
-        if (!check_zeros(bm.getAddr32(0, bottom), bm.width(), 1)) {
-            return false;
-        }
-        // left column
-        if (!check_zeros(bm.getAddr32(i, 0), bm.height(), rb >> 2)) {
-            return false;
-        }
-        int right = bm.width() - margin + i;
-        if (!check_zeros(bm.getAddr32(right, 0), bm.height(), rb >> 2)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-#define WIDTH   620
-#define HEIGHT  460
-#define MARGIN  10
-
-static void line_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 2;
-    SkPoint pts[N];
-    for (int i = 0; i < 400; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-
-        canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
-        if (!check_bitmap_margin(bm, MARGIN)) {
-            SkDebugf("---- hairline failure (%g %g) (%g %g)\n",
-                     pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
-            break;
-        }
-    }
-}
-
-static void poly_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 8;
-    SkPoint pts[N];
-    for (int i = 0; i < 50; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-        
-        SkPath path;
-        path.moveTo(pts[0]);
-        for (int j = 1; j < N; j++) {
-            path.lineTo(pts[j]);
-        }
-        canvas->drawPath(path, paint);
-    }
-}
-
-static SkPoint ave(const SkPoint& a, const SkPoint& b) {
-    SkPoint c = a + b;
-    c.fX = SkScalarHalf(c.fX);
-    c.fY = SkScalarHalf(c.fY);
-    return c;
-}
-
-static void quad_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 30;
-    SkPoint pts[N];
-    for (int i = 0; i < 10; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-        
-        SkPath path;
-        path.moveTo(pts[0]);
-        for (int j = 1; j < N - 2; j++) {
-            path.quadTo(pts[j], ave(pts[j], pts[j+1]));
-        }
-        path.quadTo(pts[N - 2], pts[N - 1]);
-        
-        canvas->drawPath(path, paint);
-    }
-}
-
-static void add_cubic(SkPath* path, const SkPoint& mid, const SkPoint& end) {
-    SkPoint start;
-    path->getLastPt(&start);
-    path->cubicTo(ave(start, mid), ave(mid, end), end);
-}
-
-static void cube_proc(SkCanvas* canvas, const SkPaint& paint,
-                      const SkBitmap& bm) {
-    const int N = 30;
-    SkPoint pts[N];
-    for (int i = 0; i < 10; i++) {
-        generate_pts(pts, N, WIDTH, HEIGHT);
-        
-        SkPath path;
-        path.moveTo(pts[0]);
-        for (int j = 1; j < N - 2; j++) {
-            add_cubic(&path, pts[j], ave(pts[j], pts[j+1]));
-        }
-        add_cubic(&path, pts[N - 2], pts[N - 1]);
-        
-        canvas->drawPath(path, paint);
-    }
-}
-
-typedef void (*HairProc)(SkCanvas*, const SkPaint&, const SkBitmap&);
-
-static const struct {
-    const char* fName;
-    HairProc    fProc;
-} gProcs[] = {
-    { "line",   line_proc },
-    { "poly",   poly_proc },
-    { "quad",   quad_proc },
-    { "cube",   cube_proc },
-};
-
-static int cycle_hairproc_index(int index) {
-    return (index + 1) % SK_ARRAY_COUNT(gProcs);
-}
-
-class HairlineView : public SampleView {
-    SkMSec fNow;
-    int fProcIndex;
-    bool fDoAA;
-public:
-	HairlineView() {
-        fCounter = 0;
-        fProcIndex = 0;
-        fDoAA = true;
-        fNow = 0;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString str;
-            str.printf("Hair-%s", gProcs[fProcIndex].fName);
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void show_bitmaps(SkCanvas* canvas, const SkBitmap& b0, const SkBitmap& b1,
-                      const SkIRect& inset) {
-        canvas->drawBitmap(b0, 0, 0, NULL);
-        canvas->drawBitmap(b1, SkIntToScalar(b0.width()), 0, NULL);
-    }
-
-    int fCounter;
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        gRand.setSeed(fNow);
-        
-        if (false) {
-            test_chromium_9005();
-        }
-        
-        SkBitmap bm, bm2;
-        bm.setConfig(SkBitmap::kARGB_8888_Config,
-                     WIDTH + MARGIN*2,
-                     HEIGHT + MARGIN*2);
-        bm.allocPixels();
-        // this will erase our margin, which we want to always stay 0
-        bm.eraseColor(0);
-
-        bm2.setConfig(SkBitmap::kARGB_8888_Config, WIDTH, HEIGHT,
-                      bm.rowBytes());
-        bm2.setPixels(bm.getAddr32(MARGIN, MARGIN));
-        
-        SkCanvas c2(bm2);
-        SkPaint paint;
-        paint.setAntiAlias(fDoAA);
-        paint.setStyle(SkPaint::kStroke_Style);
-
-        bm2.eraseColor(0);
-        gProcs[fProcIndex].fProc(&c2, paint, bm);
-        canvas->drawBitmap(bm2, SkIntToScalar(10), SkIntToScalar(10), NULL);
-
-        SkMSec now = SampleCode::GetAnimTime();
-        if (fNow != now) {
-            fNow = now;
-            fCounter += 1;
-            fDoAA = !fDoAA;
-            if (fCounter > 50) {
-                fProcIndex = cycle_hairproc_index(fProcIndex);
-                // todo: signal that we want to rebuild our TITLE
-                fCounter = 0;
-            }
-            this->inval(NULL);
-        }
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fDoAA = !fDoAA;
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new HairlineView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleImage.cpp b/samplecode/SampleImage.cpp
deleted file mode 100644
index 8867baf..0000000
--- a/samplecode/SampleImage.cpp
+++ /dev/null
@@ -1,163 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkImageRef_GlobalPool.h"
-#include "SkStream.h"
-
-static const char* gNames[] = {
-    "1.bmp", "1.gif", "1.jpg", "1.png",
-    "2.bmp", "2.gif", "2.jpg", "2.png"
-};
-
-static bool SetImageRef(SkBitmap* bitmap, SkStream* stream,
-                        SkBitmap::Config pref, const char name[] = NULL)
-{
-    if (SkImageDecoder::DecodeStream(stream, bitmap, pref,
-                                 SkImageDecoder::kDecodeBounds_Mode, NULL)) {
-        SkASSERT(bitmap->config() != SkBitmap::kNo_Config);
-    
-        SkImageRef* ref = new SkImageRef_GlobalPool(stream, bitmap->config());
-        ref->setURI(name);
-        bitmap->setPixelRef(ref)->unref();
-        return true;
-    } else {
-        return false;
-    }
-}
-
-class ImageView : public SkView {
-public:
-    SkBitmap*   fBitmaps;
-    SkShader*   fShader;
-
-	ImageView() {
-        SkImageRef_GlobalPool::SetRAMBudget(32 * 1024);
-        
-        int i, N = SK_ARRAY_COUNT(gNames);
-        fBitmaps = new SkBitmap[N];
-        
-        for (i = 0; i < N; i++) {
-            SkString str("/skimages/");
-            str.append(gNames[i]);
-            SkFILEStream* stream = new SkFILEStream(str.c_str());
-            
-            SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config, gNames[i]);
-            if (i & 1)
-                fBitmaps[i].buildMipMap();
-            stream->unref();
-        }
-        
-        fShader = SkShader::CreateBitmapShader(fBitmaps[5],
-                                               SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode);
-        
-        if (true) {
-            SkMatrix m;
-            
-            m.setRotate(SkIntToScalar(30));
-            fShader->setLocalMatrix(m);
-        }
-        
-#if 0
-        SkImageRef::DumpPool();
-        for (i = 0; i < N; i++) {
-            SkBitmap& bm = fBitmaps[i];
-
-            SkDebugf("<%s> addr=%p", gNames[i], bm.getPixels());
-            bool success = bm.lockPixels();
-            SkDebugf(" addr=%d", bm.getPixels());
-            if (success)
-                bm.unlockPixels();
-            SkDebugf(" addr=%p", bm.getPixels());
-            success = bm.lockPixels();
-            SkDebugf(" addr=%d", bm.getPixels());
-            if (success)
-                bm.unlockPixels();            
-            SkDebugf("\n");
-        }
-        SkImageRef::DumpPool();
-#endif
-    }
-    
-    virtual ~ImageView() {
-        delete[] fBitmaps;
-        delete fShader;
-
-        SkImageRef_GlobalPool::DumpPool();
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Image");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(0xFFDDDDDD);
-//        canvas->drawColor(SK_ColorWHITE);
-    }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-        
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-        
-        SkScalar x = 0, y = 0;
-        
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++) {
-            canvas->drawBitmap(fBitmaps[i], x, y);
-            x += SkIntToScalar(fBitmaps[i].width() + 10);
-        }
-        
-        canvas->translate(0, SkIntToScalar(120));
-
-        SkPaint paint;
-        paint.setShader(fShader);
-        paint.setFilterBitmap(true);
-        SkRect r = { 0, 0, SkIntToScalar(300), SkIntToScalar(100) };
-        
-        canvas->drawRect(r, paint);
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-    
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ImageView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleImageDir.cpp b/samplecode/SampleImageDir.cpp
deleted file mode 100644
index 8d6f5e0..0000000
--- a/samplecode/SampleImageDir.cpp
+++ /dev/null
@@ -1,321 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkComposeShader.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkImageRef_GlobalPool.h"
-#include "SkOSFile.h"
-#include "SkStream.h"
-
-#include "SkBlurDrawLooper.h"
-#include "SkColorMatrixFilter.h"
-
-static void drawmarshmallow(SkCanvas* canvas) {
-    SkBitmap bitmap;
-    SkPaint paint;
-    SkRect r;
-    SkMatrix m;
-
-    SkImageDecoder::DecodeFile("/Users/reed/Downloads/3elfs.jpg", &bitmap);
-    if (!bitmap.pixelRef()) {
-        return;
-    }
-
-    SkShader* s = SkShader::CreateBitmapShader(bitmap,
-                                               SkShader::kRepeat_TileMode,
-                                               SkShader::kRepeat_TileMode);
-    paint.setShader(s)->unref();
-    m.setTranslate(SkIntToScalar(250), SkIntToScalar(134));
-    s->setLocalMatrix(m);
-
-    r.set(SkIntToScalar(250),
-          SkIntToScalar(134),
-          SkIntToScalar(250 + 449),
-          SkIntToScalar(134 + 701));
-    paint.setFlags(2);
-
-    canvas->drawRect(r, paint);
-}
-
-static void DrawRoundRect(SkCanvas& canvas) {
-   bool ret = false;
-   SkPaint  paint;
-   SkBitmap bitmap;
-   SkMatrix matrix;
-   matrix.reset();
-
-   bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812);
-   bitmap.allocPixels();
-#if 0
-    SkCanvas canvas;
-    canvas.setBitmapDevice(bitmap);
-#endif
-
-   // set up clipper
-   SkRect skclip;
-   skclip.set(SkIntToScalar(284), SkIntToScalar(40), SkIntToScalar(1370), SkIntToScalar(708));
-
-//   ret = canvas.clipRect(skclip);
-//   SkASSERT(ret);
-
-   matrix.set(SkMatrix::kMTransX, SkFloatToScalar(-1153.28f));
-   matrix.set(SkMatrix::kMTransY, SkFloatToScalar(1180.50f));
-
-   matrix.set(SkMatrix::kMScaleX, SkFloatToScalar(0.177171f));
-   matrix.set(SkMatrix::kMScaleY, SkFloatToScalar(0.177043f));
-
-   matrix.set(SkMatrix::kMSkewX, SkFloatToScalar(0.126968f));
-   matrix.set(SkMatrix::kMSkewY, SkFloatToScalar(-0.126876f));
-
-   matrix.set(SkMatrix::kMPersp0, SkFloatToScalar(0.0f));
-   matrix.set(SkMatrix::kMPersp1, SkFloatToScalar(0.0f));
-
-   ret = canvas.concat(matrix);
-
-   paint.setAntiAlias(true);
-   paint.setColor(0xb2202020);
-   paint.setStyle(SkPaint::kStroke_Style);
-   paint.setStrokeWidth(SkFloatToScalar(68.13f));
-
-   SkRect r;
-   r.set(SkFloatToScalar(-313.714417f), SkFloatToScalar(-4.826389f), SkFloatToScalar(18014.447266f), SkFloatToScalar(1858.154541f));
-   canvas.drawRoundRect(r, SkFloatToScalar(91.756363f), SkFloatToScalar(91.756363f), paint);
-}
-
-static bool SetImageRef(SkBitmap* bitmap, SkStream* stream,
-                        SkBitmap::Config pref, const char name[] = NULL) {
-#if 0
-    // test buffer streams
-    SkStream* str = new SkBufferStream(stream, 717);
-    stream->unref();
-    stream = str;
-#endif
-
-    SkImageRef* ref = new SkImageRef_GlobalPool(stream, pref, 1);
-    ref->setURI(name);
-    if (!ref->getInfo(bitmap)) {
-        delete ref;
-        return false;
-    }
-    bitmap->setPixelRef(ref)->unref();
-    return true;
-}
-
-//#define SPECIFIC_IMAGE  "/skimages/72.jpg"
-#define SPECIFIC_IMAGE  "/Users/reed/Downloads/3elfs.jpg"
-
-#define IMAGE_DIR       "/skimages/"
-#define IMAGE_SUFFIX    ".gif"
-
-class ImageDirView : public SkView {
-public:
-    SkBitmap*   fBitmaps;
-    SkString*   fStrings;
-    int         fBitmapCount;
-    int         fCurrIndex;
-    SkScalar    fSaturation;
-    SkScalar    fAngle;
-
-	ImageDirView() {
-        SkImageRef_GlobalPool::SetRAMBudget(320 * 1024);
-        
-#ifdef SPECIFIC_IMAGE
-        fBitmaps = new SkBitmap[3];
-        fStrings = new SkString[3];
-        fBitmapCount = 3;
-        const SkBitmap::Config configs[] = {
-            SkBitmap::kARGB_8888_Config,
-            SkBitmap::kRGB_565_Config,
-            SkBitmap::kARGB_4444_Config
-        };
-        for (int i = 0; i < fBitmapCount; i++) {
-#if 1
-            SkStream* stream = new SkFILEStream(SPECIFIC_IMAGE);
-            SetImageRef(&fBitmaps[i], stream, configs[i], SPECIFIC_IMAGE);
-            stream->unref();
-#else
-            SkImageDecoder::DecodeFile(SPECIFIC_IMAGE, &fBitmaps[i]);
-#endif
-        }
-#else
-        int i, N = 0;
-        SkOSFile::Iter  iter(IMAGE_DIR, IMAGE_SUFFIX);
-        SkString    name;
-        while (iter.next(&name)) {
-            N += 1;
-        }
-        fBitmaps = new SkBitmap[N];
-        fStrings = new SkString[N];
-        iter.reset(IMAGE_DIR, IMAGE_SUFFIX);
-        for (i = 0; i < N; i++) {
-            iter.next(&name);
-            SkString path(IMAGE_DIR);
-            path.append(name);
-            SkStream* stream = new SkFILEStream(path.c_str());
-            
-            SetImageRef(&fBitmaps[i], stream, SkBitmap::kNo_Config,
-                        name.c_str());
-            stream->unref();
-            fStrings[i] = name;
-        }
-        fBitmapCount = N;
-#endif
-        fCurrIndex = 0;
-        fDX = fDY = 0;
-        
-        fSaturation = SK_Scalar1;
-        fAngle = 0;
-        
-        fScale = SK_Scalar1;
-    }
-    
-    virtual ~ImageDirView() {
-        delete[] fBitmaps;
-        delete[] fStrings;
-
-        SkImageRef_GlobalPool::DumpPool();
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString str("ImageDir: ");
-#ifdef SPECIFIC_IMAGE
-            str.append(SPECIFIC_IMAGE);
-#else
-            str.append(IMAGE_DIR);
-#endif
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-//        canvas->drawColor(0xFFDDDDDD);
-        canvas->drawColor(SK_ColorGRAY);
-        canvas->drawColor(SK_ColorWHITE);
-    }
-    
-    SkScalar fScale;
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-        
-        if (true) {
-            canvas->scale(SkIntToScalar(2), SkIntToScalar(2));
-            drawmarshmallow(canvas);
-            return;
-        }
-        
-        if (false) {
-            SkPaint p;
-            p.setStyle(SkPaint::kStroke_Style);
-            p.setStrokeWidth(SkIntToScalar(4));
-            canvas->drawCircle(SkIntToScalar(100), SkIntToScalar(100), SkIntToScalar(50), p);
-            p.setAntiAlias(true);
-            canvas->drawCircle(SkIntToScalar(300), SkIntToScalar(100), SkIntToScalar(50), p);
-        }
-        if (false) {
-            SkScalar cx = this->width()/2;
-            SkScalar cy = this->height()/2;
-            canvas->translate(cx, cy);
-            canvas->scale(fScale, fScale);
-            canvas->translate(-cx, -cy);
-            DrawRoundRect(*canvas);
-            return;
-        }
-    
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-        
-        SkScalar x = SkIntToScalar(32), y = SkIntToScalar(32);
-        SkPaint paint;
-
-#if 0
-        for (int i = 0; i < fBitmapCount; i++) {
-            SkPaint p;
-            
-#if 1
-            const SkScalar cm[] = {
-                SkIntToScalar(2), 0, 0, 0, SkIntToScalar(-255),
-                0, SkIntToScalar(2), 0, 0, SkIntToScalar(-255),
-                0, 0, SkIntToScalar(2), 0, SkIntToScalar(-255),
-                0, 0, 0, SkIntToScalar(1), 0
-            };
-            SkColorFilter* cf = new SkColorMatrixFilter(cm);
-            p.setColorFilter(cf)->unref();
-#endif
-            
-            canvas->drawBitmap(fBitmaps[i], x, y, &p);
-            x += SkIntToScalar(fBitmaps[i].width() + 10);
-        }
-        return;
-#endif
-
-        canvas->drawBitmap(fBitmaps[fCurrIndex], x, y, &paint);
-#ifndef SPECIFIC_IMAGE
-        if (true) {
-            fCurrIndex += 1;
-            if (fCurrIndex >= fBitmapCount) {
-                fCurrIndex = 0;
-            }
-            this->inval(NULL);
-        }
-#endif
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        if (true) {
-            fCurrIndex += 1;
-            if (fCurrIndex >= fBitmapCount)
-                fCurrIndex = 0;
-            this->inval(NULL);
-        }
-        return new Click(this);
-    }
-    
-    virtual bool onClick(Click* click)  {
-        SkScalar center = this->width()/2;
-        fSaturation = SkScalarDiv(click->fCurr.fX - center, center/2);
-        center = this->height()/2;
-        fAngle = SkScalarDiv(click->fCurr.fY - center, center) * 180;
-
-        fDX += click->fCurr.fX - click->fPrev.fX;
-        fDY += click->fCurr.fY - click->fPrev.fY;
-        
-        fScale = SkScalarDiv(click->fCurr.fX, this->width());
-
-        this->inval(NULL);
-        return true;
-        return this->INHERITED::onClick(click);
-    }
-    
-private:
-    SkScalar fDX, fDY;
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ImageDirView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLCD.cpp b/samplecode/SampleLCD.cpp
deleted file mode 100644
index 5f8cb0b..0000000
--- a/samplecode/SampleLCD.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-#include "SkShader.h"
-
-class LCDView : public SkView {
-public:
-    LCDView() {}
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "LCD Text");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorWHITE);
-    }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        
-        SkScalar textSize = SkIntToScalar(6);
-        SkScalar delta = SK_Scalar1;
-        const char* text = "HHHamburgefonts iii";
-        size_t len = strlen(text);
-        SkScalar x0 = SkIntToScalar(10);
-        SkScalar x1 = SkIntToScalar(310);
-        SkScalar y = SkIntToScalar(20);
-
-        for (int i = 0; i < 20; i++) {
-            paint.setTextSize(textSize);
-            textSize += delta;
-            
-            paint.setLCDRenderText(false);
-            canvas->drawText(text, len, x0, y, paint);
-            paint.setLCDRenderText(true);
-            canvas->drawText(text, len, x1, y, paint);
-            
-            y += paint.getFontSpacing();
-        }
-    }
-    
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new LCDView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLayerMask.cpp b/samplecode/SampleLayerMask.cpp
deleted file mode 100644
index 883642c..0000000
--- a/samplecode/SampleLayerMask.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkView.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-class LayerMaskView : public SampleView {
-public:
-	LayerMaskView() {
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "LayerMask");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawMask(SkCanvas* canvas, const SkRect& r) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-
-        if (true) {
-            SkBitmap mask;
-            int w = SkScalarRound(r.width());
-            int h = SkScalarRound(r.height());
-            mask.setConfig(SkBitmap::kARGB_8888_Config, w, h);
-            mask.allocPixels();
-            mask.eraseColor(0);
-            SkCanvas c(mask);
-            SkRect bounds = r;
-            bounds.offset(-bounds.fLeft, -bounds.fTop);
-            c.drawOval(bounds, paint);
-            
-            paint.setXfermodeMode(SkXfermode::kDstIn_Mode);
-            canvas->drawBitmap(mask, r.fLeft, r.fTop, &paint);
-        } else {
-            SkPath p;
-            p.addOval(r);
-            p.setFillType(SkPath::kInverseWinding_FillType);
-            paint.setXfermodeMode(SkXfermode::kDstOut_Mode);
-            canvas->drawPath(p, paint);
-        }
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkRect  r;
-        r.set(SkIntToScalar(20), SkIntToScalar(20), SkIntToScalar(120), SkIntToScalar(120));
-        canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
-        canvas->drawColor(SK_ColorRED);
-        drawMask(canvas, r);
-        canvas->restore();
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new LayerMaskView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLayers.cpp b/samplecode/SampleLayers.cpp
deleted file mode 100644
index 06fdd3f..0000000
--- a/samplecode/SampleLayers.cpp
+++ /dev/null
@@ -1,278 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkBlurMaskFilter.h"
-#include "SkCamera.h"
-#include "SkColorFilter.h"
-#include "SkColorPriv.h"
-#include "SkDevice.h"
-#include "SkGradientShader.h"
-#include "SkImageDecoder.h"
-#include "SkInterpolator.h"
-#include "SkMaskFilter.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkUtils.h"
-#include "SkKey.h"
-#include "SkXfermode.h"
-#include "SkDrawFilter.h"
-
-static void make_paint(SkPaint* paint) {
-    SkColor colors[] = { 0, SK_ColorWHITE };
-    SkPoint pts[] = { { 0, 0 }, { 0, SK_Scalar1*20 } };
-    SkShader* s = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
-    
-    paint->setShader(s)->unref();
-    paint->setXfermodeMode(SkXfermode::kDstIn_Mode);
-}
-
-static void dump_layers(const char label[], SkCanvas* canvas) {
-    SkDebugf("Dump Layers(%s)\n", label);
-
-    SkCanvas::LayerIter iter(canvas, true);
-    int index = 0;
-    while (!iter.done()) {
-        const SkBitmap& bm = iter.device()->accessBitmap(false);
-        const SkIRect& clip = iter.clip().getBounds();
-        SkDebugf("Layer[%d] bitmap [%d %d] X=%d Y=%d clip=[%d %d %d %d] alpha=%d\n", index++,
-                 bm.width(), bm.height(), iter.x(), iter.y(),
-                 clip.fLeft, clip.fTop, clip.fRight, clip.fBottom,
-                 iter.paint().getAlpha());
-        iter.next();
-    }
-}
-
-// test drawing with strips of fading gradient above and below
-static void test_fade(SkCanvas* canvas) {
-    SkAutoCanvasRestore ar(canvas, true);
-
-    SkRect r;
-    
-    SkPaint p;
-    p.setAlpha(0x88);
-
-    SkAutoCanvasRestore(canvas, false);
-
-    // create the layers
-
-    r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
-    canvas->clipRect(r);
-    
-    r.fBottom = SkIntToScalar(20);
-    canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
-
-    r.fTop = SkIntToScalar(80);
-    r.fBottom = SkIntToScalar(100);
-    canvas->saveLayer(&r, NULL, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
-    
-    // now draw the "content" 
-
-    if (true) {
-        r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
-
-        canvas->saveLayerAlpha(&r, 0x80);
-
-        SkPaint p;
-        p.setColor(SK_ColorRED);
-        p.setAntiAlias(true);
-        canvas->drawOval(r, p);
-        
-        dump_layers("inside layer alpha", canvas);
-        
-        canvas->restore();
-    } else {
-        r.set(0, 0, SkIntToScalar(100), SkIntToScalar(100));
-        
-        SkPaint p;
-        p.setColor(SK_ColorRED);
-        p.setAntiAlias(true);
-        canvas->drawOval(r, p);
-    }
-    
-//    return;
-
-    dump_layers("outside layer alpha", canvas);
-
-    // now apply an effect
-
-    SkPaint paint;
-    make_paint(&paint);
-    r.set(0, 0, SkIntToScalar(100), SkIntToScalar(20));
-//    SkDebugf("--------- draw top grad\n");
-    canvas->drawRect(r, paint);
-
-    SkMatrix m;
-    SkShader* s = paint.getShader();
-    m.setScale(SK_Scalar1, -SK_Scalar1);
-    m.postTranslate(0, SkIntToScalar(100));
-    s->setLocalMatrix(m);
-    
-    r.fTop = SkIntToScalar(80);
-    r.fBottom = SkIntToScalar(100);
-//    SkDebugf("--------- draw bot grad\n");
-    canvas->drawRect(r, paint);
-}
-
-class RedFilter : public SkDrawFilter {
-public:
-    virtual bool filter(SkCanvas*, SkPaint* p, SkDrawFilter::Type) {
-        fColor = p->getColor();
-        if (fColor == SK_ColorRED) {
-            p->setColor(SK_ColorGREEN);
-        }
-        return true;
-    }
-    virtual void restore(SkCanvas*, SkPaint* p, SkDrawFilter::Type) {
-        p->setColor(fColor);
-    }
-    
-private:
-    SkColor fColor;
-};
-
-class LayersView : public SkView {
-public:
-	LayersView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Layers");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorGRAY);
-    }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-        
-        if (true) {
-            SkRect r;
-			r.set(SkIntToScalar(0), SkIntToScalar(0),
-				  SkIntToScalar(220), SkIntToScalar(120));
-            SkPaint p;
-            canvas->saveLayer(&r, &p);
-            canvas->drawColor(0xFFFF0000);
-            p.setAlpha(0);  // or 0
-            p.setXfermodeMode(SkXfermode::kSrc_Mode);
-            canvas->drawOval(r, p);
-            canvas->restore();
-            return;
-        }
-        
-        if (false) {
-            SkRect r;
-			r.set(SkIntToScalar(0), SkIntToScalar(0),
-				  SkIntToScalar(220), SkIntToScalar(120));
-            SkPaint p;
-            p.setAlpha(0x88);
-            p.setAntiAlias(true);
-            
-            if (true) {
-                canvas->saveLayer(&r, &p);
-                p.setColor(0xFFFF0000);
-                canvas->drawOval(r, p);
-                canvas->restore();
-            }
-
-            p.setColor(0xFF0000FF);
-            r.offset(SkIntToScalar(20), SkIntToScalar(50));
-            canvas->drawOval(r, p);
-        }
-
-        if (false) {
-            SkPaint p;
-            p.setAlpha(0x88);
-            p.setAntiAlias(true);
-
-            canvas->translate(SkIntToScalar(300), 0);
-
-            SkRect r;
-			r.set(SkIntToScalar(0), SkIntToScalar(0),
-				  SkIntToScalar(220), SkIntToScalar(60));
-
-            canvas->saveLayer(&r, &p, (SkCanvas::SaveFlags)(SkCanvas::kHasAlphaLayer_SaveFlag | SkCanvas::kFullColorLayer_SaveFlag));
-//            canvas->clipRect(r, SkRegion::kDifference_Op);
-//            canvas->clipRect(r, SkRegion::kIntersect_Op);
-
-			r.set(SkIntToScalar(0), SkIntToScalar(0),
-				  SkIntToScalar(220), SkIntToScalar(120));
-            p.setColor(SK_ColorBLUE);
-            canvas->drawOval(r, p);
-            canvas->restore();
-            return;
-        }
-        
-        //canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
-        test_fade(canvas);
-        return;
-
-    //    canvas->setDrawFilter(new RedFilter)->unref();
-        
-        SkRect  r;
-        SkPaint p;
-        
-        canvas->translate(SkIntToScalar(220), SkIntToScalar(20));
-        
-        p.setAntiAlias(true);
-        r.set(SkIntToScalar(20), SkIntToScalar(20),
-              SkIntToScalar(220), SkIntToScalar(120));
-        
-        p.setColor(SK_ColorBLUE);
-     //   p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(8), SkBlurMaskFilter::kNormal_BlurStyle))->unref();
-        canvas->drawRect(r, p);
-        p.setMaskFilter(NULL);
-
-        SkRect bounds = r;
-        bounds.fBottom = bounds.centerY();
-        canvas->saveLayer(&bounds, NULL, SkCanvas::kARGB_NoClipLayer_SaveFlag);
-
-        p.setColor(SK_ColorRED);
-        canvas->drawOval(r, p);
-        
-        p.setAlpha(0x80);
-        p.setXfermodeMode(SkXfermode::kDstIn_Mode);
-        canvas->drawRect(bounds, p);
-
-        canvas->restore();
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-
-	virtual bool handleKey(SkKey key) {
-        this->inval(NULL);
-        return true;
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new LayersView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLineClipper.cpp b/samplecode/SampleLineClipper.cpp
deleted file mode 100644
index 8565470..0000000
--- a/samplecode/SampleLineClipper.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkRandom.h"
-
-#include "SkLineClipper.h"
-#include "SkEdgeClipper.h"
-
-#define AUTO_ANIMATE    true
-
-static int test0(SkPoint pts[], SkRect* clip) {
-    pts[0].set(200000, 140);
-    pts[1].set(-740000, 483);
-    pts[2].set(1.00000102e-06f, 9.10000017e-05f);
-    clip->set(0, 0, 640, 480);
-    return 2;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void drawQuad(SkCanvas* canvas, const SkPoint pts[3], const SkPaint& p) {
-    SkPath path;
-    path.moveTo(pts[0]);
-    path.quadTo(pts[1], pts[2]);
-    canvas->drawPath(path, p);
-}
-
-static void drawCubic(SkCanvas* canvas, const SkPoint pts[4], const SkPaint& p) {
-    SkPath path;
-    path.moveTo(pts[0]);
-    path.cubicTo(pts[1], pts[2], pts[3]);
-    canvas->drawPath(path, p);
-}
-
-typedef void (*clipper_proc)(const SkPoint src[], const SkRect& clip,
-                            SkCanvas*, const SkPaint&, const SkPaint&);
-
-static void check_clipper(int count, const SkPoint pts[], const SkRect& clip) {
-    for (int i = 0; i < count; i++) {
-        SkASSERT(pts[i].fX >= clip.fLeft);
-        SkASSERT(pts[i].fX <= clip.fRight);
-        SkASSERT(pts[i].fY >= clip.fTop);
-        SkASSERT(pts[i].fY <= clip.fBottom);
-    }
-
-    if (count > 1) {
-        sk_assert_monotonic_y(pts, count);
-    }
-}
-
-static void line_intersector(const SkPoint src[], const SkRect& clip,
-                         SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1);
-    
-    SkPoint dst[2];
-    if (SkLineClipper::IntersectLine(src, clip, dst)) {
-        check_clipper(2, dst, clip);
-        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, dst, p0);
-    }
-}
-
-static void line_clipper(const SkPoint src[], const SkRect& clip,
-                         SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
-    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, src, p1);
-    
-    SkPoint dst[SkLineClipper::kMaxPoints];
-    int count = SkLineClipper::ClipLine(src, clip, dst);
-    for (int i = 0; i < count; i++) {
-        check_clipper(2, &dst[i], clip);
-        canvas->drawPoints(SkCanvas::kLines_PointMode, 2, &dst[i], p0);
-    }
-}
-
-static void quad_clipper(const SkPoint src[], const SkRect& clip,
-                         SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
-    drawQuad(canvas, src, p1);
-    
-    SkEdgeClipper clipper;
-    if (clipper.clipQuad(src, clip)) {
-        SkPoint pts[4];
-        SkPath::Verb verb;
-        while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
-            switch (verb) {
-                case SkPath::kLine_Verb:
-                    check_clipper(2, pts, clip);
-                    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0);
-                    break;
-                case SkPath::kQuad_Verb:
-                    check_clipper(3, pts, clip);
-                    drawQuad(canvas, pts, p0);
-                    break;
-                default:
-                    SkASSERT(!"unexpected verb");
-            }
-        }
-    }
-}
-
-static void cubic_clipper(const SkPoint src[], const SkRect& clip,
-                       SkCanvas* canvas, const SkPaint& p0, const SkPaint& p1) {
-    drawCubic(canvas, src, p1);
-    
-    SkEdgeClipper clipper;
-    if (clipper.clipCubic(src, clip)) {
-        SkPoint pts[4];
-        SkPath::Verb verb;
-        while ((verb = clipper.next(pts)) != SkPath::kDone_Verb) {
-            switch (verb) {
-                case SkPath::kLine_Verb:
-                    check_clipper(2, pts, clip);
-                    canvas->drawPoints(SkCanvas::kLines_PointMode, 2, pts, p0);
-                    break;
-                case SkPath::kCubic_Verb:
-                 //   check_clipper(4, pts, clip);
-                    drawCubic(canvas, pts, p0);
-                    break;
-                default:
-                    SkASSERT(!"unexpected verb");
-            }
-        }
-    }
-}
-
-static const clipper_proc gProcs[] = {
-    line_intersector,
-    line_clipper,
-    quad_clipper,
-    cubic_clipper
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-enum {
-    W = 640/3,
-    H = 480/3
-};
-
-class LineClipperView : public SampleView {
-    SkMSec      fNow;
-    int         fCounter;
-    int         fProcIndex;
-    SkRect      fClip;
-    SkRandom    fRand;
-    SkPoint     fPts[4];
-
-    void randPts() {
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
-            fPts[i].set(fRand.nextUScalar1() * 640,
-                        fRand.nextUScalar1() * 480);
-        }
-        fCounter += 1;
-    }
-
-public:
-	LineClipperView() {
-        fProcIndex = 0;
-        fCounter = 0;
-        fNow = 0;
-
-        int x = (640 - W)/2;
-        int y = (480 - H)/2;
-        fClip.set(SkIntToScalar(x), SkIntToScalar(y),
-                  SkIntToScalar(x + W), SkIntToScalar(y + H));
-        this->randPts();
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "LineClipper");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    static void drawVLine(SkCanvas* canvas, SkScalar x, const SkPaint& paint) {
-        canvas->drawLine(x, -999, x, 999, paint);
-    }
-    
-    static void drawHLine(SkCanvas* canvas, SkScalar y, const SkPaint& paint) {
-        canvas->drawLine(-999, y, 999, y, paint);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkMSec now = SampleCode::GetAnimTime();
-        if (fNow != now) {
-            fNow = now;
-            this->randPts();
-            this->inval(NULL);
-        }
-
-     //   fProcIndex = test0(fPts, &fClip);
-
-        SkPaint paint, paint1;
-        
-        drawVLine(canvas, fClip.fLeft + SK_ScalarHalf, paint);
-        drawVLine(canvas, fClip.fRight - SK_ScalarHalf, paint);
-        drawHLine(canvas, fClip.fTop + SK_ScalarHalf, paint);
-        drawHLine(canvas, fClip.fBottom - SK_ScalarHalf, paint);
-        
-        paint.setColor(SK_ColorLTGRAY);
-        canvas->drawRect(fClip, paint);
-        
-        paint.setAntiAlias(true);
-        paint.setColor(SK_ColorBLUE);
-        paint.setStyle(SkPaint::kStroke_Style);
-      //  paint.setStrokeWidth(SkIntToScalar(3));
-        paint.setStrokeCap(SkPaint::kRound_Cap);
-        
-        paint1.setAntiAlias(true);
-        paint1.setColor(SK_ColorRED);
-        paint1.setStyle(SkPaint::kStroke_Style);
-        gProcs[fProcIndex](fPts, fClip, canvas, paint, paint1);
-        this->inval(NULL);
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-     //   fProcIndex = (fProcIndex + 1) % SK_ARRAY_COUNT(gProcs);
-        if (x < 50 && y < 50) {
-            this->randPts();
-        }
-        this->inval(NULL);
-        return NULL;
-    }
-        
-    virtual bool onClick(Click* click) {
-        return false;
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new LineClipperView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleLines.cpp b/samplecode/SampleLines.cpp
deleted file mode 100644
index 0048d39..0000000
--- a/samplecode/SampleLines.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkCornerPathEffect.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-#include "SkStream.h"
-#include "SkXMLParser.h"
-#include "SkColorPriv.h"
-#include "SkImageDecoder.h"
-
-class LinesView : public SampleView {
-public:
-	LinesView() {}
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Lines");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    /*
-     0x1F * x + 0x1F * (32 - x)
-     */
-    void drawRings(SkCanvas* canvas) {
-        canvas->scale(SkIntToScalar(1)/2, SkIntToScalar(1)/2);
-        
-        SkRect  r;        
-        SkScalar x = SkIntToScalar(10);
-        SkScalar y = SkIntToScalar(10);
-        r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100));
-        
-        SkPaint paint;
-     //   paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
-        paint.setColor(0xFFFF8800);
-     //   paint.setColor(0xFFFFFFFF);
-        canvas->drawRect(r, paint);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkBitmap bm;
-        SkImageDecoder::DecodeFile("/kill.gif", &bm);
-        canvas->drawBitmap(bm, 0, 0, NULL);
-        
-        this->drawRings(canvas);
-        return;
-
-        SkPaint paint;
-        
-      //  fAlpha = 0x80;
-        paint.setColor(SK_ColorWHITE);
-        paint.setAlpha(fAlpha & 0xFF);
-        SkRect r;
-        
-        SkScalar x = SkIntToScalar(10);
-        SkScalar y = SkIntToScalar(10);
-        r.set(x, y, x + SkIntToScalar(100), y + SkIntToScalar(100));
-        canvas->drawRect(r, paint);
-        return;
-        
-        paint.setColor(0xffffff00);            // yellow
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkIntToScalar(2));
-        
-//        y += SK_Scalar1/2;
-
-        canvas->drawLine(x, y, x + SkIntToScalar(90), y + SkIntToScalar(90), paint);
-
-        paint.setAntiAlias(true);              // with anti-aliasing
-        y += SkIntToScalar(10);
-        canvas->drawLine(x, y, x + SkIntToScalar(90), y + SkIntToScalar(90), paint);
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fAlpha = SkScalarRound(y);
-        this->inval(NULL);
-        return NULL;
-    }
-private:
-
-    int fAlpha;
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new LinesView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleMeasure.cpp b/samplecode/SampleMeasure.cpp
deleted file mode 100644
index 35ad5bd..0000000
--- a/samplecode/SampleMeasure.cpp
+++ /dev/null
@@ -1,122 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "Sk1DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkDither.h"
-
-// exercise scale/linear/devkern
-struct Setting {
-    SkScalar    fScale;
-    bool        fLinearText;
-    bool        fDevKernText;
-};
-
-static const SkScalar ONE = SkIntToScalar(9999)/10000;
-
-static const Setting gSettings[] = {
-    { 0,            false,  false   },
-    { 0,            false,  true    },
-    { 0,            true,   false   },
-    { 0,            true,   true    },
-    { ONE,   false,  false   },
-    { ONE,   false,  true    },
-    { ONE,   true,   false   },
-    { ONE,   true,   true    }
-};
-
-static void doMeasure(SkCanvas* canvas, const SkPaint& paint, const char text[]) {
-    SkScalar    dy = paint.getFontMetrics(NULL);
-
-    size_t      len = strlen(text);
-    SkAutoTMalloc<SkScalar> autoWidths(len);
-    SkScalar*   widths = autoWidths.get();
-    SkAutoTMalloc<SkRect> autoRects(len);
-    SkRect*     rects = autoRects.get();
-    SkRect      bounds;
-
-    SkPaint p(paint);
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gSettings); i++) {
-        p.setLinearText(gSettings[i].fLinearText);
-        p.setDevKernText(gSettings[i].fDevKernText);
-        SkScalar scale = gSettings[i].fScale;
-        
-        int n = p.getTextWidths(text, len, widths, rects);
-        SkScalar w = p.measureText(text, len, &bounds, scale);
-        
-        p.setStyle(SkPaint::kFill_Style);
-        p.setColor(0x8888FF88);
-        canvas->drawRect(bounds, p);
-        p.setColor(0xFF000000);
-        canvas->drawText(text, len, 0, 0, p);
-
-        p.setStyle(SkPaint::kStroke_Style);
-        p.setStrokeWidth(0);
-        p.setColor(0xFFFF0000);
-        SkScalar x = 0;
-        for (int j = 0; j < n; j++) {
-            SkRect r = rects[j];
-            r.offset(x, 0);
-            canvas->drawRect(r, p);
-            x += widths[j];
-        }
-
-        p.setColor(0xFF0000FF);
-        canvas->drawLine(0, 0, w, 0, p);
-        p.setStrokeWidth(SkIntToScalar(4));
-        canvas->drawPoint(x, 0, p);
-        
-        canvas->translate(0, dy);
-    }
-}
-
-class MeasureView : public SampleView {
-public:
-    SkPaint fPaint;
-
-	MeasureView() {
-        fPaint.setAntiAlias(true);
-        fPaint.setTextSize(SkIntToScalar(64));
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Measure");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(fPaint.getTextSize(), fPaint.getTextSize());
-        doMeasure(canvas, fPaint, "Hamburgefons");
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new MeasureView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleMipMap.cpp b/samplecode/SampleMipMap.cpp
deleted file mode 100644
index de5aac5..0000000
--- a/samplecode/SampleMipMap.cpp
+++ /dev/null
@@ -1,150 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-#include "SkShader.h"
-
-static SkBitmap createBitmap(int n) {
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, n, n);
-    bitmap.allocPixels();
-    bitmap.eraseColor(0);
-    
-    SkCanvas canvas(bitmap);
-    SkRect r;
-    r.set(0, 0, SkIntToScalar(n), SkIntToScalar(n));
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    
-    paint.setColor(SK_ColorRED);
-    canvas.drawOval(r, paint);
-    paint.setColor(SK_ColorBLUE);
-    paint.setStrokeWidth(SkIntToScalar(n)/15);
-    paint.setStyle(SkPaint::kStroke_Style);
-    canvas.drawLine(0, 0, r.fRight, r.fBottom, paint);
-    canvas.drawLine(0, r.fBottom, r.fRight, 0, paint);
-    
-    return bitmap;
-}
-
-class MipMapView : public SampleView {
-    SkBitmap fBitmap;
-    enum {
-        N = 64
-    };
-public:
-    MipMapView() {
-        fBitmap = createBitmap(N);
-        
-        fWidth = N;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "MipMaps");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawN(SkCanvas* canvas, const SkBitmap& bitmap) {
-        SkAutoCanvasRestore acr(canvas, true);
-        for (int i = N; i > 1; i >>= 1) {
-            canvas->drawBitmap(bitmap, 0, 0, NULL);
-            canvas->translate(SkIntToScalar(N + 8), 0);
-            canvas->scale(SK_ScalarHalf, SK_ScalarHalf);
-        }
-    }
-    
-    void drawN2(SkCanvas* canvas, const SkBitmap& bitmap) {
-        SkBitmap bg;
-        bg.setConfig(SkBitmap::kARGB_8888_Config, N, N);
-        bg.allocPixels();
-        
-        SkAutoCanvasRestore acr(canvas, true);
-        for (int i = 0; i < 6; i++) {
-            bg.eraseColor(0);
-            SkCanvas c(bg);
-            c.scale(SK_Scalar1 / (1 << i), SK_Scalar1 / (1 << i));
-            c.drawBitmap(bitmap, 0, 0, NULL);
-
-            canvas->save();
-            canvas->scale(SkIntToScalar(1 << i), SkIntToScalar(1 << i));
-            canvas->drawBitmap(bg, 0, 0, NULL);
-            canvas->restore();
-            canvas->translate(SkIntToScalar(N + 8), 0);
-        }
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-        
-        canvas->scale(1.00000001f, 0.9999999f);
-
-        drawN2(canvas, fBitmap);
-
-        canvas->translate(0, SkIntToScalar(N + 8));
-        SkBitmap bitmap(fBitmap);
-        bitmap.buildMipMap();
-        drawN2(canvas, bitmap);
-
-        SkScalar time = SampleCode::GetAnimScalar(SkIntToScalar(1)/4,
-                                                  SkIntToScalar(2));
-        if (time >= SK_Scalar1) {
-            time = SkIntToScalar(2) - time;
-        }
-        fWidth = 8 + SkScalarRound(N * time);
-
-        SkRect dst;
-        dst.set(0, 0, SkIntToScalar(fWidth), SkIntToScalar(fWidth));
-
-        SkPaint paint;
-        paint.setFilterBitmap(true);
-        paint.setAntiAlias(true);
-
-        canvas->translate(0, SkIntToScalar(N + 8));
-        canvas->drawBitmapRect(fBitmap, NULL, dst, NULL);
-        canvas->translate(SkIntToScalar(N + 8), 0);
-        canvas->drawBitmapRect(fBitmap, NULL, dst, &paint);
-        canvas->translate(-SkIntToScalar(N + 8), SkIntToScalar(N + 8));
-        canvas->drawBitmapRect(bitmap, NULL, dst, NULL);
-        canvas->translate(SkIntToScalar(N + 8), 0);
-        canvas->drawBitmapRect(bitmap, NULL, dst, &paint);
-        
-        SkShader* s = SkShader::CreateBitmapShader(bitmap,
-                                                   SkShader::kRepeat_TileMode,
-                                                   SkShader::kRepeat_TileMode);
-        paint.setShader(s)->unref();
-        SkMatrix m;
-        m.setScale(SkIntToScalar(fWidth) / N,
-                   SkIntToScalar(fWidth) / N);
-        s->setLocalMatrix(m);
-        SkRect r;
-        r.set(0, 0, SkIntToScalar(4*N), SkIntToScalar(5*N/2));
-        r.offset(SkIntToScalar(N + 12), -SkIntToScalar(N + 4));
-        canvas->drawRect(r, paint);
-        
-        this->inval(NULL);
-    }
-    
-private:
-    int fWidth;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new MipMapView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleMovie.cpp b/samplecode/SampleMovie.cpp
deleted file mode 100644
index 17fbc79..0000000
--- a/samplecode/SampleMovie.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkMovie.h"
-#include "SkTime.h"
-#include <new>
-
-class AnimGifView : public SkView {
-    SkMovie*    fMovie;
-public:
-	AnimGifView() {
-        fMovie = SkMovie::DecodeFile("/skimages/dollarblk.gif");
-    }
-
-    virtual ~AnimGifView() {
-        SkSafeUnref(fMovie);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Animated Gif");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(0xFFDDDDDD);
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-
-        if (fMovie) {
-            if (fMovie->duration()) {
-                fMovie->setTime(SkTime::GetMSecs() % fMovie->duration());
-            } else {
-                fMovie->setTime(0);
-            }
-            canvas->drawBitmap(fMovie->bitmap(), SkIntToScalar(20),
-                               SkIntToScalar(20));
-            this->inval(NULL);
-        }
-    }
-
-private:
-    SkRect      fClip;
-    SkIPoint*   fPoints;
-    SkPath      fPath;
-    int         fPtCount;
-
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new AnimGifView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleNinePatch.cpp b/samplecode/SampleNinePatch.cpp
deleted file mode 100644
index bab7b23..0000000
--- a/samplecode/SampleNinePatch.cpp
+++ /dev/null
@@ -1,109 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkGpuDevice.h"
-
-static void make_bitmap(SkBitmap* bitmap, GrContext* ctx, SkIRect* center) {
-    SkDevice* dev;
-    SkCanvas canvas;
-    
-    const int kFixed = 28;
-    const int kStretchy = 8;
-    const int kSize = 2*kFixed + kStretchy;
-
-    if (ctx) {
-        dev = new SkGpuDevice(ctx, SkBitmap::kARGB_8888_Config, kSize, kSize);
-        *bitmap = dev->accessBitmap(false);
-    } else {
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kSize, kSize);
-        bitmap->allocPixels();
-        dev = new SkDevice(*bitmap);
-    }
-
-    canvas.setDevice(dev)->unref();
-    canvas.clear(0);
-
-    SkRect r = SkRect::MakeWH(SkIntToScalar(kSize), SkIntToScalar(kSize));
-    const SkScalar strokeWidth = SkIntToScalar(6);
-    const SkScalar radius = SkIntToScalar(kFixed) - strokeWidth/2;
-
-    center->setXYWH(kFixed, kFixed, kStretchy, kStretchy);
-
-    SkPaint paint;
-    paint.setAntiAlias(true);
-
-    paint.setColor(0xFFFF0000);
-    canvas.drawRoundRect(r, radius, radius, paint);
-    r.setXYWH(SkIntToScalar(kFixed), 0, SkIntToScalar(kStretchy), SkIntToScalar(kSize));
-    paint.setColor(0x8800FF00);
-    canvas.drawRect(r, paint);
-    r.setXYWH(0, SkIntToScalar(kFixed), SkIntToScalar(kSize), SkIntToScalar(kStretchy));
-    paint.setColor(0x880000FF);
-    canvas.drawRect(r, paint);
-}
-
-
-class NinePatchView : public SampleView {
-public:
-	NinePatchView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "NinePatch");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkBitmap bm;
-        SkIRect center;
-        make_bitmap(&bm, SampleCode::GetGr(), &center);
-
-        // amount of bm that should not be stretched (unless we have to)
-        const SkScalar fixed = SkIntToScalar(bm.width() - center.width());
-
-        const SkTSize<SkScalar> size[] = {
-            { fixed * 4 / 5, fixed * 4 / 5 },   // shrink in both axes
-            { fixed * 4 / 5, fixed * 4 },       // shrink in X
-            { fixed * 4,     fixed * 4 / 5 },   // shrink in Y
-            { fixed * 4,     fixed * 4 }
-        };
-
-        canvas->drawBitmap(bm, SkIntToScalar(10), SkIntToScalar(10), NULL);
-
-        SkScalar x = SkIntToScalar(100);
-        SkScalar y = SkIntToScalar(100);
-
-        SkPaint paint;
-        paint.setFilterBitmap(true);
-
-        for (int iy = 0; iy < 2; ++iy) {
-            for (int ix = 0; ix < 2; ++ix) {
-                int i = ix * 2 + iy;
-                SkRect r = SkRect::MakeXYWH(x + ix * fixed, y + iy * fixed,
-                                            size[i].width(), size[i].height());
-                canvas->drawBitmapNine(bm, center, r, &paint);
-            }
-        }
-    }
-    
-private:
-    SkScalar fX, fY;
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new NinePatchView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleOvalTest.cpp b/samplecode/SampleOvalTest.cpp
deleted file mode 100644
index dab4fbc..0000000
--- a/samplecode/SampleOvalTest.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-
-static const int kILimit = 101;
-static const SkScalar kLimit = SK_Scalar1 * kILimit;
-
-class OvalTestView : public SampleView {
-public:
-    SkSize      fSize;
-    SkPMColor   fInsideColor;   // signals an interior pixel that was not set
-    SkPMColor   fOutsideColor;  // signals an exterior pixels that was set
-    SkBitmap    fBitmap;
-
-	OvalTestView() {
-        fSize.set(SK_Scalar1, SK_Scalar1);
-
-        fBitmap.setConfig(SkBitmap::kARGB_8888_Config, kILimit, kILimit);
-        fBitmap.allocPixels();
-
-        fInsideColor = SkPreMultiplyColor(SK_ColorRED);
-        fOutsideColor = SkPreMultiplyColor(SK_ColorGREEN);
-
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "OvalTest");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawOval() {
-        SkCanvas canvas(fBitmap);
-        SkPaint p;
-
-        fBitmap.eraseColor(0);
-        canvas.drawOval(SkRect::MakeSize(fSize), p);
-    }
-
-    int checkOval(int* flatCount, int* buldgeCount) {
-        int flatc = 0;
-        int buldgec = 0;
-        const SkScalar rad = SkScalarHalf(fSize.width());
-        SkScalar cx = SkScalarHalf(fSize.width());
-        SkScalar cy = SkScalarHalf(fSize.height());
-        for (int y = 0; y < kILimit; y++) {
-            for (int x = 0; x < kILimit; x++) {
-                // measure from pixel centers
-                SkScalar px = SkIntToScalar(x) + SK_ScalarHalf;
-                SkScalar py = SkIntToScalar(y) + SK_ScalarHalf;
-
-                SkPMColor* ptr = fBitmap.getAddr32(x, y);
-                SkScalar dist = SkPoint::Length(px - cx, py - cy);
-                if (dist <= rad && !*ptr) {
-                    flatc++;
-                    *ptr = fInsideColor;
-                } else if (dist > rad && *ptr) {
-                    buldgec++;
-                    *ptr = fOutsideColor;
-                }
-            }
-        }
-        if (flatCount) *flatCount = flatc;
-        if (buldgeCount) *buldgeCount = buldgec;
-        return flatc + buldgec;
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        this->drawOval();
-        int flatCount, buldgeCount;
-        this->checkOval(&flatCount, &buldgeCount);
-        this->inval(NULL);
-
-        canvas->drawBitmap(fBitmap, SkIntToScalar(20), SkIntToScalar(20), NULL);
-
-
-        static int gFlatCount;
-        static int gBuldgeCount;
-        gFlatCount += flatCount;
-        gBuldgeCount += buldgeCount;
-
-        if (fSize.fWidth < kLimit) {
-            SkDebugf("--- width=%g, flat=%d buldge=%d total: flat=%d buldge=%d\n", fSize.fWidth,
-                     flatCount, buldgeCount, gFlatCount, gBuldgeCount);
-            fSize.fWidth += SK_Scalar1;
-            fSize.fHeight += SK_Scalar1;
-        } else {
-         //   fSize.set(SK_Scalar1, SK_Scalar1);
-        }
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        return NULL;
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new OvalTestView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleOverflow.cpp b/samplecode/SampleOverflow.cpp
deleted file mode 100644
index 05b28eb..0000000
--- a/samplecode/SampleOverflow.cpp
+++ /dev/null
@@ -1,107 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-
-static void DrawRoundRect() {
-#ifdef SK_SCALAR_IS_FIXED
-    bool ret = false;
-    SkPaint  paint;
-    SkBitmap bitmap;
-    SkCanvas canvas;
-    SkMatrix matrix;
-    matrix.reset();
-    
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 1370, 812);
-    bitmap.allocPixels();
-    canvas.setBitmapDevice(bitmap);
-    
-    // set up clipper
-    SkRect skclip;
-    skclip.set(SkIntToFixed(284), SkIntToFixed(40), SkIntToFixed(1370), SkIntToFixed(708));
-    
-    ret = canvas.clipRect(skclip);
-    SkASSERT(ret);
-    
-    matrix.set(SkMatrix::kMTransX, SkFloatToFixed(-1153.28));
-    matrix.set(SkMatrix::kMTransY, SkFloatToFixed(1180.50));
-    
-    matrix.set(SkMatrix::kMScaleX, SkFloatToFixed(0.177171));
-    matrix.set(SkMatrix::kMScaleY, SkFloatToFixed(0.177043));
-    
-    matrix.set(SkMatrix::kMSkewX, SkFloatToFixed(0.126968));
-    matrix.set(SkMatrix::kMSkewY, SkFloatToFixed(-0.126876));
-    
-    matrix.set(SkMatrix::kMPersp0, SkFloatToFixed(0.0));
-    matrix.set(SkMatrix::kMPersp1, SkFloatToFixed(0.0));
-    
-    ret = canvas.concat(matrix);
-    
-    paint.setAntiAlias(true);
-    paint.setColor(0xb2202020);
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeWidth(SkFloatToFixed(68.13));
-    
-    SkRect r;
-    r.set(SkFloatToFixed(-313.714417), SkFloatToFixed(-4.826389), SkFloatToFixed(18014.447266), SkFloatToFixed(1858.154541));
-    canvas.drawRoundRect(r, SkFloatToFixed(91.756363), SkFloatToFixed(91.756363), paint);
-#endif
-}
-
-static bool HitTestPath(const SkPath& path, SkScalar x, SkScalar y) {
-    SkRegion    rgn, clip;
-    
-    int ix = SkScalarFloor(x);
-    int iy = SkScalarFloor(y);
-
-    clip.setRect(ix, iy, ix + 1, iy + 1);
-    
-    bool contains = rgn.setPath(path, clip);
-    return contains;
-}
-
-static void TestOverflowHitTest() {
-    SkPath path;
-    
-#ifdef SK_SCALAR_IS_FLOATx
-    path.addCircle(0, 0, 70000, SkPath::kCCW_Direction);
-    SkASSERT(HitTestPath(path, 40000, 40000));
-#endif
-}
-
-class OverflowView : public SampleView {
-public:
-	OverflowView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Circles");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        DrawRoundRect();
-        TestOverflowHitTest();
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new OverflowView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePageFlip.cpp b/samplecode/SamplePageFlip.cpp
deleted file mode 100644
index b2d96f0..0000000
--- a/samplecode/SamplePageFlip.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkRandom.h"
-#include "SkFlipPixelRef.h"
-#include "SkPageFlipper.h"
-
-#include <pthread.h>
-
-#define WIDTH   160
-#define HEIGHT  200
-
-static bool gDone;
-
-static void bounce(SkScalar* x, SkScalar* dx, const int max) {
-    *x += *dx;
-    if (*x < 0) {
-        *x = 0;
-        if (*dx < 0) {
-            *dx = -*dx;
-        }
-    } else if (*x > SkIntToScalar(max)) {
-        *x = SkIntToScalar(max);
-        if (*dx > 0) {
-            *dx = -*dx;
-        }
-    }
-}
-
-static void* draw_proc(void* context) {
-    const int OVALW = 32;
-    const int OVALH = 32;
-
-    const SkBitmap* bm = static_cast<const SkBitmap*>(context);
-    SkFlipPixelRef* ref = static_cast<SkFlipPixelRef*>(bm->pixelRef());
-
-    const int DSCALE = 1;
-    SkScalar    dx = SkIntToScalar(7) / DSCALE;
-    SkScalar    dy = SkIntToScalar(5) / DSCALE;
-    SkScalar    x = 0;
-    SkScalar    y = 0;
-
-    SkPaint paint;
-    
-    paint.setAntiAlias(true);
-    paint.setColor(SK_ColorRED);
-    
-    SkRect oval;
-    oval.setEmpty();
-
-    SkRect clipR = SkRect::MakeWH(SkIntToScalar(bm->width()), SkIntToScalar(bm->height()));
-    clipR.inset(SK_Scalar1/4, SK_Scalar1/4);
-                                  
-    while (!gDone) {
-        ref->inval(oval, true);
-        oval.set(x, y, x + SkIntToScalar(OVALW), y + SkIntToScalar(OVALH));
-        ref->inval(oval, true);
-
-        SkAutoFlipUpdate    update(ref);
-        
-        if (!update.dirty().isEmpty()) {
-            // this must be local to the loop, since it needs to forget the pixels
-            // its writing to after each iteration, since we do the swap
-            SkCanvas    canvas(update.bitmap());
-            canvas.clipRegion(update.dirty());
-            canvas.drawColor(0, SkXfermode::kClear_Mode);            
-            canvas.clipRect(clipR, SkRegion::kIntersect_Op, true);
-            
-            canvas.drawOval(oval, paint);
-        }
-        bounce(&x, &dx, WIDTH-OVALW);
-        bounce(&y, &dy, HEIGHT-OVALH);
-    }
-    return NULL;
-}
-
-static const SkBitmap::Config gConfigs[] = {
-    SkBitmap::kARGB_8888_Config,
-    SkBitmap::kRGB_565_Config,
-    SkBitmap::kARGB_4444_Config,
-    SkBitmap::kA8_Config
-};
-
-class PageFlipView : public SampleView {
-public:
-    
-    enum { N = SK_ARRAY_COUNT(gConfigs) };
-    
-    pthread_t   fThreads[N];
-    SkBitmap    fBitmaps[N];
-
-	PageFlipView() {
-        gDone = false;
-        for (int i = 0; i < N; i++) {
-            int             status;
-            pthread_attr_t  attr;
-            
-            status = pthread_attr_init(&attr);
-            SkASSERT(0 == status);
-
-            fBitmaps[i].setConfig(gConfigs[i], WIDTH, HEIGHT);
-            SkFlipPixelRef* pr = new SkFlipPixelRef(gConfigs[i], WIDTH, HEIGHT);
-            fBitmaps[i].setPixelRef(pr)->unref();
-            fBitmaps[i].eraseColor(0);
-
-            status = pthread_create(&fThreads[i], &attr,  draw_proc, &fBitmaps[i]);
-            SkASSERT(0 == status);
-        }
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-    virtual ~PageFlipView() {
-        gDone = true;
-        for (int i = 0; i < N; i++) {
-            void* ret;
-            int status = pthread_join(fThreads[i], &ret);
-            SkASSERT(0 == status);
-        }
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "PageFlip");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkScalar x = SkIntToScalar(10);
-        SkScalar y = SkIntToScalar(10);
-        for (int i = 0; i < N; i++) {
-            canvas->drawBitmap(fBitmaps[i], x, y);
-            x += SkIntToScalar(fBitmaps[i].width() + 20);
-        }
-        this->inval(NULL);
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PageFlipView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePatch.cpp b/samplecode/SamplePatch.cpp
deleted file mode 100644
index edc7804..0000000
--- a/samplecode/SamplePatch.cpp
+++ /dev/null
@@ -1,349 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkOSFile.h"
-#include "SkStream.h"
-
-#include "SkGeometry.h" // private include :(
-
-static void drawtriangle(SkCanvas* canvas, const SkPaint& paint,
-                         const SkPoint pts[3]) {
-    SkPath path;
-
-    path.moveTo(pts[0]);
-    path.lineTo(pts[1]);
-    path.lineTo(pts[2]);
-
-    canvas->drawPath(path, paint);
-}
-
-static SkShader* make_shader0(SkIPoint* size) {
-    SkBitmap    bm;
-
-//    SkImageDecoder::DecodeFile("/skimages/progressivejpg.jpg", &bm);
-    SkImageDecoder::DecodeFile("/skimages/logo.png", &bm);
-    size->set(bm.width(), bm.height());
-    return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
-                                        SkShader::kClamp_TileMode);
-}
-
-static SkShader* make_shader1(const SkIPoint& size) {
-    SkPoint pts[] = { { 0, 0, },
-                      { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
-    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
-    return SkGradientShader::CreateLinear(pts, colors, NULL,
-                    SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class Patch {
-public:
-    Patch() { sk_bzero(fPts, sizeof(fPts)); }
-    ~Patch() {}
-
-    void setPatch(const SkPoint pts[12]) {
-        memcpy(fPts, pts, 12 * sizeof(SkPoint));
-        fPts[12] = pts[0];  // the last shall be first
-    }
-    void setBounds(int w, int h) { fW = w; fH = h; }
-
-    void draw(SkCanvas*, const SkPaint&, int segsU, int segsV,
-              bool doTextures, bool doColors);
-
-private:
-    SkPoint fPts[13];
-    int     fW, fH;
-};
-
-static void eval_patch_edge(const SkPoint cubic[], SkPoint samples[], int segs) {
-    SkScalar t = 0;
-    SkScalar dt = SK_Scalar1 / segs;
-
-    samples[0] = cubic[0];
-    for (int i = 1; i < segs; i++) {
-        t += dt;
-        SkEvalCubicAt(cubic, t, &samples[i], NULL, NULL);
-    }
-}
-
-static void eval_sheet(const SkPoint edge[], int nu, int nv, int iu, int iv,
-                       SkPoint* pt) {
-    const int TL = 0;
-    const int TR = nu;
-    const int BR = TR + nv;
-    const int BL = BR + nu;
-
-    SkScalar u = SkIntToScalar(iu) / nu;
-    SkScalar v = SkIntToScalar(iv) / nv;
-
-    SkScalar uv = SkScalarMul(u, v);
-    SkScalar Uv = SkScalarMul(SK_Scalar1 - u, v);
-    SkScalar uV = SkScalarMul(u, SK_Scalar1 - v);
-    SkScalar UV = SkScalarMul(SK_Scalar1 - u, SK_Scalar1 - v);
-
-    SkScalar x0 = SkScalarMul(UV, edge[TL].fX) + SkScalarMul(uV, edge[TR].fX) +
-                  SkScalarMul(Uv, edge[BL].fX) + SkScalarMul(uv, edge[BR].fX);
-    SkScalar y0 = SkScalarMul(UV, edge[TL].fY) + SkScalarMul(uV, edge[TR].fY) +
-                  SkScalarMul(Uv, edge[BL].fY) + SkScalarMul(uv, edge[BR].fY);
-
-    SkScalar x =    SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fX) +
-                    SkScalarMul(u, edge[TR+iv].fX) +
-                    SkScalarMul(v, edge[BR+nu-iu].fX) +
-                    SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fX) - x0;
-    SkScalar y =    SkScalarMul(SK_Scalar1 - v, edge[TL+iu].fY) +
-                    SkScalarMul(u, edge[TR+iv].fY) +
-                    SkScalarMul(v, edge[BR+nu-iu].fY) +
-                    SkScalarMul(SK_Scalar1 - u, edge[BL+nv-iv].fY) - y0;
-    pt->set(x, y);
-}
-
-static int ScalarTo255(SkScalar v) {
-    int scale = SkScalarToFixed(v) >> 8;
-    if (scale < 0) {
-        scale = 0;
-    } else if (scale > 255) {
-        scale = 255;
-    }
-    return scale;
-}
-
-static SkColor make_color(SkScalar s, SkScalar t) {
-    int cs = ScalarTo255(s);
-    int ct = ScalarTo255(t);
-    return SkColorSetARGB(0xFF, cs, 0, 0) + SkColorSetARGB(0, 0, ct, 0);
-}
-
-void Patch::draw(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
-                 bool doTextures, bool doColors) {
-    if (nu < 1 || nv < 1) {
-        return;
-    }
-
-    int i, npts = (nu + nv) * 2;
-    SkAutoSTMalloc<16, SkPoint> storage(npts + 1);
-    SkPoint* edge0 = storage.get();
-    SkPoint* edge1 = edge0 + nu;
-    SkPoint* edge2 = edge1 + nv;
-    SkPoint* edge3 = edge2 + nu;
-
-    // evaluate the edge points
-    eval_patch_edge(fPts + 0, edge0, nu);
-    eval_patch_edge(fPts + 3, edge1, nv);
-    eval_patch_edge(fPts + 6, edge2, nu);
-    eval_patch_edge(fPts + 9, edge3, nv);
-    edge3[nv] = edge0[0];   // the last shall be first
-
-    for (i = 0; i < npts; i++) {
-//        canvas->drawLine(edge0[i].fX, edge0[i].fY, edge0[i+1].fX, edge0[i+1].fY, paint);
-    }
-
-    int row, vertCount = (nu + 1) * (nv + 1);
-    SkAutoTMalloc<SkPoint>  vertStorage(vertCount);
-    SkPoint* verts = vertStorage.get();
-
-    // first row
-    memcpy(verts, edge0, (nu + 1) * sizeof(SkPoint));
-    // rows
-    SkPoint* r = verts;
-    for (row = 1; row < nv; row++) {
-        r += nu + 1;
-        r[0] = edge3[nv - row];
-        for (int col = 1; col < nu; col++) {
-            eval_sheet(edge0, nu, nv, col, row, &r[col]);
-        }
-        r[nu] = edge1[row];
-    }
-    // last row
-    SkPoint* last = verts + nv * (nu + 1);
-    for (i = 0; i <= nu; i++) {
-        last[i] = edge2[nu - i];
-    }
-
-//    canvas->drawPoints(verts, vertCount, paint);
-
-    int stripCount = (nu + 1) * 2;
-    SkAutoTMalloc<SkPoint>  stripStorage(stripCount * 2);
-    SkAutoTMalloc<SkColor>  colorStorage(stripCount);
-    SkPoint* strip = stripStorage.get();
-    SkPoint* tex = strip + stripCount;
-    SkColor* colors = colorStorage.get();
-    SkScalar t = 0;
-    const SkScalar ds = SK_Scalar1 * fW / nu;
-    const SkScalar dt = SK_Scalar1 * fH / nv;
-    r = verts;
-    for (row = 0; row < nv; row++) {
-        SkPoint* upper = r;
-        SkPoint* lower = r + nu + 1;
-        r = lower;
-        SkScalar s = 0;
-        for (i = 0; i <= nu; i++)  {
-            strip[i*2 + 0] = *upper++;
-            strip[i*2 + 1] = *lower++;
-            tex[i*2 + 0].set(s, t);
-            tex[i*2 + 1].set(s, t + dt);
-            colors[i*2 + 0] = make_color(s/fW, t/fH);
-            colors[i*2 + 1] = make_color(s/fW, (t + dt)/fH);
-            s += ds;
-        }
-        t += dt;
-        canvas->drawVertices(SkCanvas::kTriangleStrip_VertexMode, stripCount,
-                             strip, doTextures ? tex : NULL,
-                             doColors ? colors : NULL, NULL,
-                             NULL, 0, paint);
-    }
-}
-
-static void drawpatches(SkCanvas* canvas, const SkPaint& paint, int nu, int nv,
-                        Patch* patch) {
-
-    SkAutoCanvasRestore ar(canvas, true);
-
-    patch->draw(canvas, paint, 10, 10, false, false);
-    canvas->translate(SkIntToScalar(180), 0);
-    patch->draw(canvas, paint, 10, 10, true, false);
-    canvas->translate(SkIntToScalar(180), 0);
-    patch->draw(canvas, paint, 10, 10, false, true);
-    canvas->translate(SkIntToScalar(180), 0);
-    patch->draw(canvas, paint, 10, 10, true, true);
-}
-
-class PatchView : public SampleView {
-    SkShader*   fShader0;
-    SkShader*   fShader1;
-    SkIPoint    fSize0, fSize1;
-    SkPoint     fPts[12];
-
-public:
-	PatchView() {
-        fShader0 = make_shader0(&fSize0);
-        fSize1 = fSize0;
-        if (fSize0.fX == 0 || fSize0.fY == 0) {
-            fSize1.set(2, 2);
-        }
-        fShader1 = make_shader1(fSize1);
-
-        const SkScalar S = SkIntToScalar(50);
-        const SkScalar T = SkIntToScalar(40);
-        fPts[0].set(S*0, T);
-        fPts[1].set(S*1, T);
-        fPts[2].set(S*2, T);
-        fPts[3].set(S*3, T);
-        fPts[4].set(S*3, T*2);
-        fPts[5].set(S*3, T*3);
-        fPts[6].set(S*3, T*4);
-        fPts[7].set(S*2, T*4);
-        fPts[8].set(S*1, T*4);
-        fPts[9].set(S*0, T*4);
-        fPts[10].set(S*0, T*3);
-        fPts[11].set(S*0, T*2);
-
-        this->setBGColor(SK_ColorGRAY);
-    }
-
-    virtual ~PatchView() {
-        SkSafeUnref(fShader0);
-        SkSafeUnref(fShader1);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt))
-        {
-            SkString str("Patch");
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setDither(true);
-        paint.setFilterBitmap(true);
-
-        canvas->translate(SkIntToScalar(20), 0);
-
-        Patch   patch;
-
-        paint.setShader(fShader0);
-        if (fSize0.fX == 0) {
-            fSize0.fX = 1;
-        }
-        if (fSize0.fY == 0) {
-            fSize0.fY = 1;
-        }
-        patch.setBounds(fSize0.fX, fSize0.fY);
-
-        patch.setPatch(fPts);
-        drawpatches(canvas, paint, 10, 10, &patch);
-
-        paint.setShader(NULL);
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(SkIntToScalar(5));
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, SK_ARRAY_COUNT(fPts), fPts, paint);
-
-        canvas->translate(0, SkIntToScalar(300));
-
-        paint.setAntiAlias(false);
-        paint.setShader(fShader1);
-        patch.setBounds(fSize1.fX, fSize1.fY);
-        drawpatches(canvas, paint, 10, 10, &patch);
-    }
-
-    class PtClick : public Click {
-    public:
-        int fIndex;
-        PtClick(SkView* view, int index) : Click(view), fIndex(index) {}
-    };
-
-    static bool hittest(const SkPoint& pt, SkScalar x, SkScalar y) {
-        return SkPoint::Length(pt.fX - x, pt.fY - y) < SkIntToScalar(5);
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fPts); i++) {
-            if (hittest(fPts[i], x, y)) {
-                return new PtClick(this, i);
-            }
-        }
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-
-    virtual bool onClick(Click* click) {
-        fPts[((PtClick*)click)->fIndex].set(click->fCurr.fX, click->fCurr.fY);
-        this->inval(NULL);
-        return true;
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PatchView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePath.cpp b/samplecode/SamplePath.cpp
deleted file mode 100644
index ddfbb71..0000000
--- a/samplecode/SamplePath.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkParsePath.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkGeometry.h"
-
-// http://code.google.com/p/skia/issues/detail?id=32
-static void test_cubic() {
-    SkPoint src[4] = {
-        { 556.25000f, 523.03003f },
-        { 556.23999f, 522.96002f },
-        { 556.21997f, 522.89001f },
-        { 556.21997f, 522.82001f }
-    };
-    SkPoint dst[11];
-    dst[10].set(42, -42);   // one past the end, that we don't clobber these
-    SkScalar tval[] = { 0.33333334f, 0.99999994f };
-
-    SkChopCubicAt(src, dst, tval, 2);
-
-#if 0
-    for (int i = 0; i < 11; i++) {
-        SkDebugf("--- %d [%g %g]\n", i, dst[i].fX, dst[i].fY);
-    }
-#endif
-}
-
-static void test_cubic2() {
-    const char* str = "M2242 -590088L-377758 9.94099e+07L-377758 9.94099e+07L2242 -590088Z";
-    SkPath path;
-    SkParsePath::FromSVGString(str, &path);
-    
-    {
-#ifdef SK_BUILD_FOR_WIN
-        // windows doesn't have strtof
-        float x = (float)strtod("9.94099e+07", NULL);
-#else
-        float x = strtof("9.94099e+07", NULL);
-#endif
-        int ix = (int)x;
-        int fx = (int)(x * 65536);
-        int ffx = SkScalarToFixed(x);
-        printf("%g %x %x %x\n", x, ix, fx, ffx);
-        
-        SkRect r = path.getBounds();
-        SkIRect ir;
-        r.round(&ir);
-        printf("[%g %g %g %g] [%x %x %x %x]\n",
-               SkScalarToDouble(r.fLeft), SkScalarToDouble(r.fTop),
-               SkScalarToDouble(r.fRight), SkScalarToDouble(r.fBottom),
-               ir.fLeft, ir.fTop, ir.fRight, ir.fBottom);
-    }
-    
-    SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 300, 200);
-    bitmap.allocPixels();
-
-    SkCanvas canvas(bitmap);
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    canvas.drawPath(path, paint);
-}
-
-class PathView : public SampleView {
-public:
-    int fDStroke, fStroke, fMinStroke, fMaxStroke;
-    SkPath fPath[6];
-    bool fShowHairline;
-    
-	PathView() {
-        test_cubic();
-        test_cubic2();
-
-        fShowHairline = false;
-        
-        fDStroke = 1;
-        fStroke = 10;
-        fMinStroke = 10;
-        fMaxStroke = 180;
-
-        const int V = 85;
-        
-        fPath[0].moveTo(SkIntToScalar(40), SkIntToScalar(70));
-        fPath[0].lineTo(SkIntToScalar(70), SkIntToScalar(70) + SK_Scalar1/1);
-        fPath[0].lineTo(SkIntToScalar(110), SkIntToScalar(70));
-        
-        fPath[1].moveTo(SkIntToScalar(40), SkIntToScalar(70));
-        fPath[1].lineTo(SkIntToScalar(70), SkIntToScalar(70) - SK_Scalar1/1);
-        fPath[1].lineTo(SkIntToScalar(110), SkIntToScalar(70));
-        
-        fPath[2].moveTo(SkIntToScalar(V), SkIntToScalar(V));
-        fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(V));
-        fPath[2].lineTo(SkIntToScalar(50), SkIntToScalar(50));
-        
-        fPath[3].moveTo(SkIntToScalar(50), SkIntToScalar(50));
-        fPath[3].lineTo(SkIntToScalar(50), SkIntToScalar(V));
-        fPath[3].lineTo(SkIntToScalar(V), SkIntToScalar(V));
-        
-        fPath[4].moveTo(SkIntToScalar(50), SkIntToScalar(50));
-        fPath[4].lineTo(SkIntToScalar(50), SkIntToScalar(V));
-        fPath[4].lineTo(SkIntToScalar(52), SkIntToScalar(50));
-        
-        fPath[5].moveTo(SkIntToScalar(52), SkIntToScalar(50));
-        fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(V));
-        fPath[5].lineTo(SkIntToScalar(50), SkIntToScalar(50));
-        
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-    void nextStroke() {
-        fStroke += fDStroke;
-        if (fStroke > fMaxStroke || fStroke < fMinStroke)
-            fDStroke = -fDStroke;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Paths");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawPath(SkCanvas* canvas, const SkPath& path, SkPaint::Join j) {
-        SkPaint paint;
-        
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeJoin(j);
-        paint.setStrokeWidth(SkIntToScalar(fStroke));
-
-        if (fShowHairline) {
-            SkPath  fill;
-            
-            paint.getFillPath(path, &fill);            
-            paint.setStrokeWidth(0);
-            canvas->drawPath(fill, paint);
-        } else {
-            canvas->drawPath(path, paint);
-        }
-        
-        paint.setColor(SK_ColorRED);
-        paint.setStrokeWidth(0);
-        canvas->drawPath(path, paint);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {        
-        canvas->translate(SkIntToScalar(50), SkIntToScalar(50));
-
-        static const SkPaint::Join gJoins[] = {
-            SkPaint::kBevel_Join,
-            SkPaint::kMiter_Join,
-            SkPaint::kRound_Join
-        };
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gJoins); i++) {
-            canvas->save();
-            for (size_t j = 0; j < SK_ARRAY_COUNT(fPath); j++) {
-                this->drawPath(canvas, fPath[j], gJoins[i]);
-                canvas->translate(SkIntToScalar(200), 0);
-            }
-            canvas->restore();
-            
-            canvas->translate(0, SkIntToScalar(200));
-        }
-        
-        this->nextStroke();
-        this->inval(NULL);
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fShowHairline = !fShowHairline;
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PathView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePathClip.cpp b/samplecode/SamplePathClip.cpp
deleted file mode 100644
index 5ca39e8..0000000
--- a/samplecode/SamplePathClip.cpp
+++ /dev/null
@@ -1,91 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-class PathClipView : public SampleView {
-public:
-    SkRect fOval;
-    SkPoint fCenter;
-
-	PathClipView() {
-        fOval.set(0, 0, SkIntToScalar(200), SkIntToScalar(50));
-        fCenter.set(SkIntToScalar(250), SkIntToScalar(250));
-        
-//        test_ats();
-    }
-    
-    virtual ~PathClipView() {}
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "PathClip");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkRect oval = fOval;
-        oval.offset(fCenter.fX - oval.centerX(), fCenter.fY - oval.centerY());
-        
-        SkPaint p;
-        p.setAntiAlias(true);
-        
-        p.setStyle(SkPaint::kStroke_Style);
-        canvas->drawOval(oval, p);
-
-        SkRect r;
-        r.set(SkIntToScalar(200), SkIntToScalar(200),
-              SkIntToScalar(300), SkIntToScalar(300));
-        canvas->clipRect(r);
-        
-        p.setStyle(SkPaint::kFill_Style);
-        p.setColor(SK_ColorRED);
-        canvas->drawRect(r, p);
-     
-        p.setColor(0x800000FF);
-        r.set(SkIntToScalar(150), SkIntToScalar(10),
-              SkIntToScalar(250), SkIntToScalar(400));
-        canvas->drawOval(oval, p);
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        return new Click(this);
-    }
-        
-    virtual bool onClick(Click* click) {
-        fCenter.set(click->fCurr.fX, click->fCurr.fY);
-        this->inval(NULL);
-        return NULL;
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PathClipView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePathEffects.cpp b/samplecode/SamplePathEffects.cpp
deleted file mode 100644
index 1317b82..0000000
--- a/samplecode/SamplePathEffects.cpp
+++ /dev/null
@@ -1,191 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "Sk1DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkColorPriv.h"
-#include "SkPixelXorXfermode.h"
-
-#define CORNER_RADIUS   12
-static SkScalar gPhase;
-
-static const int gXY[] = {
-    4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
-};
-
-static SkPathEffect* make_pe(int flags) {
-    if (flags == 1)
-        return new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS));
-
-    SkPath  path;
-    path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
-    for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
-        path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
-    path.close();
-    path.offset(SkIntToScalar(-6), 0);
-
-    SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), gPhase, SkPath1DPathEffect::kRotate_Style);
-    
-    if (flags == 2)
-        return outer;
-
-    SkPathEffect* inner = new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS));
-
-    SkPathEffect* pe = new SkComposePathEffect(outer, inner);
-    outer->unref();
-    inner->unref();
-    return pe;
-}
-
-static SkPathEffect* make_warp_pe() {
-    SkPath  path;
-    path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
-    for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
-        path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
-    path.close();
-    path.offset(SkIntToScalar(-6), 0);
-
-    SkPathEffect* outer = new SkPath1DPathEffect(path, SkIntToScalar(12), gPhase, SkPath1DPathEffect::kMorph_Style);
-    SkPathEffect* inner = new SkCornerPathEffect(SkIntToScalar(CORNER_RADIUS));
-
-    SkPathEffect* pe = new SkComposePathEffect(outer, inner);
-    outer->unref();
-    inner->unref();
-    return pe;
-}
-
-///////////////////////////////////////////////////////////
-
-#include "SkColorFilter.h"
-#include "SkLayerRasterizer.h"
-
-class testrast : public SkLayerRasterizer {
-public:
-    testrast() {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-
-#if 0        
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SK_Scalar1*4);
-        this->addLayer(paint);
-    
-        paint.setStrokeWidth(SK_Scalar1*1);
-        paint.setXfermode(SkXfermode::kClear_Mode);
-        this->addLayer(paint);
-#else
-        paint.setAlpha(0x66);
-        this->addLayer(paint, SkIntToScalar(4), SkIntToScalar(4));
-    
-        paint.setAlpha(0xFF);
-        this->addLayer(paint);
-#endif
-    }
-};
-
-class PathEffectView : public SampleView {
-    SkPath  fPath;
-    SkPoint fClickPt;
-public:
-	PathEffectView() {
-        SkRandom    rand;
-        int         steps = 20;
-        SkScalar    dist = SkIntToScalar(400);
-        SkScalar    x = SkIntToScalar(20);
-        SkScalar    y = SkIntToScalar(50);
-        
-        fPath.moveTo(x, y);
-        for (int i = 0; i < steps; i++) {
-            x += dist/steps;
-            SkScalar tmpY = y + SkIntToScalar(rand.nextS() % 25);
-            if (i == steps/2) {
-                fPath.moveTo(x, tmpY);
-            } else {
-                fPath.lineTo(x, tmpY);
-            }
-        }
-
-        {
-            SkRect  oval;
-            oval.set(SkIntToScalar(20), SkIntToScalar(30),
-                     SkIntToScalar(100), SkIntToScalar(60));
-            oval.offset(x, 0);
-            fPath.addRoundRect(oval, SkIntToScalar(8), SkIntToScalar(8));
-        }
-        
-        fClickPt.set(SkIntToScalar(200), SkIntToScalar(200));
-        
-        this->setBGColor(0xFFDDDDDD);
-    }
-	
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "PathEffects");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        gPhase -= SampleCode::GetAnimSecondsDelta() * 40;
-        this->inval(NULL);
-        
-        SkPaint paint;
-        
-#if 0
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkIntToScalar(5));
-        canvas->drawPath(fPath, paint);
-        paint.setStrokeWidth(0);
-        
-        paint.setColor(SK_ColorWHITE);
-        paint.setPathEffect(make_pe(1))->unref();
-        canvas->drawPath(fPath, paint);
-#endif
-        
-        canvas->translate(0, SkIntToScalar(50));
-        
-        paint.setColor(SK_ColorBLUE);
-        paint.setPathEffect(make_pe(2))->unref();
-        canvas->drawPath(fPath, paint);
-        
-        canvas->translate(0, SkIntToScalar(50));
-        
-        paint.setARGB(0xFF, 0, 0xBB, 0);
-        paint.setPathEffect(make_pe(3))->unref();
-        canvas->drawPath(fPath, paint);
-        
-        canvas->translate(0, SkIntToScalar(50));
-
-        paint.setARGB(0xFF, 0, 0, 0);
-        paint.setPathEffect(make_warp_pe())->unref();
-        paint.setRasterizer(new testrast)->unref();
-        canvas->drawPath(fPath, paint);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PathEffectView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePathFill.cpp b/samplecode/SamplePathFill.cpp
deleted file mode 100644
index bcc00d3..0000000
--- a/samplecode/SamplePathFill.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkRandom.h"
-#include "SkBlurDrawLooper.h"
-#include "SkGradientShader.h"
-
-typedef SkScalar (*MakePathProc)(SkPath*);
-
-static SkScalar make_frame(SkPath* path) {
-    SkRect r = { 10, 10, 630, 470 };
-    path->addRoundRect(r, 15, 15);
-
-    SkPaint paint;
-    paint.setStyle(SkPaint::kStroke_Style);
-    paint.setStrokeWidth(5);
-    paint.getFillPath(*path, path);
-    return 15;
-}
-
-static SkScalar make_triangle(SkPath* path) {
-    static const int gCoord[] = {
-        10, 20, 15, 5, 30, 30
-    };
-    path->moveTo(SkIntToScalar(gCoord[0]), SkIntToScalar(gCoord[1]));
-    path->lineTo(SkIntToScalar(gCoord[2]), SkIntToScalar(gCoord[3]));
-    path->lineTo(SkIntToScalar(gCoord[4]), SkIntToScalar(gCoord[5]));
-    path->close();
-    path->offset(10, 0);
-    return SkIntToScalar(30);
-}
-
-static SkScalar make_rect(SkPath* path) {
-    SkRect r = { 10, 10, 30, 30 };
-    path->addRect(r);
-    path->offset(10, 0);
-    return SkIntToScalar(30);
-}
-
-static SkScalar make_oval(SkPath* path) {
-    SkRect r = { 10, 10, 30, 30 };
-    path->addOval(r);
-    path->offset(10, 0);
-    return SkIntToScalar(30);
-}
-
-static SkScalar make_sawtooth(SkPath* path) {
-    SkScalar x = SkIntToScalar(20);
-    SkScalar y = SkIntToScalar(20);
-    const SkScalar x0 = x;
-    const SkScalar dx = SK_Scalar1 * 5;
-    const SkScalar dy = SK_Scalar1 * 10;
-    
-    path->moveTo(x, y);
-    for (int i = 0; i < 32; i++) {
-        x += dx;
-        path->lineTo(x, y - dy);
-        x += dx;
-        path->lineTo(x, y + dy);
-    }
-    path->lineTo(x, y + 2 * dy);
-    path->lineTo(x0, y + 2 * dy);
-    path->close();
-    return SkIntToScalar(30);
-}
-
-static SkScalar make_star(SkPath* path, int n) {
-    const SkScalar c = SkIntToScalar(45);
-    const SkScalar r = SkIntToScalar(20);
-
-    SkScalar rad = -SK_ScalarPI / 2;
-    const SkScalar drad = (n >> 1) * SK_ScalarPI * 2 / n;
-
-    path->moveTo(c, c - r);
-    for (int i = 1; i < n; i++) {
-        rad += drad;
-        SkScalar cosV, sinV = SkScalarSinCos(rad, &cosV);
-        path->lineTo(c + SkScalarMul(cosV, r), c + SkScalarMul(sinV, r));
-    }
-    path->close();
-    return r * 2 * 6 / 5;
-}
-
-static SkScalar make_star_5(SkPath* path) { return make_star(path, 5); }
-static SkScalar make_star_13(SkPath* path) { return make_star(path, 13); }
-
-static const MakePathProc gProcs[] = {
-    make_frame,
-    make_triangle,
-    make_rect,
-    make_oval,
-    make_sawtooth,
-    make_star_5,
-    make_star_13
-};
-
-#define N   SK_ARRAY_COUNT(gProcs)
-
-class PathFillView : public SampleView {
-    SkPath  fPath[N];
-    SkScalar fDY[N];
-
-public:
-    PathFillView() {
-        for (size_t i = 0; i < N; i++) {
-            fDY[i] = gProcs[i](&fPath[i]);
-        }
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "PathFill");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-
-        for (size_t i = 0; i < N; i++) {
-            canvas->drawPath(fPath[i], paint);
-            canvas->translate(0, fDY[i]);
-        }
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PathFillView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePicture.cpp b/samplecode/SamplePicture.cpp
deleted file mode 100644
index d2c9d65..0000000
--- a/samplecode/SamplePicture.cpp
+++ /dev/null
@@ -1,197 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkDumpCanvas.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkPicture.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-#include "SkStream.h"
-#include "SkXMLParser.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkImageRef_GlobalPool.h"
-
-static SkBitmap load_bitmap() {
-    SkStream* stream = new SkFILEStream("/skimages/sesame_street_ensemble-hp.jpg");
-    SkAutoUnref aur(stream);
-    
-    SkBitmap bm;
-    if (SkImageDecoder::DecodeStream(stream, &bm, SkBitmap::kNo_Config,
-                                     SkImageDecoder::kDecodeBounds_Mode)) {
-        SkPixelRef* pr = new SkImageRef_GlobalPool(stream, bm.config(), 1);
-        bm.setPixelRef(pr)->unref();
-    }
-    return bm;
-}
-
-static void drawCircle(SkCanvas* canvas, int r, SkColor color) {
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setColor(color);
-
-    canvas->drawCircle(SkIntToScalar(r), SkIntToScalar(r), SkIntToScalar(r),
-                       paint);
-}
-
-class PictureView : public SampleView {
-    SkBitmap fBitmap;
-public:
-	PictureView() {
-        SkImageRef_GlobalPool::SetRAMBudget(16 * 1024);
-
-        fBitmap = load_bitmap();
-
-        fPicture = new SkPicture;
-        SkCanvas* canvas = fPicture->beginRecording(100, 100);
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        
-        canvas->drawBitmap(fBitmap, 0, 0, NULL);
-
-        drawCircle(canvas, 50, SK_ColorBLACK);
-        fSubPicture = new SkPicture;
-        canvas->drawPicture(*fSubPicture);
-        canvas->translate(SkIntToScalar(50), 0);
-        canvas->drawPicture(*fSubPicture);
-        canvas->translate(0, SkIntToScalar(50));
-        canvas->drawPicture(*fSubPicture);
-        canvas->translate(SkIntToScalar(-50), 0);
-        canvas->drawPicture(*fSubPicture);
-        // fPicture now has (4) references to us. We can release ours, and just
-        // unref fPicture in our destructor, and it will in turn take care of
-        // the other references to fSubPicture
-        fSubPicture->unref();
-    }
-    
-    virtual ~PictureView() {
-        fPicture->unref();
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Picture");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawSomething(SkCanvas* canvas) {
-        SkPaint paint;
-
-        canvas->save();
-        canvas->scale(0.5f, 0.5f);
-        canvas->drawBitmap(fBitmap, 0, 0, NULL);
-        canvas->restore();
-
-        const char beforeStr[] = "before circle";
-        const char afterStr[] = "after circle";
-
-        paint.setAntiAlias(true);
-    
-        paint.setColor(SK_ColorRED);
-        canvas->drawData(beforeStr, sizeof(beforeStr));
-        canvas->drawCircle(SkIntToScalar(50), SkIntToScalar(50),
-                           SkIntToScalar(40), paint);
-        canvas->drawData(afterStr, sizeof(afterStr));
-        paint.setColor(SK_ColorBLACK);
-        paint.setTextSize(SkIntToScalar(40));
-        canvas->drawText("Picture", 7, SkIntToScalar(50), SkIntToScalar(62),
-                         paint);
-        
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        drawSomething(canvas);
-
-        SkPicture* pict = new SkPicture;
-        SkAutoUnref aur(pict);
-
-        drawSomething(pict->beginRecording(100, 100));
-        pict->endRecording();
-    
-        canvas->save();
-        canvas->translate(SkIntToScalar(300), SkIntToScalar(50));
-        canvas->scale(-SK_Scalar1, -SK_Scalar1);
-        canvas->translate(-SkIntToScalar(100), -SkIntToScalar(50));
-        canvas->drawPicture(*pict);
-        canvas->restore();
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(200), SkIntToScalar(150));
-        canvas->scale(SK_Scalar1, -SK_Scalar1);
-        canvas->translate(0, -SkIntToScalar(50));
-        canvas->drawPicture(*pict);
-        canvas->restore();
-        
-        canvas->save();
-        canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
-        canvas->scale(-SK_Scalar1, SK_Scalar1);
-        canvas->translate(-SkIntToScalar(100), 0);
-        canvas->drawPicture(*pict);
-        canvas->restore();
-
-        if (false) {
-            SkDebugfDumper dumper;
-            SkDumpCanvas dumpCanvas(&dumper);
-            dumpCanvas.drawPicture(*pict);
-        }
-        
-        // test that we can re-record a subpicture, and see the results
-        
-        SkRandom rand(SampleCode::GetAnimTime());
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(250));
-        drawCircle(fSubPicture->beginRecording(50, 50), 25,
-                   rand.nextU() | 0xFF000000);
-        canvas->drawPicture(*fPicture);
-        delayInval(500);
-    }
-    
-private:
-    #define INVAL_ALL_TYPE  "inval-all"
-    
-    void delayInval(SkMSec delay) {
-        (new SkEvent(INVAL_ALL_TYPE, this->getSinkID()))->postDelay(delay);
-    }
-    
-    virtual bool onEvent(const SkEvent& evt) {
-        if (evt.isType(INVAL_ALL_TYPE)) {
-            this->inval(NULL);
-            return true;
-        }
-        return this->INHERITED::onEvent(evt);
-    }
-
-    SkPicture*  fPicture;
-    SkPicture*  fSubPicture;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PictureView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePoints.cpp b/samplecode/SamplePoints.cpp
deleted file mode 100644
index 717cd8c..0000000
--- a/samplecode/SamplePoints.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-#include "SkStream.h"
-#include "SkXMLParser.h"
-
-class PointsView : public SampleView {
-public:
-	PointsView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Points");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) {
-        for (size_t i = 0; i < n; i++)
-            pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SK_Scalar1, SK_Scalar1);
-
-        SkRandom rand;
-        SkPaint  p0, p1, p2, p3;
-        const size_t n = 99;
-
-        p0.setColor(SK_ColorRED);
-        p1.setColor(SK_ColorGREEN);
-        p2.setColor(SK_ColorBLUE);
-        p3.setColor(SK_ColorWHITE);
-
-        p0.setStrokeWidth(SkIntToScalar(4));
-        p2.setStrokeCap(SkPaint::kRound_Cap);
-        p2.setStrokeWidth(SkIntToScalar(6));
-
-        SkPoint* pts = new SkPoint[n];
-        fill_pts(pts, n, &rand);
-
-        canvas->drawPoints(SkCanvas::kPolygon_PointMode, n, pts, p0);
-        canvas->drawPoints(SkCanvas::kLines_PointMode, n, pts, p1);
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, p2);
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, p3);
-
-        delete[] pts;
-    }
-
-private:
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PointsView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SamplePolyToPoly.cpp b/samplecode/SamplePolyToPoly.cpp
deleted file mode 100644
index dbda4da..0000000
--- a/samplecode/SamplePolyToPoly.cpp
+++ /dev/null
@@ -1,169 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGraphics.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkTime.h"
-
-extern bool SkSetPoly3To3(SkMatrix* matrix, const SkPoint src[3], const SkPoint dst[3]);
-
-class PolyToPolyView : public SampleView {
-public:
-	PolyToPolyView() {
-        // tests
-        {
-            SkPoint src[] = { { 0, 0 },
-                              { SK_Scalar1, 0 },
-                              { 0, SK_Scalar1 } };
-            SkPoint dst[] = { { 0, 0 },
-                              { 2*SK_Scalar1, 0 },
-                              { 0, 2*SK_Scalar1 } };
-            SkMatrix m1, m2;
-            bool success;
-
-            success = m1.setPolyToPoly(src, dst, 3);
-
-            m2.reset();
-            m2.set(SkMatrix::kMScaleX, dst[1].fX - dst[0].fX);
-            m2.set(SkMatrix::kMSkewX,  dst[2].fX - dst[0].fX);
-            m2.set(SkMatrix::kMTransX, dst[0].fX);
-            m2.set(SkMatrix::kMSkewY,  dst[1].fY - dst[0].fY);
-            m2.set(SkMatrix::kMScaleY, dst[2].fY - dst[0].fY);
-            m2.set(SkMatrix::kMTransY, dst[0].fY);
-
-            m1.reset();
-
-            const SkScalar src1[] = {
-                0, 0, 0, SkFloatToScalar(427), SkFloatToScalar(316), SkFloatToScalar(427), SkFloatToScalar(316), 0
-            };
-            const SkScalar dst1[] = {
-                SkFloatToScalar(158), SkFloatToScalar(177.5f), SkFloatToScalar(158), SkFloatToScalar(249.5f),
-                SkFloatToScalar(158), SkFloatToScalar(604.5f), SkFloatToScalar(158), SkFloatToScalar(-177.5f)
-            };
-
-            success = m2.setPolyToPoly((const SkPoint*)src1, (SkPoint*)dst1, 4);
-
-            {
-                const SkPoint src[] = {
-                    { SkIntToScalar(1), SkIntToScalar(0) },
-                    { SkIntToScalar(4), SkIntToScalar(7) },
-                    { SkIntToScalar(10), SkIntToScalar(2) }
-                };
-                const SkPoint dst[] = {
-                    { SkIntToScalar(4), SkIntToScalar(2) },
-                    { SkIntToScalar(45), SkIntToScalar(26) },
-                    { SkIntToScalar(32), SkIntToScalar(17) }
-                };
-
-                SkMatrix m0;
-                m0.setPolyToPoly(src, dst, 3);
-              //  SkMatrix m1;
-              //  SkSetPoly3To3(&m1, src, dst);
-              //  m0.dump();
-              //  m1.dump();
-            }
-        }
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString str("PolyToPolyView");
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    static void doDraw(SkCanvas* canvas, SkPaint* paint, const int isrc[],
-                       const int idst[], int count) {
-        SkMatrix matrix;
-        SkPoint src[4], dst[4];
-
-        for (int i = 0; i < count; i++) {
-            src[i].set(SkIntToScalar(isrc[2*i+0]), SkIntToScalar(isrc[2*i+1]));
-            dst[i].set(SkIntToScalar(idst[2*i+0]), SkIntToScalar(idst[2*i+1]));
-        }
-
-        canvas->save();
-        matrix.setPolyToPoly(src, dst, count);
-        canvas->concat(matrix);
-
-        paint->setColor(SK_ColorGRAY);
-        paint->setStyle(SkPaint::kStroke_Style);
-        const SkScalar D = SkIntToScalar(64);
-        canvas->drawRectCoords(0, 0, D, D, *paint);
-        canvas->drawLine(0, 0, D, D, *paint);
-        canvas->drawLine(0, D, D, 0, *paint);
-
-        SkPaint::FontMetrics fm;
-        paint->getFontMetrics(&fm);
-        paint->setColor(SK_ColorRED);
-        paint->setStyle(SkPaint::kFill_Style);
-        SkScalar x = D/2;
-        float y = D/2 - (fm.fAscent + fm.fDescent)/2;
-        SkString str;
-        str.appendS32(count);
-        canvas->drawText(str.c_str(), str.size(), x, y, *paint);
-
-        canvas->restore();
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStrokeWidth(SkIntToScalar(4));
-        paint.setTextSize(SkIntToScalar(40));
-        paint.setTextAlign(SkPaint::kCenter_Align);
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-        // translate (1 point)
-        const int src1[] = { 0, 0 };
-        const int dst1[] = { 5, 5 };
-        doDraw(canvas, &paint, src1, dst1, 1);
-        canvas->restore();
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(160), SkIntToScalar(10));
-        // rotate/uniform-scale (2 points)
-        const int src2[] = { 32, 32, 64, 32 };
-        const int dst2[] = { 32, 32, 64, 48 };
-        doDraw(canvas, &paint, src2, dst2, 2);
-        canvas->restore();
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(110));
-        // rotate/skew (3 points)
-        const int src3[] = { 0, 0, 64, 0, 0, 64 };
-        const int dst3[] = { 0, 0, 96, 0, 24, 64 };
-        doDraw(canvas, &paint, src3, dst3, 3);
-        canvas->restore();
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(160), SkIntToScalar(110));
-        // perspective (4 points)
-        const int src4[] = { 0, 0, 64, 0, 64, 64, 0, 64 };
-        const int dst4[] = { 0, 0, 96, 0, 64, 96, 0, 64 };
-        doDraw(canvas, &paint, src4, dst4, 4);
-        canvas->restore();
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new PolyToPolyView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleRegion.cpp b/samplecode/SampleRegion.cpp
deleted file mode 100644
index 48153ef..0000000
--- a/samplecode/SampleRegion.cpp
+++ /dev/null
@@ -1,280 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkImageDecoder.h"
-
-#ifdef SK_BUILD_FOR_WIN
-// windows doesn't have roundf
-inline float roundf(float x) { return (x-floor(x))>0.5 ? ceil(x) : floor(x); }
-#endif
-
-#ifdef SK_DEBUG
-static void make_rgn(SkRegion* rgn, int left, int top, int right, int bottom,
-                     size_t count, int32_t runs[]) {
-    SkIRect r;
-    r.set(left, top, right, bottom);
-    
-    rgn->debugSetRuns(runs, count);
-    SkASSERT(rgn->getBounds() == r);
-}
-
-static void test_union_bug_1505668(SkRegion* ra, SkRegion* rb, SkRegion* rc) {
-    static int32_t dataA[] = {
-        0x00000001, 0x000001dd,
-        0x00000001, 0x0000000c, 0x0000000d, 0x00000025,
-        0x7fffffff, 0x000001de, 0x00000001, 0x00000025,
-        0x7fffffff, 0x000004b3, 0x00000001, 0x00000026,
-        0x7fffffff, 0x000004b4, 0x0000000c, 0x00000026,
-        0x7fffffff, 0x00000579, 0x00000000, 0x0000013a,
-        0x7fffffff, 0x000005d8, 0x00000000, 0x0000013b,
-        0x7fffffff, 0x7fffffff
-    };
-    make_rgn(ra, 0, 1, 315, 1496, SK_ARRAY_COUNT(dataA), dataA);
-
-    static int32_t dataB[] = {
-        0x000000b6, 0x000000c4,
-        0x000000a1, 0x000000f0, 0x7fffffff, 0x000000d6,
-        0x7fffffff, 0x000000e4, 0x00000070, 0x00000079,
-        0x000000a1, 0x000000b0, 0x7fffffff, 0x000000e6,
-        0x7fffffff, 0x000000f4, 0x00000070, 0x00000079,
-        0x000000a1, 0x000000b0, 0x7fffffff, 0x000000f6,
-        0x7fffffff, 0x00000104, 0x000000a1, 0x000000b0,
-        0x7fffffff, 0x7fffffff
-    };
-    make_rgn(rb, 112, 182, 240, 260, SK_ARRAY_COUNT(dataB), dataB);
-    
-    rc->op(*ra, *rb, SkRegion::kUnion_Op);
-}
-#endif
-
-static void scale_rect(SkIRect* dst, const SkIRect& src, float scale) {
-    dst->fLeft = (int)::roundf(src.fLeft * scale);
-    dst->fTop = (int)::roundf(src.fTop * scale);
-    dst->fRight = (int)::roundf(src.fRight * scale);
-    dst->fBottom = (int)::roundf(src.fBottom * scale);
-}
-
-static void scale_rgn(SkRegion* dst, const SkRegion& src, float scale) {
-    SkRegion tmp;
-    SkRegion::Iterator iter(src);
-
-    for (; !iter.done(); iter.next()) {
-        SkIRect r;
-        scale_rect(&r, iter.rect(), scale);
-        tmp.op(r, SkRegion::kUnion_Op);
-    }
-    dst->swap(tmp);
-}
-
-static void paint_rgn(SkCanvas* canvas, const SkRegion& rgn,
-                      const SkPaint& paint) {
-    SkRegion scaled;
-    scale_rgn(&scaled, rgn, 0.5f);
-    
-    SkRegion::Iterator  iter(rgn);
-
-    for (; !iter.done(); iter.next())
-    {
-        SkRect    r;
-        r.set(iter.rect());
-        canvas->drawRect(r, paint);
-    }
-}
-
-class RegionView : public SampleView {
-public:
-	RegionView() {
-        fBase.set(100, 100, 150, 150);
-        fRect = fBase;
-        fRect.inset(5, 5);
-        fRect.offset(25, 25);
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-    void build_rgn(SkRegion* rgn, SkRegion::Op op) {
-        rgn->setRect(fBase);
-        SkIRect r = fBase;
-        r.offset(75, 20);
-        rgn->op(r, SkRegion::kUnion_Op);
-        rgn->op(fRect, op);
-    }
-
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Regions");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawOrig(SkCanvas* canvas, bool bg) {
-        SkRect      r;
-        SkPaint     paint;
-        
-        paint.setStyle(SkPaint::kStroke_Style);
-        if (bg)
-            paint.setColor(0xFFBBBBBB);
-        
-        r.set(fBase);
-        canvas->drawRect(r, paint);
-        r.set(fRect);
-        canvas->drawRect(r, paint);
-    }
-    
-    void drawRgnOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
-        SkRegion    rgn;
-
-        this->build_rgn(&rgn, op);
-        
-        {
-            SkRegion tmp, tmp2(rgn);
-            
-            tmp = tmp2;
-            tmp.translate(5, -3);
-            
-            {
-                char    buffer[1000];
-                size_t  size = tmp.flatten(NULL);
-                SkASSERT(size <= sizeof(buffer));
-                size_t  size2 = tmp.flatten(buffer);
-                SkASSERT(size == size2);
-                
-                SkRegion    tmp3;
-                size2 = tmp3.unflatten(buffer);
-                SkASSERT(size == size2);
-                
-                SkASSERT(tmp3 == tmp);
-            }
-
-            rgn.translate(20, 30, &tmp);
-            SkASSERT(rgn.isEmpty() || tmp != rgn);
-            tmp.translate(-20, -30);
-            SkASSERT(tmp == rgn);
-        }
-
-        this->drawOrig(canvas, true);
-
-        SkPaint paint;
-        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
-        paint_rgn(canvas, rgn, paint);
-
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setColor(color);
-        paint_rgn(canvas, rgn, paint);
-    }
-    
-    void drawPathOped(SkCanvas* canvas, SkRegion::Op op, SkColor color) {
-        SkRegion    rgn;
-        SkPath      path;
-
-        this->build_rgn(&rgn, op);
-        rgn.getBoundaryPath(&path);
-
-        this->drawOrig(canvas, true);
-
-        SkPaint paint;
-
-        paint.setStyle(SkPaint::kFill_Style);
-        paint.setColor((color & ~(0xFF << 24)) | (0x44 << 24));
-        canvas->drawPath(path, paint);
-        paint.setColor(color);
-        paint.setStyle(SkPaint::kStroke_Style);
-        canvas->drawPath(path, paint);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-#ifdef SK_DEBUG
-        if (true) {
-            SkRegion a, b, c;
-            test_union_bug_1505668(&a, &b, &c);
-            
-            if (false) {    // draw the result of the test
-                SkPaint paint;
-                
-                canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-                paint.setColor(SK_ColorRED);
-                paint_rgn(canvas, a, paint);
-                paint.setColor(0x800000FF);
-                paint_rgn(canvas, b, paint);
-                paint.setColor(SK_ColorBLACK);
-                paint.setStyle(SkPaint::kStroke_Style);
-             //   paint_rgn(canvas, c, paint);
-                return;
-            }
-        }
-#endif
-
-        static const struct {
-            SkColor         fColor;
-            const char*     fName;
-            SkRegion::Op    fOp;
-        } gOps[] = {
-            { SK_ColorBLACK,    "Difference",   SkRegion::kDifference_Op    },
-            { SK_ColorRED,      "Intersect",    SkRegion::kIntersect_Op     },
-            { 0xFF008800,       "Union",        SkRegion::kUnion_Op         },
-            { SK_ColorBLUE,     "XOR",          SkRegion::kXOR_Op           }
-        };
-
-        SkPaint textPaint;
-        textPaint.setAntiAlias(true);
-        textPaint.setTextSize(SK_Scalar1*24);
-
-        this->drawOrig(canvas, false);
-        canvas->save();
-            canvas->translate(SkIntToScalar(200), 0);
-            this->drawRgnOped(canvas, SkRegion::kUnion_Op, SK_ColorBLACK);
-        canvas->restore();
-        
-        canvas->translate(0, SkIntToScalar(200));
-
-        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); op++) {
-            canvas->drawText(gOps[op].fName, strlen(gOps[op].fName), SkIntToScalar(75), SkIntToScalar(50), textPaint);
-
-            this->drawRgnOped(canvas, gOps[op].fOp, gOps[op].fColor);
-
-            canvas->save();
-            canvas->translate(0, SkIntToScalar(200));
-            this->drawPathOped(canvas, gOps[op].fOp, gOps[op].fColor);
-            canvas->restore();
-            
-            canvas->translate(SkIntToScalar(200), 0);
-        }
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        return fRect.contains(SkScalarRound(x), SkScalarRound(y)) ? new Click(this) : NULL;
-    }
-    
-    virtual bool onClick(Click* click) {
-        fRect.offset(click->fICurr.fX - click->fIPrev.fX,
-                     click->fICurr.fY - click->fIPrev.fY);
-        this->inval(NULL);
-        return true;
-    }
-    
-private:
-    SkIRect    fBase, fRect;
-    
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new RegionView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleRepeatTile.cpp b/samplecode/SampleRepeatTile.cpp
deleted file mode 100644
index 16a23ad..0000000
--- a/samplecode/SampleRepeatTile.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkShader.h"
-#include "SkKey.h"
-
-static void make_bitmap(SkBitmap* bm) {
-    const int W = 100;
-    const int H = 100;
-    bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
-    bm->allocPixels();
-
-    SkPaint paint;
-    SkCanvas canvas(*bm);
-    canvas.drawColor(SK_ColorWHITE);
-
-    const SkColor colors[] = {
-        SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
-    };
-
-    for (int ix = 0; ix < W; ix += 1) {
-        SkScalar x = SkIntToScalar(ix) + SK_ScalarHalf;
-        paint.setColor(colors[ix & 3]);
-        canvas.drawLine(x, 0, x, SkIntToScalar(H - 1), paint);
-    }
-    paint.setColor(SK_ColorGRAY);
-    canvas.drawLine(0, 0, SkIntToScalar(W), 0, paint);
-}
-
-static void make_paint(SkPaint* paint, SkShader::TileMode tm) {
-    SkBitmap bm;
-    make_bitmap(&bm);
-
-    SkShader* shader = SkShader::CreateBitmapShader(bm, tm, tm);    
-    paint->setShader(shader)->unref();
-}
-
-class RepeatTileView : public SampleView {
-public:
-	RepeatTileView() {
-        this->setBGColor(SK_ColorGRAY);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "RepeatTile");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        make_paint(&paint, SkShader::kRepeat_TileMode);
-        
-//        canvas->scale(SK_Scalar1*2, SK_Scalar1);
-        canvas->translate(SkIntToScalar(100), SkIntToScalar(100));
-        canvas->drawPaint(paint);
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-
-	virtual bool handleKey(SkKey key) {
-        this->inval(NULL);
-        return true;
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new RepeatTileView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleShaderText.cpp b/samplecode/SampleShaderText.cpp
deleted file mode 100644
index bed4835..0000000
--- a/samplecode/SampleShaderText.cpp
+++ /dev/null
@@ -1,216 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkUnitMappers.h"
-
-static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
-    bm->setConfig(config, w, h);
-    bm->allocPixels();
-    bm->eraseColor(0);
-
-    SkCanvas    canvas(*bm);
-    SkScalar s = SkIntToScalar(w < h ? w : h);
-    SkPoint     pts[] = { { 0, 0 }, { s, s } };
-    SkColor     colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
-    SkScalar    pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
-    SkPaint     paint;
-
-    SkUnitMapper*   um = NULL;
-
-    um = new SkCosineMapper;
-
-    SkAutoUnref au(um);
-
-    paint.setDither(true);
-    paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos,
-                SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref();
-    canvas.drawPaint(paint);
-}
-
-SkShader* MakeBitmapShader(SkShader::TileMode tx, SkShader::TileMode ty,
-                           int w, int h) {
-    static SkBitmap bmp;
-    if (bmp.isNull()) {
-        makebm(&bmp, SkBitmap::kARGB_8888_Config, w/2, h/4);
-    }
-    return SkShader::CreateBitmapShader(bmp, tx, ty);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-struct GradData {
-    int             fCount;
-    const SkColor*  fColors;
-    const SkScalar* fPos;
-};
-
-static const SkColor gColors[] = {
-    SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
-};
-
-static const GradData gGradData[] = {
-    { 2, gColors, NULL },
-    { 5, gColors, NULL },
-};
-
-static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
-    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
-                                          data.fCount, tm, mapper);
-}
-
-static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
-    SkPoint center;
-    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
-               SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
-                                          data.fPos, data.fCount, tm, mapper);
-}
-
-static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
-                           SkShader::TileMode tm, SkUnitMapper* mapper) {
-    SkPoint center;
-    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
-               SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
-                                         data.fPos, data.fCount, mapper);
-}
-
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
-                           SkShader::TileMode tm, SkUnitMapper* mapper) {
-    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, mapper);
-}
-
-typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
-                     SkShader::TileMode tm, SkUnitMapper* mapper);
-static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class ShaderTextView : public SampleView {
-public:
-	ShaderTextView() {
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Shader Text");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        const char text[] = "Shaded Text";
-        const int textLen = SK_ARRAY_COUNT(text) - 1;
-        static int pointSize = 36;
-
-        int w = pointSize * textLen;
-        int h = pointSize;
-
-        SkPoint pts[2] = {
-            { 0, 0 },
-            { SkIntToScalar(w), SkIntToScalar(h) }
-        };
-        SkScalar textBase = SkIntToScalar(h/2);
-
-        SkShader::TileMode tileModes[] = {
-            SkShader::kClamp_TileMode,
-            SkShader::kRepeat_TileMode,
-            SkShader::kMirror_TileMode
-        };
-
-        static const int gradCount = SK_ARRAY_COUNT(gGradData) *
-                                     SK_ARRAY_COUNT(gGradMakers);
-        static const int bmpCount = SK_ARRAY_COUNT(tileModes) *
-                                    SK_ARRAY_COUNT(tileModes);
-        SkShader* shaders[gradCount + bmpCount];
-
-        int shdIdx = 0;
-        for (size_t d = 0; d < SK_ARRAY_COUNT(gGradData); ++d) {
-            for (size_t m = 0; m < SK_ARRAY_COUNT(gGradMakers); ++m) {
-                shaders[shdIdx++] = gGradMakers[m](pts,
-                                                   gGradData[d],
-                                                   SkShader::kClamp_TileMode,
-                                                   NULL);
-            }
-        }
-        for (size_t tx = 0; tx < SK_ARRAY_COUNT(tileModes); ++tx) {
-            for (size_t ty = 0; ty < SK_ARRAY_COUNT(tileModes); ++ty) {
-                shaders[shdIdx++] = MakeBitmapShader(tileModes[tx],
-                                                     tileModes[ty],
-                                                     w/8, h);
-            }
-        }
-
-        SkPaint paint;
-        paint.setDither(true);
-        paint.setAntiAlias(true);
-        paint.setTextSize(SkIntToScalar(pointSize));
-
-        canvas->save();
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(10));
-
-        SkPath path;
-        path.arcTo(SkRect::MakeXYWH(SkIntToScalar(-40), SkIntToScalar(15),
-                                    SkIntToScalar(300), SkIntToScalar(90)), 
-                                    SkIntToScalar(225), SkIntToScalar(90), 
-                                    false);
-        path.close();
-
-        static const int testsPerCol = 8;
-        static const int rowHeight = 60;
-        static const int colWidth = 300;
-        canvas->save();
-        for (size_t s = 0; s < SK_ARRAY_COUNT(shaders); s++) {
-            canvas->save();
-            int i = 2*s;
-            canvas->translate(SkIntToScalar((i / testsPerCol) * colWidth),
-                              SkIntToScalar((i % testsPerCol) * rowHeight));
-            paint.setShader(shaders[s])->unref();
-            canvas->drawText(text, textLen, 0, textBase, paint);
-            canvas->restore();
-            canvas->save();
-            ++i;
-            canvas->translate(SkIntToScalar((i / testsPerCol) * colWidth),
-                              SkIntToScalar((i % testsPerCol) * rowHeight));
-            canvas->drawTextOnPath(text, textLen, path, NULL, paint);
-            canvas->restore();
-        }
-        canvas->restore();
-
-        canvas->translate(0, SkIntToScalar(370));
-        this->inval(NULL);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ShaderTextView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleShaders.cpp b/samplecode/SampleShaders.cpp
deleted file mode 100644
index 99cd680..0000000
--- a/samplecode/SampleShaders.cpp
+++ /dev/null
@@ -1,141 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkComposeShader.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTransparentShader.h"
-#include "SkTypeface.h"
-
-static SkShader* make_bitmapfade(const SkBitmap& bm)
-{
-    SkPoint pts[2];
-    SkColor colors[2];
-
-    pts[0].set(0, 0);
-    pts[1].set(0, SkIntToScalar(bm.height()));
-    colors[0] = SK_ColorBLACK;
-    colors[1] = SkColorSetARGB(0, 0, 0, 0);
-    SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
-
-    SkShader* shaderB = SkShader::CreateBitmapShader(bm,
-                        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
-
-    SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
-
-    SkShader* shader = new SkComposeShader(shaderB, shaderA, mode);
-    shaderA->unref();
-    shaderB->unref();
-    mode->unref();
-
-    return shader;
-}
-
-class ShaderView : public SampleView {
-public:
-    SkShader*   fShader;
-    SkBitmap    fBitmap;
-
-	ShaderView() {
-        SkImageDecoder::DecodeFile("/skimages/logo.gif", &fBitmap);
-
-        SkPoint pts[2];
-        SkColor colors[2];
-
-        pts[0].set(0, 0);
-        pts[1].set(SkIntToScalar(100), 0);
-        colors[0] = SK_ColorRED;
-        colors[1] = SK_ColorBLUE;
-        SkShader* shaderA = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
-
-        pts[0].set(0, 0);
-        pts[1].set(0, SkIntToScalar(100));
-        colors[0] = SK_ColorBLACK;
-        colors[1] = SkColorSetARGB(0x80, 0, 0, 0);
-        SkShader* shaderB = SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kClamp_TileMode);
-
-        SkXfermode* mode = SkXfermode::Create(SkXfermode::kDstIn_Mode);
-
-        fShader = new SkComposeShader(shaderA, shaderB, mode);
-        shaderA->unref();
-        shaderB->unref();
-        mode->unref();
-    }
-    virtual ~ShaderView() {
-        SkSafeUnref(fShader);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Shaders");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->drawBitmap(fBitmap, 0, 0);
-
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(120));
-
-        SkPaint paint;
-        SkRect  r;
-
-        paint.setColor(SK_ColorGREEN);
-        canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
-        paint.setShader(fShader);
-        canvas->drawRectCoords(0, 0, SkIntToScalar(100), SkIntToScalar(100), paint);
-
-        canvas->translate(SkIntToScalar(110), 0);
-
-        int w = fBitmap.width();
-        int h = fBitmap.height();
-        w = 120;
-        h = 80;
-        r.set(0, 0, SkIntToScalar(w), SkIntToScalar(h));
-
-        paint.setShader(NULL);
-        canvas->drawRect(r, paint);
-        paint.setShader(make_bitmapfade(fBitmap))->unref();
-        canvas->drawRect(r, paint);
-
-        paint.setShader(new SkTransparentShader)->unref();
-        canvas->drawRect(r, paint);
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ShaderView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleShapes.cpp b/samplecode/SampleShapes.cpp
deleted file mode 100644
index 5a5bb4c..0000000
--- a/samplecode/SampleShapes.cpp
+++ /dev/null
@@ -1,165 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkPicture.h"
-#include "SkStream.h"
-#include "SkView.h"
-
-#define DO_AA   true
-
-#include "SkRectShape.h"
-#include "SkGroupShape.h"
-
-static SkRect make_rect(int l, int t, int r, int b) {
-    SkRect rect;
-    rect.set(SkIntToScalar(l), SkIntToScalar(t),
-             SkIntToScalar(r), SkIntToScalar(b));
-    return rect;
-}
-
-static SkShape* make_shape0(bool red) {
-    SkRectShape* s = new SkRectShape;
-    s->setRect(make_rect(10, 10, 90, 90));
-    if (red) {
-        s->paint().setColor(SK_ColorRED);
-    }
-    s->paint().setAntiAlias(DO_AA);
-    return s;
-}
-
-static SkShape* make_shape1() {
-    SkRectShape* s = new SkRectShape;
-    s->setOval(make_rect(10, 10, 90, 90));
-    s->paint().setColor(SK_ColorBLUE);
-    s->paint().setAntiAlias(DO_AA);
-    return s;
-}
-
-static SkShape* make_shape2() {
-    SkRectShape* s = new SkRectShape;
-    s->setRRect(make_rect(10, 10, 90, 90),
-                SkIntToScalar(20), SkIntToScalar(20));
-    s->paint().setColor(SK_ColorGREEN);
-    s->paint().setAntiAlias(DO_AA);
-    return s;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class ShapesView : public SampleView {
-    SkGroupShape fGroup;
-    SkMatrixRef*    fMatrixRefs[4];
-public:
-	ShapesView() {
-        SkMatrix m;
-        fGroup.appendShape(make_shape0(false))->unref();
-        m.setRotate(SkIntToScalar(30), SkIntToScalar(50), SkIntToScalar(50));
-        m.postTranslate(0, SkIntToScalar(120));
-        fGroup.appendShape(make_shape0(true), m)->unref();
-
-        m.setTranslate(SkIntToScalar(120), 0);
-        fGroup.appendShape(make_shape1(), m)->unref();
-        m.postTranslate(0, SkIntToScalar(120));
-        fGroup.appendShape(make_shape2(), m)->unref();
-        
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
-            SkSafeRef(fMatrixRefs[i] = fGroup.getShapeMatrixRef(i));
-        }
-
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-    virtual ~ShapesView() {
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fMatrixRefs); i++) {
-            SkSafeUnref(fMatrixRefs[i]);
-        }
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Shapes");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawpicture(SkCanvas* canvas, SkPicture& pict) {
-#if 0
-        SkDynamicMemoryWStream ostream;
-        pict.serialize(&ostream);
-
-        SkMemoryStream istream(ostream.getStream(), ostream.getOffset());
-        SkPicture* newPict = new SkPicture(&istream);
-        canvas->drawPicture(*newPict);
-        newPict->unref();
-#else
-        canvas->drawPicture(pict);
-#endif
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkScalar angle = SampleCode::GetAnimScalar(SkIntToScalar(180),
-                                                   SkIntToScalar(360));
-
-        SkMatrix saveM = *fMatrixRefs[3];
-        SkScalar c = SkIntToScalar(50);
-        fMatrixRefs[3]->preRotate(angle, c, c);
-        
-        const SkScalar dx = 350;
-        const SkScalar dy = 500;
-        const int N = 1;
-        for (int v = -N; v <= N; v++) {
-            for (int h = -N; h <= N; h++) {
-                SkAutoCanvasRestore acr(canvas, true);
-                canvas->translate(h * dx, v * dy);
-        
-        SkMatrix matrix;
-     
-        SkGroupShape* gs = new SkGroupShape;
-        SkAutoUnref aur(gs);
-        gs->appendShape(&fGroup);
-        matrix.setScale(-SK_Scalar1, SK_Scalar1);
-        matrix.postTranslate(SkIntToScalar(220), SkIntToScalar(240));
-        gs->appendShape(&fGroup, matrix);
-        matrix.setTranslate(SkIntToScalar(240), 0);
-        matrix.preScale(SK_Scalar1*2, SK_Scalar1*2);
-        gs->appendShape(&fGroup, matrix);
-        
-#if 1
-        SkPicture* pict = new SkPicture;
-        SkCanvas* cv = pict->beginRecording(1000, 1000);
-        cv->scale(SK_ScalarHalf, SK_ScalarHalf);
-        gs->draw(cv);
-        cv->translate(SkIntToScalar(680), SkIntToScalar(480));
-        cv->scale(-SK_Scalar1, SK_Scalar1);
-        gs->draw(cv);
-        pict->endRecording();
-        
-        drawpicture(canvas, *pict);
-        pict->unref();
-#endif
-
-        }}
-
-        *fMatrixRefs[3] = saveM;
-        this->inval(NULL);
-}
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new ShapesView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleSkLayer.cpp b/samplecode/SampleSkLayer.cpp
deleted file mode 100644
index bff6034..0000000
--- a/samplecode/SampleSkLayer.cpp
+++ /dev/null
@@ -1,246 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkView.h"
-#include "SkLayer.h"
-
-#include "SkMatrix44.h"
-static void test_inv(const char label[], const SkMatrix44& mat) {
-    SkDebugf("%s\n", label);
-    mat.dump();
-
-    SkMatrix44 inv;
-    if (mat.invert(&inv)) {
-        inv.dump();
-    } else {
-        SkDebugf("--- invert failed\n");
-    }
-
-    SkMatrix44 a, b;
-    a.setConcat(mat, inv);
-    b.setConcat(inv, mat);
-    SkDebugf("concat mat with inverse pre=%d post=%d\n", a.isIdentity(), b.isIdentity());
-    if (!a.isIdentity()) {
-        a.dump();
-    }
-    if (!b.isIdentity()) {
-        b.dump();
-    }
-    SkDebugf("\n");
-}
-
-static void test_map(SkScalar x0, SkScalar y0, SkScalar z0,
-                     const SkMatrix44& mat,
-                     SkScalar x1, SkScalar y1, SkScalar z1) {
-    SkVector4 src, dst;
-    src.set(x0, y0, z0);
-    dst = mat * src;
-    SkDebugf("map: src: %g %g %g dst: %g %g %g (%g) expected: %g %g %g match: %d\n",
-             x0, y0, z0,
-             dst.fData[0], dst.fData[1], dst.fData[2], dst.fData[3],
-             x1, y1, z1,
-             dst.fData[0] == x1 && dst.fData[1] == y1 && dst.fData[2] == z1);
-}
-
-static void test_33(const SkMatrix44& mat,
-                    SkScalar x0, SkScalar x1, SkScalar x2,
-                    SkScalar y0, SkScalar y1, SkScalar y2) {
-    SkMatrix dst = mat;
-    if (dst[0] != x0 || dst[1] != x1 || dst[2] != x2 ||
-        dst[3] != y0 || dst[4] != y1 || dst[5] != y2) {
-        SkString str;
-        dst.toDumpString(&str);
-        SkDebugf("3x3: expected 3x3 [%g %g %g] [%g %g %g] bug got %s\n",
-                 x0, x1, x2, y0, y1, y2, str.c_str());
-    }
-}
-
-static void test44() {
-    SkMatrix44 m0, m1, m2;
-
-    test_inv("identity", m0);
-    m0.setTranslate(2,3,4);
-    test_inv("translate", m0);
-    m0.setScale(2,3,4);
-    test_inv("scale", m0);
-    m0.postTranslate(5, 6, 7);
-    test_inv("postTranslate", m0);
-    m0.setScale(2,3,4);
-    m1.setTranslate(5, 6, 7);
-    m0.setConcat(m0, m1);
-    test_inv("postTranslate2", m0);
-    m0.setScale(2,3,4);
-    m0.preTranslate(5, 6, 7);
-    test_inv("preTranslate", m0);
-    
-    m0.setScale(2, 4, 6);
-    m0.postScale(SkDoubleToMScalar(0.5));
-    test_inv("scale/postscale to 1,2,3", m0);
-
-    m0.reset();
-    test_map(1, 0, 0, m0, 1, 0, 0);
-    test_map(0, 1, 0, m0, 0, 1, 0);
-    test_map(0, 0, 1, m0, 0, 0, 1);
-    m0.setScale(2, 3, 4);
-    test_map(1, 0, 0, m0, 2, 0, 0);
-    test_map(0, 1, 0, m0, 0, 3, 0);
-    test_map(0, 0, 1, m0, 0, 0, 4);
-    m0.setTranslate(2, 3, 4);
-    test_map(0, 0, 0, m0, 2, 3, 4);
-    m0.preScale(5, 6, 7);
-    test_map(1, 0, 0, m0, 7, 3, 4);
-    test_map(0, 1, 0, m0, 2, 9, 4);
-    test_map(0, 0, 1, m0, 2, 3, 11);
-
-    SkMScalar deg = 45;
-    m0.setRotateDegreesAbout(0, 0, 1, deg);
-    test_map(1, 0, 0, m0, 0.707106769, -0.707106769, 0);
-
-    m0.reset();
-    test_33(m0, 1, 0, 0, 0, 1, 0);
-    m0.setTranslate(3, 4, 5);
-    test_33(m0, 1, 0, 3, 0, 1, 4);
-}
-    
-///////////////////////////////////////////////////////////////////////////////
-
-static void dump_layers(const SkLayer* layer, int tab = 0) {
-    SkMatrix matrix;
-    SkString matrixStr;
-
-    layer->getLocalTransform(&matrix);
-    matrix.toDumpString(&matrixStr);
-
-    for (int j = 0; j < tab; j++) {
-        SkDebugf(" ");
-    }
-    SkDebugf("layer=%p parent=%p size=[%g %g] transform=%s\n",
-             layer, layer->getParent(), layer->getWidth(), layer->getHeight(),
-             matrixStr.c_str());
-    for (int i = 0; i < layer->countChildren(); i++) {
-        dump_layers(layer->getChild(i), tab + 4);
-    }
-}
-
-class TestLayer : public SkLayer {
-public:
-    TestLayer(SkColor c) : fColor(c) {}
-
-protected:
-    virtual void onDraw(SkCanvas* canvas, SkScalar opacity) {
-        SkRect r;
-        r.set(0, 0, this->getWidth(), this->getHeight());
-
-        SkPaint paint;
-        paint.setColor(fColor);
-        paint.setAlpha(SkScalarRound(opacity * 255));
-
-        canvas->drawRect(r, paint);
-    }
-
-private:
-    SkColor fColor;
-};
-
-class SkLayerView : public SkView {
-private:
-    SkLayer* fRootLayer;
-    SkLayer* fLastChild;
-public:
-	SkLayerView() {
-        test44();
-        static const int W = 600;
-        static const int H = 440;
-        static const struct {
-            int fWidth;
-            int fHeight;
-            SkColor fColor;
-            int fPosX;
-            int fPosY;
-        } gData[] = {
-            { 120, 80, SK_ColorRED, 0, 0 },
-            { 120, 80, SK_ColorGREEN, W - 120, 0 },
-            { 120, 80, SK_ColorBLUE, 0, H - 80 },
-            { 120, 80, SK_ColorMAGENTA, W - 120, H - 80 },
-        };
-
-        fRootLayer = new TestLayer(0xFFDDDDDD);
-        fRootLayer->setSize(W, H);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gData); i++) {
-            SkLayer* child = new TestLayer(gData[i].fColor);
-            child->setSize(gData[i].fWidth, gData[i].fHeight);
-            child->setPosition(gData[i].fPosX, gData[i].fPosY);
-            fRootLayer->addChild(child)->unref();
-        }
-        
-        SkLayer* child = new TestLayer(0xFFDD8844);
-        child->setSize(120, 80);
-        child->setPosition(fRootLayer->getWidth()/2 - child->getWidth()/2,
-                           fRootLayer->getHeight()/2 - child->getHeight()/2);
-        child->setAnchorPoint(SK_ScalarHalf, SK_ScalarHalf);
-        {
-            SkMatrix m;
-            m.setRotate(SkIntToScalar(30));
-            child->setMatrix(m);
-        }
-        fLastChild = child;
-        fRootLayer->addChild(child)->unref();
-        
-        if (false) {
-            SkMatrix matrix;
-            matrix.setScale(0.5, 0.5);
-            fRootLayer->setMatrix(matrix);
-        }
-
-//        dump_layers(fRootLayer);
-    }
-    
-    virtual ~SkLayerView() {
-        SkSafeUnref(fRootLayer);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "SkLayer");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorWHITE);
-        
-        canvas->translate(20, 20);
-        fRootLayer->draw(canvas);
-        
-        // visual test of getLocalTransform
-        if (true) {
-            SkMatrix matrix;
-            fLastChild->localToGlobal(&matrix);
-            SkPaint paint;
-            paint.setStyle(SkPaint::kStroke_Style);
-            paint.setStrokeWidth(5);
-            paint.setColor(0x88FF0000);
-            canvas->concat(matrix);
-            canvas->drawRect(SkRect::MakeSize(fLastChild->getSize()), paint);
-        }
-    }
-    
-private:
-    typedef SkView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new SkLayerView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleSlides.cpp b/samplecode/SampleSlides.cpp
deleted file mode 100644
index d39cee0..0000000
--- a/samplecode/SampleSlides.cpp
+++ /dev/null
@@ -1,777 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-
-#define BG_COLOR    0xFFDDDDDD
-
-typedef void (*SlideProc)(SkCanvas*);
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "Sk1DPathEffect.h"
-#include "Sk2DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkDashPathEffect.h"
-#include "SkDiscretePathEffect.h"
-
-static void compose_pe(SkPaint* paint) {
-    SkPathEffect* pe = paint->getPathEffect();
-    SkPathEffect* corner = new SkCornerPathEffect(25);
-    SkPathEffect* compose;
-    if (pe) {
-        compose = new SkComposePathEffect(pe, corner);
-        corner->unref();
-    } else {
-        compose = corner;
-    }
-    paint->setPathEffect(compose)->unref();
-}
-
-static void hair_pe(SkPaint* paint) {
-    paint->setStrokeWidth(0);
-}
-
-static void hair2_pe(SkPaint* paint) {
-    paint->setStrokeWidth(0);
-    compose_pe(paint);
-}
-
-static void stroke_pe(SkPaint* paint) {
-    paint->setStrokeWidth(12);
-    compose_pe(paint);
-}
-
-static void dash_pe(SkPaint* paint) {
-    SkScalar inter[] = { 20, 10, 10, 10 };
-    paint->setStrokeWidth(12);
-    paint->setPathEffect(new SkDashPathEffect(inter, SK_ARRAY_COUNT(inter),
-                                              0))->unref();
-    compose_pe(paint);
-}
-
-static const int gXY[] = {
-4, 0, 0, -4, 8, -4, 12, 0, 8, 4, 0, 4
-};
-
-static void scale(SkPath* path, SkScalar scale) {
-    SkMatrix m;
-    m.setScale(scale, scale);
-    path->transform(m);
-}
-
-static void one_d_pe(SkPaint* paint) {
-    SkPath  path;
-    path.moveTo(SkIntToScalar(gXY[0]), SkIntToScalar(gXY[1]));
-    for (unsigned i = 2; i < SK_ARRAY_COUNT(gXY); i += 2)
-        path.lineTo(SkIntToScalar(gXY[i]), SkIntToScalar(gXY[i+1]));
-    path.close();
-    path.offset(SkIntToScalar(-6), 0);
-    scale(&path, 1.5);
-    
-    paint->setPathEffect(new SkPath1DPathEffect(path, SkIntToScalar(21), 0,
-                                SkPath1DPathEffect::kRotate_Style))->unref();
-    compose_pe(paint);
-}
-
-typedef void (*PE_Proc)(SkPaint*);
-static const PE_Proc gPE[] = { hair_pe, hair2_pe, stroke_pe, dash_pe, one_d_pe };
-
-static void fill_pe(SkPaint* paint) {
-    paint->setStyle(SkPaint::kFill_Style);
-    paint->setPathEffect(NULL);
-}
-
-static void discrete_pe(SkPaint* paint) {
-    paint->setPathEffect(new SkDiscretePathEffect(10, 4))->unref();
-}
-
-static SkPathEffect* MakeTileEffect() {
-    SkMatrix m;
-    m.setScale(SkIntToScalar(12), SkIntToScalar(12));
-
-    SkPath path;
-    path.addCircle(0, 0, SkIntToScalar(5));
-    
-    return new SkPath2DPathEffect(m, path);
-}
-
-static void tile_pe(SkPaint* paint) {
-    paint->setPathEffect(MakeTileEffect())->unref();
-}
-
-static const PE_Proc gPE2[] = { fill_pe, discrete_pe, tile_pe };
-
-static void patheffect_slide(SkCanvas* canvas) {
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setStyle(SkPaint::kStroke_Style);
-
-    SkPath path;
-    path.moveTo(20, 20);
-    path.lineTo(70, 120);
-    path.lineTo(120, 30);
-    path.lineTo(170, 80);
-    path.lineTo(240, 50);
-
-    size_t i;
-    canvas->save();
-    for (i = 0; i < SK_ARRAY_COUNT(gPE); i++) {
-        gPE[i](&paint);
-        canvas->drawPath(path, paint);
-        canvas->translate(0, 75);
-    }
-    canvas->restore();
-
-    path.reset();
-    SkRect r = { 0, 0, 250, 120 };
-    path.addOval(r, SkPath::kCW_Direction);
-    r.inset(50, 50);
-    path.addRect(r, SkPath::kCCW_Direction);
-    
-    canvas->translate(320, 20);
-    for (i = 0; i < SK_ARRAY_COUNT(gPE2); i++) {
-        gPE2[i](&paint);
-        canvas->drawPath(path, paint);
-        canvas->translate(0, 160);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkGradientShader.h"
-
-struct GradData {
-    int             fCount;
-    const SkColor*  fColors;
-    const SkScalar* fPos;
-};
-
-static const SkColor gColors[] = {
-SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE, SK_ColorBLACK
-};
-static const SkScalar gPos0[] = { 0, SK_Scalar1 };
-static const SkScalar gPos1[] = { SK_Scalar1/4, SK_Scalar1*3/4 };
-static const SkScalar gPos2[] = {
-0, SK_Scalar1/8, SK_Scalar1/2, SK_Scalar1*7/8, SK_Scalar1
-};
-
-static const GradData gGradData[] = {
-{ 2, gColors, NULL },
-{ 2, gColors, gPos0 },
-{ 2, gColors, gPos1 },
-{ 5, gColors, NULL },
-{ 5, gColors, gPos2 }
-};
-
-static SkShader* MakeLinear(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
-    return SkGradientShader::CreateLinear(pts, data.fColors, data.fPos,
-                                          data.fCount, tm, mapper);
-}
-
-static SkShader* MakeRadial(const SkPoint pts[2], const GradData& data,
-                            SkShader::TileMode tm, SkUnitMapper* mapper) {
-    SkPoint center;
-    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
-               SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateRadial(center, center.fX, data.fColors,
-                                          data.fPos, data.fCount, tm, mapper);
-}
-
-static SkShader* MakeSweep(const SkPoint pts[2], const GradData& data,
-                           SkShader::TileMode tm, SkUnitMapper* mapper) {
-    SkPoint center;
-    center.set(SkScalarAve(pts[0].fX, pts[1].fX),
-               SkScalarAve(pts[0].fY, pts[1].fY));
-    return SkGradientShader::CreateSweep(center.fX, center.fY, data.fColors,
-                                         data.fPos, data.fCount, mapper);
-}
-
-static SkShader* Make2Radial(const SkPoint pts[2], const GradData& data,
-                             SkShader::TileMode tm, SkUnitMapper* mapper) {
-    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, mapper);
-}
-
-typedef SkShader* (*GradMaker)(const SkPoint pts[2], const GradData& data,
-                               SkShader::TileMode tm, SkUnitMapper* mapper);
-static const GradMaker gGradMakers[] = {
-    MakeLinear, MakeRadial, MakeSweep, Make2Radial
-};
-
-static void gradient_slide(SkCanvas* canvas) {
-    SkPoint pts[2] = {
-        { 0, 0 },
-        { SkIntToScalar(100), SkIntToScalar(100) }
-    };
-    SkShader::TileMode tm = SkShader::kClamp_TileMode;
-    SkRect r = { 0, 0, SkIntToScalar(100), SkIntToScalar(100) };
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setDither(true);
-    
-    canvas->translate(SkIntToScalar(20), SkIntToScalar(10));
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gGradData); i++) {
-        canvas->save();
-        for (size_t j = 0; j < SK_ARRAY_COUNT(gGradMakers); j++) {
-            SkShader* shader = gGradMakers[j](pts, gGradData[i], tm, NULL);
-            paint.setShader(shader);
-            canvas->drawRect(r, paint);
-            shader->unref();
-            canvas->translate(0, SkIntToScalar(120));
-        }
-        canvas->restore();
-        canvas->translate(SkIntToScalar(120), 0);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkPathMeasure.h"
-
-static SkScalar getpathlen(const SkPath& path) {
-    SkPathMeasure   meas(path, false);
-    return meas.getLength();
-}
-
-static void textonpath_slide(SkCanvas* canvas) {
-    const char* text = "Displacement";
-    size_t len =strlen(text);
-    SkPath path;
-    path.moveTo(100, 300);
-    path.quadTo(300, 100, 500, 300);
-    path.offset(0, -100);
-
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setTextSize(40);
-    
-    paint.setStyle(SkPaint::kStroke_Style);
-    canvas->drawPath(path, paint);
-    paint.setStyle(SkPaint::kFill_Style);
-    
-    SkScalar x = 50;
-    paint.setColor(0xFF008800);
-    canvas->drawTextOnPathHV(text, len, path,
-                             x, paint.getTextSize()*2/3, paint);
-    paint.setColor(SK_ColorRED);
-    canvas->drawTextOnPathHV(text, len, path,
-                             x + 60, 0, paint);    
-    paint.setColor(SK_ColorBLUE);
-    canvas->drawTextOnPathHV(text, len, path,
-                             x + 120, -paint.getTextSize()*2/3, paint);
-
-    path.offset(0, 200);
-    paint.setTextAlign(SkPaint::kRight_Align);
-    
-    text = "Matrices";
-    len = strlen(text);
-    SkScalar pathLen = getpathlen(path);
-    SkMatrix matrix;
-    
-    paint.setColor(SK_ColorBLACK);
-    paint.setStyle(SkPaint::kStroke_Style);
-    canvas->drawPath(path, paint);
-    paint.setStyle(SkPaint::kFill_Style);
-
-    paint.setTextSize(50);
-    canvas->drawTextOnPath(text, len, path, NULL, paint);
-    
-    paint.setColor(SK_ColorRED);
-    matrix.setScale(-SK_Scalar1, SK_Scalar1);
-    matrix.postTranslate(pathLen, 0);
-    canvas->drawTextOnPath(text, len, path, &matrix, paint);
-    
-    paint.setColor(SK_ColorBLUE);
-    matrix.setScale(SK_Scalar1, -SK_Scalar1);
-    canvas->drawTextOnPath(text, len, path, &matrix, paint);
-    
-    paint.setColor(0xFF008800);
-    matrix.setScale(-SK_Scalar1, -SK_Scalar1);
-    matrix.postTranslate(pathLen, 0);
-    canvas->drawTextOnPath(text, len, path, &matrix, paint);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkImageDecoder.h"
-#include "SkOSFile.h"
-#include "SkRandom.h"
-#include "SkStream.h"
-
-static SkShader* make_shader0(SkIPoint* size) {
-    SkBitmap    bm;
-    
-    SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm);
-    size->set(bm.width(), bm.height());
-    return SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
-                                        SkShader::kClamp_TileMode);
-}
-
-static SkShader* make_shader1(const SkIPoint& size) {
-    SkPoint pts[] = { { 0, 0 },
-                      { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
-    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
-    return SkGradientShader::CreateLinear(pts, colors, NULL,
-                                          SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
-}
-
-class Rec {
-public:
-    SkCanvas::VertexMode    fMode;
-    int                     fCount;
-    SkPoint*                fVerts;
-    SkPoint*                fTexs;
-    
-    Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {}
-    ~Rec() { delete[] fVerts; delete[] fTexs; }
-};
-
-void make_tris(Rec* rec) {
-    int n = 10;
-    SkRandom    rand;
-    
-    rec->fMode = SkCanvas::kTriangles_VertexMode;
-    rec->fCount = n * 3;
-    rec->fVerts = new SkPoint[rec->fCount];
-    
-    for (int i = 0; i < n; i++) {
-        SkPoint* v = &rec->fVerts[i*3];
-        for (int j = 0; j < 3; j++) {
-            v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250);
-        }
-    }
-}
-
-void make_fan(Rec* rec, int texWidth, int texHeight) {
-    const SkScalar tx = SkIntToScalar(texWidth);
-    const SkScalar ty = SkIntToScalar(texHeight);
-    const int n = 24;
-    
-    rec->fMode = SkCanvas::kTriangleFan_VertexMode;
-    rec->fCount = n + 2;
-    rec->fVerts = new SkPoint[rec->fCount];
-    rec->fTexs  = new SkPoint[rec->fCount];
-    
-    SkPoint* v = rec->fVerts;
-    SkPoint* t = rec->fTexs;
-    
-    v[0].set(0, 0);
-    t[0].set(0, 0);
-    for (int i = 0; i < n; i++) {
-        SkScalar cos;
-        SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
-        v[i+1].set(cos, sin);
-        t[i+1].set(i*tx/n, ty);
-    }
-    v[n+1] = v[1];
-    t[n+1].set(tx, ty);
-    
-    SkMatrix m;
-    m.setScale(SkIntToScalar(100), SkIntToScalar(100));
-    m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
-    m.mapPoints(v, rec->fCount);
-}
-
-void make_strip(Rec* rec, int texWidth, int texHeight) {
-    const SkScalar tx = SkIntToScalar(texWidth);
-    const SkScalar ty = SkIntToScalar(texHeight);
-    const int n = 24;
-    
-    rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
-    rec->fCount = 2 * (n + 1);
-    rec->fVerts = new SkPoint[rec->fCount];
-    rec->fTexs  = new SkPoint[rec->fCount];
-    
-    SkPoint* v = rec->fVerts;
-    SkPoint* t = rec->fTexs;
-    
-    for (int i = 0; i < n; i++) {
-        SkScalar cos;
-        SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
-        v[i*2 + 0].set(cos/2, sin/2);
-        v[i*2 + 1].set(cos, sin);
-        
-        t[i*2 + 0].set(tx * i / n, ty);
-        t[i*2 + 1].set(tx * i / n, 0);
-    }
-    v[2*n + 0] = v[0];
-    v[2*n + 1] = v[1];
-    
-    t[2*n + 0].set(tx, ty);
-    t[2*n + 1].set(tx, 0);
-    
-    SkMatrix m;
-    m.setScale(SkIntToScalar(100), SkIntToScalar(100));
-    m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
-    m.mapPoints(v, rec->fCount);
-}
-
-static void mesh_slide(SkCanvas* canvas) {
-    Rec fRecs[3];
-    SkIPoint    size;
-    
-    SkShader* fShader0 = make_shader0(&size);
-    SkShader* fShader1 = make_shader1(size);
-
-    SkAutoUnref aur0(fShader0);
-    SkAutoUnref aur1(fShader1);
-
-    make_strip(&fRecs[0], size.fX, size.fY);
-    make_fan(&fRecs[1], size.fX, size.fY);
-    make_tris(&fRecs[2]);
-
-    SkPaint paint;
-    paint.setDither(true);
-    paint.setFilterBitmap(true);
-    
-    for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
-        canvas->save();
-        
-        paint.setShader(NULL);
-        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                             fRecs[i].fVerts, fRecs[i].fTexs,
-                             NULL, NULL, NULL, 0, paint);
-        
-        canvas->translate(SkIntToScalar(210), 0);
-        
-        paint.setShader(fShader0);
-        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                             fRecs[i].fVerts, fRecs[i].fTexs,
-                             NULL, NULL, NULL, 0, paint);
-        
-        canvas->translate(SkIntToScalar(210), 0);
-        
-        paint.setShader(fShader1);
-        canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                             fRecs[i].fVerts, fRecs[i].fTexs,
-                             NULL, NULL, NULL, 0, paint);
-        canvas->restore();
-        
-        canvas->translate(0, SkIntToScalar(250));
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkGradientShader.h"
-#include "SkLayerRasterizer.h"
-#include "SkBlurMaskFilter.h"
-
-static void r0(SkLayerRasterizer* rast, SkPaint& p)
-{
-    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
-                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
-    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
-    
-    p.setMaskFilter(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-    
-    p.setAlpha(0x11);
-    p.setStyle(SkPaint::kFill_Style);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    rast->addLayer(p);
-}
-
-static void r1(SkLayerRasterizer* rast, SkPaint& p)
-{
-    rast->addLayer(p);
-    
-    p.setAlpha(0x40);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*2);
-    rast->addLayer(p);
-}
-
-static void r2(SkLayerRasterizer* rast, SkPaint& p)
-{
-    p.setStyle(SkPaint::kStrokeAndFill_Style);
-    p.setStrokeWidth(SK_Scalar1*4);
-    rast->addLayer(p);
-    
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*3/2);
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-}
-
-static void r3(SkLayerRasterizer* rast, SkPaint& p)
-{
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*3);
-    rast->addLayer(p);
-    
-    p.setAlpha(0x20);
-    p.setStyle(SkPaint::kFill_Style);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    rast->addLayer(p);
-}
-
-static void r4(SkLayerRasterizer* rast, SkPaint& p)
-{
-    p.setAlpha(0x60);
-    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
-    
-    p.setAlpha(0xFF);
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
-    
-    p.setXfermode(NULL);
-    rast->addLayer(p);
-}
-
-#include "SkDiscretePathEffect.h"
-
-static void r5(SkLayerRasterizer* rast, SkPaint& p)
-{
-    rast->addLayer(p);
-    
-    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
-    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
-    rast->addLayer(p);
-}
-
-static void r6(SkLayerRasterizer* rast, SkPaint& p)
-{
-    rast->addLayer(p);
-    
-    p.setAntiAlias(false);
-    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
-    r5(rast2, p);
-    p.setRasterizer(rast2)->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-}
-
-#include "Sk2DPathEffect.h"
-
-static SkPathEffect* MakeDotEffect(SkScalar radius, const SkMatrix& matrix) {
-    SkPath path;
-    path.addCircle(0, 0, radius);
-    return new SkPath2DPathEffect(matrix, path);
-}
-
-static void r7(SkLayerRasterizer* rast, SkPaint& p)
-{
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    p.setPathEffect(MakeDotEffect(SK_Scalar1*4, lattice))->unref();
-    rast->addLayer(p);
-}
-
-static void r8(SkLayerRasterizer* rast, SkPaint& p)
-{
-    rast->addLayer(p);
-    
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    p.setPathEffect(MakeDotEffect(SK_Scalar1*2, lattice))->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-    
-    p.setPathEffect(NULL);
-    p.setXfermode(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-}
-
-class Line2DPathEffect : public Sk2DPathEffect {
-public:
-    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
-    : Sk2DPathEffect(matrix), fWidth(width) {}
-    
-	virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width)
-    {
-        if (this->INHERITED::filterPath(dst, src, width))
-        {
-            *width = fWidth;
-            return true;
-        }
-        return false;
-    }
-    
-    virtual Factory getFactory() { return CreateProc; }
-    virtual void flatten(SkFlattenableWriteBuffer& buffer)
-    {
-        this->INHERITED::flatten(buffer);
-        buffer.writeScalar(fWidth);
-    }
-protected:
-	virtual void nextSpan(int u, int v, int ucount, SkPath* dst)
-    {
-        if (ucount > 1)
-        {
-            SkPoint	src[2], dstP[2];
-            
-            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
-                       SkIntToScalar(v) + SK_ScalarHalf);
-            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
-                       SkIntToScalar(v) + SK_ScalarHalf);
-            this->getMatrix().mapPoints(dstP, src, 2);
-            
-            dst->moveTo(dstP[0]);
-            dst->lineTo(dstP[1]);
-        }
-    }
-    
-    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer)
-    {
-        fWidth = buffer.readScalar();
-    }
-    
-private:
-    SkScalar fWidth;
-    
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)
-    {
-        return new Line2DPathEffect(buffer);
-    }
-    
-    typedef Sk2DPathEffect INHERITED;
-};
-
-static void r9(SkLayerRasterizer* rast, SkPaint& p)
-{
-    rast->addLayer(p);
-    
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
-    lattice.postRotate(SkIntToScalar(30), 0, 0);
-    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-    
-    p.setPathEffect(NULL);
-    p.setXfermode(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-}
-
-typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
-
-static const raster_proc gRastProcs[] = {
-    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
-};
-
-static void apply_shader(SkPaint* paint, int index) {    
-    raster_proc proc = gRastProcs[index];
-    SkPaint p;
-    SkLayerRasterizer*  rast = new SkLayerRasterizer;
-    
-    p.setAntiAlias(true);
-    proc(rast, p);
-    paint->setRasterizer(rast)->unref();
-    paint->setColor(SK_ColorBLUE);
-}
-
-#include "SkTypeface.h"
-
-static void texteffect_slide(SkCanvas* canvas) {
-    const char* str = "Google";
-    size_t len = strlen(str);
-    SkScalar x = 20;
-    SkScalar y = 80;
-    SkPaint paint;
-    paint.setTypeface(SkTypeface::CreateFromName("Georgia", SkTypeface::kItalic));
-    paint.setTextSize(75);
-    paint.setAntiAlias(true);
-    paint.setColor(SK_ColorBLUE);
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
-        apply_shader(&paint, i);
-        canvas->drawText(str, len, x, y, paint);
-        y += 80;
-        if (i == 4) {
-            x += 320;
-            y = 80;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkImageEncoder.h"
-
-static const SlideProc gProc[] = {
-    patheffect_slide,
-    gradient_slide,
-    textonpath_slide,
-    mesh_slide,
-    texteffect_slide
-};
-
-class SlideView : public SampleView {
-    int fIndex;
-public:
-    SlideView() {
-        fIndex = 0;
-        
-        SkBitmap bm;
-        bm.setConfig(SkBitmap::kARGB_8888_Config, 1024, 768);
-        bm.allocPixels();
-        SkCanvas canvas(bm);
-        SkScalar s = SkIntToScalar(1024) / 640;
-        canvas.scale(s, s);
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gProc); i++) {
-            canvas.save();
-            canvas.drawColor(BG_COLOR);
-            gProc[i](&canvas);
-            canvas.restore();
-            SkString str;
-            str.printf("/skimages/slide_%d.png", i);
-            SkImageEncoder::EncodeFile(str.c_str(), bm, SkImageEncoder::kPNG_Type, 100);
-        }
-        this->setBGColor(BG_COLOR);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Slides");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        gProc[fIndex](canvas);
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fIndex = (fIndex + 1) % SK_ARRAY_COUNT(gProc);
-        this->inval(NULL);
-        return NULL;
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new SlideView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleSpiral.cpp b/samplecode/SampleSpiral.cpp
deleted file mode 100644
index 89810fa..0000000
--- a/samplecode/SampleSpiral.cpp
+++ /dev/null
@@ -1,63 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-
-class SpiralView : public SampleView {
-public:
-	SpiralView() {
-        this->setBGColor(0xFFDDDDDD);
-	}
-	
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Spiral");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-	
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkScalarHalf(SkIntToScalar(3)));
-        paint.setStyle(SkPaint::kFill_Style);
-        
-        SkRect r;
-        SkScalar l,t,x,y;
-        l = SampleCode::GetAnimScalar(SkIntToScalar(10),
-                                      SkIntToScalar(400));
-        t = SampleCode::GetAnimScalar(SkIntToScalar(5),
-                                      SkIntToScalar(200));
-        
-        canvas->translate(320,240);
-        for (int i = 0; i < 35; i++) {
-            paint.setColor(0xFFF00FF0 - i * 0x04000000);
-            SkScalar step = SK_ScalarPI / (55 - i);
-            SkScalar angle = t * step;
-            x = (20 + SkIntToScalar(i) * 5) * SkScalarSinCos(angle, &y);
-            y *= (20 + SkIntToScalar(i) * 5);
-            r.set(x, y, x + SkIntToScalar(10), y + SkIntToScalar(10));
-            canvas->drawRect(r, paint);
-        }
-
-        this->inval(NULL);
-    }
-	
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new SpiralView; }
-static SkViewRegister reg(MyFactory);
\ No newline at end of file
diff --git a/samplecode/SampleStrokePath.cpp b/samplecode/SampleStrokePath.cpp
deleted file mode 100644
index 1fb5fea..0000000
--- a/samplecode/SampleStrokePath.cpp
+++ /dev/null
@@ -1,224 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkParsePath.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkView.h"
-
-#include "SkBlurMaskFilter.h"
-
-static void test_huge_stroke(SkCanvas* canvas) {
-    SkRect srcR = { 0, 0, 72000, 54000 };
-    SkRect dstR = { 0, 0, 640, 480 };
-    
-    SkPath path;
-    path.moveTo(17600, 8000);
-    path.lineTo(52800, 8000);
-    path.lineTo(52800, 41600);
-    path.lineTo(17600, 41600);
-    path.close();
-    
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setStrokeWidth(8000);
-    paint.setStrokeMiter(10);
-    paint.setStrokeCap(SkPaint::kButt_Cap);
-    paint.setStrokeJoin(SkPaint::kRound_Join);
-    paint.setStyle(SkPaint::kStroke_Style);
-
-    SkMatrix matrix;
-    matrix.setRectToRect(srcR, dstR, SkMatrix::kCenter_ScaleToFit);
-    canvas->concat(matrix);
-
-    canvas->drawPath(path, paint);
-}
-
-#if 0
-#include "SkBlurMask.h"
-static void test_blur() {
-    uint8_t cell[9];
-    memset(cell, 0xFF, sizeof(cell));
-    SkMask src;
-    src.fImage = cell;
-    src.fFormat = SkMask::kA8_Format;
-    SkMask dst;
-
-    for (int y = 1; y <= 3; y++) {
-        for (int x = 1; x <= 3; x++) {
-            src.fBounds.set(0, 0, x, y);
-            src.fRowBytes = src.fBounds.width();
-            
-            SkScalar radius = 1.f;
-
-            printf("src [%d %d %d %d] radius %g\n", src.fBounds.fLeft, src.fBounds.fTop,
-                   src.fBounds.fRight, src.fBounds.fBottom, radius);
-
-            SkBlurMask::Blur(&dst, src, radius, SkBlurMask::kNormal_Style);
-            uint8_t* dstPtr = dst.fImage;
-
-            for (int y = 0; y < dst.fBounds.height(); y++) {
-                for (int x = 0; x < dst.fBounds.width(); x++) {
-                    printf(" %02X", dstPtr[x]);
-                }
-                printf("\n");
-                dstPtr += dst.fRowBytes;
-            }
-        }
-    }
-}
-#endif
-
-static void scale_to_width(SkPath* path, SkScalar dstWidth) {
-    const SkRect& bounds = path->getBounds();
-    SkScalar scale = dstWidth / bounds.width();
-    SkMatrix matrix;
-
-    matrix.setScale(scale, scale);
-    path->transform(matrix);
-}
-
-static const struct {
-    SkPaint::Style  fStyle;
-    SkPaint::Join   fJoin;
-    int             fStrokeWidth;
-} gRec[] = {
-    { SkPaint::kFill_Style,             SkPaint::kMiter_Join,   0 },
-    { SkPaint::kStroke_Style,           SkPaint::kMiter_Join,   0 },
-    { SkPaint::kStroke_Style,           SkPaint::kMiter_Join,   10 },
-    { SkPaint::kStrokeAndFill_Style,    SkPaint::kMiter_Join,   10 },
-};
-
-class StrokePathView : public SampleView {
-    SkScalar    fWidth;
-    SkPath      fPath;
-public:
-	StrokePathView() {
-//        test_blur();
-        fWidth = SkIntToScalar(120);
-
-#if 0
-        const char str[] =
-            "M 0, 3"
-            "C 10, -10, 30, -10, 0, 28"
-            "C -30, -10, -10, -10, 0, 3"
-            "Z";
-        SkParsePath::FromSVGString(str, &fPath);
-#else
-        fPath.addCircle(0, 0, SkIntToScalar(50), SkPath::kCW_Direction);
-        fPath.addCircle(0, SkIntToScalar(-50), SkIntToScalar(30), SkPath::kCW_Direction);
-#endif
-        
-        scale_to_width(&fPath, fWidth);
-        const SkRect& bounds = fPath.getBounds();
-        fPath.offset(-bounds.fLeft, -bounds.fTop);
-
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "StrokePath");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    SkRandom rand;
-    
-    void drawSet(SkCanvas* canvas, SkPaint* paint) {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
-            paint->setStyle(gRec[i].fStyle);
-            paint->setStrokeJoin(gRec[i].fJoin);
-            paint->setStrokeWidth(SkIntToScalar(gRec[i].fStrokeWidth));
-            canvas->drawPath(fPath, *paint);
-            canvas->translate(fWidth * 5 / 4, 0);
-        }
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        test_huge_stroke(canvas); return;
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(10));
-
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        
-        if (true) {
-            canvas->drawColor(SK_ColorBLACK);
-
-            paint.setTextSize(24);
-            paint.setColor(SK_ColorWHITE);
-            canvas->translate(10, 30);
-
-            static const SkBlurMaskFilter::BlurStyle gStyle[] = {
-                SkBlurMaskFilter::kNormal_BlurStyle,
-                SkBlurMaskFilter::kInner_BlurStyle,
-                SkBlurMaskFilter::kOuter_BlurStyle,
-                SkBlurMaskFilter::kSolid_BlurStyle,
-            };
-            for (int x = 0; x < 5; x++) {
-                SkMaskFilter* mf;
-                SkScalar radius = 4;
-                for (int y = 0; y < 10; y++) {
-                    if (x) {
-                        mf = SkBlurMaskFilter::Create(radius, gStyle[x - 1]);
-                        paint.setMaskFilter(mf)->unref();
-                    }
-                    canvas->drawText("Title Bar", 9, x*SkIntToScalar(100), y*SkIntToScalar(30), paint);
-                    radius *= 0.75f;
-                }
-                
-            }
-            return;
-        }
-
-        paint.setColor(SK_ColorBLUE);
-
-#if 1
-        SkPath p;
-        float r = rand.nextUScalar1() + 0.5f;
-        SkScalar x = 0, y = 0;
-        p.moveTo(x, y);
-#if 0
-        p.cubicTo(x-75*r, y+75*r, x-40*r, y+125*r, x, y+85*r);
-        p.cubicTo(x+40*r, y+125*r, x+75*r, y+75*r, x, y);
-#else
-        p.cubicTo(x+75*r, y+75*r, x+40*r, y+125*r, x, y+85*r);
-        p.cubicTo(x-40*r, y+125*r, x-75*r, y+75*r, x, y);
-#endif
-        p.close();
-        fPath = p;
-        fPath.offset(100, 0);
-#endif
-        
-        fPath.setFillType(SkPath::kWinding_FillType);
-        drawSet(canvas, &paint);
-        
-        canvas->translate(0, fPath.getBounds().height() * 5 / 4);
-        fPath.setFillType(SkPath::kEvenOdd_FillType);
-        drawSet(canvas, &paint);
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new StrokePathView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleStrokeRect.cpp b/samplecode/SampleStrokeRect.cpp
deleted file mode 100644
index e9d3c2e..0000000
--- a/samplecode/SampleStrokeRect.cpp
+++ /dev/null
@@ -1,76 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-
-class StrokeRectSample : public SampleView {
-public:
-    StrokeRectSample() {}
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Stroke Rects");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SkIntToScalar(20));
-
-        SkPaint hair;
-        hair.setStyle(SkPaint::kStroke_Style);
-        hair.setColor(SK_ColorRED);
-
-        static const SkISize gSize[] = {
-            {   100,   50 },
-            {   100,    0 },
-            {     0,   50 },
-            {     0,    0 }
-        };
-
-        static const SkPaint::Join gJoin[] = {
-            SkPaint::kMiter_Join,
-            SkPaint::kRound_Join,
-            SkPaint::kBevel_Join
-        };
-
-        canvas->translate(paint.getStrokeWidth(), paint.getStrokeWidth());
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gJoin); ++i) {
-            paint.setStrokeJoin(gJoin[i]);
-
-            canvas->save();
-            for (size_t j = 0; j < SK_ARRAY_COUNT(gSize); ++j) {
-                SkRect r = SkRect::MakeWH(SkIntToScalar(gSize[j].fWidth),
-                                          SkIntToScalar(gSize[j].fHeight));
-                canvas->drawRect(r, paint);
-                canvas->drawRect(r, hair);
-                canvas->translate(0, SkIntToScalar(100));
-            }
-            canvas->restore();
-            canvas->translate(SkIntToScalar(150), 0);
-        }
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new StrokeRectSample; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleStrokeText.cpp b/samplecode/SampleStrokeText.cpp
deleted file mode 100644
index f90dc55..0000000
--- a/samplecode/SampleStrokeText.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-static void lettersToBitmap(SkBitmap* dst, const char chars[],
-                            const SkPaint& original, SkBitmap::Config config) {
-    SkPath path;
-    SkScalar x = 0;
-    SkScalar width;
-    SkPath p;
-    for (size_t i = 0; i < strlen(chars); i++) {
-        original.getTextPath(&chars[i], 1, x, 0, &p);
-        path.addPath(p);
-        original.getTextWidths(&chars[i], 1, &width);
-        x += width;
-    }
-    SkRect bounds = path.getBounds();
-    SkScalar sw = -original.getStrokeWidth();
-    bounds.inset(sw, sw);
-    path.offset(-bounds.fLeft, -bounds.fTop);
-    bounds.offset(-bounds.fLeft, -bounds.fTop);
-    
-    int w = SkScalarRound(bounds.width());
-    int h = SkScalarRound(bounds.height());
-    SkPaint paint(original);
-    SkBitmap src;
-    src.setConfig(config, w, h);
-    src.allocPixels();
-    src.eraseColor(0);
-    {
-        SkCanvas canvas(src);
-        paint.setAntiAlias(true);
-        paint.setColor(SK_ColorBLACK);
-        paint.setStyle(SkPaint::kFill_Style);
-        canvas.drawPath(path, paint);
-    }
-    
-    dst->setConfig(config, w, h);
-    dst->allocPixels();
-    dst->eraseColor(SK_ColorWHITE);
-    {
-        SkCanvas canvas(*dst);
-        paint.setXfermodeMode(SkXfermode::kDstATop_Mode);
-        canvas.drawBitmap(src, 0, 0, &paint);
-        paint.setColor(original.getColor());
-        paint.setStyle(SkPaint::kStroke_Style);
-        canvas.drawPath(path, paint);
-    }
-}
-
-static void lettersToBitmap2(SkBitmap* dst, const char chars[],
-                            const SkPaint& original, SkBitmap::Config config) {
-    SkPath path;
-    SkScalar x = 0;
-    SkScalar width;
-    SkPath p;
-    for (size_t i = 0; i < strlen(chars); i++) {
-        original.getTextPath(&chars[i], 1, x, 0, &p);
-        path.addPath(p);
-        original.getTextWidths(&chars[i], 1, &width);
-        x += width;
-    }
-    SkRect bounds = path.getBounds();
-    SkScalar sw = -original.getStrokeWidth();
-    bounds.inset(sw, sw);
-    path.offset(-bounds.fLeft, -bounds.fTop);
-    bounds.offset(-bounds.fLeft, -bounds.fTop);
-    
-    int w = SkScalarRound(bounds.width());
-    int h = SkScalarRound(bounds.height());
-    SkPaint paint(original);
-
-    paint.setAntiAlias(true);
-    paint.setXfermodeMode(SkXfermode::kDstATop_Mode);
-    paint.setColor(original.getColor());
-    paint.setStyle(SkPaint::kStroke_Style);
-    
-    dst->setConfig(config, w, h);
-    dst->allocPixels();
-    dst->eraseColor(SK_ColorWHITE);
-
-    SkCanvas canvas(*dst);
-    canvas.drawPath(path, paint);
-}
-
-class StrokeTextView : public SampleView {
-    bool fAA;
-public:
-	StrokeTextView() : fAA(false) {
-        this->setBGColor(0xFFCC8844);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "StrokeText");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkBitmap bm;
-        SkPaint paint;
-        
-        paint.setStrokeWidth(SkIntToScalar(6));
-        paint.setTextSize(SkIntToScalar(80));
-//        paint.setTypeface(Typeface.DEFAULT_BOLD);
-        
-        lettersToBitmap(&bm, "Test Case", paint, SkBitmap::kARGB_4444_Config);
-        
-        canvas->drawBitmap(bm, 0, 0);
-    }
-    
-private:
-    
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new StrokeTextView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTests.cpp b/samplecode/SampleTests.cpp
deleted file mode 100644
index 5542bf6..0000000
--- a/samplecode/SampleTests.cpp
+++ /dev/null
@@ -1,118 +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.
- */
-utils#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-
-#include "test.h"
-
-namespace skiatest {
-    
-class MyReporter : public Reporter {
-protected:
-    virtual void onStart(Test* test) {}
-    virtual void onReport(const char desc[], Reporter::Result result) {
-        SkASSERT(Reporter::kPassed == result);
-    }
-    virtual void onEnd(Test* test) {}
-};
-
-class Iter {
-public:
-    Iter(Reporter* r) : fReporter(r) {
-        r->ref();
-        fReg = TestRegistry::Head();
-    }
-    
-    ~Iter() {
-        fReporter->unref();
-    }
-    
-    Test* next() {
-        if (fReg) {
-            TestRegistry::Factory fact = fReg->factory();
-            fReg = fReg->next();
-            Test* test = fact(NULL);
-            test->setReporter(fReporter);
-            return test;
-        }
-        return NULL;
-    }
-    
-    static int Count() {
-        const TestRegistry* reg = TestRegistry::Head();
-        int count = 0;
-        while (reg) {
-            count += 1;
-            reg = reg->next();
-        }
-        return count;
-    }
-    
-private:
-    Reporter* fReporter;
-    const TestRegistry* fReg;
-};
-}
-
-class TestsView : public SkView {
-public:
-	TestsView() {}
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Tests");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorWHITE);
-    }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-
-        skiatest::MyReporter reporter;
-        skiatest::Iter iter(&reporter);
-        skiatest::Test* test;
-        
-        while ((test = iter.next()) != NULL) {
-            test->run();
-            SkDELETE(test);
-        }
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        this->inval(NULL);
-        
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    virtual bool onClick(Click* click) {
-        this->inval(NULL);
-        return this->INHERITED::onClick(click);
-    }
-
-	virtual bool handleKey(SkKey key) {
-        this->inval(NULL);
-        return true;
-    }
-
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TestsView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleText.cpp b/samplecode/SampleText.cpp
deleted file mode 100644
index 6a0eba8..0000000
--- a/samplecode/SampleText.cpp
+++ /dev/null
@@ -1,403 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-#include "SkStream.h"
-#include "SkXMLParser.h"
-
-static const int gKernel[3][3] = {
-//    { -1, -2, -1 }, { -2, 12, -2 }, { -1, -2, -1 }
-    { 1, 2, 1 }, { 2, 64-12, 2 }, { 1, 2, 1 }
-};
-static const int gShift = 6;
-
-class ReduceNoise : public SkKernel33ProcMaskFilter {
-public:
-    ReduceNoise(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
-    virtual uint8_t computeValue(uint8_t* const* srcRows)
-    {
-        int c = srcRows[1][1];
-        int min = 255, max = 0;
-        for (int i = 0; i < 3; i++)
-            for (int j = 0; j < 3; j++)
-                if (i != 1 || j != 1)
-                {
-                    int v = srcRows[i][j];
-                    if (max < v)
-                        max = v;
-                    if  (min > v)
-                        min = v;
-                }
-        if (c > max) c = max;
-    //    if (c < min) c = min;
-        return c;
-    }
-    virtual Factory getFactory() { return Create; }
-private:
-    ReduceNoise(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
-    static SkFlattenable* Create(SkFlattenableReadBuffer& rb) {
-        return new ReduceNoise(rb);
-    }
-};
-
-class Darken : public SkKernel33ProcMaskFilter {
-public:
-    Darken(int percent256) : SkKernel33ProcMaskFilter(percent256) {}
-    virtual uint8_t computeValue(uint8_t* const* srcRows)
-    {
-        int c = srcRows[1][1];
-        float f = c / 255.f;
-
-        if (c >= 0) {
-            f = sqrtf(f);
-        } else {
-            f *= f;
-        }
-        SkASSERT(f >= 0 && f <= 1);
-        return (int)(f * 255);
-    }
-    virtual Factory getFactory() { return Create; }
-private:
-    Darken(SkFlattenableReadBuffer& rb) : SkKernel33ProcMaskFilter(rb) {}
-    static SkFlattenable* Create(SkFlattenableReadBuffer& rb) {
-        return new Darken(rb);
-    }
-};
-
-static SkMaskFilter* makemf() { return new Darken(0x30); }
-
-static void test_breakText() {
-    SkPaint paint;
-    const char* text = "sdfkljAKLDFJKEWkldfjlk#$%&sdfs.dsj";
-    size_t length = strlen(text);
-    SkScalar width = paint.measureText(text, length);
-
-    SkScalar mm = 0;
-    SkScalar nn = 0;
-    for (SkScalar w = 0; w <= width; w += SK_Scalar1) {
-        SkScalar m;
-        size_t n = paint.breakText(text, length, w, &m,
-                                    SkPaint::kBackward_TextBufferDirection);
-
-        SkASSERT(n <= length);
-        SkASSERT(m <= width);
-
-        if (n == 0) {
-            SkASSERT(m == 0);
-        } else {
-            // now assert that we're monotonic
-            if (n == nn) {
-                SkASSERT(m == mm);
-            } else {
-                SkASSERT(n > nn);
-                SkASSERT(m > mm);
-            }
-        }
-        nn = SkIntToScalar(n);
-        mm = m;
-    }
-
-    SkDEBUGCODE(size_t length2 =) paint.breakText(text, length, width, &mm);
-    SkASSERT(length2 == length);
-    SkASSERT(mm == width);
-}
-
-static SkRandom gRand;
-
-class SkPowerMode : public SkXfermode {
-public:
-    SkPowerMode(SkScalar exponent) { this->init(exponent); }
-
-    virtual void xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]);
-
-    typedef SkFlattenable* (*Factory)(SkFlattenableReadBuffer&);
-
-    // overrides for SkFlattenable
-    virtual Factory getFactory() { return Create; }
-    virtual void flatten(SkFlattenableWriteBuffer& b) {
-    //    this->INHERITED::flatten(b);  How can we know if this is legal????
-        b.write32(SkScalarToFixed(fExp));
-    }
-
-private:
-    SkScalar fExp;          // user's value
-    uint8_t fTable[256];    // cache
-
-    void init(SkScalar exponent);
-    SkPowerMode(SkFlattenableReadBuffer& b) : SkXfermode(b) {
-        // read the exponent
-        this->init(SkFixedToScalar(b.readS32()));
-    }
-    static SkFlattenable* Create(SkFlattenableReadBuffer& b) {
-        return SkNEW_ARGS(SkPowerMode, (b));
-    }
-
-    typedef SkXfermode INHERITED;
-};
-
-void SkPowerMode::init(SkScalar e) {
-    fExp = e;
-    float ee = SkScalarToFloat(e);
-
-    printf("------ %g\n", ee);
-    for (int i = 0; i < 256; i++) {
-        float x = i / 255.f;
-     //   printf(" %d %g", i, x);
-        x = powf(x, ee);
-     //   printf(" %g", x);
-        int xx = SkScalarRound(SkFloatToScalar(x * 255));
-     //   printf(" %d\n", xx);
-        fTable[i] = SkToU8(xx);
-    }
-}
-
-void SkPowerMode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                         const SkAlpha aa[]) {
-    for (int i = 0; i < count; i++) {
-        SkPMColor c = src[i];
-        int r = SkGetPackedR32(c);
-        int g = SkGetPackedG32(c);
-        int b = SkGetPackedB32(c);
-        r = fTable[r];
-        g = fTable[g];
-        b = fTable[b];
-        dst[i] = SkPack888ToRGB16(r, g, b);
-    }
-}
-
-static const struct {
-    const char* fName;
-    uint32_t    fFlags;
-    bool        fFlushCache;
-} gHints[] = {
-    { "Linear", SkPaint::kLinearText_Flag,     false },
-    { "Normal",   0,                           true },
-    { "Subpixel", SkPaint::kSubpixelText_Flag, true }
-};
-
-static int count_char_points(const SkPaint& paint, char c) {
-    SkPath  path;
-
-    paint.getTextPath(&c, 1, 0, 0, &path);
-    return path.getPoints(NULL, 0);
-}
-
-static int gOld, gNew, gCount;
-
-static void dump(int c, int oldc, int newc) {
-    if (oldc != newc) {
-        gOld += oldc;
-        gNew += newc;
-        gCount += 1;
-        printf("char %c: old = %3d, new = %3d, reduction %g%%\n", c, oldc, newc, 100. * (oldc - newc) / oldc);
-    }
-}
-
-static void tab(int n) {
-//    printf("[%d] ", n); return;
-    SkASSERT(n >= 0);
-    for (int i = 0; i < n; i++)
-        printf("    ");
-}
-
-static void draw_rgn(const SkRegion& rgn, SkCanvas* canvas, const SkPaint& paint) {
-    SkRect    r;
-    SkRegion::Iterator  iter(rgn);
-
-    for (; !iter.done(); iter.next()) {
-        r.set(iter.rect());
-        canvas->drawRect(r, paint);
-    }
-}
-
-static void test_break(SkCanvas* canvas, const char text[], size_t length,
-                        SkScalar x, SkScalar y, const SkPaint& paint,
-                        SkScalar clickX) {
-    SkPaint linePaint;
-
-    linePaint.setAntiAlias(true);
-
-    SkScalar measured;
-
-    if (paint.breakText(text, length, clickX - x, &measured,
-                        SkPaint::kForward_TextBufferDirection)) {
-        linePaint.setColor(SK_ColorRED);
-        canvas->drawLine(x, y, x + measured, y, linePaint);
-    }
-
-    x += paint.measureText(text, length);
-    if (paint.breakText(text, length, x - clickX, &measured,
-                        SkPaint::kBackward_TextBufferDirection)) {
-        linePaint.setColor(SK_ColorBLUE);
-        canvas->drawLine(x - measured, y, x, y, linePaint);
-    }
-}
-
-static void DrawTheText(SkCanvas* canvas, const char text[], size_t length,
-                        SkScalar x, SkScalar y, const SkPaint& paint,
-                        SkScalar clickX, SkMaskFilter* mf) {
-    SkPaint p(paint);
-
-#if 0
-    canvas->drawText(text, length, x, y, paint);
-#else
-    {
-        SkPoint pts[1000];
-        SkScalar xpos = x;
-        SkASSERT(length <= SK_ARRAY_COUNT(pts));
-        for (size_t i = 0; i < length; i++) {
-            pts[i].set(xpos, y), xpos += paint.getTextSize();
-        }
-        canvas->drawPosText(text, length, pts, paint);
-    }
-#endif
-
-    p.setSubpixelText(true);
-    x += SkIntToScalar(180);
-    canvas->drawText(text, length, x, y, p);
-
-#ifdef SK_DEBUG
-    if (true) {
-    //    p.setMaskFilter(mf);
-        p.setSubpixelText(false);
-        p.setLinearText(true);
-        x += SkIntToScalar(180);
-        canvas->drawText(text, length, x, y, p);
-    }
-#endif
-}
-
-class TextSpeedView : public SampleView {
-public:
-	TextSpeedView() {
-        fMF = makemf();
-
-        fHints = 0;
-        fClickX = 0;
-
-        test_breakText();
-    }
-
-    virtual ~TextSpeedView() {
-        SkSafeUnref(fMF);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Text");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    static void make_textstrip(SkBitmap* bm) {
-        bm->setConfig(SkBitmap::kRGB_565_Config, 200, 18);
-        bm->allocPixels();
-        bm->eraseColor(SK_ColorWHITE);
-
-        SkCanvas    canvas(*bm);
-        SkPaint     paint;
-        const char* s = "Lorem ipsum dolor sit amet, consectetuer adipiscing elit";
-
-        paint.setFlags(paint.getFlags() | SkPaint::kAntiAlias_Flag
-                                        | SkPaint::kDevKernText_Flag);
-        paint.setTextSize(SkIntToScalar(14));
-        canvas.drawText(s, strlen(s), SkIntToScalar(8), SkIntToScalar(14), paint);
-    }
-
-    static void fill_pts(SkPoint pts[], size_t n, SkRandom* rand) {
-        for (size_t i = 0; i < n; i++)
-            pts[i].set(rand->nextUScalar1() * 640, rand->nextUScalar1() * 480);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkAutoCanvasRestore restore(canvas, false);
-        {
-            SkRect r;
-            r.set(0, 0, SkIntToScalar(1000), SkIntToScalar(20));
-       //     canvas->saveLayer(&r, NULL, SkCanvas::kHasAlphaLayer_SaveFlag);
-        }
-
-        SkPaint paint;
-//        const uint16_t glyphs[] = { 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 };
-        int         index = fHints % SK_ARRAY_COUNT(gHints);
-        index = 1;
-//        const char* style = gHints[index].fName;
-
-//        canvas->translate(0, SkIntToScalar(50));
-
-  //      canvas->drawText(style, strlen(style), SkIntToScalar(20), SkIntToScalar(20), paint);
-
-        SkSafeUnref(paint.setTypeface(SkTypeface::CreateFromFile("/skimages/samplefont.ttf")));
-        paint.setAntiAlias(true);
-        paint.setFlags(paint.getFlags() | gHints[index].fFlags);
-
-        SkRect clip;
-        clip.set(SkIntToScalar(25), SkIntToScalar(34), SkIntToScalar(88), SkIntToScalar(155));
-
-        const char* text = "Hamburgefons";
-        size_t length = strlen(text);
-
-        SkScalar y = SkIntToScalar(0);
-        for (int i = 9; i <= 24; i++) {
-            paint.setTextSize(SkIntToScalar(i) /*+ (gRand.nextU() & 0xFFFF)*/);
-            for (SkScalar dx = 0; dx <= SkIntToScalar(3)/4;
-                                            dx += SkIntToScalar(1) /* /4 */) {
-                y += paint.getFontSpacing();
-                DrawTheText(canvas, text, length, SkIntToScalar(20) + dx, y,
-                            paint, fClickX, fMF);
-            }
-        }
-        if (gHints[index].fFlushCache) {
-//                SkGraphics::SetFontCacheUsed(0);
-        }
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fClickX = x;
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-
-private:
-    int fHints;
-    SkScalar fClickX;
-    SkMaskFilter* fMF;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TextSpeedView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTextAlpha.cpp b/samplecode/SampleTextAlpha.cpp
deleted file mode 100644
index 09134c4..0000000
--- a/samplecode/SampleTextAlpha.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkBlurMaskFilter.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkOSFile.h"
-#include "SkStream.h"
-
-static void check_for_nonwhite(const SkBitmap& bm, int alpha) {
-    if (bm.config() != SkBitmap::kRGB_565_Config) {
-        return;
-    }
-    
-    for (int y = 0; y < bm.height(); y++) {
-        for (int x = 0; x < bm.width(); x++) {
-            uint16_t c = *bm.getAddr16(x, y);
-            if (c != 0xFFFF) {
-                SkDebugf("------ nonwhite alpha=%x [%d %d] %x\n", alpha, x, y, c);
-                return;
-            }
-        }
-    }
-}
-
-class TextAlphaView : public SampleView {
-public:    
-	TextAlphaView() {
-        fByte = 0xFF;
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString str("TextAlpha");
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        const char* str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
-        SkPaint paint;
-        SkScalar    x = SkIntToScalar(10);
-        SkScalar    y = SkIntToScalar(20);
-        
-        paint.setFlags(0x105);
-        
-        paint.setARGB(fByte, 0xFF, 0xFF, 0xFF);
-        
-        paint.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
-                                        SkBlurMaskFilter::kNormal_BlurStyle));
-        paint.getMaskFilter()->unref();
-        
-        SkRandom rand;
-        
-        for (int ps = 6; ps <= 35; ps++) {
-            paint.setColor(rand.nextU() | (0xFF << 24));
-            paint.setTextSize(SkIntToScalar(ps));
-            paint.setTextSize(SkIntToScalar(24));
-            canvas->drawText(str, strlen(str), x, y, paint);
-            y += paint.getFontMetrics(NULL);
-        }
-        //check_for_nonwhite(canvas->getDevice()->accessBitmap(), fByte);
-        //SkDebugf("------ byte %x\n", fByte);
-
-        if (false) {
-            fByte += 1;
-            fByte &= 0xFF;
-            this->inval(NULL);
-        }
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        return new Click(this);
-    }
-    
-    virtual bool onClick(Click* click) {
-        int y = click->fICurr.fY;
-        if (y < 0) {
-            y = 0;
-        } else if (y > 255) {
-            y = 255;
-        }
-        fByte = y;
-        this->inval(NULL);
-        return true;
-    }
-    
-private:
-    int fByte;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TextAlphaView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTextBox.cpp b/samplecode/SampleTextBox.cpp
deleted file mode 100644
index 5f37380..0000000
--- a/samplecode/SampleTextBox.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkBlurMaskFilter.h"
-#include "SkCanvas.h"
-#include "SkColorShader.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkTextBox.h"
-#include "SkOSFile.h"
-#include "SkStream.h"
-#include "SkKey.h"
-
-extern void skia_set_text_gamma(float blackGamma, float whiteGamma);
-
-#ifdef SK_BUILD_FOR_WIN
-extern SkTypeface* SkCreateTypefaceFromLOGFONT(const LOGFONT&);
-#endif
-
-static const char gText[] =
-	"When in the Course of human events it becomes necessary for one people "
-	"to dissolve the political bands which have connected them with another "
-	"and to assume among the powers of the earth, the separate and equal "
-	"station to which the Laws of Nature and of Nature's God entitle them, "
-	"a decent respect to the opinions of mankind requires that they should "
-	"declare the causes which impel them to the separation.";
-
-class TextBoxView : public SampleView {
-public:
-	TextBoxView() {
-#ifdef SK_BUILD_FOR_WIN
-		LOGFONT lf;
-		sk_bzero(&lf, sizeof(lf));
-		lf.lfHeight = 9;
-		SkTypeface* tf0 = SkCreateTypefaceFromLOGFONT(lf);
-		lf.lfHeight = 12;
-		SkTypeface* tf1 = SkCreateTypefaceFromLOGFONT(lf);
-		// we assert that different sizes should not affect which face we get
-		SkASSERT(tf0 == tf1);
-		tf0->unref();
-		tf1->unref();
-#endif
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString str("TextBox");
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    void drawTest(SkCanvas* canvas, SkScalar w, SkScalar h, SkColor fg, SkColor bg) {
-        SkAutoCanvasRestore acr(canvas, true);
-
-        canvas->clipRect(SkRect::MakeWH(w, h));
-        canvas->drawColor(bg);
-		SkScalar margin = 20;
-        SkTextBox tbox;
-		tbox.setMode(SkTextBox::kLineBreak_Mode);
-		tbox.setBox(margin, margin,
-					w - margin, h - margin);
-		tbox.setSpacing(SkIntToScalar(3)/3, 0);
-
-		SkPaint paint;
-		paint.setAntiAlias(true);
-        paint.setLCDRenderText(true);
-        paint.setColor(fg);
-		tbox.setText(gText, strlen(gText), paint);
-
-		for (int i = 9; i < 24; i += 2) {
-			paint.setTextSize(SkIntToScalar(i));
-			tbox.draw(canvas);
-			canvas->translate(0, tbox.getTextHeight() + paint.getFontSpacing());
-		}
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkScalar width = this->width() / 3;
-        drawTest(canvas, width, this->height(), SK_ColorBLACK, SK_ColorWHITE);
-        canvas->translate(width, 0);
-        drawTest(canvas, width, this->height(), SK_ColorWHITE, SK_ColorBLACK);
-        canvas->translate(width, 0);
-        drawTest(canvas, width, this->height()/2, SK_ColorGRAY, SK_ColorWHITE);
-        canvas->translate(0, this->height()/2);
-        drawTest(canvas, width, this->height()/2, SK_ColorGRAY, SK_ColorBLACK);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TextBoxView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTextEffects.cpp b/samplecode/SampleTextEffects.cpp
deleted file mode 100644
index 6eadffd..0000000
--- a/samplecode/SampleTextEffects.cpp
+++ /dev/null
@@ -1,380 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTypeface.h"
-#include "SkAvoidXfermode.h"
-
-static inline SkPMColor rgb2gray(SkPMColor c) {
-    unsigned r = SkGetPackedR32(c);
-    unsigned g = SkGetPackedG32(c);
-    unsigned b = SkGetPackedB32(c);
-
-    unsigned x = (r * 5 + g * 7 + b * 4) >> 4;
-
-    return SkPackARGB32(0, x, x, x) | (c & (SK_A32_MASK << SK_A32_SHIFT));
-}
-
-class SkGrayScaleColorFilter : public SkColorFilter {
-public:
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) {
-        for (int i = 0; i < count; i++) {
-            result[i] = rgb2gray(src[i]);
-        }
-    }
-};
-
-class SkChannelMaskColorFilter : public SkColorFilter {
-public:
-    SkChannelMaskColorFilter(U8CPU redMask, U8CPU greenMask, U8CPU blueMask) {
-        fMask = SkPackARGB32(0xFF, redMask, greenMask, blueMask);
-    }
-
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor result[]) {
-        SkPMColor mask = fMask;
-        for (int i = 0; i < count; i++) {
-            result[i] = src[i] & mask;
-        }
-    }
-
-private:
-    SkPMColor   fMask;
-};
-
-///////////////////////////////////////////////////////////
-
-#include "SkGradientShader.h"
-#include "SkLayerRasterizer.h"
-#include "SkBlurMaskFilter.h"
-
-static void r0(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setMaskFilter(SkBlurMaskFilter::Create(SkIntToScalar(3),
-                                             SkBlurMaskFilter::kNormal_BlurStyle))->unref();
-    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
-
-    p.setMaskFilter(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-
-    p.setAlpha(0x11);
-    p.setStyle(SkPaint::kFill_Style);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    rast->addLayer(p);
-}
-
-static void r1(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-
-    p.setAlpha(0x40);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*2);
-    rast->addLayer(p);
-}
-
-static void r2(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setStyle(SkPaint::kStrokeAndFill_Style);
-    p.setStrokeWidth(SK_Scalar1*4);
-    rast->addLayer(p);
-
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*3/2);
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-}
-
-static void r3(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1*3);
-    rast->addLayer(p);
-
-    p.setAlpha(0x20);
-    p.setStyle(SkPaint::kFill_Style);
-    p.setXfermodeMode(SkXfermode::kSrc_Mode);
-    rast->addLayer(p);
-}
-
-static void r4(SkLayerRasterizer* rast, SkPaint& p) {
-    p.setAlpha(0x60);
-    rast->addLayer(p, SkIntToScalar(3), SkIntToScalar(3));
-
-    p.setAlpha(0xFF);
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p, SK_Scalar1*3/2, SK_Scalar1*3/2);
-
-    p.setXfermode(NULL);
-    rast->addLayer(p);
-}
-
-#include "SkDiscretePathEffect.h"
-
-static void r5(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-
-    p.setPathEffect(new SkDiscretePathEffect(SK_Scalar1*4, SK_Scalar1*3))->unref();
-    p.setXfermodeMode(SkXfermode::kSrcOut_Mode);
-    rast->addLayer(p);
-}
-
-static void r6(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-
-    p.setAntiAlias(false);
-    SkLayerRasterizer* rast2 = new SkLayerRasterizer;
-    r5(rast2, p);
-    p.setRasterizer(rast2)->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-}
-
-#include "Sk2DPathEffect.h"
-
-static SkPathEffect* MakeDotEffect(SkScalar radius, const SkMatrix& matrix) {
-    SkPath path;
-    path.addCircle(0, 0, radius);
-    return new SkPath2DPathEffect(matrix, path);
-}
-
-static void r7(SkLayerRasterizer* rast, SkPaint& p) {
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    p.setPathEffect(MakeDotEffect(SK_Scalar1*4, lattice))->unref();
-    rast->addLayer(p);
-}
-
-static void r8(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1*6, SK_Scalar1*6, 0, 0);
-    lattice.postSkew(SK_Scalar1/3, 0, 0, 0);
-    p.setPathEffect(MakeDotEffect(SK_Scalar1*2, lattice))->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-
-    p.setPathEffect(NULL);
-    p.setXfermode(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-}
-
-class Line2DPathEffect : public Sk2DPathEffect {
-public:
-    Line2DPathEffect(SkScalar width, const SkMatrix& matrix)
-        : Sk2DPathEffect(matrix), fWidth(width) {}
-
-	virtual bool filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
-        if (this->INHERITED::filterPath(dst, src, width)) {
-            *width = fWidth;
-            return true;
-        }
-        return false;
-    }
-
-    virtual Factory getFactory() { return CreateProc; }
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
-        this->INHERITED::flatten(buffer);
-        buffer.writeScalar(fWidth);
-    }
-
-protected:
-	virtual void nextSpan(int u, int v, int ucount, SkPath* dst) {
-        if (ucount > 1) {
-            SkPoint	src[2], dstP[2];
-
-            src[0].set(SkIntToScalar(u) + SK_ScalarHalf,
-                       SkIntToScalar(v) + SK_ScalarHalf);
-            src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf,
-                       SkIntToScalar(v) + SK_ScalarHalf);
-            this->getMatrix().mapPoints(dstP, src, 2);
-
-            dst->moveTo(dstP[0]);
-            dst->lineTo(dstP[1]);
-        }
-    }
-
-    Line2DPathEffect(SkFlattenableReadBuffer& buffer) : Sk2DPathEffect(buffer) {
-        fWidth = buffer.readScalar();
-    }
-
-private:
-    SkScalar fWidth;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return new Line2DPathEffect(buffer);
-    }
-
-    typedef Sk2DPathEffect INHERITED;
-};
-
-static void r9(SkLayerRasterizer* rast, SkPaint& p) {
-    rast->addLayer(p);
-
-    SkMatrix    lattice;
-    lattice.setScale(SK_Scalar1, SK_Scalar1*6, 0, 0);
-    lattice.postRotate(SkIntToScalar(30), 0, 0);
-    p.setPathEffect(new Line2DPathEffect(SK_Scalar1*2, lattice))->unref();
-    p.setXfermodeMode(SkXfermode::kClear_Mode);
-    rast->addLayer(p);
-
-    p.setPathEffect(NULL);
-    p.setXfermode(NULL);
-    p.setStyle(SkPaint::kStroke_Style);
-    p.setStrokeWidth(SK_Scalar1);
-    rast->addLayer(p);
-}
-
-typedef void (*raster_proc)(SkLayerRasterizer*, SkPaint&);
-
-static const raster_proc gRastProcs[] = {
-    r0, r1, r2, r3, r4, r5, r6, r7, r8, r9
-};
-
-static const struct {
-    SkColor fMul, fAdd;
-} gLightingColors[] = {
-    { 0x808080, 0x800000 }, // general case
-    { 0x707070, 0x707070 }, // no-pin case
-    { 0xFFFFFF, 0x800000 }, // just-add case
-    { 0x808080, 0x000000 }, // just-mul case
-    { 0xFFFFFF, 0x000000 }  // identity case
-};
-
-#include "SkXfermode.h"
-
-static unsigned color_dist16(uint16_t a, uint16_t b) {
-    unsigned dr = SkAbs32(SkPacked16ToR32(a) - SkPacked16ToR32(b));
-    unsigned dg = SkAbs32(SkPacked16ToG32(a) - SkPacked16ToG32(b));
-    unsigned db = SkAbs32(SkPacked16ToB32(a) - SkPacked16ToB32(b));
-
-    return SkMax32(dr, SkMax32(dg, db));
-}
-
-static unsigned scale_dist(unsigned dist, unsigned scale) {
-    dist >>= 6;
-    dist = (dist << 2) | dist;
-    dist = (dist << 4) | dist;
-    return dist;
-
-//    return SkAlphaMul(dist, scale);
-}
-
-static void apply_shader(SkPaint* paint, int index) {
-    raster_proc proc = gRastProcs[index];
-    if (proc)
-    {
-        SkPaint p;
-        SkLayerRasterizer*  rast = new SkLayerRasterizer;
-
-        p.setAntiAlias(true);
-        proc(rast, p);
-        paint->setRasterizer(rast)->unref();
-    }
-
-#if 0
-    SkScalar dir[] = { SK_Scalar1, SK_Scalar1, SK_Scalar1 };
-    paint->setMaskFilter(SkBlurMaskFilter::CreateEmboss(dir, SK_Scalar1/4, SkIntToScalar(4), SkIntToScalar(3)))->unref();
-#endif
-    paint->setColor(SK_ColorBLUE);
-}
-
-static int gRastIndex;
-
-class TextEffectView : public SampleView {
-    SkTypeface* fFace;
-public:
-	TextEffectView() {
-        fFace = SkTypeface::CreateFromFile("/Users/reed/Downloads/p052024l.pfb");
-    }
-
-    virtual ~TextEffectView() {
-        SkSafeUnref(fFace);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Text Effects");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->save();
-//        canvas->scale(SK_Scalar1*2, SK_Scalar1*2, 0, 0);
-
-        SkPaint     paint;
-
-        paint.setAntiAlias(true);
-        paint.setTextSize(SkIntToScalar(56));
-        paint.setTypeface(SkTypeface::CreateFromName("sans-serif",
-                                                     SkTypeface::kBold));
-
-        SkScalar    x = SkIntToScalar(20);
-        SkScalar    y = paint.getTextSize();
-
-        SkString str("TextEffects");
-
-        paint.setTypeface(fFace);
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gRastProcs); i++) {
-            apply_shader(&paint, i);
-
-          //  paint.setMaskFilter(NULL);
-          //  paint.setColor(SK_ColorBLACK);
-
-#if 1
-            int index = i % SK_ARRAY_COUNT(gLightingColors);
-            paint.setColorFilter(SkColorFilter::CreateLightingFilter(
-                                    gLightingColors[index].fMul,
-                                    gLightingColors[index].fAdd))->unref();
-#endif
-
-            canvas->drawText(str.c_str(), str.size(), x, y, paint);
-
-            y += paint.getFontSpacing();
-        }
-
-        canvas->restore();
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        gRastIndex = (gRastIndex + 1) % SK_ARRAY_COUNT(gRastProcs);
-        this->inval(NULL);
-
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TextEffectView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTextOnPath.cpp b/samplecode/SampleTextOnPath.cpp
deleted file mode 100644
index 2b73e9a..0000000
--- a/samplecode/SampleTextOnPath.cpp
+++ /dev/null
@@ -1,290 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPackBits.h"
-#include "SkPath.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTypeface.h"
-#include "SkAvoidXfermode.h"
-
-#define REPEAT_COUNT    0
-
-static const char gText[] = "Hamburgefons";
-
-static bool gDevKern;
-
-static void rand_text(char text[], SkRandom& rand, size_t count) {
-    for (size_t i = 0; i < count; i++) {
-        text[i] = rand.nextU() & 0x7F;
-    }
-}
-
-static SkScalar sum_widths(const SkScalar widths[], int count) {
-    SkScalar w = 0;
-    for (int i = 0; i < count; i++) {
-        w += widths[i];
-    }
-    return w;
-}
-
-static void test_measure(const SkPaint& paint) {
-    char        text[256];
-    SkScalar    widths[256];
-    SkRect      rects[256];
-    SkRect      bounds;
-    int         count = 256;
-    
-    SkRandom rand;
-    
-    for (int i = 0; i < 100; i++) {
-        rand_text(text, rand, 256);
-        paint.getTextWidths(text, count, widths, NULL);
-        SkDEBUGCODE(SkScalar tw0 = sum_widths(widths, count);)
-        paint.getTextWidths(text, count, widths, rects);
-        SkDEBUGCODE(SkScalar tw1 = sum_widths(widths, count);)
-        SkASSERT(tw0 == tw1);
-
-        SkDEBUGCODE(SkScalar w0 = paint.measureText(text, count, NULL);)
-        SkDEBUGCODE(SkScalar w1 = paint.measureText(text, count, &bounds);)
-        SkASSERT(w0 == w1);
-        SkASSERT(w0 == tw0);
-        
-        SkRect r = rects[0];
-        SkScalar x = 0;
-        for (int j = 1; j < count; j++) {
-            x += widths[j-1];
-            rects[j].offset(x, 0);
-            r.join(rects[j]);
-        }
-        SkASSERT(r == bounds);
-        
-        if (r != bounds) {
-            printf("flags=%x i=%d [%g %g %g %g] [%g %g %g %g]\n",
-                   paint.getFlags(), i,
-                   SkScalarToFloat(r.fLeft),
-                   SkScalarToFloat(r.fTop),
-                   SkScalarToFloat(r.fRight),
-                   SkScalarToFloat(r.fBottom),
-                   SkScalarToFloat(bounds.fLeft),
-                   SkScalarToFloat(bounds.fTop),
-                   SkScalarToFloat(bounds.fRight),
-                   SkScalarToFloat(bounds.fBottom));
-        }
-    }
-}
-
-static void test_measure() {
-    SkPaint paint;
-    
-    for (int i = 0; i <= SkPaint::kAllFlags; i++) {
-        paint.setFlags(i);
-        test_measure(paint);
-    }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-static void test_textBounds(SkCanvas* canvas) {
-//    canvas->scale(SK_Scalar1/2, SK_Scalar1/2);
-    
-//    canvas->rotate(SkIntToScalar(30));
-
-    gDevKern = !gDevKern;
-
-    SkScalar x = SkIntToScalar(50);
-    SkScalar y = SkIntToScalar(150);
-    SkScalar w[100];
-    SkRect   r[100], bounds;
-    
-    SkPaint paint;
-    paint.setTextSize(SkIntToScalar(64));
-    paint.setAntiAlias(true);
-    paint.setDevKernText(gDevKern);
-    
-    (void)paint.measureText(gText, strlen(gText), &bounds);
-    paint.setColor(SK_ColorGREEN);
-    bounds.offset(x, y);
-    canvas->drawRect(bounds, paint);
-
-    int count = paint.getTextWidths(gText, strlen(gText), w, r);
-
-    paint.setColor(SK_ColorRED);
-    for (int i = 0; i < count; i++) {
-        r[i].offset(x, y);
-        canvas->drawRect(r[i], paint);
-        x += w[i];
-    }
-    x = SkIntToScalar(50);
-    paint.setColor(gDevKern ? SK_ColorDKGRAY : SK_ColorBLACK);
-    canvas->drawText(gText, strlen(gText), x, y, paint);
-}
-
-static void create_src(SkBitmap* bitmap, SkBitmap::Config config) {
-    bitmap->setConfig(config, 100, 100);
-    bitmap->allocPixels();
-    bitmap->eraseColor(0);
-    
-    SkCanvas    canvas(*bitmap);
-    SkPaint     paint;
-
-    paint.setAntiAlias(true);
-    canvas.drawCircle(SkIntToScalar(50), SkIntToScalar(50),
-                      SkIntToScalar(50), paint);
-}
-
-static void blur(SkBitmap* dst, const SkBitmap& src, SkScalar radius) {
-    *dst = src;
-}
-
-static void test_bitmap_blur(SkCanvas* canvas) {
-    SkBitmap    src, dst;
-    
-    create_src(&src, SkBitmap::kARGB_8888_Config);
-    blur(&dst, src, SkIntToScalar(4));
-    
-    SkPaint paint;
-    
-    paint.setColor(SK_ColorRED);
-
-    canvas->drawBitmap(dst, SkIntToScalar(30), SkIntToScalar(60), &paint);
-}
-
-static SkScalar getpathlen(const SkPath& path) {
-    SkPathMeasure   meas(path, false);
-    return meas.getLength();
-}
-
-static void test_textpathmatrix(SkCanvas* canvas) {
-    SkPaint paint;
-    SkPath  path;
-    SkMatrix matrix;
-    
-    path.moveTo(SkIntToScalar(200), SkIntToScalar(300));
-    path.quadTo(SkIntToScalar(400), SkIntToScalar(100),
-                SkIntToScalar(600), SkIntToScalar(300));
-
-    paint.setAntiAlias(true);
-    
-    paint.setStyle(SkPaint::kStroke_Style);
- //   canvas->drawPath(path, paint);
-    paint.setStyle(SkPaint::kFill_Style);
-    paint.setTextSize(SkIntToScalar(48));
-    paint.setTextAlign(SkPaint::kRight_Align);
-    
-    const char* text = "Reflection";
-    size_t      len = strlen(text);
-    SkScalar    pathLen = getpathlen(path);
-
-    canvas->drawTextOnPath(text, len, path, NULL, paint);
-    
-    paint.setColor(SK_ColorRED);
-    matrix.setScale(-SK_Scalar1, SK_Scalar1);
-    matrix.postTranslate(pathLen, 0);
-    canvas->drawTextOnPath(text, len, path, &matrix, paint);
-    
-    paint.setColor(SK_ColorBLUE);
-    matrix.setScale(SK_Scalar1, -SK_Scalar1);
-    canvas->drawTextOnPath(text, len, path, &matrix, paint);
-    
-    paint.setColor(SK_ColorGREEN);
-    matrix.setScale(-SK_Scalar1, -SK_Scalar1);
-    matrix.postTranslate(pathLen, 0);
-    canvas->drawTextOnPath(text, len, path, &matrix, paint);
-}
-
-class TextOnPathView : public SampleView {
-public:
-    SkPath      fPath;
-    SkScalar    fHOffset;
-
-	TextOnPathView() {
-        SkRect r;
-        r.set(SkIntToScalar(100), SkIntToScalar(100),
-              SkIntToScalar(300), SkIntToScalar(300));
-        fPath.addOval(r);
-        fPath.offset(SkIntToScalar(200), 0);
-
-        fHOffset = SkIntToScalar(50);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Text On Path");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        
-        paint.setAntiAlias(true);
-        paint.setTextSize(SkIntToScalar(50));
-
-        for (int j = 0; j < REPEAT_COUNT; j++) {
-            SkScalar x = fHOffset;
-
-            paint.setColor(SK_ColorBLACK);
-            canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
-                                     x, paint.getTextSize()/2, paint);
-
-            paint.setColor(SK_ColorRED);
-            canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
-                                     x + SkIntToScalar(50), 0, paint);
-
-            paint.setColor(SK_ColorBLUE);
-            canvas->drawTextOnPathHV(gText, sizeof(gText)-1, fPath,
-                         x + SkIntToScalar(100), -paint.getTextSize()/2, paint);
-        }
-        
-        paint.setColor(SK_ColorGREEN);
-        paint.setStyle(SkPaint::kStroke_Style);
-//        canvas->drawPath(fPath, paint);
-        
-        canvas->translate(0, SkIntToScalar(100));
-        test_textpathmatrix(canvas);
-        
-        if (REPEAT_COUNT > 1)
-            this->inval(NULL);
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fHints += 1;
-        this->inval(NULL);
-        return this->INHERITED::onFindClickHandler(x, y);
-    }
-    
-    virtual bool onClick(Click* click) {
-        return this->INHERITED::onClick(click);
-    }
-    
-private:
-    int fHints;
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() {
-    return new TextOnPathView;
-}
-
-static SkViewRegister reg(MyFactory);
diff --git a/samplecode/SampleTextureDomain.cpp b/samplecode/SampleTextureDomain.cpp
deleted file mode 100755
index 4291468..0000000
--- a/samplecode/SampleTextureDomain.cpp
+++ /dev/null
@@ -1,116 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkBlurMaskFilter.h"
-
-namespace {
-SkBitmap make_bitmap() {
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kARGB_8888_Config , 5, 5);
-    bm.allocPixels();
-
-    for (int y = 0; y < bm.height(); y++) {
-        uint32_t* p = bm.getAddr32(0, y);
-        for (int x = 0; x < bm.width(); x++) {
-            p[x] = ((x + y) & 1) ? SK_ColorWHITE : SK_ColorBLACK;
-        }
-    }
-    bm.unlockPixels();
-    return bm;
-}
-} // unnamed namespace
-
-class TextureDomainView : public SampleView {
-    SkBitmap    fBM;
-
-public:
-    TextureDomainView(){
-        fBM = make_bitmap();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Texture Domain");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkIRect srcRect;
-        SkRect dstRect;
-        SkPaint paint;
-        paint.setFilterBitmap(true);
-
-        // Test that bitmap draws from malloc-backed bitmaps respect
-        // the constrained texture domain.
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(5.0f, 5.0f, 305.0f, 305.0f);
-        canvas->drawBitmapRect(fBM, &srcRect, dstRect, &paint);
-
-        // Test that bitmap draws across separate devices also respect
-        // the constrainted texture domain.
-        // Note:  GPU-backed bitmaps follow a different rendering path
-        // when copying from one GPU device to another.
-        SkRefPtr<SkDevice> primaryDevice(canvas->getDevice());
-        SkRefPtr<SkDevice> secondDevice(canvas->createCompatibleDevice(
-                SkBitmap::kARGB_8888_Config, 5, 5, true));
-        secondDevice->unref();
-        SkCanvas secondCanvas(secondDevice.get());
-
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(1.0f, 1.0f, 3.0f, 3.0f);
-        secondCanvas.drawBitmapRect(fBM, &srcRect, dstRect, &paint);
-
-        SkBitmap deviceBitmap = secondDevice->accessBitmap(false);
-
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(405.0f, 5.0f, 305.0f, 305.0f);
-        canvas->drawBitmapRect(deviceBitmap, &srcRect, dstRect, &paint);
-
-        // Test that bitmap blurring using a subrect
-        // renders correctly 
-        srcRect.setXYWH(1, 1, 3, 3);
-        dstRect.setXYWH(5.0f, 405.0f, 305.0f, 305.0f);
-        SkMaskFilter* mf = SkBlurMaskFilter::Create(
-            5,
-            SkBlurMaskFilter::kNormal_BlurStyle,
-            SkBlurMaskFilter::kHighQuality_BlurFlag |
-            SkBlurMaskFilter::kIgnoreTransform_BlurFlag);
-        paint.setMaskFilter(mf)->unref();
-        canvas->drawBitmapRect(deviceBitmap, &srcRect, dstRect, &paint);
-
-        // Blur and a rotation + NULL src rect
-        // This should not trigger the texture domain code
-        // but it will test a code path in SkGpuDevice::drawBitmap
-        // that handles blurs with rects transformed to non-
-        // orthogonal rects. It also tests the NULL src rect handling
-	mf = SkBlurMaskFilter::Create(
-            5,
-            SkBlurMaskFilter::kNormal_BlurStyle,
-            SkBlurMaskFilter::kHighQuality_BlurFlag);
-        paint.setMaskFilter(mf)->unref();
-
-        dstRect.setXYWH(-150.0f, -150.0f, 300.0f, 300.0f);
-        canvas->translate(550, 550);
-        canvas->rotate(45);
-        canvas->drawBitmapRect(fBM, NULL, dstRect, &paint);
-    }
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TextureDomainView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTiling.cpp b/samplecode/SampleTiling.cpp
deleted file mode 100644
index d72544b..0000000
--- a/samplecode/SampleTiling.cpp
+++ /dev/null
@@ -1,174 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkPicture.h"
-#include "SkTypeface.h"
-
-// effects
-#include "SkGradientShader.h"
-#include "SkUnitMappers.h"
-#include "SkBlurDrawLooper.h"
-
-static void makebm(SkBitmap* bm, SkBitmap::Config config, int w, int h) {
-    bm->setConfig(config, w, h);
-    bm->allocPixels();
-    bm->eraseColor(0);
-    
-    SkCanvas    canvas(*bm);
-    SkPoint     pts[] = { { 0, 0 }, { SkIntToScalar(w), SkIntToScalar(h) } };
-    SkColor     colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
-    SkScalar    pos[] = { 0, SK_Scalar1/2, SK_Scalar1 };
-    SkPaint     paint;
-    
-    SkUnitMapper*   um = NULL;    
-
-    um = new SkCosineMapper;
-//    um = new SkDiscreteMapper(12);
-
-    SkAutoUnref au(um);
-
-    paint.setDither(true);
-    paint.setShader(SkGradientShader::CreateLinear(pts, colors, pos,
-                SK_ARRAY_COUNT(colors), SkShader::kClamp_TileMode, um))->unref();
-    canvas.drawPaint(paint);
-}
-
-static void setup(SkPaint* paint, const SkBitmap& bm, bool filter,
-                  SkShader::TileMode tmx, SkShader::TileMode tmy) {
-    SkShader* shader = SkShader::CreateBitmapShader(bm, tmx, tmy);
-    paint->setShader(shader)->unref();
-    paint->setFilterBitmap(filter);
-}
-
-static const SkBitmap::Config gConfigs[] = {
-    SkBitmap::kARGB_8888_Config,
-    SkBitmap::kRGB_565_Config,
-    SkBitmap::kARGB_4444_Config
-};
-static const int gWidth = 32;
-static const int gHeight = 32;
-
-class TilingView : public SampleView {
-    SkPicture*          fTextPicture;
-    SkBlurDrawLooper    fLooper;
-public:
-	TilingView()
-            : fLooper(SkIntToScalar(1), SkIntToScalar(2), SkIntToScalar(2),
-                      0x88000000) {
-        fTextPicture = new SkPicture();
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
-            makebm(&fTexture[i], gConfigs[i], gWidth, gHeight);
-        }
-    }
-
-    ~TilingView() {
-        fTextPicture->unref();
-    }
-
-    SkBitmap    fTexture[SK_ARRAY_COUNT(gConfigs)];
-	
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Tiling");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkRect r = { 0, 0, SkIntToScalar(gWidth*2), SkIntToScalar(gHeight*2) };
-
-        static const char* gConfigNames[] = { "8888", "565", "4444" };
-    
-        static const bool           gFilters[] = { false, true };
-        static const char*          gFilterNames[] = {     "point",                     "bilinear" };
-    
-        static const SkShader::TileMode gModes[] = { SkShader::kClamp_TileMode, SkShader::kRepeat_TileMode, SkShader::kMirror_TileMode };
-        static const char*          gModeNames[] = {    "C",                    "R",                   "M" };
-
-        SkScalar y = SkIntToScalar(24);
-        SkScalar x = SkIntToScalar(10);
-
-        SkCanvas* textCanvas = NULL;
-        if (fTextPicture->width() == 0) {
-            textCanvas = fTextPicture->beginRecording(1000, 1000);
-        }
-
-        if (textCanvas) {
-            for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
-                for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
-                    SkPaint p;
-                    SkString str;
-                    p.setAntiAlias(true);
-                    p.setDither(true);
-                    p.setLooper(&fLooper);
-                    str.printf("[%s,%s]", gModeNames[kx], gModeNames[ky]);
-
-                    p.setTextAlign(SkPaint::kCenter_Align);
-                    textCanvas->drawText(str.c_str(), str.size(), x + r.width()/2, y, p);
-                    
-                    x += r.width() * 4 / 3;
-                }
-            }
-        }
-        
-        y += SkIntToScalar(16);
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gConfigs); i++) {
-            for (size_t j = 0; j < SK_ARRAY_COUNT(gFilters); j++) {
-                x = SkIntToScalar(10);
-                for (size_t kx = 0; kx < SK_ARRAY_COUNT(gModes); kx++) {
-                    for (size_t ky = 0; ky < SK_ARRAY_COUNT(gModes); ky++) {
-                        SkPaint paint;
-                        setup(&paint, fTexture[i], gFilters[j], gModes[kx], gModes[ky]);
-                        paint.setDither(true);
-                        
-                        canvas->save();
-                        canvas->translate(x, y);
-                        canvas->drawRect(r, paint);
-                        canvas->restore();
-                        
-                        x += r.width() * 4 / 3;
-                    }
-                }
-                if (textCanvas) {
-                    SkPaint p;
-                    SkString str;
-                    p.setAntiAlias(true);
-                    p.setLooper(&fLooper);
-                    str.printf("%s, %s", gConfigNames[i], gFilterNames[j]);
-                    textCanvas->drawText(str.c_str(), str.size(), x, y + r.height() * 2 / 3, p);
-                }
-
-                y += r.height() * 4 / 3;
-            }
-        }
-
-        canvas->drawPicture(*fTextPicture);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TilingView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTinyBitmap.cpp b/samplecode/SampleTinyBitmap.cpp
deleted file mode 100644
index 605d829..0000000
--- a/samplecode/SampleTinyBitmap.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkColorPriv.h"
-#include "SkShader.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkUtils.h"
-
-static SkBitmap make_bitmap() {
-    SkBitmap bm;
-    const int N = 1;
-    SkColorTable* ctable = new SkColorTable(N);
-
-    SkPMColor* c = ctable->lockColors();
-    for (int i = 0; i < N; i++) {
-        c[i] = SkPackARGB32(0x80, 0x80, 0, 0);
-    }
-    ctable->unlockColors(true);
-    bm.setConfig(SkBitmap::kIndex8_Config, 1, 1);
-    bm.allocPixels(ctable);
-    ctable->unref();
-
-    bm.lockPixels();
-    for (int y = 0; y < bm.height(); y++) {
-        uint8_t* p = bm.getAddr8(0, y);
-        for (int x = 0; x < bm.width(); x++) {
-            p[x] = 0;
-        }
-    }
-    bm.unlockPixels();
-    return bm;
-}
-
-class TinyBitmapView : public SampleView {
-    SkBitmap    fBM;
-public:
-	TinyBitmapView() {
-        fBM = make_bitmap();
-        this->setBGColor(0xFFDDDDDD);
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "TinyBitmap");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    static void setBitmapOpaque(SkBitmap* bm, bool isOpaque) {
-        SkAutoLockPixels alp(*bm);  // needed for ctable
-        bm->setIsOpaque(isOpaque);
-        SkColorTable* ctable = bm->getColorTable();
-        if (ctable) {
-            ctable->setIsOpaque(isOpaque);
-        }
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkShader* s = SkShader::CreateBitmapShader(fBM, SkShader::kRepeat_TileMode,
-                                                   SkShader::kMirror_TileMode);
-        SkPaint paint;
-        paint.setShader(s)->unref();
-        canvas->drawPaint(paint);
-    }
-    
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TinyBitmapView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTriangles.cpp b/samplecode/SampleTriangles.cpp
deleted file mode 100644
index e2ba652..0000000
--- a/samplecode/SampleTriangles.cpp
+++ /dev/null
@@ -1,125 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkConcaveToTriangles.h"
-
-#define SIZE    SkIntToScalar(150)
-
-typedef void (*PathProc)(SkPath*);
-
-static void make_path0(SkPath* path) {
-    SkRect r;
-    r.set(0, 0, SIZE, SIZE);
-    path->addRect(r);
-}
-
-static void make_path1(SkPath* path) {
-    SkRect r;
-    r.set(0, 0, SIZE, SIZE);
-    path->addRoundRect(r, SIZE/4, SIZE/4);
-}
-
-static void make_path2(SkPath* path) {
-    SkRect r;
-    r.set(0, 0, SIZE, SIZE);
-    path->addOval(r);
-}
-
-static const PathProc gProcs[] = {
-    make_path0,
-    make_path1,
-    make_path2,
-};
-
-#define COUNT_PROCS SK_ARRAY_COUNT(gProcs)
-
-class TriangleView : public SkView {
-public:
-    SkPath fPaths[COUNT_PROCS];
-
-	TriangleView() {
-        for (size_t i = 0; i < COUNT_PROCS; i++) {
-            gProcs[i](&fPaths[i]);
-        }
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Triangles");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    void drawBG(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorGRAY);
-    }
-    
-    static void draw_path(SkCanvas* canvas, const SkPaint& pathPaint,
-                          const SkPath& path, const SkPaint& triPaint) {
-        canvas->drawPath(path, pathPaint);
-        
-        int n = path.getPoints(NULL, 0);
-        SkPoint* pts = new SkPoint[n];
-        path.getPoints(pts, n);
-        
-        SkTDArray<SkPoint> triangles;
-        if (SkConcaveToTriangles(n, pts, &triangles)) {
-            canvas->drawVertices(SkCanvas::kTriangles_VertexMode,
-                                 triangles.count(), triangles.begin(), NULL,
-                                 NULL, NULL, NULL, 0, triPaint);
-        }
-        
-        SkPaint paint;
-        paint.setColor(SK_ColorGREEN);
-        paint.setStrokeWidth(SkIntToScalar(4));
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, n, pts, paint);
-        delete[] pts;
-    }
-
-    virtual void onDraw(SkCanvas* canvas) {
-        this->drawBG(canvas);
-        
-        canvas->translate(SIZE/2, SIZE/2);
-
-        SkPaint pathPaint, triPaint;
-        
-        pathPaint.setColor(SK_ColorBLUE);
-        pathPaint.setStrokeWidth(SIZE / 12);
-
-        triPaint.setColor(SK_ColorRED);
-        triPaint.setStyle(SkPaint::kStroke_Style);
-
-        for (size_t i = 0; i < COUNT_PROCS; i++) {
-            pathPaint.setStyle(SkPaint::kFill_Style);
-            draw_path(canvas, pathPaint, fPaths[i], triPaint);
-
-            canvas->save();
-            canvas->translate(0, SIZE * 6 / 5);
-
-            pathPaint.setStyle(SkPaint::kStroke_Style);
-            draw_path(canvas, pathPaint, fPaths[i], triPaint);
-            
-            canvas->restore();
-            canvas->translate(SIZE * 6 / 5, 0);
-        }
-    }
-    
-private:
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TriangleView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleTypeface.cpp b/samplecode/SampleTypeface.cpp
deleted file mode 100644
index ccdb086..0000000
--- a/samplecode/SampleTypeface.cpp
+++ /dev/null
@@ -1,135 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkTypeface.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "Sk1DPathEffect.h"
-#include "SkCornerPathEffect.h"
-#include "SkPathMeasure.h"
-#include "SkRandom.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkDither.h"
-#include "SkTypefaceCache.h"
-
-static int dither_4444(int x) {
-    return ((x << 1) - ((x >> 4 << 4) | (x >> 4))) >> 4;
-}
-
-/** Ensure that the max of the original and dithered value (for alpha) is always
-    >= any other dithered value. We apply this "max" in colorpriv.h when we
-    predither down to 4444, to be sure that we stay in legal premultiplied form
- */
-static void test_4444_dither() {
-    int buckets[16];
-    sk_bzero(buckets, sizeof(buckets));
-
-    for (int a = 0; a <= 0xFF; a++) {
-        int da = dither_4444(a);
-        int maxa = SkMax32(a >> 4, da);
-    //    SkDebugf("--- %02X %X\n", a, da);
-        buckets[da] += 1;
-        for (int c = 0; c <= a; c++) {
-            int dc = dither_4444(c);
-            if (maxa < dc) {
-                SkDebugf("------------ error a=%d da=%d c=%d dc=%d\n", a, da,
-                         c, dc);
-            }
-        }
-    }
-    for (int i = 0; i < 16; i++) {
-    //    SkDebugf("[%d] = %d\n", i, buckets[i]);
-    }
-}
-
-static const struct {
-    const char* fName;
-    SkTypeface::Style   fStyle;
-} gFaces[] = {
-    { "sans-serif", SkTypeface::kNormal },
-    { "sans-serif", SkTypeface::kBold },
-    { "sans-serif", SkTypeface::kItalic },
-    { "sans-serif", SkTypeface::kBoldItalic },
-    { "serif", SkTypeface::kNormal },
-    { "serif", SkTypeface::kBold },
-    { "serif", SkTypeface::kItalic },
-    { "serif", SkTypeface::kBoldItalic },
-    { "monospace", SkTypeface::kNormal },
-    { "monospace", SkTypeface::kBold },
-    { "monospace", SkTypeface::kItalic },
-    { "monospace", SkTypeface::kBoldItalic },
-};
-
-static const int gFaceCount = SK_ARRAY_COUNT(gFaces);
-
-class TypefaceView : public SampleView {
-    SkTypeface* fFaces[gFaceCount];
-
-public:
-	TypefaceView() {
-        test_4444_dither();
-        for (int i = 0; i < gFaceCount; i++) {
-            fFaces[i] = SkTypeface::CreateFromName(gFaces[i].fName,
-                                                   gFaces[i].fStyle);
-        }
-
-        this->setBGColor(0xFFDDDDDD);
-    }
-
-    virtual ~TypefaceView() {
-        for (int i = 0; i < gFaceCount; i++) {
-            SkSafeUnref(fFaces[i]);
-        }
-
-        SkTypefaceCache::Dump();
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Typefaces");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setTextSize(SkIntToScalar(30));
-
-        const char* text = "Hamburgefons";
-        const size_t textLen = strlen(text);
-
-        SkScalar x = SkIntToScalar(10);
-        SkScalar dy = paint.getFontMetrics(NULL);
-        SkScalar y = dy;
-
-        paint.setLinearText(true);
-        for (int i = 0; i < gFaceCount; i++) {
-            paint.setTypeface(fFaces[i]);
-            canvas->drawText(text, textLen, x, y, paint);
-            y += dy;
-        }
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new TypefaceView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleUnitMapper.cpp b/samplecode/SampleUnitMapper.cpp
deleted file mode 100644
index d323ecb..0000000
--- a/samplecode/SampleUnitMapper.cpp
+++ /dev/null
@@ -1,164 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkDevice.h"
-#include "SkPaint.h"
-#include "SkUnitMappers.h"
-#include "SkCubicInterval.h"
-
-#include "SkWidgetViews.h"
-
-static SkStaticTextView* make_textview(SkView* parent,
-                                       const SkRect& bounds,
-                                       const SkPaint& paint) {
-    SkStaticTextView* view = new SkStaticTextView;
-    view->setMode(SkStaticTextView::kFixedSize_Mode);
-    view->setPaint(paint);
-    view->setVisibleP(true);
-    view->setSize(bounds.width(), bounds.height());
-    view->setLoc(bounds.fLeft, bounds.fTop);
-    parent->attachChildToFront(view)->unref();
-    return view;
-}
-
-static void set_scalar(SkStaticTextView* view, SkScalar value) {
-    SkString str;
-    str.appendScalar(value);
-    view->setText(str);
-}
-
-class UnitMapperView : public SampleView {
-    SkPoint fPts[4];
-    SkMatrix fMatrix;
-    SkStaticTextView* fViews[4];
-
-    void setViews() {
-        set_scalar(fViews[0], fPts[1].fX);
-        set_scalar(fViews[1], fPts[1].fY);
-        set_scalar(fViews[2], fPts[2].fX);
-        set_scalar(fViews[3], fPts[2].fY);
-    }
-
-public:
-    UnitMapperView() {
-        fPts[0].set(0, 0);
-        fPts[1].set(SK_Scalar1 / 3, SK_Scalar1 / 3);
-        fPts[2].set(SK_Scalar1 * 2 / 3, SK_Scalar1 * 2 / 3);
-        fPts[3].set(SK_Scalar1, SK_Scalar1);
-
-        fMatrix.setScale(SK_Scalar1 * 200, -SK_Scalar1 * 200);
-        fMatrix.postTranslate(SkIntToScalar(100), SkIntToScalar(300));
-
-        SkRect r = {
-            SkIntToScalar(350), SkIntToScalar(100),
-            SkIntToScalar(500), SkIntToScalar(130)
-        };
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setTextSize(SkIntToScalar(25));
-        for (int i = 0; i < 4; i++) {
-            fViews[i] = make_textview(this, r, paint);
-            r.offset(0, r.height());
-        }
-        this->setViews();
-    }
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "UnitMapper");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setAntiAlias(true);
-        paint.setColor(0xFF8888FF);
-
-        SkRect r = { 0, 0, SK_Scalar1, SK_Scalar1 };
-        
-        canvas->concat(fMatrix);
-        canvas->drawRect(r, paint);
-
-        paint.setColor(SK_ColorBLACK);
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(0);
-        paint.setStrokeCap(SkPaint::kRound_Cap);
-        
-        SkPath path;
-        path.moveTo(fPts[0]);
-        path.cubicTo(fPts[1], fPts[2], fPts[3]);
-        canvas->drawPath(path, paint);
-
-        paint.setColor(SK_ColorRED);
-        paint.setStrokeWidth(0);
-        canvas->drawLine(0, 0, SK_Scalar1, SK_Scalar1, paint);
-
-        paint.setColor(SK_ColorBLUE);
-        paint.setStrokeWidth(SK_Scalar1 / 60);
-        for (int i = 0; i < 50; i++) {
-            SkScalar x = i * SK_Scalar1 / 49;
-            canvas->drawPoint(x, SkEvalCubicInterval(&fPts[1], x), paint);
-        }
-
-        paint.setStrokeWidth(SK_Scalar1 / 20);
-        paint.setColor(SK_ColorGREEN);
-        canvas->drawPoints(SkCanvas::kPoints_PointMode, 2, &fPts[1], paint);
-    }
-
-    SkPoint invertPt(SkScalar x, SkScalar y) {
-        SkPoint pt;
-        SkMatrix m;
-        fMatrix.invert(&m);
-        m.mapXY(x, y, &pt);
-        return pt;
-    }
-
-    int hittest(SkScalar x, SkScalar y) {
-        SkPoint target = { x, y };
-        SkPoint pts[2] = { fPts[1], fPts[2] };
-        fMatrix.mapPoints(pts, 2);
-        for (int i = 0; i < 2; i++) {
-            if (SkPoint::Distance(pts[i], target) < SkIntToScalar(4)) {
-                return i + 1;
-            }
-        }
-        return -1;
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        fDragIndex = hittest(x, y);
-        return fDragIndex >= 0 ? new Click(this) : NULL;
-    }
-    
-    virtual bool onClick(Click* click) {
-        if (fDragIndex >= 0) {
-            fPts[fDragIndex] = invertPt(click->fCurr.fX, click->fCurr.fY);
-            this->setViews();
-            this->inval(NULL);
-            return true;
-        }
-        return false;
-    }
-    
-private:
-    int fDragIndex;
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new UnitMapperView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleVertices.cpp b/samplecode/SampleVertices.cpp
deleted file mode 100644
index b59442d..0000000
--- a/samplecode/SampleVertices.cpp
+++ /dev/null
@@ -1,236 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkXfermode.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-
-#include "SkOSFile.h"
-#include "SkStream.h"
-
-static SkShader* make_shader0(SkIPoint* size) {
-    SkBitmap    bm;
-    size->set(2, 2);
-    bm.setConfig(SkBitmap::kARGB_8888_Config, size->fX, size->fY);
-    SkPMColor color0 = SkPreMultiplyARGB(0x80, 0x80, 0xff, 0x80);
-    SkPMColor color1 = SkPreMultiplyARGB(0x40, 0xff, 0x00, 0xff);
-    bm.allocPixels();
-    bm.eraseColor(color0);
-    bm.lockPixels();
-    uint32_t* pixels = (uint32_t*) bm.getPixels();
-    pixels[0] = pixels[2] = color0;
-    pixels[1] = pixels[3] = color1;
-    bm.unlockPixels();
-
-    return SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
-                                            SkShader::kRepeat_TileMode);
-}
-
-static SkShader* make_shader1(const SkIPoint& size) {
-    SkPoint pts[] = { { 0, 0 },
-                      { SkIntToScalar(size.fX), SkIntToScalar(size.fY) } };
-    SkColor colors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorRED };
-    return SkGradientShader::CreateLinear(pts, colors, NULL,
-                    SK_ARRAY_COUNT(colors), SkShader::kMirror_TileMode, NULL);
-}
-
-class VerticesView : public SampleView {
-    SkShader*   fShader0;
-    SkShader*   fShader1;
-
-public:
-	VerticesView() {
-        SkIPoint    size;
-
-        fShader0 = make_shader0(&size);
-        fShader1 = make_shader1(size);
-
-        make_strip(&fRecs[0], size.fX, size.fY);
-        make_fan(&fRecs[1], size.fX, size.fY);
-        make_tris(&fRecs[2]);
-
-        fScale = SK_Scalar1;
-
-        this->setBGColor(SK_ColorGRAY);
-    }
-
-    virtual ~VerticesView() {
-        SkSafeUnref(fShader0);
-        SkSafeUnref(fShader1);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt)  {
-        if (SampleCode::TitleQ(*evt))
-        {
-            SkString str("Vertices");
-            SampleCode::TitleR(evt, str.c_str());
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    SkScalar fScale;
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkPaint paint;
-        paint.setDither(true);
-        paint.setFilterBitmap(true);
-
-        for (size_t i = 0; i < SK_ARRAY_COUNT(fRecs); i++) {
-            canvas->save();
-
-            paint.setShader(NULL);
-            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                                 fRecs[i].fVerts, fRecs[i].fTexs,
-                                 NULL, NULL, NULL, 0, paint);
-
-            canvas->translate(SkIntToScalar(250), 0);
-
-            paint.setShader(fShader0);
-            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                                 fRecs[i].fVerts, fRecs[i].fTexs,
-                                 NULL, NULL, NULL, 0, paint);
-
-            canvas->translate(SkIntToScalar(250), 0);
-
-            paint.setShader(fShader1);
-            canvas->drawVertices(fRecs[i].fMode, fRecs[i].fCount,
-                                 fRecs[i].fVerts, fRecs[i].fTexs,
-                                 NULL, NULL, NULL, 0, paint);
-            canvas->restore();
-
-            canvas->translate(0, SkIntToScalar(250));
-        }
-    }
-
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        return new Click(this);
-    }
-
-    virtual bool onClick(Click* click) {
-    //    fCurrX = click->fICurr.fX;
-    //    fCurrY = click->fICurr.fY;
-        this->inval(NULL);
-        return true;
-    }
-
-private:
-    struct Rec {
-        SkCanvas::VertexMode    fMode;
-        int                     fCount;
-        SkPoint*                fVerts;
-        SkPoint*                fTexs;
-
-        Rec() : fCount(0), fVerts(NULL), fTexs(NULL) {}
-        ~Rec() { delete[] fVerts; delete[] fTexs; }
-    };
-
-    void make_tris(Rec* rec) {
-        int n = 10;
-        SkRandom    rand;
-
-        rec->fMode = SkCanvas::kTriangles_VertexMode;
-        rec->fCount = n * 3;
-        rec->fVerts = new SkPoint[rec->fCount];
-
-        for (int i = 0; i < n; i++) {
-            SkPoint* v = &rec->fVerts[i*3];
-            for (int j = 0; j < 3; j++) {
-                v[j].set(rand.nextUScalar1() * 250, rand.nextUScalar1() * 250);
-            }
-        }
-    }
-
-    void make_fan(Rec* rec, int texWidth, int texHeight) {
-        const SkScalar tx = SkIntToScalar(texWidth);
-        const SkScalar ty = SkIntToScalar(texHeight);
-        const int n = 24;
-
-        rec->fMode = SkCanvas::kTriangleFan_VertexMode;
-        rec->fCount = n + 2;
-        rec->fVerts = new SkPoint[rec->fCount];
-        rec->fTexs  = new SkPoint[rec->fCount];
-
-        SkPoint* v = rec->fVerts;
-        SkPoint* t = rec->fTexs;
-
-        v[0].set(0, 0);
-        t[0].set(0, 0);
-        for (int i = 0; i < n; i++) {
-            SkScalar cos;
-            SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
-            v[i+1].set(cos, sin);
-            t[i+1].set(i*tx/n, ty);
-        }
-        v[n+1] = v[1];
-        t[n+1].set(tx, ty);
-
-        SkMatrix m;
-        m.setScale(SkIntToScalar(100), SkIntToScalar(100));
-        m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
-        m.mapPoints(v, rec->fCount);
-    }
-
-    void make_strip(Rec* rec, int texWidth, int texHeight) {
-        const SkScalar tx = SkIntToScalar(texWidth);
-        const SkScalar ty = SkIntToScalar(texHeight);
-        const int n = 24;
-
-        rec->fMode = SkCanvas::kTriangleStrip_VertexMode;
-        rec->fCount = 2 * (n + 1);
-        rec->fVerts = new SkPoint[rec->fCount];
-        rec->fTexs  = new SkPoint[rec->fCount];
-
-        SkPoint* v = rec->fVerts;
-        SkPoint* t = rec->fTexs;
-
-        for (int i = 0; i < n; i++) {
-            SkScalar cos;
-            SkScalar sin = SkScalarSinCos(SK_ScalarPI * 2 * i / n, &cos);
-            v[i*2 + 0].set(cos/2, sin/2);
-            v[i*2 + 1].set(cos, sin);
-
-            t[i*2 + 0].set(tx * i / n, ty);
-            t[i*2 + 1].set(tx * i / n, 0);
-        }
-        v[2*n + 0] = v[0];
-        v[2*n + 1] = v[1];
-
-        t[2*n + 0].set(tx, ty);
-        t[2*n + 1].set(tx, 0);
-
-        SkMatrix m;
-        m.setScale(SkIntToScalar(100), SkIntToScalar(100));
-        m.postTranslate(SkIntToScalar(110), SkIntToScalar(110));
-        m.mapPoints(v, rec->fCount);
-    }
-
-    Rec fRecs[3];
-
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new VerticesView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleWarp.cpp b/samplecode/SampleWarp.cpp
deleted file mode 100644
index b421c60..0000000
--- a/samplecode/SampleWarp.cpp
+++ /dev/null
@@ -1,474 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkImageDecoder.h"
-
-#include "SkBlurMaskFilter.h"
-#include "SkTableMaskFilter.h"
-
-#define kNearlyZero     (SK_Scalar1 / 8092)
-
-static void test_bigblur(SkCanvas* canvas) {
-    canvas->drawColor(SK_ColorBLACK);
-
-    SkBitmap orig, mask;
-    SkImageDecoder::DecodeFile("/skimages/app_icon.png", &orig);
-
-    SkMaskFilter* mf = SkBlurMaskFilter::Create(8, SkBlurMaskFilter::kNormal_BlurStyle);
-    SkPaint paint;
-    paint.setMaskFilter(mf)->unref();
-    SkIPoint offset;
-    orig.extractAlpha(&mask, &paint, &offset);
-
-    paint.setColor(0xFFBB8800);
-    paint.setColor(SK_ColorWHITE);
-
-    int i;
-    canvas->save();
-    float gamma = 0.8;
-    for (i = 0; i < 5; i++) {
-        paint.setMaskFilter(SkTableMaskFilter::CreateGamma(gamma))->unref();
-        canvas->drawBitmap(mask, 0, 0, &paint);
-        paint.setMaskFilter(NULL);
-        canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
-        gamma -= 0.1;
-        canvas->translate(120, 0);
-    }
-    canvas->restore();
-    canvas->translate(0, 160);
-
-    for (i = 0; i < 5; i++) {
-        paint.setMaskFilter(SkTableMaskFilter::CreateClip(i*30, 255 - 20))->unref();
-        canvas->drawBitmap(mask, 0, 0, &paint);
-        paint.setMaskFilter(NULL);
-        canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
-        canvas->translate(120, 0);
-    }
-
-#if 0
-    paint.setColor(0xFFFFFFFF);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    paint.setMaskFilter(NULL);
-    canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
-    
-    canvas->translate(120, 0);
-    
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
-    
-    canvas->translate(120, 0);
-    
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
-    
-    canvas->translate(120, 0);
-    
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
-    
-    canvas->translate(120, 0);
-    
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(mask, 0, 0, &paint);
-    canvas->drawBitmap(orig, -offset.fX, -offset.fY, &paint);
-#endif
-}
-
-#include "SkMeshUtils.h"
-
-static SkPoint SkMakePoint(SkScalar x, SkScalar y) {
-    SkPoint pt;
-    pt.set(x, y);
-    return pt;
-}
-
-static SkPoint SkPointInterp(const SkPoint& a, const SkPoint& b, SkScalar t) {
-    return SkMakePoint(SkScalarInterp(a.fX, b.fX, t),
-                       SkScalarInterp(a.fY, b.fY, t));
-}
-
-#include "SkBoundaryPatch.h"
-
-static void set_cubic(SkPoint pts[4], SkScalar x0, SkScalar y0,
-                      SkScalar x3, SkScalar y3, SkScalar scale = 1) {
-    SkPoint tmp, tmp2;
-
-    pts[0].set(x0, y0);
-    pts[3].set(x3, y3);
-    
-    tmp = SkPointInterp(pts[0], pts[3], SK_Scalar1/3);
-    tmp2 = pts[0] - tmp;
-    tmp2.rotateCW();
-    tmp2.scale(scale);
-    pts[1] = tmp + tmp2;
-    
-    tmp = SkPointInterp(pts[0], pts[3], 2*SK_Scalar1/3);
-    tmp2 = pts[3] - tmp;
-    tmp2.rotateCW();
-    tmp2.scale(scale);
-    pts[2] = tmp + tmp2;
-}
-
-static void test_patch(SkCanvas* canvas, const SkBitmap& bm, SkScalar scale) {
-    SkCubicBoundary cubic;    
-    set_cubic(cubic.fPts + 0, 0, 0, 100, 0, scale);
-    set_cubic(cubic.fPts + 3, 100, 0, 100, 100, scale);
-    set_cubic(cubic.fPts + 6, 100, 100,  0, 100, -scale);
-    set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
-    
-    SkBoundaryPatch patch;
-    patch.setBoundary(&cubic);
-    
-    const int Rows = 16;
-    const int Cols = 16;
-    SkPoint pts[Rows * Cols];
-    patch.evalPatch(pts, Rows, Cols);
-    
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setFilterBitmap(true);
-    paint.setStrokeWidth(1);
-    paint.setStrokeCap(SkPaint::kRound_Cap);
-    
-    canvas->translate(50, 50);
-    canvas->scale(3, 3);
-    
-    SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
-}
-
-static void test_drag(SkCanvas* canvas, const SkBitmap& bm,
-                      const SkPoint& p0, const SkPoint& p1) {
-    SkCubicBoundary cubic;    
-    set_cubic(cubic.fPts + 0, 0, 0, 100, 0, 0);
-    set_cubic(cubic.fPts + 3, 100, 0, 100, 100, 0);
-    set_cubic(cubic.fPts + 6, 100, 100,  0, 100, 0);
-    set_cubic(cubic.fPts + 9, 0, 100, 0, 0, 0);
-    
-#if 0
-    cubic.fPts[1] += p1 - p0;
-    cubic.fPts[2] += p1 - p0;
-#else
-    SkScalar dx = p1.fX - p0.fX;
-    if (dx > 0) dx = 0;
-    SkScalar dy = p1.fY - p0.fY;
-    if (dy > 0) dy = 0;
-
-    cubic.fPts[1].fY += dy;
-    cubic.fPts[2].fY += dy;
-    cubic.fPts[10].fX += dx;
-    cubic.fPts[11].fX += dx;
-#endif
-
-    SkBoundaryPatch patch;
-    patch.setBoundary(&cubic);
-    
-    const int Rows = 16;
-    const int Cols = 16;
-    SkPoint pts[Rows * Cols];
-    patch.evalPatch(pts, Rows, Cols);
-    
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setFilterBitmap(true);
-    paint.setStrokeWidth(1);
-    paint.setStrokeCap(SkPaint::kRound_Cap);
-    
-    canvas->translate(50, 50);
-    canvas->scale(3, 3);
-    
-    SkAutoCanvasRestore acr(canvas, true);
-
-    SkRect r = { 0, 0, 100, 100 };
-    canvas->clipRect(r);
-    SkMeshUtils::Draw(canvas, bm, Rows, Cols, pts, NULL, paint);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class Mesh {
-public:
-    Mesh();
-    ~Mesh();
-
-    Mesh& operator=(const Mesh& src);
-
-    void init(const SkRect& bounds, int rows, int cols,
-              const SkRect& texture);
-
-    const SkRect& bounds() const { return fBounds; }
-
-    int rows() const { return fRows; }
-    int cols() const { return fCols; }
-    SkPoint& pt(int row, int col) {
-        return fPts[row * (fRows + 1) + col];
-    }
-
-    void draw(SkCanvas*, const SkPaint&);
-    void drawWireframe(SkCanvas* canvas, const SkPaint& paint);
-
-private:
-    SkRect      fBounds;
-    int         fRows, fCols;
-    SkPoint*    fPts;
-    SkPoint*    fTex;   // just points into fPts, not separately allocated
-    int         fCount;
-    uint16_t*   fIndices;
-    int         fIndexCount;
-};
-
-Mesh::Mesh() : fPts(NULL), fCount(0), fIndices(NULL), fIndexCount(0) {}
-
-Mesh::~Mesh() {
-    delete[] fPts;
-    delete[] fIndices;
-}
-
-Mesh& Mesh::operator=(const Mesh& src) {
-    delete[] fPts;
-    delete[] fIndices;
-
-    fBounds = src.fBounds;
-    fRows = src.fRows;
-    fCols = src.fCols;
-
-    fCount = src.fCount;
-    fPts = new SkPoint[fCount * 2];
-    fTex = fPts + fCount;
-    memcpy(fPts, src.fPts, fCount * 2 * sizeof(SkPoint));
-    
-    delete[] fIndices;
-    fIndexCount = src.fIndexCount;
-    fIndices = new uint16_t[fIndexCount];
-    memcpy(fIndices, src.fIndices, fIndexCount * sizeof(uint16_t));
-
-    return *this;
-}
-
-void Mesh::init(const SkRect& bounds, int rows, int cols,
-                const SkRect& texture) {
-    SkASSERT(rows > 0 && cols > 0);
-
-    fBounds = bounds;
-    fRows = rows;
-    fCols = cols;
-
-    delete[] fPts;
-    fCount = (rows + 1) * (cols + 1);
-    fPts = new SkPoint[fCount * 2];
-    fTex = fPts + fCount;
-
-    delete[] fIndices;
-    fIndexCount = rows * cols * 6;
-    fIndices = new uint16_t[fIndexCount];
-    
-    SkPoint* pts = fPts;
-    const SkScalar dx = bounds.width() / rows;
-    const SkScalar dy = bounds.height() / cols;
-    SkPoint* tex = fTex;
-    const SkScalar dtx = texture.width() / rows;
-    const SkScalar dty = texture.height() / cols;
-    uint16_t* idx = fIndices;
-    int index = 0;
-    for (int y = 0; y <= cols; y++) {
-        for (int x = 0; x <= rows; x++) {
-            pts->set(bounds.fLeft + x*dx, bounds.fTop + y*dy);
-            pts += 1;
-            tex->set(texture.fLeft + x*dtx, texture.fTop + y*dty);
-            tex += 1;
-            
-            if (y < cols && x < rows) {
-                *idx++ = index;
-                *idx++ = index + rows + 1;
-                *idx++ = index + 1;
-
-                *idx++ = index + 1;
-                *idx++ = index + rows + 1;
-                *idx++ = index + rows + 2;
-                
-                index += 1;
-            }
-        }
-        index += 1;
-    }
-}
-
-void Mesh::draw(SkCanvas* canvas, const SkPaint& paint) {
-    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
-                         fPts, fTex, NULL, NULL, fIndices, fIndexCount,
-                         paint);
-}
-
-void Mesh::drawWireframe(SkCanvas* canvas, const SkPaint& paint) {
-    canvas->drawVertices(SkCanvas::kTriangles_VertexMode, fCount,
-                         fPts, NULL, NULL, NULL, fIndices, fIndexCount,
-                         paint);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class WarpView : public SkView {
-    Mesh        fMesh, fOrig;
-    SkBitmap    fBitmap;
-    SkMatrix    fMatrix, fInverse;
-public:
-	WarpView() {
-        SkBitmap bm;
-//        SkImageDecoder::DecodeFile("/skimages/marker.png", &bm);
-        SkImageDecoder::DecodeFile("/skimages/logo.gif", &bm);
-   //     SkImageDecoder::DecodeFile("/beach_shot.JPG", &bm);
-        fBitmap = bm;
-        
-        SkRect bounds, texture;
-        texture.set(0, 0, SkIntToScalar(fBitmap.width()),
-                    SkIntToScalar(fBitmap.height()));
-        bounds = texture;
-        
-//        fMesh.init(bounds, fBitmap.width() / 40, fBitmap.height() / 40, texture);
-        fMesh.init(bounds, fBitmap.width()/16, fBitmap.height()/16, texture);
-        fOrig = fMesh;
-        
-        fP0.set(0, 0);
-        fP1 = fP0;
-
-        fMatrix.setScale(2, 2);
-        fMatrix.invert(&fInverse);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Warp");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    static SkPoint apply_warp(const SkVector& drag, SkScalar dragLength,
-                              const SkPoint& dragStart, const SkPoint& dragCurr,
-                              const SkPoint& orig) {
-        SkVector delta = orig - dragCurr;
-        SkScalar length = SkPoint::Normalize(&delta);
-        if (length <= kNearlyZero) {
-            return orig;
-        }
-        
-        const SkScalar period = 20;
-        const SkScalar mag = dragLength / 3;
-        
-        SkScalar d = length / (period);
-        d = mag * SkScalarSin(d) / d;
-        SkScalar dx = delta.fX * d;
-        SkScalar dy = delta.fY * d;
-        SkScalar px = orig.fX + dx;
-        SkScalar py = orig.fY + dy;
-        return SkPoint::Make(px, py);
-    }
-    
-    static SkPoint apply_warp2(const SkVector& drag, SkScalar dragLength,
-                              const SkPoint& dragStart, const SkPoint& dragCurr,
-                              const SkPoint& orig) {
-        SkVector delta = orig - dragCurr;
-        SkScalar length = SkPoint::Normalize(&delta);
-        if (length <= kNearlyZero) {
-            return orig;
-        }
-        
-        const SkScalar period = 10 + dragLength/4;
-        const SkScalar mag = dragLength / 3;
-        
-        SkScalar d = length / (period);
-        if (d > SK_ScalarPI) {
-            d = SK_ScalarPI;
-        }
-
-        d = -mag * SkScalarSin(d);
-        
-        SkScalar dx = delta.fX * d;
-        SkScalar dy = delta.fY * d;
-        SkScalar px = orig.fX + dx;
-        SkScalar py = orig.fY + dy;
-        return SkPoint::Make(px, py);
-    }
-    
-    typedef SkPoint (*WarpProc)(const SkVector& drag, SkScalar dragLength,
-                             const SkPoint& dragStart, const SkPoint& dragCurr,
-                             const SkPoint& orig);
-
-    void warp(const SkPoint& p0, const SkPoint& p1) {
-        WarpProc proc = apply_warp2;
-        SkPoint delta = p1 - p0;
-        SkScalar length = SkPoint::Normalize(&delta);
-        for (int y = 0; y < fMesh.rows(); y++) {
-            for (int x = 0; x < fMesh.cols(); x++) {
-                fMesh.pt(x, y) = proc(delta, length, p0, p1, fOrig.pt(x, y));
-            }
-        }
-        fP0 = p0;
-        fP1 = p1;
-    }
-    
-    virtual void onDraw(SkCanvas* canvas) {
-        canvas->drawColor(SK_ColorLTGRAY);
-     //   test_bigblur(canvas); return;
-        
-        canvas->concat(fMatrix);
-
-        SkPaint paint;
-        paint.setFilterBitmap(true);
-        paint.setShader(SkShader::CreateBitmapShader(fBitmap,
-                                                     SkShader::kClamp_TileMode,
-                                                     SkShader::kClamp_TileMode))->unref();
-        fMesh.draw(canvas, paint); //return;
-        
-        paint.setShader(NULL);
-        paint.setColor(SK_ColorRED);
-        fMesh.draw(canvas, paint);
-
-    //    test_drag(canvas, fBitmap, fP0, fP1);
-    }
-    
-    virtual SkView::Click* onFindClickHandler(SkScalar x, SkScalar y) {
-        return new Click(this);
-    }
-    
-    virtual bool onClick(Click* click) {
-        SkPoint pts[2] = { click->fOrig, click->fCurr };
-        fInverse.mapPoints(pts, 2);
-        this->warp(pts[0], pts[1]);
-        this->inval(NULL);
-        return true;
-    }
-    
-private:
-    SkIRect    fBase, fRect;
-    SkPoint     fP0, fP1;
-    typedef SkView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new WarpView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleWritePixels.cpp b/samplecode/SampleWritePixels.cpp
deleted file mode 100644
index 45e9f41..0000000
--- a/samplecode/SampleWritePixels.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkCornerPathEffect.h"
-#include "SkCullPoints.h"
-#include "SkGradientShader.h"
-#include "SkPath.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-
-static void create_bitmap(SkBitmap* bitmap) {
-    const int W = 100;
-    const int H = 100;
-    bitmap->setConfig(SkBitmap::kARGB_8888_Config, W, H);
-    bitmap->allocPixels();
-
-    SkCanvas canvas(*bitmap);
-    canvas.drawColor(SK_ColorRED);
-    SkPaint paint;
-    paint.setColor(SK_ColorBLUE);
-    canvas.drawCircle(SkIntToScalar(W)/2, SkIntToScalar(H)/2, SkIntToScalar(W)/2, paint);
-}
-
-class WritePixelsView : public SampleView {
-    SkPath fPath;
-public:
-	WritePixelsView() {}
-    
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "WritePixels");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    
-    virtual void onDrawContent(SkCanvas* canvas) {
-        SkBitmap bitmap;
-        create_bitmap(&bitmap);
-        int x = bitmap.width() / 2;
-        int y = bitmap.height() / 2;
-        
-        SkBitmap subset;
-        bitmap.extractSubset(&subset, SkIRect::MakeXYWH(x, y, x, y));
-
-        canvas->translate(SkIntToScalar(20), SkIntToScalar(20));
-
-        canvas->writePixels(bitmap, 0, 0);
-        canvas->writePixels(subset, 0, 0);
-    }
-    
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new WritePixelsView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleXfermodes.cpp b/samplecode/SampleXfermodes.cpp
deleted file mode 100644
index ffea954..0000000
--- a/samplecode/SampleXfermodes.cpp
+++ /dev/null
@@ -1,257 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkCornerPathEffect.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-#include "SkStream.h"
-#include "SkXMLParser.h"
-#include "SkColorPriv.h"
-#include "SkImageDecoder.h"
-
-static void setNamedTypeface(SkPaint* paint, const char name[]) {
-    SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
-    paint->setTypeface(face);
-    SkSafeUnref(face);
-}
-
-#if 0
-static int newscale(U8CPU a, U8CPU b, int shift) {
-    unsigned prod = a * b + (1 << (shift - 1));
-    return (prod + (prod >> shift)) >> shift;
-}
-
-static void test_srcover565(SkCanvas* canvas) {
-    const int width = 32;
-    SkBitmap bm1, bm2, bm3;
-    bm1.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm1.allocPixels(NULL);
-    bm2.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm2.allocPixels(NULL);
-    bm3.setConfig(SkBitmap::kRGB_565_Config, width, 256); bm3.allocPixels(NULL);
-
-    int rgb = 0x18;
-    int r = rgb >> 3;
-    int g = rgb >> 2;
-    uint16_t dst = SkPackRGB16(r, g, r);
-    for (int alpha = 0; alpha <= 255; alpha++) {
-        SkPMColor pm = SkPreMultiplyARGB(alpha, rgb, rgb, rgb);
-        uint16_t newdst = SkSrcOver32To16(pm, dst);
-        sk_memset16(bm1.getAddr16(0, alpha), newdst, bm1.width());
-
-        int ia = 255 - alpha;
-        int iscale = SkAlpha255To256(ia);
-        int dr = (SkGetPackedR32(pm) + (r * iscale >> 5)) >> 3;
-        int dg = (SkGetPackedG32(pm) + (g * iscale >> 6)) >> 2;
-
-        sk_memset16(bm2.getAddr16(0, alpha), SkPackRGB16(dr, dg, dr), bm2.width());
-
-        int dr2 = (SkMulDiv255Round(alpha, rgb) + newscale(r, ia, 5)) >> 3;
-        int dg2 = (SkMulDiv255Round(alpha, rgb) + newscale(g, ia, 6)) >> 2;
-
-        sk_memset16(bm3.getAddr16(0, alpha), SkPackRGB16(dr2, dg2, dr2), bm3.width());
-
-//        if (mr != dr || mg != dg)
-        {
-//            SkDebugf("[%d] macro [%d %d] inline [%d %d] new [%d %d]\n", alpha, mr, mg, dr, dg, dr2, dg2);
-        }
-    }
-
-    SkScalar dx = SkIntToScalar(width+4);
-
-    canvas->drawBitmap(bm1, 0, 0, NULL); canvas->translate(dx, 0);
-    canvas->drawBitmap(bm2, 0, 0, NULL); canvas->translate(dx, 0);
-    canvas->drawBitmap(bm3, 0, 0, NULL); canvas->translate(dx, 0);
-
-    SkRect rect = { 0, 0, SkIntToScalar(bm1.width()), SkIntToScalar(bm1.height()) };
-    SkPaint p;
-    p.setARGB(0xFF, rgb, rgb, rgb);
-    canvas->drawRect(rect, p);
-}
-#endif
-
-static void make_bitmaps(int w, int h, SkBitmap* src, SkBitmap* dst) {
-    src->setConfig(SkBitmap::kARGB_8888_Config, w, h);
-    src->allocPixels();
-    src->eraseColor(0);
-
-    SkCanvas c(*src);
-    SkPaint p;
-    SkRect r;
-    SkScalar ww = SkIntToScalar(w);
-    SkScalar hh = SkIntToScalar(h);
-
-    p.setAntiAlias(true);
-    p.setColor(0xFFFFCC44);
-    r.set(0, 0, ww*3/4, hh*3/4);
-    c.drawOval(r, p);
-
-    dst->setConfig(SkBitmap::kARGB_8888_Config, w, h);
-    dst->allocPixels();
-    dst->eraseColor(0);
-    c.setBitmapDevice(*dst);
-
-    p.setColor(0xFF66AAFF);
-    r.set(ww/3, hh/3, ww*19/20, hh*19/20);
-    c.drawRect(r, p);
-}
-
-static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
-
-class XfermodesView : public SampleView {
-    SkBitmap    fBG;
-    SkBitmap    fSrcB, fDstB;
-
-    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
-                   SkScalar x, SkScalar y) {
-        SkPaint p;
-
-        canvas->drawBitmap(fSrcB, x, y, &p);
-        p.setAlpha(alpha);
-        p.setXfermode(mode);
-        canvas->drawBitmap(fDstB, x, y, &p);
-    }
-
-public:
-    const static int W = 64;
-    const static int H = 64;
-	XfermodesView() {
-        const int W = 64;
-        const int H = 64;
-
-        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
-        fBG.setPixels(gBG);
-        fBG.setIsOpaque(true);
-
-        make_bitmaps(W, H, &fSrcB, &fDstB);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "Xfermodes");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
-
-        const struct {
-            SkXfermode::Mode  fMode;
-            const char*         fLabel;
-        } gModes[] = {
-            { SkXfermode::kClear_Mode,    "Clear"     },
-            { SkXfermode::kSrc_Mode,      "Src"       },
-            { SkXfermode::kDst_Mode,      "Dst"       },
-            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
-            { SkXfermode::kDstOver_Mode,  "DstOver"   },
-            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
-            { SkXfermode::kDstIn_Mode,    "DstIn"     },
-            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
-            { SkXfermode::kDstOut_Mode,   "DstOut"    },
-            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
-            { SkXfermode::kDstATop_Mode,  "DstATop"   },
-            { SkXfermode::kXor_Mode,      "Xor"       },
-
-            { SkXfermode::kPlus_Mode,         "Plus"          },
-            { SkXfermode::kMultiply_Mode,     "Multiply"      },
-            { SkXfermode::kScreen_Mode,       "Screen"        },
-            { SkXfermode::kOverlay_Mode,      "Overlay"       },
-            { SkXfermode::kDarken_Mode,       "Darken"        },
-            { SkXfermode::kLighten_Mode,      "Lighten"       },
-            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
-            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
-            { SkXfermode::kHardLight_Mode,    "HardLight"     },
-            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
-            { SkXfermode::kDifference_Mode,   "Difference"    },
-            { SkXfermode::kExclusion_Mode,    "Exclusion"     },
-        };
-
-        const SkScalar w = SkIntToScalar(W);
-        const SkScalar h = SkIntToScalar(H);
-        SkShader* s = SkShader::CreateBitmapShader(fBG,
-                                                   SkShader::kRepeat_TileMode,
-                                                   SkShader::kRepeat_TileMode);
-        SkMatrix m;
-        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-        s->setLocalMatrix(m);
-
-        SkPaint labelP;
-        labelP.setAntiAlias(true);
-        labelP.setLCDRenderText(true);
-        labelP.setTextAlign(SkPaint::kCenter_Align);
-        setNamedTypeface(&labelP, "Menlo Regular");
-//        labelP.setTextSize(SkIntToScalar(11));
-
-        const int W = 5;
-
-        SkScalar x0 = 0;
-        for (int twice = 0; twice < 2; twice++) {
-            SkScalar x = x0, y = 0;
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
-                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
-                SkAutoUnref aur(mode);
-                SkRect r;
-                r.set(x, y, x+w, y+h);
-
-                SkPaint p;
-                p.setStyle(SkPaint::kFill_Style);
-                p.setShader(s);
-                canvas->drawRect(r, p);
-
-                canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
-         //       canvas->save();
-                draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
-                canvas->restore();
-
-                r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
-                p.setStyle(SkPaint::kStroke_Style);
-                p.setShader(NULL);
-                canvas->drawRect(r, p);
-
-#if 1
-                canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
-                                 x + w/2, y - labelP.getTextSize()/2, labelP);
-#endif
-                x += w + SkIntToScalar(10);
-                if ((i % W) == W - 1) {
-                    x = x0;
-                    y += h + SkIntToScalar(30);
-                }
-            }
-            x0 += SkIntToScalar(400);
-        }
-        s->unref();
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new XfermodesView; }
-static SkViewRegister reg(MyFactory);
-
diff --git a/samplecode/SampleXfermodesBlur.cpp b/samplecode/SampleXfermodesBlur.cpp
deleted file mode 100644
index 11b1268..0000000
--- a/samplecode/SampleXfermodesBlur.cpp
+++ /dev/null
@@ -1,238 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "Sk64.h"
-#include "SkCornerPathEffect.h"
-#include "SkGradientShader.h"
-#include "SkGraphics.h"
-#include "SkImageDecoder.h"
-#include "SkKernel33MaskFilter.h"
-#include "SkPath.h"
-#include "SkRandom.h"
-#include "SkRegion.h"
-#include "SkShader.h"
-#include "SkUtils.h"
-#include "SkColorPriv.h"
-#include "SkColorFilter.h"
-#include "SkTime.h"
-#include "SkTypeface.h"
-#include "SkXfermode.h"
-
-#include "SkStream.h"
-#include "SkXMLParser.h"
-#include "SkColorPriv.h"
-#include "SkImageDecoder.h"
-#include "SkBlurMaskFilter.h"
-
-#ifdef SK_BUILD_FOR_MAC
-#import <ApplicationServices/ApplicationServices.h>
-SkTypeface* SkCreateTypefaceFromCTFont(CTFontRef fontRef);
-#endif
-
-static void setNamedTypeface(SkPaint* paint, const char name[]) {
-    SkTypeface* face = SkTypeface::CreateFromName(name, SkTypeface::kNormal);
-    paint->setTypeface(face);
-    SkSafeUnref(face);
-}
-
-static uint16_t gBG[] = { 0xFFFF, 0xCCCF, 0xCCCF, 0xFFFF };
-
-class XfermodesBlurView : public SampleView {
-    SkBitmap    fBG;
-    SkBitmap    fSrcB, fDstB;
-
-    void draw_mode(SkCanvas* canvas, SkXfermode* mode, int alpha,
-                   SkScalar x, SkScalar y) {
-        SkPaint p;
-        SkMaskFilter* mf = SkBlurMaskFilter::Create(5, SkBlurMaskFilter::kNormal_BlurStyle, 0);
-        p.setMaskFilter(mf)->unref();
-
-        SkScalar ww = SkIntToScalar(W);
-        SkScalar hh = SkIntToScalar(H);
-
-        // draw a circle covering the upper
-        // left three quarters of the canvas
-        p.setColor(0xFFCC44FF);
-        SkRect r;
-        r.set(0, 0, ww*3/4, hh*3/4);
-        r.offset(x, y);
-        canvas->drawOval(r, p);
-
-        p.setXfermode(mode);
-
-        // draw a square overlapping the circle
-        // in the lower right of the canvas
-        p.setColor(0x00AA6633 | alpha << 24);
-        r.set(ww/3, hh/3, ww*19/20, hh*19/20);
-        r.offset(x, y);
-        canvas->drawRect(r, p);
-
-#ifdef SK_BUILD_FOR_MAC
-        static const char* gNames[] = { "Arial", "Times", "Courier", "Lucida" };
-        for (int j = 0; j < SK_ARRAY_COUNT(gNames); ++j) {
-            CFStringRef name = CFStringCreateWithCString(NULL, gNames[j], kCFStringEncodingUTF8);
-            CTFontRef font = CTFontCreateWithName(name, 0, NULL);
-            SkTypeface* face = SkCreateTypefaceFromCTFont(font);
-            SkDebugf("%s ct:%p face:%p ats:%p\n", gNames[j], font, face, CTFontGetPlatformFont(font, NULL));
-            for (int i = 9; i <= 24; ++i) {
-                CTFontRef newFont = CTFontCreateCopyWithAttributes(font, i, NULL, NULL);
-                SkTypeface* newFace = SkCreateTypefaceFromCTFont(newFont);
-                SkDebugf("size:%d ct:%p face:%p ats:%p\n", i, newFont, newFace, CTFontGetPlatformFont(newFont, NULL));
-                newFace->unref();
-                CFRelease(newFont);
-            }
-            face->unref();
-            CFRelease(font);
-            CFRelease(name);
-        }
-#endif
-    }
-
-public:
-    const static int W = 64;
-    const static int H = 64;
-    XfermodesBlurView() {
-        const int W = 64;
-        const int H = 64;
-
-        fBG.setConfig(SkBitmap::kARGB_4444_Config, 2, 2, 4);
-        fBG.setPixels(gBG);
-        fBG.setIsOpaque(true);
-    }
-
-protected:
-    // overrides from SkEventSink
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SampleCode::TitleR(evt, "XfermodesBlur");
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-
-    virtual void onDrawContent(SkCanvas* canvas) {
-        canvas->translate(SkIntToScalar(10), SkIntToScalar(20));
-
-        if (false) {
-            SkPaint paint;
-            paint.setAntiAlias(true);
-            paint.setTextSize(50);
-            paint.setTypeface(SkTypeface::CreateFromName("Arial Unicode MS", SkTypeface::kNormal));
-            SkSafeUnref(paint.getTypeface());
-            char buffer[10];
-            size_t len = SkUTF8_FromUnichar(0x8500, buffer);
-            canvas->drawText(buffer, len, 40, 40, paint);
-            return;
-        }
-        if (false) {
-            SkPaint paint;
-            paint.setAntiAlias(true);
-            
-            SkRect r0 = { 0, 0, 10.5f, 20 };
-            SkRect r1 = { 10.5f, 10, 20, 30 };
-            paint.setColor(SK_ColorRED);
-            canvas->drawRect(r0, paint);
-            paint.setColor(SK_ColorBLUE);
-            canvas->drawRect(r1, paint);
-            return;
-        }
-
-        const struct {
-            SkXfermode::Mode  fMode;
-            const char*         fLabel;
-        } gModes[] = {
-            { SkXfermode::kClear_Mode,    "Clear"     },
-            { SkXfermode::kSrc_Mode,      "Src"       },
-            { SkXfermode::kDst_Mode,      "Dst"       },
-            { SkXfermode::kSrcOver_Mode,  "SrcOver"   },
-            { SkXfermode::kDstOver_Mode,  "DstOver"   },
-            { SkXfermode::kSrcIn_Mode,    "SrcIn"     },
-            { SkXfermode::kDstIn_Mode,    "DstIn"     },
-            { SkXfermode::kSrcOut_Mode,   "SrcOut"    },
-            { SkXfermode::kDstOut_Mode,   "DstOut"    },
-            { SkXfermode::kSrcATop_Mode,  "SrcATop"   },
-            { SkXfermode::kDstATop_Mode,  "DstATop"   },
-            { SkXfermode::kXor_Mode,      "Xor"       },
-
-            { SkXfermode::kPlus_Mode,         "Plus"          },
-            /*{ SkXfermode::kMultiply_Mode,     "Multiply"      },
-            { SkXfermode::kScreen_Mode,       "Screen"        },
-            { SkXfermode::kOverlay_Mode,      "Overlay"       },
-            { SkXfermode::kDarken_Mode,       "Darken"        },
-            { SkXfermode::kLighten_Mode,      "Lighten"       },
-            { SkXfermode::kColorDodge_Mode,   "ColorDodge"    },
-            { SkXfermode::kColorBurn_Mode,    "ColorBurn"     },
-            { SkXfermode::kHardLight_Mode,    "HardLight"     },
-            { SkXfermode::kSoftLight_Mode,    "SoftLight"     },
-            { SkXfermode::kDifference_Mode,   "Difference"    },
-            { SkXfermode::kExclusion_Mode,    "Exclusion"     },*/
-        };
-
-        const SkScalar w = SkIntToScalar(W);
-        const SkScalar h = SkIntToScalar(H);
-        SkShader* s = SkShader::CreateBitmapShader(fBG,
-                                                   SkShader::kRepeat_TileMode,
-                                                   SkShader::kRepeat_TileMode);
-        SkMatrix m;
-        m.setScale(SkIntToScalar(6), SkIntToScalar(6));
-        s->setLocalMatrix(m);
-
-        SkPaint labelP;
-        labelP.setAntiAlias(true);
-        labelP.setLCDRenderText(true);
-        labelP.setTextAlign(SkPaint::kCenter_Align);
-        setNamedTypeface(&labelP, "Menlo Regular");
-
-        const int W = 5;
-
-        SkScalar x0 = 0;
-        for (int twice = 0; twice < 2; twice++) {
-            SkScalar x = x0, y = 0;
-            for (size_t i = 0; i < SK_ARRAY_COUNT(gModes); i++) {
-                SkXfermode* mode = SkXfermode::Create(gModes[i].fMode);
-                SkAutoUnref aur(mode);
-                SkRect r;
-                r.set(x, y, x+w, y+h);
-
-                SkPaint p;
-                p.setStyle(SkPaint::kFill_Style);
-                p.setShader(s);
-                canvas->drawRect(r, p);
-
-                canvas->saveLayer(&r, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
-                draw_mode(canvas, mode, twice ? 0x88 : 0xFF, r.fLeft, r.fTop);
-                canvas->restore();
-
-                r.inset(-SK_ScalarHalf, -SK_ScalarHalf);
-                p.setStyle(SkPaint::kStroke_Style);
-                p.setShader(NULL);
-                canvas->drawRect(r, p);
-
-                canvas->drawText(gModes[i].fLabel, strlen(gModes[i].fLabel),
-                                 x + w/2, y - labelP.getTextSize()/2, labelP);
-                x += w + SkIntToScalar(10);
-                if ((i % W) == W - 1) {
-                    x = x0;
-                    y += h + SkIntToScalar(30);
-                }
-            }
-            x0 += SkIntToScalar(400);
-        }
-        s->unref();
-    }
-
-private:
-    typedef SampleView INHERITED;
-};
-
-//////////////////////////////////////////////////////////////////////////////
-
-static SkView* MyFactory() { return new XfermodesBlurView; }
-static SkViewRegister reg(MyFactory);
diff --git a/samplecode/TransitionView.cpp b/samplecode/TransitionView.cpp
deleted file mode 100644
index 99d1275..0000000
--- a/samplecode/TransitionView.cpp
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "SampleCode.h"
-#include "SkView.h"
-#include "SkCanvas.h"
-#include "SkTime.h"
-#include "SkInterpolator.h"
-
-extern bool is_overview(SkView* view);
-
-static const char gIsTransitionQuery[] = "is-transition";
-static const char gReplaceTransitionEvt[] = "replace-transition-view";
-
-bool is_transition(SkView* view) {
-    SkEvent isTransition(gIsTransitionQuery);
-    return view->doQuery(&isTransition);
-}
-
-class TransitionView : public SampleView {
-public:
-    TransitionView(SkView* prev, SkView* next, int direction) : fInterp(4, 2){
-        fAnimationDirection = (Direction)(1 << (direction % 8));
-        
-        fPrev = prev;
-        fPrev->setClipToBounds(false);
-        fPrev->setVisibleP(true);
-        (void)SampleView::SetUsePipe(fPrev, false);
-        //Not calling unref because fPrev is assumed to have been created, so 
-        //this will result in a transfer of ownership
-        this->attachChildToBack(fPrev);
-        
-        fNext = next;
-        fNext->setClipToBounds(true);
-        fNext->setVisibleP(true);
-        (void)SampleView::SetUsePipe(fNext, false);
-        //Calling unref because next is a newly created view and TransitionView
-        //is now the sole owner of fNext
-        this->attachChildToFront(fNext)->unref();
-        
-        fDone = false;
-        //SkDebugf("--created transition\n");
-    }
-    
-    ~TransitionView(){
-        //SkDebugf("--deleted transition\n");
-    }
-    
-    virtual void requestMenu(SkOSMenu* menu) {
-        if (SampleView::IsSampleView(fNext))
-            ((SampleView*)fNext)->requestMenu(menu);
-    }
-    
-protected:
-    virtual bool onQuery(SkEvent* evt) {
-        if (SampleCode::TitleQ(*evt)) {
-            SkString title;
-            if (SampleCode::RequestTitle(fNext, &title)) {
-                SampleCode::TitleR(evt, title.c_str());
-                return true;
-            }
-            return false;
-        }
-        if (evt->isType(gIsTransitionQuery)) {
-            return true;
-        }
-        return this->INHERITED::onQuery(evt);
-    }
-    virtual bool onEvent(const SkEvent& evt) {
-        if (evt.isType(gReplaceTransitionEvt)) {
-            fPrev->detachFromParent();
-            fPrev = (SkView*)SkEventSink::FindSink(evt.getFast32());
-            (void)SampleView::SetUsePipe(fPrev, false);
-            //attach the new fPrev and call unref to balance the ref in onDraw
-            this->attachChildToBack(fPrev)->unref();
-            this->inval(NULL);
-            return true;
-        }
-        if (evt.isType("transition-done")) {
-            fNext->setLoc(0, 0);
-            fNext->setClipToBounds(false);
-            SkEvent* evt = new SkEvent(gReplaceTransitionEvt, 
-                                       this->getParent()->getSinkID());
-            evt->setFast32(fNext->getSinkID());
-            //increate ref count of fNext so it survives detachAllChildren
-            fNext->ref();
-            this->detachAllChildren();
-            evt->post();
-            return true;
-        }
-        return this->INHERITED::onEvent(evt);
-    }
-    virtual void onDrawBackground(SkCanvas* canvas) {}
-    virtual void onDrawContent(SkCanvas* canvas) {
-        if (fDone)
-            return;
-
-        if (is_overview(fNext) || is_overview(fPrev)) {
-            fUsePipe = false;
-        }
-        
-        SkScalar values[4];
-        SkInterpolator::Result result = fInterp.timeToValues(SkTime::GetMSecs(), values);
-        //SkDebugf("transition %x %d pipe:%d\n", this, result, fUsePipe);
-        //SkDebugf("%f %f %f %f %d\n", values[0], values[1], values[2], values[3], result);
-        if (SkInterpolator::kNormal_Result == result) {
-            fPrev->setLocX(values[kPrevX]);
-            fPrev->setLocY(values[kPrevY]);
-            fNext->setLocX(values[kNextX]);
-            fNext->setLocY(values[kNextY]);
-            this->inval(NULL);
-        }
-        else {
-            (new SkEvent("transition-done", this->getSinkID()))->post();
-            fDone = true;
-        }
-    }
-    
-    virtual void onSizeChange() {
-        this->INHERITED::onSizeChange();
-        
-        fNext->setSize(this->width(), this->height());
-        fPrev->setSize(this->width(), this->height());
-        
-        SkScalar lr = 0, ud = 0;
-        if (fAnimationDirection & (kLeftDirection|kULDirection|kDLDirection))
-            lr = this->width();
-        if (fAnimationDirection & (kRightDirection|kURDirection|kDRDirection))
-            lr = -this->width();
-        if (fAnimationDirection & (kUpDirection|kULDirection|kURDirection))
-            ud = this->height();
-        if (fAnimationDirection & (kDownDirection|kDLDirection|kDRDirection))
-            ud = -this->height();
-        
-        fBegin[kPrevX] = fBegin[kPrevY] = 0;
-        fBegin[kNextX] = lr;
-        fBegin[kNextY] = ud;
-        fNext->setLocX(lr);
-        fNext->setLocY(ud);
-        
-        if (is_transition(fPrev))
-            lr = ud = 0;
-        fEnd[kPrevX] = -lr;
-        fEnd[kPrevY] = -ud;
-        fEnd[kNextX] = fEnd[kNextY] = 0;
-        SkScalar blend[] = {0.8, 0.0, 0.0, 1.0};
-        fInterp.setKeyFrame(0, SkTime::GetMSecs(), fBegin, blend);
-        fInterp.setKeyFrame(1, SkTime::GetMSecs()+500, fEnd, blend);
-    }
-    
-private:
-    enum {
-        kPrevX = 0,
-        kPrevY = 1,
-        kNextX = 2,
-        kNextY = 3
-    };
-    SkView* fPrev;
-    SkView* fNext;
-    bool    fDone;
-    SkInterpolator fInterp;
-    
-    enum Direction{
-        kUpDirection    = 1,
-        kURDirection    = 1 << 1,
-        kRightDirection = 1 << 2,
-        kDRDirection    = 1 << 3,
-        kDownDirection  = 1 << 4,
-        kDLDirection    = 1 << 5,
-        kLeftDirection  = 1 << 6,
-        kULDirection    = 1 << 7
-    };
-    
-    Direction fAnimationDirection;
-    SkScalar fBegin[4];
-    SkScalar fEnd[4];
-    
-    typedef SampleView INHERITED;
-};
-
-SkView* create_transition(SkView* prev, SkView* next, int direction) {
-    return SkNEW_ARGS(TransitionView, (prev, next, direction));
-};
\ No newline at end of file
diff --git a/samplecode/samplecode_files.mk b/samplecode/samplecode_files.mk
deleted file mode 100644
index cb757de..0000000
--- a/samplecode/samplecode_files.mk
+++ /dev/null
@@ -1,67 +0,0 @@
-SOURCE := \
-    SampleBitmapRect.cpp \
-    SamplePathClip.cpp \
-    SampleComplexClip.cpp \
-    SampleNinePatch.cpp \
-    SampleAvoid.cpp \
-    SampleMeasure.cpp \
-    SampleArc.cpp \
-    SampleRepeatTile.cpp \
-    SampleApp.cpp \
-    vertexdump.cpp \
-    SampleShapes.cpp \
-    SampleMipMap.cpp \
-    SampleLCD.cpp \
-    SampleCamera.cpp \
-    SampleVertices.cpp \
-    SampleFontScalerTest.cpp \
-    SampleBigGradient.cpp \
-    SampleAll.cpp \
-    SampleShaderText.cpp \
-    SamplePolyToPoly.cpp \
-    SampleBlur.cpp \
-    SampleHairline.cpp \
-    SampleCircle.cpp \
-    SampleOvalTest.cpp \
-    SampleLines.cpp \
-    SampleOverflow.cpp \
-    SampleStrokePath.cpp \
-    SampleSlides.cpp \
-    SampleLayers.cpp \
-    SampleTiling.cpp \
-    SampleTinyBitmap.cpp \
-    SampleXfermodes.cpp \
-    SampleDrawLooper.cpp \
-    SampleTextEffects.cpp \
-    SampleTextOnPath.cpp \
-    SampleDitherBitmap.cpp \
-    SampleExtractAlpha.cpp \
-    SampleDither.cpp \
-    SampleEncode.cpp \
-    SampleFontCache.cpp \
-    SampleGradients.cpp \
-    SampleTypeface.cpp \
-    SampleFillType.cpp \
-    SamplePath.cpp \
-    SampleLayerMask.cpp \
-    SampleStrokeText.cpp \
-    SamplePathEffects.cpp \
-    SampleTextAlpha.cpp \
-    ClockFaceView.cpp \
-    SampleEmboss.cpp \
-    SamplePoints.cpp \
-    SampleFilter2.cpp \
-    SamplePatch.cpp \
-    SampleFilter.cpp \
-    OverView.cpp \
-    SampleFuzz.cpp \
-    SampleShaders.cpp \
-    SampleImage.cpp \
-    SampleMovie.cpp \
-    SampleImageDir.cpp \
-    SampleWarp.cpp \
-    SamplePageFlip.cpp \
-    SamplePicture.cpp \
-    SampleLineClipper.cpp \
-    SampleRegion.cpp \
-    SampleDecode.cpp \ Crashes
diff --git a/samplecode/vertexdump.cpp b/samplecode/vertexdump.cpp
deleted file mode 100644
index ae310a5..0000000
--- a/samplecode/vertexdump.cpp
+++ /dev/null
@@ -1,95 +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 "SkPoint.h"
-
-void setup_vertexbug(SkPoint verts[], SkPoint texs[], uint16_t index[]);
-
-void setup_vertexbug(SkPoint verts[], SkPoint texs[], uint16_t index[]) {
-    verts[0].set(SkFloatToScalar(107), SkFloatToScalar(189));
-    texs[0].set(SkFloatToScalar(0), SkFloatToScalar(0));
-    verts[1].set(SkFloatToScalar(116), SkFloatToScalar(189));
-    texs[1].set(SkFloatToScalar(9), SkFloatToScalar(0));
-    verts[2].set(SkFloatToScalar(203), SkFloatToScalar(189));
-    texs[2].set(SkFloatToScalar(35), SkFloatToScalar(0));
-    verts[3].set(SkFloatToScalar(212), SkFloatToScalar(189));
-    texs[3].set(SkFloatToScalar(44), SkFloatToScalar(0));
-    verts[4].set(SkFloatToScalar(107), SkFloatToScalar(198));
-    texs[4].set(SkFloatToScalar(0), SkFloatToScalar(9));
-    verts[5].set(SkFloatToScalar(116), SkFloatToScalar(198));
-    texs[5].set(SkFloatToScalar(9), SkFloatToScalar(9));
-    verts[6].set(SkFloatToScalar(203), SkFloatToScalar(198));
-    texs[6].set(SkFloatToScalar(35), SkFloatToScalar(9));
-    verts[7].set(SkFloatToScalar(212), SkFloatToScalar(198));
-    texs[7].set(SkFloatToScalar(44), SkFloatToScalar(9));
-    verts[8].set(SkFloatToScalar(107), SkFloatToScalar(224));
-    texs[8].set(SkFloatToScalar(0), SkFloatToScalar(39));
-    verts[9].set(SkFloatToScalar(116), SkFloatToScalar(224));
-    texs[9].set(SkFloatToScalar(9), SkFloatToScalar(39));
-    verts[10].set(SkFloatToScalar(203), SkFloatToScalar(224));
-    texs[10].set(SkFloatToScalar(35), SkFloatToScalar(39));
-    verts[11].set(SkFloatToScalar(212), SkFloatToScalar(224));
-    texs[11].set(SkFloatToScalar(44), SkFloatToScalar(39));
-    verts[12].set(SkFloatToScalar(107), SkFloatToScalar(233));
-    texs[12].set(SkFloatToScalar(0), SkFloatToScalar(48));
-    verts[13].set(SkFloatToScalar(116), SkFloatToScalar(233));
-    texs[13].set(SkFloatToScalar(9), SkFloatToScalar(48));
-    verts[14].set(SkFloatToScalar(203), SkFloatToScalar(233));
-    texs[14].set(SkFloatToScalar(35), SkFloatToScalar(48));
-    verts[15].set(SkFloatToScalar(212), SkFloatToScalar(233));
-    texs[15].set(SkFloatToScalar(44), SkFloatToScalar(48));
-    index[0] = 0; index[1] = 5; index[2] = 1;
-    index[3] = 0; index[4] = 4; index[5] = 5;
-#if 0
-    index[6] = 1; index[7] = 6; index[8] = 2;
-#else
-    index[6] = 6; index[7] = 2; index[8] = 1;
-#endif
-    index[9] = 1; index[10] = 5; index[11] = 6;
-    index[12] = 2;
-    index[13] = 7;
-    index[14] = 3;
-    index[15] = 2;
-    index[16] = 6;
-    index[17] = 7;
-    index[18] = 4;
-    index[19] = 9;
-    index[20] = 5;
-    index[21] = 4;
-    index[22] = 8;
-    index[23] = 9;
-    index[24] = 5;
-    index[25] = 10;
-    index[26] = 6;
-    index[27] = 5;
-    index[28] = 9;
-    index[29] = 10;
-    index[30] = 6;
-    index[31] = 11;
-    index[32] = 7;
-    index[33] = 6;
-    index[34] = 10;
-    index[35] = 11;
-    index[36] = 8;
-    index[37] = 13;
-    index[38] = 9;
-    index[39] = 8;
-    index[40] = 12;
-    index[41] = 13;
-    index[42] = 9;
-    index[43] = 14;
-    index[44] = 10;
-    index[45] = 9;
-    index[46] = 13;
-    index[47] = 14;
-    index[48] = 10;
-    index[49] = 15;
-    index[50] = 11;
-    index[51] = 10;
-    index[52] = 14;
-    index[53] = 15;
-}
diff --git a/skia.gyp b/skia.gyp
new file mode 100644
index 0000000..34c0528
--- /dev/null
+++ b/skia.gyp
@@ -0,0 +1,28 @@
+# Top-level gyp configuration for Skia.
+#
+# Projects that use Skia should depend on one or more of the targets
+# defined here.
+#
+# More targets are defined within the gyp/ directory, but those are
+# not intended for external use and may change without notice.
+#
+# Full documentation at https://sites.google.com/site/skiadocs/
+#
+{
+  'targets': [
+    {
+      'target_name': 'alltargets',
+      'type': 'none',
+      'dependencies': [
+        'gyp/everything.gyp:everything',
+        'gyp/most.gyp:most',
+      ],
+    },
+  ],
+}
+
+# Local Variables:
+# tab-width:2
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=2 shiftwidth=2:
diff --git a/src/animator/SkAnimate.h b/src/animator/SkAnimate.h
index b7018b9..1bbecf9 100644
--- a/src/animator/SkAnimate.h
+++ b/src/animator/SkAnimate.h
@@ -32,4 +32,3 @@
 };
 
 #endif // SkAnimateField_DEFINED
-
diff --git a/src/animator/SkAnimateActive.cpp b/src/animator/SkAnimateActive.cpp
index 4177aa0..46b849b 100644
--- a/src/animator/SkAnimateActive.cpp
+++ b/src/animator/SkAnimateActive.cpp
@@ -22,7 +22,7 @@
     fMaxTime(0), fMaker(maker), fDrawIndex(0), fDrawMax(0) {
 }
 
-void SkActive::init() 
+void SkActive::init()
 {
     fAnimators = fApply.fAnimators;
     int animators = fAnimators.count();
@@ -98,7 +98,7 @@
                         saveIndex -= oldCount;
                         delete[] fSaveRestore[saveIndex];
                         fSaveRestore.remove(saveIndex);
-                        delete[] fSaveInterpolators[saveIndex]; 
+                        delete[] fSaveInterpolators[saveIndex];
                         fSaveInterpolators.remove(saveIndex);
                     } while (saveIndex > 0);
                 }
@@ -127,17 +127,17 @@
         SkASSERT(saveIndex >= 0);
         SkASSERT(newTotal >= 0);
         memmove(&fSaveRestore[newTotal], &fSaveRestore[saveIndex], oldCount);
-        memset(&fSaveRestore[newTotal + oldCount], 0, 
+        memset(&fSaveRestore[newTotal + oldCount], 0,
             sizeof(fSaveRestore[0]) * (newCount - oldCount));
-        memmove(&fSaveInterpolators[newTotal], 
+        memmove(&fSaveInterpolators[newTotal],
             &fSaveInterpolators[saveIndex], oldCount);
-        memset(&fSaveInterpolators[newTotal + oldCount], 0, 
+        memset(&fSaveInterpolators[newTotal + oldCount], 0,
             sizeof(fSaveRestore[0]) * (newCount - oldCount));
     } while (saveIndex > 0);
     SkASSERT(newTotal == 0);
 }
 
-void SkActive::calcDurations(int index) 
+void SkActive::calcDurations(int index)
 {
     SkAnimateBase* animate = fAnimators[index];
     SkMSec duration = animate->dur;
@@ -174,7 +174,7 @@
         if (animate->formula.size() > 0) {
             SkTDOperandArray values;
             values.setCount(count);
-            bool success = animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL, 
+            SkDEBUGCODE(bool success = ) animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL,
                 animate->getValuesType(), animate->formula);
             SkASSERT(success);
             fApply.applyValues(index, values.begin(), count, animate->getValuesType(), time);
@@ -212,7 +212,7 @@
             if (animate->formula.size() > 0) {
                 SkTDOperandArray values;
                 values.setCount(count);
-                bool success = animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL, 
+                SkDEBUGCODE(bool success = ) animate->fFieldInfo->setValue(fMaker, &values, 0, 0, NULL,
                     animate->getValuesType(), animate->formula);
                 SkASSERT(success);
                 fApply.applyValues(index, values.begin(), count, animate->getValuesType(), time);
@@ -259,7 +259,7 @@
         memset(&fSaveRestore[oldCount], 0, sizeof(fSaveRestore[0]) * (activeTotal - oldCount));
         SkASSERT(fSaveInterpolators.count() == oldCount);
         fSaveInterpolators.setCount(activeTotal);
-        memset(&fSaveInterpolators[oldCount], 0, 
+        memset(&fSaveInterpolators[oldCount], 0,
             sizeof(fSaveInterpolators[0]) * (activeTotal - oldCount));
         return true;
     }
@@ -287,7 +287,7 @@
         state.fStarted = false;
         state.fSteps = apply->steps;
         state.fTicks = 0;
-        state.fUnpostedEndEvent = (SkBool8) animate->fHasEndEvent; 
+        state.fUnpostedEndEvent = (SkBool8) animate->fHasEndEvent;
         calcDurations(index);
         setInterpolator(index, from);
     }
@@ -317,7 +317,7 @@
         if (workingSum < originalSum) {
             SkScalar originalDistance = SkScalarSqrt(originalSum);
             SkScalar workingDistance = SkScalarSqrt(workingSum);
-            existing->fState[index].fDuration = (SkMSec) SkScalarMulDiv(fState[index].fDuration, 
+            existing->fState[index].fDuration = (SkMSec) SkScalarMulDiv(fState[index].fDuration,
                 workingDistance, originalDistance);
         }
         fInterpolators[index]->reset(components, 2, SkType_Float);
@@ -376,7 +376,7 @@
     SkMSec duration = fState[index].fDuration;
     int components = animate->components();
     SkOperandInterpolator& interpolator = *fInterpolators[index];
-    interpolator.reset(components, entries == 1 ? 2 : entries, animate->getValuesType()); 
+    interpolator.reset(components, entries == 1 ? 2 : entries, animate->getValuesType());
     interpolator.setMirror(SkToBool(animate->fMirror));
     interpolator.setReset(SkToBool(animate->fReset));
     interpolator.setRepeatCount(animate->repeat);
@@ -387,7 +387,7 @@
     }
     for (int entry = 0; entry < entries; entry++) {
         int blendIndex = SkMin32(animate->blend.count() - 1, entry);
-        interpolator.setKeyFrame(entry, entry * duration / (entries - 1), from, 
+        interpolator.setKeyFrame(entry, entry * duration / (entries - 1), from,
             animate->blend[blendIndex]);
         from += components;
     }
@@ -475,12 +475,12 @@
 // the code below should only be bumping fSave, and there shouldn't be anything
 // it needs to be synchronized with
 
-// however, if there are two animates both operating on the same field, then 
-// when one replaces the other, it may make sense to pick up the old value as a starting 
+// however, if there are two animates both operating on the same field, then
+// when one replaces the other, it may make sense to pick up the old value as a starting
 // value for the new one somehow.
 
 //void SkActive::SkState::bumpSave() {
-//  if (fMode != SkApply::kMode_hold) 
+//  if (fMode != SkApply::kMode_hold)
 //      return;
 //  if (fTransition == SkApply::kTransition_reverse) {
 //      if (fSave > 0)
@@ -502,5 +502,3 @@
     }
     return result;
 }
-
-
diff --git a/src/animator/SkAnimateBase.cpp b/src/animator/SkAnimateBase.cpp
index 2c9e862..3d50144 100644
--- a/src/animator/SkAnimateBase.cpp
+++ b/src/animator/SkAnimateBase.cpp
@@ -39,8 +39,8 @@
 DEFINE_GET_MEMBER(SkAnimateBase);
 
 SkAnimateBase::SkAnimateBase() : begin(0), dur(1), repeat(SK_Scalar1),
-        fApply(NULL), fFieldInfo(NULL), fFieldOffset(0), fStart((SkMSec) -1), fTarget(NULL), 
-        fChanged(0), fDelayed(0), fDynamic(0), fHasEndEvent(0), fHasValues(0), 
+        fApply(NULL), fFieldInfo(NULL), fFieldOffset(0), fStart((SkMSec) -1), fTarget(NULL),
+        fChanged(0), fDelayed(0), fDynamic(0), fHasEndEvent(0), fHasValues(0),
         fMirror(0), fReset(0), fResetPending(0), fTargetIsScope(0) {
     blend.setCount(1);
     blend[0] = SK_Scalar1;
@@ -54,8 +54,8 @@
     }
 }
 
-int SkAnimateBase::components() { 
-    return 1; 
+int SkAnimateBase::components() {
+    return 1;
 }
 
 SkDisplayable* SkAnimateBase::deepCopy(SkAnimateMaker* maker) {
@@ -91,11 +91,7 @@
         SkDebugf("to=\"%s\" ", to.c_str());
     }
     if (begin != 0) {
-#ifdef SK_CAN_USE_FLOAT
         SkDebugf("begin=\"%g\" ", SkScalarToFloat(SkScalarDiv(begin,1000)));
-#else
-        SkDebugf("begin=\"%x\" ", SkScalarDiv(begin,1000));
-#endif
     }
 }
 #endif
@@ -135,9 +131,9 @@
     return true;
 }
 
-bool SkAnimateBase::hasExecute() const 
+bool SkAnimateBase::hasExecute() const
 {
-    return false; 
+    return false;
 }
 
 void SkAnimateBase::onEndElement(SkAnimateMaker& maker) {
@@ -163,11 +159,11 @@
     }
 }
 
-void SkAnimateBase::packARGB(SkScalar array[], int count, SkTDOperandArray* converted) 
-{ 
+void SkAnimateBase::packARGB(SkScalar array[], int count, SkTDOperandArray* converted)
+{
     SkASSERT(count == 4);
     converted->setCount(1);
-    SkColor color = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]), 
+    SkColor color = SkColorSetARGB(SkScalarRound(array[0]), SkScalarRound(array[1]),
         SkScalarRound(array[2]), SkScalarRound(array[3]));
     (*converted)[0].fS32 = color;
 }
@@ -232,8 +228,6 @@
     }
 }
 
-bool SkAnimateBase::targetNeedsInitialization() const { 
-    return false; 
+bool SkAnimateBase::targetNeedsInitialization() const {
+    return false;
 }
-
-
diff --git a/src/animator/SkAnimateField.cpp b/src/animator/SkAnimateField.cpp
index ac96bbc..0f92989 100644
--- a/src/animator/SkAnimateField.cpp
+++ b/src/animator/SkAnimateField.cpp
@@ -28,8 +28,8 @@
 SkAnimate::~SkAnimate() {
 }
 
-int SkAnimate::components() { 
-    return fComponents; 
+int SkAnimate::components() {
+    return fComponents;
 }
 
 #ifdef SK_DUMP_ENABLED
@@ -41,29 +41,19 @@
             SkDebugf("mirror=\"true\" ");
         if (fReset)
             SkDebugf("reset=\"true\" ");
-#ifdef SK_CAN_USE_FLOAT
         SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000)));
         if (repeat != SK_Scalar1)
             SkDebugf("repeat=\"%g\" ", SkScalarToFloat(repeat));
-#else
-        SkDebugf("dur=\"%x\" ", SkScalarDiv(dur,1000));
-        if (repeat != SK_Scalar1)
-            SkDebugf("repeat=\"%x\" ", repeat);
-#endif
         //if (fHasValues)
         //    SkDebugf("values=\"%s\" ", values);
         if (blend.count() != 1 || blend[0] != SK_Scalar1) {
             SkDebugf("blend=\"[");
             bool firstElem = true;
             for (int i = 0; i < blend.count(); i++) {
-                if (!firstElem) 
+                if (!firstElem)
                     SkDebugf(",");
                 firstElem = false;
-#ifdef SK_CAN_USE_FLOAT
                 SkDebugf("%g", SkScalarToFloat(blend[i]));
-#else
-                SkDebugf("%x", blend[i]);
-#endif
             }
             SkDebugf("]\" ");
         }
@@ -92,7 +82,7 @@
         SkASSERT(to.size() > 0);
         fFieldInfo->setValue(maker, &fValues, 0, 0, NULL, outType, to);
         SkASSERT(0);
-        // !!! this needs to set fComponents 
+        // !!! this needs to set fComponents
         return;
     }
     fComponents = fFieldInfo->getCount();
@@ -119,4 +109,3 @@
     fFieldInfo->setValue(maker, &fValues, fFieldOffset, max, this, outType, from);
     fFieldInfo->setValue(maker, &fValues, fComponents + fFieldOffset, max, this, outType, to);
 }
-
diff --git a/src/animator/SkAnimateMaker.cpp b/src/animator/SkAnimateMaker.cpp
index 414e728..ddde2eb 100644
--- a/src/animator/SkAnimateMaker.cpp
+++ b/src/animator/SkAnimateMaker.cpp
@@ -28,7 +28,7 @@
 } gDefaultTimeline;
 
 SkAnimateMaker::SkAnimateMaker(SkAnimator* animator, SkCanvas* canvas, SkPaint* paint)
-    : fActiveEvent(NULL), fAdjustedStart(0), fCanvas(canvas), fEnableTime(0), 
+    : fActiveEvent(NULL), fAdjustedStart(0), fCanvas(canvas), fEnableTime(0),
         fHostEventSinkID(0), fMinimumInterval((SkMSec) -1), fPaint(paint), fParentMaker(NULL),
         fTimeline(&gDefaultTimeline), fInInclude(false), fInMovie(false),
         fFirstScriptError(false), fLoaded(false), fIDs(256), fAnimator(animator)
@@ -82,7 +82,7 @@
 
 SkDisplayable* SkAnimateMaker::createInstance(const char name[], size_t len) {
     SkDisplayTypes type = SkDisplayType::GetType(this, name, len );
-    if ((int)type >= 0) 
+    if ((int)type >= 0)
         return SkDisplayType::CreateInstance(this, type);
     return NULL;
 }
@@ -124,7 +124,7 @@
     if (index < 0) {
         *fDelayed.append() = apply;
     }
-    
+
     (new SkEvent(SK_EventType_Delay, fAnimator->getSinkID()))->postTime(time);
 }
 
@@ -302,7 +302,7 @@
 #if defined SK_DEBUG
         SkDebugf("%s\n", fErrorString.c_str());
 #endif
-    } 
+    }
 }
 
 void SkAnimateMaker::setEnableTime(SkMSec appTime, SkMSec expectedTime) {
@@ -330,7 +330,7 @@
     }
 }
 
-void SkAnimateMaker::setExtraPropertyCallBack(SkDisplayTypes type, 
+void SkAnimateMaker::setExtraPropertyCallBack(SkDisplayTypes type,
         SkScriptEngine::_propertyCallBack callBack, void* userStorage) {
     SkExtras** end = fExtras.end();
     for (SkExtras** extraPtr = fExtras.begin(); extraPtr < end; extraPtr++) {
diff --git a/src/animator/SkAnimateMaker.h b/src/animator/SkAnimateMaker.h
index f20a7d4..a5abff7 100644
--- a/src/animator/SkAnimateMaker.h
+++ b/src/animator/SkAnimateMaker.h
@@ -57,16 +57,16 @@
     void dump(const char* match);
 #endif
     int dynamicProperty(SkString& nameStr, SkDisplayable**  );
-    bool find(const char* str, SkDisplayable** displayablePtr) const { 
+    bool find(const char* str, SkDisplayable** displayablePtr) const {
         return fIDs.find(str, displayablePtr);
     }
-    bool find(const char* str, size_t len, SkDisplayable** displayablePtr) const { 
+    bool find(const char* str, size_t len, SkDisplayable** displayablePtr) const {
         return fIDs.find(str, len, displayablePtr);
     }
     bool findKey(SkDisplayable* displayable, const char** string) const {
         return fIDs.findKey(displayable, string);
     }
-//  bool find(SkString& string, SkDisplayable** displayablePtr) { 
+//  bool find(SkString& string, SkDisplayable** displayablePtr) {
 //      return fIDs.find(string.c_str(), displayablePtr);
 //  }
     SkAnimator* getAnimator() { return fAnimator; }
@@ -80,7 +80,7 @@
     bool hasError() { return fError.hasError(); }
     void helperAdd(SkDisplayable* trackMe);
     void helperRemove(SkDisplayable* alreadyTracked);
-    void idsSet(const char* attrValue, size_t len, SkDisplayable* displayable) { 
+    void idsSet(const char* attrValue, size_t len, SkDisplayable* displayable) {
         fIDs.set(attrValue, len, displayable); }
 //  void loadMovies();
     void notifyInval();
@@ -158,4 +158,3 @@
 };
 
 #endif // SkAnimateMaker_DEFINED
-
diff --git a/src/animator/SkAnimateSet.cpp b/src/animator/SkAnimateSet.cpp
index d0b4dfc..f153b16 100644
--- a/src/animator/SkAnimateSet.cpp
+++ b/src/animator/SkAnimateSet.cpp
@@ -32,18 +32,14 @@
 DEFINE_GET_MEMBER(SkSet);
 
 SkSet::SkSet() {
-    dur = 1; 
+    dur = 1;
 }
 
 #ifdef SK_DUMP_ENABLED
 void SkSet::dump(SkAnimateMaker* maker) {
     INHERITED::dump(maker);
     if (dur != 1) {
-#ifdef SK_CAN_USE_FLOAT
         SkDebugf("dur=\"%g\" ", SkScalarToFloat(SkScalarDiv(dur,1000)));
-#else
-        SkDebugf("dur=\"%x\" ", SkScalarDiv(dur,1000));
-#endif
     }
     //don't want double />\n's
     SkDebugf("/>\n");
@@ -52,7 +48,7 @@
 #endif
 
 void SkSet::refresh(SkAnimateMaker& maker) {
-    fFieldInfo->setValue(maker, &fValues, 0, fFieldInfo->fCount, NULL, 
+    fFieldInfo->setValue(maker, &fValues, 0, fFieldInfo->fCount, NULL,
         fFieldInfo->getType(), to);
 }
 
diff --git a/src/animator/SkAnimateSet.h b/src/animator/SkAnimateSet.h
index c735a08..b8f7bf5 100644
--- a/src/animator/SkAnimateSet.h
+++ b/src/animator/SkAnimateSet.h
@@ -25,4 +25,3 @@
 };
 
 #endif // SkAnimateSet_DEFINED
-
diff --git a/src/animator/SkAnimator.cpp b/src/animator/SkAnimator.cpp
index c1fba65..09fccd9 100644
--- a/src/animator/SkAnimator.cpp
+++ b/src/animator/SkAnimator.cpp
@@ -34,7 +34,7 @@
     #define _static static
 #endif
 
-_static const char gMathPrimerText[] = 
+_static const char gMathPrimerText[] =
 "<screenplay>"
     "<Math id=\"Math\"/>"
     "<Number id=\"Number\"/>"
@@ -108,9 +108,9 @@
     state.fX = x;
     state.fY = y;
     fMaker->fEnableTime = fMaker->getAppTime();
-    bool result = fMaker->fEvents.doEvent(*fMaker, 
-        clickState == 0 ? SkDisplayEvent::kMouseDown : 
-        clickState == 1 ? SkDisplayEvent::kMouseDrag : 
+    bool result = fMaker->fEvents.doEvent(*fMaker,
+        clickState == 0 ? SkDisplayEvent::kMouseDown :
+        clickState == 1 ? SkDisplayEvent::kMouseDrag :
         SkDisplayEvent::kMouseUp, &state);
     fMaker->notifyInval();
     return result;
@@ -160,7 +160,7 @@
     SkPaint paint;
     return draw(canvas, &paint, time);
 }
-    
+
 #ifdef SK_DEBUG
 void SkAnimator::eventDone(const SkEvent& ) {
 }
@@ -231,7 +231,7 @@
     return info->getArrayValue(element, index, operand);
 }
 
-int32_t SkAnimator::getArrayInt(const SkDisplayable* ae, 
+int32_t SkAnimator::getArrayInt(const SkDisplayable* ae,
         const SkMemberInfo* ai, int index) {
     SkOperand operand;
     bool result = getArrayCommon(ae, ai, index, &operand, SkType_Int);
@@ -248,7 +248,7 @@
     return getArrayInt(element, field, index);
 }
 
-SkScalar SkAnimator::getArrayScalar(const SkDisplayable* ae, 
+SkScalar SkAnimator::getArrayScalar(const SkDisplayable* ae,
         const SkMemberInfo* ai, int index) {
     SkOperand operand;
     bool result = getArrayCommon(ae, ai, index, &operand, SkType_Float);
@@ -265,7 +265,7 @@
     return getArrayScalar(element, field, index);
 }
 
-const char* SkAnimator::getArrayString(const SkDisplayable* ae, 
+const char* SkAnimator::getArrayString(const SkDisplayable* ae,
         const SkMemberInfo* ai, int index) {
     SkOperand operand;
     bool result = getArrayCommon(ae, ai, index, &operand, SkType_String);
@@ -360,7 +360,7 @@
     return getScalar(element, field);
 }
 
-const char* SkAnimator::getString(const SkDisplayable* ae, 
+const char* SkAnimator::getString(const SkDisplayable* ae,
         const SkMemberInfo* ai) {
     const SkDisplayable* element = (const SkDisplayable*) ae;
     const SkMemberInfo* info = (const SkMemberInfo*) ai;
@@ -501,11 +501,11 @@
 }
 
 SkEventSinkID SkAnimator::getHostEventSinkID() const {
-    return fMaker->fHostEventSinkID; 
+    return fMaker->fHostEventSinkID;
 }
 
 void SkAnimator::setHostEventSinkID(SkEventSinkID target) {
-    fMaker->fHostEventSinkID = target; 
+    fMaker->fHostEventSinkID = target;
 }
 
 void SkAnimator::onSetHostHandler(Handler ) {
@@ -530,7 +530,7 @@
     SkTypedArray tArray(SkType_Int);
     tArray.setCount(num);
     for (int i = 0; i < num; i++) {
-        SkOperand op;   
+        SkOperand op;
         op.fS32 = array[i];
         tArray[i] = op;
     }
@@ -546,7 +546,7 @@
     SkDisplayTypes type = element->getType();
     if (type == SkType_Array) {
         SkDisplayArray* dispArray = (SkDisplayArray*) element;
-        dispArray->values = array;  
+        dispArray->values = array;
         return true;
     }
     else
@@ -614,9 +614,9 @@
     return setScalar(element, field, scalar);
 }
 
-bool SkAnimator::setString(SkDisplayable* element, 
+bool SkAnimator::setString(SkDisplayable* element,
         const SkMemberInfo* info, const char* str) {
-    // !!! until this is fixed, can't call script with global references from here 
+    // !!! until this is fixed, can't call script with global references from here
     info->setValue(*fMaker, NULL, 0, info->fCount, element, info->getType(), str, strlen(str));
     return true;
 }
@@ -700,6 +700,3 @@
 
 void SkAnimator::Term() {
 }
-
-
-
diff --git a/src/animator/SkAnimatorScript.cpp b/src/animator/SkAnimatorScript.cpp
index 6aae006..67c53c7 100644
--- a/src/animator/SkAnimatorScript.cpp
+++ b/src/animator/SkAnimatorScript.cpp
@@ -64,8 +64,8 @@
     if (SkDisplayType::IsEnum(&maker, type)) {
         // !!! for SpiderMonkey, iterate through the enum values, and map them to globals
         const SkDisplayEnumMap& map = GetEnumValues(type);
-        propertyCallBack(EvalEnum, (void*) map.fValues); 
-    } 
+        propertyCallBack(EvalEnum, (void*) map.fValues);
+    }
     for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) {
         SkExtras* extra = *extraPtr;
         if (extra->fExtraCallBack)
@@ -116,7 +116,7 @@
             SkDisplayString* boxedValue = new SkDisplayString(*scriptValue->fOperand.fString);
             displayable = boxedValue;
             } break;
-        case SkType_Displayable: 
+        case SkType_Displayable:
             scriptValue->fOperand.fObject = scriptValue->fOperand.fDisplayable;
             scriptValue->fType = SkType_Displayable;
             return true;
@@ -130,7 +130,7 @@
     return true;
 }
 
-bool SkAnimatorScript::Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+bool SkAnimatorScript::Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* eng, SkScriptValue* value) {
     if (SK_LITERAL_STR_EQUAL("eval", function, len) == false)
         return false;
@@ -152,7 +152,7 @@
     const char* tokens = (const char*) callBack;
     value->fType = SkType_Int;
     if (MapEnums(tokens, token, len, (int*)&value->fOperand.fS32))
-        return true; 
+        return true;
     return false;
 }
 
@@ -160,7 +160,7 @@
     SkAnimatorScript* engine = (SkAnimatorScript*) user;
     SkTDict<SkDisplayable*>* ids = &engine->fMaker.fIDs;
     SkDisplayable* displayable;
-    bool success = ids->find(token, len, &displayable); 
+    bool success = ids->find(token, len, &displayable);
     if (success == false) {
         displayable = engine->fWorking;
         if (SK_LITERAL_STR_EQUAL("parent", token, len)) {
@@ -176,7 +176,7 @@
         if (displayable && EvalMember(token, len, displayable, engine, value))
             return true;
         value->fOperand.fString = NULL;
-        value->fType = SkType_String;   
+        value->fType = SkType_String;
     } else {
         SkDisplayable* working = engine->fWorking;
         value->fOperand.fDisplayable = displayable;
@@ -199,7 +199,7 @@
     return false;
 }
 
-bool SkAnimatorScript::EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+bool SkAnimatorScript::EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* eng, SkScriptValue* value) {
     if (SK_LITERAL_STR_EQUAL("rgb", function, len) == false)
         return false;
@@ -218,7 +218,7 @@
     return true;
 }
 
-bool SkAnimatorScript::EvalMemberCommon(SkScriptEngine* engine, const SkMemberInfo* info, 
+bool SkAnimatorScript::EvalMemberCommon(SkScriptEngine* engine, const SkMemberInfo* info,
         SkDisplayable* displayable, SkScriptValue* value) {
     SkDisplayTypes original;
     SkDisplayTypes type = original = (SkDisplayTypes) info->getType();
@@ -232,10 +232,10 @@
         case SkType_MSec:
         case SkType_Float:
             SkASSERT(info->getCount() == 1);
-            if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) 
+            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 = SkScalarDiv((SkScalar) value->fOperand.fS32, 1000); // dividing two ints is the same as dividing two scalars
                 type = SkType_Float;
             }
             break;
@@ -269,7 +269,7 @@
     return true;
 }
 
-bool SkAnimatorScript::EvalMember(const char* member, size_t len, void* object, void* eng, 
+bool SkAnimatorScript::EvalMember(const char* member, size_t len, void* object, void* eng,
         SkScriptValue* value) {
     SkScriptEngine* engine = (SkScriptEngine*) eng;
     SkDisplayable* displayable = (SkDisplayable*) object;
@@ -292,7 +292,7 @@
     return EvalMemberCommon(engine, info, displayable, value);
 }
 
-bool SkAnimatorScript::EvalMemberFunction(const char* member, size_t len, void* object, 
+bool SkAnimatorScript::EvalMemberFunction(const char* member, size_t len, void* object,
         SkTDArray<SkScriptValue>& params, void* eng, SkScriptValue* value) {
     SkScriptEngine* engine = (SkScriptEngine*) eng;
     SkDisplayable* displayable = (SkDisplayable*) object;
@@ -303,7 +303,7 @@
         SkASSERT(0);
         return false;
     }
-    displayable->executeFunction(displayable, info->functionIndex(), params, info->getType(), 
+    displayable->executeFunction(displayable, info->functionIndex(), params, info->getType(),
         value);
     return EvalMemberCommon(engine, info, displayable, value);
 }
@@ -355,7 +355,7 @@
 }
 
 const SkDisplayEnumMap& SkAnimatorScript::GetEnumValues(SkDisplayTypes type) {
-    int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type, 
+    int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type,
         sizeof(SkDisplayEnumMap));
     SkASSERT(index >= 0);
     return gEnumMaps[index];
@@ -369,7 +369,7 @@
     return true;
 }
 
-bool SkAnimatorScript::IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+bool SkAnimatorScript::IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* eng, SkScriptValue* value) {
     if (SK_LITERAL_STR_EQUAL(function, "isFinite", len) == false)
         return false;
@@ -379,12 +379,12 @@
     SkDisplayTypes type = scriptValue->fType;
     SkScalar scalar = scriptValue->fOperand.fScalar;
     value->fType = SkType_Int;
-    value->fOperand.fS32 = type == SkType_Float ? SkScalarIsNaN(scalar) == false && 
+    value->fOperand.fS32 = type == SkType_Float ? SkScalarIsNaN(scalar) == false &&
         SkScalarAbs(scalar) != SK_ScalarInfinity    : type == SkType_Int;
     return true;
 }
 
-bool SkAnimatorScript::IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+bool SkAnimatorScript::IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* eng, SkScriptValue* value) {
     if (SK_LITERAL_STR_EQUAL("isNaN", function, len) == false)
         return false;
@@ -429,7 +429,7 @@
     SkTDict<SkDisplayable*>* ids = (SkTDict<SkDisplayable*>*) user;
     SkDisplayable* displayable = (SkDisplayable*) object;
     const char* key;
-    bool success = ids->findKey(displayable, &key); 
+    bool success = ids->findKey(displayable, &key);
     if (success == false)
         return false;
     value->fOperand.fString =   new SkString(key);
@@ -466,7 +466,7 @@
             } break;
         default: {
             const char* id = NULL;
-            bool success = maker->findKey(displayable, &id);
+            SkDEBUGCODE(bool success = ) maker->findKey(displayable, &id);
             SkASSERT(success);
             scriptValue->fOperand.fString = SkNEW_ARGS(SkString, (id));
             type = SkType_String;
@@ -480,7 +480,7 @@
 
 #include "SkAnimator.h"
 
-static const char scriptTestSetup[]  = 
+static const char scriptTestSetup[]  =
 "<screenplay>\n"
     "<text id='label' text='defg'/>\n"
     "<add id='addLabel' use='label'/>\n"
@@ -592,5 +592,3 @@
 }
 
 #endif
-
-
diff --git a/src/animator/SkAnimatorScript.h b/src/animator/SkAnimatorScript.h
index a23f90d..8589388 100644
--- a/src/animator/SkAnimatorScript.h
+++ b/src/animator/SkAnimatorScript.h
@@ -27,8 +27,8 @@
     SkAnimatorScript(SkAnimateMaker& , SkDisplayable* , SkDisplayTypes type);
     ~SkAnimatorScript();
     bool evaluate(const char* script, SkScriptValue* , SkDisplayTypes type);
-    void track(SkDisplayable* displayable) { 
-        SkASSERT(fTrackDisplayable.find(displayable) < 0);  
+    void track(SkDisplayable* displayable) {
+        SkASSERT(fTrackDisplayable.find(displayable) < 0);
         *fTrackDisplayable.append() = displayable; }
     static bool EvaluateDisplayable(SkAnimateMaker& , SkDisplayable* , const char* script, SkDisplayable** );
     static bool EvaluateFloat(SkAnimateMaker& , SkDisplayable* , const char* script, SkScalar* );
@@ -38,24 +38,24 @@
     static bool MapEnums(const char* ptr, const char* match, size_t len, int* value);
 protected:
     static bool Box(void* user, SkScriptValue* );
-    static bool Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+    static bool Eval(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* callBack, SkScriptValue* );
     static bool EvalEnum(const char* token, size_t len, void* callBack, SkScriptValue* );
     static bool EvalID(const char* token, size_t len, void* callBack, SkScriptValue* );
-    static bool EvalMember(const char* member, size_t len, void* object, void* eng, 
+    static bool EvalMember(const char* member, size_t len, void* object, void* eng,
         SkScriptValue* value);
-    static bool EvalMemberCommon(SkScriptEngine* , const SkMemberInfo* info, 
+    static bool EvalMemberCommon(SkScriptEngine* , const SkMemberInfo* info,
         SkDisplayable* displayable, SkScriptValue* value);
-    static bool EvalMemberFunction(const char* member, size_t len, void* object, 
+    static bool EvalMemberFunction(const char* member, size_t len, void* object,
         SkTDArray<SkScriptValue>& params, void* user, SkScriptValue* value);
     static bool EvalNamedColor(const char* token, size_t len, void* callBack, SkScriptValue* );
-    static bool EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+    static bool EvalRGB(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* callBack, SkScriptValue* );
-    static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type); 
+    static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type);
     static bool Infinity(const char* token, size_t len, void* callBack, SkScriptValue* );
-    static bool IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+    static bool IsFinite(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* callBack, SkScriptValue* );
-    static bool IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params, 
+    static bool IsNaN(const char* function, size_t len, SkTDArray<SkScriptValue>& params,
         void* callBack, SkScriptValue* );
     static bool NaN(const char* token, size_t len, void* callBack, SkScriptValue* );
     static bool Unbox(void* , SkScriptValue* scriptValue);
@@ -73,4 +73,3 @@
 };
 
 #endif // SkAnimatorScript_DEFINED
-
diff --git a/src/animator/SkAnimatorScript2.cpp b/src/animator/SkAnimatorScript2.cpp
index ae51fdc..80ae0c6 100644
--- a/src/animator/SkAnimatorScript2.cpp
+++ b/src/animator/SkAnimatorScript2.cpp
@@ -17,29 +17,29 @@
 #include "SkScriptCallBack.h"
 
 static const SkDisplayEnumMap gEnumMaps[] = {
-	{ SkType_AddMode, "indirect|immediate" },
-	{ SkType_Align, "left|center|right" },
-	{ SkType_ApplyMode, "immediate|once" },
-	{ SkType_ApplyTransition, "reverse" },
-	{ SkType_BitmapEncoding, "jpeg|png" },
-	{ SkType_BitmapFormat, "none|A1|A8|Index8|RGB16|RGB32" },
-	{ SkType_Boolean, "false|true" },
-	{ SkType_Cap, "butt|round|square" },
-	{ SkType_EventCode, "none|up|down|left|right|back|end|OK|send|leftSoftKey|rightSoftKey|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash" },
-	{ SkType_EventKind, "none|keyChar|keyPress|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" },
-	{ SkType_EventMode, "deferred|immediate" },
-	{ SkType_FillType, "winding|evenOdd" },
-	{ SkType_FilterType, "none|bilinear" },
-	{ SkType_FromPathMode, "normal|angle|position" },
-	{ SkType_Join, "miter|round|blunt" },
-	{ SkType_MaskFilterBlurStyle, "normal|solid|outer|inner" },
-	{ SkType_PathDirection, "cw|ccw" },
-	{ SkType_Style, "fill|stroke|strokeAndFill" },
-	{ SkType_TextBoxAlign, "start|center|end" },
-	{ SkType_TextBoxMode, "oneLine|lineBreak" },
-	{ SkType_TileMode, "clamp|repeat|mirror" },
-	{ SkType_Xfermode, "clear|src|dst|srcOver|dstOver|srcIn|dstIn|srcOut|dstOut|"
-		"srcATop|dstATop|xor|darken|lighten" },
+    { SkType_AddMode, "indirect|immediate" },
+    { SkType_Align, "left|center|right" },
+    { SkType_ApplyMode, "immediate|once" },
+    { SkType_ApplyTransition, "reverse" },
+    { SkType_BitmapEncoding, "jpeg|png" },
+    { SkType_BitmapFormat, "none|A1|A8|Index8|RGB16|RGB32" },
+    { SkType_Boolean, "false|true" },
+    { SkType_Cap, "butt|round|square" },
+    { SkType_EventCode, "none|up|down|left|right|back|end|OK|send|leftSoftKey|rightSoftKey|key0|key1|key2|key3|key4|key5|key6|key7|key8|key9|star|hash" },
+    { SkType_EventKind, "none|keyChar|keyPress|mouseDown|mouseDrag|mouseMove|mouseUp|onEnd|onLoad|user" },
+    { SkType_EventMode, "deferred|immediate" },
+    { SkType_FillType, "winding|evenOdd" },
+    { SkType_FilterType, "none|bilinear" },
+    { SkType_FromPathMode, "normal|angle|position" },
+    { SkType_Join, "miter|round|blunt" },
+    { SkType_MaskFilterBlurStyle, "normal|solid|outer|inner" },
+    { SkType_PathDirection, "cw|ccw" },
+    { SkType_Style, "fill|stroke|strokeAndFill" },
+    { SkType_TextBoxAlign, "start|center|end" },
+    { SkType_TextBoxMode, "oneLine|lineBreak" },
+    { SkType_TileMode, "clamp|repeat|mirror" },
+    { SkType_Xfermode, "clear|src|dst|srcOver|dstOver|srcIn|dstIn|srcOut|dstOut|"
+        "srcATop|dstATop|xor|darken|lighten" },
 };
 
 static int gEnumMapCount = SK_ARRAY_COUNT(gEnumMaps);
@@ -47,577 +47,576 @@
 
 class SkAnimatorScript_Box : public SkScriptCallBackConvert {
 public:
-	SkAnimatorScript_Box() {}
+    SkAnimatorScript_Box() {}
 
-	~SkAnimatorScript_Box() {
-		for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++)
-			delete *dispPtr;
-	}
+    ~SkAnimatorScript_Box() {
+        for (SkDisplayable** dispPtr = fTrackDisplayable.begin(); dispPtr < fTrackDisplayable.end(); dispPtr++)
+            delete *dispPtr;
+    }
 
-	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
-		SkDisplayable* displayable;
-		switch (type) {
-			case SkOperand2::kArray: {
-				SkDisplayArray* boxedValue = new SkDisplayArray(*operand->fArray);
-				displayable = boxedValue;
-				} break;
-			case SkOperand2::kS32: {
-				SkDisplayInt* boxedValue = new SkDisplayInt;
-				displayable = boxedValue;
-				boxedValue->value = operand->fS32;
-				} break;
-			case SkOperand2::kScalar: {
-				SkDisplayFloat* boxedValue = new SkDisplayFloat;
-				displayable = boxedValue;
-				boxedValue->value = operand->fScalar;
-				} break;
-			case SkOperand2::kString: {
-				SkDisplayString* boxedValue = new SkDisplayString(*operand->fString);
-				displayable = boxedValue;
-				} break;
-			case SkOperand2::kObject: 
-				return true;
-			default:
-				SkASSERT(0);
-				return false;
-		}
-		track(displayable);
-		operand->fObject = (void*) displayable;
-		return true;
-	}
+    virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
+        SkDisplayable* displayable;
+        switch (type) {
+            case SkOperand2::kArray: {
+                SkDisplayArray* boxedValue = new SkDisplayArray(*operand->fArray);
+                displayable = boxedValue;
+                } break;
+            case SkOperand2::kS32: {
+                SkDisplayInt* boxedValue = new SkDisplayInt;
+                displayable = boxedValue;
+                boxedValue->value = operand->fS32;
+                } break;
+            case SkOperand2::kScalar: {
+                SkDisplayFloat* boxedValue = new SkDisplayFloat;
+                displayable = boxedValue;
+                boxedValue->value = operand->fScalar;
+                } break;
+            case SkOperand2::kString: {
+                SkDisplayString* boxedValue = new SkDisplayString(*operand->fString);
+                displayable = boxedValue;
+                } break;
+            case SkOperand2::kObject:
+                return true;
+            default:
+                SkASSERT(0);
+                return false;
+        }
+        track(displayable);
+        operand->fObject = (void*) displayable;
+        return true;
+    }
 
-	virtual SkOperand2::OpType getReturnType(int index) { 
-		return SkOperand2::kObject; 
-	}
+    virtual SkOperand2::OpType getReturnType(int index) {
+        return SkOperand2::kObject;
+    }
 
-	virtual Type getType() const { 
-		return kBox; 
-	}
+    virtual Type getType() const {
+        return kBox;
+    }
 
-	void track(SkDisplayable* displayable) { 
-		SkASSERT(fTrackDisplayable.find(displayable) < 0);  
-		*fTrackDisplayable.append() = displayable; 
-	}
+    void track(SkDisplayable* displayable) {
+        SkASSERT(fTrackDisplayable.find(displayable) < 0);
+        *fTrackDisplayable.append() = displayable;
+    }
 
-	SkTDDisplayableArray fTrackDisplayable;
+    SkTDDisplayableArray fTrackDisplayable;
 };
 
 
 class SkAnimatorScript_Enum : public SkScriptCallBackProperty {
 public:
-	SkAnimatorScript_Enum(const char* tokens) : fTokens(tokens) {}
+    SkAnimatorScript_Enum(const char* tokens) : fTokens(tokens) {}
 
-	virtual bool getConstValue(const char* name, int len, SkOperand2* value) { 
-		return SkAnimatorScript2::MapEnums(fTokens, name, len, &value->fS32);
-	}
+    virtual bool getConstValue(const char* name, int len, SkOperand2* value) {
+        return SkAnimatorScript2::MapEnums(fTokens, name, len, &value->fS32);
+    }
 
 private:
-	const char* fTokens;
+    const char* fTokens;
 };
 
-	// !!! if type is string, call invoke
-	// if any other type, return original value
-		// distinction is undone: could do this by returning index == 0 only if param is string
-		// still, caller of getParamTypes will attempt to convert param to string (I guess)
+    // !!! if type is string, call invoke
+    // if any other type, return original value
+        // distinction is undone: could do this by returning index == 0 only if param is string
+        // still, caller of getParamTypes will attempt to convert param to string (I guess)
 class SkAnimatorScript_Eval : public SkScriptCallBackFunction {
 public:
-	SkAnimatorScript_Eval(SkAnimatorScript2* engine) : fEngine(engine) {}
+    SkAnimatorScript_Eval(SkAnimatorScript2* engine) : fEngine(engine) {}
 
-	virtual bool getIndex(const char* name, int len, size_t* result) {
-		if (SK_LITERAL_STR_EQUAL("eval", name, len) != 0)
-			return false;
-		*result = 0;
-		return true;
-	}
-	
-	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
-		types->setCount(1);
-		SkOperand2::OpType* type = types->begin();
-		type[0] = SkOperand2::kString;
-	}
+    virtual bool getIndex(const char* name, int len, size_t* result) {
+        if (SK_LITERAL_STR_EQUAL("eval", name, len) != 0)
+            return false;
+        *result = 0;
+        return true;
+    }
 
-	virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
-		SkAnimatorScript2 engine(fEngine->getMaker(), fEngine->getWorking(), 
-			SkAnimatorScript2::ToDisplayType(fEngine->getReturnType()));
-		SkOperand2* op = params->begin();
-		const char* script = op->fString->c_str();
-		SkScriptValue2 value;
-		return engine.evaluateScript(&script, &value);
-		SkASSERT(value.fType == fEngine->getReturnType());
-		*answer = value.fOperand;
-		// !!! incomplete ?
-		return true;
-	}
+    virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
+        types->setCount(1);
+        SkOperand2::OpType* type = types->begin();
+        type[0] = SkOperand2::kString;
+    }
+
+    virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
+        SkAnimatorScript2 engine(fEngine->getMaker(), fEngine->getWorking(),
+            SkAnimatorScript2::ToDisplayType(fEngine->getReturnType()));
+        SkOperand2* op = params->begin();
+        const char* script = op->fString->c_str();
+        SkScriptValue2 value;
+        return engine.evaluateScript(&script, &value);
+        SkASSERT(value.fType == fEngine->getReturnType());
+        *answer = value.fOperand;
+        // !!! incomplete ?
+        return true;
+    }
 
 private:
-	SkAnimatorScript2* fEngine;
+    SkAnimatorScript2* fEngine;
 };
 
 class SkAnimatorScript_ID : public SkScriptCallBackProperty {
 public:
-	SkAnimatorScript_ID(SkAnimatorScript2* engine) : fEngine(engine) {}
+    SkAnimatorScript_ID(SkAnimatorScript2* engine) : fEngine(engine) {}
 
-	virtual bool getIndex(const char* token, int len, size_t* result) {  
-		SkDisplayable* displayable;
-		bool success = fEngine->getMaker().find(token, len, &displayable);
-		if (success == false) {
-			*result = 0;
-		} else {
-			*result = (size_t) displayable;
-			SkDisplayable* working = fEngine->getWorking();
-			if (displayable->canContainDependents() && working && working->isAnimate()) {
-				SkAnimateBase* animator = (SkAnimateBase*) working;
-				if (animator->isDynamic()) {
-					SkDisplayDepend* depend = (SkDisplayDepend* ) displayable;
-					depend->addDependent(working);
-				}
-			}
-		}
-		return true; 
-	}
+    virtual bool getIndex(const char* token, int len, size_t* result) {
+        SkDisplayable* displayable;
+        bool success = fEngine->getMaker().find(token, len, &displayable);
+        if (success == false) {
+            *result = 0;
+        } else {
+            *result = (size_t) displayable;
+            SkDisplayable* working = fEngine->getWorking();
+            if (displayable->canContainDependents() && working && working->isAnimate()) {
+                SkAnimateBase* animator = (SkAnimateBase*) working;
+                if (animator->isDynamic()) {
+                    SkDisplayDepend* depend = (SkDisplayDepend* ) displayable;
+                    depend->addDependent(working);
+                }
+            }
+        }
+        return true;
+    }
 
-	virtual bool getResult(size_t ref, SkOperand2* answer) {
-		answer->fObject = (void*) ref;
-		return true;
-	}
+    virtual bool getResult(size_t ref, SkOperand2* answer) {
+        answer->fObject = (void*) ref;
+        return true;
+    }
 
-	virtual SkOperand2::OpType getReturnType(size_t index) {
-		return index == 0 ? SkOperand2::kString : SkOperand2::kObject;
-	}
+    virtual SkOperand2::OpType getReturnType(size_t index) {
+        return index == 0 ? SkOperand2::kString : SkOperand2::kObject;
+    }
 
 private:
-	SkAnimatorScript2* fEngine;
+    SkAnimatorScript2* fEngine;
 };
 
 
 class SkAnimatorScript_Member : public SkScriptCallBackMember {
 public:
 
-	SkAnimatorScript_Member(SkAnimatorScript2* engine) : fEngine(engine) {}
+    SkAnimatorScript_Member(SkAnimatorScript2* engine) : fEngine(engine) {}
 
-	bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
-		SkDisplayable* displayable = (SkDisplayable*) object;
-		SkString name(member, len);
-		SkDisplayable* named = displayable->contains(name);
-		if (named) {
-			ref->fType = SkOperand2::kObject;
-			ref->fOperand.fObject = named;
-			return true;
-		}
-		const SkMemberInfo* info = displayable->getMember(name.c_str());
-		if (info == NULL)
-			return false;	// !!! add additional error info?
-		ref->fType = SkAnimatorScript2::ToOpType(info->getType());
-		ref->fOperand.fObject = (void*) info;
-		return true;
-	}
+    bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
+        SkDisplayable* displayable = (SkDisplayable*) object;
+        SkString name(member, len);
+        SkDisplayable* named = displayable->contains(name);
+        if (named) {
+            ref->fType = SkOperand2::kObject;
+            ref->fOperand.fObject = named;
+            return true;
+        }
+        const SkMemberInfo* info = displayable->getMember(name.c_str());
+        if (info == NULL)
+            return false;    // !!! add additional error info?
+        ref->fType = SkAnimatorScript2::ToOpType(info->getType());
+        ref->fOperand.fObject = (void*) info;
+        return true;
+    }
 
-	bool invoke(size_t ref, void* object, SkOperand2* value) {
-		const SkMemberInfo* info = (const SkMemberInfo* ) ref;
-		SkDisplayable* displayable = (SkDisplayable*) object;
-		if (info->fType == SkType_MemberProperty) {
-			if (displayable->getProperty2(info->propertyIndex(), value) == false) {
-				return false;
-			}
-		}
-		return fEngine->evalMemberCommon(info, displayable, value);
-	}
+    bool invoke(size_t ref, void* object, SkOperand2* value) {
+        const SkMemberInfo* info = (const SkMemberInfo* ) ref;
+        SkDisplayable* displayable = (SkDisplayable*) object;
+        if (info->fType == SkType_MemberProperty) {
+            if (displayable->getProperty2(info->propertyIndex(), value) == false) {
+                return false;
+            }
+        }
+        return fEngine->evalMemberCommon(info, displayable, value);
+    }
 
-	SkAnimatorScript2* fEngine;
+    SkAnimatorScript2* fEngine;
 };
 
 
 class SkAnimatorScript_MemberFunction : public SkScriptCallBackMemberFunction {
 public:
-	SkAnimatorScript_MemberFunction(SkAnimatorScript2* engine) : fEngine(engine) {}
+    SkAnimatorScript_MemberFunction(SkAnimatorScript2* engine) : fEngine(engine) {}
 
-	bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
-		SkDisplayable* displayable = (SkDisplayable*) object;
-		SkString name(member, len);
-		const SkMemberInfo* info = displayable->getMember(name.c_str());
-		if (info == NULL || info->fType != SkType_MemberFunction)
-			return false;	// !!! add additional error info?
-		ref->fType = SkAnimatorScript2::ToOpType(info->getType());
-		ref->fOperand.fObject = (void*) info;
-		return true;
-	}
+    bool getMemberReference(const char* member, size_t len, void* object, SkScriptValue2* ref) {
+        SkDisplayable* displayable = (SkDisplayable*) object;
+        SkString name(member, len);
+        const SkMemberInfo* info = displayable->getMember(name.c_str());
+        if (info == NULL || info->fType != SkType_MemberFunction)
+            return false;    // !!! add additional error info?
+        ref->fType = SkAnimatorScript2::ToOpType(info->getType());
+        ref->fOperand.fObject = (void*) info;
+        return true;
+    }
 
-	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
-		types->setCount(3);
-		SkOperand2::OpType* type = types->begin();
-		type[0] = type[1] = type[2] = SkOperand2::kS32;
-	}
+    virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
+        types->setCount(3);
+        SkOperand2::OpType* type = types->begin();
+        type[0] = type[1] = type[2] = SkOperand2::kS32;
+    }
 
-	bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value)
-	{
-		const SkMemberInfo* info = (const SkMemberInfo* ) ref;
-		SkDisplayable* displayable = (SkDisplayable*) object;
-		displayable->executeFunction2(displayable, info->functionIndex(), params, info->getType(), 
-			value);
-		return fEngine->evalMemberCommon(info, displayable, value);
-	}
+    bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value)
+    {
+        const SkMemberInfo* info = (const SkMemberInfo* ) ref;
+        SkDisplayable* displayable = (SkDisplayable*) object;
+        displayable->executeFunction2(displayable, info->functionIndex(), params, info->getType(),
+            value);
+        return fEngine->evalMemberCommon(info, displayable, value);
+    }
 
-	SkAnimatorScript2* fEngine;
+    SkAnimatorScript2* fEngine;
 };
 
 
 class SkAnimatorScript_NamedColor : public SkScriptCallBackProperty {
 public:
-	virtual bool getConstValue(const char* name, int len, SkOperand2* value) {
-		return SkParse::FindNamedColor(name, len, (SkColor*) &value->fS32) != NULL;
-	}
+    virtual bool getConstValue(const char* name, int len, SkOperand2* value) {
+        return SkParse::FindNamedColor(name, len, (SkColor*) &value->fS32) != NULL;
+    }
 };
 
 
 class SkAnimatorScript_RGB : public SkScriptCallBackFunction {
 public:
-	virtual bool getIndex(const char* name, int len, size_t* result) {
-		if (SK_LITERAL_STR_EQUAL("rgb", name, len) != 0)
-			return false;
-		*result = 0;
-		return true;
-	}
+    virtual bool getIndex(const char* name, int len, size_t* result) {
+        if (SK_LITERAL_STR_EQUAL("rgb", name, len) != 0)
+            return false;
+        *result = 0;
+        return true;
+    }
 
-	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
-		types->setCount(3);
-		SkOperand2::OpType* type = types->begin();
-		type[0] = type[1] = type[2] = SkOperand2::kS32;
-	}
+    virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) {
+        types->setCount(3);
+        SkOperand2::OpType* type = types->begin();
+        type[0] = type[1] = type[2] = SkOperand2::kS32;
+    }
 
-	virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
-		SkASSERT(index == 0);
-		unsigned result = 0xFF000000;
-		int shift = 16;
-		for (int index = 0; index < 3; index++) {
-			result |= SkClampMax(params->begin()[index].fS32, 255) << shift;
-			shift -= 8;
-		}
-		answer->fS32 = result;
-		return true;
-	}
+    virtual bool invoke(size_t index, SkOpArray* params, SkOperand2* answer) {
+        SkASSERT(index == 0);
+        unsigned result = 0xFF000000;
+        int shift = 16;
+        for (int index = 0; index < 3; index++) {
+            result |= SkClampMax(params->begin()[index].fS32, 255) << shift;
+            shift -= 8;
+        }
+        answer->fS32 = result;
+        return true;
+    }
 
 };
 
 
 class SkAnimatorScript_Unbox : public SkScriptCallBackConvert {
 public:
-	SkAnimatorScript_Unbox(SkAnimatorScript2* engine) : fEngine(engine) {}
+    SkAnimatorScript_Unbox(SkAnimatorScript2* engine) : fEngine(engine) {}
 
-	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
-		SkASSERT(type == SkOperand2::kObject);
-		SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
-		switch (displayable->getType()) {
-			case SkType_Array: {
-				SkDisplayArray* boxedValue = (SkDisplayArray*) displayable;
-				operand->fArray = new SkOpArray(SkAnimatorScript2::ToOpType(boxedValue->values.getType()));
-				int count = boxedValue->values.count();
-				operand->fArray->setCount(count);
-				memcpy(operand->fArray->begin(), boxedValue->values.begin(), count * sizeof(SkOperand2));
-				fEngine->track(operand->fArray);
-				} break;
-			case SkType_Boolean: {
-				SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable;
-				operand->fS32 = boxedValue->value;
-				} break;
-			case SkType_Int: {
-				SkDisplayInt* boxedValue = (SkDisplayInt*) displayable;
-				operand->fS32 = boxedValue->value;
-				} break;
-			case SkType_Float: {
-				SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable;
-				operand->fScalar = boxedValue->value;
-				} break;
-			case SkType_String: {
-				SkDisplayString* boxedValue = (SkDisplayString*) displayable;
-				operand->fString = SkNEW_ARGS(SkString, (boxedValue->value));
-				} break;
-			default: {
-				const char* id;
-				bool success = fEngine->getMaker().findKey(displayable, &id);
-				SkASSERT(success);
-				operand->fString = SkNEW_ARGS(SkString, (id));
-			}
-		}
-		return true;
-	}
+    virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) {
+        SkASSERT(type == SkOperand2::kObject);
+        SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
+        switch (displayable->getType()) {
+            case SkType_Array: {
+                SkDisplayArray* boxedValue = (SkDisplayArray*) displayable;
+                operand->fArray = new SkOpArray(SkAnimatorScript2::ToOpType(boxedValue->values.getType()));
+                int count = boxedValue->values.count();
+                operand->fArray->setCount(count);
+                memcpy(operand->fArray->begin(), boxedValue->values.begin(), count * sizeof(SkOperand2));
+                fEngine->track(operand->fArray);
+                } break;
+            case SkType_Boolean: {
+                SkDisplayBoolean* boxedValue = (SkDisplayBoolean*) displayable;
+                operand->fS32 = boxedValue->value;
+                } break;
+            case SkType_Int: {
+                SkDisplayInt* boxedValue = (SkDisplayInt*) displayable;
+                operand->fS32 = boxedValue->value;
+                } break;
+            case SkType_Float: {
+                SkDisplayFloat* boxedValue = (SkDisplayFloat*) displayable;
+                operand->fScalar = boxedValue->value;
+                } break;
+            case SkType_String: {
+                SkDisplayString* boxedValue = (SkDisplayString*) displayable;
+                operand->fString = SkNEW_ARGS(SkString, (boxedValue->value));
+                } break;
+            default: {
+                const char* id;
+                bool success = fEngine->getMaker().findKey(displayable, &id);
+                SkASSERT(success);
+                operand->fString = SkNEW_ARGS(SkString, (id));
+            }
+        }
+        return true;
+    }
 
-	virtual SkOperand2::OpType getReturnType(int /*index*/, SkOperand2* operand) { 
-		SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
-		switch (displayable->getType()) {
-			case SkType_Array:
-				return SkOperand2::kArray;
-			case SkType_Int:
-				return SkOperand2::kS32;
-			case SkType_Float:
-				return SkOperand2::kScalar;
-			case SkType_String:
-			default:
-				return SkOperand2::kString;
-		}
-	}
+    virtual SkOperand2::OpType getReturnType(int /*index*/, SkOperand2* operand) {
+        SkDisplayable* displayable = (SkDisplayable*) operand->fObject;
+        switch (displayable->getType()) {
+            case SkType_Array:
+                return SkOperand2::kArray;
+            case SkType_Int:
+                return SkOperand2::kS32;
+            case SkType_Float:
+                return SkOperand2::kScalar;
+            case SkType_String:
+            default:
+                return SkOperand2::kString;
+        }
+    }
 
-	virtual Type getType() const { 
-		return kUnbox; 
-	}
+    virtual Type getType() const {
+        return kUnbox;
+    }
 
-	SkAnimatorScript2* fEngine;
+    SkAnimatorScript2* fEngine;
 };
 
-SkAnimatorScript2::SkAnimatorScript2(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type) : 
-		SkScriptEngine2(ToOpType(type)), fMaker(maker), fWorking(working) {
-	*fCallBackArray.append() = new SkAnimatorScript_Member(this);
-	*fCallBackArray.append() = new SkAnimatorScript_MemberFunction(this);
-	*fCallBackArray.append() = new SkAnimatorScript_Box();
-	*fCallBackArray.append() = new SkAnimatorScript_Unbox(this);
-	*fCallBackArray.append() = new SkAnimatorScript_ID(this);
-	if (type == SkType_ARGB) {
-		*fCallBackArray.append() = new SkAnimatorScript_RGB();
-		*fCallBackArray.append() = new SkAnimatorScript_NamedColor();
-	}
-	if (SkDisplayType::IsEnum(&maker, type)) {
-		// !!! for SpiderMonkey, iterate through the enum values, and map them to globals
-		const SkDisplayEnumMap& map = GetEnumValues(type);
-		*fCallBackArray.append() = new SkAnimatorScript_Enum(map.fValues); 
-	}
-	*fCallBackArray.append() = new SkAnimatorScript_Eval(this);
-#if 0		// !!! no extra support for now
-	for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) {
-		SkExtras* extra = *extraPtr;
-		if (extra->fExtraCallBack)
-			*fCallBackArray.append() = new propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage);
-	}
+SkAnimatorScript2::SkAnimatorScript2(SkAnimateMaker& maker, SkDisplayable* working, SkDisplayTypes type) :
+        SkScriptEngine2(ToOpType(type)), fMaker(maker), fWorking(working) {
+    *fCallBackArray.append() = new SkAnimatorScript_Member(this);
+    *fCallBackArray.append() = new SkAnimatorScript_MemberFunction(this);
+    *fCallBackArray.append() = new SkAnimatorScript_Box();
+    *fCallBackArray.append() = new SkAnimatorScript_Unbox(this);
+    *fCallBackArray.append() = new SkAnimatorScript_ID(this);
+    if (type == SkType_ARGB) {
+        *fCallBackArray.append() = new SkAnimatorScript_RGB();
+        *fCallBackArray.append() = new SkAnimatorScript_NamedColor();
+    }
+    if (SkDisplayType::IsEnum(&maker, type)) {
+        // !!! for SpiderMonkey, iterate through the enum values, and map them to globals
+        const SkDisplayEnumMap& map = GetEnumValues(type);
+        *fCallBackArray.append() = new SkAnimatorScript_Enum(map.fValues);
+    }
+    *fCallBackArray.append() = new SkAnimatorScript_Eval(this);
+#if 0        // !!! no extra support for now
+    for (SkExtras** extraPtr = maker.fExtras.begin(); extraPtr < maker.fExtras.end(); extraPtr++) {
+        SkExtras* extra = *extraPtr;
+        if (extra->fExtraCallBack)
+            *fCallBackArray.append() = new propertyCallBack(extra->fExtraCallBack, extra->fExtraStorage);
+    }
 #endif
 }
 
 SkAnimatorScript2::~SkAnimatorScript2() {
-	SkScriptCallBack** end = fCallBackArray.end();
-	for (SkScriptCallBack** ptr = fCallBackArray.begin(); ptr < end; ptr++)
-		delete *ptr;
+    SkScriptCallBack** end = fCallBackArray.end();
+    for (SkScriptCallBack** ptr = fCallBackArray.begin(); ptr < end; ptr++)
+        delete *ptr;
 }
 
-bool SkAnimatorScript2::evalMemberCommon(const SkMemberInfo* info, 
-		SkDisplayable* displayable, SkOperand2* value) {
-	SkDisplayTypes original;
-	SkDisplayTypes type = original = (SkDisplayTypes) info->getType();
-	if (info->fType == SkType_Array)
-		type = SkType_Array;
-	switch (type) {
-		case SkType_ARGB:
-			type = SkType_Int;
-		case SkType_Boolean:
-		case SkType_Int:
-		case SkType_MSec:
-		case SkType_Float:
-			SkASSERT(info->getCount() == 1);
-			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 
-				type = SkType_Float;
-			}
-			break;
-		case SkType_String: {
-			SkString* displayableString;
-			if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) {
-				info->getString(displayable, &displayableString);
-				value->fString = new SkString(*displayableString);
-			}
-			} break;
-		case SkType_Array: {
-			SkASSERT(info->fType != SkType_MemberProperty); // !!! incomplete
-			SkTDOperandArray* displayableArray = (SkTDOperandArray*) info->memberData(displayable);
-			if (displayable->getType() == SkType_Array) {
-				SkDisplayArray* typedArray = (SkDisplayArray*) displayable;
-				original = typedArray->values.getType();
-			}
-			SkASSERT(original != SkType_Unknown);
-			SkOpArray* array = value->fArray = new SkOpArray(ToOpType(original));
-			track(array);
-			int count = displayableArray->count();
-			if (count > 0) {
-				array->setCount(count);
-				memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand2));
-			}
-			} break;
-		default:
-			SkASSERT(0); // unimplemented
-	}
-	return true;
+bool SkAnimatorScript2::evalMemberCommon(const SkMemberInfo* info,
+        SkDisplayable* displayable, SkOperand2* value) {
+    SkDisplayTypes original;
+    SkDisplayTypes type = original = (SkDisplayTypes) info->getType();
+    if (info->fType == SkType_Array)
+        type = SkType_Array;
+    switch (type) {
+        case SkType_ARGB:
+            type = SkType_Int;
+        case SkType_Boolean:
+        case SkType_Int:
+        case SkType_MSec:
+        case SkType_Float:
+            SkASSERT(info->getCount() == 1);
+            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
+                type = SkType_Float;
+            }
+            break;
+        case SkType_String: {
+            SkString* displayableString;
+            if (info->fType != SkType_MemberProperty && info->fType != SkType_MemberFunction) {
+                info->getString(displayable, &displayableString);
+                value->fString = new SkString(*displayableString);
+            }
+            } break;
+        case SkType_Array: {
+            SkASSERT(info->fType != SkType_MemberProperty); // !!! incomplete
+            SkTDOperandArray* displayableArray = (SkTDOperandArray*) info->memberData(displayable);
+            if (displayable->getType() == SkType_Array) {
+                SkDisplayArray* typedArray = (SkDisplayArray*) displayable;
+                original = typedArray->values.getType();
+            }
+            SkASSERT(original != SkType_Unknown);
+            SkOpArray* array = value->fArray = new SkOpArray(ToOpType(original));
+            track(array);
+            int count = displayableArray->count();
+            if (count > 0) {
+                array->setCount(count);
+                memcpy(array->begin(), displayableArray->begin(), count * sizeof(SkOperand2));
+            }
+            } break;
+        default:
+            SkASSERT(0); // unimplemented
+    }
+    return true;
 }
 
 const SkDisplayEnumMap& SkAnimatorScript2::GetEnumValues(SkDisplayTypes type) {
-	int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type, 
-		sizeof(SkDisplayEnumMap));
-	SkASSERT(index >= 0);
-	return gEnumMaps[index];
+    int index = SkTSearch<SkDisplayTypes>(&gEnumMaps[0].fType, gEnumMapCount, type,
+        sizeof(SkDisplayEnumMap));
+    SkASSERT(index >= 0);
+    return gEnumMaps[index];
 }
 
 SkDisplayTypes SkAnimatorScript2::ToDisplayType(SkOperand2::OpType type) {
-	int val = type;
-	switch (val) {
-		case SkOperand2::kNoType:
-			return SkType_Unknown;
-		case SkOperand2::kS32:
-			return SkType_Int;
-		case SkOperand2::kScalar:
-			return SkType_Float;
-		case SkOperand2::kString:
-			return SkType_String;
-		case SkOperand2::kArray:
-			return SkType_Array;
-		case SkOperand2::kObject:
-			return SkType_Displayable;
-		default:
-			SkASSERT(0);
-			return SkType_Unknown;
-	}
+    int val = type;
+    switch (val) {
+        case SkOperand2::kNoType:
+            return SkType_Unknown;
+        case SkOperand2::kS32:
+            return SkType_Int;
+        case SkOperand2::kScalar:
+            return SkType_Float;
+        case SkOperand2::kString:
+            return SkType_String;
+        case SkOperand2::kArray:
+            return SkType_Array;
+        case SkOperand2::kObject:
+            return SkType_Displayable;
+        default:
+            SkASSERT(0);
+            return SkType_Unknown;
+    }
 }
 
 SkOperand2::OpType SkAnimatorScript2::ToOpType(SkDisplayTypes type) {
-	if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
-		return SkOperand2::kObject;
-	if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
-		return SkOperand2::kS32;
-	switch (type) {
-		case SkType_ARGB:
-		case SkType_MSec:
-		case SkType_Int:
-			return SkOperand2::kS32;
-		case SkType_Float:
-		case SkType_Point:
-		case SkType_3D_Point:
-			return SkOperand2::kScalar;
-		case SkType_Base64:
-		case SkType_DynamicString:
-		case SkType_String:
-			return SkOperand2::kString;
-		case SkType_Array:
-			return SkOperand2::kArray;
-		case SkType_Unknown:
-			return SkOperand2::kNoType;
-		default:
-			SkASSERT(0);
-			return SkOperand2::kNoType;
-	}
+    if (SkDisplayType::IsDisplayable(NULL /* fMaker */, type))
+        return SkOperand2::kObject;
+    if (SkDisplayType::IsEnum(NULL /* fMaker */, type))
+        return SkOperand2::kS32;
+    switch (type) {
+        case SkType_ARGB:
+        case SkType_MSec:
+        case SkType_Int:
+            return SkOperand2::kS32;
+        case SkType_Float:
+        case SkType_Point:
+        case SkType_3D_Point:
+            return SkOperand2::kScalar;
+        case SkType_Base64:
+        case SkType_DynamicString:
+        case SkType_String:
+            return SkOperand2::kString;
+        case SkType_Array:
+            return SkOperand2::kArray;
+        case SkType_Unknown:
+            return SkOperand2::kNoType;
+        default:
+            SkASSERT(0);
+            return SkOperand2::kNoType;
+    }
 }
 
 bool SkAnimatorScript2::MapEnums(const char* ptr, const char* match, size_t len, int* value) {
-	int index = 0;
-	bool more = true;
-	do {
-		const char* last = strchr(ptr, '|');
-		if (last == NULL) {
-			last = &ptr[strlen(ptr)];
-			more = false;
-		}
-		size_t length = last - ptr;
-		if (len == length && strncmp(ptr, match, length) == 0) {
-			*value = index;
-			return true;
-		}
-		index++;
-		ptr = last + 1;
-	} while (more);
-	return false;
+    int index = 0;
+    bool more = true;
+    do {
+        const char* last = strchr(ptr, '|');
+        if (last == NULL) {
+            last = &ptr[strlen(ptr)];
+            more = false;
+        }
+        size_t length = last - ptr;
+        if (len == length && strncmp(ptr, match, length) == 0) {
+            *value = index;
+            return true;
+        }
+        index++;
+        ptr = last + 1;
+    } while (more);
+    return false;
 }
 
 #if defined SK_DEBUG
 
 #include "SkAnimator.h"
 
-static const char scriptTestSetup[]  = 
+static const char scriptTestSetup[]  =
 "<screenplay>"
-	"<apply>"
-		"<paint>"
-			"<emboss id='emboss' direction='[1,1,1]'  />"
-		"</paint>"
-		"<animateField id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>"
-		"<set lval='direction[0]' target='emboss' to='-1' />"
-	"</apply>"
-	"<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />"
-	"<color id='xColor' color='rgb(12,34,56)' />"
-	"<typedArray id='emptyArray' />"
-	"<typedArray id='intArray' values='[1, 4, 6]' />"
-	"<s32 id='idx' value='2' />"
-	"<s32 id='idy' value='2' />"
-	"<string id='alpha' value='abc' />"
-	"<rectangle id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />"
-	"<event id='evt'>"
-		"<input name='x' />"
-		"<apply scope='idy'>"
-			"<set field='value' to='evt.x.s32' />"
-		"</apply>"
-	"</event>"
+    "<apply>"
+        "<paint>"
+            "<emboss id='emboss' direction='[1,1,1]'  />"
+        "</paint>"
+        "<animateField id='animation' field='direction' target='emboss' from='[1,1,1]' to='[-1,1,1]' dur='1'/>"
+        "<set lval='direction[0]' target='emboss' to='-1' />"
+    "</apply>"
+    "<color id='testColor' color='0 ? rgb(0,0,0) : rgb(255,255,255)' />"
+    "<color id='xColor' color='rgb(12,34,56)' />"
+    "<typedArray id='emptyArray' />"
+    "<typedArray id='intArray' values='[1, 4, 6]' />"
+    "<s32 id='idx' value='2' />"
+    "<s32 id='idy' value='2' />"
+    "<string id='alpha' value='abc' />"
+    "<rectangle id='testRect' left='Math.cos(0)' top='2' right='12' bottom='5' />"
+    "<event id='evt'>"
+        "<input name='x' />"
+        "<apply scope='idy'>"
+            "<set field='value' to='evt.x.s32' />"
+        "</apply>"
+    "</event>"
 "</screenplay>";
 
 static const SkScriptNAnswer scriptTests[]  = {
-	{	"alpha+alpha", SkType_String, 0, 0, "abcabc" },
-	{	"0 ? Math.sin(0) : 1", SkType_Int, 1 },
-	{	"intArray[4]", SkType_Unknown },
-	{	"emptyArray[4]", SkType_Unknown },
-	{	"idx", SkType_Int, 2 },
-	{	"intArray.length", SkType_Int, 3 },
-	{	"intArray.values[0]", SkType_Int, 1 },
-	{	"intArray[0]", SkType_Int, 1 },
-	{	"idx.value", SkType_Int, 2 },
-	{	"alpha.value", SkType_String, 0, 0, "abc" },
-	{	"alpha", SkType_String, 0, 0, "abc" },
-	{	"alpha.value+alpha.value", SkType_String, 0, 0, "abcabc" },
-	{	"alpha+idx", SkType_String, 0, 0, "abc2" },
-	{	"idx+alpha", SkType_String, 0, 0, "2abc" },
-	{	"intArray[idx]", SkType_Int, 6 },
-	{	"alpha.slice(1,2)", SkType_String, 0, 0, "b" },
-	{	"alpha.value.slice(1,2)", SkType_String, 0, 0, "b" },
-	{	"Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) },
-	{	"testRect.left+2", SkType_Float, 0, SkIntToScalar(3) },
-	{	"0 ? intArray[0] : 1", SkType_Int, 1 },
-	{	"0 ? intArray.values[0] : 1", SkType_Int, 1 },
-	{	"0 ? idx : 1", SkType_Int, 1 },
-	{	"0 ? idx.value : 1", SkType_Int, 1 },
-	{	"0 ? alpha.slice(1,2) : 1", SkType_Int, 1 },
-	{	"0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 },
-	{ "idy", SkType_Int, 3 }
+    {    "alpha+alpha", SkType_String, 0, 0, "abcabc" },
+    {    "0 ? Math.sin(0) : 1", SkType_Int, 1 },
+    {    "intArray[4]", SkType_Unknown },
+    {    "emptyArray[4]", SkType_Unknown },
+    {    "idx", SkType_Int, 2 },
+    {    "intArray.length", SkType_Int, 3 },
+    {    "intArray.values[0]", SkType_Int, 1 },
+    {    "intArray[0]", SkType_Int, 1 },
+    {    "idx.value", SkType_Int, 2 },
+    {    "alpha.value", SkType_String, 0, 0, "abc" },
+    {    "alpha", SkType_String, 0, 0, "abc" },
+    {    "alpha.value+alpha.value", SkType_String, 0, 0, "abcabc" },
+    {    "alpha+idx", SkType_String, 0, 0, "abc2" },
+    {    "idx+alpha", SkType_String, 0, 0, "2abc" },
+    {    "intArray[idx]", SkType_Int, 6 },
+    {    "alpha.slice(1,2)", SkType_String, 0, 0, "b" },
+    {    "alpha.value.slice(1,2)", SkType_String, 0, 0, "b" },
+    {    "Math.sin(0)", SkType_Float, 0, SkIntToScalar(0) },
+    {    "testRect.left+2", SkType_Float, 0, SkIntToScalar(3) },
+    {    "0 ? intArray[0] : 1", SkType_Int, 1 },
+    {    "0 ? intArray.values[0] : 1", SkType_Int, 1 },
+    {    "0 ? idx : 1", SkType_Int, 1 },
+    {    "0 ? idx.value : 1", SkType_Int, 1 },
+    {    "0 ? alpha.slice(1,2) : 1", SkType_Int, 1 },
+    {    "0 ? alpha.value.slice(1,2) : 1", SkType_Int, 1 },
+    { "idy", SkType_Int, 3 }
 };
 
-#define SkScriptNAnswer_testCount	SK_ARRAY_COUNT(scriptTests)
+#define SkScriptNAnswer_testCount    SK_ARRAY_COUNT(scriptTests)
 
 void SkAnimatorScript2::UnitTest() {
 #if defined(SK_SUPPORT_UNITTEST)
-	SkAnimator animator;
-	SkASSERT(animator.decodeMemory(scriptTestSetup, sizeof(scriptTestSetup)-1));
-	SkEvent evt;
-	evt.setString("id", "evt");
-	evt.setS32("x", 3);
-	animator.doUserEvent(evt);
-	// set up animator with memory script above, then run value tests
-	for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
-		SkAnimatorScript2 engine(*animator.fMaker, NULL, scriptTests[index].fType);
-		SkScriptValue2 value;
-		const char* script = scriptTests[index].fScript;
-		bool success = engine.evaluateScript(&script, &value);
-		if (success == false) {
-			SkASSERT(scriptTests[index].fType == SkType_Unknown);
-			continue;
-		}
-		SkASSERT(value.fType == ToOpType(scriptTests[index].fType));
-		SkScalar error;
-		switch (value.fType) {
-			case SkOperand2::kS32:
-				SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
-				break;
-			case SkOperand2::kScalar:
-				error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
-				SkASSERT(error < SK_Scalar1 / 10000);
-				break;
-			case SkOperand2::kString:
-				SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
-				break;
-			default:
-				SkASSERT(0);
-		}
-	}
+    SkAnimator animator;
+    SkASSERT(animator.decodeMemory(scriptTestSetup, sizeof(scriptTestSetup)-1));
+    SkEvent evt;
+    evt.setString("id", "evt");
+    evt.setS32("x", 3);
+    animator.doUserEvent(evt);
+    // set up animator with memory script above, then run value tests
+    for (int index = 0; index < SkScriptNAnswer_testCount; index++) {
+        SkAnimatorScript2 engine(*animator.fMaker, NULL, scriptTests[index].fType);
+        SkScriptValue2 value;
+        const char* script = scriptTests[index].fScript;
+        bool success = engine.evaluateScript(&script, &value);
+        if (success == false) {
+            SkASSERT(scriptTests[index].fType == SkType_Unknown);
+            continue;
+        }
+        SkASSERT(value.fType == ToOpType(scriptTests[index].fType));
+        SkScalar error;
+        switch (value.fType) {
+            case SkOperand2::kS32:
+                SkASSERT(value.fOperand.fS32 == scriptTests[index].fIntAnswer);
+                break;
+            case SkOperand2::kScalar:
+                error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
+                SkASSERT(error < SK_Scalar1 / 10000);
+                break;
+            case SkOperand2::kString:
+                SkASSERT(value.fOperand.fString->equals(scriptTests[index].fStringAnswer));
+                break;
+            default:
+                SkASSERT(0);
+        }
+    }
 #endif
 }
 
 #endif
-
diff --git a/src/animator/SkAnimatorScript2.h b/src/animator/SkAnimatorScript2.h
index e6bf58e..c3995f6 100644
--- a/src/animator/SkAnimatorScript2.h
+++ b/src/animator/SkAnimatorScript2.h
@@ -17,33 +17,33 @@
 
 #ifndef SkAnimatorScript_DEFINED
 struct SkDisplayEnumMap {
-	SkDisplayTypes fType;
-	const char* fValues;
+    SkDisplayTypes fType;
+    const char* fValues;
 };
 #endif
 
 class SkAnimatorScript2 : public SkScriptEngine2 {
 public:
-	SkAnimatorScript2(SkAnimateMaker& , SkDisplayable* working, SkDisplayTypes type);
-	~SkAnimatorScript2();
-	bool evalMemberCommon(const SkMemberInfo* info, 
-		SkDisplayable* displayable, SkOperand2* value);
-	SkAnimateMaker& getMaker() { return fMaker; }
-	SkDisplayable* getWorking() { return fWorking; }
-	static bool MapEnums(const char* ptr, const char* match, size_t len, int* value);
-	static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type); 
-	static SkDisplayTypes ToDisplayType(SkOperand2::OpType type);
-	static SkOperand2::OpType ToOpType(SkDisplayTypes type);
+    SkAnimatorScript2(SkAnimateMaker& , SkDisplayable* working, SkDisplayTypes type);
+    ~SkAnimatorScript2();
+    bool evalMemberCommon(const SkMemberInfo* info,
+        SkDisplayable* displayable, SkOperand2* value);
+    SkAnimateMaker& getMaker() { return fMaker; }
+    SkDisplayable* getWorking() { return fWorking; }
+    static bool MapEnums(const char* ptr, const char* match, size_t len, int* value);
+    static const SkDisplayEnumMap& GetEnumValues(SkDisplayTypes type);
+    static SkDisplayTypes ToDisplayType(SkOperand2::OpType type);
+    static SkOperand2::OpType ToOpType(SkDisplayTypes type);
 private:
-	SkAnimateMaker& fMaker;
-	SkDisplayable* fWorking;
-	friend class SkDump;
-	friend struct SkScriptNAnswer;
-	// illegal
-	SkAnimatorScript2& operator=(const SkAnimatorScript2&);
+    SkAnimateMaker& fMaker;
+    SkDisplayable* fWorking;
+    friend class SkDump;
+    friend struct SkScriptNAnswer;
+    // illegal
+    SkAnimatorScript2& operator=(const SkAnimatorScript2&);
 #ifdef SK_DEBUG
 public:
-	static void UnitTest();
+    static void UnitTest();
 #endif
 };
 
diff --git a/src/animator/SkBoundable.cpp b/src/animator/SkBoundable.cpp
index 017b6b8..64a7005 100644
--- a/src/animator/SkBoundable.cpp
+++ b/src/animator/SkBoundable.cpp
@@ -39,7 +39,7 @@
 }
 
 
-SkBoundableAuto::SkBoundableAuto(SkBoundable* boundable, 
+SkBoundableAuto::SkBoundableAuto(SkBoundable* boundable,
         SkAnimateMaker& maker) : fBoundable(boundable), fMaker(maker) {
     if (fBoundable->hasBounds()) {
         fMaker.fCanvas->setBounder(&maker.fDisplayList);
@@ -53,4 +53,3 @@
     fMaker.fCanvas->setBounder(NULL);
     fBoundable->setBounds(fMaker.fDisplayList.fBounds);
 }
-
diff --git a/src/animator/SkBoundable.h b/src/animator/SkBoundable.h
index 1e70505..daeda23 100644
--- a/src/animator/SkBoundable.h
+++ b/src/animator/SkBoundable.h
@@ -39,4 +39,3 @@
 };
 
 #endif // SkBoundable_DEFINED
-
diff --git a/src/animator/SkBuildCondensedInfo.cpp b/src/animator/SkBuildCondensedInfo.cpp
index 30da67e..411be90 100644
--- a/src/animator/SkBuildCondensedInfo.cpp
+++ b/src/animator/SkBuildCondensedInfo.cpp
@@ -10,7 +10,7 @@
 #include "SkTypes.h"
 #if defined SK_BUILD_CONDENSED
 #include "SkMemberInfo.h"
-#if SK_USE_CONDENSED_INFO == 1 
+#if SK_USE_CONDENSED_INFO == 1
 #error "SK_USE_CONDENSED_INFO must be zero to build condensed info"
 #endif
 #if !defined SK_BUILD_FOR_WIN32
@@ -115,7 +115,7 @@
             continue;
         AddInfo(gTypeNames[index].fType, info, infoCount);
     }
-    const SkMemberInfo* extraInfo = 
+    const SkMemberInfo* extraInfo =
         SkDisplayType::GetMembers(maker, SkType_3D_Point, &infoCount);
     AddInfo(SkType_Point, extraInfo, infoCount);
     AddInfo(SkType_3D_Point, extraInfo, infoCount);
@@ -239,7 +239,7 @@
             Get3DName(scratch, gTypeNames[typeNameIndex].fName);
         } else
             sprintf(scratch, "Unknown%d", unknown++);
-        fprintf(condensed, "%d%c // %s\n\t", index, 
+        fprintf(condensed, "%d%c // %s\n\t", index,
             index < gInfosCounts.count() ? ',' : ' ', scratch);
     }
     fprintf(condensed, "\n};\n\n");
@@ -280,5 +280,3 @@
 #include "SkDisplayType.h"
 void SkDisplayType::BuildCondensedInfo(SkAnimateMaker* ) {}
 #endif
-
-
diff --git a/src/animator/SkCondensedDebug.cpp b/src/animator/SkCondensedDebug.cpp
index c1c7007..dcebe00 100644
--- a/src/animator/SkCondensedDebug.cpp
+++ b/src/animator/SkCondensedDebug.cpp
@@ -14,7 +14,7 @@
 // To change it, edit the file with the matching debug info.
 // Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file.
 
-static const char gMathStrings[] = 
+static const char gMathStrings[] =
     "E\0"
     "LN10\0"
     "LN2\0"
@@ -72,7 +72,7 @@
     {123, -18, 66, 98}
 };
 
-static const char gAddStrings[] = 
+static const char gAddStrings[] =
     "inPlace\0"
     "offset\0"
     "use\0"
@@ -86,7 +86,7 @@
     {19, 28, 37, 1}
 };
 
-static const char gAddCircleStrings[] = 
+static const char gAddCircleStrings[] =
     "\0"
     "radius\0"
     "x\0"
@@ -100,7 +100,7 @@
     {10, 32, 98, 1}
 };
 
-static const char gUnknown1Strings[] = 
+static const char gUnknown1Strings[] =
     "direction"
 ;
 
@@ -108,7 +108,7 @@
     {0, 20, 75, 1}
 };
 
-static const char gAddOvalStrings[] = 
+static const char gAddOvalStrings[] =
     ""
 ;
 
@@ -116,7 +116,7 @@
     {0, 6, 18, 5}
 };
 
-static const char gAddPathStrings[] = 
+static const char gAddPathStrings[] =
     "matrix\0"
     "path"
 ;
@@ -126,7 +126,7 @@
     {7, 24, 74, 1}
 };
 
-static const char gAddRectangleStrings[] = 
+static const char gAddRectangleStrings[] =
     "\0"
     "bottom\0"
     "left\0"
@@ -142,7 +142,7 @@
     {19, 28, 98, 1}
 };
 
-static const char gAddRoundRectStrings[] = 
+static const char gAddRoundRectStrings[] =
     "\0"
     "rx\0"
     "ry"
@@ -154,7 +154,7 @@
     {4, 44, 98, 1}
 };
 
-static const char gUnknown2Strings[] = 
+static const char gUnknown2Strings[] =
     "begin\0"
     "blend\0"
     "dur\0"
@@ -186,7 +186,7 @@
     {73, -4, 67, 40}
 };
 
-static const char gAnimateFieldStrings[] = 
+static const char gAnimateFieldStrings[] =
     ""
 ;
 
@@ -194,7 +194,7 @@
     {0, 8, 18, 13}
 };
 
-static const char gApplyStrings[] = 
+static const char gApplyStrings[] =
     "animator\0"
     "begin\0"
     "dontDraw\0"
@@ -226,7 +226,7 @@
     {88, 56, 14, 1}
 };
 
-static const char gUnknown3Strings[] = 
+static const char gUnknown3Strings[] =
     "x\0"
     "y"
 ;
@@ -236,7 +236,7 @@
     {2, 52, 98, 1}
 };
 
-static const char gBitmapStrings[] = 
+static const char gBitmapStrings[] =
     "\0"
     "erase\0"
     "format\0"
@@ -254,7 +254,7 @@
     {30, 68, 96, 1}
 };
 
-static const char gBitmapShaderStrings[] = 
+static const char gBitmapShaderStrings[] =
     "\0"
     "filterType\0"
     "image"
@@ -266,7 +266,7 @@
     {12, 32, 17, 1}
 };
 
-static const char gBlurStrings[] = 
+static const char gBlurStrings[] =
     "blurStyle\0"
     "radius"
 ;
@@ -276,7 +276,7 @@
     {10, 20, 98, 1}
 };
 
-static const char gBoundsStrings[] = 
+static const char gBoundsStrings[] =
     "\0"
     "inval"
 ;
@@ -286,7 +286,7 @@
     {1, 44, 26, 1}
 };
 
-static const char gClipStrings[] = 
+static const char gClipStrings[] =
     "path\0"
     "rectangle"
 ;
@@ -296,7 +296,7 @@
     {5, 16, 91, 1}
 };
 
-static const char gColorStrings[] = 
+static const char gColorStrings[] =
     "alpha\0"
     "blue\0"
     "color\0"
@@ -318,7 +318,7 @@
     {42, -7, 67, 98}
 };
 
-static const char gCubicToStrings[] = 
+static const char gCubicToStrings[] =
     "x1\0"
     "x2\0"
     "x3\0"
@@ -336,7 +336,7 @@
     {15, 40, 98, 1}
 };
 
-static const char gDashStrings[] = 
+static const char gDashStrings[] =
     "intervals\0"
     "phase"
 ;
@@ -346,7 +346,7 @@
     {10, 36, 98, 1}
 };
 
-static const char gDataStrings[] = 
+static const char gDataStrings[] =
     "\0"
     "name"
 ;
@@ -356,7 +356,7 @@
     {1, 32, 108, 2}
 };
 
-static const char gDiscreteStrings[] = 
+static const char gDiscreteStrings[] =
     "deviation\0"
     "segLength"
 ;
@@ -366,7 +366,7 @@
     {10, 24, 98, 1}
 };
 
-static const char gDrawToStrings[] = 
+static const char gDrawToStrings[] =
     "drawOnce\0"
     "use"
 ;
@@ -376,7 +376,7 @@
     {9, 76, 19, 1}
 };
 
-static const char gDumpStrings[] = 
+static const char gDumpStrings[] =
     "displayList\0"
     "eventList\0"
     "events\0"
@@ -394,7 +394,7 @@
     {41, 40, 26, 1}
 };
 
-static const char gEmbossStrings[] = 
+static const char gEmbossStrings[] =
     "ambient\0"
     "direction\0"
     "radius\0"
@@ -408,7 +408,7 @@
     {25, -2, 67, 98}
 };
 
-static const char gEventStrings[] = 
+static const char gEventStrings[] =
     "code\0"
     "disable\0"
     "key\0"
@@ -430,7 +430,7 @@
     {36, 40, 98, 1}
 };
 
-static const char gFromPathStrings[] = 
+static const char gFromPathStrings[] =
     "mode\0"
     "offset\0"
     "path"
@@ -442,7 +442,7 @@
     {12, 28, 74, 1}
 };
 
-static const char gUnknown4Strings[] = 
+static const char gUnknown4Strings[] =
     "\0"
     "offsets\0"
     "unitMapper"
@@ -454,7 +454,7 @@
     {9, 44, 108, 2}
 };
 
-static const char gGStrings[] = 
+static const char gGStrings[] =
     "condition\0"
     "enableCondition"
 ;
@@ -464,7 +464,7 @@
     {10, 24, 40, 2}
 };
 
-static const char gHitClearStrings[] = 
+static const char gHitClearStrings[] =
     "targets"
 ;
 
@@ -472,7 +472,7 @@
     {0, 16, 119, 36}
 };
 
-static const char gHitTestStrings[] = 
+static const char gHitTestStrings[] =
     "bullets\0"
     "hits\0"
     "targets\0"
@@ -486,7 +486,7 @@
     {21, 64, 26, 1}
 };
 
-static const char gImageStrings[] = 
+static const char gImageStrings[] =
     "\0"
     "base64\0"
     "src"
@@ -498,7 +498,7 @@
     {8, 64, 108, 2}
 };
 
-static const char gIncludeStrings[] = 
+static const char gIncludeStrings[] =
     "src"
 ;
 
@@ -506,7 +506,7 @@
     {0, 16, 108, 2}
 };
 
-static const char gInputStrings[] = 
+static const char gInputStrings[] =
     "s32\0"
     "scalar\0"
     "string"
@@ -518,7 +518,7 @@
     {11, 24, 108, 2}
 };
 
-static const char gLineStrings[] = 
+static const char gLineStrings[] =
     "x1\0"
     "x2\0"
     "y1\0"
@@ -532,7 +532,7 @@
     {9, 36, 98, 1}
 };
 
-static const char gLineToStrings[] = 
+static const char gLineToStrings[] =
     "x\0"
     "y"
 ;
@@ -542,7 +542,7 @@
     {2, 24, 98, 1}
 };
 
-static const char gLinearGradientStrings[] = 
+static const char gLinearGradientStrings[] =
     "\0"
     "points"
 ;
@@ -552,7 +552,7 @@
     {1, 88, 77, 4}
 };
 
-static const char gMatrixStrings[] = 
+static const char gMatrixStrings[] =
     "matrix\0"
     "perspectX\0"
     "perspectY\0"
@@ -582,7 +582,7 @@
     {87, -11, 67, 98}
 };
 
-static const char gMoveStrings[] = 
+static const char gMoveStrings[] =
     ""
 ;
 
@@ -590,7 +590,7 @@
     {0, 1, 18, 4}
 };
 
-static const char gMoveToStrings[] = 
+static const char gMoveToStrings[] =
     "x\0"
     "y"
 ;
@@ -600,7 +600,7 @@
     {2, 24, 98, 1}
 };
 
-static const char gMovieStrings[] = 
+static const char gMovieStrings[] =
     "src"
 ;
 
@@ -608,7 +608,7 @@
     {0, 16, 108, 2}
 };
 
-static const char gOvalStrings[] = 
+static const char gOvalStrings[] =
     ""
 ;
 
@@ -616,7 +616,7 @@
     {0, 58, 18, 7}
 };
 
-static const char gPaintStrings[] = 
+static const char gPaintStrings[] =
     "antiAlias\0"
     "ascent\0"
     "color\0"
@@ -672,7 +672,7 @@
     {235, 100, 121, 1}
 };
 
-static const char gPathStrings[] = 
+static const char gPathStrings[] =
     "d\0"
     "fillType\0"
     "length"
@@ -684,7 +684,7 @@
     {11, -2, 67, 98}
 };
 
-static const char gUnknown5Strings[] = 
+static const char gUnknown5Strings[] =
     "x\0"
     "y\0"
     "z"
@@ -696,7 +696,7 @@
     {4, 8, 98, 1}
 };
 
-static const char gPointStrings[] = 
+static const char gPointStrings[] =
     "x\0"
     "y"
 ;
@@ -706,7 +706,7 @@
     {2, 20, 98, 1}
 };
 
-static const char gPolyToPolyStrings[] = 
+static const char gPolyToPolyStrings[] =
     "destination\0"
     "source"
 ;
@@ -716,7 +716,7 @@
     {12, 20, 80, 1}
 };
 
-static const char gPolygonStrings[] = 
+static const char gPolygonStrings[] =
     ""
 ;
 
@@ -724,7 +724,7 @@
     {0, 48, 18, 1}
 };
 
-static const char gPolylineStrings[] = 
+static const char gPolylineStrings[] =
     "points"
 ;
 
@@ -732,7 +732,7 @@
     {0, 88, 119, 98}
 };
 
-static const char gPostStrings[] = 
+static const char gPostStrings[] =
     "delay\0"
     "initialized\0"
     "mode\0"
@@ -750,7 +750,7 @@
     {35, -3, 67, 108}
 };
 
-static const char gQuadToStrings[] = 
+static const char gQuadToStrings[] =
     "x1\0"
     "x2\0"
     "y1\0"
@@ -764,7 +764,7 @@
     {9, 32, 98, 1}
 };
 
-static const char gRCubicToStrings[] = 
+static const char gRCubicToStrings[] =
     ""
 ;
 
@@ -772,7 +772,7 @@
     {0, 18, 18, 6}
 };
 
-static const char gRLineToStrings[] = 
+static const char gRLineToStrings[] =
     ""
 ;
 
@@ -780,7 +780,7 @@
     {0, 35, 18, 2}
 };
 
-static const char gRMoveToStrings[] = 
+static const char gRMoveToStrings[] =
     ""
 ;
 
@@ -788,7 +788,7 @@
     {0, 39, 18, 2}
 };
 
-static const char gRQuadToStrings[] = 
+static const char gRQuadToStrings[] =
     ""
 ;
 
@@ -796,7 +796,7 @@
     {0, 50, 18, 4}
 };
 
-static const char gRadialGradientStrings[] = 
+static const char gRadialGradientStrings[] =
     "\0"
     "center\0"
     "radius"
@@ -808,7 +808,7 @@
     {8, 96, 98, 1}
 };
 
-static const char gRandomStrings[] = 
+static const char gRandomStrings[] =
     "blend\0"
     "max\0"
     "min\0"
@@ -824,7 +824,7 @@
     {21, -2, 67, 96}
 };
 
-static const char gRectToRectStrings[] = 
+static const char gRectToRectStrings[] =
     "destination\0"
     "source"
 ;
@@ -834,7 +834,7 @@
     {12, 20, 91, 1}
 };
 
-static const char gRectangleStrings[] = 
+static const char gRectangleStrings[] =
     "bottom\0"
     "height\0"
     "left\0"
@@ -854,7 +854,7 @@
     {41, -3, 67, 98}
 };
 
-static const char gRemoveStrings[] = 
+static const char gRemoveStrings[] =
     "offset\0"
     "where"
 ;
@@ -864,7 +864,7 @@
     {7, 28, 37, 1}
 };
 
-static const char gReplaceStrings[] = 
+static const char gReplaceStrings[] =
     ""
 ;
 
@@ -872,7 +872,7 @@
     {0, 1, 18, 4}
 };
 
-static const char gRotateStrings[] = 
+static const char gRotateStrings[] =
     "center\0"
     "degrees"
 ;
@@ -882,7 +882,7 @@
     {7, 20, 98, 1}
 };
 
-static const char gRoundRectStrings[] = 
+static const char gRoundRectStrings[] =
     "\0"
     "rx\0"
     "ry"
@@ -894,7 +894,7 @@
     {4, 48, 98, 1}
 };
 
-static const char gS32Strings[] = 
+static const char gS32Strings[] =
     "value"
 ;
 
@@ -902,7 +902,7 @@
     {0, 16, 96, 1}
 };
 
-static const char gScalarStrings[] = 
+static const char gScalarStrings[] =
     "value"
 ;
 
@@ -910,7 +910,7 @@
     {0, 16, 98, 1}
 };
 
-static const char gScaleStrings[] = 
+static const char gScaleStrings[] =
     "center\0"
     "x\0"
     "y"
@@ -922,7 +922,7 @@
     {9, 24, 98, 1}
 };
 
-static const char gSetStrings[] = 
+static const char gSetStrings[] =
     "begin\0"
     "dur\0"
     "dynamic\0"
@@ -944,7 +944,7 @@
     {45, 76, 40, 2}
 };
 
-static const char gShaderStrings[] = 
+static const char gShaderStrings[] =
     "matrix\0"
     "tileMode"
 ;
@@ -954,7 +954,7 @@
     {7, 24, 116, 1}
 };
 
-static const char gSkewStrings[] = 
+static const char gSkewStrings[] =
     "center\0"
     "x\0"
     "y"
@@ -966,7 +966,7 @@
     {9, 24, 98, 1}
 };
 
-static const char g3D_CameraStrings[] = 
+static const char g3D_CameraStrings[] =
     "axis\0"
     "hackHeight\0"
     "hackWidth\0"
@@ -986,7 +986,7 @@
     {50, 48, 106, 3}
 };
 
-static const char g3D_PatchStrings[] = 
+static const char g3D_PatchStrings[] =
     "origin\0"
     "rotateDegrees\0"
     "u\0"
@@ -1000,7 +1000,7 @@
     {23, 28, 106, 3}
 };
 
-static const char gUnknown6Strings[] = 
+static const char gUnknown6Strings[] =
     "x\0"
     "y\0"
     "z"
@@ -1012,7 +1012,7 @@
     {4, 8, 98, 1}
 };
 
-static const char gSnapshotStrings[] = 
+static const char gSnapshotStrings[] =
     "filename\0"
     "quality\0"
     "sequence\0"
@@ -1026,7 +1026,7 @@
     {26, 32, 20, 1}
 };
 
-static const char gStringStrings[] = 
+static const char gStringStrings[] =
     "length\0"
     "slice\0"
     "value"
@@ -1038,7 +1038,7 @@
     {13, 16, 108, 2}
 };
 
-static const char gTextStrings[] = 
+static const char gTextStrings[] =
     "length\0"
     "text\0"
     "x\0"
@@ -1052,7 +1052,7 @@
     {14, 36, 98, 1}
 };
 
-static const char gTextBoxStrings[] = 
+static const char gTextBoxStrings[] =
     "\0"
     "mode\0"
     "spacingAdd\0"
@@ -1070,7 +1070,7 @@
     {41, 44, 108, 2}
 };
 
-static const char gTextOnPathStrings[] = 
+static const char gTextOnPathStrings[] =
     "offset\0"
     "path\0"
     "text"
@@ -1082,7 +1082,7 @@
     {12, 32, 110, 1}
 };
 
-static const char gTextToPathStrings[] = 
+static const char gTextToPathStrings[] =
     "path\0"
     "text"
 ;
@@ -1092,7 +1092,7 @@
     {5, 20, 110, 1}
 };
 
-static const char gTranslateStrings[] = 
+static const char gTranslateStrings[] =
     "x\0"
     "y"
 ;
@@ -1102,7 +1102,7 @@
     {2, 24, 98, 1}
 };
 
-static const char gTypedArrayStrings[] = 
+static const char gTypedArrayStrings[] =
     "length\0"
     "values"
 ;
@@ -1112,7 +1112,7 @@
     {7, 16, 119, 0}
 };
 
-static const char gTypefaceStrings[] = 
+static const char gTypefaceStrings[] =
     "fontName"
 ;
 
@@ -1294,7 +1294,7 @@
     117, // Translate
     119, // TypedArray
     120, // Typeface
-    
+
 };
 
 static const int kTypeIDs = 81;
@@ -1385,5 +1385,3 @@
 
 #endif
 #endif
-
-
diff --git a/src/animator/SkCondensedRelease.cpp b/src/animator/SkCondensedRelease.cpp
index 234e67e..1222496 100644
--- a/src/animator/SkCondensedRelease.cpp
+++ b/src/animator/SkCondensedRelease.cpp
@@ -14,7 +14,7 @@
 // To change it, edit the file with the matching debug info.
 // Then execute SkDisplayType::BuildCondensedInfo() to regenerate this file.
 
-static const char gMathStrings[] = 
+static const char gMathStrings[] =
     "E\0"
     "LN10\0"
     "LN2\0"
@@ -72,7 +72,7 @@
     {123, -18, 66, 98}
 };
 
-static const char gAddStrings[] = 
+static const char gAddStrings[] =
     "inPlace\0"
     "offset\0"
     "use\0"
@@ -86,7 +86,7 @@
     {19, 16, 37, 1}
 };
 
-static const char gAddCircleStrings[] = 
+static const char gAddCircleStrings[] =
     "\0"
     "radius\0"
     "x\0"
@@ -100,7 +100,7 @@
     {10, 20, 98, 1}
 };
 
-static const char gUnknown1Strings[] = 
+static const char gUnknown1Strings[] =
     "direction"
 ;
 
@@ -108,7 +108,7 @@
     {0, 8, 75, 1}
 };
 
-static const char gAddOvalStrings[] = 
+static const char gAddOvalStrings[] =
     ""
 ;
 
@@ -116,7 +116,7 @@
     {0, 6, 18, 5}
 };
 
-static const char gAddPathStrings[] = 
+static const char gAddPathStrings[] =
     "matrix\0"
     "path"
 ;
@@ -126,7 +126,7 @@
     {7, 12, 74, 1}
 };
 
-static const char gAddRectangleStrings[] = 
+static const char gAddRectangleStrings[] =
     "\0"
     "bottom\0"
     "left\0"
@@ -142,7 +142,7 @@
     {19, 16, 98, 1}
 };
 
-static const char gAddRoundRectStrings[] = 
+static const char gAddRoundRectStrings[] =
     "\0"
     "rx\0"
     "ry"
@@ -154,7 +154,7 @@
     {4, 32, 98, 1}
 };
 
-static const char gUnknown2Strings[] = 
+static const char gUnknown2Strings[] =
     "begin\0"
     "blend\0"
     "dur\0"
@@ -186,7 +186,7 @@
     {73, -4, 67, 40}
 };
 
-static const char gAnimateFieldStrings[] = 
+static const char gAnimateFieldStrings[] =
     ""
 ;
 
@@ -194,7 +194,7 @@
     {0, 8, 18, 13}
 };
 
-static const char gApplyStrings[] = 
+static const char gApplyStrings[] =
     "animator\0"
     "begin\0"
     "dontDraw\0"
@@ -226,7 +226,7 @@
     {88, 40, 14, 1}
 };
 
-static const char gUnknown3Strings[] = 
+static const char gUnknown3Strings[] =
     "x\0"
     "y"
 ;
@@ -236,7 +236,7 @@
     {2, 40, 98, 1}
 };
 
-static const char gBitmapStrings[] = 
+static const char gBitmapStrings[] =
     "\0"
     "erase\0"
     "format\0"
@@ -254,7 +254,7 @@
     {30, 56, 96, 1}
 };
 
-static const char gBitmapShaderStrings[] = 
+static const char gBitmapShaderStrings[] =
     "\0"
     "filterType\0"
     "image"
@@ -266,7 +266,7 @@
     {12, 20, 17, 1}
 };
 
-static const char gBlurStrings[] = 
+static const char gBlurStrings[] =
     "blurStyle\0"
     "radius"
 ;
@@ -276,7 +276,7 @@
     {10, 8, 98, 1}
 };
 
-static const char gBoundsStrings[] = 
+static const char gBoundsStrings[] =
     "\0"
     "inval"
 ;
@@ -286,7 +286,7 @@
     {1, 32, 26, 1}
 };
 
-static const char gClipStrings[] = 
+static const char gClipStrings[] =
     "path\0"
     "rectangle"
 ;
@@ -296,7 +296,7 @@
     {5, 4, 91, 1}
 };
 
-static const char gColorStrings[] = 
+static const char gColorStrings[] =
     "alpha\0"
     "blue\0"
     "color\0"
@@ -318,7 +318,7 @@
     {42, -7, 67, 98}
 };
 
-static const char gCubicToStrings[] = 
+static const char gCubicToStrings[] =
     "x1\0"
     "x2\0"
     "x3\0"
@@ -336,7 +336,7 @@
     {15, 28, 98, 1}
 };
 
-static const char gDashStrings[] = 
+static const char gDashStrings[] =
     "intervals\0"
     "phase"
 ;
@@ -346,7 +346,7 @@
     {10, 16, 98, 1}
 };
 
-static const char gDataStrings[] = 
+static const char gDataStrings[] =
     "\0"
     "name"
 ;
@@ -356,7 +356,7 @@
     {1, 16, 108, 1}
 };
 
-static const char gDiscreteStrings[] = 
+static const char gDiscreteStrings[] =
     "deviation\0"
     "segLength"
 ;
@@ -366,7 +366,7 @@
     {10, 12, 98, 1}
 };
 
-static const char gDrawToStrings[] = 
+static const char gDrawToStrings[] =
     "drawOnce\0"
     "use"
 ;
@@ -376,7 +376,7 @@
     {9, 40, 19, 1}
 };
 
-static const char gEmbossStrings[] = 
+static const char gEmbossStrings[] =
     "ambient\0"
     "direction\0"
     "radius\0"
@@ -390,7 +390,7 @@
     {25, -2, 67, 98}
 };
 
-static const char gEventStrings[] = 
+static const char gEventStrings[] =
     "code\0"
     "disable\0"
     "key\0"
@@ -412,7 +412,7 @@
     {36, 24, 98, 1}
 };
 
-static const char gFromPathStrings[] = 
+static const char gFromPathStrings[] =
     "mode\0"
     "offset\0"
     "path"
@@ -424,7 +424,7 @@
     {12, 16, 74, 1}
 };
 
-static const char gUnknown4Strings[] = 
+static const char gUnknown4Strings[] =
     "\0"
     "offsets\0"
     "unitMapper"
@@ -436,7 +436,7 @@
     {9, 24, 108, 1}
 };
 
-static const char gGStrings[] = 
+static const char gGStrings[] =
     "condition\0"
     "enableCondition"
 ;
@@ -446,7 +446,7 @@
     {10, 8, 40, 1}
 };
 
-static const char gHitClearStrings[] = 
+static const char gHitClearStrings[] =
     "targets"
 ;
 
@@ -454,7 +454,7 @@
     {0, 4, 119, 36}
 };
 
-static const char gHitTestStrings[] = 
+static const char gHitTestStrings[] =
     "bullets\0"
     "hits\0"
     "targets\0"
@@ -468,7 +468,7 @@
     {21, 28, 26, 1}
 };
 
-static const char gImageStrings[] = 
+static const char gImageStrings[] =
     "\0"
     "base64\0"
     "src"
@@ -480,7 +480,7 @@
     {8, 52, 108, 1}
 };
 
-static const char gIncludeStrings[] = 
+static const char gIncludeStrings[] =
     "src"
 ;
 
@@ -488,7 +488,7 @@
     {0, 4, 108, 1}
 };
 
-static const char gInputStrings[] = 
+static const char gInputStrings[] =
     "s32\0"
     "scalar\0"
     "string"
@@ -500,7 +500,7 @@
     {11, 12, 108, 1}
 };
 
-static const char gLineStrings[] = 
+static const char gLineStrings[] =
     "x1\0"
     "x2\0"
     "y1\0"
@@ -514,7 +514,7 @@
     {9, 24, 98, 1}
 };
 
-static const char gLineToStrings[] = 
+static const char gLineToStrings[] =
     "x\0"
     "y"
 ;
@@ -524,7 +524,7 @@
     {2, 12, 98, 1}
 };
 
-static const char gLinearGradientStrings[] = 
+static const char gLinearGradientStrings[] =
     "\0"
     "points"
 ;
@@ -534,7 +534,7 @@
     {1, 48, 77, 4}
 };
 
-static const char gMatrixStrings[] = 
+static const char gMatrixStrings[] =
     "matrix\0"
     "perspectX\0"
     "perspectY\0"
@@ -564,7 +564,7 @@
     {87, -11, 67, 98}
 };
 
-static const char gMoveStrings[] = 
+static const char gMoveStrings[] =
     ""
 ;
 
@@ -572,7 +572,7 @@
     {0, 1, 18, 4}
 };
 
-static const char gMoveToStrings[] = 
+static const char gMoveToStrings[] =
     "x\0"
     "y"
 ;
@@ -582,7 +582,7 @@
     {2, 12, 98, 1}
 };
 
-static const char gMovieStrings[] = 
+static const char gMovieStrings[] =
     "src"
 ;
 
@@ -590,7 +590,7 @@
     {0, 4, 108, 1}
 };
 
-static const char gOvalStrings[] = 
+static const char gOvalStrings[] =
     ""
 ;
 
@@ -598,7 +598,7 @@
     {0, 57, 18, 7}
 };
 
-static const char gPaintStrings[] = 
+static const char gPaintStrings[] =
     "antiAlias\0"
     "ascent\0"
     "color\0"
@@ -654,7 +654,7 @@
     {235, 88, 121, 1}
 };
 
-static const char gPathStrings[] = 
+static const char gPathStrings[] =
     "d\0"
     "fillType\0"
     "length"
@@ -666,7 +666,7 @@
     {11, -2, 67, 98}
 };
 
-static const char gUnknown5Strings[] = 
+static const char gUnknown5Strings[] =
     "x\0"
     "y\0"
     "z"
@@ -678,7 +678,7 @@
     {4, 8, 98, 1}
 };
 
-static const char gPointStrings[] = 
+static const char gPointStrings[] =
     "x\0"
     "y"
 ;
@@ -688,7 +688,7 @@
     {2, 8, 98, 1}
 };
 
-static const char gPolyToPolyStrings[] = 
+static const char gPolyToPolyStrings[] =
     "destination\0"
     "source"
 ;
@@ -698,7 +698,7 @@
     {12, 8, 80, 1}
 };
 
-static const char gPolygonStrings[] = 
+static const char gPolygonStrings[] =
     ""
 ;
 
@@ -706,7 +706,7 @@
     {0, 47, 18, 1}
 };
 
-static const char gPolylineStrings[] = 
+static const char gPolylineStrings[] =
     "points"
 ;
 
@@ -714,7 +714,7 @@
     {0, 56, 119, 98}
 };
 
-static const char gPostStrings[] = 
+static const char gPostStrings[] =
     "delay\0"
     "initialized\0"
     "mode\0"
@@ -732,7 +732,7 @@
     {35, -3, 67, 108}
 };
 
-static const char gQuadToStrings[] = 
+static const char gQuadToStrings[] =
     "x1\0"
     "x2\0"
     "y1\0"
@@ -746,7 +746,7 @@
     {9, 20, 98, 1}
 };
 
-static const char gRCubicToStrings[] = 
+static const char gRCubicToStrings[] =
     ""
 ;
 
@@ -754,7 +754,7 @@
     {0, 18, 18, 6}
 };
 
-static const char gRLineToStrings[] = 
+static const char gRLineToStrings[] =
     ""
 ;
 
@@ -762,7 +762,7 @@
     {0, 34, 18, 2}
 };
 
-static const char gRMoveToStrings[] = 
+static const char gRMoveToStrings[] =
     ""
 ;
 
@@ -770,7 +770,7 @@
     {0, 38, 18, 2}
 };
 
-static const char gRQuadToStrings[] = 
+static const char gRQuadToStrings[] =
     ""
 ;
 
@@ -778,7 +778,7 @@
     {0, 49, 18, 4}
 };
 
-static const char gRadialGradientStrings[] = 
+static const char gRadialGradientStrings[] =
     "\0"
     "center\0"
     "radius"
@@ -790,7 +790,7 @@
     {8, 56, 98, 1}
 };
 
-static const char gRandomStrings[] = 
+static const char gRandomStrings[] =
     "blend\0"
     "max\0"
     "min\0"
@@ -806,7 +806,7 @@
     {21, -2, 67, 96}
 };
 
-static const char gRectToRectStrings[] = 
+static const char gRectToRectStrings[] =
     "destination\0"
     "source"
 ;
@@ -816,7 +816,7 @@
     {12, 8, 91, 1}
 };
 
-static const char gRectangleStrings[] = 
+static const char gRectangleStrings[] =
     "bottom\0"
     "height\0"
     "left\0"
@@ -836,7 +836,7 @@
     {41, -3, 67, 98}
 };
 
-static const char gRemoveStrings[] = 
+static const char gRemoveStrings[] =
     "offset\0"
     "where"
 ;
@@ -846,7 +846,7 @@
     {7, 16, 37, 1}
 };
 
-static const char gReplaceStrings[] = 
+static const char gReplaceStrings[] =
     ""
 ;
 
@@ -854,7 +854,7 @@
     {0, 1, 18, 4}
 };
 
-static const char gRotateStrings[] = 
+static const char gRotateStrings[] =
     "center\0"
     "degrees"
 ;
@@ -864,7 +864,7 @@
     {7, 8, 98, 1}
 };
 
-static const char gRoundRectStrings[] = 
+static const char gRoundRectStrings[] =
     "\0"
     "rx\0"
     "ry"
@@ -876,7 +876,7 @@
     {4, 36, 98, 1}
 };
 
-static const char gS32Strings[] = 
+static const char gS32Strings[] =
     "value"
 ;
 
@@ -884,7 +884,7 @@
     {0, 4, 96, 1}
 };
 
-static const char gScalarStrings[] = 
+static const char gScalarStrings[] =
     "value"
 ;
 
@@ -892,7 +892,7 @@
     {0, 4, 98, 1}
 };
 
-static const char gScaleStrings[] = 
+static const char gScaleStrings[] =
     "center\0"
     "x\0"
     "y"
@@ -904,7 +904,7 @@
     {9, 12, 98, 1}
 };
 
-static const char gSetStrings[] = 
+static const char gSetStrings[] =
     "begin\0"
     "dur\0"
     "dynamic\0"
@@ -926,7 +926,7 @@
     {45, 40, 40, 1}
 };
 
-static const char gShaderStrings[] = 
+static const char gShaderStrings[] =
     "matrix\0"
     "tileMode"
 ;
@@ -936,7 +936,7 @@
     {7, 12, 116, 1}
 };
 
-static const char gSkewStrings[] = 
+static const char gSkewStrings[] =
     "center\0"
     "x\0"
     "y"
@@ -948,7 +948,7 @@
     {9, 12, 98, 1}
 };
 
-static const char g3D_CameraStrings[] = 
+static const char g3D_CameraStrings[] =
     "axis\0"
     "hackHeight\0"
     "hackWidth\0"
@@ -968,7 +968,7 @@
     {50, 36, 106, 3}
 };
 
-static const char g3D_PatchStrings[] = 
+static const char g3D_PatchStrings[] =
     "origin\0"
     "rotateDegrees\0"
     "u\0"
@@ -982,7 +982,7 @@
     {23, 16, 106, 3}
 };
 
-static const char gUnknown6Strings[] = 
+static const char gUnknown6Strings[] =
     "x\0"
     "y\0"
     "z"
@@ -994,7 +994,7 @@
     {4, 8, 98, 1}
 };
 
-static const char gSnapshotStrings[] = 
+static const char gSnapshotStrings[] =
     "filename\0"
     "quality\0"
     "sequence\0"
@@ -1008,7 +1008,7 @@
     {26, 16, 20, 1}
 };
 
-static const char gStringStrings[] = 
+static const char gStringStrings[] =
     "length\0"
     "slice\0"
     "value"
@@ -1020,7 +1020,7 @@
     {13, 4, 108, 1}
 };
 
-static const char gTextStrings[] = 
+static const char gTextStrings[] =
     "length\0"
     "text\0"
     "x\0"
@@ -1034,7 +1034,7 @@
     {14, 20, 98, 1}
 };
 
-static const char gTextBoxStrings[] = 
+static const char gTextBoxStrings[] =
     "\0"
     "mode\0"
     "spacingAdd\0"
@@ -1052,7 +1052,7 @@
     {41, 32, 108, 1}
 };
 
-static const char gTextOnPathStrings[] = 
+static const char gTextOnPathStrings[] =
     "offset\0"
     "path\0"
     "text"
@@ -1064,7 +1064,7 @@
     {12, 20, 110, 1}
 };
 
-static const char gTextToPathStrings[] = 
+static const char gTextToPathStrings[] =
     "path\0"
     "text"
 ;
@@ -1074,7 +1074,7 @@
     {5, 8, 110, 1}
 };
 
-static const char gTranslateStrings[] = 
+static const char gTranslateStrings[] =
     "x\0"
     "y"
 ;
@@ -1084,7 +1084,7 @@
     {2, 12, 98, 1}
 };
 
-static const char gTypedArrayStrings[] = 
+static const char gTypedArrayStrings[] =
     "length\0"
     "values"
 ;
@@ -1094,7 +1094,7 @@
     {7, 4, 119, 0}
 };
 
-static const char gTypefaceStrings[] = 
+static const char gTypefaceStrings[] =
     "fontName"
 ;
 
@@ -1274,7 +1274,7 @@
     117, // Translate
     119, // TypedArray
     120, // Typeface
-    
+
 };
 
 static const int kTypeIDs = 80;
@@ -1363,4 +1363,3 @@
 };
 #endif
 #endif
-
diff --git a/src/animator/SkDisplayAdd.cpp b/src/animator/SkDisplayAdd.cpp
index 1cf89a1..2cb5e97 100644
--- a/src/animator/SkDisplayAdd.cpp
+++ b/src/animator/SkDisplayAdd.cpp
@@ -33,7 +33,7 @@
 
 DEFINE_GET_MEMBER(SkAdd);
 
-SkAdd::SkAdd() : mode(kMode_indirect), 
+SkAdd::SkAdd() : mode(kMode_indirect),
     offset(SK_MaxS32), use(NULL), where(NULL) {
 }
 
@@ -119,7 +119,7 @@
                                 parentGroup->markCopySet(index);
                                 useParentList->begin()[index] = use;
                                 break;
-                            }                               
+                            }
                         }
                         *parentList->append() = use;
                     }
@@ -243,4 +243,3 @@
 #endif
 
 DEFINE_GET_MEMBER(SkReplace);
-
diff --git a/src/animator/SkDisplayAdd.h b/src/animator/SkDisplayAdd.h
index 47948fb..d16492b 100644
--- a/src/animator/SkDisplayAdd.h
+++ b/src/animator/SkDisplayAdd.h
@@ -16,12 +16,12 @@
 class SkAdd : public SkDrawable {
     DECLARE_MEMBER_INFO(Add);
     SkAdd();
-    
+
     enum Mode {
         kMode_indirect,
         kMode_immediate
     };
-    
+
     virtual SkDisplayable* deepCopy(SkAnimateMaker* );
     virtual bool draw(SkAnimateMaker& );
 #ifdef SK_DUMP_ENABLED
@@ -69,5 +69,3 @@
 };
 
 #endif // SkDisplayAdd_DEFINED
-
-
diff --git a/src/animator/SkDisplayApply.cpp b/src/animator/SkDisplayApply.cpp
index d54ee26..8b4dfdc 100644
--- a/src/animator/SkDisplayApply.cpp
+++ b/src/animator/SkDisplayApply.cpp
@@ -51,13 +51,13 @@
     SK_MEMBER(transition, ApplyTransition)
 };
 
-#endif 
+#endif
 
 DEFINE_GET_MEMBER(SkApply);
 
 SkApply::SkApply() : begin(0), dontDraw(false), interval((SkMSec) -1), mode((Mode) -1), /*pickup(false), */
     restore(false), scope(NULL), steps(-1), transition((Transition) -1), fActive(NULL), /*fCurrentScope(NULL),*/
-    fLastTime(0), fAppended(false), fContainsScope(false), fDeleteScope(false), fEmbedded(false), 
+    fLastTime(0), fAppended(false), fContainsScope(false), fDeleteScope(false), fEmbedded(false),
     fEnabled(false), fEnabling(false) {
 }
 
@@ -105,7 +105,7 @@
 }
 
 void SkApply::applyValues(int animatorIndex, SkOperand* values, int count,
-     SkDisplayTypes valuesType, SkMSec time) 
+     SkDisplayTypes valuesType, SkMSec time)
 {
     SkAnimateBase* animator = fActive->fAnimators[animatorIndex];
     const SkMemberInfo * info = animator->fFieldInfo;
@@ -149,7 +149,7 @@
             target->setReference(info, values->fDisplayable);
         else
             info->setValue(target, values, count);
-    } 
+    }
 }
 
 bool SkApply::contains(SkDisplayable* child) {
@@ -178,7 +178,7 @@
     //!!! this is the right thing to do, but has bad side effects because of other problems
     // currently, if an apply is in a g and scopes a statement in another g, it ends up as members
     // of both containers. The disabling here incorrectly disables both instances
-    // maybe the fEnabled flag needs to be moved to the fActive data so that both 
+    // maybe the fEnabled flag needs to be moved to the fActive data so that both
     // instances are not affected.
 //  fEnabled = false;
 }
@@ -265,7 +265,7 @@
     bool initialized = fActive != NULL;
     if (dynamicScope.size() > 0)
         enableDynamic(maker);
-    if (maker.fError.hasError()) 
+    if (maker.fError.hasError())
         return false;
     int animators = fAnimators.count();
     int index;
@@ -333,7 +333,7 @@
     }
 #endif
 //  start here;
-    // now that one apply might embed another, only the parent apply should replace the scope 
+    // now that one apply might embed another, only the parent apply should replace the scope
     // or get appended to the display list
     // similarly, an apply added by an add immediate has already been located in the display list
     // and should not get moved or added again here
@@ -408,7 +408,7 @@
             step = steps; // quit
             goto next; // resolveIDs failed
         }
-        if (newID.size() > 0) 
+        if (newID.size() > 0)
             maker.setID(copyScope, newID);
         if (copy->resolveIDs(maker, this, this)) { // fix up all fields, including target
             step = steps; // quit
@@ -471,7 +471,7 @@
             info->setValue(target, fActive->fSaveRestore[activeIndex], count);
     } else {
         SkScriptValue scriptValue;
-        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkDEBUGCODE(bool success = ) target->getProperty(info->propertyIndex(), &scriptValue);
         SkASSERT(success == true);
         last[0] = scriptValue.fOperand;
         scriptValue.fOperand = fActive->fSaveRestore[activeIndex][0];
@@ -517,7 +517,7 @@
     SkAnimateBase** animEnd = fAnimators.end();
     for (SkAnimateBase** animPtr = fAnimators.begin(); animPtr < animEnd; animPtr++) {
         SkAnimateBase* animator = *animPtr;
-        if (animator->fDelayed) 
+        if (animator->fDelayed)
             return true;
     }
     return false;
@@ -548,7 +548,7 @@
     SkMSec time = maker.getAppTime();
     if (lastTime == (SkMSec) -1)
         lastTime = rawTime - 1;
-    if (fActive != NULL && 
+    if (fActive != NULL &&
         strcmp(id, "a3") == 0 && rawTime > lastTime) {
         lastTime += 1000;
         SkString debugOut;
@@ -593,7 +593,7 @@
         } else
             animate->fDelayed = false;
         SkMSec innerTime = fLastTime = state.getRelativeTime(time);
-        if (restore) 
+        if (restore)
             fActive->restoreInterpolatorValues(inner);
         if (animate->fReset) {
             if (transition != SkApply::kTransition_reverse) {
@@ -629,7 +629,7 @@
                 fLastTime = animate->dur;
             SkTypedArray formulaValues;
             formulaValues.setCount(count);
-            bool success = animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL, 
+            SkDEBUGCODE(bool success = ) animate->fFieldInfo->setValue(maker, &formulaValues, 0, 0, NULL,
                 animate->getValuesType(), animate->formula);
             SkASSERT(success);
             if (restore)
@@ -652,7 +652,7 @@
     scope->initialize();
 }
 
-void SkApply::onEndElement(SkAnimateMaker& maker) 
+void SkApply::onEndElement(SkAnimateMaker& maker)
 {
     SkDrawable* scopePtr = scope;
     while (scopePtr && scopePtr->isApply()) {
@@ -761,7 +761,7 @@
             info->setValue(target, last.begin(), count);
     } else {
         SkScriptValue scriptValue;
-        bool success = target->getProperty(info->propertyIndex(), &scriptValue);
+        SkDEBUGCODE(bool success = ) target->getProperty(info->propertyIndex(), &scriptValue);
         SkASSERT(success == true);
         SkASSERT(scriptValue.fType == SkType_Float);
         fActive->fSaveRestore[activeIndex][0] = scriptValue.fOperand;
@@ -781,7 +781,7 @@
             SkAnimateBase* animate = (SkAnimateBase*) scriptValue.fOperand.fDisplayable;
             SkASSERT(animate->isAnimate());
             *fAnimators.append() = animate;
-            return true; 
+            return true;
         }
         case SK_PROPERTY(steps):
             steps = scriptValue.fOperand.fS32;
@@ -792,8 +792,8 @@
     return false;
 }
 
-void SkApply::setSteps(int _steps) { 
-    steps = _steps; 
+void SkApply::setSteps(int _steps) {
+    steps = _steps;
 }
 
 #ifdef SK_DEBUG
@@ -802,6 +802,3 @@
         fActive->validate();
 }
 #endif
-
-
-
diff --git a/src/animator/SkDisplayApply.h b/src/animator/SkDisplayApply.h
index 018b894..e128c29 100644
--- a/src/animator/SkDisplayApply.h
+++ b/src/animator/SkDisplayApply.h
@@ -27,7 +27,7 @@
         kTransition_normal,
         kTransition_reverse
     };
-    
+
     enum Mode {
         kMode_create,
         kMode_immediate,
@@ -58,7 +58,7 @@
     bool hasDelayedAnimator() const;
     virtual bool hasEnable() const;
     bool inactivate(SkAnimateMaker& maker);
-    virtual void initialize(); 
+    virtual void initialize();
     bool interpolate(SkAnimateMaker& , SkMSec time);
     virtual void onEndElement(SkAnimateMaker& );
     virtual const SkMemberInfo* preferredChild(SkDisplayTypes type);
@@ -104,5 +104,3 @@
 };
 
 #endif // SkDisplayApply_DEFINED
-
-
diff --git a/src/animator/SkDisplayBounds.cpp b/src/animator/SkDisplayBounds.cpp
index 69ec47e..49ec9b9 100644
--- a/src/animator/SkDisplayBounds.cpp
+++ b/src/animator/SkDisplayBounds.cpp
@@ -36,11 +36,8 @@
         maker.fDisplayList.fHasUnion = true;
         if (rect.isEmpty())
             rect = fBounds;
-        else 
+        else
             rect.join(fBounds);
     }
     return result;
 }
-
-
-
diff --git a/src/animator/SkDisplayBounds.h b/src/animator/SkDisplayBounds.h
index bc3a987..0511ed7 100644
--- a/src/animator/SkDisplayBounds.h
+++ b/src/animator/SkDisplayBounds.h
@@ -22,4 +22,3 @@
 };
 
 #endif // SkDisplayBounds_DEFINED
-
diff --git a/src/animator/SkDisplayEvent.cpp b/src/animator/SkDisplayEvent.cpp
index 7d2e786..c926a8a 100644
--- a/src/animator/SkDisplayEvent.cpp
+++ b/src/animator/SkDisplayEvent.cpp
@@ -52,9 +52,9 @@
     deleteMembers();
 }
 
-bool SkDisplayEvent::add(SkAnimateMaker& , SkDisplayable* child) { 
-    *fChildren.append() = child; 
-    return true; 
+bool SkDisplayEvent::addChild(SkAnimateMaker& , SkDisplayable* child) {
+    *fChildren.append() = child;
+    return true;
 }
 
 bool SkDisplayEvent::contains(SkDisplayable* match) {
@@ -104,11 +104,7 @@
         SkDebugf("target=\"%s\" ", fTarget->id);
     }
     if (kind >= SkDisplayEvent::kMouseDown && kind <= SkDisplayEvent::kMouseUp) {
-#ifdef SK_CAN_USE_FLOAT
         SkDebugf("x=\"%g\" y=\"%g\" ", SkScalarToFloat(x), SkScalarToFloat(y));
-#else
-        SkDebugf("x=\"%x\" y=\"%x\" ", x, y);
-#endif
     }
     if (disable)
         SkDebugf("disable=\"true\" ");
@@ -116,7 +112,7 @@
 }
 #endif
 
-bool SkDisplayEvent::enableEvent(SkAnimateMaker& maker) 
+bool SkDisplayEvent::enableEvent(SkAnimateMaker& maker)
 {
     maker.fActiveEvent = this;
     if (fChildren.count() == 0)
@@ -138,7 +134,7 @@
         }
         if (displayable->enable(maker))
             continue;
-        if (maker.hasError()) 
+        if (maker.hasError())
             return true;
         if (displayable->isDrawable() == false)
             return true;    // error
@@ -178,7 +174,7 @@
         return;
     maker.fEvents.addEvent(this);
     if (kind == kOnEnd) {
-        bool found = maker.find(target.c_str(), &fTarget);
+        SkDEBUGCODE(bool found = ) maker.find(target.c_str(), &fTarget);
         SkASSERT(found);
         SkASSERT(fTarget && fTarget->isAnimate());
         SkAnimateBase* animate = (SkAnimateBase*) fTarget;
diff --git a/src/animator/SkDisplayEvent.h b/src/animator/SkDisplayEvent.h
index 8b541fc..952faea 100644
--- a/src/animator/SkDisplayEvent.h
+++ b/src/animator/SkDisplayEvent.h
@@ -34,7 +34,7 @@
     };
     SkDisplayEvent();
     virtual ~SkDisplayEvent();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
     virtual bool contains(SkDisplayable*);
     virtual SkDisplayable* contains(const SkString& );
 #ifdef SK_DEBUG
@@ -64,4 +64,3 @@
 };
 
 #endif // SkDisplayEvent_DEFINED
-
diff --git a/src/animator/SkDisplayEvents.cpp b/src/animator/SkDisplayEvents.cpp
index 38f82a0..c42fbdf 100644
--- a/src/animator/SkDisplayEvents.cpp
+++ b/src/animator/SkDisplayEvents.cpp
@@ -83,7 +83,7 @@
     int count = drawArray.count();
     for (index = 0; index < count; index++) {
         SkDrawable* drawable = drawArray[index];
-        drawable->dumpEvents(); 
+        drawable->dumpEvents();
     }
     count = fEvents.count();
     for (index = 0; index < count; index++) {
diff --git a/src/animator/SkDisplayEvents.h b/src/animator/SkDisplayEvents.h
index 2874ac5..276955b 100644
--- a/src/animator/SkDisplayEvents.h
+++ b/src/animator/SkDisplayEvents.h
@@ -40,4 +40,3 @@
 };
 
 #endif // SkDisplayEvents_DEFINED
-
diff --git a/src/animator/SkDisplayInclude.h b/src/animator/SkDisplayInclude.h
index d3fe4da..41b5d29 100644
--- a/src/animator/SkDisplayInclude.h
+++ b/src/animator/SkDisplayInclude.h
@@ -23,4 +23,3 @@
 };
 
 #endif // SkDisplayInclude_DEFINED
-
diff --git a/src/animator/SkDisplayInput.cpp b/src/animator/SkDisplayInput.cpp
index 77bc5de..7061aa8 100644
--- a/src/animator/SkDisplayInput.cpp
+++ b/src/animator/SkDisplayInput.cpp
@@ -49,7 +49,7 @@
     }
     return true;
 }
- 
+
 bool SkInput::hasEnable() const {
     return true;
 }
diff --git a/src/animator/SkDisplayInput.h b/src/animator/SkDisplayInput.h
index 79d82b5..d9871e2 100644
--- a/src/animator/SkDisplayInput.h
+++ b/src/animator/SkDisplayInput.h
@@ -31,4 +31,3 @@
 };
 
 #endif // SkDisplayInput_DEFINED
-
diff --git a/src/animator/SkDisplayList.cpp b/src/animator/SkDisplayList.cpp
index 8424b12..6434601 100644
--- a/src/animator/SkDisplayList.cpp
+++ b/src/animator/SkDisplayList.cpp
@@ -51,7 +51,7 @@
 }
 
 int SkDisplayList::findGroup(SkDrawable* match, SkTDDrawableArray** list,
-        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) {
     *parent = NULL;
     *list = &fDrawList;
     *grandList = &fDrawList;
@@ -69,7 +69,7 @@
 }
 
 int SkDisplayList::SearchForMatch(SkDrawable* match, SkTDDrawableArray** list,
-        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) { 
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList) {
     *found = NULL;
     for (int index = 0; index < (*list)->count(); index++) {
         SkDrawable* draw = (**list)[index];
@@ -88,11 +88,11 @@
                         return index;
                     //perhaps should call SearchGroupForMatch here as well (on scope)
                 }
-            } 
+            }
         }
-        if (draw->isGroup() && SearchGroupForMatch(draw, match, list, parent, found, grandList, index)) 
+        if (draw->isGroup() && SearchGroupForMatch(draw, match, list, parent, found, grandList, index))
             return index;
-        
+
     }
     return -1;
 }
@@ -120,7 +120,7 @@
             continue;
         SkApply* apply = (SkApply*) draw;
         apply->reset();
-    }           
+    }
 }
 
 void SkDisplayList::remove(SkActive* active) {
@@ -156,5 +156,3 @@
     }
 }
 #endif
-
-
diff --git a/src/animator/SkDisplayList.h b/src/animator/SkDisplayList.h
index f520b55..b870598 100644
--- a/src/animator/SkDisplayList.h
+++ b/src/animator/SkDisplayList.h
@@ -35,8 +35,8 @@
     static int fIndent;
     static int fDumpIndex;
 #endif
-    int findGroup(SkDrawable* match, SkTDDrawableArray** list, 
-        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList); 
+    int findGroup(SkDrawable* match, SkTDDrawableArray** list,
+        SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList);
     SkDrawable* get(int index) { return fDrawList[index]; }
     SkMSec getTime() { return fInTime; }
     SkTDDrawableArray* getDrawList() { return &fDrawList; }
@@ -51,8 +51,8 @@
 #endif
     static int SearchForMatch(SkDrawable* match, SkTDDrawableArray** list,
         SkGroup** parent, SkGroup** found, SkTDDrawableArray**grandList);
-    static bool SearchGroupForMatch(SkDrawable* draw, SkDrawable* match, 
-        SkTDDrawableArray** list, SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList, 
+    static bool SearchGroupForMatch(SkDrawable* draw, SkDrawable* match,
+        SkTDDrawableArray** list, SkGroup** parent, SkGroup** found, SkTDDrawableArray** grandList,
         int &index);
 public:
     SkIRect fBounds;
@@ -68,4 +68,3 @@
 };
 
 #endif // SkDisplayList_DEFINED
-
diff --git a/src/animator/SkDisplayMath.cpp b/src/animator/SkDisplayMath.cpp
index c4d496d..bdf377b 100644
--- a/src/animator/SkDisplayMath.cpp
+++ b/src/animator/SkDisplayMath.cpp
@@ -29,7 +29,7 @@
     1.442695041f,   // LOG2E
     3.141592654f,   // PI
     0.707106781f,   // SQRT1_2
-    1.414213562f        // SQRT2 
+    1.414213562f        // SQRT2
 #else
     0x2B7E1,    // E
     0x24D76,    // LN10
@@ -139,7 +139,7 @@
 
 DEFINE_GET_MEMBER(SkDisplayMath);
 
-void SkDisplayMath::executeFunction(SkDisplayable* target, int index, 
+void SkDisplayMath::executeFunction(SkDisplayable* target, int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
         SkScriptValue* scriptValue) {
     if (scriptValue == NULL)
@@ -151,7 +151,7 @@
     SkScalar scalarResult;
     switch (index) {
         case SK_FUNCTION(abs):
-            scalarResult = SkScalarAbs(input); 
+            scalarResult = SkScalarAbs(input);
             break;
         case SK_FUNCTION(acos):
             scalarResult = SkScalarACos(input);
@@ -166,7 +166,7 @@
             scalarResult = SkScalarATan2(input, parameters[1].fOperand.fScalar);
             break;
         case SK_FUNCTION(ceil):
-            scalarResult = SkIntToScalar(SkScalarCeil(input)); 
+            scalarResult = SkIntToScalar(SkScalarCeil(input));
             break;
         case SK_FUNCTION(cos):
             scalarResult = SkScalarCos(input);
@@ -175,7 +175,7 @@
             scalarResult = SkScalarExp(input);
             break;
         case SK_FUNCTION(floor):
-            scalarResult = SkIntToScalar(SkScalarFloor(input)); 
+            scalarResult = SkIntToScalar(SkScalarFloor(input));
             break;
         case SK_FUNCTION(log):
             scalarResult = SkScalarLog(input);
@@ -204,7 +204,7 @@
             scalarResult = fRandom.nextUScalar1();
             break;
         case SK_FUNCTION(round):
-            scalarResult = SkIntToScalar(SkScalarRound(input)); 
+            scalarResult = SkIntToScalar(SkScalarRound(input));
             break;
         case SK_FUNCTION(sin):
             scalarResult = SkScalarSin(input);
@@ -212,7 +212,7 @@
         case SK_FUNCTION(sqrt): {
             SkASSERT(parameters.count() == 1);
             SkASSERT(type == SkType_Float);
-            scalarResult = SkScalarSqrt(input); 
+            scalarResult = SkScalarSqrt(input);
             } break;
         case SK_FUNCTION(tan):
             scalarResult = SkScalarTan(input);
diff --git a/src/animator/SkDisplayMath.h b/src/animator/SkDisplayMath.h
index e21d03f..9153795 100644
--- a/src/animator/SkDisplayMath.h
+++ b/src/animator/SkDisplayMath.h
@@ -16,7 +16,7 @@
 
 class SkDisplayMath : public SkDisplayable {
     DECLARE_DISPLAY_MEMBER_INFO(Math);
-    virtual void executeFunction(SkDisplayable* , int index, 
+    virtual void executeFunction(SkDisplayable* , int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
         SkScriptValue* );
     virtual const SkFunctionParamType* getFunctionsParameters();
@@ -27,6 +27,5 @@
     static const SkFunctionParamType fFunctionParameters[];
 
 };
-    
-#endif // SkDisplayMath_DEFINED
 
+#endif // SkDisplayMath_DEFINED
diff --git a/src/animator/SkDisplayMovie.cpp b/src/animator/SkDisplayMovie.cpp
index 4fbf3f3..f1f82e3 100644
--- a/src/animator/SkDisplayMovie.cpp
+++ b/src/animator/SkDisplayMovie.cpp
@@ -78,7 +78,7 @@
         enable(maker);
     maker.fCanvas->save();
     SkPaint local = SkPaint(*maker.fPaint);
-    bool result = fMovie.draw(maker.fCanvas, &local, 
+    bool result = fMovie.draw(maker.fCanvas, &local,
         maker.fDisplayList.getTime()) != SkAnimator::kNotDifferent;
     maker.fDisplayList.fInvalBounds.join(fMovie.fMaker->fDisplayList.fInvalBounds);
     maker.fCanvas->restore();
@@ -126,5 +126,3 @@
     buildMovie();
     *maker.fMovies.append() = this;
 }
-
-
diff --git a/src/animator/SkDisplayMovie.h b/src/animator/SkDisplayMovie.h
index a599fb6..6210602 100644
--- a/src/animator/SkDisplayMovie.h
+++ b/src/animator/SkDisplayMovie.h
@@ -49,4 +49,3 @@
 };
 
 #endif // SkDisplayMovie_DEFINED
-
diff --git a/src/animator/SkDisplayNumber.cpp b/src/animator/SkDisplayNumber.cpp
index 282dab6..82b658f 100644
--- a/src/animator/SkDisplayNumber.cpp
+++ b/src/animator/SkDisplayNumber.cpp
@@ -31,6 +31,13 @@
 
 DEFINE_GET_MEMBER(SkDisplayNumber);
 
+#if defined _WIN32
+#pragma warning ( push )
+// we are intentionally causing an overflow here
+//      (warning C4756: overflow in constant arithmetic)
+#pragma warning ( disable : 4756 )
+#endif
+
 bool SkDisplayNumber::getProperty(int index, SkScriptValue* value) const {
     SkScalar constant;
     switch (index) {
@@ -57,3 +64,7 @@
     value->fType = SkType_Float;
     return true;
 }
+
+#if defined _WIN32
+#pragma warning ( pop )
+#endif
diff --git a/src/animator/SkDisplayNumber.h b/src/animator/SkDisplayNumber.h
index 7092e21..16c6f4f 100644
--- a/src/animator/SkDisplayNumber.h
+++ b/src/animator/SkDisplayNumber.h
@@ -18,5 +18,5 @@
     virtual bool getProperty(int index, SkScriptValue* value) const;
 private:
 };
-    
+
 #endif // SkDisplayNumber_DEFINED
diff --git a/src/animator/SkDisplayPost.cpp b/src/animator/SkDisplayPost.cpp
index 8283a9c..cc45b21 100644
--- a/src/animator/SkDisplayPost.cpp
+++ b/src/animator/SkDisplayPost.cpp
@@ -47,19 +47,19 @@
         delete *part;
 }
 
-bool SkPost::add(SkAnimateMaker& , SkDisplayable* child) {
+bool SkPost::addChild(SkAnimateMaker& , SkDisplayable* child) {
     SkASSERT(child && child->isDataInput());
     SkDataInput* part = (SkDataInput*) child;
     *fParts.append() = part;
     return true;
 }
 
-bool SkPost::childrenNeedDisposing() const { 
-    return false; 
+bool SkPost::childrenNeedDisposing() const {
+    return false;
 }
 
-void SkPost::dirty() { 
-    fDirty = true; 
+void SkPost::dirty() {
+    fDirty = true;
 }
 
 #ifdef SK_DUMP_ENABLED
@@ -74,13 +74,9 @@
     else
         SkDebugf("type=\"%s\" ", eventType->c_str());
     delete eventType;
-    
+
     if (delay > 0) {
-#ifdef SK_CAN_USE_FLOAT
         SkDebugf("delay=\"%g\" ", SkScalarToFloat(SkScalarDiv(delay, 1000)));
-#else
-        SkDebugf("delay=\"%x\" ", SkScalarDiv(delay, 1000));
-#endif
     }
 //  if (initialized == false)
 //      SkDebugf("(uninitialized) ");
@@ -103,7 +99,7 @@
     SkDisplayList::fIndent += 4;
     //this seems to work, but kinda hacky
     //for some reason the last part is id, which i don't want
-    //and the parts seem to be in the reverse order from the one in which we find the 
+    //and the parts seem to be in the reverse order from the one in which we find the
     //data itself
     //SkDataInput** ptr = fParts.end();
     //SkDataInput* data;
@@ -131,11 +127,7 @@
             case SkMetaData::kScalar_Type: {
                 SkScalar scalar;
                 meta.findScalar(name, &scalar);
-#ifdef SK_CAN_USE_FLOAT
                 SkDebugf("float=\"%g\" ", SkScalarToFloat(scalar));
-#else
-                SkDebugf("float=\"%x\" ", scalar);
-#endif
                 } break;
             case SkMetaData::kString_Type:
                 SkDebugf("string=\"%s\" ", meta.findString(name));
@@ -219,7 +211,7 @@
     SkAnimator* anim = maker.getAnimator();
     if (targetID == 0) {
         isAnimatorEvent = fEvent.findString("id") != NULL;
-        if (isAnimatorEvent) 
+        if (isAnimatorEvent)
             targetID = anim->getSinkID();
         else if (maker.fHostEventSinkID)
             targetID = maker.fHostEventSinkID;
@@ -243,7 +235,7 @@
     const char* ch = sink.c_str();
     do {
         const char* end = strchr(ch, '.');
-        size_t len = end ? end - ch : strlen(ch);
+        size_t len = end ? (size_t) (end - ch) : strlen(ch);
         SkDisplayable* displayable = NULL;
         if (SK_LITERAL_STR_EQUAL("parent", ch, len)) {
             if (fTargetMaker->fParentMaker)
@@ -268,7 +260,7 @@
     SkAnimator* anim = fTargetMaker->getAnimator();
     fSinkID = anim->getSinkID();
 }
- 
+
 bool SkPost::hasEnable() const {
     return true;
 }
@@ -282,8 +274,8 @@
     }
 }
 
-void SkPost::setChildHasID() { 
-    fChildHasID = true; 
+void SkPost::setChildHasID() {
+    fChildHasID = true;
 }
 
 bool SkPost::setProperty(int index, SkScriptValue& value) {
@@ -304,4 +296,3 @@
     }
     return true;
 }
-
diff --git a/src/animator/SkDisplayPost.h b/src/animator/SkDisplayPost.h
index b417bab..cd22306 100644
--- a/src/animator/SkDisplayPost.h
+++ b/src/animator/SkDisplayPost.h
@@ -27,7 +27,7 @@
     };
     SkPost();
     virtual ~SkPost();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
     virtual bool childrenNeedDisposing() const;
     virtual void dirty();
 #ifdef SK_DUMP_ENABLED
diff --git a/src/animator/SkDisplayRandom.cpp b/src/animator/SkDisplayRandom.cpp
index ac6c3a1..2efe8dc 100644
--- a/src/animator/SkDisplayRandom.cpp
+++ b/src/animator/SkDisplayRandom.cpp
@@ -35,15 +35,9 @@
 #ifdef SK_DUMP_ENABLED
 void SkDisplayRandom::dump(SkAnimateMaker* maker) {
     dumpBase(maker);
-#ifdef SK_CAN_USE_FLOAT
     SkDebugf("min=\"%g\" ", SkScalarToFloat(min));
     SkDebugf("max=\"%g\" ", SkScalarToFloat(max));
-    SkDebugf("blend=\"%g\" ", SkScalarToFloat(blend));    
-#else
-    SkDebugf("min=\"%x\" ", min);
-    SkDebugf("max=\"%x\" ", max);
-    SkDebugf("blend=\"%x\" ", blend);    
-#endif
+    SkDebugf("blend=\"%g\" ", SkScalarToFloat(blend));
     SkDebugf("/>\n");
 }
 #endif
@@ -69,4 +63,3 @@
     fRandom.setSeed(value.fOperand.fS32);
     return true;
 }
-
diff --git a/src/animator/SkDisplayRandom.h b/src/animator/SkDisplayRandom.h
index 87956d2..1c38653 100644
--- a/src/animator/SkDisplayRandom.h
+++ b/src/animator/SkDisplayRandom.h
@@ -38,4 +38,3 @@
 };
 
 #endif // SkDisplayRandom_DEFINED
-
diff --git a/src/animator/SkDisplayScreenplay.cpp b/src/animator/SkDisplayScreenplay.cpp
index 463f948..2663b43 100644
--- a/src/animator/SkDisplayScreenplay.cpp
+++ b/src/animator/SkDisplayScreenplay.cpp
@@ -18,5 +18,3 @@
 #endif
 
 DEFINE_GET_MEMBER(SkDisplayScreenplay);
-
-
diff --git a/src/animator/SkDisplayType.cpp b/src/animator/SkDisplayType.cpp
index 4dfa62f..dc52f0c 100644
--- a/src/animator/SkDisplayType.cpp
+++ b/src/animator/SkDisplayType.cpp
@@ -67,7 +67,7 @@
 #else
     #define CASE_DEBUG_RETURN_NIL(_class)
 #endif
-    
+
 
 SkDisplayTypes SkDisplayType::gNewTypes = kNumberOfTypes;
 
@@ -133,14 +133,14 @@
         CASE_NEW(Group);
         CASE_NEW(HitClear);
         CASE_NEW(HitTest);
-        CASE_NEW(Image);
+        CASE_NEW(ImageBaseBitmap);
         CASE_NEW(Include);
         CASE_NEW(Input);
         CASE_DISPLAY_NEW(Int);
         CASE_DEBUG_RETURN_NIL(Join);
         CASE_NEW(Line);
         CASE_NEW(LineTo);
-        CASE_NEW(LinearGradient);
+        CASE_NEW(DrawLinearGradient);
         CASE_DRAW_NEW(MaskFilter);
         CASE_DEBUG_RETURN_NIL(MaskFilterBlurStyle);
         // maskfilterlight
@@ -167,7 +167,7 @@
         CASE_NEW(RLineTo);
         CASE_NEW(RMoveTo);
         CASE_NEW(RQuadTo);
-        CASE_NEW(RadialGradient);
+        CASE_NEW(DrawRadialGradient);
         CASE_DISPLAY_NEW(Random);
         CASE_DRAW_NEW(Rect);
         CASE_NEW(RectToRect);
@@ -224,7 +224,7 @@
     info = SkDisplay##_class::fInfo; infoCount = SkDisplay##_class::fInfoCount; \
     break
 
-const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* maker, 
+const SkMemberInfo* SkDisplayType::GetMembers(SkAnimateMaker* maker,
         SkDisplayTypes type, int* infoCountPtr) {
     const SkMemberInfo* info = NULL;
     int infoCount = 0;
@@ -284,18 +284,18 @@
         CASE_GET_INFO(FromPath);
         // frompathmode
         // full
-        CASE_GET_INFO(Gradient);
+        CASE_GET_INFO(DrawGradient);
         CASE_GET_INFO(Group);
         CASE_GET_INFO(HitClear);
         CASE_GET_INFO(HitTest);
-        CASE_GET_INFO(Image);
+        CASE_GET_INFO(ImageBaseBitmap);
         CASE_GET_INFO(Include);
         CASE_GET_INFO(Input);
         CASE_GET_DISPLAY_INFO(Int);
         // join
         CASE_GET_INFO(Line);
         CASE_GET_INFO(LineTo);
-        CASE_GET_INFO(LinearGradient);
+        CASE_GET_INFO(DrawLinearGradient);
         // maskfilter
         // maskfilterblurstyle
         // maskfilterlight
@@ -322,7 +322,7 @@
         CASE_GET_INFO(RLineTo);
         CASE_GET_INFO(RMoveTo);
         CASE_GET_INFO(RQuadTo);
-        CASE_GET_INFO(RadialGradient);
+        CASE_GET_INFO(DrawRadialGradient);
         CASE_GET_DISPLAY_INFO(Random);
         CASE_GET_DRAW_INFO(Rect);
         CASE_GET_INFO(RectToRect);
@@ -355,7 +355,7 @@
         CASE_GET_DRAW_INFO(Typeface);
         // xfermode
         // knumberoftypes
-        default: 
+        default:
             if (maker) {
                 SkExtras** end = maker->fExtras.end();
                 for (SkExtras** extraPtr = maker->fExtras.begin(); extraPtr < end; extraPtr++) {
@@ -370,7 +370,7 @@
     return info;
 }
 
-const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* maker, 
+const SkMemberInfo* SkDisplayType::GetMember(SkAnimateMaker* maker,
         SkDisplayTypes type, const char** matchPtr ) {
     int infoCount;
     const SkMemberInfo* info = GetMembers(maker, type, &infoCount);
@@ -455,14 +455,14 @@
     { "group", SkType_Group                     INIT_BOOL_FIELDS },
     { "hitClear", SkType_HitClear               INIT_BOOL_FIELDS },
     { "hitTest", SkType_HitTest                 INIT_BOOL_FIELDS },
-    { "image", SkType_Image                     INIT_BOOL_FIELDS },
+    { "image", SkType_ImageBaseBitmap           INIT_BOOL_FIELDS },
     { "include", SkType_Include                 INIT_BOOL_FIELDS },
     { "input", SkType_Input                     INIT_BOOL_FIELDS },
     { "int", SkType_Int                         INIT_BOOL_FIELDS },
     // join
     { "line", SkType_Line                       INIT_BOOL_FIELDS },
     { "lineTo", SkType_LineTo                   INIT_BOOL_FIELDS },
-    { "linearGradient", SkType_LinearGradient   INIT_BOOL_FIELDS },
+    { "linearGradient", SkType_DrawLinearGradient   INIT_BOOL_FIELDS },
     { "maskFilter", SkType_MaskFilter           INIT_BOOL_FIELDS },
     // maskfilterblurstyle
     // maskfilterlight
@@ -489,7 +489,7 @@
     { "rLineTo", SkType_RLineTo                 INIT_BOOL_FIELDS },
     { "rMoveTo", SkType_RMoveTo                 INIT_BOOL_FIELDS },
     { "rQuadTo", SkType_RQuadTo                 INIT_BOOL_FIELDS },
-    { "radialGradient", SkType_RadialGradient   INIT_BOOL_FIELDS },
+    { "radialGradient", SkType_DrawRadialGradient   INIT_BOOL_FIELDS },
     DISPLAY_NAME("random", SkType_Random),
     { "rect", SkType_Rect                       INIT_BOOL_FIELDS },
     { "rectToRect", SkType_RectToRect           INIT_BOOL_FIELDS },
@@ -546,7 +546,7 @@
     SkASSERT(info);
     if (info->fType != SkType_BaseClassInfo)
         return SkType_Unknown; // if no base, done
-    // !!! could change SK_MEMBER_INHERITED macro to take type, stuff in offset, so that 
+    // !!! could change SK_MEMBER_INHERITED macro to take type, stuff in offset, so that
     // this (and table builder) could know type without the following steps:
     const SkMemberInfo* inherited = info->getInherited();
     SkDisplayTypes result = (SkDisplayTypes) (SkType_Unknown + 1);
@@ -560,7 +560,7 @@
 }
 
 SkDisplayTypes SkDisplayType::GetType(SkAnimateMaker* maker, const char match[], size_t len ) {
-    int index = SkStrSearch(&gTypeNames[0].fName, kTypeNamesSize, match, 
+    int index = SkStrSearch(&gTypeNames[0].fName, kTypeNamesSize, match,
         len, sizeof(gTypeNames[0]));
     if (index >= 0 && index < kTypeNamesSize)
         return gTypeNames[index].fType;
@@ -636,11 +636,11 @@
         case SkType_FromPath:
         case SkType_Full:
         case SkType_Group:
-        case SkType_Image:
+        case SkType_ImageBaseBitmap:
         case SkType_Input:
         case SkType_Line:
         case SkType_LineTo:
-        case SkType_LinearGradient:
+        case SkType_DrawLinearGradient:
         case SkType_Matrix:
         case SkType_Move:
         case SkType_MoveTo:
@@ -657,7 +657,7 @@
         case SkType_RLineTo:
         case SkType_RMoveTo:
         case SkType_RQuadTo:
-        case SkType_RadialGradient:
+        case SkType_DrawRadialGradient:
         case SkType_Random:
         case SkType_Rect:
         case SkType_RectToRect:
diff --git a/src/animator/SkDisplayType.h b/src/animator/SkDisplayType.h
index fe82b31..474a65e 100644
--- a/src/animator/SkDisplayType.h
+++ b/src/animator/SkDisplayType.h
@@ -14,9 +14,7 @@
 #include "SkScalar.h"
 
 #ifdef SK_DEBUG
-    #ifdef SK_CAN_USE_FLOAT
-        #define SK_DUMP_ENABLED
-    #endif
+    #define SK_DUMP_ENABLED
     #ifdef SK_BUILD_FOR_MAC
         #define SK_FIND_LEAKS
     #endif
@@ -86,18 +84,18 @@
     SkType_FromPath,
     SkType_FromPathMode,
     SkType_Full,
-    SkType_Gradient,
+    SkType_DrawGradient,
     SkType_Group,
     SkType_HitClear,
     SkType_HitTest,
-    SkType_Image,
+    SkType_ImageBaseBitmap,
     SkType_Include,
     SkType_Input,
     SkType_Int,
     SkType_Join,
     SkType_Line, // simple line primitive
     SkType_LineTo, // used as part of path construction
-    SkType_LinearGradient,
+    SkType_DrawLinearGradient,
     SkType_MaskFilter,
     SkType_MaskFilterBlurStyle,
     SkType_MaskFilterLight,
@@ -124,7 +122,7 @@
     SkType_RLineTo,
     SkType_RMoveTo,
     SkType_RQuadTo,
-    SkType_RadialGradient,
+    SkType_DrawRadialGradient,
     SkType_Random,
     SkType_Rect,
     SkType_RectToRect,
@@ -189,7 +187,7 @@
     static SkDisplayTypes RegisterNewType();
     static SkDisplayTypes Resolve(const char[] , const SkMemberInfo** );
 #ifdef SK_DEBUG
-    static bool IsAnimate(SkDisplayTypes type ) { return type == SkType_Animate || 
+    static bool IsAnimate(SkDisplayTypes type ) { return type == SkType_Animate ||
         type == SkType_Set; }
     static const char* GetName(SkAnimateMaker* , SkDisplayTypes );
 #endif
diff --git a/src/animator/SkDisplayTypes.cpp b/src/animator/SkDisplayTypes.cpp
index 6132eb7..287ca6e 100644
--- a/src/animator/SkDisplayTypes.cpp
+++ b/src/animator/SkDisplayTypes.cpp
@@ -11,7 +11,7 @@
 #include "SkAnimateBase.h"
 
 bool SkDisplayDepend::canContainDependents() const {
-    return true; 
+    return true;
 }
 
 void SkDisplayDepend::dirty() {
@@ -81,11 +81,7 @@
 #ifdef SK_DUMP_ENABLED
 void SkDisplayFloat::dump(SkAnimateMaker* maker) {
     dumpBase(maker);
-#ifdef SK_CAN_USE_FLOAT
     SkDebugf("value=\"%g\" />\n", SkScalarToFloat(value));
-#else
-    SkDebugf("value=\"%x\" />\n", value);
-#endif
 }
 #endif
 
@@ -122,7 +118,7 @@
 SkDisplayString::SkDisplayString(SkString& copyFrom) : value(copyFrom) {
 }
 
-void SkDisplayString::executeFunction(SkDisplayable* target, int index, 
+void SkDisplayString::executeFunction(SkDisplayable* target, int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
         SkScriptValue* scriptValue) {
     if (scriptValue == NULL)
@@ -154,7 +150,7 @@
 }
 
 bool SkDisplayString::getProperty(int index, SkScriptValue* scriptValue) const {
-    switch (index) { 
+    switch (index) {
         case SK_PROPERTY(length):
             scriptValue->fType = SkType_Int;
             scriptValue->fOperand.fS32 = (int32_t) value.size();
@@ -205,7 +201,7 @@
 }
 
 bool SkDisplayArray::getProperty(int index, SkScriptValue* value) const {
-    switch (index) { 
+    switch (index) {
         case SK_PROPERTY(length):
             value->fType = SkType_Int;
             value->fOperand.fS32 = values.count();
@@ -216,6 +212,3 @@
     }
     return true;
 }
-
-
-
diff --git a/src/animator/SkDisplayTypes.h b/src/animator/SkDisplayTypes.h
index 75cace6..1a3d0e5 100644
--- a/src/animator/SkDisplayTypes.h
+++ b/src/animator/SkDisplayTypes.h
@@ -75,7 +75,7 @@
     DECLARE_DISPLAY_MEMBER_INFO(String);
     SkDisplayString();
     SkDisplayString(SkString& );
-    virtual void executeFunction(SkDisplayable* , int index, 
+    virtual void executeFunction(SkDisplayable* , int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
         SkScriptValue* );
     virtual const SkFunctionParamType* getFunctionsParameters();
@@ -104,4 +104,3 @@
 };
 
 #endif // SkDisplayTypes_DEFINED
-
diff --git a/src/animator/SkDisplayXMLParser.cpp b/src/animator/SkDisplayXMLParser.cpp
index 68dc259..3ebf9dc 100644
--- a/src/animator/SkDisplayXMLParser.cpp
+++ b/src/animator/SkDisplayXMLParser.cpp
@@ -34,7 +34,7 @@
     "last offset in gradient must be one",
     "offsets in gradient must be increasing",
     "first offset in gradient must be zero",
-    "gradient attribute \"points\" must have length of four", 
+    "gradient attribute \"points\" must have length of four",
     "in include ",
     "in movie ",
     "include name unknown or missing ",
@@ -70,7 +70,7 @@
 
 
 SkDisplayXMLParser::SkDisplayXMLParser(SkAnimateMaker& maker)
-    : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude), 
+    : INHERITED(&maker.fError), fMaker(maker), fInInclude(maker.fInInclude),
         fInSkia(maker.fInInclude), fCurrDisplayable(NULL)
 {
 }
@@ -94,7 +94,7 @@
     return onAddAttributeLen(name, value, strlen(value));
 }
 
-bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[], 
+bool SkDisplayXMLParser::onAddAttributeLen(const char attrName[], const char attrValue[],
                                         size_t attrValueLen)
 {
     if (fCurrDisplayable == NULL)    // this signals we should ignore attributes for this element
@@ -172,16 +172,16 @@
         SkDisplayable* displayable = container.fDisplayable;
         fMaker.fEndDepth = parentIndex;
         displayable->onEndElement(fMaker);
-        if (fMaker.fError.hasError()) 
+        if (fMaker.fError.hasError())
             return true;
         if (parentIndex > 0) {
             SkDisplayable* parent = fParents[parentIndex - 1].fDisplayable;
-            bool result = parent->add(fMaker, displayable);
-            if (fMaker.hasError()) 
+            bool result = parent->addChild(fMaker, displayable);
+            if (fMaker.hasError())
                 return true;
             if (result == false) {
                 int infoCount;
-                const SkMemberInfo* info = 
+                const SkMemberInfo* info =
                     SkDisplayType::GetMembers(&fMaker, fParents[parentIndex - 1].fType, &infoCount);
                 const SkMemberInfo* foundInfo;
                 if ((foundInfo = searchContainer(info, infoCount)) != NULL) {
@@ -213,7 +213,7 @@
             SkDebugf("%s\n", debugOut.c_str());
 #endif
             fMaker.fEvents.doEvent(fMaker, SkDisplayEvent::kOnload, NULL);
-            if (fMaker.fError.hasError()) 
+            if (fMaker.fError.hasError())
                 return true;
             fMaker.fEvents.removeEvent(SkDisplayEvent::kOnload, NULL);
 
@@ -281,7 +281,7 @@
         }
         Parent* container = fParents.end() - 1;
         SkDisplayTypes type = (SkDisplayTypes) info->fType;
-        if (type == SkType_MemberProperty) 
+        if (type == SkType_MemberProperty)
             type = info->propertyType();
         SkDisplayTypes containerType = container->fType;
         if (type == containerType && (type == SkType_Rect || type == SkType_Polygon ||
@@ -296,7 +296,7 @@
         }
         return info;
 next:
-        if (type == SkType_Drawable || (type == SkType_Displayable && 
+        if (type == SkType_Drawable || (type == SkType_Displayable &&
             container->fDisplayable->isDrawable())) {
 rectNext:
             if (fParents.count() > 1) {
@@ -314,5 +314,3 @@
         return lastResort;
     return NULL;
 }
-
-
diff --git a/src/animator/SkDisplayXMLParser.h b/src/animator/SkDisplayXMLParser.h
index c18e48f..9c561ed 100644
--- a/src/animator/SkDisplayXMLParser.h
+++ b/src/animator/SkDisplayXMLParser.h
@@ -89,5 +89,3 @@
 };
 
 #endif // SkDisplayXMLParser_DEFINED
-
-
diff --git a/src/animator/SkDisplayable.cpp b/src/animator/SkDisplayable.cpp
index ebcc8a3..aff3aae 100644
--- a/src/animator/SkDisplayable.cpp
+++ b/src/animator/SkDisplayable.cpp
@@ -21,7 +21,7 @@
 #endif
 
 #ifdef SK_DEBUG
-SkDisplayable::SkDisplayable() { 
+SkDisplayable::SkDisplayable() {
     id = _id.c_str();
 #ifdef SK_FIND_LEAKS
     // fAllocationCount++;
@@ -39,21 +39,21 @@
 #endif
 }
 
-bool SkDisplayable::add(SkAnimateMaker& , SkDisplayable* child) {
-    return false; 
+bool SkDisplayable::addChild(SkAnimateMaker& , SkDisplayable* child) {
+    return false;
 }
 
-//void SkDisplayable::apply(SkAnimateMaker& , const SkMemberInfo* , 
-//      SkDisplayable* , SkScalar [], int count) { 
-//  SkASSERT(0); 
+//void SkDisplayable::apply(SkAnimateMaker& , const SkMemberInfo* ,
+//      SkDisplayable* , SkScalar [], int count) {
+//  SkASSERT(0);
 //}
 
 bool SkDisplayable::canContainDependents() const {
-    return false; 
+    return false;
 }
- 
-bool SkDisplayable::childrenNeedDisposing() const { 
-    return false; 
+
+bool SkDisplayable::childrenNeedDisposing() const {
+    return false;
 }
 
 void SkDisplayable::clearBounder() {
@@ -170,18 +170,18 @@
                 //last two are dummies
                 dumpValues(info, value.fType, value.fOperand, blankValue.fOperand, value.fOperand, blankValue.fOperand);
                 }
-            
+
             propIndex++;
             continue;
         }
         if (SkDisplayType::IsDisplayable(maker, info->fType)) {
             continue;
         }
-        
+
         if (info->fType == SkType_MemberFunction)
             continue;
-            
-            
+
+
         if (info->fType == SkType_Array) {
             SkTDOperandArray* array = (SkTDOperandArray*) info->memberData(this);
             int arrayCount;
@@ -201,18 +201,14 @@
                         case SkType_Displayable:
                             SkDebugf("%s", op->fDisplayable->id);
                             break;
-                        case SkType_Int:                            
+                        case SkType_Int:
                             SkDebugf("%d", op->fS32);
                             break;
                         case SkType_Float:
-#ifdef SK_CAN_USE_FLOAT
                             SkDebugf("%g", SkScalarToFloat(op->fScalar));
-#else
-                            SkDebugf("%x", op->fScalar);
-#endif
                             break;
                         case SkType_String:
-                        case SkType_DynamicString:    
+                        case SkType_DynamicString:
                             SkDebugf("%s", op->fString->c_str());
                             break;
                         default:
@@ -223,16 +219,16 @@
             SkDebugf("]\" ");
             continue;
         }
-        
+
         if (info->fType == SkType_String || info->fType == SkType_DynamicString) {
             SkString* string;
             info->getString(this, &string);
             if (string->isEmpty() == false)
-                SkDebugf("%s=\"%s\"\t", info->fName, string->c_str()); 
+                SkDebugf("%s=\"%s\"\t", info->fName, string->c_str());
             continue;
         }
-        
-        
+
+
         blankInfo = blankCopy->getMember(index);
         int i = info->fCount;
         info->getValue(this, values, i);
@@ -256,7 +252,7 @@
 }
 
 void SkDisplayable::dumpChildren(SkAnimateMaker* maker, bool closedAngle) {
-    
+
     int index = -1;
     const SkMemberInfo* info;
     index = -1;
@@ -311,11 +307,7 @@
     //should make this a separate case in dump attrs, rather than make dump values have a larger signature
     case SkType_Point:
         if (op.fScalar != blankOp.fScalar || op2.fScalar != blankOp.fScalar) {
-#ifdef SK_CAN_USE_FLOAT
             SkDebugf("%s=\"[%g,%g]\" ", info->fName, SkScalarToFloat(op.fScalar), SkScalarToFloat(op2.fScalar));
-#else
-            SkDebugf("%s=\"[%x,%x]\" ", info->fName, op.fScalar, op2.fScalar);
-#endif
         }
         break;
     case SkType_FromPathMode:
@@ -362,7 +354,7 @@
         break;
     case SkType_TileMode:
         //correct to look at the S32?
-        if (op.fS32 != blankOp.fS32) 
+        if (op.fS32 != blankOp.fS32)
             SkDebugf("%s=\"%s\" ", info->fName, op.fS32 == 0 ? "clamp" : op.fS32 == 1 ? "repeat" : "mirror");
         break;
     case SkType_Boolean:
@@ -375,46 +367,38 @@
         break;
     case SkType_Float:
         if (op.fScalar != blankOp.fScalar) { //or /65536?
-#ifdef SK_CAN_USE_FLOAT
             SkDebugf("%s=\"%g\"  ", info->fName, SkScalarToFloat(op.fScalar));
-#else
-            SkDebugf("%s=\"%x\"  ", info->fName, op.fScalar);
-#endif
         }
         break;
     case SkType_String:
     case SkType_DynamicString:
-        if (op.fString->size() > 0) 
+        if (op.fString->size() > 0)
             SkDebugf("%s=\"%s\" ", info->fName, op.fString->c_str());
         break;
     case SkType_MSec:
         if (op.fS32 != blankOp.fS32) {
-#ifdef SK_CAN_USE_FLOAT
             SkDebugf(" %s=\"%g\"  ", info->fName, SkScalarToFloat(SkScalarDiv(op.fS32, 1000)));
-#else
-            SkDebugf(" %s=\"%x\"  ", info->fName, SkScalarDiv(op.fS32, 1000));
-#endif
         }
     default:
         SkDebugf("");
-    }    
+    }
 }
 
 #endif
 
-bool SkDisplayable::enable( SkAnimateMaker& ) { 
+bool SkDisplayable::enable( SkAnimateMaker& ) {
     return false;
 }
 
 void SkDisplayable::enableBounder() {
 }
 
-void SkDisplayable::executeFunction(SkDisplayable* , int index, 
+void SkDisplayable::executeFunction(SkDisplayable* , int index,
         SkTDArray<SkScriptValue>& , SkDisplayTypes, SkScriptValue*  ) {
-    SkASSERT(0); 
+    SkASSERT(0);
 }
 
-void SkDisplayable::executeFunction(SkDisplayable* target, 
+void SkDisplayable::executeFunction(SkDisplayable* target,
         const SkMemberInfo* info, SkTypedArray* values, SkScriptValue* value) {
     SkTDArray<SkScriptValue> typedValues;
     for (SkOperand* op = values->begin(); op < values->end(); op++) {
@@ -426,9 +410,9 @@
     executeFunction(target, info->functionIndex(), typedValues, info->getType(), value);
 }
 
-void SkDisplayable::executeFunction2(SkDisplayable* , int index, 
+void SkDisplayable::executeFunction2(SkDisplayable* , int index,
         SkOpArray* params, SkDisplayTypes, SkOperand2*  ) {
-    SkASSERT(0); 
+    SkASSERT(0);
 }
 
 void SkDisplayable::getBounds(SkRect* rect) {
@@ -441,15 +425,15 @@
     return NULL;
 }
 
-const SkMemberInfo* SkDisplayable::getMember(int index) { 
-    return NULL; 
+const SkMemberInfo* SkDisplayable::getMember(int index) {
+    return NULL;
 }
 
-const SkMemberInfo* SkDisplayable::getMember(const char name[]) { 
-    return NULL; 
+const SkMemberInfo* SkDisplayable::getMember(const char name[]) {
+    return NULL;
 }
 
-const SkFunctionParamType* SkDisplayable::getParameters(const SkMemberInfo* info, 
+const SkFunctionParamType* SkDisplayable::getParameters(const SkMemberInfo* info,
         int* paramCount) {
     const SkFunctionParamType* params = getFunctionsParameters();
     SkASSERT(params != NULL);
@@ -476,17 +460,17 @@
 }
 
 bool SkDisplayable::getProperty(int index, SkScriptValue* ) const {
-//  SkASSERT(0); 
-    return false; 
+//  SkASSERT(0);
+    return false;
 }
 
 bool SkDisplayable::getProperty2(int index, SkOperand2* value) const {
-    SkASSERT(0); 
-    return false; 
+    SkASSERT(0);
+    return false;
 }
 
-SkDisplayTypes SkDisplayable::getType() const { 
-    return SkType_Unknown; 
+SkDisplayTypes SkDisplayable::getType() const {
+    return SkType_Unknown;
 }
 
 bool SkDisplayable::hasEnable() const {
@@ -494,7 +478,7 @@
 }
 
 bool SkDisplayable::isDrawable() const {
-    return false; 
+    return false;
 }
 
 void SkDisplayable::onEndElement(SkAnimateMaker& ) {}
@@ -507,8 +491,8 @@
     return false;
 }
 
-//SkDisplayable* SkDisplayable::resolveTarget(SkAnimateMaker& ) { 
-//  return this; 
+//SkDisplayable* SkDisplayable::resolveTarget(SkAnimateMaker& ) {
+//  return this;
 //}
 
 void SkDisplayable::setChildHasID() {
@@ -519,8 +503,8 @@
 }
 
 bool SkDisplayable::setProperty(int index, SkScriptValue& ) {
-    //SkASSERT(0); 
-    return false; 
+    //SkASSERT(0);
+    return false;
 }
 
 void SkDisplayable::setReference(const SkMemberInfo* info, SkDisplayable* displayable) {
@@ -554,5 +538,3 @@
 void SkDisplayable::validate() {
 }
 #endif
-
-
diff --git a/src/animator/SkDisplayable.h b/src/animator/SkDisplayable.h
index a3db002..4fd47ab 100644
--- a/src/animator/SkDisplayable.h
+++ b/src/animator/SkDisplayable.h
@@ -32,7 +32,7 @@
     SkDisplayable();
 #endif
     virtual ~SkDisplayable();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* child);
     virtual bool canContainDependents() const;
     virtual bool childrenNeedDisposing() const;
     virtual void clearBounder();
@@ -50,30 +50,30 @@
 #endif
     virtual bool enable( SkAnimateMaker& );
     virtual void enableBounder();
-    virtual void executeFunction(SkDisplayable* , int functionIndex, 
-        SkTDArray<SkScriptValue>& , SkDisplayTypes , SkScriptValue* ); 
-    void executeFunction(SkDisplayable* , const SkMemberInfo* , 
-        SkTypedArray* , SkScriptValue* ); 
-    virtual void executeFunction2(SkDisplayable* , int functionIndex, 
+    virtual void executeFunction(SkDisplayable* , int functionIndex,
+        SkTDArray<SkScriptValue>& , SkDisplayTypes , SkScriptValue* );
+    void executeFunction(SkDisplayable* , const SkMemberInfo* ,
+        SkTypedArray* , SkScriptValue* );
+    virtual void executeFunction2(SkDisplayable* , int functionIndex,
         SkOpArray* params , SkDisplayTypes , SkOperand2* ); // compiled scripting experiment
     virtual void getBounds(SkRect* );
     virtual const SkFunctionParamType* getFunctionsParameters();
     virtual const SkMemberInfo* getMember(int index);
     virtual const SkMemberInfo* getMember(const char name[]);
-    const SkFunctionParamType* getParameters(const SkMemberInfo* info, 
+    const SkFunctionParamType* getParameters(const SkMemberInfo* info,
         int* paramCount);
     virtual SkDisplayable* getParent() const;
     virtual bool getProperty(int index, SkScriptValue* value) const;
     virtual bool getProperty2(int index, SkOperand2* value) const;    // compiled scripting experiment
     virtual SkDisplayTypes getType() const;
     virtual bool hasEnable() const;
-    bool isAnimate() const { 
-        SkDisplayTypes type = getType(); 
+    bool isAnimate() const {
+        SkDisplayTypes type = getType();
         return type == SkType_Animate || type == SkType_Set; }
     bool isApply() const { return getType() == SkType_Apply; }
     bool isColor() const { return getType() == SkType_Color; }
     virtual bool isDrawable() const;
-    bool isGroup() const { return getType() == SkType_Group || 
+    bool isGroup() const { return getType() == SkType_Group ||
         getType() == SkType_Save || getType() == SkType_DrawTo ||
         getType() == SkType_SaveLayer; }
     bool isMatrix() const { return getType() == SkType_Matrix; }
diff --git a/src/animator/SkDraw3D.cpp b/src/animator/SkDraw3D.cpp
index ce2bec4..9e1c2cc 100644
--- a/src/animator/SkDraw3D.cpp
+++ b/src/animator/SkDraw3D.cpp
@@ -25,7 +25,7 @@
 DEFINE_NO_VIRTUALS_GET_MEMBER(Sk3D_Point);
 
 Sk3D_Point::Sk3D_Point() {
-    fPoint.set(0, 0, 0);    
+    fPoint.set(0, 0, 0);
 }
 
 #if SK_USE_CONDENSED_INFO == 0
@@ -85,7 +85,7 @@
 
 DEFINE_GET_MEMBER(Sk3D_Patch);
 
-void Sk3D_Patch::executeFunction(SkDisplayable* target, int index, 
+void Sk3D_Patch::executeFunction(SkDisplayable* target, int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
         SkScriptValue* ) {
     SkASSERT(target == this);
@@ -93,7 +93,7 @@
         case SK_FUNCTION(rotateDegrees):
             SkASSERT(parameters.count() == 3);
             SkASSERT(type == SkType_Float);
-            fPatch.rotateDegrees(parameters[0].fOperand.fScalar, 
+            fPatch.rotateDegrees(parameters[0].fOperand.fScalar,
                 parameters[1].fOperand.fScalar, parameters[2].fOperand.fScalar);
             break;
         default:
@@ -104,6 +104,3 @@
 const SkFunctionParamType* Sk3D_Patch::getFunctionsParameters() {
     return fFunctionParameters;
 }
-
-
-
diff --git a/src/animator/SkDraw3D.h b/src/animator/SkDraw3D.h
index e6549ea..a204044 100644
--- a/src/animator/SkDraw3D.h
+++ b/src/animator/SkDraw3D.h
@@ -38,7 +38,7 @@
 class Sk3D_Patch : public SkDisplayable {
     DECLARE_MEMBER_INFO(3D_Patch);
 private:
-    virtual void executeFunction(SkDisplayable* , int index, 
+    virtual void executeFunction(SkDisplayable* , int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
         SkScriptValue* );
     virtual const SkFunctionParamType* getFunctionsParameters();
@@ -48,4 +48,3 @@
 };
 
 #endif // SkDraw3D_DEFINED
-
diff --git a/src/animator/SkDrawBitmap.cpp b/src/animator/SkDrawBitmap.cpp
index a1d49d6..4604a71 100644
--- a/src/animator/SkDrawBitmap.cpp
+++ b/src/animator/SkDrawBitmap.cpp
@@ -56,7 +56,7 @@
 
 DEFINE_GET_MEMBER(SkDrawBitmap);
 
-SkDrawBitmap::SkDrawBitmap() : format((SkBitmap::Config) -1), height(-1), 
+SkDrawBitmap::SkDrawBitmap() : format((SkBitmap::Config) -1), height(-1),
     rowBytes(0),    width(-1), fColor(0), fColorSet(false) {
 }
 
@@ -112,14 +112,14 @@
 }
 
 
-enum SkImage_Properties {
+enum SkImageBaseBitmap_Properties {
     SK_PROPERTY(height),
     SK_PROPERTY(width)
 };
 
 #if SK_USE_CONDENSED_INFO == 0
 
-const SkMemberInfo SkImage::fInfo[] = {
+const SkMemberInfo SkImageBaseBitmap::fInfo[] = {
     SK_MEMBER_INHERITED,
     SK_MEMBER(base64, Base64),
     SK_MEMBER_PROPERTY(height, Int),
@@ -129,35 +129,35 @@
 
 #endif
 
-DEFINE_GET_MEMBER(SkImage);
+DEFINE_GET_MEMBER(SkImageBaseBitmap);
 
-SkImage::SkImage() : fDirty(true), fUriBase(NULL) {
+SkImageBaseBitmap::SkImageBaseBitmap() : fDirty(true), fUriBase(NULL) {
     base64.fData = NULL;
     base64.fLength = 0;
 }
 
-SkImage::~SkImage() {
-    delete[] base64.fData; 
+SkImageBaseBitmap::~SkImageBaseBitmap() {
+    delete[] base64.fData;
 }
 
-SkDisplayable* SkImage::deepCopy(SkAnimateMaker* maker) {
+SkDisplayable* SkImageBaseBitmap::deepCopy(SkAnimateMaker* maker) {
     SkDisplayable* copy = INHERITED::deepCopy(maker);
-    ((SkImage*) copy)->fUriBase = ((SkImage*) this)->fUriBase;
+    ((SkImageBaseBitmap*) copy)->fUriBase = ((SkImageBaseBitmap*) this)->fUriBase;
     return copy;
 }
 
-void SkImage::dirty() {
+void SkImageBaseBitmap::dirty() {
     fDirty = true;
 }
 
-bool SkImage::draw(SkAnimateMaker& maker) {
-    if (fDirty) 
+bool SkImageBaseBitmap::draw(SkAnimateMaker& maker) {
+    if (fDirty)
         resolve();
     return INHERITED::draw(maker);
 }
 
-bool SkImage::getProperty(int index, SkScriptValue* value) const {
-    if (fDirty) 
+bool SkImageBaseBitmap::getProperty(int index, SkScriptValue* value) const {
+    if (fDirty)
         resolve();
     switch (index) {
         case SK_PROPERTY(height):
@@ -174,11 +174,11 @@
     return true;
 }
 
-void SkImage::onEndElement(SkAnimateMaker& maker) {
+void SkImageBaseBitmap::onEndElement(SkAnimateMaker& maker) {
     fUriBase = maker.fPrefix.c_str();
 }
 
-void SkImage::resolve() {
+void SkImageBaseBitmap::resolve() {
     fDirty = false;
     if (base64.fData) {
         fBitmap.reset();
@@ -188,11 +188,11 @@
             return;
         fLast.set(src);
         fBitmap.reset();
-        
+
         //SkStream* stream = SkStream::GetURIStream(fUriBase, src.c_str());
         SkStream* stream = new SkFILEStream(src.c_str());
 
         SkAutoTDelete<SkStream> autoDel(stream);
         SkImageDecoder::DecodeStream(stream, &fBitmap);
-    }   
+    }
 }
diff --git a/src/animator/SkDrawBitmap.h b/src/animator/SkDrawBitmap.h
index e6d6d44..f9a9212 100644
--- a/src/animator/SkDrawBitmap.h
+++ b/src/animator/SkDrawBitmap.h
@@ -50,22 +50,22 @@
     typedef SkBaseBitmap INHERITED;
 };
 
-class SkImage : public SkBaseBitmap {
-    DECLARE_MEMBER_INFO(Image);
-    SkImage();
-    virtual ~SkImage();
+class SkImageBaseBitmap : public SkBaseBitmap {
+    DECLARE_MEMBER_INFO(ImageBaseBitmap);
+    SkImageBaseBitmap();
+    virtual ~SkImageBaseBitmap();
     virtual SkDisplayable* deepCopy(SkAnimateMaker* );
     virtual void dirty();
     virtual bool draw(SkAnimateMaker& );
     virtual bool getProperty(int index, SkScriptValue* value) const;
     virtual void onEndElement(SkAnimateMaker& maker);
 private:
-    void resolve() const { (const_cast<SkImage*>(this))->resolve(); }
+    void resolve() const { (const_cast<SkImageBaseBitmap*>(this))->resolve(); }
     void resolve();
 protected:
     SkBase64 base64;
     SkString src;
-    SkString fLast; // cache of src so that stream isn't unnecessarily decoded 
+    SkString fLast; // cache of src so that stream isn't unnecessarily decoded
     SkBool fDirty;
     const char* fUriBase;
     typedef SkBaseBitmap INHERITED;
diff --git a/src/animator/SkDrawBlur.cpp b/src/animator/SkDrawBlur.cpp
index 57e8e20..d66fc56 100644
--- a/src/animator/SkDrawBlur.cpp
+++ b/src/animator/SkDrawBlur.cpp
@@ -20,7 +20,7 @@
 
 DEFINE_GET_MEMBER(SkDrawBlur);
 
-SkDrawBlur::SkDrawBlur() : radius(-1), 
+SkDrawBlur::SkDrawBlur() : radius(-1),
     blurStyle(SkBlurMaskFilter::kNormal_BlurStyle) {
 }
 
@@ -29,4 +29,3 @@
         return NULL;
     return SkBlurMaskFilter::Create(radius, (SkBlurMaskFilter::BlurStyle) blurStyle);
 }
-
diff --git a/src/animator/SkDrawBlur.h b/src/animator/SkDrawBlur.h
index 220e211..24cb4ae 100644
--- a/src/animator/SkDrawBlur.h
+++ b/src/animator/SkDrawBlur.h
@@ -23,4 +23,3 @@
 };
 
 #endif // SkDrawBlur_DEFINED
-
diff --git a/src/animator/SkDrawClip.cpp b/src/animator/SkDrawClip.cpp
index 521bbc5..e26a1bf 100644
--- a/src/animator/SkDrawClip.cpp
+++ b/src/animator/SkDrawClip.cpp
@@ -37,4 +37,3 @@
     }
     return false;
 }
-
diff --git a/src/animator/SkDrawColor.cpp b/src/animator/SkDrawColor.cpp
index 811a5d6..af0f04b 100644
--- a/src/animator/SkDrawColor.cpp
+++ b/src/animator/SkDrawColor.cpp
@@ -73,7 +73,7 @@
         SkScalar fraction = hue / 60 - SkIntToScalar(sextant);
         SkScalar p = SkScalarMul(value , SK_Scalar1 - saturation);
         SkScalar q = SkScalarMul(value, SK_Scalar1 - SkScalarMul(saturation, fraction));
-        SkScalar t = SkScalarMul(value, SK_Scalar1 - 
+        SkScalar t = SkScalarMul(value, SK_Scalar1 -
             SkScalarMul(saturation, SK_Scalar1 - fraction));
         switch (sextant % 6) {
             case 0: red = value; green = t; blue = p; break;
@@ -85,11 +85,11 @@
         }
     }
     //used to say SkToU8((U8CPU) red) etc
-    return SkColorSetARGB(SkColorGetA(color), SkScalarRound(red), 
+    return SkColorSetARGB(SkColorGetA(color), SkScalarRound(red),
         SkScalarRound(green), SkScalarRound(blue));
 }
 
-#if defined _WIN32 && _MSC_VER >= 1300  
+#if defined _WIN32 && _MSC_VER >= 1300
 #pragma warning ( pop )
 #endif
 
@@ -120,8 +120,8 @@
 
 DEFINE_GET_MEMBER(SkDrawColor);
 
-SkDrawColor::SkDrawColor() : fDirty(false) { 
-    color = SK_ColorBLACK; 
+SkDrawColor::SkDrawColor() : fDirty(false) {
+    color = SK_ColorBLACK;
     fHue = fSaturation = fValue = SK_ScalarNaN;
 }
 
@@ -150,13 +150,13 @@
 #ifdef SK_DUMP_ENABLED
 void SkDrawColor::dump(SkAnimateMaker* maker) {
     dumpBase(maker);
-    SkDebugf("alpha=\"%d\" red=\"%d\" green=\"%d\" blue=\"%d\" />\n",  
+    SkDebugf("alpha=\"%d\" red=\"%d\" green=\"%d\" blue=\"%d\" />\n",
         SkColorGetA(color)/255, SkColorGetR(color),
         SkColorGetG(color), SkColorGetB(color));
 }
 #endif
 
-SkColor SkDrawColor::getColor() { 
+SkColor SkDrawColor::getColor() {
     if (fDirty) {
         if (SkScalarIsNaN(fValue) == false)
             color = HSV_to_RGB(color, kGetValue, fValue);
@@ -166,7 +166,7 @@
             color = HSV_to_RGB(color, kGetHue, fHue);
         fDirty = false;
     }
-    return color; 
+    return color;
 }
 
 SkDisplayable* SkDrawColor::getParent() const {
@@ -212,7 +212,7 @@
 
 bool SkDrawColor::setParent(SkDisplayable* parent) {
     SkASSERT(parent != NULL);
-    if (parent->getType() == SkType_LinearGradient || parent->getType() == SkType_RadialGradient)
+    if (parent->getType() == SkType_DrawLinearGradient || parent->getType() == SkType_DrawRadialGradient)
         return false;
     if (parent->isPaint() == false)
         return true;
@@ -231,17 +231,17 @@
         #else
             alpha = SkToU8((scalar - (scalar >= SK_ScalarHalf)) >> 8);
         #endif
-            color = SkColorSetARGB(alpha, SkColorGetR(color), 
+            color = SkColorSetARGB(alpha, SkColorGetR(color),
                 SkColorGetG(color), SkColorGetB(color));
             break;
         case SK_PROPERTY(blue):
             scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
-            color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color), 
+            color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color),
                 SkColorGetG(color), SkToU8((U8CPU) scalar));
             break;
         case SK_PROPERTY(green):
             scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
-            color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color), 
+            color = SkColorSetARGB(SkColorGetA(color), SkColorGetR(color),
                 SkToU8((U8CPU) scalar), SkColorGetB(color));
             break;
         case SK_PROPERTY(hue):
@@ -250,7 +250,7 @@
             break;
         case SK_PROPERTY(red):
             scalar = SkScalarClampMax(scalar, 255 * SK_Scalar1);
-            color = SkColorSetARGB(SkColorGetA(color), SkToU8((U8CPU) scalar), 
+            color = SkColorSetARGB(SkColorGetA(color), SkToU8((U8CPU) scalar),
                 SkColorGetG(color), SkColorGetB(color));
         break;
         case SK_PROPERTY(saturation):
@@ -267,4 +267,3 @@
     }
     return true;
 }
-
diff --git a/src/animator/SkDrawColor.h b/src/animator/SkDrawColor.h
index 29c73c3..281af0f 100644
--- a/src/animator/SkDrawColor.h
+++ b/src/animator/SkDrawColor.h
@@ -35,7 +35,7 @@
     SkScalar fValue;
     SkBool fDirty;
 private:
-    friend class SkGradient;
+    friend class SkDrawGradient;
     typedef SkPaintPart INHERITED;
 };
 
diff --git a/src/animator/SkDrawDash.cpp b/src/animator/SkDrawDash.cpp
index 0d93293..8e73aa1 100644
--- a/src/animator/SkDrawDash.cpp
+++ b/src/animator/SkDrawDash.cpp
@@ -33,4 +33,3 @@
         return NULL;
     return new SkDashPathEffect(intervals.begin(), count, phase);
 }
-
diff --git a/src/animator/SkDrawDash.h b/src/animator/SkDrawDash.h
index 483d2a3..0000462 100644
--- a/src/animator/SkDrawDash.h
+++ b/src/animator/SkDrawDash.h
@@ -24,4 +24,3 @@
 };
 
 #endif // SkDrawDash_DEFINED
-
diff --git a/src/animator/SkDrawDiscrete.cpp b/src/animator/SkDrawDiscrete.cpp
index 655c83d..18c3ee0 100644
--- a/src/animator/SkDrawDiscrete.cpp
+++ b/src/animator/SkDrawDiscrete.cpp
@@ -32,4 +32,3 @@
     else
         return new SkDiscretePathEffect(segLength, deviation);
 }
-
diff --git a/src/animator/SkDrawEmboss.cpp b/src/animator/SkDrawEmboss.cpp
index 2284504..5eed370 100644
--- a/src/animator/SkDrawEmboss.cpp
+++ b/src/animator/SkDrawEmboss.cpp
@@ -22,7 +22,7 @@
 
 DEFINE_GET_MEMBER(SkDrawEmboss);
 
-SkDrawEmboss::SkDrawEmboss() : radius(-1) { 
+SkDrawEmboss::SkDrawEmboss() : radius(-1) {
     direction.setCount(3);
 }
 
@@ -31,4 +31,3 @@
         return NULL;
     return SkBlurMaskFilter::CreateEmboss(direction.begin(), ambient, specular, radius);
 }
-
diff --git a/src/animator/SkDrawEmboss.h b/src/animator/SkDrawEmboss.h
index 50ce71a..6e61997 100644
--- a/src/animator/SkDrawEmboss.h
+++ b/src/animator/SkDrawEmboss.h
@@ -22,4 +22,3 @@
 };
 
 #endif // SkDrawEmboss_DEFINED
-
diff --git a/src/animator/SkDrawExtraPathEffect.cpp b/src/animator/SkDrawExtraPathEffect.cpp
index 12e0368..8cfde25 100644
--- a/src/animator/SkDrawExtraPathEffect.cpp
+++ b/src/animator/SkDrawExtraPathEffect.cpp
@@ -22,7 +22,7 @@
     DECLARE_PRIVATE_MEMBER_INFO(DrawShapePathEffect);
     SkDrawShapePathEffect();
     virtual ~SkDrawShapePathEffect();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* );
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* ) SK_OVERRIDE;
     virtual SkPathEffect* getPathEffect();
 protected:
     SkDrawable* addPath;
@@ -60,7 +60,7 @@
     DECLARE_EXTRAS_MEMBER_INFO(SkDrawComposePathEffect);
     SkDrawComposePathEffect(SkDisplayTypes );
     virtual ~SkDrawComposePathEffect();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* );
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* ) SK_OVERRIDE;
     virtual SkPathEffect* getPathEffect();
     virtual bool isPaint() const;
 private:
@@ -91,9 +91,10 @@
         fDraw(draw), fMaker(maker) {
     }
 
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
 protected:
-    virtual SkScalar begin(SkScalar contourLength)
-    {
+    virtual SkScalar begin(SkScalar contourLength) const {
         SkScriptValue value;
         SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
         engine.propertyCallBack(GetContourLength, &contourLength);
@@ -102,8 +103,7 @@
         return value.fOperand.fScalar;
     }
 
-    virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure& )
-    {
+    virtual SkScalar next(SkPath* dst, SkScalar distance, SkPathMeasure&) const {
         fMaker->setExtraPropertyCallBack(fDraw->fType, GetDistance, &distance);
         SkDrawPath* drawPath = NULL;
         if (fDraw->addPath->isPath()) {
@@ -138,9 +138,6 @@
     }
 
 private:
-    virtual void flatten(SkFlattenableWriteBuffer& ) {}
-    virtual Factory getFactory() { return NULL; }
-
     static bool GetContourLength(const char* token, size_t len, void* clen, SkScriptValue* value) {
         if (SK_LITERAL_STR_EQUAL("contourLength", token, len)) {
             value->fOperand.fScalar = *(SkScalar*) clen;
@@ -185,7 +182,7 @@
     SkSafeUnref(fPathEffect);
 }
 
-bool SkDrawShapePathEffect::add(SkAnimateMaker& , SkDisplayable* child) {
+bool SkDrawShapePathEffect::addChild(SkAnimateMaker& , SkDisplayable* child) {
     path = (SkDrawPath*) child;
     return true;
 }
@@ -355,7 +352,7 @@
     delete effect2;
 }
 
-bool SkDrawComposePathEffect::add(SkAnimateMaker& , SkDisplayable* child) {
+bool SkDrawComposePathEffect::addChild(SkAnimateMaker& , SkDisplayable* child) {
     if (effect1 == NULL)
         effect1 = (SkDrawPathEffect*) child;
     else
diff --git a/src/animator/SkDrawFull.cpp b/src/animator/SkDrawFull.cpp
index 762b3da..a1a5fc9 100644
--- a/src/animator/SkDrawFull.cpp
+++ b/src/animator/SkDrawFull.cpp
@@ -16,4 +16,3 @@
     maker.fCanvas->drawPaint(*maker.fPaint);
     return false;
 }
-
diff --git a/src/animator/SkDrawGradient.cpp b/src/animator/SkDrawGradient.cpp
index 37fc7e8..44086e4 100644
--- a/src/animator/SkDrawGradient.cpp
+++ b/src/animator/SkDrawGradient.cpp
@@ -13,7 +13,7 @@
 #include "SkGradientShader.h"
 #include "SkUnitMapper.h"
 
-SkScalar SkUnitToScalar(U16CPU x) {
+static SkScalar SkUnitToScalar(U16CPU x) {
 #ifdef SK_SCALAR_IS_FLOAT
     return x / 65535.0f;
 #else
@@ -21,7 +21,7 @@
 #endif
 }
 
-U16CPU SkScalarToUnit(SkScalar x) {
+static U16CPU SkScalarToUnit(SkScalar x) {
     SkScalar pin =  SkScalarPin(x, 0, SK_Scalar1);
 #ifdef SK_SCALAR_IS_FLOAT
     return (int) (pin * 65535.0f);
@@ -30,21 +30,20 @@
 #endif
 }
 
-class SkGradientUnitMapper : public SkUnitMapper {
+class SkDrawGradientUnitMapper : public SkUnitMapper {
 public:
-    SkGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) {
+    SkDrawGradientUnitMapper(SkAnimateMaker* maker, const char* script) : fMaker(maker), fScript(script) {
     }
-    
-    // overrides for SkFlattenable
-    virtual Factory getFactory() { return NULL; }
-    
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
+
 protected:
     virtual uint16_t mapUnit16(uint16_t x) {
         fUnit = SkUnitToScalar(x);
         SkScriptValue value;
         SkAnimatorScript engine(*fMaker, NULL, SkType_Float);
         engine.propertyCallBack(GetUnitValue, &fUnit);
-        if (engine.evaluate(fScript, &value, SkType_Float)) 
+        if (engine.evaluate(fScript, &value, SkType_Float))
             x = SkScalarToUnit(value.fOperand.fScalar);
         return x;
     }
@@ -66,7 +65,7 @@
 
 #if SK_USE_CONDENSED_INFO == 0
 
-const SkMemberInfo SkGradient::fInfo[] = {
+const SkMemberInfo SkDrawGradient::fInfo[] = {
     SK_MEMBER_INHERITED,
     SK_MEMBER_ARRAY(offsets, Float),
     SK_MEMBER(unitMapper, String)
@@ -74,18 +73,18 @@
 
 #endif
 
-DEFINE_GET_MEMBER(SkGradient);
+DEFINE_GET_MEMBER(SkDrawGradient);
 
-SkGradient::SkGradient() : fUnitMapper(NULL) {
+SkDrawGradient::SkDrawGradient() : fUnitMapper(NULL) {
 }
 
-SkGradient::~SkGradient() {
-    for (int index = 0; index < fDrawColors.count(); index++) 
+SkDrawGradient::~SkDrawGradient() {
+    for (int index = 0; index < fDrawColors.count(); index++)
         delete fDrawColors[index];
     delete fUnitMapper;
 }
 
-bool SkGradient::add(SkAnimateMaker& , SkDisplayable* child) {
+bool SkDrawGradient::addChild(SkAnimateMaker& , SkDisplayable* child) {
     SkASSERT(child);
     if (child->isColor()) {
         SkDrawColor* color = (SkDrawColor*) child;
@@ -95,16 +94,16 @@
     return false;
 }
 
-int SkGradient::addPrelude() {
+int SkDrawGradient::addPrelude() {
     int count = fDrawColors.count();
     fColors.setCount(count);
-    for (int index = 0; index < count; index++) 
+    for (int index = 0; index < count; index++)
         fColors[index] = fDrawColors[index]->color;
     return count;
 }
 
 #ifdef SK_DUMP_ENABLED
-void SkGradient::dumpRest(SkAnimateMaker* maker) {
+void SkDrawGradient::dumpRest(SkAnimateMaker* maker) {
     dumpAttrs(maker);
     //can a gradient have no colors?
     bool closedYet = false;
@@ -117,12 +116,12 @@
         SkDrawColor* color = *ptr;
         color->dump(maker);
     }
-    SkDisplayList::fIndent -= 4;    
+    SkDisplayList::fIndent -= 4;
     dumpChildren(maker, closedYet); //dumps the matrix if it has one
 }
 #endif
 
-void SkGradient::onEndElement(SkAnimateMaker& maker) {
+void SkDrawGradient::onEndElement(SkAnimateMaker& maker) {
     if (offsets.count() != 0) {
         if (offsets.count() != fDrawColors.count()) {
             maker.setErrorCode(SkDisplayXMLParserError::kGradientOffsetsDontMatchColors);
@@ -147,26 +146,26 @@
             }
         }
     }
-    if (unitMapper.size() > 0) 
-        fUnitMapper = new SkGradientUnitMapper(&maker, unitMapper.c_str());
+    if (unitMapper.size() > 0)
+        fUnitMapper = new SkDrawGradientUnitMapper(&maker, unitMapper.c_str());
     INHERITED::onEndElement(maker);
 }
 
 #if SK_USE_CONDENSED_INFO == 0
 
-const SkMemberInfo SkLinearGradient::fInfo[] = {
+const SkMemberInfo SkDrawLinearGradient::fInfo[] = {
     SK_MEMBER_INHERITED,
     SK_MEMBER_ARRAY(points, Float),
 };
 
 #endif
 
-DEFINE_GET_MEMBER(SkLinearGradient);
+DEFINE_GET_MEMBER(SkDrawLinearGradient);
 
-SkLinearGradient::SkLinearGradient() { 
+SkDrawLinearGradient::SkDrawLinearGradient() {
 }
 
-void SkLinearGradient::onEndElement(SkAnimateMaker& maker)
+void SkDrawLinearGradient::onEndElement(SkAnimateMaker& maker)
 {
     if (points.count() != 4)
         maker.setErrorCode(SkDisplayXMLParserError::kGradientPointsLengthMustBeFour);
@@ -174,13 +173,13 @@
 }
 
 #ifdef SK_DUMP_ENABLED
-void SkLinearGradient::dump(SkAnimateMaker* maker) {
+void SkDrawLinearGradient::dump(SkAnimateMaker* maker) {
     dumpBase(maker);
     dumpRest(maker);
     }
 #endif
 
-SkShader* SkLinearGradient::getShader() {
+SkShader* SkDrawLinearGradient::getShader() {
     if (addPrelude() == 0 || points.count() != 4)
         return NULL;
     SkShader* shader = SkGradientShader::CreateLinear((SkPoint*)points.begin(),
@@ -194,7 +193,7 @@
 
 #if SK_USE_CONDENSED_INFO == 0
 
-const SkMemberInfo SkRadialGradient::fInfo[] = {
+const SkMemberInfo SkDrawRadialGradient::fInfo[] = {
     SK_MEMBER_INHERITED,
     SK_MEMBER(center, Point),
     SK_MEMBER(radius, Float)
@@ -202,20 +201,20 @@
 
 #endif
 
-DEFINE_GET_MEMBER(SkRadialGradient);
+DEFINE_GET_MEMBER(SkDrawRadialGradient);
 
-SkRadialGradient::SkRadialGradient() : radius(0) { 
-    center.set(0, 0); 
+SkDrawRadialGradient::SkDrawRadialGradient() : radius(0) {
+    center.set(0, 0);
 }
 
 #ifdef SK_DUMP_ENABLED
-void SkRadialGradient::dump(SkAnimateMaker* maker) {
+void SkDrawRadialGradient::dump(SkAnimateMaker* maker) {
     dumpBase(maker);
     dumpRest(maker);
 }
 #endif
 
-SkShader* SkRadialGradient::getShader() {
+SkShader* SkDrawRadialGradient::getShader() {
     if (addPrelude() == 0)
         return NULL;
     SkShader* shader = SkGradientShader::CreateRadial(center,
diff --git a/src/animator/SkDrawGradient.h b/src/animator/SkDrawGradient.h
index 251e975..ff79e3f 100644
--- a/src/animator/SkDrawGradient.h
+++ b/src/animator/SkDrawGradient.h
@@ -16,14 +16,14 @@
 
 class SkUnitMapper;
 
-class SkGradient : public SkDrawShader {
-    DECLARE_PRIVATE_MEMBER_INFO(Gradient);
-    SkGradient();
-    virtual ~SkGradient();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+class SkDrawGradient : public SkDrawShader {
+    DECLARE_PRIVATE_MEMBER_INFO(DrawGradient);
+    SkDrawGradient();
+    virtual ~SkDrawGradient();
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
 #ifdef SK_DUMP_ENABLED
     virtual void dumpRest(SkAnimateMaker*);
-#endif    
+#endif
     virtual void onEndElement(SkAnimateMaker& );
 protected:
     SkTDScalarArray offsets;
@@ -36,9 +36,9 @@
     typedef SkDrawShader INHERITED;
 };
 
-class SkLinearGradient : public SkGradient {
-    DECLARE_MEMBER_INFO(LinearGradient);
-    SkLinearGradient();
+class SkDrawLinearGradient : public SkDrawGradient {
+    DECLARE_MEMBER_INFO(DrawLinearGradient);
+    SkDrawLinearGradient();
     virtual void onEndElement(SkAnimateMaker& );
 #ifdef SK_DUMP_ENABLED
     virtual void dump(SkAnimateMaker*);
@@ -47,22 +47,21 @@
 protected:
     SkTDScalarArray points;
 private:
-    typedef SkGradient INHERITED;
+    typedef SkDrawGradient INHERITED;
 };
 
-class SkRadialGradient : public SkGradient {
-    DECLARE_MEMBER_INFO(RadialGradient);
-    SkRadialGradient();
+class SkDrawRadialGradient : public SkDrawGradient {
+    DECLARE_MEMBER_INFO(DrawRadialGradient);
+    SkDrawRadialGradient();
 #ifdef SK_DUMP_ENABLED
     virtual void dump(SkAnimateMaker*);
-#endif    
+#endif
     virtual SkShader* getShader();
 protected:
     SkPoint center;
     SkScalar radius;
 private:
-    typedef SkGradient INHERITED;
+    typedef SkDrawGradient INHERITED;
 };
 
 #endif // SkDrawGradient_DEFINED
-
diff --git a/src/animator/SkDrawGroup.cpp b/src/animator/SkDrawGroup.cpp
index 939bd9f..c569434 100644
--- a/src/animator/SkDrawGroup.cpp
+++ b/src/animator/SkDrawGroup.cpp
@@ -49,8 +49,8 @@
     }
 }
 
-bool SkGroup::add(SkAnimateMaker& , SkDisplayable* child) {
-    SkASSERT(child); 
+bool SkGroup::addChild(SkAnimateMaker& , SkDisplayable* child) {
+    SkASSERT(child);
 //  SkASSERT(child->isDrawable());
     *fChildren.append() = (SkDrawable*) child;
     if (child->isGroup()) {
@@ -86,7 +86,7 @@
     for (SkDrawable** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
         SkDisplayable* displayable = (SkDisplayable*)*ptr;
         SkDisplayable* deeperCopy = displayable->deepCopy(maker);
-        ((SkGroup*)copy)->add(*maker, deeperCopy);
+        ((SkGroup*)copy)->addChild(*maker, deeperCopy);
     }
     return copy;
 }
@@ -319,5 +319,3 @@
     maker.fCanvas->restore();
     return result;
 }
-
-
diff --git a/src/animator/SkDrawGroup.h b/src/animator/SkDrawGroup.h
index 8124650..336040c 100644
--- a/src/animator/SkDrawGroup.h
+++ b/src/animator/SkDrawGroup.h
@@ -19,7 +19,7 @@
     DECLARE_MEMBER_INFO(Group);
     SkGroup();
     virtual ~SkGroup();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
     virtual bool contains(SkDisplayable* );
     SkGroup* copy();
     SkBool copySet(int index);
diff --git a/src/animator/SkDrawLine.cpp b/src/animator/SkDrawLine.cpp
index fe93c4e..d0ae7d9 100644
--- a/src/animator/SkDrawLine.cpp
+++ b/src/animator/SkDrawLine.cpp
@@ -25,7 +25,7 @@
 
 DEFINE_GET_MEMBER(SkLine);
 
-SkLine::SkLine() : x1(0), x2(0), y1(0), y2(0) { 
+SkLine::SkLine() : x1(0), x2(0), y1(0), y2(0) {
 }
 
 bool SkLine::draw(SkAnimateMaker& maker) {
diff --git a/src/animator/SkDrawLine.h b/src/animator/SkDrawLine.h
index e90c997..b287802 100644
--- a/src/animator/SkDrawLine.h
+++ b/src/animator/SkDrawLine.h
@@ -26,4 +26,3 @@
 };
 
 #endif // SkDrawLine_DEFINED
-
diff --git a/src/animator/SkDrawMatrix.cpp b/src/animator/SkDrawMatrix.cpp
index 96e8292..80b04c1 100644
--- a/src/animator/SkDrawMatrix.cpp
+++ b/src/animator/SkDrawMatrix.cpp
@@ -51,9 +51,9 @@
 
 DEFINE_GET_MEMBER(SkDrawMatrix);
 
-SkDrawMatrix::SkDrawMatrix() : fChildHasID(false), fDirty(false) { 
+SkDrawMatrix::SkDrawMatrix() : fChildHasID(false), fDirty(false) {
     fConcat.reset();
-    fMatrix.reset(); 
+    fMatrix.reset();
 }
 
 SkDrawMatrix::~SkDrawMatrix() {
@@ -61,16 +61,16 @@
         delete *part;
 }
 
-bool SkDrawMatrix::add(SkAnimateMaker& maker, SkDisplayable* child) {
+bool SkDrawMatrix::addChild(SkAnimateMaker& maker, SkDisplayable* child) {
     SkASSERT(child && child->isMatrixPart());
     SkMatrixPart* part = (SkMatrixPart*) child;
     *fParts.append() = part;
     if (part->add())
-        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToMatrix); 
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToMatrix);
     return true;
 }
 
-bool SkDrawMatrix::childrenNeedDisposing() const { 
+bool SkDrawMatrix::childrenNeedDisposing() const {
     return false;
 }
 
@@ -83,8 +83,8 @@
     return copy;
 }
 
-void SkDrawMatrix::dirty() { 
-    fDirty = true; 
+void SkDrawMatrix::dirty() {
+    fDirty = true;
 }
 
 bool SkDrawMatrix::draw(SkAnimateMaker& maker) {
@@ -207,8 +207,8 @@
     }
 }
 
-void SkDrawMatrix::setChildHasID() { 
-    fChildHasID = true; 
+void SkDrawMatrix::setChildHasID() {
+    fChildHasID = true;
 }
 
 bool SkDrawMatrix::setProperty(int index, SkScriptValue& scriptValue) {
@@ -266,4 +266,3 @@
     fConcat = fMatrix;
     return true;
 }
-
diff --git a/src/animator/SkDrawMatrix.h b/src/animator/SkDrawMatrix.h
index e303424..e3c389a 100644
--- a/src/animator/SkDrawMatrix.h
+++ b/src/animator/SkDrawMatrix.h
@@ -21,7 +21,7 @@
     DECLARE_DRAW_MEMBER_INFO(Matrix);
     SkDrawMatrix();
     virtual ~SkDrawMatrix();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
     virtual bool childrenNeedDisposing() const;
     virtual void dirty();
     virtual bool draw(SkAnimateMaker& );
diff --git a/src/animator/SkDrawOval.cpp b/src/animator/SkDrawOval.cpp
index ab0fc81..e5efa7d 100644
--- a/src/animator/SkDrawOval.cpp
+++ b/src/animator/SkDrawOval.cpp
@@ -26,4 +26,3 @@
     maker.fCanvas->drawOval(fRect, *maker.fPaint);
     return false;
 }
-
diff --git a/src/animator/SkDrawOval.h b/src/animator/SkDrawOval.h
index afdc252..3c09e0f 100644
--- a/src/animator/SkDrawOval.h
+++ b/src/animator/SkDrawOval.h
@@ -20,4 +20,3 @@
 };
 
 #endif // SkDrawOval_DEFINED
-
diff --git a/src/animator/SkDrawPaint.h b/src/animator/SkDrawPaint.h
index a2a893e..3caf6b6 100644
--- a/src/animator/SkDrawPaint.h
+++ b/src/animator/SkDrawPaint.h
@@ -32,7 +32,7 @@
 #ifdef SK_DUMP_ENABLED
     virtual void dump(SkAnimateMaker* );
 #endif
-    virtual void executeFunction(SkDisplayable* target, int index, 
+    virtual void executeFunction(SkDisplayable* target, int index,
         SkTDArray<SkScriptValue>& parameters, SkDisplayTypes type,
         SkScriptValue* );
     virtual const SkFunctionParamType* getFunctionsParameters();
@@ -77,4 +77,3 @@
 };
 
 #endif // SkDrawPaint_DEFINED
-
diff --git a/src/animator/SkDrawPath.cpp b/src/animator/SkDrawPath.cpp
index 10e9203..f62f7c0 100644
--- a/src/animator/SkDrawPath.cpp
+++ b/src/animator/SkDrawPath.cpp
@@ -45,22 +45,22 @@
         delete *part;
 }
 
-bool SkDrawPath::add(SkAnimateMaker& maker, SkDisplayable* child) {
+bool SkDrawPath::addChild(SkAnimateMaker& maker, SkDisplayable* child) {
     SkASSERT(child && child->isPathPart());
     SkPathPart* part = (SkPathPart*) child;
     *fParts.append() = part;
     if (part->add())
-        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPath); 
+        maker.setErrorCode(SkDisplayXMLParserError::kErrorAddingToPath);
     fDirty = false;
     return true;
 }
 
-bool SkDrawPath::childrenNeedDisposing() const { 
-    return false; 
+bool SkDrawPath::childrenNeedDisposing() const {
+    return false;
 }
 
-void SkDrawPath::dirty() { 
-    fDirty = true; 
+void SkDrawPath::dirty() {
+    fDirty = true;
     fLength = SK_ScalarNaN;
     if (fParent)
         fParent->dirty();
@@ -115,7 +115,7 @@
     fDirty = false;
     return fPath;
 }
-    
+
 void SkDrawPath::onEndElement(SkAnimateMaker& ) {
     if (d.size() > 0) {
         parseSVG();
@@ -153,8 +153,8 @@
     return true;
 }
 
-void SkDrawPath::setChildHasID() { 
-    fChildHasID = true; 
+void SkDrawPath::setChildHasID() {
+    fChildHasID = true;
 }
 
 bool SkDrawPath::setParent(SkDisplayable* parent) {
@@ -188,8 +188,8 @@
 
 DEFINE_GET_MEMBER(SkPolyline);
 
-bool SkPolyline::add(SkAnimateMaker& , SkDisplayable*) const {
-    return false; 
+bool SkPolyline::addChild(SkAnimateMaker& , SkDisplayable*) {
+    return false;
 }
 
 void SkPolyline::onEndElement(SkAnimateMaker& maker) {
@@ -218,4 +218,3 @@
     INHERITED::onEndElement(maker);
     fPath.close();
 }
-
diff --git a/src/animator/SkDrawPath.h b/src/animator/SkDrawPath.h
index 5c26312..9c4d305 100644
--- a/src/animator/SkDrawPath.h
+++ b/src/animator/SkDrawPath.h
@@ -19,7 +19,7 @@
     DECLARE_DRAW_MEMBER_INFO(Path);
     SkDrawPath();
     virtual ~SkDrawPath();
-    virtual bool add(SkAnimateMaker& , SkDisplayable* child);
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable* child) SK_OVERRIDE;
     bool childHasID() { return SkToBool(fChildHasID); }
     virtual bool childrenNeedDisposing() const;
     virtual void dirty();
@@ -51,7 +51,7 @@
 
 class SkPolyline : public SkDrawPath {
     DECLARE_MEMBER_INFO(Polyline);
-    virtual bool add(SkAnimateMaker& , SkDisplayable*) const;
+    virtual bool addChild(SkAnimateMaker& , SkDisplayable*) SK_OVERRIDE;
     virtual void onEndElement(SkAnimateMaker& );
 protected:
     SkTDScalarArray points;
diff --git a/src/animator/SkDrawPoint.cpp b/src/animator/SkDrawPoint.cpp
index 94fe4d2..41a6be4 100644
--- a/src/animator/SkDrawPoint.cpp
+++ b/src/animator/SkDrawPoint.cpp
@@ -34,13 +34,11 @@
 
 DEFINE_GET_MEMBER(SkDrawPoint);
 
-SkDrawPoint::SkDrawPoint() { 
-    fPoint.set(0, 0);   
+SkDrawPoint::SkDrawPoint() {
+    fPoint.set(0, 0);
 }
 
 void SkDrawPoint::getBounds(SkRect* rect ) {
     rect->fLeft = rect->fRight = fPoint.fX;
     rect->fTop = rect->fBottom = fPoint.fY;
 }
-
-
diff --git a/src/animator/SkDrawRectangle.cpp b/src/animator/SkDrawRectangle.cpp
index 19edf50..c3fb7ff 100644
--- a/src/animator/SkDrawRectangle.cpp
+++ b/src/animator/SkDrawRectangle.cpp
@@ -36,8 +36,8 @@
 
 DEFINE_GET_MEMBER(SkDrawRect);
 
-SkDrawRect::SkDrawRect() : fParent(NULL) { 
-    fRect.setEmpty(); 
+SkDrawRect::SkDrawRect() : fParent(NULL) {
+    fRect.setEmpty();
 }
 
 void SkDrawRect::dirty() {
@@ -140,6 +140,3 @@
             SkScalarToFloat(fRect.fBottom), SkScalarToFloat(rx), SkScalarToFloat(ry));
 }
 #endif
-
-
-
diff --git a/src/animator/SkDrawRectangle.h b/src/animator/SkDrawRectangle.h
index 49c9cf4..42af02b 100644
--- a/src/animator/SkDrawRectangle.h
+++ b/src/animator/SkDrawRectangle.h
@@ -44,7 +44,7 @@
     virtual bool draw(SkAnimateMaker& );
 #ifdef SK_DUMP_ENABLED
     virtual void dump(SkAnimateMaker* );
-#endif    
+#endif
 protected:
     SkScalar rx;
     SkScalar ry;
@@ -53,4 +53,3 @@
 };
 
 #endif // SkDrawRectangle_DEFINED
-
diff --git a/src/animator/SkDrawSaveLayer.cpp b/src/animator/SkDrawSaveLayer.cpp
index a7592db..4e97a04 100644
--- a/src/animator/SkDrawSaveLayer.cpp
+++ b/src/animator/SkDrawSaveLayer.cpp
@@ -35,7 +35,7 @@
     if (!bounds) {
         return false;
     }
-    SkPaint* save = maker.fPaint;   
+    SkPaint* save = maker.fPaint;
     //paint is an SkDrawPaint
     if (paint)
     {
@@ -74,5 +74,3 @@
         maker.setErrorCode(SkDisplayXMLParserError::kSaveLayerNeedsBounds);
     INHERITED::onEndElement(maker);
 }
-
-
diff --git a/src/animator/SkDrawShader.cpp b/src/animator/SkDrawShader.cpp
index 5019622..e3aa4da 100644
--- a/src/animator/SkDrawShader.cpp
+++ b/src/animator/SkDrawShader.cpp
@@ -24,7 +24,7 @@
 
 DEFINE_GET_MEMBER(SkDrawShader);
 
-SkDrawShader::SkDrawShader() : matrix(NULL), 
+SkDrawShader::SkDrawShader() : matrix(NULL),
     tileMode(SkShader::kClamp_TileMode) {
 }
 
@@ -66,14 +66,14 @@
 SkShader* SkDrawBitmapShader::getShader() {
     if (image == NULL)
         return NULL;
-    
+
     // note: bitmap shader now supports independent tile modes for X and Y
     // we pass the same to both, but later we should extend this flexibility
     // to the xml (e.g. tileModeX="repeat" tileModeY="clmap")
-    // 
+    //
     // oops, bitmapshader no longer takes filterBitmap, but deduces it at
-    // draw-time from the paint 
-    SkShader* shader  = SkShader::CreateBitmapShader(image->fBitmap, 
+    // draw-time from the paint
+    SkShader* shader  = SkShader::CreateBitmapShader(image->fBitmap,
                                                     (SkShader::TileMode) tileMode,
                                                     (SkShader::TileMode) tileMode);
     SkAutoTDelete<SkShader> autoDel(shader);
diff --git a/src/animator/SkDrawText.cpp b/src/animator/SkDrawText.cpp
index b8d38b4..e7e5fac 100644
--- a/src/animator/SkDrawText.cpp
+++ b/src/animator/SkDrawText.cpp
@@ -53,4 +53,3 @@
     value->fOperand.fS32 = (int32_t) text.size();
     return true;
 }
-
diff --git a/src/animator/SkDrawText.h b/src/animator/SkDrawText.h
index 7dd2e26..3ac2479 100644
--- a/src/animator/SkDrawText.h
+++ b/src/animator/SkDrawText.h
@@ -21,7 +21,7 @@
 #ifdef SK_DUMP_ENABLED
     virtual void dump(SkAnimateMaker* );
 #endif
-    virtual bool getProperty(int index, SkScriptValue* value) const ; 
+    virtual bool getProperty(int index, SkScriptValue* value) const ;
     const char* getText() { return text.c_str(); }
     size_t getSize() { return text.size(); }
 protected:
diff --git a/src/animator/SkDrawTextBox.cpp b/src/animator/SkDrawTextBox.cpp
index d97dc5c..7a3251a 100644
--- a/src/animator/SkDrawTextBox.cpp
+++ b/src/animator/SkDrawTextBox.cpp
@@ -47,7 +47,7 @@
 {
     dumpBase(maker);
     dumpAttrs(maker);
-    if (mode == 0) 
+    if (mode == 0)
         SkDebugf("mode=\"oneLine\" ");
     if (spacingAlign == 1)
         SkDebugf("spacingAlign=\"center\" ");
@@ -78,5 +78,3 @@
     box.draw(maker.fCanvas, fText.c_str(), fText.size(), *maker.fPaint);
     return false;
 }
-
-
diff --git a/src/animator/SkDrawTextBox.h b/src/animator/SkDrawTextBox.h
index d8d7f0c..6155bef 100644
--- a/src/animator/SkDrawTextBox.h
+++ b/src/animator/SkDrawTextBox.h
@@ -36,4 +36,3 @@
 };
 
 #endif // SkDrawTextBox_DEFINED
-
diff --git a/src/animator/SkDrawTo.cpp b/src/animator/SkDrawTo.cpp
index bc5cd6d..ef084d1 100644
--- a/src/animator/SkDrawTo.cpp
+++ b/src/animator/SkDrawTo.cpp
@@ -53,4 +53,3 @@
     dumpDrawables(maker);
 }
 #endif
-
diff --git a/src/animator/SkDrawTransparentShader.cpp b/src/animator/SkDrawTransparentShader.cpp
index bb5392a..2f286f4 100644
--- a/src/animator/SkDrawTransparentShader.cpp
+++ b/src/animator/SkDrawTransparentShader.cpp
@@ -13,4 +13,3 @@
 SkShader* SkDrawTransparentShader::getShader() {
     return new SkTransparentShader();
 }
-
diff --git a/src/animator/SkDrawTransparentShader.h b/src/animator/SkDrawTransparentShader.h
index df1c6eb..bf66174 100644
--- a/src/animator/SkDrawTransparentShader.h
+++ b/src/animator/SkDrawTransparentShader.h
@@ -18,4 +18,3 @@
 };
 
 #endif // SkDrawTransparentShader_DEFINED
-
diff --git a/src/animator/SkDrawable.cpp b/src/animator/SkDrawable.cpp
index 8db0333..610c397 100644
--- a/src/animator/SkDrawable.cpp
+++ b/src/animator/SkDrawable.cpp
@@ -13,8 +13,8 @@
     return false;
 }
 
-bool SkDrawable::isDrawable() const { 
-    return true; 
+bool SkDrawable::isDrawable() const {
+    return true;
 }
 
 void SkDrawable::initialize() {
@@ -22,4 +22,3 @@
 
 void SkDrawable::setSteps(int steps) {
 }
-
diff --git a/src/animator/SkDrawable.h b/src/animator/SkDrawable.h
index 139ce85..6bb9608 100644
--- a/src/animator/SkDrawable.h
+++ b/src/animator/SkDrawable.h
@@ -19,7 +19,7 @@
 class SkDrawable :  public SkDisplayable {
 public:
     virtual bool doEvent(SkDisplayEvent::Kind , SkEventState* state );
-    virtual bool draw(SkAnimateMaker& ) = 0; 
+    virtual bool draw(SkAnimateMaker& ) = 0;
     virtual void initialize();
     virtual bool isDrawable() const;
     virtual void setSteps(int steps);
diff --git a/src/animator/SkDump.cpp b/src/animator/SkDump.cpp
index bd91277..563b0e1 100644
--- a/src/animator/SkDump.cpp
+++ b/src/animator/SkDump.cpp
@@ -144,7 +144,7 @@
 }
 
 bool SkDump::setProperty(int index, SkScriptValue& ) {
-    return index <= SK_PROPERTY(posts); 
+    return index <= SK_PROPERTY(posts);
 }
 
 #endif
diff --git a/src/animator/SkDump.h b/src/animator/SkDump.h
index 3222f06..0a31b1c 100644
--- a/src/animator/SkDump.h
+++ b/src/animator/SkDump.h
@@ -40,4 +40,3 @@
 
 
 #endif // SkDump_DEFINED
-
diff --git a/src/animator/SkGetCondensedInfo.cpp b/src/animator/SkGetCondensedInfo.cpp
index a583cef..f2471bb 100644
--- a/src/animator/SkGetCondensedInfo.cpp
+++ b/src/animator/SkGetCondensedInfo.cpp
@@ -26,7 +26,7 @@
         int mid = (hi + lo) >> 1;
         if (strcmp(&strings[lengths[mid << 2]], target) < 0)
             lo = mid + 1;
-        else 
+        else
             hi = mid;
     }
     if (strcmp(&strings[lengths[hi << 2]], target) != 0)
@@ -119,4 +119,3 @@
 }
 
 #endif
-
diff --git a/src/animator/SkHitClear.cpp b/src/animator/SkHitClear.cpp
index 70b3e73..96b1747 100644
--- a/src/animator/SkHitClear.cpp
+++ b/src/animator/SkHitClear.cpp
@@ -30,4 +30,3 @@
 bool SkHitClear::hasEnable() const {
     return true;
 }
-
diff --git a/src/animator/SkHitTest.cpp b/src/animator/SkHitTest.cpp
index dc4e739..7506edc 100644
--- a/src/animator/SkHitTest.cpp
+++ b/src/animator/SkHitTest.cpp
@@ -72,4 +72,3 @@
         return getMember("bullets");
     return getMember("targets"); // !!! cwap! need to refer to member through enum like kScope instead
 }
-
diff --git a/src/animator/SkIntArray.h b/src/animator/SkIntArray.h
index 6769a9e..401e51b 100644
--- a/src/animator/SkIntArray.h
+++ b/src/animator/SkIntArray.h
@@ -36,23 +36,20 @@
 typedef SkIntArray(SkMSec) SkTDMSecArray;
 typedef SkIntArray(SkScalar) SkTDScalarArray;
 
-typedef SkLongArray(SkActive*) SkTDActiveArray; 
-typedef SkLongArray(SkAnimateBase*) SkTDAnimateArray; 
-typedef SkLongArray(SkDataInput*) SkTDDataArray; 
-typedef SkLongArray(SkDisplayable*) SkTDDisplayableArray; 
-typedef SkLongArray(SkDisplayEvent*) SkTDDisplayEventArray; 
-typedef SkLongArray(SkDrawable*) SkTDDrawableArray; 
-typedef SkLongArray(SkDrawColor*) SkTDDrawColorArray; 
-typedef SkLongArray(SkMatrixPart*) SkTDMatrixPartArray; 
+typedef SkLongArray(SkActive*) SkTDActiveArray;
+typedef SkLongArray(SkAnimateBase*) SkTDAnimateArray;
+typedef SkLongArray(SkDataInput*) SkTDDataArray;
+typedef SkLongArray(SkDisplayable*) SkTDDisplayableArray;
+typedef SkLongArray(SkDisplayEvent*) SkTDDisplayEventArray;
+typedef SkLongArray(SkDrawable*) SkTDDrawableArray;
+typedef SkLongArray(SkDrawColor*) SkTDDrawColorArray;
+typedef SkLongArray(SkMatrixPart*) SkTDMatrixPartArray;
 typedef SkLongArray(const SkMemberInfo*) SkTDMemberInfoArray;
-typedef SkLongArray(SkPaintPart*) SkTDPaintPartArray; 
-typedef SkLongArray(SkPathPart*) SkTDPathPartArray; 
-typedef SkLongArray(SkTypedArray*) SkTDTypedArrayArray; 
-typedef SkLongArray(SkString*) SkTDStringArray; 
-typedef SkLongArray(SkOperand) SkTDOperandArray; 
-typedef SkLongArray(SkOperand*) SkTDOperandPtrArray; 
+typedef SkLongArray(SkPaintPart*) SkTDPaintPartArray;
+typedef SkLongArray(SkPathPart*) SkTDPathPartArray;
+typedef SkLongArray(SkTypedArray*) SkTDTypedArrayArray;
+typedef SkLongArray(SkString*) SkTDStringArray;
+typedef SkLongArray(SkOperand) SkTDOperandArray;
+typedef SkLongArray(SkOperand*) SkTDOperandPtrArray;
 
 #endif // SkIntArray_DEFINED
-
-
-
diff --git a/src/animator/SkMatrixParts.cpp b/src/animator/SkMatrixParts.cpp
index f00bb8e..a2f3a9a 100644
--- a/src/animator/SkMatrixParts.cpp
+++ b/src/animator/SkMatrixParts.cpp
@@ -16,8 +16,8 @@
 SkMatrixPart::SkMatrixPart() : fMatrix(NULL) {
 }
 
-void SkMatrixPart::dirty() { 
-    fMatrix->dirty(); 
+void SkMatrixPart::dirty() {
+    fMatrix->dirty();
 }
 
 SkDisplayable* SkMatrixPart::getParent() const {
@@ -44,8 +44,8 @@
 
 DEFINE_GET_MEMBER(SkRotate);
 
-SkRotate::SkRotate() : degrees(0) { 
-    center.fX = center.fY = 0; 
+SkRotate::SkRotate() : degrees(0) {
+    center.fX = center.fY = 0;
 }
 
 bool SkRotate::add() {
@@ -66,12 +66,12 @@
 
 DEFINE_GET_MEMBER(SkScale);
 
-SkScale::SkScale() : x(SK_Scalar1), y(SK_Scalar1) { 
-    center.fX = center.fY = 0; 
+SkScale::SkScale() : x(SK_Scalar1), y(SK_Scalar1) {
+    center.fX = center.fY = 0;
 }
 
 bool SkScale::add() {
-    fMatrix->scale(x, y, center);   
+    fMatrix->scale(x, y, center);
     return false;
 }
 
@@ -88,12 +88,12 @@
 
 DEFINE_GET_MEMBER(SkSkew);
 
-SkSkew::SkSkew() : x(0), y(0) { 
-    center.fX = center.fY = 0; 
+SkSkew::SkSkew() : x(0), y(0) {
+    center.fX = center.fY = 0;
 }
 
 bool SkSkew::add() {
-    fMatrix->skew(x, y, center);    
+    fMatrix->skew(x, y, center);
     return false;
 }
 
@@ -113,7 +113,7 @@
 }
 
 bool SkTranslate::add() {
-    fMatrix->translate(x, y);   
+    fMatrix->translate(x, y);
     return false;
 }
 
@@ -130,7 +130,7 @@
 
 DEFINE_GET_MEMBER(SkFromPath);
 
-SkFromPath::SkFromPath() : 
+SkFromPath::SkFromPath() :
     mode(0), offset(0), path(NULL) {
 }
 
@@ -166,7 +166,7 @@
 
 DEFINE_GET_MEMBER(SkRectToRect);
 
-SkRectToRect::SkRectToRect() : 
+SkRectToRect::SkRectToRect() :
     source(NULL), destination(NULL) {
 }
 
@@ -200,7 +200,7 @@
         SkDisplayList::fIndent += 4;
         destination->dump(maker);
         SkDisplayList::fIndent -= 4;
-        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");        
+        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");
     }
     SkDisplayList::fIndent -= 4;
     dumpEnd(maker);
@@ -268,7 +268,7 @@
         SkDisplayList::fIndent += 4;
         destination->dump(maker);
         SkDisplayList::fIndent -= 4;
-        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");        
+        SkDebugf("%*s</destination>\n", SkDisplayList::fIndent, "");
     }
     SkDisplayList::fIndent -= 4;
     dumpEnd(maker);
@@ -290,5 +290,3 @@
         return getMember("destination");
     }
 }
-
-
diff --git a/src/animator/SkMatrixParts.h b/src/animator/SkMatrixParts.h
index 5f6cd54..51c9559 100644
--- a/src/animator/SkMatrixParts.h
+++ b/src/animator/SkMatrixParts.h
@@ -114,6 +114,6 @@
     SkPolygon* destination;
 };
 
-// !!! add concat matrix ? 
+// !!! add concat matrix ?
 
 #endif // SkMatrixParts_DEFINED
diff --git a/src/animator/SkMemberInfo.cpp b/src/animator/SkMemberInfo.cpp
index 7fae503..5e54b53 100644
--- a/src/animator/SkMemberInfo.cpp
+++ b/src/animator/SkMemberInfo.cpp
@@ -52,7 +52,7 @@
         case SkType_Displayable:
         case SkType_Drawable:
         case SkType_Matrix:
-            byteSize = sizeof(void*); 
+            byteSize = sizeof(void*);
             break;
         case SkType_MSec:
             byteSize = sizeof(SkMSec);
@@ -137,7 +137,7 @@
 
 void SkMemberInfo::getValue(const SkDisplayable* displayable, SkOperand value[], int count) const {
     SkASSERT(fType != SkType_String && fType != SkType_MemberProperty);
-    SkASSERT(count == fCount);  
+    SkASSERT(count == fCount);
     void* valuePtr = memberData(displayable);
     size_t byteSize = getSize(displayable);
     SkASSERT(sizeof(value[0].fScalar) == sizeof(value[0])); // no support for 64 bit pointers, yet
@@ -150,7 +150,7 @@
     displayable->dirty();
 }
 
-void SkMemberInfo::setValue(SkDisplayable* displayable, const SkOperand values[], 
+void SkMemberInfo::setValue(SkDisplayable* displayable, const SkOperand values[],
                             int count) const {
     SkASSERT(sizeof(values[0].fScalar) == sizeof(values[0]));   // no support for 64 bit pointers, yet
     char* dst = (char*) memberData(displayable);
@@ -163,7 +163,7 @@
     displayable->dirty();
 }
 
-                            
+
 static inline bool is_between(int c, int min, int max)
 {
     return (unsigned)(c - min) <= (unsigned)(max - min);
@@ -180,9 +180,9 @@
 }
 
 
-bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, 
+bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage,
     int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
-    const char rawValue[], size_t rawValueLen) const 
+    const char rawValue[], size_t rawValueLen) const
 {
     SkString valueStr(rawValue, rawValueLen);
     SkScriptValue scriptValue;
@@ -218,7 +218,7 @@
             }
             *(char*) poundPos = '0'; // overwrite '#'
             valueStr.insert(offset + 1, "xFF");
-        } 
+        }
     }
     if (SkDisplayType::IsDisplayable(&maker, type) || SkDisplayType::IsEnum(&maker, type) || type == SkType_ARGB)
         goto scriptCommon;
@@ -226,22 +226,22 @@
         case SkType_String:
 #if 0
             if (displayable && displayable->isAnimate()) {
-                
+
                 goto noScriptString;
-            } 
+            }
             if (strncmp(rawValue, "#string:", sizeof("#string:") - 1) == 0) {
                 SkASSERT(sizeof("string") == sizeof("script"));
                 char* stringHeader = valueStr.writable_str();
                 memcpy(&stringHeader[1], "script", sizeof("script") - 1);
                 rawValue = valueStr.c_str();
                 goto noScriptString;
-            } else 
+            } else
 #endif
             if (strncmp(rawValue, "#script:", sizeof("#script:") - 1) != 0)
                 goto noScriptString;
             valueStr.remove(0, 8);
         case SkType_Unknown:
-        case SkType_Int: 
+        case SkType_Int:
         case SkType_MSec:  // for the purposes of script, MSec is treated as a Scalar
         case SkType_Point:
         case SkType_3D_Point:
@@ -331,10 +331,10 @@
             SkASSERT(0);
             break;
     }
-//  if (SkDisplayType::IsStruct(type) == false) 
+//  if (SkDisplayType::IsStruct(type) == false)
     {
 writeStruct:
-        if (writeValue(displayable, arrayStorage, storageOffset, maxStorage, 
+        if (writeValue(displayable, arrayStorage, storageOffset, maxStorage,
                 untypedStorage, outType, scriptValue)) {
                     maker.setErrorCode(SkDisplayXMLParserError::kUnexpectedType);
             return false;
@@ -346,18 +346,18 @@
     return true;
 }
 
-bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage, 
+bool SkMemberInfo::setValue(SkAnimateMaker& maker, SkTDOperandArray* arrayStorage,
         int storageOffset, int maxStorage, SkDisplayable* displayable, SkDisplayTypes outType,
         SkString& raw) const {
     return setValue(maker, arrayStorage, storageOffset, maxStorage, displayable, outType, raw.c_str(),
         raw.size());
 }
 
-bool SkMemberInfo::writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage, 
-    int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType, 
+bool SkMemberInfo::writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage,
+    int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType,
     SkScriptValue& scriptValue) const
 {
-    SkOperand* storage = untypedStorage ? (SkOperand*) untypedStorage : arrayStorage ? 
+    SkOperand* storage = untypedStorage ? (SkOperand*) untypedStorage : arrayStorage ?
         arrayStorage->begin() : NULL;
     if (storage)
         storage += storageOffset;
@@ -424,7 +424,7 @@
         string->set(*scriptValue.fOperand.fString);
     } else if (type == SkType_ARGB && outType == SkType_Float) {
         SkTypedArray* array = scriptValue.fOperand.fArray;
-        SkASSERT(scriptValue.fType == SkType_Int || scriptValue.fType == SkType_ARGB || 
+        SkASSERT(scriptValue.fType == SkType_Int || scriptValue.fType == SkType_ARGB ||
             scriptValue.fType == SkType_Array);
         SkASSERT(scriptValue.fType != SkType_Array || (array != NULL &&
             array->getType() == SkType_Int));
@@ -434,7 +434,7 @@
         if (maxStorage == 0)
             arrayStorage->setCount(numberOfComponents);
         for (int index = 0; index < numberOfColors; index++) {
-            SkColor color = scriptValue.fType == SkType_Array ? 
+            SkColor color = scriptValue.fType == SkType_Array ?
                 (SkColor) array->begin()[index].fS32 : (SkColor) scriptValue.fOperand.fS32;
             storage[0].fScalar = SkIntToScalar(SkColorGetA(color));
             storage[1].fScalar = SkIntToScalar(SkColorGetR(color));
@@ -528,11 +528,11 @@
 #endif // SK_USE_CONDENSED_INFO == 0
 
 #if 0
-bool SkMemberInfo::SetValue(void* valuePtr, const char value[], SkDisplayTypes type, 
+bool SkMemberInfo::SetValue(void* valuePtr, const char value[], SkDisplayTypes type,
                             int count) {
     switch (type) {
         case SkType_Animate:
-        case SkType_BaseBitmap: 
+        case SkType_BaseBitmap:
         case SkType_Bitmap:
         case SkType_Dash:
         case SkType_Displayable:
@@ -548,7 +548,7 @@
         case SkType_3D_Point:
         case SkType_Point:
     //  case SkType_PointArray:
-        case SkType_ScalarArray: 
+        case SkType_ScalarArray:
             SkParse::FindScalars(value, (SkScalar*) valuePtr, count);
             break;
         default:
@@ -557,5 +557,3 @@
     return true;
 }
 #endif
-
-
diff --git a/src/animator/SkMemberInfo.h b/src/animator/SkMemberInfo.h
index f2e690c..a1b1941 100644
--- a/src/animator/SkMemberInfo.h
+++ b/src/animator/SkMemberInfo.h
@@ -83,15 +83,15 @@
     }
     void setString(SkDisplayable* , SkString* ) const;
     void setValue(SkDisplayable* , const SkOperand values[], int count) const;
-    bool setValue(SkAnimateMaker& , SkTDOperandArray* storage, 
-        int storageOffset, int maxStorage, SkDisplayable* , 
+    bool setValue(SkAnimateMaker& , SkTDOperandArray* storage,
+        int storageOffset, int maxStorage, SkDisplayable* ,
         SkDisplayTypes outType, const char value[], size_t len) const;
-    bool setValue(SkAnimateMaker& , SkTDOperandArray* storage, 
-        int storageOffset, int maxStorage, SkDisplayable* , 
+    bool setValue(SkAnimateMaker& , SkTDOperandArray* storage,
+        int storageOffset, int maxStorage, SkDisplayable* ,
         SkDisplayTypes outType, SkString& str) const;
 //  void setValue(SkDisplayable* , const char value[], const char name[]) const;
-    bool writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage, 
-        int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType, 
+    bool writeValue(SkDisplayable* displayable, SkTDOperandArray* arrayStorage,
+        int storageOffset, int maxStorage, void* untypedStorage, SkDisplayTypes outType,
         SkScriptValue& scriptValue) const;
 #if SK_USE_CONDENSED_INFO == 0
     static const SkMemberInfo* Find(const SkMemberInfo [], int count, int* index);
@@ -119,7 +119,7 @@
 #define SK_MEMBER_INHERITED \
     { (const char*) INHERITED::fInfo, 0, SkType_BaseClassInfo, INHERITED::fInfoCount }
 
-// #define SK_MEMBER_KEY_TYPE(_member, _type) 
+// #define SK_MEMBER_KEY_TYPE(_member, _type)
 //  {#_member, (size_t) -1, SkType_##_type, 0}
 
 #define SK_FUNCTION(_member) \
@@ -184,7 +184,7 @@
 #define DECLARE_EMPTY_MEMBER_INFO(_type) \
 public: \
     virtual SkDisplayTypes getType() const { return SkType_##_type; }
-    
+
 #define DECLARE_EXTRAS_MEMBER_INFO(_type) \
 public: \
     static const SkMemberInfo fInfo[]; \
@@ -268,4 +268,3 @@
 #endif
 
 #endif // SkMemberInfo_DEFINED
-
diff --git a/src/animator/SkOpArray.cpp b/src/animator/SkOpArray.cpp
index 1f14476..94298cc 100644
--- a/src/animator/SkOpArray.cpp
+++ b/src/animator/SkOpArray.cpp
@@ -14,10 +14,10 @@
 }
 
 bool SkOpArray::getIndex(int index, SkOperand2* operand) {
-	if (index >= count()) {
-		SkASSERT(0);
-		return false;
-	}
-	*operand = begin()[index];
-	return true;
+    if (index >= count()) {
+        SkASSERT(0);
+        return false;
+    }
+    *operand = begin()[index];
+    return true;
 }
diff --git a/src/animator/SkOpArray.h b/src/animator/SkOpArray.h
index d5b9fe7..260bf78 100644
--- a/src/animator/SkOpArray.h
+++ b/src/animator/SkOpArray.h
@@ -11,19 +11,19 @@
 #include "SkOperand2.h"
 #include "SkTDArray_Experimental.h"
 
-typedef SkLongArray(SkOperand2) SkTDOperand2Array; 
+typedef SkLongArray(SkOperand2) SkTDOperand2Array;
 
 class SkOpArray : public SkTDOperand2Array {
 public:
-	SkOpArray();
-	SkOpArray(SkOperand2::OpType type);
-	bool getIndex(int index, SkOperand2* operand);
-	SkOperand2::OpType getType() { return fType; }
-	void setType(SkOperand2::OpType type) { 
-		fType = type;
-	}
+    SkOpArray();
+    SkOpArray(SkOperand2::OpType type);
+    bool getIndex(int index, SkOperand2* operand);
+    SkOperand2::OpType getType() { return fType; }
+    void setType(SkOperand2::OpType type) {
+        fType = type;
+    }
 protected:
-	SkOperand2::OpType fType;
+    SkOperand2::OpType fType;
 };
 
 #endif // SkOpArray_DEFINED
diff --git a/src/animator/SkOperand2.h b/src/animator/SkOperand2.h
index 4f09a01..f844b6b 100644
--- a/src/animator/SkOperand2.h
+++ b/src/animator/SkOperand2.h
@@ -14,40 +14,40 @@
 class SkString;
 
 union SkOperand2 {
-	enum OpType {
-		kNoType,
-		kS32 = 1,
-		kScalar = 2,
-		kString = 4,
-		kArray = 8,
-		kObject = 16
-	};
-	SkOpArray* fArray;
-	void* fObject;
-	size_t fReference;
-	int32_t fS32;
-	SkScalar fScalar;
-	SkString* fString;
+    enum OpType {
+        kNoType,
+        kS32 = 1,
+        kScalar = 2,
+        kString = 4,
+        kArray = 8,
+        kObject = 16
+    };
+    SkOpArray* fArray;
+    void* fObject;
+    size_t fReference;
+    int32_t fS32;
+    SkScalar fScalar;
+    SkString* fString;
 };
 
 struct SkScriptValue2 {
-	enum IsConstant {
-		kConstant,
-		kVariable
-	};
-	enum IsWritten {
-		kUnwritten,
-		kWritten
-	};
-	SkOperand2 fOperand;
-	SkOperand2::OpType fType : 8;
-	IsConstant fIsConstant : 8;
-	IsWritten fIsWritten : 8;
-	SkOpArray* getArray() { SkASSERT(fType == SkOperand2::kArray); return fOperand.fArray; }
-	void* getObject() { SkASSERT(fType == SkOperand2::kObject); return fOperand.fObject; }
-	int32_t getS32() { SkASSERT(fType == SkOperand2::kS32); return fOperand.fS32; }
-	SkScalar getScalar() { SkASSERT(fType == SkOperand2::kScalar); return fOperand.fScalar; }
-	SkString* getString() { SkASSERT(fType == SkOperand2::kString); return fOperand.fString; }
+    enum IsConstant {
+        kConstant,
+        kVariable
+    };
+    enum IsWritten {
+        kUnwritten,
+        kWritten
+    };
+    SkOperand2 fOperand;
+    SkOperand2::OpType fType : 8;
+    IsConstant fIsConstant : 8;
+    IsWritten fIsWritten : 8;
+    SkOpArray* getArray() { SkASSERT(fType == SkOperand2::kArray); return fOperand.fArray; }
+    void* getObject() { SkASSERT(fType == SkOperand2::kObject); return fOperand.fObject; }
+    int32_t getS32() { SkASSERT(fType == SkOperand2::kS32); return fOperand.fS32; }
+    SkScalar getScalar() { SkASSERT(fType == SkOperand2::kScalar); return fOperand.fScalar; }
+    SkString* getString() { SkASSERT(fType == SkOperand2::kString); return fOperand.fString; }
         bool isConstant() const { return fIsConstant == kConstant; }
 };
 
diff --git a/src/animator/SkOperandInterpolator.h b/src/animator/SkOperandInterpolator.h
index 82fa272..adbe69f 100644
--- a/src/animator/SkOperandInterpolator.h
+++ b/src/animator/SkOperandInterpolator.h
@@ -45,4 +45,3 @@
 };
 
 #endif // SkOperandInterpolator_DEFINED
-
diff --git a/src/animator/SkOperandIterpolator.cpp b/src/animator/SkOperandIterpolator.cpp
index dcc454d..7822ee2 100644
--- a/src/animator/SkOperandIterpolator.cpp
+++ b/src/animator/SkOperandIterpolator.cpp
@@ -15,7 +15,7 @@
     fType = SkType_Unknown;
 }
 
-SkOperandInterpolator::SkOperandInterpolator(int elemCount, int frameCount, 
+SkOperandInterpolator::SkOperandInterpolator(int elemCount, int frameCount,
                                              SkDisplayTypes type)
 {
     this->reset(elemCount, frameCount, type);
@@ -147,5 +147,3 @@
 }
 
 #endif
-
-
diff --git a/src/animator/SkPaintParts.cpp b/src/animator/SkPaintParts.cpp
index 2959238..da47178 100644
--- a/src/animator/SkPaintParts.cpp
+++ b/src/animator/SkPaintParts.cpp
@@ -99,5 +99,3 @@
     SkDebugf("style=\"%s\" />\n", string.c_str());
 }
 #endif
-
-
diff --git a/src/animator/SkParseSVGPath.cpp b/src/animator/SkParseSVGPath.cpp
index 1b375aa..4b548e2 100644
--- a/src/animator/SkParseSVGPath.cpp
+++ b/src/animator/SkParseSVGPath.cpp
@@ -101,7 +101,7 @@
     return str;
 }
 
-static const char* find_scalar(const char str[], SkScalar* value, 
+static const char* find_scalar(const char str[], SkScalar* value,
     bool isRelative, SkScalar relative)
 {
     str = SkParse::FindScalar(str, value);
@@ -146,7 +146,7 @@
                 op = 'L';
                 c = points[0];
                 break;
-            case 'L': 
+            case 'L':
                 data = find_points(data, points, 1, relative, &c);
                 fPath.lineTo(points[0]);
                 c = points[0];
@@ -165,10 +165,10 @@
                 c.fY = y;
             }
                 break;
-            case 'C': 
+            case 'C':
                 data = find_points(data, points, 3, relative, &c);
                 goto cubicCommon;
-            case 'S': 
+            case 'S':
                 data = find_points(data, &points[1], 2, relative, &c);
                 points[0] = c;
                 if (previousOp == 'C' || previousOp == 'S') {
@@ -232,4 +232,3 @@
         previousOp = op;
     } while (data[0] > 0);
 }
-
diff --git a/src/animator/SkPathParts.cpp b/src/animator/SkPathParts.cpp
index 5af8150..efc7c33 100644
--- a/src/animator/SkPathParts.cpp
+++ b/src/animator/SkPathParts.cpp
@@ -16,8 +16,8 @@
 SkPathPart::SkPathPart() : fPath(NULL) {
 }
 
-void SkPathPart::dirty() { 
-    fPath->dirty(); 
+void SkPathPart::dirty() {
+    fPath->dirty();
 }
 
 SkDisplayable* SkPathPart::getParent() const {
@@ -86,7 +86,7 @@
 }
 
 bool SkLineTo::add() {
-    fPath->fPath.lineTo(x, y);  
+    fPath->fPath.lineTo(x, y);
     return false;
 }
 
@@ -103,7 +103,7 @@
 DEFINE_GET_MEMBER(SkRLineTo);
 
 bool SkRLineTo::add() {
-    fPath->fPath.rLineTo(x, y); 
+    fPath->fPath.rLineTo(x, y);
     return false;
 }
 
@@ -225,8 +225,8 @@
 
 DEFINE_GET_MEMBER(SkAddRect);
 
-SkAddRect::SkAddRect() { 
-    fRect.setEmpty(); 
+SkAddRect::SkAddRect() {
+    fRect.setEmpty();
 }
 
 bool SkAddRect::add() {
@@ -309,12 +309,10 @@
 }
 
 bool SkAddPath::add() {
-    SkASSERT (path != NULL); 
+    SkASSERT (path != NULL);
     if (matrix)
         fPath->fPath.addPath(path->fPath, matrix->getMatrix());
     else
         fPath->fPath.addPath(path->fPath);
     return false;
 }
-
-
diff --git a/src/animator/SkPathParts.h b/src/animator/SkPathParts.h
index cc81cdd..f82aa74 100644
--- a/src/animator/SkPathParts.h
+++ b/src/animator/SkPathParts.h
@@ -162,4 +162,3 @@
 };
 
 #endif // SkPathParts_DEFINED
-
diff --git a/src/animator/SkPostParts.cpp b/src/animator/SkPostParts.cpp
index 4b776a8..7c0931e 100644
--- a/src/animator/SkPostParts.cpp
+++ b/src/animator/SkPostParts.cpp
@@ -29,7 +29,7 @@
         fParent->fEvent.setS32(dataName, fInt);
     else if (SkScalarIsNaN(fFloat) == false)
         fParent->fEvent.setScalar(dataName, fFloat);
-    else if (string.size() > 0) 
+    else if (string.size() > 0)
         fParent->fEvent.setString(dataName, string);
 //  else
 //      SkASSERT(0);
@@ -54,4 +54,3 @@
 void SkDataInput::onEndElement(SkAnimateMaker&) {
     add();
 }
-
diff --git a/src/animator/SkScript.cpp b/src/animator/SkScript.cpp
index aa06169..14ca625 100644
--- a/src/animator/SkScript.cpp
+++ b/src/animator/SkScript.cpp
@@ -16,7 +16,7 @@
 /* things to do
     ? re-enable support for struct literals (e.g., for initializing points or rects)
         {x:1, y:2}
-    ? use standard XML / script notation like document.getElementById("canvas");  
+    ? use standard XML / script notation like document.getElementById("canvas");
     finish support for typed arrays
         ? allow indexing arrays by string
             this could map to the 'name' attribute of a given child of an array
@@ -224,7 +224,7 @@
             if (nextChar == '>') {
                 op = kShiftRight;
                 goto twoChar;
-            } 
+            }
             op = kGreaterEqual;
             if (nextChar == '=')
                 goto twoChar;
@@ -253,7 +253,7 @@
 twoChar:
                 advance++;
                 break;
-            } 
+            }
             op = kLogicalNot;
             break;
         case '?':
@@ -298,7 +298,7 @@
             } while (true);
             signed char topPrecedence = gPrecedence[compare];
             SkASSERT(topPrecedence != -1);
-            if (topPrecedence > precedence || (topPrecedence == precedence && 
+            if (topPrecedence > precedence || (topPrecedence == precedence &&
                     gOpAttributes[op].fLeftType == kNoType)) {
                 break;
             }
@@ -327,7 +327,7 @@
     *fUserCallBacks.prepend() = callBack;
 }
 
-bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params, 
+bool SkScriptEngine::convertParams(SkTDArray<SkScriptValue>& params,
         const SkFunctionParamType* paramTypes, int paramCount) {
     if (params.count() > paramCount) {
         fError = kTooManyParameters;
@@ -366,7 +366,7 @@
     return ConvertTo(this, toType, value);
 }
 
-bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) { 
+bool SkScriptEngine::evaluateDot(const char*& script, bool suppressed) {
     size_t fieldLength = token_length(++script);        // skip dot
     if (fieldLength == 0) {
         fError = kExpectedFieldName;
@@ -382,8 +382,8 @@
     return evaluateDotParam(script, suppressed, field, fieldLength);
 }
 
-bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed, 
-        const char* field, size_t fieldLength) { 
+bool SkScriptEngine::evaluateDotParam(const char*& script, bool suppressed,
+        const char* field, size_t fieldLength) {
     void* object;
     if (suppressed)
         object = NULL;
@@ -397,7 +397,7 @@
         fOperandStack.pop();
     }
     char ch; // see if it is a simple member or a function
-    while (is_ws(ch = script[0])) 
+    while (is_ws(ch = script[0]))
         script++;
     bool success = true;
     if (ch != '(') {
@@ -410,10 +410,10 @@
         *fBraceStack.push() = kFunctionBrace;
         success = functionParams(&script, params);
         if (success && suppressed == false &&
-                (success = handleMemberFunction(field, fieldLength, object, params)) == false) 
-            fError = kHandleMemberFunctionFailed;       
+                (success = handleMemberFunction(field, fieldLength, object, params)) == false)
+            fError = kHandleMemberFunctionFailed;
     }
-    return success; 
+    return success;
 }
 
 bool SkScriptEngine::evaluateScript(const char** scriptPtr, SkScriptValue* value) {
@@ -559,15 +559,15 @@
                             if (convertTo(tokenInfo->getType(), &tokenValue) == false)
                                 return false;
                         }
-                        tokenInfo->writeValue(fDisplayable, NULL, 0, 0, 
+                        tokenInfo->writeValue(fDisplayable, NULL, 0, 0,
                             (void*) ((char*) fInfo->memberData(fDisplayable) + tokenInfo->fOffset + fArrayOffset),
                             tokenInfo->getType(), tokenValue);
                     }
                 }
                 lastPush = false;
                 continue;
-            } else 
-#endif              
+            } else
+#endif
             if (fBraceStack.top() == kArrayBrace) {
                 SkScriptValue tokenValue;
                 success = innerScript(&script, &tokenValue);    // terminate and return on comma, close brace
@@ -579,12 +579,12 @@
 #if 0 // no support for structures for now
                     if (tokenValue.fType == SkType_Structure) {
                         fArrayOffset += (int) fInfo->getSize(fDisplayable);
-                    } else 
+                    } else
 #endif
                     {
                         SkDisplayTypes type = ToDisplayType(fReturnType);
                         if (fReturnType == kNoType) {
-                            // !!! short sighted; in the future, allow each returned array component to carry 
+                            // !!! short sighted; in the future, allow each returned array component to carry
                             // its own type, and let caller do any needed conversions
                             if (value->fOperand.fArray->count() == 0)
                                 value->fOperand.fArray->setType(type = tokenValue.fType);
@@ -730,11 +730,11 @@
                 if (success == false)
                     return false;
                 lastPush = true;
-                continue; 
+                continue;
             }
             // get next token, and evaluate immediately
             success = evaluateDot(script, SkToBool(suppressed));
-            if (success == false)               
+            if (success == false)
                 return false;
             lastPush = true;
             continue;
@@ -772,7 +772,7 @@
         }
 #endif
         if (ch == ')' && fBraceStack.count() > 0) {
-            SkBraceStyle braceStyle = fBraceStack.top(); 
+            SkBraceStyle braceStyle = fBraceStack.top();
             if (braceStyle == kFunctionBrace) {
                 fBraceStack.pop();
                 break;
@@ -795,7 +795,7 @@
         int advance = logicalOp(ch, nextChar);
         if (advance < 0)     // error
             return false;
-        if (advance == 0) 
+        if (advance == 0)
             advance = arithmeticOp(ch, nextChar, lastPush);
         if (advance == 0) // unknown token
             return false;
@@ -814,7 +814,7 @@
             return false;
         if (processOp() == false)
             return false;
-    }   
+    }
     SkOpType topType = fTypeStack.count() > 0 ? fTypeStack.top() : kNoType;
     if (suppressed == false && topType != fReturnType &&
             topType == kString && fReturnType != kNoType) { // if result is a string, give handle property a chance to convert it to the property value
@@ -945,7 +945,7 @@
         for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
             if (callBack->fCallBackType != kFunction)
                 continue;
-            success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params, 
+            success = (*callBack->fFunctionCallBack)(functionName.c_str(), functionName.size(), params,
                 callBack->fUserStorage, &callbackResult);
             if (success) {
                 fOperandStack.push(callbackResult.fOperand);
@@ -986,7 +986,7 @@
     for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
         if (callBack->fCallBackType != kMemberFunction)
             continue;
-        success = (*callBack->fMemberFunctionCallBack)(field, len, object, params, 
+        success = (*callBack->fMemberFunctionCallBack)(field, len, object, params,
             callBack->fUserStorage, &callbackResult);
         if (success) {
             if (callbackResult.fType == SkType_String)
@@ -1008,7 +1008,7 @@
     for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
         if (callBack->fCallBackType != kObjectToString)
             continue;
-        success = (*callBack->fObjectToStringCallBack)(object, 
+        success = (*callBack->fObjectToStringCallBack)(object,
             callBack->fUserStorage, &callbackResult);
         if (success) {
             if (callbackResult.fType == SkType_String)
@@ -1027,14 +1027,14 @@
 bool SkScriptEngine::handleProperty(bool suppressed) {
     SkScriptValue callbackResult;
     bool success = true;
-    if (suppressed) 
+    if (suppressed)
         goto done;
     success = false; // note that with standard animator-script plugins, callback never returns false
     {
         for (UserCallBack* callBack = fUserCallBacks.begin(); callBack < fUserCallBacks.end(); callBack++) {
             if (callBack->fCallBackType != kProperty)
                 continue;
-            success = (*callBack->fPropertyCallBack)(fToken, fTokenLength, 
+            success = (*callBack->fPropertyCallBack)(fToken, fTokenLength,
                 callBack->fUserStorage, &callbackResult);
             if (success) {
                 if (callbackResult.fType == SkType_String && callbackResult.fOperand.fString == NULL) {
@@ -1146,7 +1146,7 @@
                 fError = kMismatchedBrackets;
                 return -1;
             }
-            if (match == kParen) 
+            if (match == kParen)
                 fOpStack.pop();
             else {
                 SkOpType indexType;
@@ -1157,7 +1157,7 @@
                 }
                 SkOperand indexOperand;
                 fOperandStack.pop(&indexOperand);
-                int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) : 
+                int index = indexType == kScalar ? SkScalarFloor(indexOperand.fScalar) :
                     indexOperand.fS32;
                 SkOpType arrayType;
                 fTypeStack.pop(&arrayType);
@@ -1192,7 +1192,7 @@
             }
             suppress.fSuppress = ifValue.fOperand.fS32 == 0;
             suppress.fOperator = kIf;
-            suppress.fOpStackDepth = fOpStack.count(); 
+            suppress.fOpStackDepth = fOpStack.count();
             suppress.fElse = false;
             fSuppressStack.push(suppress);
             // if left is true, do only up to colon
@@ -1218,7 +1218,7 @@
             if (match == kLogicalOr ? topInt != 0 : topInt == 0) {
                 suppress.fSuppress = true;
                 suppress.fOperator = match;
-                suppress.fOpStackDepth = fOpStack.count(); 
+                suppress.fOpStackDepth = fOpStack.count();
                 suppress.fElse = false;
                 fSuppressStack.push(suppress);
             } else {
@@ -1467,16 +1467,16 @@
     commonCallBack(kProperty, callBack, userStorage);
 }
 
-void SkScriptEngine::track(SkTypedArray* array) { 
-    SkASSERT(fTrackArray.find(array) < 0);  
-    *(fTrackArray.end() - 1) = array; 
-    fTrackArray.appendClear(); 
+void SkScriptEngine::track(SkTypedArray* array) {
+    SkASSERT(fTrackArray.find(array) < 0);
+    *(fTrackArray.end() - 1) = array;
+    fTrackArray.appendClear();
 }
 
-void SkScriptEngine::track(SkString* string) { 
-    SkASSERT(fTrackString.find(string) < 0);  
-    *(fTrackString.end() - 1) = string; 
-    fTrackString.appendClear(); 
+void SkScriptEngine::track(SkString* string) {
+    SkASSERT(fTrackString.find(string) < 0);
+    *(fTrackString.end() - 1) = string;
+    fTrackString.appendClear();
 }
 
 void SkScriptEngine::unboxCallBack(_unboxCallBack func, void* userStorage) {
@@ -1494,7 +1494,7 @@
     if (toType == SkType_Drawable)
         toType = SkType_Displayable;
     SkDisplayTypes type = value->fType;
-    if (type == toType) 
+    if (type == toType)
         return true;
     SkOperand& operand = value->fOperand;
     bool success = true;
@@ -1514,7 +1514,7 @@
             break;
         case SkType_Float:
             if (type == SkType_Int) {
-                if ((uint32_t)operand.fS32 == SK_NaN32)
+                if (operand.fS32 == SK_NaN32)
                     operand.fScalar = SK_ScalarNaN;
                 else if (SkAbs32(operand.fS32) == SK_MaxS32)
                     operand.fScalar = SkSign32(operand.fS32) * SK_ScalarMax;
@@ -1534,7 +1534,7 @@
             engine->track(strPtr);
             if (type == SkType_Int)
                 strPtr->appendS32(operand.fS32);
-            else if (type == SkType_Displayable) 
+            else if (type == SkType_Displayable)
                 SkASSERT(0); // must call through instance version instead of static version
             else {
                 if (type != SkType_Float) {
@@ -1562,7 +1562,7 @@
 
 SkScalar SkScriptEngine::IntToScalar(int32_t s32) {
     SkScalar scalar;
-    if ((uint32_t)s32 == SK_NaN32)
+    if (s32 == SK_NaN32)
         scalar = SK_ScalarNaN;
     else if (SkAbs32(s32) == SK_MaxS32)
         scalar = SkSign32(s32) * SK_ScalarMax;
@@ -1644,9 +1644,7 @@
 
 #ifdef SK_SUPPORT_UNITTEST
 
-#ifdef SK_CAN_USE_FLOAT
-    #include "SkFloatingPoint.h"
-#endif
+#include "SkFloatingPoint.h"
 
 #define DEF_SCALAR_ANSWER   0
 #define DEF_STRING_ANSWER   NULL
@@ -1656,10 +1654,8 @@
     #define testScalar(expression) { #expression, SkType_Float, 0, (float) expression, DEF_STRING_ANSWER }
     #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, sk_float_mod(exp1, exp2), DEF_STRING_ANSWER }
 #else
-    #ifdef SK_CAN_USE_FLOAT
-        #define testScalar(expression) { #expression, SkType_Float, 0, (int) ((expression) * 65536.0f), DEF_STRING_ANSWER }
-        #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, (int) (sk_float_mod(exp1, exp2)  * 65536.0f), DEF_STRING_ANSWER }
-    #endif
+    #define testScalar(expression) { #expression, SkType_Float, 0, (int) ((expression) * 65536.0f), DEF_STRING_ANSWER }
+    #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkType_Float, 0, (int) (sk_float_mod(exp1, exp2)  * 65536.0f), DEF_STRING_ANSWER }
 #endif
 #define testTrue(expression) { #expression, SkType_Int, 1, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
 #define testFalse(expression) { #expression, SkType_Int, 0, DEF_SCALAR_ANSWER, DEF_STRING_ANSWER }
@@ -1669,18 +1665,17 @@
     testInt((6+7)*8),
     testInt(0&&1?2:3),
     testInt(3*(4+5)),
-#ifdef SK_CAN_USE_FLOAT
-    testScalar(1.0+2.0), 
-    testScalar(1.0+5), 
-    testScalar(3.0-1.0), 
-    testScalar(6-1.0), 
-    testScalar(- -5.5- -1.5), 
-    testScalar(2.5*6.), 
-    testScalar(0.5*4), 
-    testScalar(4.5/.5), 
-    testScalar(9.5/19), 
-    testRemainder(9.5, 0.5), 
-    testRemainder(9.,2), 
+    testScalar(1.0+2.0),
+    testScalar(1.0+5),
+    testScalar(3.0-1.0),
+    testScalar(6-1.0),
+    testScalar(- -5.5- -1.5),
+    testScalar(2.5*6.),
+    testScalar(0.5*4),
+    testScalar(4.5/.5),
+    testScalar(9.5/19),
+    testRemainder(9.5, 0.5),
+    testRemainder(9.,2),
     testRemainder(9,2.5),
     testRemainder(-9,2.5),
     testTrue(-9==-9.0),
@@ -1689,7 +1684,6 @@
     testFalse(-9!=-9.0),
     testFalse(-9.!=-4.0-5),
     testFalse(-9.*1!=-4-5),
-#endif
     testInt(0x123),
     testInt(0XABC),
     testInt(0xdeadBEEF),
@@ -1740,7 +1734,6 @@
     testInt(2!=2),
     testInt(2!=11),
     testInt(20!=11),
-#ifdef SK_CAN_USE_FLOAT
     // left int, right scalar
     testInt(2<2.),
     testInt(2<11.),
@@ -1797,7 +1790,6 @@
     testInt(2.!=2.),
     testInt(2.!=11.),
     testInt(20.!=11.),
-#endif
     // int, string (string is int)
     testFalse(2<'2'),
     testTrue(2<'11'),
@@ -1821,7 +1813,6 @@
     testFalse(20<'11.'),
     testTrue(2=='2.'),
     testFalse(2=='11.'),
-#ifdef SK_CAN_USE_FLOAT
     // scalar, string
     testFalse(2.<'2.'),
     testTrue(2.<'11.'),
@@ -1840,7 +1831,6 @@
     testFalse('20'<11.),
     testTrue('2'==2.),
     testFalse('2'==11.),
-#endif
     // string, string
     testFalse('2'<'2'),
     testFalse('2'<'11'),
@@ -1864,7 +1854,7 @@
     testInt(0?2?3:4:5),
     testInt(1?0?3:4:5),
     testInt(0?0?3:4:5),
-    
+
     testInt(1?2:(3?4:5)),
     testInt(0?2:(3?4:5)),
     testInt(1?0:(3?4:5)),
@@ -1873,9 +1863,7 @@
     testInt(0?2:3?4:5),
     testInt(1?0:3?4:5),
     testInt(0?0:3?4:5)
-#ifdef SK_CAN_USE_FLOAT
     , { "123.5", SkType_Float, 0, SkIntToScalar(123) + SK_Scalar1/2, DEF_STRING_ANSWER }
-#endif
 };
 
 #define SkScriptNAnswer_testCount   SK_ARRAY_COUNT(scriptTests)
@@ -1905,4 +1893,3 @@
     }
 }
 #endif
-
diff --git a/src/animator/SkScript.h b/src/animator/SkScript.h
index 95930e8..aa8d9a3 100644
--- a/src/animator/SkScript.h
+++ b/src/animator/SkScript.h
@@ -63,9 +63,9 @@
     typedef bool (*_boxCallBack)(void* userStorage, SkScriptValue* result);
     typedef bool (*_functionCallBack)(const char* func, size_t len, SkTDArray<SkScriptValue>& params,
         void* userStorage, SkScriptValue* result);
-    typedef bool (*_memberCallBack)(const char* member, size_t len, void* object, 
+    typedef bool (*_memberCallBack)(const char* member, size_t len, void* object,
         void* userStorage, SkScriptValue* result);
-    typedef bool (*_memberFunctionCallBack)(const char* member, size_t len, void* object, 
+    typedef bool (*_memberFunctionCallBack)(const char* member, size_t len, void* object,
         SkTDArray<SkScriptValue>& params, void* userStorage, SkScriptValue* result);
 //  typedef bool (*_objectToStringCallBack)(void* object, void* userStorage, SkScriptValue* result);
     typedef bool (*_propertyCallBack)(const char* prop, size_t len, void* userStorage, SkScriptValue* result);
@@ -169,7 +169,7 @@
         kTowardsNumber = 0,
         kTowardsString
     };
-    
+
 protected:
 
     struct SkOperatorAttributes {
diff --git a/src/animator/SkScript2.h b/src/animator/SkScript2.h
index 05073e4..33e2af7 100644
--- a/src/animator/SkScript2.h
+++ b/src/animator/SkScript2.h
@@ -15,278 +15,277 @@
 #include "SkTDict.h"
 #include "SkTDStack.h"
 
-typedef SkLongArray(SkString*) SkTDStringArray; 
+typedef SkLongArray(SkString*) SkTDStringArray;
 
 class SkAnimateMaker;
 class SkScriptCallBack;
 
 class SkScriptEngine2 {
 public:
-	enum Error {
-		kNoError,
-		kArrayIndexOutOfBounds,
-		kCouldNotFindReferencedID,
-		kFunctionCallFailed,
-		kMemberOpFailed,
-		kPropertyOpFailed
-	};
+    enum Error {
+        kNoError,
+        kArrayIndexOutOfBounds,
+        kCouldNotFindReferencedID,
+        kFunctionCallFailed,
+        kMemberOpFailed,
+        kPropertyOpFailed
+    };
 
-	enum Attrs {
-		kConstant,
-		kVariable
-	};
+    enum Attrs {
+        kConstant,
+        kVariable
+    };
 
-	SkScriptEngine2(SkOperand2::OpType returnType);
-	~SkScriptEngine2();
-	bool convertTo(SkOperand2::OpType , SkScriptValue2* );
-	bool evaluateScript(const char** script, SkScriptValue2* value);
-	void forget(SkOpArray* array);
-	Error getError() { return fError; }
-	SkOperand2::OpType getReturnType() { return fReturnType; }
-	void track(SkOpArray* array) { 
-		SkASSERT(fTrackArray.find(array) < 0);  
-		*fTrackArray.append() = array; }
-	void track(SkString* string) { 
-		SkASSERT(fTrackString.find(string) < 0);  
-		*fTrackString.append() = string; 
-	}
-	static bool ConvertTo(SkScriptEngine2* , SkOperand2::OpType toType, SkScriptValue2* value);
-	static SkScalar IntToScalar(int32_t );
-	static bool ValueToString(const SkScriptValue2& value, SkString* string);
+    SkScriptEngine2(SkOperand2::OpType returnType);
+    ~SkScriptEngine2();
+    bool convertTo(SkOperand2::OpType , SkScriptValue2* );
+    bool evaluateScript(const char** script, SkScriptValue2* value);
+    void forget(SkOpArray* array);
+    Error getError() { return fError; }
+    SkOperand2::OpType getReturnType() { return fReturnType; }
+    void track(SkOpArray* array) {
+        SkASSERT(fTrackArray.find(array) < 0);
+        *fTrackArray.append() = array; }
+    void track(SkString* string) {
+        SkASSERT(fTrackString.find(string) < 0);
+        *fTrackString.append() = string;
+    }
+    static bool ConvertTo(SkScriptEngine2* , SkOperand2::OpType toType, SkScriptValue2* value);
+    static SkScalar IntToScalar(int32_t );
+    static bool ValueToString(const SkScriptValue2& value, SkString* string);
 
-	enum Op {		// used by tokenizer attribute table
-		kUnassigned,
-		kAdd,
-		kBitAnd,
-		kBitNot,
-		kBitOr,
-		kDivide,
-		kEqual,
-		kFlipOps,
-		kGreaterEqual,
-		kLogicalAnd,
-		kLogicalNot,
-		kLogicalOr,
-		kMinus,
-		kModulo,
-		kMultiply,
-		kShiftLeft,
-		kShiftRight,	// signed
-		kSubtract,
-		kXor,
+    enum Op {        // used by tokenizer attribute table
+        kUnassigned,
+        kAdd,
+        kBitAnd,
+        kBitNot,
+        kBitOr,
+        kDivide,
+        kEqual,
+        kFlipOps,
+        kGreaterEqual,
+        kLogicalAnd,
+        kLogicalNot,
+        kLogicalOr,
+        kMinus,
+        kModulo,
+        kMultiply,
+        kShiftLeft,
+        kShiftRight,    // signed
+        kSubtract,
+        kXor,
 // following not in attribute table
-		kArrayOp,
-		kElse,
-		kIf,
-		kParen,
-		kLastLogicalOp,
-		kArtificialOp = 0x20
-	};
+        kArrayOp,
+        kElse,
+        kIf,
+        kParen,
+        kLastLogicalOp,
+        kArtificialOp = 0x20
+    };
 
-	enum TypeOp {	// generated by tokenizer
-		kNop, // should never get generated
-		kAccumulatorPop,
-		kAccumulatorPush,
-		kAddInt,
-		kAddScalar,
-		kAddString,	// string concat
-		kArrayIndex,
-		kArrayParam,
-		kArrayToken,
-		kBitAndInt,
-		kBitNotInt,
-		kBitOrInt,
-		kBoxToken,
-		kCallback,
-		kDivideInt,
-		kDivideScalar,
-		kDotOperator,
-		kElseOp,
-		kEnd,
-		kEqualInt,
-		kEqualScalar,
-		kEqualString,
-		kFunctionCall,
-		kFlipOpsOp,
-		kFunctionToken,
-		kGreaterEqualInt,
-		kGreaterEqualScalar,
-		kGreaterEqualString,
-		kIfOp,
-		kIntToScalar,
-		kIntToScalar2,
-		kIntToString,
-		kIntToString2,
-		kIntegerAccumulator,
-		kIntegerOperand,
-		kLogicalAndInt,
-		kLogicalNotInt,
-		kLogicalOrInt,
-		kMemberOp,
-		kMinusInt,
-		kMinusScalar,
-		kModuloInt,
-		kModuloScalar,
-		kMultiplyInt,
-		kMultiplyScalar,
-		kPropertyOp,
-		kScalarAccumulator,
-		kScalarOperand,
-		kScalarToInt,
-		kScalarToInt2,
-		kScalarToString,
-		kScalarToString2,
-		kShiftLeftInt,
-		kShiftRightInt,	// signed
-		kStringAccumulator,
-		kStringOperand,
-		kStringToInt,
-		kStringToScalar,
-		kStringToScalar2,
-		kStringTrack,
-		kSubtractInt,
-		kSubtractScalar,
-		kToBool,
-		kUnboxToken,
-		kUnboxToken2,
-		kXorInt,
-		kLastTypeOp
-	};
+    enum TypeOp {    // generated by tokenizer
+        kNop, // should never get generated
+        kAccumulatorPop,
+        kAccumulatorPush,
+        kAddInt,
+        kAddScalar,
+        kAddString,    // string concat
+        kArrayIndex,
+        kArrayParam,
+        kArrayToken,
+        kBitAndInt,
+        kBitNotInt,
+        kBitOrInt,
+        kBoxToken,
+        kCallback,
+        kDivideInt,
+        kDivideScalar,
+        kDotOperator,
+        kElseOp,
+        kEnd,
+        kEqualInt,
+        kEqualScalar,
+        kEqualString,
+        kFunctionCall,
+        kFlipOpsOp,
+        kFunctionToken,
+        kGreaterEqualInt,
+        kGreaterEqualScalar,
+        kGreaterEqualString,
+        kIfOp,
+        kIntToScalar,
+        kIntToScalar2,
+        kIntToString,
+        kIntToString2,
+        kIntegerAccumulator,
+        kIntegerOperand,
+        kLogicalAndInt,
+        kLogicalNotInt,
+        kLogicalOrInt,
+        kMemberOp,
+        kMinusInt,
+        kMinusScalar,
+        kModuloInt,
+        kModuloScalar,
+        kMultiplyInt,
+        kMultiplyScalar,
+        kPropertyOp,
+        kScalarAccumulator,
+        kScalarOperand,
+        kScalarToInt,
+        kScalarToInt2,
+        kScalarToString,
+        kScalarToString2,
+        kShiftLeftInt,
+        kShiftRightInt,    // signed
+        kStringAccumulator,
+        kStringOperand,
+        kStringToInt,
+        kStringToScalar,
+        kStringToScalar2,
+        kStringTrack,
+        kSubtractInt,
+        kSubtractScalar,
+        kToBool,
+        kUnboxToken,
+        kUnboxToken2,
+        kXorInt,
+        kLastTypeOp
+    };
 
-	enum OpBias {
-		kNoBias,
-		kTowardsNumber = 0,
-		kTowardsString
-	};
+    enum OpBias {
+        kNoBias,
+        kTowardsNumber = 0,
+        kTowardsString
+    };
 
 protected:
 
-	enum BraceStyle {
-	//	kStructBrace,
-		kArrayBrace,
-		kFunctionBrace
-	};
+    enum BraceStyle {
+    //    kStructBrace,
+        kArrayBrace,
+        kFunctionBrace
+    };
 
-	enum AddTokenRegister {
-		kAccumulator,
-		kOperand
-	};
-	
-	enum ResultIsBoolean {
-		kResultIsNotBoolean,
-		kResultIsBoolean
-	};
+    enum AddTokenRegister {
+        kAccumulator,
+        kOperand
+    };
 
-	struct OperatorAttributes {
-		unsigned int fLeftType : 3;	// SkOpType union, but only lower values
-		unsigned int fRightType : 3;	 // SkOpType union, but only lower values
-		OpBias fBias : 1;
-		ResultIsBoolean fResultIsBoolean : 1;
-	};
-	
-	struct Branch {
-		Branch() {
-		}
-		
-		Branch(Op op, int depth, unsigned offset) : fOffset(offset), fOpStackDepth(depth), fOperator(op),
-			fPrimed(kIsNotPrimed), fDone(kIsNotDone) {
-		}
+    enum ResultIsBoolean {
+        kResultIsNotBoolean,
+        kResultIsBoolean
+    };
 
-		enum Primed {
-			kIsNotPrimed,
-			kIsPrimed
-		};
+    struct OperatorAttributes {
+        unsigned int fLeftType : 3;    // SkOpType union, but only lower values
+        unsigned int fRightType : 3;     // SkOpType union, but only lower values
+        OpBias fBias : 1;
+        ResultIsBoolean fResultIsBoolean : 1;
+    };
 
-		enum Done {
-			kIsNotDone,
-			kIsDone,
-		};
+    struct Branch {
+        Branch() {
+        }
 
-		unsigned fOffset : 16; // offset in generated stream where branch needs to go
-		int fOpStackDepth : 7; // depth when operator was found
-		Op fOperator : 6; // operand which generated branch
-		mutable Primed fPrimed : 1;	// mark when next instruction generates branch
-		Done fDone : 1;	// mark when branch is complete
-		void prime() { fPrimed = kIsPrimed; }
-		void resolve(SkDynamicMemoryWStream* , size_t offset);
-	};
+        Branch(Op op, int depth, unsigned offset) : fOffset(offset), fOpStackDepth(depth), fOperator(op),
+            fPrimed(kIsNotPrimed), fDone(kIsNotDone) {
+        }
 
-	static const OperatorAttributes gOpAttributes[];
-	static const signed char gPrecedence[];
-	static const TypeOp gTokens[];
-	void addToken(TypeOp );
-	void addTokenConst(SkScriptValue2* , AddTokenRegister , SkOperand2::OpType , TypeOp );
-	void addTokenInt(int );
-	void addTokenScalar(SkScalar );
-	void addTokenString(const SkString& );
-	void addTokenValue(const SkScriptValue2& , AddTokenRegister );
-	int arithmeticOp(char ch, char nextChar, bool lastPush);
-	bool convertParams(SkTDArray<SkScriptValue2>* ,
-		const SkOperand2::OpType* paramTypes, int paramTypeCount);
-	void convertToString(SkOperand2* operand, SkOperand2::OpType type) {
-		SkScriptValue2 scriptValue;
-		scriptValue.fOperand = *operand;
-		scriptValue.fType = type;
-		convertTo(SkOperand2::kString, &scriptValue);
-		*operand = scriptValue.fOperand;
-	}
-	bool evaluateDot(const char*& script);
-	bool evaluateDotParam(const char*& script, const char* field, size_t fieldLength);
-	bool functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params);
-	size_t getTokenOffset();
-	SkOperand2::OpType getUnboxType(SkOperand2 scriptValue);
-	bool handleArrayIndexer(const char** scriptPtr);
-	bool handleFunction(const char** scriptPtr);
-	bool handleMember(const char* field, size_t len, void* object);
-	bool handleMemberFunction(const char* field, size_t len, void* object, 
-		SkTDArray<SkScriptValue2>* params);
-	bool handleProperty();
-	bool handleUnbox(SkScriptValue2* scriptValue);
-	bool innerScript(const char** scriptPtr, SkScriptValue2* value);
-	int logicalOp(char ch, char nextChar);
-	void processLogicalOp(Op op);
-	bool processOp();
-	void resolveBranch(Branch& );
-//	void setAnimateMaker(SkAnimateMaker* maker) { fMaker = maker; }
-	SkDynamicMemoryWStream fStream;
-	SkDynamicMemoryWStream* fActiveStream;
-	SkTDStack<BraceStyle> fBraceStack;		// curly, square, function paren
-	SkTDStack<Branch> fBranchStack;  // logical operators, slot to store forward branch
-	SkLongArray(SkScriptCallBack*) fCallBackArray;
-	SkTDStack<Op> fOpStack;
-	SkTDStack<SkScriptValue2> fValueStack;
-//	SkAnimateMaker* fMaker;
-	SkLongArray(SkOpArray*) fTrackArray;
-	SkTDStringArray fTrackString;
-	const char* fToken; // one-deep stack
-	size_t fTokenLength;
-	SkOperand2::OpType fReturnType;
-	Error fError;
-	SkOperand2::OpType fAccumulatorType;	// tracking for code generation
-	SkBool fBranchPopAllowed;
-	SkBool fConstExpression;
-	SkBool fOperandInUse;
+        enum Primed {
+            kIsNotPrimed,
+            kIsPrimed
+        };
+
+        enum Done {
+            kIsNotDone,
+            kIsDone,
+        };
+
+        unsigned fOffset : 16; // offset in generated stream where branch needs to go
+        int fOpStackDepth : 7; // depth when operator was found
+        Op fOperator : 6; // operand which generated branch
+        mutable Primed fPrimed : 1;    // mark when next instruction generates branch
+        Done fDone : 1;    // mark when branch is complete
+        void prime() { fPrimed = kIsPrimed; }
+        void resolve(SkDynamicMemoryWStream* , size_t offset);
+    };
+
+    static const OperatorAttributes gOpAttributes[];
+    static const signed char gPrecedence[];
+    static const TypeOp gTokens[];
+    void addToken(TypeOp );
+    void addTokenConst(SkScriptValue2* , AddTokenRegister , SkOperand2::OpType , TypeOp );
+    void addTokenInt(int );
+    void addTokenScalar(SkScalar );
+    void addTokenString(const SkString& );
+    void addTokenValue(const SkScriptValue2& , AddTokenRegister );
+    int arithmeticOp(char ch, char nextChar, bool lastPush);
+    bool convertParams(SkTDArray<SkScriptValue2>* ,
+        const SkOperand2::OpType* paramTypes, int paramTypeCount);
+    void convertToString(SkOperand2* operand, SkOperand2::OpType type) {
+        SkScriptValue2 scriptValue;
+        scriptValue.fOperand = *operand;
+        scriptValue.fType = type;
+        convertTo(SkOperand2::kString, &scriptValue);
+        *operand = scriptValue.fOperand;
+    }
+    bool evaluateDot(const char*& script);
+    bool evaluateDotParam(const char*& script, const char* field, size_t fieldLength);
+    bool functionParams(const char** scriptPtr, SkTDArray<SkScriptValue2>* params);
+    size_t getTokenOffset();
+    SkOperand2::OpType getUnboxType(SkOperand2 scriptValue);
+    bool handleArrayIndexer(const char** scriptPtr);
+    bool handleFunction(const char** scriptPtr);
+    bool handleMember(const char* field, size_t len, void* object);
+    bool handleMemberFunction(const char* field, size_t len, void* object,
+        SkTDArray<SkScriptValue2>* params);
+    bool handleProperty();
+    bool handleUnbox(SkScriptValue2* scriptValue);
+    bool innerScript(const char** scriptPtr, SkScriptValue2* value);
+    int logicalOp(char ch, char nextChar);
+    void processLogicalOp(Op op);
+    bool processOp();
+    void resolveBranch(Branch& );
+//    void setAnimateMaker(SkAnimateMaker* maker) { fMaker = maker; }
+    SkDynamicMemoryWStream fStream;
+    SkDynamicMemoryWStream* fActiveStream;
+    SkTDStack<BraceStyle> fBraceStack;        // curly, square, function paren
+    SkTDStack<Branch> fBranchStack;  // logical operators, slot to store forward branch
+    SkLongArray(SkScriptCallBack*) fCallBackArray;
+    SkTDStack<Op> fOpStack;
+    SkTDStack<SkScriptValue2> fValueStack;
+//    SkAnimateMaker* fMaker;
+    SkLongArray(SkOpArray*) fTrackArray;
+    SkTDStringArray fTrackString;
+    const char* fToken; // one-deep stack
+    size_t fTokenLength;
+    SkOperand2::OpType fReturnType;
+    Error fError;
+    SkOperand2::OpType fAccumulatorType;    // tracking for code generation
+    SkBool fBranchPopAllowed;
+    SkBool fConstExpression;
+    SkBool fOperandInUse;
 private:
 #ifdef SK_DEBUG
 public:
-	void decompile(const unsigned char* , size_t );
-	static void UnitTest();
-	static void ValidateDecompileTable();
+    void decompile(const unsigned char* , size_t );
+    static void UnitTest();
+    static void ValidateDecompileTable();
 #endif
 };
 
 #ifdef SK_DEBUG
 
 struct SkScriptNAnswer2 {
-	const char* fScript;
-	SkOperand2::OpType fType;
-	int32_t fIntAnswer;
-	SkScalar fScalarAnswer;
-	const char* fStringAnswer;
+    const char* fScript;
+    SkOperand2::OpType fType;
+    int32_t fIntAnswer;
+    SkScalar fScalarAnswer;
+    const char* fStringAnswer;
 };
 
 #endif
 
 
 #endif // SkScript2_DEFINED
-
diff --git a/src/animator/SkScriptCallBack.h b/src/animator/SkScriptCallBack.h
index b2a1958..dcbaf11 100644
--- a/src/animator/SkScriptCallBack.h
+++ b/src/animator/SkScriptCallBack.h
@@ -13,53 +13,53 @@
 
 class SkScriptCallBack {
 public:
-	enum Type {
-		kBox,
-		kFunction,
-		kMember,
-		kMemberFunction,
-		kProperty,
-		kUnbox
-	};
+    enum Type {
+        kBox,
+        kFunction,
+        kMember,
+        kMemberFunction,
+        kProperty,
+        kUnbox
+    };
 
-	virtual bool getReference(const char* , size_t len, SkScriptValue2* result) {  return false; }
-	virtual SkOperand2::OpType getReturnType(size_t ref, SkOperand2*) { 
-		return SkOperand2::kS32; }
-	virtual Type getType() const = 0;
+    virtual bool getReference(const char* , size_t len, SkScriptValue2* result) {  return false; }
+    virtual SkOperand2::OpType getReturnType(size_t ref, SkOperand2*) {
+        return SkOperand2::kS32; }
+    virtual Type getType() const = 0;
 };
 
 class SkScriptCallBackConvert : public SkScriptCallBack {
 public:
-	virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) = 0;
+    virtual bool convert(SkOperand2::OpType type, SkOperand2* operand) = 0;
 };
 
 class SkScriptCallBackFunction : public SkScriptCallBack {
 public:
-	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0;
-	virtual Type getType() const { return kFunction; }
-	virtual bool invoke(size_t ref, SkOpArray* params, SkOperand2* value) = 0;
+    virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0;
+    virtual Type getType() const { return kFunction; }
+    virtual bool invoke(size_t ref, SkOpArray* params, SkOperand2* value) = 0;
 };
 
 class SkScriptCallBackMember: public SkScriptCallBack {
 public:
-	bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref);
-	virtual Type getType() const { return kMember; }
-	virtual bool invoke(size_t ref, void* object, SkOperand2* value) = 0;
+    bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref);
+    virtual Type getType() const { return kMember; }
+    virtual bool invoke(size_t ref, void* object, SkOperand2* value) = 0;
 };
 
 class SkScriptCallBackMemberFunction : public SkScriptCallBack {
 public:
-	bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref);
-	virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0;
-	virtual Type getType() const { return kMemberFunction; }
-	virtual bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value) = 0;
+    bool getMemberReference(const char* , size_t len, void* object, SkScriptValue2* ref);
+    virtual void getParamTypes(SkIntArray(SkOperand2::OpType)* types) = 0;
+    virtual Type getType() const { return kMemberFunction; }
+    virtual bool invoke(size_t ref, void* object, SkOpArray* params, SkOperand2* value) = 0;
 };
 
 class SkScriptCallBackProperty : public SkScriptCallBack {
 public:
-	virtual bool getConstValue(const char* name, size_t len, SkOperand2* value) { return false; }
-	virtual bool getResult(size_t ref, SkOperand2* answer) { return false; }
-	virtual Type getType() const { return kProperty; }
+    virtual bool getConstValue(const char* name, size_t len, SkOperand2* value) { return false; }
+    virtual bool getResult(size_t ref, SkOperand2* answer) { return false; }
+    virtual Type getType() const { return kProperty; }
 };
 
 #endif // SkScriptCallBack_DEFINED
diff --git a/src/animator/SkScriptDecompile.cpp b/src/animator/SkScriptDecompile.cpp
index 933e0d7..995da87 100644
--- a/src/animator/SkScriptDecompile.cpp
+++ b/src/animator/SkScriptDecompile.cpp
@@ -99,7 +99,7 @@
     OperandName(kString),
     OperandName(kArray),
     OperandName(kObject)
-};  
+};
 
 static size_t gOperandNamesSize = sizeof(gOperandNames) / sizeof(gOperandNames[0]);
 
@@ -135,7 +135,7 @@
             opCode += sizeof(index);
             SkDebugf(" index: %d", index);
             } break;
-        case SkScriptEngine2::kFunctionCall: 
+        case SkScriptEngine2::kFunctionCall:
         case SkScriptEngine2::kMemberOp:
         case SkScriptEngine2::kPropertyOp: {
             size_t ref;
@@ -155,11 +155,7 @@
             SkScalar scalar;
             memcpy(&scalar, opCode, sizeof(scalar));
             opCode += sizeof(SkScalar);
-#ifdef SK_CAN_USE_FLOAT
             SkDebugf(" scalar: %g", SkScalarToFloat(scalar));
-#else
-            SkDebugf(" scalar: %x", scalar);
-#endif
             } break;
         case SkScriptEngine2::kStringAccumulator:
         case SkScriptEngine2::kStringOperand: {
diff --git a/src/animator/SkScriptRuntime.cpp b/src/animator/SkScriptRuntime.cpp
index 6a96633..f287850 100644
--- a/src/animator/SkScriptRuntime.cpp
+++ b/src/animator/SkScriptRuntime.cpp
@@ -27,326 +27,325 @@
 // replace script string with script tokens preceeded by special value
 
 // need second version of script plugins that return private index of found value?
-	// then would need in script index of plugin, private index
+    // then would need in script index of plugin, private index
 
 // encode brace stack push/pop as opcodes
 
 // should token script enocde type where possible?
 
 // current flow:
-	// strip whitespace
-	// if in array brace [ recurse, continue
-	// if token, handle function, or array, or property (continue)
-	// parse number, continue
-	// parse token, continue
-	// parse string literal, continue
-	// if dot operator, handle dot, continue
-	// if [ , handle array literal or accessor, continue
-	// if ), pop (if function, break)
-	// if ], pop ; if ',' break
-	// handle logical ops
-	// or, handle arithmetic ops
-	// loop
+    // strip whitespace
+    // if in array brace [ recurse, continue
+    // if token, handle function, or array, or property (continue)
+    // parse number, continue
+    // parse token, continue
+    // parse string literal, continue
+    // if dot operator, handle dot, continue
+    // if [ , handle array literal or accessor, continue
+    // if ), pop (if function, break)
+    // if ], pop ; if ',' break
+    // handle logical ops
+    // or, handle arithmetic ops
+    // loop
 
 // !!! things to do
-	// add separate processing loop to advance while suppressed
-	// or, include jump offset to skip suppressed code?
+    // add separate processing loop to advance while suppressed
+    // or, include jump offset to skip suppressed code?
 
 SkScriptRuntime::~SkScriptRuntime() {
-	for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
-		delete *stringPtr;
-	for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
-		delete *arrayPtr;
+    for (SkString** stringPtr = fTrackString.begin(); stringPtr < fTrackString.end(); stringPtr++)
+        delete *stringPtr;
+    for (SkOpArray** arrayPtr = fTrackArray.begin(); arrayPtr < fTrackArray.end(); arrayPtr++)
+        delete *arrayPtr;
 }
 
 bool SkScriptRuntime::executeTokens(unsigned char* opCode) {
-	SkOperand2 operand[2];	// 1=accumulator and 2=operand
-	SkScriptEngine2::TypeOp op;
-	size_t ref;
-	int index, size;
-	int registerLoad;
-	SkScriptCallBack* callBack SK_INIT_TO_AVOID_WARNING;
-	do {
-	switch ((op = (SkScriptEngine2::TypeOp) *opCode++)) {
-		case SkScriptEngine2::kArrayToken:	// create an array
-			operand[0].fArray = new SkOpArray(SkOperand2::kNoType /*fReturnType*/);
-			break;
-		case SkScriptEngine2::kArrayIndex:	// array accessor
-			index = operand[1].fS32;
-			if (index >= operand[0].fArray->count()) {
-				fError = kArrayIndexOutOfBounds;
-				return false;
-			}
-			operand[0] = operand[0].fArray->begin()[index];
-			break;
-		case SkScriptEngine2::kArrayParam:	// array initializer, or function param
-			*operand[0].fArray->append() = operand[1];
-			break;
-		case SkScriptEngine2::kCallback:
-			memcpy(&index, opCode, sizeof(index));
-			opCode += sizeof(index);
-			callBack = fCallBackArray[index];
-			break;
-		case SkScriptEngine2::kFunctionCall: {
-			memcpy(&ref, opCode, sizeof(ref));
-			opCode += sizeof(ref);
-			SkScriptCallBackFunction* callBackFunction = (SkScriptCallBackFunction*) callBack;
-			if (callBackFunction->invoke(ref, operand[0].fArray, /* params */
-					&operand[0] /* result */) == false) {
-				fError = kFunctionCallFailed;
-				return false;
-			}
-			} break;
-		case SkScriptEngine2::kMemberOp: {
-			memcpy(&ref, opCode, sizeof(ref));
-			opCode += sizeof(ref);
-			SkScriptCallBackMember* callBackMember = (SkScriptCallBackMember*) callBack;
-			if (callBackMember->invoke(ref, operand[0].fObject, &operand[0]) == false) {
-				fError = kMemberOpFailed;
-				return false;
-			}
-			} break;
-		case SkScriptEngine2::kPropertyOp: {
-			memcpy(&ref, opCode, sizeof(ref));
-			opCode += sizeof(ref);
-			SkScriptCallBackProperty* callBackProperty = (SkScriptCallBackProperty*) callBack;
-			if (callBackProperty->getResult(ref, &operand[0])== false) {
-				fError = kPropertyOpFailed;
-				return false;
-			}
-			} break;
-		case SkScriptEngine2::kAccumulatorPop:
-			fRunStack.pop(&operand[0]);
-			break;
-		case SkScriptEngine2::kAccumulatorPush:
-			*fRunStack.push() = operand[0];
-			break;
-		case SkScriptEngine2::kIntegerAccumulator:
-		case SkScriptEngine2::kIntegerOperand:
-			registerLoad = op - SkScriptEngine2::kIntegerAccumulator;
-			memcpy(&operand[registerLoad].fS32, opCode, sizeof(int32_t));
-			opCode += sizeof(int32_t);
-			break;
-		case SkScriptEngine2::kScalarAccumulator:
-		case SkScriptEngine2::kScalarOperand:
-			registerLoad = op - SkScriptEngine2::kScalarAccumulator;
-			memcpy(&operand[registerLoad].fScalar, opCode, sizeof(SkScalar));
-			opCode += sizeof(SkScalar);
-			break;
-		case SkScriptEngine2::kStringAccumulator:
-		case SkScriptEngine2::kStringOperand: {
-			SkString* strPtr = new SkString();
-			track(strPtr);
-			registerLoad = op - SkScriptEngine2::kStringAccumulator;
-			memcpy(&size, opCode, sizeof(size));
-			opCode += sizeof(size);
-			strPtr->set((char*) opCode, size);
-			opCode += size;
-			operand[registerLoad].fString = strPtr;
-			} break;
-		case SkScriptEngine2::kStringTrack: // call after kObjectToValue
-			track(operand[0].fString);
-			break;
-		case SkScriptEngine2::kBoxToken: {
-			SkOperand2::OpType type;
-			memcpy(&type, opCode, sizeof(type));
-			opCode += sizeof(type);
-			SkScriptCallBackConvert* callBackBox = (SkScriptCallBackConvert*) callBack;
-			if (callBackBox->convert(type, &operand[0]) == false)
-				return false;
-			} break;
-		case SkScriptEngine2::kUnboxToken:
-		case SkScriptEngine2::kUnboxToken2: {
-			SkScriptCallBackConvert* callBackUnbox = (SkScriptCallBackConvert*) callBack;
-			if (callBackUnbox->convert(SkOperand2::kObject, &operand[0]) == false)
-				return false;
-			} break;
-		case SkScriptEngine2::kIfOp:
-		case SkScriptEngine2::kLogicalAndInt:
-			memcpy(&size, opCode, sizeof(size));
-			opCode += sizeof(size);
-			if (operand[0].fS32 == 0)
-				opCode += size; // skip to else (or end of if predicate)
-			break;
-		case SkScriptEngine2::kElseOp:
-			memcpy(&size, opCode, sizeof(size));
-			opCode += sizeof(size);
-			opCode += size; // if true: after predicate, always skip to end of else
-			break;
-		case SkScriptEngine2::kLogicalOrInt:
-			memcpy(&size, opCode, sizeof(size));
-			opCode += sizeof(size);
-			if (operand[0].fS32 != 0)
-				opCode += size; // skip to kToBool opcode after || predicate
-			break;
-		// arithmetic conversion ops
-		case SkScriptEngine2::kFlipOpsOp:
-			SkTSwap(operand[0], operand[1]);
-			break;
-		case SkScriptEngine2::kIntToString: 
-		case SkScriptEngine2::kIntToString2: 
-		case SkScriptEngine2::kScalarToString:
-		case SkScriptEngine2::kScalarToString2:{
-			SkString* strPtr = new SkString();
-			track(strPtr);
-			if (op == SkScriptEngine2::kIntToString || op == SkScriptEngine2::kIntToString2)
-				strPtr->appendS32(operand[op - SkScriptEngine2::kIntToString].fS32);
-			else
-				strPtr->appendScalar(operand[op - SkScriptEngine2::kScalarToString].fScalar);
-			operand[0].fString = strPtr;
-			} break;
-		case SkScriptEngine2::kIntToScalar:
-		case SkScriptEngine2::kIntToScalar2:
-			operand[0].fScalar = SkScriptEngine2::IntToScalar(operand[op - SkScriptEngine2::kIntToScalar].fS32);
-			break;
-		case SkScriptEngine2::kStringToInt:
-			if (SkParse::FindS32(operand[0].fString->c_str(), &operand[0].fS32) == false)
-				return false; 
-			break;
-		case SkScriptEngine2::kStringToScalar:
-		case SkScriptEngine2::kStringToScalar2:
-			if (SkParse::FindScalar(operand[0].fString->c_str(), 
-					&operand[op - SkScriptEngine2::kStringToScalar].fScalar) == false) 
-				return false; 
-			break;
-		case SkScriptEngine2::kScalarToInt:
-			operand[0].fS32 = SkScalarFloor(operand[0].fScalar);
-			break;
-		// arithmetic ops
-		case SkScriptEngine2::kAddInt:
-			operand[0].fS32 += operand[1].fS32;
-			break;
-		case SkScriptEngine2::kAddScalar:
-			operand[0].fScalar += operand[1].fScalar;
-			break;
-		case SkScriptEngine2::kAddString:
-//			if (fTrackString.find(operand[1].fString) < 0) {
-//				operand[1].fString = SkNEW_ARGS(SkString, (*operand[1].fString));
-//				track(operand[1].fString);
-//			}
-			operand[0].fString->append(*operand[1].fString);
-			break;
-		case SkScriptEngine2::kBitAndInt:
-			operand[0].fS32 &= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kBitNotInt:
-			operand[0].fS32 = ~operand[0].fS32;
-			break;
-		case SkScriptEngine2::kBitOrInt:
-			operand[0].fS32 |= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kDivideInt:
-			SkASSERT(operand[1].fS32 != 0);
-			if (operand[1].fS32 == 0)
-				operand[0].fS32 = operand[0].fS32 == 0 ? SK_NaN32 : 
-					operand[0].fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
-			else
-			if (operand[1].fS32 != 0) // throw error on divide by zero?
-				operand[0].fS32 /= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kDivideScalar:
-			if (operand[1].fScalar == 0)
-				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);
-			break;
-		case SkScriptEngine2::kEqualInt:
-			operand[0].fS32 = operand[0].fS32 == operand[1].fS32;
-			break;
-		case SkScriptEngine2::kEqualScalar:
-			operand[0].fS32 = operand[0].fScalar == operand[1].fScalar;
-			break;
-		case SkScriptEngine2::kEqualString:
-			operand[0].fS32 = *operand[0].fString == *operand[1].fString;
-			break;
-		case SkScriptEngine2::kGreaterEqualInt:
-			operand[0].fS32 = operand[0].fS32 >= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kGreaterEqualScalar:
-			operand[0].fS32 = operand[0].fScalar >= operand[1].fScalar;
-			break;
-		case SkScriptEngine2::kGreaterEqualString:
-			operand[0].fS32 = strcmp(operand[0].fString->c_str(), operand[1].fString->c_str()) >= 0;
-			break;
-		case SkScriptEngine2::kToBool:
-			operand[0].fS32 = !! operand[0].fS32;
-			break;
-		case SkScriptEngine2::kLogicalNotInt:
-			operand[0].fS32 = ! operand[0].fS32;
-			break;
-		case SkScriptEngine2::kMinusInt:
-			operand[0].fS32 = -operand[0].fS32;
-			break;
-		case SkScriptEngine2::kMinusScalar:
-			operand[0].fScalar = -operand[0].fScalar;
-			break;
-		case SkScriptEngine2::kModuloInt:
-			operand[0].fS32 %= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kModuloScalar:
-			operand[0].fScalar = SkScalarMod(operand[0].fScalar, operand[1].fScalar);
-			break;
-		case SkScriptEngine2::kMultiplyInt:
-			operand[0].fS32 *= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kMultiplyScalar:
-			operand[0].fScalar = SkScalarMul(operand[0].fScalar, operand[1].fScalar);
-			break;
-		case SkScriptEngine2::kShiftLeftInt:
-			operand[0].fS32 <<= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kShiftRightInt:
-			operand[0].fS32 >>= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kSubtractInt:
-			operand[0].fS32 -= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kSubtractScalar:
-			operand[0].fScalar -= operand[1].fScalar;
-			break;
-		case SkScriptEngine2::kXorInt:
-			operand[0].fS32 ^= operand[1].fS32;
-			break;
-		case SkScriptEngine2::kEnd:
-			goto done;
-		case SkScriptEngine2::kNop:
-				SkASSERT(0);
+    SkOperand2 operand[2];    // 1=accumulator and 2=operand
+    SkScriptEngine2::TypeOp op;
+    size_t ref;
+    int index, size;
+    int registerLoad;
+    SkScriptCallBack* callBack SK_INIT_TO_AVOID_WARNING;
+    do {
+    switch ((op = (SkScriptEngine2::TypeOp) *opCode++)) {
+        case SkScriptEngine2::kArrayToken:    // create an array
+            operand[0].fArray = new SkOpArray(SkOperand2::kNoType /*fReturnType*/);
+            break;
+        case SkScriptEngine2::kArrayIndex:    // array accessor
+            index = operand[1].fS32;
+            if (index >= operand[0].fArray->count()) {
+                fError = kArrayIndexOutOfBounds;
+                return false;
+            }
+            operand[0] = operand[0].fArray->begin()[index];
+            break;
+        case SkScriptEngine2::kArrayParam:    // array initializer, or function param
+            *operand[0].fArray->append() = operand[1];
+            break;
+        case SkScriptEngine2::kCallback:
+            memcpy(&index, opCode, sizeof(index));
+            opCode += sizeof(index);
+            callBack = fCallBackArray[index];
+            break;
+        case SkScriptEngine2::kFunctionCall: {
+            memcpy(&ref, opCode, sizeof(ref));
+            opCode += sizeof(ref);
+            SkScriptCallBackFunction* callBackFunction = (SkScriptCallBackFunction*) callBack;
+            if (callBackFunction->invoke(ref, operand[0].fArray, /* params */
+                    &operand[0] /* result */) == false) {
+                fError = kFunctionCallFailed;
+                return false;
+            }
+            } break;
+        case SkScriptEngine2::kMemberOp: {
+            memcpy(&ref, opCode, sizeof(ref));
+            opCode += sizeof(ref);
+            SkScriptCallBackMember* callBackMember = (SkScriptCallBackMember*) callBack;
+            if (callBackMember->invoke(ref, operand[0].fObject, &operand[0]) == false) {
+                fError = kMemberOpFailed;
+                return false;
+            }
+            } break;
+        case SkScriptEngine2::kPropertyOp: {
+            memcpy(&ref, opCode, sizeof(ref));
+            opCode += sizeof(ref);
+            SkScriptCallBackProperty* callBackProperty = (SkScriptCallBackProperty*) callBack;
+            if (callBackProperty->getResult(ref, &operand[0])== false) {
+                fError = kPropertyOpFailed;
+                return false;
+            }
+            } break;
+        case SkScriptEngine2::kAccumulatorPop:
+            fRunStack.pop(&operand[0]);
+            break;
+        case SkScriptEngine2::kAccumulatorPush:
+            *fRunStack.push() = operand[0];
+            break;
+        case SkScriptEngine2::kIntegerAccumulator:
+        case SkScriptEngine2::kIntegerOperand:
+            registerLoad = op - SkScriptEngine2::kIntegerAccumulator;
+            memcpy(&operand[registerLoad].fS32, opCode, sizeof(int32_t));
+            opCode += sizeof(int32_t);
+            break;
+        case SkScriptEngine2::kScalarAccumulator:
+        case SkScriptEngine2::kScalarOperand:
+            registerLoad = op - SkScriptEngine2::kScalarAccumulator;
+            memcpy(&operand[registerLoad].fScalar, opCode, sizeof(SkScalar));
+            opCode += sizeof(SkScalar);
+            break;
+        case SkScriptEngine2::kStringAccumulator:
+        case SkScriptEngine2::kStringOperand: {
+            SkString* strPtr = new SkString();
+            track(strPtr);
+            registerLoad = op - SkScriptEngine2::kStringAccumulator;
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            strPtr->set((char*) opCode, size);
+            opCode += size;
+            operand[registerLoad].fString = strPtr;
+            } break;
+        case SkScriptEngine2::kStringTrack: // call after kObjectToValue
+            track(operand[0].fString);
+            break;
+        case SkScriptEngine2::kBoxToken: {
+            SkOperand2::OpType type;
+            memcpy(&type, opCode, sizeof(type));
+            opCode += sizeof(type);
+            SkScriptCallBackConvert* callBackBox = (SkScriptCallBackConvert*) callBack;
+            if (callBackBox->convert(type, &operand[0]) == false)
+                return false;
+            } break;
+        case SkScriptEngine2::kUnboxToken:
+        case SkScriptEngine2::kUnboxToken2: {
+            SkScriptCallBackConvert* callBackUnbox = (SkScriptCallBackConvert*) callBack;
+            if (callBackUnbox->convert(SkOperand2::kObject, &operand[0]) == false)
+                return false;
+            } break;
+        case SkScriptEngine2::kIfOp:
+        case SkScriptEngine2::kLogicalAndInt:
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            if (operand[0].fS32 == 0)
+                opCode += size; // skip to else (or end of if predicate)
+            break;
+        case SkScriptEngine2::kElseOp:
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            opCode += size; // if true: after predicate, always skip to end of else
+            break;
+        case SkScriptEngine2::kLogicalOrInt:
+            memcpy(&size, opCode, sizeof(size));
+            opCode += sizeof(size);
+            if (operand[0].fS32 != 0)
+                opCode += size; // skip to kToBool opcode after || predicate
+            break;
+        // arithmetic conversion ops
+        case SkScriptEngine2::kFlipOpsOp:
+            SkTSwap(operand[0], operand[1]);
+            break;
+        case SkScriptEngine2::kIntToString:
+        case SkScriptEngine2::kIntToString2:
+        case SkScriptEngine2::kScalarToString:
+        case SkScriptEngine2::kScalarToString2:{
+            SkString* strPtr = new SkString();
+            track(strPtr);
+            if (op == SkScriptEngine2::kIntToString || op == SkScriptEngine2::kIntToString2)
+                strPtr->appendS32(operand[op - SkScriptEngine2::kIntToString].fS32);
+            else
+                strPtr->appendScalar(operand[op - SkScriptEngine2::kScalarToString].fScalar);
+            operand[0].fString = strPtr;
+            } break;
+        case SkScriptEngine2::kIntToScalar:
+        case SkScriptEngine2::kIntToScalar2:
+            operand[0].fScalar = SkScriptEngine2::IntToScalar(operand[op - SkScriptEngine2::kIntToScalar].fS32);
+            break;
+        case SkScriptEngine2::kStringToInt:
+            if (SkParse::FindS32(operand[0].fString->c_str(), &operand[0].fS32) == false)
+                return false;
+            break;
+        case SkScriptEngine2::kStringToScalar:
+        case SkScriptEngine2::kStringToScalar2:
+            if (SkParse::FindScalar(operand[0].fString->c_str(),
+                    &operand[op - SkScriptEngine2::kStringToScalar].fScalar) == false)
+                return false;
+            break;
+        case SkScriptEngine2::kScalarToInt:
+            operand[0].fS32 = SkScalarFloor(operand[0].fScalar);
+            break;
+        // arithmetic ops
+        case SkScriptEngine2::kAddInt:
+            operand[0].fS32 += operand[1].fS32;
+            break;
+        case SkScriptEngine2::kAddScalar:
+            operand[0].fScalar += operand[1].fScalar;
+            break;
+        case SkScriptEngine2::kAddString:
+//            if (fTrackString.find(operand[1].fString) < 0) {
+//                operand[1].fString = SkNEW_ARGS(SkString, (*operand[1].fString));
+//                track(operand[1].fString);
+//            }
+            operand[0].fString->append(*operand[1].fString);
+            break;
+        case SkScriptEngine2::kBitAndInt:
+            operand[0].fS32 &= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kBitNotInt:
+            operand[0].fS32 = ~operand[0].fS32;
+            break;
+        case SkScriptEngine2::kBitOrInt:
+            operand[0].fS32 |= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kDivideInt:
+            SkASSERT(operand[1].fS32 != 0);
+            if (operand[1].fS32 == 0)
+                operand[0].fS32 = operand[0].fS32 == 0 ? SK_NaN32 :
+                    operand[0].fS32 > 0 ? SK_MaxS32 : -SK_MaxS32;
+            else
+            if (operand[1].fS32 != 0) // throw error on divide by zero?
+                operand[0].fS32 /= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kDivideScalar:
+            if (operand[1].fScalar == 0)
+                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);
+            break;
+        case SkScriptEngine2::kEqualInt:
+            operand[0].fS32 = operand[0].fS32 == operand[1].fS32;
+            break;
+        case SkScriptEngine2::kEqualScalar:
+            operand[0].fS32 = operand[0].fScalar == operand[1].fScalar;
+            break;
+        case SkScriptEngine2::kEqualString:
+            operand[0].fS32 = *operand[0].fString == *operand[1].fString;
+            break;
+        case SkScriptEngine2::kGreaterEqualInt:
+            operand[0].fS32 = operand[0].fS32 >= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kGreaterEqualScalar:
+            operand[0].fS32 = operand[0].fScalar >= operand[1].fScalar;
+            break;
+        case SkScriptEngine2::kGreaterEqualString:
+            operand[0].fS32 = strcmp(operand[0].fString->c_str(), operand[1].fString->c_str()) >= 0;
+            break;
+        case SkScriptEngine2::kToBool:
+            operand[0].fS32 = !! operand[0].fS32;
+            break;
+        case SkScriptEngine2::kLogicalNotInt:
+            operand[0].fS32 = ! operand[0].fS32;
+            break;
+        case SkScriptEngine2::kMinusInt:
+            operand[0].fS32 = -operand[0].fS32;
+            break;
+        case SkScriptEngine2::kMinusScalar:
+            operand[0].fScalar = -operand[0].fScalar;
+            break;
+        case SkScriptEngine2::kModuloInt:
+            operand[0].fS32 %= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kModuloScalar:
+            operand[0].fScalar = SkScalarMod(operand[0].fScalar, operand[1].fScalar);
+            break;
+        case SkScriptEngine2::kMultiplyInt:
+            operand[0].fS32 *= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kMultiplyScalar:
+            operand[0].fScalar = SkScalarMul(operand[0].fScalar, operand[1].fScalar);
+            break;
+        case SkScriptEngine2::kShiftLeftInt:
+            operand[0].fS32 <<= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kShiftRightInt:
+            operand[0].fS32 >>= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kSubtractInt:
+            operand[0].fS32 -= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kSubtractScalar:
+            operand[0].fScalar -= operand[1].fScalar;
+            break;
+        case SkScriptEngine2::kXorInt:
+            operand[0].fS32 ^= operand[1].fS32;
+            break;
+        case SkScriptEngine2::kEnd:
+            goto done;
+        case SkScriptEngine2::kNop:
+                SkASSERT(0);
     default:
         break;
-	}
-	} while (true);
+    }
+    } while (true);
 done:
-	fRunStack.push(operand[0]);
-	return true;
+    fRunStack.push(operand[0]);
+    return true;
 }
 
 bool SkScriptRuntime::getResult(SkOperand2* result) {
-	if (fRunStack.count() == 0)
-		return false;
-	fRunStack.pop(result);
-	return true;
+    if (fRunStack.count() == 0)
+        return false;
+    fRunStack.pop(result);
+    return true;
 }
 
-void SkScriptRuntime::track(SkOpArray* array) { 
-	SkASSERT(fTrackArray.find(array) < 0);  
-	*fTrackArray.append() = array; 
+void SkScriptRuntime::track(SkOpArray* array) {
+    SkASSERT(fTrackArray.find(array) < 0);
+    *fTrackArray.append() = array;
 }
 
-void SkScriptRuntime::track(SkString* string) { 
-	SkASSERT(fTrackString.find(string) < 0);  
-	*fTrackString.append() = string; 
+void SkScriptRuntime::track(SkString* string) {
+    SkASSERT(fTrackString.find(string) < 0);
+    *fTrackString.append() = string;
 }
 
 void SkScriptRuntime::untrack(SkOpArray* array) {
-	int index = fTrackArray.find(array);
-	SkASSERT(index >= 0);
-	fTrackArray.begin()[index] = NULL;
+    int index = fTrackArray.find(array);
+    SkASSERT(index >= 0);
+    fTrackArray.begin()[index] = NULL;
 }
 
 void SkScriptRuntime::untrack(SkString* string) {
-	int index = fTrackString.find(string);
-	SkASSERT(index >= 0);
-	fTrackString.begin()[index] = NULL;
+    int index = fTrackString.find(string);
+    SkASSERT(index >= 0);
+    fTrackString.begin()[index] = NULL;
 }
-
diff --git a/src/animator/SkScriptRuntime.h b/src/animator/SkScriptRuntime.h
index 5de7b30..3e73801 100644
--- a/src/animator/SkScriptRuntime.h
+++ b/src/animator/SkScriptRuntime.h
@@ -14,37 +14,37 @@
 
 class SkScriptCallBack;
 
-typedef SkLongArray(SkString*) SkTDStringArray; 
-typedef SkLongArray(SkScriptCallBack*) SkTDScriptCallBackArray; 
+typedef SkLongArray(SkString*) SkTDStringArray;
+typedef SkLongArray(SkScriptCallBack*) SkTDScriptCallBackArray;
 
 class SkScriptRuntime {
 public:
-	enum SkError {
-		kNoError,
-		kArrayIndexOutOfBounds,
-		kCouldNotFindReferencedID,
-		kFunctionCallFailed,
-		kMemberOpFailed,
-		kPropertyOpFailed
-	};
+    enum SkError {
+        kNoError,
+        kArrayIndexOutOfBounds,
+        kCouldNotFindReferencedID,
+        kFunctionCallFailed,
+        kMemberOpFailed,
+        kPropertyOpFailed
+    };
 
-	SkScriptRuntime(SkTDScriptCallBackArray& callBackArray) : fCallBackArray(callBackArray)
-		{  }
-	~SkScriptRuntime();
-	bool executeTokens(unsigned char* opCode);
-	bool getResult(SkOperand2* result);
-	void untrack(SkOpArray* array);
-	void untrack(SkString* string);
+    SkScriptRuntime(SkTDScriptCallBackArray& callBackArray) : fCallBackArray(callBackArray)
+        {  }
+    ~SkScriptRuntime();
+    bool executeTokens(unsigned char* opCode);
+    bool getResult(SkOperand2* result);
+    void untrack(SkOpArray* array);
+    void untrack(SkString* string);
 private:
-	void track(SkOpArray* array);
-	void track(SkString* string);
-	SkTDScriptCallBackArray& fCallBackArray;
-	SkError fError;
-	SkTDStack<SkOperand2> fRunStack;
-	SkLongArray(SkOpArray*) fTrackArray;
-	SkTDStringArray fTrackString;
-	// illegal
-	SkScriptRuntime& operator=(const SkScriptRuntime&);
+    void track(SkOpArray* array);
+    void track(SkString* string);
+    SkTDScriptCallBackArray& fCallBackArray;
+    SkError fError;
+    SkTDStack<SkOperand2> fRunStack;
+    SkLongArray(SkOpArray*) fTrackArray;
+    SkTDStringArray fTrackString;
+    // illegal
+    SkScriptRuntime& operator=(const SkScriptRuntime&);
 };
 
-#endif // SkScriptRuntime_DEFINED
\ No newline at end of file
+#endif // SkScriptRuntime_DEFINED
diff --git a/src/animator/SkScriptTokenizer.cpp b/src/animator/SkScriptTokenizer.cpp
index b2b6e4f..ca6cab0 100644
--- a/src/animator/SkScriptTokenizer.cpp
+++ b/src/animator/SkScriptTokenizer.cpp
@@ -17,31 +17,31 @@
 
 const SkScriptEngine2::OperatorAttributes SkScriptEngine2::gOpAttributes[] = {
 { SkOperand2::kNoType, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean },
-{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), 
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
     SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsString, kResultIsNotBoolean },    // kAdd
 { SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitAnd
 { SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitNot
 { SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kBitOr
-{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), 
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
     SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kDivide
-{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), 
-    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber, 
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
+    SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar |SkOperand2:: kString), kTowardsNumber,
     kResultIsBoolean }, // kEqual
 { SkOperand2::kS32, SkOperand2::kNoType, kNoBias, kResultIsNotBoolean },     // kFlipOps
-{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), 
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString),
     SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar | SkOperand2::kString), kTowardsNumber,
     kResultIsBoolean }, // kGreaterEqual
 { SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalAnd    (really, ToBool)
 { SkOperand2::kNoType, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalNot
 { SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kLogicalOr
 { SkOperand2::kNoType, SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMinus
-{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), 
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
     SkOperand2::OpType(SkOperand2::kS32 |SkOperand2:: kScalar), kNoBias, kResultIsNotBoolean }, // kModulo
-{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), 
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
     SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kMultiply
 { SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftLeft
 { SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean }, // kShiftRight
-{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), 
+{ SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar),
     SkOperand2::OpType(SkOperand2::kS32 | SkOperand2::kScalar), kNoBias, kResultIsNotBoolean }, // kSubtract
 { SkOperand2::kS32, SkOperand2::kS32, kNoBias, kResultIsNotBoolean } // kXor
 };
@@ -120,7 +120,7 @@
 }
 
 SkScriptEngine2::SkScriptEngine2(SkOperand2::OpType returnType) : fActiveStream(&fStream),
-fTokenLength(0), fReturnType(returnType), fError(kNoError), 
+fTokenLength(0), fReturnType(returnType), fError(kNoError),
 fAccumulatorType(SkOperand2::kNoType),
 fBranchPopAllowed(true), fConstExpression(true), fOperandInUse(false)
 {
@@ -151,7 +151,7 @@
     fActiveStream->write(&charOp, sizeof(charOp));
 }
 
-void SkScriptEngine2::addTokenConst(SkScriptValue2* value, AddTokenRegister reg, 
+void SkScriptEngine2::addTokenConst(SkScriptValue2* value, AddTokenRegister reg,
                                     SkOperand2::OpType toType, SkScriptEngine2::TypeOp op) {
     if (value->fIsConstant == SkScriptValue2::kConstant && convertTo(toType, value))
         return;
@@ -243,7 +243,7 @@
             if (nextChar == '>') {
                 op = kShiftRight;
                 goto twoChar;
-            } 
+            }
             op = kGreaterEqual;
             if (nextChar == '=')
                 goto twoChar;
@@ -272,7 +272,7 @@
 twoChar:
                     advance++;
                 break;
-            } 
+            }
             op = kLogicalNot;
             break;
         case '?':
@@ -316,7 +316,7 @@
         } while (true);
         signed char topPrecedence = gPrecedence[compare];
         SkASSERT(topPrecedence != -1);
-        if (topPrecedence > precedence || (topPrecedence == precedence && 
+        if (topPrecedence > precedence || (topPrecedence == precedence &&
             gOpAttributes[op].fLeftType == SkOperand2::kNoType)) {
             break;
         }
@@ -331,14 +331,14 @@
     return advance;
 }
 
-bool SkScriptEngine2::convertParams(SkTDArray<SkScriptValue2>* params, 
+bool SkScriptEngine2::convertParams(SkTDArray<SkScriptValue2>* params,
                                     const SkOperand2::OpType* paramTypes, int paramCount) {
     int count = params->count();
     if (count > paramCount) {
         SkASSERT(0);
         return false;    // too many parameters passed
     }
-    for (int index = 0; index < count; index++) 
+    for (int index = 0; index < count; index++)
         convertTo(paramTypes[index], &(*params)[index]);
     return true;
 }
@@ -355,7 +355,7 @@
     return ConvertTo(this, toType, value);
 }
 
-bool SkScriptEngine2::evaluateDot(const char*& script) { 
+bool SkScriptEngine2::evaluateDot(const char*& script) {
     size_t fieldLength = token_length(++script);        // skip dot
     SkASSERT(fieldLength > 0); // !!! add error handling
     const char* field = script;
@@ -370,14 +370,14 @@
         return false;
 }
 
-bool SkScriptEngine2::evaluateDotParam(const char*& script, const char* field, size_t fieldLength) { 
+bool SkScriptEngine2::evaluateDotParam(const char*& script, const char* field, size_t fieldLength) {
     SkScriptValue2& top = fValueStack.top();
     if (top.fType != SkOperand2::kObject)
         return false;
     void* object = top.fOperand.fObject;
     fValueStack.pop();
     char ch; // see if it is a simple member or a function
-    while (is_ws(ch = script[0])) 
+    while (is_ws(ch = script[0]))
         script++;
     bool success = true;
     if (ch != '(')
@@ -389,7 +389,7 @@
         if (success)
             success = handleMemberFunction(field, fieldLength, object, &params);
     }
-    return success; 
+    return success;
 }
 
 bool SkScriptEngine2::evaluateScript(const char** scriptPtr, SkScriptValue2* value) {
@@ -492,7 +492,7 @@
                 {
                     SkOperand2::OpType type = fReturnType;
                     if (fReturnType == SkOperand2::kNoType) {
-                        // !!! short sighted; in the future, allow each returned array component to carry 
+                        // !!! short sighted; in the future, allow each returned array component to carry
                         // its own type, and let caller do any needed conversions
                         if (value->fOperand.fArray->count() == 0)
                             value->fOperand.fArray->setType(type = tokenValue.fType);
@@ -513,7 +513,7 @@
             if (ch == '(') {
                 *fBraceStack.push() = kFunctionBrace;
                 SkString functionName(fToken, fTokenLength);
-                
+
                 if (handleFunction(&script) == false)
                     return false;
                 lastPush = true;
@@ -611,7 +611,7 @@
                 SkASSERT(fValueStack.count() > 0); // !!! add error handling
                 SkScriptValue2 top;
                 fValueStack.pop(&top);
-                
+
                 addTokenInt(top.fType);
                 addToken(kBoxToken);
                 top.fType = SkOperand2::kObject;
@@ -621,7 +621,7 @@
                 success = evaluateDotParam(script, token, tokenLength);
                 SkASSERT(success);
                 lastPush = true;
-                continue; 
+                continue;
             }
             // get next token, and evaluate immediately
             success = evaluateDot(script);
@@ -638,7 +638,7 @@
                 *fBraceStack.push() = kArrayBrace;
                 operand.fOperand.fArray = value->fOperand.fArray = new SkOpArray(fReturnType);
                 track(value->fOperand.fArray);
-                
+
                 operand.fType = SkOperand2::kArray;
                 operand.fIsConstant = SkScriptValue2::kVariable;
                 fValueStack.push(operand);
@@ -663,7 +663,7 @@
         }
 #endif
         if (ch == ')' && fBraceStack.count() > 0) {
-            BraceStyle braceStyle = fBraceStack.top(); 
+            BraceStyle braceStyle = fBraceStack.top();
             if (braceStyle == kFunctionBrace) {
                 fBraceStack.pop();
                 break;
@@ -681,7 +681,7 @@
         }
         char nextChar = script[1];
         int advance = logicalOp(ch, nextChar);
-        if (advance == 0) 
+        if (advance == 0)
             advance = arithmeticOp(ch, nextChar, lastPush);
         if (advance == 0) // unknown token
             return false;
@@ -747,10 +747,10 @@
         addToken(kEnd);
         SkAutoDataUnref data(fStream.copyToData());
 #ifdef SK_DEBUG
-        decompile(data.bytes(), data.size());
+        decompile(data->bytes(), data->size());
 #endif
         SkScriptRuntime runtime(fCallBackArray);
-        runtime.executeTokens((unsigned char*) data.bytes());
+        runtime.executeTokens((unsigned char*) data->bytes());
         SkScriptValue2 value1;
         runtime.getResult(&value1.fOperand);
         value1.fType = fReturnType;
@@ -760,7 +760,7 @@
         if (fValueStack.count() == 0)
             return false;
         fValueStack.pop(value);
-        if (value->fType != fReturnType && value->fType == SkOperand2::kObject && 
+        if (value->fType != fReturnType && value->fType == SkOperand2::kObject &&
             fReturnType != SkOperand2::kNoType)
             convertTo(fReturnType, value);
     }
@@ -850,7 +850,7 @@
         return success;
 }
 
-bool SkScriptEngine2::handleMemberFunction(const char* field, size_t len, void* object, 
+bool SkScriptEngine2::handleMemberFunction(const char* field, size_t len, void* object,
                                            SkTDArray<SkScriptValue2>* params) {
     bool success = true;
     for (SkScriptCallBack** callBack = fCallBackArray.begin(); callBack < fCallBackArray.end(); callBack++) {
@@ -980,20 +980,20 @@
         case kParen:
         case kArrayOp:
             SkASSERT(fOpStack.count() > 1 && fOpStack.top() == op);    // !!! add error handling
-            if (op == kParen) 
+            if (op == kParen)
                 fOpStack.pop();
             else {
                 SkScriptValue2 value;
                 fValueStack.pop(&value);
                 SkASSERT(value.fType == SkOperand2::kS32 || value.fType == SkOperand2::kScalar); // !!! add error handling (although, could permit strings eventually)
-                int index = value.fType == SkOperand2::kScalar ? SkScalarFloor(value.fOperand.fScalar) : 
+                int index = value.fType == SkOperand2::kScalar ? SkScalarFloor(value.fOperand.fScalar) :
                     value.fOperand.fS32;
                 SkScriptValue2 arrayValue;
                 fValueStack.pop(&arrayValue);
                 SkASSERT(arrayValue.fType == SkOperand2::kArray);  // !!! add error handling
                 SkOpArray* array = arrayValue.fOperand.fArray;
                 SkOperand2 operand;
-                bool success = array->getIndex(index, &operand);
+                SkDEBUGCODE(bool success = ) array->getIndex(index, &operand);
                 SkASSERT(success); // !!! add error handling
                 SkScriptValue2 resultValue;
                 resultValue.fType = array->getType();
@@ -1046,7 +1046,7 @@
             // if 'or', write bne goto to bool
             addToken(op == kLogicalAnd ? kLogicalAndInt : kLogicalOrInt);
             Branch branch(op, fOpStack.count(), getTokenOffset());
-            addTokenInt(0); // placeholder for future branch            
+            addTokenInt(0); // placeholder for future branch
             oldTop.fPrimed = wasPrime;
             oldTop.fDone = wasDone;
             *fBranchStack.push() = branch;
@@ -1073,7 +1073,7 @@
     bool constantOperands = value2.fIsConstant == SkScriptValue2::kConstant;
     if (attributes->fLeftType != SkOperand2::kNoType) {
         fValueStack.pop(&value1);
-        constantOperands &= value1.fIsConstant == SkScriptValue2::kConstant; 
+        constantOperands &= value1.fIsConstant == SkScriptValue2::kConstant;
         value1.fIsWritten = SkScriptValue2::kUnwritten;
         if (op == kFlipOps) {
             SkTSwap(value1, value2);
@@ -1094,17 +1094,17 @@
     }
     if (attributes->fLeftType != SkOperand2::kNoType) {
         if (value1.fType != value2.fType) {
-            if ((attributes->fLeftType & SkOperand2::kString) && attributes->fBias & kTowardsString && 
+            if ((attributes->fLeftType & SkOperand2::kString) && attributes->fBias & kTowardsString &&
                 ((value1.fType | value2.fType) & SkOperand2::kString)) {
                 if (value1.fType == SkOperand2::kS32 || value1.fType == SkOperand2::kScalar) {
-                    addTokenConst(&value1, kAccumulator, SkOperand2::kString, 
+                    addTokenConst(&value1, kAccumulator, SkOperand2::kString,
                                   value1.fType == SkOperand2::kS32 ? kIntToString : kScalarToString);
                 }
                 if (value2.fType == SkOperand2::kS32 || value2.fType == SkOperand2::kScalar) {
-                    addTokenConst(&value2, kOperand, SkOperand2::kString, 
+                    addTokenConst(&value2, kOperand, SkOperand2::kString,
                                   value2.fType == SkOperand2::kS32 ? kIntToString2 : kScalarToString2);
                 }
-            } else if (attributes->fLeftType & SkOperand2::kScalar && ((value1.fType | value2.fType) & 
+            } else if (attributes->fLeftType & SkOperand2::kScalar && ((value1.fType | value2.fType) &
                                                                        SkOperand2::kScalar)) {
                 if (value1.fType == SkOperand2::kS32)
                     addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kIntToScalar);
@@ -1115,7 +1115,7 @@
         if ((value1.fType & attributes->fLeftType) == 0 || value1.fType != value2.fType) {
             if (value1.fType == SkOperand2::kString)
                 addTokenConst(&value1, kAccumulator, SkOperand2::kScalar, kStringToScalar);
-            if (value1.fType == SkOperand2::kScalar && (attributes->fLeftType == SkOperand2::kS32 || 
+            if (value1.fType == SkOperand2::kScalar && (attributes->fLeftType == SkOperand2::kS32 ||
                                                         value2.fType == SkOperand2::kS32))
                 addTokenConst(&value1, kAccumulator, SkOperand2::kS32, kScalarToInt);
         }
@@ -1125,7 +1125,7 @@
     if ((value2.fType & attributes->fRightType) == 0 || value1.fType != value2.fType) {
         if (value2.fType == SkOperand2::kString)
             addTokenConst(&value2, rhRegister, SkOperand2::kScalar, kStringToScalar2);
-        if (value2.fType == SkOperand2::kScalar && (attributes->fRightType == SkOperand2::kS32 || 
+        if (value2.fType == SkOperand2::kScalar && (attributes->fRightType == SkOperand2::kS32 ||
                                                     value1.fType == SkOperand2::kS32))
             addTokenConst(&value2, rhRegister, SkOperand2::kS32, kScalarToInt2);
     }
@@ -1154,11 +1154,11 @@
     if (constantOperands) {
         addToken(kEnd);
         SkAutoDataUnref data(fStream.copyToData());
-#ifdef SK_DEBUG        
-        decompile(data.bytes(), data.size());
+#ifdef SK_DEBUG
+        decompile(data->bytes(), data->size());
 #endif
         SkScriptRuntime runtime(fCallBackArray);
-        runtime.executeTokens((unsigned char*)data.bytes());
+        runtime.executeTokens((unsigned char*)data->bytes());
         runtime.getResult(&value1.fOperand);
         if (attributes->fResultIsBoolean == kResultIsBoolean)
             value1.fType = SkOperand2::kS32;
@@ -1195,7 +1195,7 @@
 bool SkScriptEngine2::ConvertTo(SkScriptEngine2* engine, SkOperand2::OpType toType, SkScriptValue2* value ) {
     SkASSERT(value);
     SkOperand2::OpType type = value->fType;
-    if (type == toType) 
+    if (type == toType)
         return true;
     SkOperand2& operand = value->fOperand;
     bool success = true;
@@ -1276,35 +1276,30 @@
 
 #define testInt(expression) { #expression, SkOperand2::kS32, expression, 0, NULL }
 #ifdef SK_SCALAR_IS_FLOAT
-#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) expression, NULL }
-#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf(exp1, exp2), NULL }
+#define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (float) (expression), NULL }
+#define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, fmodf((float) exp1, (float) exp2), NULL }
 #else
-#ifdef SK_CAN_USE_FLOAT
 #define testScalar(expression) { #expression, SkOperand2::kScalar, 0, (int) ((expression) * 65536.0f), NULL }
 #define testRemainder(exp1, exp2) { #exp1 "%" #exp2, SkOperand2::kScalar, 0, (int) (fmod(exp1, exp2)  * 65536.0f), NULL }
 #endif
-#endif
 #define testTrue(expression) { #expression, SkOperand2::kS32, 1, 0, NULL }
 #define testFalse(expression) { #expression, SkOperand2::kS32, 0, 0, NULL }
 
 static const SkScriptNAnswer2 scriptTests[]  = {
     testInt(1||(0&&3)),
-#ifdef SK_CAN_USE_FLOAT
     testScalar(- -5.5- -1.5),
-    testScalar(1.0+5), 
-#endif
+    testScalar(1.0+5),
     testInt((6+7)*8),
     testInt(3*(4+5)),
-#ifdef SK_CAN_USE_FLOAT
     testScalar(1.0+2.0),
-    testScalar(3.0-1.0), 
-    testScalar(6-1.0), 
-    testScalar(2.5*6.), 
-    testScalar(0.5*4), 
-    testScalar(4.5/.5), 
-    testScalar(9.5/19), 
-    testRemainder(9.5, 0.5), 
-    testRemainder(9.,2), 
+    testScalar(3.0-1.0),
+    testScalar(6-1.0),
+    testScalar(2.5*6.),
+    testScalar(0.5*4),
+    testScalar(4.5/.5),
+    testScalar(9.5/19),
+    testRemainder(9.5, 0.5),
+    testRemainder(9.,2),
     testRemainder(9,2.5),
     testRemainder(-9,2.5),
     testTrue(-9==-9.0),
@@ -1313,7 +1308,6 @@
     testFalse(-9!=-9.0),
     testFalse(-9.!=-4.0-5),
     testFalse(-9.*1!=-4-5),
-#endif
     testInt(0x123),
     testInt(0XABC),
     testInt(0xdeadBEEF),
@@ -1364,7 +1358,6 @@
     testInt(2!=2),
     testInt(2!=11),
     testInt(20!=11),
-#ifdef SK_CAN_USE_FLOAT
     // left int, right scalar
     testInt(2<2.),
     testInt(2<11.),
@@ -1421,7 +1414,6 @@
     testInt(2.!=2.),
     testInt(2.!=11.),
     testInt(20.!=11.),
-#endif
     // int, string (string is int)
     testFalse(2<'2'),
     testTrue(2<'11'),
@@ -1445,7 +1437,6 @@
     testFalse(20<'11.'),
     testTrue(2=='2.'),
     testFalse(2=='11.'),
-#ifdef SK_CAN_USE_FLOAT
     // scalar, string
     testFalse(2.<'2.'),
     testTrue(2.<'11.'),
@@ -1464,7 +1455,6 @@
     testFalse('20'<11.),
     testTrue('2'==2.),
     testFalse('2'==11.),
-#endif
     // string, string
     testFalse('2'<'2'),
     testFalse('2'<'11'),
@@ -1481,9 +1471,7 @@
     testInt(0||(0&&3)),
     testInt(0||(1&&3)),
     testInt(0&&1?2:3)
-#ifdef SK_CAN_USE_FLOAT
     , {    "123.5", SkOperand2::kScalar, 0, SkIntToScalar(123) + SK_Scalar1/2, NULL }
-#endif
 };
 
 #define SkScriptNAnswer_testCount    SK_ARRAY_COUNT(scriptTests)
@@ -1507,10 +1495,8 @@
                 break;
             case SkOperand2::kScalar:
                 error = SkScalarAbs(value.fOperand.fScalar - scriptTests[index].fScalarAnswer);
-#ifdef SK_CAN_USE_FLOAT
                 if (error >= SK_Scalar1 / 10000)
                     SkDEBUGF(("script '%s' == value %g != expected answer %g\n", script, value.fOperand.fScalar / (1.0f * SK_Scalar1), scriptTests[index].fScalarAnswer / (1.0f * SK_Scalar1)));
-#endif
                 SkASSERT(error < SK_Scalar1 / 10000);
                 break;
             case SkOperand2::kString:
diff --git a/src/animator/SkSnapshot.cpp b/src/animator/SkSnapshot.cpp
index 4e78bbd..a253d8e 100644
--- a/src/animator/SkSnapshot.cpp
+++ b/src/animator/SkSnapshot.cpp
@@ -61,4 +61,3 @@
                         SkScalarFloor(quality));
     return false;
 }
-
diff --git a/src/animator/SkSnapshot.h b/src/animator/SkSnapshot.h
index beb26ca..5ae6917 100644
--- a/src/animator/SkSnapshot.h
+++ b/src/animator/SkSnapshot.h
@@ -28,4 +28,3 @@
 };
 
 #endif // SkSnapShot_DEFINED
-
diff --git a/src/animator/SkTDArray_Experimental.h b/src/animator/SkTDArray_Experimental.h
index e598a6d..094d106 100644
--- a/src/animator/SkTDArray_Experimental.h
+++ b/src/animator/SkTDArray_Experimental.h
@@ -38,9 +38,9 @@
     int32_t* append() { return this->append(1, NULL); }
     int32_t* append(U16CPU count, const int32_t* src = NULL);
 
-    int32_t* appendClear() 
-    { 
-        int32_t* result = this->append(); 
+    int32_t* appendClear()
+    {
+        int32_t* result = this->append();
         *result = 0;
         return result;
     }
@@ -108,9 +108,9 @@
     SkTDS32Array(const SkTDS32Array<T>& src) : SkDS32Array(src) {}
     ~SkTDS32Array() { sk_free(fArray); }
     T&  operator[](int index) const { SYNC(); SkASSERT((unsigned)index < fCount); return ((T*) fArray)[index]; }
-    SkTDS32Array<T>& operator=(const SkTDS32Array<T>& src) { 
+    SkTDS32Array<T>& operator=(const SkTDS32Array<T>& src) {
         return (SkTDS32Array<T>&) SkDS32Array::operator=(src); }
-    friend int operator==(const SkTDS32Array<T>& a, const SkTDS32Array<T>& b) { 
+    friend int operator==(const SkTDS32Array<T>& a, const SkTDS32Array<T>& b) {
         return operator==((const SkDS32Array&) a, (const SkDS32Array&) b); }
     T* append() { return (T*) SkDS32Array::append(); }
     T* appendClear() { return (T*) SkDS32Array::appendClear(); }
diff --git a/src/animator/SkTextOnPath.cpp b/src/animator/SkTextOnPath.cpp
index e7f7651..7bdb7fd 100644
--- a/src/animator/SkTextOnPath.cpp
+++ b/src/animator/SkTextOnPath.cpp
@@ -33,7 +33,7 @@
     SkASSERT(text);
     SkASSERT(path);
     SkBoundableAuto boundable(this, maker);
-    maker.fCanvas->drawTextOnPathHV(text->getText(), text->getSize(), 
+    maker.fCanvas->drawTextOnPathHV(text->getText(), text->getSize(),
                                     path->getPath(), offset, 0, *maker.fPaint);
     return false;
 }
diff --git a/src/animator/SkTextToPath.cpp b/src/animator/SkTextToPath.cpp
index cf78ff5..0c1b0e4 100644
--- a/src/animator/SkTextToPath.cpp
+++ b/src/animator/SkTextToPath.cpp
@@ -42,7 +42,6 @@
     }
     SkPaint realPaint;
     paint->setupPaint(&realPaint);
-    realPaint.getTextPath(text->getText(), text->getSize(), text->x, 
+    realPaint.getTextPath(text->getText(), text->getSize(), text->x,
         text->y, &path->getPath());
 }
-
diff --git a/src/animator/SkTextToPath.h b/src/animator/SkTextToPath.h
index cb2a15e..ac44ad7 100644
--- a/src/animator/SkTextToPath.h
+++ b/src/animator/SkTextToPath.h
@@ -29,4 +29,3 @@
 };
 
 #endif // SkTextToPath_DEFINED
-
diff --git a/src/animator/SkTime.cpp b/src/animator/SkTime.cpp
index 35c7eb8..ffd6f38 100644
--- a/src/animator/SkTime.cpp
+++ b/src/animator/SkTime.cpp
@@ -51,7 +51,7 @@
     {
         tm      syst;
         time_t  tm;
-        
+
         time(&tm);
         localtime_r(&tm, &syst);
         t->fYear        = SkToU16(syst.tm_year);
@@ -78,4 +78,3 @@
 }
 
 #endif
-
diff --git a/src/animator/SkTypedArray.h b/src/animator/SkTypedArray.h
index 222c8df..e93b106 100644
--- a/src/animator/SkTypedArray.h
+++ b/src/animator/SkTypedArray.h
@@ -20,7 +20,7 @@
     bool getIndex(int index, SkOperand* operand);
     SkDisplayTypes getType() { return fType; }
     SkScriptEngine::SkOpType getOpType() { return SkScriptEngine::ToOpType(fType); }
-    void setType(SkDisplayTypes type) { 
+    void setType(SkDisplayTypes type) {
     //  SkASSERT(count() == 0);
         fType = type;
     }
diff --git a/src/animator/SkXMLAnimatorWriter.cpp b/src/animator/SkXMLAnimatorWriter.cpp
index 6d299b6..58f20f1 100644
--- a/src/animator/SkXMLAnimatorWriter.cpp
+++ b/src/animator/SkXMLAnimatorWriter.cpp
@@ -80,4 +80,3 @@
 }
 
 #endif
-
diff --git a/src/animator/SkXMLAnimatorWriter.h b/src/animator/SkXMLAnimatorWriter.h
index bd6ccf4..1b17f6f 100644
--- a/src/animator/SkXMLAnimatorWriter.h
+++ b/src/animator/SkXMLAnimatorWriter.h
@@ -31,4 +31,3 @@
 };
 
 #endif // SkXMLAnimatorWriter_DEFINED
-
diff --git a/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
index e76ab08..87121cf 100644
--- a/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
+++ b/src/core/ARGB32_Clamp_Bilinear_BitmapShader.h
@@ -48,16 +48,16 @@
                                        int srcRB, const SkFilterPtrProc* proc_table)
 {
     int ix = fx >> 16;
-    
+
     const SkPMColor *p00, *p01, *p10, *p11;
-    
+
     p00 = p01 = srcPixels + SkClampMax(ix, srcMaxX);
     if ((unsigned)ix < srcMaxX)
         p01 += 1;
 
     p10 = (const SkPMColor*)((const char*)p00 + srcRB);
     p11 = (const SkPMColor*)((const char*)p01 + srcRB);
-    
+
     SkFilterPtrProc proc = SkGetBilinearFilterPtrXProc(proc_table, fx);
     return proc(p00, p01, p10, p11);
 }
@@ -65,7 +65,7 @@
 void ARGB32_Clamp_Bilinear_BitmapShader::shadeSpan(int x, int y, SkPMColor dstC[], int count)
 {
     SkASSERT(count > 0);
-    
+
     unsigned srcScale = SkAlpha255To256(this->getPaintAlpha());
 
     const SkMatrix& inv = this->getTotalInverse();
@@ -175,4 +175,3 @@
         }
     }
 }
-
diff --git a/src/core/Sk64.cpp b/src/core/Sk64.cpp
index 0d74904..c530ed8 100644
--- a/src/core/Sk64.cpp
+++ b/src/core/Sk64.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,9 +5,8 @@
  * found in the LICENSE file.
  */
 
-
 #include "Sk64.h"
-#include "SkMath.h"
+#include "SkMathPriv.h"
 
 #define shift_left(hi, lo)          \
     hi = (hi << 1) | (lo >> 31);    \
@@ -360,4 +358,3 @@
     }
     return SkApplySign(result, sign);
 }
-
diff --git a/src/core/SkAAClip.cpp b/src/core/SkAAClip.cpp
index 64c2728..988e2d7 100644
--- a/src/core/SkAAClip.cpp
+++ b/src/core/SkAAClip.cpp
@@ -57,7 +57,7 @@
     int32_t fRefCnt;
     int32_t fRowCount;
     int32_t fDataSize;
-    
+
     YOffset* yoffsets() {
         return (YOffset*)((char*)this + sizeof(RunHead));
     }
@@ -135,9 +135,10 @@
         fDone = true;
         fTop = fBottom = clip.fBounds.fBottom;
         fData = NULL;
+        fStopYOff = NULL;
         return;
     }
-    
+
     const RunHead* head = clip.fRunHead;
     fCurrYOff = head->yoffsets();
     fStopYOff = fCurrYOff + head->fRowCount;
@@ -220,6 +221,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// Count the number of zeros on the left and right edges of the passed in
+// RLE row. If 'row' is all zeros return 'width' in both variables.
 static void count_left_right_zeros(const uint8_t* row, int width,
                                    int* leftZ, int* riteZ) {
     int zeros = 0;
@@ -236,6 +239,12 @@
     } while (width > 0);
     *leftZ = zeros;
 
+    if (0 == width) {
+        // this line is completely empty return 'width' in both variables
+        *riteZ = *leftZ;
+        return;
+    }
+
     zeros = 0;
     while (width > 0) {
         int n = row[0];
@@ -264,7 +273,7 @@
     const uint8_t data2[] = {  7, 0,     5, 0, 2, 0, 3, 0xFF };
     const uint8_t data3[] = {  0, 5,     5, 0xFF, 2, 0, 3, 0 };
     const uint8_t data4[] = {  2, 3,     2, 0, 5, 0xFF, 3, 0 };
-    const uint8_t data5[] = { 10, 0,     10, 0 };
+    const uint8_t data5[] = { 10, 10,    10, 0 };
     const uint8_t data6[] = {  2, 2,     2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
 
     const uint8_t* array[] = {
@@ -325,7 +334,7 @@
             SkASSERT(riteZ >= 0);
         } while (riteZ > 0);
     }
-    
+
     return trim;
 }
 
@@ -348,7 +357,7 @@
         return;
     }
     gOnce = true;
-    
+
     uint8_t data0[] = {  0, 0, 0,   10,    10, 0xFF };
     uint8_t data1[] = {  2, 0, 0,   10,    5, 0, 2, 0, 3, 0xFF };
     uint8_t data2[] = {  5, 0, 2,   10,    5, 0, 2, 0, 3, 0xFF };
@@ -360,13 +369,13 @@
     uint8_t data8[] = {  2, 2, 2,   10,    2, 0, 2, 0xFF, 2, 0, 2, 0xFF, 2, 0 };
     uint8_t data9[] = {  5, 2, 4,   10,    2, 0, 2, 0, 2, 0, 2, 0xFF, 2, 0 };
     uint8_t data10[] ={  74, 0, 4, 150,    9, 0, 65, 0, 76, 0xFF };
-    
+
     uint8_t* array[] = {
         data0, data1, data2, data3, data4,
         data5, data6, data7, data8, data9,
         data10
     };
-    
+
     for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
         uint8_t* data = array[i];
         const int trimL = *data++;
@@ -388,7 +397,7 @@
     if (this->isEmpty()) {
         return false;
     }
-    
+
     AUTO_AACLIP_VALIDATE(*this);
 
     const int width = fBounds.width();
@@ -397,11 +406,15 @@
     YOffset* stop = yoff + head->fRowCount;
     uint8_t* base = head->data();
 
+    // After this loop, 'leftZeros' & 'rightZeros' will contain the minimum
+    // number of zeros on the left and right of the clip. This information
+    // can be used to shrink the bounding box.
     int leftZeros = width;
     int riteZeros = width;
     while (yoff < stop) {
         int L, R;
         count_left_right_zeros(base + yoff->fOffset, width, &L, &R);
+        SkASSERT(L + R < width || (L == width && R == width));
         if (L < leftZeros) {
             leftZeros = L;
         }
@@ -416,7 +429,8 @@
     }
 
     SkASSERT(leftZeros || riteZeros);
-    if (width == (leftZeros + riteZeros)) {
+    if (width == leftZeros) {
+        SkASSERT(width == riteZeros);
         return this->setEmpty();
     }
 
@@ -500,7 +514,7 @@
         SkASSERT(!fBounds.isEmpty());
         head->fRowCount -= skip;
         SkASSERT(head->fRowCount > 0);
-        
+
         this->validate();
         // need to reset this after the memmove
         base = head->data();
@@ -607,10 +621,10 @@
     if (a.fBounds != b.fBounds) {
         return false;
     }
-    
+
     const SkAAClip::RunHead* ah = a.fRunHead;
     const SkAAClip::RunHead* bh = b.fRunHead;
-    
+
     // this catches empties and rects being equal
     if (ah == bh) {
         return true;
@@ -712,7 +726,7 @@
     }
     this->swap(clip);
     return !this->isEmpty();
-#else    
+#else
     const SkIRect& bounds = rgn.getBounds();
     const int offsetX = bounds.fLeft;
     const int offsetY = bounds.fTop;
@@ -809,7 +823,9 @@
     for (;;) {
         int n = data[0];
         if (x < n) {
-            *initialCount = n - x;
+            if (initialCount) {
+                *initialCount = n - x;
+            }
             break;
         }
         data += 2;
@@ -831,7 +847,7 @@
     }
 #endif
 
-    int lastY;
+    int lastY SK_INIT_TO_AVOID_WARNING;
     const uint8_t* row = this->findRow(top, &lastY);
     if (lastY < bottom) {
         return false;
@@ -893,10 +909,10 @@
         SkASSERT(count > 0);
         SkASSERT(fBounds.contains(x, y));
         SkASSERT(fBounds.contains(x + count - 1, y));
-        
+
         x -= fBounds.left();
         y -= fBounds.top();
-                             
+
         Row* row = fCurrRow;
         if (y != fPrevY) {
             SkASSERT(y > fPrevY);
@@ -935,7 +951,7 @@
         SkASSERT(y == fCurrRow->fY);
         fCurrRow->fY = y + height - 1;
     }
- 
+
     void addRectRun(int x, int y, int width, int height) {
         SkASSERT(fBounds.contains(x + width - 1, y + height - 1));
         this->addRun(x, y, 0xFF, width);
@@ -988,7 +1004,7 @@
         const Row* row = fRows.begin();
         const Row* stop = fRows.end();
 
-        size_t dataSize = 0;    
+        size_t dataSize = 0;
         while (row < stop) {
             dataSize += row->fData->count();
             row += 1;
@@ -1017,7 +1033,7 @@
             yoffset->fY = row->fY - adjustY;
             yoffset->fOffset = data - baseData;
             yoffset += 1;
-            
+
             size_t n = row->fData->count();
             memcpy(data, row->fData->begin(), n);
 #ifdef SK_DEBUG
@@ -1025,7 +1041,7 @@
             SkASSERT(bytesNeeded == n);
 #endif
             data += n;
-            
+
             row += 1;
         }
 
@@ -1055,6 +1071,9 @@
 
     void validate() {
 #ifdef SK_DEBUG
+        if (false) { // avoid bit rot, suppress warning
+            test_count_left_right_zeros();
+        }
         int prevY = -1;
         for (int i = 0; i < fRows.count(); ++i) {
             const Row& row = fRows[i];
@@ -1147,7 +1166,7 @@
     /*
         If we see a gap of 1 or more empty scanlines while building in Y-order,
         we inject an explicit empty scanline (alpha==0)
-     
+
         See AAClipTest.cpp : test_path_with_hole()
      */
     void checkForYGap(int y) {
@@ -1246,7 +1265,7 @@
                 localCount -= right - fRight;
                 SkASSERT(localCount >= 0);
             }
-            
+
             if (localCount) {
                 fBuilder->addRun(localX, y, *alpha, localCount);
             }
@@ -1296,7 +1315,7 @@
         tmpClip.setRect(ibounds);
         clip = &tmpClip;
     }
-    
+
     if (path.isInverseFillType()) {
         ibounds = clip->getBounds();
     } else {
@@ -1324,12 +1343,6 @@
                         const uint8_t* rowA, const SkIRect& rectA,
                         const uint8_t* rowB, const SkIRect& rectB);
 
-static void sectRowProc(SkAAClip::Builder& builder, int bottom,
-                        const uint8_t* rowA, const SkIRect& rectA,
-                        const uint8_t* rowB, const SkIRect& rectB) {
-    
-}
-
 typedef U8CPU (*AlphaProc)(U8CPU alphaA, U8CPU alphaB);
 
 static U8CPU sectAlphaProc(U8CPU alphaA, U8CPU alphaB) {
@@ -1434,6 +1447,7 @@
     }
 }
 
+#if 0 // UNUSED
 static bool intersect(int& min, int& max, int boundsMin, int boundsMax) {
     SkASSERT(min < max);
     SkASSERT(boundsMin < boundsMax);
@@ -1448,6 +1462,7 @@
     }
     return true;
 }
+#endif
 
 static void operatorX(SkAAClip::Builder& builder, int lastY,
                       RowIter& iterA, RowIter& iterB,
@@ -1463,7 +1478,7 @@
         U8CPU alphaA = 0;
         U8CPU alphaB = 0;
         int left, rite;
- 
+
         if (leftA < leftB) {
             left = leftA;
             alphaA = iterA.alpha();
@@ -1546,7 +1561,7 @@
             } else {
                 bot = topA = topB;
             }
-            
+
         } else if (topB < topA) {
             top = topB;
             rowB = iterB.data();
@@ -1588,14 +1603,14 @@
 bool SkAAClip::op(const SkAAClip& clipAOrig, const SkAAClip& clipBOrig,
                   SkRegion::Op op) {
     AUTO_AACLIP_VALIDATE(*this);
-    
+
     if (SkRegion::kReplace_Op == op) {
         return this->set(clipBOrig);
     }
-    
+
     const SkAAClip* clipA = &clipAOrig;
     const SkAAClip* clipB = &clipBOrig;
-    
+
     if (SkRegion::kReverseDifference_Op == op) {
         SkTSwap(clipA, clipB);
         op = SkRegion::kDifference_Op;
@@ -1615,14 +1630,14 @@
             }
             bounds = clipA->fBounds;
             break;
-            
+
         case SkRegion::kIntersect_Op:
             if ((a_empty | b_empty) || !bounds.intersect(clipA->fBounds,
                                                          clipB->fBounds)) {
                 return this->setEmpty();
             }
             break;
-            
+
         case SkRegion::kUnion_Op:
         case SkRegion::kXOR_Op:
             if (a_empty) {
@@ -1694,13 +1709,17 @@
 bool SkAAClip::op(const SkRect& rOrig, SkRegion::Op op, bool doAA) {
     SkRect        rStorage, boundsStorage;
     const SkRect* r = &rOrig;
-    
+
     boundsStorage.set(fBounds);
     switch (op) {
         case SkRegion::kIntersect_Op:
         case SkRegion::kDifference_Op:
             if (!rStorage.intersect(rOrig, boundsStorage)) {
-                return this->setEmpty();
+                if (SkRegion::kIntersect_Op == op) {
+                    return this->setEmpty();
+                } else {    // kDifference
+                    return !this->isEmpty();
+                }
             }
             r = &rStorage;   // use the intersected bounds
             break;
@@ -1712,7 +1731,7 @@
         default:
             break;
     }
-    
+
     SkAAClip clip;
     clip.setRect(*r, doAA);
     return this->op(*this, clip, op);
@@ -1728,13 +1747,14 @@
     if (NULL == dst) {
         return !this->isEmpty();
     }
-    
+
     if (this->isEmpty()) {
         return dst->setEmpty();
     }
-    
+
     if (this != dst) {
         sk_atomic_inc(&fRunHead->fRefCnt);
+        dst->freeRuns();
         dst->fRunHead = fRunHead;
         dst->fBounds = fBounds;
     }
@@ -1764,16 +1784,16 @@
         mask->fRowBytes = 0;
         return;
     }
-    
+
     mask->fBounds = fBounds;
     mask->fRowBytes = fBounds.width();
     size_t size = mask->computeImageSize();
     mask->fImage = SkMask::AllocImage(size);
-    
+
     Iter iter(*this);
     uint8_t* dst = mask->fImage;
     const int width = fBounds.width();
-    
+
     int y = fBounds.fTop;
     while (!iter.done()) {
         do {
@@ -1802,7 +1822,7 @@
 
         aa[0] = data[1];
         aa += n;
-        
+
         data += 2;
         width -= n;
         if (0 == width) {
@@ -1835,8 +1855,7 @@
     SkASSERT(fAAClipBounds.contains(x, y));
     SkASSERT(fAAClipBounds.contains(x + width  - 1, y));
 
-    int lastY;
-    const uint8_t* row = fAAClip->findRow(y, &lastY);
+    const uint8_t* row = fAAClip->findRow(y);
     int initialCount;
     row = fAAClip->findX(row, x, &initialCount);
 
@@ -1894,7 +1913,7 @@
             row += 2;
             rowN = row[0];  // reload
         }
-        
+
         SkDEBUGCODE(accumulated += minN;)
         SkASSERT(accumulated <= width);
     }
@@ -1903,8 +1922,8 @@
 
 void SkAAClipBlitter::blitAntiH(int x, int y, const SkAlpha aa[],
                                 const int16_t runs[]) {
-    int lastY;
-    const uint8_t* row = fAAClip->findRow(y, &lastY);
+
+    const uint8_t* row = fAAClip->findRow(y);
     int initialCount;
     row = fAAClip->findX(row, x, &initialCount);
 
@@ -1921,7 +1940,7 @@
     }
 
     for (;;) {
-        int lastY;
+        int lastY SK_INIT_TO_AVOID_WARNING;
         const uint8_t* row = fAAClip->findRow(y, &lastY);
         int dy = lastY - y + 1;
         if (dy > height) {
@@ -1929,8 +1948,7 @@
         }
         height -= dy;
 
-        int initialCount;
-        row = fAAClip->findX(row, x, &initialCount);
+        row = fAAClip->findX(row, x);
         SkAlpha newAlpha = SkMulDiv255Round(alpha, row[1]);
         if (newAlpha) {
             fBlitter->blitV(x, y, dy, newAlpha);
@@ -1974,8 +1992,8 @@
     unsigned g = SkGetPackedG16(value);
     unsigned b = SkGetPackedB16(value);
     return SkPackRGB16(SkMulDiv255Round(r, alpha),
-                       SkMulDiv255Round(r, alpha),
-                       SkMulDiv255Round(r, alpha));
+                       SkMulDiv255Round(g, alpha),
+                       SkMulDiv255Round(b, alpha));
 }
 static inline SkPMColor mergeOne(SkPMColor value, unsigned alpha) {
     unsigned a = SkGetPackedA32(value);
@@ -1991,11 +2009,10 @@
 template <typename T> void mergeT(const T* SK_RESTRICT src, int srcN,
                                  const uint8_t* SK_RESTRICT row, int rowN,
                                  T* SK_RESTRICT dst) {
-    SkDEBUGCODE(int accumulated = 0;)
     for (;;) {
         SkASSERT(rowN > 0);
         SkASSERT(srcN > 0);
-        
+
         int n = SkMin32(rowN, srcN);
         unsigned rowA = row[1];
         if (0xFF == rowA) {
@@ -2007,14 +2024,14 @@
                 dst[i] = mergeOne(src[i], rowA);
             }
         }
-        
+
         if (0 == (srcN -= n)) {
             break;
         }
-        
+
         src += n;
         dst += n;
-        
+
         SkASSERT(rowN == n);
         row += 2;
         rowN = row[0];
@@ -2139,7 +2156,7 @@
     const int stopY = y + clip.height();
 
     do {
-        int localStopY;
+        int localStopY SK_INIT_TO_AVOID_WARNING;
         const uint8_t* row = fAAClip->findRow(y, &localStopY);
         // findRow returns last Y, not stop, so we add 1
         localStopY = SkMin32(localStopY + 1, stopY);
@@ -2159,4 +2176,3 @@
 const SkBitmap* SkAAClipBlitter::justAnOpaqueColor(uint32_t* value) {
     return NULL;
 }
-
diff --git a/src/core/SkAAClip.h b/src/core/SkAAClip.h
index 6036427..f2cde62 100644
--- a/src/core/SkAAClip.h
+++ b/src/core/SkAAClip.h
@@ -55,14 +55,14 @@
     void copyToMask(SkMask*) const;
 
     // called internally
-    
+
     bool quickContains(int left, int top, int right, int bottom) const;
     bool quickContains(const SkIRect& r) const {
         return this->quickContains(r.fLeft, r.fTop, r.fRight, r.fBottom);
     }
 
-    const uint8_t* findRow(int y, int* lastYForRow) const;
-    const uint8_t* findX(const uint8_t data[], int x, int* initialCount) const;
+    const uint8_t* findRow(int y, int* lastYForRow = NULL) const;
+    const uint8_t* findX(const uint8_t data[], int x, int* initialCount = NULL) const;
 
     class Iter;
     struct RunHead;
@@ -102,7 +102,7 @@
         fAAClip = aaclip;
         fAAClipBounds = aaclip->getBounds();
     }
-    
+
     virtual void blitH(int x, int y, int width) SK_OVERRIDE;
     virtual void blitAntiH(int x, int y, const SkAlpha[],
                            const int16_t runs[]) SK_OVERRIDE;
@@ -110,7 +110,7 @@
     virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
     virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
     virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
-    
+
 private:
     SkBlitter*      fBlitter;
     const SkAAClip* fAAClip;
diff --git a/src/core/SkAdvancedTypefaceMetrics.cpp b/src/core/SkAdvancedTypefaceMetrics.cpp
index 8af70fb..b647ada 100644
--- a/src/core/SkAdvancedTypefaceMetrics.cpp
+++ b/src/core/SkAdvancedTypefaceMetrics.cpp
@@ -10,6 +10,12 @@
 #include "SkAdvancedTypefaceMetrics.h"
 #include "SkTypes.h"
 
+SK_DEFINE_INST_COUNT(SkAdvancedTypefaceMetrics)
+
+#if defined(SK_BUILD_FOR_WIN)
+#include <dwrite.h>
+#endif
+
 #if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
 #include <ft2build.h>
 #include FT_FREETYPE_H
@@ -40,12 +46,12 @@
 void stripUninterestingTrailingAdvancesFromRange<int16_t>(
                                                           SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* range) {
     SkASSERT(range);
-    
+
     int expectedAdvanceCount = range->fEndId - range->fStartId + 1;
     if (range->fAdvance.count() < expectedAdvanceCount) {
         return;
     }
-    
+
     for (int i = expectedAdvanceCount - 1; i >= 0; --i) {
         if (range->fAdvance[i] != kDontCareAdvance &&
             range->fAdvance[i] != kInvalidAdvance &&
@@ -86,7 +92,7 @@
         return;
     }
     SkASSERT(range->fAdvance.count() == range->fEndId - range->fStartId + 1);
-    
+
     // Zero out wildcards.
     for (int i = 0; i < range->fAdvance.count(); ++i) {
         if (range->fAdvance[i] == kDontCareAdvance) {
@@ -94,7 +100,7 @@
         }
     }
 }
-    
+
 template <typename Data>
 void finishRange(
         SkAdvancedTypefaceMetrics::AdvanceMetric<Data>* range,
@@ -258,6 +264,12 @@
         const uint32_t* subsetGlyphIDs,
         uint32_t subsetGlyphIDsLength,
         bool (*getAdvance)(HDC hdc, int gId, int16_t* data));
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        IDWriteFontFace* fontFace,
+        int num_glyphs,
+        const uint32_t* subsetGlyphIDs,
+        uint32_t subsetGlyphIDsLength,
+        bool (*getAdvance)(IDWriteFontFace* fontFace, int gId, int16_t* data));
 #elif defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
 template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
         FT_Face face,
@@ -296,4 +308,12 @@
         int endId,
         SkAdvancedTypefaceMetrics::VerticalAdvanceRange::MetricType type);
 
+// additional declaration needed for testing with a face of an unknown type
+template SkAdvancedTypefaceMetrics::WidthRange* getAdvanceData(
+        void* fontData,
+        int num_glyphs,
+        const uint32_t* subsetGlyphIDs,
+        uint32_t subsetGlyphIDsLength,
+        bool (*getAdvance)(void* fontData, int gId, int16_t* data));
+
 } // namespace skia_advanced_typeface_metrics_utils
diff --git a/src/core/SkAlphaRuns.cpp b/src/core/SkAlphaRuns.cpp
index 116d132..a80e9af 100644
--- a/src/core/SkAlphaRuns.cpp
+++ b/src/core/SkAlphaRuns.cpp
@@ -176,4 +176,3 @@
         SkASSERT(count == fWidth);
     }
 #endif
-
diff --git a/src/core/SkAnnotation.cpp b/src/core/SkAnnotation.cpp
new file mode 100644
index 0000000..5e4363e
--- /dev/null
+++ b/src/core/SkAnnotation.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "SkAnnotation.h"
+#include "SkDataSet.h"
+#include "SkFlattenableBuffers.h"
+#include "SkStream.h"
+
+SkAnnotation::SkAnnotation(SkDataSet* data, uint32_t flags) {
+    if (NULL == data) {
+        data = SkDataSet::NewEmpty();
+    } else {
+        data->ref();
+    }
+    fDataSet = data;
+    fFlags = flags;
+}
+
+SkAnnotation::~SkAnnotation() {
+    fDataSet->unref();
+}
+
+SkData* SkAnnotation::find(const char name[]) const {
+    return fDataSet->find(name);
+}
+
+SkAnnotation::SkAnnotation(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fFlags = buffer.readUInt();
+    fDataSet = buffer.readFlattenableT<SkDataSet>();
+}
+
+void SkAnnotation::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeUInt(fFlags);
+    buffer.writeFlattenable(fDataSet);
+}
+
+const char* SkAnnotationKeys::URL_Key() {
+    return "SkAnnotationKey_URL";
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkCanvas.h"
+
+void SkAnnotateRectWithURL(SkCanvas* canvas, const SkRect& rect, SkData* value) {
+    if (NULL == value) {
+        return;
+    }
+
+    const char* key = SkAnnotationKeys::URL_Key();
+    SkAutoTUnref<SkDataSet> dataset(SkNEW_ARGS(SkDataSet, (key, value)));
+    SkAnnotation* ann = SkNEW_ARGS(SkAnnotation, (dataset,
+                                                  SkAnnotation::kNoDraw_Flag));
+
+    SkPaint paint;
+    paint.setAnnotation(ann)->unref();
+    SkASSERT(paint.isNoDrawAnnotation());
+
+    canvas->drawRect(rect, paint);
+}
diff --git a/src/core/SkAntiRun.h b/src/core/SkAntiRun.h
index 56b5ee5..1239726 100644
--- a/src/core/SkAntiRun.h
+++ b/src/core/SkAntiRun.h
@@ -31,7 +31,7 @@
 
     /// Reinitialize for a new scanline.
     void    reset(int width);
-    
+
     /**
      *  Insert into the buffer a run starting at (x-offsetX):
      *      if startAlpha > 0
@@ -90,4 +90,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkAutoKern.h b/src/core/SkAutoKern.h
new file mode 100644
index 0000000..0b22e56
--- /dev/null
+++ b/src/core/SkAutoKern.h
@@ -0,0 +1,53 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkAutoKern_DEFINED
+#define SkAutoKern_DEFINED
+
+#include "SkGlyph.h"
+
+#define SkAutoKern_AdjustF(prev, next)    (((next) - (prev) + 32) >> 6 << 16)
+#define SkAutoKern_AdjustS(prev, next)    SkIntToScalar(((next) - (prev) + 32) >> 6)
+
+/* this is a helper class to perform auto-kerning
+ * the adjust() method returns a SkFixed corresponding
+ * to a +1/0/-1 pixel adjustment
+ */
+
+class SkAutoKern {
+public:
+    SkAutoKern() : fPrevRsbDelta(0) {}
+
+    SkFixed  adjust(const SkGlyph&  glyph)
+    {
+//        if (SkAbs32(glyph.fLsbDelta) > 47 || SkAbs32(glyph.fRsbDelta) > 47)
+//            printf("------- %d> L %d R %d\n", glyph.f_GlyphID, glyph.fLsbDelta, glyph.fRsbDelta);
+
+#if 0
+        int  distort = fPrevRsbDelta - glyph.fLsbDelta;
+
+        fPrevRsbDelta = glyph.fRsbDelta;
+
+        if (distort >= 32)
+            return -SK_Fixed1;
+        else if (distort < -32)
+            return +SK_Fixed1;
+        else
+            return 0;
+#else
+        SkFixed adjust = SkAutoKern_AdjustF(fPrevRsbDelta, glyph.fLsbDelta);
+        fPrevRsbDelta = glyph.fRsbDelta;
+        return adjust;
+#endif
+    }
+private:
+    int   fPrevRsbDelta;
+};
+
+#endif
diff --git a/src/core/SkBBoxHierarchy.cpp b/src/core/SkBBoxHierarchy.cpp
new file mode 100644
index 0000000..5232fb7
--- /dev/null
+++ b/src/core/SkBBoxHierarchy.cpp
@@ -0,0 +1,11 @@
+
+/*
+ * 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 "SkBBoxHierarchy.h"
+
+SK_DEFINE_INST_COUNT(SkBBoxHierarchy)
diff --git a/src/core/SkBBoxHierarchy.h b/src/core/SkBBoxHierarchy.h
new file mode 100644
index 0000000..4c8b2ae
--- /dev/null
+++ b/src/core/SkBBoxHierarchy.h
@@ -0,0 +1,56 @@
+
+/*
+ * 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 SkBBoxHierarchy_DEFINED
+#define SkBBoxHierarchy_DEFINED
+
+#include "SkRect.h"
+#include "SkTDArray.h"
+#include "SkRefCnt.h"
+
+/**
+ * Interface for a spatial data structure that associates user data pointers with axis-aligned
+ * bounding boxes, and allows efficient retrieval of intersections with query rectangles.
+ */
+class SkBBoxHierarchy : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkBBoxHierarchy)
+
+    /**
+     * Insert a data pointer and corresponding bounding box
+     * @param data The data pointer, may be NULL
+     * @param bounds The bounding box, should not be empty
+     * @param defer Whether or not it is acceptable to delay insertion of this element (building up
+     *        an entire spatial data structure at once is often faster and produces better
+     *        structures than repeated inserts) until flushDeferredInserts is called or the first
+     *        search.
+     */
+    virtual void insert(void* data, const SkIRect& bounds, bool defer = false) = 0;
+
+    /**
+     * If any insertions have been deferred, this forces them to be inserted
+     */
+    virtual void flushDeferredInserts() = 0;
+
+    /**
+     * Populate 'results' with data pointers corresponding to bounding boxes that intersect 'query'
+     */
+    virtual void search(const SkIRect& query, SkTDArray<void*>* results) = 0;
+
+    virtual void clear() = 0;
+
+    /**
+     * Gets the number of insertions
+     */
+    virtual int getCount() const = 0;
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBBoxHierarchyRecord.cpp b/src/core/SkBBoxHierarchyRecord.cpp
new file mode 100644
index 0000000..16172f3
--- /dev/null
+++ b/src/core/SkBBoxHierarchyRecord.cpp
@@ -0,0 +1,105 @@
+
+/*
+ * 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 "SkBBoxHierarchyRecord.h"
+#include "SkPictureStateTree.h"
+#include "SkBBoxHierarchy.h"
+
+SkBBoxHierarchyRecord::SkBBoxHierarchyRecord(uint32_t recordFlags,
+                                             SkBBoxHierarchy* h,
+                                             SkDevice* device)
+    : INHERITED(recordFlags, device) {
+    fStateTree = SkNEW(SkPictureStateTree);
+    fBoundingHierarchy = h;
+    fBoundingHierarchy->ref();
+}
+
+void SkBBoxHierarchyRecord::handleBBox(const SkRect& bounds) {
+    SkIRect r;
+    bounds.roundOut(&r);
+    SkPictureStateTree::Draw* draw = fStateTree->appendDraw(this->writeStream().size());
+    fBoundingHierarchy->insert(draw, r, true);
+}
+
+int SkBBoxHierarchyRecord::save(SaveFlags flags) {
+    fStateTree->appendSave();
+    return INHERITED::save(flags);
+}
+
+int SkBBoxHierarchyRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
+                                     SaveFlags flags) {
+    fStateTree->appendSaveLayer(this->writeStream().size());
+    return INHERITED::saveLayer(bounds, paint, flags);
+}
+
+void SkBBoxHierarchyRecord::restore() {
+    fStateTree->appendRestore();
+    INHERITED::restore();
+}
+
+bool SkBBoxHierarchyRecord::translate(SkScalar dx, SkScalar dy) {
+    bool result = INHERITED::translate(dx, dy);
+    fStateTree->appendTransform(getTotalMatrix());
+    return result;
+}
+
+bool SkBBoxHierarchyRecord::scale(SkScalar sx, SkScalar sy) {
+    bool result = INHERITED::scale(sx, sy);
+    fStateTree->appendTransform(getTotalMatrix());
+    return result;
+}
+
+bool SkBBoxHierarchyRecord::rotate(SkScalar degrees) {
+    bool result = INHERITED::rotate(degrees);
+    fStateTree->appendTransform(getTotalMatrix());
+    return result;
+}
+
+bool SkBBoxHierarchyRecord::skew(SkScalar sx, SkScalar sy) {
+    bool result = INHERITED::skew(sx, sy);
+    fStateTree->appendTransform(getTotalMatrix());
+    return result;
+}
+
+bool SkBBoxHierarchyRecord::concat(const SkMatrix& matrix) {
+    bool result = INHERITED::concat(matrix);
+    fStateTree->appendTransform(getTotalMatrix());
+    return result;
+}
+
+void SkBBoxHierarchyRecord::setMatrix(const SkMatrix& matrix) {
+    INHERITED::setMatrix(matrix);
+    fStateTree->appendTransform(getTotalMatrix());
+}
+
+bool SkBBoxHierarchyRecord::clipRect(const SkRect& rect,
+                                     SkRegion::Op op,
+                                     bool doAntiAlias) {
+    fStateTree->appendClip(this->writeStream().size());
+    return INHERITED::clipRect(rect, op, doAntiAlias);
+}
+
+bool SkBBoxHierarchyRecord::clipRegion(const SkRegion& region,
+                                       SkRegion::Op op) {
+    fStateTree->appendClip(this->writeStream().size());
+    return INHERITED::clipRegion(region, op);
+}
+
+bool SkBBoxHierarchyRecord::clipPath(const SkPath& path,
+                                     SkRegion::Op op,
+                                     bool doAntiAlias) {
+    fStateTree->appendClip(this->writeStream().size());
+    return INHERITED::clipPath(path, op, doAntiAlias);
+}
+
+bool SkBBoxHierarchyRecord::clipRRect(const SkRRect& rrect,
+                                      SkRegion::Op op,
+                                      bool doAntiAlias) {
+    fStateTree->appendClip(this->writeStream().size());
+    return INHERITED::clipRRect(rrect, op, doAntiAlias);
+}
diff --git a/src/core/SkBBoxHierarchyRecord.h b/src/core/SkBBoxHierarchyRecord.h
new file mode 100644
index 0000000..854e525
--- /dev/null
+++ b/src/core/SkBBoxHierarchyRecord.h
@@ -0,0 +1,54 @@
+
+/*
+ * 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 SkRTreeCanvas_DEFINED
+#define SkRTreeCanvas_DEFINED
+
+#include "SkBBoxRecord.h"
+
+/**
+ * This records bounding box information into an SkBBoxHierarchy, and clip/transform information
+ * into an SkPictureStateTree to allow for efficient culling and correct playback of draws.
+ */
+class SkBBoxHierarchyRecord : public SkBBoxRecord {
+public:
+    /** This will take a ref of h */
+    SkBBoxHierarchyRecord(uint32_t recordFlags, SkBBoxHierarchy* h,
+                          SkDevice*);
+
+    virtual void handleBBox(const SkRect& bounds) SK_OVERRIDE;
+
+    virtual int save(SaveFlags flags = kMatrixClip_SaveFlag) SK_OVERRIDE;
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags = kARGB_ClipLayer_SaveFlag) SK_OVERRIDE;
+    virtual void restore() SK_OVERRIDE;
+
+    virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE;
+    virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+    virtual bool rotate(SkScalar degrees) SK_OVERRIDE;
+    virtual bool skew(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+    virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
+    virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
+
+    virtual bool clipRect(const SkRect& rect,
+                          SkRegion::Op op = SkRegion::kIntersect_Op,
+                          bool doAntiAlias = false) SK_OVERRIDE;
+    virtual bool clipRegion(const SkRegion& region,
+                            SkRegion::Op op = SkRegion::kIntersect_Op) SK_OVERRIDE;
+    virtual bool clipPath(const SkPath& path,
+                          SkRegion::Op op = SkRegion::kIntersect_Op,
+                          bool doAntiAlias = false) SK_OVERRIDE;
+    virtual bool clipRRect(const SkRRect& rrect,
+                           SkRegion::Op op = SkRegion::kIntersect_Op,
+                           bool doAntiAlias = false) SK_OVERRIDE;
+
+private:
+    typedef SkBBoxRecord INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBBoxRecord.cpp b/src/core/SkBBoxRecord.cpp
new file mode 100644
index 0000000..52d599f
--- /dev/null
+++ b/src/core/SkBBoxRecord.cpp
@@ -0,0 +1,284 @@
+
+/*
+ * 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 "SkBBoxRecord.h"
+
+void SkBBoxRecord::drawOval(const SkRect& rect, const SkPaint& paint) {
+    if (this->transformBounds(rect, &paint)) {
+        INHERITED::drawOval(rect, paint);
+    }
+}
+
+void SkBBoxRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    if (this->transformBounds(rrect.rect(), &paint)) {
+        INHERITED::drawRRect(rrect, paint);
+    }
+}
+
+void SkBBoxRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
+    if (this->transformBounds(rect, &paint)) {
+        INHERITED::drawRect(rect, paint);
+    }
+}
+
+void SkBBoxRecord::drawPath(const SkPath& path, const SkPaint& paint) {
+    if (path.isInverseFillType()) {
+        // If path is inverse filled, use the current clip bounds as the
+        // path's device-space bounding box.
+        SkIRect clipBounds;
+        if (this->getClipDeviceBounds(&clipBounds)) {
+            this->handleBBox(SkRect::MakeFromIRect(clipBounds));
+            INHERITED::drawPath(path, paint);
+        }
+    } else if (this->transformBounds(path.getBounds(), &paint)) {
+        INHERITED::drawPath(path, paint);
+    }
+}
+
+void SkBBoxRecord::drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                              const SkPaint& paint) {
+    SkRect bbox;
+    bbox.set(pts, count);
+    // Small min width value, just to ensure hairline point bounding boxes aren't empty.
+    // Even though we know hairline primitives are drawn one pixel wide, we do not use a
+    // minimum of 1 because the playback scale factor is unknown at record time. Later
+    // outsets will take care of adding additional padding for antialiasing and rounding out
+    // to integer device coordinates, guaranteeing that the rasterized pixels will be included
+    // in the computed bounds.
+    // Note: The device coordinate outset in SkBBoxHierarchyRecord::handleBBox is currently
+    // done in the recording coordinate space, which is wrong.
+    // http://code.google.com/p/skia/issues/detail?id=1021
+    static const SkScalar kMinWidth = SkFloatToScalar(0.01f);
+    SkScalar halfStrokeWidth = SkMaxScalar(paint.getStrokeWidth(), kMinWidth) / 2;
+    bbox.outset(halfStrokeWidth, halfStrokeWidth);
+    if (this->transformBounds(bbox, &paint)) {
+        INHERITED::drawPoints(mode, count, pts, paint);
+    }
+}
+
+void SkBBoxRecord::drawPaint(const SkPaint& paint) {
+    SkRect bbox;
+    if (this->getClipBounds(&bbox)) {
+        if (this->transformBounds(bbox, &paint)) {
+            INHERITED::drawPaint(paint);
+        }
+    }
+}
+
+void SkBBoxRecord::clear(SkColor color) {
+    SkISize size = this->getDeviceSize();
+    SkRect bbox = {0, 0, SkIntToScalar(size.width()), SkIntToScalar(size.height())};
+    this->handleBBox(bbox);
+    INHERITED::clear(color);
+}
+
+void SkBBoxRecord::drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                            const SkPaint& paint) {
+    SkRect bbox;
+    paint.measureText(text, byteLength, &bbox);
+    SkPaint::FontMetrics metrics;
+    paint.getFontMetrics(&metrics);
+
+    // Vertical and aligned text need to be offset
+    if (paint.isVerticalText()) {
+        SkScalar h = bbox.fBottom - bbox.fTop;
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            bbox.fTop    -= h / 2;
+            bbox.fBottom -= h / 2;
+        }
+        // Pad top and bottom with max extents from FontMetrics
+        bbox.fBottom += metrics.fBottom;
+        bbox.fTop += metrics.fTop;
+    } else {
+        SkScalar w = bbox.fRight - bbox.fLeft;
+        if (paint.getTextAlign() == SkPaint::kCenter_Align) {
+            bbox.fLeft  -= w / 2;
+            bbox.fRight -= w / 2;
+        } else if (paint.getTextAlign() == SkPaint::kRight_Align) {
+            bbox.fLeft  -= w;
+            bbox.fRight -= w;
+        }
+        // Set vertical bounds to max extents from font metrics
+        bbox.fTop = metrics.fTop;
+        bbox.fBottom = metrics.fBottom;
+    }
+
+    // Pad horizontal bounds on each side by half of max vertical extents (this is sort of
+    // arbitrary, but seems to produce reasonable results, if there were a way of getting max
+    // glyph X-extents to pad by, that may be better here, but FontMetrics fXMin and fXMax seem
+    // incorrect on most platforms (too small in Linux, never even set in Windows).
+    SkScalar pad = (metrics.fBottom - metrics.fTop) / 2;
+    bbox.fLeft  -= pad;
+    bbox.fRight += pad;
+
+    bbox.fLeft += x;
+    bbox.fRight += x;
+    bbox.fTop += y;
+    bbox.fBottom += y;
+    if (this->transformBounds(bbox, &paint)) {
+        INHERITED::drawText(text, byteLength, x, y, paint);
+    }
+}
+
+void SkBBoxRecord::drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                              const SkPaint* paint) {
+    SkRect bbox = {left, top, left + bitmap.width(), top + bitmap.height()};
+    if (this->transformBounds(bbox, paint)) {
+        INHERITED::drawBitmap(bitmap, left, top, paint);
+    }
+}
+
+void SkBBoxRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                                  const SkRect& dst, const SkPaint* paint) {
+    if (this->transformBounds(dst, paint)) {
+        INHERITED::drawBitmapRectToRect(bitmap, src, dst, paint);
+    }
+}
+
+void SkBBoxRecord::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& mat,
+                                    const SkPaint* paint) {
+    SkMatrix m = mat;
+    SkRect bbox = {0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height())};
+    m.mapRect(&bbox);
+    if (this->transformBounds(bbox, paint)) {
+        INHERITED::drawBitmapMatrix(bitmap, mat, paint);
+    }
+}
+
+void SkBBoxRecord::drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                  const SkRect& dst, const SkPaint* paint) {
+    if (this->transformBounds(dst, paint)) {
+        INHERITED::drawBitmapNine(bitmap, center, dst, paint);
+    }
+}
+
+void SkBBoxRecord::drawPosText(const void* text, size_t byteLength,
+                               const SkPoint pos[], const SkPaint& paint) {
+    SkRect bbox;
+    bbox.set(pos, paint.countText(text, byteLength));
+    SkPaint::FontMetrics metrics;
+    paint.getFontMetrics(&metrics);
+    bbox.fTop += metrics.fTop;
+    bbox.fBottom += metrics.fBottom;
+
+    // pad on left and right by half of max vertical glyph extents
+    SkScalar pad = (metrics.fTop - metrics.fBottom) / 2;
+    bbox.fLeft += pad;
+    bbox.fRight -= pad;
+
+    if (this->transformBounds(bbox, &paint)) {
+        INHERITED::drawPosText(text, byteLength, pos, paint);
+    }
+}
+
+void SkBBoxRecord::drawPosTextH(const void* text, size_t byteLength, const SkScalar xpos[],
+                                SkScalar constY, const SkPaint& paint) {
+    SkRect bbox;
+    size_t numChars = paint.countText(text, byteLength);
+    if (numChars > 0) {
+        bbox.fLeft  = xpos[0];
+        bbox.fRight = xpos[numChars - 1];
+        // if we had a guarantee that these will be monotonically increasing, this could be sped up
+        for (size_t i = 1; i < numChars; ++i) {
+            if (xpos[i] < bbox.fLeft) {
+                bbox.fLeft = xpos[i];
+            }
+            if (xpos[i] > bbox.fRight) {
+                bbox.fRight = xpos[i];
+            }
+        }
+        SkPaint::FontMetrics metrics;
+        paint.getFontMetrics(&metrics);
+
+        // pad horizontally by max glyph height
+        SkScalar pad = (metrics.fTop - metrics.fBottom);
+        bbox.fLeft  += pad;
+        bbox.fRight -= pad;
+
+        bbox.fTop    = metrics.fTop + constY;
+        bbox.fBottom = metrics.fBottom + constY;
+        if (!this->transformBounds(bbox, &paint)) {
+            return;
+        }
+    }
+    INHERITED::drawPosTextH(text, byteLength, xpos, constY, paint);
+}
+
+void SkBBoxRecord::drawSprite(const SkBitmap& bitmap, int left, int top,
+                              const SkPaint* paint) {
+    SkRect bbox;
+    bbox.set(SkIRect::MakeXYWH(left, top, bitmap.width(), bitmap.height()));
+    this->handleBBox(bbox); // directly call handleBBox, matrix is ignored
+    INHERITED::drawSprite(bitmap, left, top, paint);
+}
+
+void SkBBoxRecord::drawTextOnPath(const void* text, size_t byteLength,
+                                  const SkPath& path, const SkMatrix* matrix,
+                                  const SkPaint& paint) {
+    SkRect bbox = path.getBounds();
+    SkPaint::FontMetrics metrics;
+    paint.getFontMetrics(&metrics);
+
+    // pad out all sides by the max glyph height above baseline
+    SkScalar pad = metrics.fTop;
+    bbox.fLeft += pad;
+    bbox.fRight -= pad;
+    bbox.fTop += pad;
+    bbox.fBottom -= pad;
+
+    if (this->transformBounds(bbox, &paint)) {
+        INHERITED::drawTextOnPath(text, byteLength, path, matrix, paint);
+    }
+}
+
+void SkBBoxRecord::drawVertices(VertexMode mode, int vertexCount,
+                                const SkPoint vertices[], const SkPoint texs[],
+                                const SkColor colors[], SkXfermode* xfer,
+                                const uint16_t indices[], int indexCount,
+                                const SkPaint& paint) {
+    SkRect bbox;
+    bbox.set(vertices, vertexCount);
+    if (this->transformBounds(bbox, &paint)) {
+        INHERITED::drawVertices(mode, vertexCount, vertices, texs,
+                                colors, xfer, indices, indexCount, paint);
+    }
+}
+
+void SkBBoxRecord::drawPicture(SkPicture& picture) {
+    if (picture.width() > 0 && picture.height() > 0 &&
+        this->transformBounds(SkRect::MakeWH(picture.width(), picture.height()), NULL)) {
+        INHERITED::drawPicture(picture);
+    }
+}
+
+bool SkBBoxRecord::transformBounds(const SkRect& bounds, const SkPaint* paint) {
+    SkRect outBounds = bounds;
+    outBounds.sort();
+
+    if (paint) {
+        // account for stroking, path effects, shadows, etc
+        if (paint->canComputeFastBounds()) {
+            SkRect temp;
+            outBounds = paint->computeFastBounds(outBounds, &temp);
+        } else {
+            // set bounds to current clip
+            if (!this->getClipBounds(&outBounds)) {
+                // current clip is empty
+                return false;
+            }
+        }
+    }
+
+    if (!outBounds.isEmpty() && !this->quickReject(outBounds)) {
+        this->getTotalMatrix().mapRect(&outBounds);
+        this->handleBBox(outBounds);
+        return true;
+    }
+
+    return false;
+}
diff --git a/src/core/SkBBoxRecord.h b/src/core/SkBBoxRecord.h
new file mode 100644
index 0000000..9f79671
--- /dev/null
+++ b/src/core/SkBBoxRecord.h
@@ -0,0 +1,78 @@
+
+/*
+ * 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 SkBBoxRecord_DEFINED
+#define SkBBoxRecord_DEFINED
+
+#include "SkPictureRecord.h"
+
+/**
+  * This is an abstract SkPictureRecord subclass that intercepts draw calls and computes an
+  * axis-aligned bounding box for each draw that it sees, subclasses implement handleBBox()
+  * which will be called every time we get a new bounding box.
+  */
+class SkBBoxRecord : public SkPictureRecord {
+public:
+
+    SkBBoxRecord(uint32_t recordFlags, SkDevice* device)
+            : INHERITED(recordFlags, device) { }
+    virtual ~SkBBoxRecord() { }
+
+    /**
+     * This is called each time we get a bounding box, it will be axis-aligned,
+     * in device coordinates, and expanded to include stroking, shadows, etc.
+     */
+    virtual void handleBBox(const SkRect& bbox) = 0;
+
+    virtual void drawOval(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRRect(const SkRRect& rrect, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawRect(const SkRect& rect, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPath(const SkPath& path, const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPoints(PointMode mode, size_t count, const SkPoint pts[],
+                            const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
+    virtual void clear(SkColor) SK_OVERRIDE;
+    virtual void drawText(const void* text, size_t byteLength, SkScalar x, SkScalar y,
+                          const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawBitmap(const SkBitmap& bitmap, SkScalar left, SkScalar top,
+                            const SkPaint* paint = NULL) SK_OVERRIDE;
+    virtual void drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                                const SkRect& dst, const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& mat,
+                                  const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                const SkRect& dst, const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawPosText(const void* text, size_t byteLength,
+                             const SkPoint pos[], const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPosTextH(const void* text, size_t byteLength,
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawSprite(const SkBitmap& bitmap, int left, int top,
+                            const SkPaint* paint) SK_OVERRIDE;
+    virtual void drawTextOnPath(const void* text, size_t byteLength,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawVertices(VertexMode mode, int vertexCount,
+                              const SkPoint vertices[], const SkPoint texs[],
+                              const SkColor colors[], SkXfermode* xfer,
+                              const uint16_t indices[], int indexCount,
+                              const SkPaint& paint) SK_OVERRIDE;
+    virtual void drawPicture(SkPicture& picture) SK_OVERRIDE;
+
+private:
+    /**
+     * Takes a bounding box in current canvas view space, accounts for stroking and effects, and
+     * computes an axis-aligned bounding box in device coordinates, then passes it to handleBBox()
+     * returns false if the draw is completely clipped out, and may safely be ignored.
+     **/
+    bool transformBounds(const SkRect& bounds, const SkPaint* paint);
+
+    typedef SkPictureRecord INHERITED;
+};
+
+#endif
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 0b98513..e5a202c 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -13,6 +13,8 @@
 #include "SkFlattenable.h"
 #include "SkMallocPixelRef.h"
 #include "SkMask.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
 #include "SkPixelRef.h"
 #include "SkThread.h"
 #include "SkUnPreMultiply.h"
@@ -20,7 +22,7 @@
 #include "SkPackBits.h"
 #include <new>
 
-extern int32_t SkNextPixelRefGenerationID();
+SK_DEFINE_INST_COUNT(SkBitmap::Allocator)
 
 static bool isPos32Bits(const Sk64& value) {
     return !value.isNeg() && value.is32();
@@ -137,7 +139,6 @@
     SkTSwap(fPixelLockCount, other.fPixelLockCount);
     SkTSwap(fMipMap, other.fMipMap);
     SkTSwap(fPixels, other.fPixels);
-    SkTSwap(fRawPixelGenerationID, other.fRawPixelGenerationID);
     SkTSwap(fRowBytes, other.fRowBytes);
     SkTSwap(fWidth, other.fWidth);
     SkTSwap(fHeight, other.fHeight);
@@ -252,6 +253,19 @@
     return (safeSize.is32() ? safeSize.get32() : 0);
 }
 
+void SkBitmap::getBounds(SkRect* bounds) const {
+    SkASSERT(bounds);
+    bounds->set(0, 0,
+                SkIntToScalar(fWidth), SkIntToScalar(fHeight));
+}
+
+void SkBitmap::getBounds(SkIRect* bounds) const {
+    SkASSERT(bounds);
+    bounds->set(0, 0, fWidth, fHeight);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 void SkBitmap::setConfig(Config c, int width, int height, int rowBytes) {
     this->freePixels();
 
@@ -344,18 +358,21 @@
 }
 
 bool SkBitmap::lockPixelsAreWritable() const {
-    if (fPixelRef) {
-        return fPixelRef->lockPixelsAreWritable();
-    } else {
-        return fPixels != NULL;
-    }
+    return (fPixelRef) ? fPixelRef->lockPixelsAreWritable() : false;
 }
 
 void SkBitmap::setPixels(void* p, SkColorTable* ctable) {
-    this->freePixels();
-    fPixels = p;
-    SkRefCnt_SafeAssign(fColorTable, ctable);
+    if (NULL == p) {
+        this->setPixelRef(NULL, 0);
+        return;
+    }
 
+    Sk64 size = this->getSize64();
+    SkASSERT(!size.isNeg() && size.is32());
+
+    this->setPixelRef(new SkMallocPixelRef(p, size.get32(), ctable, false))->unref();
+    // since we're already allocated, we lockPixels right away
+    this->lockPixels();
     SkDEBUGCODE(this->validate();)
 }
 
@@ -397,23 +414,13 @@
 }
 
 uint32_t SkBitmap::getGenerationID() const {
-    if (fPixelRef) {
-        return fPixelRef->getGenerationID();
-    } else {
-        SkASSERT(fPixels || !fRawPixelGenerationID);
-        if (fPixels && !fRawPixelGenerationID) {
-            fRawPixelGenerationID = SkNextPixelRefGenerationID();
-        }
-        return fRawPixelGenerationID;
-    }
+    return (fPixelRef) ? fPixelRef->getGenerationID() : 0;
 }
 
 void SkBitmap::notifyPixelsChanged() const {
     SkASSERT(!this->isImmutable());
     if (fPixelRef) {
         fPixelRef->notifyPixelsChanged();
-    } else {
-        fRawPixelGenerationID = 0; // will grab next ID in getGenerationID
     }
 }
 
@@ -457,7 +464,7 @@
     return ComputeSafeSize64(getConfig(), fWidth, fHeight, fRowBytes);
 }
 
-bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize, 
+bool SkBitmap::copyPixelsTo(void* const dst, size_t dstSize,
                             int dstRowBytes, bool preserveDstPad) const {
 
     if (dstRowBytes == -1)
@@ -505,9 +512,9 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkBitmap::isImmutable() const { 
+bool SkBitmap::isImmutable() const {
     return fPixelRef ? fPixelRef->isImmutable() :
-        fFlags & kImageIsImmutable_Flag; 
+        fFlags & kImageIsImmutable_Flag;
 }
 
 void SkBitmap::setImmutable() {
@@ -661,6 +668,81 @@
     return 0;
 }
 
+bool SkBitmap::ComputeIsOpaque(const SkBitmap& bm) {
+    SkAutoLockPixels alp(bm);
+    if (!bm.getPixels()) {
+        return false;
+    }
+
+    const int height = bm.height();
+    const int width = bm.width();
+
+    switch (bm.config()) {
+        case SkBitmap::kA1_Config: {
+            // TODO
+        } break;
+        case SkBitmap::kA8_Config: {
+            unsigned a = 0xFF;
+            for (int y = 0; y < height; ++y) {
+                const uint8_t* row = bm.getAddr8(0, y);
+                for (int x = 0; x < width; ++x) {
+                    a &= row[x];
+                }
+                if (0xFF != a) {
+                    return false;
+                }
+            }
+            return true;
+        } break;
+        case kRLE_Index8_Config:
+        case SkBitmap::kIndex8_Config: {
+            SkAutoLockColors alc(bm);
+            const SkPMColor* table = alc.colors();
+            if (!table) {
+                return false;
+            }
+            SkPMColor c = (SkPMColor)~0;
+            for (int i = bm.getColorTable()->count() - 1; i >= 0; --i) {
+                c &= table[i];
+            }
+            return 0xFF == SkGetPackedA32(c);
+        } break;
+        case SkBitmap::kRGB_565_Config:
+            return true;
+            break;
+        case SkBitmap::kARGB_4444_Config: {
+            unsigned c = 0xFFFF;
+            for (int y = 0; y < height; ++y) {
+                const SkPMColor16* row = bm.getAddr16(0, y);
+                for (int x = 0; x < width; ++x) {
+                    c &= row[x];
+                }
+                if (0xF != SkGetPackedA4444(c)) {
+                    return false;
+                }
+            }
+            return true;
+        } break;
+        case SkBitmap::kARGB_8888_Config: {
+            SkPMColor c = (SkPMColor)~0;
+            for (int y = 0; y < height; ++y) {
+                const SkPMColor* row = bm.getAddr32(0, y);
+                for (int x = 0; x < width; ++x) {
+                    c &= row[x];
+                }
+                if (0xFF != SkGetPackedA32(c)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        default:
+            break;
+    }
+    return false;
+}
+
+
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -746,10 +828,18 @@
 
 #define SUB_OFFSET_FAILURE  ((size_t)-1)
 
-static size_t getSubOffset(const SkBitmap& bm, int x, int y) {
-    SkASSERT((unsigned)x < (unsigned)bm.width());
-    SkASSERT((unsigned)y < (unsigned)bm.height());
+// Declare these non-static so they can be tested by GpuBitmapCopyTest.
+size_t getSubOffset(const SkBitmap& bm, int x, int y);
+bool getUpperLeftFromOffset(const SkBitmap& bm, int* x, int* y);
 
+/**
+ *  Based on the Config and rowBytes() of bm, return the offset into an SkPixelRef of the pixel at
+ *  (x, y).
+ *  Note that the SkPixelRef does not need to be set yet. deepCopyTo takes advantage of this fact.
+ *  Also note that (x, y) may be outside the range of (0 - width(), 0 - height()), so long as it is
+ *  within the bounds of the SkPixelRef being used.
+ */
+size_t getSubOffset(const SkBitmap& bm, int x, int y) {
     switch (bm.getConfig()) {
         case SkBitmap::kA8_Config:
         case SkBitmap:: kIndex8_Config:
@@ -773,10 +863,53 @@
     return y * bm.rowBytes() + x;
 }
 
+/**
+ *  Using the pixelRefOffset(), rowBytes(), and Config of bm, determine the (x, y) coordinate of the
+ *  upper left corner of bm relative to its SkPixelRef.
+ *  x and y must be non-NULL.
+ */
+bool getUpperLeftFromOffset(const SkBitmap& bm, int* x, int* y) {
+    SkASSERT(x != NULL && y != NULL);
+    const size_t offset = bm.pixelRefOffset();
+    if (0 == offset) {
+        *x = *y = 0;
+        return true;
+    }
+    // Use integer division to find the correct y position.
+    *y = offset / bm.rowBytes();
+    // The remainder will be the x position, after we reverse getSubOffset.
+    *x = offset % bm.rowBytes();
+    switch (bm.getConfig()) {
+        case SkBitmap::kA8_Config:
+            // Fall through.
+        case SkBitmap::kIndex8_Config:
+            // x is unmodified
+            break;
+
+        case SkBitmap::kRGB_565_Config:
+            // Fall through.
+        case SkBitmap::kARGB_4444_Config:
+            *x >>= 1;
+            break;
+
+        case SkBitmap::kARGB_8888_Config:
+            *x >>= 2;
+            break;
+
+        case SkBitmap::kNo_Config:
+            // Fall through.
+        case SkBitmap::kA1_Config:
+            // Fall through.
+        default:
+            return false;
+    }
+    return true;
+}
+
 bool SkBitmap::extractSubset(SkBitmap* result, const SkIRect& subset) const {
     SkDEBUGCODE(this->validate();)
 
-    if (NULL == result || (NULL == fPixelRef && NULL == fPixels)) {
+    if (NULL == result || NULL == fPixelRef) {
         return false;   // no src pixels
     }
 
@@ -786,6 +919,21 @@
         return false;   // r is empty (i.e. no intersection)
     }
 
+    if (fPixelRef->getTexture() != NULL) {
+        // Do a deep copy
+        SkPixelRef* pixelRef = fPixelRef->deepCopy(this->config(), &subset);
+        if (pixelRef != NULL) {
+            SkBitmap dst;
+            dst.setConfig(this->config(), subset.width(), subset.height());
+            dst.setIsVolatile(this->isVolatile());
+            dst.setIsOpaque(this->isOpaque());
+            dst.setPixelRef(pixelRef)->unref();
+            SkDEBUGCODE(dst.validate());
+            result->swap(dst);
+            return true;
+        }
+    }
+
     if (kRLE_Index8_Config == fConfig) {
         SkAutoLockPixels alp(*this);
         // don't call readyToDraw(), since we can operate w/o a colortable
@@ -814,6 +962,11 @@
         return true;
     }
 
+    // If the upper left of the rectangle was outside the bounds of this SkBitmap, we should have
+    // exited above.
+    SkASSERT(static_cast<unsigned>(r.fLeft) < static_cast<unsigned>(this->width()));
+    SkASSERT(static_cast<unsigned>(r.fTop) < static_cast<unsigned>(this->height()));
+
     size_t offset = getSubOffset(*this, r.fLeft, r.fTop);
     if (SUB_OFFSET_FAILURE == offset) {
         return false;   // config not supported
@@ -822,13 +975,11 @@
     SkBitmap dst;
     dst.setConfig(this->config(), r.width(), r.height(), this->rowBytes());
     dst.setIsVolatile(this->isVolatile());
+    dst.setIsOpaque(this->isOpaque());
 
     if (fPixelRef) {
         // share the pixelref with a custom offset
         dst.setPixelRef(fPixelRef, fPixelRefOffset + offset);
-    } else {
-        // share the pixels (owned by the caller)
-        dst.setPixels((char*)fPixels + offset, this->getColorTable());
     }
     SkDEBUGCODE(dst.validate();)
 
@@ -881,18 +1032,28 @@
     SkBitmap tmpSrc;
     const SkBitmap* src = this;
 
-    if (fPixelRef && fPixelRef->readPixels(&tmpSrc)) {
-        SkASSERT(tmpSrc.width() == this->width());
-        SkASSERT(tmpSrc.height() == this->height());
+    if (fPixelRef) {
+        SkIRect subset;
+        if (getUpperLeftFromOffset(*this, &subset.fLeft, &subset.fTop)) {
+            subset.fRight = subset.fLeft + fWidth;
+            subset.fBottom = subset.fTop + fHeight;
+            if (fPixelRef->readPixels(&tmpSrc, &subset)) {
+                SkASSERT(tmpSrc.width() == this->width());
+                SkASSERT(tmpSrc.height() == this->height());
 
-        // did we get lucky and we can just return tmpSrc?
-        if (tmpSrc.config() == dstConfig && NULL == alloc) {
-            dst->swap(tmpSrc);
-            return true;
+                // did we get lucky and we can just return tmpSrc?
+                if (tmpSrc.config() == dstConfig && NULL == alloc) {
+                    dst->swap(tmpSrc);
+                    if (dst->pixelRef() && this->config() == dstConfig) {
+                        dst->pixelRef()->fGenerationID = fPixelRef->getGenerationID();
+                    }
+                    return true;
+                }
+
+                // fall through to the raster case
+                src = &tmpSrc;
+            }
         }
-
-        // fall through to the raster case
-        src = &tmpSrc;
     }
 
     // we lock this now, since we may need its colortable
@@ -900,10 +1061,10 @@
     if (!src->readyToDraw()) {
         return false;
     }
-    
+
     SkBitmap tmpDst;
     tmpDst.setConfig(dstConfig, src->width(), src->height());
-    
+
     // allocate colortable if srcConfig == kIndex8_Config
     SkColorTable* ctable = (dstConfig == kIndex8_Config) ?
     new SkColorTable(*src->getColorTable()) : NULL;
@@ -911,18 +1072,21 @@
     if (!tmpDst.allocPixels(alloc, ctable)) {
         return false;
     }
-    
-    SkAutoLockPixels dstlock(tmpDst);
+
     if (!tmpDst.readyToDraw()) {
         // allocator/lock failed
         return false;
     }
-    
+
     /* do memcpy for the same configs cases, else use drawing
     */
     if (src->config() == dstConfig) {
         if (tmpDst.getSize() == src->getSize()) {
             memcpy(tmpDst.getPixels(), src->getPixels(), src->getSafeSize());
+            SkPixelRef* pixelRef = tmpDst.pixelRef();
+            if (pixelRef != NULL) {
+                pixelRef->fGenerationID = this->getGenerationID();
+            }
         } else {
             const char* srcP = reinterpret_cast<const char*>(src->getPixels());
             char* dstP = reinterpret_cast<char*>(tmpDst.getPixels());
@@ -937,7 +1101,7 @@
     } else {
         // if the src has alpha, we have to clear the dst first
         if (!src->isOpaque()) {
-            tmpDst.eraseColor(0);
+            tmpDst.eraseColor(SK_ColorTRANSPARENT);
         }
 
         SkCanvas canvas(tmpDst);
@@ -963,8 +1127,34 @@
     if (fPixelRef) {
         SkPixelRef* pixelRef = fPixelRef->deepCopy(dstConfig);
         if (pixelRef) {
-            dst->setConfig(dstConfig, fWidth, fHeight);
-            dst->setPixelRef(pixelRef)->unref();
+            uint32_t rowBytes;
+            if (dstConfig == fConfig) {
+                pixelRef->fGenerationID = fPixelRef->getGenerationID();
+                // Use the same rowBytes as the original.
+                rowBytes = fRowBytes;
+            } else {
+                // With the new config, an appropriate fRowBytes will be computed by setConfig.
+                rowBytes = 0;
+            }
+            dst->setConfig(dstConfig, fWidth, fHeight, rowBytes);
+
+            size_t pixelRefOffset;
+            if (0 == fPixelRefOffset || dstConfig == fConfig) {
+                // Use the same offset as the original.
+                pixelRefOffset = fPixelRefOffset;
+            } else {
+                // Find the correct offset in the new config. This needs to be done after calling
+                // setConfig so dst's fConfig and fRowBytes have been set properly.
+                int x, y;
+                if (!getUpperLeftFromOffset(*this, &x, &y)) {
+                    return false;
+                }
+                pixelRefOffset = getSubOffset(*dst, x, y);
+                if (SUB_OFFSET_FAILURE == pixelRefOffset) {
+                    return false;
+                }
+            }
+            dst->setPixelRef(pixelRef, pixelRefOffset)->unref();
             return true;
         }
     }
@@ -1149,7 +1339,7 @@
     uint8_t*    addr = (uint8_t*)mm->pixels();
     int         width = this->width();
     int         height = this->height();
-    unsigned    rowBytes = this->rowBytes();
+    unsigned    rowBytes;
     SkBitmap    dstBM;
 
     for (int i = 0; i < maxLevels; i++) {
@@ -1165,11 +1355,13 @@
         dstBM.setConfig(config, width, height, rowBytes);
         dstBM.setPixels(addr);
 
+        srcBM.lockPixels();
         for (int y = 0; y < height; y++) {
             for (int x = 0; x < width; x++) {
                 proc(&dstBM, x, y, srcBM);
             }
         }
+        srcBM.unlockPixels();
 
         srcBM = dstBM;
         addr += height * rowBytes;
@@ -1360,96 +1552,27 @@
 
 enum {
     SERIALIZE_PIXELTYPE_NONE,
-    SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE,
-    SERIALIZE_PIXELTYPE_RAW_NO_CTABLE,
-    SERIALIZE_PIXELTYPE_REF_DATA,
-    SERIALIZE_PIXELTYPE_REF_PTR,
+    SERIALIZE_PIXELTYPE_REF_DATA
 };
 
-static void writeString(SkFlattenableWriteBuffer& buffer, const char str[]) {
-    size_t len = strlen(str);
-    buffer.write32(len);
-    buffer.writePad(str, len);
-}
-
-static SkPixelRef::Factory deserialize_factory(SkFlattenableReadBuffer& buffer) {
-    size_t len = buffer.readInt();
-    SkAutoSMalloc<256> storage(len + 1);
-    char* str = (char*)storage.get();
-    buffer.read(str, len);
-    str[len] = 0;
-    return SkPixelRef::NameToFactory(str);
-}
-
-/*
-    It is tricky to know how much to flatten. If we don't have a pixelref (i.e.
-    we just have pixels, then we can only flatten the pixels, or write out an
-    empty bitmap.
-
-    With a pixelref, we still have the question of recognizing when two sitings
-    of the same pixelref are the same, and when they are different. Perhaps we
-    should look at the generationID and keep a record of that in some dictionary
-    associated with the buffer. SkGLTextureCache does this sort of thing to know
-    when to create a new texture.
-*/
 void SkBitmap::flatten(SkFlattenableWriteBuffer& buffer) const {
-    buffer.write32(fWidth);
-    buffer.write32(fHeight);
-    buffer.write32(fRowBytes);
-    buffer.write8(fConfig);
+    buffer.writeInt(fWidth);
+    buffer.writeInt(fHeight);
+    buffer.writeInt(fRowBytes);
+    buffer.writeInt(fConfig);
     buffer.writeBool(this->isOpaque());
 
-    /*  If we are called in this mode, then it is up to the caller to manage
-        the owner-counts on the pixelref, as we just record the ptr itself.
-    */
-    if (!buffer.persistBitmapPixels()) {
-        if (fPixelRef) {
-            buffer.write8(SERIALIZE_PIXELTYPE_REF_PTR);
-            buffer.write32(fPixelRefOffset);
-            buffer.writeRefCnt(fPixelRef);
-            return;
-        } else {
-            // we ignore the non-persist request, since we don't have a ref
-            // ... or we could just write an empty bitmap...
-            // (true) will write an empty bitmap, (false) will flatten the pix
-            if (true) {
-                buffer.write8(SERIALIZE_PIXELTYPE_NONE);
-                return;
-            }
-        }
-    }
-
     if (fPixelRef) {
-        SkPixelRef::Factory fact = fPixelRef->getFactory();
-        if (fact) {
-            const char* name = SkPixelRef::FactoryToName(fact);
-            if (name && *name) {
-                buffer.write8(SERIALIZE_PIXELTYPE_REF_DATA);
-                buffer.write32(fPixelRefOffset);
-                writeString(buffer, name);
-                fPixelRef->flatten(buffer);
-                return;
-            }
+        if (fPixelRef->getFactory()) {
+            buffer.writeInt(SERIALIZE_PIXELTYPE_REF_DATA);
+            buffer.writeUInt(fPixelRefOffset);
+            buffer.writeFlattenable(fPixelRef);
+            return;
         }
         // if we get here, we can't record the pixels
-        buffer.write8(SERIALIZE_PIXELTYPE_NONE);
-    } else if (fPixels) {
-        if (fColorTable) {
-            buffer.write8(SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE);
-            fColorTable->flatten(buffer);
-        } else {
-            buffer.write8(SERIALIZE_PIXELTYPE_RAW_NO_CTABLE);
-        }
-        buffer.writePad(fPixels, this->getSafeSize());
-        // There is no writeZeroPad() fcn, so write individual bytes.
-        if (this->getSize() > this->getSafeSize()) {
-            size_t deltaSize = this->getSize() - this->getSafeSize();
-            // Need aligned pointer to write into due to internal implementa-
-            // tion of SkWriter32.
-            memset(buffer.reserve(SkAlign4(deltaSize)), 0, deltaSize);
-        }
+        buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
     } else {
-        buffer.write8(SERIALIZE_PIXELTYPE_NONE);
+        buffer.writeInt(SERIALIZE_PIXELTYPE_NONE);
     }
 }
 
@@ -1459,46 +1582,19 @@
     int width = buffer.readInt();
     int height = buffer.readInt();
     int rowBytes = buffer.readInt();
-    int config = buffer.readU8();
+    int config = buffer.readInt();
 
     this->setConfig((Config)config, width, height, rowBytes);
     this->setIsOpaque(buffer.readBool());
 
-    int reftype = buffer.readU8();
+    int reftype = buffer.readInt();
     switch (reftype) {
-        case SERIALIZE_PIXELTYPE_REF_PTR: {
-            size_t offset = buffer.readU32();
-            SkPixelRef* pr = (SkPixelRef*)buffer.readRefCnt();
-            this->setPixelRef(pr, offset);
-            break;
-        }
         case SERIALIZE_PIXELTYPE_REF_DATA: {
-            size_t offset = buffer.readU32();
-            SkPixelRef::Factory fact = deserialize_factory(buffer);
-            SkPixelRef* pr = fact(buffer);
+            size_t offset = buffer.readUInt();
+            SkPixelRef* pr = buffer.readFlattenableT<SkPixelRef>();
             SkSafeUnref(this->setPixelRef(pr, offset));
             break;
         }
-        case SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE:
-        case SERIALIZE_PIXELTYPE_RAW_NO_CTABLE: {
-            SkColorTable* ctable = NULL;
-            if (SERIALIZE_PIXELTYPE_RAW_WITH_CTABLE == reftype) {
-                ctable = SkNEW_ARGS(SkColorTable, (buffer));
-            }
-            size_t size = this->getSize();
-            if (this->allocPixels(ctable)) {
-                this->lockPixels();
-                // Just read what we need.
-                buffer.read(this->getPixels(), this->getSafeSize());
-                // Keep aligned for subsequent reads.
-                buffer.skip(size - this->getSafeSize());
-                this->unlockPixels();
-            } else {
-                buffer.skip(size); // Still skip the full-sized buffer though.
-            }
-            SkSafeUnref(ctable);
-            break;
-        }
         case SERIALIZE_PIXELTYPE_NONE:
             break;
         default:
@@ -1525,7 +1621,7 @@
 void SkBitmap::validate() const {
     SkASSERT(fConfig < kConfigCount);
     SkASSERT(fRowBytes >= (unsigned)ComputeRowBytes((Config)fConfig, fWidth));
-    SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag));
+    SkASSERT(fFlags <= (kImageIsOpaque_Flag | kImageIsVolatile_Flag | kImageIsImmutable_Flag));
     SkASSERT(fPixelLockCount >= 0);
     SkASSERT(NULL == fColorTable || (unsigned)fColorTable->getRefCnt() < 10000);
     SkASSERT((uint8_t)ComputeBytesPerPixel((Config)fConfig) == fBytesPerPixel);
@@ -1542,3 +1638,43 @@
 #endif
 }
 #endif
+
+#ifdef SK_DEVELOPER
+void SkBitmap::toString(SkString* str) const {
+
+    static const char* gConfigNames[kConfigCount] = {
+        "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888", "RLE"
+    };
+
+    str->appendf("bitmap: ((%d, %d) %s", this->width(), this->height(),
+                 gConfigNames[this->config()]);
+
+    str->append(" (");
+    if (this->isOpaque()) {
+        str->append("opaque");
+    } else {
+        str->append("transparent");
+    }
+    if (this->isImmutable()) {
+        str->append(", immutable");
+    } else {
+        str->append(", not-immutable");
+    }
+    str->append(")");
+
+    SkPixelRef* pr = this->pixelRef();
+    if (NULL == pr) {
+        // show null or the explicit pixel address (rare)
+        str->appendf(" pixels:%p", this->getPixels());
+    } else {
+        const char* uri = pr->getURI();
+        if (NULL != uri) {
+            str->appendf(" uri:\"%s\"", uri);
+        } else {
+            str->appendf(" pixelref:%p", pr);
+        }
+    }
+
+    str->append(")");
+}
+#endif
diff --git a/src/core/SkBitmapHeap.cpp b/src/core/SkBitmapHeap.cpp
new file mode 100644
index 0000000..48194b1
--- /dev/null
+++ b/src/core/SkBitmapHeap.cpp
@@ -0,0 +1,403 @@
+
+/*
+ * 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 "SkBitmapHeap.h"
+
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkTSearch.h"
+
+SK_DEFINE_INST_COUNT(SkBitmapHeapReader)
+SK_DEFINE_INST_COUNT(SkBitmapHeap::ExternalStorage)
+
+SkBitmapHeapEntry::SkBitmapHeapEntry()
+    : fSlot(-1)
+    , fRefCount(0)
+    , fBytesAllocated(0) {
+}
+
+SkBitmapHeapEntry::~SkBitmapHeapEntry() {
+    SkASSERT(0 == fRefCount);
+}
+
+void SkBitmapHeapEntry::addReferences(int count) {
+    if (0 == fRefCount) {
+        // If there are no current owners then the heap manager
+        // will be the only one able to modify it, so it does not
+        // need to be an atomic operation.
+        fRefCount = count;
+    } else {
+        sk_atomic_add(&fRefCount, count);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkBitmapHeap::LookupEntry::Compare(const SkBitmapHeap::LookupEntry *a,
+                                       const SkBitmapHeap::LookupEntry *b) {
+    if (a->fGenerationId < b->fGenerationId) {
+        return -1;
+    } else if (a->fGenerationId > b->fGenerationId) {
+        return 1;
+    } else if (a->fPixelOffset < b->fPixelOffset) {
+        return -1;
+    } else if (a->fPixelOffset > b->fPixelOffset) {
+        return 1;
+    } else if (a->fWidth < b->fWidth) {
+        return -1;
+    } else if (a->fWidth > b->fWidth) {
+        return 1;
+    } else if (a->fHeight < b->fHeight) {
+        return -1;
+    } else if (a->fHeight > b->fHeight) {
+        return 1;
+    }
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBitmapHeap::SkBitmapHeap(int32_t preferredSize, int32_t ownerCount)
+    : INHERITED()
+    , fExternalStorage(NULL)
+    , fMostRecentlyUsed(NULL)
+    , fLeastRecentlyUsed(NULL)
+    , fPreferredCount(preferredSize)
+    , fOwnerCount(ownerCount)
+    , fBytesAllocated(0)
+    , fDeferAddingOwners(false) {
+}
+
+SkBitmapHeap::SkBitmapHeap(ExternalStorage* storage, int32_t preferredSize)
+    : INHERITED()
+    , fExternalStorage(storage)
+    , fMostRecentlyUsed(NULL)
+    , fLeastRecentlyUsed(NULL)
+    , fPreferredCount(preferredSize)
+    , fOwnerCount(IGNORE_OWNERS)
+    , fBytesAllocated(0)
+    , fDeferAddingOwners(false) {
+    SkSafeRef(storage);
+}
+
+SkBitmapHeap::~SkBitmapHeap() {
+    SkDEBUGCODE(
+    for (int i = 0; i < fStorage.count(); i++) {
+        bool unused = false;
+        for (int j = 0; j < fUnusedSlots.count(); j++) {
+            if (fUnusedSlots[j] == fStorage[i]->fSlot) {
+                unused = true;
+                break;
+            }
+        }
+        if (!unused) {
+            fBytesAllocated -= fStorage[i]->fBytesAllocated;
+        }
+    }
+    fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry));
+    )
+    SkASSERT(0 == fBytesAllocated);
+    fStorage.deleteAll();
+    SkSafeUnref(fExternalStorage);
+    fLookupTable.deleteAll();
+}
+
+SkTRefArray<SkBitmap>* SkBitmapHeap::extractBitmaps() const {
+    const int size = fStorage.count();
+    SkTRefArray<SkBitmap>* array = NULL;
+    if (size > 0) {
+        array = SkTRefArray<SkBitmap>::Create(size);
+        for (int i = 0; i < size; i++) {
+            // make a shallow copy of the bitmap
+            array->writableAt(i) = fStorage[i]->fBitmap;
+        }
+    }
+    return array;
+}
+
+void SkBitmapHeap::removeFromLRU(SkBitmapHeap::LookupEntry* entry) {
+    if (fMostRecentlyUsed == entry) {
+        fMostRecentlyUsed = entry->fLessRecentlyUsed;
+        if (NULL == fMostRecentlyUsed) {
+            SkASSERT(fLeastRecentlyUsed == entry);
+            fLeastRecentlyUsed = NULL;
+        } else {
+            fMostRecentlyUsed->fMoreRecentlyUsed = NULL;
+        }
+    } else {
+        // Remove entry from its prior place, and make sure to cover the hole.
+        if (fLeastRecentlyUsed == entry) {
+            SkASSERT(entry->fMoreRecentlyUsed != NULL);
+            fLeastRecentlyUsed = entry->fMoreRecentlyUsed;
+        }
+        // Since we have already considered the case where entry is the most recently used, it must
+        // have a more recently used at this point.
+        SkASSERT(entry->fMoreRecentlyUsed != NULL);
+        entry->fMoreRecentlyUsed->fLessRecentlyUsed = entry->fLessRecentlyUsed;
+
+        if (entry->fLessRecentlyUsed != NULL) {
+            SkASSERT(fLeastRecentlyUsed != entry);
+            entry->fLessRecentlyUsed->fMoreRecentlyUsed = entry->fMoreRecentlyUsed;
+        }
+    }
+    entry->fMoreRecentlyUsed = NULL;
+}
+
+void SkBitmapHeap::appendToLRU(SkBitmapHeap::LookupEntry* entry) {
+    if (fMostRecentlyUsed != NULL) {
+        SkASSERT(NULL == fMostRecentlyUsed->fMoreRecentlyUsed);
+        fMostRecentlyUsed->fMoreRecentlyUsed = entry;
+        entry->fLessRecentlyUsed = fMostRecentlyUsed;
+    }
+    fMostRecentlyUsed = entry;
+    if (NULL == fLeastRecentlyUsed) {
+        fLeastRecentlyUsed = entry;
+    }
+}
+
+// iterate through our LRU cache and try to find an entry to evict
+SkBitmapHeap::LookupEntry* SkBitmapHeap::findEntryToReplace(const SkBitmap& replacement) {
+    SkASSERT(fPreferredCount != UNLIMITED_SIZE);
+    SkASSERT(fStorage.count() >= fPreferredCount);
+
+    SkBitmapHeap::LookupEntry* iter = fLeastRecentlyUsed;
+    while (iter != NULL) {
+        SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot];
+        if (heapEntry->fRefCount > 0) {
+            // If the least recently used bitmap has not been unreferenced
+            // by its owner, then according to our LRU specifications a more
+            // recently used one can not have used all its references yet either.
+            return NULL;
+        }
+        if (replacement.getGenerationID() == iter->fGenerationId) {
+            // Do not replace a bitmap with a new one using the same
+            // pixel ref. Instead look for a different one that will
+            // potentially free up more space.
+            iter = iter->fMoreRecentlyUsed;
+        } else {
+            return iter;
+        }
+    }
+    return NULL;
+}
+
+size_t SkBitmapHeap::freeMemoryIfPossible(size_t bytesToFree) {
+    if (UNLIMITED_SIZE == fPreferredCount) {
+        return 0;
+    }
+    LookupEntry* iter = fLeastRecentlyUsed;
+    size_t origBytesAllocated = fBytesAllocated;
+    // Purge starting from LRU until a non-evictable bitmap is found or until
+    // everything is evicted.
+    while (iter != NULL) {
+        SkBitmapHeapEntry* heapEntry = fStorage[iter->fStorageSlot];
+        if (heapEntry->fRefCount > 0) {
+            break;
+        }
+        LookupEntry* next = iter->fMoreRecentlyUsed;
+        this->removeEntryFromLookupTable(iter);
+        // Free the pixel memory. removeEntryFromLookupTable already reduced
+        // fBytesAllocated properly.
+        heapEntry->fBitmap.reset();
+        // Add to list of unused slots which can be reused in the future.
+        fUnusedSlots.push(heapEntry->fSlot);
+        iter = next;
+        if (origBytesAllocated - fBytesAllocated >= bytesToFree) {
+            break;
+        }
+    }
+
+    if (fLeastRecentlyUsed != iter) {
+        // There was at least one eviction.
+        fLeastRecentlyUsed = iter;
+        if (NULL == fLeastRecentlyUsed) {
+            // Everything was evicted
+            fMostRecentlyUsed = NULL;
+            fBytesAllocated -= (fStorage.count() * sizeof(SkBitmapHeapEntry));
+            fStorage.deleteAll();
+            fUnusedSlots.reset();
+            SkASSERT(0 == fBytesAllocated);
+        } else {
+            fLeastRecentlyUsed->fLessRecentlyUsed = NULL;
+        }
+    }
+
+    return origBytesAllocated - fBytesAllocated;
+}
+
+int SkBitmapHeap::findInLookupTable(const LookupEntry& indexEntry, SkBitmapHeapEntry** entry) {
+    int index = SkTSearch<const LookupEntry>((const LookupEntry**)fLookupTable.begin(),
+                                             fLookupTable.count(),
+                                             &indexEntry, sizeof(void*), LookupEntry::Compare);
+
+    if (index < 0) {
+        // insert ourselves into the bitmapIndex
+        index = ~index;
+        *fLookupTable.insert(index) = SkNEW_ARGS(LookupEntry, (indexEntry));
+    } else if (entry != NULL) {
+        // populate the entry if needed
+        *entry = fStorage[fLookupTable[index]->fStorageSlot];
+    }
+
+    return index;
+}
+
+bool SkBitmapHeap::copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap) {
+    SkASSERT(!fExternalStorage);
+
+    // If the bitmap is mutable, we need to do a deep copy, since the
+    // caller may modify it afterwards.
+    if (originalBitmap.isImmutable()) {
+        copiedBitmap = originalBitmap;
+// TODO if we have the pixel ref in the heap we could pass it here to avoid a potential deep copy
+//    else if (sharedPixelRef != NULL) {
+//        copiedBitmap = orig;
+//        copiedBitmap.setPixelRef(sharedPixelRef, originalBitmap.pixelRefOffset());
+    } else if (originalBitmap.empty()) {
+        copiedBitmap.reset();
+    } else if (!originalBitmap.deepCopyTo(&copiedBitmap, originalBitmap.getConfig())) {
+        return false;
+    }
+    copiedBitmap.setImmutable();
+    return true;
+}
+
+int SkBitmapHeap::removeEntryFromLookupTable(LookupEntry* entry) {
+    // remove the bitmap index for the deleted entry
+    SkDEBUGCODE(int count = fLookupTable.count();)
+    int index = this->findInLookupTable(*entry, NULL);
+    // Verify that findInLookupTable found an existing entry rather than adding
+    // a new entry to the lookup table.
+    SkASSERT(count == fLookupTable.count());
+    fBytesAllocated -= fStorage[entry->fStorageSlot]->fBytesAllocated;
+    SkDELETE(fLookupTable[index]);
+    fLookupTable.remove(index);
+    return index;
+}
+
+int32_t SkBitmapHeap::insert(const SkBitmap& originalBitmap) {
+    SkBitmapHeapEntry* entry = NULL;
+    int searchIndex = this->findInLookupTable(LookupEntry(originalBitmap), &entry);
+
+    if (entry) {
+        // Already had a copy of the bitmap in the heap.
+        if (fOwnerCount != IGNORE_OWNERS) {
+            if (fDeferAddingOwners) {
+                *fDeferredEntries.append() = entry->fSlot;
+            } else {
+                entry->addReferences(fOwnerCount);
+            }
+        }
+        if (fPreferredCount != UNLIMITED_SIZE) {
+            LookupEntry* lookupEntry = fLookupTable[searchIndex];
+            if (lookupEntry != fMostRecentlyUsed) {
+                this->removeFromLRU(lookupEntry);
+                this->appendToLRU(lookupEntry);
+            }
+        }
+        return entry->fSlot;
+    }
+
+    // decide if we need to evict an existing heap entry or create a new one
+    if (fPreferredCount != UNLIMITED_SIZE && fStorage.count() >= fPreferredCount) {
+        // iterate through our LRU cache and try to find an entry to evict
+        LookupEntry* lookupEntry = this->findEntryToReplace(originalBitmap);
+        if (lookupEntry != NULL) {
+            // we found an entry to evict
+            entry = fStorage[lookupEntry->fStorageSlot];
+            // Remove it from the LRU. The new entry will be added to the LRU later.
+            this->removeFromLRU(lookupEntry);
+            int index = this->removeEntryFromLookupTable(lookupEntry);
+
+            // update the current search index now that we have removed one
+            if (index < searchIndex) {
+                searchIndex--;
+            }
+        }
+    }
+
+    // if we didn't have an entry yet we need to create one
+    if (!entry) {
+        if (fPreferredCount != UNLIMITED_SIZE && fUnusedSlots.count() > 0) {
+            int slot;
+            fUnusedSlots.pop(&slot);
+            entry = fStorage[slot];
+        } else {
+            entry = SkNEW(SkBitmapHeapEntry);
+            fStorage.append(1, &entry);
+            entry->fSlot = fStorage.count() - 1;
+            fBytesAllocated += sizeof(SkBitmapHeapEntry);
+        }
+    }
+
+    // create a copy of the bitmap
+    bool copySucceeded;
+    if (fExternalStorage) {
+        copySucceeded = fExternalStorage->insert(originalBitmap, entry->fSlot);
+    } else {
+        copySucceeded = copyBitmap(originalBitmap, entry->fBitmap);
+    }
+
+    // if the copy failed then we must abort
+    if (!copySucceeded) {
+        // delete the index
+        SkDELETE(fLookupTable[searchIndex]);
+        fLookupTable.remove(searchIndex);
+        // If entry is the last slot in storage, it is safe to delete it.
+        if (fStorage.count() - 1 == entry->fSlot) {
+            // free the slot
+            fStorage.remove(entry->fSlot);
+            fBytesAllocated -= sizeof(SkBitmapHeapEntry);
+            SkDELETE(entry);
+        } else {
+            fUnusedSlots.push(entry->fSlot);
+        }
+        return INVALID_SLOT;
+    }
+
+    // update the index with the appropriate slot in the heap
+    fLookupTable[searchIndex]->fStorageSlot = entry->fSlot;
+
+    // compute the space taken by this entry
+    // TODO if there is a shared pixel ref don't count it
+    // If the SkBitmap does not share an SkPixelRef with an SkBitmap already
+    // in the SharedHeap, also include the size of its pixels.
+    entry->fBytesAllocated = originalBitmap.getSize();
+
+    // add the bytes from this entry to the total count
+    fBytesAllocated += entry->fBytesAllocated;
+
+    if (fOwnerCount != IGNORE_OWNERS) {
+        if (fDeferAddingOwners) {
+            *fDeferredEntries.append() = entry->fSlot;
+        } else {
+            entry->addReferences(fOwnerCount);
+        }
+    }
+    if (fPreferredCount != UNLIMITED_SIZE) {
+        this->appendToLRU(fLookupTable[searchIndex]);
+    }
+    return entry->fSlot;
+}
+
+void SkBitmapHeap::deferAddingOwners() {
+    fDeferAddingOwners = true;
+}
+
+void SkBitmapHeap::endAddingOwnersDeferral(bool add) {
+    if (add) {
+        for (int i = 0; i < fDeferredEntries.count(); i++) {
+            SkASSERT(fOwnerCount != IGNORE_OWNERS);
+            SkBitmapHeapEntry* heapEntry = this->getEntry(fDeferredEntries[i]);
+            SkASSERT(heapEntry != NULL);
+            heapEntry->addReferences(fOwnerCount);
+        }
+    }
+    fDeferAddingOwners = false;
+    fDeferredEntries.reset();
+}
diff --git a/src/core/SkBitmapHeap.h b/src/core/SkBitmapHeap.h
new file mode 100644
index 0000000..be99e19
--- /dev/null
+++ b/src/core/SkBitmapHeap.h
@@ -0,0 +1,307 @@
+
+/*
+ * 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 SkBitmapHeap_DEFINED
+#define SkBitmapHeap_DEFINED
+
+#include "SkBitmap.h"
+#include "SkFlattenable.h"
+#include "SkRefCnt.h"
+#include "SkTDArray.h"
+#include "SkThread.h"
+#include "SkTRefArray.h"
+
+/**
+ * SkBitmapHeapEntry provides users of SkBitmapHeap (using internal storage) with a means to...
+ *  (1) get access a bitmap in the heap
+ *  (2) indicate they are done with bitmap by releasing their reference (if they were an owner).
+ */
+class SkBitmapHeapEntry : SkNoncopyable {
+public:
+    ~SkBitmapHeapEntry();
+
+    int32_t getSlot() { return fSlot; }
+
+    SkBitmap* getBitmap() { return &fBitmap; }
+
+    void releaseRef() {
+        sk_atomic_dec(&fRefCount);
+    }
+
+private:
+    SkBitmapHeapEntry();
+
+    void addReferences(int count);
+
+    int32_t fSlot;
+    int32_t fRefCount;
+
+    SkBitmap fBitmap;
+    // Keep track of the bytes allocated for this bitmap. When replacing the
+    // bitmap or removing this HeapEntry we know how much memory has been
+    // reclaimed.
+    size_t fBytesAllocated;
+
+    friend class SkBitmapHeap;
+    friend class SkBitmapHeapTester;
+};
+
+
+class SkBitmapHeapReader : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkBitmapHeapReader)
+
+    SkBitmapHeapReader() : INHERITED() {}
+    virtual SkBitmap* getBitmap(int32_t slot) const = 0;
+    virtual void releaseRef(int32_t slot) = 0;
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+
+/**
+ * TODO: stores immutable bitmaps into a heap
+ */
+class SkBitmapHeap : public SkBitmapHeapReader {
+public:
+    class ExternalStorage : public SkRefCnt {
+     public:
+        SK_DECLARE_INST_COUNT(ExternalStorage)
+
+        virtual bool insert(const SkBitmap& bitmap, int32_t slot) = 0;
+
+     private:
+        typedef SkRefCnt INHERITED;
+    };
+
+    static const int32_t UNLIMITED_SIZE = -1;
+    static const int32_t IGNORE_OWNERS  = -1;
+    static const int32_t INVALID_SLOT   = -1;
+
+    /**
+     * Constructs a heap that is responsible for allocating and managing its own storage.  In the
+     * case where we choose to allow the heap to grow indefinitely (i.e. UNLIMITED_SIZE) we
+     * guarantee that once allocated in the heap a bitmap's index in the heap is immutable.
+     * Otherwise we guarantee the bitmaps placement in the heap until its owner count goes to zero.
+     *
+     * @param preferredSize  Specifies the preferred maximum number of bitmaps to store. This is
+     *   not a hard limit as it can grow larger if the number of bitmaps in the heap with active
+     *   owners exceeds this limit.
+     * @param ownerCount  The number of owners to assign to each inserted bitmap. NOTE: while a
+     *   bitmap in the heap has a least one owner it can't be removed.
+     */
+    SkBitmapHeap(int32_t preferredSize = UNLIMITED_SIZE, int32_t ownerCount = IGNORE_OWNERS);
+
+    /**
+     * Constructs a heap that defers the responsibility of storing the bitmaps to an external
+     * function. This is especially useful if the bitmaps will be used in a separate process as the
+     * external storage can ensure the data is properly shuttled to the appropriate processes.
+     *
+     * Our LRU implementation assumes that inserts into the external storage are consumed in the
+     * order that they are inserted (i.e. SkPipe). This ensures that we don't need to query the
+     * external storage to see if a slot in the heap is eligible to be overwritten.
+     *
+     * @param externalStorage  The class responsible for storing the bitmaps inserted into the heap
+     * @param heapSize  The maximum size of the heap. Because of the sequential limitation imposed
+     *   by our LRU implementation we can guarantee that the heap will never grow beyond this size.
+     */
+    SkBitmapHeap(ExternalStorage* externalStorage, int32_t heapSize = UNLIMITED_SIZE);
+
+    ~SkBitmapHeap();
+
+    /**
+     * Makes a shallow copy of all bitmaps currently in the heap and returns them as an array. The
+     * array indices match their position in the heap.
+     *
+     * @return  a ptr to an array of bitmaps or NULL if external storage is being used.
+     */
+    SkTRefArray<SkBitmap>* extractBitmaps() const;
+
+    /**
+     * Retrieves the bitmap from the specified slot in the heap
+     *
+     * @return  The bitmap located at that slot or NULL if external storage is being used.
+     */
+    virtual SkBitmap* getBitmap(int32_t slot) const SK_OVERRIDE {
+        SkASSERT(fExternalStorage == NULL);
+        SkBitmapHeapEntry* entry = getEntry(slot);
+        if (entry) {
+            return &entry->fBitmap;
+        }
+        return NULL;
+    }
+
+    /**
+     * Retrieves the bitmap from the specified slot in the heap
+     *
+     * @return  The bitmap located at that slot or NULL if external storage is being used.
+     */
+    virtual void releaseRef(int32_t slot) SK_OVERRIDE {
+        SkASSERT(fExternalStorage == NULL);
+        if (fOwnerCount != IGNORE_OWNERS) {
+            SkBitmapHeapEntry* entry = getEntry(slot);
+            if (entry) {
+                entry->releaseRef();
+            }
+        }
+    }
+
+    /**
+     * Inserts a bitmap into the heap. The stored version of bitmap is guaranteed to be immutable
+     * and is not dependent on the lifecycle of the provided bitmap.
+     *
+     * @param bitmap  the bitmap to be inserted into the heap
+     * @return  the slot in the heap where the bitmap is stored or INVALID_SLOT if the bitmap could
+     *          not be added to the heap. If it was added the slot will remain valid...
+     *            (1) indefinitely if no owner count has been specified.
+     *            (2) until all owners have called releaseRef on the appropriate SkBitmapHeapEntry*
+     */
+    int32_t insert(const SkBitmap& bitmap);
+
+    /**
+     * Retrieves an entry from the heap at a given slot.
+     *
+     * @param slot  the slot in the heap where a bitmap was stored.
+     * @return  a SkBitmapHeapEntry that wraps the bitmap or NULL if external storage is used.
+     */
+    SkBitmapHeapEntry* getEntry(int32_t slot) const {
+        SkASSERT(slot <= fStorage.count());
+        if (fExternalStorage != NULL) {
+            return NULL;
+        }
+        return fStorage[slot];
+    }
+
+    /**
+     * Returns a count of the number of items currently in the heap
+     */
+    int count() const {
+        SkASSERT(fExternalStorage != NULL ||
+                 fStorage.count() - fUnusedSlots.count() == fLookupTable.count());
+        return fLookupTable.count();
+    }
+
+    /**
+     * Returns the total number of bytes allocated by the bitmaps in the heap
+     */
+    size_t bytesAllocated() const {
+        return fBytesAllocated;
+    }
+
+    /**
+     * Attempt to reduce the storage allocated.
+     * @param bytesToFree minimum number of bytes that should be attempted to
+     *   be freed.
+     * @return number of bytes actually freed.
+     */
+    size_t freeMemoryIfPossible(size_t bytesToFree);
+
+    /**
+     * Defer any increments of owner counts until endAddingOwnersDeferral is called. So if an
+     * existing SkBitmap is inserted into the SkBitmapHeap, its corresponding SkBitmapHeapEntry will
+     * not have addReferences called on it, and the client does not need to make a corresponding
+     * call to releaseRef. Only meaningful if this SkBitmapHeap was created with an owner count not
+     * equal to IGNORE_OWNERS.
+     */
+    void deferAddingOwners();
+
+    /**
+     * Resume adding references when duplicate SkBitmaps are inserted.
+     * @param add If true, add references to the SkBitmapHeapEntrys whose SkBitmaps were re-inserted
+     *            while deferring.
+     */
+    void endAddingOwnersDeferral(bool add);
+
+private:
+    struct LookupEntry {
+        LookupEntry(const SkBitmap& bm)
+        : fGenerationId(bm.getGenerationID())
+        , fPixelOffset(bm.pixelRefOffset())
+        , fWidth(bm.width())
+        , fHeight(bm.height())
+        , fMoreRecentlyUsed(NULL)
+        , fLessRecentlyUsed(NULL){}
+
+        const uint32_t fGenerationId; // SkPixelRef GenerationID.
+        const size_t   fPixelOffset;
+        const uint32_t fWidth;
+        const uint32_t fHeight;
+
+        // TODO: Generalize the LRU caching mechanism
+        LookupEntry* fMoreRecentlyUsed;
+        LookupEntry* fLessRecentlyUsed;
+
+        uint32_t fStorageSlot; // slot of corresponding bitmap in fStorage.
+
+        /**
+         * Compare two LookupEntry pointers, returning -1, 0, 1 for sorting.
+         */
+        static int Compare(const LookupEntry* a, const LookupEntry* b);
+    };
+
+    /**
+     * Remove the entry from the lookup table. Also deletes the entry pointed
+     * to by the table. Therefore, if a pointer to that one was passed in, the
+     * pointer should no longer be used, since the object to which it points has
+     * been deleted.
+     * @return The index in the lookup table of the entry before removal.
+     */
+    int removeEntryFromLookupTable(LookupEntry*);
+
+    /**
+     * Searches for the bitmap in the lookup table and returns the bitmaps index within the table.
+     * If the bitmap was not already in the table it is added.
+     *
+     * @param key    The key to search the lookup table, created from a bitmap.
+     * @param entry  A pointer to a SkBitmapHeapEntry* that if non-null AND the bitmap is found
+     *               in the lookup table is populated with the entry from the heap storage.
+     */
+    int findInLookupTable(const LookupEntry& key, SkBitmapHeapEntry** entry);
+
+    LookupEntry* findEntryToReplace(const SkBitmap& replacement);
+    bool copyBitmap(const SkBitmap& originalBitmap, SkBitmap& copiedBitmap);
+
+    /**
+     * Remove a LookupEntry from the LRU, in preparation for either deleting or appending as most
+     * recent. Points the LookupEntry's old neighbors at each other, and sets fLeastRecentlyUsed
+     * (if there is still an entry left). Sets LookupEntry's fMoreRecentlyUsed to NULL and leaves
+     * its fLessRecentlyUsed unmodified.
+     */
+    void removeFromLRU(LookupEntry* entry);
+
+    /**
+     * Append a LookupEntry to the end of the LRU cache, marking it as the most
+     * recently used. Assumes that the LookupEntry is already in fLookupTable,
+     * but is not in the LRU cache. If it is in the cache, removeFromLRU should
+     * be called first.
+     */
+    void appendToLRU(LookupEntry*);
+
+    // searchable index that maps to entries in the heap
+    SkTDArray<LookupEntry*> fLookupTable;
+
+    // heap storage
+    SkTDArray<SkBitmapHeapEntry*> fStorage;
+    // Used to mark slots in fStorage as deleted without actually deleting
+    // the slot so as not to mess up the numbering.
+    SkTDArray<int> fUnusedSlots;
+    ExternalStorage* fExternalStorage;
+
+    LookupEntry* fMostRecentlyUsed;
+    LookupEntry* fLeastRecentlyUsed;
+
+    const int32_t fPreferredCount;
+    const int32_t fOwnerCount;
+    size_t fBytesAllocated;
+
+    bool fDeferAddingOwners;
+    SkTDArray<int> fDeferredEntries;
+
+    typedef SkBitmapHeapReader INHERITED;
+};
+
+#endif // SkBitmapHeap_DEFINED
diff --git a/src/core/SkBitmapProcShader.cpp b/src/core/SkBitmapProcShader.cpp
index 6d64716..96cfea6 100644
--- a/src/core/SkBitmapProcShader.cpp
+++ b/src/core/SkBitmapProcShader.cpp
@@ -7,6 +7,7 @@
  */
 #include "SkBitmapProcShader.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
 #include "SkPixelRef.h"
 
 bool SkBitmapProcShader::CanDo(const SkBitmap& bm, TileMode tx, TileMode ty) {
@@ -33,28 +34,16 @@
 
 SkBitmapProcShader::SkBitmapProcShader(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {
-    fRawBitmap.unflatten(buffer);
-    fState.fTileModeX = buffer.readU8();
-    fState.fTileModeY = buffer.readU8();
+    buffer.readBitmap(&fRawBitmap);
+    fRawBitmap.setImmutable();
+    fState.fTileModeX = buffer.readUInt();
+    fState.fTileModeY = buffer.readUInt();
     fFlags = 0; // computed in setContext
 }
 
-void SkBitmapProcShader::beginSession() {
-    this->INHERITED::beginSession();
-
-    fRawBitmap.lockPixels();
-}
-
-void SkBitmapProcShader::endSession() {
-    fRawBitmap.unlockPixels();
-
-    this->INHERITED::endSession();
-}
-
 SkShader::BitmapType SkBitmapProcShader::asABitmap(SkBitmap* texture,
                                                    SkMatrix* texM,
-                                                   TileMode xy[],
-                                       SkScalar* twoPointRadialParams) const {
+                                                   TileMode xy[]) const {
     if (texture) {
         *texture = fRawBitmap;
     }
@@ -68,12 +57,12 @@
     return kDefault_BitmapType;
 }
 
-void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkBitmapProcShader::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    fRawBitmap.flatten(buffer);
-    buffer.write8(fState.fTileModeX);
-    buffer.write8(fState.fTileModeY);
+    buffer.writeBitmap(fRawBitmap);
+    buffer.writeUInt(fState.fTileModeX);
+    buffer.writeUInt(fState.fTileModeY);
 }
 
 static bool only_scale_and_translate(const SkMatrix& matrix) {
@@ -97,10 +86,13 @@
     fState.fOrigBitmap.lockPixels();
     if (!fState.fOrigBitmap.getTexture() && !fState.fOrigBitmap.readyToDraw()) {
         fState.fOrigBitmap.unlockPixels();
+        this->INHERITED::endContext();
         return false;
     }
 
     if (!fState.chooseProcs(this->getTotalInverse(), paint)) {
+        fState.fOrigBitmap.unlockPixels();
+        this->INHERITED::endContext();
         return false;
     }
 
@@ -135,7 +127,7 @@
         flags &= ~kHasSpan16_Flag;
     }
 
-    // if we're only 1-pixel heigh, and we don't rotate, then we can claim this
+    // if we're only 1-pixel high, and we don't rotate, then we can claim this
     if (1 == bitmap.height() &&
             only_scale_and_translate(this->getTotalInverse())) {
         flags |= kConstInY32_Flag;
@@ -148,6 +140,11 @@
     return true;
 }
 
+void SkBitmapProcShader::endContext() {
+    fState.fOrigBitmap.unlockPixels();
+    this->INHERITED::endContext();
+}
+
 #define BUF_MAX     128
 
 #define TEST_BUFFER_OVERRITEx
@@ -161,14 +158,14 @@
 
 void SkBitmapProcShader::shadeSpan(int x, int y, SkPMColor dstC[], int count) {
     const SkBitmapProcState& state = fState;
-    if (state.fShaderProc32) {
-        state.fShaderProc32(state, x, y, dstC, count);
+    if (state.getShaderProc32()) {
+        state.getShaderProc32()(state, x, y, dstC, count);
         return;
     }
 
     uint32_t buffer[BUF_MAX + TEST_BUFFER_EXTRA];
-    SkBitmapProcState::MatrixProc   mproc = state.fMatrixProc;
-    SkBitmapProcState::SampleProc32 sproc = state.fSampleProc32;
+    SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
+    SkBitmapProcState::SampleProc32 sproc = state.getSampleProc32();
     int max = fState.maxCountForBufferSize(sizeof(buffer[0]) * BUF_MAX);
 
     SkASSERT(state.fBitmap->getPixels());
@@ -203,16 +200,24 @@
     }
 }
 
+SkShader::ShadeProc SkBitmapProcShader::asAShadeProc(void** ctx) {
+    if (fState.getShaderProc32()) {
+        *ctx = &fState;
+        return (ShadeProc)fState.getShaderProc32();
+    }
+    return NULL;
+}
+
 void SkBitmapProcShader::shadeSpan16(int x, int y, uint16_t dstC[], int count) {
     const SkBitmapProcState& state = fState;
-    if (state.fShaderProc16) {
-        state.fShaderProc16(state, x, y, dstC, count);
+    if (state.getShaderProc16()) {
+        state.getShaderProc16()(state, x, y, dstC, count);
         return;
     }
 
     uint32_t buffer[BUF_MAX];
-    SkBitmapProcState::MatrixProc   mproc = state.fMatrixProc;
-    SkBitmapProcState::SampleProc16 sproc = state.fSampleProc16;
+    SkBitmapProcState::MatrixProc   mproc = state.getMatrixProc();
+    SkBitmapProcState::SampleProc16 sproc = state.getSampleProc16();
     int max = fState.maxCountForBufferSize(sizeof(buffer));
 
     SkASSERT(state.fBitmap->getPixels());
@@ -271,12 +276,22 @@
 
 #include "SkTemplatesPriv.h"
 
+static bool bitmapIsTooBig(const SkBitmap& bm) {
+    // SkBitmapProcShader stores bitmap coordinates in a 16bit buffer, as it
+    // communicates between its matrix-proc and its sampler-proc. Until we can
+    // widen that, we have to reject bitmaps that are larger.
+    //
+    const int maxSize = 65535;
+
+    return bm.width() > maxSize || bm.height() > maxSize;
+}
+
 SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
                                        TileMode tmx, TileMode tmy,
                                        void* storage, size_t storageSize) {
     SkShader* shader;
     SkColor color;
-    if (src.isNull()) {
+    if (src.isNull() || bitmapIsTooBig(src)) {
         SK_PLACEMENT_NEW(shader, SkEmptyShader, storage, storageSize);
     }
     else if (canUseColorShader(src, &color)) {
@@ -289,41 +304,64 @@
     return shader;
 }
 
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkBitmapProcShader)
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEVELOPER
+void SkBitmapProcShader::toString(SkString* str) const {
+    static const char* gTileModeName[SkShader::kTileModeCount] = {
+        "clamp", "repeat", "mirror"
+    };
+
+    str->append("BitmapShader: (");
+
+    str->appendf("(%s, %s)",
+                 gTileModeName[fState.fTileModeX],
+                 gTileModeName[fState.fTileModeY]);
+
+    str->append(" ");
+    fRawBitmap.toString(str);
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static const char* gTileModeName[] = {
-    "clamp", "repeat", "mirror"
-};
+#if SK_SUPPORT_GPU
 
-bool SkBitmapProcShader::toDumpString(SkString* str) const {
-    str->printf("BitmapShader: [%d %d %d",
-                fRawBitmap.width(), fRawBitmap.height(),
-                fRawBitmap.bytesPerPixel());
+#include "GrTextureAccess.h"
+#include "effects/GrSimpleTextureEffect.h"
+#include "SkGr.h"
 
-    // add the pixelref
-    SkPixelRef* pr = fRawBitmap.pixelRef();
-    if (pr) {
-        const char* uri = pr->getURI();
-        if (uri) {
-            str->appendf(" \"%s\"", uri);
+GrEffectRef* SkBitmapProcShader::asNewEffect(GrContext* context, const SkPaint& paint) const {
+    SkMatrix matrix;
+    matrix.setIDiv(fRawBitmap.width(), fRawBitmap.height());
+
+    if (this->hasLocalMatrix()) {
+        SkMatrix inverse;
+        if (!this->getLocalMatrix().invert(&inverse)) {
+            return NULL;
         }
+        matrix.preConcat(inverse);
+    }
+    SkShader::TileMode tm[] = {
+        (TileMode)fState.fTileModeX,
+        (TileMode)fState.fTileModeY,
+    };
+
+    // Must set wrap and filter on the sampler before requesting a texture.
+    GrTextureParams params(tm, paint.isFilterBitmap());
+    GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, fRawBitmap, &params);
+
+    if (NULL == texture) {
+        SkDebugf("Couldn't convert bitmap to texture.\n");
+        return NULL;
     }
 
-    // add the (optional) matrix
-    {
-        SkMatrix m;
-        if (this->getLocalMatrix(&m)) {
-            SkString info;
-            m.toDumpString(&info);
-            str->appendf(" %s", info.c_str());
-        }
-    }
-
-    str->appendf(" [%s %s]]",
-                 gTileModeName[fState.fTileModeX],
-                 gTileModeName[fState.fTileModeY]);
-    return true;
+    GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
+    GrUnlockAndUnrefCachedBitmapTexture(texture);
+    return effect;
 }
-
+#endif
diff --git a/src/core/SkBitmapProcShader.h b/src/core/SkBitmapProcShader.h
index 4f8d0c5..5a599c3 100644
--- a/src/core/SkBitmapProcShader.h
+++ b/src/core/SkBitmapProcShader.h
@@ -19,29 +19,26 @@
 
     // overrides from SkShader
     virtual bool isOpaque() const SK_OVERRIDE;
-    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&);
-    virtual uint32_t getFlags() { return fFlags; }
-    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
-    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count);
-    virtual void beginSession();
-    virtual void endSession();
-    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
-                                 SkScalar* twoPointRadialParams) const;
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
+    virtual void endContext() SK_OVERRIDE;
+    virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+    virtual ShadeProc asAShadeProc(void** ctx) SK_OVERRIDE;
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
 
     static bool CanDo(const SkBitmap&, TileMode tx, TileMode ty);
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkBitmapProcShader, (buffer));
-    }
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBitmapProcShader)
 
-    // override from flattenable
-    virtual bool toDumpString(SkString* str) const;
+#if SK_SUPPORT_GPU
+    GrEffectRef* asNewEffect(GrContext*, const SkPaint&) const SK_OVERRIDE;
+#endif
 
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
 protected:
     SkBitmapProcShader(SkFlattenableReadBuffer& );
-    virtual void flatten(SkFlattenableWriteBuffer& );
-    virtual Factory getFactory() { return CreateProc; }
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
 
     SkBitmap          fRawBitmap;   // experimental for RLE encoding
     SkBitmapProcState fState;
diff --git a/src/core/SkBitmapProcState.cpp b/src/core/SkBitmapProcState.cpp
index 3d34b20..97ffef5 100644
--- a/src/core/SkBitmapProcState.cpp
+++ b/src/core/SkBitmapProcState.cpp
@@ -6,339 +6,72 @@
  * found in the LICENSE file.
  */
 #include "SkBitmapProcState.h"
-#include "SkBitmapProcState_filter.h"
 #include "SkColorPriv.h"
 #include "SkFilterProc.h"
 #include "SkPaint.h"
 #include "SkShader.h"   // for tilemodes
+#include "SkUtilsArm.h"
 
-// returns expanded * 5bits
-static inline uint32_t Filter_565_Expanded(unsigned x, unsigned y,
-                                           uint32_t a00, uint32_t a01,
-                                           uint32_t a10, uint32_t a11) {
-    SkASSERT((unsigned)x <= 0xF);
-    SkASSERT((unsigned)y <= 0xF);
-    
-    a00 = SkExpand_rgb_16(a00);
-    a01 = SkExpand_rgb_16(a01);
-    a10 = SkExpand_rgb_16(a10);
-    a11 = SkExpand_rgb_16(a11);
-    
-    int xy = x * y >> 3;
-    return  a00 * (32 - 2*y - 2*x + xy) +
-            a01 * (2*x - xy) +
-            a10 * (2*y - xy) +
-            a11 * xy;
-}
+#if !SK_ARM_NEON_IS_NONE
+// These are defined in src/opts/SkBitmapProcState_arm_neon.cpp
+extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
+extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
+extern void  S16_D16_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, uint16_t*);
+extern void  Clamp_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
+extern void  Repeat_S16_D16_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint16_t*, int);
+extern void  SI8_opaque_D32_filter_DX_neon(const SkBitmapProcState&, const uint32_t*, int, SkPMColor*);
+extern void  SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
+extern void  Clamp_SI8_opaque_D32_filter_DX_shaderproc_neon(const SkBitmapProcState&, int, int, uint32_t*, int);
+#endif
 
-// turn an expanded 565 * 5bits into SkPMColor
-// g:11 | r:10 | x:1 | b:10
-static inline SkPMColor SkExpanded_565_To_PMColor(uint32_t c) {
-    unsigned r = (c >> 13) & 0xFF;
-    unsigned g = (c >> 24);
-    unsigned b = (c >> 2) & 0xFF;
-    return SkPackARGB32(0xFF, r, g, b);
-}
-
-// returns answer in SkPMColor format
-static inline SkPMColor Filter_4444_D32(unsigned x, unsigned y,
-                                        uint32_t a00, uint32_t a01,
-                                        uint32_t a10, uint32_t a11) {
-    SkASSERT((unsigned)x <= 0xF);
-    SkASSERT((unsigned)y <= 0xF);
-    
-    a00 = SkExpand_4444(a00);
-    a01 = SkExpand_4444(a01);
-    a10 = SkExpand_4444(a10);
-    a11 = SkExpand_4444(a11);
-
-    int xy = x * y >> 4;
-    uint32_t result =   a00 * (16 - y - x + xy) +
-                        a01 * (x - xy) +
-                        a10 * (y - xy) +
-                        a11 * xy;
-
-    return SkCompact_8888(result);
-}
-
-static inline U8CPU Filter_8(unsigned x, unsigned y,
-                             U8CPU a00, U8CPU a01,
-                             U8CPU a10, U8CPU a11) {
-    SkASSERT((unsigned)x <= 0xF);
-    SkASSERT((unsigned)y <= 0xF);
-    
-    int xy = x * y;
-    unsigned result =   a00 * (256 - 16*y - 16*x + xy) +
-                        a01 * (16*x - xy) +
-                        a10 * (16*y - xy) +
-                        a11 * xy;
-    
-    return result >> 8;
-}
-
-/*****************************************************************************
- *
- *  D32 functions
- *
- */
-
-// SRC == 8888
-
-#define FILTER_PROC(x, y, a, b, c, d, dst)   Filter_32_opaque(x, y, a, b, c, d, dst)
-
-#define MAKENAME(suffix)        S32_opaque_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 SkPMColor
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
-                                SkASSERT(state.fAlphaScale == 256)
-#define RETURNDST(src)          src
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst)   Filter_32_alpha(x, y, a, b, c, d, dst, alphaScale)
-
-#define MAKENAME(suffix)        S32_alpha_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 SkPMColor
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
-                                SkASSERT(state.fAlphaScale < 256)
-#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale
-#define RETURNDST(src)          SkAlphaMulQ(src, alphaScale)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-// SRC == 565
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst) \
-    do {                                                        \
-        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
-        *(dst) = SkExpanded_565_To_PMColor(tmp);                \
-    } while (0)
-
-#define MAKENAME(suffix)        S16_opaque_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 uint16_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
-                                SkASSERT(state.fAlphaScale == 256)
-#define RETURNDST(src)          SkPixel16ToPixel32(src)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst) \
-    do {                                                                    \
-        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);               \
-        *(dst) = SkAlphaMulQ(SkExpanded_565_To_PMColor(tmp), alphaScale);   \
-    } while (0)
-
-#define MAKENAME(suffix)        S16_alpha_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 uint16_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
-                                SkASSERT(state.fAlphaScale < 256)
-#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale
-#define RETURNDST(src)          SkAlphaMulQ(SkPixel16ToPixel32(src), alphaScale)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-// SRC == Index8
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst)   Filter_32_opaque(x, y, a, b, c, d, dst)
-
-#define MAKENAME(suffix)        SI8_opaque_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 uint8_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
-                                SkASSERT(state.fAlphaScale == 256)
-#define PREAMBLE(state)         const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
-#define RETURNDST(src)          table[src]
-#define SRC_TO_FILTER(src)      table[src]
-#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
-#include "SkBitmapProcState_sample.h"
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst)   Filter_32_alpha(x, y, a, b, c, d, dst, alphaScale)
-
-#define MAKENAME(suffix)        SI8_alpha_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 uint8_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
-                                SkASSERT(state.fAlphaScale < 256)
-#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale; \
-                                const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
-#define RETURNDST(src)          SkAlphaMulQ(table[src], alphaScale)
-#define SRC_TO_FILTER(src)      table[src]
-#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
-#include "SkBitmapProcState_sample.h"
-
-// SRC == 4444
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst)  *(dst) = Filter_4444_D32(x, y, a, b, c, d)
-
-#define MAKENAME(suffix)        S4444_opaque_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 SkPMColor16
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
-                                SkASSERT(state.fAlphaScale == 256)
-#define RETURNDST(src)          SkPixel4444ToPixel32(src)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst)  \
-    do {                                                    \
-        uint32_t tmp = Filter_4444_D32(x, y, a, b, c, d);   \
-        *(dst) = SkAlphaMulQ(tmp, alphaScale);              \
-    } while (0)
-
-#define MAKENAME(suffix)        S4444_alpha_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 SkPMColor16
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
-                                SkASSERT(state.fAlphaScale < 256)
-#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale
-#define RETURNDST(src)          SkAlphaMulQ(SkPixel4444ToPixel32(src), alphaScale)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-// SRC == A8
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst) \
-    do {                                                        \
-        unsigned tmp = Filter_8(x, y, a, b, c, d);              \
-        *(dst) = SkAlphaMulQ(pmColor, SkAlpha255To256(tmp));    \
-    } while (0)
-
-#define MAKENAME(suffix)        SA8_alpha_D32 ## suffix
-#define DSTSIZE                 32
-#define SRCTYPE                 uint8_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kA8_Config); \
-                                SkASSERT(state.fAlphaScale == 256)
-#define PREAMBLE(state)         const SkPMColor pmColor = state.fPaintPMColor;
-#define RETURNDST(src)          SkAlphaMulQ(pmColor, SkAlpha255To256(src))
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-/*****************************************************************************
- *
- *  D16 functions
- *
- */
-
-// SRC == 8888
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst) \
-    do {                                                \
-        SkPMColor dstColor;                             \
-        Filter_32_opaque(x, y, a, b, c, d, &dstColor);  \
-        (*dst) = SkPixel32ToPixel16(dstColor);          \
-    } while (0)
-
-#define MAKENAME(suffix)        S32_D16 ## suffix
-#define DSTSIZE                 16
-#define SRCTYPE                 SkPMColor
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
-                                SkASSERT(state.fBitmap->isOpaque())
-#define RETURNDST(src)          SkPixel32ToPixel16(src)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-// SRC == 565
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst) \
-    do {                                                        \
-        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
-        *(dst) = SkCompact_rgb_16((tmp) >> 5);                  \
-    } while (0)
-
-#define MAKENAME(suffix)        S16_D16 ## suffix
-#define DSTSIZE                 16
-#define SRCTYPE                 uint16_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
-#define RETURNDST(src)          src
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_sample.h"
-
-// SRC == Index8
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst) \
-    do {                                                        \
-        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
-        *(dst) = SkCompact_rgb_16((tmp) >> 5);                  \
-    } while (0)
-
-#define MAKENAME(suffix)        SI8_D16 ## suffix
-#define DSTSIZE                 16
-#define SRCTYPE                 uint8_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
-                                SkASSERT(state.fBitmap->isOpaque())
-#define PREAMBLE(state)         const uint16_t* SK_RESTRICT table = state.fBitmap->getColorTable()->lock16BitCache()
-#define RETURNDST(src)          table[src]
-#define SRC_TO_FILTER(src)      table[src]
-#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlock16BitCache()
-#include "SkBitmapProcState_sample.h"
+#define   NAME_WRAP(x)  x
+#include "SkBitmapProcState_filter.h"
+#include "SkBitmapProcState_procs.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst) \
-    do {                                                        \
-        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
-        *(dst) = SkCompact_rgb_16((tmp) >> 5);                  \
-    } while (0)
+/**
+ *  For the purposes of drawing bitmaps, if a matrix is "almost" translate
+ *  go ahead and treat it as if it were, so that subsequent code can go fast.
+ */
+static bool just_trans_clamp(const SkMatrix& matrix, const SkBitmap& bitmap) {
+    SkMatrix::TypeMask mask = matrix.getType();
 
+    if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
+        return false;
+    }
+    if (mask & SkMatrix::kScale_Mask) {
+        SkScalar sx = matrix[SkMatrix::kMScaleX];
+        SkScalar sy = matrix[SkMatrix::kMScaleY];
+        int w = bitmap.width();
+        int h = bitmap.height();
+        int sw = SkScalarRound(SkScalarMul(sx, SkIntToScalar(w)));
+        int sh = SkScalarRound(SkScalarMul(sy, SkIntToScalar(h)));
+        return sw == w && sh == h;
+    }
+    // if we got here, we're either kTranslate_Mask or identity
+    return true;
+}
 
-// clamp
+static bool just_trans_general(const SkMatrix& matrix) {
+    SkMatrix::TypeMask mask = matrix.getType();
 
-#define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
-#define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
-#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
-#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+    if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
+        return false;
+    }
+    if (mask & SkMatrix::kScale_Mask) {
+        const SkScalar tol = SK_Scalar1 / 32768;
 
-#define MAKENAME(suffix)        Clamp_S16_D16 ## suffix
-#define SRCTYPE                 uint16_t
-#define DSTTYPE                 uint16_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_shaderproc.h"
-
-
-#define TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
-#define TILEY_PROCF(fy, max)    (((fy) & 0xFFFF) * ((max) + 1) >> 16)
-#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
-#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
-
-#define MAKENAME(suffix)        Repeat_S16_D16 ## suffix
-#define SRCTYPE                 uint16_t
-#define DSTTYPE                 uint16_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
-#define SRC_TO_FILTER(src)      src
-#include "SkBitmapProcState_shaderproc.h"
-
-
-#define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
-#define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
-#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
-#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
-
-#undef FILTER_PROC
-#define FILTER_PROC(x, y, a, b, c, d, dst)   Filter_32_opaque(x, y, a, b, c, d, dst)
-#define MAKENAME(suffix)        Clamp_SI8_opaque_D32 ## suffix
-#define SRCTYPE                 uint8_t
-#define DSTTYPE                 uint32_t
-#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config)
-#define PREAMBLE(state)         const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
-#define SRC_TO_FILTER(src)      table[src]
-#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
-#include "SkBitmapProcState_shaderproc.h"
+        if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol)) {
+            return false;
+        }
+        if (!SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol)) {
+            return false;
+        }
+    }
+    // if we got here, treat us as either kTranslate_Mask or identity
+    return true;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -371,7 +104,7 @@
         int shift = fOrigBitmap.extractMipLevel(&fMipBitmap,
                                                 SkScalarToFixed(m->getScaleX()),
                                                 SkScalarToFixed(m->getSkewY()));
-        
+
         if (shift > 0) {
             if (m != &fUnitInvMatrix) {
                 fUnitInvMatrix = *m;
@@ -380,17 +113,52 @@
 
             SkScalar scale = SkFixedToScalar(SK_Fixed1 >> shift);
             fUnitInvMatrix.postScale(scale, scale);
-            
+
             // now point here instead of fOrigBitmap
             fBitmap = &fMipBitmap;
         }
     }
 
+    // wack our matrix to exactly no-scale, if we're really close to begin with
+    {
+        bool fixupMatrix = clamp_clamp ?
+        just_trans_clamp(*m, *fBitmap) : just_trans_general(*m);
+        if (fixupMatrix) {
+#ifdef SK_IGNORE_TRANS_CLAMP_FIX
+            if (m != &fUnitInvMatrix) {    // can't mutate the original
+                fUnitInvMatrix = inv;
+                m = &fUnitInvMatrix;
+            }
+            fUnitInvMatrix.set(SkMatrix::kMScaleX, SK_Scalar1);
+            fUnitInvMatrix.set(SkMatrix::kMScaleY, SK_Scalar1);
+#else
+            // If we can be treated just like translate, construct that inverse
+            // such that we landed in the proper place. Given that m may have
+            // some slight scale, we have to invert it to compute this new
+            // matrix.
+            SkMatrix forward;
+            if (m->invert(&forward)) {
+                SkScalar tx = -SkScalarRoundToScalar(forward.getTranslateX());
+                SkScalar ty = -SkScalarRoundToScalar(forward.getTranslateY());
+                fUnitInvMatrix.setTranslate(tx, ty);
+                m = &fUnitInvMatrix;
+                // now the following code will sniff m, and decide to take the
+                // fast case (since m is purely translate).
+            }
+#endif
+        }
+    }
+
+    // Below this point, we should never refer to the inv parameter, since we
+    // may be using a munged version for "our" inverse.
+
     fInvMatrix      = m;
     fInvProc        = m->getMapXYProc();
     fInvType        = m->getType();
     fInvSx          = SkScalarToFixed(m->getScaleX());
+    fInvSxFractionalInt = SkScalarToFractionalInt(m->getScaleX());
     fInvKy          = SkScalarToFixed(m->getSkewY());
+    fInvKyFractionalInt = SkScalarToFractionalInt(m->getSkewY());
 
     fAlphaScale = SkAlpha255To256(paint.getAlpha());
 
@@ -400,7 +168,7 @@
     // note: we explicitly check inv, since m might be scaled due to unitinv
     //       trickery, but we don't want to see that for this test
     fDoFilter = paint.isFilterBitmap() &&
-                (inv.getType() > SkMatrix::kTranslate_Mask &&
+                (fInvType > SkMatrix::kTranslate_Mask &&
                  valid_for_filtering(fBitmap->width() | fBitmap->height()));
 
     fShaderProc32 = NULL;
@@ -414,7 +182,7 @@
     }
 
     ///////////////////////////////////////////////////////////////////////
-    
+
     int index = 0;
     if (fAlphaScale < 256) {  // note: this distinction is not used for D16
         index |= 1;
@@ -447,7 +215,8 @@
             return false;
     }
 
-    static const SampleProc32 gSample32[] = {
+#if !SK_ARM_NEON_IS_ALWAYS
+    static const SampleProc32 gSkBitmapProcStateSample32[] = {
         S32_opaque_D32_nofilter_DXDY,
         S32_alpha_D32_nofilter_DXDY,
         S32_opaque_D32_nofilter_DX,
@@ -456,7 +225,7 @@
         S32_alpha_D32_filter_DXDY,
         S32_opaque_D32_filter_DX,
         S32_alpha_D32_filter_DX,
-        
+
         S16_opaque_D32_nofilter_DXDY,
         S16_alpha_D32_nofilter_DXDY,
         S16_opaque_D32_nofilter_DX,
@@ -465,7 +234,7 @@
         S16_alpha_D32_filter_DXDY,
         S16_opaque_D32_filter_DX,
         S16_alpha_D32_filter_DX,
-        
+
         SI8_opaque_D32_nofilter_DXDY,
         SI8_alpha_D32_nofilter_DXDY,
         SI8_opaque_D32_nofilter_DX,
@@ -474,7 +243,7 @@
         SI8_alpha_D32_filter_DXDY,
         SI8_opaque_D32_filter_DX,
         SI8_alpha_D32_filter_DX,
-        
+
         S4444_opaque_D32_nofilter_DXDY,
         S4444_alpha_D32_nofilter_DXDY,
         S4444_opaque_D32_nofilter_DX,
@@ -483,8 +252,8 @@
         S4444_alpha_D32_filter_DXDY,
         S4444_opaque_D32_filter_DX,
         S4444_alpha_D32_filter_DX,
-        
-        // A8 treats alpha/opauqe the same (equally efficient)
+
+        // A8 treats alpha/opaque the same (equally efficient)
         SA8_alpha_D32_nofilter_DXDY,
         SA8_alpha_D32_nofilter_DXDY,
         SA8_alpha_D32_nofilter_DX,
@@ -494,43 +263,48 @@
         SA8_alpha_D32_filter_DX,
         SA8_alpha_D32_filter_DX
     };
-    
-    static const SampleProc16 gSample16[] = {
+
+    static const SampleProc16 gSkBitmapProcStateSample16[] = {
         S32_D16_nofilter_DXDY,
         S32_D16_nofilter_DX,
         S32_D16_filter_DXDY,
         S32_D16_filter_DX,
-        
+
         S16_D16_nofilter_DXDY,
         S16_D16_nofilter_DX,
         S16_D16_filter_DXDY,
         S16_D16_filter_DX,
-        
+
         SI8_D16_nofilter_DXDY,
         SI8_D16_nofilter_DX,
         SI8_D16_filter_DXDY,
         SI8_D16_filter_DX,
-        
+
         // Don't support 4444 -> 565
         NULL, NULL, NULL, NULL,
         // Don't support A8 -> 565
         NULL, NULL, NULL, NULL
     };
+#endif
 
-    fSampleProc32 = gSample32[index];
+    fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index];
     index >>= 1;    // shift away any opaque/alpha distinction
-    fSampleProc16 = gSample16[index];
+    fSampleProc16 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample16)[index];
 
     // our special-case shaderprocs
-    if (S16_D16_filter_DX == fSampleProc16) {
+    if (SK_ARM_NEON_WRAP(S16_D16_filter_DX) == fSampleProc16) {
         if (clamp_clamp) {
-            fShaderProc16 = Clamp_S16_D16_filter_DX_shaderproc;
+            fShaderProc16 = SK_ARM_NEON_WRAP(Clamp_S16_D16_filter_DX_shaderproc);
         } else if (SkShader::kRepeat_TileMode == fTileModeX &&
                    SkShader::kRepeat_TileMode == fTileModeY) {
-            fShaderProc16 = Repeat_S16_D16_filter_DX_shaderproc;
+            fShaderProc16 = SK_ARM_NEON_WRAP(Repeat_S16_D16_filter_DX_shaderproc);
         }
-    } else if (SI8_opaque_D32_filter_DX == fSampleProc32 && clamp_clamp) {
-        fShaderProc32 = Clamp_SI8_opaque_D32_filter_DX_shaderproc;
+    } else if (SK_ARM_NEON_WRAP(SI8_opaque_D32_filter_DX) == fSampleProc32 && clamp_clamp) {
+        fShaderProc32 = SK_ARM_NEON_WRAP(Clamp_SI8_opaque_D32_filter_DX_shaderproc);
+    }
+
+    if (NULL == fShaderProc32) {
+        fShaderProc32 = this->chooseShaderProc32();
     }
 
     // see if our platform has any accelerated overrides
@@ -538,11 +312,396 @@
     return true;
 }
 
+static void Clamp_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
+                                                    int x, int y,
+                                                    SkPMColor* SK_RESTRICT colors,
+                                                    int count) {
+    SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(!s.fDoFilter);
+
+    const int maxX = s.fBitmap->width() - 1;
+    const int maxY = s.fBitmap->height() - 1;
+    int ix = s.fFilterOneX + x;
+    int iy = SkClampMax(s.fFilterOneY + y, maxY);
+#ifdef SK_DEBUG
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                   SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        int iy2 = SkClampMax(SkScalarFloorToInt(pt.fY), maxY);
+        int ix2 = SkScalarFloorToInt(pt.fX);
+
+        SkASSERT(iy == iy2);
+        SkASSERT(ix == ix2);
+    }
+#endif
+    const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
+
+    // clamp to the left
+    if (ix < 0) {
+        int n = SkMin32(-ix, count);
+        sk_memset32(colors, row[0], n);
+        count -= n;
+        if (0 == count) {
+            return;
+        }
+        colors += n;
+        SkASSERT(-ix == n);
+        ix = 0;
+    }
+    // copy the middle
+    if (ix <= maxX) {
+        int n = SkMin32(maxX - ix + 1, count);
+        memcpy(colors, row + ix, n * sizeof(SkPMColor));
+        count -= n;
+        if (0 == count) {
+            return;
+        }
+        colors += n;
+    }
+    SkASSERT(count > 0);
+    // clamp to the right
+    sk_memset32(colors, row[maxX], count);
+}
+
+static inline int sk_int_mod(int x, int n) {
+    SkASSERT(n > 0);
+    if ((unsigned)x >= (unsigned)n) {
+        if (x < 0) {
+            x = n + ~(~x % n);
+        } else {
+            x = x % n;
+        }
+    }
+    return x;
+}
+
+static inline int sk_int_mirror(int x, int n) {
+    x = sk_int_mod(x, 2 * n);
+    if (x >= n) {
+        x = n + ~(x - n);
+    }
+    return x;
+}
+
+static void Repeat_S32_D32_nofilter_trans_shaderproc(const SkBitmapProcState& s,
+                                                     int x, int y,
+                                                     SkPMColor* SK_RESTRICT colors,
+                                                     int count) {
+    SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(!s.fDoFilter);
+
+    const int stopX = s.fBitmap->width();
+    const int stopY = s.fBitmap->height();
+    int ix = s.fFilterOneX + x;
+    int iy = sk_int_mod(s.fFilterOneY + y, stopY);
+#ifdef SK_DEBUG
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                   SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        int iy2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
+        int ix2 = SkScalarFloorToInt(pt.fX);
+
+        SkASSERT(iy == iy2);
+        SkASSERT(ix == ix2);
+    }
+#endif
+    const SkPMColor* row = s.fBitmap->getAddr32(0, iy);
+
+    ix = sk_int_mod(ix, stopX);
+    for (;;) {
+        int n = SkMin32(stopX - ix, count);
+        memcpy(colors, row + ix, n * sizeof(SkPMColor));
+        count -= n;
+        if (0 == count) {
+            return;
+        }
+        colors += n;
+        ix = 0;
+    }
+}
+
+static void S32_D32_constX_shaderproc(const SkBitmapProcState& s,
+                                      int x, int y,
+                                      SkPMColor* SK_RESTRICT colors,
+                                      int count) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(1 == s.fBitmap->width());
+
+    int iY0, iY1, iSubY;
+
+    if (s.fDoFilter) {
+        SkBitmapProcState::MatrixProc mproc = s.getMatrixProc();
+        uint32_t xy[2];
+
+        mproc(s, xy, 1, x, y);
+
+        iY0 = xy[0] >> 18;
+        iY1 = xy[0] & 0x3FFF;
+        iSubY = (xy[0] >> 14) & 0xF;
+    } else {
+        int yTemp;
+
+        if (s.fInvType > SkMatrix::kTranslate_Mask) {
+            SkPoint pt;
+            s.fInvProc(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf,
+                       &pt);
+            // When the matrix has a scale component the setup code in
+            // chooseProcs multiples the inverse matrix by the inverse of the
+            // bitmap's width and height. Since this method is going to do
+            // its own tiling and sampling we need to undo that here.
+            if (SkShader::kClamp_TileMode != s.fTileModeX ||
+                SkShader::kClamp_TileMode != s.fTileModeY) {
+                yTemp = SkScalarFloorToInt(pt.fY * s.fBitmap->height());
+            } else {
+                yTemp = SkScalarFloorToInt(pt.fY);
+            }
+        } else {
+            yTemp = s.fFilterOneY + y;
+        }
+
+        const int stopY = s.fBitmap->height();
+        switch (s.fTileModeY) {
+            case SkShader::kClamp_TileMode:
+                iY0 = SkClampMax(yTemp, stopY-1);
+                break;
+            case SkShader::kRepeat_TileMode:
+                iY0 = sk_int_mod(yTemp, stopY);
+                break;
+            case SkShader::kMirror_TileMode:
+            default:
+                iY0 = sk_int_mirror(yTemp, stopY);
+                break;
+        }
+
+#ifdef SK_DEBUG
+        {
+            SkPoint pt;
+            s.fInvProc(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf,
+                       &pt);
+            if (s.fInvType > SkMatrix::kTranslate_Mask &&
+                (SkShader::kClamp_TileMode != s.fTileModeX ||
+                 SkShader::kClamp_TileMode != s.fTileModeY)) {
+                pt.fY *= s.fBitmap->height();
+            }
+            int iY2;
+
+            switch (s.fTileModeY) {
+            case SkShader::kClamp_TileMode:
+                iY2 = SkClampMax(SkScalarFloorToInt(pt.fY), stopY-1);
+                break;
+            case SkShader::kRepeat_TileMode:
+                iY2 = sk_int_mod(SkScalarFloorToInt(pt.fY), stopY);
+                break;
+            case SkShader::kMirror_TileMode:
+            default:
+                iY2 = sk_int_mirror(SkScalarFloorToInt(pt.fY), stopY);
+                break;
+            }
+
+            SkASSERT(iY0 == iY2);
+        }
+#endif
+    }
+
+    const SkPMColor* row0 = s.fBitmap->getAddr32(0, iY0);
+    SkPMColor color;
+
+    if (s.fDoFilter) {
+        const SkPMColor* row1 = s.fBitmap->getAddr32(0, iY1);
+
+        if (s.fAlphaScale < 256) {
+            Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale);
+        } else {
+            Filter_32_opaque(iSubY, *row0, *row1, &color);
+        }
+    } else {
+        if (s.fAlphaScale < 256) {
+            color = SkAlphaMulQ(*row0, s.fAlphaScale);
+        } else {
+            color = *row0;
+        }
+    }
+
+    sk_memset32(colors, color, count);
+}
+
+static void DoNothing_shaderproc(const SkBitmapProcState&, int x, int y,
+                                 SkPMColor* SK_RESTRICT colors, int count) {
+    // if we get called, the matrix is too tricky, so we just draw nothing
+    sk_memset32(colors, 0, count);
+}
+
+bool SkBitmapProcState::setupForTranslate() {
+    SkPoint pt;
+    fInvProc(*fInvMatrix, SK_ScalarHalf, SK_ScalarHalf, &pt);
+
+    /*
+     *  if the translate is larger than our ints, we can get random results, or
+     *  worse, we might get 0x80000000, which wreaks havoc on us, since we can't
+     *  negate it.
+     */
+    const SkScalar too_big = SkIntToScalar(1 << 30);
+    if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) {
+        return false;
+    }
+
+    // Since we know we're not filtered, we re-purpose these fields allow
+    // us to go from device -> src coordinates w/ just an integer add,
+    // rather than running through the inverse-matrix
+    fFilterOneX = SkScalarFloorToInt(pt.fX);
+    fFilterOneY = SkScalarFloorToInt(pt.fY);
+    return true;
+}
+
+SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() {
+
+    if (SkBitmap::kARGB_8888_Config != fBitmap->config()) {
+        return NULL;
+    }
+
+#ifndef SK_IGNORE_1XN_BITMAP_OPT
+    static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+
+    if (1 == fBitmap->width() && 0 == (fInvType & ~kMask)) {
+        if (!fDoFilter && fInvType <= SkMatrix::kTranslate_Mask && !this->setupForTranslate()) {
+            return DoNothing_shaderproc;
+        }
+        return S32_D32_constX_shaderproc;
+    }
+#endif
+
+    if (fAlphaScale < 256) {
+        return NULL;
+    }
+    if (fInvType > SkMatrix::kTranslate_Mask) {
+        return NULL;
+    }
+    if (fDoFilter) {
+        return NULL;
+    }
+
+    SkShader::TileMode tx = (SkShader::TileMode)fTileModeX;
+    SkShader::TileMode ty = (SkShader::TileMode)fTileModeY;
+
+    if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) {
+        if (this->setupForTranslate()) {
+            return Clamp_S32_D32_nofilter_trans_shaderproc;
+        }
+        return DoNothing_shaderproc;
+    }
+    if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) {
+        if (this->setupForTranslate()) {
+            return Repeat_S32_D32_nofilter_trans_shaderproc;
+        }
+        return DoNothing_shaderproc;
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+static void check_scale_nofilter(uint32_t bitmapXY[], int count,
+                                 unsigned mx, unsigned my) {
+    unsigned y = *bitmapXY++;
+    SkASSERT(y < my);
+
+    const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY);
+    for (int i = 0; i < count; ++i) {
+        SkASSERT(xptr[i] < mx);
+    }
+}
+
+static void check_scale_filter(uint32_t bitmapXY[], int count,
+                                 unsigned mx, unsigned my) {
+    uint32_t YY = *bitmapXY++;
+    unsigned y0 = YY >> 18;
+    unsigned y1 = YY & 0x3FFF;
+    SkASSERT(y0 < my);
+    SkASSERT(y1 < my);
+
+    for (int i = 0; i < count; ++i) {
+        uint32_t XX = bitmapXY[i];
+        unsigned x0 = XX >> 18;
+        unsigned x1 = XX & 0x3FFF;
+        SkASSERT(x0 < mx);
+        SkASSERT(x1 < mx);
+    }
+}
+
+static void check_affine_nofilter(uint32_t bitmapXY[], int count,
+                                 unsigned mx, unsigned my) {
+    for (int i = 0; i < count; ++i) {
+        uint32_t XY = bitmapXY[i];
+        unsigned x = XY & 0xFFFF;
+        unsigned y = XY >> 16;
+        SkASSERT(x < mx);
+        SkASSERT(y < my);
+    }
+}
+
+static void check_affine_filter(uint32_t bitmapXY[], int count,
+                                 unsigned mx, unsigned my) {
+    for (int i = 0; i < count; ++i) {
+        uint32_t YY = *bitmapXY++;
+        unsigned y0 = YY >> 18;
+        unsigned y1 = YY & 0x3FFF;
+        SkASSERT(y0 < my);
+        SkASSERT(y1 < my);
+
+        uint32_t XX = *bitmapXY++;
+        unsigned x0 = XX >> 18;
+        unsigned x1 = XX & 0x3FFF;
+        SkASSERT(x0 < mx);
+        SkASSERT(x1 < mx);
+    }
+}
+
+void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state,
+                                        uint32_t bitmapXY[], int count,
+                                        int x, int y) {
+    SkASSERT(bitmapXY);
+    SkASSERT(count > 0);
+
+    state.fMatrixProc(state, bitmapXY, count, x, y);
+
+    void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my);
+
+    // There are four formats possible:
+    //  scale -vs- affine
+    //  filter -vs- nofilter
+    if (state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) {
+        proc = state.fDoFilter ? check_scale_filter : check_scale_nofilter;
+    } else {
+        proc = state.fDoFilter ? check_affine_filter : check_affine_nofilter;
+    }
+    proc(bitmapXY, count, state.fBitmap->width(), state.fBitmap->height());
+}
+
+SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const {
+    return DebugMatrixProc;
+}
+
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 /*
     The storage requirements for the different matrix procs are as follows,
     where each X or Y is 2 bytes, and N is the number of pixels/elements:
- 
+
     scale/translate     nofilter      Y(4bytes) + N * X
     affine/perspective  nofilter      N * (X Y)
     scale/translate     filter        Y Y + N * (X X)
@@ -568,4 +727,3 @@
 
     return size;
 }
-
diff --git a/src/core/SkBitmapProcState.h b/src/core/SkBitmapProcState.h
index c04992b..69f4c2f 100644
--- a/src/core/SkBitmapProcState.h
+++ b/src/core/SkBitmapProcState.h
@@ -13,6 +13,22 @@
 #include "SkBitmap.h"
 #include "SkMatrix.h"
 
+#define FractionalInt_IS_64BIT
+
+#ifdef FractionalInt_IS_64BIT
+    typedef SkFixed48    SkFractionalInt;
+    #define SkScalarToFractionalInt(x)  SkScalarToFixed48(x)
+    #define SkFractionalIntToFixed(x)   SkFixed48ToFixed(x)
+    #define SkFixedToFractionalInt(x)   SkFixedToFixed48(x)
+    #define SkFractionalIntToInt(x)     SkFixed48ToInt(x)
+#else
+    typedef SkFixed    SkFractionalInt;
+    #define SkScalarToFractionalInt(x)  SkScalarToFixed(x)
+    #define SkFractionalIntToFixed(x)   (x)
+    #define SkFixedToFractionalInt(x)   (x)
+    #define SkFractionalIntToInt(x)     ((x) >> 16)
+#endif
+
 class SkPaint;
 
 struct SkBitmapProcState {
@@ -27,7 +43,7 @@
                                uint32_t bitmapXY[],
                                int count,
                                int x, int y);
-    
+
     typedef void (*SampleProc32)(const SkBitmapProcState&,
                                  const uint32_t[],
                                  int count,
@@ -37,25 +53,22 @@
                                  const uint32_t[],
                                  int count,
                                  uint16_t colors[]);
-    
-    typedef U16CPU (*FixedTileProc)(SkFixed);   // returns 0..0xFFFF
-    typedef U16CPU (*IntTileProc)(int value, int count);   // returns 0..count-1
 
-    // If a shader proc is present, then the corresponding matrix/sample procs
-    // are ignored
-    ShaderProc32        fShaderProc32;      // chooseProcs
-    ShaderProc16        fShaderProc16;      // chooseProcs
-    // These are used if the shaderproc is NULL
-    MatrixProc          fMatrixProc;        // chooseProcs
-    SampleProc32        fSampleProc32;      // chooseProcs
-    SampleProc16        fSampleProc16;      // chooseProcs
+    typedef U16CPU (*FixedTileProc)(SkFixed);   // returns 0..0xFFFF
+    typedef U16CPU (*FixedTileLowBitsProc)(SkFixed, int);   // returns 0..0xF
+    typedef U16CPU (*IntTileProc)(int value, int count);   // returns 0..count-1
 
     const SkBitmap*     fBitmap;            // chooseProcs - orig or mip
     const SkMatrix*     fInvMatrix;         // chooseProcs
     SkMatrix::MapXYProc fInvProc;           // chooseProcs
 
+    SkFractionalInt     fInvSxFractionalInt;
+    SkFractionalInt     fInvKyFractionalInt;
+
     FixedTileProc       fTileProcX;         // chooseProcs
     FixedTileProc       fTileProcY;         // chooseProcs
+    FixedTileLowBitsProc fTileLowBitsProcX; // chooseProcs
+    FixedTileLowBitsProc fTileLowBitsProcY; // chooseProcs
     IntTileProc         fIntTileProcY;      // chooseProcs
     SkFixed             fFilterOneX;
     SkFixed             fFilterOneY;
@@ -89,21 +102,50 @@
         (i.e. the number of SkPMColor values to be written by the sample proc).
         This routine takes into account that filtering and scale-vs-affine
         affect the amount of buffer space needed.
-     
+
         Only valid to call after chooseProcs (setContext) has been called. It is
         safe to call this inside the shader's shadeSpan() method.
      */
     int maxCountForBufferSize(size_t bufferSize) const;
 
+    // If a shader proc is present, then the corresponding matrix/sample procs
+    // are ignored
+    ShaderProc32 getShaderProc32() const { return fShaderProc32; }
+    ShaderProc16 getShaderProc16() const { return fShaderProc16; }
+
+#ifdef SK_DEBUG
+    MatrixProc getMatrixProc() const;
+#else
+    MatrixProc getMatrixProc() const { return fMatrixProc; }
+#endif
+    SampleProc32 getSampleProc32() const { return fSampleProc32; }
+    SampleProc16 getSampleProc16() const { return fSampleProc16; }
+
 private:
     friend class SkBitmapProcShader;
 
+    ShaderProc32        fShaderProc32;      // chooseProcs
+    ShaderProc16        fShaderProc16;      // chooseProcs
+    // These are used if the shaderproc is NULL
+    MatrixProc          fMatrixProc;        // chooseProcs
+    SampleProc32        fSampleProc32;      // chooseProcs
+    SampleProc16        fSampleProc16;      // chooseProcs
+
     SkMatrix            fUnitInvMatrix;     // chooseProcs
     SkBitmap            fOrigBitmap;        // CONSTRUCTOR
     SkBitmap            fMipBitmap;
 
     MatrixProc chooseMatrixProc(bool trivial_matrix);
     bool chooseProcs(const SkMatrix& inv, const SkPaint&);
+    ShaderProc32 chooseShaderProc32();
+
+    // Return false if we failed to setup for fast translate (e.g. overflow)
+    bool setupForTranslate();
+
+#ifdef SK_DEBUG
+    static void DebugMatrixProc(const SkBitmapProcState&,
+                                uint32_t[], int count, int x, int y);
+#endif
 };
 
 /*  Macros for packing and unpacking pairs of 16bit values in a 32bit uint.
@@ -136,6 +178,10 @@
                               int count, SkPMColor colors[]);
 void S32_alpha_D32_filter_DX(const SkBitmapProcState& s, const uint32_t xy[],
                              int count, SkPMColor colors[]);
+void S32_opaque_D32_filter_DXDY(const SkBitmapProcState& s,
+                           const uint32_t xy[], int count, SkPMColor colors[]);
+void S32_alpha_D32_filter_DXDY(const SkBitmapProcState& s,
+                           const uint32_t xy[], int count, SkPMColor colors[]);
 void ClampX_ClampY_filter_scale(const SkBitmapProcState& s, uint32_t xy[],
                                 int count, int x, int y);
 void ClampX_ClampY_nofilter_scale(const SkBitmapProcState& s, uint32_t xy[],
@@ -144,5 +190,7 @@
                                  uint32_t xy[], int count, int x, int y);
 void ClampX_ClampY_nofilter_affine(const SkBitmapProcState& s,
                                    uint32_t xy[], int count, int x, int y);
+void S32_D16_filter_DX(const SkBitmapProcState& s,
+                                   const uint32_t* xy, int count, uint16_t* colors);
 
 #endif
diff --git a/src/core/SkBitmapProcState_filter.h b/src/core/SkBitmapProcState_filter.h
index f69e17a..1260665 100644
--- a/src/core/SkBitmapProcState_filter.h
+++ b/src/core/SkBitmapProcState_filter.h
@@ -11,137 +11,65 @@
 
 /*
     Filter_32_opaque
-    
+
     There is no hard-n-fast rule that the filtering must produce
     exact results for the color components, but if the 4 incoming colors are
     all opaque, then the output color must also be opaque. Subsequent parts of
     the drawing pipeline may rely on this (e.g. which blitrow proc to use).
  */
 
-#if defined(__ARM_HAVE_NEON) && !defined(SK_CPU_BENDIAN)
-static inline void Filter_32_opaque_neon(unsigned x, unsigned y, 
+static inline void Filter_32_opaque(unsigned x, unsigned y,
                                     SkPMColor a00, SkPMColor a01,
                                     SkPMColor a10, SkPMColor a11,
-                                    SkPMColor *dst) {
-    asm volatile(
-                 "vdup.8         d0, %[y]                \n\t"   // duplicate y into d0
-                 "vmov.u8        d16, #16                \n\t"   // set up constant in d16
-                 "vsub.u8        d1, d16, d0             \n\t"   // d1 = 16-y
-                 
-                 "vdup.32        d4, %[a00]              \n\t"   // duplicate a00 into d4
-                 "vdup.32        d5, %[a10]              \n\t"   // duplicate a10 into d5
-                 "vmov.32        d4[1], %[a01]           \n\t"   // set top of d4 to a01
-                 "vmov.32        d5[1], %[a11]           \n\t"   // set top of d5 to a11
-                 
-                 "vmull.u8       q3, d4, d1              \n\t"   // q3 = [a01|a00] * (16-y)
-                 "vmull.u8       q0, d5, d0              \n\t"   // q0 = [a11|a10] * y
-                 
-                 "vdup.16        d5, %[x]                \n\t"   // duplicate x into d5
-                 "vmov.u16       d16, #16                \n\t"   // set up constant in d16
-                 "vsub.u16       d3, d16, d5             \n\t"   // d3 = 16-x
-                 
-                 "vmul.i16       d4, d7, d5              \n\t"   // d4  = a01 * x
-                 "vmla.i16       d4, d1, d5              \n\t"   // d4 += a11 * x
-                 "vmla.i16       d4, d6, d3              \n\t"   // d4 += a00 * (16-x)
-                 "vmla.i16       d4, d0, d3              \n\t"   // d4 += a10 * (16-x)
-                 "vshrn.i16      d0, q2, #8              \n\t"   // shift down result by 8
-                 "vst1.32        {d0[0]}, [%[dst]]       \n\t"   // store result
-                 :
-                 : [x] "r" (x), [y] "r" (y), [a00] "r" (a00), [a01] "r" (a01), [a10] "r" (a10), [a11] "r" (a11), [dst] "r" (dst)
-                 : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16"
-                 );
-}
-
-static inline void Filter_32_alpha_neon(unsigned x, unsigned y,
-                                          SkPMColor a00, SkPMColor a01,
-                                          SkPMColor a10, SkPMColor a11,
-                                          SkPMColor *dst, uint16_t scale) {
-    asm volatile(
-                 "vdup.8         d0, %[y]                \n\t"   // duplicate y into d0
-                 "vmov.u8        d16, #16                \n\t"   // set up constant in d16
-                 "vsub.u8        d1, d16, d0             \n\t"   // d1 = 16-y
-                 
-                 "vdup.32        d4, %[a00]              \n\t"   // duplicate a00 into d4
-                 "vdup.32        d5, %[a10]              \n\t"   // duplicate a10 into d5
-                 "vmov.32        d4[1], %[a01]           \n\t"   // set top of d4 to a01
-                 "vmov.32        d5[1], %[a11]           \n\t"   // set top of d5 to a11
-                 
-                 "vmull.u8       q3, d4, d1              \n\t"   // q3 = [a01|a00] * (16-y)
-                 "vmull.u8       q0, d5, d0              \n\t"   // q0 = [a11|a10] * y
-                 
-                 "vdup.16        d5, %[x]                \n\t"   // duplicate x into d5
-                 "vmov.u16       d16, #16                \n\t"   // set up constant in d16
-                 "vsub.u16       d3, d16, d5             \n\t"   // d3 = 16-x
-                 
-                 "vmul.i16       d4, d7, d5              \n\t"   // d4  = a01 * x
-                 "vmla.i16       d4, d1, d5              \n\t"   // d4 += a11 * x
-                 "vmla.i16       d4, d6, d3              \n\t"   // d4 += a00 * (16-x)
-                 "vmla.i16       d4, d0, d3              \n\t"   // d4 += a10 * (16-x)
-                 "vdup.16        d3, %[scale]            \n\t"   // duplicate scale into d3
-                 "vshr.u16       d4, d4, #8              \n\t"   // shift down result by 8
-                 "vmul.i16       d4, d4, d3              \n\t"   // multiply result by scale
-                 "vshrn.i16      d0, q2, #8              \n\t"   // shift down result by 8
-                 "vst1.32        {d0[0]}, [%[dst]]       \n\t"   // store result
-                 :
-                 : [x] "r" (x), [y] "r" (y), [a00] "r" (a00), [a01] "r" (a01), [a10] "r" (a10), [a11] "r" (a11), [dst] "r" (dst), [scale] "r" (scale)
-                 : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16"
-                 );
-}
-#define Filter_32_opaque    Filter_32_opaque_neon
-#define Filter_32_alpha     Filter_32_alpha_neon
-#else
-static inline void Filter_32_opaque_portable(unsigned x, unsigned y,
-                                             SkPMColor a00, SkPMColor a01,
-                                             SkPMColor a10, SkPMColor a11,
-                                             SkPMColor* dstColor) {
+                                    SkPMColor* dstColor) {
     SkASSERT((unsigned)x <= 0xF);
     SkASSERT((unsigned)y <= 0xF);
-    
+
     int xy = x * y;
     static const uint32_t mask = gMask_00FF00FF; //0xFF00FF;
-    
+
     int scale = 256 - 16*y - 16*x + xy;
     uint32_t lo = (a00 & mask) * scale;
     uint32_t hi = ((a00 >> 8) & mask) * scale;
-    
+
     scale = 16*x - xy;
     lo += (a01 & mask) * scale;
     hi += ((a01 >> 8) & mask) * scale;
-    
+
     scale = 16*y - xy;
     lo += (a10 & mask) * scale;
     hi += ((a10 >> 8) & mask) * scale;
-    
+
     lo += (a11 & mask) * xy;
     hi += ((a11 >> 8) & mask) * xy;
-    
+
     *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
 }
 
-static inline void Filter_32_alpha_portable(unsigned x, unsigned y,
-                                            SkPMColor a00, SkPMColor a01,
-                                            SkPMColor a10, SkPMColor a11,
-                                            SkPMColor* dstColor,
-                                            unsigned alphaScale) {
+static inline void Filter_32_alpha(unsigned x, unsigned y,
+                                   SkPMColor a00, SkPMColor a01,
+                                   SkPMColor a10, SkPMColor a11,
+                                   SkPMColor* dstColor,
+                                   unsigned alphaScale) {
     SkASSERT((unsigned)x <= 0xF);
     SkASSERT((unsigned)y <= 0xF);
     SkASSERT(alphaScale <= 256);
-    
+
     int xy = x * y;
     static const uint32_t mask = gMask_00FF00FF; //0xFF00FF;
-    
+
     int scale = 256 - 16*y - 16*x + xy;
     uint32_t lo = (a00 & mask) * scale;
     uint32_t hi = ((a00 >> 8) & mask) * scale;
-    
+
     scale = 16*x - xy;
     lo += (a01 & mask) * scale;
     hi += ((a01 >> 8) & mask) * scale;
-    
+
     scale = 16*y - xy;
     lo += (a10 & mask) * scale;
     hi += ((a10 >> 8) & mask) * scale;
-    
+
     lo += (a11 & mask) * xy;
     hi += ((a11 >> 8) & mask) * xy;
 
@@ -150,7 +78,48 @@
 
     *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
 }
-#define Filter_32_opaque    Filter_32_opaque_portable
-#define Filter_32_alpha     Filter_32_alpha_portable
-#endif
 
+// Two color version, where we filter only along 1 axis
+static inline void Filter_32_opaque(unsigned t,
+                                    SkPMColor color0,
+                                    SkPMColor color1,
+                                    SkPMColor* dstColor) {
+    SkASSERT((unsigned)t <= 0xF);
+
+    static const uint32_t mask = gMask_00FF00FF; //0x00FF00FF;
+
+    int scale = 256 - 16*t;
+    uint32_t lo = (color0 & mask) * scale;
+    uint32_t hi = ((color0 >> 8) & mask) * scale;
+
+    scale = 16*t;
+    lo += (color1 & mask) * scale;
+    hi += ((color1 >> 8) & mask) * scale;
+
+    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
+}
+
+// Two color version, where we filter only along 1 axis
+static inline void Filter_32_alpha(unsigned t,
+                                   SkPMColor color0,
+                                   SkPMColor color1,
+                                   SkPMColor* dstColor,
+                                   unsigned alphaScale) {
+    SkASSERT((unsigned)t <= 0xF);
+    SkASSERT(alphaScale <= 256);
+
+    static const uint32_t mask = gMask_00FF00FF; //0x00FF00FF;
+
+    int scale = 256 - 16*t;
+    uint32_t lo = (color0 & mask) * scale;
+    uint32_t hi = ((color0 >> 8) & mask) * scale;
+
+    scale = 16*t;
+    lo += (color1 & mask) * scale;
+    hi += ((color1 >> 8) & mask) * scale;
+
+    lo = ((lo >> 8) & mask) * alphaScale;
+    hi = ((hi >> 8) & mask) * alphaScale;
+
+    *dstColor = ((lo >> 8) & mask) | (hi & ~mask);
+}
diff --git a/src/core/SkBitmapProcState_matrix.h b/src/core/SkBitmapProcState_matrix.h
index f427e96..cc65c2d 100644
--- a/src/core/SkBitmapProcState_matrix.h
+++ b/src/core/SkBitmapProcState_matrix.h
@@ -7,7 +7,7 @@
  */
 
 #include "SkMath.h"
-
+#include "SkMathPriv.h"
 
 #define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale)
 #define SCALE_FILTER_NAME       MAKENAME(_filter_scale)
@@ -27,6 +27,22 @@
     #define PREAMBLE_ARG_Y
 #endif
 
+// declare functions externally to suppress warnings.
+void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t xy[], int count, int x, int y);
+void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                 uint32_t xy[], int count, int x, int y);
+void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t* SK_RESTRICT xy,
+                                int count, int x, int y);
+void SCALE_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t xy[], int count, int x, int y);
+void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
+                               uint32_t xy[], int count, int x, int y);
+void PERSP_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t* SK_RESTRICT xy, int count,
+                              int x, int y);
+
 void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
                                 uint32_t xy[], int count, int x, int y) {
     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
@@ -36,45 +52,44 @@
     // we store y, x, x, x, x, x
 
     const unsigned maxX = s.fBitmap->width() - 1;
-    SkFixed fx;
+    SkFractionalInt fx;
     {
         SkPoint pt;
         s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
                                   SkIntToScalar(y) + SK_ScalarHalf, &pt);
-        fx = SkScalarToFixed(pt.fY);
+        fx = SkScalarToFractionalInt(pt.fY);
         const unsigned maxY = s.fBitmap->height() - 1;
-        *xy++ = TILEY_PROCF(fx, maxY);
-        fx = SkScalarToFixed(pt.fX);
+        *xy++ = TILEY_PROCF(SkFractionalIntToFixed(fx), maxY);
+        fx = SkScalarToFractionalInt(pt.fX);
     }
-    
+
     if (0 == maxX) {
         // all of the following X values must be 0
         memset(xy, 0, count * sizeof(uint16_t));
         return;
     }
 
-    const SkFixed dx = s.fInvSx;
+    const SkFractionalInt dx = s.fInvSxFractionalInt;
 
 #ifdef CHECK_FOR_DECAL
-    // test if we don't need to apply the tile proc
-    if ((unsigned)(fx >> 16) <= maxX &&
-        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
-        decal_nofilter_scale(xy, fx, dx, count);
+    if (can_truncate_to_fixed_for_decal(fx, dx, count, maxX)) {
+        decal_nofilter_scale(xy, SkFractionalIntToFixed(fx),
+                             SkFractionalIntToFixed(dx), count);
     } else
 #endif
     {
         int i;
         for (i = (count >> 2); i > 0; --i) {
             unsigned a, b;
-            a = TILEX_PROCF(fx, maxX); fx += dx;
-            b = TILEX_PROCF(fx, maxX); fx += dx;
+            a = TILEX_PROCF(SkFractionalIntToFixed(fx), maxX); fx += dx;
+            b = TILEX_PROCF(SkFractionalIntToFixed(fx), maxX); fx += dx;
 #ifdef SK_CPU_BENDIAN
             *xy++ = (a << 16) | b;
 #else
             *xy++ = (b << 16) | a;
 #endif
-            a = TILEX_PROCF(fx, maxX); fx += dx;
-            b = TILEX_PROCF(fx, maxX); fx += dx;
+            a = TILEX_PROCF(SkFractionalIntToFixed(fx), maxX); fx += dx;
+            b = TILEX_PROCF(SkFractionalIntToFixed(fx), maxX); fx += dx;
 #ifdef SK_CPU_BENDIAN
             *xy++ = (a << 16) | b;
 #else
@@ -83,7 +98,7 @@
         }
         uint16_t* xx = (uint16_t*)xy;
         for (i = (count & 3); i > 0; --i) {
-            *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
+            *xx++ = TILEX_PROCF(SkFractionalIntToFixed(fx), maxX); fx += dx;
         }
     }
 }
@@ -98,22 +113,23 @@
     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                              SkMatrix::kScale_Mask |
                              SkMatrix::kAffine_Mask)) == 0);
-    
+
     PREAMBLE(s);
     SkPoint srcPt;
     s.fInvProc(*s.fInvMatrix,
                SkIntToScalar(x) + SK_ScalarHalf,
                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
-    SkFixed fx = SkScalarToFixed(srcPt.fX);
-    SkFixed fy = SkScalarToFixed(srcPt.fY);
-    SkFixed dx = s.fInvSx;
-    SkFixed dy = s.fInvKy;
+
+    SkFractionalInt fx = SkScalarToFractionalInt(srcPt.fX);
+    SkFractionalInt fy = SkScalarToFractionalInt(srcPt.fY);
+    SkFractionalInt dx = s.fInvSxFractionalInt;
+    SkFractionalInt dy = s.fInvKyFractionalInt;
     int maxX = s.fBitmap->width() - 1;
     int maxY = s.fBitmap->height() - 1;
-    
+
     for (int i = count; i > 0; --i) {
-        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
+        *xy++ = (TILEY_PROCF(SkFractionalIntToFixed(fy), maxY) << 16) |
+                 TILEX_PROCF(SkFractionalIntToFixed(fx), maxX);
         fx += dx; fy += dy;
     }
 }
@@ -122,15 +138,15 @@
                                 uint32_t* SK_RESTRICT xy,
                                 int count, int x, int y) {
     SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
-    
+
     PREAMBLE(s);
     int maxX = s.fBitmap->width() - 1;
     int maxY = s.fBitmap->height() - 1;
-    
+
     SkPerspIter   iter(*s.fInvMatrix,
                        SkIntToScalar(x) + SK_ScalarHalf,
                        SkIntToScalar(y) + SK_ScalarHalf, count);
-    
+
     while ((count = iter.next()) != 0) {
         const SkFixed* SK_RESTRICT srcXY = iter.getXY();
         while (--count >= 0) {
@@ -164,11 +180,11 @@
     SkASSERT(s.fInvKy == 0);
 
     PREAMBLE(s);
-    
+
     const unsigned maxX = s.fBitmap->width() - 1;
     const SkFixed one = s.fFilterOneX;
-    const SkFixed dx = s.fInvSx;
-    SkFixed fx;
+    const SkFractionalInt dx = s.fInvSxFractionalInt;
+    SkFractionalInt fx;
 
     {
         SkPoint pt;
@@ -179,20 +195,19 @@
         // compute our two Y values up front
         *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
         // now initialize fx
-        fx = SkScalarToFixed(pt.fX) - (one >> 1);
+        fx = SkScalarToFractionalInt(pt.fX) - (SkFixedToFractionalInt(one) >> 1);
     }
 
 #ifdef CHECK_FOR_DECAL
-    // test if we don't need to apply the tile proc
-    if (dx > 0 &&
-            (unsigned)(fx >> 16) <= maxX &&
-            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
-        decal_filter_scale(xy, fx, dx, count);
+    if (can_truncate_to_fixed_for_decal(fx, dx, count, maxX)) {
+        decal_filter_scale(xy, SkFractionalIntToFixed(fx),
+                           SkFractionalIntToFixed(dx), count);
     } else
 #endif
     {
         do {
-            *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
+            SkFixed fixedFx = SkFractionalIntToFixed(fx);
+            *xy++ = PACK_FILTER_X_NAME(fixedFx, maxX, one PREAMBLE_ARG_X);
             fx += dx;
         } while (--count != 0);
     }
@@ -204,13 +219,13 @@
     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                              SkMatrix::kScale_Mask |
                              SkMatrix::kAffine_Mask)) == 0);
-    
+
     PREAMBLE(s);
     SkPoint srcPt;
     s.fInvProc(*s.fInvMatrix,
                SkIntToScalar(x) + SK_ScalarHalf,
                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
+
     SkFixed oneX = s.fFilterOneX;
     SkFixed oneY = s.fFilterOneY;
     SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
@@ -219,7 +234,7 @@
     SkFixed dy = s.fInvKy;
     unsigned maxX = s.fBitmap->width() - 1;
     unsigned maxY = s.fBitmap->height() - 1;
-    
+
     do {
         *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
         fy += dy;
@@ -232,17 +247,17 @@
                               uint32_t* SK_RESTRICT xy, int count,
                               int x, int y) {
     SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
-    
+
     PREAMBLE(s);
     unsigned maxX = s.fBitmap->width() - 1;
     unsigned maxY = s.fBitmap->height() - 1;
     SkFixed oneX = s.fFilterOneX;
     SkFixed oneY = s.fFilterOneY;
-    
+
     SkPerspIter   iter(*s.fInvMatrix,
                        SkIntToScalar(x) + SK_ScalarHalf,
                        SkIntToScalar(y) + SK_ScalarHalf, count);
-    
+
     while ((count = iter.next()) != 0) {
         const SkFixed* SK_RESTRICT srcXY = iter.getXY();
         do {
diff --git a/src/core/SkBitmapProcState_matrixProcs.cpp b/src/core/SkBitmapProcState_matrixProcs.cpp
index bda2438..15c17b6 100644
--- a/src/core/SkBitmapProcState_matrixProcs.cpp
+++ b/src/core/SkBitmapProcState_matrixProcs.cpp
@@ -1,4 +1,4 @@
-/* NEON optimized code (C) COPYRIGHT 2009 Motorola 
+/* NEON optimized code (C) COPYRIGHT 2009 Motorola
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -8,11 +8,19 @@
 #include "SkPerspIter.h"
 #include "SkShader.h"
 #include "SkUtils.h"
+#include "SkUtilsArm.h"
+
+// Helper to ensure that when we shift down, we do it w/o sign-extension
+// so the caller doesn't have to manually mask off the top 16 bits
+//
+static unsigned SK_USHIFT16(unsigned x) {
+    return x >> 16;
+}
 
 /*  returns 0...(n-1) given any x (positive or negative).
-    
+
     As an example, if n (which is always positive) is 5...
- 
+
           x: -8 -7 -6 -5 -4 -3 -2 -1  0  1  2  3  4  5  6  7  8
     returns:  2  3  4  0  1  2  3  4  0  1  2  3  4  0  1  2  3
  */
@@ -28,59 +36,98 @@
     return x;
 }
 
+/*
+ *  The decal_ functions require that
+ *  1. dx > 0
+ *  2. [fx, fx+dx, fx+2dx, fx+3dx, ... fx+(count-1)dx] are all <= maxX
+ *
+ *  In addition, we use SkFractionalInt to keep more fractional precision than
+ *  just SkFixed, so we will abort the decal_ call if dx is very small, since
+ *  the decal_ function just operates on SkFixed. If that were changed, we could
+ *  skip the very_small test here.
+ */
+static inline bool can_truncate_to_fixed_for_decal(SkFractionalInt frX,
+                                                   SkFractionalInt frDx,
+                                                   int count, unsigned max) {
+    SkFixed dx = SkFractionalIntToFixed(frDx);
+
+    // if decal_ kept SkFractionalInt precision, this would just be dx <= 0
+    // I just made up the 1/256. Just don't want to perceive accumulated error
+    // if we truncate frDx and lose its low bits.
+    if (dx <= SK_Fixed1 / 256) {
+        return false;
+    }
+
+    // We cast to unsigned so we don't have to check for negative values, which
+    // will now appear as very large positive values, and thus fail our test!
+    SkFixed fx = SkFractionalIntToFixed(frX);
+    return (unsigned)SkFixedFloorToInt(fx) <= max &&
+           (unsigned)SkFixedFloorToInt(fx + dx * (count - 1)) < max;
+}
+
 void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
 void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
 
+// Compile neon code paths if needed
+#if !SK_ARM_NEON_IS_NONE
+
+// These are defined in src/opts/SkBitmapProcState_matrixProcs_neon.cpp
+extern const SkBitmapProcState::MatrixProc ClampX_ClampY_Procs_neon[];
+extern const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs_neon[];
+
+#endif // !SK_ARM_NEON_IS_NONE
+
+// Compile non-neon code path if needed
+#if !SK_ARM_NEON_IS_ALWAYS
 #define MAKENAME(suffix)        ClampX_ClampY ## suffix
 #define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
 #define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
 #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
 #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
 #define CHECK_FOR_DECAL
-#if	defined(__ARM_HAVE_NEON)
-    #include "SkBitmapProcState_matrix_clamp.h"
-#else
-    #include "SkBitmapProcState_matrix.h"
-#endif
+#include "SkBitmapProcState_matrix.h"
 
 #define MAKENAME(suffix)        RepeatX_RepeatY ## suffix
-#define TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
-#define TILEY_PROCF(fy, max)    (((fy) & 0xFFFF) * ((max) + 1) >> 16)
+#define TILEX_PROCF(fx, max)    SK_USHIFT16(((fx) & 0xFFFF) * ((max) + 1))
+#define TILEY_PROCF(fy, max)    SK_USHIFT16(((fy) & 0xFFFF) * ((max) + 1))
 #define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
 #define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
-#if	defined(__ARM_HAVE_NEON)
-    #include "SkBitmapProcState_matrix_repeat.h"
-#else
-    #include "SkBitmapProcState_matrix.h"
+#include "SkBitmapProcState_matrix.h"
 #endif
 
 #define MAKENAME(suffix)        GeneralXY ## suffix
-#define PREAMBLE(state)         SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; \
-                                SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY
-#define PREAMBLE_PARAM_X        , SkBitmapProcState::FixedTileProc tileProcX
-#define PREAMBLE_PARAM_Y        , SkBitmapProcState::FixedTileProc tileProcY
-#define PREAMBLE_ARG_X          , tileProcX
-#define PREAMBLE_ARG_Y          , tileProcY
-#define TILEX_PROCF(fx, max)    (tileProcX(fx) * ((max) + 1) >> 16)
-#define TILEY_PROCF(fy, max)    (tileProcY(fy) * ((max) + 1) >> 16)
-#define TILEX_LOW_BITS(fx, max) ((tileProcX(fx) * ((max) + 1) >> 12) & 0xF)
-#define TILEY_LOW_BITS(fy, max) ((tileProcY(fy) * ((max) + 1) >> 12) & 0xF)
+#define PREAMBLE(state)         SkBitmapProcState::FixedTileProc tileProcX = (state).fTileProcX; (void) tileProcX; \
+                                SkBitmapProcState::FixedTileProc tileProcY = (state).fTileProcY; (void) tileProcY; \
+                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcX = (state).fTileLowBitsProcX; (void) tileLowBitsProcX; \
+                                SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcY = (state).fTileLowBitsProcY; (void) tileLowBitsProcY
+#define PREAMBLE_PARAM_X        , SkBitmapProcState::FixedTileProc tileProcX, SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcX
+#define PREAMBLE_PARAM_Y        , SkBitmapProcState::FixedTileProc tileProcY, SkBitmapProcState::FixedTileLowBitsProc tileLowBitsProcY
+#define PREAMBLE_ARG_X          , tileProcX, tileLowBitsProcX
+#define PREAMBLE_ARG_Y          , tileProcY, tileLowBitsProcY
+#define TILEX_PROCF(fx, max)    SK_USHIFT16(tileProcX(fx) * ((max) + 1))
+#define TILEY_PROCF(fy, max)    SK_USHIFT16(tileProcY(fy) * ((max) + 1))
+#define TILEX_LOW_BITS(fx, max) tileLowBitsProcX(fx, (max) + 1)
+#define TILEY_LOW_BITS(fy, max) tileLowBitsProcY(fy, (max) + 1)
 #include "SkBitmapProcState_matrix.h"
 
 static inline U16CPU fixed_clamp(SkFixed x)
 {
 #ifdef SK_CPU_HAS_CONDITIONAL_INSTR
-    if (x >> 16)
-        x = 0xFFFF;
     if (x < 0)
         x = 0;
+    if (x >> 16)
+        x = 0xFFFF;
 #else
     if (x >> 16)
     {
+#if 0   // is this faster?
+        x = (~x >> 31) & 0xFFFF;
+#else
         if (x < 0)
             x = 0;
         else
             x = 0xFFFF;
+#endif
     }
 #endif
     return x;
@@ -91,6 +138,12 @@
     return x & 0xFFFF;
 }
 
+// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
+// See http://code.google.com/p/skia/issues/detail?id=472
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#pragma optimize("", off)
+#endif
+
 static inline U16CPU fixed_mirror(SkFixed x)
 {
     SkFixed s = x << 15 >> 31;
@@ -98,6 +151,10 @@
     return (x ^ s) & 0xFFFF;
 }
 
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#pragma optimize("", on)
+#endif
+
 static SkBitmapProcState::FixedTileProc choose_tile_proc(unsigned m)
 {
     if (SkShader::kClamp_TileMode == m)
@@ -108,6 +165,25 @@
     return fixed_mirror;
 }
 
+static inline U16CPU fixed_clamp_lowbits(SkFixed x, int) {
+    return (x >> 12) & 0xF;
+}
+
+static inline U16CPU fixed_repeat_or_mirrow_lowbits(SkFixed x, int scale) {
+    return ((x * scale) >> 12) & 0xF;
+}
+
+static SkBitmapProcState::FixedTileLowBitsProc choose_tile_lowbits_proc(unsigned m) {
+    if (SkShader::kClamp_TileMode == m) {
+        return fixed_clamp_lowbits;
+    } else {
+        SkASSERT(SkShader::kMirror_TileMode == m ||
+                 SkShader::kRepeat_TileMode == m);
+        // mirror and repeat have the same behavior for the low bits.
+        return fixed_repeat_or_mirrow_lowbits;
+    }
+}
+
 static inline U16CPU int_clamp(int x, int n) {
 #ifdef SK_CPU_HAS_CONDITIONAL_INSTR
     if (x >= n)
@@ -161,52 +237,6 @@
 {
     int i;
 
-#if	defined(__ARM_HAVE_NEON)
-    if (count >= 8) {
-        /* SkFixed is 16.16 fixed point */
-        SkFixed dx2 = dx+dx;
-        SkFixed dx4 = dx2+dx2;
-        SkFixed dx8 = dx4+dx4;
-
-        /* now build fx/fx+dx/fx+2dx/fx+3dx */
-        SkFixed fx1, fx2, fx3;
-        int32x2_t lower, upper;
-        int32x4_t lbase, hbase;
-        uint16_t *dst16 = (uint16_t *)dst;
-
-        fx1 = fx+dx;
-        fx2 = fx1+dx;
-        fx3 = fx2+dx;
-
-        /* avoid an 'lbase unitialized' warning */
-        lbase = vdupq_n_s32(fx);
-        lbase = vsetq_lane_s32(fx1, lbase, 1);
-        lbase = vsetq_lane_s32(fx2, lbase, 2);
-        lbase = vsetq_lane_s32(fx3, lbase, 3);
-        hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));
-
-        /* take upper 16 of each, store, and bump everything */
-        do {
-            int32x4_t lout, hout;
-            uint16x8_t hi16;
-
-            lout = lbase;
-            hout = hbase;
-            /* gets hi's of all louts then hi's of all houts */
-            asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
-            hi16 = vreinterpretq_u16_s32(hout);
-            vst1q_u16(dst16, hi16);
-
-            /* on to the next */
-            lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
-            hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
-            dst16 += 8;
-            count -= 8;
-            fx += dx8;
-        } while (count >= 8);
-        dst = (uint32_t *) dst16;
-    }
-#else
     for (i = (count >> 2); i > 0; --i)
     {
         *dst++ = pack_two_shorts(fx >> 16, (fx + dx) >> 16);
@@ -215,7 +245,6 @@
         fx += dx+dx;
     }
     count &= 3;
-#endif
 
     uint16_t* xx = (uint16_t*)dst;
     for (i = count; i > 0; --i) {
@@ -226,42 +255,6 @@
 void decal_filter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
 {
 
-#if	defined(__ARM_HAVE_NEON)
-    if (count >= 8) {
-        int32x4_t wide_fx;
-        int32x4_t wide_fx2;
-        int32x4_t wide_dx8 = vdupq_n_s32(dx*8);
-
-        wide_fx = vdupq_n_s32(fx);
-        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
-        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
-        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
-
-        wide_fx2 = vaddq_s32(wide_fx, vdupq_n_s32(dx+dx+dx+dx));
-
-        while (count >= 8) {
-            int32x4_t wide_out;
-            int32x4_t wide_out2;
-
-            wide_out = vshlq_n_s32(vshrq_n_s32(wide_fx, 12), 14);
-            wide_out = vorrq_s32(wide_out,
-            vaddq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(1)));
-
-            wide_out2 = vshlq_n_s32(vshrq_n_s32(wide_fx2, 12), 14);
-            wide_out2 = vorrq_s32(wide_out2,
-            vaddq_s32(vshrq_n_s32(wide_fx2,16), vdupq_n_s32(1)));
-
-            vst1q_u32(dst, vreinterpretq_u32_s32(wide_out));
-            vst1q_u32(dst+4, vreinterpretq_u32_s32(wide_out2));
-
-            dst += 8;
-            fx += dx*8;
-            wide_fx = vaddq_s32(wide_fx, wide_dx8);
-            wide_fx2 = vaddq_s32(wide_fx2, wide_dx8);
-            count -= 8;
-        }
-    }
-#endif
 
     if (count & 1)
     {
@@ -332,13 +325,13 @@
     SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
 
     int xpos = nofilter_trans_preamble(s, &xy, x, y);
-    const int width = s.fBitmap->width();    
+    const int width = s.fBitmap->width();
     if (1 == width) {
         // all of the following X values must be 0
         memset(xy, 0, count * sizeof(uint16_t));
         return;
     }
-    
+
     uint16_t* xptr = reinterpret_cast<uint16_t*>(xy);
     int n;
 
@@ -380,7 +373,7 @@
     SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
 
     int xpos = nofilter_trans_preamble(s, &xy, x, y);
-    const int width = s.fBitmap->width();    
+    const int width = s.fBitmap->width();
     if (1 == width) {
         // all of the following X values must be 0
         memset(xy, 0, count * sizeof(uint16_t));
@@ -420,7 +413,7 @@
     SkASSERT((s.fInvType & ~SkMatrix::kTranslate_Mask) == 0);
 
     int xpos = nofilter_trans_preamble(s, &xy, x, y);
-    const int width = s.fBitmap->width();    
+    const int width = s.fBitmap->width();
     if (1 == width) {
         // all of the following X values must be 0
         memset(xy, 0, count * sizeof(uint16_t));
@@ -451,7 +444,7 @@
     forward = !forward;
     xptr += n;
     count -= n;
-    
+
     while (count >= width) {
         if (forward) {
             fill_sequential(xptr, 0, width);
@@ -462,7 +455,7 @@
         xptr += width;
         count -= width;
     }
-    
+
     if (count > 0) {
         if (forward) {
             fill_sequential(xptr, 0, count);
@@ -490,7 +483,7 @@
                 return mirrorx_nofilter_trans;
         }
     }
-    
+
     int index = 0;
     if (fDoFilter) {
         index = 1;
@@ -500,28 +493,29 @@
     } else if (fInvType & SkMatrix::kAffine_Mask) {
         index += 2;
     }
-    
+
     if (SkShader::kClamp_TileMode == fTileModeX &&
         SkShader::kClamp_TileMode == fTileModeY)
     {
         // clamp gets special version of filterOne
         fFilterOneX = SK_Fixed1;
         fFilterOneY = SK_Fixed1;
-        return ClampX_ClampY_Procs[index];
+        return SK_ARM_NEON_WRAP(ClampX_ClampY_Procs)[index];
     }
-    
+
     // all remaining procs use this form for filterOne
     fFilterOneX = SK_Fixed1 / fBitmap->width();
     fFilterOneY = SK_Fixed1 / fBitmap->height();
-    
+
     if (SkShader::kRepeat_TileMode == fTileModeX &&
         SkShader::kRepeat_TileMode == fTileModeY)
     {
-        return RepeatX_RepeatY_Procs[index];
+        return SK_ARM_NEON_WRAP(RepeatX_RepeatY_Procs)[index];
     }
-    
+
     fTileProcX = choose_tile_proc(fTileModeX);
     fTileProcY = choose_tile_proc(fTileModeY);
+    fTileLowBitsProcX = choose_tile_lowbits_proc(fTileModeX);
+    fTileLowBitsProcY = choose_tile_lowbits_proc(fTileModeY);
     return GeneralXY_Procs[index];
 }
-
diff --git a/src/core/SkBitmapProcState_matrix_clamp.h b/src/core/SkBitmapProcState_matrix_clamp.h
deleted file mode 100644
index 06bc0fa..0000000
--- a/src/core/SkBitmapProcState_matrix_clamp.h
+++ /dev/null
@@ -1,924 +0,0 @@
-/* NEON optimized code (C) COPYRIGHT 2009 Motorola
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/*
- * Modifications done in-house at Motorola 
- *
- * this is a clone of SkBitmapProcState_matrix.h
- * and has been tuned to work with the NEON unit.
- *
- * Still going back and forth between whether this approach
- * (clone the entire SkBitmapProcState_matrix.h file or
- * if I should put just the modified routines in here and
- * then use a construct like #define DONT_DO_THIS_FUNCTION or
- * something like that...
- *
- * This is for the ClampX_ClampY instance
- *
- */
-
-
-#if	!defined(__ARM_HAVE_NEON)
-#error	this file can be used only when the NEON unit is enabled
-#endif
-
-#include <arm_neon.h>
-
-/*
- * This has been modified on the knowledge that (at the time)
- * we had the following macro definitions in the parent file
- *
- * #define MAKENAME(suffix)        ClampX_ClampY ## suffix
- * #define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
- * #define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
- * #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
- * #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
- * #define CHECK_FOR_DECAL
- */
-
-/* SkClampMax(val,max) -- bound to 0..max */
-
-#define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale_neon)
-#define SCALE_FILTER_NAME       MAKENAME(_filter_scale_neon)
-#define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine_neon)
-#define AFFINE_FILTER_NAME      MAKENAME(_filter_affine_neon)
-#define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp_neon)
-#define PERSP_FILTER_NAME       MAKENAME(_filter_persp_neon)
-
-#define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
-#define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)
-
-#ifndef PREAMBLE
-    #define PREAMBLE(state)
-    #define PREAMBLE_PARAM_X
-    #define PREAMBLE_PARAM_Y
-    #define PREAMBLE_ARG_X
-    #define PREAMBLE_ARG_Y
-#endif
-
-static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
-                                uint32_t xy[], int count, int x, int y) {
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask)) == 0);
-
-    PREAMBLE(s);
-    // we store y, x, x, x, x, x
-
-    const unsigned maxX = s.fBitmap->width() - 1;
-    SkFixed fx;
-    {
-        SkPoint pt;
-        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
-                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
-        fx = SkScalarToFixed(pt.fY);
-        const unsigned maxY = s.fBitmap->height() - 1;
-        *xy++ = TILEY_PROCF(fx, maxY);
-        fx = SkScalarToFixed(pt.fX);
-    }
-    
-    if (0 == maxX) {
-        // all of the following X values must be 0
-        memset(xy, 0, count * sizeof(uint16_t));
-        return;
-    }
-
-    const SkFixed dx = s.fInvSx;
-
-#ifdef CHECK_FOR_DECAL
-    // test if we don't need to apply the tile proc
-    if ((unsigned)(fx >> 16) <= maxX &&
-        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
-        decal_nofilter_scale(xy, fx, dx, count);
-        return;
-    }
-#endif
-
-    int i;
-
-	/* very much like done in decal_nofilter, but with
-	 * an extra clamping function applied.
-	 * TILEX_PROCF(fx,max) SkClampMax((fx)>>16, max)
-	 */
-	if (count >= 8) {
-	    /* SkFixed is 16.16 fixed point */
-	    SkFixed dx2 = dx+dx;
-	    SkFixed dx4 = dx2+dx2;
-	    SkFixed dx8 = dx4+dx4;
-
-	    /* now build fx/fx+dx/fx+2dx/fx+3dx */
-	    SkFixed fx1, fx2, fx3;
-	    int32x2_t lower, upper;
-	    int32x4_t lbase, hbase;
-	    int16_t *dst16 = (int16_t *)xy;
-
-	    fx1 = fx+dx;
-	    fx2 = fx1+dx;
-	    fx3 = fx2+dx;
-
-	    /* build my template(s) */
-	    /* avoid the 'lbase unitialized' warning */
-	    lbase = vdupq_n_s32(fx);
-	    lbase = vsetq_lane_s32(fx1, lbase, 1);
-	    lbase = vsetq_lane_s32(fx2, lbase, 2);
-	    lbase = vsetq_lane_s32(fx3, lbase, 3);
-
-	    hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));
-
-	    /* store & bump */
-	    do {
-	        int32x4_t lout;
-            int32x4_t hout;
-            int16x8_t hi16;
-
-            /* get the hi 16s of all those 32s */
-            lout = lbase;
-            hout = hbase;
-            /* this sets up all lout's then all hout's in hout */
-            asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
-            hi16 = vreinterpretq_s16_s32(hout);
-
-            /* clamp & output */
-            hi16 = vmaxq_s16(hi16, vdupq_n_s16(0));
-            hi16 = vminq_s16(hi16, vdupq_n_s16(maxX));
-            vst1q_s16(dst16, hi16);
-
-            /* but preserving base & on to the next */
-            lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
-            hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
-            dst16 += 8;
-            count -= 8;
-            fx += dx8;
-	    } while (count >= 8);
-	    xy = (uint32_t *) dst16;
-	}
-
-    uint16_t* xx = (uint16_t*)xy;
-    for (i = count; i > 0; --i) {
-        *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
-    }
-}
-
-// note: we could special-case on a matrix which is skewed in X but not Y.
-// this would require a more general setup thatn SCALE does, but could use
-// SCALE's inner loop that only looks at dx
-
-static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
-                                 uint32_t xy[], int count, int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask |
-                             SkMatrix::kAffine_Mask)) == 0);
-    
-    PREAMBLE(s);
-    SkPoint srcPt;
-    s.fInvProc(*s.fInvMatrix,
-               SkIntToScalar(x) + SK_ScalarHalf,
-               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
-    SkFixed fx = SkScalarToFixed(srcPt.fX);
-    SkFixed fy = SkScalarToFixed(srcPt.fY);
-    SkFixed dx = s.fInvSx;
-    SkFixed dy = s.fInvKy;
-    int maxX = s.fBitmap->width() - 1;
-    int maxY = s.fBitmap->height() - 1;
-    
-    /* NEON lets us do an 8x unrolling */
-    if (count >= 8) {
-        /* SkFixed is 16.16 fixed point */
-        SkFixed dx4 = dx * 4;
-        SkFixed dy4 = dy * 4;
-        SkFixed dx8 = dx * 8;
-        SkFixed dy8 = dy * 8;
-
-        int32x4_t xbase, ybase;
-        int32x4_t x2base, y2base;
-        int16_t *dst16 = (int16_t *) xy;
-
-        /* my sets of maxx/maxy for clamping */
-        int32_t maxpair = (maxX&0xffff) | ((maxY&0xffff)<<16);
-        int16x8_t maxXY = vreinterpretq_s16_s32(vdupq_n_s32(maxpair));
-
-        /* now build fx/fx+dx/fx+2dx/fx+3dx */
-        /* avoid the 'xbase unitialized' warning...*/
-        xbase = vdupq_n_s32(fx);
-        xbase = vsetq_lane_s32(fx+dx, xbase, 1);
-        xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2);
-        xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3);
-
-        /* same for fy */
-        /* avoid the 'ybase unitialized' warning...*/
-        ybase = vdupq_n_s32(fy);
-        ybase = vsetq_lane_s32(fy+dy, ybase, 1);
-        ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2);
-        ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3);
-
-        x2base = vaddq_s32(xbase, vdupq_n_s32(dx4));
-        y2base = vaddq_s32(ybase, vdupq_n_s32(dy4));
-
-        /* store & bump */
-        do {
-            int32x4_t xout, yout;
-            int32x4_t x2out, y2out;
-            int16x8_t hi16, hi16_2;
-
-            xout = xbase;
-            yout = ybase;
-
-            /* overlay y's low16 with hi16 from x */
-            /* so we properly shifted xyxyxyxy */
-            yout = vsriq_n_s32(yout, xout, 16);
-            hi16 = vreinterpretq_s16_s32 (yout);
-
-            /* do the clamping; both guys get 0's */
-            hi16 = vmaxq_s16 (hi16, vdupq_n_s16(0));
-            hi16 = vminq_s16 (hi16, maxXY);
-
-            vst1q_s16 (dst16, hi16);
-
-            /* and for the other 4 pieces of this iteration */
-            x2out = x2base;
-            y2out = y2base;
-
-            /* overlay y's low16 with hi16 from x */
-            /* so we properly shifted xyxyxyxy */
-            y2out = vsriq_n_s32(y2out, x2out, 16);
-            hi16_2 = vreinterpretq_s16_s32 (y2out);
-
-            /* do the clamping; both guys get 0's */
-            hi16_2 = vmaxq_s16 (hi16_2, vdupq_n_s16(0));
-            hi16_2 = vminq_s16 (hi16_2, maxXY);
-
-            /* RBE: gcc regenerates dst16+8 all the time instead
-             * of folding it into an addressing mode. *sigh* */
-            vst1q_s16 (dst16+8, hi16_2);
-
-            /* moving base and on to the next */
-            xbase = vaddq_s32 (xbase, vdupq_n_s32 (dx8));
-            ybase = vaddq_s32 (ybase, vdupq_n_s32 (dy8));
-            x2base = vaddq_s32 (x2base, vdupq_n_s32 (dx8));
-            y2base = vaddq_s32 (y2base, vdupq_n_s32 (dy8));
-
-            dst16 += 16;		/* 8x32 aka 16x16 */
-            count -= 8;
-            fx += dx8;
-            fy += dy8;
-        } while (count >= 8);
-        xy = (uint32_t *) dst16;
-    }
-
-    for (int i = count; i > 0; --i) {
-        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
-        fx += dx; fy += dy;
-    }
-}
-
-#undef	DEBUG_PERSP_NOFILTER
-
-static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
-                                uint32_t* SK_RESTRICT xy,
-                                int count, int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
-    
-    PREAMBLE(s);
-    /* max{X,Y} are int here, but later shown/assumed to fit in 16 bits */
-    int maxX = s.fBitmap->width() - 1;
-    int maxY = s.fBitmap->height() - 1;
-    
-    SkPerspIter   iter(*s.fInvMatrix,
-                       SkIntToScalar(x) + SK_ScalarHalf,
-                       SkIntToScalar(y) + SK_ScalarHalf, count);
-    
-    while ((count = iter.next()) != 0) {
-        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
-
-#if defined(DEBUG_PERSP_NOFILTER)
-	/* debugging stuff */
-	const SkFixed *end_srcXY = srcXY + (count*2);
-	uint32_t *end_xy = xy + (count);
-	const SkFixed *base_srcXY = srcXY;
-	uint32_t *base_xy = xy;
-	int base_count = count;
-#endif
-
-#if 1
-        // 2009/9/30: crashes in ApiDemos - Views - Animation - 3D Transition
-	// 2009/10/9: reworked to avoid illegal (but allowed by gas) insn
-
-        /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1...
-         * but we immediately discard the low 16 bits...
-         * so what we're going to do is vld4, which will give us
-         * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo'
-         * parts....
-         */
-        if (count >= 8) {
-            int16_t *mysrc = (int16_t *) srcXY;
-            int16_t *mydst = (int16_t *) xy;
-            int16x4_t maxX4 = vdup_n_s16((int16_t)maxX);
-            int16x4_t maxY4 = vdup_n_s16((int16_t)maxY);
-            int16x4_t zero4 = vdup_n_s16(0);
-
-	    /* The constructs with local blocks for register assignments
-	     * and asm() instructions is to make keep any hard register
-	     * assignments to as small a scope as possible. and to avoid
-	     * burning call-preserved hard registers on the vld/vst 
-	     * instructions.
-	     */
-
-            do {
-                int16x4_t xlo, xhi, ylo, yhi;
-                int16x4_t x2lo, x2hi, y2lo, y2hi;
-
-                /* vld4 does the de-interleaving for us */
-		{
-                    register int16x4_t t_xlo asm("d0");
-                    register int16x4_t t_xhi asm("d1");
-                    register int16x4_t t_ylo asm("d2");
-                    register int16x4_t t_yhi asm("d3");
-
-                    asm ("vld4.16	{d0-d3},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
-                        : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
-                        : "r" (mysrc)
-                    );
-		    xlo = t_xlo;
-		    xhi = t_xhi;
-		    ylo = t_ylo;
-		    yhi = t_yhi;
-		}
-
-                /* clamp X>>16 (aka xhi) to 0..maxX */
-                xhi = vmax_s16(xhi, zero4);	/* now 0.. */
-                xhi = vmin_s16(xhi, maxX4);	/* now 0..maxX */
-
-                /* clamp Y>>16 (aka yhi) to 0..maxY */
-                yhi = vmax_s16(yhi, zero4);	/* now 0.. */
-                yhi = vmin_s16(yhi, maxY4);	/* now 0..maxY */
-
-		/* deal with the second set of numbers */
-		{
-                    register int16x4_t t_xlo asm("d4");
-                    register int16x4_t t_xhi asm("d5");
-                    register int16x4_t t_ylo asm("d6");
-                    register int16x4_t t_yhi asm("d7");
-
-                    /* offset == 256 bits == 32 bytes == 8 longs == 16 shorts */
-                    asm ("vld4.16	{d4-d7},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
-                        : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
-                        : "r" (mysrc+16)
-                    );
-		    x2lo = t_xlo;
-		    x2hi = t_xhi;
-		    y2lo = t_ylo;
-		    y2hi = t_yhi;
-		}
-
-                /* clamp the second 4 here */
-
-		if (0) { extern void rbe(void); rbe(); }
-
-                /* clamp X>>16 (aka xhi) to 0..maxX */
-                x2hi = vmax_s16(x2hi, zero4);	/* now 0.. */
-                x2hi = vmin_s16(x2hi, maxX4);	/* now 0..maxX */
-
-                /* clamp Y>>16 (aka yhi) to 0..maxY */
-                y2hi = vmax_s16(y2hi, zero4);	/* now 0.. */
-                y2hi = vmin_s16(y2hi, maxY4);	/* now 0..maxY */
-
-                /* we're storing as {x,y}s: x is [0], y is [1] */
-                /* we'll use vst2 to make this happen */
-
-		{
-                    register int16x4_t out_x asm("d16") = xhi;
-                    register int16x4_t out_y asm("d17") = yhi;
-
-                    asm ("vst2.16	{d16-d17},[%2]  /* xlo=%P0 xhi=%P1 */"
-			:
-			: "w" (out_x), "w" (out_y), "r" (mydst)
-			);
-		}
-		{
-                    register int16x4_t out_x asm("d18") = x2hi;
-                    register int16x4_t out_y asm("d19") = y2hi;
-
-                    asm ("vst2.16	{d18-d19},[%2]  /* xlo=%P0 xhi=%P1 */"
-			:
-			: "w" (out_x), "w" (out_y), "r" (mydst+8)
-			);
-		}
-
-                /* XXX: gcc isn't interleaving these with the NEON ops
-                 * but i think that all the scoreboarding works out */
-                count -= 8;	/* 8 iterations */
-                mysrc += 32;	/* 16 longs, aka 32 shorts */
-                mydst += 16;	/* 16 shorts, aka 8 longs */
-            } while (count >= 8);
-            /* get xy and srcXY fixed up */
-            srcXY = (const SkFixed *) mysrc;
-            xy = (uint32_t *) mydst;
-        }
-#endif
-
-        while (--count >= 0) {
-            *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
-                     TILEX_PROCF(srcXY[0], maxX);
-            srcXY += 2;
-        }
-
-#if defined(DEBUG_PERSP_NOFILTER)
-	/* for checking our NEON-produced results against vanilla code */
-	{
-	    int bad = (-1);
-	    for (int i = 0; i < base_count; i++) {
-            uint32_t val;
-            val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
-                    TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);
-
-            if (val != base_xy[i]) {
-                bad = i;
-                break;
-            }
-	    }
-	    if (bad >= 0) {
-            SkDebugf("clamp-nofilter-persp failed piece %d\n", bad);
-            SkDebugf("    maxX %08x maxY %08x\n", maxX, maxY);
-            bad -= (bad & 0x7);	       /* align */
-            for (int i = bad; i < bad + 8; i++) {
-                uint32_t val;
-                val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
-                TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);
-
-                SkDebugf("%d: got %08x want %08x srcXY[0] %08x srcXY[1] %08x\n",
-                          i, base_xy[i], val, base_srcXY[i * 2 + 0],
-                 base_srcXY[i * 2 + 1]);
-            }
-            SkDebugf ("---\n");
-	    }
-
-	    if (end_xy != xy) {
-            SkDebugf("xy ended at %08x, should be %08x\n", xy, end_xy);
-	    }
-	    if (end_srcXY != srcXY) {
-            SkDebugf("srcXY ended at %08x, should be %08x\n", srcXY,
-                      end_srcXY);
-	    }
-	}
-#endif
-    }
-}
-
-#undef	DEBUG_PERSP_NOFILTER
-
-//////////////////////////////////////////////////////////////////////////////
-
-static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
-                                          SkFixed one PREAMBLE_PARAM_Y) {
-    unsigned i = TILEY_PROCF(f, max);
-    i = (i << 4) | TILEY_LOW_BITS(f, max);
-    return (i << 14) | (TILEY_PROCF((f + one), max));
-}
-
-static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
-                                          SkFixed one PREAMBLE_PARAM_X) {
-    unsigned i = TILEX_PROCF(f, max);
-    i = (i << 4) | TILEX_LOW_BITS(f, max);
-    return (i << 14) | (TILEX_PROCF((f + one), max));
-}
-
-static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
-                              uint32_t xy[], int count, int x, int y) {
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask)) == 0);
-    SkASSERT(s.fInvKy == 0);
-
-    PREAMBLE(s);
-    
-    const unsigned maxX = s.fBitmap->width() - 1;
-    const SkFixed one = s.fFilterOneX;
-    const SkFixed dx = s.fInvSx;
-    SkFixed fx;
-
-    {
-        SkPoint pt;
-        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
-                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
-        const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
-        const unsigned maxY = s.fBitmap->height() - 1;
-        // compute our two Y values up front
-        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
-        // now initialize fx
-        fx = SkScalarToFixed(pt.fX) - (one >> 1);
-    }
-
-#ifdef CHECK_FOR_DECAL
-    // test if we don't need to apply the tile proc
-    if (dx > 0 &&
-            (unsigned)(fx >> 16) <= maxX &&
-            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
-        decal_filter_scale(xy, fx, dx, count);
-    } else
-#endif
-
-    if (count >= 4) {
-        int32x4_t wide_dx, wide_one;
-        int32x4_t wide_fx, wide_fx1, wide_i, wide_lo;
-    #if 0
-        /* verification hooks -- see below */
-        SkFixed debug_fx = fx;
-        int count_done = 0;
-    #endif
-
-        wide_fx = vdupq_n_s32(fx);
-        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
-        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
-        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
-
-        wide_dx = vdupq_n_s32(dx);
-        wide_one = vdupq_n_s32(one);
-
-        while (count >= 4) {
-            /* original expands to: 
-             * unsigned i = SkClampMax((f) >> 16, max);
-             * i = (i << 4) | (((f) >> 12) & 0xF);
-             * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
-             */
-
-            /* i = SkClampMax(f>>16, maxX) */
-            wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
-            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));
-
-            /* i<<4 | TILEX_LOW_BITS(fx) */
-            wide_lo = vshrq_n_s32(wide_fx, 12);
-            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
-
-            /* i<<14 */
-            wide_i = vshlq_n_s32(wide_i, 14);
-
-            /* SkClampMax(((f + one)) >> 16, max) */
-            wide_fx1 = vaddq_s32(wide_fx, wide_one);
-            wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
-            wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));
-
-            /* final combination */
-            wide_i = vorrq_s32(wide_i, wide_fx1);
-
-            vst1q_u32(xy, vreinterpretq_u32_s32(wide_i));
-
-    #if 0
-            /* having a verification hook is a good idea */
-            /* use debug_fx, debug_fx+dx, etc. */
-
-            for (int i=0;i<4;i++) {
-            uint32_t want = PACK_FILTER_X_NAME(debug_fx, maxX, one PREAMBLE_ARG_X);
-                    if (xy[i] != want)
-                {
-                /* print a nastygram */
-                SkDebugf("clamp-filter-scale fails\n");
-                SkDebugf("got %08x want %08x\n", xy[i], want);
-                SkDebugf("fx %08x debug_fx %08x dx %08x done %d\n",
-                fx, debug_fx, dx, count_done);
-                SkDebugf(" maxX %08x one %08x\n", maxX, one);
-
-                }
-            debug_fx += dx;
-            count_done++;
-            }
-    #endif
-            wide_fx += vdupq_n_s32(dx+dx+dx+dx);
-            fx += dx+dx+dx+dx;
-            xy += 4;
-            count -= 4;
-        }
-    }
-   
-    while (--count >= 0) {
-        *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
-        fx += dx;
-    }
-}
-
-static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
-                               uint32_t xy[], int count, int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask |
-                             SkMatrix::kAffine_Mask)) == 0);
-    
-    PREAMBLE(s);
-    SkPoint srcPt;
-    s.fInvProc(*s.fInvMatrix,
-               SkIntToScalar(x) + SK_ScalarHalf,
-               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
-    SkFixed oneX = s.fFilterOneX;
-    SkFixed oneY = s.fFilterOneY;
-    SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
-    SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
-    SkFixed dx = s.fInvSx;
-    SkFixed dy = s.fInvKy;
-    unsigned maxX = s.fBitmap->width() - 1;
-    unsigned maxY = s.fBitmap->height() - 1;
-    
-    if (count >= 4) {
-        int32x4_t wide_one, wide_i, wide_lo;
-        int32x4_t wide_dx, wide_fx, wide_onex, wide_fx1;
-        int32x4_t wide_dy, wide_fy, wide_oney, wide_fy1;
-
-    #undef	AFFINE_DEBUG
-    #if	defined(AFFINE_DEBUG)
-        SkFixed fyp = fy;
-        SkFixed fxp = fx;
-        uint32_t *xyp = xy;
-        int count_done = 0;
-    #endif
-
-        wide_fx = vdupq_n_s32(fx);
-        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
-        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
-        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
-        wide_dx = vdupq_n_s32(dx);
-
-        wide_fy = vdupq_n_s32(fy);
-        wide_fy = vsetq_lane_s32(fy+dy, wide_fy, 1);
-        wide_fy = vsetq_lane_s32(fy+dy+dy, wide_fy, 2);
-        wide_fy = vsetq_lane_s32(fy+dy+dy+dy, wide_fy, 3);
-        wide_dy = vdupq_n_s32(dy);
-
-        wide_onex = vdupq_n_s32(oneX);
-        wide_oney = vdupq_n_s32(oneY);
-
-        while (count >= 4) {
-            int32x4_t wide_x;
-            int32x4_t wide_y;
-
-            /* do the X side, then the Y side, then interleave them */
-
-            /* original expands to: 
-             * unsigned i = SkClampMax((f) >> 16, max);
-             * i = (i << 4) | (((f) >> 12) & 0xF);
-             * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
-             */
-
-            /* i = SkClampMax(f>>16, maxX) */
-            wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
-            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));
-
-            /* i<<4 | TILEX_LOW_BITS(fx) */
-            wide_lo = vshrq_n_s32(wide_fx, 12);
-            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
-
-            /* i<<14 */
-            wide_i = vshlq_n_s32(wide_i, 14);
-
-            /* SkClampMax(((f + one)) >> 16, max) */
-            wide_fx1 = vaddq_s32(wide_fx, wide_onex);
-            wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
-            wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));
-
-            /* final combination */
-            wide_x = vorrq_s32(wide_i, wide_fx1);
-
-            /* And now the Y side */
-
-            /* i = SkClampMax(f>>16, maxX) */
-            wide_i = vmaxq_s32(vshrq_n_s32(wide_fy,16), vdupq_n_s32(0));
-            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxY));
-
-            /* i<<4 | TILEX_LOW_BITS(fx) */
-            wide_lo = vshrq_n_s32(wide_fy, 12);
-            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
-
-            /* i<<14 */
-            wide_i = vshlq_n_s32(wide_i, 14);
-
-            /* SkClampMax(((f + one)) >> 16, max) */
-            wide_fy1 = vaddq_s32(wide_fy, wide_oney);
-            wide_fy1 = vmaxq_s32(vshrq_n_s32(wide_fy1,16), vdupq_n_s32(0));
-            wide_fy1 = vminq_s32(wide_fy1, vdupq_n_s32(maxY));
-
-            /* final combination */
-            wide_y = vorrq_s32(wide_i, wide_fy1);
-
-            /* interleave as YXYXYXYX as part of the storing */
-	    {
-                /* vst2.32 needs side-by-side registers */
-                register int32x4_t t_x asm("q1");
-                register int32x4_t t_y asm("q0");
-
-		t_x = wide_x; t_y = wide_y;
-                asm ("vst2.32	{q0-q1},[%2]  /* y=%q0 x=%q1 */"
-                    :
-                    : "w" (t_y), "w" (t_x), "r" (xy)
-                    );
-	    }
-
-    #if	defined(AFFINE_DEBUG)
-            /* make sure we're good here -- check the 4 we just output */
-            for (int i = 0; i<4;i++) {
-            uint32_t val;
-            val = PACK_FILTER_Y_NAME(fyp, maxY, oneY PREAMBLE_ARG_Y);
-            if (val != xy[i*2+0]) {
-                /* print a nastygram */
-                SkDebugf("clamp-filter-affine fails\n");
-                SkDebugf("[bad-y] got %08x want %08x\n", xy[i*2+0], val);
-                SkDebugf("fy %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
-                fy, fxp, fyp, dx, dy, count_done);
-                SkDebugf(" maxY %08x oneY %08x\n", maxY, oneY);
-                }
-            val = PACK_FILTER_X_NAME(fxp, maxX, oneX PREAMBLE_ARG_X);
-            if (val != xy[i*2+1]) {
-                /* print a nastygram */
-                SkDebugf("clamp-filter-affine fails\n");
-                SkDebugf("[bad-x] got %08x want %08x\n", xy[i*2+1], val);
-                SkDebugf("fx %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
-                fx, fxp, fyp, dx, dy, count_done);
-                SkDebugf(" maxX %08x one %08x\n", maxX, oneX);
-            }
-            fyp += dy;
-            fxp += dx;
-            count_done++;
-            }
-    #endif
-
-            wide_fx += vdupq_n_s32(dx+dx+dx+dx);
-            fx += dx+dx+dx+dx;
-            wide_fy += vdupq_n_s32(dy+dy+dy+dy);
-            fy += dy+dy+dy+dy;
-            xy += 8;		/* 4 x's, 4 y's */
-            count -= 4;
-        }
-    }
-
-    while (--count >= 0) {
-        /* NB: writing Y/X */
-        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
-        fy += dy;
-        *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
-        fx += dx;
-    }
-}
-
-static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
-                              uint32_t* SK_RESTRICT xy, int count,
-                              int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
-
-    PREAMBLE(s);
-    unsigned maxX = s.fBitmap->width() - 1;
-    unsigned maxY = s.fBitmap->height() - 1;
-    SkFixed oneX = s.fFilterOneX;
-    SkFixed oneY = s.fFilterOneY;
-
-    SkPerspIter   iter(*s.fInvMatrix,
-                       SkIntToScalar(x) + SK_ScalarHalf,
-                       SkIntToScalar(y) + SK_ScalarHalf, count);
-
-    while ((count = iter.next()) != 0) {
-        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
-
-        if (count >= 4) {
-            int32x4_t wide_one, wide_i, wide_lo;
-            int32x4_t wide_fx1;
-            int32x4_t wide_fy1;
-            int32x4_t wide_x, wide_y;
-
-            while (count >= 4) {
-                /* RBE: it's good, but:
-                 * -- we spill a constant that could be easily regnerated
-                 *    [perhaps tweak gcc's NEON constant costs?]
-                 */
-
-                /* load src:  x-y-x-y-x-y-x-y */
-		{
-		    register int32x4_t q0 asm ("q0");
-		    register int32x4_t q1 asm ("q1");
-                    asm ("vld2.32	{q0-q1},[%2]  /* x=%q0 y=%q1 */"
-                         : "=w" (q0), "=w" (q1)
-                         : "r" (srcXY));
-		    wide_x = q0; wide_y = q1;
-		}
-
-                /* do the X side, then the Y side, then interleave them */
-
-                wide_x = vsubq_s32(wide_x, vdupq_n_s32 (oneX>>1));
-
-                /* original expands to: 
-                 * unsigned i = SkClampMax((f) >> 16, max);
-                 * i = (i << 4) | (((f) >> 12) & 0xF);
-                 * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
-                 */
-
-                /* i = SkClampMax(f>>16, maxX) */
-                wide_i = vmaxq_s32 (vshrq_n_s32 (wide_x, 16), vdupq_n_s32 (0));
-                wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxX));
-
-                /* i<<4 | TILEX_LOW_BITS(fx) */
-                wide_lo = vshrq_n_s32 (wide_x, 12);
-                wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);
-
-                /* i<<14 */
-                wide_i = vshlq_n_s32 (wide_i, 14);
-
-                /* SkClampMax(((f + one)) >> 16, max) */
-                wide_fx1 = vaddq_s32 (wide_x, vdupq_n_s32(oneX));
-                wide_fx1 = vmaxq_s32 (vshrq_n_s32 (wide_fx1, 16), vdupq_n_s32 (0));
-                wide_fx1 = vminq_s32 (wide_fx1, vdupq_n_s32 (maxX));
-
-                /* final combination */
-                wide_x = vorrq_s32 (wide_i, wide_fx1);
-
-
-                /* And now the Y side */
-
-                wide_y = vsubq_s32(wide_y, vdupq_n_s32 (oneY>>1));
-
-                /* i = SkClampMax(f>>16, maxX) */
-                wide_i = vmaxq_s32 (vshrq_n_s32 (wide_y, 16), vdupq_n_s32 (0));
-                wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxY));
-
-                /* i<<4 | TILEX_LOW_BITS(fx) */
-                wide_lo = vshrq_n_s32 (wide_y, 12);
-                wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);
-
-                /* i<<14 */
-                wide_i = vshlq_n_s32 (wide_i, 14);
-
-                /* SkClampMax(((f + one)) >> 16, max) */
-
-                /* wide_fy1_1 and wide_fy1_2 are just temporary variables to
-                 * work-around an ICE in debug */
-                int32x4_t wide_fy1_1 = vaddq_s32 (wide_y, vdupq_n_s32(oneY));
-                int32x4_t wide_fy1_2 = vmaxq_s32 (vshrq_n_s32 (wide_fy1_1, 16),
-                                                  vdupq_n_s32 (0));
-                wide_fy1 = vminq_s32 (wide_fy1_2, vdupq_n_s32 (maxY));
-
-                /* final combination */
-                wide_y = vorrq_s32 (wide_i, wide_fy1);
-
-                /* switch them around; have to do it this way to get them
-                 * in the proper registers to match our instruction */
-
-                /* iteration bookkeeping, ahead of the asm() for scheduling */
-                srcXY += 2*4;
-                count -= 4;
-
-                /* store interleaved as y-x-y-x-y-x-y-x (NB != read order) */
-		{
-		    register int32x4_t q0 asm ("q0") = wide_y;
-		    register int32x4_t q1 asm ("q1") = wide_x;
-			
-                    asm ("vst2.32	{q0-q1},[%2]  /* y=%q0 x=%q1 */"
-                        :
-                        : "w" (q0), "w" (q1), "r" (xy));
-		}
-
-                /* on to the next iteration */
-                /* count, srcXY are handled above */
-                xy += 2*4;
-            }
-        }
-
-        /* was do-while; NEON code invalidates original count>0 assumption */
-        while (--count >= 0) { 
-	    /* NB: we read x/y, we write y/x */
-            *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
-                                       oneY PREAMBLE_ARG_Y);
-            *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
-                                       oneX PREAMBLE_ARG_X);
-            srcXY += 2;
-        }
-    }
-}
-
-static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
-    SCALE_NOFILTER_NAME,
-    SCALE_FILTER_NAME,
-    AFFINE_NOFILTER_NAME,
-    AFFINE_FILTER_NAME,
-    PERSP_NOFILTER_NAME,
-    PERSP_FILTER_NAME
-};
-
-#undef MAKENAME
-#undef TILEX_PROCF
-#undef TILEY_PROCF
-#ifdef CHECK_FOR_DECAL
-    #undef CHECK_FOR_DECAL
-#endif
-
-#undef SCALE_NOFILTER_NAME
-#undef SCALE_FILTER_NAME
-#undef AFFINE_NOFILTER_NAME
-#undef AFFINE_FILTER_NAME
-#undef PERSP_NOFILTER_NAME
-#undef PERSP_FILTER_NAME
-
-#undef PREAMBLE
-#undef PREAMBLE_PARAM_X
-#undef PREAMBLE_PARAM_Y
-#undef PREAMBLE_ARG_X
-#undef PREAMBLE_ARG_Y
-
-#undef TILEX_LOW_BITS
-#undef TILEY_LOW_BITS
diff --git a/src/core/SkBitmapProcState_matrix_repeat.h b/src/core/SkBitmapProcState_matrix_repeat.h
deleted file mode 100644
index a7fa089..0000000
--- a/src/core/SkBitmapProcState_matrix_repeat.h
+++ /dev/null
@@ -1,577 +0,0 @@
-/* NEON optimized code (C) COPYRIGHT 2009 Motorola
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- 
-/*
- * Modifications done in-house at Motorola 
- *
- * this is a clone of SkBitmapProcState_matrix.h
- * and has been tuned to work with the NEON unit.
- *
- * Still going back and forth between whether this approach
- * (clone the entire SkBitmapProcState_matrix.h file or
- * if I should put just the modified routines in here and
- * then use a construct like #define DONT_DO_THIS_FUNCTION or
- * something like that...
- *
- * This is for the RepeatX_RepeatY part of the world
- */
-
-
-#if	!defined(__ARM_HAVE_NEON)
-#error	this file can be used only when the NEON unit is enabled
-#endif
-
-#include <arm_neon.h>
-
-/*
- * This has been modified on the knowledge that (at the time)
- * we had the following macro definitions in the parent file
- *
- * #define MAKENAME(suffix)        RepeatX_RepeatY ## suffix
- * #define TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
- * #define TILEY_PROCF(fy, max)    (((fy) & 0xFFFF) * ((max) + 1) >> 16)
- * #define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
- * #define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
- */
-
-/* SkClampMax(val,max) -- bound to 0..max */
-
-#define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale_neon)
-#define SCALE_FILTER_NAME       MAKENAME(_filter_scale)
-#define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine_neon)
-#define AFFINE_FILTER_NAME      MAKENAME(_filter_affine)
-#define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp_neon)
-#define PERSP_FILTER_NAME       MAKENAME(_filter_persp)
-
-#define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
-#define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)
-
-#ifndef PREAMBLE
-    #define PREAMBLE(state)
-    #define PREAMBLE_PARAM_X
-    #define PREAMBLE_PARAM_Y
-    #define PREAMBLE_ARG_X
-    #define PREAMBLE_ARG_Y
-#endif
-
-static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
-                                uint32_t xy[], int count, int x, int y) {
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask)) == 0);
-
-    PREAMBLE(s);
-    // we store y, x, x, x, x, x
-
-    const unsigned maxX = s.fBitmap->width() - 1;
-    SkFixed fx;
-    {
-        SkPoint pt;
-        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
-                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
-        fx = SkScalarToFixed(pt.fY);
-        const unsigned maxY = s.fBitmap->height() - 1;
-        *xy++ = TILEY_PROCF(fx, maxY);
-        fx = SkScalarToFixed(pt.fX);
-    }
-    
-    if (0 == maxX) {
-        // all of the following X values must be 0
-        memset(xy, 0, count * sizeof(uint16_t));
-        return;
-    }
-
-    const SkFixed dx = s.fInvSx;
-
-#ifdef CHECK_FOR_DECAL
-    // test if we don't need to apply the tile proc
-    if ((unsigned)(fx >> 16) <= maxX &&
-        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
-        decal_nofilter_scale(xy, fx, dx, count);
-    } else
-#endif
-    {
-        int i;
-
-#if	defined(__ARM_HAVE_NEON)
-	/* RBE: very much like done in decal_nofilter ,
-	 * but some processing of the 'fx' information 
-         * TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
-	 */
-	if (count >= 8) {
-	    /* SkFixed is 16.16 fixed point */
-	    SkFixed dx2 = dx+dx;
-	    SkFixed dx4 = dx2+dx2;
-	    SkFixed dx8 = dx4+dx4;
-
-	    /* now build fx/fx+dx/fx+2dx/fx+3dx */
-	    SkFixed fx1, fx2, fx3;
-	    int32x2_t lower, upper;
-	    int32x4_t lbase, hbase;
-	    int16_t *dst16 = (int16_t *)xy;
-
-	    fx1 = fx+dx;
-	    fx2 = fx1+dx;
-	    fx3 = fx2+dx;
-
-	    lbase = vdupq_n_s32(fx);
-	    lbase = vsetq_lane_s32(fx1, lbase, 1);
-	    lbase = vsetq_lane_s32(fx2, lbase, 2);
-	    lbase = vsetq_lane_s32(fx3, lbase, 3);
-	    hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));
-
-	    /* store & bump */
-	    do
-	    {
-	        int32x4_t lout;
-		int32x4_t hout;
-		int16x8_t hi16;
-
-         	/* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */
-		/* mask to low 16 [would like to use uzp tricks) */
-	        lout = vandq_s32(lbase, vdupq_n_s32(0xffff));
-	        hout = vandq_s32(hbase, vdupq_n_s32(0xffff));
-		/* bare multiplication, not SkFixedMul */
-		lout = vmulq_s32(lout, vdupq_n_s32(maxX+1));
-		hout = vmulq_s32(hout, vdupq_n_s32(maxX+1));
-
-		/* extraction, using uzp */
-		/* this is ok -- we want all hi(lout)s then all hi(hout)s */
-		asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
-		hi16 = vreinterpretq_s16_s32(hout);
-		vst1q_s16(dst16, hi16);
-
-		/* bump our base on to the next */
-		lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
-		hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
-		dst16 += 8;
-		count -= 8;
-		fx += dx8;
-	    } while (count >= 8);
-	    xy = (uint32_t *) dst16;
-	}
-#else
-	/* simple, portable way of looking at 4 at a crack;
-	 * so gets some loop unrolling, but not full SIMD speed
-	 */
-        for (i = (count >> 2); i > 0; --i) {
-            unsigned a, b;
-            a = TILEX_PROCF(fx, maxX); fx += dx;
-            b = TILEX_PROCF(fx, maxX); fx += dx;
-#ifdef SK_CPU_BENDIAN
-            *xy++ = (a << 16) | b;
-#else
-            *xy++ = (b << 16) | a;
-#endif
-            a = TILEX_PROCF(fx, maxX); fx += dx;
-            b = TILEX_PROCF(fx, maxX); fx += dx;
-#ifdef SK_CPU_BENDIAN
-            *xy++ = (a << 16) | b;
-#else
-            *xy++ = (b << 16) | a;
-#endif
-        }
-	/* loop doesn't adjust count */
-	count -= (count>>2);
-#endif
-        uint16_t* xx = (uint16_t*)xy;
-        for (i = count; i > 0; --i) {
-            *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
-        }
-    }
-}
-
-// note: we could special-case on a matrix which is skewed in X but not Y.
-// this would require a more general setup thatn SCALE does, but could use
-// SCALE's inner loop that only looks at dx
-
-
-static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
-                                 uint32_t xy[], int count, int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask |
-                             SkMatrix::kAffine_Mask)) == 0);
-    
-    PREAMBLE(s);
-    SkPoint srcPt;
-    s.fInvProc(*s.fInvMatrix,
-               SkIntToScalar(x) + SK_ScalarHalf,
-               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
-    SkFixed fx = SkScalarToFixed(srcPt.fX);
-    SkFixed fy = SkScalarToFixed(srcPt.fY);
-    SkFixed dx = s.fInvSx;
-    SkFixed dy = s.fInvKy;
-    int maxX = s.fBitmap->width() - 1;
-    int maxY = s.fBitmap->height() - 1;
-
-#if 1
-    int ocount = count;
-    uint32_t *oxy = xy;
-    SkFixed bfx = fx, bfy=fy, bdx=dx, bdy=dy;
-#endif
-
-#if	defined(__ARM_HAVE_NEON)
-
-	if (0) { extern void rbe(void); rbe(); }
-
-	/* RBE: benchmarks show this eats up time; can we neonize it? */
-	/* RBE: very much like done in decal_nofilter ,
-	 * but some processing of the 'fx' information 
-         * TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
-	 */
-	if (count >= 4) {
-	    /* SkFixed is 16.16 fixed point */
-	    SkFixed dx4 = dx*4;
-	    SkFixed dy4 = dy*4;
-
-	    /* now build fx/fx+dx/fx+2dx/fx+3dx */
-	    int32x2_t lower, upper;
-	    int32x4_t xbase, ybase;
-	    int16_t *dst16 = (int16_t *)xy;
-
-	    /* synthesize 4x for both X and Y */
-	    xbase = vdupq_n_s32(fx);
-	    xbase = vsetq_lane_s32(fx+dx, xbase, 1);
-	    xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2);
-	    xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3);
-
-	    ybase = vdupq_n_s32(fy);
-	    ybase = vsetq_lane_s32(fy+dy, ybase, 1);
-	    ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2);
-	    ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3);
-
-	    /* store & bump */
-	    do {
-	        int32x4_t xout;
-            int32x4_t yout;
-            int16x8_t hi16;
-
-         	/* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */
-		/* mask to low 16 [would like to use uzp tricks) */
-	        xout = vandq_s32(xbase, vdupq_n_s32(0xffff));
-	        yout = vandq_s32(ybase, vdupq_n_s32(0xffff));
-		/* bare multiplication, not SkFixedMul */
-		xout = vmulq_s32(xout, vdupq_n_s32(maxX+1));
-		yout = vmulq_s32(yout, vdupq_n_s32(maxY+1));
-
-		/* put hi16 from xout over low16 from yout */
-		yout = vsriq_n_s32(yout, xout, 16);
-
-		/* and then yout has the interleaved upper 16's */
-		hi16 = vreinterpretq_s16_s32(yout);
-		vst1q_s16(dst16, hi16);
-
-		/* bump preserved base & on to the next */
-		xbase = vaddq_s32 (xbase, vdupq_n_s32(dx4));
-		ybase = vaddq_s32 (ybase, vdupq_n_s32(dy4));
-		dst16 += 8;	/* 8 x16 aka 4x32 */
-		count -= 4;
-		fx += dx4;
-		fy += dy4;
-	    } while (count >= 4);
-	    xy = (uint32_t *) dst16;
-	}
-
-#if 0
-    /* diagnostics... see whether we agree with the NEON code */
-    int bad = 0;
-    uint32_t *myxy = oxy;
-    int myi = (-1);
-    SkFixed ofx = bfx, ofy= bfy, odx= bdx, ody= bdy;
-    for (myi = ocount; myi > 0; --myi) {
-	uint32_t val = (TILEY_PROCF(ofy, maxY) << 16) | TILEX_PROCF(ofx, maxX);
-	if (val != *myxy++) {
-		bad++;
-		break;
-	}
-        ofx += odx; ofy += ody;
-    }
-    if (bad) {
-        SkDebugf("repeat-nofilter-affine fails\n");
-        SkDebugf("count %d myi %d\n", ocount, myi);
-        SkDebugf(" bfx %08x, bdx %08x, bfy %08x bdy %08x\n",
-                bfx, bdx, bfy, bdy);
-        SkDebugf("maxX %08x maxY %08x\n", maxX, maxY);
-    }
-#endif
-#endif
-
-    for (int i = count; i > 0; --i) {
-	/* fx, fy, dx, dy are all 32 bit 16.16 fixed point */
-	/* (((fx) & 0xFFFF) * ((max) + 1) >> 16) */
-        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
-        fx += dx; fy += dy;
-    }
-}
-
-static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
-                                uint32_t* SK_RESTRICT xy,
-                                int count, int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
-    
-    PREAMBLE(s);
-    int maxX = s.fBitmap->width() - 1;
-    int maxY = s.fBitmap->height() - 1;
-    
-    SkPerspIter   iter(*s.fInvMatrix,
-                       SkIntToScalar(x) + SK_ScalarHalf,
-                       SkIntToScalar(y) + SK_ScalarHalf, count);
-    
-    while ((count = iter.next()) != 0) {
-        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
-
-#if	defined(__ARM_HAVE_NEON)
-	/* RBE: */
-	/* TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) */
-	/* it's a little more complicated than what I did for the
-	 * clamp case -- where I could immediately snip to the top
-	 * 16 bits and do my min/max games there.
-	 * ... might only be able to get 4x unrolling here
-	 */
-
-	/* vld2 to get a set of 32x4's ... */
-	/* do the tile[xy]_procf operations */
-	/* which includes doing vuzp to get hi16's */
-	/* store it */
-	/* -- inner loop (other than vld2) can be had from above */
-
-	/* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1...
-	 * but we immediately discard the low 16 bits...
-	 * so what we're going to do is vld4, which will give us
-	 * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo'
-	 * parts....
-	 */
-	if (0) { extern void rbe(void); rbe(); }
-	if (count >= 8) {
-	    int32_t *mysrc = (int32_t *) srcXY;
-	    int16_t *mydst = (int16_t *) xy;
-	    do {
-		int32x4_t x, y, x2, y2;
-		int16x8_t hi, hi2;
-
-		/* read array of x,y,x,y,x,y */
-	        /* vld2 does the de-interleaving for us */
-		/* isolate reg-bound scopes; gcc will minimize register
-		 * motion if possible; this ensures that we don't lose
-		 * a register across a debugging call because it happens
-		 * to be bound into a call-clobbered register
-		 */
-		{
-		    register int32x4_t q0 asm("q0");
-		    register int32x4_t q1 asm("q1");
-		    asm ("vld2.32	{q0-q1},[%2]  /* x=%q0 y=%q1 */"
-		        : "=w" (q0), "=w" (q1)
-		        : "r" (mysrc)
-		        );
-		    x = q0; y = q1;
-		}
-
-		/* offset == 256 bits == 32 bytes == 8 longs */
-		{
-		    register int32x4_t q2 asm("q2");
-		    register int32x4_t q3 asm("q3");
-		    asm ("vld2.32	{q2-q3},[%2]  /* x=%q0 y=%q1 */"
-		        : "=w" (q2), "=w" (q3)
-		        : "r" (mysrc+8)
-		        );
-		    x2 = q2; y2 = q3;
-		}
-
-         	/* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */
-		/* mask to low 16 [would like to use uzp tricks) */
-		/* bare multiplication, not SkFixedMul */
-	        x = vandq_s32(x, vdupq_n_s32(0xffff));
-		x = vmulq_s32(x, vdupq_n_s32(maxX+1));
-	        y = vandq_s32(y, vdupq_n_s32(0xffff));
-		y = vmulq_s32(y, vdupq_n_s32(maxY+1));
-
-	        x2 = vandq_s32(x2, vdupq_n_s32(0xffff));
-		x2 = vmulq_s32(x2, vdupq_n_s32(maxX+1));
-	        y2 = vandq_s32(y2, vdupq_n_s32(0xffff));
-		y2 = vmulq_s32(y2, vdupq_n_s32(maxY+1));
-
-		/* now collect interleaved high 16's */
-		/* (hi-x, hi-y)4  (hi-x2; hi-y2)4 */
-
-		/* extraction, using uzp, leaves hi16's in y */
-		y = vsriq_n_s32(y, x, 16);
-		hi = vreinterpretq_s16_s32(y);
-		vst1q_s16(mydst, hi);
-
-		/* and likewise for the second 8 entries */
-		y2 = vsriq_n_s32(y2, x2, 16);
-		hi2 = vreinterpretq_s16_s32(y2);
-		vst1q_s16(mydst+8, hi2);
-
-		/* XXX: gcc isn't interleaving these with the NEON ops
-		 * but i think that all the scoreboarding works out */
-		count -= 8;	/* 8 iterations */
-		mysrc += 16;	/* 16 longs */
-		mydst += 16;	/* 16 shorts, aka 8 longs */
-	    } while (count >= 8);
-	    /* get xy and srcXY fixed up */
-	    srcXY = (const SkFixed *) mysrc;
-	    xy = (uint32_t *) mydst;
-	}
-#endif
-        while (--count >= 0) {
-            *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
-                     TILEX_PROCF(srcXY[0], maxX);
-            srcXY += 2;
-        }
-    }
-}
-
-//////////////////////////////////////////////////////////////////////////////
-
-static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
-                                          SkFixed one PREAMBLE_PARAM_Y) {
-    unsigned i = TILEY_PROCF(f, max);
-    i = (i << 4) | TILEY_LOW_BITS(f, max);
-    return (i << 14) | (TILEY_PROCF((f + one), max));
-}
-
-static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
-                                          SkFixed one PREAMBLE_PARAM_X) {
-    unsigned i = TILEX_PROCF(f, max);
-    i = (i << 4) | TILEX_LOW_BITS(f, max);
-    return (i << 14) | (TILEX_PROCF((f + one), max));
-}
-
-static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
-                              uint32_t xy[], int count, int x, int y) {
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask)) == 0);
-    SkASSERT(s.fInvKy == 0);
-
-    PREAMBLE(s);
-    
-    const unsigned maxX = s.fBitmap->width() - 1;
-    const SkFixed one = s.fFilterOneX;
-    const SkFixed dx = s.fInvSx;
-    SkFixed fx;
-
-    {
-        SkPoint pt;
-        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
-                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
-        const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
-        const unsigned maxY = s.fBitmap->height() - 1;
-        // compute our two Y values up front
-        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
-        // now initialize fx
-        fx = SkScalarToFixed(pt.fX) - (one >> 1);
-    }
-
-#ifdef CHECK_FOR_DECAL
-    // test if we don't need to apply the tile proc
-    if (dx > 0 &&
-            (unsigned)(fx >> 16) <= maxX &&
-            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
-        decal_filter_scale(xy, fx, dx, count);
-    } else
-#endif
-    {
-        do {
-            *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
-            fx += dx;
-        } while (--count != 0);
-    }
-}
-
-static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
-                               uint32_t xy[], int count, int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
-    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
-                             SkMatrix::kScale_Mask |
-                             SkMatrix::kAffine_Mask)) == 0);
-    
-    PREAMBLE(s);
-    SkPoint srcPt;
-    s.fInvProc(*s.fInvMatrix,
-               SkIntToScalar(x) + SK_ScalarHalf,
-               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
-    SkFixed oneX = s.fFilterOneX;
-    SkFixed oneY = s.fFilterOneY;
-    SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
-    SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
-    SkFixed dx = s.fInvSx;
-    SkFixed dy = s.fInvKy;
-    unsigned maxX = s.fBitmap->width() - 1;
-    unsigned maxY = s.fBitmap->height() - 1;
-    
-    do {
-        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
-        fy += dy;
-        *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
-        fx += dx;
-    } while (--count != 0);
-}
-
-static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
-                              uint32_t* SK_RESTRICT xy, int count,
-                              int x, int y) {
-    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
-
-    extern void rbe(void);
-    
-    PREAMBLE(s);
-    unsigned maxX = s.fBitmap->width() - 1;
-    unsigned maxY = s.fBitmap->height() - 1;
-    SkFixed oneX = s.fFilterOneX;
-    SkFixed oneY = s.fFilterOneY;
-
-
-    
-    SkPerspIter   iter(*s.fInvMatrix,
-                       SkIntToScalar(x) + SK_ScalarHalf,
-                       SkIntToScalar(y) + SK_ScalarHalf, count);
-
-    while ((count = iter.next()) != 0) {
-        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
-        do {
-            *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
-                                       oneY PREAMBLE_ARG_Y);
-            *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
-                                       oneX PREAMBLE_ARG_X);
-            srcXY += 2;
-        } while (--count != 0);
-    }
-}
-
-static SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
-    SCALE_NOFILTER_NAME,
-    SCALE_FILTER_NAME,
-    AFFINE_NOFILTER_NAME,
-    AFFINE_FILTER_NAME,
-    PERSP_NOFILTER_NAME,
-    PERSP_FILTER_NAME
-};
-
-#undef MAKENAME
-#undef TILEX_PROCF
-#undef TILEY_PROCF
-#ifdef CHECK_FOR_DECAL
-    #undef CHECK_FOR_DECAL
-#endif
-
-#undef SCALE_NOFILTER_NAME
-#undef SCALE_FILTER_NAME
-#undef AFFINE_NOFILTER_NAME
-#undef AFFINE_FILTER_NAME
-#undef PERSP_NOFILTER_NAME
-#undef PERSP_FILTER_NAME
-
-#undef PREAMBLE
-#undef PREAMBLE_PARAM_X
-#undef PREAMBLE_PARAM_Y
-#undef PREAMBLE_ARG_X
-#undef PREAMBLE_ARG_Y
-
-#undef TILEX_LOW_BITS
-#undef TILEY_LOW_BITS
diff --git a/src/core/SkBitmapProcState_procs.h b/src/core/SkBitmapProcState_procs.h
new file mode 100644
index 0000000..da9ca89
--- /dev/null
+++ b/src/core/SkBitmapProcState_procs.h
@@ -0,0 +1,343 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Define NAME_WRAP(x) before including this header to perform name-wrapping
+// E.g. for ARM NEON, defined it as 'x ## _neon' to ensure all important
+// identifiers have a _neon suffix.
+#ifndef NAME_WRAP
+#error "Please define NAME_WRAP() before including this file"
+#endif
+
+// returns expanded * 5bits
+static inline uint32_t Filter_565_Expanded(unsigned x, unsigned y,
+                                           uint32_t a00, uint32_t a01,
+                                           uint32_t a10, uint32_t a11) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+
+    a00 = SkExpand_rgb_16(a00);
+    a01 = SkExpand_rgb_16(a01);
+    a10 = SkExpand_rgb_16(a10);
+    a11 = SkExpand_rgb_16(a11);
+
+    int xy = x * y >> 3;
+    return  a00 * (32 - 2*y - 2*x + xy) +
+            a01 * (2*x - xy) +
+            a10 * (2*y - xy) +
+            a11 * xy;
+}
+
+// turn an expanded 565 * 5bits into SkPMColor
+// g:11 | r:10 | x:1 | b:10
+static inline SkPMColor SkExpanded_565_To_PMColor(uint32_t c) {
+    unsigned r = (c >> 13) & 0xFF;
+    unsigned g = (c >> 24);
+    unsigned b = (c >> 2) & 0xFF;
+    return SkPackARGB32(0xFF, r, g, b);
+}
+
+// returns answer in SkPMColor format
+static inline SkPMColor Filter_4444_D32(unsigned x, unsigned y,
+                                        uint32_t a00, uint32_t a01,
+                                        uint32_t a10, uint32_t a11) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+
+    a00 = SkExpand_4444(a00);
+    a01 = SkExpand_4444(a01);
+    a10 = SkExpand_4444(a10);
+    a11 = SkExpand_4444(a11);
+
+    int xy = x * y >> 4;
+    uint32_t result =   a00 * (16 - y - x + xy) +
+                        a01 * (x - xy) +
+                        a10 * (y - xy) +
+                        a11 * xy;
+
+    return SkCompact_8888(result);
+}
+
+static inline U8CPU Filter_8(unsigned x, unsigned y,
+                             U8CPU a00, U8CPU a01,
+                             U8CPU a10, U8CPU a11) {
+    SkASSERT((unsigned)x <= 0xF);
+    SkASSERT((unsigned)y <= 0xF);
+
+    int xy = x * y;
+    unsigned result =   a00 * (256 - 16*y - 16*x + xy) +
+                        a01 * (16*x - xy) +
+                        a10 * (16*y - xy) +
+                        a11 * xy;
+
+    return result >> 8;
+}
+
+/*****************************************************************************
+ *
+ *  D32 functions
+ *
+ */
+
+// SRC == 8888
+
+#define FILTER_PROC(x, y, a, b, c, d, dst)   NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, dst)
+
+#define MAKENAME(suffix)        NAME_WRAP(S32_opaque_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src)          src
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst)   NAME_WRAP(Filter_32_alpha)(x, y, a, b, c, d, dst, alphaScale)
+
+#define MAKENAME(suffix)        NAME_WRAP(S32_alpha_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+                                SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale
+#define RETURNDST(src)          SkAlphaMulQ(src, alphaScale)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 565
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                        \
+        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
+        *(dst) = SkExpanded_565_To_PMColor(tmp);                \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(S16_opaque_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src)          SkPixel16ToPixel32(src)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                                    \
+        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);               \
+        *(dst) = SkAlphaMulQ(SkExpanded_565_To_PMColor(tmp), alphaScale);   \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(S16_alpha_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config); \
+                                SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale
+#define RETURNDST(src)          SkAlphaMulQ(SkPixel16ToPixel32(src), alphaScale)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+// SRC == Index8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst)   NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, dst)
+
+#define MAKENAME(suffix)        NAME_WRAP(SI8_opaque_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define PREAMBLE(state)         const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
+#define RETURNDST(src)          table[src]
+#define SRC_TO_FILTER(src)      table[src]
+#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
+#include "SkBitmapProcState_sample.h"
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst)   NAME_WRAP(Filter_32_alpha)(x, y, a, b, c, d, dst, alphaScale)
+
+#define MAKENAME(suffix)        NAME_WRAP(SI8_alpha_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+                                SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale; \
+                                const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
+#define RETURNDST(src)          SkAlphaMulQ(table[src], alphaScale)
+#define SRC_TO_FILTER(src)      table[src]
+#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 4444
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst)  *(dst) = Filter_4444_D32(x, y, a, b, c, d)
+
+#define MAKENAME(suffix)        NAME_WRAP(S4444_opaque_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor16
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
+                                SkASSERT(state.fAlphaScale == 256)
+#define RETURNDST(src)          SkPixel4444ToPixel32(src)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst)  \
+    do {                                                    \
+        uint32_t tmp = Filter_4444_D32(x, y, a, b, c, d);   \
+        *(dst) = SkAlphaMulQ(tmp, alphaScale);              \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(S4444_alpha_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 SkPMColor16
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_4444_Config); \
+                                SkASSERT(state.fAlphaScale < 256)
+#define PREAMBLE(state)         unsigned alphaScale = state.fAlphaScale
+#define RETURNDST(src)          SkAlphaMulQ(SkPixel4444ToPixel32(src), alphaScale)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+// SRC == A8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                        \
+        unsigned tmp = Filter_8(x, y, a, b, c, d);              \
+        *(dst) = SkAlphaMulQ(pmColor, SkAlpha255To256(tmp));    \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(SA8_alpha_D32 ## suffix)
+#define DSTSIZE                 32
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kA8_Config);
+#define PREAMBLE(state)         const SkPMColor pmColor = state.fPaintPMColor;
+#define RETURNDST(src)          SkAlphaMulQ(pmColor, SkAlpha255To256(src))
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+/*****************************************************************************
+ *
+ *  D16 functions
+ *
+ */
+
+// SRC == 8888
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                \
+        SkPMColor dstColor;                             \
+        NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, &dstColor);  \
+        (*dst) = SkPixel32ToPixel16(dstColor);          \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(S32_D16 ## suffix)
+#define DSTSIZE                 16
+#define SRCTYPE                 SkPMColor
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kARGB_8888_Config); \
+                                SkASSERT(state.fBitmap->isOpaque())
+#define RETURNDST(src)          SkPixel32ToPixel16(src)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+// SRC == 565
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                        \
+        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
+        *(dst) = SkCompact_rgb_16((tmp) >> 5);                  \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(S16_D16 ## suffix)
+#define DSTSIZE                 16
+#define SRCTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
+#define RETURNDST(src)          src
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_sample.h"
+
+// SRC == Index8
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                        \
+        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
+        *(dst) = SkCompact_rgb_16((tmp) >> 5);                  \
+    } while (0)
+
+#define MAKENAME(suffix)        NAME_WRAP(SI8_D16 ## suffix)
+#define DSTSIZE                 16
+#define SRCTYPE                 uint8_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config); \
+                                SkASSERT(state.fBitmap->isOpaque())
+#define PREAMBLE(state)         const uint16_t* SK_RESTRICT table = state.fBitmap->getColorTable()->lock16BitCache()
+#define RETURNDST(src)          table[src]
+#define SRC_TO_FILTER(src)      table[src]
+#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlock16BitCache()
+#include "SkBitmapProcState_sample.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst) \
+    do {                                                        \
+        uint32_t tmp = Filter_565_Expanded(x, y, a, b, c, d);   \
+        *(dst) = SkCompact_rgb_16((tmp) >> 5);                  \
+    } while (0)
+
+
+// clamp
+
+#define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
+#define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
+#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+
+#define MAKENAME(suffix)        NAME_WRAP(Clamp_S16_D16 ## suffix)
+#define SRCTYPE                 uint16_t
+#define DSTTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_shaderproc.h"
+
+
+#define TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
+#define TILEY_PROCF(fy, max)    (((fy) & 0xFFFF) * ((max) + 1) >> 16)
+#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+
+#define MAKENAME(suffix)        NAME_WRAP(Repeat_S16_D16 ## suffix)
+#define SRCTYPE                 uint16_t
+#define DSTTYPE                 uint16_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kRGB_565_Config)
+#define SRC_TO_FILTER(src)      src
+#include "SkBitmapProcState_shaderproc.h"
+
+
+#define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
+#define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
+#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+
+#undef FILTER_PROC
+#define FILTER_PROC(x, y, a, b, c, d, dst)   NAME_WRAP(Filter_32_opaque)(x, y, a, b, c, d, dst)
+#define MAKENAME(suffix)        NAME_WRAP(Clamp_SI8_opaque_D32 ## suffix)
+#define SRCTYPE                 uint8_t
+#define DSTTYPE                 uint32_t
+#define CHECKSTATE(state)       SkASSERT(state.fBitmap->config() == SkBitmap::kIndex8_Config)
+#define PREAMBLE(state)         const SkPMColor* SK_RESTRICT table = state.fBitmap->getColorTable()->lockColors()
+#define SRC_TO_FILTER(src)      table[src]
+#define POSTAMBLE(state)        state.fBitmap->getColorTable()->unlockColors(false)
+#include "SkBitmapProcState_shaderproc.h"
+
+#undef NAME_WRAP
diff --git a/src/core/SkBitmapProcState_sample.h b/src/core/SkBitmapProcState_sample.h
index e6b587f..b38eb77 100644
--- a/src/core/SkBitmapProcState_sample.h
+++ b/src/core/SkBitmapProcState_sample.h
@@ -23,6 +23,21 @@
     #error "unsupported DSTSIZE"
 #endif
 
+
+// declare functions externally to suppress warnings.
+void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s,
+                              const uint32_t* SK_RESTRICT xy,
+                              int count, DSTTYPE* SK_RESTRICT colors);
+void MAKENAME(_nofilter_DX)(const SkBitmapProcState& s,
+                            const uint32_t* SK_RESTRICT xy,
+                            int count, DSTTYPE* SK_RESTRICT colors);
+void MAKENAME(_filter_DX)(const SkBitmapProcState& s,
+                          const uint32_t* SK_RESTRICT xy,
+                           int count, DSTTYPE* SK_RESTRICT colors);
+void MAKENAME(_filter_DXDY)(const SkBitmapProcState& s,
+                            const uint32_t* SK_RESTRICT xy,
+                            int count, DSTTYPE* SK_RESTRICT colors);
+
 void MAKENAME(_nofilter_DXDY)(const SkBitmapProcState& s,
                               const uint32_t* SK_RESTRICT xy,
                               int count, DSTTYPE* SK_RESTRICT colors) {
@@ -38,14 +53,14 @@
 
     uint32_t XY;
     SRCTYPE src;
-    
+
     for (i = (count >> 1); i > 0; --i) {
         XY = *xy++;
         SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
                  (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
         src = ((const SRCTYPE*)(srcAddr + (XY >> 16) * rb))[XY & 0xFFFF];
         *colors++ = RETURNDST(src);
-        
+
         XY = *xy++;
         SkASSERT((XY >> 16) < (unsigned)s.fBitmap->height() &&
                  (XY & 0xFFFF) < (unsigned)s.fBitmap->width());
@@ -84,9 +99,9 @@
     srcAddr = (const SRCTYPE*)((const char*)srcAddr +
                                                 xy[0] * s.fBitmap->rowBytes());
     xy += 1;
-    
+
     SRCTYPE src;
-    
+
     if (1 == s.fBitmap->width()) {
         src = srcAddr[0];
         DSTTYPE dstValue = RETURNDST(src);
@@ -100,7 +115,7 @@
             SRCTYPE x1 = srcAddr[UNPACK_SECONDARY_SHORT(xx0)];
             SRCTYPE x2 = srcAddr[UNPACK_PRIMARY_SHORT(xx1)];
             SRCTYPE x3 = srcAddr[UNPACK_SECONDARY_SHORT(xx1)];
-            
+
             *colors++ = RETURNDST(x0);
             *colors++ = RETURNDST(x1);
             *colors++ = RETURNDST(x2);
@@ -112,7 +127,7 @@
             src = srcAddr[*xx++]; *colors++ = RETURNDST(src);
         }
     }
-    
+
 #ifdef POSTAMBLE
     POSTAMBLE(s);
 #endif
@@ -144,12 +159,12 @@
         row1 = (const SRCTYPE*)(srcAddr + (XY & 0x3FFF) * rb);
         subY = y0 & 0xF;
     }
-    
+
     do {
         uint32_t XX = *xy++;    // x0:14 | 4 | x1:14
         unsigned x0 = XX >> 14;
         unsigned x1 = XX & 0x3FFF;
-        unsigned subX = x0 & 0xF;        
+        unsigned subX = x0 & 0xF;
         x0 >>= 4;
 
         FILTER_PROC(subX, subY,
@@ -161,7 +176,7 @@
         colors += 1;
 
     } while (--count != 0);
-    
+
 #ifdef POSTAMBLE
     POSTAMBLE(s);
 #endif
@@ -172,29 +187,29 @@
     SkASSERT(count > 0 && colors != NULL);
     SkASSERT(s.fDoFilter);
     SkDEBUGCODE(CHECKSTATE(s);)
-        
+
 #ifdef PREAMBLE
         PREAMBLE(s);
 #endif
     const char* SK_RESTRICT srcAddr = (const char*)s.fBitmap->getPixels();
     int rb = s.fBitmap->rowBytes();
-    
+
     do {
         uint32_t data = *xy++;
         unsigned y0 = data >> 14;
         unsigned y1 = data & 0x3FFF;
         unsigned subY = y0 & 0xF;
         y0 >>= 4;
-        
+
         data = *xy++;
         unsigned x0 = data >> 14;
         unsigned x1 = data & 0x3FFF;
         unsigned subX = x0 & 0xF;
         x0 >>= 4;
-        
+
         const SRCTYPE* SK_RESTRICT row0 = (const SRCTYPE*)(srcAddr + y0 * rb);
         const SRCTYPE* SK_RESTRICT row1 = (const SRCTYPE*)(srcAddr + y1 * rb);
-        
+
         FILTER_PROC(subX, subY,
                     SRC_TO_FILTER(row0[x0]),
                     SRC_TO_FILTER(row0[x1]),
@@ -203,7 +218,7 @@
                     colors);
         colors += 1;
     } while (--count != 0);
-    
+
 #ifdef POSTAMBLE
     POSTAMBLE(s);
 #endif
diff --git a/src/core/SkBitmapProcState_shaderproc.h b/src/core/SkBitmapProcState_shaderproc.h
index a3a8a99..cf15a50 100644
--- a/src/core/SkBitmapProcState_shaderproc.h
+++ b/src/core/SkBitmapProcState_shaderproc.h
@@ -6,12 +6,17 @@
  * found in the LICENSE file.
  */
 
-
+#include "SkMathPriv.h"
 
 #define SCALE_FILTER_NAME       MAKENAME(_filter_DX_shaderproc)
 
-static void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y,
-                              DSTTYPE* SK_RESTRICT colors, int count) {
+// Can't be static in the general case because some of these implementations
+// will be defined and referenced in different object files.
+void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y,
+                       DSTTYPE* SK_RESTRICT colors, int count);
+
+void SCALE_FILTER_NAME(const SkBitmapProcState& s, int x, int y,
+                       DSTTYPE* SK_RESTRICT colors, int count) {
     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                              SkMatrix::kScale_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
@@ -49,7 +54,7 @@
 #ifdef PREAMBLE
     PREAMBLE(s);
 #endif
-    
+
     do {
         unsigned subX = TILEX_LOW_BITS(fx, maxX);
         unsigned x0 = TILEX_PROCF(fx, maxX);
diff --git a/src/core/SkBitmapSampler.cpp b/src/core/SkBitmapSampler.cpp
index 37cbc5a..da8aa8a 100644
--- a/src/core/SkBitmapSampler.cpp
+++ b/src/core/SkBitmapSampler.cpp
@@ -32,7 +32,7 @@
 
     fMaxX = SkToU16(bm.width() - 1);
     fMaxY = SkToU16(bm.height() - 1);
-    
+
     fTileProcX = get_tilemode_proc(tmx);
     fTileProcY = get_tilemode_proc(tmy);
 }
@@ -88,13 +88,13 @@
         // turn pixel centers into the top-left of our filter-box
         x -= SK_FixedHalf;
         y -= SK_FixedHalf;
-    
+
         // compute our pointers
         {
             const SkBitmap* bitmap = &fBitmap;
             int ix = x >> 16;
             int iy = y >> 16;
-            
+
             int             maxX = fMaxX;
             SkTileModeProc  procX = fTileProcX;
             int             maxY = fMaxY;
@@ -116,7 +116,7 @@
         SkFilterPtrProc proc = SkGetBilinearFilterPtrProc(fPtrProcTable, x, y);
         return proc(p00, p01, p10, p11);
     }
-    
+
 private:
     const SkFilterPtrProc* fPtrProcTable;
 };
@@ -136,13 +136,13 @@
         // turn pixel centers into the top-left of our filter-box
         x -= SK_FixedHalf;
         y -= SK_FixedHalf;
-    
+
         // compute our pointers
         {
             const SkBitmap* bitmap = &fBitmap;
             int ix = x >> 16;
             int iy = y >> 16;
-            
+
             int             maxX = fMaxX;
             SkTileModeProc  procX = fTileProcX;
             int             maxY = fMaxY;
@@ -167,7 +167,7 @@
 
         return SkPixel16ToPixel32((uint16_t)SkCompact_rgb_16(c));
     }
-    
+
 private:
     const SkFilterProc* fProcTable;
 };
@@ -192,12 +192,12 @@
          // turn pixel centers into the top-left of our filter-box
         x -= SK_FixedHalf;
         y -= SK_FixedHalf;
-    
+
        // compute our pointers
         {
             int ix = x >> 16;
             int iy = y >> 16;
-            
+
             int             maxX = fMaxX;
             SkTileModeProc  procX = fTileProcX;
             int             maxY = fMaxY;
@@ -225,7 +225,7 @@
 
         return c;
     }
-    
+
 private:
     const SkFilterPtrProc* fPtrProcTable;
 };
@@ -234,6 +234,7 @@
 public:
     A8_Bilinear_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
         : SkBitmapSampler(bm, true, tmx, tmy)
+        , fColor(0)
     {
         fProcTable = SkGetBilinearFilterProcTable();
     }
@@ -250,13 +251,13 @@
         // turn pixel centers into the top-left of our filter-box
         x -= SK_FixedHalf;
         y -= SK_FixedHalf;
-    
+
         // compute our pointers
         {
             const SkBitmap* bitmap = &fBitmap;
             int ix = x >> 16;
             int iy = y >> 16;
-            
+
             int             maxX = fMaxX;
             SkTileModeProc  procX = fTileProcX;
             int             maxY = fMaxY;
@@ -279,7 +280,7 @@
         int alpha = proc(*p00, *p01, *p10, *p11);
         return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
     }
-    
+
 private:
     const SkFilterProc* fProcTable;
     SkPMColor           fColor;
@@ -289,6 +290,7 @@
 public:
     A8_NoFilter_Sampler(const SkBitmap& bm, SkShader::TileMode tmx, SkShader::TileMode tmy)
         : SkBitmapSampler(bm, false, tmx, tmy)
+        , fProcTable(NULL)
     {
     }
 
@@ -301,11 +303,11 @@
     {
         int ix = SkFixedFloor(x);
         int iy = SkFixedFloor(y);
-        
+
         int alpha = *fBitmap.getAddr8(fTileProcX(ix, fMaxX), fTileProcY(iy, fMaxY));
         return SkAlphaMulQ(fColor, SkAlpha255To256(alpha));
     }
-    
+
 private:
     const SkFilterProc* fProcTable;
     SkPMColor           fColor;
@@ -412,4 +414,3 @@
     }
     return SkNEW_ARGS(SkNullBitmapSampler, (bm, doFilter, tmx, tmy));
 }
-
diff --git a/src/core/SkBitmapShader16BilerpTemplate.h b/src/core/SkBitmapShader16BilerpTemplate.h
index 0769e1c..435b806 100644
--- a/src/core/SkBitmapShader16BilerpTemplate.h
+++ b/src/core/SkBitmapShader16BilerpTemplate.h
@@ -21,7 +21,7 @@
     virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count)
     {
         SkASSERT(count > 0);
-        
+
         U8CPU alpha = this->getPaintAlpha();
 
         const SkMatrix& inv = this->getTotalInverse();
@@ -157,7 +157,7 @@
                     SkFixed fy = *srcXY++ - SK_FixedHalf;
                     int ix = fx >> 16;
                     int iy = fy >> 16;
-                    
+
                     const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
 
                     p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
@@ -207,7 +207,7 @@
             do {
                 int ix = fx >> 16;
                 int iy = fy >> 16;
-                
+
                 const BILERP_BITMAP16_SHADER_TYPE *p00, *p01, *p10, *p11;
 
                 p00 = p01 = ((const BILERP_BITMAP16_SHADER_TYPE*)((const char*)srcPixels +
diff --git a/src/core/SkBitmapShaderTemplate.h b/src/core/SkBitmapShaderTemplate.h
index bfb10d9..20e5518 100644
--- a/src/core/SkBitmapShaderTemplate.h
+++ b/src/core/SkBitmapShaderTemplate.h
@@ -29,7 +29,7 @@
                                          NOFILTER_BITMAP_SHADER_TILEMODE)
     {
     }
-    
+
     virtual bool setContext(const SkBitmap& device, const SkPaint& paint, const SkMatrix& matrix)
     {
         if (!this->INHERITED::setContext(device, paint, matrix))
@@ -221,7 +221,7 @@
             while ((count = iter.next()) != 0)
             {
                 const SkFixed* srcXY = iter.getXY();
-                
+
                 while (--count >= 0)
                 {
                     fx = *srcXY++;
diff --git a/src/core/SkBitmap_scroll.cpp b/src/core/SkBitmap_scroll.cpp
index 54110e8..36e5f05 100644
--- a/src/core/SkBitmap_scroll.cpp
+++ b/src/core/SkBitmap_scroll.cpp
@@ -11,6 +11,10 @@
 bool SkBitmap::scrollRect(const SkIRect* subset, int dx, int dy,
                           SkRegion* inval) const
 {
+    if (this->isImmutable()) {
+        return false;
+    }
+
     if (NULL != subset) {
         SkBitmap tmp;
 
@@ -39,8 +43,8 @@
     }
 
     int width = this->width();
-    int height = this->height();    
-    
+    int height = this->height();
+
     // check if there's nothing to do
     if ((dx | dy) == 0 || width <= 0 || height <= 0) {
         if (NULL != inval) {
@@ -52,23 +56,23 @@
     // compute the inval region now, before we see if there are any pixels
     if (NULL != inval) {
         SkIRect r;
-        
+
         r.set(0, 0, width, height);
         // initial the region with the entire bounds
         inval->setRect(r);
         // do the "scroll"
         r.offset(dx, dy);
-        
+
         // check if we scrolled completely away
         if (!SkIRect::Intersects(r, inval->getBounds())) {
             // inval has already been updated...
             return true;
         }
-        
+
         // compute the dirty area
         inval->op(r, SkRegion::kDifference_Op);
     }
-    
+
     SkAutoLockPixels    alp(*this);
     // if we have no pixels, just return (inval is already updated)
     // don't call readyToDraw(), since we don't require a colortable per se
@@ -113,5 +117,7 @@
         dst += rowBytes;
         src += rowBytes;
     }
+
+    this->notifyPixelsChanged();
     return true;
 }
diff --git a/src/core/SkBlitBWMaskTemplate.h b/src/core/SkBlitBWMaskTemplate.h
index ecbdfb3..15ed54e 100644
--- a/src/core/SkBlitBWMaskTemplate.h
+++ b/src/core/SkBlitBWMaskTemplate.h
@@ -119,7 +119,7 @@
             } while (--height != 0);
         }
     }
-}   
+}
 
 #undef SK_BLITBWMASK_NAME
 #undef SK_BLITBWMASK_ARGS
diff --git a/src/core/SkBlitMask.h b/src/core/SkBlitMask.h
index 9c0fe0f..ea1da25 100644
--- a/src/core/SkBlitMask.h
+++ b/src/core/SkBlitMask.h
@@ -29,14 +29,14 @@
     typedef void (*ColorProc)(void* dst, size_t dstRB,
                               const void* mask, size_t maskRB,
                               SkColor color, int width, int height);
-    
+
     /**
-     *  Function pointer that blits a row of mask(lcd16) into a row of dst 
+     *  Function pointer that blits a row of mask(lcd16) into a row of dst
      *  colorized by a single color. The number of pixels to blit is specified
      *  by width.
      */
     typedef void (*BlitLCD16RowProc)(SkPMColor dst[], const uint16_t src[],
-                                     SkColor color, int width, 
+                                     SkColor color, int width,
                                      SkPMColor opaqueDst);
 
     /**
@@ -46,19 +46,19 @@
      */
     typedef void (*RowProc)(void* dst, const void* mask,
                             const SkPMColor* src, int width);
-    
+
     /**
      *  Public entry-point to return a blitmask ColorProc.
      *  May return NULL if config or format are not supported.
      */
     static ColorProc ColorFactory(SkBitmap::Config, SkMask::Format, SkColor);
-    
+
     /**
      *  Return either platform specific optimized blitmask ColorProc,
      *  or NULL if no optimized routine is available.
      */
     static ColorProc PlatformColorProcs(SkBitmap::Config, SkMask::Format, SkColor);
-    
+
     /**
      *  Public entry-point to return a blitcolor BlitLCD16RowProc.
      */
@@ -79,7 +79,7 @@
      *  May return NULL if config or format are not supported.
      */
     static RowProc RowFactory(SkBitmap::Config, SkMask::Format, RowFlags);
-    
+
     /**
      *  Return either platform specific optimized blitmask RowProc,
      *  or NULL if no optimized routine is available.
diff --git a/src/core/SkBlitMask_D32.cpp b/src/core/SkBlitMask_D32.cpp
index c97e9e6..80cc787 100644
--- a/src/core/SkBlitMask_D32.cpp
+++ b/src/core/SkBlitMask_D32.cpp
@@ -69,7 +69,7 @@
     if (proc) {
         return proc;
     }
-    
+
     if (isOpaque) {
         return  SkBlitLCD16OpaqueRow;
     } else {
@@ -80,11 +80,11 @@
 static void D32_LCD16_Proc(void* SK_RESTRICT dst, size_t dstRB,
                            const void* SK_RESTRICT mask, size_t maskRB,
                            SkColor color, int width, int height) {
-    
-    SkPMColor*		dstRow = (SkPMColor*)dst;
+
+    SkPMColor*        dstRow = (SkPMColor*)dst;
     const uint16_t* srcRow = (const uint16_t*)mask;
     SkPMColor       opaqueDst;
-    
+
     SkBlitMask::BlitLCD16RowProc proc = NULL;
     bool isOpaque = (0xFF == SkColorGetA(color));
     proc = SkBlitMask::BlitLCD16RowFactory(isOpaque);
@@ -95,7 +95,7 @@
     } else {
         opaqueDst = 0;  // ignored
     }
-    
+
     do {
         proc(dstRow, srcRow, color, width, opaqueDst);
         dstRow = (SkPMColor*)((char*)dstRow + dstRB);
@@ -111,28 +111,28 @@
     int srcR = SkColorGetR(color);
     int srcG = SkColorGetG(color);
     int srcB = SkColorGetB(color);
-    
+
     for (int i = 0; i < width; i++) {
         SkPMColor mask = src[i];
         if (0 == mask) {
             continue;
         }
-        
+
         SkPMColor d = dst[i];
-        
+
         int maskR = SkGetPackedR32(mask);
         int maskG = SkGetPackedG32(mask);
         int maskB = SkGetPackedB32(mask);
-        
+
         // Now upscale them to 0..256, so we can use SkAlphaBlend
         maskR = SkAlpha255To256(maskR);
         maskG = SkAlpha255To256(maskG);
         maskB = SkAlpha255To256(maskB);
-        
+
         int dstR = SkGetPackedR32(d);
         int dstG = SkGetPackedG32(d);
         int dstB = SkGetPackedB32(d);
-        
+
         // LCD blitting is only supported if the dst is known/required
         // to be opaque
         dst[i] = SkPackARGB32(0xFF,
@@ -149,34 +149,34 @@
     int srcR = SkColorGetR(color);
     int srcG = SkColorGetG(color);
     int srcB = SkColorGetB(color);
-    
+
     srcA = SkAlpha255To256(srcA);
-    
+
     for (int i = 0; i < width; i++) {
         SkPMColor mask = src[i];
         if (0 == mask) {
             continue;
         }
-        
+
         SkPMColor d = dst[i];
-        
+
         int maskR = SkGetPackedR32(mask);
         int maskG = SkGetPackedG32(mask);
         int maskB = SkGetPackedB32(mask);
-        
+
         // Now upscale them to 0..256, so we can use SkAlphaBlend
         maskR = SkAlpha255To256(maskR);
         maskG = SkAlpha255To256(maskG);
         maskB = SkAlpha255To256(maskB);
-        
+
         maskR = maskR * srcA >> 8;
         maskG = maskG * srcA >> 8;
         maskB = maskB * srcA >> 8;
-        
+
         int dstR = SkGetPackedR32(d);
         int dstG = SkGetPackedG32(d);
         int dstB = SkGetPackedB32(d);
-        
+
         // LCD blitting is only supported if the dst is known/required
         // to be opaque
         dst[i] = SkPackARGB32(0xFF,
@@ -192,7 +192,7 @@
     SkASSERT(height > 0);
     SkPMColor* SK_RESTRICT dstRow = (SkPMColor*)dst;
     const SkPMColor* SK_RESTRICT srcRow = (const SkPMColor*)mask;
-    
+
     do {
         blit_lcd32_row(dstRow, srcRow, color, width);
         dstRow = (SkPMColor*)((char*)dstRow + dstRB);
@@ -206,7 +206,7 @@
     SkASSERT(height > 0);
     SkPMColor* SK_RESTRICT dstRow = (SkPMColor*)dst;
     const SkPMColor* SK_RESTRICT srcRow = (const SkPMColor*)mask;
-    
+
     do {
         blit_lcd32_opaque_row(dstRow, srcRow, color, width);
         dstRow = (SkPMColor*)((char*)dstRow + dstRB);
@@ -353,7 +353,9 @@
 static void A8_RowProc_Opaque(SkPMColor* SK_RESTRICT dst,
                               const uint8_t* SK_RESTRICT mask,
                               const SkPMColor* SK_RESTRICT src, int count) {
+#if 0 // suppress warning
     const uint32_t rbmask = gMask_00FF00FF;
+#endif
     for (int i = 0; i < count; ++i) {
         int m = mask[i];
         if (m) {
@@ -386,7 +388,7 @@
 }
 
 static int src_alpha_blend(int src, int dst, int srcA, int mask) {
-    
+
     return dst + mul(src - mul(srcA, dst), mask);
 }
 
@@ -398,7 +400,7 @@
         if (0 == m) {
             continue;
         }
-        
+
         SkPMColor s = src[i];
         SkPMColor d = dst[i];
 
@@ -415,15 +417,15 @@
         int maskR = SkGetPackedR16(m) >> (SK_R16_BITS - 5);
         int maskG = SkGetPackedG16(m) >> (SK_G16_BITS - 5);
         int maskB = SkGetPackedB16(m) >> (SK_B16_BITS - 5);
-        
+
         maskR = upscale31To255(maskR);
         maskG = upscale31To255(maskG);
         maskB = upscale31To255(maskB);
-        
+
         int dstR = SkGetPackedR32(d);
         int dstG = SkGetPackedG32(d);
         int dstB = SkGetPackedB32(d);
-        
+
         // LCD blitting is only supported if the dst is known/required
         // to be opaque
         dst[i] = SkPackARGB32(0xFF,
@@ -441,10 +443,10 @@
         if (0 == m) {
             continue;
         }
-        
+
         SkPMColor s = src[i];
         SkPMColor d = dst[i];
-        
+
         int srcR = SkGetPackedR32(s);
         int srcG = SkGetPackedG32(s);
         int srcB = SkGetPackedB32(s);
@@ -455,16 +457,16 @@
         int maskR = SkGetPackedR16(m) >> (SK_R16_BITS - 5);
         int maskG = SkGetPackedG16(m) >> (SK_G16_BITS - 5);
         int maskB = SkGetPackedB16(m) >> (SK_B16_BITS - 5);
-        
+
         // Now upscale them to 0..32, so we can use blend32
         maskR = SkUpscale31To32(maskR);
         maskG = SkUpscale31To32(maskG);
         maskB = SkUpscale31To32(maskB);
-        
+
         int dstR = SkGetPackedR32(d);
         int dstG = SkGetPackedG32(d);
         int dstB = SkGetPackedB32(d);
-        
+
         // LCD blitting is only supported if the dst is known/required
         // to be opaque
         dst[i] = SkPackARGB32(0xFF,
@@ -488,11 +490,11 @@
         int srcR = SkGetPackedR32(s);
         int srcG = SkGetPackedG32(s);
         int srcB = SkGetPackedB32(s);
-        
+
         srcA = SkAlpha255To256(srcA);
-        
+
         SkPMColor d = dst[i];
-        
+
         int maskR = SkGetPackedR32(m);
         int maskG = SkGetPackedG32(m);
         int maskB = SkGetPackedB32(m);
@@ -505,7 +507,7 @@
         int dstR = SkGetPackedR32(d);
         int dstG = SkGetPackedG32(d);
         int dstB = SkGetPackedB32(d);
-        
+
         // LCD blitting is only supported if the dst is known/required
         // to be opaque
         dst[i] = SkPackARGB32(0xFF,
@@ -523,7 +525,7 @@
         if (0 == m) {
             continue;
         }
-        
+
         SkPMColor s = src[i];
         SkPMColor d = dst[i];
 
@@ -538,7 +540,7 @@
         int dstR = SkGetPackedR32(d);
         int dstG = SkGetPackedG32(d);
         int dstB = SkGetPackedB32(d);
-        
+
         // Now upscale them to 0..256, so we can use SkAlphaBlend
         maskR = SkAlpha255To256(maskR);
         maskG = SkAlpha255To256(maskG);
@@ -564,7 +566,8 @@
 
     static const RowProc gProcs[] = {
         // need X coordinate to handle BW
-        NULL, NULL, //(RowProc)BW_RowProc_Blend,      (RowProc)BW_RowProc_Opaque,
+        false ? (RowProc)BW_RowProc_Blend : NULL, // suppress unused warning
+        false ? (RowProc)BW_RowProc_Opaque : NULL, // suppress unused warning
         (RowProc)A8_RowProc_Blend,      (RowProc)A8_RowProc_Opaque,
         (RowProc)LCD16_RowProc_Blend,   (RowProc)LCD16_RowProc_Opaque,
         (RowProc)LCD32_RowProc_Blend,   (RowProc)LCD32_RowProc_Opaque,
@@ -591,4 +594,3 @@
     }
     return NULL;
 }
-
diff --git a/src/core/SkBlitRow_D16.cpp b/src/core/SkBlitRow_D16.cpp
index c815468..309e459 100644
--- a/src/core/SkBlitRow_D16.cpp
+++ b/src/core/SkBlitRow_D16.cpp
@@ -8,6 +8,7 @@
 #include "SkBlitRow.h"
 #include "SkColorPriv.h"
 #include "SkDither.h"
+#include "SkMathPriv.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -66,7 +67,7 @@
                               const SkPMColor* SK_RESTRICT src, int count,
                                U8CPU alpha, int /*x*/, int /*y*/) {
     SkASSERT(255 > alpha);
-    
+
     if (count > 0) {
         do {
             SkPMColor sc = *src++;
@@ -85,12 +86,12 @@
 }
 
 /////////////////////////////////////////////////////////////////////////////
-                               
+
 static void S32_D565_Opaque_Dither(uint16_t* SK_RESTRICT dst,
                                      const SkPMColor* SK_RESTRICT src,
                                      int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 == alpha);
-    
+
     if (count > 0) {
         DITHER_565_SCAN(y);
         do {
@@ -108,7 +109,7 @@
                                     const SkPMColor* SK_RESTRICT src,
                                     int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 > alpha);
-    
+
     if (count > 0) {
         int scale = SkAlpha255To256(alpha);
         DITHER_565_SCAN(y);
@@ -116,7 +117,7 @@
             SkPMColor c = *src++;
             SkPMColorAssert(c);
 
-            int dither = DITHER_VALUE(x);            
+            int dither = DITHER_VALUE(x);
             int sr = SkGetPackedR32(c);
             int sg = SkGetPackedG32(c);
             int sb = SkGetPackedB32(c);
@@ -137,7 +138,7 @@
                                       const SkPMColor* SK_RESTRICT src,
                                       int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 == alpha);
-    
+
     if (count > 0) {
         DITHER_565_SCAN(y);
         do {
@@ -145,16 +146,16 @@
             SkPMColorAssert(c);
             if (c) {
                 unsigned a = SkGetPackedA32(c);
-                
+
                 int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
-                
+
                 unsigned sr = SkGetPackedR32(c);
                 unsigned sg = SkGetPackedG32(c);
                 unsigned sb = SkGetPackedB32(c);
                 sr = SkDITHER_R32_FOR_565(sr, d);
                 sg = SkDITHER_G32_FOR_565(sg, d);
                 sb = SkDITHER_B32_FOR_565(sb, d);
-                
+
                 uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
                 uint32_t dst_expanded = SkExpand_rgb_16(*dst);
                 dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
@@ -171,7 +172,7 @@
                                      const SkPMColor* SK_RESTRICT src,
                                      int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 > alpha);
-    
+
     if (count > 0) {
         int src_scale = SkAlpha255To256(alpha);
         DITHER_565_SCAN(y);
@@ -184,18 +185,18 @@
                 int sa = SkGetPackedA32(c);
                 int dst_scale = SkAlpha255To256(255 - SkAlphaMul(sa, src_scale));
                 int dither = DITHER_VALUE(x);
-                
+
                 int sr = SkGetPackedR32(c);
                 int sg = SkGetPackedG32(c);
                 int sb = SkGetPackedB32(c);
                 sr = SkDITHER_R32To565(sr, dither);
                 sg = SkDITHER_G32To565(sg, dither);
                 sb = SkDITHER_B32To565(sb, dither);
-                
+
                 int dr = (sr * src_scale + SkGetPackedR16(d) * dst_scale) >> 8;
                 int dg = (sg * src_scale + SkGetPackedG16(d) * dst_scale) >> 8;
                 int db = (sb * src_scale + SkGetPackedB16(d) * dst_scale) >> 8;
-                
+
                 *dst = SkPackRGB16(dr, dg, db);
             }
             dst += 1;
@@ -224,7 +225,7 @@
 };
 
 extern SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags);
-    
+
 SkBlitRow::Proc SkBlitRow::Factory(unsigned flags, SkBitmap::Config config) {
     SkASSERT(flags < SK_ARRAY_COUNT(gDefault_565_Procs));
     // just so we don't crash
@@ -250,4 +251,3 @@
     }
     return proc;
 }
-    
diff --git a/src/core/SkBlitRow_D32.cpp b/src/core/SkBlitRow_D32.cpp
index 97aa665..c858af6 100644
--- a/src/core/SkBlitRow_D32.cpp
+++ b/src/core/SkBlitRow_D32.cpp
@@ -12,6 +12,8 @@
 
 #define UNROLL
 
+SkBlitRow::ColorRectProc PlatformColorRectProcFactory();
+
 static void S32_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
                                  const SkPMColor* SK_RESTRICT src,
                                  int count, U8CPU alpha) {
@@ -51,8 +53,6 @@
     }
 }
 
-//#define TEST_SRC_ALPHA
-
 static void S32A_Opaque_BlitRow32(SkPMColor* SK_RESTRICT dst,
                                   const SkPMColor* SK_RESTRICT src,
                                   int count, U8CPU alpha) {
@@ -74,19 +74,7 @@
         }
 #else
         do {
-#ifdef TEST_SRC_ALPHA
-            SkPMColor sc = *src;
-            if (sc) {
-                unsigned srcA = SkGetPackedA32(sc);
-                SkPMColor result = sc;
-                if (srcA != 255) {
-                    result = SkPMSrcOver(sc, *dst);
-                }
-                *dst = result;
-            }
-#else
             *dst = SkPMSrcOver(*src, *dst);
-#endif
             src += 1;
             dst += 1;
         } while (--count > 0);
@@ -178,3 +166,84 @@
     }
 }
 
+template <size_t N> void assignLoop(SkPMColor* dst, SkPMColor color) {
+    for (size_t i = 0; i < N; ++i) {
+        *dst++ = color;
+    }
+}
+
+static inline void assignLoop(SkPMColor dst[], SkPMColor color, int count) {
+    while (count >= 4) {
+        *dst++ = color;
+        *dst++ = color;
+        *dst++ = color;
+        *dst++ = color;
+        count -= 4;
+    }
+    if (count >= 2) {
+        *dst++ = color;
+        *dst++ = color;
+        count -= 2;
+    }
+    if (count > 0) {
+        *dst++ = color;
+    }
+}
+
+void SkBlitRow::ColorRect32(SkPMColor* dst, int width, int height,
+                            size_t rowBytes, SkPMColor color) {
+    if (width <= 0 || height <= 0 || 0 == color) {
+        return;
+    }
+
+    // Just made up this value, since I saw it once in a SSE2 file.
+    // We should consider writing some tests to find the optimimal break-point
+    // (or query the Platform proc?)
+    static const int MIN_WIDTH_FOR_SCANLINE_PROC = 32;
+
+    bool isOpaque = (0xFF == SkGetPackedA32(color));
+
+    if (!isOpaque || width >= MIN_WIDTH_FOR_SCANLINE_PROC) {
+        SkBlitRow::ColorProc proc = SkBlitRow::ColorProcFactory();
+        while (--height >= 0) {
+            (*proc)(dst, dst, width, color);
+            dst = (SkPMColor*) ((char*)dst + rowBytes);
+        }
+    } else {
+        switch (width) {
+            case 1:
+                while (--height >= 0) {
+                    assignLoop<1>(dst, color);
+                    dst = (SkPMColor*) ((char*)dst + rowBytes);
+                }
+                break;
+            case 2:
+                while (--height >= 0) {
+                    assignLoop<2>(dst, color);
+                    dst = (SkPMColor*) ((char*)dst + rowBytes);
+                }
+                break;
+            case 3:
+                while (--height >= 0) {
+                    assignLoop<3>(dst, color);
+                    dst = (SkPMColor*) ((char*)dst + rowBytes);
+                }
+                break;
+            default:
+                while (--height >= 0) {
+                    assignLoop(dst, color, width);
+                    dst = (SkPMColor*) ((char*)dst + rowBytes);
+                }
+                break;
+        }
+    }
+}
+
+SkBlitRow::ColorRectProc SkBlitRow::ColorRectProcFactory() {
+    SkBlitRow::ColorRectProc proc = PlatformColorRectProcFactory();
+    if (NULL == proc) {
+        proc = ColorRect32;
+    }
+    SkASSERT(proc);
+    return proc;
+}
diff --git a/src/core/SkBlitRow_D4444.cpp b/src/core/SkBlitRow_D4444.cpp
index d66d2b7..026d156 100644
--- a/src/core/SkBlitRow_D4444.cpp
+++ b/src/core/SkBlitRow_D4444.cpp
@@ -72,7 +72,7 @@
                              const SkPMColor* SK_RESTRICT src, int count,
                              U8CPU alpha, int /*x*/, int /*y*/) {
     SkASSERT(255 > alpha);
-    
+
     if (count > 0) {
         int src_scale = SkAlpha255To256(alpha) >> 4;
         do {
@@ -91,18 +91,17 @@
 }
 
 /////////////////////////////////////////////////////////////////////////////
-                               
+
 static void S32_D4444_Opaque_Dither(uint16_t* SK_RESTRICT dst,
                                     const SkPMColor* SK_RESTRICT src,
                                     int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 == alpha);
-    
+
     if (count > 0) {
         DITHER_4444_SCAN(y);
         do {
             SkPMColor c = *src++;
             SkPMColorAssert(c);
-            SkASSERT(SkGetPackedA32(c) == 255);
 
             unsigned dither = DITHER_VALUE(x);
             *dst++ = SkDitherARGB32To4444(c, dither);
@@ -115,7 +114,7 @@
                                    const SkPMColor* SK_RESTRICT src,
                                    int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 > alpha);
-    
+
     if (count > 0) {
         int scale16 = SkAlpha255To256(alpha) >> 4;
         DITHER_4444_SCAN(y);
@@ -126,7 +125,7 @@
 
             uint32_t src_expand = SkExpand32_4444(c) * scale16;
             uint32_t dst_expand = SkExpand_4444(*dst) * (16 - scale16);
-            
+
             c = SkCompact_8888(src_expand + dst_expand); // convert back to SkPMColor
             *dst++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
             DITHER_INC_X(x);
@@ -138,16 +137,16 @@
                                      const SkPMColor* SK_RESTRICT src,
                                      int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 == alpha);
-    
+
     if (count > 0) {
         DITHER_4444_SCAN(y);
         do {
             SkPMColor c = *src++;
             SkPMColorAssert(c);
             if (c) {
-                unsigned a = SkGetPackedA32(c);                
+                unsigned a = SkGetPackedA32(c);
                 int d = SkAlphaMul(DITHER_VALUE(x), SkAlpha255To256(a));
-                
+
                 unsigned scale16 = SkAlpha255To256(255 - a) >> 4;
                 uint32_t src_expand = SkExpand_8888(c);
                 uint32_t dst_expand = SkExpand_4444(*dst) * scale16;
@@ -167,7 +166,7 @@
                                     const SkPMColor* SK_RESTRICT src,
                                     int count, U8CPU alpha, int x, int y) {
     SkASSERT(255 > alpha);
-    
+
     if (count > 0) {
         int src_scale = SkAlpha255To256(alpha) >> 4;
         DITHER_4444_SCAN(y);
@@ -177,7 +176,7 @@
             if (c) {
                 unsigned a = SkAlpha255To256(SkGetPackedA32(c));
                 int d = SkAlphaMul(DITHER_VALUE(x), a);
-                
+
                 unsigned dst_scale = 16 - SkAlphaMul(src_scale, a);
                 uint32_t src_expand = SkExpand32_4444(c) * src_scale;
                 uint32_t dst_expand = SkExpand_4444(*dst) * dst_scale;
@@ -198,24 +197,22 @@
     // no dither
     S32_D4444_Opaque,
     S32_D4444_Blend,
-    
+
     S32A_D4444_Opaque,
     S32A_D4444_Blend,
-    
+
     // dither
     S32_D4444_Opaque_Dither,
     S32_D4444_Blend_Dither,
-    
+
     S32A_D4444_Opaque_Dither,
     S32A_D4444_Blend_Dither
 };
-    
+
 SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags);
 SkBlitRow::Proc SkBlitRow_Factory_4444(unsigned flags)
 {
     SkASSERT(flags < SK_ARRAY_COUNT(gProcs4444));
-    
+
     return gProcs4444[flags];
 }
-    
-    
diff --git a/src/core/SkBlitter.cpp b/src/core/SkBlitter.cpp
index df25b7c..f43ad75 100644
--- a/src/core/SkBlitter.cpp
+++ b/src/core/SkBlitter.cpp
@@ -11,14 +11,20 @@
 #include "SkAntiRun.h"
 #include "SkColor.h"
 #include "SkColorFilter.h"
+#include "SkFilterShader.h"
+#include "SkFlattenableBuffers.h"
 #include "SkMask.h"
 #include "SkMaskFilter.h"
 #include "SkTemplatesPriv.h"
+#include "SkTLazy.h"
 #include "SkUtils.h"
 #include "SkXfermode.h"
+#include "SkString.h"
 
 SkBlitter::~SkBlitter() {}
 
+bool SkBlitter::isNullBlitter() const { return false; }
+
 const SkBitmap* SkBlitter::justAnOpaqueColor(uint32_t* value) {
     return NULL;
 }
@@ -233,6 +239,8 @@
     return NULL;
 }
 
+bool SkNullBlitter::isNullBlitter() const { return true; }
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static int compute_anti_width(const int16_t runs[]) {
@@ -495,7 +503,7 @@
             fBlitter->blitRect(r.fLeft, r.fTop, r.width(), r.height());
         } else if (1 == r.width()) {
             if (r.fLeft == x) {
-                fBlitter->blitV(r.fLeft, r.fTop, r.height(), 
+                fBlitter->blitV(r.fLeft, r.fTop, r.height(),
                                 effectiveLeftAlpha);
             } else {
                 SkASSERT(r.fLeft == x + width + 1);
@@ -569,16 +577,30 @@
     void setMask(const SkMask* mask) { fMask = mask; }
 
     virtual bool setContext(const SkBitmap& device, const SkPaint& paint,
-                            const SkMatrix& matrix) {
+                            const SkMatrix& matrix) SK_OVERRIDE {
+        if (!this->INHERITED::setContext(device, paint, matrix)) {
+            return false;
+        }
         if (fProxy) {
-            return fProxy->setContext(device, paint, matrix);
+            if (!fProxy->setContext(device, paint, matrix)) {
+                // must keep our set/end context calls balanced
+                this->INHERITED::endContext();
+                return false;
+            }
         } else {
             fPMColor = SkPreMultiplyColor(paint.getColor());
-            return this->INHERITED::setContext(device, paint, matrix);
         }
+        return true;
     }
 
-    virtual void shadeSpan(int x, int y, SkPMColor span[], int count) {
+    virtual void endContext() SK_OVERRIDE {
+        if (fProxy) {
+            fProxy->endContext();
+        }
+        this->INHERITED::endContext();
+    }
+
+    virtual void shadeSpan(int x, int y, SkPMColor span[], int count) SK_OVERRIDE {
         if (fProxy) {
             fProxy->shadeSpan(x, y, span, count);
         }
@@ -642,43 +664,37 @@
         }
     }
 
-    virtual void beginSession() {
-        this->INHERITED::beginSession();
-        if (fProxy) {
-            fProxy->beginSession();
-        }
-    }
+#ifdef SK_DEVELOPER
+    virtual void toString(SkString* str) const SK_OVERRIDE {
+        str->append("Sk3DShader: (");
 
-    virtual void endSession() {
-        if (fProxy) {
-            fProxy->endSession();
+        if (NULL != fProxy) {
+            str->append("Proxy: ");
+            fProxy->toString(str);
         }
-        this->INHERITED::endSession();
+
+        this->INHERITED::toString(str);
+
+        str->append(")");
     }
+#endif
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Sk3DShader)
 
 protected:
-    Sk3DShader(SkFlattenableReadBuffer& buffer) :
-            INHERITED(buffer) {
-        fProxy = static_cast<SkShader*>(buffer.readFlattenable());
-        fPMColor = buffer.readU32();
+    Sk3DShader(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fProxy = buffer.readFlattenableT<SkShader>();
+        fPMColor = buffer.readColor();
         fMask = NULL;
     }
 
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
         this->INHERITED::flatten(buffer);
         buffer.writeFlattenable(fProxy);
-        buffer.write32(fPMColor);
-    }
-
-    virtual Factory getFactory() {
-        return CreateProc;
+        buffer.writeColor(fPMColor);
     }
 
 private:
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(Sk3DShader, (buffer));
-    }
-
     SkShader*       fProxy;
     SkPMColor       fPMColor;
     const SkMask*   fMask;
@@ -846,24 +862,26 @@
         return blitter;
     }
 
-    SkPaint paint(origPaint);
-    SkShader* shader = paint.getShader();
-    SkColorFilter* cf = paint.getColorFilter();
-    SkXfermode* mode = paint.getXfermode();
-
+    SkShader* shader = origPaint.getShader();
+    SkColorFilter* cf = origPaint.getColorFilter();
+    SkXfermode* mode = origPaint.getXfermode();
     Sk3DShader* shader3D = NULL;
-    if (paint.getMaskFilter() != NULL &&
-            paint.getMaskFilter()->getFormat() == SkMask::k3D_Format) {
+
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+
+    if (origPaint.getMaskFilter() != NULL &&
+            origPaint.getMaskFilter()->getFormat() == SkMask::k3D_Format) {
         shader3D = SkNEW_ARGS(Sk3DShader, (shader));
-        paint.setShader(shader3D)->unref();
+        // we know we haven't initialized lazyPaint yet, so just do it
+        paint.writable()->setShader(shader3D)->unref();
         shader = shader3D;
     }
 
     if (NULL != mode) {
-        switch (interpret_xfermode(paint, mode, device.config())) {
+        switch (interpret_xfermode(*paint, mode, device.config())) {
             case kSrcOver_XferInterp:
                 mode = NULL;
-                paint.setXfermode(NULL);
+                paint.writable()->setXfermode(NULL);
                 break;
             case kSkipDrawing_XferInterp:
                 SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
@@ -873,20 +891,30 @@
         }
     }
 
+    /*
+     *  If the xfermode is CLEAR, then we can completely ignore the installed
+     *  color/shader/colorfilter, and just pretend we're SRC + color==0. This
+     *  will fall into our optimizations for SRC mode.
+     */
+    if (SkXfermode::IsMode(mode, SkXfermode::kClear_Mode)) {
+        SkPaint* p = paint.writable();
+        shader = p->setShader(NULL);
+        cf = p->setColorFilter(NULL);
+        mode = p->setXfermodeMode(SkXfermode::kSrc_Mode);
+        p->setColor(0);
+    }
+
     if (NULL == shader) {
-#ifdef SK_IGNORE_CF_OPTIMIZATION
-        if (mode || cf) {
-#else
         if (mode) {
-#endif
             // xfermodes (and filters) require shaders for our current blitters
             shader = SkNEW(SkColorShader);
-            paint.setShader(shader)->unref();
+            paint.writable()->setShader(shader)->unref();
         } else if (cf) {
             // if no shader && no xfermode, we just apply the colorfilter to
             // our color and move on.
-            paint.setColor(cf->filterColor(paint.getColor()));
-            paint.setColorFilter(NULL);
+            SkPaint* writablePaint = paint.writable();
+            writablePaint->setColor(cf->filterColor(paint->getColor()));
+            writablePaint->setColorFilter(NULL);
             cf = NULL;
         }
     }
@@ -894,52 +922,61 @@
     if (cf) {
         SkASSERT(shader);
         shader = SkNEW_ARGS(SkFilterShader, (shader, cf));
-        paint.setShader(shader)->unref();
+        paint.writable()->setShader(shader)->unref();
         // blitters should ignore the presence/absence of a filter, since
         // if there is one, the shader will take care of it.
     }
 
-    if (shader && !shader->setContext(device, paint, matrix)) {
-        return SkNEW(SkNullBlitter);
+    /*
+     *  We need to have balanced calls to the shader:
+     *      setContext
+     *      endContext
+     *  We make the first call here, in case it fails we can abort the draw.
+     *  The endContext() call is made by the blitter (assuming setContext did
+     *  not fail) in its destructor.
+     */
+    if (shader && !shader->setContext(device, *paint, matrix)) {
+        SK_PLACEMENT_NEW(blitter, SkNullBlitter, storage, storageSize);
+        return blitter;
     }
 
     switch (device.getConfig()) {
         case SkBitmap::kA1_Config:
             SK_PLACEMENT_NEW_ARGS(blitter, SkA1_Blitter,
-                                  storage, storageSize, (device, paint));
+                                  storage, storageSize, (device, *paint));
             break;
 
         case SkBitmap::kA8_Config:
             if (shader) {
                 SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Shader_Blitter,
-                                      storage, storageSize, (device, paint));
+                                      storage, storageSize, (device, *paint));
             } else {
                 SK_PLACEMENT_NEW_ARGS(blitter, SkA8_Blitter,
-                                      storage, storageSize, (device, paint));
+                                      storage, storageSize, (device, *paint));
             }
             break;
 
         case SkBitmap::kARGB_4444_Config:
-            blitter = SkBlitter_ChooseD4444(device, paint, storage, storageSize);
+            blitter = SkBlitter_ChooseD4444(device, *paint, storage, storageSize);
             break;
 
         case SkBitmap::kRGB_565_Config:
-            blitter = SkBlitter_ChooseD565(device, paint, storage, storageSize);
+            blitter = SkBlitter_ChooseD565(device, *paint, storage, storageSize);
             break;
 
         case SkBitmap::kARGB_8888_Config:
             if (shader) {
                 SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Shader_Blitter,
-                                      storage, storageSize, (device, paint));
-            } else if (paint.getColor() == SK_ColorBLACK) {
+                                      storage, storageSize, (device, *paint));
+            } else if (paint->getColor() == SK_ColorBLACK) {
                 SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Black_Blitter,
-                                      storage, storageSize, (device, paint));
-            } else if (paint.getAlpha() == 0xFF) {
+                                      storage, storageSize, (device, *paint));
+            } else if (paint->getAlpha() == 0xFF) {
                 SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Opaque_Blitter,
-                                      storage, storageSize, (device, paint));
+                                      storage, storageSize, (device, *paint));
             } else {
                 SK_PLACEMENT_NEW_ARGS(blitter, SkARGB32_Blitter,
-                                      storage, storageSize, (device, paint));
+                                      storage, storageSize, (device, *paint));
             }
             break;
 
@@ -970,14 +1007,14 @@
         : INHERITED(device) {
     fShader = paint.getShader();
     SkASSERT(fShader);
+    SkASSERT(fShader->setContextHasBeenCalled());
 
     fShader->ref();
-    fShader->beginSession();
     fShaderFlags = fShader->getFlags();
 }
 
 SkShaderBlitter::~SkShaderBlitter() {
-    fShader->endSession();
+    SkASSERT(fShader->setContextHasBeenCalled());
+    fShader->endContext();
     fShader->unref();
 }
-
diff --git a/src/core/SkBlitter.h b/src/core/SkBlitter.h
new file mode 100644
index 0000000..0a075f8
--- /dev/null
+++ b/src/core/SkBlitter.h
@@ -0,0 +1,174 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkBlitter_DEFINED
+#define SkBlitter_DEFINED
+
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+#include "SkRefCnt.h"
+#include "SkRegion.h"
+#include "SkMask.h"
+
+/** SkBlitter and its subclasses are responsible for actually writing pixels
+    into memory. Besides efficiency, they handle clipping and antialiasing.
+*/
+class SkBlitter {
+public:
+    virtual ~SkBlitter();
+
+    /// Blit a horizontal run of one or more pixels.
+    virtual void blitH(int x, int y, int width);
+    /// 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[]);
+    /// Blit a vertical run of pixels with a constant alpha value.
+    virtual void blitV(int x, int y, int height, SkAlpha alpha);
+    /// Blit a solid rectangle one or more pixels wide.
+    virtual void blitRect(int x, int y, int width, int height);
+    /** 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);
+    /// Blit a pattern of pixels defined by a rectangle-clipped mask;
+    /// typically used for text.
+    virtual void blitMask(const SkMask&, const SkIRect& clip);
+
+    /** 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.
+    */
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value);
+
+    /**
+     *  Special method just to identify the null blitter, which is returned
+     *  from Choose() if the request cannot be fulfilled. Default impl
+     *  returns false.
+     */
+    virtual bool isNullBlitter() const;
+
+    ///@name non-virtual helpers
+    void blitMaskRegion(const SkMask& mask, const SkRegion& clip);
+    void blitRectRegion(const SkIRect& rect, const SkRegion& clip);
+    void blitRegion(const SkRegion& clip);
+    ///@}
+
+    /** @name Factories
+        Return the correct blitter to use given the specified context.
+     */
+    static SkBlitter* Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& paint) {
+        return Choose(device, matrix, paint, NULL, 0);
+    }
+
+    static SkBlitter* Choose(const SkBitmap& device,
+                             const SkMatrix& matrix,
+                             const SkPaint& paint,
+                             void* storage, size_t storageSize);
+
+    static SkBlitter* ChooseSprite(const SkBitmap& device,
+                                   const SkPaint&,
+                                   const SkBitmap& src,
+                                   int left, int top,
+                                   void* storage, size_t storageSize);
+    ///@}
+
+private:
+};
+
+/** This blitter silently never draws anything.
+*/
+class SkNullBlitter : public SkBlitter {
+public:
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    virtual void blitAntiH(int x, int y, const SkAlpha[],
+                           const int16_t runs[]) SK_OVERRIDE;
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+    virtual bool isNullBlitter() const SK_OVERRIDE;
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+    called with coordinates that have been clipped by the specified clipRect.
+    This means the caller need not perform the clipping ahead of time.
+*/
+class SkRectClipBlitter : public SkBlitter {
+public:
+    void init(SkBlitter* blitter, const SkIRect& clipRect) {
+        SkASSERT(!clipRect.isEmpty());
+        fBlitter = blitter;
+        fClipRect = clipRect;
+    }
+
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    virtual void blitAntiH(int x, int y, const SkAlpha[],
+                           const int16_t runs[]) SK_OVERRIDE;
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    virtual void blitAntiRect(int x, int y, int width, int height,
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
+    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+
+private:
+    SkBlitter*  fBlitter;
+    SkIRect     fClipRect;
+};
+
+/** Wraps another (real) blitter, and ensures that the real blitter is only
+    called with coordinates that have been clipped by the specified clipRgn.
+    This means the caller need not perform the clipping ahead of time.
+*/
+class SkRgnClipBlitter : public SkBlitter {
+public:
+    void init(SkBlitter* blitter, const SkRegion* clipRgn) {
+        SkASSERT(clipRgn && !clipRgn->isEmpty());
+        fBlitter = blitter;
+        fRgn = clipRgn;
+    }
+
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    virtual void blitAntiH(int x, int y, const SkAlpha[],
+                           const int16_t runs[]) SK_OVERRIDE;
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    virtual void blitAntiRect(int x, int y, int width, int height,
+                     SkAlpha leftAlpha, SkAlpha rightAlpha) SK_OVERRIDE;
+    virtual void blitMask(const SkMask&, const SkIRect& clip) SK_OVERRIDE;
+    virtual const SkBitmap* justAnOpaqueColor(uint32_t* value) SK_OVERRIDE;
+
+private:
+    SkBlitter*      fBlitter;
+    const SkRegion* fRgn;
+};
+
+/** Factory to set up the appropriate most-efficient wrapper blitter
+    to apply a clip. Returns a pointer to a member, so lifetime must
+    be managed carefully.
+*/
+class SkBlitterClipper {
+public:
+    SkBlitter*  apply(SkBlitter* blitter, const SkRegion* clip,
+                      const SkIRect* bounds = NULL);
+
+private:
+    SkNullBlitter       fNullBlitter;
+    SkRectClipBlitter   fRectBlitter;
+    SkRgnClipBlitter    fRgnBlitter;
+};
+
+#endif
diff --git a/src/core/SkBlitter_4444.cpp b/src/core/SkBlitter_4444.cpp
index ec62523..3844b76 100644
--- a/src/core/SkBlitter_4444.cpp
+++ b/src/core/SkBlitter_4444.cpp
@@ -478,4 +478,3 @@
     }
     return blitter;
 }
-
diff --git a/src/core/SkBlitter_A1.cpp b/src/core/SkBlitter_A1.cpp
index 5638477..b64afe2 100644
--- a/src/core/SkBlitter_A1.cpp
+++ b/src/core/SkBlitter_A1.cpp
@@ -48,4 +48,3 @@
         *dst |= rite_mask;
     }
 }
-
diff --git a/src/core/SkBlitter_A8.cpp b/src/core/SkBlitter_A8.cpp
index 92a4971..4213c6b 100644
--- a/src/core/SkBlitter_A8.cpp
+++ b/src/core/SkBlitter_A8.cpp
@@ -343,4 +343,3 @@
         alpha += mask.fRowBytes;
     }
 }
-
diff --git a/src/core/SkBlitter_ARGB32.cpp b/src/core/SkBlitter_ARGB32.cpp
index 24ab330..16487ad 100644
--- a/src/core/SkBlitter_ARGB32.cpp
+++ b/src/core/SkBlitter_ARGB32.cpp
@@ -15,24 +15,24 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static void SkARGB32_Blit32(const SkBitmap& device, const SkMask& mask,
-							const SkIRect& clip, SkPMColor srcColor) {
-	U8CPU alpha = SkGetPackedA32(srcColor);
-	unsigned flags = SkBlitRow::kSrcPixelAlpha_Flag32;
-	if (alpha != 255) {
-		flags |= SkBlitRow::kGlobalAlpha_Flag32;
-	}
-	SkBlitRow::Proc32 proc = SkBlitRow::Factory32(flags);
+                            const SkIRect& clip, SkPMColor srcColor) {
+    U8CPU alpha = SkGetPackedA32(srcColor);
+    unsigned flags = SkBlitRow::kSrcPixelAlpha_Flag32;
+    if (alpha != 255) {
+        flags |= SkBlitRow::kGlobalAlpha_Flag32;
+    }
+    SkBlitRow::Proc32 proc = SkBlitRow::Factory32(flags);
 
     int x = clip.fLeft;
     int y = clip.fTop;
     int width = clip.width();
     int height = clip.height();
 
-    SkPMColor*		 dstRow = device.getAddr32(x, y);
+    SkPMColor*         dstRow = device.getAddr32(x, y);
     const SkPMColor* srcRow = reinterpret_cast<const SkPMColor*>(mask.getAddr8(x, y));
 
     do {
-		proc(dstRow, srcRow, width, alpha);
+        proc(dstRow, srcRow, width, alpha);
         dstRow = (SkPMColor*)((char*)dstRow + device.rowBytes());
         srcRow = (const SkPMColor*)((const char*)srcRow + mask.fRowBytes);
     } while (--height != 0);
@@ -53,6 +53,7 @@
 
     fPMColor = SkPackARGB32(fSrcA, fSrcR, fSrcG, fSrcB);
     fColor32Proc = SkBlitRow::ColorProcFactory();
+    fColorRect32Proc = SkBlitRow::ColorRectProcFactory();
 }
 
 const SkBitmap* SkARGB32_Blitter::justAnOpaqueColor(uint32_t* value) {
@@ -161,7 +162,7 @@
     if (mask.fFormat == SkMask::kBW_Format) {
         SkARGB32_BlendBW(fDevice, mask, clip, fPMColor, SkAlpha255To256(255 - fSrcA));
     } else if (SkMask::kARGB32_Format == mask.fFormat) {
-		SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
+        SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
     }
 }
 
@@ -172,12 +173,12 @@
     if (SkBlitMask::BlitColor(fDevice, mask, clip, fColor)) {
         return;
     }
-    
+
     if (mask.fFormat == SkMask::kBW_Format) {
         SkARGB32_BlitBW(fDevice, mask, clip, fPMColor);
     } else if (SkMask::kARGB32_Format == mask.fFormat) {
-		SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
-	}
+        SkARGB32_Blit32(fDevice, mask, clip, fPMColor);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -213,9 +214,13 @@
     uint32_t    color = fPMColor;
     size_t      rowBytes = fDevice.rowBytes();
 
-    while (--height >= 0) {
-        fColor32Proc(device, device, width, color);
-        device = (uint32_t*)((char*)device + rowBytes);
+    if (255 == SkGetPackedA32(color)) {
+        fColorRect32Proc(device, width, height, rowBytes, color);
+    } else {
+        while (--height >= 0) {
+            fColor32Proc(device, device, width, color);
+            device = (uint32_t*)((char*)device + rowBytes);
+        }
     }
 }
 
@@ -258,6 +263,17 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// Special version of SkBlitRow::Factory32 that knows we're in kSrc_Mode,
+// instead of kSrcOver_Mode
+static void blend_srcmode(SkPMColor* SK_RESTRICT device,
+                          const SkPMColor* SK_RESTRICT span,
+                          int count, U8CPU aa) {
+    int aa256 = SkAlpha255To256(aa);
+    for (int i = 0; i < count; ++i) {
+        device[i] = SkFourByteInterp256(span[i], device[i], aa256);
+    }
+}
+
 SkARGB32_Shader_Blitter::SkARGB32_Shader_Blitter(const SkBitmap& device,
                             const SkPaint& paint) : INHERITED(device, paint) {
     fBuffer = (SkPMColor*)sk_malloc_throw(device.width() * (sizeof(SkPMColor)));
@@ -273,6 +289,23 @@
     fProc32 = SkBlitRow::Factory32(flags);
     // we call this on the output from the shader + alpha from the aa buffer
     fProc32Blend = SkBlitRow::Factory32(flags | SkBlitRow::kGlobalAlpha_Flag32);
+
+    fShadeDirectlyIntoDevice = false;
+    if (fXfermode == NULL) {
+        if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) {
+            fShadeDirectlyIntoDevice = true;
+        }
+    } else {
+        SkXfermode::Mode mode;
+        if (fXfermode->asMode(&mode)) {
+            if (SkXfermode::kSrc_Mode == mode) {
+                fShadeDirectlyIntoDevice = true;
+                fProc32Blend = blend_srcmode;
+            }
+        }
+    }
+
+    fConstInY = SkToBool(fShader->getFlags() & SkShader::kConstInY32_Flag);
 }
 
 SkARGB32_Shader_Blitter::~SkARGB32_Shader_Blitter() {
@@ -285,7 +318,7 @@
 
     uint32_t*   device = fDevice.getAddr32(x, y);
 
-    if (fXfermode == NULL && (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
+    if (fShadeDirectlyIntoDevice) {
         fShader->shadeSpan(x, y, device, width);
     } else {
         SkPMColor*  span = fBuffer;
@@ -298,13 +331,89 @@
     }
 }
 
+void SkARGB32_Shader_Blitter::blitRect(int x, int y, int width, int height) {
+    SkASSERT(x >= 0 && y >= 0 &&
+             x + width <= fDevice.width() && y + height <= fDevice.height());
+
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    size_t      deviceRB = fDevice.rowBytes();
+    SkShader*   shader = fShader;
+    SkPMColor*  span = fBuffer;
+
+    if (fConstInY) {
+        if (fShadeDirectlyIntoDevice) {
+            // shade the first row directly into the device
+            fShader->shadeSpan(x, y, device, width);
+            span = device;
+            while (--height > 0) {
+                device = (uint32_t*)((char*)device + deviceRB);
+                memcpy(device, span, width << 2);
+            }
+        } else {
+            fShader->shadeSpan(x, y, span, width);
+            SkXfermode* xfer = fXfermode;
+            if (xfer) {
+                do {
+                    xfer->xfer32(device, span, width, NULL);
+                    y += 1;
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            } else {
+                SkBlitRow::Proc32 proc = fProc32;
+                do {
+                    proc(device, span, width, 255);
+                    y += 1;
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            }
+        }
+        return;
+    }
+
+    if (fShadeDirectlyIntoDevice) {
+        void* ctx;
+        SkShader::ShadeProc shadeProc = fShader->asAShadeProc(&ctx);
+        if (shadeProc) {
+            do {
+                shadeProc(ctx, x, y, device, width);
+                y += 1;
+                device = (uint32_t*)((char*)device + deviceRB);
+            } while (--height > 0);
+        } else {
+            do {
+                shader->shadeSpan(x, y, device, width);
+                y += 1;
+                device = (uint32_t*)((char*)device + deviceRB);
+            } while (--height > 0);
+        }
+    } else {
+        SkXfermode* xfer = fXfermode;
+        if (xfer) {
+            do {
+                shader->shadeSpan(x, y, span, width);
+                xfer->xfer32(device, span, width, NULL);
+                y += 1;
+                device = (uint32_t*)((char*)device + deviceRB);
+            } while (--height > 0);
+        } else {
+            SkBlitRow::Proc32 proc = fProc32;
+            do {
+                shader->shadeSpan(x, y, span, width);
+                proc(device, span, width, 255);
+                y += 1;
+                device = (uint32_t*)((char*)device + deviceRB);
+            } while (--height > 0);
+        }
+    }
+}
+
 void SkARGB32_Shader_Blitter::blitAntiH(int x, int y, const SkAlpha antialias[],
                                         const int16_t runs[]) {
     SkPMColor*  span = fBuffer;
     uint32_t*   device = fDevice.getAddr32(x, y);
     SkShader*   shader = fShader;
 
-    if (fXfermode) {
+    if (fXfermode && !fShadeDirectlyIntoDevice) {
         for (;;) {
             SkXfermode* xfer = fXfermode;
 
@@ -328,7 +437,8 @@
             antialias += count;
             x += count;
         }
-    } else if (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag) {
+    } else if (fShadeDirectlyIntoDevice ||
+               (fShader->getFlags() & SkShader::kOpaqueAlpha_Flag)) {
         for (;;) {
             int count = *runs;
             if (count <= 0) {
@@ -349,7 +459,7 @@
             antialias += count;
             x += count;
         }
-    } else {    // no xfermode but the shader not opaque
+    } else {
         for (;;) {
             int count = *runs;
             if (count <= 0) {
@@ -429,3 +539,100 @@
     }
 }
 
+void SkARGB32_Shader_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
+    SkASSERT(x >= 0 && y >= 0 && y + height <= fDevice.height());
+
+    uint32_t*   device = fDevice.getAddr32(x, y);
+    size_t      deviceRB = fDevice.rowBytes();
+    SkShader*   shader = fShader;
+
+    if (fConstInY) {
+        SkPMColor c;
+        fShader->shadeSpan(x, y, &c, 1);
+
+        if (fShadeDirectlyIntoDevice) {
+            if (255 == alpha) {
+                do {
+                    *device = c;
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            } else {
+                do {
+                    *device = SkFourByteInterp(c, *device, alpha);
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            }
+        } else {
+            SkXfermode* xfer = fXfermode;
+            if (xfer) {
+                do {
+                    xfer->xfer32(device, &c, 1, &alpha);
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            } else {
+                SkBlitRow::Proc32 proc = (255 == alpha) ? fProc32 : fProc32Blend;
+                do {
+                    proc(device, &c, 1, alpha);
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            }
+        }
+        return;
+    }
+
+    if (fShadeDirectlyIntoDevice) {
+        void* ctx;
+        SkShader::ShadeProc shadeProc = fShader->asAShadeProc(&ctx);
+        if (255 == alpha) {
+            if (shadeProc) {
+                do {
+                    shadeProc(ctx, x, y, device, 1);
+                    y += 1;
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            } else {
+                do {
+                    shader->shadeSpan(x, y, device, 1);
+                    y += 1;
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            }
+        } else {    // alpha < 255
+            SkPMColor c;
+            if (shadeProc) {
+                do {
+                    shadeProc(ctx, x, y, &c, 1);
+                    *device = SkFourByteInterp(c, *device, alpha);
+                    y += 1;
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            } else {
+                do {
+                    shader->shadeSpan(x, y, &c, 1);
+                    *device = SkFourByteInterp(c, *device, alpha);
+                    y += 1;
+                    device = (uint32_t*)((char*)device + deviceRB);
+                } while (--height > 0);
+            }
+        }
+    } else {
+        SkPMColor* span = fBuffer;
+        SkXfermode* xfer = fXfermode;
+        if (xfer) {
+            do {
+                shader->shadeSpan(x, y, span, 1);
+                xfer->xfer32(device, span, 1, &alpha);
+                y += 1;
+                device = (uint32_t*)((char*)device + deviceRB);
+            } while (--height > 0);
+        } else {
+            SkBlitRow::Proc32 proc = (255 == alpha) ? fProc32 : fProc32Blend;
+            do {
+                shader->shadeSpan(x, y, span, 1);
+                proc(device, span, 1, alpha);
+                y += 1;
+                device = (uint32_t*)((char*)device + deviceRB);
+            } while (--height > 0);
+        }
+    }
+}
diff --git a/src/core/SkBlitter_RGB16.cpp b/src/core/SkBlitter_RGB16.cpp
index 8a4d454..175409c 100644
--- a/src/core/SkBlitter_RGB16.cpp
+++ b/src/core/SkBlitter_RGB16.cpp
@@ -35,14 +35,14 @@
             count -= 1;
             SkTSwap(value, other);
         }
-        
+
         // fast way to set [value,other] pairs
 #ifdef SK_CPU_BENDIAN
         sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
 #else
         sk_memset32((uint32_t*)dst, (other << 16) | value, count >> 1);
 #endif
-        
+
         if (count & 1) {
             dst[count - 1] = value;
         }
@@ -62,7 +62,7 @@
     virtual void blitMask(const SkMask&,
                           const SkIRect&);
     virtual const SkBitmap* justAnOpaqueColor(uint32_t*);
-    
+
 protected:
     SkPMColor   fSrcColor32;
     uint32_t    fExpandedRaw16;
@@ -71,10 +71,10 @@
     uint16_t    fRawColor16;    // unscaled
     uint16_t    fRawDither16;   // unscaled
     SkBool8     fDoDither;
-    
+
     // illegal
     SkRGB16_Blitter& operator=(const SkRGB16_Blitter&);
-    
+
     typedef SkRasterBlitter INHERITED;
 };
 
@@ -88,7 +88,7 @@
     virtual void blitRect(int x, int y, int width, int height);
     virtual void blitMask(const SkMask&,
                           const SkIRect&);
-    
+
 private:
     typedef SkRGB16_Blitter INHERITED;
 };
@@ -100,7 +100,7 @@
     virtual void blitMask(const SkMask&, const SkIRect&);
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
                            const int16_t* runs);
-    
+
 private:
     typedef SkRGB16_Opaque_Blitter INHERITED;
 };
@@ -114,16 +114,16 @@
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
                            const int16_t* runs);
     virtual void blitRect(int x, int y, int width, int height);
-    
+
 protected:
     SkPMColor*      fBuffer;
     SkBlitRow::Proc fOpaqueProc;
     SkBlitRow::Proc fAlphaProc;
-    
+
 private:
     // illegal
     SkRGB16_Shader_Blitter& operator=(const SkRGB16_Shader_Blitter&);
-    
+
     typedef SkShaderBlitter INHERITED;
 };
 
@@ -135,7 +135,7 @@
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
                            const int16_t* runs);
     virtual void blitRect(int x, int y, int width, int height);
-    
+
 private:
     typedef SkRGB16_Shader_Blitter INHERITED;
 };
@@ -147,15 +147,15 @@
     virtual void blitH(int x, int y, int width);
     virtual void blitAntiH(int x, int y, const SkAlpha* antialias,
                            const int16_t* runs);
-    
+
 private:
     SkXfermode* fXfermode;
     SkPMColor*  fBuffer;
     uint8_t*    fAAExpand;
-    
+
     // illegal
     SkRGB16_Shader_Xfermode_Blitter& operator=(const SkRGB16_Shader_Xfermode_Blitter&);
-    
+
     typedef SkShaderBlitter INHERITED;
 };
 
@@ -291,7 +291,7 @@
 
 // return 1 or 0 from a bool
 static inline int Bool2Int(int value) {
-	return !!value;
+    return !!value;
 }
 
 void SkRGB16_Opaque_Blitter::blitAntiH(int x, int y,
@@ -386,20 +386,20 @@
     uint32_t    expanded32 = fExpandedRaw16;
 
 #ifdef SK_USE_NEON
-#define	UNROLL	8
+#define    UNROLL    8
     do {
         int w = width;
         if (w >= UNROLL) {
-            uint32x4_t color;		/* can use same one */
+            uint32x4_t color;        /* can use same one */
             uint32x4_t dev_lo, dev_hi;
             uint32x4_t t1, t2;
             uint32x4_t wn1, wn2;
             uint16x4_t odev_lo, odev_hi;
             uint16x4_t alpha_lo, alpha_hi;
             uint16x8_t  alpha_full;
-            
+
             color = vdupq_n_u32(expanded32);
-            
+
             do {
                 /* alpha is 8x8, widen and split to get pair of 16x4's */
                 alpha_full = vmovl_u8(vld1_u8(alpha));
@@ -407,53 +407,53 @@
                 alpha_full = vshrq_n_u16(alpha_full, 3);
                 alpha_lo = vget_low_u16(alpha_full);
                 alpha_hi = vget_high_u16(alpha_full);
-                
+
                 dev_lo = vmovl_u16(vld1_u16(device));
                 dev_hi = vmovl_u16(vld1_u16(device+4));
-                
+
                 /* unpack in 32 bits */
                 dev_lo = vorrq_u32(
                                    vandq_u32(dev_lo, vdupq_n_u32(0x0000F81F)),
-                                   vshlq_n_u32(vandq_u32(dev_lo, 
+                                   vshlq_n_u32(vandq_u32(dev_lo,
                                                          vdupq_n_u32(0x000007E0)),
                                                16)
                                    );
                 dev_hi = vorrq_u32(
                                    vandq_u32(dev_hi, vdupq_n_u32(0x0000F81F)),
-                                   vshlq_n_u32(vandq_u32(dev_hi, 
+                                   vshlq_n_u32(vandq_u32(dev_hi,
                                                          vdupq_n_u32(0x000007E0)),
                                                16)
                                    );
-                
+
                 /* blend the two */
                 t1 = vmulq_u32(vsubq_u32(color, dev_lo), vmovl_u16(alpha_lo));
                 t1 = vshrq_n_u32(t1, 5);
                 dev_lo = vaddq_u32(dev_lo, t1);
-                
+
                 t1 = vmulq_u32(vsubq_u32(color, dev_hi), vmovl_u16(alpha_hi));
                 t1 = vshrq_n_u32(t1, 5);
                 dev_hi = vaddq_u32(dev_hi, t1);
-                
+
                 /* re-compact and store */
                 wn1 = vandq_u32(dev_lo, vdupq_n_u32(0x0000F81F)),
                 wn2 = vshrq_n_u32(dev_lo, 16);
                 wn2 = vandq_u32(wn2, vdupq_n_u32(0x000007E0));
                 odev_lo = vmovn_u32(vorrq_u32(wn1, wn2));
-                
+
                 wn1 = vandq_u32(dev_hi, vdupq_n_u32(0x0000F81F)),
                 wn2 = vshrq_n_u32(dev_hi, 16);
                 wn2 = vandq_u32(wn2, vdupq_n_u32(0x000007E0));
                 odev_hi = vmovn_u32(vorrq_u32(wn1, wn2));
-                
+
                 vst1_u16(device, odev_lo);
                 vst1_u16(device+4, odev_hi);
-                
+
                 device += UNROLL;
                 alpha += UNROLL;
                 w -= UNROLL;
             } while (w >= UNROLL);
         }
-        
+
         /* residuals (which is everything if we have no neon) */
         while (w > 0) {
             *device = blend_compact(expanded32, SkExpand_rgb_16(*device),
@@ -464,7 +464,7 @@
         device = (uint16_t*)((char*)device + deviceRB);
         alpha += maskRB;
     } while (--height != 0);
-#undef	UNROLL
+#undef    UNROLL
 #else   // non-neon code
     do {
         int w = width;
@@ -482,7 +482,7 @@
 void SkRGB16_Opaque_Blitter::blitV(int x, int y, int height, SkAlpha alpha) {
     uint16_t* SK_RESTRICT device = fDevice.getAddr16(x, y);
     unsigned    deviceRB = fDevice.rowBytes();
-    
+
     // TODO: respect fDoDither
     unsigned scale5 = SkAlpha255To256(alpha) >> 3;
     uint32_t src32 =  fExpandedRaw16 * scale5;
@@ -821,7 +821,7 @@
 
     // compute SkBlitRow::Procs
     unsigned flags = 0;
-    
+
     uint32_t shaderFlags = fShaderFlags;
     // shaders take care of global alpha, so we never set it in SkBlitRow
     if (!(shaderFlags & SkShader::kOpaqueAlpha_Flag)) {
@@ -1017,7 +1017,7 @@
             SkASSERT(count > 0);
             aa = *antialias;
         }
-    } 
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1060,6 +1060,6 @@
                                   storageSize, (device, paint));
         }
     }
-    
+
     return blitter;
 }
diff --git a/src/core/SkBlitter_Sprite.cpp b/src/core/SkBlitter_Sprite.cpp
index c2a0062..db7cc69 100644
--- a/src/core/SkBlitter_Sprite.cpp
+++ b/src/core/SkBlitter_Sprite.cpp
@@ -85,4 +85,3 @@
     }
     return blitter;
 }
-
diff --git a/src/core/SkBuffer.h b/src/core/SkBuffer.h
new file mode 100644
index 0000000..9633389
--- /dev/null
+++ b/src/core/SkBuffer.h
@@ -0,0 +1,138 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkBuffer_DEFINED
+#define SkBuffer_DEFINED
+
+#include "SkScalar.h"
+
+/** \class SkRBuffer
+
+    Light weight class for reading data from a memory block.
+    The RBuffer is given the buffer to read from, with either a specified size
+    or no size (in which case no range checking is performed). It is iillegal
+    to attempt to read a value from an empty RBuffer (data == null).
+*/
+class SkRBuffer : SkNoncopyable {
+public:
+    SkRBuffer() : fData(0), fPos(0), fStop(0) {}
+    /** Initialize RBuffer with a data pointer, but no specified length.
+        This signals the RBuffer to not perform range checks during reading.
+    */
+    SkRBuffer(const void* data) {
+        fData = (const char*)data;
+        fPos = (const char*)data;
+        fStop = 0;  // no bounds checking
+    }
+    /** Initialize RBuffer with a data point and length.
+    */
+    SkRBuffer(const void* data, size_t size) {
+        SkASSERT(data != 0 || size == 0);
+        fData = (const char*)data;
+        fPos = (const char*)data;
+        fStop = (const char*)data + size;
+    }
+
+    /** Return the number of bytes that have been read from the beginning
+        of the data pointer.
+    */
+    size_t  pos() const { return fPos - fData; }
+    /** Return the total size of the data pointer. Only defined if the length was
+        specified in the constructor or in a call to reset().
+    */
+    size_t  size() const { return fStop - fData; }
+    /** Return true if the buffer has read to the end of the data pointer.
+        Only defined if the length was specified in the constructor or in a call
+        to reset(). Always returns true if the length was not specified.
+    */
+    bool    eof() const { return fPos >= fStop; }
+
+    /** Read the specified number of bytes from the data pointer. If buffer is not
+        null, copy those bytes into buffer.
+    */
+    void read(void* buffer, size_t size) {
+        if (size) {
+            this->readNoSizeCheck(buffer, size);
+        }
+    }
+
+    const void* skip(size_t size); // return start of skipped data
+    size_t  skipToAlign4();
+
+    void*       readPtr() { void* ptr; read(&ptr, sizeof(ptr)); return ptr; }
+    SkScalar    readScalar() { SkScalar x; read(&x, 4); return x; }
+    uint32_t    readU32() { uint32_t x; read(&x, 4); return x; }
+    int32_t     readS32() { int32_t x; read(&x, 4); return x; }
+    uint16_t    readU16() { uint16_t x; read(&x, 2); return x; }
+    int16_t     readS16() { int16_t x; read(&x, 2); return x; }
+    uint8_t     readU8() { uint8_t x; read(&x, 1); return x; }
+    bool        readBool() { return this->readU8() != 0; }
+
+private:
+    void    readNoSizeCheck(void* buffer, size_t size);
+
+    const char* fData;
+    const char* fPos;
+    const char* fStop;
+};
+
+/** \class SkWBuffer
+
+    Light weight class for writing data to a memory block.
+    The WBuffer is given the buffer to write into, with either a specified size
+    or no size, in which case no range checking is performed. An empty WBuffer
+    is legal, in which case no data is ever written, but the relative pos()
+    is updated.
+*/
+class SkWBuffer : SkNoncopyable {
+public:
+    SkWBuffer() : fData(0), fPos(0), fStop(0) {}
+    SkWBuffer(void* data) { reset(data); }
+    SkWBuffer(void* data, size_t size) { reset(data, size); }
+
+    void reset(void* data) {
+        fData = (char*)data;
+        fPos = (char*)data;
+        fStop = 0;  // no bounds checking
+    }
+
+    void reset(void* data, size_t size) {
+        SkASSERT(data != 0 || size == 0);
+        fData = (char*)data;
+        fPos = (char*)data;
+        fStop = (char*)data + size;
+    }
+
+    size_t  pos() const { return fPos - fData; }
+    void*   skip(size_t size); // return start of skipped data
+
+    void write(const void* buffer, size_t size) {
+        if (size) {
+            this->writeNoSizeCheck(buffer, size);
+        }
+    }
+
+    size_t  padToAlign4();
+
+    void    writePtr(const void* x) { this->writeNoSizeCheck(&x, sizeof(x)); }
+    void    writeScalar(SkScalar x) { this->writeNoSizeCheck(&x, 4); }
+    void    write32(int32_t x) { this->writeNoSizeCheck(&x, 4); }
+    void    write16(int16_t x) { this->writeNoSizeCheck(&x, 2); }
+    void    write8(int8_t x) { this->writeNoSizeCheck(&x, 1); }
+    void    writeBool(bool x) { this->write8(x); }
+
+private:
+    void    writeNoSizeCheck(const void* buffer, size_t size);
+
+    char* fData;
+    char* fPos;
+    char* fStop;
+};
+
+#endif
diff --git a/src/core/SkCanvas.cpp b/src/core/SkCanvas.cpp
index 0e26728..63f783a 100644
--- a/src/core/SkCanvas.cpp
+++ b/src/core/SkCanvas.cpp
@@ -10,17 +10,28 @@
 #include "SkCanvas.h"
 #include "SkBounder.h"
 #include "SkDevice.h"
+#include "SkDeviceImageFilterProxy.h"
 #include "SkDraw.h"
 #include "SkDrawFilter.h"
 #include "SkDrawLooper.h"
+#include "SkMetaData.h"
 #include "SkPicture.h"
 #include "SkRasterClip.h"
+#include "SkRRect.h"
 #include "SkScalarCompare.h"
+#include "SkSurface_Base.h"
 #include "SkTemplates.h"
 #include "SkTextFormatParams.h"
 #include "SkTLazy.h"
 #include "SkUtils.h"
 
+SK_DEFINE_INST_COUNT(SkBounder)
+SK_DEFINE_INST_COUNT(SkCanvas)
+SK_DEFINE_INST_COUNT(SkDrawFilter)
+
+// experimental for faster tiled drawing...
+//#define SK_ENABLE_CLIP_QUICKREJECT
+
 //#define SK_TRACE_SAVERESTORE
 
 #ifdef SK_TRACE_SAVERESTORE
@@ -44,14 +55,75 @@
     #define dec_canvas()
 #endif
 
+#ifdef SK_DEBUG
+#include "SkPixelRef.h"
+
+/*
+ *  Some pixelref subclasses can support being "locked" from another thread
+ *  during the lock-scope of skia calling them. In these instances, this balance
+ *  check will fail, but may not be indicative of a problem, so we allow a build
+ *  flag to disable this check.
+ *
+ *  Potentially another fix would be to have a (debug-only) virtual or flag on
+ *  pixelref, which could tell us at runtime if this check is valid. That would
+ *  eliminate the need for this heavy-handed build check.
+ */
+#ifdef SK_DISABLE_PIXELREF_LOCKCOUNT_BALANCE_CHECK
+class AutoCheckLockCountBalance {
+public:
+    AutoCheckLockCountBalance(const SkBitmap&) { /* do nothing */ }
+};
+#else
+class AutoCheckLockCountBalance {
+public:
+    AutoCheckLockCountBalance(const SkBitmap& bm) : fPixelRef(bm.pixelRef()) {
+        fLockCount = fPixelRef ? fPixelRef->getLockCount() : 0;
+    }
+    ~AutoCheckLockCountBalance() {
+        const int count = fPixelRef ? fPixelRef->getLockCount() : 0;
+        SkASSERT(count == fLockCount);
+    }
+
+private:
+    const SkPixelRef* fPixelRef;
+    int               fLockCount;
+};
+#endif
+
+class AutoCheckNoSetContext {
+public:
+    AutoCheckNoSetContext(const SkPaint& paint) : fPaint(paint) {
+        this->assertNoSetContext(fPaint);
+    }
+    ~AutoCheckNoSetContext() {
+        this->assertNoSetContext(fPaint);
+    }
+
+private:
+    const SkPaint& fPaint;
+
+    void assertNoSetContext(const SkPaint& paint) {
+        SkShader* s = paint.getShader();
+        if (s) {
+            SkASSERT(!s->setContextHasBeenCalled());
+        }
+    }
+};
+
+#define CHECK_LOCKCOUNT_BALANCE(bitmap)  AutoCheckLockCountBalance clcb(bitmap)
+#define CHECK_SHADER_NOSETCONTEXT(paint) AutoCheckNoSetContext     cshsc(paint)
+
+#else
+    #define CHECK_LOCKCOUNT_BALANCE(bitmap)
+    #define CHECK_SHADER_NOSETCONTEXT(paint)
+#endif
+
 typedef SkTLazy<SkPaint> SkLazyPaint;
 
-///////////////////////////////////////////////////////////////////////////////
-// Helpers for computing fast bounds for quickReject tests
-
-static SkCanvas::EdgeType paint2EdgeType(const SkPaint* paint) {
-    return paint != NULL && paint->isAntiAlias() ?
-            SkCanvas::kAA_EdgeType : SkCanvas::kBW_EdgeType;
+void SkCanvas::predrawNotify() {
+    if (fSurfaceBase) {
+        fSurfaceBase->aboutToDraw(this);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -69,15 +141,12 @@
     SkRasterClip        fClip;
     const SkMatrix*     fMatrix;
     SkPaint*            fPaint; // may be null (in the future)
-    // optional, related to canvas' external matrix
-    const SkMatrix*     fMVMatrix;
-    const SkMatrix*     fExtMatrix;
 
-    DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint)
+    DeviceCM(SkDevice* device, int x, int y, const SkPaint* paint, SkCanvas* canvas)
             : fNext(NULL) {
         if (NULL != device) {
             device->ref();
-            device->lockPixels();
+            device->onAttachToCanvas(canvas);
         }
         fDevice = device;
         fPaint = paint ? SkNEW_ARGS(SkPaint, (*paint)) : NULL;
@@ -85,7 +154,7 @@
 
     ~DeviceCM() {
         if (NULL != fDevice) {
-            fDevice->unlockPixels();
+            fDevice->onDetachFromCanvas();
             fDevice->unref();
         }
         SkDELETE(fPaint);
@@ -128,20 +197,10 @@
             SkASSERT(deviceR.contains(fClip.getBounds()));
         }
 #endif
-        // default is to assume no external matrix
-        fMVMatrix = NULL;
-        fExtMatrix = NULL;
-    }
-
-    // can only be called after calling updateMC()
-    void updateExternalMatrix(const SkMatrix& extM, const SkMatrix& extI) {
-        fMVMatrixStorage.setConcat(extI, *fMatrix);
-        fMVMatrix = &fMVMatrixStorage;
-        fExtMatrix = &extM; // assumes extM has long life-time (owned by canvas)
     }
 
 private:
-    SkMatrix    fMatrixStorage, fMVMatrixStorage;
+    SkMatrix    fMatrixStorage;
 };
 
 /*  This is the record we keep for each save/restore level in the stack.
@@ -218,7 +277,7 @@
         fCanvas = canvas;
         canvas->updateDeviceCMCache();
 
-        fClipStack = &canvas->getTotalClipStack();
+        fClipStack = &canvas->fClipStack;
         fBounder = canvas->getBounder();
         fCurrLayer = canvas->fMCRec->fTopLayer;
         fSkipEmptyClips = skipEmptyClips;
@@ -241,8 +300,6 @@
             fDevice = rec->fDevice;
             fBitmap = &fDevice->accessBitmap(true);
             fPaint  = rec->fPaint;
-            fMVMatrix = rec->fMVMatrix;
-            fExtMatrix = rec->fExtMatrix;
             SkDEBUGCODE(this->validate();)
 
             fCurrLayer = rec->fNext;
@@ -251,7 +308,6 @@
             }
             // fCurrLayer may be NULL now
 
-            fCanvas->prepareForDeviceDraw(fDevice, *fMatrix, *fClip, *fClipStack);
             return true;
         }
         return false;
@@ -277,20 +333,40 @@
 
 class AutoDrawLooper {
 public:
-    AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint) : fOrigPaint(paint) {
+    AutoDrawLooper(SkCanvas* canvas, const SkPaint& paint,
+                   bool skipLayerForImageFilter = false) : fOrigPaint(paint) {
         fCanvas = canvas;
         fLooper = paint.getLooper();
         fFilter = canvas->getDrawFilter();
         fPaint = NULL;
         fSaveCount = canvas->getSaveCount();
+        fDoClearImageFilter = false;
         fDone = false;
 
+        if (!skipLayerForImageFilter && fOrigPaint.getImageFilter()) {
+            SkPaint tmp;
+            tmp.setImageFilter(fOrigPaint.getImageFilter());
+            // it would be nice if we had a guess at the bounds, instead of null
+            (void)canvas->internalSaveLayer(NULL, &tmp,
+                                    SkCanvas::kARGB_ClipLayer_SaveFlag, true);
+            // we'll clear the imageFilter for the actual draws in next(), so
+            // it will only be applied during the restore().
+            fDoClearImageFilter = true;
+        }
+
         if (fLooper) {
             fLooper->init(canvas);
+            fIsSimple = false;
+        } else {
+            // can we be marked as simple?
+            fIsSimple = !fFilter && !fDoClearImageFilter;
         }
     }
 
     ~AutoDrawLooper() {
+        if (fDoClearImageFilter) {
+            fCanvas->internalRestore();
+        }
         SkASSERT(fCanvas->getSaveCount() == fSaveCount);
     }
 
@@ -299,7 +375,17 @@
         return *fPaint;
     }
 
-    bool next(SkDrawFilter::Type drawType);
+    bool next(SkDrawFilter::Type drawType) {
+        if (fDone) {
+            return false;
+        } else if (fIsSimple) {
+            fDone = true;
+            fPaint = &fOrigPaint;
+            return !fPaint->nothingToDraw();
+        } else {
+            return this->doNext(drawType);
+        }
+    }
 
 private:
     SkLazyPaint     fLazyPaint;
@@ -309,32 +395,43 @@
     SkDrawFilter*   fFilter;
     const SkPaint*  fPaint;
     int             fSaveCount;
+    bool            fDoClearImageFilter;
     bool            fDone;
+    bool            fIsSimple;
+
+    bool doNext(SkDrawFilter::Type drawType);
 };
 
-bool AutoDrawLooper::next(SkDrawFilter::Type drawType) {
+bool AutoDrawLooper::doNext(SkDrawFilter::Type drawType) {
     fPaint = NULL;
-    if (fDone) {
-        return false;
+    SkASSERT(!fIsSimple);
+    SkASSERT(fLooper || fFilter || fDoClearImageFilter);
+
+    SkPaint* paint = fLazyPaint.set(fOrigPaint);
+
+    if (fDoClearImageFilter) {
+        paint->setImageFilter(NULL);
     }
 
-    if (fLooper || fFilter) {
-        SkPaint* paint = fLazyPaint.set(fOrigPaint);
-        if (fLooper && !fLooper->next(fCanvas, paint)) {
+    if (fLooper && !fLooper->next(fCanvas, paint)) {
+        fDone = true;
+        return false;
+    }
+    if (fFilter) {
+        if (!fFilter->filter(paint, drawType)) {
             fDone = true;
             return false;
         }
-        if (fFilter) {
-            fFilter->filter(paint, drawType);
-            if (NULL == fLooper) {
-                // no looper means we only draw once
-                fDone = true;
-            }
+        if (NULL == fLooper) {
+            // no looper means we only draw once
+            fDone = true;
         }
-        fPaint = paint;
-    } else {
+    }
+    fPaint = paint;
+
+    // if we only came in here for the imagefilter, mark us as done
+    if (!fLooper && !fFilter) {
         fDone = true;
-        fPaint = &fOrigPaint;
     }
 
     // call this after any possible paint modifiers
@@ -386,8 +483,17 @@
 
 ////////// macros to place around the internal draw calls //////////////////
 
+#define LOOPER_BEGIN_DRAWDEVICE(paint, type)                        \
+/*    AutoValidator   validator(fMCRec->fTopLayer->fDevice); */     \
+    this->predrawNotify();                                          \
+    AutoDrawLooper  looper(this, paint, true);                      \
+    while (looper.next(type)) {                                     \
+        SkAutoBounderCommit ac(fBounder);                           \
+        SkDrawIter          iter(this);
+
 #define LOOPER_BEGIN(paint, type)                                   \
 /*    AutoValidator   validator(fMCRec->fTopLayer->fDevice); */     \
+    this->predrawNotify();                                          \
     AutoDrawLooper  looper(this, paint);                            \
     while (looper.next(type)) {                                     \
         SkAutoBounderCommit ac(fBounder);                           \
@@ -401,22 +507,19 @@
     fBounder = NULL;
     fLocalBoundsCompareType.setEmpty();
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeBW.setEmpty();
-    fLocalBoundsCompareTypeDirtyBW = true;
-    fLastDeviceToGainFocus = NULL;
+    fAllowSoftClip = true;
     fDeviceCMDirty = false;
-    fLayerCount = 0;
+    fSaveLayerCount = 0;
+    fMetaData = NULL;
 
     fMCRec = (MCRec*)fMCStack.push_back();
     new (fMCRec) MCRec(NULL, 0);
 
-    fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL));
+    fMCRec->fLayer = SkNEW_ARGS(DeviceCM, (NULL, 0, 0, NULL, NULL));
     fMCRec->fTopLayer = fMCRec->fLayer;
     fMCRec->fNext = NULL;
 
-    fExternalMatrix.reset();
-    fExternalInverse.reset();
-    fUseExternalMatrix = false;
+    fSurfaceBase = NULL;
 
     return this->setDevice(device);
 }
@@ -425,8 +528,7 @@
 : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) {
     inc_canvas();
 
-    SkBitmap bitmap;
-    this->init(SkNEW_ARGS(SkDevice, (bitmap)))->unref();
+    this->init(NULL);
 }
 
 SkCanvas::SkCanvas(SkDevice* device)
@@ -446,11 +548,12 @@
 SkCanvas::~SkCanvas() {
     // free up the contents of our deque
     this->restoreToCount(1);    // restore everything but the last
-    SkASSERT(0 == fLayerCount);
+    SkASSERT(0 == fSaveLayerCount);
 
     this->internalRestore();    // restore the last, since we're going away
 
     SkSafeUnref(fBounder);
+    SkDELETE(fMetaData);
 
     dec_canvas();
 }
@@ -469,6 +572,15 @@
     return filter;
 }
 
+SkMetaData& SkCanvas::getMetaData() {
+    // metadata users are rare, so we lazily allocate it. If that changes we
+    // can decide to just make it a field in the device (rather than a ptr)
+    if (NULL == fMetaData) {
+        fMetaData = new SkMetaData;
+    }
+    return *fMetaData;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkCanvas::flush() {
@@ -485,13 +597,15 @@
 
 SkDevice* SkCanvas::getDevice() const {
     // return root device
-    SkDeque::F2BIter iter(fMCStack);
-    MCRec*           rec = (MCRec*)iter.next();
+    MCRec* rec = (MCRec*) fMCStack.front();
     SkASSERT(rec && rec->fLayer);
     return rec->fLayer->fDevice;
 }
 
-SkDevice* SkCanvas::getTopDevice() const {
+SkDevice* SkCanvas::getTopDevice(bool updateMatrixClip) const {
+    if (updateMatrixClip) {
+        const_cast<SkCanvas*>(this)->updateDeviceCMCache();
+    }
     return fMCRec->fTopLayer->fDevice;
 }
 
@@ -506,14 +620,11 @@
         return device;
     }
 
-    /* Notify the devices that they are going in/out of scope, so they can do
-       things like lock/unlock their pixels, etc.
-    */
     if (device) {
-        device->lockPixels();
+        device->onAttachToCanvas(this);
     }
     if (rootDevice) {
-        rootDevice->unlockPixels();
+        rootDevice->onDetachFromCanvas();
     }
 
     SkRefCnt_SafeAssign(rec->fLayer->fDevice, device);
@@ -535,30 +646,18 @@
         accurately take advantage of the new device bounds.
     */
 
-    if (NULL == device) {
-        rec->fRasterClip->setEmpty();
-        while ((rec = (MCRec*)iter.next()) != NULL) {
-            (void)rec->fRasterClip->setEmpty();
-        }
-        fClipStack.reset();
-    } else {
-        // compute our total bounds for all devices
-        SkIRect bounds;
-
+    SkIRect bounds;
+    if (device) {
         bounds.set(0, 0, device->width(), device->height());
-
-        // now jam our 1st clip to be bounds, and intersect the rest with that
-        rec->fRasterClip->setRect(bounds);
-        while ((rec = (MCRec*)iter.next()) != NULL) {
-            (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
-        }
+    } else {
+        bounds.setEmpty();
     }
-    return device;
-}
+    // now jam our 1st clip to be bounds, and intersect the rest with that
+    rec->fRasterClip->setRect(bounds);
+    while ((rec = (MCRec*)iter.next()) != NULL) {
+        (void)rec->fRasterClip->op(bounds, SkRegion::kIntersect_Op);
+    }
 
-SkDevice* SkCanvas::setBitmapDevice(const SkBitmap& bitmap) {
-    SkDevice* device = this->setDevice(SkNEW_ARGS(SkDevice, (bitmap)));
-    device->unref();
     return device;
 }
 
@@ -599,7 +698,11 @@
                            Config8888 config8888) {
     SkDevice* device = this->getDevice();
     if (device) {
-        device->writePixels(bitmap, x, y, config8888);
+        if (SkIRect::Intersects(SkIRect::MakeSize(this->getDeviceSize()),
+                                SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height()))) {
+            device->accessBitmap(true);
+            device->writePixels(bitmap, x, y, config8888);
+        }
     }
 }
 
@@ -617,34 +720,16 @@
 
         if (NULL == layer->fNext) {   // only one layer
             layer->updateMC(totalMatrix, totalClip, fClipStack, NULL);
-            if (fUseExternalMatrix) {
-                layer->updateExternalMatrix(fExternalMatrix,
-                                            fExternalInverse);
-            }
         } else {
             SkRasterClip clip(totalClip);
             do {
                 layer->updateMC(totalMatrix, clip, fClipStack, &clip);
-                if (fUseExternalMatrix) {
-                    layer->updateExternalMatrix(fExternalMatrix,
-                                                fExternalInverse);
-                }
             } while ((layer = layer->fNext) != NULL);
         }
         fDeviceCMDirty = false;
     }
 }
 
-void SkCanvas::prepareForDeviceDraw(SkDevice* device, const SkMatrix& matrix,
-                                    const SkRegion& clip,
-                                    const SkClipStack& clipStack) {
-    SkASSERT(device);
-    if (fLastDeviceToGainFocus != device) {
-        device->gainFocus(this, matrix, clip, clipStack);
-        fLastDeviceToGainFocus = device;
-    }
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 int SkCanvas::internalSave(SaveFlags flags) {
@@ -750,6 +835,11 @@
 
 int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
                         SaveFlags flags) {
+    return this->internalSaveLayer(bounds, paint, flags, false);
+}
+
+int SkCanvas::internalSaveLayer(const SkRect* bounds, const SkPaint* paint,
+                                SaveFlags flags, bool justForImageFilter) {
     // do this before we create the layer. We don't call the public save() since
     // that would invoke a possibly overridden virtual
     int count = this->internalSave(flags);
@@ -765,6 +855,10 @@
     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 count;
+            }
             SkPaint* p = lazyP.set(*paint);
             p->setImageFilter(NULL);
             paint = p;
@@ -788,14 +882,14 @@
     }
 
     device->setOrigin(ir.fLeft, ir.fTop);
-    DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint));
+    DeviceCM* layer = SkNEW_ARGS(DeviceCM, (device, ir.fLeft, ir.fTop, paint, this));
     device->unref();
 
     layer->fNext = fMCRec->fTopLayer;
     fMCRec->fLayer = layer;
     fMCRec->fTopLayer = layer;    // this field is NOT an owner of layer
 
-    fLayerCount += 1;
+    fSaveLayerCount += 1;
     return count;
 }
 
@@ -822,7 +916,6 @@
 
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
 
     fClipStack.restore();
     // reserve our layer (if any)
@@ -842,13 +935,13 @@
     if (NULL != layer) {
         if (layer->fNext) {
             const SkIPoint& origin = layer->fDevice->getOrigin();
-            this->drawDevice(layer->fDevice, origin.x(), origin.y(),
-                             layer->fPaint);
-            // reset this, since drawDevice will have set it to true
+            this->internalDrawDevice(layer->fDevice, origin.x(), origin.y(),
+                                     layer->fPaint);
+            // reset this, since internalDrawDevice will have set it to true
             fDeviceCMDirty = true;
 
-            SkASSERT(fLayerCount > 0);
-            fLayerCount -= 1;
+            SkASSERT(fSaveLayerCount > 0);
+            fSaveLayerCount -= 1;
         }
         SkDELETE(layer);
     }
@@ -873,7 +966,7 @@
 }
 
 bool SkCanvas::isDrawingToLayer() const {
-    return fLayerCount > 0;
+    return fSaveLayerCount > 0;
 }
 
 /////////////////////////////////////////////////////////////////////////////
@@ -900,56 +993,28 @@
     this->commonDrawBitmap(bitmap, srcRect, matrix, *paint);
 }
 
-#include "SkImageFilter.h"
-
-class DeviceImageFilterProxy : public SkImageFilter::Proxy {
-public:
-    DeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
-
-    virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE;
-    virtual bool filterImage(SkImageFilter*, const SkBitmap& src,
-                             const SkMatrix& ctm,
-                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
-
-private:
-    SkDevice* fDevice;
-};
-
-SkDevice* DeviceImageFilterProxy::createDevice(int w, int h) {
-    return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
-                                           w, h, false);
-}
-
-bool DeviceImageFilterProxy::filterImage(SkImageFilter* filter,
-                                         const SkBitmap& src,
-                                         const SkMatrix& ctm,
-                                         SkBitmap* result,
-                                         SkIPoint* offset) {
-    return fDevice->filterImage(filter, src, ctm, result, offset);
-}
-
-void SkCanvas::drawDevice(SkDevice* srcDev, int x, int y,
-                          const SkPaint* paint) {
+void SkCanvas::internalDrawDevice(SkDevice* srcDev, int x, int y,
+                                  const SkPaint* paint) {
     SkPaint tmp;
     if (NULL == paint) {
         tmp.setDither(true);
         paint = &tmp;
     }
 
-    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+    LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
     while (iter.next()) {
         SkDevice* dstDev = iter.fDevice;
         paint = &looper.paint();
         SkImageFilter* filter = paint->getImageFilter();
         SkIPoint pos = { x - iter.getX(), y - iter.getY() };
-        if (filter) {
-            DeviceImageFilterProxy proxy(dstDev);
+        if (filter && !dstDev->canHandleImageFilter(filter)) {
+            SkDeviceImageFilterProxy proxy(dstDev);
             SkBitmap dst;
             const SkBitmap& src = srcDev->accessBitmap(false);
             if (filter->filterImage(&proxy, src, *iter.fMatrix, &dst, &pos)) {
-                SkPaint tmp(*paint);
-                tmp.setImageFilter(NULL);
-                dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmp);
+                SkPaint tmpUnfiltered(*paint);
+                tmpUnfiltered.setImageFilter(NULL);
+                dstDev->drawSprite(iter, dst, pos.x(), pos.y(), tmpUnfiltered);
             }
         } else {
             dstDev->drawDevice(iter, srcDev, pos.x(), pos.y(), *paint);
@@ -958,47 +1023,78 @@
     LOOPER_END
 }
 
+void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
+                          const SkPaint* paint) {
+    SkDEBUGCODE(bitmap.validate();)
+    CHECK_LOCKCOUNT_BALANCE(bitmap);
+
+    if (reject_bitmap(bitmap)) {
+        return;
+    }
+
+    SkPaint tmp;
+    if (NULL == paint) {
+        paint = &tmp;
+    }
+
+    LOOPER_BEGIN_DRAWDEVICE(*paint, SkDrawFilter::kBitmap_Type)
+
+    while (iter.next()) {
+        paint = &looper.paint();
+        SkImageFilter* filter = paint->getImageFilter();
+        SkIPoint pos = { x - iter.getX(), y - iter.getY() };
+        if (filter && !iter.fDevice->canHandleImageFilter(filter)) {
+            SkDeviceImageFilterProxy proxy(iter.fDevice);
+            SkBitmap dst;
+            if (filter->filterImage(&proxy, bitmap, *iter.fMatrix,
+                                    &dst, &pos)) {
+                SkPaint tmpUnfiltered(*paint);
+                tmpUnfiltered.setImageFilter(NULL);
+                iter.fDevice->drawSprite(iter, dst, pos.x(), pos.y(),
+                                         tmpUnfiltered);
+            }
+        } else {
+            iter.fDevice->drawSprite(iter, bitmap, pos.x(), pos.y(), *paint);
+        }
+    }
+    LOOPER_END
+}
+
 /////////////////////////////////////////////////////////////////////////////
 
 bool SkCanvas::translate(SkScalar dx, SkScalar dy) {
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
     return fMCRec->fMatrix->preTranslate(dx, dy);
 }
 
 bool SkCanvas::scale(SkScalar sx, SkScalar sy) {
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
     return fMCRec->fMatrix->preScale(sx, sy);
 }
 
 bool SkCanvas::rotate(SkScalar degrees) {
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
     return fMCRec->fMatrix->preRotate(degrees);
 }
 
 bool SkCanvas::skew(SkScalar sx, SkScalar sy) {
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
     return fMCRec->fMatrix->preSkew(sx, sy);
 }
 
 bool SkCanvas::concat(const SkMatrix& matrix) {
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
     return fMCRec->fMatrix->preConcat(matrix);
 }
 
 void SkCanvas::setMatrix(const SkMatrix& matrix) {
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
     *fMCRec->fMatrix = matrix;
 }
 
@@ -1014,11 +1110,27 @@
 //////////////////////////////////////////////////////////////////////////////
 
 bool SkCanvas::clipRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
+#ifdef SK_ENABLE_CLIP_QUICKREJECT
+    if (SkRegion::kIntersect_Op == op) {
+        if (fMCRec->fRasterClip->isEmpty()) {
+            return false;
+        }
+
+        if (this->quickReject(rect)) {
+            fDeviceCMDirty = true;
+            fLocalBoundsCompareTypeDirty = true;
+
+            fClipStack.clipEmpty();
+            return fMCRec->fRasterClip->setEmpty();
+        }
+    }
+#endif
+
     AutoValidateClip avc(this);
 
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
+    doAA &= fAllowSoftClip;
 
     if (fMCRec->fMatrix->rectStaysRect()) {
         // for these simpler matrices, we can stay a rect ever after applying
@@ -1078,12 +1190,40 @@
     }
 }
 
+bool SkCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    if (rrect.isRect()) {
+        // call the non-virtual version
+        return this->SkCanvas::clipRect(rrect.getBounds(), op, doAA);
+    } else {
+        SkPath path;
+        path.addRRect(rrect);
+        // call the non-virtual version
+        return this->SkCanvas::clipPath(path, op, doAA);
+    }
+}
+
 bool SkCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+#ifdef SK_ENABLE_CLIP_QUICKREJECT
+    if (SkRegion::kIntersect_Op == op && !path.isInverseFillType()) {
+        if (fMCRec->fRasterClip->isEmpty()) {
+            return false;
+        }
+
+        if (this->quickReject(path.getBounds())) {
+            fDeviceCMDirty = true;
+            fLocalBoundsCompareTypeDirty = true;
+
+            fClipStack.clipEmpty();
+            return fMCRec->fRasterClip->setEmpty();
+        }
+    }
+#endif
+
     AutoValidateClip avc(this);
 
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
+    doAA &= fAllowSoftClip;
 
     SkPath devPath;
     path.transform(*fMCRec->fMatrix, &devPath);
@@ -1109,11 +1249,10 @@
 
     fDeviceCMDirty = true;
     fLocalBoundsCompareTypeDirty = true;
-    fLocalBoundsCompareTypeDirtyBW = true;
 
     // todo: signal fClipStack that we have a region, and therefore (I guess)
     // we have to ignore it, and use the region directly?
-    fClipStack.clipDevRect(rgn.getBounds());
+    fClipStack.clipDevRect(rgn.getBounds(), op);
 
     return fMCRec->fRasterClip->op(rgn, op);
 }
@@ -1131,16 +1270,24 @@
     ir.set(0, 0, device->width(), device->height());
     SkRasterClip tmpClip(ir);
 
-    SkClipStack::B2FIter                iter(fClipStack);
-    const SkClipStack::B2FIter::Clip*   clip;
-    while ((clip = iter.next()) != NULL) {
-        if (clip->fPath) {
-            clipPathHelper(this, &tmpClip, *clip->fPath, clip->fOp, clip->fDoAA);
-        } else if (clip->fRect) {
-            clip->fRect->round(&ir);
-            tmpClip.op(ir, clip->fOp);
-        } else {
-            tmpClip.setEmpty();
+    SkClipStack::B2TIter                iter(fClipStack);
+    const SkClipStack::Element* element;
+    while ((element = iter.next()) != NULL) {
+        switch (element->getType()) {
+            case SkClipStack::Element::kPath_Type:
+                clipPathHelper(this,
+                               &tmpClip,
+                               element->getPath(),
+                               element->getOp(),
+                               element->isAA());
+                break;
+            case SkClipStack::Element::kRect_Type:
+                element->getRect().round(&ir);
+                tmpClip.op(ir, element->getOp());
+                break;
+            case SkClipStack::Element::kEmpty_Type:
+                tmpClip.setEmpty();
+                break;
         }
     }
 
@@ -1152,28 +1299,42 @@
 }
 #endif
 
-///////////////////////////////////////////////////////////////////////////////
+void SkCanvas::replayClips(ClipVisitor* visitor) const {
+    SkClipStack::B2TIter                iter(fClipStack);
+    const SkClipStack::Element*         element;
 
-void SkCanvas::computeLocalClipBoundsCompareType(EdgeType et) const {
-    SkRect r;
-    SkRectCompareType& rCompare = et == kAA_EdgeType ? fLocalBoundsCompareType :
-            fLocalBoundsCompareTypeBW;
-
-    if (!this->getClipBounds(&r, et)) {
-        rCompare.setEmpty();
-    } else {
-        rCompare.set(SkScalarToCompareType(r.fLeft),
-                     SkScalarToCompareType(r.fTop),
-                     SkScalarToCompareType(r.fRight),
-                     SkScalarToCompareType(r.fBottom));
+    static const SkRect kEmpty = { 0, 0, 0, 0 };
+    while ((element = iter.next()) != NULL) {
+        switch (element->getType()) {
+            case SkClipStack::Element::kPath_Type:
+                visitor->clipPath(element->getPath(), element->getOp(), element->isAA());
+                break;
+            case SkClipStack::Element::kRect_Type:
+                visitor->clipRect(element->getRect(), element->getOp(), element->isAA());
+                break;
+            case SkClipStack::Element::kEmpty_Type:
+                visitor->clipRect(kEmpty, SkRegion::kIntersect_Op, false);
+                break;
+        }
     }
 }
 
-/*  current impl ignores edgetype, and relies on
-    getLocalClipBoundsCompareType(), which always returns a value assuming
-    antialiasing (worst case)
- */
-bool SkCanvas::quickReject(const SkRect& rect, EdgeType et) const {
+///////////////////////////////////////////////////////////////////////////////
+
+void SkCanvas::computeLocalClipBoundsCompareType() const {
+    SkRect r;
+
+    if (!this->getClipBounds(&r)) {
+        fLocalBoundsCompareType.setEmpty();
+    } else {
+        fLocalBoundsCompareType.set(SkScalarToCompareType(r.fLeft),
+                                    SkScalarToCompareType(r.fTop),
+                                    SkScalarToCompareType(r.fRight),
+                                    SkScalarToCompareType(r.fBottom));
+    }
+}
+
+bool SkCanvas::quickReject(const SkRect& rect) const {
 
     if (!rect.isFinite())
         return true;
@@ -1189,7 +1350,7 @@
         dst.roundOut(&idst);
         return !SkIRect::Intersects(idst, fMCRec->fRasterClip->getBounds());
     } else {
-        const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType(et);
+        const SkRectCompareType& clipR = this->getLocalClipBoundsCompareType();
 
         // for speed, do the most likely reject compares first
         SkScalarCompareType userT = SkScalarToCompareType(rect.fTop);
@@ -1206,8 +1367,8 @@
     }
 }
 
-bool SkCanvas::quickReject(const SkPath& path, EdgeType et) const {
-    return path.isEmpty() || this->quickReject(path.getBounds(), et);
+bool SkCanvas::quickReject(const SkPath& path) const {
+    return path.isEmpty() || this->quickReject(path.getBounds());
 }
 
 static inline int pinIntForScalar(int x) {
@@ -1221,7 +1382,7 @@
     return x;
 }
 
-bool SkCanvas::getClipBounds(SkRect* bounds, EdgeType et) const {
+bool SkCanvas::getClipBounds(SkRect* bounds) const {
     SkIRect ibounds;
     if (!getClipDeviceBounds(&ibounds)) {
         return false;
@@ -1238,8 +1399,8 @@
 
     if (NULL != bounds) {
         SkRect r;
-        // adjust it outwards if we are antialiasing
-        int inset = (kAA_EdgeType == et);
+        // adjust it outwards in case we are antialiasing
+        const int inset = 1;
 
         // SkRect::iset() will correctly assert if we pass a value out of range
         // (when SkScalar==fixed), so we pin to legal values. This does not
@@ -1248,7 +1409,7 @@
         // that can be larger than 32K in width or height).
         r.iset(pinIntForScalar(ibounds.fLeft - inset),
                pinIntForScalar(ibounds.fTop - inset),
-               pinIntForScalar(ibounds.fRight + inset), 
+               pinIntForScalar(ibounds.fRight + inset),
                pinIntForScalar(ibounds.fBottom + inset));
         inverse.mapRect(bounds, r);
     }
@@ -1284,25 +1445,6 @@
     return fMCRec->fRasterClip->forceGetBW();
 }
 
-const SkClipStack& SkCanvas::getTotalClipStack() const {
-    return fClipStack;
-}
-
-void SkCanvas::setExternalMatrix(const SkMatrix* matrix) {
-    if (NULL == matrix || matrix->isIdentity()) {
-        if (fUseExternalMatrix) {
-            fDeviceCMDirty = true;
-        }
-        fUseExternalMatrix = false;
-    } else {
-        fUseExternalMatrix = true;
-        fDeviceCMDirty = true;  // |= (fExternalMatrix != *matrix)
-
-        fExternalMatrix = *matrix;
-        matrix->invert(&fExternalInverse);
-    }
-}
-
 SkDevice* SkCanvas::createLayerDevice(SkBitmap::Config config,
                                       int width, int height,
                                       bool isOpaque) {
@@ -1344,6 +1486,8 @@
 }
 
 void SkCanvas::internalDrawPaint(const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kPaint_Type)
 
     while (iter.next()) {
@@ -1359,6 +1503,22 @@
         return;
     }
 
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
+    if (paint.canComputeFastBounds()) {
+        SkRect r;
+        // special-case 2 points (common for drawing a single line)
+        if (2 == count) {
+            r.set(pts[0], pts[1]);
+        } else {
+            r.set(pts, count);
+        }
+        SkRect storage;
+        if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) {
+            return;
+        }
+    }
+
     SkASSERT(pts != NULL);
 
     LOOPER_BEGIN(paint, SkDrawFilter::kPoint_Type)
@@ -1371,10 +1531,11 @@
 }
 
 void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     if (paint.canComputeFastBounds()) {
         SkRect storage;
-        if (this->quickReject(paint.computeFastBounds(r, &storage),
-                              paint2EdgeType(&paint))) {
+        if (this->quickReject(paint.computeFastBounds(r, &storage))) {
             return;
         }
     }
@@ -1388,12 +1549,58 @@
     LOOPER_END
 }
 
+void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(oval, &storage))) {
+            return;
+        }
+    }
+
+    LOOPER_BEGIN(paint, SkDrawFilter::kOval_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawOval(iter, oval, looper.paint());
+    }
+
+    LOOPER_END
+}
+
+void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
+    if (paint.canComputeFastBounds()) {
+        SkRect storage;
+        if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) {
+            return;
+        }
+    }
+
+    if (rrect.isRect()) {
+        // call the non-virtual version
+        this->SkCanvas::drawRect(rrect.getBounds(), paint);
+    } else {
+        SkPath  path;
+        path.addRRect(rrect);
+        // call the non-virtual version
+        this->SkCanvas::drawPath(path, paint);
+    }
+}
+
+
 void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
+    if (!path.isFinite()) {
+        return;
+    }
+
     if (!path.isInverseFillType() && paint.canComputeFastBounds()) {
         SkRect storage;
         const SkRect& bounds = path.getBounds();
-        if (this->quickReject(paint.computeFastBounds(bounds, &storage),
-                              paint2EdgeType(&paint))) {
+        if (this->quickReject(paint.computeFastBounds(bounds, &storage))) {
             return;
         }
     }
@@ -1426,7 +1633,7 @@
         if (paint) {
             (void)paint->computeFastBounds(bounds, &bounds);
         }
-        if (this->quickReject(bounds, paint2EdgeType(paint))) {
+        if (this->quickReject(bounds)) {
             return;
         }
     }
@@ -1437,61 +1644,41 @@
 }
 
 // this one is non-virtual, so it can be called safely by other canvas apis
-void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+void SkCanvas::internalDrawBitmapRect(const SkBitmap& bitmap, const SkRect* src,
                                       const SkRect& dst, const SkPaint* paint) {
     if (bitmap.width() == 0 || bitmap.height() == 0 || dst.isEmpty()) {
         return;
     }
 
-    // do this now, to avoid the cost of calling extract for RLE bitmaps
+    CHECK_LOCKCOUNT_BALANCE(bitmap);
+
     if (NULL == paint || paint->canComputeFastBounds()) {
         SkRect storage;
         const SkRect* bounds = &dst;
         if (paint) {
             bounds = &paint->computeFastBounds(dst, &storage);
         }
-        if (this->quickReject(*bounds, paint2EdgeType(paint))) {
+        if (this->quickReject(*bounds)) {
             return;
         }
     }
 
-    const SkBitmap* bitmapPtr = &bitmap;
-
-    SkMatrix matrix;
-    SkRect tmpSrc;
-    if (src) {
-        tmpSrc.set(*src);
-        // if the extract process clipped off the top or left of the
-        // original, we adjust for that here to get the position right.
-        if (tmpSrc.fLeft > 0) {
-            tmpSrc.fRight -= tmpSrc.fLeft;
-            tmpSrc.fLeft = 0;
-        }
-        if (tmpSrc.fTop > 0) {
-            tmpSrc.fBottom -= tmpSrc.fTop;
-            tmpSrc.fTop = 0;
-        }
-    } else {
-        tmpSrc.set(0, 0, SkIntToScalar(bitmap.width()),
-                   SkIntToScalar(bitmap.height()));
+    SkLazyPaint lazy;
+    if (NULL == paint) {
+        paint = lazy.init();
     }
-    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
 
-    // ensure that src is "valid" before we pass it to our internal routines
-    // and to SkDevice. i.e. sure it is contained inside the original bitmap.
-    SkIRect tmpISrc;
-    if (src) {
-        tmpISrc.set(0, 0, bitmap.width(), bitmap.height());
-        if (!tmpISrc.intersect(*src)) {
-            return;
-        }
-        src = &tmpISrc;
+    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
+
+    while (iter.next()) {
+        iter.fDevice->drawBitmapRect(iter, bitmap, src, dst, looper.paint());
     }
-    this->internalDrawBitmap(*bitmapPtr, src, matrix, paint);
+
+    LOOPER_END
 }
 
-void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
-                              const SkRect& dst, const SkPaint* paint) {
+void SkCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                                    const SkRect& dst, const SkPaint* paint) {
     SkDEBUGCODE(bitmap.validate();)
     this->internalDrawBitmapRect(bitmap, src, dst, paint);
 }
@@ -1505,6 +1692,7 @@
 void SkCanvas::commonDrawBitmap(const SkBitmap& bitmap, const SkIRect* srcRect,
                                 const SkMatrix& matrix, const SkPaint& paint) {
     SkDEBUGCODE(bitmap.validate();)
+    CHECK_LOCKCOUNT_BALANCE(bitmap);
 
     LOOPER_BEGIN(paint, SkDrawFilter::kBitmap_Type)
 
@@ -1524,7 +1712,7 @@
         if (paint) {
             bounds = &paint->computeFastBounds(dst, &storage);
         }
-        if (this->quickReject(*bounds, paint2EdgeType(paint))) {
+        if (this->quickReject(*bounds)) {
             return;
         }
     }
@@ -1539,8 +1727,12 @@
     c.fRight = SkPin32(center.fRight, c.fLeft, w);
     c.fBottom = SkPin32(center.fBottom, c.fTop, h);
 
-    const int32_t srcX[4] = { 0, c.fLeft, c.fRight, w };
-    const int32_t srcY[4] = { 0, c.fTop, c.fBottom, h };
+    const SkScalar srcX[4] = {
+        0, SkIntToScalar(c.fLeft), SkIntToScalar(c.fRight), SkIntToScalar(w)
+    };
+    const SkScalar srcY[4] = {
+        0, SkIntToScalar(c.fTop), SkIntToScalar(c.fBottom), SkIntToScalar(h)
+    };
     SkScalar dstX[4] = {
         dst.fLeft, dst.fLeft + SkIntToScalar(c.fLeft),
         dst.fRight - SkIntToScalar(w - c.fRight), dst.fRight
@@ -1560,9 +1752,9 @@
         dstY[2] = dstY[1];
     }
 
-    SkIRect s;
-    SkRect  d;
     for (int y = 0; y < 3; y++) {
+        SkRect s, d;
+
         s.fTop = srcY[y];
         s.fBottom = srcY[y+1];
         d.fTop = dstY[y];
@@ -1585,28 +1777,6 @@
     this->internalDrawBitmapNine(bitmap, center, dst, paint);
 }
 
-void SkCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
-                          const SkPaint* paint) {
-    SkDEBUGCODE(bitmap.validate();)
-
-    if (reject_bitmap(bitmap)) {
-        return;
-    }
-
-    SkPaint tmp;
-    if (NULL == paint) {
-        paint = &tmp;
-    }
-
-    LOOPER_BEGIN(*paint, SkDrawFilter::kBitmap_Type)
-
-    while (iter.next()) {
-        iter.fDevice->drawSprite(iter, bitmap, x - iter.getX(), y - iter.getY(),
-                                 looper.paint());
-    }
-    LOOPER_END
-}
-
 class SkDeviceFilteredPaint {
 public:
     SkDeviceFilteredPaint(SkDevice* device, const SkPaint& paint) {
@@ -1702,6 +1872,8 @@
 
 void SkCanvas::drawText(const void* text, size_t byteLength,
                         SkScalar x, SkScalar y, const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
 
     while (iter.next()) {
@@ -1716,6 +1888,8 @@
 
 void SkCanvas::drawPosText(const void* text, size_t byteLength,
                            const SkPoint pos[], const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
 
     while (iter.next()) {
@@ -1730,6 +1904,8 @@
 void SkCanvas::drawPosTextH(const void* text, size_t byteLength,
                             const SkScalar xpos[], SkScalar constY,
                             const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
 
     while (iter.next()) {
@@ -1744,6 +1920,8 @@
 void SkCanvas::drawTextOnPath(const void* text, size_t byteLength,
                               const SkPath& path, const SkMatrix* matrix,
                               const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
 
     while (iter.next()) {
@@ -1758,6 +1936,8 @@
 void SkCanvas::drawPosTextOnPath(const void* text, size_t byteLength,
                                  const SkPoint pos[], const SkPaint& paint,
                                  const SkPath& path, const SkMatrix* matrix) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kText_Type)
 
     while (iter.next()) {
@@ -1774,6 +1954,8 @@
                             const SkColor colors[], SkXfermode* xmode,
                             const uint16_t indices[], int indexCount,
                             const SkPaint& paint) {
+    CHECK_SHADER_NOSETCONTEXT(paint);
+
     LOOPER_BEGIN(paint, SkDrawFilter::kPath_Type)
 
     while (iter.next()) {
@@ -1857,18 +2039,7 @@
 
     SkRect  r;
     r.set(cx - radius, cy - radius, cx + radius, cy + radius);
-
-    if (paint.canComputeFastBounds()) {
-        SkRect storage;
-        if (this->quickReject(paint.computeFastBounds(r, &storage),
-                              paint2EdgeType(&paint))) {
-            return;
-        }
-    }
-
-    SkPath  path;
-    path.addOval(r);
-    this->drawPath(path, paint);
+    this->drawOval(r, paint);
 }
 
 void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry,
@@ -1876,34 +2047,18 @@
     if (rx > 0 && ry > 0) {
         if (paint.canComputeFastBounds()) {
             SkRect storage;
-            if (this->quickReject(paint.computeFastBounds(r, &storage),
-                                  paint2EdgeType(&paint))) {
+            if (this->quickReject(paint.computeFastBounds(r, &storage))) {
                 return;
             }
         }
-
-        SkPath  path;
-        path.addRoundRect(r, rx, ry, SkPath::kCW_Direction);
-        this->drawPath(path, paint);
+        SkRRect rrect;
+        rrect.setRectXY(r, rx, ry);
+        this->drawRRect(rrect, paint);
     } else {
         this->drawRect(r, paint);
     }
 }
 
-void SkCanvas::drawOval(const SkRect& oval, const SkPaint& paint) {
-    if (paint.canComputeFastBounds()) {
-        SkRect storage;
-        if (this->quickReject(paint.computeFastBounds(oval, &storage),
-                              paint2EdgeType(&paint))) {
-            return;
-        }
-    }
-
-    SkPath  path;
-    path.addOval(oval);
-    this->drawPath(path, paint);
-}
-
 void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle,
                        SkScalar sweepAngle, bool useCenter,
                        const SkPaint& paint) {
@@ -1934,9 +2089,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkCanvas::drawPicture(SkPicture& picture) {
-    int saveCount = save();
     picture.draw(this);
-    restoreToCount(saveCount);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1978,3 +2131,7 @@
 const SkRegion& SkCanvas::LayerIter::clip() const { return fImpl->getClip(); }
 int SkCanvas::LayerIter::x() const { return fImpl->getX(); }
 int SkCanvas::LayerIter::y() const { return fImpl->getY(); }
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkCanvas::ClipVisitor::~ClipVisitor() { }
diff --git a/src/core/SkChunkAlloc.cpp b/src/core/SkChunkAlloc.cpp
index 56b4abe..30cc4e1 100644
--- a/src/core/SkChunkAlloc.cpp
+++ b/src/core/SkChunkAlloc.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,41 +5,35 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkChunkAlloc.h"
 
+// Don't malloc any chunks smaller than this
+#define MIN_CHUNKALLOC_BLOCK_SIZE   1024
+
+// Return the new min blocksize given the current value
+static size_t increase_next_size(size_t size) {
+    return size + (size >> 1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 struct SkChunkAlloc::Block {
     Block*  fNext;
     size_t  fFreeSize;
     char*   fFreePtr;
     // data[] follows
-    
+
     char* startOfData() {
         return reinterpret_cast<char*>(this + 1);
     }
 
-    void freeChain() {    // this can be null
-        Block* block = this;
+    static void FreeChain(Block* block) {
         while (block) {
             Block* next = block->fNext;
             sk_free(block);
             block = next;
         }
     };
-    
-    Block* tail() {
-        Block* block = this;
-        if (block) {
-            for (;;) {
-                Block* next = block->fNext;
-                if (NULL == next) {
-                    break;
-                }
-                block = next;
-            }
-        }
-        return block;
-    }
 
     bool contains(const void* addr) const {
         const char* ptr = reinterpret_cast<const char*>(addr);
@@ -48,9 +41,18 @@
     }
 };
 
-SkChunkAlloc::SkChunkAlloc(size_t minSize)
-    : fBlock(NULL), fMinSize(SkAlign4(minSize)), fPool(NULL), fTotalCapacity(0)
-{
+///////////////////////////////////////////////////////////////////////////////
+
+SkChunkAlloc::SkChunkAlloc(size_t minSize) {
+    if (minSize < MIN_CHUNKALLOC_BLOCK_SIZE) {
+        minSize = MIN_CHUNKALLOC_BLOCK_SIZE;
+    }
+
+    fBlock = NULL;
+    fMinSize = minSize;
+    fChunkSize = fMinSize;
+    fTotalCapacity = 0;
+    fBlockCount = 0;
 }
 
 SkChunkAlloc::~SkChunkAlloc() {
@@ -58,43 +60,31 @@
 }
 
 void SkChunkAlloc::reset() {
-    fBlock->freeChain();
+    Block::FreeChain(fBlock);
     fBlock = NULL;
-    fPool->freeChain();
-    fPool = NULL;
+    fChunkSize = fMinSize;  // reset to our initial minSize
     fTotalCapacity = 0;
-}
-
-void SkChunkAlloc::reuse() {
-    if (fPool && fBlock) {
-        fPool->tail()->fNext = fBlock;
-    }
-    fPool = fBlock;
-    fBlock = NULL;
-    fTotalCapacity = 0;
+    fBlockCount = 0;
 }
 
 SkChunkAlloc::Block* SkChunkAlloc::newBlock(size_t bytes, AllocFailType ftype) {
-    Block* block = fPool;
-
-    if (block && bytes <= block->fFreeSize) {
-        fPool = block->fNext;
-        return block;
+    size_t size = bytes;
+    if (size < fChunkSize) {
+        size = fChunkSize;
     }
 
-    size_t size = bytes;
-    if (size < fMinSize)
-        size = fMinSize;
-
-    block = (Block*)sk_malloc_flags(sizeof(Block) + size,
+    Block* block = (Block*)sk_malloc_flags(sizeof(Block) + size,
                         ftype == kThrow_AllocFailType ? SK_MALLOC_THROW : 0);
 
     if (block) {
         //    block->fNext = fBlock;
         block->fFreeSize = size;
         block->fFreePtr = block->startOfData();
-        
+
         fTotalCapacity += size;
+        fBlockCount += 1;
+
+        fChunkSize = increase_next_size(fChunkSize);
     }
     return block;
 }
@@ -114,10 +104,10 @@
     }
 
     SkASSERT(block && bytes <= block->fFreeSize);
-    void* ptr = block->fFreePtr;
+    char* ptr = block->fFreePtr;
 
     block->fFreeSize -= bytes;
-    block->fFreePtr += bytes;
+    block->fFreePtr = ptr + bytes;
     return ptr;
 }
 
@@ -146,4 +136,3 @@
     }
     return false;
 }
-
diff --git a/src/core/SkClampRange.cpp b/src/core/SkClampRange.cpp
deleted file mode 100644
index 1f7cce3..0000000
--- a/src/core/SkClampRange.cpp
+++ /dev/null
@@ -1,166 +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 "SkClampRange.h"
-
-/*
- *  returns [0..count] for the number of steps (<= count) for which x0 <= edge
- *  given each step is followed by x0 += dx
- */
-static int chop(int64_t x0, SkFixed edge, int64_t x1, int64_t dx, int count) {
-    SkASSERT(dx > 0);
-    SkASSERT(count >= 0);
-
-    if (x0 >= edge) {
-        return 0;
-    }
-    if (x1 <= edge) {
-        return count;
-    }
-    int64_t n = (edge - x0 + dx - 1) / dx;
-    SkASSERT(n >= 0);
-    SkASSERT(n <= count);
-    return (int)n;
-}
-
-static bool overflows_fixed(int64_t x) {
-    return x < -SK_FixedMax || x > SK_FixedMax;
-}
-
-void SkClampRange::initFor1(SkFixed fx) {
-    fCount0 = fCount1 = fCount2 = 0;
-    if (fx <= 0) {
-        fCount0 = 1;
-    } else if (fx < 0xFFFF) {
-        fCount1 = 1;
-        fFx1 = fx;
-    } else {
-        fCount2 = 1;
-    }
-}
-
-void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
-    SkASSERT(count > 0);
-
-    fV0 = v0;
-    fV1 = v1;
-    fOverflowed = false;
-
-    // special case 1 == count, as it is slightly common for skia
-    // and avoids us ever calling divide or 64bit multiply
-    if (1 == count) {
-        this->initFor1(fx0);
-        return;
-    }
-
-    int64_t fx = fx0;
-    int64_t dx = dx0;
-    // start with ex equal to the last computed value
-    int64_t ex = fx + (count - 1) * dx;
-    fOverflowed = overflows_fixed(ex);
-
-    if ((uint64_t)(fx | ex) <= 0xFFFF) {
-        fCount0 = fCount2 = 0;
-        fCount1 = count;
-        fFx1 = fx0;
-        return;
-    }
-    if (fx <= 0 && ex <= 0) {
-        fCount1 = fCount2 = 0;
-        fCount0 = count;
-        return;
-    }
-    if (fx >= 0xFFFF && ex >= 0xFFFF) {
-        fCount0 = fCount1 = 0;
-        fCount2 = count;
-        return;
-    }
-
-    int extraCount = 0;
-
-    // now make ex be 1 past the last computed value
-    ex += dx;
-    fOverflowed = overflows_fixed(ex);
-    // now check for over/under flow
-    if (fOverflowed) {
-        int originalCount = count;
-        int64_t ccount;
-        bool swap = dx < 0;
-        if (swap) {
-            dx = -dx;
-            fx = -fx;
-        }
-        ccount = (SK_FixedMax - fx + dx - 1) / dx;
-        if (swap) {
-            dx = -dx;
-            fx = -fx;
-        }
-        SkASSERT(ccount > 0 && ccount <= SK_FixedMax);
-
-        count = (int)ccount;
-        if (0 == count) {
-            this->initFor1(fx0);
-            if (dx > 0) {
-                fCount2 += originalCount - 1;
-            } else {
-                fCount0 += originalCount - 1;
-            }
-            return;
-        }
-        extraCount = originalCount - count;
-        ex = fx + dx * count;
-    }
-    
-    bool doSwap = dx < 0;
-
-    if (doSwap) {
-        ex -= dx;
-        fx -= dx;
-        SkTSwap(fx, ex);
-        dx = -dx;
-    }
-
-
-    fCount0 = chop(fx, 0, ex, dx, count);
-    count -= fCount0;
-    fx += fCount0 * dx;
-    SkASSERT(fx >= 0);
-    SkASSERT(fCount0 == 0 || (fx - dx) < 0);
-    fCount1 = chop(fx, 0xFFFF, ex, dx, count);
-    count -= fCount1;
-    fCount2 = count;
-
-#ifdef SK_DEBUG
-    fx += fCount1 * dx;
-    SkASSERT(fx <= ex);
-    if (fCount2 > 0) {
-        SkASSERT(fx >= 0xFFFF);
-        if (fCount1 > 0) {
-            SkASSERT(fx - dx < 0xFFFF);
-        }
-    }
-#endif
-
-    if (doSwap) {
-        SkTSwap(fCount0, fCount2);
-        SkTSwap(fV0, fV1);
-        dx = -dx;
-    }
-
-    if (fCount1 > 0) {
-        fFx1 = fx0 + fCount0 * (int)dx;
-    }
-
-    if (dx > 0) {
-        fCount2 += extraCount;
-    } else {
-        fCount0 += extraCount;
-    }
-}
-
diff --git a/src/core/SkClipStack.cpp b/src/core/SkClipStack.cpp
index f885240..9afef3f 100644
--- a/src/core/SkClipStack.cpp
+++ b/src/core/SkClipStack.cpp
@@ -7,81 +7,387 @@
  */
 #include "SkClipStack.h"
 #include "SkPath.h"
+#include "SkThread.h"
+
 #include <new>
 
-struct SkClipStack::Rec {
-    enum State {
-        kEmpty_State,
-        kRect_State,
-        kPath_State
-    };
 
-    SkPath          fPath;
-    SkRect          fRect;
-    int             fSaveCount;
-    SkRegion::Op    fOp;
-    State           fState;
-    bool            fDoAA;
+// 0-2 are reserved for invalid, empty & wide-open
+static const int32_t kFirstUnreservedGenID = 3;
+int32_t SkClipStack::gGenID = kFirstUnreservedGenID;
 
-    Rec(int saveCount, const SkRect& rect, SkRegion::Op op, bool doAA) : fRect(rect) {
-        fSaveCount = saveCount;
-        fOp = op;
-        fState = kRect_State;
-        fDoAA = doAA;
+void SkClipStack::Element::invertShapeFillType() {
+    switch (fType) {
+        case kRect_Type:
+            fPath.reset();
+            fPath.addRect(fRect);
+            fPath.setFillType(SkPath::kInverseWinding_FillType);
+            fType = kPath_Type;
+            break;
+        case kPath_Type:
+            fPath.toggleInverseFillType();
+        case kEmpty_Type:
+            break;
     }
-
-    Rec(int saveCount, const SkPath& path, SkRegion::Op op, bool doAA) : fPath(path) {
-        fRect.setEmpty();
-        fSaveCount = saveCount;
-        fOp = op;
-        fState = kPath_State;
-        fDoAA = doAA;
-    }
-
-    bool operator==(const Rec& b) const {
-        if (fSaveCount != b.fSaveCount || fOp != b.fOp || fState != b.fState ||
-                fDoAA != b.fDoAA) {
-            return false;
-        }
-        switch (fState) {
-            case kEmpty_State:
-                return true;
-            case kRect_State:
-                return fRect == b.fRect;
-            case kPath_State:
-                return fPath == b.fPath;
-        }
-        return false;  // Silence the compiler.
-    }
-
-    bool operator!=(const Rec& b) const {
-        return !(*this == b);
-    }
-
-
-    /**
-     *  Returns true if this Rec can be intersected in place with a new clip
-     */
-    bool canBeIntersected(int saveCount, SkRegion::Op op) const {
-        if (kEmpty_State == fState && (
-                    SkRegion::kDifference_Op == op ||
-                    SkRegion::kIntersect_Op == op)) {
-            return true;
-        }
-        return  fSaveCount == saveCount &&
-                SkRegion::kIntersect_Op == fOp &&
-                SkRegion::kIntersect_Op == op;
-    }
-};
-
-SkClipStack::SkClipStack() : fDeque(sizeof(Rec)) {
-    fSaveCount = 0;
 }
 
-SkClipStack::SkClipStack(const SkClipStack& b) : fDeque(sizeof(Rec)) {
+void SkClipStack::Element::checkEmpty() const {
+    SkASSERT(fFiniteBound.isEmpty());
+    SkASSERT(kNormal_BoundsType == fFiniteBoundType);
+    SkASSERT(!fIsIntersectionOfRects);
+    SkASSERT(kEmptyGenID == fGenID);
+    SkASSERT(fPath.isEmpty());
+}
+
+bool SkClipStack::Element::canBeIntersectedInPlace(int saveCount, SkRegion::Op op) const {
+    if (kEmpty_Type == fType &&
+        (SkRegion::kDifference_Op == op || SkRegion::kIntersect_Op == op)) {
+        return true;
+    }
+    // Only clips within the same save/restore frame (as captured by
+    // the save count) can be merged
+    return  fSaveCount == saveCount &&
+            SkRegion::kIntersect_Op == op &&
+            (SkRegion::kIntersect_Op == fOp || SkRegion::kReplace_Op == fOp);
+}
+
+bool SkClipStack::Element::rectRectIntersectAllowed(const SkRect& newR, bool newAA) const {
+    SkASSERT(kRect_Type == fType);
+
+    if (fDoAA == newAA) {
+        // if the AA setting is the same there is no issue
+        return true;
+    }
+
+    if (!SkRect::Intersects(fRect, newR)) {
+        // The calling code will correctly set the result to the empty clip
+        return true;
+    }
+
+    if (fRect.contains(newR)) {
+        // if the new rect carves out a portion of the old one there is no
+        // issue
+        return true;
+    }
+
+    // So either the two overlap in some complex manner or newR contains oldR.
+    // In the first, case the edges will require different AA. In the second,
+    // the AA setting that would be carried forward is incorrect (e.g., oldR
+    // is AA while newR is BW but since newR contains oldR, oldR will be
+    // drawn BW) since the new AA setting will predominate.
+    return false;
+}
+
+// a mirror of combineBoundsRevDiff
+void SkClipStack::Element::combineBoundsDiff(FillCombo combination, const SkRect& prevFinite) {
+    switch (combination) {
+        case kInvPrev_InvCur_FillCombo:
+            // In this case the only pixels that can remain set
+            // are inside the current clip rect since the extensions
+            // to infinity of both clips cancel out and whatever
+            // is outside of the current clip is removed
+            fFiniteBoundType = kNormal_BoundsType;
+            break;
+        case kInvPrev_Cur_FillCombo:
+            // In this case the current op is finite so the only pixels
+            // that aren't set are whatever isn't set in the previous
+            // clip and whatever this clip carves out
+            fFiniteBound.join(prevFinite);
+            fFiniteBoundType = kInsideOut_BoundsType;
+            break;
+        case kPrev_InvCur_FillCombo:
+            // In this case everything outside of this clip's bound
+            // is erased, so the only pixels that can remain set
+            // occur w/in the intersection of the two finite bounds
+            if (!fFiniteBound.intersect(prevFinite)) {
+                fFiniteBound.setEmpty();
+                fGenID = kEmptyGenID;
+            }
+            fFiniteBoundType = kNormal_BoundsType;
+            break;
+        case kPrev_Cur_FillCombo:
+            // The most conservative result bound is that of the
+            // prior clip. This could be wildly incorrect if the
+            // second clip either exactly matches the first clip
+            // (which should yield the empty set) or reduces the
+            // size of the prior bound (e.g., if the second clip
+            // exactly matched the bottom half of the prior clip).
+            // We ignore these two possibilities.
+            fFiniteBound = prevFinite;
+            break;
+        default:
+            SkDEBUGFAIL("SkClipStack::Element::combineBoundsDiff Invalid fill combination");
+            break;
+    }
+}
+
+void SkClipStack::Element::combineBoundsXOR(int combination, const SkRect& prevFinite) {
+
+    switch (combination) {
+        case kInvPrev_Cur_FillCombo:       // fall through
+        case kPrev_InvCur_FillCombo:
+            // With only one of the clips inverted the result will always
+            // extend to infinity. The only pixels that may be un-writeable
+            // lie within the union of the two finite bounds
+            fFiniteBound.join(prevFinite);
+            fFiniteBoundType = kInsideOut_BoundsType;
+            break;
+        case kInvPrev_InvCur_FillCombo:
+            // The only pixels that can survive are within the
+            // union of the two bounding boxes since the extensions
+            // to infinity of both clips cancel out
+            // fall through!
+        case kPrev_Cur_FillCombo:
+            // The most conservative bound for xor is the
+            // union of the two bounds. If the two clips exactly overlapped
+            // the xor could yield the empty set. Similarly the xor
+            // could reduce the size of the original clip's bound (e.g.,
+            // if the second clip exactly matched the bottom half of the
+            // first clip). We ignore these two cases.
+            fFiniteBound.join(prevFinite);
+            fFiniteBoundType = kNormal_BoundsType;
+            break;
+        default:
+            SkDEBUGFAIL("SkClipStack::Element::combineBoundsXOR Invalid fill combination");
+            break;
+    }
+}
+
+// a mirror of combineBoundsIntersection
+void SkClipStack::Element::combineBoundsUnion(int combination, const SkRect& prevFinite) {
+
+    switch (combination) {
+        case kInvPrev_InvCur_FillCombo:
+            if (!fFiniteBound.intersect(prevFinite)) {
+                fFiniteBound.setEmpty();
+                fGenID = kWideOpenGenID;
+            }
+            fFiniteBoundType = kInsideOut_BoundsType;
+            break;
+        case kInvPrev_Cur_FillCombo:
+            // The only pixels that won't be drawable are inside
+            // the prior clip's finite bound
+            fFiniteBound = prevFinite;
+            fFiniteBoundType = kInsideOut_BoundsType;
+            break;
+        case kPrev_InvCur_FillCombo:
+            // The only pixels that won't be drawable are inside
+            // this clip's finite bound
+            break;
+        case kPrev_Cur_FillCombo:
+            fFiniteBound.join(prevFinite);
+            break;
+        default:
+            SkDEBUGFAIL("SkClipStack::Element::combineBoundsUnion Invalid fill combination");
+            break;
+    }
+}
+
+// a mirror of combineBoundsUnion
+void SkClipStack::Element::combineBoundsIntersection(int combination, const SkRect& prevFinite) {
+
+    switch (combination) {
+        case kInvPrev_InvCur_FillCombo:
+            // The only pixels that aren't writable in this case
+            // occur in the union of the two finite bounds
+            fFiniteBound.join(prevFinite);
+            fFiniteBoundType = kInsideOut_BoundsType;
+            break;
+        case kInvPrev_Cur_FillCombo:
+            // In this case the only pixels that will remain writeable
+            // are within the current clip
+            break;
+        case kPrev_InvCur_FillCombo:
+            // In this case the only pixels that will remain writeable
+            // are with the previous clip
+            fFiniteBound = prevFinite;
+            fFiniteBoundType = kNormal_BoundsType;
+            break;
+        case kPrev_Cur_FillCombo:
+            if (!fFiniteBound.intersect(prevFinite)) {
+                fFiniteBound.setEmpty();
+                fGenID = kEmptyGenID;
+            }
+            break;
+        default:
+            SkDEBUGFAIL("SkClipStack::Element::combineBoundsIntersection Invalid fill combination");
+            break;
+    }
+}
+
+// a mirror of combineBoundsDiff
+void SkClipStack::Element::combineBoundsRevDiff(int combination, const SkRect& prevFinite) {
+
+    switch (combination) {
+        case kInvPrev_InvCur_FillCombo:
+            // The only pixels that can survive are in the
+            // previous bound since the extensions to infinity in
+            // both clips cancel out
+            fFiniteBound = prevFinite;
+            fFiniteBoundType = kNormal_BoundsType;
+            break;
+        case kInvPrev_Cur_FillCombo:
+            if (!fFiniteBound.intersect(prevFinite)) {
+                fFiniteBound.setEmpty();
+                fGenID = kEmptyGenID;
+            }
+            fFiniteBoundType = kNormal_BoundsType;
+            break;
+        case kPrev_InvCur_FillCombo:
+            fFiniteBound.join(prevFinite);
+            fFiniteBoundType = kInsideOut_BoundsType;
+            break;
+        case kPrev_Cur_FillCombo:
+            // Fall through - as with the kDifference_Op case, the
+            // most conservative result bound is the bound of the
+            // current clip. The prior clip could reduce the size of this
+            // bound (as in the kDifference_Op case) but we are ignoring
+            // those cases.
+            break;
+        default:
+            SkDEBUGFAIL("SkClipStack::Element::combineBoundsRevDiff Invalid fill combination");
+            break;
+    }
+}
+
+void SkClipStack::Element::updateBoundAndGenID(const Element* prior) {
+    // We set this first here but we may overwrite it later if we determine that the clip is
+    // either wide-open or empty.
+    fGenID = GetNextGenID();
+
+    // First, optimistically update the current Element's bound information
+    // with the current clip's bound
+    fIsIntersectionOfRects = false;
+    if (kRect_Type == fType) {
+        fFiniteBound = fRect;
+        fFiniteBoundType = kNormal_BoundsType;
+
+        if (SkRegion::kReplace_Op == fOp ||
+            (SkRegion::kIntersect_Op == fOp && NULL == prior) ||
+            (SkRegion::kIntersect_Op == fOp && prior->fIsIntersectionOfRects &&
+                prior->rectRectIntersectAllowed(fRect, fDoAA))) {
+            fIsIntersectionOfRects = true;
+        }
+
+    } else {
+        SkASSERT(kPath_Type == fType);
+
+        fFiniteBound = fPath.getBounds();
+
+        if (fPath.isInverseFillType()) {
+            fFiniteBoundType = kInsideOut_BoundsType;
+        } else {
+            fFiniteBoundType = kNormal_BoundsType;
+        }
+    }
+
+    if (!fDoAA) {
+        // Here we mimic a non-anti-aliased scanline system. If there is
+        // no anti-aliasing we can integerize the bounding box to exclude
+        // fractional parts that won't be rendered.
+        // Note: the left edge is handled slightly differently below. We
+        // are a bit more generous in the rounding since we don't want to
+        // risk missing the left pixels when fLeft is very close to .5
+        fFiniteBound.set(SkIntToScalar(SkScalarFloorToInt(fFiniteBound.fLeft+0.45f)),
+                         SkIntToScalar(SkScalarRound(fFiniteBound.fTop)),
+                         SkIntToScalar(SkScalarRound(fFiniteBound.fRight)),
+                         SkIntToScalar(SkScalarRound(fFiniteBound.fBottom)));
+    }
+
+    // Now determine the previous Element's bound information taking into
+    // account that there may be no previous clip
+    SkRect prevFinite;
+    SkClipStack::BoundsType prevType;
+
+    if (NULL == prior) {
+        // no prior clip means the entire plane is writable
+        prevFinite.setEmpty();   // there are no pixels that cannot be drawn to
+        prevType = kInsideOut_BoundsType;
+    } else {
+        prevFinite = prior->fFiniteBound;
+        prevType = prior->fFiniteBoundType;
+    }
+
+    FillCombo combination = kPrev_Cur_FillCombo;
+    if (kInsideOut_BoundsType == fFiniteBoundType) {
+        combination = (FillCombo) (combination | 0x01);
+    }
+    if (kInsideOut_BoundsType == prevType) {
+        combination = (FillCombo) (combination | 0x02);
+    }
+
+    SkASSERT(kInvPrev_InvCur_FillCombo == combination ||
+                kInvPrev_Cur_FillCombo == combination ||
+                kPrev_InvCur_FillCombo == combination ||
+                kPrev_Cur_FillCombo == combination);
+
+    // Now integrate with clip with the prior clips
+    switch (fOp) {
+        case SkRegion::kDifference_Op:
+            this->combineBoundsDiff(combination, prevFinite);
+            break;
+        case SkRegion::kXOR_Op:
+            this->combineBoundsXOR(combination, prevFinite);
+            break;
+        case SkRegion::kUnion_Op:
+            this->combineBoundsUnion(combination, prevFinite);
+            break;
+        case SkRegion::kIntersect_Op:
+            this->combineBoundsIntersection(combination, prevFinite);
+            break;
+        case SkRegion::kReverseDifference_Op:
+            this->combineBoundsRevDiff(combination, prevFinite);
+            break;
+        case SkRegion::kReplace_Op:
+            // Replace just ignores everything prior
+            // The current clip's bound information is already filled in
+            // so nothing to do
+            break;
+        default:
+            SkDebugf("SkRegion::Op error/n");
+            SkASSERT(0);
+            break;
+    }
+}
+
+// This constant determines how many Element's are allocated together as a block in
+// the deque. As such it needs to balance allocating too much memory vs.
+// incurring allocation/deallocation thrashing. It should roughly correspond to
+// the deepest save/restore stack we expect to see.
+static const int kDefaultElementAllocCnt = 8;
+
+SkClipStack::SkClipStack()
+    : fDeque(sizeof(Element), kDefaultElementAllocCnt)
+    , fSaveCount(0) {
+}
+
+SkClipStack::SkClipStack(const SkClipStack& b)
+    : fDeque(sizeof(Element), kDefaultElementAllocCnt) {
     *this = b;
 }
 
+SkClipStack::SkClipStack(const SkRect& r)
+    : fDeque(sizeof(Element), kDefaultElementAllocCnt)
+    , fSaveCount(0) {
+    if (!r.isEmpty()) {
+        this->clipDevRect(r, SkRegion::kReplace_Op, false);
+    }
+}
+
+SkClipStack::SkClipStack(const SkIRect& r)
+    : fDeque(sizeof(Element), kDefaultElementAllocCnt)
+    , fSaveCount(0) {
+    if (!r.isEmpty()) {
+        SkRect temp;
+        temp.set(r);
+        this->clipDevRect(temp, SkRegion::kReplace_Op, false);
+    }
+}
+
+SkClipStack::~SkClipStack() {
+    reset();
+}
+
 SkClipStack& SkClipStack::operator=(const SkClipStack& b) {
     if (this == &b) {
         return *this;
@@ -90,38 +396,43 @@
 
     fSaveCount = b.fSaveCount;
     SkDeque::F2BIter recIter(b.fDeque);
-    for (const Rec* rec = (const Rec*)recIter.next();
-            rec != NULL;
-            rec = (const Rec*)recIter.next()) {
-        new (fDeque.push_back()) Rec(*rec);
+    for (const Element* element = (const Element*)recIter.next();
+         element != NULL;
+         element = (const Element*)recIter.next()) {
+        new (fDeque.push_back()) Element(*element);
     }
 
     return *this;
 }
 
 bool SkClipStack::operator==(const SkClipStack& b) const {
-    if (fSaveCount != b.fSaveCount || fDeque.count() != b.fDeque.count()) {
+    if (fSaveCount != b.fSaveCount ||
+        fDeque.count() != b.fDeque.count()) {
         return false;
     }
     SkDeque::F2BIter myIter(fDeque);
     SkDeque::F2BIter bIter(b.fDeque);
-    const Rec* myRec = (const Rec*)myIter.next();
-    const Rec* bRec = (const Rec*)bIter.next();
+    const Element* myElement = (const Element*)myIter.next();
+    const Element* bElement = (const Element*)bIter.next();
 
-    while (myRec != NULL && bRec != NULL) {
-        if (*myRec != *bRec) {
+    while (myElement != NULL && bElement != NULL) {
+        if (*myElement != *bElement) {
             return false;
         }
-        myRec = (const Rec*)myIter.next();
-        bRec = (const Rec*)bIter.next();
+        myElement = (const Element*)myIter.next();
+        bElement = (const Element*)bIter.next();
     }
-    return myRec == NULL && bRec == NULL;
+    return myElement == NULL && bElement == NULL;
 }
 
 void SkClipStack::reset() {
-    // don't have a reset() on SkDeque, so fake it here
-    fDeque.~SkDeque();
-    new (&fDeque) SkDeque(sizeof(Rec));
+    // We used a placement new for each object in fDeque, so we're responsible
+    // for calling the destructor on each of them as well.
+    while (!fDeque.empty()) {
+        Element* element = (Element*)fDeque.back();
+        element->~Element();
+        fDeque.pop_back();
+    }
 
     fSaveCount = 0;
 }
@@ -133,109 +444,327 @@
 void SkClipStack::restore() {
     fSaveCount -= 1;
     while (!fDeque.empty()) {
-        Rec* rec = (Rec*)fDeque.back();
-        if (rec->fSaveCount <= fSaveCount) {
+        Element* element = (Element*)fDeque.back();
+        if (element->fSaveCount <= fSaveCount) {
             break;
         }
-        rec->~Rec();
+        this->purgeClip(element);
+        element->~Element();
         fDeque.pop_back();
     }
 }
 
+void SkClipStack::getBounds(SkRect* canvFiniteBound,
+                            BoundsType* boundType,
+                            bool* isIntersectionOfRects) const {
+    SkASSERT(NULL != canvFiniteBound && NULL != boundType);
+
+    Element* element = (Element*)fDeque.back();
+
+    if (NULL == element) {
+        // the clip is wide open - the infinite plane w/ no pixels un-writeable
+        canvFiniteBound->setEmpty();
+        *boundType = kInsideOut_BoundsType;
+        if (NULL != isIntersectionOfRects) {
+            *isIntersectionOfRects = false;
+        }
+        return;
+    }
+
+    *canvFiniteBound = element->fFiniteBound;
+    *boundType = element->fFiniteBoundType;
+    if (NULL != isIntersectionOfRects) {
+        *isIntersectionOfRects = element->fIsIntersectionOfRects;
+    }
+}
+
+bool SkClipStack::intersectRectWithClip(SkRect* rect) const {
+    SkASSERT(NULL != rect);
+
+    SkRect bounds;
+    SkClipStack::BoundsType bt;
+    this->getBounds(&bounds, &bt);
+    if (bt == SkClipStack::kInsideOut_BoundsType) {
+        if (bounds.contains(*rect)) {
+            return false;
+        } else {
+            // If rect's x values are both within bound's x range we
+            // could clip here. Same for y. But we don't bother to check.
+            return true;
+        }
+    } else {
+        return rect->intersect(bounds);
+    }
+}
+
+bool SkClipStack::quickContains(const SkRect& rect) const {
+
+    Iter iter(*this, Iter::kTop_IterStart);
+    const Element* element = iter.prev();
+    while (element != NULL) {
+        if (SkRegion::kIntersect_Op != element->getOp() && SkRegion::kReplace_Op != element->getOp())
+            return false;
+        if (element->isInverseFilled()) {
+            // Part of 'rect' could be trimmed off by the inverse-filled clip element
+            if (SkRect::Intersects(element->getBounds(), rect)) {
+                return false;
+            }
+        } else {
+            if (!element->contains(rect)) {
+                return false;
+            }
+        }
+        if (SkRegion::kReplace_Op == element->getOp()) {
+            break;
+        }
+        element = iter.prev();
+    }
+    return true;
+}
+
 void SkClipStack::clipDevRect(const SkRect& rect, SkRegion::Op op, bool doAA) {
-    Rec* rec = (Rec*)fDeque.back();
-    if (rec && rec->canBeIntersected(fSaveCount, op)) {
-        switch (rec->fState) {
-            case Rec::kEmpty_State:
+
+    // Use reverse iterator instead of back because Rect path may need previous
+    SkDeque::Iter iter(fDeque, SkDeque::Iter::kBack_IterStart);
+    Element* element = (Element*) iter.prev();
+
+    if (element && element->canBeIntersectedInPlace(fSaveCount, op)) {
+        switch (element->fType) {
+            case Element::kEmpty_Type:
+                element->checkEmpty();
                 return;
-            case Rec::kRect_State:
-                if (!rec->fRect.intersect(rect)) {
-                    rec->fState = Rec::kEmpty_State;
+            case Element::kRect_Type:
+                if (element->rectRectIntersectAllowed(rect, doAA)) {
+                    this->purgeClip(element);
+                    if (!element->fRect.intersect(rect)) {
+                        element->setEmpty();
+                        return;
+                    }
+
+                    element->fDoAA = doAA;
+                    Element* prev = (Element*) iter.prev();
+                    element->updateBoundAndGenID(prev);
+                    return;
                 }
-                return;
-            case Rec::kPath_State:
-                if (!SkRect::Intersects(rec->fPath.getBounds(), rect)) {
-                    rec->fState = Rec::kEmpty_State;
+                break;
+            case Element::kPath_Type:
+                if (!SkRect::Intersects(element->fPath.getBounds(), rect)) {
+                    this->purgeClip(element);
+                    element->setEmpty();
                     return;
                 }
                 break;
         }
     }
-    new (fDeque.push_back()) Rec(fSaveCount, rect, op, doAA);
+    new (fDeque.push_back()) Element(fSaveCount, rect, op, doAA);
+    ((Element*) fDeque.back())->updateBoundAndGenID(element);
+
+    if (element && element->fSaveCount == fSaveCount) {
+        this->purgeClip(element);
+    }
 }
 
 void SkClipStack::clipDevPath(const SkPath& path, SkRegion::Op op, bool doAA) {
-    Rec* rec = (Rec*)fDeque.back();
-    if (rec && rec->canBeIntersected(fSaveCount, op)) {
+    SkRect alt;
+    if (path.isRect(&alt) && !path.isInverseFillType()) {
+        return this->clipDevRect(alt, op, doAA);
+    }
+
+    Element* element = (Element*)fDeque.back();
+    if (element && element->canBeIntersectedInPlace(fSaveCount, op)) {
         const SkRect& pathBounds = path.getBounds();
-        switch (rec->fState) {
-            case Rec::kEmpty_State:
+        switch (element->fType) {
+            case Element::kEmpty_Type:
+                element->checkEmpty();
                 return;
-            case Rec::kRect_State:
-                if (!SkRect::Intersects(rec->fRect, pathBounds)) {
-                    rec->fState = Rec::kEmpty_State;
+            case Element::kRect_Type:
+                if (!SkRect::Intersects(element->fRect, pathBounds)) {
+                    this->purgeClip(element);
+                    element->setEmpty();
                     return;
                 }
                 break;
-            case Rec::kPath_State:
-                if (!SkRect::Intersects(rec->fPath.getBounds(), pathBounds)) {
-                    rec->fState = Rec::kEmpty_State;
+            case Element::kPath_Type:
+                if (!SkRect::Intersects(element->fPath.getBounds(), pathBounds)) {
+                    this->purgeClip(element);
+                    element->setEmpty();
                     return;
                 }
                 break;
         }
     }
-    new (fDeque.push_back()) Rec(fSaveCount, path, op, doAA);
+    new (fDeque.push_back()) Element(fSaveCount, path, op, doAA);
+    ((Element*) fDeque.back())->updateBoundAndGenID(element);
+
+    if (element && element->fSaveCount == fSaveCount) {
+        this->purgeClip(element);
+    }
+}
+
+void SkClipStack::clipEmpty() {
+
+    Element* element = (Element*) fDeque.back();
+
+    if (element && element->canBeIntersectedInPlace(fSaveCount, SkRegion::kIntersect_Op)) {
+        switch (element->fType) {
+            case Element::kEmpty_Type:
+                element->checkEmpty();
+                return;
+            case Element::kRect_Type:
+            case Element::kPath_Type:
+                this->purgeClip(element);
+                element->setEmpty();
+                return;
+        }
+    }
+    new (fDeque.push_back()) Element(fSaveCount);
+
+    if (element && element->fSaveCount == fSaveCount) {
+        this->purgeClip(element);
+    }
+    ((Element*)fDeque.back())->fGenID = kEmptyGenID;
+}
+
+bool SkClipStack::isWideOpen() const {
+    if (0 == fDeque.count()) {
+        return true;
+    }
+
+    const Element* back = (const Element*) fDeque.back();
+    return kWideOpenGenID == back->fGenID ||
+           (kInsideOut_BoundsType == back->fFiniteBoundType && back->fFiniteBound.isEmpty());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkClipStack::B2FIter::B2FIter() {
+SkClipStack::Iter::Iter() : fStack(NULL) {
 }
 
-bool operator==(const SkClipStack::B2FIter::Clip& a,
-               const SkClipStack::B2FIter::Clip& b) {
-    return a.fOp == b.fOp && a.fDoAA == b.fDoAA &&
-           ((a.fRect == NULL && b.fRect == NULL) ||
-               (a.fRect != NULL && b.fRect != NULL && *a.fRect == *b.fRect)) &&
-           ((a.fPath == NULL && b.fPath == NULL) ||
-               (a.fPath != NULL && b.fPath != NULL && *a.fPath == *b.fPath));
+SkClipStack::Iter::Iter(const SkClipStack& stack, IterStart startLoc)
+    : fStack(&stack) {
+    this->reset(stack, startLoc);
 }
 
-bool operator!=(const SkClipStack::B2FIter::Clip& a,
-               const SkClipStack::B2FIter::Clip& b) {
-    return !(a == b);
+const SkClipStack::Element* SkClipStack::Iter::next() {
+    return (const SkClipStack::Element*)fIter.next();
 }
 
-SkClipStack::B2FIter::B2FIter(const SkClipStack& stack) {
-    this->reset(stack);
+const SkClipStack::Element* SkClipStack::Iter::prev() {
+    return (const SkClipStack::Element*)fIter.prev();
 }
 
-const SkClipStack::B2FIter::Clip* SkClipStack::B2FIter::next() {
-    const SkClipStack::Rec* rec = (const SkClipStack::Rec*)fIter.next();
-    if (NULL == rec) {
+const SkClipStack::Element* SkClipStack::Iter::skipToTopmost(SkRegion::Op op) {
+
+    if (NULL == fStack) {
         return NULL;
     }
 
-    switch (rec->fState) {
-        case SkClipStack::Rec::kEmpty_State:
-            fClip.fRect = NULL;
-            fClip.fPath = NULL;
+    fIter.reset(fStack->fDeque, SkDeque::Iter::kBack_IterStart);
+
+    const SkClipStack::Element* element = NULL;
+
+    for (element = (const SkClipStack::Element*) fIter.prev();
+         NULL != element;
+         element = (const SkClipStack::Element*) fIter.prev()) {
+
+        if (op == element->fOp) {
+            // The Deque's iterator is actually one pace ahead of the
+            // returned value. So while "element" is the element we want to
+            // return, the iterator is actually pointing at (and will
+            // return on the next "next" or "prev" call) the element
+            // in front of it in the deque. Bump the iterator forward a
+            // step so we get the expected result.
+            if (NULL == fIter.next()) {
+                // The reverse iterator has run off the front of the deque
+                // (i.e., the "op" clip is the first clip) and can't
+                // recover. Reset the iterator to start at the front.
+                fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
+            }
             break;
-        case SkClipStack::Rec::kRect_State:
-            fClip.fRect = &rec->fRect;
-            fClip.fPath = NULL;
-            break;
-        case SkClipStack::Rec::kPath_State:
-            fClip.fRect = NULL;
-            fClip.fPath = &rec->fPath;
-            break;
+        }
     }
-    fClip.fOp = rec->fOp;
-    fClip.fDoAA = rec->fDoAA;
-    return &fClip;
+
+    if (NULL == element) {
+        // There were no "op" clips
+        fIter.reset(fStack->fDeque, SkDeque::Iter::kFront_IterStart);
+    }
+
+    return this->next();
 }
 
-void SkClipStack::B2FIter::reset(const SkClipStack& stack) {
-    fIter.reset(stack.fDeque);
+void SkClipStack::Iter::reset(const SkClipStack& stack, IterStart startLoc) {
+    fStack = &stack;
+    fIter.reset(stack.fDeque, static_cast<SkDeque::Iter::IterStart>(startLoc));
+}
+
+// helper method
+void SkClipStack::getConservativeBounds(int offsetX,
+                                        int offsetY,
+                                        int maxWidth,
+                                        int maxHeight,
+                                        SkRect* devBounds,
+                                        bool* isIntersectionOfRects) const {
+    SkASSERT(NULL != devBounds);
+
+    devBounds->setLTRB(0, 0,
+                       SkIntToScalar(maxWidth), SkIntToScalar(maxHeight));
+
+    SkRect temp;
+    SkClipStack::BoundsType boundType;
+
+    // temp starts off in canvas space here
+    this->getBounds(&temp, &boundType, isIntersectionOfRects);
+    if (SkClipStack::kInsideOut_BoundsType == boundType) {
+        return;
+    }
+
+    // but is converted to device space here
+    temp.offset(SkIntToScalar(offsetX), SkIntToScalar(offsetY));
+
+    if (!devBounds->intersect(temp)) {
+        devBounds->setEmpty();
+    }
+}
+
+void SkClipStack::addPurgeClipCallback(PFPurgeClipCB callback, void* data) const {
+    ClipCallbackData temp = { callback, data };
+    fCallbackData.append(1, &temp);
+}
+
+void SkClipStack::removePurgeClipCallback(PFPurgeClipCB callback, void* data) const {
+    ClipCallbackData temp = { callback, data };
+    int index = fCallbackData.find(temp);
+    if (index >= 0) {
+        fCallbackData.removeShuffle(index);
+    }
+}
+
+// The clip state represented by 'element' will never be used again. Purge it.
+void SkClipStack::purgeClip(Element* element) {
+    SkASSERT(NULL != element);
+    if (element->fGenID >= 0 && element->fGenID < kFirstUnreservedGenID) {
+        return;
+    }
+
+    for (int i = 0; i < fCallbackData.count(); ++i) {
+        (*fCallbackData[i].fCallback)(element->fGenID, fCallbackData[i].fData);
+    }
+
+    // Invalidate element's gen ID so handlers can detect already handled records
+    element->fGenID = kInvalidGenID;
+}
+
+int32_t SkClipStack::GetNextGenID() {
+    // TODO: handle overflow.
+    return sk_atomic_inc(&gGenID);
+}
+
+int32_t SkClipStack::getTopmostGenID() const {
+
+    if (fDeque.empty()) {
+        return kInvalidGenID;
+    }
+
+    Element* element = (Element*)fDeque.back();
+    return element->fGenID;
 }
diff --git a/src/core/SkColor.cpp b/src/core/SkColor.cpp
index fe7b8f8..6fa239f 100644
--- a/src/core/SkColor.cpp
+++ b/src/core/SkColor.cpp
@@ -51,7 +51,7 @@
     SkScalar s = ByteDivToScalar(delta, max);
     SkASSERT(s >= 0 && s <= SK_Scalar1);
 
-    SkScalar h;    
+    SkScalar h;
     if (r == max) {
         h = ByteDivToScalar(g - b, delta);
     } else if (g == max) {
@@ -92,12 +92,12 @@
     }
     SkFixed hx = (hsv[0] < 0 || hsv[0] >= SkIntToScalar(360)) ? 0 : SkScalarToFixed(hsv[0]/60);
     SkFixed f = hx & 0xFFFF;
-    
+
     unsigned v_scale = SkAlpha255To256(v);
     unsigned p = SkAlphaMul(255 - s, v_scale);
     unsigned q = SkAlphaMul(255 - (s * f >> 16), v_scale);
     unsigned t = SkAlphaMul(255 - (s * (SK_Fixed1 - f) >> 16), v_scale);
-    
+
     unsigned r, g, b;
 
     SkASSERT((unsigned)(hx >> 16) < 6);
@@ -111,4 +111,3 @@
     }
     return SkColorSetARGB(a, r, g, b);
 }
-
diff --git a/src/core/SkColorFilter.cpp b/src/core/SkColorFilter.cpp
index 2ca88bb..383397d 100644
--- a/src/core/SkColorFilter.cpp
+++ b/src/core/SkColorFilter.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,24 +5,28 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkColorFilter.h"
+#include "SkFilterShader.h"
+#include "SkFlattenableBuffers.h"
 #include "SkShader.h"
 #include "SkUnPreMultiply.h"
+#include "SkString.h"
 
-bool SkColorFilter::asColorMode(SkColor* color, SkXfermode::Mode* mode) {
+SK_DEFINE_INST_COUNT(SkColorFilter)
+
+bool SkColorFilter::asColorMode(SkColor* color, SkXfermode::Mode* mode) const {
     return false;
 }
 
-bool SkColorFilter::asColorMatrix(SkScalar matrix[20]) {
+bool SkColorFilter::asColorMatrix(SkScalar matrix[20]) const {
     return false;
 }
 
-bool SkColorFilter::asComponentTable(SkBitmap*) {
+bool SkColorFilter::asComponentTable(SkBitmap*) const {
     return false;
 }
 
-void SkColorFilter::filterSpan16(const uint16_t s[], int count, uint16_t d[]) {
+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");
 
@@ -32,12 +35,16 @@
     }
 }
 
-SkColor SkColorFilter::filterColor(SkColor c) {
+SkColor SkColorFilter::filterColor(SkColor c) const {
     SkPMColor dst, src = SkPreMultiplyColor(c);
     this->filterSpan(&src, 1, &dst);
     return SkUnPreMultiply::PMColorToColor(dst);
 }
 
+GrEffectRef* SkColorFilter::asNewEffect(GrContext*) const {
+    return NULL;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkFilterShader::SkFilterShader(SkShader* shader, SkColorFilter* filter) {
@@ -47,8 +54,8 @@
 
 SkFilterShader::SkFilterShader(SkFlattenableReadBuffer& buffer) :
         INHERITED(buffer) {
-    fShader = static_cast<SkShader*>(buffer.readFlattenable());
-    fFilter = static_cast<SkColorFilter*>(buffer.readFlattenable());
+    fShader = buffer.readFlattenableT<SkShader>();
+    fFilter = buffer.readFlattenableT<SkColorFilter>();
 }
 
 SkFilterShader::~SkFilterShader() {
@@ -56,17 +63,7 @@
     fShader->unref();
 }
 
-void SkFilterShader::beginSession() {
-    this->INHERITED::beginSession();
-    fShader->beginSession();
-}
-
-void SkFilterShader::endSession() {
-    fShader->endSession();
-    this->INHERITED::endSession();
-}
-
-void SkFilterShader::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkFilterShader::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
     buffer.writeFlattenable(fShader);
     buffer.writeFlattenable(fFilter);
@@ -75,7 +72,7 @@
 uint32_t SkFilterShader::getFlags() {
     uint32_t shaderF = fShader->getFlags();
     uint32_t filterF = 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;
@@ -90,8 +87,22 @@
 bool SkFilterShader::setContext(const SkBitmap& device,
                                 const SkPaint& paint,
                                 const SkMatrix& matrix) {
-    return  this->INHERITED::setContext(device, paint, matrix) &&
-            fShader->setContext(device, paint, matrix);
+    // we need to keep the setContext/endContext calls balanced. If we return
+    // false, our endContext() will not be called.
+
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+    if (!fShader->setContext(device, paint, matrix)) {
+        this->INHERITED::endContext();
+        return false;
+    }
+    return true;
+}
+
+void SkFilterShader::endContext() {
+    fShader->endContext();
+    this->INHERITED::endContext();
 }
 
 void SkFilterShader::shadeSpan(int x, int y, SkPMColor result[], int count) {
@@ -107,3 +118,17 @@
     fFilter->filterSpan16(result, count, result);
 }
 
+#ifdef SK_DEVELOPER
+void SkFilterShader::toString(SkString* str) const {
+    str->append("SkFilterShader: (");
+
+    str->append("Shader: ");
+    fShader->toString(str);
+    str->append(" Filter: ");
+    // TODO: add "fFilter->toString(str);" once SkColorFilter::toString is added
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/core/SkColorTable.cpp b/src/core/SkColorTable.cpp
index 4a9480d..e1ebf92 100644
--- a/src/core/SkColorTable.cpp
+++ b/src/core/SkColorTable.cpp
@@ -7,11 +7,13 @@
  */
 
 
-#include "SkBitmap.h"
-#include "SkFlattenable.h"
+#include "SkColorTable.h"
+#include "SkFlattenableBuffers.h"
 #include "SkStream.h"
 #include "SkTemplates.h"
 
+SK_DEFINE_INST_COUNT(SkColorTable)
+
 SkColorTable::SkColorTable(int count)
     : f16BitCache(NULL), fFlags(0)
 {
@@ -28,8 +30,9 @@
     SkDEBUGCODE(f16BitCacheLockCount = 0;)
 }
 
-// call SkRefCnt's constructor explicitly, to avoid warning
-SkColorTable::SkColorTable(const SkColorTable& src) : SkRefCnt() {
+// As copy constructor is hidden in the class hierarchy, we need to call
+// default constructor explicitly to suppress a compiler warning.
+SkColorTable::SkColorTable(const SkColorTable& src) : INHERITED() {
     f16BitCache = NULL;
     fFlags = src.fFlags;
     int count = src.count();
@@ -78,7 +81,7 @@
 void SkColorTable::unlockColors(bool changed)
 {
     SkASSERT(fColorLockCount != 0);
-    SkDEBUGCODE(fColorLockCount -= 1;)
+    SkDEBUGCODE(sk_atomic_dec(&fColorLockCount);)
     if (changed)
         this->inval16BitCache();
 }
@@ -140,19 +143,17 @@
     SkDEBUGCODE(fColorLockCount = 0;)
     SkDEBUGCODE(f16BitCacheLockCount = 0;)
 
-    fCount = buffer.readU16();
-    SkASSERT((unsigned)fCount <= 256);
-
-    fFlags = buffer.readU8();
-
+    fFlags = buffer.readUInt();
+    fCount = buffer.getArrayCount();
     fColors = (SkPMColor*)sk_malloc_throw(fCount * sizeof(SkPMColor));
-    buffer.read(fColors, fCount * sizeof(SkPMColor));
+    SkDEBUGCODE(const uint32_t countRead =) buffer.readColorArray(fColors);
+#ifdef SK_DEBUG
+    SkASSERT((unsigned)fCount <= 256);
+    SkASSERT(countRead == fCount);
+#endif
 }
 
 void SkColorTable::flatten(SkFlattenableWriteBuffer& buffer) const {
-    int count = this->count();
-    buffer.write16(count);
-    buffer.write8(this->getFlags());
-    buffer.writeMul4(fColors, count * sizeof(SkPMColor));
+    buffer.writeUInt(fFlags);
+    buffer.writeColorArray(fColors, fCount);
 }
-
diff --git a/src/core/SkComposeShader.cpp b/src/core/SkComposeShader.cpp
index c8d3299..fd3b21e 100644
--- a/src/core/SkComposeShader.cpp
+++ b/src/core/SkComposeShader.cpp
@@ -11,7 +11,9 @@
 #include "SkColorFilter.h"
 #include "SkColorPriv.h"
 #include "SkColorShader.h"
+#include "SkFlattenableBuffers.h"
 #include "SkXfermode.h"
+#include "SkString.h"
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -25,15 +27,15 @@
 
 SkComposeShader::SkComposeShader(SkFlattenableReadBuffer& buffer) :
     INHERITED(buffer) {
-    fShaderA = static_cast<SkShader*>(buffer.readFlattenable());
+    fShaderA = buffer.readFlattenableT<SkShader>();
     if (NULL == fShaderA) {
         fShaderA = SkNEW_ARGS(SkColorShader, (0));
     }
-    fShaderB = static_cast<SkShader*>(buffer.readFlattenable());
+    fShaderB = buffer.readFlattenableT<SkShader>();
     if (NULL == fShaderB) {
         fShaderB = SkNEW_ARGS(SkColorShader, (0));
     }
-    fMode = static_cast<SkXfermode*>(buffer.readFlattenable());
+    fMode = buffer.readFlattenableT<SkXfermode>();
 }
 
 SkComposeShader::~SkComposeShader() {
@@ -42,18 +44,6 @@
     fShaderA->unref();
 }
 
-void SkComposeShader::beginSession() {
-    this->INHERITED::beginSession();
-    fShaderA->beginSession();
-    fShaderB->beginSession();
-}
-
-void SkComposeShader::endSession() {
-    fShaderA->endSession();
-    fShaderB->endSession();
-    this->INHERITED::endSession();
-}
-
 class SkAutoAlphaRestore {
 public:
     SkAutoAlphaRestore(SkPaint* paint, uint8_t newAlpha) {
@@ -70,7 +60,7 @@
     uint8_t     fAlpha;
 };
 
-void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkComposeShader::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
     buffer.writeFlattenable(fShaderA);
     buffer.writeFlattenable(fShaderB);
@@ -80,7 +70,10 @@
 /*  We call setContext on our two worker shaders. However, we
     always let them see opaque alpha, and if the paint really
     is translucent, then we apply that after the fact.
-*/
+
+    We need to keep the calls to setContext/endContext balanced, since if we
+    return false, our endContext() will not be called.
+ */
 bool SkComposeShader::setContext(const SkBitmap& device,
                                  const SkPaint& paint,
                                  const SkMatrix& matrix) {
@@ -93,13 +86,29 @@
 
     SkMatrix tmpM;
 
-    (void)this->getLocalMatrix(&tmpM);
-    tmpM.setConcat(matrix, tmpM);
+    tmpM.setConcat(matrix, this->getLocalMatrix());
 
     SkAutoAlphaRestore  restore(const_cast<SkPaint*>(&paint), 0xFF);
 
-    return  fShaderA->setContext(device, paint, tmpM) &&
-            fShaderB->setContext(device, paint, tmpM);
+    bool setContextA = fShaderA->setContext(device, paint, tmpM);
+    bool setContextB = fShaderB->setContext(device, paint, tmpM);
+    if (!setContextA || !setContextB) {
+        if (setContextB) {
+            fShaderB->endContext();
+        }
+        else if (setContextA) {
+            fShaderA->endContext();
+        }
+        this->INHERITED::endContext();
+        return false;
+    }
+    return true;
+}
+
+void SkComposeShader::endContext() {
+    fShaderB->endContext();
+    fShaderA->endContext();
+    this->INHERITED::endContext();
 }
 
 // larger is better (fewer times we have to loop), but we shouldn't
@@ -165,3 +174,19 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkComposeShader::toString(SkString* str) const {
+    str->append("SkComposeShader: (");
+
+    str->append("ShaderA: ");
+    fShaderA->toString(str);
+    str->append(" ShaderB: ");
+    fShaderB->toString(str);
+    str->append(" Xfermode: ");
+    // TODO: add "fMode->toString(str);" once SkXfermode::toString is added
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/core/SkConcaveToTriangles.cpp b/src/core/SkConcaveToTriangles.cpp
deleted file mode 100644
index 2ca6795..0000000
--- a/src/core/SkConcaveToTriangles.cpp
+++ /dev/null
@@ -1,960 +0,0 @@
-
-/*
- * Copyright 2009 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// This is an implementation of the triangulation algorithm described by Alain
-// Fournier and Delfin Montuno, in "Triangulating Simple Polygons and Equivalent
-// Problems", in the ACM Transactions on Graphics, vol. 3, no. 2, April 1984,
-// pp. 153-174.
-//
-// No new vertices are created in the triangulation: triangles are constructed
-// only from the points in the original polygon, so there is no possibility for
-// cracks to develop from finite precision arithmetic.
-////////////////////////////////////////////////////////////////////////////////
-
-// TODO:
-// - RemoveDegenerateTrapezoids() was added to make the code robust, but it
-//   unfortunately introduces T-vertices. Make it robust without T-vertices.
-// - It should be easy enough to detect whether the outer contour is right- or
-//   left-handed by looking at the top vertex, which is available in the
-//   pre-sort during trapezoidization. Use this information in angleIsConvex()
-//   to allowed either handedness outer contour. In either case, though, holes
-//   need to have the opposite orientation.
-// - SkTHeapSort was broken, so I wrote a bubble sort so that I could make other
-//   things work. Use SkQSort() instead.
-// - The ActiveTrapezoid array does a linear search which is O(n) inefficient.
-//   Use SkSearch to implement O(log n) binary search and insertion sort.
-// - There is no need to use SkTDArray for everything. Use SkAutoTMalloc for
-//   everything else.
-
-#include "SkTDArray.h"
-#include "SkGeometry.h"
-#include "SkTSort.h"
-
-// This is used to prevent runaway code bugs, and can probably be removed after
-// the code has been proven robust.
-#define kMaxCount 1000
-
-#define DEBUG
-#ifdef DEBUG
-//------------------------------------------------------------------------------
-// Debugging support
-//------------------------------------------------------------------------------
-
-#include <cstdio>
-#include <stdarg.h>
-
-static int gDebugLevel = 0;   // This enables debug reporting.
-
-static void DebugPrintf(const char *format, ...) {
-    if (gDebugLevel > 0) {
-        va_list ap;
-        va_start(ap, format);
-        vprintf(format, ap);
-        va_end(ap);
-    }
-}
-
-static void FailureMessage(const char *format, ...) {
-    if (1) {
-        printf("FAILURE: ");
-        va_list ap;
-        va_start(ap, format);
-        vprintf(format, ap);
-        va_end(ap);
-    }
-}
-#else  // !DEBUG
-inline void DebugPrintf(const char *format, ...) {}
-inline void FailureMessage(const char *format, ...) {}
-#endif  // DEBUG
-
-
-// Forward declaration.
-class Vertex;
-
-
-//------------------------------------------------------------------------------
-// The Trapezoid (actually, up to two of them) is embedded into a Vertex, whose
-// point() provides the top of the Trapezoid, whereas the bottom, and the left
-// and right edges, are stored in the Trapezoid. The edges are represented by
-// their tail point; the head is the successive point modulo the number of
-// points in the polygon. Only the Y coordinate of the top and bottom are
-// relevant.
-//------------------------------------------------------------------------------
-class Trapezoid {
-public:
-    const Vertex* left()   const    { return fLeft;   }
-    const Vertex* right()  const    { return fRight;  }
-    const Vertex* bottom() const    { return fBottom; }
-    Vertex* left()                  { return fLeft;   }
-    Vertex* right()                 { return fRight;  }
-    Vertex* bottom()                { return fBottom; }
-    void   setLeft(Vertex *left)    { fLeft   = left;   }
-    void  setRight(Vertex *right)   { fRight  = right;  }
-    void setBottom(Vertex *bottom)  { fBottom = bottom; }
-    void nullify()                  { setBottom(NULL); }
-
-    bool operator<(Trapezoid &t1)   { return compare(t1) < 0; }
-    bool operator>(Trapezoid &t1)   { return compare(t1) > 0; }
-
-private:
-    Vertex *fLeft, *fRight, *fBottom;
-
-    // These return a number that is less than, equal to, or greater than zero
-    // depending on whether the trapezoid or point is to the left, on, or to the
-    // right.
-    SkScalar compare(const Trapezoid &t1) const;
-    SkScalar compare(const SkPoint &p) const;
-};
-
-
-//------------------------------------------------------------------------------
-// The ActiveTrapezoids are a sorted list containing the currently active
-// trapezoids, i.e. those that have the top, left, and right, but still need the
-// bottom. This could use some optimization, to reduce search time from O(n) to
-// O(log n).
-//------------------------------------------------------------------------------
-class ActiveTrapezoids {
-public:
-    ActiveTrapezoids() { fTrapezoids.setCount(0); }
-
-    size_t count() const { return fTrapezoids.count(); }
-
-    // Select an unused trapezoid from the Vertex vt, initialize it with the
-    // left and right edges, and insert it into the sorted list.
-    bool insertNewTrapezoid(Vertex *vt, Vertex *left, Vertex *right);
-
-    // Remove the specified Trapezoids from the active list.
-    void remove(Trapezoid *t);
-
-    // Determine whether the given point lies within any active trapezoid, and
-    // return a pointer to that Trapezoid.
-    bool withinActiveTrapezoid(const SkPoint &pt, Trapezoid **tp);
-
-    // Find an active trapezoid that contains the given edge.
-    Trapezoid* getTrapezoidWithEdge(const Vertex *edge);
-
-private:
-    // Insert the specified Trapezoid into the sorted list.
-    void insert(Trapezoid *t);
-
-    // The sorted list of active trapezoids. This is O(n), and would benefit
-    // a 2-3 tree o achieve O(log n).
-    SkTDArray<Trapezoid*> fTrapezoids;  // Fournier suggests a 2-3 tree instead.
-};
-
-
-//------------------------------------------------------------------------------
-// The Vertex is used to communicate information between the various phases of
-// triangulation.
-//------------------------------------------------------------------------------
-class Vertex {
-public:
-    enum VertexType { MONOTONE, CONVEX, CONCAVE };
-
-    Trapezoid fTrap0;
-    Trapezoid fTrap1;
-
-    const SkPoint &point() const        { return fPoint; }
-    void setPoint(const SkPoint &point) { fPoint = point; }
-
-    // The next and previous vertices around the polygon.
-    Vertex *next()                      { return fNext; }
-    Vertex *prev()                      { return fPrev; }
-    const Vertex *next() const          { return fNext; }
-    const Vertex *prev() const          { return fPrev; }
-    void setNext(Vertex *next)          { fNext = next; }
-    void setPrev(Vertex *prev)          { fPrev = prev; }
-
-    void setDone(bool done)             { fDone = done; }
-    bool done() const                   { return fDone; }
-
-    // Trapezoid accessors return non-null for any complete trapezoids.
-    void trapezoids(Trapezoid **trap0, Trapezoid **trap1) {
-        *trap0 = (fTrap0.bottom() != NULL) ? &fTrap0 : NULL;
-        *trap1 = (fTrap1.bottom() != NULL) ? &fTrap1 : NULL;
-    }
-
-    // Eliminate a trapezoid.
-    void nullifyTrapezoid() {
-        if (fTrap1.bottom() != NULL) {
-            DebugPrintf("Unexpected non-null second trapezoid.\n");
-            fTrap1.nullify();
-            return;
-        }
-        fTrap0.nullify();
-    }
-
-    // Determine whether the edge specified by this Vertex shares the given top
-    // and bottom.
-    bool shareEdge(Vertex *top, Vertex *bottom);
-
-    // Determines whether the angle specified by { prev, this, next } is convex.
-    // Note that collinear is considered to be convex.
-    bool angleIsConvex();
-
-    // Remove this Vertex from the { prev, next } linked list.
-    void delink() {
-        Vertex *p = prev(),
-               *n = next();
-        p->setNext(n);
-        n->setPrev(p);
-    }
-
-    // Return a number that is less than, equal to, or greater than zero
-    // depending on whether the point is to the left, on, or to the right of the
-    // edge that has this Vertex as a base.
-    SkScalar compare(const SkPoint &pt) const;
-
-    // Classify the vertex, and return its sorted edges.
-    VertexType classify(Vertex **e0, Vertex **e1);
-
-    // This helps determine unimonotonicity.
-    Vertex *diagonal();
-
-private:
-    SkPoint fPoint;
-    Vertex *fNext;
-    Vertex *fPrev;
-    bool fDone;
-};
-
-
-bool Vertex::angleIsConvex() {
-    SkPoint vPrev = prev()->point() - point(),
-            vNext = next()->point() - point();
-    // TODO(turk): There might be overflow in fixed-point.
-    return SkPoint::CrossProduct(vNext, vPrev) >= 0;
-}
-
-
-bool Vertex::shareEdge(Vertex *top, Vertex *bottom) {
-    return (((this == top)    && (this->next() == bottom)) ||
-            ((this == bottom) && (this->next() == top)));
-}
-
-
-SkScalar Vertex::compare(const SkPoint &pt) const  {
-    SkPoint ve = next()->point() - point(),
-            vp = pt              - point();
-    SkScalar d;
-    if (ve.fY == 0) {
-        // Return twice the distance to the center of the horizontal edge.
-        d = ve.fX + pt.fX - next()->point().fX;
-    } else {
-        // Return the distance to the edge, scaled by the edge length.
-        d = SkPoint::CrossProduct(ve, vp);
-        if (ve.fY > 0) d = -d;
-    }
-    return d;
-}
-
-
-SkScalar Trapezoid::compare(const SkPoint &pt) const {
-    SkScalar d = left()->compare(pt);
-    if (d <= 0)
-        return d;   // Left of the left edge.
-    d = right()->compare(pt);
-    if (d >= 0)
-        return d;   // Right of the right edge.
-    return 0;       // Somewhere between the left and the right edges.
-}
-
-
-
-SkScalar Trapezoid::compare(const Trapezoid &t1) const {
-#if 1
-    SkScalar d = left()->compare(t1.left()->point());
-    if (d == 0)
-        d = right()->compare(t1.right()->point());
-    return d;
-#else
-    SkScalar dl =  left()->compare( t1.left()->point()),
-             dr = right()->compare(t1.right()->point());
-    if (dl < 0 && dr < 0)
-        return dr;
-    if (dl > 0 && dr > 0)
-        return dl;
-    return 0;
-#endif
-}
-
-
-Trapezoid* ActiveTrapezoids::getTrapezoidWithEdge(const Vertex *edge) {
-    DebugPrintf("Entering getTrapezoidWithEdge(): looking through %d\n",
-           fTrapezoids.count());
-    DebugPrintf("trying to find %p: ", edge);
-    Trapezoid **tp;
-    for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) {
-        SkASSERT(tp != NULL);
-        SkASSERT(*tp != NULL);
-        DebugPrintf("%p and %p, ", (**tp).left(), (**tp).right());
-        if ((**tp).left() == edge || (**tp).right() == edge) {
-            DebugPrintf("\ngetTrapezoidWithEdge found the trapezoid\n");
-            return *tp;
-        }
-    }
-    DebugPrintf("getTrapezoidWithEdge found no trapezoid\n");
-    return NULL;
-}
-
-
-bool ActiveTrapezoids::insertNewTrapezoid(Vertex *vt,
-                                          Vertex *left,
-                                          Vertex *right) {
-    DebugPrintf("Inserting a trapezoid...");
-    if (vt->fTrap0.left() == NULL && vt->fTrap0.right() == NULL) {
-        vt->fTrap0.setLeft(left);
-        vt->fTrap0.setRight(right);
-        insert(&vt->fTrap0);
-    } else if (vt->fTrap1.left() == NULL && vt->fTrap1.right() == NULL) {
-        DebugPrintf("a second one...");
-        vt->fTrap1.setLeft(left);
-        vt->fTrap1.setRight(right);
-        if (vt->fTrap1 < vt->fTrap0) {  // Keep trapezoids sorted.
-            remove(&vt->fTrap0);
-            Trapezoid t = vt->fTrap0;
-            vt->fTrap0 = vt->fTrap1;
-            vt->fTrap1 = t;
-            insert(&vt->fTrap0);
-        }
-        insert(&vt->fTrap1);
-    } else {
-        FailureMessage("More than 2 trapezoids requested for a vertex\n");
-        return false;
-    }
-    DebugPrintf(" done. %d incomplete trapezoids\n", fTrapezoids.count());
-    return true;
-}
-
-
-void ActiveTrapezoids::insert(Trapezoid *t) {
-    Trapezoid **tp;
-    for (tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp)
-        if (**tp > *t)
-            break;
-    fTrapezoids.insert(tp - fTrapezoids.begin(), 1, &t);
-    // SHOULD VERIFY THAT ALL TRAPEZOIDS ARE PROPERLY SORTED
-}
-
-
-void ActiveTrapezoids::remove(Trapezoid *t) {
-    DebugPrintf("Removing a trapezoid...");
-    for (Trapezoid **tp = fTrapezoids.begin(); tp < fTrapezoids.end(); ++tp) {
-        if (*tp == t) {
-            fTrapezoids.remove(tp - fTrapezoids.begin());
-            DebugPrintf(" done.\n");
-            return;
-        }
-    }
-    DebugPrintf(" Arghh! Panic!\n");
-    SkASSERT(t == 0);   // Cannot find t in active trapezoid list.
-}
-
-
-bool ActiveTrapezoids::withinActiveTrapezoid(const SkPoint &pt,
-                                             Trapezoid **trap) {
-    DebugPrintf("Entering withinActiveTrapezoid()\n");
-    // This is where a good search data structure would be helpful.
-    Trapezoid **t;
-    for (t = fTrapezoids.begin(); t < fTrapezoids.end(); ++t) {
-        if ((**t).left()->compare(pt) <= 0) {
-            // The point is to the left of the left edge of this trapezoid.
-            DebugPrintf("withinActiveTrapezoid: Before a trapezoid\n");
-            *trap = *t;     // Return the place where a new trapezoid would go.
-// We have a bug with the sorting -- look through everything.
-            continue;
-//             return false;   // Outside all trapezoids, since they are sorted.
-        }
-        // The point is to the right of the left edge of this trapezoid.
-
-        if ((**t).right()->compare(pt) < 0) {
-            // The point is to the left of the right edge.
-            DebugPrintf("withinActiveTrapezoid: Within an Active Trapezoid\n");
-            *trap = *t;
-            return true;
-        }
-    }
-
-    // The point is to the right of all trapezoids.
-    DebugPrintf("withinActiveTrapezoid: After all trapezoids\n");
-    *trap = NULL;
-    return false;
-}
-
-
-Vertex* Vertex::diagonal() {
-    Vertex *diag = NULL;
-    if (fTrap0.bottom() != NULL) {
-        if (!fTrap0.left() ->shareEdge(this, fTrap0.bottom()) &&
-            !fTrap0.right()->shareEdge(this, fTrap0.bottom())
-        ) {
-            diag = fTrap0.bottom();
-            fTrap0 = fTrap1;
-            fTrap1.nullify();
-        } else if (fTrap1.bottom() != NULL &&
-                  !fTrap1.left() ->shareEdge(this, fTrap1.bottom()) &&
-                  !fTrap1.right()->shareEdge(this, fTrap1.bottom())
-        ) {
-            diag = fTrap1.bottom();
-            fTrap1.nullify();
-        }
-    }
-    return diag;
-}
-
-
-// We use this to sort the edges vertically for a scan-conversion type of
-// operation.
-class VertexPtr {
-public:
-    Vertex *vt;
-};
-
-
-bool operator<(VertexPtr &v0, VertexPtr &v1) {
-    // DebugPrintf("< %p %p\n", &v0, &v1);
-    if (v0.vt->point().fY < v1.vt->point().fY)  return true;
-    if (v0.vt->point().fY > v1.vt->point().fY)  return false;
-    if (v0.vt->point().fX < v1.vt->point().fX)  return true;
-    else                                        return false;
-}
-
-
-bool operator>(VertexPtr &v0, VertexPtr &v1) {
-    // DebugPrintf("> %p %p\n", &v0, &v1);
-    if (v0.vt->point().fY > v1.vt->point().fY)  return true;
-    if (v0.vt->point().fY < v1.vt->point().fY)  return false;
-    if (v0.vt->point().fX > v1.vt->point().fX)  return true;
-    else                                        return false;
-}
-
-
-static void SetVertexPoints(size_t numPts, const SkPoint *pt, Vertex *vt) {
-    for (; numPts-- != 0; ++pt, ++vt)
-        vt->setPoint(*pt);
-}
-
-
-static void InitializeVertexTopology(size_t numPts, Vertex *v1) {
-    Vertex *v0 = v1 + numPts - 1, *v_1 = v0 - 1;
-    for (; numPts-- != 0; v_1 = v0, v0 = v1++) {
-        v0->setPrev(v_1);
-        v0->setNext(v1);
-    }
-}
-
-Vertex::VertexType Vertex::classify(Vertex **e0, Vertex **e1) {
-    VertexType type;
-    SkPoint vPrev, vNext;
-    vPrev.fX = prev()->point().fX - point().fX;
-    vPrev.fY = prev()->point().fY - point().fY;
-    vNext.fX = next()->point().fX - point().fX;
-    vNext.fY = next()->point().fY - point().fY;
-
-    // This can probably be simplified, but there are enough potential bugs,
-    // we will leave it expanded until all cases are tested appropriately.
-    if (vPrev.fY < 0) {
-        if (vNext.fY > 0) {
-            // Prev comes from above, Next goes below.
-            type = MONOTONE;
-            *e0 = prev();
-            *e1 = this;
-        } else if (vNext.fY < 0) {
-            // The are both above: sort so that e0 is on the left.
-            type = CONCAVE;
-            if (SkPoint::CrossProduct(vPrev, vNext) <= 0) {
-                *e0 = this;
-                *e1 = prev();
-            } else {
-                *e0 = prev();
-                *e1 = this;
-            }
-        } else {
-            DebugPrintf("### py < 0, ny = 0\n");
-            if (vNext.fX < 0) {
-                type = CONCAVE;
-                *e0 = this;     // flat to the left
-                *e1 = prev();   // concave on the right
-            } else {
-                type = CONCAVE;
-                *e0 = prev();   // concave to the left
-                *e1 = this;     // flat to the right
-            }
-        }
-    } else if (vPrev.fY > 0) {
-        if (vNext.fY < 0) {
-            // Next comes from above, Prev goes below.
-            type = MONOTONE;
-            *e0 = this;
-            *e1 = prev();
-        } else if (vNext.fY > 0) {
-            // They are both below: sort so that e0 is on the left.
-            type = CONVEX;
-            if (SkPoint::CrossProduct(vPrev, vNext) <= 0) {
-                *e0 = prev();
-                *e1 = this;
-            } else {
-                *e0 = this;
-                *e1 = prev();
-            }
-        } else {
-            DebugPrintf("### py > 0, ny = 0\n");
-            if (vNext.fX < 0) {
-                type = MONOTONE;
-                *e0 = this;     // flat to the left
-                *e1 = prev();   // convex on the right - try monotone first
-            } else {
-                type = MONOTONE;
-                *e0 = prev();   // convex to the left - try monotone first
-                *e1 = this;     // flat to the right
-            }
-        }
-    } else {  // vPrev.fY == 0
-        if (vNext.fY < 0) {
-            DebugPrintf("### py = 0, ny < 0\n");
-            if (vPrev.fX < 0) {
-                type = CONCAVE;
-                *e0 = prev();   // flat to the left
-                *e1 = this;     // concave on the right
-            } else {
-                type = CONCAVE;
-                *e0 = this;     // concave on the left - defer
-                *e1 = prev();   // flat to the right
-            }
-        } else if (vNext.fY > 0) {
-            DebugPrintf("### py = 0, ny > 0\n");
-            if (vPrev.fX < 0) {
-                type = MONOTONE;
-                *e0 = prev();   // flat to the left
-                *e1 = this;     // convex on the right - try monotone first
-            } else {
-                type = MONOTONE;
-                *e0 = this;     // convex to the left - try monotone first
-                *e1 = prev();   // flat to the right
-            }
-        } else {
-            DebugPrintf("### py = 0, ny = 0\n");
-            // First we try concave, then monotone, then convex.
-            if (vPrev.fX <= vNext.fX) {
-                type = CONCAVE;
-                *e0 = prev();   // flat to the left
-                *e1 = this;     // flat to the right
-            } else {
-                type = CONCAVE;
-                *e0 = this;     // flat to the left
-                *e1 = prev();   // flat to the right
-            }
-        }
-    }
-    return type;
-}
-
-
-#ifdef DEBUG
-static const char* GetVertexTypeString(Vertex::VertexType type) {
-    const char *typeStr = NULL;
-    switch (type) {
-        case Vertex::MONOTONE:
-            typeStr = "MONOTONE";
-            break;
-        case Vertex::CONCAVE:
-            typeStr = "CONCAVE";
-            break;
-        case Vertex::CONVEX:
-            typeStr = "CONVEX";
-            break;
-    }
-    return typeStr;
-}
-
-
-static void PrintVertices(size_t numPts, Vertex *vt) {
-    DebugPrintf("\nVertices:\n");
-    for (size_t i = 0; i < numPts; i++) {
-        Vertex *e0, *e1;
-        Vertex::VertexType type = vt[i].classify(&e0, &e1);
-        DebugPrintf("%2d: (%.7g, %.7g), prev(%d), next(%d), "
-                    "type(%s), left(%d), right(%d)",
-                    i, vt[i].point().fX, vt[i].point().fY,
-                    vt[i].prev() - vt, vt[i].next() - vt,
-                    GetVertexTypeString(type), e0 - vt, e1 - vt);
-        Trapezoid *trap[2];
-        vt[i].trapezoids(trap, trap+1);
-        for (int j = 0; j < 2; ++j) {
-            if (trap[j] != NULL) {
-                DebugPrintf(", trap(L=%d, R=%d, B=%d)",
-                            trap[j]->left()   - vt,
-                            trap[j]->right()  - vt,
-                            trap[j]->bottom() - vt);
-            }
-        }
-        DebugPrintf("\n");
-    }
-}
-
-
-static void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {
-    DebugPrintf("\nSorted Vertices:\n");
-    for (size_t i = 0; i < numPts; i++) {
-        Vertex *e0, *e1;
-        Vertex *vt = vp[i].vt;
-        Vertex::VertexType type = vt->classify(&e0, &e1);
-        DebugPrintf("%2d: %2d: (%.7g, %.7g), prev(%d), next(%d), "
-                    "type(%s), left(%d), right(%d)",
-                    i, vt - vtBase, vt->point().fX, vt->point().fY,
-                    vt->prev() - vtBase, vt->next() - vtBase,
-                    GetVertexTypeString(type), e0 - vtBase, e1 - vtBase);
-        Trapezoid *trap[2];
-        vt->trapezoids(trap, trap+1);
-        for (int j = 0; j < 2; ++j) {
-            if (trap[j] != NULL) {
-                DebugPrintf(", trap(L=%d, R=%d, B=%d)",
-                            trap[j]->left()   - vtBase,
-                            trap[j]->right()  - vtBase,
-                            trap[j]->bottom() - vtBase);
-            }
-        }
-        DebugPrintf("\n");
-    }
-}
-#else  // !DEBUG
-inline void PrintVertices(size_t numPts, Vertex *vt) {}
-inline void PrintVertexPtrs(size_t numPts, VertexPtr *vp, Vertex *vtBase) {}
-#endif  // !DEBUG
-
-
-// SkTHeapSort is broken, so we use a bubble sort in the meantime.
-template <typename T>
-void BubbleSort(T *array, size_t count) {
-    bool sorted;
-    size_t count_1 = count - 1;
-    do {
-        sorted = true;
-        for (size_t i = 0; i < count_1; ++i) {
-            if (array[i + 1] < array[i]) {
-                T t = array[i];
-                array[i] = array[i + 1];
-                array[i + 1] = t;
-                sorted = false;
-            }
-        }
-    } while (!sorted);
-}
-
-
-// Remove zero-height trapezoids.
-static void RemoveDegenerateTrapezoids(size_t numVt, Vertex *vt) {
-    for (; numVt-- != 0; vt++) {
-        Trapezoid *traps[2];
-        vt->trapezoids(traps, traps+1);
-        if (traps[1] != NULL &&
-                vt->point().fY >= traps[1]->bottom()->point().fY) {
-            traps[1]->nullify();
-            traps[1] = NULL;
-        }
-        if (traps[0] != NULL &&
-                vt->point().fY >= traps[0]->bottom()->point().fY) {
-            if (traps[1] != NULL) {
-                *traps[0] = *traps[1];
-                traps[1]->nullify();
-            } else {
-                traps[0]->nullify();
-            }
-        }
-    }
-}
-
-
-// Enhance the polygon with trapezoids.
-bool ConvertPointsToVertices(size_t numPts, const SkPoint *pts, Vertex *vta) {
-    DebugPrintf("ConvertPointsToVertices()\n");
-
-    // Clear everything.
-    DebugPrintf("Zeroing vertices\n");
-    sk_bzero(vta, numPts * sizeof(*vta));
-
-    // Initialize vertices.
-    DebugPrintf("Initializing vertices\n");
-    SetVertexPoints(numPts, pts, vta);
-    InitializeVertexTopology(numPts, vta);
-
-    PrintVertices(numPts, vta);
-
-    SkTDArray<VertexPtr> vtptr;
-    vtptr.setCount(numPts);
-    for (int i = numPts; i-- != 0;)
-        vtptr[i].vt = vta + i;
-    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
-    DebugPrintf("Sorting vertrap ptr array [%d] %p %p\n", vtptr.count(),
-        &vtptr[0], &vtptr[vtptr.count() - 1]
-    );
-//  SkTHeapSort(vtptr.begin(), vtptr.count());
-    BubbleSort(vtptr.begin(), vtptr.count());
-    DebugPrintf("Done sorting\n");
-    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
-
-    DebugPrintf("Traversing sorted vertrap ptrs\n");
-    ActiveTrapezoids incompleteTrapezoids;
-    for (VertexPtr *vtpp = vtptr.begin(); vtpp < vtptr.end(); ++vtpp) {
-        DebugPrintf("%d: sorted vertrap %d\n",
-                    vtpp - vtptr.begin(), vtpp->vt - vta);
-        Vertex *vt = vtpp->vt;
-        Vertex *e0, *e1;
-        Trapezoid *t;
-        switch (vt->classify(&e0, &e1)) {
-            case Vertex::MONOTONE:
-            monotone:
-                DebugPrintf("MONOTONE %d %d\n", e0 - vta, e1 - vta);
-                // We should find one edge.
-                t = incompleteTrapezoids.getTrapezoidWithEdge(e0);
-                if (t == NULL) {      // One of the edges is flat.
-                    DebugPrintf("Monotone: cannot find a trapezoid with e0: "
-                                "trying convex\n");
-                    goto convex;
-                }
-                t->setBottom(vt);     // This trapezoid is now complete.
-                incompleteTrapezoids.remove(t);
-
-                if (e0 == t->left())  // Replace the left edge.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right());
-                else                  // Replace the right edge.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e1);
-                break;
-
-            case Vertex::CONVEX:      // Start of a new trapezoid.
-            convex:
-                // We don't expect to find any edges.
-                DebugPrintf("CONVEX %d %d\n", e0 - vta, e1 - vta);
-                if (incompleteTrapezoids.withinActiveTrapezoid(
-                        vt->point(), &t)) {
-                    // Complete trapezoid.
-                    SkASSERT(t != NULL);
-                    t->setBottom(vt);
-                    incompleteTrapezoids.remove(t);
-                    // Introduce two new trapezoids.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(), e0);
-                    incompleteTrapezoids.insertNewTrapezoid(vt, e1, t->right());
-                } else {
-                    // Insert a new trapezoid.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, e0, e1);
-                }
-                break;
-
-            case Vertex::CONCAVE:   // End of a trapezoid.
-                DebugPrintf("CONCAVE %d %d\n", e0 - vta, e1 - vta);
-                // We should find two edges.
-                t = incompleteTrapezoids.getTrapezoidWithEdge(e0);
-                if (t == NULL) {
-                    DebugPrintf("Concave: cannot find a trapezoid with e0: "
-                                " trying monotone\n");
-                    goto monotone;
-                }
-                SkASSERT(t != NULL);
-                if (e0 == t->left() && e1 == t->right()) {
-                    DebugPrintf(
-                            "Concave edges belong to the same trapezoid.\n");
-                    // Edges belong to the same trapezoid.
-                    // Complete trapezoid & transfer it from the active list.
-                    t->setBottom(vt);
-                    incompleteTrapezoids.remove(t);
-                } else {  // Edges belong to different trapezoids
-                    DebugPrintf(
-                            "Concave edges belong to different trapezoids.\n");
-                    // Complete left and right trapezoids.
-                    Trapezoid *s = incompleteTrapezoids.getTrapezoidWithEdge(
-                                                                            e1);
-                    if (s == NULL) {
-                        DebugPrintf(
-                                "Concave: cannot find a trapezoid with e1: "
-                                " trying monotone\n");
-                        goto monotone;
-                    }
-                    t->setBottom(vt);
-                    s->setBottom(vt);
-                    incompleteTrapezoids.remove(t);
-                    incompleteTrapezoids.remove(s);
-
-                    // Merge the two trapezoids into one below this vertex.
-                    incompleteTrapezoids.insertNewTrapezoid(vt, t->left(),
-                                                                s->right());
-                }
-                break;
-        }
-    }
-
-    RemoveDegenerateTrapezoids(numPts, vta);
-
-    DebugPrintf("Done making trapezoids\n");
-    PrintVertexPtrs(vtptr.count(), vtptr.begin(), vta);
-
-    size_t k = incompleteTrapezoids.count();
-    if (k > 0) {
-        FailureMessage("%d incomplete trapezoids\n", k);
-        return false;
-    }
-    return true;
-}
-
-
-inline void appendTriangleAtVertex(const Vertex *v,
-                                   SkTDArray<SkPoint> *triangles) {
-    SkPoint *p = triangles->append(3);
-    p[0] = v->prev()->point();
-    p[1] = v->point();
-    p[2] = v->next()->point();
-    DebugPrintf(
-          "Appending triangle: { (%.7g, %.7g), (%.7g, %.7g), (%.7g, %.7g) }\n",
-          p[0].fX, p[0].fY,
-          p[1].fX, p[1].fY,
-          p[2].fX, p[2].fY
-    );
-}
-
-
-static size_t CountVertices(const Vertex *first, const Vertex *last) {
-    DebugPrintf("Counting vertices: ");
-    size_t count = 1;
-    for (; first != last; first = first->next()) {
-        ++count;
-        SkASSERT(count <= kMaxCount);
-        if (count >= kMaxCount) {
-            FailureMessage("Vertices do not seem to be in a linked chain\n");
-            break;
-        }
-    }
-    return count;
-}
-
-
-bool operator<(const SkPoint &p0, const SkPoint &p1) {
-    if (p0.fY < p1.fY)  return true;
-    if (p0.fY > p1.fY)  return false;
-    if (p0.fX < p1.fX)  return true;
-    else                return false;
-}
-
-
-static void PrintLinkedVertices(size_t n, Vertex *vertices) {
-    DebugPrintf("%d vertices:\n", n);
-    Vertex *v;
-    for (v = vertices; n-- != 0; v = v->next())
-        DebugPrintf("   (%.7g, %.7g)\n", v->point().fX, v->point().fY);
-    if (v != vertices)
-        FailureMessage("Vertices are not in a linked chain\n");
-}
-
-
-// Triangulate an unimonotone chain.
-bool TriangulateMonotone(Vertex *first, Vertex *last,
-                         SkTDArray<SkPoint> *triangles) {
-    DebugPrintf("TriangulateMonotone()\n");
-
-    size_t numVertices = CountVertices(first, last);
-    if (numVertices == kMaxCount) {
-        FailureMessage("Way too many vertices: %d:\n", numVertices);
-        PrintLinkedVertices(numVertices, first);
-        return false;
-    }
-
-    Vertex *start = first;
-    // First find the point with the smallest Y.
-    DebugPrintf("TriangulateMonotone: finding bottom\n");
-    int count = kMaxCount;    // Maximum number of vertices.
-    for (Vertex *v = first->next(); v != first && count-- > 0; v = v->next())
-        if (v->point() < start->point())
-            start = v;
-    if (count <= 0) {
-        FailureMessage("TriangulateMonotone() was given disjoint chain\n");
-        return false;   // Something went wrong.
-    }
-
-    // Start at the far end of the long edge.
-    if (start->prev()->point() < start->next()->point())
-        start = start->next();
-
-    Vertex *current = start->next();
-    while (numVertices >= 3) {
-        if (current->angleIsConvex()) {
-            DebugPrintf("Angle %p is convex\n", current);
-            // Print the vertices
-            PrintLinkedVertices(numVertices, start);
-
-            appendTriangleAtVertex(current, triangles);
-            if (triangles->count() > kMaxCount * 3) {
-                FailureMessage("An extraordinarily large number of triangles "
-                               "were generated\n");
-                return false;
-            }
-            Vertex *save = current->prev();
-            current->delink();
-            current = (save == start || save == start->prev()) ? start->next()
-                                                               : save;
-            --numVertices;
-        } else {
-            if (numVertices == 3) {
-                FailureMessage("Convexity error in TriangulateMonotone()\n");
-                appendTriangleAtVertex(current, triangles);
-                return false;
-            }
-            DebugPrintf("Angle %p is concave\n", current);
-            current = current->next();
-        }
-    }
-    return true;
-}
-
-
-// Split the polygon into sets of unimonotone chains, and eventually call
-// TriangulateMonotone() to convert them into triangles.
-bool Triangulate(Vertex *first, Vertex *last, SkTDArray<SkPoint> *triangles) {
-    DebugPrintf("Triangulate()\n");
-    Vertex *currentVertex = first;
-    while (!currentVertex->done()) {
-        currentVertex->setDone(true);
-        Vertex *bottomVertex = currentVertex->diagonal();
-        if (bottomVertex != NULL) {
-            Vertex *saveNext = currentVertex->next();
-            Vertex *savePrev = bottomVertex->prev();
-            currentVertex->setNext(bottomVertex);
-            bottomVertex->setPrev(currentVertex);
-            currentVertex->nullifyTrapezoid();
-            bool success = Triangulate(bottomVertex, currentVertex, triangles);
-            currentVertex->setDone(false);
-            bottomVertex->setDone(false);
-            currentVertex->setNext(saveNext);
-            bottomVertex->setPrev(savePrev);
-            bottomVertex->setNext(currentVertex);
-            currentVertex->setPrev(bottomVertex);
-            return Triangulate(currentVertex, bottomVertex, triangles)
-                   && success;
-        } else {
-            currentVertex = currentVertex->next();
-        }
-    }
-    return TriangulateMonotone(first, last, triangles);
-}
-
-
-bool SkConcaveToTriangles(size_t numPts,
-                          const SkPoint pts[],
-                          SkTDArray<SkPoint> *triangles) {
-    DebugPrintf("SkConcaveToTriangles()\n");
-
-    SkTDArray<Vertex> vertices;
-    vertices.setCount(numPts);
-    if (!ConvertPointsToVertices(numPts, pts, vertices.begin()))
-        return false;
-
-    triangles->setReserve(numPts);
-    triangles->setCount(0);
-    return Triangulate(vertices.begin(), vertices.end() - 1, triangles);
-}
diff --git a/src/core/SkConcaveToTriangles.h b/src/core/SkConcaveToTriangles.h
deleted file mode 100644
index e7eb09b..0000000
--- a/src/core/SkConcaveToTriangles.h
+++ /dev/null
@@ -1,31 +0,0 @@
-
-/*
- * Copyright 2009 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef SkConcaveToTriangles_DEFINED
-#define SkConcaveToTriangles_DEFINED
-
-#include "SkPoint.h"
-#include "SkTDArray.h"
-
-
-// Triangulate a polygon.
-// The polygon can be convex or concave, and can have holes or multiple contours
-// of arbitrary recursion.
-// The holes must have opposite orientation of the outer contours, whereas
-// islands within the holes must have the same orientation as the outer contour.
-// Contours should be joined by zero-thickness double-edges, to mimic a single
-// polygon.  The polygon should not be self-intersecting.
-// Currently, the outer contour must be right-handed, i.e. it should be oriented
-// in the direction that rotates the X-axis to the Y-axis.
-bool SkConcaveToTriangles(size_t count,
-                          const SkPoint pts[],
-                          SkTDArray<SkPoint> *triangles);
-
-
-#endif  // SkConcaveToTriangles_DEFINED
diff --git a/src/core/SkConfig8888.cpp b/src/core/SkConfig8888.cpp
index 10a1b36..f889f20 100644
--- a/src/core/SkConfig8888.cpp
+++ b/src/core/SkConfig8888.cpp
@@ -1,4 +1,5 @@
 #include "SkConfig8888.h"
+#include "SkMathPriv.h"
 
 namespace {
 
diff --git a/src/core/SkConfig8888.h b/src/core/SkConfig8888.h
index a891370..96eaef2 100644
--- a/src/core/SkConfig8888.h
+++ b/src/core/SkConfig8888.h
@@ -31,34 +31,17 @@
                           uint32_t g,
                           uint32_t b);
 
+///////////////////////////////////////////////////////////////////////////////
+// Implementation
+
 namespace {
 
 /**
   Copies all pixels from a bitmap to a dst ptr with a given rowBytes and
   Config8888. The bitmap must have kARGB_8888_Config.
  */
-inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels,
-                                     size_t dstRowBytes,
-                                     SkCanvas::Config8888 dstConfig8888,
-                                     const SkBitmap& srcBmp);
 
-/**
-  Copies over all pixels in a bitmap from a src ptr with a given rowBytes and
-  Config8888. The bitmap must have pixels and be kARGB_8888_Config.
- */
-inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp,
-                                     const uint32_t* srcPixels,
-                                     size_t srcRowBytes,
-                                     SkCanvas::Config8888 srcConfig8888);
-
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Implementation
-
-namespace {
-
-inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels,
+static inline void SkCopyBitmapToConfig8888(uint32_t* dstPixels,
                                      size_t dstRowBytes,
                                      SkCanvas::Config8888 dstConfig8888,
                                      const SkBitmap& srcBmp) {
@@ -72,7 +55,11 @@
     SkConvertConfig8888Pixels(dstPixels, dstRowBytes, dstConfig8888, srcPixels, srcRowBytes, SkCanvas::kNative_Premul_Config8888, w, h);
 }
 
-inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp,
+/**
+  Copies over all pixels in a bitmap from a src ptr with a given rowBytes and
+  Config8888. The bitmap must have pixels and be kARGB_8888_Config.
+ */
+static inline void SkCopyConfig8888ToBitmap(const SkBitmap& dstBmp,
                                      const uint32_t* srcPixels,
                                      size_t srcRowBytes,
                                      SkCanvas::Config8888 srcConfig8888) {
diff --git a/src/core/SkCordic.cpp b/src/core/SkCordic.cpp
index 8fb60c5..00dd76e 100644
--- a/src/core/SkCordic.cpp
+++ b/src/core/SkCordic.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,9 +5,8 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkCordic.h"
-#include "SkMath.h"
+#include "SkMathPriv.h"
 #include "Sk64.h"
 
 // 0x20000000 equals pi / 4
@@ -20,7 +18,7 @@
 
 const int32_t kFixedInvGain1 = 0x18bde0bb;  // 0.607252935
 
-static void SkCircularRotation(int32_t* x0, int32_t* y0, int32_t* z0) 
+static void SkCircularRotation(int32_t* x0, int32_t* y0, int32_t* z0)
 {
     int32_t t = 0;
     int32_t x = *x0;
@@ -30,7 +28,7 @@
    do {
         int32_t x1 = y >> t;
         int32_t y1 = x >> t;
-        int32_t tan = *tanPtr++;    
+        int32_t tan = *tanPtr++;
         if (z >= 0) {
             x -= x1;
             y += y1;
@@ -51,7 +49,7 @@
     int32_t scaledRadians = radians * 0x28be;   // scale radians to 65536 / PI()
     int quadrant = scaledRadians >> 30;
     quadrant += 1;
-    if (quadrant & 2) 
+    if (quadrant & 2)
         scaledRadians = -scaledRadians + 0x80000000;
     /* |a| <= 90 degrees as a 1.31 number */
     SkFixed sin = 0;
@@ -67,14 +65,14 @@
     return sin;
 }
 
-SkFixed SkCordicTan(SkFixed a) 
+SkFixed SkCordicTan(SkFixed a)
 {
     int32_t cos;
     int32_t sin = SkCordicSinCos(a, &cos);
     return SkFixedDiv(sin, cos);
 }
 
-static int32_t SkCircularVector(int32_t* y0, int32_t* x0, int32_t vecMode) 
+static int32_t SkCircularVector(int32_t* y0, int32_t* x0, int32_t vecMode)
 {
     int32_t x = *x0;
     int32_t y = *y0;
@@ -84,7 +82,7 @@
    do {
         int32_t x1 = y >> t;
         int32_t y1 = x >> t;
-        int32_t tan = *tanPtr++;    
+        int32_t tan = *tanPtr++;
         if (y < vecMode) {
             x -= x1;
             y += y1;
@@ -135,7 +133,7 @@
     return result;
 }
 
-const int32_t kATanHDegrees[] = { 
+const int32_t kATanHDegrees[] = {
     0x1661788D, 0xA680D61, 0x51EA6FC, 0x28CBFDD, 0x1460E34,
     0xA2FCE8, 0x517D2E, 0x28BE6E, 0x145F32,
     0xA2F98, 0x517CC, 0x28BE6, 0x145F3, 0xA2F9, 0x517C,
@@ -144,7 +142,7 @@
 
 const int32_t kFixedInvGain2 = 0x31330AAA;  // 1.207534495
 
-static void SkHyperbolic(int32_t* x0, int32_t* y0, int32_t* z0, int mode) 
+static void SkHyperbolic(int32_t* x0, int32_t* y0, int32_t* z0, int mode)
 {
     int32_t t = 1;
     int32_t x = *x0;
@@ -155,7 +153,7 @@
     do {
         int32_t x1 = y >> t;
         int32_t y1 = x >> t;
-        int32_t tan = *tanPtr++;    
+        int32_t tan = *tanPtr++;
         int count = 2 + (k >> 31);
         if (++k == 1)
             k = -2;
@@ -197,13 +195,11 @@
 
 #ifdef SK_DEBUG
 
-#ifdef SK_CAN_USE_FLOAT
-    #include "SkFloatingPoint.h"
-#endif
+#include "SkFloatingPoint.h"
 
 void SkCordic_UnitTest()
 {
-#if defined(SK_SUPPORT_UNITTEST) && defined(SK_CAN_USE_FLOAT)
+#if defined(SK_SUPPORT_UNITTEST)
     float val;
     for (float angle = -720; angle < 720; angle += 30) {
         float radian = angle * 3.1415925358f / 180.0f;
diff --git a/src/core/SkCordic.h b/src/core/SkCordic.h
index b70f987..fecf645 100644
--- a/src/core/SkCordic.h
+++ b/src/core/SkCordic.h
@@ -25,5 +25,4 @@
     void SkCordic_UnitTest();
 #endif
 
-#endif // SkCordic 
-
+#endif // SkCordic
diff --git a/src/core/SkCoreBlitters.h b/src/core/SkCoreBlitters.h
index 4947198..823cb41 100644
--- a/src/core/SkCoreBlitters.h
+++ b/src/core/SkCoreBlitters.h
@@ -94,6 +94,7 @@
     SkColor                fColor;
     SkPMColor              fPMColor;
     SkBlitRow::ColorProc   fColor32Proc;
+    SkBlitRow::ColorRectProc fColorRect32Proc;
 
 private:
     unsigned fSrcA, fSrcR, fSrcG, fSrcB;
@@ -128,15 +129,19 @@
 public:
     SkARGB32_Shader_Blitter(const SkBitmap& device, const SkPaint& paint);
     virtual ~SkARGB32_Shader_Blitter();
-    virtual void blitH(int x, int y, int width);
-    virtual void blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]);
-    virtual void blitMask(const SkMask&, const SkIRect&);
+    virtual void blitH(int x, int y, int width) SK_OVERRIDE;
+    virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE;
+    virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
+    virtual void blitAntiH(int x, int y, const SkAlpha[], const int16_t[]) SK_OVERRIDE;
+    virtual void blitMask(const SkMask&, const SkIRect&) SK_OVERRIDE;
 
 private:
     SkXfermode*         fXfermode;
     SkPMColor*          fBuffer;
     SkBlitRow::Proc32   fProc32;
     SkBlitRow::Proc32   fProc32Blend;
+    bool                fShadeDirectlyIntoDevice;
+    bool                fConstInY;
 
     // illegal
     SkARGB32_Shader_Blitter& operator=(const SkARGB32_Shader_Blitter&);
@@ -184,4 +189,3 @@
                                        void* storage, size_t storageSize);
 
 #endif
-
diff --git a/src/core/SkCubicClipper.cpp b/src/core/SkCubicClipper.cpp
index 662591f..aed681b 100644
--- a/src/core/SkCubicClipper.cpp
+++ b/src/core/SkCubicClipper.cpp
@@ -10,7 +10,9 @@
 #include "SkCubicClipper.h"
 #include "SkGeometry.h"
 
-SkCubicClipper::SkCubicClipper() {}
+SkCubicClipper::SkCubicClipper() {
+    fClip.setEmpty();
+}
 
 void SkCubicClipper::setClip(const SkIRect& clip) {
     // conver to scalars, since that's where we'll see the points
@@ -158,4 +160,3 @@
     }
     return true;
 }
-
diff --git a/src/core/SkData.cpp b/src/core/SkData.cpp
index f9f4043..3e0c71a 100644
--- a/src/core/SkData.cpp
+++ b/src/core/SkData.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,9 +5,10 @@
  * found in the LICENSE file.
  */
 
-
-
 #include "SkData.h"
+#include "SkFlattenableBuffers.h"
+
+SK_DEFINE_INST_COUNT(SkData)
 
 SkData::SkData(const void* ptr, size_t size, ReleaseProc proc, void* context) {
     fPtr = ptr;
@@ -23,6 +23,14 @@
     }
 }
 
+bool SkData::equals(const SkData* other) const {
+    if (NULL == other) {
+        return false;
+    }
+
+    return fSize == other->fSize && !memcmp(fPtr, other->fPtr, fSize);
+}
+
 size_t SkData::copyRange(size_t offset, size_t length, void* buffer) const {
     size_t available = fSize;
     if (offset >= available || 0 == length) {
@@ -101,3 +109,194 @@
                          const_cast<SkData*>(src));
 }
 
+SkData* SkData::NewWithCString(const char cstr[]) {
+    size_t size;
+    if (NULL == cstr) {
+        cstr = "";
+        size = 1;
+    } else {
+        size = strlen(cstr) + 1;
+    }
+    return NewWithCopy(cstr, size);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkData::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeByteArray(fPtr, fSize);
+}
+
+SkData::SkData(SkFlattenableReadBuffer& buffer) {
+    fSize = buffer.getArrayCount();
+    fReleaseProcContext = NULL;
+
+    if (fSize > 0) {
+        fPtr = sk_malloc_throw(fSize);
+        fReleaseProc = sk_free_releaseproc;
+    } else {
+        fPtr = NULL;
+        fReleaseProc = NULL;
+    }
+
+    buffer.readByteArray(const_cast<void*>(fPtr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkDataSet.h"
+#include "SkFlattenable.h"
+#include "SkStream.h"
+
+static SkData* dupdata(SkData* data) {
+    if (data) {
+        data->ref();
+    } else {
+        data = SkData::NewEmpty();
+    }
+    return data;
+}
+
+static SkData* findValue(const char key[], const SkDataSet::Pair array[], int n) {
+    for (int i = 0; i < n; ++i) {
+        if (!strcmp(key, array[i].fKey)) {
+            return array[i].fValue;
+        }
+    }
+    return NULL;
+}
+
+static SkDataSet::Pair* allocatePairStorage(int count, size_t storage) {
+    size_t size = count * sizeof(SkDataSet::Pair) + storage;
+    return (SkDataSet::Pair*)sk_malloc_throw(size);
+}
+
+SkDataSet::SkDataSet(const char key[], SkData* value) {
+    size_t keyLen = strlen(key);
+
+    fCount = 1;
+    fKeySize = keyLen + 1;
+    fPairs = allocatePairStorage(1, keyLen + 1);
+
+    fPairs[0].fKey = (char*)(fPairs + 1);
+    memcpy(const_cast<char*>(fPairs[0].fKey), key, keyLen + 1);
+
+    fPairs[0].fValue = dupdata(value);
+}
+
+SkDataSet::SkDataSet(const Pair array[], int count) {
+    if (count < 1) {
+        fCount = 0;
+        fKeySize = 0;
+        fPairs = NULL;
+        return;
+    }
+
+    int i;
+    size_t keySize = 0;
+    for (i = 0; i < count; ++i) {
+        keySize += strlen(array[i].fKey) + 1;
+    }
+
+    Pair* pairs = fPairs = allocatePairStorage(count, keySize);
+    char* keyStorage = (char*)(pairs + count);
+
+    keySize = 0;    // reset this, so we can compute the size for unique keys
+    int uniqueCount = 0;
+    for (int i = 0; i < count; ++i) {
+        if (!findValue(array[i].fKey, pairs, uniqueCount)) {
+            size_t len = strlen(array[i].fKey);
+            memcpy(keyStorage, array[i].fKey, len + 1);
+            pairs[uniqueCount].fKey = keyStorage;
+            keyStorage += len + 1;
+            keySize += len + 1;
+
+            pairs[uniqueCount].fValue = dupdata(array[i].fValue);
+            uniqueCount += 1;
+        }
+    }
+    fCount = uniqueCount;
+    fKeySize = keySize;
+}
+
+SkDataSet::~SkDataSet() {
+    for (int i = 0; i < fCount; ++i) {
+        fPairs[i].fValue->unref();
+    }
+    sk_free(fPairs);    // this also frees the key storage
+}
+
+SkData* SkDataSet::find(const char key[]) const {
+    return findValue(key, fPairs, fCount);
+}
+
+void SkDataSet::writeToStream(SkWStream* stream) const {
+    stream->write32(fCount);
+    if (fCount > 0) {
+        stream->write32(fKeySize);
+        // our first key points to all the key storage
+        stream->write(fPairs[0].fKey, fKeySize);
+        for (int i = 0; i < fCount; ++i) {
+            stream->writeData(fPairs[i].fValue);
+        }
+    }
+}
+
+void SkDataSet::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeInt(fCount);
+    if (fCount > 0) {
+        buffer.writeByteArray(fPairs[0].fKey, fKeySize);
+        for (int i = 0; i < fCount; ++i) {
+            buffer.writeFlattenable(fPairs[i].fValue);
+        }
+    }
+}
+
+SkDataSet::SkDataSet(SkStream* stream) {
+    fCount = stream->readU32();
+    if (fCount > 0) {
+        fKeySize = stream->readU32();
+        fPairs = allocatePairStorage(fCount, fKeySize);
+        char* keyStorage = (char*)(fPairs + fCount);
+
+        stream->read(keyStorage, fKeySize);
+
+        for (int i = 0; i < fCount; ++i) {
+            fPairs[i].fKey = keyStorage;
+            keyStorage += strlen(keyStorage) + 1;
+            fPairs[i].fValue = stream->readData();
+        }
+    } else {
+        fKeySize = 0;
+        fPairs = NULL;
+    }
+}
+
+SkDataSet::SkDataSet(SkFlattenableReadBuffer& buffer) {
+    fCount = buffer.readInt();
+    if (fCount > 0) {
+        fKeySize = buffer.getArrayCount();
+        fPairs = allocatePairStorage(fCount, fKeySize);
+        char* keyStorage = (char*)(fPairs + fCount);
+
+        buffer.readByteArray(keyStorage);
+
+        for (int i = 0; i < fCount; ++i) {
+            fPairs[i].fKey = keyStorage;
+            keyStorage += strlen(keyStorage) + 1;
+            fPairs[i].fValue = buffer.readFlattenableT<SkData>();
+        }
+    } else {
+        fKeySize = 0;
+        fPairs = NULL;
+    }
+}
+
+SkDataSet* SkDataSet::NewEmpty() {
+    static SkDataSet* gEmptySet;
+    if (NULL == gEmptySet) {
+        gEmptySet = SkNEW_ARGS(SkDataSet, (NULL, 0));
+    }
+    gEmptySet->ref();
+    return gEmptySet;
+}
diff --git a/src/core/SkDebug.cpp b/src/core/SkDebug.cpp
index 4b33836..a10d443 100644
--- a/src/core/SkDebug.cpp
+++ b/src/core/SkDebug.cpp
@@ -48,4 +48,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkDeque.cpp b/src/core/SkDeque.cpp
index 385b48d..d210dcf 100644
--- a/src/core/SkDeque.cpp
+++ b/src/core/SkDeque.cpp
@@ -9,11 +9,9 @@
 
 #include "SkDeque.h"
 
-#define INIT_ELEM_COUNT 1  // should we let the caller set this?
-
-struct SkDeque::Head {
-    Head*   fNext;
-    Head*   fPrev;
+struct SkDeque::Block {
+    Block*  fNext;
+    Block*  fPrev;
     char*   fBegin; // start of used section in this chunk
     char*   fEnd;   // end of used section in this chunk
     char*   fStop;  // end of the allocated chunk
@@ -28,80 +26,56 @@
     }
 };
 
-SkDeque::SkDeque(size_t elemSize)
-        : fElemSize(elemSize), fInitialStorage(NULL), fCount(0) {
+SkDeque::SkDeque(size_t elemSize, int allocCount)
+        : fElemSize(elemSize)
+        , fInitialStorage(NULL)
+        , fCount(0)
+        , fAllocCount(allocCount) {
+    SkASSERT(allocCount >= 1);
+    fFrontBlock = fBackBlock = NULL;
     fFront = fBack = NULL;
 }
 
-SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize)
-        : fElemSize(elemSize), fInitialStorage(storage), fCount(0) {
+SkDeque::SkDeque(size_t elemSize, void* storage, size_t storageSize, int allocCount)
+        : fElemSize(elemSize)
+        , fInitialStorage(storage)
+        , fCount(0)
+        , fAllocCount(allocCount) {
     SkASSERT(storageSize == 0 || storage != NULL);
+    SkASSERT(allocCount >= 1);
 
-    if (storageSize >= sizeof(Head) + elemSize) {
-        fFront = (Head*)storage;
-        fFront->init(storageSize);
+    if (storageSize >= sizeof(Block) + elemSize) {
+        fFrontBlock = (Block*)storage;
+        fFrontBlock->init(storageSize);
     } else {
-        fFront = NULL;
+        fFrontBlock = NULL;
     }
-    fBack = fFront;
+    fBackBlock = fFrontBlock;
+    fFront = fBack = NULL;
 }
 
 SkDeque::~SkDeque() {
-    Head* head = fFront;
-    Head* initialHead = (Head*)fInitialStorage;
+    Block* head = fFrontBlock;
+    Block* initialHead = (Block*)fInitialStorage;
 
     while (head) {
-        Head* next = head->fNext;
+        Block* next = head->fNext;
         if (head != initialHead) {
-            sk_free(head);
+            this->freeBlock(head);
         }
         head = next;
     }
 }
 
-const void* SkDeque::front() const {
-    Head* front = fFront;
-
-    if (NULL == front) {
-        return NULL;
-    }
-    if (NULL == front->fBegin) {
-        front = front->fNext;
-        if (NULL == front) {
-            return NULL;
-        }
-    }
-    SkASSERT(front->fBegin);
-    return front->fBegin;
-}
-
-const void* SkDeque::back() const {
-    Head* back = fBack;
-
-    if (NULL == back) {
-        return NULL;
-    }
-    if (NULL == back->fEnd) {  // marked as deleted
-        back = back->fPrev;
-        if (NULL == back) {
-            return NULL;
-        }
-    }
-    SkASSERT(back->fEnd);
-    return back->fEnd - fElemSize;
-}
-
 void* SkDeque::push_front() {
     fCount += 1;
 
-    if (NULL == fFront) {
-        fFront = (Head*)sk_malloc_throw(sizeof(Head) +
-                                        INIT_ELEM_COUNT * fElemSize);
-        fFront->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize);
-        fBack = fFront;     // update our linklist
+    if (NULL == fFrontBlock) {
+        fFrontBlock = this->allocateBlock(fAllocCount);
+        fBackBlock = fFrontBlock;     // update our linklist
     }
 
-    Head*   first = fFront;
+    Block*  first = fFrontBlock;
     char*   begin;
 
     if (NULL == first->fBegin) {
@@ -112,32 +86,36 @@
         begin = first->fBegin - fElemSize;
         if (begin < first->start()) {    // no more room in this chunk
             // should we alloc more as we accumulate more elements?
-            size_t  size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize;
-
-            first = (Head*)sk_malloc_throw(size);
-            first->init(size);
-            first->fNext = fFront;
-            fFront->fPrev = first;
-            fFront = first;
+            first = this->allocateBlock(fAllocCount);
+            first->fNext = fFrontBlock;
+            fFrontBlock->fPrev = first;
+            fFrontBlock = first;
             goto INIT_CHUNK;
         }
     }
 
     first->fBegin = begin;
+
+    if (NULL == fFront) {
+        SkASSERT(NULL == fBack);
+        fFront = fBack = begin;
+    } else {
+        SkASSERT(NULL != fBack);
+        fFront = begin;
+    }
+
     return begin;
 }
 
 void* SkDeque::push_back() {
     fCount += 1;
 
-    if (NULL == fBack) {
-        fBack = (Head*)sk_malloc_throw(sizeof(Head) +
-                                       INIT_ELEM_COUNT * fElemSize);
-        fBack->init(sizeof(Head) + INIT_ELEM_COUNT * fElemSize);
-        fFront = fBack; // update our linklist
+    if (NULL == fBackBlock) {
+        fBackBlock = this->allocateBlock(fAllocCount);
+        fFrontBlock = fBackBlock; // update our linklist
     }
 
-    Head*   last = fBack;
+    Block*  last = fBackBlock;
     char*   end;
 
     if (NULL == last->fBegin) {
@@ -148,44 +126,59 @@
         end = last->fEnd + fElemSize;
         if (end > last->fStop) {  // no more room in this chunk
             // should we alloc more as we accumulate more elements?
-            size_t  size = sizeof(Head) + INIT_ELEM_COUNT * fElemSize;
-
-            last = (Head*)sk_malloc_throw(size);
-            last->init(size);
-            last->fPrev = fBack;
-            fBack->fNext = last;
-            fBack = last;
+            last = this->allocateBlock(fAllocCount);
+            last->fPrev = fBackBlock;
+            fBackBlock->fNext = last;
+            fBackBlock = last;
             goto INIT_CHUNK;
         }
     }
 
     last->fEnd = end;
-    return end - fElemSize;
+    end -= fElemSize;
+
+    if (NULL == fBack) {
+        SkASSERT(NULL == fFront);
+        fFront = fBack = end;
+    } else {
+        SkASSERT(NULL != fFront);
+        fBack = end;
+    }
+
+    return end;
 }
 
 void SkDeque::pop_front() {
     SkASSERT(fCount > 0);
     fCount -= 1;
 
-    Head*   first = fFront;
+    Block*  first = fFrontBlock;
 
     SkASSERT(first != NULL);
 
     if (first->fBegin == NULL) {  // we were marked empty from before
         first = first->fNext;
         first->fPrev = NULL;
-        sk_free(fFront);
-        fFront = first;
+        this->freeBlock(fFrontBlock);
+        fFrontBlock = first;
         SkASSERT(first != NULL);    // else we popped too far
     }
 
     char* begin = first->fBegin + fElemSize;
     SkASSERT(begin <= first->fEnd);
 
-    if (begin < fFront->fEnd) {
+    if (begin < fFrontBlock->fEnd) {
         first->fBegin = begin;
+        SkASSERT(NULL != first->fBegin);
+        fFront = first->fBegin;
     } else {
         first->fBegin = first->fEnd = NULL;  // mark as empty
+        if (NULL == first->fNext) {
+            fFront = fBack = NULL;
+        } else {
+            SkASSERT(NULL != first->fNext->fBegin);
+            fFront = first->fNext->fBegin;
+        }
     }
 }
 
@@ -193,15 +186,15 @@
     SkASSERT(fCount > 0);
     fCount -= 1;
 
-    Head* last = fBack;
+    Block* last = fBackBlock;
 
     SkASSERT(last != NULL);
 
     if (last->fEnd == NULL) {  // we were marked empty from before
         last = last->fPrev;
         last->fNext = NULL;
-        sk_free(fBack);
-        fBack = last;
+        this->freeBlock(fBackBlock);
+        fBackBlock = last;
         SkASSERT(last != NULL);  // else we popped too far
     }
 
@@ -210,41 +203,106 @@
 
     if (end > last->fBegin) {
         last->fEnd = end;
+        SkASSERT(NULL != last->fEnd);
+        fBack = last->fEnd - fElemSize;
     } else {
         last->fBegin = last->fEnd = NULL;    // mark as empty
+        if (NULL == last->fPrev) {
+            fFront = fBack = NULL;
+        } else {
+            SkASSERT(NULL != last->fPrev->fEnd);
+            fBack = last->fPrev->fEnd - fElemSize;
+        }
     }
 }
 
+int SkDeque::numBlocksAllocated() const {
+    int numBlocks = 0;
+
+    for (const Block* temp = fFrontBlock; temp; temp = temp->fNext) {
+        ++numBlocks;
+    }
+
+    return numBlocks;
+}
+
+SkDeque::Block* SkDeque::allocateBlock(int allocCount) {
+    Block* newBlock = (Block*)sk_malloc_throw(sizeof(Block) + allocCount * fElemSize);
+    newBlock->init(sizeof(Block) + allocCount * fElemSize);
+    return newBlock;
+}
+
+void SkDeque::freeBlock(Block* block) {
+    sk_free(block);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
-SkDeque::F2BIter::F2BIter() : fHead(NULL), fPos(NULL), fElemSize(0) {}
+SkDeque::Iter::Iter() : fCurBlock(NULL), fPos(NULL), fElemSize(0) {}
 
-SkDeque::F2BIter::F2BIter(const SkDeque& d) {
-    this->reset(d);
+SkDeque::Iter::Iter(const SkDeque& d, IterStart startLoc) {
+    this->reset(d, startLoc);
 }
 
-void* SkDeque::F2BIter::next() {
+// Due to how reset and next work, next actually returns the current element
+// pointed to by fPos and then updates fPos to point to the next one.
+void* SkDeque::Iter::next() {
     char* pos = fPos;
 
     if (pos) {   // if we were valid, try to move to the next setting
         char* next = pos + fElemSize;
-        SkASSERT(next <= fHead->fEnd);
-        if (next == fHead->fEnd) { // exhausted this chunk, move to next
+        SkASSERT(next <= fCurBlock->fEnd);
+        if (next == fCurBlock->fEnd) { // exhausted this chunk, move to next
             do {
-                fHead = fHead->fNext;
-            } while (fHead != NULL && fHead->fBegin == NULL);
-            next = fHead ? fHead->fBegin : NULL;
+                fCurBlock = fCurBlock->fNext;
+            } while (fCurBlock != NULL && fCurBlock->fBegin == NULL);
+            next = fCurBlock ? fCurBlock->fBegin : NULL;
         }
         fPos = next;
     }
     return pos;
 }
 
-void SkDeque::F2BIter::reset(const SkDeque& d) {
-    fElemSize = d.fElemSize;
-    fHead = d.fFront;
-    while (fHead != NULL && fHead->fBegin == NULL) {
-        fHead = fHead->fNext;
+// Like next, prev actually returns the current element pointed to by fPos and
+// then makes fPos point to the previous element.
+void* SkDeque::Iter::prev() {
+    char* pos = fPos;
+
+    if (pos) {   // if we were valid, try to move to the prior setting
+        char* prev = pos - fElemSize;
+        SkASSERT(prev >= fCurBlock->fBegin - fElemSize);
+        if (prev < fCurBlock->fBegin) { // exhausted this chunk, move to prior
+            do {
+                fCurBlock = fCurBlock->fPrev;
+            } while (fCurBlock != NULL && fCurBlock->fEnd == NULL);
+            prev = fCurBlock ? fCurBlock->fEnd - fElemSize : NULL;
+        }
+        fPos = prev;
     }
-    fPos = fHead ? fHead->fBegin : NULL;
+    return pos;
+}
+
+// reset works by skipping through the spare blocks at the start (or end)
+// of the doubly linked list until a non-empty one is found. The fPos
+// member is then set to the first (or last) element in the block. If
+// there are no elements in the deque both fCurBlock and fPos will come
+// out of this routine NULL.
+void SkDeque::Iter::reset(const SkDeque& d, IterStart startLoc) {
+    fElemSize = d.fElemSize;
+
+    if (kFront_IterStart == startLoc) {
+        // initialize the iterator to start at the front
+        fCurBlock = d.fFrontBlock;
+        while (NULL != fCurBlock && NULL == fCurBlock->fBegin) {
+            fCurBlock = fCurBlock->fNext;
+        }
+        fPos = fCurBlock ? fCurBlock->fBegin : NULL;
+    } else {
+        // initialize the iterator to start at the back
+        fCurBlock = d.fBackBlock;
+        while (NULL != fCurBlock && NULL == fCurBlock->fEnd) {
+            fCurBlock = fCurBlock->fPrev;
+        }
+        fPos = fCurBlock ? fCurBlock->fEnd - fElemSize : NULL;
+    }
 }
diff --git a/src/core/SkDescriptor.h b/src/core/SkDescriptor.h
new file mode 100644
index 0000000..79b086f
--- /dev/null
+++ b/src/core/SkDescriptor.h
@@ -0,0 +1,164 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkDescriptor_DEFINED
+#define SkDescriptor_DEFINED
+
+#include "SkChecksum.h"
+#include "SkTypes.h"
+
+class SkDescriptor : SkNoncopyable {
+public:
+    static size_t ComputeOverhead(int entryCount) {
+        SkASSERT(entryCount >= 0);
+        return sizeof(SkDescriptor) + entryCount * sizeof(Entry);
+    }
+
+    static SkDescriptor* Alloc(size_t length) {
+        SkASSERT(SkAlign4(length) == length);
+        SkDescriptor* desc = (SkDescriptor*)sk_malloc_throw(length);
+        return desc;
+    }
+
+    static void Free(SkDescriptor* desc) {
+        sk_free(desc);
+    }
+
+    void init() {
+        fLength = sizeof(SkDescriptor);
+        fCount  = 0;
+    }
+
+    uint32_t getLength() const { return fLength; }
+
+    void* addEntry(uint32_t tag, uint32_t length, const void* data = NULL) {
+        SkASSERT(tag);
+        SkASSERT(SkAlign4(length) == length);
+        SkASSERT(this->findEntry(tag, NULL) == NULL);
+
+        Entry*  entry = (Entry*)((char*)this + fLength);
+        entry->fTag = tag;
+        entry->fLen = length;
+        if (data) {
+            memcpy(entry + 1, data, length);
+        }
+
+        fCount += 1;
+        fLength += sizeof(Entry) + length;
+        return (entry + 1); // return its data
+    }
+
+    void computeChecksum() {
+        fChecksum = SkDescriptor::ComputeChecksum(this);
+    }
+
+#ifdef SK_DEBUG
+    void assertChecksum() const {
+        SkASSERT(SkDescriptor::ComputeChecksum(this) == fChecksum);
+    }
+#endif
+
+    const void* findEntry(uint32_t tag, uint32_t* length) const {
+        const Entry* entry = (const Entry*)(this + 1);
+        int          count = fCount;
+
+        while (--count >= 0) {
+            if (entry->fTag == tag) {
+                if (length) {
+                    *length = entry->fLen;
+                }
+                return entry + 1;
+            }
+            entry = (const Entry*)((const char*)(entry + 1) + entry->fLen);
+        }
+        return NULL;
+    }
+
+    SkDescriptor* copy() const {
+        SkDescriptor* desc = SkDescriptor::Alloc(fLength);
+        memcpy(desc, this, fLength);
+        return desc;
+    }
+
+    bool equals(const SkDescriptor& other) const {
+        // probe to see if we have a good checksum algo
+//        SkASSERT(a.fChecksum != b.fChecksum || memcmp(&a, &b, a.fLength) == 0);
+
+        // the first value we should look at is the checksum, so this loop
+        // should terminate early if they descriptors are different.
+        // NOTE: if we wrote a sentinel value at the end of each, we chould
+        //       remove the aa < stop test in the loop...
+        const uint32_t* aa = (const uint32_t*)this;
+        const uint32_t* bb = (const uint32_t*)&other;
+        const uint32_t* stop = (const uint32_t*)((const char*)aa + fLength);
+        do {
+            if (*aa++ != *bb++)
+                return false;
+        } while (aa < stop);
+        return true;
+    }
+
+    uint32_t getChecksum() const { return fChecksum; }
+
+    struct Entry {
+        uint32_t fTag;
+        uint32_t fLen;
+    };
+
+#ifdef SK_DEBUG
+    uint32_t getCount() const { return fCount; }
+#endif
+
+private:
+    uint32_t fChecksum;  // must be first
+    uint32_t fLength;    // must be second
+    uint32_t fCount;
+
+    static uint32_t ComputeChecksum(const SkDescriptor* desc) {
+        const uint32_t* ptr = (const uint32_t*)desc + 1; // skip the checksum field
+        size_t len = desc->fLength - sizeof(uint32_t);
+        return SkChecksum::Compute(ptr, len);
+    }
+
+    // private so no one can create one except our factories
+    SkDescriptor() {}
+};
+
+#include "SkScalerContext.h"
+
+class SkAutoDescriptor : SkNoncopyable {
+public:
+    SkAutoDescriptor(size_t size) {
+        if (size <= sizeof(fStorage)) {
+            fDesc = (SkDescriptor*)(void*)fStorage;
+        } else {
+            fDesc = SkDescriptor::Alloc(size);
+        }
+    }
+
+    ~SkAutoDescriptor() {
+        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
+                        + sizeof(SkDescriptor::Entry) + sizeof(void*)                   // for typeface
+                        + 32   // slop for occational small extras
+    };
+    SkDescriptor*   fDesc;
+    uint32_t        fStorage[(kStorageSize + 3) >> 2];
+};
+
+
+#endif
diff --git a/src/core/SkDevice.cpp b/src/core/SkDevice.cpp
index f1da2ef..9aa652d 100644
--- a/src/core/SkDevice.cpp
+++ b/src/core/SkDevice.cpp
@@ -6,19 +6,49 @@
  * found in the LICENSE file.
  */
 #include "SkDevice.h"
+#include "SkDeviceProperties.h"
 #include "SkDraw.h"
 #include "SkImageFilter.h"
 #include "SkMetaData.h"
+#include "SkRasterClip.h"
 #include "SkRect.h"
+#include "SkShader.h"
+
+SK_DEFINE_INST_COUNT(SkDevice)
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkDevice::SkDevice(const SkBitmap& bitmap) : fBitmap(bitmap) {
+#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
+    do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDevice::SkDevice(const SkBitmap& bitmap)
+    : fBitmap(bitmap), fLeakyProperties(SkDeviceProperties::MakeDefault())
+#ifdef SK_DEBUG
+    , fAttachedToCanvas(false)
+#endif
+{
     fOrigin.setZero();
     fMetaData = NULL;
 }
 
-SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque) {
+SkDevice::SkDevice(const SkBitmap& bitmap, const SkDeviceProperties& deviceProperties)
+    : fBitmap(bitmap), fLeakyProperties(deviceProperties)
+#ifdef SK_DEBUG
+    , fAttachedToCanvas(false)
+#endif
+{
+    fOrigin.setZero();
+    fMetaData = NULL;
+}
+
+SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque)
+    : fLeakyProperties(SkDeviceProperties::MakeDefault())
+#ifdef SK_DEBUG
+    , fAttachedToCanvas(false)
+#endif
+{
     fOrigin.setZero();
     fMetaData = NULL;
 
@@ -26,7 +56,25 @@
     fBitmap.allocPixels();
     fBitmap.setIsOpaque(isOpaque);
     if (!isOpaque) {
-        fBitmap.eraseColor(0);
+        fBitmap.eraseColor(SK_ColorTRANSPARENT);
+    }
+}
+
+SkDevice::SkDevice(SkBitmap::Config config, int width, int height, bool isOpaque,
+                   const SkDeviceProperties& deviceProperties)
+    : fLeakyProperties(deviceProperties)
+#ifdef SK_DEBUG
+    , fAttachedToCanvas(false)
+#endif
+{
+    fOrigin.setZero();
+    fMetaData = NULL;
+
+    fBitmap.setConfig(config, width, height);
+    fBitmap.allocPixels();
+    fBitmap.setIsOpaque(isOpaque);
+    if (!isOpaque) {
+        fBitmap.eraseColor(SK_ColorTRANSPARENT);
     }
 }
 
@@ -34,6 +82,13 @@
     delete fMetaData;
 }
 
+void SkDevice::replaceBitmapBackendForRasterSurface(const SkBitmap& bm) {
+    SkASSERT(bm.width() == fBitmap.width());
+    SkASSERT(bm.height() == fBitmap.height());
+    fBitmap = bm;   // intent is to use bm's pixelRef (and rowbytes/config)
+    fBitmap.lockPixels();
+}
+
 SkDevice* SkDevice::createCompatibleDevice(SkBitmap::Config config,
                                            int width, int height,
                                            bool isOpaque) {
@@ -52,7 +107,7 @@
                                              int width, int height,
                                              bool isOpaque,
                                              Usage usage) {
-    return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque));
+    return SkNEW_ARGS(SkDevice,(config, width, height, isOpaque, fLeakyProperties));
 }
 
 SkMetaData& SkDevice::getMetaData() {
@@ -101,9 +156,13 @@
                              const SkClipStack& clipStack) {
 }
 
-bool SkDevice::filterImage(SkImageFilter*, const SkBitmap& src,
-                           const SkMatrix& ctm,
-                           SkBitmap* result, SkIPoint* offset) {
+bool SkDevice::canHandleImageFilter(SkImageFilter*) {
+    return false;
+}
+
+bool SkDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
+                           const SkMatrix& ctm, SkBitmap* result,
+                           SkIPoint* offset) {
     return false;
 }
 
@@ -272,8 +331,13 @@
 
     SkPaint paint;
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    SkCanvas canvas(this);
-    canvas.drawSprite(*sprite, x, y, &paint);
+    SkRasterClip clip(SkIRect::MakeWH(fBitmap.width(), fBitmap.height()));
+    SkDraw  draw;
+    draw.fRC = &clip;
+    draw.fClip = &clip.bwRgn();
+    draw.fBitmap = &fBitmap; // canvas should have already called accessBitmap
+    draw.fMatrix = &SkMatrix::I();
+    this->drawSprite(draw, *sprite, x, y, paint);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -283,18 +347,28 @@
 }
 
 void SkDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode, size_t count,
-                              const SkPoint pts[], const SkPaint& paint) {
+                          const SkPoint pts[], const SkPaint& paint) {
     draw.drawPoints(mode, count, pts, paint);
 }
 
-void SkDevice::drawRect(const SkDraw& draw, const SkRect& r,
-                            const SkPaint& paint) {
+void SkDevice::drawRect(const SkDraw& draw, const SkRect& r, const SkPaint& paint) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
     draw.drawRect(r, paint);
 }
 
+void SkDevice::drawOval(const SkDraw& draw, const SkRect& oval, const SkPaint& paint) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+
+    SkPath path;
+    path.addOval(oval);
+    // call the non-virtual version
+    this->SkDevice::drawPath(draw, path, paint, NULL, true);
+}
+
 void SkDevice::drawPath(const SkDraw& draw, const SkPath& path,
                         const SkPaint& paint, const SkMatrix* prePathMatrix,
                         bool pathIsMutable) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
     draw.drawPath(path, paint, prePathMatrix, pathIsMutable);
 }
 
@@ -313,6 +387,92 @@
     draw.drawBitmap(*bitmapPtr, matrix, paint);
 }
 
+void SkDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                              const SkRect* src, const SkRect& dst,
+                              const SkPaint& paint) {
+    SkMatrix    matrix;
+    SkRect      bitmapBounds, tmpSrc, tmpDst;
+    SkBitmap    tmpBitmap;
+
+    bitmapBounds.isetWH(bitmap.width(), bitmap.height());
+
+    // Compute matrix from the two rectangles
+    if (src) {
+        tmpSrc = *src;
+    } else {
+        tmpSrc = bitmapBounds;
+    }
+    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+
+    const SkRect* dstPtr = &dst;
+    const SkBitmap* bitmapPtr = &bitmap;
+
+    // clip the tmpSrc to the bounds of the bitmap, and recompute dstRect if
+    // needed (if the src was clipped). No check needed if src==null.
+    if (src) {
+        if (!bitmapBounds.contains(*src)) {
+            if (!tmpSrc.intersect(bitmapBounds)) {
+                return; // nothing to draw
+            }
+            // recompute dst, based on the smaller tmpSrc
+            matrix.mapRect(&tmpDst, tmpSrc);
+            dstPtr = &tmpDst;
+        }
+
+        // since we may need to clamp to the borders of the src rect within
+        // the bitmap, we extract a subset.
+        SkIRect srcIR;
+        tmpSrc.roundOut(&srcIR);
+        if (!bitmap.extractSubset(&tmpBitmap, srcIR)) {
+            return;
+        }
+        bitmapPtr = &tmpBitmap;
+
+        // Since we did an extract, we need to adjust the matrix accordingly
+        SkScalar dx = 0, dy = 0;
+        if (srcIR.fLeft > 0) {
+            dx = SkIntToScalar(srcIR.fLeft);
+        }
+        if (srcIR.fTop > 0) {
+            dy = SkIntToScalar(srcIR.fTop);
+        }
+        if (dx || dy) {
+            matrix.preTranslate(dx, dy);
+        }
+
+        SkRect extractedBitmapBounds;
+        extractedBitmapBounds.isetWH(bitmapPtr->width(), bitmapPtr->height());
+        if (extractedBitmapBounds == tmpSrc) {
+            // no fractional part in src, we can just call drawBitmap
+            goto USE_DRAWBITMAP;
+        }
+    } else {
+        USE_DRAWBITMAP:
+        // We can go faster by just calling drawBitmap, which will concat the
+        // matrix with the CTM, and try to call drawSprite if it can. If not,
+        // it will make a shader and call drawRect, as we do below.
+        this->drawBitmap(draw, *bitmapPtr, NULL, matrix, paint);
+        return;
+    }
+
+    // construct a shader, so we can call drawRect with the dst
+    SkShader* s = SkShader::CreateBitmapShader(*bitmapPtr,
+                                               SkShader::kClamp_TileMode,
+                                               SkShader::kClamp_TileMode);
+    if (NULL == s) {
+        return;
+    }
+    s->setLocalMatrix(matrix);
+
+    SkPaint paintWithShader(paint);
+    paintWithShader.setStyle(SkPaint::kFill_Style);
+    paintWithShader.setShader(s)->unref();
+
+    // Call ourself, in case the subclass wanted to share this setup code
+    // but handle the drawRect code themselves.
+    this->drawRect(draw, *dstPtr, paintWithShader);
+}
+
 void SkDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
                               int x, int y, const SkPaint& paint) {
     draw.drawSprite(bitmap, x, y, paint);
@@ -382,4 +542,3 @@
     // we're cool with the paint as is
     return false;
 }
-
diff --git a/src/core/SkDeviceImageFilterProxy.h b/src/core/SkDeviceImageFilterProxy.h
new file mode 100644
index 0000000..98a120c
--- /dev/null
+++ b/src/core/SkDeviceImageFilterProxy.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 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 SkDeviceImageFilterProxy_DEFINED
+#define SkDeviceImageFilterProxy_DEFINED
+
+#include "SkImageFilter.h"
+
+class SkDeviceImageFilterProxy : public SkImageFilter::Proxy {
+public:
+    SkDeviceImageFilterProxy(SkDevice* device) : fDevice(device) {}
+
+    virtual SkDevice* createDevice(int w, int h) SK_OVERRIDE {
+        return fDevice->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
+                                               w, h, false);
+    }
+    virtual bool canHandleImageFilter(SkImageFilter* filter) SK_OVERRIDE {
+        return fDevice->canHandleImageFilter(filter);
+    }
+    virtual bool filterImage(SkImageFilter* filter, const SkBitmap& src,
+                             const SkMatrix& ctm,
+                             SkBitmap* result, SkIPoint* offset) SK_OVERRIDE {
+        return fDevice->filterImage(filter, src, ctm, result, offset);
+    }
+
+private:
+    SkDevice* fDevice;
+};
+
+#endif
diff --git a/src/core/SkDeviceProfile.cpp b/src/core/SkDeviceProfile.cpp
index 2c2cb88..ce3e566 100644
--- a/src/core/SkDeviceProfile.cpp
+++ b/src/core/SkDeviceProfile.cpp
@@ -1,9 +1,17 @@
+/*
+ * 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 "SkDeviceProfile.h"
 
-#define DEFAULT_GAMMAEXP        2.2
-#define DEFAULT_CONTRASTSCALE   0.5
+SK_DEFINE_INST_COUNT(SkDeviceProfile)
+
+#define DEFAULT_GAMMAEXP        2.2f
+#define DEFAULT_CONTRASTSCALE   0.5f
 #define DEFAULT_LCDCONFIG       SkDeviceProfile::kNone_LCDConfig
 #define DEFAULT_FONTHINTLEVEL   SkDeviceProfile::kSlight_FontHintLevel
 
@@ -62,11 +70,9 @@
     gGlobalProfile->ref();
     return gGlobalProfile;
 }
-    
+
 void SkDeviceProfile::SetGlobal(SkDeviceProfile* profile) {
     SkAutoMutexAcquire amc(gMutex);
 
     SkRefCnt_SafeAssign(gGlobalProfile, profile);
 }
-
-
diff --git a/src/core/SkDeviceProfile.h b/src/core/SkDeviceProfile.h
new file mode 100644
index 0000000..6ef8dfb
--- /dev/null
+++ b/src/core/SkDeviceProfile.h
@@ -0,0 +1,98 @@
+/*
+ * 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 SkDeviceProfile_DEFINED
+#define SkDeviceProfile_DEFINED
+
+#include "SkRefCnt.h"
+
+class SkDeviceProfile : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkDeviceProfile)
+
+    enum LCDConfig {
+        kNone_LCDConfig,   // disables LCD text rendering, uses A8 instead
+        kRGB_Horizontal_LCDConfig,
+        kBGR_Horizontal_LCDConfig,
+        kRGB_Vertical_LCDConfig,
+        kBGR_Vertical_LCDConfig
+    };
+
+    enum FontHintLevel {
+        kNone_FontHintLevel,
+        kSlight_FontHintLevel,
+        kNormal_FontHintLevel,
+        kFull_FontHintLevel,
+        kAuto_FontHintLevel
+    };
+
+    /**
+     *  gammaExp is typically between 1.0 and 2.2. For no gamma adjustment,
+     *  specify 1.0
+     *
+     *  contrastScale will be pinned between 0.0 and 1.0. For no contrast
+     *  adjustment, specify 0.0
+     *
+     *  @param config   Describes the LCD layout for this device. If this is set
+     *                  to kNone, then all requests for LCD text will be
+     *                  devolved to A8 antialiasing.
+     *
+     *  @param level    The hinting level to be used, IF the paint specifies
+     *                  "default". Otherwise the paint's hinting level will be
+     *                  respected.
+     */
+    static SkDeviceProfile* Create(float gammaExp,
+                                   float contrastScale,
+                                   LCDConfig,
+                                   FontHintLevel);
+
+    /**
+     *  Returns the global default profile, that is used if no global profile is
+     *  specified with SetGlobal(), or if NULL is specified to SetGlobal().
+     *  The references count is *not* incremented, and the caller should not
+     *  call unref().
+     */
+    static SkDeviceProfile* GetDefault();
+
+    /**
+     *  Return the current global profile (or the default if no global had yet
+     *  been set) and increment its reference count. The call *must* call unref()
+     *  when it is done using it.
+     */
+    static SkDeviceProfile* RefGlobal();
+
+    /**
+     *  Make the specified profile be the global value for all subsequently
+     *  instantiated devices. Does not affect any existing devices.
+     *  Increments the reference count on the profile.
+     *  Specify NULL for the "identity" profile (where there is no gamma or
+     *  contrast correction).
+     */
+    static void SetGlobal(SkDeviceProfile*);
+
+    float getFontGammaExponent() const { return fGammaExponent; }
+    float getFontContrastScale() const { return fContrastScale; }
+
+    /**
+     *  Given a luminance byte (0 for black, 0xFF for white), generate a table
+     *  that applies the gamma/contrast settings to linear coverage values.
+     */
+    void generateTableForLuminanceByte(U8CPU lumByte, uint8_t table[256]) const;
+
+private:
+    SkDeviceProfile(float gammaExp, float contrastScale, LCDConfig,
+                    FontHintLevel);
+
+    float           fGammaExponent;
+    float           fContrastScale;
+    LCDConfig       fLCDConfig;
+    FontHintLevel   fFontHintLevel;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/core/SkDither.cpp b/src/core/SkDither.cpp
index bdd8c2f..546d479 100644
--- a/src/core/SkDither.cpp
+++ b/src/core/SkDither.cpp
@@ -8,7 +8,7 @@
 #include "SkDither.h"
 
 /*  The base dither matrix we use to derive optimized ones for 565 and 4444
- 
+
     { 0,  32, 8,  40, 2,  34, 10, 42 },
     { 48, 16, 56, 24, 50, 18, 58, 26 },
     { 12, 44, 4,  36, 14, 46, 6,  38 },
@@ -53,4 +53,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkDraw.cpp b/src/core/SkDraw.cpp
index 23a6d59..4b06b88 100644
--- a/src/core/SkDraw.cpp
+++ b/src/core/SkDraw.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -21,6 +20,7 @@
 #include "SkRasterizer.h"
 #include "SkScan.h"
 #include "SkShader.h"
+#include "SkString.h"
 #include "SkStroke.h"
 #include "SkTemplatesPriv.h"
 #include "SkTLazy.h"
@@ -29,6 +29,7 @@
 #include "SkAutoKern.h"
 #include "SkBitmapProcShader.h"
 #include "SkDrawProcs.h"
+#include "SkMatrixUtils.h"
 
 //#define TRACE_BITMAP_DRAWS
 
@@ -46,7 +47,7 @@
         fBlitter = SkBlitter::Choose(device, matrix, paint,
                                      fStorage, sizeof(fStorage));
     }
-    
+
     ~SkAutoBlitterChoose();
 
     SkBlitter*  operator->() { return fBlitter; }
@@ -121,6 +122,23 @@
     memcpy(this, &src, sizeof(*this));
 }
 
+bool SkDraw::computeConservativeLocalClipBounds(SkRect* localBounds) const {
+    if (fRC->isEmpty()) {
+        return false;
+    }
+
+    SkMatrix inverse;
+    if (!fMatrix->invert(&inverse)) {
+        return false;
+    }
+
+    SkIRect devBounds = fRC->getBounds();
+    // outset to have slop for antialasing and hairlines
+    devBounds.outset(1, 1);
+    inverse.mapRect(localBounds, SkRect::Make(devBounds));
+    return true;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 typedef void (*BitmapXferProc)(void* pixels, size_t bytes, uint32_t data);
@@ -318,8 +336,8 @@
     const SkIRect& r = rec.fClip->getBounds();
 
     for (int i = 0; i < count; i++) {
-        int x = SkScalarFloor(devPts[i].fX);
-        int y = SkScalarFloor(devPts[i].fY);
+        int x = SkScalarFloorToInt(devPts[i].fX);
+        int y = SkScalarFloorToInt(devPts[i].fY);
         if (r.contains(x, y)) {
             blitter->blitH(x, y, 1);
         }
@@ -339,15 +357,35 @@
     int rb = bitmap->rowBytes();
 
     for (int i = 0; i < count; i++) {
-        int x = SkScalarFloor(devPts[i].fX);
-        int y = SkScalarFloor(devPts[i].fY);
+        int x = SkScalarFloorToInt(devPts[i].fX);
+        int y = SkScalarFloorToInt(devPts[i].fY);
         if (r.contains(x, y)) {
-//            *bitmap->getAddr16(x, y) = SkToU16(value);
             ((uint16_t*)((char*)addr + y * rb))[x] = SkToU16(value);
         }
     }
 }
 
+static void bw_pt_rect_32_hair_proc(const PtProcRec& rec,
+                                    const SkPoint devPts[], int count,
+                                    SkBlitter* blitter) {
+    SkASSERT(rec.fRC->isRect());
+    const SkIRect& r = rec.fRC->getBounds();
+    uint32_t value;
+    const SkBitmap* bitmap = blitter->justAnOpaqueColor(&value);
+    SkASSERT(bitmap);
+
+    SkPMColor* addr = bitmap->getAddr32(0, 0);
+    int rb = bitmap->rowBytes();
+
+    for (int i = 0; i < count; i++) {
+        int x = SkScalarFloorToInt(devPts[i].fX);
+        int y = SkScalarFloorToInt(devPts[i].fY);
+        if (r.contains(x, y)) {
+            ((SkPMColor*)((char*)addr + y * rb))[x] = value;
+        }
+    }
+}
+
 static void bw_pt_hair_proc(const PtProcRec& rec, const SkPoint devPts[],
                             int count, SkBlitter* blitter) {
     for (int i = 0; i < count; i++) {
@@ -437,7 +475,7 @@
         fPaint = &paint;
         fClip = NULL;
         fRC = rc;
-        fRadius = SK_Fixed1 >> 1;
+        fRadius = SK_FixedHalf;
         return true;
     }
     if (paint.getStrokeCap() != SkPaint::kRound_Cap &&
@@ -479,19 +517,25 @@
     SkASSERT(2 == SkCanvas::kPolygon_PointMode);
     SkASSERT((unsigned)fMode <= (unsigned)SkCanvas::kPolygon_PointMode);
 
-    // first check for hairlines
-    if (0 == fPaint->getStrokeWidth()) {
-        if (fPaint->isAntiAlias()) {
+    if (fPaint->isAntiAlias()) {
+        if (0 == fPaint->getStrokeWidth()) {
             static const Proc gAAProcs[] = {
                 aa_square_proc, aa_line_hair_proc, aa_poly_hair_proc
             };
             proc = gAAProcs[fMode];
-        } else {
+        } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
+            SkASSERT(SkCanvas::kPoints_PointMode == fMode);
+            proc = aa_square_proc;
+        }
+    } else {    // BW
+        if (fRadius <= SK_FixedHalf) {    // small radii and hairline
             if (SkCanvas::kPoints_PointMode == fMode && fClip->isRect()) {
                 uint32_t value;
                 const SkBitmap* bm = blitter->justAnOpaqueColor(&value);
-                if (bm && bm->config() == SkBitmap::kRGB_565_Config) {
+                if (bm && SkBitmap::kRGB_565_Config == bm->config()) {
                     proc = bw_pt_rect_16_hair_proc;
+                } else if (bm && SkBitmap::kARGB_8888_Config == bm->config()) {
+                    proc = bw_pt_rect_32_hair_proc;
                 } else {
                     proc = bw_pt_rect_hair_proc;
                 }
@@ -501,11 +545,6 @@
                 };
                 proc = gBWProcs[fMode];
             }
-        }
-    } else if (fPaint->getStrokeCap() != SkPaint::kRound_Cap) {
-        SkASSERT(SkCanvas::kPoints_PointMode == fMode);
-        if (fPaint->isAntiAlias()) {
-            proc = aa_square_proc;
         } else {
             proc = bw_square_proc;
         }
@@ -556,7 +595,7 @@
         if (!bounder_points(fBounder, mode, count, pts, paint, *fMatrix)) {
             return;
         }
-        
+
         // clear the bounder and call this again, so we don't invoke the bounder
         // later if we happen to call ourselves for drawRect, drawPath, etc.
         SkDraw noBounder(*this);
@@ -564,7 +603,7 @@
         noBounder.drawPoints(mode, count, pts, paint, forceUseDevice);
         return;
     }
-    
+
     PtProcRec rec;
     if (!forceUseDevice && rec.init(mode, paint, fMatrix, fRC)) {
         SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, paint);
@@ -635,6 +674,92 @@
                 break;
             }
             case SkCanvas::kLines_PointMode:
+#ifndef SK_DISABLE_DASHING_OPTIMIZATION
+                if (2 == count && NULL != paint.getPathEffect()) {
+                    // most likely a dashed line - see if it is one of the ones
+                    // we can accelerate
+                    SkStrokeRec rec(paint);
+                    SkPathEffect::PointData pointData;
+
+                    SkPath path;
+                    path.moveTo(pts[0]);
+                    path.lineTo(pts[1]);
+
+                    SkRect cullRect = SkRect::Make(fRC->getBounds());
+
+                    if (paint.getPathEffect()->asPoints(&pointData, path, rec,
+                                                        *fMatrix, &cullRect)) {
+                        // 'asPoints' managed to find some fast path
+
+                        SkPaint newP(paint);
+                        newP.setPathEffect(NULL);
+                        newP.setStyle(SkPaint::kFill_Style);
+
+                        if (!pointData.fFirst.isEmpty()) {
+                            if (fDevice) {
+                                fDevice->drawPath(*this, pointData.fFirst, newP);
+                            } else {
+                                this->drawPath(pointData.fFirst, newP);
+                            }
+                        }
+
+                        if (!pointData.fLast.isEmpty()) {
+                            if (fDevice) {
+                                fDevice->drawPath(*this, pointData.fLast, newP);
+                            } else {
+                                this->drawPath(pointData.fLast, newP);
+                            }
+                        }
+
+                        if (pointData.fSize.fX == pointData.fSize.fY) {
+                            // The rest of the dashed line can just be drawn as points
+                            SkASSERT(pointData.fSize.fX == SkScalarHalf(newP.getStrokeWidth()));
+
+                            if (SkPathEffect::PointData::kCircles_PointFlag & pointData.fFlags) {
+                                newP.setStrokeCap(SkPaint::kRound_Cap);
+                            } else {
+                                newP.setStrokeCap(SkPaint::kButt_Cap);
+                            }
+
+                            if (fDevice) {
+                                fDevice->drawPoints(*this,
+                                                    SkCanvas::kPoints_PointMode,
+                                                    pointData.fNumPoints,
+                                                    pointData.fPoints,
+                                                    newP);
+                            } else {
+                                this->drawPoints(SkCanvas::kPoints_PointMode,
+                                                 pointData.fNumPoints,
+                                                 pointData.fPoints,
+                                                 newP,
+                                                 forceUseDevice);
+                            }
+                            break;
+                        } else {
+                            // The rest of the dashed line must be drawn as rects
+                            SkASSERT(!(SkPathEffect::PointData::kCircles_PointFlag &
+                                      pointData.fFlags));
+
+                            SkRect r;
+
+                            for (int i = 0; i < pointData.fNumPoints; ++i) {
+                                r.set(pointData.fPoints[i].fX - pointData.fSize.fX,
+                                      pointData.fPoints[i].fY - pointData.fSize.fY,
+                                      pointData.fPoints[i].fX + pointData.fSize.fX,
+                                      pointData.fPoints[i].fY + pointData.fSize.fY);
+                                if (fDevice) {
+                                    fDevice->drawRect(*this, r, newP);
+                                } else {
+                                    this->drawRect(r, newP);
+                                }
+                            }
+                        }
+
+                        break;
+                    }
+                }
+#endif // DISABLE_DASHING_OPTIMIZATION
+                // couldn't take fast path so fall through!
             case SkCanvas::kPolygon_PointMode: {
                 count -= 1;
                 SkPath path;
@@ -671,7 +796,7 @@
         paint.getStrokeMiter() < SK_ScalarSqrt2) {
         return false;
     }
-    
+
     SkASSERT(matrix.rectStaysRect());
     SkPoint pt = { paint.getStrokeWidth(), paint.getStrokeWidth() };
     matrix.mapVectors(strokeSize, &pt, 1);
@@ -687,11 +812,11 @@
     const SkScalar width = paint.getStrokeWidth();
     const bool zeroWidth = (0 == width);
     SkPaint::Style style = paint.getStyle();
-    
+
     if ((SkPaint::kStrokeAndFill_Style == style) && zeroWidth) {
         style = SkPaint::kFill_Style;
     }
-    
+
     if (paint.getPathEffect() || paint.getMaskFilter() ||
         paint.getRasterizer() || !matrix.rectStaysRect() ||
         SkPaint::kStrokeAndFill_Style == style) {
@@ -708,9 +833,12 @@
     return rtype;
 }
 
-static SkPoint* rect_points(SkRect& r, int index) {
-    SkASSERT((unsigned)index < 2);
-    return &((SkPoint*)(void*)&r)[index];
+static const SkPoint* rect_points(const SkRect& r) {
+    return (const SkPoint*)(void*)&r;
+}
+
+static SkPoint* rect_points(SkRect& r) {
+    return (SkPoint*)(void*)&r;
 }
 
 void SkDraw::drawRect(const SkRect& rect, const SkPaint& paint) const {
@@ -724,12 +852,6 @@
     SkPoint strokeSize;
     RectType rtype = ComputeRectType(paint, *fMatrix, &strokeSize);
 
-#ifdef SK_DISABLE_FAST_AA_STROKE_RECT
-    if (kStroke_RectType == rtype && paint.isAntiAlias()) {
-        rtype = kPath_RectType;
-    }
-#endif
-
     if (kPath_RectType == rtype) {
         SkPath  tmp;
         tmp.addRect(rect);
@@ -742,18 +864,15 @@
     SkRect          devRect;
 
     // transform rect into devRect
-    {
-        matrix.mapXY(rect.fLeft, rect.fTop, rect_points(devRect, 0));
-        matrix.mapXY(rect.fRight, rect.fBottom, rect_points(devRect, 1));
-        devRect.sort();
-    }
+    matrix.mapPoints(rect_points(devRect), rect_points(rect), 2);
+    devRect.sort();
 
     if (fBounder && !fBounder->doRect(devRect, paint)) {
         return;
     }
 
     // look for the quick exit, before we build a blitter
-    {
+    if (true) {
         SkIRect ir;
         devRect.roundOut(&ir);
         if (paint.getStyle() != SkPaint::kFill_Style) {
@@ -848,7 +967,7 @@
     if (!SkXfermode::AsCoeff(xfer, NULL, &dc)) {
         return false;
     }
-    
+
     switch (dc) {
         case SkXfermode::kOne_Coeff:
         case SkXfermode::kISA_Coeff:
@@ -931,16 +1050,13 @@
     // at this point we're done with prePathMatrix
     SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
 
-    const SkPaint* paint = &origPaint;
-    SkTLazy<SkPaint> lazyPaint;
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
 
     {
         SkScalar coverage;
         if (SkDrawTreatAsHairline(origPaint, *matrix, &coverage)) {
             if (SK_Scalar1 == coverage) {
-                lazyPaint.set(origPaint);
-                lazyPaint.get()->setStrokeWidth(0);
-                paint = lazyPaint.get();
+                paint.writable()->setStrokeWidth(0);
             } else if (xfermodeSupportsCoverageAsAlpha(origPaint.getXfermode())) {
                 U8CPU newAlpha;
 #if 0
@@ -953,16 +1069,20 @@
                 int scale = (int)SkScalarMul(coverage, 256);
                 newAlpha = origPaint.getAlpha() * scale >> 8;
 #endif
-                lazyPaint.set(origPaint);
-                lazyPaint.get()->setStrokeWidth(0);
-                lazyPaint.get()->setAlpha(newAlpha);
-                paint = lazyPaint.get();
+                SkPaint* writablePaint = paint.writable();
+                writablePaint->setStrokeWidth(0);
+                writablePaint->setAlpha(newAlpha);
             }
         }
     }
 
     if (paint->getPathEffect() || paint->getStyle() != SkPaint::kFill_Style) {
-        doFill = paint->getFillPath(*pathPtr, &tmpPath);
+        SkRect cullRect;
+        const SkRect* cullRectPtr = NULL;
+        if (this->computeConservativeLocalClipBounds(&cullRect)) {
+            cullRectPtr = &cullRect;
+        }
+        doFill = paint->getFillPath(*pathPtr, &tmpPath, cullRectPtr);
         pathPtr = &tmpPath;
     }
 
@@ -985,11 +1105,14 @@
 
     SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, *paint);
 
-    // how does filterPath() know to fill or hairline the path??? <mrr>
-    if (paint->getMaskFilter() &&
-            paint->getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fRC,
-                                               fBounder, blitter.get())) {
-        return; // filterPath() called the blitter, so we're done
+    if (paint->getMaskFilter()) {
+        SkPaint::Style style = doFill ? SkPaint::kFill_Style :
+            SkPaint::kStroke_Style;
+        if (paint->getMaskFilter()->filterPath(*devPathPtr, *fMatrix, *fRC,
+                                               fBounder, blitter.get(),
+                                               style)) {
+            return; // filterPath() called the blitter, so we're done
+        }
     }
 
     if (fBounder && !fBounder->doPath(*devPathPtr, *paint, doFill)) {
@@ -1017,6 +1140,7 @@
     go ahead and treat it as if it were, so that subsequent code can go fast.
  */
 static bool just_translate(const SkMatrix& matrix, const SkBitmap& bitmap) {
+#ifdef SK_IGNORE_TRANS_CLAMP_FIX
     SkMatrix::TypeMask mask = matrix.getType();
 
     if (mask & (SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask)) {
@@ -1033,6 +1157,11 @@
     }
     // if we got here, we're either kTranslate_Mask or identity
     return true;
+#else
+    unsigned bits = 0;  // TODO: find a way to allow the caller to tell us to
+                        // respect filtering.
+    return SkTreatAsSprite(matrix, bitmap.width(), bitmap.height(), bits);
+#endif
 }
 
 void SkDraw::drawBitmapAsMask(const SkBitmap& bitmap,
@@ -1043,6 +1172,11 @@
         int ix = SkScalarRound(fMatrix->getTranslateX());
         int iy = SkScalarRound(fMatrix->getTranslateY());
 
+        SkAutoLockPixels alp(bitmap);
+        if (!bitmap.readyToDraw()) {
+            return;
+        }
+
         SkMask  mask;
         mask.fBounds.set(ix, iy, ix + bitmap.width(), iy + bitmap.height());
         mask.fFormat = SkMask::kA8_Format;
@@ -1173,15 +1307,16 @@
         }
     }
 
-    // only lock the pixels if we passed the clip and bounder tests
-    SkAutoLockPixels alp(bitmap);
-    // after the lock, check if we are valid
-    if (!bitmap.readyToDraw()) {
-        return;
-    }
-
     if (bitmap.getConfig() != SkBitmap::kA8_Config &&
             just_translate(matrix, bitmap)) {
+        //
+        // It is safe to call lock pixels now, since we know the matrix is
+        // (more or less) identity.
+        //
+        SkAutoLockPixels alp(bitmap);
+        if (!bitmap.readyToDraw()) {
+            return;
+        }
         int ix = SkScalarRound(matrix.getTranslateX());
         int iy = SkScalarRound(matrix.getTranslateY());
         if (clipHandlesSprite(*fRC, ix, iy, bitmap)) {
@@ -1281,6 +1416,7 @@
 
 #include "SkScalerContext.h"
 #include "SkGlyphCache.h"
+#include "SkTextToPathIter.h"
 #include "SkUtils.h"
 
 static void measure_text(SkGlyphCache* cache, SkDrawCacheProc glyphCacheProc,
@@ -1308,7 +1444,7 @@
                               const SkPaint& paint) const {
     SkDEBUGCODE(this->validate();)
 
-    SkTextToPathIter iter(text, byteLength, paint, true, true);
+    SkTextToPathIter iter(text, byteLength, paint, true);
 
     SkMatrix    matrix;
     matrix.setScale(iter.getPathScale(), iter.getPathScale());
@@ -1317,13 +1453,15 @@
     const SkPath* iterPath;
     SkScalar xpos, prevXPos = 0;
 
-    while ((iterPath = iter.next(&xpos)) != NULL) {
+    while (iter.next(&iterPath, &xpos)) {
         matrix.postTranslate(xpos - prevXPos, 0);
-        const SkPaint& pnt = iter.getPaint();
-        if (fDevice) {
-            fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
-        } else {
-            this->drawPath(*iterPath, pnt, &matrix, false);
+        if (iterPath) {
+            const SkPaint& pnt = iter.getPaint();
+            if (fDevice) {
+                fDevice->drawPath(*this, *iterPath, pnt, &matrix, false);
+            } else {
+                this->drawPath(*iterPath, pnt, &matrix, false);
+            }
         }
         prevXPos = xpos;
     }
@@ -1353,9 +1491,9 @@
     int right   = left + glyph.fWidth;
     int bottom  = top + glyph.fHeight;
 
-    SkMask		mask;
-    SkIRect		storage;
-    SkIRect*	bounds = &mask.fBounds;
+    SkMask        mask;
+    SkIRect        storage;
+    SkIRect*    bounds = &mask.fBounds;
 
     mask.fBounds.set(left, top, right, bottom);
 
@@ -1404,7 +1542,7 @@
         if (NULL == aa) {
             aa = (uint8_t*)state.fCache->findImage(glyph);
             if (NULL == aa) {
-            	return;
+                return;
             }
         }
 
@@ -1424,15 +1562,15 @@
     int left = SkFixedFloor(fx);
     int top = SkFixedFloor(fy);
     SkASSERT(glyph.fWidth > 0 && glyph.fHeight > 0);
-    
+
     SkMask  mask;
-    
+
     left += glyph.fLeft;
     top  += glyph.fTop;
-    
+
     mask.fBounds.set(left, top, left + glyph.fWidth, top + glyph.fHeight);
     SkRegion::Cliperator clipper(*state.fClip, mask.fBounds);
-    
+
     if (!clipper.done()) {
         const SkIRect&  cr = clipper.rect();
         const uint8_t*  aa = (const uint8_t*)glyph.fImage;
@@ -1441,8 +1579,8 @@
             if (NULL == aa) {
                 return;
             }
-    	}
-        
+        }
+
         // we need to pass the origin, which we approximate with our
         // (unadjusted) left,top coordinates (the caller called fixedfloor)
         if (state.fBounder->doIRectGlyph(cr,
@@ -1532,8 +1670,11 @@
         return;
     }
 
+    // SkScalarRec doesn't currently have a way of representing hairline stroke and
+    // will fill if its frame-width is 0.
     if (/*paint.isLinearText() ||*/
-        (fMatrix->hasPerspective())) {
+        (fMatrix->hasPerspective()) ||
+        (0 == paint.getStrokeWidth() && SkPaint::kStroke_Style == paint.getStyle())) {
         this->drawText_asPaths(text, byteLength, x, y, paint);
         return;
     }
@@ -1541,15 +1682,8 @@
     SkDrawCacheProc glyphCacheProc = paint.getDrawCacheProc();
 
     const SkMatrix* matrix = fMatrix;
-    if (hasCustomD1GProc(*this)) {
-        // only support the fMVMatrix (for now) for the GPU case, which also
-        // sets the fD1GProc
-        if (fMVMatrix) {
-            matrix = fMVMatrix;
-        }
-    }
 
-    SkAutoGlyphCache    autoCache(paint, matrix);
+    SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, matrix);
     SkGlyphCache*       cache = autoCache.getCache();
 
     // transform our starting point
@@ -1590,7 +1724,7 @@
         } else if (kY_SkAxisAlignment == baseline) {
             fxMask = 0;
         }
-    
+
     // apply bias here to avoid adding 1/2 the sampling frequency in the loop
         fx += SK_FixedHalf >> SkGlyph::kSubBits;
         fy += SK_FixedHalf >> SkGlyph::kSubBits;
@@ -1741,16 +1875,9 @@
     }
 
     const SkMatrix* matrix = fMatrix;
-    if (hasCustomD1GProc(*this)) {
-        // only support the fMVMatrix (for now) for the GPU case, which also
-        // sets the fD1GProc
-        if (fMVMatrix) {
-            matrix = fMVMatrix;
-        }
-    }
 
     SkDrawCacheProc     glyphCacheProc = paint.getDrawCacheProc();
-    SkAutoGlyphCache    autoCache(paint, matrix);
+    SkAutoGlyphCache    autoCache(paint, &fDevice->fLeakyProperties, matrix);
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkAAClipBlitterWrapper wrapper;
@@ -1764,7 +1891,7 @@
             blitter = wrapper.getBlitter();
         }
     }
-    
+
     const char*        stop = text + byteLength;
     AlignProc          alignProc = pick_align_proc(paint.getTextAlign());
     SkDraw1Glyph       d1g;
@@ -1781,13 +1908,9 @@
 
                 tmsProc(tms, pos);
 
-#ifdef SK_DRAW_POS_TEXT_IGNORE_SUBPIXEL_LEFT_ALIGN_FIX
-                SkFixed fx = SkScalarToFixed(tms.fLoc.fX);
-                SkFixed fy = SkScalarToFixed(tms.fLoc.fY);
-#else
                 SkFixed fx = SkScalarToFixed(tms.fLoc.fX) + (SK_FixedHalf >> SkGlyph::kSubBits);
                 SkFixed fy = SkScalarToFixed(tms.fLoc.fY) + (SK_FixedHalf >> SkGlyph::kSubBits);
-#endif
+
                 SkFixed fxMask = ~0;
                 SkFixed fyMask = ~0;
 
@@ -1845,22 +1968,39 @@
             }
         }
     } else {    // not subpixel
-        while (text < stop) {
-            // the last 2 parameters are ignored
-            const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+        if (SkPaint::kLeft_Align == paint.getTextAlign()) {
+            while (text < stop) {
+                // the last 2 parameters are ignored
+                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
 
-            if (glyph.fWidth) {
-                tmsProc(tms, pos);
+                if (glyph.fWidth) {
+                    tmsProc(tms, pos);
 
-                SkIPoint fixedLoc;
-                alignProc(tms.fLoc, glyph, &fixedLoc);
-
-                proc(d1g,
-                     fixedLoc.fX + SK_FixedHalf,
-                     fixedLoc.fY + SK_FixedHalf,
-                     glyph);
+                    proc(d1g,
+                         SkScalarToFixed(tms.fLoc.fX) + SK_FixedHalf,
+                         SkScalarToFixed(tms.fLoc.fY) + SK_FixedHalf,
+                         glyph);
+                }
+                pos += scalarsPerPosition;
             }
-            pos += scalarsPerPosition;
+        } else {
+            while (text < stop) {
+                // the last 2 parameters are ignored
+                const SkGlyph& glyph = glyphCacheProc(cache, &text, 0, 0);
+
+                if (glyph.fWidth) {
+                    tmsProc(tms, pos);
+
+                    SkIPoint fixedLoc;
+                    alignProc(tms.fLoc, glyph, &fixedLoc);
+
+                    proc(d1g,
+                         fixedLoc.fX + SK_FixedHalf,
+                         fixedLoc.fY + SK_FixedHalf,
+                         glyph);
+                }
+                pos += scalarsPerPosition;
+            }
         }
     }
 }
@@ -1885,7 +2025,10 @@
         SkScalar sx = pos.fX;
         SkScalar sy = pos.fY;
 
-        meas.getPosTan(sx, &pos, &tangent);
+        if (!meas.getPosTan(sx, &pos, &tangent)) {
+            // set to 0 if the measure failed, so that we just set dst == pos
+            tangent.set(0, 0);
+        }
 
         /*  This is the old way (that explains our approach but is way too slow
             SkMatrix    matrix;
@@ -1955,7 +2098,7 @@
         return;
     }
 
-    SkTextToPathIter    iter(text, byteLength, paint, true, true);
+    SkTextToPathIter    iter(text, byteLength, paint, true);
     SkPathMeasure       meas(follow, false);
     SkScalar            hOffset = 0;
 
@@ -1975,19 +2118,21 @@
 
     scaledMatrix.setScale(scale, scale);
 
-    while ((iterPath = iter.next(&xpos)) != NULL) {
-        SkPath      tmp;
-        SkMatrix    m(scaledMatrix);
+    while (iter.next(&iterPath, &xpos)) {
+        if (iterPath) {
+            SkPath      tmp;
+            SkMatrix    m(scaledMatrix);
 
-        m.postTranslate(xpos + hOffset, 0);
-        if (matrix) {
-            m.postConcat(*matrix);
-        }
-        morphpath(&tmp, *iterPath, meas, m);
-        if (fDevice) {
-            fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true);
-        } else {
-            this->drawPath(tmp, iter.getPaint(), NULL, true);
+            m.postTranslate(xpos + hOffset, 0);
+            if (matrix) {
+                m.postConcat(*matrix);
+            }
+            morphpath(&tmp, *iterPath, meas, m);
+            if (fDevice) {
+                fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true);
+            } else {
+                this->drawPath(tmp, iter.getPaint(), NULL, true);
+            }
         }
     }
 }
@@ -2021,7 +2166,7 @@
     // End copied from SkTextToPathIter constructor
 
     // detach cache
-    SkGlyphCache* cache = tempPaint.detachCache(NULL);
+    SkGlyphCache* cache = tempPaint.detachCache(NULL, NULL);
 
     // Must set scale, even if 1
     SkScalar scale = SK_Scalar1;
@@ -2210,20 +2355,18 @@
 
     bool setup(const SkPoint pts[], const SkColor colors[], int, int, int);
 
-    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count);
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTriColorShader)
 
 protected:
     SkTriColorShader(SkFlattenableReadBuffer& buffer) : SkShader(buffer) {}
 
-    virtual Factory getFactory() { return CreateProc; }
-
 private:
     SkMatrix    fDstToUnit;
     SkPMColor   fColors[3];
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkTriColorShader, (buffer));
-    }
     typedef SkShader INHERITED;
 };
 
@@ -2287,6 +2430,16 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkTriColorShader::toString(SkString* str) const {
+    str->append("SkTriColorShader: (");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
 void SkDraw::drawVertices(SkCanvas::VertexMode vmode, int count,
                           const SkPoint vertices[], const SkPoint textures[],
                           const SkColor colors[], SkXfermode* xmode,
@@ -2340,13 +2493,13 @@
     if (NULL != colors) {
         if (NULL == textures) {
             // just colors (no texture)
-            p.setShader(&triShader);
+            shader = p.setShader(&triShader);
         } else {
             // colors * texture
             SkASSERT(shader);
             bool releaseMode = false;
             if (NULL == xmode) {
-                xmode = SkXfermode::Create(SkXfermode::kMultiply_Mode);
+                xmode = SkXfermode::Create(SkXfermode::kModulate_Mode);
                 releaseMode = true;
             }
             SkShader* compose = SkNEW_ARGS(SkComposeShader,
@@ -2355,32 +2508,36 @@
             if (releaseMode) {
                 xmode->unref();
             }
+            shader = compose;
         }
     }
 
     SkAutoBlitterChoose blitter(*fBitmap, *fMatrix, p);
+    // important that we abort early, as below we may manipulate the shader
+    // and that is only valid if the shader returned true from setContext.
+    // If it returned false, then our blitter will be the NullBlitter.
+    if (blitter->isNullBlitter()) {
+        return;
+    }
+
     // setup our state and function pointer for iterating triangles
     VertState       state(count, indices, indexCount);
     VertState::Proc vertProc = state.chooseProc(vmode);
 
     if (NULL != textures || NULL != colors) {
-        SkMatrix  localM, tempM;
-        bool      hasLocalM = shader && shader->getLocalMatrix(&localM);
-
-        if (NULL != colors) {
-            if (!triShader.setContext(*fBitmap, p, *fMatrix)) {
-                colors = NULL;
-            }
+        SkMatrix  tempM;
+        SkMatrix  savedLocalM;
+        if (shader) {
+            savedLocalM = shader->getLocalMatrix();
         }
 
         while (vertProc(&state)) {
             if (NULL != textures) {
                 if (texture_to_matrix(state, vertices, textures, &tempM)) {
-                    if (hasLocalM) {
-                        tempM.postConcat(localM);
-                    }
+                    tempM.postConcat(savedLocalM);
                     shader->setLocalMatrix(tempM);
                     // need to recal setContext since we changed the local matrix
+                    shader->endContext();
                     if (!shader->setContext(*fBitmap, p, *fMatrix)) {
                         continue;
                     }
@@ -2400,11 +2557,7 @@
         }
         // now restore the shader's original local matrix
         if (NULL != shader) {
-            if (hasLocalM) {
-                shader->setLocalMatrix(localM);
-            } else {
-                shader->resetLocalMatrix();
-            }
+            shader->setLocalMatrix(savedLocalM);
         }
     } else {
         // no colors[] and no texture
@@ -2434,9 +2587,6 @@
 
     br.set(0, 0, fBitmap->width(), fBitmap->height());
     SkASSERT(cr.isEmpty() || br.contains(cr));
-
-    // assert that both are null, or both are not-null
-    SkASSERT(!fMVMatrix == !fExtMatrix);
 }
 
 #endif
@@ -2540,7 +2690,7 @@
 #include "SkBlitter.h"
 
 static bool compute_bounds(const SkPath& devPath, const SkIRect* clipBounds,
-                           SkMaskFilter* filter, const SkMatrix* filterMatrix,
+                       const SkMaskFilter* filter, const SkMatrix* filterMatrix,
                            SkIRect* bounds) {
     if (devPath.isEmpty()) {
         return false;
@@ -2553,7 +2703,7 @@
         pathBounds.roundOut(bounds);
     }
 
-    SkIPoint margin;
+    SkIPoint margin = SkIPoint::Make(0, 0);
     if (filter) {
         SkASSERT(filterMatrix);
 
@@ -2588,7 +2738,8 @@
     return true;
 }
 
-static void draw_into_mask(const SkMask& mask, const SkPath& devPath) {
+static void draw_into_mask(const SkMask& mask, const SkPath& devPath,
+                           SkPaint::Style style) {
     SkBitmap        bm;
     SkDraw          draw;
     SkRasterClip    clip;
@@ -2608,12 +2759,14 @@
     draw.fMatrix    = &matrix;
     draw.fBounder   = NULL;
     paint.setAntiAlias(true);
+    paint.setStyle(style);
     draw.drawPath(devPath, paint);
 }
 
 bool SkDraw::DrawToMask(const SkPath& devPath, const SkIRect* clipBounds,
-                        SkMaskFilter* filter, const SkMatrix* filterMatrix,
-                        SkMask* mask, SkMask::CreateMode mode) {
+                        const SkMaskFilter* filter, const SkMatrix* filterMatrix,
+                        SkMask* mask, SkMask::CreateMode mode,
+                        SkPaint::Style style) {
     if (SkMask::kJustRenderImage_CreateMode != mode) {
         if (!compute_bounds(devPath, clipBounds, filter, filterMatrix, &mask->fBounds))
             return false;
@@ -2632,7 +2785,7 @@
     }
 
     if (SkMask::kJustComputeBounds_CreateMode != mode) {
-        draw_into_mask(*mask, devPath);
+        draw_into_mask(*mask, devPath, style);
     }
 
     return true;
diff --git a/src/core/SkDrawProcs.h b/src/core/SkDrawProcs.h
index 2e26ecf..ec086dd 100644
--- a/src/core/SkDrawProcs.h
+++ b/src/core/SkDrawProcs.h
@@ -26,7 +26,7 @@
     // i.e. half the sampling frequency has been added.
     // e.g. 1/2 or 1/(2^(SkGlyph::kSubBits+1)) has already been added.
     typedef void (*Proc)(const SkDraw1Glyph&, SkFixed x, SkFixed y, const SkGlyph&);
-    
+
     Proc init(const SkDraw* draw, SkBlitter* blitter, SkGlyphCache* cache);
 };
 
@@ -35,12 +35,11 @@
 };
 
 /**
- *  If the current paint is set to stroke and the stroke-width when applied to 
+ *  If the current paint is set to stroke and the stroke-width when applied to
  *  the matrix is <= 1.0, then this returns true, and sets coverage (simulating
- *  a stroke by drawing a hairline with partial coverage). If any of these 
+ *  a stroke by drawing a hairline with partial coverage). If any of these
  *  conditions are false, then this returns false and coverage is ignored.
  */
 bool SkDrawTreatAsHairline(const SkPaint&, const SkMatrix&, SkScalar* coverage);
 
 #endif
-
diff --git a/src/core/SkEdge.cpp b/src/core/SkEdge.cpp
index aab1c76..8904ca7 100644
--- a/src/core/SkEdge.cpp
+++ b/src/core/SkEdge.cpp
@@ -23,6 +23,12 @@
     or pt >> 8 for antialiasing. This is implemented as pt >> (10 - shift).
 */
 
+static inline SkFixed SkFDot6ToFixedDiv2(SkFDot6 value) {
+    // we want to return SkFDot6ToFixed(value >> 1), but we don't want to throw
+    // away data in value, so just perform a modify up-shift
+    return value << (16 - 6 - 1);
+}
+
 /////////////////////////////////////////////////////////////////////////
 
 int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, const SkIRect* clip,
@@ -66,8 +72,9 @@
     }
 
     SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
+    const int dy  = SkEdge_Compute_DY(top, y0);
 
-    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63));   // + SK_Fixed1/2
+    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy));   // + SK_Fixed1/2
     fDX         = slope;
     fFirstY     = top;
     fLastY      = bot - 1;
@@ -106,8 +113,9 @@
     x1 >>= 10;
 
     SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
+    const int dy  = SkEdge_Compute_DY(top, y0);
 
-    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, (32 - y0) & 63));   // + SK_Fixed1/2
+    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy));   // + SK_Fixed1/2
     fDX         = slope;
     fFirstY     = top;
     fLastY      = bot - 1;
@@ -219,21 +227,40 @@
     } else if (shift > MAX_COEFF_SHIFT) {
         shift = MAX_COEFF_SHIFT;
     }
-    
+
     fWinding    = SkToS8(winding);
-    fCurveShift = SkToU8(shift);
     //fCubicDShift only set for cubics
     fCurveCount = SkToS8(1 << shift);
 
-    SkFixed A = SkFDot6ToFixed(x0 - x1 - x1 + x2);
-    SkFixed B = SkFDot6ToFixed(x1 - x0 + x1 - x0);
+    /*
+     *  We want to reformulate into polynomial form, to make it clear how we
+     *  should forward-difference.
+     *
+     *  p0 (1 - t)^2 + p1 t(1 - t) + p2 t^2 ==> At^2 + Bt + C
+     *
+     *  A = p0 - 2p1 + p2
+     *  B = 2(p1 - p0)
+     *  C = p0
+     *
+     *  Our caller must have constrained our inputs (p0..p2) to all fit into
+     *  16.16. However, as seen above, we sometimes compute values that can be
+     *  larger (e.g. B = 2*(p1 - p0)). To guard against overflow, we will store
+     *  A and B at 1/2 of their actual value, and just apply a 2x scale during
+     *  application in updateQuadratic(). Hence we store (shift - 1) in
+     *  fCurveShift.
+     */
+
+    fCurveShift = SkToU8(shift - 1);
+
+    SkFixed A = SkFDot6ToFixedDiv2(x0 - x1 - x1 + x2);  // 1/2 the real value
+    SkFixed B = SkFDot6ToFixed(x1 - x0);                // 1/2 the real value
 
     fQx     = SkFDot6ToFixed(x0);
     fQDx    = B + (A >> shift);     // biased by shift
     fQDDx   = A >> (shift - 1);     // biased by shift
 
-    A = SkFDot6ToFixed(y0 - y1 - y1 + y2);
-    B = SkFDot6ToFixed(y1 - y0 + y1 - y0);
+    A = SkFDot6ToFixedDiv2(y0 - y1 - y1 + y2);  // 1/2 the real value
+    B = SkFDot6ToFixed(y1 - y0);                // 1/2 the real value
 
     fQy     = SkFDot6ToFixed(y0);
     fQDy    = B + (A >> shift);     // biased by shift
@@ -451,6 +478,13 @@
             newx    = fCLastX;
             newy    = fCLastY;
         }
+
+        // we want to say SkASSERT(oldy <= newy), but our finite fixedpoint
+        // doesn't always achieve that, so we have to explicitly pin it here.
+        if (newy < oldy) {
+            newy = oldy;
+        }
+
         success = this->updateLine(oldx, oldy, newx, newy);
         oldx = newx;
         oldy = newy;
@@ -461,6 +495,3 @@
     fCurveCount = SkToS8(count);
     return success;
 }
-
-
-
diff --git a/src/core/SkEdge.h b/src/core/SkEdge.h
index 29a50d1..a8ce2af 100644
--- a/src/core/SkEdge.h
+++ b/src/core/SkEdge.h
@@ -11,6 +11,16 @@
 #define SkEdge_DEFINED
 
 #include "SkRect.h"
+#include "SkFDot6.h"
+#include "SkMath.h"
+
+#ifdef SK_IGNORE_SETLINE_FIX
+    #define SkEdge_Compute_DY(top, y0)  ((32 - (y0)) & 63)
+#else
+    // This is correct, as it favors the lower-pixel when y0 is on a 1/2 pixel
+    // boundary, returning 64 instead of the old code, which returns 0.
+    #define SkEdge_Compute_DY(top, y0)  ((top << 6) + 32 - (y0))
+#endif
 
 struct SkEdge {
     enum Type {
@@ -33,6 +43,8 @@
 
     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);
     void chopLineWithClip(const SkIRect& clip);
 
@@ -43,11 +55,7 @@
 
 #ifdef SK_DEBUG
     void dump() const {
-    #ifdef SK_CAN_USE_FLOAT
         SkDebugf("edge: firstY:%d lastY:%d x:%g dx:%g w:%d\n", fFirstY, fLastY, SkFixedToFloat(fX), SkFixedToFloat(fDX), fWinding);
-    #else
-        SkDebugf("edge: firstY:%d lastY:%d x:%x dx:%x w:%d\n", fFirstY, fLastY, fX, fDX, fWinding);
-    #endif
     }
 
     void validate() const {
@@ -82,4 +90,53 @@
     int updateCubic();
 };
 
+int SkEdge::setLine(const SkPoint& p0, const SkPoint& p1, int shift) {
+    SkFDot6 x0, y0, x1, y1;
+
+    {
+#ifdef SK_SCALAR_IS_FLOAT
+        float scale = float(1 << (shift + 6));
+        x0 = int(p0.fX * scale);
+        y0 = int(p0.fY * scale);
+        x1 = int(p1.fX * scale);
+        y1 = int(p1.fY * scale);
+#else
+        shift = 10 - shift;
+        x0 = p0.fX >> shift;
+        y0 = p0.fY >> shift;
+        x1 = p1.fX >> shift;
+        y1 = p1.fY >> shift;
+#endif
+    }
+
+    int winding = 1;
+
+    if (y0 > y1) {
+        SkTSwap(x0, x1);
+        SkTSwap(y0, y1);
+        winding = -1;
+    }
+
+    int top = SkFDot6Round(y0);
+    int bot = SkFDot6Round(y1);
+
+    // are we a zero-height line?
+    if (top == bot) {
+        return 0;
+    }
+
+    SkFixed slope = SkFDot6Div(x1 - x0, y1 - y0);
+    const int dy  = SkEdge_Compute_DY(top, y0);
+
+    fX          = SkFDot6ToFixed(x0 + SkFixedMul(slope, dy));   // + SK_Fixed1/2
+    fDX         = slope;
+    fFirstY     = top;
+    fLastY      = bot - 1;
+    fCurveCount = 0;
+    fWinding    = SkToS8(winding);
+    fCurveShift = 0;
+    return 1;
+}
+
+
 #endif
diff --git a/src/core/SkEdgeBuilder.cpp b/src/core/SkEdgeBuilder.cpp
index 01417e4..608a83c 100644
--- a/src/core/SkEdgeBuilder.cpp
+++ b/src/core/SkEdgeBuilder.cpp
@@ -12,17 +12,19 @@
 #include "SkLineClipper.h"
 #include "SkGeometry.h"
 
-SkEdgeBuilder::SkEdgeBuilder() : fAlloc(16*1024) {}
-
 template <typename T> static T* typedAllocThrow(SkChunkAlloc& alloc) {
     return static_cast<T*>(alloc.allocThrow(sizeof(T)));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkEdgeBuilder::SkEdgeBuilder() : fAlloc(16*1024) {
+    fEdgeList = NULL;
+}
+
 void SkEdgeBuilder::addLine(const SkPoint pts[]) {
     SkEdge* edge = typedAllocThrow<SkEdge>(fAlloc);
-    if (edge->setLine(pts[0], pts[1], NULL, fShiftUp)) {
+    if (edge->setLine(pts[0], pts[1], fShiftUp)) {
         fList.push(edge);
     } else {
         // TODO: unallocate edge from storage...
@@ -77,12 +79,90 @@
              SkIntToScalar(src.fBottom >> shift));
 }
 
+int SkEdgeBuilder::buildPoly(const SkPath& path, const SkIRect* iclip,
+                             int shiftUp) {
+    SkPath::Iter    iter(path, true);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    int maxEdgeCount = path.countPoints();
+    if (iclip) {
+        // clipping can turn 1 line into (up to) kMaxClippedLineSegments, since
+        // we turn portions that are clipped out on the left/right into vertical
+        // segments.
+        maxEdgeCount *= SkLineClipper::kMaxClippedLineSegments;
+    }
+    size_t maxEdgeSize = maxEdgeCount * sizeof(SkEdge);
+    size_t maxEdgePtrSize = maxEdgeCount * sizeof(SkEdge*);
+
+    // lets store the edges and their pointers in the same block
+    char* storage = (char*)fAlloc.allocThrow(maxEdgeSize + maxEdgePtrSize);
+    SkEdge* edge = reinterpret_cast<SkEdge*>(storage);
+    SkEdge** edgePtr = reinterpret_cast<SkEdge**>(storage + maxEdgeSize);
+    // Record the beginning of our pointers, so we can return them to the caller
+    fEdgeList = edgePtr;
+
+    if (iclip) {
+        SkRect clip;
+        setShiftedClip(&clip, *iclip, shiftUp);
+
+        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
+            switch (verb) {
+                case SkPath::kMove_Verb:
+                case SkPath::kClose_Verb:
+                    // we ignore these, and just get the whole segment from
+                    // the corresponding line/quad/cubic verbs
+                    break;
+                case SkPath::kLine_Verb: {
+                    SkPoint lines[SkLineClipper::kMaxPoints];
+                    int lineCount = SkLineClipper::ClipLine(pts, clip, lines);
+                    SkASSERT(lineCount <= SkLineClipper::kMaxClippedLineSegments);
+                    for (int i = 0; i < lineCount; i++) {
+                        if (edge->setLine(lines[i], lines[i + 1], shiftUp)) {
+                            *edgePtr++ = edge++;
+                        }
+                    }
+                    break;
+                }
+                default:
+                    SkDEBUGFAIL("unexpected verb");
+                    break;
+            }
+        }
+    } else {
+        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
+            switch (verb) {
+                case SkPath::kMove_Verb:
+                case SkPath::kClose_Verb:
+                    // we ignore these, and just get the whole segment from
+                    // the corresponding line/quad/cubic verbs
+                    break;
+                case SkPath::kLine_Verb:
+                    if (edge->setLine(pts[0], pts[1], shiftUp)) {
+                        *edgePtr++ = edge++;
+                    }
+                    break;
+                default:
+                    SkDEBUGFAIL("unexpected verb");
+                    break;
+            }
+        }
+    }
+    SkASSERT((char*)edge <= (char*)fEdgeList);
+    SkASSERT(edgePtr - fEdgeList <= maxEdgeCount);
+    return edgePtr - fEdgeList;
+}
+
 int SkEdgeBuilder::build(const SkPath& path, const SkIRect* iclip,
                          int shiftUp) {
     fAlloc.reset();
     fList.reset();
     fShiftUp = shiftUp;
 
+    if (SkPath::kLine_SegmentMask == path.getSegmentMasks()) {
+        return this->buildPoly(path, iclip, shiftUp);
+    }
+
     SkPath::Iter    iter(path, true);
     SkPoint         pts[4];
     SkPath::Verb    verb;
@@ -92,7 +172,7 @@
         setShiftedClip(&clip, *iclip, shiftUp);
         SkEdgeClipper clipper;
 
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
             switch (verb) {
                 case SkPath::kMove_Verb:
                 case SkPath::kClose_Verb:
@@ -123,7 +203,7 @@
             }
         }
     } else {
-        while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+        while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
             switch (verb) {
                 case SkPath::kMove_Verb:
                 case SkPath::kClose_Verb:
@@ -155,7 +235,6 @@
             }
         }
     }
+    fEdgeList = fList.begin();
     return fList.count();
 }
-
-
diff --git a/src/core/SkEdgeBuilder.h b/src/core/SkEdgeBuilder.h
index ae21f05..b296f77 100644
--- a/src/core/SkEdgeBuilder.h
+++ b/src/core/SkEdgeBuilder.h
@@ -19,20 +19,33 @@
 class SkEdgeBuilder {
 public:
     SkEdgeBuilder();
-    
+
+    // returns the number of built edges. The array of those edge pointers
+    // is returned from edgeList().
     int build(const SkPath& path, const SkIRect* clip, int shiftUp);
 
-    SkEdge** edgeList() { return fList.begin(); }
+    SkEdge** edgeList() { return fEdgeList; }
 
 private:
     SkChunkAlloc        fAlloc;
     SkTDArray<SkEdge*>  fList;
+
+    /*
+     *  If we're in general mode, we allcoate the pointers in fList, and this
+     *  will point at fList.begin(). If we're in polygon mode, fList will be
+     *  empty, as we will have preallocated room for the pointers in fAlloc's
+     *  block, and fEdgeList will point into that.
+     */
+    SkEdge**            fEdgeList;
+
     int                 fShiftUp;
 
     void addLine(const SkPoint pts[]);
     void addQuad(const SkPoint pts[]);
     void addCubic(const SkPoint pts[]);
     void addClipper(SkEdgeClipper*);
+
+    int buildPoly(const SkPath& path, const SkIRect* clip, int shiftUp);
 };
 
 #endif
diff --git a/src/core/SkEdgeClipper.cpp b/src/core/SkEdgeClipper.cpp
index d77f6f8..1f0e382 100644
--- a/src/core/SkEdgeClipper.cpp
+++ b/src/core/SkEdgeClipper.cpp
@@ -54,7 +54,7 @@
     SkScalar A = c0 - c1 - c1 + c2;
     SkScalar B = 2*(c1 - c0);
     SkScalar C = c0 - target;
-    
+
     SkScalar roots[2];  // we only expect one, but make room for 2 for safety
     int count = SkFindUnitQuadRoots(A, B, C, roots);
     if (count) {
@@ -82,8 +82,10 @@
         if (chopMonoQuadAtY(pts, clip.fTop, &t)) {
             // take the 2nd chopped quad
             SkChopQuadAt(pts, tmp, t);
-            clamp_ge(tmp[2].fY, clip.fTop);
+            // clamp to clean up imprecise numerics in the chop
+            tmp[2].fY = clip.fTop;
             clamp_ge(tmp[3].fY, clip.fTop);
+
             pts[0] = tmp[2];
             pts[1] = tmp[3];
         } else {
@@ -96,13 +98,15 @@
             }
         }
     }
-    
+
     // are we partially below
     if (pts[2].fY > clip.fBottom) {
         if (chopMonoQuadAtY(pts, clip.fBottom, &t)) {
             SkChopQuadAt(pts, tmp, t);
+            // clamp to clean up imprecise numerics in the chop
             clamp_le(tmp[1].fY, clip.fBottom);
-            clamp_le(tmp[2].fY, clip.fBottom);
+            tmp[2].fY = clip.fBottom;
+
             pts[1] = tmp[1];
             pts[2] = tmp[2];
         } else {
@@ -126,7 +130,7 @@
     if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
         return;
     }
-    
+
     // Now chop so that pts is contained within clip in Y
     chop_quad_in_Y(pts, clip);
 
@@ -156,8 +160,10 @@
         if (chopMonoQuadAtX(pts, clip.fLeft, &t)) {
             SkChopQuadAt(pts, tmp, t);
             this->appendVLine(clip.fLeft, tmp[0].fY, tmp[2].fY, reverse);
-            clamp_ge(tmp[2].fX, clip.fLeft);
+            // clamp to clean up imprecise numerics in the chop
+            tmp[2].fX = clip.fLeft;
             clamp_ge(tmp[3].fX, clip.fLeft);
+
             pts[0] = tmp[2];
             pts[1] = tmp[3];
         } else {
@@ -167,13 +173,15 @@
             return;
         }
     }
-    
+
     // are we partially to the right
     if (pts[2].fX > clip.fRight) {
         if (chopMonoQuadAtX(pts, clip.fRight, &t)) {
             SkChopQuadAt(pts, tmp, t);
+            // clamp to clean up imprecise numerics in the chop
             clamp_le(tmp[1].fX, clip.fRight);
-            clamp_le(tmp[2].fX, clip.fRight);
+            tmp[2].fX = clip.fRight;
+
             this->appendQuad(tmp, reverse);
             this->appendVLine(clip.fRight, tmp[2].fY, tmp[4].fY, reverse);
         } else {
@@ -192,7 +200,7 @@
 
     SkRect  bounds;
     bounds.set(srcPts, 3);
-    
+
     if (!quick_reject(bounds, clip)) {
         SkPoint monoY[5];
         int countY = SkChopQuadAtYExtrema(srcPts, monoY);
@@ -238,7 +246,11 @@
     SkScalar maxT = SK_Scalar1;
     SkScalar mid;
     int i;
+#ifdef SK_IGNORE_CLIP_BUG_FIX
     for (i = 0; i < 16; i++) {
+#else
+    for (i = 0; i < 24; i++) {
+#endif
         mid = SkScalarAve(minT, maxT);
         SkScalar delta = eval_cubic_coeff(A, B, C, D, mid);
         if (delta < 0) {
@@ -266,7 +278,7 @@
 
 // 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;
@@ -274,12 +286,11 @@
             SkPoint tmp[7];
             SkChopCubicAt(pts, tmp, t);
 
-            // tmp[3, 4, 5].fY should all be to the below clip.fTop, and
+            // tmp[3, 4].fY should all be to the below clip.fTop, and
             // still be monotonic in Y. Since we can't trust the numerics of
             // the chopper, we force those conditions now
             tmp[3].fY = clip.fTop;
-            tmp[4].fY = SkMaxScalar(tmp[4].fY, clip.fTop);
-            tmp[5].fY = SkMaxScalar(tmp[5].fY, tmp[4].fY);
+            clamp_ge(tmp[4].fY, clip.fTop);
 
             pts[0] = tmp[3];
             pts[1] = tmp[4];
@@ -292,16 +303,16 @@
             }
         }
     }
-    
+
     // 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);
-            clamp_le(tmp[1].fY, clip.fBottom);
+            tmp[3].fY = clip.fBottom;
             clamp_le(tmp[2].fY, clip.fBottom);
-            clamp_le(tmp[3].fY, clip.fBottom);
+
             pts[1] = tmp[1];
             pts[2] = tmp[2];
             pts[3] = tmp[3];
@@ -319,7 +330,7 @@
 void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
     SkPoint pts[4];
     bool reverse = sort_increasing_Y(pts, src, 4);
-    
+
     // are we completely above or below
     if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) {
         return;
@@ -333,9 +344,9 @@
         SkTSwap<SkPoint>(pts[1], pts[2]);
         reverse = !reverse;
     }
-    
+
     // Now chop in X has needed, and record the segments
-    
+
     if (pts[3].fX <= clip.fLeft) {  // wholly to the left
         this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse);
         return;
@@ -357,8 +368,8 @@
             // still be monotonic in X. Since we can't trust the numerics of
             // the chopper, we force those conditions now
             tmp[3].fX = clip.fLeft;
-            tmp[4].fX = SkMaxScalar(tmp[4].fX, clip.fLeft);
-            tmp[5].fX = SkMaxScalar(tmp[5].fX, tmp[4].fX);
+            clamp_ge(tmp[4].fX, clip.fLeft);
+            clamp_ge(tmp[5].fX, tmp[4].fX);
 
             pts[0] = tmp[3];
             pts[1] = tmp[4];
@@ -370,16 +381,17 @@
             return;
         }
     }
-    
+
     // 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);
-            clamp_le(tmp[1].fX, clip.fRight);
+            tmp[3].fX = clip.fRight;
             clamp_le(tmp[2].fX, clip.fRight);
-            clamp_le(tmp[3].fX, clip.fRight);
+            clamp_le(tmp[1].fX, tmp[2].fX);
+
             this->appendCubic(tmp, reverse);
             this->appendVLine(clip.fRight, tmp[3].fY, tmp[6].fY, reverse);
         } else {
@@ -395,10 +407,10 @@
 bool SkEdgeClipper::clipCubic(const SkPoint srcPts[4], const SkRect& clip) {
     fCurrPoint = fPoints;
     fCurrVerb = fVerbs;
-    
+
     SkRect  bounds;
     bounds.set(srcPts, 4);
-    
+
     if (!quick_reject(bounds, clip)) {
         SkPoint monoY[10];
         int countY = SkChopCubicAtYExtrema(srcPts, monoY);
@@ -415,7 +427,7 @@
             }
         }
     }
-    
+
     *fCurrVerb = SkPath::kDone_Verb;
     fCurrPoint = fPoints;
     fCurrVerb = fVerbs;
@@ -427,7 +439,7 @@
 void SkEdgeClipper::appendVLine(SkScalar x, SkScalar y0, SkScalar y1,
                                 bool reverse) {
     *fCurrVerb++ = SkPath::kLine_Verb;
-    
+
     if (reverse) {
         SkTSwap<SkScalar>(y0, y1);
     }
@@ -438,7 +450,7 @@
 
 void SkEdgeClipper::appendQuad(const SkPoint pts[3], bool reverse) {
     *fCurrVerb++ = SkPath::kQuad_Verb;
-    
+
     if (reverse) {
         fCurrPoint[0] = pts[2];
         fCurrPoint[2] = pts[0];
@@ -452,7 +464,7 @@
 
 void SkEdgeClipper::appendCubic(const SkPoint pts[4], bool reverse) {
     *fCurrVerb++ = SkPath::kCubic_Verb;
-    
+
     if (reverse) {
         for (int i = 0; i < 4; i++) {
             fCurrPoint[i] = pts[3 - i];
diff --git a/src/core/SkEdgeClipper.h b/src/core/SkEdgeClipper.h
new file mode 100644
index 0000000..e16ed55
--- /dev/null
+++ b/src/core/SkEdgeClipper.h
@@ -0,0 +1,51 @@
+
+/*
+ * Copyright 2009 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkEdgeClipper_DEFINED
+#define SkEdgeClipper_DEFINED
+
+#include "SkPath.h"
+
+/** This is basically an iterator. It is initialized with an edge and a clip,
+    and then next() is called until it returns kDone_Verb.
+ */
+class SkEdgeClipper {
+public:
+    bool clipQuad(const SkPoint pts[3], const SkRect& clip);
+    bool clipCubic(const SkPoint pts[4], const SkRect& clip);
+
+    SkPath::Verb next(SkPoint pts[]);
+
+private:
+    SkPoint*        fCurrPoint;
+    SkPath::Verb*   fCurrVerb;
+
+    enum {
+        kMaxVerbs = 13,
+        kMaxPoints = 32
+    };
+    SkPoint         fPoints[kMaxPoints];
+    SkPath::Verb    fVerbs[kMaxVerbs];
+
+    void clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip);
+    void clipMonoCubic(const SkPoint srcPts[4], const SkRect& clip);
+    void appendVLine(SkScalar x, SkScalar y0, SkScalar y1, bool reverse);
+    void appendQuad(const SkPoint pts[3], bool reverse);
+    void appendCubic(const SkPoint pts[4], bool reverse);
+};
+
+#ifdef SK_DEBUG
+    void sk_assert_monotonic_x(const SkPoint pts[], int count);
+    void sk_assert_monotonic_y(const SkPoint pts[], int count);
+#else
+    #define sk_assert_monotonic_x(pts, count)
+    #define sk_assert_monotonic_y(pts, count)
+#endif
+
+#endif
diff --git a/src/core/SkFDot6.h b/src/core/SkFDot6.h
new file mode 100644
index 0000000..a88ea92
--- /dev/null
+++ b/src/core/SkFDot6.h
@@ -0,0 +1,60 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkFDot6_DEFINED
+#define SkFDot6_DEFINED
+
+#include "SkScalar.h"
+#include "SkMath.h"
+
+typedef int32_t SkFDot6;
+
+#define SK_FDot6One         (64)
+#define SK_FDot6Half        (32)
+
+#ifdef SK_DEBUG
+    inline SkFDot6 SkIntToFDot6(S16CPU x) {
+        SkASSERT(SkToS16(x) == x);
+        return x << 6;
+    }
+#else
+    #define SkIntToFDot6(x) ((x) << 6)
+#endif
+
+#define SkFDot6Floor(x)     ((x) >> 6)
+#define SkFDot6Ceil(x)      (((x) + 63) >> 6)
+#define SkFDot6Round(x)     (((x) + 32) >> 6)
+
+#define SkFixedToFDot6(x)   ((x) >> 10)
+
+inline SkFixed SkFDot6ToFixed(SkFDot6 x) {
+    SkASSERT((x << 10 >> 10) == x);
+
+    return x << 10;
+}
+
+#ifdef SK_SCALAR_IS_FLOAT
+    #define SkScalarToFDot6(x)  (SkFDot6)((x) * 64)
+    #define SkFDot6ToScalar(x)  ((SkScalar)(x) * SkFloatToScalar(0.015625f))
+#else
+    #define SkScalarToFDot6(x)  ((x) >> 10)
+    #define SkFDot6ToScalar(x)  ((x) << 10)
+#endif
+
+inline SkFixed SkFDot6Div(SkFDot6 a, SkFDot6 b) {
+    SkASSERT(b != 0);
+
+    if (a == (int16_t)a) {
+        return (a << 16) / b;
+    } else {
+        return SkFixedDiv(a, b);
+    }
+}
+
+#endif
diff --git a/src/core/SkFilterProc.cpp b/src/core/SkFilterProc.cpp
index 8c24bb6..2903849 100644
--- a/src/core/SkFilterProc.cpp
+++ b/src/core/SkFilterProc.cpp
@@ -156,14 +156,14 @@
 }
 static uint32_t bilerptr01(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
     uint32_t c00 = *a00;
-    uint32_t c01 = *a01;   
+    uint32_t c01 = *a01;
     uint32_t lo = (3 * LO_PAIR(c00) + LO_PAIR(c01)) >> 2;
     uint32_t hi = (3 * HI_PAIR(c00) + HI_PAIR(c01)) >> 2;
     return COMBINE(lo, hi);
 }
 static uint32_t bilerptr02(const uint32_t* a00, const uint32_t* a01, const uint32_t* a10, const uint32_t* a11) {
     uint32_t c00 = *a00;
-    uint32_t c01 = *a01;   
+    uint32_t c01 = *a01;
     uint32_t lo = (LO_PAIR(c00) + LO_PAIR(c01)) >> 1;
     uint32_t hi = (HI_PAIR(c00) + HI_PAIR(c01)) >> 1;
     return COMBINE(lo, hi);
diff --git a/src/core/SkFilterProc.h b/src/core/SkFilterProc.h
index 2b515e2..3b20d59 100644
--- a/src/core/SkFilterProc.h
+++ b/src/core/SkFilterProc.h
@@ -22,7 +22,7 @@
                                             SkFixed x, SkFixed y)
 {
     SkASSERT(table);
-    
+
     // convert to dot 2
     x = (unsigned)(x << 16) >> 30;
     y = (unsigned)(y << 16) >> 30;
@@ -33,7 +33,7 @@
                                               unsigned x, unsigned y)
 {
     SkASSERT(table);
-    
+
     // extract low 2 bits
     x = x << 30 >> 30;
     y = y << 30 >> 30;
@@ -51,7 +51,7 @@
 inline SkFilterProc SkGetBilinearFilterProc22RowProc(const SkFilterProc* row,
                                                      unsigned x)
 {
-    SkASSERT(row);    
+    SkASSERT(row);
     // extract low 2 bits
     return row[x << 30 >> 30];
 }
@@ -67,7 +67,7 @@
                                           unsigned x, unsigned y)
 {
     SkASSERT(table);
-    
+
     // extract low 2 bits
     x = x << 30 >> 30;
     y = y << 30 >> 30;
@@ -133,5 +133,3 @@
 }
 
 #endif
-
-
diff --git a/src/core/SkFilterShader.h b/src/core/SkFilterShader.h
new file mode 100644
index 0000000..be19640
--- /dev/null
+++ b/src/core/SkFilterShader.h
@@ -0,0 +1,41 @@
+/*
+ * 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 SkFilterShader_DEFINED
+#define SkFilterShader_DEFINED
+
+#include "SkShader.h"
+
+class SkColorFilter;
+
+class SkFilterShader : public SkShader {
+public:
+    SkFilterShader(SkShader* shader, SkColorFilter* filter);
+    virtual ~SkFilterShader();
+
+    virtual uint32_t getFlags() SK_OVERRIDE;
+    virtual bool setContext(const SkBitmap&, const SkPaint&,
+                            const SkMatrix&) SK_OVERRIDE;
+    virtual void endContext() SK_OVERRIDE;
+    virtual void shadeSpan(int x, int y, SkPMColor[], int count) SK_OVERRIDE;
+    virtual void shadeSpan16(int x, int y, uint16_t[], int count) SK_OVERRIDE;
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkFilterShader)
+
+protected:
+    SkFilterShader(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    SkShader*       fShader;
+    SkColorFilter*  fFilter;
+
+    typedef SkShader INHERITED;
+};
+
+#endif
diff --git a/src/core/SkFlate.cpp b/src/core/SkFlate.cpp
index a5e15d1..8258cdc 100644
--- a/src/core/SkFlate.cpp
+++ b/src/core/SkFlate.cpp
@@ -11,7 +11,7 @@
 #include "SkFlate.h"
 #include "SkStream.h"
 
-#ifndef SK_ZLIB_INCLUDE
+#ifndef SK_HAS_ZLIB
 bool SkFlate::HaveFlate() { return false; }
 bool SkFlate::Deflate(SkStream*, SkWStream*) { return false; }
 bool SkFlate::Deflate(const void*, size_t, SkWStream*) { return false; }
@@ -26,7 +26,11 @@
 
 namespace {
 
+#ifdef SK_SYSTEM_ZLIB
+#include <zlib.h>
+#else
 #include SK_ZLIB_INCLUDE
+#endif
 
 // static
 const size_t kBufferSize = 1024;
@@ -134,4 +138,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkFlattenable.cpp b/src/core/SkFlattenable.cpp
index 131cf4f..b4efe91 100644
--- a/src/core/SkFlattenable.cpp
+++ b/src/core/SkFlattenable.cpp
@@ -6,38 +6,13 @@
  * found in the LICENSE file.
  */
 #include "SkFlattenable.h"
-#include "SkTypeface.h"
+#include "SkPtrRecorder.h"
 
-#include "SkMatrix.h"
-#include "SkRegion.h"
-
-void SkReadMatrix(SkReader32* reader, SkMatrix* matrix) {
-    size_t size = matrix->unflatten(reader->peek());
-    SkASSERT(SkAlign4(size) == size);
-    (void)reader->skip(size);
-}
-
-void SkWriteMatrix(SkWriter32* writer, const SkMatrix& matrix) {
-    size_t size = matrix.flatten(NULL);
-    SkASSERT(SkAlign4(size) == size);
-    matrix.flatten(writer->reserve(size));
-}
-
-void SkReadRegion(SkReader32* reader, SkRegion* rgn) {
-    size_t size = rgn->unflatten(reader->peek());
-    SkASSERT(SkAlign4(size) == size);
-    (void)reader->skip(size);
-}
-
-void SkWriteRegion(SkWriter32* writer, const SkRegion& rgn) {
-    size_t size = rgn.flatten(NULL);
-    SkASSERT(SkAlign4(size) == size);
-    rgn.flatten(writer->reserve(size));
-}
+SK_DEFINE_INST_COUNT(SkFlattenable)
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkFlattenable::flatten(SkFlattenableWriteBuffer&)
+void SkFlattenable::flatten(SkFlattenableWriteBuffer&) const
 {
     /*  we don't write anything at the moment, but this allows our subclasses
         to not know that, since we want them to always call INHERITED::flatten()
@@ -46,300 +21,27 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
 
-SkFlattenableReadBuffer::SkFlattenableReadBuffer() {
-    fRCArray = NULL;
-    fRCCount = 0;
+SkNamedFactorySet::SkNamedFactorySet() : fNextAddedFactory(0) {}
 
-    fTFArray = NULL;
-    fTFCount = 0;
-
-    fFactoryTDArray = NULL;
-    fFactoryArray = NULL;
-    fFactoryCount = 0;
-    fPictureVersion = PICTURE_VERSION_JB;
-}
-
-SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data) :
-        INHERITED(data, 1024 * 1024) {
-    fRCArray = NULL;
-    fRCCount = 0;
-
-    fTFArray = NULL;
-    fTFCount = 0;
-
-    fFactoryTDArray = NULL;
-    fFactoryArray = NULL;
-    fFactoryCount = 0;
-    fPictureVersion = PICTURE_VERSION_JB;
-}
-
-SkFlattenableReadBuffer::SkFlattenableReadBuffer(const void* data, size_t size)
-        : INHERITED(data, size) {
-    fRCArray = NULL;
-    fRCCount = 0;
-
-    fTFArray = NULL;
-    fTFCount = 0;
-
-    fFactoryTDArray = NULL;
-    fFactoryArray = NULL;
-    fFactoryCount = 0;
-    fPictureVersion = PICTURE_VERSION_JB;
-}
-
-SkTypeface* SkFlattenableReadBuffer::readTypeface() {
-    uint32_t index = this->readU32();
-    if (0 == index || index > (unsigned)fTFCount) {
-        if (index) {
-            SkDebugf("====== typeface index %d\n", index);
-        }
-        return NULL;
-    } else {
-        SkASSERT(fTFArray);
-        return fTFArray[index - 1];
+uint32_t SkNamedFactorySet::find(SkFlattenable::Factory factory) {
+    uint32_t index = fFactorySet.find(factory);
+    if (index > 0) {
+        return index;
     }
-}
-
-SkRefCnt* SkFlattenableReadBuffer::readRefCnt() {
-    uint32_t index = this->readU32();
-    if (0 == index || index > (unsigned)fRCCount) {
-        return NULL;
-    } else {
-        SkASSERT(fRCArray);
-        return fRCArray[index - 1];
+    const char* name = SkFlattenable::FactoryToName(factory);
+    if (NULL == name) {
+        return 0;
     }
+    *fNames.append() = name;
+    return fFactorySet.add(factory);
 }
 
-SkFlattenable* SkFlattenableReadBuffer::readFlattenable() {
-
-    if(fPictureVersion == PICTURE_VERSION_ICS) {
-        SkFlattenable::Factory factory = NULL;
-
-        if (fFactoryCount > 0) {
-            uint32_t index = this->readU32();
-            if (index > 0) {
-                index -= 1;
-                SkASSERT(index < (unsigned)fFactoryCount);
-                factory = fFactoryArray[index];
-                // if we recorded an index, but failed to get a factory, we need
-                // to skip the flattened data in the buffer
-                if (NULL == factory) {
-                    uint32_t size = this->readU32();
-                    this->skip(size);
-                    // fall through and return NULL for the object
-                }
-            }
-        } else {
-            factory = (SkFlattenable::Factory)readFunctionPtr();
-        }
-
-        SkFlattenable* obj = NULL;
-        if (factory) {
-            uint32_t sizeRecorded = this->readU32();
-            uint32_t offset = this->offset();
-            obj = (*factory)(*this);
-            // check that we read the amount we expected
-            uint32_t sizeRead = this->offset() - offset;
-            if (sizeRecorded != sizeRead) {
-                // we could try to fix up the offset...
-                sk_throw();
-            }
-        }
-        return obj;
+const char* SkNamedFactorySet::getNextAddedFactoryName() {
+    if (fNextAddedFactory < fNames.count()) {
+        return fNames[fNextAddedFactory++];
     }
-
-    SkFlattenable::Factory factory = NULL;
-
-    if (fFactoryCount > 0) {
-        int32_t index = this->readU32();
-        if (0 == index) {
-            return NULL; // writer failed to give us the flattenable
-        }
-        index = -index; // we stored the negative of the index
-        index -= 1;     // we stored the index-base-1
-        SkASSERT(index < fFactoryCount);
-        factory = fFactoryArray[index];
-    } else if (fFactoryTDArray) {
-        const int32_t* peek = (const int32_t*)this->peek();
-        if (*peek <= 0) {
-            int32_t index = this->readU32();
-            if (0 == index) {
-                return NULL; // writer failed to give us the flattenable
-            }
-            index = -index; // we stored the negative of the index
-            index -= 1;     // we stored the index-base-1
-            factory = (*fFactoryTDArray)[index];
-        } else {
-            const char* name = this->readString();
-            factory = SkFlattenable::NameToFactory(name);
-            if (factory) {
-                SkASSERT(fFactoryTDArray->find(factory) < 0);
-                *fFactoryTDArray->append() = factory;
-            } else {
-//                SkDebugf("can't find factory for [%s]\n", name);
-            }
-            // if we didn't find a factory, that's our failure, not the writer's,
-            // so we fall through, so we can skip the sizeRecorded data.
-        }
-    } else {
-        factory = (SkFlattenable::Factory)readFunctionPtr();
-        if (NULL == factory) {
-            return NULL; // writer failed to give us the flattenable
-        }
-    }
-
-    // if we get here, factory may still be null, but if that is the case, the
-    // failure was ours, not the writer.
-    SkFlattenable* obj = NULL;
-    uint32_t sizeRecorded = this->readU32();
-    if (factory) {
-        uint32_t offset = this->offset();
-        obj = (*factory)(*this);
-        // check that we read the amount we expected
-        uint32_t sizeRead = this->offset() - offset;
-        if (sizeRecorded != sizeRead) {
-            // we could try to fix up the offset...
-            sk_throw();
-        }
-    } else {
-        // we must skip the remaining data
-        this->skip(sizeRecorded);
-    }
-    return obj;
-}
-
-void* SkFlattenableReadBuffer::readFunctionPtr() {
-    void* proc;
-    this->read(&proc, sizeof(proc));
-    return proc;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkFlattenableWriteBuffer::SkFlattenableWriteBuffer(size_t minSize) :
-        INHERITED(minSize) {
-    fFlags = (Flags)0;
-    fRCSet = NULL;
-    fTFSet = NULL;
-    fFactorySet = NULL;
-}
-
-SkFlattenableWriteBuffer::~SkFlattenableWriteBuffer() {
-    SkSafeUnref(fRCSet);
-    SkSafeUnref(fTFSet);
-    SkSafeUnref(fFactorySet);
-}
-
-SkRefCntSet* SkFlattenableWriteBuffer::setRefCntRecorder(SkRefCntSet* rec) {
-    SkRefCnt_SafeAssign(fRCSet, rec);
-    return rec;
-}
-
-SkRefCntSet* SkFlattenableWriteBuffer::setTypefaceRecorder(SkRefCntSet* rec) {
-    SkRefCnt_SafeAssign(fTFSet, rec);
-    return rec;
-}
-
-SkFactorySet* SkFlattenableWriteBuffer::setFactoryRecorder(SkFactorySet* rec) {
-    SkRefCnt_SafeAssign(fFactorySet, rec);
-    return rec;
-}
-
-void SkFlattenableWriteBuffer::writeTypeface(SkTypeface* obj) {
-    if (NULL == obj || NULL == fTFSet) {
-        this->write32(0);
-    } else {
-        this->write32(fTFSet->add(obj));
-    }
-}
-
-void SkFlattenableWriteBuffer::writeRefCnt(SkRefCnt* obj) {
-    if (NULL == obj || NULL == fRCSet) {
-        this->write32(0);
-    } else {
-        this->write32(fRCSet->add(obj));
-    }
-}
-
-void SkFlattenableWriteBuffer::writeFlattenable(SkFlattenable* flattenable) {
-    /*
-     *  If we have a factoryset, then the first 32bits tell us...
-     *       0: failure to write the flattenable
-     *      <0: we store the negative of the (1-based) index
-     *      >0: the length of the name
-     *  If we don't have a factoryset, then the first "ptr" is either the
-     *  factory, or null for failure.
-     *
-     *  The distinction is important, since 0-index is 32bits (always), but a
-     *  0-functionptr might be 32 or 64 bits.
-     */
-
-    SkFlattenable::Factory factory = NULL;
-    if (flattenable) {
-        factory = flattenable->getFactory();
-    }
-    if (NULL == factory) {
-        if (fFactorySet) {
-            this->write32(0);
-        } else {
-            this->writeFunctionPtr(NULL);
-        }
-        return;
-    }
-
-    /*
-     *  We can write 1 of 3 versions of the flattenable:
-     *  1.  function-ptr : this is the fastest for the reader, but assumes that
-     *      the writer and reader are in the same process.
-     *  2.  index into fFactorySet : This is assumes the writer will later
-     *      resolve the function-ptrs into strings for its reader. SkPicture
-     *      does exactly this, by writing a table of names (matching the indices)
-     *      up front in its serialized form.
-     *  3.  names : Reuse fFactorySet to store indices, but only after we've
-     *      written the name the first time. SkGPipe uses this technique, as it
-     *      doesn't require the reader to be told to know the table of names
-     *      up front.
-     */
-    if (fFactorySet) {
-        if (this->inlineFactoryNames()) {
-            int index = fFactorySet->find(factory);
-            if (index) {
-                // we write the negative of the index, to distinguish it from
-                // the length of a string
-                this->write32(-index);
-            } else {
-                const char* name = SkFlattenable::FactoryToName(factory);
-                if (NULL == name) {
-                    this->write32(0);
-                    return;
-                }
-                this->writeString(name);
-                index = fFactorySet->add(factory);
-            }
-        } else {
-            // we write the negative of the index, to distinguish it from
-            // the length of a string
-            this->write32(-(int)fFactorySet->add(factory));
-        }
-    } else {
-        this->writeFunctionPtr((void*)factory);
-    }
-
-    // make room for the size of the flatttened object
-    (void)this->reserve(sizeof(uint32_t));
-    // record the current size, so we can subtract after the object writes.
-    uint32_t offset = this->size();
-    // now flatten the object
-    flattenable->flatten(*this);
-    uint32_t objSize = this->size() - offset;
-    // record the obj's size
-    *this->peek32(offset - sizeof(uint32_t)) = objSize;
-}
-
-void SkFlattenableWriteBuffer::writeFunctionPtr(void* proc) {
-    *(void**)this->reserve(sizeof(void*)) = proc;
+    return NULL;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -361,7 +63,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
-#define MAX_PAIR_COUNT  64
+#define MAX_PAIR_COUNT  1024
 
 struct Pair {
     const char*             fName;
@@ -388,7 +90,7 @@
     gCount += 1;
 }
 
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS && defined(SK_DEBUG)
+#ifdef SK_DEBUG
 static void report_no_entries(const char* functionName) {
     if (!gCount) {
         SkDebugf("%s has no registered name/factory pairs."
@@ -399,7 +101,7 @@
 #endif
 
 SkFlattenable::Factory SkFlattenable::NameToFactory(const char name[]) {
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS && defined(SK_DEBUG)
+#ifdef SK_DEBUG
     report_no_entries(__FUNCTION__);
 #endif
     const Pair* pairs = gPairs;
@@ -412,7 +114,7 @@
 }
 
 const char* SkFlattenable::FactoryToName(Factory fact) {
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS && defined(SK_DEBUG)
+#ifdef SK_DEBUG
     report_no_entries(__FUNCTION__);
 #endif
     const Pair* pairs = gPairs;
@@ -423,7 +125,3 @@
     }
     return NULL;
 }
-
-bool SkFlattenable::toDumpString(SkString* str) const {
-    return false;
-}
diff --git a/src/core/SkFlattenableBuffers.cpp b/src/core/SkFlattenableBuffers.cpp
new file mode 100644
index 0000000..50a47d5
--- /dev/null
+++ b/src/core/SkFlattenableBuffers.cpp
@@ -0,0 +1,56 @@
+
+/*
+ * 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 "SkFlattenableBuffers.h"
+#include "SkPaint.h"
+#include "SkTypeface.h"
+
+SkFlattenableReadBuffer::SkFlattenableReadBuffer() {
+    // Set default values. These should be explicitly set by our client
+    // via setFlags() if the buffer came from serialization.
+    fFlags = 0;
+#ifdef SK_SCALAR_IS_FLOAT
+    fFlags |= kScalarIsFloat_Flag;
+#endif
+    if (8 == sizeof(void*)) {
+        fFlags |= kPtrIs64Bit_Flag;
+    }
+}
+
+SkFlattenableReadBuffer::~SkFlattenableReadBuffer() { }
+
+void* SkFlattenableReadBuffer::readFunctionPtr() {
+    void* proc;
+    SkASSERT(sizeof(void*) == this->getArrayCount());
+    this->readByteArray(&proc);
+    return proc;
+}
+
+void SkFlattenableReadBuffer::readPaint(SkPaint* paint) {
+    paint->unflatten(*this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlattenableWriteBuffer::SkFlattenableWriteBuffer() {
+    fFlags = (Flags)0;
+}
+
+SkFlattenableWriteBuffer::~SkFlattenableWriteBuffer() { }
+
+void SkFlattenableWriteBuffer::writeFunctionPtr(void* ptr) {
+    void* ptrStorage[] = { ptr };
+    this->writeByteArray(ptrStorage, sizeof(void*));
+}
+
+void SkFlattenableWriteBuffer::writePaint(const SkPaint& paint) {
+    paint.flatten(*this);
+}
+
+void SkFlattenableWriteBuffer::flattenObject(SkFlattenable* obj, SkFlattenableWriteBuffer& buffer) {
+    obj->flatten(buffer);
+}
diff --git a/src/core/SkFloat.cpp b/src/core/SkFloat.cpp
index ffa5d9a..b5cc4f1 100644
--- a/src/core/SkFloat.cpp
+++ b/src/core/SkFloat.cpp
@@ -8,7 +8,7 @@
 
 
 #include "SkFloat.h"
-#include "SkMath.h"
+#include "SkMathPriv.h"
 
 #define EXP_BIAS    (127+23)
 
@@ -268,9 +268,7 @@
 #ifdef SK_DEBUG
 
 #include "SkRandom.h"
-#ifdef SK_CAN_USE_FLOAT
-    #include "SkFloatingPoint.h"
-#endif
+#include "SkFloatingPoint.h"
 
 void SkFloat::UnitTest()
 {
@@ -291,11 +289,10 @@
     SkASSERT(n == -3);
 
     d.setAdd(c, b);
-    SkDebugf("SkFloat: %d + %d = %d\n", c.getInt(), b.getInt(), d.getInt());    
+    SkDebugf("SkFloat: %d + %d = %d\n", c.getInt(), b.getInt(), d.getInt());
 
     SkRandom    rand;
 
-#ifdef SK_CAN_USE_FLOAT
     int i;
     for (i = 0; i < 1000; i++)
     {
@@ -391,7 +388,6 @@
 #endif
     }
 #endif
-#endif
 }
 
 #endif
diff --git a/src/core/SkFloat.h b/src/core/SkFloat.h
index fe41c27..74cd19e 100644
--- a/src/core/SkFloat.h
+++ b/src/core/SkFloat.h
@@ -69,7 +69,7 @@
             float   fFloat;
             int32_t fPacked;
         } tmp;
-        
+
         tmp.fFloat = f;
         int d = tmp.fPacked - fPacked;
         SkASSERT(SkAbs32(d) <= tolerance);
@@ -80,7 +80,7 @@
             float   fFloat;
             int32_t fPacked;
         } tmp;
-        
+
         tmp.fPacked = fPacked;
         return tmp.fFloat;
     }
diff --git a/src/core/SkFloatBits.cpp b/src/core/SkFloatBits.cpp
index fc46005..39b51ab 100644
--- a/src/core/SkFloatBits.cpp
+++ b/src/core/SkFloatBits.cpp
@@ -1,12 +1,12 @@
-
 /*
  * 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 "SkFloatBits.h"
-#include "SkMath.h"
+#include "SkMathPriv.h"
 
 /******************************************************************************
     SkFloatBits_toInt[Floor, Round, Ceil] are identical except for what they
@@ -45,7 +45,7 @@
 int32_t SkFloatBits_toIntCast(int32_t packed) {
     int exp = unpack_exp(packed) - EXP_BIAS;
     int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
-    
+
     if (exp >= 0) {
         if (exp > 7) {    // overflow
             value = SK_MaxS32;
@@ -68,7 +68,7 @@
     if ((packed << 1) == 0) {
         return 0;
     }
-    
+
     int exp = unpack_exp(packed) - EXP_BIAS;
     int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
 
@@ -98,10 +98,10 @@
     if ((packed << 1) == 0) {
         return 0;
     }
-    
+
     int exp = unpack_exp(packed) - EXP_BIAS;
     int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
-    
+
     if (exp >= 0) {
         if (exp > 7) {    // overflow
             value = SK_MaxS32;
@@ -128,10 +128,10 @@
     if ((packed << 1) == 0) {
         return 0;
     }
-    
+
     int exp = unpack_exp(packed) - EXP_BIAS;
     int value = unpack_matissa_dirty(packed) | MATISSA_MAGIC_BIG;
-    
+
     if (exp >= 0) {
         if (exp > 7) {    // overflow
             value = SK_MaxS32;
@@ -152,19 +152,17 @@
     }
 }
 
-#ifdef SK_CAN_USE_FLOAT
-
 float SkIntToFloatCast(int32_t value) {
     if (0 == value) {
         return 0;
     }
 
     int shift = EXP_BIAS;
-    
+
     // record the sign and make value positive
     int sign = SkExtractSign(value);
     value = SkApplySign(value, sign);
-    
+
     if (value >> 24) {    // value is too big (has more than 24 bits set)
         int bias = 8 - SkCLZ(value);
         SkDebugf("value = %d, bias = %d\n", value, bias);
@@ -177,11 +175,11 @@
         value <<= zeros;
         shift -= zeros;
     }
-    
+
     // now value is left-aligned to 24 bits
     SkASSERT((value >> 23) == 1);
     SkASSERT(shift >= 0 && shift <= 255);
-    
+
     SkFloatIntUnion data;
     data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
     return data.fFloat;
@@ -193,18 +191,16 @@
     }
 
     int shift = EXP_BIAS;
-    
+
     // record the sign and make value positive
     int sign = SkExtractSign(value);
     value = SkApplySign(value, sign);
-    
+
     int zeros = SkCLZ(value << 8);
     value <<= zeros;
     shift -= zeros;
-    
+
     SkFloatIntUnion data;
     data.fSignBitInt = (sign << 31) | (shift << 23) | (value & ~MATISSA_MAGIC_BIG);
     return data.fFloat;
 }
-
-#endif
diff --git a/src/core/SkFontDescriptor.cpp b/src/core/SkFontDescriptor.cpp
new file mode 100644
index 0000000..7679d92
--- /dev/null
+++ b/src/core/SkFontDescriptor.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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 "SkFontDescriptor.h"
+#include "SkStream.h"
+
+enum {
+    // these must match the sfnt 'name' enums
+    kFontFamilyName = 0x01,
+    kFullName       = 0x04,
+    kPostscriptName = 0x06,
+
+    // These count backwards from 0xFF, so as not to collide with the SFNT
+    // defines for names in its 'name' table.
+    kFontFileName   = 0xFE,
+    kSentinel       = 0xFF,
+};
+
+SkFontDescriptor::SkFontDescriptor(SkTypeface::Style style) {
+    fStyle = style;
+}
+
+static void read_string(SkStream* stream, SkString* string) {
+    const uint32_t length = stream->readPackedUInt();
+    if (length > 0) {
+        string->resize(length);
+        stream->read(string->writable_str(), length);
+    }
+}
+
+static void write_string(SkWStream* stream, const SkString& string,
+                         uint32_t id) {
+    if (!string.isEmpty()) {
+        stream->writePackedUInt(id);
+        stream->writePackedUInt(string.size());
+        stream->write(string.c_str(), string.size());
+    }
+}
+
+SkFontDescriptor::SkFontDescriptor(SkStream* stream) {
+    fStyle = (SkTypeface::Style)stream->readPackedUInt();
+
+    for (;;) {
+        switch (stream->readPackedUInt()) {
+            case kFontFamilyName:
+                read_string(stream, &fFamilyName);
+                break;
+            case kFullName:
+                read_string(stream, &fFullName);
+                break;
+            case kPostscriptName:
+                read_string(stream, &fPostscriptName);
+                break;
+            case kFontFileName:
+                read_string(stream, &fFontFileName);
+                break;
+            case kSentinel:
+                return;
+            default:
+                SkDEBUGFAIL("Unknown id used by a font descriptor");
+                return;
+        }
+    }
+}
+
+void SkFontDescriptor::serialize(SkWStream* stream) {
+    stream->writePackedUInt(fStyle);
+
+    write_string(stream, fFamilyName, kFontFamilyName);
+    write_string(stream, fFullName, kFullName);
+    write_string(stream, fPostscriptName, kPostscriptName);
+    write_string(stream, fFontFileName, kFontFileName);
+
+    stream->writePackedUInt(kSentinel);
+}
diff --git a/src/core/SkFontDescriptor.h b/src/core/SkFontDescriptor.h
new file mode 100644
index 0000000..5febfd8
--- /dev/null
+++ b/src/core/SkFontDescriptor.h
@@ -0,0 +1,46 @@
+/*
+ * 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 SkFontDescriptor_DEFINED
+#define SkFontDescriptor_DEFINED
+
+#include "SkString.h"
+#include "SkTypeface.h"
+
+class SkStream;
+class SkWStream;
+
+class SkFontDescriptor {
+public:
+    SkFontDescriptor(SkTypeface::Style = SkTypeface::kNormal);
+    SkFontDescriptor(SkStream*);
+
+    void serialize(SkWStream*);
+
+    SkTypeface::Style getStyle() { return fStyle; }
+    void setStyle(SkTypeface::Style style) { fStyle = style; }
+
+    const char* getFamilyName() { return fFamilyName.c_str(); }
+    const char* getFullName() { return fFullName.c_str(); }
+    const char* getPostscriptName() { return fPostscriptName.c_str(); }
+    const char* getFontFileName() { return fFontFileName.c_str(); }
+
+    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); }
+
+private:
+    SkString fFamilyName;
+    SkString fFullName;
+    SkString fPostscriptName;
+    SkString fFontFileName;
+
+    SkTypeface::Style fStyle;
+};
+
+#endif // SkFontDescriptor_DEFINED
diff --git a/src/core/SkGeometry.cpp b/src/core/SkGeometry.cpp
index de86827..1ca40be 100644
--- a/src/core/SkGeometry.cpp
+++ b/src/core/SkGeometry.cpp
@@ -204,7 +204,7 @@
     return SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
 #else
     SkScalar    ab = SkScalarInterp(src[0], src[2], t);
-    SkScalar    bc = SkScalarInterp(src[2], src[4], t); 
+    SkScalar    bc = SkScalarInterp(src[2], src[4], t);
     return SkScalarInterp(ab, bc, t);
 #endif
 }
@@ -316,7 +316,7 @@
 {
     SkASSERT(src);
     SkASSERT(dst);
-    
+
 #if 0
     static bool once = true;
     if (once)
@@ -324,16 +324,16 @@
         once = false;
         SkPoint s[3] = { 0, 26398, 0, 26331, 0, 20621428 };
         SkPoint d[6];
-        
+
         int n = SkChopQuadAtYExtrema(s, d);
         SkDebugf("chop=%d, Y=[%x %x %x %x %x %x]\n", n, d[0].fY, d[1].fY, d[2].fY, d[3].fY, d[4].fY, d[5].fY);
     }
 #endif
-    
+
     SkScalar a = src[0].fY;
     SkScalar b = src[1].fY;
     SkScalar c = src[2].fY;
-    
+
     if (is_not_monotonic(a, b, c))
     {
         SkScalar    tValue;
@@ -360,11 +360,11 @@
 {
     SkASSERT(src);
     SkASSERT(dst);
-    
+
     SkScalar a = src[0].fX;
     SkScalar b = src[1].fX;
     SkScalar c = src[2].fX;
-    
+
     if (is_not_monotonic(a, b, c)) {
         SkScalar tValue;
         if (valid_unit_divide(a - b, a - b - b + c, &tValue)) {
@@ -592,7 +592,7 @@
 }
 
 /*  http://code.google.com/p/skia/issues/detail?id=32
- 
+
     This test code would fail when we didn't check the return result of
     valid_unit_divide in SkChopCubicAt(... tValues[], int roots). The reason is
     that after the first chop, the parameters to valid_unit_divide are equal
@@ -699,7 +699,7 @@
     SkScalar    tValues[2];
     int         roots = SkFindCubicExtrema(src[0].fY, src[1].fY, src[2].fY,
                                            src[3].fY, tValues);
-    
+
     SkChopCubicAt(src, dst, tValues, roots);
     if (dst && roots > 0) {
         // we do some cleanup to ensure our Y extrema are flat
@@ -715,7 +715,7 @@
     SkScalar    tValues[2];
     int         roots = SkFindCubicExtrema(src[0].fX, src[1].fX, src[2].fX,
                                            src[3].fX, tValues);
-    
+
     SkChopCubicAt(src, dst, tValues, roots);
     if (dst && roots > 0) {
         // we do some cleanup to ensure our Y extrema are flat
@@ -839,7 +839,6 @@
  *  keeping the existing sorting, and return the new count
  */
 static int collaps_duplicates(float array[], int count) {
-    int n = count;
     for (int n = count; n > 1; --n) {
         if (array[0] == array[1]) {
             for (int i = 1; i < n; ++i) {
@@ -900,7 +899,7 @@
 /*  Solve coeff(t) == 0, returning the number of roots that
     lie withing 0 < t < 1.
     coeff[0]t^3 + coeff[1]t^2 + coeff[2]t + coeff[3]
- 
+
     Eliminates repeated roots (so that all tValues are distinct, and are always
     in increasing order.
 */
@@ -985,7 +984,7 @@
 }
 
 /*  Looking for F' dot F'' == 0
-    
+
     A = b - a
     B = c - 2b + a
     C = d - 3c + 3b - a
@@ -1017,7 +1016,7 @@
 #define kMinTValueForChopping   0
 
 /*  Looking for F' dot F'' == 0
-    
+
     A = b - a
     B = c - 2b + a
     C = d - 3c + 3b - a
@@ -1239,7 +1238,7 @@
             catch the >= 1 roots (which given our caller, will basically mean
             a root of 1, give-or-take numerical instability). If we are in the
             >= 1 case, return the existing offCurve point.
-        
+
             The test below checks to see if we are close to the "end" of the
             curve (near base[4]). Rather than specifying a tolerance, I just
             check to see if value is on to the right/left of the middle point
@@ -1255,28 +1254,52 @@
     return false;
 }
 
+#ifdef SK_SCALAR_IS_FLOAT
+// Due to floating point issues (i.e., 1.0f - SK_ScalarRoot2Over2 !=
+// SK_ScalarRoot2Over2 - SK_ScalarTanPIOver8) a cruder root2over2
+// approximation is required to make the quad circle points convex. The
+// root of the problem is that with the root2over2 value in SkScalar.h
+// the arcs really are ever so slightly concave. Some alternative fixes
+// to this problem (besides just arbitrarily pushing out the mid-point as
+// is done here) are:
+//    Adjust all the points (not just the middle) to both better approximate
+//             the curve and remain convex
+//    Switch over to using cubics rather then quads
+//    Use a different method to create the mid-point (e.g., compute
+//             the two side points, average them, then move it out as needed
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
+    #define SK_ScalarRoot2Over2_QuadCircle    0.7072f
+#else
+    #define SK_ScalarRoot2Over2_QuadCircle    SK_ScalarRoot2Over2
+#endif
+
+#else
+    #define SK_ScalarRoot2Over2_QuadCircle    SK_FixedRoot2Over2
+#endif
+
+
 static const SkPoint gQuadCirclePts[kSkBuildQuadArcStorage] = {
-    { SK_Scalar1,           0               },
-    { SK_Scalar1,           SK_ScalarTanPIOver8 },
-    { SK_ScalarRoot2Over2,  SK_ScalarRoot2Over2 },
-    { SK_ScalarTanPIOver8,  SK_Scalar1          },
+    { SK_Scalar1,                      0                                  },
+    { SK_Scalar1,                      SK_ScalarTanPIOver8                },
+    { SK_ScalarRoot2Over2_QuadCircle,  SK_ScalarRoot2Over2_QuadCircle     },
+    { SK_ScalarTanPIOver8,             SK_Scalar1                         },
 
-    { 0,                    SK_Scalar1      },
-    { -SK_ScalarTanPIOver8, SK_Scalar1  },
-    { -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2 },
-    { -SK_Scalar1,          SK_ScalarTanPIOver8 },
+    { 0,                               SK_Scalar1                         },
+    { -SK_ScalarTanPIOver8,            SK_Scalar1                         },
+    { -SK_ScalarRoot2Over2_QuadCircle, SK_ScalarRoot2Over2_QuadCircle     },
+    { -SK_Scalar1,                     SK_ScalarTanPIOver8                },
 
-    { -SK_Scalar1,          0               },
-    { -SK_Scalar1,          -SK_ScalarTanPIOver8    },
-    { -SK_ScalarRoot2Over2, -SK_ScalarRoot2Over2    },
-    { -SK_ScalarTanPIOver8, -SK_Scalar1     },
+    { -SK_Scalar1,                     0                                  },
+    { -SK_Scalar1,                     -SK_ScalarTanPIOver8               },
+    { -SK_ScalarRoot2Over2_QuadCircle, -SK_ScalarRoot2Over2_QuadCircle    },
+    { -SK_ScalarTanPIOver8,            -SK_Scalar1                        },
 
-    { 0,                    -SK_Scalar1     },
-    { SK_ScalarTanPIOver8,  -SK_Scalar1     },
-    { SK_ScalarRoot2Over2,  -SK_ScalarRoot2Over2    },
-    { SK_Scalar1,           -SK_ScalarTanPIOver8    },
+    { 0,                               -SK_Scalar1                        },
+    { SK_ScalarTanPIOver8,             -SK_Scalar1                        },
+    { SK_ScalarRoot2Over2_QuadCircle,  -SK_ScalarRoot2Over2_QuadCircle    },
+    { SK_Scalar1,                      -SK_ScalarTanPIOver8               },
 
-    { SK_Scalar1,           0   }
+    { SK_Scalar1,                      0                                  }
 };
 
 int SkBuildQuadArc(const SkVector& uStart, const SkVector& uStop,
@@ -1298,7 +1321,7 @@
     if (absY <= SK_ScalarNearlyZero && x > 0 &&
         ((y >= 0 && kCW_SkRotationDirection == dir) ||
          (y <= 0 && kCCW_SkRotationDirection == dir))) {
-            
+
         // just return the start-point
         quadPoints[0].set(SK_Scalar1, 0);
         pointCount = 1;
@@ -1360,4 +1383,3 @@
     matrix.mapPoints(quadPoints, pointCount);
     return pointCount;
 }
-
diff --git a/src/core/SkGlyph.h b/src/core/SkGlyph.h
new file mode 100644
index 0000000..649fa7d
--- /dev/null
+++ b/src/core/SkGlyph.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkGlyph_DEFINED
+#define SkGlyph_DEFINED
+
+#include "SkTypes.h"
+#include "SkFixed.h"
+#include "SkMask.h"
+
+class SkPath;
+
+// needs to be != to any valid SkMask::Format
+#define MASK_FORMAT_UNKNOWN         (0xFF)
+#define MASK_FORMAT_JUST_ADVANCE    MASK_FORMAT_UNKNOWN
+
+#define kMaxGlyphWidth (1<<13)
+
+struct SkGlyph {
+    void*       fImage;
+    SkPath*     fPath;
+    SkFixed     fAdvanceX, fAdvanceY;
+
+    uint32_t    fID;
+    uint16_t    fWidth, fHeight;
+    int16_t     fTop, fLeft;
+
+    uint8_t     fMaskFormat;
+    int8_t      fRsbDelta, fLsbDelta;  // used by auto-kerning
+
+    void init(uint32_t id) {
+        fID             = id;
+        fImage          = NULL;
+        fPath           = NULL;
+        fMaskFormat     = MASK_FORMAT_UNKNOWN;
+    }
+
+    /**
+     *  Compute the rowbytes for the specified width and mask-format.
+     */
+    static unsigned ComputeRowBytes(unsigned width, SkMask::Format format) {
+        unsigned rb = width;
+        if (SkMask::kBW_Format == format) {
+            rb = (rb + 7) >> 3;
+        } else if (SkMask::kARGB32_Format == format ||
+                   SkMask::kLCD32_Format == format)
+        {
+            rb <<= 2;
+        } else if (SkMask::kLCD16_Format == format) {
+            rb = SkAlign4(rb << 1);
+        } else {
+            rb = SkAlign4(rb);
+        }
+        return rb;
+    }
+
+    unsigned rowBytes() const {
+        return ComputeRowBytes(fWidth, (SkMask::Format)fMaskFormat);
+    }
+
+    bool isJustAdvance() const {
+        return MASK_FORMAT_JUST_ADVANCE == fMaskFormat;
+    }
+
+    bool isFullMetrics() const {
+        return MASK_FORMAT_JUST_ADVANCE != fMaskFormat;
+    }
+
+    uint16_t getGlyphID() const {
+        return ID2Code(fID);
+    }
+
+    unsigned getGlyphID(unsigned baseGlyphCount) const {
+        unsigned code = ID2Code(fID);
+        SkASSERT(code >= baseGlyphCount);
+        return code - baseGlyphCount;
+    }
+
+    unsigned getSubX() const {
+        return ID2SubX(fID);
+    }
+
+    SkFixed getSubXFixed() const {
+        return SubToFixed(ID2SubX(fID));
+    }
+
+    SkFixed getSubYFixed() const {
+        return SubToFixed(ID2SubY(fID));
+    }
+
+    size_t computeImageSize() const;
+
+    /** Call this to set all of the metrics fields to 0 (e.g. if the scaler
+        encounters an error measuring a glyph). Note: this does not alter the
+        fImage, fPath, fID, fMaskFormat fields.
+     */
+    void zeroMetrics();
+
+    enum {
+        kSubBits = 2,
+        kSubMask = ((1 << kSubBits) - 1),
+        kSubShift = 24, // must be large enough for glyphs and unichars
+        kCodeMask = ((1 << kSubShift) - 1),
+        // relative offsets for X and Y subpixel bits
+        kSubShiftX = kSubBits,
+        kSubShiftY = 0
+    };
+
+    static unsigned ID2Code(uint32_t id) {
+        return id & kCodeMask;
+    }
+
+    static unsigned ID2SubX(uint32_t id) {
+        return id >> (kSubShift + kSubShiftX);
+    }
+
+    static unsigned ID2SubY(uint32_t id) {
+        return (id >> (kSubShift + kSubShiftY)) & kSubMask;
+    }
+
+    static unsigned FixedToSub(SkFixed n) {
+        return (n >> (16 - kSubBits)) & kSubMask;
+    }
+
+    static SkFixed SubToFixed(unsigned sub) {
+        SkASSERT(sub <= kSubMask);
+        return sub << (16 - kSubBits);
+    }
+
+    static uint32_t MakeID(unsigned code) {
+        return code;
+    }
+
+    static uint32_t MakeID(unsigned code, SkFixed x, SkFixed y) {
+        SkASSERT(code <= kCodeMask);
+        x = FixedToSub(x);
+        y = FixedToSub(y);
+        return (x << (kSubShift + kSubShiftX)) |
+               (y << (kSubShift + kSubShiftY)) |
+               code;
+    }
+
+    void toMask(SkMask* mask) const;
+};
+
+#endif
diff --git a/src/core/SkGlyphCache.cpp b/src/core/SkGlyphCache.cpp
index 9f0dfba..2673aba 100644
--- a/src/core/SkGlyphCache.cpp
+++ b/src/core/SkGlyphCache.cpp
@@ -10,7 +10,9 @@
 #include "SkGlyphCache.h"
 #include "SkGraphics.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "SkTemplates.h"
+#include "SkTLS.h"
 
 //#define SPEW_PURGE_STATUS
 //#define USE_CACHE_HASH
@@ -312,7 +314,7 @@
             const_cast<SkGlyph&>(glyph).fPath = SkNEW(SkPath);
             fScalerContext->getPath(glyph, glyph.fPath);
             fMemoryUsed += sizeof(SkPath) +
-                    glyph.fPath->getPoints(NULL, 0x7FFFFFFF) * sizeof(SkPoint);
+                    glyph.fPath->countPoints() * sizeof(SkPoint);
         }
     }
     return glyph.fPath;
@@ -355,25 +357,6 @@
     fAuxProcList = rec;
 }
 
-void SkGlyphCache::removeAuxProc(void (*proc)(void*)) {
-    AuxProcRec* rec = fAuxProcList;
-    AuxProcRec* prev = NULL;
-    while (rec) {
-        AuxProcRec* next = rec->fNext;
-        if (rec->fProc == proc) {
-            if (prev) {
-                prev->fNext = next;
-            } else {
-                fAuxProcList = next;
-            }
-            SkDELETE(rec);
-            return;
-        }
-        prev = rec;
-        rec = next;
-    }
-}
-
 void SkGlyphCache::invokeAndRemoveAuxProcs() {
     AuxProcRec* rec = fAuxProcList;
     while (rec) {
@@ -387,6 +370,10 @@
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
+#ifndef SK_DEFAULT_FONT_CACHE_LIMIT
+    #define SK_DEFAULT_FONT_CACHE_LIMIT     (2 * 1024 * 1024)
+#endif
+
 #ifdef USE_CACHE_HASH
     #define HASH_BITCOUNT   6
     #define HASH_COUNT      (1 << HASH_BITCOUNT)
@@ -410,15 +397,34 @@
 
 class SkGlyphCache_Globals {
 public:
-    SkGlyphCache_Globals() {
+    enum UseMutex {
+        kNo_UseMutex,  // thread-local cache
+        kYes_UseMutex  // shared cache
+    };
+
+    SkGlyphCache_Globals(UseMutex um) {
         fHead = NULL;
         fTotalMemoryUsed = 0;
+        fFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT;
+        fMutex = (kYes_UseMutex == um) ? SkNEW(SkMutex) : NULL;
+
 #ifdef USE_CACHE_HASH
         sk_bzero(fHash, sizeof(fHash));
 #endif
     }
 
-    SkMutex         fMutex;
+    ~SkGlyphCache_Globals() {
+        SkGlyphCache* cache = fHead;
+        while (cache) {
+            SkGlyphCache* next = cache->fNext;
+            SkDELETE(cache);
+            cache = next;
+        }
+
+        SkDELETE(fMutex);
+    }
+
+    SkMutex*        fMutex;
     SkGlyphCache*   fHead;
     size_t          fTotalMemoryUsed;
 #ifdef USE_CACHE_HASH
@@ -430,14 +436,70 @@
 #else
     void validate() const {}
 #endif
+
+    size_t  getFontCacheLimit() const { return fFontCacheLimit; }
+    size_t  setFontCacheLimit(size_t limit);
+    void    purgeAll(); // does not change budget
+
+    // can return NULL
+    static SkGlyphCache_Globals* FindTLS() {
+        return (SkGlyphCache_Globals*)SkTLS::Find(CreateTLS);
+    }
+
+    static SkGlyphCache_Globals& GetTLS() {
+        return *(SkGlyphCache_Globals*)SkTLS::Get(CreateTLS, DeleteTLS);
+    }
+
+    static void DeleteTLS() { SkTLS::Delete(CreateTLS); }
+
+private:
+    size_t  fFontCacheLimit;
+
+    static void* CreateTLS() {
+        return SkNEW_ARGS(SkGlyphCache_Globals, (kNo_UseMutex));
+    }
+
+    static void DeleteTLS(void* ptr) {
+        SkDELETE((SkGlyphCache_Globals*)ptr);
+    }
 };
 
-static SkGlyphCache_Globals& getGlobals() {
+size_t SkGlyphCache_Globals::setFontCacheLimit(size_t newLimit) {
+    static const size_t minLimit = 256 * 1024;
+    if (newLimit < minLimit) {
+        newLimit = minLimit;
+    }
+
+    size_t prevLimit = fFontCacheLimit;
+    fFontCacheLimit = newLimit;
+
+    size_t currUsed = fTotalMemoryUsed;
+    if (currUsed > newLimit) {
+        SkAutoMutexAcquire    ac(fMutex);
+        SkGlyphCache::InternalFreeCache(this, currUsed - newLimit);
+    }
+    return prevLimit;
+}
+
+void SkGlyphCache_Globals::purgeAll() {
+    SkAutoMutexAcquire    ac(fMutex);
+    SkGlyphCache::InternalFreeCache(this, fTotalMemoryUsed);
+}
+
+// Returns the shared globals
+static SkGlyphCache_Globals& getSharedGlobals() {
     // we leak this, so we don't incur any shutdown cost of the destructor
-    static SkGlyphCache_Globals* gGlobals = new SkGlyphCache_Globals;
+    static SkGlyphCache_Globals* gGlobals = SkNEW_ARGS(SkGlyphCache_Globals,
+                                       (SkGlyphCache_Globals::kYes_UseMutex));
     return *gGlobals;
 }
 
+// Returns the TLS globals (if set), or the shared globals
+static SkGlyphCache_Globals& getGlobals() {
+    SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
+    return tls ? *tls : getSharedGlobals();
+}
+
 void SkGlyphCache::VisitAllCaches(bool (*proc)(SkGlyphCache*, void*),
                                   void* context) {
     SkGlyphCache_Globals& globals = getGlobals();
@@ -537,7 +599,7 @@
     // if we have a fixed budget for our cache, do a purge here
     {
         size_t allocated = globals.fTotalMemoryUsed + cache->fMemoryUsed;
-        size_t budgeted = SkGraphics::GetFontCacheLimit();
+        size_t budgeted = globals.getFontCacheLimit();
         if (allocated > budgeted) {
             (void)InternalFreeCache(&globals, allocated - budgeted);
         }
@@ -555,25 +617,6 @@
     globals.validate();
 }
 
-size_t SkGlyphCache::GetCacheUsed() {
-    SkGlyphCache_Globals& globals = getGlobals();
-    SkAutoMutexAcquire  ac(globals.fMutex);
-
-    return SkGlyphCache::ComputeMemoryUsed(globals.fHead);
-}
-
-bool SkGlyphCache::SetCacheUsed(size_t bytesUsed) {
-    size_t curr = SkGlyphCache::GetCacheUsed();
-
-    if (curr > bytesUsed) {
-        SkGlyphCache_Globals& globals = getGlobals();
-        SkAutoMutexAcquire  ac(globals.fMutex);
-
-        return InternalFreeCache(&globals, curr - bytesUsed) > 0;
-    }
-    return false;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 SkGlyphCache* SkGlyphCache::FindTail(SkGlyphCache* cache) {
@@ -585,19 +628,16 @@
     return cache;
 }
 
-size_t SkGlyphCache::ComputeMemoryUsed(const SkGlyphCache* head) {
-    size_t size = 0;
-
-    while (head != NULL) {
-        size += head->fMemoryUsed;
-        head = head->fNext;
-    }
-    return size;
-}
-
 #ifdef SK_DEBUG
 void SkGlyphCache_Globals::validate() const {
-    size_t computed = SkGlyphCache::ComputeMemoryUsed(fHead);
+    size_t computed = 0;
+
+    const SkGlyphCache* head = fHead;
+    while (head != NULL) {
+        computed += head->fMemoryUsed;
+        head = head->fNext;
+    }
+
     if (fTotalMemoryUsed != computed) {
         printf("total %d, computed %d\n", (int)fTotalMemoryUsed, (int)computed);
     }
@@ -650,9 +690,10 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-#ifdef SK_DEBUG
 
+#ifdef SK_DEBUG
 void SkGlyphCache::validate() const {
+#ifdef SK_DEBUG_GLYPH_CACHE
     int count = fGlyphArray.count();
     for (int i = 0; i < count; i++) {
         const SkGlyph* glyph = fGlyphArray[i];
@@ -662,6 +703,41 @@
             SkASSERT(fImageAlloc.contains(glyph->fImage));
         }
     }
+#endif
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkTypefaceCache.h"
+
+size_t SkGraphics::GetFontCacheLimit() {
+    return getSharedGlobals().getFontCacheLimit();
 }
 
-#endif
+size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
+    return getSharedGlobals().setFontCacheLimit(bytes);
+}
+
+size_t SkGraphics::GetFontCacheUsed() {
+    return getSharedGlobals().fTotalMemoryUsed;
+}
+
+void SkGraphics::PurgeFontCache() {
+    getSharedGlobals().purgeAll();
+    SkTypefaceCache::PurgeAll();
+}
+
+size_t SkGraphics::GetTLSFontCacheLimit() {
+    const SkGlyphCache_Globals* tls = SkGlyphCache_Globals::FindTLS();
+    return tls ? tls->getFontCacheLimit() : 0;
+}
+
+void SkGraphics::SetTLSFontCacheLimit(size_t bytes) {
+    if (0 == bytes) {
+        SkGlyphCache_Globals::DeleteTLS();
+    } else {
+        SkGlyphCache_Globals::GetTLS().setFontCacheLimit(bytes);
+    }
+}
diff --git a/src/core/SkGlyphCache.h b/src/core/SkGlyphCache.h
index 23f3c55..537b447 100644
--- a/src/core/SkGlyphCache.h
+++ b/src/core/SkGlyphCache.h
@@ -13,9 +13,12 @@
 #include "SkBitmap.h"
 #include "SkChunkAlloc.h"
 #include "SkDescriptor.h"
+#include "SkGlyph.h"
 #include "SkScalerContext.h"
 #include "SkTemplates.h"
+#include "SkTDArray.h"
 
+struct SkDeviceProperties;
 class SkPaint;
 
 class SkGlyphCache_Globals;
@@ -119,9 +122,6 @@
     bool getAuxProcData(void (*auxProc)(void*), void** dataPtr) const;
     //! Add a proc/data pair to the glyphcache. proc should be non-null
     void setAuxProc(void (*auxProc)(void*), void* auxData);
-    //! If found, remove the proc/data pair from the glyphcache (does not
-    //  call the proc)
-    void removeAuxProc(void (*auxProc)(void*));
 
     SkScalerContext* getScalerContext() const { return fScalerContext; }
 
@@ -158,18 +158,6 @@
         return VisitCache(desc, DetachProc, NULL);
     }
 
-    /** Return the approximate number of bytes used by the font cache
-    */
-    static size_t GetCacheUsed();
-
-    /** This can be called to purge old font data, in an attempt to free
-        enough bytes such that the font cache is not using more than the
-        specified number of bytes. It is thread-safe, and may be called at
-        any time.
-        Return true if some amount of the cache was purged.
-    */
-    static bool SetCacheUsed(size_t bytesUsed);
-
 #ifdef SK_DEBUG
     void validate() const;
 #else
@@ -278,7 +266,6 @@
     static size_t InternalFreeCache(SkGlyphCache_Globals*, size_t bytesNeeded);
 
     inline static SkGlyphCache* FindTail(SkGlyphCache* head);
-    static size_t ComputeMemoryUsed(const SkGlyphCache* head);
 
     friend class SkGlyphCache_Globals;
 };
@@ -289,8 +276,10 @@
     SkAutoGlyphCache(const SkDescriptor* desc) {
         fCache = SkGlyphCache::DetachCache(desc);
     }
-    SkAutoGlyphCache(const SkPaint& paint, const SkMatrix* matrix) {
-        fCache = paint.detachCache(matrix);
+    SkAutoGlyphCache(const SkPaint& paint,
+                     const SkDeviceProperties* deviceProperties,
+                     const SkMatrix* matrix) {
+        fCache = paint.detachCache(deviceProperties, matrix);
     }
     ~SkAutoGlyphCache() {
         if (fCache) {
@@ -314,4 +303,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkGraphics.cpp b/src/core/SkGraphics.cpp
index ff38f85..873d546 100644
--- a/src/core/SkGraphics.cpp
+++ b/src/core/SkGraphics.cpp
@@ -21,6 +21,7 @@
 #include "SkPixelRef.h"
 #include "SkRandom.h"
 #include "SkRefCnt.h"
+#include "SkRTConf.h"
 #include "SkScalerContext.h"
 #include "SkShader.h"
 #include "SkStream.h"
@@ -52,10 +53,14 @@
 #endif
 
 void SkGraphics::Init() {
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-    SkFlattenable::InitializeFlattenables();
-    SkPixelRef::InitializeFlattenables();
+#ifdef SK_DEVELOPER
+    skRTConfRegistry().possiblyDumpFile();
+    skRTConfRegistry().validate();
+    SkDebugf("Non-default runtime configuration options:\n");
+    skRTConfRegistry().printNonDefault( );
 #endif
+
+    SkFlattenable::InitializeFlattenables();
 #ifdef BUILD_EMBOSS_TABLE
     SkEmbossMask_BuildTable();
 #endif
@@ -119,60 +124,25 @@
              GetFontCacheLimit() >> 10);
 
 #endif
+
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkGlyphCache.h"
-#include "SkTypefaceCache.h"
-
 void SkGraphics::Term() {
     PurgeFontCache();
-}
-
-#ifndef SK_DEFAULT_FONT_CACHE_LIMIT
-    #define SK_DEFAULT_FONT_CACHE_LIMIT (2 * 1024 * 1024)
-#endif
-
-#define SK_MIN_FONT_CACHE_LIMIT    (256 * 1024)
-
-static size_t gFontCacheLimit = SK_DEFAULT_FONT_CACHE_LIMIT;
-
-size_t SkGraphics::GetFontCacheLimit() {
-    return gFontCacheLimit;
-}
-
-size_t SkGraphics::SetFontCacheLimit(size_t bytes) {
-    size_t prev = gFontCacheLimit;
-
-    if (bytes < SK_MIN_FONT_CACHE_LIMIT) {
-        bytes = SK_MIN_FONT_CACHE_LIMIT;
-    }
-    gFontCacheLimit = bytes;
-    
-    // trigger a purge if the new size is smaller that our currently used amount
-    if (bytes < SkGlyphCache::GetCacheUsed()) {
-        SkGlyphCache::SetCacheUsed(bytes);
-    }
-    return prev;
-}
-
-void SkGraphics::PurgeFontCache() {
-    SkGlyphCache::SetCacheUsed(0);
-    SkTypefaceCache::PurgeAll();
+    SkPaint::Term();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 static const char kFontCacheLimitStr[] = "font-cache-limit";
-static const size_t kFontCacheLimitLen = sizeof(kFontCacheLimitStr) - 1; 
+static const size_t kFontCacheLimitLen = sizeof(kFontCacheLimitStr) - 1;
 
 static const struct {
     const char* fStr;
     size_t fLen;
     size_t (*fFunc)(size_t);
 } gFlags[] = {
-    {kFontCacheLimitStr, kFontCacheLimitLen, SkGraphics::SetFontCacheLimit}
+    { kFontCacheLimitStr, kFontCacheLimitLen, SkGraphics::SetFontCacheLimit }
 };
 
 /* flags are of the form param; or param=value; */
diff --git a/src/core/SkImageFilter.cpp b/src/core/SkImageFilter.cpp
new file mode 100644
index 0000000..616b8b9
--- /dev/null
+++ b/src/core/SkImageFilter.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2012 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 "SkImageFilter.h"
+
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkRect.h"
+
+SK_DEFINE_INST_COUNT(SkImageFilter)
+
+SkImageFilter::SkImageFilter(int inputCount, SkImageFilter** inputs)
+  : fInputCount(inputCount), fInputs(new SkImageFilter*[inputCount]) {
+    for (int i = 0; i < inputCount; ++i) {
+        fInputs[i] = inputs[i];
+        SkSafeRef(fInputs[i]);
+    }
+}
+
+SkImageFilter::SkImageFilter(SkImageFilter* input)
+  : fInputCount(1), fInputs(new SkImageFilter*[1]) {
+    fInputs[0] = input;
+    SkSafeRef(fInputs[0]);
+}
+
+SkImageFilter::SkImageFilter(SkImageFilter* input1, SkImageFilter* input2)
+  : fInputCount(2), fInputs(new SkImageFilter*[2]) {
+    fInputs[0] = input1;
+    fInputs[1] = input2;
+    SkSafeRef(fInputs[0]);
+    SkSafeRef(fInputs[1]);
+}
+
+SkImageFilter::~SkImageFilter() {
+    for (int i = 0; i < fInputCount; i++) {
+        SkSafeUnref(fInputs[i]);
+    }
+    delete[] fInputs;
+}
+
+SkImageFilter::SkImageFilter(SkFlattenableReadBuffer& buffer)
+    : fInputCount(buffer.readInt()), fInputs(new SkImageFilter*[fInputCount]) {
+    for (int i = 0; i < fInputCount; i++) {
+        if (buffer.readBool()) {
+            fInputs[i] = static_cast<SkImageFilter*>(buffer.readFlattenable());
+        } else {
+            fInputs[i] = NULL;
+        }
+    }
+}
+
+void SkImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    buffer.writeInt(fInputCount);
+    for (int i = 0; i < fInputCount; i++) {
+        SkImageFilter* input = getInput(i);
+        buffer.writeBool(input != NULL);
+        if (input != NULL) {
+            buffer.writeFlattenable(input);
+        }
+    }
+}
+
+SkBitmap SkImageFilter::getInputResult(int index, Proxy* proxy,
+                                       const SkBitmap& src, const SkMatrix& ctm,
+                                       SkIPoint* loc) {
+    SkASSERT(index < fInputCount);
+    SkImageFilter* input = getInput(index);
+    SkBitmap result;
+    if (input && input->filterImage(proxy, src, ctm, &result, loc)) {
+        return result;
+    } else {
+        return src;
+    }
+}
+
+
+bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
+                                const SkMatrix& ctm,
+                                SkBitmap* result, SkIPoint* loc) {
+    SkASSERT(result);
+    SkASSERT(loc);
+    /*
+     *  Give the proxy first shot at the filter. If it returns false, ask
+     *  the filter to do it.
+     */
+    return (proxy && proxy->filterImage(this, src, ctm, result, loc)) ||
+           this->onFilterImage(proxy, src, ctm, result, loc);
+}
+
+bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
+                                 SkIRect* dst) {
+    SkASSERT(&src);
+    SkASSERT(dst);
+    return this->onFilterBounds(src, ctm, dst);
+}
+
+bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
+                                  SkBitmap*, SkIPoint*) {
+    return false;
+}
+
+bool SkImageFilter::canFilterImageGPU() const {
+    return false;
+}
+
+bool SkImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkASSERT(false);  // Should never be called, since canFilterImageGPU() returned false.
+    return false;
+}
+
+bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+                                   SkIRect* dst) {
+    *dst = src;
+    return true;
+}
+
+bool SkImageFilter::asNewEffect(GrEffectRef**, GrTexture*) const {
+    return false;
+}
+
+SkColorFilter* SkImageFilter::asColorFilter() const {
+    return NULL;
+}
diff --git a/src/core/SkInstCnt.cpp b/src/core/SkInstCnt.cpp
new file mode 100644
index 0000000..2f9a57d
--- /dev/null
+++ b/src/core/SkInstCnt.cpp
@@ -0,0 +1,12 @@
+/*
+ * 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 "SkInstCnt.h"
+
+#if SK_ENABLE_INST_COUNT
+bool gPrintInstCount = false;
+#endif
diff --git a/src/core/SkLineClipper.cpp b/src/core/SkLineClipper.cpp
index 708d3a9..fc4e3d2 100644
--- a/src/core/SkLineClipper.cpp
+++ b/src/core/SkLineClipper.cpp
@@ -7,6 +7,21 @@
  */
 #include "SkLineClipper.h"
 
+template <typename T> T pin_unsorted(T value, T limit0, T limit1) {
+    if (limit1 < limit0) {
+        SkTSwap(limit0, limit1);
+    }
+    // now the limits are sorted
+    SkASSERT(limit0 <= limit1);
+
+    if (value < limit0) {
+        value = limit0;
+    } else if (value > limit1) {
+        value = limit1;
+    }
+    return value;
+}
+
 // return X coordinate of intersection with horizontal line at Y
 static SkScalar sect_with_horizontal(const SkPoint src[2], SkScalar Y) {
     SkScalar dy = src[1].fY - src[0].fY;
@@ -21,7 +36,11 @@
         double X1 = src[1].fX;
         double Y1 = src[1].fY;
         double result = X0 + ((double)Y - Y0) * (X1 - X0) / (Y1 - Y0);
-        return (float)result;
+
+        // The computed X value might still exceed [X0..X1] due to quantum flux
+        // when the doubles were added and subtracted, so we have to pin the
+        // answer :(
+        return (float)pin_unsorted(result, X0, X1);
 #else
         return src[0].fX + SkScalarMulDiv(Y - src[0].fY, src[1].fX - src[0].fX,
                                           dy);
@@ -68,7 +87,7 @@
 bool SkLineClipper::IntersectLine(const SkPoint src[2], const SkRect& clip,
                                   SkPoint dst[2]) {
     SkRect bounds;
-    
+
     bounds.set(src, 2);
     if (containsNoEmptyCheck(clip, bounds)) {
         if (src != dst) {
@@ -86,7 +105,7 @@
     }
 
     int index0, index1;
-    
+
     if (src[0].fY < src[1].fY) {
         index0 = 0;
         index1 = 1;
@@ -105,7 +124,7 @@
     if (tmp[index1].fY > clip.fBottom) {
         tmp[index1].set(sect_with_horizontal(src, clip.fBottom), clip.fBottom);
     }
-    
+
     if (tmp[0].fX < tmp[1].fX) {
         index0 = 0;
         index1 = 1;
@@ -148,8 +167,37 @@
 }
 #endif
 
+#ifdef SK_SCALAR_IS_FLOAT
+#ifdef SK_DEBUG
+// This is an example of why we need to pin the result computed in
+// sect_with_horizontal. If we didn't explicitly pin, is_between_unsorted would
+// fail.
+//
+static void sect_with_horizontal_test_for_pin_results() {
+    const SkPoint pts[] = {
+        { -540000,    -720000 },
+        { SkFloatToScalar(-9.10000017e-05f), SkFloatToScalar(9.99999996e-13f) }
+    };
+    float x = sect_with_horizontal(pts, 0);
+    SkASSERT(is_between_unsorted(x, pts[0].fX, pts[1].fX));
+}
+#endif
+#endif
+
 int SkLineClipper::ClipLine(const SkPoint pts[], const SkRect& clip,
                             SkPoint lines[]) {
+#ifdef SK_SCALAR_IS_FLOAT
+#ifdef SK_DEBUG
+    {
+        static bool gOnce;
+        if (!gOnce) {
+            sect_with_horizontal_test_for_pin_results();
+            gOnce = true;
+        }
+    }
+#endif
+#endif
+
     int index0, index1;
 
     if (pts[0].fY < pts[1].fY) {
@@ -168,7 +216,7 @@
     if (pts[index0].fY >= clip.fBottom) {  // we're below the clip
         return 0;
     }
-    
+
     // Chop in Y to produce a single segment, stored in tmp[0..1]
 
     SkPoint tmp[2];
@@ -213,7 +261,7 @@
     } else {
         result = resultStorage;
         SkPoint* r = result;
-        
+
         if (tmp[index0].fX < clip.fLeft) {
             r->set(clip.fLeft, tmp[index0].fY);
             r += 1;
@@ -247,4 +295,3 @@
     }
     return lineCount;
 }
-
diff --git a/src/core/SkMMapStream.cpp b/src/core/SkMMapStream.cpp
index 0aec5e1..7388e81 100644
--- a/src/core/SkMMapStream.cpp
+++ b/src/core/SkMMapStream.cpp
@@ -15,6 +15,7 @@
 SkMMAPStream::SkMMAPStream(const char filename[])
 {
     fAddr = NULL;   // initialize to failure case
+    fSize = 0;
 
     int fildes = open(filename, O_RDONLY);
     if (fildes < 0)
@@ -74,4 +75,3 @@
         fAddr = NULL;
     }
 }
-
diff --git a/src/core/SkMallocPixelRef.cpp b/src/core/SkMallocPixelRef.cpp
index 72dbb3d..d700983 100644
--- a/src/core/SkMallocPixelRef.cpp
+++ b/src/core/SkMallocPixelRef.cpp
@@ -7,24 +7,28 @@
  */
 #include "SkMallocPixelRef.h"
 #include "SkBitmap.h"
-#include "SkFlattenable.h"
+#include "SkFlattenableBuffers.h"
 
 SkMallocPixelRef::SkMallocPixelRef(void* storage, size_t size,
-                                   SkColorTable* ctable) {
+                                   SkColorTable* ctable, bool ownPixels) {
     if (NULL == storage) {
+        SkASSERT(ownPixels);
         storage = sk_malloc_throw(size);
     }
     fStorage = storage;
     fSize = size;
     fCTable = ctable;
     SkSafeRef(ctable);
-    
+    fOwnPixels = ownPixels;
+
     this->setPreLocked(fStorage, fCTable);
 }
 
 SkMallocPixelRef::~SkMallocPixelRef() {
     SkSafeUnref(fCTable);
-    sk_free(fStorage);
+    if (fOwnPixels) {
+        sk_free(fStorage);
+    }
 }
 
 void* SkMallocPixelRef::onLockPixels(SkColorTable** ct) {
@@ -39,28 +43,24 @@
 void SkMallocPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    buffer.write32(fSize);
-    buffer.writePad(fStorage, fSize);
+    buffer.writeByteArray(fStorage, fSize);
+    buffer.writeBool(fCTable != NULL);
     if (fCTable) {
-        buffer.writeBool(true);
-        fCTable->flatten(buffer);
-    } else {
-        buffer.writeBool(false);
+        buffer.writeFlattenable(fCTable);
     }
 }
 
 SkMallocPixelRef::SkMallocPixelRef(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer, NULL) {
-    fSize = buffer.readU32();
+    fSize = buffer.getArrayCount();
     fStorage = sk_malloc_throw(fSize);
-    buffer.read(fStorage, fSize);
+    buffer.readByteArray(fStorage);
     if (buffer.readBool()) {
-        fCTable = SkNEW_ARGS(SkColorTable, (buffer));
+        fCTable = buffer.readFlattenableT<SkColorTable>();
     } else {
         fCTable = NULL;
     }
+    fOwnPixels = true;
 
     this->setPreLocked(fStorage, fCTable);
 }
-
-SK_DEFINE_PIXEL_REF_REGISTRAR(SkMallocPixelRef)
diff --git a/src/core/SkMask.cpp b/src/core/SkMask.cpp
index 36047fe..0974419 100644
--- a/src/core/SkMask.cpp
+++ b/src/core/SkMask.cpp
@@ -68,10 +68,9 @@
     SkASSERT(kBW_Format != fFormat);
     SkASSERT(fBounds.contains(x, y));
     SkASSERT(fImage);
-    
+
     char* addr = (char*)fImage;
     addr += (y - fBounds.fTop) * fRowBytes;
     addr += (x - fBounds.fLeft) << maskFormatToShift(fFormat);
     return addr;
 }
-
diff --git a/src/core/SkMaskFilter.cpp b/src/core/SkMaskFilter.cpp
index 42d07a6..9805bf9 100644
--- a/src/core/SkMaskFilter.cpp
+++ b/src/core/SkMaskFilter.cpp
@@ -10,22 +10,222 @@
 #include "SkMaskFilter.h"
 #include "SkBlitter.h"
 #include "SkBounder.h"
-#include "SkBuffer.h"
 #include "SkDraw.h"
 #include "SkRasterClip.h"
 
+
+SK_DEFINE_INST_COUNT(SkMaskFilter)
+
 bool SkMaskFilter::filterMask(SkMask*, const SkMask&, const SkMatrix&,
-                              SkIPoint*) {
+                              SkIPoint*) const {
     return false;
 }
 
+static void extractMaskSubset(const SkMask& src, SkMask* dst) {
+    SkASSERT(src.fBounds.contains(dst->fBounds));
+
+    const int dx = dst->fBounds.left() - src.fBounds.left();
+    const int dy = dst->fBounds.top() - src.fBounds.top();
+    dst->fImage = src.fImage + dy * src.fRowBytes + dx;
+    dst->fRowBytes = src.fRowBytes;
+    dst->fFormat = src.fFormat;
+}
+
+static void blitClippedMask(SkBlitter* blitter, const SkMask& mask,
+                            const SkIRect& bounds, const SkIRect& clipR) {
+    SkIRect r;
+    if (r.intersect(bounds, clipR)) {
+        blitter->blitMask(mask, r);
+    }
+}
+
+static void blitClippedRect(SkBlitter* blitter, const SkIRect& rect, const SkIRect& clipR) {
+    SkIRect r;
+    if (r.intersect(rect, clipR)) {
+        blitter->blitRect(r.left(), r.top(), r.width(), r.height());
+    }
+}
+
+#if 0
+static void dump(const SkMask& mask) {
+    for (int y = mask.fBounds.top(); y < mask.fBounds.bottom(); ++y) {
+        for (int x = mask.fBounds.left(); x < mask.fBounds.right(); ++x) {
+            SkDebugf("%02X", *mask.getAddr8(x, y));
+        }
+        SkDebugf("\n");
+    }
+    SkDebugf("\n");
+}
+#endif
+
+static void draw_nine_clipped(const SkMask& mask, const SkIRect& outerR,
+                              const SkIPoint& center, bool fillCenter,
+                              const SkIRect& clipR, SkBlitter* blitter) {
+    int cx = center.x();
+    int cy = center.y();
+    SkMask m;
+
+    // top-left
+    m.fBounds = mask.fBounds;
+    m.fBounds.fRight = cx;
+    m.fBounds.fBottom = cy;
+    extractMaskSubset(mask, &m);
+    m.fBounds.offsetTo(outerR.left(), outerR.top());
+    blitClippedMask(blitter, m, m.fBounds, clipR);
+
+    // top-right
+    m.fBounds = mask.fBounds;
+    m.fBounds.fLeft = cx + 1;
+    m.fBounds.fBottom = cy;
+    extractMaskSubset(mask, &m);
+    m.fBounds.offsetTo(outerR.right() - m.fBounds.width(), outerR.top());
+    blitClippedMask(blitter, m, m.fBounds, clipR);
+
+    // bottom-left
+    m.fBounds = mask.fBounds;
+    m.fBounds.fRight = cx;
+    m.fBounds.fTop = cy + 1;
+    extractMaskSubset(mask, &m);
+    m.fBounds.offsetTo(outerR.left(), outerR.bottom() - m.fBounds.height());
+    blitClippedMask(blitter, m, m.fBounds, clipR);
+
+    // bottom-right
+    m.fBounds = mask.fBounds;
+    m.fBounds.fLeft = cx + 1;
+    m.fBounds.fTop = cy + 1;
+    extractMaskSubset(mask, &m);
+    m.fBounds.offsetTo(outerR.right() - m.fBounds.width(),
+                       outerR.bottom() - m.fBounds.height());
+    blitClippedMask(blitter, m, m.fBounds, clipR);
+
+    SkIRect innerR;
+    innerR.set(outerR.left() + cx - mask.fBounds.left(),
+               outerR.top() + cy - mask.fBounds.top(),
+               outerR.right() + (cx + 1 - mask.fBounds.right()),
+               outerR.bottom() + (cy + 1 - mask.fBounds.bottom()));
+    if (fillCenter) {
+        blitClippedRect(blitter, innerR, clipR);
+    }
+
+    const int innerW = innerR.width();
+    size_t storageSize = (innerW + 1) * (sizeof(int16_t) + sizeof(uint8_t));
+    SkAutoSMalloc<4*1024> storage(storageSize);
+    int16_t* runs = (int16_t*)storage.get();
+    uint8_t* alpha = (uint8_t*)(runs + innerW + 1);
+
+    SkIRect r;
+    // top
+    r.set(innerR.left(), outerR.top(), innerR.right(), innerR.top());
+    if (r.intersect(clipR)) {
+        int startY = SkMax32(0, r.top() - outerR.top());
+        int stopY = startY + r.height();
+        int width = r.width();
+        for (int y = startY; y < stopY; ++y) {
+            runs[0] = width;
+            runs[width] = 0;
+            alpha[0] = *mask.getAddr8(cx, mask.fBounds.top() + y);
+            blitter->blitAntiH(r.left(), outerR.top() + y, alpha, runs);
+        }
+    }
+    // bottom
+    r.set(innerR.left(), innerR.bottom(), innerR.right(), outerR.bottom());
+    if (r.intersect(clipR)) {
+        int startY = outerR.bottom() - r.bottom();
+        int stopY = startY + r.height();
+        int width = r.width();
+        for (int y = startY; y < stopY; ++y) {
+            runs[0] = width;
+            runs[width] = 0;
+            alpha[0] = *mask.getAddr8(cx, mask.fBounds.bottom() - y - 1);
+            blitter->blitAntiH(r.left(), outerR.bottom() - y - 1, alpha, runs);
+        }
+    }
+    // left
+    r.set(outerR.left(), innerR.top(), innerR.left(), innerR.bottom());
+    if (r.intersect(clipR)) {
+        int startX = r.left() - outerR.left();
+        int stopX = startX + r.width();
+        int height = r.height();
+        for (int x = startX; x < stopX; ++x) {
+            blitter->blitV(outerR.left() + x, r.top(), height,
+                           *mask.getAddr8(mask.fBounds.left() + x, mask.fBounds.top() + cy));
+        }
+    }
+    // right
+    r.set(innerR.right(), innerR.top(), outerR.right(), innerR.bottom());
+    if (r.intersect(clipR)) {
+        int startX = outerR.right() - r.right();
+        int stopX = startX + r.width();
+        int height = r.height();
+        for (int x = startX; x < stopX; ++x) {
+            blitter->blitV(outerR.right() - x - 1, r.top(), height,
+                           *mask.getAddr8(mask.fBounds.right() - x - 1, mask.fBounds.top() + cy));
+        }
+    }
+}
+
+static void draw_nine(const SkMask& mask, const SkIRect& outerR,
+                      const SkIPoint& center, bool fillCenter,
+                      const SkRasterClip& clip, SkBounder* bounder,
+                      SkBlitter* blitter) {
+    // if we get here, we need to (possibly) resolve the clip and blitter
+    SkAAClipBlitterWrapper wrapper(clip, blitter);
+    blitter = wrapper.getBlitter();
+
+    SkRegion::Cliperator clipper(wrapper.getRgn(), outerR);
+
+    if (!clipper.done() && (!bounder || bounder->doIRect(outerR))) {
+        const SkIRect& cr = clipper.rect();
+        do {
+            draw_nine_clipped(mask, outerR, center, fillCenter, cr, blitter);
+            clipper.next();
+        } while (!clipper.done());
+    }
+}
+
+static int countNestedRects(const SkPath& path, SkRect rects[2]) {
+    if (path.isNestedRects(rects)) {
+        return 2;
+    }
+    return path.isRect(&rects[0]);
+}
+
 bool SkMaskFilter::filterPath(const SkPath& devPath, const SkMatrix& matrix,
                               const SkRasterClip& clip, SkBounder* bounder,
-                              SkBlitter* blitter) {
+                              SkBlitter* blitter, SkPaint::Style style) const {
+    SkRect rects[2];
+    int rectCount = 0;
+    if (SkPaint::kFill_Style == style) {
+        rectCount = countNestedRects(devPath, rects);
+    }
+    if (rectCount > 0) {
+        NinePatch patch;
+
+        patch.fMask.fImage = NULL;
+        switch (this->filterRectsToNine(rects, rectCount, matrix,
+                                        clip.getBounds(), &patch)) {
+            case kFalse_FilterReturn:
+                SkASSERT(NULL == patch.fMask.fImage);
+                return false;
+
+            case kTrue_FilterReturn:
+                draw_nine(patch.fMask, patch.fOuterRect, patch.fCenter,
+                          1 == rectCount, clip, bounder, blitter);
+                SkMask::FreeImage(patch.fMask.fImage);
+                return true;
+
+            case kUnimplemented_FilterReturn:
+                SkASSERT(NULL == patch.fMask.fImage);
+                // fall through
+                break;
+        }
+    }
+
     SkMask  srcM, dstM;
 
     if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), this, &matrix, &srcM,
-                            SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode,
+                            style)) {
         return false;
     }
     SkAutoMaskFreeImage autoSrc(srcM.fImage);
@@ -52,11 +252,17 @@
     return true;
 }
 
+SkMaskFilter::FilterReturn
+SkMaskFilter::filterRectsToNine(const SkRect[], int count, const SkMatrix&,
+                                const SkIRect& clipBounds, NinePatch*) const {
+    return kUnimplemented_FilterReturn;
+}
+
 SkMaskFilter::BlurType SkMaskFilter::asABlur(BlurInfo*) const {
     return kNone_BlurType;
 }
 
-void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) {
+void SkMaskFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
     SkMask  srcM, dstM;
 
     srcM.fImage = NULL;
@@ -71,5 +277,3 @@
         dst->set(srcM.fBounds);
     }
 }
-
-
diff --git a/src/core/SkMaskGamma.cpp b/src/core/SkMaskGamma.cpp
new file mode 100644
index 0000000..9066fb7
--- /dev/null
+++ b/src/core/SkMaskGamma.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "SkTypes.h"
+
+#include "SkColor.h"
+#include "SkFloatingPoint.h"
+#include "SkMaskGamma.h"
+
+class SkLinearColorSpaceLuminance : public SkColorSpaceLuminance {
+    virtual SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE {
+        SkASSERT(SK_Scalar1 == gamma);
+        return luminance;
+    }
+    virtual SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE {
+        SkASSERT(SK_Scalar1 == gamma);
+        return luma;
+    }
+};
+
+class SkGammaColorSpaceLuminance : public SkColorSpaceLuminance {
+    virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const SK_OVERRIDE {
+        return SkScalarPow(luminance, gamma);
+    }
+    virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const SK_OVERRIDE {
+        return SkScalarPow(luma, SkScalarInvert(gamma));
+    }
+};
+
+class SkSRGBColorSpaceLuminance : public SkColorSpaceLuminance {
+    virtual SkScalar toLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luminance) const SK_OVERRIDE {
+        SkASSERT(0 == gamma);
+        //The magic numbers are derived from the sRGB specification.
+        //See http://www.color.org/chardata/rgb/srgb.xalter .
+        if (luminance <= SkFloatToScalar(0.04045f)) {
+            return luminance / SkFloatToScalar(12.92f);
+        }
+        return SkScalarPow((luminance + SkFloatToScalar(0.055f)) / SkFloatToScalar(1.055f),
+                        SkFloatToScalar(2.4f));
+    }
+    virtual SkScalar fromLuma(SkScalar SkDEBUGCODE(gamma), SkScalar luma) const SK_OVERRIDE {
+        SkASSERT(0 == gamma);
+        //The magic numbers are derived from the sRGB specification.
+        //See http://www.color.org/chardata/rgb/srgb.xalter .
+        if (luma <= SkFloatToScalar(0.0031308f)) {
+            return luma * SkFloatToScalar(12.92f);
+        }
+        return SkFloatToScalar(1.055f) * SkScalarPow(luma, SkScalarInvert(SkFloatToScalar(2.4f)))
+               - SkFloatToScalar(0.055f);
+    }
+};
+
+/*static*/ const SkColorSpaceLuminance& SkColorSpaceLuminance::Fetch(SkScalar gamma) {
+    static SkLinearColorSpaceLuminance gSkLinearColorSpaceLuminance;
+    static SkGammaColorSpaceLuminance gSkGammaColorSpaceLuminance;
+    static SkSRGBColorSpaceLuminance gSkSRGBColorSpaceLuminance;
+
+    if (0 == gamma) {
+        return gSkSRGBColorSpaceLuminance;
+    } else if (SK_Scalar1 == gamma) {
+        return gSkLinearColorSpaceLuminance;
+    } else {
+        return gSkGammaColorSpaceLuminance;
+    }
+}
+
+static float apply_contrast(float srca, float contrast) {
+    return srca + ((1.0f - srca) * contrast * srca);
+}
+
+void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast,
+                                       const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma,
+                                       const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma) {
+    const float src = (float)srcI / 255.0f;
+    const float linSrc = srcConvert.toLuma(srcGamma, src);
+    //Guess at the dst. The perceptual inverse provides smaller visual
+    //discontinuities when slight changes to desaturated colors cause a channel
+    //to map to a different correcting lut with neighboring srcI.
+    //See https://code.google.com/p/chromium/issues/detail?id=141425#c59 .
+    const float dst = 1.0f - src;
+    const float linDst = dstConvert.toLuma(dstGamma, dst);
+
+    //Contrast value tapers off to 0 as the src luminance becomes white
+    const float adjustedContrast = SkScalarToFloat(contrast) * linDst;
+
+    //Remove discontinuity and instability when src is close to dst.
+    //The value 1/256 is arbitrary and appears to contain the instability.
+    if (fabs(src - dst) < (1.0f / 256.0f)) {
+        float ii = 0.0f;
+        for (int i = 0; i < 256; ++i, ii += 1.0f) {
+            float rawSrca = ii / 255.0f;
+            float srca = apply_contrast(rawSrca, adjustedContrast);
+            table[i] = SkToU8(sk_float_round2int(255.0f * srca));
+        }
+    } else {
+        // Avoid slow int to float conversion.
+        float ii = 0.0f;
+        for (int i = 0; i < 256; ++i, ii += 1.0f) {
+            // 'rawSrca += 1.0f / 255.0f' and even
+            // 'rawSrca = i * (1.0f / 255.0f)' can add up to more than 1.0f.
+            // When this happens the table[255] == 0x0 instead of 0xff.
+            // See http://code.google.com/p/chromium/issues/detail?id=146466
+            float rawSrca = ii / 255.0f;
+            float srca = apply_contrast(rawSrca, adjustedContrast);
+            SkASSERT(srca <= 1.0f);
+            float dsta = 1.0f - srca;
+
+            //Calculate the output we want.
+            float linOut = (linSrc * srca + dsta * linDst);
+            SkASSERT(linOut <= 1.0f);
+            float out = dstConvert.fromLuma(dstGamma, linOut);
+
+            //Undo what the blit blend will do.
+            float result = (out - dst) / (src - dst);
+            SkASSERT(sk_float_round2int(255.0f * result) <= 255);
+
+            table[i] = SkToU8(sk_float_round2int(255.0f * result));
+        }
+    }
+}
diff --git a/src/core/SkMaskGamma.h b/src/core/SkMaskGamma.h
new file mode 100644
index 0000000..fafe4ac
--- /dev/null
+++ b/src/core/SkMaskGamma.h
@@ -0,0 +1,221 @@
+/*
+ * 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 SkMaskGamma_DEFINED
+#define SkMaskGamma_DEFINED
+
+#include "SkTypes.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkRefCnt.h"
+
+/**
+ * SkColorSpaceLuminance is used to convert luminances to and from linear and
+ * perceptual color spaces.
+ *
+ * Luma is used to specify a linear luminance value [0.0, 1.0].
+ * Luminance is used to specify a luminance value in an arbitrary color space [0.0, 1.0].
+ */
+class SkColorSpaceLuminance : SkNoncopyable {
+public:
+    virtual ~SkColorSpaceLuminance() { }
+
+    /** Converts a color component luminance in the color space to a linear luma. */
+    virtual SkScalar toLuma(SkScalar gamma, SkScalar luminance) const = 0;
+    /** Converts a linear luma to a color component luminance in the color space. */
+    virtual SkScalar fromLuma(SkScalar gamma, SkScalar luma) const = 0;
+
+    /** Converts a color to a luminance value. */
+    static U8CPU computeLuminance(SkScalar gamma, SkColor c) {
+        const SkColorSpaceLuminance& luminance = Fetch(gamma);
+        SkScalar r = luminance.toLuma(gamma, SkIntToScalar(SkColorGetR(c)) / 255);
+        SkScalar g = luminance.toLuma(gamma, SkIntToScalar(SkColorGetG(c)) / 255);
+        SkScalar b = luminance.toLuma(gamma, SkIntToScalar(SkColorGetB(c)) / 255);
+        SkScalar luma = r * SkFloatToScalar(SK_LUM_COEFF_R) +
+                        g * SkFloatToScalar(SK_LUM_COEFF_G) +
+                        b * SkFloatToScalar(SK_LUM_COEFF_B);
+        SkASSERT(luma <= SK_Scalar1);
+        return SkScalarRoundToInt(luminance.fromLuma(gamma, luma) * 255);
+    }
+
+    /** Retrieves the SkColorSpaceLuminance for the given gamma. */
+    static const SkColorSpaceLuminance& Fetch(SkScalar gamma);
+};
+
+///@{
+/**
+ * Scales base <= 2^N-1 to 2^8-1
+ * @param N [1, 8] the number of bits used by base.
+ * @param base the number to be scaled to [0, 255].
+ */
+template<U8CPU N> static inline U8CPU sk_t_scale255(U8CPU base) {
+    base <<= (8 - N);
+    U8CPU lum = base;
+    for (unsigned int i = N; i < 8; i += N) {
+        lum |= base >> i;
+    }
+    return lum;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<1>(U8CPU base) {
+    return base * 0xFF;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<2>(U8CPU base) {
+    return base * 0x55;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<4>(U8CPU base) {
+    return base * 0x11;
+}
+template<> /*static*/ inline U8CPU sk_t_scale255<8>(U8CPU base) {
+    return base;
+}
+///@}
+
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend;
+
+void SkTMaskGamma_build_correcting_lut(uint8_t table[256], U8CPU srcI, SkScalar contrast,
+                                       const SkColorSpaceLuminance& srcConvert, SkScalar srcGamma,
+                                       const SkColorSpaceLuminance& dstConvert, SkScalar dstGamma);
+
+/**
+ * A regular mask contains linear alpha values. A gamma correcting mask
+ * contains non-linear alpha values in an attempt to create gamma correct blits
+ * in the presence of a gamma incorrect (linear) blend in the blitter.
+ *
+ * SkMaskGamma creates and maintains tables which convert linear alpha values
+ * to gamma correcting alpha values.
+ * @param R The number of luminance bits to use [1, 8] from the red channel.
+ * @param G The number of luminance bits to use [1, 8] from the green channel.
+ * @param B The number of luminance bits to use [1, 8] from the blue channel.
+ */
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskGamma : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT_TEMPLATE(SkTMaskGamma)
+
+    /** Creates a linear SkTMaskGamma. */
+    SkTMaskGamma() : fIsLinear(true) { }
+
+    /**
+     * Creates tables to convert linear alpha values to gamma correcting alpha
+     * values.
+     *
+     * @param contrast A value in the range [0.0, 1.0] which indicates the
+     *                 amount of artificial contrast to add.
+     * @param paint The color space in which the paint color was chosen.
+     * @param device The color space of the target device.
+     */
+    SkTMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) : fIsLinear(false) {
+        const SkColorSpaceLuminance& paintConvert = SkColorSpaceLuminance::Fetch(paintGamma);
+        const SkColorSpaceLuminance& deviceConvert = SkColorSpaceLuminance::Fetch(deviceGamma);
+        for (U8CPU i = 0; i < (1 << MAX_LUM_BITS); ++i) {
+            U8CPU lum = sk_t_scale255<MAX_LUM_BITS>(i);
+            SkTMaskGamma_build_correcting_lut(fGammaTables[i], lum, contrast,
+                                              paintConvert, paintGamma,
+                                              deviceConvert, deviceGamma);
+        }
+    }
+
+    /** Given a color, returns the closest canonical color. */
+    static SkColor CanonicalColor(SkColor color) {
+        return SkColorSetRGB(
+                   sk_t_scale255<R_LUM_BITS>(SkColorGetR(color) >> (8 - R_LUM_BITS)),
+                   sk_t_scale255<G_LUM_BITS>(SkColorGetG(color) >> (8 - G_LUM_BITS)),
+                   sk_t_scale255<B_LUM_BITS>(SkColorGetB(color) >> (8 - B_LUM_BITS)));
+    }
+
+    /** The type of the mask pre-blend which will be returned from preBlend(SkColor). */
+    typedef SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> PreBlend;
+
+    /**
+     * Provides access to the tables appropriate for converting linear alpha
+     * values into gamma correcting alpha values when drawing the given color
+     * through the mask. The destination color will be approximated.
+     */
+    PreBlend preBlend(SkColor color) const;
+
+private:
+    static const int MAX_LUM_BITS =
+          B_LUM_BITS > (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS)
+        ? B_LUM_BITS : (R_LUM_BITS > G_LUM_BITS ? R_LUM_BITS : G_LUM_BITS);
+    uint8_t fGammaTables[1 << MAX_LUM_BITS][256];
+    bool fIsLinear;
+
+    typedef SkRefCnt INHERITED;
+};
+
+
+#define MacroComma ,
+SK_DEFINE_INST_COUNT_TEMPLATE(
+    template <int R_LUM_BITS MacroComma int G_LUM_BITS MacroComma int B_LUM_BITS>,
+    SkTMaskGamma<R_LUM_BITS MacroComma G_LUM_BITS MacroComma B_LUM_BITS>);
+
+/**
+ * SkTMaskPreBlend is a tear-off of SkTMaskGamma. It provides the tables to
+ * convert a linear alpha value for a given channel to a gamma correcting alpha
+ * value for that channel. This class is immutable.
+ *
+ * If fR, fG, or fB is NULL, all of them will be. This indicates that no mask
+ * pre blend should be applied. SkTMaskPreBlend::isApplicable() is provided as
+ * a convenience function to test for the absence of this case.
+ */
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS> class SkTMaskPreBlend {
+private:
+    SkTMaskPreBlend(const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>* parent,
+                    const uint8_t* r, const uint8_t* g, const uint8_t* b)
+    : fParent(SkSafeRef(parent)), fR(r), fG(g), fB(b) { }
+
+    SkAutoTUnref<const SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS> > fParent;
+    friend class SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>;
+public:
+    /** Creates a non applicable SkTMaskPreBlend. */
+    SkTMaskPreBlend() : fParent(), fR(NULL), fG(NULL), fB(NULL) { }
+
+    /**
+     * This copy contructor exists for correctness, but should never be called
+     * when return value optimization is enabled.
+     */
+    SkTMaskPreBlend(const SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>& that)
+    : fParent(SkSafeRef(that.fParent.get())), fR(that.fR), fG(that.fG), fB(that.fB) { }
+
+    ~SkTMaskPreBlend() { }
+
+    /** True if this PreBlend should be applied. When false, fR, fG, and fB are NULL. */
+    bool isApplicable() const {
+        return NULL != this->fG;
+    }
+
+    const uint8_t* fR;
+    const uint8_t* fG;
+    const uint8_t* fB;
+};
+
+template <int R_LUM_BITS, int G_LUM_BITS, int B_LUM_BITS>
+SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>
+SkTMaskGamma<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>::preBlend(SkColor color) const {
+    return fIsLinear ? SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>()
+                     : SkTMaskPreBlend<R_LUM_BITS, G_LUM_BITS, B_LUM_BITS>(this,
+                         fGammaTables[SkColorGetR(color) >> (8 - MAX_LUM_BITS)],
+                         fGammaTables[SkColorGetG(color) >> (8 - MAX_LUM_BITS)],
+                         fGammaTables[SkColorGetB(color) >> (8 - MAX_LUM_BITS)]);
+}
+
+///@{
+/**
+ *  If APPLY_LUT is false, returns component unchanged.
+ *  If APPLY_LUT is true, returns lut[component].
+ *  @param APPLY_LUT whether or not the look-up table should be applied to component.
+ *  @component the initial component.
+ *  @lut a look-up table which transforms the component.
+ */
+template<bool APPLY_LUT> static inline U8CPU sk_apply_lut_if(U8CPU component, const uint8_t*) {
+    return component;
+}
+template<> /*static*/ inline U8CPU sk_apply_lut_if<true>(U8CPU component, const uint8_t* lut) {
+    return lut[component];
+}
+///@}
+
+#endif
diff --git a/src/core/SkMath.cpp b/src/core/SkMath.cpp
index 7b75305..0efedd7 100644
--- a/src/core/SkMath.cpp
+++ b/src/core/SkMath.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 The Android Open Source Project
  *
@@ -6,8 +5,7 @@
  * found in the LICENSE file.
  */
 
-
-#include "SkMath.h"
+#include "SkMathPriv.h"
 #include "SkCordic.h"
 #include "SkFloatBits.h"
 #include "SkFloatingPoint.h"
@@ -17,12 +15,13 @@
 #ifdef SK_SCALAR_IS_FLOAT
     const uint32_t gIEEENotANumber = 0x7FFFFFFF;
     const uint32_t gIEEEInfinity = 0x7F800000;
+    const uint32_t gIEEENegativeInfinity = 0xFF800000;
 #endif
 
 #define sub_shift(zeros, x, n)  \
     zeros -= n;                 \
     x >>= n
-    
+
 int SkCLZ_portable(uint32_t x) {
     if (x == 0) {
         return 32;
@@ -231,7 +230,7 @@
     int lz = SkCLZ(a);
     a = a << lz >> 16;
 
-    // compute 1/a approximation (0.5 <= a < 1.0) 
+    // compute 1/a approximation (0.5 <= a < 1.0)
     uint32_t r = 0x17400 - a;      // (2.90625 (~2.914) - 2*a) >> 1
 
     // Newton-Raphson iteration:
@@ -255,13 +254,13 @@
     case n:                                             \
         if ((numer = (numer << 1) - denom) >= 0)        \
             result |= 1 << (n - 1); else numer += denom
-            
+
 int32_t SkDivBits(int32_t numer, int32_t denom, int shift_bias) {
     SkASSERT(denom != 0);
     if (numer == 0) {
         return 0;
     }
-        
+
     // make numer and denom positive, and sign hold the resulting sign
     int32_t sign = SkExtractSign(numer ^ denom);
     numer = SkAbs32(numer);
@@ -280,16 +279,16 @@
 
     denom <<= dbits;
     numer <<= nbits;
-    
+
     SkFixed result = 0;
-    
+
     // do the first one
     if ((numer -= denom) >= 0) {
         result = 1;
     } else {
         numer += denom;
     }
-    
+
     // Now fall into our switch statement if there are more bits to compute
     if (bits > 0) {
         // make room for the rest of the answer bits
@@ -326,7 +325,7 @@
 
     numer = SkApplySign(numer, sn);
     denom = SkApplySign(denom, sd);
-    
+
     if (numer < denom) {
         return SkApplySign(numer, sn);
     } else if (numer == denom) {
@@ -389,7 +388,7 @@
 
 SkFixed SkFixedMean(SkFixed a, SkFixed b) {
     Sk64 tmp;
-    
+
     tmp.setMul(a, b);
     return tmp.getSqrt();
 }
@@ -535,4 +534,3 @@
 SkFixed SkFixedATan2(SkFixed y, SkFixed x) { return SkCordicATan2(y, x); }
 SkFixed SkFixedExp(SkFixed x) { return SkCordicExp(x); }
 SkFixed SkFixedLog(SkFixed x) { return SkCordicLog(x); }
-
diff --git a/src/core/SkMathPriv.h b/src/core/SkMathPriv.h
new file mode 100644
index 0000000..53cf430
--- /dev/null
+++ b/src/core/SkMathPriv.h
@@ -0,0 +1,89 @@
+/*
+ * 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 SkMathPriv_DEFINED
+#define SkMathPriv_DEFINED
+
+#include "SkMath.h"
+
+/** Returns -1 if n < 0, else returns 0
+ */
+#define SkExtractSign(n)    ((int32_t)(n) >> 31)
+
+/** If sign == -1, returns -n, else sign must be 0, and returns n.
+ Typically used in conjunction with SkExtractSign().
+ */
+static inline int32_t SkApplySign(int32_t n, int32_t sign) {
+    SkASSERT(sign == 0 || sign == -1);
+    return (n ^ sign) - sign;
+}
+
+/** Return x with the sign of y */
+static inline int32_t SkCopySign32(int32_t x, int32_t y) {
+    return SkApplySign(x, SkExtractSign(x ^ y));
+}
+
+/** Given a positive value and a positive max, return the value
+ pinned against max.
+ Note: only works as long as max - value doesn't wrap around
+ @return max if value >= max, else value
+ */
+static inline unsigned SkClampUMax(unsigned value, unsigned max) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (value > max) {
+        value = max;
+    }
+    return value;
+#else
+    int diff = max - value;
+    // clear diff if diff is positive
+    diff &= diff >> 31;
+
+    return value + diff;
+#endif
+}
+
+/** Computes the 64bit product of a * b, and then shifts the answer down by
+ shift bits, returning the low 32bits. shift must be [0..63]
+ e.g. to perform a fixedmul, call SkMulShift(a, b, 16)
+ */
+int32_t SkMulShift(int32_t a, int32_t b, unsigned shift);
+
+/** Return the integer cube root of value, with a bias of bitBias
+ */
+int32_t SkCubeRootBits(int32_t value, int bitBias);
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Return a*b/255, truncating away any fractional bits. Only valid if both
+ a and b are 0..255
+ */
+static inline U8CPU SkMulDiv255Trunc(U8CPU a, U8CPU b) {
+    SkASSERT((uint8_t)a == a);
+    SkASSERT((uint8_t)b == b);
+    unsigned prod = SkMulS16(a, b) + 1;
+    return (prod + (prod >> 8)) >> 8;
+}
+
+/** Return (a*b)/255, taking the ceiling of any fractional bits. Only valid if
+ both a and b are 0..255. The expected result equals (a * b + 254) / 255.
+ */
+static inline U8CPU SkMulDiv255Ceiling(U8CPU a, U8CPU b) {
+    SkASSERT((uint8_t)a == a);
+    SkASSERT((uint8_t)b == b);
+    unsigned prod = SkMulS16(a, b) + 255;
+    return (prod + (prod >> 8)) >> 8;
+}
+
+/** Just the rounding step in SkDiv255Round: round(value / 255)
+ */
+static inline unsigned SkDiv255Round(unsigned prod) {
+    prod += 128;
+    return (prod + (prod >> 8)) >> 8;
+}
+
+#endif
diff --git a/src/core/SkMatrix.cpp b/src/core/SkMatrix.cpp
index 6e0bf02..31a8e37 100644
--- a/src/core/SkMatrix.cpp
+++ b/src/core/SkMatrix.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkMatrix.h"
 #include "Sk64.h"
 #include "SkFloatBits.h"
@@ -30,7 +28,7 @@
 
 void SkMatrix::reset() {
     fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
-    fMat[kMSkewX]  = fMat[kMSkewY] = 
+    fMat[kMSkewX]  = fMat[kMSkewY] =
     fMat[kMTransX] = fMat[kMTransY] =
     fMat[kMPersp0] = fMat[kMPersp1] = 0;
     fMat[kMPersp2] = kMatrix22Elem;
@@ -57,15 +55,27 @@
 #endif
 
 uint8_t SkMatrix::computePerspectiveTypeMask() const {
-    unsigned mask = kOnlyPerspectiveValid_Mask | kUnknown_Mask;
-
+#ifdef SK_SCALAR_SLOW_COMPARES
     if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
             SkScalarAs2sCompliment(fMat[kMPersp1]) |
             (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
-        mask |= kPerspective_Mask;
+        return SkToU8(kORableMasks);
     }
+#else
+    // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
+    // is a win, but replacing those below is not. We don't yet understand
+    // that result.
+    if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 ||
+        fMat[kMPersp2] != kMatrix22Elem) {
+        // If this is a perspective transform, we return true for all other
+        // transform flags - this does not disable any optimizations, respects
+        // the rule that the type mask must be conservative, and speeds up
+        // type mask computation.
+        return SkToU8(kORableMasks);
+    }
+#endif
 
-    return SkToU8(mask);
+    return SkToU8(kOnlyPerspectiveValid_Mask | kUnknown_Mask);
 }
 
 uint8_t SkMatrix::computeTypeMask() const {
@@ -75,7 +85,7 @@
     if (SkScalarAs2sCompliment(fMat[kMPersp0]) |
             SkScalarAs2sCompliment(fMat[kMPersp1]) |
             (SkScalarAs2sCompliment(fMat[kMPersp2]) - kPersp1Int)) {
-        mask |= kPerspective_Mask;
+        return SkToU8(kORableMasks);
     }
 
     if (SkScalarAs2sCompliment(fMat[kMTransX]) |
@@ -83,12 +93,11 @@
         mask |= kTranslate_Mask;
     }
 #else
-    // Benchmarking suggests that replacing this set of SkScalarAs2sCompliment
-    // is a win, but replacing those below is not. We don't yet understand
-    // that result.
     if (fMat[kMPersp0] != 0 || fMat[kMPersp1] != 0 ||
         fMat[kMPersp2] != kMatrix22Elem) {
-        mask |= kPerspective_Mask;
+        // Once it is determined that that this is a perspective transform,
+        // all other flags are moot as far as optimizations are concerned.
+        return SkToU8(kORableMasks);
     }
 
     if (fMat[kMTransX] != 0 || fMat[kMTransY] != 0) {
@@ -102,30 +111,43 @@
     int m11 = SkScalarAs2sCompliment(fMat[SkMatrix::kMScaleY]);
 
     if (m01 | m10) {
-        mask |= kAffine_Mask;
-    }
+        // The skew components may be scale-inducing, unless we are dealing
+        // with a pure rotation.  Testing for a pure rotation is expensive,
+        // so we opt for being conservative by always setting the scale bit.
+        // along with affine.
+        // By doing this, we are also ensuring that matrices have the same
+        // type masks as their inverses.
+        mask |= kAffine_Mask | kScale_Mask;
 
-    if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
-        mask |= kScale_Mask;
-    }
+        // For rectStaysRect, in the affine case, we only need check that
+        // the primary diagonal is all zeros and that the secondary diagonal
+        // is all non-zero.
 
-    if ((mask & kPerspective_Mask) == 0) {
         // map non-zero to 1
-        m00 = m00 != 0;
         m01 = m01 != 0;
         m10 = m10 != 0;
-        m11 = m11 != 0;
 
-        // record if the (p)rimary and (s)econdary diagonals are all 0 or
-        // all non-zero (answer is 0 or 1)
-        int dp0 = (m00 | m11) ^ 1;  // true if both are 0
-        int dp1 = m00 & m11;        // true if both are 1
-        int ds0 = (m01 | m10) ^ 1;  // true if both are 0
+        int dp0 = 0 == (m00 | m11) ;  // true if both are 0
         int ds1 = m01 & m10;        // true if both are 1
 
-        // return 1 if primary is 1 and secondary is 0 or
-        // primary is 0 and secondary is 1
-        mask |= ((dp0 & ds1) | (dp1 & ds0)) << kRectStaysRect_Shift;
+        mask |= (dp0 & ds1) << kRectStaysRect_Shift;
+    } else {
+        // Only test for scale explicitly if not affine, since affine sets the
+        // scale bit.
+        if ((m00 - kScalar1Int) | (m11 - kScalar1Int)) {
+            mask |= kScale_Mask;
+        }
+
+        // Not affine, therefore we already know secondary diagonal is
+        // all zeros, so we just need to check that primary diagonal is
+        // all non-zero.
+
+        // map non-zero to 1
+        m00 = m00 != 0;
+        m11 = m11 != 0;
+
+        // record if the (p)rimary diagonal is all non-zero
+        mask |= (m00 & m11) << kRectStaysRect_Shift;
     }
 
     return SkToU8(mask);
@@ -148,13 +170,50 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+bool SkMatrix::isSimilarity(SkScalar tol) const {
+    // if identity or translate matrix
+    TypeMask mask = this->getType();
+    if (mask <= kTranslate_Mask) {
+        return true;
+    }
+    if (mask & kPerspective_Mask) {
+        return false;
+    }
+
+    SkScalar mx = fMat[kMScaleX];
+    SkScalar my = fMat[kMScaleY];
+    // if no skew, can just compare scale factors
+    if (!(mask & kAffine_Mask)) {
+        return !SkScalarNearlyZero(mx) && SkScalarNearlyEqual(SkScalarAbs(mx), SkScalarAbs(my));
+    }
+    SkScalar sx = fMat[kMSkewX];
+    SkScalar sy = fMat[kMSkewY];
+
+    // degenerate matrix, non-similarity
+    if (SkScalarNearlyZero(mx) && SkScalarNearlyZero(my)
+        && SkScalarNearlyZero(sx) && SkScalarNearlyZero(sy)) {
+        return false;
+    }
+
+    // it has scales and skews, but it could also be rotation, check it out.
+    SkVector vec[2];
+    vec[0].set(mx, sx);
+    vec[1].set(sy, my);
+
+    return SkScalarNearlyZero(vec[0].dot(vec[1]), SkScalarSquare(tol)) &&
+           SkScalarNearlyEqual(vec[0].lengthSqd(), vec[1].lengthSqd(),
+                SkScalarSquare(tol));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 void SkMatrix::setTranslate(SkScalar dx, SkScalar dy) {
     if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
         fMat[kMTransX] = dx;
         fMat[kMTransY] = dy;
 
         fMat[kMScaleX] = fMat[kMScaleY] = SK_Scalar1;
-        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMSkewX]  = fMat[kMSkewY] =
         fMat[kMPersp0] = fMat[kMPersp1] = 0;
         fMat[kMPersp2] = kMatrix22Elem;
 
@@ -170,7 +229,7 @@
         m.setTranslate(dx, dy);
         return this->preConcat(m);
     }
-    
+
     if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
         fMat[kMTransX] += SkScalarMul(fMat[kMScaleX], dx) +
                           SkScalarMul(fMat[kMSkewX], dy);
@@ -188,7 +247,7 @@
         m.setTranslate(dx, dy);
         return this->postConcat(m);
     }
-    
+
     if (SkScalarToCompareType(dx) || SkScalarToCompareType(dy)) {
         fMat[kMTransX] += dx;
         fMat[kMTransY] += dy;
@@ -209,9 +268,9 @@
         fMat[kMTransY] = py - SkScalarMul(sy, py);
         fMat[kMPersp2] = kMatrix22Elem;
 
-        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMSkewX]  = fMat[kMSkewY] =
         fMat[kMPersp0] = fMat[kMPersp1] = 0;
-        
+
         this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
     }
 }
@@ -225,7 +284,7 @@
         fMat[kMPersp2] = kMatrix22Elem;
 
         fMat[kMTransX] = fMat[kMTransY] =
-        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMSkewX]  = fMat[kMSkewY] =
         fMat[kMPersp0] = fMat[kMPersp1] = 0;
 
         this->setTypeMask(kScale_Mask | kRectStaysRect_Mask);
@@ -298,7 +357,7 @@
         int ds = denom >> 31;
         numer = (numer ^ ns) - ns;
         denom = (denom ^ ds) - ds;
-        
+
         SkFixed answer = (numer + (denom >> 1)) / denom;
         int as = ns ^ ds;
         return (answer ^ as) - as;
@@ -327,7 +386,7 @@
     fMat[kMScaleX] *= invX;
     fMat[kMSkewX]  *= invX;
     fMat[kMTransX] *= invX;
-    
+
     fMat[kMScaleY] *= invY;
     fMat[kMSkewY]  *= invY;
     fMat[kMTransY] *= invY;
@@ -353,7 +412,7 @@
 
     fMat[kMPersp0] = fMat[kMPersp1] = 0;
     fMat[kMPersp2] = kMatrix22Elem;
-    
+
     this->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
 }
 
@@ -501,7 +560,7 @@
             } else {
                 diff = dst.height() - SkScalarMul(src.height(), sy);
             }
-            
+
             if (align == kCenter_ScaleToFit) {
                 diff = SkScalarHalf(diff);
             }
@@ -517,10 +576,17 @@
         fMat[kMScaleY] = sy;
         fMat[kMTransX] = tx;
         fMat[kMTransY] = ty;
-        fMat[kMSkewX]  = fMat[kMSkewY] = 
+        fMat[kMSkewX]  = fMat[kMSkewY] =
         fMat[kMPersp0] = fMat[kMPersp1] = 0;
 
-        this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
+        unsigned mask = kRectStaysRect_Mask;
+        if (sx != SK_Scalar1 || sy != SK_Scalar1) {
+            mask |= kScale_Mask;
+        }
+        if (tx || ty) {
+            mask |= kTranslate_Mask;
+        }
+        this->setTypeMask(mask);
     }
     // shared cleanup
     fMat[kMPersp2] = kMatrix22Elem;
@@ -817,8 +883,51 @@
     return true;
 }
 
-bool SkMatrix::invert(SkMatrix* inv) const {
-    int         isPersp = this->hasPerspective();
+bool SkMatrix::invertNonIdentity(SkMatrix* inv) const {
+    SkASSERT(!this->isIdentity());
+
+    TypeMask mask = this->getType();
+
+#ifndef SK_IGNORE_FAST_SCALEMATRIX_INVERT
+    if (0 == (mask & ~(kScale_Mask | kTranslate_Mask))) {
+        bool invertible = true;
+        if (inv) {
+            if (mask & kScale_Mask) {
+                SkScalar invX = fMat[kMScaleX];
+                SkScalar invY = fMat[kMScaleY];
+                if (0 == invX || 0 == invY) {
+                    return false;
+                }
+                invX = SkScalarInvert(invX);
+                invY = SkScalarInvert(invY);
+
+                // Must be careful when writing to inv, since it may be the
+                // same memory as this.
+
+                inv->fMat[kMSkewX] = inv->fMat[kMSkewY] =
+                inv->fMat[kMPersp0] = inv->fMat[kMPersp1] = 0;
+
+                inv->fMat[kMScaleX] = invX;
+                inv->fMat[kMScaleY] = invY;
+                inv->fMat[kMPersp2] = kMatrix22Elem;
+                inv->fMat[kMTransX] = -SkScalarMul(fMat[kMTransX], invX);
+                inv->fMat[kMTransY] = -SkScalarMul(fMat[kMTransY], invY);
+
+                inv->setTypeMask(mask | kRectStaysRect_Mask);
+            } else {
+                // translate only
+                inv->setTranslate(-fMat[kMTransX], -fMat[kMTransY]);
+            }
+        } else {    // inv is NULL, just check if we're invertible
+            if (!fMat[kMScaleX] || !fMat[kMScaleY]) {
+                invertible = false;
+            }
+        }
+        return invertible;
+    }
+#endif
+
+    int         isPersp = mask & kPerspective_Mask;
     int         shift;
     SkDetScalar scale = sk_inv_determinant(fMat, isPersp, &shift);
 
@@ -831,7 +940,6 @@
         if (inv == this) {
             inv = &tmp;
         }
-        inv->setTypeMask(kUnknown_Mask);
 
         if (isPersp) {
             shift = 61 - shift;
@@ -843,7 +951,7 @@
             inv->fMat[kMScaleY] = SkScalarMulShift(SkPerspMul(fMat[kMScaleX], fMat[kMPersp2]) - SkPerspMul(fMat[kMTransX],  fMat[kMPersp0]), scale, shift);
             inv->fMat[kMTransY] = SkScalarMulShift(SkScalarMul(fMat[kMTransX], fMat[kMSkewY]) - SkScalarMul(fMat[kMScaleX], fMat[kMTransY]), scale, shift);
 
-            inv->fMat[kMPersp0] = SkScalarMulShift(SkScalarMul(fMat[kMSkewY], fMat[kMPersp1]) - SkScalarMul(fMat[kMScaleY], fMat[kMPersp0]), scale, shift);             
+            inv->fMat[kMPersp0] = SkScalarMulShift(SkScalarMul(fMat[kMSkewY], fMat[kMPersp1]) - SkScalarMul(fMat[kMScaleY], fMat[kMPersp0]), scale, shift);
             inv->fMat[kMPersp1] = SkScalarMulShift(SkScalarMul(fMat[kMSkewX], fMat[kMPersp0]) - SkScalarMul(fMat[kMScaleX], fMat[kMPersp1]), scale, shift);
             inv->fMat[kMPersp2] = SkScalarMulShift(SkScalarMul(fMat[kMScaleX], fMat[kMScaleY]) - SkScalarMul(fMat[kMSkewX], fMat[kMSkewY]), scale, shift);
 #ifdef SK_SCALAR_IS_FIXED
@@ -862,7 +970,6 @@
             }
             inv->fMat[kMPersp2] = SkFixedToFract(inv->fMat[kMPersp2]);
 #endif
-            inv->setTypeMask(kUnknown_Mask);
         } else {   // not perspective
 #ifdef SK_SCALAR_IS_FIXED
             Sk64    tx, ty;
@@ -893,7 +1000,7 @@
             inv->fMat[kMScaleX] = SkMulShift(fMat[kMScaleY], scale, fixedShift);
             inv->fMat[kMSkewX]  = SkMulShift(-fMat[kMSkewX], scale, fixedShift);
             inv->fMat[kMTransX] = SkMulShift(tx.getShiftRight(33 - clzNumer), scale, sk64shift);
-                
+
             inv->fMat[kMSkewY]  = SkMulShift(-fMat[kMSkewY], scale, fixedShift);
             inv->fMat[kMScaleY] = SkMulShift(fMat[kMScaleX], scale, fixedShift);
             inv->fMat[kMTransY] = SkMulShift(ty.getShiftRight(33 - clzNumer), scale, sk64shift);
@@ -902,7 +1009,7 @@
             inv->fMat[kMSkewX] = SkDoubleToFloat(-fMat[kMSkewX] * scale);
             inv->fMat[kMTransX] = mul_diff_scale(fMat[kMSkewX], fMat[kMTransY],
                                      fMat[kMScaleY], fMat[kMTransX], scale);
-                
+
             inv->fMat[kMSkewY] = SkDoubleToFloat(-fMat[kMSkewY] * scale);
             inv->fMat[kMScaleY] = SkDoubleToFloat(fMat[kMScaleX] * scale);
             inv->fMat[kMTransY] = mul_diff_scale(fMat[kMSkewY], fMat[kMTransX],
@@ -911,9 +1018,11 @@
             inv->fMat[kMPersp0] = 0;
             inv->fMat[kMPersp1] = 0;
             inv->fMat[kMPersp2] = kMatrix22Elem;
-            inv->setTypeMask(kUnknown_Mask | kOnlyPerspectiveValid_Mask);
+
         }
 
+        inv->setTypeMask(fTypeMask);
+
         if (inv == &tmp) {
             *(SkMatrix*)this = tmp;
         }
@@ -1173,7 +1282,7 @@
 void SkMatrix::RotTrans_xy(const SkMatrix& m, SkScalar sx, SkScalar sy,
                            SkPoint* pt) {
     SkASSERT((m.getType() & (kAffine_Mask | kPerspective_Mask)) == kAffine_Mask);
-    
+
 #ifdef SK_SCALAR_IS_FIXED
     pt->fX = fixmuladdmul(sx, m.fMat[kMScaleX], sy, m.fMat[kMSkewX]) +
              m.fMat[kMTransX];
@@ -1208,7 +1317,7 @@
                              SkPoint* pt) {
     SkASSERT((m.getType() & (kScale_Mask | kAffine_Mask | kPerspective_Mask))
              == kScale_Mask);
-    
+
     pt->fX = SkScalarMulAdd(sx, m.fMat[kMScaleX], m.fMat[kMTransX]);
     pt->fY = SkScalarMulAdd(sy, m.fMat[kMScaleY], m.fMat[kMTransY]);
 }
@@ -1309,7 +1418,7 @@
 
 int SkPerspIter::next() {
     int n = fCount;
-    
+
     if (0 == n) {
         return 0;
     }
@@ -1340,7 +1449,7 @@
         *p++ = x; x += dx;
         *p++ = y; y += dy;
     }
-    
+
     fCount -= n;
     return n;
 }
@@ -1720,7 +1829,7 @@
         gOnce = true;
     }
     return gIdentity;
-};
+}
 
 const SkMatrix& SkMatrix::InvalidMatrix() {
     static SkMatrix gInvalid;
@@ -1737,7 +1846,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-uint32_t SkMatrix::flatten(void* buffer) const {
+uint32_t SkMatrix::writeToMemory(void* buffer) const {
     // TODO write less for simple matrices
     if (buffer) {
         memcpy(buffer, fMat, 9 * sizeof(SkScalar));
@@ -1745,7 +1854,7 @@
     return 9 * sizeof(SkScalar);
 }
 
-uint32_t SkMatrix::unflatten(const void* buffer) {
+uint32_t SkMatrix::readFromMemory(const void* buffer) {
     if (buffer) {
         memcpy(fMat, buffer, 9 * sizeof(SkScalar));
         this->setTypeMask(kUnknown_Mask);
@@ -1753,15 +1862,15 @@
     return 9 * sizeof(SkScalar);
 }
 
+#ifdef SK_DEVELOPER
 void SkMatrix::dump() const {
     SkString str;
-    this->toDumpString(&str);
+    this->toString(&str);
     SkDebugf("%s\n", str.c_str());
 }
 
-void SkMatrix::toDumpString(SkString* str) const {
-#ifdef SK_CAN_USE_FLOAT
-    str->printf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
+void SkMatrix::toString(SkString* str) const {
+    str->appendf("[%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f][%8.4f %8.4f %8.4f]",
 #ifdef SK_SCALAR_IS_FLOAT
              fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
              fMat[6], fMat[7], fMat[8]);
@@ -1770,9 +1879,57 @@
     SkFixedToFloat(fMat[3]), SkFixedToFloat(fMat[4]), SkFixedToFloat(fMat[5]),
     SkFractToFloat(fMat[6]), SkFractToFloat(fMat[7]), SkFractToFloat(fMat[8]));
 #endif
-#else   // can't use float
-    str->printf("[%x %x %x][%x %x %x][%x %x %x]",
-                fMat[0], fMat[1], fMat[2], fMat[3], fMat[4], fMat[5],
-                fMat[6], fMat[7], fMat[8]);
+}
 #endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkMatrixUtils.h"
+
+bool SkTreatAsSprite(const SkMatrix& mat, int width, int height,
+                     unsigned subpixelBits) {
+    // quick reject on affine or perspective
+    if (mat.getType() & ~(SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask)) {
+        return false;
+    }
+
+    // quick success check
+    if (!subpixelBits && !(mat.getType() & ~SkMatrix::kTranslate_Mask)) {
+        return true;
+    }
+
+    // mapRect supports negative scales, so we eliminate those first
+    if (mat.getScaleX() < 0 || mat.getScaleY() < 0) {
+        return false;
+    }
+
+    SkRect dst;
+    SkIRect isrc = { 0, 0, width, height };
+
+    {
+        SkRect src;
+        src.set(isrc);
+        mat.mapRect(&dst, src);
+    }
+
+    // just apply the translate to isrc
+    isrc.offset(SkScalarRoundToInt(mat.getTranslateX()),
+                SkScalarRoundToInt(mat.getTranslateY()));
+
+    if (subpixelBits) {
+        isrc.fLeft <<= subpixelBits;
+        isrc.fTop <<= subpixelBits;
+        isrc.fRight <<= subpixelBits;
+        isrc.fBottom <<= subpixelBits;
+
+        const float scale = 1 << subpixelBits;
+        dst.fLeft *= scale;
+        dst.fTop *= scale;
+        dst.fRight *= scale;
+        dst.fBottom *= scale;
+    }
+
+    SkIRect idst;
+    dst.round(&idst);
+    return isrc == idst;
 }
diff --git a/src/core/SkMatrixUtils.h b/src/core/SkMatrixUtils.h
new file mode 100644
index 0000000..2074267
--- /dev/null
+++ b/src/core/SkMatrixUtils.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMatrixUtils_DEFINED
+#define SkMatrixUtils_DEFINED
+
+#include "SkMatrix.h"
+
+/**
+ *  Number of subpixel bits used in skia's bilerp.
+ *  See SkBitmapProcState_procs.h and SkBitmapProcState_filter.h
+ */
+#define kSkSubPixelBitsForBilerp   4
+
+/**
+ *  Given a matrix and width/height, return true if the computed dst-rect would
+ *  align such that there is a 1-to-1 coorspondence between src and dst pixels.
+ *  This can be called by drawing code to see if drawBitmap can be turned into
+ *  drawSprite (which is faster).
+ *
+ *  The src-rect is defined to be { 0, 0, width, height }
+ *
+ *  The "closeness" test is based on the subpixelBits parameter. Pass 0 for
+ *  round-to-nearest behavior (e.g. nearest neighbor sampling). Pass the number
+ *  of subpixel-bits to simulate filtering.
+ */
+bool SkTreatAsSprite(const SkMatrix&, int width, int height,
+                     unsigned subpixelBits);
+
+/**
+ *  Calls SkTreatAsSprite() with default subpixelBits value to match Skia's
+ *  filter-bitmap implementation (i.e. kSkSubPixelBitsForBilerp).
+ */
+static inline bool SkTreatAsSpriteFilter(const SkMatrix& matrix,
+                                         int width, int height) {
+    return SkTreatAsSprite(matrix, width, height, kSkSubPixelBitsForBilerp);
+}
+
+#endif
diff --git a/src/core/SkMemory_stdlib.cpp b/src/core/SkMemory_stdlib.cpp
index 033b331..fa13e9c 100644
--- a/src/core/SkMemory_stdlib.cpp
+++ b/src/core/SkMemory_stdlib.cpp
@@ -50,7 +50,7 @@
     // data goes here. The offset to this point must be a multiple of 8
     char fTrailer[sizeof(kBlockTrailerTag)];
 
-    void* add(size_t realSize) 
+    void* add(size_t realSize)
     {
         SkAutoMutexAcquire  ac(get_block_mutex());
         InMutexValidate();
@@ -69,7 +69,7 @@
         memcpy(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag));
         return result;
     }
-    
+
     static void Dump()
     {
         SkAutoMutexAcquire  ac(get_block_mutex());
@@ -128,7 +128,7 @@
         const char* trailer = fTrailer + fSize;
         SkASSERT(memcmp(trailer, kBlockTrailerTag, sizeof(kBlockTrailerTag)) == 0);
     }
-    
+
     static void Validate()
     {
         SkAutoMutexAcquire  ac(get_block_mutex());
@@ -188,7 +188,7 @@
         addr = header;
     }
     size_t realSize = size;
-    if (size) 
+    if (size)
         size += sizeof(SkBlockHeader);
 #endif
 
@@ -221,7 +221,7 @@
     {
         ValidateHeap();
 #ifdef SK_TAG_BLOCKS
-        SkBlockHeader* header = (SkBlockHeader*) 
+        SkBlockHeader* header = (SkBlockHeader*)
             ((char*)p - SK_OFFSETOF(SkBlockHeader, fTrailer));
         header->remove();
 #ifdef SK_TRACK_ALLOC
@@ -244,7 +244,7 @@
     size_t realSize = size;
     size += sizeof(SkBlockHeader);
 #endif
-    
+
     void* p = malloc(size);
     if (p == NULL)
     {
@@ -265,4 +265,3 @@
     ValidateHeap();
     return p;
 }
-
diff --git a/src/core/SkMetaData.cpp b/src/core/SkMetaData.cpp
index 338ec5e..5a494b3 100644
--- a/src/core/SkMetaData.cpp
+++ b/src/core/SkMetaData.cpp
@@ -334,4 +334,3 @@
 void SkMetaData::Rec::Free(Rec* rec) {
     sk_free(rec);
 }
-
diff --git a/src/core/SkOrderedReadBuffer.cpp b/src/core/SkOrderedReadBuffer.cpp
new file mode 100644
index 0000000..daca74c
--- /dev/null
+++ b/src/core/SkOrderedReadBuffer.cpp
@@ -0,0 +1,256 @@
+
+/*
+ * 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 "SkOrderedReadBuffer.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+
+SkOrderedReadBuffer::SkOrderedReadBuffer() : INHERITED() {
+    fMemoryPtr = NULL;
+
+    fBitmapStorage = NULL;
+    fTFArray = NULL;
+    fTFCount = 0;
+
+    fFactoryTDArray = NULL;
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
+    fBitmapDecoder = NULL;
+}
+
+SkOrderedReadBuffer::SkOrderedReadBuffer(const void* data, size_t size) : INHERITED()  {
+    fReader.setMemory(data, size);
+    fMemoryPtr = NULL;
+
+    fBitmapStorage = NULL;
+    fTFArray = NULL;
+    fTFCount = 0;
+
+    fFactoryTDArray = NULL;
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
+    fBitmapDecoder = NULL;
+}
+
+SkOrderedReadBuffer::SkOrderedReadBuffer(SkStream* stream) {
+    const size_t length = stream->getLength();
+    fMemoryPtr = sk_malloc_throw(length);
+    stream->read(fMemoryPtr, length);
+    fReader.setMemory(fMemoryPtr, length);
+
+    fBitmapStorage = NULL;
+    fTFArray = NULL;
+    fTFCount = 0;
+
+    fFactoryTDArray = NULL;
+    fFactoryArray = NULL;
+    fFactoryCount = 0;
+    fBitmapDecoder = NULL;
+}
+
+SkOrderedReadBuffer::~SkOrderedReadBuffer() {
+    sk_free(fMemoryPtr);
+    SkSafeUnref(fBitmapStorage);
+}
+
+bool SkOrderedReadBuffer::readBool() {
+    return fReader.readBool();
+}
+
+SkColor SkOrderedReadBuffer::readColor() {
+    return fReader.readInt();
+}
+
+SkFixed SkOrderedReadBuffer::readFixed() {
+    return fReader.readS32();
+}
+
+int32_t SkOrderedReadBuffer::readInt() {
+    return fReader.readInt();
+}
+
+SkScalar SkOrderedReadBuffer::readScalar() {
+    return fReader.readScalar();
+}
+
+uint32_t SkOrderedReadBuffer::readUInt() {
+    return fReader.readU32();
+}
+
+int32_t SkOrderedReadBuffer::read32() {
+    return fReader.readInt();
+}
+
+char* SkOrderedReadBuffer::readString() {
+    const char* string = fReader.readString();
+    const int32_t length = strlen(string);
+    char* value = (char*)sk_malloc_throw(length + 1);
+    strcpy(value, string);
+    return value;
+}
+
+void* SkOrderedReadBuffer::readEncodedString(size_t* length, SkPaint::TextEncoding encoding) {
+    SkDEBUGCODE(int32_t encodingType = ) fReader.readInt();
+    SkASSERT(encodingType == encoding);
+    *length =  fReader.readInt();
+    void* data = sk_malloc_throw(*length);
+    memcpy(data, fReader.skip(SkAlign4(*length)), *length);
+    return data;
+}
+
+void SkOrderedReadBuffer::readPoint(SkPoint* point) {
+    point->fX = fReader.readScalar();
+    point->fY = fReader.readScalar();
+}
+
+void SkOrderedReadBuffer::readMatrix(SkMatrix* matrix) {
+    fReader.readMatrix(matrix);
+}
+
+void SkOrderedReadBuffer::readIRect(SkIRect* rect) {
+    memcpy(rect, fReader.skip(sizeof(SkIRect)), sizeof(SkIRect));
+}
+
+void SkOrderedReadBuffer::readRect(SkRect* rect) {
+    memcpy(rect, fReader.skip(sizeof(SkRect)), sizeof(SkRect));
+}
+
+void SkOrderedReadBuffer::readRegion(SkRegion* region) {
+    fReader.readRegion(region);
+}
+
+void SkOrderedReadBuffer::readPath(SkPath* path) {
+    fReader.readPath(path);
+}
+
+uint32_t SkOrderedReadBuffer::readByteArray(void* value) {
+    const uint32_t length = fReader.readU32();
+    memcpy(value, fReader.skip(SkAlign4(length)), length);
+    return length;
+}
+
+uint32_t SkOrderedReadBuffer::readColorArray(SkColor* colors) {
+    const uint32_t count = fReader.readU32();
+    const uint32_t byteLength = count * sizeof(SkColor);
+    memcpy(colors, fReader.skip(SkAlign4(byteLength)), byteLength);
+    return count;
+}
+
+uint32_t SkOrderedReadBuffer::readIntArray(int32_t* values) {
+    const uint32_t count = fReader.readU32();
+    const uint32_t byteLength = count * sizeof(int32_t);
+    memcpy(values, fReader.skip(SkAlign4(byteLength)), byteLength);
+    return count;
+}
+
+uint32_t SkOrderedReadBuffer::readPointArray(SkPoint* points) {
+    const uint32_t count = fReader.readU32();
+    const uint32_t byteLength = count * sizeof(SkPoint);
+    memcpy(points, fReader.skip(SkAlign4(byteLength)), byteLength);
+    return count;
+}
+
+uint32_t SkOrderedReadBuffer::readScalarArray(SkScalar* values) {
+    const uint32_t count = fReader.readU32();
+    const uint32_t byteLength = count * sizeof(SkScalar);
+    memcpy(values, fReader.skip(SkAlign4(byteLength)), byteLength);
+    return count;
+}
+
+uint32_t SkOrderedReadBuffer::getArrayCount() {
+    return *(uint32_t*)fReader.peek();
+}
+
+void SkOrderedReadBuffer::readBitmap(SkBitmap* bitmap) {
+    size_t length = this->readUInt();
+    if (length > 0) {
+        // Bitmap was encoded.
+        SkMemoryStream stream(const_cast<void*>(this->skip(length)), length, false);
+        if (fBitmapDecoder != NULL && fBitmapDecoder(&stream, bitmap)) {
+            // Skip the width and height, which were written in case of failure.
+            fReader.skip(2 * sizeof(int));
+        } else {
+            // This bitmap was encoded when written, but we are unable to decode, possibly due to
+            // not having a decoder. Use a placeholder bitmap.
+            SkDebugf("Could not decode bitmap. Resulting bitmap will be red.\n");
+            int width = this->readInt();
+            int height = this->readInt();
+            bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+            bitmap->allocPixels();
+            bitmap->eraseColor(SK_ColorRED);
+        }
+    } else {
+        if (fBitmapStorage) {
+            const uint32_t index = fReader.readU32();
+            fReader.readU32(); // bitmap generation ID (see SkOrderedWriteBuffer::writeBitmap)
+            *bitmap = *fBitmapStorage->getBitmap(index);
+            fBitmapStorage->releaseRef(index);
+        } else {
+            bitmap->unflatten(*this);
+        }
+    }
+}
+
+SkTypeface* SkOrderedReadBuffer::readTypeface() {
+
+    uint32_t index = fReader.readU32();
+    if (0 == index || index > (unsigned)fTFCount) {
+        if (index) {
+            SkDebugf("====== typeface index %d\n", index);
+        }
+        return NULL;
+    } else {
+        SkASSERT(fTFArray);
+        return fTFArray[index - 1];
+    }
+}
+
+SkFlattenable* SkOrderedReadBuffer::readFlattenable() {
+    SkFlattenable::Factory factory = NULL;
+
+    if (fFactoryCount > 0) {
+        int32_t index = fReader.readU32();
+        if (0 == index) {
+            return NULL; // writer failed to give us the flattenable
+        }
+        index -= 1;     // we stored the index-base-1
+        SkASSERT(index < fFactoryCount);
+        factory = fFactoryArray[index];
+    } else if (fFactoryTDArray) {
+        int32_t index = fReader.readU32();
+        if (0 == index) {
+            return NULL; // writer failed to give us the flattenable
+        }
+        index -= 1;     // we stored the index-base-1
+        factory = (*fFactoryTDArray)[index];
+    } else {
+        factory = (SkFlattenable::Factory)readFunctionPtr();
+        if (NULL == factory) {
+            return NULL; // writer failed to give us the flattenable
+        }
+    }
+
+    // if we get here, factory may still be null, but if that is the case, the
+    // failure was ours, not the writer.
+    SkFlattenable* obj = NULL;
+    uint32_t sizeRecorded = fReader.readU32();
+    if (factory) {
+        uint32_t offset = fReader.offset();
+        obj = (*factory)(*this);
+        // check that we read the amount we expected
+        uint32_t sizeRead = fReader.offset() - offset;
+        if (sizeRecorded != sizeRead) {
+            // we could try to fix up the offset...
+            sk_throw();
+        }
+    } else {
+        // we must skip the remaining data
+        fReader.skip(sizeRecorded);
+    }
+    return obj;
+}
diff --git a/src/core/SkOrderedReadBuffer.h b/src/core/SkOrderedReadBuffer.h
new file mode 100644
index 0000000..b3fde17
--- /dev/null
+++ b/src/core/SkOrderedReadBuffer.h
@@ -0,0 +1,127 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOrderedReadBuffer_DEFINED
+#define SkOrderedReadBuffer_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkBitmap.h"
+#include "SkBitmapHeap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkPath.h"
+#include "SkReader32.h"
+#include "SkSerializationHelpers.h"
+
+class SkOrderedReadBuffer : public SkFlattenableReadBuffer {
+public:
+    SkOrderedReadBuffer();
+    SkOrderedReadBuffer(const void* data, size_t size);
+    SkOrderedReadBuffer(SkStream* stream);
+    virtual ~SkOrderedReadBuffer();
+
+    virtual SkOrderedReadBuffer* getOrderedBinaryBuffer() SK_OVERRIDE { return this; }
+
+    SkReader32* getReader32() { return &fReader; }
+
+    uint32_t size() { return fReader.size(); }
+    uint32_t offset() { return fReader.offset(); }
+    bool eof() { return fReader.eof(); }
+    const void* skip(size_t size) { return fReader.skip(size); }
+
+    // primitives
+    virtual bool readBool() SK_OVERRIDE;
+    virtual SkColor readColor() SK_OVERRIDE;
+    virtual SkFixed readFixed() SK_OVERRIDE;
+    virtual int32_t readInt() SK_OVERRIDE;
+    virtual SkScalar readScalar() SK_OVERRIDE;
+    virtual uint32_t readUInt() SK_OVERRIDE;
+    virtual int32_t read32() SK_OVERRIDE;
+
+    // strings -- the caller is responsible for freeing the string contents
+    virtual char* readString() SK_OVERRIDE;
+    virtual void* readEncodedString(size_t* length, SkPaint::TextEncoding encoding) SK_OVERRIDE;
+
+    // common data structures
+    virtual SkFlattenable* readFlattenable() SK_OVERRIDE;
+    virtual void readPoint(SkPoint* point) SK_OVERRIDE;
+    virtual void readMatrix(SkMatrix* matrix) SK_OVERRIDE;
+    virtual void readIRect(SkIRect* rect) SK_OVERRIDE;
+    virtual void readRect(SkRect* rect) SK_OVERRIDE;
+    virtual void readRegion(SkRegion* region) SK_OVERRIDE;
+    virtual void readPath(SkPath* path) SK_OVERRIDE;
+
+    // binary data and arrays
+    virtual uint32_t readByteArray(void* value) SK_OVERRIDE;
+    virtual uint32_t readColorArray(SkColor* colors) SK_OVERRIDE;
+    virtual uint32_t readIntArray(int32_t* values) SK_OVERRIDE;
+    virtual uint32_t readPointArray(SkPoint* points) SK_OVERRIDE;
+    virtual uint32_t readScalarArray(SkScalar* values) SK_OVERRIDE;
+
+    // helpers to get info about arrays and binary data
+    virtual uint32_t getArrayCount() SK_OVERRIDE;
+
+    virtual void readBitmap(SkBitmap* bitmap) SK_OVERRIDE;
+    virtual SkTypeface* readTypeface() SK_OVERRIDE;
+
+    void setBitmapStorage(SkBitmapHeapReader* bitmapStorage) {
+        SkRefCnt_SafeAssign(fBitmapStorage, bitmapStorage);
+    }
+
+    void setTypefaceArray(SkTypeface* array[], int count) {
+        fTFArray = array;
+        fTFCount = count;
+    }
+
+    /**
+     *  Call this with a pre-loaded array of Factories, in the same order as
+     *  were created/written by the writer. SkPicture uses this.
+     */
+    void setFactoryPlayback(SkFlattenable::Factory array[], int count) {
+        fFactoryTDArray = NULL;
+        fFactoryArray = array;
+        fFactoryCount = count;
+    }
+
+    /**
+     *  Call this with an initially empty array, so the reader can cache each
+     *  factory it sees by name. Used by the pipe code in conjunction with
+     *  SkOrderedWriteBuffer::setNamedFactoryRecorder.
+     */
+    void setFactoryArray(SkTDArray<SkFlattenable::Factory>* array) {
+        fFactoryTDArray = array;
+        fFactoryArray = NULL;
+        fFactoryCount = 0;
+    }
+
+    /**
+     *  Provide a function to decode an SkBitmap from an SkStream. Only used if the writer encoded
+     *  the SkBitmap. If the proper decoder cannot be used, a red bitmap with the appropriate size
+     *  will be used.
+     */
+    void setBitmapDecoder(SkSerializationHelpers::DecodeBitmap bitmapDecoder) {
+        fBitmapDecoder = bitmapDecoder;
+    }
+
+private:
+    SkReader32 fReader;
+    void* fMemoryPtr;
+
+    SkBitmapHeapReader* fBitmapStorage;
+    SkTypeface** fTFArray;
+    int        fTFCount;
+
+    SkTDArray<SkFlattenable::Factory>* fFactoryTDArray;
+    SkFlattenable::Factory* fFactoryArray;
+    int                     fFactoryCount;
+
+    SkSerializationHelpers::DecodeBitmap fBitmapDecoder;
+
+    typedef SkFlattenableReadBuffer INHERITED;
+};
+
+#endif // SkOrderedReadBuffer_DEFINED
diff --git a/src/core/SkOrderedWriteBuffer.cpp b/src/core/SkOrderedWriteBuffer.cpp
new file mode 100644
index 0000000..de26587
--- /dev/null
+++ b/src/core/SkOrderedWriteBuffer.cpp
@@ -0,0 +1,323 @@
+
+/*
+ * 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 "SkOrderedWriteBuffer.h"
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkPixelRef.h"
+#include "SkPtrRecorder.h"
+#include "SkStream.h"
+#include "SkTypeface.h"
+
+SkOrderedWriteBuffer::SkOrderedWriteBuffer(size_t minSize)
+    : INHERITED()
+    , fFactorySet(NULL)
+    , fNamedFactorySet(NULL)
+    , fWriter(minSize)
+    , fBitmapHeap(NULL)
+    , fTFSet(NULL)
+    , fBitmapEncoder(NULL) {
+}
+
+SkOrderedWriteBuffer::SkOrderedWriteBuffer(size_t minSize, void* storage, size_t storageSize)
+    : INHERITED()
+    , fFactorySet(NULL)
+    , fNamedFactorySet(NULL)
+    , fWriter(minSize, storage, storageSize)
+    , fBitmapHeap(NULL)
+    , fTFSet(NULL)
+    , fBitmapEncoder(NULL) {
+}
+
+SkOrderedWriteBuffer::~SkOrderedWriteBuffer() {
+    SkSafeUnref(fFactorySet);
+    SkSafeUnref(fNamedFactorySet);
+    SkSafeUnref(fBitmapHeap);
+    SkSafeUnref(fTFSet);
+}
+
+void SkOrderedWriteBuffer::writeByteArray(const void* data, size_t size) {
+    fWriter.write32(size);
+    fWriter.writePad(data, size);
+}
+
+void SkOrderedWriteBuffer::writeBool(bool value) {
+    fWriter.writeBool(value);
+}
+
+void SkOrderedWriteBuffer::writeFixed(SkFixed value) {
+    fWriter.write32(value);
+}
+
+void SkOrderedWriteBuffer::writeScalar(SkScalar value) {
+    fWriter.writeScalar(value);
+}
+
+void SkOrderedWriteBuffer::writeScalarArray(const SkScalar* value, uint32_t count) {
+    fWriter.write32(count);
+    fWriter.write(value, count * sizeof(SkScalar));
+}
+
+void SkOrderedWriteBuffer::writeInt(int32_t value) {
+    fWriter.write32(value);
+}
+
+void SkOrderedWriteBuffer::writeIntArray(const int32_t* value, uint32_t count) {
+    fWriter.write32(count);
+    fWriter.write(value, count * sizeof(int32_t));
+}
+
+void SkOrderedWriteBuffer::writeUInt(uint32_t value) {
+    fWriter.write32(value);
+}
+
+void SkOrderedWriteBuffer::write32(int32_t value) {
+    fWriter.write32(value);
+}
+
+void SkOrderedWriteBuffer::writeString(const char* value) {
+    fWriter.writeString(value);
+}
+
+void SkOrderedWriteBuffer::writeEncodedString(const void* value, size_t byteLength,
+                                              SkPaint::TextEncoding encoding) {
+    fWriter.writeInt(encoding);
+    fWriter.writeInt(byteLength);
+    fWriter.write(value, byteLength);
+}
+
+
+void SkOrderedWriteBuffer::writeColor(const SkColor& color) {
+    fWriter.write32(color);
+}
+
+void SkOrderedWriteBuffer::writeColorArray(const SkColor* color, uint32_t count) {
+    fWriter.write32(count);
+    fWriter.write(color, count * sizeof(SkColor));
+}
+
+void SkOrderedWriteBuffer::writePoint(const SkPoint& point) {
+    fWriter.writeScalar(point.fX);
+    fWriter.writeScalar(point.fY);
+}
+
+void SkOrderedWriteBuffer::writePointArray(const SkPoint* point, uint32_t count) {
+    fWriter.write32(count);
+    fWriter.write(point, count * sizeof(SkPoint));
+}
+
+void SkOrderedWriteBuffer::writeMatrix(const SkMatrix& matrix) {
+    fWriter.writeMatrix(matrix);
+}
+
+void SkOrderedWriteBuffer::writeIRect(const SkIRect& rect) {
+    fWriter.write(&rect, sizeof(SkIRect));
+}
+
+void SkOrderedWriteBuffer::writeRect(const SkRect& rect) {
+    fWriter.writeRect(rect);
+}
+
+void SkOrderedWriteBuffer::writeRegion(const SkRegion& region) {
+    fWriter.writeRegion(region);
+}
+
+void SkOrderedWriteBuffer::writePath(const SkPath& path) {
+    fWriter.writePath(path);
+}
+
+size_t SkOrderedWriteBuffer::writeStream(SkStream* stream, size_t length) {
+    fWriter.write32(length);
+    size_t bytesWritten = fWriter.readFromStream(stream, length);
+    if (bytesWritten < length) {
+        fWriter.reservePad(length - bytesWritten);
+    }
+    return bytesWritten;
+}
+
+bool SkOrderedWriteBuffer::writeToStream(SkWStream* stream) {
+    return fWriter.writeToStream(stream);
+}
+
+void SkOrderedWriteBuffer::writeBitmap(const SkBitmap& bitmap) {
+    // Record information about the bitmap in one of three ways, in order of priority:
+    // 1. If there is an SkBitmapHeap, store it in the heap. The client can avoid serializing the
+    //    bitmap entirely or serialize it later as desired.
+    // 2. Write an encoded version of the bitmap. Afterwards the width and height are written, so
+    //    a reader without a decoder can draw a dummy bitmap of the right size.
+    //    A. If the bitmap has an encoded representation, write it to the stream.
+    //    B. If there is a function for encoding bitmaps, use it.
+    // 3. Call SkBitmap::flatten.
+    // For an encoded bitmap, write the size first. Otherwise store a 0 so the reader knows not to
+    // decode.
+    if (fBitmapHeap != NULL) {
+        SkASSERT(NULL == fBitmapEncoder);
+        // Bitmap was not encoded. Record a zero, implying that the reader need not decode.
+        this->writeUInt(0);
+        int32_t slot = fBitmapHeap->insert(bitmap);
+        fWriter.write32(slot);
+        // crbug.com/155875
+        // The generation ID is not required information. We write it to prevent collisions
+        // in SkFlatDictionary.  It is possible to get a collision when a previously
+        // unflattened (i.e. stale) instance of a similar flattenable is in the dictionary
+        // and the instance currently being written is re-using the same slot from the
+        // bitmap heap.
+        fWriter.write32(bitmap.getGenerationID());
+        return;
+    }
+    bool encoded = false;
+    // Before attempting to encode the SkBitmap, check to see if there is already an encoded
+    // version.
+    SkPixelRef* ref = bitmap.pixelRef();
+    if (ref != NULL) {
+        SkAutoDataUnref data(ref->refEncodedData());
+        if (data.get() != NULL) {
+            // Write the length to indicate that the bitmap was encoded successfully, followed
+            // by the actual data. This must match the case where fBitmapEncoder is used so the
+            // reader need not know the difference.
+            this->writeUInt(data->size());
+            fWriter.writePad(data->data(), data->size());
+            encoded = true;
+        }
+    }
+    if (fBitmapEncoder != NULL && !encoded) {
+        SkASSERT(NULL == fBitmapHeap);
+        SkDynamicMemoryWStream stream;
+        if (fBitmapEncoder(&stream, bitmap)) {
+            uint32_t offset = fWriter.bytesWritten();
+            // Write the length to indicate that the bitmap was encoded successfully, followed
+            // by the actual data. This must match the case where the original data is used so the
+            // reader need not know the difference.
+            size_t length = stream.getOffset();
+            this->writeUInt(length);
+            if (stream.read(fWriter.reservePad(length), 0, length)) {
+                encoded = true;
+            } else {
+                // Writing the stream failed, so go back to original state to store another way.
+                fWriter.rewindToOffset(offset);
+            }
+        }
+    }
+    if (encoded) {
+        // Write the width and height in case the reader does not have a decoder.
+        this->writeInt(bitmap.width());
+        this->writeInt(bitmap.height());
+    } else {
+        // Bitmap was not encoded. Record a zero, implying that the reader need not decode.
+        this->writeUInt(0);
+        bitmap.flatten(*this);
+    }
+}
+
+void SkOrderedWriteBuffer::writeTypeface(SkTypeface* obj) {
+    if (NULL == obj || NULL == fTFSet) {
+        fWriter.write32(0);
+    } else {
+        fWriter.write32(fTFSet->add(obj));
+    }
+}
+
+SkFactorySet* SkOrderedWriteBuffer::setFactoryRecorder(SkFactorySet* rec) {
+    SkRefCnt_SafeAssign(fFactorySet, rec);
+    if (fNamedFactorySet != NULL) {
+        fNamedFactorySet->unref();
+        fNamedFactorySet = NULL;
+    }
+    return rec;
+}
+
+SkNamedFactorySet* SkOrderedWriteBuffer::setNamedFactoryRecorder(SkNamedFactorySet* rec) {
+    SkRefCnt_SafeAssign(fNamedFactorySet, rec);
+    if (fFactorySet != NULL) {
+        fFactorySet->unref();
+        fFactorySet = NULL;
+    }
+    return rec;
+}
+
+SkRefCntSet* SkOrderedWriteBuffer::setTypefaceRecorder(SkRefCntSet* rec) {
+    SkRefCnt_SafeAssign(fTFSet, rec);
+    return rec;
+}
+
+void SkOrderedWriteBuffer::setBitmapHeap(SkBitmapHeap* bitmapHeap) {
+    SkRefCnt_SafeAssign(fBitmapHeap, bitmapHeap);
+    if (bitmapHeap != NULL) {
+        SkASSERT(NULL == fBitmapEncoder);
+        fBitmapEncoder = NULL;
+    }
+}
+
+void SkOrderedWriteBuffer::setBitmapEncoder(SkSerializationHelpers::EncodeBitmap bitmapEncoder) {
+    fBitmapEncoder = bitmapEncoder;
+    if (bitmapEncoder != NULL) {
+        SkASSERT(NULL == fBitmapHeap);
+        SkSafeUnref(fBitmapHeap);
+        fBitmapHeap = NULL;
+    }
+}
+
+void SkOrderedWriteBuffer::writeFlattenable(SkFlattenable* flattenable) {
+    /*
+     *  If we have a factoryset, then the first 32bits tell us...
+     *       0: failure to write the flattenable
+     *      >0: (1-based) index into the SkFactorySet or SkNamedFactorySet
+     *  If we don't have a factoryset, then the first "ptr" is either the
+     *  factory, or null for failure.
+     *
+     *  The distinction is important, since 0-index is 32bits (always), but a
+     *  0-functionptr might be 32 or 64 bits.
+     */
+
+    SkFlattenable::Factory factory = NULL;
+    if (flattenable) {
+        factory = flattenable->getFactory();
+    }
+    if (NULL == factory) {
+        if (fFactorySet != NULL || fNamedFactorySet != NULL) {
+            this->write32(0);
+        } else {
+            this->writeFunctionPtr(NULL);
+        }
+        return;
+    }
+
+    /*
+     *  We can write 1 of 3 versions of the flattenable:
+     *  1.  function-ptr : this is the fastest for the reader, but assumes that
+     *      the writer and reader are in the same process.
+     *  2.  index into fFactorySet : This is assumes the writer will later
+     *      resolve the function-ptrs into strings for its reader. SkPicture
+     *      does exactly this, by writing a table of names (matching the indices)
+     *      up front in its serialized form.
+     *  3.  index into fNamedFactorySet. fNamedFactorySet will also store the
+     *      name. SkGPipe uses this technique so it can write the name to its
+     *      stream before writing the flattenable.
+     */
+    if (fFactorySet) {
+        this->write32(fFactorySet->add(factory));
+    } else if (fNamedFactorySet) {
+        int32_t index = fNamedFactorySet->find(factory);
+        this->write32(index);
+        if (0 == index) {
+            return;
+        }
+    } else {
+        this->writeFunctionPtr((void*)factory);
+    }
+
+    // make room for the size of the flattened object
+    (void)fWriter.reserve(sizeof(uint32_t));
+    // record the current size, so we can subtract after the object writes.
+    uint32_t offset = fWriter.size();
+    // now flatten the object
+    flattenObject(flattenable, *this);
+    uint32_t objSize = fWriter.size() - offset;
+    // record the obj's size
+    *fWriter.peek32(offset - sizeof(uint32_t)) = objSize;
+}
diff --git a/src/core/SkOrderedWriteBuffer.h b/src/core/SkOrderedWriteBuffer.h
new file mode 100644
index 0000000..cd37f47
--- /dev/null
+++ b/src/core/SkOrderedWriteBuffer.h
@@ -0,0 +1,109 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOrderedWriteBuffer_DEFINED
+#define SkOrderedWriteBuffer_DEFINED
+
+#include "SkFlattenableBuffers.h"
+
+#include "SkRefCnt.h"
+#include "SkBitmapHeap.h"
+#include "SkPath.h"
+#include "SkSerializationHelpers.h"
+#include "SkWriter32.h"
+
+class SkBitmap;
+class SkFlattenable;
+class SkFactorySet;
+class SkNamedFactorySet;
+class SkRefCntSet;
+
+class SkOrderedWriteBuffer : public SkFlattenableWriteBuffer {
+public:
+    SkOrderedWriteBuffer(size_t minSize);
+    SkOrderedWriteBuffer(size_t minSize, void* initialStorage,
+                         size_t storageSize);
+    virtual ~SkOrderedWriteBuffer();
+
+    virtual bool isOrderedBinaryBuffer() SK_OVERRIDE { return true; }
+    virtual SkOrderedWriteBuffer* getOrderedBinaryBuffer() SK_OVERRIDE { return this; }
+
+    SkWriter32* getWriter32() { return &fWriter; }
+
+    void writeToMemory(void* dst) { fWriter.flatten(dst); }
+    uint32_t* reserve(size_t size) { return fWriter.reserve(size); }
+    uint32_t size() { return fWriter.size(); }
+
+    virtual void writeByteArray(const void* data, size_t size) SK_OVERRIDE;
+    virtual void writeBool(bool value) SK_OVERRIDE;
+    virtual void writeFixed(SkFixed value) SK_OVERRIDE;
+    virtual void writeScalar(SkScalar value) SK_OVERRIDE;
+    virtual void writeScalarArray(const SkScalar* value, uint32_t count) SK_OVERRIDE;
+    virtual void writeInt(int32_t value) SK_OVERRIDE;
+    virtual void writeIntArray(const int32_t* value, uint32_t count) SK_OVERRIDE;
+    virtual void writeUInt(uint32_t value) SK_OVERRIDE;
+    virtual void write32(int32_t value) SK_OVERRIDE;
+    virtual void writeString(const char* value) SK_OVERRIDE;
+    virtual void writeEncodedString(const void* value, size_t byteLength,
+                                    SkPaint::TextEncoding encoding) SK_OVERRIDE;
+
+    virtual void writeFlattenable(SkFlattenable* flattenable) SK_OVERRIDE;
+    virtual void writeColor(const SkColor& color) SK_OVERRIDE;
+    virtual void writeColorArray(const SkColor* color, uint32_t count) SK_OVERRIDE;
+    virtual void writePoint(const SkPoint& point) SK_OVERRIDE;
+    virtual void writePointArray(const SkPoint* point, uint32_t count) SK_OVERRIDE;
+    virtual void writeMatrix(const SkMatrix& matrix) SK_OVERRIDE;
+    virtual void writeIRect(const SkIRect& rect)SK_OVERRIDE;
+    virtual void writeRect(const SkRect& rect) SK_OVERRIDE;
+    virtual void writeRegion(const SkRegion& region) SK_OVERRIDE;
+    virtual void writePath(const SkPath& path) SK_OVERRIDE;
+    virtual size_t writeStream(SkStream* stream, size_t length) SK_OVERRIDE;
+
+    virtual void writeBitmap(const SkBitmap& bitmap) SK_OVERRIDE;
+    virtual void writeTypeface(SkTypeface* typeface) SK_OVERRIDE;
+
+    virtual bool writeToStream(SkWStream*) SK_OVERRIDE;
+
+    SkFactorySet* setFactoryRecorder(SkFactorySet*);
+    SkNamedFactorySet* setNamedFactoryRecorder(SkNamedFactorySet*);
+
+    SkRefCntSet* getTypefaceRecorder() const { return fTFSet; }
+    SkRefCntSet* setTypefaceRecorder(SkRefCntSet*);
+
+    /**
+     * Set an SkBitmapHeap to store bitmaps rather than flattening.
+     *
+     * Incompatible with an EncodeBitmap function. If an EncodeBitmap function is set, setting an
+     * SkBitmapHeap will set the function to NULL in release mode and crash in debug.
+     */
+    void setBitmapHeap(SkBitmapHeap*);
+
+    /**
+     * Provide a function to encode an SkBitmap to an SkStream. writeBitmap will attempt to use
+     * bitmapEncoder to store the SkBitmap. If the reader does not provide a function to decode, it
+     * will not be able to restore SkBitmaps, but will still be able to read the rest of the stream.
+     *
+     * Incompatible with the SkBitmapHeap. If an encoder is set fBitmapHeap will be set to NULL in
+     * release and crash in debug.
+     */
+    void setBitmapEncoder(SkSerializationHelpers::EncodeBitmap);
+
+private:
+    SkFactorySet* fFactorySet;
+    SkNamedFactorySet* fNamedFactorySet;
+    SkWriter32 fWriter;
+
+    SkBitmapHeap* fBitmapHeap;
+    SkRefCntSet* fTFSet;
+
+    SkSerializationHelpers::EncodeBitmap fBitmapEncoder;
+
+    typedef SkFlattenableWriteBuffer INHERITED;
+};
+
+#endif // SkOrderedWriteBuffer_DEFINED
diff --git a/src/core/SkPackBits.cpp b/src/core/SkPackBits.cpp
index 8edd4c0..7a1444b 100644
--- a/src/core/SkPackBits.cpp
+++ b/src/core/SkPackBits.cpp
@@ -88,7 +88,7 @@
     SkASSERT((unsigned)n <= 128);
     gMemSetBuckets[n] += 1;
     gCounter += 1;
-    
+
     if ((gCounter & 0xFF) == 0) {
         SkDebugf("----- packbits memset stats: ");
         for (size_t i = 0; i < SK_ARRAY_COUNT(gMemSetBuckets); i++) {
@@ -102,7 +102,7 @@
     SkASSERT((unsigned)n <= 128);
     gMemCpyBuckets[n] += 1;
     gCounter += 1;
-    
+
     if ((gCounter & 0x1FF) == 0) {
         SkDebugf("----- packbits memcpy stats: ");
         for (size_t i = 0; i < SK_ARRAY_COUNT(gMemCpyBuckets); i++) {
@@ -207,10 +207,10 @@
             *dst++ = (uint8_t)*src;
             return dst - origDst;
         }
-        
+
         unsigned value = *src;
         const uint16_t* s = src + 1;
-        
+
         if (*s == value) { // accumulate same values...
             do {
                 s++;
@@ -249,10 +249,10 @@
             *dst++ = *src;
             return dst - origDst;
         }
-        
+
         unsigned value = *src;
         const uint8_t* s = src + 1;
-        
+
         if (*s == value) { // accumulate same values...
             do {
                 s++;
@@ -283,7 +283,7 @@
                          uint16_t* SK_RESTRICT dst) {
     uint16_t* origDst = dst;
     const uint8_t* stop = src + srcSize;
-    
+
     while (src < stop) {
         unsigned n = *src++;
         if (n <= 127) {   // repeat count (n + 1)
@@ -305,7 +305,7 @@
                         uint8_t* SK_RESTRICT dst) {
     uint8_t* origDst = dst;
     const uint8_t* stop = src + srcSize;
-    
+
     while (src < stop) {
         unsigned n = *src++;
         if (n <= 127) {   // repeat count (n + 1)
@@ -336,7 +336,7 @@
 
     UnpackState state = CLEAN_STATE;
     size_t      stateCount = 0;
-    
+
     // state 1: do the skip-loop
     while (dstSkip > 0) {
         unsigned n = *src++;
@@ -361,7 +361,7 @@
         }
         dstSkip -= n;
     }
-    
+
     // stage 2: perform any catchup from the skip-stage
     if (stateCount > dstWrite) {
         stateCount = dstWrite;
diff --git a/src/core/SkPaint.cpp b/src/core/SkPaint.cpp
index 56630ae..6c490e4 100644
--- a/src/core/SkPaint.cpp
+++ b/src/core/SkPaint.cpp
@@ -6,14 +6,14 @@
  * found in the LICENSE file.
  */
 
-
-#include <new>
-
 #include "SkPaint.h"
+#include "SkAnnotation.h"
 #include "SkColorFilter.h"
+#include "SkDeviceProperties.h"
 #include "SkFontHost.h"
 #include "SkImageFilter.h"
 #include "SkMaskFilter.h"
+#include "SkMaskGamma.h"
 #include "SkPathEffect.h"
 #include "SkRasterizer.h"
 #include "SkShader.h"
@@ -21,11 +21,14 @@
 #include "SkScalerContext.h"
 #include "SkStroke.h"
 #include "SkTextFormatParams.h"
+#include "SkTextToPathIter.h"
 #include "SkTypeface.h"
 #include "SkXfermode.h"
 #include "SkAutoKern.h"
 #include "SkGlyphCache.h"
 #include "SkPaintDefaults.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
 
 // define this to get a printf for out-of-range parameter in setters
 // e.g. setTextSize(-1)
@@ -57,11 +60,15 @@
     fRasterizer  = NULL;
     fLooper      = NULL;
     fImageFilter = NULL;
+    fAnnotation  = NULL;
     fWidth      = 0;
 #endif
 
     fTextSize   = SkPaintDefaults_TextSize;
     fTextScaleX = SK_Scalar1;
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+    fHintingScaleFactor = SK_Scalar1;
+#endif
     fColor      = SK_ColorBLACK;
     fMiterLimit = SkPaintDefaults_MiterLimit;
     fFlags      = SkPaintDefaults_Flags;
@@ -71,6 +78,7 @@
     fStyle      = kFill_Style;
     fTextEncoding = kUTF8_TextEncoding;
     fHinting    = SkPaintDefaults_Hinting;
+    fPrivFlags  = 0;
 #ifdef SK_BUILD_FOR_ANDROID
     fLanguage = SkLanguage();
     fFontVariant = kDefault_Variant;
@@ -90,6 +98,7 @@
     SkSafeRef(fRasterizer);
     SkSafeRef(fLooper);
     SkSafeRef(fImageFilter);
+    SkSafeRef(fAnnotation);
 }
 
 SkPaint::~SkPaint() {
@@ -102,6 +111,7 @@
     SkSafeUnref(fRasterizer);
     SkSafeUnref(fLooper);
     SkSafeUnref(fImageFilter);
+    SkSafeUnref(fAnnotation);
 }
 
 SkPaint& SkPaint::operator=(const SkPaint& src) {
@@ -116,6 +126,7 @@
     SkSafeRef(src.fRasterizer);
     SkSafeRef(src.fLooper);
     SkSafeRef(src.fImageFilter);
+    SkSafeRef(src.fAnnotation);
 
     SkSafeUnref(fTypeface);
     SkSafeUnref(fPathEffect);
@@ -126,6 +137,7 @@
     SkSafeUnref(fRasterizer);
     SkSafeUnref(fLooper);
     SkSafeUnref(fImageFilter);
+    SkSafeUnref(fAnnotation);
 
 #ifdef SK_BUILD_FOR_ANDROID
     uint32_t oldGenerationID = fGenerationID;
@@ -167,7 +179,7 @@
 
 #ifdef SK_BUILD_FOR_ANDROID
 unsigned SkPaint::getBaseGlyphCount(SkUnichar text) const {
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache* cache = autoCache.getCache();
     return cache->getBaseGlyphCount(text);
 }
@@ -351,6 +363,13 @@
     fTextSkewX = skewX;
 }
 
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+void SkPaint::setHintingScaleFactor(SkScalar hintingScaleFactor) {
+    GEN_ID_INC_EVAL(hintingScaleFactor != fHintingScaleFactor);
+    fHintingScaleFactor = hintingScaleFactor;
+}
+#endif
+
 void SkPaint::setTextEncoding(TextEncoding encoding) {
     if ((unsigned)encoding <= kGlyphID_TextEncoding) {
         GEN_ID_INC_EVAL((unsigned)encoding != fTextEncoding);
@@ -409,6 +428,16 @@
     return imageFilter;
 }
 
+SkAnnotation* SkPaint::setAnnotation(SkAnnotation* annotation) {
+    SkRefCnt_SafeAssign(fAnnotation, annotation);
+    GEN_ID_INC;
+
+    bool isNoDraw = annotation && annotation->isNoDraw();
+    fPrivFlags = SkSetClearMask(fPrivFlags, isNoDraw, kNoDrawAnnotation_PrivFlag);
+
+    return annotation;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkGlyphCache.h"
@@ -421,7 +450,7 @@
 #ifdef SK_BUILD_FOR_ANDROID
 const SkGlyph& SkPaint::getUnicharMetrics(SkUnichar text, const SkMatrix* deviceMatrix) {
     SkGlyphCache* cache;
-    descriptorProc(deviceMatrix, DetachDescProc, &cache, true);
+    descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
 
     const SkGlyph& glyph = cache->getUnicharMetrics(text);
 
@@ -431,7 +460,7 @@
 
 const SkGlyph& SkPaint::getGlyphMetrics(uint16_t glyphId, const SkMatrix* deviceMatrix) {
     SkGlyphCache* cache;
-    descriptorProc(deviceMatrix, DetachDescProc, &cache, true);
+    descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
 
     const SkGlyph& glyph = cache->getGlyphIDMetrics(glyphId);
 
@@ -442,44 +471,13 @@
 const void* SkPaint::findImage(const SkGlyph& glyph, const SkMatrix* deviceMatrix) {
     // See ::detachCache()
     SkGlyphCache* cache;
-    descriptorProc(deviceMatrix, DetachDescProc, &cache, true);
+    descriptorProc(NULL, deviceMatrix, DetachDescProc, &cache, true);
 
     const void* image = cache->findImage(glyph);
 
     SkGlyphCache::AttachCache(cache);
     return image;
 }
-
-int SkPaint::utfToGlyphs(const void* textData, TextEncoding encoding,
-                         size_t byteLength, uint16_t glyphs[]) const {
-
-    SkAutoGlyphCache autoCache(*this, NULL);
-    SkGlyphCache* cache = autoCache.getCache();
-    
-    const char* text = (const char*) textData;
-    const char* stop = text + byteLength;
-    uint16_t* gptr = glyphs;
-    
-    switch (encoding) {
-        case SkPaint::kUTF8_TextEncoding:
-            while (text < stop) {
-                *gptr++ = cache->unicharToGlyph(SkUTF8_NextUnichar(&text));
-            }
-            break;
-        case SkPaint::kUTF16_TextEncoding: {
-            const uint16_t* text16 = (const uint16_t*)text;
-            const uint16_t* stop16 = (const uint16_t*)stop;
-            while (text16 < stop16) {
-                *gptr++ = cache->unicharToGlyph(SkUTF16_NextUnichar(&text16));
-            }
-            break;
-        }
-        default:
-            SkDEBUGFAIL("unknown text encoding");
-    }
-    return gptr - glyphs;
-}
-
 #endif
 
 int SkPaint::textToGlyphs(const void* textData, size_t byteLength,
@@ -497,6 +495,8 @@
         case kUTF16_TextEncoding:
             return SkUTF16_CountUnichars((const uint16_t*)textData,
                                          byteLength >> 1);
+        case kUTF32_TextEncoding:
+            return byteLength >> 2;
         case kGlyphID_TextEncoding:
             return byteLength >> 1;
         default:
@@ -514,7 +514,7 @@
         return byteLength >> 1;
     }
 
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
 
     const char* text = (const char*)textData;
@@ -535,6 +535,14 @@
             }
             break;
         }
+        case kUTF32_TextEncoding: {
+            const int32_t* text32 = (const int32_t*)text;
+            const int32_t* stop32 = (const int32_t*)stop;
+            while (text32 < stop32) {
+                *gptr++ = cache->unicharToGlyph(*text32++);
+            }
+            break;
+        }
         default:
             SkDEBUGFAIL("unknown text encoding");
     }
@@ -560,7 +568,7 @@
         return true;
     }
 
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
 
     switch (this->getTextEncoding()) {
@@ -584,6 +592,16 @@
             }
             break;
         }
+        case SkPaint::kUTF32_TextEncoding: {
+            const int32_t* text = static_cast<const int32_t*>(textData);
+            const int32_t* stop = text + (byteLength >> 2);
+            while (text < stop) {
+                if (0 == cache->unicharToGlyph(*text++)) {
+                    return false;
+                }
+            }
+            break;
+        }
         default:
             SkDEBUGFAIL("unknown text encoding");
             return false;
@@ -600,7 +618,7 @@
     SkASSERT(glyphs != NULL);
     SkASSERT(textData != NULL);
 
-    SkAutoGlyphCache autoCache(*this, NULL);
+    SkAutoGlyphCache autoCache(*this, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
 
     for (int index = 0; index < count; index++) {
@@ -642,6 +660,28 @@
     return cache->getUnicharMetrics(SkUTF16_PrevUnichar((const uint16_t**)text));
 }
 
+static const SkGlyph& sk_getMetrics_utf32_next(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const int32_t* ptr = *(const int32_t**)text;
+    SkUnichar uni = *ptr++;
+    *text = (const char*)ptr;
+    return cache->getUnicharMetrics(uni);
+}
+
+static const SkGlyph& sk_getMetrics_utf32_prev(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const int32_t* ptr = *(const int32_t**)text;
+    SkUnichar uni = *--ptr;
+    *text = (const char*)ptr;
+    return cache->getUnicharMetrics(uni);
+}
+
 static const SkGlyph& sk_getMetrics_glyph_next(SkGlyphCache* cache,
                                                const char** text) {
     SkASSERT(cache != NULL);
@@ -698,6 +738,28 @@
     return cache->getUnicharAdvance(SkUTF16_PrevUnichar((const uint16_t**)text));
 }
 
+static const SkGlyph& sk_getAdvance_utf32_next(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const int32_t* ptr = *(const int32_t**)text;
+    SkUnichar uni = *ptr++;
+    *text = (const char*)ptr;
+    return cache->getUnicharAdvance(uni);
+}
+
+static const SkGlyph& sk_getAdvance_utf32_prev(SkGlyphCache* cache,
+                                               const char** text) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const int32_t* ptr = *(const int32_t**)text;
+    SkUnichar uni = *--ptr;
+    *text = (const char*)ptr;
+    return cache->getUnicharAdvance(uni);
+}
+
 static const SkGlyph& sk_getAdvance_glyph_next(SkGlyphCache* cache,
                                                const char** text) {
     SkASSERT(cache != NULL);
@@ -727,28 +789,32 @@
     static const SkMeasureCacheProc gMeasureCacheProcs[] = {
         sk_getMetrics_utf8_next,
         sk_getMetrics_utf16_next,
+        sk_getMetrics_utf32_next,
         sk_getMetrics_glyph_next,
 
         sk_getMetrics_utf8_prev,
         sk_getMetrics_utf16_prev,
+        sk_getMetrics_utf32_prev,
         sk_getMetrics_glyph_prev,
 
         sk_getAdvance_utf8_next,
         sk_getAdvance_utf16_next,
+        sk_getAdvance_utf32_next,
         sk_getAdvance_glyph_next,
 
         sk_getAdvance_utf8_prev,
         sk_getAdvance_utf16_prev,
+        sk_getAdvance_utf32_prev,
         sk_getAdvance_glyph_prev
     };
 
     unsigned index = this->getTextEncoding();
 
     if (kBackward_TextBufferDirection == tbd) {
-        index += 3;
+        index += 4;
     }
     if (!needFullMetrics && !this->isDevKernText()) {
-        index += 6;
+        index += 8;
     }
 
     SkASSERT(index < SK_ARRAY_COUNT(gMeasureCacheProcs));
@@ -790,6 +856,28 @@
                                     x, y);
 }
 
+static const SkGlyph& sk_getMetrics_utf32_00(SkGlyphCache* cache,
+                                    const char** text, SkFixed, SkFixed) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const int32_t* ptr = *(const int32_t**)text;
+    SkUnichar uni = *ptr++;
+    *text = (const char*)ptr;
+    return cache->getUnicharMetrics(uni);
+}
+
+static const SkGlyph& sk_getMetrics_utf32_xy(SkGlyphCache* cache,
+                                    const char** text, SkFixed x, SkFixed y) {
+    SkASSERT(cache != NULL);
+    SkASSERT(text != NULL);
+
+    const int32_t* ptr = *(const int32_t**)text;
+    SkUnichar uni = *--ptr;
+    *text = (const char*)ptr;
+    return cache->getUnicharMetrics(uni);
+}
+
 static const SkGlyph& sk_getMetrics_glyph_00(SkGlyphCache* cache,
                                          const char** text, SkFixed, SkFixed) {
     SkASSERT(cache != NULL);
@@ -818,16 +906,18 @@
     static const SkDrawCacheProc gDrawCacheProcs[] = {
         sk_getMetrics_utf8_00,
         sk_getMetrics_utf16_00,
+        sk_getMetrics_utf32_00,
         sk_getMetrics_glyph_00,
 
         sk_getMetrics_utf8_xy,
         sk_getMetrics_utf16_xy,
+        sk_getMetrics_utf32_xy,
         sk_getMetrics_glyph_xy
     };
 
     unsigned index = this->getTextEncoding();
     if (fFlags & kSubpixelText_Flag) {
-        index += 3;
+        index += 4;
     }
 
     SkASSERT(index < SK_ARRAY_COUNT(gDrawCacheProcs));
@@ -996,7 +1086,7 @@
         zoomPtr = &zoomMatrix;
     }
 
-    SkAutoGlyphCache    autoCache(*this, zoomPtr);
+    SkAutoGlyphCache    autoCache(*this, NULL, zoomPtr);
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkScalar width = 0;
@@ -1072,7 +1162,7 @@
         ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
     }
 
-    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkAutoGlyphCache    autoCache(*this, NULL, NULL);
     SkGlyphCache*       cache = autoCache.getCache();
 
     SkMeasureCacheProc glyphCacheProc = this->getMeasureCacheProc(tbd, false);
@@ -1160,7 +1250,7 @@
         metrics = &storage;
     }
 
-    this->descriptorProc(zoomPtr, FontMetricsDescProc, metrics, true);
+    this->descriptorProc(NULL, zoomPtr, FontMetricsDescProc, metrics, true);
 
     if (scale) {
         metrics->fTop = SkScalarMul(metrics->fTop, scale);
@@ -1202,7 +1292,7 @@
         ((SkPaint*)this)->setTextSize(SkIntToScalar(kCanonicalTextSizeForPaths));
     }
 
-    SkAutoGlyphCache    autoCache(*this, NULL);
+    SkAutoGlyphCache    autoCache(*this, NULL, NULL);
     SkGlyphCache*       cache = autoCache.getCache();
     SkMeasureCacheProc  glyphCacheProc;
     glyphCacheProc = this->getMeasureCacheProc(kForward_TextBufferDirection,
@@ -1302,7 +1392,7 @@
         return;
     }
 
-    SkTextToPathIter    iter(text, length, *this, false, true);
+    SkTextToPathIter    iter(text, length, *this, false);
     SkMatrix            matrix;
     SkScalar            prevXPos = 0;
 
@@ -1312,16 +1402,47 @@
 
     SkScalar        xpos;
     const SkPath*   iterPath;
-    while ((iterPath = iter.next(&xpos)) != NULL) {
+    while (iter.next(&iterPath, &xpos)) {
         matrix.postTranslate(xpos - prevXPos, 0);
-        path->addPath(*iterPath, matrix);
+        if (iterPath) {
+            path->addPath(*iterPath, matrix);
+        }
         prevXPos = xpos;
     }
 }
 
+void SkPaint::getPosTextPath(const void* textData, size_t length,
+                             const SkPoint pos[], SkPath* path) const {
+    SkASSERT(length == 0 || textData != NULL);
+
+    const char* text = (const char*)textData;
+    if (text == NULL || length == 0 || path == NULL) {
+        return;
+    }
+
+    SkTextToPathIter    iter(text, length, *this, false);
+    SkMatrix            matrix;
+    SkPoint             prevPos;
+    prevPos.set(0, 0);
+
+    matrix.setScale(iter.getPathScale(), iter.getPathScale());
+    path->reset();
+
+    unsigned int    i = 0;
+    const SkPath*   iterPath;
+    while (iter.next(&iterPath, NULL)) {
+        matrix.postTranslate(pos[i].fX - prevPos.fX, pos[i].fY - prevPos.fY);
+        if (iterPath) {
+            path->addPath(*iterPath, matrix);
+        }
+        prevPos = pos[i];
+        i++;
+    }
+}
+
 static void add_flattenable(SkDescriptor* desc, uint32_t tag,
-                            SkFlattenableWriteBuffer* buffer) {
-    buffer->flatten(desc->addEntry(tag, buffer->size(), NULL));
+                            SkOrderedWriteBuffer* buffer) {
+    buffer->writeToMemory(desc->addEntry(tag, buffer->size(), NULL));
 }
 
 // SkFontHost can override this choice in FilterRec()
@@ -1366,7 +1487,6 @@
     return true;
 }
 
-#ifdef SK_USE_COLOR_LUMINANCE
 static SkColor computeLuminanceColor(const SkPaint& paint) {
     SkColor c;
     if (!justAColor(paint, &c)) {
@@ -1377,53 +1497,6 @@
 
 #define assert_byte(x)  SkASSERT(0 == ((x) >> 8))
 
-static U8CPU reduce_lumbits(U8CPU x) {
-    static const uint8_t gReduceBits[] = {
-        0x0, 0x55, 0xAA, 0xFF
-    };
-    assert_byte(x);
-    return gReduceBits[x >> 6];
-}
-
-static unsigned computeLuminance(SkColor c) {
-    int r = SkColorGetR(c);
-    int g = SkColorGetG(c);
-    int b = SkColorGetB(c);
-    // compute luminance
-    // R=0.2126 G=0.7152 B=0.0722
-    // scaling by 127 yields 27, 92, 9
-    int luminance = r * 27 + g * 92 + b * 9;
-    luminance >>= 7;
-    assert_byte(luminance);
-    return luminance;
-}
-
-#else
-// returns 0..kLuminance_Max
-static unsigned computeLuminance(const SkPaint& paint) {
-    SkColor c;
-    if (justAColor(paint, &c)) {
-        int r = SkColorGetR(c);
-        int g = SkColorGetG(c);
-        int b = SkColorGetB(c);
-        // compute luminance
-        // R=0.2126 G=0.7152 B=0.0722
-        // scaling by 127 yields 27, 92, 9
-#if 1
-        int luminance = r * 27 + g * 92 + b * 9;
-        luminance >>= 15 - SkScalerContext::kLuminance_Bits;
-#else
-        int luminance = r * 2 + g * 5 + b * 1;
-        luminance >>= 11 - SkScalerContext::kLuminance_Bits;
-#endif
-        SkASSERT(luminance <= SkScalerContext::kLuminance_Max);
-        return luminance;
-    }
-    // if we're not a single color, return the middle of the luminance range
-    return SkScalerContext::kLuminance_Max >> 1;
-}
-#endif
-
 // Beyond this size, LCD doesn't appreciably improve quality, but it always
 // cost more RAM and draws slower, so we set a cap.
 #ifndef SK_MAX_SIZE_FOR_LCDTEXT
@@ -1453,15 +1526,23 @@
 }
 
 void SkScalerContext::MakeRec(const SkPaint& paint,
-                              const SkMatrix* deviceMatrix, Rec* rec) {
+                              const SkDeviceProperties* deviceProperties,
+                              const SkMatrix* deviceMatrix,
+                              Rec* rec) {
     SkASSERT(deviceMatrix == NULL || !deviceMatrix->hasPerspective());
 
     SkTypeface* typeface = paint.getTypeface();
-    rec->fOrigFontID = SkTypeface::UniqueID(typeface);
+    if (NULL == typeface) {
+        typeface = SkTypeface::GetDefaultTypeface();
+    }
+    rec->fOrigFontID = typeface->uniqueID();
     rec->fFontID = rec->fOrigFontID;
     rec->fTextSize = paint.getTextSize();
     rec->fPreScaleX = paint.getTextScaleX();
     rec->fPreSkewX  = paint.getTextSkewX();
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+    rec->fHintingScaleFactor = paint.getHintingScaleFactor();
+#endif
 
     if (deviceMatrix) {
         rec->fPost2x2[0][0] = sk_relax(deviceMatrix->getScaleX());
@@ -1528,19 +1609,18 @@
 
     rec->fMaskFormat = SkToU8(computeMaskFormat(paint));
 
-    if (SkMask::kLCD16_Format == rec->fMaskFormat ||
-        SkMask::kLCD32_Format == rec->fMaskFormat)
-    {
-        SkFontHost::LCDOrder order = SkFontHost::GetSubpixelOrder();
-        SkFontHost::LCDOrientation orient = SkFontHost::GetSubpixelOrientation();
-        if (SkFontHost::kNONE_LCDOrder == order || tooBigForLCD(*rec)) {
+    SkDeviceProperties::Geometry geometry = deviceProperties
+                                          ? deviceProperties->fGeometry
+                                          : SkDeviceProperties::Geometry::MakeDefault();
+    if (SkMask::kLCD16_Format == rec->fMaskFormat || SkMask::kLCD32_Format == rec->fMaskFormat) {
+        if (!geometry.isOrientationKnown() || !geometry.isLayoutKnown() || tooBigForLCD(*rec)) {
             // eeek, can't support LCD
             rec->fMaskFormat = SkMask::kA8_Format;
         } else {
-            if (SkFontHost::kVertical_LCDOrientation == orient) {
+            if (SkDeviceProperties::Geometry::kVertical_Orientation == geometry.getOrientation()) {
                 flags |= SkScalerContext::kLCD_Vertical_Flag;
             }
-            if (SkFontHost::kBGR_LCDOrder == order) {
+            if (SkDeviceProperties::Geometry::kBGR_Layout == geometry.getLayout()) {
                 flags |= SkScalerContext::kLCD_BGROrder_Flag;
             }
         }
@@ -1565,11 +1645,36 @@
 
     // these modify fFlags, so do them after assigning fFlags
     rec->setHinting(computeHinting(paint));
-#ifdef SK_USE_COLOR_LUMINANCE
+
     rec->setLuminanceColor(computeLuminanceColor(paint));
+
+    if (NULL == deviceProperties) {
+        rec->setDeviceGamma(SK_GAMMA_EXPONENT);
+        rec->setPaintGamma(SK_GAMMA_EXPONENT);
+    } else {
+        rec->setDeviceGamma(deviceProperties->fGamma);
+
+        //For now always set the paint gamma equal to the device gamma.
+        //The math in SkMaskGamma can handle them being different,
+        //but it requires superluminous masks when
+        //Ex : deviceGamma(x) < paintGamma(x) and x is sufficiently large.
+        rec->setPaintGamma(deviceProperties->fGamma);
+    }
+
+#ifdef SK_GAMMA_CONTRAST
+    rec->setContrast(SK_GAMMA_CONTRAST);
 #else
-    rec->setLuminanceBits(computeLuminance(paint));
+    /**
+     * A value of 0.5 for SK_GAMMA_CONTRAST appears to be a good compromise.
+     * With lower values small text appears washed out (though correctly so).
+     * With higher values lcd fringing is worse and the smoothing effect of
+     * partial coverage is diminished.
+     */
+    rec->setContrast(SkFloatToScalar(0.5f));
 #endif
+
+    rec->fReservedAlign = 0;
+    
 #ifdef SK_BUILD_FOR_ANDROID
     rec->fLanguage = paint.getLanguage();
     rec->fFontVariant = paint.getFontVariant();
@@ -1580,52 +1685,93 @@
         they can modify our rec up front, so we don't create duplicate cache
         entries.
      */
-    SkFontHost::FilterRec(rec);
+    SkFontHost::FilterRec(rec, typeface);
 
     // be sure to call PostMakeRec(rec) before you actually use it!
 }
 
 /**
+ * In order to call cachedDeviceLuminance, cachedPaintLuminance, or
+ * cachedMaskGamma the caller must hold the gMaskGammaCacheMutex and continue
+ * to hold it until the returned pointer is refed or forgotten.
+ */
+SK_DECLARE_STATIC_MUTEX(gMaskGammaCacheMutex);
+
+static SkMaskGamma* gLinearMaskGamma = NULL;
+static SkMaskGamma* gMaskGamma = NULL;
+static SkScalar gContrast = SK_ScalarMin;
+static SkScalar gPaintGamma = SK_ScalarMin;
+static SkScalar gDeviceGamma = SK_ScalarMin;
+/**
+ * The caller must hold the gMaskGammaCacheMutex and continue to hold it until
+ * the returned SkMaskGamma pointer is refed or forgotten.
+ */
+static const SkMaskGamma& cachedMaskGamma(SkScalar contrast, SkScalar paintGamma, SkScalar deviceGamma) {
+    if (0 == contrast && SK_Scalar1 == paintGamma && SK_Scalar1 == deviceGamma) {
+        if (NULL == gLinearMaskGamma) {
+            gLinearMaskGamma = SkNEW(SkMaskGamma);
+        }
+        return *gLinearMaskGamma;
+    }
+    if (gContrast != contrast || gPaintGamma != paintGamma || gDeviceGamma != deviceGamma) {
+        SkSafeUnref(gMaskGamma);
+        gMaskGamma = SkNEW_ARGS(SkMaskGamma, (contrast, paintGamma, deviceGamma));
+        gContrast = contrast;
+        gPaintGamma = paintGamma;
+        gDeviceGamma = deviceGamma;
+    }
+    return *gMaskGamma;
+}
+
+/*static*/ void SkPaint::Term() {
+    SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
+
+    SkSafeUnref(gLinearMaskGamma);
+    gLinearMaskGamma = NULL;
+    SkSafeUnref(gMaskGamma);
+    gMaskGamma = NULL;
+    SkDEBUGCODE(gContrast = SK_ScalarMin;)
+    SkDEBUGCODE(gPaintGamma = SK_ScalarMin;)
+    SkDEBUGCODE(gDeviceGamma = SK_ScalarMin;)
+}
+
+/**
  *  We ensure that the rec is self-consistent and efficient (where possible)
  */
-void SkScalerContext::PostMakeRec(SkScalerContext::Rec* rec) {
-
+void SkScalerContext::PostMakeRec(const SkPaint& paint, SkScalerContext::Rec* rec) {
     /**
      *  If we're asking for A8, we force the colorlum to be gray, since that
-     *  that limits the number of unique entries, and the scaler will only
-     *  look at the lum of one of them.
+     *  limits the number of unique entries, and the scaler will only look at
+     *  the lum of one of them.
      */
     switch (rec->fMaskFormat) {
         case SkMask::kLCD16_Format:
         case SkMask::kLCD32_Format: {
-#ifdef SK_USE_COLOR_LUMINANCE
             // filter down the luminance color to a finite number of bits
-            SkColor c = rec->getLuminanceColor();
-            c = SkColorSetRGB(reduce_lumbits(SkColorGetR(c)),
-                              reduce_lumbits(SkColorGetG(c)),
-                              reduce_lumbits(SkColorGetB(c)));
-            rec->setLuminanceColor(c);
-#endif
+            SkColor color = rec->getLuminanceColor();
+            rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
             break;
         }
         case SkMask::kA8_Format: {
-#ifdef SK_USE_COLOR_LUMINANCE
             // filter down the luminance to a single component, since A8 can't
             // use per-component information
-            unsigned lum = computeLuminance(rec->getLuminanceColor());
+
+            SkColor color = rec->getLuminanceColor();
+            U8CPU lum = SkColorSpaceLuminance::computeLuminance(rec->getPaintGamma(), color);
+            //If we are asked to look like LCD, look like LCD.
+            if (!(rec->fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
+                // HACK: Prevents green from being pre-blended as white.
+                lum -= ((255 - lum) * lum) / 255;
+            }
+
             // reduce to our finite number of bits
-            lum = reduce_lumbits(lum);
-            rec->setLuminanceColor(SkColorSetRGB(lum, lum, lum));
-#endif
+            color = SkColorSetRGB(lum, lum, lum);
+            rec->setLuminanceColor(SkMaskGamma::CanonicalColor(color));
             break;
         }
         case SkMask::kBW_Format:
             // No need to differentiate gamma if we're BW
-#ifdef SK_USE_COLOR_LUMINANCE
-            rec->setLuminanceColor(0);
-#else
-            rec->setLuminanceBits(0);
-#endif
+            rec->ignorePreBlend();
             break;
     }
 }
@@ -1643,18 +1789,15 @@
  *  would be for the fontcache lookup to know to ignore the luminance field
  *  entirely, but not sure how to do that and keep it fast.
  */
-void SkPaint::descriptorProc(const SkMatrix* deviceMatrix,
+void SkPaint::descriptorProc(const SkDeviceProperties* deviceProperties,
+                             const SkMatrix* deviceMatrix,
                              void (*proc)(const SkDescriptor*, void*),
                              void* context, bool ignoreGamma) const {
     SkScalerContext::Rec    rec;
 
-    SkScalerContext::MakeRec(*this, deviceMatrix, &rec);
+    SkScalerContext::MakeRec(*this, deviceProperties, deviceMatrix, &rec);
     if (ignoreGamma) {
-#ifdef SK_USE_COLOR_LUMINANCE
         rec.setLuminanceColor(0);
-#else
-        rec.setLuminanceBits(0);
-#endif
     }
 
     size_t          descSize = sizeof(rec);
@@ -1663,9 +1806,9 @@
     SkMaskFilter*   mf = this->getMaskFilter();
     SkRasterizer*   ra = this->getRasterizer();
 
-    SkFlattenableWriteBuffer    peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
-    SkFlattenableWriteBuffer    mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
-    SkFlattenableWriteBuffer    raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+    SkOrderedWriteBuffer    peBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+    SkOrderedWriteBuffer    mfBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
+    SkOrderedWriteBuffer    raBuffer(MIN_SIZE_FOR_EFFECT_BUFFER);
 
     if (pe) {
         peBuffer.writeFlattenable(pe);
@@ -1679,6 +1822,11 @@
         descSize += mfBuffer.size();
         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);
@@ -1689,8 +1837,8 @@
 
     ///////////////////////////////////////////////////////////////////////////
     // Now that we're done tweaking the rec, call the PostMakeRec cleanup
-    SkScalerContext::PostMakeRec(&rec);
-    
+    SkScalerContext::PostMakeRec(*this, &rec);
+
     descSize += SkDescriptor::ComputeOverhead(entryCount);
 
     SkAutoDescriptor    ad(descSize);
@@ -1724,15 +1872,15 @@
         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);
@@ -1745,7 +1893,7 @@
             add_flattenable(desc1, kRasterizer_SkDescriptorTag, &raBuffer);
             add_flattenable(desc2, kRasterizer_SkDescriptorTag, &raBuffer);
         }
-        
+
         SkASSERT(descSize == desc1->getLength());
         SkASSERT(descSize == desc2->getLength());
         desc1->computeChecksum();
@@ -1754,16 +1902,29 @@
         SkASSERT(!memcmp(desc, desc2, descSize));
     }
 #endif
-    
+
     proc(desc, context);
 }
 
-SkGlyphCache* SkPaint::detachCache(const SkMatrix* deviceMatrix) const {
+SkGlyphCache* SkPaint::detachCache(const SkDeviceProperties* deviceProperties,
+                                   const SkMatrix* deviceMatrix) const {
     SkGlyphCache* cache;
-    this->descriptorProc(deviceMatrix, DetachDescProc, &cache);
+    this->descriptorProc(deviceProperties, deviceMatrix, DetachDescProc, &cache, false);
     return cache;
 }
 
+/**
+ * Expands fDeviceGamma, fPaintGamma, fContrast, and fLumBits into a mask pre-blend.
+ */
+//static
+SkMaskGamma::PreBlend SkScalerContext::GetMaskPreBlend(const SkScalerContext::Rec& rec) {
+    SkAutoMutexAcquire ama(gMaskGammaCacheMutex);
+    const SkMaskGamma& maskGamma = cachedMaskGamma(rec.getContrast(),
+                                                   rec.getPaintGamma(),
+                                                   rec.getDeviceGamma());
+    return maskGamma.preBlend(rec.getLuminanceColor());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkStream.h"
@@ -1802,11 +1963,15 @@
 
 enum FlatFlags {
     kHasTypeface_FlatFlag   = 0x01,
-    kHasEffects_FlatFlag    = 0x02
+    kHasEffects_FlatFlag    = 0x02,
 };
 
 // The size of a flat paint's POD fields
-static const uint32_t kPODPaintSize =   5 * sizeof(SkScalar) +
+// Include an SkScalar for hinting scale factor whether it is
+// supported or not so that an SKP is valid whether it was
+// created with support or not.
+
+static const uint32_t kPODPaintSize =   6 * sizeof(SkScalar) +
                                         1 * sizeof(SkColor) +
                                         1 * sizeof(uint16_t) +
                                         6 * sizeof(uint8_t);
@@ -1826,34 +1991,60 @@
         asint(this->getColorFilter()) |
         asint(this->getRasterizer()) |
         asint(this->getLooper()) |
+        asint(this->getAnnotation()) |
         asint(this->getImageFilter())) {
         flatFlags |= kHasEffects_FlatFlag;
     }
 
-    SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
-    uint32_t* ptr = buffer.reserve(kPODPaintSize);
 
-    ptr = write_scalar(ptr, this->getTextSize());
-    ptr = write_scalar(ptr, this->getTextScaleX());
-    ptr = write_scalar(ptr, this->getTextSkewX());
-    ptr = write_scalar(ptr, this->getStrokeWidth());
-    ptr = write_scalar(ptr, this->getStrokeMiter());
-    *ptr++ = this->getColor();
-    // previously flags:16, textAlign:8, flatFlags:8
-    // now flags:16, hinting:4, textAlign:4, flatFlags:8
-    *ptr++ = (this->getFlags() << 16) |
-             // hinting added later. 0 in this nibble means use the default.
-             ((this->getHinting()+1) << 12) |
-             (this->getTextAlign() << 8) |
-             flatFlags;
-    *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
-                    this->getStyle(), this->getTextEncoding());
+    if (buffer.isOrderedBinaryBuffer()) {
+        SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
+        uint32_t* ptr = buffer.getOrderedBinaryBuffer()->reserve(kPODPaintSize);
 
-#ifdef SK_BUILD_FOR_ANDROID
-    buffer.writeInt(this->getFontVariant());
-    const SkString& langTag = this->getLanguage().getTag();
-    buffer.writeString(langTag.c_str(), langTag.size());
+        ptr = write_scalar(ptr, this->getTextSize());
+        ptr = write_scalar(ptr, this->getTextScaleX());
+        ptr = write_scalar(ptr, this->getTextSkewX());
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+        ptr = write_scalar(ptr, this->getHintingScaleFactor());
+#else
+        // Dummy value.
+        ptr = write_scalar(ptr, SK_Scalar1);
 #endif
+        ptr = write_scalar(ptr, this->getStrokeWidth());
+        ptr = write_scalar(ptr, this->getStrokeMiter());
+        *ptr++ = this->getColor();
+        // previously flags:16, textAlign:8, flatFlags:8
+        // now flags:16, hinting:4, textAlign:4, flatFlags:8
+        *ptr++ = (this->getFlags() << 16) |
+                 // hinting added later. 0 in this nibble means use the default.
+                 ((this->getHinting()+1) << 12) |
+                 (this->getTextAlign() << 8) |
+                 flatFlags;
+        *ptr++ = pack_4(this->getStrokeCap(), this->getStrokeJoin(),
+                        this->getStyle(), this->getTextEncoding());
+    } else {
+        buffer.writeScalar(fTextSize);
+        buffer.writeScalar(fTextScaleX);
+        buffer.writeScalar(fTextSkewX);
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+        buffer.writeScalar(fHintingScaleFactor);
+#else
+        // Dummy value.
+        buffer.writeScalar(SK_Scalar1);
+#endif
+        buffer.writeScalar(fWidth);
+        buffer.writeScalar(fMiterLimit);
+        buffer.writeColor(fColor);
+        buffer.writeUInt(fFlags);
+        buffer.writeUInt(fHinting);
+        buffer.writeUInt(fTextAlign);
+        buffer.writeUInt(flatFlags);
+
+        buffer.writeUInt(fCapType);
+        buffer.writeUInt(fJoinType);
+        buffer.writeUInt(fStyle);
+        buffer.writeUInt(fTextEncoding);
+    }
 
     // now we're done with ptr and the (pre)reserved space. If we need to write
     // additional fields, use the buffer directly
@@ -1869,50 +2060,74 @@
         buffer.writeFlattenable(this->getRasterizer());
         buffer.writeFlattenable(this->getLooper());
         buffer.writeFlattenable(this->getImageFilter());
+        buffer.writeFlattenable(this->getAnnotation());
     }
 }
 
 void SkPaint::unflatten(SkFlattenableReadBuffer& buffer) {
-    SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
-    const void* podData = buffer.skip(kPODPaintSize);
-    const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
+    fPrivFlags = 0;
 
-    // the order we read must match the order we wrote in flatten()
-    this->setTextSize(read_scalar(pod));
-    this->setTextScaleX(read_scalar(pod));
-    this->setTextSkewX(read_scalar(pod));
-    this->setStrokeWidth(read_scalar(pod));
-    this->setStrokeMiter(read_scalar(pod));
-    this->setColor(*pod++);
+    uint8_t flatFlags = 0;
+    if (buffer.isOrderedBinaryBuffer()) {
+        SkASSERT(SkAlign4(kPODPaintSize) == kPODPaintSize);
+        const void* podData = buffer.getOrderedBinaryBuffer()->skip(kPODPaintSize);
+        const uint32_t* pod = reinterpret_cast<const uint32_t*>(podData);
 
-    // previously flags:16, textAlign:8, flatFlags:8
-    // now flags:16, hinting:4, textAlign:4, flatFlags:8
-    uint32_t tmp = *pod++;
-    this->setFlags(tmp >> 16);
+        // the order we read must match the order we wrote in flatten()
+        this->setTextSize(read_scalar(pod));
+        this->setTextScaleX(read_scalar(pod));
+        this->setTextSkewX(read_scalar(pod));
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+        this->setHintingScaleFactor(read_scalar(pod));
+#else
+        // Skip the hinting scalar factor, which is not supported.
+        read_scalar(pod);
+#endif
+        this->setStrokeWidth(read_scalar(pod));
+        this->setStrokeMiter(read_scalar(pod));
+        this->setColor(*pod++);
 
-    if (buffer.getPictureVersion() == PICTURE_VERSION_ICS) {
-        this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xFF));
-        this->setHinting(SkPaintDefaults_Hinting);
-    } else {
+        // previously flags:16, textAlign:8, flatFlags:8
+        // now flags:16, hinting:4, textAlign:4, flatFlags:8
+        uint32_t tmp = *pod++;
+        this->setFlags(tmp >> 16);
+
         // hinting added later. 0 in this nibble means use the default.
         uint32_t hinting = (tmp >> 12) & 0xF;
         this->setHinting(0 == hinting ? kNormal_Hinting : static_cast<Hinting>(hinting-1));
 
         this->setTextAlign(static_cast<Align>((tmp >> 8) & 0xF));
-    }
 
-    uint8_t flatFlags = tmp & 0xFF;
+        flatFlags = tmp & 0xFF;
 
-    tmp = *pod++;
-    this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
-    this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
-    this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
-    this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
-
-#ifdef SK_BUILD_FOR_ANDROID
-    this->setFontVariant(SkPaint::FontVariant(buffer.readInt()));
-    this->setLanguage(SkLanguage(buffer.readString()));
+        tmp = *pod++;
+        this->setStrokeCap(static_cast<Cap>((tmp >> 24) & 0xFF));
+        this->setStrokeJoin(static_cast<Join>((tmp >> 16) & 0xFF));
+        this->setStyle(static_cast<Style>((tmp >> 8) & 0xFF));
+        this->setTextEncoding(static_cast<TextEncoding>((tmp >> 0) & 0xFF));
+    } else {
+        this->setTextSize(buffer.readScalar());
+        this->setTextScaleX(buffer.readScalar());
+        this->setTextSkewX(buffer.readScalar());
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+        this->setHintingScaleFactor(buffer.readScalar());
+#else
+        // Skip the hinting scalar factor, which is not supported.
+        buffer.readScalar();
 #endif
+        this->setStrokeWidth(buffer.readScalar());
+        this->setStrokeMiter(buffer.readScalar());
+        this->setColor(buffer.readColor());
+        this->setFlags(buffer.readUInt());
+        this->setHinting(static_cast<SkPaint::Hinting>(buffer.readUInt()));
+        this->setTextAlign(static_cast<SkPaint::Align>(buffer.readUInt()));
+        flatFlags = buffer.readUInt();
+
+        this->setStrokeCap(static_cast<SkPaint::Cap>(buffer.readUInt()));
+        this->setStrokeJoin(static_cast<SkPaint::Join>(buffer.readUInt()));
+        this->setStyle(static_cast<SkPaint::Style>(buffer.readUInt()));
+        this->setTextEncoding(static_cast<SkPaint::TextEncoding>(buffer.readUInt()));
+    }
 
     if (flatFlags & kHasTypeface_FlatFlag) {
         this->setTypeface(buffer.readTypeface());
@@ -1921,17 +2136,15 @@
     }
 
     if (flatFlags & kHasEffects_FlatFlag) {
-        SkSafeUnref(this->setPathEffect((SkPathEffect*) buffer.readFlattenable()));
-        SkSafeUnref(this->setShader((SkShader*) buffer.readFlattenable()));
-        SkSafeUnref(this->setXfermode((SkXfermode*) buffer.readFlattenable()));
-        SkSafeUnref(this->setMaskFilter((SkMaskFilter*) buffer.readFlattenable()));
-        SkSafeUnref(this->setColorFilter((SkColorFilter*) buffer.readFlattenable()));
-        SkSafeUnref(this->setRasterizer((SkRasterizer*) buffer.readFlattenable()));
-        SkSafeUnref(this->setLooper((SkDrawLooper*) buffer.readFlattenable()));
-        if (buffer.getPictureVersion() != PICTURE_VERSION_ICS)
-            SkSafeUnref(this->setImageFilter((SkImageFilter*) buffer.readFlattenable()));
-        else
-            this->setImageFilter(NULL);
+        SkSafeUnref(this->setPathEffect(buffer.readFlattenableT<SkPathEffect>()));
+        SkSafeUnref(this->setShader(buffer.readFlattenableT<SkShader>()));
+        SkSafeUnref(this->setXfermode(buffer.readFlattenableT<SkXfermode>()));
+        SkSafeUnref(this->setMaskFilter(buffer.readFlattenableT<SkMaskFilter>()));
+        SkSafeUnref(this->setColorFilter(buffer.readFlattenableT<SkColorFilter>()));
+        SkSafeUnref(this->setRasterizer(buffer.readFlattenableT<SkRasterizer>()));
+        SkSafeUnref(this->setLooper(buffer.readFlattenableT<SkDrawLooper>()));
+        SkSafeUnref(this->setImageFilter(buffer.readFlattenableT<SkImageFilter>()));
+        SkSafeUnref(this->setAnnotation(buffer.readFlattenableT<SkAnnotation>()));
     } else {
         this->setPathEffect(NULL);
         this->setShader(NULL);
@@ -1985,75 +2198,51 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkPaint::getFillPath(const SkPath& src, SkPath* dst) const {
-    SkPath          effectPath, strokePath;
-    const SkPath*   path = &src;
+bool SkPaint::getFillPath(const SkPath& src, SkPath* dst,
+                          const SkRect* cullRect) const {
+    SkStrokeRec rec(*this);
 
-    SkScalar width = this->getStrokeWidth();
+    const SkPath* srcPtr = &src;
+    SkPath tmpPath;
 
-    switch (this->getStyle()) {
-        case SkPaint::kFill_Style:
-            width = -1; // mark it as no-stroke
-            break;
-        case SkPaint::kStrokeAndFill_Style:
-            if (width == 0) {
-                width = -1; // mark it as no-stroke
-            }
-            break;
-        case SkPaint::kStroke_Style:
-            break;
-        default:
-            SkDEBUGFAIL("unknown paint style");
+    if (fPathEffect && fPathEffect->filterPath(&tmpPath, src, &rec, cullRect)) {
+        srcPtr = &tmpPath;
     }
 
-    if (this->getPathEffect()) {
-        // lie to the pathEffect if our style is strokeandfill, so that it treats us as just fill
-        if (this->getStyle() == SkPaint::kStrokeAndFill_Style) {
-            width = -1; // mark it as no-stroke
-        }
-
-        if (this->getPathEffect()->filterPath(&effectPath, src, &width)) {
-            path = &effectPath;
-        }
-
-        // restore the width if we earlier had to lie, and if we're still set to no-stroke
-        // note: if we're now stroke (width >= 0), then the pathEffect asked for that change
-        // and we want to respect that (i.e. don't overwrite their setting for width)
-        if (this->getStyle() == SkPaint::kStrokeAndFill_Style && width < 0) {
-            width = this->getStrokeWidth();
-            if (width == 0) {
-                width = -1;
-            }
+    if (!rec.applyToPath(dst, *srcPtr)) {
+        if (srcPtr == &tmpPath) {
+            // If path's were copy-on-write, this trick would not be needed.
+            // As it is, we want to save making a deep-copy from tmpPath -> dst
+            // since we know we're just going to delete tmpPath when we return,
+            // so the swap saves that copy.
+            dst->swap(tmpPath);
+        } else {
+            *dst = *srcPtr;
         }
     }
-
-    if (width > 0 && !path->isEmpty()) {
-        SkStroke stroker(*this, width);
-        stroker.strokePath(*path, &strokePath);
-        path = &strokePath;
-    }
-
-    if (path == &src) {
-        *dst = src;
-    } else {
-        SkASSERT(path == &effectPath || path == &strokePath);
-        dst->swap(*(SkPath*)path);
-    }
-
-    return width != 0;  // return true if we're filled, or false if we're hairline (width == 0)
+    return !rec.isHairlineStyle();
 }
 
-const SkRect& SkPaint::doComputeFastBounds(const SkRect& src,
-                                                 SkRect* storage) const {
+const SkRect& SkPaint::doComputeFastBounds(const SkRect& origSrc,
+                                           SkRect* storage,
+                                           Style style) const {
     SkASSERT(storage);
 
+    const SkRect* src = &origSrc;
+
     if (this->getLooper()) {
         SkASSERT(this->getLooper()->canComputeFastBounds(*this));
-        this->getLooper()->computeFastBounds(*this, src, storage);
+        this->getLooper()->computeFastBounds(*this, *src, storage);
         return *storage;
     }
 
-    if (this->getStyle() != SkPaint::kFill_Style) {
+    SkRect tmpSrc;
+    if (this->getPathEffect()) {
+        this->getPathEffect()->computeFastBounds(&tmpSrc, origSrc);
+        src = &tmpSrc;
+    }
+
+    if (kFill_Style != style) {
         // since we're stroked, outset the rect by the radius (and join type)
         SkScalar radius = SkScalarHalf(this->getStrokeWidth());
         if (0 == radius) {  // hairline
@@ -2064,13 +2253,12 @@
                 radius = SkScalarMul(radius, scale);
             }
         }
-        storage->set(src.fLeft - radius, src.fTop - radius,
-                     src.fRight + radius, src.fBottom + radius);
+        storage->set(src->fLeft - radius, src->fTop - radius,
+                     src->fRight + radius, src->fBottom + radius);
     } else {
-        *storage = src;
+        *storage = *src;
     }
 
-    // check the mask filter
     if (this->getMaskFilter()) {
         this->getMaskFilter()->computeFastBounds(*storage, storage);
     }
@@ -2087,21 +2275,19 @@
 
 SkTextToPathIter::SkTextToPathIter( const char text[], size_t length,
                                     const SkPaint& paint,
-                                    bool applyStrokeAndPathEffects,
-                                    bool forceLinearTextOn) : fPaint(paint) {
+                                    bool applyStrokeAndPathEffects)
+                                    : fPaint(paint) {
     fGlyphCacheProc = paint.getMeasureCacheProc(SkPaint::kForward_TextBufferDirection,
                                                 true);
 
-    if (forceLinearTextOn) {
-        fPaint.setLinearText(true);
-    }
+    fPaint.setLinearText(true);
     fPaint.setMaskFilter(NULL);   // don't want this affecting our path-cache lookup
 
     if (fPaint.getPathEffect() == NULL && !has_thick_frame(fPaint)) {
         applyStrokeAndPathEffects = false;
     }
 
-    // can't use our canonical size if we need to apply patheffects/strokes
+    // can't use our canonical size if we need to apply patheffects
     if (fPaint.getPathEffect() == NULL) {
         fPaint.setTextSize(SkIntToScalar(SkPaint::kCanonicalTextSizeForPaths));
         fScale = paint.getTextSize() / SkPaint::kCanonicalTextSizeForPaths;
@@ -2117,7 +2303,7 @@
         fPaint.setPathEffect(NULL);
     }
 
-    fCache = fPaint.detachCache(NULL);
+    fCache = fPaint.detachCache(NULL, NULL);
 
     SkPaint::Style  style = SkPaint::kFill_Style;
     SkPathEffect*   pe = NULL;
@@ -2147,7 +2333,7 @@
 
     fText = text;
     fStop = text + length;
-    
+
     fXYIndex = paint.isVerticalText() ? 1 : 0;
 }
 
@@ -2155,21 +2341,28 @@
     SkGlyphCache::AttachCache(fCache);
 }
 
-const SkPath* SkTextToPathIter::next(SkScalar* xpos) {
-    while (fText < fStop) {
+bool SkTextToPathIter::next(const SkPath** path, SkScalar* xpos) {
+    if (fText < fStop) {
         const SkGlyph& glyph = fGlyphCacheProc(fCache, &fText);
 
         fXPos += SkScalarMul(SkFixedToScalar(fPrevAdvance + fAutoKern.adjust(glyph)), fScale);
         fPrevAdvance = advance(glyph, fXYIndex);   // + fPaint.getTextTracking();
 
         if (glyph.fWidth) {
-            if (xpos) {
-                *xpos = fXPos;
+            if (path) {
+                *path = fCache->findPath(glyph);
             }
-            return fCache->findPath(glyph);
+        } else {
+            if (path) {
+                *path = NULL;
+            }
         }
+        if (xpos) {
+            *xpos = fXPos;
+        }
+        return true;
     }
-    return NULL;
+    return false;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -2199,51 +2392,7 @@
 
 //////////// Move these to their own file soon.
 
-bool SkImageFilter::filterImage(Proxy* proxy, const SkBitmap& src,
-                                const SkMatrix& ctm,
-                                SkBitmap* result, SkIPoint* loc) {
-    SkASSERT(proxy);
-    SkASSERT(result);
-    SkASSERT(loc);
-    /*
-     *  Give the proxy first shot at the filter. If it returns false, ask
-     *  the filter to do it.
-     */
-    return proxy->filterImage(this, src, ctm, result, loc) ||
-           this->onFilterImage(proxy, src, ctm, result, loc);
-}
-
-bool SkImageFilter::filterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                 SkIRect* dst) {
-    SkASSERT(&src);
-    SkASSERT(dst);
-    return this->onFilterBounds(src, ctm, dst);
-}
-
-bool SkImageFilter::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
-                                  SkBitmap*, SkIPoint*) {
-    return false;
-}
-
-bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                   SkIRect* dst) {
-    *dst = src;
-    return true;
-}
-
-bool SkImageFilter::asABlur(SkSize* sigma) const {
-    return false;
-}
-
-bool SkImageFilter::asAnErode(SkISize* radius) const {
-    return false;
-}
-
-bool SkImageFilter::asADilate(SkISize* radius) const {
-    return false;
-}
-
-//////
+SK_DEFINE_INST_COUNT(SkDrawLooper)
 
 bool SkDrawLooper::canComputeFastBounds(const SkPaint& paint) {
     SkCanvas canvas;
@@ -2266,7 +2415,7 @@
 void SkDrawLooper::computeFastBounds(const SkPaint& paint, const SkRect& src,
                                      SkRect* dst) {
     SkCanvas canvas;
-    
+
     this->init(&canvas);
     for (bool firstTime = true;; firstTime = false) {
         SkPaint p(paint);
@@ -2287,4 +2436,3 @@
         }
     }
 }
-
diff --git a/src/core/SkPaintPriv.cpp b/src/core/SkPaintPriv.cpp
new file mode 100644
index 0000000..ce05389
--- /dev/null
+++ b/src/core/SkPaintPriv.cpp
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkPaintPriv.h"
+
+#include "SkBitmap.h"
+#include "SkColorFilter.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+
+bool isPaintOpaque(const SkPaint* paint,
+                   const SkBitmap* bmpReplacesShader) {
+    // TODO: SkXfermode should have a virtual isOpaque method, which would
+    // make it possible to test modes that do not have a Coeff representation.
+
+    if (!paint) {
+        return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
+    }
+
+    SkXfermode::Coeff srcCoeff, dstCoeff;
+    if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
+        if (SkXfermode::kDA_Coeff == srcCoeff || SkXfermode::kDC_Coeff == srcCoeff ||
+            SkXfermode::kIDA_Coeff == srcCoeff || SkXfermode::kIDC_Coeff == srcCoeff) {
+            return false;
+        }
+        switch (dstCoeff) {
+        case SkXfermode::kZero_Coeff:
+            return true;
+        case SkXfermode::kISA_Coeff:
+            if (paint->getAlpha() != 255) {
+                break;
+            }
+            if (bmpReplacesShader) {
+                if (!bmpReplacesShader->isOpaque()) {
+                    break;
+                }
+            } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
+                break;
+            }
+            if (paint->getColorFilter() &&
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSA_Coeff:
+            if (paint->getAlpha() != 0) {
+                break;
+            }
+            if (paint->getColorFilter() &&
+                ((paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        case SkXfermode::kSC_Coeff:
+            if (paint->getColor() != 0) { // all components must be 0
+                break;
+            }
+            if (bmpReplacesShader || paint->getShader()) {
+                break;
+            }
+            if (paint->getColorFilter() && (
+                (paint->getColorFilter()->getFlags() &
+                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
+                break;
+            }
+            return true;
+        default:
+            break;
+        }
+    }
+    return false;
+}
diff --git a/src/core/SkPaintPriv.h b/src/core/SkPaintPriv.h
new file mode 100644
index 0000000..38c9063
--- /dev/null
+++ b/src/core/SkPaintPriv.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkPaintPriv_DEFINED
+#define SkPaintPriv_DEFINED
+
+class SkBitmap;
+class SkPaint;
+
+#include "SkTypes.h"
+/** Returns true if draw calls that use the paint will completely occlude
+    canvas contents that are covered by the draw.
+    @param paint The paint to be analyzed, NULL is equivalent to
+        the default paint.
+    @param bmpReplacesShader a bitmap to be used in place of the paint's
+        shader.
+    @return true if paint is opaque
+*/
+bool isPaintOpaque(const SkPaint* paint,
+                   const SkBitmap* bmpReplacesShader = NULL);
+#endif
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 3436996..df9ca9e 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -8,9 +8,72 @@
 
 
 #include "SkPath.h"
-#include "SkReader32.h"
-#include "SkWriter32.h"
+#include "SkBuffer.h"
 #include "SkMath.h"
+#include "SkPathRef.h"
+#include "SkRRect.h"
+#include "SkThread.h"
+
+////////////////////////////////////////////////////////////////////////////
+
+#if SK_DEBUG_PATH_REF
+
+SkPath::PathRefDebugRef::PathRefDebugRef(SkPath* owner) : fOwner(owner) {}
+
+SkPath::PathRefDebugRef::PathRefDebugRef(SkPathRef* pr, SkPath* owner)
+: fPathRef(pr)
+, fOwner(owner) {
+    pr->addOwner(owner);
+}
+
+SkPath::PathRefDebugRef::~PathRefDebugRef() {
+    fPathRef->removeOwner(fOwner);
+}
+
+void SkPath::PathRefDebugRef::reset(SkPathRef* ref) {
+    bool diff = (ref != fPathRef.get());
+    if (diff && NULL != fPathRef.get()) {
+        fPathRef.get()->removeOwner(fOwner);
+    }
+    fPathRef.reset(ref);
+    if (diff && NULL != fPathRef.get()) {
+        fPathRef.get()->addOwner(fOwner);
+    }
+}
+
+void SkPath::PathRefDebugRef::swap(SkPath::PathRefDebugRef* other) {
+    if (other->fPathRef.get() != fPathRef.get()) {
+        other->fPathRef->removeOwner(other->fOwner);
+        other->fPathRef->addOwner(fOwner);
+
+        fPathRef->removeOwner(fOwner);
+        fPathRef->addOwner(other->fOwner);
+    }
+
+    fPathRef.swap(&other->fPathRef);
+}
+
+SkPathRef* SkPath::PathRefDebugRef::get() const { return fPathRef.get(); }
+
+SkAutoTUnref<SkPathRef>::BlockRefType *SkPath::PathRefDebugRef::operator->() const {
+    return fPathRef.operator->();
+}
+
+SkPath::PathRefDebugRef::operator SkPathRef*() {
+    return fPathRef.operator SkPathRef *();
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////
+
+
+SK_DEFINE_INST_COUNT(SkPath);
+
+// This value is just made-up for now. When count is 4, calling memset was much
+// slower than just writing the loop. This seems odd, and hopefully in the
+// future this we appear to have been a fluke...
+#define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16
 
 ////////////////////////////////////////////////////////////////////////////
 
@@ -32,6 +95,36 @@
     return SkPath::kDone_Verb == iter.next(pts);
 }
 
+class SkAutoDisableOvalCheck {
+public:
+    SkAutoDisableOvalCheck(SkPath* path) : fPath(path) {
+        fSaved = fPath->fIsOval;
+    }
+
+    ~SkAutoDisableOvalCheck() {
+        fPath->fIsOval = fSaved;
+    }
+
+private:
+    SkPath* fPath;
+    bool    fSaved;
+};
+
+class SkAutoDisableDirectionCheck {
+public:
+    SkAutoDisableDirectionCheck(SkPath* path) : fPath(path) {
+        fSaved = static_cast<SkPath::Direction>(fPath->fDirection);
+    }
+
+    ~SkAutoDisableDirectionCheck() {
+        fPath->fDirection = fSaved;
+    }
+
+private:
+    SkPath*              fPath;
+    SkPath::Direction    fSaved;
+};
+
 /*  This guy's constructor/destructor bracket a path editing operation. It is
     used when we know the bounds of the amount we are going to add to the path
     (usually a new contour, but not required).
@@ -61,9 +154,11 @@
         if (fEmpty) {
             fPath->fBounds = fRect;
             fPath->fBoundsIsDirty = false;
+            fPath->fIsFinite = fPath->fBounds.isFinite();
         } else if (!fDirty) {
             joinNoEmptyChecks(&fPath->fBounds, fRect);
             fPath->fBoundsIsDirty = false;
+            fPath->fIsFinite = fPath->fBounds.isFinite();
         }
     }
 
@@ -77,7 +172,9 @@
     // returns true if we should proceed
     void init(SkPath* path) {
         fPath = path;
-        fDirty = SkToBool(path->fBoundsIsDirty);
+        // Mark the path's bounds as dirty if (1) they are, or (2) the path
+        // is non-finite, and therefore its bounds are not meaningful
+        fDirty = SkToBool(path->fBoundsIsDirty) || !path->fIsFinite;
         fDegenerate = is_degenerate(*path);
         fEmpty = path->isEmpty();
         // Cannot use fRect for our bounds unless we know it is sorted
@@ -85,12 +182,14 @@
     }
 };
 
-static void compute_pt_bounds(SkRect* bounds, const SkTDArray<SkPoint>& pts) {
-    if (pts.count() <= 1) {  // we ignore just 1 point (moveto)
-        bounds->set(0, 0, 0, 0);
+// Return true if the computed bounds are finite.
+static bool compute_pt_bounds(SkRect* bounds, const SkPathRef& ref) {
+    int count = ref.countPoints();
+    if (count <= 1) {  // we ignore just 1 point (moveto)
+        bounds->setEmpty();
+        return count ? ref.points()->isFinite() : true;
     } else {
-        bounds->set(pts.begin(), pts.count());
-//        SkDebugf("------- compute bounds %p %d", &pts, pts.count());
+        return bounds->setBoundsCheck(ref.points(), count);
     }
 }
 
@@ -113,21 +212,43 @@
 // flag to require a moveTo if we begin with something else, like lineTo etc.
 #define INITIAL_LASTMOVETOINDEX_VALUE   ~0
 
-SkPath::SkPath() 
-    : fFillType(kWinding_FillType)
+SkPath::SkPath()
+#if SK_DEBUG_PATH_REF
+    : fPathRef(SkPathRef::CreateEmpty(), this)
+#else
+    : fPathRef(SkPathRef::CreateEmpty())
+#endif
+    , fFillType(kWinding_FillType)
     , fBoundsIsDirty(true) {
     fConvexity = kUnknown_Convexity;
+    fDirection = kUnknown_Direction;
     fSegmentMask = 0;
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+    fIsOval = false;
+    fIsFinite = false;  // gets computed when we know our bounds
 #ifdef SK_BUILD_FOR_ANDROID
     fGenerationID = 0;
     fSourcePath = NULL;
 #endif
 }
 
-SkPath::SkPath(const SkPath& src) {
+SkPath::SkPath(const SkPath& src)
+#if SK_DEBUG_PATH_REF
+    : fPathRef(this)
+#endif
+{
     SkDEBUGCODE(src.validate();)
-    *this = src;
+    src.fPathRef.get()->ref();
+    fPathRef.reset(src.fPathRef.get());
+    fBounds         = src.fBounds;
+    fFillType       = src.fFillType;
+    fBoundsIsDirty  = src.fBoundsIsDirty;
+    fConvexity      = src.fConvexity;
+    fDirection      = src.fDirection;
+    fIsFinite       = src.fIsFinite;
+    fSegmentMask    = src.fSegmentMask;
+    fLastMoveToIndex = src.fLastMoveToIndex;
+    fIsOval         = src.fIsOval;
 #ifdef SK_BUILD_FOR_ANDROID
     // the assignment operator above increments the ID so correct for that here
     fGenerationID = src.fGenerationID;
@@ -143,21 +264,24 @@
     SkDEBUGCODE(src.validate();)
 
     if (this != &src) {
+        src.fPathRef.get()->ref();
+        fPathRef.reset(src.fPathRef.get());
         fBounds         = src.fBounds;
-        fPts            = src.fPts;
-        fVerbs          = src.fVerbs;
         fFillType       = src.fFillType;
         fBoundsIsDirty  = src.fBoundsIsDirty;
         fConvexity      = src.fConvexity;
+        fDirection      = src.fDirection;
+        fIsFinite       = src.fIsFinite;
         fSegmentMask    = src.fSegmentMask;
         fLastMoveToIndex = src.fLastMoveToIndex;
+        fIsOval         = src.fIsOval;
         GEN_ID_INC;
     }
     SkDEBUGCODE(this->validate();)
     return *this;
 }
 
-bool operator==(const SkPath& a, const SkPath& b) {
+SK_API bool operator==(const SkPath& a, const SkPath& b) {
     // note: don't need to look at isConvex or bounds, since just comparing the
     // raw data is sufficient.
 
@@ -167,7 +291,7 @@
 
     return &a == &b ||
         (a.fFillType == b.fFillType && a.fSegmentMask == b.fSegmentMask &&
-         a.fVerbs == b.fVerbs && a.fPts == b.fPts);
+         *a.fPathRef.get() == *b.fPathRef.get());
 }
 
 void SkPath::swap(SkPath& other) {
@@ -175,17 +299,99 @@
 
     if (this != &other) {
         SkTSwap<SkRect>(fBounds, other.fBounds);
-        fPts.swap(other.fPts);
-        fVerbs.swap(other.fVerbs);
+        fPathRef.swap(&other.fPathRef);
         SkTSwap<uint8_t>(fFillType, other.fFillType);
         SkTSwap<uint8_t>(fBoundsIsDirty, other.fBoundsIsDirty);
         SkTSwap<uint8_t>(fConvexity, other.fConvexity);
+        SkTSwap<uint8_t>(fDirection, other.fDirection);
         SkTSwap<uint8_t>(fSegmentMask, other.fSegmentMask);
         SkTSwap<int>(fLastMoveToIndex, other.fLastMoveToIndex);
+        SkTSwap<SkBool8>(fIsOval, other.fIsOval);
+        SkTSwap<SkBool8>(fIsFinite, other.fIsFinite);
         GEN_ID_INC;
     }
 }
 
+static inline bool check_edge_against_rect(const SkPoint& p0,
+                                           const SkPoint& p1,
+                                           const SkRect& rect,
+                                           SkPath::Direction dir) {
+    const SkPoint* edgeBegin;
+    SkVector v;
+    if (SkPath::kCW_Direction == dir) {
+        v = p1 - p0;
+        edgeBegin = &p0;
+    } else {
+        v = p0 - p1;
+        edgeBegin = &p1;
+    }
+    if (v.fX || v.fY) {
+        // check the cross product of v with the vec from edgeBegin to each rect corner
+        SkScalar yL = SkScalarMul(v.fY, rect.fLeft - edgeBegin->fX);
+        SkScalar xT = SkScalarMul(v.fX, rect.fTop - edgeBegin->fY);
+        SkScalar yR = SkScalarMul(v.fY, rect.fRight - edgeBegin->fX);
+        SkScalar xB = SkScalarMul(v.fX, rect.fBottom - edgeBegin->fY);
+        if ((xT < yL) || (xT < yR) || (xB < yL) || (xB < yR)) {
+            return false;
+        }
+    }
+    return true;
+}
+
+bool SkPath::conservativelyContainsRect(const SkRect& rect) const {
+    // This only handles non-degenerate convex paths currently.
+    if (kConvex_Convexity != this->getConvexity()) {
+        return false;
+    }
+
+    Direction direction;
+    if (!this->cheapComputeDirection(&direction)) {
+        return false;
+    }
+
+    SkPoint firstPt;
+    SkPoint prevPt;
+    RawIter iter(*this);
+    SkPath::Verb verb;
+    SkPoint pts[4];
+    SkDEBUGCODE(int moveCnt = 0;)
+
+    while ((verb = iter.next(pts)) != kDone_Verb) {
+        int nextPt = -1;
+        switch (verb) {
+            case kMove_Verb:
+                SkASSERT(!moveCnt);
+                SkDEBUGCODE(++moveCnt);
+                firstPt = prevPt = pts[0];
+                break;
+            case kLine_Verb:
+                nextPt = 1;
+                SkASSERT(moveCnt);
+                break;
+            case kQuad_Verb:
+                SkASSERT(moveCnt);
+                nextPt = 2;
+                break;
+            case kCubic_Verb:
+                SkASSERT(moveCnt);
+                nextPt = 3;
+                break;
+            case kClose_Verb:
+                break;
+            default:
+                SkDEBUGFAIL("unknown verb");
+        }
+        if (-1 != nextPt) {
+            if (!check_edge_against_rect(prevPt, pts[nextPt], rect, direction)) {
+                return false;
+            }
+            prevPt = pts[nextPt];
+        }
+    }
+
+    return check_edge_against_rect(prevPt, firstPt, rect, direction);
+}
+
 #ifdef SK_BUILD_FOR_ANDROID
 uint32_t SkPath::getGenerationID() const {
     return fGenerationID;
@@ -203,42 +409,61 @@
 void SkPath::reset() {
     SkDEBUGCODE(this->validate();)
 
-    fPts.reset();
-    fVerbs.reset();
+    fPathRef.reset(SkPathRef::CreateEmpty());
     GEN_ID_INC;
     fBoundsIsDirty = true;
     fConvexity = kUnknown_Convexity;
+    fDirection = kUnknown_Direction;
     fSegmentMask = 0;
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+    fIsOval = false;
 }
 
 void SkPath::rewind() {
     SkDEBUGCODE(this->validate();)
 
-    fPts.rewind();
-    fVerbs.rewind();
+    SkPathRef::Rewind(&fPathRef);
     GEN_ID_INC;
     fConvexity = kUnknown_Convexity;
     fBoundsIsDirty = true;
     fSegmentMask = 0;
     fLastMoveToIndex = INITIAL_LASTMOVETOINDEX_VALUE;
+    fIsOval = false;
 }
 
 bool SkPath::isEmpty() const {
     SkDEBUGCODE(this->validate();)
-    return 0 == fVerbs.count();
+    return 0 == fPathRef->countVerbs();
+}
+
+bool SkPath::isLine(SkPoint line[2]) const {
+    int verbCount = fPathRef->countVerbs();
+    int ptCount = fPathRef->countVerbs();
+
+    if (2 == verbCount && 2 == ptCount) {
+        if (kMove_Verb == fPathRef->atVerb(0) &&
+            kLine_Verb == fPathRef->atVerb(1)) {
+            if (line) {
+                const SkPoint* pts = fPathRef->points();
+                line[0] = pts[0];
+                line[1] = pts[1];
+            }
+            return true;
+        }
+    }
+    return false;
 }
 
 /*
  Determines if path is a rect by keeping track of changes in direction
  and looking for a loop either clockwise or counterclockwise.
- 
+
  The direction is computed such that:
   0: vertical up
-  1: horizontal right
+  1: horizontal left
   2: vertical down
-  3: horizontal left
- 
+  3: horizontal right
+
 A rectangle cycles up/right/down/left or up/left/down/right.
 
 The test fails if:
@@ -252,11 +477,11 @@
   The path contains a quadratic or cubic.
   The path contains fewer than four points.
   The final point isn't equal to the first point.
-  
+
 It's OK if the path has:
   Several colinear line segments composing a rectangle side.
   Single points on the rectangle side.
-  
+
 The direction takes advantage of the corners found since opposite sides
 must travel in opposite directions.
 
@@ -264,11 +489,12 @@
 FIXME: If the API passes fill-only, return true if the filled stroke
        is a rectangle, though the caller failed to close the path.
  */
-bool SkPath::isRect(SkRect* rect) const {
-    SkDEBUGCODE(this->validate();)
-
+bool SkPath::isRectContour(bool allowPartial, int* currVerb, const SkPoint** ptsPtr,
+        bool* isClosed, Direction* direction) const {
     int corners = 0;
     SkPoint first, last;
+    const SkPoint* pts = *ptsPtr;
+    const SkPoint* savePts = NULL;
     first.set(0, 0);
     last.set(0, 0);
     int firstDirection = 0;
@@ -276,13 +502,12 @@
     int nextDirection = 0;
     bool closedOrMoved = false;
     bool autoClose = false;
-    const uint8_t* verbs = fVerbs.begin();
-    const uint8_t* verbStop = fVerbs.end();
-    const SkPoint* pts = fPts.begin();
-    while (verbs != verbStop) {
-        switch (*verbs++) {
+    int verbCnt = fPathRef->countVerbs();
+    while (*currVerb < verbCnt && (!allowPartial || !autoClose)) {
+        switch (fPathRef->atVerb(*currVerb)) {
             case kClose_Verb:
-                pts = fPts.begin();
+                savePts = pts;
+                pts = *ptsPtr;
                 autoClose = true;
             case kLine_Verb: {
                 SkScalar left = last.fX;
@@ -309,6 +534,9 @@
                 if (closedOrMoved) {
                     return false; // closed followed by a line
                 }
+                if (autoClose && nextDirection == firstDirection) {
+                    break; // colinear with first
+                }
                 closedOrMoved = autoClose;
                 if (lastDirection != nextDirection) {
                     if (++corners > 4) {
@@ -337,41 +565,122 @@
                 closedOrMoved = true;
                 break;
         }
+        *currVerb += 1;
         lastDirection = nextDirection;
     }
     // Success if 4 corners and first point equals last
-    bool result = 4 == corners && first == last;
+    bool result = 4 == corners && (first == last || autoClose);
+    if (savePts) {
+        *ptsPtr = savePts;
+    }
+    if (result && isClosed) {
+        *isClosed = autoClose;
+    }
+    if (result && direction) {
+        *direction = firstDirection == ((lastDirection + 1) & 3) ? kCCW_Direction : kCW_Direction;
+    }
+    return result;
+}
+
+bool SkPath::isRect(SkRect* rect) const {
+    SkDEBUGCODE(this->validate();)
+    int currVerb = 0;
+    const SkPoint* pts = fPathRef->points();
+    bool result = isRectContour(false, &currVerb, &pts, NULL, NULL);
     if (result && rect) {
         *rect = getBounds();
     }
     return result;
 }
 
-int SkPath::getPoints(SkPoint copy[], int max) const {
+bool SkPath::isRect(bool* isClosed, Direction* direction) const {
+    SkDEBUGCODE(this->validate();)
+    int currVerb = 0;
+    const SkPoint* pts = fPathRef->points();
+    return isRectContour(false, &currVerb, &pts, isClosed, direction);
+}
+
+bool SkPath::isNestedRects(SkRect rects[2]) const {
+    SkDEBUGCODE(this->validate();)
+    int currVerb = 0;
+    const SkPoint* pts = fPathRef->points();
+    const SkPoint* first = pts;
+    if (!isRectContour(true, &currVerb, &pts, NULL, NULL)) {
+        return false;
+    }
+    const SkPoint* last = pts;
+    SkRect testRects[2];
+    if (isRectContour(false, &currVerb, &pts, NULL, NULL)) {
+        testRects[0].set(first, last - first);
+        testRects[1].set(last, pts - last);
+        if (testRects[0].contains(testRects[1])) {
+            if (rects) {
+                rects[0] = testRects[0];
+                rects[1] = testRects[1];
+            }
+            return true;
+        }
+        if (testRects[1].contains(testRects[0])) {
+            if (rects) {
+                rects[0] = testRects[1];
+                rects[1] = testRects[0];
+            }
+            return true;
+        }
+    }
+    return false;
+}
+
+int SkPath::countPoints() const {
+    return fPathRef->countPoints();
+}
+
+int SkPath::getPoints(SkPoint dst[], int max) const {
     SkDEBUGCODE(this->validate();)
 
     SkASSERT(max >= 0);
-    int count = fPts.count();
-    if (copy && max > 0 && count > 0) {
-        memcpy(copy, fPts.begin(), sizeof(SkPoint) * SkMin32(max, count));
-    }
-    return count;
+    SkASSERT(!max || dst);
+    int count = SkMin32(max, fPathRef->countPoints());
+    memcpy(dst, fPathRef->points(), count * sizeof(SkPoint));
+    return fPathRef->countPoints();
 }
 
 SkPoint SkPath::getPoint(int index) const {
-    if ((unsigned)index < (unsigned)fPts.count()) {
-        return fPts[index];
+    if ((unsigned)index < (unsigned)fPathRef->countPoints()) {
+        return fPathRef->atPoint(index);
     }
     return SkPoint::Make(0, 0);
 }
 
+int SkPath::countVerbs() const {
+    return fPathRef->countVerbs();
+}
+
+static inline void copy_verbs_reverse(uint8_t* inorderDst,
+                                      const uint8_t* reversedSrc,
+                                      int count) {
+    for (int i = 0; i < count; ++i) {
+        inorderDst[i] = reversedSrc[~i];
+    }
+}
+
+int SkPath::getVerbs(uint8_t dst[], int max) const {
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(max >= 0);
+    SkASSERT(!max || dst);
+    int count = SkMin32(max, fPathRef->countVerbs());
+    copy_verbs_reverse(dst, fPathRef->verbs(), count);
+    return fPathRef->countVerbs();
+}
+
 bool SkPath::getLastPt(SkPoint* lastPt) const {
     SkDEBUGCODE(this->validate();)
 
-    int count = fPts.count();
+    int count = fPathRef->countPoints();
     if (count > 0) {
         if (lastPt) {
-            *lastPt = fPts[count - 1];
+            *lastPt = fPathRef->atPoint(count - 1);
         }
         return true;
     }
@@ -384,11 +693,13 @@
 void SkPath::setLastPt(SkScalar x, SkScalar y) {
     SkDEBUGCODE(this->validate();)
 
-    int count = fPts.count();
+    int count = fPathRef->countPoints();
     if (count == 0) {
         this->moveTo(x, y);
     } else {
-        fPts[count - 1].set(x, y);
+        fIsOval = false;
+        SkPathRef::Editor ed(&fPathRef);
+        ed.atPoint(count-1)->set(x, y);
         GEN_ID_INC;
     }
 }
@@ -397,8 +708,8 @@
     SkDEBUGCODE(this->validate();)
     SkASSERT(fBoundsIsDirty);
 
+    fIsFinite = compute_pt_bounds(&fBounds, *fPathRef.get());
     fBoundsIsDirty = false;
-    compute_pt_bounds(&fBounds, fPts);
 }
 
 void SkPath::setConvexity(Convexity c) {
@@ -415,37 +726,33 @@
     do {                                 \
         fBoundsIsDirty = true;           \
         fConvexity = kUnknown_Convexity; \
+        fDirection = kUnknown_Direction; \
+        fIsOval = false;                 \
     } while (0)
 
-#define DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE    \
-    do {                                        \
-        fBoundsIsDirty = true;                  \
+#define DIRTY_AFTER_EDIT_NO_CONVEXITY_OR_DIRECTION_CHANGE   \
+    do {                                                    \
+        fBoundsIsDirty = true;                              \
     } while (0)
 
 void SkPath::incReserve(U16CPU inc) {
     SkDEBUGCODE(this->validate();)
-
-    fVerbs.setReserve(fVerbs.count() + inc);
-    fPts.setReserve(fPts.count() + inc);
-
+    SkPathRef::Editor(&fPathRef, inc, inc);
     SkDEBUGCODE(this->validate();)
 }
 
 void SkPath::moveTo(SkScalar x, SkScalar y) {
     SkDEBUGCODE(this->validate();)
 
-    int      vc = fVerbs.count();
-    SkPoint* pt;
+    SkPathRef::Editor ed(&fPathRef);
 
     // remember our index
-    fLastMoveToIndex = fPts.count();
+    fLastMoveToIndex = ed.pathRef()->countPoints();
 
-    pt = fPts.append();
-    *fVerbs.append() = kMove_Verb;
-    pt->set(x, y);
+    ed.growForVerb(kMove_Verb)->set(x, y);
 
     GEN_ID_INC;
-    DIRTY_AFTER_EDIT_NO_CONVEXITY_CHANGE;
+    DIRTY_AFTER_EDIT_NO_CONVEXITY_OR_DIRECTION_CHANGE;
 }
 
 void SkPath::rMoveTo(SkScalar x, SkScalar y) {
@@ -457,10 +764,10 @@
 void SkPath::injectMoveToIfNeeded() {
     if (fLastMoveToIndex < 0) {
         SkScalar x, y;
-        if (fVerbs.count() == 0) {
+        if (fPathRef->countVerbs() == 0) {
             x = y = 0;
         } else {
-            const SkPoint& pt = fPts[~fLastMoveToIndex];
+            const SkPoint& pt = fPathRef->atPoint(~fLastMoveToIndex);
             x = pt.fX;
             y = pt.fY;
         }
@@ -473,8 +780,8 @@
 
     this->injectMoveToIfNeeded();
 
-    fPts.append()->set(x, y);
-    *fVerbs.append() = kLine_Verb;
+    SkPathRef::Editor ed(&fPathRef);
+    ed.growForVerb(kLine_Verb)->set(x, y);
     fSegmentMask |= kLine_SegmentMask;
 
     GEN_ID_INC;
@@ -492,10 +799,10 @@
 
     this->injectMoveToIfNeeded();
 
-    SkPoint* pts = fPts.append(2);
+    SkPathRef::Editor ed(&fPathRef);
+    SkPoint* pts = ed.growForVerb(kQuad_Verb);
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
-    *fVerbs.append() = kQuad_Verb;
     fSegmentMask |= kQuad_SegmentMask;
 
     GEN_ID_INC;
@@ -514,11 +821,11 @@
 
     this->injectMoveToIfNeeded();
 
-    SkPoint* pts = fPts.append(3);
+    SkPathRef::Editor ed(&fPathRef);
+    SkPoint* pts = ed.growForVerb(kCubic_Verb);
     pts[0].set(x1, y1);
     pts[1].set(x2, y2);
     pts[2].set(x3, y3);
-    *fVerbs.append() = kCubic_Verb;
     fSegmentMask |= kCubic_SegmentMask;
 
     GEN_ID_INC;
@@ -536,16 +843,18 @@
 void SkPath::close() {
     SkDEBUGCODE(this->validate();)
 
-    int count = fVerbs.count();
+    int count = fPathRef->countVerbs();
     if (count > 0) {
-        switch (fVerbs[count - 1]) {
+        switch (fPathRef->atVerb(count - 1)) {
             case kLine_Verb:
             case kQuad_Verb:
             case kCubic_Verb:
-            case kMove_Verb:
-                *fVerbs.append() = kClose_Verb;
+            case kMove_Verb: {
+                SkPathRef::Editor ed(&fPathRef);
+                ed.growForVerb(kClose_Verb);
                 GEN_ID_INC;
                 break;
+            }
             default:
                 // don't add a close if it's the first verb or a repeat
                 break;
@@ -564,12 +873,20 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static void assert_known_direction(int dir) {
+    SkASSERT(SkPath::kCW_Direction == dir || SkPath::kCCW_Direction == dir);
+}
+
 void SkPath::addRect(const SkRect& rect, Direction dir) {
     this->addRect(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, dir);
 }
 
 void SkPath::addRect(SkScalar left, SkScalar top, SkScalar right,
                      SkScalar bottom, Direction dir) {
+    assert_known_direction(dir);
+    fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
+    SkAutoDisableDirectionCheck addc(this);
+
     SkAutoPathBoundsUpdate apbu(this, left, top, right, bottom);
 
     this->incReserve(5);
@@ -587,10 +904,138 @@
     this->close();
 }
 
+void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
+    SkDEBUGCODE(this->validate();)
+    if (count <= 0) {
+        return;
+    }
+
+    SkPathRef::Editor ed(&fPathRef);
+    fLastMoveToIndex = ed.pathRef()->countPoints();
+    uint8_t* vb;
+    SkPoint* p;
+    // +close makes room for the extra kClose_Verb
+    ed.grow(count + close, count, &vb, &p);
+
+    memcpy(p, pts, count * sizeof(SkPoint));
+    vb[~0] = kMove_Verb;
+    if (count > 1) {
+        // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to
+        // be 0, the compiler will remove the test/branch entirely.
+        if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) {
+            memset(vb - count, kLine_Verb, count - 1);
+        } else {
+            for (int i = 1; i < count; ++i) {
+                vb[~i] = kLine_Verb;
+            }
+        }
+        fSegmentMask |= kLine_SegmentMask;
+    }
+    if (close) {
+        vb[~count] = kClose_Verb;
+    }
+
+    GEN_ID_INC;
+    DIRTY_AFTER_EDIT;
+    SkDEBUGCODE(this->validate();)
+}
+
+static void add_corner_arc(SkPath* path, const SkRect& rect,
+                           SkScalar rx, SkScalar ry, int startAngle,
+                           SkPath::Direction dir, bool forceMoveTo) {
+    // These two asserts are not sufficient, since really we want to know
+    // that the pair of radii (e.g. left and right, or top and bottom) sum
+    // to <= dimension, but we don't have that data here, so we just have
+    // these conservative asserts.
+    SkASSERT(0 <= rx && rx <= rect.width());
+    SkASSERT(0 <= ry && ry <= rect.height());
+
+    SkRect   r;
+    r.set(-rx, -ry, rx, ry);
+
+    switch (startAngle) {
+        case   0:
+            r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
+            break;
+        case  90:
+            r.offset(rect.fLeft - r.fLeft,   rect.fBottom - r.fBottom);
+            break;
+        case 180: r.offset(rect.fLeft - r.fLeft,   rect.fTop - r.fTop); break;
+        case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
+        default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");
+    }
+
+    SkScalar start = SkIntToScalar(startAngle);
+    SkScalar sweep = SkIntToScalar(90);
+    if (SkPath::kCCW_Direction == dir) {
+        start += sweep;
+        sweep = -sweep;
+    }
+
+    path->arcTo(r, start, sweep, forceMoveTo);
+}
+
+void SkPath::addRoundRect(const SkRect& rect, const SkScalar radii[],
+                          Direction dir) {
+    SkRRect rrect;
+    rrect.setRectRadii(rect, (const SkVector*) radii);
+    this->addRRect(rrect, dir);
+}
+
+void SkPath::addRRect(const SkRRect& rrect, Direction dir) {
+    assert_known_direction(dir);
+
+    if (rrect.isEmpty()) {
+        return;
+    }
+
+    const SkRect& bounds = rrect.getBounds();
+
+    if (rrect.isRect()) {
+        this->addRect(bounds, dir);
+    } else if (rrect.isOval()) {
+        this->addOval(bounds, dir);
+    } else if (rrect.isSimple()) {
+        const SkVector& rad = rrect.getSimpleRadii();
+        this->addRoundRect(bounds, rad.x(), rad.y(), dir);
+    } else {
+        SkAutoPathBoundsUpdate apbu(this, bounds);
+
+        if (kCW_Direction == dir) {
+            add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true);
+            add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,   0, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,  90, dir, false);
+        } else {
+            add_corner_arc(this, bounds, rrect.fRadii[0].fX, rrect.fRadii[0].fY, 180, dir, true);
+            add_corner_arc(this, bounds, rrect.fRadii[3].fX, rrect.fRadii[3].fY,  90, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[2].fX, rrect.fRadii[2].fY,   0, dir, false);
+            add_corner_arc(this, bounds, rrect.fRadii[1].fX, rrect.fRadii[1].fY, 270, dir, false);
+        }
+        this->close();
+    }
+}
+
+bool SkPath::hasOnlyMoveTos() const {
+    int count = fPathRef->countVerbs();
+    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbsMemBegin();
+    for (int i = 0; i < count; ++i) {
+        if (*verbs == kLine_Verb ||
+            *verbs == kQuad_Verb ||
+            *verbs == kCubic_Verb) {
+            return false;
+        }
+        ++verbs;
+    }
+    return true;
+}
+
 #define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
 
 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
                           Direction dir) {
+    assert_known_direction(dir);
+
     SkScalar    w = rect.width();
     SkScalar    halfW = SkScalarHalf(w);
     SkScalar    h = rect.height();
@@ -608,7 +1053,10 @@
         return;
     }
 
+    fDirection = this->hasOnlyMoveTos() ? dir : kUnknown_Direction;
+
     SkAutoPathBoundsUpdate apbu(this, rect);
+    SkAutoDisableDirectionCheck(this);
 
     if (skip_hori) {
         rx = halfW;
@@ -675,85 +1123,32 @@
     this->close();
 }
 
-static void add_corner_arc(SkPath* path, const SkRect& rect,
-                           SkScalar rx, SkScalar ry, int startAngle,
-                           SkPath::Direction dir, bool forceMoveTo) {
-    rx = SkMinScalar(SkScalarHalf(rect.width()), rx);
-    ry = SkMinScalar(SkScalarHalf(rect.height()), ry);
-
-    SkRect   r;
-    r.set(-rx, -ry, rx, ry);
-
-    switch (startAngle) {
-        case   0:
-            r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);
-            break;
-        case  90:
-            r.offset(rect.fLeft - r.fLeft,   rect.fBottom - r.fBottom);
-            break;
-        case 180: r.offset(rect.fLeft - r.fLeft,   rect.fTop - r.fTop); break;
-        case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;
-        default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");
-    }
-
-    SkScalar start = SkIntToScalar(startAngle);
-    SkScalar sweep = SkIntToScalar(90);
-    if (SkPath::kCCW_Direction == dir) {
-        start += sweep;
-        sweep = -sweep;
-    }
-
-    path->arcTo(r, start, sweep, forceMoveTo);
-}
-
-void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],
-                          Direction dir) {
-    // abort before we invoke SkAutoPathBoundsUpdate()
-    if (rect.isEmpty()) {
-        return;
-    }
-
-    SkAutoPathBoundsUpdate apbu(this, rect);
-
-    if (kCW_Direction == dir) {
-        add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
-        add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
-        add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
-        add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
-    } else {
-        add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);
-        add_corner_arc(this, rect, rad[6], rad[7],  90, dir, false);
-        add_corner_arc(this, rect, rad[4], rad[5],   0, dir, false);
-        add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);
-    }
-    this->close();
-}
-
 void SkPath::addOval(const SkRect& oval, Direction dir) {
+    assert_known_direction(dir);
+
+    /* If addOval() is called after previous moveTo(),
+       this path is still marked as an oval. This is used to
+       fit into WebKit's calling sequences.
+       We can't simply check isEmpty() in this case, as additional
+       moveTo() would mark the path non empty.
+     */
+    fIsOval = hasOnlyMoveTos();
+    if (fIsOval) {
+        fDirection = dir;
+    } else {
+        fDirection = kUnknown_Direction;
+    }
+
+    SkAutoDisableOvalCheck adoc(this);
+    SkAutoDisableDirectionCheck addc(this);
+
     SkAutoPathBoundsUpdate apbu(this, oval);
 
     SkScalar    cx = oval.centerX();
     SkScalar    cy = oval.centerY();
     SkScalar    rx = SkScalarHalf(oval.width());
     SkScalar    ry = SkScalarHalf(oval.height());
-#if 0   // these seem faster than using quads (1/2 the number of edges)
-    SkScalar    sx = SkScalarMul(rx, CUBIC_ARC_FACTOR);
-    SkScalar    sy = SkScalarMul(ry, CUBIC_ARC_FACTOR);
 
-    this->incReserve(13);
-    this->moveTo(cx + rx, cy);
-    if (dir == kCCW_Direction) {
-        this->cubicTo(cx + rx, cy - sy, cx + sx, cy - ry, cx, cy - ry);
-        this->cubicTo(cx - sx, cy - ry, cx - rx, cy - sy, cx - rx, cy);
-        this->cubicTo(cx - rx, cy + sy, cx - sx, cy + ry, cx, cy + ry);
-        this->cubicTo(cx + sx, cy + ry, cx + rx, cy + sy, cx + rx, cy);
-    } else {
-        this->cubicTo(cx + rx, cy + sy, cx + sx, cy + ry, cx, cy + ry);
-        this->cubicTo(cx - sx, cy + ry, cx - rx, cy + sy, cx - rx, cy);
-        this->cubicTo(cx - rx, cy - sy, cx - sx, cy - ry, cx, cy - ry);
-        this->cubicTo(cx + sx, cy - ry, cx + rx, cy - sy, cx + rx, cy);
-    }
-#else
     SkScalar    sx = SkScalarMul(rx, SK_ScalarTanPIOver8);
     SkScalar    sy = SkScalarMul(ry, SK_ScalarTanPIOver8);
     SkScalar    mx = SkScalarMul(rx, SK_ScalarRoot2Over2);
@@ -791,10 +1186,17 @@
         this->quadTo(cx + sx,       T, cx + mx, cy - my);
         this->quadTo(      R, cy - sy,       R, cy     );
     }
-#endif
     this->close();
 }
 
+bool SkPath::isOval(SkRect* rect) const {
+    if (fIsOval && rect) {
+        *rect = getBounds();
+    }
+
+    return fIsOval;
+}
+
 void SkPath::addCircle(SkScalar x, SkScalar y, SkScalar r, Direction dir) {
     if (r > 0) {
         SkRect  rect;
@@ -808,6 +1210,25 @@
 static int build_arc_points(const SkRect& oval, SkScalar startAngle,
                             SkScalar sweepAngle,
                             SkPoint pts[kSkBuildQuadArcStorage]) {
+
+    if (0 == sweepAngle &&
+        (0 == startAngle || SkIntToScalar(360) == startAngle)) {
+        // Chrome uses this path to move into and out of ovals. If not
+        // treated as a special case the moves can distort the oval's
+        // bounding box (and break the circle special case).
+        pts[0].set(oval.fRight, oval.centerY());
+        return 1;
+    } else if (0 == oval.width() && 0 == oval.height()) {
+        // Chrome will sometimes create 0 radius round rects. Having degenerate
+        // quad segments in the path prevents the path from being recognized as
+        // a rect.
+        // TODO: optimizing the case where only one of width or height is zero
+        // should also be considered. This case, however, doesn't seem to be
+        // as common as the single point case.
+        pts[0].set(oval.fRight, oval.fTop);
+        return 1;
+    }
+
     SkVector start, stop;
 
     start.fY = SkScalarSinCos(SkDegreesToRadians(startAngle), &start.fX);
@@ -856,7 +1277,7 @@
     int count = build_arc_points(oval, startAngle, sweepAngle, pts);
     SkASSERT((count & 1) == 1);
 
-    if (fVerbs.count() == 0) {
+    if (fPathRef->countVerbs() == 0) {
         forceMoveTo = true;
     }
     this->incReserve(count);
@@ -968,7 +1389,9 @@
 }
 
 void SkPath::addPath(const SkPath& path, const SkMatrix& matrix) {
-    this->incReserve(path.fPts.count());
+    SkPathRef::Editor(&fPathRef, path.countVerbs(), path.countPoints());
+
+    fIsOval = false;
 
     RawIter iter(path);
     SkPoint pts[4];
@@ -1016,19 +1439,23 @@
 
 // ignore the initial moveto, and stop when the 1st contour ends
 void SkPath::pathTo(const SkPath& path) {
-    int i, vcount = path.fVerbs.count();
-    if (vcount == 0) {
+    int i, vcount = path.fPathRef->countVerbs();
+    // exit early if the path is empty, or just has a moveTo.
+    if (vcount < 2) {
         return;
     }
 
-    this->incReserve(vcount);
+    SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
 
-    const uint8_t*  verbs = path.fVerbs.begin();
-    const SkPoint*  pts = path.fPts.begin() + 1;    // 1 for the initial moveTo
+    fIsOval = false;
 
-    SkASSERT(verbs[0] == kMove_Verb);
+    const uint8_t* verbs = path.fPathRef->verbs();
+    // skip the initial moveTo
+    const SkPoint*  pts = path.fPathRef->points() + 1;
+
+    SkASSERT(verbs[~0] == kMove_Verb);
     for (i = 1; i < vcount; i++) {
-        switch (verbs[i]) {
+        switch (verbs[~i]) {
             case kLine_Verb:
                 this->lineTo(pts[0].fX, pts[0].fY);
                 break;
@@ -1036,31 +1463,33 @@
                 this->quadTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
                 break;
             case kCubic_Verb:
-                this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY,
-                              pts[2].fX, pts[2].fY);
+                this->cubicTo(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
                 break;
             case kClose_Verb:
                 return;
         }
-        pts += gPtsInVerb[verbs[i]];
+        pts += gPtsInVerb[verbs[~i]];
     }
 }
 
 // ignore the last point of the 1st contour
 void SkPath::reversePathTo(const SkPath& path) {
-    int i, vcount = path.fVerbs.count();
-    if (vcount == 0) {
+    int i, vcount = path.fPathRef->countVerbs();
+    // exit early if the path is empty, or just has a moveTo.
+    if (vcount < 2) {
         return;
     }
 
-    this->incReserve(vcount);
+    SkPathRef::Editor(&fPathRef, vcount, path.countPoints());
 
-    const uint8_t*  verbs = path.fVerbs.begin();
-    const SkPoint*  pts = path.fPts.begin();
+    fIsOval = false;
 
-    SkASSERT(verbs[0] == kMove_Verb);
-    for (i = 1; i < vcount; i++) {
-        int n = gPtsInVerb[verbs[i]];
+    const uint8_t*  verbs = path.fPathRef->verbs();
+    const SkPoint*  pts = path.fPathRef->points();
+
+    SkASSERT(verbs[~0] == kMove_Verb);
+    for (i = 1; i < vcount; ++i) {
+        int n = gPtsInVerb[verbs[~i]];
         if (n == 0) {
             break;
         }
@@ -1068,7 +1497,7 @@
     }
 
     while (--i > 0) {
-        switch (verbs[i]) {
+        switch (verbs[~i]) {
             case kLine_Verb:
                 this->lineTo(pts[-1].fX, pts[-1].fY);
                 break;
@@ -1083,22 +1512,24 @@
                 SkDEBUGFAIL("bad verb");
                 break;
         }
-        pts -= gPtsInVerb[verbs[i]];
+        pts -= gPtsInVerb[verbs[~i]];
     }
 }
 
 void SkPath::reverseAddPath(const SkPath& src) {
-    this->incReserve(src.fPts.count());
+    SkPathRef::Editor ed(&fPathRef, src.fPathRef->countPoints(), src.fPathRef->countVerbs());
 
-    const SkPoint* startPts = src.fPts.begin();
-    const SkPoint* pts = src.fPts.end();
-    const uint8_t* startVerbs = src.fVerbs.begin();
-    const uint8_t* verbs = src.fVerbs.end();
+    const SkPoint* pts = src.fPathRef->pointsEnd();
+    // we will iterator through src's verbs backwards
+    const uint8_t* verbs = src.fPathRef->verbsMemBegin(); // points at the last verb
+    const uint8_t* verbsEnd = src.fPathRef->verbs(); // points just past the first verb
+
+    fIsOval = false;
 
     bool needMove = true;
     bool needClose = false;
-    while (verbs > startVerbs) {
-        uint8_t v = *--verbs;
+    while (verbs < verbsEnd) {
+        uint8_t v = *(verbs++);
         int n = gPtsInVerb[v];
 
         if (needMove) {
@@ -1185,7 +1616,7 @@
         SkPoint         pts[4];
         SkPath::Verb    verb;
 
-        while ((verb = iter.next(pts)) != kDone_Verb) {
+        while ((verb = iter.next(pts, false)) != kDone_Verb) {
             switch (verb) {
                 case kMove_Verb:
                     tmp.moveTo(pts[0]);
@@ -1210,21 +1641,42 @@
 
         // swap() will increment the gen id if needed
         dst->swap(tmp);
-        matrix.mapPoints(dst->fPts.begin(), dst->fPts.count());
+        SkPathRef::Editor ed(&dst->fPathRef);
+        matrix.mapPoints(ed.points(), ed.pathRef()->countPoints());
+        dst->fDirection = kUnknown_Direction;
     } else {
-        // remember that dst might == this, so be sure to check
-        // fBoundsIsDirty before we set it
-        if (!fBoundsIsDirty && matrix.rectStaysRect() && fPts.count() > 1) {
-            // if we're empty, fastbounds should not be mapped
-            matrix.mapRect(&dst->fBounds, fBounds);
+        /*
+         *  If we're not in perspective, we can transform all of the points at
+         *  once.
+         *
+         *  Here we also want to optimize bounds, by noting if the bounds are
+         *  already known, and if so, we just transform those as well and mark
+         *  them as "known", rather than force the transformed path to have to
+         *  recompute them.
+         *
+         *  Special gotchas if the path is effectively empty (<= 1 point) or
+         *  if it is non-finite. In those cases bounds need to stay empty,
+         *  regardless of the matrix.
+         */
+        if (!fBoundsIsDirty && matrix.rectStaysRect() && fPathRef->countPoints() > 1) {
             dst->fBoundsIsDirty = false;
+            if (fIsFinite) {
+                matrix.mapRect(&dst->fBounds, fBounds);
+                if (!(dst->fIsFinite = dst->fBounds.isFinite())) {
+                    dst->fBounds.setEmpty();
+                }
+            } else {
+                dst->fIsFinite = false;
+                dst->fBounds.setEmpty();
+            }
         } else {
+            GEN_ID_PTR_INC(dst);
             dst->fBoundsIsDirty = true;
         }
 
+        SkPathRef::CreateTransformedCopy(&dst->fPathRef, *fPathRef.get(), matrix);
+
         if (this != dst) {
-            dst->fVerbs = fVerbs;
-            dst->fPts.setCount(fPts.count());
             dst->fFillType = fFillType;
             dst->fSegmentMask = fSegmentMask;
             dst->fConvexity = fConvexity;
@@ -1233,7 +1685,24 @@
         if (!matrix.isIdentity()) {
             GEN_ID_PTR_INC(dst);
         }
-        matrix.mapPoints(dst->fPts.begin(), fPts.begin(), fPts.count());
+
+        if (kUnknown_Direction == fDirection) {
+            dst->fDirection = kUnknown_Direction;
+        } else {
+            SkScalar det2x2 =
+                SkScalarMul(matrix.get(SkMatrix::kMScaleX), matrix.get(SkMatrix::kMScaleY)) -
+                SkScalarMul(matrix.get(SkMatrix::kMSkewX), matrix.get(SkMatrix::kMSkewY));
+            if (det2x2 < 0) {
+                dst->fDirection = SkPath::OppositeDirection(static_cast<Direction>(fDirection));
+            } else if (det2x2 > 0) {
+                dst->fDirection = fDirection;
+            } else {
+                dst->fDirection = kUnknown_Direction;
+            }
+        }
+
+        // It's an oval only if it stays a rect.
+        dst->fIsOval = fIsOval && matrix.rectStaysRect();
 
         SkDEBUGCODE(dst->validate();)
     }
@@ -1269,9 +1738,9 @@
 }
 
 void SkPath::Iter::setPath(const SkPath& path, bool forceClose) {
-    fPts = path.fPts.begin();
-    fVerbs = path.fVerbs.begin();
-    fVerbStop = path.fVerbs.end();
+    fPts = path.fPathRef->points();
+    fVerbs = path.fPathRef->verbs();
+    fVerbStop = path.fPathRef->verbsMemBegin();
     fLastPt.fX = fLastPt.fY = 0;
     fMoveTo.fX = fMoveTo.fY = 0;
     fForceClose = SkToU8(forceClose);
@@ -1290,12 +1759,13 @@
     const uint8_t* verbs = fVerbs;
     const uint8_t* stop = fVerbStop;
 
-    if (kMove_Verb == *verbs) {
-        verbs += 1; // skip the initial moveto
+    if (kMove_Verb == *(verbs - 1)) {
+        verbs -= 1; // skip the initial moveto
     }
 
-    while (verbs < stop) {
-        unsigned v = *verbs++;
+    while (verbs > stop) {
+        // verbs points one beyond the current verb, decrement first.
+        unsigned v = *(--verbs);
         if (kMove_Verb == v) {
             break;
         }
@@ -1307,6 +1777,7 @@
 }
 
 SkPath::Verb SkPath::Iter::autoClose(SkPoint pts[2]) {
+    SkASSERT(pts);
     if (fLastPt != fMoveTo) {
         // A special case: if both points are NaN, SkPoint::operation== returns
         // false, but the iterator expects that they are treated as the same.
@@ -1316,10 +1787,8 @@
             return kClose_Verb;
         }
 
-        if (pts) {
-            pts[0] = fLastPt;
-            pts[1] = fMoveTo;
-        }
+        pts[0] = fLastPt;
+        pts[1] = fMoveTo;
         fLastPt = fMoveTo;
         fCloseLine = true;
         return kLine_Verb;
@@ -1329,21 +1798,16 @@
     }
 }
 
-bool SkPath::Iter::cons_moveTo(SkPoint pts[1]) {
+const SkPoint& SkPath::Iter::cons_moveTo() {
     if (fSegmentState == kAfterMove_SegmentState) {
         // Set the first return pt to the move pt
-        if (pts) {
-            *pts = fMoveTo;
-        }
         fSegmentState = kAfterPrimitive_SegmentState;
+        return fMoveTo;
     } else {
         SkASSERT(fSegmentState == kAfterPrimitive_SegmentState);
          // Set the first return pt to the last pt of the previous primitive.
-        if (pts) {
-            *pts = fPts[-1];
-        }
+        return fPts[-1];
     }
-    return false;
 }
 
 void SkPath::Iter::consumeDegenerateSegments() {
@@ -1353,24 +1817,25 @@
     const SkPoint* lastMovePt = 0;
     SkPoint lastPt = fLastPt;
     while (fVerbs != fVerbStop) {
-        unsigned verb = *fVerbs;
+        unsigned verb = *(fVerbs - 1); // fVerbs is one beyond the current verb
         switch (verb) {
             case kMove_Verb:
                 // Keep a record of this most recent move
                 lastMoveVerb = fVerbs;
                 lastMovePt = fPts;
                 lastPt = fPts[0];
-                fVerbs++;
+                fVerbs--;
                 fPts++;
                 break;
 
             case kClose_Verb:
-                // A close when we are in a segment is always valid
-                if (fSegmentState == kAfterPrimitive_SegmentState) {
+                // A close when we are in a segment is always valid except when it
+                // follows a move which follows a segment.
+                if (fSegmentState == kAfterPrimitive_SegmentState && !lastMoveVerb) {
                     return;
                 }
                 // A close at any other time must be ignored
-                fVerbs++;
+                fVerbs--;
                 break;
 
             case kLine_Verb:
@@ -1383,7 +1848,7 @@
                     return;
                 }
                 // Ignore this line and continue
-                fVerbs++;
+                fVerbs--;
                 fPts++;
                 break;
 
@@ -1397,7 +1862,7 @@
                     return;
                 }
                 // Ignore this line and continue
-                fVerbs++;
+                fVerbs--;
                 fPts += 2;
                 break;
 
@@ -1411,7 +1876,7 @@
                     return;
                 }
                 // Ignore this line and continue
-                fVerbs++;
+                fVerbs--;
                 fPts += 3;
                 break;
 
@@ -1421,13 +1886,13 @@
     }
 }
 
-SkPath::Verb SkPath::Iter::next(SkPoint pts[4]) {
-    this->consumeDegenerateSegments();
+SkPath::Verb SkPath::Iter::doNext(SkPoint ptsParam[4]) {
+    SkASSERT(ptsParam);
 
     if (fVerbs == fVerbStop) {
         // Close the curve if requested and if there is some curve to close
         if (fNeedClose && fSegmentState == kAfterPrimitive_SegmentState) {
-            if (kLine_Verb == this->autoClose(pts)) {
+            if (kLine_Verb == this->autoClose(ptsParam)) {
                 return kLine_Verb;
             }
             fNeedClose = false;
@@ -1436,13 +1901,15 @@
         return kDone_Verb;
     }
 
-    unsigned        verb = *fVerbs++;
-    const SkPoint*  srcPts = fPts;
+    // fVerbs is one beyond the current verb, decrement first
+    unsigned verb = *(--fVerbs);
+    const SkPoint* SK_RESTRICT srcPts = fPts;
+    SkPoint* SK_RESTRICT       pts = ptsParam;
 
     switch (verb) {
         case kMove_Verb:
             if (fNeedClose) {
-                fVerbs -= 1;
+                fVerbs++; // move back one verb
                 verb = this->autoClose(pts);
                 if (verb == kClose_Verb) {
                     fNeedClose = false;
@@ -1453,49 +1920,35 @@
                 return kDone_Verb;
             }
             fMoveTo = *srcPts;
-            if (pts) {
-                pts[0] = *srcPts;
-            }
+            pts[0] = *srcPts;
             srcPts += 1;
             fSegmentState = kAfterMove_SegmentState;
             fLastPt = fMoveTo;
             fNeedClose = fForceClose;
             break;
         case kLine_Verb:
-            if (this->cons_moveTo(pts)) {
-                return kMove_Verb;
-            }
-            if (pts) {
-                pts[1] = srcPts[0];
-            }
+            pts[0] = this->cons_moveTo();
+            pts[1] = srcPts[0];
             fLastPt = srcPts[0];
             fCloseLine = false;
             srcPts += 1;
             break;
         case kQuad_Verb:
-            if (this->cons_moveTo(pts)) {
-                return kMove_Verb;
-            }
-            if (pts) {
-                memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
-            }
+            pts[0] = this->cons_moveTo();
+            memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
             fLastPt = srcPts[1];
             srcPts += 2;
             break;
         case kCubic_Verb:
-            if (this->cons_moveTo(pts)) {
-                return kMove_Verb;
-            }
-            if (pts) {
-                memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
-            }
+            pts[0] = this->cons_moveTo();
+            memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
             fLastPt = srcPts[2];
             srcPts += 3;
             break;
         case kClose_Verb:
             verb = this->autoClose(pts);
             if (verb == kLine_Verb) {
-                fVerbs -= 1;
+                fVerbs++; // move back one verb
             } else {
                 fNeedClose = false;
                 fSegmentState = kEmptyContour_SegmentState;
@@ -1524,59 +1977,51 @@
 }
 
 void SkPath::RawIter::setPath(const SkPath& path) {
-    fPts = path.fPts.begin();
-    fVerbs = path.fVerbs.begin();
-    fVerbStop = path.fVerbs.end();
+    fPts = path.fPathRef->points();
+    fVerbs = path.fPathRef->verbs();
+    fVerbStop = path.fPathRef->verbsMemBegin();
     fMoveTo.fX = fMoveTo.fY = 0;
     fLastPt.fX = fLastPt.fY = 0;
 }
 
 SkPath::Verb SkPath::RawIter::next(SkPoint pts[4]) {
+    SkASSERT(NULL != pts);
     if (fVerbs == fVerbStop) {
         return kDone_Verb;
     }
 
-    unsigned        verb = *fVerbs++;
-    const SkPoint*  srcPts = fPts;
+    // fVerbs points one beyond next verb so decrement first.
+    unsigned verb = *(--fVerbs);
+    const SkPoint* srcPts = fPts;
 
     switch (verb) {
         case kMove_Verb:
-            if (pts) {
-                pts[0] = *srcPts;
-            }
+            pts[0] = *srcPts;
             fMoveTo = srcPts[0];
             fLastPt = fMoveTo;
             srcPts += 1;
             break;
         case kLine_Verb:
-            if (pts) {
-                pts[0] = fLastPt;
-                pts[1] = srcPts[0];
-            }
+            pts[0] = fLastPt;
+            pts[1] = srcPts[0];
             fLastPt = srcPts[0];
             srcPts += 1;
             break;
         case kQuad_Verb:
-            if (pts) {
-                pts[0] = fLastPt;
-                memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
-            }
+            pts[0] = fLastPt;
+            memcpy(&pts[1], srcPts, 2 * sizeof(SkPoint));
             fLastPt = srcPts[1];
             srcPts += 2;
             break;
         case kCubic_Verb:
-            if (pts) {
-                pts[0] = fLastPt;
-                memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
-            }
+            pts[0] = fLastPt;
+            memcpy(&pts[1], srcPts, 3 * sizeof(SkPoint));
             fLastPt = srcPts[2];
             srcPts += 3;
             break;
         case kClose_Verb:
             fLastPt = fMoveTo;
-            if (pts) {
-                pts[0] = fMoveTo;
-            }
+            pts[0] = fMoveTo;
             break;
     }
     fPts = srcPts;
@@ -1586,36 +2031,115 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 /*
-    Format in flattened buffer: [ptCount, verbCount, pts[], verbs[]]
+    Format in compressed buffer: [ptCount, verbCount, pts[], verbs[]]
 */
 
-void SkPath::flatten(SkWriter32& buffer) const {
+uint32_t SkPath::writeToMemory(void* storage) const {
     SkDEBUGCODE(this->validate();)
 
-    buffer.write32(fPts.count());
-    buffer.write32(fVerbs.count());
-    buffer.write32((fFillType << 8) | fSegmentMask);
-    buffer.writeMul4(fPts.begin(), sizeof(SkPoint) * fPts.count());
-    buffer.writePad(fVerbs.begin(), fVerbs.count());
+    if (NULL == storage) {
+        const int byteCount = sizeof(int32_t)
+#if NEW_PICTURE_FORMAT
+                      + fPathRef->writeSize()
+#else
+                      + 2 * sizeof(int32_t)
+                      + sizeof(SkPoint) * fPathRef->countPoints()
+                      + sizeof(uint8_t) * fPathRef->countVerbs()
+#endif
+                      + sizeof(SkRect);
+        return SkAlign4(byteCount);
+    }
+
+    SkWBuffer   buffer(storage);
+#if !NEW_PICTURE_FORMAT
+    buffer.write32(fPathRef->countPoints());
+    buffer.write32(fPathRef->countVerbs());
+#endif
+
+    // Call getBounds() to ensure (as a side-effect) that fBounds
+    // and fIsFinite are computed.
+    const SkRect& bounds = this->getBounds();
+    SkASSERT(!fBoundsIsDirty);
+
+    int32_t packed = ((fIsFinite & 1) << kIsFinite_SerializationShift) |
+                     ((fIsOval & 1) << kIsOval_SerializationShift) |
+                     (fConvexity << kConvexity_SerializationShift) |
+                     (fFillType << kFillType_SerializationShift) |
+                     (fSegmentMask << kSegmentMask_SerializationShift) |
+                     (fDirection << kDirection_SerializationShift);
+
+    buffer.write32(packed);
+
+    fPathRef->writeToBuffer(&buffer);
+
+    buffer.write(&bounds, sizeof(bounds));
+
+    buffer.padToAlign4();
+    return buffer.pos();
 }
 
-void SkPath::unflatten(SkReader32& buffer) {
-    fPts.setCount(buffer.readS32());
-    fVerbs.setCount(buffer.readS32());
+uint32_t SkPath::readFromMemory(const void* storage) {
+    SkRBuffer   buffer(storage);
+#if !NEW_PICTURE_FORMAT
+    int32_t pcount = buffer.readS32();
+    int32_t vcount = buffer.readS32();
+#endif
+
     uint32_t packed = buffer.readS32();
-    fFillType = packed >> 8;
-    fSegmentMask = packed & 0xFF;
-    buffer.read(fPts.begin(), sizeof(SkPoint) * fPts.count());
-    buffer.read(fVerbs.begin(), fVerbs.count());
+    fIsFinite = (packed >> kIsFinite_SerializationShift) & 1;
+    fIsOval = (packed >> kIsOval_SerializationShift) & 1;
+    fConvexity = (packed >> kConvexity_SerializationShift) & 0xFF;
+    fFillType = (packed >> kFillType_SerializationShift) & 0xFF;
+    fSegmentMask = (packed >> kSegmentMask_SerializationShift) & 0x7;
+    fDirection = (packed >> kDirection_SerializationShift) & 0x3;
+
+#if NEW_PICTURE_FORMAT
+    fPathRef.reset(SkPathRef::CreateFromBuffer(&buffer));
+#else
+    fPathRef.reset(SkPathRef::CreateFromBuffer(vcount, pcount, &buffer));
+#endif
+
+    buffer.read(&fBounds, sizeof(fBounds));
+    fBoundsIsDirty = false;
+
+    buffer.skipToAlign4();
 
     GEN_ID_INC;
-    DIRTY_AFTER_EDIT;
 
     SkDEBUGCODE(this->validate();)
+    return buffer.pos();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#include "SkString.h"
+
+static void append_scalar(SkString* str, SkScalar value) {
+    SkString tmp;
+    tmp.printf("%g", value);
+    if (tmp.contains('.')) {
+        tmp.appendUnichar('f');
+    }
+    str->append(tmp);
+}
+
+static void append_params(SkString* str, const char label[], const SkPoint pts[],
+                          int count) {
+    str->append(label);
+    str->append("(");
+
+    const SkScalar* values = &pts[0].fX;
+    count *= 2;
+
+    for (int i = 0; i < count; ++i) {
+        append_scalar(str, values[i]);
+        if (i < count - 1) {
+            str->append(", ");
+        }
+    }
+    str->append(");\n");
+}
+
 void SkPath::dump(bool forceClose, const char title[]) const {
     Iter    iter(*this, forceClose);
     SkPoint pts[4];
@@ -1624,48 +2148,25 @@
     SkDebugf("path: forceClose=%s %s\n", forceClose ? "true" : "false",
              title ? title : "");
 
-    while ((verb = iter.next(pts)) != kDone_Verb) {
+    SkString builder;
+
+    while ((verb = iter.next(pts, false)) != kDone_Verb) {
         switch (verb) {
             case kMove_Verb:
-#ifdef SK_CAN_USE_FLOAT
-                SkDebugf("  path: moveTo [%g %g]\n",
-                        SkScalarToFloat(pts[0].fX), SkScalarToFloat(pts[0].fY));
-#else
-                SkDebugf("  path: moveTo [%x %x]\n", pts[0].fX, pts[0].fY);
-#endif
+                append_params(&builder, "path.moveTo", &pts[0], 1);
                 break;
             case kLine_Verb:
-#ifdef SK_CAN_USE_FLOAT
-                SkDebugf("  path: lineTo [%g %g]\n",
-                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY));
-#else
-                SkDebugf("  path: lineTo [%x %x]\n", pts[1].fX, pts[1].fY);
-#endif
+                append_params(&builder, "path.lineTo", &pts[1], 1);
+                append_params(&builder, "path.lineTo", &pts[1], 1);
                 break;
             case kQuad_Verb:
-#ifdef SK_CAN_USE_FLOAT
-                SkDebugf("  path: quadTo [%g %g] [%g %g]\n",
-                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
-                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY));
-#else
-                SkDebugf("  path: quadTo [%x %x] [%x %x]\n",
-                         pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
-#endif
+                append_params(&builder, "path.quadTo", &pts[1], 2);
                 break;
             case kCubic_Verb:
-#ifdef SK_CAN_USE_FLOAT
-                SkDebugf("  path: cubeTo [%g %g] [%g %g] [%g %g]\n",
-                        SkScalarToFloat(pts[1].fX), SkScalarToFloat(pts[1].fY),
-                        SkScalarToFloat(pts[2].fX), SkScalarToFloat(pts[2].fY),
-                        SkScalarToFloat(pts[3].fX), SkScalarToFloat(pts[3].fY));
-#else
-                SkDebugf("  path: cubeTo [%x %x] [%x %x] [%x %x]\n",
-                         pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY,
-                         pts[3].fX, pts[3].fY);
-#endif
+                append_params(&builder, "path.cubicTo", &pts[1], 3);
                 break;
             case kClose_Verb:
-                SkDebugf("  path: close\n");
+                builder.append("path.close();\n");
                 break;
             default:
                 SkDebugf("  path: UNKNOWN VERB %d, aborting dump...\n", verb);
@@ -1673,7 +2174,7 @@
                 break;
         }
     }
-    SkDebugf("path: done %s\n", title ? title : "");
+    SkDebugf("%s\n", builder.c_str());
 }
 
 void SkPath::dump() const {
@@ -1684,13 +2185,15 @@
 void SkPath::validate() const {
     SkASSERT(this != NULL);
     SkASSERT((fFillType & ~3) == 0);
-    fPts.validate();
-    fVerbs.validate();
 
+#ifdef SK_DEBUG_PATH
     if (!fBoundsIsDirty) {
         SkRect bounds;
-        compute_pt_bounds(&bounds, fPts);
-        if (fPts.count() <= 1) {
+
+        bool isFinite = compute_pt_bounds(&bounds, *fPathRef.get());
+        SkASSERT(SkToBool(fIsFinite) == isFinite);
+
+        if (fPathRef->countPoints() <= 1) {
             // if we're empty, fBounds may be empty but translated, so we can't
             // necessarily compare to bounds directly
             // try path.addOval(2, 2, 2, 2) which is empty, but the bounds will
@@ -1709,8 +2212,9 @@
     }
 
     uint32_t mask = 0;
-    for (int i = 0; i < fVerbs.count(); i++) {
-        switch (fVerbs[i]) {
+    const uint8_t* verbs = const_cast<const SkPathRef*>(fPathRef.get())->verbs();
+    for (int i = 0; i < fPathRef->countVerbs(); i++) {
+        switch (verbs[~i]) {
             case kLine_Verb:
                 mask |= kLine_SegmentMask;
                 break;
@@ -1719,11 +2223,21 @@
                 break;
             case kCubic_Verb:
                 mask |= kCubic_SegmentMask;
+            case kMove_Verb:  // these verbs aren't included in the segment mask.
+            case kClose_Verb:
+                break;
+            case kDone_Verb:
+                SkDEBUGFAIL("Done verb shouldn't be recorded.");
+                break;
+            default:
+                SkDEBUGFAIL("Unknown Verb");
+                break;
         }
     }
     SkASSERT(mask == fSegmentMask);
+#endif // SK_DEBUG_PATH
 }
-#endif
+#endif // SK_DEBUG
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -1736,7 +2250,10 @@
 
 // only valid for a single contour
 struct Convexicator {
-    Convexicator() : fPtCount(0), fConvexity(SkPath::kConvex_Convexity) {
+    Convexicator()
+    : fPtCount(0)
+    , fConvexity(SkPath::kConvex_Convexity)
+    , fDirection(SkPath::kUnknown_Direction) {
         fSign = 0;
         // warnings
         fCurrPt.set(0, 0);
@@ -1750,6 +2267,9 @@
 
     SkPath::Convexity getConvexity() const { return fConvexity; }
 
+    /** The direction returned is only valid if the path is determined convex */
+    SkPath::Direction getDirection() const { return fDirection; }
+
     void addPt(const SkPoint& pt) {
         if (SkPath::kConcave_Convexity == fConvexity) {
             return;
@@ -1768,14 +2288,14 @@
                     SkASSERT(fPtCount > 2);
                     this->addVec(vec);
                 }
-                
+
                 int sx = sign(vec.fX);
                 int sy = sign(vec.fY);
                 fDx += (sx != fSx);
                 fDy += (sy != fSy);
                 fSx = sx;
                 fSy = sy;
-                
+
                 if (fDx > 3 || fDy > 3) {
                     fConvexity = SkPath::kConcave_Convexity;
                 }
@@ -1797,9 +2317,15 @@
         int sign = CrossProductSign(fVec0, fVec1);
         if (0 == fSign) {
             fSign = sign;
+            if (1 == sign) {
+                fDirection = SkPath::kCW_Direction;
+            } else if (-1 == sign) {
+                fDirection = SkPath::kCCW_Direction;
+            }
         } else if (sign) {
             if (fSign != sign) {
                 fConvexity = SkPath::kConcave_Convexity;
+                fDirection = SkPath::kUnknown_Direction;
             }
         }
     }
@@ -1809,13 +2335,15 @@
     int                 fPtCount;   // non-degenerate points
     int                 fSign;
     SkPath::Convexity   fConvexity;
+    SkPath::Direction   fDirection;
     int                 fDx, fDy, fSx, fSy;
 };
 
-SkPath::Convexity SkPath::ComputeConvexity(const SkPath& path) {
+SkPath::Convexity SkPath::internalGetConvexity() const {
+    SkASSERT(kUnknown_Convexity == fConvexity);
     SkPoint         pts[4];
     SkPath::Verb    verb;
-    SkPath::Iter    iter(path, true);
+    SkPath::Iter    iter(*this, true);
 
     int             contourCount = 0;
     int             count;
@@ -1825,6 +2353,7 @@
         switch (verb) {
             case kMove_Verb:
                 if (++contourCount > 1) {
+                    fConvexity = kConcave_Convexity;
                     return kConcave_Convexity;
                 }
                 pts[1] = pts[0];
@@ -1839,6 +2368,7 @@
                 break;
             default:
                 SkDEBUGFAIL("bad verb");
+                fConvexity = kConcave_Convexity;
                 return kConcave_Convexity;
         }
 
@@ -1847,17 +2377,22 @@
         }
         // early exit
         if (kConcave_Convexity == state.getConvexity()) {
+            fConvexity = kConcave_Convexity;
             return kConcave_Convexity;
         }
     }
-    return state.getConvexity();
+    fConvexity = state.getConvexity();
+    if (kConvex_Convexity == fConvexity && kUnknown_Direction == fDirection) {
+        fDirection = state.getDirection();
+    }
+    return static_cast<Convexity>(fConvexity);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 class ContourIter {
 public:
-    ContourIter(const SkTDArray<uint8_t>& verbs, const SkTDArray<SkPoint>& pts);
+    ContourIter(const SkPathRef& pathRef);
 
     bool done() const { return fDone; }
     // if !done() then these may be called
@@ -1874,20 +2409,18 @@
     SkDEBUGCODE(int fContourCounter;)
 };
 
-ContourIter::ContourIter(const SkTDArray<uint8_t>& verbs,
-                         const SkTDArray<SkPoint>& pts) {
-    fStopVerbs = verbs.begin() + verbs.count();
-    
+ContourIter::ContourIter(const SkPathRef& pathRef) {
+    fStopVerbs = pathRef.verbsMemBegin();
     fDone = false;
-    fCurrPt = pts.begin();
-    fCurrVerb = verbs.begin();
+    fCurrPt = pathRef.points();
+    fCurrVerb = pathRef.verbs();
     fCurrPtCount = 0;
     SkDEBUGCODE(fContourCounter = 0;)
     this->next();
 }
 
 void ContourIter::next() {
-    if (fCurrVerb >= fStopVerbs) {
+    if (fCurrVerb <= fStopVerbs) {
         fDone = true;
     }
     if (fDone) {
@@ -1897,12 +2430,12 @@
     // skip pts of prev contour
     fCurrPt += fCurrPtCount;
 
-    SkASSERT(SkPath::kMove_Verb == fCurrVerb[0]);
+    SkASSERT(SkPath::kMove_Verb == fCurrVerb[~0]);
     int ptCount = 1;    // moveTo
     const uint8_t* verbs = fCurrVerb;
 
-    for (++verbs; verbs < fStopVerbs; ++verbs) {
-        switch (*verbs) {
+    for (--verbs; verbs > fStopVerbs; --verbs) {
+        switch (verbs[~0]) {
             case SkPath::kMove_Verb:
                 goto CONTOUR_END;
             case SkPath::kLine_Verb:
@@ -2003,11 +2536,10 @@
     return minIndex;
 }
 
-static bool crossToDir(SkScalar cross, SkPath::Direction* dir) {
+static void crossToDir(SkScalar cross, SkPath::Direction* dir) {
     if (dir) {
         *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
     }
-    return true;
 }
 
 #if 0
@@ -2020,6 +2552,51 @@
 }
 #endif
 
+namespace {
+// for use with convex_dir_test
+double mul(double a, double b) { return a * b; }
+SkScalar mul(SkScalar a, SkScalar b) { return SkScalarMul(a, b); }
+double toDouble(SkScalar a) { return SkScalarToDouble(a); }
+SkScalar toScalar(SkScalar a) { return a; }
+
+// determines the winding direction of a convex polygon with the precision
+// of T. CAST_SCALAR casts an SkScalar to T.
+template <typename T, T (CAST_SCALAR)(SkScalar)>
+bool convex_dir_test(int n, const SkPoint pts[], SkPath::Direction* dir) {
+    // we find the first three points that form a non-degenerate
+    // triangle. If there are no such points then the path is
+    // degenerate. The first is always point 0. Now we find the second
+    // point.
+    int i = 0;
+    enum { kX = 0, kY = 1 };
+    T v0[2];
+    while (1) {
+        v0[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
+        v0[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
+        if (v0[kX] || v0[kY]) {
+            break;
+        }
+        if (++i == n - 1) {
+            return false;
+        }
+    }
+    // now find a third point that is not colinear with the first two
+    // points and check the orientation of the triangle (which will be
+    // the same as the orientation of the path).
+    for (++i; i < n; ++i) {
+        T v1[2];
+        v1[kX] = CAST_SCALAR(pts[i].fX) - CAST_SCALAR(pts[0].fX);
+        v1[kY] = CAST_SCALAR(pts[i].fY) - CAST_SCALAR(pts[0].fY);
+        T cross = mul(v0[kX], v1[kY]) - mul(v0[kY], v1[kX]);
+        if (0 != cross) {
+            *dir = cross > 0 ? SkPath::kCW_Direction : SkPath::kCCW_Direction;
+            return true;
+        }
+    }
+    return false;
+}
+}
+
 /*
  *  We loop through all contours, and keep the computed cross-product of the
  *  contour that contained the global y-max. If we just look at the first
@@ -2032,9 +2609,14 @@
 //    dumpPath(*this);
     // don't want to pay the cost for computing this if it
     // is unknown, so we don't call isConvex()
+
+    if (kUnknown_Direction != fDirection) {
+        *dir = static_cast<Direction>(fDirection);
+        return true;
+    }
     const Convexity conv = this->getConvexityOrUnknown();
 
-    ContourIter iter(fVerbs, fPts);
+    ContourIter iter(*fPathRef.get());
 
     // initialize with our logical y-min
     SkScalar ymax = this->getBounds().fTop;
@@ -2049,15 +2631,18 @@
         const SkPoint* pts = iter.pts();
         SkScalar cross = 0;
         if (kConvex_Convexity == conv) {
-            // we loop, skipping over degenerate or flat segments that will
-            // return 0 for the cross-product
-            for (int i = 0; i < n - 2; ++i) {
-                cross = cross_prod(pts[i], pts[i + 1], pts[i + 2]);
-                if (cross) {
-                    // early-exit, as kConvex is assumed to have only 1
-                    // non-degenerate contour
-                    return crossToDir(cross, dir);
-                }
+            // We try first at scalar precision, and then again at double
+            // precision. This is because the vectors computed between distant
+            // points may lose too much precision.
+            if (convex_dir_test<SkScalar, toScalar>(n, pts, dir)) {
+                fDirection = *dir;
+                return true;
+            }
+            if (convex_dir_test<double, toDouble>(n, pts, dir)) {
+                fDirection = *dir;
+                return true;
+            } else {
+                return false;
             }
         } else {
             int index = find_max_y(pts, n);
@@ -2087,7 +2672,7 @@
                 // Its possible that we can't find two non-degenerate vectors, so
                 // we have to guard our search (e.g. all the pts could be in the
                 // same place).
-                
+
                 // we pass n - 1 instead of -1 so we don't foul up % operator by
                 // passing it a negative LH argument.
                 int prev = find_diff_pt(pts, index, n, n - 1);
@@ -2098,14 +2683,15 @@
                 int next = find_diff_pt(pts, index, n, 1);
                 SkASSERT(next != index);
                 cross = cross_prod(pts[prev], pts[index], pts[next]);
-                // if we get a zero, but the pts aren't on top of each other, then
-                // we can just look at the direction
-                if (0 == cross) {
+                // if we get a zero and the points are horizontal, then we look at the spread in
+                // x-direction. We really should continue to walk away from the degeneracy until
+                // there is a divergence.
+                if (0 == cross && pts[prev].fY == pts[index].fY && pts[next].fY == pts[index].fY) {
                     // construct the subtract so we get the correct Direction below
                     cross = pts[index].fX - pts[next].fX;
                 }
             }
-            
+
             if (cross) {
                 // record our best guess so far
                 ymax = pts[index].fY;
@@ -2113,6 +2699,262 @@
             }
         }
     }
+    if (ymaxCross) {
+        crossToDir(ymaxCross, dir);
+        fDirection = *dir;
+        return true;
+    } else {
+        return false;
+    }
+}
 
-    return ymaxCross ? crossToDir(ymaxCross, dir) : false;
+///////////////////////////////////////////////////////////////////////////////
+
+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);
+}
+
+static SkScalar eval_cubic_pts(SkScalar c0, SkScalar c1, SkScalar c2, SkScalar c3,
+                               SkScalar t) {
+    SkScalar A = c3 + 3*(c1 - c2) - c0;
+    SkScalar B = 3*(c2 - c1 - c1 + c0);
+    SkScalar C = 3*(c1 - c0);
+    SkScalar D = c0;
+    return eval_cubic_coeff(A, B, C, D, t);
+}
+
+/*  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;
+    int i;
+    for (i = 0; i < 16; 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;
+    return true;
+}
+
+template <size_t N> static void find_minmax(const SkPoint pts[],
+                                            SkScalar* minPtr, SkScalar* maxPtr) {
+    SkScalar min, max;
+    min = max = pts[0].fX;
+    for (size_t i = 1; i < N; ++i) {
+        min = SkMinScalar(min, pts[i].fX);
+        max = SkMaxScalar(max, pts[i].fX);
+    }
+    *minPtr = min;
+    *maxPtr = max;
+}
+
+static int winding_mono_cubic(const SkPoint pts[], SkScalar x, SkScalar y) {
+    SkPoint storage[4];
+
+    int dir = 1;
+    if (pts[0].fY > pts[3].fY) {
+        storage[0] = pts[3];
+        storage[1] = pts[2];
+        storage[2] = pts[1];
+        storage[3] = pts[0];
+        pts = storage;
+        dir = -1;
+    }
+    if (y < pts[0].fY || y >= pts[3].fY) {
+        return 0;
+    }
+
+    // quickreject or quickaccept
+    SkScalar min, max;
+    find_minmax<4>(pts, &min, &max);
+    if (x < min) {
+        return 0;
+    }
+    if (x > max) {
+        return dir;
+    }
+
+    // compute the actual x(t) value
+    SkScalar t, xt;
+    if (chopMonoCubicAt(pts[0].fY, pts[1].fY, pts[2].fY, pts[3].fY, y, &t)) {
+        xt = eval_cubic_pts(pts[0].fX, pts[1].fX, pts[2].fX, pts[3].fX, t);
+    } else {
+        SkScalar mid = SkScalarAve(pts[0].fY, pts[3].fY);
+        xt = y < mid ? pts[0].fX : pts[3].fX;
+    }
+    return xt < x ? dir : 0;
+}
+
+static int winding_cubic(const SkPoint pts[], SkScalar x, SkScalar y) {
+    SkPoint dst[10];
+    int n = SkChopCubicAtYExtrema(pts, dst);
+    int w = 0;
+    for (int i = 0; i <= n; ++i) {
+        w += winding_mono_cubic(&dst[i * 3], x, y);
+    }
+    return w;
+}
+
+static int winding_mono_quad(const SkPoint pts[], SkScalar x, SkScalar y) {
+    SkScalar y0 = pts[0].fY;
+    SkScalar y2 = pts[2].fY;
+
+    int dir = 1;
+    if (y0 > y2) {
+        SkTSwap(y0, y2);
+        dir = -1;
+    }
+    if (y < y0 || y >= y2) {
+        return 0;
+    }
+
+    // bounds check on X (not required. is it faster?)
+#if 0
+    if (pts[0].fX > x && pts[1].fX > x && pts[2].fX > x) {
+        return 0;
+    }
+#endif
+
+    SkScalar roots[2];
+    int n = SkFindUnitQuadRoots(pts[0].fY - 2 * pts[1].fY + pts[2].fY,
+                                2 * (pts[1].fY - pts[0].fY),
+                                pts[0].fY - y,
+                                roots);
+    SkASSERT(n <= 1);
+    SkScalar xt;
+    if (0 == n) {
+        SkScalar mid = SkScalarAve(y0, y2);
+        // Need [0] and [2] if dir == 1
+        // and  [2] and [0] if dir == -1
+        xt = y < mid ? pts[1 - dir].fX : pts[dir - 1].fX;
+    } else {
+        SkScalar t = roots[0];
+        SkScalar C = pts[0].fX;
+        SkScalar A = pts[2].fX - 2 * pts[1].fX + C;
+        SkScalar B = 2 * (pts[1].fX - C);
+        xt = SkScalarMulAdd(SkScalarMulAdd(A, t, B), t, C);
+    }
+    return xt < x ? dir : 0;
+}
+
+static bool is_mono_quad(SkScalar y0, SkScalar y1, SkScalar y2) {
+    //    return SkScalarSignAsInt(y0 - y1) + SkScalarSignAsInt(y1 - y2) != 0;
+    if (y0 == y1) {
+        return true;
+    }
+    if (y0 < y1) {
+        return y1 <= y2;
+    } else {
+        return y1 >= y2;
+    }
+}
+
+static int winding_quad(const SkPoint pts[], SkScalar x, SkScalar y) {
+    SkPoint dst[5];
+    int     n = 0;
+
+    if (!is_mono_quad(pts[0].fY, pts[1].fY, pts[2].fY)) {
+        n = SkChopQuadAtYExtrema(pts, dst);
+        pts = dst;
+    }
+    int w = winding_mono_quad(pts, x, y);
+    if (n > 0) {
+        w += winding_mono_quad(&pts[2], x, y);
+    }
+    return w;
+}
+
+static int winding_line(const SkPoint pts[], SkScalar x, SkScalar y) {
+    SkScalar x0 = pts[0].fX;
+    SkScalar y0 = pts[0].fY;
+    SkScalar x1 = pts[1].fX;
+    SkScalar y1 = pts[1].fY;
+
+    SkScalar dy = y1 - y0;
+
+    int dir = 1;
+    if (y0 > y1) {
+        SkTSwap(y0, y1);
+        dir = -1;
+    }
+    if (y < y0 || y >= y1) {
+        return 0;
+    }
+
+    SkScalar cross = SkScalarMul(x1 - x0, y - pts[0].fY) -
+    SkScalarMul(dy, x - pts[0].fX);
+
+    if (SkScalarSignAsInt(cross) == dir) {
+        dir = 0;
+    }
+    return dir;
+}
+
+bool SkPath::contains(SkScalar x, SkScalar y) const {
+    bool isInverse = this->isInverseFillType();
+    if (this->isEmpty()) {
+        return isInverse;
+    }
+
+    const SkRect& bounds = this->getBounds();
+    if (!bounds.contains(x, y)) {
+        return isInverse;
+    }
+
+    SkPath::Iter iter(*this, true);
+    bool done = false;
+    int w = 0;
+    do {
+        SkPoint pts[4];
+        switch (iter.next(pts, false)) {
+            case SkPath::kMove_Verb:
+            case SkPath::kClose_Verb:
+                break;
+            case SkPath::kLine_Verb:
+                w += winding_line(pts, x, y);
+                break;
+            case SkPath::kQuad_Verb:
+                w += winding_quad(pts, x, y);
+                break;
+            case SkPath::kCubic_Verb:
+                w += winding_cubic(pts, x, y);
+                break;
+            case SkPath::kDone_Verb:
+                done = true;
+                break;
+        }
+    } while (!done);
+
+    switch (this->getFillType()) {
+        case SkPath::kEvenOdd_FillType:
+        case SkPath::kInverseEvenOdd_FillType:
+            w &= 1;
+            break;
+        default:
+            break;
+    }
+    return SkToBool(w);
 }
diff --git a/src/core/SkPathEffect.cpp b/src/core/SkPathEffect.cpp
index d81ac9f..8306d7a 100644
--- a/src/core/SkPathEffect.cpp
+++ b/src/core/SkPathEffect.cpp
@@ -6,10 +6,22 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkPathEffect.h"
 #include "SkPath.h"
-#include "SkBuffer.h"
+#include "SkFlattenableBuffers.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_INST_COUNT(SkPathEffect)
+
+void SkPathEffect::computeFastBounds(SkRect* dst, const SkRect& src) const {
+    *dst = src;
+}
+
+bool SkPathEffect::asPoints(PointData* results, const SkPath& src,
+                    const SkStrokeRec&, const SkMatrix&, const SkRect*) const {
+    return false;
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -29,21 +41,22 @@
 /*
     Format: [oe0-factory][pe1-factory][pe0-size][pe0-data][pe1-data]
 */
-void SkPairPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkPairPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
     buffer.writeFlattenable(fPE0);
     buffer.writeFlattenable(fPE1);
 }
 
 SkPairPathEffect::SkPairPathEffect(SkFlattenableReadBuffer& buffer) {
-    fPE0 = (SkPathEffect*)buffer.readFlattenable();
-    fPE1 = (SkPathEffect*)buffer.readFlattenable();
+    fPE0 = buffer.readFlattenableT<SkPathEffect>();
+    fPE1 = buffer.readFlattenableT<SkPathEffect>();
     // either of these may fail, so we have to check for nulls later on
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkComposePathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                     SkScalar* width) {
+                             SkStrokeRec* rec, const SkRect* cullRect) const {
     // we may have failed to unflatten these, so we have to check
     if (!fPE0 || !fPE1) {
         return false;
@@ -52,91 +65,17 @@
     SkPath          tmp;
     const SkPath*   ptr = &src;
 
-    if (fPE1->filterPath(&tmp, src, width)) {
+    if (fPE1->filterPath(&tmp, src, rec, cullRect)) {
         ptr = &tmp;
     }
-    return fPE0->filterPath(dst, *ptr, width);
+    return fPE0->filterPath(dst, *ptr, rec, cullRect);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 bool SkSumPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                 SkScalar* width) {
+                             SkStrokeRec* rec, const SkRect* cullRect) const {
     // use bit-or so that we always call both, even if the first one succeeds
-    return  fPE0->filterPath(dst, src, width) | fPE1->filterPath(dst, src, width);
+    return fPE0->filterPath(dst, src, rec, cullRect) |
+           fPE1->filterPath(dst, src, rec, cullRect);
 }
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkStroke.h"
-
-SkStrokePathEffect::SkStrokePathEffect(const SkPaint& paint)
-    : fWidth(paint.getStrokeWidth()), fMiter(paint.getStrokeMiter()),
-      fStyle(SkToU8(paint.getStyle())), fJoin(SkToU8(paint.getStrokeJoin())),
-      fCap(SkToU8(paint.getStrokeCap())) {
-}
-
-SkStrokePathEffect::SkStrokePathEffect(SkScalar width, SkPaint::Style style,
-                           SkPaint::Join join, SkPaint::Cap cap, SkScalar miter)
-        : fWidth(width), fMiter(miter), fStyle(SkToU8(style)),
-          fJoin(SkToU8(join)), fCap(SkToU8(cap)) {
-    if (miter < 0) {  // signal they want the default
-        fMiter = SkIntToScalar(4);
-    }
-}
-
-bool SkStrokePathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                    SkScalar* width) {
-    if (fWidth < 0 || fStyle == SkPaint::kFill_Style) {
-        return false;
-    }
-
-    if (fStyle == SkPaint::kStroke_Style && fWidth == 0) {  // hairline
-        *width = 0;
-        return true;
-    }
-
-    SkStroke    stroke;
-
-    stroke.setWidth(fWidth);
-    stroke.setMiterLimit(fMiter);
-    stroke.setJoin((SkPaint::Join)fJoin);
-    stroke.setCap((SkPaint::Cap)fCap);
-    stroke.setDoFill(fStyle == SkPaint::kStrokeAndFill_Style);
-
-    stroke.strokePath(src, dst);
-    return true;
-}
-
-SkFlattenable::Factory SkStrokePathEffect::getFactory() {
-    return CreateProc;
-}
-
-SkFlattenable* SkStrokePathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkStrokePathEffect, (buffer));
-}
-
-void SkStrokePathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
-    buffer.writeScalar(fWidth);
-    buffer.writeScalar(fMiter);
-    buffer.write8(fStyle);
-    buffer.write8(fJoin);
-    buffer.write8(fCap);
-}
-
-SkStrokePathEffect::SkStrokePathEffect(SkFlattenableReadBuffer& buffer) {
-    fWidth = buffer.readScalar();
-    fMiter = buffer.readScalar();
-    fStyle = buffer.readU8();
-    fJoin = buffer.readU8();
-    fCap = buffer.readU8();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkPathEffect)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposePathEffect)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStrokePathEffect)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
diff --git a/src/core/SkPathHeap.cpp b/src/core/SkPathHeap.cpp
index 9bf77c1..b8ca40b 100644
--- a/src/core/SkPathHeap.cpp
+++ b/src/core/SkPathHeap.cpp
@@ -8,9 +8,11 @@
 #include "SkPathHeap.h"
 #include "SkPath.h"
 #include "SkStream.h"
-#include "SkFlattenable.h"
+#include "SkFlattenableBuffers.h"
 #include <new>
 
+SK_DEFINE_INST_COUNT(SkPathHeap)
+
 #define kPathCount  64
 
 SkPathHeap::SkPathHeap() : fHeap(kPathCount * sizeof(SkPath)) {
@@ -18,7 +20,7 @@
 
 SkPathHeap::SkPathHeap(SkFlattenableReadBuffer& buffer)
             : fHeap(kPathCount * sizeof(SkPath)) {
-    int count = buffer.readS32();
+    const int count = buffer.readInt();
 
     fPaths.setCount(count);
     SkPath** ptr = fPaths.begin();
@@ -26,7 +28,7 @@
 
     for (int i = 0; i < count; i++) {
         new (p) SkPath;
-        p->unflatten(buffer);
+        buffer.readPath(p);
         *ptr++ = p; // record the pointer
         p++;        // move to the next storage location
     }
@@ -50,13 +52,12 @@
 
 void SkPathHeap::flatten(SkFlattenableWriteBuffer& buffer) const {
     int count = fPaths.count();
-    
-    buffer.write32(count);
+
+    buffer.writeInt(count);
     SkPath** iter = fPaths.begin();
     SkPath** stop = fPaths.end();
     while (iter < stop) {
-        (*iter)->flatten(buffer);
+        buffer.writePath(**iter);
         iter++;
     }
 }
-
diff --git a/src/core/SkPathHeap.h b/src/core/SkPathHeap.h
index 99c2626..095e84a 100644
--- a/src/core/SkPathHeap.h
+++ b/src/core/SkPathHeap.h
@@ -18,8 +18,10 @@
 
 class SkPathHeap : public SkRefCnt {
 public:
-            SkPathHeap();
-            SkPathHeap(SkFlattenableReadBuffer&);
+    SK_DECLARE_INST_COUNT(SkPathHeap)
+
+    SkPathHeap();
+    SkPathHeap(SkFlattenableReadBuffer&);
     virtual ~SkPathHeap();
 
     /** Copy the path into the heap, and return the new total number of paths.
@@ -27,21 +29,22 @@
         this newly added (copied) path.
      */
     int append(const SkPath&);
-    
+
     // called during picture-playback
     int count() const { return fPaths.count(); }
     const SkPath& operator[](int index) const {
         return *fPaths[index];
     }
-    
+
     void flatten(SkFlattenableWriteBuffer&) const;
-        
+
 private:
     // we store the paths in the heap (placement new)
     SkChunkAlloc        fHeap;
     // we just store ptrs into fHeap here
     SkTDArray<SkPath*>  fPaths;
+
+    typedef SkRefCnt INHERITED;
 };
 
 #endif
-
diff --git a/src/core/SkPathMeasure.cpp b/src/core/SkPathMeasure.cpp
index 04d19c2..436753e 100644
--- a/src/core/SkPathMeasure.cpp
+++ b/src/core/SkPathMeasure.cpp
@@ -51,17 +51,6 @@
     return tspan >> 10;
 }
 
-#if 0
-static inline bool tangents_too_curvy(const SkVector& tan0, SkVector& tan1) {
-    static const SkScalar kFlatEnoughTangentDotProd = SK_Scalar1 * 99 / 100;
-
-    SkASSERT(kFlatEnoughTangentDotProd > 0 &&
-             kFlatEnoughTangentDotProd < SK_Scalar1);
-
-    return SkPoint::DotProduct(tan0, tan1) < kFlatEnoughTangentDotProd;
-}
-#endif
-
 // can't use tangents, since we need [0..1..................2] to be seen
 // as definitely not a line (it is when drawn, but not parametrically)
 // so we compare midpoints
@@ -107,9 +96,9 @@
         distance = this->compute_quad_segs(&tmp[2], distance, halft, maxt, ptIndex);
     } else {
         SkScalar d = SkPoint::Distance(pts[0], pts[2]);
-        SkASSERT(d >= 0);
-        if (!SkScalarNearlyZero(d)) {
-            distance += d;
+        SkScalar prevD = distance;
+        distance += d;
+        if (distance > prevD) {
             Segment* seg = fSegments.append();
             seg->fDistance = distance;
             seg->fPtIndex = ptIndex;
@@ -131,9 +120,9 @@
         distance = this->compute_cubic_segs(&tmp[3], distance, halft, maxt, ptIndex);
     } else {
         SkScalar d = SkPoint::Distance(pts[0], pts[3]);
-        SkASSERT(d >= 0);
-        if (!SkScalarNearlyZero(d)) {
-            distance += d;
+        SkScalar prevD = distance;
+        distance += d;
+        if (distance > prevD) {
             Segment* seg = fSegments.append();
             seg->fDistance = distance;
             seg->fPtIndex = ptIndex;
@@ -147,11 +136,18 @@
 void SkPathMeasure::buildSegments() {
     SkPoint         pts[4];
     int             ptIndex = fFirstPtIndex;
-    SkScalar        d, distance = 0;
+    SkScalar        distance = 0;
     bool            isClosed = fForceClosed;
     bool            firstMoveTo = ptIndex < 0;
     Segment*        seg;
 
+    /*  Note:
+     *  as we accumulate distance, we have to check that the result of +=
+     *  actually made it larger, since a very small delta might be > 0, but
+     *  still have no effect on distance (if distance >>> delta).
+     *
+     *  We do this check below, and in compute_quad_segs and compute_cubic_segs
+     */
     fSegments.reset();
     bool done = false;
     do {
@@ -166,37 +162,46 @@
                 firstMoveTo = false;
                 break;
 
-            case SkPath::kLine_Verb:
-                d = SkPoint::Distance(pts[0], pts[1]);
+            case SkPath::kLine_Verb: {
+                SkScalar d = SkPoint::Distance(pts[0], pts[1]);
                 SkASSERT(d >= 0);
+                SkScalar prevD = distance;
                 distance += d;
-                seg = fSegments.append();
-                seg->fDistance = distance;
-                seg->fPtIndex = ptIndex;
-                seg->fType = kLine_SegType;
-                seg->fTValue = kMaxTValue;
-                fPts.append(1, pts + 1);
-                ptIndex++;
-                break;
+                if (distance > prevD) {
+                    seg = fSegments.append();
+                    seg->fDistance = distance;
+                    seg->fPtIndex = ptIndex;
+                    seg->fType = kLine_SegType;
+                    seg->fTValue = kMaxTValue;
+                    fPts.append(1, pts + 1);
+                    ptIndex++;
+                }
+            } break;
 
-            case SkPath::kQuad_Verb:
+            case SkPath::kQuad_Verb: {
+                SkScalar prevD = distance;
                 distance = this->compute_quad_segs(pts, distance, 0,
                                                    kMaxTValue, ptIndex);
-                fPts.append(2, pts + 1);
-                ptIndex += 2;
-                break;
+                if (distance > prevD) {
+                    fPts.append(2, pts + 1);
+                    ptIndex += 2;
+                }
+            } break;
 
-            case SkPath::kCubic_Verb:
+            case SkPath::kCubic_Verb: {
+                SkScalar prevD = distance;
                 distance = this->compute_cubic_segs(pts, distance, 0,
                                                     kMaxTValue, ptIndex);
-                fPts.append(3, pts + 1);
-                ptIndex += 3;
-                break;
+                if (distance > prevD) {
+                    fPts.append(3, pts + 1);
+                    ptIndex += 3;
+                }
+            } break;
 
             case SkPath::kClose_Verb:
                 isClosed = true;
                 break;
-                
+
             case SkPath::kDone_Verb:
                 done = true;
                 break;
@@ -235,15 +240,13 @@
 #endif
 }
 
-static void compute_pos_tan(const SkTDArray<SkPoint>& segmentPts, int ptIndex,
-                    int segType, SkScalar t, SkPoint* pos, SkVector* tangent) {
-    const SkPoint*  pts = &segmentPts[ptIndex];
-
+static void compute_pos_tan(const SkPoint pts[], int segType,
+                            SkScalar t, SkPoint* pos, SkVector* tangent) {
     switch (segType) {
         case kLine_SegType:
             if (pos) {
                 pos->set(SkScalarInterp(pts[0].fX, pts[1].fX, t),
-                        SkScalarInterp(pts[0].fY, pts[1].fY, t));
+                         SkScalarInterp(pts[0].fY, pts[1].fY, t));
             }
             if (tangent) {
                 tangent->setNormalize(pts[1].fX - pts[0].fX, pts[1].fY - pts[0].fY);
@@ -266,17 +269,16 @@
     }
 }
 
-static void seg_to(const SkTDArray<SkPoint>& segmentPts, int ptIndex,
-                   int segType, SkScalar startT, SkScalar stopT, SkPath* dst) {
+static void seg_to(const SkPoint pts[], int segType,
+                   SkScalar startT, SkScalar stopT, SkPath* dst) {
     SkASSERT(startT >= 0 && startT <= SK_Scalar1);
     SkASSERT(stopT >= 0 && stopT <= SK_Scalar1);
     SkASSERT(startT <= stopT);
 
-    if (SkScalarNearlyZero(stopT - startT)) {
-        return;
+    if (startT == stopT) {
+        return; // should we report this, to undo a moveTo?
     }
 
-    const SkPoint*  pts = &segmentPts[ptIndex];
     SkPoint         tmp0[7], tmp1[7];
 
     switch (segType) {
@@ -416,8 +418,7 @@
 
 bool SkPathMeasure::getPosTan(SkScalar distance, SkPoint* pos,
                               SkVector* tangent) {
-    SkASSERT(fPath);
-    if (fPath == NULL) {
+    if (NULL == fPath) {
         return false;
     }
 
@@ -434,16 +435,20 @@
     } else if (distance > length) {
         distance = length;
     }
-    
+
     SkScalar        t;
     const Segment*  seg = this->distanceToSegment(distance, &t);
 
-    compute_pos_tan(fPts, seg->fPtIndex, seg->fType, t, pos, tangent);
+    compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, t, pos, tangent);
     return true;
 }
 
 bool SkPathMeasure::getMatrix(SkScalar distance, SkMatrix* matrix,
                               MatrixFlags flags) {
+    if (NULL == fPath) {
+        return false;
+    }
+
     SkPoint     position;
     SkVector    tangent;
 
@@ -486,19 +491,19 @@
     SkASSERT(seg <= stopSeg);
 
     if (startWithMoveTo) {
-        compute_pos_tan(fPts, seg->fPtIndex, seg->fType, startT, &p, NULL);
+        compute_pos_tan(&fPts[seg->fPtIndex], seg->fType, startT, &p, NULL);
         dst->moveTo(p);
     }
 
     if (seg->fPtIndex == stopSeg->fPtIndex) {
-        seg_to(fPts, seg->fPtIndex, seg->fType, startT, stopT, dst);
+        seg_to(&fPts[seg->fPtIndex], seg->fType, startT, stopT, dst);
     } else {
         do {
-            seg_to(fPts, seg->fPtIndex, seg->fType, startT, SK_Scalar1, dst);
+            seg_to(&fPts[seg->fPtIndex], seg->fType, startT, SK_Scalar1, dst);
             seg = SkPathMeasure::NextSegment(seg);
             startT = 0;
         } while (seg->fPtIndex < stopSeg->fPtIndex);
-        seg_to(fPts, seg->fPtIndex, seg->fType, 0, stopT, dst);
+        seg_to(&fPts[seg->fPtIndex], seg->fType, 0, stopT, dst);
     }
     return true;
 }
diff --git a/src/core/SkPathRef.h b/src/core/SkPathRef.h
new file mode 100644
index 0000000..5f85b7b
--- /dev/null
+++ b/src/core/SkPathRef.h
@@ -0,0 +1,606 @@
+
+/*
+ * 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 SkPathRef_DEFINED
+#define SkPathRef_DEFINED
+
+#include "SkRefCnt.h"
+#include <stddef.h> // ptrdiff_t
+
+// When we're ready to break the picture format. Changes:
+// * Write genID.
+// * SkPathRef read/write counts (which will change the field order)
+// * SkPathRef reads/writes verbs backwards.
+#define NEW_PICTURE_FORMAT 0
+
+/**
+ * Holds the path verbs and points. It is versioned by a generation ID. None of its public methods
+ * modify the contents. To modify or append to the verbs/points wrap the SkPathRef in an
+ * SkPathRef::Editor object. Installing the editor resets the generation ID. It also performs
+ * copy-on-write if the SkPathRef is shared by multipls SkPaths. The caller passes the Editor's
+ * constructor a SkAutoTUnref, which may be updated to point to a new SkPathRef after the editor's
+ * constructor returns.
+ *
+ * The points and verbs are stored in a single allocation. The points are at the begining of the
+ * allocation while the verbs are stored at end of the allocation, in reverse order. Thus the points
+ * and verbs both grow into the middle of the allocation until the meet. To access verb i in the
+ * verb array use ref.verbs()[~i] (because verbs() returns a pointer just beyond the first
+ * logical verb or the last verb in memory).
+ */
+
+class SkPathRef;
+
+// This path ref should never be deleted once it is created. It should not be global but was made
+// so for checks when SK_DEBUG_PATH_REF is enabled. It we be re-hidden when the debugging code is
+// reverted.
+SkPathRef* gEmptyPathRef;
+
+// Temporary hackery to try to nail down http://code.google.com/p/chromium/issues/detail?id=148637
+#if SK_DEBUG_PATH_REF
+    #define PR_CONTAINER SkPath::PathRefDebugRef
+    #define SkDEBUGCODE_X(code) code
+    #define SkASSERT_X(cond) SK_DEBUGBREAK(cond)
+    // We put the mutex in a factory function to protect against static-initializion order
+    // fiasco when SkPaths are created before main().
+    static SkMutex* owners_mutex() {
+        static SkMutex* gOwnersMutex;
+        if (!gOwnersMutex) {
+            gOwnersMutex = new SkMutex(); // leak!
+        }
+        return gOwnersMutex;
+    }
+    // We have a static initializer that calls owners_mutex before main() so that
+    // hopefully that we only wind up with one mutex (assuming no threads created
+    // before static initialization is finished.)
+    static const SkMutex* gOwnersMutexForce = owners_mutex();
+#else
+    #define PR_CONTAINER SkAutoTUnref<SkPathRef>
+    #define SkDEBUGCODE_X(code) SkDEBUGCODE(code)
+    #define SkASSERT_X(cond) SkASSERT(cond)
+#endif
+
+class SkPathRef : public ::SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkPathRef);
+
+    class Editor {
+    public:
+        Editor(PR_CONTAINER* pathRef,
+               int incReserveVerbs = 0,
+               int incReservePoints = 0) {
+            if (pathRef->get()->getRefCnt() > 1) {
+                SkPathRef* copy = SkNEW(SkPathRef);
+                copy->copy(*pathRef->get(), incReserveVerbs, incReservePoints);
+                pathRef->reset(copy);
+            } else {
+                (*pathRef)->incReserve(incReserveVerbs, incReservePoints);
+            }
+            fPathRef = pathRef->get();
+            fPathRef->fGenerationID = 0;
+            SkDEBUGCODE_X(sk_atomic_inc(&fPathRef->fEditorsAttached);)
+        }
+
+        ~Editor() { SkDEBUGCODE_X(sk_atomic_dec(&fPathRef->fEditorsAttached);) }
+
+        /**
+         * Returns the array of points.
+         */
+        SkPoint* points() { return fPathRef->fPoints; }
+
+        /**
+         * Gets the ith point. Shortcut for this->points() + i
+         */
+        SkPoint* atPoint(int i) {
+            SkASSERT((unsigned) i < (unsigned) fPathRef->fPointCnt);
+            return this->points() + i;
+        };
+
+        /**
+         * Adds the verb and allocates space for the number of points indicated by the verb. The
+         * return value is a pointer to where the points for the verb should be written.
+         */
+        SkPoint* growForVerb(SkPath::Verb verb) {
+            fPathRef->validate();
+            return fPathRef->growForVerb(verb);
+        }
+
+        /**
+         * Allocates space for additional verbs and points and returns pointers to the new verbs and
+         * points. verbs will point one beyond the first new verb (index it using [~<i>]). pts points
+         * at the first new point (indexed normally [<i>]).
+         */
+        void grow(int newVerbs, int newPts, uint8_t** verbs, SkPoint** pts) {
+            SkASSERT(NULL != verbs);
+            SkASSERT(NULL != pts);
+            fPathRef->validate();
+            int oldVerbCnt = fPathRef->fVerbCnt;
+            int oldPointCnt = fPathRef->fPointCnt;
+            SkASSERT(verbs && pts);
+            fPathRef->grow(newVerbs, newPts);
+            *verbs = fPathRef->fVerbs - oldVerbCnt;
+            *pts = fPathRef->fPoints + oldPointCnt;
+            fPathRef->validate();
+        }
+
+        /**
+         * Resets the path ref to a new verb and point count. The new verbs and points are
+         * uninitialized.
+         */
+        void resetToSize(int newVerbCnt, int newPointCnt) {
+            fPathRef->resetToSize(newVerbCnt, newPointCnt);
+        }
+        /**
+         * Gets the path ref that is wrapped in the Editor.
+         */
+        SkPathRef* pathRef() { return fPathRef; }
+
+    private:
+        SkPathRef* fPathRef;
+    };
+
+public:
+#if SK_DEBUG_PATH_REF
+    void addOwner(SkPath* owner) {
+        SkAutoMutexAcquire ac(owners_mutex());
+        for (int i = 0; i < fOwners.count(); ++i) {
+            SkASSERT_X(fOwners[i] != owner);
+        }
+        *fOwners.append() = owner;
+        SkASSERT_X((this->getRefCnt() == fOwners.count()) ||
+                   (this == gEmptyPathRef && this->getRefCnt() == fOwners.count() + 1));
+    }
+
+    void removeOwner(SkPath* owner) {
+        SkAutoMutexAcquire ac(owners_mutex());
+        SkASSERT_X((this->getRefCnt() == fOwners.count()) ||
+                   (this == gEmptyPathRef && this->getRefCnt() == fOwners.count() + 1));
+        bool found = false;
+        for (int i = 0; !found && i < fOwners.count(); ++i) {
+            found = (owner == fOwners[i]);
+            if (found) {
+                fOwners.remove(i);
+            }
+        }
+        SkASSERT_X(found);
+    }
+#endif
+
+    /**
+     * Gets a path ref with no verbs or points.
+     */
+    static SkPathRef* CreateEmpty() {
+        if (!gEmptyPathRef) {
+            gEmptyPathRef = SkNEW(SkPathRef); // leak!
+        }
+        return SkRef(gEmptyPathRef);
+    }
+
+    /**
+     * Transforms a path ref by a matrix, allocating a new one only if necessary.
+     */
+    static void CreateTransformedCopy(PR_CONTAINER* dst,
+                                      const SkPathRef& src,
+                                      const SkMatrix& matrix) {
+        src.validate();
+        if (matrix.isIdentity()) {
+            if (dst->get() != &src) {
+                src.ref();
+                dst->reset(const_cast<SkPathRef*>(&src));
+                (*dst)->validate();
+            }
+            return;
+        }
+        int32_t rcnt = dst->get()->getRefCnt();
+        if (&src == dst->get() && 1 == rcnt) {
+            matrix.mapPoints((*dst)->fPoints, (*dst)->fPointCnt);
+            return;
+        } else if (rcnt > 1) {
+            dst->reset(SkNEW(SkPathRef));
+        }
+        (*dst)->resetToSize(src.fVerbCnt, src.fPointCnt);
+        memcpy((*dst)->verbsMemWritable(), src.verbsMemBegin(), src.fVerbCnt * sizeof(uint8_t));
+        matrix.mapPoints((*dst)->fPoints, src.points(), src.fPointCnt);
+        (*dst)->validate();
+    }
+
+#if NEW_PICTURE_FORMAT
+    static SkPathRef* CreateFromBuffer(SkRBuffer* buffer) {
+        SkPathRef* ref = SkNEW(SkPathRef);
+        ref->fGenerationID = buffer->readU32();
+        int32_t verbCount = buffer->readS32();
+        int32_t pointCount = buffer->readS32();
+        ref->resetToSize(verbCount, pointCount);
+
+        SkASSERT(verbCount == ref->countVerbs());
+        SkASSERT(pointCount == ref->countPoints());
+        buffer->read(ref->verbsMemWritable(), verbCount * sizeof(uint8_t));
+        buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
+        return ref;
+    }
+#else
+    static SkPathRef* CreateFromBuffer(int verbCount, int pointCount, SkRBuffer* buffer) {
+        SkPathRef* ref = SkNEW(SkPathRef);
+
+        ref->resetToSize(verbCount, pointCount);
+        SkASSERT(verbCount == ref->countVerbs());
+        SkASSERT(pointCount == ref->countPoints());
+        buffer->read(ref->fPoints, pointCount * sizeof(SkPoint));
+        for (int i = 0; i < verbCount; ++i) {
+            ref->fVerbs[~i] = buffer->readU8();
+        }
+        return ref;
+    }
+#endif
+
+    /**
+     * Rollsback a path ref to zero verbs and points with the assumption that the path ref will be
+     * repopulated with approximately the same number of verbs and points. A new path ref is created
+     * only if necessary.
+     */
+    static void Rewind(PR_CONTAINER* pathRef) {
+        if (1 == (*pathRef)->getRefCnt()) {
+            (*pathRef)->validate();
+            (*pathRef)->fVerbCnt = 0;
+            (*pathRef)->fPointCnt = 0;
+            (*pathRef)->fFreeSpace = (*pathRef)->currSize();
+            (*pathRef)->fGenerationID = 0;
+            (*pathRef)->validate();
+        } else {
+            int oldVCnt = (*pathRef)->countVerbs();
+            int oldPCnt = (*pathRef)->countPoints();
+            pathRef->reset(SkNEW(SkPathRef));
+            (*pathRef)->resetToSize(0, 0, oldVCnt, oldPCnt);
+        }
+    }
+
+    virtual ~SkPathRef() {
+        SkASSERT_X(this != gEmptyPathRef);
+#if SK_DEBUG_PATH_REF
+        SkASSERT_X(!fOwners.count());
+#endif
+
+        this->validate();
+        sk_free(fPoints);
+
+        SkDEBUGCODE_X(fPoints = NULL;)
+        SkDEBUGCODE_X(fVerbs = NULL;)
+        SkDEBUGCODE_X(fVerbCnt = 0x9999999;)
+        SkDEBUGCODE_X(fPointCnt = 0xAAAAAAA;)
+        SkDEBUGCODE_X(fPointCnt = 0xBBBBBBB;)
+        SkDEBUGCODE_X(fGenerationID = 0xEEEEEEEE;)
+        SkDEBUGCODE_X(fEditorsAttached = 0x7777777;)
+    }
+
+    int countPoints() const { this->validate(); return fPointCnt; }
+    int countVerbs() const { this->validate(); return fVerbCnt; }
+
+    /**
+     * Returns a pointer one beyond the first logical verb (last verb in memory order).
+     */
+    const uint8_t* verbs() const { this->validate(); return fVerbs; }
+
+    /**
+     * Returns a const pointer to the first verb in memory (which is the last logical verb).
+     */
+    const uint8_t* verbsMemBegin() const { return this->verbs() - fVerbCnt; }
+
+    /**
+     * Returns a const pointer to the first point.
+     */
+    const SkPoint* points() const { this->validate(); return fPoints; }
+
+    /**
+     * Shortcut for this->points() + this->countPoints()
+     */
+    const SkPoint* pointsEnd() const { return this->points() + this->countPoints(); }
+
+    /**
+     * Convenience methods for getting to a verb or point by index.
+     */
+    uint8_t atVerb(int index) {
+        SkASSERT((unsigned) index < (unsigned) fVerbCnt);
+        return this->verbs()[~index];
+    }
+    const SkPoint& atPoint(int index) const {
+        SkASSERT((unsigned) index < (unsigned) fPointCnt);
+        return this->points()[index];
+    }
+
+    bool operator== (const SkPathRef& ref) const {
+        this->validate();
+        ref.validate();
+        bool genIDMatch = fGenerationID && fGenerationID == ref.fGenerationID;
+#ifdef SK_RELEASE
+        if (genIDMatch) {
+            return true;
+        }
+#endif
+        if (fPointCnt != ref.fPointCnt ||
+            fVerbCnt != ref.fVerbCnt) {
+            SkASSERT(!genIDMatch);
+            return false;
+        }
+        if (0 != memcmp(this->verbsMemBegin(),
+                        ref.verbsMemBegin(),
+                        ref.fVerbCnt * sizeof(uint8_t))) {
+            SkASSERT(!genIDMatch);
+            return false;
+        }
+        if (0 != memcmp(this->points(),
+                        ref.points(),
+                        ref.fPointCnt * sizeof(SkPoint))) {
+            SkASSERT(!genIDMatch);
+            return false;
+        }
+        // We've done the work to determine that these are equal. If either has a zero genID, copy
+        // the other's. If both are 0 then genID() will compute the next ID.
+        if (0 == fGenerationID) {
+            fGenerationID = ref.genID();
+        } else if (0 == ref.fGenerationID) {
+            ref.fGenerationID = this->genID();
+        }
+        return true;
+    }
+
+    /**
+     * Writes the path points and verbs to a buffer.
+     */
+#if NEW_PICTURE_FORMAT
+    void writeToBuffer(SkWBuffer* buffer) {
+        this->validate();
+        SkDEBUGCODE_X(size_t beforePos = buffer->pos();)
+
+        // TODO: write gen ID here. Problem: We don't know if we're cross process or not from
+        // SkWBuffer. Until this is fixed we write 0.
+        buffer->write32(0);
+        buffer->write32(this->fVerbCnt);
+        buffer->write32(this->fPointCnt);
+        buffer->write(this->verbsMemBegin(), fVerbCnt * sizeof(uint8_t));
+        buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
+
+        SkASSERT(buffer->pos() - beforePos == (size_t) this->writeSize());
+    }
+
+    /**
+     * Gets the number of bytes that would be written in writeBuffer()
+     */
+    uint32_t writeSize() {
+        return 3 * sizeof(uint32_t) + fVerbCnt * sizeof(uint8_t) + fPointCnt * sizeof(SkPoint);
+    }
+#else
+    void writeToBuffer(SkWBuffer* buffer) {
+        this->validate();
+        buffer->write(fPoints, fPointCnt * sizeof(SkPoint));
+        for (int i = 0; i < fVerbCnt; ++i) {
+            buffer->write8(fVerbs[~i]);
+        }
+    }
+#endif
+
+private:
+    SkPathRef() {
+        fPointCnt = 0;
+        fVerbCnt = 0;
+        fVerbs = NULL;
+        fPoints = NULL;
+        fFreeSpace = 0;
+        fGenerationID = kEmptyGenID;
+        SkDEBUGCODE_X(fEditorsAttached = 0;)
+        this->validate();
+    }
+
+    void copy(const SkPathRef& ref, int additionalReserveVerbs, int additionalReservePoints) {
+        this->validate();
+        this->resetToSize(ref.fVerbCnt, ref.fPointCnt,
+                          additionalReserveVerbs, additionalReservePoints);
+        memcpy(this->verbsMemWritable(), ref.verbsMemBegin(), ref.fVerbCnt * sizeof(uint8_t));
+        memcpy(this->fPoints, ref.fPoints, ref.fPointCnt * sizeof(SkPoint));
+        // We could call genID() here to force a real ID (instead of 0). However, if we're making
+        // a copy then presumably we intend to make a modification immediately afterwards.
+        fGenerationID = ref.fGenerationID;
+        this->validate();
+    }
+
+    /** Makes additional room but does not change the counts or change the genID */
+    void incReserve(int additionalVerbs, int additionalPoints) {
+        this->validate();
+        size_t space = additionalVerbs * sizeof(uint8_t) + additionalPoints * sizeof (SkPoint);
+        this->makeSpace(space);
+        this->validate();
+    }
+
+    /** Resets the path ref with verbCount verbs and pointCount points, all unitialized. Also
+     *  allocates space for reserveVerb additional verbs and reservePoints additional points.*/
+    void resetToSize(int verbCount, int pointCount, int reserveVerbs = 0, int reservePoints = 0) {
+        this->validate();
+        fGenerationID = 0;
+
+        size_t newSize = sizeof(uint8_t) * verbCount + sizeof(SkPoint) * pointCount;
+        size_t newReserve = sizeof(uint8_t) * reserveVerbs + sizeof(SkPoint) * reservePoints;
+        size_t minSize = newSize + newReserve;
+
+        ptrdiff_t sizeDelta = this->currSize() - minSize;
+
+        if (sizeDelta < 0 || static_cast<size_t>(sizeDelta) >= 3 * minSize) {
+            sk_free(fPoints);
+            fPoints = NULL;
+            fVerbs = NULL;
+            fFreeSpace = 0;
+            fVerbCnt = 0;
+            fPointCnt = 0;
+            this->makeSpace(minSize);
+            fVerbCnt = verbCount;
+            fPointCnt = pointCount;
+            fFreeSpace -= newSize;
+        } else {
+            fPointCnt = pointCount;
+            fVerbCnt = verbCount;
+            fFreeSpace = this->currSize() - minSize;
+        }
+        this->validate();
+    }
+
+    /**
+     * Increases the verb count by newVerbs and the point count be newPoints. New verbs and points
+     * are uninitialized.
+     */
+    void grow(int newVerbs, int newPoints) {
+        this->validate();
+        size_t space = newVerbs * sizeof(uint8_t) + newPoints * sizeof (SkPoint);
+        this->makeSpace(space);
+        fVerbCnt += newVerbs;
+        fPointCnt += newPoints;
+        fFreeSpace -= space;
+        this->validate();
+    }
+
+    /**
+     * Increases the verb count 1, records the new verb, and creates room for the requisite number
+     * of additional points. A pointer to the first point is returned. Any new points are
+     * uninitialized.
+     */
+    SkPoint* growForVerb(SkPath::Verb verb) {
+        this->validate();
+        int pCnt;
+        switch (verb) {
+            case SkPath::kMove_Verb:
+                pCnt = 1;
+                break;
+            case SkPath::kLine_Verb:
+                pCnt = 1;
+                break;
+            case SkPath::kQuad_Verb:
+                pCnt = 2;
+                break;
+            case SkPath::kCubic_Verb:
+                pCnt = 3;
+                break;
+            default:
+                pCnt = 0;
+        }
+        size_t space = sizeof(uint8_t) + pCnt * sizeof (SkPoint);
+        this->makeSpace(space);
+        this->fVerbs[~fVerbCnt] = verb;
+        SkPoint* ret = fPoints + fPointCnt;
+        fVerbCnt += 1;
+        fPointCnt += pCnt;
+        fFreeSpace -= space;
+        this->validate();
+        return ret;
+    }
+
+    /**
+     * Ensures that the free space available in the path ref is >= size. The verb and point counts
+     * are not changed.
+     */
+    void makeSpace(size_t size) {
+        this->validate();
+        ptrdiff_t growSize = size - fFreeSpace;
+        if (growSize <= 0) {
+            return;
+        }
+        size_t oldSize = this->currSize();
+        // round to next multiple of 8 bytes
+        growSize = (growSize + 7) & ~static_cast<size_t>(7);
+        // we always at least double the allocation
+        if (static_cast<size_t>(growSize) < oldSize) {
+            growSize = oldSize;
+        }
+        if (growSize < kMinSize) {
+            growSize = kMinSize;
+        }
+        size_t newSize = oldSize + growSize;
+        // Note that realloc could memcpy more than we need. It seems to be a win anyway. TODO:
+        // encapsulate this.
+        fPoints = reinterpret_cast<SkPoint*>(sk_realloc_throw(fPoints, newSize));
+        size_t oldVerbSize = fVerbCnt * sizeof(uint8_t);
+        void* newVerbsDst = reinterpret_cast<void*>(
+                                reinterpret_cast<intptr_t>(fPoints) + newSize - oldVerbSize);
+        void* oldVerbsSrc = reinterpret_cast<void*>(
+                                reinterpret_cast<intptr_t>(fPoints) + oldSize - oldVerbSize);
+        memmove(newVerbsDst, oldVerbsSrc, oldVerbSize);
+        fVerbs = reinterpret_cast<uint8_t*>(reinterpret_cast<intptr_t>(fPoints) + newSize);
+        fFreeSpace += growSize;
+        this->validate();
+    }
+
+    /**
+     * Private, non-const-ptr version of the public function verbsMemBegin().
+     */
+    uint8_t* verbsMemWritable() {
+        this->validate();
+        return fVerbs - fVerbCnt;
+    }
+
+    /**
+     * Gets the total amount of space allocated for verbs, points, and reserve.
+     */
+    size_t currSize() const {
+        return reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints);
+    }
+
+    /**
+     * Gets an ID that uniquely identifies the contents of the path ref. If two path refs have the
+     * same ID then they have the same verbs and points. However, two path refs may have the same
+     * contents but different genIDs. Zero is reserved and means an ID has not yet been determined
+     * for the path ref.
+     */
+    int32_t genID() const {
+        SkASSERT_X(!fEditorsAttached);
+        if (!fGenerationID) {
+            if (0 == fPointCnt && 0 == fVerbCnt) {
+                fGenerationID = kEmptyGenID;
+            } else {
+                static int32_t  gPathRefGenerationID;
+                // do a loop in case our global wraps around, as we never want to return a 0 or the
+                // empty ID
+                do {
+                    fGenerationID = sk_atomic_inc(&gPathRefGenerationID) + 1;
+                } while (fGenerationID <= kEmptyGenID);
+            }
+        }
+        return fGenerationID;
+    }
+
+    void validate() const {
+        SkASSERT(static_cast<ptrdiff_t>(fFreeSpace) >= 0);
+        SkASSERT(reinterpret_cast<intptr_t>(fVerbs) - reinterpret_cast<intptr_t>(fPoints) >= 0);
+        SkASSERT((NULL == fPoints) == (NULL == fVerbs));
+        SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
+        SkASSERT(!(NULL == fPoints && 0 != fFreeSpace));
+        SkASSERT(!(NULL == fPoints && fPointCnt));
+        SkASSERT(!(NULL == fVerbs && fVerbCnt));
+        SkASSERT(this->currSize() ==
+                 fFreeSpace + sizeof(SkPoint) * fPointCnt + sizeof(uint8_t) * fVerbCnt);
+    }
+
+    enum {
+        kMinSize = 256,
+    };
+
+    SkPoint*            fPoints; // points to begining of the allocation
+    uint8_t*            fVerbs; // points just past the end of the allocation (verbs grow backwards)
+    int                 fVerbCnt;
+    int                 fPointCnt;
+    size_t              fFreeSpace; // redundant but saves computation
+    enum {
+        kEmptyGenID = 1, // GenID reserved for path ref with zero points and zero verbs.
+    };
+    mutable int32_t     fGenerationID;
+    SkDEBUGCODE_X(int32_t fEditorsAttached;) // assert that only one editor in use at any time.
+
+#if SK_DEBUG_PATH_REF
+    SkTDArray<SkPath*> fOwners;
+#endif
+
+    typedef SkRefCnt INHERITED;
+};
+
+SK_DEFINE_INST_COUNT(SkPathRef);
+
+#endif
diff --git a/src/core/SkPerspIter.h b/src/core/SkPerspIter.h
new file mode 100644
index 0000000..821f153
--- /dev/null
+++ b/src/core/SkPerspIter.h
@@ -0,0 +1,48 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkPerspIter_DEFINED
+#define SkPerspIter_DEFINED
+
+#include "SkMatrix.h"
+
+class SkPerspIter {
+public:
+    /** Iterate a line through the matrix [x,y] ... [x+count-1, y].
+        @param m    The matrix we will be iterating a line through
+        @param x    The initial X coordinate to be mapped through the matrix
+        @param y    The initial Y coordinate to be mapped through the matrix
+        @param count The number of points (x,y) (x+1,y) (x+2,y) ... we will eventually map
+    */
+    SkPerspIter(const SkMatrix& m, SkScalar x, SkScalar y, int count);
+
+    /** Return the buffer of [x,y] fixed point values we will be filling.
+        This always returns the same value, so it can be saved across calls to
+        next().
+    */
+    const SkFixed* getXY() const { return fStorage; }
+
+    /** Return the number of [x,y] pairs that have been filled in the getXY() buffer.
+        When this returns 0, the iterator is finished.
+    */
+    int next();
+
+private:
+    enum {
+        kShift  = 4,
+        kCount  = (1 << kShift)
+    };
+    const SkMatrix& fMatrix;
+    SkFixed         fStorage[kCount * 2];
+    SkFixed         fX, fY;
+    SkScalar        fSX, fSY;
+    int             fCount;
+};
+
+#endif
diff --git a/src/core/SkPicture.cpp b/src/core/SkPicture.cpp
index 6aaaf2d..3e464e0 100644
--- a/src/core/SkPicture.cpp
+++ b/src/core/SkPicture.cpp
@@ -13,6 +13,7 @@
 
 #include "SkCanvas.h"
 #include "SkChunkAlloc.h"
+#include "SkDevice.h"
 #include "SkPicture.h"
 #include "SkRegion.h"
 #include "SkStream.h"
@@ -22,6 +23,10 @@
 
 #include "SkReader32.h"
 #include "SkWriter32.h"
+#include "SkRTree.h"
+#include "SkBBoxHierarchyRecord.h"
+
+SK_DEFINE_INST_COUNT(SkPicture)
 
 #define DUMP_BUFFER_SIZE 65536
 
@@ -136,6 +141,43 @@
     SkTSwap(fHeight, other.fHeight);
 }
 
+SkPicture* SkPicture::clone() const {
+    SkPicture* clonedPicture = SkNEW(SkPicture);
+    clone(clonedPicture, 1);
+    return clonedPicture;
+}
+
+void SkPicture::clone(SkPicture* pictures, int count) const {
+    SkPictCopyInfo copyInfo;
+
+    for (int i = 0; i < count; i++) {
+        SkPicture* clone = &pictures[i];
+
+        clone->fWidth = fWidth;
+        clone->fHeight = fHeight;
+        clone->fRecord = NULL;
+
+        if (NULL != clone->fRecord) {
+            clone->fRecord->unref();
+            clone->fRecord = NULL;
+        }
+        SkDELETE(clone->fPlayback);
+
+        /*  We want to copy the src's playback. However, if that hasn't been built
+            yet, we need to fake a call to endRecording() without actually calling
+            it (since it is destructive, and we don't want to change src).
+         */
+        if (fPlayback) {
+            clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fPlayback, &copyInfo));
+        } else if (fRecord) {
+            // here we do a fake src.endRecording()
+            clone->fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord, true));
+        } else {
+            clone->fPlayback = NULL;
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkCanvas* SkPicture::beginRecording(int width, int height,
@@ -150,18 +192,39 @@
         fRecord = NULL;
     }
 
-    fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags));
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, width, height);
+    SkAutoTUnref<SkDevice> dev(SkNEW_ARGS(SkDevice, (bm)));
 
+    // Must be set before calling createBBoxHierarchy
     fWidth = width;
     fHeight = height;
 
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kNo_Config, width, height);
-    fRecord->setBitmapDevice(bm);
+    if (recordingFlags & kOptimizeForClippedPlayback_RecordingFlag) {
+        SkBBoxHierarchy* tree = this->createBBoxHierarchy();
+        SkASSERT(NULL != tree);
+        fRecord = SkNEW_ARGS(SkBBoxHierarchyRecord, (recordingFlags, tree, dev));
+        tree->unref();
+    } else {
+        fRecord = SkNEW_ARGS(SkPictureRecord, (recordingFlags, dev));
+    }
+    fRecord->beginRecording();
 
     return fRecord;
 }
 
+SkBBoxHierarchy* SkPicture::createBBoxHierarchy() const {
+    // These values were empirically determined to produce reasonable
+    // performance in most cases.
+    static const int kRTreeMinChildren = 6;
+    static const int kRTreeMaxChildren = 11;
+
+    SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
+                                       SkIntToScalar(fHeight));
+    return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
+                           aspectRatio);
+}
+
 SkCanvas* SkPicture::getRecordingCanvas() const {
     // will be null if we are not recording
     return fRecord;
@@ -170,6 +233,7 @@
 void SkPicture::endRecording() {
     if (NULL == fPlayback) {
         if (NULL != fRecord) {
+            fRecord->endRecording();
             fPlayback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
             fRecord->unref();
             fRecord = NULL;
@@ -189,39 +253,65 @@
 
 #include "SkStream.h"
 
-#define PICTURE_VERSION     1
-
-SkPicture::SkPicture(SkStream* stream) : SkRefCnt() {
-    const uint32_t  pictureVersion = stream->readU32();
-    if (pictureVersion != PICTURE_VERSION_ICS &&
-        pictureVersion != PICTURE_VERSION_JB) {
-        sk_throw();
+SkPicture::SkPicture(SkStream* stream, bool* success, SkSerializationHelpers::DecodeBitmap decoder) : SkRefCnt() {
+    if (success) {
+        *success = false;
     }
-
-    fWidth = stream->readU32();
-    fHeight = stream->readU32();
-
     fRecord = NULL;
     fPlayback = NULL;
+    fWidth = fHeight = 0;
+
+    SkPictInfo info;
+
+    if (!stream->read(&info, sizeof(info))) {
+        return;
+    }
+    if (PICTURE_VERSION != info.fVersion) {
+        return;
+    }
 
     if (stream->readBool()) {
-        fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, pictureVersion));
+        bool isValid = false;
+        fPlayback = SkNEW_ARGS(SkPicturePlayback, (stream, info, &isValid, decoder));
+        if (!isValid) {
+            SkDELETE(fPlayback);
+            fPlayback = NULL;
+            return;
+        }
+    }
+
+    // do this at the end, so that they will be zero if we hit an error.
+    fWidth = info.fWidth;
+    fHeight = info.fHeight;
+    if (success) {
+        *success = true;
     }
 }
 
-void SkPicture::serialize(SkWStream* stream) const {
+void SkPicture::serialize(SkWStream* stream, SkSerializationHelpers::EncodeBitmap encoder) const {
     SkPicturePlayback* playback = fPlayback;
 
     if (NULL == playback && fRecord) {
         playback = SkNEW_ARGS(SkPicturePlayback, (*fRecord));
     }
 
-    stream->write32(PICTURE_VERSION_JB);
-    stream->write32(fWidth);
-    stream->write32(fHeight);
+    SkPictInfo info;
+
+    info.fVersion = PICTURE_VERSION;
+    info.fWidth = fWidth;
+    info.fHeight = fHeight;
+    info.fFlags = SkPictInfo::kCrossProcess_Flag;
+#ifdef SK_SCALAR_IS_FLOAT
+    info.fFlags |= SkPictInfo::kScalarIsFloat_Flag;
+#endif
+    if (8 == sizeof(void*)) {
+        info.fFlags |= SkPictInfo::kPtrIs64Bit_Flag;
+    }
+
+    stream->write(&info, sizeof(info));
     if (playback) {
         stream->writeBool(true);
-        playback->serialize(stream);
+        playback->serialize(stream, encoder);
         // delete playback if it is a local version (i.e. cons'd up just now)
         if (playback != fPlayback) {
             SkDELETE(playback);
@@ -231,11 +321,11 @@
     }
 }
 
+#ifdef SK_BUILD_FOR_ANDROID
 void SkPicture::abortPlayback() {
     if (NULL == fPlayback) {
         return;
     }
     fPlayback->abort();
 }
-
-
+#endif
diff --git a/src/core/SkPictureFlat.cpp b/src/core/SkPictureFlat.cpp
index 1058526..9221c1e 100644
--- a/src/core/SkPictureFlat.cpp
+++ b/src/core/SkPictureFlat.cpp
@@ -7,6 +7,7 @@
  */
 #include "SkPictureFlat.h"
 
+#include "SkChecksum.h"
 #include "SkColorFilter.h"
 #include "SkDrawLooper.h"
 #include "SkMaskFilter.h"
@@ -15,227 +16,24 @@
 #include "SkTypeface.h"
 #include "SkXfermode.h"
 
-SkFlatData* SkFlatData::Alloc(SkChunkAlloc* heap, int32_t size, int index) {
-    SkFlatData* result = (SkFlatData*) heap->allocThrow(size + sizeof(SkFlatData));
-    result->fIndex = index;
-    result->fAllocSize = size + sizeof(result->fAllocSize);
-    return result;
-}
-
-SkFlatBitmap* SkFlatBitmap::Flatten(SkChunkAlloc* heap, const SkBitmap& bitmap,
-                                    int index, SkRefCntSet* rec) {
-    SkFlattenableWriteBuffer buffer(1024);
-    buffer.setRefCntRecorder(rec);
-    
-    bitmap.flatten(buffer);
-    size_t size = buffer.size();
-    SkFlatBitmap* result = (SkFlatBitmap*) INHERITED::Alloc(heap, size, index);
-    buffer.flatten(result->fBitmapData);
-    return result;
-}
-
-SkFlatMatrix* SkFlatMatrix::Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index) {
-    size_t size = matrix.flatten(NULL);
-    SkFlatMatrix* result = (SkFlatMatrix*) INHERITED::Alloc(heap, size, index);
-    matrix.flatten(&result->fMatrixData);
-    return result;
-}
-
-#ifdef SK_DEBUG_DUMP
-void SkFlatMatrix::dump() const {
-    const SkMatrix* matrix = (const SkMatrix*) fMatrixData;
-    char pBuffer[DUMP_BUFFER_SIZE];
-    char* bufferPtr = pBuffer;
-    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-    "matrix: ");
-    SkScalar scaleX = matrix->getScaleX();
-    SkMatrix defaultMatrix;
-    defaultMatrix.reset();
-    if (scaleX != defaultMatrix.getScaleX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "scaleX:%g ", SkScalarToFloat(scaleX));
-    SkScalar scaleY = matrix->getScaleY();
-    if (scaleY != defaultMatrix.getScaleY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "scaleY:%g ", SkScalarToFloat(scaleY));
-    SkScalar skewX = matrix->getSkewX();
-    if (skewX != defaultMatrix.getSkewX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "skewX:%g ", SkScalarToFloat(skewX));
-    SkScalar skewY = matrix->getSkewY();
-    if (skewY != defaultMatrix.getSkewY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "skewY:%g ", SkScalarToFloat(skewY));
-    SkScalar translateX = matrix->getTranslateX();
-    if (translateX != defaultMatrix.getTranslateX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "translateX:%g ", SkScalarToFloat(translateX));
-    SkScalar translateY = matrix->getTranslateY();
-    if (translateY != defaultMatrix.getTranslateY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "translateY:%g ", SkScalarToFloat(translateY));
-    SkScalar perspX = matrix->getPerspX();
-    if (perspX != defaultMatrix.getPerspX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "perspX:%g ", SkFractToFloat(perspX));
-    SkScalar perspY = matrix->getPerspY();
-    if (perspY != defaultMatrix.getPerspY())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "perspY:%g ", SkFractToFloat(perspY));
-    SkDebugf("%s\n", pBuffer);
-}
-#endif
+SK_DEFINE_INST_COUNT(SkFlatController)
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkFlatPaint* SkFlatPaint::Flatten(SkChunkAlloc* heap, const SkPaint& paint,
-                                  int index, SkRefCntSet* rec,
-                                  SkRefCntSet* faceRecorder) {
-    SkFlattenableWriteBuffer buffer(2*sizeof(SkPaint));
-    buffer.setRefCntRecorder(rec);
-    buffer.setTypefaceRecorder(faceRecorder);
+SkTypefacePlayback::SkTypefacePlayback() : fCount(0), fArray(NULL) {}
 
-    paint.flatten(buffer);
-    uint32_t size = buffer.size();
-    SkFlatPaint* result = (SkFlatPaint*) INHERITED::Alloc(heap, size, index);
-    buffer.flatten(&result->fPaintData);
-    return result;
-}
-    
-void SkFlatPaint::Read(const void* storage, SkPaint* paint,
-                   SkRefCntPlayback* rcp, SkTypefacePlayback* facePlayback) {
-    SkFlattenableReadBuffer buffer(storage);
-    if (rcp) {
-        rcp->setupBuffer(buffer);
-    }
-    if (facePlayback) {
-        facePlayback->setupBuffer(buffer);
-    }
-    paint->unflatten(buffer);
-}
-
-#ifdef SK_DEBUG_DUMP
-void SkFlatPaint::dump() const {
-    SkPaint defaultPaint;
-    SkFlattenableReadBuffer buffer(fPaintData);
-    SkTypeface* typeface = (SkTypeface*) buffer.readPtr();
-    char pBuffer[DUMP_BUFFER_SIZE];
-    char* bufferPtr = pBuffer;
-    bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-        "paint: ");
-    if (typeface != defaultPaint.getTypeface())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "typeface:%p ", typeface);
-    SkScalar textSize = buffer.readScalar();
-    if (textSize != defaultPaint.getTextSize())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textSize:%g ", SkScalarToFloat(textSize));
-    SkScalar textScaleX = buffer.readScalar();
-    if (textScaleX != defaultPaint.getTextScaleX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textScaleX:%g ", SkScalarToFloat(textScaleX));
-    SkScalar textSkewX = buffer.readScalar();
-    if (textSkewX != defaultPaint.getTextSkewX())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textSkewX:%g ", SkScalarToFloat(textSkewX));
-    const SkPathEffect* pathEffect = (const SkPathEffect*) buffer.readFlattenable();
-    if (pathEffect != defaultPaint.getPathEffect())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "pathEffect:%p ", pathEffect);
-    SkDELETE(pathEffect);
-    const SkShader* shader = (const SkShader*) buffer.readFlattenable();
-    if (shader != defaultPaint.getShader())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "shader:%p ", shader);
-    SkDELETE(shader);
-    const SkXfermode* xfermode = (const SkXfermode*) buffer.readFlattenable();
-    if (xfermode != defaultPaint.getXfermode())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "xfermode:%p ", xfermode);
-    SkDELETE(xfermode);
-    const SkMaskFilter* maskFilter = (const SkMaskFilter*) buffer.readFlattenable();
-    if (maskFilter != defaultPaint.getMaskFilter())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "maskFilter:%p ", maskFilter);
-    SkDELETE(maskFilter);
-    const SkColorFilter* colorFilter = (const SkColorFilter*) buffer.readFlattenable();
-    if (colorFilter != defaultPaint.getColorFilter())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "colorFilter:%p ", colorFilter);
-    SkDELETE(colorFilter);
-    const SkRasterizer* rasterizer = (const SkRasterizer*) buffer.readFlattenable();
-    if (rasterizer != defaultPaint.getRasterizer())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "rasterizer:%p ", rasterizer);
-    SkDELETE(rasterizer);
-    const SkDrawLooper* drawLooper = (const SkDrawLooper*) buffer.readFlattenable();
-    if (drawLooper != defaultPaint.getLooper())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "drawLooper:%p ", drawLooper);
-    SkDELETE(drawLooper);
-    unsigned color = buffer.readU32();
-    if (color != defaultPaint.getColor())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "color:0x%x ", color);
-    SkScalar strokeWidth = buffer.readScalar();
-    if (strokeWidth != defaultPaint.getStrokeWidth())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "strokeWidth:%g ", SkScalarToFloat(strokeWidth));
-    SkScalar strokeMiter = buffer.readScalar();
-    if (strokeMiter != defaultPaint.getStrokeMiter())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "strokeMiter:%g ", SkScalarToFloat(strokeMiter));
-    unsigned flags = buffer.readU16();
-    if (flags != defaultPaint.getFlags())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "flags:0x%x ", flags);
-    int align = buffer.readU8();
-    if (align != defaultPaint.getTextAlign())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "align:0x%x ", align);
-    int strokeCap = buffer.readU8();
-    if (strokeCap != defaultPaint.getStrokeCap())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "strokeCap:0x%x ", strokeCap);
-    int strokeJoin = buffer.readU8();
-    if (strokeJoin != defaultPaint.getStrokeJoin())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "align:0x%x ", strokeJoin);
-    int style = buffer.readU8();
-    if (style != defaultPaint.getStyle())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "style:0x%x ", style);
-    int textEncoding = buffer.readU8();
-    if (textEncoding != defaultPaint.getTextEncoding())
-        bufferPtr += snprintf(bufferPtr, DUMP_BUFFER_SIZE - (bufferPtr - pBuffer), 
-            "textEncoding:0x%x ", textEncoding);
-    SkDebugf("%s\n", pBuffer);
-}
-#endif
-
-SkFlatRegion* SkFlatRegion::Flatten(SkChunkAlloc* heap, const SkRegion& region, int index) {
-    uint32_t size = region.flatten(NULL);
-    SkFlatRegion* result = (SkFlatRegion*) INHERITED::Alloc(heap, size, index);
-    region.flatten(&result->fRegionData);
-    return result;
-}
-    
-///////////////////////////////////////////////////////////////////////////////
-
-SkRefCntPlayback::SkRefCntPlayback() : fCount(0), fArray(NULL) {}
-
-SkRefCntPlayback::~SkRefCntPlayback() {
+SkTypefacePlayback::~SkTypefacePlayback() {
     this->reset(NULL);
 }
 
-void SkRefCntPlayback::reset(const SkRefCntSet* rec) {
+void SkTypefacePlayback::reset(const SkRefCntSet* rec) {
     for (int i = 0; i < fCount; i++) {
         SkASSERT(fArray[i]);
         fArray[i]->unref();
     }
     SkDELETE_ARRAY(fArray);
-    
-    if (rec) {
+
+    if (rec!= NULL && rec->count() > 0) {
         fCount = rec->count();
         fArray = SkNEW_ARRAY(SkRefCnt*, fCount);
         rec->copyToArray(fArray);
@@ -248,17 +46,104 @@
     }
 }
 
-void SkRefCntPlayback::setCount(int count) {
+void SkTypefacePlayback::setCount(int count) {
     this->reset(NULL);
-    
+
     fCount = count;
     fArray = SkNEW_ARRAY(SkRefCnt*, count);
     sk_bzero(fArray, count * sizeof(SkRefCnt*));
 }
 
-SkRefCnt* SkRefCntPlayback::set(int index, SkRefCnt* obj) {
+SkRefCnt* SkTypefacePlayback::set(int index, SkRefCnt* obj) {
     SkASSERT((unsigned)index < (unsigned)fCount);
     SkRefCnt_SafeAssign(fArray[index], obj);
     return obj;
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlatController::SkFlatController()
+: fBitmapHeap(NULL)
+, fTypefaceSet(NULL)
+, fTypefacePlayback(NULL)
+, fFactorySet(NULL)
+, fWriteBufferFlags(0) {}
+
+SkFlatController::~SkFlatController() {
+    SkSafeUnref(fBitmapHeap);
+    SkSafeUnref(fTypefaceSet);
+    SkSafeUnref(fFactorySet);
+}
+
+void SkFlatController::setBitmapHeap(SkBitmapHeap* heap) {
+    SkRefCnt_SafeAssign(fBitmapHeap, heap);
+}
+
+void SkFlatController::setTypefaceSet(SkRefCntSet *set) {
+    SkRefCnt_SafeAssign(fTypefaceSet, set);
+}
+
+void SkFlatController::setTypefacePlayback(SkTypefacePlayback* playback) {
+    fTypefacePlayback = playback;
+}
+
+SkNamedFactorySet* SkFlatController::setNamedFactorySet(SkNamedFactorySet* set) {
+    SkRefCnt_SafeAssign(fFactorySet, set);
+    return set;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkFlatData* SkFlatData::Create(SkFlatController* controller, const void* obj,
+        int index, void (*flattenProc)(SkOrderedWriteBuffer&, const void*)) {
+    // a buffer of 256 bytes should be sufficient for most paints, regions,
+    // and matrices.
+    intptr_t storage[256];
+    SkOrderedWriteBuffer buffer(256, storage, sizeof(storage));
+
+    buffer.setBitmapHeap(controller->getBitmapHeap());
+    buffer.setTypefaceRecorder(controller->getTypefaceSet());
+    buffer.setNamedFactoryRecorder(controller->getNamedFactorySet());
+    buffer.setFlags(controller->getWriteBufferFlags());
+
+    flattenProc(buffer, obj);
+    uint32_t size = buffer.size();
+    SkASSERT(SkIsAlign4(size));
+
+    /**
+     *  Allocate enough memory to hold
+     *  1. SkFlatData struct
+     *  2. flattenProc's data (4-byte aligned)
+     *  3. 4-byte sentinel
+     */
+    size_t allocSize = sizeof(SkFlatData) + size + sizeof(uint32_t);
+    SkFlatData* result = (SkFlatData*) controller->allocThrow(allocSize);
+
+    result->fIndex = index;
+    result->setTopBotUnwritten();
+    result->fFlatSize = size;
+
+    // put the serialized contents into the data section of the new allocation
+    buffer.writeToMemory(result->data());
+    result->fChecksum = SkChecksum::Compute(result->data32(), size);
+    result->setSentinelAsCandidate();
+    return result;
+}
+
+void SkFlatData::unflatten(void* result,
+        void (*unflattenProc)(SkOrderedReadBuffer&, void*),
+        SkBitmapHeap* bitmapHeap,
+        SkTypefacePlayback* facePlayback) const {
+
+    SkOrderedReadBuffer buffer(this->data(), fFlatSize);
+
+    if (bitmapHeap) {
+        buffer.setBitmapStorage(bitmapHeap);
+    }
+    if (facePlayback) {
+        facePlayback->setupBuffer(buffer);
+    }
+
+    unflattenProc(buffer, result);
+    SkASSERT(fFlatSize == (int32_t)buffer.offset());
+}
diff --git a/src/core/SkPictureFlat.h b/src/core/SkPictureFlat.h
index bfd253a..6354e9f 100644
--- a/src/core/SkPictureFlat.h
+++ b/src/core/SkPictureFlat.h
@@ -8,25 +8,36 @@
 #ifndef SkPictureFlat_DEFINED
 #define SkPictureFlat_DEFINED
 
+//#define SK_DEBUG_SIZE
+
 #include "SkChunkAlloc.h"
 #include "SkBitmap.h"
+#include "SkBitmapHeap.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
 #include "SkPicture.h"
+#include "SkPtrRecorder.h"
 #include "SkMatrix.h"
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkRegion.h"
+#include "SkTRefArray.h"
+#include "SkTSearch.h"
 
 enum DrawType {
     UNUSED,
     CLIP_PATH,
     CLIP_REGION,
     CLIP_RECT,
+    CLIP_RRECT,
     CONCAT,
     DRAW_BITMAP,
     DRAW_BITMAP_MATRIX,
-    DRAW_BITMAP_RECT,
+    DRAW_BITMAP_NINE,
+    DRAW_BITMAP_RECT_TO_RECT,
     DRAW_CLEAR,
     DRAW_DATA,
+    DRAW_OVAL,
     DRAW_PAINT,
     DRAW_PATH,
     DRAW_PICTURE,
@@ -36,7 +47,7 @@
     DRAW_POS_TEXT_H,
     DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
     DRAW_RECT,
-    DRAW_SHAPE,
+    DRAW_RRECT,
     DRAW_SPRITE,
     DRAW_TEXT,
     DRAW_TEXT_ON_PATH,
@@ -50,7 +61,8 @@
     SET_MATRIX,
     SKEW,
     TRANSLATE,
-    DRAW_BITMAP_NINE
+
+    LAST_DRAWTYPE_ENUM = TRANSLATE
 };
 
 enum DrawVertexFlags {
@@ -78,34 +90,27 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class SkRefCntPlayback {
+class SkTypefacePlayback {
 public:
-    SkRefCntPlayback();
-    virtual ~SkRefCntPlayback();
-    
+    SkTypefacePlayback();
+    virtual ~SkTypefacePlayback();
+
     int count() const { return fCount; }
-    
+
     void reset(const SkRefCntSet*);
 
     void setCount(int count);
     SkRefCnt* set(int index, SkRefCnt*);
 
-    virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const {
-        buffer.setRefCntArray(fArray, fCount);
+    void setupBuffer(SkOrderedReadBuffer& buffer) const {
+        buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
     }
-    
+
 protected:
     int fCount;
     SkRefCnt** fArray;
 };
 
-class SkTypefacePlayback : public SkRefCntPlayback {
-public:
-    virtual void setupBuffer(SkFlattenableReadBuffer& buffer) const {
-        buffer.setTypefaceArray((SkTypeface**)fArray, fCount);
-    }
-};
-
 class SkFactoryPlayback {
 public:
     SkFactoryPlayback(int count) : fCount(count) {
@@ -115,124 +120,555 @@
     ~SkFactoryPlayback() {
         SkDELETE_ARRAY(fArray);
     }
-    
+
     SkFlattenable::Factory* base() const { return fArray; }
 
-    void setupBuffer(SkFlattenableReadBuffer& buffer) const {
+    void setupBuffer(SkOrderedReadBuffer& buffer) const {
         buffer.setFactoryPlayback(fArray, fCount);
     }
-    
+
 private:
     int fCount;
     SkFlattenable::Factory* fArray;
 };
 
-class SkFlatData {
+///////////////////////////////////////////////////////////////////////////////
+//
+//
+// The following templated classes provide an efficient way to store and compare
+// objects that have been flattened (i.e. serialized in an ordered binary
+// format).
+//
+// SkFlatData:       is a simple indexable container for the flattened data
+//                   which is agnostic to the type of data is is indexing. It is
+//                   also responsible for flattening/unflattening objects but
+//                   details of that operation are hidden in the provided procs
+// SkFlatDictionary: is an abstract templated dictionary that maintains a
+//                   searchable set of SkFlataData objects of type T.
+// SkFlatController: is an interface provided to SkFlatDictionary which handles
+//                   allocation and unallocation in some cases. It also holds
+//                   ref count recorders and the like.
+//
+// NOTE: any class that wishes to be used in conjunction with SkFlatDictionary
+// must subclass the dictionary and provide the necessary flattening procs.
+// The end of this header contains dictionary subclasses for some common classes
+// like SkBitmap, SkMatrix, SkPaint, and SkRegion. SkFlatController must also
+// be implemented, or SkChunkFlatController can be used to use an
+// SkChunkAllocator and never do replacements.
+//
+//
+///////////////////////////////////////////////////////////////////////////////
+
+class SkFlatData;
+
+class SkFlatController : public SkRefCnt {
 public:
-    static int Compare(const SkFlatData* a, const SkFlatData* b) {
-        return memcmp(&a->fAllocSize, &b->fAllocSize, a->fAllocSize);
-    }
-    
-    int index() const { return fIndex; }
-    
-#ifdef SK_DEBUG_SIZE
-    size_t size() const { return sizeof(fIndex) + fAllocSize; }
-#endif
+    SK_DECLARE_INST_COUNT(SkFlatController)
+
+    SkFlatController();
+    virtual ~SkFlatController();
+    /**
+     * Provide a new block of memory for the SkFlatDictionary to use.
+     */
+    virtual void* allocThrow(size_t bytes) = 0;
+
+    /**
+     * Unallocate a previously allocated block, returned by allocThrow.
+     * Implementation should at least perform an unallocation if passed the last
+     * pointer returned by allocThrow. If findAndReplace() is intended to be
+     * used, unalloc should also be able to unallocate the SkFlatData that is
+     * provided.
+     */
+    virtual void unalloc(void* ptr) = 0;
+
+    /**
+     * Used during creation and unflattening of SkFlatData objects. If the
+     * objects being flattened contain bitmaps they are stored in this heap
+     * and the flattenable stores the index to the bitmap on the heap.
+     * This should be set by the protected setBitmapHeap.
+     */
+    SkBitmapHeap* getBitmapHeap() { return fBitmapHeap; }
+
+    /**
+     * Used during creation of SkFlatData objects. If a typeface recorder is
+     * required to flatten the objects being flattened (i.e. for SkPaints), this
+     * should be set by the protected setTypefaceSet.
+     */
+    SkRefCntSet* getTypefaceSet() { return fTypefaceSet; }
+
+    /**
+     * Used during unflattening of the SkFlatData objects in the
+     * SkFlatDictionary. Needs to be set by the protected setTypefacePlayback
+     * and needs to be reset to the SkRefCntSet passed to setTypefaceSet.
+     */
+    SkTypefacePlayback* getTypefacePlayback() { return fTypefacePlayback; }
+
+    /**
+     * Optional factory recorder used during creation of SkFlatData objects. Set
+     * using the protected method setNamedFactorySet.
+     */
+    SkNamedFactorySet* getNamedFactorySet() { return fFactorySet; }
+
+    /**
+     * Flags to use during creation of SkFlatData objects. Defaults to zero.
+     */
+    uint32_t getWriteBufferFlags() { return fWriteBufferFlags; }
 
 protected:
-    static SkFlatData* Alloc(SkChunkAlloc* heap, int32_t size, int index);
-    
-    int fIndex;
-    int32_t fAllocSize;
+    /**
+     * Set an SkBitmapHeap to be used to store/read SkBitmaps. Ref counted.
+     */
+    void setBitmapHeap(SkBitmapHeap*);
+
+    /**
+     * Set an SkRefCntSet to be used to store SkTypefaces during flattening. Ref
+     * counted.
+     */
+    void setTypefaceSet(SkRefCntSet*);
+
+    /**
+     * Set an SkTypefacePlayback to be used to find references to SkTypefaces
+     * during unflattening. Should be reset to the set provided to
+     * setTypefaceSet.
+     */
+    void setTypefacePlayback(SkTypefacePlayback*);
+
+    /**
+     * Set an SkNamedFactorySet to be used to store Factorys and their
+     * corresponding names during flattening. Ref counted. Returns the same
+     * set as a convenience.
+     */
+    SkNamedFactorySet* setNamedFactorySet(SkNamedFactorySet*);
+
+    /**
+     * Set the flags to be used during flattening.
+     */
+    void setWriteBufferFlags(uint32_t flags) { fWriteBufferFlags = flags; }
+
+private:
+    SkBitmapHeap*       fBitmapHeap;
+    SkRefCntSet*        fTypefaceSet;
+    SkTypefacePlayback* fTypefacePlayback;
+    SkNamedFactorySet*  fFactorySet;
+    uint32_t            fWriteBufferFlags;
+
+    typedef SkRefCnt INHERITED;
 };
 
-class SkFlatBitmap : public SkFlatData {
+class SkFlatData {
 public:
-    static SkFlatBitmap* Flatten(SkChunkAlloc*, const SkBitmap&, int index,
-                                 SkRefCntSet*);
+    /**
+     *  Compare two SkFlatData ptrs, returning -1, 0, 1 to allow them to be
+     *  sorted.
+     *
+     *  Note: this assumes that a and b have different sentinel values, either
+     *  InCache or AsCandidate, otherwise the loop will go beyond the end of
+     *  the buffers.
+     *
+     *  dataToCompare() returns 2 fields before the flattened data:
+     *      - checksum
+     *      - size
+     *  This ensures that if we see two blocks of different length, we will
+     *  notice that right away, and not read any further. It also ensures that
+     *  we see the checksum right away, so that most of the time it is enough
+     *  to short-circuit our comparison.
+     */
+    static int Compare(const SkFlatData* a, const SkFlatData* b) {
+        const uint32_t* stop = a->dataStop();
+        const uint32_t* a_ptr = a->dataToCompare() - 1;
+        const uint32_t* b_ptr = b->dataToCompare() - 1;
+        // We use -1 above, so we can pre-increment our pointers in the loop
+        while (*++a_ptr == *++b_ptr) {}
 
-    void unflatten(SkBitmap* bitmap, SkRefCntPlayback* rcp) const {
-        SkFlattenableReadBuffer buffer(fBitmapData);
-        if (rcp) {
-            rcp->setupBuffer(buffer);
+        if (a_ptr == stop) {    // sentinel
+            SkASSERT(b->dataStop() == b_ptr);
+            return 0;
         }
-        bitmap->unflatten(buffer);
+        SkASSERT(a_ptr < a->dataStop());
+        SkASSERT(b_ptr < b->dataStop());
+        return (*a_ptr < *b_ptr) ? -1 : 1;
     }
 
-#ifdef SK_DEBUG_VALIDATE
-    void validate() const {
-        // to be written
+    int index() const { return fIndex; }
+    const void* data() const { return (const char*)this + sizeof(*this); }
+    void* data() { return (char*)this + sizeof(*this); }
+    // Our data is always 32bit aligned, so we can offer this accessor
+    uint32_t* data32() { return (uint32_t*)this->data(); }
+    // Returns the size of the flattened data.
+    size_t flatSize() const { return fFlatSize; }
+
+    void setSentinelInCache() {
+        this->setSentinel(kInCache_Sentinel);
+    }
+    void setSentinelAsCandidate() {
+        this->setSentinel(kCandidate_Sentinel);
+    }
+
+    uint32_t checksum() const { return fChecksum; }
+
+#ifdef SK_DEBUG_SIZE
+    // returns the logical size of our data. Does not return any sentinel or
+    // padding we might have.
+    size_t size() const {
+        return sizeof(SkFlatData) + fFlatSize;
     }
 #endif
 
+    static SkFlatData* Create(SkFlatController* controller, const void* obj, int index,
+                              void (*flattenProc)(SkOrderedWriteBuffer&, const void*));
+
+    void unflatten(void* result,
+                   void (*unflattenProc)(SkOrderedReadBuffer&, void*),
+                   SkBitmapHeap* bitmapHeap = NULL,
+                   SkTypefacePlayback* facePlayback = NULL) const;
+
+    // When we purge an entry, we want to reuse an old index for the new entry,
+    // so we expose this setter.
+    void setIndex(int index) { fIndex = index; }
+
+    // for unittesting
+    friend bool operator==(const SkFlatData& a, const SkFlatData& b) {
+        size_t N = (const char*)a.dataStop() - (const char*)a.dataToCompare();
+        return !memcmp(a.dataToCompare(), b.dataToCompare(), N);
+    }
+
+    // returns true if fTopBot[] has been recorded
+    bool isTopBotWritten() const {
+        return !SkScalarIsNaN(fTopBot[0]);
+    }
+
+    // Returns fTopBot array, so it can be passed to a routine to compute them.
+    // For efficiency, we assert that fTopBot have not been recorded yet.
+    SkScalar* writableTopBot() const {
+        SkASSERT(!this->isTopBotWritten());
+        return fTopBot;
+    }
+
+    // return the topbot[] after it has been recorded
+    const SkScalar* topBot() const {
+        SkASSERT(this->isTopBotWritten());
+        return fTopBot;
+    }
+
 private:
-    char fBitmapData[1];
-    typedef SkFlatData INHERITED;
+    // This is *not* part of the key for search/sort
+    int fIndex;
+
+    // Cache of paint's FontMetrics fTop,fBottom
+    // initialied to [NaN,NaN] as a sentinel that they have not been recorded yet
+    //
+    // This is *not* part of the key for search/sort
+    mutable SkScalar fTopBot[2];
+
+    // marks fTopBot[] as unrecorded
+    void setTopBotUnwritten() {
+        this->fTopBot[0] = this->fTopBot[1] = SK_ScalarNaN; // initial to sentinel values
+    }
+
+    // From here down is the data we look at in the search/sort. We always begin
+    // with the checksum and then length.
+    uint32_t fChecksum;
+    int32_t  fFlatSize;  // size of flattened data
+    // uint32_t flattenedData[]
+    // uint32_t sentinelValue
+
+    const uint32_t* dataToCompare() const {
+        return (const uint32_t*)&fChecksum;
+    }
+    const uint32_t* dataStop() const {
+        SkASSERT(SkIsAlign4(fFlatSize));
+        return (const uint32_t*)((const char*)this->data() + fFlatSize);
+    }
+
+    enum {
+        kInCache_Sentinel = 0,
+        kCandidate_Sentinel = ~0U,
+    };
+    void setSentinel(uint32_t value) {
+        SkASSERT(SkIsAlign4(fFlatSize));
+        this->data32()[fFlatSize >> 2] = value;
+    }
 };
 
-class SkFlatMatrix : public SkFlatData {
+template <class T>
+class SkFlatDictionary {
 public:
-    static SkFlatMatrix* Flatten(SkChunkAlloc* heap, const SkMatrix& matrix, int index);
-
-    void unflatten(SkMatrix* result) const {
-        result->unflatten(fMatrixData);
+    SkFlatDictionary(SkFlatController* controller)
+    : fController(controller) {
+        fFlattenProc = NULL;
+        fUnflattenProc = NULL;
+        SkASSERT(controller);
+        fController->ref();
+        // set to 1 since returning a zero from find() indicates failure
+        fNextIndex = 1;
+        sk_bzero(fHash, sizeof(fHash));
     }
 
-#ifdef SK_DEBUG_DUMP
-    void dump() const;
-#endif
-
-#ifdef SK_DEBUG_VALIDATE
-    void validate() const {
-        // to be written
+    virtual ~SkFlatDictionary() {
+        fController->unref();
     }
-#endif
+
+    int count() const { return fData.count(); }
+
+    const SkFlatData*  operator[](int index) const {
+        SkASSERT(index >= 0 && index < fData.count());
+        return fData[index];
+    }
+
+    /**
+     * Clears the dictionary of all entries. However, it does NOT free the
+     * memory that was allocated for each entry.
+     */
+    void reset() {
+        fData.reset();
+        fNextIndex = 1;
+        sk_bzero(fHash, sizeof(fHash));
+    }
+
+    /**
+     * Similar to find. Allows the caller to specify an SkFlatData to replace in
+     * the case of an add. Also tells the caller whether a new SkFlatData was
+     * added and whether the old one was replaced. The parameters added and
+     * replaced are required to be non-NULL. Rather than returning the index of
+     * the entry in the dictionary, it returns the actual SkFlatData.
+     */
+    const SkFlatData* findAndReplace(const T& element,
+                                     const SkFlatData* toReplace, bool* added,
+                                     bool* replaced) {
+        SkASSERT(added != NULL && replaced != NULL);
+        int oldCount = fData.count();
+        const SkFlatData* flat = this->findAndReturnFlat(element);
+        *added = fData.count() == oldCount + 1;
+        *replaced = false;
+        if (*added && toReplace != NULL) {
+            // First, find the index of the one to replace
+            int indexToReplace = fData.find(toReplace);
+            if (indexToReplace >= 0) {
+                // findAndReturnFlat set the index to fNextIndex and increased
+                // fNextIndex by one. Reuse the index from the one being
+                // replaced and reset fNextIndex to the proper value.
+                const_cast<SkFlatData*>(flat)->setIndex(toReplace->index());
+                fNextIndex--;
+                // Remove from the array.
+                fData.remove(indexToReplace);
+                // Remove from the hash table.
+                int oldHash = ChecksumToHashIndex(toReplace->checksum());
+                if (fHash[oldHash] == toReplace) {
+                    fHash[oldHash] = NULL;
+                }
+                // Delete the actual object.
+                fController->unalloc((void*)toReplace);
+                *replaced = true;
+            }
+        }
+        return flat;
+    }
+
+    /**
+     * Given an element of type T return its 1-based index in the dictionary. If
+     * the element wasn't previously in the dictionary it is automatically
+     * added.
+     *
+     * To make the Compare function fast, we write a sentinel value at the end
+     * of each block. The blocks in our fData[] all have a 0 sentinel. The
+     * newly created block we're comparing against has a -1 in the sentinel.
+     *
+     * This trick allows Compare to always loop until failure. If it fails on
+     * the sentinal value, we know the blocks are equal.
+     */
+    int find(const T& element) {
+        return this->findAndReturnFlat(element)->index();
+    }
+
+    /**
+     *  Unflatten the objects and return them in SkTRefArray, or return NULL
+     *  if there no objects (instead of an empty array).
+     */
+    SkTRefArray<T>* unflattenToArray() const {
+        int count = fData.count();
+        SkTRefArray<T>* array = NULL;
+        if (count > 0) {
+            array = SkTRefArray<T>::Create(count);
+            this->unflattenIntoArray(&array->writableAt(0));
+        }
+        return array;
+    }
+
+    const SkFlatData* findAndReturnFlat(const T& element) {
+        SkFlatData* flat = SkFlatData::Create(fController, &element, fNextIndex, fFlattenProc);
+
+        int hashIndex = ChecksumToHashIndex(flat->checksum());
+        const SkFlatData* candidate = fHash[hashIndex];
+        if (candidate && !SkFlatData::Compare(flat, candidate)) {
+            fController->unalloc(flat);
+            return candidate;
+        }
+
+        int index = SkTSearch<SkFlatData>((const SkFlatData**) fData.begin(),
+                                          fData.count(), flat, sizeof(flat),
+                                          &SkFlatData::Compare);
+        if (index >= 0) {
+            fController->unalloc(flat);
+            fHash[hashIndex] = fData[index];
+            return fData[index];
+        }
+
+        index = ~index;
+        *fData.insert(index) = flat;
+        SkASSERT(fData.count() == fNextIndex);
+        fNextIndex++;
+        flat->setSentinelInCache();
+        fHash[hashIndex] = flat;
+        return flat;
+    }
+
+protected:
+    void (*fFlattenProc)(SkOrderedWriteBuffer&, const void*);
+    void (*fUnflattenProc)(SkOrderedReadBuffer&, void*);
 
 private:
-    char fMatrixData[1];
-    typedef SkFlatData INHERITED;
+    void unflattenIntoArray(T* array) const {
+        const int count = fData.count();
+        const SkFlatData** iter = fData.begin();
+        for (int i = 0; i < count; ++i) {
+            const SkFlatData* element = iter[i];
+            int index = element->index() - 1;
+            SkASSERT((unsigned)index < (unsigned)count);
+            element->unflatten(&array[index], fUnflattenProc,
+                               fController->getBitmapHeap(),
+                               fController->getTypefacePlayback());
+        }
+    }
+
+    SkFlatController * const     fController;
+    int                          fNextIndex;
+    SkTDArray<const SkFlatData*> fData;
+
+    enum {
+        // Determined by trying diff values on picture-recording benchmarks
+        // (e.g. PictureRecordBench.cpp), choosing the smallest value that
+        // showed a big improvement. Even better would be to benchmark diff
+        // values on recording representative web-pages or other "real" content.
+        HASH_BITS   = 7,
+        HASH_MASK   = (1 << HASH_BITS) - 1,
+        HASH_COUNT  = 1 << HASH_BITS
+    };
+    const SkFlatData* fHash[HASH_COUNT];
+
+    static int ChecksumToHashIndex(uint32_t checksum) {
+        int n = checksum;
+        if (HASH_BITS < 32) {
+            n ^= n >> 16;
+        }
+        if (HASH_BITS < 16) {
+            n ^= n >> 8;
+        }
+        if (HASH_BITS < 8) {
+            n ^= n >> 4;
+        }
+        return n & HASH_MASK;
+    }
 };
 
-class SkFlatPaint : public SkFlatData {
-public:
-    static SkFlatPaint* Flatten(SkChunkAlloc* heap, const SkPaint& paint,
-                                int index, SkRefCntSet*,
-                                SkRefCntSet* faceRecorder);
-    
-    void unflatten(SkPaint* result, SkRefCntPlayback* rcp,
-                   SkTypefacePlayback* facePlayback) const {
-        Read(fPaintData, result, rcp, facePlayback);
-    }
-    
-    static void Read(const void* storage, SkPaint* paint, SkRefCntPlayback*,
-                     SkTypefacePlayback* facePlayback);
+///////////////////////////////////////////////////////////////////////////////
+// Some common dictionaries are defined here for both reference and convenience
+///////////////////////////////////////////////////////////////////////////////
 
-#ifdef SK_DEBUG_DUMP
-    void dump() const;
-#endif
-    
+template <class T>
+static void SkFlattenObjectProc(SkOrderedWriteBuffer& buffer, const void* obj) {
+    ((T*)obj)->flatten(buffer);
+}
+
+template <class T>
+static void SkUnflattenObjectProc(SkOrderedReadBuffer& buffer, void* obj) {
+    ((T*)obj)->unflatten(buffer);
+}
+
+class SkChunkFlatController : public SkFlatController {
+public:
+    SkChunkFlatController(size_t minSize)
+    : fHeap(minSize)
+    , fTypefaceSet(SkNEW(SkRefCntSet)) {
+        this->setTypefaceSet(fTypefaceSet);
+        this->setTypefacePlayback(&fTypefacePlayback);
+    }
+
+    ~SkChunkFlatController() {
+        fTypefaceSet->unref();
+    }
+
+    virtual void* allocThrow(size_t bytes) SK_OVERRIDE {
+        return fHeap.allocThrow(bytes);
+    }
+
+    virtual void unalloc(void* ptr) SK_OVERRIDE {
+        (void) fHeap.unalloc(ptr);
+    }
+
+    void setupPlaybacks() const {
+        fTypefacePlayback.reset(fTypefaceSet);
+    }
+
+    void setBitmapStorage(SkBitmapHeap* heap) {
+        this->setBitmapHeap(heap);
+    }
+
 private:
-    char fPaintData[1];
-    typedef SkFlatData INHERITED;
+    SkChunkAlloc               fHeap;
+    SkRefCntSet*               fTypefaceSet;
+    mutable SkTypefacePlayback fTypefacePlayback;
 };
 
-class SkFlatRegion : public SkFlatData {
+class SkBitmapDictionary : public SkFlatDictionary<SkBitmap> {
 public:
-    static SkFlatRegion* Flatten(SkChunkAlloc* heap, const SkRegion& region, int index);
-    
-    void unflatten(SkRegion* result) const {
-        result->unflatten(fRegionData);
+    SkBitmapDictionary(SkFlatController* controller)
+    : SkFlatDictionary<SkBitmap>(controller) {
+        fFlattenProc = &SkFlattenObjectProc<SkBitmap>;
+        fUnflattenProc = &SkUnflattenObjectProc<SkBitmap>;
+    }
+};
+
+class SkMatrixDictionary : public SkFlatDictionary<SkMatrix> {
+ public:
+    SkMatrixDictionary(SkFlatController* controller)
+    : SkFlatDictionary<SkMatrix>(controller) {
+        fFlattenProc = &flattenMatrix;
+        fUnflattenProc = &unflattenMatrix;
     }
 
-#ifdef SK_DEBUG_VALIDATE
-    void validate() const {
-        // to be written
+    static void flattenMatrix(SkOrderedWriteBuffer& buffer, const void* obj) {
+        buffer.getWriter32()->writeMatrix(*(SkMatrix*)obj);
     }
-#endif
 
-private:
-    char fRegionData[1];
-    typedef SkFlatData INHERITED;
+    static void unflattenMatrix(SkOrderedReadBuffer& buffer, void* obj) {
+        buffer.getReader32()->readMatrix((SkMatrix*)obj);
+    }
+};
+
+class SkPaintDictionary : public SkFlatDictionary<SkPaint> {
+ public:
+    SkPaintDictionary(SkFlatController* controller)
+    : SkFlatDictionary<SkPaint>(controller) {
+        fFlattenProc = &SkFlattenObjectProc<SkPaint>;
+        fUnflattenProc = &SkUnflattenObjectProc<SkPaint>;
+    }
+};
+
+class SkRegionDictionary : public SkFlatDictionary<SkRegion> {
+ public:
+    SkRegionDictionary(SkFlatController* controller)
+    : SkFlatDictionary<SkRegion>(controller) {
+        fFlattenProc = &flattenRegion;
+        fUnflattenProc = &unflattenRegion;
+    }
+
+    static void flattenRegion(SkOrderedWriteBuffer& buffer, const void* obj) {
+        buffer.getWriter32()->writeRegion(*(SkRegion*)obj);
+    }
+
+    static void unflattenRegion(SkOrderedReadBuffer& buffer, void* obj) {
+        buffer.getReader32()->readRegion((SkRegion*)obj);
+    }
 };
 
 #endif
diff --git a/src/core/SkPicturePlayback.cpp b/src/core/SkPicturePlayback.cpp
index f5756a3..3ca26d4 100644
--- a/src/core/SkPicturePlayback.cpp
+++ b/src/core/SkPicturePlayback.cpp
@@ -8,7 +8,16 @@
 #include "SkPicturePlayback.h"
 #include "SkPictureRecord.h"
 #include "SkTypeface.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
 #include <new>
+#include "SkBBoxHierarchy.h"
+#include "SkPictureStateTree.h"
+#include "SkTSort.h"
+
+template <typename T> int SafeCount(const T* obj) {
+    return obj ? obj->count() : 0;
+}
 
 /*  Define this to spew out a debug statement whenever we skip the remainder of
     a save/restore block because a clip... command returned false (empty).
@@ -19,7 +28,7 @@
     this->init();
 }
 
-SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record) {
+SkPicturePlayback::SkPicturePlayback(const SkPictureRecord& record, bool deepCopy) {
 #ifdef SK_DEBUG_SIZE
     size_t overallBytes, bitmapBytes, matricesBytes,
     paintBytes, pathBytes, pictureBytes, regionBytes;
@@ -60,81 +69,58 @@
     record.validate();
     const SkWriter32& writer = record.writeStream();
     init();
-    if (writer.size() == 0)
+    if (writer.size() == 0) {
+        fOpData = SkData::NewEmpty();
         return;
+    }
+
+    fBoundingHierarchy = record.fBoundingHierarchy;
+    fStateTree = record.fStateTree;
+
+    SkSafeRef(fBoundingHierarchy);
+    SkSafeRef(fStateTree);
+
+    if (NULL != fBoundingHierarchy) {
+        fBoundingHierarchy->flushDeferredInserts();
+    }
 
     {
         size_t size = writer.size();
         void* buffer = sk_malloc_throw(size);
         writer.flatten(buffer);
-        fReader.setMemory(buffer, size);    // fReader owns buffer now
+        SkASSERT(!fOpData);
+        fOpData = SkData::NewFromMalloc(buffer, size);
     }
 
     // copy over the refcnt dictionary to our reader
-    //
-    fRCPlayback.reset(&record.fRCSet);
-    fRCPlayback.setupBuffer(fReader);
+    record.fFlattenableHeap.setupPlaybacks();
 
-    fTFPlayback.reset(&record.fTFSet);
-    fTFPlayback.setupBuffer(fReader);
+    fBitmaps = record.fBitmapHeap->extractBitmaps();
+    fMatrices = record.fMatrices.unflattenToArray();
+    fPaints = record.fPaints.unflattenToArray();
+    fRegions = record.fRegions.unflattenToArray();
 
-    const SkTDArray<const SkFlatBitmap* >& bitmaps = record.getBitmaps();
-    fBitmapCount = bitmaps.count();
-    if (fBitmapCount > 0) {
-        fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
-        for (const SkFlatBitmap** flatBitmapPtr = bitmaps.begin();
-             flatBitmapPtr != bitmaps.end(); flatBitmapPtr++) {
-            const SkFlatBitmap* flatBitmap = *flatBitmapPtr;
-            int index = flatBitmap->index() - 1;
-            flatBitmap->unflatten(&fBitmaps[index], &fRCPlayback);
+    fBitmapHeap.reset(SkSafeRef(record.fBitmapHeap));
+    fPathHeap.reset(SkSafeRef(record.fPathHeap));
+
+    // ensure that the paths bounds are pre-computed
+    if (fPathHeap.get()) {
+        for (int i = 0; i < fPathHeap->count(); i++) {
+            (*fPathHeap)[i].updateBoundsCache();
         }
     }
 
-    const SkTDArray<const SkFlatMatrix* >& matrices = record.getMatrices();
-    fMatrixCount = matrices.count();
-    if (fMatrixCount > 0) {
-        fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
-        for (const SkFlatMatrix** matrixPtr = matrices.begin();
-             matrixPtr != matrices.end(); matrixPtr++) {
-            const SkFlatMatrix* flatMatrix = *matrixPtr;
-            flatMatrix->unflatten(&fMatrices[flatMatrix->index() - 1]);
-        }
-    }
-
-    const SkTDArray<const SkFlatPaint* >& paints = record.getPaints();
-    fPaintCount = paints.count();
-    if (fPaintCount > 0) {
-        fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
-        for (const SkFlatPaint** flatPaintPtr = paints.begin();
-             flatPaintPtr != paints.end(); flatPaintPtr++) {
-            const SkFlatPaint* flatPaint = *flatPaintPtr;
-            int index = flatPaint->index() - 1;
-            SkASSERT((unsigned)index < (unsigned)fPaintCount);
-            flatPaint->unflatten(&fPaints[index], &fRCPlayback, &fTFPlayback);
-        }
-    }
-
-    fPathHeap = record.fPathHeap;
-    SkSafeRef(fPathHeap);
-
     const SkTDArray<SkPicture* >& pictures = record.getPictureRefs();
     fPictureCount = pictures.count();
     if (fPictureCount > 0) {
         fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
         for (int i = 0; i < fPictureCount; i++) {
-            fPictureRefs[i] = pictures[i];
-            fPictureRefs[i]->ref();
-        }
-    }
-
-    const SkTDArray<const SkFlatRegion* >& regions = record.getRegions();
-    fRegionCount = regions.count();
-    if (fRegionCount > 0) {
-        fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
-        for (const SkFlatRegion** flatRegionPtr = regions.begin();
-             flatRegionPtr != regions.end(); flatRegionPtr++) {
-            const SkFlatRegion* flatRegion = *flatRegionPtr;
-            flatRegion->unflatten(&fRegions[flatRegion->index() - 1]);
+            if (deepCopy) {
+                fPictureRefs[i] = pictures[i]->clone();
+            } else {
+                fPictureRefs[i] = pictures[i];
+                fPictureRefs[i]->ref();
+            }
         }
     }
 
@@ -160,49 +146,116 @@
 #endif
 }
 
-SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src) {
+static bool needs_deep_copy(const SkPaint& paint) {
+    /*
+     *  These fields are known to be immutable, and so can be shallow-copied
+     *
+     *  getTypeface();
+     *  getAnnotation();
+     */
+
+    return paint.getPathEffect() ||
+           paint.getShader() ||
+           paint.getXfermode() ||
+           paint.getMaskFilter() ||
+           paint.getColorFilter() ||
+           paint.getRasterizer() ||
+           paint.getLooper() ||
+           paint.getImageFilter();
+}
+
+SkPicturePlayback::SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInfo* deepCopyInfo) {
     this->init();
 
-    // copy the data from fReader
-    {
-        size_t size = src.fReader.size();
-        void* buffer = sk_malloc_throw(size);
-        memcpy(buffer, src.fReader.base(), size);
-        fReader.setMemory(buffer, size);
+    fBitmapHeap.reset(SkSafeRef(src.fBitmapHeap.get()));
+    fPathHeap.reset(SkSafeRef(src.fPathHeap.get()));
+
+    fMatrices = SkSafeRef(src.fMatrices);
+    fRegions = SkSafeRef(src.fRegions);
+    fOpData = SkSafeRef(src.fOpData);
+
+    fBoundingHierarchy = src.fBoundingHierarchy;
+    fStateTree = src.fStateTree;
+
+    SkSafeRef(fBoundingHierarchy);
+    SkSafeRef(fStateTree);
+
+    if (deepCopyInfo) {
+        int paintCount = SafeCount(src.fPaints);
+
+        if (src.fBitmaps) {
+            fBitmaps = SkTRefArray<SkBitmap>::Create(src.fBitmaps->begin(), src.fBitmaps->count());
+        }
+
+        if (!deepCopyInfo->initialized) {
+            /* The alternative to doing this is to have a clone method on the paint and have it make
+             * the deep copy of its internal structures as needed. The holdup to doing that is at
+             * this point we would need to pass the SkBitmapHeap so that we don't unnecessarily
+             * flatten the pixels in a bitmap shader.
+             */
+            deepCopyInfo->paintData.setCount(paintCount);
+
+            /* Use an SkBitmapHeap to avoid flattening bitmaps in shaders. If there already is one,
+             * use it. If this SkPicturePlayback was created from a stream, fBitmapHeap will be
+             * NULL, so create a new one.
+             */
+            if (fBitmapHeap.get() == NULL) {
+                // FIXME: Put this on the stack inside SkPicture::clone. Further, is it possible to
+                // do the rest of this initialization in SkPicture::clone as well?
+                SkBitmapHeap* heap = SkNEW(SkBitmapHeap);
+                deepCopyInfo->controller.setBitmapStorage(heap);
+                heap->unref();
+            } else {
+                deepCopyInfo->controller.setBitmapStorage(fBitmapHeap);
+            }
+
+            SkDEBUGCODE(int heapSize = SafeCount(fBitmapHeap.get());)
+            for (int i = 0; i < paintCount; i++) {
+                if (needs_deep_copy(src.fPaints->at(i))) {
+                    deepCopyInfo->paintData[i] = SkFlatData::Create(&deepCopyInfo->controller,
+                                                                    &src.fPaints->at(i), 0,
+                                                                    &SkFlattenObjectProc<SkPaint>);
+                } else {
+                    // this is our sentinel, which we use in the unflatten loop
+                    deepCopyInfo->paintData[i] = NULL;
+                }
+            }
+            SkASSERT(SafeCount(fBitmapHeap.get()) == heapSize);
+
+            // needed to create typeface playback
+            deepCopyInfo->controller.setupPlaybacks();
+            deepCopyInfo->initialized = true;
+        }
+
+        fPaints = SkTRefArray<SkPaint>::Create(paintCount);
+        SkASSERT(deepCopyInfo->paintData.count() == paintCount);
+        SkBitmapHeap* bmHeap = deepCopyInfo->controller.getBitmapHeap();
+        SkTypefacePlayback* tfPlayback = deepCopyInfo->controller.getTypefacePlayback();
+        for (int i = 0; i < paintCount; i++) {
+            if (deepCopyInfo->paintData[i]) {
+                deepCopyInfo->paintData[i]->unflatten(&fPaints->writableAt(i),
+                                                      &SkUnflattenObjectProc<SkPaint>,
+                                                      bmHeap, tfPlayback);
+            } else {
+                // needs_deep_copy was false, so just need to assign
+                fPaints->writableAt(i) = src.fPaints->at(i);
+            }
+        }
+
+    } else {
+        fBitmaps = SkSafeRef(src.fBitmaps);
+        fPaints = SkSafeRef(src.fPaints);
     }
 
-    int i;
-
-    fBitmapCount = src.fBitmapCount;
-    fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
-    for (i = 0; i < fBitmapCount; i++) {
-        fBitmaps[i] = src.fBitmaps[i];
-    }
-
-    fMatrixCount = src.fMatrixCount;
-    fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
-    memcpy(fMatrices, src.fMatrices, fMatrixCount * sizeof(SkMatrix));
-
-    fPaintCount = src.fPaintCount;
-    fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
-    for (i = 0; i < fPaintCount; i++) {
-        fPaints[i] = src.fPaints[i];
-    }
-
-    fPathHeap = src.fPathHeap;
-    SkSafeRef(fPathHeap);
-
     fPictureCount = src.fPictureCount;
     fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
     for (int i = 0; i < fPictureCount; i++) {
-        fPictureRefs[i] = src.fPictureRefs[i];
-        fPictureRefs[i]->ref();
-    }
-
-    fRegionCount = src.fRegionCount;
-    fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
-    for (i = 0; i < fRegionCount; i++) {
-        fRegions[i] = src.fRegions[i];
+        if (deepCopyInfo) {
+            fPictureRefs[i] = src.fPictureRefs[i]->clone();
+        } else {
+            fPictureRefs[i] = src.fPictureRefs[i];
+            fPictureRefs[i]->ref();
+        }
     }
 }
 
@@ -210,24 +263,24 @@
     fBitmaps = NULL;
     fMatrices = NULL;
     fPaints = NULL;
-    fPathHeap = NULL;
     fPictureRefs = NULL;
     fRegions = NULL;
-    fBitmapCount = fMatrixCount = fPaintCount = fPictureCount =
-    fRegionCount = 0;
-
+    fPictureCount = 0;
+    fOpData = NULL;
     fFactoryPlayback = NULL;
+    fBoundingHierarchy = NULL;
+    fStateTree = NULL;
 }
 
 SkPicturePlayback::~SkPicturePlayback() {
-    sk_free((void*) fReader.base());
+    fOpData->unref();
 
-    SkDELETE_ARRAY(fBitmaps);
-    SkDELETE_ARRAY(fMatrices);
-    SkDELETE_ARRAY(fPaints);
-    SkDELETE_ARRAY(fRegions);
-
-    SkSafeUnref(fPathHeap);
+    SkSafeUnref(fBitmaps);
+    SkSafeUnref(fMatrices);
+    SkSafeUnref(fPaints);
+    SkSafeUnref(fRegions);
+    SkSafeUnref(fBoundingHierarchy);
+    SkSafeUnref(fStateTree);
 
     for (int i = 0; i < fPictureCount; i++) {
         fPictureRefs[i]->unref();
@@ -239,38 +292,40 @@
 
 void SkPicturePlayback::dumpSize() const {
     SkDebugf("--- picture size: ops=%d bitmaps=%d [%d] matrices=%d [%d] paints=%d [%d] paths=%d regions=%d\n",
-             fReader.size(),
-             fBitmapCount, fBitmapCount * sizeof(SkBitmap),
-             fMatrixCount, fMatrixCount * sizeof(SkMatrix),
-             fPaintCount, fPaintCount * sizeof(SkPaint),
-             fPathHeap ? fPathHeap->count() : 0,
-             fRegionCount);
+             fOpData->size(),
+             SafeCount(fBitmaps), SafeCount(fBitmaps) * sizeof(SkBitmap),
+             SafeCount(fMatrices), SafeCount(fMatrices) * sizeof(SkMatrix),
+             SafeCount(fPaints), SafeCount(fPaints) * sizeof(SkPaint),
+             SafeCount(fPathHeap.get()),
+             SafeCount(fRegions));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
-// The chunks are writte/read in this order...
-
 #define PICT_READER_TAG     SkSetFourByteTag('r', 'e', 'a', 'd')
 #define PICT_FACTORY_TAG    SkSetFourByteTag('f', 'a', 'c', 't')
 #define PICT_TYPEFACE_TAG   SkSetFourByteTag('t', 'p', 'f', 'c')
 #define PICT_PICTURE_TAG    SkSetFourByteTag('p', 'c', 't', 'r')
-#define PICT_ARRAYS_TAG     SkSetFourByteTag('a', 'r', 'a', 'y')
+
+// This tag specifies the size of the ReadBuffer, needed for the following tags
+#define PICT_BUFFER_SIZE_TAG     SkSetFourByteTag('a', 'r', 'a', 'y')
 // these are all inside the ARRAYS tag
-#define PICT_BITMAP_TAG     SkSetFourByteTag('b', 't', 'm', 'p')
-#define PICT_MATRIX_TAG     SkSetFourByteTag('m', 't', 'r', 'x')
-#define PICT_PAINT_TAG      SkSetFourByteTag('p', 'n', 't', ' ')
-#define PICT_PATH_TAG       SkSetFourByteTag('p', 't', 'h', ' ')
-#define PICT_REGION_TAG     SkSetFourByteTag('r', 'g', 'n', ' ')
-#define PICT_SHAPE_TAG      SkSetFourByteTag('s', 'h', 'p', ' ')
+#define PICT_BITMAP_BUFFER_TAG  SkSetFourByteTag('b', 't', 'm', 'p')
+#define PICT_MATRIX_BUFFER_TAG  SkSetFourByteTag('m', 't', 'r', 'x')
+#define PICT_PAINT_BUFFER_TAG   SkSetFourByteTag('p', 'n', 't', ' ')
+#define PICT_PATH_BUFFER_TAG    SkSetFourByteTag('p', 't', 'h', ' ')
+#define PICT_REGION_BUFFER_TAG  SkSetFourByteTag('r', 'g', 'n', ' ')
+
+// Always write this guy last (with no length field afterwards)
+#define PICT_EOF_TAG     SkSetFourByteTag('e', 'o', 'f', ' ')
 
 #include "SkStream.h"
 
-static void writeTagSize(SkFlattenableWriteBuffer& buffer, uint32_t tag,
+static void writeTagSize(SkOrderedWriteBuffer& buffer, uint32_t tag,
                          uint32_t size) {
-    buffer.write32(tag);
-    buffer.write32(size);
+    buffer.writeUInt(tag);
+    buffer.writeUInt(size);
 }
 
 static void writeTagSize(SkWStream* stream, uint32_t tag,
@@ -315,168 +370,234 @@
     }
 }
 
-void SkPicturePlayback::serialize(SkWStream* stream) const {
-    writeTagSize(stream, PICT_READER_TAG, fReader.size());
-    stream->write(fReader.base(), fReader.size());
+void SkPicturePlayback::flattenToBuffer(SkOrderedWriteBuffer& buffer) const {
+    int i, n;
 
-    SkRefCntSet  typefaceSet;
-    SkFactorySet factSet;
-
-    SkFlattenableWriteBuffer buffer(1024);
-
-    buffer.setFlags(SkFlattenableWriteBuffer::kCrossProcess_Flag);
-    buffer.setTypefaceRecorder(&typefaceSet);
-    buffer.setFactoryRecorder(&factSet);
-
-    int i;
-
-    writeTagSize(buffer, PICT_BITMAP_TAG, fBitmapCount);
-    for (i = 0; i < fBitmapCount; i++) {
-        fBitmaps[i].flatten(buffer);
-    }
-
-    writeTagSize(buffer, PICT_MATRIX_TAG, fMatrixCount);
-    buffer.writeMul4(fMatrices, fMatrixCount * sizeof(SkMatrix));
-
-    writeTagSize(buffer, PICT_PAINT_TAG, fPaintCount);
-    for (i = 0; i < fPaintCount; i++) {
-        fPaints[i].flatten(buffer);
-    }
-
-    {
-        int count = fPathHeap ? fPathHeap->count() : 0;
-        writeTagSize(buffer, PICT_PATH_TAG, count);
-        if (count > 0) {
-            fPathHeap->flatten(buffer);
+    if ((n = SafeCount(fBitmaps)) > 0) {
+        writeTagSize(buffer, PICT_BITMAP_BUFFER_TAG, n);
+        for (i = 0; i < n; i++) {
+            buffer.writeBitmap((*fBitmaps)[i]);
         }
     }
 
-    writeTagSize(buffer, PICT_REGION_TAG, fRegionCount);
-    for (i = 0; i < fRegionCount; i++) {
-        uint32_t size = fRegions[i].flatten(NULL);
-        buffer.write32(size);
-        SkAutoSMalloc<512> storage(size);
-        fRegions[i].flatten(storage.get());
-        buffer.writePad(storage.get(), size);
+    if ((n = SafeCount(fMatrices)) > 0) {
+        writeTagSize(buffer, PICT_MATRIX_BUFFER_TAG, n);
+        for (i = 0; i < n; i++) {
+            buffer.writeMatrix((*fMatrices)[i]);
+        }
+
     }
 
-    // now we can write to the stream again
-
-    writeFactories(stream, factSet);
-    writeTypefaces(stream, typefaceSet);
-
-    writeTagSize(stream, PICT_PICTURE_TAG, fPictureCount);
-    for (i = 0; i < fPictureCount; i++) {
-        fPictureRefs[i]->serialize(stream);
+    if ((n = SafeCount(fPaints)) > 0) {
+        writeTagSize(buffer, PICT_PAINT_BUFFER_TAG, n);
+        for (i = 0; i < n; i++) {
+            buffer.writePaint((*fPaints)[i]);
+        }
     }
 
-    writeTagSize(stream, PICT_ARRAYS_TAG, buffer.size());
-    buffer.writeToStream(stream);
+    if ((n = SafeCount(fPathHeap.get())) > 0) {
+        writeTagSize(buffer, PICT_PATH_BUFFER_TAG, n);
+        fPathHeap->flatten(buffer);
+    }
+
+    if ((n = SafeCount(fRegions)) > 0) {
+        writeTagSize(buffer, PICT_REGION_BUFFER_TAG, n);
+        for (i = 0; i < n; i++) {
+            buffer.writeRegion((*fRegions)[i]);
+        }
+    }
+}
+
+void SkPicturePlayback::serialize(SkWStream* stream,
+                                  SkSerializationHelpers::EncodeBitmap encoder) const {
+    writeTagSize(stream, PICT_READER_TAG, fOpData->size());
+    stream->write(fOpData->bytes(), fOpData->size());
+
+    if (fPictureCount > 0) {
+        writeTagSize(stream, PICT_PICTURE_TAG, fPictureCount);
+        for (int i = 0; i < fPictureCount; i++) {
+            fPictureRefs[i]->serialize(stream);
+        }
+    }
+
+    // Write some of our data into a writebuffer, and then serialize that
+    // into our stream
+    {
+        SkRefCntSet  typefaceSet;
+        SkFactorySet factSet;
+
+        SkOrderedWriteBuffer buffer(1024);
+
+        buffer.setFlags(SkFlattenableWriteBuffer::kCrossProcess_Flag);
+        buffer.setTypefaceRecorder(&typefaceSet);
+        buffer.setFactoryRecorder(&factSet);
+        buffer.setBitmapEncoder(encoder);
+
+        this->flattenToBuffer(buffer);
+
+        // We have to write these to sets into the stream *before* we write
+        // the buffer, since parsing that buffer will require that we already
+        // have these sets available to use.
+        writeFactories(stream, factSet);
+        writeTypefaces(stream, typefaceSet);
+
+        writeTagSize(stream, PICT_BUFFER_SIZE_TAG, buffer.size());
+        buffer.writeToStream(stream);
+    }
+
+    stream->write32(PICT_EOF_TAG);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static int readTagSize(SkFlattenableReadBuffer& buffer, uint32_t expectedTag) {
-    uint32_t tag = buffer.readU32();
-    if (tag != expectedTag) {
-        sk_throw();
+/**
+ *  Return the corresponding SkFlattenableReadBuffer flags, given a set of
+ *  SkPictInfo flags.
+ */
+static uint32_t pictInfoFlagsToReadBufferFlags(uint32_t pictInfoFlags) {
+    static const struct {
+        uint32_t    fSrc;
+        uint32_t    fDst;
+    } gSD[] = {
+        { SkPictInfo::kCrossProcess_Flag,   SkFlattenableReadBuffer::kCrossProcess_Flag },
+        { SkPictInfo::kScalarIsFloat_Flag,  SkFlattenableReadBuffer::kScalarIsFloat_Flag },
+        { SkPictInfo::kPtrIs64Bit_Flag,     SkFlattenableReadBuffer::kPtrIs64Bit_Flag },
+    };
+
+    uint32_t rbMask = 0;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gSD); ++i) {
+        if (pictInfoFlags & gSD[i].fSrc) {
+            rbMask |= gSD[i].fDst;
+        }
     }
-    return buffer.readU32();
+    return rbMask;
 }
 
-static int readTagSize(SkStream* stream, uint32_t expectedTag) {
-    uint32_t tag = stream->readU32();
-    if (tag != expectedTag) {
-        sk_throw();
+bool SkPicturePlayback::parseStreamTag(SkStream* stream, const SkPictInfo& info,
+                                       uint32_t tag, size_t size,
+                                       SkSerializationHelpers::DecodeBitmap decoder) {
+    /*
+     *  By the time we encounter BUFFER_SIZE_TAG, we need to have already seen
+     *  its dependents: FACTORY_TAG and TYPEFACE_TAG. These two are not required
+     *  but if they are present, they need to have been seen before the buffer.
+     *
+     *  We assert that if/when we see either of these, that we have not yet seen
+     *  the buffer tag, because if we have, then its too-late to deal with the
+     *  factories or typefaces.
+     */
+    bool haveBuffer = false;
+
+    switch (tag) {
+        case PICT_READER_TAG: {
+            void* storage = sk_malloc_throw(size);
+            stream->read(storage, size);
+            SkASSERT(NULL == fOpData);
+            fOpData = SkData::NewFromMalloc(storage, size);
+        } break;
+        case PICT_FACTORY_TAG: {
+            SkASSERT(!haveBuffer);
+            fFactoryPlayback = SkNEW_ARGS(SkFactoryPlayback, (size));
+            for (size_t i = 0; i < size; i++) {
+                SkString str;
+                int len = stream->readPackedUInt();
+                str.resize(len);
+                stream->read(str.writable_str(), len);
+                fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str());
+            }
+        } break;
+        case PICT_TYPEFACE_TAG: {
+            SkASSERT(!haveBuffer);
+            fTFPlayback.setCount(size);
+            for (size_t i = 0; i < size; i++) {
+                SkSafeUnref(fTFPlayback.set(i, SkTypeface::Deserialize(stream)));
+            }
+        } break;
+        case PICT_PICTURE_TAG: {
+            fPictureCount = size;
+            fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
+            for (int i = 0; i < fPictureCount; i++) {
+                fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream));
+            }
+        } break;
+        case PICT_BUFFER_SIZE_TAG: {
+            SkAutoMalloc storage(size);
+            stream->read(storage.get(), size);
+
+            SkOrderedReadBuffer buffer(storage.get(), size);
+            buffer.setFlags(pictInfoFlagsToReadBufferFlags(info.fFlags));
+
+            fFactoryPlayback->setupBuffer(buffer);
+            fTFPlayback.setupBuffer(buffer);
+            buffer.setBitmapDecoder(decoder);
+
+            while (!buffer.eof()) {
+                tag = buffer.readUInt();
+                size = buffer.readUInt();
+                if (!this->parseBufferTag(buffer, tag, size)) {
+                    return false;
+                }
+            }
+            haveBuffer = true;
+        } break;
     }
-    return stream->readU32();
+    return true;    // success
 }
 
-SkPicturePlayback::SkPicturePlayback(SkStream* stream, uint32_t pictureVersion) {
+bool SkPicturePlayback::parseBufferTag(SkOrderedReadBuffer& buffer,
+                                       uint32_t tag, size_t size) {
+    switch (tag) {
+        case PICT_BITMAP_BUFFER_TAG: {
+            fBitmaps = SkTRefArray<SkBitmap>::Create(size);
+            for (size_t i = 0; i < size; ++i) {
+                SkBitmap* bm = &fBitmaps->writableAt(i);
+                buffer.readBitmap(bm);
+                bm->setImmutable();
+            }
+        } break;
+        case PICT_MATRIX_BUFFER_TAG:
+            fMatrices = SkTRefArray<SkMatrix>::Create(size);
+            for (size_t i = 0; i < size; ++i) {
+                buffer.readMatrix(&fMatrices->writableAt(i));
+            }
+            break;
+        case PICT_PAINT_BUFFER_TAG: {
+            fPaints = SkTRefArray<SkPaint>::Create(size);
+            for (size_t i = 0; i < size; ++i) {
+                buffer.readPaint(&fPaints->writableAt(i));
+            }
+        } break;
+        case PICT_PATH_BUFFER_TAG:
+            if (size > 0) {
+                fPathHeap.reset(SkNEW_ARGS(SkPathHeap, (buffer)));
+            }
+            break;
+        case PICT_REGION_BUFFER_TAG: {
+            fRegions = SkTRefArray<SkRegion>::Create(size);
+            for (size_t i = 0; i < size; ++i) {
+                buffer.readRegion(&fRegions->writableAt(i));
+            }
+        } break;
+    }
+    return true;    // success
+}
+
+SkPicturePlayback::SkPicturePlayback(SkStream* stream, const SkPictInfo& info,
+                                     bool* isValid, SkSerializationHelpers::DecodeBitmap decoder) {
     this->init();
 
-    int i;
+    *isValid = false;   // wait until we're done parsing to mark as true
+    for (;;) {
+        uint32_t tag = stream->readU32();
+        if (PICT_EOF_TAG == tag) {
+            break;
+        }
 
-    {
-        size_t size = readTagSize(stream, PICT_READER_TAG);
-        void* storage = sk_malloc_throw(size);
-        stream->read(storage, size);
-        fReader.setMemory(storage, size);
-        fReader.setPictureVersion(pictureVersion);
-    }
-
-    int factoryCount = readTagSize(stream, PICT_FACTORY_TAG);
-    fFactoryPlayback = SkNEW_ARGS(SkFactoryPlayback, (factoryCount));
-    for (i = 0; i < factoryCount; i++) {
-        SkString str;
-        int len = stream->readPackedUInt();
-        str.resize(len);
-        stream->read(str.writable_str(), len);
-//        SkDebugf("--- factory playback [%d] <%s>\n", i, str.c_str());
-        fFactoryPlayback->base()[i] = SkFlattenable::NameToFactory(str.c_str());
-    }
-
-    int typefaceCount = readTagSize(stream, PICT_TYPEFACE_TAG);
-    fTFPlayback.setCount(typefaceCount);
-    for (i = 0; i < typefaceCount; i++) {
-        SkSafeUnref(fTFPlayback.set(i, SkTypeface::Deserialize(stream)));
-    }
-
-    fPictureCount = readTagSize(stream, PICT_PICTURE_TAG);
-    fPictureRefs = SkNEW_ARRAY(SkPicture*, fPictureCount);
-    for (i = 0; i < fPictureCount; i++) {
-        fPictureRefs[i] = SkNEW_ARGS(SkPicture, (stream));
-    }
-
-    /*
-        Now read the arrays chunk, and parse using a read buffer
-    */
-    uint32_t size = readTagSize(stream, PICT_ARRAYS_TAG);
-    SkAutoMalloc storage(size);
-    stream->read(storage.get(), size);
-
-    SkFlattenableReadBuffer buffer(storage.get(), size);
-    buffer.setPictureVersion(pictureVersion);
-    fFactoryPlayback->setupBuffer(buffer);
-    fTFPlayback.setupBuffer(buffer);
-
-    fBitmapCount = readTagSize(buffer, PICT_BITMAP_TAG);
-    fBitmaps = SkNEW_ARRAY(SkBitmap, fBitmapCount);
-    for (i = 0; i < fBitmapCount; i++) {
-        fBitmaps[i].unflatten(buffer);
-    }
-
-    fMatrixCount = readTagSize(buffer, PICT_MATRIX_TAG);
-    fMatrices = SkNEW_ARRAY(SkMatrix, fMatrixCount);
-    buffer.read(fMatrices, fMatrixCount * sizeof(SkMatrix));
-
-    fPaintCount = readTagSize(buffer, PICT_PAINT_TAG);
-    fPaints = SkNEW_ARRAY(SkPaint, fPaintCount);
-    for (i = 0; i < fPaintCount; i++) {
-        fPaints[i].unflatten(buffer);
-    }
-
-    {
-        int count = readTagSize(buffer, PICT_PATH_TAG);
-        if (count > 0) {
-            fPathHeap = SkNEW_ARGS(SkPathHeap, (buffer));
+        uint32_t size = stream->readU32();
+        if (!this->parseStreamTag(stream, info, tag, size, decoder)) {
+            return; // we're invalid
         }
     }
-
-    fRegionCount = readTagSize(buffer, PICT_REGION_TAG);
-    fRegions = SkNEW_ARRAY(SkRegion, fRegionCount);
-    for (i = 0; i < fRegionCount; i++) {
-        uint32_t size = buffer.readU32();
-        SkDEBUGCODE(uint32_t bytes =) fRegions[i].unflatten(buffer.skip(size));
-        SkASSERT(size == bytes);
-    }
-
-    if (pictureVersion == PICTURE_VERSION_ICS) {
-        int shapeCount = readTagSize(buffer, PICT_SHAPE_TAG);
-        for (i = 0; i < shapeCount; i++) {
-            buffer.readFlattenable();
-        }
-    }
+    *isValid = true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -499,218 +620,316 @@
 };
 #endif
 
+#ifdef SK_DEVELOPER
+size_t SkPicturePlayback::preDraw(size_t offset, int type) {
+    return 0;
+}
+
+void SkPicturePlayback::postDraw(size_t offset) {
+}
+#endif
+
 void SkPicturePlayback::draw(SkCanvas& canvas) {
 #ifdef ENABLE_TIME_DRAW
     SkAutoTime  at("SkPicture::draw", 50);
 #endif
 
 #ifdef SPEW_CLIP_SKIPPING
-    SkipClipRec skipRect, skipRegion, skipPath;
+    SkipClipRec skipRect, skipRRect, skipRegion, skipPath;
 #endif
 
 #ifdef SK_BUILD_FOR_ANDROID
     SkAutoMutexAcquire autoMutex(fDrawMutex);
 #endif
 
-    TextContainer text;
-    fReader.rewind();
+    // kDrawComplete will be the signal that we have reached the end of
+    // the command stream
+    static const uint32_t kDrawComplete = SK_MaxU32;
 
-    while (!fReader.eof()) {
-        switch (fReader.readInt()) {
+    SkReader32 reader(fOpData->bytes(), fOpData->size());
+    TextContainer text;
+    SkTDArray<void*> results;
+
+    if (NULL != fStateTree && NULL != fBoundingHierarchy) {
+        SkRect clipBounds;
+        if (canvas.getClipBounds(&clipBounds)) {
+            SkIRect query;
+            clipBounds.roundOut(&query);
+            fBoundingHierarchy->search(query, &results);
+            if (results.count() == 0) {
+                return;
+            }
+            SkTQSort<SkPictureStateTree::Draw>(
+                reinterpret_cast<SkPictureStateTree::Draw**>(results.begin()),
+                reinterpret_cast<SkPictureStateTree::Draw**>(results.end()-1));
+        }
+    }
+
+    SkPictureStateTree::Iterator it = (NULL == fStateTree) ?
+        SkPictureStateTree::Iterator() :
+        fStateTree->getIterator(results, &canvas);
+
+    if (it.isValid()) {
+        uint32_t skipTo = it.draw();
+        if (kDrawComplete == skipTo) {
+            return;
+        }
+        reader.setOffset(skipTo);
+    }
+
+    // Record this, so we can concat w/ it if we encounter a setMatrix()
+    SkMatrix initialMatrix = canvas.getTotalMatrix();
+
+#ifdef SK_BUILD_FOR_ANDROID
+    fAbortCurrentPlayback = false;
+#endif
+
+    while (!reader.eof()) {
+#ifdef SK_BUILD_FOR_ANDROID
+        if (fAbortCurrentPlayback) {
+            return;
+        }
+#endif
+
+#ifdef SK_DEVELOPER
+        size_t curOffset = reader.offset();
+#endif
+        int type = reader.readInt();
+#ifdef SK_DEVELOPER
+        size_t skipTo = this->preDraw(curOffset, type);
+        if (0 != skipTo) {
+            if (kDrawComplete == skipTo) {
+                break;
+            }
+            reader.setOffset(skipTo);
+            continue;
+        }
+#endif
+        switch (type) {
             case CLIP_PATH: {
-                const SkPath& path = getPath();
-                uint32_t packed = getInt();
+                const SkPath& path = getPath(reader);
+                uint32_t packed = reader.readInt();
                 SkRegion::Op op = ClipParams_unpackRegionOp(packed);
                 bool doAA = ClipParams_unpackDoAA(packed);
-                size_t offsetToRestore = getInt();
+                size_t offsetToRestore = reader.readInt();
+                SkASSERT(!offsetToRestore || \
+                    offsetToRestore >= reader.offset());
                 if (!canvas.clipPath(path, op, doAA) && offsetToRestore) {
 #ifdef SPEW_CLIP_SKIPPING
-                    skipPath.recordSkip(offsetToRestore - fReader.offset());
+                    skipPath.recordSkip(offsetToRestore - reader.offset());
 #endif
-                    fReader.setOffset(offsetToRestore);
+                    reader.setOffset(offsetToRestore);
                 }
             } break;
             case CLIP_REGION: {
-                const SkRegion& region = getRegion();
-                uint32_t packed = getInt();
+                const SkRegion& region = getRegion(reader);
+                uint32_t packed = reader.readInt();
                 SkRegion::Op op = ClipParams_unpackRegionOp(packed);
-                size_t offsetToRestore = getInt();
+                size_t offsetToRestore = reader.readInt();
+                SkASSERT(!offsetToRestore || \
+                    offsetToRestore >= reader.offset());
                 if (!canvas.clipRegion(region, op) && offsetToRestore) {
 #ifdef SPEW_CLIP_SKIPPING
-                    skipRegion.recordSkip(offsetToRestore - fReader.offset());
+                    skipRegion.recordSkip(offsetToRestore - reader.offset());
 #endif
-                    fReader.setOffset(offsetToRestore);
+                    reader.setOffset(offsetToRestore);
                 }
             } break;
             case CLIP_RECT: {
-                const SkRect& rect = fReader.skipT<SkRect>();
-                uint32_t packed = getInt();
+                const SkRect& rect = reader.skipT<SkRect>();
+                uint32_t packed = reader.readInt();
                 SkRegion::Op op = ClipParams_unpackRegionOp(packed);
                 bool doAA = ClipParams_unpackDoAA(packed);
-                size_t offsetToRestore = getInt();
+                size_t offsetToRestore = reader.readInt();
+                SkASSERT(!offsetToRestore || \
+                         offsetToRestore >= reader.offset());
                 if (!canvas.clipRect(rect, op, doAA) && offsetToRestore) {
 #ifdef SPEW_CLIP_SKIPPING
-                    skipRect.recordSkip(offsetToRestore - fReader.offset());
+                    skipRect.recordSkip(offsetToRestore - reader.offset());
 #endif
-                    fReader.setOffset(offsetToRestore);
+                    reader.setOffset(offsetToRestore);
+                }
+            } break;
+            case CLIP_RRECT: {
+                SkRRect rrect;
+                reader.readRRect(&rrect);
+                uint32_t packed = reader.readInt();
+                SkRegion::Op op = ClipParams_unpackRegionOp(packed);
+                bool doAA = ClipParams_unpackDoAA(packed);
+                size_t offsetToRestore = reader.readInt();
+                SkASSERT(!offsetToRestore || \
+                         offsetToRestore >= reader.offset());
+                if (!canvas.clipRRect(rrect, op, doAA) && offsetToRestore) {
+#ifdef SPEW_CLIP_SKIPPING
+                    skipRRect.recordSkip(offsetToRestore - reader.offset());
+#endif
+                    reader.setOffset(offsetToRestore);
                 }
             } break;
             case CONCAT:
-                canvas.concat(*getMatrix());
+                canvas.concat(*getMatrix(reader));
                 break;
             case DRAW_BITMAP: {
-                const SkPaint* paint = getPaint();
-                const SkBitmap& bitmap = getBitmap();
-                const SkPoint& loc = fReader.skipT<SkPoint>();
+                const SkPaint* paint = getPaint(reader);
+                const SkBitmap& bitmap = getBitmap(reader);
+                const SkPoint& loc = reader.skipT<SkPoint>();
                 canvas.drawBitmap(bitmap, loc.fX, loc.fY, paint);
             } break;
-            case DRAW_BITMAP_RECT: {
-                const SkPaint* paint = getPaint();
-                const SkBitmap& bitmap = getBitmap();
-                const SkIRect* src = this->getIRectPtr();   // may be null
-                const SkRect& dst = fReader.skipT<SkRect>();     // required
-                canvas.drawBitmapRect(bitmap, src, dst, paint);
+            case DRAW_BITMAP_RECT_TO_RECT: {
+                const SkPaint* paint = getPaint(reader);
+                const SkBitmap& bitmap = getBitmap(reader);
+                const SkRect* src = this->getRectPtr(reader);   // may be null
+                const SkRect& dst = reader.skipT<SkRect>();     // required
+                canvas.drawBitmapRectToRect(bitmap, src, dst, paint);
             } break;
             case DRAW_BITMAP_MATRIX: {
-                const SkPaint* paint = getPaint();
-                const SkBitmap& bitmap = getBitmap();
-                const SkMatrix* matrix = getMatrix();
+                const SkPaint* paint = getPaint(reader);
+                const SkBitmap& bitmap = getBitmap(reader);
+                const SkMatrix* matrix = getMatrix(reader);
                 canvas.drawBitmapMatrix(bitmap, *matrix, paint);
             } break;
             case DRAW_BITMAP_NINE: {
-                const SkPaint* paint = getPaint();
-                const SkBitmap& bitmap = getBitmap();
-                const SkIRect& src = fReader.skipT<SkIRect>();
-                const SkRect& dst = fReader.skipT<SkRect>();
+                const SkPaint* paint = getPaint(reader);
+                const SkBitmap& bitmap = getBitmap(reader);
+                const SkIRect& src = reader.skipT<SkIRect>();
+                const SkRect& dst = reader.skipT<SkRect>();
                 canvas.drawBitmapNine(bitmap, src, dst, paint);
             } break;
             case DRAW_CLEAR:
-                canvas.clear(getInt());
+                canvas.clear(reader.readInt());
                 break;
             case DRAW_DATA: {
-                size_t length = getInt();
-                canvas.drawData(fReader.skip(length), length);
+                size_t length = reader.readInt();
+                canvas.drawData(reader.skip(length), length);
                 // skip handles padding the read out to a multiple of 4
             } break;
+            case DRAW_OVAL: {
+                const SkPaint& paint = *getPaint(reader);
+                canvas.drawOval(reader.skipT<SkRect>(), paint);
+            } break;
             case DRAW_PAINT:
-                canvas.drawPaint(*getPaint());
+                canvas.drawPaint(*getPaint(reader));
                 break;
             case DRAW_PATH: {
-                const SkPaint& paint = *getPaint();
-                canvas.drawPath(getPath(), paint);
+                const SkPaint& paint = *getPaint(reader);
+                canvas.drawPath(getPath(reader), paint);
             } break;
             case DRAW_PICTURE:
-                canvas.drawPicture(getPicture());
+                canvas.drawPicture(getPicture(reader));
                 break;
             case DRAW_POINTS: {
-                const SkPaint& paint = *getPaint();
-                SkCanvas::PointMode mode = (SkCanvas::PointMode)getInt();
-                size_t count = getInt();
-                const SkPoint* pts = (const SkPoint*)fReader.skip(sizeof(SkPoint) * count);
+                const SkPaint& paint = *getPaint(reader);
+                SkCanvas::PointMode mode = (SkCanvas::PointMode)reader.readInt();
+                size_t count = reader.readInt();
+                const SkPoint* pts = (const SkPoint*)reader.skip(sizeof(SkPoint) * count);
                 canvas.drawPoints(mode, count, pts, paint);
             } break;
             case DRAW_POS_TEXT: {
-                const SkPaint& paint = *getPaint();
-                getText(&text);
-                size_t points = getInt();
-                const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
+                const SkPaint& paint = *getPaint(reader);
+                getText(reader, &text);
+                size_t points = reader.readInt();
+                const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint));
                 canvas.drawPosText(text.text(), text.length(), pos, paint);
             } break;
             case DRAW_POS_TEXT_TOP_BOTTOM: {
-                const SkPaint& paint = *getPaint();
-                getText(&text);
-                size_t points = getInt();
-                const SkPoint* pos = (const SkPoint*)fReader.skip(points * sizeof(SkPoint));
-                const SkScalar top = fReader.readScalar();
-                const SkScalar bottom = fReader.readScalar();
-                if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+                const SkPaint& paint = *getPaint(reader);
+                getText(reader, &text);
+                size_t points = reader.readInt();
+                const SkPoint* pos = (const SkPoint*)reader.skip(points * sizeof(SkPoint));
+                const SkScalar top = reader.readScalar();
+                const SkScalar bottom = reader.readScalar();
+                if (!canvas.quickRejectY(top, bottom)) {
                     canvas.drawPosText(text.text(), text.length(), pos, paint);
                 }
             } break;
             case DRAW_POS_TEXT_H: {
-                const SkPaint& paint = *getPaint();
-                getText(&text);
-                size_t xCount = getInt();
-                const SkScalar constY = getScalar();
-                const SkScalar* xpos = (const SkScalar*)fReader.skip(xCount * sizeof(SkScalar));
+                const SkPaint& paint = *getPaint(reader);
+                getText(reader, &text);
+                size_t xCount = reader.readInt();
+                const SkScalar constY = reader.readScalar();
+                const SkScalar* xpos = (const SkScalar*)reader.skip(xCount * sizeof(SkScalar));
                 canvas.drawPosTextH(text.text(), text.length(), xpos, constY,
                                     paint);
             } break;
             case DRAW_POS_TEXT_H_TOP_BOTTOM: {
-                const SkPaint& paint = *getPaint();
-                getText(&text);
-                size_t xCount = getInt();
-                const SkScalar* xpos = (const SkScalar*)fReader.skip((3 + xCount) * sizeof(SkScalar));
+                const SkPaint& paint = *getPaint(reader);
+                getText(reader, &text);
+                size_t xCount = reader.readInt();
+                const SkScalar* xpos = (const SkScalar*)reader.skip((3 + xCount) * sizeof(SkScalar));
                 const SkScalar top = *xpos++;
                 const SkScalar bottom = *xpos++;
                 const SkScalar constY = *xpos++;
-                if (!canvas.quickRejectY(top, bottom, SkCanvas::kAA_EdgeType)) {
+                if (!canvas.quickRejectY(top, bottom)) {
                     canvas.drawPosTextH(text.text(), text.length(), xpos,
                                         constY, paint);
                 }
             } break;
             case DRAW_RECT: {
-                const SkPaint& paint = *getPaint();
-                canvas.drawRect(fReader.skipT<SkRect>(), paint);
+                const SkPaint& paint = *getPaint(reader);
+                canvas.drawRect(reader.skipT<SkRect>(), paint);
+            } break;
+            case DRAW_RRECT: {
+                const SkPaint& paint = *getPaint(reader);
+                SkRRect rrect;
+                canvas.drawRRect(*reader.readRRect(&rrect), paint);
             } break;
             case DRAW_SPRITE: {
-                const SkPaint* paint = getPaint();
-                const SkBitmap& bitmap = getBitmap();
-                int left = getInt();
-                int top = getInt();
+                const SkPaint* paint = getPaint(reader);
+                const SkBitmap& bitmap = getBitmap(reader);
+                int left = reader.readInt();
+                int top = reader.readInt();
                 canvas.drawSprite(bitmap, left, top, paint);
             } break;
             case DRAW_TEXT: {
-                const SkPaint& paint = *getPaint();
-                getText(&text);
-                SkScalar x = getScalar();
-                SkScalar y = getScalar();
+                const SkPaint& paint = *getPaint(reader);
+                getText(reader, &text);
+                SkScalar x = reader.readScalar();
+                SkScalar y = reader.readScalar();
                 canvas.drawText(text.text(), text.length(), x, y, paint);
             } break;
             case DRAW_TEXT_TOP_BOTTOM: {
-                const SkPaint& paint = *getPaint();
-                getText(&text);
-                const SkScalar* ptr = (const SkScalar*)fReader.skip(4 * sizeof(SkScalar));
+                const SkPaint& paint = *getPaint(reader);
+                getText(reader, &text);
+                const SkScalar* ptr = (const SkScalar*)reader.skip(4 * sizeof(SkScalar));
                 // ptr[0] == x
                 // ptr[1] == y
                 // ptr[2] == top
                 // ptr[3] == bottom
-                if (!canvas.quickRejectY(ptr[2], ptr[3],
-                                         SkCanvas::kAA_EdgeType)) {
+                if (!canvas.quickRejectY(ptr[2], ptr[3])) {
                     canvas.drawText(text.text(), text.length(), ptr[0], ptr[1],
                                     paint);
                 }
             } break;
             case DRAW_TEXT_ON_PATH: {
-                const SkPaint& paint = *getPaint();
-                getText(&text);
-                const SkPath& path = getPath();
-                const SkMatrix* matrix = getMatrix();
+                const SkPaint& paint = *getPaint(reader);
+                getText(reader, &text);
+                const SkPath& path = getPath(reader);
+                const SkMatrix* matrix = getMatrix(reader);
                 canvas.drawTextOnPath(text.text(), text.length(), path,
                                       matrix, paint);
             } break;
             case DRAW_VERTICES: {
-                const SkPaint& paint = *getPaint();
-                DrawVertexFlags flags = (DrawVertexFlags)getInt();
-                SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)getInt();
-                int vCount = getInt();
-                const SkPoint* verts = (const SkPoint*)fReader.skip(
+                const SkPaint& paint = *getPaint(reader);
+                DrawVertexFlags flags = (DrawVertexFlags)reader.readInt();
+                SkCanvas::VertexMode vmode = (SkCanvas::VertexMode)reader.readInt();
+                int vCount = reader.readInt();
+                const SkPoint* verts = (const SkPoint*)reader.skip(
                                                     vCount * sizeof(SkPoint));
                 const SkPoint* texs = NULL;
                 const SkColor* colors = NULL;
                 const uint16_t* indices = NULL;
                 int iCount = 0;
                 if (flags & DRAW_VERTICES_HAS_TEXS) {
-                    texs = (const SkPoint*)fReader.skip(
+                    texs = (const SkPoint*)reader.skip(
                                                     vCount * sizeof(SkPoint));
                 }
                 if (flags & DRAW_VERTICES_HAS_COLORS) {
-                    colors = (const SkColor*)fReader.skip(
+                    colors = (const SkColor*)reader.skip(
                                                     vCount * sizeof(SkColor));
                 }
                 if (flags & DRAW_VERTICES_HAS_INDICES) {
-                    iCount = getInt();
-                    indices = (const uint16_t*)fReader.skip(
+                    iCount = reader.readInt();
+                    indices = (const uint16_t*)reader.skip(
                                                     iCount * sizeof(uint16_t));
                 }
                 canvas.drawVertices(vmode, vCount, verts, texs, colors, NULL,
@@ -720,162 +939,64 @@
                 canvas.restore();
                 break;
             case ROTATE:
-                canvas.rotate(getScalar());
+                canvas.rotate(reader.readScalar());
                 break;
             case SAVE:
-                canvas.save((SkCanvas::SaveFlags) getInt());
+                canvas.save((SkCanvas::SaveFlags) reader.readInt());
                 break;
             case SAVE_LAYER: {
-                const SkRect* boundsPtr = getRectPtr();
-                const SkPaint* paint = getPaint();
-                canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) getInt());
+                const SkRect* boundsPtr = getRectPtr(reader);
+                const SkPaint* paint = getPaint(reader);
+                canvas.saveLayer(boundsPtr, paint, (SkCanvas::SaveFlags) reader.readInt());
                 } break;
             case SCALE: {
-                SkScalar sx = getScalar();
-                SkScalar sy = getScalar();
+                SkScalar sx = reader.readScalar();
+                SkScalar sy = reader.readScalar();
                 canvas.scale(sx, sy);
             } break;
-            case SET_MATRIX:
-                canvas.setMatrix(*getMatrix());
-                break;
+            case SET_MATRIX: {
+                SkMatrix matrix;
+                matrix.setConcat(initialMatrix, *getMatrix(reader));
+                canvas.setMatrix(matrix);
+            } break;
             case SKEW: {
-                SkScalar sx = getScalar();
-                SkScalar sy = getScalar();
+                SkScalar sx = reader.readScalar();
+                SkScalar sy = reader.readScalar();
                 canvas.skew(sx, sy);
             } break;
             case TRANSLATE: {
-                SkScalar dx = getScalar();
-                SkScalar dy = getScalar();
+                SkScalar dx = reader.readScalar();
+                SkScalar dy = reader.readScalar();
                 canvas.translate(dx, dy);
             } break;
             default:
                 SkASSERT(0);
         }
+
+#ifdef SK_DEVELOPER
+        this->postDraw(curOffset);
+#endif
+
+        if (it.isValid()) {
+            uint32_t skipTo = it.draw();
+            if (kDrawComplete == skipTo) {
+                break;
+            }
+            reader.setOffset(skipTo);
+        }
     }
 
 #ifdef SPEW_CLIP_SKIPPING
     {
-        size_t size =  skipRect.fSize + skipPath.fSize + skipRegion.fSize;
-        SkDebugf("--- Clip skips %d%% rect:%d path:%d rgn:%d\n",
-             size * 100 / fReader.offset(), skipRect.fCount, skipPath.fCount,
-             skipRegion.fCount);
+        size_t size =  skipRect.fSize + skipRRect.fSize + skipPath.fSize + skipRegion.fSize;
+        SkDebugf("--- Clip skips %d%% rect:%d rrect:%d path:%d rgn:%d\n",
+             size * 100 / reader.offset(), skipRect.fCount, skipRRect.fCount,
+                 skipPath.fCount, skipRegion.fCount);
     }
 #endif
 //    this->dumpSize();
 }
 
-void SkPicturePlayback::abort() {
-    fReader.skip(fReader.size() - fReader.offset());
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if 0
-uint32_t SkPicturePlayback::flatten(void* storage) const {
-    SkWBuffer buffer(storage);
-    buffer.write32(fBitmapCount);
-    int index;
-    for (index = 0; index < fBitmapCount; index++) {
-        const SkBitmap& bitmap = fBitmaps[index];
-        uint32_t size = bitmap.flatten(NULL, true);
-        buffer.write32(size);
-        void* local = buffer.skip(size);
-        bitmap.flatten(local, true);
-    }
-    buffer.write32(fPaintCount);
-    for (index = 0; index < fPaintCount; index++) {
-        SkFlattenableWriteBuffer flatWrite;
-        const SkPaint& paint = fPaints[index];
-        SkFlatPaint::Write(&flatWrite, paint);
-        uint32_t size = flatWrite.pos();
-        buffer.write32(size);
-        void* local = buffer.skip(size);
-        flatWrite.reset(local);
-        SkFlatPaint::Write(&flatWrite, paint);
-    }
-    buffer.write32(fPathCount);
-    for (index = 0; index < fPathCount; index++) {
-        const SkPath& path = fPaths[index];
-        uint32_t size = path.flatten(NULL);
-        buffer.write32(size);
-        void* local = buffer.skip(size);
-        path.flatten(local);
-    }
-
-#if 0
-    buffer.write32(fPictureCount);
-    for (index = 0; index < fPictureCount; index++) {
-        const SkPicture& picture = fPictures[index];
-        uint32_t size = picture.flatten(NULL);
-        buffer.write32(size);
-        void* local = buffer.skip(size);
-        picture.flatten(local);
-    }
-#endif
-
-    buffer.write32(fRegionCount);
-    for (index = 0; index < fRegionCount; index++) {
-        const SkRegion& region = fRegions[index];
-        size_t size = region.computeBufferSize();
-        buffer.write32(size);
-        void* local = buffer.skip(size);
-        region.writeToBuffer(local);
-    }
-    fReader.rewind();
-    size_t length = fReader.size();
-    buffer.write32(length);
-    memcpy(buffer.skip(length), fReader.base(), length);
-    return (uint32_t) buffer.pos();
-}
-
-void SkPicturePlayback::unflatten(const void* storage) {
-    SkRBuffer buffer(storage);
-    int index;
-    fBitmapCount = buffer.readU32();
-    fBitmaps = new SkBitmap[fBitmapCount];
-    for (index = 0; index < fBitmapCount; index++) {
-        uint32_t size = buffer.readU32();
-        const void* local = buffer.skip(size);
-        fBitmaps[index].unflatten(local);
-    }
-    fPaintCount = buffer.readU32();
-    fPaints = new SkPaint[fPaintCount];
-    for (index = 0; index < fPaintCount; index++) {
-        uint32_t size = buffer.readU32();
-        const void* local = buffer.skip(size);
-        SkFlatPaint::Read(local, &fPaints[index]);
-    }
-    fPathCount = buffer.readU32();
-    fPaths = new SkPath[fPathCount];
-    for (index = 0; index < fPathCount; index++) {
-        uint32_t size = buffer.readU32();
-        const void* local = buffer.skip(size);
-        fPaths[index].unflatten(local);
-    }
-
-#if 0
-    fPictureCount = buffer.readU32();
-    fPictures = new SkPicture[fPictureCount];
-    for (index = 0; index < fPictureCount; index++) {
-        uint32_t size = buffer.readU32();
-        const void* local = buffer.skip(size);
-        fPictures[index].unflatten(local);
-    }
-#endif
-
-    fRegionCount = buffer.readU32();
-    fRegions = new SkRegion[fRegionCount];
-    for (index = 0; index < fRegionCount; index++) {
-        uint32_t size = buffer.readU32();
-        const void* local = buffer.skip(size);
-        fRegions[index].readFromBuffer(local);
-    }
-    int32_t length = buffer.readS32();
-    const void* stream = buffer.skip(length);
-    fReader.setMemory(stream, length);
-}
-#endif
-
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifdef SK_DEBUG_SIZE
@@ -885,7 +1006,7 @@
     objects += paths(sizePtr);
     objects += pictures(sizePtr);
     objects += regions(sizePtr);
-    *sizePtr = fReader.size();
+    *sizePtr = fOpData.size();
     return objects;
 }
 
diff --git a/src/core/SkPicturePlayback.h b/src/core/SkPicturePlayback.h
index 70bd0ce..cd3a6a5 100644
--- a/src/core/SkPicturePlayback.h
+++ b/src/core/SkPicturePlayback.h
@@ -12,12 +12,16 @@
 #include "SkReader32.h"
 
 #include "SkBitmap.h"
+#include "SkData.h"
 #include "SkMatrix.h"
+#include "SkOrderedReadBuffer.h"
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkPathHeap.h"
 #include "SkRegion.h"
+#include "SkRRect.h"
 #include "SkPictureFlat.h"
+#include "SkSerializationHelpers.h"
 
 #ifdef SK_BUILD_FOR_ANDROID
 #include "SkThread.h"
@@ -26,28 +30,63 @@
 class SkPictureRecord;
 class SkStream;
 class SkWStream;
+class SkBBoxHierarchy;
+class SkPictureStateTree;
+
+struct SkPictInfo {
+    enum Flags {
+        kCrossProcess_Flag      = 1 << 0,
+        kScalarIsFloat_Flag     = 1 << 1,
+        kPtrIs64Bit_Flag        = 1 << 2,
+    };
+
+    uint32_t    fVersion;
+    uint32_t    fWidth;
+    uint32_t    fHeight;
+    uint32_t    fFlags;
+};
+
+/**
+ * Container for data that is needed to deep copy a SkPicture. The container
+ * enables the data to be generated once and reused for subsequent copies.
+ */
+struct SkPictCopyInfo {
+    SkPictCopyInfo() : initialized(false), controller(1024) {}
+
+    bool initialized;
+    SkChunkFlatController controller;
+    SkTDArray<SkFlatData*> paintData;
+};
 
 class SkPicturePlayback {
 public:
     SkPicturePlayback();
-    SkPicturePlayback(const SkPicturePlayback& src);
-    explicit SkPicturePlayback(const SkPictureRecord& record);
-    explicit SkPicturePlayback(SkStream*, uint32_t pictureVersion = PICTURE_VERSION_JB);
+    SkPicturePlayback(const SkPicturePlayback& src, SkPictCopyInfo* deepCopyInfo = NULL);
+    explicit SkPicturePlayback(const SkPictureRecord& record, bool deepCopy = false);
+    SkPicturePlayback(SkStream*, const SkPictInfo&, bool* isValid,
+                      SkSerializationHelpers::DecodeBitmap decoder);
 
     virtual ~SkPicturePlayback();
 
     void draw(SkCanvas& canvas);
 
-    void serialize(SkWStream*) const;
+    void serialize(SkWStream*, SkSerializationHelpers::EncodeBitmap) const;
 
     void dumpSize() const;
 
+#ifdef SK_BUILD_FOR_ANDROID
     // Can be called in the middle of playback (the draw() call). WIll abort the
     // drawing and return from draw() after the "current" op code is done
-    void abort();
+    void abort() { fAbortCurrentPlayback = true; }
+#endif
+
+protected:
+#ifdef SK_DEVELOPER
+    virtual size_t preDraw(size_t offset, int type);
+    virtual void postDraw(size_t offset);
+#endif
 
 private:
-
     class TextContainer {
     public:
         size_t length() { return fByteLength; }
@@ -56,70 +95,65 @@
         const char* fText;
     };
 
-    const SkBitmap& getBitmap() {
-        int index = getInt();
-        SkASSERT(index > 0);
-        return fBitmaps[index - 1];
+    const SkBitmap& getBitmap(SkReader32& reader) {
+        const int index = reader.readInt();
+        if (SkBitmapHeap::INVALID_SLOT == index) {
+            SkDebugf("An invalid bitmap was recorded!\n");
+            return fBadBitmap;
+        }
+        return (*fBitmaps)[index];
     }
 
-    int getIndex() { return fReader.readInt(); }
-    int getInt() { return fReader.readInt(); }
-
-    const SkMatrix* getMatrix() {
-        int index = getInt();
+    const SkMatrix* getMatrix(SkReader32& reader) {
+        int index = reader.readInt();
         if (index == 0) {
             return NULL;
         }
-        SkASSERT(index > 0 && index <= fMatrixCount);
-        return &fMatrices[index - 1];
+        return &(*fMatrices)[index - 1];
     }
 
-    const SkPath& getPath() {
-        return (*fPathHeap)[getInt() - 1];
+    const SkPath& getPath(SkReader32& reader) {
+        return (*fPathHeap)[reader.readInt() - 1];
     }
 
-    SkPicture& getPicture() {
-        int index = getInt();
+    SkPicture& getPicture(SkReader32& reader) {
+        int index = reader.readInt();
         SkASSERT(index > 0 && index <= fPictureCount);
         return *fPictureRefs[index - 1];
     }
 
-    const SkPaint* getPaint() {
-        int index = getInt();
+    const SkPaint* getPaint(SkReader32& reader) {
+        int index = reader.readInt();
         if (index == 0) {
             return NULL;
         }
-        SkASSERT(index > 0 && index <= fPaintCount);
-        return &fPaints[index - 1];
+        return &(*fPaints)[index - 1];
     }
 
-    const SkRect* getRectPtr() {
-        if (fReader.readBool()) {
-            return &fReader.skipT<SkRect>();
+    const SkRect* getRectPtr(SkReader32& reader) {
+        if (reader.readBool()) {
+            return &reader.skipT<SkRect>();
         } else {
             return NULL;
         }
     }
 
-    const SkIRect* getIRectPtr() {
-        if (fReader.readBool()) {
-            return &fReader.skipT<SkIRect>();
+    const SkIRect* getIRectPtr(SkReader32& reader) {
+        if (reader.readBool()) {
+            return &reader.skipT<SkIRect>();
         } else {
             return NULL;
         }
     }
 
-    const SkRegion& getRegion() {
-        int index = getInt();
-        SkASSERT(index > 0);
-        return fRegions[index - 1];
+    const SkRegion& getRegion(SkReader32& reader) {
+        int index = reader.readInt();
+        return (*fRegions)[index - 1];
     }
 
-    SkScalar getScalar() { return fReader.readScalar(); }
-
-    void getText(TextContainer* text) {
-        size_t length = text->fByteLength = getInt();
-        text->fText = (const char*)fReader.skip(length);
+    void getText(SkReader32& reader, TextContainer* text) {
+        size_t length = text->fByteLength = reader.readInt();
+        text->fText = (const char*)reader.skip(length);
     }
 
     void init();
@@ -156,26 +190,38 @@
     void dump() const;
 #endif
 
+private:    // these help us with reading/writing
+    bool parseStreamTag(SkStream*, const SkPictInfo&, uint32_t tag, size_t size,
+                        SkSerializationHelpers::DecodeBitmap decoder);
+    bool parseBufferTag(SkOrderedReadBuffer&, uint32_t tag, size_t size);
+    void flattenToBuffer(SkOrderedWriteBuffer&) const;
+
 private:
-    SkPathHeap* fPathHeap;  // reference counted
-    SkBitmap* fBitmaps;
-    int fBitmapCount;
-    SkMatrix* fMatrices;
-    int fMatrixCount;
-    SkPaint* fPaints;
-    int fPaintCount;
-    SkRegion* fRegions;
-    int fRegionCount;
-    mutable SkFlattenableReadBuffer fReader;
+    // Only used by getBitmap() if the passed in index is SkBitmapHeap::INVALID_SLOT. This empty
+    // bitmap allows playback to draw nothing and move on.
+    SkBitmap fBadBitmap;
+
+    SkAutoTUnref<SkBitmapHeap> fBitmapHeap;
+    SkAutoTUnref<SkPathHeap> fPathHeap;
+
+    SkTRefArray<SkBitmap>* fBitmaps;
+    SkTRefArray<SkMatrix>* fMatrices;
+    SkTRefArray<SkPaint>* fPaints;
+    SkTRefArray<SkRegion>* fRegions;
+
+    SkData* fOpData;    // opcodes and parameters
 
     SkPicture** fPictureRefs;
     int fPictureCount;
 
-    SkRefCntPlayback fRCPlayback;
+    SkBBoxHierarchy* fBoundingHierarchy;
+    SkPictureStateTree* fStateTree;
+
     SkTypefacePlayback fTFPlayback;
-    SkFactoryPlayback*   fFactoryPlayback;
+    SkFactoryPlayback* fFactoryPlayback;
 #ifdef SK_BUILD_FOR_ANDROID
     SkMutex fDrawMutex;
+    bool fAbortCurrentPlayback;
 #endif
 };
 
diff --git a/src/core/SkPictureRecord.cpp b/src/core/SkPictureRecord.cpp
index fdeec3f..6fea77b 100644
--- a/src/core/SkPictureRecord.cpp
+++ b/src/core/SkPictureRecord.cpp
@@ -7,50 +7,83 @@
  */
 #include "SkPictureRecord.h"
 #include "SkTSearch.h"
+#include "SkPixelRef.h"
+#include "SkRRect.h"
+#include "SkBBoxHierarchy.h"
+#include "SkPictureStateTree.h"
 
 #define MIN_WRITER_SIZE 16384
 #define HEAP_BLOCK_SIZE 4096
 
-SkPictureRecord::SkPictureRecord(uint32_t flags) :
-        fHeap(HEAP_BLOCK_SIZE), fWriter(MIN_WRITER_SIZE), fRecordFlags(flags) {
-    fBitmapIndex = fMatrixIndex = fPaintIndex = fRegionIndex = 1;
+enum {
+    // just need a value that save or getSaveCount would never return
+    kNoInitialSave = -1,
+};
+
+SkPictureRecord::SkPictureRecord(uint32_t flags, SkDevice* device) :
+        INHERITED(device),
+        fBoundingHierarchy(NULL),
+        fStateTree(NULL),
+        fFlattenableHeap(HEAP_BLOCK_SIZE),
+        fMatrices(&fFlattenableHeap),
+        fPaints(&fFlattenableHeap),
+        fRegions(&fFlattenableHeap),
+        fWriter(MIN_WRITER_SIZE),
+        fRecordFlags(flags) {
 #ifdef SK_DEBUG_SIZE
     fPointBytes = fRectBytes = fTextBytes = 0;
     fPointWrites = fRectWrites = fTextWrites = 0;
 #endif
 
     fRestoreOffsetStack.setReserve(32);
-    fRestoreOffsetStack.push(0);
 
+    fBitmapHeap = SkNEW(SkBitmapHeap);
+    fFlattenableHeap.setBitmapStorage(fBitmapHeap);
     fPathHeap = NULL;   // lazy allocate
     fFirstSavedLayerIndex = kNoSavedLayerIndex;
+
+    fInitialSaveCount = kNoInitialSave;
 }
 
 SkPictureRecord::~SkPictureRecord() {
-    reset();
+    SkSafeUnref(fBitmapHeap);
+    SkSafeUnref(fPathHeap);
+    SkSafeUnref(fBoundingHierarchy);
+    SkSafeUnref(fStateTree);
+    fFlattenableHeap.setBitmapStorage(NULL);
+    fPictureRefs.unrefAll();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+SkDevice* SkPictureRecord::setDevice(SkDevice* device) {
+    SkASSERT(!"eeek, don't try to change the device on a recording canvas");
+    return this->INHERITED::setDevice(device);
+}
+
 int SkPictureRecord::save(SaveFlags flags) {
+    // record the offset to us, making it non-positive to distinguish a save
+    // from a clip entry.
+    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
+
     addDraw(SAVE);
     addInt(flags);
 
-    fRestoreOffsetStack.push(0);
-
     validate();
     return this->INHERITED::save(flags);
 }
 
 int SkPictureRecord::saveLayer(const SkRect* bounds, const SkPaint* paint,
                                SaveFlags flags) {
+    // record the offset to us, making it non-positive to distinguish a save
+    // from a clip entry.
+    fRestoreOffsetStack.push(-(int32_t)fWriter.size());
+
     addDraw(SAVE_LAYER);
     addRectPtr(bounds);
     addPaintPtr(paint);
     addInt(flags);
 
-    fRestoreOffsetStack.push(0);
-
     if (kNoSavedLayerIndex == fFirstSavedLayerIndex) {
         fFirstSavedLayerIndex = fRestoreOffsetStack.count();
     }
@@ -71,28 +104,135 @@
     return fFirstSavedLayerIndex != kNoSavedLayerIndex;
 }
 
+// Return the size of the specified drawType's recorded block, or 0 if this verb
+// is variable sized, and therefore not known.
+static inline uint32_t getSkipableSize(unsigned drawType) {
+    static const uint8_t gSizes[LAST_DRAWTYPE_ENUM + 1] = {
+        0,  // UNUSED,
+        4,  // CLIP_PATH,
+        4,  // CLIP_REGION,
+        7,  // CLIP_RECT,
+        15, // CLIP_RRECT,
+        2,  // CONCAT,
+        0,  // DRAW_BITMAP,
+        0,  // DRAW_BITMAP_MATRIX,
+        0,  // DRAW_BITMAP_NINE,
+        0,  // DRAW_BITMAP_RECT,
+        0,  // DRAW_CLEAR,
+        0,  // DRAW_DATA,
+        0,  // DRAW_OVAL,
+        0,  // DRAW_PAINT,
+        0,  // DRAW_PATH,
+        0,  // DRAW_PICTURE,
+        0,  // DRAW_POINTS,
+        0,  // DRAW_POS_TEXT,
+        0,  // DRAW_POS_TEXT_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT
+        0,  // DRAW_POS_TEXT_H,
+        0,  // DRAW_POS_TEXT_H_TOP_BOTTOM, // fast variant of DRAW_POS_TEXT_H
+        0,  // DRAW_RECT,
+        0,  // DRAW_RRECT,
+        0,  // DRAW_SPRITE,
+        0,  // DRAW_TEXT,
+        0,  // DRAW_TEXT_ON_PATH,
+        0,  // DRAW_TEXT_TOP_BOTTOM,   // fast variant of DRAW_TEXT
+        0,  // DRAW_VERTICES,
+        0,  // RESTORE,
+        2,  // ROTATE,
+        2,  // SAVE,
+        0,  // SAVE_LAYER,
+        3,  // SCALE,
+        2,  // SET_MATRIX,
+        3,  // SKEW,
+        3,  // TRANSLATE,
+    };
+
+    SkASSERT(sizeof(gSizes) == LAST_DRAWTYPE_ENUM + 1);
+    SkASSERT((unsigned)drawType <= (unsigned)LAST_DRAWTYPE_ENUM);
+    return gSizes[drawType] * sizeof(uint32_t);
+}
+
+#ifdef TRACK_COLLAPSE_STATS
+    static int gCollapseCount, gCollapseCalls;
+#endif
+
+/*
+ *  Restore has just been called (but not recoreded), so look back at the
+ *  matching save(), and see if we can eliminate the pair of them, due to no
+ *  intervening matrix/clip calls.
+ *
+ *  If so, update the writer and return true, in which case we won't even record
+ *  the restore() call. If we still need the restore(), return false.
+ */
+static bool collapseSaveClipRestore(SkWriter32* writer, int32_t offset) {
+#ifdef TRACK_COLLAPSE_STATS
+    gCollapseCalls += 1;
+#endif
+
+    int32_t restoreOffset = (int32_t)writer->size();
+
+    // back up to the save block
+    while (offset > 0) {
+        offset = *writer->peek32(offset);
+    }
+
+    // now offset points to a save
+    offset = -offset;
+    if (SAVE_LAYER == *writer->peek32(offset)) {
+        // not ready to cull these out yet (mrr)
+        return false;
+    }
+    SkASSERT(SAVE == *writer->peek32(offset));
+
+    // Walk forward until we get back to either a draw-verb (abort) or we hit
+    // our restore (success).
+    int32_t saveOffset = offset;
+
+    offset += getSkipableSize(SAVE);
+    while (offset < restoreOffset) {
+        uint32_t* block = writer->peek32(offset);
+        uint32_t op = *block;
+        uint32_t opSize = getSkipableSize(op);
+        if (0 == opSize) {
+            // drawing verb, abort
+            return false;
+        }
+        offset += opSize;
+    }
+
+#ifdef TRACK_COLLAPSE_STATS
+    gCollapseCount += 1;
+    SkDebugf("Collapse [%d out of %d] %g%spn", gCollapseCount, gCollapseCalls,
+             (double)gCollapseCount / gCollapseCalls, "%");
+#endif
+
+    writer->rewindToOffset(saveOffset);
+    return true;
+}
+
 void SkPictureRecord::restore() {
+    // FIXME: SkDeferredCanvas needs to be refactored to respect
+    // save/restore balancing so that the following test can be
+    // turned on permanently.
+#if 0
+    SkASSERT(fRestoreOffsetStack.count() > 1);
+#endif
+
     // check for underflow
     if (fRestoreOffsetStack.count() == 0) {
         return;
     }
 
-    // patch up the clip offsets
-    uint32_t restoreOffset = (uint32_t)fWriter.size();
-    uint32_t offset = fRestoreOffsetStack.top();
-    while (offset) {
-        uint32_t* peek = fWriter.peek32(offset);
-        offset = *peek;
-        *peek = restoreOffset;
-    }
-
     if (fRestoreOffsetStack.count() == fFirstSavedLayerIndex) {
         fFirstSavedLayerIndex = kNoSavedLayerIndex;
     }
 
+    if (!collapseSaveClipRestore(&fWriter, fRestoreOffsetStack.top())) {
+        fillRestoreOffsetPlaceholdersForCurrentStackLevel((uint32_t)fWriter.size());
+        this->addDraw(RESTORE);
+    }
+
     fRestoreOffsetStack.pop();
 
-    addDraw(RESTORE);
     validate();
     return this->INHERITED::restore();
 }
@@ -160,21 +300,53 @@
     }
 }
 
-void SkPictureRecord::recordOffsetForRestore(SkRegion::Op op) {
+void SkPictureRecord::fillRestoreOffsetPlaceholdersForCurrentStackLevel(
+    uint32_t restoreOffset) {
+    int32_t offset = fRestoreOffsetStack.top();
+    while (offset > 0) {
+        uint32_t* peek = fWriter.peek32(offset);
+        offset = *peek;
+        *peek = restoreOffset;
+    }
+
+#ifdef SK_DEBUG
+    // assert that the final offset value points to a save verb
+    uint32_t drawOp = *fWriter.peek32(-offset);
+    SkASSERT(SAVE == drawOp || SAVE_LAYER == drawOp);
+#endif
+}
+
+void SkPictureRecord::beginRecording() {
+    // we have to call this *after* our constructor, to ensure that it gets
+    // recorded. This is balanced by restoreToCount() call from endRecording,
+    // which in-turn calls our overridden restore(), so those get recorded too.
+    fInitialSaveCount = this->save(kMatrixClip_SaveFlag);
+}
+
+void SkPictureRecord::endRecording() {
+    SkASSERT(kNoInitialSave != fInitialSaveCount);
+    this->restoreToCount(fInitialSaveCount);
+}
+
+void SkPictureRecord::recordRestoreOffsetPlaceholder(SkRegion::Op op) {
+    if (fRestoreOffsetStack.isEmpty()) {
+        return;
+    }
+
     if (regionOpExpands(op)) {
         // Run back through any previous clip ops, and mark their offset to
         // be 0, disabling their ability to trigger a jump-to-restore, otherwise
         // they could hide this clips ability to expand the clip (i.e. go from
         // empty to non-empty).
-        uint32_t offset = fRestoreOffsetStack.top();
-        while (offset) {
-            uint32_t* peek = fWriter.peek32(offset);
-            offset = *peek;
-            *peek = 0;
-        }
+        fillRestoreOffsetPlaceholdersForCurrentStackLevel(0);
     }
 
     size_t offset = fWriter.size();
+    // The RestoreOffset field is initially filled with a placeholder
+    // value that points to the offset of the previous RestoreOffset
+    // in the current stack level, thus forming a linked list so that
+    // the restore offsets can be filled in when the corresponding
+    // restore command is recorded.
     addInt(fRestoreOffsetStack.top());
     fRestoreOffsetStack.top() = offset;
 }
@@ -183,19 +355,42 @@
     addDraw(CLIP_RECT);
     addRect(rect);
     addInt(ClipParams_pack(op, doAA));
-
-    this->recordOffsetForRestore(op);
+    recordRestoreOffsetPlaceholder(op);
 
     validate();
     return this->INHERITED::clipRect(rect, op, doAA);
 }
 
+bool SkPictureRecord::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    if (rrect.isRect()) {
+        return this->SkPictureRecord::clipRect(rrect.getBounds(), op, doAA);
+    }
+
+    addDraw(CLIP_RRECT);
+    addRRect(rrect);
+    addInt(ClipParams_pack(op, doAA));
+    recordRestoreOffsetPlaceholder(op);
+
+    validate();
+
+    if (fRecordFlags & SkPicture::kUsePathBoundsForClip_RecordingFlag) {
+        return this->INHERITED::clipRect(rrect.getBounds(), op, doAA);
+    } else {
+        return this->INHERITED::clipRRect(rrect, op, doAA);
+    }
+}
+
 bool SkPictureRecord::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
+
+    SkRect r;
+    if (!path.isInverseFillType() && path.isRect(&r)) {
+        return this->clipRect(r, op, doAA);
+    }
+
     addDraw(CLIP_PATH);
     addPath(path);
     addInt(ClipParams_pack(op, doAA));
-
-    this->recordOffsetForRestore(op);
+    recordRestoreOffsetPlaceholder(op);
 
     validate();
 
@@ -210,8 +405,7 @@
     addDraw(CLIP_REGION);
     addRegion(region);
     addInt(ClipParams_pack(op, false));
-
-    this->recordOffsetForRestore(op);
+    recordRestoreOffsetPlaceholder(op);
 
     validate();
     return this->INHERITED::clipRegion(region, op);
@@ -239,6 +433,13 @@
     validate();
 }
 
+void SkPictureRecord::drawOval(const SkRect& oval, const SkPaint& paint) {
+    addDraw(DRAW_OVAL);
+    addPaint(paint);
+    addRect(oval);
+    validate();
+}
+
 void SkPictureRecord::drawRect(const SkRect& rect, const SkPaint& paint) {
     addDraw(DRAW_RECT);
     addPaint(paint);
@@ -246,6 +447,23 @@
     validate();
 }
 
+void SkPictureRecord::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    if (rrect.isRect()) {
+        addDraw(DRAW_RECT);
+        addPaint(paint);
+        addRect(rrect.getBounds());
+    } else if (rrect.isOval()) {
+        addDraw(DRAW_OVAL);
+        addPaint(paint);
+        addRect(rrect.getBounds());
+    } else {
+        addDraw(DRAW_RRECT);
+        addPaint(paint);
+        addRRect(rrect);
+    }
+    validate();
+}
+
 void SkPictureRecord::drawPath(const SkPath& path, const SkPaint& paint) {
     addDraw(DRAW_PATH);
     addPaint(paint);
@@ -263,12 +481,12 @@
     validate();
 }
 
-void SkPictureRecord::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+void SkPictureRecord::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                             const SkRect& dst, const SkPaint* paint) {
-    addDraw(DRAW_BITMAP_RECT);
+    addDraw(DRAW_BITMAP_RECT_TO_RECT);
     addPaintPtr(paint);
     addBitmap(bitmap);
-    addIRectPtr(src);  // may be null
+    addRectPtr(src);  // may be null
     addRect(dst);
     validate();
 }
@@ -302,19 +520,29 @@
     validate();
 }
 
-void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint,
-                                              SkScalar minY, SkScalar maxY) {
+// Return fontmetrics.fTop,fBottom in topbot[0,1], after they have been
+// tweaked by paint.computeFastBounds().
+//
+static void computeFontMetricsTopBottom(const SkPaint& paint, SkScalar topbot[2]) {
     SkPaint::FontMetrics metrics;
     paint.getFontMetrics(&metrics);
     SkRect bounds;
     // construct a rect so we can see any adjustments from the paint.
     // we use 0,1 for left,right, just so the rect isn't empty
-    bounds.set(0, metrics.fTop + minY,
-               SK_Scalar1, metrics.fBottom + maxY);
+    bounds.set(0, metrics.fTop, SK_Scalar1, metrics.fBottom);
     (void)paint.computeFastBounds(bounds, &bounds);
-    // now record the top and bottom
-    addScalar(bounds.fTop);
-    addScalar(bounds.fBottom);
+    topbot[0] = bounds.fTop;
+    topbot[1] = bounds.fBottom;
+}
+
+void SkPictureRecord::addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData& flat,
+                                              SkScalar minY, SkScalar maxY) {
+    if (!flat.isTopBotWritten()) {
+        computeFontMetricsTopBottom(paint, flat.writableTopBot());
+        SkASSERT(flat.isTopBotWritten());
+    }
+    addScalar(flat.topBot()[0] + minY);
+    addScalar(flat.topBot()[1] + maxY);
 }
 
 void SkPictureRecord::drawText(const void* text, size_t byteLength, SkScalar x,
@@ -322,12 +550,13 @@
     bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
 
     addDraw(fast ? DRAW_TEXT_TOP_BOTTOM : DRAW_TEXT);
-    addPaint(paint);
+    const SkFlatData* flatPaintData = addPaint(paint);
+    SkASSERT(flatPaintData);
     addText(text, byteLength);
     addScalar(x);
     addScalar(y);
     if (fast) {
-        addFontMetricsTopBottom(paint, y, y);
+        addFontMetricsTopBottom(paint, *flatPaintData, y, y);
     }
     validate();
 }
@@ -368,7 +597,8 @@
     } else {
         addDraw(DRAW_POS_TEXT);
     }
-    addPaint(paint);
+    const SkFlatData* flatPaintData = addPaint(paint);
+    SkASSERT(flatPaintData);
     addText(text, byteLength);
     addInt(points);
 
@@ -377,7 +607,7 @@
 #endif
     if (canUseDrawH) {
         if (fast) {
-            addFontMetricsTopBottom(paint, pos[0].fY, pos[0].fY);
+            addFontMetricsTopBottom(paint, *flatPaintData, pos[0].fY, pos[0].fY);
         }
         addScalar(pos[0].fY);
         SkScalar* xptr = (SkScalar*)fWriter.reserve(points * sizeof(SkScalar));
@@ -387,7 +617,7 @@
     else {
         fWriter.writeMul4(pos, points * sizeof(SkPoint));
         if (fastBounds) {
-            addFontMetricsTopBottom(paint, minY, maxY);
+            addFontMetricsTopBottom(paint, *flatPaintData, minY, maxY);
         }
     }
 #ifdef SK_DEBUG_SIZE
@@ -407,7 +637,8 @@
     bool fast = !paint.isVerticalText() && paint.canComputeFastBounds();
 
     addDraw(fast ? DRAW_POS_TEXT_H_TOP_BOTTOM : DRAW_POS_TEXT_H);
-    addPaint(paint);
+    const SkFlatData* flatPaintData = addPaint(paint);
+    SkASSERT(flatPaintData);
     addText(text, byteLength);
     addInt(points);
 
@@ -415,7 +646,7 @@
     size_t start = fWriter.size();
 #endif
     if (fast) {
-        addFontMetricsTopBottom(paint, constY, constY);
+        addFontMetricsTopBottom(paint, *flatPaintData, constY, constY);
     }
     addScalar(constY);
     fWriter.writeMul4(xpos, points * sizeof(SkScalar));
@@ -485,27 +716,13 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkPictureRecord::reset() {
-    SkSafeUnref(fPathHeap);
-    fPathHeap = NULL;
-
-    fBitmaps.reset();
-    fMatrices.reset();
-    fPaints.reset();
-    fPictureRefs.unrefAll();
-    fRegions.reset();
-    fWriter.reset();
-    fHeap.reset();
-
-    fRestoreOffsetStack.setCount(1);
-    fRestoreOffsetStack.top() = 0;
-
-    fRCSet.reset();
-    fTFSet.reset();
-}
-
 void SkPictureRecord::addBitmap(const SkBitmap& bitmap) {
-    addInt(find(fBitmaps, bitmap));
+    const int index = fBitmapHeap->insert(bitmap);
+    // In debug builds, a bad return value from insert() will crash, allowing for debugging. In
+    // release builds, the invalid value will be recorded so that the reader will know that there
+    // was a problem.
+    SkASSERT(index != SkBitmapHeap::INVALID_SLOT);
+    addInt(index);
 }
 
 void SkPictureRecord::addMatrix(const SkMatrix& matrix) {
@@ -513,15 +730,14 @@
 }
 
 void SkPictureRecord::addMatrixPtr(const SkMatrix* matrix) {
-    addInt(find(fMatrices, matrix));
+    this->addInt(matrix ? fMatrices.find(*matrix) : 0);
 }
 
-void SkPictureRecord::addPaint(const SkPaint& paint) {
-    addPaintPtr(&paint);
-}
-
-void SkPictureRecord::addPaintPtr(const SkPaint* paint) {
-    addInt(find(fPaints, paint));
+const SkFlatData* SkPictureRecord::addPaintPtr(const SkPaint* paint) {
+    const SkFlatData* data = paint ? fPaints.findAndReturnFlat(*paint) : NULL;
+    int index = data ? data->index() : 0;
+    this->addInt(index);
+    return data;
 }
 
 void SkPictureRecord::addPath(const SkPath& path) {
@@ -588,8 +804,12 @@
     }
 }
 
+void SkPictureRecord::addRRect(const SkRRect& rrect) {
+    fWriter.writeRRect(rrect);
+}
+
 void SkPictureRecord::addRegion(const SkRegion& region) {
-    addInt(find(fRegions, region));
+    addInt(fRegions.find(region));
 }
 
 void SkPictureRecord::addText(const void* text, size_t byteLength) {
@@ -606,85 +826,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-int SkPictureRecord::find(SkTDArray<const SkFlatBitmap* >& bitmaps, const SkBitmap& bitmap) {
-    SkFlatBitmap* flat = SkFlatBitmap::Flatten(&fHeap, bitmap, fBitmapIndex,
-                                               &fRCSet);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) bitmaps.begin(),
-        bitmaps.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return bitmaps[index]->index();
-    }
-    index = ~index;
-    *bitmaps.insert(index) = flat;
-    return fBitmapIndex++;
-}
-
-int SkPictureRecord::find(SkTDArray<const SkFlatMatrix* >& matrices, const SkMatrix* matrix) {
-    if (matrix == NULL)
-        return 0;
-    SkFlatMatrix* flat = SkFlatMatrix::Flatten(&fHeap, *matrix, fMatrixIndex);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) matrices.begin(),
-        matrices.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return matrices[index]->index();
-    }
-    index = ~index;
-    *matrices.insert(index) = flat;
-    return fMatrixIndex++;
-}
-
-int SkPictureRecord::find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint) {
-    if (paint == NULL) {
-        return 0;
-    }
-
-    SkFlatPaint* flat = SkFlatPaint::Flatten(&fHeap, *paint, fPaintIndex,
-                                             &fRCSet, &fTFSet);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) paints.begin(),
-        paints.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return paints[index]->index();
-    }
-
-    index = ~index;
-    *paints.insert(index) = flat;
-    return fPaintIndex++;
-}
-
-int SkPictureRecord::find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region) {
-    SkFlatRegion* flat = SkFlatRegion::Flatten(&fHeap, region, fRegionIndex);
-    int index = SkTSearch<SkFlatData>((const SkFlatData**) regions.begin(),
-        regions.count(), (SkFlatData*) flat, sizeof(flat), &SkFlatData::Compare);
-    if (index >= 0) {
-        (void)fHeap.unalloc(flat);
-        return regions[index]->index();
-    }
-    index = ~index;
-    *regions.insert(index) = flat;
-    return fRegionIndex++;
-}
-
-#ifdef SK_DEBUG_DUMP
-void SkPictureRecord::dumpMatrices() {
-    int count = fMatrices.count();
-    SkMatrix defaultMatrix;
-    defaultMatrix.reset();
-    for (int index = 0; index < count; index++) {
-        const SkFlatMatrix* flatMatrix = fMatrices[index];
-        flatMatrix->dump();
-    }
-}
-
-void SkPictureRecord::dumpPaints() {
-    int count = fPaints.count();
-    for (int index = 0; index < count; index++)
-        fPaints[index]->dump();
-}
-#endif
-
 #ifdef SK_DEBUG_SIZE
 size_t SkPictureRecord::size() const {
     size_t result = 0;
@@ -812,4 +953,3 @@
     }
 }
 #endif
-
diff --git a/src/core/SkPictureRecord.h b/src/core/SkPictureRecord.h
index dfddffe..84fbe85 100644
--- a/src/core/SkPictureRecord.h
+++ b/src/core/SkPictureRecord.h
@@ -16,11 +16,16 @@
 #include "SkTemplates.h"
 #include "SkWriter32.h"
 
+class SkPictureStateTree;
+class SkBBoxHierarchy;
+
 class SkPictureRecord : public SkCanvas {
 public:
-    SkPictureRecord(uint32_t recordFlags);
+    SkPictureRecord(uint32_t recordFlags, SkDevice*);
     virtual ~SkPictureRecord();
 
+    virtual SkDevice* setDevice(SkDevice* device) SK_OVERRIDE;
+
     virtual int save(SaveFlags) SK_OVERRIDE;
     virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags) SK_OVERRIDE;
     virtual void restore() SK_OVERRIDE;
@@ -31,18 +36,21 @@
     virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
     virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
     virtual bool clipRect(const SkRect&, SkRegion::Op, bool) SK_OVERRIDE;
+    virtual bool clipRRect(const SkRRect&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipPath(const SkPath&, SkRegion::Op, bool) SK_OVERRIDE;
     virtual bool clipRegion(const SkRegion& region, SkRegion::Op op) SK_OVERRIDE;
     virtual void clear(SkColor) SK_OVERRIDE;
     virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
                             const SkPaint&) SK_OVERRIDE;
-    virtual void drawRect(const SkRect& rect, const SkPaint&) SK_OVERRIDE;
+    virtual void drawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawRect(const SkRect&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
     virtual void drawPath(const SkPath& path, const SkPaint&) SK_OVERRIDE;
     virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
                             const SkPaint*) SK_OVERRIDE;
-    virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
-                                const SkRect& dst, const SkPaint*) SK_OVERRIDE;
+    virtual void drawBitmapRectToRect(const SkBitmap&, const SkRect* src,
+                                      const SkRect& dst, const SkPaint*) SK_OVERRIDE;
     virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
                                   const SkPaint*) SK_OVERRIDE;
     virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
@@ -67,38 +75,38 @@
     virtual void drawData(const void*, size_t) SK_OVERRIDE;
     virtual bool isDrawingToLayer() const SK_OVERRIDE;
 
-    void addFontMetricsTopBottom(const SkPaint& paint, SkScalar minY, SkScalar maxY);
+    void addFontMetricsTopBottom(const SkPaint& paint, const SkFlatData&,
+                                 SkScalar minY, SkScalar maxY);
 
-    const SkTDArray<const SkFlatBitmap* >& getBitmaps() const {
-        return fBitmaps;
-    }
-    const SkTDArray<const SkFlatMatrix* >& getMatrices() const {
-        return fMatrices;
-    }
-    const SkTDArray<const SkFlatPaint* >& getPaints() const {
-        return fPaints;
-    }
     const SkTDArray<SkPicture* >& getPictureRefs() const {
         return fPictureRefs;
     }
-    const SkTDArray<const SkFlatRegion* >& getRegions() const {
-        return fRegions;
-    }
 
-    void reset();
+    void setFlags(uint32_t recordFlags) {
+        fRecordFlags = recordFlags;
+    }
 
     const SkWriter32& writeStream() const {
         return fWriter;
     }
 
+    void beginRecording();
+    void endRecording();
+
 private:
-    SkTDArray<uint32_t> fRestoreOffsetStack;
+    void recordRestoreOffsetPlaceholder(SkRegion::Op);
+    void fillRestoreOffsetPlaceholdersForCurrentStackLevel(
+        uint32_t restoreOffset);
+
+    SkTDArray<int32_t> fRestoreOffsetStack;
     int fFirstSavedLayerIndex;
     enum {
         kNoSavedLayerIndex = -1
     };
 
     void addDraw(DrawType drawType) {
+        this->predrawNotify();
+
 #ifdef SK_DEBUG_TRACE
         SkDebugf("add %s\n", DrawTypeToString(drawType));
 #endif
@@ -114,8 +122,8 @@
     void addBitmap(const SkBitmap& bitmap);
     void addMatrix(const SkMatrix& matrix);
     void addMatrixPtr(const SkMatrix* matrix);
-    void addPaint(const SkPaint& paint);
-    void addPaintPtr(const SkPaint* paint);
+    const SkFlatData* addPaint(const SkPaint& paint) { return this->addPaintPtr(&paint); }
+    const SkFlatData* addPaintPtr(const SkPaint* paint);
     void addPath(const SkPath& path);
     void addPicture(SkPicture& picture);
     void addPoint(const SkPoint& point);
@@ -124,15 +132,11 @@
     void addRectPtr(const SkRect* rect);
     void addIRect(const SkIRect& rect);
     void addIRectPtr(const SkIRect* rect);
+    void addRRect(const SkRRect&);
     void addRegion(const SkRegion& region);
     void addText(const void* text, size_t byteLength);
 
-    int find(SkTDArray<const SkFlatBitmap* >& bitmaps,
-                   const SkBitmap& bitmap);
-    int find(SkTDArray<const SkFlatMatrix* >& matrices,
-                   const SkMatrix* matrix);
-    int find(SkTDArray<const SkFlatPaint* >& paints, const SkPaint* paint);
-    int find(SkTDArray<const SkFlatRegion* >& regions, const SkRegion& region);
+    int find(const SkBitmap& bitmap);
 
 #ifdef SK_DEBUG_DUMP
 public:
@@ -168,29 +172,31 @@
     void validate() const {}
 #endif
 
+protected:
+
+    // These are set to NULL in our constructor, but may be changed by
+    // subclasses, in which case they will be SkSafeUnref'd in our destructor.
+    SkBBoxHierarchy* fBoundingHierarchy;
+    SkPictureStateTree* fStateTree;
+
+    // Allocated in the constructor and managed by this class.
+    SkBitmapHeap* fBitmapHeap;
+
 private:
-    SkChunkAlloc fHeap;
-    int fBitmapIndex;
-    SkTDArray<const SkFlatBitmap* > fBitmaps;
-    int fMatrixIndex;
-    SkTDArray<const SkFlatMatrix* > fMatrices;
-    int fPaintIndex;
-    SkTDArray<const SkFlatPaint* > fPaints;
-    int fRegionIndex;
-    SkTDArray<const SkFlatRegion* > fRegions;
+    SkChunkFlatController fFlattenableHeap;
+
+    SkMatrixDictionary fMatrices;
+    SkPaintDictionary fPaints;
+    SkRegionDictionary fRegions;
+
     SkPathHeap* fPathHeap;  // reference counted
     SkWriter32 fWriter;
 
     // we ref each item in these arrays
     SkTDArray<SkPicture*> fPictureRefs;
 
-    SkRefCntSet fRCSet;
-    SkRefCntSet fTFSet;
-
     uint32_t fRecordFlags;
-
-    // helper function to handle save/restore culling offsets
-    void recordOffsetForRestore(SkRegion::Op op);
+    int fInitialSaveCount;
 
     friend class SkPicturePlayback;
     friend class SkPictureTester; // for unit testing
diff --git a/src/core/SkPictureStateTree.cpp b/src/core/SkPictureStateTree.cpp
new file mode 100644
index 0000000..2abed19
--- /dev/null
+++ b/src/core/SkPictureStateTree.cpp
@@ -0,0 +1,177 @@
+
+/*
+ * 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 "SkPictureStateTree.h"
+#include "SkCanvas.h"
+
+SK_DEFINE_INST_COUNT(SkPictureStateTree)
+
+SkPictureStateTree::SkPictureStateTree()
+    : fAlloc(2048)
+    , fRoot(NULL)
+    , fStateStack(sizeof(Draw), 16) {
+    SkMatrix* identity = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
+    identity->reset();
+    fRoot = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
+    fRoot->fParent = NULL;
+    fRoot->fMatrix = identity;
+    fRoot->fFlags = Node::kSave_Flag;
+    fRoot->fOffset = 0;
+    fRoot->fLevel = 0;
+    fCurrentState.fNode = fRoot;
+    fCurrentState.fMatrix = identity;
+    *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
+}
+
+SkPictureStateTree::~SkPictureStateTree() {
+}
+
+SkPictureStateTree::Draw* SkPictureStateTree::appendDraw(uint32_t offset) {
+    Draw* draw = static_cast<Draw*>(fAlloc.allocThrow(sizeof(Draw)));
+    *draw = fCurrentState;
+    draw->fOffset = offset;
+    return draw;
+}
+
+void SkPictureStateTree::appendSave() {
+    *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
+    fCurrentState.fNode->fFlags |= Node::kSave_Flag;
+}
+
+void SkPictureStateTree::appendSaveLayer(uint32_t offset) {
+    *static_cast<Draw*>(fStateStack.push_back()) = fCurrentState;
+    this->appendNode(offset);
+    fCurrentState.fNode->fFlags |= Node::kSaveLayer_Flag;
+}
+
+void SkPictureStateTree::appendRestore() {
+    fCurrentState = *static_cast<Draw*>(fStateStack.back());
+    fStateStack.pop_back();
+}
+
+void SkPictureStateTree::appendTransform(const SkMatrix& trans) {
+    SkMatrix* m = static_cast<SkMatrix*>(fAlloc.allocThrow(sizeof(SkMatrix)));
+    *m = trans;
+    fCurrentState.fMatrix = m;
+}
+
+void SkPictureStateTree::appendClip(uint32_t offset) {
+    this->appendNode(offset);
+}
+
+SkPictureStateTree::Iterator SkPictureStateTree::getIterator(const SkTDArray<void*>& draws,
+                                                             SkCanvas* canvas) {
+    return Iterator(draws, canvas, fRoot);
+}
+
+void SkPictureStateTree::appendNode(uint32_t offset) {
+    Node* n = static_cast<Node*>(fAlloc.allocThrow(sizeof(Node)));
+    n->fOffset = offset;
+    n->fFlags = 0;
+    n->fParent = fCurrentState.fNode;
+    n->fLevel = fCurrentState.fNode->fLevel + 1;
+    n->fMatrix = fCurrentState.fMatrix;
+    fCurrentState.fNode = n;
+}
+
+SkPictureStateTree::Iterator::Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root)
+    : fDraws(&draws)
+    , fCanvas(canvas)
+    , fCurrentNode(root)
+    , fPlaybackMatrix(canvas->getTotalMatrix())
+    , fCurrentMatrix(NULL)
+    , fPlaybackIndex(0)
+    , fSave(false)
+    , fValid(true) {
+}
+
+uint32_t SkPictureStateTree::Iterator::draw() {
+    SkASSERT(this->isValid());
+    if (fPlaybackIndex >= fDraws->count()) {
+        // restore back to where we started
+        if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
+        fCurrentNode = fCurrentNode->fParent;
+        while (NULL != fCurrentNode) {
+            if (fCurrentNode->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
+            if (fCurrentNode->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
+            fCurrentNode = fCurrentNode->fParent;
+        }
+        fCanvas->setMatrix(fPlaybackMatrix);
+        return kDrawComplete;
+    }
+
+    Draw* draw = static_cast<Draw*>((*fDraws)[fPlaybackIndex]);
+    Node* targetNode = draw->fNode;
+
+    if (fSave) {
+        fCanvas->save(SkCanvas::kClip_SaveFlag);
+        fSave = false;
+    }
+
+    if (fCurrentNode != targetNode) {
+        // If we're not at the target and we don't have a list of nodes to get there, we need to
+        // figure out the path from our current node, to the target
+        if (fNodes.count() == 0) {
+            // Trace back up to a common ancestor, restoring to get our current state to match that
+            // of the ancestor, and saving a list of nodes whose state we need to apply to get to
+            // the target (we can restore up to the ancestor immediately, but we'll need to return
+            // an offset for each node on the way down to the target, to apply the desired clips and
+            // saveLayers, so it may take several draw() calls before the next draw actually occurs)
+            Node* tmp = fCurrentNode;
+            Node* ancestor = targetNode;
+            while (tmp != ancestor) {
+                uint16_t currentLevel = tmp->fLevel;
+                uint16_t targetLevel = ancestor->fLevel;
+                if (currentLevel >= targetLevel) {
+                    if (tmp != fCurrentNode && tmp->fFlags & Node::kSave_Flag) { fCanvas->restore(); }
+                    if (tmp->fFlags & Node::kSaveLayer_Flag) { fCanvas->restore(); }
+                    tmp = tmp->fParent;
+                }
+                if (currentLevel <= targetLevel) {
+                    fNodes.push(ancestor);
+                    ancestor = ancestor->fParent;
+                }
+            }
+
+            if (ancestor->fFlags & Node::kSave_Flag) {
+                if (fCurrentNode != ancestor) { fCanvas->restore(); }
+                if (targetNode != ancestor) { fCanvas->save(SkCanvas::kClip_SaveFlag); }
+            }
+            fCurrentNode = ancestor;
+        }
+
+        // If we're not at the target node yet, we'll need to return an offset to make the caller
+        // apply the next clip or saveLayer.
+        if (fCurrentNode != targetNode) {
+            if (fCurrentMatrix != fNodes.top()->fMatrix) {
+                fCurrentMatrix = fNodes.top()->fMatrix;
+                SkMatrix tmp = *fNodes.top()->fMatrix;
+                tmp.postConcat(fPlaybackMatrix);
+                fCanvas->setMatrix(tmp);
+            }
+            uint32_t offset = fNodes.top()->fOffset;
+            fCurrentNode = fNodes.top();
+            fSave = fCurrentNode != targetNode && fCurrentNode->fFlags & Node::kSave_Flag;
+            fNodes.pop();
+            return offset;
+        }
+    }
+
+    // If we got this far, the clip/saveLayer state is all set, so we can proceed to set the matrix
+    // for the draw, and return its offset.
+
+    if (fCurrentMatrix != draw->fMatrix) {
+        SkMatrix tmp = *draw->fMatrix;
+        tmp.postConcat(fPlaybackMatrix);
+        fCanvas->setMatrix(tmp);
+        fCurrentMatrix = draw->fMatrix;
+    }
+
+    ++fPlaybackIndex;
+    return draw->fOffset;
+}
diff --git a/src/core/SkPictureStateTree.h b/src/core/SkPictureStateTree.h
new file mode 100644
index 0000000..798e552
--- /dev/null
+++ b/src/core/SkPictureStateTree.h
@@ -0,0 +1,135 @@
+
+/*
+ * 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 SkPictureStateTree_DEFINED
+#define SkPictureStateTree_DEFINED
+
+#include "SkTDArray.h"
+#include "SkChunkAlloc.h"
+#include "SkDeque.h"
+#include "SkMatrix.h"
+#include "SkRefCnt.h"
+
+class SkCanvas;
+
+/**
+ * Provides an interface that, given a sequence of draws into an SkPicture with corresponding
+ * offsets, allows for playback of an arbitrary subset of the draws (note that Z-order is only
+ * guaranteed if the draws are explicitly sorted).
+ */
+class SkPictureStateTree : public SkRefCnt {
+private:
+    struct Node;
+public:
+    SK_DECLARE_INST_COUNT(SkPictureStateTree)
+
+    /**
+     * A draw call, stores offset into command buffer, a pointer to the matrix, and a pointer to
+     * the node in the tree that corresponds to its clip/layer state
+     */
+    struct Draw {
+        SkMatrix* fMatrix;
+        Node* fNode;
+        uint32_t fOffset;
+        bool operator<(const Draw& other) const { return fOffset < other.fOffset; }
+    };
+
+    class Iterator;
+
+    SkPictureStateTree();
+    ~SkPictureStateTree();
+
+    /**
+     * Creates and returns a struct representing a draw at the given offset.
+     */
+    Draw* appendDraw(uint32_t offset);
+
+    /**
+     * Given a list of draws, and a canvas, returns an iterator that produces the correct sequence
+     * of offsets into the command buffer to carry out those calls with correct matrix/clip state.
+     * This handles saves/restores, and does all necessary matrix setup.
+     */
+    Iterator getIterator(const SkTDArray<void*>& draws, SkCanvas* canvas);
+
+    void appendSave();
+    void appendSaveLayer(uint32_t offset);
+    void appendRestore();
+    void appendTransform(const SkMatrix& trans);
+    void appendClip(uint32_t offset);
+
+    /**
+     * Playback helper
+     */
+    class Iterator {
+    public:
+        /** Returns the next offset into the picture stream, or kDrawComplete if complete. */
+        uint32_t draw();
+        static const uint32_t kDrawComplete = SK_MaxU32;
+        Iterator() : fPlaybackMatrix(), fValid(false) { }
+        bool isValid() const { return fValid; }
+    private:
+        Iterator(const SkTDArray<void*>& draws, SkCanvas* canvas, Node* root);
+        // The draws this iterator is associated with
+        const SkTDArray<void*>* fDraws;
+
+        // canvas this is playing into (so we can insert saves/restores as necessary)
+        SkCanvas* fCanvas;
+
+        // current state node
+        Node* fCurrentNode;
+
+        // List of nodes whose state we need to apply to reach TargetNode
+        SkTDArray<Node*> fNodes;
+
+        // The matrix of the canvas we're playing back into
+        const SkMatrix fPlaybackMatrix;
+
+        // Cache of current matrix, so we can avoid redundantly setting it
+        SkMatrix* fCurrentMatrix;
+
+        // current position in the array of draws
+        int fPlaybackIndex;
+        // Whether or not we need to do a save next iteration
+        bool fSave;
+
+        // Whether or not this is a valid iterator (the default public constructor sets this false)
+        bool fValid;
+
+        friend class SkPictureStateTree;
+    };
+
+private:
+
+    void appendNode(uint32_t offset);
+
+    SkChunkAlloc fAlloc;
+    Node* fRoot;
+
+    // The currently active state
+    Draw fCurrentState;
+    // A stack of states for tracking save/restores
+    SkDeque fStateStack;
+
+    // Represents a notable piece of state that requires an offset into the command buffer,
+    // corresponding to a clip/saveLayer/etc call, to apply.
+    struct Node {
+        Node* fParent;
+        uint32_t fOffset;
+        uint16_t fLevel;
+        uint16_t fFlags;
+        SkMatrix* fMatrix;
+        enum Flags {
+            kSave_Flag      = 0x1,
+            kSaveLayer_Flag = 0x2
+        };
+    };
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/core/SkPixelRef.cpp b/src/core/SkPixelRef.cpp
index 1279f06..4cf47e3 100644
--- a/src/core/SkPixelRef.cpp
+++ b/src/core/SkPixelRef.cpp
@@ -6,9 +6,11 @@
  * found in the LICENSE file.
  */
 #include "SkPixelRef.h"
-#include "SkFlattenable.h"
+#include "SkFlattenableBuffers.h"
 #include "SkThread.h"
 
+SK_DEFINE_INST_COUNT(SkPixelRef)
+
 // must be a power-of-2. undef to just use 1 mutex
 #define PIXELREF_MUTEX_RING_COUNT       32
 
@@ -19,7 +21,7 @@
     SK_DECLARE_STATIC_MUTEX(gPixelRefMutex);
 #endif
 
-SkBaseMutex* get_default_mutex() {
+static SkBaseMutex* get_default_mutex() {
 #ifdef PIXELREF_MUTEX_RING_COUNT
     // atomic_inc might be overkill here. It may be fine if once in a while
     // we hit a race-condition and two subsequent calls get the same index...
@@ -32,7 +34,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-extern int32_t SkNextPixelRefGenerationID();
+int32_t SkNextPixelRefGenerationID();
+
 int32_t SkNextPixelRefGenerationID() {
     static int32_t  gPixelRefGenerationID;
     // do a loop in case our global wraps around, as we never want to
@@ -66,27 +69,41 @@
     fPreLocked = false;
 }
 
-SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex) {
+SkPixelRef::SkPixelRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex)
+        : INHERITED(buffer) {
     this->setMutex(mutex);
     fPixels = NULL;
     fColorTable = NULL; // we do not track ownership of this
     fLockCount = 0;
-    fGenerationID = 0;  // signal to rebuild
     fIsImmutable = buffer.readBool();
+    fGenerationID = buffer.readUInt();
     fPreLocked = false;
 }
 
 void SkPixelRef::setPreLocked(void* pixels, SkColorTable* ctable) {
+#ifndef SK_IGNORE_PIXELREF_SETPRELOCKED
     // only call me in your constructor, otherwise fLockCount tracking can get
     // out of sync.
     fPixels = pixels;
     fColorTable = ctable;
     fLockCount = SKPIXELREF_PRELOCKED_LOCKCOUNT;
     fPreLocked = true;
+#endif
 }
 
 void SkPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
     buffer.writeBool(fIsImmutable);
+    // We write the gen ID into the picture for within-process recording. This
+    // is safe since the same genID will never refer to two different sets of
+    // pixels (barring overflow). However, each process has its own "namespace"
+    // of genIDs. So for cross-process recording we write a zero which will
+    // trigger assignment of a new genID in playback.
+    if (buffer.isCrossProcess()) {
+        buffer.writeUInt(0);
+    } else {
+        buffer.writeUInt(fGenerationID);
+    }
 }
 
 void SkPixelRef::lockPixels() {
@@ -103,7 +120,7 @@
 
 void SkPixelRef::unlockPixels() {
     SkASSERT(!fPreLocked || SKPIXELREF_PRELOCKED_LOCKCOUNT == fLockCount);
-    
+
     if (!fPreLocked) {
         SkAutoMutexAcquire  ac(*fMutex);
 
@@ -153,68 +170,7 @@
     return false;
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-#define MAX_PAIR_COUNT  16
-
-struct Pair {
-    const char*          fName;
-    SkPixelRef::Factory  fFactory;
-};
-
-static int gCount;
-static Pair gPairs[MAX_PAIR_COUNT];
-
-void SkPixelRef::Register(const char name[], Factory factory) {
-    SkASSERT(name);
-    SkASSERT(factory);
-
-    static bool gOnce;
-    if (!gOnce) {
-        gCount = 0;
-        gOnce = true;
-    }
-
-    SkASSERT(gCount < MAX_PAIR_COUNT);
-
-    gPairs[gCount].fName = name;
-    gPairs[gCount].fFactory = factory;
-    gCount += 1;
-}
-
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS && defined(SK_DEBUG)
-static void report_no_entries(const char* functionName) {
-    if (!gCount) {
-        SkDebugf("%s has no registered name/factory pairs."
-                 " Call SkGraphics::Init() at process initialization time.",
-                 functionName);
-    }
-}
-#endif
-
-SkPixelRef::Factory SkPixelRef::NameToFactory(const char name[]) {
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS && defined(SK_DEBUG)
-    report_no_entries(__FUNCTION__);
-#endif
-    const Pair* pairs = gPairs;
-    for (int i = gCount - 1; i >= 0; --i) {
-        if (strcmp(pairs[i].fName, name) == 0) {
-            return pairs[i].fFactory;
-        }
-    }
-    return NULL;
-}
-
-const char* SkPixelRef::FactoryToName(Factory fact) {
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS && defined(SK_DEBUG)
-    report_no_entries(__FUNCTION__);
-#endif
-    const Pair* pairs = gPairs;
-    for (int i = gCount - 1; i >= 0; --i) {
-        if (pairs[i].fFactory == fact) {
-            return pairs[i].fName;
-        }
-    }
+SkData* SkPixelRef::onRefEncodedData() {
     return NULL;
 }
 
diff --git a/src/core/SkPoint.cpp b/src/core/SkPoint.cpp
index 5747504..ebbe3a4 100644
--- a/src/core/SkPoint.cpp
+++ b/src/core/SkPoint.cpp
@@ -31,21 +31,21 @@
 
 void SkPoint::setIRectFan(int l, int t, int r, int b, size_t stride) {
     SkASSERT(stride >= sizeof(SkPoint));
-    
-    ((SkPoint*)((intptr_t)this + 0 * stride))->set(SkIntToScalar(l), 
+
+    ((SkPoint*)((intptr_t)this + 0 * stride))->set(SkIntToScalar(l),
                                                    SkIntToScalar(t));
-    ((SkPoint*)((intptr_t)this + 1 * stride))->set(SkIntToScalar(l), 
+    ((SkPoint*)((intptr_t)this + 1 * stride))->set(SkIntToScalar(l),
                                                    SkIntToScalar(b));
-    ((SkPoint*)((intptr_t)this + 2 * stride))->set(SkIntToScalar(r), 
+    ((SkPoint*)((intptr_t)this + 2 * stride))->set(SkIntToScalar(r),
                                                    SkIntToScalar(b));
-    ((SkPoint*)((intptr_t)this + 3 * stride))->set(SkIntToScalar(r), 
+    ((SkPoint*)((intptr_t)this + 3 * stride))->set(SkIntToScalar(r),
                                                    SkIntToScalar(t));
 }
 
 void SkPoint::setRectFan(SkScalar l, SkScalar t, SkScalar r, SkScalar b,
                          size_t stride) {
     SkASSERT(stride >= sizeof(SkPoint));
-    
+
     ((SkPoint*)((intptr_t)this + 0 * stride))->set(l, t);
     ((SkPoint*)((intptr_t)this + 1 * stride))->set(l, b);
     ((SkPoint*)((intptr_t)this + 2 * stride))->set(r, b);
@@ -87,31 +87,44 @@
     return this->setLength(fX, fY, length);
 }
 
+#ifdef SK_SCALAR_IS_FLOAT
+
+// Returns the square of the Euclidian distance to (dx,dy).
+static inline float getLengthSquared(float dx, float dy) {
+    return dx * dx + dy * dy;
+}
+
+// Calculates the square of the Euclidian distance to (dx,dy) and stores it in
+// *lengthSquared.  Returns true if the distance is judged to be "nearly zero".
+//
+// This logic is encapsulated in a helper method to make it explicit that we
+// always perform this check in the same manner, to avoid inconsistencies
+// (see http://code.google.com/p/skia/issues/detail?id=560 ).
+static inline bool isLengthNearlyZero(float dx, float dy,
+                                      float *lengthSquared) {
+    *lengthSquared = getLengthSquared(dx, dy);
+    return *lengthSquared <= (SK_ScalarNearlyZero * SK_ScalarNearlyZero);
+}
+
 SkScalar SkPoint::Normalize(SkPoint* pt) {
-    SkScalar mag = SkPoint::Length(pt->fX, pt->fY);
-    if (mag > SK_ScalarNearlyZero) {
-        SkScalar scale = SkScalarInvert(mag);
-        pt->fX = SkScalarMul(pt->fX, scale);
-        pt->fY = SkScalarMul(pt->fY, scale);
+    float mag2;
+    if (!isLengthNearlyZero(pt->fX, pt->fY, &mag2)) {
+        float mag = sk_float_sqrt(mag2);
+        float scale = 1.0f / mag;
+        pt->fX = pt->fX * scale;
+        pt->fY = pt->fY * scale;
         return mag;
     }
     return 0;
 }
 
-#ifdef SK_SCALAR_IS_FLOAT
-
-bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
-    float mag2 = dx * dx + dy * dy;
-    return mag2 > SK_ScalarNearlyZero * SK_ScalarNearlyZero;
-}
-
 SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
-    return sk_float_sqrt(dx * dx + dy * dy);
+    return sk_float_sqrt(getLengthSquared(dx, dy));
 }
 
 bool SkPoint::setLength(float x, float y, float length) {
-    float mag2 = x * x + y * y;
-    if (mag2 > SK_ScalarNearlyZero * SK_ScalarNearlyZero) {
+    float mag2;
+    if (!isLengthNearlyZero(x, y, &mag2)) {
         float scale = length / sk_float_sqrt(mag2);
         fX = x * scale;
         fY = y * scale;
@@ -124,12 +137,25 @@
 
 #include "Sk64.h"
 
-bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
-    Sk64    tmp1, tmp2, tolSqr;
-    
-    tmp1.setMul(dx, dx);
-    tmp2.setMul(dy, dy);
-    tmp1.add(tmp2);
+// Returns the square of the Euclidian distance to (dx,dy) in *result.
+static inline void getLengthSquared(SkScalar dx, SkScalar dy, Sk64 *result) {
+    Sk64    dySqr;
+
+    result->setMul(dx, dx);
+    dySqr.setMul(dy, dy);
+    result->add(dySqr);
+}
+
+// Calculates the square of the Euclidian distance to (dx,dy) and stores it in
+// *lengthSquared.  Returns true if the distance is judged to be "nearly zero".
+//
+// This logic is encapsulated in a helper method to make it explicit that we
+// always perform this check in the same manner, to avoid inconsistencies
+// (see http://code.google.com/p/skia/issues/detail?id=560 ).
+static inline bool isLengthNearlyZero(SkScalar dx, SkScalar dy,
+                                      Sk64 *lengthSquared) {
+    Sk64 tolSqr;
+    getLengthSquared(dx, dy, lengthSquared);
 
     // we want nearlyzero^2, but to compute it fast we want to just do a
     // 32bit multiply, so we require that it not exceed 31bits. That is true
@@ -138,17 +164,30 @@
     SkASSERT(SK_ScalarNearlyZero <= 0xB504);
 
     tolSqr.set(0, SK_ScalarNearlyZero * SK_ScalarNearlyZero);
-    return tmp1 > tolSqr;
+    return *lengthSquared <= tolSqr;
+}
+
+SkScalar SkPoint::Normalize(SkPoint* pt) {
+    Sk64 mag2;
+    if (!isLengthNearlyZero(pt->fX, pt->fY, &mag2)) {
+        SkScalar mag = mag2.getSqrt();
+        SkScalar scale = SkScalarInvert(mag);
+        pt->fX = SkScalarMul(pt->fX, scale);
+        pt->fY = SkScalarMul(pt->fY, scale);
+        return mag;
+    }
+    return 0;
+}
+
+bool SkPoint::CanNormalize(SkScalar dx, SkScalar dy) {
+    Sk64 mag2_unused;
+    return !isLengthNearlyZero(dx, dy, &mag2_unused);
 }
 
 SkScalar SkPoint::Length(SkScalar dx, SkScalar dy) {
-    Sk64    tmp1, tmp2;
-
-    tmp1.setMul(dx, dx);
-    tmp2.setMul(dy, dy);
-    tmp1.add(tmp2);
-
-    return tmp1.getSqrt();
+    Sk64    tmp;
+    getLengthSquared(dx, dy, &tmp);
+    return tmp.getSqrt();
 }
 
 #ifdef SK_DEBUGx
@@ -215,7 +254,7 @@
     SkASSERT(top >= 8 && top <= 63);
     SkASSERT(top - 8 < SK_ARRAY_COUNT(gInvSqrt14GuessTable));
     unsigned U = gInvSqrt14GuessTable[top - 8];
-    
+
     U = invsqrt_iter(V, U);
     return invsqrt_iter(V, U);
 }
@@ -246,7 +285,7 @@
     // make x,y 1.14 values so our fast sqr won't overflow
     if (zeros > 17) {
         x <<= zeros - 17;
-        y <<= zeros - 17; 
+        y <<= zeros - 17;
     } else {
         x >>= 17 - zeros;
         y >>= 17 - zeros;
@@ -371,7 +410,7 @@
         x = SkFixedMul(x, length);
         y = SkFixedMul(y, length);
     }
-    
+
     this->set(x, y);
     return true;
 }
@@ -387,7 +426,7 @@
 
     SkVector u = b - a;
     SkVector v = *this - a;
-    
+
     SkScalar uLengthSqd = u.lengthSqd();
     SkScalar det = u.cross(v);
     if (NULL != side) {
@@ -402,7 +441,7 @@
 SkScalar SkPoint::distanceToLineSegmentBetweenSqd(const SkPoint& a,
                                                   const SkPoint& b) const {
     // See comments to distanceToLineBetweenSqd. If the projection of c onto
-    // u is between a and b then this returns the same result as that 
+    // u is between a and b then this returns the same result as that
     // function. Otherwise, it returns the distance to the closer of a and
     // b. Let the projection of v onto u be v'.  There are three cases:
     //    1. v' points opposite to u. c is not between a and b and is closer
@@ -412,17 +451,17 @@
     //       to the line ab.
     //    3. v' points along u and has greater magnitude than u. c is not
     //       not between a and b and is closer to b than a.
-    // v' = (u dot v) * u / |u|. So if (u dot v)/|u| is less than zero we're 
+    // v' = (u dot v) * u / |u|. So if (u dot v)/|u| is less than zero we're
     // in case 1. If (u dot v)/|u| is > |u| we are in case 3. Otherwise
-    // we're in case 2. We actually compare (u dot v) to 0 and |u|^2 to 
+    // we're in case 2. We actually compare (u dot v) to 0 and |u|^2 to
     // avoid a sqrt to compute |u|.
-    
+
     SkVector u = b - a;
     SkVector v = *this - a;
-    
+
     SkScalar uLengthSqd = u.lengthSqd();
     SkScalar uDotV = SkPoint::DotProduct(u, v);
-    
+
     if (uDotV <= 0) {
         return v.lengthSqd();
     } else if (uDotV > uLengthSqd) {
diff --git a/src/core/SkPtrRecorder.cpp b/src/core/SkPtrRecorder.cpp
index e3a9eee..d473cab 100644
--- a/src/core/SkPtrRecorder.cpp
+++ b/src/core/SkPtrRecorder.cpp
@@ -8,6 +8,9 @@
 #include "SkPtrRecorder.h"
 #include "SkTSearch.h"
 
+SK_DEFINE_INST_COUNT(SkPtrSet)
+SK_DEFINE_INST_COUNT(SkNamedFactorySet)
+
 void SkPtrSet::reset() {
     Pair* p = fList.begin();
     Pair* stop = fList.end();
@@ -18,20 +21,20 @@
     fList.reset();
 }
 
-int SkPtrSet::Cmp(const Pair& a, const Pair& b) {
-    return (char*)a.fPtr - (char*)b.fPtr;
+int SkPtrSet::Cmp(const Pair* a, const Pair* b) {
+    return (char*)a->fPtr - (char*)b->fPtr;
 }
 
 uint32_t SkPtrSet::find(void* ptr) const {
     if (NULL == ptr) {
         return 0;
     }
-    
+
     int count = fList.count();
     Pair pair;
     pair.fPtr = ptr;
-    
-    int index = SkTSearch<Pair>(fList.begin(), count, pair, sizeof(pair), &Cmp);
+
+    int index = SkTSearch<Pair, Cmp>(fList.begin(), count, pair, sizeof(pair));
     if (index < 0) {
         return 0;
     }
@@ -47,7 +50,7 @@
     Pair pair;
     pair.fPtr = ptr;
 
-    int index = SkTSearch<Pair>(fList.begin(), count, pair, sizeof(pair), &Cmp);
+    int index = SkTSearch<Pair, Cmp>(fList.begin(), count, pair, sizeof(pair));
     if (index < 0) {
         index = ~index; // turn it back into an index for insertion
         this->incPtr(ptr);
@@ -72,5 +75,3 @@
         }
     }
 }
-
-
diff --git a/src/core/SkPtrRecorder.h b/src/core/SkPtrRecorder.h
new file mode 100644
index 0000000..00e75b4
--- /dev/null
+++ b/src/core/SkPtrRecorder.h
@@ -0,0 +1,151 @@
+
+/*
+ * Copyright 2008 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 SkPtrSet_DEFINED
+#define SkPtrSet_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkFlattenable.h"
+#include "SkTDArray.h"
+
+/**
+ *  Maintains a set of ptrs, assigning each a unique ID [1...N]. Duplicate ptrs
+ *  return the same ID (since its a set). Subclasses can override inPtr()
+ *  and decPtr(). incPtr() is called each time a unique ptr is added ot the
+ *  set. decPtr() is called on each ptr when the set is destroyed or reset.
+ */
+class SkPtrSet : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkPtrSet)
+
+    /**
+     *  Search for the specified ptr in the set. If it is found, return its
+     *  32bit ID [1..N], or if not found, return 0. Always returns 0 for NULL.
+     */
+    uint32_t find(void*) const;
+
+    /**
+     *  Add the specified ptr to the set, returning a unique 32bit ID for it
+     *  [1...N]. Duplicate ptrs will return the same ID.
+     *
+     *  If the ptr is NULL, it is not added, and 0 is returned.
+     */
+    uint32_t add(void*);
+
+    /**
+     *  Return the number of (non-null) ptrs in the set.
+     */
+    int count() const { return fList.count(); }
+
+    /**
+     *  Copy the ptrs in the set into the specified array (allocated by the
+     *  caller). The ptrs are assgined to the array based on their corresponding
+     *  ID. e.g. array[ptr.ID - 1] = ptr.
+     *
+     *  incPtr() and decPtr() are not called during this operation.
+     */
+    void copyToArray(void* array[]) const;
+
+    /**
+     *  Call decPtr() on each ptr in the set, and the reset the size of the set
+     *  to 0.
+     */
+    void reset();
+
+protected:
+    virtual void incPtr(void* ptr) {}
+    virtual void decPtr(void* ptr) {}
+
+private:
+    struct Pair {
+        void*       fPtr;   // never NULL
+        uint32_t    fIndex; // 1...N
+    };
+
+    // we store the ptrs in sorted-order (using Cmp) so that we can efficiently
+    // detect duplicates when add() is called. Hence we need to store the
+    // ptr and its ID/fIndex explicitly, since the ptr's position in the array
+    // is not related to its "index".
+    SkTDArray<Pair>  fList;
+
+    static int Cmp(const Pair* a, const Pair* b);
+
+    typedef SkRefCnt INHERITED;
+};
+
+/**
+ *  Templated wrapper for SkPtrSet, just meant to automate typecasting
+ *  parameters to and from void* (which the base class expects).
+ */
+template <typename T> class SkTPtrSet : public SkPtrSet {
+public:
+    uint32_t find(T ptr) {
+        return this->INHERITED::find((void*)ptr);
+    }
+    uint32_t add(T ptr) {
+        return this->INHERITED::add((void*)ptr);
+    }
+
+    void copyToArray(T* array) const {
+        this->INHERITED::copyToArray((void**)array);
+    }
+
+private:
+    typedef SkPtrSet INHERITED;
+};
+
+/**
+ *  Subclass of SkTPtrSet specialed to call ref() and unref() when the
+ *  base class's incPtr() and decPtr() are called. This makes it a valid owner
+ *  of each ptr, which is released when the set is reset or destroyed.
+ */
+class SkRefCntSet : public SkTPtrSet<SkRefCnt*> {
+public:
+    virtual ~SkRefCntSet();
+
+protected:
+    // overrides
+    virtual void incPtr(void*);
+    virtual void decPtr(void*);
+};
+
+class SkFactorySet : public SkTPtrSet<SkFlattenable::Factory> {};
+
+/**
+ * Similar to SkFactorySet, but only allows Factorys that have registered names.
+ * Also has a function to return the next added Factory's name.
+ */
+class SkNamedFactorySet : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(SkNamedFactorySet)
+
+    SkNamedFactorySet();
+
+    /**
+     * Find the specified Factory in the set. If it is not already in the set,
+     * and has registered its name, add it to the set, and return its index.
+     * If the Factory has no registered name, return 0.
+     */
+    uint32_t find(SkFlattenable::Factory);
+
+    /**
+     * If new Factorys have been added to the set, return the name of the first
+     * Factory added after the Factory name returned by the last call to this
+     * function.
+     */
+    const char* getNextAddedFactoryName();
+private:
+    int                    fNextAddedFactory;
+    SkFactorySet           fFactorySet;
+    SkTDArray<const char*> fNames;
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/core/SkQuadClipper.cpp b/src/core/SkQuadClipper.cpp
index b79d5a2..a67a23f 100644
--- a/src/core/SkQuadClipper.cpp
+++ b/src/core/SkQuadClipper.cpp
@@ -22,7 +22,9 @@
     }
 }
 
-SkQuadClipper::SkQuadClipper() {}
+SkQuadClipper::SkQuadClipper() {
+    fClip.setEmpty();
+}
 
 void SkQuadClipper::setClip(const SkIRect& clip) {
     // conver to scalars, since that's where we'll see the points
@@ -40,7 +42,7 @@
     SkScalar A = c0 - c1 - c1 + c2;
     SkScalar B = 2*(c1 - c0);
     SkScalar C = c0 - target;
-    
+
     SkScalar roots[2];  // we only expect one, but make room for 2 for safety
     int count = SkFindUnitQuadRoots(A, B, C, roots);
     if (count) {
@@ -62,7 +64,7 @@
  */
 bool SkQuadClipper::clipQuad(const SkPoint srcPts[3], SkPoint dst[3]) {
     bool reverse;
-    
+
     // we need the data to be monotonically increasing in Y
     if (srcPts[0].fY > srcPts[2].fY) {
         dst[0] = srcPts[2];
@@ -73,17 +75,17 @@
         memcpy(dst, srcPts, 3 * sizeof(SkPoint));
         reverse = false;
     }
-    
+
     // are we completely above or below
     const SkScalar ctop = fClip.fTop;
     const SkScalar cbot = fClip.fBottom;
     if (dst[2].fY <= ctop || dst[0].fY >= cbot) {
         return false;
     }
-    
+
     SkScalar t;
     SkPoint tmp[5]; // for SkChopQuadAt
-    
+
     // are we partially above
     if (dst[0].fY < ctop) {
         if (chopMonoQuadAtY(dst, ctop, &t)) {
@@ -101,7 +103,7 @@
             }
         }
     }
-    
+
     // are we partially below
     if (dst[2].fY > cbot) {
         if (chopMonoQuadAtY(dst, cbot, &t)) {
@@ -118,10 +120,9 @@
             }
         }
     }
-    
+
     if (reverse) {
         SkTSwap<SkPoint>(dst[0], dst[2]);
     }
     return true;
 }
-
diff --git a/src/core/SkQuadClipper.h b/src/core/SkQuadClipper.h
index be0cea0..c0b8695 100644
--- a/src/core/SkQuadClipper.h
+++ b/src/core/SkQuadClipper.h
@@ -14,16 +14,16 @@
 
 /** This class is initialized with a clip rectangle, and then can be fed quads,
     which must already be monotonic in Y.
- 
+
     In the future, it might return a series of segments, allowing it to clip
     also in X, to ensure that all segments fit in a finite coordinate system.
  */
 class SkQuadClipper {
 public:
     SkQuadClipper();
-    
+
     void setClip(const SkIRect& clip);
-    
+
     bool clipQuad(const SkPoint src[3], SkPoint dst[3]);
 
 private:
@@ -40,11 +40,11 @@
     bool clipCubic(const SkPoint pts[4], const SkRect& clip);
 
     SkPath::Verb next(SkPoint pts[]);
-    
+
 private:
     SkPoint*        fCurrPoint;
     SkPath::Verb*   fCurrVerb;
-    
+
     enum {
         kMaxVerbs = 13,
         kMaxPoints = 32
diff --git a/src/core/SkRRect.cpp b/src/core/SkRRect.cpp
new file mode 100644
index 0000000..fc1a1cf
--- /dev/null
+++ b/src/core/SkRRect.cpp
@@ -0,0 +1,332 @@
+/*
+ * 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 "SkRRect.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkRRect::setRectXY(const SkRect& rect, SkScalar xRad, SkScalar yRad) {
+    if (rect.isEmpty()) {
+        this->setEmpty();
+        return;
+    }
+
+    if (xRad <= 0 || yRad <= 0) {
+        // all corners are square in this case
+        this->setRect(rect);
+        return;
+    }
+
+    if (rect.width() < xRad+xRad || rect.height() < yRad+yRad) {
+        SkScalar scale = SkMinScalar(SkScalarDiv(rect.width(), xRad + xRad),
+                                     SkScalarDiv(rect.height(), yRad + yRad));
+        SkASSERT(scale < SK_Scalar1);
+        xRad = SkScalarMul(xRad, scale);
+        yRad = SkScalarMul(yRad, scale);
+    }
+
+    fRect = rect;
+    for (int i = 0; i < 4; ++i) {
+        fRadii[i].set(xRad, yRad);
+    }
+    fType = kSimple_Type;
+    if (xRad >= SkScalarHalf(fRect.width()) && yRad >= SkScalarHalf(fRect.height())) {
+        fType = kOval_Type;
+        // TODO: assert that all the x&y radii are already W/2 & H/2
+    }
+
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkRRect::setRectRadii(const SkRect& rect, const SkVector radii[4]) {
+    if (rect.isEmpty()) {
+        this->setEmpty();
+        return;
+    }
+
+    fRect = rect;
+    memcpy(fRadii, radii, sizeof(fRadii));
+
+    bool allCornersSquare = true;
+
+    // Clamp negative radii to zero
+    for (int i = 0; i < 4; ++i) {
+        if (fRadii[i].fX <= 0 || fRadii[i].fY <= 0) {
+            // In this case we are being a little fast & loose. Since one of
+            // the radii is 0 the corner is square. However, the other radii
+            // could still be non-zero and play in the global scale factor
+            // computation.
+            fRadii[i].fX = 0;
+            fRadii[i].fY = 0;
+        } else {
+            allCornersSquare = false;
+        }
+    }
+
+    if (allCornersSquare) {
+        this->setRect(rect);
+        return;
+    }
+
+    // Proportionally scale down all radii to fit. Find the minimum ratio
+    // of a side and the radii on that side (for all four sides) and use
+    // that to scale down _all_ the radii. This algorithm is from the
+    // W3 spec (http://www.w3.org/TR/css3-background/) section 5.5 - Overlapping
+    // Curves:
+    // "Let f = min(Li/Si), where i is one of { top, right, bottom, left },
+    //   Si is the sum of the two corresponding radii of the corners on side i,
+    //   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;
+
+    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));
+    }
+
+    if (scale < SK_Scalar1) {
+        for (int i = 0; i < 4; ++i) {
+            fRadii[i].fX = SkScalarMul(fRadii[i].fX, scale);
+            fRadii[i].fY = SkScalarMul(fRadii[i].fY, scale);
+        }
+    }
+
+    // At this point we're either oval, simple, or complex (not empty or rect)
+    // but we lazily resolve the type to avoid the work if the information
+    // isn't required.
+    fType = (SkRRect::Type) kUnknown_Type;
+
+    SkDEBUGCODE(this->validate();)
+}
+
+bool SkRRect::contains(SkScalar x, SkScalar y) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (kEmpty_Type == this->type()) {
+        return false;
+    }
+
+    if (!fRect.contains(x, y)) {
+        return false;
+    }
+
+    if (kRect_Type == this->type()) {
+        // the 'fRect' test above was sufficient
+        return true;
+    }
+
+    // We know the point is inside the RR's bounds. The only way it can
+    // be out is if it outside one of the corners
+    SkPoint canonicalPt; // (x,y) translated to one of the quadrants
+    int index;
+
+    if (kOval_Type == this->type()) {
+        canonicalPt.set(x - fRect.centerX(), y - fRect.centerY());
+        index = kUpperLeft_Corner;  // any corner will do in this case
+    } else {
+        if (x < fRect.fLeft + fRadii[kUpperLeft_Corner].fX &&
+            y < fRect.fTop + fRadii[kUpperLeft_Corner].fY) {
+            // UL corner
+            index = kUpperLeft_Corner;
+            canonicalPt.set(x - (fRect.fLeft + fRadii[kUpperLeft_Corner].fX),
+                            y - (fRect.fTop + fRadii[kUpperLeft_Corner].fY));
+            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY < 0);
+        } else if (x < fRect.fLeft + fRadii[kLowerLeft_Corner].fX &&
+                   y > fRect.fBottom - fRadii[kLowerLeft_Corner].fY) {
+            // LL corner
+            index = kLowerLeft_Corner;
+            canonicalPt.set(x - (fRect.fLeft + fRadii[kLowerLeft_Corner].fX),
+                            y - (fRect.fBottom - fRadii[kLowerLeft_Corner].fY));
+            SkASSERT(canonicalPt.fX < 0 && canonicalPt.fY > 0);
+        } else if (x > fRect.fRight - fRadii[kUpperRight_Corner].fX &&
+                   y < fRect.fTop + fRadii[kUpperRight_Corner].fY) {
+            // UR corner
+            index = kUpperRight_Corner;
+            canonicalPt.set(x - (fRect.fRight - fRadii[kUpperRight_Corner].fX),
+                            y - (fRect.fTop + fRadii[kUpperRight_Corner].fY));
+            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY < 0);
+        } else if (x > fRect.fRight - fRadii[kLowerRight_Corner].fX &&
+                   y > fRect.fBottom - fRadii[kLowerRight_Corner].fY) {
+            // LR corner
+            index = kLowerRight_Corner;
+            canonicalPt.set(x - (fRect.fRight - fRadii[kLowerRight_Corner].fX),
+                            y - (fRect.fBottom - fRadii[kLowerRight_Corner].fY));
+            SkASSERT(canonicalPt.fX > 0 && canonicalPt.fY > 0);
+        } else {
+            // not in any of the corners
+            return true;
+        }
+    }
+
+    // A point is in an ellipse (in standard position) if:
+    //      x^2     y^2
+    //     ----- + ----- <= 1
+    //      a^2     b^2
+    SkScalar dist =  SkScalarDiv(SkScalarSquare(canonicalPt.fX), SkScalarSquare(fRadii[index].fX)) +
+                     SkScalarDiv(SkScalarSquare(canonicalPt.fY), SkScalarSquare(fRadii[index].fY));
+    return dist <= SK_Scalar1;
+}
+
+// There is a simplified version of this method in setRectXY
+void SkRRect::computeType() const {
+    SkDEBUGCODE(this->validate();)
+
+    if (fRect.isEmpty()) {
+        fType = kEmpty_Type;
+        return;
+    }
+
+    bool allRadiiEqual = true; // are all x radii equal and all y radii?
+    bool allCornersSquare = 0 == fRadii[0].fX || 0 == fRadii[0].fY;
+
+    for (int i = 1; i < 4; ++i) {
+        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
+            // if either radius is zero the corner is square so both have to
+            // be non-zero to have a rounded corner
+            allCornersSquare = false;
+        }
+        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
+            allRadiiEqual = false;
+        }
+    }
+
+    if (allCornersSquare) {
+        fType = kRect_Type;
+        return;
+    }
+
+    if (allRadiiEqual) {
+        if (fRadii[0].fX >= SkScalarHalf(fRect.width()) &&
+            fRadii[0].fY >= SkScalarHalf(fRect.height())) {
+            fType = kOval_Type;
+        } else {
+            fType = kSimple_Type;
+        }
+        return;
+    }
+
+    fType = kComplex_Type;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkRRect::inset(SkScalar dx, SkScalar dy, SkRRect* dst) const {
+    SkRect r = fRect;
+
+    r.inset(dx, dy);
+    if (r.isEmpty()) {
+        dst->setEmpty();
+        return;
+    }
+
+    SkVector radii[4];
+    memcpy(radii, fRadii, sizeof(radii));
+    for (int i = 0; i < 4; ++i) {
+        if (radii[i].fX) {
+            radii[i].fX -= dx;
+        }
+        if (radii[i].fY) {
+            radii[i].fY -= dy;
+        }
+    }
+    dst->setRectRadii(r, radii);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+uint32_t SkRRect::writeToMemory(void* buffer) const {
+    SkASSERT(kSizeInMemory == sizeof(SkRect) + sizeof(fRadii));
+
+    memcpy(buffer, &fRect, sizeof(SkRect));
+    memcpy((char*)buffer + sizeof(SkRect), fRadii, sizeof(fRadii));
+    return kSizeInMemory;
+}
+
+uint32_t SkRRect::readFromMemory(const void* buffer) {
+    SkScalar storage[12];
+    SkASSERT(sizeof(storage) == kSizeInMemory);
+
+    // we make a local copy, to ensure alignment before we cast
+    memcpy(storage, buffer, kSizeInMemory);
+
+    this->setRectRadii(*(const SkRect*)&storage[0],
+                       (const SkVector*)&storage[4]);
+    return kSizeInMemory;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+void SkRRect::validate() const {
+    bool allRadiiZero = (0 == fRadii[0].fX && 0 == fRadii[0].fY);
+    bool allCornersSquare = (0 == fRadii[0].fX || 0 == fRadii[0].fY);
+    bool allRadiiSame = true;
+
+    for (int i = 1; i < 4; ++i) {
+        if (0 != fRadii[i].fX || 0 != fRadii[i].fY) {
+            allRadiiZero = false;
+        }
+
+        if (fRadii[i].fX != fRadii[i-1].fX || fRadii[i].fY != fRadii[i-1].fY) {
+            allRadiiSame = false;
+        }
+
+        if (0 != fRadii[i].fX && 0 != fRadii[i].fY) {
+            allCornersSquare = false;
+        }
+    }
+
+    switch (fType) {
+        case kEmpty_Type:
+            SkASSERT(fRect.isEmpty());
+            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
+
+            SkASSERT(0 == fRect.fLeft && 0 == fRect.fTop &&
+                     0 == fRect.fRight && 0 == fRect.fBottom);
+            break;
+        case kRect_Type:
+            SkASSERT(!fRect.isEmpty());
+            SkASSERT(allRadiiZero && allRadiiSame && allCornersSquare);
+            break;
+        case kOval_Type:
+            SkASSERT(!fRect.isEmpty());
+            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
+
+            for (int i = 0; i < 4; ++i) {
+                SkASSERT(SkScalarNearlyEqual(fRadii[i].fX, SkScalarHalf(fRect.width())));
+                SkASSERT(SkScalarNearlyEqual(fRadii[i].fY, SkScalarHalf(fRect.height())));
+            }
+            break;
+        case kSimple_Type:
+            SkASSERT(!fRect.isEmpty());
+            SkASSERT(!allRadiiZero && allRadiiSame && !allCornersSquare);
+            break;
+        case kComplex_Type:
+            SkASSERT(!fRect.isEmpty());
+            SkASSERT(!allRadiiZero && !allRadiiSame && !allCornersSquare);
+            break;
+        case kUnknown_Type:
+            // no limits on this
+            break;
+    }
+}
+#endif // SK_DEBUG
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/core/SkRTree.cpp b/src/core/SkRTree.cpp
new file mode 100644
index 0000000..88fc079
--- /dev/null
+++ b/src/core/SkRTree.cpp
@@ -0,0 +1,473 @@
+
+/*
+ * 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 "SkRTree.h"
+#include "SkTSort.h"
+
+static inline uint32_t get_area(const SkIRect& rect);
+static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2);
+static inline uint32_t get_margin(const SkIRect& rect);
+static inline uint32_t get_overlap_increase(const SkIRect& rect1, const SkIRect& rect2,
+                                            SkIRect expandBy);
+static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2);
+static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out);
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_INST_COUNT(SkRTree)
+
+SkRTree* SkRTree::Create(int minChildren, int maxChildren, SkScalar aspectRatio) {
+    if (minChildren < maxChildren && (maxChildren + 1) / 2 >= minChildren &&
+        minChildren > 0 && maxChildren < static_cast<int>(SK_MaxU16)) {
+        return new SkRTree(minChildren, maxChildren, aspectRatio);
+    }
+    return NULL;
+}
+
+SkRTree::SkRTree(int minChildren, int maxChildren, SkScalar aspectRatio)
+    : fMinChildren(minChildren)
+    , fMaxChildren(maxChildren)
+    , fNodeSize(sizeof(Node) + sizeof(Branch) * maxChildren)
+    , fCount(0)
+    , fNodes(fNodeSize * 256)
+    , fAspectRatio(aspectRatio) {
+    SkASSERT(minChildren < maxChildren && minChildren > 0 && maxChildren <
+             static_cast<int>(SK_MaxU16));
+    SkASSERT((maxChildren + 1) / 2 >= minChildren);
+    this->validate();
+}
+
+SkRTree::~SkRTree() {
+    this->clear();
+}
+
+void SkRTree::insert(void* data, const SkIRect& bounds, bool defer) {
+    this->validate();
+    if (bounds.isEmpty()) {
+        SkASSERT(false);
+        return;
+    }
+    Branch newBranch;
+    newBranch.fBounds = bounds;
+    newBranch.fChild.data = data;
+    if (this->isEmpty()) {
+        // since a bulk-load into an existing tree is as of yet unimplemented (and arguably not
+        // of vital importance right now), we only batch up inserts if the tree is empty.
+        if (defer) {
+            fDeferredInserts.push(newBranch);
+            return;
+        } else {
+            fRoot.fChild.subtree = allocateNode(0);
+            fRoot.fChild.subtree->fNumChildren = 0;
+        }
+    }
+
+    Branch* newSibling = insert(fRoot.fChild.subtree, &newBranch);
+    fRoot.fBounds = this->computeBounds(fRoot.fChild.subtree);
+
+    if (NULL != newSibling) {
+        Node* oldRoot = fRoot.fChild.subtree;
+        Node* newRoot = this->allocateNode(oldRoot->fLevel + 1);
+        newRoot->fNumChildren = 2;
+        *newRoot->child(0) = fRoot;
+        *newRoot->child(1) = *newSibling;
+        fRoot.fChild.subtree = newRoot;
+        fRoot.fBounds = this->computeBounds(fRoot.fChild.subtree);
+    }
+
+    ++fCount;
+    this->validate();
+}
+
+void SkRTree::flushDeferredInserts() {
+    this->validate();
+    if (this->isEmpty() && fDeferredInserts.count() > 0) {
+        fCount = fDeferredInserts.count();
+        if (1 == fCount) {
+            fRoot.fChild.subtree = allocateNode(0);
+            fRoot.fChild.subtree->fNumChildren = 0;
+            this->insert(fRoot.fChild.subtree, &fDeferredInserts[0]);
+            fRoot.fBounds = fDeferredInserts[0].fBounds;
+        } else {
+            fRoot = this->bulkLoad(&fDeferredInserts);
+        }
+    } else {
+        // TODO: some algorithm for bulk loading into an already populated tree
+        SkASSERT(0 == fDeferredInserts.count());
+    }
+    fDeferredInserts.rewind();
+    this->validate();
+}
+
+void SkRTree::search(const SkIRect& query, SkTDArray<void*>* results) {
+    this->validate();
+    if (0 != fDeferredInserts.count()) {
+        this->flushDeferredInserts();
+    }
+    if (!this->isEmpty() && SkIRect::IntersectsNoEmptyCheck(fRoot.fBounds, query)) {
+        this->search(fRoot.fChild.subtree, query, results);
+    }
+    this->validate();
+}
+
+void SkRTree::clear() {
+    this->validate();
+    fNodes.reset();
+    fDeferredInserts.rewind();
+    fCount = 0;
+    this->validate();
+}
+
+SkRTree::Node* SkRTree::allocateNode(uint16_t level) {
+    Node* out = static_cast<Node*>(fNodes.allocThrow(fNodeSize));
+    out->fNumChildren = 0;
+    out->fLevel = level;
+    return out;
+}
+
+SkRTree::Branch* SkRTree::insert(Node* root, Branch* branch, uint16_t level) {
+    Branch* toInsert = branch;
+    if (root->fLevel != level) {
+        int childIndex = this->chooseSubtree(root, branch);
+        toInsert = this->insert(root->child(childIndex)->fChild.subtree, branch, level);
+        root->child(childIndex)->fBounds = this->computeBounds(
+            root->child(childIndex)->fChild.subtree);
+    }
+    if (NULL != toInsert) {
+        if (root->fNumChildren == fMaxChildren) {
+            // handle overflow by splitting. TODO: opportunistic reinsertion
+
+            // decide on a distribution to divide with
+            Node* newSibling = this->allocateNode(root->fLevel);
+            Branch* toDivide = SkNEW_ARRAY(Branch, fMaxChildren + 1);
+            for (int i = 0; i < fMaxChildren; ++i) {
+                toDivide[i] = *root->child(i);
+            }
+            toDivide[fMaxChildren] = *toInsert;
+            int splitIndex = this->distributeChildren(toDivide);
+
+            // divide up the branches
+            root->fNumChildren = splitIndex;
+            newSibling->fNumChildren = fMaxChildren + 1 - splitIndex;
+            for (int i = 0; i < splitIndex; ++i) {
+                *root->child(i) = toDivide[i];
+            }
+            for (int i = splitIndex; i < fMaxChildren + 1; ++i) {
+                *newSibling->child(i - splitIndex) = toDivide[i];
+            }
+            SkDELETE_ARRAY(toDivide);
+
+            // pass the new sibling branch up to the parent
+            branch->fChild.subtree = newSibling;
+            branch->fBounds = this->computeBounds(newSibling);
+            return branch;
+        } else {
+            *root->child(root->fNumChildren) = *toInsert;
+            ++root->fNumChildren;
+            return NULL;
+        }
+    }
+    return NULL;
+}
+
+int SkRTree::chooseSubtree(Node* root, Branch* branch) {
+    SkASSERT(!root->isLeaf());
+    if (1 < root->fLevel) {
+        // root's child pointers do not point to leaves, so minimize area increase
+        int32_t minAreaIncrease = SK_MaxS32;
+        int32_t minArea         = SK_MaxS32;
+        int32_t bestSubtree     = -1;
+        for (int i = 0; i < root->fNumChildren; ++i) {
+            const SkIRect& subtreeBounds = root->child(i)->fBounds;
+            int32_t areaIncrease = get_area_increase(subtreeBounds, branch->fBounds);
+            // break ties in favor of subtree with smallest area
+            if (areaIncrease < minAreaIncrease || (areaIncrease == minAreaIncrease &&
+                static_cast<int32_t>(get_area(subtreeBounds)) < minArea)) {
+                minAreaIncrease = areaIncrease;
+                minArea = get_area(subtreeBounds);
+                bestSubtree = i;
+            }
+        }
+        SkASSERT(-1 != bestSubtree);
+        return bestSubtree;
+    } else if (1 == root->fLevel) {
+        // root's child pointers do point to leaves, so minimize overlap increase
+        int32_t minOverlapIncrease = SK_MaxS32;
+        int32_t minAreaIncrease    = SK_MaxS32;
+        int32_t bestSubtree = -1;
+        for (int32_t i = 0; i < root->fNumChildren; ++i) {
+            const SkIRect& subtreeBounds = root->child(i)->fBounds;
+            SkIRect expandedBounds = subtreeBounds;
+            join_no_empty_check(branch->fBounds, &expandedBounds);
+            int32_t overlap = 0;
+            for (int32_t j = 0; j < root->fNumChildren; ++j) {
+                if (j == i) continue;
+                // Note: this would be more correct if we subtracted the original pre-expanded
+                // overlap, but computing overlaps is expensive and omitting it doesn't seem to
+                // hurt query performance. See get_overlap_increase()
+                overlap += get_overlap(expandedBounds, root->child(j)->fBounds);
+            }
+            // break ties with lowest area increase
+            if (overlap < minOverlapIncrease || (overlap == minOverlapIncrease &&
+                static_cast<int32_t>(get_area_increase(branch->fBounds, subtreeBounds)) <
+                minAreaIncrease)) {
+                minOverlapIncrease = overlap;
+                minAreaIncrease = get_area_increase(branch->fBounds, subtreeBounds);
+                bestSubtree = i;
+            }
+        }
+        return bestSubtree;
+    } else {
+        SkASSERT(false);
+        return 0;
+    }
+}
+
+SkIRect SkRTree::computeBounds(Node* n) {
+    SkIRect r = n->child(0)->fBounds;
+    for (int i = 1; i < n->fNumChildren; ++i) {
+        join_no_empty_check(n->child(i)->fBounds, &r);
+    }
+    return r;
+}
+
+int SkRTree::distributeChildren(Branch* children) {
+    // We have two sides to sort by on each of two axes:
+    const static SortSide sorts[2][2] = {
+        {&SkIRect::fLeft, &SkIRect::fRight},
+        {&SkIRect::fTop, &SkIRect::fBottom}
+    };
+
+    // We want to choose an axis to split on, then a distribution along that axis; we'll need
+    // three pieces of info: the split axis, the side to sort by on that axis, and the index
+    // to split the sorted array on.
+    int32_t sortSide = -1;
+    int32_t k        = -1;
+    int32_t axis     = -1;
+    int32_t bestS    = SK_MaxS32;
+
+    // Evaluate each axis, we want the min summed margin-value (s) over all distributions
+    for (int i = 0; i < 2; ++i) {
+        int32_t minOverlap   = SK_MaxS32;
+        int32_t minArea      = SK_MaxS32;
+        int32_t axisBestK    = 0;
+        int32_t axisBestSide = 0;
+        int32_t s = 0;
+
+        // Evaluate each sort
+        for (int j = 0; j < 2; ++j) {
+            SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[i][j]));
+
+            // Evaluate each split index
+            for (int32_t k = 1; k <= fMaxChildren - 2 * fMinChildren + 2; ++k) {
+                SkIRect r1 = children[0].fBounds;
+                SkIRect r2 = children[fMinChildren + k - 1].fBounds;
+                for (int32_t l = 1; l < fMinChildren - 1 + k; ++l) {
+                    join_no_empty_check(children[l].fBounds, &r1);
+                }
+                for (int32_t l = fMinChildren + k; l < fMaxChildren + 1; ++l) {
+                    join_no_empty_check(children[l].fBounds, &r2);
+                }
+
+                int32_t area = get_area(r1) + get_area(r2);
+                int32_t overlap = get_overlap(r1, r2);
+                s += get_margin(r1) + get_margin(r2);
+
+                if (overlap < minOverlap || (overlap == minOverlap && area < minArea)) {
+                    minOverlap = overlap;
+                    minArea = area;
+                    axisBestSide = j;
+                    axisBestK = k;
+                }
+            }
+        }
+
+        if (s < bestS) {
+            bestS = s;
+            axis = i;
+            sortSide = axisBestSide;
+            k = axisBestK;
+        }
+    }
+
+    // replicate the sort of the winning distribution, (we can skip this if the last
+    // sort ended up being best)
+    if (!(axis == 1 && sortSide == 1)) {
+        SkTQSort(children, children + fMaxChildren, RectLessThan(sorts[axis][sortSide]));
+    }
+
+    return fMinChildren - 1 + k;
+}
+
+void SkRTree::search(Node* root, const SkIRect query, SkTDArray<void*>* results) const {
+    for (int i = 0; i < root->fNumChildren; ++i) {
+        if (SkIRect::IntersectsNoEmptyCheck(root->child(i)->fBounds, query)) {
+            if (root->isLeaf()) {
+                results->push(root->child(i)->fChild.data);
+            } else {
+                this->search(root->child(i)->fChild.subtree, query, results);
+            }
+        }
+    }
+}
+
+SkRTree::Branch SkRTree::bulkLoad(SkTDArray<Branch>* branches, int level) {
+    if (branches->count() == 1) {
+        // Only one branch: it will be the root
+        Branch out = (*branches)[0];
+        branches->rewind();
+        return out;
+    } else {
+        // First we sort the whole list by y coordinates
+        SkTQSort(branches->begin(), branches->end() - 1, RectLessY());
+
+        int numBranches = branches->count() / fMaxChildren;
+        int remainder = branches->count() % fMaxChildren;
+        int newBranches = 0;
+
+        if (0 != remainder) {
+            ++numBranches;
+            // If the remainder isn't enough to fill a node, we'll need to add fewer nodes to
+            // some other branches to make up for it
+            if (remainder >= fMinChildren) {
+                remainder = 0;
+            } else {
+                remainder = fMinChildren - remainder;
+            }
+        }
+
+        int numStrips = SkScalarCeil(SkScalarSqrt(SkIntToScalar(numBranches) *
+                                     SkScalarInvert(fAspectRatio)));
+        int numTiles = SkScalarCeil(SkIntToScalar(numBranches) /
+                                    SkIntToScalar(numStrips));
+        int currentBranch = 0;
+
+        for (int i = 0; i < numStrips; ++i) {
+            int begin = currentBranch;
+            int end = currentBranch + numTiles * fMaxChildren - SkMin32(remainder,
+                      (fMaxChildren - fMinChildren) * numTiles);
+            if (end > branches->count()) {
+                end = branches->count();
+            }
+
+            // Now we sort horizontal strips of rectangles by their x coords
+            SkTQSort(branches->begin() + begin, branches->begin() + end - 1, RectLessX());
+
+            for (int j = 0; j < numTiles && currentBranch < branches->count(); ++j) {
+                int incrementBy = fMaxChildren;
+                if (remainder != 0) {
+                    // if need be, omit some nodes to make up for remainder
+                    if (remainder <= fMaxChildren - fMinChildren) {
+                        incrementBy -= remainder;
+                        remainder = 0;
+                    } else {
+                        incrementBy = fMinChildren;
+                        remainder -= fMaxChildren - fMinChildren;
+                    }
+                }
+                Node* n = allocateNode(level);
+                n->fNumChildren = 1;
+                *n->child(0) = (*branches)[currentBranch];
+                Branch b;
+                b.fBounds = (*branches)[currentBranch].fBounds;
+                b.fChild.subtree = n;
+                ++currentBranch;
+                for (int k = 1; k < incrementBy && currentBranch < branches->count(); ++k) {
+                    b.fBounds.join((*branches)[currentBranch].fBounds);
+                    *n->child(k) = (*branches)[currentBranch];
+                    ++n->fNumChildren;
+                    ++currentBranch;
+                }
+                (*branches)[newBranches] = b;
+                ++newBranches;
+            }
+        }
+        branches->setCount(newBranches);
+        return this->bulkLoad(branches, level + 1);
+    }
+}
+
+void SkRTree::validate() {
+#ifdef SK_DEBUG
+    if (this->isEmpty()) {
+        return;
+    }
+    SkASSERT(fCount == (size_t)this->validateSubtree(fRoot.fChild.subtree, fRoot.fBounds, true));
+#endif
+}
+
+int SkRTree::validateSubtree(Node* root, SkIRect bounds, bool isRoot) {
+    // make sure the pointer is pointing to a valid place
+    SkASSERT(fNodes.contains(static_cast<void*>(root)));
+
+    if (isRoot) {
+        // If the root of this subtree is the overall root, we have looser standards:
+        if (root->isLeaf()) {
+            SkASSERT(root->fNumChildren >= 1 && root->fNumChildren <= fMaxChildren);
+        } else {
+            SkASSERT(root->fNumChildren >= 2 && root->fNumChildren <= fMaxChildren);
+        }
+    } else {
+        SkASSERT(root->fNumChildren >= fMinChildren && root->fNumChildren <= fMaxChildren);
+    }
+
+    for (int i = 0; i < root->fNumChildren; ++i) {
+        SkASSERT(bounds.contains(root->child(i)->fBounds));
+    }
+
+    if (root->isLeaf()) {
+        SkASSERT(0 == root->fLevel);
+        return root->fNumChildren;
+    } else {
+        int childCount = 0;
+        for (int i = 0; i < root->fNumChildren; ++i) {
+            SkASSERT(root->child(i)->fChild.subtree->fLevel == root->fLevel - 1);
+            childCount += this->validateSubtree(root->child(i)->fChild.subtree,
+                                                root->child(i)->fBounds);
+        }
+        return childCount;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+static inline uint32_t get_area(const SkIRect& rect) {
+    return rect.width() * rect.height();
+}
+
+static inline uint32_t get_overlap(const SkIRect& rect1, const SkIRect& rect2) {
+    // I suspect there's a more efficient way of computing this...
+    return SkMax32(0, SkMin32(rect1.fRight, rect2.fRight) - SkMax32(rect1.fLeft, rect2.fLeft)) *
+           SkMax32(0, SkMin32(rect1.fBottom, rect2.fBottom) - SkMax32(rect1.fTop, rect2.fTop));
+}
+
+// Get the margin (aka perimeter)
+static inline uint32_t get_margin(const SkIRect& rect) {
+    return 2 * (rect.width() + rect.height());
+}
+
+static inline uint32_t get_overlap_increase(const SkIRect& rect1, const SkIRect& rect2,
+                                          SkIRect expandBy) {
+    join_no_empty_check(rect1, &expandBy);
+    return get_overlap(expandBy, rect2) - get_overlap(rect1, rect2);
+}
+
+static inline uint32_t get_area_increase(const SkIRect& rect1, SkIRect rect2) {
+    join_no_empty_check(rect1, &rect2);
+    return get_area(rect2) - get_area(rect1);
+}
+
+// Expand 'out' to include 'joinWith'
+static inline void join_no_empty_check(const SkIRect& joinWith, SkIRect* out) {
+    // since we check for empty bounds on insert, we know we'll never have empty rects
+    // and we can save the empty check that SkIRect::join requires
+    if (joinWith.fLeft < out->fLeft) { out->fLeft = joinWith.fLeft; }
+    if (joinWith.fTop < out->fTop) { out->fTop = joinWith.fTop; }
+    if (joinWith.fRight > out->fRight) { out->fRight = joinWith.fRight; }
+    if (joinWith.fBottom > out->fBottom) { out->fBottom = joinWith.fBottom; }
+}
diff --git a/src/core/SkRTree.h b/src/core/SkRTree.h
new file mode 100644
index 0000000..0a53667
--- /dev/null
+++ b/src/core/SkRTree.h
@@ -0,0 +1,191 @@
+
+/*
+ * 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 SkRTree_DEFINED
+#define SkRTree_DEFINED
+
+#include "SkRect.h"
+#include "SkTDArray.h"
+#include "SkChunkAlloc.h"
+#include "SkBBoxHierarchy.h"
+
+/**
+ * An R-Tree implementation. In short, it is a balanced n-ary tree containing a hierarchy of
+ * bounding rectangles.
+ *
+ * Much like a B-Tree it maintains balance by enforcing minimum and maximum child counts, and
+ * splitting nodes when they become overfull. Unlike B-trees, however, we're using spatial data; so
+ * there isn't a canonical ordering to use when choosing insertion locations and splitting
+ * distributions. A variety of heuristics have been proposed for these problems; here, we're using
+ * something resembling an R*-tree, which attempts to minimize area and overlap during insertion,
+ * and aims to minimize a combination of margin, overlap, and area when splitting.
+ *
+ * One detail that is thus far unimplemented that may improve tree quality is attempting to remove
+ * and reinsert nodes when they become full, instead of immediately splitting (nodes that may have
+ * been placed well early on may hurt the tree later when more nodes have been added; removing
+ * and reinserting nodes generally helps reduce overlap and make a better tree). Deletion of nodes
+ * is also unimplemented.
+ *
+ * For more details see:
+ *
+ *  Beckmann, N.; Kriegel, H. P.; Schneider, R.; Seeger, B. (1990). "The R*-tree:
+ *      an efficient and robust access method for points and rectangles"
+ *
+ * It also supports bulk-loading from a batch of bounds and values; if you don't require the tree
+ * to be usable in its intermediate states while it is being constructed, this is significantly
+ * quicker than individual insertions and produces more consistent trees.
+ */
+class SkRTree : public SkBBoxHierarchy {
+public:
+    SK_DECLARE_INST_COUNT(SkRTree)
+
+    /**
+     * Create a new R-Tree with specified min/max child counts.
+     * The child counts are valid iff:
+     * - (max + 1) / 2 >= min (splitting an overfull node must be enough to populate 2 nodes)
+     * - min < max
+     * - min > 0
+     * - max < SK_MaxU16
+     * If you have some prior information about the distribution of bounds you're expecting, you
+     * can provide an optional aspect ratio parameter. This allows the bulk-load algorithm to create
+     * better proportioned tiles of rectangles.
+     */
+    static SkRTree* Create(int minChildren, int maxChildren, SkScalar aspectRatio = 1);
+    virtual ~SkRTree();
+
+    /**
+     * Insert a node, consisting of bounds and a data value into the tree, if we don't immediately
+     * need to use the tree; we may allow the insert to be deferred (this can allow us to bulk-load
+     * a large batch of nodes at once, which tends to be faster and produce a better tree).
+     *  @param data The data value
+     *  @param bounds The corresponding bounding box
+     *  @param defer Can this insert be deferred? (this may be ignored)
+     */
+    virtual void insert(void* data, const SkIRect& bounds, bool defer = false);
+
+    /**
+     * If any inserts have been deferred, this will add them into the tree
+     */
+    virtual void flushDeferredInserts();
+
+    /**
+     * Given a query rectangle, populates the passed-in array with the elements it intersects
+     */
+    virtual void search(const SkIRect& query, SkTDArray<void*>* results);
+
+    virtual void clear();
+    bool isEmpty() const { return 0 == fCount; }
+    int getDepth() const { return this->isEmpty() ? 0 : fRoot.fChild.subtree->fLevel + 1; }
+
+    /**
+     * This gets the insertion count (rather than the node count)
+     */
+    virtual int getCount() const { return fCount; }
+
+private:
+
+    struct Node;
+
+    /**
+     * A branch of the tree, this may contain a pointer to another interior node, or a data value
+     */
+    struct Branch {
+        union {
+            Node* subtree;
+            void* data;
+        } fChild;
+        SkIRect fBounds;
+    };
+
+    /**
+     * A node in the tree, has between fMinChildren and fMaxChildren (the root is a special case)
+     */
+    struct Node {
+        uint16_t fNumChildren;
+        uint16_t fLevel;
+        bool isLeaf() { return 0 == fLevel; }
+        // Since we want to be able to pick min/max child counts at runtime, we assume the creator
+        // has allocated sufficient space directly after us in memory, and index into that space
+        Branch* child(size_t index) {
+            return reinterpret_cast<Branch*>(this + 1) + index;
+        }
+    };
+
+    typedef int32_t SkIRect::*SortSide;
+
+    // Helper for sorting our children arrays by sides of their rects
+    struct RectLessThan {
+        RectLessThan(SkRTree::SortSide side) : fSide(side) { }
+        bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) const {
+            return lhs.fBounds.*fSide < rhs.fBounds.*fSide;
+        }
+    private:
+        const SkRTree::SortSide fSide;
+    };
+
+    struct RectLessX {
+        bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) {
+            return ((lhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1) <
+                   ((rhs.fBounds.fRight - lhs.fBounds.fLeft) >> 1);
+        }
+    };
+
+    struct RectLessY {
+        bool operator()(const SkRTree::Branch lhs, const SkRTree::Branch rhs) {
+            return ((lhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1) <
+                   ((rhs.fBounds.fBottom - lhs.fBounds.fTop) >> 1);
+        }
+    };
+
+    SkRTree(int minChildren, int maxChildren, SkScalar aspectRatio);
+
+    /**
+     * Recursively descend the tree to find an insertion position for 'branch', updates
+     * bounding boxes on the way up.
+     */
+    Branch* insert(Node* root, Branch* branch, uint16_t level = 0);
+
+    int chooseSubtree(Node* root, Branch* branch);
+    SkIRect computeBounds(Node* n);
+    int distributeChildren(Branch* children);
+    void search(Node* root, const SkIRect query, SkTDArray<void*>* results) const;
+
+    /**
+     * This performs a bottom-up bulk load using the STR (sort-tile-recursive) algorithm, this
+     * seems to generally produce better, more consistent trees at significantly lower cost than
+     * repeated insertions.
+     *
+     * This consumes the input array.
+     *
+     * TODO: Experiment with other bulk-load algorithms (in particular the Hilbert pack variant,
+     * which groups rects by position on the Hilbert curve, is probably worth a look). There also
+     * exist top-down bulk load variants (VAMSplit, TopDownGreedy, etc).
+     */
+    Branch bulkLoad(SkTDArray<Branch>* branches, int level = 0);
+
+    void validate();
+    int validateSubtree(Node* root, SkIRect bounds, bool isRoot = false);
+
+    const int fMinChildren;
+    const int fMaxChildren;
+    const size_t fNodeSize;
+
+    // This is the count of data elements (rather than total nodes in the tree)
+    size_t fCount;
+
+    Branch fRoot;
+    SkChunkAlloc fNodes;
+    SkTDArray<Branch> fDeferredInserts;
+    SkScalar fAspectRatio;
+
+    Node* allocateNode(uint16_t level);
+
+    typedef SkBBoxHierarchy INHERITED;
+};
+
+#endif
diff --git a/src/core/SkRasterClip.cpp b/src/core/SkRasterClip.cpp
index 5a2447d..919fde9 100644
--- a/src/core/SkRasterClip.cpp
+++ b/src/core/SkRasterClip.cpp
@@ -10,33 +10,35 @@
 
 SkRasterClip::SkRasterClip() {
     fIsBW = true;
+    fIsEmpty = true;
+    fIsRect = false;
+    SkDEBUGCODE(this->validate();)
 }
 
 SkRasterClip::SkRasterClip(const SkRasterClip& src) {
     AUTO_RASTERCLIP_VALIDATE(src);
-    
+
     fIsBW = src.fIsBW;
     if (fIsBW) {
         fBW = src.fBW;
     } else {
         fAA = src.fAA;
     }
+
+    fIsEmpty = src.isEmpty();
+    fIsRect = src.isRect();
+    SkDEBUGCODE(this->validate();)
 }
 
 SkRasterClip::SkRasterClip(const SkIRect& bounds) : fBW(bounds) {
     fIsBW = true;
+    fIsEmpty = this->computeIsEmpty();  // bounds might be empty, so compute
+    fIsRect = !fIsEmpty;
+    SkDEBUGCODE(this->validate();)
 }
 
 SkRasterClip::~SkRasterClip() {
-    AUTO_RASTERCLIP_VALIDATE(*this);
-}
-
-bool SkRasterClip::isEmpty() const {
-    return fIsBW ? fBW.isEmpty() : fAA.isEmpty();
-}
-
-bool SkRasterClip::isRect() const {
-    return fIsBW ? fBW.isRect() : false;
+    SkDEBUGCODE(this->validate();)
 }
 
 bool SkRasterClip::isComplex() const {
@@ -53,30 +55,35 @@
     fIsBW = true;
     fBW.setEmpty();
     fAA.setEmpty();
+    fIsEmpty = true;
+    fIsRect = false;
     return false;
 }
 
 bool SkRasterClip::setRect(const SkIRect& rect) {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    
+
     fIsBW = true;
     fAA.setEmpty();
-    return fBW.setRect(rect);
+    fIsRect = fBW.setRect(rect);
+    fIsEmpty = !fIsRect;
+    return fIsRect;
 }
 
 bool SkRasterClip::setPath(const SkPath& path, const SkRegion& clip, bool doAA) {
     AUTO_RASTERCLIP_VALIDATE(*this);
 
     if (this->isBW() && !doAA) {
-        return fBW.setPath(path, clip);
+        (void)fBW.setPath(path, clip);
     } else {
         // TODO: since we are going to over-write fAA completely (aren't we?)
         // we should just clear our BW data (if any) and set fIsAA=true
         if (this->isBW()) {
             this->convertToAA();
         }
-        return fAA.setPath(path, &clip, doAA);
+        (void)fAA.setPath(path, &clip, doAA);
     }
+    return this->updateCacheAndReturnNonEmpty();
 }
 
 bool SkRasterClip::setPath(const SkPath& path, const SkIRect& clip, bool doAA) {
@@ -101,20 +108,22 @@
 
 bool SkRasterClip::op(const SkIRect& rect, SkRegion::Op op) {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    
-    return fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
+
+    fIsBW ? fBW.op(rect, op) : fAA.op(rect, op);
+    return this->updateCacheAndReturnNonEmpty();
 }
 
 bool SkRasterClip::op(const SkRegion& rgn, SkRegion::Op op) {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    
+
     if (fIsBW) {
-        return fBW.op(rgn, op);
+        (void)fBW.op(rgn, op);
     } else {
         SkAAClip tmp;
         tmp.setRegion(rgn);
-        return fAA.op(tmp, op);
+        (void)fAA.op(tmp, op);
     }
+    return this->updateCacheAndReturnNonEmpty();
 }
 
 bool SkRasterClip::op(const SkRasterClip& clip, SkRegion::Op op) {
@@ -122,7 +131,7 @@
     clip.validate();
 
     if (this->isBW() && clip.isBW()) {
-        return fBW.op(clip.fBW, op);
+        (void)fBW.op(clip.fBW, op);
     } else {
         SkAAClip tmp;
         const SkAAClip* other;
@@ -136,25 +145,32 @@
         } else {
             other = &clip.aaRgn();
         }
-        return fAA.op(*other, op);
+        (void)fAA.op(*other, op);
     }
+    return this->updateCacheAndReturnNonEmpty();
 }
 
-// return true if x is nearly integral (within 1/16) since that is the highest
-// precision our aa code can have.
-static bool is_integral(SkScalar x) {
-    int ix = SkScalarRoundToInt(x);
-    SkScalar sx = SkIntToScalar(ix);
-    return SkScalarAbs(sx - x) < (SK_Scalar1 / 16);
+/**
+ *  Our antialiasing currently has a granularity of 1/4 of a pixel along each
+ *  axis. Thus we can treat an axis coordinate as an integer if it differs
+ *  from its nearest int by < half of that value (1.8 in this case).
+ */
+static bool nearly_integral(SkScalar x) {
+    static const SkScalar domain = SK_Scalar1 / 4;
+    static const SkScalar halfDomain = domain / 2;
+
+    x += halfDomain;
+    return x - SkScalarFloorToScalar(x) < domain;
 }
 
 bool SkRasterClip::op(const SkRect& r, SkRegion::Op op, bool doAA) {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    
-    if (doAA) {
-        // check that the rect really needs aa
-        if (is_integral(r.fLeft) && is_integral(r.fTop) &&
-            is_integral(r.fRight) && is_integral(r.fBottom)) {
+
+    if (fIsBW && doAA) {
+        // check that the rect really needs aa, or is it close enought to
+        // integer boundaries that we can just treat it as a BW rect?
+        if (nearly_integral(r.fLeft) && nearly_integral(r.fTop) &&
+            nearly_integral(r.fRight) && nearly_integral(r.fBottom)) {
             doAA = false;
         }
     }
@@ -162,13 +178,14 @@
     if (fIsBW && !doAA) {
         SkIRect ir;
         r.round(&ir);
-        return fBW.op(ir, op);
+        (void)fBW.op(ir, op);
     } else {
         if (fIsBW) {
             this->convertToAA();
         }
-        return fAA.op(r, op, doAA);
+        (void)fAA.op(r, op, doAA);
     }
+    return this->updateCacheAndReturnNonEmpty();
 }
 
 void SkRasterClip::translate(int dx, int dy, SkRasterClip* dst) const {
@@ -177,7 +194,7 @@
     }
 
     AUTO_RASTERCLIP_VALIDATE(*this);
-    
+
     if (this->isEmpty()) {
         dst->setEmpty();
         return;
@@ -195,6 +212,7 @@
         fAA.translate(dx, dy, &dst->fAA);
         dst->fBW.setEmpty();
     }
+    dst->updateCacheAndReturnNonEmpty();
 }
 
 bool SkRasterClip::quickContains(const SkIRect& ir) const {
@@ -205,7 +223,7 @@
 
 const SkRegion& SkRasterClip::forceGetBW() {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    
+
     if (!fIsBW) {
         fBW.setRect(fAA.getBounds());
     }
@@ -214,10 +232,11 @@
 
 void SkRasterClip::convertToAA() {
     AUTO_RASTERCLIP_VALIDATE(*this);
-    
+
     SkASSERT(fIsBW);
     fAA.setRegion(fBW);
     fIsBW = false;
+    (void)this->updateCacheAndReturnNonEmpty();
 }
 
 #ifdef SK_DEBUG
@@ -229,6 +248,9 @@
 
     fBW.validate();
     fAA.validate();
+
+    SkASSERT(this->computeIsEmpty() == fIsEmpty);
+    SkASSERT(this->computeIsRect() == fIsRect);
 }
 #endif
 
@@ -269,4 +291,3 @@
         fBlitter = &fAABlitter;
     }
 }
-
diff --git a/src/core/SkRasterClip.h b/src/core/SkRasterClip.h
index 8f18270..7e015aa 100644
--- a/src/core/SkRasterClip.h
+++ b/src/core/SkRasterClip.h
@@ -23,8 +23,16 @@
     const SkRegion& bwRgn() const { SkASSERT(fIsBW); return fBW; }
     const SkAAClip& aaRgn() const { SkASSERT(!fIsBW); return fAA; }
 
-    bool isEmpty() const;
-    bool isRect() const;
+    bool isEmpty() const {
+        SkASSERT(this->computeIsEmpty() == fIsEmpty);
+        return fIsEmpty;
+    }
+
+    bool isRect() const {
+        SkASSERT(this->computeIsRect() == fIsRect);
+        return fIsRect;
+    }
+
     bool isComplex() const;
     const SkIRect& getBounds() const;
 
@@ -49,7 +57,7 @@
     bool quickContains(int left, int top, int right, int bottom) const {
         return quickContains(SkIRect::MakeLTRB(left, top, right, bottom));
     }
-    
+
     /**
      *  Return true if this region is empty, or if the specified rectangle does
      *  not intersect the region. Returning false is not a guarantee that they
@@ -59,7 +67,7 @@
         return this->isEmpty() || rect.isEmpty() ||
                !SkIRect::Intersects(this->getBounds(), rect);
     }
-    
+
     // hack for SkCanvas::getTotalClip
     const SkRegion& forceGetBW();
 
@@ -73,6 +81,23 @@
     SkRegion    fBW;
     SkAAClip    fAA;
     bool        fIsBW;
+    // these 2 are caches based on querying the right obj based on fIsBW
+    bool        fIsEmpty;
+    bool        fIsRect;
+
+    bool computeIsEmpty() const {
+        return fIsBW ? fBW.isEmpty() : fAA.isEmpty();
+    }
+
+    bool computeIsRect() const {
+        return fIsBW ? fBW.isRect() : false;
+    }
+
+    bool updateCacheAndReturnNonEmpty() {
+        fIsEmpty = this->computeIsEmpty();
+        fIsRect = this->computeIsRect();
+        return !fIsEmpty;
+    }
 
     void convertToAA();
 };
@@ -111,7 +136,7 @@
     SkAAClipBlitterWrapper();
     SkAAClipBlitterWrapper(const SkRasterClip&, SkBlitter*);
     SkAAClipBlitterWrapper(const SkAAClip*, SkBlitter*);
-    
+
     void init(const SkRasterClip&, SkBlitter*);
 
     const SkIRect& getBounds() const {
@@ -126,7 +151,7 @@
         SkASSERT(fBlitter);
         return fBlitter;
     }
-    
+
 private:
     const SkAAClip* fAAClip;
     SkRegion        fBWRgn;
diff --git a/src/core/SkRasterizer.cpp b/src/core/SkRasterizer.cpp
index 7ccced8..a65d541 100644
--- a/src/core/SkRasterizer.cpp
+++ b/src/core/SkRasterizer.cpp
@@ -12,18 +12,17 @@
 #include "SkMaskFilter.h"
 #include "SkPath.h"
 
-// do nothing for now, since we don't store anything at flatten time
-SkRasterizer::SkRasterizer(SkFlattenableReadBuffer&) {}
+SK_DEFINE_INST_COUNT(SkRasterizer)
 
 bool SkRasterizer::rasterize(const SkPath& fillPath, const SkMatrix& matrix,
                              const SkIRect* clipBounds, SkMaskFilter* filter,
-                             SkMask* mask, SkMask::CreateMode mode) {
+                             SkMask* mask, SkMask::CreateMode mode) const {
     SkIRect storage;
-    
-    if (clipBounds && filter && SkMask::kJustRenderImage_CreateMode != mode) {        
+
+    if (clipBounds && filter && SkMask::kJustRenderImage_CreateMode != mode) {
         SkIPoint    margin;
         SkMask      srcM, dstM;
-        
+
         srcM.fFormat = SkMask::kA8_Format;
         srcM.fBounds.set(0, 0, 1, 1);
         srcM.fImage = NULL;
@@ -34,7 +33,7 @@
         storage.inset(-margin.fX, -margin.fY);
         clipBounds = &storage;
     }
-    
+
     return this->onRasterize(fillPath, matrix, clipBounds, mask, mode);
 }
 
@@ -42,10 +41,10 @@
 */
 bool SkRasterizer::onRasterize(const SkPath& fillPath, const SkMatrix& matrix,
                              const SkIRect* clipBounds,
-                             SkMask* mask, SkMask::CreateMode mode) {
+                             SkMask* mask, SkMask::CreateMode mode) const {
     SkPath  devPath;
-    
-    fillPath.transform(matrix, &devPath);
-    return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode);
-}
 
+    fillPath.transform(matrix, &devPath);
+    return SkDraw::DrawToMask(devPath, clipBounds, NULL, NULL, mask, mode,
+                              SkPaint::kFill_Style);
+}
diff --git a/src/core/SkRect.cpp b/src/core/SkRect.cpp
index 5e3d93c..c62f2f3 100644
--- a/src/core/SkRect.cpp
+++ b/src/core/SkRect.cpp
@@ -61,9 +61,20 @@
     #define SkFLOATCODE(code)
 #endif
 
-void SkRect::set(const SkPoint pts[], int count) {
+// For float compares (at least on x86, by removing the else from the min/max
+// computation, we get MAXSS and MINSS instructions, and no branches.
+// Fixed point has no such opportunity (afaik), so we leave the else in that case
+#ifdef SK_SCALAR_IS_FLOAT
+    #define MINMAX_ELSE
+#else
+    #define MINMAX_ELSE else
+#endif
+
+bool SkRect::setBoundsCheck(const SkPoint pts[], int count) {
     SkASSERT((pts && count > 0) || count == 0);
 
+    bool isFinite = true;
+
     if (count <= 0) {
         sk_bzero(this, sizeof(SkRect));
     } else {
@@ -86,29 +97,37 @@
                   Sk2sComplimentAsScalar(b));
 #else
         SkScalar    l, t, r, b;
-        SkFLOATCODE(int isNaN;)
 
         l = r = pts[0].fX;
         t = b = pts[0].fY;
-        SkFLOATCODE(isNaN = (l != l) | (t != t);)
+
+        // If all of the points are finite, accum should stay 0. If we encounter
+        // a NaN or infinity, then accum should become NaN.
+        SkFLOATCODE(float accum = 0;)
+        SkFLOATCODE(accum *= l; accum *= t;)
 
         for (int i = 1; i < count; i++) {
             SkScalar x = pts[i].fX;
             SkScalar y = pts[i].fY;
-            SkFLOATCODE(isNaN |= (x != x) | (y != y);)
 
-            if (x < l) l = x; else if (x > r) r = x;
-            if (y < t) t = y; else if (y > b) b = y;
+            SkFLOATCODE(accum *= x; accum *= y;)
+
+            if (x < l) l = x; MINMAX_ELSE if (x > r) r = x;
+            if (y < t) t = y; MINMAX_ELSE if (y > b) b = y;
         }
 
 #ifdef SK_SCALAR_IS_FLOAT
-        if (isNaN) {
+        SkASSERT(!accum || !SkScalarIsFinite(accum));
+        if (accum) {
             l = t = r = b = 0;
+            isFinite = false;
         }
 #endif
         this->set(l, t, r, b);
 #endif
     }
+
+    return isFinite;
 }
 
 bool SkRect::intersect(SkScalar left, SkScalar top, SkScalar right,
@@ -162,4 +181,3 @@
         if (bottom > fBottom) fBottom = bottom;
     }
 }
-
diff --git a/src/core/SkRefCnt.cpp b/src/core/SkRefCnt.cpp
new file mode 100644
index 0000000..8d09065
--- /dev/null
+++ b/src/core/SkRefCnt.cpp
@@ -0,0 +1,13 @@
+/*
+ * 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 "SkRefCnt.h"
+#include "SkWeakRefCnt.h"
+
+SK_DEFINE_INST_COUNT(SkRefCnt)
+SK_DEFINE_INST_COUNT(SkWeakRefCnt)
diff --git a/src/core/SkRefDict.cpp b/src/core/SkRefDict.cpp
index 53b099b..50a469d 100644
--- a/src/core/SkRefDict.cpp
+++ b/src/core/SkRefDict.cpp
@@ -86,4 +86,3 @@
     }
     fImpl = NULL;
 }
-
diff --git a/src/core/SkRefDict.h b/src/core/SkRefDict.h
new file mode 100644
index 0000000..55b9bfe
--- /dev/null
+++ b/src/core/SkRefDict.h
@@ -0,0 +1,54 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkRefDict_DEFINED
+#define SkRefDict_DEFINED
+
+#include "SkRefCnt.h"
+
+/**
+ *  A dictionary of string,refcnt pairs. The dictionary is also an owner of the
+ *  refcnt objects while they are contained.
+ */
+class SK_API SkRefDict : SkNoncopyable {
+public:
+    SkRefDict();
+    ~SkRefDict();
+
+    /**
+     *  Return the data associated with name[], or NULL if no matching entry
+     *  is found. The reference-count of the entry is not affected.
+     */
+    SkRefCnt* find(const char name[]) const;
+
+    /**
+     *  If data is NULL, remove (if present) the entry matching name and call
+     *  prev_data->unref() on the data for the matching entry.
+     *  If data is not-NULL, replace the existing entry matching name and
+     *  call (prev_data->unref()), or add a new one. In either case,
+     *  data->ref() is called.
+     */
+    void set(const char name[], SkRefCnt* data);
+
+    /**
+     *  Remove the matching entry (if found) and unref its data.
+     */
+    void remove(const char name[]) { this->set(name, NULL); }
+
+    /**
+     *  Remove all entries, and unref() their associated data.
+     */
+    void removeAll();
+
+private:
+    struct Impl;
+    Impl* fImpl;
+};
+
+#endif
diff --git a/src/core/SkRegion.cpp b/src/core/SkRegion.cpp
index dee652b..936c87e 100644
--- a/src/core/SkRegion.cpp
+++ b/src/core/SkRegion.cpp
@@ -10,64 +10,60 @@
 #include "SkRegionPriv.h"
 #include "SkTemplates.h"
 #include "SkThread.h"
+#include "SkUtils.h"
+
+/* Region Layout
+ *
+ *  TOP
+ *
+ *  [ Bottom, X-Intervals, [Left, Right]..., X-Sentinel ]
+ *  ...
+ *
+ *  Y-Sentinel
+ */
 
 SkDEBUGCODE(int32_t gRgnAllocCounter;)
 
 /////////////////////////////////////////////////////////////////////////////////////////////////
 
-/*  Pass in a scanline, beginning with the Left value of the pair (i.e. not the Y beginning)
-*/
-static SkRegion::RunType* skip_scanline(const SkRegion::RunType runs[])
-{
-    while (runs[0] != SkRegion::kRunTypeSentinel)
-    {
-        SkASSERT(runs[0] < runs[1]);    // valid span
-        runs += 2;
+/*  Pass in the beginning with the intervals.
+ *  We back up 1 to read the interval-count.
+ *  Return the beginning of the next scanline (i.e. the next Y-value)
+ */
+static SkRegion::RunType* skip_intervals(const SkRegion::RunType runs[]) {
+    int intervals = runs[-1];
+#ifdef SK_DEBUG
+    if (intervals > 0) {
+        SkASSERT(runs[0] < runs[1]);
+        SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
+    } else {
+        SkASSERT(0 == intervals);
+        SkASSERT(SkRegion::kRunTypeSentinel == runs[0]);
     }
-    return (SkRegion::RunType*)(runs + 1);  // return past the X-sentinel
+#endif
+    runs += intervals * 2 + 1;
+    return const_cast<SkRegion::RunType*>(runs);
 }
 
-// returns true if runs are just a rect
-bool SkRegion::ComputeRunBounds(const SkRegion::RunType runs[], int count, SkIRect* bounds)
-{
+bool SkRegion::RunsAreARect(const SkRegion::RunType runs[], int count,
+                            SkIRect* bounds) {
     assert_sentinel(runs[0], false);    // top
+    SkASSERT(count >= kRectRegionRuns);
 
-    if (count == kRectRegionRuns)
-    {
+    if (count == kRectRegionRuns) {
         assert_sentinel(runs[1], false);    // bottom
-        assert_sentinel(runs[2], false);    // left
-        assert_sentinel(runs[3], false);    // right
-        assert_sentinel(runs[4], true);
+        SkASSERT(1 == runs[2]);
+        assert_sentinel(runs[3], false);    // left
+        assert_sentinel(runs[4], false);    // right
         assert_sentinel(runs[5], true);
+        assert_sentinel(runs[6], true);
 
         SkASSERT(runs[0] < runs[1]);    // valid height
-        SkASSERT(runs[2] < runs[3]);    // valid width
+        SkASSERT(runs[3] < runs[4]);    // valid width
 
-        bounds->set(runs[2], runs[0], runs[3], runs[1]);
+        bounds->set(runs[3], runs[0], runs[4], runs[1]);
         return true;
     }
-
-    int left = SK_MaxS32;
-    int rite = SK_MinS32;
-    int bot;
-
-    bounds->fTop = *runs++;
-    do {
-        bot = *runs++;
-        if (*runs < SkRegion::kRunTypeSentinel)
-        {
-            if (left > *runs)
-                left = *runs;
-            runs = skip_scanline(runs);
-            if (rite < runs[-2])
-                rite = runs[-2];
-        }
-        else
-            runs += 1;  // skip X-sentinel
-    } while (runs[0] < SkRegion::kRunTypeSentinel);
-    bounds->fLeft = left;
-    bounds->fRight = rite;
-    bounds->fBottom = bot;
     return false;
 }
 
@@ -93,7 +89,7 @@
 }
 
 void SkRegion::freeRuns() {
-    if (fRunHead->isComplex()) {
+    if (this->isComplex()) {
         SkASSERT(fRunHead->fRefCnt >= 1);
         if (sk_atomic_dec(&fRunHead->fRefCnt) == 1) {
             //SkASSERT(gRgnAllocCounter > 0);
@@ -104,10 +100,20 @@
     }
 }
 
+void SkRegion::allocateRuns(int count, int ySpanCount, int intervalCount) {
+    fRunHead = RunHead::Alloc(count, ySpanCount, intervalCount);
+}
+
 void SkRegion::allocateRuns(int count) {
     fRunHead = RunHead::Alloc(count);
 }
 
+void SkRegion::allocateRuns(const RunHead& head) {
+    fRunHead = RunHead::Alloc(head.fRunCount,
+                              head.getYSpanCount(),
+                              head.getIntervalCount());
+}
+
 SkRegion& SkRegion::operator=(const SkRegion& src) {
     (void)this->setRegion(src);
     return *this;
@@ -146,7 +152,7 @@
 
         fBounds = src.fBounds;
         fRunHead = src.fRunHead;
-        if (fRunHead->isComplex()) {
+        if (this->isComplex()) {
             sk_atomic_inc(&fRunHead->fRefCnt);
         }
     }
@@ -168,8 +174,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifdef SK_BUILD_FOR_ANDROID
-char* SkRegion::toString()
-{
+char* SkRegion::toString() {
     Iterator iter(*this);
     int count = 0;
     while (!iter.done()) {
@@ -194,12 +199,10 @@
 }
 #endif
 
-//////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 
-int SkRegion::count_runtype_values(int* itop, int* ibot) const
-{
-    if (this == NULL)
-    {
+int SkRegion::count_runtype_values(int* itop, int* ibot) const {
+    if (this == NULL) {
         *itop = SK_MinS32;
         *ibot = SK_MaxS32;
         return 0;
@@ -207,36 +210,26 @@
 
     int maxT;
 
-    if (this->isRect())
+    if (this->isRect()) {
         maxT = 2;
-    else
-    {
+    } else {
         SkASSERT(this->isComplex());
-        // skip the top
-        const RunType*  runs = fRunHead->readonly_runs() + 1;
-        maxT = 0;
-
-        do {
-            const RunType* next = skip_scanline(runs + 1);
-            SkASSERT(next > runs);
-            int         T = (int)(next - runs - 1);
-            if (maxT < T)
-                maxT = T;
-            runs = next;
-        } while (runs[0] < SkRegion::kRunTypeSentinel);
+        maxT = fRunHead->getIntervalCount() * 2;
     }
     *itop = fBounds.fTop;
     *ibot = fBounds.fBottom;
     return maxT;
 }
 
-bool SkRegion::setRuns(RunType runs[], int count)
-{
+static bool isRunCountEmpty(int count) {
+    return count <= 2;
+}
+
+bool SkRegion::setRuns(RunType runs[], int count) {
     SkDEBUGCODE(this->validate();)
     SkASSERT(count > 0);
 
-    if (count <= 2)
-    {
+    if (isRunCountEmpty(count)) {
     //  SkDEBUGF(("setRuns: empty\n"));
         assert_sentinel(runs[count-1], true);
         return this->setEmpty();
@@ -244,75 +237,56 @@
 
     // trim off any empty spans from the top and bottom
     // weird I should need this, perhaps op() could be smarter...
-    if (count > kRectRegionRuns)
-    {
+    if (count > kRectRegionRuns) {
         RunType* stop = runs + count;
         assert_sentinel(runs[0], false);    // top
         assert_sentinel(runs[1], false);    // bottom
-        if (runs[2] == SkRegion::kRunTypeSentinel)    // should be first left...
-        {
-            runs += 2;  // skip empty initial span
-            runs[0] = runs[-1]; // set new top to prev bottom
+        // runs[2] is uncomputed intervalCount
+
+        if (runs[3] == SkRegion::kRunTypeSentinel) {  // should be first left...
+            runs += 3;  // skip empty initial span
+            runs[0] = runs[-2]; // set new top to prev bottom
             assert_sentinel(runs[1], false);    // bot: a sentinal would mean two in a row
-            assert_sentinel(runs[2], false);    // left
-            assert_sentinel(runs[3], false);    // right
+            assert_sentinel(runs[2], false);    // intervalcount
+            assert_sentinel(runs[3], false);    // left
+            assert_sentinel(runs[4], false);    // right
         }
 
-        // now check for a trailing empty span
         assert_sentinel(stop[-1], true);
         assert_sentinel(stop[-2], true);
-        assert_sentinel(stop[-3], false);   // should be last right
-        if (stop[-4] == SkRegion::kRunTypeSentinel)   // eek, stop[-3] was a bottom with no x-runs
-        {
-            stop[-3] = SkRegion::kRunTypeSentinel;    // kill empty last span
-            stop -= 2;
-            assert_sentinel(stop[-1], true);
-            assert_sentinel(stop[-2], true);
-            assert_sentinel(stop[-3], false);
-            assert_sentinel(stop[-4], false);
-            assert_sentinel(stop[-5], false);
+
+        // now check for a trailing empty span
+        if (stop[-5] == SkRegion::kRunTypeSentinel) { // eek, stop[-4] was a bottom with no x-runs
+            stop[-4] = SkRegion::kRunTypeSentinel;    // kill empty last span
+            stop -= 3;
+            assert_sentinel(stop[-1], true);    // last y-sentinel
+            assert_sentinel(stop[-2], true);    // last x-sentinel
+            assert_sentinel(stop[-3], false);   // last right
+            assert_sentinel(stop[-4], false);   // last left
+            assert_sentinel(stop[-5], false);   // last interval-count
+            assert_sentinel(stop[-6], false);   // last bottom
         }
         count = (int)(stop - runs);
     }
 
     SkASSERT(count >= kRectRegionRuns);
 
-    if (ComputeRunBounds(runs, count, &fBounds))
-    {
-    //  SkDEBUGF(("setRuns: rect[%d %d %d %d]\n", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom));
+    if (SkRegion::RunsAreARect(runs, count, &fBounds)) {
         return this->setRect(fBounds);
     }
 
     //  if we get here, we need to become a complex region
 
-    if (!fRunHead->isComplex() || fRunHead->fRunCount != count)
-    {
-#ifdef SK_DEBUGx
-        SkDebugf("setRuns: rgn [");
-        {
-            const RunType* r = runs;
-
-            SkDebugf(" top: %d\n", *r++);
-            while (*r < SkRegion::kRunTypeSentinel)
-            {
-                SkDebugf(" bottom: %d", *r++);
-                while (*r < SkRegion::kRunTypeSentinel)
-                {
-                    SkDebugf(" [%d %d]", r[0], r[1]);
-                    r += 2;
-                }
-                SkDebugf("\n");
-            }
-        }
-#endif
+    if (!this->isComplex() || fRunHead->fRunCount != count) {
         this->freeRuns();
         this->allocateRuns(count);
     }
-    
+
     // must call this before we can write directly into runs()
     // in case we are sharing the buffer with another region (copy on write)
     fRunHead = fRunHead->ensureWritable();
     memcpy(fRunHead->writable_runs(), runs, count * sizeof(RunType));
+    fRunHead->computeRunBounds(&fBounds);
 
     SkDEBUGCODE(this->validate();)
 
@@ -320,140 +294,210 @@
 }
 
 void SkRegion::BuildRectRuns(const SkIRect& bounds,
-                             RunType runs[kRectRegionRuns])
-{
+                             RunType runs[kRectRegionRuns]) {
     runs[0] = bounds.fTop;
     runs[1] = bounds.fBottom;
-    runs[2] = bounds.fLeft;
-    runs[3] = bounds.fRight;
-    runs[4] = kRunTypeSentinel;
+    runs[2] = 1;    // 1 interval for this scanline
+    runs[3] = bounds.fLeft;
+    runs[4] = bounds.fRight;
     runs[5] = kRunTypeSentinel;
+    runs[6] = kRunTypeSentinel;
 }
 
-static SkRegion::RunType* find_scanline(const SkRegion::RunType runs[], int y)
-{
-    SkASSERT(y >= runs[0]); // if this fails, we didn't do a quick check on the boudns
+bool SkRegion::contains(int32_t x, int32_t y) const {
+    SkDEBUGCODE(this->validate();)
 
-    runs += 1;  // skip top-Y
-    for (;;)
-    {
-        if (runs[0] == SkRegion::kRunTypeSentinel)
-            break;
-        if (y < runs[0])
-            return (SkRegion::RunType*)&runs[1];
-        runs = skip_scanline(runs + 1); // skip the Y value before calling
-    }
-    return NULL;
-}
-
-bool SkRegion::contains(int32_t x, int32_t y) const
-{
-    if (!fBounds.contains(x, y))
+    if (!fBounds.contains(x, y)) {
         return false;
-
-    if (this->isRect())
+    }
+    if (this->isRect()) {
         return true;
-
+    }
     SkASSERT(this->isComplex());
-    const RunType* runs = find_scanline(fRunHead->readonly_runs(), y);
 
-    if (runs)
-    {   for (;;)
-        {   if (x < runs[0])
-                break;
-            if (x < runs[1])
-                return true;
-            runs += 2;
+    const RunType* runs = fRunHead->findScanline(y);
+
+    // Skip the Bottom and IntervalCount
+    runs += 2;
+
+    // Just walk this scanline, checking each interval. The X-sentinel will
+    // appear as a left-inteval (runs[0]) and should abort the search.
+    //
+    // We could do a bsearch, using interval-count (runs[1]), but need to time
+    // when that would be worthwhile.
+    //
+    for (;;) {
+        if (x < runs[0]) {
+            break;
         }
+        if (x < runs[1]) {
+            return true;
+        }
+        runs += 2;
     }
     return false;
 }
 
-bool SkRegion::contains(const SkIRect& r) const
-{
-    SkRegion tmp(r);
-    
-    return this->contains(tmp);
+static SkRegion::RunType scanline_bottom(const SkRegion::RunType runs[]) {
+    return runs[0];
 }
 
-bool SkRegion::contains(const SkRegion& rgn) const
-{
-    if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds))
+static const SkRegion::RunType* scanline_next(const SkRegion::RunType runs[]) {
+    // skip [B N [L R]... S]
+    return runs + 2 + runs[1] * 2 + 1;
+}
+
+static bool scanline_contains(const SkRegion::RunType runs[],
+                              SkRegion::RunType L, SkRegion::RunType R) {
+    runs += 2;  // skip Bottom and IntervalCount
+    for (;;) {
+        if (L < runs[0]) {
+            break;
+        }
+        if (R <= runs[1]) {
+            return true;
+        }
+        runs += 2;
+    }
+    return false;
+}
+
+bool SkRegion::contains(const SkIRect& r) const {
+    SkDEBUGCODE(this->validate();)
+
+    if (!fBounds.contains(r)) {
         return false;
-
-    if (this->isRect())
+    }
+    if (this->isRect()) {
         return true;
+    }
+    SkASSERT(this->isComplex());
 
-    SkRegion    tmp;
-    
-    tmp.op(*this, rgn, kUnion_Op);
-    return tmp == *this;
+    const RunType* scanline = fRunHead->findScanline(r.fTop);
+    for (;;) {
+        if (!scanline_contains(scanline, r.fLeft, r.fRight)) {
+            return false;
+        }
+        if (r.fBottom <= scanline_bottom(scanline)) {
+            break;
+        }
+        scanline = scanline_next(scanline);
+    }
+    return true;
 }
 
-const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[], int* count) const
-{
-    SkASSERT(tmpStorage && count);
+bool SkRegion::contains(const SkRegion& rgn) const {
+    SkDEBUGCODE(this->validate();)
+    SkDEBUGCODE(rgn.validate();)
+
+    if (this->isEmpty() || rgn.isEmpty() || !fBounds.contains(rgn.fBounds)) {
+        return false;
+    }
+    if (this->isRect()) {
+        return true;
+    }
+    if (rgn.isRect()) {
+        return this->contains(rgn.getBounds());
+    }
+
+    /*
+     *  A contains B is equivalent to
+     *  B - A == 0
+     */
+    return !Oper(rgn, *this, kDifference_Op, NULL);
+}
+
+const SkRegion::RunType* SkRegion::getRuns(RunType tmpStorage[],
+                                           int* intervals) const {
+    SkASSERT(tmpStorage && intervals);
     const RunType* runs = tmpStorage;
 
-    if (this->isEmpty())
-    {
+    if (this->isEmpty()) {
         tmpStorage[0] = kRunTypeSentinel;
-        *count = 1;
-    }
-    else if (this->isRect())
-    {
+        *intervals = 0;
+    } else if (this->isRect()) {
         BuildRectRuns(fBounds, tmpStorage);
-        *count = kRectRegionRuns;
-    }
-    else
-    {
-        *count = fRunHead->fRunCount;
+        *intervals = 1;
+    } else {
         runs = fRunHead->readonly_runs();
+        *intervals = fRunHead->getIntervalCount();
     }
     return runs;
 }
 
-/////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+static bool scanline_intersects(const SkRegion::RunType runs[],
+                                SkRegion::RunType L, SkRegion::RunType R) {
+    runs += 2;  // skip Bottom and IntervalCount
+    for (;;) {
+        if (R <= runs[0]) {
+            break;
+        }
+        if (L < runs[1]) {
+            return true;
+        }
+        runs += 2;
+    }
+    return false;
+}
 
 bool SkRegion::intersects(const SkIRect& r) const {
+    SkDEBUGCODE(this->validate();)
+
     if (this->isEmpty() || r.isEmpty()) {
         return false;
     }
-    
-    if (!SkIRect::Intersects(fBounds, r)) {
+
+    SkIRect sect;
+    if (!sect.intersect(fBounds, r)) {
         return false;
     }
-
     if (this->isRect()) {
         return true;
     }
-    
-    // we are complex
-    SkRegion tmp;
-    return tmp.op(*this, r, kIntersect_Op);
+    SkASSERT(this->isComplex());
+
+    const RunType* scanline = fRunHead->findScanline(sect.fTop);
+    for (;;) {
+        if (scanline_intersects(scanline, sect.fLeft, sect.fRight)) {
+            return true;
+        }
+        if (sect.fBottom <= scanline_bottom(scanline)) {
+            break;
+        }
+        scanline = scanline_next(scanline);
+    }
+    return false;
 }
 
 bool SkRegion::intersects(const SkRegion& rgn) const {
     if (this->isEmpty() || rgn.isEmpty()) {
         return false;
     }
-    
+
     if (!SkIRect::Intersects(fBounds, rgn.fBounds)) {
         return false;
     }
-    
-    if (this->isRect() && rgn.isRect()) {
+
+    bool weAreARect = this->isRect();
+    bool theyAreARect = rgn.isRect();
+
+    if (weAreARect && theyAreARect) {
         return true;
     }
-    
-    // one or both of us is complex
-    // TODO: write a faster version that aborts as soon as we write the first
-    //       non-empty span, to avoid build the entire result
-    SkRegion tmp;
-    return tmp.op(*this, rgn, kIntersect_Op);
+    if (weAreARect) {
+        return rgn.intersects(this->getBounds());
+    }
+    if (theyAreARect) {
+        return this->intersects(rgn.getBounds());
+    }
+
+    // both of us are complex
+    return Oper(*this, rgn, kIntersect_Op, NULL);
 }
 
-/////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 
 bool SkRegion::operator==(const SkRegion& b) const {
     SkDEBUGCODE(validate();)
@@ -465,7 +509,7 @@
     if (fBounds != b.fBounds) {
         return false;
     }
-    
+
     const SkRegion::RunHead* ah = fRunHead;
     const SkRegion::RunHead* bh = b.fRunHead;
 
@@ -474,7 +518,7 @@
         return true;
     }
     // now we insist that both are complex (but different ptrs)
-    if (!ah->isComplex() || !bh->isComplex()) {
+    if (!this->isComplex() || !b.isComplex()) {
         return false;
     }
     return  ah->fRunCount == bh->fRunCount &&
@@ -498,13 +542,13 @@
             dst->fRunHead = dst->fRunHead->ensureWritable();
         } else {
             SkRegion    tmp;
-            tmp.allocateRuns(fRunHead->fRunCount);
+            tmp.allocateRuns(*fRunHead);
             tmp.fBounds = fBounds;
             dst->swap(tmp);
         }
 
         dst->fBounds.offset(dx, dy);
-        
+
         const RunType*  sruns = fRunHead->readonly_runs();
         RunType*        druns = dst->fRunHead->writable_runs();
 
@@ -515,6 +559,7 @@
                 break;
             }
             *druns++ = (SkRegion::RunType)(bottom + dy);  // bottom;
+            *druns++ = *sruns++;    // copy intervalCount;
             for (;;) {
                 int x = *sruns++;
                 if (x == kRunTypeSentinel) {
@@ -569,9 +614,9 @@
     const SkRegion::RunType*    fB_runs;
     int                         fA_left, fA_rite, fB_left, fB_rite;
     int                         fLeft, fRite, fInside;
-    
-    void init(const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
-    {        
+
+    void init(const SkRegion::RunType a_runs[],
+              const SkRegion::RunType b_runs[]) {
         fA_left = *a_runs++;
         fA_rite = *a_runs++;
         fB_left = *b_runs++;
@@ -580,87 +625,75 @@
         fA_runs = a_runs;
         fB_runs = b_runs;
     }
-    
-    bool done() const
-    {
+
+    bool done() const {
         SkASSERT(fA_left <= SkRegion::kRunTypeSentinel);
         SkASSERT(fB_left <= SkRegion::kRunTypeSentinel);
-        return fA_left == SkRegion::kRunTypeSentinel && fB_left == SkRegion::kRunTypeSentinel;
+        return fA_left == SkRegion::kRunTypeSentinel &&
+               fB_left == SkRegion::kRunTypeSentinel;
     }
 
-    void next()
-    {
+    void next() {
         assert_valid_pair(fA_left, fA_rite);
         assert_valid_pair(fB_left, fB_rite);
 
         int     inside, left, rite SK_INIT_TO_AVOID_WARNING;
         bool    a_flush = false;
         bool    b_flush = false;
-        
+
         int a_left = fA_left;
         int a_rite = fA_rite;
         int b_left = fB_left;
         int b_rite = fB_rite;
 
-        if (a_left < b_left)
-        {
+        if (a_left < b_left) {
             inside = 1;
             left = a_left;
-            if (a_rite <= b_left)   // [...] <...>
-            {
+            if (a_rite <= b_left) {   // [...] <...>
                 rite = a_rite;
                 a_flush = true;
-            }
-            else // [...<..]...> or [...<...>...]
+            } else { // [...<..]...> or [...<...>...]
                 rite = a_left = b_left;
-        }
-        else if (b_left < a_left)
-        {
+            }
+        } else if (b_left < a_left) {
             inside = 2;
             left = b_left;
-            if (b_rite <= a_left)   // [...] <...>
-            {
+            if (b_rite <= a_left) {   // [...] <...>
                 rite = b_rite;
                 b_flush = true;
-            }
-            else // [...<..]...> or [...<...>...]
+            } else {    // [...<..]...> or [...<...>...]
                 rite = b_left = a_left;
-        }
-        else    // a_left == b_left
-        {
+            }
+        } else {    // a_left == b_left
             inside = 3;
             left = a_left;  // or b_left
-            if (a_rite <= b_rite)
-            {
+            if (a_rite <= b_rite) {
                 rite = b_left = a_rite;
                 a_flush = true;
             }
-            if (b_rite <= a_rite)
-            {
+            if (b_rite <= a_rite) {
                 rite = a_left = b_rite;
                 b_flush = true;
             }
         }
 
-        if (a_flush)
-        {
+        if (a_flush) {
             a_left = *fA_runs++;
             a_rite = *fA_runs++;
         }
-        if (b_flush)
-        {
+        if (b_flush) {
             b_left = *fB_runs++;
             b_rite = *fB_runs++;
         }
 
         SkASSERT(left <= rite);
-        
+
         // now update our state
         fA_left = a_left;
         fA_rite = a_rite;
         fB_left = b_left;
         fB_rite = b_rite;
-        
+
         fLeft = left;
         fRite = rite;
         fInside = inside;
@@ -670,32 +703,29 @@
 static SkRegion::RunType* operate_on_span(const SkRegion::RunType a_runs[],
                                           const SkRegion::RunType b_runs[],
                                           SkRegion::RunType dst[],
-                                          int min, int max)
-{
+                                          int min, int max) {
     spanRec rec;
     bool    firstInterval = true;
-    
+
     rec.init(a_runs, b_runs);
 
-    while (!rec.done())
-    {
+    while (!rec.done()) {
         rec.next();
-        
+
         int left = rec.fLeft;
         int rite = rec.fRite;
-        
+
         // add left,rite to our dst buffer (checking for coincidence
         if ((unsigned)(rec.fInside - min) <= (unsigned)(max - min) &&
-            left < rite)    // skip if equal
-        {
-            if (firstInterval || dst[-1] < left)
-            {
+                left < rite) {    // skip if equal
+            if (firstInterval || dst[-1] < left) {
                 *dst++ = (SkRegion::RunType)(left);
                 *dst++ = (SkRegion::RunType)(rite);
                 firstInterval = false;
-            }
-            else    // update the right edge
+            } else {
+                // update the right edge
                 dst[-1] = (SkRegion::RunType)(rite);
+            }
         }
     }
 
@@ -719,8 +749,7 @@
 
 class RgnOper {
 public:
-    RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op)
-    {
+    RgnOper(int top, SkRegion::RunType dst[], SkRegion::Op op) {
         // need to ensure that the op enum lines up with our minmax array
         SkASSERT(SkRegion::kDifference_Op == 0);
         SkASSERT(SkRegion::kIntersect_Op == 1);
@@ -737,33 +766,41 @@
         fMax = gOpMinMax[op].fMax;
     }
 
-    void addSpan(int bottom, const SkRegion::RunType a_runs[], const SkRegion::RunType b_runs[])
-    {
-        SkRegion::RunType*  start = fPrevDst + fPrevLen + 1;    // skip X values and slot for the next Y
+    void addSpan(int bottom, const SkRegion::RunType a_runs[],
+                 const SkRegion::RunType b_runs[]) {
+        // skip X values and slots for the next Y+intervalCount
+        SkRegion::RunType*  start = fPrevDst + fPrevLen + 2;
+        // start points to beginning of dst interval
         SkRegion::RunType*  stop = operate_on_span(a_runs, b_runs, start, fMin, fMax);
         size_t              len = stop - start;
+        SkASSERT(len >= 1 && (len & 1) == 1);
+        SkASSERT(SkRegion::kRunTypeSentinel == stop[-1]);
 
-        if (fPrevLen == len && !memcmp(fPrevDst, start, len * sizeof(SkRegion::RunType)))   // update Y value
-            fPrevDst[-1] = (SkRegion::RunType)(bottom);
-        else    // accept the new span
-        {
+        if (fPrevLen == len &&
+            (1 == len || !memcmp(fPrevDst, start,
+                                 (len - 1) * sizeof(SkRegion::RunType)))) {
+            // update Y value
+            fPrevDst[-2] = (SkRegion::RunType)(bottom);
+        } else {    // accept the new span
             if (len == 1 && fPrevLen == 0) {
                 fTop = (SkRegion::RunType)(bottom); // just update our bottom
             } else {
-                start[-1] = (SkRegion::RunType)(bottom);
+                start[-2] = (SkRegion::RunType)(bottom);
+                start[-1] = len >> 1;
                 fPrevDst = start;
                 fPrevLen = len;
             }
         }
     }
-    
-    int flush()
-    {
+
+    int flush() {
         fStartDst[0] = fTop;
         fPrevDst[fPrevLen] = SkRegion::kRunTypeSentinel;
         return (int)(fPrevDst - fStartDst + fPrevLen + 1);
     }
 
+    bool isEmpty() const { return 0 == fPrevLen; }
+
     uint8_t fMin, fMax;
 
 private:
@@ -773,33 +810,46 @@
     SkRegion::RunType   fTop;
 };
 
+// want a unique value to signal that we exited due to quickExit
+#define QUICK_EXIT_TRUE_COUNT   (-1)
+
 static int operate(const SkRegion::RunType a_runs[],
                    const SkRegion::RunType b_runs[],
                    SkRegion::RunType dst[],
-                   SkRegion::Op op) {
-    const SkRegion::RunType gSentinel[] = {
+                   SkRegion::Op op,
+                   bool quickExit) {
+    const SkRegion::RunType gEmptyScanline[] = {
+        0,  // dummy bottom value
+        0,  // zero intervals
         SkRegion::kRunTypeSentinel,
         // just need a 2nd value, since spanRec.init() reads 2 values, even
         // though if the first value is the sentinel, it ignores the 2nd value.
         // w/o the 2nd value here, we might read uninitialized memory.
-        0,
+        // This happens when we are using gSentinel, which is pointing at
+        // our sentinel value.
+        0
     };
+    const SkRegion::RunType* const gSentinel = &gEmptyScanline[2];
 
     int a_top = *a_runs++;
     int a_bot = *a_runs++;
     int b_top = *b_runs++;
     int b_bot = *b_runs++;
 
+    a_runs += 1;    // skip the intervalCount;
+    b_runs += 1;    // skip the intervalCount;
+
+    // Now a_runs and b_runs to their intervals (or sentinel)
+
     assert_sentinel(a_top, false);
     assert_sentinel(a_bot, false);
     assert_sentinel(b_top, false);
     assert_sentinel(b_bot, false);
 
     RgnOper oper(SkMin32(a_top, b_top), dst, op);
-    
-    bool firstInterval = true;
+
     int prevBot = SkRegion::kRunTypeSentinel; // so we fail the first test
-    
+
     while (a_bot < SkRegion::kRunTypeSentinel ||
            b_bot < SkRegion::kRunTypeSentinel) {
         int                         top, bot SK_INIT_TO_AVOID_WARNING;
@@ -839,30 +889,35 @@
                 b_flush = true;
             }
         }
-        
+
         if (top > prevBot) {
             oper.addSpan(top, gSentinel, gSentinel);
         }
         oper.addSpan(bot, run0, run1);
-        firstInterval = false;
+
+        if (quickExit && !oper.isEmpty()) {
+            return QUICK_EXIT_TRUE_COUNT;
+        }
 
         if (a_flush) {
-            a_runs = skip_scanline(a_runs);
+            a_runs = skip_intervals(a_runs);
             a_top = a_bot;
             a_bot = *a_runs++;
+            a_runs += 1;    // skip uninitialized intervalCount
             if (a_bot == SkRegion::kRunTypeSentinel) {
                 a_top = a_bot;
             }
         }
         if (b_flush) {
-            b_runs = skip_scanline(b_runs);
+            b_runs = skip_intervals(b_runs);
             b_top = b_bot;
             b_bot = *b_runs++;
+            b_runs += 1;    // skip uninitialized intervalCount
             if (b_bot == SkRegion::kRunTypeSentinel) {
                 b_top = b_bot;
             }
         }
-        
+
         prevBot = bot;
     }
     return oper.flush();
@@ -873,55 +928,65 @@
 /*  Given count RunTypes in a complex region, return the worst case number of
     logical intervals that represents (i.e. number of rects that would be
     returned from the iterator).
- 
+
     We could just return count/2, since there must be at least 2 values per
     interval, but we can first trim off the const overhead of the initial TOP
     value, plus the final BOTTOM + 2 sentinels.
  */
+#if 0 // UNUSED
 static int count_to_intervals(int count) {
     SkASSERT(count >= 6);   // a single rect is 6 values
     return (count - 4) >> 1;
 }
+#endif
 
 /*  Given a number of intervals, what is the worst case representation of that
     many intervals?
- 
+
     Worst case (from a storage perspective), is a vertical stack of single
-    intervals:  TOP + N * (BOTTOM LEFT RIGHT SENTINEL) + SENTINEL
+    intervals:  TOP + N * (BOTTOM INTERVALCOUNT LEFT RIGHT SENTINEL) + SENTINEL
  */
 static int intervals_to_count(int intervals) {
-    return 1 + intervals * 4 + 1;
+    return 1 + intervals * 5 + 1;
 }
 
-/*  Given the counts of RunTypes in two regions, return the worst-case number
+/*  Given the intervalCounts of RunTypes in two regions, return the worst-case number
     of RunTypes need to store the result after a region-op.
  */
-static int compute_worst_case_count(int a_count, int b_count) {
-    int a_intervals = count_to_intervals(a_count);
-    int b_intervals = count_to_intervals(b_count);
+static int compute_worst_case_count(int a_intervals, int b_intervals) {
     // Our heuristic worst case is ai * (bi + 1) + bi * (ai + 1)
     int intervals = 2 * a_intervals * b_intervals + a_intervals + b_intervals;
     // convert back to number of RunType values
     return intervals_to_count(intervals);
 }
 
-bool SkRegion::op(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op)
-{
-    SkDEBUGCODE(this->validate();)
+static bool setEmptyCheck(SkRegion* result) {
+    return result ? result->setEmpty() : false;
+}
 
+static bool setRectCheck(SkRegion* result, const SkIRect& rect) {
+    return result ? result->setRect(rect) : !rect.isEmpty();
+}
+
+static bool setRegionCheck(SkRegion* result, const SkRegion& rgn) {
+    return result ? result->setRegion(rgn) : !rgn.isEmpty();
+}
+
+bool SkRegion::Oper(const SkRegion& rgnaOrig, const SkRegion& rgnbOrig, Op op,
+                    SkRegion* result) {
     SkASSERT((unsigned)op < kOpCount);
-    
-    if (kReplace_Op == op)
-        return this->set(rgnbOrig);
-    
+
+    if (kReplace_Op == op) {
+        return setRegionCheck(result, rgnbOrig);
+    }
+
     // swith to using pointers, so we can swap them as needed
     const SkRegion* rgna = &rgnaOrig;
     const SkRegion* rgnb = &rgnbOrig;
     // after this point, do not refer to rgnaOrig or rgnbOrig!!!
 
     // collaps difference and reverse-difference into just difference
-    if (kReverseDifference_Op == op)
-    {
+    if (kReverseDifference_Op == op) {
         SkTSwap<const SkRegion*>(rgna, rgnb);
         op = kDifference_Op;
     }
@@ -934,67 +999,104 @@
 
     switch (op) {
     case kDifference_Op:
-        if (a_empty)
-            return this->setEmpty();
-        if (b_empty || !SkIRect::Intersects(rgna->fBounds, rgnb->fBounds))
-            return this->setRegion(*rgna);
+        if (a_empty) {
+            return setEmptyCheck(result);
+        }
+        if (b_empty || !SkIRect::IntersectsNoEmptyCheck(rgna->fBounds,
+                                                        rgnb->fBounds)) {
+            return setRegionCheck(result, *rgna);
+        }
+        if (b_rect && rgnb->fBounds.containsNoEmptyCheck(rgna->fBounds)) {
+            return setEmptyCheck(result);
+        }
         break;
 
     case kIntersect_Op:
         if ((a_empty | b_empty)
-                || !bounds.intersect(rgna->fBounds, rgnb->fBounds))
-            return this->setEmpty();
-        if (a_rect & b_rect)
-            return this->setRect(bounds);
+                || !bounds.intersect(rgna->fBounds, rgnb->fBounds)) {
+            return setEmptyCheck(result);
+        }
+        if (a_rect & b_rect) {
+            return setRectCheck(result, bounds);
+        }
+        if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
+            return setRegionCheck(result, *rgnb);
+        }
+        if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
+            return setRegionCheck(result, *rgna);
+        }
         break;
 
     case kUnion_Op:
-        if (a_empty)
-            return this->setRegion(*rgnb);
-        if (b_empty)
-            return this->setRegion(*rgna);
-        if (a_rect && rgna->fBounds.contains(rgnb->fBounds))
-            return this->setRegion(*rgna);
-        if (b_rect && rgnb->fBounds.contains(rgna->fBounds))
-            return this->setRegion(*rgnb);
+        if (a_empty) {
+            return setRegionCheck(result, *rgnb);
+        }
+        if (b_empty) {
+            return setRegionCheck(result, *rgna);
+        }
+        if (a_rect && rgna->fBounds.contains(rgnb->fBounds)) {
+            return setRegionCheck(result, *rgna);
+        }
+        if (b_rect && rgnb->fBounds.contains(rgna->fBounds)) {
+            return setRegionCheck(result, *rgnb);
+        }
         break;
 
     case kXOR_Op:
-        if (a_empty)
-            return this->setRegion(*rgnb);
-        if (b_empty)
-            return this->setRegion(*rgna);
+        if (a_empty) {
+            return setRegionCheck(result, *rgnb);
+        }
+        if (b_empty) {
+            return setRegionCheck(result, *rgna);
+        }
         break;
     default:
         SkDEBUGFAIL("unknown region op");
-        return !this->isEmpty();
+        return false;
     }
 
     RunType tmpA[kRectRegionRuns];
     RunType tmpB[kRectRegionRuns];
 
-    int a_count, b_count;
-    const RunType* a_runs = rgna->getRuns(tmpA, &a_count);
-    const RunType* b_runs = rgnb->getRuns(tmpB, &b_count);
+    int a_intervals, b_intervals;
+    const RunType* a_runs = rgna->getRuns(tmpA, &a_intervals);
+    const RunType* b_runs = rgnb->getRuns(tmpB, &b_intervals);
 
-    int dstCount = compute_worst_case_count(a_count, b_count);
-    SkAutoSTMalloc<32, RunType> array(dstCount);
+    int dstCount = compute_worst_case_count(a_intervals, b_intervals);
+    SkAutoSTMalloc<256, RunType> array(dstCount);
 
-    int count = operate(a_runs, b_runs, array.get(), op);
+#ifdef SK_DEBUG
+//  Sometimes helpful to seed everything with a known value when debugging
+//  sk_memset32((uint32_t*)array.get(), 0x7FFFFFFF, dstCount);
+#endif
+
+    int count = operate(a_runs, b_runs, array.get(), op, NULL == result);
     SkASSERT(count <= dstCount);
-    return this->setRuns(array.get(), count);
+
+    if (result) {
+        SkASSERT(count >= 0);
+        return result->setRuns(array.get(), count);
+    } else {
+        return (QUICK_EXIT_TRUE_COUNT == count) || !isRunCountEmpty(count);
+    }
 }
 
-//////////////////////////////////////////////////////////////////////////////////////////////////////////
+bool SkRegion::op(const SkRegion& rgna, const SkRegion& rgnb, Op op) {
+    SkDEBUGCODE(this->validate();)
+    return SkRegion::Oper(rgna, rgnb, op, this);
+}
+
+///////////////////////////////////////////////////////////////////////////////
 
 #include "SkBuffer.h"
 
-uint32_t SkRegion::flatten(void* storage) const {
+uint32_t SkRegion::writeToMemory(void* storage) const {
     if (NULL == storage) {
         uint32_t size = sizeof(int32_t); // -1 (empty), 0 (rect), runCount
         if (!this->isEmpty()) {
             size += sizeof(fBounds);
             if (this->isComplex()) {
+                size += 2 * sizeof(int32_t);    // ySpanCount + intervalCount
                 size += fRunHead->fRunCount * sizeof(RunType);
             }
         }
@@ -1012,6 +1114,8 @@
         buffer.write(&fBounds, sizeof(fBounds));
 
         if (!isRect) {
+            buffer.write32(fRunHead->getYSpanCount());
+            buffer.write32(fRunHead->getIntervalCount());
             buffer.write(fRunHead->readonly_runs(),
                          fRunHead->fRunCount * sizeof(RunType));
         }
@@ -1019,18 +1123,20 @@
     return buffer.pos();
 }
 
-uint32_t SkRegion::unflatten(const void* storage) {
+uint32_t SkRegion::readFromMemory(const void* storage) {
     SkRBuffer   buffer(storage);
     SkRegion    tmp;
     int32_t     count;
-    
+
     count = buffer.readS32();
     if (count >= 0) {
         buffer.read(&tmp.fBounds, sizeof(tmp.fBounds));
         if (count == 0) {
             tmp.fRunHead = SkRegion_gRectRunHeadPtr;
         } else {
-            tmp.allocateRuns(count);
+            int32_t ySpanCount = buffer.readS32();
+            int32_t intervalCount = buffer.readS32();
+            tmp.allocateRuns(count, ySpanCount, intervalCount);
             buffer.read(tmp.fRunHead->writable_runs(), count * sizeof(RunType));
         }
     }
@@ -1049,45 +1155,82 @@
 
 #ifdef SK_DEBUG
 
-static const SkRegion::RunType* validate_line(const SkRegion::RunType run[], const SkIRect& bounds)
-{
-    // *run is the bottom of the current span
-    SkASSERT(*run > bounds.fTop);
-    SkASSERT(*run <= bounds.fBottom);
-    run += 1;
+// Starts with first X-interval, and returns a ptr to the X-sentinel
+static const SkRegion::RunType* skip_intervals_slow(const SkRegion::RunType runs[]) {
+    // want to track that our intevals are all disjoint, such that
+    // prev-right < next-left. We rely on this optimization in places such as
+    // contains().
+    //
+    SkRegion::RunType prevR = -SkRegion::kRunTypeSentinel;
 
-    // check for empty span
-    if (*run != SkRegion::kRunTypeSentinel)
-    {
-        int prevRite = bounds.fLeft - 1;
-        do {
-            int left = *run++;
-            int rite = *run++;
-            SkASSERT(left < rite);
-            SkASSERT(left > prevRite);
-            SkASSERT(rite <= bounds.fRight);
-            prevRite = rite;
-        } while (*run < SkRegion::kRunTypeSentinel);
+    while (runs[0] < SkRegion::kRunTypeSentinel) {
+        SkASSERT(prevR < runs[0]);
+        SkASSERT(runs[0] < runs[1]);
+        SkASSERT(runs[1] < SkRegion::kRunTypeSentinel);
+        prevR = runs[1];
+        runs += 2;
     }
-    return run + 1; // skip sentinel
+    return runs;
 }
 
-void SkRegion::validate() const
-{
-    if (this->isEmpty())
-    {
+static void compute_bounds(const SkRegion::RunType runs[], int count,
+                           SkIRect* bounds, int* ySpanCountPtr,
+                           int* intervalCountPtr) {
+    assert_sentinel(runs[0], false);    // top
+
+    int left = SK_MaxS32;
+    int rite = SK_MinS32;
+    int bot;
+    int ySpanCount = 0;
+    int intervalCount = 0;
+
+    bounds->fTop = *runs++;
+    do {
+        bot = *runs++;
+        SkASSERT(SkRegion::kRunTypeSentinel > bot);
+
+        ySpanCount += 1;
+
+        runs += 1;  // skip intervalCount for now
+        if (*runs < SkRegion::kRunTypeSentinel) {
+            if (left > *runs) {
+                left = *runs;
+            }
+
+            const SkRegion::RunType* prev = runs;
+            runs = skip_intervals_slow(runs);
+            int intervals = (runs - prev) >> 1;
+            SkASSERT(prev[-1] == intervals);
+            intervalCount += intervals;
+
+            if (rite < runs[-1]) {
+                rite = runs[-1];
+            }
+        } else {
+            SkASSERT(0 == runs[-1]);    // no intervals
+        }
+        SkASSERT(SkRegion::kRunTypeSentinel == *runs);
+        runs += 1;
+    } while (SkRegion::kRunTypeSentinel != *runs);
+
+    bounds->fLeft = left;
+    bounds->fRight = rite;
+    bounds->fBottom = bot;
+    *ySpanCountPtr = ySpanCount;
+    *intervalCountPtr = intervalCount;
+}
+
+void SkRegion::validate() const {
+    if (this->isEmpty()) {
         // check for explicit empty (the zero rect), so we can compare rects to know when
         // two regions are equal (i.e. emptyRectA == emptyRectB)
         // this is stricter than just asserting fBounds.isEmpty()
         SkASSERT(fBounds.fLeft == 0 && fBounds.fTop == 0 && fBounds.fRight == 0 && fBounds.fBottom == 0);
-    }
-    else
-    {
+    } else {
         SkASSERT(!fBounds.isEmpty());
-        if (!this->isRect())
-        {
+        if (!this->isRect()) {
             SkASSERT(fRunHead->fRefCnt >= 1);
-            SkASSERT(fRunHead->fRunCount >= kRectRegionRuns);
+            SkASSERT(fRunHead->fRunCount > kRectRegionRuns);
 
             const RunType* run = fRunHead->readonly_runs();
             const RunType* stop = run + fRunHead->fRunCount;
@@ -1095,30 +1238,25 @@
             // check that our bounds match our runs
             {
                 SkIRect bounds;
-                bool isARect = ComputeRunBounds(run, stop - run, &bounds);
-                SkASSERT(!isARect);
-                SkASSERT(bounds == fBounds);
-            }
+                int ySpanCount, intervalCount;
+                compute_bounds(run, stop - run, &bounds, &ySpanCount, &intervalCount);
 
-            SkASSERT(*run == fBounds.fTop);
-            run++;
-            do {
-                run = validate_line(run, fBounds);
-            } while (*run < kRunTypeSentinel);
-            SkASSERT(run + 1 == stop);
+                SkASSERT(bounds == fBounds);
+                SkASSERT(ySpanCount > 0);
+                SkASSERT(fRunHead->getYSpanCount() == ySpanCount);
+           //     SkASSERT(intervalCount > 1);
+                SkASSERT(fRunHead->getIntervalCount() == intervalCount);
+            }
         }
     }
 }
 
-void SkRegion::dump() const
-{
-    if (this->isEmpty())
+void SkRegion::dump() const {
+    if (this->isEmpty()) {
         SkDebugf("  rgn: empty\n");
-    else
-    {
+    } else {
         SkDebugf("  rgn: [%d %d %d %d]", fBounds.fLeft, fBounds.fTop, fBounds.fRight, fBounds.fBottom);
-        if (this->isComplex())
-        {
+        if (this->isComplex()) {
             const RunType* runs = fRunHead->readonly_runs();
             for (int i = 0; i < fRunHead->fRunCount; i++)
                 SkDebugf(" %d", runs[i]);
@@ -1129,7 +1267,7 @@
 
 #endif
 
-/////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 
 SkRegion::Iterator::Iterator(const SkRegion& rgn) {
     this->reset(rgn);
@@ -1154,8 +1292,9 @@
             fRuns = NULL;
         } else {
             fRuns = rgn.fRunHead->readonly_runs();
-            fRect.set(fRuns[2], fRuns[0], fRuns[3], fRuns[1]);
-            fRuns += 4;
+            fRect.set(fRuns[3], fRuns[0], fRuns[4], fRuns[1]);
+            fRuns += 5;
+            // Now fRuns points to the 2nd interval (or x-sentinel)
         }
     }
 }
@@ -1179,18 +1318,20 @@
     } else {    // we're at the end of a line
         runs += 1;
         if (runs[0] < kRunTypeSentinel) { // valid Y value
-            if (runs[1] == kRunTypeSentinel) {    // empty line
+            int intervals = runs[1];
+            if (0 == intervals) {    // empty line
                 fRect.fTop = runs[0];
-                runs += 2;
+                runs += 3;
             } else {
                 fRect.fTop = fRect.fBottom;
             }
-    
+
             fRect.fBottom = runs[0];
-            assert_sentinel(runs[1], false);
-            fRect.fLeft = runs[1];
-            fRect.fRight = runs[2];
-            runs += 3;
+            assert_sentinel(runs[2], false);
+            assert_sentinel(runs[3], false);
+            fRect.fLeft = runs[2];
+            fRect.fRight = runs[3];
+            runs += 4;
         } else {    // end of rgn
             fDone = true;
         }
@@ -1237,24 +1378,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static SkRegion::RunType* find_y(const SkRegion::RunType runs[], int y) {
-    int top = *runs++;
-    if (top <= y) {
-        for (;;) {
-            int bot = *runs++;
-            if (bot > y) {
-                if (bot == SkRegion::kRunTypeSentinel ||
-                    *runs == SkRegion::kRunTypeSentinel) {
-                    break;
-				}
-                return (SkRegion::RunType*)runs;
-            }
-            runs = skip_scanline(runs);
-        }
-    }
-    return NULL;
-}
-
 SkRegion::Spanerator::Spanerator(const SkRegion& rgn, int y, int left,
                                  int right) {
     SkDEBUGCODE(rgn.validate();)
@@ -1276,26 +1399,24 @@
             fRuns = NULL;    // means we're a rect, not a rgn
             fDone = false;
         } else {
-            const SkRegion::RunType* runs = find_y(
-                                              rgn.fRunHead->readonly_runs(), y);
-            if (runs) {
-                for (;;) {
-                    // runs[0..1] is to the right of the span, so we're done
-                    if (runs[0] >= right) {
-                        break;
-                    }
-                    // runs[0..1] is to the left of the span, so continue
-                    if (runs[1] <= left) {
-                        runs += 2;
-                        continue;
-                    }
-                    // runs[0..1] intersects the span
-                    fRuns = runs;
-                    fLeft = left;
-                    fRight = right;
-                    fDone = false;
+            const SkRegion::RunType* runs = rgn.fRunHead->findScanline(y);
+            runs += 2;  // skip Bottom and IntervalCount
+            for (;;) {
+                // runs[0..1] is to the right of the span, so we're done
+                if (runs[0] >= right) {
                     break;
                 }
+                // runs[0..1] is to the left of the span, so continue
+                if (runs[1] <= left) {
+                    runs += 2;
+                    continue;
+                }
+                // runs[0..1] intersects the span
+                fRuns = runs;
+                fLeft = left;
+                fRight = right;
+                fDone = false;
+                break;
             }
         }
     }
@@ -1343,7 +1464,7 @@
 bool SkRegion::debugSetRuns(const RunType runs[], int count) {
     // we need to make a copy, since the real method may modify the array, and
     // so it cannot be const.
-    
+
     SkAutoTArray<RunType> storage(count);
     memcpy(storage.get(), runs, count * sizeof(RunType));
     return this->setRuns(storage.get(), count);
diff --git a/src/core/SkRegionPriv.h b/src/core/SkRegionPriv.h
index 648643f..f299f3a 100644
--- a/src/core/SkRegionPriv.h
+++ b/src/core/SkRegionPriv.h
@@ -18,12 +18,48 @@
 
 //SkDEBUGCODE(extern int32_t gRgnAllocCounter;)
 
+#ifdef SK_DEBUG
+// Given the first interval (just past the interval-count), compute the
+// interval count, by search for the x-sentinel
+//
+static int compute_intervalcount(const SkRegion::RunType runs[]) {
+    const SkRegion::RunType* curr = runs;
+    while (*curr < SkRegion::kRunTypeSentinel) {
+        SkASSERT(curr[0] < curr[1]);
+        SkASSERT(curr[1] < SkRegion::kRunTypeSentinel);
+        curr += 2;
+    }
+    return (curr - runs) >> 1;
+}
+#endif
+
 struct SkRegion::RunHead {
+private:
+
+public:
     int32_t fRefCnt;
     int32_t fRunCount;
-    
-    static RunHead* Alloc(int count)
-    {
+
+    /**
+     *  Number of spans with different Y values. This does not count the initial
+     *  Top value, nor does it count the final Y-Sentinel value. In the logical
+     *  case of a rectangle, this would return 1, and an empty region would
+     *  return 0.
+     */
+    int getYSpanCount() const {
+        return fYSpanCount;
+    }
+
+    /**
+     *  Number of intervals in the entire region. This equals the number of
+     *  rects that would be returned by the Iterator. In the logical case of
+     *  a rect, this would return 1, and an empty region would return 0.
+     */
+    int getIntervalCount() const {
+        return fIntervalCount;
+    }
+
+    static RunHead* Alloc(int count) {
         //SkDEBUGCODE(sk_atomic_inc(&gRgnAllocCounter);)
         //SkDEBUGF(("************** gRgnAllocCounter::alloc %d\n", gRgnAllocCounter));
 
@@ -32,50 +68,166 @@
         RunHead* head = (RunHead*)sk_malloc_throw(sizeof(RunHead) + count * sizeof(RunType));
         head->fRefCnt = 1;
         head->fRunCount = count;
+        // these must be filled in later, otherwise we will be invalid
+        head->fYSpanCount = 0;
+        head->fIntervalCount = 0;
         return head;
     }
-    
-    bool isComplex() const
-    {
-        return this != SkRegion_gEmptyRunHeadPtr && this != SkRegion_gRectRunHeadPtr;
+
+    static RunHead* Alloc(int count, int yspancount, int intervalCount) {
+        SkASSERT(yspancount > 0);
+        SkASSERT(intervalCount > 1);
+
+        RunHead* head = Alloc(count);
+        head->fYSpanCount = yspancount;
+        head->fIntervalCount = intervalCount;
+        return head;
     }
 
-    SkRegion::RunType* writable_runs()
-    {
-        SkASSERT(this->isComplex());
+    SkRegion::RunType* writable_runs() {
         SkASSERT(fRefCnt == 1);
         return (SkRegion::RunType*)(this + 1);
     }
-    const SkRegion::RunType* readonly_runs() const
-    {
-        SkASSERT(this->isComplex());
+
+    const SkRegion::RunType* readonly_runs() const {
         return (const SkRegion::RunType*)(this + 1);
     }
-    
-    RunHead* ensureWritable()
-    {
-        SkASSERT(this->isComplex());
-        
+
+    RunHead* ensureWritable() {
         RunHead* writable = this;
-        if (fRefCnt > 1)
-        {
+        if (fRefCnt > 1) {
             // We need to alloc & copy the current region before we call
             // sk_atomic_dec because it could be freed in the meantime,
-            // otherwise.            
-            writable = Alloc(fRunCount);
+            // otherwise.
+            writable = Alloc(fRunCount, fYSpanCount, fIntervalCount);
             memcpy(writable->writable_runs(), this->readonly_runs(),
                    fRunCount * sizeof(RunType));
 
             // fRefCount might have changed since we last checked.
             // If we own the last reference at this point, we need to
             // free the memory.
-            if (sk_atomic_dec(&fRefCnt) == 1)
-            {
+            if (sk_atomic_dec(&fRefCnt) == 1) {
                 sk_free(this);
             }
         }
         return writable;
     }
+
+    /**
+     *  Given a scanline (including its Bottom value at runs[0]), return the next
+     *  scanline. Asserts that there is one (i.e. runs[0] < Sentinel)
+     */
+    static SkRegion::RunType* SkipEntireScanline(const SkRegion::RunType runs[]) {
+        // we are not the Y Sentinel
+        SkASSERT(runs[0] < SkRegion::kRunTypeSentinel);
+
+        const int intervals = runs[1];
+        SkASSERT(runs[2 + intervals * 2] == SkRegion::kRunTypeSentinel);
+#ifdef SK_DEBUG
+        {
+            int n = compute_intervalcount(&runs[2]);
+            SkASSERT(n == intervals);
+        }
+#endif
+
+        // skip the entire line [B N [L R] S]
+        runs += 1 + 1 + intervals * 2 + 1;
+        return const_cast<SkRegion::RunType*>(runs);
+    }
+
+
+    /**
+     *  Return the scanline that contains the Y value. This requires that the Y
+     *  value is already known to be contained within the bounds of the region,
+     *  and so this routine never returns NULL.
+     *
+     *  It returns the beginning of the scanline, starting with its Bottom value.
+     */
+    SkRegion::RunType* findScanline(int y) const {
+        const RunType* runs = this->readonly_runs();
+
+        // if the top-check fails, we didn't do a quick check on the bounds
+        SkASSERT(y >= runs[0]);
+
+        runs += 1;  // skip top-Y
+        for (;;) {
+            int bottom = runs[0];
+            // If we hit this, we've walked off the region, and our bounds check
+            // failed.
+            SkASSERT(bottom < SkRegion::kRunTypeSentinel);
+            if (y < bottom) {
+                break;
+            }
+            runs = SkipEntireScanline(runs);
+        }
+        return const_cast<SkRegion::RunType*>(runs);
+    }
+
+    // Copy src runs into us, computing interval counts and bounds along the way
+    void computeRunBounds(SkIRect* bounds) {
+        RunType* runs = this->writable_runs();
+        bounds->fTop = *runs++;
+
+        int bot;
+        int ySpanCount = 0;
+        int intervalCount = 0;
+        int left = SK_MaxS32;
+        int rite = SK_MinS32;
+
+        do {
+            bot = *runs++;
+            SkASSERT(bot < SkRegion::kRunTypeSentinel);
+            ySpanCount += 1;
+
+            const int intervals = *runs++;
+            SkASSERT(intervals >= 0);
+            SkASSERT(intervals < SkRegion::kRunTypeSentinel);
+
+            if (intervals > 0) {
+#ifdef SK_DEBUG
+                {
+                    int n = compute_intervalcount(runs);
+                    SkASSERT(n == intervals);
+                }
+#endif
+                RunType L = runs[0];
+                SkASSERT(L < SkRegion::kRunTypeSentinel);
+                if (left > L) {
+                    left = L;
+                }
+
+                runs += intervals * 2;
+                RunType R = runs[-1];
+                SkASSERT(R < SkRegion::kRunTypeSentinel);
+                if (rite < R) {
+                    rite = R;
+                }
+
+                intervalCount += intervals;
+            }
+            SkASSERT(SkRegion::kRunTypeSentinel == *runs);
+            runs += 1;  // skip x-sentinel
+
+            // test Y-sentinel
+        } while (SkRegion::kRunTypeSentinel > *runs);
+
+#ifdef SK_DEBUG
+        // +1 to skip the last Y-sentinel
+        int runCount = runs - this->writable_runs() + 1;
+        SkASSERT(runCount == fRunCount);
+#endif
+
+        fYSpanCount = ySpanCount;
+        fIntervalCount = intervalCount;
+
+        bounds->fLeft = left;
+        bounds->fRight = rite;
+        bounds->fBottom = bot;
+    }
+
+private:
+    int32_t fYSpanCount;
+    int32_t fIntervalCount;
 };
 
 #endif
diff --git a/src/core/SkRegion_path.cpp b/src/core/SkRegion_path.cpp
index 345ecf8..80149a3 100644
--- a/src/core/SkRegion_path.cpp
+++ b/src/core/SkRegion_path.cpp
@@ -16,7 +16,7 @@
 class SkRgnBuilder : public SkBlitter {
 public:
     virtual ~SkRgnBuilder();
-    
+
     // returns true if it could allocate the working storage needed
     bool init(int maxHeight, int maxTransitions);
 
@@ -51,13 +51,26 @@
     }
 #endif
 private:
+    /*
+     *  Scanline mimics a row in the region, nearly. A row in a region is:
+     *      [Bottom IntervalCount [L R]... Sentinel]
+     *  while a Scanline is
+     *      [LastY XCount [L R]... uninitialized]
+     *  The two are the same length (which is good), but we have to transmute
+     *  the scanline a little when we convert it to a region-row.
+     *
+     *  Potentially we could recode this to exactly match the row format, in
+     *  which case copyToRgn() could be a single memcpy. Not sure that is worth
+     *  the effort.
+     */
     struct Scanline {
         SkRegion::RunType fLastY;
         SkRegion::RunType fXCount;
 
         SkRegion::RunType* firstX() const { return (SkRegion::RunType*)(this + 1); }
         Scanline* nextScanline() const {
-            return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount);
+            // add final +1 for the x-sentinel
+            return (Scanline*)((SkRegion::RunType*)(this + 1) + fXCount + 1);
         }
     };
     SkRegion::RunType*  fStorage;
@@ -66,7 +79,7 @@
     //  points at next avialable x[] in fCurrScanline
     SkRegion::RunType*  fCurrXPtr;
     SkRegion::RunType   fTop;           // first Y value
-    
+
     int fStorageCount;
 
     bool collapsWithPrev() {
@@ -171,7 +184,8 @@
 
 void SkRgnBuilder::copyToRect(SkIRect* r) const {
     SkASSERT(fCurrScanline != NULL);
-    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 4);
+    // A rect's scanline is [bottom intervals left right sentinel] == 5
+    SkASSERT((const SkRegion::RunType*)fCurrScanline - fStorage == 5);
 
     const Scanline* line = (const Scanline*)fStorage;
     SkASSERT(line->fXCount == 2);
@@ -190,6 +204,7 @@
     do {
         *runs++ = (SkRegion::RunType)(line->fLastY + 1);
         int count = line->fXCount;
+        *runs++ = count >> 1;   // intervalCount
         if (count) {
             memcpy(runs, line->firstX(), count * sizeof(SkRegion::RunType));
             runs += count;
@@ -228,7 +243,7 @@
     SkScalar    top = SkIntToScalar(SK_MaxS16);
     SkScalar    bot = SkIntToScalar(SK_MinS16);
 
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+    while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
         maxEdges += gPathVerbToMaxEdges[verb];
 
         int lastIndex = gPathVerbToInitialLastIndex[verb];
@@ -275,7 +290,7 @@
     int pathTransitions = count_path_runtype_values(path, &pathTop, &pathBot);
     int clipTop, clipBot;
     int clipTransitions;
-    
+
     clipTransitions = clip.count_runtype_values(&clipTop, &clipBot);
 
     int top = SkMax32(pathTop, clipTop);
@@ -285,7 +300,7 @@
         return this->setEmpty();
 
     SkRgnBuilder builder;
-    
+
     if (!builder.init(bot - top, SkMax32(pathTransitions, clipTransitions))) {
         // can't allocate working space, so return false
         return this->setEmpty();
@@ -301,11 +316,11 @@
         builder.copyToRect(&fBounds);
         this->setRect(fBounds);
     } else {
-        SkRegion    tmp;
+        SkRegion tmp;
 
         tmp.fRunHead = RunHead::Alloc(count);
         builder.copyToRgn(tmp.fRunHead->writable_runs());
-        ComputeRunBounds(tmp.fRunHead->readonly_runs(), count, &tmp.fBounds);
+        tmp.fRunHead->computeRunBounds(&tmp.fBounds);
         this->swap(tmp);
     }
     SkDEBUGCODE(this->validate();)
@@ -319,7 +334,7 @@
     enum {
         kY0Link = 0x01,
         kY1Link = 0x02,
-        
+
         kCompleteLink = (kY0Link | kY1Link)
     };
 
@@ -327,7 +342,7 @@
     SkRegion::RunType fY0, fY1;
     uint8_t fFlags;
     Edge*   fNext;
-    
+
     void set(int x, int y0, int y1) {
         SkASSERT(y0 != y1);
 
@@ -337,7 +352,7 @@
         fFlags = 0;
         SkDEBUGCODE(fNext = NULL;)
     }
-    
+
     int top() const {
         return SkFastMin32(fY0, fY1);
     }
@@ -368,7 +383,7 @@
             }
         }
     }
-    
+
     e = base;
     if ((base->fFlags & Edge::kY1Link) == 0) {
         for (;;) {
@@ -381,7 +396,7 @@
             }
         }
     }
-        
+
     base->fFlags = Edge::kCompleteLink;
 }
 
@@ -433,7 +448,7 @@
     const SkIRect& bounds = this->getBounds();
 
     if (this->isRect()) {
-        SkRect  r;        
+        SkRect  r;
         r.set(bounds);      // this converts the ints to scalars
         path->addRect(r);
         return true;
@@ -441,15 +456,14 @@
 
     SkRegion::Iterator  iter(*this);
     SkTDArray<Edge>     edges;
-    
+
     for (const SkIRect& r = iter.rect(); !iter.done(); iter.next()) {
         Edge* edge = edges.append(2);
         edge[0].set(r.fLeft, r.fBottom, r.fTop);
         edge[1].set(r.fRight, r.fTop, r.fBottom);
     }
-    SkQSort(edges.begin(), edges.count(), sizeof(Edge),
-            (SkQSortCompareProc)EdgeProc);
-    
+    qsort(edges.begin(), edges.count(), sizeof(Edge), SkCastForQSort(EdgeProc));
+
     int count = edges.count();
     Edge* start = edges.begin();
     Edge* stop = start + count;
@@ -474,4 +488,3 @@
 
     return true;
 }
-
diff --git a/src/core/SkRegion_rects.cpp b/src/core/SkRegion_rects.cpp
index 1777a1e..4121080 100644
--- a/src/core/SkRegion_rects.cpp
+++ b/src/core/SkRegion_rects.cpp
@@ -15,12 +15,12 @@
 struct VEdge {
     VEdge*  fPrev;
     VEdge*  fNext;
-    
+
     SkRegion::RunType   fX;
     SkRegion::RunType   fTop;
     SkRegion::RunType   fBottom;
     int                 fWinding;
-    
+
     void removeFromList() {
         fPrev->fNext = fNext;
         fNext->fPrev = fPrev;
@@ -34,7 +34,7 @@
             // remove prev from the list
             prev->fPrev->fNext = next;
             next->fPrev = prev->fPrev;
-            
+
             // insert prev after next
             prev->fNext = next->fNext;
             next->fNext->fPrev = prev;
@@ -48,7 +48,7 @@
         edges[0].fTop = r.fTop;
         edges[0].fBottom = r.fBottom;
         edges[0].fWinding = -1;
-        
+
         edges[1].fX = r.fRight;
         edges[1].fTop = r.fTop;
         edges[1].fBottom = r.fBottom;
@@ -60,12 +60,12 @@
 public:
     Accumulator(SkRegion::RunType top, int numRects);
     ~Accumulator() {}
-    
+
     SkRegion::RunType append(SkRegion::RunType top, const VEdge* edge);
-    
+
     int count() const { return fTotalCount; }
     void copyTo(SkRegion::RunType dst[]);
-    
+
 private:
     struct Row {
         SkRegion::RunType*  fPtr;
@@ -94,7 +94,7 @@
     size_t size = fRectCount * 2 * sizeof(SkRegion::RunType);
     SkRegion::RunType* row = (SkRegion::RunType*)fAlloc.allocThrow(size);
     SkRegion::RunType* rowHead = row;
-    
+
     SkRegion::RunType nextY = SkRegion::kRunTypeSentinel;
     int winding = edge->fWinding;
 
@@ -123,7 +123,7 @@
                 *row++ = edge->fX;
                 TRACE_ROW(SkDebugf(" %d] [%d", currR, edge->fX);)
             }
-            
+
             nextY = SkMin32(nextY, edge->fBottom);
             edge = edge->fNext;
         }
@@ -132,7 +132,7 @@
         TRACE_ROW(SkDebugf(" %d]\n", currR);)
     }
     int rowCount = row - rowHead;
-    
+
     // now see if we have already seen this row, or if its unique
 
     Row* r = fRows.count() ? &fRows[fRows.count() - 1] : NULL;
@@ -148,15 +148,15 @@
             r->fCount = rowCount;
             fTotalCount += 1 + rowCount + 1;
         }
-    
+
     return nextY;
 }
 
 void Accumulator::copyTo(SkRegion::RunType dst[]) {
     SkDEBUGCODE(SkRegion::RunType* startDst = dst;)
-    
+
     *dst++ = fTop;
-    
+
     const Row* curr = fRows.begin();
     const Row* stop = fRows.end();
     while (curr < stop) {
@@ -207,7 +207,7 @@
             *ptr++ = edge++;
         }
     }
-    
+
     int edgeCount = ptr - edgePtr;
     if (0 == edgeCount) {
         // all the rects[] were empty
@@ -251,7 +251,7 @@
     headEdge.fTop = SK_MinS32;
     headEdge.fX = SK_MinS32;
     head->fPrev = &headEdge;
-    
+
     tailEdge.fPrev = tail;
     tailEdge.fNext = NULL;
     tailEdge.fTop = SK_MaxS32;
@@ -259,7 +259,7 @@
 
     int32_t currY = head->fTop;
     Accumulator accum(currY, rectCount);
-    
+
     while (head->fNext) {
         VEdge* edge = head;
         // accumulate the current
diff --git a/src/core/SkScalerContext.cpp b/src/core/SkScalerContext.cpp
index eee0dc5..3f5c5eb 100644
--- a/src/core/SkScalerContext.cpp
+++ b/src/core/SkScalerContext.cpp
@@ -12,13 +12,17 @@
 #include "SkDescriptor.h"
 #include "SkDraw.h"
 #include "SkFontHost.h"
+#include "SkGlyph.h"
 #include "SkMaskFilter.h"
+#include "SkMaskGamma.h"
+#include "SkOrderedReadBuffer.h"
 #include "SkPathEffect.h"
 #include "SkRasterizer.h"
 #include "SkRasterClip.h"
 #include "SkStroke.h"
 #include "SkThread.h"
 
+
 #define ComputeBWRowBytes(width)        (((unsigned)(width) + 7) >> 3)
 
 void SkGlyph::toMask(SkMask* mask) const {
@@ -64,7 +68,7 @@
     const void*     data = desc->findEntry(tag, &len);
 
     if (data) {
-        SkFlattenableReadBuffer   buffer(data, len);
+        SkOrderedReadBuffer   buffer(data, len);
         obj = buffer.readFlattenable();
         SkASSERT(buffer.offset() == buffer.size());
     }
@@ -72,16 +76,23 @@
 }
 
 SkScalerContext::SkScalerContext(const SkDescriptor* desc)
-    : fPathEffect(NULL), fMaskFilter(NULL)
+    : fRec(*static_cast<const Rec*>(desc->findEntry(kRec_SkDescriptorTag, NULL)))
+
+    , fBaseGlyphCount(0)
+
+    , fPathEffect(static_cast<SkPathEffect*>(load_flattenable(desc, kPathEffect_SkDescriptorTag)))
+    , fMaskFilter(static_cast<SkMaskFilter*>(load_flattenable(desc, kMaskFilter_SkDescriptorTag)))
+    , fRasterizer(static_cast<SkRasterizer*>(load_flattenable(desc, kRasterizer_SkDescriptorTag)))
+
+      // Initialize based on our settings. Subclasses can also force this.
+    , fGenerateImageFromPath(fRec.fFrameWidth > 0 || fPathEffect != NULL || fRasterizer != NULL)
+
+    , fNextContext(NULL)
+
+    , fPreBlend(fMaskFilter ? SkMaskGamma::PreBlend() : SkScalerContext::GetMaskPreBlend(fRec))
+    , fPreBlendForFilter(fMaskFilter ? SkScalerContext::GetMaskPreBlend(fRec)
+                                     : SkMaskGamma::PreBlend())
 {
-    fBaseGlyphCount = 0;
-    fNextContext = NULL;
-
-    const Rec* rec = (const Rec*)desc->findEntry(kRec_SkDescriptorTag, NULL);
-    SkASSERT(rec);
-
-    fRec = *rec;
-
 #ifdef DUMP_REC
     desc->assertChecksum();
     SkDebugf("SkScalarContext checksum %x count %d length %d\n",
@@ -96,14 +107,6 @@
              desc->findEntry(kPathEffect_SkDescriptorTag, NULL),
         desc->findEntry(kMaskFilter_SkDescriptorTag, NULL));
 #endif
-
-    fPathEffect = (SkPathEffect*)load_flattenable(desc, kPathEffect_SkDescriptorTag);
-    fMaskFilter = (SkMaskFilter*)load_flattenable(desc, kMaskFilter_SkDescriptorTag);
-    fRasterizer = (SkRasterizer*)load_flattenable(desc, kRasterizer_SkDescriptorTag);
-
-    // initialize based on our settings. subclasses can also force this
-    fGenerateImageFromPath = fRec.fFrameWidth > 0 || fPathEffect != NULL ||
-                             fRasterizer != NULL;
 }
 
 SkScalerContext::~SkScalerContext() {
@@ -188,7 +191,7 @@
         glyphID -= count;
         ctx = ctx->getNextContext();
         if (NULL == ctx) {
-            SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
+//            SkDebugf("--- no context for glyph %x\n", glyph.getGlyphID());
             // just return the original context (this)
             return this;
         }
@@ -324,9 +327,9 @@
         }
     }
 
-	if (SkMask::kARGB32_Format != glyph->fMaskFormat) {
-		glyph->fMaskFormat = fRec.fMaskFormat;
-	}
+    if (SkMask::kARGB32_Format != glyph->fMaskFormat) {
+        glyph->fMaskFormat = fRec.fMaskFormat;
+    }
 
     if (fMaskFilter) {
         SkMask      src, dst;
@@ -337,6 +340,9 @@
 
         src.fImage = NULL;  // only want the bounds from the filter
         if (fMaskFilter->filterMask(&dst, src, matrix, NULL)) {
+            if (dst.fBounds.isEmpty() || !dst.fBounds.is16Bit()) {
+                goto SK_ERROR;
+            }
             SkASSERT(dst.fImage == NULL);
             glyph->fLeft    = dst.fBounds.fLeft;
             glyph->fTop     = dst.fBounds.fTop;
@@ -358,94 +364,67 @@
     glyph->fMaskFormat = fRec.fMaskFormat;
 }
 
-static bool isLCD(const SkScalerContext::Rec& rec) {
-    return SkMask::kLCD16_Format == rec.fMaskFormat ||
-           SkMask::kLCD32_Format == rec.fMaskFormat;
-}
 
-static uint16_t a8_to_rgb565(unsigned a8) {
-    return SkPackRGB16(a8 >> 3, a8 >> 2, a8 >> 3);
-}
+static void applyLUTToA8Mask(const SkMask& mask, const uint8_t* lut) {
+    uint8_t* SK_RESTRICT dst = (uint8_t*)mask.fImage;
+    unsigned rowBytes = mask.fRowBytes;
 
-static void copyToLCD16(const SkBitmap& src, const SkMask& dst) {
-    SkASSERT(SkBitmap::kA8_Config == src.config());
-    SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
-
-    const int width = dst.fBounds.width();
-    const int height = dst.fBounds.height();
-    const uint8_t* srcP = src.getAddr8(0, 0);
-    size_t srcRB = src.rowBytes();
-    uint16_t* dstP = (uint16_t*)dst.fImage;
-    size_t dstRB = dst.fRowBytes;
-    for (int y = 0; y < height; ++y) {
-        for (int x = 0; x < width; ++x) {
-            dstP[x] = a8_to_rgb565(srcP[x]);
+    for (int y = mask.fBounds.height() - 1; y >= 0; --y) {
+        for (int x = mask.fBounds.width() - 1; x >= 0; --x) {
+            dst[x] = lut[dst[x]];
         }
-        srcP += srcRB;
-        dstP = (uint16_t*)((char*)dstP + dstRB);
+        dst += rowBytes;
     }
 }
 
-#define SK_FREETYPE_LCD_LERP    160
-
-static int lerp(int start, int end) {
-    SkASSERT((unsigned)SK_FREETYPE_LCD_LERP <= 256);
-    return start + ((end - start) * (SK_FREETYPE_LCD_LERP) >> 8);
-}
-
-static uint16_t packLCD16(unsigned r, unsigned g, unsigned b) {
-    if (SK_FREETYPE_LCD_LERP) {
-        // want (a+b+c)/3, but we approx to avoid the divide
-        unsigned ave = (5 * (r + g + b) + g) >> 4;
-        r = lerp(r, ave);
-        g = lerp(g, ave);
-        b = lerp(b, ave);
-    }
-    return SkPackRGB16(r >> 3, g >> 2, b >> 3);
-}
-
-static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst) {
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD16(const SkBitmap& src, const SkMask& dst,
+                           const SkMaskGamma::PreBlend& maskPreBlend) {
     SkASSERT(SkBitmap::kA8_Config == src.config());
     SkASSERT(SkMask::kLCD16_Format == dst.fFormat);
-    
+
     const int width = dst.fBounds.width();
     const int height = dst.fBounds.height();
     uint16_t* dstP = (uint16_t*)dst.fImage;
     size_t dstRB = dst.fRowBytes;
+
     for (int y = 0; y < height; ++y) {
         const uint8_t* srcP = src.getAddr8(0, y);
         for (int x = 0; x < width; ++x) {
-            unsigned r = *srcP++;
-            unsigned g = *srcP++;
-            unsigned b = *srcP++;
-            dstP[x] = packLCD16(r, g, b);
+            U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fR);
+            U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fG);
+            U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fB);
+            dstP[x] = SkPack888ToRGB16(r, g, b);
         }
         dstP = (uint16_t*)((char*)dstP + dstRB);
     }
 }
 
-static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst) {
+template<bool APPLY_PREBLEND>
+static void pack3xHToLCD32(const SkBitmap& src, const SkMask& dst,
+                           const SkMaskGamma::PreBlend& maskPreBlend) {
     SkASSERT(SkBitmap::kA8_Config == src.config());
     SkASSERT(SkMask::kLCD32_Format == dst.fFormat);
-    
+
     const int width = dst.fBounds.width();
     const int height = dst.fBounds.height();
     SkPMColor* dstP = (SkPMColor*)dst.fImage;
     size_t dstRB = dst.fRowBytes;
+
     for (int y = 0; y < height; ++y) {
         const uint8_t* srcP = src.getAddr8(0, y);
         for (int x = 0; x < width; ++x) {
-            unsigned r = *srcP++;
-            unsigned g = *srcP++;
-            unsigned b = *srcP++;
-            unsigned a = SkMax32(SkMax32(r, g), b);
-            dstP[x] = SkPackARGB32(a, r, g, b);
+            U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fR);
+            U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fG);
+            U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>(*srcP++, maskPreBlend.fB);
+            dstP[x] = SkPackARGB32(0xFF, r, g, b);
         }
         dstP = (SkPMColor*)((char*)dstP + dstRB);
     }
 }
 
-static void generateMask(const SkMask& mask, const SkPath& path) {
+static void generateMask(const SkMask& mask, const SkPath& path,
+                         const SkMaskGamma::PreBlend& maskPreBlend) {
     SkBitmap::Config config;
     SkPaint     paint;
 
@@ -487,32 +466,46 @@
     bm.setConfig(config, dstW, dstH, dstRB);
 
     if (0 == dstRB) {
-        bm.allocPixels();
+        if (!bm.allocPixels()) {
+            // can't allocate offscreen, so empty the mask and return
+            sk_bzero(mask.fImage, mask.computeImageSize());
+            return;
+        }
         bm.lockPixels();
     } else {
         bm.setPixels(mask.fImage);
     }
     sk_bzero(bm.getPixels(), bm.getSafeSize());
-    
+
     SkDraw  draw;
-    sk_bzero(&draw, sizeof(draw));
     draw.fRC    = &clip;
     draw.fClip  = &clip.bwRgn();
     draw.fMatrix = &matrix;
     draw.fBitmap = &bm;
     draw.drawPath(path, paint);
-    
-    if (0 == dstRB) {
-        switch (mask.fFormat) {
-            case SkMask::kLCD16_Format:
-                pack3xHToLCD16(bm, mask);
-                break;
-            case SkMask::kLCD32_Format:
-                pack3xHToLCD32(bm, mask);
-                break;
-            default:
-                SkDEBUGFAIL("bad format for copyback");
-        }
+
+    switch (mask.fFormat) {
+        case SkMask::kA8_Format:
+            if (maskPreBlend.isApplicable()) {
+                applyLUTToA8Mask(mask, maskPreBlend.fG);
+            }
+            break;
+        case SkMask::kLCD16_Format:
+            if (maskPreBlend.isApplicable()) {
+                pack3xHToLCD16<true>(bm, mask, maskPreBlend);
+            } else {
+                pack3xHToLCD16<false>(bm, mask, maskPreBlend);
+            }
+            break;
+        case SkMask::kLCD32_Format:
+            if (maskPreBlend.isApplicable()) {
+                pack3xHToLCD32<true>(bm, mask, maskPreBlend);
+            } else {
+                pack3xHToLCD32<false>(bm, mask, maskPreBlend);
+            }
+            break;
+        default:
+            break;
     }
 }
 
@@ -554,8 +547,11 @@
                                         SkMask::kJustRenderImage_CreateMode)) {
                 return;
             }
+            if (fPreBlend.isApplicable()) {
+                applyLUTToA8Mask(mask, fPreBlend.fG);
+            }
         } else {
-            generateMask(mask, devPath);
+            generateMask(mask, devPath, fPreBlend);
         }
     } else {
         this->getGlyphContext(*glyph)->generateImage(*glyph);
@@ -593,6 +589,10 @@
                 dst += dstRB;
             }
             SkMask::FreeImage(dstM.fImage);
+
+            if (fPreBlendForFilter.isApplicable()) {
+                applyLUTToA8Mask(srcM, fPreBlendForFilter.fG);
+            }
         }
     }
 }
@@ -635,30 +635,37 @@
         SkMatrix    matrix, inverse;
 
         fRec.getMatrixFrom2x2(&matrix);
-        matrix.invert(&inverse);
+        if (!matrix.invert(&inverse)) {
+            // assume fillPath and devPath are already empty.
+            return;
+        }
         path.transform(inverse, &localPath);
         // now localPath is only affected by the paint settings, and not the canvas matrix
 
-        SkScalar width = fRec.fFrameWidth;
+        SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
+
+        if (fRec.fFrameWidth > 0) {
+            rec.setStrokeStyle(fRec.fFrameWidth,
+                               SkToBool(fRec.fFlags & kFrameAndFill_Flag));
+            // glyphs are always closed contours, so cap type is ignored,
+            // so we just pass something.
+            rec.setStrokeParams(SkPaint::kButt_Cap,
+                                (SkPaint::Join)fRec.fStrokeJoin,
+                                fRec.fMiterLimit);
+        }
 
         if (fPathEffect) {
             SkPath effectPath;
-
-            if (fPathEffect->filterPath(&effectPath, localPath, &width)) {
+            if (fPathEffect->filterPath(&effectPath, localPath, &rec, NULL)) {
                 localPath.swap(effectPath);
             }
         }
 
-        if (width > 0) {
-            SkStroke    stroker;
-            SkPath      outline;
-
-            stroker.setWidth(width);
-            stroker.setMiterLimit(fRec.fMiterLimit);
-            stroker.setJoin((SkPaint::Join)fRec.fStrokeJoin);
-            stroker.setDoFill(SkToBool(fRec.fFlags & kFrameAndFill_Flag));
-            stroker.strokePath(localPath, &outline);
-            localPath.swap(outline);
+        if (rec.needToApply()) {
+            SkPath strokePath;
+            if (rec.applyToPath(&strokePath, localPath)) {
+                localPath.swap(strokePath);
+            }
         }
 
         // now return stuff to the caller
@@ -697,22 +704,20 @@
 }
 
 
-void SkScalerContext::Rec::getMatrixFrom2x2(SkMatrix* dst) const {
-    dst->reset();
-    dst->setScaleX(fPost2x2[0][0]);
-    dst->setSkewX( fPost2x2[0][1]);
-    dst->setSkewY( fPost2x2[1][0]);
-    dst->setScaleY(fPost2x2[1][1]);
+void SkScalerContextRec::getMatrixFrom2x2(SkMatrix* dst) const {
+    dst->setAll(fPost2x2[0][0], fPost2x2[0][1], 0,
+                fPost2x2[1][0], fPost2x2[1][1], 0,
+                0,              0,              SkScalarToPersp(SK_Scalar1));
 }
 
-void SkScalerContext::Rec::getLocalMatrix(SkMatrix* m) const {
+void SkScalerContextRec::getLocalMatrix(SkMatrix* m) const {
     m->setScale(SkScalarMul(fTextSize, fPreScaleX), fTextSize);
     if (fPreSkewX) {
         m->postSkew(fPreSkewX, 0);
     }
 }
 
-void SkScalerContext::Rec::getSingleMatrix(SkMatrix* m) const {
+void SkScalerContextRec::getSingleMatrix(SkMatrix* m) const {
     this->getLocalMatrix(m);
 
     //  now concat the device matrix
@@ -723,7 +728,7 @@
 
 SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix) {
     SkASSERT(!matrix.hasPerspective());
-    
+
     if (0 == matrix[SkMatrix::kMSkewY]) {
         return kX_SkAxisAlignment;
     }
@@ -742,22 +747,22 @@
     SkScalerContext_Empty(const SkDescriptor* desc) : SkScalerContext(desc) {}
 
 protected:
-    virtual unsigned generateGlyphCount() {
+    virtual unsigned generateGlyphCount() SK_OVERRIDE {
         return 0;
     }
-    virtual uint16_t generateCharToGlyph(SkUnichar uni) {
+    virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE {
         return 0;
     }
-    virtual void generateAdvance(SkGlyph* glyph) {
+    virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE {
         glyph->zeroMetrics();
     }
-    virtual void generateMetrics(SkGlyph* glyph) {
+    virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE {
         glyph->zeroMetrics();
     }
-    virtual void generateImage(const SkGlyph& glyph) {}
-    virtual void generatePath(const SkGlyph& glyph, SkPath* path) {}
+    virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE {}
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE {}
     virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
-                                     SkPaint::FontMetrics* my) {
+                                     SkPaint::FontMetrics* my) SK_OVERRIDE {
         if (mx) {
             sk_bzero(mx, sizeof(*mx));
         }
@@ -770,13 +775,12 @@
 extern SkScalerContext* SkCreateColorScalerContext(const SkDescriptor* desc);
 
 SkScalerContext* SkScalerContext::Create(const SkDescriptor* desc) {
-	SkScalerContext* c = NULL;  //SkCreateColorScalerContext(desc);
-	if (NULL == c) {
-		c = SkFontHost::CreateScalerContext(desc);
-	}
+    SkScalerContext* c = NULL;  //SkCreateColorScalerContext(desc);
+    if (NULL == c) {
+        c = SkFontHost::CreateScalerContext(desc);
+    }
     if (NULL == c) {
         c = SkNEW_ARGS(SkScalerContext_Empty, (desc));
     }
     return c;
 }
-
diff --git a/src/core/SkScalerContext.h b/src/core/SkScalerContext.h
new file mode 100644
index 0000000..a586547
--- /dev/null
+++ b/src/core/SkScalerContext.h
@@ -0,0 +1,294 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkScalerContext_DEFINED
+#define SkScalerContext_DEFINED
+
+#include "SkMask.h"
+#include "SkMaskGamma.h"
+#include "SkMatrix.h"
+#include "SkPaint.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+    #include "SkLanguage.h"
+    //For SkFontID
+    #include "SkTypeface.h"
+#endif
+
+struct SkGlyph;
+class SkDescriptor;
+class SkMaskFilter;
+class SkPathEffect;
+class SkRasterizer;
+
+/*
+ *  To allow this to be forward-declared, it must be its own typename, rather
+ *  than a nested struct inside SkScalerContext (where it started).
+ */
+struct SkScalerContextRec {
+    uint32_t    fOrigFontID;
+    uint32_t    fFontID;
+    SkScalar    fTextSize, fPreScaleX, fPreSkewX;
+    SkScalar    fPost2x2[2][2];
+    SkScalar    fFrameWidth, fMiterLimit;
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+    SkScalar    fHintingScaleFactor;
+#endif
+#ifdef SK_BUILD_FOR_ANDROID
+    SkLanguage fLanguage;
+    SkPaint::FontVariant fFontVariant;
+#endif
+
+    //These describe the parameters to create (uniquely identify) the pre-blend.
+    uint32_t    fLumBits;
+    uint8_t     fDeviceGamma; //2.6, (0.0, 4.0) gamma, 0.0 for sRGB
+    uint8_t     fPaintGamma;  //2.6, (0.0, 4.0) gamma, 0.0 for sRGB
+    uint8_t     fContrast;    //0.8+1, [0.0, 1.0] artificial contrast
+    uint8_t     fReservedAlign;
+
+    SkScalar getDeviceGamma() const {
+        return SkIntToScalar(fDeviceGamma) / (1 << 6);
+    }
+    void setDeviceGamma(SkScalar dg) {
+        SkASSERT(0 <= dg && dg < SkIntToScalar(4));
+        fDeviceGamma = SkScalarFloorToInt(dg * (1 << 6));
+    }
+
+    SkScalar getPaintGamma() const {
+        return SkIntToScalar(fPaintGamma) / (1 << 6);
+    }
+    void setPaintGamma(SkScalar pg) {
+        SkASSERT(0 <= pg && pg < SkIntToScalar(4));
+        fPaintGamma = SkScalarFloorToInt(pg * (1 << 6));
+    }
+
+    SkScalar getContrast() const {
+        return SkIntToScalar(fContrast) / ((1 << 8) - 1);
+    }
+    void setContrast(SkScalar c) {
+        SkASSERT(0 <= c && c <= SK_Scalar1);
+        fContrast = SkScalarRoundToInt(c * ((1 << 8) - 1));
+    }
+
+    /**
+     *  Causes the luminance color and contrast to be ignored, and the
+     *  paint and device gamma to be effectively 1.0.
+     */
+    void ignorePreBlend() {
+        setLuminanceColor(SK_ColorTRANSPARENT);
+        setPaintGamma(SK_Scalar1);
+        setDeviceGamma(SK_Scalar1);
+        setContrast(0);
+    }
+
+    uint8_t     fMaskFormat;
+    uint8_t     fStrokeJoin;
+    uint16_t    fFlags;
+    // Warning: when adding members note that the size of this structure
+    // must be a multiple of 4. SkDescriptor requires that its arguments be
+    // multiples of four and this structure is put in an SkDescriptor in
+    // SkPaint::MakeRec.
+
+    void    getMatrixFrom2x2(SkMatrix*) const;
+    void    getLocalMatrix(SkMatrix*) const;
+    void    getSingleMatrix(SkMatrix*) const;
+
+    inline SkPaint::Hinting getHinting() const;
+    inline void setHinting(SkPaint::Hinting);
+
+    SkMask::Format getFormat() const {
+        return static_cast<SkMask::Format>(fMaskFormat);
+    }
+
+    SkColor getLuminanceColor() const {
+        return fLumBits;
+    }
+
+    void setLuminanceColor(SkColor c) {
+        fLumBits = c;
+    }
+};
+
+//The following typedef hides from the rest of the implementation the number of
+//most significant bits to consider when creating mask gamma tables. Two bits
+//per channel was chosen as a balance between fidelity (more bits) and cache
+//sizes (fewer bits). Three bits per channel was chosen when #303942; (used by
+//the Chrome UI) turned out too green.
+typedef SkTMaskGamma<3, 3, 3> SkMaskGamma;
+
+class SkScalerContext {
+public:
+    typedef SkScalerContextRec Rec;
+
+    enum Flags {
+        kFrameAndFill_Flag        = 0x0001,
+        kDevKernText_Flag         = 0x0002,
+        kEmbeddedBitmapText_Flag  = 0x0004,
+        kEmbolden_Flag            = 0x0008,
+        kSubpixelPositioning_Flag = 0x0010,
+        kAutohinting_Flag         = 0x0020,
+        kVertical_Flag            = 0x0040,
+
+        // together, these two flags resulting in a two bit value which matches
+        // up with the SkPaint::Hinting enum.
+        kHinting_Shift            = 7, // to shift into the other flags above
+        kHintingBit1_Flag         = 0x0080,
+        kHintingBit2_Flag         = 0x0100,
+
+        // these should only ever be set if fMaskFormat is LCD16 or LCD32
+        kLCD_Vertical_Flag        = 0x0200,    // else Horizontal
+        kLCD_BGROrder_Flag        = 0x0400,    // else RGB order
+
+        // Generate A8 from LCD source (for GDI), only meaningful if fMaskFormat is kA8
+        // Perhaps we can store this (instead) in fMaskFormat, in hight bit?
+        kGenA8FromLCD_Flag        = 0x0800,
+    };
+
+    // computed values
+    enum {
+        kHinting_Mask   = kHintingBit1_Flag | kHintingBit2_Flag,
+    };
+
+
+    SkScalerContext(const SkDescriptor* desc);
+    virtual ~SkScalerContext();
+
+    SkMask::Format getMaskFormat() const {
+        return (SkMask::Format)fRec.fMaskFormat;
+    }
+
+    bool isSubpixel() const {
+        return SkToBool(fRec.fFlags & kSubpixelPositioning_Flag);
+    }
+
+    // remember our glyph offset/base
+    void setBaseGlyphCount(unsigned baseGlyphCount) {
+        fBaseGlyphCount = baseGlyphCount;
+    }
+
+    /** Return the corresponding glyph for the specified unichar. Since contexts
+        may be chained (under the hood), the glyphID that is returned may in
+        fact correspond to a different font/context. In that case, we use the
+        base-glyph-count to know how to translate back into local glyph space.
+     */
+    uint16_t charToGlyphID(SkUnichar uni);
+
+    /** Map the glyphID to its glyph index, and then to its char code. Unmapped
+        glyphs return zero.
+    */
+    SkUnichar glyphIDToChar(uint16_t glyphID);
+
+    unsigned    getGlyphCount() { return this->generateGlyphCount(); }
+    void        getAdvance(SkGlyph*);
+    void        getMetrics(SkGlyph*);
+    void        getImage(const SkGlyph&);
+    void        getPath(const SkGlyph&, SkPath*);
+    void        getFontMetrics(SkPaint::FontMetrics* mX,
+                               SkPaint::FontMetrics* mY);
+
+#ifdef SK_BUILD_FOR_ANDROID
+    unsigned getBaseGlyphCount(SkUnichar charCode);
+
+    // This function must be public for SkTypeface_android.h, but should not be
+    // called by other callers
+    SkFontID findTypefaceIdForChar(SkUnichar uni);
+#endif
+
+    static inline void MakeRec(const SkPaint&, const SkDeviceProperties* deviceProperties,
+                               const SkMatrix*, Rec* rec);
+    static inline void PostMakeRec(const SkPaint&, Rec*);
+
+    static SkScalerContext* Create(const SkDescriptor*);
+    static SkMaskGamma::PreBlend GetMaskPreBlend(const Rec& rec);
+
+protected:
+    Rec         fRec;
+    unsigned    fBaseGlyphCount;
+
+    virtual unsigned generateGlyphCount() = 0;
+    virtual uint16_t generateCharToGlyph(SkUnichar) = 0;
+    virtual void generateAdvance(SkGlyph*) = 0;
+    virtual void generateMetrics(SkGlyph*) = 0;
+    virtual void generateImage(const SkGlyph&) = 0;
+    virtual void generatePath(const SkGlyph&, SkPath*) = 0;
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
+                                     SkPaint::FontMetrics* mY) = 0;
+    // default impl returns 0, indicating failure.
+    virtual SkUnichar generateGlyphToChar(uint16_t);
+
+    void forceGenerateImageFromPath() { fGenerateImageFromPath = true; }
+
+private:
+    SkScalerContext* getContextFromChar(SkUnichar uni, unsigned& glyphID);
+
+    SkPathEffect*   fPathEffect;
+    SkMaskFilter*   fMaskFilter;
+    SkRasterizer*   fRasterizer;
+
+    // if this is set, we draw the image from a path, rather than
+    // calling generateImage.
+    bool fGenerateImageFromPath;
+
+    void internalGetPath(const SkGlyph& glyph, SkPath* fillPath,
+                         SkPath* devPath, SkMatrix* fillToDevMatrix);
+
+    // return the next context, treating fNextContext as a cache of the answer
+    SkScalerContext* getNextContext();
+
+    // returns the right context from our link-list for this glyph. If no match
+    // is found, just returns the original context (this)
+    SkScalerContext* getGlyphContext(const SkGlyph& glyph);
+
+    // link-list of context, to handle missing chars. null-terminated.
+    SkScalerContext* fNextContext;
+
+    // SkMaskGamma::PreBlend converts linear masks to gamma correcting masks.
+protected:
+    // Visible to subclasses so that generateImage can apply the pre-blend directly.
+    const SkMaskGamma::PreBlend fPreBlend;
+private:
+    // When there is a filter, previous steps must create a linear mask
+    // and the pre-blend applied as a final step.
+    const SkMaskGamma::PreBlend fPreBlendForFilter;
+};
+
+#define kRec_SkDescriptorTag            SkSetFourByteTag('s', 'r', 'e', 'c')
+#define kPathEffect_SkDescriptorTag     SkSetFourByteTag('p', 't', 'h', 'e')
+#define kMaskFilter_SkDescriptorTag     SkSetFourByteTag('m', 's', 'k', 'f')
+#define kRasterizer_SkDescriptorTag     SkSetFourByteTag('r', 'a', 's', 't')
+
+///////////////////////////////////////////////////////////////////////////////
+
+enum SkAxisAlignment {
+    kNone_SkAxisAlignment,
+    kX_SkAxisAlignment,
+    kY_SkAxisAlignment
+};
+
+/**
+ *  Return the axis (if any) that the baseline for horizontal text will land on
+ *  after running through the specified matrix.
+ *
+ *  As an example, the identity matrix will return kX_SkAxisAlignment
+ */
+SkAxisAlignment SkComputeAxisAlignmentForHText(const SkMatrix& matrix);
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkPaint::Hinting SkScalerContextRec::getHinting() const {
+    unsigned hint = (fFlags & SkScalerContext::kHinting_Mask) >>
+                                            SkScalerContext::kHinting_Shift;
+    return static_cast<SkPaint::Hinting>(hint);
+}
+
+void SkScalerContextRec::setHinting(SkPaint::Hinting hinting) {
+    fFlags = (fFlags & ~SkScalerContext::kHinting_Mask) |
+                                (hinting << SkScalerContext::kHinting_Shift);
+}
+
+
+#endif
diff --git a/src/core/SkScan.cpp b/src/core/SkScan.cpp
index cee328f..44968bd 100644
--- a/src/core/SkScan.cpp
+++ b/src/core/SkScan.cpp
@@ -21,7 +21,7 @@
         if (clip) {
             if (clip->isRect()) {
                 const SkIRect& clipBounds = clip->getBounds();
-                
+
                 if (clipBounds.contains(r)) {
                     blitrect(blitter, r);
                 } else {
@@ -33,7 +33,7 @@
             } else {
                 SkRegion::Cliperator    cliper(*clip, r);
                 const SkIRect&          rr = cliper.rect();
-                
+
                 while (!cliper.done()) {
                     blitrect(blitter, rr);
                     cliper.next();
@@ -48,7 +48,7 @@
 void SkScan::FillXRect(const SkXRect& xr, const SkRegion* clip,
                        SkBlitter* blitter) {
     SkIRect r;
-    
+
     XRect_round(xr, &r);
     SkScan::FillIRect(r, clip, blitter);
 }
@@ -58,7 +58,7 @@
 void SkScan::FillRect(const SkRect& r, const SkRegion* clip,
                        SkBlitter* blitter) {
     SkIRect ir;
-    
+
     r.round(&ir);
     SkScan::FillIRect(ir, clip, blitter);
 }
@@ -72,7 +72,7 @@
     if (clip.isEmpty() || r.isEmpty()) {
         return;
     }
-    
+
     if (clip.isBW()) {
         FillIRect(r, &clip.bwRgn(), blitter);
         return;
@@ -87,7 +87,7 @@
     if (clip.isEmpty() || xr.isEmpty()) {
         return;
     }
-    
+
     if (clip.isBW()) {
         FillXRect(xr, &clip.bwRgn(), blitter);
         return;
@@ -104,7 +104,7 @@
     if (clip.isEmpty() || r.isEmpty()) {
         return;
     }
-    
+
     if (clip.isBW()) {
         FillRect(r, &clip.bwRgn(), blitter);
         return;
@@ -115,4 +115,3 @@
 }
 
 #endif
-
diff --git a/src/core/SkScan.h b/src/core/SkScan.h
new file mode 100644
index 0000000..5989435
--- /dev/null
+++ b/src/core/SkScan.h
@@ -0,0 +1,138 @@
+
+/*
+ * Copyright 2011 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 SkScan_DEFINED
+#define SkScan_DEFINED
+
+#include "SkRect.h"
+
+class SkRasterClip;
+class SkRegion;
+class SkBlitter;
+class SkPath;
+
+/** Defines a fixed-point rectangle, identical to the integer SkIRect, but its
+    coordinates are treated as SkFixed rather than int32_t.
+*/
+typedef SkIRect SkXRect;
+
+class SkScan {
+public:
+    static void FillPath(const SkPath&, const SkIRect&, SkBlitter*);
+
+    ///////////////////////////////////////////////////////////////////////////
+    // rasterclip
+
+    static void FillIRect(const SkIRect&, const SkRasterClip&, SkBlitter*);
+    static void FillXRect(const SkXRect&, const SkRasterClip&, SkBlitter*);
+#ifdef SK_SCALAR_IS_FIXED
+    static void FillRect(const SkRect& rect, const SkRasterClip& clip,
+                         SkBlitter* blitter) {
+        SkScan::FillXRect(*(const SkXRect*)&rect, clip, blitter);
+    }
+    static void AntiFillRect(const SkRect& rect, const SkRasterClip& clip,
+                             SkBlitter* blitter) {
+        SkScan::AntiFillXRect(*(const SkXRect*)&rect, clip, blitter);
+    }
+#else
+    static void FillRect(const SkRect&, const SkRasterClip&, SkBlitter*);
+    static void AntiFillRect(const SkRect&, const SkRasterClip&, SkBlitter*);
+#endif
+    static void AntiFillXRect(const SkXRect&, const SkRasterClip&, SkBlitter*);
+    static void FillPath(const SkPath&, const SkRasterClip&, SkBlitter*);
+    static void AntiFillPath(const SkPath&, const SkRasterClip&, SkBlitter*);
+    static void FrameRect(const SkRect&, const SkPoint& strokeSize,
+                          const SkRasterClip&, SkBlitter*);
+    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 HairRect(const SkRect&, const SkRasterClip&, SkBlitter*);
+    static void AntiHairRect(const SkRect&, const SkRasterClip&, SkBlitter*);
+    static void HairPath(const SkPath&, const SkRasterClip&, SkBlitter*);
+    static void AntiHairPath(const SkPath&, const SkRasterClip&, SkBlitter*);
+
+private:
+    friend class SkAAClip;
+    friend class SkRegion;
+
+    static void FillIRect(const SkIRect&, const SkRegion* clip, SkBlitter*);
+    static void FillXRect(const SkXRect&, const SkRegion* clip, SkBlitter*);
+#ifdef SK_SCALAR_IS_FIXED
+    static void FillRect(const SkRect& rect, const SkRegion* clip,
+                         SkBlitter* blitter) {
+        SkScan::FillXRect(*(const SkXRect*)&rect, clip, blitter);
+    }
+    static void AntiFillRect(const SkRect& rect, const SkRegion* clip,
+                             SkBlitter* blitter) {
+        SkScan::AntiFillXRect(*(const SkXRect*)&rect, clip, blitter);
+    }
+#else
+    static void FillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+    static void AntiFillRect(const SkRect&, const SkRegion* clip, SkBlitter*);
+#endif
+    static void AntiFillXRect(const SkXRect&, const SkRegion*, SkBlitter*);
+    static void FillPath(const SkPath&, const SkRegion& clip, SkBlitter*);
+    static void AntiFillPath(const SkPath&, const SkRegion& clip, SkBlitter*,
+                             bool forceRLE = false);
+    static void FillTriangle(const SkPoint pts[], const SkRegion*, SkBlitter*);
+
+    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*);
+};
+
+/** Assign an SkXRect from a SkIRect, by promoting the src rect's coordinates
+    from int to SkFixed. Does not check for overflow if the src coordinates
+    exceed 32K
+*/
+static inline void XRect_set(SkXRect* xr, const SkIRect& src) {
+    xr->fLeft = SkIntToFixed(src.fLeft);
+    xr->fTop = SkIntToFixed(src.fTop);
+    xr->fRight = SkIntToFixed(src.fRight);
+    xr->fBottom = SkIntToFixed(src.fBottom);
+}
+
+/** Assign an SkXRect from a SkRect, by promoting the src rect's coordinates
+    from SkScalar to SkFixed. Does not check for overflow if the src coordinates
+    exceed 32K
+*/
+static inline void XRect_set(SkXRect* xr, const SkRect& src) {
+    xr->fLeft = SkScalarToFixed(src.fLeft);
+    xr->fTop = SkScalarToFixed(src.fTop);
+    xr->fRight = SkScalarToFixed(src.fRight);
+    xr->fBottom = SkScalarToFixed(src.fBottom);
+}
+
+/** Round the SkXRect coordinates, and store the result in the SkIRect.
+*/
+static inline void XRect_round(const SkXRect& xr, SkIRect* dst) {
+    dst->fLeft = SkFixedRound(xr.fLeft);
+    dst->fTop = SkFixedRound(xr.fTop);
+    dst->fRight = SkFixedRound(xr.fRight);
+    dst->fBottom = SkFixedRound(xr.fBottom);
+}
+
+/** Round the SkXRect coordinates out (i.e. use floor for left/top, and ceiling
+    for right/bottom), and store the result in the SkIRect.
+*/
+static inline void XRect_roundOut(const SkXRect& xr, SkIRect* dst) {
+    dst->fLeft = SkFixedFloor(xr.fLeft);
+    dst->fTop = SkFixedFloor(xr.fTop);
+    dst->fRight = SkFixedCeil(xr.fRight);
+    dst->fBottom = SkFixedCeil(xr.fBottom);
+}
+
+#endif
diff --git a/src/core/SkScanPriv.h b/src/core/SkScanPriv.h
index 96ed5ab..75ceee0 100644
--- a/src/core/SkScanPriv.h
+++ b/src/core/SkScanPriv.h
@@ -15,7 +15,8 @@
 
 class SkScanClipper {
 public:
-    SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& bounds);
+    SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& bounds,
+                  bool skipRejectTest = false);
 
     SkBlitter*      getBlitter() const { return fBlitter; }
     const SkIRect*  getClipRect() const { return fClipRect; }
@@ -37,4 +38,3 @@
 void sk_blit_below(SkBlitter*, const SkIRect& avoid, const SkRegion& clip);
 
 #endif
-
diff --git a/src/core/SkScan_AntiPath.cpp b/src/core/SkScan_AntiPath.cpp
index 0834caa..0d2152c 100644
--- a/src/core/SkScan_AntiPath.cpp
+++ b/src/core/SkScan_AntiPath.cpp
@@ -30,19 +30,14 @@
         abbreviated with 'i' or 'I' in variable names
     - supersampled coordinates, scale equal to the output * SCALE
 
-    NEW_AA is a set of code-changes to try to make both paths produce identical
-    results. Its not quite there yet, though the remaining differences may be
-    in the subsequent blits, and not in the different masks/runs...
-
-    SK_USE_EXACT_COVERAGE makes coverage_to_partial_alpha() behave similarly to
-    coverage_to_exact_alpha(). Enabling it will requrie rebaselining about 1/3
-    of GMs for changes in the 3 least significant bits along the edges of
-    antialiased spans.
+    Enabling SK_USE_LEGACY_AA_COVERAGE keeps the aa coverage calculations as
+    they were before the fix that unified the output of the RLE and MASK
+    supersamplers.
  */
+
 //#define FORCE_SUPERMASK
 //#define FORCE_RLE
-//#define SK_SUPPORT_NEW_AA
-//#define SK_USE_EXACT_COVERAGE
+//#define SK_USE_LEGACY_AA_COVERAGE
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -90,7 +85,7 @@
      */
     const int left = clip.getBounds().fLeft;
     const int right = clip.getBounds().fRight;
-    
+
     fLeft = left;
     fSuperLeft = left << SHIFT;
     fWidth = right - left;
@@ -165,13 +160,11 @@
     coverage_to_exact_alpha().
 */
 static inline int coverage_to_partial_alpha(int aa) {
-#ifdef SK_USE_EXACT_COVERAGE
-    return aa << (8 - 2 * SHIFT);
-#else
     aa <<= 8 - 2*SHIFT;
+#ifdef SK_USE_LEGACY_AA_COVERAGE
     aa -= aa >> (8 - SHIFT - 1);
-    return aa;
 #endif
+    return aa;
 }
 
 /** coverage_to_exact_alpha() is being used by our blitter, which wants
@@ -204,7 +197,7 @@
         fOffsetX = 0;
         fCurrY = y;
     }
-    
+
     if (iy != fCurrIY) {  // new scanline
         this->flush();
         fCurrIY = iy;
@@ -242,6 +235,7 @@
 #endif
 }
 
+#if 0 // UNUSED
 static void set_left_rite_runs(SkAlphaRuns& runs, int ileft, U8CPU leftA,
                                int n, U8CPU riteA) {
     SkASSERT(leftA <= 0xFF);
@@ -277,6 +271,7 @@
     }
     run[0] = 0;
 }
+#endif
 
 void SuperBlitter::blitRect(int x, int y, int width, int height) {
     SkASSERT(width > 0);
@@ -452,6 +447,17 @@
     return (pair << 16) | pair;
 }
 
+// Perform this tricky subtract, to avoid overflowing to 256. Our caller should
+// only ever call us with at most enough to hit 256 (never larger), so it is
+// enough to just subtract the high-bit. Actually clamping with a branch would
+// be slower (e.g. if (tmp > 255) tmp = 255;)
+//
+static inline void saturated_add(uint8_t* ptr, U8CPU add) {
+    unsigned tmp = *ptr + add;
+    SkASSERT(tmp <= 256);
+    *ptr = SkToU8(tmp - (tmp >> 8));
+}
+
 // minimum count before we want to setup an inner loop, adding 4-at-a-time
 #define MIN_COUNT_FOR_QUAD_LOOP  16
 
@@ -459,22 +465,8 @@
                         U8CPU stopAlpha, U8CPU maxValue) {
     SkASSERT(middleCount >= 0);
 
-    /*  I should be able to just add alpha[x] + startAlpha.
-        However, if the trailing edge of the previous span and the leading
-        edge of the current span round to the same super-sampled x value,
-        I might overflow to 256 with this add, hence the funny subtract.
-    */
-#ifdef SK_SUPPORT_NEW_AA
-    if (startAlpha) {
-        unsigned tmp = *alpha + startAlpha;
-        SkASSERT(tmp <= 256);
-        *alpha++ = SkToU8(tmp - (tmp >> 8));
-    }
-#else
-    unsigned tmp = *alpha + startAlpha;
-    SkASSERT(tmp <= 256);
-    *alpha++ = SkToU8(tmp - (tmp >> 8));
-#endif
+    saturated_add(alpha, startAlpha);
+    alpha += 1;
 
     if (middleCount >= MIN_COUNT_FOR_QUAD_LOOP) {
         // loop until we're quad-byte aligned
@@ -505,7 +497,7 @@
     // only happens if stopAlpha is also 0. Rather than test for stopAlpha != 0
     // every time (slow), we just do it, and ensure that we've allocated extra space
     // (see the + 1 comment in fStorage[]
-    *alpha = SkToU8(*alpha + stopAlpha);
+    saturated_add(alpha, stopAlpha);
 }
 
 void MaskSuperBlitter::blitH(int x, int y, int width) {
@@ -552,15 +544,7 @@
         SkASSERT(row < fMask.fImage + kMAX_STORAGE + 1);
         add_aa_span(row, coverage_to_partial_alpha(fe - fb));
     } else {
-#ifdef SK_SUPPORT_NEW_AA
-        if (0 == fb) {
-            n += 1;
-        } else {
-            fb = SCALE - fb;
-        }
-#else
         fb = SCALE - fb;
-#endif
         SkASSERT(row >= fMask.fImage);
         SkASSERT(row + n + 1 < fMask.fImage + kMAX_STORAGE + 1);
         add_aa_span(row,  coverage_to_partial_alpha(fb),
@@ -730,13 +714,13 @@
     if (clip.isEmpty()) {
         return;
     }
-    
+
     if (clip.isBW()) {
         FillPath(path, clip.bwRgn(), blitter);
     } else {
         SkRegion        tmp;
         SkAAClipBlitter aaBlitter;
-        
+
         tmp.setRect(clip.getBounds());
         aaBlitter.init(blitter, &clip.aaRgn());
         SkScan::FillPath(path, tmp, &aaBlitter);
@@ -760,4 +744,3 @@
         SkScan::AntiFillPath(path, tmp, &aaBlitter, true);
     }
 }
-
diff --git a/src/core/SkScan_Antihair.cpp b/src/core/SkScan_Antihair.cpp
index 3b28634..a6a0869 100644
--- a/src/core/SkScan_Antihair.cpp
+++ b/src/core/SkScan_Antihair.cpp
@@ -86,45 +86,83 @@
     } while (count > 0);
 }
 
-static SkFixed hline(int x, int stopx, SkFixed fy, SkFixed /*slope*/,
-                     SkBlitter* blitter, int mod64) {
-    SkASSERT(x < stopx);
-    int count = stopx - x;
-    fy += SK_Fixed1/2;
+class SkAntiHairBlitter {
+public:
+    SkAntiHairBlitter() : fBlitter(NULL) {}
+    virtual ~SkAntiHairBlitter() {}
 
-    int y = fy >> 16;
-    uint8_t  a = (uint8_t)(fy >> 8);
+    SkBlitter* getBlitter() const { return fBlitter; }
 
-    // lower line
-    unsigned ma = SmallDot6Scale(a, mod64);
-    if (ma) {
-        call_hline_blitter(blitter, x, y, count, ma);
+    void setup(SkBlitter* blitter) {
+        fBlitter = blitter;
     }
 
-    // upper line
-    ma = SmallDot6Scale(255 - a, mod64);
-    if (ma) {
-        call_hline_blitter(blitter, x, y - 1, count, ma);
+    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
+    virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
+
+private:
+    SkBlitter*  fBlitter;
+};
+
+class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
+public:
+    virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) SK_OVERRIDE {
+        fy += SK_Fixed1/2;
+
+        int y = fy >> 16;
+        uint8_t  a = (uint8_t)(fy >> 8);
+
+        // lower line
+        unsigned ma = SmallDot6Scale(a, mod64);
+        if (ma) {
+            call_hline_blitter(this->getBlitter(), x, y, 1, ma);
+        }
+
+        // upper line
+        ma = SmallDot6Scale(255 - a, mod64);
+        if (ma) {
+            call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
+        }
+
+        return fy - SK_Fixed1/2;
     }
-    
-    return fy - SK_Fixed1/2;
-}
 
-static SkFixed horish(int x, int stopx, SkFixed fy, SkFixed dy,
-                      SkBlitter* blitter, int mod64) {
-    SkASSERT(x < stopx);
+    virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
+                             SkFixed slope) SK_OVERRIDE {
+        SkASSERT(x < stopx);
+        int count = stopx - x;
+        fy += SK_Fixed1/2;
 
-#ifdef TEST_GAMMA
-    const uint8_t* gamma = gGammaTable;
-#endif
-    int16_t runs[2];
-    uint8_t  aa[1];
+        int y = fy >> 16;
+        uint8_t  a = (uint8_t)(fy >> 8);
 
-    runs[0] = 1;
-    runs[1] = 0;
+        // lower line
+        if (a) {
+            call_hline_blitter(this->getBlitter(), x, y, count, a);
+        }
 
-    fy += SK_Fixed1/2;
-    do {
+        // upper line
+        a = 255 - a;
+        if (a) {
+            call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
+        }
+
+        return fy - SK_Fixed1/2;
+    }
+};
+
+class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
+public:
+    virtual 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;
+
+        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);
@@ -144,64 +182,140 @@
             SkASSERT(runs[1] == 0);
         }
         fy += dy;
-    } while (++x < stopx);
-    
-    return fy - SK_Fixed1/2;
-}
 
-static SkFixed vline(int y, int stopy, SkFixed fx, SkFixed /*slope*/,
-                     SkBlitter* blitter, int mod64) {
-    SkASSERT(y < stopy);
-    fx += SK_Fixed1/2;
-
-    int x = fx >> 16;
-    int a = (uint8_t)(fx >> 8);
-
-    unsigned ma = SmallDot6Scale(a, mod64);
-    if (ma) {
-        blitter->blitV(x, y, stopy - y, ApplyGamma(gGammaTable, ma));
+        return fy - SK_Fixed1/2;
     }
-    ma = SmallDot6Scale(255 - a, mod64);
-    if (ma) {
-        blitter->blitV(x - 1, y, stopy - y, ApplyGamma(gGammaTable, ma));
+
+    virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) SK_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);
+            }
+            fy += dy;
+        } while (++x < stopx);
+
+        return fy - SK_Fixed1/2;
     }
-    
-    return fx - SK_Fixed1/2;
-}
+};
 
-static SkFixed vertish(int y, int stopy, SkFixed fx, SkFixed dx,
-                       SkBlitter* blitter, int mod64) {
-    SkASSERT(y < stopy);
-#ifdef TEST_GAMMA
-    const uint8_t* gamma = gGammaTable;
-#endif
-    int16_t runs[3];
-    uint8_t  aa[2];
+class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
+public:
+    virtual SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) SK_OVERRIDE {
+        SkASSERT(0 == dx);
+        fx += SK_Fixed1/2;
 
-    runs[0] = 1;
-    runs[2] = 0;
+        int x = fx >> 16;
+        int a = (uint8_t)(fx >> 8);
 
-    fx += SK_Fixed1/2;
-    do {
+        unsigned ma = SmallDot6Scale(a, mod64);
+        if (ma) {
+            this->getBlitter()->blitV(x, y, 1, ma);
+        }
+        ma = SmallDot6Scale(255 - a, mod64);
+        if (ma) {
+            this->getBlitter()->blitV(x - 1, y, 1, ma);
+        }
+
+        return fx - SK_Fixed1/2;
+    }
+
+    virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_OVERRIDE {
+        SkASSERT(y < stopy);
+        SkASSERT(0 == dx);
+        fx += SK_Fixed1/2;
+
+        int x = fx >> 16;
+        int a = (uint8_t)(fx >> 8);
+
+        if (a) {
+            this->getBlitter()->blitV(x, y, stopy - y, a);
+        }
+        a = 255 - a;
+        if (a) {
+            this->getBlitter()->blitV(x - 1, y, stopy - y, a);
+        }
+
+        return fx - SK_Fixed1/2;
+    }
+};
+
+class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
+public:
+    virtual 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;
+
+        fx += SK_Fixed1/2;
         int x = fx >> 16;
         uint8_t  a = (uint8_t)(fx >> 8);
 
-        aa[0] = ApplyGamma(gamma, SmallDot6Scale(255 - a, mod64));
-        aa[1] = ApplyGamma(gamma, SmallDot6Scale(a, mod64));
+        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;
-        blitter->blitAntiH(x - 1, y, aa, runs);
+        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;
-    } while (++y < stopy);
 
-    return fx - SK_Fixed1/2;
-}
+        return fx - SK_Fixed1/2;
+    }
 
-typedef SkFixed (*LineProc)(int istart, int istop, SkFixed fstart,
-                            SkFixed slope, SkBlitter*, int);
+    virtual SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) SK_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);
+            fx += dx;
+        } while (++y < stopy);
+
+        return fx - SK_Fixed1/2;
+    }
+};
 
 static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
     SkASSERT((a << 16 >> 16) == a);
@@ -209,10 +323,68 @@
     return (a << 16) / b;
 }
 
+#define SkBITCOUNT(x)   (sizeof(x) << 3)
+
+#if 1
+// returns high-bit set iff x==0x8000...
+static inline int bad_int(int x) {
+    return x & -x;
+}
+
+static int any_bad_ints(int a, int b, int c, int d) {
+    return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
+}
+#else
+static inline int good_int(int x) {
+    return x ^ (1 << (SkBITCOUNT(x) - 1));
+}
+
+static int any_bad_ints(int a, int b, int c, int d) {
+    return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
+}
+#endif
+
+#ifdef SK_DEBUG
+static bool canConvertFDot6ToFixed(SkFDot6 x) {
+    const int maxDot6 = SK_MaxS32 >> (16 - 6);
+    return SkAbs32(x) <= maxDot6;
+}
+#endif
+
+/*
+ *  We want the fractional part of ordinate, but we want multiples of 64 to
+ *  return 64, not 0, so we can't just say (ordinate & 63).
+ *  We basically want to compute those bits, and if they're 0, return 64.
+ *  We can do that w/o a branch with an extra sub and add.
+ */
+static int contribution_64(SkFDot6 ordinate) {
+#if 0
+    int result = ordinate & 63;
+    if (0 == result) {
+        result = 64;
+    }
+#else
+    int result = ((ordinate - 1) & 63) + 1;
+#endif
+    SkASSERT(result > 0 && result <= 64);
+    return result;
+}
+
 static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
                              const SkIRect* clip, SkBlitter* blitter) {
-    // check that we're no larger than 511 pixels (so we can do a faster div).
-    // if we are, subdivide and call again
+    // check for integer NaN (0x80000000) which we can't handle (can't negate it)
+    // It appears typically from a huge float (inf or nan) being converted to int.
+    // If we see it, just don't draw.
+    if (any_bad_ints(x0, y0, x1, y1)) {
+        return;
+    }
+
+    // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
+    // (in dot6 format)
+    SkASSERT(canConvertFDot6ToFixed(x0));
+    SkASSERT(canConvertFDot6ToFixed(y0));
+    SkASSERT(canConvertFDot6ToFixed(x1));
+    SkASSERT(canConvertFDot6ToFixed(y1));
 
     if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
         /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
@@ -230,8 +402,13 @@
 
     int         scaleStart, scaleStop;
     int         istart, istop;
-    SkFixed     fstart, slope; 
-    LineProc    proc;
+    SkFixed     fstart, slope;
+
+    HLine_SkAntiHairBlitter     hline_blitter;
+    Horish_SkAntiHairBlitter    horish_blitter;
+    VLine_SkAntiHairBlitter     vline_blitter;
+    Vertish_SkAntiHairBlitter   vertish_blitter;
+    SkAntiHairBlitter*          hairBlitter = NULL;
 
     if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal
         if (x0 > x1) {    // we want to go left-to-right
@@ -244,16 +421,17 @@
         fstart = SkFDot6ToFixed(y0);
         if (y0 == y1) {   // completely horizontal, take fast case
             slope = 0;
-            proc = hline;
+            hairBlitter = &hline_blitter;
         } else {
             slope = fastfixdiv(y1 - y0, x1 - x0);
             SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
             fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
-            proc = horish;
+            hairBlitter = &horish_blitter;
         }
-        
+
         SkASSERT(istop > istart);
         if (istop - istart == 1) {
+            // we are within a single pixel
             scaleStart = x1 - x0;
             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
             scaleStop = 0;
@@ -270,11 +448,17 @@
                 fstart += slope * (clip->fLeft - istart);
                 istart = clip->fLeft;
                 scaleStart = 64;
+                if (istop - istart == 1) {
+                    // we are within a single pixel
+                    scaleStart = contribution_64(x1);
+                    scaleStop = 0;
+                }
             }
             if (istop > clip->fRight) {
                 istop = clip->fRight;
-                scaleStop = 64;
+                scaleStop = 0;  // so we don't draw this last column
             }
+
             SkASSERT(istart <= istop);
             if (istart == istop) {
                 return;
@@ -313,16 +497,17 @@
                 return;     // nothing to do
             }
             slope = 0;
-            proc = vline;
+            hairBlitter = &vline_blitter;
         } else {
             slope = fastfixdiv(x1 - x0, y1 - y0);
             SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
             fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
-            proc = vertish;
+            hairBlitter = &vertish_blitter;
         }
 
         SkASSERT(istop > istart);
         if (istop - istart == 1) {
+            // we are within a single pixel
             scaleStart = y1 - y0;
             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
             scaleStop = 0;
@@ -330,7 +515,7 @@
             scaleStart = 64 - (y0 & 63);
             scaleStop = y1 & 63;
         }
-        
+
         if (clip) {
             if (istart >= clip->fBottom || istop <= clip->fTop) {
                 return;
@@ -339,11 +524,17 @@
                 fstart += slope * (clip->fTop - istart);
                 istart = clip->fTop;
                 scaleStart = 64;
+                if (istop - istart == 1) {
+                    // we are within a single pixel
+                    scaleStart = contribution_64(y1);
+                    scaleStop = 0;
+                }
             }
             if (istop > clip->fBottom) {
                 istop = clip->fBottom;
-                scaleStop = 64;
+                scaleStop = 0;  // so we don't draw this last row
             }
+
             SkASSERT(istart <= istop);
             if (istart == istop)
                 return;
@@ -375,15 +566,25 @@
         rectClipper.init(blitter, *clip);
         blitter = &rectClipper;
     }
-    
-    fstart = proc(istart, istart + 1, fstart, slope, blitter, scaleStart);
+
+    SkASSERT(hairBlitter);
+    hairBlitter->setup(blitter);
+
+#ifdef SK_DEBUG
+    if (scaleStart > 0 && scaleStop > 0) {
+        // be sure we don't draw twice in the same pixel
+        SkASSERT(istart < istop - 1);
+    }
+#endif
+
+    fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
     istart += 1;
     int fullSpans = istop - istart - (scaleStop > 0);
     if (fullSpans > 0) {
-        fstart = proc(istart, istart + fullSpans, fstart, slope, blitter, 64);
+        fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
     }
     if (scaleStop > 0) {
-        proc(istop - 1, istop, fstart, slope, blitter, scaleStop);
+        hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
     }
 }
 
@@ -401,6 +602,19 @@
 
     SkPoint pts[2] = { pt0, pt1 };
 
+#ifdef SK_SCALAR_IS_FLOAT
+    // 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;
+        }
+    }
+#endif
+
     if (clip) {
         SkRect clipBounds;
         clipBounds.set(clip->getBounds());
@@ -419,7 +633,7 @@
             return;
         }
     }
-        
+
     SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
     SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
     SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
@@ -481,14 +695,14 @@
 static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
                         SkBlitter* blitter) {
     SkASSERT(L < R);
-    
+
     if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
         blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
         return;
     }
-    
+
     int left = L >> 8;
-    
+
     if (L & 0xFF) {
         blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
         left += 1;
@@ -515,12 +729,12 @@
         do_scanline(L, top, R, B - T - 1, blitter);
         return;
     }
-    
+
     if (T & 0xFF) {
         do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
         top += 1;
     }
-    
+
     int bot = B >> 8;
     int height = bot - top;
     if (height > 0) {
@@ -542,7 +756,7 @@
             }
         }
     }
-    
+
     if (B & 0xFF) {
         do_scanline(L, bot, R, B & 0xFF, blitter);
     }
@@ -580,10 +794,10 @@
         } else {
             SkRegion::Cliperator clipper(*clip, outerBounds);
             const SkIRect&       rr = clipper.rect();
-            
+
             while (!clipper.done()) {
                 SkXRect  tmpR;
-                
+
                 // this keeps our original edges fractional
                 XRect_set(&tmpR, rr);
                 if (tmpR.intersect(xr)) {
@@ -622,7 +836,7 @@
 */
 static void antifillrect(const SkRect& r, SkBlitter* blitter) {
     SkXRect xr;
-    
+
     XRect_set(&xr, r);
     antifillrect(xr, blitter);
 }
@@ -630,7 +844,7 @@
 /*  We repeat the clipping logic of AntiFillXRect because the float rect might
     overflow if we blindly converted it to an XRect. This sucks that we have to
     repeat the clipping logic, but I don't see how to share the code/logic.
- 
+
     We clip r (as needed) into one or more (smaller) float rects, and then pass
     those to our version of antifillrect, which converts it into an XRect and
     then calls the blit.
@@ -646,7 +860,7 @@
 
         SkIRect outerBounds;
         newR.roundOut(&outerBounds);
-        
+
         if (clip->isRect()) {
             antifillrect(newR, blitter);
         } else {
@@ -713,24 +927,24 @@
 static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
                            SkBlitter* blitter) {
     SkASSERT(L < R);
-    
+
     if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
         blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, R - L));
         return;
     }
-    
+
     int left = L >> 8;
     if (L & 0xFF) {
         blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
         left += 1;
     }
-    
+
     int rite = R >> 8;
     int width = rite - left;
     if (width > 0) {
         call_hline_blitter(blitter, left, top, width, alpha);
     }
-    
+
     if (R & 0xFF) {
         blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
     }
@@ -742,15 +956,19 @@
 
     int top = T >> 8;
     if (top == ((B - 1) >> 8)) {   // just one scanline high
-        inner_scanline(L, top, R, B - T, blitter);
+        // We want the inverse of B-T, since we're the inner-stroke
+        int alpha = 256 - (B - T);
+        if (alpha) {
+            inner_scanline(L, top, R, alpha, blitter);
+        }
         return;
     }
-    
+
     if (T & 0xFF) {
         inner_scanline(L, top, R, T & 0xFF, blitter);
         top += 1;
     }
-    
+
     int bot = B >> 8;
     int height = bot - top;
     if (height > 0) {
@@ -761,7 +979,7 @@
             blitter->blitV(R >> 8, top, height, ~R & 0xFF);
         }
     }
-    
+
     if (B & 0xFF) {
         inner_scanline(L, bot, R, ~B & 0xFF, blitter);
     }
@@ -794,7 +1012,7 @@
         }
         // now we can ignore clip for the rest of the function
     }
-    
+
     // stroke the outer hull
     antifilldot8(L, T, R, B, blitter, false);
 
@@ -844,4 +1062,3 @@
         AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
     }
 }
-
diff --git a/src/core/SkScan_Hairline.cpp b/src/core/SkScan_Hairline.cpp
index 412ec03..ada0078 100644
--- a/src/core/SkScan_Hairline.cpp
+++ b/src/core/SkScan_Hairline.cpp
@@ -33,6 +33,13 @@
     } while (++y < stopy);
 }
 
+#ifdef SK_DEBUG
+static bool canConvertFDot6ToFixed(SkFDot6 x) {
+    const int maxDot6 = SK_MaxS32 >> (16 - 6);
+    return SkAbs32(x) <= maxDot6;
+}
+#endif
+
 void SkScan::HairLineRgn(const SkPoint& pt0, const SkPoint& pt1,
                          const SkRegion* clip, SkBlitter* blitter) {
     SkBlitterClipper    clipper;
@@ -40,6 +47,19 @@
     SkIRect clipR, ptsR;
     SkPoint pts[2] = { pt0, pt1 };
 
+#ifdef SK_SCALAR_IS_FLOAT
+    // 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;
+        }
+    }
+#endif
+
     if (clip) {
         // Perform a clip in scalar space, so we catch huge values which might
         // be missed after we convert to SkFDot6 (overflow)
@@ -53,7 +73,12 @@
     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
@@ -148,7 +173,7 @@
 
     int width = r.width();
     int height = r.height();
-    
+
     if ((width | height) == 0) {
         return;
     }
@@ -253,7 +278,6 @@
     }
 
     SkAAClipBlitterWrapper wrap;
-    const SkIRect* clipR = NULL;
     const SkRegion* clip = NULL;
 
     {
@@ -265,7 +289,6 @@
             return;
         }
         if (!rclip.quickContains(ibounds)) {
-            clipR = &rclip.getBounds();
             if (rclip.isBW()) {
                 clip = &rclip.bwRgn();
             } else {
@@ -280,7 +303,7 @@
     SkPoint         pts[4];
     SkPath::Verb    verb;
 
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+    while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
         switch (verb) {
             case SkPath::kLine_Verb:
                 lineproc(pts[0], pts[1], clip, blitter);
@@ -392,7 +415,7 @@
         r.sort();
         r.roundOut(&ir);
         ir.inset(-1, -1);
-        
+
         SkAAClipBlitterWrapper wrap;
         if (!clip.quickContains(ir)) {
             wrap.init(clip, blitter);
diff --git a/src/core/SkScan_Path.cpp b/src/core/SkScan_Path.cpp
index 5b92ff9..66e9507 100644
--- a/src/core/SkScan_Path.cpp
+++ b/src/core/SkScan_Path.cpp
@@ -15,6 +15,11 @@
 #include "SkRasterClip.h"
 #include "SkRegion.h"
 #include "SkTemplates.h"
+#include "SkTSort.h"
+
+#ifdef SK_USE_LEGACY_AA_COVERAGE
+    #define SK_USE_STD_SORT_FOR_EDGES
+#endif
 
 #define kEDGE_HEAD_Y    SK_MinS32
 #define kEDGE_TAIL_Y    SK_MaxS32
@@ -214,11 +219,8 @@
 static void walk_convex_edges(SkEdge* prevHead, SkPath::FillType,
                               SkBlitter* blitter, int start_y, int stop_y,
                               PrePostProc proc) {
-    static int gCalls;
-    gCalls++;
-    
     validate_sort(prevHead->fNext);
-    
+
     SkEdge* leftE = prevHead->fNext;
     SkEdge* riteE = leftE->fNext;
     SkEdge* currE = riteE->fNext;
@@ -232,11 +234,8 @@
     int local_top = SkMax32(leftE->fFirstY, riteE->fFirstY);
 #endif
     SkASSERT(local_top >= start_y);
-    
-    int gLoops = 0;
-    for (;;) {
-        gLoops++;
 
+    for (;;) {
         SkASSERT(leftE->fFirstY <= stop_y);
         SkASSERT(riteE->fFirstY <= stop_y);
 
@@ -244,11 +243,11 @@
                                       leftE->fDX > riteE->fDX)) {
             SkTSwap(leftE, riteE);
         }
-        
+
         int local_bot = SkMin32(leftE->fLastY, riteE->fLastY);
         local_bot = SkMin32(local_bot, stop_y - 1);
         SkASSERT(local_top <= local_bot);
-        
+
         SkFixed left = leftE->fX;
         SkFixed dLeft = leftE->fDX;
         SkFixed rite = riteE->fX;
@@ -295,7 +294,7 @@
             riteE = currE;
             currE = currE->fNext;
         }
-        
+
         SkASSERT(leftE);
         SkASSERT(riteE);
 
@@ -374,6 +373,7 @@
 #pragma warning ( pop )
 #endif
 
+#ifdef SK_USE_STD_SORT_FOR_EDGES
 extern "C" {
     static int edge_compare(const void* a, const void* b) {
         const SkEdge* edgea = *(const SkEdge**)a;
@@ -393,9 +393,26 @@
         return (valuea < valueb) ? -1 : (valuea > valueb);
     }
 }
+#else
+static bool operator<(const SkEdge& a, const SkEdge& b) {
+    int valuea = a.fFirstY;
+    int valueb = b.fFirstY;
+
+    if (valuea == valueb) {
+        valuea = a.fX;
+        valueb = b.fX;
+    }
+
+    return valuea < valueb;
+}
+#endif
 
 static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) {
+#ifdef SK_USE_STD_SORT_FOR_EDGES
     qsort(list, count, sizeof(SkEdge*), edge_compare);
+#else
+    SkTQSort(list, list + count - 1);
+#endif
 
     // now make the edges linked in sorted order
     for (int i = 1; i < count; i++) {
@@ -424,11 +441,25 @@
 
     if (count < 2) {
         if (path.isInverseFillType()) {
-            const SkIRect& clipRect = clipRgn.getBounds();
-            blitter->blitRect(clipRect.fLeft << shiftEdgesUp,
-                              clipRect.fTop << shiftEdgesUp,
-                              clipRect.width() << shiftEdgesUp,
-                              clipRect.height() << shiftEdgesUp);
+            /*
+             *  Since we are in inverse-fill, our caller has already drawn above
+             *  our top (start_y) and will draw below our bottom (stop_y). Thus
+             *  we need to restrict our drawing to the intersection of the clip
+             *  and those two limits.
+             */
+            SkIRect rect = clipRgn.getBounds();
+            if (rect.fTop < start_y) {
+                rect.fTop = start_y;
+            }
+            if (rect.fBottom > stop_y) {
+                rect.fBottom = stop_y;
+            }
+            if (!rect.isEmpty()) {
+                blitter->blitRect(rect.fLeft << shiftEdgesUp,
+                                  rect.fTop << shiftEdgesUp,
+                                  rect.width() << shiftEdgesUp,
+                                  rect.height() << shiftEdgesUp);
+            }
         }
 
         return;
@@ -504,14 +535,19 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+/**
+ *  If the caller is drawing an inverse-fill path, then it pass true for
+ *  skipRejectTest, so we don't abort drawing just because the src bounds (ir)
+ *  is outside of the clip.
+ */
 SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip,
-                             const SkIRect& ir) {
+                             const SkIRect& ir, bool skipRejectTest) {
     fBlitter = NULL;     // null means blit nothing
     fClipRect = NULL;
 
     if (clip) {
         fClipRect = &clip->getBounds();
-        if (!SkIRect::Intersects(*fClipRect, ir)) { // completely clipped out
+        if (!skipRejectTest && !SkIRect::Intersects(*fClipRect, ir)) { // completely clipped out
             return;
         }
 
@@ -574,7 +610,7 @@
         return;
     }
 
-    SkScanClipper   clipper(blitter, clipPtr, ir);
+    SkScanClipper clipper(blitter, clipPtr, ir, path.isInverseFillType());
 
     blitter = clipper.getBlitter();
     if (blitter) {
@@ -691,4 +727,3 @@
         sk_fill_triangle(pts, clipper.getClipRect(), blitter, ir);
     }
 }
-
diff --git a/src/core/SkShader.cpp b/src/core/SkShader.cpp
index ce7ff9e..d51c2ef 100644
--- a/src/core/SkShader.cpp
+++ b/src/core/SkShader.cpp
@@ -9,97 +9,72 @@
 
 #include "SkScalar.h"
 #include "SkShader.h"
+#include "SkFlattenableBuffers.h"
 #include "SkPaint.h"
 #include "SkMallocPixelRef.h"
 
-SkShader::SkShader() : fLocalMatrix(NULL) {
-    SkDEBUGCODE(fInSession = false;)
+SK_DEFINE_INST_COUNT(SkShader)
+
+SkShader::SkShader() {
+    fLocalMatrix.reset();
+    SkDEBUGCODE(fInSetContext = false;)
 }
 
 SkShader::SkShader(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer), fLocalMatrix(NULL) {
+        : INHERITED(buffer) {
     if (buffer.readBool()) {
-        SkMatrix matrix;
-        SkReadMatrix(&buffer, &matrix);
-        setLocalMatrix(matrix);
+        buffer.readMatrix(&fLocalMatrix);
+    } else {
+        fLocalMatrix.reset();
     }
-    SkDEBUGCODE(fInSession = false;)
+
+    SkDEBUGCODE(fInSetContext = false;)
 }
 
 SkShader::~SkShader() {
-    SkASSERT(!fInSession);
-    sk_free(fLocalMatrix);
+    SkASSERT(!fInSetContext);
 }
 
-void SkShader::beginSession() {
-    SkASSERT(!fInSession);
-    SkDEBUGCODE(fInSession = true;)
-}
-
-void SkShader::endSession() {
-    SkASSERT(fInSession);
-    SkDEBUGCODE(fInSession = false;)
-}
-
-void SkShader::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkShader::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    buffer.writeBool(fLocalMatrix != NULL);
-    if (fLocalMatrix) {
-        SkWriteMatrix(&buffer, *fLocalMatrix);
-    }
-}
-
-bool SkShader::getLocalMatrix(SkMatrix* localM) const {
-    if (fLocalMatrix) {
-        if (localM) {
-            *localM = *fLocalMatrix;
-        }
-        return true;
-    } else {
-        if (localM) {
-            localM->reset();
-        }
-        return false;
-    }
-}
-
-void SkShader::setLocalMatrix(const SkMatrix& localM) {
-    if (localM.isIdentity()) {
-        this->resetLocalMatrix();
-    } else {
-        if (fLocalMatrix == NULL) {
-            fLocalMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
-        }
-        *fLocalMatrix = localM;
-    }
-}
-
-void SkShader::resetLocalMatrix() {
-    if (fLocalMatrix) {
-        sk_free(fLocalMatrix);
-        fLocalMatrix = NULL;
+    bool hasLocalM = this->hasLocalMatrix();
+    buffer.writeBool(hasLocalM);
+    if (hasLocalM) {
+        buffer.writeMatrix(fLocalMatrix);
     }
 }
 
 bool SkShader::setContext(const SkBitmap& device,
                           const SkPaint& paint,
                           const SkMatrix& matrix) {
+    SkASSERT(!this->setContextHasBeenCalled());
+
     const SkMatrix* m = &matrix;
     SkMatrix        total;
 
     fDeviceConfig = SkToU8(device.getConfig());
     fPaintAlpha = paint.getAlpha();
-    if (fLocalMatrix) {
-        total.setConcat(matrix, *fLocalMatrix);
+    if (this->hasLocalMatrix()) {
+        total.setConcat(matrix, this->getLocalMatrix());
         m = &total;
     }
     if (m->invert(&fTotalInverse)) {
         fTotalInverseClass = (uint8_t)ComputeMatrixClass(fTotalInverse);
+        SkDEBUGCODE(fInSetContext = true;)
         return true;
     }
     return false;
 }
 
+void SkShader::endContext() {
+    SkASSERT(fInSetContext);
+    SkDEBUGCODE(fInSetContext = false;)
+}
+
+SkShader::ShadeProc SkShader::asAShadeProc(void** ctx) {
+    return NULL;
+}
+
 #include "SkColorPriv.h"
 
 void SkShader::shadeSpan16(int x, int y, uint16_t span16[], int count) {
@@ -190,7 +165,7 @@
 //////////////////////////////////////////////////////////////////////////////
 
 SkShader::BitmapType SkShader::asABitmap(SkBitmap*, SkMatrix*,
-                                         TileMode*, SkScalar*) const {
+                                         TileMode*) const {
     return kNone_BitmapType;
 }
 
@@ -198,11 +173,24 @@
     return kNone_GradientType;
 }
 
+GrEffectRef* SkShader::asNewEffect(GrContext*, const SkPaint&) const {
+    return NULL;
+}
+
 SkShader* SkShader::CreateBitmapShader(const SkBitmap& src,
                                        TileMode tmx, TileMode tmy) {
     return SkShader::CreateBitmapShader(src, tmx, tmy, NULL, 0);
 }
 
+#ifdef SK_DEVELOPER
+void SkShader::toString(SkString* str) const {
+    if (this->hasLocalMatrix()) {
+        str->append(" ");
+        this->getLocalMatrix().toString(str);
+    }
+}
+#endif
+
 //////////////////////////////////////////////////////////////////////////////
 
 #include "SkColorShader.h"
@@ -231,28 +219,20 @@
 SkColorShader::SkColorShader(SkFlattenableReadBuffer& b) : INHERITED(b) {
     fFlags = 0; // computed in setContext
 
-    fInheritColor = b.readU8();
+    fInheritColor = b.readBool();
     if (fInheritColor) {
         return;
     }
-    fColor = b.readU32();
+    fColor = b.readColor();
 }
 
-void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkColorShader::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    buffer.write8(fInheritColor);
+    buffer.writeBool(fInheritColor);
     if (fInheritColor) {
         return;
     }
-    buffer.write32(fColor);
-}
-
-SkFlattenable* SkColorShader::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkColorShader, (buffer));
-}
-
-SkFlattenable::Factory SkColorShader::getFactory() {
-    return CreateProc;
+    buffer.writeColor(fColor);
 }
 
 uint32_t SkColorShader::getFlags() {
@@ -317,8 +297,7 @@
 
 // if we had a asAColor method, that would be more efficient...
 SkShader::BitmapType SkColorShader::asABitmap(SkBitmap* bitmap, SkMatrix* matrix,
-                                              TileMode modes[],
-                                      SkScalar* twoPointRadialParams) const {
+                                              TileMode modes[]) const {
     return kNone_BitmapType;
 }
 
@@ -333,12 +312,27 @@
     return kColor_GradientType;
 }
 
+#ifdef SK_DEVELOPER
+void SkColorShader::toString(SkString* str) const {
+    str->append("SkColorShader: (");
+
+    if (fInheritColor) {
+        str->append("Color: inherited from paint");
+    } else {
+        str->append("Color: ");
+        str->appendHex(fColor);
+    }
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkEmptyShader.h"
 
-SkEmptyShader::SkEmptyShader(SkFlattenableReadBuffer& b) : INHERITED(b) {}
-
 uint32_t SkEmptyShader::getFlags() { return 0; }
 uint8_t SkEmptyShader::getSpan16Alpha() const { return 0; }
 
@@ -357,9 +351,12 @@
     SkDEBUGFAIL("should never get called, since setContext() returned false");
 }
 
-SkFlattenable::Factory SkEmptyShader::getFactory() { return NULL; }
+#ifdef SK_DEVELOPER
+void SkEmptyShader::toString(SkString* str) const {
+    str->append("SkEmptyShader: (");
 
-void SkEmptyShader::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
+    this->INHERITED::toString(str);
+
+    str->append(")");
 }
-
+#endif
diff --git a/src/core/SkShape.cpp b/src/core/SkShape.cpp
deleted file mode 100644
index 339601a..0000000
--- a/src/core/SkShape.cpp
+++ /dev/null
@@ -1,77 +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 "SkCanvas.h"
-#include "SkShape.h"
-#include "SkMatrix.h"
-
-#if 0
-static int gShapeCounter;
-static void inc_shape(const SkShape* s) {
-    SkDebugf("inc %d\n", gShapeCounter);
-    gShapeCounter += 1;
-}
-static void dec_shape(const SkShape* s) {
-    --gShapeCounter;
-    SkDebugf("dec %d\n", gShapeCounter);
-}
-#else
-#define inc_shape(s)
-#define dec_shape(s)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkShape::draw(SkCanvas* canvas) {
-    int saveCount = canvas->getSaveCount();
-    this->onDraw(canvas);
-    canvas->restoreToCount(saveCount);
-}
-
-void SkShape::drawXY(SkCanvas* canvas, SkScalar dx, SkScalar dy) {
-    int saveCount = canvas->save(SkCanvas::kMatrix_SaveFlag);
-    canvas->translate(dx, dy);
-    this->onDraw(canvas);
-    canvas->restoreToCount(saveCount);
-}
-
-void SkShape::drawMatrix(SkCanvas* canvas, const SkMatrix& matrix) {
-    int saveCount = canvas->save(SkCanvas::kMatrix_SaveFlag);
-    canvas->concat(matrix);
-    this->onDraw(canvas);
-    canvas->restoreToCount(saveCount);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkShape::SkShape() {
-    inc_shape(this);
-}
-
-SkShape::~SkShape() {
-    dec_shape(this);
-}
-
-SkShape::SkShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    inc_shape(this);
-}
-
-SkFlattenable* SkShape::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkShape, (buffer));
-}
-
-SkFlattenable::Factory SkShape::getFactory() {
-    return CreateProc;
-}
-
-void SkShape::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-}
-
-void SkShape::onDraw(SkCanvas*) {}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkShape)
diff --git a/src/core/SkSpriteBlitter.h b/src/core/SkSpriteBlitter.h
index c1dbfa2..ae79afe 100644
--- a/src/core/SkSpriteBlitter.h
+++ b/src/core/SkSpriteBlitter.h
@@ -44,4 +44,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkSpriteBlitter_RGB16.cpp b/src/core/SkSpriteBlitter_RGB16.cpp
index 2c38f71..1a8404f 100644
--- a/src/core/SkSpriteBlitter_RGB16.cpp
+++ b/src/core/SkSpriteBlitter_RGB16.cpp
@@ -98,14 +98,14 @@
     } while (0)
 
 #define SkSPRITE_CLASSNAME                  Sprite_D16_S4444_Opaque
-#define SkSPRITE_ARGS                       
-#define SkSPRITE_FIELDS                     
-#define SkSPRITE_INIT                       
+#define SkSPRITE_ARGS
+#define SkSPRITE_FIELDS
+#define SkSPRITE_INIT
 #define SkSPRITE_DST_TYPE                   uint16_t
 #define SkSPRITE_SRC_TYPE                   SkPMColor16
 #define SkSPRITE_DST_GETADDR                getAddr16
 #define SkSPRITE_SRC_GETADDR                getAddr16
-#define SkSPRITE_PREAMBLE(srcBM, x, y)      
+#define SkSPRITE_PREAMBLE(srcBM, x, y)
 #define SkSPRITE_BLIT_PIXEL(dst, src)       D16_S4444_Opaque(dst, src)
 #define SkSPRITE_NEXT_ROW
 #define SkSPRITE_POSTAMBLE(srcBM)
@@ -260,15 +260,15 @@
 public:
     Sprite_D16_S32_BlitRowProc(const SkBitmap& source)
         : SkSpriteBlitter(source) {}
-    
+
     // overrides
-    
+
     virtual void setup(const SkBitmap& device, int left, int top,
                        const SkPaint& paint) {
         this->INHERITED::setup(device, left, top, paint);
-        
+
         unsigned flags = 0;
-        
+
         if (paint.getAlpha() < 0xFF) {
             flags |= SkBlitRow::kGlobalAlpha_Flag;
         }
@@ -280,7 +280,7 @@
         }
         fProc = SkBlitRow::Factory(flags, SkBitmap::kRGB_565_Config);
     }
-    
+
     virtual void blitRect(int x, int y, int width, int height) {
         uint16_t* SK_RESTRICT dst = fDevice->getAddr16(x, y);
         const SkPMColor* SK_RESTRICT src = fSource->getAddr32(x - fLeft,
@@ -289,7 +289,7 @@
         unsigned srcRB = fSource->rowBytes();
         SkBlitRow::Proc proc = fProc;
         U8CPU alpha = fPaint->getAlpha();
-        
+
         while (--height >= 0) {
             proc(dst, src, width, alpha, x, y);
             y += 1;
@@ -297,10 +297,10 @@
             src = (const SkPMColor* SK_RESTRICT)((const char*)src + srcRB);
         }
     }
-    
+
 private:
     SkBlitRow::Proc fProc;
-    
+
     typedef SkSpriteBlitter INHERITED;
 };
 
diff --git a/src/core/SkStream.cpp b/src/core/SkStream.cpp
index 8b71244..fb343ea 100644
--- a/src/core/SkStream.cpp
+++ b/src/core/SkStream.cpp
@@ -13,7 +13,18 @@
 #include "SkString.h"
 #include "SkOSFile.h"
 
-SkStream::~SkStream() {}
+SK_DEFINE_INST_COUNT(SkStream)
+SK_DEFINE_INST_COUNT(SkWStream)
+SK_DEFINE_INST_COUNT(SkFILEStream)
+SK_DEFINE_INST_COUNT(SkFDStream)
+SK_DEFINE_INST_COUNT(SkMemoryStream)
+SK_DEFINE_INST_COUNT(SkBufferStream)
+SK_DEFINE_INST_COUNT(SkFILEWStream)
+SK_DEFINE_INST_COUNT(SkMemoryWStream)
+SK_DEFINE_INST_COUNT(SkDynamicMemoryWStream)
+SK_DEFINE_INST_COUNT(SkDebugWStream)
+
+///////////////////////////////////////////////////////////////////////////////
 
 const char* SkStream::getFileName()
 {
@@ -69,7 +80,7 @@
 #define SK_BYTE_SENTINEL_FOR_U32    0xFF
 
 size_t SkStream::readPackedUInt() {
-    uint8_t byte;    
+    uint8_t byte;
     if (!this->read(&byte, 1)) {
         return 0;
     }
@@ -82,6 +93,17 @@
     }
 }
 
+SkData* SkStream::readData() {
+    size_t size = this->readU32();
+    if (0 == size) {
+        return SkData::NewEmpty();
+    } else {
+        void* buffer = sk_malloc_throw(size);
+        this->read(buffer, size);
+        return SkData::NewFromMalloc(buffer, size);
+    }
+}
+
 //////////////////////////////////////////////////////////////////////////////////////
 
 SkWStream::~SkWStream()
@@ -172,7 +194,7 @@
 bool SkWStream::writeStream(SkStream* stream, size_t length) {
     char scratch[1024];
     const size_t MAX = sizeof(scratch);
-    
+
     while (length != 0) {
         size_t n = length;
         if (n > MAX) {
@@ -189,7 +211,10 @@
 
 bool SkWStream::writeData(const SkData* data) {
     if (data) {
+        this->write32(data->size());
         this->write(data->data(), data->size());
+    } else {
+        this->write32(0);
     }
     return true;
 }
@@ -274,6 +299,16 @@
     fOffset = 0;
 }
 
+SkMemoryStream::SkMemoryStream(SkData* data) {
+    if (NULL == data) {
+        fData = SkData::NewEmpty();
+    } else {
+        fData = data;
+        fData->ref();
+    }
+    fOffset = 0;
+}
+
 SkMemoryStream::~SkMemoryStream() {
     fData->unref();
 }
@@ -296,7 +331,13 @@
 }
 
 SkData* SkMemoryStream::setData(SkData* data) {
-    SkRefCnt_SafeAssign(fData, data);
+    fData->unref();
+    if (NULL == data) {
+        fData = SkData::NewEmpty();
+    } else {
+        fData = data;
+        fData->ref();
+    }
     return data;
 }
 
@@ -565,14 +606,14 @@
     char*   start() { return (char*)(this + 1); }
     size_t  avail() const { return fStop - fCurr; }
     size_t  written() const { return fCurr - this->start(); }
-    
+
     void init(size_t size)
     {
         fNext = NULL;
         fCurr = this->start();
         fStop = this->start() + size;
     }
-    
+
     const void* append(const void* data, size_t size)
     {
         SkASSERT((size_t)(fStop - fCurr) >= size);
@@ -595,9 +636,9 @@
 void SkDynamicMemoryWStream::reset()
 {
     this->invalidateCopy();
-    
+
     Block*  block = fHead;
-    
+
     while (block != NULL) {
         Block*  next = block->fNext;
         sk_free(block);
@@ -613,23 +654,23 @@
         this->invalidateCopy();
 
         fBytesWritten += count;
-        
+
         size_t  size;
-        
+
         if (fTail != NULL && fTail->avail() > 0) {
             size = SkMin32(fTail->avail(), count);
             buffer = fTail->append(buffer, size);
             SkASSERT(count >= size);
-            count -= size;        
+            count -= size;
             if (count == 0)
                 return true;
         }
-            
+
         size = SkMax32(count, SkDynamicMemoryWStream_MinBlockSize);
         Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
         block->init(size);
         block->append(buffer, count);
-        
+
         if (fTail != NULL)
             fTail->fNext = block;
         else
@@ -646,7 +687,7 @@
     }
 
     this->invalidateCopy();
-    
+
     Block* block = fHead;
     while (block != NULL) {
         size_t size = block->written();
@@ -691,7 +732,7 @@
         memcpy(dst, fCopy->data(), fBytesWritten);
     } else {
         Block* block = fHead;
-        
+
         while (block != NULL) {
             size_t size = block->written();
             memcpy(dst, block->start(), size);
@@ -732,14 +773,14 @@
 
 void SkDebugWStream::newline()
 {
-#ifdef SK_DEBUG
+#if defined(SK_DEBUG) || defined(SK_DEVELOPER)
     SkDebugf("\n");
 #endif
 }
 
 bool SkDebugWStream::write(const void* buffer, size_t size)
 {
-#ifdef SK_DEBUG
+#if defined(SK_DEBUG) || defined(SK_DEVELOPER)
     char* s = new char[size+1];
     memcpy(s, buffer, size);
     s[size] = 0;
diff --git a/src/core/SkString.cpp b/src/core/SkString.cpp
index 4658ff8..5d1b8ce 100644
--- a/src/core/SkString.cpp
+++ b/src/core/SkString.cpp
@@ -15,7 +15,7 @@
 #include <stdio.h>
 
 // number of bytes (on the stack) to receive the printf result
-static const size_t kBufferSize = 256;
+static const size_t kBufferSize = 512;
 
 #ifdef SK_BUILD_FOR_WIN
     #define VSNPRINTF(buffer, size, format, args) \
@@ -36,19 +36,23 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkStrStartsWith(const char string[], const char prefix[]) {
+bool SkStrEndsWith(const char string[], const char suffixStr[]) {
     SkASSERT(string);
-    SkASSERT(prefix);
-    return !strncmp(string, prefix, strlen(prefix));
+    SkASSERT(suffixStr);
+    size_t  strLen = strlen(string);
+    size_t  suffixLen = strlen(suffixStr);
+    return  strLen >= suffixLen &&
+            !strncmp(string + strLen - suffixLen, suffixStr, suffixLen);
 }
 
-bool SkStrEndsWith(const char string[], const char suffix[]) {
+bool SkStrEndsWith(const char string[], const char suffixChar) {
     SkASSERT(string);
-    SkASSERT(suffix);
     size_t  strLen = strlen(string);
-    size_t  suffixLen = strlen(suffix);
-    return  strLen >= suffixLen &&
-            !strncmp(string + strLen - suffixLen, suffix, suffixLen);
+    if (0 == strLen) {
+        return false;
+    } else {
+        return (suffixChar == string[strLen-1]);
+    }
 }
 
 int SkStrStartsWithOneOf(const char string[], const char prefixes[]) {
@@ -107,7 +111,7 @@
     }
 
     do {
-        *--p = SkToU8('0' + dec % 10);
+        *--p = SkToU8('0' + (int32_t) (dec % 10));
         dec /= 10;
         minDigits--;
     } while (dec != 0);
@@ -129,7 +133,6 @@
     return string;
 }
 
-#ifdef SK_CAN_USE_FLOAT
 char* SkStrAppendFloat(char string[], float value) {
     // since floats have at most 8 significant digits, we limit our %g to that.
     static const char gFormat[] = "%.8g";
@@ -140,7 +143,6 @@
     SkASSERT(len <= SkStrAppendScalar_MaxSize);
     return string + len;
 }
-#endif
 
 char* SkStrAppendFixed(char string[], SkFixed x) {
     SkDEBUGCODE(char* start = string;)
@@ -610,4 +612,3 @@
 
 #undef VSNPRINTF
 #undef SNPRINTF
-
diff --git a/src/core/SkStringUtils.cpp b/src/core/SkStringUtils.cpp
new file mode 100644
index 0000000..0f93b8e
--- /dev/null
+++ b/src/core/SkStringUtils.cpp
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkString.h"
+#include "SkStringUtils.h"
+
+void SkAddFlagToString(SkString* string, bool flag, const char* flagStr, bool* needSeparator) {
+    if (flag) {
+        if (*needSeparator) {
+            string->append("|");
+        }
+        string->append(flagStr);
+        *needSeparator = true;
+    }
+}
diff --git a/src/core/SkStroke.cpp b/src/core/SkStroke.cpp
index 4b486b1..cc98d74 100644
--- a/src/core/SkStroke.cpp
+++ b/src/core/SkStroke.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 The Android Open Source Project
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkStrokerPriv.h"
 #include "SkGeometry.h"
 #include "SkPath.h"
@@ -63,7 +61,8 @@
 
 class SkPathStroker {
 public:
-    SkPathStroker(SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
+    SkPathStroker(const SkPath& src,
+                  SkScalar radius, SkScalar miterLimit, SkPaint::Cap cap,
                   SkPaint::Join join);
 
     void moveTo(const SkPoint&);
@@ -171,13 +170,16 @@
             fOuter.close();
         }
     }
-    fInner.reset();
+    // since we may re-use fInner, we rewind instead of reset, to save on
+    // reallocating its internal storage.
+    fInner.rewind();
     fSegmentCount = -1;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkPathStroker::SkPathStroker(SkScalar radius, SkScalar miterLimit,
+SkPathStroker::SkPathStroker(const SkPath& src,
+                             SkScalar radius, SkScalar miterLimit,
                              SkPaint::Cap cap, SkPaint::Join join)
         : fRadius(radius) {
 
@@ -197,6 +199,15 @@
     fJoiner = SkStrokerPriv::JoinFactory(join);
     fSegmentCount = -1;
     fPrevIsLine = false;
+
+    // Need some estimate of how large our final result (fOuter)
+    // and our per-contour temp (fInner) will be, so we don't spend
+    // extra time repeatedly growing these arrays.
+    //
+    // 3x for result == inner + outer + join (swag)
+    // 1x for inner == 'wag' (worst contour length would be better guess)
+    fOuter.incReserve(src.countPoints() * 3);
+    fInner.incReserve(src.countPoints());
 }
 
 void SkPathStroker::moveTo(const SkPoint& pt) {
@@ -244,9 +255,19 @@
         this->quad_to(&tmp[0], normalAB, unitNormalAB, &norm, &unit, subDivide);
         this->quad_to(&tmp[2], norm, unit, normalBC, unitNormalBC, subDivide);
     } else {
-        SkVector    normalB, unitB;
+        SkVector    normalB;
+
+#ifdef SK_IGNORE_QUAD_STROKE_FIX
+        SkVector unitB;
         SkAssertResult(set_normal_unitnormal(pts[0], pts[2], fRadius,
                                              &normalB, &unitB));
+#else
+        normalB = pts[2] - pts[0];
+        normalB.rotateCCW();
+        SkScalar dot = SkPoint::DotProduct(unitNormalAB, *unitNormalBC);
+        SkAssertResult(normalB.setLength(SkScalarDiv(fRadius,
+                                     SkScalarSqrt((SK_Scalar1 + dot)/2))));
+#endif
 
         fOuter.quadTo(  pts[1].fX + normalB.fX, pts[1].fY + normalB.fY,
                         pts[2].fX + normalBC->fX, pts[2].fY + normalBC->fY);
@@ -306,7 +327,7 @@
         this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
     } else {
         SkVector    normalB, normalC;
-        
+
         // need normals to inset/outset the off-curve pts B and C
 
         if (0) {    // this is normal to the line between our adjacent pts
@@ -449,6 +470,15 @@
 
         }
 
+#if 0
+        /*
+         *  Why was this code here? It caused us to draw circles where we didn't
+         *  want them. See http://code.google.com/p/chromium/issues/detail?id=112145
+         *  and gm/dashcubics.cpp
+         *
+         *  Simply removing this code seemed to fix the problem (no more circles).
+         *  Wish I had a repro case earlier when I added this check/hack...
+         */
         // check for too pinchy
         for (i = 1; i < count; i++) {
             SkPoint p;
@@ -463,7 +493,7 @@
                 fExtra.addCircle(p.fX, p.fY, fRadius, SkPath::kCW_Direction);
             }
         }
-
+#endif
     }
 
     this->postJoinTo(pt3, normalCD, unitCD);
@@ -550,16 +580,58 @@
     #define APPLY_PROC(proc, pts, count)
 #endif
 
+// If src==dst, then we use a tmp path to record the stroke, and then swap
+// its contents with src when we're done.
+class AutoTmpPath {
+public:
+    AutoTmpPath(const SkPath& src, SkPath** dst) : fSrc(src) {
+        if (&src == *dst) {
+            *dst = &fTmpDst;
+            fSwapWithSrc = true;
+        } else {
+            (*dst)->reset();
+            fSwapWithSrc = false;
+        }
+    }
+
+    ~AutoTmpPath() {
+        if (fSwapWithSrc) {
+            fTmpDst.swap(*const_cast<SkPath*>(&fSrc));
+        }
+    }
+
+private:
+    SkPath          fTmpDst;
+    const SkPath&   fSrc;
+    bool            fSwapWithSrc;
+};
+
 void SkStroke::strokePath(const SkPath& src, SkPath* dst) const {
     SkASSERT(&src != NULL && dst != NULL);
 
     SkScalar radius = SkScalarHalf(fWidth);
 
-    dst->reset();
+    AutoTmpPath tmp(src, &dst);
+
     if (radius <= 0) {
         return;
     }
-    
+
+    // If src is really a rect, call our specialty strokeRect() method
+    {
+        bool isClosed;
+        SkPath::Direction dir;
+        if (src.isRect(&isClosed, &dir) && isClosed) {
+            this->strokeRect(src.getBounds(), dst, dir);
+            // our answer should preserve the inverseness of the src
+            if (src.isInverseFillType()) {
+                SkASSERT(!dst->isInverseFillType());
+                dst->toggleInverseFillType();
+            }
+            return;
+        }
+    }
+
 #ifdef SK_SCALAR_IS_FIXED
     void (*proc)(SkPoint pts[], int count) = identity_proc;
     if (needs_to_shrink(src)) {
@@ -571,14 +643,14 @@
     }
 #endif
 
-    SkPathStroker   stroker(radius, fMiterLimit, this->getCap(),
+    SkPathStroker   stroker(src, radius, fMiterLimit, this->getCap(),
                             this->getJoin());
 
     SkPath::Iter    iter(src, false);
     SkPoint         pts[4];
     SkPath::Verb    verb, lastSegment = SkPath::kMove_Verb;
 
-    while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
+    while ((verb = iter.next(pts, false)) != SkPath::kDone_Verb) {
         switch (verb) {
             case SkPath::kMove_Verb:
                 APPLY_PROC(proc, &pts[0], 1);
@@ -637,7 +709,7 @@
             paint.setStrokeWidth(7);
             paint.setStrokeCap(SkPaint::kRound_Cap);
             canvas->drawLine(pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, paint);
-        }        
+        }
 #endif
 #if 0
         if (2 == src.countPoints()) {
@@ -653,12 +725,82 @@
     }
 }
 
-void SkStroke::strokeLine(const SkPoint& p0, const SkPoint& p1,
-                          SkPath* dst) const {
-    SkPath  tmp;
-
-    tmp.moveTo(p0);
-    tmp.lineTo(p1);
-    this->strokePath(tmp, dst);
+static SkPath::Direction reverse_direction(SkPath::Direction dir) {
+    SkASSERT(SkPath::kUnknown_Direction != dir);
+    return SkPath::kCW_Direction == dir ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
 }
 
+static void addBevel(SkPath* path, const SkRect& r, const SkRect& outer, SkPath::Direction dir) {
+    SkPoint pts[8];
+
+    if (SkPath::kCW_Direction == dir) {
+        pts[0].set(r.fLeft, outer.fTop);
+        pts[1].set(r.fRight, outer.fTop);
+        pts[2].set(outer.fRight, r.fTop);
+        pts[3].set(outer.fRight, r.fBottom);
+        pts[4].set(r.fRight, outer.fBottom);
+        pts[5].set(r.fLeft, outer.fBottom);
+        pts[6].set(outer.fLeft, r.fBottom);
+        pts[7].set(outer.fLeft, r.fTop);
+    } else {
+        pts[7].set(r.fLeft, outer.fTop);
+        pts[6].set(r.fRight, outer.fTop);
+        pts[5].set(outer.fRight, r.fTop);
+        pts[4].set(outer.fRight, r.fBottom);
+        pts[3].set(r.fRight, outer.fBottom);
+        pts[2].set(r.fLeft, outer.fBottom);
+        pts[1].set(outer.fLeft, r.fBottom);
+        pts[0].set(outer.fLeft, r.fTop);
+    }
+    path->addPoly(pts, 8, true);
+}
+
+void SkStroke::strokeRect(const SkRect& origRect, SkPath* dst,
+                          SkPath::Direction dir) const {
+    SkASSERT(dst != NULL);
+    dst->reset();
+
+    SkScalar radius = SkScalarHalf(fWidth);
+    if (radius <= 0) {
+        return;
+    }
+
+    SkScalar rw = origRect.width();
+    SkScalar rh = origRect.height();
+    if ((rw < 0) ^ (rh < 0)) {
+        dir = reverse_direction(dir);
+    }
+    SkRect rect(origRect);
+    rect.sort();
+    // reassign these, now that we know they'll be >= 0
+    rw = rect.width();
+    rh = rect.height();
+
+    SkRect r(rect);
+    r.outset(radius, radius);
+
+    SkPaint::Join join = (SkPaint::Join)fJoin;
+    if (SkPaint::kMiter_Join == join && fMiterLimit < SK_ScalarSqrt2) {
+        join = SkPaint::kBevel_Join;
+    }
+
+    switch (join) {
+        case SkPaint::kMiter_Join:
+            dst->addRect(r, dir);
+            break;
+        case SkPaint::kBevel_Join:
+            addBevel(dst, rect, r, dir);
+            break;
+        case SkPaint::kRound_Join:
+            dst->addRoundRect(r, radius, radius, dir);
+            break;
+        default:
+            break;
+    }
+
+    if (fWidth < SkMinScalar(rw, rh) && !fDoFill) {
+        r = rect;
+        r.inset(radius, radius);
+        dst->addRect(r, reverse_direction(dir));
+    }
+}
diff --git a/src/core/SkStroke.h b/src/core/SkStroke.h
new file mode 100644
index 0000000..a6a9f08
--- /dev/null
+++ b/src/core/SkStroke.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkStroke_DEFINED
+#define SkStroke_DEFINED
+
+#include "SkPath.h"
+#include "SkPoint.h"
+#include "SkPaint.h"
+
+/** \class SkStroke
+    SkStroke is the utility class that constructs paths by stroking
+    geometries (lines, rects, ovals, roundrects, paths). This is
+    invoked when a geometry or text is drawn in a canvas with the
+    kStroke_Mask bit set in the paint.
+*/
+class SkStroke {
+public:
+    SkStroke();
+    SkStroke(const SkPaint&);
+    SkStroke(const SkPaint&, SkScalar width);   // width overrides paint.getStrokeWidth()
+
+    SkPaint::Cap    getCap() const { return (SkPaint::Cap)fCap; }
+    void        setCap(SkPaint::Cap);
+
+    SkPaint::Join   getJoin() const { return (SkPaint::Join)fJoin; }
+    void        setJoin(SkPaint::Join);
+
+    void    setMiterLimit(SkScalar);
+    void    setWidth(SkScalar);
+
+    bool    getDoFill() const { return SkToBool(fDoFill); }
+    void    setDoFill(bool doFill) { fDoFill = SkToU8(doFill); }
+
+    /**
+     *  Stroke the specified rect, winding it in the specified direction..
+     */
+    void    strokeRect(const SkRect& rect, SkPath* result,
+                       SkPath::Direction = SkPath::kCW_Direction) const;
+    void    strokePath(const SkPath& path, SkPath*) const;
+
+    ////////////////////////////////////////////////////////////////
+
+private:
+    SkScalar    fWidth, fMiterLimit;
+    uint8_t     fCap, fJoin;
+    SkBool8     fDoFill;
+
+    friend class SkPaint;
+};
+
+#endif
diff --git a/src/core/SkStrokeRec.cpp b/src/core/SkStrokeRec.cpp
new file mode 100644
index 0000000..cdaf241
--- /dev/null
+++ b/src/core/SkStrokeRec.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "SkStrokeRec.h"
+#include "SkPaintDefaults.h"
+
+// must be < 0, since ==0 means hairline, and >0 means normal stroke
+#define kStrokeRec_FillStyleWidth     (-SK_Scalar1)
+
+SkStrokeRec::SkStrokeRec(InitStyle s) {
+    fWidth          = (kFill_InitStyle == s) ? kStrokeRec_FillStyleWidth : 0;
+    fMiterLimit     = SkPaintDefaults_MiterLimit;
+    fCap            = SkPaint::kDefault_Cap;
+    fJoin           = SkPaint::kDefault_Join;
+    fStrokeAndFill  = false;
+}
+
+SkStrokeRec::SkStrokeRec(const SkStrokeRec& src) {
+    memcpy(this, &src, sizeof(src));
+}
+
+SkStrokeRec::SkStrokeRec(const SkPaint& paint) {
+    switch (paint.getStyle()) {
+        case SkPaint::kFill_Style:
+            fWidth = kStrokeRec_FillStyleWidth;
+            fStrokeAndFill = false;
+            break;
+        case SkPaint::kStroke_Style:
+            fWidth = paint.getStrokeWidth();
+            fStrokeAndFill = false;
+            break;
+        case SkPaint::kStrokeAndFill_Style:
+            if (0 == paint.getStrokeWidth()) {
+                // hairline+fill == fill
+                fWidth = kStrokeRec_FillStyleWidth;
+                fStrokeAndFill = false;
+            } else {
+                fWidth = paint.getStrokeWidth();
+                fStrokeAndFill = true;
+            }
+            break;
+        default:
+            SkASSERT(!"unknown paint style");
+            // fall back on just fill
+            fWidth = kStrokeRec_FillStyleWidth;
+            fStrokeAndFill = false;
+            break;
+    }
+
+    // copy these from the paint, regardless of our "style"
+    fMiterLimit = paint.getStrokeMiter();
+    fCap        = paint.getStrokeCap();
+    fJoin       = paint.getStrokeJoin();
+}
+
+SkStrokeRec::Style SkStrokeRec::getStyle() const {
+    if (fWidth < 0) {
+        return kFill_Style;
+    } else if (0 == fWidth) {
+        return kHairline_Style;
+    } else {
+        return fStrokeAndFill ? kStrokeAndFill_Style : kStroke_Style;
+    }
+}
+
+void SkStrokeRec::setFillStyle() {
+    fWidth = kStrokeRec_FillStyleWidth;
+    fStrokeAndFill = false;
+}
+
+void SkStrokeRec::setHairlineStyle() {
+    fWidth = 0;
+    fStrokeAndFill = false;
+}
+
+void SkStrokeRec::setStrokeStyle(SkScalar width, bool strokeAndFill) {
+    if (strokeAndFill && (0 == width)) {
+        // hairline+fill == fill
+        this->setFillStyle();
+    } else {
+        fWidth = width;
+        fStrokeAndFill = strokeAndFill;
+    }
+}
+
+#include "SkStroke.h"
+
+bool SkStrokeRec::applyToPath(SkPath* dst, const SkPath& src) const {
+    if (fWidth <= 0) {  // hairline or fill
+        return false;
+    }
+
+    SkStroke stroker;
+    stroker.setCap(fCap);
+    stroker.setJoin(fJoin);
+    stroker.setMiterLimit(fMiterLimit);
+    stroker.setWidth(fWidth);
+    stroker.setDoFill(fStrokeAndFill);
+    stroker.strokePath(src, dst);
+    return true;
+}
diff --git a/src/core/SkStrokerPriv.cpp b/src/core/SkStrokerPriv.cpp
index 85aae02..269ebd3 100644
--- a/src/core/SkStrokerPriv.cpp
+++ b/src/core/SkStrokerPriv.cpp
@@ -180,7 +180,7 @@
         currIsLine = false;
         goto DO_BLUNT;
     }
-    
+
     ccw = !is_clockwise(before, after);
     if (ccw)
     {
@@ -188,7 +188,7 @@
         before.negate();
         after.negate();
     }
-    
+
     /*  Before we enter the world of square-roots and divides,
         check if we're trying to join an upright right angle
         (common case for stroking rectangles). If so, special case
@@ -262,6 +262,3 @@
     SkASSERT((unsigned)join < SkPaint::kJoinCount);
     return gJoiners[join];
 }
-
-
-
diff --git a/src/core/SkStrokerPriv.h b/src/core/SkStrokerPriv.h
index e67d677..1c35f0a 100644
--- a/src/core/SkStrokerPriv.h
+++ b/src/core/SkStrokerPriv.h
@@ -39,4 +39,3 @@
 };
 
 #endif
-
diff --git a/src/core/SkTLList.h b/src/core/SkTLList.h
new file mode 100644
index 0000000..298ce51
--- /dev/null
+++ b/src/core/SkTLList.h
@@ -0,0 +1,385 @@
+/*
+ * 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 SkTLList_DEFINED
+#define SkTLList_DEFINED
+
+#include "SkTInternalLList.h"
+#include "SkTemplates.h"
+
+template <typename T> class SkTLList;
+template <typename T>
+inline void* operator new(size_t, SkTLList<T>* list,
+                          typename SkTLList<T>::Placement placement,
+                          const typename SkTLList<T>::Iter& location);
+
+/** Doubly-linked list of objects. The objects' lifetimes are controlled by the list. I.e. the
+    the list creates the objects and they are deleted upon removal. This class block-allocates
+    space for entries based on a param passed to the constructor.
+
+    Elements of the list can be constructed in place using the following macros:
+        SkNEW_INSERT_IN_LLIST_BEFORE(list, location, type_name, args)
+        SkNEW_INSERT_IN_LLIST_AFTER(list, location, type_name, args)
+    where list is a SkTLList<type_name>*, location is an iterator, and args is the paren-surrounded
+    constructor arguments for type_name. These macros behave like addBefore() and addAfter().
+*/
+template <typename T>
+class SkTLList : public SkNoncopyable {
+private:
+    struct Block;
+    struct Node {
+        char fObj[sizeof(T)];
+        SK_DECLARE_INTERNAL_LLIST_INTERFACE(Node);
+        Block* fBlock; // owning block.
+    };
+    typedef SkTInternalLList<Node> NodeList;
+
+public:
+
+    class Iter;
+
+    /** allocCnt is the number of objects to allocate as a group. In the worst case fragmentation
+        each object is using the space required for allocCnt unfragmented objects. */
+    SkTLList(int allocCnt = 1) : fCount(0), fAllocCnt(allocCnt) {
+        SkASSERT(allocCnt > 0);
+        this->validate();
+    }
+
+    ~SkTLList() {
+        this->validate();
+        typename NodeList::Iter iter;
+        Node* node = iter.init(fList, Iter::kHead_IterStart);
+        while (NULL != node) {
+            SkTCast<T*>(node->fObj)->~T();
+            Block* block = node->fBlock;
+            node = iter.next();
+            if (0 == --block->fNodesInUse) {
+                for (int i = 0; i < fAllocCnt; ++i) {
+                    block->fNodes[i].~Node();
+                }
+                sk_free(block);
+            }
+        }
+    }
+
+    T* addToHead(const T& t) {
+        this->validate();
+        Node* node = this->createNode();
+        fList.addToHead(node);
+        SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
+        this->validate();
+        return reinterpret_cast<T*>(node->fObj);
+    }
+
+    T* addToTail(const T& t) {
+        this->validate();
+        Node* node = this->createNode();
+        fList.addToTail(node);
+        SkNEW_PLACEMENT_ARGS(node->fObj, T, (t));
+        this->validate();
+        return reinterpret_cast<T*>(node->fObj);
+    }
+
+    /** Adds a new element to the list before the location indicated by the iterator. If the
+        iterator refers to a NULL location then the new element is added at the tail */
+    T* addBefore(const T& t, const Iter& location) {
+        return SkNEW_PLACEMENT_ARGS(this->internalAddBefore(location), T, (t));
+    }
+
+    /** Adds a new element to the list after the location indicated by the iterator. If the
+        iterator refers to a NULL location then the new element is added at the head */
+    T* addAfter(const T& t, const Iter& location) {
+        return SkNEW_PLACEMENT_ARGS(this->internalAddAfter(location), T, (t));
+    }
+
+    /** Convenience methods for getting an iterator initialized to the head/tail of the list. */
+    Iter headIter() const { return Iter(*this, Iter::kHead_IterStart); }
+    Iter tailIter() const { return Iter(*this, Iter::kTail_IterStart); }
+
+    T* head() { return Iter(*this, Iter::kHead_IterStart).get(); }
+    T* tail() { return Iter(*this, Iter::kTail_IterStart).get(); }
+    const T* head() const { return Iter(*this, Iter::kHead_IterStart).get(); }
+    const T* tail() const { return Iter(*this, Iter::kTail_IterStart).get(); }
+
+    void popHead() {
+        this->validate();
+        Node* node = fList.head();
+        if (NULL != node) {
+            this->removeNode(node);
+        }
+        this->validate();
+    }
+
+    void popTail() {
+        this->validate();
+        Node* node = fList.head();
+        if (NULL != node) {
+            this->removeNode(node);
+        }
+        this->validate();
+    }
+
+    void remove(T* t) {
+        this->validate();
+        Node* node = reinterpret_cast<Node*>(t);
+        SkASSERT(reinterpret_cast<T*>(node->fObj) == t);
+        this->removeNode(node);
+        this->validate();
+    }
+
+    void reset() {
+        this->validate();
+        Iter iter(*this, Iter::kHead_IterStart);
+        while (iter.get()) {
+            Iter next = iter;
+            next.next();
+            this->remove(iter.get());
+            iter = next;
+        }
+        SkASSERT(0 == fCount);
+        this->validate();
+    }
+
+    int count() const { return fCount; }
+    bool isEmpty() const { this->validate(); return 0 == fCount; }
+
+    bool operator== (const SkTLList& list) const {
+        if (this == &list) {
+            return true;
+        }
+        if (fCount != list.fCount) {
+            return false;
+        }
+        for (Iter a(*this, Iter::kHead_IterStart), b(list, Iter::kHead_IterStart);
+             a.get();
+             a.next(), b.next()) {
+            SkASSERT(NULL != b.get()); // already checked that counts match.
+            if (!(*a.get() == *b.get())) {
+                return false;
+            }
+        }
+        return true;
+    }
+    bool operator!= (const SkTLList& list) const { return !(*this == list); }
+
+    /** The iterator becomes invalid if the element it refers to is removed from the list. */
+    class Iter : private NodeList::Iter {
+    private:
+        typedef typename NodeList::Iter INHERITED;
+
+    public:
+        typedef typename INHERITED::IterStart IterStart;
+        //!< Start the iterator at the head of the list.
+        static const IterStart kHead_IterStart = INHERITED::kHead_IterStart;
+        //!< Start the iterator at the tail of the list.
+        static const IterStart kTail_IterStart = INHERITED::kTail_IterStart;
+
+        Iter() {}
+
+        Iter(const SkTLList& list, IterStart start) {
+            INHERITED::init(list.fList, start);
+        }
+
+        T* init(const SkTLList& list, IterStart start) {
+            return this->nodeToObj(INHERITED::init(list.fList, start));
+        }
+
+        T* get() { return this->nodeToObj(INHERITED::get()); }
+
+        T* next() { return this->nodeToObj(INHERITED::next()); }
+
+        T* prev() { return this->nodeToObj(INHERITED::prev()); }
+
+        Iter& operator= (const Iter& iter) { INHERITED::operator=(iter); return *this; }
+
+    private:
+        friend class SkTLList;
+        Node* getNode() { return INHERITED::get(); }
+
+        T* nodeToObj(Node* node) {
+            if (NULL != node) {
+                return reinterpret_cast<T*>(node->fObj);
+            } else {
+                return NULL;
+            }
+        }
+    };
+
+    // For use with operator new
+    enum Placement {
+        kBefore_Placement,
+        kAfter_Placement,
+    };
+
+private:
+    struct Block {
+        int fNodesInUse;
+        Node fNodes[1];
+    };
+
+    size_t blockSize() const { return sizeof(Block) + sizeof(Node) * (fAllocCnt-1); }
+
+    Node* createNode() {
+        Node* node = fFreeList.head();
+        if (NULL != node) {
+            fFreeList.remove(node);
+            ++node->fBlock->fNodesInUse;
+        } else {
+            Block* block = reinterpret_cast<Block*>(sk_malloc_flags(this->blockSize(), 0));
+            node = &block->fNodes[0];
+            SkNEW_PLACEMENT(node, Node);
+            node->fBlock = block;
+            block->fNodesInUse = 1;
+            for (int i = 1; i < fAllocCnt; ++i) {
+                SkNEW_PLACEMENT(block->fNodes + i, Node);
+                fFreeList.addToHead(block->fNodes + i);
+                block->fNodes[i].fBlock = block;
+            }
+        }
+        ++fCount;
+        return node;
+    }
+
+    void removeNode(Node* node) {
+        SkASSERT(NULL != node);
+        fList.remove(node);
+        SkTCast<T*>(node->fObj)->~T();
+        if (0 == --node->fBlock->fNodesInUse) {
+            Block* block = node->fBlock;
+            for (int i = 0; i < fAllocCnt; ++i) {
+                if (block->fNodes + i != node) {
+                    fFreeList.remove(block->fNodes + i);
+                }
+                block->fNodes[i].~Node();
+            }
+            sk_free(block);
+        } else {
+            fFreeList.addToHead(node);
+        }
+        --fCount;
+        this->validate();
+    }
+
+    void validate() const {
+#ifdef SK_DEBUG
+        SkASSERT((0 == fCount) == fList.isEmpty());
+        SkASSERT((0 != fCount) || fFreeList.isEmpty());
+
+        fList.validate();
+        fFreeList.validate();
+        typename NodeList::Iter iter;
+        Node* freeNode = iter.init(fFreeList, Iter::kHead_IterStart);
+        while (freeNode) {
+            SkASSERT(fFreeList.isInList(freeNode));
+            Block* block = freeNode->fBlock;
+            SkASSERT(block->fNodesInUse > 0 && block->fNodesInUse < fAllocCnt);
+
+            int activeCnt = 0;
+            int freeCnt = 0;
+            for (int i = 0; i < fAllocCnt; ++i) {
+                bool free = fFreeList.isInList(block->fNodes + i);
+                bool active = fList.isInList(block->fNodes + i);
+                SkASSERT(free != active);
+                activeCnt += active;
+                freeCnt += free;
+            }
+            SkASSERT(activeCnt == block->fNodesInUse);
+            freeNode = iter.next();
+        }
+
+        int count = 0;
+        Node* activeNode = iter.init(fList, Iter::kHead_IterStart);
+        while (activeNode) {
+            ++count;
+            SkASSERT(fList.isInList(activeNode));
+            Block* block = activeNode->fBlock;
+            SkASSERT(block->fNodesInUse > 0 && block->fNodesInUse <= fAllocCnt);
+
+            int activeCnt = 0;
+            int freeCnt = 0;
+            for (int i = 0; i < fAllocCnt; ++i) {
+                bool free = fFreeList.isInList(block->fNodes + i);
+                bool active = fList.isInList(block->fNodes + i);
+                SkASSERT(free != active);
+                activeCnt += active;
+                freeCnt += free;
+            }
+            SkASSERT(activeCnt == block->fNodesInUse);
+            activeNode = iter.next();
+        }
+        SkASSERT(count == fCount);
+#endif
+    }
+
+    // Support in-place initializing of objects inserted into the list via operator new.
+    friend void* operator new<T>(size_t,
+                                 SkTLList* list,
+                                 Placement placement,
+                                 const Iter& location);
+
+
+    // Helpers that insert the node and returns a pointer to where the new object should be init'ed.
+    void* internalAddBefore(Iter location) {
+        this->validate();
+        Node* node = this->createNode();
+        fList.addBefore(node, location.getNode());
+        this->validate();
+        return node->fObj;
+    }
+
+    void* internalAddAfter(Iter location) {
+        this->validate();
+        Node* node = this->createNode();
+        fList.addAfter(node, location.getNode());
+        this->validate();
+        return node->fObj;
+    }
+
+    NodeList fList;
+    NodeList fFreeList;
+    int fCount;
+    int fAllocCnt;
+
+};
+
+// Use the below macros rather than calling this directly
+template <typename T>
+void *operator new(size_t, SkTLList<T>* list,
+                   typename SkTLList<T>::Placement placement,
+                   const typename SkTLList<T>::Iter& location) {
+    SkASSERT(NULL != list);
+    if (SkTLList<T>::kBefore_Placement == placement) {
+        return list->internalAddBefore(location);
+    } else {
+        return list->internalAddAfter(location);
+    }
+}
+
+// Skia doesn't use C++ exceptions but it may be compiled with them enabled. Having an op delete
+// to match the op new silences warnings about missing op delete when a constructor throws an
+// exception.
+template <typename T>
+void operator delete(void*,
+                     SkTLList<T>*,
+                     typename SkTLList<T>::Placement,
+                     const typename SkTLList<T>::Iter&) {
+    SK_CRASH();
+}
+
+#define SkNEW_INSERT_IN_LLIST_BEFORE(list, location, type_name, args) \
+    (new ((list), SkTLList< type_name >::kBefore_Placement, (location)) type_name args)
+
+#define SkNEW_INSERT_IN_LLIST_AFTER(list, location, type_name, args) \
+    (new ((list), SkTLList< type_name >::kAfter_Placement, (location)) type_name args)
+
+#define SkNEW_INSERT_AT_LLIST_HEAD(list, type_name, args) \
+    SkNEW_INSERT_IN_LLIST_BEFORE((list), (list)->headIter(), type_name, args)
+
+#define SkNEW_INSERT_AT_LLIST_TAIL(list, type_name, args) \
+    SkNEW_INSERT_IN_LLIST_AFTER((list), (list)->tailIter(), type_name, args)
+
+#endif
diff --git a/src/core/SkTLS.cpp b/src/core/SkTLS.cpp
new file mode 100755
index 0000000..f7bf304
--- /dev/null
+++ b/src/core/SkTLS.cpp
@@ -0,0 +1,123 @@
+#include "SkTLS.h"
+
+// enable to help debug TLS storage
+//#define SK_TRACE_TLS_LIFETIME
+
+
+#ifdef SK_TRACE_TLS_LIFETIME
+    #include "SkThread.h"
+    static int32_t gTLSRecCount;
+#endif
+
+struct SkTLSRec {
+    SkTLSRec*           fNext;
+    void*               fData;
+    SkTLS::CreateProc   fCreateProc;
+    SkTLS::DeleteProc   fDeleteProc;
+
+#ifdef SK_TRACE_TLS_LIFETIME
+    SkTLSRec() {
+        int n = sk_atomic_inc(&gTLSRecCount);
+        SkDebugf(" SkTLSRec[%d]\n", n);
+    }
+#endif
+
+    ~SkTLSRec() {
+        if (fDeleteProc) {
+            fDeleteProc(fData);
+        }
+        // else we leak fData, or it will be managed by the caller
+
+#ifdef SK_TRACE_TLS_LIFETIME
+        int n = sk_atomic_dec(&gTLSRecCount);
+        SkDebugf("~SkTLSRec[%d]\n", n - 1);
+#endif
+    }
+};
+
+void SkTLS::Destructor(void* ptr) {
+#ifdef SK_TRACE_TLS_LIFETIME
+    SkDebugf("SkTLS::Destructor(%p)\n", ptr);
+#endif
+
+    SkTLSRec* rec = (SkTLSRec*)ptr;
+    do {
+        SkTLSRec* next = rec->fNext;
+        SkDELETE(rec);
+        rec = next;
+    } while (NULL != rec);
+}
+
+void* SkTLS::Get(CreateProc createProc, DeleteProc deleteProc) {
+    if (NULL == createProc) {
+        return NULL;
+    }
+
+    void* ptr = SkTLS::PlatformGetSpecific(true);
+
+    if (ptr) {
+        const SkTLSRec* rec = (const SkTLSRec*)ptr;
+        do {
+            if (rec->fCreateProc == createProc) {
+                SkASSERT(rec->fDeleteProc == deleteProc);
+                return rec->fData;
+            }
+        } while ((rec = rec->fNext) != NULL);
+        // not found, so create a new one
+    }
+
+    // add a new head of our change
+    SkTLSRec* rec = new SkTLSRec;
+    rec->fNext = (SkTLSRec*)ptr;
+
+    SkTLS::PlatformSetSpecific(rec);
+
+    rec->fData = createProc();
+    rec->fCreateProc = createProc;
+    rec->fDeleteProc = deleteProc;
+    return rec->fData;
+}
+
+void* SkTLS::Find(CreateProc createProc) {
+    if (NULL == createProc) {
+        return NULL;
+    }
+
+    void* ptr = SkTLS::PlatformGetSpecific(false);
+
+    if (ptr) {
+        const SkTLSRec* rec = (const SkTLSRec*)ptr;
+        do {
+            if (rec->fCreateProc == createProc) {
+                return rec->fData;
+            }
+        } while ((rec = rec->fNext) != NULL);
+    }
+    return NULL;
+}
+
+void SkTLS::Delete(CreateProc createProc) {
+    if (NULL == createProc) {
+        return;
+    }
+
+    void* ptr = SkTLS::PlatformGetSpecific(false);
+
+    SkTLSRec* curr = (SkTLSRec*)ptr;
+    SkTLSRec* prev = NULL;
+    while (curr) {
+        SkTLSRec* next = curr->fNext;
+        if (curr->fCreateProc == createProc) {
+            if (prev) {
+                prev->fNext = next;
+            } else {
+                // we have a new head of our chain
+                SkTLS::PlatformSetSpecific(next);
+            }
+            SkDELETE(curr);
+            break;
+        }
+        prev = curr;
+        curr = next;
+    }
+}
diff --git a/src/core/SkTLS.h b/src/core/SkTLS.h
new file mode 100644
index 0000000..ad5daa7
--- /dev/null
+++ b/src/core/SkTLS.h
@@ -0,0 +1,84 @@
+//
+//  SkTLS.h
+//
+//
+//  Created by Mike Reed on 4/21/12.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef SkTLS_DEFINED
+#define SkTLS_DEFINED
+
+#include "SkTypes.h"
+
+/**
+ *  Maintains a per-thread cache, using a CreateProc as the key into that cache.
+ */
+class SkTLS {
+public:
+    typedef void* (*CreateProc)();
+    typedef void  (*DeleteProc)(void*);
+
+    /**
+     *  If Get() has previously been called with this CreateProc, then this
+     *  returns its cached data, otherwise it returns NULL. The CreateProc is
+     *  never invoked in Find, it is only used as a key for searching the
+     *  cache.
+     */
+    static void* Find(CreateProc);
+
+    /**
+     *  Return the cached data that was returned by the CreateProc. This proc
+     *  is only called the first time Get is called, and there after it is
+     *  cached (per-thread), using the CreateProc as a key to look it up.
+     *
+     *  When this thread, or Delete is called, the cached data is removed, and
+     *  if a DeleteProc was specified, it is passed the pointer to the cached
+     *  data.
+     */
+    static void* Get(CreateProc, DeleteProc);
+
+    /**
+     *  Remove (optionally calling the DeleteProc if it was specificed in Get)
+     *  the cached data associated with this CreateProc. If no associated cached
+     *  data is found, do nothing.
+     */
+    static void Delete(CreateProc);
+
+private:
+    // Our implementation requires only 1 TLS slot, as we manage multiple values
+    // ourselves in a list, with the platform specific value as our head.
+
+    /**
+     *  Implemented by the platform, to return the value of our (one) slot per-thread
+     *
+     *  If forceCreateTheSlot is true, then we must have created the "slot" for
+     *  our TLS, even though we know that the return value will be NULL in that
+     *  case (i.e. no-slot and first-time-slot both return NULL). This ensures
+     *  that after calling GetSpecific, we know that we can legally call
+     *  SetSpecific.
+     *
+     *  If forceCreateTheSlot is false, then the impl can either create the
+     *  slot or not.
+     */
+    static void* PlatformGetSpecific(bool forceCreateTheSlot);
+
+    /**
+     *  Implemented by the platform, to set the value for our (one) slot per-thread
+     *
+     *  The implementation can rely on GetSpecific(true) having been previously
+     *  called before SetSpecific is called.
+     */
+    static void  PlatformSetSpecific(void*);
+
+public:
+    /**
+     *  Will delete our internal list. To be called by the platform if/when its
+     *  TLS slot is deleted (often at thread shutdown).
+     *
+     *  Public *only* for the platform's use, not to be called by a client.
+     */
+    static void Destructor(void* ptr);
+};
+
+#endif
diff --git a/src/core/SkTRefArray.h b/src/core/SkTRefArray.h
new file mode 100755
index 0000000..9692adb
--- /dev/null
+++ b/src/core/SkTRefArray.h
@@ -0,0 +1,112 @@
+//
+//  SkTRefArray.h
+//  core
+//
+//  Created by Mike Reed on 7/17/12.
+//  Copyright (c) 2012 __MyCompanyName__. All rights reserved.
+//
+
+#ifndef SkTRefArray_DEFINED
+#define SkTRefArray_DEFINED
+
+#include "SkRefCnt.h"
+#include <new>
+
+/**
+ *  Wrapper to manage thread-safe sharing of an array of T objects. The array
+ *  cannot be grown or shrunk.
+ */
+template <typename T> class SkTRefArray : public SkRefCnt {
+    /*
+     *  Shared factory to allocate the space needed for our instance plus N
+     *  T entries at the end. We call our constructor, but not the constructors
+     *  for the elements. Those are called by the proper Create method.
+     */
+    static SkTRefArray<T>* Alloc(int count) {
+        // space for us, and our [count] elements
+        size_t size = sizeof(SkTRefArray<T>) + count * sizeof(T);
+        SkTRefArray<T>* obj = (SkTRefArray<T>*)sk_malloc_throw(size);
+
+        SkNEW_PLACEMENT(obj, SkTRefArray<T>);
+        obj->fCount = count;
+        return obj;
+    }
+
+public:
+    /**
+     *  Return a new array with 'count' elements, initialized to their default
+     *  value. To change them to some other value, use writableBegin/End or
+     *  writableAt(), but do that before this array is given to another thread.
+     */
+    static SkTRefArray<T>* Create(int count) {
+        SkTRefArray<T>* obj = Alloc(count);
+        T* array = const_cast<T*>(obj->begin());
+        for (int i = 0; i < count; ++i) {
+            SkNEW_PLACEMENT(&array[i], T);
+        }
+        return obj;
+    }
+
+    /**
+     *  Return a new array with 'count' elements, initialized from the provided
+     *  src array. To change them to some other value, use writableBegin/End or
+     *  writableAt(), but do that before this array is given to another thread.
+     */
+    static SkTRefArray<T>* Create(const T src[], int count) {
+        SkTRefArray<T>* obj = Alloc(count);
+        T* array = const_cast<T*>(obj->begin());
+        for (int i = 0; i < count; ++i) {
+            SkNEW_PLACEMENT_ARGS(&array[i], T, (src[i]));
+        }
+        return obj;
+    }
+
+    int count() const { return fCount; }
+    const T* begin() const { return (const T*)(this + 1); }
+    const T* end() const { return this->begin() + fCount; }
+    const T& at(int index) const {
+        SkASSERT((unsigned)index < (unsigned)fCount);
+        return this->begin()[index];
+    }
+    const T& operator[](int index) const { return this->at(index); }
+
+    // For the writable methods, we assert that we are the only owner if we
+    // call these, since other owners are not informed if we change an element.
+
+    T* writableBegin() {
+        SkASSERT(1 == this->getRefCnt());
+        return (T*)(this + 1);
+    }
+    T* writableEnd() {
+        return this->writableBegin() + fCount;
+    }
+    T& writableAt(int index) {
+        SkASSERT((unsigned)index < (unsigned)fCount);
+        return this->writableBegin()[index];
+    }
+
+protected:
+    virtual void internal_dispose() const SK_OVERRIDE {
+        T* array = const_cast<T*>(this->begin());
+        int n = fCount;
+
+        for (int i = 0; i < n; ++i) {
+            array->~T();
+            array += 1;
+        }
+
+        this->internal_dispose_restore_refcnt_to_1();
+        this->~SkTRefArray<T>();
+        sk_free((void*)this);
+    }
+
+private:
+    int fCount;
+
+    // hide this
+    virtual ~SkTRefArray() {}
+
+    typedef SkRefCnt INHERITED;
+};
+
+#endif
diff --git a/src/core/SkTSearch.cpp b/src/core/SkTSearch.cpp
index ccdcecb..64c70cb 100644
--- a/src/core/SkTSearch.cpp
+++ b/src/core/SkTSearch.cpp
@@ -93,7 +93,7 @@
         lc = (char*)sk_malloc_throw(len + 1);
     }
     fLC = lc;
-    
+
     // convert any asii to lower-case. we let non-ascii (utf8) chars pass
     // through unchanged
     for (int i = (int)(len - 1); i >= 0; --i) {
@@ -112,68 +112,3 @@
         sk_free(fLC);
     }
 }
-
-//////////////////////////////////////////////////////////////////////////////
-
-#define SK_QSortTempSize    16
-
-static inline void sk_qsort_swap(char a[], char b[], size_t elemSize)
-{
-    char    tmp[SK_QSortTempSize];
-
-    while (elemSize > 0)
-    {
-        size_t size = elemSize;
-        if (size > SK_QSortTempSize)
-            size = SK_QSortTempSize;
-        elemSize -= size;
-
-        memcpy(tmp, a, size);
-        memcpy(a, b, size);
-        memcpy(b, tmp, size);
-        a += size;
-        b += size;
-    }
-}
-
-static void SkQSort_Partition(char* first, char* last, size_t elemSize, SkQSortCompareProc compare)
-{
-    char*   left = first;
-    char*   rite = last;
-    char*   pivot = left;
-
-    while (left <= rite)
-    {
-        while (left < last && compare(left, pivot) < 0)
-            left += elemSize;
-        while (first < rite && compare(rite, pivot) > 0)
-            rite -= elemSize;
-        if (left <= rite)
-        {
-            if (left < rite)
-            {
-                SkASSERT(compare(left, rite) >= 0);
-                sk_qsort_swap(left, rite, elemSize);
-            }
-            left += elemSize;
-            rite -= elemSize;
-        }
-    }
-    if (first < rite)
-        SkQSort_Partition(first, rite, elemSize, compare);
-    if (left < last)
-        SkQSort_Partition(left, last, elemSize, compare);
-}
-
-void SkQSort(void* base, size_t count, size_t elemSize, SkQSortCompareProc compare)
-{
-    SkASSERT(base);
-    SkASSERT(compare);
-    SkASSERT(elemSize > 0);
-
-    if (count <= 1)
-        return;
-
-    SkQSort_Partition((char*)base, (char*)base + (count - 1) * elemSize, elemSize, compare);
-}
-
diff --git a/src/core/SkTSort.h b/src/core/SkTSort.h
index 38f8f22..fbdb55b 100644
--- a/src/core/SkTSort.h
+++ b/src/core/SkTSort.h
@@ -11,32 +11,202 @@
 #define SkTSort_DEFINED
 
 #include "SkTypes.h"
+#include "SkMath.h"
+#include <stddef.h>
 
-template <typename T>
-void SkTHeapSort_SiftDown(T array[], int root, int bottom) {
-    while (root*2 + 1 <= bottom) {
-        int child = root * 2 + 1;
-        if (child+1 <= bottom && array[child] < array[child+1]) {
-            child += 1;
+/* A comparison functor which performs the comparison 'a < b'. */
+template <typename T> struct SkTCompareLT {
+    bool operator()(const T a, const T b) const { return a < b; }
+};
+
+/* A comparison functor which performs the comparison '*a < *b'. */
+template <typename T> struct SkTPointerCompareLT {
+    bool operator()(const T* a, const T* b) const { return *a < *b; }
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+/*  Sifts a broken heap. The input array is a heap from root to bottom
+ *  except that the root entry may be out of place.
+ *
+ *  Sinks a hole from array[root] to leaf and then sifts the original array[root] element
+ *  from the leaf level up.
+ *
+ *  This version does extra work, in that it copies child to parent on the way down,
+ *  then copies parent to child on the way back up. When copies are inexpensive,
+ *  this is an optimization as this sift variant should only be used when
+ *  the potentially out of place root entry value is expected to be small.
+ *
+ *  @param root the one based index into array of the out-of-place root of the heap.
+ *  @param bottom the one based index in the array of the last entry in the heap.
+ */
+template <typename T, typename C>
+void SkTHeapSort_SiftUp(T array[], size_t root, size_t bottom, C lessThan) {
+    T x = array[root-1];
+    size_t start = root;
+    size_t j = root << 1;
+    while (j <= bottom) {
+        if (j < bottom && lessThan(array[j-1], array[j])) {
+            ++j;
         }
-        if (array[root] < array[child]) {
-            SkTSwap<T>(array[root], array[child]);
-            root = child;
+        array[root-1] = array[j-1];
+        root = j;
+        j = root << 1;
+    }
+    j = root >> 1;
+    while (j >= start) {
+        if (lessThan(array[j-1], x)) {
+            array[root-1] = array[j-1];
+            root = j;
+            j = root >> 1;
         } else {
             break;
         }
     }
+    array[root-1] = x;
 }
 
-template <typename T> void SkTHeapSort(T array[], int count) {
-    int i;
-    for (i = count/2 - 1; i >= 0; --i) {
-        SkTHeapSort_SiftDown<T>(array, i, count-1);
+/*  Sifts a broken heap. The input array is a heap from root to bottom
+ *  except that the root entry may be out of place.
+ *
+ *  Sifts the array[root] element from the root down.
+ *
+ *  @param root the one based index into array of the out-of-place root of the heap.
+ *  @param bottom the one based index in the array of the last entry in the heap.
+ */
+template <typename T, typename C>
+void SkTHeapSort_SiftDown(T array[], size_t root, size_t bottom, C lessThan) {
+    T x = array[root-1];
+    size_t child = root << 1;
+    while (child <= bottom) {
+        if (child < bottom && lessThan(array[child-1], array[child])) {
+            ++child;
+        }
+        if (lessThan(x, array[child-1])) {
+            array[root-1] = array[child-1];
+            root = child;
+            child = root << 1;
+        } else {
+            break;
+        }
     }
-    for (i = count - 1; i > 0; --i) {
+    array[root-1] = x;
+}
+
+/** Sorts the array of size count using comparator lessThan using a Heap Sort algorithm
+ *
+ *  @param array the array to be sorted.
+ *  @param count the number of elements in the array.
+ *  @param lessThan a functor with bool operator()(T a, T b) which returns true if a comes before b.
+ */
+template <typename T, typename C> void SkTHeapSort(T array[], size_t count, C lessThan) {
+    for (size_t i = count >> 1; i > 0; --i) {
+        SkTHeapSort_SiftDown(array, i, count, lessThan);
+    }
+
+    for (size_t i = count - 1; i > 0; --i) {
         SkTSwap<T>(array[0], array[i]);
-        SkTHeapSort_SiftDown<T>(array, 0, i-1);
+        SkTHeapSort_SiftUp(array, 1, i, lessThan);
     }
 }
 
+/** Sorts the array of size count using comparator '<' using a Heap Sort algorithm. */
+template <typename T> void SkTHeapSort(T array[], size_t count) {
+    SkTHeapSort(array, count, SkTCompareLT<T>());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/** Sorts the array of size count using comparator lessThan using an Insertion Sort algorithm. */
+template <typename T, typename C> static void SkTInsertionSort(T* left, T* right, C lessThan) {
+    for (T* next = left + 1; next <= right; ++next) {
+        T insert = *next;
+        T* hole = next;
+        while (left < hole && lessThan(insert, *(hole - 1))) {
+            *hole = *(hole - 1);
+            --hole;
+        }
+        *hole = insert;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T, typename C>
+static T* SkTQSort_Partition(T* left, T* right, T* pivot, C lessThan) {
+    T pivotValue = *pivot;
+    SkTSwap(*pivot, *right);
+    T* newPivot = left;
+    while (left < right) {
+        if (lessThan(*left, pivotValue)) {
+            SkTSwap(*left, *newPivot);
+            newPivot += 1;
+        }
+        left += 1;
+    }
+    SkTSwap(*newPivot, *right);
+    return newPivot;
+}
+
+/*  Intro Sort is a modified Quick Sort.
+ *  It recurses on the smaller region after pivoting and loops on the larger.
+ *  When the region to be sorted is a small constant size it uses Insertion Sort.
+ *  When depth becomes zero, it switches over to Heap Sort.
+ */
+template <typename T, typename C> void SkTIntroSort(int depth, T* left, T* right, C lessThan) {
+    while (left < right) {
+        if (depth == 0) {
+            SkTHeapSort<T>(left, right - left + 1, lessThan);
+            return;
+        }
+        --depth;
+
+        T* pivot = left + ((right - left) >> 1);
+        pivot = SkTQSort_Partition(left, right, pivot, lessThan);
+
+        ptrdiff_t leftSize = pivot - left;
+        ptrdiff_t rightSize = right - pivot;
+        if (leftSize < rightSize) {
+            if (leftSize < 8) {
+                SkTInsertionSort(left, pivot - 1, lessThan);
+            } else {
+                SkTIntroSort(depth, left, pivot - 1, lessThan);
+            }
+            left = pivot + 1;
+        } else {
+            if (rightSize < 8) {
+                SkTInsertionSort(pivot + 1, right, lessThan);
+            } else {
+                SkTIntroSort(depth, pivot + 1, right, lessThan);
+            }
+            right = pivot - 1;
+        }
+    }
+}
+
+/** Sorts the region from left to right using comparator lessThan using a Quick Sort algorithm.
+ *
+ *  @param left the beginning of the region to be sorted.
+ *  @param right the end of the region to be sorted (inclusive).
+ *  @param lessThan a functor with bool operator()(T a, T b) which returns true if a comes before b.
+ */
+template <typename T, typename C> void SkTQSort(T* left, T* right, C lessThan) {
+    if (left >= right) {
+        return;
+    }
+    ptrdiff_t size = right - left;
+    int depth = SkNextLog2(SkToU32(size));
+    SkTIntroSort(depth * 2, left, right, lessThan);
+}
+
+/** Sorts the region from left to right using comparator '<' using a Quick Sort algorithm. */
+template <typename T> void SkTQSort(T* left, T* right) {
+    SkTQSort(left, right, SkTCompareLT<T>());
+}
+
+/** Sorts the region from left to right using comparator '* < *' using a Quick Sort algorithm. */
+template <typename T> void SkTQSort(T** left, T** right) {
+    SkTQSort(left, right, SkTPointerCompareLT<T>());
+}
+
 #endif
diff --git a/src/core/SkTextToPathIter.h b/src/core/SkTextToPathIter.h
new file mode 100644
index 0000000..73f27ed
--- /dev/null
+++ b/src/core/SkTextToPathIter.h
@@ -0,0 +1,50 @@
+/*
+ * 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 SkTextToPathIter_DEFINED
+#define SkTextToPathIter_DEFINED
+
+#include "SkAutoKern.h"
+#include "SkPaint.h"
+
+class SkGlyphCache;
+
+class SkTextToPathIter {
+public:
+    SkTextToPathIter(const char text[], size_t length, const SkPaint& paint,
+                     bool applyStrokeAndPathEffects);
+    ~SkTextToPathIter();
+
+    const SkPaint&  getPaint() const { return fPaint; }
+    SkScalar        getPathScale() const { return fScale; }
+
+    struct Rec {
+        const SkPath*   fPath;  // may be null for "whitespace" glyphs
+        SkScalar        fXPos;
+    };
+
+    /**
+     *  Returns false when all of the text has been consumed
+     */
+    bool next(const SkPath** path, SkScalar* xpos);
+
+private:
+    SkGlyphCache*   fCache;
+    SkPaint         fPaint;
+    SkScalar        fScale;
+    SkFixed         fPrevAdvance;
+    const char*     fText;
+    const char*     fStop;
+    SkMeasureCacheProc fGlyphCacheProc;
+
+    const SkPath*   fPath;      // returned in next
+    SkScalar        fXPos;      // accumulated xpos, returned in next
+    SkAutoKern      fAutoKern;
+    int             fXYIndex;   // cache for horizontal -vs- vertical text
+};
+
+#endif
diff --git a/src/core/SkTileGrid.cpp b/src/core/SkTileGrid.cpp
new file mode 100644
index 0000000..2b885c9
--- /dev/null
+++ b/src/core/SkTileGrid.cpp
@@ -0,0 +1,105 @@
+
+/*
+ * 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 "SkTileGrid.h"
+
+SkTileGrid::SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount, SkTileGridNextDatumFunctionPtr nextDatumFunction)
+{
+    fTileWidth = tileWidth;
+    fTileHeight = tileHeight;
+    fXTileCount = xTileCount;
+    fYTileCount = yTileCount;
+    fTileCount = fXTileCount * fYTileCount;
+    fInsertionCount = 0;
+    fGridBounds = SkIRect::MakeXYWH(0, 0, fTileWidth * fXTileCount, fTileHeight * fYTileCount);
+    fNextDatumFunction = nextDatumFunction;
+    fTileData = SkNEW_ARRAY(SkTDArray<void *>, fTileCount);
+}
+
+SkTileGrid::~SkTileGrid() {
+    SkDELETE_ARRAY(fTileData);
+}
+
+SkTDArray<void *>& SkTileGrid::tile(int x, int y) {
+    return fTileData[y * fXTileCount + x];
+}
+
+void SkTileGrid::insert(void* data, const SkIRect& bounds, bool) {
+    SkASSERT(!bounds.isEmpty());
+    SkIRect dilatedBounds = bounds;
+    dilatedBounds.outset(1,1); // Consideration for filtering and AA
+
+    if (!SkIRect::Intersects(dilatedBounds, fGridBounds)) {
+        return;
+    }
+
+    int minTileX = SkMax32(SkMin32(dilatedBounds.left() / fTileWidth, fXTileCount - 1), 0);
+    int maxTileX = SkMax32(SkMin32(dilatedBounds.right() / fTileWidth, fXTileCount - 1), 0);
+    int minTileY = SkMax32(SkMin32(dilatedBounds.top() / fTileHeight, fYTileCount -1), 0);
+    int maxTileY = SkMax32(SkMin32(dilatedBounds.bottom() / fTileHeight, fYTileCount -1), 0);
+
+    for (int x = minTileX; x <= maxTileX; x++) {
+        for (int y = minTileY; y <= maxTileY; y++) {
+            this->tile(x, y).push(data);
+        }
+    }
+    fInsertionCount++;
+}
+
+void SkTileGrid::search(const SkIRect& query, SkTDArray<void*>* results) {
+    // The +1/-1 is to compensate for the outset in applied SkCanvas::getClipBounds
+    int tileStartX = (query.left() + 1) / fTileWidth;
+    int tileEndX = (query.right() + fTileWidth - 1) / fTileWidth;
+    int tileStartY = (query.top() + 1) / fTileHeight;
+    int tileEndY = (query.bottom() + fTileHeight - 1) / fTileHeight;
+    if (tileStartX >= fXTileCount || tileStartY >= fYTileCount || tileEndX <= 0 || tileEndY <= 0) {
+        return; // query does not intersect the grid
+    }
+    // clamp to grid
+    if (tileStartX < 0) tileStartX = 0;
+    if (tileStartY < 0) tileStartY = 0;
+    if (tileEndX > fXTileCount) tileEndX = fXTileCount;
+    if (tileEndY > fYTileCount) tileEndY = fYTileCount;
+
+    int queryTileCount = (tileEndX - tileStartX) * (tileEndY - tileStartY);
+    if (queryTileCount == 1) {
+        *results = this->tile(tileStartX, tileStartY);
+    } else {
+        results->reset();
+        SkTDArray<int> curPositions;
+        curPositions.setCount(queryTileCount);
+        // Note: Reserving space for 1024 tile pointers on the stack. If the
+        // malloc becomes a bottleneck, we may consider increasing that number.
+        // Typical large web page, say 2k x 16k, would require 512 tiles of
+        // size 256 x 256 pixels.
+        SkAutoSTArray<1024, SkTDArray<void *>*> storage(queryTileCount);
+        SkTDArray<void *>** tileRange = storage.get();
+        int tile = 0;
+        for (int x = tileStartX; x < tileEndX; ++x) {
+            for (int y = tileStartY; y < tileEndY; ++y) {
+                tileRange[tile] = &this->tile(x, y);
+                curPositions[tile] = tileRange[tile]->count() ? 0 : kTileFinished;
+                ++tile;
+            }
+        }
+        void *nextElement;
+        while(NULL != (nextElement = fNextDatumFunction(tileRange, curPositions))) {
+            results->push(nextElement);
+        }
+    }
+}
+
+void SkTileGrid::clear() {
+    for (int i = 0; i < fTileCount; i++) {
+        fTileData[i].reset();
+    }
+}
+
+int SkTileGrid::getCount() const {
+    return fInsertionCount;
+}
diff --git a/src/core/SkTileGrid.h b/src/core/SkTileGrid.h
new file mode 100644
index 0000000..8ae3f2c
--- /dev/null
+++ b/src/core/SkTileGrid.h
@@ -0,0 +1,120 @@
+
+/*
+ * 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 SkTileGrid_DEFINED
+#define SkTileGrid_DEFINED
+
+#include "SkBBoxHierarchy.h"
+#include "SkPictureStateTree.h"
+
+/**
+ * Subclass of SkBBoxHierarchy that stores elements in buckets that correspond
+ * to tile regions, disposed in a regular grid.  This is useful when the tile
+ * structure that will be use in search() calls is known prior to insertion.
+ * Calls to search will return in constant time.
+ *
+ * Note: Current implementation of search() only supports looking-up regions
+ * that are an exact match to a single tile.  Implementation could be augmented
+ * to support arbitrary rectangles, but performance would be sub-optimal.
+ */
+class SkTileGrid : public SkBBoxHierarchy {
+public:
+    typedef void* (*SkTileGridNextDatumFunctionPtr)(SkTDArray<void*>** tileData, SkTDArray<int>& tileIndices);
+
+    SkTileGrid(int tileWidth, int tileHeight, int xTileCount, int yTileCount,
+        SkTileGridNextDatumFunctionPtr nextDatumFunction);
+
+    virtual ~SkTileGrid();
+
+    /**
+     * Insert a data pointer and corresponding bounding box
+     * @param data The data pointer, may be NULL
+     * @param bounds The bounding box, should not be empty
+     * @param defer Ignored, TileArray does not defer insertions
+     */
+    virtual void insert(void* data, const SkIRect& bounds, bool) SK_OVERRIDE;
+
+    virtual void flushDeferredInserts() SK_OVERRIDE {};
+
+    /**
+     * Populate 'results' with data pointers corresponding to bounding boxes that intersect 'query'
+     * The query argument is expected to be an exact match to a tile of the grid
+     */
+    virtual void search(const SkIRect& query, SkTDArray<void*>* results) SK_OVERRIDE;
+
+    virtual void clear() SK_OVERRIDE;
+
+    /**
+     * Gets the number of insertions
+     */
+    virtual int getCount() const SK_OVERRIDE;
+
+    // Used by search() and in SkTileGridHelper implementations
+    enum {
+        kTileFinished = -1,
+    };
+private:
+    SkTDArray<void*>& tile(int x, int y);
+
+    int fTileWidth, fTileHeight, fXTileCount, fYTileCount, fTileCount;
+    SkTDArray<void*>* fTileData;
+    int fInsertionCount;
+    SkIRect fGridBounds;
+    SkTileGridNextDatumFunctionPtr fNextDatumFunction;
+
+    friend class TileGridTest;
+    typedef SkBBoxHierarchy INHERITED;
+};
+
+/**
+ * Generic implementation for SkTileGridNextDatumFunctionPtr. user code may instantiate
+ * this template to get a valid SkTileGridNextDatumFunction implementation
+ *
+ * Returns the next element of tileData[i][tileIndices[i]] for all i and advances
+ * tileIndices[] past them. The order in which data are returned by successive
+ * calls to this method must reflect the order in which the were originally
+ * recorded into the tile grid.
+ *
+ * \param tileData array of pointers to arrays of tile data
+ * \param tileIndices per-tile data indices, indices are incremented for tiles that contain
+ *     the next datum.
+ * \tparam T a type to which it is safe to cast a datum and that has an operator <
+ *     such that 'a < b' is true if 'a' was inserted into the tile grid before 'b'.
+ */
+template <typename T>
+void* SkTileGridNextDatum(SkTDArray<void*>** tileData, SkTDArray<int>& tileIndices) {
+    bool haveVal = false;
+    T* minVal;
+    int tileCount = tileIndices.count();
+    // Find the next Datum
+    for (int tile = 0; tile < tileCount; ++tile) {
+        int pos = tileIndices[tile];
+        if (pos != SkTileGrid::kTileFinished) {
+            T* candidate = (T*)(*tileData[tile])[pos];
+            if (!haveVal || (*candidate) < (*minVal)) {
+                minVal = candidate;
+                haveVal = true;
+            }
+        }
+    }
+    // Increment indices past the next datum
+    if (haveVal) {
+        for (int tile = 0; tile < tileCount; ++tile) {
+            int pos = tileIndices[tile];
+            if (pos != SkTileGrid::kTileFinished && (*tileData[tile])[pos] == minVal) {
+                if (++(tileIndices[tile]) >= tileData[tile]->count()) {
+                    tileIndices[tile] = SkTileGrid::kTileFinished;
+                }
+            }
+        }
+        return minVal;
+    }
+    return NULL;
+}
+
+#endif
diff --git a/src/core/SkTileGridPicture.cpp b/src/core/SkTileGridPicture.cpp
new file mode 100644
index 0000000..212e3b6
--- /dev/null
+++ b/src/core/SkTileGridPicture.cpp
@@ -0,0 +1,24 @@
+/*
+ * 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 "SkTileGridPicture.h"
+
+#include "SkPictureStateTree.h"
+#include "SkTileGrid.h"
+
+
+SkTileGridPicture::SkTileGridPicture(int tileWidth, int tileHeight, int width, int height) {
+    fTileWidth = tileWidth;
+    fTileHeight = tileHeight;
+    fXTileCount = (width + tileWidth - 1) / tileWidth;
+    fYTileCount = (height + tileHeight - 1) / tileHeight;
+}
+
+SkBBoxHierarchy* SkTileGridPicture::createBBoxHierarchy() const {
+    return SkNEW_ARGS(SkTileGrid, (fTileWidth, fTileHeight, fXTileCount, fYTileCount,
+        SkTileGridNextDatum<SkPictureStateTree::Draw>));
+}
diff --git a/src/core/SkTypeface.cpp b/src/core/SkTypeface.cpp
index e3b49e6..c30e5a9 100644
--- a/src/core/SkTypeface.cpp
+++ b/src/core/SkTypeface.cpp
@@ -11,6 +11,8 @@
 #include "SkTypeface.h"
 #include "SkFontHost.h"
 
+SK_DEFINE_INST_COUNT(SkTypeface)
+
 //#define TRACE_LIFECYCLE
 
 #ifdef TRACE_LIFECYCLE
@@ -34,7 +36,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-static SkTypeface* get_default_typeface() {
+SkTypeface* SkTypeface::GetDefaultTypeface() {
     // we keep a reference to this guy for all time, since if we return its
     // fontID, the font cache may later on ask to resolve that back into a
     // typeface object.
@@ -42,15 +44,14 @@
 
     if (NULL == gDefaultTypeface) {
         gDefaultTypeface =
-        SkFontHost::CreateTypeface(NULL, NULL, NULL, 0,
-                                   SkTypeface::kNormal);
+        SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
     }
     return gDefaultTypeface;
 }
 
 uint32_t SkTypeface::UniqueID(const SkTypeface* face) {
     if (NULL == face) {
-        face = get_default_typeface();
+        face = GetDefaultTypeface();
     }
     return face->uniqueID();
 }
@@ -62,16 +63,11 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 SkTypeface* SkTypeface::CreateFromName(const char name[], Style style) {
-    return SkFontHost::CreateTypeface(NULL, name, NULL, 0, style);
-}
-
-SkTypeface* SkTypeface::CreateForChars(const void* data, size_t bytelength,
-                                       Style s) {
-    return SkFontHost::CreateTypeface(NULL, NULL, data, bytelength, s);
+    return SkFontHost::CreateTypeface(NULL, name, style);
 }
 
 SkTypeface* SkTypeface::CreateFromTypeface(const SkTypeface* family, Style s) {
-    return SkFontHost::CreateTypeface(family, NULL, NULL, 0, s);
+    return SkFontHost::CreateTypeface(family, NULL, s);
 }
 
 SkTypeface* SkTypeface::CreateFromStream(SkStream* stream) {
@@ -101,3 +97,40 @@
                                                   glyphIDs,
                                                   glyphIDsCount);
 }
+
+///////////////////////////////////////////////////////////////////////////////
+
+int SkTypeface::countTables() const {
+    return SkFontHost::CountTables(fUniqueID);
+}
+
+int SkTypeface::getTableTags(SkFontTableTag tags[]) const {
+    return SkFontHost::GetTableTags(fUniqueID, tags);
+}
+
+size_t SkTypeface::getTableSize(SkFontTableTag tag) const {
+    return SkFontHost::GetTableSize(fUniqueID, tag);
+}
+
+size_t SkTypeface::getTableData(SkFontTableTag tag, size_t offset, size_t length,
+                                void* data) const {
+    return SkFontHost::GetTableData(fUniqueID, tag, offset, length, data);
+}
+
+int SkTypeface::getUnitsPerEm() const {
+    int upem = 0;
+
+#ifdef SK_BUILD_FOR_ANDROID
+    upem = SkFontHost::GetUnitsPerEm(fUniqueID);
+#else
+    SkAdvancedTypefaceMetrics* metrics;
+    metrics = SkFontHost::GetAdvancedTypefaceMetrics(fUniqueID,
+                                 SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo,
+                                 NULL, 0);
+    if (metrics) {
+        upem = metrics->fEmSize;
+        metrics->unref();
+    }
+#endif
+    return upem;
+}
diff --git a/src/core/SkTypefaceCache.cpp b/src/core/SkTypefaceCache.cpp
index f4397a6..bb83d55 100644
--- a/src/core/SkTypefaceCache.cpp
+++ b/src/core/SkTypefaceCache.cpp
@@ -13,7 +13,9 @@
 
 #define TYPEFACE_CACHE_LIMIT    128
 
-void SkTypefaceCache::add(SkTypeface* face, SkTypeface::Style requestedStyle) {
+void SkTypefaceCache::add(SkTypeface* face,
+                          SkTypeface::Style requestedStyle,
+                          bool strong) {
     if (fArray.count() >= TYPEFACE_CACHE_LIMIT) {
         this->purge(TYPEFACE_CACHE_LIMIT >> 2);
     }
@@ -21,7 +23,12 @@
     Rec* rec = fArray.append();
     rec->fFace = face;
     rec->fRequestedStyle = requestedStyle;
-    face->ref();
+    rec->fStrong = strong;
+    if (strong) {
+        face->ref();
+    } else {
+        face->weak_ref();
+    }
 }
 
 SkTypeface* SkTypefaceCache::findByID(SkFontID fontID) const {
@@ -36,12 +43,20 @@
     return NULL;
 }
 
-SkTypeface* SkTypefaceCache::findByProc(FindProc proc, void* ctx) const {
+SkTypeface* SkTypefaceCache::findByProcAndRef(FindProc proc, void* ctx) const {
     const Rec* curr = fArray.begin();
     const Rec* stop = fArray.end();
     while (curr < stop) {
-        if (proc(curr->fFace, curr->fRequestedStyle, ctx)) {
-            return curr->fFace;
+        SkTypeface* currFace = curr->fFace;
+        if (proc(currFace, curr->fRequestedStyle, ctx)) {
+            if (curr->fStrong) {
+                currFace->ref();
+                return currFace;
+            } else if (currFace->try_ref()) {
+                return currFace;
+            } else {
+                //remove currFace from fArray?
+            }
         }
         curr += 1;
     }
@@ -53,8 +68,15 @@
     int i = 0;
     while (i < count) {
         SkTypeface* face = fArray[i].fFace;
-        if (1 == face->getRefCnt()) {
-            face->unref();
+        bool strong = fArray[i].fStrong;
+        if ((strong && face->getRefCnt() == 1) ||
+            (!strong && face->weak_expired()))
+        {
+            if (strong) {
+                face->unref();
+            } else {
+                face->weak_unref();
+            }
             fArray.remove(i);
             --count;
             if (--numToPurge == 0) {
@@ -84,9 +106,11 @@
 
 SK_DECLARE_STATIC_MUTEX(gMutex);
 
-void SkTypefaceCache::Add(SkTypeface* face, SkTypeface::Style requestedStyle) {
+void SkTypefaceCache::Add(SkTypeface* face,
+                          SkTypeface::Style requestedStyle,
+                          bool strong) {
     SkAutoMutexAcquire ama(gMutex);
-    Get().add(face, requestedStyle);
+    Get().add(face, requestedStyle, strong);
 }
 
 SkTypeface* SkTypefaceCache::FindByID(SkFontID fontID) {
@@ -96,8 +120,7 @@
 
 SkTypeface* SkTypefaceCache::FindByProcAndRef(FindProc proc, void* ctx) {
     SkAutoMutexAcquire ama(gMutex);
-    SkTypeface* typeface = Get().findByProc(proc, ctx);
-    SkSafeRef(typeface);
+    SkTypeface* typeface = Get().findByProcAndRef(proc, ctx);
     return typeface;
 }
 
@@ -119,7 +142,6 @@
 void SkTypefaceCache::Dump() {
 #ifdef SK_DEBUG
     SkAutoMutexAcquire ama(gMutex);
-    (void)Get().findByProc(DumpProc, NULL);
+    (void)Get().findByProcAndRef(DumpProc, NULL);
 #endif
 }
-
diff --git a/src/core/SkTypefaceCache.h b/src/core/SkTypefaceCache.h
index e65ec90..b6cab81 100644
--- a/src/core/SkTypefaceCache.h
+++ b/src/core/SkTypefaceCache.h
@@ -26,7 +26,7 @@
     /**
      * Callback for FindByProc. Returns true if the given typeface is a match
      * for the given context. The passed typeface is owned by the cache and is
-     * not additionally ref()ed.
+     * not additionally ref()ed. The typeface may be in the disposed state.
      */
     typedef bool (*FindProc)(SkTypeface*, SkTypeface::Style, void* context);
 
@@ -42,7 +42,9 @@
      *  whose refcnt is 1 (meaning only the cache is an owner) will be
      *  unref()ed.
      */
-    static void Add(SkTypeface*, SkTypeface::Style requested);
+    static void Add(SkTypeface*,
+                    SkTypeface::Style requested,
+                    bool strong = true);
 
     /**
      *  Search the cache for a typeface with the specified fontID (uniqueID).
@@ -75,14 +77,15 @@
 private:
     static SkTypefaceCache& Get();
 
-    void add(SkTypeface*, SkTypeface::Style requested);
+    void add(SkTypeface*, SkTypeface::Style requested, bool strong = true);
     SkTypeface* findByID(SkFontID findID) const;
-    SkTypeface* findByProc(FindProc proc, void* ctx) const;
+    SkTypeface* findByProcAndRef(FindProc proc, void* ctx) const;
     void purge(int count);
     void purgeAll();
 
     struct Rec {
         SkTypeface*         fFace;
+        bool                fStrong;
         SkTypeface::Style   fRequestedStyle;
     };
     SkTDArray<Rec> fArray;
diff --git a/src/core/SkUnPreMultiply.cpp b/src/core/SkUnPreMultiply.cpp
index 5f32da6..ad87f8a 100644
--- a/src/core/SkUnPreMultiply.cpp
+++ b/src/core/SkUnPreMultiply.cpp
@@ -56,18 +56,18 @@
 void SkUnPreMultiply_BuildTable() {
     for (unsigned i = 0; i <= 255; i++) {
         uint32_t scale;
-        
+
         if (0 == i) {
             scale = 0;
         } else {
             scale = ((255 << 24) + (i >> 1)) / i;
         }
-        
+
         SkDebugf(" 0x%08X,", scale);
         if ((i & 7) == 7) {
             SkDebugf("\n");
         }
-        
+
         // test the result
         for (int j = 1; j <= i; j++) {
             uint32_t test = (j * scale + (1 << 23)) >> 24;
diff --git a/src/core/SkUtils.cpp b/src/core/SkUtils.cpp
index 3f1c65e..1af64c2 100644
--- a/src/core/SkUtils.cpp
+++ b/src/core/SkUtils.cpp
@@ -207,7 +207,7 @@
     const uint8_t*  p = (const uint8_t*)*ptr;
     int             c = *p;
     int             hic = c << 24;
-    
+
     assert_utf8_leadingbyte(c);
 
     if (hic < 0) {
@@ -227,7 +227,7 @@
     SkASSERT(NULL != ptr && NULL != *ptr);
 
     const char* p = *ptr;
-    
+
     if (*--p & 0x80) {
         while (*--p & 0x40) {
             ;
@@ -314,15 +314,15 @@
 
 SkUnichar SkUTF16_NextUnichar(const uint16_t** srcPtr) {
     SkASSERT(srcPtr && *srcPtr);
-    
+
     const uint16_t* src = *srcPtr;
     SkUnichar       c = *src++;
-    
+
     SkASSERT(!SkUTF16_IsLowSurrogate(c));
     if (SkUTF16_IsHighSurrogate(c)) {
         unsigned c2 = *src++;
         SkASSERT(SkUTF16_IsLowSurrogate(c2));
-        
+
         // c = ((c & 0x3FF) << 10) + (c2 & 0x3FF) + 0x10000
         // c = (((c & 0x3FF) + 64) << 10) + (c2 & 0x3FF)
         c = (c << 10) + c2 + (0x10000 - (0xD800 << 10) - 0xDC00);
@@ -333,10 +333,10 @@
 
 SkUnichar SkUTF16_PrevUnichar(const uint16_t** srcPtr) {
     SkASSERT(srcPtr && *srcPtr);
-    
+
     const uint16_t* src = *srcPtr;
     SkUnichar       c = *--src;
-    
+
     SkASSERT(!SkUTF16_IsHighSurrogate(c));
     if (SkUTF16_IsLowSurrogate(c)) {
         unsigned c2 = *--src;
@@ -358,7 +358,7 @@
             // dst[0] = SkToU16(0xD800 | ((uni >> 10) - 64));
             dst[0] = SkToU16((0xD800 - 64) + (uni >> 10));
             dst[1] = SkToU16(0xDC00 | (uni & 0x3FF));
-            
+
             SkASSERT(SkUTF16_IsHighSurrogate(dst[0]));
             SkASSERT(SkUTF16_IsLowSurrogate(dst[1]));
         } else {
@@ -378,10 +378,10 @@
     }
 
     SkASSERT(utf16 != NULL);
-    
+
     const uint16_t* stop = utf16 + numberOf16BitValues;
     size_t          size = 0;
-    
+
     if (utf8 == NULL) {    // just count
         while (utf16 < stop) {
             size += SkUTF8_FromUnichar(SkUTF16_NextUnichar(&utf16), NULL);
@@ -428,4 +428,3 @@
             round_to_K(mi.uordblks));
 #endif
 }
-
diff --git a/src/core/SkUtilsArm.cpp b/src/core/SkUtilsArm.cpp
new file mode 100644
index 0000000..b0eaac0
--- /dev/null
+++ b/src/core/SkUtilsArm.cpp
@@ -0,0 +1,197 @@
+
+/*
+ * Copyright 2012 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 "SkUtilsArm.h"
+
+#if SK_ARM_NEON_IS_DYNAMIC
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <pthread.h>
+
+// Set USE_ANDROID_NDK_CPU_FEATURES to use the Android NDK's
+// cpu-features helper library to detect NEON at runtime. See
+// http://crbug.com/164154 to see why this is needed in Chromium
+// for Android.
+#if defined(SK_BUILD_FOR_ANDROID) && defined(SK_BUILD_FOR_CHROMIUM)
+#  define USE_ANDROID_NDK_CPU_FEATURES 1
+#else
+#  define USE_ANDROID_NDK_CPU_FEATURES 0
+#endif
+
+#if USE_ANDROID_NDK_CPU_FEATURES
+#  include <cpu-features.h>
+#endif
+
+// Set NEON_DEBUG to 1 to allow debugging of the CPU features probing.
+// For now, we always set it for SK_DEBUG builds.
+#ifdef SK_DEBUG
+#  define NEON_DEBUG  1
+#else
+#  define NEON_DEBUG 0
+#endif
+
+#if NEON_DEBUG
+#  ifdef SK_BUILD_FOR_ANDROID
+     // used to declare PROP_VALUE_MAX and __system_property_get()
+#    include <sys/system_properties.h>
+#  endif
+#endif
+
+// A function used to determine at runtime if the target CPU supports
+// the ARM NEON instruction set. This implementation is Linux-specific.
+static bool sk_cpu_arm_check_neon(void) {
+    bool result = false;
+
+#if NEON_DEBUG
+    // Allow forcing the mode through the environment during debugging.
+#  ifdef SK_BUILD_FOR_ANDROID
+    // On Android, we use a system property
+#   define PROP_NAME  "debug.skia.arm_neon_mode"
+    char prop[PROP_VALUE_MAX];
+    if (__system_property_get(PROP_NAME, prop) > 0) {
+#  else
+#   define PROP_NAME   "SKIA_ARM_NEON_MODE"
+    // On ARM Linux, we use an environment variable
+    const char* prop = getenv(PROP_NAME);
+    if (prop != NULL) {
+#  endif
+        SkDebugf("%s: %s", PROP_NAME, prop);
+        if (!strcmp(prop, "1")) {
+            SkDebugf("Forcing ARM Neon mode to full!\n");
+            return true;
+        }
+        if (!strcmp(prop, "0")) {
+            SkDebugf("Disabling ARM NEON mode\n");
+            return false;
+        }
+    }
+    SkDebugf("Running dynamic CPU feature detection\n");
+#endif
+
+#if USE_ANDROID_NDK_CPU_FEATURES
+
+  result = (android_getCpuFeatures() & ANDROID_CPU_ARM_FEATURE_NEON) != 0;
+
+#else  // USE_ANDROID_NDK_CPU_FEATURES
+
+    // There is no user-accessible CPUID instruction on ARM that we can use.
+    // Instead, we must parse /proc/cpuinfo and look for the 'neon' feature.
+    // For example, here's a typical output (Nexus S running ICS 4.0.3):
+    /*
+    Processor       : ARMv7 Processor rev 2 (v7l)
+    BogoMIPS        : 994.65
+    Features        : swp half thumb fastmult vfp edsp thumbee neon vfpv3
+    CPU implementer : 0x41
+    CPU architecture: 7
+    CPU variant     : 0x2
+    CPU part        : 0xc08
+    CPU revision    : 2
+
+    Hardware        : herring
+    Revision        : 000b
+    Serial          : 3833c77d6dc000ec
+    */
+    char   buffer[4096];
+
+    // If we fail any of the following, assume we don't have NEON instructions
+    // This allows us to return immediately in case of error.
+    result = false;
+
+    do {
+        // open /proc/cpuinfo
+        int fd = TEMP_FAILURE_RETRY(open("/proc/cpuinfo", O_RDONLY));
+        if (fd < 0) {
+            SkDebugf("Could not open /proc/cpuinfo: %s\n", strerror(errno));
+            break;
+        }
+
+        // Read the file. To simplify our search, we're going to place two
+        // sentinel '\n' characters: one at the start of the buffer, and one at
+        // the end. This means we reserve the first and last buffer bytes.
+        buffer[0] = '\n';
+        int size = TEMP_FAILURE_RETRY(read(fd, buffer+1, sizeof(buffer)-2));
+        close(fd);
+
+        if (size < 0) {  // should not happen
+            SkDebugf("Could not read /proc/cpuinfo: %s\n", strerror(errno));
+            break;
+        }
+
+        SkDebugf("START /proc/cpuinfo:\n%.*s\nEND /proc/cpuinfo\n",
+                 size, buffer+1);
+
+        // Compute buffer limit, and place final sentinel
+        char* buffer_end = buffer + 1 + size;
+        buffer_end[0] = '\n';
+
+        // Now, find a line that starts with "Features", i.e. look for
+        // '\nFeatures ' in our buffer.
+        const char features[] = "\nFeatures\t";
+        const size_t features_len = sizeof(features)-1;
+
+        char*  line = (char*) memmem(buffer, buffer_end - buffer,
+                                     features, features_len);
+        if (line == NULL) {  // Weird, no Features line, bad kernel?
+            SkDebugf("Could not find a line starting with 'Features'"
+              "in /proc/cpuinfo ?\n");
+            break;
+        }
+
+        line += features_len;  // Skip the "\nFeatures\t" prefix
+
+        // Find the end of the current line
+        char* line_end = (char*) memchr(line, '\n', buffer_end - line);
+        if (line_end == NULL)
+            line_end = buffer_end;
+
+        // Now find an instance of 'neon' in the flags list. We want to
+        // ensure it's only 'neon' and not something fancy like 'noneon'
+        // so check that it follows a space.
+        const char neon[] = " neon";
+        const size_t neon_len = sizeof(neon)-1;
+        const char* flag = (const char*) memmem(line, line_end - line,
+                                                neon, neon_len);
+        if (flag == NULL)
+            break;
+
+        // Ensure it is followed by a space or a newline.
+        if (flag[neon_len] != ' ' && flag[neon_len] != '\n')
+            break;
+
+        // Fine, we support Arm NEON !
+        result = true;
+
+    } while (0);
+
+#endif  // USE_ANDROID_NDK_CPU_FEATURES
+
+    if (result) {
+        SkDebugf("Device supports ARM NEON instructions!\n");
+    } else {
+        SkDebugf("Device does NOT support ARM NEON instructions!\n");
+    }
+    return result;
+}
+
+static pthread_once_t  sOnce;
+static bool            sHasArmNeon;
+
+// called through pthread_once()
+void sk_cpu_arm_probe_features(void) {
+    sHasArmNeon = sk_cpu_arm_check_neon();
+}
+
+bool sk_cpu_arm_has_neon(void) {
+    pthread_once(&sOnce, sk_cpu_arm_probe_features);
+    return sHasArmNeon;
+}
+
+#endif // SK_ARM_NEON_IS_DYNAMIC
diff --git a/src/core/SkUtilsArm.h b/src/core/SkUtilsArm.h
new file mode 100644
index 0000000..b9a2614
--- /dev/null
+++ b/src/core/SkUtilsArm.h
@@ -0,0 +1,87 @@
+
+/*
+ * Copyright 2012 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 SkUtilsArm_DEFINED
+#define SkUtilsArm_DEFINED
+
+#include "SkUtils.h"
+
+// Define SK_ARM_NEON_MODE to one of the following values
+// corresponding respectively to:
+// - No ARM Neon support at all  (not targetting ARMv7-A, or don't have NEON)
+// - Full ARM Neon support (i.e. assume the CPU always supports it)
+// - Optional ARM Neon support (i.e. probe CPU at runtime)
+//
+#define SK_ARM_NEON_MODE_NONE     0
+#define SK_ARM_NEON_MODE_ALWAYS   1
+#define SK_ARM_NEON_MODE_DYNAMIC  2
+
+#if defined(SK_CPU_ARM) && defined(__ARM_HAVE_OPTIONAL_NEON_SUPPORT)
+#  define SK_ARM_NEON_MODE  SK_ARM_NEON_MODE_DYNAMIC
+#elif defined(SK_CPU_ARM) && defined(__ARM_HAVE_NEON)
+#  define SK_ARM_NEON_MODE  SK_ARM_NEON_MODE_ALWAYS
+#else
+#  define SK_ARM_NEON_MODE  SK_ARM_NEON_MODE_NONE
+#endif
+
+// Convenience test macros, always defined as 0 or 1
+#define SK_ARM_NEON_IS_NONE    (SK_ARM_NEON_MODE == SK_ARM_NEON_MODE_NONE)
+#define SK_ARM_NEON_IS_ALWAYS  (SK_ARM_NEON_MODE == SK_ARM_NEON_MODE_ALWAYS)
+#define SK_ARM_NEON_IS_DYNAMIC (SK_ARM_NEON_MODE == SK_ARM_NEON_MODE_DYNAMIC)
+
+// The sk_cpu_arm_has_neon() function returns true iff the target device
+// is ARMv7-A and supports Neon instructions. In DYNAMIC mode, this actually
+// probes the CPU at runtime (and caches the result).
+
+#if SK_ARM_NEON_IS_NONE
+static inline bool sk_cpu_arm_has_neon(void) {
+    return false;
+}
+#elif SK_ARM_NEON_IS_ALWAYS
+static inline bool sk_cpu_arm_has_neon(void) {
+    return true;
+}
+#else // SK_ARM_NEON_IS_DYNAMIC
+
+extern bool sk_cpu_arm_has_neon(void) SK_PURE_FUNC;
+#endif
+
+// Use SK_ARM_NEON_WRAP(symbol) to map 'symbol' to a NEON-specific symbol
+// when applicable. This will transform 'symbol' differently depending on
+// the current NEON configuration, i.e.:
+//
+//    NONE           -> 'symbol'
+//    ALWAYS         -> 'symbol_neon'
+//    DYNAMIC        -> 'symbol' or 'symbol_neon' depending on runtime check.
+//
+// The goal is to simplify user code, for example:
+//
+//      return SK_ARM_NEON_WRAP(do_something)(params);
+//
+// Replaces the equivalent:
+//
+//     #if SK_ARM_NEON_IS_NONE
+//       return do_something(params);
+//     #elif SK_ARM_NEON_IS_ALWAYS
+//       return do_something_neon(params);
+//     #elif SK_ARM_NEON_IS_DYNAMIC
+//       if (sk_cpu_arm_has_neon())
+//         return do_something_neon(params);
+//       else
+//         return do_something(params);
+//     #endif
+//
+#if SK_ARM_NEON_IS_NONE
+#  define SK_ARM_NEON_WRAP(x)   (x)
+#elif SK_ARM_NEON_IS_ALWAYS
+#  define SK_ARM_NEON_WRAP(x)   (x ## _neon)
+#elif SK_ARM_NEON_IS_DYNAMIC
+#  define SK_ARM_NEON_WRAP(x)   (sk_cpu_arm_has_neon() ? x ## _neon : x)
+#endif
+
+#endif // SkUtilsArm_DEFINED
diff --git a/src/core/SkWriter32.cpp b/src/core/SkWriter32.cpp
index dfa18a0..56edd19 100644
--- a/src/core/SkWriter32.cpp
+++ b/src/core/SkWriter32.cpp
@@ -1,54 +1,35 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkWriter32.h"
 
-struct SkWriter32::Block {
-    Block*  fNext;
-    size_t  fSize;
-    size_t  fAllocated;
-    
-    size_t  available() const { return fSize - fAllocated; }
-    char*   base() { return (char*)(this + 1); }
-    const char* base() const { return (const char*)(this + 1); }
-    
-    uint32_t* alloc(size_t size) {
-        SkASSERT(SkAlign4(size) == size);
-        SkASSERT(this->available() >= size);
-        void* ptr = this->base() + fAllocated;
-        fAllocated += size;
-        SkASSERT(fAllocated <= fSize);
-        return (uint32_t*)ptr;
-    }
-    
-    uint32_t* peek32(size_t offset) {
-        SkASSERT(offset <= fAllocated + 4);
-        void* ptr = this->base() + offset;
-        return (uint32_t*)ptr;
-    }
+SkWriter32::SkWriter32(size_t minSize, void* storage, size_t storageSize) {
+    fMinSize = minSize;
+    fSize = 0;
+    fWrittenBeforeLastBlock = 0;
+    fHead = fTail = NULL;
 
-    static Block* Create(size_t size) {
-        SkASSERT(SkAlign4(size) == size);
-        Block* block = (Block*)sk_malloc_throw(sizeof(Block) + size);
-        block->fNext = NULL;
-        block->fSize = size;
-        block->fAllocated = 0;
-        return block;
+    if (storageSize) {
+        this->reset(storage, storageSize);
     }
-};
-
-///////////////////////////////////////////////////////////////////////////////
+}
 
 SkWriter32::~SkWriter32() {
     this->reset();
 }
 
 void SkWriter32::reset() {
-    Block* block = fHead;    
+    Block* block = fHead;
+
+    if (this->isHeadExternallyAllocated()) {
+        SkASSERT(block);
+        // don't 'free' the first block, since it is owned by the caller
+        block = block->fNext;
+    }
     while (block) {
         Block* next = block->fNext;
         sk_free(block);
@@ -56,73 +37,119 @@
     }
 
     fSize = 0;
+    fWrittenBeforeLastBlock = 0;
     fHead = fTail = NULL;
-    fSingleBlock = NULL;
 }
 
-void SkWriter32::reset(void* block, size_t size) {
+void SkWriter32::reset(void* storage, size_t storageSize) {
     this->reset();
-    SkASSERT(0 == ((fSingleBlock - (char*)0) & 3));   // need 4-byte alignment
-    fSingleBlock = (char*)block;
-    fSingleBlockSize = (size & ~3);
+
+    storageSize &= ~3;  // trunc down to multiple of 4
+    if (storageSize > 0 && SkIsAlign4((intptr_t)storage)) {
+        fHead = fTail = fExternalBlock.initFromStorage(storage, storageSize);
+    }
 }
 
-uint32_t* SkWriter32::reserve(size_t size) {
+SkWriter32::Block* SkWriter32::doReserve(size_t size) {
     SkASSERT(SkAlign4(size) == size);
 
-    if (fSingleBlock) {
-        uint32_t* ptr = (uint32_t*)(fSingleBlock + fSize);
-        fSize += size;
-        SkASSERT(fSize <= fSingleBlockSize);
-        return ptr;
-    }
-
     Block* block = fTail;
+    SkASSERT(NULL == block || block->available() < size);
 
     if (NULL == block) {
         SkASSERT(NULL == fHead);
         fHead = fTail = block = Block::Create(SkMax32(size, fMinSize));
-    } else if (block->available() < size) {
+        SkASSERT(0 == fWrittenBeforeLastBlock);
+    } else {
+        SkASSERT(fSize > 0);
+        fWrittenBeforeLastBlock = fSize;
+
         fTail = Block::Create(SkMax32(size, fMinSize));
         block->fNext = fTail;
         block = fTail;
     }
-    
-    fSize += size;
-
-    return block->alloc(size);
+    return block;
 }
 
 uint32_t* SkWriter32::peek32(size_t offset) {
+    SkDEBUGCODE(this->validate();)
+
     SkASSERT(SkAlign4(offset) == offset);
     SkASSERT(offset <= fSize);
 
-    if (fSingleBlock) {
-        return (uint32_t*)(fSingleBlock + offset);
+    // try the fast case, where offset is within fTail
+    if (offset >= fWrittenBeforeLastBlock) {
+        return fTail->peek32(offset - fWrittenBeforeLastBlock);
     }
 
     Block* block = fHead;
     SkASSERT(NULL != block);
-    
-    while (offset >= block->fAllocated) {
-        offset -= block->fAllocated;
+
+    while (offset >= block->fAllocatedSoFar) {
+        offset -= block->fAllocatedSoFar;
         block = block->fNext;
         SkASSERT(NULL != block);
     }
     return block->peek32(offset);
 }
 
-void SkWriter32::flatten(void* dst) const {
-    if (fSingleBlock) {
-        memcpy(dst, fSingleBlock, fSize);
+void SkWriter32::rewindToOffset(size_t offset) {
+    if (offset >= fSize) {
+        return;
+    }
+    if (0 == offset) {
+        this->reset();
         return;
     }
 
+    SkDEBUGCODE(this->validate();)
+
+    SkASSERT(SkAlign4(offset) == offset);
+    SkASSERT(offset <= fSize);
+    fSize = offset;
+
+    // Try the fast case, where offset is within fTail
+    if (offset >= fWrittenBeforeLastBlock) {
+        fTail->fAllocatedSoFar = offset - fWrittenBeforeLastBlock;
+    } else {
+        // Similar to peek32, except that we free up any following blocks.
+        // We have to re-compute fWrittenBeforeLastBlock as well.
+
+        size_t globalOffset = offset;
+        Block* block = fHead;
+        SkASSERT(NULL != block);
+        while (offset >= block->fAllocatedSoFar) {
+            offset -= block->fAllocatedSoFar;
+            block = block->fNext;
+            SkASSERT(NULL != block);
+        }
+
+        // this has to be recomputed, since we may free up fTail
+        fWrittenBeforeLastBlock = globalOffset - offset;
+
+        // update the size on the "last" block
+        block->fAllocatedSoFar = offset;
+        // end our list
+        fTail = block;
+        Block* next = block->fNext;
+        block->fNext = NULL;
+        // free up any trailing blocks
+        block = next;
+        while (block) {
+            Block* next = block->fNext;
+            sk_free(block);
+            block = next;
+        }
+    }
+    SkDEBUGCODE(this->validate();)
+}
+
+void SkWriter32::flatten(void* dst) const {
     const Block* block = fHead;
     SkDEBUGCODE(size_t total = 0;)
 
     while (block) {
-        size_t allocated = block->fAllocated;
+        size_t allocated = block->fAllocatedSoFar;
         memcpy(dst, block->base(), allocated);
         dst = (char*)dst + allocated;
         block = block->fNext;
@@ -133,35 +160,33 @@
     SkASSERT(total == fSize);
 }
 
+uint32_t* SkWriter32::reservePad(size_t size) {
+    if (size > 0) {
+        size_t alignedSize = SkAlign4(size);
+        char* dst = (char*)this->reserve(alignedSize);
+        // Pad the last four bytes with zeroes in one step.
+        uint32_t* padding = (uint32_t*)(dst + (alignedSize - 4));
+        *padding = 0;
+        return (uint32_t*) dst;
+    }
+    return this->reserve(0);
+}
+
 void SkWriter32::writePad(const void* src, size_t size) {
-    size_t alignedSize = SkAlign4(size);
-    char* dst = (char*)this->reserve(alignedSize);
-    memcpy(dst, src, size);
-    dst += size;
-    int n = alignedSize - size;
-    while (--n >= 0) {
-        *dst++ = 0;
+    if (size > 0) {
+        char* dst = (char*)this->reservePad(size);
+        // Copy the actual data.
+        memcpy(dst, src, size);
     }
 }
 
 #include "SkStream.h"
 
 size_t SkWriter32::readFromStream(SkStream* stream, size_t length) {
-    if (fSingleBlock) {
-        SkASSERT(fSingleBlockSize >= fSize);
-        size_t remaining = fSingleBlockSize - fSize;
-        if (length > remaining) {
-            length = remaining;
-        }
-        stream->read(fSingleBlock + fSize, length);
-        fSize += length;
-        return length;
-    }
-
     char scratch[1024];
     const size_t MAX = sizeof(scratch);
     size_t remaining = length;
-    
+
     while (remaining != 0) {
         size_t n = remaining;
         if (n > MAX) {
@@ -178,13 +203,9 @@
 }
 
 bool SkWriter32::writeToStream(SkWStream* stream) {
-    if (fSingleBlock) {
-        return stream->write(fSingleBlock, fSize);
-    }
-
-    const Block* block = fHead;    
+    const Block* block = fHead;
     while (block) {
-        if (!stream->write(block->base(), block->fAllocated)) {
+        if (!stream->write(block->base(), block->fAllocatedSoFar)) {
             return false;
         }
         block = block->fNext;
@@ -192,6 +213,28 @@
     return true;
 }
 
+#ifdef SK_DEBUG
+void SkWriter32::validate() const {
+    SkASSERT(SkIsAlign4(fSize));
+
+    size_t accum = 0;
+    const Block* block = fHead;
+    while (block) {
+        SkASSERT(SkIsAlign4(block->fSizeOfBlock));
+        SkASSERT(SkIsAlign4(block->fAllocatedSoFar));
+        SkASSERT(block->fAllocatedSoFar <= block->fSizeOfBlock);
+        if (NULL == block->fNext) {
+            SkASSERT(fTail == block);
+            SkASSERT(fWrittenBeforeLastBlock == accum);
+        }
+        accum += block->fAllocatedSoFar;
+        SkASSERT(accum <= fSize);
+        block = block->fNext;
+    }
+    SkASSERT(accum == fSize);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkReader32.h"
@@ -233,10 +276,13 @@
     // add 1 since we also write a terminating 0
     size_t alignedLen = SkAlign4(len + 1);
     char* ptr = (char*)this->reserve(alignedLen);
+    {
+        // Write the terminating 0 and fill in the rest with zeroes
+        uint32_t* padding = (uint32_t*)(ptr + (alignedLen - 4));
+        *padding = 0;
+    }
+    // Copy the string itself.
     memcpy(ptr, str, len);
-    ptr[len] = 0;
-    // we may have left 0,1,2,3 bytes uninitialized, since we reserved align4
-    // number of bytes. That's ok, since the reader will know to skip those
 }
 
 size_t SkWriter32::WriteStringSize(const char* str, size_t len) {
@@ -248,5 +294,3 @@
     // add 1 since we also write a terminating 0
     return SkAlign4(lenBytes + len + 1);
 }
-
-
diff --git a/src/core/SkXfermode.cpp b/src/core/SkXfermode.cpp
index efedda4..520ab87 100644
--- a/src/core/SkXfermode.cpp
+++ b/src/core/SkXfermode.cpp
@@ -9,6 +9,11 @@
 
 #include "SkXfermode.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMathPriv.h"
+#include "SkString.h"
+
+SK_DEFINE_INST_COUNT(SkXfermode)
 
 #define SkAlphaMulAlpha(a, b)   SkMulDiv255Round(a, b)
 
@@ -177,8 +182,8 @@
     return SkPackARGB32(a, r, g, b);
 }
 
-// kMultiply_Mode
-static SkPMColor multiply_modeproc(SkPMColor src, SkPMColor dst) {
+// kModulate_Mode
+static SkPMColor modulate_modeproc(SkPMColor src, SkPMColor dst) {
     int a = SkAlphaMulAlpha(SkGetPackedA32(src), SkGetPackedA32(dst));
     int r = SkAlphaMulAlpha(SkGetPackedR32(src), SkGetPackedR32(dst));
     int g = SkAlphaMulAlpha(SkGetPackedG32(src), SkGetPackedG32(dst));
@@ -431,7 +436,7 @@
     { xor_modeproc,     SkXfermode::kIDA_Coeff,     SkXfermode::kISA_Coeff },
 
     { plus_modeproc,    SkXfermode::kOne_Coeff,     SkXfermode::kOne_Coeff },
-    { multiply_modeproc,SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
+    { modulate_modeproc,SkXfermode::kZero_Coeff,    SkXfermode::kSC_Coeff },
     { screen_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { overlay_modeproc,     CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
     { darken_modeproc,      CANNOT_USE_COEFF,       CANNOT_USE_COEFF },
@@ -446,22 +451,22 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) {
+bool SkXfermode::asCoeff(Coeff* src, Coeff* dst) const {
     return false;
 }
 
-bool SkXfermode::asMode(Mode* mode) {
+bool SkXfermode::asMode(Mode* mode) const {
     return false;
 }
 
-SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) {
+SkPMColor SkXfermode::xferColor(SkPMColor src, SkPMColor dst) const{
     // no-op. subclasses should override this
     return dst;
 }
 
 void SkXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
                         const SkPMColor* SK_RESTRICT src, int count,
-                        const SkAlpha* SK_RESTRICT aa) {
+                        const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     if (NULL == aa) {
@@ -485,7 +490,7 @@
 
 void SkXfermode::xfer16(uint16_t* dst,
                         const SkPMColor* SK_RESTRICT src, int count,
-                        const SkAlpha* SK_RESTRICT aa) {
+                        const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     if (NULL == aa) {
@@ -510,8 +515,7 @@
 
 void SkXfermode::xfer4444(SkPMColor16* SK_RESTRICT dst,
                           const SkPMColor* SK_RESTRICT src, int count,
-                          const SkAlpha* SK_RESTRICT aa)
-{
+                          const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     if (NULL == aa) {
@@ -536,8 +540,7 @@
 
 void SkXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
                         const SkPMColor src[], int count,
-                        const SkAlpha* SK_RESTRICT aa)
-{
+                        const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     if (NULL == aa) {
@@ -565,7 +568,7 @@
 
 void SkProcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
                             const SkPMColor* SK_RESTRICT src, int count,
-                            const SkAlpha* SK_RESTRICT aa) {
+                            const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     SkXfermodeProc proc = fProc;
@@ -593,7 +596,7 @@
 
 void SkProcXfermode::xfer16(uint16_t* SK_RESTRICT dst,
                             const SkPMColor* SK_RESTRICT src, int count,
-                            const SkAlpha* SK_RESTRICT aa) {
+                            const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     SkXfermodeProc proc = fProc;
@@ -622,7 +625,7 @@
 
 void SkProcXfermode::xfer4444(SkPMColor16* SK_RESTRICT dst,
                               const SkPMColor* SK_RESTRICT src, int count,
-                              const SkAlpha* SK_RESTRICT aa) {
+                              const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     SkXfermodeProc proc = fProc;
@@ -651,7 +654,7 @@
 
 void SkProcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
                             const SkPMColor* SK_RESTRICT src, int count,
-                            const SkAlpha* SK_RESTRICT aa) {
+                            const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     SkXfermodeProc proc = fProc;
@@ -681,20 +684,25 @@
 
 SkProcXfermode::SkProcXfermode(SkFlattenableReadBuffer& buffer)
         : SkXfermode(buffer) {
-    // Might be a NULL if the Xfermode is recorded using the CrossProcess flag
-    fProc = (SkXfermodeProc)buffer.readFunctionPtr();
+    fProc = NULL;
+    if (!buffer.isCrossProcess()) {
+        fProc = (SkXfermodeProc)buffer.readFunctionPtr();
+    }
 }
 
-void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) {
-    if (buffer.isCrossProcess()) {
-        // function pointer is only valid in the current process. Write a NULL
-        // so it can't be accidentally used
-        buffer.writeFunctionPtr(NULL);
-    } else {
+void SkProcXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    if (!buffer.isCrossProcess()) {
         buffer.writeFunctionPtr((void*)fProc);
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkProcXfermode::toString(SkString* str) const {
+    str->appendf("SkProcXfermode: %p", fProc);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -708,14 +716,14 @@
         fDstCoeff = rec.fDC;
     }
 
-    virtual bool asMode(Mode* mode) {
+    virtual bool asMode(Mode* mode) const SK_OVERRIDE {
         if (mode) {
             *mode = fMode;
         }
         return true;
     }
 
-    virtual bool asCoeff(Coeff* sc, Coeff* dc) {
+    virtual bool asCoeff(Coeff* sc, Coeff* dc) const SK_OVERRIDE {
         if (CANNOT_USE_COEFF == fSrcCoeff) {
             return false;
         }
@@ -729,26 +737,12 @@
         return true;
     }
 
-    virtual Factory getFactory() { return CreateProc; }
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
-        this->INHERITED::flatten(buffer);
-        buffer.write32(fMode);
-    }
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkProcCoeffXfermode, (buffer));
-    }
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkProcCoeffXfermode)
 
 protected:
-    SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer)
-            : INHERITED(buffer) {
-        fMode = (SkXfermode::Mode)buffer.readU32();
-
-        if (buffer.getPictureVersion() == PICTURE_VERSION_ICS) {
-            fSrcCoeff = (Coeff)buffer.readU32();
-            fDstCoeff = (Coeff)buffer.readU32();
-            return;
-        }
+    SkProcCoeffXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fMode = (SkXfermode::Mode)buffer.read32();
 
         const ProcCoeff& rec = gProcCoeffs[fMode];
         // these may be valid, or may be CANNOT_USE_COEFF
@@ -758,37 +752,74 @@
         this->INHERITED::setProc(rec.fProc);
     }
 
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+        this->INHERITED::flatten(buffer);
+        buffer.write32(fMode);
+    }
+
 private:
     Mode    fMode;
     Coeff   fSrcCoeff, fDstCoeff;
 
-
     typedef SkProcXfermode INHERITED;
 };
 
+#ifdef SK_DEVELOPER
+void SkProcCoeffXfermode::toString(SkString* str) const {
+    str->append("SkProcCoeffXfermode: ");
+
+    const char *gModeStrings[kLastMode+1] = {
+        "Clear", "Src", "Dst", "SrcOver", "DstOver", "SrcIn", "DstIn",
+        "SrcOut", "DstOut", "SrcATop", "DstATop", "Xor", "Plus",
+        "Modulate", "Screen", "Overlay", "Darken", "Lighten", "ColorDodge",
+        "ColorBurn", "HardLight", "SoftLight", "Difference", "Exclusion"
+    };
+
+    str->append("mode: ");
+    str->append(gModeStrings[fMode]);
+
+    static const char* gCoeffStrings[kCoeffCount] = {
+        "Zero", "One", "SC", "ISC", "DC", "IDC", "SA", "ISA", "DA", "IDA"
+    };
+
+    str->append(" src: ");
+    if (CANNOT_USE_COEFF == fSrcCoeff) {
+        str->append("can't use");
+    } else {
+        str->append(gCoeffStrings[fSrcCoeff]);
+    }
+
+    str->append(" dst: ");
+    if (CANNOT_USE_COEFF == fDstCoeff) {
+        str->append("can't use");
+    } else {
+        str->append(gCoeffStrings[fDstCoeff]);
+    }
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkClearXfermode : public SkProcCoeffXfermode {
 public:
     SkClearXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kClear_Mode) {}
 
-    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) SK_OVERRIDE;
-    virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
+    virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkClearXfermode, (buffer));
-    }
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkClearXfermode)
 
 private:
     SkClearXfermode(SkFlattenableReadBuffer& buffer)
         : SkProcCoeffXfermode(buffer) {}
 
+    typedef SkProcCoeffXfermode INHERITED;
 };
 
 void SkClearXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
                              const SkPMColor* SK_RESTRICT, int count,
-                             const SkAlpha* SK_RESTRICT aa) {
+                             const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && count >= 0);
 
     if (NULL == aa) {
@@ -806,7 +837,7 @@
 }
 void SkClearXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
                              const SkPMColor* SK_RESTRICT, int count,
-                             const SkAlpha* SK_RESTRICT aa) {
+                             const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && count >= 0);
 
     if (NULL == aa) {
@@ -823,29 +854,34 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkClearXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 class SkSrcXfermode : public SkProcCoeffXfermode {
 public:
     SkSrcXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kSrc_Mode) {}
 
-    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) SK_OVERRIDE;
-    virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
+    virtual void xferA8(SkAlpha*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkSrcXfermode, (buffer));
-    }
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSrcXfermode)
 
 private:
     SkSrcXfermode(SkFlattenableReadBuffer& buffer)
         : SkProcCoeffXfermode(buffer) {}
 
+    typedef SkProcCoeffXfermode INHERITED;
 };
 
 void SkSrcXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
                            const SkPMColor* SK_RESTRICT src, int count,
-                           const SkAlpha* SK_RESTRICT aa) {
+                           const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     if (NULL == aa) {
@@ -864,7 +900,7 @@
 
 void SkSrcXfermode::xferA8(SkAlpha* SK_RESTRICT dst,
                            const SkPMColor* SK_RESTRICT src, int count,
-                           const SkAlpha* SK_RESTRICT aa) {
+                           const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src && count >= 0);
 
     if (NULL == aa) {
@@ -885,19 +921,22 @@
         }
     }
 }
+#ifdef SK_DEVELOPER
+void SkSrcXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
 
-////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
 
 class SkDstInXfermode : public SkProcCoeffXfermode {
 public:
     SkDstInXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstIn_Mode) {}
 
-    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkDstInXfermode, (buffer));
-    }
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstInXfermode)
 
 private:
     SkDstInXfermode(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {}
@@ -907,7 +946,7 @@
 
 void SkDstInXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
                              const SkPMColor* SK_RESTRICT src, int count,
-                             const SkAlpha* SK_RESTRICT aa) {
+                             const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src);
 
     if (count <= 0) {
@@ -925,18 +964,22 @@
     } while (--count != 0);
 }
 
-/////////////////////////////////////////////////////////////////////////////////////////
+#ifdef SK_DEVELOPER
+void SkDstInXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
 
 class SkDstOutXfermode : public SkProcCoeffXfermode {
 public:
     SkDstOutXfermode(const ProcCoeff& rec) : SkProcCoeffXfermode(rec, kDstOut_Mode) {}
 
-    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
+    virtual void xfer32(SkPMColor*, const SkPMColor*, int, const SkAlpha*) const SK_OVERRIDE;
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkDstOutXfermode, (buffer));
-    }
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDstOutXfermode)
 
 private:
     SkDstOutXfermode(SkFlattenableReadBuffer& buffer)
@@ -947,7 +990,7 @@
 
 void SkDstOutXfermode::xfer32(SkPMColor* SK_RESTRICT dst,
                               const SkPMColor* SK_RESTRICT src, int count,
-                              const SkAlpha* SK_RESTRICT aa) {
+                              const SkAlpha* SK_RESTRICT aa) const {
     SkASSERT(dst && src);
 
     if (count <= 0) {
@@ -965,6 +1008,12 @@
     } while (--count != 0);
 }
 
+#ifdef SK_DEVELOPER
+void SkDstOutXfermode::toString(SkString* str) const {
+    this->INHERITED::toString(str);
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkXfermode* SkXfermode::Create(Mode mode) {
@@ -1021,7 +1070,7 @@
     return true;
 }
 
-bool SkXfermode::AsMode(SkXfermode* xfer, Mode* mode) {
+bool SkXfermode::AsMode(const SkXfermode* xfer, Mode* mode) {
     if (NULL == xfer) {
         if (mode) {
             *mode = kSrcOver_Mode;
@@ -1031,14 +1080,14 @@
     return xfer->asMode(mode);
 }
 
-bool SkXfermode::AsCoeff(SkXfermode* xfer, Coeff* src, Coeff* dst) {
+bool SkXfermode::AsCoeff(const SkXfermode* xfer, Coeff* src, Coeff* dst) {
     if (NULL == xfer) {
         return ModeAsCoeff(kSrcOver_Mode, src, dst);
     }
     return xfer->asCoeff(src, dst);
 }
 
-bool SkXfermode::IsMode(SkXfermode* xfer, Mode mode) {
+bool SkXfermode::IsMode(const SkXfermode* xfer, Mode mode) {
     // if xfer==null then the mode is srcover
     Mode m = kSrcOver_Mode;
     if (xfer && !xfer->asMode(&m)) {
@@ -1185,7 +1234,7 @@
     { NULL,                 NULL,                   NULL            }, // XOR
 
     { NULL,                 NULL,                   NULL            }, // plus
-    { NULL,                 NULL,                   NULL            }, // multiply
+    { NULL,                 NULL,                   NULL            }, // modulate
     { NULL,                 NULL,                   NULL            }, // screen
     { NULL,                 NULL,                   NULL            }, // overlay
     { darken_modeproc16_0,  darken_modeproc16_255,  NULL            }, // darken
diff --git a/src/device/xps/SkXPSDevice.cpp b/src/device/xps/SkXPSDevice.cpp
new file mode 100644
index 0000000..2f01123
--- /dev/null
+++ b/src/device/xps/SkXPSDevice.cpp
@@ -0,0 +1,2424 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef UNICODE
+#define UNICODE
+#endif
+#ifndef _UNICODE
+#define _UNICODE
+#endif
+#include "SkTypes.h"
+#include <ObjBase.h>
+#include <XpsObjectModel.h>
+#include <T2EmbApi.h>
+#include <FontSub.h>
+
+#include "SkColor.h"
+#include "SkConstexprMath.h"
+#include "SkData.h"
+#include "SkDraw.h"
+#include "SkDrawProcs.h"
+#include "SkFontHost.h"
+#include "SkGlyphCache.h"
+#include "SkHRESULT.h"
+#include "SkImageEncoder.h"
+#include "SkIStream.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+#include "SkRasterizer.h"
+#include "SkShader.h"
+#include "SkSize.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTLazy.h"
+#include "SkTScopedComPtr.h"
+#include "SkUtils.h"
+#include "SkXPSDevice.h"
+
+//Windows defines a FLOAT type,
+//make it clear when converting a scalar that this is what is wanted.
+#define SkScalarToFLOAT(n) SkScalarToFloat(n)
+
+//Dummy representation of a GUID from create_id.
+#define L_GUID_ID L"XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX"
+//Length of GUID representation from create_id, including NULL terminator.
+#define GUID_ID_LEN SK_ARRAY_COUNT(L_GUID_ID)
+
+/**
+   Formats a GUID and places it into buffer.
+   buffer should have space for at least GUID_ID_LEN wide characters.
+   The string will always be wchar null terminated.
+   XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0
+   @return -1 if there was an error, > 0 if success.
+ */
+static int format_guid(const GUID& guid,
+                       wchar_t* buffer, size_t bufferSize,
+                       wchar_t sep = '-') {
+    SkASSERT(bufferSize >= GUID_ID_LEN);
+    return swprintf_s(buffer,
+                      bufferSize,
+                      L"%08lX%c%04X%c%04X%c%02X%02X%c%02X%02X%02X%02X%02X%02X",
+                      guid.Data1,
+                      sep,
+                      guid.Data2,
+                      sep,
+                      guid.Data3,
+                      sep,
+                      guid.Data4[0],
+                      guid.Data4[1],
+                      sep,
+                      guid.Data4[2],
+                      guid.Data4[3],
+                      guid.Data4[4],
+                      guid.Data4[5],
+                      guid.Data4[6],
+                      guid.Data4[7]);
+}
+/**
+   Creates a GUID based id and places it into buffer.
+   buffer should have space for at least GUID_ID_LEN wide characters.
+   The string will always be wchar null terminated.
+   XXXXXXXXsXXXXsXXXXsXXXXsXXXXXXXXXXXX0
+   The string may begin with a digit,
+   and so may not be suitable as a bare resource key.
+ */
+static HRESULT create_id(wchar_t* buffer, size_t bufferSize,
+                         wchar_t sep = '-') {
+    GUID guid = {};
+    HRM(CoCreateGuid(&guid), "Could not create GUID for id.");
+
+    if (format_guid(guid, buffer, bufferSize, sep) == -1) {
+        HRM(E_UNEXPECTED, "Could not format GUID into id.");
+    }
+
+    return S_OK;
+}
+
+static SkBitmap make_fake_bitmap(int width, int height) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kNo_Config, width, height);
+    return bitmap;
+}
+
+SkXPSDevice::SkXPSDevice()
+    : SkDevice(make_fake_bitmap(10000, 10000))
+    , fCurrentPage(0) {
+}
+
+SkXPSDevice::~SkXPSDevice() {
+}
+
+SkXPSDevice::TypefaceUse::TypefaceUse()
+    : typefaceId(0xffffffff)
+    , fontData(NULL)
+    , xpsFont(NULL)
+    , glyphsUsed(NULL) {
+}
+
+SkXPSDevice::TypefaceUse::~TypefaceUse() {
+    //xpsFont owns fontData ref
+    this->xpsFont->Release();
+    delete this->glyphsUsed;
+}
+
+bool SkXPSDevice::beginPortfolio(SkWStream* outputStream) {
+    if (!this->fAutoCo.succeeded()) return false;
+
+    //Create XPS Factory.
+    HRBM(CoCreateInstance(
+             CLSID_XpsOMObjectFactory,
+             NULL,
+             CLSCTX_INPROC_SERVER,
+             IID_PPV_ARGS(&this->fXpsFactory)),
+         "Could not create XPS factory.");
+
+    HRBM(SkWIStream::CreateFromSkWStream(outputStream, &this->fOutputStream),
+         "Could not convert SkStream to IStream.");
+
+    return true;
+}
+
+bool SkXPSDevice::beginSheet(
+        const SkVector& unitsPerMeter,
+        const SkVector& pixelsPerMeter,
+        const SkSize& trimSize,
+        const SkRect* mediaBox,
+        const SkRect* bleedBox,
+        const SkRect* artBox,
+        const SkRect* cropBox) {
+    ++this->fCurrentPage;
+
+    //For simplicity, just write everything out in geometry units,
+    //then have a base canvas do the scale to physical units.
+    this->fCurrentCanvasSize = trimSize;
+    this->fCurrentUnitsPerMeter = unitsPerMeter;
+    this->fCurrentPixelsPerMeter = pixelsPerMeter;
+
+    this->fCurrentXpsCanvas.reset();
+    HRBM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas),
+         "Could not create base canvas.");
+
+    return true;
+}
+
+HRESULT SkXPSDevice::createXpsThumbnail(IXpsOMPage* page,
+                                        const unsigned int pageNum,
+                                        IXpsOMImageResource** image) {
+    SkTScopedComPtr<IXpsOMThumbnailGenerator> thumbnailGenerator;
+    HRM(CoCreateInstance(
+            CLSID_XpsOMThumbnailGenerator,
+            NULL,
+            CLSCTX_INPROC_SERVER,
+            IID_PPV_ARGS(&thumbnailGenerator)),
+        "Could not create thumbnail generator.");
+
+    SkTScopedComPtr<IOpcPartUri> partUri;
+    static const size_t size = SK_MAX(
+        SK_ARRAY_COUNT(L"/Documents/1/Metadata/.png") + SK_DIGITS_IN(pageNum),
+        SK_ARRAY_COUNT(L"/Metadata/" L_GUID_ID L".png")
+    );
+    wchar_t buffer[size];
+    if (pageNum > 0) {
+        swprintf_s(buffer, size, L"/Documents/1/Metadata/%u.png", pageNum);
+    } else {
+        wchar_t id[GUID_ID_LEN];
+        HR(create_id(id, GUID_ID_LEN));
+        swprintf_s(buffer, size, L"/Metadata/%s.png", id);
+    }
+    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
+        "Could not create thumbnail part uri.");
+
+    HRM(thumbnailGenerator->GenerateThumbnail(page,
+                                              XPS_IMAGE_TYPE_PNG,
+                                              XPS_THUMBNAIL_SIZE_LARGE,
+                                              partUri.get(),
+                                              image),
+        "Could not generate thumbnail.");
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::createXpsPage(const XPS_SIZE& pageSize,
+                                   IXpsOMPage** page) {
+    static const size_t size = SK_ARRAY_COUNT(L"/Documents/1/Pages/.fpage")
+                             + SK_DIGITS_IN(fCurrentPage);
+    wchar_t buffer[size];
+    swprintf_s(buffer, size, L"/Documents/1/Pages/%u.fpage",
+                             this->fCurrentPage);
+    SkTScopedComPtr<IOpcPartUri> partUri;
+    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
+        "Could not create page part uri.");
+
+    //If the language is unknown, use "und" (XPS Spec 2.3.5.1).
+    HRM(this->fXpsFactory->CreatePage(&pageSize,
+                                      L"und",
+                                      partUri.get(),
+                                      page),
+        "Could not create page.");
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::initXpsDocumentWriter(IXpsOMImageResource* image) {
+    //Create package writer.
+    {
+        SkTScopedComPtr<IOpcPartUri> partUri;
+        HRM(this->fXpsFactory->CreatePartUri(L"/FixedDocumentSequence.fdseq",
+                                             &partUri),
+            "Could not create document sequence part uri.");
+        HRM(this->fXpsFactory->CreatePackageWriterOnStream(
+                this->fOutputStream.get(),
+                TRUE,
+                XPS_INTERLEAVING_OFF, //XPS_INTERLEAVING_ON,
+                partUri.get(),
+                NULL,
+                image,
+                NULL,
+                NULL,
+                &this->fPackageWriter),
+            "Could not create package writer.");
+    }
+
+    //Begin the lone document.
+    {
+        SkTScopedComPtr<IOpcPartUri> partUri;
+        HRM(this->fXpsFactory->CreatePartUri(
+                L"/Documents/1/FixedDocument.fdoc",
+                &partUri),
+            "Could not create fixed document part uri.");
+        HRM(this->fPackageWriter->StartNewDocument(partUri.get(),
+                                                   NULL,
+                                                   NULL,
+                                                   NULL,
+                                                   NULL),
+            "Could not start document.");
+    }
+
+    return S_OK;
+}
+
+bool SkXPSDevice::endSheet() {
+    //XPS is fixed at 96dpi (XPS Spec 11.1).
+    static const float xpsDPI = 96.0f;
+    static const float inchesPerMeter = 10000.0f / 254.0f;
+    static const float targetUnitsPerMeter = xpsDPI * inchesPerMeter;
+    const float scaleX = targetUnitsPerMeter
+                       / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fX);
+    const float scaleY = targetUnitsPerMeter
+                       / SkScalarToFLOAT(this->fCurrentUnitsPerMeter.fY);
+
+    //Create the scale canvas.
+    SkTScopedComPtr<IXpsOMCanvas> scaleCanvas;
+    HRBM(this->fXpsFactory->CreateCanvas(&scaleCanvas),
+         "Could not create scale canvas.");
+    SkTScopedComPtr<IXpsOMVisualCollection> scaleCanvasVisuals;
+    HRBM(scaleCanvas->GetVisuals(&scaleCanvasVisuals),
+         "Could not get scale canvas visuals.");
+
+    SkTScopedComPtr<IXpsOMMatrixTransform> geomToPhys;
+    XPS_MATRIX rawGeomToPhys = { scaleX, 0, 0, scaleY, 0, 0, };
+    HRBM(this->fXpsFactory->CreateMatrixTransform(&rawGeomToPhys, &geomToPhys),
+         "Could not create geometry to physical transform.");
+    HRBM(scaleCanvas->SetTransformLocal(geomToPhys.get()),
+         "Could not set transform on scale canvas.");
+
+    //Add the content canvas to the scale canvas.
+    HRBM(scaleCanvasVisuals->Append(this->fCurrentXpsCanvas.get()),
+         "Could not add base canvas to scale canvas.");
+
+    //Create the page.
+    XPS_SIZE pageSize = {
+        SkScalarToFLOAT(this->fCurrentCanvasSize.width()) * scaleX,
+        SkScalarToFLOAT(this->fCurrentCanvasSize.height()) * scaleY,
+    };
+    SkTScopedComPtr<IXpsOMPage> page;
+    HRB(this->createXpsPage(pageSize, &page));
+
+    SkTScopedComPtr<IXpsOMVisualCollection> pageVisuals;
+    HRBM(page->GetVisuals(&pageVisuals), "Could not get page visuals.");
+
+    //Add the scale canvas to the page.
+    HRBM(pageVisuals->Append(scaleCanvas.get()),
+         "Could not add scale canvas to page.");
+
+    //Create the package writer if it hasn't been created yet.
+    if (NULL == this->fPackageWriter.get()) {
+        SkTScopedComPtr<IXpsOMImageResource> image;
+        //Ignore return, thumbnail is completely optional.
+        this->createXpsThumbnail(page.get(), 0, &image);
+
+        HRB(this->initXpsDocumentWriter(image.get()));
+    }
+
+    HRBM(this->fPackageWriter->AddPage(page.get(),
+                                       &pageSize,
+                                       NULL,
+                                       NULL,
+                                       NULL,
+                                       NULL),
+         "Could not write the page.");
+    this->fCurrentXpsCanvas.reset();
+
+    return true;
+}
+
+static HRESULT subset_typeface(SkXPSDevice::TypefaceUse* current) {
+    //CreateFontPackage wants unsigned short.
+    //Microsoft, Y U NO stdint.h?
+    SkTDArray<unsigned short> keepList;
+    current->glyphsUsed->exportTo(&keepList);
+
+    //The following are declared with the types required by CreateFontPackage.
+    unsigned char *puchFontPackageBuffer;
+    unsigned long pulFontPackageBufferSize;
+    unsigned long pulBytesWritten;
+    unsigned long result = CreateFontPackage(
+        (unsigned char *) current->fontData->getMemoryBase(),
+        (unsigned long) current->fontData->getLength(),
+        &puchFontPackageBuffer,
+        &pulFontPackageBufferSize,
+        &pulBytesWritten,
+        TTFCFP_FLAGS_SUBSET | TTFCFP_FLAGS_GLYPHLIST,// | TTFCFP_FLAGS_TTC,
+        0,//TTC index
+        TTFCFP_SUBSET,
+        0,
+        0,
+        0,
+        keepList.begin(),
+        keepList.count(),
+        sk_malloc_throw,
+        sk_realloc_throw,
+        sk_free,
+        NULL);
+    if (result != NO_ERROR) {
+        SkDEBUGF(("CreateFontPackage Error %lu", result));
+        return E_UNEXPECTED;
+    }
+
+    SkMemoryStream* newStream = new SkMemoryStream;
+    newStream->setMemoryOwned(puchFontPackageBuffer, pulBytesWritten);
+    SkTScopedComPtr<IStream> newIStream;
+    SkIStream::CreateFromSkStream(newStream, true, &newIStream);
+
+    XPS_FONT_EMBEDDING embedding;
+    HRM(current->xpsFont->GetEmbeddingOption(&embedding),
+        "Could not get embedding option from font.");
+
+    SkTScopedComPtr<IOpcPartUri> partUri;
+    HRM(current->xpsFont->GetPartName(&partUri),
+        "Could not get part uri from font.");
+
+    HRM(current->xpsFont->SetContent(
+            newIStream.get(),
+            embedding,
+            partUri.get()),
+        "Could not set new stream for subsetted font.");
+
+    return S_OK;
+}
+
+bool SkXPSDevice::endPortfolio() {
+    //Subset fonts
+    if (!this->fTypefaces.empty()) {
+        SkXPSDevice::TypefaceUse* current = &this->fTypefaces.front();
+        const TypefaceUse* last = &this->fTypefaces.back();
+        for (; current <= last; ++current) {
+            //Ignore return for now, if it didn't subset, let it be.
+            subset_typeface(current);
+        }
+    }
+
+    HRBM(this->fPackageWriter->Close(), "Could not close writer.");
+
+    return true;
+}
+
+static XPS_COLOR xps_color(const SkColor skColor) {
+    //XPS uses non-pre-multiplied alpha (XPS Spec 11.4).
+    XPS_COLOR xpsColor;
+    xpsColor.colorType = XPS_COLOR_TYPE_SRGB;
+    xpsColor.value.sRGB.alpha = SkColorGetA(skColor);
+    xpsColor.value.sRGB.red = SkColorGetR(skColor);
+    xpsColor.value.sRGB.green = SkColorGetG(skColor);
+    xpsColor.value.sRGB.blue = SkColorGetB(skColor);
+
+    return xpsColor;
+}
+
+static XPS_POINT xps_point(const SkPoint& point) {
+    XPS_POINT xpsPoint = {
+        SkScalarToFLOAT(point.fX),
+        SkScalarToFLOAT(point.fY),
+    };
+    return xpsPoint;
+}
+
+static XPS_POINT xps_point(const SkPoint& point, const SkMatrix& matrix) {
+    SkPoint skTransformedPoint;
+    matrix.mapXY(point.fX, point.fY, &skTransformedPoint);
+    return xps_point(skTransformedPoint);
+}
+
+static XPS_SPREAD_METHOD xps_spread_method(SkShader::TileMode tileMode) {
+    switch (tileMode) {
+    case SkShader::kClamp_TileMode:
+        return XPS_SPREAD_METHOD_PAD;
+    case SkShader::kRepeat_TileMode:
+        return XPS_SPREAD_METHOD_REPEAT;
+    case SkShader::kMirror_TileMode:
+        return XPS_SPREAD_METHOD_REFLECT;
+    default:
+        SkASSERT(!"Unknown tile mode.");
+    }
+    return XPS_SPREAD_METHOD_PAD;
+}
+
+static void transform_offsets(SkScalar* stopOffsets, const int numOffsets,
+                              const SkPoint& start, const SkPoint& end,
+                              const SkMatrix& transform) {
+    SkPoint startTransformed;
+    transform.mapXY(start.fX, start.fY, &startTransformed);
+    SkPoint endTransformed;
+    transform.mapXY(end.fX, end.fY, &endTransformed);
+
+    //Manhattan distance between transformed start and end.
+    SkScalar startToEnd = (endTransformed.fX - startTransformed.fX)
+                        + (endTransformed.fY - startTransformed.fY);
+    if (SkScalarNearlyZero(startToEnd)) {
+        for (int i = 0; i < numOffsets; ++i) {
+            stopOffsets[i] = 0;
+        }
+        return;
+    }
+
+    for (int i = 0; i < numOffsets; ++i) {
+        SkPoint stop;
+        stop.fX = SkScalarMul(end.fX - start.fX, stopOffsets[i]);
+        stop.fY = SkScalarMul(end.fY - start.fY, stopOffsets[i]);
+
+        SkPoint stopTransformed;
+        transform.mapXY(stop.fX, stop.fY, &stopTransformed);
+
+        //Manhattan distance between transformed start and stop.
+        SkScalar startToStop = (stopTransformed.fX - startTransformed.fX)
+                             + (stopTransformed.fY - startTransformed.fY);
+        //Percentage along transformed line.
+        stopOffsets[i] = SkScalarDiv(startToStop, startToEnd);
+    }
+}
+
+HRESULT SkXPSDevice::createXpsTransform(const SkMatrix& matrix,
+                                        IXpsOMMatrixTransform** xpsTransform) {
+    SkScalar affine[6];
+    if (!matrix.asAffine(affine)) {
+        *xpsTransform = NULL;
+        return S_FALSE;
+    }
+    XPS_MATRIX rawXpsMatrix = {
+        SkScalarToFLOAT(affine[SkMatrix::kAScaleX]),
+        SkScalarToFLOAT(affine[SkMatrix::kASkewY]),
+        SkScalarToFLOAT(affine[SkMatrix::kASkewX]),
+        SkScalarToFLOAT(affine[SkMatrix::kAScaleY]),
+        SkScalarToFLOAT(affine[SkMatrix::kATransX]),
+        SkScalarToFLOAT(affine[SkMatrix::kATransY]),
+    };
+    HRM(this->fXpsFactory->CreateMatrixTransform(&rawXpsMatrix, xpsTransform),
+        "Could not create transform.");
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::createPath(IXpsOMGeometryFigure* figure,
+                                IXpsOMVisualCollection* visuals,
+                                IXpsOMPath** path) {
+    SkTScopedComPtr<IXpsOMGeometry> geometry;
+    HRM(this->fXpsFactory->CreateGeometry(&geometry),
+        "Could not create geometry.");
+
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> figureCollection;
+    HRM(geometry->GetFigures(&figureCollection), "Could not get figures.");
+    HRM(figureCollection->Append(figure), "Could not add figure.");
+
+    HRM(this->fXpsFactory->CreatePath(path), "Could not create path.");
+    HRM((*path)->SetGeometryLocal(geometry.get()), "Could not set geometry");
+
+    HRM(visuals->Append(*path), "Could not add path to visuals.");
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::createXpsSolidColorBrush(const SkColor skColor,
+                                              const SkAlpha alpha,
+                                              IXpsOMBrush** xpsBrush) {
+    XPS_COLOR xpsColor = xps_color(skColor);
+    SkTScopedComPtr<IXpsOMSolidColorBrush> solidBrush;
+    HRM(this->fXpsFactory->CreateSolidColorBrush(&xpsColor, NULL, &solidBrush),
+        "Could not create solid color brush.");
+    HRM(solidBrush->SetOpacity(alpha / 255.0f), "Could not set opacity.");
+    HRM(solidBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI Fail.");
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::sideOfClamp(const SkRect& areaToFill,
+                                 const XPS_RECT& imageViewBox,
+                                 IXpsOMImageResource* image,
+                                 IXpsOMVisualCollection* visuals) {
+    SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
+    HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
+
+    SkTScopedComPtr<IXpsOMPath> areaToFillPath;
+    HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
+
+    SkTScopedComPtr<IXpsOMImageBrush> areaToFillBrush;
+    HRM(this->fXpsFactory->CreateImageBrush(image,
+                                            &imageViewBox,
+                                            &imageViewBox,
+                                            &areaToFillBrush),
+        "Could not create brush for side of clamp.");
+    HRM(areaToFillBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
+        "Could not set tile mode for side of clamp.");
+    HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
+        "Could not set brush for side of clamp");
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::cornerOfClamp(const SkRect& areaToFill,
+                                   const SkColor color,
+                                   IXpsOMVisualCollection* visuals) {
+    SkTScopedComPtr<IXpsOMGeometryFigure> areaToFillFigure;
+    HR(this->createXpsRect(areaToFill, FALSE, TRUE, &areaToFillFigure));
+
+    SkTScopedComPtr<IXpsOMPath> areaToFillPath;
+    HR(this->createPath(areaToFillFigure.get(), visuals, &areaToFillPath));
+
+    SkTScopedComPtr<IXpsOMBrush> areaToFillBrush;
+    HR(this->createXpsSolidColorBrush(color, 0xFF, &areaToFillBrush));
+    HRM(areaToFillPath->SetFillBrushLocal(areaToFillBrush.get()),
+        "Could not set brush for corner of clamp.");
+
+    return S_OK;
+}
+
+static const XPS_TILE_MODE XTM_N  = XPS_TILE_MODE_NONE;
+static const XPS_TILE_MODE XTM_T  = XPS_TILE_MODE_TILE;
+static const XPS_TILE_MODE XTM_X  = XPS_TILE_MODE_FLIPX;
+static const XPS_TILE_MODE XTM_Y  = XPS_TILE_MODE_FLIPY;
+static const XPS_TILE_MODE XTM_XY = XPS_TILE_MODE_FLIPXY;
+
+//TODO(bungeman): In the future, should skia add None,
+//handle None+Mirror and None+Repeat correctly.
+//None is currently an internal hack so masks don't repeat (None+None only).
+static XPS_TILE_MODE SkToXpsTileMode[SkShader::kTileModeCount+1]
+                                    [SkShader::kTileModeCount+1] = {
+           //Clamp //Repeat //Mirror //None
+/*Clamp */ XTM_N,  XTM_T,   XTM_Y,   XTM_N,
+/*Repeat*/ XTM_T,  XTM_T,   XTM_Y,   XTM_N,
+/*Mirror*/ XTM_X,  XTM_X,   XTM_XY,  XTM_X,
+/*None  */ XTM_N,  XTM_N,   XTM_Y,   XTM_N,
+};
+
+HRESULT SkXPSDevice::createXpsImageBrush(
+        const SkBitmap& bitmap,
+        const SkMatrix& localMatrix,
+        const SkShader::TileMode (&xy)[2],
+        const SkAlpha alpha,
+        IXpsOMTileBrush** xpsBrush) {
+    SkDynamicMemoryWStream write;
+    if (!SkImageEncoder::EncodeStream(&write, bitmap,
+                                      SkImageEncoder::kPNG_Type, 100)) {
+        HRM(E_FAIL, "Unable to encode bitmap as png.");
+    }
+    SkMemoryStream* read = new SkMemoryStream;
+    read->setData(write.copyToData())->unref();
+    SkTScopedComPtr<IStream> readWrapper;
+    HRM(SkIStream::CreateFromSkStream(read, true, &readWrapper),
+        "Could not create stream from png data.");
+
+    const size_t size =
+        SK_ARRAY_COUNT(L"/Documents/1/Resources/Images/" L_GUID_ID L".png");
+    wchar_t buffer[size];
+    wchar_t id[GUID_ID_LEN];
+    HR(create_id(id, GUID_ID_LEN));
+    swprintf_s(buffer, size, L"/Documents/1/Resources/Images/%s.png", id);
+
+    SkTScopedComPtr<IOpcPartUri> imagePartUri;
+    HRM(this->fXpsFactory->CreatePartUri(buffer, &imagePartUri),
+        "Could not create image part uri.");
+
+    SkTScopedComPtr<IXpsOMImageResource> imageResource;
+    HRM(this->fXpsFactory->CreateImageResource(
+            readWrapper.get(),
+            XPS_IMAGE_TYPE_PNG,
+            imagePartUri.get(),
+            &imageResource),
+        "Could not create image resource.");
+
+    XPS_RECT bitmapRect = {
+        0.0, 0.0,
+        static_cast<FLOAT>(bitmap.width()), static_cast<FLOAT>(bitmap.height())
+    };
+    SkTScopedComPtr<IXpsOMImageBrush> xpsImageBrush;
+    HRM(this->fXpsFactory->CreateImageBrush(imageResource.get(),
+                                            &bitmapRect, &bitmapRect,
+                                            &xpsImageBrush),
+        "Could not create image brush.");
+
+    if (SkShader::kClamp_TileMode != xy[0] &&
+        SkShader::kClamp_TileMode != xy[1]) {
+
+        HRM(xpsImageBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]),
+            "Could not set image tile mode");
+        HRM(xpsImageBrush->SetOpacity(alpha / 255.0f),
+            "Could not set image opacity.");
+        HRM(xpsImageBrush->QueryInterface(xpsBrush), "QI failed.");
+    } else {
+        //TODO(bungeman): compute how big this really needs to be.
+        const SkScalar BIG = SkIntToScalar(1000); //SK_ScalarMax;
+        const FLOAT BIG_F = SkScalarToFLOAT(BIG);
+        const SkScalar bWidth = SkIntToScalar(bitmap.width());
+        const SkScalar bHeight = SkIntToScalar(bitmap.height());
+
+        //create brush canvas
+        SkTScopedComPtr<IXpsOMCanvas> brushCanvas;
+        HRM(this->fXpsFactory->CreateCanvas(&brushCanvas),
+            "Could not create image brush canvas.");
+        SkTScopedComPtr<IXpsOMVisualCollection> brushVisuals;
+        HRM(brushCanvas->GetVisuals(&brushVisuals),
+            "Could not get image brush canvas visuals collection.");
+
+        //create central figure
+        const SkRect bitmapPoints = SkRect::MakeLTRB(0, 0, bWidth, bHeight);
+        SkTScopedComPtr<IXpsOMGeometryFigure> centralFigure;
+        HR(this->createXpsRect(bitmapPoints, FALSE, TRUE, &centralFigure));
+
+        SkTScopedComPtr<IXpsOMPath> centralPath;
+        HR(this->createPath(centralFigure.get(),
+                            brushVisuals.get(),
+                            &centralPath));
+        HRM(xpsImageBrush->SetTileMode(XPS_TILE_MODE_FLIPXY),
+            "Could not set tile mode for image brush central path.");
+        HRM(centralPath->SetFillBrushLocal(xpsImageBrush.get()),
+            "Could not set fill brush for image brush central path.");
+
+        //add left/right
+        if (SkShader::kClamp_TileMode == xy[0]) {
+            SkRect leftArea = SkRect::MakeLTRB(-BIG, 0, 0, bHeight);
+            XPS_RECT leftImageViewBox = {
+                0.0, 0.0,
+                1.0, static_cast<FLOAT>(bitmap.height()),
+            };
+            HR(this->sideOfClamp(leftArea, leftImageViewBox,
+                                 imageResource.get(),
+                                 brushVisuals.get()));
+
+            SkRect rightArea = SkRect::MakeLTRB(bWidth, 0, BIG, bHeight);
+            XPS_RECT rightImageViewBox = {
+                bitmap.width() - 1.0f, 0.0f,
+                1.0f, static_cast<FLOAT>(bitmap.height()),
+            };
+            HR(this->sideOfClamp(rightArea, rightImageViewBox,
+                                 imageResource.get(),
+                                 brushVisuals.get()));
+        }
+
+        //add top/bottom
+        if (SkShader::kClamp_TileMode == xy[1]) {
+            SkRect topArea = SkRect::MakeLTRB(0, -BIG, bWidth, 0);
+            XPS_RECT topImageViewBox = {
+                0.0, 0.0,
+                static_cast<FLOAT>(bitmap.width()), 1.0,
+            };
+            HR(this->sideOfClamp(topArea, topImageViewBox,
+                                 imageResource.get(),
+                                 brushVisuals.get()));
+
+            SkRect bottomArea = SkRect::MakeLTRB(0, bHeight, bWidth, BIG);
+            XPS_RECT bottomImageViewBox = {
+                0.0f, bitmap.height() - 1.0f,
+                static_cast<FLOAT>(bitmap.width()), 1.0f,
+            };
+            HR(this->sideOfClamp(bottomArea, bottomImageViewBox,
+                                 imageResource.get(),
+                                 brushVisuals.get()));
+        }
+
+        //add tl, tr, bl, br
+        if (SkShader::kClamp_TileMode == xy[0] &&
+            SkShader::kClamp_TileMode == xy[1]) {
+
+            SkAutoLockPixels alp(bitmap);
+
+            const SkColor tlColor = bitmap.getColor(0,0);
+            const SkRect tlArea = SkRect::MakeLTRB(-BIG, -BIG, 0, 0);
+            HR(this->cornerOfClamp(tlArea, tlColor, brushVisuals.get()));
+
+            const SkColor trColor = bitmap.getColor(bitmap.width()-1,0);
+            const SkRect trArea = SkRect::MakeLTRB(bWidth, -BIG, BIG, 0);
+            HR(this->cornerOfClamp(trArea, trColor, brushVisuals.get()));
+
+            const SkColor brColor = bitmap.getColor(bitmap.width()-1,
+                                                    bitmap.height()-1);
+            const SkRect brArea = SkRect::MakeLTRB(bWidth, bHeight, BIG, BIG);
+            HR(this->cornerOfClamp(brArea, brColor, brushVisuals.get()));
+
+            const SkColor blColor = bitmap.getColor(0,bitmap.height()-1);
+            const SkRect blArea = SkRect::MakeLTRB(-BIG, bHeight, 0, BIG);
+            HR(this->cornerOfClamp(blArea, blColor, brushVisuals.get()));
+        }
+
+        //create visual brush from canvas
+        XPS_RECT bound = {};
+        if (SkShader::kClamp_TileMode == xy[0] &&
+            SkShader::kClamp_TileMode == xy[1]) {
+
+            bound.x = BIG_F / -2;
+            bound.y = BIG_F / -2;
+            bound.width = BIG_F;
+            bound.height = BIG_F;
+        } else if (SkShader::kClamp_TileMode == xy[0]) {
+            bound.x = BIG_F / -2;
+            bound.y = 0.0f;
+            bound.width = BIG_F;
+            bound.height = static_cast<FLOAT>(bitmap.height());
+        } else if (SkShader::kClamp_TileMode == xy[1]) {
+            bound.x = 0;
+            bound.y = BIG_F / -2;
+            bound.width = static_cast<FLOAT>(bitmap.width());
+            bound.height = BIG_F;
+        }
+        SkTScopedComPtr<IXpsOMVisualBrush> clampBrush;
+        HRM(this->fXpsFactory->CreateVisualBrush(&bound, &bound, &clampBrush),
+            "Could not create visual brush for image brush.");
+        HRM(clampBrush->SetVisualLocal(brushCanvas.get()),
+            "Could not set canvas on visual brush for image brush.");
+        HRM(clampBrush->SetTileMode(SkToXpsTileMode[xy[0]][xy[1]]),
+            "Could not set tile mode on visual brush for image brush.");
+        HRM(clampBrush->SetOpacity(alpha / 255.0f),
+            "Could not set opacity on visual brush for image brush.");
+
+        HRM(clampBrush->QueryInterface(xpsBrush), "QI failed.");
+    }
+
+    SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
+    HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
+    if (NULL != xpsMatrixToUse.get()) {
+        HRM((*xpsBrush)->SetTransformLocal(xpsMatrixToUse.get()),
+            "Could not set transform for image brush.");
+    } else {
+        //TODO(bungeman): perspective bitmaps in general.
+    }
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::createXpsGradientStop(const SkColor skColor,
+                                           const SkScalar offset,
+                                           IXpsOMGradientStop** xpsGradStop) {
+    XPS_COLOR gradStopXpsColor = xps_color(skColor);
+    HRM(this->fXpsFactory->CreateGradientStop(&gradStopXpsColor,
+                                              NULL,
+                                              SkScalarToFLOAT(offset),
+                                              xpsGradStop),
+        "Could not create gradient stop.");
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::createXpsLinearGradient(SkShader::GradientInfo info,
+                                             const SkAlpha alpha,
+                                             const SkMatrix& localMatrix,
+                                             IXpsOMMatrixTransform* xpsMatrix,
+                                             IXpsOMBrush** xpsBrush) {
+    XPS_POINT startPoint;
+    XPS_POINT endPoint;
+    if (NULL != xpsMatrix) {
+        startPoint = xps_point(info.fPoint[0]);
+        endPoint = xps_point(info.fPoint[1]);
+    } else {
+        transform_offsets(info.fColorOffsets, info.fColorCount,
+                          info.fPoint[0], info.fPoint[1],
+                          localMatrix);
+        startPoint = xps_point(info.fPoint[0], localMatrix);
+        endPoint = xps_point(info.fPoint[1], localMatrix);
+    }
+
+    SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
+    HR(createXpsGradientStop(info.fColors[0],
+                             info.fColorOffsets[0],
+                             &gradStop0));
+
+    SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
+    HR(createXpsGradientStop(info.fColors[1],
+                             info.fColorOffsets[1],
+                             &gradStop1));
+
+    SkTScopedComPtr<IXpsOMLinearGradientBrush> gradientBrush;
+    HRM(this->fXpsFactory->CreateLinearGradientBrush(gradStop0.get(),
+                                                     gradStop1.get(),
+                                                     &startPoint,
+                                                     &endPoint,
+                                                     &gradientBrush),
+        "Could not create linear gradient brush.");
+    if (NULL != xpsMatrix) {
+        HRM(gradientBrush->SetTransformLocal(xpsMatrix),
+            "Could not set transform on linear gradient brush.");
+    }
+
+    SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
+    HRM(gradientBrush->GetGradientStops(&gradStopCollection),
+        "Could not get linear gradient stop collection.");
+    for (int i = 2; i < info.fColorCount; ++i) {
+        SkTScopedComPtr<IXpsOMGradientStop> gradStop;
+        HR(createXpsGradientStop(info.fColors[i],
+                                 info.fColorOffsets[i],
+                                 &gradStop));
+        HRM(gradStopCollection->Append(gradStop.get()),
+            "Could not add linear gradient stop.");
+    }
+
+    HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)),
+        "Could not set spread method of linear gradient.");
+
+    HRM(gradientBrush->SetOpacity(alpha / 255.0f),
+        "Could not set opacity of linear gradient brush.");
+    HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed");
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::createXpsRadialGradient(SkShader::GradientInfo info,
+                                             const SkAlpha alpha,
+                                             const SkMatrix& localMatrix,
+                                             IXpsOMMatrixTransform* xpsMatrix,
+                                             IXpsOMBrush** xpsBrush) {
+    SkTScopedComPtr<IXpsOMGradientStop> gradStop0;
+    HR(createXpsGradientStop(info.fColors[0],
+                             info.fColorOffsets[0],
+                             &gradStop0));
+
+    SkTScopedComPtr<IXpsOMGradientStop> gradStop1;
+    HR(createXpsGradientStop(info.fColors[1],
+                             info.fColorOffsets[1],
+                             &gradStop1));
+
+    //TODO: figure out how to fake better if not affine
+    XPS_POINT centerPoint;
+    XPS_POINT gradientOrigin;
+    XPS_SIZE radiiSizes;
+    if (NULL != xpsMatrix) {
+        centerPoint = xps_point(info.fPoint[0]);
+        gradientOrigin = xps_point(info.fPoint[0]);
+        radiiSizes.width = SkScalarToFLOAT(info.fRadius[0]);
+        radiiSizes.height = SkScalarToFLOAT(info.fRadius[0]);
+    } else {
+        centerPoint = xps_point(info.fPoint[0], localMatrix);
+        gradientOrigin = xps_point(info.fPoint[0], localMatrix);
+
+        SkScalar radius = info.fRadius[0];
+        SkVector vec[2];
+
+        vec[0].set(radius, 0);
+        vec[1].set(0, radius);
+        localMatrix.mapVectors(vec, 2);
+
+        SkScalar d0 = vec[0].length();
+        SkScalar d1 = vec[1].length();
+
+        radiiSizes.width = SkScalarToFLOAT(d0);
+        radiiSizes.height = SkScalarToFLOAT(d1);
+    }
+
+    SkTScopedComPtr<IXpsOMRadialGradientBrush> gradientBrush;
+    HRM(this->fXpsFactory->CreateRadialGradientBrush(gradStop0.get(),
+                                                     gradStop1.get(),
+                                                     &centerPoint,
+                                                     &gradientOrigin,
+                                                     &radiiSizes,
+                                                     &gradientBrush),
+        "Could not create radial gradient brush.");
+    if (NULL != xpsMatrix) {
+        HRM(gradientBrush->SetTransformLocal(xpsMatrix),
+            "Could not set transform on radial gradient brush.");
+    }
+
+    SkTScopedComPtr<IXpsOMGradientStopCollection> gradStopCollection;
+    HRM(gradientBrush->GetGradientStops(&gradStopCollection),
+        "Could not get radial gradient stop collection.");
+    for (int i = 2; i < info.fColorCount; ++i) {
+        SkTScopedComPtr<IXpsOMGradientStop> gradStop;
+        HR(createXpsGradientStop(info.fColors[i],
+                                 info.fColorOffsets[i],
+                                 &gradStop));
+        HRM(gradStopCollection->Append(gradStop.get()),
+            "Could not add radial gradient stop.");
+    }
+
+    HRM(gradientBrush->SetSpreadMethod(xps_spread_method(info.fTileMode)),
+        "Could not set spread method of radial gradient.");
+
+    HRM(gradientBrush->SetOpacity(alpha / 255.0f),
+        "Could not set opacity of radial gradient brush.");
+    HRM(gradientBrush->QueryInterface<IXpsOMBrush>(xpsBrush), "QI failed.");
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::createXpsBrush(const SkPaint& skPaint,
+                                    IXpsOMBrush** brush,
+                                    const SkMatrix* parentTransform) {
+    const SkShader *shader = skPaint.getShader();
+    if (NULL == shader) {
+        HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
+        return S_OK;
+    }
+
+    //Gradient shaders.
+    SkShader::GradientInfo info;
+    info.fColorCount = 0;
+    info.fColors = NULL;
+    info.fColorOffsets = NULL;
+    SkShader::GradientType gradientType = shader->asAGradient(&info);
+
+    if (SkShader::kNone_GradientType == gradientType) {
+        //Nothing to see, move along.
+
+    } else if (SkShader::kColor_GradientType == gradientType) {
+        SkASSERT(1 == info.fColorCount);
+        SkColor color;
+        info.fColors = &color;
+        SkShader::GradientType gradientType = shader->asAGradient(&info);
+        SkAlpha alpha = skPaint.getAlpha();
+        HR(this->createXpsSolidColorBrush(color, alpha, brush));
+        return S_OK;
+
+    } else {
+        if (info.fColorCount == 0) {
+            const SkColor color = skPaint.getColor();
+            HR(this->createXpsSolidColorBrush(color, 0xFF, brush));
+            return S_OK;
+        }
+
+        SkAutoTArray<SkColor> colors(info.fColorCount);
+        SkAutoTArray<SkScalar> colorOffsets(info.fColorCount);
+        info.fColors = colors.get();
+        info.fColorOffsets = colorOffsets.get();
+        shader->asAGradient(&info);
+
+        if (1 == info.fColorCount) {
+            SkColor color = info.fColors[0];
+            SkAlpha alpha = skPaint.getAlpha();
+            HR(this->createXpsSolidColorBrush(color, alpha, brush));
+            return S_OK;
+        }
+
+        SkMatrix localMatrix = shader->getLocalMatrix();
+        if (NULL != parentTransform) {
+            localMatrix.preConcat(*parentTransform);
+        }
+        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
+        HR(this->createXpsTransform(localMatrix, &xpsMatrixToUse));
+
+        if (SkShader::kLinear_GradientType == gradientType) {
+            HR(this->createXpsLinearGradient(info,
+                                             skPaint.getAlpha(),
+                                             localMatrix,
+                                             xpsMatrixToUse.get(),
+                                             brush));
+            return S_OK;
+        }
+
+        if (SkShader::kRadial_GradientType == gradientType) {
+            HR(this->createXpsRadialGradient(info,
+                                             skPaint.getAlpha(),
+                                             localMatrix,
+                                             xpsMatrixToUse.get(),
+                                             brush));
+            return S_OK;
+        }
+
+        if (SkShader::kRadial2_GradientType == gradientType ||
+            SkShader::kConical_GradientType == gradientType) {
+            //simple if affine and one is 0, otherwise will have to fake
+        }
+
+        if (SkShader::kSweep_GradientType == gradientType) {
+            //have to fake
+        }
+    }
+
+    SkBitmap outTexture;
+    SkMatrix outMatrix;
+    SkShader::TileMode xy[2];
+    SkShader::BitmapType bitmapType = shader->asABitmap(&outTexture,
+                                                        &outMatrix,
+                                                        xy);
+    switch (bitmapType) {
+        case SkShader::kNone_BitmapType:
+            break;
+        case SkShader::kDefault_BitmapType: {
+            //TODO: outMatrix??
+            SkMatrix localMatrix = shader->getLocalMatrix();
+            if (NULL != parentTransform) {
+                localMatrix.preConcat(*parentTransform);
+            }
+
+            SkTScopedComPtr<IXpsOMTileBrush> tileBrush;
+            HR(this->createXpsImageBrush(outTexture,
+                                         localMatrix,
+                                         xy,
+                                         skPaint.getAlpha(),
+                                         &tileBrush));
+
+            HRM(tileBrush->QueryInterface<IXpsOMBrush>(brush), "QI failed.");
+
+            return S_OK;
+        }
+        case SkShader::kRadial_BitmapType:
+        case SkShader::kSweep_BitmapType:
+        case SkShader::kTwoPointRadial_BitmapType:
+        default:
+            break;
+    }
+
+    HR(this->createXpsSolidColorBrush(skPaint.getColor(), 0xFF, brush));
+    return S_OK;
+}
+
+static bool rect_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
+    const bool zeroWidth = (0 == paint.getStrokeWidth());
+    const bool stroke = (SkPaint::kFill_Style != paint.getStyle());
+
+    return paint.getPathEffect() ||
+           paint.getMaskFilter() ||
+           paint.getRasterizer() ||
+           (stroke && (
+               (matrix.hasPerspective() && !zeroWidth) ||
+               SkPaint::kMiter_Join != paint.getStrokeJoin() ||
+               (SkPaint::kMiter_Join == paint.getStrokeJoin() &&
+                paint.getStrokeMiter() < SK_ScalarSqrt2)
+           ))
+    ;
+}
+
+HRESULT SkXPSDevice::createXpsRect(const SkRect& rect, BOOL stroke, BOOL fill,
+                                   IXpsOMGeometryFigure** xpsRect) {
+    const SkPoint points[4] = {
+        { rect.fLeft, rect.fTop },
+        { rect.fRight, rect.fTop },
+        { rect.fRight, rect.fBottom },
+        { rect.fLeft, rect.fBottom },
+    };
+    return this->createXpsQuad(points, stroke, fill, xpsRect);
+}
+HRESULT SkXPSDevice::createXpsQuad(const SkPoint (&points)[4],
+                                   BOOL stroke, BOOL fill,
+                                   IXpsOMGeometryFigure** xpsQuad) {
+    // Define the start point.
+    XPS_POINT startPoint = xps_point(points[0]);
+
+    // Create the figure.
+    HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint, xpsQuad),
+        "Could not create quad geometry figure.");
+
+    // Define the type of each segment.
+    XPS_SEGMENT_TYPE segmentTypes[3] = {
+        XPS_SEGMENT_TYPE_LINE,
+        XPS_SEGMENT_TYPE_LINE,
+        XPS_SEGMENT_TYPE_LINE,
+    };
+
+    // Define the x and y coordinates of each corner of the figure.
+    FLOAT segmentData[6] = {
+        SkScalarToFLOAT(points[1].fX), SkScalarToFLOAT(points[1].fY),
+        SkScalarToFLOAT(points[2].fX), SkScalarToFLOAT(points[2].fY),
+        SkScalarToFLOAT(points[3].fX), SkScalarToFLOAT(points[3].fY),
+    };
+
+    // Describe if the segments are stroked.
+    BOOL segmentStrokes[3] = {
+        stroke, stroke, stroke,
+    };
+
+    // Add the segment data to the figure.
+    HRM((*xpsQuad)->SetSegments(
+            3, 6,
+            segmentTypes , segmentData, segmentStrokes),
+        "Could not add segment data to quad.");
+
+    // Set the closed and filled properties of the figure.
+    HRM((*xpsQuad)->SetIsClosed(stroke), "Could not set quad close.");
+    HRM((*xpsQuad)->SetIsFilled(fill), "Could not set quad fill.");
+
+    return S_OK;
+}
+
+uint32_t SkXPSDevice::getDeviceCapabilities() {
+    return kVector_Capability;
+}
+
+void SkXPSDevice::clear(SkColor color) {
+    //TODO: override this for XPS
+    SkDEBUGF(("XPS clear not yet implemented."));
+}
+
+void SkXPSDevice::drawPoints(const SkDraw& d, SkCanvas::PointMode mode,
+                             size_t count, const SkPoint points[],
+                             const SkPaint& paint) {
+    //This will call back into the device to do the drawing.
+    d.drawPoints(mode, count, points, paint, true);
+}
+
+void SkXPSDevice::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) {
+    //TODO: override this for XPS
+    SkDEBUGF(("XPS drawVertices not yet implemented."));
+}
+
+void SkXPSDevice::drawPaint(const SkDraw& d, const SkPaint& origPaint) {
+    const SkRect r = SkRect::MakeSize(this->fCurrentCanvasSize);
+
+    //If trying to paint with a stroke, ignore that and fill.
+    SkPaint* fillPaint = const_cast<SkPaint*>(&origPaint);
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+    if (paint->getStyle() != SkPaint::kFill_Style) {
+        paint.writable()->setStyle(SkPaint::kFill_Style);
+    }
+
+    this->internalDrawRect(d, r, false, *fillPaint);
+}
+
+void SkXPSDevice::drawRect(const SkDraw& d,
+                           const SkRect& r,
+                           const SkPaint& paint) {
+    this->internalDrawRect(d, r, true, paint);
+}
+
+void SkXPSDevice::internalDrawRect(const SkDraw& d,
+                                   const SkRect& r,
+                                   bool transformRect,
+                                   const SkPaint& paint) {
+    //Exit early if there is nothing to draw.
+    if (d.fClip->isEmpty() ||
+        (paint.getAlpha() == 0 && paint.getXfermode() == NULL)) {
+        return;
+    }
+
+    //Path the rect if we can't optimize it.
+    if (rect_must_be_pathed(paint, *d.fMatrix)) {
+        SkPath tmp;
+        tmp.addRect(r);
+        tmp.setFillType(SkPath::kWinding_FillType);
+        this->drawPath(d, tmp, paint, NULL, true);
+        return;
+    }
+
+    //Create the shaded path.
+    SkTScopedComPtr<IXpsOMPath> shadedPath;
+    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
+         "Could not create shaded path for rect.");
+
+    //Create the shaded geometry.
+    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
+    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
+         "Could not create shaded geometry for rect.");
+
+    //Add the geometry to the shaded path.
+    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
+         "Could not set shaded geometry for rect.");
+
+    //Set the brushes.
+    BOOL fill = FALSE;
+    BOOL stroke = FALSE;
+    HRV(this->shadePath(shadedPath.get(), paint, *d.fMatrix, &fill, &stroke));
+
+    bool xpsTransformsPath = true;
+    //Transform the geometry.
+    if (transformRect && xpsTransformsPath) {
+        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
+        HRV(this->createXpsTransform(*d.fMatrix, &xpsTransform));
+        if (xpsTransform.get()) {
+            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
+                 "Could not set transform for rect.");
+        } else {
+            xpsTransformsPath = false;
+        }
+    }
+
+    //Create the figure.
+    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
+    {
+        SkPoint points[4] = {
+            { r.fLeft, r.fTop },
+            { r.fLeft, r.fBottom },
+            { r.fRight, r.fBottom },
+            { r.fRight, r.fTop },
+        };
+        if (!xpsTransformsPath && transformRect) {
+            d.fMatrix->mapPoints(points, SK_ARRAY_COUNT(points));
+        }
+        HRV(this->createXpsQuad(points, stroke, fill, &rectFigure));
+    }
+
+    //Get the figures of the shaded geometry.
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
+    HRVM(shadedGeometry->GetFigures(&shadedFigures),
+         "Could not get shaded figures for rect.");
+
+    //Add the figure to the shaded geometry figures.
+    HRVM(shadedFigures->Append(rectFigure.get()),
+         "Could not add shaded figure for rect.");
+
+    HRV(this->clip(shadedPath.get(), d));
+
+    //Add the shaded path to the current visuals.
+    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
+    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
+         "Could not get current visuals for rect.");
+    HRVM(currentVisuals->Append(shadedPath.get()),
+         "Could not add rect to current visuals.");
+}
+
+static HRESULT close_figure(const SkTDArray<XPS_SEGMENT_TYPE>& segmentTypes,
+                            const SkTDArray<BOOL>& segmentStrokes,
+                            const SkTDArray<FLOAT>& segmentData,
+                            BOOL stroke, BOOL fill,
+                            IXpsOMGeometryFigure* figure,
+                            IXpsOMGeometryFigureCollection* figures) {
+    // Add the segment data to the figure.
+    HRM(figure->SetSegments(segmentTypes.count(), segmentData.count(),
+                            segmentTypes.begin() , segmentData.begin(),
+                            segmentStrokes.begin()),
+        "Could not set path segments.");
+
+    // Set the closed and filled properties of the figure.
+    HRM(figure->SetIsClosed(stroke), "Could not set path closed.");
+    HRM(figure->SetIsFilled(fill), "Could not set path fill.");
+
+    // Add the figure created above to this geometry.
+    HRM(figures->Append(figure), "Could not add path to geometry.");
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::addXpsPathGeometry(
+        IXpsOMGeometryFigureCollection* xpsFigures,
+        BOOL stroke, BOOL fill, const SkPath& path) {
+    SkTDArray<XPS_SEGMENT_TYPE> segmentTypes;
+    SkTDArray<BOOL> segmentStrokes;
+    SkTDArray<FLOAT> segmentData;
+
+    SkTScopedComPtr<IXpsOMGeometryFigure> xpsFigure;
+    SkPath::Iter iter(path, true);
+    SkPoint points[4];
+    SkPath::Verb verb;
+    while ((verb = iter.next(points)) != SkPath::kDone_Verb) {
+        switch (verb) {
+            case SkPath::kMove_Verb: {
+                if (NULL != xpsFigure.get()) {
+                    HR(close_figure(segmentTypes, segmentStrokes, segmentData,
+                                    stroke, fill,
+                                    xpsFigure.get() , xpsFigures));
+                    xpsFigure.reset();
+                    segmentTypes.rewind();
+                    segmentStrokes.rewind();
+                    segmentData.rewind();
+                }
+                // Define the start point.
+                XPS_POINT startPoint = xps_point(points[0]);
+                // Create the figure.
+                HRM(this->fXpsFactory->CreateGeometryFigure(&startPoint,
+                                                            &xpsFigure),
+                    "Could not create path geometry figure.");
+                break;
+            }
+            case SkPath::kLine_Verb:
+                if (iter.isCloseLine()) break; //ignore the line, auto-closed
+                segmentTypes.push(XPS_SEGMENT_TYPE_LINE);
+                segmentStrokes.push(stroke);
+                segmentData.push(SkScalarToFLOAT(points[1].fX));
+                segmentData.push(SkScalarToFLOAT(points[1].fY));
+                break;
+            case SkPath::kQuad_Verb:
+                segmentTypes.push(XPS_SEGMENT_TYPE_QUADRATIC_BEZIER);
+                segmentStrokes.push(stroke);
+                segmentData.push(SkScalarToFLOAT(points[1].fX));
+                segmentData.push(SkScalarToFLOAT(points[1].fY));
+                segmentData.push(SkScalarToFLOAT(points[2].fX));
+                segmentData.push(SkScalarToFLOAT(points[2].fY));
+                break;
+            case SkPath::kCubic_Verb:
+                segmentTypes.push(XPS_SEGMENT_TYPE_BEZIER);
+                segmentStrokes.push(stroke);
+                segmentData.push(SkScalarToFLOAT(points[1].fX));
+                segmentData.push(SkScalarToFLOAT(points[1].fY));
+                segmentData.push(SkScalarToFLOAT(points[2].fX));
+                segmentData.push(SkScalarToFLOAT(points[2].fY));
+                segmentData.push(SkScalarToFLOAT(points[3].fX));
+                segmentData.push(SkScalarToFLOAT(points[3].fY));
+                break;
+            case SkPath::kClose_Verb:
+                // we ignore these, and just get the whole segment from
+                // the corresponding line/quad/cubic verbs
+                break;
+            default:
+                SkASSERT(!"unexpected verb");
+                break;
+        }
+    }
+    if (NULL != xpsFigure.get()) {
+        HR(close_figure(segmentTypes, segmentStrokes, segmentData,
+                        stroke, fill,
+                        xpsFigure.get(), xpsFigures));
+    }
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::drawInverseWindingPath(const SkDraw& d,
+                                            const SkPath& devicePath,
+                                            IXpsOMPath* shadedPath) {
+    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
+        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
+
+    const XPS_RECT universeRectXps = {
+        0.0f, 0.0f,
+        SkScalarToFLOAT(this->fCurrentCanvasSize.fWidth),
+        SkScalarToFLOAT(this->fCurrentCanvasSize.fHeight),
+    };
+
+    //Get the geometry.
+    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
+    HRM(shadedPath->GetGeometry(&shadedGeometry),
+        "Could not get shaded geometry for inverse path.");
+
+    //Get the figures from the geometry.
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
+    HRM(shadedGeometry->GetFigures(&shadedFigures),
+        "Could not get shaded figures for inverse path.");
+
+    HRM(shadedGeometry->SetFillRule(XPS_FILL_RULE_NONZERO),
+        "Could not set shaded fill rule for inverse path.");
+
+    //Take everything drawn so far, and make a shared resource out of it.
+    //Replace everything drawn so far with
+    //inverse canvas
+    //  old canvas of everything so far
+    //  world shaded figure, clipped to current clip
+    //  top canvas of everything so far, clipped to path
+    //Note: this is not quite right when there is nothing solid in the
+    //canvas of everything so far, as the bit on top will allow
+    //the world paint to show through.
+
+    //Create new canvas.
+    SkTScopedComPtr<IXpsOMCanvas> newCanvas;
+    HRM(this->fXpsFactory->CreateCanvas(&newCanvas),
+        "Could not create inverse canvas.");
+
+    //Save the old canvas to a dictionary on the new canvas.
+    SkTScopedComPtr<IXpsOMDictionary> newDictionary;
+    HRM(this->fXpsFactory->CreateDictionary(&newDictionary),
+        "Could not create inverse dictionary.");
+    HRM(newCanvas->SetDictionaryLocal(newDictionary.get()),
+        "Could not set inverse dictionary.");
+
+    const size_t size = SK_ARRAY_COUNT(L"ID" L_GUID_ID);
+    wchar_t buffer[size];
+    wchar_t id[GUID_ID_LEN];
+    HR(create_id(id, GUID_ID_LEN, '_'));
+    swprintf_s(buffer, size, L"ID%s", id);
+    HRM(newDictionary->Append(buffer, this->fCurrentXpsCanvas.get()),
+        "Could not add canvas to inverse dictionary.");
+
+    //Start drawing
+    SkTScopedComPtr<IXpsOMVisualCollection> newVisuals;
+    HRM(newCanvas->GetVisuals(&newVisuals),
+        "Could not get inverse canvas visuals.");
+
+    //Draw old canvas from dictionary onto new canvas.
+    SkTScopedComPtr<IXpsOMGeometry> oldGeometry;
+    HRM(this->fXpsFactory->CreateGeometry(&oldGeometry),
+        "Could not create old inverse geometry.");
+
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> oldFigures;
+    HRM(oldGeometry->GetFigures(&oldFigures),
+        "Could not get old inverse figures.");
+
+    SkTScopedComPtr<IXpsOMGeometryFigure> oldFigure;
+    HR(this->createXpsRect(universeRect, FALSE, TRUE, &oldFigure));
+    HRM(oldFigures->Append(oldFigure.get()),
+        "Could not add old inverse figure.");
+
+    SkTScopedComPtr<IXpsOMVisualBrush> oldBrush;
+    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
+                                             &universeRectXps,
+                                             &oldBrush),
+        "Could not create old inverse brush.");
+
+    SkTScopedComPtr<IXpsOMPath> oldPath;
+    HRM(this->fXpsFactory->CreatePath(&oldPath),
+        "Could not create old inverse path.");
+    HRM(oldPath->SetGeometryLocal(oldGeometry.get()),
+        "Could not set old inverse geometry.");
+    HRM(oldPath->SetFillBrushLocal(oldBrush.get()),
+        "Could not set old inverse fill brush.");
+    //the brush must be parented before setting the lookup.
+    HRM(newVisuals->Append(oldPath.get()),
+        "Could not add old inverse path to new canvas visuals.");
+    HRM(oldBrush->SetVisualLookup(buffer),
+        "Could not set old inverse brush visual lookup.");
+
+    //Draw the clip filling shader.
+    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
+    HR(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure));
+    HRM(shadedFigures->Append(shadedFigure.get()),
+        "Could not add inverse shaded figure.");
+    //the geometry is already set
+    HR(this->clip(shadedPath, d));
+    HRM(newVisuals->Append(shadedPath),
+        "Could not add inverse shaded path to canvas visuals.");
+
+    //Draw the old canvas on top, clipped to the original path.
+    SkTScopedComPtr<IXpsOMCanvas> topCanvas;
+    HRM(this->fXpsFactory->CreateCanvas(&topCanvas),
+        "Could not create top inverse canvas.");
+    //Clip the canvas to prevent alpha spill.
+    //This is the entire reason this canvas exists.
+    HR(this->clip(topCanvas.get(), d));
+
+    SkTScopedComPtr<IXpsOMGeometry> topGeometry;
+    HRM(this->fXpsFactory->CreateGeometry(&topGeometry),
+        "Could not create top inverse geometry.");
+
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> topFigures;
+    HRM(topGeometry->GetFigures(&topFigures),
+        "Could not get top inverse figures.");
+
+    SkTScopedComPtr<IXpsOMGeometryFigure> topFigure;
+    HR(this->createXpsRect(universeRect, FALSE, TRUE, &topFigure));
+    HRM(topFigures->Append(topFigure.get()),
+        "Could not add old inverse figure.");
+
+    SkTScopedComPtr<IXpsOMVisualBrush> topBrush;
+    HRM(this->fXpsFactory->CreateVisualBrush(&universeRectXps,
+                                             &universeRectXps,
+                                             &topBrush),
+        "Could not create top inverse brush.");
+
+    SkTScopedComPtr<IXpsOMPath> topPath;
+    HRM(this->fXpsFactory->CreatePath(&topPath),
+        "Could not create top inverse path.");
+    HRM(topPath->SetGeometryLocal(topGeometry.get()),
+        "Could not set top inverse geometry.");
+    HRM(topPath->SetFillBrushLocal(topBrush.get()),
+        "Could not set top inverse fill brush.");
+    //the brush must be parented before setting the lookup.
+    HRM(newVisuals->Append(topCanvas.get()),
+        "Could not add top canvas to inverse canvas visuals.");
+    SkTScopedComPtr<IXpsOMVisualCollection> topVisuals;
+    HRM(topCanvas->GetVisuals(&topVisuals),
+        "Could not get top inverse canvas visuals.");
+    HRM(topVisuals->Append(topPath.get()),
+        "Could not add top inverse path to top canvas visuals.");
+    HRM(topBrush->SetVisualLookup(buffer),
+        "Could not set top inverse brush visual lookup.");
+
+    HR(this->clipToPath(topPath.get(), devicePath, XPS_FILL_RULE_NONZERO));
+
+    //swap current canvas to new canvas
+    this->fCurrentXpsCanvas.swap(newCanvas);
+
+    return S_OK;
+}
+
+void SkXPSDevice::convertToPpm(const SkMaskFilter* filter,
+                               SkMatrix* matrix,
+                               SkVector* ppuScale,
+                               const SkIRect& clip, SkIRect* clipIRect) {
+    //TODO: currently ignoring the ppm if blur ignoring transform.
+    if (filter) {
+        SkMaskFilter::BlurInfo blurInfo;
+        SkMaskFilter::BlurType blurType = filter->asABlur(&blurInfo);
+
+        if (SkMaskFilter::kNone_BlurType != blurType
+            && blurInfo.fIgnoreTransform) {
+
+            ppuScale->fX = SK_Scalar1;
+            ppuScale->fY = SK_Scalar1;
+            *clipIRect = clip;
+            return;
+        }
+    }
+
+    //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);
+
+    matrix->postScale(ppuScale->fX, ppuScale->fY);
+
+    const SkIRect& irect = clip;
+    SkRect clipRect = SkRect::MakeLTRB(
+        SkScalarMul(SkIntToScalar(irect.fLeft), ppuScale->fX),
+        SkScalarMul(SkIntToScalar(irect.fTop), ppuScale->fY),
+        SkScalarMul(SkIntToScalar(irect.fRight), ppuScale->fX),
+        SkScalarMul(SkIntToScalar(irect.fBottom), ppuScale->fY));
+    clipRect.roundOut(clipIRect);
+}
+
+HRESULT SkXPSDevice::applyMask(const SkDraw& d,
+                               const SkMask& mask,
+                               const SkVector& ppuScale,
+                               IXpsOMPath* shadedPath) {
+    //Get the geometry object.
+    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
+    HRM(shadedPath->GetGeometry(&shadedGeometry),
+        "Could not get mask shaded geometry.");
+
+    //Get the figures from the geometry.
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
+    HRM(shadedGeometry->GetFigures(&shadedFigures),
+        "Could not get mask shaded figures.");
+
+    SkMatrix m;
+    m.reset();
+    m.setTranslate(SkIntToScalar(mask.fBounds.fLeft),
+                   SkIntToScalar(mask.fBounds.fTop));
+    m.postScale(SkScalarInvert(ppuScale.fX), SkScalarInvert(ppuScale.fY));
+
+    SkShader::TileMode xy[2];
+    xy[0] = (SkShader::TileMode)3;
+    xy[1] = (SkShader::TileMode)3;
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kA8_Config,
+                 mask.fBounds.width(),
+                 mask.fBounds.height(),
+                 mask.fRowBytes);
+    bm.setPixels(mask.fImage);
+
+    SkTScopedComPtr<IXpsOMTileBrush> maskBrush;
+    HR(this->createXpsImageBrush(bm, m, xy, 0xFF, &maskBrush));
+    HRM(shadedPath->SetOpacityMaskBrushLocal(maskBrush.get()),
+        "Could not set mask.");
+
+    const SkRect universeRect = SkRect::MakeLTRB(0, 0,
+        this->fCurrentCanvasSize.fWidth, this->fCurrentCanvasSize.fHeight);
+    SkTScopedComPtr<IXpsOMGeometryFigure> shadedFigure;
+    HRM(this->createXpsRect(universeRect, FALSE, TRUE, &shadedFigure),
+        "Could not create mask shaded figure.");
+    HRM(shadedFigures->Append(shadedFigure.get()),
+        "Could not add mask shaded figure.");
+
+    HR(this->clip(shadedPath, d));
+
+    //Add the path to the active visual collection.
+    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
+    HRM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
+        "Could not get mask current visuals.");
+    HRM(currentVisuals->Append(shadedPath),
+        "Could not add masked shaded path to current visuals.");
+
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::shadePath(IXpsOMPath* shadedPath,
+                               const SkPaint& shaderPaint,
+                               const SkMatrix& matrix,
+                               BOOL* fill, BOOL* stroke) {
+    *fill = FALSE;
+    *stroke = FALSE;
+
+    const SkPaint::Style style = shaderPaint.getStyle();
+    const bool hasFill = SkPaint::kFill_Style == style
+                      || SkPaint::kStrokeAndFill_Style == style;
+    const bool hasStroke = SkPaint::kStroke_Style == style
+                        || SkPaint::kStrokeAndFill_Style == style;
+
+    //TODO(bungeman): use dictionaries and lookups.
+    if (hasFill) {
+        *fill = TRUE;
+        SkTScopedComPtr<IXpsOMBrush> fillBrush;
+        HR(this->createXpsBrush(shaderPaint, &fillBrush, &matrix));
+        HRM(shadedPath->SetFillBrushLocal(fillBrush.get()),
+            "Could not set fill for shaded path.");
+    }
+
+    if (hasStroke) {
+        *stroke = TRUE;
+        SkTScopedComPtr<IXpsOMBrush> strokeBrush;
+        HR(this->createXpsBrush(shaderPaint, &strokeBrush, &matrix));
+        HRM(shadedPath->SetStrokeBrushLocal(strokeBrush.get()),
+            "Could not set stroke brush for shaded path.");
+        HRM(shadedPath->SetStrokeThickness(
+                SkScalarToFLOAT(shaderPaint.getStrokeWidth())),
+            "Could not set shaded path stroke thickness.");
+
+        if (0 == shaderPaint.getStrokeWidth()) {
+            //XPS hair width is a hack. (XPS Spec 11.6.12).
+            SkTScopedComPtr<IXpsOMDashCollection> dashes;
+            HRM(shadedPath->GetStrokeDashes(&dashes),
+                "Could not set dashes for shaded path.");
+            XPS_DASH dash;
+            dash.length = 1.0;
+            dash.gap = 0.0;
+            HRM(dashes->Append(&dash), "Could not add dashes to shaded path.");
+            HRM(shadedPath->SetStrokeDashOffset(-2.0),
+                "Could not set dash offset for shaded path.");
+        }
+    }
+    return S_OK;
+}
+
+void SkXPSDevice::drawPath(const SkDraw& d,
+                           const SkPath& platonicPath,
+                           const SkPaint& origPaint,
+                           const SkMatrix* prePathMatrix,
+                           bool pathIsMutable) {
+    SkTCopyOnFirstWrite<SkPaint> paint(origPaint);
+
+    // nothing to draw
+    if (d.fClip->isEmpty() ||
+        (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
+        return;
+    }
+
+    SkPath modifiedPath;
+    const bool paintHasPathEffect = paint->getPathEffect()
+                                 || paint->getStyle() != SkPaint::kFill_Style;
+
+    //Apply pre-path matrix [Platonic-path -> Skeletal-path].
+    SkMatrix matrix = *d.fMatrix;
+    SkPath* skeletalPath = const_cast<SkPath*>(&platonicPath);
+    if (prePathMatrix) {
+        if (paintHasPathEffect || paint->getRasterizer()) {
+            if (!pathIsMutable) {
+                skeletalPath = &modifiedPath;
+                pathIsMutable = true;
+            }
+            platonicPath.transform(*prePathMatrix, skeletalPath);
+        } else {
+            if (!matrix.preConcat(*prePathMatrix)) {
+                return;
+            }
+        }
+    }
+
+    //Apply path effect [Skeletal-path -> Fillable-path].
+    SkPath* fillablePath = skeletalPath;
+    if (paintHasPathEffect) {
+        if (!pathIsMutable) {
+            fillablePath = &modifiedPath;
+            pathIsMutable = true;
+        }
+        bool fill = paint->getFillPath(*skeletalPath, fillablePath);
+
+        SkPaint* writablePaint = paint.writable();
+        writablePaint->setPathEffect(NULL);
+        if (fill) {
+            writablePaint->setStyle(SkPaint::kFill_Style);
+        } else {
+            writablePaint->setStyle(SkPaint::kStroke_Style);
+            writablePaint->setStrokeWidth(0);
+        }
+    }
+
+    //Create the shaded path. This will be the path which is painted.
+    SkTScopedComPtr<IXpsOMPath> shadedPath;
+    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
+         "Could not create shaded path for path.");
+
+    //Create the geometry for the shaded path.
+    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
+    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
+         "Could not create shaded geometry for path.");
+
+    //Add the geometry to the shaded path.
+    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
+         "Could not add the shaded geometry to shaded path.");
+
+    SkRasterizer* rasterizer = paint->getRasterizer();
+    SkMaskFilter* filter = paint->getMaskFilter();
+
+    //Determine if we will draw or shade and mask.
+    if (rasterizer || filter) {
+        if (paint->getStyle() != SkPaint::kFill_Style) {
+            paint.writable()->setStyle(SkPaint::kFill_Style);
+        }
+    }
+
+    //Set the brushes.
+    BOOL fill;
+    BOOL stroke;
+    HRV(this->shadePath(shadedPath.get(),
+                        *paint,
+                        *d.fMatrix,
+                        &fill,
+                        &stroke));
+
+    //Rasterizer
+    if (rasterizer) {
+        SkIRect clipIRect;
+        SkVector ppuScale;
+        this->convertToPpm(filter,
+                           &matrix,
+                           &ppuScale,
+                           d.fClip->getBounds(),
+                           &clipIRect);
+
+        SkMask* mask = NULL;
+
+        //[Fillable-path -> Mask]
+        SkMask rasteredMask;
+        if (rasterizer->rasterize(
+                *fillablePath,
+                matrix,
+                &clipIRect,
+                filter,  //just to compute how much to draw.
+                &rasteredMask,
+                SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
+
+            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
+            mask = &rasteredMask;
+
+            //[Mask -> Mask]
+            SkMask filteredMask;
+            if (filter &&
+                filter->filterMask(&filteredMask, *mask, *d.fMatrix, NULL)) {
+
+                mask = &filteredMask;
+            } else {
+                filteredMask.fImage = NULL;
+            }
+            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
+
+            //Draw mask.
+            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
+        }
+        return;
+    }
+
+    //Mask filter
+    if (filter) {
+        SkIRect clipIRect;
+        SkVector ppuScale;
+        this->convertToPpm(filter,
+                           &matrix,
+                           &ppuScale,
+                           d.fClip->getBounds(),
+                           &clipIRect);
+
+        //[Fillable-path -> Pixel-path]
+        SkPath* pixelPath = pathIsMutable ? fillablePath : &modifiedPath;
+        fillablePath->transform(matrix, pixelPath);
+
+        SkMask* mask = NULL;
+
+        //[Pixel-path -> Mask]
+        SkMask rasteredMask;
+        if (SkDraw::DrawToMask(
+                        *pixelPath,
+                        &clipIRect,
+                        filter,  //just to compute how much to draw.
+                        &matrix,
+                        &rasteredMask,
+                        SkMask::kComputeBoundsAndRenderImage_CreateMode,
+                        paint->getStyle())) {
+
+            SkAutoMaskFreeImage rasteredAmi(rasteredMask.fImage);
+            mask = &rasteredMask;
+
+            //[Mask -> Mask]
+            SkMask filteredMask;
+            if (filter->filterMask(&filteredMask,
+                                   rasteredMask,
+                                   matrix,
+                                   NULL)) {
+                mask = &filteredMask;
+            } else {
+                filteredMask.fImage = NULL;
+            }
+            SkAutoMaskFreeImage filteredAmi(filteredMask.fImage);
+
+            //Draw mask.
+            HRV(this->applyMask(d, *mask, ppuScale, shadedPath.get()));
+        }
+        return;
+    }
+
+    //Get the figures from the shaded geometry.
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
+    HRVM(shadedGeometry->GetFigures(&shadedFigures),
+         "Could not get shaded figures for shaded path.");
+
+    bool xpsTransformsPath = true;
+
+    //Set the fill rule.
+    XPS_FILL_RULE xpsFillRule;
+    switch (platonicPath.getFillType()) {
+        case SkPath::kWinding_FillType:
+            xpsFillRule = XPS_FILL_RULE_NONZERO;
+            break;
+        case SkPath::kEvenOdd_FillType:
+            xpsFillRule = XPS_FILL_RULE_EVENODD;
+            break;
+        case SkPath::kInverseWinding_FillType: {
+            //[Fillable-path -> Device-path]
+            SkPath* devicePath = pathIsMutable ? fillablePath : &modifiedPath;
+            fillablePath->transform(matrix, devicePath);
+
+            HRV(this->drawInverseWindingPath(d,
+                                             *devicePath,
+                                             shadedPath.get()));
+            return;
+        }
+        case SkPath::kInverseEvenOdd_FillType: {
+            const SkRect universe = SkRect::MakeLTRB(
+                0, 0,
+                this->fCurrentCanvasSize.fWidth,
+                this->fCurrentCanvasSize.fHeight);
+            SkTScopedComPtr<IXpsOMGeometryFigure> addOneFigure;
+            HRV(this->createXpsRect(universe, FALSE, TRUE, &addOneFigure));
+            HRVM(shadedFigures->Append(addOneFigure.get()),
+                 "Could not add even-odd flip figure to shaded path.");
+            xpsTransformsPath = false;
+            xpsFillRule = XPS_FILL_RULE_EVENODD;
+            break;
+        }
+        default:
+            SkASSERT(!"Unknown SkPath::FillType.");
+    }
+    HRVM(shadedGeometry->SetFillRule(xpsFillRule),
+         "Could not set fill rule for shaded path.");
+
+    //Create the XPS transform, if possible.
+    if (xpsTransformsPath) {
+        SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
+        HRV(this->createXpsTransform(matrix, &xpsTransform));
+
+        if (xpsTransform.get()) {
+            HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
+                 "Could not set transform on shaded path.");
+        } else {
+            xpsTransformsPath = false;
+        }
+    }
+
+    SkPath* devicePath = fillablePath;
+    if (!xpsTransformsPath) {
+        //[Fillable-path -> Device-path]
+        devicePath = pathIsMutable ? fillablePath : &modifiedPath;
+        fillablePath->transform(matrix, devicePath);
+    }
+    HRV(this->addXpsPathGeometry(shadedFigures.get(),
+                                 stroke, fill, *devicePath));
+
+    HRV(this->clip(shadedPath.get(), d));
+
+    //Add the path to the active visual collection.
+    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
+    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
+         "Could not get current visuals for shaded path.");
+    HRVM(currentVisuals->Append(shadedPath.get()),
+         "Could not add shaded path to current visuals.");
+}
+
+HRESULT SkXPSDevice::clip(IXpsOMVisual* xpsVisual, const SkDraw& d) {
+    SkPath clipPath;
+    SkAssertResult(d.fClip->getBoundaryPath(&clipPath));
+
+    return this->clipToPath(xpsVisual, clipPath, XPS_FILL_RULE_EVENODD);
+}
+HRESULT SkXPSDevice::clipToPath(IXpsOMVisual* xpsVisual,
+                                const SkPath& clipPath,
+                                XPS_FILL_RULE fillRule) {
+    //Create the geometry.
+    SkTScopedComPtr<IXpsOMGeometry> clipGeometry;
+    HRM(this->fXpsFactory->CreateGeometry(&clipGeometry),
+        "Could not create clip geometry.");
+
+    //Get the figure collection of the geometry.
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> clipFigures;
+    HRM(clipGeometry->GetFigures(&clipFigures),
+        "Could not get the clip figures.");
+
+    //Create the figures into the geometry.
+    HR(this->addXpsPathGeometry(
+        clipFigures.get(),
+        FALSE, TRUE, clipPath));
+
+    HRM(clipGeometry->SetFillRule(fillRule),
+        "Could not set fill rule.");
+    HRM(xpsVisual->SetClipGeometryLocal(clipGeometry.get()),
+        "Could not set clip geometry.");
+
+    return S_OK;
+}
+
+void SkXPSDevice::drawBitmap(const SkDraw& d, const SkBitmap& bitmap,
+                             const SkIRect* srcRectOrNull,
+                             const SkMatrix& matrix, const SkPaint& paint) {
+    if (d.fClip->isEmpty()) {
+        return;
+    }
+
+    SkIRect srcRect;
+    SkBitmap tmp;
+    const SkBitmap* bitmapPtr = &bitmap;
+    if (NULL == srcRectOrNull) {
+        srcRect.set(0, 0, bitmap.width(), bitmap.height());
+        bitmapPtr = &bitmap;
+    } else {
+        srcRect = *srcRectOrNull;
+        if (!bitmap.extractSubset(&tmp, srcRect)) {
+            return; // extraction failed
+        }
+        bitmapPtr = &tmp;
+    }
+
+    //Create the new shaded path.
+    SkTScopedComPtr<IXpsOMPath> shadedPath;
+    HRVM(this->fXpsFactory->CreatePath(&shadedPath),
+         "Could not create path for bitmap.");
+
+    //Create the shaded geometry.
+    SkTScopedComPtr<IXpsOMGeometry> shadedGeometry;
+    HRVM(this->fXpsFactory->CreateGeometry(&shadedGeometry),
+         "Could not create geometry for bitmap.");
+
+    //Add the shaded geometry to the shaded path.
+    HRVM(shadedPath->SetGeometryLocal(shadedGeometry.get()),
+         "Could not set the geometry for bitmap.");
+
+    //Get the shaded figures from the shaded geometry.
+    SkTScopedComPtr<IXpsOMGeometryFigureCollection> shadedFigures;
+    HRVM(shadedGeometry->GetFigures(&shadedFigures),
+         "Could not get the figures for bitmap.");
+
+    SkMatrix transform = matrix;
+    transform.postConcat(*d.fMatrix);
+
+    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
+    HRV(this->createXpsTransform(transform, &xpsTransform));
+    if (xpsTransform.get()) {
+        HRVM(shadedGeometry->SetTransformLocal(xpsTransform.get()),
+             "Could not set transform for bitmap.");
+    } else {
+        //TODO: perspective that bitmap!
+    }
+
+    SkTScopedComPtr<IXpsOMGeometryFigure> rectFigure;
+    if (NULL != xpsTransform.get()) {
+        const SkShader::TileMode xy[2] = {
+            SkShader::kClamp_TileMode,
+            SkShader::kClamp_TileMode,
+        };
+        SkTScopedComPtr<IXpsOMTileBrush> xpsImageBrush;
+        HRV(this->createXpsImageBrush(*bitmapPtr,
+                                      transform,
+                                      xy,
+                                      paint.getAlpha(),
+                                      &xpsImageBrush));
+        HRVM(shadedPath->SetFillBrushLocal(xpsImageBrush.get()),
+             "Could not set bitmap brush.");
+
+        const SkRect bitmapRect = SkRect::MakeLTRB(0, 0,
+            SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
+        HRV(this->createXpsRect(bitmapRect, FALSE, TRUE, &rectFigure));
+    }
+    HRVM(shadedFigures->Append(rectFigure.get()),
+         "Could not add bitmap figure.");
+
+    //Get the current visual collection and add the shaded path to it.
+    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
+    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
+         "Could not get current visuals for bitmap");
+    HRVM(currentVisuals->Append(shadedPath.get()),
+         "Could not add bitmap to current visuals.");
+
+    HRV(this->clip(shadedPath.get(), d));
+}
+
+void SkXPSDevice::drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                             int x, int y,
+                             const SkPaint& paint) {
+    //TODO: override this for XPS
+    SkDEBUGF(("XPS drawSprite not yet implemented."));
+}
+
+HRESULT SkXPSDevice::CreateTypefaceUse(const SkPaint& paint,
+                                       TypefaceUse** typefaceUse) {
+    const SkTypeface* typeface = paint.getTypeface();
+
+    //Check cache.
+    const SkFontID typefaceID = SkTypeface::UniqueID(typeface);
+    if (!this->fTypefaces.empty()) {
+        TypefaceUse* current = &this->fTypefaces.front();
+        const TypefaceUse* last = &this->fTypefaces.back();
+        for (; current <= last; ++current) {
+            if (current->typefaceId == typefaceID) {
+                *typefaceUse = current;
+                return S_OK;
+            }
+        }
+    }
+
+    //TODO: create glyph only fonts
+    //and let the host deal with what kind of font we're looking at.
+    XPS_FONT_EMBEDDING embedding = XPS_FONT_EMBEDDING_RESTRICTED;
+
+    SkTScopedComPtr<IStream> fontStream;
+    SkStream* fontData = SkFontHost::OpenStream(typefaceID);
+    HRM(SkIStream::CreateFromSkStream(fontData, true, &fontStream),
+        "Could not create font stream.");
+
+    const size_t size =
+        SK_ARRAY_COUNT(L"/Resources/Fonts/" L_GUID_ID L".odttf");
+    wchar_t buffer[size];
+    wchar_t id[GUID_ID_LEN];
+    HR(create_id(id, GUID_ID_LEN));
+    swprintf_s(buffer, size, L"/Resources/Fonts/%s.odttf", id);
+
+    SkTScopedComPtr<IOpcPartUri> partUri;
+    HRM(this->fXpsFactory->CreatePartUri(buffer, &partUri),
+        "Could not create font resource part uri.");
+
+    SkTScopedComPtr<IXpsOMFontResource> xpsFontResource;
+    HRM(this->fXpsFactory->CreateFontResource(fontStream.get(),
+                                              embedding,
+                                              partUri.get(),
+                                              FALSE,
+                                              &xpsFontResource),
+        "Could not create font resource.");
+
+    TypefaceUse& newTypefaceUse = this->fTypefaces.push_back();
+    newTypefaceUse.typefaceId = typefaceID;
+    newTypefaceUse.fontData = fontData;
+    newTypefaceUse.xpsFont = xpsFontResource.release();
+
+    SkAutoGlyphCache agc = SkAutoGlyphCache(paint, NULL, &SkMatrix::I());
+    SkGlyphCache* glyphCache = agc.getCache();
+    unsigned int glyphCount = glyphCache->getGlyphCount();
+    newTypefaceUse.glyphsUsed = new SkBitSet(glyphCount);
+
+    *typefaceUse = &newTypefaceUse;
+    return S_OK;
+}
+
+HRESULT SkXPSDevice::AddGlyphs(const SkDraw& d,
+                               IXpsOMObjectFactory* xpsFactory,
+                               IXpsOMCanvas* canvas,
+                               IXpsOMFontResource* font,
+                               LPCWSTR text,
+                               XPS_GLYPH_INDEX* xpsGlyphs,
+                               UINT32 xpsGlyphsLen,
+                               XPS_POINT *origin,
+                               FLOAT fontSize,
+                               XPS_STYLE_SIMULATION sims,
+                               const SkMatrix& transform,
+                               const SkPaint& paint) {
+    SkTScopedComPtr<IXpsOMGlyphs> glyphs;
+    HRM(xpsFactory->CreateGlyphs(font, &glyphs), "Could not create glyphs.");
+
+    //XPS uses affine transformations for everything...
+    //...except positioning text.
+    bool useCanvasForClip;
+    if ((transform.getType() & ~SkMatrix::kTranslate_Mask) == 0) {
+        origin->x += SkScalarToFLOAT(transform.getTranslateX());
+        origin->y += SkScalarToFLOAT(transform.getTranslateY());
+        useCanvasForClip = false;
+    } else {
+        SkTScopedComPtr<IXpsOMMatrixTransform> xpsMatrixToUse;
+        HR(this->createXpsTransform(transform, &xpsMatrixToUse));
+        if (xpsMatrixToUse.get()) {
+            HRM(glyphs->SetTransformLocal(xpsMatrixToUse.get()),
+                "Could not set transform matrix.");
+            useCanvasForClip = true;
+        } else {
+            SkASSERT(!"Attempt to add glyphs in perspective.");
+            useCanvasForClip = false;
+        }
+    }
+
+    SkTScopedComPtr<IXpsOMGlyphsEditor> glyphsEditor;
+    HRM(glyphs->GetGlyphsEditor(&glyphsEditor), "Could not get glyph editor.");
+
+    if (NULL != text) {
+        HRM(glyphsEditor->SetUnicodeString(text),
+            "Could not set unicode string.");
+    }
+
+    if (NULL != xpsGlyphs) {
+        HRM(glyphsEditor->SetGlyphIndices(xpsGlyphsLen, xpsGlyphs),
+            "Could not set glyphs.");
+    }
+
+    HRM(glyphsEditor->ApplyEdits(), "Could not apply glyph edits.");
+
+    SkTScopedComPtr<IXpsOMBrush> xpsFillBrush;
+    HR(this->createXpsBrush(
+            paint,
+            &xpsFillBrush,
+            useCanvasForClip ? NULL : &transform));
+
+    HRM(glyphs->SetFillBrushLocal(xpsFillBrush.get()),
+        "Could not set fill brush.");
+
+    HRM(glyphs->SetOrigin(origin), "Could not set glyph origin.");
+
+    HRM(glyphs->SetFontRenderingEmSize(fontSize),
+        "Could not set font size.");
+
+    HRM(glyphs->SetStyleSimulations(sims),
+        "Could not set style simulations.");
+
+    SkTScopedComPtr<IXpsOMVisualCollection> visuals;
+    HRM(canvas->GetVisuals(&visuals), "Could not get glyph canvas visuals.");
+
+    if (!useCanvasForClip) {
+        HR(this->clip(glyphs.get(), d));
+        HRM(visuals->Append(glyphs.get()), "Could not add glyphs to canvas.");
+    } else {
+        SkTScopedComPtr<IXpsOMCanvas> glyphCanvas;
+        HRM(this->fXpsFactory->CreateCanvas(&glyphCanvas),
+            "Could not create glyph canvas.");
+
+        SkTScopedComPtr<IXpsOMVisualCollection> glyphCanvasVisuals;
+        HRM(glyphCanvas->GetVisuals(&glyphCanvasVisuals),
+            "Could not get glyph visuals collection.");
+
+        HRM(glyphCanvasVisuals->Append(glyphs.get()),
+            "Could not add glyphs to page.");
+        HR(this->clip(glyphCanvas.get(), d));
+
+        HRM(visuals->Append(glyphCanvas.get()),
+            "Could not add glyph canvas to page.");
+    }
+
+    return S_OK;
+}
+
+struct SkXPSDrawProcs : public SkDrawProcs {
+public:
+    /** [in] Advance width and offsets for glyphs measured in
+    hundredths of the font em size (XPS Spec 5.1.3). */
+    FLOAT centemPerUnit;
+    /** [in,out] The accumulated glyphs used in the current typeface. */
+    SkBitSet* glyphUse;
+    /** [out] The glyphs to draw. */
+    SkTDArray<XPS_GLYPH_INDEX> xpsGlyphs;
+};
+
+static void xps_draw_1_glyph(const SkDraw1Glyph& state,
+                             SkFixed x, SkFixed y,
+                             const SkGlyph& skGlyph) {
+    SkASSERT(skGlyph.fWidth > 0 && skGlyph.fHeight > 0);
+
+    SkXPSDrawProcs* procs = static_cast<SkXPSDrawProcs*>(state.fDraw->fProcs);
+
+    //Draw pre-adds half the sampling frequency for floor rounding.
+    if (state.fCache->isSubpixel()) {
+        x -= (SK_FixedHalf >> SkGlyph::kSubBits);
+        y -= (SK_FixedHalf >> SkGlyph::kSubBits);
+    } else {
+        x -= SK_FixedHalf;
+        y -= SK_FixedHalf;
+    }
+
+    XPS_GLYPH_INDEX* xpsGlyph = procs->xpsGlyphs.append();
+    uint16_t glyphID = skGlyph.getGlyphID();
+    procs->glyphUse->setBit(glyphID, true);
+    xpsGlyph->index = glyphID;
+    if (1 == procs->xpsGlyphs.count()) {
+        xpsGlyph->advanceWidth = 0.0f;
+        xpsGlyph->horizontalOffset = SkFixedToFloat(x) * procs->centemPerUnit;
+        xpsGlyph->verticalOffset = SkFixedToFloat(y) * -procs->centemPerUnit;
+    } else {
+        const XPS_GLYPH_INDEX& first = procs->xpsGlyphs[0];
+        xpsGlyph->advanceWidth = 0.0f;
+        xpsGlyph->horizontalOffset = (SkFixedToFloat(x) * procs->centemPerUnit)
+                                     - first.horizontalOffset;
+        xpsGlyph->verticalOffset = (SkFixedToFloat(y) * -procs->centemPerUnit)
+                                   - first.verticalOffset;
+    }
+}
+
+static void text_draw_init(const SkPaint& paint,
+                           const void* text, size_t byteLength,
+                           SkBitSet& glyphsUsed,
+                           SkDraw& myDraw, SkXPSDrawProcs& procs) {
+    procs.fD1GProc = xps_draw_1_glyph;
+    int numGlyphGuess;
+    switch (paint.getTextEncoding()) {
+        case SkPaint::kUTF8_TextEncoding:
+            numGlyphGuess = SkUTF8_CountUnichars(
+                static_cast<const char *>(text),
+                byteLength);
+            break;
+        case SkPaint::kUTF16_TextEncoding:
+            numGlyphGuess = SkUTF16_CountUnichars(
+                static_cast<const uint16_t *>(text),
+                byteLength);
+            break;
+        case SkPaint::kGlyphID_TextEncoding:
+            numGlyphGuess = byteLength / 2;
+            break;
+        default:
+            SK_DEBUGBREAK(true);
+    }
+    procs.xpsGlyphs.setReserve(numGlyphGuess);
+    procs.glyphUse = &glyphsUsed;
+    procs.centemPerUnit = 100.0f / SkScalarToFLOAT(paint.getTextSize());
+
+    myDraw.fProcs = &procs;
+}
+
+static bool text_must_be_pathed(const SkPaint& paint, const SkMatrix& matrix) {
+    const SkPaint::Style style = paint.getStyle();
+    return matrix.hasPerspective()
+        || SkPaint::kStroke_Style == style
+        || SkPaint::kStrokeAndFill_Style == style
+        || paint.getMaskFilter()
+        || paint.getRasterizer()
+    ;
+}
+
+void SkXPSDevice::drawText(const SkDraw& d,
+                           const void* text, size_t byteLen,
+                           SkScalar x, SkScalar y,
+                           const SkPaint& paint) {
+    if (byteLen < 1) return;
+
+    if (text_must_be_pathed(paint, *d.fMatrix)) {
+        SkPath path;
+        paint.getTextPath(text, byteLen, x, y, &path);
+        this->drawPath(d, path, paint, NULL, true);
+        //TODO: add automation "text"
+        return;
+    }
+
+    TypefaceUse* typeface;
+    HRV(CreateTypefaceUse(paint, &typeface));
+
+    SkDraw myDraw(d);
+    SkXPSDrawProcs procs;
+    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
+
+    myDraw.drawText(static_cast<const char*>(text), byteLen, x, y, paint);
+
+    // SkDraw may have clipped out the glyphs, so we need to check
+    if (procs.xpsGlyphs.count() == 0) {
+        return;
+    }
+
+    XPS_POINT origin = {
+        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
+        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
+    };
+    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
+    procs.xpsGlyphs[0].verticalOffset = 0.0f;
+
+    HRV(AddGlyphs(d,
+                  this->fXpsFactory.get(),
+                  this->fCurrentXpsCanvas.get(),
+                  typeface->xpsFont,
+                  NULL,
+                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
+                  &origin,
+                  SkScalarToFLOAT(paint.getTextSize()),
+                  XPS_STYLE_SIMULATION_NONE,
+                  *d.fMatrix,
+                  paint));
+}
+
+void SkXPSDevice::drawPosText(const SkDraw& d,
+                              const void* text, size_t byteLen,
+                              const SkScalar pos[],
+                              SkScalar constY, int scalarsPerPos,
+                              const SkPaint& paint) {
+    if (byteLen < 1) return;
+
+    if (text_must_be_pathed(paint, *d.fMatrix)) {
+        SkPath path;
+        //TODO: make this work, Draw currently does not handle as well.
+        //paint.getTextPath(text, byteLength, x, y, &path);
+        //this->drawPath(d, path, paint, NULL, true);
+        //TODO: add automation "text"
+        return;
+    }
+
+    TypefaceUse* typeface;
+    HRV(CreateTypefaceUse(paint, &typeface));
+
+    SkDraw myDraw(d);
+    SkXPSDrawProcs procs;
+    text_draw_init(paint, text, byteLen, *typeface->glyphsUsed, myDraw, procs);
+
+    myDraw.drawPosText(static_cast<const char*>(text), byteLen,
+                       pos, constY, scalarsPerPos,
+                       paint);
+
+    // SkDraw may have clipped out the glyphs, so we need to check
+    if (procs.xpsGlyphs.count() == 0) {
+        return;
+    }
+
+    XPS_POINT origin = {
+        procs.xpsGlyphs[0].horizontalOffset / procs.centemPerUnit,
+        procs.xpsGlyphs[0].verticalOffset / -procs.centemPerUnit,
+    };
+    procs.xpsGlyphs[0].horizontalOffset = 0.0f;
+    procs.xpsGlyphs[0].verticalOffset = 0.0f;
+
+    HRV(AddGlyphs(d,
+                  this->fXpsFactory.get(),
+                  this->fCurrentXpsCanvas.get(),
+                  typeface->xpsFont,
+                  NULL,
+                  procs.xpsGlyphs.begin(), procs.xpsGlyphs.count(),
+                  &origin,
+                  SkScalarToFLOAT(paint.getTextSize()),
+                  XPS_STYLE_SIMULATION_NONE,
+                  *d.fMatrix,
+                  paint));
+}
+
+void SkXPSDevice::drawTextOnPath(const SkDraw& d, const void* text, size_t len,
+                                 const SkPath& path, const SkMatrix* matrix,
+                                 const SkPaint& paint) {
+    //This will call back into the device to do the drawing.
+     d.drawTextOnPath((const char*)text, len, path, matrix, paint);
+}
+
+void SkXPSDevice::drawDevice(const SkDraw& d, SkDevice* dev,
+                             int x, int y,
+                             const SkPaint&) {
+    SkXPSDevice* that = static_cast<SkXPSDevice*>(dev);
+
+    SkTScopedComPtr<IXpsOMMatrixTransform> xpsTransform;
+    XPS_MATRIX rawTransform = {
+        1.0f,
+        0.0f,
+        0.0f,
+        1.0f,
+        static_cast<FLOAT>(x),
+        static_cast<FLOAT>(y),
+    };
+    HRVM(this->fXpsFactory->CreateMatrixTransform(&rawTransform, &xpsTransform),
+         "Could not create layer transform.");
+    HRVM(that->fCurrentXpsCanvas->SetTransformLocal(xpsTransform.get()),
+         "Could not set layer transform.");
+
+    //Get the current visual collection and add the layer to it.
+    SkTScopedComPtr<IXpsOMVisualCollection> currentVisuals;
+    HRVM(this->fCurrentXpsCanvas->GetVisuals(&currentVisuals),
+         "Could not get current visuals for layer.");
+    HRVM(currentVisuals->Append(that->fCurrentXpsCanvas.get()),
+         "Could not add layer to current visuals.");
+}
+
+bool SkXPSDevice::onReadPixels(const SkBitmap& bitmap, int x, int y,
+                               SkCanvas::Config8888) {
+    return false;
+}
+
+SkDevice* SkXPSDevice::onCreateCompatibleDevice(SkBitmap::Config config,
+                                                int width, int height,
+                                                bool isOpaque,
+                                                Usage usage) {
+    if (SkDevice::kGeneral_Usage == usage) {
+        return NULL;
+        SK_CRASH();
+        //To what stream do we write?
+        //SkXPSDevice* dev = new SkXPSDevice(this);
+        //SkSize s = SkSize::Make(width, height);
+        //dev->BeginCanvas(s, s, SkMatrix::I());
+        //return dev;
+    }
+
+    return new SkXPSDevice(this->fXpsFactory.get());
+}
+
+SkXPSDevice::SkXPSDevice(IXpsOMObjectFactory* xpsFactory)
+    : SkDevice(make_fake_bitmap(10000, 10000))
+    , fCurrentPage(0) {
+
+    HRVM(CoCreateInstance(
+             CLSID_XpsOMObjectFactory,
+             NULL,
+             CLSCTX_INPROC_SERVER,
+             IID_PPV_ARGS(&this->fXpsFactory)),
+         "Could not create factory for layer.");
+
+    HRVM(this->fXpsFactory->CreateCanvas(&this->fCurrentXpsCanvas),
+         "Could not create canvas for layer.");
+}
+
+bool SkXPSDevice::allowImageFilter(SkImageFilter*) {
+    return false;
+}
diff --git a/src/effects/Sk1DPathEffect.cpp b/src/effects/Sk1DPathEffect.cpp
index 9ccc453..10a68b9 100644
--- a/src/effects/Sk1DPathEffect.cpp
+++ b/src/effects/Sk1DPathEffect.cpp
@@ -8,9 +8,11 @@
 
 
 #include "Sk1DPathEffect.h"
+#include "SkFlattenableBuffers.h"
 #include "SkPathMeasure.h"
 
-bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
+bool Sk1DPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                SkStrokeRec*, const SkRect*) const {
     SkPathMeasure   meas(src, false);
     do {
         SkScalar    length = meas.getLength();
@@ -28,12 +30,14 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance, 
+SkPath1DPathEffect::SkPath1DPathEffect(const SkPath& path, SkScalar advance,
     SkScalar phase, Style style) : fPath(path)
 {
     if (advance <= 0 || path.isEmpty()) {
         SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n"));
         fAdvance = 0;   // signals we can't draw anything
+        fInitialOffset = 0;
+        fStyle = kStyleCount;
     } else {
         // cleanup their phase parameter, inverting it so that it becomes an
         // offset along the path (to match the interpretation in PostScript)
@@ -56,7 +60,7 @@
 
         fAdvance = advance;
         fInitialOffset = phase;
-        
+
         if ((unsigned)style >= kStyleCount) {
             SkDEBUGF(("SkPath1DPathEffect style enum out of range %d\n", style));
         }
@@ -65,34 +69,37 @@
 }
 
 bool SkPath1DPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                    SkScalar* width) {
+                            SkStrokeRec* rec, const SkRect* cullRect) const {
     if (fAdvance > 0) {
-        *width = -1;
-        return this->INHERITED::filterPath(dst, src, width);
+        rec->setFillStyle();
+        return this->INHERITED::filterPath(dst, src, rec, cullRect);
     }
     return false;
 }
 
-static void morphpoints(SkPoint dst[], const SkPoint src[], int count,
+static bool morphpoints(SkPoint dst[], const SkPoint src[], int count,
                         SkPathMeasure& meas, SkScalar dist) {
     for (int i = 0; i < count; i++) {
         SkPoint pos;
         SkVector tangent;
-        
+
         SkScalar sx = src[i].fX;
         SkScalar sy = src[i].fY;
-        
-        meas.getPosTan(dist + sx, &pos, &tangent);
-        
+
+        if (!meas.getPosTan(dist + sx, &pos, &tangent)) {
+            return false;
+        }
+
         SkMatrix    matrix;
         SkPoint     pt;
-        
+
         pt.set(sx, sy);
         matrix.setSinCos(tangent.fY, tangent.fX, 0, 0);
         matrix.preTranslate(-sx, 0);
         matrix.postTranslate(pos.fX, pos.fY);
         matrix.mapPoints(&dst[i], &pt, 1);
     }
+    return true;
 }
 
 /*  TODO
@@ -110,8 +117,9 @@
     while ((verb = iter.next(srcP)) != SkPath::kDone_Verb) {
         switch (verb) {
             case SkPath::kMove_Verb:
-                morphpoints(dstP, srcP, 1, meas, dist);
-                dst->moveTo(dstP[0]);
+                if (morphpoints(dstP, srcP, 1, meas, dist)) {
+                    dst->moveTo(dstP[0]);
+                }
                 break;
             case SkPath::kLine_Verb:
                 srcP[2] = srcP[1];
@@ -119,12 +127,14 @@
                             SkScalarAve(srcP[0].fY, srcP[2].fY));
                 // fall through to quad
             case SkPath::kQuad_Verb:
-                morphpoints(dstP, &srcP[1], 2, meas, dist);
-                dst->quadTo(dstP[0], dstP[1]);
+                if (morphpoints(dstP, &srcP[1], 2, meas, dist)) {
+                    dst->quadTo(dstP[0], dstP[1]);
+                }
                 break;
             case SkPath::kCubic_Verb:
-                morphpoints(dstP, &srcP[1], 3, meas, dist);
-                dst->cubicTo(dstP[0], dstP[1], dstP[2]);
+                if (morphpoints(dstP, &srcP[1], 3, meas, dist)) {
+                    dst->cubicTo(dstP[0], dstP[1], dstP[2]);
+                }
                 break;
             case SkPath::kClose_Verb:
                 dst->close();
@@ -139,37 +149,45 @@
 SkPath1DPathEffect::SkPath1DPathEffect(SkFlattenableReadBuffer& buffer) {
     fAdvance = buffer.readScalar();
     if (fAdvance > 0) {
-        fPath.unflatten(buffer);
+        buffer.readPath(&fPath);
         fInitialOffset = buffer.readScalar();
-        fStyle = (Style) buffer.readU8();
+        fStyle = (Style) buffer.readUInt();
+    } else {
+        SkDEBUGF(("SkPath1DPathEffect can't use advance <= 0\n"));
+        // Make Coverity happy.
+        fInitialOffset = 0;
+        fStyle = kStyleCount;
     }
 }
 
-SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) {
+SkScalar SkPath1DPathEffect::begin(SkScalar contourLength) const {
     return fInitialOffset;
 }
 
-void SkPath1DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkPath1DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
     buffer.writeScalar(fAdvance);
     if (fAdvance > 0) {
-        fPath.flatten(buffer);
+        buffer.writePath(fPath);
         buffer.writeScalar(fInitialOffset);
-        buffer.write8(fStyle);
+        buffer.writeUInt(fStyle);
     }
 }
 
 SkScalar SkPath1DPathEffect::next(SkPath* dst, SkScalar distance,
-                                  SkPathMeasure& meas) {
+                                  SkPathMeasure& meas) const {
     switch (fStyle) {
         case kTranslate_Style: {
             SkPoint pos;
-            meas.getPosTan(distance, &pos, NULL);
-            dst->addPath(fPath, pos.fX, pos.fY);
+            if (meas.getPosTan(distance, &pos, NULL)) {
+                dst->addPath(fPath, pos.fX, pos.fY);
+            }
         } break;
         case kRotate_Style: {
             SkMatrix matrix;
-            meas.getMatrix(distance, &matrix);
-            dst->addPath(fPath, matrix);
+            if (meas.getMatrix(distance, &matrix)) {
+                dst->addPath(fPath, matrix);
+            }
         } break;
         case kMorph_Style:
             morphpath(dst, fPath, meas, distance);
@@ -180,8 +198,3 @@
     }
     return fAdvance;
 }
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkPath1DPathEffect)
-
diff --git a/src/effects/Sk2DPathEffect.cpp b/src/effects/Sk2DPathEffect.cpp
index 3729610..dc15f07 100644
--- a/src/effects/Sk2DPathEffect.cpp
+++ b/src/effects/Sk2DPathEffect.cpp
@@ -8,45 +8,48 @@
 
 
 #include "Sk2DPathEffect.h"
-#include "SkBlitter.h"
+#include "SkFlattenableBuffers.h"
 #include "SkPath.h"
-#include "SkScan.h"
-
-class Sk2DPathEffectBlitter : public SkBlitter {
-public:
-    Sk2DPathEffectBlitter(Sk2DPathEffect* pe, SkPath* dst)
-        : fPE(pe), fDst(dst) {}
-
-    virtual void blitH(int x, int y, int count) {
-        fPE->nextSpan(x, y, count, fDst);
-    }
-private:
-    Sk2DPathEffect* fPE;
-    SkPath*         fDst;
-};
-
-///////////////////////////////////////////////////////////////////////////////
+#include "SkRegion.h"
 
 Sk2DPathEffect::Sk2DPathEffect(const SkMatrix& mat) : fMatrix(mat) {
-    mat.invert(&fInverse);
+    fMatrixIsInvertible = mat.invert(&fInverse);
 }
 
-bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src, SkScalar* width) {
-    Sk2DPathEffectBlitter   blitter(this, dst);
-    SkPath                  tmp;
-    SkIRect                 ir;
+bool Sk2DPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                                SkStrokeRec*, const SkRect*) const {
+    if (!fMatrixIsInvertible) {
+        return false;
+    }
+
+    SkPath  tmp;
+    SkIRect ir;
 
     src.transform(fInverse, &tmp);
     tmp.getBounds().round(&ir);
     if (!ir.isEmpty()) {
         this->begin(ir, dst);
-        SkScan::FillPath(tmp, ir, &blitter);
+
+        SkRegion rgn;
+        rgn.setPath(tmp, SkRegion(ir));
+        SkRegion::Iterator iter(rgn);
+        for (; !iter.done(); iter.next()) {
+            const SkIRect& rect = iter.rect();
+            for (int y = rect.fTop; y < rect.fBottom; ++y) {
+                this->nextSpan(rect.fLeft, y, rect.width(), dst);
+            }
+        }
+
         this->end(dst);
     }
     return true;
 }
 
-void Sk2DPathEffect::nextSpan(int x, int y, int count, SkPath* path) {
+void Sk2DPathEffect::nextSpan(int x, int y, int count, SkPath* path) const {
+    if (!fMatrixIsInvertible) {
+        return;
+    }
+
     const SkMatrix& mat = this->getMatrix();
     SkPoint src, dst;
 
@@ -58,37 +61,55 @@
     } while (--count > 0);
 }
 
-void Sk2DPathEffect::begin(const SkIRect& uvBounds, SkPath* dst) {}
-void Sk2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) {}
-void Sk2DPathEffect::end(SkPath* dst) {}
+void Sk2DPathEffect::begin(const SkIRect& uvBounds, SkPath* dst) const {}
+void Sk2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) const {}
+void Sk2DPathEffect::end(SkPath* dst) const {}
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
-    char storage[SkMatrix::kMaxFlattenSize];
-    uint32_t size = fMatrix.flatten(storage);
-    buffer.write32(size);
-    buffer.write(storage, size);
+void Sk2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeMatrix(fMatrix);
 }
 
 Sk2DPathEffect::Sk2DPathEffect(SkFlattenableReadBuffer& buffer) {
-    char storage[SkMatrix::kMaxFlattenSize];
-    uint32_t size = buffer.readS32();
-    SkASSERT(size <= sizeof(storage));
-    buffer.read(storage, size);
-    fMatrix.unflatten(storage);
-    fMatrix.invert(&fInverse);
-}
-
-SkFlattenable::Factory Sk2DPathEffect::getFactory() {
-    return CreateProc;
-}
-
-SkFlattenable* Sk2DPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(Sk2DPathEffect, (buffer));
+    buffer.readMatrix(&fMatrix);
+    fMatrixIsInvertible = fMatrix.invert(&fInverse);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+
+bool SkLine2DPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                            SkStrokeRec* rec, const SkRect* cullRect) const {
+    if (this->INHERITED::filterPath(dst, src, rec, cullRect)) {
+        rec->setStrokeStyle(fWidth);
+        return true;
+    }
+    return false;
+}
+
+void SkLine2DPathEffect::nextSpan(int u, int v, int ucount, SkPath* dst) const {
+    if (ucount > 1) {
+        SkPoint    src[2], dstP[2];
+
+        src[0].set(SkIntToScalar(u) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
+        src[1].set(SkIntToScalar(u+ucount) + SK_ScalarHalf, SkIntToScalar(v) + SK_ScalarHalf);
+        this->getMatrix().mapPoints(dstP, src, 2);
+
+        dst->moveTo(dstP[0]);
+        dst->lineTo(dstP[1]);
+    }
+}
+
+SkLine2DPathEffect::SkLine2DPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fWidth = buffer.readScalar();
+}
+
+void SkLine2DPathEffect::flatten(SkFlattenableWriteBuffer &buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fWidth);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkPath2DPathEffect::SkPath2DPathEffect(const SkMatrix& m, const SkPath& p)
@@ -97,25 +118,15 @@
 
 SkPath2DPathEffect::SkPath2DPathEffect(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {
-    fPath.unflatten(buffer);
+    buffer.readPath(&fPath);
 }
 
-SkFlattenable* SkPath2DPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkPath2DPathEffect, (buffer));
-}
-
-void SkPath2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkPath2DPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    fPath.flatten(buffer);
+    buffer.writePath(fPath);
 }
 
-SkFlattenable::Factory SkPath2DPathEffect::getFactory() {
-    return CreateProc;
-}
-
-void SkPath2DPathEffect::next(const SkPoint& loc, int u, int v, SkPath* dst) {
+void SkPath2DPathEffect::next(const SkPoint& loc, int u, int v,
+                              SkPath* dst) const {
     dst->addPath(fPath, loc.fX, loc.fY);
 }
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkPath2DPathEffect)
-
diff --git a/src/effects/SkArithmeticMode.cpp b/src/effects/SkArithmeticMode.cpp
index 8190b39..7492cce 100644
--- a/src/effects/SkArithmeticMode.cpp
+++ b/src/effects/SkArithmeticMode.cpp
@@ -1,5 +1,13 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
 #include "SkArithmeticMode.h"
 #include "SkColorPriv.h"
+#include "SkString.h"
 #include "SkUnPreMultiply.h"
 
 class SkArithmeticMode_scalar : public SkXfermode {
@@ -12,20 +20,16 @@
     }
 
     virtual void xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                        const SkAlpha aa[]) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
+                        const SkAlpha aa[]) const SK_OVERRIDE;
 
-    static SkFlattenable* Create(SkFlattenableReadBuffer& buffer) {
-        return NULL;
-    }
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
 
 private:
     SkScalar fK[4];
-};
 
-SkFlattenable::Factory SkArithmeticMode_scalar::getFactory() {
-    return Create;
-}
+    typedef SkXfermode INHERITED;
+};
 
 static int pinToByte(int value) {
     if (value < 0) {
@@ -55,7 +59,7 @@
 }
 
 void SkArithmeticMode_scalar::xfer32(SkPMColor dst[], const SkPMColor src[],
-                                     int count, const SkAlpha aaCoverage[]) {
+                                 int count, const SkAlpha aaCoverage[]) const {
     SkScalar k1 = fK[0] / 255;
     SkScalar k2 = fK[1];
     SkScalar k3 = fK[2];
@@ -73,7 +77,7 @@
 
             int a, r, g, b;
 
-            if (!srcNeedsUnpremul && !srcNeedsUnpremul) {
+            if (!srcNeedsUnpremul && !dstNeedsUnpremul) {
                 a = arith(k1, k2, k3, k4, sa, sa);
                 r = arith(k1, k2, k3, k4, SkGetPackedR32(sc), SkGetPackedR32(dc));
                 g = arith(k1, k2, k3, k4, SkGetPackedG32(sc), SkGetPackedG32(dc));
@@ -126,6 +130,17 @@
     }
 }
 
+#ifdef SK_DEVELOPER
+void SkArithmeticMode_scalar::toString(SkString* str) const {
+    str->append("SkArithmeticMode_scalar: ");
+    for (int i = 0; i < 4; ++i) {
+        str->appendScalar(fK[i]);
+        if (i < 3) {
+            str->append(" ");
+        }
+    }
+}
+#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -140,6 +155,7 @@
 #endif
 }
 
+#if 0 // UNUSED
 static int32_t toDot8(SkScalar x) {
 #ifdef SK_SCALAR_IS_FIXED
     x += 1 << 7;
@@ -149,17 +165,18 @@
     return (int32_t)(x * 256);
 #endif
 }
+#endif
 
 SkXfermode* SkArithmeticMode::Create(SkScalar k1, SkScalar k2,
                                      SkScalar k3, SkScalar k4) {
     if (fitsInBits(k1, 8) && fitsInBits(k2, 16) &&
         fitsInBits(k2, 16) && fitsInBits(k2, 24)) {
 
+#if 0 // UNUSED
         int32_t i1 = toDot8(k1);
         int32_t i2 = toDot8(k2);
         int32_t i3 = toDot8(k3);
         int32_t i4 = toDot8(k4);
-#if 0
         if (i1) {
             return SkNEW_ARGS(SkArithmeticMode_quad, (i1, i2, i3, i4));
         }
@@ -174,4 +191,3 @@
     }
     return SkNEW_ARGS(SkArithmeticMode_scalar, (k1, k2, k3, k4));
 }
-
diff --git a/src/effects/SkAvoidXfermode.cpp b/src/effects/SkAvoidXfermode.cpp
index 9f4e396..206f7e9 100644
--- a/src/effects/SkAvoidXfermode.cpp
+++ b/src/effects/SkAvoidXfermode.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,12 +5,12 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkAvoidXfermode.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkString.h"
 
-SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode)
-{
+SkAvoidXfermode::SkAvoidXfermode(SkColor opColor, U8CPU tolerance, Mode mode) {
     if (tolerance > 255) {
         tolerance = 255;
     }
@@ -22,35 +21,22 @@
 }
 
 SkAvoidXfermode::SkAvoidXfermode(SkFlattenableReadBuffer& buffer)
-    : INHERITED(buffer)
-{
-    fOpColor = buffer.readU32();
-    fDistMul = buffer.readU32();
-    fMode = (Mode)buffer.readU8();
+    : INHERITED(buffer) {
+    fOpColor = buffer.readColor();
+    fDistMul = buffer.readUInt();
+    fMode = (Mode)buffer.readUInt();
 }
 
-void SkAvoidXfermode::flatten(SkFlattenableWriteBuffer& buffer)
-{
+void SkAvoidXfermode::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    buffer.write32(fOpColor);
-    buffer.write32(fDistMul);
-    buffer.write8(fMode);
-}
-
-SkFlattenable* SkAvoidXfermode::Create(SkFlattenableReadBuffer& rb)
-{
-    return SkNEW_ARGS(SkAvoidXfermode, (rb));
-}
-
-SkFlattenable::Factory SkAvoidXfermode::getFactory()
-{
-    return Create;
+    buffer.writeColor(fOpColor);
+    buffer.writeUInt(fDistMul);
+    buffer.writeUInt(fMode);
 }
 
 // returns 0..31
-static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b)
-{
+static unsigned color_dist16(uint16_t c, unsigned r, unsigned g, unsigned b) {
     SkASSERT(r <= SK_R16_MASK);
     SkASSERT(g <= SK_G16_MASK);
     SkASSERT(b <= SK_B16_MASK);
@@ -63,8 +49,7 @@
 }
 
 // returns 0..15
-static unsigned color_dist4444(uint16_t c, unsigned r, unsigned g, unsigned b)
-{
+static unsigned color_dist4444(uint16_t c, unsigned r, unsigned g, unsigned b) {
     SkASSERT(r <= 0xF);
     SkASSERT(g <= 0xF);
     SkASSERT(b <= 0xF);
@@ -77,8 +62,7 @@
 }
 
 // returns 0..255
-static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b)
-{
+static unsigned color_dist32(SkPMColor c, U8CPU r, U8CPU g, U8CPU b) {
     SkASSERT(r <= 0xFF);
     SkASSERT(g <= 0xFF);
     SkASSERT(b <= 0xFF);
@@ -90,8 +74,7 @@
     return SkMax32(dr, SkMax32(dg, db));
 }
 
-static int scale_dist_14(int dist, uint32_t mul, uint32_t sub)
-{
+static int scale_dist_14(int dist, uint32_t mul, uint32_t sub) {
     int tmp = dist * mul - sub;
     int result = (tmp + (1 << 13)) >> 14;
 
@@ -103,8 +86,7 @@
 }
 
 void SkAvoidXfermode::xfer32(SkPMColor dst[], const SkPMColor src[], int count,
-                             const SkAlpha aa[])
-{
+                             const SkAlpha aa[]) const {
     unsigned    opR = SkColorGetR(fOpColor);
     unsigned    opG = SkColorGetG(fOpColor);
     unsigned    opB = SkColorGetB(fOpColor);
@@ -138,13 +120,12 @@
                     continue;
                 }
             }
-            dst[i] = SkFourByteInterp(src[i], dst[i], d);
+            dst[i] = SkFourByteInterp256(src[i], dst[i], d);
         }
     }
 }
 
-static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale)
-{
+static inline U16CPU SkBlend3216(SkPMColor src, U16CPU dst, unsigned scale) {
     SkASSERT(scale <= 32);
     scale <<= 3;
 
@@ -154,8 +135,7 @@
 }
 
 void SkAvoidXfermode::xfer16(uint16_t dst[], const SkPMColor src[], int count,
-                             const SkAlpha aa[])
-{
+                             const SkAlpha aa[]) const {
     unsigned    opR = SkColorGetR(fOpColor) >> (8 - SK_R16_BITS);
     unsigned    opG = SkColorGetG(fOpColor) >> (8 - SK_G16_BITS);
     unsigned    opB = SkColorGetB(fOpColor) >> (8 - SK_R16_BITS);
@@ -195,8 +175,7 @@
 }
 
 void SkAvoidXfermode::xfer4444(uint16_t dst[], const SkPMColor src[], int count,
-                               const SkAlpha aa[])
-{
+                               const SkAlpha aa[]) const {
     unsigned    opR = SkColorGetR(fOpColor) >> 4;
     unsigned    opG = SkColorGetG(fOpColor) >> 4;
     unsigned    opB = SkColorGetB(fOpColor) >> 4;
@@ -235,9 +214,19 @@
     }
 }
 
-void SkAvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count, const SkAlpha aa[])
-{
+void SkAvoidXfermode::xferA8(SkAlpha dst[], const SkPMColor src[], int count,
+                             const SkAlpha aa[]) const {
     // override in subclass
 }
 
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkAvoidXfermode)
+#ifdef SK_DEVELOPER
+void SkAvoidXfermode::toString(SkString* str) const {
+    str->append("SkAvoidXfermode: opColor: ");
+    str->appendHex(fOpColor);
+    str->appendf("distMul: %d ", fDistMul);
+
+    static const char* gModeStrings[] = { "Avoid", "Target" };
+
+    str->appendf("mode: %s", gModeStrings[fMode]);
+}
+#endif
diff --git a/src/effects/SkBicubicImageFilter.cpp b/src/effects/SkBicubicImageFilter.cpp
new file mode 100644
index 0000000..0670c4d
--- /dev/null
+++ b/src/effects/SkBicubicImageFilter.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBicubicImageFilter.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMatrix.h"
+#include "SkRect.h"
+#include "SkUnPreMultiply.h"
+
+#if SK_SUPPORT_GPU
+#include "gl/GrGLEffectMatrix.h"
+#include "effects/GrSingleTextureEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "SkImageFilterUtils.h"
+#endif
+
+SkBicubicImageFilter::SkBicubicImageFilter(const SkSize& scale, const SkScalar coefficients[16], SkImageFilter* input)
+  : INHERITED(input),
+    fScale(scale) {
+    memcpy(fCoefficients, coefficients, sizeof(fCoefficients));
+}
+
+#define DS(x) SkDoubleToScalar(x)
+
+SkBicubicImageFilter* SkBicubicImageFilter::CreateMitchell(const SkSize& scale,
+                                                           SkImageFilter* input) {
+    static const SkScalar coefficients[16] = {
+        DS( 1.0 / 18.0), DS(-9.0 / 18.0), DS( 15.0 / 18.0), DS( -7.0 / 18.0),
+        DS(16.0 / 18.0), DS( 0.0 / 18.0), DS(-36.0 / 18.0), DS( 21.0 / 18.0),
+        DS( 1.0 / 18.0), DS( 9.0 / 18.0), DS( 27.0 / 18.0), DS(-21.0 / 18.0),
+        DS( 0.0 / 18.0), DS( 0.0 / 18.0), DS( -6.0 / 18.0), DS(  7.0 / 18.0),
+    };
+    return SkNEW_ARGS(SkBicubicImageFilter, (scale, coefficients, input));
+}
+
+SkBicubicImageFilter::SkBicubicImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    SkDEBUGCODE(uint32_t readSize =) buffer.readScalarArray(fCoefficients);
+    SkASSERT(readSize == 16);
+    fScale.fWidth = buffer.readScalar();
+    fScale.fHeight = buffer.readScalar();
+}
+
+void SkBicubicImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalarArray(fCoefficients, 16);
+    buffer.writeScalar(fScale.fWidth);
+    buffer.writeScalar(fScale.fHeight);
+}
+
+SkBicubicImageFilter::~SkBicubicImageFilter() {
+}
+
+inline SkPMColor cubicBlend(const SkScalar c[16], SkScalar t, SkPMColor c0, SkPMColor c1, SkPMColor c2, SkPMColor c3) {
+    SkScalar t2 = t * t, t3 = t2 * t;
+    SkScalar cc[4];
+    // FIXME:  For the fractx case, this should be refactored out of this function.
+    cc[0] = c[0]  + SkScalarMul(c[1], t) + SkScalarMul(c[2], t2) + SkScalarMul(c[3], t3);
+    cc[1] = c[4]  + SkScalarMul(c[5], t) + SkScalarMul(c[6], t2) + SkScalarMul(c[7], t3);
+    cc[2] = c[8]  + SkScalarMul(c[9], t) + SkScalarMul(c[10], t2) + SkScalarMul(c[11], t3);
+    cc[3] = c[12] + SkScalarMul(c[13], t) + SkScalarMul(c[14], t2) + SkScalarMul(c[15], t3);
+    SkScalar a = SkScalarMul(cc[0], SkGetPackedA32(c0)) + SkScalarMul(cc[1], SkGetPackedA32(c1)) + SkScalarMul(cc[2], SkGetPackedA32(c2)) + SkScalarMul(cc[3], SkGetPackedA32(c3));
+    SkScalar r = SkScalarMul(cc[0], SkGetPackedR32(c0)) + SkScalarMul(cc[1], SkGetPackedR32(c1)) + SkScalarMul(cc[2], SkGetPackedR32(c2)) + SkScalarMul(cc[3], SkGetPackedR32(c3));
+    SkScalar g = SkScalarMul(cc[0], SkGetPackedG32(c0)) + SkScalarMul(cc[1], SkGetPackedG32(c1)) + SkScalarMul(cc[2], SkGetPackedG32(c2)) + SkScalarMul(cc[3], SkGetPackedG32(c3));
+    SkScalar b = SkScalarMul(cc[0], SkGetPackedB32(c0)) + SkScalarMul(cc[1], SkGetPackedB32(c1)) + SkScalarMul(cc[2], SkGetPackedB32(c2)) + SkScalarMul(cc[3], SkGetPackedB32(c3));
+    return SkPackARGB32(SkScalarRoundToInt(SkScalarClampMax(a, 255)), SkScalarRoundToInt(SkScalarClampMax(r, 255)), SkScalarRoundToInt(SkScalarClampMax(g, 255)), SkScalarRoundToInt(SkScalarClampMax(b, 255)));
+}
+
+bool SkBicubicImageFilter::onFilterImage(Proxy* proxy,
+                                         const SkBitmap& source,
+                                         const SkMatrix& matrix,
+                                         SkBitmap* result,
+                                         SkIPoint* loc) {
+    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
+    if (src.config() != SkBitmap::kARGB_8888_Config) {
+        return false;
+    }
+
+    SkAutoLockPixels alp(src);
+    if (!src.getPixels()) {
+        return false;
+    }
+
+    SkRect dstRect = SkRect::MakeWH(SkScalarMul(SkIntToScalar(src.width()), fScale.fWidth),
+                                    SkScalarMul(SkIntToScalar(src.height()), fScale.fHeight));
+    SkIRect dstIRect;
+    dstRect.roundOut(&dstIRect);
+    result->setConfig(src.config(), dstIRect.width(), dstIRect.height());
+    result->allocPixels();
+    if (!result->getPixels()) {
+        return false;
+    }
+
+    SkRect srcRect;
+    src.getBounds(&srcRect);
+    SkMatrix inverse;
+    inverse.setRectToRect(dstRect, srcRect, SkMatrix::kFill_ScaleToFit);
+    inverse.postTranslate(SkFloatToScalar(-0.5f), SkFloatToScalar(-0.5f));
+
+    for (int y = dstIRect.fTop; y < dstIRect.fBottom; ++y) {
+        SkPMColor* dptr = result->getAddr32(dstIRect.fLeft, y);
+        for (int x = dstIRect.fLeft; x < dstIRect.fRight; ++x) {
+            SkPoint srcPt, dstPt = SkPoint::Make(SkIntToScalar(x), SkIntToScalar(y));
+            inverse.mapPoints(&srcPt, &dstPt, 1);
+            SkScalar fractx = srcPt.fX - SkScalarFloorToScalar(srcPt.fX);
+            SkScalar fracty = srcPt.fY - SkScalarFloorToScalar(srcPt.fY);
+            int sx = SkScalarFloorToInt(srcPt.fX);
+            int sy = SkScalarFloorToInt(srcPt.fY);
+            int x0 = SkClampMax(sx - 1, src.width() - 1);
+            int x1 = SkClampMax(sx    , src.width() - 1);
+            int x2 = SkClampMax(sx + 1, src.width() - 1);
+            int x3 = SkClampMax(sx + 2, src.width() - 1);
+            int y0 = SkClampMax(sy - 1, src.height() - 1);
+            int y1 = SkClampMax(sy    , src.height() - 1);
+            int y2 = SkClampMax(sy + 1, src.height() - 1);
+            int y3 = SkClampMax(sy + 2, src.height() - 1);
+            SkPMColor s00 = *src.getAddr32(x0, y0);
+            SkPMColor s10 = *src.getAddr32(x1, y0);
+            SkPMColor s20 = *src.getAddr32(x2, y0);
+            SkPMColor s30 = *src.getAddr32(x3, y0);
+            SkPMColor s0 = cubicBlend(fCoefficients, fractx, s00, s10, s20, s30);
+            SkPMColor s01 = *src.getAddr32(x0, y1);
+            SkPMColor s11 = *src.getAddr32(x1, y1);
+            SkPMColor s21 = *src.getAddr32(x2, y1);
+            SkPMColor s31 = *src.getAddr32(x3, y1);
+            SkPMColor s1 = cubicBlend(fCoefficients, fractx, s01, s11, s21, s31);
+            SkPMColor s02 = *src.getAddr32(x0, y2);
+            SkPMColor s12 = *src.getAddr32(x1, y2);
+            SkPMColor s22 = *src.getAddr32(x2, y2);
+            SkPMColor s32 = *src.getAddr32(x3, y2);
+            SkPMColor s2 = cubicBlend(fCoefficients, fractx, s02, s12, s22, s32);
+            SkPMColor s03 = *src.getAddr32(x0, y3);
+            SkPMColor s13 = *src.getAddr32(x1, y3);
+            SkPMColor s23 = *src.getAddr32(x2, y3);
+            SkPMColor s33 = *src.getAddr32(x3, y3);
+            SkPMColor s3 = cubicBlend(fCoefficients, fractx, s03, s13, s23, s33);
+            *dptr++ = cubicBlend(fCoefficients, fracty, s0, s1, s2, s3);
+        }
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+class GrGLBicubicEffect;
+
+class GrBicubicEffect : public GrSingleTextureEffect {
+public:
+    virtual ~GrBicubicEffect();
+
+    static const char* Name() { return "Bicubic"; }
+    const float* coefficients() const { return fCoefficients; }
+
+    typedef GrGLBicubicEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    static GrEffectRef* Create(GrTexture* tex, const SkScalar coefficients[16]) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrBicubicEffect, (tex, coefficients)));
+        return CreateEffectRef(effect);
+    }
+
+private:
+    GrBicubicEffect(GrTexture*, const SkScalar coefficients[16]);
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+    float    fCoefficients[16];
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+class GrGLBicubicEffect : public GrGLEffect {
+public:
+    GrGLBicubicEffect(const GrBackendEffectFactory& factory,
+                      const GrEffectRef& effect);
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+private:
+    typedef GrGLUniformManager::UniformHandle        UniformHandle;
+
+    UniformHandle       fCoefficientsUni;
+    UniformHandle       fImageIncrementUni;
+
+    GrGLEffectMatrix    fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLBicubicEffect::GrGLBicubicEffect(const GrBackendEffectFactory& factory,
+                                     const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fCoefficientsUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle) {
+}
+
+void GrGLBicubicEffect::emitCode(GrGLShaderBuilder* builder,
+                                 const GrEffectStage&,
+                                 EffectKey key,
+                                 const char* vertexCoords,
+                                 const char* outputColor,
+                                 const char* inputColor,
+                                 const TextureSamplerArray& samplers) {
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+    fCoefficientsUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                           kMat44f_GrSLType, "Coefficients");
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "ImageIncrement");
+    SkString* code = &builder->fFSCode;
+
+    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+    const char* coeff = builder->getUniformCStr(fCoefficientsUni);
+
+    SkString cubicBlendName;
+
+    static const GrGLShaderVar gCubicBlendArgs[] = {
+        GrGLShaderVar("coefficients",  kMat44f_GrSLType),
+        GrGLShaderVar("t",             kFloat_GrSLType),
+        GrGLShaderVar("c0",            kVec4f_GrSLType),
+        GrGLShaderVar("c1",            kVec4f_GrSLType),
+        GrGLShaderVar("c2",            kVec4f_GrSLType),
+        GrGLShaderVar("c3",            kVec4f_GrSLType),
+    };
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kVec4f_GrSLType,
+                          "cubicBlend",
+                          SK_ARRAY_COUNT(gCubicBlendArgs),
+                          gCubicBlendArgs,
+                          "\tvec4 ts = vec4(1.0, t, t * t, t * t * t);\n"
+                          "\tvec4 c = coefficients * ts;\n"
+                          "\treturn c.x * c0 + c.y * c1 + c.z * c2 + c.w * c3;\n",
+                          &cubicBlendName);
+    code->appendf("\tvec2 coord = %s - %s * vec2(0.5, 0.5);\n", coords, imgInc);
+    code->appendf("\tvec2 f = fract(coord / %s);\n", imgInc);
+    for (int y = 0; y < 4; ++y) {
+        for (int x = 0; x < 4; ++x) {
+            SkString coord;
+            coord.printf("coord + %s * vec2(%d, %d)", imgInc, x - 1, y - 1);
+            code->appendf("\tvec4 s%d%d = ", x, y);
+            builder->appendTextureLookup(&builder->fFSCode, samplers[0], coord.c_str());
+            code->appendf(";\n");
+        }
+        code->appendf("\tvec4 s%d = %s(%s, f.x, s0%d, s1%d, s2%d, s3%d);\n", y, cubicBlendName.c_str(), coeff, y, y, y, y);
+    }
+    code->appendf("\t%s = %s(%s, f.y, s0, s1, s2, s3);\n", outputColor, cubicBlendName.c_str(), coeff);
+}
+
+GrGLEffect::EffectKey GrGLBicubicEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
+    const GrBicubicEffect& m = GetEffectFromStage<GrBicubicEffect>(s);
+    EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
+                                                   s.getCoordChangeMatrix(),
+                                                   m.texture(0));
+    return matrixKey;
+}
+
+void GrGLBicubicEffect::setData(const GrGLUniformManager& uman,
+                                const GrEffectStage& stage) {
+    const GrBicubicEffect& effect = GetEffectFromStage<GrBicubicEffect>(stage);
+    GrTexture& texture = *effect.texture(0);
+    float imageIncrement[2];
+    imageIncrement[0] = 1.0f / texture.width();
+    imageIncrement[1] = 1.0f / texture.height();
+    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+    uman.setMatrix4f(fCoefficientsUni, effect.coefficients());
+    fEffectMatrix.setData(uman,
+                          effect.getMatrix(),
+                          stage.getCoordChangeMatrix(),
+                          effect.texture(0));
+}
+
+GrBicubicEffect::GrBicubicEffect(GrTexture* texture,
+                                 const SkScalar coefficients[16])
+  : INHERITED(texture, MakeDivByTextureWHMatrix(texture)) {
+    for (int y = 0; y < 4; y++) {
+        for (int x = 0; x < 4; x++) {
+            // Convert from row-major scalars to column-major floats.
+            fCoefficients[x * 4 + y] = SkScalarToFloat(coefficients[y * 4 + x]);
+        }
+    }
+}
+
+GrBicubicEffect::~GrBicubicEffect() {
+}
+
+const GrBackendEffectFactory& GrBicubicEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrBicubicEffect>::getInstance();
+}
+
+bool GrBicubicEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrBicubicEffect& s = CastEffect<GrBicubicEffect>(sBase);
+    return this->texture(0) == s.texture(0) &&
+           !memcmp(fCoefficients, s.coefficients(), 16);
+}
+
+void GrBicubicEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // FIXME:  Perhaps we can do better.
+    *validFlags = 0;
+    return;
+}
+
+GR_DEFINE_EFFECT_TEST(GrBicubicEffect);
+
+GrEffectRef* GrBicubicEffect::TestCreate(SkRandom* random,
+                                         GrContext* context,
+                                         GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    SkScalar coefficients[16];
+    for (int i = 0; i < 16; i++) {
+        coefficients[i] = random->nextSScalar1();
+    }
+    return GrBicubicEffect::Create(textures[texIdx], coefficients);
+}
+
+bool SkBicubicImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap srcBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &srcBM)) {
+        return false;
+    }
+    GrTexture* srcTexture = (GrTexture*) srcBM.getTexture();
+    GrContext* context = srcTexture->getContext();
+
+    SkRect dstRect = SkRect::MakeWH(srcBM.width() * fScale.fWidth,
+                                    srcBM.height() * fScale.fHeight);
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = SkScalarCeilToInt(dstRect.width());
+    desc.fHeight = SkScalarCeilToInt(dstRect.height());
+    desc.fConfig = kSkia8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    SkAutoTUnref<GrTexture> dst(ast.detach());
+    if (!dst) {
+        return false;
+    }
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(GrBicubicEffect::Create(srcTexture, fCoefficients))->unref();
+    SkRect srcRect;
+    srcBM.getBounds(&srcRect);
+    context->drawRectToRect(paint, dstRect, srcRect);
+    return SkImageFilterUtils::WrapTexture(dst, desc.fWidth, desc.fHeight, result);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/effects/SkBitmapCache.cpp b/src/effects/SkBitmapCache.cpp
deleted file mode 100644
index f4b7b91..0000000
--- a/src/effects/SkBitmapCache.cpp
+++ /dev/null
@@ -1,154 +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 "SkBitmapCache.h"
-
-struct SkBitmapCache::Entry {
-    Entry*      fPrev;
-    Entry*      fNext;
-
-    void*       fBuffer;
-    size_t      fSize;
-    SkBitmap    fBitmap;
-
-    Entry(const void* buffer, size_t size, const SkBitmap& bm)
-            : fPrev(NULL),
-              fNext(NULL),
-              fBitmap(bm) {
-        fBuffer = sk_malloc_throw(size);
-        fSize = size;
-        memcpy(fBuffer, buffer, size);
-    }
-
-    ~Entry() { sk_free(fBuffer); }
-
-    bool equals(const void* buffer, size_t size) const {
-        return (fSize == size) && !memcmp(fBuffer, buffer, size);
-    }
-};
-
-SkBitmapCache::SkBitmapCache(int max) : fMaxEntries(max) {
-    fEntryCount = 0;
-    fHead = fTail = NULL;
-
-    this->validate();
-}
-
-SkBitmapCache::~SkBitmapCache() {
-    this->validate();
-
-    Entry* entry = fHead;
-    while (entry) {
-        Entry* next = entry->fNext;
-        delete entry;
-        entry = next;
-    }
-}
-
-SkBitmapCache::Entry* SkBitmapCache::detach(Entry* entry) const {
-    if (entry->fPrev) {
-        SkASSERT(fHead != entry);
-        entry->fPrev->fNext = entry->fNext;
-    } else {
-        SkASSERT(fHead == entry);
-        fHead = entry->fNext;
-    }
-    if (entry->fNext) {
-        SkASSERT(fTail != entry);
-        entry->fNext->fPrev = entry->fPrev;
-    } else {
-        SkASSERT(fTail == entry);
-        fTail = entry->fPrev;
-    }
-    return entry;
-}
-
-void SkBitmapCache::attachToHead(Entry* entry) const {
-    entry->fPrev = NULL;
-    entry->fNext = fHead;
-    if (fHead) {
-        fHead->fPrev = entry;
-    } else {
-        fTail = entry;
-    }
-    fHead = entry;
-}
-
-bool SkBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const {
-    AutoValidate av(this);
-
-    Entry* entry = fHead;
-    while (entry) {
-        if (entry->equals(buffer, size)) {
-            if (bm) {
-                *bm = entry->fBitmap;
-            }
-            // move to the head of our list, so we purge it last
-            this->detach(entry);
-            this->attachToHead(entry);
-            return true;
-        }
-        entry = entry->fNext;
-    }
-    return false;
-}
-
-void SkBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) {
-    AutoValidate av(this);
-
-    if (fEntryCount == fMaxEntries) {
-        SkASSERT(fTail);
-        delete this->detach(fTail);
-        fEntryCount -= 1;
-    }
-
-    Entry* entry = new Entry(buffer, len, bm);
-    this->attachToHead(entry);
-    fEntryCount += 1;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#ifdef SK_DEBUG
-
-void SkBitmapCache::validate() const {
-    SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries);
-
-    if (fEntryCount > 0) {
-        SkASSERT(NULL == fHead->fPrev);
-        SkASSERT(NULL == fTail->fNext);
-
-        if (fEntryCount == 1) {
-            SkASSERT(fHead == fTail);
-        } else {
-            SkASSERT(fHead != fTail);
-        }
-
-        Entry* entry = fHead;
-        int count = 0;
-        while (entry) {
-            count += 1;
-            entry = entry->fNext;
-        }
-        SkASSERT(count == fEntryCount);
-
-        entry = fTail;
-        while (entry) {
-            count -= 1;
-            entry = entry->fPrev;
-        }
-        SkASSERT(0 == count);
-    } else {
-        SkASSERT(NULL == fHead);
-        SkASSERT(NULL == fTail);
-    }
-}
-
-#endif
-
diff --git a/src/effects/SkBitmapCache.h b/src/effects/SkBitmapCache.h
deleted file mode 100644
index ea9cf91..0000000
--- a/src/effects/SkBitmapCache.h
+++ /dev/null
@@ -1,50 +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 SkBitmapCache_DEFINED
-#define SkBitmapCache_DEFINED
-
-#include "SkBitmap.h"
-
-class SkBitmapCache : SkNoncopyable {
-public:
-    SkBitmapCache(int maxEntries);
-    ~SkBitmapCache();
-
-    bool find(const void* buffer, size_t len, SkBitmap*) const;
-    void add(const void* buffer, size_t len, const SkBitmap&);
-
-private:
-    int fEntryCount;
-    const int fMaxEntries;
-
-    struct Entry;
-    mutable Entry*  fHead;
-    mutable Entry*  fTail;
-
-    inline Entry* detach(Entry*) const;
-    inline void attachToHead(Entry*) const;
-
-#ifdef SK_DEBUG
-    void validate() const;
-#else
-    void validate() const {}
-#endif
-
-    class AutoValidate : SkNoncopyable {
-    public:
-        AutoValidate(const SkBitmapCache* bc) : fBC(bc) { bc->validate(); }
-        ~AutoValidate() { fBC->validate(); }
-    private:
-        const SkBitmapCache* fBC;
-    };
-};
-
-#endif
-
diff --git a/src/effects/SkBitmapSource.cpp b/src/effects/SkBitmapSource.cpp
new file mode 100644
index 0000000..854df9d
--- /dev/null
+++ b/src/effects/SkBitmapSource.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2012 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 "SkBitmapSource.h"
+
+SkBitmapSource::SkBitmapSource(const SkBitmap& bitmap)
+  : INHERITED(0),
+    fBitmap(bitmap) {
+}
+
+SkBitmapSource::SkBitmapSource(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    fBitmap.unflatten(buffer);
+}
+
+void SkBitmapSource::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    fBitmap.flatten(buffer);
+}
+
+bool SkBitmapSource::onFilterImage(Proxy*, const SkBitmap&, const SkMatrix&,
+                                   SkBitmap* result, SkIPoint* offset) {
+    *result = fBitmap;
+    return true;
+}
diff --git a/src/effects/SkBlendImageFilter.cpp b/src/effects/SkBlendImageFilter.cpp
new file mode 100644
index 0000000..3f6d3c5
--- /dev/null
+++ b/src/effects/SkBlendImageFilter.cpp
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2012 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 "SkBlendImageFilter.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "GrTBackendEffectFactory.h"
+#include "SkImageFilterUtils.h"
+#endif
+
+namespace {
+
+SkXfermode::Mode modeToXfermode(SkBlendImageFilter::Mode mode)
+{
+    switch (mode) {
+      case SkBlendImageFilter::kNormal_Mode:
+        return SkXfermode::kSrcOver_Mode;
+      case SkBlendImageFilter::kMultiply_Mode:
+        return SkXfermode::kModulate_Mode;
+      case SkBlendImageFilter::kScreen_Mode:
+        return SkXfermode::kScreen_Mode;
+      case SkBlendImageFilter::kDarken_Mode:
+        return SkXfermode::kDarken_Mode;
+      case SkBlendImageFilter::kLighten_Mode:
+        return SkXfermode::kLighten_Mode;
+    }
+    SkASSERT(0);
+    return SkXfermode::kSrcOver_Mode;
+}
+
+SkPMColor multiply_proc(SkPMColor src, SkPMColor dst) {
+    int omsa = 255 - SkGetPackedA32(src);
+    int sr = SkGetPackedR32(src), sg = SkGetPackedG32(src), sb = SkGetPackedB32(src);
+    int omda = 255 - SkGetPackedA32(dst);
+    int dr = SkGetPackedR32(dst), dg = SkGetPackedG32(dst), db = SkGetPackedB32(dst);
+    int a = 255 - SkMulDiv255Round(omsa, omda);
+    int r = SkMulDiv255Round(omsa, dr) + SkMulDiv255Round(omda, sr) + SkMulDiv255Round(sr, dr);
+    int g = SkMulDiv255Round(omsa, dg) + SkMulDiv255Round(omda, sg) + SkMulDiv255Round(sg, dg);
+    int b = SkMulDiv255Round(omsa, db) + SkMulDiv255Round(omda, sb) + SkMulDiv255Round(sb, db);
+    return SkPackARGB32(a, r, g, b);
+}
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkBlendImageFilter::SkBlendImageFilter(SkBlendImageFilter::Mode mode, SkImageFilter* background, SkImageFilter* foreground)
+  : INHERITED(background, foreground), fMode(mode)
+{
+}
+
+SkBlendImageFilter::~SkBlendImageFilter() {
+}
+
+SkBlendImageFilter::SkBlendImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer)
+{
+    fMode = (SkBlendImageFilter::Mode) buffer.readInt();
+}
+
+void SkBlendImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt((int) fMode);
+}
+
+bool SkBlendImageFilter::onFilterImage(Proxy* proxy,
+                                       const SkBitmap& src,
+                                       const SkMatrix& ctm,
+                                       SkBitmap* dst,
+                                       SkIPoint* offset) {
+    SkBitmap background, foreground = src;
+    SkImageFilter* backgroundInput = getBackgroundInput();
+    SkImageFilter* foregroundInput = getForegroundInput();
+    SkASSERT(NULL != backgroundInput);
+    if (!backgroundInput->filterImage(proxy, src, ctm, &background, offset)) {
+        return false;
+    }
+    if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, offset)) {
+        return false;
+    }
+    SkAutoLockPixels alp_foreground(foreground), alp_background(background);
+    if (!foreground.getPixels() || !background.getPixels()) {
+        return false;
+    }
+    dst->setConfig(background.config(), background.width(), background.height());
+    dst->allocPixels();
+    SkCanvas canvas(*dst);
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    canvas.drawBitmap(background, 0, 0, &paint);
+    // FEBlend's multiply mode is (1 - Sa) * Da + (1 - Da) * Sc + Sc * Dc
+    // Skia's is just Sc * Dc.  So we use a custom proc to implement FEBlend's
+    // version.
+    if (fMode == SkBlendImageFilter::kMultiply_Mode) {
+        paint.setXfermode(new SkProcXfermode(multiply_proc))->unref();
+    } else {
+        paint.setXfermodeMode(modeToXfermode(fMode));
+    }
+    canvas.drawBitmap(foreground, 0, 0, &paint);
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+class GrGLBlendEffect : public GrGLEffect {
+public:
+    GrGLBlendEffect(const GrBackendEffectFactory& factory,
+                    const GrEffectRef& effect);
+    virtual ~GrGLBlendEffect();
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
+
+private:
+    SkBlendImageFilter::Mode    fMode;
+    GrGLEffectMatrix            fForegroundEffectMatrix;
+    GrGLEffectMatrix            fBackgroundEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrBlendEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(SkBlendImageFilter::Mode mode,
+                               GrTexture* foreground,
+                               GrTexture* background) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrBlendEffect, (mode, foreground, background)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrBlendEffect();
+
+    const GrBackendEffectFactory& getFactory() const;
+    SkBlendImageFilter::Mode mode() const { return fMode; }
+
+    typedef GrGLBlendEffect GLEffect;
+    static const char* Name() { return "Blend"; }
+
+    void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrBlendEffect(SkBlendImageFilter::Mode mode, GrTexture* foreground, GrTexture* background);
+    GrTextureAccess             fForegroundAccess;
+    GrTextureAccess             fBackgroundAccess;
+    SkBlendImageFilter::Mode    fMode;
+
+    typedef GrEffect INHERITED;
+};
+
+bool SkBlendImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap backgroundBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getBackgroundInput(), proxy, src, &backgroundBM)) {
+        return false;
+    }
+    GrTexture* background = (GrTexture*) backgroundBM.getTexture();
+    SkBitmap foregroundBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getForegroundInput(), proxy, src, &foregroundBM)) {
+        return false;
+    }
+    GrTexture* foreground = (GrTexture*) foregroundBM.getTexture();
+    GrContext* context = foreground->getContext();
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = src.width();
+    desc.fHeight = src.height();
+    desc.fConfig = kSkia8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    SkAutoTUnref<GrTexture> dst(ast.detach());
+
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(
+        GrBlendEffect::Create(fMode, foreground, background))->unref();
+    SkRect srcRect;
+    src.getBounds(&srcRect);
+    context->drawRect(paint, srcRect);
+    return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrBlendEffect::GrBlendEffect(SkBlendImageFilter::Mode mode,
+                             GrTexture* foreground,
+                             GrTexture* background)
+    : fForegroundAccess(foreground)
+    , fBackgroundAccess(background)
+    , fMode(mode) {
+    this->addTextureAccess(&fForegroundAccess);
+    this->addTextureAccess(&fBackgroundAccess);
+}
+
+GrBlendEffect::~GrBlendEffect() {
+}
+
+bool GrBlendEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrBlendEffect& s = CastEffect<GrBlendEffect>(sBase);
+    return fForegroundAccess.getTexture() == s.fForegroundAccess.getTexture() &&
+           fBackgroundAccess.getTexture() == s.fBackgroundAccess.getTexture() &&
+           fMode == s.fMode;
+}
+
+const GrBackendEffectFactory& GrBlendEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrBlendEffect>::getInstance();
+}
+
+void GrBlendEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // The output alpha is always 1 - (1 - FGa) * (1 - BGa). So if either FGa or BGa is known to
+    // be one then the output alpha is one. (This effect ignores its input. We should have a way to
+    // communicate this.)
+    if (GrPixelConfigIsOpaque(fForegroundAccess.getTexture()->config()) ||
+        GrPixelConfigIsOpaque(fBackgroundAccess.getTexture()->config())) {
+        *validFlags = kA_ValidComponentFlag;
+        *color = GrColorPackRGBA(0, 0, 0, 0xff);
+    } else {
+        *validFlags = 0;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLBlendEffect::GrGLBlendEffect(const GrBackendEffectFactory& factory, const GrEffectRef& effect)
+    : INHERITED(factory),
+      fMode(CastEffect<GrBlendEffect>(effect).mode()) {
+}
+
+GrGLBlendEffect::~GrGLBlendEffect() {
+}
+
+void GrGLBlendEffect::emitCode(GrGLShaderBuilder* builder,
+                               const GrEffectStage&,
+                               EffectKey key,
+                               const char* vertexCoords,
+                               const char* outputColor,
+                               const char* inputColor,
+                               const TextureSamplerArray& samplers) {
+    const char* fgCoords;
+    const char* bgCoords;
+    GrSLType fgCoordsType =  fForegroundEffectMatrix.emitCode(
+        builder, key, vertexCoords, &fgCoords, NULL, "FG");
+    GrSLType bgCoordsType =  fBackgroundEffectMatrix.emitCode(
+        builder, key, vertexCoords, &bgCoords, NULL, "BG");
+
+    SkString* code = &builder->fFSCode;
+    const char* bgColor = "bgColor";
+    const char* fgColor = "fgColor";
+
+    code->appendf("\t\tvec4 %s = ", fgColor);
+    builder->appendTextureLookup(code, samplers[0], fgCoords, fgCoordsType);
+    code->append(";\n");
+
+    code->appendf("\t\tvec4 %s = ", bgColor);
+    builder->appendTextureLookup(code, samplers[1], bgCoords, bgCoordsType);
+    code->append(";\n");
+
+    code->appendf("\t\t%s.a = 1.0 - (1.0 - %s.a) * (1.0 - %s.b);\n", outputColor, bgColor, fgColor);
+    switch (fMode) {
+      case SkBlendImageFilter::kNormal_Mode:
+        code->appendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + %s.rgb;\n", outputColor, fgColor, bgColor, fgColor);
+        break;
+      case SkBlendImageFilter::kMultiply_Mode:
+        code->appendf("\t\t%s.rgb = (1.0 - %s.a) * %s.rgb + (1.0 - %s.a) * %s.rgb + %s.rgb * %s.rgb;\n", outputColor, fgColor, bgColor, bgColor, fgColor, fgColor, bgColor);
+        break;
+      case SkBlendImageFilter::kScreen_Mode:
+        code->appendf("\t\t%s.rgb = %s.rgb + %s.rgb - %s.rgb * %s.rgb;\n", outputColor, bgColor, fgColor, fgColor, bgColor);
+        break;
+      case SkBlendImageFilter::kDarken_Mode:
+        code->appendf("\t\t%s.rgb = min((1.0 - %s.a) * %s.rgb + %s.rgb, (1.0 - %s.a) * %s.rgb + %s.rgb);\n", outputColor, fgColor, bgColor, fgColor, bgColor, fgColor, bgColor);
+        break;
+      case SkBlendImageFilter::kLighten_Mode:
+        code->appendf("\t\t%s.rgb = max((1.0 - %s.a) * %s.rgb + %s.rgb, (1.0 - %s.a) * %s.rgb + %s.rgb);\n", outputColor, fgColor, bgColor, fgColor, bgColor, fgColor, bgColor);
+        break;
+    }
+}
+
+void GrGLBlendEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage);
+    GrTexture* fgTex = blend.texture(0);
+    GrTexture* bgTex = blend.texture(1);
+    fForegroundEffectMatrix.setData(uman,
+                                    GrEffect::MakeDivByTextureWHMatrix(fgTex),
+                                    stage.getCoordChangeMatrix(),
+                                    fgTex);
+    fBackgroundEffectMatrix.setData(uman,
+                                    GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                    stage.getCoordChangeMatrix(),
+                                    bgTex);
+
+}
+
+GrGLEffect::EffectKey GrGLBlendEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+    const GrBlendEffect& blend = GetEffectFromStage<GrBlendEffect>(stage);
+
+    GrTexture* fgTex = blend.texture(0);
+    GrTexture* bgTex = blend.texture(1);
+
+    EffectKey fgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(fgTex),
+                                               stage.getCoordChangeMatrix(),
+                                               fgTex);
+
+    EffectKey bgKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(bgTex),
+                                               stage.getCoordChangeMatrix(),
+                                               bgTex);
+    bgKey <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey modeKey = blend.mode() << (2 * GrGLEffectMatrix::kKeyBits);
+
+    return  modeKey | bgKey | fgKey;
+}
+#endif
diff --git a/src/effects/SkBlurDrawLooper.cpp b/src/effects/SkBlurDrawLooper.cpp
index a44f081..b5a8754 100644
--- a/src/effects/SkBlurDrawLooper.cpp
+++ b/src/effects/SkBlurDrawLooper.cpp
@@ -8,13 +8,16 @@
 #include "SkBlurDrawLooper.h"
 #include "SkBlurMaskFilter.h"
 #include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkMaskFilter.h"
 #include "SkColorFilter.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMaskFilter.h"
+#include "SkPaint.h"
+#include "SkString.h"
+#include "SkStringUtils.h"
 
 SkBlurDrawLooper::SkBlurDrawLooper(SkScalar radius, SkScalar dx, SkScalar dy,
                                    SkColor color, uint32_t flags)
-    : fDx(dx), fDy(dy), fBlurColor(color), fBlurFlags(flags) {
+    : fDx(dx), fDy(dy), fBlurColor(color), fBlurFlags(flags), fState(kDone) {
 
     SkASSERT(flags <= kAll_BlurFlag);
     if (radius > 0) {
@@ -23,11 +26,11 @@
             SkBlurMaskFilter::kNone_BlurFlag;
 
         blurFlags |= flags & kHighQuality_BlurFlag ?
-            SkBlurMaskFilter::kHighQuality_BlurFlag : 
+            SkBlurMaskFilter::kHighQuality_BlurFlag :
             SkBlurMaskFilter::kNone_BlurFlag;
 
         fBlur = SkBlurMaskFilter::Create(radius,
-                                         SkBlurMaskFilter::kNormal_BlurStyle,  
+                                         SkBlurMaskFilter::kNormal_BlurStyle,
                                          blurFlags);
     } else {
         fBlur = NULL;
@@ -50,10 +53,10 @@
 
     fDx = buffer.readScalar();
     fDy = buffer.readScalar();
-    fBlurColor = buffer.readU32();
-    fBlur = static_cast<SkMaskFilter*>(buffer.readFlattenable());
-    fColorFilter = static_cast<SkColorFilter*>(buffer.readFlattenable());
-    fBlurFlags = buffer.readU32() & kAll_BlurFlag;
+    fBlurColor = buffer.readColor();
+    fBlur = buffer.readFlattenableT<SkMaskFilter>();
+    fColorFilter = buffer.readFlattenableT<SkColorFilter>();
+    fBlurFlags = buffer.readUInt() & kAll_BlurFlag;
 }
 
 SkBlurDrawLooper::~SkBlurDrawLooper() {
@@ -61,13 +64,14 @@
     SkSafeUnref(fColorFilter);
 }
 
-void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkBlurDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
     buffer.writeScalar(fDx);
     buffer.writeScalar(fDy);
-    buffer.write32(fBlurColor);
+    buffer.writeColor(fBlurColor);
     buffer.writeFlattenable(fBlur);
     buffer.writeFlattenable(fColorFilter);
-    buffer.write32(fBlurFlags);
+    buffer.writeUInt(fBlurFlags);
 }
 
 void SkBlurDrawLooper::init(SkCanvas* canvas) {
@@ -114,7 +118,34 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
+#ifdef SK_DEVELOPER
+void SkBlurDrawLooper::toString(SkString* str) const {
+    str->append("SkBlurDrawLooper: ");
 
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkBlurDrawLooper)
+    str->append("dx: ");
+    str->appendScalar(fDx);
 
+    str->append(" dy: ");
+    str->appendScalar(fDy);
+
+    str->append(" color: ");
+    str->appendHex(fBlurColor);
+
+    str->append(" flags: (");
+    if (kNone_BlurFlag == fBlurFlags) {
+        str->append("None");
+    } else {
+        bool needsSeparator = false;
+        SkAddFlagToString(str, SkToBool(kIgnoreTransform_BlurFlag & fBlurFlags), "IgnoreTransform",
+                          &needsSeparator);
+        SkAddFlagToString(str, SkToBool(kOverrideColor_BlurFlag & fBlurFlags), "OverrideColor",
+                          &needsSeparator);
+        SkAddFlagToString(str, SkToBool(kHighQuality_BlurFlag & fBlurFlags), "HighQuality",
+                          &needsSeparator);
+    }
+    str->append(")");
+
+    // TODO: add optional "fBlurFilter->toString(str);" when SkMaskFilter::toString is added
+    // alternatively we could cache the radius in SkBlurDrawLooper and just add it here
+}
+#endif
diff --git a/src/effects/SkBlurImageFilter.cpp b/src/effects/SkBlurImageFilter.cpp
index 1446e92..78359d2 100644
--- a/src/effects/SkBlurImageFilter.cpp
+++ b/src/effects/SkBlurImageFilter.cpp
@@ -5,8 +5,14 @@
  * found in the LICENSE file.
  */
 
+#include "SkBitmap.h"
 #include "SkBlurImageFilter.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "SkImageFilterUtils.h"
+#endif
 
 SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
   : INHERITED(buffer) {
@@ -14,17 +20,12 @@
     fSigma.fHeight = buffer.readScalar();
 }
 
-SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY)
-    : fSigma(SkSize::Make(sigmaX, sigmaY)) {
+SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input)
+    : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) {
     SkASSERT(sigmaX >= 0 && sigmaY >= 0);
 }
 
-bool SkBlurImageFilter::asABlur(SkSize* sigma) const {
-    *sigma = fSigma;
-    return true;
-}
-
-void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
     buffer.writeScalar(fSigma.fWidth);
     buffer.writeScalar(fSigma.fHeight);
@@ -133,9 +134,10 @@
     }
 }
 
-bool SkBlurImageFilter::onFilterImage(Proxy*,
-                                      const SkBitmap& src, const SkMatrix&,
-                                      SkBitmap* dst, SkIPoint*) {
+bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
+                                      const SkBitmap& source, const SkMatrix& ctm,
+                                      SkBitmap* dst, SkIPoint* offset) {
+    SkBitmap src = this->getInputResult(proxy, source, ctm, offset);
     if (src.config() != SkBitmap::kARGB_8888_Config) {
         return false;
     }
@@ -186,4 +188,20 @@
     return true;
 }
 
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkBlurImageFilter)
+bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+#if SK_SUPPORT_GPU
+    SkBitmap input;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &input)) {
+        return false;
+    }
+    GrTexture* source = (GrTexture*) input.getTexture();
+    SkRect rect;
+    src.getBounds(&rect);
+    SkAutoTUnref<GrTexture> tex(source->getContext()->gaussianBlur(source, false, rect,
+        fSigma.width(), fSigma.height()));
+    return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result);
+#else
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+#endif
+}
diff --git a/src/effects/SkBlurMask.cpp b/src/effects/SkBlurMask.cpp
index 95a1e6b..99bf2d5 100644
--- a/src/effects/SkBlurMask.cpp
+++ b/src/effects/SkBlurMask.cpp
@@ -12,6 +12,314 @@
 #include "SkTemplates.h"
 #include "SkEndian.h"
 
+// scale factor for the blur radius to match the behavior of the all existing blur
+// code (both on the CPU and the GPU).  This magic constant is  1/sqrt(3).
+
+// TODO: get rid of this fudge factor and move any required fudging up into
+// the calling library
+
+#define kBlurRadiusFudgeFactor SkFloatToScalar( .57735f )
+
+#define UNROLL_SEPARABLE_LOOPS
+
+/**
+ * This function performs a box blur in X, of the given radius.  If the
+ * "transpose" parameter is true, it will transpose the pixels on write,
+ * such that X and Y are swapped. Reads are always performed from contiguous
+ * memory in X, for speed. The destination buffer (dst) must be at least
+ * (width + leftRadius + rightRadius) * height bytes in size.
+ */
+static int boxBlur(const uint8_t* src, int src_y_stride, uint8_t* dst,
+                   int leftRadius, int rightRadius, int width, int height,
+                   bool transpose)
+{
+    int diameter = leftRadius + rightRadius;
+    int kernelSize = diameter + 1;
+    int border = SkMin32(width, diameter);
+    uint32_t scale = (1 << 24) / kernelSize;
+    int new_width = width + SkMax32(leftRadius, rightRadius) * 2;
+    int dst_x_stride = transpose ? height : 1;
+    int dst_y_stride = transpose ? 1 : new_width;
+    for (int y = 0; y < height; ++y) {
+        int sum = 0;
+        uint8_t* dptr = dst + y * dst_y_stride;
+        const uint8_t* right = src + y * src_y_stride;
+        const uint8_t* left = right;
+        for (int x = 0; x < rightRadius - leftRadius; x++) {
+            *dptr = 0;
+            dptr += dst_x_stride;
+        }
+#define LEFT_BORDER_ITER \
+            sum += *right++; \
+            *dptr = (sum * scale) >> 24; \
+            dptr += dst_x_stride;
+
+        int x = 0;
+#ifdef UNROLL_SEPARABLE_LOOPS
+        for (; x < border - 16; x += 16) {
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+        }
+#endif
+        for (; x < border; ++x) {
+            LEFT_BORDER_ITER
+        }
+#undef LEFT_BORDER_ITER
+#define TRIVIAL_ITER \
+            *dptr = (sum * scale) >> 24; \
+            dptr += dst_x_stride;
+        x = width;
+#ifdef UNROLL_SEPARABLE_LOOPS
+        for (; x < diameter - 16; x += 16) {
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+            TRIVIAL_ITER
+        }
+#endif
+        for (; x < diameter; ++x) {
+            TRIVIAL_ITER
+        }
+#undef TRIVIAL_ITER
+#define CENTER_ITER \
+            sum += *right++; \
+            *dptr = (sum * scale) >> 24; \
+            sum -= *left++; \
+            dptr += dst_x_stride;
+
+        x = diameter;
+#ifdef UNROLL_SEPARABLE_LOOPS
+        for (; x < width - 16; x += 16) {
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+        }
+#endif
+        for (; x < width; ++x) {
+            CENTER_ITER
+        }
+#undef CENTER_ITER
+#define RIGHT_BORDER_ITER \
+            *dptr = (sum * scale) >> 24; \
+            sum -= *left++; \
+            dptr += dst_x_stride;
+
+        x = 0;
+#ifdef UNROLL_SEPARABLE_LOOPS
+        for (; x < border - 16; x += 16) {
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+        }
+#endif
+        for (; x < border; ++x) {
+            RIGHT_BORDER_ITER
+        }
+#undef RIGHT_BORDER_ITER
+        for (int x = 0; x < leftRadius - rightRadius; x++) {
+            *dptr = 0;
+            dptr += dst_x_stride;
+        }
+        SkASSERT(sum == 0);
+    }
+    return new_width;
+}
+
+/**
+ * This variant of the box blur handles blurring of non-integer radii.  It
+ * keeps two running sums: an outer sum for the rounded-up kernel radius, and
+ * an inner sum for the rounded-down kernel radius.  For each pixel, it linearly
+ * interpolates between them.  In float this would be:
+ *  outer_weight * outer_sum / kernelSize +
+ *  (1.0 - outer_weight) * innerSum / (kernelSize - 2)
+ */
+static int boxBlurInterp(const uint8_t* src, int src_y_stride, uint8_t* dst,
+                         int radius, int width, int height,
+                         bool transpose, uint8_t outer_weight)
+{
+    int diameter = radius * 2;
+    int kernelSize = diameter + 1;
+    int border = SkMin32(width, diameter);
+    int inner_weight = 255 - outer_weight;
+    outer_weight += outer_weight >> 7;
+    inner_weight += inner_weight >> 7;
+    uint32_t outer_scale = (outer_weight << 16) / kernelSize;
+    uint32_t inner_scale = (inner_weight << 16) / (kernelSize - 2);
+    int new_width = width + diameter;
+    int dst_x_stride = transpose ? height : 1;
+    int dst_y_stride = transpose ? 1 : new_width;
+    for (int y = 0; y < height; ++y) {
+        int outer_sum = 0, inner_sum = 0;
+        uint8_t* dptr = dst + y * dst_y_stride;
+        const uint8_t* right = src + y * src_y_stride;
+        const uint8_t* left = right;
+        int x = 0;
+
+#define LEFT_BORDER_ITER \
+            inner_sum = outer_sum; \
+            outer_sum += *right++; \
+            *dptr = (outer_sum * outer_scale + inner_sum * inner_scale) >> 24; \
+            dptr += dst_x_stride;
+
+#ifdef UNROLL_SEPARABLE_LOOPS
+        for (;x < border - 16; x += 16) {
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+            LEFT_BORDER_ITER
+        }
+#endif
+
+        for (;x < border; x++) {
+            LEFT_BORDER_ITER
+        }
+#undef LEFT_BORDER_ITER
+        for (int x = width; x < diameter; ++x) {
+            *dptr = (outer_sum * outer_scale + inner_sum * inner_scale) >> 24;
+            dptr += dst_x_stride;
+        }
+        x = diameter;
+
+#define CENTER_ITER \
+            inner_sum = outer_sum - *left; \
+            outer_sum += *right++; \
+            *dptr = (outer_sum * outer_scale + inner_sum * inner_scale) >> 24; \
+            dptr += dst_x_stride; \
+            outer_sum -= *left++;
+
+#ifdef UNROLL_SEPARABLE_LOOPS
+        for (; x < width - 16; x += 16) {
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+            CENTER_ITER
+        }
+#endif
+        for (; x < width; ++x) {
+            CENTER_ITER
+        }
+#undef CENTER_ITER
+
+        #define RIGHT_BORDER_ITER \
+            inner_sum = outer_sum - *left++; \
+            *dptr = (outer_sum * outer_scale + inner_sum * inner_scale) >> 24; \
+            dptr += dst_x_stride; \
+            outer_sum = inner_sum;
+
+        x = 0;
+#ifdef UNROLL_SEPARABLE_LOOPS
+        for (; x < border - 16; x += 16) {
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+            RIGHT_BORDER_ITER
+        }
+#endif
+        for (; x < border; x++) {
+            RIGHT_BORDER_ITER
+        }
+#undef RIGHT_BORDER_ITER
+        SkASSERT(outer_sum == 0 && inner_sum == 0);
+    }
+    return new_width;
+}
+
+static void get_adjusted_radii(SkScalar passRadius, int *loRadius, int *hiRadius)
+{
+    *loRadius = *hiRadius = SkScalarCeil(passRadius);
+    if (SkIntToScalar(*hiRadius) - passRadius > SkFloatToScalar(0.5f)) {
+        *loRadius = *hiRadius - 1;
+    }
+}
+
 // Unrolling the integer blur kernel seems to give us a ~15% speedup on Windows,
 // breakeven on Mac, and ~15% slowdown on Linux.
 // Reading a word at a time when bulding the sum buffer seems to give
@@ -24,10 +332,10 @@
     src values at their position, plus all values above and to the left.
     When we sample into this buffer, we need an initial row and column of 0s,
     so we have an index correspondence as follows:
- 
+
     src[i, j] == sum[i+1, j+1]
     sum[0, j] == sum[i, 0] == 0
- 
+
     We assume that the sum buffer's stride == its width
  */
 static void build_sum_buffer(uint32_t sum[], int srcW, int srcH,
@@ -544,7 +852,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-// we use a local funciton to wrap the class static method to work around
+// we use a local function to wrap the class static method to work around
 // a bug in gcc98
 void SkMask_FreeImage(uint8_t* image);
 void SkMask_FreeImage(uint8_t* image) {
@@ -553,18 +861,20 @@
 
 bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
                       SkScalar radius, Style style, Quality quality,
-                      SkIPoint* margin)
+                      SkIPoint* margin, bool separable)
 {
     if (src.fFormat != SkMask::kA8_Format) {
         return false;
     }
 
     // Force high quality off for small radii (performance)
-    if (radius < SkIntToScalar(3)) quality = kLow_Quality;
+    if (radius < SkIntToScalar(3)) {
+        quality = kLow_Quality;
+    }
 
     // highQuality: use three box blur passes as a cheap way to approximate a Gaussian blur
-    int passCount = (quality == kHigh_Quality) ? 3 : 1;
-    SkScalar passRadius = SkScalarDiv(radius, SkScalarSqrt(SkIntToScalar(passCount)));
+    int passCount = (kHigh_Quality == quality) ? 3 : 1;
+    SkScalar passRadius = (kHigh_Quality == quality) ? SkScalarMul( radius, kBlurRadiusFudgeFactor): radius;
 
     int rx = SkScalarCeil(passRadius);
     int outer_weight = 255 - SkScalarRound((SkIntToScalar(rx) - passRadius) * 255);
@@ -602,7 +912,43 @@
         SkAutoTCallVProc<uint8_t, SkMask_FreeImage> autoCall(dp);
 
         // build the blurry destination
-        {
+        if (separable) {
+            SkAutoTMalloc<uint8_t>  tmpBuffer(dstSize);
+            uint8_t*                tp = tmpBuffer.get();
+            int w = sw, h = sh;
+
+            if (outer_weight == 255) {
+                int loRadius, hiRadius;
+                get_adjusted_radii(passRadius, &loRadius, &hiRadius);
+                if (kHigh_Quality == quality) {
+                    // Do three X blurs, with a transpose on the final one.
+                    w = boxBlur(sp, src.fRowBytes, tp, loRadius, hiRadius, w, h, false);
+                    w = boxBlur(tp, w,             dp, hiRadius, loRadius, w, h, false);
+                    w = boxBlur(dp, w,             tp, hiRadius, hiRadius, w, h, true);
+                    // Do three Y blurs, with a transpose on the final one.
+                    h = boxBlur(tp, h,             dp, loRadius, hiRadius, h, w, false);
+                    h = boxBlur(dp, h,             tp, hiRadius, loRadius, h, w, false);
+                    h = boxBlur(tp, h,             dp, hiRadius, hiRadius, h, w, true);
+                } else {
+                    w = boxBlur(sp, src.fRowBytes, tp, rx, rx, w, h, true);
+                    h = boxBlur(tp, h,             dp, ry, ry, h, w, true);
+                }
+            } else {
+                if (kHigh_Quality == quality) {
+                    // Do three X blurs, with a transpose on the final one.
+                    w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, false, outer_weight);
+                    w = boxBlurInterp(tp, w,             dp, rx, w, h, false, outer_weight);
+                    w = boxBlurInterp(dp, w,             tp, rx, w, h, true, outer_weight);
+                    // Do three Y blurs, with a transpose on the final one.
+                    h = boxBlurInterp(tp, h,             dp, ry, h, w, false, outer_weight);
+                    h = boxBlurInterp(dp, h,             tp, ry, h, w, false, outer_weight);
+                    h = boxBlurInterp(tp, h,             dp, ry, h, w, true, outer_weight);
+                } else {
+                    w = boxBlurInterp(sp, src.fRowBytes, tp, rx, w, h, true, outer_weight);
+                    h = boxBlurInterp(tp, h,             dp, ry, h, w, true, outer_weight);
+                }
+            }
+        } else {
             const size_t storageW = sw + 2 * (passCount - 1) * rx + 1;
             const size_t storageH = sh + 2 * (passCount - 1) * ry + 1;
             SkAutoTMalloc<uint32_t> storage(storageW * storageH);
@@ -616,7 +962,7 @@
                 apply_kernel_interp(dp, rx, ry, sumBuffer, sw, sh, outer_weight);
             }
 
-            if (quality == kHigh_Quality) {
+            if (kHigh_Quality == quality) {
                 //pass2: dp is source, tmpBuffer is destination
                 int tmp_sw = sw + 2 * rx;
                 int tmp_sh = sh + 2 * ry;
@@ -670,3 +1016,167 @@
     return true;
 }
 
+bool SkBlurMask::BlurSeparable(SkMask* dst, const SkMask& src,
+                               SkScalar radius, Style style, Quality quality,
+                               SkIPoint* margin)
+{
+    return SkBlurMask::Blur(dst, src, radius, style, quality, margin, true);
+}
+
+bool SkBlurMask::Blur(SkMask* dst, const SkMask& src,
+                     SkScalar radius, Style style, Quality quality,
+                     SkIPoint* margin)
+{
+    return SkBlurMask::Blur(dst, src, radius, style, quality, margin, false);
+}
+
+/* Convolving a box with itself three times results in a piecewise
+   quadratic function:
+
+   0                              x <= -1.5
+   9/8 + 3/2 x + 1/2 x^2   -1.5 < x <= 1.5
+   3/4 - x^2                -.5 < x <= .5
+   9/8 - 3/2 x + 1/2 x^2    0.5 < x <= 1.5
+   0                        1.5 < x
+
+   To get the profile curve of the blurred step function at the rectangle
+   edge, we evaluate the indefinite integral, which is piecewise cubic:
+
+   0                                        x <= -1.5
+   5/8 + 9/8 x + 3/4 x^2 + 1/6 x^3   -1.5 < x <= -0.5
+   1/2 + 3/4 x - 1/3 x^3              -.5 < x <= .5
+   3/8 + 9/8 x - 3/4 x^2 + 1/6 x^3     .5 < x <= 1.5
+   1                                  1.5 < x
+*/
+
+static float gaussian_integral( float x ) {
+    if ( x > 1.5f ) {
+        return 0.0f;
+    }
+    if ( x < -1.5f ) {
+        return 1.0f;
+    }
+
+    float x2 = x*x;
+    float x3 = x2*x;
+
+    if ( x > 0.5f ) {
+        return 0.5625f - ( x3 / 6.0f - 3.0f * x2 * 0.25f + 1.125f * x);
+    }
+    if ( x > -0.5f ) {
+        return 0.5f - (0.75f * x - x3 / 3.0f);
+    }
+    return 0.4375f + (-x3 / 6.0f - 3.0f * x2 * 0.25f - 1.125f * x);
+}
+
+/*
+    compute_profile allocates and fills in an array of floating
+    point values between 0 and 255 for the profile signature of
+    a blurred half-plane with the given blur radius.  Since we're
+    going to be doing screened multiplications (i.e., 1 - (1-x)(1-y))
+    all the time, we actually fill in the profile pre-inverted
+    (already done 255-x).
+
+    The function returns the size of the array allocated for the
+    profile.  It's the responsibility of the caller to delete the
+    memory returned in profile_out.
+*/
+
+static int compute_profile( SkScalar radius, unsigned int **profile_out ) {
+    int size = SkScalarFloorToInt(radius * 3 + 1);
+    int center = size >> 1;
+
+    unsigned int *profile = SkNEW_ARRAY(unsigned int, size);
+
+    float invr = 1.0f/radius;
+
+    profile[0] = 255;
+    for (int x = 1 ; x < size ; x++) {
+        float scaled_x = ( center - x ) * invr;
+        float gi = gaussian_integral( scaled_x );
+        profile[x] = 255 - (uint8_t) ( 255.f * gi );
+    }
+
+    *profile_out = profile;
+    return size;
+}
+
+// TODO MAYBE: Maintain a profile cache to avoid recomputing this for
+// commonly used radii.  Consider baking some of the most common blur radii
+// directly in as static data?
+
+// Implementation adapted from Michael Herf's approach:
+// http://stereopsis.com/shadowrect/
+
+bool SkBlurMask::BlurRect(SkMask *dst, const SkRect &src,
+                          SkScalar provided_radius, Style style, Quality quality,
+                          SkIPoint *margin) {
+    int profile_size;
+    unsigned int *profile;
+
+
+    float radius = SkScalarToFloat( SkScalarMul( provided_radius, kBlurRadiusFudgeFactor ) );
+
+    profile_size = compute_profile( radius, &profile );
+    SkAutoTDeleteArray<unsigned int> ada(profile);
+
+    int pad = (int) (radius * 1.5f + 1);
+    if (margin) {
+        margin->set( pad, pad );
+    }
+    dst->fBounds = SkIRect::MakeWH(SkScalarFloorToInt(src.width()), SkScalarFloorToInt(src.height()));
+    dst->fBounds.outset(pad, pad);
+
+    dst->fRowBytes = dst->fBounds.width();
+    dst->fFormat = SkMask::kA8_Format;
+    dst->fImage = NULL;
+
+    size_t dstSize = dst->computeImageSize();
+    if (0 == dstSize) {
+        return false;   // too big to allocate, abort
+    }
+
+    int             sw = SkScalarFloorToInt(src.width());
+    int             sh = SkScalarFloorToInt(src.height());
+
+    uint8_t*        dp = SkMask::AllocImage(dstSize);
+
+    dst->fImage = dp;
+
+    int dst_height = dst->fBounds.height();
+    int dst_width = dst->fBounds.width();
+
+    // nearest odd number less than the profile size represents the center
+    // of the (2x scaled) profile
+    int center = ( profile_size & ~1 ) - 1;
+
+    int w = sw - center;
+    int h = sh - center;
+
+    uint8_t *outptr = dp;
+
+    for (int y = 0 ; y < dst_height ; y++)
+    {
+        // time to fill in a scanline of the blurry rectangle.
+        // to avoid floating point math, everything is multiplied by
+        // 2 where needed.  This keeps things nice and integer-oriented.
+
+        int dy = abs((y << 1) - dst_height) - h; // how far are we from the original edge?
+        int oy = dy >> 1;
+        if (oy < 0) oy = 0;
+
+        unsigned int profile_y = profile[oy];
+
+        for (int x = 0 ; x < (dst_width << 1) ; x += 2) {
+            int dx = abs( x - dst_width ) - w;
+            int ox = dx >> 1;
+            if (ox < 0) ox = 0;
+
+            unsigned int maskval = SkMulDiv255Round(profile[ox], profile_y);
+
+            *(outptr++) = maskval;
+        }
+    }
+
+    return true;
+}
diff --git a/src/effects/SkBlurMask.h b/src/effects/SkBlurMask.h
index 3709dee..853ba52 100644
--- a/src/effects/SkBlurMask.h
+++ b/src/effects/SkBlurMask.h
@@ -28,12 +28,20 @@
         kHigh_Quality   //!< three pass box blur (similar to gaussian)
     };
 
+    static bool BlurRect(SkMask *dst, const SkRect &src,
+                         SkScalar radius, Style style, Quality quality,
+                         SkIPoint *margin = NULL);
+
     static bool Blur(SkMask* dst, const SkMask& src,
                      SkScalar radius, Style style, Quality quality,
                      SkIPoint* margin = NULL);
+    static bool BlurSeparable(SkMask* dst, const SkMask& src,
+                              SkScalar radius, Style style, Quality quality,
+                              SkIPoint* margin = NULL);
+private:
+    static bool Blur(SkMask* dst, const SkMask& src,
+                     SkScalar radius, Style style, Quality quality,
+                     SkIPoint* margin, bool separable);
 };
 
 #endif
-
-
-
diff --git a/src/effects/SkBlurMaskFilter.cpp b/src/effects/SkBlurMaskFilter.cpp
index fe428d9..c49a785 100644
--- a/src/effects/SkBlurMaskFilter.cpp
+++ b/src/effects/SkBlurMaskFilter.cpp
@@ -6,10 +6,9 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkBlurMaskFilter.h"
 #include "SkBlurMask.h"
-#include "SkBuffer.h"
+#include "SkFlattenableBuffers.h"
 #include "SkMaskFilter.h"
 
 class SkBlurMaskFilterImpl : public SkMaskFilter {
@@ -18,17 +17,18 @@
                          uint32_t flags);
 
     // overrides from SkMaskFilter
-    virtual SkMask::Format getFormat() SK_OVERRIDE;
+    virtual SkMask::Format getFormat() const SK_OVERRIDE;
     virtual bool filterMask(SkMask* dst, const SkMask& src, const SkMatrix&,
-                            SkIPoint* margin) SK_OVERRIDE;
+                            SkIPoint* margin) const SK_OVERRIDE;
     virtual BlurType asABlur(BlurInfo*) const SK_OVERRIDE;
-    virtual void computeFastBounds(const SkRect& src, SkRect* dst) SK_OVERRIDE;
+    virtual void computeFastBounds(const SkRect&, SkRect*) const SK_OVERRIDE;
 
-    // overrides from SkFlattenable
-    virtual Factory getFactory() SK_OVERRIDE;
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkBlurMaskFilterImpl)
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer&);
+protected:
+    virtual FilterReturn filterRectsToNine(const SkRect[], int count, const SkMatrix&,
+                                           const SkIRect& clipBounds,
+                                           NinePatch*) const SK_OVERRIDE;
 
 private:
     SkScalar                    fRadius;
@@ -36,7 +36,8 @@
     uint32_t                    fBlurFlags;
 
     SkBlurMaskFilterImpl(SkFlattenableReadBuffer&);
-    
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
     typedef SkMaskFilter INHERITED;
 };
 
@@ -44,7 +45,7 @@
                                        SkBlurMaskFilter::BlurStyle style,
                                        uint32_t flags) {
     // use !(radius > 0) instead of radius <= 0 to reject NaN values
-    if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount 
+    if (!(radius > 0) || (unsigned)style >= SkBlurMaskFilter::kBlurStyleCount
         || flags > SkBlurMaskFilter::kAll_BlurFlag) {
         return NULL;
     }
@@ -73,12 +74,13 @@
     SkASSERT(flags <= SkBlurMaskFilter::kAll_BlurFlag);
 }
 
-SkMask::Format SkBlurMaskFilterImpl::getFormat() {
+SkMask::Format SkBlurMaskFilterImpl::getFormat() const {
     return SkMask::kA8_Format;
 }
 
 bool SkBlurMaskFilterImpl::filterMask(SkMask* dst, const SkMask& src,
-                                      const SkMatrix& matrix, SkIPoint* margin) {
+                                      const SkMatrix& matrix,
+                                      SkIPoint* margin) const{
     SkScalar radius;
     if (fBlurFlags & SkBlurMaskFilter::kIgnoreTransform_BlurFlag) {
         radius = fRadius;
@@ -92,40 +94,182 @@
     static const SkScalar MAX_RADIUS = SkIntToScalar(128);
     radius = SkMinScalar(radius, MAX_RADIUS);
     SkBlurMask::Quality blurQuality =
-        (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ? 
+        (fBlurFlags & SkBlurMaskFilter::kHighQuality_BlurFlag) ?
             SkBlurMask::kHigh_Quality : SkBlurMask::kLow_Quality;
 
+#ifndef SK_DISABLE_SEPARABLE_MASK_BLUR
+    return SkBlurMask::BlurSeparable(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
+                            blurQuality, margin);
+#else
     return SkBlurMask::Blur(dst, src, radius, (SkBlurMask::Style)fBlurStyle,
                             blurQuality, margin);
+#endif
 }
 
-void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src, SkRect* dst) {
+#include "SkCanvas.h"
+
+static bool drawRectsIntoMask(const SkRect rects[], int count, SkMask* mask) {
+    rects[0].roundOut(&mask->fBounds);
+    mask->fRowBytes = SkAlign4(mask->fBounds.width());
+    mask->fFormat = SkMask::kA8_Format;
+    size_t size = mask->computeImageSize();
+    mask->fImage = SkMask::AllocImage(size);
+    if (NULL == mask->fImage) {
+        return false;
+    }
+    sk_bzero(mask->fImage, size);
+
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kA8_Config,
+                     mask->fBounds.width(), mask->fBounds.height(),
+                     mask->fRowBytes);
+    bitmap.setPixels(mask->fImage);
+
+    SkCanvas canvas(bitmap);
+    canvas.translate(-SkIntToScalar(mask->fBounds.left()),
+                     -SkIntToScalar(mask->fBounds.top()));
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    if (1 == count) {
+        canvas.drawRect(rects[0], paint);
+    } else {
+        // todo: do I need a fast way to do this?
+        SkPath path;
+        path.addRect(rects[0]);
+        path.addRect(rects[1]);
+        path.setFillType(SkPath::kEvenOdd_FillType);
+        canvas.drawPath(path, paint);
+    }
+    return true;
+}
+
+static bool rect_exceeds(const SkRect& r, SkScalar v) {
+    return r.fLeft < -v || r.fTop < -v || r.fRight > v || r.fBottom > v ||
+           r.width() > v || r.height() > v;
+}
+
+SkMaskFilter::FilterReturn
+SkBlurMaskFilterImpl::filterRectsToNine(const SkRect rects[], int count,
+                                        const SkMatrix& matrix,
+                                        const SkIRect& clipBounds,
+                                        NinePatch* patch) const {
+    if (count < 1 || count > 2) {
+        return kUnimplemented_FilterReturn;
+    }
+
+    // TODO: report correct metrics for innerstyle, where we do not grow the
+    // total bounds, but we do need an inset the size of our blur-radius
+    if (SkBlurMaskFilter::kInner_BlurStyle == fBlurStyle) {
+        return kUnimplemented_FilterReturn;
+    }
+
+    // TODO: take clipBounds into account to limit our coordinates up front
+    // for now, just skip too-large src rects (to take the old code path).
+    if (rect_exceeds(rects[0], SkIntToScalar(32767))) {
+        return kUnimplemented_FilterReturn;
+    }
+
+    SkIPoint margin;
+    SkMask  srcM, dstM;
+    rects[0].roundOut(&srcM.fBounds);
+    srcM.fImage = NULL;
+    srcM.fFormat = SkMask::kA8_Format;
+    srcM.fRowBytes = 0;
+    if (!this->filterMask(&dstM, srcM, matrix, &margin)) {
+        return kFalse_FilterReturn;
+    }
+
+    /*
+     *  smallR is the smallest version of 'rect' that will still guarantee that
+     *  we get the same blur results on all edges, plus 1 center row/col that is
+     *  representative of the extendible/stretchable edges of the ninepatch.
+     *  Since our actual edge may be fractional we inset 1 more to be sure we
+     *  don't miss any interior blur.
+     *  x is an added pixel of blur, and { and } are the (fractional) edge
+     *  pixels from the original rect.
+     *
+     *   x x { x x .... x x } x x
+     *
+     *  Thus, in this case, we inset by a total of 5 (on each side) beginning
+     *  with our outer-rect (dstM.fBounds)
+     */
+    SkRect smallR[2];
+    SkIPoint center;
+
+    // +2 is from +1 for each edge (to account for possible fractional edges
+    int smallW = dstM.fBounds.width() - srcM.fBounds.width() + 2;
+    int smallH = dstM.fBounds.height() - srcM.fBounds.height() + 2;
+    SkIRect innerIR;
+
+    if (1 == count) {
+        innerIR = srcM.fBounds;
+        center.set(smallW, smallH);
+    } else {
+        SkASSERT(2 == count);
+        rects[1].roundIn(&innerIR);
+        center.set(smallW + (innerIR.left() - srcM.fBounds.left()),
+                   smallH + (innerIR.top() - srcM.fBounds.top()));
+    }
+
+    // +1 so we get a clean, stretchable, center row/col
+    smallW += 1;
+    smallH += 1;
+
+    // we want the inset amounts to be integral, so we don't change any
+    // fractional phase on the fRight or fBottom of our smallR.
+    const SkScalar dx = SkIntToScalar(innerIR.width() - smallW);
+    const SkScalar dy = SkIntToScalar(innerIR.height() - smallH);
+    if (dx < 0 || dy < 0) {
+        // we're too small, relative to our blur, to break into nine-patch,
+        // so we ask to have our normal filterMask() be called.
+        return kUnimplemented_FilterReturn;
+    }
+
+    smallR[0].set(rects[0].left(), rects[0].top(), rects[0].right() - dx, rects[0].bottom() - dy);
+    SkASSERT(!smallR[0].isEmpty());
+    if (2 == count) {
+        smallR[1].set(rects[1].left(), rects[1].top(),
+                      rects[1].right() - dx, rects[1].bottom() - dy);
+        SkASSERT(!smallR[1].isEmpty());
+    }
+
+    if (!drawRectsIntoMask(smallR, count, &srcM)) {
+        return kFalse_FilterReturn;
+    }
+
+    SkAutoMaskFreeImage amf(srcM.fImage);
+
+    if (!this->filterMask(&patch->fMask, srcM, matrix, &margin)) {
+        return kFalse_FilterReturn;
+    }
+    patch->fMask.fBounds.offsetTo(0, 0);
+    patch->fOuterRect = dstM.fBounds;
+    patch->fCenter = center;
+    return kTrue_FilterReturn;
+}
+
+void SkBlurMaskFilterImpl::computeFastBounds(const SkRect& src,
+                                             SkRect* dst) const {
     dst->set(src.fLeft - fRadius, src.fTop - fRadius,
              src.fRight + fRadius, src.fBottom + fRadius);
 }
 
-SkFlattenable* SkBlurMaskFilterImpl::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkBlurMaskFilterImpl, (buffer));
-}
-
-SkFlattenable::Factory SkBlurMaskFilterImpl::getFactory() {
-    return CreateProc;
-}
-
 SkBlurMaskFilterImpl::SkBlurMaskFilterImpl(SkFlattenableReadBuffer& buffer)
         : SkMaskFilter(buffer) {
     fRadius = buffer.readScalar();
-    fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readS32();
-    fBlurFlags = buffer.readU32() & SkBlurMaskFilter::kAll_BlurFlag;
+    fBlurStyle = (SkBlurMaskFilter::BlurStyle)buffer.readInt();
+    fBlurFlags = buffer.readUInt() & SkBlurMaskFilter::kAll_BlurFlag;
     SkASSERT(fRadius >= 0);
     SkASSERT((unsigned)fBlurStyle < SkBlurMaskFilter::kBlurStyleCount);
 }
 
-void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkBlurMaskFilterImpl::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
     buffer.writeScalar(fRadius);
-    buffer.write32(fBlurStyle);
-    buffer.write32(fBlurFlags);
+    buffer.writeInt(fBlurStyle);
+    buffer.writeUInt(fBlurFlags);
 }
 
 static const SkMaskFilter::BlurType gBlurStyle2BlurType[] = {
diff --git a/src/effects/SkColorFilterImageFilter.cpp b/src/effects/SkColorFilterImageFilter.cpp
new file mode 100755
index 0000000..369f782
--- /dev/null
+++ b/src/effects/SkColorFilterImageFilter.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2012 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 "SkColorFilterImageFilter.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColorMatrixFilter.h"
+#include "SkDevice.h"
+#include "SkColorFilter.h"
+#include "SkFlattenableBuffers.h"
+
+namespace {
+
+void mult_color_matrix(SkScalar a[20], SkScalar b[20], SkScalar out[20]) {
+    for (int j = 0; j < 4; ++j) {
+        for (int i = 0; i < 5; ++i) {
+            out[i+j*5] = 4 == i ? a[4+j*5] : 0;
+            for (int k = 0; k < 4; ++k)
+                out[i+j*5] += SkScalarMul(a[k+j*5], b[i+k*5]);
+        }
+    }
+}
+
+// To detect if we need to apply clamping after applying a matrix, we check if
+// any output component might go outside of [0, 255] for any combination of
+// input components in [0..255].
+// Each output component is an affine transformation of the input component, so
+// the minimum and maximum values are for any combination of minimum or maximum
+// values of input components (i.e. 0 or 255).
+// E.g. if R' = x*R + y*G + z*B + w*A + t
+// Then the maximum value will be for R=255 if x>0 or R=0 if x<0, and the
+// minimum value will be for R=0 if x>0 or R=255 if x<0.
+// Same goes for all components.
+bool component_needs_clamping(SkScalar row[5]) {
+    SkScalar maxValue = row[4] / 255;
+    SkScalar minValue = row[4] / 255;
+    for (int i = 0; i < 4; ++i) {
+        if (row[i] > 0)
+            maxValue += row[i];
+        else
+            minValue += row[i];
+    }
+    return (maxValue > 1) || (minValue < 0);
+}
+
+bool matrix_needs_clamping(SkScalar matrix[20]) {
+    return component_needs_clamping(matrix)
+        || component_needs_clamping(matrix+5)
+        || component_needs_clamping(matrix+10)
+        || component_needs_clamping(matrix+15);
+}
+
+};
+
+SkColorFilterImageFilter* SkColorFilterImageFilter::Create(SkColorFilter* cf,
+        SkImageFilter* input) {
+    SkASSERT(cf);
+    SkScalar colorMatrix[20], inputMatrix[20];
+    SkColorFilter* inputColorFilter;
+    if (input && cf->asColorMatrix(colorMatrix)
+              && (inputColorFilter = input->asColorFilter())
+              && inputColorFilter->asColorMatrix(inputMatrix)
+              && !matrix_needs_clamping(inputMatrix)) {
+        SkScalar combinedMatrix[20];
+        mult_color_matrix(inputMatrix, colorMatrix, combinedMatrix);
+        SkAutoTUnref<SkColorFilter> newCF(SkNEW_ARGS(SkColorMatrixFilter, (combinedMatrix)));
+        return SkNEW_ARGS(SkColorFilterImageFilter, (newCF, input->getInput(0)));
+    } else {
+        return SkNEW_ARGS(SkColorFilterImageFilter, (cf, input));
+    }
+}
+
+SkColorFilterImageFilter::SkColorFilterImageFilter(SkColorFilter* cf, SkImageFilter* input) : INHERITED(input), fColorFilter(cf) {
+    SkASSERT(cf);
+    SkSafeRef(cf);
+}
+
+SkColorFilterImageFilter::SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fColorFilter = buffer.readFlattenableT<SkColorFilter>();
+}
+
+void SkColorFilterImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeFlattenable(fColorFilter);
+}
+
+SkColorFilterImageFilter::~SkColorFilterImageFilter() {
+    SkSafeUnref(fColorFilter);
+}
+
+bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
+                                             const SkMatrix& matrix,
+                                             SkBitmap* result,
+                                             SkIPoint* loc) {
+    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
+    SkAutoTUnref<SkDevice> device(proxy->createDevice(src.width(), src.height()));
+    SkCanvas canvas(device.get());
+    SkPaint paint;
+
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    paint.setColorFilter(fColorFilter);
+    canvas.drawSprite(src, 0, 0, &paint);
+
+    *result = device.get()->accessBitmap(false);
+    return true;
+}
+
+SkColorFilter* SkColorFilterImageFilter::asColorFilter() const {
+    return fColorFilter;
+}
diff --git a/src/effects/SkColorFilters.cpp b/src/effects/SkColorFilters.cpp
index e6262c1..a14babc 100644
--- a/src/effects/SkColorFilters.cpp
+++ b/src/effects/SkColorFilters.cpp
@@ -10,6 +10,7 @@
 #include "SkBlitRow.h"
 #include "SkColorFilter.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
 #include "SkUtils.h"
 
 #define ILLEGAL_XFERMODE_MODE   ((SkXfermode::Mode)-1)
@@ -20,18 +21,21 @@
     SkModeColorFilter(SkColor color) {
         fColor = color;
         fMode = ILLEGAL_XFERMODE_MODE;
-
-        fPMColor = SkPreMultiplyColor(fColor);
+        this->updateCache();
     }
 
     SkModeColorFilter(SkColor color, SkXfermode::Mode mode) {
         fColor = color;
         fMode = mode;
-
-        fPMColor = SkPreMultiplyColor(fColor);
+        this->updateCache();
     };
 
-    virtual bool asColorMode(SkColor* color, SkXfermode::Mode* mode) {
+    SkColor getColor() const { return fColor; }
+    SkXfermode::Mode getMode() const { return fMode; }
+    bool isModeValid() const { return ILLEGAL_XFERMODE_MODE != fMode; }
+    SkPMColor getPMColor() const { return fPMColor; }
+
+    virtual bool asColorMode(SkColor* color, SkXfermode::Mode* mode) const SK_OVERRIDE {
         if (ILLEGAL_XFERMODE_MODE == fMode) {
             return false;
         }
@@ -45,30 +49,60 @@
         return true;
     }
 
-    SkColor getColor() const { return fColor; }
-    SkXfermode::Mode getMode() const { return fMode; }
-    bool isModeValid() const { return ILLEGAL_XFERMODE_MODE != fMode; }
+    virtual uint32_t getFlags() const SK_OVERRIDE {
+        return fProc16 ? (kAlphaUnchanged_Flag | kHasFilter16_Flag) : 0;
+    }
+
+    virtual void filterSpan(const SkPMColor shader[], int count,
+                            SkPMColor result[]) const SK_OVERRIDE {
+        SkPMColor       color = fPMColor;
+        SkXfermodeProc  proc = fProc;
+
+        for (int i = 0; i < count; i++) {
+            result[i] = proc(color, shader[i]);
+        }
+    }
+
+    virtual void filterSpan16(const uint16_t shader[], int count,
+                              uint16_t result[]) const SK_OVERRIDE {
+        SkASSERT(this->getFlags() & kHasFilter16_Flag);
+
+        SkPMColor        color = fPMColor;
+        SkXfermodeProc16 proc16 = fProc16;
+
+        for (int i = 0; i < count; i++) {
+            result[i] = proc16(color, shader[i]);
+        }
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkModeColorFilter)
 
 protected:
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
         this->INHERITED::flatten(buffer);
-        buffer.write32(fColor);
-        buffer.write32(fMode);
+        buffer.writeColor(fColor);
+        buffer.writeUInt(fMode);
     }
 
     SkModeColorFilter(SkFlattenableReadBuffer& buffer) {
-        fColor = buffer.readU32();
-        fMode = (SkXfermode::Mode)buffer.readU32();
-
-        fPMColor = SkPreMultiplyColor(fColor);
+        fColor = buffer.readColor();
+        fMode = (SkXfermode::Mode)buffer.readUInt();
+        this->updateCache();
     }
 
-    // cache of fColor in premultiply space
-    SkPMColor   fPMColor;
-
 private:
     SkColor             fColor;
     SkXfermode::Mode    fMode;
+    // cache
+    SkPMColor           fPMColor;
+    SkXfermodeProc      fProc;
+    SkXfermodeProc16    fProc16;
+
+    void updateCache() {
+        fPMColor = SkPreMultiplyColor(fColor);
+        fProc = SkXfermode::GetProc(fMode);
+        fProc16 = SkXfermode::GetProc16(fMode, fColor);
+    }
 
     typedef SkColorFilter INHERITED;
 };
@@ -77,8 +111,8 @@
 public:
     Src_SkModeColorFilter(SkColor color) : INHERITED(color, SkXfermode::kSrc_Mode) {}
 
-    virtual uint32_t getFlags() {
-        if (SkGetPackedA32(fPMColor) == 0xFF) {
+    virtual uint32_t getFlags() const SK_OVERRIDE {
+        if (SkGetPackedA32(this->getPMColor()) == 0xFF) {
             return kAlphaUnchanged_Flag | kHasFilter16_Flag;
         } else {
             return 0;
@@ -86,23 +120,19 @@
     }
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
-        sk_memset32(result, fPMColor, count);
+                            SkPMColor result[]) const SK_OVERRIDE {
+        sk_memset32(result, this->getPMColor(), count);
     }
 
     virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) {
+                              uint16_t result[]) const SK_OVERRIDE {
         SkASSERT(this->getFlags() & kHasFilter16_Flag);
-        sk_memset16(result, SkPixel32ToPixel16(fPMColor), count);
+        sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
     }
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(Src_SkModeColorFilter, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(Src_SkModeColorFilter)
 
 protected:
-    virtual Factory getFactory() { return CreateProc; }
-
     Src_SkModeColorFilter(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {}
 
@@ -114,11 +144,11 @@
 public:
     SrcOver_SkModeColorFilter(SkColor color)
             : INHERITED(color, SkXfermode::kSrcOver_Mode) {
-        fColor32Proc = NULL;
+        fColor32Proc = SkBlitRow::ColorProcFactory();
     }
 
-    virtual uint32_t getFlags() {
-        if (SkGetPackedA32(fPMColor) == 0xFF) {
+    virtual uint32_t getFlags() const SK_OVERRIDE {
+        if (SkGetPackedA32(this->getPMColor()) == 0xFF) {
             return kAlphaUnchanged_Flag | kHasFilter16_Flag;
         } else {
             return 0;
@@ -126,28 +156,23 @@
     }
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
-        if (NULL == fColor32Proc) {
-            fColor32Proc = SkBlitRow::ColorProcFactory();
-        }
-        fColor32Proc(result, shader, count, fPMColor);
+                            SkPMColor result[]) const SK_OVERRIDE {
+        fColor32Proc(result, shader, count, this->getPMColor());
     }
 
     virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) {
+                              uint16_t result[]) const SK_OVERRIDE {
         SkASSERT(this->getFlags() & kHasFilter16_Flag);
-        sk_memset16(result, SkPixel32ToPixel16(fPMColor), count);
+        sk_memset16(result, SkPixel32ToPixel16(this->getPMColor()), count);
     }
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SrcOver_SkModeColorFilter, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SrcOver_SkModeColorFilter)
 
 protected:
-    virtual Factory getFactory() { return CreateProc;  }
-
     SrcOver_SkModeColorFilter(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer), fColor32Proc(NULL) {}
+        : INHERITED(buffer) {
+            fColor32Proc = SkBlitRow::ColorProcFactory();
+        }
 
 private:
 
@@ -158,83 +183,6 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-class Proc_SkModeColorFilter : public SkModeColorFilter {
-public:
-    Proc_SkModeColorFilter(SkColor color, SkXfermode::Mode mode) : INHERITED(color, mode) {
-        fProc = SkXfermode::GetProc(mode);
-        fProc16 = SkXfermode::GetProc16(mode, color);
-    }
-
-    Proc_SkModeColorFilter(SkColor color,
-                           SkXfermodeProc proc, SkXfermodeProc16 proc16)
-            : INHERITED(color, ILLEGAL_XFERMODE_MODE) {
-        fProc = proc;
-        fProc16 = proc16;
-    }
-
-    virtual uint32_t getFlags() {
-        return fProc16 ? (kAlphaUnchanged_Flag | kHasFilter16_Flag) : 0;
-    }
-
-    virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
-        SkPMColor       color = fPMColor;
-        SkXfermodeProc  proc = fProc;
-
-        for (int i = 0; i < count; i++) {
-            result[i] = proc(color, shader[i]);
-        }
-    }
-
-    virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) {
-        SkASSERT(this->getFlags() & kHasFilter16_Flag);
-
-        SkPMColor        color = fPMColor;
-        SkXfermodeProc16 proc16 = fProc16;
-
-        for (int i = 0; i < count; i++) {
-            result[i] = proc16(color, shader[i]);
-        }
-    }
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(Proc_SkModeColorFilter, (buffer));
-    }
-
-protected:
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
-        this->INHERITED::flatten(buffer);
-        buffer.writeFunctionPtr((void*)fProc);
-        buffer.writeFunctionPtr((void*)fProc16);
-    }
-
-    virtual Factory getFactory() {
-        return CreateProc;
-    }
-
-    Proc_SkModeColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-        fProc = (SkXfermodeProc) buffer.readFunctionPtr();
-        fProc16 = (SkXfermodeProc16) buffer.readFunctionPtr();
-    }
-
-private:
-    SkXfermodeProc   fProc;
-    SkXfermodeProc16 fProc16;
-
-    typedef SkModeColorFilter INHERITED;
-};
-
-SkColorFilter* SkColorFilter::CreateProcFilter(SkColor color,
-                                               SkXfermodeProc proc,
-                                               SkXfermodeProc16 proc16) {
-    return proc ?
-            SkNEW_ARGS(Proc_SkModeColorFilter, (color, proc, proc16)) :
-            NULL;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
 SkColorFilter* SkColorFilter::CreateModeFilter(SkColor color,
                                                SkXfermode::Mode mode) {
     unsigned alpha = SkColorGetA(color);
@@ -271,7 +219,7 @@
         case SkXfermode::kSrcOver_Mode:
             return SkNEW_ARGS(SrcOver_SkModeColorFilter, (color));
         default:
-            return SkNEW_ARGS(Proc_SkModeColorFilter, (color, mode));
+            return SkNEW_ARGS(SkModeColorFilter, (color, mode));
     }
 }
 
@@ -284,23 +232,12 @@
     return value;
 }
 
-static inline unsigned SkUClampMax(unsigned value, unsigned max) {
-    SkASSERT((int32_t)value >= 0);
-    SkASSERT((int32_t)max >= 0);
-
-    int diff = max - value;
-    // clear diff if diff is positive
-    diff &= diff >> 31;
-
-    return value + diff;
-}
-
 class SkLightingColorFilter : public SkColorFilter {
 public:
     SkLightingColorFilter(SkColor mul, SkColor add) : fMul(mul), fAdd(add) {}
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
+                            SkPMColor result[]) const SK_OVERRIDE {
         unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
         unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
         unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
@@ -323,24 +260,18 @@
         }
     }
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkLightingColorFilter, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter)
 
 protected:
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
         this->INHERITED::flatten(buffer);
-        buffer.write32(fMul);
-        buffer.write32(fAdd);
-    }
-
-    virtual Factory getFactory() {
-        return CreateProc;
+        buffer.writeColor(fMul);
+        buffer.writeColor(fAdd);
     }
 
     SkLightingColorFilter(SkFlattenableReadBuffer& buffer) {
-        fMul = buffer.readU32();
-        fAdd = buffer.readU32();
+        fMul = buffer.readColor();
+        fAdd = buffer.readColor();
     }
 
     SkColor fMul, fAdd;
@@ -355,7 +286,7 @@
         : INHERITED(mul, add) {}
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
+                            SkPMColor result[]) const SK_OVERRIDE {
         unsigned addR = SkColorGetR(fAdd);
         unsigned addG = SkColorGetG(fAdd);
         unsigned addB = SkColorGetB(fAdd);
@@ -374,13 +305,9 @@
         }
     }
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer)  {
-        return SkNEW_ARGS(SkLightingColorFilter_JustAdd, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_JustAdd)
 
 protected:
-    virtual Factory getFactory() { return CreateProc; }
-
     SkLightingColorFilter_JustAdd(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {}
 
@@ -394,7 +321,7 @@
         : INHERITED(mul, add) {}
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
+                            SkPMColor result[]) const SK_OVERRIDE {
         unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
         unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
         unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
@@ -412,13 +339,9 @@
         }
     }
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkLightingColorFilter_JustMul, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_JustMul)
 
 protected:
-    virtual Factory getFactory() { return CreateProc; }
-
     SkLightingColorFilter_JustMul(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {}
 
@@ -437,12 +360,12 @@
         SkASSERT(SkColorGetR(mul) == SkColorGetB(mul));
     }
 
-    virtual uint32_t getFlags() {
+    virtual uint32_t getFlags() const SK_OVERRIDE {
         return this->INHERITED::getFlags() | (kAlphaUnchanged_Flag | kHasFilter16_Flag);
     }
 
     virtual void filterSpan16(const uint16_t shader[], int count,
-                              uint16_t result[]) {
+                              uint16_t result[]) const SK_OVERRIDE {
         // all mul components are the same
         unsigned scale = SkAlpha255To256(SkColorGetR(fMul));
 
@@ -453,13 +376,9 @@
         }
     }
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkLightingColorFilter_SingleMul, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_SingleMul)
 
 protected:
-    virtual Factory getFactory() { return CreateProc; }
-
     SkLightingColorFilter_SingleMul(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {}
 
@@ -473,7 +392,7 @@
     : INHERITED(mul, add) {}
 
     virtual void filterSpan(const SkPMColor shader[], int count,
-                            SkPMColor result[]) {
+                            SkPMColor result[]) const SK_OVERRIDE {
         unsigned scaleR = SkAlpha255To256(SkColorGetR(fMul));
         unsigned scaleG = SkAlpha255To256(SkColorGetG(fMul));
         unsigned scaleB = SkAlpha255To256(SkColorGetB(fMul));
@@ -496,13 +415,9 @@
         }
     }
 
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkLightingColorFilter_NoPin, (buffer));
-    }
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLightingColorFilter_NoPin)
 
 protected:
-    virtual Factory getFactory() { return CreateProc; }
-
     SkLightingColorFilter_NoPin(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {}
 
@@ -519,13 +434,14 @@
     }
 
 protected:
-    void filterSpan(const SkPMColor src[], int count, SkPMColor result[]) {
+    void filterSpan(const SkPMColor src[], int count, SkPMColor
+                    result[]) const SK_OVERRIDE {
         if (result != src) {
             memcpy(result, src, count * sizeof(SkPMColor));
         }
     }
 
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) {}
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {}
 
     virtual Factory getFactory() {
         return CreateProc;
@@ -564,9 +480,9 @@
 }
 
 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkColorFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkModeColorFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Src_SkModeColorFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SrcOver_SkModeColorFilter)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Proc_SkModeColorFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingColorFilter)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingColorFilter_JustAdd)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingColorFilter_JustMul)
@@ -574,4 +490,3 @@
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLightingColorFilter_NoPin)
     SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSimpleColorFilter)
 SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
-
diff --git a/src/effects/SkColorMatrix.cpp b/src/effects/SkColorMatrix.cpp
new file mode 100644
index 0000000..d6cb940
--- /dev/null
+++ b/src/effects/SkColorMatrix.cpp
@@ -0,0 +1,161 @@
+
+/*
+ * 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 "SkColorMatrix.h"
+#include "SkFlattenableBuffers.h"
+
+#define kRScale     0
+#define kGScale     6
+#define kBScale     12
+#define kAScale     18
+
+void SkColorMatrix::setIdentity() {
+    memset(fMat, 0, sizeof(fMat));
+    fMat[kRScale] = fMat[kGScale] = fMat[kBScale] = fMat[kAScale] = SK_Scalar1;
+}
+
+void SkColorMatrix::setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
+                             SkScalar aScale) {
+    memset(fMat, 0, sizeof(fMat));
+    fMat[kRScale] = rScale;
+    fMat[kGScale] = gScale;
+    fMat[kBScale] = bScale;
+    fMat[kAScale] = aScale;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkColorMatrix::setRotate(Axis axis, SkScalar degrees) {
+    SkScalar S, C;
+
+    S = SkScalarSinCos(SkDegreesToRadians(degrees), &C);
+
+    this->setSinCos(axis, S, C);
+}
+
+void SkColorMatrix::setSinCos(Axis axis, SkScalar sine, SkScalar cosine) {
+    SkASSERT((unsigned)axis < 3);
+
+    static const uint8_t gRotateIndex[] = {
+        6, 7, 11, 12,
+        0, 10, 2, 12,
+        0, 1,  5,  6,
+    };
+    const uint8_t* index = gRotateIndex + axis * 4;
+
+    this->setIdentity();
+    fMat[index[0]] = cosine;
+    fMat[index[1]] = sine;
+    fMat[index[2]] = -sine;
+    fMat[index[3]] = cosine;
+}
+
+void SkColorMatrix::preRotate(Axis axis, SkScalar degrees) {
+    SkColorMatrix tmp;
+    tmp.setRotate(axis, degrees);
+    this->preConcat(tmp);
+}
+
+void SkColorMatrix::postRotate(Axis axis, SkScalar degrees) {
+    SkColorMatrix tmp;
+    tmp.setRotate(axis, degrees);
+    this->postConcat(tmp);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkColorMatrix::setConcat(const SkColorMatrix& matA,
+                              const SkColorMatrix& matB) {
+    SkScalar    tmp[20];
+    SkScalar*   result = fMat;
+
+    if (&matA == this || &matB == this) {
+        result = tmp;
+    }
+
+    const SkScalar* a = matA.fMat;
+    const SkScalar* b = matB.fMat;
+
+    int index = 0;
+    for (int j = 0; j < 20; j += 5) {
+        for (int i = 0; i < 4; i++) {
+            result[index++] =   SkScalarMul(a[j + 0], b[i + 0]) +
+                                SkScalarMul(a[j + 1], b[i + 5]) +
+                                SkScalarMul(a[j + 2], b[i + 10]) +
+                                SkScalarMul(a[j + 3], b[i + 15]);
+        }
+        result[index++] =   SkScalarMul(a[j + 0], b[4]) +
+                            SkScalarMul(a[j + 1], b[9]) +
+                            SkScalarMul(a[j + 2], b[14]) +
+                            SkScalarMul(a[j + 3], b[19]) +
+                            a[j + 4];
+    }
+
+    if (fMat != result) {
+        memcpy(fMat, result, sizeof(fMat));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void setrow(SkScalar row[], SkScalar r, SkScalar g, SkScalar b) {
+    row[0] = r;
+    row[1] = g;
+    row[2] = b;
+}
+
+static const SkScalar kHueR = SkFloatToScalar(0.213f);
+static const SkScalar kHueG = SkFloatToScalar(0.715f);
+static const SkScalar kHueB = SkFloatToScalar(0.072f);
+
+void SkColorMatrix::setSaturation(SkScalar sat) {
+    memset(fMat, 0, sizeof(fMat));
+
+    const SkScalar R = SkScalarMul(kHueR, SK_Scalar1 - sat);
+    const SkScalar G = SkScalarMul(kHueG, SK_Scalar1 - sat);
+    const SkScalar B = SkScalarMul(kHueB, SK_Scalar1 - sat);
+
+    setrow(fMat +  0, R + sat, G, B);
+    setrow(fMat +  5, R, G + sat, B);
+    setrow(fMat + 10, R, G, B + sat);
+    fMat[18] = SK_Scalar1;
+}
+
+static const SkScalar kR2Y = SkFloatToScalar(0.299f);
+static const SkScalar kG2Y = SkFloatToScalar(0.587f);
+static const SkScalar kB2Y = SkFloatToScalar(0.114f);
+
+static const SkScalar kR2U = SkFloatToScalar(-0.16874f);
+static const SkScalar kG2U = SkFloatToScalar(-0.33126f);
+static const SkScalar kB2U = SkFloatToScalar(0.5f);
+
+static const SkScalar kR2V = SkFloatToScalar(0.5f);
+static const SkScalar kG2V = SkFloatToScalar(-0.41869f);
+static const SkScalar kB2V = SkFloatToScalar(-0.08131f);
+
+void SkColorMatrix::setRGB2YUV() {
+    memset(fMat, 0, sizeof(fMat));
+
+    setrow(fMat +  0, kR2Y, kG2Y, kB2Y);
+    setrow(fMat +  5, kR2U, kG2U, kB2U);
+    setrow(fMat + 10, kR2V, kG2V, kB2V);
+    fMat[18] = SK_Scalar1;
+}
+
+static const SkScalar kV2R = SkFloatToScalar(1.402f);
+static const SkScalar kU2G = SkFloatToScalar(-0.34414f);
+static const SkScalar kV2G = SkFloatToScalar(-0.71414f);
+static const SkScalar kU2B = SkFloatToScalar(1.772f);
+
+void SkColorMatrix::setYUV2RGB() {
+    memset(fMat, 0, sizeof(fMat));
+
+    setrow(fMat +  0, SK_Scalar1, 0, kV2R);
+    setrow(fMat +  5, SK_Scalar1, kU2G, kV2G);
+    setrow(fMat + 10, SK_Scalar1, kU2B, 0);
+    fMat[18] = SK_Scalar1;
+}
diff --git a/src/effects/SkColorMatrixFilter.cpp b/src/effects/SkColorMatrixFilter.cpp
index 79fbea5..1582464 100644
--- a/src/effects/SkColorMatrixFilter.cpp
+++ b/src/effects/SkColorMatrixFilter.cpp
@@ -8,6 +8,7 @@
 #include "SkColorMatrixFilter.h"
 #include "SkColorMatrix.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
 #include "SkUnPreMultiply.h"
 
 static int32_t rowmul4(const int32_t array[], unsigned r, unsigned g,
@@ -20,11 +21,11 @@
     return array[0] * r + array[1] * g  + array[2] * b + array[4];
 }
 
-static void General(SkColorMatrixFilter::State* state,
-                    unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    const int shift = state->fShift;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void General(const SkColorMatrixFilter::State& state,
+                    unsigned r, unsigned g, unsigned b, unsigned a,
+                    int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
+    const int shift = state.fShift;
 
     result[0] = rowmul4(&array[0], r, g, b, a) >> shift;
     result[1] = rowmul4(&array[5], r, g, b, a) >> shift;
@@ -32,10 +33,10 @@
     result[3] = rowmul4(&array[15], r, g, b, a) >> shift;
 }
 
-static void General16(SkColorMatrixFilter::State* state,
-                      unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void General16(const SkColorMatrixFilter::State& state,
+                      unsigned r, unsigned g, unsigned b, unsigned a,
+                      int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
 
     result[0] = rowmul4(&array[0], r, g, b, a) >> 16;
     result[1] = rowmul4(&array[5], r, g, b, a) >> 16;
@@ -43,11 +44,11 @@
     result[3] = rowmul4(&array[15], r, g, b, a) >> 16;
 }
 
-static void AffineAdd(SkColorMatrixFilter::State* state,
-                      unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    const int shift = state->fShift;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void AffineAdd(const SkColorMatrixFilter::State& state,
+                      unsigned r, unsigned g, unsigned b, unsigned a,
+                      int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
+    const int shift = state.fShift;
 
     result[0] = rowmul3(&array[0], r, g, b) >> shift;
     result[1] = rowmul3(&array[5], r, g, b) >> shift;
@@ -55,10 +56,10 @@
     result[3] = a;
 }
 
-static void AffineAdd16(SkColorMatrixFilter::State* state,
-                        unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void AffineAdd16(const SkColorMatrixFilter::State& state,
+                        unsigned r, unsigned g, unsigned b, unsigned a,
+                        int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
 
     result[0] = rowmul3(&array[0], r, g, b) >> 16;
     result[1] = rowmul3(&array[5], r, g, b) >> 16;
@@ -66,11 +67,11 @@
     result[3] = a;
 }
 
-static void ScaleAdd(SkColorMatrixFilter::State* state,
-                     unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    const int shift = state->fShift;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void ScaleAdd(const SkColorMatrixFilter::State& state,
+                     unsigned r, unsigned g, unsigned b, unsigned a,
+                     int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
+    const int shift = state.fShift;
 
     // cast to (int) to keep the expression signed for the shift
     result[0] = (array[0] * (int)r + array[4]) >> shift;
@@ -79,10 +80,10 @@
     result[3] = a;
 }
 
-static void ScaleAdd16(SkColorMatrixFilter::State* state,
-                       unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void ScaleAdd16(const SkColorMatrixFilter::State& state,
+                       unsigned r, unsigned g, unsigned b, unsigned a,
+                       int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
 
     // cast to (int) to keep the expression signed for the shift
     result[0] = (array[0] * (int)r + array[4]) >> 16;
@@ -91,11 +92,11 @@
     result[3] = a;
 }
 
-static void Add(SkColorMatrixFilter::State* state,
-                unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    const int shift = state->fShift;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void Add(const SkColorMatrixFilter::State& state,
+                unsigned r, unsigned g, unsigned b, unsigned a,
+                int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
+    const int shift = state.fShift;
 
     result[0] = r + (array[4] >> shift);
     result[1] = g + (array[9] >> shift);
@@ -103,10 +104,10 @@
     result[3] = a;
 }
 
-static void Add16(SkColorMatrixFilter::State* state,
-                  unsigned r, unsigned g, unsigned b, unsigned a) {
-    const int32_t* SK_RESTRICT array = state->fArray;
-    int32_t* SK_RESTRICT result = state->fResult;
+static void Add16(const SkColorMatrixFilter::State& state,
+                  unsigned r, unsigned g, unsigned b, unsigned a,
+                  int32_t* SK_RESTRICT result) {
+    const int32_t* SK_RESTRICT array = state.fArray;
 
     result[0] = r + (array[4] >> 16);
     result[1] = g + (array[9] >> 16);
@@ -119,19 +120,9 @@
 
 // src is [20] but some compilers won't accept __restrict__ on anything
 // but an raw pointer or reference
-void SkColorMatrixFilter::setup(const SkScalar* SK_RESTRICT src) {
-    if (NULL == src) {
-        fProc = NULL;   // signals identity
-        fFlags  = kNO_ALPHA_FLAGS;
-        // fState is undefined, but that is OK, since we shouldn't look at it
-        return;
-    }
-
+void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) {
     int32_t* array = fState.fArray;
-
-    int i;
     SkFixed max = 0;
-
     for (int i = 0; i < 20; i++) {
         SkFixed value = SkScalarToFixed(src[i]);
         array[i] = value;
@@ -150,7 +141,7 @@
     if (bits < 9) {
         bits = 9 - bits;
         fState.fShift -= bits;
-        for (i = 0; i < 20; i++) {
+        for (int i = 0; i < 20; i++) {
             array[i] >>= bits;
         }
         one >>= bits;
@@ -212,27 +203,24 @@
     return value;
 }
 
-SkColorMatrixFilter::SkColorMatrixFilter() {
-    this->setup(NULL);
-}
-
-SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) {
-    this->setup(cm.fMat);
+SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm) {
+    this->initState(cm.fMat);
 }
 
 SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) {
-    this->setup(array);
+    memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar));
+    this->initState(array);
 }
 
-uint32_t SkColorMatrixFilter::getFlags() {
+uint32_t SkColorMatrixFilter::getFlags() const {
     return this->INHERITED::getFlags() | fFlags;
 }
 
 void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count,
-                                     SkPMColor dst[]) {
+                                     SkPMColor dst[]) const {
     Proc proc = fProc;
-    State* state = &fState;
-    int32_t* result = state->fResult;
+    const State& state = fState;
+    int32_t result[4];
 
     if (NULL == proc) {
         if (src != dst) {
@@ -263,7 +251,7 @@
             SkASSERT(b <= 255);
         }
 
-        proc(state, r, g, b, a);
+        proc(state, r, g, b, a, result);
 
         r = pin(result[0], SK_R32_MASK);
         g = pin(result[1], SK_G32_MASK);
@@ -275,12 +263,12 @@
 }
 
 void SkColorMatrixFilter::filterSpan16(const uint16_t src[], int count,
-                                       uint16_t dst[]) {
+                                       uint16_t dst[]) const {
     SkASSERT(fFlags & SkColorFilter::kHasFilter16_Flag);
 
     Proc   proc = fProc;
-    State* state = &fState;
-    int32_t* result = state->fResult;
+    const State& state = fState;
+    int32_t result[4];
 
     if (NULL == proc) {
         if (src != dst) {
@@ -297,7 +285,7 @@
         unsigned g = SkPacked16ToG32(c);
         unsigned b = SkPacked16ToB32(c);
 
-        proc(state, r, g, b, 0);
+        proc(state, r, g, b, 0, result);
 
         r = pin(result[0], SK_R32_MASK);
         g = pin(result[1], SK_G32_MASK);
@@ -310,50 +298,182 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer)  {
+void SkColorMatrixFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-
-    buffer.writeFunctionPtr((void*)fProc);
-    buffer.writeMul4(&fState, sizeof(fState));
-    buffer.write32(fFlags);
+    SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20);
+    buffer.writeScalarArray(fMatrix.fMat, 20);
 }
 
-SkFlattenable::Factory SkColorMatrixFilter::getFactory() { return CreateProc;  }
-
 SkColorMatrixFilter::SkColorMatrixFilter(SkFlattenableReadBuffer& buffer)
         : INHERITED(buffer) {
-    fProc = (Proc)buffer.readFunctionPtr();
-    buffer.read(&fState, sizeof(fState));
-    fFlags = buffer.readU32();
+    SkASSERT(buffer.getArrayCount() == 20);
+    buffer.readScalarArray(fMatrix.fMat);
+    this->initState(fMatrix.fMat);
 }
 
-bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) {
-    int32_t* array = fState.fArray;
-    int unshift = 16 - fState.fShift;
-    for (int i = 0; i < 20; i++) {
-        matrix[i] = SkFixedToScalar(array[i] << unshift);
-    }
-    if (NULL != fProc) {
-        // Undo the offset applied to the constant column in setup().
-        SkFixed offset = 1 << (fState.fShift - 1);
-        matrix[4] = SkFixedToScalar((array[4] - offset) << unshift);
-        matrix[9] = SkFixedToScalar((array[9] - offset) << unshift);
-        matrix[14] = SkFixedToScalar((array[14] - offset) << unshift);
-        matrix[19] = SkFixedToScalar((array[19] - offset) << unshift);
+bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const {
+    if (matrix) {
+        memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar));
     }
     return true;
 }
 
-SkFlattenable* SkColorMatrixFilter::CreateProc(SkFlattenableReadBuffer& buf) {
-    return SkNEW_ARGS(SkColorMatrixFilter, (buf));
+#if SK_SUPPORT_GPU
+#include "GrEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+
+class ColorMatrixEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(const SkColorMatrix& matrix) {
+        AutoEffectUnref effect(SkNEW_ARGS(ColorMatrixEffect, (matrix)));
+        return CreateEffectRef(effect);
+    }
+
+    static const char* Name() { return "Color Matrix"; }
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<ColorMatrixEffect>::getInstance();
+    }
+
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // We only bother to check whether the alpha channel will be constant. If SkColorMatrix had
+        // type flags it might be worth checking the other components.
+
+        // The matrix is defined such the 4th row determines the output alpha. The first four
+        // columns of that row multiply the input r, g, b, and a, respectively, and the last column
+        // is the "translation".
+        static const ValidComponentFlags kRGBAFlags[] = {
+            kR_ValidComponentFlag,
+            kG_ValidComponentFlag,
+            kB_ValidComponentFlag,
+            kA_ValidComponentFlag
+        };
+        static const int kShifts[] = {
+            GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A,
+        };
+        enum {
+            kAlphaRowStartIdx = 15,
+            kAlphaRowTranslateIdx = 19,
+        };
+
+        SkScalar outputA = 0;
+        for (int i = 0; i < 4; ++i) {
+            // If any relevant component of the color to be passed through the matrix is non-const
+            // then we can't know the final result.
+            if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) {
+                if (!(*validFlags & kRGBAFlags[i])) {
+                    *validFlags = 0;
+                    return;
+                } else {
+                    uint32_t component = (*color >> kShifts[i]) & 0xFF;
+                    outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component;
+                }
+            }
+        }
+        outputA += fMatrix.fMat[kAlphaRowTranslateIdx];
+        *validFlags = kA_ValidComponentFlag;
+        // We pin the color to [0,1]. This would happen to the *final* color output from the frag
+        // shader but currently the effect does not pin its own output. So in the case of over/
+        // underflow this may deviate from the actual result. Maybe the effect should pin its
+        // result if the matrix could over/underflow for any component?
+        *color = static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) << GrColor_SHIFT_A;
+    }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    class GLEffect : public GrGLEffect {
+    public:
+        // this class always generates the same code.
+        static EffectKey GenKey(const GrEffectStage&, const GrGLCaps&) { return 0; }
+
+        GLEffect(const GrBackendEffectFactory& factory,
+                 const GrEffectRef& effect)
+        : INHERITED(factory)
+        , fMatrixHandle(GrGLUniformManager::kInvalidUniformHandle)
+        , fVectorHandle(GrGLUniformManager::kInvalidUniformHandle) {}
+
+        virtual void emitCode(GrGLShaderBuilder* builder,
+                              const GrEffectStage&,
+                              EffectKey,
+                              const char* vertexCoords,
+                              const char* outputColor,
+                              const char* inputColor,
+                              const TextureSamplerArray&) SK_OVERRIDE {
+            fMatrixHandle = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                kMat44f_GrSLType,
+                                                "ColorMatrix");
+            fVectorHandle = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                kVec4f_GrSLType,
+                                                "ColorMatrixVector");
+
+            if (NULL == inputColor) {
+                // could optimize this case, but we aren't for now.
+                inputColor = GrGLSLOnesVecf(4);
+            }
+            // The max() is to guard against 0 / 0 during unpremul when the incoming color is
+            // transparent black.
+            builder->fFSCode.appendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);\n", inputColor);
+            builder->fFSCode.appendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha) + %s;\n",
+                                     outputColor,
+                                     builder->getUniformCStr(fMatrixHandle),
+                                     inputColor,
+                                     builder->getUniformCStr(fVectorHandle));
+            builder->fFSCode.appendf("\t%s.rgb *= %s.a;\n", outputColor, outputColor);
+        }
+
+        virtual void setData(const GrGLUniformManager& uniManager,
+                             const GrEffectStage& stage) SK_OVERRIDE {
+            const ColorMatrixEffect& cme = GetEffectFromStage<ColorMatrixEffect>(stage);
+            const float* m = cme.fMatrix.fMat;
+            // The GL matrix is transposed from SkColorMatrix.
+            GrGLfloat mt[]  = {
+                m[0], m[5], m[10], m[15],
+                m[1], m[6], m[11], m[16],
+                m[2], m[7], m[12], m[17],
+                m[3], m[8], m[13], m[18],
+            };
+            static const float kScale = 1.0f / 255.0f;
+            GrGLfloat vec[] = {
+                m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale,
+            };
+            uniManager.setMatrix4fv(fMatrixHandle, 0, 1, mt);
+            uniManager.set4fv(fVectorHandle, 0, 1, vec);
+        }
+
+    private:
+        GrGLUniformManager::UniformHandle fMatrixHandle;
+        GrGLUniformManager::UniformHandle fVectorHandle;
+    };
+
+private:
+    ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) {}
+
+    virtual bool onIsEqual(const GrEffect& s) const {
+        const ColorMatrixEffect& cme = CastEffect<ColorMatrixEffect>(s);
+        return cme.fMatrix == fMatrix;
+    }
+
+    SkColorMatrix fMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+GR_DEFINE_EFFECT_TEST(ColorMatrixEffect);
+
+GrEffectRef* ColorMatrixEffect::TestCreate(SkRandom* random,
+                                           GrContext*,
+                                           GrTexture* dummyTextures[2]) {
+    SkColorMatrix colorMatrix;
+    for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) {
+        colorMatrix.fMat[i] = random->nextSScalar1();
+    }
+    return ColorMatrixEffect::Create(colorMatrix);
 }
 
-void SkColorMatrixFilter::setMatrix(const SkColorMatrix& matrix) {
-    setup(matrix.fMat);
+GrEffectRef* SkColorMatrixFilter::asNewEffect(GrContext*) const {
+    return ColorMatrixEffect::Create(fMatrix);
 }
 
-void SkColorMatrixFilter::setArray(const SkScalar array[20]) {
-    setup(array);
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkColorMatrixFilter)
+#endif
diff --git a/src/effects/SkCornerPathEffect.cpp b/src/effects/SkCornerPathEffect.cpp
index 4da31ab..1cf797a 100644
--- a/src/effects/SkCornerPathEffect.cpp
+++ b/src/effects/SkCornerPathEffect.cpp
@@ -10,22 +10,17 @@
 #include "SkCornerPathEffect.h"
 #include "SkPath.h"
 #include "SkPoint.h"
-#include "SkBuffer.h"
+#include "SkFlattenableBuffers.h"
 
-SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) : fRadius(radius)
-{
-}
-
-SkCornerPathEffect::~SkCornerPathEffect()
-{
-}
+SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) : fRadius(radius) {}
+SkCornerPathEffect::~SkCornerPathEffect() {}
 
 static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius,
                         SkPoint* step) {
     SkScalar dist = SkPoint::Distance(a, b);
 
     step->set(b.fX - a.fX, b.fY - a.fY);
-    
+
     if (dist <= radius * 2) {
         step->scale(SK_ScalarHalf);
         return false;
@@ -36,8 +31,8 @@
 }
 
 bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                    SkScalar* width) {
-    if (fRadius == 0) {
+                                    SkStrokeRec*, const SkRect*) const {
+    if (0 == fRadius) {
         return false;
     }
 
@@ -56,7 +51,7 @@
     lastCorner.set(0, 0);
 
     for (;;) {
-        switch (verb = iter.next(pts)) {
+        switch (verb = iter.next(pts, false)) {
             case SkPath::kMove_Verb:
                     // close out the previous (open) contour
                 if (SkPath::kLine_Verb == prevVerb) {
@@ -129,21 +124,11 @@
     return true;
 }
 
-SkFlattenable::Factory SkCornerPathEffect::getFactory() {
-    return CreateProc;
-}
-
-void SkCornerPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkCornerPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
     buffer.writeScalar(fRadius);
 }
 
-SkFlattenable* SkCornerPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkCornerPathEffect, (buffer));
-}
-
 SkCornerPathEffect::SkCornerPathEffect(SkFlattenableReadBuffer& buffer) {
     fRadius = buffer.readScalar();
 }
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkCornerPathEffect)
-
diff --git a/src/effects/SkDashPathEffect.cpp b/src/effects/SkDashPathEffect.cpp
index 7950d64..1032270 100644
--- a/src/effects/SkDashPathEffect.cpp
+++ b/src/effects/SkDashPathEffect.cpp
@@ -8,7 +8,7 @@
 
 
 #include "SkDashPathEffect.h"
-#include "SkBuffer.h"
+#include "SkFlattenableBuffers.h"
 #include "SkPathMeasure.h"
 
 static inline int is_even(int x) {
@@ -16,14 +16,21 @@
 }
 
 static SkScalar FindFirstInterval(const SkScalar intervals[], SkScalar phase,
-                                  int32_t* index) {
-    int i;
-
-    for (i = 0; phase > intervals[i]; i++) {
-        phase -= intervals[i];
+                                  int32_t* index, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (phase > intervals[i]) {
+            phase -= intervals[i];
+        } else {
+            *index = i;
+            return intervals[i] - phase;
+        }
     }
-    *index = i;
-    return intervals[i] - phase;
+    // If we get here, phase "appears" to be larger than our length. This
+    // shouldn't happen with perfect precision, but we can accumulate errors
+    // during the initial length computation (rounding can make our sum be too
+    // big or too small. In that event, we just have to eat the error here.
+    *index = 0;
+    return intervals[0];
 }
 
 SkDashPathEffect::SkDashPathEffect(const SkScalar intervals[], int count,
@@ -43,29 +50,36 @@
     }
     fIntervalLength = len;
 
-    if (len > 0) {  // we don't handle 0 length dash arrays
+    // watch out for values that might make us go out of bounds
+    if ((len > 0) && SkScalarIsFinite(phase) && SkScalarIsFinite(len)) {
+
+        // Adjust phase to be between 0 and len, "flipping" phase if negative.
+        // e.g., if len is 100, then phase of -20 (or -120) is equivalent to 80
         if (phase < 0) {
             phase = -phase;
             if (phase > len) {
                 phase = SkScalarMod(phase, len);
             }
             phase = len - phase;
+
+            // Due to finite precision, it's possible that phase == len,
+            // even after the subtract (if len >>> phase), so fix that here.
+            // This fixes http://crbug.com/124652 .
+            SkASSERT(phase <= len);
+            if (phase == len) {
+                phase = 0;
+            }
         } else if (phase >= len) {
             phase = SkScalarMod(phase, len);
         }
-
-        // got to watch out for values that might make us go out of bounds
-        if (!SkScalarIsFinite(phase) || !SkScalarIsFinite(len)) {
-            goto BAD_DASH;
-        }
-
         SkASSERT(phase >= 0 && phase < len);
-        fInitialDashLength = FindFirstInterval(intervals, phase, &fInitialDashIndex);
+
+        fInitialDashLength = FindFirstInterval(intervals, phase,
+                                               &fInitialDashIndex, count);
 
         SkASSERT(fInitialDashLength >= 0);
         SkASSERT(fInitialDashIndex >= 0 && fInitialDashIndex < fCount);
     } else {
-        BAD_DASH:
         fInitialDashLength = -1;    // signal bad dash intervals
     }
 }
@@ -74,15 +88,171 @@
     sk_free(fIntervals);
 }
 
-bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                  SkScalar* width) {
-    // we do nothing if the src wants to be filled, or if our dashlength is 0
-    if (*width < 0 || fInitialDashLength < 0) {
+static void outset_for_stroke(SkRect* rect, const SkStrokeRec& rec) {
+    SkScalar radius = SkScalarHalf(rec.getWidth());
+    if (0 == radius) {
+        radius = SK_Scalar1;    // hairlines
+    }
+    if (SkPaint::kMiter_Join == rec.getJoin()) {
+        radius = SkScalarMul(radius, rec.getMiter());
+    }
+    rect->outset(radius, radius);
+}
+
+// Only handles lines for now. If returns true, dstPath is the new (smaller)
+// path. If returns false, then dstPath parameter is ignored.
+static bool cull_path(const SkPath& srcPath, const SkStrokeRec& rec,
+                      const SkRect* cullRect, SkScalar intervalLength,
+                      SkPath* dstPath) {
+    if (NULL == cullRect) {
         return false;
     }
 
-    SkPathMeasure   meas(src, false);
+    SkPoint pts[2];
+    if (!srcPath.isLine(pts)) {
+        return false;
+    }
+
+    SkRect bounds = *cullRect;
+    outset_for_stroke(&bounds, rec);
+
+    SkScalar dx = pts[1].x() - pts[0].x();
+    SkScalar dy = pts[1].y() - pts[0].y();
+
+    // just do horizontal lines for now (lazy)
+    if (dy) {
+        return false;
+    }
+
+    SkScalar minX = pts[0].fX;
+    SkScalar maxX = pts[1].fX;
+
+    if (maxX < bounds.fLeft || minX > bounds.fRight) {
+        return false;
+    }
+
+    if (dx < 0) {
+        SkTSwap(minX, maxX);
+    }
+
+    // Now we actually perform the chop, removing the excess to the left and
+    // right of the bounds (keeping our new line "in phase" with the dash,
+    // hence the (mod intervalLength).
+
+    if (minX < bounds.fLeft) {
+        minX = bounds.fLeft - SkScalarMod(bounds.fLeft - minX,
+                                          intervalLength);
+    }
+    if (maxX > bounds.fRight) {
+        maxX = bounds.fRight + SkScalarMod(maxX - bounds.fRight,
+                                           intervalLength);
+    }
+
+    SkASSERT(maxX >= minX);
+    if (dx < 0) {
+        SkTSwap(minX, maxX);
+    }
+    pts[0].fX = minX;
+    pts[1].fX = maxX;
+
+    dstPath->moveTo(pts[0]);
+    dstPath->lineTo(pts[1]);
+    return true;
+}
+
+class SpecialLineRec {
+public:
+    bool init(const SkPath& src, SkPath* dst, SkStrokeRec* rec,
+              int intervalCount, SkScalar intervalLength) {
+        if (rec->isHairlineStyle() || !src.isLine(fPts)) {
+            return false;
+        }
+
+        // can relax this in the future, if we handle square and round caps
+        if (SkPaint::kButt_Cap != rec->getCap()) {
+            return false;
+        }
+
+        SkScalar pathLength = SkPoint::Distance(fPts[0], fPts[1]);
+
+        fTangent = fPts[1] - fPts[0];
+        if (fTangent.isZero()) {
+            return false;
+        }
+
+        fPathLength = pathLength;
+        fTangent.scale(SkScalarInvert(pathLength));
+        fTangent.rotateCCW(&fNormal);
+        fNormal.scale(SkScalarHalf(rec->getWidth()));
+
+        // now estimate how many quads will be added to the path
+        //     resulting segments = pathLen * intervalCount / intervalLen
+        //     resulting points = 4 * segments
+
+        SkScalar ptCount = SkScalarMulDiv(pathLength,
+                                          SkIntToScalar(intervalCount),
+                                          intervalLength);
+        int n = SkScalarCeilToInt(ptCount) << 2;
+        dst->incReserve(n);
+
+        // we will take care of the stroking
+        rec->setFillStyle();
+        return true;
+    }
+
+    void addSegment(SkScalar d0, SkScalar d1, SkPath* path) const {
+        SkASSERT(d0 < fPathLength);
+        // clamp the segment to our length
+        if (d1 > fPathLength) {
+            d1 = fPathLength;
+        }
+
+        SkScalar x0 = fPts[0].fX + SkScalarMul(fTangent.fX, d0);
+        SkScalar x1 = fPts[0].fX + SkScalarMul(fTangent.fX, d1);
+        SkScalar y0 = fPts[0].fY + SkScalarMul(fTangent.fY, d0);
+        SkScalar y1 = fPts[0].fY + SkScalarMul(fTangent.fY, d1);
+
+        SkPoint pts[4];
+        pts[0].set(x0 + fNormal.fX, y0 + fNormal.fY);   // moveTo
+        pts[1].set(x1 + fNormal.fX, y1 + fNormal.fY);   // lineTo
+        pts[2].set(x1 - fNormal.fX, y1 - fNormal.fY);   // lineTo
+        pts[3].set(x0 - fNormal.fX, y0 - fNormal.fY);   // lineTo
+
+        path->addPoly(pts, SK_ARRAY_COUNT(pts), false);
+    }
+
+private:
+    SkPoint fPts[2];
+    SkVector fTangent;
+    SkVector fNormal;
+    SkScalar fPathLength;
+};
+
+bool SkDashPathEffect::filterPath(SkPath* dst, const SkPath& src,
+                              SkStrokeRec* rec, const SkRect* cullRect) const {
+
+#ifdef SK_IGNORE_LARGE_DASH_OPT
+    cullRect = NULL;
+#endif
+
+    // we do nothing if the src wants to be filled, or if our dashlength is 0
+    if (rec->isFillStyle() || fInitialDashLength < 0) {
+        return false;
+    }
+
     const SkScalar* intervals = fIntervals;
+    SkScalar        dashCount = 0;
+
+    SkPath cullPathStorage;
+    const SkPath* srcPtr = &src;
+    if (cull_path(src, *rec, cullRect, fIntervalLength, &cullPathStorage)) {
+        srcPtr = &cullPathStorage;
+    }
+
+    SpecialLineRec lineRec;
+    bool specialLine = lineRec.init(*srcPtr, dst, rec, fCount >> 1, fIntervalLength);
+
+    SkPathMeasure   meas(*srcPtr, false);
 
     do {
         bool        skipFirstSegment = meas.isClosed();
@@ -91,6 +261,21 @@
         int         index = fInitialDashIndex;
         SkScalar    scale = SK_Scalar1;
 
+        // Since the path length / dash length ratio may be arbitrarily large, we can exert
+        // significant memory pressure while attempting to build the filtered path. To avoid this,
+        // we simply give up dashing beyond a certain threshold.
+        //
+        // The original bug report (http://crbug.com/165432) is based on a path yielding more than
+        // 90 million dash segments and crashing the memory allocator. A limit of 1 million
+        // segments seems reasonable: at 2 verbs per segment * 9 bytes per verb, this caps the
+        // maximum dash memory overhead at roughly 17MB per path.
+        static const SkScalar kMaxDashCount = 1000000;
+        dashCount += length * (fCount >> 1) / fIntervalLength;
+        if (dashCount > kMaxDashCount) {
+            dst->reset();
+            return false;
+        }
+
         if (fScaleToFit) {
             if (fIntervalLength >= length) {
                 scale = SkScalarDiv(length, fIntervalLength);
@@ -101,15 +286,26 @@
             }
         }
 
-        SkScalar    distance = 0;
-        SkScalar    dlen = SkScalarMul(fInitialDashLength, scale);
+        // Using double precision to avoid looping indefinitely due to single precision rounding
+        // (for extreme path_length/dash_length ratios). See test_infinite_dash() unittest.
+        double  distance = 0;
+        double  dlen = SkScalarMul(fInitialDashLength, scale);
 
         while (distance < length) {
             SkASSERT(dlen >= 0);
             addedSegment = false;
             if (is_even(index) && dlen > 0 && !skipFirstSegment) {
                 addedSegment = true;
-                meas.getSegment(distance, distance + dlen, dst, true);
+
+                if (specialLine) {
+                    lineRec.addSegment(SkDoubleToScalar(distance),
+                                       SkDoubleToScalar(distance + dlen),
+                                       dst);
+                } else {
+                    meas.getSegment(SkDoubleToScalar(distance),
+                                    SkDoubleToScalar(distance + dlen),
+                                    dst, true);
+                }
             }
             distance += dlen;
 
@@ -133,6 +329,200 @@
             meas.getSegment(0, SkScalarMul(fInitialDashLength, scale), dst, !addedSegment);
         }
     } while (meas.nextContour());
+
+    return true;
+}
+
+// Currently asPoints is more restrictive then it needs to be. In the future
+// we need to:
+//      allow kRound_Cap capping (could allow rotations in the matrix with this)
+//      allow paths to be returned
+bool SkDashPathEffect::asPoints(PointData* results,
+                                const SkPath& src,
+                                const SkStrokeRec& rec,
+                                const SkMatrix& matrix,
+                                const SkRect* cullRect) const {
+    // width < 0 -> fill && width == 0 -> hairline so requiring width > 0 rules both out
+    if (fInitialDashLength < 0 || 0 >= rec.getWidth()) {
+        return false;
+    }
+
+    // TODO: this next test could be eased up. We could allow any number of
+    // intervals as long as all the ons match and all the offs match.
+    // Additionally, they do not necessarily need to be integers.
+    // We cannot allow arbitrary intervals since we want the returned points
+    // to be uniformly sized.
+    if (fCount != 2 ||
+        !SkScalarNearlyEqual(fIntervals[0], fIntervals[1]) ||
+        !SkScalarIsInt(fIntervals[0]) ||
+        !SkScalarIsInt(fIntervals[1])) {
+        return false;
+    }
+
+    // TODO: this next test could be eased up. The rescaling should not impact
+    // the equality of the ons & offs. However, we would need to remove the
+    // integer intervals restriction first
+    if (fScaleToFit) {
+        return false;
+    }
+
+    SkPoint pts[2];
+
+    if (!src.isLine(pts)) {
+        return false;
+    }
+
+    // TODO: this test could be eased up to allow circles
+    if (SkPaint::kButt_Cap != rec.getCap()) {
+        return false;
+    }
+
+    // TODO: this test could be eased up for circles. Rotations could be allowed.
+    if (!matrix.rectStaysRect()) {
+        return false;
+    }
+
+    SkScalar        length = SkPoint::Distance(pts[1], pts[0]);
+
+    SkVector tangent = pts[1] - pts[0];
+    if (tangent.isZero()) {
+        return false;
+    }
+
+    tangent.scale(SkScalarInvert(length));
+
+    // TODO: make this test for horizontal & vertical lines more robust
+    bool isXAxis = true;
+    if (SK_Scalar1 == tangent.fX || -SK_Scalar1 == tangent.fX) {
+        results->fSize.set(SkScalarHalf(fIntervals[0]), SkScalarHalf(rec.getWidth()));
+    } else if (SK_Scalar1 == tangent.fY || -SK_Scalar1 == tangent.fY) {
+        results->fSize.set(SkScalarHalf(rec.getWidth()), SkScalarHalf(fIntervals[0]));
+        isXAxis = false;
+    } else if (SkPaint::kRound_Cap != rec.getCap()) {
+        // Angled lines don't have axis-aligned boxes.
+        return false;
+    }
+
+    if (NULL != results) {
+        results->fFlags = 0;
+        SkScalar clampedInitialDashLength = SkMinScalar(length, fInitialDashLength);
+
+        if (SkPaint::kRound_Cap == rec.getCap()) {
+            results->fFlags |= PointData::kCircles_PointFlag;
+        }
+
+        results->fNumPoints = 0;
+        SkScalar len2 = length;
+        if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
+            SkASSERT(len2 >= clampedInitialDashLength);
+            if (0 == fInitialDashIndex) {
+                if (clampedInitialDashLength > 0) {
+                    if (clampedInitialDashLength >= fIntervals[0]) {
+                        ++results->fNumPoints;  // partial first dash
+                    }
+                    len2 -= clampedInitialDashLength;
+                }
+                len2 -= fIntervals[1];  // also skip first space
+                if (len2 < 0) {
+                    len2 = 0;
+                }
+            } else {
+                len2 -= clampedInitialDashLength; // skip initial partial empty
+            }
+        }
+        int numMidPoints = SkScalarFloorToInt(SkScalarDiv(len2, fIntervalLength));
+        results->fNumPoints += numMidPoints;
+        len2 -= numMidPoints * fIntervalLength;
+        bool partialLast = false;
+        if (len2 > 0) {
+            if (len2 < fIntervals[0]) {
+                partialLast = true;
+            } else {
+                ++numMidPoints;
+                ++results->fNumPoints;
+            }
+        }
+
+        results->fPoints = new SkPoint[results->fNumPoints];
+
+        SkScalar    distance = 0;
+        int         curPt = 0;
+
+        if (clampedInitialDashLength > 0 || 0 == fInitialDashIndex) {
+            SkASSERT(clampedInitialDashLength <= length);
+
+            if (0 == fInitialDashIndex) {
+                if (clampedInitialDashLength > 0) {
+                    // partial first block
+                    SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
+                    SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, SkScalarHalf(clampedInitialDashLength));
+                    SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, SkScalarHalf(clampedInitialDashLength));
+                    SkScalar halfWidth, halfHeight;
+                    if (isXAxis) {
+                        halfWidth = SkScalarHalf(clampedInitialDashLength);
+                        halfHeight = SkScalarHalf(rec.getWidth());
+                    } else {
+                        halfWidth = SkScalarHalf(rec.getWidth());
+                        halfHeight = SkScalarHalf(clampedInitialDashLength);
+                    }
+                    if (clampedInitialDashLength < fIntervals[0]) {
+                        // This one will not be like the others
+                        results->fFirst.addRect(x - halfWidth, y - halfHeight,
+                                                x + halfWidth, y + halfHeight);
+                    } else {
+                        SkASSERT(curPt < results->fNumPoints);
+                        results->fPoints[curPt].set(x, y);
+                        ++curPt;
+                    }
+
+                    distance += clampedInitialDashLength;
+                }
+
+                distance += fIntervals[1];  // skip over the next blank block too
+            } else {
+                distance += clampedInitialDashLength;
+            }
+        }
+
+        if (0 != numMidPoints) {
+            distance += SkScalarHalf(fIntervals[0]);
+
+            for (int i = 0; i < numMidPoints; ++i) {
+                SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance);
+                SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance);
+
+                SkASSERT(curPt < results->fNumPoints);
+                results->fPoints[curPt].set(x, y);
+                ++curPt;
+
+                distance += fIntervalLength;
+            }
+
+            distance -= SkScalarHalf(fIntervals[0]);
+        }
+
+        if (partialLast) {
+            // partial final block
+            SkASSERT(SkPaint::kRound_Cap != rec.getCap()); // can't handle partial circles
+            SkScalar temp = length - distance;
+            SkASSERT(temp < fIntervals[0]);
+            SkScalar x = pts[0].fX + SkScalarMul(tangent.fX, distance + SkScalarHalf(temp));
+            SkScalar y = pts[0].fY + SkScalarMul(tangent.fY, distance + SkScalarHalf(temp));
+            SkScalar halfWidth, halfHeight;
+            if (isXAxis) {
+                halfWidth = SkScalarHalf(temp);
+                halfHeight = SkScalarHalf(rec.getWidth());
+            } else {
+                halfWidth = SkScalarHalf(rec.getWidth());
+                halfHeight = SkScalarHalf(temp);
+            }
+            results->fLast.addRect(x - halfWidth, y - halfHeight,
+                                   x + halfWidth, y + halfHeight);
+        }
+
+        SkASSERT(curPt == results->fNumPoints);
+    }
+
     return true;
 }
 
@@ -140,32 +530,28 @@
     return fInitialDashLength < 0 ? NULL : CreateProc;
 }
 
-void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkDashPathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
     SkASSERT(fInitialDashLength >= 0);
 
-    buffer.write32(fCount);
-    buffer.write32(fInitialDashIndex);
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt(fInitialDashIndex);
     buffer.writeScalar(fInitialDashLength);
     buffer.writeScalar(fIntervalLength);
-    buffer.write32(fScaleToFit);
-    buffer.writeMul4(fIntervals, fCount * sizeof(fIntervals[0]));
+    buffer.writeBool(fScaleToFit);
+    buffer.writeScalarArray(fIntervals, fCount);
 }
 
 SkFlattenable* SkDashPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
     return SkNEW_ARGS(SkDashPathEffect, (buffer));
 }
 
-SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) {
-    fCount = buffer.readS32();
-    fInitialDashIndex = buffer.readS32();
+SkDashPathEffect::SkDashPathEffect(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fInitialDashIndex = buffer.readInt();
     fInitialDashLength = buffer.readScalar();
     fIntervalLength = buffer.readScalar();
-    fScaleToFit = (buffer.readS32() != 0);
-    
+    fScaleToFit = buffer.readBool();
+
+    fCount = buffer.getArrayCount();
     fIntervals = (SkScalar*)sk_malloc_throw(sizeof(SkScalar) * fCount);
-    buffer.read(fIntervals, fCount * sizeof(fIntervals[0]));
+    buffer.readScalarArray(fIntervals);
 }
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkDashPathEffect)
diff --git a/src/effects/SkDiscretePathEffect.cpp b/src/effects/SkDiscretePathEffect.cpp
index a438859..2c95208 100644
--- a/src/effects/SkDiscretePathEffect.cpp
+++ b/src/effects/SkDiscretePathEffect.cpp
@@ -8,7 +8,7 @@
 
 
 #include "SkDiscretePathEffect.h"
-#include "SkBuffer.h"
+#include "SkFlattenableBuffers.h"
 #include "SkPathMeasure.h"
 #include "SkRandom.h"
 
@@ -26,8 +26,8 @@
 }
 
 bool SkDiscretePathEffect::filterPath(SkPath* dst, const SkPath& src,
-                                      SkScalar* width) {
-    bool doFill = *width < 0;
+                                      SkStrokeRec* rec, const SkRect*) const {
+    bool doFill = rec->isFillStyle();
 
     SkPathMeasure   meas(src, doFill);
     uint32_t        seed = SkScalarRound(meas.getLength());
@@ -50,14 +50,17 @@
                 n -= 1;
                 distance += delta/2;
             }
-            meas.getPosTan(distance, &p, &v);
-            Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale));
-            dst->moveTo(p);
+
+            if (meas.getPosTan(distance, &p, &v)) {
+                Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale));
+                dst->moveTo(p);
+            }
             while (--n >= 0) {
                 distance += delta;
-                meas.getPosTan(distance, &p, &v);
-                Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale));
-                dst->lineTo(p);
+                if (meas.getPosTan(distance, &p, &v)) {
+                    Perterb(&p, v, SkScalarMul(rand.nextSScalar1(), scale));
+                    dst->lineTo(p);
+                }
             }
             if (meas.isClosed()) {
                 dst->close();
@@ -67,15 +70,8 @@
     return true;
 }
 
-SkFlattenable::Factory SkDiscretePathEffect::getFactory() {
-    return CreateProc;
-}
-
-SkFlattenable* SkDiscretePathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkDiscretePathEffect, (buffer));
-}
-
-void SkDiscretePathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkDiscretePathEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
     buffer.writeScalar(fSegLength);
     buffer.writeScalar(fPerterb);
 }
@@ -84,8 +80,3 @@
     fSegLength = buffer.readScalar();
     fPerterb = buffer.readScalar();
 }
-
-///////////////////////////////////////////////////////////////////////////////
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkDiscretePathEffect)
-
diff --git a/src/effects/SkDisplacementMapEffect.cpp b/src/effects/SkDisplacementMapEffect.cpp
new file mode 100644
index 0000000..f652bb2
--- /dev/null
+++ b/src/effects/SkDisplacementMapEffect.cpp
@@ -0,0 +1,518 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkDisplacementMapEffect.h"
+#include "SkFlattenableBuffers.h"
+#include "SkUnPreMultiply.h"
+#include "SkColorPriv.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "GrTBackendEffectFactory.h"
+#include "SkImageFilterUtils.h"
+#endif
+
+namespace {
+
+template<SkDisplacementMapEffect::ChannelSelectorType type>
+uint32_t getValue(SkColor, const SkUnPreMultiply::Scale*) {
+    SkASSERT(!"Unknown channel selector");
+    return 0;
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kR_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedR32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kG_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedG32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kB_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale* table) {
+    return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedB32(l));
+}
+
+template<> uint32_t getValue<SkDisplacementMapEffect::kA_ChannelSelectorType>(
+    SkColor l, const SkUnPreMultiply::Scale*) {
+    return SkGetPackedA32(l);
+}
+
+template<SkDisplacementMapEffect::ChannelSelectorType typeX,
+         SkDisplacementMapEffect::ChannelSelectorType typeY>
+void computeDisplacement(SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    static const SkScalar Inv8bit = SkScalarDiv(SK_Scalar1, SkFloatToScalar(255.0f));
+    static const SkScalar Half8bit = SkFloatToScalar(255.0f * 0.5f);
+    const int dstW = displ->width();
+    const int dstH = displ->height();
+    const int srcW = src->width();
+    const int srcH = src->height();
+    const SkScalar scaleX = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstW)), Inv8bit);
+    const SkScalar scaleY = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstH)), Inv8bit);
+    const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
+    for (int y = 0; y < dstH; ++y) {
+        const SkPMColor* displPtr = displ->getAddr32(0, y);
+        SkPMColor* dstPtr = dst->getAddr32(0, y);
+        for (int x = 0; x < dstW; ++x, ++displPtr, ++dstPtr) {
+            const SkScalar displX =
+                SkScalarMul(scaleX, SkIntToScalar(getValue<typeX>(*displPtr, table))-Half8bit);
+            const SkScalar displY =
+                SkScalarMul(scaleY, SkIntToScalar(getValue<typeY>(*displPtr, table))-Half8bit);
+            const int coordX = x + SkScalarRoundToInt(displX);
+            const int coordY = y + SkScalarRoundToInt(displY);
+            *dstPtr = ((coordX < 0) || (coordX >= srcW) || (coordY < 0) || (coordY >= srcH)) ?
+                      0 : *(src->getAddr32(coordX, coordY));
+        }
+    }
+}
+
+template<SkDisplacementMapEffect::ChannelSelectorType typeX>
+void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    switch (yChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kR_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kG_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kB_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        computeDisplacement<typeX, SkDisplacementMapEffect::kA_ChannelSelectorType>(
+            scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown Y channel selector");
+    }
+}
+
+void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                         SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                         SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
+{
+    switch (xChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kR_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kG_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kB_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        computeDisplacement<SkDisplacementMapEffect::kA_ChannelSelectorType>(
+            yChannelSelector, scale, dst, displ, src);
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown X channel selector");
+    }
+}
+
+} // end namespace
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDisplacementMapEffect::SkDisplacementMapEffect(ChannelSelectorType xChannelSelector,
+                                                 ChannelSelectorType yChannelSelector,
+                                                 SkScalar scale,
+                                                 SkImageFilter* displacement,
+                                                 SkImageFilter* color)
+  : INHERITED(displacement, color)
+  , fXChannelSelector(xChannelSelector)
+  , fYChannelSelector(yChannelSelector)
+  , fScale(scale)
+{
+}
+
+SkDisplacementMapEffect::~SkDisplacementMapEffect() {
+}
+
+SkDisplacementMapEffect::SkDisplacementMapEffect(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer)
+{
+    fXChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
+    fYChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
+    fScale            = buffer.readScalar();
+}
+
+void SkDisplacementMapEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt((int) fXChannelSelector);
+    buffer.writeInt((int) fYChannelSelector);
+    buffer.writeScalar(fScale);
+}
+
+bool SkDisplacementMapEffect::onFilterImage(Proxy* proxy,
+                                            const SkBitmap& src,
+                                            const SkMatrix& ctm,
+                                            SkBitmap* dst,
+                                            SkIPoint* offset) {
+    SkBitmap displ, color = src;
+    SkImageFilter* colorInput = getColorInput();
+    SkImageFilter* displacementInput = getDisplacementInput();
+    SkASSERT(NULL != displacementInput);
+    if ((colorInput && !colorInput->filterImage(proxy, src, ctm, &color, offset)) ||
+        !displacementInput->filterImage(proxy, src, ctm, &displ, offset)) {
+        return false;
+    }
+    if ((displ.config() != SkBitmap::kARGB_8888_Config) ||
+        (color.config() != SkBitmap::kARGB_8888_Config)) {
+        return false;
+    }
+
+    SkAutoLockPixels alp_displacement(displ), alp_color(color);
+    if (!displ.getPixels() || !color.getPixels()) {
+        return false;
+    }
+    dst->setConfig(displ.config(), displ.width(), displ.height());
+    dst->allocPixels();
+    if (!dst->getPixels()) {
+        return false;
+    }
+
+    computeDisplacement(fXChannelSelector, fYChannelSelector, fScale, dst, &displ, &color);
+
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+class GrGLDisplacementMapEffect : public GrGLEffect {
+public:
+    GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory,
+                              const GrEffectRef& effect);
+    virtual ~GrGLDisplacementMapEffect();
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
+
+private:
+    SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
+    SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
+    GrGLEffectMatrix fDisplacementEffectMatrix;
+    GrGLEffectMatrix fColorEffectMatrix;
+    GrGLUniformManager::UniformHandle fScaleUni;
+    GrGLUniformManager::UniformHandle fYSignColor;
+    GrGLUniformManager::UniformHandle fYSignDispl;
+
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrDisplacementMapEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                               SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                               SkScalar scale, GrTexture* displacement, GrTexture* color) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrDisplacementMapEffect, (xChannelSelector,
+                                                                    yChannelSelector,
+                                                                    scale,
+                                                                    displacement,
+                                                                    color)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrDisplacementMapEffect();
+
+    const GrBackendEffectFactory& getFactory() const;
+    SkDisplacementMapEffect::ChannelSelectorType xChannelSelector() const
+        { return fXChannelSelector; }
+    SkDisplacementMapEffect::ChannelSelectorType yChannelSelector() const
+        { return fYChannelSelector; }
+    SkScalar scale() const { return fScale; }
+
+    typedef GrGLDisplacementMapEffect GLEffect;
+    static const char* Name() { return "DisplacementMap"; }
+
+    void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                            SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                            SkScalar scale, GrTexture* displacement, GrTexture* color);
+
+    GR_DECLARE_EFFECT_TEST;
+
+    GrTextureAccess             fDisplacementAccess;
+    GrTextureAccess             fColorAccess;
+    SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
+    SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
+    SkScalar fScale;
+
+    typedef GrEffect INHERITED;
+};
+
+bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap colorBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getColorInput(), proxy, src, &colorBM)) {
+        return false;
+    }
+    GrTexture* color = (GrTexture*) colorBM.getTexture();
+    SkBitmap displacementBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getDisplacementInput(), proxy, src, &displacementBM)) {
+        return false;
+    }
+    GrTexture* displacement = (GrTexture*) displacementBM.getTexture();
+    GrContext* context = color->getContext();
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = src.width();
+    desc.fHeight = src.height();
+    desc.fConfig = kSkia8888_GrPixelConfig;
+
+    GrAutoScratchTexture ast(context, desc);
+    SkAutoTUnref<GrTexture> dst(ast.detach());
+
+    GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
+
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(
+        GrDisplacementMapEffect::Create(fXChannelSelector,
+                                        fYChannelSelector,
+                                        fScale,
+                                        displacement,
+                                        color))->unref();
+    SkRect srcRect;
+    src.getBounds(&srcRect);
+    context->drawRect(paint, srcRect);
+    return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDisplacementMapEffect::GrDisplacementMapEffect(
+                             SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
+                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
+                             SkScalar scale,
+                             GrTexture* displacement,
+                             GrTexture* color)
+    : fDisplacementAccess(displacement)
+    , fColorAccess(color)
+    , fXChannelSelector(xChannelSelector)
+    , fYChannelSelector(yChannelSelector)
+    , fScale(scale) {
+    this->addTextureAccess(&fDisplacementAccess);
+    this->addTextureAccess(&fColorAccess);
+}
+
+GrDisplacementMapEffect::~GrDisplacementMapEffect() {
+}
+
+bool GrDisplacementMapEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrDisplacementMapEffect& s = CastEffect<GrDisplacementMapEffect>(sBase);
+    return fDisplacementAccess.getTexture() == s.fDisplacementAccess.getTexture() &&
+           fColorAccess.getTexture() == s.fColorAccess.getTexture() &&
+           fXChannelSelector == s.fXChannelSelector &&
+           fYChannelSelector == s.fYChannelSelector &&
+           fScale == s.fScale;
+}
+
+const GrBackendEffectFactory& GrDisplacementMapEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrDisplacementMapEffect>::getInstance();
+}
+
+void GrDisplacementMapEffect::getConstantColorComponents(GrColor* color,
+                                                         uint32_t* validFlags) const {
+    // Any displacement offset bringing a pixel out of bounds will output a color of (0,0,0,0),
+    // so the only way we'd get a constant alpha is if the input color image has a constant alpha
+    // and no displacement offset push any texture coordinates out of bounds OR if the constant
+    // alpha is 0. Since this isn't trivial to compute at this point, let's assume the output is
+    // not of constant color when a displacement effect is applied.
+    *validFlags = 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrDisplacementMapEffect);
+
+GrEffectRef* GrDisplacementMapEffect::TestCreate(SkRandom* random,
+                                                 GrContext* context,
+                                                 GrTexture* textures[]) {
+    int texIdxDispl = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                           GrEffectUnitTest::kAlphaTextureIdx;
+    int texIdxColor = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                           GrEffectUnitTest::kAlphaTextureIdx;
+    static const int kMaxComponent = 4;
+    SkDisplacementMapEffect::ChannelSelectorType xChannelSelector =
+        static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
+        random->nextRangeU(1, kMaxComponent));
+    SkDisplacementMapEffect::ChannelSelectorType yChannelSelector =
+        static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
+        random->nextRangeU(1, kMaxComponent));
+    SkScalar scale = random->nextUScalar1();
+
+    return GrDisplacementMapEffect::Create(xChannelSelector, yChannelSelector, scale,
+                                           textures[texIdxDispl], textures[texIdxColor]);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLDisplacementMapEffect::GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory,
+                                                     const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fXChannelSelector(CastEffect<GrDisplacementMapEffect>(effect).xChannelSelector())
+    , fYChannelSelector(CastEffect<GrDisplacementMapEffect>(effect).yChannelSelector()) {
+}
+
+GrGLDisplacementMapEffect::~GrGLDisplacementMapEffect() {
+}
+
+void GrGLDisplacementMapEffect::emitCode(GrGLShaderBuilder* builder,
+                               const GrEffectStage&,
+                               EffectKey key,
+                               const char* vertexCoords,
+                               const char* outputColor,
+                               const char* inputColor,
+                               const TextureSamplerArray& samplers) {
+    fScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                    kVec2f_GrSLType, "Scale");
+    const char* scaleUni = builder->getUniformCStr(fScaleUni);
+
+    const char* dCoordsIn;
+    GrSLType dCoordsType = fDisplacementEffectMatrix.emitCode(
+                                builder, key, vertexCoords, &dCoordsIn, NULL, "DISPL");
+    const char* cCoordsIn;
+    GrSLType cCoordsType = fColorEffectMatrix.emitCode(
+                                builder, key, vertexCoords, &cCoordsIn, NULL, "COLOR");
+
+    SkString* code = &builder->fFSCode;
+    const char* dColor = "dColor";
+    const char* cCoords = "cCoords";
+    const char* nearZero = "1e-6"; // Since 6.10352e−5 is the smallest half float, use
+                                   // a number smaller than that to approximate 0, but
+                                   // leave room for 32-bit float GPU rounding errors.
+
+    code->appendf("\t\tvec4 %s = ", dColor);
+    builder->appendTextureLookup(code, samplers[0], dCoordsIn, dCoordsType);
+    code->append(";\n");
+
+    // Unpremultiply the displacement
+    code->appendf("\t\t%s.rgb = (%s.a < %s) ? vec3(0.0) : clamp(%s.rgb / %s.a, 0.0, 1.0);",
+                  dColor, dColor, nearZero, dColor, dColor);
+
+    code->appendf("\t\tvec2 %s = %s + %s*(%s.",
+                  cCoords, cCoordsIn, scaleUni, dColor);
+
+    switch (fXChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        code->append("r");
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        code->append("g");
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        code->append("b");
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        code->append("a");
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown X channel selector");
+    }
+
+    switch (fYChannelSelector) {
+      case SkDisplacementMapEffect::kR_ChannelSelectorType:
+        code->append("r");
+        break;
+      case SkDisplacementMapEffect::kG_ChannelSelectorType:
+        code->append("g");
+        break;
+      case SkDisplacementMapEffect::kB_ChannelSelectorType:
+        code->append("b");
+        break;
+      case SkDisplacementMapEffect::kA_ChannelSelectorType:
+        code->append("a");
+        break;
+      case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
+      default:
+        SkASSERT(!"Unknown Y channel selector");
+    }
+    code->append("-vec2(0.5));\t\t");
+
+    // FIXME : This can be achieved with a "clamp to border" texture repeat mode and
+    //         a 0 border color instead of computing if cCoords is out of bounds here.
+    code->appendf(
+        "%s = any(greaterThan(vec4(vec2(0.0), %s), vec4(%s, vec2(1.0)))) ? vec4(0.0) : ",
+        outputColor, cCoords, cCoords);
+    builder->appendTextureLookup(code, samplers[1], cCoords, cCoordsType);
+    code->append(";\n");
+}
+
+void GrGLDisplacementMapEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrDisplacementMapEffect& displacementMap = GetEffectFromStage<GrDisplacementMapEffect>(stage);
+    GrTexture* displTex = displacementMap.texture(0);
+    GrTexture* colorTex = displacementMap.texture(1);
+    fDisplacementEffectMatrix.setData(uman,
+                                     GrEffect::MakeDivByTextureWHMatrix(displTex),
+                                     stage.getCoordChangeMatrix(),
+                                     displTex);
+    fColorEffectMatrix.setData(uman,
+                               GrEffect::MakeDivByTextureWHMatrix(colorTex),
+                               stage.getCoordChangeMatrix(),
+                               colorTex);
+
+    uman.set2f(fScaleUni, SkScalarToFloat(displacementMap.scale()),
+                colorTex->origin() == kTopLeft_GrSurfaceOrigin ?
+                SkScalarToFloat(displacementMap.scale()) :
+                SkScalarToFloat(-displacementMap.scale()));
+}
+
+GrGLEffect::EffectKey GrGLDisplacementMapEffect::GenKey(const GrEffectStage& stage,
+                                                        const GrGLCaps&) {
+    const GrDisplacementMapEffect& displacementMap =
+        GetEffectFromStage<GrDisplacementMapEffect>(stage);
+
+    GrTexture* displTex = displacementMap.texture(0);
+    GrTexture* colorTex = displacementMap.texture(1);
+
+    EffectKey displKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(displTex),
+                                                  stage.getCoordChangeMatrix(),
+                                                  displTex);
+
+    EffectKey colorKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(colorTex),
+                                                  stage.getCoordChangeMatrix(),
+                                                  colorTex);
+
+    colorKey <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey xKey = displacementMap.xChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits);
+    EffectKey yKey = displacementMap.yChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits +
+                                                            SkDisplacementMapEffect::kKeyBits);
+
+    return xKey | yKey | displKey | colorKey;
+}
+#endif
diff --git a/src/effects/SkEffects.cpp b/src/effects/SkEffects.cpp
deleted file mode 100644
index d4ccac0..0000000
--- a/src/effects/SkEffects.cpp
+++ /dev/null
@@ -1,53 +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 "SkTypes.h"
-
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-
-#include "Sk1DPathEffect.h"
-#include "Sk2DPathEffect.h"
-#include "SkAvoidXfermode.h"
-#include "SkBlurDrawLooper.h"
-#include "SkBlurImageFilter.h"
-#include "SkBlurMaskFilter.h"
-#include "SkColorFilter.h"
-#include "SkColorMatrixFilter.h"
-#include "SkCornerPathEffect.h"
-#include "SkDashPathEffect.h"
-#include "SkDiscretePathEffect.h"
-#include "SkEffects.h"
-#include "SkFlattenable.h"
-#include "SkGradientShader.h"
-#include "SkGroupShape.h"
-#include "SkLayerDrawLooper.h"
-#include "SkLayerRasterizer.h"
-#include "SkPathEffect.h"
-#include "SkPixelXorXfermode.h"
-#include "SkRectShape.h"
-
-void SkEffects::Init() {
-    SkAvoidXfermode::Init();
-    SkBlurDrawLooper::Init();
-    SkBlurImageFilter::Init();
-    SkBlurMaskFilter::Init();
-    SkColorFilter::Init();
-    SkColorMatrixFilter::Init();
-    SkCornerPathEffect::Init();
-    SkDashPathEffect::Init();
-    SkDiscretePathEffect::Init();
-    SkGradientShader::Init();
-    SkGroupShape::Init();
-    SkLayerDrawLooper::Init();
-    SkLayerRasterizer::Init();
-    SkPath1DPathEffect::Init();
-    SkPath2DPathEffect::Init();
-    SkPixelXorXfermode::Init();
-    SkRectShape::Init();
-}
-
-#endif
diff --git a/src/effects/SkEffects_none.cpp b/src/effects/SkEffects_none.cpp
deleted file mode 100644
index 519fcf7..0000000
--- a/src/effects/SkEffects_none.cpp
+++ /dev/null
@@ -1,17 +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 "SkTypes.h"
-
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-
-#include "../../include/effects/SkEffects.h"
-
-void SkEffects::Init() {
-}
-
-#endif
diff --git a/src/effects/SkEmbossMask.cpp b/src/effects/SkEmbossMask.cpp
index 300494b..32e9b23 100644
--- a/src/effects/SkEmbossMask.cpp
+++ b/src/effects/SkEmbossMask.cpp
@@ -161,5 +161,3 @@
         prev_row = rowBytes;
     }
 }
-
-
diff --git a/src/effects/SkEmbossMask.h b/src/effects/SkEmbossMask.h
index 15e2474..7053be2 100644
--- a/src/effects/SkEmbossMask.h
+++ b/src/effects/SkEmbossMask.h
@@ -18,4 +18,3 @@
 };
 
 #endif
-
diff --git a/src/effects/SkEmbossMaskFilter.cpp b/src/effects/SkEmbossMaskFilter.cpp
index ce37718..0c91585 100644
--- a/src/effects/SkEmbossMaskFilter.cpp
+++ b/src/effects/SkEmbossMaskFilter.cpp
@@ -11,7 +11,7 @@
 #include "SkBlurMaskFilter.h"
 #include "SkBlurMask.h"
 #include "SkEmbossMask.h"
-#include "SkBuffer.h"
+#include "SkFlattenableBuffers.h"
 
 static inline int pin2byte(int n) {
     if (n < 0) {
@@ -36,11 +36,11 @@
     int sp = pin2byte(SkScalarToFixed(specular) >> 12);
 
     SkEmbossMaskFilter::Light   light;
-    
+
     memcpy(light.fDirection, direction, sizeof(light.fDirection));
     light.fAmbient = SkToU8(am);
     light.fSpecular = SkToU8(sp);
-    
+
     return SkNEW_ARGS(SkEmbossMaskFilter, (light, blurRadius));
 }
 
@@ -60,12 +60,12 @@
     normalize(fLight.fDirection);
 }
 
-SkMask::Format SkEmbossMaskFilter::getFormat() {
+SkMask::Format SkEmbossMaskFilter::getFormat() const {
     return SkMask::k3D_Format;
 }
 
 bool SkEmbossMaskFilter::filterMask(SkMask* dst, const SkMask& src,
-                                    const SkMatrix& matrix, SkIPoint* margin) {
+                            const SkMatrix& matrix, SkIPoint* margin) const {
     SkScalar radius = matrix.mapRadius(fBlurRadius);
 
     if (!SkBlurMask::Blur(dst, src, radius, SkBlurMask::kInner_Style,
@@ -115,26 +115,19 @@
     return true;
 }
 
-SkFlattenable* SkEmbossMaskFilter::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkEmbossMaskFilter, (buffer));
-}
-
-SkFlattenable::Factory SkEmbossMaskFilter::getFactory() {
-    return CreateProc;
-}
-
 SkEmbossMaskFilter::SkEmbossMaskFilter(SkFlattenableReadBuffer& buffer)
         : SkMaskFilter(buffer) {
-    buffer.read(&fLight, sizeof(fLight));
+    SkASSERT(buffer.getArrayCount() == sizeof(Light));
+    buffer.readByteArray(&fLight);
     SkASSERT(fLight.fPad == 0); // for the font-cache lookup to be clean
     fBlurRadius = buffer.readScalar();
 }
 
-void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkEmbossMaskFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    fLight.fPad = 0;    // for the font-cache lookup to be clean
-    buffer.writeMul4(&fLight, sizeof(fLight));
+    Light tmpLight = fLight;
+    tmpLight.fPad = 0;    // for the font-cache lookup to be clean
+    buffer.writeByteArray(&tmpLight, sizeof(tmpLight));
     buffer.writeScalar(fBlurRadius);
 }
-
diff --git a/src/effects/SkEmbossMask_Table.h b/src/effects/SkEmbossMask_Table.h
index fc29a15..0d331ee 100644
--- a/src/effects/SkEmbossMask_Table.h
+++ b/src/effects/SkEmbossMask_Table.h
@@ -10,1029 +10,1029 @@
 #include "SkTypes.h"
 
 static const uint16_t gInvSqrtTable[128 * 128] = {
-    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 
-    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 
-    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
-    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 
-    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 
-    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
-    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 
-    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 
-    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
-    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 
-    0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
-    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
-    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0618, 0x0618, 0x05D1, 
-    0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
-    0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 
-    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 
-    0x0590, 0x0590, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
-    0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 
-    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 0x05D1, 
-    0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 
-    0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
-    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590, 
-    0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 
-    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
-    0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 
-    0x0555, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 
-    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
-    0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
-    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590, 0x0590, 
-    0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 
-    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
-    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0555, 
-    0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 
-    0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 
-    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 
-    0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 
-    0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 
-    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x051E, 
-    0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E, 
-    0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 
-    0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
-    0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 
-    0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x0375, 
-    0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 
-    0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
-    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 
-    0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 
-    0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 
-    0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
-    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC, 
-    0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x0375, 
-    0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 
-    0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 
-    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD, 
-    0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 
-    0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 
-    0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 
-    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
-    0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 
-    0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E, 
-    0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 
-    0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
-    0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
-    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492, 
-    0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 
-    0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 
-    0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
-    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
-    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 
-    0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 
-    0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 
-    0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
-    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 
-    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
-    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469, 
-    0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333, 
-    0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 
-    0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 
-    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 
-    0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
-    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444, 
-    0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 
-    0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 
-    0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 
-    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 
-    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
-    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
-    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0444, 0x0444, 0x0421, 
-    0x0421, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F, 
-    0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 
-    0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 
-    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 
-    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
-    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
-    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 
-    0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x031F, 
-    0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 
-    0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 
-    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 
-    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
-    0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
-    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
-    0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x0400, 
-    0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 
-    0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 
-    0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 
-    0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 
-    0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
-    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
-    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
-    0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0, 
-    0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 
-    0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253, 
-    0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 
-    0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 
-    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
-    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 
-    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
-    0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 
-    0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 
-    0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 
-    0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 
-    0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 
-    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 
-    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
-    0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
-    0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 
-    0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 
-    0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 
-    0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 
-    0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 
-    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 
-    0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
-    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
-    0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 
-    0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 
-    0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 
-    0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 
-    0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 
-    0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 
-    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
-    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
-    0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E, 
-    0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 
-    0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 
-    0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 
-    0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 
-    0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 
-    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 
-    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
-    0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x0375, 
-    0x0375, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 
-    0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 
-    0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 
-    0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 
-    0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 
-    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
-    0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
-    0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 
-    0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 
-    0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 
-    0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 
-    0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 
-    0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 
-    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
-    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
-    0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 
-    0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 
-    0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 
-    0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 
-    0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 
-    0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 
-    0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
-    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 
-    0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 
-    0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 
-    0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 
-    0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 
-    0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 
-    0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 
-    0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 
-    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
-    0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 
-    0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 
-    0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 
-    0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 
-    0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 
-    0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 
-    0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 
-    0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
-    0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 
-    0x031F, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 
-    0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 
-    0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 
-    0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 
-    0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 
-    0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 
-    0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
-    0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x030C, 
-    0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 
-    0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 
-    0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 
-    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 
-    0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 
-    0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 
-    0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 
-    0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x02FA, 
-    0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 
-    0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 
-    0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 
-    0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 
-    0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 
-    0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 
-    0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
-    0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA, 
-    0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 
-    0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 
-    0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 
-    0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 
-    0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 
-    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 
-    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
-    0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8, 
-    0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 
-    0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 
-    0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 
-    0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 
-    0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 
-    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 
-    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
-    0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 
-    0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 
-    0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208, 
-    0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 
-    0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 
-    0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 
-    0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 
-    0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
-    0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 
-    0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 
-    0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 
-    0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 
-    0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 
-    0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 
-    0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 
-    0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
-    0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 
-    0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 
-    0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 
-    0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 
-    0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 
-    0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 
-    0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 
-    0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
-    0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 
-    0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 
-    0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 
-    0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 
-    0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 
-    0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 
-    0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 
-    0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 
-    0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 
-    0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 
-    0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 
-    0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 
-    0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 
-    0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 
-    0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 
-    0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 
-    0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 
-    0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 
-    0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 
-    0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 
-    0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 
-    0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 
-    0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 
-    0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 
-    0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F, 
-    0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E, 
-    0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 
-    0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 
-    0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 
-    0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 
-    0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 
-    0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
-    0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282, 
-    0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 
-    0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 
-    0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 
-    0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 
-    0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 
-    0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 
-    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
-    0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276, 
-    0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 
-    0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 
-    0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 
-    0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 
-    0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 
-    0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 
-    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
-    0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 
-    0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 
-    0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 
-    0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 
-    0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 
-    0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 
-    0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 
-    0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 
-    0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 
-    0x025E, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 
-    0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 
-    0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 
-    0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 
-    0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 
-    0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 
-    0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 
-    0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253, 
-    0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 
-    0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 
-    0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 
-    0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 
-    0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 
-    0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 
-    0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 
-    0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 
-    0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 
-    0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 
-    0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 
-    0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 
-    0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 
-    0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 
-    0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 
-    0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 
-    0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 
-    0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 
-    0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 
-    0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 
-    0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 
-    0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 
-    0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 
-    0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 
-    0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 
-    0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 
-    0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 
-    0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 
-    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 
-    0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 
-    0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 
-    0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B, 
-    0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 
-    0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 
-    0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 
-    0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 
-    0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 
-    0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 
-    0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 
-    0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222, 
-    0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 
-    0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 
-    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 
-    0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 
-    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 
-    0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 
-    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 
-    0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219, 
-    0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 
-    0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 
-    0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 
-    0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 
-    0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 
-    0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 
-    0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 
-    0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210, 
-    0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 
-    0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 
-    0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 
-    0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 
-    0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 
-    0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 
-    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 
-    0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 
-    0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 
-    0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 
-    0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 
-    0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 
-    0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 
-    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 
-    0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 
-    0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 
-    0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 
-    0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 
-    0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 
-    0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 
-    0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 
-    0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 
-    0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 
-    0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 
-    0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 
-    0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 
-    0x019E, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 
-    0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 
-    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 
-    0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 
-    0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 
-    0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 
-    0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 
-    0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 
-    0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 
-    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 
-    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 
-    0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 
-    0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 
-    0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 
-    0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 
-    0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 
-    0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 
-    0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 
-    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 
-    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 
-    0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 
-    0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 
-    0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 
-    0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 
-    0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 
-    0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 
-    0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 
-    0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 
-    0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 
-    0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 
-    0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 
-    0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 
-    0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 
-    0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 
-    0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 
-    0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 
-    0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 
-    0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 
-    0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 
-    0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 
-    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 
-    0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 
-    0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 
-    0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 
-    0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 
-    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 
-    0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 
-    0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 
-    0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 
-    0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 
-    0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 
-    0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 
-    0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 
-    0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 
-    0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 
-    0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 
-    0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 
-    0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 
-    0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 
-    0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 
-    0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 
-    0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 
-    0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 
-    0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 
-    0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 
-    0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 
-    0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 
-    0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 
-    0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 
-    0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 
-    0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 
-    0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 
-    0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 
-    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 
-    0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 
-    0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 
-    0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 
-    0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 
-    0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 
-    0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 
-    0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 
-    0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
-    0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 
-    0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 
-    0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 
-    0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 
-    0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 
-    0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 
-    0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 
-    0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 
-    0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 
-    0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 
-    0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 
-    0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 
-    0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 
-    0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 
-    0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 
-    0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 
-    0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 
-    0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 
-    0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 
-    0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 
-    0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 
-    0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 
-    0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 
-    0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 
-    0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 
-    0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 
-    0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 
-    0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 
-    0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 
-    0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 
-    0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 
-    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 
-    0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 
-    0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 
-    0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 
-    0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 
-    0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 
-    0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 
-    0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 
-    0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 
-    0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 
-    0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 
-    0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 
-    0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 
-    0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 
-    0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 
-    0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 
-    0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
-    0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 
-    0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 
-    0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 
-    0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 
-    0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 
-    0x0141, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 
-    0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 
-    0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 
-    0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 
-    0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 
-    0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 
-    0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 
-    0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 
-    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 
-    0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 
-    0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 
-    0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 
-    0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 
-    0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 
-    0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 
-    0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 
-    0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 
-    0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 
-    0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 
-    0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 
-    0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 
-    0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 
-    0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 
-    0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 
-    0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 
-    0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 
-    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 
-    0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 
-    0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 
-    0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 
-    0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 
-    0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x013B, 
-    0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 
-    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 
-    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 
-    0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 
-    0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 
-    0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C, 
-    0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 
-    0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 
-    0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 
-    0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 
-    0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 
-    0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 
-    0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 
-    0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 
-    0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 
-    0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 
-    0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 
-    0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 
-    0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 
-    0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 
-    0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 
-    0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 
-    0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 
-    0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 
-    0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 
-    0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 
-    0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 
-    0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 
-    0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 
-    0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 
-    0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 
-    0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 
-    0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 
-    0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 
-    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 
-    0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 
-    0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 
-    0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 
-    0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 
-    0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 
-    0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 
-    0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 
-    0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 
-    0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 
-    0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 
-    0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 
-    0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 
-    0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 
-    0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 
-    0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 
-    0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 
-    0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 
-    0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 
-    0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 
-    0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 
-    0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 
-    0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 
-    0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 
-    0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 
-    0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 
-    0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 
-    0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 
-    0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 
-    0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 
-    0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 
-    0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 
-    0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 
-    0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 
-    0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 
-    0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 
-    0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 
-    0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 
-    0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 
-    0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
-    0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 
-    0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 
-    0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 
-    0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 
-    0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 
-    0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 
-    0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 
-    0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 
-    0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 
-    0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 
-    0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 
-    0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 
-    0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 
-    0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 
-    0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 
-    0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 
-    0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 
-    0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 
-    0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 
-    0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 
-    0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 
-    0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 
-    0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 
-    0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 
-    0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 
-    0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 
-    0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 
-    0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 
-    0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 
-    0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 
-    0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 
-    0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 
-    0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 
-    0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 
-    0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 
-    0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 
-    0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 
-    0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 
-    0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 
-    0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 
-    0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 
-    0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 
-    0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 
-    0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 
-    0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 
-    0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 
-    0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 
-    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 
-    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 
-    0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 
-    0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 
-    0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 
-    0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 
-    0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 
-    0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 
-    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 
-    0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 
-    0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 
-    0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 
-    0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 
-    0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 
-    0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 
-    0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 
-    0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 
-    0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 
-    0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 
-    0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 
-    0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 
-    0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 
-    0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 
-    0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 
-    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 
-    0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 
-    0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 
-    0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 
-    0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 
-    0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 
-    0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 
-    0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 
-    0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 
-    0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 
-    0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 
-    0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 
-    0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 
-    0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 
-    0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 
-    0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 
-    0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 
-    0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 
-    0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 
-    0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 
-    0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 
-    0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 
-    0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 
-    0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 
-    0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 
-    0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 
-    0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 
-    0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 
-    0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 
-    0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 
-    0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 
-    0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 
-    0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 
-    0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 
-    0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 
-    0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 
-    0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 
-    0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 
-    0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 
-    0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
-    0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 
-    0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 
-    0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 
-    0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 
-    0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 
-    0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 
-    0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 
-    0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 
-    0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 
-    0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 
-    0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5, 
-    0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 
-    0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 
-    0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 
-    0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 
-    0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 
-    0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 
-    0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 
-    0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 
-    0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 
-    0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 
-    0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 
-    0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 
-    0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 
-    0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 
-    0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 
-    0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 
-    0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 
-    0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 
-    0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 
-    0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 
-    0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 
-    0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 
-    0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 
-    0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3, 
-    0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 
-    0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 
-    0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 
-    0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 
-    0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 
-    0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 
-    0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 
-    0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 
-    0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 
-    0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 
-    0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 
-    0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 
-    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 
-    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 
-    0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 
-    0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 
-    0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 
-    0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 
-    0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 
-    0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 
-    0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 
-    0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 
-    0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 
-    0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0, 
-    0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 
-    0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 
-    0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 
-    0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 
-    0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 
-    0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 
-    0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 
-    0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0, 0x00C0, 
-    0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 
-    0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 
-    0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 
-    0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 
-    0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 
-    0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 
-    0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 
-    0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 
-    0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 
-    0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 
-    0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 
-    0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 
-    0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 
-    0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 
-    0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 
-    0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE, 
-    0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 
-    0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 
-    0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 
-    0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 
-    0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 
-    0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 
-    0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 
-    0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 
-    0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 
-    0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 
-    0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 
-    0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 
-    0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 
-    0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 
-    0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 
-    0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 
-    0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 
-    0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 
-    0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 
-    0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 
-    0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 
-    0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 
-    0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 
-    0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 
-    0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 
-    0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 
-    0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 
-    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 
-    0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 
-    0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 
-    0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 
-    0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 
-    0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 
-    0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 
-    0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 
-    0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 
-    0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 
-    0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 
-    0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 
-    0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 
-    0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 
-    0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 
-    0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 
-    0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 
-    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 
-    0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 
-    0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 
-    0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 
-    0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 
-    0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 
-    0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 
-    0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 
-    0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 
-    0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 
-    0x00D2, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 
-    0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 
-    0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 
-    0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102, 
-    0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 
-    0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 
-    0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 
-    0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 
-    0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 
-    0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 
-    0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 
-    0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100, 
-    0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 
-    0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 
-    0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 
-    0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 
-    0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 
-    0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 
-    0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 
-    0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 
-    0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 
-    0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 
-    0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 
-    0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00D0, 
-    0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 
-    0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 
-    0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 
-    0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 
-    0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 
-    0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 
-    0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 
-    0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 
-    0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 
-    0x00C1, 0x00C0, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 
-    0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 
-    0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 
-    0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 
-    0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 
-    0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 
-    0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 
-    0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 0x00C1, 
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618,
+    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3,
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C,
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618,
+    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3,
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C,
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1,
+    0x05D1, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3,
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C,
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1,
+    0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8,
+    0x03A8, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C,
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0800, 0x0800, 0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0618, 0x0618, 0x05D1,
+    0x05D1, 0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8,
+    0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C,
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0800, 0x0800, 0x0800, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1,
+    0x0590, 0x0590, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8,
+    0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C,
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x05D1, 0x05D1,
+    0x0590, 0x0555, 0x0555, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8,
+    0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F,
+    0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0787, 0x0787, 0x0787, 0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590,
+    0x0590, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8,
+    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F,
+    0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0787, 0x0787, 0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590,
+    0x0555, 0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8,
+    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F,
+    0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194,
+    0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x071C, 0x071C, 0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x0590, 0x0590,
+    0x0555, 0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E,
+    0x038E, 0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F,
+    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x071C, 0x071C, 0x071C, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0555,
+    0x0555, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E,
+    0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F,
+    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x06BC, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555,
+    0x051E, 0x051E, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E,
+    0x0375, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F,
+    0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x051E,
+    0x051E, 0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E,
+    0x0375, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282,
+    0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0,
+    0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0666, 0x0666, 0x0666, 0x0666, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E,
+    0x04EC, 0x04EC, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x0375,
+    0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282,
+    0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0,
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x04EC,
+    0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375,
+    0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282,
+    0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0,
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x0618, 0x0618, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC,
+    0x04BD, 0x04BD, 0x0492, 0x0469, 0x0469, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x0375,
+    0x035E, 0x0348, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282,
+    0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0,
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x05D1, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD,
+    0x04BD, 0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E,
+    0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276,
+    0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0,
+    0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F,
+    0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0590, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04BD, 0x04BD,
+    0x0492, 0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0400, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E,
+    0x0348, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276,
+    0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9,
+    0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F,
+    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492,
+    0x0492, 0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348,
+    0x0333, 0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276,
+    0x026A, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9,
+    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F,
+    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x0555, 0x0555, 0x0555, 0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469,
+    0x0469, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348,
+    0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A,
+    0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9,
+    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F,
+    0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E,
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x051E, 0x051E, 0x051E, 0x051E, 0x051E, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469,
+    0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0333,
+    0x0333, 0x031F, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A,
+    0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1,
+    0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A,
+    0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E,
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04EC, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444,
+    0x0444, 0x0421, 0x0421, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333,
+    0x031F, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A,
+    0x025E, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1,
+    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A,
+    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E,
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F,
+    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x04BD, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0444, 0x0444, 0x0421,
+    0x0421, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F,
+    0x030C, 0x030C, 0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E,
+    0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1,
+    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A,
+    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E,
+    0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F,
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421,
+    0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x031F, 0x031F,
+    0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E,
+    0x0253, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1,
+    0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A,
+    0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A,
+    0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F,
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC,
+    0x0492, 0x0492, 0x0492, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0400, 0x0400,
+    0x0400, 0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C,
+    0x02FA, 0x02FA, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253,
+    0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA,
+    0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186,
+    0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A,
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F,
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC,
+    0x0469, 0x0469, 0x0469, 0x0469, 0x0469, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0,
+    0x03E0, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C,
+    0x02FA, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253,
+    0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA,
+    0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186,
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A,
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F,
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC,
+    0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0444, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03C3,
+    0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA,
+    0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249,
+    0x0249, 0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4,
+    0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186,
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A,
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C,
+    0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC,
+    0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0421, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3,
+    0x03A8, 0x03A8, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8,
+    0x02E8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249,
+    0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4,
+    0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181,
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147,
+    0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C,
+    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC,
+    0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x0400, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8,
+    0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8,
+    0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0249, 0x023E,
+    0x023E, 0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4,
+    0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181,
+    0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147,
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C,
+    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA,
+    0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03E0, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E,
+    0x038E, 0x0375, 0x0375, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8,
+    0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E,
+    0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD,
+    0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181,
+    0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147,
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C,
+    0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA,
+    0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x0375,
+    0x0375, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8,
+    0x02C8, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x0234,
+    0x0234, 0x022B, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD,
+    0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D,
+    0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147,
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A,
+    0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA,
+    0x03C3, 0x03C3, 0x03C3, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375,
+    0x035E, 0x035E, 0x0348, 0x0348, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8,
+    0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x026A, 0x026A, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234,
+    0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7,
+    0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D,
+    0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144,
+    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A,
+    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA,
+    0x03A8, 0x03A8, 0x03A8, 0x03A8, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E,
+    0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9,
+    0x02AA, 0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x022B,
+    0x022B, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7,
+    0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D,
+    0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144,
+    0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A,
+    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA,
+    0x038E, 0x038E, 0x038E, 0x038E, 0x038E, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348,
+    0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA,
+    0x02AA, 0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x0253, 0x0253, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B,
+    0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7,
+    0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178,
+    0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144,
+    0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A,
+    0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8,
+    0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x0375, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333,
+    0x0333, 0x031F, 0x031F, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02AA, 0x02AA,
+    0x029C, 0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x0222,
+    0x0222, 0x0219, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0,
+    0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178,
+    0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141,
+    0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118,
+    0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8,
+    0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x035E, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F,
+    0x031F, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C,
+    0x028F, 0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222,
+    0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0,
+    0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178,
+    0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141,
+    0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118,
+    0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8,
+    0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0348, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x030C,
+    0x030C, 0x030C, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x028F,
+    0x028F, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219,
+    0x0210, 0x0210, 0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA,
+    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174,
+    0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141,
+    0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118,
+    0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8,
+    0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x0333, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x02FA,
+    0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F,
+    0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219,
+    0x0210, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA,
+    0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174,
+    0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E,
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115,
+    0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6,
+    0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x031F, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA,
+    0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282,
+    0x0276, 0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210,
+    0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01B4,
+    0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170,
+    0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E,
+    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115,
+    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6,
+    0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x030C, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8,
+    0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276,
+    0x0276, 0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208,
+    0x0208, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4,
+    0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170,
+    0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E,
+    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115,
+    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6,
+    0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02FA, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8,
+    0x02D8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x026A,
+    0x026A, 0x025E, 0x025E, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0208, 0x0208,
+    0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF,
+    0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170,
+    0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B,
+    0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113,
+    0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4,
+    0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8,
+    0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A,
+    0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200,
+    0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF,
+    0x01A9, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C,
+    0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B,
+    0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113,
+    0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4,
+    0x02E8, 0x02E8, 0x02E8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9,
+    0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x025E, 0x025E,
+    0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x01F8,
+    0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9,
+    0x01A4, 0x019E, 0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C,
+    0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138,
+    0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113,
+    0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4,
+    0x02D8, 0x02D8, 0x02D8, 0x02D8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA,
+    0x02AA, 0x02AA, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253,
+    0x0253, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8,
+    0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9,
+    0x01A4, 0x019E, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168,
+    0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138,
+    0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111,
+    0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2,
+    0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02C8, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C,
+    0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249,
+    0x0249, 0x023E, 0x023E, 0x0234, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0,
+    0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4,
+    0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168,
+    0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138,
+    0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111,
+    0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2,
+    0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02B9, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F,
+    0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249,
+    0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01E9,
+    0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x019E,
+    0x019E, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164,
+    0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135,
+    0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111,
+    0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2,
+    0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x02AA, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F,
+    0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E,
+    0x0234, 0x0234, 0x022B, 0x022B, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9,
+    0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E,
+    0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164,
+    0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135,
+    0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E,
+    0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0,
+    0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x029C, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282,
+    0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234,
+    0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1,
+    0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x0199,
+    0x0194, 0x0194, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160,
+    0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132,
+    0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E,
+    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0,
+    0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x028F, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276,
+    0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B,
+    0x022B, 0x0222, 0x0222, 0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA,
+    0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x0199, 0x0199,
+    0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160,
+    0x015C, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132,
+    0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E,
+    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0,
+    0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0282, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A,
+    0x026A, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x0222,
+    0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA,
+    0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194,
+    0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C,
+    0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F,
+    0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C,
+    0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF,
+    0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x0276, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E,
+    0x025E, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222,
+    0x0219, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4,
+    0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x018F,
+    0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C,
+    0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F,
+    0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C,
+    0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF,
+    0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x026A, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253,
+    0x0253, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219,
+    0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD,
+    0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F,
+    0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158,
+    0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C,
+    0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A,
+    0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF,
+    0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x025E, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249,
+    0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210,
+    0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD,
+    0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A,
+    0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158,
+    0x0155, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C,
+    0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A,
+    0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED,
+    0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0253, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E,
+    0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208,
+    0x0208, 0x0200, 0x0200, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7,
+    0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A,
+    0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155,
+    0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C,
+    0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108,
+    0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED,
+    0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x0249, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234,
+    0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200,
+    0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0,
+    0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186,
+    0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151,
+    0x0151, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129,
+    0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108,
+    0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED,
+    0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x023E, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B,
+    0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8,
+    0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA,
+    0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181,
+    0x0181, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151,
+    0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129,
+    0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108,
+    0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB,
+    0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x0234, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222,
+    0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8,
+    0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01BA, 0x01BA,
+    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181,
+    0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x014E,
+    0x014E, 0x014A, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127,
+    0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106,
+    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB,
+    0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x022B, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219,
+    0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0,
+    0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4,
+    0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D,
+    0x0178, 0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E,
+    0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127,
+    0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106,
+    0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA,
+    0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0222, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210,
+    0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01E9, 0x01E9,
+    0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01B4, 0x01B4, 0x01AF,
+    0x01AF, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178,
+    0x0178, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A,
+    0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124,
+    0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104,
+    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA,
+    0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0219, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210,
+    0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1,
+    0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01A9,
+    0x01A9, 0x01A4, 0x01A4, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178,
+    0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A,
+    0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124,
+    0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104,
+    0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA,
+    0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0210, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208,
+    0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA,
+    0x01DA, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9,
+    0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174,
+    0x0170, 0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147,
+    0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121,
+    0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102,
+    0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8,
+    0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0208, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200,
+    0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4,
+    0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A4, 0x01A4,
+    0x019E, 0x019E, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170,
+    0x0170, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0144,
+    0x0144, 0x0141, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102,
+    0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8,
+    0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x0200, 0x01F8, 0x01F8, 0x01F8, 0x01F8,
+    0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01CD,
+    0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E,
+    0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170,
+    0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144,
+    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F,
+    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100,
+    0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6,
+    0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0,
+    0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7,
+    0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199,
+    0x0199, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C,
+    0x0168, 0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141,
+    0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C,
+    0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100,
+    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6,
+    0x01F8, 0x01F8, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9,
+    0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7,
+    0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0194,
+    0x0194, 0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168,
+    0x0168, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141,
+    0x013E, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C,
+    0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5,
+    0x01F0, 0x01F0, 0x01F0, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1,
+    0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0,
+    0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194,
+    0x018F, 0x018F, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168,
+    0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E,
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A,
+    0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE,
+    0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5,
+    0x01E9, 0x01E9, 0x01E9, 0x01E9, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA,
+    0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA,
+    0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x018F, 0x018F,
+    0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164,
+    0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B,
+    0x013B, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A,
+    0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC,
+    0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3,
+    0x01E1, 0x01E1, 0x01E1, 0x01E1, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4,
+    0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01B4, 0x01B4,
+    0x01B4, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A,
+    0x018A, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160,
+    0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013B, 0x013B,
+    0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118,
+    0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC,
+    0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3,
+    0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01DA, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD,
+    0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01AF, 0x01AF,
+    0x01AF, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186,
+    0x0186, 0x0181, 0x0181, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C,
+    0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138,
+    0x0135, 0x0135, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118,
+    0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA,
+    0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3,
+    0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01D4, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7,
+    0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9,
+    0x01A9, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0181,
+    0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C,
+    0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135,
+    0x0135, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115,
+    0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA,
+    0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1,
+    0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01CD, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0,
+    0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4,
+    0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181,
+    0x017D, 0x017D, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158,
+    0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0135, 0x0135,
+    0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113,
+    0x0113, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8,
+    0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1,
+    0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C7, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA,
+    0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x019E,
+    0x019E, 0x019E, 0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D,
+    0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155,
+    0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132,
+    0x012F, 0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113,
+    0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8,
+    0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0,
+    0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01C0, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4,
+    0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x0199,
+    0x0199, 0x0199, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178,
+    0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151,
+    0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F,
+    0x012F, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111,
+    0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6,
+    0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0,
+    0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01BA, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF,
+    0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199,
+    0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174,
+    0x0174, 0x0170, 0x0170, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151,
+    0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x012F, 0x012F,
+    0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111,
+    0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6,
+    0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE,
+    0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01B4, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9,
+    0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194,
+    0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170,
+    0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E,
+    0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C,
+    0x0129, 0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E,
+    0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4,
+    0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE,
+    0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01AF, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4,
+    0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F,
+    0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170,
+    0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A,
+    0x014A, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129,
+    0x0129, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C,
+    0x010C, 0x010A, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4,
+    0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD,
+    0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A9, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E,
+    0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A,
+    0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C,
+    0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147,
+    0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129,
+    0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C,
+    0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2,
+    0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD,
+    0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x01A4, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199,
+    0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186,
+    0x0186, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168,
+    0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147,
+    0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127,
+    0x0124, 0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A,
+    0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0,
+    0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB,
+    0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x019E, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194,
+    0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181,
+    0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164,
+    0x0164, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144,
+    0x0141, 0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124,
+    0x0124, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A,
+    0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0,
+    0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB,
+    0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0199, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194,
+    0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D,
+    0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160,
+    0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0144, 0x0144, 0x0141,
+    0x0141, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124,
+    0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108,
+    0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00EF,
+    0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA,
+    0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x0194, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F,
+    0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178,
+    0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C,
+    0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E,
+    0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121,
+    0x011F, 0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106,
+    0x0106, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF,
+    0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA,
+    0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018F, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A,
+    0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174,
+    0x0174, 0x0174, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158,
+    0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E,
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F,
+    0x011F, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106,
+    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED,
+    0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9,
+    0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x018A, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186,
+    0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170,
+    0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158,
+    0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013B, 0x013B,
+    0x0138, 0x0138, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F,
+    0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104,
+    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED,
+    0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7,
+    0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0186, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181,
+    0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x016C,
+    0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155,
+    0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138,
+    0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C,
+    0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102,
+    0x0102, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB,
+    0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7,
+    0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x0181, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D,
+    0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C,
+    0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151,
+    0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135,
+    0x0135, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A,
+    0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102,
+    0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EA,
+    0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6,
+    0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x017D, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178,
+    0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168,
+    0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E,
+    0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0132,
+    0x0132, 0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118,
+    0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100,
+    0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA,
+    0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6,
+    0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0178, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174,
+    0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164,
+    0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A,
+    0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132,
+    0x012F, 0x012F, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118,
+    0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE,
+    0x00FE, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8,
+    0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4,
+    0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0174, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170,
+    0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160,
+    0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147,
+    0x0147, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x012F, 0x012F,
+    0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115,
+    0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE,
+    0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8,
+    0x00E6, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4,
+    0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x0170, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C,
+    0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C,
+    0x015C, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144,
+    0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C,
+    0x012C, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113,
+    0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC,
+    0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6,
+    0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3,
+    0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x016C, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168,
+    0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158,
+    0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144,
+    0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012C, 0x012C, 0x0129,
+    0x0129, 0x0127, 0x0127, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111,
+    0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA,
+    0x00FA, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5,
+    0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3,
+    0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0168, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164,
+    0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155,
+    0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141,
+    0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0127,
+    0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111,
+    0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA,
+    0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5,
+    0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2,
+    0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0164, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160,
+    0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151,
+    0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E,
+    0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127,
+    0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E,
+    0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8,
+    0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3,
+    0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0,
+    0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x0160, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C,
+    0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E,
+    0x014E, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B,
+    0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0124, 0x0124,
+    0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C,
+    0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6,
+    0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E1,
+    0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0,
+    0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x015C, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158,
+    0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A,
+    0x014A, 0x014A, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138,
+    0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121,
+    0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A,
+    0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6,
+    0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1,
+    0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF,
+    0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0158, 0x0155, 0x0155, 0x0155, 0x0155,
+    0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147,
+    0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135,
+    0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F,
+    0x011F, 0x011C, 0x011C, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A,
+    0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4,
+    0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0,
+    0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF,
+    0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0155, 0x0151, 0x0151, 0x0151, 0x0151,
+    0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144,
+    0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132,
+    0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x011F, 0x011F, 0x011C,
+    0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108,
+    0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2,
+    0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0,
+    0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE,
+    0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x0151, 0x014E, 0x014E, 0x014E, 0x014E,
+    0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141,
+    0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F,
+    0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C,
+    0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106,
+    0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0,
+    0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE,
+    0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CC,
+    0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014E, 0x014A, 0x014A, 0x014A, 0x014A,
+    0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x013E,
+    0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F,
+    0x012C, 0x012C, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A,
+    0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104,
+    0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F0, 0x00F0,
+    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD,
+    0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC,
+    0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x014A, 0x0147, 0x0147, 0x0147, 0x0147,
+    0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013B,
+    0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C,
+    0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118,
+    0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104,
+    0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF,
+    0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD,
+    0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB,
+    0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0147, 0x0144, 0x0144, 0x0144, 0x0144,
+    0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B,
+    0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129,
+    0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115,
+    0x0115, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102,
+    0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED,
+    0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB,
+    0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB,
+    0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0144, 0x0141, 0x0141, 0x0141,
+    0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138,
+    0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127,
+    0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113,
+    0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100,
+    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED,
+    0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA,
+    0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA,
+    0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x0141, 0x013E, 0x013E, 0x013E,
+    0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135,
+    0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124,
+    0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111,
+    0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE,
+    0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB,
+    0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA,
+    0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00C9,
+    0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013E, 0x013B, 0x013B, 0x013B,
+    0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132,
+    0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0121, 0x0121,
+    0x0121, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111,
+    0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC,
+    0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA,
+    0x00EA, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9,
+    0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9,
+    0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x013B, 0x0138, 0x0138, 0x0138,
+    0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F,
+    0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F,
+    0x011F, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E,
+    0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC,
+    0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8,
+    0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7,
+    0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7,
+    0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0138, 0x0135, 0x0135, 0x0135,
+    0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C,
+    0x012C, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C,
+    0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C,
+    0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA,
+    0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8,
+    0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7,
+    0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7,
+    0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0135, 0x0132, 0x0132, 0x0132,
+    0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129,
+    0x0129, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011A,
+    0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A,
+    0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8,
+    0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6,
+    0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6,
+    0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6,
+    0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x0132, 0x012F, 0x012F,
+    0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127,
+    0x0127, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x0118,
+    0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108,
+    0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6,
+    0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5,
+    0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4,
+    0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5,
+    0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012F, 0x012C, 0x012C,
+    0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124,
+    0x0124, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118,
+    0x0115, 0x0115, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106,
+    0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6,
+    0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5,
+    0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3,
+    0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C5, 0x00C5,
+    0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x012C, 0x0129, 0x0129,
+    0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121,
+    0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115,
+    0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104,
+    0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4,
+    0x00F2, 0x00F2, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3,
+    0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3,
+    0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4,
+    0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0129, 0x0127, 0x0127,
+    0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F,
+    0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113,
+    0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104,
+    0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2,
+    0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1,
+    0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2,
+    0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3,
+    0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0127, 0x0124, 0x0124,
+    0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C,
+    0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111,
+    0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102,
+    0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0,
+    0x00F0, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0,
+    0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D0,
+    0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C3, 0x00C3,
+    0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0124, 0x0121, 0x0121,
+    0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A,
+    0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E,
+    0x010E, 0x010C, 0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100,
+    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF,
+    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0,
+    0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0,
+    0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1,
+    0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x0121, 0x011F, 0x011F,
+    0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118,
+    0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C,
+    0x010C, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE,
+    0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF,
+    0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE,
+    0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF,
+    0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0,
+    0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011F, 0x011C,
+    0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115,
+    0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010A, 0x010A,
+    0x010A, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC,
+    0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED,
+    0x00EB, 0x00EB, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD,
+    0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE,
+    0x00CE, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C0, 0x00C0,
+    0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011C, 0x011A,
+    0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113,
+    0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108,
+    0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA,
+    0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB,
+    0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB,
+    0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE,
+    0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF,
+    0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x011A, 0x0118,
+    0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111,
+    0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106,
+    0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8,
+    0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA,
+    0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA,
+    0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC,
+    0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE,
+    0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0118, 0x0115,
+    0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E,
+    0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104,
+    0x0104, 0x0104, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8,
+    0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8,
+    0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA,
+    0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB,
+    0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BE, 0x00BE,
+    0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0115, 0x0113,
+    0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C,
+    0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102,
+    0x0102, 0x0102, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6,
+    0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E6,
+    0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9,
+    0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA,
+    0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD,
+    0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0113, 0x0111,
+    0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A,
+    0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0100,
+    0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4,
+    0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6,
+    0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7,
+    0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA,
+    0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC,
+    0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x0111, 0x010E,
+    0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108,
+    0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100,
+    0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2,
+    0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5,
+    0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6,
+    0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9,
+    0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC,
+    0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E, 0x010E,
+    0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108,
+    0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE,
+    0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0,
+    0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E3, 0x00E3,
+    0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6,
+    0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7,
+    0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB,
+    0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C, 0x010C,
+    0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106,
+    0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC,
+    0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF,
+    0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1,
+    0x00E1, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4,
+    0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7,
+    0x00C6, 0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA,
+    0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A, 0x010A,
+    0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104,
+    0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA,
+    0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED,
+    0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0,
+    0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3,
+    0x00D2, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6,
+    0x00C5, 0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA,
+    0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108, 0x0108,
+    0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102,
+    0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8,
+    0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB,
+    0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00DE,
+    0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2,
+    0x00D2, 0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5,
+    0x00C4, 0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9,
+    0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106, 0x0106,
+    0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100,
+    0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6,
+    0x00F6, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA,
+    0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE,
+    0x00DD, 0x00DD, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0,
+    0x00D0, 0x00CF, 0x00CF, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4,
+    0x00C4, 0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BD, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8,
+    0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104, 0x0104,
+    0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE,
+    0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4,
+    0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA,
+    0x00E8, 0x00E8, 0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD,
+    0x00DB, 0x00DB, 0x00DA, 0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00D0,
+    0x00CF, 0x00CE, 0x00CE, 0x00CC, 0x00CC, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C6, 0x00C5, 0x00C4, 0x00C4,
+    0x00C3, 0x00C1, 0x00C1, 0x00C0, 0x00BF, 0x00BF, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8,
+    0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102, 0x0102,
+    0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC,
+    0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2,
+    0x00F2, 0x00F0, 0x00F0, 0x00F0, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8,
+    0x00E6, 0x00E6, 0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00E0, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB,
+    0x00DA, 0x00DA, 0x00D9, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D2, 0x00D0, 0x00CF, 0x00CF,
+    0x00CE, 0x00CE, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00CA, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3,
+    0x00C1, 0x00C0, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 0x00B7,
+    0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100, 0x0100,
+    0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FE, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FC, 0x00FA, 0x00FA, 0x00FA, 0x00FA,
+    0x00FA, 0x00F8, 0x00F8, 0x00F8, 0x00F8, 0x00F6, 0x00F6, 0x00F6, 0x00F4, 0x00F4, 0x00F4, 0x00F2, 0x00F2, 0x00F2, 0x00F0, 0x00F0,
+    0x00F0, 0x00EF, 0x00EF, 0x00EF, 0x00ED, 0x00ED, 0x00ED, 0x00EB, 0x00EB, 0x00EA, 0x00EA, 0x00EA, 0x00E8, 0x00E8, 0x00E6, 0x00E6,
+    0x00E5, 0x00E5, 0x00E3, 0x00E3, 0x00E3, 0x00E1, 0x00E1, 0x00E0, 0x00E0, 0x00DE, 0x00DE, 0x00DD, 0x00DD, 0x00DB, 0x00DB, 0x00DA,
+    0x00DA, 0x00D9, 0x00D7, 0x00D7, 0x00D6, 0x00D6, 0x00D4, 0x00D4, 0x00D3, 0x00D3, 0x00D2, 0x00D0, 0x00D0, 0x00CF, 0x00CF, 0x00CE,
+    0x00CC, 0x00CC, 0x00CB, 0x00CB, 0x00CA, 0x00C9, 0x00C9, 0x00C7, 0x00C7, 0x00C6, 0x00C5, 0x00C5, 0x00C4, 0x00C3, 0x00C3, 0x00C1,
     0x00C0, 0x00C0, 0x00BF, 0x00BE, 0x00BE, 0x00BD, 0x00BC, 0x00BC, 0x00BB, 0x00BA, 0x00BA, 0x00B9, 0x00B8, 0x00B8, 0x00B7, 0x00B6
 };
 #define kDeltaUsedToBuildTable  32
diff --git a/src/effects/SkGradientShader.cpp b/src/effects/SkGradientShader.cpp
deleted file mode 100644
index 24ed445..0000000
--- a/src/effects/SkGradientShader.cpp
+++ /dev/null
@@ -1,2568 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkGradientShader.h"
-#include "SkClampRange.h"
-#include "SkColorPriv.h"
-#include "SkMallocPixelRef.h"
-#include "SkUnitMapper.h"
-#include "SkUtils.h"
-#include "SkTemplates.h"
-#include "SkBitmapCache.h"
-
-#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
-    #define USE_DITHER_32BIT_GRADIENT
-#endif
-
-static void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
-                               int count) {
-    if (count > 0) {
-        if (v0 == v1) {
-            sk_memset32(dst, v0, count);
-        } else {
-            int pairs = count >> 1;
-            for (int i = 0; i < pairs; i++) {
-                *dst++ = v0;
-                *dst++ = v1;
-            }
-            if (count & 1) {
-                *dst = v0;
-            }
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Can't use a two-argument function with side effects like this in a
-// constructor's initializer's argument list because the order of
-// evaluations in that context is undefined (and backwards on linux/gcc).
-static SkPoint unflatten_point(SkReader32& buffer) {
-    SkPoint retval;
-    retval.fX = buffer.readScalar();
-    retval.fY = buffer.readScalar();
-    return retval;
-}
-
-//  Clamp
-
-static SkFixed clamp_tileproc(SkFixed x) {
-    return SkClampMax(x, 0xFFFF);
-}
-
-// Repeat
-
-static SkFixed repeat_tileproc(SkFixed x) {
-    return x & 0xFFFF;
-}
-
-static inline int repeat_bits(int x, const int bits) {
-    return x & ((1 << bits) - 1);
-}
-
-static inline int repeat_8bits(int x) {
-    return x & 0xFF;
-}
-
-// Mirror
-
-// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
-// See http://code.google.com/p/skia/issues/detail?id=472
-#if defined(_MSC_VER) && (_MSC_VER >= 1600)
-#pragma optimize("", off)
-#endif
-
-static inline SkFixed mirror_tileproc(SkFixed x) {
-    int s = x << 15 >> 31;
-    return (x ^ s) & 0xFFFF;
-}
-
-static inline int mirror_bits(int x, const int bits) {
-#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
-    if (x & (1 << bits))
-        x = ~x;
-    return x & ((1 << bits) - 1);
-#else
-    int s = x << (31 - bits) >> 31;
-    return (x ^ s) & ((1 << bits) - 1);
-#endif
-}
-
-static inline int mirror_8bits(int x) {
-#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
-    if (x & 256) {
-        x = ~x;
-    }
-    return x & 255;
-#else
-    int s = x << 23 >> 31;
-    return (x ^ s) & 0xFF;
-#endif
-}
-
-#if defined(_MSC_VER) && (_MSC_VER >= 1600)
-#pragma optimize("", on)
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-typedef SkFixed (*TileProc)(SkFixed);
-
-static const TileProc gTileProcs[] = {
-    clamp_tileproc,
-    repeat_tileproc,
-    mirror_tileproc
-};
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-class Gradient_Shader : public SkShader {
-public:
-    Gradient_Shader(const SkColor colors[], const SkScalar pos[],
-                int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
-    virtual ~Gradient_Shader();
-
-    // overrides
-    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
-    virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
-    virtual bool isOpaque() const SK_OVERRIDE;
-
-    enum {
-        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
-        /// it, use a larger cache.
-        kCache16Bits    = 8,
-        kGradient16Length = (1 << kCache16Bits),
-        /// Each cache gets 1 extra entry at the end so we don't have to
-        /// test for end-of-cache in lerps. This is also the value used
-        /// to stride *writes* into the dither cache; it must not be zero.
-        /// Total space for a cache is 2x kCache16Count entries: one
-        /// regular cache, one for dithering.
-        kCache16Count   = kGradient16Length + 1,
-        kCache16Shift   = 16 - kCache16Bits,
-        kSqrt16Shift    = 8 - kCache16Bits,
-
-        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
-        /// it, use a larger cache.
-        kCache32Bits    = 8,
-        kGradient32Length = (1 << kCache32Bits),
-        /// Each cache gets 1 extra entry at the end so we don't have to
-        /// test for end-of-cache in lerps. This is also the value used
-        /// to stride *writes* into the dither cache; it must not be zero.
-        /// Total space for a cache is 2x kCache32Count entries: one
-        /// regular cache, one for dithering.
-        kCache32Count   = kGradient32Length + 1,
-        kCache32Shift   = 16 - kCache32Bits,
-        kSqrt32Shift    = 8 - kCache32Bits,
-
-        /// This value is used to *read* the dither cache; it may be 0
-        /// if dithering is disabled.
-#ifdef USE_DITHER_32BIT_GRADIENT
-        kDitherStride32 = kCache32Count,
-#else
-        kDitherStride32 = 0,
-#endif
-        kDitherStride16 = kCache16Count,
-        kLerpRemainderMask32 = (1 << (16 - kCache32Bits)) - 1
-    };
-
-
-protected:
-    Gradient_Shader(SkFlattenableReadBuffer& );
-    SkUnitMapper* fMapper;
-    SkMatrix    fPtsToUnit;     // set by subclass
-    SkMatrix    fDstToIndex;
-    SkMatrix::MapXYProc fDstToIndexProc;
-    TileMode    fTileMode;
-    TileProc    fTileProc;
-    int         fColorCount;
-    uint8_t     fDstToIndexClass;
-    uint8_t     fFlags;
-
-    struct Rec {
-        SkFixed     fPos;   // 0...1
-        uint32_t    fScale; // (1 << 24) / range
-    };
-    Rec*        fRecs;
-
-    virtual void flatten(SkFlattenableWriteBuffer& );
-    const uint16_t*     getCache16() const;
-    const SkPMColor*    getCache32() const;
-
-    void commonAsABitmap(SkBitmap*) const;
-    void commonAsAGradient(GradientInfo*) const;
-
-private:
-    enum {
-        kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
-
-        kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
-    };
-    SkColor     fStorage[(kStorageSize + 3) >> 2];
-    SkColor*    fOrigColors; // original colors, before modulation by paint in setContext
-    bool        fColorsAreOpaque;
-
-    mutable uint16_t*   fCache16;   // working ptr. If this is NULL, we need to recompute the cache values
-    mutable SkPMColor*  fCache32;   // working ptr. If this is NULL, we need to recompute the cache values
-
-    mutable uint16_t*   fCache16Storage;    // storage for fCache16, allocated on demand
-    mutable SkMallocPixelRef* fCache32PixelRef;
-    mutable unsigned    fCacheAlpha;        // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
-
-    static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
-    static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
-                                U8CPU alpha);
-    void setCacheAlpha(U8CPU alpha) const;
-    void initCommon();
-
-    typedef SkShader INHERITED;
-};
-
-static inline unsigned scalarToU16(SkScalar x) {
-    SkASSERT(x >= 0 && x <= SK_Scalar1);
-
-#ifdef SK_SCALAR_IS_FLOAT
-    return (unsigned)(x * 0xFFFF);
-#else
-    return x - (x >> 16);   // probably should be x - (x > 0x7FFF) but that is slower
-#endif
-}
-
-Gradient_Shader::Gradient_Shader(const SkColor colors[], const SkScalar pos[],
-             int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
-    SkASSERT(colorCount > 1);
-
-    fCacheAlpha = 256;  // init to a value that paint.getAlpha() can't return
-
-    fMapper = mapper;
-    SkSafeRef(mapper);
-
-    SkASSERT((unsigned)mode < SkShader::kTileModeCount);
-    SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
-    fTileMode = mode;
-    fTileProc = gTileProcs[mode];
-
-    fCache16 = fCache16Storage = NULL;
-    fCache32 = NULL;
-    fCache32PixelRef = NULL;
-
-    /*  Note: we let the caller skip the first and/or last position.
-        i.e. pos[0] = 0.3, pos[1] = 0.7
-        In these cases, we insert dummy entries to ensure that the final data
-        will be bracketed by [0, 1].
-        i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
-
-        Thus colorCount (the caller's value, and fColorCount (our value) may
-        differ by up to 2. In the above example:
-            colorCount = 2
-            fColorCount = 4
-     */
-    fColorCount = colorCount;
-    // check if we need to add in dummy start and/or end position/colors
-    bool dummyFirst = false;
-    bool dummyLast = false;
-    if (pos) {
-        dummyFirst = pos[0] != 0;
-        dummyLast = pos[colorCount - 1] != SK_Scalar1;
-        fColorCount += dummyFirst + dummyLast;
-    }
-
-    if (fColorCount > kColorStorageCount) {
-        size_t size = sizeof(SkColor) + sizeof(Rec);
-        fOrigColors = reinterpret_cast<SkColor*>(
-                                        sk_malloc_throw(size * fColorCount));
-    }
-    else {
-        fOrigColors = fStorage;
-    }
-
-    // Now copy over the colors, adding the dummies as needed
-    {
-        SkColor* origColors = fOrigColors;
-        if (dummyFirst) {
-            *origColors++ = colors[0];
-        }
-        memcpy(origColors, colors, colorCount * sizeof(SkColor));
-        if (dummyLast) {
-            origColors += colorCount;
-            *origColors = colors[colorCount - 1];
-        }
-    }
-
-    fRecs = (Rec*)(fOrigColors + fColorCount);
-    if (fColorCount > 2) {
-        Rec* recs = fRecs;
-        recs->fPos = 0;
-        //  recs->fScale = 0; // unused;
-        recs += 1;
-        if (pos) {
-            /*  We need to convert the user's array of relative positions into
-                fixed-point positions and scale factors. We need these results
-                to be strictly monotonic (no two values equal or out of order).
-                Hence this complex loop that just jams a zero for the scale
-                value if it sees a segment out of order, and it assures that
-                we start at 0 and end at 1.0
-            */
-            SkFixed prev = 0;
-            int startIndex = dummyFirst ? 0 : 1;
-            int count = colorCount + dummyLast;
-            for (int i = startIndex; i < count; i++) {
-                // force the last value to be 1.0
-                SkFixed curr;
-                if (i == colorCount) {  // we're really at the dummyLast
-                    curr = SK_Fixed1;
-                } else {
-                    curr = SkScalarToFixed(pos[i]);
-                }
-                // pin curr withing range
-                if (curr < 0) {
-                    curr = 0;
-                } else if (curr > SK_Fixed1) {
-                    curr = SK_Fixed1;
-                }
-                recs->fPos = curr;
-                if (curr > prev) {
-                    recs->fScale = (1 << 24) / (curr - prev);
-                } else {
-                    recs->fScale = 0; // ignore this segment
-                }
-                // get ready for the next value
-                prev = curr;
-                recs += 1;
-            }
-        } else {    // assume even distribution
-            SkFixed dp = SK_Fixed1 / (colorCount - 1);
-            SkFixed p = dp;
-            SkFixed scale = (colorCount - 1) << 8;  // (1 << 24) / dp
-            for (int i = 1; i < colorCount; i++) {
-                recs->fPos   = p;
-                recs->fScale = scale;
-                recs += 1;
-                p += dp;
-            }
-        }
-    }
-    this->initCommon();
-}
-
-Gradient_Shader::Gradient_Shader(SkFlattenableReadBuffer& buffer) :
-    INHERITED(buffer) {
-    fCacheAlpha = 256;
-
-    fMapper = static_cast<SkUnitMapper*>(buffer.readFlattenable());
-
-    fCache16 = fCache16Storage = NULL;
-    fCache32 = NULL;
-    fCache32PixelRef = NULL;
-
-    int colorCount = fColorCount = buffer.readU32();
-    if (colorCount > kColorStorageCount) {
-        size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
-        fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
-    } else {
-        fOrigColors = fStorage;
-    }
-    buffer.read(fOrigColors, colorCount * sizeof(SkColor));
-
-    fTileMode = (TileMode)buffer.readU8();
-    fTileProc = gTileProcs[fTileMode];
-    fRecs = (Rec*)(fOrigColors + colorCount);
-    if (colorCount > 2) {
-        Rec* recs = fRecs;
-        recs[0].fPos = 0;
-        for (int i = 1; i < colorCount; i++) {
-            recs[i].fPos = buffer.readS32();
-            recs[i].fScale = buffer.readU32();
-        }
-    }
-    SkReadMatrix(&buffer, &fPtsToUnit);
-    this->initCommon();
-}
-
-Gradient_Shader::~Gradient_Shader() {
-    if (fCache16Storage) {
-        sk_free(fCache16Storage);
-    }
-    SkSafeUnref(fCache32PixelRef);
-    if (fOrigColors != fStorage) {
-        sk_free(fOrigColors);
-    }
-    SkSafeUnref(fMapper);
-}
-
-void Gradient_Shader::initCommon() {
-    fFlags = 0;
-    unsigned colorAlpha = 0xFF;
-    for (int i = 0; i < fColorCount; i++) {
-        colorAlpha &= SkColorGetA(fOrigColors[i]);
-    }
-    fColorsAreOpaque = colorAlpha == 0xFF;
-}
-
-void Gradient_Shader::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-    buffer.writeFlattenable(fMapper);
-    buffer.write32(fColorCount);
-    buffer.writeMul4(fOrigColors, fColorCount * sizeof(SkColor));
-    buffer.write8(fTileMode);
-    if (fColorCount > 2) {
-        Rec* recs = fRecs;
-        for (int i = 1; i < fColorCount; i++) {
-            buffer.write32(recs[i].fPos);
-            buffer.write32(recs[i].fScale);
-        }
-    }
-    SkWriteMatrix(&buffer, fPtsToUnit);
-}
-
-bool Gradient_Shader::isOpaque() const {
-    return fColorsAreOpaque;
-}
-
-bool Gradient_Shader::setContext(const SkBitmap& device,
-                                 const SkPaint& paint,
-                                 const SkMatrix& matrix) {
-    if (!this->INHERITED::setContext(device, paint, matrix)) {
-        return false;
-    }
-
-    const SkMatrix& inverse = this->getTotalInverse();
-
-    if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
-        return false;
-    }
-
-    fDstToIndexProc = fDstToIndex.getMapXYProc();
-    fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
-
-    // now convert our colors in to PMColors
-    unsigned paintAlpha = this->getPaintAlpha();
-
-    fFlags = this->INHERITED::getFlags();
-    if (fColorsAreOpaque && paintAlpha == 0xFF) {
-        fFlags |= kOpaqueAlpha_Flag;
-    }
-    // we can do span16 as long as our individual colors are opaque,
-    // regardless of the paint's alpha
-    if (fColorsAreOpaque) {
-        fFlags |= kHasSpan16_Flag;
-    }
-
-    this->setCacheAlpha(paintAlpha);
-    return true;
-}
-
-void Gradient_Shader::setCacheAlpha(U8CPU alpha) const {
-    // if the new alpha differs from the previous time we were called, inval our cache
-    // this will trigger the cache to be rebuilt.
-    // we don't care about the first time, since the cache ptrs will already be NULL
-    if (fCacheAlpha != alpha) {
-        fCache16 = NULL;            // inval the cache
-        fCache32 = NULL;            // inval the cache
-        fCacheAlpha = alpha;        // record the new alpha
-        // inform our subclasses
-        if (fCache32PixelRef) {
-            fCache32PixelRef->notifyPixelsChanged();
-        }
-    }
-}
-
-static inline int blend8(int a, int b, int scale) {
-    SkASSERT(a == SkToU8(a));
-    SkASSERT(b == SkToU8(b));
-    SkASSERT(scale >= 0 && scale <= 256);
-    return a + ((b - a) * scale >> 8);
-}
-
-static inline uint32_t dot8_blend_packed32(uint32_t s0, uint32_t s1,
-                                           int blend) {
-#if 0
-    int a = blend8(SkGetPackedA32(s0), SkGetPackedA32(s1), blend);
-    int r = blend8(SkGetPackedR32(s0), SkGetPackedR32(s1), blend);
-    int g = blend8(SkGetPackedG32(s0), SkGetPackedG32(s1), blend);
-    int b = blend8(SkGetPackedB32(s0), SkGetPackedB32(s1), blend);
-
-    return SkPackARGB32(a, r, g, b);
-#else
-    int otherBlend = 256 - blend;
-
-#if 0
-    U32 t0 = (((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF;
-    U32 t1 = (((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00;
-    SkASSERT((t0 & t1) == 0);
-    return t0 | t1;
-#else
-    return  ((((s0 & 0xFF00FF) * blend + (s1 & 0xFF00FF) * otherBlend) >> 8) & 0xFF00FF) |
-            ((((s0 >> 8) & 0xFF00FF) * blend + ((s1 >> 8) & 0xFF00FF) * otherBlend) & 0xFF00FF00);
-#endif
-
-#endif
-}
-
-#define Fixed_To_Dot8(x)        (((x) + 0x80) >> 8)
-
-/** We take the original colors, not our premultiplied PMColors, since we can
-    build a 16bit table as long as the original colors are opaque, even if the
-    paint specifies a non-opaque alpha.
-*/
-void Gradient_Shader::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
-                                      int count) {
-    SkASSERT(count > 1);
-    SkASSERT(SkColorGetA(c0) == 0xFF);
-    SkASSERT(SkColorGetA(c1) == 0xFF);
-
-    SkFixed r = SkColorGetR(c0);
-    SkFixed g = SkColorGetG(c0);
-    SkFixed b = SkColorGetB(c0);
-
-    SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
-    SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
-    SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
-
-    r = SkIntToFixed(r) + 0x8000;
-    g = SkIntToFixed(g) + 0x8000;
-    b = SkIntToFixed(b) + 0x8000;
-
-    do {
-        unsigned rr = r >> 16;
-        unsigned gg = g >> 16;
-        unsigned bb = b >> 16;
-        cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
-        cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
-        cache += 1;
-        r += dr;
-        g += dg;
-        b += db;
-    } while (--count != 0);
-}
-
-/*
- *  2x2 dither a fixed-point color component (8.16) down to 8, matching the
- *  semantics of how we 2x2 dither 32->16
- */
-static inline U8CPU dither_fixed_to_8(SkFixed n) {
-    n >>= 8;
-    return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
-}
-
-/*
- *  For dithering with premultiply, we want to ceiling the alpha component,
- *  to ensure that it is always >= any color component.
- */
-static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
-    n >>= 8;
-    return ((n << 1) - (n | (n >> 8))) >> 8;
-}
-
-void Gradient_Shader::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
-                                      int count, U8CPU paintAlpha) {
-    SkASSERT(count > 1);
-
-    // need to apply paintAlpha to our two endpoints
-    SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
-    SkFixed da;
-    {
-        int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
-        da = SkIntToFixed(tmp - a) / (count - 1);
-    }
-
-    SkFixed r = SkColorGetR(c0);
-    SkFixed g = SkColorGetG(c0);
-    SkFixed b = SkColorGetB(c0);
-    SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
-    SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
-    SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
-
-    a = SkIntToFixed(a) + 0x8000;
-    r = SkIntToFixed(r) + 0x8000;
-    g = SkIntToFixed(g) + 0x8000;
-    b = SkIntToFixed(b) + 0x8000;
-
-    do {
-        cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
-        cache[kCache32Count] =
-            SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
-                                    dither_fixed_to_8(r),
-                                    dither_fixed_to_8(g),
-                                    dither_fixed_to_8(b));
-        cache += 1;
-        a += da;
-        r += dr;
-        g += dg;
-        b += db;
-    } while (--count != 0);
-}
-
-static inline int SkFixedToFFFF(SkFixed x) {
-    SkASSERT((unsigned)x <= SK_Fixed1);
-    return x - (x >> 16);
-}
-
-static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
-    SkASSERT(x < (1U << bits));
-    if (6 == bits) {
-        return (x << 10) | (x << 4) | (x >> 2);
-    }
-    if (8 == bits) {
-        return (x << 8) | x;
-    }
-    sk_throw();
-    return 0;
-}
-
-/** We duplicate the last value in each half of the cache so that
-    interpolation doesn't have to special-case being at the last point.
-*/
-static void complete_16bit_cache(uint16_t* cache, int stride) {
-    cache[stride - 1] = cache[stride - 2];
-    cache[2 * stride - 1] = cache[2 * stride - 2];
-}
-
-const uint16_t* Gradient_Shader::getCache16() const {
-    if (fCache16 == NULL) {
-        // double the count for dither entries
-        const int entryCount = kCache16Count * 2;
-        const size_t allocSize = sizeof(uint16_t) * entryCount;
-
-        if (fCache16Storage == NULL) { // set the storage and our working ptr
-            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
-        }
-        fCache16 = fCache16Storage;
-        if (fColorCount == 2) {
-            Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
-                            kGradient16Length);
-        } else {
-            Rec* rec = fRecs;
-            int prevIndex = 0;
-            for (int i = 1; i < fColorCount; i++) {
-                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
-                SkASSERT(nextIndex < kCache16Count);
-
-                if (nextIndex > prevIndex)
-                    Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
-                prevIndex = nextIndex;
-            }
-            // one extra space left over at the end for complete_16bit_cache()
-            SkASSERT(prevIndex == kGradient16Length - 1);
-        }
-
-        if (fMapper) {
-            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
-            uint16_t* linear = fCache16;         // just computed linear data
-            uint16_t* mapped = fCache16Storage;  // storage for mapped data
-            SkUnitMapper* map = fMapper;
-            for (int i = 0; i < kGradient16Length; i++) {
-                int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
-                mapped[i] = linear[index];
-                mapped[i + kCache16Count] = linear[index + kCache16Count];
-            }
-            sk_free(fCache16);
-            fCache16 = fCache16Storage;
-        }
-        complete_16bit_cache(fCache16, kCache16Count);
-    }
-    return fCache16;
-}
-
-/** We duplicate the last value in each half of the cache so that
-    interpolation doesn't have to special-case being at the last point.
-*/
-static void complete_32bit_cache(SkPMColor* cache, int stride) {
-    cache[stride - 1] = cache[stride - 2];
-    cache[2 * stride - 1] = cache[2 * stride - 2];
-}
-
-const SkPMColor* Gradient_Shader::getCache32() const {
-    if (fCache32 == NULL) {
-        // double the count for dither entries
-        const int entryCount = kCache32Count * 2;
-        const size_t allocSize = sizeof(SkPMColor) * entryCount;
-
-        if (NULL == fCache32PixelRef) {
-            fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
-                                          (NULL, allocSize, NULL));
-        }
-        fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
-        if (fColorCount == 2) {
-            Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
-                            kGradient32Length, fCacheAlpha);
-        } else {
-            Rec* rec = fRecs;
-            int prevIndex = 0;
-            for (int i = 1; i < fColorCount; i++) {
-                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
-                SkASSERT(nextIndex < kGradient32Length);
-
-                if (nextIndex > prevIndex)
-                    Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
-                                    fOrigColors[i],
-                                    nextIndex - prevIndex + 1, fCacheAlpha);
-                prevIndex = nextIndex;
-            }
-            SkASSERT(prevIndex == kGradient32Length - 1);
-        }
-
-        if (fMapper) {
-            SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
-                                                 (NULL, allocSize, NULL));
-            SkPMColor* linear = fCache32;           // just computed linear data
-            SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
-            SkUnitMapper* map = fMapper;
-            for (int i = 0; i < kGradient32Length; i++) {
-                int index = map->mapUnit16((i << 8) | i) >> 8;
-                mapped[i] = linear[index];
-                mapped[i + kCache32Count] = linear[index + kCache32Count];
-            }
-            fCache32PixelRef->unref();
-            fCache32PixelRef = newPR;
-            fCache32 = (SkPMColor*)newPR->getAddr();
-        }
-        complete_32bit_cache(fCache32, kCache32Count);
-    }
-    return fCache32;
-}
-
-/*
- *  Because our caller might rebuild the same (logically the same) gradient
- *  over and over, we'd like to return exactly the same "bitmap" if possible,
- *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
- *  To do that, we maintain a private cache of built-bitmaps, based on our
- *  colors and positions. Note: we don't try to flatten the fMapper, so if one
- *  is present, we skip the cache for now.
- */
-void Gradient_Shader::commonAsABitmap(SkBitmap* bitmap) const {
-    // our caller assumes no external alpha, so we ensure that our cache is
-    // built with 0xFF
-    this->setCacheAlpha(0xFF);
-
-    // don't have a way to put the mapper into our cache-key yet
-    if (fMapper) {
-        // force our cahce32pixelref to be built
-        (void)this->getCache32();
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
-        bitmap->setPixelRef(fCache32PixelRef);
-        return;
-    }
-
-    // build our key: [numColors + colors[] + {positions[]} ]
-    int count = 1 + fColorCount;
-    if (fColorCount > 2) {
-        count += fColorCount - 1;    // fRecs[].fPos
-    }
-
-    SkAutoSTMalloc<16, int32_t> storage(count);
-    int32_t* buffer = storage.get();
-
-    *buffer++ = fColorCount;
-    memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
-    buffer += fColorCount;
-    if (fColorCount > 2) {
-        for (int i = 1; i < fColorCount; i++) {
-            *buffer++ = fRecs[i].fPos;
-        }
-    }
-    SkASSERT(buffer - storage.get() == count);
-
-    ///////////////////////////////////
-
-    SK_DECLARE_STATIC_MUTEX(gMutex);
-    static SkBitmapCache* gCache;
-    // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
-    static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
-    SkAutoMutexAcquire ama(gMutex);
-
-    if (NULL == gCache) {
-        gCache = new SkBitmapCache(MAX_NUM_CACHED_GRADIENT_BITMAPS);
-    }
-    size_t size = count * sizeof(int32_t);
-
-    if (!gCache->find(storage.get(), size, bitmap)) {
-        // force our cahce32pixelref to be built
-        (void)this->getCache32();
-        // Only expose the linear section of the cache; don't let the caller
-        // know about the padding at the end to make interpolation faster.
-        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kGradient32Length, 1);
-        bitmap->setPixelRef(fCache32PixelRef);
-
-        gCache->add(storage.get(), size, *bitmap);
-    }
-}
-
-void Gradient_Shader::commonAsAGradient(GradientInfo* info) const {
-    if (info) {
-        if (info->fColorCount >= fColorCount) {
-            if (info->fColors) {
-                memcpy(info->fColors, fOrigColors,
-                       fColorCount * sizeof(SkColor));
-            }
-            if (info->fColorOffsets) {
-                if (fColorCount == 2) {
-                    info->fColorOffsets[0] = 0;
-                    info->fColorOffsets[1] = SK_Scalar1;
-                } else if (fColorCount > 2) {
-                    for (int i = 0; i < fColorCount; i++)
-                        info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
-                }
-            }
-        }
-        info->fColorCount = fColorCount;
-        info->fTileMode = fTileMode;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
-    SkVector    vec = pts[1] - pts[0];
-    SkScalar    mag = vec.length();
-    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
-
-    vec.scale(inv);
-    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
-    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
-    matrix->postScale(inv, inv);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class Linear_Gradient : public Gradient_Shader {
-public:
-    Linear_Gradient(const SkPoint pts[2],
-                    const SkColor colors[], const SkScalar pos[], int colorCount,
-                    SkShader::TileMode mode, SkUnitMapper* mapper)
-        : Gradient_Shader(colors, pos, colorCount, mode, mapper),
-          fStart(pts[0]),
-          fEnd(pts[1])
-    {
-        pts_to_unit_matrix(pts, &fPtsToUnit);
-    }
-
-    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
-    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
-    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*,
-                             SkScalar* twoPointRadialParams) const SK_OVERRIDE;
-    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(Linear_Gradient, (buffer));
-    }
-
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
-        this->INHERITED::flatten(buffer);
-        buffer.writeScalar(fStart.fX);
-        buffer.writeScalar(fStart.fY);
-        buffer.writeScalar(fEnd.fX);
-        buffer.writeScalar(fEnd.fY);
-    }
-
-    SK_DECLARE_FLATTENABLE_REGISTRAR()
-
-protected:
-    Linear_Gradient(SkFlattenableReadBuffer& buffer)
-        : Gradient_Shader(buffer),
-          fStart(unflatten_point(buffer)),
-          fEnd(unflatten_point(buffer)) {
-    }
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-
-private:
-    typedef Gradient_Shader INHERITED;
-    const SkPoint fStart;
-    const SkPoint fEnd;
-};
-
-bool Linear_Gradient::setContext(const SkBitmap& device, const SkPaint& paint,
-                                 const SkMatrix& matrix) {
-    if (!this->INHERITED::setContext(device, paint, matrix)) {
-        return false;
-    }
-
-    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
-    if ((fDstToIndex.getType() & ~mask) == 0) {
-        fFlags |= SkShader::kConstInY32_Flag;
-        if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
-            // only claim this if we do have a 16bit mode (i.e. none of our
-            // colors have alpha), and if we are not dithering (which obviously
-            // is not const in Y).
-            fFlags |= SkShader::kConstInY16_Flag;
-        }
-    }
-    return true;
-}
-
-#define NO_CHECK_ITER               \
-    do {                            \
-    unsigned fi = fx >> Gradient_Shader::kCache32Shift; \
-    SkASSERT(fi <= 0xFF);           \
-    fx += dx;                       \
-    *dstC++ = cache[toggle + fi];   \
-    toggle ^= Gradient_Shader::kDitherStride32; \
-    } while (0)
-
-namespace {
-
-typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
-                                SkPMColor* dstC, const SkPMColor* cache,
-                                int toggle, int count);
-
-// This function is deprecated, and will be replaced by 
-// shadeSpan_linear_vertical_lerp() once Chrome has been weaned off of it.
-void shadeSpan_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
-                               SkPMColor* SK_RESTRICT dstC,
-                               const SkPMColor* SK_RESTRICT cache,
-                               int toggle, int count) {
-    // We're a vertical gradient, so no change in a span.
-    // If colors change sharply across the gradient, dithering is
-    // insufficient (it subsamples the color space) and we need to lerp.
-    unsigned fullIndex = proc(fx);
-    unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
-    sk_memset32_dither(dstC,
-            cache[toggle + fi],
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi],
-            count);
-}
-
-// Linear interpolation (lerp) is unnecessary if there are no sharp
-// discontinuities in the gradient - which must be true if there are
-// only 2 colors - but it's cheap.
-void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
-                                    SkPMColor* SK_RESTRICT dstC,
-                                    const SkPMColor* SK_RESTRICT cache,
-                                    int toggle, int count) {
-    // We're a vertical gradient, so no change in a span.
-    // If colors change sharply across the gradient, dithering is
-    // insufficient (it subsamples the color space) and we need to lerp.
-    unsigned fullIndex = proc(fx);
-    unsigned fi = fullIndex >> (16 - Gradient_Shader::kCache32Bits);
-    unsigned remainder = fullIndex & Gradient_Shader::kLerpRemainderMask32;
-    SkPMColor lerp =
-        SkFastFourByteInterp(
-            cache[toggle + fi + 1],
-            cache[toggle + fi], remainder);
-    SkPMColor dlerp =
-        SkFastFourByteInterp(
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi + 1],
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + fi], remainder);
-    sk_memset32_dither(dstC, lerp, dlerp, count);
-}
-
-void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
-                            SkPMColor* SK_RESTRICT dstC,
-                            const SkPMColor* SK_RESTRICT cache,
-                            int toggle, int count) {
-    SkClampRange range;
-    range.init(fx, dx, count, 0, Gradient_Shader::kGradient32Length);
-
-    if ((count = range.fCount0) > 0) {
-        sk_memset32_dither(dstC,
-            cache[toggle + range.fV0],
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV0],
-            count);
-        dstC += count;
-    }
-    if ((count = range.fCount1) > 0) {
-        int unroll = count >> 3;
-        fx = range.fFx1;
-        for (int i = 0; i < unroll; i++) {
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-            NO_CHECK_ITER;  NO_CHECK_ITER;
-        }
-        if ((count &= 7) > 0) {
-            do {
-                NO_CHECK_ITER;
-            } while (--count != 0);
-        }
-    }
-    if ((count = range.fCount2) > 0) {
-        sk_memset32_dither(dstC,
-            cache[toggle + range.fV1],
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + range.fV1],
-            count);
-    }
-}
-
-void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
-                             SkPMColor* SK_RESTRICT dstC,
-                             const SkPMColor* SK_RESTRICT cache,
-                             int toggle, int count) {
-    do {
-        unsigned fi = mirror_8bits(fx >> 8);
-        SkASSERT(fi <= 0xFF);
-        fx += dx;
-        *dstC++ = cache[toggle + fi];
-        toggle ^= Gradient_Shader::kDitherStride32;
-    } while (--count != 0);
-}
-
-void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
-        SkPMColor* SK_RESTRICT dstC,
-        const SkPMColor* SK_RESTRICT cache,
-        int toggle, int count) {
-    do {
-        unsigned fi = repeat_8bits(fx >> 8);
-        SkASSERT(fi <= 0xFF);
-        fx += dx;
-        *dstC++ = cache[toggle + fi];
-        toggle ^= Gradient_Shader::kDitherStride32;
-    } while (--count != 0);
-}
-
-}
-
-void Linear_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
-                                int count) {
-    SkASSERT(count > 0);
-
-    SkPoint             srcPt;
-    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
-#ifdef USE_DITHER_32BIT_GRADIENT
-    int                 toggle = ((x ^ y) & 1) * kDitherStride32;
-#else
-    int toggle = 0;
-#endif
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            SkFixed dxStorage[1];
-            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
-            dx = dxStorage[0];
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = SkScalarToFixed(fDstToIndex.getScaleX());
-        }
-
-        LinearShadeProc shadeProc = shadeSpan_linear_repeat;
-        // We really should check the endpoint colors, but short of that change
-        // we reduce the tolerance of SkFixedNearlyZero to be more restrictive.
-        if (SkFixedNearlyZero(dx, (SK_Fixed1 >> 14))) {
-#ifdef SK_SIMPLE_TWOCOLOR_VERTICAL_GRADIENTS
-            if (fColorCount > 2) {
-                shadeProc = shadeSpan_linear_vertical_lerp;
-            } else {
-                shadeProc = shadeSpan_linear_vertical;
-            }
-#else
-            shadeProc = shadeSpan_linear_vertical_lerp;
-#endif
-        } else if (proc == clamp_tileproc) {
-            shadeProc = shadeSpan_linear_clamp;
-        } else if (proc == mirror_tileproc) {
-            shadeProc = shadeSpan_linear_mirror;
-        } else {
-            SkASSERT(proc == repeat_tileproc);
-        }
-        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
-    } else {
-        SkScalar    dstX = SkIntToScalar(x);
-        SkScalar    dstY = SkIntToScalar(y);
-        do {
-            dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
-            SkASSERT(fi <= 0xFFFF);
-            *dstC++ = cache[toggle + (fi >> kCache32Shift)];
-            toggle ^= Gradient_Shader::kDitherStride32;
-            dstX += SK_Scalar1;
-        } while (--count != 0);
-    }
-}
-
-SkShader::BitmapType Linear_Gradient::asABitmap(SkBitmap* bitmap,
-                                                SkMatrix* matrix,
-                                                TileMode xy[],
-                                        SkScalar* twoPointRadialParams) const {
-    if (bitmap) {
-        this->commonAsABitmap(bitmap);
-    }
-    if (matrix) {
-        matrix->setScale(SkIntToScalar(kGradient32Length), SK_Scalar1);
-        matrix->preConcat(fPtsToUnit);
-    }
-    if (xy) {
-        xy[0] = fTileMode;
-        xy[1] = kClamp_TileMode;
-    }
-    return kDefault_BitmapType;
-}
-
-SkShader::GradientType Linear_Gradient::asAGradient(GradientInfo* info) const {
-    if (info) {
-        commonAsAGradient(info);
-        info->fPoint[0] = fStart;
-        info->fPoint[1] = fEnd;
-    }
-    return kLinear_GradientType;
-}
-
-static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
-                            int count) {
-    if (reinterpret_cast<uintptr_t>(dst) & 2) {
-        *dst++ = value;
-        count -= 1;
-        SkTSwap(value, other);
-    }
-
-    sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
-
-    if (count & 1) {
-        dst[count - 1] = value;
-    }
-}
-
-#define NO_CHECK_ITER_16                \
-    do {                                \
-    unsigned fi = fx >> Gradient_Shader::kCache16Shift;  \
-    SkASSERT(fi < Gradient_Shader::kCache16Count);       \
-    fx += dx;                           \
-    *dstC++ = cache[toggle + fi];       \
-    toggle ^= Gradient_Shader::kDitherStride16;            \
-    } while (0)
-
-namespace {
-
-typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
-                                  uint16_t* dstC, const uint16_t* cache,
-                                  int toggle, int count);
-
-void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
-                                 uint16_t* SK_RESTRICT dstC,
-                                 const uint16_t* SK_RESTRICT cache,
-                                 int toggle, int count) {
-    // we're a vertical gradient, so no change in a span
-    unsigned fi = proc(fx) >> Gradient_Shader::kCache16Shift;
-    SkASSERT(fi < Gradient_Shader::kCache16Count);
-    dither_memset16(dstC, cache[toggle + fi],
-        cache[(toggle ^ Gradient_Shader::kDitherStride16) + fi], count);
-
-}
-
-void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
-                              uint16_t* SK_RESTRICT dstC,
-                              const uint16_t* SK_RESTRICT cache,
-                              int toggle, int count) {
-    SkClampRange range;
-    range.init(fx, dx, count, 0, Gradient_Shader::kGradient16Length);
-
-    if ((count = range.fCount0) > 0) {
-        dither_memset16(dstC,
-            cache[toggle + range.fV0],
-            cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV0],
-            count);
-        dstC += count;
-    }
-    if ((count = range.fCount1) > 0) {
-        int unroll = count >> 3;
-        fx = range.fFx1;
-        for (int i = 0; i < unroll; i++) {
-            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
-            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
-            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
-            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
-        }
-        if ((count &= 7) > 0) {
-            do {
-                NO_CHECK_ITER_16;
-            } while (--count != 0);
-        }
-    }
-    if ((count = range.fCount2) > 0) {
-        dither_memset16(dstC,
-            cache[toggle + range.fV1],
-            cache[(toggle ^ Gradient_Shader::kDitherStride16) + range.fV1],
-            count);
-    }
-}
-
-void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
-                               uint16_t* SK_RESTRICT dstC,
-                               const uint16_t* SK_RESTRICT cache,
-                               int toggle, int count) {
-    do {
-        unsigned fi = mirror_bits(fx >> Gradient_Shader::kCache16Shift,
-                                        Gradient_Shader::kCache16Bits);
-        SkASSERT(fi < Gradient_Shader::kCache16Count);
-        fx += dx;
-        *dstC++ = cache[toggle + fi];
-        toggle ^= Gradient_Shader::kDitherStride16;
-    } while (--count != 0);
-}
-
-void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
-                               uint16_t* SK_RESTRICT dstC,
-                               const uint16_t* SK_RESTRICT cache,
-                               int toggle, int count) {
-    SkASSERT(proc == repeat_tileproc);
-    do {
-        unsigned fi = repeat_bits(fx >> Gradient_Shader::kCache16Shift,
-                                  Gradient_Shader::kCache16Bits);
-        SkASSERT(fi < Gradient_Shader::kCache16Count);
-        fx += dx;
-        *dstC++ = cache[toggle + fi];
-        toggle ^= Gradient_Shader::kDitherStride16;
-    } while (--count != 0);
-}
-}
-
-void Linear_Gradient::shadeSpan16(int x, int y,
-                                  uint16_t* SK_RESTRICT dstC, int count) {
-    SkASSERT(count > 0);
-
-    SkPoint             srcPt;
-    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const uint16_t* SK_RESTRICT cache = this->getCache16();
-    int                 toggle = ((x ^ y) & 1) * kDitherStride16;
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            SkFixed dxStorage[1];
-            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
-            dx = dxStorage[0];
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = SkScalarToFixed(fDstToIndex.getScaleX());
-        }
-
-        LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
-        if (SkFixedNearlyZero(dx)) {
-            shadeProc = shadeSpan16_linear_vertical;
-        } else if (proc == clamp_tileproc) {
-            shadeProc = shadeSpan16_linear_clamp;
-        } else if (proc == mirror_tileproc) {
-            shadeProc = shadeSpan16_linear_mirror;
-        } else {
-            SkASSERT(proc == repeat_tileproc);
-        }
-        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
-    } else {
-        SkScalar    dstX = SkIntToScalar(x);
-        SkScalar    dstY = SkIntToScalar(y);
-        do {
-            dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
-            SkASSERT(fi <= 0xFFFF);
-
-            int index = fi >> kCache16Shift;
-            *dstC++ = cache[toggle + index];
-            toggle ^= Gradient_Shader::kDitherStride16;
-
-            dstX += SK_Scalar1;
-        } while (--count != 0);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#define kSQRT_TABLE_BITS    11
-#define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
-
-#include "SkRadialGradient_Table.h"
-
-#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
-
-#include <stdio.h>
-
-void SkRadialGradient_BuildTable() {
-    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
-
-    FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
-    SkASSERT(file);
-    ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
-
-    for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
-        if ((i & 15) == 0) {
-            ::fprintf(file, "\t");
-        }
-
-        uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
-
-        ::fprintf(file, "0x%02X", value);
-        if (i < kSQRT_TABLE_SIZE-1) {
-            ::fprintf(file, ", ");
-        }
-        if ((i & 15) == 15) {
-            ::fprintf(file, "\n");
-        }
-    }
-    ::fprintf(file, "};\n");
-    ::fclose(file);
-}
-
-#endif
-
-
-static void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
-                               SkMatrix* matrix) {
-    SkScalar    inv = SkScalarInvert(radius);
-
-    matrix->setTranslate(-center.fX, -center.fY);
-    matrix->postScale(inv, inv);
-}
-
-
-namespace {
-
-typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        uint16_t* dstC, const uint16_t* cache,
-        int toggle, int count);
-
-void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
-        int toggle, int count) {
-    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
-
-    /* knock these down so we can pin against +- 0x7FFF, which is an
-       immediate load, rather than 0xFFFF which is slower. This is a
-       compromise, since it reduces our precision, but that appears
-       to be visually OK. If we decide this is OK for all of our cases,
-       we could (it seems) put this scale-down into fDstToIndex,
-       to avoid having to do these extra shifts each time.
-    */
-    SkFixed fx = SkScalarToFixed(sfx) >> 1;
-    SkFixed dx = SkScalarToFixed(sdx) >> 1;
-    SkFixed fy = SkScalarToFixed(sfy) >> 1;
-    SkFixed dy = SkScalarToFixed(sdy) >> 1;
-    // might perform this check for the other modes,
-    // but the win will be a smaller % of the total
-    if (dy == 0) {
-        fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
-        fy *= fy;
-        do {
-            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
-            unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
-            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
-            fx += dx;
-            *dstC++ = cache[toggle +
-                            (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
-            toggle ^= Gradient_Shader::kDitherStride16;
-        } 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));
-            fx += dx;
-            fy += dy;
-            *dstC++ = cache[toggle +
-                            (sqrt_table[fi] >> Gradient_Shader::kSqrt16Shift)];
-            toggle ^= Gradient_Shader::kDitherStride16;
-        } while (--count != 0);
-    }
-}
-
-void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
-        int toggle, int count) {
-    do {
-#ifdef SK_SCALAR_IS_FLOAT
-        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
-        SkFixed dist = SkFloatToFixed(fdist);
-#else
-        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
-            SkFixedSquare(sfy);
-        if (magnitudeSquared < 0) // Overflow.
-            magnitudeSquared = SK_FixedMax;
-        SkFixed dist = SkFixedSqrt(magnitudeSquared);
-#endif
-        unsigned fi = mirror_tileproc(dist);
-        SkASSERT(fi <= 0xFFFF);
-        *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
-        toggle ^= Gradient_Shader::kDitherStride16;
-        sfx += sdx;
-        sfy += sdy;
-    } while (--count != 0);
-}
-
-void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
-        int toggle, int count) {
-    SkFixed fx = SkScalarToFixed(sfx);
-    SkFixed dx = SkScalarToFixed(sdx);
-    SkFixed fy = SkScalarToFixed(sfy);
-    SkFixed dy = SkScalarToFixed(sdy);
-    do {
-        SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
-        unsigned fi = repeat_tileproc(dist);
-        SkASSERT(fi <= 0xFFFF);
-        fx += dx;
-        fy += dy;
-        *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache16Shift)];
-        toggle ^= Gradient_Shader::kDitherStride16;
-    } while (--count != 0);
-}
-
-}
-
-class Radial_Gradient : public Gradient_Shader {
-public:
-    Radial_Gradient(const SkPoint& center, SkScalar radius,
-                    const SkColor colors[], const SkScalar pos[], int colorCount,
-                    SkShader::TileMode mode, SkUnitMapper* mapper)
-        : Gradient_Shader(colors, pos, colorCount, mode, mapper),
-          fCenter(center),
-          fRadius(radius)
-    {
-        // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
-        SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
-
-        rad_to_unit_matrix(center, radius, &fPtsToUnit);
-    }
-
-    virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
-        SK_OVERRIDE;
-    virtual void shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
-                             int count) SK_OVERRIDE {
-        SkASSERT(count > 0);
-
-        SkPoint             srcPt;
-        SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-        TileProc            proc = fTileProc;
-        const uint16_t* SK_RESTRICT cache = this->getCache16();
-        int                 toggle = ((x ^ y) & 1) * kDitherStride16;
-
-        if (fDstToIndexClass != kPerspective_MatrixClass) {
-            dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                                 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-
-            SkScalar sdx = fDstToIndex.getScaleX();
-            SkScalar sdy = fDstToIndex.getSkewY();
-
-            if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-                SkFixed storage[2];
-                (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
-                                               &storage[0], &storage[1]);
-                sdx = SkFixedToScalar(storage[0]);
-                sdy = SkFixedToScalar(storage[1]);
-            } else {
-                SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            }
-
-            RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
-            if (proc == clamp_tileproc) {
-                shadeProc = shadeSpan16_radial_clamp;
-            } else if (proc == mirror_tileproc) {
-                shadeProc = shadeSpan16_radial_mirror;
-            } else {
-                SkASSERT(proc == repeat_tileproc);
-            }
-            (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
-                         cache, toggle, count);
-        } else {    // perspective case
-            SkScalar dstX = SkIntToScalar(x);
-            SkScalar dstY = SkIntToScalar(y);
-            do {
-                dstProc(fDstToIndex, dstX, dstY, &srcPt);
-                unsigned fi = proc(SkScalarToFixed(srcPt.length()));
-                SkASSERT(fi <= 0xFFFF);
-
-                int index = fi >> (16 - kCache16Bits);
-                *dstC++ = cache[toggle + index];
-                toggle ^= kDitherStride16;
-
-                dstX += SK_Scalar1;
-            } while (--count != 0);
-        }
-    }
-
-    virtual BitmapType asABitmap(SkBitmap* bitmap,
-                                 SkMatrix* matrix,
-                                 TileMode* xy,
-                                 SkScalar* twoPointRadialParams)
-            const SK_OVERRIDE {
-        if (bitmap) {
-            this->commonAsABitmap(bitmap);
-        }
-        if (matrix) {
-            matrix->setScale(SkIntToScalar(kGradient32Length),
-                             SkIntToScalar(kGradient32Length));
-            matrix->preConcat(fPtsToUnit);
-        }
-        if (xy) {
-            xy[0] = fTileMode;
-            xy[1] = kClamp_TileMode;
-        }
-        return kRadial_BitmapType;
-    }
-    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
-        if (info) {
-            commonAsAGradient(info);
-            info->fPoint[0] = fCenter;
-            info->fRadius[0] = fRadius;
-        }
-        return kRadial_GradientType;
-    }
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(Radial_Gradient, (buffer));
-    }
-
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
-        this->INHERITED::flatten(buffer);
-        buffer.writeScalar(fCenter.fX);
-        buffer.writeScalar(fCenter.fY);
-        buffer.writeScalar(fRadius);
-    }
-
-protected:
-    Radial_Gradient(SkFlattenableReadBuffer& buffer)
-        : Gradient_Shader(buffer),
-          fCenter(unflatten_point(buffer)),
-          fRadius(buffer.readScalar()) {
-    }
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-
-private:
-    typedef Gradient_Shader INHERITED;
-    const SkPoint fCenter;
-    const SkScalar fRadius;
-};
-
-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);
-
-    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] >> Gradient_Shader::kSqrt32Shift)]; \
-    toggle ^= Gradient_Shader::kDitherStride32; \
-    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)) {
-        unsigned fi = Gradient_Shader::kGradient32Length;
-        sk_memset32_dither(dstC,
-            cache[toggle + fi],
-            cache[(toggle ^ Gradient_Shader::kDitherStride32) + 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;
-        }
-        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] >>
-                    Gradient_Shader::kSqrt32Shift)];
-                toggle ^= Gradient_Shader::kDitherStride32;
-                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] >>
-                    Gradient_Shader::kSqrt32Shift)];
-                toggle ^= Gradient_Shader::kDitherStride32;
-                fx += dx;
-                fy += dy;
-            } while (--count != 0);
-        }
-    }
-}
-
-// Unrolling this loop doesn't seem to help (when float); we're stalling to
-// get the results of the sqrt (?), and don't have enough extra registers to
-// have many in flight.
-void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-        int count, int toggle) {
-    do {
-#ifdef SK_SCALAR_IS_FLOAT
-        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
-        SkFixed dist = SkFloatToFixed(fdist);
-#else
-        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
-            SkFixedSquare(sfy);
-        if (magnitudeSquared < 0) // Overflow.
-            magnitudeSquared = SK_FixedMax;
-        SkFixed dist = SkFixedSqrt(magnitudeSquared);
-#endif
-        unsigned fi = mirror_tileproc(dist);
-        SkASSERT(fi <= 0xFFFF);
-        *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
-        toggle ^= Gradient_Shader::kDitherStride32;
-        sfx += sdx;
-        sfy += sdy;
-    } while (--count != 0);
-}
-
-void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
-        SkScalar sfy, SkScalar sdy,
-        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
-        int count, int toggle) {
-    SkFixed fx = SkScalarToFixed(sfx);
-    SkFixed dx = SkScalarToFixed(sdx);
-    SkFixed fy = SkScalarToFixed(sfy);
-    SkFixed dy = SkScalarToFixed(sdy);
-    do {
-        SkFixed magnitudeSquared = SkFixedSquare(fx) +
-            SkFixedSquare(fy);
-        if (magnitudeSquared < 0) // Overflow.
-            magnitudeSquared = SK_FixedMax;
-        SkFixed dist = SkFixedSqrt(magnitudeSquared);
-        unsigned fi = repeat_tileproc(dist);
-        SkASSERT(fi <= 0xFFFF);
-        *dstC++ = cache[toggle + (fi >> Gradient_Shader::kCache32Shift)];
-        toggle ^= Gradient_Shader::kDitherStride32;
-        fx += dx;
-        fy += dy;
-    } while (--count != 0);
-}
-}
-
-void Radial_Gradient::shadeSpan(int x, int y,
-                                SkPMColor* SK_RESTRICT dstC, int count) {
-    SkASSERT(count > 0);
-
-    SkPoint             srcPt;
-    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-    TileProc            proc = fTileProc;
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
-#ifdef USE_DITHER_32BIT_GRADIENT
-    int toggle = ((x ^ y) & 1) * Gradient_Shader::kDitherStride32;
-#else
-    int toggle = 0;
-#endif
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
-                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkScalar sdx = fDstToIndex.getScaleX();
-        SkScalar sdy = fDstToIndex.getSkewY();
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            SkFixed storage[2];
-            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
-                                           &storage[0], &storage[1]);
-            sdx = SkFixedToScalar(storage[0]);
-            sdy = SkFixedToScalar(storage[1]);
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-        }
-
-        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
-        if (proc == clamp_tileproc) {
-            shadeProc = shadeSpan_radial_clamp;
-        } else if (proc == mirror_tileproc) {
-            shadeProc = shadeSpan_radial_mirror;
-        } else {
-            SkASSERT(proc == repeat_tileproc);
-        }
-        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
-    } else {    // perspective case
-        SkScalar dstX = SkIntToScalar(x);
-        SkScalar dstY = SkIntToScalar(y);
-        do {
-            dstProc(fDstToIndex, dstX, dstY, &srcPt);
-            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
-            SkASSERT(fi <= 0xFFFF);
-            *dstC++ = cache[fi >> Gradient_Shader::kCache32Shift];
-            dstX += SK_Scalar1;
-        } while (--count != 0);
-    }
-}
-
-/* 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 >> Gradient_Shader::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 >> Gradient_Shader::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 >> Gradient_Shader::kCache32Shift];
-        fx += dx;
-        fy += dy;
-        b += db;
-    }
-}
-
-
-
-}
-
-class Two_Point_Radial_Gradient : public Gradient_Shader {
-public:
-    Two_Point_Radial_Gradient(const SkPoint& start, SkScalar startRadius,
-                              const SkPoint& end, SkScalar endRadius,
-                              const SkColor colors[], const SkScalar pos[],
-                              int colorCount, SkShader::TileMode mode,
-                              SkUnitMapper* mapper)
-            : Gradient_Shader(colors, pos, colorCount, mode, mapper),
-              fCenter1(start),
-              fCenter2(end),
-              fRadius1(startRadius),
-              fRadius2(endRadius) {
-        init();
-    }
-
-    virtual BitmapType asABitmap(SkBitmap* bitmap,
-                                 SkMatrix* matrix,
-                                 TileMode* xy,
-                                 SkScalar* twoPointRadialParams) const {
-        if (bitmap) {
-            this->commonAsABitmap(bitmap);
-        }
-        SkScalar diffL = 0; // just to avoid gcc warning
-        if (matrix || twoPointRadialParams) {
-            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;
-        }
-        if (NULL != twoPointRadialParams) {
-            twoPointRadialParams[0] = diffL;
-            twoPointRadialParams[1] = fStartRadius;
-            twoPointRadialParams[2] = fDiffRadius;
-        }
-        return kTwoPointRadial_BitmapType;
-    }
-
-    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
-        if (info) {
-            commonAsAGradient(info);
-            info->fPoint[0] = fCenter1;
-            info->fPoint[1] = fCenter2;
-            info->fRadius[0] = fRadius1;
-            info->fRadius[1] = fRadius2;
-        }
-        return kRadial2_GradientType;
-    }
-
-    virtual void shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
-                           int count) SK_OVERRIDE {
-        SkASSERT(count > 0);
-
-        // Zero difference between radii:  fill with transparent black.
-        if (fDiffRadius == 0) {
-          sk_bzero(dstC, count * sizeof(*dstC));
-          return;
-        }
-        SkMatrix::MapXYProc dstProc = fDstToIndexProc;
-        TileProc            proc = fTileProc;
-        const SkPMColor* SK_RESTRICT cache = this->getCache32();
-
-        SkScalar foura = fA * 4;
-        bool posRoot = 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(fDiff.fX, fx) +
-                         SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
-            SkScalar db = (SkScalarMul(fDiff.fX, dx) +
-                          SkScalarMul(fDiff.fY, dy)) * 2;
-
-            TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
-            if (proc == clamp_tileproc) {
-                shadeProc = shadeSpan_twopoint_clamp;
-            } else if (proc == mirror_tileproc) {
-                shadeProc = shadeSpan_twopoint_mirror;
-            } else {
-                SkASSERT(proc == repeat_tileproc);
-            }
-            (*shadeProc)(fx, dx, fy, dy, b, db,
-                         fSr2D2, foura, 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(fDiff.fX, fx) +
-                             SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
-                SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
-                                             fOneOverTwoA, posRoot);
-                SkFixed index = proc(t);
-                SkASSERT(index <= 0xFFFF);
-                *dstC++ = cache[index >> Gradient_Shader::kCache32Shift];
-                dstX += SK_Scalar1;
-            }
-        }
-    }
-
-    virtual bool setContext(const SkBitmap& device,
-                            const SkPaint& paint,
-                            const SkMatrix& matrix) SK_OVERRIDE {
-        if (!this->INHERITED::setContext(device, paint, matrix)) {
-            return false;
-        }
-
-        // we don't have a span16 proc
-        fFlags &= ~kHasSpan16_Flag;
-        return true;
-    }
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(Two_Point_Radial_Gradient, (buffer));
-    }
-
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
-        this->INHERITED::flatten(buffer);
-        buffer.writeScalar(fCenter1.fX);
-        buffer.writeScalar(fCenter1.fY);
-        buffer.writeScalar(fCenter2.fX);
-        buffer.writeScalar(fCenter2.fY);
-        buffer.writeScalar(fRadius1);
-        buffer.writeScalar(fRadius2);
-    }
-
-protected:
-    Two_Point_Radial_Gradient(SkFlattenableReadBuffer& buffer)
-            : Gradient_Shader(buffer),
-              fCenter1(unflatten_point(buffer)),
-              fCenter2(unflatten_point(buffer)),
-              fRadius1(buffer.readScalar()),
-              fRadius2(buffer.readScalar()) {
-        init();
-    };
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-
-private:
-    typedef Gradient_Shader INHERITED;
-    const SkPoint fCenter1;
-    const SkPoint fCenter2;
-    const SkScalar fRadius1;
-    const SkScalar fRadius2;
-    SkPoint fDiff;
-    SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
-
-    void init() {
-        fDiff = fCenter1 - fCenter2;
-        fDiffRadius = fRadius2 - fRadius1;
-        SkScalar inv = SkScalarInvert(fDiffRadius);
-        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;
-
-        fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
-        fPtsToUnit.postScale(inv, inv);
-    }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-class Sweep_Gradient : public Gradient_Shader {
-public:
-    Sweep_Gradient(SkScalar cx, SkScalar cy, const SkColor colors[],
-                   const SkScalar pos[], int count, SkUnitMapper* mapper)
-    : Gradient_Shader(colors, pos, count, SkShader::kClamp_TileMode, mapper),
-      fCenter(SkPoint::Make(cx, cy))
-    {
-        fPtsToUnit.setTranslate(-cx, -cy);
-    }
-    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
-    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
-
-    virtual BitmapType asABitmap(SkBitmap* bitmap,
-                                 SkMatrix* matrix,
-                                 TileMode* xy,
-                                 SkScalar* twoPointRadialParams) const SK_OVERRIDE {
-        if (bitmap) {
-            this->commonAsABitmap(bitmap);
-        }
-        if (matrix) {
-            *matrix = fPtsToUnit;
-        }
-        if (xy) {
-            xy[0] = fTileMode;
-            xy[1] = kClamp_TileMode;
-        }
-        return kSweep_BitmapType;
-    }
-
-    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE {
-        if (info) {
-            commonAsAGradient(info);
-            info->fPoint[0] = fCenter;
-        }
-        return kSweep_GradientType;
-    }
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(Sweep_Gradient, (buffer));
-    }
-
-    virtual void flatten(SkFlattenableWriteBuffer& buffer) SK_OVERRIDE {
-        this->INHERITED::flatten(buffer);
-        buffer.writeScalar(fCenter.fX);
-        buffer.writeScalar(fCenter.fY);
-    }
-
-protected:
-    Sweep_Gradient(SkFlattenableReadBuffer& buffer)
-        : Gradient_Shader(buffer),
-          fCenter(unflatten_point(buffer)) {
-    }
-
-    virtual Factory getFactory() SK_OVERRIDE { return CreateProc; }
-
-private:
-    typedef Gradient_Shader INHERITED;
-    const SkPoint fCenter;
-};
-
-#ifdef COMPUTE_SWEEP_TABLE
-#define PI  3.14159265
-static bool gSweepTableReady;
-static uint8_t gSweepTable[65];
-
-/*  Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
-    We scale the results to [0..32]
-*/
-static const uint8_t* build_sweep_table() {
-    if (!gSweepTableReady) {
-        const int N = 65;
-        const double DENOM = N - 1;
-
-        for (int i = 0; i < N; i++)
-        {
-            double arg = i / DENOM;
-            double v = atan(arg);
-            int iv = (int)round(v * DENOM * 2 / PI);
-//            printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
-            printf("%d, ", iv);
-            gSweepTable[i] = iv;
-        }
-        gSweepTableReady = true;
-    }
-    return gSweepTable;
-}
-#else
-static const uint8_t gSweepTable[] = {
-    0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
-    10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
-    19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
-    26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
-    32
-};
-static const uint8_t* build_sweep_table() { return gSweepTable; }
-#endif
-
-// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
-// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
-// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
-
-//unsigned div_64(int numer, int denom);
-static unsigned div_64(int numer, int denom) {
-    SkASSERT(numer <= denom);
-    SkASSERT(numer > 0);
-    SkASSERT(denom > 0);
-
-    int nbits = SkCLZ(numer);
-    int dbits = SkCLZ(denom);
-    int bits = 6 - nbits + dbits;
-    SkASSERT(bits <= 6);
-
-    if (bits < 0) {  // detect underflow
-        return 0;
-    }
-
-    denom <<= dbits - 1;
-    numer <<= nbits - 1;
-
-    unsigned result = 0;
-
-    // do the first one
-    if ((numer -= denom) >= 0) {
-        result = 1;
-    } else {
-        numer += denom;
-    }
-
-    // Now fall into our switch statement if there are more bits to compute
-    if (bits > 0) {
-        // make room for the rest of the answer bits
-        result <<= bits;
-        switch (bits) {
-        case 6:
-            if ((numer = (numer << 1) - denom) >= 0)
-                result |= 32;
-            else
-                numer += denom;
-        case 5:
-            if ((numer = (numer << 1) - denom) >= 0)
-                result |= 16;
-            else
-                numer += denom;
-        case 4:
-            if ((numer = (numer << 1) - denom) >= 0)
-                result |= 8;
-            else
-                numer += denom;
-        case 3:
-            if ((numer = (numer << 1) - denom) >= 0)
-                result |= 4;
-            else
-                numer += denom;
-        case 2:
-            if ((numer = (numer << 1) - denom) >= 0)
-                result |= 2;
-            else
-                numer += denom;
-        case 1:
-        default:    // not strictly need, but makes GCC make better ARM code
-            if ((numer = (numer << 1) - denom) >= 0)
-                result |= 1;
-            else
-                numer += denom;
-        }
-    }
-    return result;
-}
-
-// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
-static unsigned atan_0_90(SkFixed y, SkFixed x) {
-#ifdef SK_DEBUG
-    {
-        static bool gOnce;
-        if (!gOnce) {
-            gOnce = true;
-            SkASSERT(div_64(55, 55) == 64);
-            SkASSERT(div_64(128, 256) == 32);
-            SkASSERT(div_64(2326528, 4685824) == 31);
-            SkASSERT(div_64(753664, 5210112) == 9);
-            SkASSERT(div_64(229376, 4882432) == 3);
-            SkASSERT(div_64(2, 64) == 2);
-            SkASSERT(div_64(1, 64) == 1);
-            // test that we handle underflow correctly
-            SkASSERT(div_64(12345, 0x54321234) == 0);
-        }
-    }
-#endif
-
-    SkASSERT(y > 0 && x > 0);
-    const uint8_t* table = build_sweep_table();
-
-    unsigned result;
-    bool swap = (x < y);
-    if (swap) {
-        // first part of the atan(v) = PI/2 - atan(1/v) identity
-        // since our div_64 and table want v <= 1, where v = y/x
-        SkTSwap<SkFixed>(x, y);
-    }
-
-    result = div_64(y, x);
-
-#ifdef SK_DEBUG
-    {
-        unsigned result2 = SkDivBits(y, x, 6);
-        SkASSERT(result2 == result ||
-                 (result == 1 && result2 == 0));
-    }
-#endif
-
-    SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
-    result = table[result];
-
-    if (swap) {
-        // complete the atan(v) = PI/2 - atan(1/v) identity
-        result = 64 - result;
-        // pin to 63
-        result -= result >> 6;
-    }
-
-    SkASSERT(result <= 63);
-    return result;
-}
-
-//  returns angle in a circle [0..2PI) -> [0..255]
-#ifdef SK_SCALAR_IS_FLOAT
-static unsigned SkATan2_255(float y, float x) {
-    //    static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
-    static const float g255Over2PI = 40.584510488433314f;
-    
-    float result = sk_float_atan2(y, x);
-    if (result < 0) {
-        result += 2 * SK_ScalarPI;
-    }
-    SkASSERT(result >= 0);
-    // since our value is always >= 0, we can cast to int, which is faster than
-    // calling floorf()
-    int ir = (int)(result * g255Over2PI);
-    SkASSERT(ir >= 0 && ir <= 255);
-    return ir;
-}
-#else
-static unsigned SkATan2_255(SkFixed y, SkFixed x) {
-    if (x == 0) {
-        if (y == 0) {
-            return 0;
-        }
-        return y < 0 ? 192 : 64;
-    }
-    if (y == 0) {
-        return x < 0 ? 128 : 0;
-    }
-
-    /*  Find the right quadrant for x,y
-        Since atan_0_90 only handles the first quadrant, we rotate x,y
-        appropriately before calling it, and then add the right amount
-        to account for the real quadrant.
-        quadrant 0 : add 0                  | x > 0 && y > 0
-        quadrant 1 : add 64 (90 degrees)    | x < 0 && y > 0
-        quadrant 2 : add 128 (180 degrees)  | x < 0 && y < 0
-        quadrant 3 : add 192 (270 degrees)  | x > 0 && y < 0
-
-        map x<0 to (1 << 6)
-        map y<0 to (3 << 6)
-        add = map_x ^ map_y
-    */
-    int xsign = x >> 31;
-    int ysign = y >> 31;
-    int add = ((-xsign) ^ (ysign & 3)) << 6;
-
-#ifdef SK_DEBUG
-    if (0 == add)
-        SkASSERT(x > 0 && y > 0);
-    else if (64 == add)
-        SkASSERT(x < 0 && y > 0);
-    else if (128 == add)
-        SkASSERT(x < 0 && y < 0);
-    else if (192 == add)
-        SkASSERT(x > 0 && y < 0);
-    else
-        SkDEBUGFAIL("bad value for add");
-#endif
-
-    /*  This ^ trick makes x, y positive, and the swap<> handles quadrants
-        where we need to rotate x,y by 90 or -90
-    */
-    x = (x ^ xsign) - xsign;
-    y = (y ^ ysign) - ysign;
-    if (add & 64) {             // quads 1 or 3 need to swap x,y
-        SkTSwap<SkFixed>(x, y);
-    }
-
-    unsigned result = add + atan_0_90(y, x);
-    SkASSERT(result < 256);
-    return result;
-}
-#endif
-
-void Sweep_Gradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
-                               int count) {
-    SkMatrix::MapXYProc proc = fDstToIndexProc;
-    const SkMatrix&     matrix = fDstToIndex;
-    const SkPMColor* SK_RESTRICT cache = this->getCache32();
-    SkPoint             srcPt;
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
-                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkScalar dx, fx = srcPt.fX;
-        SkScalar dy, fy = srcPt.fY;
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            SkFixed storage[2];
-            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
-                                      &storage[0], &storage[1]);
-            dx = SkFixedToScalar(storage[0]);
-            dy = SkFixedToScalar(storage[1]);
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = matrix.getScaleX();
-            dy = matrix.getSkewY();
-        }
-
-        for (; count > 0; --count) {
-            *dstC++ = cache[SkATan2_255(fy, fx)];
-            fx += dx;
-            fy += dy;
-        }
-    } else {  // perspective case
-        for (int stop = x + count; x < stop; x++) {
-            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
-                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-            *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
-        }
-    }
-}
-
-void Sweep_Gradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
-                                 int count) {
-    SkMatrix::MapXYProc proc = fDstToIndexProc;
-    const SkMatrix&     matrix = fDstToIndex;
-    const uint16_t* SK_RESTRICT cache = this->getCache16();
-    int                 toggle = ((x ^ y) & 1) * kDitherStride16;
-    SkPoint             srcPt;
-
-    if (fDstToIndexClass != kPerspective_MatrixClass) {
-        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
-                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-        SkScalar dx, fx = srcPt.fX;
-        SkScalar dy, fy = srcPt.fY;
-
-        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
-            SkFixed storage[2];
-            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
-                                      &storage[0], &storage[1]);
-            dx = SkFixedToScalar(storage[0]);
-            dy = SkFixedToScalar(storage[1]);
-        } else {
-            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
-            dx = matrix.getScaleX();
-            dy = matrix.getSkewY();
-        }
-
-        for (; count > 0; --count) {
-            int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
-            *dstC++ = cache[toggle + index];
-            toggle ^= kDitherStride16;
-            fx += dx;
-            fy += dy;
-        }
-    } else {  // perspective case
-        for (int stop = x + count; x < stop; x++) {
-            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
-                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-
-            int index = SkATan2_255(srcPt.fY, srcPt.fX);
-            index >>= (8 - kCache16Bits);
-            *dstC++ = cache[toggle + index];
-            toggle ^= kDitherStride16;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////
-
-// assumes colors is SkColor* and pos is SkScalar*
-#define EXPAND_1_COLOR(count)               \
-    SkColor tmp[2];                         \
-    do {                                    \
-        if (1 == count) {                   \
-            tmp[0] = tmp[1] = colors[0];    \
-            colors = tmp;                   \
-            pos = NULL;                     \
-            count = 2;                      \
-        }                                   \
-    } while (0)
-
-SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
-                                         const SkColor colors[],
-                                         const SkScalar pos[], int colorCount,
-                                         SkShader::TileMode mode,
-                                         SkUnitMapper* mapper) {
-    if (NULL == pts || NULL == colors || colorCount < 1) {
-        return NULL;
-    }
-    EXPAND_1_COLOR(colorCount);
-
-    return SkNEW_ARGS(Linear_Gradient,
-                      (pts, colors, pos, colorCount, mode, mapper));
-}
-
-SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
-                                         const SkColor colors[],
-                                         const SkScalar pos[], int colorCount,
-                                         SkShader::TileMode mode,
-                                         SkUnitMapper* mapper) {
-    if (radius <= 0 || NULL == colors || colorCount < 1) {
-        return NULL;
-    }
-    EXPAND_1_COLOR(colorCount);
-
-    return SkNEW_ARGS(Radial_Gradient,
-                      (center, radius, colors, pos, colorCount, mode, mapper));
-}
-
-SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
-                                                 SkScalar startRadius,
-                                                 const SkPoint& end,
-                                                 SkScalar endRadius,
-                                                 const SkColor colors[],
-                                                 const SkScalar pos[],
-                                                 int colorCount,
-                                                 SkShader::TileMode mode,
-                                                 SkUnitMapper* mapper) {
-    if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
-        return NULL;
-    }
-    EXPAND_1_COLOR(colorCount);
-
-    return SkNEW_ARGS(Two_Point_Radial_Gradient,
-                      (start, startRadius, end, endRadius, colors, pos,
-                       colorCount, mode, mapper));
-}
-
-SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
-                                        const SkColor colors[],
-                                        const SkScalar pos[],
-                                        int count, SkUnitMapper* mapper) {
-    if (NULL == colors || count < 1) {
-        return NULL;
-    }
-    EXPAND_1_COLOR(count);
-
-    return SkNEW_ARGS(Sweep_Gradient, (cx, cy, colors, pos, count, mapper));
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Linear_Gradient)
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Radial_Gradient)
-
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sweep_Gradient)
-
-    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Two_Point_Radial_Gradient)
-SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/SkGroupShape.cpp b/src/effects/SkGroupShape.cpp
deleted file mode 100644
index fd741ee..0000000
--- a/src/effects/SkGroupShape.cpp
+++ /dev/null
@@ -1,135 +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 "SkGroupShape.h"
-
-SkGroupShape::SkGroupShape() {}
-
-SkGroupShape::~SkGroupShape() {
-    this->removeAllShapes();
-}
-
-int SkGroupShape::countShapes() const {
-    return fList.count();
-}
-
-SkShape* SkGroupShape::getShape(int index, SkMatrixRef** mr) const {
-    if ((unsigned)index < (unsigned)fList.count()) {
-        const Rec& rec = fList[index];
-        if (mr) {
-            *mr = rec.fMatrixRef;
-        }
-        return rec.fShape;
-    }
-    return NULL;
-}
-
-void SkGroupShape::addShape(int index, SkShape* shape, SkMatrixRef* mr) {
-    int count = fList.count();
-    if (NULL == shape || index < 0 || index > count) {
-        return;
-    }
-
-    shape->ref();
-    SkMatrixRef::SafeRef(mr);
-
-    Rec* rec;
-    if (index == count) {
-        rec = fList.append();
-    } else {
-        rec = fList.insert(index);
-    }
-    rec->fShape = shape;
-    rec->fMatrixRef = mr;
-}
-
-void SkGroupShape::removeShape(int index) {
-    if ((unsigned)index < (unsigned)fList.count()) {
-        Rec& rec = fList[index];
-        rec.fShape->unref();
-        SkMatrixRef::SafeUnref(rec.fMatrixRef);
-        fList.remove(index);
-    }
-}
-
-void SkGroupShape::removeAllShapes() {
-    Rec* rec = fList.begin();
-    Rec* stop = fList.end();
-    while (rec < stop) {
-        rec->fShape->unref();
-        SkMatrixRef::SafeUnref(rec->fMatrixRef);
-        rec++;
-    }
-    fList.reset();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkGroupShape::onDraw(SkCanvas* canvas) {
-    const Rec* rec = fList.begin();
-    const Rec* stop = fList.end();
-    while (rec < stop) {
-        SkShape* shape = rec->fShape;
-        if (rec->fMatrixRef) {
-            shape->drawMatrix(canvas, *rec->fMatrixRef);
-        } else {
-            shape->draw(canvas);
-        }
-        rec++;
-    }
-}
-
-SkFlattenable::Factory SkGroupShape::getFactory() {
-    return CreateProc;
-}
-
-void SkGroupShape::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-
-    int count = fList.count();
-    buffer.write32(count);
-    const Rec* rec = fList.begin();
-    const Rec* stop = fList.end();
-    while (rec < stop) {
-        buffer.writeFlattenable(rec->fShape);
-        if (rec->fMatrixRef) {
-            char storage[SkMatrix::kMaxFlattenSize];
-            uint32_t size = rec->fMatrixRef->flatten(storage);
-            buffer.write32(size);
-            buffer.writePad(storage, size);
-        } else {
-            buffer.write32(0);
-        }
-        rec += 1;
-    }
-}
-
-SkGroupShape::SkGroupShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer){
-    int count = buffer.readS32();
-    for (int i = 0; i < count; i++) {
-        SkShape* shape = reinterpret_cast<SkShape*>(buffer.readFlattenable());
-        SkMatrixRef* mr = NULL;
-        uint32_t size = buffer.readS32();
-        if (size) {
-            char storage[SkMatrix::kMaxFlattenSize];
-            buffer.read(storage, SkAlign4(size));
-            mr = SkNEW(SkMatrixRef);
-            mr->unflatten(storage);
-        }
-        if (shape) {
-            this->appendShape(shape, mr)->unref();
-        }
-        SkSafeUnref(mr);
-    }
-}
-
-SkFlattenable* SkGroupShape::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkGroupShape, (buffer));
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkGroupShape)
-
diff --git a/src/effects/SkImageFilterUtils.cpp b/src/effects/SkImageFilterUtils.cpp
new file mode 100644
index 0000000..2dfb33d
--- /dev/null
+++ b/src/effects/SkImageFilterUtils.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkMatrix.h"
+
+#if SK_SUPPORT_GPU
+#include "GrTexture.h"
+#include "SkImageFilterUtils.h"
+#include "SkBitmap.h"
+#include "SkGrPixelRef.h"
+#include "SkGr.h"
+
+bool SkImageFilterUtils::WrapTexture(GrTexture* texture, int width, int height, SkBitmap* result) {
+    SkASSERT(texture->config() == kSkia8888_GrPixelConfig);
+    result->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    result->setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
+    return true;
+}
+
+bool SkImageFilterUtils::GetInputResultGPU(SkImageFilter* filter, SkImageFilter::Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    if (!filter) {
+        *result = src;
+        return true;
+    } else if (filter->canFilterImageGPU()) {
+        return filter->filterImageGPU(proxy, src, result);
+    } else {
+        SkIPoint offset;
+        if (filter->filterImage(proxy, src, SkMatrix(), result, &offset)) {
+            if (!result->getTexture()) {
+                GrContext* context = ((GrTexture *) src.getTexture())->getContext();
+                GrTexture* resultTex = GrLockAndRefCachedBitmapTexture(context,
+                    *result, NULL);
+                result->setPixelRef(new SkGrPixelRef(resultTex))->unref();
+                GrUnlockAndUnrefCachedBitmapTexture(resultTex);
+            }
+            return true;
+        } else {
+            return false;
+        }
+    }
+}
+#endif
diff --git a/src/effects/SkKernel33MaskFilter.cpp b/src/effects/SkKernel33MaskFilter.cpp
index 852168c..9c42b39 100644
--- a/src/effects/SkKernel33MaskFilter.cpp
+++ b/src/effects/SkKernel33MaskFilter.cpp
@@ -7,30 +7,31 @@
  */
 #include "SkKernel33MaskFilter.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
 
-SkMask::Format SkKernel33ProcMaskFilter::getFormat() {
+SkMask::Format SkKernel33ProcMaskFilter::getFormat() const {
     return SkMask::kA8_Format;
 }
 
 bool SkKernel33ProcMaskFilter::filterMask(SkMask* dst, const SkMask& src,
-                                          const SkMatrix&, SkIPoint* margin) {
+                                      const SkMatrix&, SkIPoint* margin) const {
     // margin???
     dst->fImage = NULL;
     dst->fBounds = src.fBounds;
     dst->fBounds.inset(-1, -1);
     dst->fFormat = SkMask::kA8_Format;
-    
+
     if (NULL == src.fImage) {
         return true;
     }
-    
+
     dst->fRowBytes = dst->fBounds.width();
     size_t size = dst->computeImageSize();
     if (0 == size) {
         return false;   // too big to allocate, abort
     }
     dst->fImage = SkMask::AllocImage(size);
-    
+
     const int h = src.fBounds.height();
     const int w = src.fBounds.width();
     const int srcRB = src.fRowBytes;
@@ -39,13 +40,13 @@
 
     uint8_t* srcRows[3];
     uint8_t storage[3][3];
-    
+
     srcRows[0] = storage[0];
     srcRows[1] = storage[1];
     srcRows[2] = storage[2];
 
     unsigned scale = fPercent256;
-    
+
     for (int y = -1; y <= h; y++) {
         uint8_t* dstRow = dstImage;
         for (int x = -1; x <= w; x++) {
@@ -60,9 +61,9 @@
                     }
                     storagePtr++;
                 }
-            }            
+            }
             int value = this->computeValue(srcRows);
-            
+
             if (scale < 256) {
                 value = SkAlphaBlend(value, srcRows[1][1], scale);
             }
@@ -73,19 +74,19 @@
     return true;
 }
 
-void SkKernel33ProcMaskFilter::flatten(SkFlattenableWriteBuffer& wb) {
+void SkKernel33ProcMaskFilter::flatten(SkFlattenableWriteBuffer& wb) const {
     this->INHERITED::flatten(wb);
-    wb.write32(fPercent256);
+    wb.writeInt(fPercent256);
 }
 
 SkKernel33ProcMaskFilter::SkKernel33ProcMaskFilter(SkFlattenableReadBuffer& rb)
         : SkMaskFilter(rb) {
-    fPercent256 = rb.readS32();
+    fPercent256 = rb.readInt();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-uint8_t SkKernel33MaskFilter::computeValue(uint8_t* const* srcRows) {
+uint8_t SkKernel33MaskFilter::computeValue(uint8_t* const* srcRows) const {
     int value = 0;
 
     for (int i = 0; i < 3; i++) {
@@ -93,7 +94,7 @@
             value += fKernel[i][j] * srcRows[i][j];
         }
     }
-    
+
     value >>= fShift;
 
     if (value < 0) {
@@ -104,23 +105,15 @@
     return (uint8_t)value;
 }
 
-void SkKernel33MaskFilter::flatten(SkFlattenableWriteBuffer& wb) {
+void SkKernel33MaskFilter::flatten(SkFlattenableWriteBuffer& wb) const {
     this->INHERITED::flatten(wb);
-    wb.writeMul4(fKernel, 9 * sizeof(int));
-    wb.write32(fShift);
-}
-
-SkFlattenable::Factory SkKernel33MaskFilter::getFactory() {
-    return Create;
-}
-
-SkFlattenable* SkKernel33MaskFilter::Create(SkFlattenableReadBuffer& rb) {
-    return new SkKernel33MaskFilter(rb);
+    wb.writeIntArray(&fKernel[0][0], 9);
+    wb.writeInt(fShift);
 }
 
 SkKernel33MaskFilter::SkKernel33MaskFilter(SkFlattenableReadBuffer& rb)
         : SkKernel33ProcMaskFilter(rb) {
-    rb.read(fKernel, 9 * sizeof(int));
-    fShift = rb.readS32();
+    SkDEBUGCODE(const uint32_t count = )rb.readIntArray(&fKernel[0][0]);
+    SkASSERT(9 == count);
+    fShift = rb.readInt();
 }
-
diff --git a/src/effects/SkLayerDrawLooper.cpp b/src/effects/SkLayerDrawLooper.cpp
index 16636dc..0ffc08e 100644
--- a/src/effects/SkLayerDrawLooper.cpp
+++ b/src/effects/SkLayerDrawLooper.cpp
@@ -7,10 +7,15 @@
  */
 #include "SkCanvas.h"
 #include "SkColor.h"
+#include "SkFlattenableBuffers.h"
 #include "SkLayerDrawLooper.h"
 #include "SkPaint.h"
+#include "SkString.h"
+#include "SkStringUtils.h"
 #include "SkUnPreMultiply.h"
 
+SK_DEFINE_INST_COUNT(SkLayerDrawLooper)
+
 SkLayerDrawLooper::LayerInfo::LayerInfo() {
     fFlagsMask = 0;                     // ignore our paint flags
     fPaintBits = 0;                     // ignore our paint fields
@@ -180,7 +185,7 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkLayerDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkLayerDrawLooper::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
 #ifdef SK_DEBUG
@@ -196,15 +201,15 @@
 #endif
 
     buffer.writeInt(fCount);
-    
+
     Rec* rec = fRecs;
     for (int i = 0; i < fCount; i++) {
+        buffer.writeInt(rec->fInfo.fFlagsMask);
         buffer.writeInt(rec->fInfo.fPaintBits);
         buffer.writeInt(rec->fInfo.fColorMode);
-        buffer.writeScalar(rec->fInfo.fOffset.fX);
-        buffer.writeScalar(rec->fInfo.fOffset.fY);
+        buffer.writePoint(rec->fInfo.fOffset);
         buffer.writeBool(rec->fInfo.fPostTranslate);
-        rec->fPaint.flatten(buffer);
+        buffer.writePaint(rec->fPaint);
         rec = rec->fNext;
     }
 }
@@ -218,20 +223,18 @@
 
     for (int i = 0; i < count; i++) {
         LayerInfo info;
-        if (buffer.getPictureVersion() == PICTURE_VERSION_ICS)
-            info.fFlagsMask = buffer.readInt();
+        info.fFlagsMask = buffer.readInt();
         info.fPaintBits = buffer.readInt();
         info.fColorMode = (SkXfermode::Mode)buffer.readInt();
-        info.fOffset.fX = buffer.readScalar();
-        info.fOffset.fY = buffer.readScalar();
+        buffer.readPoint(&info.fOffset);
         info.fPostTranslate = buffer.readBool();
-        this->addLayer(info)->unflatten(buffer);
+        buffer.readPaint(this->addLayer(info));
     }
     SkASSERT(count == fCount);
 
     // we're in reverse order, so fix it now
     fRecs = Rec::Reverse(fRecs);
-    
+
 #ifdef SK_DEBUG
     {
         Rec* rec = fRecs;
@@ -245,6 +248,98 @@
 #endif
 }
 
-///////////////////////////////////////////////////////////////////////////////
+#ifdef SK_DEVELOPER
+void SkLayerDrawLooper::toString(SkString* str) const {
+    str->appendf("SkLayerDrawLooper (%d): ", fCount);
 
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkLayerDrawLooper)
+    Rec* rec = fRecs;
+    for (int i = 0; i < fCount; i++) {
+        str->appendf("%d: ", i);
+
+        str->append("flagsMask: (");
+        if (0 == rec->fInfo.fFlagsMask) {
+            str->append("None");
+        } else {
+            bool needSeparator = false;
+            SkAddFlagToString(str, SkToBool(SkPaint::kAntiAlias_Flag & rec->fInfo.fFlagsMask),
+                              "AntiAlias", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kFilterBitmap_Flag & rec->fInfo.fFlagsMask),
+                              "FilterBitmap", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kDither_Flag & rec->fInfo.fFlagsMask),
+                              "Dither", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kUnderlineText_Flag & rec->fInfo.fFlagsMask),
+                              "UnderlineText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kStrikeThruText_Flag & rec->fInfo.fFlagsMask),
+                              "StrikeThruText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kFakeBoldText_Flag & rec->fInfo.fFlagsMask),
+                              "FakeBoldText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kLinearText_Flag & rec->fInfo.fFlagsMask),
+                              "LinearText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kSubpixelText_Flag & rec->fInfo.fFlagsMask),
+                              "SubpixelText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kDevKernText_Flag & rec->fInfo.fFlagsMask),
+                              "DevKernText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kLCDRenderText_Flag & rec->fInfo.fFlagsMask),
+                              "LCDRenderText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kEmbeddedBitmapText_Flag & rec->fInfo.fFlagsMask),
+                              "EmbeddedBitmapText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kAutoHinting_Flag & rec->fInfo.fFlagsMask),
+                              "Autohinted", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kVerticalText_Flag & rec->fInfo.fFlagsMask),
+                              "VerticalText", &needSeparator);
+            SkAddFlagToString(str, SkToBool(SkPaint::kGenA8FromLCD_Flag & rec->fInfo.fFlagsMask),
+                              "GenA8FromLCD", &needSeparator);
+        }
+        str->append(") ");
+
+        str->append("paintBits: (");
+        if (0 == rec->fInfo.fPaintBits) {
+            str->append("None");
+        } else if (kEntirePaint_Bits == rec->fInfo.fPaintBits) {
+            str->append("EntirePaint");
+        } else {
+            bool needSeparator = false;
+            SkAddFlagToString(str, SkToBool(kStyle_Bit & rec->fInfo.fPaintBits), "Style",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kTextSkewX_Bit & rec->fInfo.fPaintBits), "TextSkewX",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kPathEffect_Bit & rec->fInfo.fPaintBits), "PathEffect",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kMaskFilter_Bit & rec->fInfo.fPaintBits), "MaskFilter",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kShader_Bit & rec->fInfo.fPaintBits), "Shader",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kColorFilter_Bit & rec->fInfo.fPaintBits), "ColorFilter",
+                              &needSeparator);
+            SkAddFlagToString(str, SkToBool(kXfermode_Bit & rec->fInfo.fPaintBits), "Xfermode",
+                              &needSeparator);
+        }
+        str->append(") ");
+
+        static const char* gModeStrings[SkXfermode::kLastMode+1] = {
+            "kClear", "kSrc", "kDst", "kSrcOver", "kDstOver", "kSrcIn", "kDstIn",
+            "kSrcOut", "kDstOut", "kSrcATop", "kDstATop", "kXor", "kPlus",
+            "kMultiply", "kScreen", "kOverlay", "kDarken", "kLighten", "kColorDodge",
+            "kColorBurn", "kHardLight", "kSoftLight", "kDifference", "kExclusion"
+        };
+
+        str->appendf("mode: %s ", gModeStrings[rec->fInfo.fColorMode]);
+
+        str->append("offset: (");
+        str->appendScalar(rec->fInfo.fOffset.fX);
+        str->append(", ");
+        str->appendScalar(rec->fInfo.fOffset.fY);
+        str->append(") ");
+
+        str->append("postTranslate: ");
+        if (rec->fInfo.fPostTranslate) {
+            str->append("true ");
+        } else {
+            str->append("false ");
+        }
+
+        // TODO: add "rec->fPaint.toString(str);" when SkPaint::toString is added
+        rec = rec->fNext;
+    }
+}
+#endif
diff --git a/src/effects/SkLayerRasterizer.cpp b/src/effects/SkLayerRasterizer.cpp
index 9b29550..ea5808c 100644
--- a/src/effects/SkLayerRasterizer.cpp
+++ b/src/effects/SkLayerRasterizer.cpp
@@ -8,12 +8,13 @@
 
 
 #include "SkLayerRasterizer.h"
-#include "SkBuffer.h"
 #include "SkDraw.h"
+#include "SkFlattenableBuffers.h"
 #include "SkMask.h"
 #include "SkMaskFilter.h"
 #include "SkPaint.h"
 #include "SkPath.h"
+#include "SkPathEffect.h"
 #include "../core/SkRasterClip.h"
 #include "SkXfermode.h"
 #include <new>
@@ -39,7 +40,7 @@
                                  SkScalar dy) {
     SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back();
 
-    new (&rec->fPaint) SkPaint(paint);
+    SkNEW_PLACEMENT_ARGS(&rec->fPaint, SkPaint, (paint));
     rec->fOffset.set(dx, dy);
 }
 
@@ -74,7 +75,8 @@
         SkMask  mask;
         if (!SkDraw::DrawToMask(devPath, clipBounds, paint.getMaskFilter(),
                                 &matrix, &mask,
-                                SkMask::kJustComputeBounds_CreateMode)) {
+                                SkMask::kJustComputeBounds_CreateMode,
+                                SkPaint::kFill_Style)) {
             return false;
         }
 
@@ -85,7 +87,7 @@
 
 bool SkLayerRasterizer::onRasterize(const SkPath& path, const SkMatrix& matrix,
                                     const SkIRect* clipBounds,
-                                    SkMask* mask, SkMask::CreateMode mode) {
+                                    SkMask* mask, SkMask::CreateMode mode) const {
     if (fLayers.empty()) {
         return false;
     }
@@ -141,88 +143,29 @@
     return true;
 }
 
-/////////// Routines for flattening /////////////////
-
-static void paint_read(SkPaint* paint, SkFlattenableReadBuffer& buffer) {
-    paint->setAntiAlias(buffer.readBool());
-    paint->setStyle((SkPaint::Style)buffer.readU8());
-    paint->setAlpha(buffer.readU8());
-
-    if (paint->getStyle() != SkPaint::kFill_Style) {
-        paint->setStrokeWidth(buffer.readScalar());
-        paint->setStrokeMiter(buffer.readScalar());
-        paint->setStrokeCap((SkPaint::Cap)buffer.readU8());
-        paint->setStrokeJoin((SkPaint::Join)buffer.readU8());
-    }
-
-    SkSafeUnref(paint->setMaskFilter((SkMaskFilter*)buffer.readFlattenable()));
-    SkSafeUnref(paint->setPathEffect((SkPathEffect*)buffer.readFlattenable()));
-    SkSafeUnref(paint->setRasterizer((SkRasterizer*)buffer.readFlattenable()));
-    SkSafeUnref(paint->setXfermode((SkXfermode*)buffer.readFlattenable()));
-}
-
-static void paint_write(const SkPaint& paint, SkFlattenableWriteBuffer& buffer) {
-    buffer.writeBool(paint.isAntiAlias());
-    buffer.write8(paint.getStyle());
-    buffer.write8(paint.getAlpha());
-
-    if (paint.getStyle() != SkPaint::kFill_Style) {
-        buffer.writeScalar(paint.getStrokeWidth());
-        buffer.writeScalar(paint.getStrokeMiter());
-        buffer.write8(paint.getStrokeCap());
-        buffer.write8(paint.getStrokeJoin());
-    }
-
-    buffer.writeFlattenable(paint.getMaskFilter());
-    buffer.writeFlattenable(paint.getPathEffect());
-    buffer.writeFlattenable(paint.getRasterizer());
-    buffer.writeFlattenable(paint.getXfermode());
-}
-
 SkLayerRasterizer::SkLayerRasterizer(SkFlattenableReadBuffer& buffer)
     : SkRasterizer(buffer), fLayers(sizeof(SkLayerRasterizer_Rec)) {
-    int count = buffer.readS32();
+    int count = buffer.readInt();
 
     for (int i = 0; i < count; i++) {
         SkLayerRasterizer_Rec* rec = (SkLayerRasterizer_Rec*)fLayers.push_back();
 
-#if 0
-        new (&rec->fPaint) SkPaint(buffer);
-#else
-        new (&rec->fPaint) SkPaint;
-        paint_read(&rec->fPaint, buffer);
-#endif
-        rec->fOffset.fX = buffer.readScalar();
-        rec->fOffset.fY = buffer.readScalar();
+        SkNEW_PLACEMENT(&rec->fPaint, SkPaint);
+        buffer.readPaint(&rec->fPaint);
+        buffer.readPoint(&rec->fOffset);
     }
 }
 
-void SkLayerRasterizer::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkLayerRasterizer::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    buffer.write32(fLayers.count());
+    buffer.writeInt(fLayers.count());
 
     SkDeque::F2BIter                iter(fLayers);
     const SkLayerRasterizer_Rec*    rec;
 
     while ((rec = (const SkLayerRasterizer_Rec*)iter.next()) != NULL) {
-#if 0
-        rec->fPaint.flatten(buffer);
-#else
-        paint_write(rec->fPaint, buffer);
-#endif
-        buffer.writeScalar(rec->fOffset.fX);
-        buffer.writeScalar(rec->fOffset.fY);
+        buffer.writePaint(rec->fPaint);
+        buffer.writePoint(rec->fOffset);
     }
 }
-
-SkFlattenable* SkLayerRasterizer::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkLayerRasterizer, (buffer));
-}
-
-SkFlattenable::Factory SkLayerRasterizer::getFactory() {
-    return CreateProc;
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkLayerRasterizer)
-
diff --git a/src/effects/SkLightingImageFilter.cpp b/src/effects/SkLightingImageFilter.cpp
new file mode 100644
index 0000000..498c8e0
--- /dev/null
+++ b/src/effects/SkLightingImageFilter.cpp
@@ -0,0 +1,1480 @@
+/*
+ * Copyright 2012 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 "SkLightingImageFilter.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+#include "effects/GrSingleTextureEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "GrEffect.h"
+#include "GrTBackendEffectFactory.h"
+
+class GrGLDiffuseLightingEffect;
+class GrGLSpecularLightingEffect;
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+#endif
+
+namespace {
+
+const SkScalar gOneThird = SkScalarInvert(SkIntToScalar(3));
+const SkScalar gTwoThirds = SkScalarDiv(SkIntToScalar(2), SkIntToScalar(3));
+const SkScalar gOneHalf = SkFloatToScalar(0.5f);
+const SkScalar gOneQuarter = SkFloatToScalar(0.25f);
+
+#if SK_SUPPORT_GPU
+void setUniformPoint3(const GrGLUniformManager& uman, UniformHandle uni, const SkPoint3& point) {
+    GR_STATIC_ASSERT(sizeof(SkPoint3) == 3 * sizeof(GrGLfloat));
+    uman.set3fv(uni, 0, 1, &point.fX);
+}
+
+void setUniformNormal3(const GrGLUniformManager& uman, UniformHandle uni, const SkPoint3& point) {
+    setUniformPoint3(uman, uni, SkPoint3(point.fX, point.fY, point.fZ));
+}
+#endif
+
+// Shift matrix components to the left, as we advance pixels to the right.
+inline void shiftMatrixLeft(int m[9]) {
+    m[0] = m[1];
+    m[3] = m[4];
+    m[6] = m[7];
+    m[1] = m[2];
+    m[4] = m[5];
+    m[7] = m[8];
+}
+
+class DiffuseLightingType {
+public:
+    DiffuseLightingType(SkScalar kd)
+        : fKD(kd) {}
+    SkPMColor light(const SkPoint3& normal, const SkPoint3& surfaceTolight, const SkPoint3& lightColor) const {
+        SkScalar colorScale = SkScalarMul(fKD, normal.dot(surfaceTolight));
+        colorScale = SkScalarClampMax(colorScale, SK_Scalar1);
+        SkPoint3 color(lightColor * colorScale);
+        return SkPackARGB32(255,
+                            SkScalarFloorToInt(color.fX),
+                            SkScalarFloorToInt(color.fY),
+                            SkScalarFloorToInt(color.fZ));
+    }
+private:
+    SkScalar fKD;
+};
+
+class SpecularLightingType {
+public:
+    SpecularLightingType(SkScalar ks, SkScalar shininess)
+        : fKS(ks), fShininess(shininess) {}
+    SkPMColor light(const SkPoint3& normal, const SkPoint3& surfaceTolight, const SkPoint3& lightColor) const {
+        SkPoint3 halfDir(surfaceTolight);
+        halfDir.fZ += SK_Scalar1;        // eye position is always (0, 0, 1)
+        halfDir.normalize();
+        SkScalar colorScale = SkScalarMul(fKS,
+            SkScalarPow(normal.dot(halfDir), fShininess));
+        colorScale = SkScalarClampMax(colorScale, SK_Scalar1);
+        SkPoint3 color(lightColor * colorScale);
+        return SkPackARGB32(SkScalarFloorToInt(color.maxComponent()),
+                            SkScalarFloorToInt(color.fX),
+                            SkScalarFloorToInt(color.fY),
+                            SkScalarFloorToInt(color.fZ));
+    }
+private:
+    SkScalar fKS;
+    SkScalar fShininess;
+};
+
+inline SkScalar sobel(int a, int b, int c, int d, int e, int f, SkScalar scale) {
+    return SkScalarMul(SkIntToScalar(-a + b - 2 * c + 2 * d -e + f), scale);
+}
+
+inline SkPoint3 pointToNormal(SkScalar x, SkScalar y, SkScalar surfaceScale) {
+    SkPoint3 vector(SkScalarMul(-x, surfaceScale),
+                    SkScalarMul(-y, surfaceScale),
+                    SK_Scalar1);
+    vector.normalize();
+    return vector;
+}
+
+inline SkPoint3 topLeftNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(0, 0, m[4], m[5], m[7], m[8], gTwoThirds),
+                         sobel(0, 0, m[4], m[7], m[5], m[8], gTwoThirds),
+                         surfaceScale);
+}
+
+inline SkPoint3 topNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(   0,    0, m[3], m[5], m[6], m[8], gOneThird),
+                         sobel(m[3], m[6], m[4], m[7], m[5], m[8], gOneHalf),
+                         surfaceScale);
+}
+
+inline SkPoint3 topRightNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(   0,    0, m[3], m[4], m[6], m[7], gTwoThirds),
+                         sobel(m[3], m[6], m[4], m[7],    0,    0, gTwoThirds),
+                         surfaceScale);
+}
+
+inline SkPoint3 leftNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(m[1], m[2], m[4], m[5], m[7], m[8], gOneHalf),
+                         sobel(   0,    0, m[1], m[7], m[2], m[8], gOneThird),
+                         surfaceScale);
+}
+
+
+inline SkPoint3 interiorNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(m[0], m[2], m[3], m[5], m[6], m[8], gOneQuarter),
+                         sobel(m[0], m[6], m[1], m[7], m[2], m[8], gOneQuarter),
+                         surfaceScale);
+}
+
+inline SkPoint3 rightNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(m[0], m[1], m[3], m[4], m[6], m[7], gOneHalf),
+                         sobel(m[0], m[6], m[1], m[7],    0,    0, gOneThird),
+                         surfaceScale);
+}
+
+inline SkPoint3 bottomLeftNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(m[1], m[2], m[4], m[5],    0,    0, gTwoThirds),
+                         sobel(   0,    0, m[1], m[4], m[2], m[5], gTwoThirds),
+                         surfaceScale);
+}
+
+inline SkPoint3 bottomNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(m[0], m[2], m[3], m[5],    0,    0, gOneThird),
+                         sobel(m[0], m[3], m[1], m[4], m[2], m[5], gOneHalf),
+                         surfaceScale);
+}
+
+inline SkPoint3 bottomRightNormal(int m[9], SkScalar surfaceScale) {
+    return pointToNormal(sobel(m[0], m[1], m[3], m[4], 0,  0, gTwoThirds),
+                         sobel(m[0], m[3], m[1], m[4], 0,  0, gTwoThirds),
+                         surfaceScale);
+}
+
+template <class LightingType, class LightType> void lightBitmap(const LightingType& lightingType, const SkLight* light, const SkBitmap& src, SkBitmap* dst, SkScalar surfaceScale) {
+    const LightType* l = static_cast<const LightType*>(light);
+    int y = 0;
+    {
+        const SkPMColor* row1 = src.getAddr32(0, 0);
+        const SkPMColor* row2 = src.getAddr32(0, 1);
+        SkPMColor* dptr = dst->getAddr32(0, 0);
+        int m[9];
+        int x = 0;
+        m[4] = SkGetPackedA32(*row1++);
+        m[5] = SkGetPackedA32(*row1++);
+        m[7] = SkGetPackedA32(*row2++);
+        m[8] = SkGetPackedA32(*row2++);
+        SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+        *dptr++ = lightingType.light(topLeftNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+        for (x = 1; x < src.width() - 1; ++x)
+        {
+            shiftMatrixLeft(m);
+            m[5] = SkGetPackedA32(*row1++);
+            m[8] = SkGetPackedA32(*row2++);
+            surfaceToLight = l->surfaceToLight(x, 0, m[4], surfaceScale);
+            *dptr++ = lightingType.light(topNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+        }
+        shiftMatrixLeft(m);
+        surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+        *dptr++ = lightingType.light(topRightNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+    }
+
+    for (++y; y < src.height() - 1; ++y) {
+        const SkPMColor* row0 = src.getAddr32(0, y - 1);
+        const SkPMColor* row1 = src.getAddr32(0, y);
+        const SkPMColor* row2 = src.getAddr32(0, y + 1);
+        SkPMColor* dptr = dst->getAddr32(0, y);
+        int m[9];
+        int x = 0;
+        m[1] = SkGetPackedA32(*row0++);
+        m[2] = SkGetPackedA32(*row0++);
+        m[4] = SkGetPackedA32(*row1++);
+        m[5] = SkGetPackedA32(*row1++);
+        m[7] = SkGetPackedA32(*row2++);
+        m[8] = SkGetPackedA32(*row2++);
+        SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+        *dptr++ = lightingType.light(leftNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+        for (x = 1; x < src.width() - 1; ++x) {
+            shiftMatrixLeft(m);
+            m[2] = SkGetPackedA32(*row0++);
+            m[5] = SkGetPackedA32(*row1++);
+            m[8] = SkGetPackedA32(*row2++);
+            surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+            *dptr++ = lightingType.light(interiorNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+        }
+        shiftMatrixLeft(m);
+        surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+        *dptr++ = lightingType.light(rightNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+    }
+
+    {
+        const SkPMColor* row0 = src.getAddr32(0, src.height() - 2);
+        const SkPMColor* row1 = src.getAddr32(0, src.height() - 1);
+        int x = 0;
+        SkPMColor* dptr = dst->getAddr32(0, src.height() - 1);
+        int m[9];
+        m[1] = SkGetPackedA32(*row0++);
+        m[2] = SkGetPackedA32(*row0++);
+        m[4] = SkGetPackedA32(*row1++);
+        m[5] = SkGetPackedA32(*row1++);
+        SkPoint3 surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+        *dptr++ = lightingType.light(bottomLeftNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+        for (x = 1; x < src.width() - 1; ++x)
+        {
+            shiftMatrixLeft(m);
+            m[2] = SkGetPackedA32(*row0++);
+            m[5] = SkGetPackedA32(*row1++);
+            surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+            *dptr++ = lightingType.light(bottomNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+        }
+        shiftMatrixLeft(m);
+        surfaceToLight = l->surfaceToLight(x, y, m[4], surfaceScale);
+        *dptr++ = lightingType.light(bottomRightNormal(m, surfaceScale), surfaceToLight, l->lightColor(surfaceToLight));
+    }
+}
+
+SkPoint3 readPoint3(SkFlattenableReadBuffer& buffer) {
+    SkPoint3 point;
+    point.fX = buffer.readScalar();
+    point.fY = buffer.readScalar();
+    point.fZ = buffer.readScalar();
+    return point;
+};
+
+void writePoint3(const SkPoint3& point, SkFlattenableWriteBuffer& buffer) {
+    buffer.writeScalar(point.fX);
+    buffer.writeScalar(point.fY);
+    buffer.writeScalar(point.fZ);
+};
+
+class SkDiffuseLightingImageFilter : public SkLightingImageFilter {
+public:
+    SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale,
+                                 SkScalar kd, SkImageFilter* input);
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDiffuseLightingImageFilter)
+
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture*) const SK_OVERRIDE;
+    SkScalar kd() const { return fKD; }
+
+protected:
+    explicit SkDiffuseLightingImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+
+
+private:
+    typedef SkLightingImageFilter INHERITED;
+    SkScalar fKD;
+};
+
+class SkSpecularLightingImageFilter : public SkLightingImageFilter {
+public:
+    SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, SkImageFilter* input);
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpecularLightingImageFilter)
+
+    virtual bool asNewEffect(GrEffectRef** effect, GrTexture*) const SK_OVERRIDE;
+    SkScalar ks() const { return fKS; }
+    SkScalar shininess() const { return fShininess; }
+
+protected:
+    explicit SkSpecularLightingImageFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+    virtual bool onFilterImage(Proxy*, const SkBitmap& src, const SkMatrix&,
+                               SkBitmap* result, SkIPoint* offset) SK_OVERRIDE;
+
+private:
+    typedef SkLightingImageFilter INHERITED;
+    SkScalar fKS;
+    SkScalar fShininess;
+};
+
+#if SK_SUPPORT_GPU
+
+class GrLightingEffect : public GrSingleTextureEffect {
+public:
+    GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale);
+    virtual ~GrLightingEffect();
+
+    const SkLight* light() const { return fLight; }
+    SkScalar surfaceScale() const { return fSurfaceScale; }
+
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // lighting shaders are complicated. We just throw up our hands.
+        *validFlags = 0;
+    }
+
+protected:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+private:
+    typedef GrSingleTextureEffect INHERITED;
+    const SkLight* fLight;
+    SkScalar fSurfaceScale;
+};
+
+class GrDiffuseLightingEffect : public GrLightingEffect {
+public:
+    static GrEffectRef* Create(GrTexture* texture,
+                               const SkLight* light,
+                               SkScalar surfaceScale,
+                               SkScalar kd) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrDiffuseLightingEffect, (texture,
+                                                                    light,
+                                                                    surfaceScale,
+                                                                    kd)));
+        return CreateEffectRef(effect);
+    }
+
+    static const char* Name() { return "DiffuseLighting"; }
+
+    typedef GrGLDiffuseLightingEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    SkScalar kd() const { return fKD; }
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrDiffuseLightingEffect(GrTexture* texture,
+                            const SkLight* light,
+                            SkScalar surfaceScale,
+                            SkScalar kd);
+
+    GR_DECLARE_EFFECT_TEST;
+    typedef GrLightingEffect INHERITED;
+    SkScalar fKD;
+};
+
+class GrSpecularLightingEffect : public GrLightingEffect {
+public:
+    static GrEffectRef* Create(GrTexture* texture,
+                               const SkLight* light,
+                               SkScalar surfaceScale,
+                               SkScalar ks,
+                               SkScalar shininess) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSpecularLightingEffect, (texture,
+                                                                     light,
+                                                                     surfaceScale,
+                                                                     ks,
+                                                                     shininess)));
+        return CreateEffectRef(effect);
+    }
+    static const char* Name() { return "SpecularLighting"; }
+
+    typedef GrGLSpecularLightingEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    SkScalar ks() const { return fKS; }
+    SkScalar shininess() const { return fShininess; }
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrSpecularLightingEffect(GrTexture* texture,
+                             const SkLight* light,
+                             SkScalar surfaceScale,
+                             SkScalar ks,
+                             SkScalar shininess);
+
+    GR_DECLARE_EFFECT_TEST;
+    typedef GrLightingEffect INHERITED;
+    SkScalar fKS;
+    SkScalar fShininess;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLLight {
+public:
+    virtual ~GrGLLight() {}
+
+    /**
+     * This is called by GrGLLightingEffect::emitCode() before either of the two virtual functions
+     * below. It adds a vec3f uniform visible in the FS that represents the constant light color.
+     */
+    void emitLightColorUniform(GrGLShaderBuilder*);
+
+    /**
+     * These two functions are called from GrGLLightingEffect's emitCode() function.
+     * emitSurfaceToLight places an expression in param out that is the vector from the surface to
+     * the light. The expression will be used in the FS. emitLightColor writes an expression into
+     * the FS that is the color of the light. Either function may add functions and/or uniforms to
+     * the FS. The default of emitLightColor appends the name of the constant light color uniform
+     * and so this function only needs to be overridden if the light color varies spatially.
+     */
+    virtual void emitSurfaceToLight(GrGLShaderBuilder*, SkString* out, const char* z) = 0;
+    virtual void emitLightColor(GrGLShaderBuilder*, const char *surfaceToLight);
+
+    // This is called from GrGLLightingEffect's setData(). Subclasses of GrGLLight must call
+    // INHERITED::setData().
+    virtual void setData(const GrGLUniformManager&, const SkLight* light) const;
+
+protected:
+    /**
+     * Gets the constant light color uniform. Subclasses can use this in their emitLightColor
+     * function.
+     */
+    UniformHandle lightColorUni() const { return fColorUni; }
+
+private:
+    UniformHandle fColorUni;
+
+    typedef SkRefCnt INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLDistantLight : public GrGLLight {
+public:
+    virtual ~GrGLDistantLight() {}
+    virtual void setData(const GrGLUniformManager&, const SkLight* light) const SK_OVERRIDE;
+    virtual void emitSurfaceToLight(GrGLShaderBuilder*, SkString* out,  const char* z) SK_OVERRIDE;
+private:
+    typedef GrGLLight INHERITED;
+    UniformHandle fDirectionUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLPointLight : public GrGLLight {
+public:
+    virtual ~GrGLPointLight() {}
+    virtual void setData(const GrGLUniformManager&, const SkLight* light) const SK_OVERRIDE;
+    virtual void emitSurfaceToLight(GrGLShaderBuilder*, SkString* out, const char* z) SK_OVERRIDE;
+private:
+    typedef GrGLLight INHERITED;
+    SkPoint3 fLocation;
+    UniformHandle fLocationUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLSpotLight : public GrGLLight {
+public:
+    virtual ~GrGLSpotLight() {}
+    virtual void setData(const GrGLUniformManager&, const SkLight* light) const SK_OVERRIDE;
+    virtual void emitSurfaceToLight(GrGLShaderBuilder*, SkString* out, const char* z) SK_OVERRIDE;
+    virtual void emitLightColor(GrGLShaderBuilder*, const char *surfaceToLight) SK_OVERRIDE;
+private:
+    typedef GrGLLight INHERITED;
+
+    SkString        fLightColorFunc;
+    UniformHandle   fLocationUni;
+    UniformHandle   fExponentUni;
+    UniformHandle   fCosOuterConeAngleUni;
+    UniformHandle   fCosInnerConeAngleUni;
+    UniformHandle   fConeScaleUni;
+    UniformHandle   fSUni;
+};
+#else
+
+class GrGLLight;
+
+#endif
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkLight : public SkFlattenable {
+public:
+    SK_DECLARE_INST_COUNT(SkLight)
+
+    enum LightType {
+        kDistant_LightType,
+        kPoint_LightType,
+        kSpot_LightType,
+    };
+    virtual LightType type() const = 0;
+    const SkPoint3& color() const { return fColor; }
+    virtual GrGLLight* createGLLight() const = 0;
+    virtual bool isEqual(const SkLight& other) const {
+        return fColor == other.fColor;
+    }
+
+protected:
+    SkLight(SkColor color)
+      : fColor(SkIntToScalar(SkColorGetR(color)),
+               SkIntToScalar(SkColorGetG(color)),
+               SkIntToScalar(SkColorGetB(color))) {}
+    SkLight(SkFlattenableReadBuffer& buffer)
+      : INHERITED(buffer) {
+        fColor = readPoint3(buffer);
+    }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE {
+        INHERITED::flatten(buffer);
+        writePoint3(fColor, buffer);
+    }
+
+private:
+    typedef SkFlattenable INHERITED;
+    SkPoint3 fColor;
+};
+
+SK_DEFINE_INST_COUNT(SkLight)
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkDistantLight : public SkLight {
+public:
+    SkDistantLight(const SkPoint3& direction, SkColor color)
+      : INHERITED(color), fDirection(direction) {
+    }
+
+    SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const {
+        return fDirection;
+    };
+    SkPoint3 lightColor(const SkPoint3&) const { return color(); }
+    virtual LightType type() const { return kDistant_LightType; }
+    const SkPoint3& direction() const { return fDirection; }
+    virtual GrGLLight* createGLLight() const SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        return SkNEW(GrGLDistantLight);
+#else
+        SkDEBUGFAIL("Should not call in GPU-less build");
+        return NULL;
+#endif
+    }
+    virtual bool isEqual(const SkLight& other) const SK_OVERRIDE {
+        if (other.type() != kDistant_LightType) {
+            return false;
+        }
+
+        const SkDistantLight& o = static_cast<const SkDistantLight&>(other);
+        return INHERITED::isEqual(other) &&
+               fDirection == o.fDirection;
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDistantLight)
+
+protected:
+    SkDistantLight(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fDirection = readPoint3(buffer);
+    }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const {
+        INHERITED::flatten(buffer);
+        writePoint3(fDirection, buffer);
+    }
+
+private:
+    typedef SkLight INHERITED;
+    SkPoint3 fDirection;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkPointLight : public SkLight {
+public:
+    SkPointLight(const SkPoint3& location, SkColor color)
+     : INHERITED(color), fLocation(location) {}
+
+    SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const {
+        SkPoint3 direction(fLocation.fX - SkIntToScalar(x),
+                           fLocation.fY - SkIntToScalar(y),
+                           fLocation.fZ - SkScalarMul(SkIntToScalar(z), surfaceScale));
+        direction.normalize();
+        return direction;
+    };
+    SkPoint3 lightColor(const SkPoint3&) const { return color(); }
+    virtual LightType type() const { return kPoint_LightType; }
+    const SkPoint3& location() const { return fLocation; }
+    virtual GrGLLight* createGLLight() const SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        return SkNEW(GrGLPointLight);
+#else
+        SkDEBUGFAIL("Should not call in GPU-less build");
+        return NULL;
+#endif
+    }
+    virtual bool isEqual(const SkLight& other) const SK_OVERRIDE {
+        if (other.type() != kPoint_LightType) {
+            return false;
+        }
+        const SkPointLight& o = static_cast<const SkPointLight&>(other);
+        return INHERITED::isEqual(other) &&
+               fLocation == o.fLocation;
+    }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkPointLight)
+
+protected:
+    SkPointLight(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fLocation = readPoint3(buffer);
+    }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const {
+        INHERITED::flatten(buffer);
+        writePoint3(fLocation, buffer);
+    }
+
+private:
+    typedef SkLight INHERITED;
+    SkPoint3 fLocation;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkSpotLight : public SkLight {
+public:
+    SkSpotLight(const SkPoint3& location, const SkPoint3& target, SkScalar specularExponent, SkScalar cutoffAngle, SkColor color)
+     : INHERITED(color),
+       fLocation(location),
+       fTarget(target),
+       fSpecularExponent(specularExponent)
+    {
+       fS = target - location;
+       fS.normalize();
+       fCosOuterConeAngle = SkScalarCos(SkDegreesToRadians(cutoffAngle));
+       const SkScalar antiAliasThreshold = SkFloatToScalar(0.016f);
+       fCosInnerConeAngle = fCosOuterConeAngle + antiAliasThreshold;
+       fConeScale = SkScalarInvert(antiAliasThreshold);
+    }
+
+    SkPoint3 surfaceToLight(int x, int y, int z, SkScalar surfaceScale) const {
+        SkPoint3 direction(fLocation.fX - SkIntToScalar(x),
+                           fLocation.fY - SkIntToScalar(y),
+                           fLocation.fZ - SkScalarMul(SkIntToScalar(z), surfaceScale));
+        direction.normalize();
+        return direction;
+    };
+    SkPoint3 lightColor(const SkPoint3& surfaceToLight) const {
+        SkScalar cosAngle = -surfaceToLight.dot(fS);
+        if (cosAngle < fCosOuterConeAngle) {
+            return SkPoint3(0, 0, 0);
+        }
+        SkScalar scale = SkScalarPow(cosAngle, fSpecularExponent);
+        if (cosAngle < fCosInnerConeAngle) {
+            scale = SkScalarMul(scale, cosAngle - fCosOuterConeAngle);
+            return color() * SkScalarMul(scale, fConeScale);
+        }
+        return color() * scale;
+    }
+    virtual GrGLLight* createGLLight() const SK_OVERRIDE {
+#if SK_SUPPORT_GPU
+        return SkNEW(GrGLSpotLight);
+#else
+        SkDEBUGFAIL("Should not call in GPU-less build");
+        return NULL;
+#endif
+    }
+    virtual LightType type() const { return kSpot_LightType; }
+    const SkPoint3& location() const { return fLocation; }
+    const SkPoint3& target() const { return fTarget; }
+    SkScalar specularExponent() const { return fSpecularExponent; }
+    SkScalar cosInnerConeAngle() const { return fCosInnerConeAngle; }
+    SkScalar cosOuterConeAngle() const { return fCosOuterConeAngle; }
+    SkScalar coneScale() const { return fConeScale; }
+    const SkPoint3& s() const { return fS; }
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSpotLight)
+
+protected:
+    SkSpotLight(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+        fLocation = readPoint3(buffer);
+        fTarget = readPoint3(buffer);
+        fSpecularExponent = buffer.readScalar();
+        fCosOuterConeAngle = buffer.readScalar();
+        fCosInnerConeAngle = buffer.readScalar();
+        fConeScale = buffer.readScalar();
+        fS = readPoint3(buffer);
+    }
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const {
+        INHERITED::flatten(buffer);
+        writePoint3(fLocation, buffer);
+        writePoint3(fTarget, buffer);
+        buffer.writeScalar(fSpecularExponent);
+        buffer.writeScalar(fCosOuterConeAngle);
+        buffer.writeScalar(fCosInnerConeAngle);
+        buffer.writeScalar(fConeScale);
+        writePoint3(fS, buffer);
+    }
+
+    virtual bool isEqual(const SkLight& other) const SK_OVERRIDE {
+        if (other.type() != kSpot_LightType) {
+            return false;
+        }
+
+        const SkSpotLight& o = static_cast<const SkSpotLight&>(other);
+        return INHERITED::isEqual(other) &&
+               fLocation == o.fLocation &&
+               fTarget == o.fTarget &&
+               fSpecularExponent == o.fSpecularExponent &&
+               fCosOuterConeAngle == o.fCosOuterConeAngle;
+    }
+
+private:
+    typedef SkLight INHERITED;
+    SkPoint3 fLocation;
+    SkPoint3 fTarget;
+    SkScalar fSpecularExponent;
+    SkScalar fCosOuterConeAngle;
+    SkScalar fCosInnerConeAngle;
+    SkScalar fConeScale;
+    SkPoint3 fS;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkLightingImageFilter::SkLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkImageFilter* input)
+  : INHERITED(input),
+    fLight(light),
+    fSurfaceScale(SkScalarDiv(surfaceScale, SkIntToScalar(255)))
+{
+    SkASSERT(fLight);
+    // our caller knows that we take ownership of the light, so we don't
+    // need to call ref() here.
+}
+
+SkImageFilter* SkLightingImageFilter::CreateDistantLitDiffuse(
+    const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale,
+    SkScalar kd, SkImageFilter* input) {
+    return SkNEW_ARGS(SkDiffuseLightingImageFilter,
+        (SkNEW_ARGS(SkDistantLight, (direction, lightColor)), surfaceScale, kd,
+        input));
+}
+
+SkImageFilter* SkLightingImageFilter::CreatePointLitDiffuse(
+    const SkPoint3& location, SkColor lightColor, SkScalar surfaceScale,
+    SkScalar kd, SkImageFilter* input) {
+    return SkNEW_ARGS(SkDiffuseLightingImageFilter,
+        (SkNEW_ARGS(SkPointLight, (location, lightColor)), surfaceScale, kd,
+        input));
+}
+
+SkImageFilter* SkLightingImageFilter::CreateSpotLitDiffuse(
+    const SkPoint3& location, const SkPoint3& target,
+    SkScalar specularExponent, SkScalar cutoffAngle,
+    SkColor lightColor, SkScalar surfaceScale, SkScalar kd,
+    SkImageFilter* input) {
+    return SkNEW_ARGS(SkDiffuseLightingImageFilter,
+        (SkNEW_ARGS(SkSpotLight, (location, target, specularExponent,
+                                  cutoffAngle, lightColor)),
+                    surfaceScale, kd, input));
+}
+
+SkImageFilter* SkLightingImageFilter::CreateDistantLitSpecular(
+    const SkPoint3& direction, SkColor lightColor, SkScalar surfaceScale,
+    SkScalar ks, SkScalar shininess, SkImageFilter* input) {
+    return SkNEW_ARGS(SkSpecularLightingImageFilter,
+        (SkNEW_ARGS(SkDistantLight, (direction, lightColor)),
+        surfaceScale, ks, shininess, input));
+}
+
+SkImageFilter* SkLightingImageFilter::CreatePointLitSpecular(
+    const SkPoint3& location, SkColor lightColor, SkScalar surfaceScale,
+    SkScalar ks, SkScalar shininess, SkImageFilter* input) {
+    return SkNEW_ARGS(SkSpecularLightingImageFilter,
+        (SkNEW_ARGS(SkPointLight, (location, lightColor)),
+        surfaceScale, ks, shininess, input));
+}
+
+SkImageFilter* SkLightingImageFilter::CreateSpotLitSpecular(
+    const SkPoint3& location, const SkPoint3& target,
+    SkScalar specularExponent, SkScalar cutoffAngle,
+    SkColor lightColor, SkScalar surfaceScale,
+    SkScalar ks, SkScalar shininess, SkImageFilter* input) {
+    return SkNEW_ARGS(SkSpecularLightingImageFilter,
+        (SkNEW_ARGS(SkSpotLight, (location, target, specularExponent, cutoffAngle, lightColor)),
+        surfaceScale, ks, shininess, input));
+}
+
+SkLightingImageFilter::~SkLightingImageFilter() {
+    fLight->unref();
+}
+
+SkLightingImageFilter::SkLightingImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer)
+{
+    fLight = buffer.readFlattenableT<SkLight>();
+    fSurfaceScale = buffer.readScalar();
+}
+
+void SkLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fLight);
+    buffer.writeScalar(fSurfaceScale);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar kd, SkImageFilter* input)
+  : SkLightingImageFilter(light, surfaceScale, input),
+    fKD(kd)
+{
+}
+
+SkDiffuseLightingImageFilter::SkDiffuseLightingImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer)
+{
+    fKD = buffer.readScalar();
+}
+
+void SkDiffuseLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fKD);
+}
+
+bool SkDiffuseLightingImageFilter::onFilterImage(Proxy*,
+                                                 const SkBitmap& src,
+                                                 const SkMatrix&,
+                                                 SkBitmap* dst,
+                                                 SkIPoint*) {
+    if (src.config() != SkBitmap::kARGB_8888_Config) {
+        return false;
+    }
+    SkAutoLockPixels alp(src);
+    if (!src.getPixels()) {
+        return false;
+    }
+    if (src.width() < 2 || src.height() < 2) {
+        return false;
+    }
+    dst->setConfig(src.config(), src.width(), src.height());
+    dst->allocPixels();
+
+    DiffuseLightingType lightingType(fKD);
+    switch (light()->type()) {
+        case SkLight::kDistant_LightType:
+            lightBitmap<DiffuseLightingType, SkDistantLight>(lightingType, light(), src, dst, surfaceScale());
+            break;
+        case SkLight::kPoint_LightType:
+            lightBitmap<DiffuseLightingType, SkPointLight>(lightingType, light(), src, dst, surfaceScale());
+            break;
+        case SkLight::kSpot_LightType:
+            lightBitmap<DiffuseLightingType, SkSpotLight>(lightingType, light(), src, dst, surfaceScale());
+            break;
+    }
+    return true;
+}
+
+bool SkDiffuseLightingImageFilter::asNewEffect(GrEffectRef** effect,
+                                               GrTexture* texture) const {
+#if SK_SUPPORT_GPU
+    if (effect) {
+        SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
+        *effect = GrDiffuseLightingEffect::Create(texture, light(), scale, kd());
+    }
+    return true;
+#else
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess, SkImageFilter* input)
+  : SkLightingImageFilter(light, surfaceScale, input),
+    fKS(ks),
+    fShininess(shininess)
+{
+}
+
+SkSpecularLightingImageFilter::SkSpecularLightingImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer)
+{
+    fKS = buffer.readScalar();
+    fShininess = buffer.readScalar();
+}
+
+void SkSpecularLightingImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fKS);
+    buffer.writeScalar(fShininess);
+}
+
+bool SkSpecularLightingImageFilter::onFilterImage(Proxy*,
+                                                  const SkBitmap& src,
+                                                  const SkMatrix&,
+                                                  SkBitmap* dst,
+                                                  SkIPoint*) {
+    if (src.config() != SkBitmap::kARGB_8888_Config) {
+        return false;
+    }
+    SkAutoLockPixels alp(src);
+    if (!src.getPixels()) {
+        return false;
+    }
+    if (src.width() < 2 || src.height() < 2) {
+        return false;
+    }
+    dst->setConfig(src.config(), src.width(), src.height());
+    dst->allocPixels();
+
+    SpecularLightingType lightingType(fKS, fShininess);
+    switch (light()->type()) {
+        case SkLight::kDistant_LightType:
+            lightBitmap<SpecularLightingType, SkDistantLight>(lightingType, light(), src, dst, surfaceScale());
+            break;
+        case SkLight::kPoint_LightType:
+            lightBitmap<SpecularLightingType, SkPointLight>(lightingType, light(), src, dst, surfaceScale());
+            break;
+        case SkLight::kSpot_LightType:
+            lightBitmap<SpecularLightingType, SkSpotLight>(lightingType, light(), src, dst, surfaceScale());
+            break;
+    }
+    return true;
+}
+
+bool SkSpecularLightingImageFilter::asNewEffect(GrEffectRef** effect,
+                                                GrTexture* texture) const {
+#if SK_SUPPORT_GPU
+    if (effect) {
+        SkScalar scale = SkScalarMul(surfaceScale(), SkIntToScalar(255));
+        *effect = GrSpecularLightingEffect::Create(texture, light(), scale, ks(), shininess());
+    }
+    return true;
+#else
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return false;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+namespace {
+SkPoint3 random_point3(SkRandom* random) {
+    return SkPoint3(SkScalarToFloat(random->nextSScalar1()),
+                    SkScalarToFloat(random->nextSScalar1()),
+                    SkScalarToFloat(random->nextSScalar1()));
+}
+
+SkLight* create_random_light(SkRandom* random) {
+    int type = random->nextULessThan(3);
+    switch (type) {
+        case 0: {
+            return SkNEW_ARGS(SkDistantLight, (random_point3(random), random->nextU()));
+        }
+        case 1: {
+            return SkNEW_ARGS(SkPointLight, (random_point3(random), random->nextU()));
+        }
+        case 2: {
+            return SkNEW_ARGS(SkSpotLight, (random_point3(random),
+                                            random_point3(random),
+                                            random->nextUScalar1(),
+                                            random->nextUScalar1(),
+                                            random->nextU()));
+        }
+        default:
+            GrCrash();
+            return NULL;
+    }
+}
+
+}
+
+class GrGLLightingEffect  : public GrGLEffect {
+public:
+    GrGLLightingEffect(const GrBackendEffectFactory& factory,
+                       const GrEffectRef& effect);
+    virtual ~GrGLLightingEffect();
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    /**
+     * Subclasses of GrGLLightingEffect must call INHERITED::setData();
+     */
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+protected:
+    virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) = 0;
+
+private:
+    typedef GrGLEffect INHERITED;
+
+    UniformHandle       fImageIncrementUni;
+    UniformHandle       fSurfaceScaleUni;
+    GrGLLight*          fLight;
+    GrGLEffectMatrix    fEffectMatrix;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLDiffuseLightingEffect  : public GrGLLightingEffect {
+public:
+    GrGLDiffuseLightingEffect(const GrBackendEffectFactory& factory,
+                              const GrEffectRef& effect);
+    virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+private:
+    typedef GrGLLightingEffect INHERITED;
+
+    UniformHandle   fKDUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLSpecularLightingEffect  : public GrGLLightingEffect {
+public:
+    GrGLSpecularLightingEffect(const GrBackendEffectFactory& factory,
+                               const GrEffectRef& effect);
+    virtual void emitLightFunc(GrGLShaderBuilder*, SkString* funcName) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+private:
+    typedef GrGLLightingEffect INHERITED;
+
+    UniformHandle   fKSUni;
+    UniformHandle   fShininessUni;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrLightingEffect::GrLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale)
+    : INHERITED(texture, MakeDivByTextureWHMatrix(texture))
+    , fLight(light)
+    , fSurfaceScale(surfaceScale) {
+    fLight->ref();
+}
+
+GrLightingEffect::~GrLightingEffect() {
+    fLight->unref();
+}
+
+bool GrLightingEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrLightingEffect& s = CastEffect<GrLightingEffect>(sBase);
+    return this->texture(0) == s.texture(0) &&
+           fLight->isEqual(*s.fLight) &&
+           fSurfaceScale == s.fSurfaceScale;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrDiffuseLightingEffect::GrDiffuseLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale, SkScalar kd)
+    : INHERITED(texture, light, surfaceScale), fKD(kd) {
+}
+
+const GrBackendEffectFactory& GrDiffuseLightingEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrDiffuseLightingEffect>::getInstance();
+}
+
+bool GrDiffuseLightingEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrDiffuseLightingEffect& s = CastEffect<GrDiffuseLightingEffect>(sBase);
+    return INHERITED::onIsEqual(sBase) &&
+            this->kd() == s.kd();
+}
+
+GR_DEFINE_EFFECT_TEST(GrDiffuseLightingEffect);
+
+GrEffectRef* GrDiffuseLightingEffect::TestCreate(SkRandom* random,
+                                                 GrContext* context,
+                                                 GrTexture* textures[]) {
+    SkScalar surfaceScale = random->nextSScalar1();
+    SkScalar kd = random->nextUScalar1();
+    SkAutoTUnref<SkLight> light(create_random_light(random));
+    return GrDiffuseLightingEffect::Create(textures[GrEffectUnitTest::kAlphaTextureIdx],
+                                           light, surfaceScale, kd);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLLightingEffect::GrGLLightingEffect(const GrBackendEffectFactory& factory,
+                                       const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fImageIncrementUni(kInvalidUniformHandle)
+    , fSurfaceScaleUni(kInvalidUniformHandle) {
+    const GrLightingEffect& m = CastEffect<GrLightingEffect>(effect);
+    fLight = m.light()->createGLLight();
+}
+
+GrGLLightingEffect::~GrGLLightingEffect() {
+    delete fLight;
+}
+
+void GrGLLightingEffect::emitCode(GrGLShaderBuilder* builder,
+                                  const GrEffectStage&,
+                                  EffectKey key,
+                                  const char* vertexCoords,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TextureSamplerArray& samplers) {
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                              kVec2f_GrSLType,
+                                             "ImageIncrement");
+    fSurfaceScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                           kFloat_GrSLType,
+                                           "SurfaceScale");
+    fLight->emitLightColorUniform(builder);
+    SkString* code = &builder->fFSCode;
+    SkString lightFunc;
+    this->emitLightFunc(builder, &lightFunc);
+    static const GrGLShaderVar gSobelArgs[] =  {
+        GrGLShaderVar("a", kFloat_GrSLType),
+        GrGLShaderVar("b", kFloat_GrSLType),
+        GrGLShaderVar("c", kFloat_GrSLType),
+        GrGLShaderVar("d", kFloat_GrSLType),
+        GrGLShaderVar("e", kFloat_GrSLType),
+        GrGLShaderVar("f", kFloat_GrSLType),
+        GrGLShaderVar("scale", kFloat_GrSLType),
+    };
+    SkString sobelFuncName;
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kFloat_GrSLType,
+                          "sobel",
+                          SK_ARRAY_COUNT(gSobelArgs),
+                          gSobelArgs,
+                          "\treturn (-a + b - 2.0 * c + 2.0 * d -e + f) * scale;\n",
+                          &sobelFuncName);
+    static const GrGLShaderVar gPointToNormalArgs[] =  {
+        GrGLShaderVar("x", kFloat_GrSLType),
+        GrGLShaderVar("y", kFloat_GrSLType),
+        GrGLShaderVar("scale", kFloat_GrSLType),
+    };
+    SkString pointToNormalName;
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kVec3f_GrSLType,
+                          "pointToNormal",
+                          SK_ARRAY_COUNT(gPointToNormalArgs),
+                          gPointToNormalArgs,
+                          "\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;
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kVec3f_GrSLType,
+                          "interiorNormal",
+                          SK_ARRAY_COUNT(gInteriorNormalArgs),
+                          gInteriorNormalArgs,
+                          interiorNormalBody.c_str(),
+                          &interiorNormalName);
+
+    code->appendf("\t\tvec2 coord = %s;\n", coords);
+    code->appendf("\t\tfloat m[9];\n");
+
+    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+    const char* surfScale = builder->getUniformCStr(fSurfaceScaleUni);
+
+    int index = 0;
+    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);
+            code->appendf("\t\tm[%d] = ", index++);
+            builder->appendTextureLookup(code, samplers[0], texCoords.c_str());
+            code->appendf(".a;\n");
+        }
+    }
+    code->appendf("\t\tvec3 surfaceToLight = ");
+    SkString arg;
+    arg.appendf("%s * m[4]", surfScale);
+    fLight->emitSurfaceToLight(builder, code, arg.c_str());
+    code->append(";\n");
+    code->appendf("\t\t%s = %s(%s(m, %s), surfaceToLight, ",
+                  outputColor, lightFunc.c_str(), interiorNormalName.c_str(), surfScale);
+    fLight->emitLightColor(builder, "surfaceToLight");
+    code->append(");\n");
+    GrGLSLMulVarBy4f(code, 2, outputColor, inputColor);
+}
+
+GrGLEffect::EffectKey GrGLLightingEffect::GenKey(const GrEffectStage& s,
+                                                 const GrGLCaps& caps) {
+    const GrLightingEffect& effect = GetEffectFromStage<GrLightingEffect>(s);
+    EffectKey key = effect.light()->type();
+    key <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey matrixKey = GrGLEffectMatrix::GenKey(effect.getMatrix(),
+                                                   s.getCoordChangeMatrix(),
+                                                   effect.texture(0));
+    return key | matrixKey;
+}
+
+void GrGLLightingEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrLightingEffect& effect = GetEffectFromStage<GrLightingEffect>(stage);
+    GrTexture* texture = effect.texture(0);
+    float ySign = texture->origin() == kTopLeft_GrSurfaceOrigin ? -1.0f : 1.0f;
+    uman.set2f(fImageIncrementUni, 1.0f / texture->width(), ySign / texture->height());
+    uman.set1f(fSurfaceScaleUni, effect.surfaceScale());
+    fLight->setData(uman, effect.light());
+    fEffectMatrix.setData(uman,
+                          effect.getMatrix(),
+                          stage.getCoordChangeMatrix(),
+                          effect.texture(0));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLDiffuseLightingEffect::GrGLDiffuseLightingEffect(const GrBackendEffectFactory& factory,
+                                                     const GrEffectRef& effect)
+    : INHERITED(factory, effect)
+    , fKDUni(kInvalidUniformHandle) {
+}
+
+void GrGLDiffuseLightingEffect::emitLightFunc(GrGLShaderBuilder* builder, SkString* funcName) {
+    const char* kd;
+    fKDUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                 kFloat_GrSLType,
+                                 "KD",
+                                 &kd);
+
+    static const GrGLShaderVar gLightArgs[] = {
+        GrGLShaderVar("normal", kVec3f_GrSLType),
+        GrGLShaderVar("surfaceToLight", kVec3f_GrSLType),
+        GrGLShaderVar("lightColor", kVec3f_GrSLType)
+    };
+    SkString lightBody;
+    lightBody.appendf("\tfloat colorScale = %s * dot(normal, surfaceToLight);\n", kd);
+    lightBody.appendf("\treturn vec4(lightColor * clamp(colorScale, 0.0, 1.0), 1.0);\n");
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kVec4f_GrSLType,
+                          "light",
+                          SK_ARRAY_COUNT(gLightArgs),
+                          gLightArgs,
+                          lightBody.c_str(),
+                          funcName);
+}
+
+void GrGLDiffuseLightingEffect::setData(const GrGLUniformManager& uman,
+                                        const GrEffectStage& stage) {
+    INHERITED::setData(uman, stage);
+    const GrDiffuseLightingEffect& effect = GetEffectFromStage<GrDiffuseLightingEffect>(stage);
+    uman.set1f(fKDUni, effect.kd());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrSpecularLightingEffect::GrSpecularLightingEffect(GrTexture* texture, const SkLight* light, SkScalar surfaceScale, SkScalar ks, SkScalar shininess)
+    : INHERITED(texture, light, surfaceScale),
+      fKS(ks),
+      fShininess(shininess) {
+}
+
+const GrBackendEffectFactory& GrSpecularLightingEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrSpecularLightingEffect>::getInstance();
+}
+
+bool GrSpecularLightingEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrSpecularLightingEffect& s = CastEffect<GrSpecularLightingEffect>(sBase);
+    return INHERITED::onIsEqual(sBase) &&
+           this->ks() == s.ks() &&
+           this->shininess() == s.shininess();
+}
+
+GR_DEFINE_EFFECT_TEST(GrSpecularLightingEffect);
+
+GrEffectRef* GrSpecularLightingEffect::TestCreate(SkRandom* random,
+                                                  GrContext* context,
+                                                  GrTexture* textures[]) {
+    SkScalar surfaceScale = random->nextSScalar1();
+    SkScalar ks = random->nextUScalar1();
+    SkScalar shininess = random->nextUScalar1();
+    SkAutoTUnref<SkLight> light(create_random_light(random));
+    return GrSpecularLightingEffect::Create(textures[GrEffectUnitTest::kAlphaTextureIdx],
+                                            light, surfaceScale, ks, shininess);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLSpecularLightingEffect::GrGLSpecularLightingEffect(const GrBackendEffectFactory& factory,
+                                                       const GrEffectRef& effect)
+    : GrGLLightingEffect(factory, effect)
+    , fKSUni(kInvalidUniformHandle)
+    , fShininessUni(kInvalidUniformHandle) {
+}
+
+void GrGLSpecularLightingEffect::emitLightFunc(GrGLShaderBuilder* builder, SkString* funcName) {
+    const char* ks;
+    const char* shininess;
+
+    fKSUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                 kFloat_GrSLType, "KS", &ks);
+    fShininessUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                        kFloat_GrSLType, "Shininess", &shininess);
+
+    static const GrGLShaderVar gLightArgs[] = {
+        GrGLShaderVar("normal", kVec3f_GrSLType),
+        GrGLShaderVar("surfaceToLight", kVec3f_GrSLType),
+        GrGLShaderVar("lightColor", kVec3f_GrSLType)
+    };
+    SkString lightBody;
+    lightBody.appendf("\tvec3 halfDir = vec3(normalize(surfaceToLight + vec3(0, 0, 1)));\n");
+    lightBody.appendf("\tfloat colorScale = %s * pow(dot(normal, halfDir), %s);\n", ks, shininess);
+    lightBody.appendf("\tvec3 color = lightColor * clamp(colorScale, 0.0, 1.0);\n");
+    lightBody.appendf("\treturn vec4(color, max(max(color.r, color.g), color.b));\n");
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kVec4f_GrSLType,
+                          "light",
+                          SK_ARRAY_COUNT(gLightArgs),
+                          gLightArgs,
+                          lightBody.c_str(),
+                          funcName);
+}
+
+void GrGLSpecularLightingEffect::setData(const GrGLUniformManager& uman,
+                                         const GrEffectStage& stage) {
+    INHERITED::setData(uman, stage);
+    const GrSpecularLightingEffect& effect = GetEffectFromStage<GrSpecularLightingEffect>(stage);
+    uman.set1f(fKSUni, effect.ks());
+    uman.set1f(fShininessUni, effect.shininess());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void GrGLLight::emitLightColorUniform(GrGLShaderBuilder* builder) {
+    fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                    kVec3f_GrSLType, "LightColor");
+}
+
+void GrGLLight::emitLightColor(GrGLShaderBuilder* builder,
+                               const char *surfaceToLight) {
+    builder->fFSCode.append(builder->getUniformCStr(this->lightColorUni()));
+}
+
+void GrGLLight::setData(const GrGLUniformManager& uman,
+                        const SkLight* light) const {
+    setUniformPoint3(uman, fColorUni, light->color() * SkScalarInvert(SkIntToScalar(255)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLDistantLight::setData(const GrGLUniformManager& uman, const SkLight* light) const {
+    INHERITED::setData(uman, light);
+    SkASSERT(light->type() == SkLight::kDistant_LightType);
+    const SkDistantLight* distantLight = static_cast<const SkDistantLight*>(light);
+    setUniformNormal3(uman, fDirectionUni, distantLight->direction());
+}
+
+void GrGLDistantLight::emitSurfaceToLight(GrGLShaderBuilder* builder,
+                                          SkString* out,
+                                          const char* z) {
+    const char* dir;
+    fDirectionUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType,
+                                        "LightDirection", &dir);
+    out->append(dir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLPointLight::setData(const GrGLUniformManager& uman,
+                             const SkLight* light) const {
+    INHERITED::setData(uman, light);
+    SkASSERT(light->type() == SkLight::kPoint_LightType);
+    const SkPointLight* pointLight = static_cast<const SkPointLight*>(light);
+    setUniformPoint3(uman, fLocationUni, pointLight->location());
+}
+
+void GrGLPointLight::emitSurfaceToLight(GrGLShaderBuilder* builder,
+                                        SkString* out,
+                                        const char* z) {
+    const char* loc;
+    fLocationUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType, kVec3f_GrSLType,
+                                       "LightLocation", &loc);
+    out->appendf("normalize(%s - vec3(%s.xy, %s))", loc, builder->fragmentPosition(), z);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLSpotLight::setData(const GrGLUniformManager& uman,
+                            const SkLight* light) const {
+    INHERITED::setData(uman, light);
+    SkASSERT(light->type() == SkLight::kSpot_LightType);
+    const SkSpotLight* spotLight = static_cast<const SkSpotLight *>(light);
+    setUniformPoint3(uman, fLocationUni, spotLight->location());
+    uman.set1f(fExponentUni, spotLight->specularExponent());
+    uman.set1f(fCosInnerConeAngleUni, spotLight->cosInnerConeAngle());
+    uman.set1f(fCosOuterConeAngleUni, spotLight->cosOuterConeAngle());
+    uman.set1f(fConeScaleUni, spotLight->coneScale());
+    setUniformNormal3(uman, fSUni, spotLight->s());
+}
+
+void GrGLSpotLight::emitSurfaceToLight(GrGLShaderBuilder* builder,
+                                       SkString* out,
+                                       const char* z) {
+    const char* location;
+    fLocationUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                       kVec3f_GrSLType, "LightLocation", &location);
+    out->appendf("normalize(%s - vec3(%s.xy, %s))", location, builder->fragmentPosition(), z);
+}
+
+void GrGLSpotLight::emitLightColor(GrGLShaderBuilder* builder,
+                                   const char *surfaceToLight) {
+
+    const char* color = builder->getUniformCStr(this->lightColorUni()); // created by parent class.
+
+    const char* exponent;
+    const char* cosInner;
+    const char* cosOuter;
+    const char* coneScale;
+    const char* s;
+    fExponentUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                       kFloat_GrSLType, "Exponent", &exponent);
+    fCosInnerConeAngleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                kFloat_GrSLType, "CosInnerConeAngle", &cosInner);
+    fCosOuterConeAngleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                kFloat_GrSLType, "CosOuterConeAngle", &cosOuter);
+    fConeScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                        kFloat_GrSLType, "ConeScale", &coneScale);
+    fSUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                kVec3f_GrSLType, "S", &s);
+
+    static const GrGLShaderVar gLightColorArgs[] = {
+        GrGLShaderVar("surfaceToLight", kVec3f_GrSLType)
+    };
+    SkString lightColorBody;
+    lightColorBody.appendf("\tfloat cosAngle = -dot(surfaceToLight, %s);\n", s);
+    lightColorBody.appendf("\tif (cosAngle < %s) {\n", cosOuter);
+    lightColorBody.appendf("\t\treturn vec3(0);\n");
+    lightColorBody.appendf("\t}\n");
+    lightColorBody.appendf("\tfloat scale = pow(cosAngle, %s);\n", exponent);
+    lightColorBody.appendf("\tif (cosAngle < %s) {\n", cosInner);
+    lightColorBody.appendf("\t\treturn %s * scale * (cosAngle - %s) * %s;\n",
+                           color, cosOuter, coneScale);
+    lightColorBody.appendf("\t}\n");
+    lightColorBody.appendf("\treturn %s;\n", color);
+    builder->emitFunction(GrGLShaderBuilder::kFragment_ShaderType,
+                          kVec3f_GrSLType,
+                          "lightColor",
+                          SK_ARRAY_COUNT(gLightColorArgs),
+                          gLightColorArgs,
+                          lightColorBody.c_str(),
+                          &fLightColorFunc);
+
+    builder->fFSCode.appendf("%s(%s)", fLightColorFunc.c_str(), surfaceToLight);
+}
+
+#endif
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkLightingImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiffuseLightingImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpecularLightingImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDistantLight)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPointLight)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSpotLight)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/SkMagnifierImageFilter.cpp b/src/effects/SkMagnifierImageFilter.cpp
new file mode 100644
index 0000000..d1e3b81
--- /dev/null
+++ b/src/effects/SkMagnifierImageFilter.cpp
@@ -0,0 +1,354 @@
+/*
+ * Copyright 2012 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 "SkBitmap.h"
+#include "SkMagnifierImageFilter.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+
+////////////////////////////////////////////////////////////////////////////////
+#if SK_SUPPORT_GPU
+#include "effects/GrSingleTextureEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrTBackendEffectFactory.h"
+
+class GrGLMagnifierEffect;
+
+class GrMagnifierEffect : public GrSingleTextureEffect {
+
+public:
+    static GrEffectRef* Create(GrTexture* texture,
+                               float xOffset,
+                               float yOffset,
+                               float xZoom,
+                               float yZoom,
+                               float xInset,
+                               float yInset) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrMagnifierEffect, (texture,
+                                                              xOffset,
+                                                              yOffset,
+                                                              xZoom,
+                                                              yZoom,
+                                                              xInset,
+                                                              yInset)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrMagnifierEffect() {};
+
+    static const char* Name() { return "Magnifier"; }
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    float x_offset() const { return fXOffset; }
+    float y_offset() const { return fYOffset; }
+    float x_zoom() const { return fXZoom; }
+    float y_zoom() const { return fYZoom; }
+    float x_inset() const { return fXInset; }
+    float y_inset() const { return fYInset; }
+
+    typedef GrGLMagnifierEffect GLEffect;
+
+private:
+    GrMagnifierEffect(GrTexture* texture,
+                      float xOffset,
+                      float yOffset,
+                      float xZoom,
+                      float yZoom,
+                      float xInset,
+                      float yInset)
+        : GrSingleTextureEffect(texture, MakeDivByTextureWHMatrix(texture))
+        , fXOffset(xOffset)
+        , fYOffset(yOffset)
+        , fXZoom(xZoom)
+        , fYZoom(yZoom)
+        , fXInset(xInset)
+        , fYInset(yInset) {}
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    float fXOffset;
+    float fYOffset;
+    float fXZoom;
+    float fYZoom;
+    float fXInset;
+    float fYInset;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+
+class GrGLMagnifierEffect : public GrGLEffect {
+public:
+    GrGLMagnifierEffect(const GrBackendEffectFactory& factory, const GrEffectRef& effect);
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+private:
+
+    UniformHandle       fOffsetVar;
+    UniformHandle       fZoomVar;
+    UniformHandle       fInsetVar;
+
+    GrGLEffectMatrix    fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLMagnifierEffect::GrGLMagnifierEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
+    : INHERITED(factory)
+    , fOffsetVar(GrGLUniformManager::kInvalidUniformHandle)
+    , fZoomVar(GrGLUniformManager::kInvalidUniformHandle)
+    , fInsetVar(GrGLUniformManager::kInvalidUniformHandle) {
+}
+
+void GrGLMagnifierEffect::emitCode(GrGLShaderBuilder* builder,
+                                   const GrEffectStage&,
+                                   EffectKey key,
+                                   const char* vertexCoords,
+                                   const char* outputColor,
+                                   const char* inputColor,
+                                   const TextureSamplerArray& samplers) {
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+    fOffsetVar = builder->addUniform(
+        GrGLShaderBuilder::kFragment_ShaderType |
+        GrGLShaderBuilder::kVertex_ShaderType,
+        kVec2f_GrSLType, "uOffset");
+    fZoomVar = builder->addUniform(
+        GrGLShaderBuilder::kFragment_ShaderType |
+        GrGLShaderBuilder::kVertex_ShaderType,
+        kVec2f_GrSLType, "uZoom");
+    fInsetVar = builder->addUniform(
+        GrGLShaderBuilder::kFragment_ShaderType |
+        GrGLShaderBuilder::kVertex_ShaderType,
+        kVec2f_GrSLType, "uInset");
+
+    SkString* code = &builder->fFSCode;
+
+    code->appendf("\t\tvec2 coord = %s;\n", coords);
+    code->appendf("\t\tvec2 zoom_coord = %s + %s / %s;\n",
+                  builder->getUniformCStr(fOffsetVar),
+                  coords,
+                  builder->getUniformCStr(fZoomVar));
+
+    code->appendf("\t\tvec2 delta = min(coord, vec2(1.0, 1.0) - coord);\n");
+
+    code->appendf("\t\tdelta = delta / %s;\n", builder->getUniformCStr(fInsetVar));
+
+    code->appendf("\t\tfloat weight = 0.0;\n");
+    code->appendf("\t\tif (delta.s < 2.0 && delta.t < 2.0) {\n");
+    code->appendf("\t\t\tdelta = vec2(2.0, 2.0) - delta;\n");
+    code->appendf("\t\t\tfloat dist = length(delta);\n");
+    code->appendf("\t\t\tdist = max(2.0 - dist, 0.0);\n");
+    code->appendf("\t\t\tweight = min(dist * dist, 1.0);\n");
+    code->appendf("\t\t} else {\n");
+    code->appendf("\t\t\tvec2 delta_squared = delta * delta;\n");
+    code->appendf("\t\t\tweight = min(min(delta_squared.s, delta_squared.y), 1.0);\n");
+    code->appendf("\t\t}\n");
+
+    code->appendf("\t\tvec2 mix_coord = mix(coord, zoom_coord, weight);\n");
+    code->appendf("\t\tvec4 output_color = ");
+    builder->appendTextureLookup(code, samplers[0], "mix_coord");
+    code->append(";\n");
+
+    code->appendf("\t\t%s = output_color;", outputColor);
+    GrGLSLMulVarBy4f(code, 2, outputColor, inputColor);
+}
+
+void GrGLMagnifierEffect::setData(const GrGLUniformManager& uman,
+                                  const GrEffectStage& stage) {
+    const GrMagnifierEffect& zoom = GetEffectFromStage<GrMagnifierEffect>(stage);
+    uman.set2f(fOffsetVar, zoom.x_offset(), zoom.y_offset());
+    uman.set2f(fZoomVar, zoom.x_zoom(), zoom.y_zoom());
+    uman.set2f(fInsetVar, zoom.x_inset(), zoom.y_inset());
+    fEffectMatrix.setData(uman, zoom.getMatrix(), stage.getCoordChangeMatrix(), zoom.texture(0));
+}
+
+GrGLEffect::EffectKey GrGLMagnifierEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+    const GrMagnifierEffect& zoom = GetEffectFromStage<GrMagnifierEffect>(stage);
+    return GrGLEffectMatrix::GenKey(zoom.getMatrix(),
+                                    stage.getCoordChangeMatrix(),
+                                    zoom.texture(0));
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrMagnifierEffect);
+
+GrEffectRef* GrMagnifierEffect::TestCreate(SkRandom* random,
+                                           GrContext* context,
+                                           GrTexture** textures) {
+    const int kMaxWidth = 200;
+    const int kMaxHeight = 200;
+    const int kMaxInset = 20;
+    uint32_t width = random->nextULessThan(kMaxWidth);
+    uint32_t height = random->nextULessThan(kMaxHeight);
+    uint32_t x = random->nextULessThan(kMaxWidth - width);
+    uint32_t y = random->nextULessThan(kMaxHeight - height);
+    SkScalar inset = SkIntToScalar(random->nextULessThan(kMaxInset));
+
+    SkAutoTUnref<SkImageFilter> filter(
+            new SkMagnifierImageFilter(
+                SkRect::MakeXYWH(SkIntToScalar(x), SkIntToScalar(y),
+                                 SkIntToScalar(width), SkIntToScalar(height)),
+                inset));
+    GrEffectRef* effect;
+    filter->asNewEffect(&effect, textures[0]);
+    GrAssert(NULL != effect);
+    return effect;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const GrBackendEffectFactory& GrMagnifierEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrMagnifierEffect>::getInstance();
+}
+
+bool GrMagnifierEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrMagnifierEffect& s = CastEffect<GrMagnifierEffect>(sBase);
+    return (this->texture(0) == s.texture(0) &&
+            this->fXOffset == s.fXOffset &&
+            this->fYOffset == s.fYOffset &&
+            this->fXZoom == s.fXZoom &&
+            this->fYZoom == s.fYZoom &&
+            this->fXInset == s.fXInset &&
+            this->fYInset == s.fYInset);
+}
+
+void GrMagnifierEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+SkMagnifierImageFilter::SkMagnifierImageFilter(SkFlattenableReadBuffer& buffer)
+  : INHERITED(buffer) {
+    float x = buffer.readScalar();
+    float y = buffer.readScalar();
+    float width = buffer.readScalar();
+    float height = buffer.readScalar();
+    fSrcRect = SkRect::MakeXYWH(x, y, width, height);
+    fInset = buffer.readScalar();
+}
+
+// FIXME:  implement single-input semantics
+SkMagnifierImageFilter::SkMagnifierImageFilter(SkRect srcRect, SkScalar inset)
+    : INHERITED(0), fSrcRect(srcRect), fInset(inset) {
+    SkASSERT(srcRect.x() >= 0 && srcRect.y() >= 0 && inset >= 0);
+}
+
+bool SkMagnifierImageFilter::asNewEffect(GrEffectRef** effect, GrTexture* texture) const {
+#if SK_SUPPORT_GPU
+    if (effect) {
+      *effect = GrMagnifierEffect::Create(texture,
+                                          fSrcRect.x() / texture->width(),
+                                          fSrcRect.y() / texture->height(),
+                                          texture->width() / fSrcRect.width(),
+                                          texture->height() / fSrcRect.height(),
+                                          fInset / texture->width(),
+                                          fInset / texture->height());
+    }
+    return true;
+#else
+    return false;
+#endif
+}
+
+void SkMagnifierImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeScalar(fSrcRect.x());
+    buffer.writeScalar(fSrcRect.y());
+    buffer.writeScalar(fSrcRect.width());
+    buffer.writeScalar(fSrcRect.height());
+    buffer.writeScalar(fInset);
+}
+
+bool SkMagnifierImageFilter::onFilterImage(Proxy*, const SkBitmap& src,
+                                           const SkMatrix&, SkBitmap* dst,
+                                           SkIPoint* offset) {
+    SkASSERT(src.config() == SkBitmap::kARGB_8888_Config);
+    SkASSERT(fSrcRect.width() < src.width());
+    SkASSERT(fSrcRect.height() < src.height());
+
+    if (src.config() != SkBitmap::kARGB_8888_Config) {
+      return false;
+    }
+
+    SkAutoLockPixels alp(src);
+    SkASSERT(src.getPixels());
+    if (!src.getPixels() || src.width() <= 0 || src.height() <= 0) {
+      return false;
+    }
+
+    SkScalar inv_inset = fInset > 0 ? SkScalarInvert(fInset) : SK_Scalar1;
+
+    SkScalar inv_x_zoom = fSrcRect.width() / src.width();
+    SkScalar inv_y_zoom = fSrcRect.height() / src.height();
+
+    dst->setConfig(src.config(), src.width(), src.height());
+    dst->allocPixels();
+    SkColor* sptr = src.getAddr32(0, 0);
+    SkColor* dptr = dst->getAddr32(0, 0);
+    int width = src.width(), height = src.height();
+    for (int y = 0; y < height; ++y) {
+        for (int x = 0; x < width; ++x) {
+            SkScalar x_dist = SkMin32(x, width - x - 1) * inv_inset;
+            SkScalar y_dist = SkMin32(y, height - y - 1) * inv_inset;
+            SkScalar weight = 0;
+
+            static const SkScalar kScalar2 = SkScalar(2);
+
+            // To create a smooth curve at the corners, we need to work on
+            // a square twice the size of the inset.
+            if (x_dist < kScalar2 && y_dist < kScalar2) {
+                x_dist = kScalar2 - x_dist;
+                y_dist = kScalar2 - y_dist;
+
+                SkScalar dist = SkScalarSqrt(SkScalarSquare(x_dist) +
+                                             SkScalarSquare(y_dist));
+                dist = SkMaxScalar(kScalar2 - dist, 0);
+                weight = SkMinScalar(SkScalarSquare(dist), SK_Scalar1);
+            } else {
+                SkScalar sqDist = SkMinScalar(SkScalarSquare(x_dist),
+                                              SkScalarSquare(y_dist));
+                weight = SkMinScalar(sqDist, SK_Scalar1);
+            }
+
+            SkScalar x_interp = SkScalarMul(weight, (fSrcRect.x() + x * inv_x_zoom)) +
+                           (SK_Scalar1 - weight) * x;
+            SkScalar y_interp = SkScalarMul(weight, (fSrcRect.y() + y * inv_y_zoom)) +
+                           (SK_Scalar1 - weight) * y;
+
+            int x_val = SkMin32(SkScalarFloorToInt(x_interp), width - 1);
+            int y_val = SkMin32(SkScalarFloorToInt(y_interp), height - 1);
+
+            *dptr = sptr[y_val * width + x_val];
+            dptr++;
+        }
+    }
+    return true;
+}
diff --git a/src/effects/SkMatrixConvolutionImageFilter.cpp b/src/effects/SkMatrixConvolutionImageFilter.cpp
new file mode 100644
index 0000000..5a97ec4
--- /dev/null
+++ b/src/effects/SkMatrixConvolutionImageFilter.cpp
@@ -0,0 +1,585 @@
+/*
+ * Copyright 2012 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 "SkMatrixConvolutionImageFilter.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkRect.h"
+#include "SkUnPreMultiply.h"
+
+#if SK_SUPPORT_GPU
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "effects/GrSingleTextureEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrTexture.h"
+#include "SkMatrix.h"
+
+#endif
+
+SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(const SkISize& kernelSize, const SkScalar* kernel, SkScalar gain, SkScalar bias, const SkIPoint& target, TileMode tileMode, bool convolveAlpha, SkImageFilter* input)
+  : INHERITED(input),
+    fKernelSize(kernelSize),
+    fGain(gain),
+    fBias(bias),
+    fTarget(target),
+    fTileMode(tileMode),
+    fConvolveAlpha(convolveAlpha) {
+    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
+    fKernel = SkNEW_ARRAY(SkScalar, size);
+    memcpy(fKernel, kernel, size * sizeof(SkScalar));
+    SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
+    SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth);
+    SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight);
+}
+
+SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    fKernelSize.fWidth = buffer.readInt();
+    fKernelSize.fHeight = buffer.readInt();
+    uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
+    fKernel = SkNEW_ARRAY(SkScalar, size);
+    SkDEBUGCODE(uint32_t readSize = )buffer.readScalarArray(fKernel);
+    SkASSERT(readSize == size);
+    fGain = buffer.readScalar();
+    fBias = buffer.readScalar();
+    fTarget.fX = buffer.readInt();
+    fTarget.fY = buffer.readInt();
+    fTileMode = (TileMode) buffer.readInt();
+    fConvolveAlpha = buffer.readBool();
+}
+
+void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeInt(fKernelSize.fWidth);
+    buffer.writeInt(fKernelSize.fHeight);
+    buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
+    buffer.writeScalar(fGain);
+    buffer.writeScalar(fBias);
+    buffer.writeInt(fTarget.fX);
+    buffer.writeInt(fTarget.fY);
+    buffer.writeInt((int) fTileMode);
+    buffer.writeBool(fConvolveAlpha);
+}
+
+SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
+    delete[] fKernel;
+}
+
+class UncheckedPixelFetcher {
+public:
+    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
+        return *src.getAddr32(x, y);
+    }
+};
+
+class ClampPixelFetcher {
+public:
+    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
+        x = SkClampMax(x, src.width() - 1);
+        y = SkClampMax(y, src.height() - 1);
+        return *src.getAddr32(x, y);
+    }
+};
+
+class RepeatPixelFetcher {
+public:
+    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
+        x %= src.width();
+        y %= src.height();
+        if (x < 0) {
+            x += src.width();
+        }
+        if (y < 0) {
+            y += src.height();
+        }
+        return *src.getAddr32(x, y);
+    }
+};
+
+class ClampToBlackPixelFetcher {
+public:
+    static inline SkPMColor fetch(const SkBitmap& src, int x, int y) {
+        if (x < 0 || x >= src.width() || y < 0 || y >= src.height()) {
+            return 0;
+        } else {
+            return *src.getAddr32(x, y);
+        }
+    }
+};
+
+template<class PixelFetcher, bool convolveAlpha>
+void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
+    for (int y = rect.fTop; y < rect.fBottom; ++y) {
+        SkPMColor* dptr = result->getAddr32(rect.fLeft, y);
+        for (int x = rect.fLeft; x < rect.fRight; ++x) {
+            SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
+            for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
+                for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
+                    SkPMColor s = PixelFetcher::fetch(src, x + cx - fTarget.fX, y + cy - fTarget.fY);
+                    SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
+                    if (convolveAlpha) {
+                        sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
+                    }
+                    sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
+                    sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
+                    sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
+                }
+            }
+            int a = convolveAlpha
+                  ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255)
+                  : 255;
+            int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
+            int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
+            int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
+            if (!convolveAlpha) {
+                a = SkGetPackedA32(PixelFetcher::fetch(src, x, y));
+                *dptr++ = SkPreMultiplyARGB(a, r, g, b);
+            } else {
+                *dptr++ = SkPackARGB32(a, r, g, b);
+            }
+        }
+    }
+}
+
+template<class PixelFetcher>
+void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
+    if (fConvolveAlpha) {
+        filterPixels<PixelFetcher, true>(src, result, rect);
+    } else {
+        filterPixels<PixelFetcher, false>(src, result, rect);
+    }
+}
+
+void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
+    filterPixels<UncheckedPixelFetcher>(src, result, rect);
+}
+
+void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src, SkBitmap* result, const SkIRect& rect) {
+    switch (fTileMode) {
+        case kClamp_TileMode:
+            filterPixels<ClampPixelFetcher>(src, result, rect);
+            break;
+        case kRepeat_TileMode:
+            filterPixels<RepeatPixelFetcher>(src, result, rect);
+            break;
+        case kClampToBlack_TileMode:
+            filterPixels<ClampToBlackPixelFetcher>(src, result, rect);
+            break;
+    }
+}
+
+// FIXME:  This should be refactored to SkSingleInputImageFilter for
+// use by other filters.  For now, we assume the input is always
+// premultiplied and unpremultiply it
+static SkBitmap unpremultiplyBitmap(const SkBitmap& src)
+{
+    SkAutoLockPixels alp(src);
+    if (!src.getPixels()) {
+        return SkBitmap();
+    }
+    SkBitmap result;
+    result.setConfig(src.config(), src.width(), src.height());
+    result.allocPixels();
+    if (!result.getPixels()) {
+        return SkBitmap();
+    }
+    for (int y = 0; y < src.height(); ++y) {
+        const uint32_t* srcRow = src.getAddr32(0, y);
+        uint32_t* dstRow = result.getAddr32(0, y);
+        for (int x = 0; x < src.width(); ++x) {
+            dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
+        }
+    }
+    return result;
+}
+
+bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
+                                                   const SkBitmap& source,
+                                                   const SkMatrix& matrix,
+                                                   SkBitmap* result,
+                                                   SkIPoint* loc) {
+    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
+    if (src.config() != SkBitmap::kARGB_8888_Config) {
+        return false;
+    }
+
+    if (!fConvolveAlpha && !src.isOpaque()) {
+        src = unpremultiplyBitmap(src);
+    }
+
+    SkAutoLockPixels alp(src);
+    if (!src.getPixels()) {
+        return false;
+    }
+
+    result->setConfig(src.config(), src.width(), src.height());
+    result->allocPixels();
+
+    SkIRect interior = SkIRect::MakeXYWH(fTarget.fX, fTarget.fY,
+                                         src.width() - fKernelSize.fWidth + 1,
+                                         src.height() - fKernelSize.fHeight + 1);
+    SkIRect top = SkIRect::MakeWH(src.width(), fTarget.fY);
+    SkIRect bottom = SkIRect::MakeLTRB(0, interior.bottom(),
+                                       src.width(), src.height());
+    SkIRect left = SkIRect::MakeXYWH(0, interior.top(),
+                                     fTarget.fX, interior.height());
+    SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
+                                      src.width(), interior.bottom());
+    filterBorderPixels(src, result, top);
+    filterBorderPixels(src, result, left);
+    filterInteriorPixels(src, result, interior);
+    filterBorderPixels(src, result, right);
+    filterBorderPixels(src, result, bottom);
+    return true;
+}
+
+#if SK_SUPPORT_GPU
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLMatrixConvolutionEffect;
+
+class GrMatrixConvolutionEffect : public GrSingleTextureEffect {
+public:
+    typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
+    static GrEffectRef* Create(GrTexture* texture,
+                               const SkISize& kernelSize,
+                               const SkScalar* kernel,
+                               SkScalar gain,
+                               SkScalar bias,
+                               const SkIPoint& target,
+                               TileMode tileMode,
+                               bool convolveAlpha) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
+                                                                      kernelSize,
+                                                                      kernel,
+                                                                      gain,
+                                                                      bias,
+                                                                      target,
+                                                                      tileMode,
+                                                                      convolveAlpha)));
+        return CreateEffectRef(effect);
+    }
+    virtual ~GrMatrixConvolutionEffect();
+
+    virtual void getConstantColorComponents(GrColor* color,
+                                            uint32_t* validFlags) const SK_OVERRIDE {
+        // TODO: Try to do better?
+        *validFlags = 0;
+    }
+
+    static const char* Name() { return "MatrixConvolution"; }
+    const SkISize& kernelSize() const { return fKernelSize; }
+    const float* target() const { return fTarget; }
+    const float* kernel() const { return fKernel; }
+    float gain() const { return fGain; }
+    float bias() const { return fBias; }
+    TileMode tileMode() const { return fTileMode; }
+    bool convolveAlpha() const { return fConvolveAlpha; }
+
+    typedef GrGLMatrixConvolutionEffect GLEffect;
+
+
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrMatrixConvolutionEffect(GrTexture*,
+                              const SkISize& kernelSize,
+                              const SkScalar* kernel,
+                              SkScalar gain,
+                              SkScalar bias,
+                              const SkIPoint& target,
+                              TileMode tileMode,
+                              bool convolveAlpha);
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    SkISize  fKernelSize;
+    float   *fKernel;
+    float    fGain;
+    float    fBias;
+    float    fTarget[2];
+    TileMode fTileMode;
+    bool     fConvolveAlpha;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+class GrGLMatrixConvolutionEffect : public GrGLEffect {
+public:
+    GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
+                                const GrEffectRef& effect);
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+private:
+    typedef GrGLUniformManager::UniformHandle        UniformHandle;
+    typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
+    SkISize             fKernelSize;
+    TileMode            fTileMode;
+    bool                fConvolveAlpha;
+
+    UniformHandle       fKernelUni;
+    UniformHandle       fImageIncrementUni;
+    UniformHandle       fTargetUni;
+    UniformHandle       fGainUni;
+    UniformHandle       fBiasUni;
+
+    GrGLEffectMatrix    fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
+                                                         const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fKernelUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fTargetUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fGainUni(GrGLUniformManager::kInvalidUniformHandle)
+    , fBiasUni(GrGLUniformManager::kInvalidUniformHandle) {
+    const GrMatrixConvolutionEffect& m = CastEffect<GrMatrixConvolutionEffect>(effect);
+    fKernelSize = m.kernelSize();
+    fTileMode = m.tileMode();
+    fConvolveAlpha = m.convolveAlpha();
+}
+
+static void appendTextureLookup(GrGLShaderBuilder* builder,
+                                const GrGLShaderBuilder::TextureSampler& sampler,
+                                const char* coord,
+                                SkMatrixConvolutionImageFilter::TileMode tileMode) {
+    SkString* code = &builder->fFSCode;
+    SkString clampedCoord;
+    switch (tileMode) {
+        case SkMatrixConvolutionImageFilter::kClamp_TileMode:
+            clampedCoord.printf("clamp(%s, 0.0, 1.0)", coord);
+            coord = clampedCoord.c_str();
+            break;
+        case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
+            clampedCoord.printf("fract(%s)", coord);
+            coord = clampedCoord.c_str();
+            break;
+        case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
+            code->appendf("clamp(%s, 0.0, 1.0) != %s ? vec4(0, 0, 0, 0) : ", coord, coord);
+            break;
+    }
+    builder->appendTextureLookup(code, sampler, coord);
+}
+
+void GrGLMatrixConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
+                                           const GrEffectStage&,
+                                           EffectKey key,
+                                           const char* vertexCoords,
+                                           const char* outputColor,
+                                           const char* inputColor,
+                                           const TextureSamplerArray& samplers) {
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "ImageIncrement");
+    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kFloat_GrSLType, "Kernel", fKernelSize.width() * fKernelSize.height());
+    fTargetUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "Target");
+    fGainUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                   kFloat_GrSLType, "Gain");
+    fBiasUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                   kFloat_GrSLType, "Bias");
+
+    SkString* code = &builder->fFSCode;
+
+    const char* target = builder->getUniformCStr(fTargetUni);
+    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+    const char* kernel = builder->getUniformCStr(fKernelUni);
+    const char* gain = builder->getUniformCStr(fGainUni);
+    const char* bias = builder->getUniformCStr(fBiasUni);
+    int kWidth = fKernelSize.width();
+    int kHeight = fKernelSize.height();
+
+    code->appendf("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
+    code->appendf("\t\tvec2 coord = %s - %s * %s;\n", coords, target, imgInc);
+    code->appendf("\t\tfor (int y = 0; y < %d; y++) {\n", kHeight);
+    code->appendf("\t\t\tfor (int x = 0; x < %d; x++) {\n", kWidth);
+    code->appendf("\t\t\t\tfloat k = %s[y * %d + x];\n", kernel, kWidth);
+    code->appendf("\t\t\t\tvec2 coord2 = coord + vec2(x, y) * %s;\n", imgInc);
+    code->appendf("\t\t\t\tvec4 c = ");
+    appendTextureLookup(builder, samplers[0], "coord2", fTileMode);
+    code->appendf(";\n");
+    if (!fConvolveAlpha) {
+        code->appendf("\t\t\t\tc.rgb /= c.a;\n");
+    }
+    code->appendf("\t\t\t\tsum += c * k;\n");
+    code->appendf("\t\t\t}\n");
+    code->appendf("\t\t}\n");
+    if (fConvolveAlpha) {
+        code->appendf("\t\t%s = sum * %s + %s;\n", outputColor, gain, bias);
+        code->appendf("\t\t%s.rgb = clamp(%s.rgb, 0.0, %s.a);\n", outputColor, outputColor, outputColor);
+    } else {
+        code->appendf("\t\tvec4 c = ");
+        appendTextureLookup(builder, samplers[0], coords, fTileMode);
+        code->appendf(";\n");
+        code->appendf("\t\t%s.a = c.a;\n", outputColor);
+        code->appendf("\t\t%s.rgb = sum.rgb * %s + %s;\n", outputColor, gain, bias);
+        code->appendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
+    }
+}
+
+namespace {
+
+int encodeXY(int x, int y) {
+    SkASSERT(x >= 1 && y >= 1 && x * y <= 32);
+    if (y < x)
+        return 0x40 | encodeXY(y, x);
+    else
+        return (0x40 >> x) | (y - x);
+}
+
+};
+
+GrGLEffect::EffectKey GrGLMatrixConvolutionEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
+    const GrMatrixConvolutionEffect& m = GetEffectFromStage<GrMatrixConvolutionEffect>(s);
+    EffectKey key = encodeXY(m.kernelSize().width(), m.kernelSize().height());
+    key |= m.tileMode() << 7;
+    key |= m.convolveAlpha() ? 1 << 9 : 0;
+    key <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
+                                                   s.getCoordChangeMatrix(),
+                                                   m.texture(0));
+    return key | matrixKey;
+}
+
+void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
+                                          const GrEffectStage& stage) {
+    const GrMatrixConvolutionEffect& effect = GetEffectFromStage<GrMatrixConvolutionEffect>(stage);
+    GrTexture& texture = *effect.texture(0);
+    // the code we generated was for a specific kernel size
+    GrAssert(effect.kernelSize() == fKernelSize);
+    GrAssert(effect.tileMode() == fTileMode);
+    float imageIncrement[2];
+    float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
+    imageIncrement[0] = 1.0f / texture.width();
+    imageIncrement[1] = ySign / texture.height();
+    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+    uman.set2fv(fTargetUni, 0, 1, effect.target());
+    uman.set1fv(fKernelUni, 0, fKernelSize.width() * fKernelSize.height(), effect.kernel());
+    uman.set1f(fGainUni, effect.gain());
+    uman.set1f(fBiasUni, effect.bias());
+    fEffectMatrix.setData(uman,
+                          effect.getMatrix(),
+                          stage.getCoordChangeMatrix(),
+                          effect.texture(0));
+}
+
+GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
+                                                     const SkISize& kernelSize,
+                                                     const SkScalar* kernel,
+                                                     SkScalar gain,
+                                                     SkScalar bias,
+                                                     const SkIPoint& target,
+                                                     TileMode tileMode,
+                                                     bool convolveAlpha)
+  : INHERITED(texture, MakeDivByTextureWHMatrix(texture)),
+    fKernelSize(kernelSize),
+    fGain(SkScalarToFloat(gain)),
+    fBias(SkScalarToFloat(bias) / 255.0f),
+    fTileMode(tileMode),
+    fConvolveAlpha(convolveAlpha) {
+    fKernel = new float[kernelSize.width() * kernelSize.height()];
+    for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
+        fKernel[i] = SkScalarToFloat(kernel[i]);
+    }
+    fTarget[0] = static_cast<float>(target.x());
+    fTarget[1] = static_cast<float>(target.y());
+}
+
+GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
+    delete[] fKernel;
+}
+
+const GrBackendEffectFactory& GrMatrixConvolutionEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrMatrixConvolutionEffect>::getInstance();
+}
+
+bool GrMatrixConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrMatrixConvolutionEffect& s = CastEffect<GrMatrixConvolutionEffect>(sBase);
+    return this->texture(0) == s.texture(0) &&
+           fKernelSize == s.kernelSize() &&
+           !memcmp(fKernel, s.kernel(), fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
+           fGain == s.gain() &&
+           fBias == s.bias() &&
+           fTarget == s.target() &&
+           fTileMode == s.tileMode() &&
+           fConvolveAlpha == s.convolveAlpha();
+}
+
+GR_DEFINE_EFFECT_TEST(GrMatrixConvolutionEffect);
+
+// A little bit less than the minimum # uniforms required by DX9SM2 (32).
+// Allows for a 5x5 kernel (or 25x1, for that matter).
+#define MAX_KERNEL_SIZE 25
+
+GrEffectRef* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
+                                                   GrContext* context,
+                                                   GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
+    int height = random->nextRangeU(1, MAX_KERNEL_SIZE / width);
+    SkISize kernelSize = SkISize::Make(width, height);
+    SkScalar* kernel = new SkScalar[width * height];
+    for (int i = 0; i < width * height; i++) {
+        kernel[i] = random->nextSScalar1();
+    }
+    SkScalar gain = random->nextSScalar1();
+    SkScalar bias = random->nextSScalar1();
+    SkIPoint target = SkIPoint::Make(random->nextRangeU(0, kernelSize.width()),
+                                     random->nextRangeU(0, kernelSize.height()));
+    TileMode tileMode = static_cast<TileMode>(random->nextRangeU(0, 2));
+    bool convolveAlpha = random->nextBool();
+    return GrMatrixConvolutionEffect::Create(textures[texIdx],
+                                             kernelSize,
+                                             kernel,
+                                             gain,
+                                             bias,
+                                             target,
+                                             tileMode,
+                                             convolveAlpha);
+
+}
+
+bool SkMatrixConvolutionImageFilter::asNewEffect(GrEffectRef** effect,
+                                                 GrTexture* texture) const {
+    bool ok = fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
+    if (ok && effect) {
+        *effect = GrMatrixConvolutionEffect::Create(texture,
+                                                    fKernelSize,
+                                                    fKernel,
+                                                    fGain,
+                                                    fBias,
+                                                     fTarget,
+                                                     fTileMode,
+                                                     fConvolveAlpha);
+    }
+    return ok;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#endif
diff --git a/src/effects/SkMergeImageFilter.cpp b/src/effects/SkMergeImageFilter.cpp
new file mode 100755
index 0000000..0c1388f
--- /dev/null
+++ b/src/effects/SkMergeImageFilter.cpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2012 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 "SkMergeImageFilter.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkFlattenableBuffers.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMergeImageFilter::initAllocModes() {
+    int inputCount = countInputs();
+    if (inputCount) {
+        size_t size = sizeof(uint8_t) * inputCount;
+        if (size <= sizeof(fStorage)) {
+            fModes = SkTCast<uint8_t*>(fStorage);
+        } else {
+            fModes = SkTCast<uint8_t*>(sk_malloc_throw(size));
+        }
+    } else {
+        fModes = NULL;
+    }
+}
+
+void SkMergeImageFilter::initModes(const SkXfermode::Mode modes[]) {
+    if (modes) {
+        this->initAllocModes();
+        int inputCount = countInputs();
+        for (int i = 0; i < inputCount; ++i) {
+            fModes[i] = SkToU8(modes[i]);
+        }
+    } else {
+        fModes = NULL;
+    }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
+                                       SkXfermode::Mode mode) : INHERITED(first, second) {
+    if (SkXfermode::kSrcOver_Mode != mode) {
+        SkXfermode::Mode modes[] = { mode, mode };
+        this->initModes(modes);
+    } else {
+        fModes = NULL;
+    }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* filters[], int count,
+                                       const SkXfermode::Mode modes[]) : INHERITED(count, filters) {
+    this->initModes(modes);
+}
+
+SkMergeImageFilter::~SkMergeImageFilter() {
+
+    if (fModes != SkTCast<uint8_t*>(fStorage)) {
+        sk_free(fModes);
+    }
+}
+
+bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+                                        SkIRect* dst) {
+    if (countInputs() < 1) {
+        return false;
+    }
+
+    SkIRect totalBounds;
+
+    int inputCount = countInputs();
+    for (int i = 0; i < inputCount; ++i) {
+        SkImageFilter* filter = getInput(i);
+        SkIRect r;
+        if (filter) {
+            if (!filter->filterBounds(src, ctm, &r)) {
+                return false;
+            }
+        } else {
+            r = src;
+        }
+        if (0 == i) {
+            totalBounds = r;
+        } else {
+            totalBounds.join(r);
+        }
+    }
+
+    // don't modify dst until now, so we don't accidentally change it in the
+    // loop, but then return false on the next filter.
+    *dst = totalBounds;
+    return true;
+}
+
+bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
+                                       const SkMatrix& ctm,
+                                       SkBitmap* result, SkIPoint* loc) {
+    if (countInputs() < 1) {
+        return false;
+    }
+
+    const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
+                                                src.width(), src.height());
+    SkIRect bounds;
+    if (!this->filterBounds(srcBounds, ctm, &bounds)) {
+        return false;
+    }
+
+    const int x0 = bounds.left();
+    const int y0 = bounds.top();
+
+    SkAutoTUnref<SkDevice> dst(proxy->createDevice(bounds.width(), bounds.height()));
+    if (NULL == dst) {
+        return false;
+    }
+    SkCanvas canvas(dst);
+    SkPaint paint;
+
+    int inputCount = countInputs();
+    for (int i = 0; i < inputCount; ++i) {
+        SkBitmap tmp;
+        const SkBitmap* srcPtr;
+        SkIPoint pos = *loc;
+        SkImageFilter* filter = getInput(i);
+        if (filter) {
+            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
+                return false;
+            }
+            srcPtr = &tmp;
+        } else {
+            srcPtr = &src;
+        }
+
+        if (fModes) {
+            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
+        } else {
+            paint.setXfermode(NULL);
+        }
+        canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
+    }
+
+    loc->set(bounds.left(), bounds.top());
+    *result = dst->accessBitmap(false);
+    return true;
+}
+
+void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+
+    buffer.writeBool(fModes != NULL);
+    if (fModes) {
+        buffer.writeByteArray(fModes, countInputs() * sizeof(fModes[0]));
+    }
+}
+
+SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    bool hasModes = buffer.readBool();
+    if (hasModes) {
+        this->initAllocModes();
+        SkASSERT(buffer.getArrayCount() == countInputs() * sizeof(fModes[0]));
+        buffer.readByteArray(fModes);
+    } else {
+        fModes = 0;
+    }
+}
diff --git a/src/effects/SkMorphologyImageFilter.cpp b/src/effects/SkMorphologyImageFilter.cpp
index 78fabc5..09d7926 100644
--- a/src/effects/SkMorphologyImageFilter.cpp
+++ b/src/effects/SkMorphologyImageFilter.cpp
@@ -6,23 +6,35 @@
  */
 
 #include "SkMorphologyImageFilter.h"
+#include "SkBitmap.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkRect.h"
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "effects/Gr1DKernelEffect.h"
+#include "SkImageFilterUtils.h"
+#endif
 
 SkMorphologyImageFilter::SkMorphologyImageFilter(SkFlattenableReadBuffer& buffer)
   : INHERITED(buffer) {
-    fRadius.fWidth = buffer.readScalar();
-    fRadius.fHeight = buffer.readScalar();
+    fRadius.fWidth = buffer.readInt();
+    fRadius.fHeight = buffer.readInt();
 }
 
-SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY)
-    : fRadius(SkISize::Make(radiusX, radiusY)) {
+SkMorphologyImageFilter::SkMorphologyImageFilter(int radiusX, int radiusY, SkImageFilter* input)
+    : INHERITED(input), fRadius(SkISize::Make(radiusX, radiusY)) {
 }
 
 
-void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkMorphologyImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    buffer.writeScalar(fRadius.fWidth);
-    buffer.writeScalar(fRadius.fHeight);
+    buffer.writeInt(fRadius.fWidth);
+    buffer.writeInt(fRadius.fHeight);
 }
 
 static void erode(const SkPMColor* src, SkPMColor* dst,
@@ -30,7 +42,8 @@
                   int srcStrideX, int srcStrideY,
                   int dstStrideX, int dstStrideY)
 {
-    const SkPMColor* upperSrc = src + SkMin32(radius, width - 1) * srcStrideX;
+    radius = SkMin32(radius, width - 1);
+    const SkPMColor* upperSrc = src + radius * srcStrideX;
     for (int x = 0; x < width; ++x) {
         const SkPMColor* lp = src;
         const SkPMColor* up = upperSrc;
@@ -77,7 +90,8 @@
                    int srcStrideX, int srcStrideY,
                    int dstStrideX, int dstStrideY)
 {
-    const SkPMColor* upperSrc = src + SkMin32(radius, width - 1) * srcStrideX;
+    radius = SkMin32(radius, width - 1);
+    const SkPMColor* upperSrc = src + radius * srcStrideX;
     for (int x = 0; x < width; ++x) {
         const SkPMColor* lp = src;
         const SkPMColor* up = upperSrc;
@@ -119,9 +133,10 @@
            src.rowBytesAsPixels(), 1, dst->rowBytesAsPixels(), 1);
 }
 
-bool SkErodeImageFilter::onFilterImage(Proxy*,
-                                       const SkBitmap& src, const SkMatrix&,
-                                       SkBitmap* dst, SkIPoint*) {
+bool SkErodeImageFilter::onFilterImage(Proxy* proxy,
+                                       const SkBitmap& source, const SkMatrix& ctm,
+                                       SkBitmap* dst, SkIPoint* offset) {
+    SkBitmap src = this->getInputResult(proxy, source, ctm, offset);
     if (src.config() != SkBitmap::kARGB_8888_Config) {
         return false;
     }
@@ -163,9 +178,10 @@
     return true;
 }
 
-bool SkDilateImageFilter::onFilterImage(Proxy*,
-                                        const SkBitmap& src, const SkMatrix&,
-                                        SkBitmap* dst, SkIPoint*) {
+bool SkDilateImageFilter::onFilterImage(Proxy* proxy,
+                                        const SkBitmap& source, const SkMatrix& ctm,
+                                        SkBitmap* dst, SkIPoint* offset) {
+    SkBitmap src = this->getInputResult(proxy, source, ctm, offset);
     if (src.config() != SkBitmap::kARGB_8888_Config) {
         return false;
     }
@@ -207,15 +223,299 @@
     return true;
 }
 
-bool SkDilateImageFilter::asADilate(SkISize* radius) const {
-    *radius = this->radius();
-    return true;
+#if SK_SUPPORT_GPU
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLMorphologyEffect;
+
+/**
+ * Morphology effects. Depending upon the type of morphology, either the
+ * component-wise min (Erode_Type) or max (Dilate_Type) of all pixels in the
+ * kernel is selected as the new color. The new color is modulated by the input
+ * color.
+ */
+class GrMorphologyEffect : public Gr1DKernelEffect {
+
+public:
+
+    enum MorphologyType {
+        kErode_MorphologyType,
+        kDilate_MorphologyType,
+    };
+
+    static GrEffectRef* Create(GrTexture* tex, Direction dir, int radius, MorphologyType type) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrMorphologyEffect, (tex, dir, radius, type)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrMorphologyEffect();
+
+    MorphologyType type() const { return fType; }
+
+    static const char* Name() { return "Morphology"; }
+
+    typedef GrGLMorphologyEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+protected:
+
+    MorphologyType fType;
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GrMorphologyEffect(GrTexture*, Direction, int radius, MorphologyType);
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef Gr1DKernelEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GrGLMorphologyEffect : public GrGLEffect {
+public:
+    GrGLMorphologyEffect (const GrBackendEffectFactory&, const GrEffectRef&);
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+private:
+    int width() const { return GrMorphologyEffect::WidthFromRadius(fRadius); }
+
+    int                                 fRadius;
+    GrMorphologyEffect::MorphologyType  fType;
+    GrGLUniformManager::UniformHandle   fImageIncrementUni;
+    GrGLEffectMatrix                    fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLMorphologyEffect::GrGLMorphologyEffect(const GrBackendEffectFactory& factory,
+                                           const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fImageIncrementUni(GrGLUniformManager::kInvalidUniformHandle) {
+    const GrMorphologyEffect& m = CastEffect<GrMorphologyEffect>(effect);
+    fRadius = m.radius();
+    fType = m.type();
 }
 
-bool SkErodeImageFilter::asAnErode(SkISize* radius) const {
-    *radius = this->radius();
-    return true;
+void GrGLMorphologyEffect::emitCode(GrGLShaderBuilder* builder,
+                                    const GrEffectStage&,
+                                    EffectKey key,
+                                    const char* vertexCoords,
+                                    const char* outputColor,
+                                    const char* inputColor,
+                                    const TextureSamplerArray& samplers) {
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "ImageIncrement");
+
+    SkString* code = &builder->fFSCode;
+
+    const char* func;
+    switch (fType) {
+        case GrMorphologyEffect::kErode_MorphologyType:
+            code->appendf("\t\t%s = vec4(1, 1, 1, 1);\n", outputColor);
+            func = "min";
+            break;
+        case GrMorphologyEffect::kDilate_MorphologyType:
+            code->appendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
+            func = "max";
+            break;
+        default:
+            GrCrash("Unexpected type");
+            func = ""; // suppress warning
+            break;
+    }
+    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+
+    code->appendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);
+    code->appendf("\t\tfor (int i = 0; i < %d; i++) {\n", this->width());
+    code->appendf("\t\t\t%s = %s(%s, ", outputColor, func, outputColor);
+    builder->appendTextureLookup(&builder->fFSCode, samplers[0], "coord");
+    code->appendf(");\n");
+    code->appendf("\t\t\tcoord += %s;\n", imgInc);
+    code->appendf("\t\t}\n");
+    GrGLSLMulVarBy4f(code, 2, outputColor, inputColor);
 }
 
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkDilateImageFilter)
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkErodeImageFilter)
+GrGLEffect::EffectKey GrGLMorphologyEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
+    const GrMorphologyEffect& m = GetEffectFromStage<GrMorphologyEffect>(s);
+    EffectKey key = static_cast<EffectKey>(m.radius());
+    key |= (m.type() << 8);
+    key <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey matrixKey = GrGLEffectMatrix::GenKey(m.getMatrix(),
+                                                   s.getCoordChangeMatrix(),
+                                                   m.texture(0));
+    return key | matrixKey;
+}
+
+void GrGLMorphologyEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const Gr1DKernelEffect& kern = GetEffectFromStage<Gr1DKernelEffect>(stage);
+    GrTexture& texture = *kern.texture(0);
+    // the code we generated was for a specific kernel radius
+    GrAssert(kern.radius() == fRadius);
+    float imageIncrement[2] = { 0 };
+    switch (kern.direction()) {
+        case Gr1DKernelEffect::kX_Direction:
+            imageIncrement[0] = 1.0f / texture.width();
+            break;
+        case Gr1DKernelEffect::kY_Direction:
+            imageIncrement[1] = 1.0f / texture.height();
+            break;
+        default:
+            GrCrash("Unknown filter direction.");
+    }
+    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+    fEffectMatrix.setData(uman, kern.getMatrix(), stage.getCoordChangeMatrix(), kern.texture(0));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrMorphologyEffect::GrMorphologyEffect(GrTexture* texture,
+                                       Direction direction,
+                                       int radius,
+                                       MorphologyType type)
+    : Gr1DKernelEffect(texture, direction, radius)
+    , fType(type) {
+}
+
+GrMorphologyEffect::~GrMorphologyEffect() {
+}
+
+const GrBackendEffectFactory& GrMorphologyEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrMorphologyEffect>::getInstance();
+}
+
+bool GrMorphologyEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrMorphologyEffect& s = CastEffect<GrMorphologyEffect>(sBase);
+    return (this->texture(0) == s.texture(0) &&
+            this->radius() == s.radius() &&
+            this->direction() == s.direction() &&
+            this->type() == s.type());
+}
+
+void GrMorphologyEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // This is valid because the color components of the result of the kernel all come
+    // exactly from existing values in the source texture.
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrMorphologyEffect);
+
+GrEffectRef* GrMorphologyEffect::TestCreate(SkRandom* random,
+                                            GrContext* context,
+                                            GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
+    static const int kMaxRadius = 10;
+    int radius = random->nextRangeU(1, kMaxRadius);
+    MorphologyType type = random->nextBool() ? GrMorphologyEffect::kErode_MorphologyType :
+                                               GrMorphologyEffect::kDilate_MorphologyType;
+
+    return GrMorphologyEffect::Create(textures[texIdx], dir, radius, type);
+}
+
+namespace {
+
+void apply_morphology_pass(GrContext* context,
+                           GrTexture* texture,
+                           const SkIRect& rect,
+                           int radius,
+                           GrMorphologyEffect::MorphologyType morphType,
+                           Gr1DKernelEffect::Direction direction) {
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(GrMorphologyEffect::Create(texture,
+                                                              direction,
+                                                              radius,
+                                                              morphType))->unref();
+    context->drawRect(paint, SkRect::MakeFromIRect(rect));
+}
+
+GrTexture* apply_morphology(GrTexture* srcTexture,
+                            const SkIRect& rect,
+                            GrMorphologyEffect::MorphologyType morphType,
+                            SkISize radius) {
+    GrContext* context = srcTexture->getContext();
+    srcTexture->ref();
+
+    GrContext::AutoMatrix am;
+    am.setIdentity(context);
+
+    GrContext::AutoClip acs(context, GrRect::MakeWH(SkIntToScalar(srcTexture->width()),
+                                                    SkIntToScalar(srcTexture->height())));
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = rect.width();
+    desc.fHeight = rect.height();
+    desc.fConfig = kSkia8888_GrPixelConfig;
+
+    if (radius.fWidth > 0) {
+        GrAutoScratchTexture ast(context, desc);
+        GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
+        apply_morphology_pass(context, srcTexture, rect, radius.fWidth,
+                              morphType, Gr1DKernelEffect::kX_Direction);
+        SkIRect clearRect = SkIRect::MakeXYWH(rect.fLeft, rect.fBottom,
+                                              rect.width(), radius.fHeight);
+        context->clear(&clearRect, 0x0);
+        srcTexture->unref();
+        srcTexture = ast.detach();
+    }
+    if (radius.fHeight > 0) {
+        GrAutoScratchTexture ast(context, desc);
+        GrContext::AutoRenderTarget art(context, ast.texture()->asRenderTarget());
+        apply_morphology_pass(context, srcTexture, rect, radius.fHeight,
+                              morphType, Gr1DKernelEffect::kY_Direction);
+        srcTexture->unref();
+        srcTexture = ast.detach();
+    }
+    return srcTexture;
+}
+
+};
+
+bool SkDilateImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap inputBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) {
+        return false;
+    }
+    GrTexture* input = (GrTexture*) inputBM.getTexture();
+    SkIRect bounds;
+    src.getBounds(&bounds);
+    SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
+        GrMorphologyEffect::kDilate_MorphologyType, radius()));
+    return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
+}
+
+bool SkErodeImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
+    SkBitmap inputBM;
+    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &inputBM)) {
+        return false;
+    }
+    GrTexture* input = (GrTexture*) inputBM.getTexture();
+    SkIRect bounds;
+    src.getBounds(&bounds);
+    SkAutoTUnref<GrTexture> resultTex(apply_morphology(input, bounds,
+        GrMorphologyEffect::kErode_MorphologyType, radius()));
+    return SkImageFilterUtils::WrapTexture(resultTex, src.width(), src.height(), result);
+}
+
+#endif
diff --git a/src/effects/SkOffsetImageFilter.cpp b/src/effects/SkOffsetImageFilter.cpp
new file mode 100644
index 0000000..fb39aa8
--- /dev/null
+++ b/src/effects/SkOffsetImageFilter.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2012 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 "SkOffsetImageFilter.h"
+#include "SkBitmap.h"
+#include "SkMatrix.h"
+#include "SkFlattenableBuffers.h"
+
+bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& source,
+                                        const SkMatrix& matrix,
+                                        SkBitmap* result,
+                                        SkIPoint* loc) {
+    SkBitmap src = this->getInputResult(proxy, source, matrix, loc);
+    SkVector vec;
+    matrix.mapVectors(&vec, &fOffset, 1);
+
+    loc->fX += SkScalarRoundToInt(vec.fX);
+    loc->fY += SkScalarRoundToInt(vec.fY);
+    *result = src;
+    return true;
+}
+
+bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
+                                         SkIRect* dst) {
+    SkVector vec;
+    ctm.mapVectors(&vec, &fOffset, 1);
+
+    *dst = src;
+    dst->offset(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
+    return true;
+}
+
+void SkOffsetImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fOffset);
+}
+
+SkOffsetImageFilter::SkOffsetImageFilter(SkScalar dx, SkScalar dy,
+                                         SkImageFilter* input) : INHERITED(input) {
+    fOffset.set(dx, dy);
+}
+
+SkOffsetImageFilter::SkOffsetImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
+    buffer.readPoint(&fOffset);
+}
diff --git a/src/effects/SkPaintFlagsDrawFilter.cpp b/src/effects/SkPaintFlagsDrawFilter.cpp
index 1fe4402..dc1c007 100644
--- a/src/effects/SkPaintFlagsDrawFilter.cpp
+++ b/src/effects/SkPaintFlagsDrawFilter.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkPaintFlagsDrawFilter.h"
 #include "SkPaint.h"
 
@@ -16,7 +14,7 @@
     fSetFlags = SkToU16(setFlags & SkPaint::kAllFlags);
 }
 
-void SkPaintFlagsDrawFilter::filter(SkPaint* paint, Type) {
+bool SkPaintFlagsDrawFilter::filter(SkPaint* paint, Type) {
     paint->setFlags((paint->getFlags() & ~fClearFlags) | fSetFlags);
+    return true;
 }
-
diff --git a/src/effects/SkPixelXorXfermode.cpp b/src/effects/SkPixelXorXfermode.cpp
index 935a475..b9c96dc 100644
--- a/src/effects/SkPixelXorXfermode.cpp
+++ b/src/effects/SkPixelXorXfermode.cpp
@@ -9,31 +9,30 @@
 
 #include "SkPixelXorXfermode.h"
 #include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkString.h"
 
 // we always return an opaque color, 'cause I don't know what to do with
 // the alpha-component and still return a valid premultiplied color.
-SkPMColor SkPixelXorXfermode::xferColor(SkPMColor src, SkPMColor dst) {
+SkPMColor SkPixelXorXfermode::xferColor(SkPMColor src, SkPMColor dst) const {
     SkPMColor res = src ^ dst ^ fOpColor;
     res |= (SK_A32_MASK << SK_A32_SHIFT);   // force it to be opaque
     return res;
 }
 
-void SkPixelXorXfermode::flatten(SkFlattenableWriteBuffer& wb) {
+void SkPixelXorXfermode::flatten(SkFlattenableWriteBuffer& wb) const {
     this->INHERITED::flatten(wb);
-    wb.write32(fOpColor);
+    wb.writeColor(fOpColor);
 }
 
 SkPixelXorXfermode::SkPixelXorXfermode(SkFlattenableReadBuffer& rb)
-        : SkXfermode(rb) {
-    fOpColor = rb.readU32();
+        : INHERITED(rb) {
+    fOpColor = rb.readColor();
 }
 
-SkFlattenable::Factory SkPixelXorXfermode::getFactory() {
-    return Create;
+#ifdef SK_DEVELOPER
+void SkPixelXorXfermode::toString(SkString* str) const {
+    str->append("SkPixelXorXfermode: ");
+    str->appendHex(fOpColor);
 }
-
-SkFlattenable* SkPixelXorXfermode::Create(SkFlattenableReadBuffer& rb) {
-    return SkNEW_ARGS(SkPixelXorXfermode, (rb));
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkPixelXorXfermode)
+#endif
diff --git a/src/effects/SkPorterDuff.cpp b/src/effects/SkPorterDuff.cpp
index 8acb345..816ddae 100644
--- a/src/effects/SkPorterDuff.cpp
+++ b/src/effects/SkPorterDuff.cpp
@@ -36,7 +36,7 @@
     MAKE_PAIR(Xor),
     MAKE_PAIR(Darken),
     MAKE_PAIR(Lighten),
-    MAKE_PAIR(Multiply),
+    MAKE_PAIR(Modulate),
     MAKE_PAIR(Screen),
     { SkPorterDuff::kAdd_Mode, SkXfermode::kPlus_Mode },
 #ifdef SK_BUILD_FOR_ANDROID
@@ -85,4 +85,3 @@
 SkXfermodeProc16 SkPorterDuff::GetXfermodeProc16(Mode mode, SkColor srcColor) {
     return SkXfermode::GetProc16(gPairs[mode].fXF, srcColor);
 }
-
diff --git a/src/effects/SkRadialGradient_Table.h b/src/effects/SkRadialGradient_Table.h
deleted file mode 100644
index 7db23df..0000000
--- a/src/effects/SkRadialGradient_Table.h
+++ /dev/null
@@ -1,139 +0,0 @@
-
-/*
- * Copyright 2006 The Android Open Source Project
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-static const uint8_t gSqrt8Table[] = {
-    0x00, 0x05, 0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0E, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x15, 
-    0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 
-    0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26, 
-    0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C, 0x2C, 
-    0x2D, 0x2D, 0x2D, 0x2E, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32, 
-    0x32, 0x32, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37, 
-    0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3A, 0x3B, 0x3B, 0x3B, 
-    0x3B, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F, 
-    0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43, 
-    0x43, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47, 
-    0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, 0x4A, 
-    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4E, 
-    0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51, 
-    0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54, 
-    0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57, 
-    0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A, 
-    0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5D, 
-    0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F, 
-    0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62, 
-    0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65, 
-    0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67, 
-    0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 
-    0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6C, 0x6C, 0x6C, 
-    0x6C, 0x6C, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E, 
-    0x6E, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 
-    0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73, 
-    0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75, 
-    0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77, 
-    0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 
-    0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 
-    0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 
-    0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 
-    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 
-    0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 
-    0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 
-    0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 
-    0x87, 0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89, 
-    0x89, 0x89, 0x89, 0x89, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B, 
-    0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8D, 0x8D, 
-    0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 
-    0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 
-    0x90, 0x90, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92, 
-    0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94, 
-    0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 
-    0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 
-    0x97, 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99, 
-    0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B, 
-    0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 
-    0x9C, 0x9C, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9E, 0x9E, 0x9E, 
-    0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 
-    0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 
-    0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA3, 
-    0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 
-    0xA4, 0xA4, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA6, 0xA6, 
-    0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 
-    0xA7, 0xA7, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA9, 0xA9, 0xA9, 
-    0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 
-    0xAA, 0xAA, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAC, 0xAC, 0xAC, 
-    0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 
-    0xAD, 0xAD, 0xAD, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAF, 0xAF, 
-    0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 
-    0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2, 
-    0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 
-    0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 
-    0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB6, 0xB6, 0xB6, 0xB6, 
-    0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 
-    0xB7, 0xB7, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB9, 0xB9, 
-    0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 
-    0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 
-    0xBB, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBD, 0xBD, 0xBD, 
-    0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 
-    0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 
-    0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1, 
-    0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 
-    0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 
-    0xC3, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC5, 0xC5, 0xC5, 
-    0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 
-    0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 
-    0xC7, 0xC7, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC9, 
-    0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xCA, 0xCA, 0xCA, 0xCA, 
-    0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 
-    0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 
-    0xCC, 0xCC, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCE, 
-    0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF, 
-    0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 
-    0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 
-    0xD1, 0xD1, 0xD1, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 
-    0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD4, 0xD4, 0xD4, 
-    0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 
-    0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 
-    0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 
-    0xD7, 0xD7, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 
-    0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA, 
-    0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 
-    0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 
-    0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 
-    0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 
-    0xDE, 0xDE, 0xDE, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 
-    0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE1, 
-    0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2, 
-    0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 
-    0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 
-    0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 
-    0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 
-    0xE6, 0xE6, 0xE6, 0xE6, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 
-    0xE7, 0xE7, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 
-    0xE8, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 
-    0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEB, 0xEB, 
-    0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEC, 0xEC, 0xEC, 
-    0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0xED, 0xED, 0xED, 
-    0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 
-    0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 
-    0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 
-    0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 
-    0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 
-    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 
-    0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 
-    0xF4, 0xF4, 0xF4, 0xF4, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 
-    0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 
-    0xF6, 0xF6, 0xF6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 
-    0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 
-    0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 
-    0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 
-    0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 
-    0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 
-    0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 
-    0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 
-    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
-};
diff --git a/src/effects/SkRectShape.cpp b/src/effects/SkRectShape.cpp
deleted file mode 100644
index 3e37072..0000000
--- a/src/effects/SkRectShape.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#include "SkRectShape.h"
-#include "SkCanvas.h"
-
-SkPaintShape::SkPaintShape() {
-    fPaint.setAntiAlias(true);
-}
-
-SkRectShape::SkRectShape() {
-    fBounds.setEmpty();
-    fRadii.set(0, 0);
-}
-
-void SkRectShape::setRect(const SkRect& bounds) {
-    fBounds = bounds;
-    fRadii.set(0, 0);
-}
-
-void SkRectShape::setOval(const SkRect& bounds) {
-    fBounds = bounds;
-    fRadii.set(-SK_Scalar1, -SK_Scalar1);
-}
-
-void SkRectShape::setCircle(SkScalar cx, SkScalar cy, SkScalar radius) {
-    fBounds.set(cx - radius, cy - radius, cx + radius, cy + radius);
-    fRadii.set(-SK_Scalar1, -SK_Scalar1);
-}
-
-void SkRectShape::setRRect(const SkRect& bounds, SkScalar rx, SkScalar ry) {
-    if (rx < 0) {
-        rx = 0;
-    }
-    if (ry < 0) {
-        ry = 0;
-    }
-
-    fBounds = bounds;
-    fRadii.set(rx, ry);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkRectShape::onDraw(SkCanvas* canvas) {
-    const SkPaint& paint = this->paint();
-
-    if (fRadii.fWidth < 0) {
-        canvas->drawOval(fBounds, paint);
-    } else if (fRadii.isZero()) {
-        canvas->drawRect(fBounds, paint);
-    } else {
-        canvas->drawRoundRect(fBounds, fRadii.fWidth, fRadii.fHeight, paint);
-    }
-}
-
-SkFlattenable::Factory SkRectShape::getFactory() {
-    return CreateProc;
-}
-
-void SkRectShape::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-
-    buffer.writeRect(fBounds);
-    *(SkSize*)buffer.reserve(sizeof(SkSize)) = fRadii;
-}
-
-SkRectShape::SkRectShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {    
-    buffer.read(&fBounds, sizeof(fBounds));
-    buffer.read(&fRadii, sizeof(fRadii));
-}
-
-SkFlattenable* SkRectShape::CreateProc(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkRectShape, (buffer));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkPaintShape::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-    
-    fPaint.flatten(buffer);
-}
-
-SkPaintShape::SkPaintShape(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    fPaint.unflatten(buffer);
-}
-
-SK_DEFINE_FLATTENABLE_REGISTRAR(SkRectShape)
-
diff --git a/src/effects/SkSingleInputImageFilter.cpp b/src/effects/SkSingleInputImageFilter.cpp
new file mode 100644
index 0000000..9e45c35
--- /dev/null
+++ b/src/effects/SkSingleInputImageFilter.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "SkSingleInputImageFilter.h"
+#include "SkBitmap.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMatrix.h"
+
+SkSingleInputImageFilter::SkSingleInputImageFilter(SkImageFilter* input) : INHERITED(input) {
+}
+
+SkSingleInputImageFilter::~SkSingleInputImageFilter() {
+}
+
+SkSingleInputImageFilter::SkSingleInputImageFilter(SkFlattenableReadBuffer& rb)
+    : INHERITED(rb) {
+}
+
+void SkSingleInputImageFilter::flatten(SkFlattenableWriteBuffer& wb) const {
+    this->INHERITED::flatten(wb);
+}
+
+SkBitmap SkSingleInputImageFilter::getInputResult(Proxy* proxy,
+                                                  const SkBitmap& src,
+                                                  const SkMatrix& ctm,
+                                                  SkIPoint* offset) {
+    return this->INHERITED::getInputResult(0, proxy, src, ctm, offset);
+}
diff --git a/src/effects/SkStippleMaskFilter.cpp b/src/effects/SkStippleMaskFilter.cpp
new file mode 100644
index 0000000..24b9114
--- /dev/null
+++ b/src/effects/SkStippleMaskFilter.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 "SkStippleMaskFilter.h"
+
+
+bool SkStippleMaskFilter::filterMask(SkMask* dst,
+                                     const SkMask& src,
+                                     const SkMatrix& matrix,
+                                     SkIPoint* margin) const {
+
+    if (src.fFormat != SkMask::kA8_Format) {
+        return false;
+    }
+
+    dst->fBounds = src.fBounds;
+    dst->fRowBytes = dst->fBounds.width();
+    dst->fFormat = SkMask::kA8_Format;
+    dst->fImage = NULL;
+
+    if (NULL != src.fImage) {
+        size_t dstSize = dst->computeImageSize();
+        if (0 == dstSize) {
+            return false;   // too big to allocate, abort
+        }
+
+        dst->fImage = SkMask::AllocImage(dstSize);
+
+        uint8_t* srcScanLine = src.fImage;
+        uint8_t* scanline = dst->fImage;
+
+        for (int y = 0; y < src.fBounds.height(); ++y) {
+            for (int x = 0; x < src.fBounds.width(); ++x) {
+                scanline[x] = srcScanLine[x] && ((x+y) & 0x1) ? 0xFF : 0x00;
+            }
+            scanline += dst->fRowBytes;
+            srcScanLine += src.fRowBytes;
+        }
+    }
+
+    return true;
+}
diff --git a/src/effects/SkTableColorFilter.cpp b/src/effects/SkTableColorFilter.cpp
index 800d4a9..5c00794 100644
--- a/src/effects/SkTableColorFilter.cpp
+++ b/src/effects/SkTableColorFilter.cpp
@@ -1,5 +1,8 @@
-#include "SkColorPriv.h"
+
+#include "SkBitmap.h"
 #include "SkTableColorFilter.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
 #include "SkUnPreMultiply.h"
 
 class SkTable_ColorFilter : public SkColorFilter {
@@ -31,22 +34,20 @@
         }
     }
 
-    virtual bool asComponentTable(SkBitmap* table) SK_OVERRIDE;
-
-    virtual void filterSpan(const SkPMColor src[], int count,
-                            SkPMColor dst[]) SK_OVERRIDE;
-    virtual void flatten(SkFlattenableWriteBuffer&) SK_OVERRIDE;
-    virtual Factory getFactory() SK_OVERRIDE;
-
-    static SkFlattenable* CreateProc(SkFlattenableReadBuffer& buffer) {
-        return SkNEW_ARGS(SkTable_ColorFilter, (buffer));
+    virtual ~SkTable_ColorFilter() {
+        SkDELETE(fBitmap);
     }
 
-protected:
-    SkTable_ColorFilter(SkFlattenableReadBuffer& buffer);
+    virtual bool asComponentTable(SkBitmap* table) const SK_OVERRIDE;
 
-private:
-    SkBitmap* fBitmap;
+#if SK_SUPPORT_GPU
+    virtual GrEffectRef* asNewEffect(GrContext* context) const SK_OVERRIDE;
+#endif
+
+    virtual void filterSpan(const SkPMColor src[], int count,
+                            SkPMColor dst[]) const SK_OVERRIDE;
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTable_ColorFilter)
 
     enum {
         kA_Flag = 1 << 0,
@@ -54,6 +55,14 @@
         kG_Flag = 1 << 2,
         kB_Flag = 1 << 3,
     };
+
+protected:
+    SkTable_ColorFilter(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    mutable const SkBitmap* fBitmap; // lazily allocated
+
     uint8_t fStorage[256 * 4];
     unsigned fFlags;
 
@@ -61,42 +70,42 @@
 };
 
 static const uint8_t gIdentityTable[] = {
-    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 
-    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 
-    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 
-    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 
-    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 
-    0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 
-    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 
-    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 
-    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 
-    0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 
-    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 
-    0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 
-    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 
-    0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 
-    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 
-    0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 
-    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 
-    0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 
-    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 
-    0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 
-    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 
-    0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 
-    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 
-    0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF, 
-    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 
-    0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 
-    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 
-    0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 
-    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 
-    0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 
-    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 
+    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+    0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
+    0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
+    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+    0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
+    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+    0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F,
+    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+    0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F,
+    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
+    0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F,
+    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
+    0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
+    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+    0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F,
+    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+    0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F,
+    0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
+    0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF,
+    0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7,
+    0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
+    0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7,
+    0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF,
+    0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7,
+    0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF,
+    0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7,
+    0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xEF,
+    0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7,
     0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
 };
 
 void SkTable_ColorFilter::filterSpan(const SkPMColor src[], int count,
-                                     SkPMColor dst[]) {
+                                     SkPMColor dst[]) const {
     const uint8_t* table = fStorage;
     const uint8_t* tableA = gIdentityTable;
     const uint8_t* tableR = gIdentityTable;
@@ -139,10 +148,6 @@
     }
 }
 
-SkFlattenable::Factory SkTable_ColorFilter::getFactory() {
-    return CreateProc;
-}
-
 static const uint8_t gCountNibBits[] = {
     0, 1, 1, 2,
     1, 2, 2, 3,
@@ -152,7 +157,7 @@
 
 #include "SkPackBits.h"
 
-void SkTable_ColorFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkTable_ColorFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
     uint8_t storage[5*256];
@@ -161,10 +166,9 @@
     SkASSERT(size <= sizeof(storage));
 
 //    SkDebugf("raw %d packed %d\n", count * 256, size);
-    
+
     buffer.writeInt(fFlags);
-    buffer.writeInt(size);
-    buffer.write(storage, size);
+    buffer.writeByteArray(storage, size);
 }
 
 SkTable_ColorFilter::SkTable_ColorFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
@@ -173,29 +177,222 @@
     uint8_t storage[5*256];
 
     fFlags = buffer.readInt();
-    size_t size = buffer.readInt();
-    buffer.read(storage, size);
 
-    size_t raw = SkPackBits::Unpack8(storage, size, fStorage);
+    size_t size = buffer.getArrayCount();
+    SkASSERT(size <= sizeof(storage));
+    buffer.readByteArray(storage);
+
+    SkDEBUGCODE(size_t raw = ) SkPackBits::Unpack8(storage, size, fStorage);
 
     SkASSERT(raw <= sizeof(fStorage));
-    size_t count = gCountNibBits[fFlags & 0xF];
+    SkDEBUGCODE(size_t count = gCountNibBits[fFlags & 0xF]);
     SkASSERT(raw == count * 256);
 }
 
-bool SkTable_ColorFilter::asComponentTable(SkBitmap* table) {
+bool SkTable_ColorFilter::asComponentTable(SkBitmap* table) const {
     if (table) {
         if (NULL == fBitmap) {
-            fBitmap = new SkBitmap;
-            fBitmap->setConfig(SkBitmap::kA8_Config, 256, 4, 256);
-            fBitmap->allocPixels();
-            memcpy(fBitmap->getAddr8(0, 0), fStorage, 256 * 4);
+            SkBitmap* bmp = SkNEW(SkBitmap);
+            bmp->setConfig(SkBitmap::kA8_Config, 256, 4, 256);
+            bmp->allocPixels();
+            uint8_t* bitmapPixels = bmp->getAddr8(0, 0);
+            int offset = 0;
+            static const unsigned kFlags[] = { kA_Flag, kR_Flag, kG_Flag, kB_Flag };
+
+            for (int x = 0; x < 4; ++x) {
+                if (!(fFlags & kFlags[x])) {
+                    memcpy(bitmapPixels, gIdentityTable, sizeof(gIdentityTable));
+                } else {
+                    memcpy(bitmapPixels, fStorage + offset, 256);
+                    offset += 256;
+                }
+                bitmapPixels += 256;
+            }
+            fBitmap = bmp;
         }
         *table = *fBitmap;
     }
     return true;
 }
 
+#if SK_SUPPORT_GPU
+
+#include "GrEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+#include "SkGr.h"
+
+class GLColorTableEffect;
+
+class ColorTableEffect : public GrEffect {
+public:
+    static GrEffectRef* Create(GrTexture* texture, unsigned flags) {
+        AutoEffectUnref effect(SkNEW_ARGS(ColorTableEffect, (texture, flags)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~ColorTableEffect();
+
+    static const char* Name() { return "ColorTable"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    typedef GLColorTableEffect GLEffect;
+
+private:
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    explicit ColorTableEffect(GrTexture* texture, unsigned flags);
+
+    GR_DECLARE_EFFECT_TEST;
+
+    GrTextureAccess fTextureAccess;
+    unsigned        fFlags; // currently not used in shader code, just to assist
+                            // getConstantColorComponents().
+
+    typedef GrEffect INHERITED;
+};
+
+class GLColorTableEffect : public GrGLEffect {
+public:
+    GLColorTableEffect(const GrBackendEffectFactory&, const GrEffectRef&);
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE {}
+
+    static EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+private:
+
+    typedef GrGLEffect INHERITED;
+};
+
+GLColorTableEffect::GLColorTableEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
+    : INHERITED(factory) {
+ }
+
+void GLColorTableEffect::emitCode(GrGLShaderBuilder* builder,
+                                  const GrEffectStage&,
+                                  EffectKey,
+                                  const char* vertexCoords,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TextureSamplerArray& samplers) {
+
+    static const float kColorScaleFactor = 255.0f / 256.0f;
+    static const float kColorOffsetFactor = 1.0f / 512.0f;
+    SkString* code = &builder->fFSCode;
+    if (NULL == inputColor) {
+        // the input color is solid white (all ones).
+        static const float kMaxValue = kColorScaleFactor + kColorOffsetFactor;
+        code->appendf("\t\tvec4 coord = vec4(%f, %f, %f, %f);\n",
+                      kMaxValue, kMaxValue, kMaxValue, kMaxValue);
+
+    } else {
+        code->appendf("\t\tfloat nonZeroAlpha = max(%s.a, .0001);\n", inputColor);
+        code->appendf("\t\tvec4 coord = vec4(%s.rgb / nonZeroAlpha, nonZeroAlpha);\n", inputColor);
+        code->appendf("\t\tcoord = coord * %f + vec4(%f, %f, %f, %f);\n",
+                      kColorScaleFactor,
+                      kColorOffsetFactor, kColorOffsetFactor,
+                      kColorOffsetFactor, kColorOffsetFactor);
+    }
+
+    code->appendf("\t\t%s.a = ", outputColor);
+    builder->appendTextureLookup(code, samplers[0], "vec2(coord.a, 0.125)");
+    code->append(";\n");
+
+    code->appendf("\t\t%s.r = ", outputColor);
+    builder->appendTextureLookup(code, samplers[0], "vec2(coord.r, 0.375)");
+    code->append(";\n");
+
+    code->appendf("\t\t%s.g = ", outputColor);
+    builder->appendTextureLookup(code, samplers[0], "vec2(coord.g, 0.625)");
+    code->append(";\n");
+
+    code->appendf("\t\t%s.b = ", outputColor);
+    builder->appendTextureLookup(code, samplers[0], "vec2(coord.b, 0.875)");
+    code->append(";\n");
+
+    code->appendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
+}
+
+GrGLEffect::EffectKey GLColorTableEffect::GenKey(const GrEffectStage&, const GrGLCaps&) {
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ColorTableEffect::ColorTableEffect(GrTexture* texture, unsigned flags)
+    : fTextureAccess(texture, "a")
+    , fFlags(flags) {
+    this->addTextureAccess(&fTextureAccess);
+}
+
+ColorTableEffect::~ColorTableEffect() {
+}
+
+const GrBackendEffectFactory&  ColorTableEffect::getFactory() const {
+    return GrTBackendEffectFactory<ColorTableEffect>::getInstance();
+}
+
+bool ColorTableEffect::onIsEqual(const GrEffect& sBase) const {
+    return this->texture(0) == sBase.texture(0);
+}
+
+void ColorTableEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    // If we kept the table in the effect then we could actually run known inputs through the
+    // table.
+    if (fFlags & SkTable_ColorFilter::kR_Flag) {
+        *validFlags &= ~kR_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kG_Flag) {
+        *validFlags &= ~kG_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kB_Flag) {
+        *validFlags &= ~kB_ValidComponentFlag;
+    }
+    if (fFlags & SkTable_ColorFilter::kA_Flag) {
+        *validFlags &= ~kA_ValidComponentFlag;
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(ColorTableEffect);
+
+GrEffectRef* ColorTableEffect::TestCreate(SkRandom* random,
+                                          GrContext* context,
+                                          GrTexture* textures[]) {
+    static unsigned kAllFlags = SkTable_ColorFilter::kR_Flag | SkTable_ColorFilter::kG_Flag |
+                                SkTable_ColorFilter::kB_Flag | SkTable_ColorFilter::kA_Flag;
+    return ColorTableEffect::Create(textures[GrEffectUnitTest::kAlphaTextureIdx], kAllFlags);
+}
+
+GrEffectRef* SkTable_ColorFilter::asNewEffect(GrContext* context) const {
+    SkBitmap bitmap;
+    this->asComponentTable(&bitmap);
+    // passing NULL because this effect does no tiling or filtering.
+    GrTexture* texture = GrLockAndRefCachedBitmapTexture(context, bitmap, NULL);
+    GrEffectRef* effect = ColorTableEffect::Create(texture, fFlags);
+
+    // Unlock immediately, this is not great, but we don't have a way of
+    // knowing when else to unlock it currently. TODO: Remove this when
+    // unref becomes the unlock replacement for all types of textures.
+    GrUnlockAndUnrefCachedBitmapTexture(texture);
+    return effect;
+}
+
+#endif // SK_SUPPORT_GPU
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #ifdef SK_CPU_BENDIAN
@@ -218,3 +415,7 @@
                                               const uint8_t tableB[256]) {
     return SkNEW_ARGS(SkTable_ColorFilter, (tableA, tableR, tableG, tableB));
 }
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkTableColorFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkTable_ColorFilter)
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_END
diff --git a/src/effects/SkTableMaskFilter.cpp b/src/effects/SkTableMaskFilter.cpp
index 4024372..ada5d01 100644
--- a/src/effects/SkTableMaskFilter.cpp
+++ b/src/effects/SkTableMaskFilter.cpp
@@ -8,6 +8,7 @@
 
 
 #include "SkTableMaskFilter.h"
+#include "SkFlattenableBuffers.h"
 
 SkTableMaskFilter::SkTableMaskFilter() {
     for (int i = 0; i < 256; i++) {
@@ -16,17 +17,13 @@
 }
 
 SkTableMaskFilter::SkTableMaskFilter(const uint8_t table[256]) {
-    this->setTable(table);
+    memcpy(fTable, table, sizeof(fTable));
 }
 
 SkTableMaskFilter::~SkTableMaskFilter() {}
 
-void SkTableMaskFilter::setTable(const uint8_t table[256]) {
-    memcpy(fTable, table, 256);
-}
-
 bool SkTableMaskFilter::filterMask(SkMask* dst, const SkMask& src,
-                                 const SkMatrix&, SkIPoint* margin) {
+                                 const SkMatrix&, SkIPoint* margin) const {
     if (src.fFormat != SkMask::kA8_Format) {
         return false;
     }
@@ -35,16 +32,16 @@
     dst->fRowBytes = SkAlign4(dst->fBounds.width());
     dst->fFormat = SkMask::kA8_Format;
     dst->fImage = NULL;
-    
+
     if (src.fImage) {
         dst->fImage = SkMask::AllocImage(dst->computeImageSize());
-        
+
         const uint8_t* srcP = src.fImage;
         uint8_t* dstP = dst->fImage;
         const uint8_t* table = fTable;
         int dstWidth = dst->fBounds.width();
         int extraZeros = dst->fRowBytes - dstWidth;
-        
+
         for (int y = dst->fBounds.height() - 1; y >= 0; --y) {
             for (int x = dstWidth - 1; x >= 0; --x) {
                 dstP[x] = table[srcP[x]];
@@ -67,26 +64,19 @@
     return true;
 }
 
-SkMask::Format SkTableMaskFilter::getFormat() {
+SkMask::Format SkTableMaskFilter::getFormat() const {
     return SkMask::kA8_Format;
 }
 
-void SkTableMaskFilter::flatten(SkFlattenableWriteBuffer& wb) {
+void SkTableMaskFilter::flatten(SkFlattenableWriteBuffer& wb) const {
     this->INHERITED::flatten(wb);
-    wb.writePad(fTable, 256);
+    wb.writeByteArray(fTable, 256);
 }
 
 SkTableMaskFilter::SkTableMaskFilter(SkFlattenableReadBuffer& rb)
         : INHERITED(rb) {
-    rb.read(fTable, 256);
-}
-
-SkFlattenable* SkTableMaskFilter::Factory(SkFlattenableReadBuffer& rb) {
-    return SkNEW_ARGS(SkTableMaskFilter, (rb));
-}
-
-SkFlattenable::Factory SkTableMaskFilter::getFactory() {
-    return SkTableMaskFilter::Factory;
+    SkASSERT(256 == rb.getArrayCount());
+    rb.readByteArray(fTable);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -97,7 +87,7 @@
 
     float x = 0;
     for (int i = 0; i < 256; i++) {
-        float ee = powf(x, g) * 255;
+     // float ee = powf(x, g) * 255;
         table[i] = SkPin32(sk_float_round2int(powf(x, g) * 255), 0, 255);
         x += dx;
     }
@@ -136,4 +126,3 @@
     SkDebugf("\n\n");
 #endif
 }
-
diff --git a/src/effects/SkTestImageFilters.cpp b/src/effects/SkTestImageFilters.cpp
index a772f64..c280555 100755
--- a/src/effects/SkTestImageFilters.cpp
+++ b/src/effects/SkTestImageFilters.cpp
@@ -1,7 +1,16 @@
+
 #include "SkTestImageFilters.h"
 #include "SkCanvas.h"
 #include "SkDevice.h"
+#include "SkFlattenableBuffers.h"
 
+// Simple helper canvas that "takes ownership" of the provided device, so that
+// when this canvas goes out of scope, so will its device. Could be replaced
+// with the following:
+//
+//  SkCanvas canvas(device);
+//  SkAutoTUnref<SkDevice> aur(device);
+//
 class OwnDeviceCanvas : public SkCanvas {
 public:
     OwnDeviceCanvas(SkDevice* device) : SkCanvas(device) {
@@ -9,49 +18,9 @@
     }
 };
 
-bool SkOffsetImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
-                                        const SkMatrix& matrix,
-                                        SkBitmap* result,
-                                        SkIPoint* loc) {
-    SkVector vec;
-    matrix.mapVectors(&vec, &fOffset, 1);
-    
-    loc->fX += SkScalarRoundToInt(vec.fX);
-    loc->fY += SkScalarRoundToInt(vec.fY);
-    *result = src;
-    return true;
-}
-
-bool SkOffsetImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                         SkIRect* dst) {
-    SkVector vec;
-    ctm.mapVectors(&vec, &fOffset, 1);
-
-    *dst = src;
-    dst->offset(SkScalarRoundToInt(vec.fX), SkScalarRoundToInt(vec.fY));
-    return true;
-}
-
-void SkOffsetImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-    buffer.writeScalar(fOffset.x());
-    buffer.writeScalar(fOffset.y());
-}
-
-SkOffsetImageFilter::SkOffsetImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    fOffset.fX = buffer.readScalar();
-    fOffset.fY = buffer.readScalar();
-}
-
-SkFlattenable::Factory SkOffsetImageFilter::getFactory() {
-    return CreateProc;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 SkComposeImageFilter::~SkComposeImageFilter() {
-    SkSafeUnref(fInner);
-    SkSafeUnref(fOuter);
 }
 
 bool SkComposeImageFilter::onFilterImage(Proxy* proxy,
@@ -59,278 +28,42 @@
                                          const SkMatrix& ctm,
                                          SkBitmap* result,
                                          SkIPoint* loc) {
-    if (!fOuter && !fInner) {
+    SkImageFilter* outer = getInput(0);
+    SkImageFilter* inner = getInput(1);
+
+    if (!outer && !inner) {
         return false;
     }
-    
-    if (!fOuter || !fInner) {
-        return (fOuter ? fOuter : fInner)->filterImage(proxy, src, ctm, result, loc);
+
+    if (!outer || !inner) {
+        return (outer ? outer : inner)->filterImage(proxy, src, ctm, result, loc);
     }
-    
+
     SkBitmap tmp;
-    return fInner->filterImage(proxy, src, ctm, &tmp, loc) &&
-           fOuter->filterImage(proxy, tmp, ctm, result, loc);
+    return inner->filterImage(proxy, src, ctm, &tmp, loc) &&
+           outer->filterImage(proxy, tmp, ctm, result, loc);
 }
 
 bool SkComposeImageFilter::onFilterBounds(const SkIRect& src,
                                           const SkMatrix& ctm,
                                           SkIRect* dst) {
-    if (!fOuter && !fInner) {
+    SkImageFilter* outer = getInput(0);
+    SkImageFilter* inner = getInput(1);
+
+    if (!outer && !inner) {
         return false;
     }
-    
-    if (!fOuter || !fInner) {
-        return (fOuter ? fOuter : fInner)->filterBounds(src, ctm, dst);
+
+    if (!outer || !inner) {
+        return (outer ? outer : inner)->filterBounds(src, ctm, dst);
     }
-    
+
     SkIRect tmp;
-    return fInner->filterBounds(src, ctm, &tmp) &&
-           fOuter->filterBounds(tmp, ctm, dst);
-}
-
-void SkComposeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-
-    buffer.writeFlattenable(fOuter);
-    buffer.writeFlattenable(fInner);
+    return inner->filterBounds(src, ctm, &tmp) &&
+           outer->filterBounds(tmp, ctm, dst);
 }
 
 SkComposeImageFilter::SkComposeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    fOuter = (SkImageFilter*)buffer.readFlattenable();
-    fInner = (SkImageFilter*)buffer.readFlattenable();
-}
-
-SkFlattenable::Factory SkComposeImageFilter::getFactory() {
-    return CreateProc;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-template <typename T> T* SkSafeRefReturn(T* obj) {
-    SkSafeRef(obj);
-    return obj;
-}
-
-void SkMergeImageFilter::initAlloc(int count, bool hasModes) {
-    if (count < 1) {
-        fFilters = NULL;
-        fModes = NULL;
-        fCount = 0;
-    } else {
-        int modeCount = hasModes ? count : 0;
-        size_t size = sizeof(SkImageFilter*) * count + sizeof(uint8_t) * modeCount;
-        if (size <= sizeof(fStorage)) {
-            fFilters = SkTCast<SkImageFilter**>(fStorage);
-        } else {
-            fFilters = SkTCast<SkImageFilter**>(sk_malloc_throw(size));
-        }
-        fModes = hasModes ? SkTCast<uint8_t*>(fFilters + count) : NULL;
-        fCount = count;
-    }
-}
-
-void SkMergeImageFilter::init(SkImageFilter* const filters[], int count,
-                              const SkXfermode::Mode modes[]) {
-    this->initAlloc(count, !!modes);
-    for (int i = 0; i < count; ++i) {
-        fFilters[i] = SkSafeRefReturn(filters[i]);
-        if (modes) {
-            fModes[i] = SkToU8(modes[i]);
-        }
-    }
-}
-
-SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* first, SkImageFilter* second,
-                                       SkXfermode::Mode mode) {
-    SkImageFilter* filters[] = { first, second };
-    SkXfermode::Mode modes[] = { mode, mode };
-    this->init(filters, 2, SkXfermode::kSrcOver_Mode == mode ? NULL : modes);
-}
-
-SkMergeImageFilter::SkMergeImageFilter(SkImageFilter* const filters[], int count,
-                                       const SkXfermode::Mode modes[]) {
-    this->init(filters, count, modes);
-}
-
-SkMergeImageFilter::~SkMergeImageFilter() {
-    for (int i = 0; i < fCount; ++i) {
-        SkSafeUnref(fFilters[i]);
-    }
-
-    if (fFilters != SkTCast<SkImageFilter**>(fStorage)) {
-        sk_free(fFilters);
-        // fModes is allocated in the same block as fFilters, so no need to
-        // separately free it.
-    }
-}
-
-bool SkMergeImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
-                                        SkIRect* dst) {
-    if (fCount < 1) {
-        return false;
-    }
-
-    SkIRect totalBounds;
-    
-    for (int i = 0; i < fCount; ++i) {
-        SkImageFilter* filter = fFilters[i];
-        SkIRect r;
-        if (filter) {
-            if (!filter->filterBounds(src, ctm, &r)) {
-                return false;
-            }
-        } else {
-            r = src;
-        }
-        if (0 == i) {
-            totalBounds = r;
-        } else {
-            totalBounds.join(r);
-        }
-    }
-
-    // don't modify dst until now, so we don't accidentally change it in the
-    // loop, but then return false on the next filter.
-    *dst = totalBounds;
-    return true;
-}
-
-bool SkMergeImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
-                                       const SkMatrix& ctm,
-                                       SkBitmap* result, SkIPoint* loc) {
-    if (fCount < 1) {
-        return false;
-    }
-
-    const SkIRect srcBounds = SkIRect::MakeXYWH(loc->x(), loc->y(),
-                                                src.width(), src.height());
-    SkIRect bounds;
-    if (!this->filterBounds(srcBounds, ctm, &bounds)) {
-        return false;
-    }
-
-    const int x0 = bounds.left();
-    const int y0 = bounds.top();
-
-    SkDevice* dst = proxy->createDevice(bounds.width(), bounds.height());
-    if (NULL == dst) {
-        return false;
-    }
-    OwnDeviceCanvas canvas(dst);
-    SkPaint paint;
-
-    for (int i = 0; i < fCount; ++i) {
-        SkBitmap tmp;
-        const SkBitmap* srcPtr;
-        SkIPoint pos = *loc;
-        SkImageFilter* filter = fFilters[i];
-        if (filter) {
-            if (!filter->filterImage(proxy, src, ctm, &tmp, &pos)) {
-                return false;
-            }
-            srcPtr = &tmp;
-        } else {
-            srcPtr = &src;
-        }
-        
-        if (fModes) {
-            paint.setXfermodeMode((SkXfermode::Mode)fModes[i]);
-        } else {
-            paint.setXfermode(NULL);
-        }
-        canvas.drawSprite(*srcPtr, pos.x() - x0, pos.y() - y0, &paint);
-    }
-
-    loc->set(bounds.left(), bounds.top());
-    *result = dst->accessBitmap(false);
-    return true;
-}
-
-void SkMergeImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-
-    int storedCount = fCount;
-    if (fModes) {
-        // negative count signals we have modes
-        storedCount = -storedCount;
-    }
-    buffer.write32(storedCount);
-
-    if (fCount) {
-        for (int i = 0; i < fCount; ++i) {
-            buffer.writeFlattenable(fFilters[i]);
-        }
-        if (fModes) {
-            buffer.write(fModes, fCount * sizeof(fModes[0]));
-        }
-    }
-}
-
-SkMergeImageFilter::SkMergeImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    int storedCount = buffer.readS32();
-    this->initAlloc(SkAbs32(storedCount), storedCount < 0);
-
-    for (int i = 0; i < fCount; ++i) {
-        fFilters[i] = (SkImageFilter*)buffer.readFlattenable();
-    }
-
-    if (fModes) {
-        SkASSERT(storedCount < 0);
-        buffer.read(fModes, fCount * sizeof(fModes[0]));
-    } else {
-        SkASSERT(storedCount >= 0);
-    }
-}
-
-SkFlattenable::Factory SkMergeImageFilter::getFactory() {
-    return CreateProc;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkColorFilter.h"
-
-SkColorFilterImageFilter::~SkColorFilterImageFilter() {
-    SkSafeUnref(fColorFilter);
-}
-
-bool SkColorFilterImageFilter::onFilterImage(Proxy* proxy, const SkBitmap& src,
-                                             const SkMatrix& matrix,
-                                             SkBitmap* result,
-                                             SkIPoint* loc) {
-    SkColorFilter* cf = fColorFilter;
-    if (NULL == cf) {
-        *result = src;
-        return true;
-    }
-
-    SkDevice* dev = proxy->createDevice(src.width(), src.height());
-    if (NULL == dev) {
-        return false;
-    }
-    OwnDeviceCanvas canvas(dev);
-    SkPaint paint;
-
-    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    paint.setColorFilter(fColorFilter);
-    canvas.drawSprite(src, 0, 0, &paint);
-
-    *result = dev->accessBitmap(false);
-    return true;
-}
-
-void SkColorFilterImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-    
-    buffer.writeFlattenable(fColorFilter);
-}
-
-SkColorFilterImageFilter::SkColorFilterImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
-    fColorFilter = (SkColorFilter*)buffer.readFlattenable();
-}
-
-SkFlattenable::Factory SkColorFilterImageFilter::getFactory() {
-    return CreateProc;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -342,7 +75,7 @@
     if (scale > SK_Scalar1 || scale <= 0) {
         return false;
     }
-    
+
     int dstW = SkScalarRoundToInt(src.width() * scale);
     int dstH = SkScalarRoundToInt(src.height() * scale);
     if (dstW < 1) {
@@ -385,17 +118,12 @@
     return true;
 }
 
-void SkDownSampleImageFilter::flatten(SkFlattenableWriteBuffer& buffer) {
+void SkDownSampleImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
-    
+
     buffer.writeScalar(fScale);
 }
 
 SkDownSampleImageFilter::SkDownSampleImageFilter(SkFlattenableReadBuffer& buffer) : INHERITED(buffer) {
     fScale = buffer.readScalar();
 }
-
-SkFlattenable::Factory SkDownSampleImageFilter::getFactory() {
-    return CreateProc;
-}
-
diff --git a/src/effects/SkTransparentShader.cpp b/src/effects/SkTransparentShader.cpp
index 486fc89..7b92fde 100644
--- a/src/effects/SkTransparentShader.cpp
+++ b/src/effects/SkTransparentShader.cpp
@@ -9,6 +9,7 @@
 
 #include "SkTransparentShader.h"
 #include "SkColorPriv.h"
+#include "SkString.h"
 
 bool SkTransparentShader::setContext(const SkBitmap& device,
                                      const SkPaint& paint,
@@ -126,11 +127,12 @@
     }
 }
 
-SkFlattenable::Factory SkTransparentShader::getFactory() {
-    return Create;
-}
+#ifdef SK_DEVELOPER
+void SkTransparentShader::toString(SkString* str) const {
+    str->append("SkTransparentShader: (");
 
-void SkTransparentShader::flatten(SkFlattenableWriteBuffer& buffer) {
-    this->INHERITED::flatten(buffer);
-}
+    this->INHERITED::toString(str);
 
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkBitmapCache.cpp b/src/effects/gradients/SkBitmapCache.cpp
new file mode 100644
index 0000000..95175e4
--- /dev/null
+++ b/src/effects/gradients/SkBitmapCache.cpp
@@ -0,0 +1,153 @@
+
+/*
+ * 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 "SkBitmapCache.h"
+
+struct SkBitmapCache::Entry {
+    Entry*      fPrev;
+    Entry*      fNext;
+
+    void*       fBuffer;
+    size_t      fSize;
+    SkBitmap    fBitmap;
+
+    Entry(const void* buffer, size_t size, const SkBitmap& bm)
+            : fPrev(NULL),
+              fNext(NULL),
+              fBitmap(bm) {
+        fBuffer = sk_malloc_throw(size);
+        fSize = size;
+        memcpy(fBuffer, buffer, size);
+    }
+
+    ~Entry() { sk_free(fBuffer); }
+
+    bool equals(const void* buffer, size_t size) const {
+        return (fSize == size) && !memcmp(fBuffer, buffer, size);
+    }
+};
+
+SkBitmapCache::SkBitmapCache(int max) : fMaxEntries(max) {
+    fEntryCount = 0;
+    fHead = fTail = NULL;
+
+    this->validate();
+}
+
+SkBitmapCache::~SkBitmapCache() {
+    this->validate();
+
+    Entry* entry = fHead;
+    while (entry) {
+        Entry* next = entry->fNext;
+        delete entry;
+        entry = next;
+    }
+}
+
+SkBitmapCache::Entry* SkBitmapCache::detach(Entry* entry) const {
+    if (entry->fPrev) {
+        SkASSERT(fHead != entry);
+        entry->fPrev->fNext = entry->fNext;
+    } else {
+        SkASSERT(fHead == entry);
+        fHead = entry->fNext;
+    }
+    if (entry->fNext) {
+        SkASSERT(fTail != entry);
+        entry->fNext->fPrev = entry->fPrev;
+    } else {
+        SkASSERT(fTail == entry);
+        fTail = entry->fPrev;
+    }
+    return entry;
+}
+
+void SkBitmapCache::attachToHead(Entry* entry) const {
+    entry->fPrev = NULL;
+    entry->fNext = fHead;
+    if (fHead) {
+        fHead->fPrev = entry;
+    } else {
+        fTail = entry;
+    }
+    fHead = entry;
+}
+
+bool SkBitmapCache::find(const void* buffer, size_t size, SkBitmap* bm) const {
+    AutoValidate av(this);
+
+    Entry* entry = fHead;
+    while (entry) {
+        if (entry->equals(buffer, size)) {
+            if (bm) {
+                *bm = entry->fBitmap;
+            }
+            // move to the head of our list, so we purge it last
+            this->detach(entry);
+            this->attachToHead(entry);
+            return true;
+        }
+        entry = entry->fNext;
+    }
+    return false;
+}
+
+void SkBitmapCache::add(const void* buffer, size_t len, const SkBitmap& bm) {
+    AutoValidate av(this);
+
+    if (fEntryCount == fMaxEntries) {
+        SkASSERT(fTail);
+        delete this->detach(fTail);
+        fEntryCount -= 1;
+    }
+
+    Entry* entry = SkNEW_ARGS(Entry, (buffer, len, bm));
+    this->attachToHead(entry);
+    fEntryCount += 1;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef SK_DEBUG
+
+void SkBitmapCache::validate() const {
+    SkASSERT(fEntryCount >= 0 && fEntryCount <= fMaxEntries);
+
+    if (fEntryCount > 0) {
+        SkASSERT(NULL == fHead->fPrev);
+        SkASSERT(NULL == fTail->fNext);
+
+        if (fEntryCount == 1) {
+            SkASSERT(fHead == fTail);
+        } else {
+            SkASSERT(fHead != fTail);
+        }
+
+        Entry* entry = fHead;
+        int count = 0;
+        while (entry) {
+            count += 1;
+            entry = entry->fNext;
+        }
+        SkASSERT(count == fEntryCount);
+
+        entry = fTail;
+        while (entry) {
+            count -= 1;
+            entry = entry->fPrev;
+        }
+        SkASSERT(0 == count);
+    } else {
+        SkASSERT(NULL == fHead);
+        SkASSERT(NULL == fTail);
+    }
+}
+
+#endif
diff --git a/src/effects/gradients/SkBitmapCache.h b/src/effects/gradients/SkBitmapCache.h
new file mode 100644
index 0000000..d2af5e1
--- /dev/null
+++ b/src/effects/gradients/SkBitmapCache.h
@@ -0,0 +1,49 @@
+
+/*
+ * 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 SkBitmapCache_DEFINED
+#define SkBitmapCache_DEFINED
+
+#include "SkBitmap.h"
+
+class SkBitmapCache : SkNoncopyable {
+public:
+    SkBitmapCache(int maxEntries);
+    ~SkBitmapCache();
+
+    bool find(const void* buffer, size_t len, SkBitmap*) const;
+    void add(const void* buffer, size_t len, const SkBitmap&);
+
+private:
+    int fEntryCount;
+    const int fMaxEntries;
+
+    struct Entry;
+    mutable Entry*  fHead;
+    mutable Entry*  fTail;
+
+    inline Entry* detach(Entry*) const;
+    inline void attachToHead(Entry*) const;
+
+#ifdef SK_DEBUG
+    void validate() const;
+#else
+    void validate() const {}
+#endif
+
+    class AutoValidate : SkNoncopyable {
+    public:
+        AutoValidate(const SkBitmapCache* bc) : fBC(bc) { bc->validate(); }
+        ~AutoValidate() { fBC->validate(); }
+    private:
+        const SkBitmapCache* fBC;
+    };
+};
+
+#endif
diff --git a/src/effects/gradients/SkClampRange.cpp b/src/effects/gradients/SkClampRange.cpp
new file mode 100644
index 0000000..3e2ca8e
--- /dev/null
+++ b/src/effects/gradients/SkClampRange.cpp
@@ -0,0 +1,165 @@
+
+/*
+ * 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 "SkClampRange.h"
+
+/*
+ *  returns [0..count] for the number of steps (<= count) for which x0 <= edge
+ *  given each step is followed by x0 += dx
+ */
+static int chop(int64_t x0, SkFixed edge, int64_t x1, int64_t dx, int count) {
+    SkASSERT(dx > 0);
+    SkASSERT(count >= 0);
+
+    if (x0 >= edge) {
+        return 0;
+    }
+    if (x1 <= edge) {
+        return count;
+    }
+    int64_t n = (edge - x0 + dx - 1) / dx;
+    SkASSERT(n >= 0);
+    SkASSERT(n <= count);
+    return (int)n;
+}
+
+static bool overflows_fixed(int64_t x) {
+    return x < -SK_FixedMax || x > SK_FixedMax;
+}
+
+void SkClampRange::initFor1(SkFixed fx) {
+    fCount0 = fCount1 = fCount2 = 0;
+    if (fx <= 0) {
+        fCount0 = 1;
+    } else if (fx < 0xFFFF) {
+        fCount1 = 1;
+        fFx1 = fx;
+    } else {
+        fCount2 = 1;
+    }
+}
+
+void SkClampRange::init(SkFixed fx0, SkFixed dx0, int count, int v0, int v1) {
+    SkASSERT(count > 0);
+
+    fV0 = v0;
+    fV1 = v1;
+    fOverflowed = false;
+
+    // special case 1 == count, as it is slightly common for skia
+    // and avoids us ever calling divide or 64bit multiply
+    if (1 == count) {
+        this->initFor1(fx0);
+        return;
+    }
+
+    int64_t fx = fx0;
+    int64_t dx = dx0;
+    // start with ex equal to the last computed value
+    int64_t ex = fx + (count - 1) * dx;
+    fOverflowed = overflows_fixed(ex);
+
+    if ((uint64_t)(fx | ex) <= 0xFFFF) {
+        fCount0 = fCount2 = 0;
+        fCount1 = count;
+        fFx1 = fx0;
+        return;
+    }
+    if (fx <= 0 && ex <= 0) {
+        fCount1 = fCount2 = 0;
+        fCount0 = count;
+        return;
+    }
+    if (fx >= 0xFFFF && ex >= 0xFFFF) {
+        fCount0 = fCount1 = 0;
+        fCount2 = count;
+        return;
+    }
+
+    int extraCount = 0;
+
+    // now make ex be 1 past the last computed value
+    ex += dx;
+    fOverflowed = overflows_fixed(ex);
+    // now check for over/under flow
+    if (fOverflowed) {
+        int originalCount = count;
+        int64_t ccount;
+        bool swap = dx < 0;
+        if (swap) {
+            dx = -dx;
+            fx = -fx;
+        }
+        ccount = (SK_FixedMax - fx + dx - 1) / dx;
+        if (swap) {
+            dx = -dx;
+            fx = -fx;
+        }
+        SkASSERT(ccount > 0 && ccount <= SK_FixedMax);
+
+        count = (int)ccount;
+        if (0 == count) {
+            this->initFor1(fx0);
+            if (dx > 0) {
+                fCount2 += originalCount - 1;
+            } else {
+                fCount0 += originalCount - 1;
+            }
+            return;
+        }
+        extraCount = originalCount - count;
+        ex = fx + dx * count;
+    }
+
+    bool doSwap = dx < 0;
+
+    if (doSwap) {
+        ex -= dx;
+        fx -= dx;
+        SkTSwap(fx, ex);
+        dx = -dx;
+    }
+
+
+    fCount0 = chop(fx, 0, ex, dx, count);
+    count -= fCount0;
+    fx += fCount0 * dx;
+    SkASSERT(fx >= 0);
+    SkASSERT(fCount0 == 0 || (fx - dx) < 0);
+    fCount1 = chop(fx, 0xFFFF, ex, dx, count);
+    count -= fCount1;
+    fCount2 = count;
+
+#ifdef SK_DEBUG
+    fx += fCount1 * dx;
+    SkASSERT(fx <= ex);
+    if (fCount2 > 0) {
+        SkASSERT(fx >= 0xFFFF);
+        if (fCount1 > 0) {
+            SkASSERT(fx - dx < 0xFFFF);
+        }
+    }
+#endif
+
+    if (doSwap) {
+        SkTSwap(fCount0, fCount2);
+        SkTSwap(fV0, fV1);
+        dx = -dx;
+    }
+
+    if (fCount1 > 0) {
+        fFx1 = fx0 + fCount0 * (int)dx;
+    }
+
+    if (dx > 0) {
+        fCount2 += extraCount;
+    } else {
+        fCount0 += extraCount;
+    }
+}
diff --git a/src/effects/gradients/SkClampRange.h b/src/effects/gradients/SkClampRange.h
new file mode 100644
index 0000000..376dc93
--- /dev/null
+++ b/src/effects/gradients/SkClampRange.h
@@ -0,0 +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.
+ */
+
+
+#ifndef SkClampRange_DEFINED
+#define SkClampRange_DEFINED
+
+#include "SkFixed.h"
+
+/**
+ *  Iteration fixed fx by dx, clamping as you go to [0..0xFFFF], this class
+ *  computes the (up to) 3 spans there are:
+ *
+ *  range0: use constant value V0
+ *  range1: iterate as usual fx += dx
+ *  range2: use constant value V1
+ */
+struct SkClampRange {
+    int fCount0;    // count for fV0
+    int fCount1;    // count for interpolating (fV0...fV1)
+    int fCount2;    // count for fV1
+    SkFixed fFx1;   // initial fx value for the fCount1 range.
+                    // only valid if fCount1 > 0
+    int fV0, fV1;
+    bool fOverflowed;   // true if we had to clamp due to numerical overflow
+
+    void init(SkFixed fx, SkFixed dx, int count, int v0, int v1);
+
+private:
+    void initFor1(SkFixed fx);
+};
+
+#endif
diff --git a/src/effects/gradients/SkGradientShader.cpp b/src/effects/gradients/SkGradientShader.cpp
new file mode 100644
index 0000000..de9ae9e
--- /dev/null
+++ b/src/effects/gradients/SkGradientShader.cpp
@@ -0,0 +1,851 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkGradientShaderPriv.h"
+#include "SkLinearGradient.h"
+#include "SkRadialGradient.h"
+#include "SkTwoPointRadialGradient.h"
+#include "SkTwoPointConicalGradient.h"
+#include "SkSweepGradient.h"
+
+SkGradientShaderBase::SkGradientShaderBase(const SkColor colors[], const SkScalar pos[],
+             int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper) {
+    SkASSERT(colorCount > 1);
+
+    fCacheAlpha = 256;  // init to a value that paint.getAlpha() can't return
+
+    fMapper = mapper;
+    SkSafeRef(mapper);
+
+    SkASSERT((unsigned)mode < SkShader::kTileModeCount);
+    SkASSERT(SkShader::kTileModeCount == SK_ARRAY_COUNT(gTileProcs));
+    fTileMode = mode;
+    fTileProc = gTileProcs[mode];
+
+    fCache16 = fCache16Storage = NULL;
+    fCache32 = NULL;
+    fCache32PixelRef = NULL;
+
+    /*  Note: we let the caller skip the first and/or last position.
+        i.e. pos[0] = 0.3, pos[1] = 0.7
+        In these cases, we insert dummy entries to ensure that the final data
+        will be bracketed by [0, 1].
+        i.e. our_pos[0] = 0, our_pos[1] = 0.3, our_pos[2] = 0.7, our_pos[3] = 1
+
+        Thus colorCount (the caller's value, and fColorCount (our value) may
+        differ by up to 2. In the above example:
+            colorCount = 2
+            fColorCount = 4
+     */
+    fColorCount = colorCount;
+    // check if we need to add in dummy start and/or end position/colors
+    bool dummyFirst = false;
+    bool dummyLast = false;
+    if (pos) {
+        dummyFirst = pos[0] != 0;
+        dummyLast = pos[colorCount - 1] != SK_Scalar1;
+        fColorCount += dummyFirst + dummyLast;
+    }
+
+    if (fColorCount > kColorStorageCount) {
+        size_t size = sizeof(SkColor) + sizeof(Rec);
+        fOrigColors = reinterpret_cast<SkColor*>(
+                                        sk_malloc_throw(size * fColorCount));
+    }
+    else {
+        fOrigColors = fStorage;
+    }
+
+    // Now copy over the colors, adding the dummies as needed
+    {
+        SkColor* origColors = fOrigColors;
+        if (dummyFirst) {
+            *origColors++ = colors[0];
+        }
+        memcpy(origColors, colors, colorCount * sizeof(SkColor));
+        if (dummyLast) {
+            origColors += colorCount;
+            *origColors = colors[colorCount - 1];
+        }
+    }
+
+    fRecs = (Rec*)(fOrigColors + fColorCount);
+    if (fColorCount > 2) {
+        Rec* recs = fRecs;
+        recs->fPos = 0;
+        //  recs->fScale = 0; // unused;
+        recs += 1;
+        if (pos) {
+            /*  We need to convert the user's array of relative positions into
+                fixed-point positions and scale factors. We need these results
+                to be strictly monotonic (no two values equal or out of order).
+                Hence this complex loop that just jams a zero for the scale
+                value if it sees a segment out of order, and it assures that
+                we start at 0 and end at 1.0
+            */
+            SkFixed prev = 0;
+            int startIndex = dummyFirst ? 0 : 1;
+            int count = colorCount + dummyLast;
+            for (int i = startIndex; i < count; i++) {
+                // force the last value to be 1.0
+                SkFixed curr;
+                if (i == colorCount) {  // we're really at the dummyLast
+                    curr = SK_Fixed1;
+                } else {
+                    curr = SkScalarToFixed(pos[i]);
+                }
+                // pin curr withing range
+                if (curr < 0) {
+                    curr = 0;
+                } else if (curr > SK_Fixed1) {
+                    curr = SK_Fixed1;
+                }
+                recs->fPos = curr;
+                if (curr > prev) {
+                    recs->fScale = (1 << 24) / (curr - prev);
+                } else {
+                    recs->fScale = 0; // ignore this segment
+                }
+                // get ready for the next value
+                prev = curr;
+                recs += 1;
+            }
+        } else {    // assume even distribution
+            SkFixed dp = SK_Fixed1 / (colorCount - 1);
+            SkFixed p = dp;
+            SkFixed scale = (colorCount - 1) << 8;  // (1 << 24) / dp
+            for (int i = 1; i < colorCount; i++) {
+                recs->fPos   = p;
+                recs->fScale = scale;
+                recs += 1;
+                p += dp;
+            }
+        }
+    }
+    this->initCommon();
+}
+
+SkGradientShaderBase::SkGradientShaderBase(SkFlattenableReadBuffer& buffer) :
+    INHERITED(buffer) {
+    fCacheAlpha = 256;
+
+    fMapper = buffer.readFlattenableT<SkUnitMapper>();
+
+    fCache16 = fCache16Storage = NULL;
+    fCache32 = NULL;
+    fCache32PixelRef = NULL;
+
+    int colorCount = fColorCount = buffer.getArrayCount();
+    if (colorCount > kColorStorageCount) {
+        size_t size = sizeof(SkColor) + sizeof(SkPMColor) + sizeof(Rec);
+        fOrigColors = (SkColor*)sk_malloc_throw(size * colorCount);
+    } else {
+        fOrigColors = fStorage;
+    }
+    buffer.readColorArray(fOrigColors);
+
+    fTileMode = (TileMode)buffer.readUInt();
+    fTileProc = gTileProcs[fTileMode];
+    fRecs = (Rec*)(fOrigColors + colorCount);
+    if (colorCount > 2) {
+        Rec* recs = fRecs;
+        recs[0].fPos = 0;
+        for (int i = 1; i < colorCount; i++) {
+            recs[i].fPos = buffer.readInt();
+            recs[i].fScale = buffer.readUInt();
+        }
+    }
+    buffer.readMatrix(&fPtsToUnit);
+    this->initCommon();
+}
+
+SkGradientShaderBase::~SkGradientShaderBase() {
+    if (fCache16Storage) {
+        sk_free(fCache16Storage);
+    }
+    SkSafeUnref(fCache32PixelRef);
+    if (fOrigColors != fStorage) {
+        sk_free(fOrigColors);
+    }
+    SkSafeUnref(fMapper);
+}
+
+void SkGradientShaderBase::initCommon() {
+    fFlags = 0;
+    unsigned colorAlpha = 0xFF;
+    for (int i = 0; i < fColorCount; i++) {
+        colorAlpha &= SkColorGetA(fOrigColors[i]);
+    }
+    fColorsAreOpaque = colorAlpha == 0xFF;
+}
+
+void SkGradientShaderBase::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fMapper);
+    buffer.writeColorArray(fOrigColors, fColorCount);
+    buffer.writeUInt(fTileMode);
+    if (fColorCount > 2) {
+        Rec* recs = fRecs;
+        for (int i = 1; i < fColorCount; i++) {
+            buffer.writeInt(recs[i].fPos);
+            buffer.writeUInt(recs[i].fScale);
+        }
+    }
+    buffer.writeMatrix(fPtsToUnit);
+}
+
+bool SkGradientShaderBase::isOpaque() const {
+    return fColorsAreOpaque;
+}
+
+bool SkGradientShaderBase::setContext(const SkBitmap& device,
+                                 const SkPaint& paint,
+                                 const SkMatrix& matrix) {
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    const SkMatrix& inverse = this->getTotalInverse();
+
+    if (!fDstToIndex.setConcat(fPtsToUnit, inverse)) {
+        // need to keep our set/end context calls balanced.
+        this->INHERITED::endContext();
+        return false;
+    }
+
+    fDstToIndexProc = fDstToIndex.getMapXYProc();
+    fDstToIndexClass = (uint8_t)SkShader::ComputeMatrixClass(fDstToIndex);
+
+    // now convert our colors in to PMColors
+    unsigned paintAlpha = this->getPaintAlpha();
+
+    fFlags = this->INHERITED::getFlags();
+    if (fColorsAreOpaque && paintAlpha == 0xFF) {
+        fFlags |= kOpaqueAlpha_Flag;
+    }
+    // we can do span16 as long as our individual colors are opaque,
+    // regardless of the paint's alpha
+    if (fColorsAreOpaque) {
+        fFlags |= kHasSpan16_Flag;
+    }
+
+    this->setCacheAlpha(paintAlpha);
+    return true;
+}
+
+void SkGradientShaderBase::setCacheAlpha(U8CPU alpha) const {
+    // if the new alpha differs from the previous time we were called, inval our cache
+    // this will trigger the cache to be rebuilt.
+    // we don't care about the first time, since the cache ptrs will already be NULL
+    if (fCacheAlpha != alpha) {
+        fCache16 = NULL;            // inval the cache
+        fCache32 = NULL;            // inval the cache
+        fCacheAlpha = alpha;        // record the new alpha
+        // inform our subclasses
+        if (fCache32PixelRef) {
+            fCache32PixelRef->notifyPixelsChanged();
+        }
+    }
+}
+
+#define Fixed_To_Dot8(x)        (((x) + 0x80) >> 8)
+
+/** We take the original colors, not our premultiplied PMColors, since we can
+    build a 16bit table as long as the original colors are opaque, even if the
+    paint specifies a non-opaque alpha.
+*/
+void SkGradientShaderBase::Build16bitCache(uint16_t cache[], SkColor c0, SkColor c1,
+                                      int count) {
+    SkASSERT(count > 1);
+    SkASSERT(SkColorGetA(c0) == 0xFF);
+    SkASSERT(SkColorGetA(c1) == 0xFF);
+
+    SkFixed r = SkColorGetR(c0);
+    SkFixed g = SkColorGetG(c0);
+    SkFixed b = SkColorGetB(c0);
+
+    SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
+    SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
+    SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
+
+    r = SkIntToFixed(r) + 0x8000;
+    g = SkIntToFixed(g) + 0x8000;
+    b = SkIntToFixed(b) + 0x8000;
+
+    do {
+        unsigned rr = r >> 16;
+        unsigned gg = g >> 16;
+        unsigned bb = b >> 16;
+        cache[0] = SkPackRGB16(SkR32ToR16(rr), SkG32ToG16(gg), SkB32ToB16(bb));
+        cache[kCache16Count] = SkDitherPack888ToRGB16(rr, gg, bb);
+        cache += 1;
+        r += dr;
+        g += dg;
+        b += db;
+    } while (--count != 0);
+}
+
+/*
+ *  2x2 dither a fixed-point color component (8.16) down to 8, matching the
+ *  semantics of how we 2x2 dither 32->16
+ */
+static inline U8CPU dither_fixed_to_8(SkFixed n) {
+    n >>= 8;
+    return ((n << 1) - ((n >> 8 << 8) | (n >> 8))) >> 8;
+}
+
+/*
+ *  For dithering with premultiply, we want to ceiling the alpha component,
+ *  to ensure that it is always >= any color component.
+ */
+static inline U8CPU dither_ceil_fixed_to_8(SkFixed n) {
+    n >>= 8;
+    return ((n << 1) - (n | (n >> 8))) >> 8;
+}
+
+void SkGradientShaderBase::Build32bitCache(SkPMColor cache[], SkColor c0, SkColor c1,
+                                      int count, U8CPU paintAlpha) {
+    SkASSERT(count > 1);
+
+    // need to apply paintAlpha to our two endpoints
+    SkFixed a = SkMulDiv255Round(SkColorGetA(c0), paintAlpha);
+    SkFixed da;
+    {
+        int tmp = SkMulDiv255Round(SkColorGetA(c1), paintAlpha);
+        da = SkIntToFixed(tmp - a) / (count - 1);
+    }
+
+    SkFixed r = SkColorGetR(c0);
+    SkFixed g = SkColorGetG(c0);
+    SkFixed b = SkColorGetB(c0);
+    SkFixed dr = SkIntToFixed(SkColorGetR(c1) - r) / (count - 1);
+    SkFixed dg = SkIntToFixed(SkColorGetG(c1) - g) / (count - 1);
+    SkFixed db = SkIntToFixed(SkColorGetB(c1) - b) / (count - 1);
+
+    a = SkIntToFixed(a) + 0x8000;
+    r = SkIntToFixed(r) + 0x8000;
+    g = SkIntToFixed(g) + 0x8000;
+    b = SkIntToFixed(b) + 0x8000;
+
+    do {
+        cache[0] = SkPremultiplyARGBInline(a >> 16, r >> 16, g >> 16, b >> 16);
+        cache[kCache32Count] =
+            SkPremultiplyARGBInline(dither_ceil_fixed_to_8(a),
+                                    dither_fixed_to_8(r),
+                                    dither_fixed_to_8(g),
+                                    dither_fixed_to_8(b));
+        cache += 1;
+        a += da;
+        r += dr;
+        g += dg;
+        b += db;
+    } while (--count != 0);
+}
+
+static inline int SkFixedToFFFF(SkFixed x) {
+    SkASSERT((unsigned)x <= SK_Fixed1);
+    return x - (x >> 16);
+}
+
+static inline U16CPU bitsTo16(unsigned x, const unsigned bits) {
+    SkASSERT(x < (1U << bits));
+    if (6 == bits) {
+        return (x << 10) | (x << 4) | (x >> 2);
+    }
+    if (8 == bits) {
+        return (x << 8) | x;
+    }
+    sk_throw();
+    return 0;
+}
+
+const uint16_t* SkGradientShaderBase::getCache16() const {
+    if (fCache16 == NULL) {
+        // double the count for dither entries
+        const int entryCount = kCache16Count * 2;
+        const size_t allocSize = sizeof(uint16_t) * entryCount;
+
+        if (fCache16Storage == NULL) { // set the storage and our working ptr
+            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
+        }
+        fCache16 = fCache16Storage;
+        if (fColorCount == 2) {
+            Build16bitCache(fCache16, fOrigColors[0], fOrigColors[1],
+                            kCache16Count);
+        } else {
+            Rec* rec = fRecs;
+            int prevIndex = 0;
+            for (int i = 1; i < fColorCount; i++) {
+                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache16Shift;
+                SkASSERT(nextIndex < kCache16Count);
+
+                if (nextIndex > prevIndex)
+                    Build16bitCache(fCache16 + prevIndex, fOrigColors[i-1], fOrigColors[i], nextIndex - prevIndex + 1);
+                prevIndex = nextIndex;
+            }
+        }
+
+        if (fMapper) {
+            fCache16Storage = (uint16_t*)sk_malloc_throw(allocSize);
+            uint16_t* linear = fCache16;         // just computed linear data
+            uint16_t* mapped = fCache16Storage;  // storage for mapped data
+            SkUnitMapper* map = fMapper;
+            for (int i = 0; i < kCache16Count; i++) {
+                int index = map->mapUnit16(bitsTo16(i, kCache16Bits)) >> kCache16Shift;
+                mapped[i] = linear[index];
+                mapped[i + kCache16Count] = linear[index + kCache16Count];
+            }
+            sk_free(fCache16);
+            fCache16 = fCache16Storage;
+        }
+    }
+    return fCache16;
+}
+
+const SkPMColor* SkGradientShaderBase::getCache32() const {
+    if (fCache32 == NULL) {
+        // double the count for dither entries
+        const int entryCount = kCache32Count * 2;
+        const size_t allocSize = sizeof(SkPMColor) * entryCount;
+
+        if (NULL == fCache32PixelRef) {
+            fCache32PixelRef = SkNEW_ARGS(SkMallocPixelRef,
+                                          (NULL, allocSize, NULL));
+        }
+        fCache32 = (SkPMColor*)fCache32PixelRef->getAddr();
+        if (fColorCount == 2) {
+            Build32bitCache(fCache32, fOrigColors[0], fOrigColors[1],
+                            kCache32Count, fCacheAlpha);
+        } else {
+            Rec* rec = fRecs;
+            int prevIndex = 0;
+            for (int i = 1; i < fColorCount; i++) {
+                int nextIndex = SkFixedToFFFF(rec[i].fPos) >> kCache32Shift;
+                SkASSERT(nextIndex < kCache32Count);
+
+                if (nextIndex > prevIndex)
+                    Build32bitCache(fCache32 + prevIndex, fOrigColors[i-1],
+                                    fOrigColors[i],
+                                    nextIndex - prevIndex + 1, fCacheAlpha);
+                prevIndex = nextIndex;
+            }
+        }
+
+        if (fMapper) {
+            SkMallocPixelRef* newPR = SkNEW_ARGS(SkMallocPixelRef,
+                                                 (NULL, allocSize, NULL));
+            SkPMColor* linear = fCache32;           // just computed linear data
+            SkPMColor* mapped = (SkPMColor*)newPR->getAddr();    // storage for mapped data
+            SkUnitMapper* map = fMapper;
+            for (int i = 0; i < kCache32Count; i++) {
+                int index = map->mapUnit16((i << 8) | i) >> 8;
+                mapped[i] = linear[index];
+                mapped[i + kCache32Count] = linear[index + kCache32Count];
+            }
+            fCache32PixelRef->unref();
+            fCache32PixelRef = newPR;
+            fCache32 = (SkPMColor*)newPR->getAddr();
+        }
+    }
+    return fCache32;
+}
+
+/*
+ *  Because our caller might rebuild the same (logically the same) gradient
+ *  over and over, we'd like to return exactly the same "bitmap" if possible,
+ *  allowing the client to utilize a cache of our bitmap (e.g. with a GPU).
+ *  To do that, we maintain a private cache of built-bitmaps, based on our
+ *  colors and positions. Note: we don't try to flatten the fMapper, so if one
+ *  is present, we skip the cache for now.
+ */
+void SkGradientShaderBase::getGradientTableBitmap(SkBitmap* bitmap) const {
+    // our caller assumes no external alpha, so we ensure that our cache is
+    // built with 0xFF
+    this->setCacheAlpha(0xFF);
+
+    // don't have a way to put the mapper into our cache-key yet
+    if (fMapper) {
+        // force our cahce32pixelref to be built
+        (void)this->getCache32();
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
+        bitmap->setPixelRef(fCache32PixelRef);
+        return;
+    }
+
+    // build our key: [numColors + colors[] + {positions[]} ]
+    int count = 1 + fColorCount;
+    if (fColorCount > 2) {
+        count += fColorCount - 1;    // fRecs[].fPos
+    }
+
+    SkAutoSTMalloc<16, int32_t> storage(count);
+    int32_t* buffer = storage.get();
+
+    *buffer++ = fColorCount;
+    memcpy(buffer, fOrigColors, fColorCount * sizeof(SkColor));
+    buffer += fColorCount;
+    if (fColorCount > 2) {
+        for (int i = 1; i < fColorCount; i++) {
+            *buffer++ = fRecs[i].fPos;
+        }
+    }
+    SkASSERT(buffer - storage.get() == count);
+
+    ///////////////////////////////////
+
+    SK_DECLARE_STATIC_MUTEX(gMutex);
+    static SkBitmapCache* gCache;
+    // each cache cost 1K of RAM, since each bitmap will be 1x256 at 32bpp
+    static const int MAX_NUM_CACHED_GRADIENT_BITMAPS = 32;
+    SkAutoMutexAcquire ama(gMutex);
+
+    if (NULL == gCache) {
+        gCache = SkNEW_ARGS(SkBitmapCache, (MAX_NUM_CACHED_GRADIENT_BITMAPS));
+    }
+    size_t size = count * sizeof(int32_t);
+
+    if (!gCache->find(storage.get(), size, bitmap)) {
+        // force our cahce32pixelref to be built
+        (void)this->getCache32();
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, kCache32Count, 1);
+        bitmap->setPixelRef(fCache32PixelRef);
+
+        gCache->add(storage.get(), size, *bitmap);
+    }
+}
+
+void SkGradientShaderBase::commonAsAGradient(GradientInfo* info) const {
+    if (info) {
+        if (info->fColorCount >= fColorCount) {
+            if (info->fColors) {
+                memcpy(info->fColors, fOrigColors, fColorCount * sizeof(SkColor));
+            }
+            if (info->fColorOffsets) {
+                if (fColorCount == 2) {
+                    info->fColorOffsets[0] = 0;
+                    info->fColorOffsets[1] = SK_Scalar1;
+                } else if (fColorCount > 2) {
+                    for (int i = 0; i < fColorCount; ++i) {
+                        info->fColorOffsets[i] = SkFixedToScalar(fRecs[i].fPos);
+                    }
+                }
+            }
+        }
+        info->fColorCount = fColorCount;
+        info->fTileMode = fTileMode;
+    }
+}
+
+#ifdef SK_DEVELOPER
+void SkGradientShaderBase::toString(SkString* str) const {
+
+    str->appendf("%d colors: ", fColorCount);
+
+    for (int i = 0; i < fColorCount; ++i) {
+        str->appendHex(fOrigColors[i]);
+        if (i < fColorCount-1) {
+            str->append(", ");
+        }
+    }
+
+    if (fColorCount > 2) {
+        str->append(" points: (");
+        for (int i = 0; i < fColorCount; ++i) {
+            str->appendScalar(SkFixedToScalar(fRecs[i].fPos));
+            if (i < fColorCount-1) {
+                str->append(", ");
+            }
+        }
+        str->append(")");
+    }
+
+    static const char* gTileModeName[SkShader::kTileModeCount] = {
+        "clamp", "repeat", "mirror"
+    };
+
+    str->append(" ");
+    str->append(gTileModeName[fTileMode]);
+
+    // TODO: add "fMapper->toString(str);" when SkUnitMapper::toString is added
+
+    this->INHERITED::toString(str);
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkEmptyShader.h"
+
+// assumes colors is SkColor* and pos is SkScalar*
+#define EXPAND_1_COLOR(count)               \
+    SkColor tmp[2];                         \
+    do {                                    \
+        if (1 == count) {                   \
+            tmp[0] = tmp[1] = colors[0];    \
+            colors = tmp;                   \
+            pos = NULL;                     \
+            count = 2;                      \
+        }                                   \
+    } while (0)
+
+SkShader* SkGradientShader::CreateLinear(const SkPoint pts[2],
+                                         const SkColor colors[],
+                                         const SkScalar pos[], int colorCount,
+                                         SkShader::TileMode mode,
+                                         SkUnitMapper* mapper) {
+    if (NULL == pts || NULL == colors || colorCount < 1) {
+        return NULL;
+    }
+    EXPAND_1_COLOR(colorCount);
+
+    return SkNEW_ARGS(SkLinearGradient,
+                      (pts, colors, pos, colorCount, mode, mapper));
+}
+
+SkShader* SkGradientShader::CreateRadial(const SkPoint& center, SkScalar radius,
+                                         const SkColor colors[],
+                                         const SkScalar pos[], int colorCount,
+                                         SkShader::TileMode mode,
+                                         SkUnitMapper* mapper) {
+    if (radius <= 0 || NULL == colors || colorCount < 1) {
+        return NULL;
+    }
+    EXPAND_1_COLOR(colorCount);
+
+    return SkNEW_ARGS(SkRadialGradient,
+                      (center, radius, colors, pos, colorCount, mode, mapper));
+}
+
+SkShader* SkGradientShader::CreateTwoPointRadial(const SkPoint& start,
+                                                 SkScalar startRadius,
+                                                 const SkPoint& end,
+                                                 SkScalar endRadius,
+                                                 const SkColor colors[],
+                                                 const SkScalar pos[],
+                                                 int colorCount,
+                                                 SkShader::TileMode mode,
+                                                 SkUnitMapper* mapper) {
+    if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
+        return NULL;
+    }
+    EXPAND_1_COLOR(colorCount);
+
+    return SkNEW_ARGS(SkTwoPointRadialGradient,
+                      (start, startRadius, end, endRadius, colors, pos,
+                       colorCount, mode, mapper));
+}
+
+SkShader* SkGradientShader::CreateTwoPointConical(const SkPoint& start,
+                                                 SkScalar startRadius,
+                                                 const SkPoint& end,
+                                                 SkScalar endRadius,
+                                                 const SkColor colors[],
+                                                 const SkScalar pos[],
+                                                 int colorCount,
+                                                 SkShader::TileMode mode,
+                                                 SkUnitMapper* mapper) {
+    if (startRadius < 0 || endRadius < 0 || NULL == colors || colorCount < 1) {
+        return NULL;
+    }
+    if (start == end && startRadius == endRadius) {
+        return SkNEW(SkEmptyShader);
+    }
+    EXPAND_1_COLOR(colorCount);
+
+    return SkNEW_ARGS(SkTwoPointConicalGradient,
+                      (start, startRadius, end, endRadius, colors, pos,
+                       colorCount, mode, mapper));
+}
+
+SkShader* SkGradientShader::CreateSweep(SkScalar cx, SkScalar cy,
+                                        const SkColor colors[],
+                                        const SkScalar pos[],
+                                        int count, SkUnitMapper* mapper) {
+    if (NULL == colors || count < 1) {
+        return NULL;
+    }
+    EXPAND_1_COLOR(count);
+
+    return SkNEW_ARGS(SkSweepGradient, (cx, cy, colors, pos, count, mapper));
+}
+
+SK_DEFINE_FLATTENABLE_REGISTRAR_GROUP_START(SkGradientShader)
+    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
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "effects/GrTextureStripAtlas.h"
+#include "SkGr.h"
+
+GrGLGradientEffect::GrGLGradientEffect(const GrBackendEffectFactory& factory)
+    : INHERITED(factory)
+    , fCachedYCoord(SK_ScalarMax)
+    , fFSYUni(GrGLUniformManager::kInvalidUniformHandle) {
+}
+
+GrGLGradientEffect::~GrGLGradientEffect() { }
+
+void GrGLGradientEffect::emitYCoordUniform(GrGLShaderBuilder* builder) {
+    fFSYUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                  kFloat_GrSLType, "GradientYCoordFS");
+}
+
+void GrGLGradientEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrGradientEffect& e = GetEffectFromStage<GrGradientEffect>(stage);
+    const GrTexture* texture = e.texture(0);
+    fEffectMatrix.setData(uman, e.getMatrix(), stage.getCoordChangeMatrix(), texture);
+
+    SkScalar yCoord = e.getYCoord();
+    if (yCoord != fCachedYCoord) {
+        uman.set1f(fFSYUni, yCoord);
+        fCachedYCoord = yCoord;
+    }
+}
+
+GrGLEffect::EffectKey GrGLGradientEffect::GenMatrixKey(const GrEffectStage& s) {
+    const GrGradientEffect& e = GetEffectFromStage<GrGradientEffect>(s);
+    const GrTexture* texture = e.texture(0);
+    return GrGLEffectMatrix::GenKey(e.getMatrix(), s.getCoordChangeMatrix(), texture);
+}
+
+void GrGLGradientEffect::setupMatrix(GrGLShaderBuilder* builder,
+                                     EffectKey key,
+                                     const char* vertexCoords,
+                                     const char** fsCoordName,
+                                     const char** vsVaryingName,
+                                     GrSLType* vsVaryingType) {
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder,
+                                         key & kMatrixKeyMask,
+                                         vertexCoords,
+                                         fsCoordName,
+                                         vsVaryingName,
+                                         vsVaryingType);
+}
+
+void GrGLGradientEffect::emitColorLookup(GrGLShaderBuilder* builder,
+                                         const char* gradientTValue,
+                                         const char* outputColor,
+                                         const char* inputColor,
+                                         const GrGLShaderBuilder::TextureSampler& sampler) {
+
+    SkString* code = &builder->fFSCode;
+    code->appendf("\tvec2 coord = vec2(%s, %s);\n",
+                  gradientTValue,
+                  builder->getUniformVariable(fFSYUni).c_str());
+    code->appendf("\t%s = ", outputColor);
+    builder->appendTextureLookupAndModulate(code, inputColor, sampler, "coord");
+    code->append(";\n");
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrGradientEffect::GrGradientEffect(GrContext* ctx,
+                                   const SkGradientShaderBase& shader,
+                                   const SkMatrix& matrix,
+                                   SkShader::TileMode tileMode) {
+    // TODO: check for simple cases where we don't need a texture:
+    //GradientInfo info;
+    //shader.asAGradient(&info);
+    //if (info.fColorCount == 2) { ...
+
+    fMatrix = matrix;
+
+    SkBitmap bitmap;
+    shader.getGradientTableBitmap(&bitmap);
+
+    fIsOpaque = shader.isOpaque();
+
+    GrTextureStripAtlas::Desc desc;
+    desc.fWidth  = bitmap.width();
+    desc.fHeight = 32;
+    desc.fRowHeight = bitmap.height();
+    desc.fContext = ctx;
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
+    fAtlas = GrTextureStripAtlas::GetAtlas(desc);
+    GrAssert(NULL != fAtlas);
+
+    // We always filter the gradient table. Each table is one row of a texture, so always y-clamp.
+    GrTextureParams params;
+    params.setBilerp(true);
+    params.setTileModeX(tileMode);
+
+    fRow = fAtlas->lockRow(bitmap);
+    if (-1 != fRow) {
+        fYCoord = fAtlas->getYOffset(fRow) + SK_ScalarHalf *
+                  fAtlas->getVerticalScaleFactor();
+        fTextureAccess.reset(fAtlas->getTexture(), params);
+    } else {
+        GrTexture* texture = GrLockAndRefCachedBitmapTexture(ctx, bitmap, &params);
+        fTextureAccess.reset(texture, params);
+        fYCoord = SK_ScalarHalf;
+
+        // Unlock immediately, this is not great, but we don't have a way of
+        // knowing when else to unlock it currently, so it may get purged from
+        // the cache, but it'll still be ref'd until it's no longer being used.
+        GrUnlockAndUnrefCachedBitmapTexture(texture);
+    }
+    this->addTextureAccess(&fTextureAccess);
+}
+
+GrGradientEffect::~GrGradientEffect() {
+    if (this->useAtlas()) {
+        fAtlas->unlockRow(fRow);
+    }
+}
+
+bool GrGradientEffect::onIsEqual(const GrEffect& effect) const {
+    const GrGradientEffect& s = CastEffect<GrGradientEffect>(effect);
+    return fTextureAccess.getTexture() == s.fTextureAccess.getTexture()  &&
+           fTextureAccess.getParams().getTileModeX() ==
+                s.fTextureAccess.getParams().getTileModeX() &&
+           this->useAtlas() == s.useAtlas() &&
+           fYCoord == s.getYCoord() &&
+           fMatrix.cheapEqualTo(s.getMatrix());
+}
+
+void GrGradientEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    if (fIsOpaque && (kA_ValidComponentFlag & *validFlags) && 0xff == GrColorUnpackA(*color)) {
+        *validFlags = kA_ValidComponentFlag;
+    } else {
+        *validFlags = 0;
+    }
+}
+
+int GrGradientEffect::RandomGradientParams(SkRandom* random,
+                                           SkColor colors[],
+                                           SkScalar** stops,
+                                           SkShader::TileMode* tm) {
+    int outColors = random->nextRangeU(1, kMaxRandomGradientColors);
+
+    // if one color, omit stops, otherwise randomly decide whether or not to
+    if (outColors == 1 || (outColors >= 2 && random->nextBool())) {
+        *stops = NULL;
+    }
+
+    SkScalar stop = 0.f;
+    for (int i = 0; i < outColors; ++i) {
+        colors[i] = random->nextU();
+        if (NULL != *stops) {
+            (*stops)[i] = stop;
+            stop = i < outColors - 1 ? stop + random->nextUScalar1() * (1.f - stop) : 1.f;
+        }
+    }
+    *tm = static_cast<SkShader::TileMode>(random->nextULessThan(SkShader::kTileModeCount));
+
+    return outColors;
+}
+
+#endif
diff --git a/src/effects/gradients/SkGradientShaderPriv.h b/src/effects/gradients/SkGradientShaderPriv.h
new file mode 100644
index 0000000..4d14dc6
--- /dev/null
+++ b/src/effects/gradients/SkGradientShaderPriv.h
@@ -0,0 +1,340 @@
+
+/*
+ * 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 SkGradientShaderPriv_DEFINED
+#define SkGradientShaderPriv_DEFINED
+
+#include "SkGradientShader.h"
+#include "SkClampRange.h"
+#include "SkColorPriv.h"
+#include "SkFlattenableBuffers.h"
+#include "SkMallocPixelRef.h"
+#include "SkUnitMapper.h"
+#include "SkUtils.h"
+#include "SkTemplates.h"
+#include "SkBitmapCache.h"
+#include "SkShader.h"
+
+#ifndef SK_DISABLE_DITHER_32BIT_GRADIENT
+    #define USE_DITHER_32BIT_GRADIENT
+#endif
+
+static inline void sk_memset32_dither(uint32_t dst[], uint32_t v0, uint32_t v1,
+                               int count) {
+    if (count > 0) {
+        if (v0 == v1) {
+            sk_memset32(dst, v0, count);
+        } else {
+            int pairs = count >> 1;
+            for (int i = 0; i < pairs; i++) {
+                *dst++ = v0;
+                *dst++ = v1;
+            }
+            if (count & 1) {
+                *dst = v0;
+            }
+        }
+    }
+}
+
+//  Clamp
+
+static inline SkFixed clamp_tileproc(SkFixed x) {
+    return SkClampMax(x, 0xFFFF);
+}
+
+// Repeat
+
+static inline SkFixed repeat_tileproc(SkFixed x) {
+    return x & 0xFFFF;
+}
+
+// Mirror
+
+// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
+// See http://code.google.com/p/skia/issues/detail?id=472
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#pragma optimize("", off)
+#endif
+
+static inline SkFixed mirror_tileproc(SkFixed x) {
+    int s = x << 15 >> 31;
+    return (x ^ s) & 0xFFFF;
+}
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#pragma optimize("", on)
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+typedef SkFixed (*TileProc)(SkFixed);
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const TileProc gTileProcs[] = {
+    clamp_tileproc,
+    repeat_tileproc,
+    mirror_tileproc
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkGradientShaderBase : public SkShader {
+public:
+    SkGradientShaderBase(const SkColor colors[], const SkScalar pos[],
+                int colorCount, SkShader::TileMode mode, SkUnitMapper* mapper);
+    virtual ~SkGradientShaderBase();
+
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
+    virtual uint32_t getFlags() SK_OVERRIDE { return fFlags; }
+    virtual bool isOpaque() const SK_OVERRIDE;
+
+    void getGradientTableBitmap(SkBitmap*) const;
+
+    enum {
+        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
+        /// it, use a larger cache.
+        kCache16Bits    = 8,
+        kCache16Count = (1 << kCache16Bits),
+        kCache16Shift   = 16 - kCache16Bits,
+        kSqrt16Shift    = 8 - kCache16Bits,
+
+        /// Seems like enough for visual accuracy. TODO: if pos[] deserves
+        /// it, use a larger cache.
+        kCache32Bits    = 8,
+        kCache32Count = (1 << kCache32Bits),
+        kCache32Shift   = 16 - kCache32Bits,
+        kSqrt32Shift    = 8 - kCache32Bits,
+
+        /// This value is used to *read* the dither cache; it may be 0
+        /// if dithering is disabled.
+#ifdef USE_DITHER_32BIT_GRADIENT
+        kDitherStride32 = kCache32Count,
+#else
+        kDitherStride32 = 0,
+#endif
+        kDitherStride16 = kCache16Count,
+    };
+
+
+protected:
+    SkGradientShaderBase(SkFlattenableReadBuffer& );
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+    SK_DEVELOPER_TO_STRING()
+
+    SkUnitMapper* fMapper;
+    SkMatrix    fPtsToUnit;     // set by subclass
+    SkMatrix    fDstToIndex;
+    SkMatrix::MapXYProc fDstToIndexProc;
+    TileMode    fTileMode;
+    TileProc    fTileProc;
+    int         fColorCount;
+    uint8_t     fDstToIndexClass;
+    uint8_t     fFlags;
+
+    struct Rec {
+        SkFixed     fPos;   // 0...1
+        uint32_t    fScale; // (1 << 24) / range
+    };
+    Rec*        fRecs;
+
+    const uint16_t*     getCache16() const;
+    const SkPMColor*    getCache32() const;
+
+    void commonAsAGradient(GradientInfo*) const;
+
+private:
+    enum {
+        kColorStorageCount = 4, // more than this many colors, and we'll use sk_malloc for the space
+
+        kStorageSize = kColorStorageCount * (sizeof(SkColor) + sizeof(Rec))
+    };
+    SkColor     fStorage[(kStorageSize + 3) >> 2];
+    SkColor*    fOrigColors; // original colors, before modulation by paint in setContext
+    bool        fColorsAreOpaque;
+
+    mutable uint16_t*   fCache16;   // working ptr. If this is NULL, we need to recompute the cache values
+    mutable SkPMColor*  fCache32;   // working ptr. If this is NULL, we need to recompute the cache values
+
+    mutable uint16_t*   fCache16Storage;    // storage for fCache16, allocated on demand
+    mutable SkMallocPixelRef* fCache32PixelRef;
+    mutable unsigned    fCacheAlpha;        // the alpha value we used when we computed the cache. larger than 8bits so we can store uninitialized value
+
+    static void Build16bitCache(uint16_t[], SkColor c0, SkColor c1, int count);
+    static void Build32bitCache(SkPMColor[], SkColor c0, SkColor c1, int count,
+                                U8CPU alpha);
+    void setCacheAlpha(U8CPU alpha) const;
+    void initCommon();
+
+    typedef SkShader INHERITED;
+};
+
+static inline int init_dither_toggle(int x, int y) {
+    return ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride32;
+}
+
+static inline int next_dither_toggle(int toggle) {
+    return toggle ^ SkGradientShaderBase::kDitherStride32;
+}
+
+static inline int init_dither_toggle16(int x, int y) {
+    return ((x ^ y) & 1) * SkGradientShaderBase::kDitherStride16;
+}
+
+static inline int next_dither_toggle16(int toggle) {
+    return toggle ^ SkGradientShaderBase::kDitherStride16;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+
+class GrEffectStage;
+class GrBackendEffectFactory;
+
+/*
+ * The interpretation of the texture matrix depends on the sample mode. The
+ * texture matrix is applied both when the texture coordinates are explicit
+ * and  when vertex positions are used as texture  coordinates. In the latter
+ * case the texture matrix is applied to the pre-view-matrix position
+ * values.
+ *
+ * Normal SampleMode
+ *  The post-matrix texture coordinates are in normalize space with (0,0) at
+ *  the top-left and (1,1) at the bottom right.
+ * RadialGradient
+ *  The matrix specifies the radial gradient parameters.
+ *  (0,0) in the post-matrix space is center of the radial gradient.
+ * Radial2Gradient
+ *   Matrix transforms to space where first circle is centered at the
+ *   origin. The second circle will be centered (x, 0) where x may be
+ *   0 and is provided by setRadial2Params. The post-matrix space is
+ *   normalized such that 1 is the second radius - first radius.
+ * SweepGradient
+ *  The angle from the origin of texture coordinates in post-matrix space
+ *  determines the gradient value.
+ */
+
+ class GrTextureStripAtlas;
+
+// Base class for Gr gradient effects
+class GrGradientEffect : public GrEffect {
+public:
+
+    GrGradientEffect(GrContext* ctx,
+                     const SkGradientShaderBase& shader,
+                     const SkMatrix& matrix,
+                     SkShader::TileMode tileMode);
+
+    virtual ~GrGradientEffect();
+
+    bool useAtlas() const { return SkToBool(-1 != fRow); }
+    SkScalar getYCoord() const { return fYCoord; };
+    const SkMatrix& getMatrix() const { return fMatrix;}
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+protected:
+
+    /** Populates a pair of arrays with colors and stop info to construct a random gradient.
+        The function decides whether stop values should be used or not. The return value indicates
+        the number of colors, which will be capped by kMaxRandomGradientColors. colors should be
+        sized to be at least kMaxRandomGradientColors. stops is a pointer to an array of at least
+        size kMaxRandomGradientColors. It may be updated to NULL, indicating that NULL should be
+        passed to the gradient factory rather than the array.
+    */
+    static const int kMaxRandomGradientColors = 4;
+    static int RandomGradientParams(SkRandom* r,
+                                    SkColor colors[kMaxRandomGradientColors],
+                                    SkScalar** stops,
+                                    SkShader::TileMode* tm);
+
+    virtual bool onIsEqual(const GrEffect& effect) const SK_OVERRIDE;
+
+private:
+
+    GrTextureAccess fTextureAccess;
+    SkScalar fYCoord;
+    GrTextureStripAtlas* fAtlas;
+    int fRow;
+    SkMatrix fMatrix;
+    bool fIsOpaque;
+
+    typedef GrEffect INHERITED;
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Base class for GL gradient effects
+class GrGLGradientEffect : public GrGLEffect {
+public:
+    GrGLGradientEffect(const GrBackendEffectFactory& factory);
+    virtual ~GrGLGradientEffect();
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+protected:
+    /**
+     * Subclasses must reserve the lower kMatrixKeyBitCnt of their key for use by
+     * GrGLGradientEffect.
+     */
+    enum {
+        kMatrixKeyBitCnt = GrGLEffectMatrix::kKeyBits,
+        kMatrixKeyMask = (1 << kMatrixKeyBitCnt) - 1,
+    };
+
+    /**
+     * Subclasses must call this. It will return a value restricted to the lower kMatrixKeyBitCnt
+     * bits.
+     */
+    static EffectKey GenMatrixKey(const GrEffectStage& s);
+
+    /**
+     * Inserts code to implement the GrGradientEffect's matrix. This should be called before a
+     * subclass emits its own code. The name of the 2D coords is output via fsCoordName and already
+     * incorporates any perspective division. The caller can also optionally retrieve the name of
+     * the varying inserted in the VS and its type, which may be either vec2f or vec3f depending
+     * upon whether the matrix has perspective or not. It is not necessary to mask the key before
+     * calling.
+     */
+    void setupMatrix(GrGLShaderBuilder* builder,
+                     EffectKey key,
+                     const char* vertexCoords,
+                     const char** fsCoordName,
+                     const char** vsVaryingName = NULL,
+                     GrSLType* vsVaryingType = NULL);
+
+    // Emits the uniform used as the y-coord to texture samples in derived classes. Subclasses
+    // should call this method from their emitCode().
+    void emitYCoordUniform(GrGLShaderBuilder* builder);
+
+    // emit code that gets a fragment's color from an expression for t; for now this always uses the
+    // texture, but for simpler cases we'll be able to lerp. Subclasses should call this method from
+    // their emitCode().
+    void emitColorLookup(GrGLShaderBuilder* builder,
+                         const char* gradientTValue,
+                         const char* outputColor,
+                         const char* inputColor,
+                         const GrGLShaderBuilder::TextureSampler&);
+
+private:
+    SkScalar fCachedYCoord;
+    GrGLUniformManager::UniformHandle fFSYUni;
+    GrGLEffectMatrix fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+#endif
+
+#endif
diff --git a/src/effects/gradients/SkLinearGradient.cpp b/src/effects/gradients/SkLinearGradient.cpp
new file mode 100644
index 0000000..9adbbad
--- /dev/null
+++ b/src/effects/gradients/SkLinearGradient.cpp
@@ -0,0 +1,588 @@
+
+/*
+ * 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 "SkLinearGradient.h"
+
+static inline int repeat_bits(int x, const int bits) {
+    return x & ((1 << bits) - 1);
+}
+
+static inline int repeat_8bits(int x) {
+    return x & 0xFF;
+}
+
+// Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly.
+// See http://code.google.com/p/skia/issues/detail?id=472
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#pragma optimize("", off)
+#endif
+
+static inline int mirror_bits(int x, const int bits) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x & (1 << bits))
+        x = ~x;
+    return x & ((1 << bits) - 1);
+#else
+    int s = x << (31 - bits) >> 31;
+    return (x ^ s) & ((1 << bits) - 1);
+#endif
+}
+
+static inline int mirror_8bits(int x) {
+#ifdef SK_CPU_HAS_CONDITIONAL_INSTR
+    if (x & 256) {
+        x = ~x;
+    }
+    return x & 255;
+#else
+    int s = x << 23 >> 31;
+    return (x ^ s) & 0xFF;
+#endif
+}
+
+#if defined(_MSC_VER) && (_MSC_VER >= 1600)
+#pragma optimize("", on)
+#endif
+
+static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) {
+    SkVector    vec = pts[1] - pts[0];
+    SkScalar    mag = vec.length();
+    SkScalar    inv = mag ? SkScalarInvert(mag) : 0;
+
+    vec.scale(inv);
+    matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY);
+    matrix->postTranslate(-pts[0].fX, -pts[0].fY);
+    matrix->postScale(inv, inv);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkLinearGradient::SkLinearGradient(const SkPoint pts[2],
+                                   const SkColor colors[],
+                                   const SkScalar pos[],
+                                   int colorCount,
+                                   SkShader::TileMode mode,
+                                   SkUnitMapper* mapper)
+    : SkGradientShaderBase(colors, pos, colorCount, mode, mapper)
+    , fStart(pts[0])
+    , fEnd(pts[1]) {
+    pts_to_unit_matrix(pts, &fPtsToUnit);
+}
+
+SkLinearGradient::SkLinearGradient(SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer)
+    , fStart(buffer.readPoint())
+    , fEnd(buffer.readPoint()) {
+}
+
+void SkLinearGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fStart);
+    buffer.writePoint(fEnd);
+}
+
+bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint,
+                                 const SkMatrix& matrix) {
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
+    if ((fDstToIndex.getType() & ~mask) == 0) {
+        fFlags |= SkShader::kConstInY32_Flag;
+        if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) {
+            // only claim this if we do have a 16bit mode (i.e. none of our
+            // colors have alpha), and if we are not dithering (which obviously
+            // is not const in Y).
+            fFlags |= SkShader::kConstInY16_Flag;
+        }
+    }
+    return true;
+}
+
+#define NO_CHECK_ITER               \
+    do {                            \
+    unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \
+    SkASSERT(fi <= 0xFF);           \
+    fx += dx;                       \
+    *dstC++ = cache[toggle + fi];   \
+    toggle = next_dither_toggle(toggle); \
+    } while (0)
+
+namespace {
+
+typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx,
+                                SkPMColor* dstC, const SkPMColor* cache,
+                                int toggle, int count);
+
+// Linear interpolation (lerp) is unnecessary if there are no sharp
+// discontinuities in the gradient - which must be true if there are
+// only 2 colors - but it's cheap.
+void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx,
+                                    SkPMColor* SK_RESTRICT dstC,
+                                    const SkPMColor* SK_RESTRICT cache,
+                                    int toggle, int count) {
+    // We're a vertical gradient, so no change in a span.
+    // If colors change sharply across the gradient, dithering is
+    // insufficient (it subsamples the color space) and we need to lerp.
+    unsigned fullIndex = proc(fx);
+    unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift;
+    unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1);
+    
+    int index0 = fi + toggle;
+    int index1 = index0;
+    if (fi < SkGradientShaderBase::kCache32Count - 1) {
+        index1 += 1;
+    }
+    SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
+    index0 ^= SkGradientShaderBase::kDitherStride32;
+    index1 ^= SkGradientShaderBase::kDitherStride32;
+    SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder);
+    sk_memset32_dither(dstC, lerp, dlerp, count);
+}
+
+void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
+                            SkPMColor* SK_RESTRICT dstC,
+                            const SkPMColor* SK_RESTRICT cache,
+                            int toggle, int count) {
+    SkClampRange range;
+    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
+
+    if ((count = range.fCount0) > 0) {
+        sk_memset32_dither(dstC,
+            cache[toggle + range.fV0],
+            cache[next_dither_toggle(toggle) + range.fV0],
+            count);
+        dstC += count;
+    }
+    if ((count = range.fCount1) > 0) {
+        int unroll = count >> 3;
+        fx = range.fFx1;
+        for (int i = 0; i < unroll; i++) {
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+            NO_CHECK_ITER;  NO_CHECK_ITER;
+        }
+        if ((count &= 7) > 0) {
+            do {
+                NO_CHECK_ITER;
+            } while (--count != 0);
+        }
+    }
+    if ((count = range.fCount2) > 0) {
+        sk_memset32_dither(dstC,
+            cache[toggle + range.fV1],
+            cache[next_dither_toggle(toggle) + range.fV1],
+            count);
+    }
+}
+
+void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
+                             SkPMColor* SK_RESTRICT dstC,
+                             const SkPMColor* SK_RESTRICT cache,
+                             int toggle, int count) {
+    do {
+        unsigned fi = mirror_8bits(fx >> 8);
+        SkASSERT(fi <= 0xFF);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle = next_dither_toggle(toggle);
+    } while (--count != 0);
+}
+
+void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
+        SkPMColor* SK_RESTRICT dstC,
+        const SkPMColor* SK_RESTRICT cache,
+        int toggle, int count) {
+    do {
+        unsigned fi = repeat_8bits(fx >> 8);
+        SkASSERT(fi <= 0xFF);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle = next_dither_toggle(toggle);
+    } while (--count != 0);
+}
+
+}
+
+void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
+                                int count) {
+    SkASSERT(count > 0);
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = fTileProc;
+    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+#ifdef USE_DITHER_32BIT_GRADIENT
+    int                 toggle = init_dither_toggle(x, y);
+#else
+    int toggle = 0;
+#endif
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed dxStorage[1];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
+            dx = dxStorage[0];
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+        }
+
+        LinearShadeProc shadeProc = shadeSpan_linear_repeat;
+        if (SkFixedNearlyZero(dx, (SK_Fixed1 >> 14))) {
+            shadeProc = shadeSpan_linear_vertical_lerp;
+        } else if (SkShader::kClamp_TileMode == fTileMode) {
+            shadeProc = shadeSpan_linear_clamp;
+        } else if (SkShader::kMirror_TileMode == fTileMode) {
+            shadeProc = shadeSpan_linear_mirror;
+        } else {
+            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+        }
+        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
+    } else {
+        SkScalar    dstX = SkIntToScalar(x);
+        SkScalar    dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
+            SkASSERT(fi <= 0xFFFF);
+            *dstC++ = cache[toggle + (fi >> kCache32Shift)];
+            toggle = next_dither_toggle(toggle);
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap,
+                                                SkMatrix* matrix,
+                                                TileMode xy[]) const {
+    if (bitmap) {
+        this->getGradientTableBitmap(bitmap);
+    }
+    if (matrix) {
+        matrix->preConcat(fPtsToUnit);
+    }
+    if (xy) {
+        xy[0] = fTileMode;
+        xy[1] = kClamp_TileMode;
+    }
+    return kLinear_BitmapType;
+}
+
+SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info);
+        info->fPoint[0] = fStart;
+        info->fPoint[1] = fEnd;
+    }
+    return kLinear_GradientType;
+}
+
+static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other,
+                            int count) {
+    if (reinterpret_cast<uintptr_t>(dst) & 2) {
+        *dst++ = value;
+        count -= 1;
+        SkTSwap(value, other);
+    }
+
+    sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1);
+
+    if (count & 1) {
+        dst[count - 1] = value;
+    }
+}
+
+#define NO_CHECK_ITER_16                \
+    do {                                \
+    unsigned fi = fx >> SkGradientShaderBase::kCache16Shift;  \
+    SkASSERT(fi < SkGradientShaderBase::kCache16Count);       \
+    fx += dx;                           \
+    *dstC++ = cache[toggle + fi];       \
+    toggle = next_dither_toggle16(toggle);            \
+    } while (0)
+
+namespace {
+
+typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx,
+                                  uint16_t* dstC, const uint16_t* cache,
+                                  int toggle, int count);
+
+void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx,
+                                 uint16_t* SK_RESTRICT dstC,
+                                 const uint16_t* SK_RESTRICT cache,
+                                 int toggle, int count) {
+    // we're a vertical gradient, so no change in a span
+    unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift;
+    SkASSERT(fi < SkGradientShaderBase::kCache16Count);
+    dither_memset16(dstC, cache[toggle + fi],
+        cache[next_dither_toggle16(toggle) + fi], count);
+
+}
+
+void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx,
+                              uint16_t* SK_RESTRICT dstC,
+                              const uint16_t* SK_RESTRICT cache,
+                              int toggle, int count) {
+    SkClampRange range;
+    range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1);
+
+    if ((count = range.fCount0) > 0) {
+        dither_memset16(dstC,
+            cache[toggle + range.fV0],
+            cache[next_dither_toggle16(toggle) + range.fV0],
+            count);
+        dstC += count;
+    }
+    if ((count = range.fCount1) > 0) {
+        int unroll = count >> 3;
+        fx = range.fFx1;
+        for (int i = 0; i < unroll; i++) {
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+            NO_CHECK_ITER_16;  NO_CHECK_ITER_16;
+        }
+        if ((count &= 7) > 0) {
+            do {
+                NO_CHECK_ITER_16;
+            } while (--count != 0);
+        }
+    }
+    if ((count = range.fCount2) > 0) {
+        dither_memset16(dstC,
+            cache[toggle + range.fV1],
+            cache[next_dither_toggle16(toggle) + range.fV1],
+            count);
+    }
+}
+
+void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx,
+                               uint16_t* SK_RESTRICT dstC,
+                               const uint16_t* SK_RESTRICT cache,
+                               int toggle, int count) {
+    do {
+        unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift,
+                                        SkGradientShaderBase::kCache16Bits);
+        SkASSERT(fi < SkGradientShaderBase::kCache16Count);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle = next_dither_toggle16(toggle);
+    } while (--count != 0);
+}
+
+void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx,
+                               uint16_t* SK_RESTRICT dstC,
+                               const uint16_t* SK_RESTRICT cache,
+                               int toggle, int count) {
+    do {
+        unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift,
+                                  SkGradientShaderBase::kCache16Bits);
+        SkASSERT(fi < SkGradientShaderBase::kCache16Count);
+        fx += dx;
+        *dstC++ = cache[toggle + fi];
+        toggle = next_dither_toggle16(toggle);
+    } while (--count != 0);
+}
+}
+
+void SkLinearGradient::shadeSpan16(int x, int y,
+                                  uint16_t* SK_RESTRICT dstC, int count) {
+    SkASSERT(count > 0);
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = fTileProc;
+    const uint16_t* SK_RESTRICT cache = this->getCache16();
+    int                 toggle = init_dither_toggle16(x, y);
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkFixed dx, fx = SkScalarToFixed(srcPt.fX);
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed dxStorage[1];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL);
+            dx = dxStorage[0];
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = SkScalarToFixed(fDstToIndex.getScaleX());
+        }
+
+        LinearShade16Proc shadeProc = shadeSpan16_linear_repeat;
+        if (SkFixedNearlyZero(dx)) {
+            shadeProc = shadeSpan16_linear_vertical;
+        } else if (SkShader::kClamp_TileMode == fTileMode) {
+            shadeProc = shadeSpan16_linear_clamp;
+        } else if (SkShader::kMirror_TileMode == fTileMode) {
+            shadeProc = shadeSpan16_linear_mirror;
+        } else {
+            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+        }
+        (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count);
+    } else {
+        SkScalar    dstX = SkIntToScalar(x);
+        SkScalar    dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.fX));
+            SkASSERT(fi <= 0xFFFF);
+
+            int index = fi >> kCache16Shift;
+            *dstC++ = cache[toggle + index];
+            toggle = next_dither_toggle16(toggle);
+
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+#if SK_SUPPORT_GPU
+
+#include "GrTBackendEffectFactory.h"
+
+/////////////////////////////////////////////////////////////////////
+
+class GrGLLinearGradient : public GrGLGradientEffect {
+public:
+
+    GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrEffectRef&)
+                       : INHERITED (factory) { }
+
+    virtual ~GrGLLinearGradient() { }
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        return GenMatrixKey(stage);
+    }
+
+private:
+
+    typedef GrGLGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrLinearGradient : public GrGradientEffect {
+public:
+
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkLinearGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrLinearGradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrLinearGradient() { }
+
+    static const char* Name() { return "Linear Gradient"; }
+    const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<GrLinearGradient>::getInstance();
+    }
+
+    typedef GrGLLinearGradient GLEffect;
+
+private:
+    GrLinearGradient(GrContext* ctx,
+                     const SkLinearGradient& shader,
+                     const SkMatrix& matrix,
+                     SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm) { }
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrLinearGradient);
+
+GrEffectRef* GrLinearGradient::TestCreate(SkRandom* random,
+                                          GrContext* context,
+                                          GrTexture**) {
+    SkPoint points[] = {{random->nextUScalar1(), random->nextUScalar1()},
+                        {random->nextUScalar1(), random->nextUScalar1()}};
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tm;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points,
+                                                                 colors, stops, colorCount,
+                                                                 tm));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+void GrGLLinearGradient::emitCode(GrGLShaderBuilder* builder,
+                                  const GrEffectStage& stage,
+                                  EffectKey key,
+                                  const char* vertexCoords,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TextureSamplerArray& samplers) {
+    this->emitYCoordUniform(builder);
+    const char* coords;
+    this->setupMatrix(builder, key, vertexCoords, &coords);
+    SkString t;
+    t.append(coords);
+    t.append(".x");
+    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return NULL;
+    }
+    matrix.postConcat(fPtsToUnit);
+    return GrLinearGradient::Create(context, *this, matrix, fTileMode);
+}
+
+#else
+
+GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return NULL;
+}
+
+#endif
+
+#ifdef SK_DEVELOPER
+void SkLinearGradient::toString(SkString* str) const {
+    str->append("SkLinearGradient (");
+
+    str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
+    str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkLinearGradient.h b/src/effects/gradients/SkLinearGradient.h
new file mode 100644
index 0000000..ff1796b
--- /dev/null
+++ b/src/effects/gradients/SkLinearGradient.h
@@ -0,0 +1,40 @@
+
+/*
+ * 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 SkLinearGradient_DEFINED
+#define SkLinearGradient_DEFINED
+
+#include "SkGradientShaderPriv.h"
+
+class SkLinearGradient : public SkGradientShaderBase {
+public:
+    SkLinearGradient(const SkPoint pts[2],
+                     const SkColor colors[], const SkScalar pos[], int colorCount,
+                     SkShader::TileMode mode, SkUnitMapper* mapper);
+
+    virtual bool setContext(const SkBitmap&, const SkPaint&, const SkMatrix&) SK_OVERRIDE;
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+    virtual BitmapType asABitmap(SkBitmap*, SkMatrix*, TileMode*) const SK_OVERRIDE;
+    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkLinearGradient)
+
+protected:
+    SkLinearGradient(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+
+private:
+    typedef SkGradientShaderBase INHERITED;
+    const SkPoint fStart;
+    const SkPoint fEnd;
+};
+
+#endif
diff --git a/src/effects/gradients/SkRadialGradient.cpp b/src/effects/gradients/SkRadialGradient.cpp
new file mode 100644
index 0000000..328fe76
--- /dev/null
+++ b/src/effects/gradients/SkRadialGradient.cpp
@@ -0,0 +1,615 @@
+
+/*
+ * 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 "SkRadialGradient.h"
+#include "SkRadialGradient_Table.h"
+
+#define kSQRT_TABLE_BITS    11
+#define kSQRT_TABLE_SIZE    (1 << kSQRT_TABLE_BITS)
+
+#if defined(SK_BUILD_FOR_WIN32) && defined(SK_DEBUG)
+
+#include <stdio.h>
+
+void SkRadialGradient_BuildTable() {
+    // build it 0..127 x 0..127, so we use 2^15 - 1 in the numerator for our "fixed" table
+
+    FILE* file = ::fopen("SkRadialGradient_Table.h", "w");
+    SkASSERT(file);
+    ::fprintf(file, "static const uint8_t gSqrt8Table[] = {\n");
+
+    for (int i = 0; i < kSQRT_TABLE_SIZE; i++) {
+        if ((i & 15) == 0) {
+            ::fprintf(file, "\t");
+        }
+
+        uint8_t value = SkToU8(SkFixedSqrt(i * SK_Fixed1 / kSQRT_TABLE_SIZE) >> 8);
+
+        ::fprintf(file, "0x%02X", value);
+        if (i < kSQRT_TABLE_SIZE-1) {
+            ::fprintf(file, ", ");
+        }
+        if ((i & 15) == 15) {
+            ::fprintf(file, "\n");
+        }
+    }
+    ::fprintf(file, "};\n");
+    ::fclose(file);
+}
+
+#endif
+
+namespace {
+
+void rad_to_unit_matrix(const SkPoint& center, SkScalar radius,
+                               SkMatrix* matrix) {
+    SkScalar    inv = SkScalarInvert(radius);
+
+    matrix->setTranslate(-center.fX, -center.fY);
+    matrix->postScale(inv, inv);
+}
+
+typedef void (* RadialShade16Proc)(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        uint16_t* dstC, const uint16_t* cache,
+        int toggle, int count);
+
+void shadeSpan16_radial_clamp(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
+        int toggle, int count) {
+    const uint8_t* SK_RESTRICT sqrt_table = gSqrt8Table;
+
+    /* knock these down so we can pin against +- 0x7FFF, which is an
+       immediate load, rather than 0xFFFF which is slower. This is a
+       compromise, since it reduces our precision, but that appears
+       to be visually OK. If we decide this is OK for all of our cases,
+       we could (it seems) put this scale-down into fDstToIndex,
+       to avoid having to do these extra shifts each time.
+    */
+    SkFixed fx = SkScalarToFixed(sfx) >> 1;
+    SkFixed dx = SkScalarToFixed(sdx) >> 1;
+    SkFixed fy = SkScalarToFixed(sfy) >> 1;
+    SkFixed dy = SkScalarToFixed(sdy) >> 1;
+    // might perform this check for the other modes,
+    // but the win will be a smaller % of the total
+    if (dy == 0) {
+        fy = SkPin32(fy, -0xFFFF >> 1, 0xFFFF >> 1);
+        fy *= fy;
+        do {
+            unsigned xx = SkPin32(fx, -0xFFFF >> 1, 0xFFFF >> 1);
+            unsigned fi = (xx * xx + fy) >> (14 + 16 - kSQRT_TABLE_BITS);
+            fi = SkFastMin32(fi, 0xFFFF >> (16 - kSQRT_TABLE_BITS));
+            fx += dx;
+            *dstC++ = cache[toggle +
+                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
+            toggle = next_dither_toggle16(toggle);
+        } 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));
+            fx += dx;
+            fy += dy;
+            *dstC++ = cache[toggle +
+                            (sqrt_table[fi] >> SkGradientShaderBase::kSqrt16Shift)];
+            toggle = next_dither_toggle16(toggle);
+        } while (--count != 0);
+    }
+}
+
+void shadeSpan16_radial_mirror(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
+        int toggle, int count) {
+    do {
+#ifdef SK_SCALAR_IS_FLOAT
+        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
+        SkFixed dist = SkFloatToFixed(fdist);
+#else
+        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
+            SkFixedSquare(sfy);
+        if (magnitudeSquared < 0) // Overflow.
+            magnitudeSquared = SK_FixedMax;
+        SkFixed dist = SkFixedSqrt(magnitudeSquared);
+#endif
+        unsigned fi = mirror_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
+        toggle = next_dither_toggle16(toggle);
+        sfx += sdx;
+        sfy += sdy;
+    } while (--count != 0);
+}
+
+void shadeSpan16_radial_repeat(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        uint16_t* SK_RESTRICT dstC, const uint16_t* SK_RESTRICT cache,
+        int toggle, int count) {
+    SkFixed fx = SkScalarToFixed(sfx);
+    SkFixed dx = SkScalarToFixed(sdx);
+    SkFixed fy = SkScalarToFixed(sfy);
+    SkFixed dy = SkScalarToFixed(sdy);
+    do {
+        SkFixed dist = SkFixedSqrt(SkFixedSquare(fx) + SkFixedSquare(fy));
+        unsigned fi = repeat_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        fx += dx;
+        fy += dy;
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache16Shift)];
+        toggle = next_dither_toggle16(toggle);
+    } while (--count != 0);
+}
+
+}
+
+/////////////////////////////////////////////////////////////////////
+
+SkRadialGradient::SkRadialGradient(const SkPoint& center, SkScalar radius,
+                const SkColor colors[], const SkScalar pos[], int colorCount,
+                SkShader::TileMode mode, SkUnitMapper* mapper)
+    : SkGradientShaderBase(colors, pos, colorCount, mode, mapper),
+      fCenter(center),
+      fRadius(radius)
+{
+    // make sure our table is insync with our current #define for kSQRT_TABLE_SIZE
+    SkASSERT(sizeof(gSqrt8Table) == kSQRT_TABLE_SIZE);
+
+    rad_to_unit_matrix(center, radius, &fPtsToUnit);
+}
+
+void SkRadialGradient::shadeSpan16(int x, int y, uint16_t* dstCParam,
+                         int count) {
+    SkASSERT(count > 0);
+
+    uint16_t* SK_RESTRICT dstC = dstCParam;
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = fTileProc;
+    const uint16_t* SK_RESTRICT cache = this->getCache16();
+    int                 toggle = init_dither_toggle16(x, y);
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+        SkScalar sdx = fDstToIndex.getScaleX();
+        SkScalar sdy = fDstToIndex.getSkewY();
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed storage[2];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
+                                           &storage[0], &storage[1]);
+            sdx = SkFixedToScalar(storage[0]);
+            sdy = SkFixedToScalar(storage[1]);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+        }
+
+        RadialShade16Proc shadeProc = shadeSpan16_radial_repeat;
+        if (SkShader::kClamp_TileMode == fTileMode) {
+            shadeProc = shadeSpan16_radial_clamp;
+        } else if (SkShader::kMirror_TileMode == fTileMode) {
+            shadeProc = shadeSpan16_radial_mirror;
+        } else {
+            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+        }
+        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC,
+                     cache, toggle, count);
+    } else {    // perspective case
+        SkScalar dstX = SkIntToScalar(x);
+        SkScalar dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
+            SkASSERT(fi <= 0xFFFF);
+
+            int index = fi >> (16 - kCache16Bits);
+            *dstC++ = cache[toggle + index];
+            toggle = next_dither_toggle16(toggle);
+
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+SkShader::BitmapType SkRadialGradient::asABitmap(SkBitmap* bitmap,
+    SkMatrix* matrix, SkShader::TileMode* xy) const {
+    if (bitmap) {
+        this->getGradientTableBitmap(bitmap);
+    }
+    if (matrix) {
+        matrix->setScale(SkIntToScalar(kCache32Count),
+                         SkIntToScalar(kCache32Count));
+        matrix->preConcat(fPtsToUnit);
+    }
+    if (xy) {
+        xy[0] = fTileMode;
+        xy[1] = kClamp_TileMode;
+    }
+    return kRadial_BitmapType;
+}
+
+SkShader::GradientType SkRadialGradient::asAGradient(GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info);
+        info->fPoint[0] = fCenter;
+        info->fRadius[0] = fRadius;
+    }
+    return kRadial_GradientType;
+}
+
+SkRadialGradient::SkRadialGradient(SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer),
+      fCenter(buffer.readPoint()),
+      fRadius(buffer.readScalar()) {
+}
+
+void SkRadialGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fCenter);
+    buffer.writeScalar(fRadius);
+}
+
+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);
+
+    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)) {
+        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;
+        }
+        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)];
+                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);
+        }
+    }
+}
+
+// Unrolling this loop doesn't seem to help (when float); we're stalling to
+// get the results of the sqrt (?), and don't have enough extra registers to
+// have many in flight.
+void shadeSpan_radial_mirror(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+        int count, int toggle) {
+    do {
+#ifdef SK_SCALAR_IS_FLOAT
+        float fdist = sk_float_sqrt(sfx*sfx + sfy*sfy);
+        SkFixed dist = SkFloatToFixed(fdist);
+#else
+        SkFixed magnitudeSquared = SkFixedSquare(sfx) +
+            SkFixedSquare(sfy);
+        if (magnitudeSquared < 0) // Overflow.
+            magnitudeSquared = SK_FixedMax;
+        SkFixed dist = SkFixedSqrt(magnitudeSquared);
+#endif
+        unsigned fi = mirror_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
+        toggle = next_dither_toggle(toggle);
+        sfx += sdx;
+        sfy += sdy;
+    } while (--count != 0);
+}
+
+void shadeSpan_radial_repeat(SkScalar sfx, SkScalar sdx,
+        SkScalar sfy, SkScalar sdy,
+        SkPMColor* SK_RESTRICT dstC, const SkPMColor* SK_RESTRICT cache,
+        int count, int toggle) {
+    SkFixed fx = SkScalarToFixed(sfx);
+    SkFixed dx = SkScalarToFixed(sdx);
+    SkFixed fy = SkScalarToFixed(sfy);
+    SkFixed dy = SkScalarToFixed(sdy);
+    do {
+        SkFixed magnitudeSquared = SkFixedSquare(fx) +
+            SkFixedSquare(fy);
+        if (magnitudeSquared < 0) // Overflow.
+            magnitudeSquared = SK_FixedMax;
+        SkFixed dist = SkFixedSqrt(magnitudeSquared);
+        unsigned fi = repeat_tileproc(dist);
+        SkASSERT(fi <= 0xFFFF);
+        *dstC++ = cache[toggle + (fi >> SkGradientShaderBase::kCache32Shift)];
+        toggle = next_dither_toggle(toggle);
+        fx += dx;
+        fy += dy;
+    } while (--count != 0);
+}
+}
+
+void SkRadialGradient::shadeSpan(int x, int y,
+                                SkPMColor* SK_RESTRICT dstC, int count) {
+    SkASSERT(count > 0);
+
+    SkPoint             srcPt;
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = fTileProc;
+    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+#ifdef USE_DITHER_32BIT_GRADIENT
+    int toggle = init_dither_toggle(x, y);
+#else
+    int toggle = 0;
+#endif
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
+                             SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar sdx = fDstToIndex.getScaleX();
+        SkScalar sdy = fDstToIndex.getSkewY();
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed storage[2];
+            (void)fDstToIndex.fixedStepInX(SkIntToScalar(y),
+                                           &storage[0], &storage[1]);
+            sdx = SkFixedToScalar(storage[0]);
+            sdy = SkFixedToScalar(storage[1]);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+        }
+
+        RadialShadeProc shadeProc = shadeSpan_radial_repeat;
+        if (SkShader::kClamp_TileMode == fTileMode) {
+            shadeProc = shadeSpan_radial_clamp;
+        } else if (SkShader::kMirror_TileMode == fTileMode) {
+            shadeProc = shadeSpan_radial_mirror;
+        } else {
+            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+        }
+        (*shadeProc)(srcPt.fX, sdx, srcPt.fY, sdy, dstC, cache, count, toggle);
+    } else {    // perspective case
+        SkScalar dstX = SkIntToScalar(x);
+        SkScalar dstY = SkIntToScalar(y);
+        do {
+            dstProc(fDstToIndex, dstX, dstY, &srcPt);
+            unsigned fi = proc(SkScalarToFixed(srcPt.length()));
+            SkASSERT(fi <= 0xFFFF);
+            *dstC++ = cache[fi >> SkGradientShaderBase::kCache32Shift];
+            dstX += SK_Scalar1;
+        } while (--count != 0);
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrTBackendEffectFactory.h"
+
+class GrGLRadialGradient : public GrGLGradientEffect {
+public:
+
+    GrGLRadialGradient(const GrBackendEffectFactory& factory,
+                       const GrEffectRef&) : INHERITED (factory) { }
+    virtual ~GrGLRadialGradient() { }
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        return GenMatrixKey(stage);
+    }
+
+private:
+
+    typedef GrGLGradientEffect INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrRadialGradient : public GrGradientEffect {
+public:
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkRadialGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrRadialGradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrRadialGradient() { }
+
+    static const char* Name() { return "Radial Gradient"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<GrRadialGradient>::getInstance();
+    }
+
+    typedef GrGLRadialGradient GLEffect;
+
+private:
+    GrRadialGradient(GrContext* ctx,
+                     const SkRadialGradient& shader,
+                     const SkMatrix& matrix,
+                     SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm) {
+    }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrRadialGradient);
+
+GrEffectRef* GrRadialGradient::TestCreate(SkRandom* random,
+                                          GrContext* context,
+                                          GrTexture**) {
+    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
+    SkScalar radius = random->nextUScalar1();
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tm;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateRadial(center, radius,
+                                                                 colors, stops, colorCount,
+                                                                 tm));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+void GrGLRadialGradient::emitCode(GrGLShaderBuilder* builder,
+                                  const GrEffectStage& stage,
+                                  EffectKey key,
+                                  const char* vertexCoords,
+                                  const char* outputColor,
+                                  const char* inputColor,
+                                  const TextureSamplerArray& samplers) {
+    this->emitYCoordUniform(builder);
+    const char* coords;
+    this->setupMatrix(builder, key, vertexCoords, &coords);
+    SkString t("length(");
+    t.append(coords);
+    t.append(")");
+    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrEffectRef* SkRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
+
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return NULL;
+    }
+    matrix.postConcat(fPtsToUnit);
+    return GrRadialGradient::Create(context, *this, matrix, fTileMode);
+}
+
+#else
+
+GrEffectRef* SkRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return NULL;
+}
+
+#endif
+
+#ifdef SK_DEVELOPER
+void SkRadialGradient::toString(SkString* str) const {
+    str->append("SkRadialGradient: (");
+
+    str->append("center: (");
+    str->appendScalar(fCenter.fX);
+    str->append(", ");
+    str->appendScalar(fCenter.fY);
+    str->append(") radius: ");
+    str->appendScalar(fRadius);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkRadialGradient.h b/src/effects/gradients/SkRadialGradient.h
new file mode 100644
index 0000000..83f79ae
--- /dev/null
+++ b/src/effects/gradients/SkRadialGradient.h
@@ -0,0 +1,42 @@
+
+/*
+ * 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 SkRadialGradient_DEFINED
+#define SkRadialGradient_DEFINED
+
+#include "SkGradientShaderPriv.h"
+
+class SkRadialGradient : public SkGradientShaderBase {
+public:
+    SkRadialGradient(const SkPoint& center, SkScalar radius,
+                    const SkColor colors[], const SkScalar pos[], int colorCount,
+                    SkShader::TileMode mode, SkUnitMapper* mapper);
+    virtual void shadeSpan(int x, int y, SkPMColor* dstC, int count)
+        SK_OVERRIDE;
+    virtual void shadeSpan16(int x, int y, uint16_t* dstCParam,
+                             int count) SK_OVERRIDE;
+    virtual BitmapType asABitmap(SkBitmap* bitmap,
+                                 SkMatrix* matrix,
+                                 TileMode* xy) const SK_OVERRIDE;
+    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkRadialGradient)
+
+protected:
+    SkRadialGradient(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+
+private:
+    typedef SkGradientShaderBase INHERITED;
+    const SkPoint fCenter;
+    const SkScalar fRadius;
+};
+
+#endif
diff --git a/src/effects/gradients/SkRadialGradient_Table.h b/src/effects/gradients/SkRadialGradient_Table.h
new file mode 100644
index 0000000..9a8a5f8
--- /dev/null
+++ b/src/effects/gradients/SkRadialGradient_Table.h
@@ -0,0 +1,139 @@
+
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+static const uint8_t gSqrt8Table[] = {
+    0x00, 0x05, 0x08, 0x09, 0x0B, 0x0C, 0x0D, 0x0E, 0x10, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x15,
+    0x16, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F,
+    0x20, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x26, 0x26,
+    0x27, 0x27, 0x28, 0x28, 0x28, 0x29, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2B, 0x2C, 0x2C, 0x2C,
+    0x2D, 0x2D, 0x2D, 0x2E, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x30, 0x30, 0x30, 0x31, 0x31, 0x31, 0x32,
+    0x32, 0x32, 0x33, 0x33, 0x33, 0x34, 0x34, 0x34, 0x35, 0x35, 0x35, 0x35, 0x36, 0x36, 0x36, 0x37,
+    0x37, 0x37, 0x38, 0x38, 0x38, 0x38, 0x39, 0x39, 0x39, 0x39, 0x3A, 0x3A, 0x3A, 0x3B, 0x3B, 0x3B,
+    0x3B, 0x3C, 0x3C, 0x3C, 0x3C, 0x3D, 0x3D, 0x3D, 0x3D, 0x3E, 0x3E, 0x3E, 0x3E, 0x3F, 0x3F, 0x3F,
+    0x40, 0x40, 0x40, 0x40, 0x40, 0x41, 0x41, 0x41, 0x41, 0x42, 0x42, 0x42, 0x42, 0x43, 0x43, 0x43,
+    0x43, 0x44, 0x44, 0x44, 0x44, 0x45, 0x45, 0x45, 0x45, 0x45, 0x46, 0x46, 0x46, 0x46, 0x47, 0x47,
+    0x47, 0x47, 0x48, 0x48, 0x48, 0x48, 0x48, 0x49, 0x49, 0x49, 0x49, 0x49, 0x4A, 0x4A, 0x4A, 0x4A,
+    0x4B, 0x4B, 0x4B, 0x4B, 0x4B, 0x4C, 0x4C, 0x4C, 0x4C, 0x4C, 0x4D, 0x4D, 0x4D, 0x4D, 0x4D, 0x4E,
+    0x4E, 0x4E, 0x4E, 0x4E, 0x4F, 0x4F, 0x4F, 0x4F, 0x50, 0x50, 0x50, 0x50, 0x50, 0x50, 0x51, 0x51,
+    0x51, 0x51, 0x51, 0x52, 0x52, 0x52, 0x52, 0x52, 0x53, 0x53, 0x53, 0x53, 0x53, 0x54, 0x54, 0x54,
+    0x54, 0x54, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x56, 0x56, 0x56, 0x56, 0x56, 0x57, 0x57, 0x57,
+    0x57, 0x57, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x59, 0x59, 0x59, 0x59, 0x59, 0x59, 0x5A, 0x5A,
+    0x5A, 0x5A, 0x5A, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5B, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5C, 0x5D,
+    0x5D, 0x5D, 0x5D, 0x5D, 0x5D, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5E, 0x5F, 0x5F, 0x5F, 0x5F, 0x5F,
+    0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x61, 0x61, 0x61, 0x61, 0x61, 0x61, 0x62, 0x62, 0x62,
+    0x62, 0x62, 0x62, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x64, 0x64, 0x64, 0x64, 0x64, 0x64, 0x65,
+    0x65, 0x65, 0x65, 0x65, 0x65, 0x65, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x67, 0x67, 0x67, 0x67,
+    0x67, 0x67, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69, 0x69,
+    0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6A, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6B, 0x6C, 0x6C, 0x6C,
+    0x6C, 0x6C, 0x6C, 0x6C, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6D, 0x6E, 0x6E, 0x6E, 0x6E, 0x6E,
+    0x6E, 0x6E, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x6F, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+    0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x71, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x72, 0x73, 0x73,
+    0x73, 0x73, 0x73, 0x73, 0x73, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74, 0x75, 0x75, 0x75, 0x75,
+    0x75, 0x75, 0x75, 0x75, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x76, 0x77, 0x77, 0x77, 0x77, 0x77,
+    0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
+    0x79, 0x79, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7A, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B, 0x7B,
+    0x7B, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7C, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D, 0x7D,
+    0x7D, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F,
+    0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81,
+    0x81, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x82, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83, 0x83,
+    0x83, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x84, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85, 0x85,
+    0x85, 0x85, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x86, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87,
+    0x87, 0x87, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89, 0x89, 0x89, 0x89, 0x89,
+    0x89, 0x89, 0x89, 0x89, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8A, 0x8B, 0x8B, 0x8B, 0x8B,
+    0x8B, 0x8B, 0x8B, 0x8B, 0x8B, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8C, 0x8D, 0x8D,
+    0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8D, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E, 0x8E,
+    0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x8F, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90,
+    0x90, 0x90, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x91, 0x92, 0x92, 0x92, 0x92, 0x92,
+    0x92, 0x92, 0x92, 0x92, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x93, 0x94, 0x94, 0x94,
+    0x94, 0x94, 0x94, 0x94, 0x94, 0x94, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95, 0x95,
+    0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x96, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97, 0x97,
+    0x97, 0x97, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x99, 0x99, 0x99, 0x99,
+    0x99, 0x99, 0x99, 0x99, 0x99, 0x99, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9A, 0x9B,
+    0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9B, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C, 0x9C,
+    0x9C, 0x9C, 0x9C, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9D, 0x9E, 0x9E, 0x9E,
+    0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9E, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F, 0x9F,
+    0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA0, 0xA1, 0xA1, 0xA1, 0xA1, 0xA1,
+    0xA1, 0xA1, 0xA1, 0xA1, 0xA1, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA2, 0xA3,
+    0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA3, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4, 0xA4,
+    0xA4, 0xA4, 0xA4, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA5, 0xA6, 0xA6,
+    0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7, 0xA7,
+    0xA7, 0xA7, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA9, 0xA9, 0xA9,
+    0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xA9, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
+    0xAA, 0xAA, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAB, 0xAC, 0xAC, 0xAC,
+    0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAC, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD, 0xAD,
+    0xAD, 0xAD, 0xAD, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAE, 0xAF, 0xAF,
+    0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xAF, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0, 0xB0,
+    0xB0, 0xB0, 0xB0, 0xB0, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB1, 0xB2,
+    0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB2, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB3,
+    0xB3, 0xB3, 0xB3, 0xB3, 0xB3, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4, 0xB4,
+    0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB5, 0xB6, 0xB6, 0xB6, 0xB6,
+    0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB6, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7, 0xB7,
+    0xB7, 0xB7, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB9, 0xB9,
+    0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xB9, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBA,
+    0xBA, 0xBA, 0xBA, 0xBA, 0xBA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
+    0xBB, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBC, 0xBD, 0xBD, 0xBD,
+    0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBD, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBE,
+    0xBE, 0xBE, 0xBE, 0xBE, 0xBE, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF, 0xBF,
+    0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC0, 0xC1, 0xC1, 0xC1,
+    0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC1, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC2,
+    0xC2, 0xC2, 0xC2, 0xC2, 0xC2, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3,
+    0xC3, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC4, 0xC5, 0xC5, 0xC5,
+    0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC5, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6,
+    0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7, 0xC7,
+    0xC7, 0xC7, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC9,
+    0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xC9, 0xCA, 0xCA, 0xCA, 0xCA,
+    0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCA, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCB,
+    0xCB, 0xCB, 0xCB, 0xCB, 0xCB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
+    0xCC, 0xCC, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCD, 0xCE,
+    0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCE, 0xCF, 0xCF, 0xCF, 0xCF,
+    0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xCF, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0,
+    0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD0, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1, 0xD1,
+    0xD1, 0xD1, 0xD1, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2, 0xD2,
+    0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD3, 0xD4, 0xD4, 0xD4,
+    0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD4, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5,
+    0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD5, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6,
+    0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7, 0xD7,
+    0xD7, 0xD7, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+    0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xD9, 0xDA, 0xDA,
+    0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDA, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB,
+    0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC,
+    0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD,
+    0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE, 0xDE,
+    0xDE, 0xDE, 0xDE, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF, 0xDF,
+    0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE0, 0xE1,
+    0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE1, 0xE2, 0xE2, 0xE2,
+    0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE2, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3,
+    0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE3, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4,
+    0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE4, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5,
+    0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE5, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6, 0xE6,
+    0xE6, 0xE6, 0xE6, 0xE6, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7, 0xE7,
+    0xE7, 0xE7, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
+    0xE8, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9, 0xE9,
+    0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEA, 0xEB, 0xEB,
+    0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEB, 0xEC, 0xEC, 0xEC,
+    0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xEC, 0xED, 0xED, 0xED, 0xED,
+    0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xED, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
+    0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF,
+    0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xEF, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
+    0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1,
+    0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF1, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+    0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF3,
+    0xF3, 0xF3, 0xF3, 0xF3, 0xF3, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4, 0xF4,
+    0xF4, 0xF4, 0xF4, 0xF4, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5, 0xF5,
+    0xF5, 0xF5, 0xF5, 0xF5, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6, 0xF6,
+    0xF6, 0xF6, 0xF6, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7, 0xF7,
+    0xF7, 0xF7, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
+    0xF8, 0xF8, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9, 0xF9,
+    0xF9, 0xF9, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA, 0xFA,
+    0xFA, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB, 0xFB,
+    0xFB, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC, 0xFC,
+    0xFC, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD, 0xFD,
+    0xFD, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE,
+    0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
diff --git a/src/effects/gradients/SkSweepGradient.cpp b/src/effects/gradients/SkSweepGradient.cpp
new file mode 100644
index 0000000..db18521
--- /dev/null
+++ b/src/effects/gradients/SkSweepGradient.cpp
@@ -0,0 +1,512 @@
+
+/*
+ * 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 "SkSweepGradient.h"
+
+SkSweepGradient::SkSweepGradient(SkScalar cx, SkScalar cy, const SkColor colors[],
+               const SkScalar pos[], int count, SkUnitMapper* mapper)
+: SkGradientShaderBase(colors, pos, count, SkShader::kClamp_TileMode, mapper),
+  fCenter(SkPoint::Make(cx, cy))
+{
+    fPtsToUnit.setTranslate(-cx, -cy);
+}
+
+SkShader::BitmapType SkSweepGradient::asABitmap(SkBitmap* bitmap,
+    SkMatrix* matrix, SkShader::TileMode* xy) const {
+    if (bitmap) {
+        this->getGradientTableBitmap(bitmap);
+    }
+    if (matrix) {
+        *matrix = fPtsToUnit;
+    }
+    if (xy) {
+        xy[0] = fTileMode;
+        xy[1] = kClamp_TileMode;
+    }
+    return kSweep_BitmapType;
+}
+
+SkShader::GradientType SkSweepGradient::asAGradient(GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info);
+        info->fPoint[0] = fCenter;
+    }
+    return kSweep_GradientType;
+}
+
+SkSweepGradient::SkSweepGradient(SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer),
+      fCenter(buffer.readPoint()) {
+}
+
+void SkSweepGradient::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fCenter);
+}
+
+#ifndef SK_SCALAR_IS_FLOAT
+#ifdef COMPUTE_SWEEP_TABLE
+#define PI  3.14159265
+static bool gSweepTableReady;
+static uint8_t gSweepTable[65];
+
+/*  Our table stores precomputed values for atan: [0...1] -> [0..PI/4]
+    We scale the results to [0..32]
+*/
+static const uint8_t* build_sweep_table() {
+    if (!gSweepTableReady) {
+        const int N = 65;
+        const double DENOM = N - 1;
+
+        for (int i = 0; i < N; i++)
+        {
+            double arg = i / DENOM;
+            double v = atan(arg);
+            int iv = (int)round(v * DENOM * 2 / PI);
+//            printf("[%d] atan(%g) = %g %d\n", i, arg, v, iv);
+            printf("%d, ", iv);
+            gSweepTable[i] = iv;
+        }
+        gSweepTableReady = true;
+    }
+    return gSweepTable;
+}
+#else
+static const uint8_t gSweepTable[] = {
+    0, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9,
+    10, 11, 11, 12, 12, 13, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18,
+    19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 25, 26,
+    26, 27, 27, 27, 28, 28, 29, 29, 29, 30, 30, 30, 31, 31, 31, 32,
+    32
+};
+static const uint8_t* build_sweep_table() { return gSweepTable; }
+#endif
+#endif
+
+// divide numer/denom, with a bias of 6bits. Assumes numer <= denom
+// and denom != 0. Since our table is 6bits big (+1), this is a nice fit.
+// Same as (but faster than) SkFixedDiv(numer, denom) >> 10
+
+//unsigned div_64(int numer, int denom);
+#ifndef SK_SCALAR_IS_FLOAT
+static unsigned div_64(int numer, int denom) {
+    SkASSERT(numer <= denom);
+    SkASSERT(numer > 0);
+    SkASSERT(denom > 0);
+
+    int nbits = SkCLZ(numer);
+    int dbits = SkCLZ(denom);
+    int bits = 6 - nbits + dbits;
+    SkASSERT(bits <= 6);
+
+    if (bits < 0) {  // detect underflow
+        return 0;
+    }
+
+    denom <<= dbits - 1;
+    numer <<= nbits - 1;
+
+    unsigned result = 0;
+
+    // do the first one
+    if ((numer -= denom) >= 0) {
+        result = 1;
+    } else {
+        numer += denom;
+    }
+
+    // Now fall into our switch statement if there are more bits to compute
+    if (bits > 0) {
+        // make room for the rest of the answer bits
+        result <<= bits;
+        switch (bits) {
+        case 6:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 32;
+            else
+                numer += denom;
+        case 5:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 16;
+            else
+                numer += denom;
+        case 4:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 8;
+            else
+                numer += denom;
+        case 3:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 4;
+            else
+                numer += denom;
+        case 2:
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 2;
+            else
+                numer += denom;
+        case 1:
+        default:    // not strictly need, but makes GCC make better ARM code
+            if ((numer = (numer << 1) - denom) >= 0)
+                result |= 1;
+            else
+                numer += denom;
+        }
+    }
+    return result;
+}
+#endif
+
+// Given x,y in the first quadrant, return 0..63 for the angle [0..90]
+#ifndef SK_SCALAR_IS_FLOAT
+static unsigned atan_0_90(SkFixed y, SkFixed x) {
+#ifdef SK_DEBUG
+    {
+        static bool gOnce;
+        if (!gOnce) {
+            gOnce = true;
+            SkASSERT(div_64(55, 55) == 64);
+            SkASSERT(div_64(128, 256) == 32);
+            SkASSERT(div_64(2326528, 4685824) == 31);
+            SkASSERT(div_64(753664, 5210112) == 9);
+            SkASSERT(div_64(229376, 4882432) == 3);
+            SkASSERT(div_64(2, 64) == 2);
+            SkASSERT(div_64(1, 64) == 1);
+            // test that we handle underflow correctly
+            SkASSERT(div_64(12345, 0x54321234) == 0);
+        }
+    }
+#endif
+
+    SkASSERT(y > 0 && x > 0);
+    const uint8_t* table = build_sweep_table();
+
+    unsigned result;
+    bool swap = (x < y);
+    if (swap) {
+        // first part of the atan(v) = PI/2 - atan(1/v) identity
+        // since our div_64 and table want v <= 1, where v = y/x
+        SkTSwap<SkFixed>(x, y);
+    }
+
+    result = div_64(y, x);
+
+#ifdef SK_DEBUG
+    {
+        unsigned result2 = SkDivBits(y, x, 6);
+        SkASSERT(result2 == result ||
+                 (result == 1 && result2 == 0));
+    }
+#endif
+
+    SkASSERT(result < SK_ARRAY_COUNT(gSweepTable));
+    result = table[result];
+
+    if (swap) {
+        // complete the atan(v) = PI/2 - atan(1/v) identity
+        result = 64 - result;
+        // pin to 63
+        result -= result >> 6;
+    }
+
+    SkASSERT(result <= 63);
+    return result;
+}
+#endif
+
+//  returns angle in a circle [0..2PI) -> [0..255]
+#ifdef SK_SCALAR_IS_FLOAT
+static unsigned SkATan2_255(float y, float x) {
+    //    static const float g255Over2PI = 255 / (2 * SK_ScalarPI);
+    static const float g255Over2PI = 40.584510488433314f;
+
+    float result = sk_float_atan2(y, x);
+    if (result < 0) {
+        result += 2 * SK_ScalarPI;
+    }
+    SkASSERT(result >= 0);
+    // since our value is always >= 0, we can cast to int, which is faster than
+    // calling floorf()
+    int ir = (int)(result * g255Over2PI);
+    SkASSERT(ir >= 0 && ir <= 255);
+    return ir;
+}
+#else
+static unsigned SkATan2_255(SkFixed y, SkFixed x) {
+    if (x == 0) {
+        if (y == 0) {
+            return 0;
+        }
+        return y < 0 ? 192 : 64;
+    }
+    if (y == 0) {
+        return x < 0 ? 128 : 0;
+    }
+
+    /*  Find the right quadrant for x,y
+        Since atan_0_90 only handles the first quadrant, we rotate x,y
+        appropriately before calling it, and then add the right amount
+        to account for the real quadrant.
+        quadrant 0 : add 0                  | x > 0 && y > 0
+        quadrant 1 : add 64 (90 degrees)    | x < 0 && y > 0
+        quadrant 2 : add 128 (180 degrees)  | x < 0 && y < 0
+        quadrant 3 : add 192 (270 degrees)  | x > 0 && y < 0
+
+        map x<0 to (1 << 6)
+        map y<0 to (3 << 6)
+        add = map_x ^ map_y
+    */
+    int xsign = x >> 31;
+    int ysign = y >> 31;
+    int add = ((-xsign) ^ (ysign & 3)) << 6;
+
+#ifdef SK_DEBUG
+    if (0 == add)
+        SkASSERT(x > 0 && y > 0);
+    else if (64 == add)
+        SkASSERT(x < 0 && y > 0);
+    else if (128 == add)
+        SkASSERT(x < 0 && y < 0);
+    else if (192 == add)
+        SkASSERT(x > 0 && y < 0);
+    else
+        SkDEBUGFAIL("bad value for add");
+#endif
+
+    /*  This ^ trick makes x, y positive, and the swap<> handles quadrants
+        where we need to rotate x,y by 90 or -90
+    */
+    x = (x ^ xsign) - xsign;
+    y = (y ^ ysign) - ysign;
+    if (add & 64) {             // quads 1 or 3 need to swap x,y
+        SkTSwap<SkFixed>(x, y);
+    }
+
+    unsigned result = add + atan_0_90(y, x);
+    SkASSERT(result < 256);
+    return result;
+}
+#endif
+
+void SkSweepGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
+                               int count) {
+    SkMatrix::MapXYProc proc = fDstToIndexProc;
+    const SkMatrix&     matrix = fDstToIndex;
+    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+    SkPoint             srcPt;
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar dx, fx = srcPt.fX;
+        SkScalar dy, fy = srcPt.fY;
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed storage[2];
+            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
+                                      &storage[0], &storage[1]);
+            dx = SkFixedToScalar(storage[0]);
+            dy = SkFixedToScalar(storage[1]);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = matrix.getScaleX();
+            dy = matrix.getSkewY();
+        }
+
+        for (; count > 0; --count) {
+            *dstC++ = cache[SkATan2_255(fy, fx)];
+            fx += dx;
+            fy += dy;
+        }
+    } else {  // perspective case
+        for (int stop = x + count; x < stop; x++) {
+            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+            *dstC++ = cache[SkATan2_255(srcPt.fY, srcPt.fX)];
+        }
+    }
+}
+
+void SkSweepGradient::shadeSpan16(int x, int y, uint16_t* SK_RESTRICT dstC,
+                                 int count) {
+    SkMatrix::MapXYProc proc = fDstToIndexProc;
+    const SkMatrix&     matrix = fDstToIndex;
+    const uint16_t* SK_RESTRICT cache = this->getCache16();
+    int                 toggle = init_dither_toggle16(x, y);
+    SkPoint             srcPt;
+
+    if (fDstToIndexClass != kPerspective_MatrixClass) {
+        proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                     SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+        SkScalar dx, fx = srcPt.fX;
+        SkScalar dy, fy = srcPt.fY;
+
+        if (fDstToIndexClass == kFixedStepInX_MatrixClass) {
+            SkFixed storage[2];
+            (void)matrix.fixedStepInX(SkIntToScalar(y) + SK_ScalarHalf,
+                                      &storage[0], &storage[1]);
+            dx = SkFixedToScalar(storage[0]);
+            dy = SkFixedToScalar(storage[1]);
+        } else {
+            SkASSERT(fDstToIndexClass == kLinear_MatrixClass);
+            dx = matrix.getScaleX();
+            dy = matrix.getSkewY();
+        }
+
+        for (; count > 0; --count) {
+            int index = SkATan2_255(fy, fx) >> (8 - kCache16Bits);
+            *dstC++ = cache[toggle + index];
+            toggle = next_dither_toggle16(toggle);
+            fx += dx;
+            fy += dy;
+        }
+    } else {  // perspective case
+        for (int stop = x + count; x < stop; x++) {
+            proc(matrix, SkIntToScalar(x) + SK_ScalarHalf,
+                         SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+            int index = SkATan2_255(srcPt.fY, srcPt.fX);
+            index >>= (8 - kCache16Bits);
+            *dstC++ = cache[toggle + index];
+            toggle = next_dither_toggle16(toggle);
+        }
+    }
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrTBackendEffectFactory.h"
+
+class GrGLSweepGradient : public GrGLGradientEffect {
+public:
+
+    GrGLSweepGradient(const GrBackendEffectFactory& factory,
+                      const GrEffectRef&) : INHERITED (factory) { }
+    virtual ~GrGLSweepGradient() { }
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        return GenMatrixKey(stage);
+    }
+
+private:
+
+    typedef GrGLGradientEffect INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrSweepGradient : public GrGradientEffect {
+public:
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkSweepGradient& shader,
+                               const SkMatrix& matrix) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSweepGradient, (ctx, shader, matrix)));
+        return CreateEffectRef(effect);
+    }
+    virtual ~GrSweepGradient() { }
+
+    static const char* Name() { return "Sweep Gradient"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<GrSweepGradient>::getInstance();
+    }
+
+    typedef GrGLSweepGradient GLEffect;
+
+private:
+    GrSweepGradient(GrContext* ctx,
+                    const SkSweepGradient& shader,
+                    const SkMatrix& matrix)
+    : INHERITED(ctx, shader, matrix, SkShader::kClamp_TileMode) { }
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrSweepGradient);
+
+GrEffectRef* GrSweepGradient::TestCreate(SkRandom* random,
+                                         GrContext* context,
+                                         GrTexture**) {
+    SkPoint center = {random->nextUScalar1(), random->nextUScalar1()};
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tmIgnored;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tmIgnored);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateSweep(center.fX, center.fY,
+                                                                colors, stops, colorCount));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+void GrGLSweepGradient::emitCode(GrGLShaderBuilder* builder,
+                                 const GrEffectStage& stage,
+                                 EffectKey key,
+                                 const char* vertexCoords,
+                                 const char* outputColor,
+                                 const char* inputColor,
+                                 const TextureSamplerArray& samplers) {
+    this->emitYCoordUniform(builder);
+    const char* coords;
+    this->setupMatrix(builder, key, vertexCoords, &coords);
+    SkString t;
+    t.printf("atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5", coords, coords);
+    this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrEffectRef* SkSweepGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return NULL;
+    }
+    matrix.postConcat(fPtsToUnit);
+    return GrSweepGradient::Create(context, *this, matrix);
+}
+
+#else
+
+GrEffectRef* SkSweepGradient::asNewEffect(GrContext*, const SkPaint&) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return NULL;
+}
+
+#endif
+
+#ifdef SK_DEVELOPER
+void SkSweepGradient::toString(SkString* str) const {
+    str->append("SkSweepGradient: (");
+
+    str->append("center: (");
+    str->appendScalar(fCenter.fX);
+    str->append(", ");
+    str->appendScalar(fCenter.fY);
+    str->append(") ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkSweepGradient.h b/src/effects/gradients/SkSweepGradient.h
new file mode 100644
index 0000000..d572429
--- /dev/null
+++ b/src/effects/gradients/SkSweepGradient.h
@@ -0,0 +1,41 @@
+
+/*
+ * 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 SkSweepGradient_DEFINED
+#define SkSweepGradient_DEFINED
+
+#include "SkGradientShaderPriv.h"
+
+class SkSweepGradient : public SkGradientShaderBase {
+public:
+    SkSweepGradient(SkScalar cx, SkScalar cy, const SkColor colors[],
+                   const SkScalar pos[], int count, SkUnitMapper* mapper);
+    virtual void shadeSpan(int x, int y, SkPMColor dstC[], int count) SK_OVERRIDE;
+    virtual void shadeSpan16(int x, int y, uint16_t dstC[], int count) SK_OVERRIDE;
+
+    virtual BitmapType asABitmap(SkBitmap* bitmap,
+                                 SkMatrix* matrix,
+                                 TileMode* xy) const SK_OVERRIDE;
+
+    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkSweepGradient)
+
+protected:
+    SkSweepGradient(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+
+private:
+    typedef SkGradientShaderBase INHERITED;
+    const SkPoint fCenter;
+};
+
+#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.cpp b/src/effects/gradients/SkTwoPointConicalGradient.cpp
new file mode 100644
index 0000000..e239530
--- /dev/null
+++ b/src/effects/gradients/SkTwoPointConicalGradient.cpp
@@ -0,0 +1,750 @@
+
+/*
+ * 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 "SkTwoPointConicalGradient.h"
+
+static int valid_divide(float numer, float denom, float* ratio) {
+    SkASSERT(ratio);
+    if (0 == denom) {
+        return 0;
+    }
+    *ratio = numer / denom;
+    return 1;
+}
+
+// Return the number of distinct real roots, and write them into roots[] in
+// ascending order
+static int find_quad_roots(float A, float B, float C, float roots[2]) {
+    SkASSERT(roots);
+
+    if (A == 0) {
+        return valid_divide(-C, B, roots);
+    }
+
+    float R = B*B - 4*A*C;
+    if (R < 0) {
+        return 0;
+    }
+    R = sk_float_sqrt(R);
+
+#if 1
+    float Q = B;
+    if (Q < 0) {
+        Q -= R;
+    } else {
+        Q += R;
+    }
+#else
+    // on 10.6 this was much slower than the above branch :(
+    float Q = B + copysignf(R, B);
+#endif
+    Q *= -0.5f;
+    if (0 == Q) {
+        roots[0] = 0;
+        return 1;
+    }
+
+    float r0 = Q / A;
+    float r1 = C / Q;
+    roots[0] = r0 < r1 ? r0 : r1;
+    roots[1] = r0 > r1 ? r0 : r1;
+    return 2;
+}
+
+static float lerp(float x, float dx, float t) {
+    return x + t * dx;
+}
+
+static float sqr(float x) { return x * x; }
+
+void TwoPtRadial::init(const SkPoint& center0, SkScalar rad0,
+                       const SkPoint& center1, SkScalar rad1) {
+    fCenterX = SkScalarToFloat(center0.fX);
+    fCenterY = SkScalarToFloat(center0.fY);
+    fDCenterX = SkScalarToFloat(center1.fX) - fCenterX;
+    fDCenterY = SkScalarToFloat(center1.fY) - fCenterY;
+    fRadius = SkScalarToFloat(rad0);
+    fDRadius = SkScalarToFloat(rad1) - fRadius;
+
+    fA = sqr(fDCenterX) + sqr(fDCenterY) - sqr(fDRadius);
+    fRadius2 = sqr(fRadius);
+    fRDR = fRadius * fDRadius;
+}
+
+void TwoPtRadial::setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy) {
+    fRelX = SkScalarToFloat(fx) - fCenterX;
+    fRelY = SkScalarToFloat(fy) - fCenterY;
+    fIncX = SkScalarToFloat(dfx);
+    fIncY = SkScalarToFloat(dfy);
+    fB = -2 * (fDCenterX * fRelX + fDCenterY * fRelY + fRDR);
+    fDB = -2 * (fDCenterX * fIncX + fDCenterY * fIncY);
+}
+
+SkFixed TwoPtRadial::nextT() {
+    float roots[2];
+
+    float C = sqr(fRelX) + sqr(fRelY) - fRadius2;
+    int countRoots = find_quad_roots(fA, fB, C, roots);
+
+    fRelX += fIncX;
+    fRelY += fIncY;
+    fB += fDB;
+
+    if (0 == countRoots) {
+        return kDontDrawT;
+    }
+
+    // Prefer the bigger t value if both give a radius(t) > 0
+    // find_quad_roots returns the values sorted, so we start with the last
+    float t = roots[countRoots - 1];
+    float r = lerp(fRadius, fDRadius, t);
+    if (r <= 0) {
+        t = roots[0];   // might be the same as roots[countRoots-1]
+        r = lerp(fRadius, fDRadius, t);
+        if (r <= 0) {
+            return kDontDrawT;
+        }
+    }
+    return SkFloatToFixed(t);
+}
+
+typedef void (*TwoPointRadialProc)(TwoPtRadial* rec, SkPMColor* dstC,
+                                   const SkPMColor* cache, int count);
+
+static void twopoint_clamp(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
+                           const SkPMColor* SK_RESTRICT cache, int count) {
+    for (; count > 0; --count) {
+        SkFixed t = rec->nextT();
+        if (TwoPtRadial::DontDrawT(t)) {
+            *dstC++ = 0;
+        } else {
+            SkFixed index = SkClampMax(t, 0xFFFF);
+            SkASSERT(index <= 0xFFFF);
+            *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
+        }
+    }
+}
+
+static void twopoint_repeat(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
+                            const SkPMColor* SK_RESTRICT cache, int count) {
+    for (; count > 0; --count) {
+        SkFixed t = rec->nextT();
+        if (TwoPtRadial::DontDrawT(t)) {
+            *dstC++ = 0;
+        } else {
+            SkFixed index = repeat_tileproc(t);
+            SkASSERT(index <= 0xFFFF);
+            *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
+        }
+    }
+}
+
+static void twopoint_mirror(TwoPtRadial* rec, SkPMColor* SK_RESTRICT dstC,
+                            const SkPMColor* SK_RESTRICT cache, int count) {
+    for (; count > 0; --count) {
+        SkFixed t = rec->nextT();
+        if (TwoPtRadial::DontDrawT(t)) {
+            *dstC++ = 0;
+        } else {
+            SkFixed index = mirror_tileproc(t);
+            SkASSERT(index <= 0xFFFF);
+            *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
+        }
+    }
+}
+
+void SkTwoPointConicalGradient::init() {
+    fRec.init(fCenter1, fRadius1, fCenter2, fRadius2);
+    fPtsToUnit.reset();
+}
+
+/////////////////////////////////////////////////////////////////////
+
+SkTwoPointConicalGradient::SkTwoPointConicalGradient(
+    const SkPoint& start, SkScalar startRadius,
+    const SkPoint& end, SkScalar endRadius,
+    const SkColor colors[], const SkScalar pos[],
+    int colorCount, SkShader::TileMode mode,
+    SkUnitMapper* mapper)
+    : SkGradientShaderBase(colors, pos, colorCount, mode, mapper),
+    fCenter1(start),
+    fCenter2(end),
+    fRadius1(startRadius),
+    fRadius2(endRadius) {
+    // this is degenerate, and should be caught by our caller
+    SkASSERT(fCenter1 != fCenter2 || fRadius1 != fRadius2);
+    this->init();
+}
+
+void SkTwoPointConicalGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
+                                          int count) {
+    SkASSERT(count > 0);
+
+    SkPMColor* SK_RESTRICT dstC = dstCParam;
+
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+
+    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+
+    TwoPointRadialProc shadeProc = twopoint_repeat;
+    if (SkShader::kClamp_TileMode == fTileMode) {
+        shadeProc = twopoint_clamp;
+    } else if (SkShader::kMirror_TileMode == fTileMode) {
+        shadeProc = twopoint_mirror;
+    } else {
+        SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+    }
+
+    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();
+        }
+
+        fRec.setup(fx, fy, dx, dy);
+        (*shadeProc)(&fRec, 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);
+            dstX += SK_Scalar1;
+
+            fRec.setup(srcPt.fX, srcPt.fY, 0, 0);
+            (*shadeProc)(&fRec, dstC, cache, 1);
+        }
+    }
+}
+
+bool SkTwoPointConicalGradient::setContext(const SkBitmap& device,
+                                           const SkPaint& paint,
+                                           const SkMatrix& matrix) {
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    // we don't have a span16 proc
+    fFlags &= ~kHasSpan16_Flag;
+
+    // in general, we might discard based on computed-radius, so clear
+    // this flag (todo: sometimes we can detect that we never discard...)
+    fFlags &= ~kOpaqueAlpha_Flag;
+
+    return true;
+}
+
+SkShader::BitmapType SkTwoPointConicalGradient::asABitmap(
+    SkBitmap* bitmap, SkMatrix* matrix, SkShader::TileMode* xy) const {
+    SkPoint diff = fCenter2 - fCenter1;
+    SkScalar diffLen = 0;
+
+    if (bitmap) {
+        this->getGradientTableBitmap(bitmap);
+    }
+    if (matrix) {
+        diffLen = diff.length();
+    }
+    if (matrix) {
+        if (diffLen) {
+            SkScalar invDiffLen = SkScalarInvert(diffLen);
+            // rotate to align circle centers with the x-axis
+            matrix->setSinCos(-SkScalarMul(invDiffLen, diff.fY),
+                              SkScalarMul(invDiffLen, diff.fX));
+        } else {
+            matrix->reset();
+        }
+        matrix->preTranslate(-fCenter1.fX, -fCenter1.fY);
+    }
+    if (xy) {
+        xy[0] = fTileMode;
+        xy[1] = kClamp_TileMode;
+    }
+    return kTwoPointConical_BitmapType;
+}
+
+SkShader::GradientType SkTwoPointConicalGradient::asAGradient(
+    GradientInfo* info) const {
+    if (info) {
+        commonAsAGradient(info);
+        info->fPoint[0] = fCenter1;
+        info->fPoint[1] = fCenter2;
+        info->fRadius[0] = fRadius1;
+        info->fRadius[1] = fRadius2;
+    }
+    return kConical_GradientType;
+}
+
+SkTwoPointConicalGradient::SkTwoPointConicalGradient(
+    SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer),
+    fCenter1(buffer.readPoint()),
+    fCenter2(buffer.readPoint()),
+    fRadius1(buffer.readScalar()),
+    fRadius2(buffer.readScalar()) {
+    this->init();
+};
+
+void SkTwoPointConicalGradient::flatten(
+    SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fCenter1);
+    buffer.writePoint(fCenter2);
+    buffer.writeScalar(fRadius1);
+    buffer.writeScalar(fRadius2);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrTBackendEffectFactory.h"
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+
+class GrGLConical2Gradient : public GrGLGradientEffect {
+public:
+
+    GrGLConical2Gradient(const GrBackendEffectFactory& factory,
+                         const GrEffectRef&);
+    virtual ~GrGLConical2Gradient() { }
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps);
+
+protected:
+
+    UniformHandle           fVSParamUni;
+    UniformHandle           fFSParamUni;
+
+    const char* fVSVaryingName;
+    const char* fFSVaryingName;
+
+    bool fIsDegenerate;
+
+    // @{
+    /// Values last uploaded as uniforms
+
+    SkScalar fCachedCenter;
+    SkScalar fCachedRadius;
+    SkScalar fCachedDiffRadius;
+
+    // @}
+
+private:
+
+    typedef GrGLGradientEffect INHERITED;
+
+};
+
+/////////////////////////////////////////////////////////////////////
+
+class GrConical2Gradient : public GrGradientEffect {
+public:
+
+    static GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointConicalGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrConical2Gradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrConical2Gradient() { }
+
+    static const char* Name() { return "Two-Point Conical Gradient"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<GrConical2Gradient>::getInstance();
+    }
+
+    // The radial gradient parameters can collapse to a linear (instead of quadratic) equation.
+    bool isDegenerate() const { return SkScalarAbs(fDiffRadius) == SkScalarAbs(fCenterX1); }
+    SkScalar center() const { return fCenterX1; }
+    SkScalar diffRadius() const { return fDiffRadius; }
+    SkScalar radius() const { return fRadius0; }
+
+    typedef GrGLConical2Gradient GLEffect;
+
+private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const GrConical2Gradient& s = CastEffect<GrConical2Gradient>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fDiffRadius == s.fDiffRadius);
+    }
+
+    GrConical2Gradient(GrContext* ctx,
+                       const SkTwoPointConicalGradient& shader,
+                       const SkMatrix& matrix,
+                       SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm)
+        , fCenterX1(shader.getCenterX1())
+        , fRadius0(shader.getStartRadius())
+        , fDiffRadius(shader.getDiffRadius()) { }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    // @{
+    // Cache of values - these can change arbitrarily, EXCEPT
+    // we shouldn't change between degenerate and non-degenerate?!
+
+    SkScalar fCenterX1;
+    SkScalar fRadius0;
+    SkScalar fDiffRadius;
+
+    // @}
+
+    typedef GrGradientEffect INHERITED;
+};
+
+GR_DEFINE_EFFECT_TEST(GrConical2Gradient);
+
+GrEffectRef* GrConical2Gradient::TestCreate(SkRandom* random,
+                                            GrContext* context,
+                                            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 ();
+        // If the circles are identical the factory will give us an empty shader.
+    } while (radius1 == radius2 && center1 == center2);
+
+    SkColor colors[kMaxRandomGradientColors];
+    SkScalar stopsArray[kMaxRandomGradientColors];
+    SkScalar* stops = stopsArray;
+    SkShader::TileMode tm;
+    int colorCount = RandomGradientParams(random, colors, &stops, &tm);
+    SkAutoTUnref<SkShader> shader(SkGradientShader::CreateTwoPointConical(center1, radius1,
+                                                                          center2, radius2,
+                                                                          colors, stops, colorCount,
+                                                                          tm));
+    SkPaint paint;
+    return shader->asNewEffect(context, paint);
+}
+
+
+/////////////////////////////////////////////////////////////////////
+
+GrGLConical2Gradient::GrGLConical2Gradient(const GrBackendEffectFactory& factory,
+                                           const GrEffectRef& baseData)
+    : INHERITED(factory)
+    , fVSParamUni(kInvalidUniformHandle)
+    , fFSParamUni(kInvalidUniformHandle)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedCenter(SK_ScalarMax)
+    , fCachedRadius(-SK_ScalarMax)
+    , fCachedDiffRadius(-SK_ScalarMax) {
+
+    const GrConical2Gradient& data = CastEffect<GrConical2Gradient>(baseData);
+    fIsDegenerate = data.isDegenerate();
+}
+
+void GrGLConical2Gradient::emitCode(GrGLShaderBuilder* builder,
+                                    const GrEffectStage& stage,
+                                    EffectKey key,
+                                    const char* vertexCoords,
+                                    const char* outputColor,
+                                    const char* inputColor,
+                                    const TextureSamplerArray& samplers) {
+    const char* fsCoords;
+    const char* vsCoordsVarying;
+    GrSLType coordsVaryingType;
+    this->setupMatrix(builder, key, vertexCoords, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
+
+    this->emitYCoordUniform(builder);
+    // 2 copies of uniform array, 1 for each of vertex & fragment shader,
+    // to work around Xoom bug. Doesn't seem to cause performance decrease
+    // in test apps, but need to keep an eye on it.
+    fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
+                                           kFloat_GrSLType, "Conical2VSParams", 6);
+    fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
+                                           kFloat_GrSLType, "Conical2FSParams", 6);
+
+    // For radial gradients without perspective we can pass the linear
+    // part of the quadratic as a varying.
+    if (kVec2f_GrSLType == coordsVaryingType) {
+        builder->addVarying(kFloat_GrSLType, "Conical2BCoeff",
+                            &fVSVaryingName, &fFSVaryingName);
+    }
+
+    // VS
+    {
+        SkString* code = &builder->fVSCode;
+        SkString p2; // distance between centers
+        SkString p3; // start radius
+        SkString p5; // difference in radii (r1 - r0)
+        builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
+        builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
+        builder->getUniformVariable(fVSParamUni).appendArrayAccess(5, &p5);
+
+        // For radial gradients without perspective we can pass the linear
+        // part of the quadratic as a varying.
+        if (kVec2f_GrSLType == coordsVaryingType) {
+            // r2Var = -2 * (r2Parm[2] * varCoord.x - r2Param[3] * r2Param[5])
+            code->appendf("\t%s = -2.0 * (%s * %s.x + %s * %s);\n",
+                          fVSVaryingName, p2.c_str(),
+                          vsCoordsVarying, p3.c_str(), p5.c_str());
+        }
+    }
+
+    // FS
+    {
+        SkString* code = &builder->fFSCode;
+
+        SkString cName("c");
+        SkString ac4Name("ac4");
+        SkString dName("d");
+        SkString qName("q");
+        SkString r0Name("r0");
+        SkString r1Name("r1");
+        SkString tName("t");
+        SkString p0; // 4a
+        SkString p1; // 1/a
+        SkString p2; // distance between centers
+        SkString p3; // start radius
+        SkString p4; // start radius squared
+        SkString p5; // difference in radii (r1 - r0)
+
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(0, &p0);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
+
+        // If we we're able to interpolate the linear component,
+        // bVar is the varying; otherwise compute it
+        SkString bVar;
+        if (kVec2f_GrSLType == coordsVaryingType) {
+            bVar = fFSVaryingName;
+        } else {
+            bVar = "b";
+            code->appendf("\tfloat %s = -2.0 * (%s * %s.x + %s * %s);\n",
+                          bVar.c_str(), p2.c_str(), fsCoords,
+                          p3.c_str(), p5.c_str());
+        }
+
+        // output will default to transparent black (we simply won't write anything
+        // else to it if invalid, instead of discarding or returning prematurely)
+        code->appendf("\t%s = vec4(0.0,0.0,0.0,0.0);\n", outputColor);
+
+        // c = (x^2)+(y^2) - params[4]
+        code->appendf("\tfloat %s = dot(%s, %s) - %s;\n", cName.c_str(),
+                      fsCoords, fsCoords,
+                      p4.c_str());
+
+        // Non-degenerate case (quadratic)
+        if (!fIsDegenerate) {
+
+            // ac4 = params[0] * c
+            code->appendf("\tfloat %s = %s * %s;\n", ac4Name.c_str(), p0.c_str(),
+                          cName.c_str());
+
+            // d = b^2 - ac4
+            code->appendf("\tfloat %s = %s * %s - %s;\n", dName.c_str(),
+                          bVar.c_str(), bVar.c_str(), ac4Name.c_str());
+
+            // only proceed if discriminant is >= 0
+            code->appendf("\tif (%s >= 0.0) {\n", dName.c_str());
+
+            // intermediate value we'll use to compute the roots
+            // q = -0.5 * (b +/- sqrt(d))
+            code->appendf("\t\tfloat %s = -0.5 * (%s + (%s < 0.0 ? -1.0 : 1.0)"
+                          " * sqrt(%s));\n", qName.c_str(), bVar.c_str(),
+                          bVar.c_str(), dName.c_str());
+
+            // compute both roots
+            // r0 = q * params[1]
+            code->appendf("\t\tfloat %s = %s * %s;\n", r0Name.c_str(),
+                          qName.c_str(), p1.c_str());
+            // r1 = c / q
+            code->appendf("\t\tfloat %s = %s / %s;\n", r1Name.c_str(),
+                          cName.c_str(), qName.c_str());
+
+            // Note: If there are two roots that both generate radius(t) > 0, the
+            // Canvas spec says to choose the larger t.
+
+            // so we'll look at the larger one first:
+            code->appendf("\t\tfloat %s = max(%s, %s);\n", tName.c_str(),
+                          r0Name.c_str(), r1Name.c_str());
+
+            // if r(t) > 0, then we're done; t will be our x coordinate
+            code->appendf("\t\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
+                          p5.c_str(), p3.c_str());
+
+            code->appendf("\t\t");
+            this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]);
+
+            // otherwise, if r(t) for the larger root was <= 0, try the other root
+            code->appendf("\t\t} else {\n");
+            code->appendf("\t\t\t%s = min(%s, %s);\n", tName.c_str(),
+                          r0Name.c_str(), r1Name.c_str());
+
+            // if r(t) > 0 for the smaller root, then t will be our x coordinate
+            code->appendf("\t\t\tif (%s * %s + %s > 0.0) {\n",
+                          tName.c_str(), p5.c_str(), p3.c_str());
+
+            code->appendf("\t\t\t");
+            this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]);
+
+            // end if (r(t) > 0) for smaller root
+            code->appendf("\t\t\t}\n");
+            // end if (r(t) > 0), else, for larger root
+            code->appendf("\t\t}\n");
+            // end if (discriminant >= 0)
+            code->appendf("\t}\n");
+        } else {
+
+            // linear case: t = -c/b
+            code->appendf("\tfloat %s = -(%s / %s);\n", tName.c_str(),
+                          cName.c_str(), bVar.c_str());
+
+            // if r(t) > 0, then t will be the x coordinate
+            code->appendf("\tif (%s * %s + %s > 0.0) {\n", tName.c_str(),
+                          p5.c_str(), p3.c_str());
+            code->appendf("\t");
+            this->emitColorLookup(builder, tName.c_str(), outputColor, inputColor, samplers[0]);
+            code->appendf("\t}\n");
+        }
+    }
+}
+
+void GrGLConical2Gradient::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    INHERITED::setData(uman, stage);
+    const GrConical2Gradient& data = GetEffectFromStage<GrConical2Gradient>(stage);
+    GrAssert(data.isDegenerate() == fIsDegenerate);
+    SkScalar centerX1 = data.center();
+    SkScalar radius0 = data.radius();
+    SkScalar diffRadius = data.diffRadius();
+
+    if (fCachedCenter != centerX1 ||
+        fCachedRadius != radius0 ||
+        fCachedDiffRadius != diffRadius) {
+
+        SkScalar a = SkScalarMul(centerX1, centerX1) - diffRadius * diffRadius;
+
+        // 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 * 4),
+            1.f / (SkScalarToFloat(a)),
+            SkScalarToFloat(centerX1),
+            SkScalarToFloat(radius0),
+            SkScalarToFloat(SkScalarMul(radius0, radius0)),
+            SkScalarToFloat(diffRadius)
+        };
+
+        uman.set1fv(fVSParamUni, 0, 6, values);
+        uman.set1fv(fFSParamUni, 0, 6, values);
+        fCachedCenter = centerX1;
+        fCachedRadius = radius0;
+        fCachedDiffRadius = diffRadius;
+    }
+}
+
+GrGLEffect::EffectKey GrGLConical2Gradient::GenKey(const GrEffectStage& s, const GrGLCaps&) {
+    enum {
+        kIsDegenerate = 1 << kMatrixKeyBitCnt,
+    };
+
+    EffectKey key = GenMatrixKey(s);
+    if (GetEffectFromStage<GrConical2Gradient>(s).isDegenerate()) {
+        key |= kIsDegenerate;
+    }
+    return key;
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
+    SkASSERT(fPtsToUnit.isIdentity());
+    // invert the localM, translate to center1, rotate so center2 is on x axis.
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return NULL;
+    }
+    matrix.postTranslate(-fCenter1.fX, -fCenter1.fY);
+
+    SkPoint diff = fCenter2 - fCenter1;
+    SkScalar diffLen = diff.length();
+    if (0 != diffLen) {
+        SkScalar invDiffLen = SkScalarInvert(diffLen);
+        SkMatrix rot;
+        rot.setSinCos(-SkScalarMul(invDiffLen, diff.fY),
+                       SkScalarMul(invDiffLen, diff.fX));
+        matrix.postConcat(rot);
+    }
+
+    return GrConical2Gradient::Create(context, *this, matrix, fTileMode);
+}
+
+#else
+
+GrEffectRef* SkTwoPointConicalGradient::asNewEffect(GrContext*, const SkPaint&) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return NULL;
+}
+
+#endif
+
+#ifdef SK_DEVELOPER
+void SkTwoPointConicalGradient::toString(SkString* str) const {
+    str->append("SkTwoPointConicalGradient: (");
+
+    str->append("center1: (");
+    str->appendScalar(fCenter1.fX);
+    str->append(", ");
+    str->appendScalar(fCenter1.fY);
+    str->append(") radius1: ");
+    str->appendScalar(fRadius1);
+    str->append(" ");
+
+    str->append("center2: (");
+    str->appendScalar(fCenter2.fX);
+    str->append(", ");
+    str->appendScalar(fCenter2.fY);
+    str->append(") radius2: ");
+    str->appendScalar(fRadius2);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
diff --git a/src/effects/gradients/SkTwoPointConicalGradient.h b/src/effects/gradients/SkTwoPointConicalGradient.h
new file mode 100644
index 0000000..e02c390
--- /dev/null
+++ b/src/effects/gradients/SkTwoPointConicalGradient.h
@@ -0,0 +1,85 @@
+
+/*
+ * 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 SkTwoPointConicalGradient_DEFINED
+ #define SkTwoPointConicalGradient_DEFINED
+
+#include "SkGradientShaderPriv.h"
+
+struct TwoPtRadial {
+    enum {
+        kDontDrawT  = 0x80000000
+    };
+
+    float   fCenterX, fCenterY;
+    float   fDCenterX, fDCenterY;
+    float   fRadius;
+    float   fDRadius;
+    float   fA;
+    float   fRadius2;
+    float   fRDR;
+
+    void init(const SkPoint& center0, SkScalar rad0,
+              const SkPoint& center1, SkScalar rad1);
+
+    // used by setup and nextT
+    float   fRelX, fRelY, fIncX, fIncY;
+    float   fB, fDB;
+
+    void setup(SkScalar fx, SkScalar fy, SkScalar dfx, SkScalar dfy);
+    SkFixed nextT();
+
+    static bool DontDrawT(SkFixed t) {
+        return kDontDrawT == (uint32_t)t;
+    }
+};
+
+
+class SkTwoPointConicalGradient : public SkGradientShaderBase {
+    TwoPtRadial fRec;
+    void init();
+
+public:
+    SkTwoPointConicalGradient(const SkPoint& start, SkScalar startRadius,
+                              const SkPoint& end, SkScalar endRadius,
+                              const SkColor colors[], const SkScalar pos[],
+                              int colorCount, SkShader::TileMode mode,
+                              SkUnitMapper* mapper);
+
+    virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
+                           int count) SK_OVERRIDE;
+    virtual bool setContext(const SkBitmap& device,
+                            const SkPaint& paint,
+                            const SkMatrix& matrix) SK_OVERRIDE;
+
+    virtual BitmapType asABitmap(SkBitmap* bitmap,
+                                 SkMatrix* matrix,
+                                 TileMode* xy) const;
+    virtual SkShader::GradientType asAGradient(GradientInfo* info) const  SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint& paint) const SK_OVERRIDE;
+
+    SkScalar getCenterX1() const { return SkPoint::Distance(fCenter1, fCenter2); }
+    SkScalar getStartRadius() const { return fRadius1; }
+    SkScalar getDiffRadius() const { return fRadius2 - fRadius1; }
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointConicalGradient)
+
+protected:
+    SkTwoPointConicalGradient(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+
+private:
+    typedef SkGradientShaderBase INHERITED;
+    const SkPoint fCenter1;
+    const SkPoint fCenter2;
+    const SkScalar fRadius1;
+    const SkScalar fRadius2;
+};
+
+#endif
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.cpp b/src/effects/gradients/SkTwoPointRadialGradient.cpp
new file mode 100644
index 0000000..4eaf487
--- /dev/null
+++ b/src/effects/gradients/SkTwoPointRadialGradient.cpp
@@ -0,0 +1,720 @@
+
+/*
+ * 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;
+    }
+}
+}
+
+/////////////////////////////////////////////////////////////////////
+
+SkTwoPointRadialGradient::SkTwoPointRadialGradient(
+    const SkPoint& start, SkScalar startRadius,
+    const SkPoint& end, SkScalar endRadius,
+    const SkColor colors[], const SkScalar pos[],
+    int colorCount, SkShader::TileMode mode,
+    SkUnitMapper* mapper)
+    : SkGradientShaderBase(colors, pos, colorCount, mode, mapper),
+      fCenter1(start),
+      fCenter2(end),
+      fRadius1(startRadius),
+      fRadius2(endRadius) {
+    init();
+}
+
+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;
+}
+
+void SkTwoPointRadialGradient::shadeSpan(int x, int y, SkPMColor* dstCParam,
+                                         int count) {
+    SkASSERT(count > 0);
+
+    SkPMColor* SK_RESTRICT dstC = dstCParam;
+
+    // Zero difference between radii:  fill with transparent black.
+    if (fDiffRadius == 0) {
+      sk_bzero(dstC, count * sizeof(*dstC));
+      return;
+    }
+    SkMatrix::MapXYProc dstProc = fDstToIndexProc;
+    TileProc            proc = fTileProc;
+    const SkPMColor* SK_RESTRICT cache = this->getCache32();
+
+    SkScalar foura = fA * 4;
+    bool posRoot = 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(fDiff.fX, fx) +
+                     SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
+        SkScalar db = (SkScalarMul(fDiff.fX, dx) +
+                      SkScalarMul(fDiff.fY, dy)) * 2;
+
+        TwoPointRadialShadeProc shadeProc = shadeSpan_twopoint_repeat;
+        if (SkShader::kClamp_TileMode == fTileMode) {
+            shadeProc = shadeSpan_twopoint_clamp;
+        } else if (SkShader::kMirror_TileMode == fTileMode) {
+            shadeProc = shadeSpan_twopoint_mirror;
+        } else {
+            SkASSERT(SkShader::kRepeat_TileMode == fTileMode);
+        }
+        (*shadeProc)(fx, dx, fy, dy, b, db,
+                     fSr2D2, foura, 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(fDiff.fX, fx) +
+                         SkScalarMul(fDiff.fY, fy) - fStartRadius) * 2;
+            SkFixed t = two_point_radial(b, fx, fy, fSr2D2, foura,
+                                         fOneOverTwoA, posRoot);
+            SkFixed index = proc(t);
+            SkASSERT(index <= 0xFFFF);
+            *dstC++ = cache[index >> SkGradientShaderBase::kCache32Shift];
+            dstX += SK_Scalar1;
+        }
+    }
+}
+
+bool SkTwoPointRadialGradient::setContext( const SkBitmap& device,
+                                          const SkPaint& paint,
+                                          const SkMatrix& matrix){
+    // For now, we might have divided by zero, so detect that
+    if (0 == fDiffRadius) {
+        return false;
+    }
+
+    if (!this->INHERITED::setContext(device, paint, matrix)) {
+        return false;
+    }
+
+    // we don't have a span16 proc
+    fFlags &= ~kHasSpan16_Flag;
+    return true;
+}
+
+#ifdef SK_DEVELOPER
+void SkTwoPointRadialGradient::toString(SkString* str) const {
+    str->append("SkTwoPointRadialGradient: (");
+
+    str->append("center1: (");
+    str->appendScalar(fCenter1.fX);
+    str->append(", ");
+    str->appendScalar(fCenter1.fY);
+    str->append(") radius1: ");
+    str->appendScalar(fRadius1);
+    str->append(" ");
+
+    str->append("center2: (");
+    str->appendScalar(fCenter2.fX);
+    str->append(", ");
+    str->appendScalar(fCenter2.fY);
+    str->append(") radius2: ");
+    str->appendScalar(fRadius2);
+    str->append(" ");
+
+    this->INHERITED::toString(str);
+
+    str->append(")");
+}
+#endif
+
+SkTwoPointRadialGradient::SkTwoPointRadialGradient(
+    SkFlattenableReadBuffer& buffer)
+    : INHERITED(buffer),
+      fCenter1(buffer.readPoint()),
+      fCenter2(buffer.readPoint()),
+      fRadius1(buffer.readScalar()),
+      fRadius2(buffer.readScalar()) {
+    init();
+};
+
+void SkTwoPointRadialGradient::flatten(
+    SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writePoint(fCenter1);
+    buffer.writePoint(fCenter2);
+    buffer.writeScalar(fRadius1);
+    buffer.writeScalar(fRadius2);
+}
+
+void SkTwoPointRadialGradient::init() {
+    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;
+
+    fPtsToUnit.setTranslate(-fCenter1.fX, -fCenter1.fY);
+    fPtsToUnit.postScale(inv, inv);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+
+#include "GrTBackendEffectFactory.h"
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+
+class GrGLRadial2Gradient : public GrGLGradientEffect {
+
+public:
+
+    GrGLRadial2Gradient(const GrBackendEffectFactory& factory, const GrEffectRef&);
+    virtual ~GrGLRadial2Gradient() { }
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+    static EffectKey GenKey(const GrEffectStage&, const GrGLCaps& caps);
+
+protected:
+
+    UniformHandle   fVSParamUni;
+    UniformHandle   fFSParamUni;
+
+    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 GrEffectRef* Create(GrContext* ctx,
+                               const SkTwoPointRadialGradient& shader,
+                               const SkMatrix& matrix,
+                               SkShader::TileMode tm) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrRadial2Gradient, (ctx, shader, matrix, tm)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrRadial2Gradient() { }
+
+    static const char* Name() { return "Two-Point Radial Gradient"; }
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE {
+        return GrTBackendEffectFactory<GrRadial2Gradient>::getInstance();
+    }
+
+    // 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); }
+
+    typedef GrGLRadial2Gradient GLEffect;
+
+private:
+    virtual bool onIsEqual(const GrEffect& sBase) const SK_OVERRIDE {
+        const GrRadial2Gradient& s = CastEffect<GrRadial2Gradient>(sBase);
+        return (INHERITED::onIsEqual(sBase) &&
+                this->fCenterX1 == s.fCenterX1 &&
+                this->fRadius0 == s.fRadius0 &&
+                this->fPosRoot == s.fPosRoot);
+    }
+
+    GrRadial2Gradient(GrContext* ctx,
+                      const SkTwoPointRadialGradient& shader,
+                      const SkMatrix& matrix,
+                      SkShader::TileMode tm)
+        : INHERITED(ctx, shader, matrix, tm)
+        , fCenterX1(shader.getCenterX1())
+        , fRadius0(shader.getStartRadius())
+        , fPosRoot(shader.getDiffRadius() < 0) { }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    // @{
+    // Cache of values - these can change arbitrarily, EXCEPT
+    // we shouldn't change between degenerate and non-degenerate?!
+
+    SkScalar fCenterX1;
+    SkScalar fRadius0;
+    SkBool8  fPosRoot;
+
+    // @}
+
+    typedef GrGradientEffect INHERITED;
+};
+
+/////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrRadial2Gradient);
+
+GrEffectRef* GrRadial2Gradient::TestCreate(SkRandom* random,
+                                           GrContext* context,
+                                           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;
+    return shader->asNewEffect(context, paint);
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrGLRadial2Gradient::GrGLRadial2Gradient(const GrBackendEffectFactory& factory,
+                                         const GrEffectRef& baseData)
+    : INHERITED(factory)
+    , fVSParamUni(kInvalidUniformHandle)
+    , fFSParamUni(kInvalidUniformHandle)
+    , fVSVaryingName(NULL)
+    , fFSVaryingName(NULL)
+    , fCachedCenter(SK_ScalarMax)
+    , fCachedRadius(-SK_ScalarMax)
+    , fCachedPosRoot(0) {
+
+    const GrRadial2Gradient& data = CastEffect<GrRadial2Gradient>(baseData);
+    fIsDegenerate = data.isDegenerate();
+}
+
+void GrGLRadial2Gradient::emitCode(GrGLShaderBuilder* builder,
+                                   const GrEffectStage& stage,
+                                   EffectKey key,
+                                   const char* vertexCoords,
+                                   const char* outputColor,
+                                   const char* inputColor,
+                                   const TextureSamplerArray& samplers) {
+
+    this->emitYCoordUniform(builder);
+    const char* fsCoords;
+    const char* vsCoordsVarying;
+    GrSLType coordsVaryingType;
+    this->setupMatrix(builder, key, vertexCoords, &fsCoords, &vsCoordsVarying, &coordsVaryingType);
+
+    // 2 copies of uniform array, 1 for each of vertex & fragment shader,
+    // to work around Xoom bug. Doesn't seem to cause performance decrease
+    // in test apps, but need to keep an eye on it.
+    fVSParamUni = builder->addUniformArray(GrGLShaderBuilder::kVertex_ShaderType,
+                                           kFloat_GrSLType, "Radial2VSParams", 6);
+    fFSParamUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
+                                           kFloat_GrSLType, "Radial2FSParams", 6);
+
+    // For radial gradients without perspective we can pass the linear
+    // part of the quadratic as a varying.
+    if (kVec2f_GrSLType == coordsVaryingType) {
+        builder->addVarying(kFloat_GrSLType, "Radial2BCoeff", &fVSVaryingName, &fFSVaryingName);
+    }
+
+    // VS
+    {
+        SkString* code = &builder->fVSCode;
+        SkString p2;
+        SkString p3;
+        builder->getUniformVariable(fVSParamUni).appendArrayAccess(2, &p2);
+        builder->getUniformVariable(fVSParamUni).appendArrayAccess(3, &p3);
+
+        // For radial gradients without perspective we can pass the linear
+        // part of the quadratic as a varying.
+        if (kVec2f_GrSLType == coordsVaryingType) {
+            // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
+            code->appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
+                          fVSVaryingName, p2.c_str(),
+                          vsCoordsVarying, p3.c_str());
+        }
+    }
+
+    // FS
+    {
+        SkString* code = &builder->fFSCode;
+        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(fFSParamUni).appendArrayAccess(0, &p0);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(1, &p1);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(2, &p2);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(3, &p3);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(4, &p4);
+        builder->getUniformVariable(fFSParamUni).appendArrayAccess(5, &p5);
+
+        // If we we're able to interpolate the linear component,
+        // bVar is the varying; otherwise compute it
+        SkString bVar;
+        if (kVec2f_GrSLType == coordsVaryingType) {
+            bVar = fFSVaryingName;
+        } else {
+            bVar = "b";
+            code->appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
+                          bVar.c_str(), p2.c_str(), fsCoords, p3.c_str());
+        }
+
+        // c = (x^2)+(y^2) - params[4]
+        code->appendf("\tfloat %s = dot(%s, %s) - %s;\n",
+                      cName.c_str(),
+                      fsCoords,
+                      fsCoords,
+                      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
+            code->appendf("\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)
+            code->appendf("\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->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]);
+    }
+}
+
+void GrGLRadial2Gradient::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    INHERITED::setData(uman, stage);
+    const GrRadial2Gradient& data = GetEffectFromStage<GrRadial2Gradient>(stage);
+    GrAssert(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
+        };
+
+        uman.set1fv(fVSParamUni, 0, 6, values);
+        uman.set1fv(fFSParamUni, 0, 6, values);
+        fCachedCenter = centerX1;
+        fCachedRadius = radius0;
+        fCachedPosRoot = data.isPosRoot();
+    }
+}
+
+GrGLEffect::EffectKey GrGLRadial2Gradient::GenKey(const GrEffectStage& s, const GrGLCaps&) {
+    enum {
+        kIsDegenerate = 1 << kMatrixKeyBitCnt,
+    };
+
+    EffectKey key = GenMatrixKey(s);
+    if (GetEffectFromStage<GrRadial2Gradient>(s).isDegenerate()) {
+        key |= kIsDegenerate;
+    }
+    return key;
+}
+
+/////////////////////////////////////////////////////////////////////
+
+GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext* context, const SkPaint&) const {
+    SkASSERT(NULL != context);
+    // invert the localM, translate to center1 (fPtsToUni), rotate so center2 is on x axis.
+    SkMatrix matrix;
+    if (!this->getLocalMatrix().invert(&matrix)) {
+        return NULL;
+    }
+    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);
+    }
+
+    return GrRadial2Gradient::Create(context, *this, matrix, fTileMode);
+}
+
+#else
+
+GrEffectRef* SkTwoPointRadialGradient::asNewEffect(GrContext*, const SkPaint&) const {
+    SkDEBUGFAIL("Should not call in GPU-less build");
+    return NULL;
+}
+
+#endif
diff --git a/src/effects/gradients/SkTwoPointRadialGradient.h b/src/effects/gradients/SkTwoPointRadialGradient.h
new file mode 100644
index 0000000..e82fc75
--- /dev/null
+++ b/src/effects/gradients/SkTwoPointRadialGradient.h
@@ -0,0 +1,57 @@
+
+/*
+ * 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 SkColor colors[], const SkScalar pos[],
+                              int colorCount, SkShader::TileMode mode,
+                              SkUnitMapper* mapper);
+
+    virtual BitmapType asABitmap(SkBitmap* bitmap,
+                                 SkMatrix* matrix,
+                                 TileMode* xy) const SK_OVERRIDE;
+    virtual GradientType asAGradient(GradientInfo* info) const SK_OVERRIDE;
+    virtual GrEffectRef* asNewEffect(GrContext* context, const SkPaint&) const SK_OVERRIDE;
+
+    virtual void shadeSpan(int x, int y, SkPMColor* dstCParam,
+                           int count) SK_OVERRIDE;
+    virtual bool setContext(const SkBitmap& device,
+                            const SkPaint& paint,
+                            const SkMatrix& matrix) SK_OVERRIDE;
+
+    SkScalar getCenterX1() const { return fDiff.length(); }
+    SkScalar getStartRadius() const { return fStartRadius; }
+    SkScalar getDiffRadius() const { return fDiffRadius; }
+
+    SK_DEVELOPER_TO_STRING()
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkTwoPointRadialGradient)
+
+protected:
+    SkTwoPointRadialGradient(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer& buffer) const SK_OVERRIDE;
+
+private:
+    typedef SkGradientShaderBase INHERITED;
+    const SkPoint fCenter1;
+    const SkPoint fCenter2;
+    const SkScalar fRadius1;
+    const SkScalar fRadius2;
+    SkPoint fDiff;
+    SkScalar fStartRadius, fDiffRadius, fSr2D2, fA, fOneOverTwoA;
+
+    void init();
+};
+
+#endif
diff --git a/src/gpu/FlingState.cpp b/src/gpu/FlingState.cpp
index 050a810..f0db501 100644
--- a/src/gpu/FlingState.cpp
+++ b/src/gpu/FlingState.cpp
@@ -110,18 +110,16 @@
     if (!fTime0) {
         return fValue1;
     }
-    
+
     double elapsed = (SkTime::GetMSecs() - fTime0) * 0.001;
     if (elapsed >= fDuration) {
         fTime0 = 0;
         return fValue1;
     }
-    
+
     double t = elapsed / fDuration;
     if (true) {
         t = (3 - 2 * t) * t * t;
     }
     return fValue0 + t * (fValue1 - fValue0);
 }
-
-
diff --git a/src/gpu/GrAAConvexPathRenderer.cpp b/src/gpu/GrAAConvexPathRenderer.cpp
index 60749d8..930d8a2 100644
--- a/src/gpu/GrAAConvexPathRenderer.cpp
+++ b/src/gpu/GrAAConvexPathRenderer.cpp
@@ -12,9 +12,9 @@
 #include "GrDrawState.h"
 #include "GrPathUtils.h"
 #include "SkString.h"
+#include "SkStrokeRec.h"
 #include "SkTrace.h"
 
-
 GrAAConvexPathRenderer::GrAAConvexPathRenderer() {
 }
 
@@ -22,9 +22,11 @@
 
 struct Segment {
     enum {
-        kLine,
-        kQuad
+        // These enum values are assumed in member functions below.
+        kLine = 0,
+        kQuad = 1,
     } fType;
+
     // line uses one pt, quad uses 2 pts
     GrPoint fPts[2];
     // normal to edge ending at each pt
@@ -34,24 +36,26 @@
     GrVec fMid;
 
     int countPoints() {
-        return (kLine == fType) ? 1 : 2;
+        GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
+        return fType + 1;
     }
     const SkPoint& endPt() const {
-        return (kLine == fType) ? fPts[0] : fPts[1];
+        GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
+        return fPts[fType];
     };
     const SkPoint& endNorm() const {
-        return (kLine == fType) ? fNorms[0] : fNorms[1];
+        GR_STATIC_ASSERT(0 == kLine && 1 == kQuad);
+        return fNorms[fType];
     };
 };
 
 typedef SkTArray<Segment, true> SegmentArray;
 
 void center_of_mass(const SegmentArray& segments, SkPoint* c) {
-    GrScalar area = 0;
-    SkPoint center;
-    center.set(0, 0);
+    SkScalar area = 0;
+    SkPoint center = {0, 0};
     int count = segments.count();
-    SkPoint p0;
+    SkPoint p0 = {0, 0};
     if (count > 2) {
         // We translate the polygon so that the first point is at the origin.
         // This avoids some precision issues with small area polygons far away
@@ -59,7 +63,7 @@
         p0 = segments[0].endPt();
         SkPoint pi;
         SkPoint pj;
-        // the first and last interation of the below loop would compute
+        // the first and last iteration of the below loop would compute
         // zeros since the starting / ending point is (0,0). So instead we start
         // at i=1 and make the last iteration i=count-2.
         pj = segments[1].endPt() - p0;
@@ -67,7 +71,7 @@
             pi = pj;
             const SkPoint pj = segments[i + 1].endPt() - p0;
 
-            GrScalar t = GrMul(pi.fX, pj.fY) - GrMul(pj.fX, pi.fY);
+            SkScalar t = SkScalarMul(pi.fX, pj.fY) - SkScalarMul(pj.fX, pi.fY);
             area += t;
             center.fX += (pi.fX + pj.fX) * t;
             center.fY += (pi.fY + pj.fY) * t;
@@ -89,9 +93,9 @@
         *c = avg;
     } else {
         area *= 3;
-        area = GrScalarDiv(GR_Scalar1, area);
-        center.fX = GrScalarMul(center.fX, area);
-        center.fY = GrScalarMul(center.fY, area);
+        area = SkScalarDiv(SK_Scalar1, area);
+        center.fX = SkScalarMul(center.fX, area);
+        center.fY = SkScalarMul(center.fY, area);
         // undo the translate of p0 to the origin.
         *c = center + p0;
     }
@@ -164,7 +168,7 @@
     }           fStage;
     GrPoint     fFirstPoint;
     GrVec       fLineNormal;
-    GrScalar    fLineC;
+    SkScalar    fLineC;
 };
 
 void update_degenerate_test(DegenerateTestData* data, const GrPoint& pt) {
@@ -196,29 +200,51 @@
     }
 }
 
-bool get_segments(const GrPath& path,
-                 SegmentArray* segments,
-                 SkPoint* fanPt,
-                 int* vCount,
-                 int* iCount) {
+inline bool get_direction(const SkPath& path, const SkMatrix& m, SkPath::Direction* dir) {
+    if (!path.cheapComputeDirection(dir)) {
+        return false;
+    }
+    // check whether m reverses the orientation
+    GrAssert(!m.hasPerspective());
+    SkScalar det2x2 = SkScalarMul(m.get(SkMatrix::kMScaleX), m.get(SkMatrix::kMScaleY)) -
+                      SkScalarMul(m.get(SkMatrix::kMSkewX), m.get(SkMatrix::kMSkewY));
+    if (det2x2 < 0) {
+        *dir = SkPath::OppositeDirection(*dir);
+    }
+    return true;
+}
+
+bool get_segments(const SkPath& path,
+                  const SkMatrix& m,
+                  SegmentArray* segments,
+                  SkPoint* fanPt,
+                  int* vCount,
+                  int* iCount) {
     SkPath::Iter iter(path, true);
-    // This renderer overemphasises very thin path regions. We use the distance
+    // This renderer over-emphasizes very thin path regions. We use the distance
     // to the path from the sample to compute coverage. Every pixel intersected
     // by the path will be hit and the maximum distance is sqrt(2)/2. We don't
-    // notice that the sample may be close to a very thin area of the path and 
+    // notice that the sample may be close to a very thin area of the path and
     // thus should be very light. This is particularly egregious for degenerate
     // line paths. We detect paths that are very close to a line (zero area) and
     // draw nothing.
     DegenerateTestData degenerateData;
+    SkPath::Direction dir;
+    // get_direction can fail for some degenerate paths.
+    if (!get_direction(path, m, &dir)) {
+        return false;
+    }
 
     for (;;) {
         GrPoint pts[4];
         GrPathCmd cmd = (GrPathCmd)iter.next(pts);
         switch (cmd) {
             case kMove_PathCmd:
+                m.mapPoints(pts, 1);
                 update_degenerate_test(&degenerateData, pts[0]);
                 break;
             case kLine_PathCmd: {
+                m.mapPoints(pts + 1, 1);
                 update_degenerate_test(&degenerateData, pts[1]);
                 segments->push_back();
                 segments->back().fType = Segment::kLine;
@@ -226,6 +252,7 @@
                 break;
             }
             case kQuadratic_PathCmd:
+                m.mapPoints(pts + 1, 2);
                 update_degenerate_test(&degenerateData, pts[1]);
                 update_degenerate_test(&degenerateData, pts[2]);
                 segments->push_back();
@@ -234,11 +261,14 @@
                 segments->back().fPts[1] = pts[2];
                 break;
             case kCubic_PathCmd: {
+                m.mapPoints(pts, 4);
                 update_degenerate_test(&degenerateData, pts[1]);
                 update_degenerate_test(&degenerateData, pts[2]);
                 update_degenerate_test(&degenerateData, pts[3]);
+                // unlike quads and lines, the pts[0] will also be read (in
+                // convertCubicToQuads).
                 SkSTArray<15, SkPoint, true> quads;
-                GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, &quads);
+                GrPathUtils::convertCubicToQuads(pts, SK_Scalar1, true, dir, &quads);
                 int count = quads.count();
                 for (int q = 0; q < count; q += 3) {
                     segments->push_back();
@@ -252,10 +282,6 @@
                 if (degenerateData.isDegenerate()) {
                     return false;
                 } else {
-                    SkPath::Direction dir;
-                    GR_DEBUGCODE(bool succeeded = )
-                    path.cheapComputeDirection(&dir);
-                    GrAssert(succeeded);
                     compute_vectors(segments, fanPt, dir, vCount, iCount);
                     return true;
                 }
@@ -268,10 +294,10 @@
 struct QuadVertex {
     GrPoint  fPos;
     GrPoint  fUV;
-    GrScalar fD0;
-    GrScalar fD1;
+    SkScalar fD0;
+    SkScalar fD1;
 };
-    
+
 void create_vertices(const SegmentArray&  segments,
                      const SkPoint& fanPt,
                      QuadVertex*    verts,
@@ -284,7 +310,7 @@
         const Segment& sega = segments[a];
         int b = (a + 1) % count;
         const Segment& segb = segments[b];
-        
+
         // FIXME: These tris are inset in the 1 unit arc around the corner
         verts[v + 0].fPos = sega.endPt();
         verts[v + 1].fPos = verts[v + 0].fPos + sega.endNorm();
@@ -298,14 +324,14 @@
         verts[v + 1].fD0 = verts[v + 1].fD1 = -SK_Scalar1;
         verts[v + 2].fD0 = verts[v + 2].fD1 = -SK_Scalar1;
         verts[v + 3].fD0 = verts[v + 3].fD1 = -SK_Scalar1;
-        
+
         idxs[i + 0] = v + 0;
         idxs[i + 1] = v + 2;
         idxs[i + 2] = v + 1;
         idxs[i + 3] = v + 0;
         idxs[i + 4] = v + 3;
         idxs[i + 5] = v + 2;
-        
+
         v += 4;
         i += 6;
 
@@ -319,7 +345,7 @@
 
             // we draw the line edge as a degenerate quad (u is 0, v is the
             // signed distance to the edge)
-            GrScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
+            SkScalar dist = fanPt.distanceToLineBetween(verts[v + 1].fPos,
                                                         verts[v + 2].fPos);
             verts[v + 0].fUV.set(0, dist);
             verts[v + 1].fUV.set(0, 0);
@@ -360,28 +386,24 @@
             verts[v + 4].fPos = qpts[2] + segb.fNorms[1];
             verts[v + 5].fPos = qpts[1] + midVec;
 
-            GrScalar c = segb.fNorms[0].dot(qpts[0]);
+            SkScalar c = segb.fNorms[0].dot(qpts[0]);
             verts[v + 0].fD0 =  -segb.fNorms[0].dot(fanPt) + c;
             verts[v + 1].fD0 =  0.f;
             verts[v + 2].fD0 =  -segb.fNorms[0].dot(qpts[2]) + c;
-            verts[v + 3].fD0 = -GR_ScalarMax/100;
-            verts[v + 4].fD0 = -GR_ScalarMax/100;
-            verts[v + 5].fD0 = -GR_ScalarMax/100;
+            verts[v + 3].fD0 = -SK_ScalarMax/100;
+            verts[v + 4].fD0 = -SK_ScalarMax/100;
+            verts[v + 5].fD0 = -SK_ScalarMax/100;
 
             c = segb.fNorms[1].dot(qpts[2]);
             verts[v + 0].fD1 =  -segb.fNorms[1].dot(fanPt) + c;
             verts[v + 1].fD1 =  -segb.fNorms[1].dot(qpts[0]) + c;
             verts[v + 2].fD1 =  0.f;
-            verts[v + 3].fD1 = -GR_ScalarMax/100;
-            verts[v + 4].fD1 = -GR_ScalarMax/100;
-            verts[v + 5].fD1 = -GR_ScalarMax/100;
+            verts[v + 3].fD1 = -SK_ScalarMax/100;
+            verts[v + 4].fD1 = -SK_ScalarMax/100;
+            verts[v + 5].fD1 = -SK_ScalarMax/100;
 
-            GrMatrix toUV;
-            GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &toUV);
-            toUV.mapPointsWithStride(&verts[v].fUV,
-                                     &verts[v].fPos,
-                                     sizeof(QuadVertex),
-                                     6);
+            GrPathUtils::QuadUVMatrix toUV(qpts);
+            toUV.apply<6, sizeof(QuadVertex), sizeof(GrPoint)>(verts + v);
 
             idxs[i + 0] = v + 3;
             idxs[i + 1] = v + 1;
@@ -407,83 +429,75 @@
 }
 
 bool GrAAConvexPathRenderer::canDrawPath(const SkPath& path,
-                                         GrPathFill fill,
+                                         const SkStrokeRec& stroke,
                                          const GrDrawTarget* target,
                                          bool antiAlias) const {
-    if (!target->getCaps().fShaderDerivativeSupport || !antiAlias ||
-        kHairLine_PathFill == fill || GrIsFillInverted(fill) ||
-        !path.isConvex()) {
-        return false;
-    }  else {
-        return true;
-    }
+    return (target->getCaps().shaderDerivativeSupport() && antiAlias &&
+            stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex());
 }
 
 bool GrAAConvexPathRenderer::onDrawPath(const SkPath& origPath,
-                                        GrPathFill fill,
-                                        const GrVec* translate,
+                                        const SkStrokeRec&,
                                         GrDrawTarget* target,
-                                        GrDrawState::StageMask stageMask,
                                         bool antiAlias) {
 
-
-    if (origPath.isEmpty()) {
+    const SkPath* path = &origPath;
+    if (path->isEmpty()) {
         return true;
     }
     GrDrawState* drawState = target->drawState();
 
-    GrDrawTarget::AutoStateRestore asr;
-    GrMatrix vm = drawState->getViewMatrix();
-    if (NULL != translate) {
-        vm.postTranslate(translate->fX, translate->fY);
+    GrDrawState::AutoDeviceCoordDraw adcd(drawState);
+    if (!adcd.succeeded()) {
+        return false;
     }
-    asr.set(target);
-    GrMatrix ivm;
-    if (vm.invert(&ivm)) {
-        drawState->preConcatSamplerMatrices(stageMask, ivm);
-    }
-    drawState->setViewMatrix(GrMatrix::I());
-
-    SkPath path;
-    origPath.transform(vm, &path);
+    const SkMatrix* vm = &adcd.getOriginalMatrix();
 
     GrVertexLayout layout = 0;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if ((1 << s) & stageMask) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
-        }
+    layout |= GrDrawState::kEdge_VertexLayoutBit;
+
+    // We use the fact that SkPath::transform path does subdivision based on
+    // perspective. Otherwise, we apply the view matrix when copying to the
+    // segment representation.
+    SkPath tmpPath;
+    if (vm->hasPerspective()) {
+        origPath.transform(*vm, &tmpPath);
+        path = &tmpPath;
+        vm = &SkMatrix::I();
     }
-    layout |= GrDrawTarget::kEdge_VertexLayoutBit;
 
     QuadVertex *verts;
     uint16_t* idxs;
 
     int vCount;
     int iCount;
-    SegmentArray segments;
+    enum {
+        kPreallocSegmentCnt = 512 / sizeof(Segment),
+    };
+    SkSTArray<kPreallocSegmentCnt, Segment, true> segments;
     SkPoint fanPt;
-    if (!get_segments(path, &segments, &fanPt, &vCount, &iCount)) {
+
+    if (!get_segments(*path, *vm, &segments, &fanPt, &vCount, &iCount)) {
         return false;
     }
 
-    if (!target->reserveVertexSpace(layout,
-                                    vCount,
-                                    reinterpret_cast<void**>(&verts))) {
+    GrDrawTarget::AutoReleaseGeometry arg(target, layout, vCount, iCount);
+    if (!arg.succeeded()) {
         return false;
     }
-    if (!target->reserveIndexSpace(iCount, reinterpret_cast<void**>(&idxs))) {
-        target->resetVertexSource();
-        return false;
-    }
+    verts = reinterpret_cast<QuadVertex*>(arg.vertices());
+    idxs = reinterpret_cast<uint16_t*>(arg.indices());
 
     create_vertices(segments, fanPt, verts, idxs);
 
+    GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType();
     drawState->setVertexEdgeType(GrDrawState::kQuad_EdgeType);
-    target->drawIndexed(kTriangles_PrimitiveType,
+    target->drawIndexed(kTriangles_GrPrimitiveType,
                         0,        // start vertex
                         0,        // start index
                         vCount,
                         iCount);
+    drawState->setVertexEdgeType(oldEdgeType);
+
     return true;
 }
-
diff --git a/src/gpu/GrAAConvexPathRenderer.h b/src/gpu/GrAAConvexPathRenderer.h
index df0c001..62394a3 100644
--- a/src/gpu/GrAAConvexPathRenderer.h
+++ b/src/gpu/GrAAConvexPathRenderer.h
@@ -14,14 +14,13 @@
     GrAAConvexPathRenderer();
 
     virtual bool canDrawPath(const SkPath& path,
-                             GrPathFill fill,
+                             const SkStrokeRec& stroke,
                              const GrDrawTarget* target,
                              bool antiAlias) const SK_OVERRIDE;
+
 protected:
     virtual bool onDrawPath(const SkPath& path,
-                            GrPathFill fill,
-                            const GrVec* translate,
+                            const SkStrokeRec& stroke,
                             GrDrawTarget* target,
-                            GrDrawState::StageMask stageMask,
                             bool antiAlias) SK_OVERRIDE;
 };
diff --git a/src/gpu/GrAAHairLinePathRenderer.cpp b/src/gpu/GrAAHairLinePathRenderer.cpp
index 3ed4488..c229f66 100644
--- a/src/gpu/GrAAHairLinePathRenderer.cpp
+++ b/src/gpu/GrAAHairLinePathRenderer.cpp
@@ -14,6 +14,7 @@
 #include "GrIndexBuffer.h"
 #include "GrPathUtils.h"
 #include "SkGeometry.h"
+#include "SkStroke.h"
 #include "SkTemplates.h"
 
 namespace {
@@ -35,7 +36,7 @@
     uint16_t* data = (uint16_t*) qIdxBuffer->lock();
     bool tempData = NULL == data;
     if (tempData) {
-        data = new uint16_t[kNumQuadsInIdxBuffer * kIdxsPerQuad];
+        data = SkNEW_ARRAY(uint16_t, kNumQuadsInIdxBuffer * kIdxsPerQuad);
     }
     for (int i = 0; i < kNumQuadsInIdxBuffer; ++i) {
 
@@ -50,7 +51,7 @@
         //     a0              c0
         //      a            c
         //       a1       c1
-        // Each is drawn as three triagnles specified by these 9 indices:
+        // Each is drawn as three triangles specified by these 9 indices:
         int baseIdx = i * kIdxsPerQuad;
         uint16_t baseVert = (uint16_t)(i * kVertsPerQuad);
         data[0 + baseIdx] = baseVert + 0; // a0
@@ -86,9 +87,8 @@
         !push_quad_index_data(qIdxBuf)) {
         return NULL;
     }
-    return new GrAAHairLinePathRenderer(context,
-                                        lIdxBuffer,
-                                        qIdxBuf);
+    return SkNEW_ARGS(GrAAHairLinePathRenderer,
+                      (context, lIdxBuffer, qIdxBuf));
 }
 
 GrAAHairLinePathRenderer::GrAAHairLinePathRenderer(
@@ -140,7 +140,7 @@
 // if it returns -1 then should be drawn as lines
 int num_quad_subdivs(const SkPoint p[3]) {
     static const SkScalar gDegenerateToLineTol = SK_Scalar1;
-    static const SkScalar gDegenerateToLineTolSqd = 
+    static const SkScalar gDegenerateToLineTolSqd =
         SkScalarMul(gDegenerateToLineTol, gDegenerateToLineTol);
 
     if (p[0].distanceToSqd(p[1]) < gDegenerateToLineTolSqd ||
@@ -148,7 +148,7 @@
         return -1;
     }
 
-    GrScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
+    SkScalar dsqd = p[1].distanceToLineBetweenSqd(p[0], p[2]);
     if (dsqd < gDegenerateToLineTolSqd) {
         return -1;
     }
@@ -164,7 +164,7 @@
     // maybe different when do this using gpu (geo or tess shaders)
     static const SkScalar gSubdivTol = 175 * SK_Scalar1;
 
-    if (dsqd <= gSubdivTol*gSubdivTol) {
+    if (dsqd <= SkScalarMul(gSubdivTol, gSubdivTol)) {
         return 0;
     } else {
         // subdividing the quad reduces d by 4. so we want x = log4(d/tol)
@@ -177,7 +177,9 @@
         log = GrMin(GrMax(0, log), kMaxSub);
         return log;
 #else
-        SkScalar log = SkScalarLog(SkScalarDiv(dsqd,gSubdivTol*gSubdivTol));
+        SkScalar log = SkScalarLog(
+                          SkScalarDiv(dsqd,
+                                      SkScalarMul(gSubdivTol, gSubdivTol)));
         static const SkScalar conv = SkScalarInvert(SkScalarLog(2));
         log = SkScalarMul(log, conv);
         return  GrMin(GrMax(0, SkScalarCeilToInt(log)),kMaxSub);
@@ -196,8 +198,7 @@
  */
 int generate_lines_and_quads(const SkPath& path,
                              const SkMatrix& m,
-                             const SkVector& translate,
-                             GrIRect clip,
+                             const GrIRect& devClipBounds,
                              PtArray* lines,
                              PtArray* quads,
                              IntArray* quadSubdivCnts) {
@@ -217,24 +218,22 @@
             case kMove_PathCmd:
                 break;
             case kLine_PathCmd:
-                SkPoint::Offset(pts, 2, translate);
                 m.mapPoints(devPts, pts, 2);
                 bounds.setBounds(devPts, 2);
                 bounds.outset(SK_Scalar1, SK_Scalar1);
                 bounds.roundOut(&ibounds);
-                if (SkIRect::Intersects(clip, ibounds)) {
+                if (SkIRect::Intersects(devClipBounds, ibounds)) {
                     SkPoint* pts = lines->push_back_n(2);
                     pts[0] = devPts[0];
                     pts[1] = devPts[1];
                 }
                 break;
             case kQuadratic_PathCmd:
-                SkPoint::Offset(pts, 3, translate);
                 m.mapPoints(devPts, pts, 3);
                 bounds.setBounds(devPts, 3);
                 bounds.outset(SK_Scalar1, SK_Scalar1);
                 bounds.roundOut(&ibounds);
-                if (SkIRect::Intersects(clip, ibounds)) {
+                if (SkIRect::Intersects(devClipBounds, ibounds)) {
                     int subdiv = num_quad_subdivs(devPts);
                     GrAssert(subdiv >= -1);
                     if (-1 == subdiv) {
@@ -254,24 +253,25 @@
                         totalQuadCount += 1 << subdiv;
                     }
                 }
-            break;
+                break;
             case kCubic_PathCmd:
-                SkPoint::Offset(pts, 4, translate);
                 m.mapPoints(devPts, pts, 4);
                 bounds.setBounds(devPts, 4);
                 bounds.outset(SK_Scalar1, SK_Scalar1);
                 bounds.roundOut(&ibounds);
-                if (SkIRect::Intersects(clip, ibounds)) {
+                if (SkIRect::Intersects(devClipBounds, ibounds)) {
                     PREALLOC_PTARRAY(32) q;
+                    // we don't need a direction if we aren't constraining the subdivision
+                    static const SkPath::Direction kDummyDir = SkPath::kCCW_Direction;
                     // We convert cubics to quadratics (for now).
                     // In perspective have to do conversion in src space.
                     if (persp) {
-                        SkScalar tolScale = 
+                        SkScalar tolScale =
                             GrPathUtils::scaleToleranceToSrc(SK_Scalar1, m,
                                                              path.getBounds());
-                        GrPathUtils::convertCubicToQuads(pts, tolScale, &q);
+                        GrPathUtils::convertCubicToQuads(pts, tolScale, false, kDummyDir, &q);
                     } else {
-                        GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, &q);
+                        GrPathUtils::convertCubicToQuads(devPts, SK_Scalar1, false, kDummyDir, &q);
                     }
                     for (int i = 0; i < q.count(); i += 3) {
                         SkPoint* qInDevSpace;
@@ -287,7 +287,7 @@
                         }
                         bounds.outset(SK_Scalar1, SK_Scalar1);
                         bounds.roundOut(&ibounds);
-                        if (SkIRect::Intersects(clip, ibounds)) {
+                        if (SkIRect::Intersects(devClipBounds, ibounds)) {
                             int subdiv = num_quad_subdivs(qInDevSpace);
                             GrAssert(subdiv >= -1);
                             if (-1 == subdiv) {
@@ -310,7 +310,7 @@
                         }
                     }
                 }
-            break;
+                break;
             case kClose_PathCmd:
                 break;
             case kEnd_PathCmd:
@@ -323,13 +323,13 @@
     GrPoint fPos;
     union {
         struct {
-            GrScalar fA;
-            GrScalar fB;
-            GrScalar fC;
+            SkScalar fA;
+            SkScalar fB;
+            SkScalar fC;
         } fLine;
         GrVec   fQuadCoord;
         struct {
-            GrScalar fBogus[4];
+            SkScalar fBogus[4];
         };
     };
 };
@@ -348,13 +348,13 @@
 
     result->fX = SkScalarMul(normA.fY, lineBW) - SkScalarMul(lineAW, normB.fY);
     result->fX = SkScalarMul(result->fX, wInv);
-    
+
     result->fY = SkScalarMul(lineAW, normB.fX) - SkScalarMul(normA.fX, lineBW);
     result->fY = SkScalarMul(result->fY, wInv);
 }
 
-void bloat_quad(const SkPoint qpts[3], const GrMatrix* toDevice,
-                const GrMatrix* toSrc, Vertex verts[kVertsPerQuad]) {
+void bloat_quad(const SkPoint qpts[3], const SkMatrix* toDevice,
+                const SkMatrix* toSrc, Vertex verts[kVertsPerQuad]) {
     GrAssert(!toDevice == !toSrc);
     // original quad is specified by tri a,b,c
     SkPoint a = qpts[0];
@@ -362,8 +362,7 @@
     SkPoint c = qpts[2];
 
     // this should be in the src space, not dev coords, when we have perspective
-    SkMatrix DevToUV;
-    GrPathUtils::quadDesignSpaceToUVCoordsMatrix(qpts, &DevToUV);
+    GrPathUtils::QuadUVMatrix DevToUV(qpts);
 
     if (toDevice) {
         toDevice->mapPoints(&a, 1);
@@ -427,14 +426,13 @@
     if (toSrc) {
         toSrc->mapPointsWithStride(&verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
     }
-    DevToUV.mapPointsWithStride(&verts[0].fQuadCoord,
-                                &verts[0].fPos, sizeof(Vertex), kVertsPerQuad);
+    DevToUV.apply<kVertsPerQuad, sizeof(Vertex), sizeof(GrPoint)>(verts);
 }
 
 void add_quads(const SkPoint p[3],
                int subdiv,
-               const GrMatrix* toDevice,
-               const GrMatrix* toSrc,
+               const SkMatrix* toDevice,
+               const SkMatrix* toSrc,
                Vertex** vert) {
     GrAssert(subdiv >= 0);
     if (subdiv) {
@@ -461,16 +459,7 @@
     if (orthVec.setLength(SK_Scalar1)) {
         orthVec.setOrthog(orthVec);
 
-        // the values we pass down to the frag shader
-        // have to be in y-points-up space;
-        SkVector normal;
-        normal.fX = orthVec.fX;
-        normal.fY = -orthVec.fY;
-        SkPoint aYDown;
-        aYDown.fX = a.fX;
-        aYDown.fY = rtHeight - a.fY;
-
-        SkScalar lineC = -(aYDown.dot(normal));
+        SkScalar lineC = -(a.dot(orthVec));
         for (int i = 0; i < kVertsPerLineSeg; ++i) {
             (*vert)[i].fPos = (i < 2) ? a : b;
             if (0 == i || 3 == i) {
@@ -478,8 +467,8 @@
             } else {
                 (*vert)[i].fPos += orthVec;
             }
-            (*vert)[i].fLine.fA = normal.fX;
-            (*vert)[i].fLine.fB = normal.fY;
+            (*vert)[i].fLine.fA = orthVec.fX;
+            (*vert)[i].fLine.fB = orthVec.fY;
             (*vert)[i].fLine.fC = lineC;
         }
         if (NULL != toSrc) {
@@ -500,56 +489,42 @@
 
 }
 
-bool GrAAHairLinePathRenderer::createGeom(const SkPath& path,
-                                          const GrVec* translate,
-                                          GrDrawTarget* target,
-                                          GrDrawState::StageMask stageMask,
-                                          int* lineCnt,
-                                          int* quadCnt) {
+bool GrAAHairLinePathRenderer::createGeom(
+            const SkPath& path,
+            GrDrawTarget* target,
+            int* lineCnt,
+            int* quadCnt,
+            GrDrawTarget::AutoReleaseGeometry* arg) {
     const GrDrawState& drawState = target->getDrawState();
     int rtHeight = drawState.getRenderTarget()->height();
 
-    GrIRect clip;
-    if (target->getClip().hasConservativeBounds()) {
-        GrRect clipRect =  target->getClip().getConservativeBounds();
-        clipRect.roundOut(&clip);
-    } else {
-        clip.setLargest();
-    }
+    GrIRect devClipBounds;
+    target->getClip()->getConservativeBounds(drawState.getRenderTarget(),
+                                             &devClipBounds);
 
-
-    GrVertexLayout layout = GrDrawTarget::kEdge_VertexLayoutBit;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if ((1 << s) & stageMask) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
-        }
-    }
-
-    GrMatrix viewM = drawState.getViewMatrix();
+    GrVertexLayout layout = GrDrawState::kEdge_VertexLayoutBit;
+    SkMatrix viewM = drawState.getViewMatrix();
 
     PREALLOC_PTARRAY(128) lines;
     PREALLOC_PTARRAY(128) quads;
     IntArray qSubdivs;
-    static const GrVec gZeroVec = {0, 0};
-    if (NULL == translate) {
-        translate = &gZeroVec;
-    }
-    *quadCnt = generate_lines_and_quads(path, viewM, *translate, clip,
+    *quadCnt = generate_lines_and_quads(path, viewM, devClipBounds,
                                         &lines, &quads, &qSubdivs);
 
     *lineCnt = lines.count() / 2;
     int vertCnt = kVertsPerLineSeg * *lineCnt + kVertsPerQuad * *quadCnt;
 
-    GrAssert(sizeof(Vertex) == GrDrawTarget::VertexSize(layout));
+    GrAssert(sizeof(Vertex) == GrDrawState::VertexSize(layout));
 
-    Vertex* verts;
-    if (!target->reserveVertexSpace(layout, vertCnt, (void**)&verts)) {
+    if (!arg->set(target, layout, vertCnt, 0)) {
         return false;
     }
 
-    const GrMatrix* toDevice = NULL;
-    const GrMatrix* toSrc = NULL;
-    GrMatrix ivm;
+    Vertex* verts = reinterpret_cast<Vertex*>(arg->vertices());
+
+    const SkMatrix* toDevice = NULL;
+    const SkMatrix* toSrc = NULL;
+    SkMatrix ivm;
 
     if (viewM.hasPerspective()) {
         if (viewM.invert(&ivm)) {
@@ -572,16 +547,16 @@
 }
 
 bool GrAAHairLinePathRenderer::canDrawPath(const SkPath& path,
-                                           GrPathFill fill,
+                                           const SkStrokeRec& stroke,
                                            const GrDrawTarget* target,
                                            bool antiAlias) const {
-    if (fill != kHairLine_PathFill || !antiAlias) {
+    if (!stroke.isHairlineStyle() || !antiAlias) {
         return false;
     }
 
     static const uint32_t gReqDerivMask = SkPath::kCubic_SegmentMask |
                                           SkPath::kQuad_SegmentMask;
-    if (!target->getCaps().fShaderDerivativeSupport &&
+    if (!target->getCaps().shaderDerivativeSupport() &&
         (gReqDerivMask & path.getSegmentMasks())) {
         return false;
     }
@@ -589,45 +564,44 @@
 }
 
 bool GrAAHairLinePathRenderer::onDrawPath(const SkPath& path,
-                                          GrPathFill fill,
-                                          const GrVec* translate,
+                                          const SkStrokeRec&,
                                           GrDrawTarget* target,
-                                          GrDrawState::StageMask stageMask,
                                           bool antiAlias) {
 
     int lineCnt;
     int quadCnt;
-
+    GrDrawTarget::AutoReleaseGeometry arg;
     if (!this->createGeom(path,
-                          translate,
                           target,
-                          stageMask,
                           &lineCnt,
-                          &quadCnt)) {
+                          &quadCnt,
+                          &arg)) {
         return false;
     }
 
+    GrDrawState::AutoDeviceCoordDraw adcd;
     GrDrawState* drawState = target->drawState();
-
-    GrDrawTarget::AutoStateRestore asr;
+    // createGeom transforms the geometry to device space when the matrix does not have
+    // perspective.
     if (!drawState->getViewMatrix().hasPerspective()) {
-        asr.set(target);
-        GrMatrix ivm;
-        if (drawState->getViewInverse(&ivm)) {
-            drawState->preConcatSamplerMatrices(stageMask, ivm);
+        adcd.set(drawState);
+        if (!adcd.succeeded()) {
+            return false;
         }
-        drawState->setViewMatrix(GrMatrix::I());
     }
 
     // TODO: See whether rendering lines as degenerate quads improves perf
     // when we have a mix
+
+    GrDrawState::VertexEdgeType oldEdgeType = drawState->getVertexEdgeType();
+
     target->setIndexSourceToBuffer(fLinesIndexBuffer);
     int lines = 0;
     int nBufLines = fLinesIndexBuffer->maxQuads();
+    drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType);
     while (lines < lineCnt) {
         int n = GrMin(lineCnt - lines, nBufLines);
-        drawState->setVertexEdgeType(GrDrawState::kHairLine_EdgeType);
-        target->drawIndexed(kTriangles_PrimitiveType,
+        target->drawIndexed(kTriangles_GrPrimitiveType,
                             kVertsPerLineSeg*lines,    // startV
                             0,                         // startI
                             kVertsPerLineSeg*n,        // vCount
@@ -637,16 +611,16 @@
 
     target->setIndexSourceToBuffer(fQuadsIndexBuffer);
     int quads = 0;
+    drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType);
     while (quads < quadCnt) {
         int n = GrMin(quadCnt - quads, kNumQuadsInIdxBuffer);
-        drawState->setVertexEdgeType(GrDrawState::kHairQuad_EdgeType);
-        target->drawIndexed(kTriangles_PrimitiveType,
+        target->drawIndexed(kTriangles_GrPrimitiveType,
                             4 * lineCnt + kVertsPerQuad*quads, // startV
                             0,                                 // startI
                             kVertsPerQuad*n,                   // vCount
                             kIdxsPerQuad*n);                   // iCount
         quads += n;
     }
+    drawState->setVertexEdgeType(oldEdgeType);
     return true;
 }
-
diff --git a/src/gpu/GrAAHairLinePathRenderer.h b/src/gpu/GrAAHairLinePathRenderer.h
index 33b7332..7d1a6db 100644
--- a/src/gpu/GrAAHairLinePathRenderer.h
+++ b/src/gpu/GrAAHairLinePathRenderer.h
@@ -18,17 +18,16 @@
     static GrPathRenderer* Create(GrContext* context);
 
     virtual bool canDrawPath(const SkPath& path,
-                            GrPathFill fill,
-                            const GrDrawTarget* target,
-                            bool antiAlias) const SK_OVERRIDE;
+                             const SkStrokeRec& stroke,
+                             const GrDrawTarget* target,
+                             bool antiAlias) const SK_OVERRIDE;
+
 protected:
     virtual bool onDrawPath(const SkPath& path,
-                            GrPathFill fill,
-                            const GrVec* translate,
+                            const SkStrokeRec& stroke,
                             GrDrawTarget* target,
-                            GrDrawState::StageMask stageMask,
                             bool antiAlias) SK_OVERRIDE;
- 
+
 private:
 
     GrAAHairLinePathRenderer(const GrContext* context,
@@ -36,11 +35,10 @@
                              const GrIndexBuffer* fQuadsIndexBuffer);
 
     bool createGeom(const SkPath& path,
-                    const GrVec* translate,
                     GrDrawTarget* target,
-                    GrDrawState::StageMask stageMask,
                     int* lineCnt,
-                    int* quadCnt);
+                    int* quadCnt,
+                    GrDrawTarget::AutoReleaseGeometry* arg);
 
     const GrIndexBuffer*        fLinesIndexBuffer;
     const GrIndexBuffer*        fQuadsIndexBuffer;
@@ -50,4 +48,3 @@
 
 
 #endif
-
diff --git a/src/gpu/GrAARectRenderer.cpp b/src/gpu/GrAARectRenderer.cpp
new file mode 100644
index 0000000..b23ed9a
--- /dev/null
+++ b/src/gpu/GrAARectRenderer.cpp
@@ -0,0 +1,258 @@
+/*
+ * 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 "GrAARectRenderer.h"
+#include "GrRefCnt.h"
+#include "GrGpu.h"
+
+SK_DEFINE_INST_COUNT(GrAARectRenderer)
+
+namespace {
+
+static GrVertexLayout aa_rect_layout(bool useCoverage) {
+    GrVertexLayout layout = 0;
+    if (useCoverage) {
+        layout |= GrDrawState::kCoverage_VertexLayoutBit;
+    } else {
+        layout |= GrDrawState::kColor_VertexLayoutBit;
+    }
+    return layout;
+}
+
+static void set_inset_fan(GrPoint* pts, size_t stride,
+                          const GrRect& r, SkScalar dx, SkScalar dy) {
+    pts->setRectFan(r.fLeft + dx, r.fTop + dy,
+                    r.fRight - dx, r.fBottom - dy, stride);
+}
+
+};
+
+void GrAARectRenderer::reset() {
+    GrSafeSetNull(fAAFillRectIndexBuffer);
+    GrSafeSetNull(fAAStrokeRectIndexBuffer);
+}
+
+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 = GR_ARRAY_COUNT(gFillAARectIdx);
+static const int kVertsPerAAFillRect = 8;
+static const int kNumAAFillRectsInIndexBuffer = 256;
+
+GrIndexBuffer* GrAARectRenderer::aaFillRectIndexBuffer(GrGpu* gpu) {
+    static const size_t kAAFillRectIndexBufferSize = kIndicesPerAAFillRect *
+                                                     sizeof(uint16_t) *
+                                                     kNumAAFillRectsInIndexBuffer;
+
+    if (NULL == fAAFillRectIndexBuffer) {
+        fAAFillRectIndexBuffer = gpu->createIndexBuffer(kAAFillRectIndexBufferSize, false);
+        if (NULL != fAAFillRectIndexBuffer) {
+            uint16_t* data = (uint16_t*) fAAFillRectIndexBuffer->lock();
+            bool useTempData = (NULL == data);
+            if (useTempData) {
+                data = SkNEW_ARRAY(uint16_t, kNumAAFillRectsInIndexBuffer * kIndicesPerAAFillRect);
+            }
+            for (int i = 0; i < kNumAAFillRectsInIndexBuffer; ++i) {
+                // Each AA filled rect is drawn with 8 vertices and 10 triangles (8 around
+                // the inner rect (for AA) and 2 for the inner rect.
+                int baseIdx = i * kIndicesPerAAFillRect;
+                uint16_t baseVert = (uint16_t)(i * kVertsPerAAFillRect);
+                for (int j = 0; j < kIndicesPerAAFillRect; ++j) {
+                    data[baseIdx+j] = baseVert + gFillAARectIdx[j];
+                }
+            }
+            if (useTempData) {
+                if (!fAAFillRectIndexBuffer->updateData(data, kAAFillRectIndexBufferSize)) {
+                    GrCrash("Can't get AA Fill Rect indices into buffer!");
+                }
+                SkDELETE_ARRAY(data);
+            } else {
+                fAAFillRectIndexBuffer->unlock();
+            }
+        }
+    }
+
+    return fAAFillRectIndexBuffer;
+}
+
+static const uint16_t gStrokeAARectIdx[] = {
+    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,
+};
+
+int GrAARectRenderer::aaStrokeRectIndexCount() {
+    return GR_ARRAY_COUNT(gStrokeAARectIdx);
+}
+
+GrIndexBuffer* GrAARectRenderer::aaStrokeRectIndexBuffer(GrGpu* gpu) {
+    if (NULL == fAAStrokeRectIndexBuffer) {
+        fAAStrokeRectIndexBuffer =
+                  gpu->createIndexBuffer(sizeof(gStrokeAARectIdx), false);
+        if (NULL != fAAStrokeRectIndexBuffer) {
+#if GR_DEBUG
+            bool updated =
+#endif
+            fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx,
+                                                 sizeof(gStrokeAARectIdx));
+            GR_DEBUGASSERT(updated);
+        }
+    }
+    return fAAStrokeRectIndexBuffer;
+}
+
+void GrAARectRenderer::fillAARect(GrGpu* gpu,
+                                  GrDrawTarget* target,
+                                  const GrRect& devRect,
+                                  bool useVertexCoverage) {
+    GrVertexLayout layout = aa_rect_layout(useVertexCoverage);
+
+    size_t vsize = GrDrawState::VertexSize(layout);
+
+    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 8, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
+        return;
+    }
+
+    GrIndexBuffer* indexBuffer = this->aaFillRectIndexBuffer(gpu);
+    if (NULL == indexBuffer) {
+        GrPrintf("Failed to create index buffer!\n");
+        return;
+    }
+
+    intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
+
+    GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
+    GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
+
+    set_inset_fan(fan0Pos, vsize, devRect, -SK_ScalarHalf, -SK_ScalarHalf);
+    set_inset_fan(fan1Pos, vsize, devRect,  SK_ScalarHalf,  SK_ScalarHalf);
+
+    verts += sizeof(GrPoint);
+    for (int i = 0; i < 4; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+    }
+
+    GrColor innerColor;
+    if (useVertexCoverage) {
+        innerColor = 0xffffffff;
+    } else {
+        innerColor = target->getDrawState().getColor();
+    }
+
+    verts += 4 * vsize;
+    for (int i = 0; i < 4; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
+    }
+
+    target->setIndexSourceToBuffer(indexBuffer);
+    target->drawIndexedInstances(kTriangles_GrPrimitiveType, 1,
+                                 kVertsPerAAFillRect,
+                                 kIndicesPerAAFillRect);
+}
+
+void GrAARectRenderer::strokeAARect(GrGpu* gpu,
+                                    GrDrawTarget* target,
+                                    const GrRect& devRect,
+                                    const GrVec& devStrokeSize,
+                                    bool useVertexCoverage) {
+    const SkScalar& dx = devStrokeSize.fX;
+    const SkScalar& dy = devStrokeSize.fY;
+    const SkScalar rx = SkScalarMul(dx, SK_ScalarHalf);
+    const SkScalar ry = SkScalarMul(dy, SK_ScalarHalf);
+
+    SkScalar spare;
+    {
+        SkScalar w = devRect.width() - dx;
+        SkScalar h = devRect.height() - dy;
+        spare = GrMin(w, h);
+    }
+
+    if (spare <= 0) {
+        GrRect r(devRect);
+        r.inset(-rx, -ry);
+        this->fillAARect(gpu, target, r, useVertexCoverage);
+        return;
+    }
+    GrVertexLayout layout = aa_rect_layout(useVertexCoverage);
+    size_t vsize = GrDrawState::VertexSize(layout);
+
+    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
+        return;
+    }
+    GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer(gpu);
+    if (NULL == indexBuffer) {
+        GrPrintf("Failed to create index buffer!\n");
+        return;
+    }
+
+    intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
+
+    // We create vertices for four nested rectangles. There are two ramps from 0 to full
+    // coverage, one on the exterior of the stroke and the other on the interior.
+    // The following pointers refer to the four rects, from outermost to innermost.
+    GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
+    GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
+    GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
+    GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
+
+    set_inset_fan(fan0Pos, vsize, devRect,
+                  -rx - SK_ScalarHalf, -ry - SK_ScalarHalf);
+    set_inset_fan(fan1Pos, vsize, devRect,
+                  -rx + SK_ScalarHalf, -ry + SK_ScalarHalf);
+    set_inset_fan(fan2Pos, vsize, devRect,
+                  rx - SK_ScalarHalf,  ry - SK_ScalarHalf);
+    set_inset_fan(fan3Pos, vsize, devRect,
+                  rx + SK_ScalarHalf,  ry + SK_ScalarHalf);
+
+    // The outermost rect has 0 coverage
+    verts += sizeof(GrPoint);
+    for (int i = 0; i < 4; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+    }
+
+    // The inner two rects have full coverage
+    GrColor innerColor;
+    if (useVertexCoverage) {
+        innerColor = 0xffffffff;
+    } else {
+        innerColor = target->getDrawState().getColor();
+    }
+    verts += 4 * vsize;
+    for (int i = 0; i < 8; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
+    }
+
+    // The innermost rect has full coverage
+    verts += 8 * vsize;
+    for (int i = 0; i < 4; ++i) {
+        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
+    }
+
+    target->setIndexSourceToBuffer(indexBuffer);
+    target->drawIndexed(kTriangles_GrPrimitiveType,
+                        0, 0, 16, aaStrokeRectIndexCount());
+}
diff --git a/src/gpu/GrAddPathRenderers_default.cpp b/src/gpu/GrAddPathRenderers_default.cpp
index 8f6eb1e..4f17243 100644
--- a/src/gpu/GrAddPathRenderers_default.cpp
+++ b/src/gpu/GrAddPathRenderers_default.cpp
@@ -6,18 +6,29 @@
  * found in the LICENSE file.
  */
 
- 
+
+#include "GrStencilAndCoverPathRenderer.h"
 #include "GrAAHairLinePathRenderer.h"
 #include "GrAAConvexPathRenderer.h"
+#if GR_STROKE_PATH_RENDERING
+#include "../../experimental/StrokePathRenderer/GrStrokePathRenderer.h"
+#endif
+#if GR_ANDROID_PATH_RENDERING
+#include "../../experimental/AndroidPathRenderer/GrAndroidPathRenderer.h"
+#endif
 
-void GrPathRenderer::AddPathRenderers(GrContext* ctx,
-                                      GrPathRendererChain::UsageFlags flags,
-                                      GrPathRendererChain* chain) {
-    if (!(GrPathRendererChain::kNonAAOnly_UsageFlag & flags)) {
-
-        if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
-            chain->addPathRenderer(pr)->unref();
-        }
-        chain->addPathRenderer(new GrAAConvexPathRenderer())->unref();
+void GrPathRenderer::AddPathRenderers(GrContext* ctx, GrPathRendererChain* chain) {
+#if GR_STROKE_PATH_RENDERING
+    chain->addPathRenderer(SkNEW(GrStrokePathRenderer))->unref();
+#endif
+#if GR_ANDROID_PATH_RENDERING
+    chain->addPathRenderer(SkNEW(GrAndroidPathRenderer))->unref();
+#endif
+    if (GrPathRenderer* pr = GrStencilAndCoverPathRenderer::Create(ctx)) {
+        chain->addPathRenderer(pr)->unref();
     }
+    if (GrPathRenderer* pr = GrAAHairLinePathRenderer::Create(ctx)) {
+        chain->addPathRenderer(pr)->unref();
+    }
+    chain->addPathRenderer(SkNEW(GrAAConvexPathRenderer))->unref();
 }
diff --git a/src/gpu/GrAddPathRenderers_none.cpp b/src/gpu/GrAddPathRenderers_none.cpp
index 46855db..02da710 100644
--- a/src/gpu/GrAddPathRenderers_none.cpp
+++ b/src/gpu/GrAddPathRenderers_none.cpp
@@ -10,6 +10,6 @@
 #include "GrPathRenderer.h"
 
 
-void GrPathRenderer::AddPathRenderers(GrContext*, 
+void GrPathRenderer::AddPathRenderers(GrContext*,
                                       GrPathRendererChain::UsageFlags,
                                       GrPathRendererChain*) {}
diff --git a/src/gpu/GrAddPathRenderers_tesselated.cpp b/src/gpu/GrAddPathRenderers_tesselated.cpp
deleted file mode 100644
index a1cde13..0000000
--- a/src/gpu/GrAddPathRenderers_tesselated.cpp
+++ /dev/null
@@ -1,17 +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 "GrTesselatedPathRenderer.h"
-
-
-void GrPathRenderer::AddPathRenderers(GrContext*,
-                                      GrPathRendererChain::UsageFlags flags,
-                                      GrPathRendererChain* chain) {
-    chain->addPathRenderer(new GrTesselatedPathRenderer())->unref();
-}
diff --git a/src/gpu/GrAllocPool.cpp b/src/gpu/GrAllocPool.cpp
index ecd2acf..971f8ee 100644
--- a/src/gpu/GrAllocPool.cpp
+++ b/src/gpu/GrAllocPool.cpp
@@ -40,7 +40,7 @@
         fPtr += bytes;
         return ptr;
     }
-    
+
     size_t release(size_t bytes) {
         GrAssert(bytes > 0);
         size_t free = GrMin(bytes, fBytesTotal - fBytesFree);
@@ -48,7 +48,7 @@
         fPtr -= free;
         return bytes - free;
     }
-    
+
     bool empty() const { return fBytesTotal == fBytesFree; }
 };
 
@@ -79,7 +79,7 @@
 
 void* GrAllocPool::alloc(size_t size) {
     this->validate();
-    
+
     if (!fBlock || !fBlock->canAlloc(size)) {
         size_t blockSize = GrMax(fMinBlockSize, size);
         fBlock = Block::Create(blockSize, fBlock);
@@ -90,7 +90,7 @@
 
 void GrAllocPool::release(size_t bytes) {
     this->validate();
-    
+
     while (bytes && NULL != fBlock) {
         bytes = fBlock->release(bytes);
         if (fBlock->empty()) {
@@ -116,5 +116,3 @@
 }
 
 #endif
-
-
diff --git a/src/gpu/GrAllocPool.h b/src/gpu/GrAllocPool.h
index 3ecc4aa..4f58f00 100644
--- a/src/gpu/GrAllocPool.h
+++ b/src/gpu/GrAllocPool.h
@@ -29,7 +29,7 @@
      *  address was allocated by malloc or new (because it hasn't).
      */
     void* alloc(size_t bytes);
-    
+
     /**
      * Releases the most recently allocated bytes back to allocpool.
      */
@@ -61,4 +61,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrAllocator.h b/src/gpu/GrAllocator.h
index 555e56f..dc4c3de 100755
--- a/src/gpu/GrAllocator.h
+++ b/src/gpu/GrAllocator.h
@@ -11,6 +11,7 @@
 #ifndef GrAllocator_DEFINED
 #define GrAllocator_DEFINED
 
+#include "GrNoncopyable.h"
 #include "GrConfig.h"
 #include "SkTArray.h"
 
@@ -54,8 +55,8 @@
             } else if (fOwnFirstBlock) {
                 fBlocks[0] = GrMalloc(fBlockSize);
             }
-        }        
-        void* ret = (char*)fBlocks[fCount/fItemsPerBlock] + 
+        }
+        void* ret = (char*)fBlocks[fCount/fItemsPerBlock] +
                     fItemSize * indexInBlock;
         ++fCount;
         return ret;
@@ -64,8 +65,8 @@
     /**
      * removes all added items
      */
-    void reset() {        
-        int blockCount = GrMax((unsigned)1, 
+    void reset() {
+        int blockCount = GrMax((unsigned)1,
                                GrUIDivRoundUp(fCount, fItemsPerBlock));
         for (int i = 1; i < blockCount; ++i) {
             GrFree(fBlocks[i]);
@@ -84,12 +85,12 @@
     int count() const {
         return fCount;
     }
-    
+
     /**
      * is the count 0
      */
     bool empty() const { return fCount == 0; }
-    
+
     /**
      * access last item, only call if count() != 0
      */
@@ -97,7 +98,7 @@
         GrAssert(fCount);
         return (*this)[fCount-1];
     }
-    
+
     /**
      * access last item, only call if count() != 0
      */
@@ -105,28 +106,28 @@
         GrAssert(fCount);
         return (*this)[fCount-1];
     }
-    
+
     /**
      * access item by index.
-     */    
+     */
     void* operator[] (int i) {
         GrAssert(i >= 0 && i < fCount);
-        return (char*)fBlocks[i / fItemsPerBlock] + 
+        return (char*)fBlocks[i / fItemsPerBlock] +
                fItemSize * (i % fItemsPerBlock);
     }
 
     /**
      * access item by index.
-     */  
+     */
     const void* operator[] (int i) const {
         GrAssert(i >= 0 && i < fCount);
-        return (const char*)fBlocks[i / fItemsPerBlock] + 
+        return (const char*)fBlocks[i / fItemsPerBlock] +
                fItemSize * (i % fItemsPerBlock);
     }
 
 private:
     static const int NUM_INIT_BLOCK_PTRS = 8;
-    
+
     SkSTArray<NUM_INIT_BLOCK_PTRS, void*>   fBlocks;
     size_t                                  fBlockSize;
     size_t                                  fItemSize;
@@ -162,14 +163,14 @@
     T& push_back() {
         void* item = fAllocator.push_back();
         GrAssert(NULL != item);
-        new (item) T;
+        SkNEW_PLACEMENT(item, T);
         return *(T*)item;
     }
 
     T& push_back(const T& t) {
         void* item = fAllocator.push_back();
         GrAssert(NULL != item);
-        new (item) T(t);
+        SkNEW_PLACEMENT_ARGS(item, T, (t));
         return *(T*)item;
     }
 
@@ -183,19 +184,19 @@
         }
         fAllocator.reset();
     }
-    
+
     /**
      * count of items
      */
     int count() const {
         return fAllocator.count();
     }
-    
+
     /**
      * is the count 0
      */
     bool empty() const { return fAllocator.empty(); }
-    
+
     /**
      * access last item, only call if count() != 0
      */
@@ -212,11 +213,11 @@
 
     /**
      * access item by index.
-     */  
+     */
     T& operator[] (int i) {
         return *(T*)(fAllocator[i]);
     }
-    
+
     /**
      * access item by index.
      */
diff --git a/src/gpu/GrAtlas.cpp b/src/gpu/GrAtlas.cpp
index 9553cf2..48a4f39 100644
--- a/src/gpu/GrAtlas.cpp
+++ b/src/gpu/GrAtlas.cpp
@@ -111,13 +111,13 @@
     }
     adjustForPlot(loc, fPlot);
     GrContext* context = fTexture->getContext();
-    // We call the internal version so that we don't force a flush. We assume
-    // our caller is smart and hasn't referenced the part of the texture we're
-    // about to update since the last flush.
-    context->internalWriteTexturePixels(fTexture, loc->fX, loc->fY,
-                                        dstW, dstH, fTexture->config(),
-                                        image, 0,
-                                        GrContext::kDontFlush_PixelOpsFlag);
+    // We pass the flag that does not force a flush. We assume our caller is
+    // smart and hasn't referenced the part of the texture we're about to update
+    // since the last flush.
+    context->writeTexturePixels(fTexture,
+                                loc->fX, loc->fY, dstW, dstH,
+                                fTexture->config(), image, 0,
+                                GrContext::kDontFlush_PixelOpsFlag);
 
     // now tell the caller to skip the top/left BORDER
     loc->fX += BORDER;
@@ -131,7 +131,7 @@
     fGpu = gpu;
     gpu->ref();
     Gr_bzero(fTexture, sizeof(fTexture));
-    fPlotMgr = new GrPlotMgr(GR_PLOT_WIDTH, GR_PLOT_HEIGHT);
+    fPlotMgr = SkNEW_ARGS(GrPlotMgr, (GR_PLOT_WIDTH, GR_PLOT_HEIGHT));
 }
 
 GrAtlasMgr::~GrAtlasMgr() {
@@ -177,20 +177,20 @@
     GrAssert(0 == kA8_GrMaskFormat);
     GrAssert(1 == kA565_GrMaskFormat);
     if (NULL == fTexture[format]) {
-        GrTextureDesc desc = {
-            kDynamicUpdate_GrTextureFlagBit,
-            GR_ATLAS_TEXTURE_WIDTH,
-            GR_ATLAS_TEXTURE_HEIGHT,
-            maskformat2pixelconfig(format),
-            {0} // samples
-        };
+        // TODO: Update this to use the cache rather than directly creating a texture.
+        GrTextureDesc desc;
+        desc.fFlags = kDynamicUpdate_GrTextureFlagBit;
+        desc.fWidth = GR_ATLAS_TEXTURE_WIDTH;
+        desc.fHeight = GR_ATLAS_TEXTURE_HEIGHT;
+        desc.fConfig = maskformat2pixelconfig(format);
+
         fTexture[format] = fGpu->createTexture(desc, NULL, 0);
         if (NULL == fTexture[format]) {
             return NULL;
         }
     }
 
-    GrAtlas* newAtlas = new GrAtlas(this, plot.fX, plot.fY, format);
+    GrAtlas* newAtlas = SkNEW_ARGS(GrAtlas, (this, plot.fX, plot.fY, format));
     if (!newAtlas->addSubImage(width, height, image, loc)) {
         delete newAtlas;
         return NULL;
@@ -204,5 +204,3 @@
     GrAssert(fPlotMgr->isBusy(x, y));
     fPlotMgr->freePlot(x, y);
 }
-
-
diff --git a/src/gpu/GrAtlas.h b/src/gpu/GrAtlas.h
index f0114e3..40a2154 100644
--- a/src/gpu/GrAtlas.h
+++ b/src/gpu/GrAtlas.h
@@ -13,7 +13,6 @@
 
 #include "GrPoint.h"
 #include "GrTexture.h"
-#include "GrTDArray.h"
 
 class GrGpu;
 class GrRectanizer;
@@ -80,5 +79,3 @@
 };
 
 #endif
-
-
diff --git a/src/gpu/GrBinHashKey.h b/src/gpu/GrBinHashKey.h
index 028bb91..8fa53ef 100644
--- a/src/gpu/GrBinHashKey.h
+++ b/src/gpu/GrBinHashKey.h
@@ -13,38 +13,50 @@
 #include "GrTypes.h"
 
 /**
- *  Hash function class that can take a data chunk of any predetermined
- *  length. The hash function used is the One-at-a-Time Hash
- *  (http://burtleburtle.net/bob/hash/doobs.html).
+ *  Hash function class that can take a data chunk of any predetermined length. The hash function
+ *  used is the One-at-a-Time Hash (http://burtleburtle.net/bob/hash/doobs.html).
+ *
+ *  Keys are computed from ENTRY objects. ENTRY must be fully ordered by a member:
+ *      int compare(const GrTBinHashKey<ENTRY, ..>& k);
+ *  which returns negative if the ENTRY < k, 0 if it equals k, and positive if k < the ENTRY.
+ *  Additionally, ENTRY must be flattenable into the key using setKeyData.
+ *
+ *  This class satisfies the requirements to be a key for a GrTHashTable.
  */
-template<typename Entry, size_t KeySize>
-class GrBinHashKey {
+template<typename ENTRY, size_t KEY_SIZE>
+class GrTBinHashKey {
 public:
-    GrBinHashKey()
-        : fHash(0)
-#if GR_DEBUG
-        , fIsValid(false)
-#endif
-    {}
+    enum { kKeySize = KEY_SIZE };
 
-    GrBinHashKey(const GrBinHashKey<Entry, KeySize>& other) {
+    GrTBinHashKey() {
+        this->reset();
+    }
+
+    GrTBinHashKey(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) {
         *this = other;
     }
-    GrBinHashKey<Entry, KeySize>& operator=(const GrBinHashKey<Entry,
-        KeySize>& other) {
+
+    GrTBinHashKey<ENTRY, KEY_SIZE>& operator=(const GrTBinHashKey<ENTRY, KEY_SIZE>& other) {
         memcpy(this, &other, sizeof(*this));
         return *this;
     }
 
-    ~GrBinHashKey() {
+    ~GrTBinHashKey() {
+    }
+
+    void reset() {
+        fHash = 0;
+#if GR_DEBUG
+        fIsValid = false;
+#endif
     }
 
     void setKeyData(const uint32_t* SK_RESTRICT data) {
-        GrAssert(GrIsALIGN4(KeySize));
-        memcpy(&fData, data, KeySize);
+        GrAssert(GrIsALIGN4(KEY_SIZE));
+        memcpy(&fData, data, KEY_SIZE);
 
         uint32_t hash = 0;
-        size_t len = KeySize;
+        size_t len = KEY_SIZE;
         while (len >= 4) {
             hash += *data++;
             hash += (fHash << 10);
@@ -60,19 +72,17 @@
         fHash = hash;
     }
 
-    int compare(const GrBinHashKey<Entry, KeySize>& key) const {
+    int compare(const GrTBinHashKey<ENTRY, KEY_SIZE>& key) const {
         GrAssert(fIsValid && key.fIsValid);
-        return memcmp(fData, key.fData, KeySize);
+        return memcmp(fData, key.fData, KEY_SIZE);
     }
 
-    static bool
-    EQ(const Entry& entry, const GrBinHashKey<Entry, KeySize>& key) {
+    static bool EQ(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) {
         GrAssert(key.fIsValid);
         return 0 == entry.compare(key);
     }
 
-    static bool
-    LT(const Entry& entry, const GrBinHashKey<Entry, KeySize>& key) {
+    static bool LT(const ENTRY& entry, const GrTBinHashKey<ENTRY, KEY_SIZE>& key) {
         GrAssert(key.fIsValid);
         return entry.compare(key) < 0;
     }
@@ -82,9 +92,14 @@
         return fHash;
     }
 
+    const uint8_t* getData() const {
+        GrAssert(fIsValid);
+        return fData;
+    }
+
 private:
     uint32_t            fHash;
-    uint8_t             fData[KeySize];  //Buffer for key storage
+    uint8_t             fData[KEY_SIZE];  // Buffer for key storage
 
 #if GR_DEBUG
 public:
diff --git a/src/gpu/GrBufferAllocPool.cpp b/src/gpu/GrBufferAllocPool.cpp
index 8bed75f..ec8a9c9 100644
--- a/src/gpu/GrBufferAllocPool.cpp
+++ b/src/gpu/GrBufferAllocPool.cpp
@@ -134,11 +134,11 @@
         GrAssert(!fBlocks[i].fBuffer->isLocked());
     }
     for (int i = 0; i < fBlocks.count(); ++i) {
-        size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - fBlocks[i].fBytesFree; 
+        size_t bytes = fBlocks[i].fBuffer->sizeInBytes() - fBlocks[i].fBytesFree;
         bytesInUse += bytes;
         GrAssert(bytes || unusedBlockAllowed);
     }
-    
+
     GrAssert(bytesInUse == fBytesInUse);
     if (unusedBlockAllowed) {
         GrAssert((fBytesInUse && !fBlocks.empty()) ||
@@ -168,7 +168,8 @@
             *offset = usedBytes;
             *buffer = back.fBuffer;
             back.fBytesFree -= size + pad;
-            fBytesInUse += size;
+            fBytesInUse += size + pad;
+            VALIDATE();
             return (void*)(reinterpret_cast<intptr_t>(fBufferPtr) + usedBytes);
         }
     }
@@ -180,7 +181,7 @@
     // may be cheating on the actual buffer size by shrinking the buffer on
     // updateData() if the amount of data passed is less than the full buffer
     // size.
-    
+
     if (!createBlock(size)) {
         return NULL;
     }
@@ -295,7 +296,7 @@
 
     GrAssert(NULL == fBufferPtr);
 
-    if (fGpu->getCaps().fBufferLockSupport &&
+    if (fGpu->getCaps().bufferLockSupport() &&
         size > GR_GEOM_BUFFER_LOCK_THRESHOLD &&
         (!fFrequentResetHint || requestSize > GR_GEOM_BUFFER_LOCK_THRESHOLD)) {
         fBufferPtr = block.fBuffer->lock();
@@ -335,8 +336,9 @@
     GrAssert(!buffer->isLocked());
     GrAssert(fCpuData.get() == fBufferPtr);
     GrAssert(flushSize <= buffer->sizeInBytes());
+    VALIDATE(true);
 
-    if (fGpu->getCaps().fBufferLockSupport &&
+    if (fGpu->getCaps().bufferLockSupport() &&
         flushSize > GR_GEOM_BUFFER_LOCK_THRESHOLD) {
         void* data = buffer->lock();
         if (NULL != data) {
@@ -346,6 +348,7 @@
         }
     }
     buffer->updateData(fBufferPtr, flushSize);
+    VALIDATE(true);
 }
 
 GrGeometryBuffer* GrBufferAllocPool::createBuffer(size_t size) {
@@ -370,7 +373,7 @@
                     preallocBufferCnt) {
 }
 
-void* GrVertexBufferAllocPool::makeSpace(GrVertexLayout layout,
+void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize,
                                          int vertexCount,
                                          const GrVertexBuffer** buffer,
                                          int* startVertex) {
@@ -379,43 +382,41 @@
     GrAssert(NULL != buffer);
     GrAssert(NULL != startVertex);
 
-    size_t vSize = GrDrawTarget::VertexSize(layout);
     size_t offset = 0; // assign to suppress warning
     const GrGeometryBuffer* geomBuffer = NULL; // assign to suppress warning
-    void* ptr = INHERITED::makeSpace(vSize * vertexCount,
-                                     vSize,
+    void* ptr = INHERITED::makeSpace(vertexSize * vertexCount,
+                                     vertexSize,
                                      &geomBuffer,
                                      &offset);
 
     *buffer = (const GrVertexBuffer*) geomBuffer;
-    GrAssert(0 == offset % vSize);
-    *startVertex = offset / vSize;
+    GrAssert(0 == offset % vertexSize);
+    *startVertex = offset / vertexSize;
     return ptr;
 }
 
-bool GrVertexBufferAllocPool::appendVertices(GrVertexLayout layout,
+bool GrVertexBufferAllocPool::appendVertices(size_t vertexSize,
                                              int vertexCount,
                                              const void* vertices,
                                              const GrVertexBuffer** buffer,
                                              int* startVertex) {
-    void* space = makeSpace(layout, vertexCount, buffer, startVertex);
+    void* space = makeSpace(vertexSize, vertexCount, buffer, startVertex);
     if (NULL != space) {
         memcpy(space,
                vertices,
-               GrDrawTarget::VertexSize(layout) * vertexCount);
+               vertexSize * vertexCount);
         return true;
     } else {
         return false;
     }
 }
 
-int GrVertexBufferAllocPool::preallocatedBufferVertices(GrVertexLayout layout) const {
-    return INHERITED::preallocatedBufferSize() /
-            GrDrawTarget::VertexSize(layout);
+int GrVertexBufferAllocPool::preallocatedBufferVertices(size_t vertexSize) const {
+    return INHERITED::preallocatedBufferSize() / vertexSize;
 }
 
-int GrVertexBufferAllocPool::currentBufferVertices(GrVertexLayout layout) const {
-    return currentBufferItems(GrDrawTarget::VertexSize(layout));
+int GrVertexBufferAllocPool::currentBufferVertices(size_t vertexSize) const {
+    return currentBufferItems(vertexSize);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrBufferAllocPool.h b/src/gpu/GrBufferAllocPool.h
index 3e2cd39..ffd8c34 100644
--- a/src/gpu/GrBufferAllocPool.h
+++ b/src/gpu/GrBufferAllocPool.h
@@ -12,9 +12,9 @@
 #define GrBufferAllocPool_DEFINED
 
 #include "GrNoncopyable.h"
-#include "GrTDArray.h"
 
 #include "SkTArray.h"
+#include "SkTDArray.h"
 
 class GrGeometryBuffer;
 class GrGpu;
@@ -164,13 +164,13 @@
 #if GR_DEBUG
     void validate(bool unusedBlockAllowed = false) const;
 #endif
-    
+
     size_t                          fBytesInUse;
 
     GrGpu*                          fGpu;
     bool                            fGpuIsReffed;
     bool                            fFrequentResetHint;
-    GrTDArray<GrGeometryBuffer*>    fPreallocBuffers;
+    SkTDArray<GrGeometryBuffer*>    fPreallocBuffers;
     size_t                          fMinBlockSize;
     BufferType                      fBufferType;
 
@@ -222,7 +222,7 @@
      * the buffer at the offset indicated by startVertex. Until that time they
      * may be in temporary storage and/or the buffer may be locked.
      *
-     * @param layout       specifies type of vertices to allocate space for
+     * @param vertexSize   specifies size of a vertex to allocate space for
      * @param vertexCount  number of vertices to allocate space for
      * @param buffer       returns the vertex buffer that will hold the
      *                     vertices.
@@ -230,7 +230,7 @@
      *                     In units of the size of a vertex from layout param.
      * @return pointer to first vertex.
      */
-    void* makeSpace(GrVertexLayout layout,
+    void* makeSpace(size_t vertexSize,
                     int vertexCount,
                     const GrVertexBuffer** buffer,
                     int* startVertex);
@@ -238,7 +238,7 @@
     /**
      * Shortcut to make space and then write verts into the made space.
      */
-    bool appendVertices(GrVertexLayout layout,
+    bool appendVertices(size_t vertexSize,
                         int vertexCount,
                         const void* vertices,
                         const GrVertexBuffer** buffer,
@@ -251,21 +251,21 @@
      * would fit in the next available preallocated buffer. If any makeSpace
      * would force a new VB to be created the return value will be zero.
      *
-     * @param   the format of vertices to compute space for.
+     * @param   the size of a vertex to compute space for.
      * @return the number of vertices that would fit in the current buffer.
      */
-    int currentBufferVertices(GrVertexLayout layout) const;
+    int currentBufferVertices(size_t vertexSize) const;
 
     /**
      * Gets the number of vertices that can fit in a  preallocated vertex buffer.
      * Zero if no preallocated buffers.
      *
-     * @param   the format of vertices to compute space for.
+     * @param   the size of a vertex to compute space for.
      *
      * @return number of vertices that fit in one of the preallocated vertex
      *         buffers.
      */
-    int preallocatedBufferVertices(GrVertexLayout layout) const;
+    int preallocatedBufferVertices(size_t vertexSize) const;
 
 private:
     typedef GrBufferAllocPool INHERITED;
diff --git a/src/gpu/GrCacheID.cpp b/src/gpu/GrCacheID.cpp
new file mode 100644
index 0000000..c3d1d66
--- /dev/null
+++ b/src/gpu/GrCacheID.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 "GrTypes.h"
+#include "SkThread.h"       // for sk_atomic_inc
+
+static GrCacheID::Key kAssertKey;
+GR_STATIC_ASSERT(sizeof(kAssertKey.fData8)  == sizeof(kAssertKey.fData32));
+GR_STATIC_ASSERT(sizeof(kAssertKey.fData8) == sizeof(kAssertKey.fData64));
+GR_STATIC_ASSERT(sizeof(kAssertKey.fData8) == sizeof(kAssertKey));
+
+GrCacheID::Domain GrCacheID::GenerateDomain() {
+    static int32_t gNextDomain = kInvalid_Domain + 1;
+
+    int32_t domain = sk_atomic_inc(&gNextDomain);
+    if (domain >= 1 << (8 * sizeof(Domain))) {
+        GrCrash("Too many Cache Domains");
+    }
+
+    return static_cast<Domain>(domain);
+}
diff --git a/src/gpu/GrClip.cpp b/src/gpu/GrClip.cpp
deleted file mode 100644
index a02d9f4..0000000
--- a/src/gpu/GrClip.cpp
+++ /dev/null
@@ -1,145 +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 "GrClip.h"
-
-GrClip::GrClip() {
-    fConservativeBounds.setEmpty();
-    fConservativeBoundsValid = true;
-}
-
-GrClip::GrClip(const GrClip& src) {
-    *this = src;
-}
-
-GrClip::GrClip(const GrIRect& rect) {
-    this->setFromIRect(rect);
-}
-
-GrClip::GrClip(const GrRect& rect) {
-    this->setFromRect(rect);
-}
-
-GrClip::GrClip(GrClipIterator* iter, GrScalar tx, GrScalar ty,
-               const GrRect* bounds) {
-    this->setFromIterator(iter, tx, ty, bounds);
-}
-
-GrClip::~GrClip() {}
-
-GrClip& GrClip::operator=(const GrClip& src) {
-    fList = src.fList;
-    fConservativeBounds = src.fConservativeBounds;
-    fConservativeBoundsValid = src.fConservativeBoundsValid;
-    return *this;
-}
-
-void GrClip::setEmpty() {
-    fList.reset();
-    fConservativeBounds.setEmpty();
-    fConservativeBoundsValid = true;
-}
-
-void GrClip::setFromRect(const GrRect& r) {
-    fList.reset();
-    if (r.isEmpty()) {
-        // use a canonical empty rect for == testing.
-        setEmpty();
-    } else {
-        fList.push_back();
-        fList.back().fRect = r;
-        fList.back().fType = kRect_ClipType;
-        fList.back().fOp = kReplace_SetOp;
-        fConservativeBounds = r;
-        fConservativeBoundsValid = true;
-    }
-}
-
-void GrClip::setFromIRect(const GrIRect& r) {
-    fList.reset();
-    if (r.isEmpty()) {
-        // use a canonical empty rect for == testing.
-        setEmpty();
-    } else {
-        fList.push_back();
-        fList.back().fRect.set(r);
-        fList.back().fType = kRect_ClipType;
-        fList.back().fOp = kReplace_SetOp;
-        fConservativeBounds.set(r);
-        fConservativeBoundsValid = true;
-    }
-}
-
-static void intersectWith(SkRect* dst, const SkRect& src) {
-    if (!dst->intersect(src)) {
-        dst->setEmpty();
-    }
-}
-
-void GrClip::setFromIterator(GrClipIterator* iter, GrScalar tx, GrScalar ty,
-                             const GrRect* conservativeBounds) {
-    fList.reset();
-
-    int rectCount = 0;
-
-    // compute bounds for common case of series of intersecting rects.
-    bool isectRectValid = true;
-
-    if (iter) {
-        for (iter->rewind(); !iter->isDone(); iter->next()) {
-            Element& e = fList.push_back();
-            e.fType = iter->getType();
-            e.fOp = iter->getOp();
-            // iterators should not emit replace
-            GrAssert(kReplace_SetOp != e.fOp);
-            switch (e.fType) {
-                case kRect_ClipType:
-                    iter->getRect(&e.fRect);
-                    if (tx || ty) {
-                        e.fRect.offset(tx, ty);
-                    }
-                    ++rectCount;
-                    if (isectRectValid) {
-                        if (kIntersect_SetOp == e.fOp) {
-                            GrAssert(fList.count() <= 2);
-                            if (fList.count() > 1) {
-                                GrAssert(2 == rectCount);
-                                rectCount = 1;
-                                fList.pop_back();
-                                GrAssert(kRect_ClipType == fList.back().fType);
-                                intersectWith(&fList.back().fRect, e.fRect);
-                            }
-                        } else {
-                            isectRectValid = false;
-                        }
-                    }
-                    break;
-                case kPath_ClipType:
-                    e.fPath = *iter->getPath();
-                    if (tx || ty) {
-                        e.fPath.offset(tx, ty);
-                    }
-                    e.fPathFill = iter->getPathFill();
-                    isectRectValid = false;
-                    break;
-                default:
-                    GrCrash("Unknown clip element type.");
-            }
-        }
-    }
-    fConservativeBoundsValid = false;
-    if (isectRectValid && rectCount) {
-        fConservativeBounds = fList[0].fRect;
-        fConservativeBoundsValid = true;
-    } else if (NULL != conservativeBounds) {
-        fConservativeBounds = *conservativeBounds;
-        fConservativeBoundsValid = true;
-    }
-}
diff --git a/src/gpu/GrClipData.cpp b/src/gpu/GrClipData.cpp
new file mode 100644
index 0000000..ce9dcdf
--- /dev/null
+++ b/src/gpu/GrClipData.cpp
@@ -0,0 +1,34 @@
+
+/*
+ * 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 "GrClipData.h"
+#include "GrSurface.h"
+#include "GrRect.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * getConservativeBounds returns the conservative bounding box of the clip
+ * in device (as opposed to canvas) coordinates. If the bounding box is
+ * the result of purely intersections of rects (with an initial replace)
+ * isIntersectionOfRects will be set to true.
+ */
+void GrClipData::getConservativeBounds(const GrSurface* surface,
+                                       GrIRect* devResult,
+                                       bool* isIntersectionOfRects) const {
+    GrRect devBounds;
+
+    fClipStack->getConservativeBounds(-fOrigin.fX,
+                                      -fOrigin.fY,
+                                      surface->width(),
+                                      surface->height(),
+                                      &devBounds,
+                                      isIntersectionOfRects);
+
+    devBounds.roundOut(devResult);
+}
diff --git a/src/gpu/GrClipMaskCache.cpp b/src/gpu/GrClipMaskCache.cpp
new file mode 100644
index 0000000..470cc4a
--- /dev/null
+++ b/src/gpu/GrClipMaskCache.cpp
@@ -0,0 +1,21 @@
+
+/*
+ * 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 "GrClipMaskCache.h"
+
+GrClipMaskCache::GrClipMaskCache()
+    : fContext(NULL)
+    , fStack(sizeof(GrClipStackFrame)) {
+    // We need an initial frame to capture the clip state prior to
+    // any pushes
+    SkNEW_PLACEMENT(fStack.push_back(), GrClipStackFrame);
+}
+
+void GrClipMaskCache::push() {
+    SkNEW_PLACEMENT(fStack.push_back(), GrClipStackFrame);
+}
diff --git a/src/gpu/GrClipMaskCache.h b/src/gpu/GrClipMaskCache.h
new file mode 100644
index 0000000..9a091c4
--- /dev/null
+++ b/src/gpu/GrClipMaskCache.h
@@ -0,0 +1,239 @@
+/*
+ * 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 GrClipMaskCache_DEFINED
+#define GrClipMaskCache_DEFINED
+
+#include "GrContext.h"
+#include "GrNoncopyable.h"
+#include "SkClipStack.h"
+
+class GrTexture;
+
+/**
+ * The stencil buffer stores the last clip path - providing a single entry
+ * "cache". This class provides similar functionality for AA clip paths
+ */
+class GrClipMaskCache : public GrNoncopyable {
+public:
+    GrClipMaskCache();
+
+    ~GrClipMaskCache() {
+
+        while (!fStack.empty()) {
+            GrClipStackFrame* temp = (GrClipStackFrame*) fStack.back();
+            temp->~GrClipStackFrame();
+            fStack.pop_back();
+        }
+    }
+
+    bool canReuse(int32_t clipGenID, const SkIRect& bounds) {
+
+        SkASSERT(clipGenID != SkClipStack::kWideOpenGenID);
+        SkASSERT(clipGenID != SkClipStack::kEmptyGenID);
+
+        if (SkClipStack::kInvalidGenID == clipGenID) {
+            return false;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        // We could reuse the mask if bounds is a subset of last bounds. We'd have to communicate
+        // an offset to the caller.
+        if (back->fLastMask.texture() &&
+            back->fLastBound == bounds &&
+            back->fLastClipGenID == clipGenID) {
+            return true;
+        }
+
+        return false;
+    }
+
+    void reset() {
+        if (fStack.empty()) {
+//            GrAssert(false);
+            return;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        back->reset();
+    }
+
+    /**
+     * After a "push" the clip state is entirely open. Currently, the
+     * entire clip stack will be re-rendered into a new clip mask.
+     * TODO: can we take advantage of the nested nature of the clips to
+     * reduce the mask creation cost?
+     */
+    void push();
+
+    void pop() {
+        //GrAssert(!fStack.empty());
+
+        if (!fStack.empty()) {
+            GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+            back->~GrClipStackFrame();
+            fStack.pop_back();
+        }
+    }
+
+    int32_t getLastClipGenID() const {
+
+        if (fStack.empty()) {
+            return SkClipStack::kInvalidGenID;
+        }
+
+        return ((GrClipStackFrame*) fStack.back())->fLastClipGenID;
+    }
+
+    GrTexture* getLastMask() {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            return NULL;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        return back->fLastMask.texture();
+    }
+
+    const GrTexture* getLastMask() const {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            return NULL;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        return back->fLastMask.texture();
+    }
+
+    void acquireMask(int32_t clipGenID,
+                     const GrTextureDesc& desc,
+                     const GrIRect& bound) {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            return;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        back->acquireMask(fContext, clipGenID, desc, bound);
+    }
+
+    int getLastMaskWidth() const {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            return -1;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        if (NULL == back->fLastMask.texture()) {
+            return -1;
+        }
+
+        return back->fLastMask.texture()->width();
+    }
+
+    int getLastMaskHeight() const {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            return -1;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        if (NULL == back->fLastMask.texture()) {
+            return -1;
+        }
+
+        return back->fLastMask.texture()->height();
+    }
+
+    void getLastBound(GrIRect* bound) const {
+
+        if (fStack.empty()) {
+            GrAssert(false);
+            bound->setEmpty();
+            return;
+        }
+
+        GrClipStackFrame* back = (GrClipStackFrame*) fStack.back();
+
+        *bound = back->fLastBound;
+    }
+
+    void setContext(GrContext* context) {
+        fContext = context;
+    }
+
+    GrContext* getContext() {
+        return fContext;
+    }
+
+    void releaseResources() {
+
+        SkDeque::F2BIter iter(fStack);
+        for (GrClipStackFrame* frame = (GrClipStackFrame*) iter.next();
+                frame != NULL;
+                frame = (GrClipStackFrame*) iter.next()) {
+            frame->reset();
+        }
+    }
+
+private:
+    struct GrClipStackFrame {
+
+        GrClipStackFrame() {
+            this->reset();
+        }
+
+        void acquireMask(GrContext* context,
+                         int32_t clipGenID,
+                         const GrTextureDesc& desc,
+                         const GrIRect& bound) {
+
+            fLastClipGenID = clipGenID;
+
+            fLastMask.set(context, desc);
+
+            fLastBound = bound;
+        }
+
+        void reset () {
+            fLastClipGenID = SkClipStack::kInvalidGenID;
+
+            GrTextureDesc desc;
+
+            fLastMask.set(NULL, desc);
+            fLastBound.setEmpty();
+        }
+
+        int32_t                 fLastClipGenID;
+        // The mask's width & height values are used by GrClipMaskManager to correctly scale the
+        // texture coords for the geometry drawn with this mask.
+        GrAutoScratchTexture    fLastMask;
+        // fLastBound stores the bounding box of the clip mask in clip-stack space. This rect is
+        // used by GrClipMaskManager to position a rect and compute texture coords for the mask.
+        GrIRect                 fLastBound;
+    };
+
+    GrContext*   fContext;
+    SkDeque      fStack;
+
+    typedef GrNoncopyable INHERITED;
+};
+
+#endif // GrClipMaskCache_DEFINED
diff --git a/src/gpu/GrClipMaskManager.cpp b/src/gpu/GrClipMaskManager.cpp
new file mode 100644
index 0000000..d2bfe7b
--- /dev/null
+++ b/src/gpu/GrClipMaskManager.cpp
@@ -0,0 +1,996 @@
+
+/*
+ * 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 "GrClipMaskManager.h"
+#include "effects/GrTextureDomainEffect.h"
+#include "GrGpu.h"
+#include "GrRenderTarget.h"
+#include "GrStencilBuffer.h"
+#include "GrPathRenderer.h"
+#include "GrPaint.h"
+#include "SkRasterClip.h"
+#include "SkStrokeRec.h"
+#include "GrAAConvexPathRenderer.h"
+#include "GrAAHairLinePathRenderer.h"
+#include "GrSWMaskHelper.h"
+
+#include "SkTLazy.h"
+
+#define GR_AA_CLIP 1
+
+typedef SkClipStack::Element Element;
+
+using namespace GrReducedClip;
+
+////////////////////////////////////////////////////////////////////////////////
+namespace {
+// set up the draw state to enable the aa clipping mask. Besides setting up the
+// stage matrix this also alters the vertex layout
+void setup_drawstate_aaclip(GrGpu* gpu,
+                            GrTexture* result,
+                            const GrIRect &devBound) {
+    GrDrawState* drawState = gpu->drawState();
+    GrAssert(drawState);
+
+    static const int kMaskStage = GrPaint::kTotalStages+1;
+
+    SkMatrix mat;
+    mat.setIDiv(result->width(), result->height());
+    mat.preTranslate(SkIntToScalar(-devBound.fLeft),
+                     SkIntToScalar(-devBound.fTop));
+    mat.preConcat(drawState->getViewMatrix());
+
+    SkIRect domainTexels = SkIRect::MakeWH(devBound.width(), devBound.height());
+    // This could be a long-lived effect that is cached with the alpha-mask.
+    drawState->setEffect(kMaskStage,
+                         GrTextureDomainEffect::Create(result,
+                                      mat,
+                                      GrTextureDomainEffect::MakeTexelDomain(result, domainTexels),
+                                      GrTextureDomainEffect::kDecal_WrapMode))->unref();
+}
+
+bool path_needs_SW_renderer(GrContext* context,
+                            GrGpu* gpu,
+                            const SkPath& origPath,
+                            const SkStrokeRec& stroke,
+                            bool doAA) {
+    // the gpu alpha mask will draw the inverse paths as non-inverse to a temp buffer
+    SkTCopyOnFirstWrite<SkPath> path(origPath);
+    if (path->isInverseFillType()) {
+        path.writable()->toggleInverseFillType();
+    }
+    // last (false) parameter disallows use of the SW path renderer
+    GrPathRendererChain::DrawType type = doAA ?
+                                         GrPathRendererChain::kColorAntiAlias_DrawType :
+                                         GrPathRendererChain::kColor_DrawType;
+
+    return NULL == context->getPathRenderer(*path, stroke, gpu, false, type);
+}
+
+}
+
+/*
+ * This method traverses the clip stack to see if the GrSoftwarePathRenderer
+ * will be used on any element. If so, it returns true to indicate that the
+ * entire clip should be rendered in SW and then uploaded en masse to the gpu.
+ */
+bool GrClipMaskManager::useSWOnlyPath(const ElementList& elements) {
+
+    // 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);
+
+    for (ElementList::Iter iter(elements.headIter()); iter.get(); iter.next()) {
+        const Element* element = iter.get();
+        // rects can always be drawn directly w/o using the software path
+        // so only paths need to be checked
+        if (Element::kPath_Type == element->getType() &&
+            path_needs_SW_renderer(this->getContext(), fGpu,
+                                   element->getPath(),
+                                   stroke,
+                                   element->isAA())) {
+            return true;
+        }
+    }
+    return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// sort out what kind of clip mask needs to be created: alpha, stencil,
+// scissor, or entirely software
+bool GrClipMaskManager::setupClipping(const GrClipData* clipDataIn) {
+    fCurrClipMaskType = kNone_ClipMaskType;
+
+    ElementList elements(16);
+    InitialState initialState;
+    SkIRect clipSpaceIBounds;
+    bool requiresAA;
+    bool isRect = false;
+
+    GrDrawState* drawState = fGpu->drawState();
+
+    const GrRenderTarget* rt = drawState->getRenderTarget();
+    // GrDrawTarget should have filtered this for us
+    GrAssert(NULL != rt);
+
+    bool ignoreClip = !drawState->isClipState() || clipDataIn->fClipStack->isWideOpen();
+
+    if (!ignoreClip) {
+        SkIRect clipSpaceRTIBounds = SkIRect::MakeWH(rt->width(), rt->height());
+        clipSpaceRTIBounds.offset(clipDataIn->fOrigin);
+        ReduceClipStack(*clipDataIn->fClipStack,
+                        clipSpaceRTIBounds,
+                        &elements,
+                        &initialState,
+                        &clipSpaceIBounds,
+                        &requiresAA);
+        if (elements.isEmpty()) {
+            if (kAllIn_InitialState == initialState) {
+                ignoreClip = clipSpaceIBounds == clipSpaceRTIBounds;
+                isRect = true;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    if (ignoreClip) {
+        fGpu->disableScissor();
+        this->setGpuStencil();
+        return true;
+    }
+
+#if GR_AA_CLIP
+    // TODO: catch isRect && requiresAA and use clip planes if available rather than a mask.
+
+    // If MSAA is enabled we can do everything in the stencil buffer.
+    if (0 == rt->numSamples() && requiresAA) {
+        int32_t genID = clipDataIn->fClipStack->getTopmostGenID();
+        GrTexture* result = NULL;
+
+        if (this->useSWOnlyPath(elements)) {
+            // The clip geometry is complex enough that it will be more efficient to create it
+            // entirely in software
+            result = this->createSoftwareClipMask(genID,
+                                                  initialState,
+                                                  elements,
+                                                  clipSpaceIBounds);
+        } else {
+            result = this->createAlphaClipMask(genID,
+                                               initialState,
+                                               elements,
+                                               clipSpaceIBounds);
+        }
+
+        if (NULL != result) {
+            // The mask's top left coord should be pinned to the rounded-out top left corner of
+            // clipSpace bounds. We determine the mask's position WRT to the render target here.
+            SkIRect rtSpaceMaskBounds = clipSpaceIBounds;
+            rtSpaceMaskBounds.offset(-clipDataIn->fOrigin);
+            setup_drawstate_aaclip(fGpu, result, rtSpaceMaskBounds);
+            fGpu->disableScissor();
+            this->setGpuStencil();
+            return true;
+        }
+        // if alpha clip mask creation fails fall through to the non-AA code paths
+    }
+#endif // GR_AA_CLIP
+
+    // Either a hard (stencil buffer) clip was explicitly requested or an anti-aliased clip couldn't
+    // be created. In either case, free up the texture in the anti-aliased mask cache.
+    // TODO: this may require more investigation. Ganesh performs a lot of utility draws (e.g.,
+    // clears, InOrderDrawBuffer playbacks) that hit the stencil buffer path. These may be
+    // "incorrectly" clearing the AA cache.
+    fAACache.reset();
+
+    // If the clip is a rectangle then just set the scissor. Otherwise, create
+    // a stencil mask.
+    if (isRect) {
+        SkIRect clipRect = clipSpaceIBounds;
+        clipRect.offset(-clipDataIn->fOrigin);
+        fGpu->enableScissor(clipRect);
+        this->setGpuStencil();
+        return true;
+    }
+
+    // use the stencil clip if we can't represent the clip as a rectangle.
+    SkIPoint clipSpaceToStencilSpaceOffset = -clipDataIn->fOrigin;
+    this->createStencilClipMask(initialState,
+                                elements,
+                                clipSpaceIBounds,
+                                clipSpaceToStencilSpaceOffset);
+
+    // This must occur after createStencilClipMask. That function may change the scissor. Also, it
+    // only guarantees that the stencil mask is correct within the bounds it was passed, so we must
+    // use both stencil and scissor test to the bounds for the final draw.
+    SkIRect scissorSpaceIBounds(clipSpaceIBounds);
+    scissorSpaceIBounds.offset(clipSpaceToStencilSpaceOffset);
+    fGpu->enableScissor(scissorSpaceIBounds);
+    this->setGpuStencil();
+    return true;
+}
+
+#define VISUALIZE_COMPLEX_CLIP 0
+
+#if VISUALIZE_COMPLEX_CLIP
+    #include "SkRandom.h"
+    SkRandom gRandom;
+    #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
+#else
+    #define SET_RANDOM_COLOR
+#endif
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+// set up the OpenGL blend function to perform the specified
+// boolean operation for alpha clip mask creation
+void setup_boolean_blendcoeffs(GrDrawState* drawState, SkRegion::Op op) {
+
+    switch (op) {
+        case SkRegion::kReplace_Op:
+            drawState->setBlendFunc(kOne_GrBlendCoeff, kZero_GrBlendCoeff);
+            break;
+        case SkRegion::kIntersect_Op:
+            drawState->setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
+            break;
+        case SkRegion::kUnion_Op:
+            drawState->setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
+            break;
+        case SkRegion::kXOR_Op:
+            drawState->setBlendFunc(kIDC_GrBlendCoeff, kISC_GrBlendCoeff);
+            break;
+        case SkRegion::kDifference_Op:
+            drawState->setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
+            break;
+        case SkRegion::kReverseDifference_Op:
+            drawState->setBlendFunc(kIDC_GrBlendCoeff, kZero_GrBlendCoeff);
+            break;
+        default:
+            GrAssert(false);
+            break;
+    }
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+bool GrClipMaskManager::drawElement(GrTexture* target,
+                                    const SkClipStack::Element* element,
+                                    GrPathRenderer* pr) {
+    GrDrawState* drawState = fGpu->drawState();
+
+    drawState->setRenderTarget(target->asRenderTarget());
+
+    switch (element->getType()) {
+        case Element::kRect_Type:
+            // TODO: Do rects directly to the accumulator using a aa-rect GrEffect that covers the
+            // entire mask bounds and writes 0 outside the rect.
+            if (element->isAA()) {
+                getContext()->getAARectRenderer()->fillAARect(fGpu,
+                                                              fGpu,
+                                                              element->getRect(),
+                                                              false);
+            } else {
+                fGpu->drawSimpleRect(element->getRect(), NULL);
+            }
+            return true;
+        case Element::kPath_Type: {
+            SkTCopyOnFirstWrite<SkPath> path(element->getPath());
+            if (path->isInverseFillType()) {
+                path.writable()->toggleInverseFillType();
+            }
+            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+            if (NULL == pr) {
+                GrPathRendererChain::DrawType type;
+                type = element->isAA() ? GrPathRendererChain::kColorAntiAlias_DrawType :
+                                         GrPathRendererChain::kColor_DrawType;
+                pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type);
+            }
+            if (NULL == pr) {
+                return false;
+            }
+            pr->drawPath(element->getPath(), stroke, fGpu, element->isAA());
+            break;
+        }
+        default:
+            // something is wrong if we're trying to draw an empty element.
+            GrCrash("Unexpected element type");
+            return false;
+    }
+    return true;
+}
+
+bool GrClipMaskManager::canStencilAndDrawElement(GrTexture* target,
+                                                 const SkClipStack::Element* element,
+                                                 GrPathRenderer** pr) {
+    GrDrawState* drawState = fGpu->drawState();
+    drawState->setRenderTarget(target->asRenderTarget());
+
+    switch (element->getType()) {
+        case Element::kRect_Type:
+            return true;
+        case Element::kPath_Type: {
+            SkTCopyOnFirstWrite<SkPath> path(element->getPath());
+            if (path->isInverseFillType()) {
+                path.writable()->toggleInverseFillType();
+            }
+            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+            GrPathRendererChain::DrawType type = element->isAA() ?
+                GrPathRendererChain::kStencilAndColorAntiAlias_DrawType :
+                GrPathRendererChain::kStencilAndColor_DrawType;
+            *pr = this->getContext()->getPathRenderer(*path, stroke, fGpu, false, type);
+            return NULL != *pr;
+        }
+        default:
+            // something is wrong if we're trying to draw an empty element.
+            GrCrash("Unexpected element type");
+            return false;
+    }
+}
+
+void GrClipMaskManager::mergeMask(GrTexture* dstMask,
+                                  GrTexture* srcMask,
+                                  SkRegion::Op op,
+                                  const GrIRect& dstBound,
+                                  const GrIRect& srcBound) {
+    GrDrawState* drawState = fGpu->drawState();
+    SkMatrix oldMatrix = drawState->getViewMatrix();
+    drawState->viewMatrix()->reset();
+
+    drawState->setRenderTarget(dstMask->asRenderTarget());
+
+    setup_boolean_blendcoeffs(drawState, op);
+
+    SkMatrix sampleM;
+    sampleM.setIDiv(srcMask->width(), srcMask->height());
+    drawState->setEffect(0,
+        GrTextureDomainEffect::Create(srcMask,
+                                      sampleM,
+                                      GrTextureDomainEffect::MakeTexelDomain(srcMask, srcBound),
+                                      GrTextureDomainEffect::kDecal_WrapMode))->unref();
+    fGpu->drawSimpleRect(SkRect::MakeFromIRect(dstBound), NULL);
+
+    drawState->disableStage(0);
+    drawState->setViewMatrix(oldMatrix);
+}
+
+// get a texture to act as a temporary buffer for AA clip boolean operations
+// TODO: given the expense of createTexture we may want to just cache this too
+void GrClipMaskManager::getTemp(int width, int height, GrAutoScratchTexture* temp) {
+    if (NULL != temp->texture()) {
+        // we've already allocated the temp texture
+        return;
+    }
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    temp->set(this->getContext(), desc);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Handles caching & allocation (if needed) of a clip alpha-mask texture for both the sw-upload
+// or gpu-rendered cases. Returns true if there is no more work to be done (i.e., we got a cache
+// hit)
+bool GrClipMaskManager::getMaskTexture(int32_t clipStackGenID,
+                                       const SkIRect& clipSpaceIBounds,
+                                       GrTexture** result) {
+    bool cached = fAACache.canReuse(clipStackGenID, clipSpaceIBounds);
+    if (!cached) {
+
+        // There isn't a suitable entry in the cache so we create a new texture to store the mask.
+        // Since we are setting up the cache we know the last lookup was a miss. Free up the
+        // currently cached mask so it can be reused.
+        fAACache.reset();
+
+        GrTextureDesc desc;
+        desc.fFlags = kRenderTarget_GrTextureFlagBit;
+        desc.fWidth = clipSpaceIBounds.width();
+        desc.fHeight = clipSpaceIBounds.height();
+        desc.fConfig = kAlpha_8_GrPixelConfig;
+
+        fAACache.acquireMask(clipStackGenID, desc, clipSpaceIBounds);
+    }
+
+    *result = fAACache.getLastMask();
+    return cached;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 8-bit clip mask in alpha
+GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID,
+                                                  InitialState initialState,
+                                                  const ElementList& elements,
+                                                  const SkIRect& clipSpaceIBounds) {
+    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
+
+    GrTexture* result;
+    if (this->getMaskTexture(clipStackGenID, clipSpaceIBounds, &result)) {
+        fCurrClipMaskType = kAlpha_ClipMaskType;
+        return result;
+    }
+
+    if (NULL == result) {
+        fAACache.reset();
+        return NULL;
+    }
+
+    GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+    GrDrawState* drawState = fGpu->drawState();
+
+    GrDrawTarget::AutoGeometryPush agp(fGpu);
+
+    // The top-left of the mask corresponds to the top-left corner of the bounds.
+    SkVector clipToMaskOffset = {
+        SkIntToScalar(-clipSpaceIBounds.fLeft),
+        SkIntToScalar(-clipSpaceIBounds.fTop)
+    };
+    // The texture may be larger than necessary, this rect represents the part of the texture
+    // we populate with a rasterization of the clip.
+    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
+
+    // We're drawing a coverage mask and want coverage to be run through the blend function.
+    drawState->enableState(GrDrawState::kCoverageDrawing_StateBit);
+
+    // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
+    drawState->viewMatrix()->setTranslate(clipToMaskOffset);
+
+    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
+    // clear the part that we care about.
+    fGpu->clear(&maskSpaceIBounds,
+                kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
+                result->asRenderTarget());
+
+    // When we use the stencil in the below loop it is important to have this clip installed.
+    // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
+    // pass must not set values outside of this bounds or stencil values outside the rect won't be
+    // cleared.
+    GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds);
+    drawState->enableState(GrDrawState::kClip_StateBit);
+
+    GrAutoScratchTexture temp;
+    // walk through each clip element and perform its set op
+    for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
+        const Element* element = iter.get();
+        SkRegion::Op op = element->getOp();
+        bool invert = element->isInverseFilled();
+
+        if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
+            GrPathRenderer* pr = NULL;
+            bool useTemp = !this->canStencilAndDrawElement(result, element, &pr);
+            GrTexture* dst;
+            // This is the bounds of the clip element in the space of the alpha-mask. The temporary
+            // mask buffer can be substantially larger than the actually clip stack element. We
+            // touch the minimum number of pixels necessary and use decal mode to combine it with
+            // the accumulator.
+            GrIRect maskSpaceElementIBounds;
+
+            if (useTemp) {
+                if (invert) {
+                    maskSpaceElementIBounds = maskSpaceIBounds;
+                } else {
+                    GrRect elementBounds = element->getBounds();
+                    elementBounds.offset(clipToMaskOffset);
+                    elementBounds.roundOut(&maskSpaceElementIBounds);
+                }
+
+                this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
+                if (NULL == temp.texture()) {
+                    fAACache.reset();
+                    return NULL;
+                }
+                dst = temp.texture();
+                // clear the temp target and set blend to replace
+                fGpu->clear(&maskSpaceElementIBounds,
+                            invert ? 0xffffffff : 0x00000000,
+                            dst->asRenderTarget());
+                setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
+
+            } else {
+                // draw directly into the result with the stencil set to make the pixels affected
+                // by the clip shape be non-zero.
+                dst = result;
+                GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
+                                             kReplace_StencilOp,
+                                             kReplace_StencilOp,
+                                             kAlways_StencilFunc,
+                                             0xffff,
+                                             0xffff,
+                                             0xffff);
+                drawState->setStencil(kStencilInElement);
+                setup_boolean_blendcoeffs(drawState, op);
+            }
+
+            drawState->setAlpha(invert ? 0x00 : 0xff);
+
+            if (!this->drawElement(dst, element, pr)) {
+                fAACache.reset();
+                return NULL;
+            }
+
+            if (useTemp) {
+                // Now draw into the accumulator using the real operation and the temp buffer as a
+                // texture
+                this->mergeMask(result,
+                                temp.texture(),
+                                op,
+                                maskSpaceIBounds,
+                                maskSpaceElementIBounds);
+            } else {
+                // Draw to the exterior pixels (those with a zero stencil value).
+                drawState->setAlpha(invert ? 0xff : 0x00);
+                GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement,
+                                             kZero_StencilOp,
+                                             kZero_StencilOp,
+                                             kEqual_StencilFunc,
+                                             0xffff,
+                                             0x0000,
+                                             0xffff);
+                drawState->setStencil(kDrawOutsideElement);
+                fGpu->drawSimpleRect(clipSpaceIBounds);
+                drawState->disableStencil();
+            }
+        } else {
+            // all the remaining ops can just be directly draw into the accumulation buffer
+            drawState->setAlpha(0xff);
+            setup_boolean_blendcoeffs(drawState, op);
+            this->drawElement(result, element);
+        }
+    }
+
+    fCurrClipMaskType = kAlpha_ClipMaskType;
+    return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Create a 1-bit clip mask in the stencil buffer. 'devClipBounds' are in device
+// (as opposed to canvas) coordinates
+bool GrClipMaskManager::createStencilClipMask(InitialState initialState,
+                                              const ElementList& elements,
+                                              const SkIRect& clipSpaceIBounds,
+                                              const SkIPoint& clipSpaceToStencilOffset) {
+
+    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
+
+    GrDrawState* drawState = fGpu->drawState();
+    GrAssert(drawState->isClipState());
+
+    GrRenderTarget* rt = drawState->getRenderTarget();
+    GrAssert(NULL != rt);
+
+    // TODO: dynamically attach a SB when needed.
+    GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
+    if (NULL == stencilBuffer) {
+        return false;
+    }
+    int32_t genID = elements.tail()->getGenID();
+
+    if (stencilBuffer->mustRenderClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset)) {
+
+        stencilBuffer->setLastClip(genID, clipSpaceIBounds, clipSpaceToStencilOffset);
+
+        GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+        drawState = fGpu->drawState();
+        drawState->setRenderTarget(rt);
+        GrDrawTarget::AutoGeometryPush agp(fGpu);
+
+        // We set the current clip to the bounds so that our recursive draws are scissored to them.
+        SkIRect stencilSpaceIBounds(clipSpaceIBounds);
+        stencilSpaceIBounds.offset(clipSpaceToStencilOffset);
+        GrDrawTarget::AutoClipRestore acr(fGpu, stencilSpaceIBounds);
+        drawState->enableState(GrDrawState::kClip_StateBit);
+
+        // Set the matrix so that rendered clip elements are transformed from clip to stencil space.
+        SkVector translate = {
+            SkIntToScalar(clipSpaceToStencilOffset.fX),
+            SkIntToScalar(clipSpaceToStencilOffset.fY)
+        };
+        drawState->viewMatrix()->setTranslate(translate);
+
+#if !VISUALIZE_COMPLEX_CLIP
+        drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
+#endif
+
+        int clipBit = stencilBuffer->bits();
+        SkASSERT((clipBit <= 16) && "Ganesh only handles 16b or smaller stencil buffers");
+        clipBit = (1 << (clipBit-1));
+
+        fGpu->clearStencilClip(stencilSpaceIBounds, kAllIn_InitialState == initialState);
+
+        // walk through each clip element and perform its set op
+        // with the existing clip.
+        for (ElementList::Iter iter(elements.headIter()); NULL != iter.get(); iter.next()) {
+            const Element* element = iter.get();
+            bool fillInverted = false;
+            // enabled at bottom of loop
+            drawState->disableState(GrGpu::kModifyStencilClip_StateBit);
+            // if the target is MSAA then we want MSAA enabled when the clip is soft
+            if (rt->isMultisampled()) {
+                drawState->setState(GrDrawState::kHWAntialias_StateBit, element->isAA());
+            }
+
+            // This will be used to determine whether the clip shape can be rendered into the
+            // stencil with arbitrary stencil settings.
+            GrPathRenderer::StencilSupport stencilSupport;
+
+            SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+
+            SkRegion::Op op = element->getOp();
+
+            GrPathRenderer* pr = NULL;
+            SkTCopyOnFirstWrite<SkPath> clipPath;
+            if (Element::kRect_Type == element->getType()) {
+                stencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
+                fillInverted = false;
+            } else {
+                GrAssert(Element::kPath_Type == element->getType());
+                clipPath.init(element->getPath());
+                fillInverted = clipPath->isInverseFillType();
+                if (fillInverted) {
+                    clipPath.writable()->toggleInverseFillType();
+                }
+                pr = this->getContext()->getPathRenderer(*clipPath,
+                                                         stroke,
+                                                         fGpu,
+                                                         false,
+                                                         GrPathRendererChain::kStencilOnly_DrawType,
+                                                         &stencilSupport);
+                if (NULL == pr) {
+                    return false;
+                }
+            }
+
+            int passes;
+            GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
+
+            bool canRenderDirectToStencil =
+                GrPathRenderer::kNoRestriction_StencilSupport == stencilSupport;
+            bool canDrawDirectToClip; // Given the renderer, the element,
+                                      // fill rule, and set operation can
+                                      // we render the element directly to
+                                      // stencil bit used for clipping.
+            canDrawDirectToClip = GrStencilSettings::GetClipPasses(op,
+                                                                   canRenderDirectToStencil,
+                                                                   clipBit,
+                                                                   fillInverted,
+                                                                   &passes,
+                                                                   stencilSettings);
+
+            // draw the element to the client stencil bits if necessary
+            if (!canDrawDirectToClip) {
+                GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
+                                             kIncClamp_StencilOp,
+                                             kIncClamp_StencilOp,
+                                             kAlways_StencilFunc,
+                                             0xffff,
+                                             0x0000,
+                                             0xffff);
+                SET_RANDOM_COLOR
+                if (Element::kRect_Type == element->getType()) {
+                    *drawState->stencil() = gDrawToStencil;
+                    fGpu->drawSimpleRect(element->getRect(), NULL);
+                } else {
+                    GrAssert(Element::kPath_Type == element->getType());
+                    if (canRenderDirectToStencil) {
+                        *drawState->stencil() = gDrawToStencil;
+                        pr->drawPath(*clipPath, stroke, fGpu, false);
+                    } else {
+                        pr->stencilPath(*clipPath, stroke, fGpu);
+                    }
+                }
+            }
+
+            // now we modify the clip bit by rendering either the clip
+            // element directly or a bounding rect of the entire clip.
+            drawState->enableState(GrGpu::kModifyStencilClip_StateBit);
+            for (int p = 0; p < passes; ++p) {
+                *drawState->stencil() = stencilSettings[p];
+                if (canDrawDirectToClip) {
+                    if (Element::kRect_Type == element->getType()) {
+                        SET_RANDOM_COLOR
+                        fGpu->drawSimpleRect(element->getRect(), NULL);
+                    } else {
+                        GrAssert(Element::kPath_Type == element->getType());
+                        SET_RANDOM_COLOR
+                        pr->drawPath(*clipPath, stroke, fGpu, false);
+                    }
+                } else {
+                    SET_RANDOM_COLOR
+                    // The view matrix is setup to do clip space -> stencil space translation, so
+                    // draw rect in clip space.
+                    fGpu->drawSimpleRect(SkRect::MakeFromIRect(clipSpaceIBounds), NULL);
+                }
+            }
+        }
+    }
+    // set this last because recursive draws may overwrite it back to kNone.
+    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
+    fCurrClipMaskType = kStencil_ClipMaskType;
+    return true;
+}
+
+
+// mapping of clip-respecting stencil funcs to normal stencil funcs
+// mapping depends on whether stencil-clipping is in effect.
+static const GrStencilFunc
+    gSpecialToBasicStencilFunc[2][kClipStencilFuncCount] = {
+    {// Stencil-Clipping is DISABLED,  we are effectively always inside the clip
+        // In the Clip Funcs
+        kAlways_StencilFunc,          // kAlwaysIfInClip_StencilFunc
+        kEqual_StencilFunc,           // kEqualIfInClip_StencilFunc
+        kLess_StencilFunc,            // kLessIfInClip_StencilFunc
+        kLEqual_StencilFunc,          // kLEqualIfInClip_StencilFunc
+        // Special in the clip func that forces user's ref to be 0.
+        kNotEqual_StencilFunc,        // kNonZeroIfInClip_StencilFunc
+                                      // make ref 0 and do normal nequal.
+    },
+    {// Stencil-Clipping is ENABLED
+        // In the Clip Funcs
+        kEqual_StencilFunc,           // kAlwaysIfInClip_StencilFunc
+                                      // eq stencil clip bit, mask
+                                      // out user bits.
+
+        kEqual_StencilFunc,           // kEqualIfInClip_StencilFunc
+                                      // add stencil bit to mask and ref
+
+        kLess_StencilFunc,            // kLessIfInClip_StencilFunc
+        kLEqual_StencilFunc,          // kLEqualIfInClip_StencilFunc
+                                      // for both of these we can add
+                                      // the clip bit to the mask and
+                                      // ref and compare as normal
+        // Special in the clip func that forces user's ref to be 0.
+        kLess_StencilFunc,            // kNonZeroIfInClip_StencilFunc
+                                      // make ref have only the clip bit set
+                                      // and make comparison be less
+                                      // 10..0 < 1..user_bits..
+    }
+};
+
+namespace {
+// Sets the settings to clip against the stencil buffer clip while ignoring the
+// client bits.
+const GrStencilSettings& basic_apply_stencil_clip_settings() {
+    // stencil settings to use when clip is in stencil
+    GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
+        kKeep_StencilOp,
+        kKeep_StencilOp,
+        kAlwaysIfInClip_StencilFunc,
+        0x0000,
+        0x0000,
+        0x0000);
+    return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
+}
+}
+
+void GrClipMaskManager::setGpuStencil() {
+    // We make two copies of the StencilSettings here (except in the early
+    // exit scenario. One copy from draw state to the stack var. Then another
+    // from the stack var to the gpu. We could make this class hold a ptr to
+    // GrGpu's fStencilSettings and eliminate the stack copy here.
+
+    const GrDrawState& drawState = fGpu->getDrawState();
+
+    // use stencil for clipping if clipping is enabled and the clip
+    // has been written into the stencil.
+    GrClipMaskManager::StencilClipMode clipMode;
+    if (this->isClipInStencil() && drawState.isClipState()) {
+        clipMode = GrClipMaskManager::kRespectClip_StencilClipMode;
+        // We can't be modifying the clip and respecting it at the same time.
+        GrAssert(!drawState.isStateFlagEnabled(
+                    GrGpu::kModifyStencilClip_StateBit));
+    } else if (drawState.isStateFlagEnabled(
+                    GrGpu::kModifyStencilClip_StateBit)) {
+        clipMode = GrClipMaskManager::kModifyClip_StencilClipMode;
+    } else {
+        clipMode = GrClipMaskManager::kIgnoreClip_StencilClipMode;
+    }
+
+    GrStencilSettings settings;
+    // The GrGpu client may not be using the stencil buffer but we may need to
+    // enable it in order to respect a stencil clip.
+    if (drawState.getStencil().isDisabled()) {
+        if (GrClipMaskManager::kRespectClip_StencilClipMode == clipMode) {
+            settings = basic_apply_stencil_clip_settings();
+        } else {
+            fGpu->disableStencil();
+            return;
+        }
+    } else {
+        settings = drawState.getStencil();
+    }
+
+    // TODO: dynamically attach a stencil buffer
+    int stencilBits = 0;
+    GrStencilBuffer* stencilBuffer =
+        drawState.getRenderTarget()->getStencilBuffer();
+    if (NULL != stencilBuffer) {
+        stencilBits = stencilBuffer->bits();
+    }
+
+    GrAssert(fGpu->getCaps().stencilWrapOpsSupport() || !settings.usesWrapOp());
+    GrAssert(fGpu->getCaps().twoSidedStencilSupport() || !settings.isTwoSided());
+    this->adjustStencilParams(&settings, clipMode, stencilBits);
+    fGpu->setStencilSettings(settings);
+}
+
+void GrClipMaskManager::adjustStencilParams(GrStencilSettings* settings,
+                                            StencilClipMode mode,
+                                            int stencilBitCnt) {
+    GrAssert(stencilBitCnt > 0);
+
+    if (kModifyClip_StencilClipMode == mode) {
+        // We assume that this clip manager itself is drawing to the GrGpu and
+        // has already setup the correct values.
+        return;
+    }
+
+    unsigned int clipBit = (1 << (stencilBitCnt - 1));
+    unsigned int userBits = clipBit - 1;
+
+    GrStencilSettings::Face face = GrStencilSettings::kFront_Face;
+    bool twoSided = fGpu->getCaps().twoSidedStencilSupport();
+
+    bool finished = false;
+    while (!finished) {
+        GrStencilFunc func = settings->func(face);
+        uint16_t writeMask = settings->writeMask(face);
+        uint16_t funcMask = settings->funcMask(face);
+        uint16_t funcRef = settings->funcRef(face);
+
+        GrAssert((unsigned) func < kStencilFuncCount);
+
+        writeMask &= userBits;
+
+        if (func >= kBasicStencilFuncCount) {
+            int respectClip = kRespectClip_StencilClipMode == mode;
+            if (respectClip) {
+                // The GrGpu class should have checked this
+                GrAssert(this->isClipInStencil());
+                switch (func) {
+                    case kAlwaysIfInClip_StencilFunc:
+                        funcMask = clipBit;
+                        funcRef = clipBit;
+                        break;
+                    case kEqualIfInClip_StencilFunc:
+                    case kLessIfInClip_StencilFunc:
+                    case kLEqualIfInClip_StencilFunc:
+                        funcMask = (funcMask & userBits) | clipBit;
+                        funcRef  = (funcRef  & userBits) | clipBit;
+                        break;
+                    case kNonZeroIfInClip_StencilFunc:
+                        funcMask = (funcMask & userBits) | clipBit;
+                        funcRef = clipBit;
+                        break;
+                    default:
+                        GrCrash("Unknown stencil func");
+                }
+            } else {
+                funcMask &= userBits;
+                funcRef &= userBits;
+            }
+            const GrStencilFunc* table =
+                gSpecialToBasicStencilFunc[respectClip];
+            func = table[func - kBasicStencilFuncCount];
+            GrAssert(func >= 0 && func < kBasicStencilFuncCount);
+        } else {
+            funcMask &= userBits;
+            funcRef &= userBits;
+        }
+
+        settings->setFunc(face, func);
+        settings->setWriteMask(face, writeMask);
+        settings->setFuncMask(face, funcMask);
+        settings->setFuncRef(face, funcRef);
+
+        if (GrStencilSettings::kFront_Face == face) {
+            face = GrStencilSettings::kBack_Face;
+            finished = !twoSided;
+        } else {
+            finished = true;
+        }
+    }
+    if (!twoSided) {
+        settings->copyFrontSettingsToBack();
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+GrTexture* GrClipMaskManager::createSoftwareClipMask(int32_t clipStackGenID,
+                                                     GrReducedClip::InitialState initialState,
+                                                     const GrReducedClip::ElementList& elements,
+                                                     const SkIRect& clipSpaceIBounds) {
+    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);
+
+    GrTexture* result;
+    if (this->getMaskTexture(clipStackGenID, clipSpaceIBounds, &result)) {
+        return result;
+    }
+
+    if (NULL == result) {
+        fAACache.reset();
+        return NULL;
+    }
+
+    // The mask texture may be larger than necessary. We round out the clip space bounds and pin
+    // the top left corner of the resulting rect to the top left of the texture.
+    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());
+
+    GrSWMaskHelper helper(this->getContext());
+
+    SkMatrix matrix;
+    matrix.setTranslate(SkIntToScalar(-clipSpaceIBounds.fLeft),
+                        SkIntToScalar(-clipSpaceIBounds.fTop));
+    helper.init(maskSpaceIBounds, &matrix);
+
+    helper.clear(kAllIn_InitialState == initialState ? 0xFF : 0x00);
+
+    SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+
+    for (ElementList::Iter iter(elements.headIter()) ; NULL != iter.get(); iter.next()) {
+
+        const Element* element = iter.get();
+        SkRegion::Op op = element->getOp();
+
+        if (SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
+            // Intersect and reverse difference require modifying pixels outside of the geometry
+            // that is being "drawn". In both cases we erase all the pixels outside of the geometry
+            // but leave the pixels inside the geometry alone. For reverse difference we invert all
+            // the pixels before clearing the ones outside the geometry.
+            if (SkRegion::kReverseDifference_Op == op) {
+                SkRect temp = SkRect::MakeFromIRect(clipSpaceIBounds);
+                // invert the entire scene
+                helper.draw(temp, SkRegion::kXOR_Op, false, 0xFF);
+            }
+
+            if (Element::kRect_Type == element->getType()) {
+                // convert the rect to a path so we can invert the fill
+                SkPath temp;
+                temp.addRect(element->getRect());
+                temp.setFillType(SkPath::kInverseEvenOdd_FillType);
+
+                helper.draw(temp, stroke, SkRegion::kReplace_Op,
+                            element->isAA(),
+                            0x00);
+            } else {
+                GrAssert(Element::kPath_Type == element->getType());
+                SkPath clipPath = element->getPath();
+                clipPath.toggleInverseFillType();
+                helper.draw(clipPath, stroke,
+                            SkRegion::kReplace_Op,
+                            element->isAA(),
+                            0x00);
+            }
+
+            continue;
+        }
+
+        // The other ops (union, xor, diff) only affect pixels inside
+        // the geometry so they can just be drawn normally
+        if (Element::kRect_Type == element->getType()) {
+            helper.draw(element->getRect(), op, element->isAA(), 0xFF);
+        } else {
+            GrAssert(Element::kPath_Type == element->getType());
+            helper.draw(element->getPath(), stroke, op, element->isAA(), 0xFF);
+        }
+    }
+
+    helper.toTexture(result, kAllIn_InitialState == initialState ? 0xFF : 0x00);
+
+    fCurrClipMaskType = kAlpha_ClipMaskType;
+    return result;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void GrClipMaskManager::releaseResources() {
+    fAACache.releaseResources();
+}
diff --git a/src/gpu/GrClipMaskManager.h b/src/gpu/GrClipMaskManager.h
new file mode 100644
index 0000000..534689a
--- /dev/null
+++ b/src/gpu/GrClipMaskManager.h
@@ -0,0 +1,178 @@
+
+/*
+ * 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 GrClipMaskManager_DEFINED
+#define GrClipMaskManager_DEFINED
+
+#include "GrContext.h"
+#include "GrNoncopyable.h"
+#include "GrRect.h"
+#include "GrReducedClip.h"
+#include "GrStencil.h"
+#include "GrTexture.h"
+
+#include "SkClipStack.h"
+#include "SkDeque.h"
+#include "SkPath.h"
+#include "SkRefCnt.h"
+#include "SkTLList.h"
+
+#include "GrClipMaskCache.h"
+
+class GrGpu;
+class GrPathRenderer;
+class GrPathRendererChain;
+class SkPath;
+class GrTexture;
+class GrDrawState;
+
+/**
+ * The clip mask creator handles the generation of the clip mask. If anti
+ * aliasing is requested it will (in the future) generate a single channel
+ * (8bit) mask. If no anti aliasing is requested it will generate a 1-bit
+ * mask in the stencil buffer. In the non anti-aliasing case, if the clip
+ * mask can be represented as a rectangle then scissoring is used. In all
+ * cases scissoring is used to bound the range of the clip mask.
+ */
+class GrClipMaskManager : public GrNoncopyable {
+public:
+    GrClipMaskManager()
+        : fGpu(NULL)
+        , fCurrClipMaskType(kNone_ClipMaskType) {
+    }
+
+    /**
+     * Creates a clip mask if necessary as a stencil buffer or alpha texture
+     * and sets the GrGpu's scissor and stencil state. If the return is false
+     * then the draw can be skipped.
+     */
+    bool setupClipping(const GrClipData* clipDataIn);
+
+    void releaseResources();
+
+    bool isClipInStencil() const {
+        return kStencil_ClipMaskType == fCurrClipMaskType;
+    }
+    bool isClipInAlpha() const {
+        return kAlpha_ClipMaskType == fCurrClipMaskType;
+    }
+
+    void invalidateStencilMask() {
+        if (kStencil_ClipMaskType == fCurrClipMaskType) {
+            fCurrClipMaskType = kNone_ClipMaskType;
+        }
+    }
+
+    void setContext(GrContext* context) {
+        fAACache.setContext(context);
+    }
+
+    GrContext* getContext() {
+        return fAACache.getContext();
+    }
+
+    void setGpu(GrGpu* gpu) {
+        fGpu = gpu;
+    }
+
+private:
+    /**
+     * Informs the helper function adjustStencilParams() about how the stencil
+     * buffer clip is being used.
+     */
+    enum StencilClipMode {
+        // Draw to the clip bit of the stencil buffer
+        kModifyClip_StencilClipMode,
+        // Clip against the existing representation of the clip in the high bit
+        // of the stencil buffer.
+        kRespectClip_StencilClipMode,
+        // Neither writing to nor clipping against the clip bit.
+        kIgnoreClip_StencilClipMode,
+    };
+
+    GrGpu* fGpu;
+
+    /**
+     * We may represent the clip as a mask in the stencil buffer or as an alpha
+     * texture. It may be neither because the scissor rect suffices or we
+     * haven't yet examined the clip.
+     */
+    enum ClipMaskType {
+        kNone_ClipMaskType,
+        kStencil_ClipMaskType,
+        kAlpha_ClipMaskType,
+    } fCurrClipMaskType;
+
+    GrClipMaskCache fAACache;       // cache for the AA path
+
+    // Draws the clip into the stencil buffer
+    bool createStencilClipMask(GrReducedClip::InitialState initialState,
+                               const GrReducedClip::ElementList& elements,
+                               const SkIRect& clipSpaceIBounds,
+                               const SkIPoint& clipSpaceToStencilOffset);
+    // Creates an alpha mask of the clip. The mask is a rasterization of elements through the
+    // rect specified by clipSpaceIBounds.
+    GrTexture* createAlphaClipMask(int32_t clipStackGenID,
+                                   GrReducedClip::InitialState initialState,
+                                   const GrReducedClip::ElementList& elements,
+                                   const SkIRect& clipSpaceIBounds);
+    // Similar to createAlphaClipMask but it rasterizes in SW and uploads to the result texture.
+    GrTexture* createSoftwareClipMask(int32_t clipStackGenID,
+                                      GrReducedClip::InitialState initialState,
+                                      const GrReducedClip::ElementList& elements,
+                                      const SkIRect& clipSpaceIBounds);
+
+    // Gets a texture to use for the clip mask. If true is returned then a cached mask was found
+    // that already contains the rasterization of the clip stack, otherwise an uninitialized texture
+    // is returned.
+    bool getMaskTexture(int32_t clipStackGenID,
+                        const SkIRect& clipSpaceIBounds,
+                        GrTexture** result);
+
+    bool useSWOnlyPath(const GrReducedClip::ElementList& elements);
+
+    // Draws a clip element into the target alpha mask. The caller should have already setup the
+    // desired blend operation. Optionally if the caller already selected a path renderer it can
+    // be passed. Otherwise the function will select one if the element is a path.
+    bool drawElement(GrTexture* target, const SkClipStack::Element*, GrPathRenderer* = NULL);
+
+    // Determines whether it is possible to draw the element to both the stencil buffer and the
+    // alpha mask simultaneously. If so and the element is a path a compatible path renderer is
+    // also returned.
+    bool canStencilAndDrawElement(GrTexture* target, const SkClipStack::Element*, GrPathRenderer**);
+
+    void mergeMask(GrTexture* dstMask,
+                   GrTexture* srcMask,
+                   SkRegion::Op op,
+                   const GrIRect& dstBound,
+                   const GrIRect& srcBound);
+
+    void getTemp(int width, int height, GrAutoScratchTexture* temp);
+
+    void setupCache(const SkClipStack& clip,
+                    const GrIRect& bounds);
+
+    /**
+     * Called prior to return control back the GrGpu in setupClipping. It
+     * updates the GrGpu with stencil settings that account stencil-based
+     * clipping.
+     */
+    void setGpuStencil();
+
+    /**
+     * Adjusts the stencil settings to account for interaction with stencil
+     * clipping.
+     */
+    void adjustStencilParams(GrStencilSettings* settings,
+                             StencilClipMode mode,
+                             int stencilBitCnt);
+
+    typedef GrNoncopyable INHERITED;
+};
+
+#endif // GrClipMaskManager_DEFINED
diff --git a/src/gpu/GrContext.cpp b/src/gpu/GrContext.cpp
index 2a12399..b729d69 100644
--- a/src/gpu/GrContext.cpp
+++ b/src/gpu/GrContext.cpp
@@ -9,63 +9,110 @@
 
 #include "GrContext.h"
 
+#include "effects/GrConvolutionEffect.h"
+#include "effects/GrSingleTextureEffect.h"
+#include "effects/GrConfigConversionEffect.h"
+
 #include "GrBufferAllocPool.h"
-#include "GrClipIterator.h"
 #include "GrGpu.h"
 #include "GrIndexBuffer.h"
 #include "GrInOrderDrawBuffer.h"
 #include "GrPathRenderer.h"
 #include "GrPathUtils.h"
 #include "GrResourceCache.h"
+#include "GrSoftwarePathRenderer.h"
 #include "GrStencilBuffer.h"
 #include "GrTextStrike.h"
+#include "SkStrokeRec.h"
 #include "SkTLazy.h"
+#include "SkTLS.h"
 #include "SkTrace.h"
 
-#define DEFER_TEXT_RENDERING 1
+SK_DEFINE_INST_COUNT(GrContext)
+SK_DEFINE_INST_COUNT(GrDrawState)
 
-#define BATCH_RECT_TO_RECT (1 && !GR_STATIC_RECT_VB)
+// It can be useful to set this to kNo_BufferedDraw to test whether a bug is caused by using the
+// InOrderDrawBuffer, to compare performance of using/not using InOrderDrawBuffer, or to make
+// debugging easier.
+#define DEFAULT_BUFFERING (GR_DISABLE_DRAW_BUFFERING ? kNo_BufferedDraw : kYes_BufferedDraw)
+
+#define MAX_BLUR_SIGMA 4.0f
 
 // When we're using coverage AA but the blend is incompatible (given gpu
 // limitations) should we disable AA or draw wrong?
 #define DISABLE_COVERAGE_AA_FOR_BLEND 1
 
-static const size_t MAX_TEXTURE_CACHE_COUNT = 256;
-static const size_t MAX_TEXTURE_CACHE_BYTES = 16 * 1024 * 1024;
+#if GR_DEBUG
+    // change this to a 1 to see notifications when partial coverage fails
+    #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
+#else
+    #define GR_DEBUG_PARTIAL_COVERAGE_CHECK 0
+#endif
 
-static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 18;
+static const size_t MAX_TEXTURE_CACHE_COUNT = 2048;
+static const size_t MAX_TEXTURE_CACHE_BYTES = GR_DEFAULT_TEXTURE_CACHE_MB_LIMIT * 1024 * 1024;
+
+static const size_t DRAW_BUFFER_VBPOOL_BUFFER_SIZE = 1 << 15;
 static const int DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS = 4;
 
-// We are currently only batching Text and drawRectToRect, both
-// of which use the quad index buffer.
-static const size_t DRAW_BUFFER_IBPOOL_BUFFER_SIZE = 0;
-static const int DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS = 0;
+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) GrAssert(!(R) || (R)->getContext() == this)
 
-GrContext* GrContext::Create(GrEngine engine,
-                             GrPlatform3DContext context3D) {
+GrContext* GrContext::Create(GrBackend backend, GrBackendContext context) {
     GrContext* ctx = NULL;
-    GrGpu* fGpu = GrGpu::Create(engine, context3D);
+    GrGpu* fGpu = GrGpu::Create(backend, context);
     if (NULL != fGpu) {
-        ctx = new GrContext(fGpu);
+        ctx = SkNEW_ARGS(GrContext, (fGpu));
         fGpu->unref();
     }
     return ctx;
 }
 
+namespace {
+void* CreateThreadInstanceCount() {
+    return SkNEW_ARGS(int, (0));
+}
+void DeleteThreadInstanceCount(void* v) {
+    delete reinterpret_cast<int*>(v);
+}
+#define THREAD_INSTANCE_COUNT                                               \
+    (*reinterpret_cast<int*>(SkTLS::Get(CreateThreadInstanceCount,          \
+                                        DeleteThreadInstanceCount)))
+
+}
+
+int GrContext::GetThreadInstanceCount() {
+    return THREAD_INSTANCE_COUNT;
+}
+
 GrContext::~GrContext() {
+    for (int i = 0; i < fCleanUpData.count(); ++i) {
+        (*fCleanUpData[i].fFunc)(this, fCleanUpData[i].fInfo);
+    }
+
     this->flush();
+
+    // Since the gpu can hold scratch textures, give it a chance to let go
+    // of them before freeing the texture cache
+    fGpu->purgeResources();
+
     delete fTextureCache;
+    fTextureCache = NULL;
     delete fFontCache;
     delete fDrawBuffer;
     delete fDrawBufferVBAllocPool;
     delete fDrawBufferIBAllocPool;
 
-    GrSafeUnref(fAAFillRectIndexBuffer);
-    GrSafeUnref(fAAStrokeRectIndexBuffer);
+    fAARectRenderer->unref();
+
     fGpu->unref();
     GrSafeUnref(fPathRendererChain);
+    GrSafeUnref(fSoftwarePathRenderer);
+    fDrawState->unref();
+
+    --THREAD_INSTANCE_COUNT;
 }
 
 void GrContext::contextLost() {
@@ -81,6 +128,7 @@
     // a path renderer may be holding onto resources that
     // are now unusable
     GrSafeSetNull(fPathRendererChain);
+    GrSafeSetNull(fSoftwarePathRenderer);
 
     delete fDrawBuffer;
     fDrawBuffer = NULL;
@@ -91,10 +139,9 @@
     delete fDrawBufferIBAllocPool;
     fDrawBufferIBAllocPool = NULL;
 
-    GrSafeSetNull(fAAFillRectIndexBuffer);
-    GrSafeSetNull(fAAStrokeRectIndexBuffer);
+    fAARectRenderer->reset();
 
-    fTextureCache->removeAll();
+    fTextureCache->purgeAllUnlocked();
     fFontCache->freeAll();
     fGpu->markContextDirty();
 }
@@ -105,10 +152,16 @@
 
 void GrContext::freeGpuResources() {
     this->flush();
-    fTextureCache->removeAll();
+
+    fGpu->purgeResources();
+
+    fAARectRenderer->reset();
+
+    fTextureCache->purgeAllUnlocked();
     fFontCache->freeAll();
     // a path renderer may be holding onto resources
     GrSafeSetNull(fPathRendererChain);
+    GrSafeSetNull(fSoftwarePathRenderer);
 }
 
 size_t GrContext::getGpuTextureCacheBytes() const {
@@ -117,157 +170,80 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-int GrContext::PaintStageVertexLayoutBits(
-                            const GrPaint& paint,
-                            const bool hasTexCoords[GrPaint::kTotalStages]) {
-    int stageMask = paint.getActiveStageMask();
-    int layout = 0;
-    for (int i = 0; i < GrPaint::kTotalStages; ++i) {
-        if ((1 << i) & stageMask) {
-            if (NULL != hasTexCoords && hasTexCoords[i]) {
-                layout |= GrDrawTarget::StageTexCoordVertexLayoutBit(i, i);
-            } else {
-                layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
-            }
-        }
-    }
-    return layout;
+namespace {
+
+void scale_rect(SkRect* rect, float xScale, float yScale) {
+    rect->fLeft = SkScalarMul(rect->fLeft, SkFloatToScalar(xScale));
+    rect->fTop = SkScalarMul(rect->fTop, SkFloatToScalar(yScale));
+    rect->fRight = SkScalarMul(rect->fRight, SkFloatToScalar(xScale));
+    rect->fBottom = SkScalarMul(rect->fBottom, SkFloatToScalar(yScale));
 }
 
+float adjust_sigma(float sigma, int *scaleFactor, int *radius) {
+    *scaleFactor = 1;
+    while (sigma > MAX_BLUR_SIGMA) {
+        *scaleFactor *= 2;
+        sigma *= 0.5f;
+    }
+    *radius = static_cast<int>(ceilf(sigma * 3.0f));
+    GrAssert(*radius <= GrConvolutionEffect::kMaxKernelRadius);
+    return sigma;
+}
+
+void convolve_gaussian(GrDrawTarget* target,
+                       GrTexture* texture,
+                       const SkRect& rect,
+                       float sigma,
+                       int radius,
+                       Gr1DKernelEffect::Direction direction) {
+    GrRenderTarget* rt = target->drawState()->getRenderTarget();
+    GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kReset_ASRInit);
+    GrDrawState* drawState = target->drawState();
+    drawState->setRenderTarget(rt);
+    SkAutoTUnref<GrEffectRef> conv(GrConvolutionEffect::CreateGaussian(texture,
+                                                                       direction,
+                                                                       radius,
+                                                                       sigma));
+    drawState->setEffect(0, conv);
+    target->drawSimpleRect(rect, NULL);
+}
+
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 
-enum {
-    // flags for textures
-    kNPOTBit            = 0x1,
-    kFilterBit          = 0x2,
-    kScratchBit         = 0x4,
-
-    // resource type
-    kTextureBit         = 0x8,
-    kStencilBufferBit   = 0x10
-};
-
-GrTexture* GrContext::TextureCacheEntry::texture() const {
-    if (NULL == fEntry) {
-        return NULL; 
-    } else {
-        return (GrTexture*) fEntry->resource();
-    }
+GrTexture* GrContext::findAndRefTexture(const GrTextureDesc& desc,
+                                        const GrCacheID& cacheID,
+                                        const GrTextureParams* params) {
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
+    GrResource* resource = fTextureCache->find(resourceKey);
+    SkSafeRef(resource);
+    return static_cast<GrTexture*>(resource);
 }
 
-namespace {
-// returns true if this is a "special" texture because of gpu NPOT limitations
-bool gen_texture_key_values(const GrGpu* gpu,
-                            const GrSamplerState* sampler,
-                            GrContext::TextureKey clientKey,
-                            int width,
-                            int height,
-                            int sampleCnt,
-                            bool scratch,
-                            uint32_t v[4]) {
-    GR_STATIC_ASSERT(sizeof(GrContext::TextureKey) == sizeof(uint64_t));
-    // we assume we only need 16 bits of width and height
-    // assert that texture creation will fail anyway if this assumption
-    // would cause key collisions.
-    GrAssert(gpu->getCaps().fMaxTextureSize <= SK_MaxU16);
-    v[0] = clientKey & 0xffffffffUL;
-    v[1] = (clientKey >> 32) & 0xffffffffUL;
-    v[2] = width | (height << 16);
-
-    v[3] = (sampleCnt << 24);
-    GrAssert(sampleCnt >= 0 && sampleCnt < 256);
-
-    if (!gpu->getCaps().fNPOTTextureTileSupport) {
-        bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
-
-        bool tiled = NULL != sampler &&
-                     ((sampler->getWrapX() != GrSamplerState::kClamp_WrapMode) ||
-                      (sampler->getWrapY() != GrSamplerState::kClamp_WrapMode));
-
-        if (tiled && !isPow2) {
-            v[3] |= kNPOTBit;
-            if (GrSamplerState::kNearest_Filter != sampler->getFilter()) {
-                v[3] |= kFilterBit;
-            }
-        }
-    }
-
-    if (scratch) {
-        v[3] |= kScratchBit;
-    }
-
-    v[3] |= kTextureBit;
-
-    return v[3] & kNPOTBit;
-}
-
-// we should never have more than one stencil buffer with same combo of
-// (width,height,samplecount)
-void gen_stencil_key_values(int width, int height,
-                            int sampleCnt, uint32_t v[4]) {
-    v[0] = width;
-    v[1] = height;
-    v[2] = sampleCnt;
-    v[3] = kStencilBufferBit;
-}
-
-void gen_stencil_key_values(const GrStencilBuffer* sb,
-                            uint32_t v[4]) {
-    gen_stencil_key_values(sb->width(), sb->height(),
-                           sb->numSamples(), v);
-}
-
-}
-
-GrContext::TextureCacheEntry GrContext::findAndLockTexture(
-        TextureKey key,
-        int width,
-        int height,
-        const GrSamplerState* sampler) {
-    uint32_t v[4];
-    gen_texture_key_values(fGpu, sampler, key, width, height, 0, false, v);
-    GrResourceKey resourceKey(v);
-    return TextureCacheEntry(fTextureCache->findAndLock(resourceKey,
-                                            GrResourceCache::kNested_LockType));
-}
-
-bool GrContext::isTextureInCache(TextureKey key,
-                                 int width,
-                                 int height,
-                                 const GrSamplerState* sampler) const {
-    uint32_t v[4];
-    gen_texture_key_values(fGpu, sampler, key, width, height, 0, false, v);
-    GrResourceKey resourceKey(v);
+bool GrContext::isTextureInCache(const GrTextureDesc& desc,
+                                 const GrCacheID& cacheID,
+                                 const GrTextureParams* params) const {
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
     return fTextureCache->hasKey(resourceKey);
 }
 
-GrResourceEntry* GrContext::addAndLockStencilBuffer(GrStencilBuffer* sb) {
+void GrContext::addStencilBuffer(GrStencilBuffer* sb) {
     ASSERT_OWNED_RESOURCE(sb);
-    uint32_t v[4];
-    gen_stencil_key_values(sb, v);
-    GrResourceKey resourceKey(v);
-    return fTextureCache->createAndLock(resourceKey, sb);
+
+    GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(sb->width(),
+                                                            sb->height(),
+                                                            sb->numSamples());
+    fTextureCache->addResource(resourceKey, sb);
 }
 
 GrStencilBuffer* GrContext::findStencilBuffer(int width, int height,
                                               int sampleCnt) {
-    uint32_t v[4];
-    gen_stencil_key_values(width, height, sampleCnt, v);
-    GrResourceKey resourceKey(v);
-    GrResourceEntry* entry = fTextureCache->findAndLock(resourceKey,
-                                            GrResourceCache::kSingle_LockType);
-    if (NULL != entry) {
-        GrStencilBuffer* sb = (GrStencilBuffer*) entry->resource();
-        return sb;
-    } else {
-        return NULL;
-    }
-}
-
-void GrContext::unlockStencilBuffer(GrResourceEntry* sbEntry) {
-    ASSERT_OWNED_RESOURCE(sbEntry->resource());
-    fTextureCache->unlock(sbEntry);
+    GrResourceKey resourceKey = GrStencilBuffer::ComputeKey(width,
+                                                            height,
+                                                            sampleCnt);
+    GrResource* resource = fTextureCache->find(resourceKey);
+    return static_cast<GrStencilBuffer*>(resource);
 }
 
 static void stretchImage(void* dst,
@@ -297,167 +273,142 @@
     }
 }
 
-GrContext::TextureCacheEntry GrContext::createAndLockTexture(
-        TextureKey key,
-        const GrSamplerState* sampler,
-        const GrTextureDesc& desc,
-        void* srcData,
-        size_t rowBytes) {
-    SK_TRACE_EVENT0("GrContext::createAndLockTexture");
+// The desired texture is NPOT and tiled but that isn't supported by
+// the current hardware. Resize the texture to be a POT
+GrTexture* GrContext::createResizedTexture(const GrTextureDesc& desc,
+                                           const GrCacheID& cacheID,
+                                           void* srcData,
+                                           size_t rowBytes,
+                                           bool needsFiltering) {
+    SkAutoTUnref<GrTexture> clampedTexture(this->findAndRefTexture(desc, cacheID, NULL));
+    if (NULL == clampedTexture) {
+        clampedTexture.reset(this->createTexture(NULL, desc, cacheID, srcData, rowBytes));
 
-#if GR_DUMP_TEXTURE_UPLOAD
-    GrPrintf("GrContext::createAndLockTexture [%d %d]\n", desc.fWidth, desc.fHeight);
-#endif
-
-    TextureCacheEntry entry;
-    uint32_t v[4];
-    bool special = gen_texture_key_values(fGpu, sampler, key,
-                                          desc.fWidth, desc.fHeight,
-                                          desc.fSampleCnt, false, v);
-    GrResourceKey resourceKey(v);
-
-    if (special) {
-        GrAssert(NULL != sampler);
-        TextureCacheEntry clampEntry = this->findAndLockTexture(key,
-                                                                desc.fWidth,
-                                                                desc.fHeight,
-                                                                NULL);
-
-        if (NULL == clampEntry.texture()) {
-            clampEntry = this->createAndLockTexture(key, NULL, desc,
-                                                    srcData, rowBytes);
-            GrAssert(NULL != clampEntry.texture());
-            if (NULL == clampEntry.texture()) {
-                return entry;
-            }
-        }
-        GrTextureDesc rtDesc = desc;
-        rtDesc.fFlags =  rtDesc.fFlags |
-                         kRenderTarget_GrTextureFlagBit |
-                         kNoStencil_GrTextureFlagBit;
-        rtDesc.fWidth  = GrNextPow2(GrMax(desc.fWidth, 64));
-        rtDesc.fHeight = GrNextPow2(GrMax(desc.fHeight, 64));
-
-        GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);
-
-        if (NULL != texture) {
-            GrDrawTarget::AutoStateRestore asr(fGpu);
-            GrDrawState* drawState = fGpu->drawState();
-            drawState->reset();
-            drawState->setRenderTarget(texture->asRenderTarget());
-            drawState->setTexture(0, clampEntry.texture());
-
-            GrSamplerState::Filter filter;
-            // if filtering is not desired then we want to ensure all
-            // texels in the resampled image are copies of texels from
-            // the original.
-            if (GrSamplerState::kNearest_Filter == sampler->getFilter()) {
-                filter = GrSamplerState::kNearest_Filter;
-            } else {
-                filter = GrSamplerState::kBilinear_Filter;
-            }
-            drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
-                                         filter);
-
-            static const GrVertexLayout layout =
-                                GrDrawTarget::StageTexCoordVertexLayoutBit(0,0);
-            GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);
-
-            if (arg.succeeded()) {
-                GrPoint* verts = (GrPoint*) arg.vertices();
-                verts[0].setIRectFan(0, 0,
-                                     texture->width(),
-                                     texture->height(),
-                                     2*sizeof(GrPoint));
-                verts[1].setIRectFan(0, 0, 1, 1, 2*sizeof(GrPoint));
-                fGpu->drawNonIndexed(kTriangleFan_PrimitiveType,
-                                     0, 4);
-                entry.set(fTextureCache->createAndLock(resourceKey, texture));
-            }
-            texture->releaseRenderTarget();
-        } else {
-            // TODO: Our CPU stretch doesn't filter. But we create separate
-            // stretched textures when the sampler state is either filtered or
-            // not. Either implement filtered stretch blit on CPU or just create
-            // one when FBO case fails.
-
-            rtDesc.fFlags = kNone_GrTextureFlags;
-            // no longer need to clamp at min RT size.
-            rtDesc.fWidth  = GrNextPow2(desc.fWidth);
-            rtDesc.fHeight = GrNextPow2(desc.fHeight);
-            int bpp = GrBytesPerPixel(desc.fConfig);
-            SkAutoSMalloc<128*128*4> stretchedPixels(bpp *
-                                                     rtDesc.fWidth *
-                                                     rtDesc.fHeight);
-            stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight,
-                         srcData, desc.fWidth, desc.fHeight, bpp);
-
-            size_t stretchedRowBytes = rtDesc.fWidth * bpp;
-
-            GrTexture* texture = fGpu->createTexture(rtDesc,
-                                                     stretchedPixels.get(),
-                                                     stretchedRowBytes);
-            GrAssert(NULL != texture);
-            entry.set(fTextureCache->createAndLock(resourceKey, texture));
-        }
-        fTextureCache->unlock(clampEntry.cacheEntry());
-
-    } else {
-        GrTexture* texture = fGpu->createTexture(desc, srcData, rowBytes);
-        if (NULL != texture) {
-            entry.set(fTextureCache->createAndLock(resourceKey, texture));
+        if (NULL == clampedTexture) {
+            return NULL;
         }
     }
-    return entry;
+
+    GrTextureDesc rtDesc = desc;
+    rtDesc.fFlags =  rtDesc.fFlags |
+                     kRenderTarget_GrTextureFlagBit |
+                     kNoStencil_GrTextureFlagBit;
+    rtDesc.fWidth  = GrNextPow2(GrMax(desc.fWidth, 64));
+    rtDesc.fHeight = GrNextPow2(GrMax(desc.fHeight, 64));
+
+    GrTexture* texture = fGpu->createTexture(rtDesc, NULL, 0);
+
+    if (NULL != texture) {
+        GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+        GrDrawState* drawState = fGpu->drawState();
+        drawState->setRenderTarget(texture->asRenderTarget());
+
+        // if filtering is not desired then we want to ensure all
+        // texels in the resampled image are copies of texels from
+        // the original.
+        GrTextureParams params(SkShader::kClamp_TileMode, needsFiltering);
+        drawState->createTextureEffect(0, clampedTexture, SkMatrix::I(), params);
+
+        static const GrVertexLayout layout = GrDrawState::StageTexCoordVertexLayoutBit(0,0);
+        GrDrawTarget::AutoReleaseGeometry arg(fGpu, layout, 4, 0);
+
+        if (arg.succeeded()) {
+            GrPoint* verts = (GrPoint*) arg.vertices();
+            verts[0].setIRectFan(0, 0, texture->width(), texture->height(), 2 * sizeof(GrPoint));
+            verts[1].setIRectFan(0, 0, 1, 1, 2 * sizeof(GrPoint));
+            fGpu->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
+        }
+        texture->releaseRenderTarget();
+    } else {
+        // TODO: Our CPU stretch doesn't filter. But we create separate
+        // stretched textures when the texture params is either filtered or
+        // not. Either implement filtered stretch blit on CPU or just create
+        // one when FBO case fails.
+
+        rtDesc.fFlags = kNone_GrTextureFlags;
+        // no longer need to clamp at min RT size.
+        rtDesc.fWidth  = GrNextPow2(desc.fWidth);
+        rtDesc.fHeight = GrNextPow2(desc.fHeight);
+        int bpp = GrBytesPerPixel(desc.fConfig);
+        SkAutoSMalloc<128*128*4> stretchedPixels(bpp * rtDesc.fWidth * rtDesc.fHeight);
+        stretchImage(stretchedPixels.get(), rtDesc.fWidth, rtDesc.fHeight,
+                     srcData, desc.fWidth, desc.fHeight, bpp);
+
+        size_t stretchedRowBytes = rtDesc.fWidth * bpp;
+
+        SkDEBUGCODE(GrTexture* texture = )fGpu->createTexture(rtDesc, stretchedPixels.get(), stretchedRowBytes);
+        GrAssert(NULL != texture);
+    }
+
+    return texture;
 }
 
-namespace {
-inline void gen_scratch_tex_key_values(const GrGpu* gpu, 
-                                       const GrTextureDesc& desc,
-                                       uint32_t v[4]) {
-    // Instead of a client-provided key of the texture contents
-    // we create a key of from the descriptor.
-    GrContext::TextureKey descKey = (desc.fFlags << 8) |
-                                    ((uint64_t) desc.fConfig << 32);
-    // this code path isn't friendly to tiling with NPOT restricitons
-    // We just pass ClampNoFilter()
-    gen_texture_key_values(gpu, NULL, descKey, desc.fWidth,
-                           desc.fHeight, desc.fSampleCnt, true, v);
-}
+GrTexture* GrContext::createTexture(const GrTextureParams* params,
+                                    const GrTextureDesc& desc,
+                                    const GrCacheID& cacheID,
+                                    void* srcData,
+                                    size_t rowBytes) {
+    SK_TRACE_EVENT0("GrContext::createTexture");
+
+#if GR_DUMP_TEXTURE_UPLOAD
+    GrPrintf("GrContext::createTexture[%d %d]\n", desc.fWidth, desc.fHeight);
+#endif
+
+    GrResourceKey resourceKey = GrTexture::ComputeKey(fGpu, params, desc, cacheID);
+
+    GrTexture* texture;
+    if (GrTexture::NeedsResizing(resourceKey)) {
+        texture = this->createResizedTexture(desc, cacheID,
+                                             srcData, rowBytes,
+                                             GrTexture::NeedsFiltering(resourceKey));
+    } else {
+        texture= fGpu->createTexture(desc, srcData, rowBytes);
+    }
+
+    if (NULL != texture) {
+        fTextureCache->addResource(resourceKey, texture);
+    }
+
+    return texture;
 }
 
-GrContext::TextureCacheEntry GrContext::lockScratchTexture(
-                                                const GrTextureDesc& inDesc,
-                                                ScratchTexMatch match) {
-
+GrTexture* GrContext::lockAndRefScratchTexture(const GrTextureDesc& inDesc, ScratchTexMatch match) {
     GrTextureDesc desc = inDesc;
-    if (kExact_ScratchTexMatch != match) {
+
+    GrAssert((desc.fFlags & kRenderTarget_GrTextureFlagBit) ||
+             !(desc.fFlags & kNoStencil_GrTextureFlagBit));
+
+    if (kApprox_ScratchTexMatch == match) {
         // bin by pow2 with a reasonable min
         static const int MIN_SIZE = 256;
         desc.fWidth  = GrMax(MIN_SIZE, GrNextPow2(desc.fWidth));
         desc.fHeight = GrMax(MIN_SIZE, GrNextPow2(desc.fHeight));
     }
 
-    GrResourceEntry* entry;
+    GrResource* resource = NULL;
     int origWidth = desc.fWidth;
     int origHeight = desc.fHeight;
     bool doubledW = false;
     bool doubledH = false;
 
     do {
-        uint32_t v[4];
-        gen_scratch_tex_key_values(fGpu, desc, v);
-        GrResourceKey key(v);
-        entry = fTextureCache->findAndLock(key,
-                                           GrResourceCache::kNested_LockType);
-        // if we miss, relax the fit of the flags...
-        // then try doubling width... then height.
-        if (NULL != entry || kExact_ScratchTexMatch == match) {
+        GrResourceKey key = GrTexture::ComputeScratchKey(desc);
+        // Ensure we have exclusive access to the texture so future 'find' calls don't return it
+        resource = fTextureCache->find(key, GrResourceCache::kHide_OwnershipFlag);
+        if (NULL != resource) {
+            resource->ref();
             break;
         }
-        if (!(desc.fFlags & kRenderTarget_GrTextureFlagBit)) {
-            desc.fFlags = desc.fFlags | kRenderTarget_GrTextureFlagBit;
-        } else if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
+        if (kExact_ScratchTexMatch == match) {
+            break;
+        }
+        // We had a cache miss and we are in approx mode, relax the fit of the flags... then try
+        // doubling width... then the height.
+
+        // We no longer try to reuse textures that were previously used as render targets in
+        // situations where no RT is needed; doing otherwise can confuse the video driver and
+        // cause significant performance problems in some cases.
+        if (desc.fFlags & kNoStencil_GrTextureFlagBit) {
             desc.fFlags = desc.fFlags & ~kNoStencil_GrTextureFlagBit;
         } else if (!doubledW) {
             desc.fFlags = inDesc.fFlags;
@@ -471,47 +422,72 @@
         } else {
             break;
         }
-        
+
     } while (true);
 
-    if (NULL == entry) {
+    if (NULL == resource) {
         desc.fFlags = inDesc.fFlags;
         desc.fWidth = origWidth;
         desc.fHeight = origHeight;
         GrTexture* texture = fGpu->createTexture(desc, NULL, 0);
         if (NULL != texture) {
-            uint32_t v[4];
-            gen_scratch_tex_key_values(fGpu, desc, v);
-            GrResourceKey key(v);
-            entry = fTextureCache->createAndLock(key, texture);
+            GrResourceKey key = GrTexture::ComputeScratchKey(texture->desc());
+            // Make the resource exclusive so future 'find' calls don't return it
+            fTextureCache->addResource(key, texture, GrResourceCache::kHide_OwnershipFlag);
+            resource = texture;
         }
     }
 
-    // If the caller gives us the same desc/sampler twice we don't want
-    // to return the same texture the second time (unless it was previously
-    // released). So we detach the entry from the cache and reattach at release.
-    if (NULL != entry) {
-        fTextureCache->detach(entry);
-    }
-    return TextureCacheEntry(entry);
+    return static_cast<GrTexture*>(resource);
 }
 
-void GrContext::unlockTexture(TextureCacheEntry entry) {
-    ASSERT_OWNED_RESOURCE(entry.texture());
+void GrContext::addExistingTextureToCache(GrTexture* texture) {
+
+    if (NULL == texture) {
+        return;
+    }
+
+    // This texture should already have a cache entry since it was once
+    // attached
+    GrAssert(NULL != texture->getCacheEntry());
+
+    // Conceptually, the cache entry is going to assume responsibility
+    // for the creation ref.
+    GrAssert(1 == texture->getRefCnt());
+
+    // Since this texture came from an AutoScratchTexture it should
+    // still be in the exclusive pile
+    fTextureCache->makeNonExclusive(texture->getCacheEntry());
+
+    this->purgeCache();
+}
+
+
+void GrContext::unlockScratchTexture(GrTexture* texture) {
+    ASSERT_OWNED_RESOURCE(texture);
+    GrAssert(NULL != texture->getCacheEntry());
+
     // If this is a scratch texture we detached it from the cache
     // while it was locked (to avoid two callers simultaneously getting
     // the same texture).
-    if (kScratchBit & entry.cacheEntry()->key().getValue32(3)) {
-        fTextureCache->reattachAndUnlock(entry.cacheEntry());
-    } else {
-        fTextureCache->unlock(entry.cacheEntry());
+    if (texture->getCacheEntry()->key().isScratch()) {
+        fTextureCache->makeNonExclusive(texture->getCacheEntry());
+    }
+
+    this->purgeCache();
+}
+
+void GrContext::purgeCache() {
+    if (NULL != fTextureCache) {
+        fTextureCache->purgeAsNeeded();
     }
 }
 
-GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& desc,
+GrTexture* GrContext::createUncachedTexture(const GrTextureDesc& descIn,
                                             void* srcData,
                                             size_t rowBytes) {
-    return fGpu->createTexture(desc, srcData, rowBytes);
+    GrTextureDesc descCopy = descIn;
+    return fGpu->createTexture(descCopy, srcData, rowBytes);
 }
 
 void GrContext::getTextureCacheLimits(int* maxTextures,
@@ -524,39 +500,37 @@
 }
 
 int GrContext::getMaxTextureSize() const {
-    return fGpu->getCaps().fMaxTextureSize;
+    return fGpu->getCaps().maxTextureSize();
 }
 
 int GrContext::getMaxRenderTargetSize() const {
-    return fGpu->getCaps().fMaxRenderTargetSize;
+    return fGpu->getCaps().maxRenderTargetSize();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-GrTexture* GrContext::createPlatformTexture(const GrPlatformTextureDesc& desc) {
-    return fGpu->createPlatformTexture(desc);
+GrTexture* GrContext::wrapBackendTexture(const GrBackendTextureDesc& desc) {
+    return fGpu->wrapBackendTexture(desc);
 }
 
-GrRenderTarget* GrContext::createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
-    return fGpu->createPlatformRenderTarget(desc);
+GrRenderTarget* GrContext::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
+    return fGpu->wrapBackendRenderTarget(desc);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool GrContext::supportsIndex8PixelConfig(const GrSamplerState* sampler,
+bool GrContext::supportsIndex8PixelConfig(const GrTextureParams* params,
                                           int width, int height) const {
     const GrDrawTarget::Caps& caps = fGpu->getCaps();
-    if (!caps.f8BitPaletteSupport) {
+    if (!caps.eightBitPaletteSupport()) {
         return false;
     }
 
     bool isPow2 = GrIsPow2(width) && GrIsPow2(height);
 
     if (!isPow2) {
-        bool tiled = NULL != sampler &&
-                     (sampler->getWrapX() != GrSamplerState::kClamp_WrapMode ||
-                      sampler->getWrapY() != GrSamplerState::kClamp_WrapMode);
-        if (tiled && !caps.fNPOTTextureTileSupport) {
+        bool tiled = NULL != params && params->isTiled();
+        if (tiled && !caps.npotTextureTileSupport()) {
             return false;
         }
     }
@@ -565,70 +539,56 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-const GrClip& GrContext::getClip() const { return fGpu->getClip(); }
-
-void GrContext::setClip(const GrClip& clip) {
-    fGpu->setClip(clip);
-    fGpu->drawState()->enableState(GrDrawState::kClip_StateBit);
+const GrClipData* GrContext::getClip() const {
+    return fGpu->getClip();
 }
 
-void GrContext::setClip(const GrIRect& rect) {
-    GrClip clip;
-    clip.setFromIRect(rect);
-    fGpu->setClip(clip);
+void GrContext::setClip(const GrClipData* clipData) {
+    fGpu->setClip(clipData);
+
+    fDrawState->setState(GrDrawState::kClip_StateBit,
+                         clipData && clipData->fClipStack && !clipData->fClipStack->isWideOpen());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrContext::clear(const GrIRect* rect, const GrColor color) {
-    this->flush();
-    fGpu->clear(rect, color);
+void GrContext::clear(const GrIRect* rect,
+                      const GrColor color,
+                      GrRenderTarget* target) {
+    this->prepareToDraw(NULL, DEFAULT_BUFFERING)->clear(rect, color, target);
 }
 
-void GrContext::drawPaint(const GrPaint& paint) {
+void GrContext::drawPaint(const GrPaint& origPaint) {
     // set rect to be big enough to fill the space, but not super-huge, so we
     // don't overflow fixed-point implementations
     GrRect r;
     r.setLTRB(0, 0,
-              GrIntToScalar(getRenderTarget()->width()),
-              GrIntToScalar(getRenderTarget()->height()));
-    GrMatrix inverse;
-    SkTLazy<GrPaint> tmpPaint;
-    const GrPaint* p = &paint;
-    GrDrawState* drawState = fGpu->drawState();
-    GrAutoMatrix am;
+              SkIntToScalar(getRenderTarget()->width()),
+              SkIntToScalar(getRenderTarget()->height()));
+    SkMatrix inverse;
+    SkTCopyOnFirstWrite<GrPaint> paint(origPaint);
+    AutoMatrix am;
 
     // We attempt to map r by the inverse matrix and draw that. mapRect will
     // map the four corners and bound them with a new rect. This will not
     // produce a correct result for some perspective matrices.
     if (!this->getMatrix().hasPerspective()) {
-        if (!drawState->getViewInverse(&inverse)) {
-            GrPrintf("Could not invert matrix");
+        if (!fDrawState->getViewInverse(&inverse)) {
+            GrPrintf("Could not invert matrix\n");
             return;
         }
         inverse.mapRect(&r);
     } else {
-        if (paint.getActiveMaskStageMask() || paint.getActiveStageMask()) {
-            if (!drawState->getViewInverse(&inverse)) {
-                GrPrintf("Could not invert matrix");
-                return;
-            }
-            tmpPaint.set(paint);
-            tmpPaint.get()->preConcatActiveSamplerMatrices(inverse);
-            p = tmpPaint.get();
+        if (!am.setIdentity(this, paint.writable())) {
+            GrPrintf("Could not invert matrix\n");
+            return;
         }
-        am.set(this, GrMatrix::I());
     }
     // by definition this fills the entire clip, no need for AA
-    if (paint.fAntiAlias) {
-        if (!tmpPaint.isValid()) {
-            tmpPaint.set(paint);
-            p = tmpPaint.get();
-        }
-        GrAssert(p == tmpPaint.get());
-        tmpPaint.get()->fAntiAlias = false;
+    if (paint->isAntiAlias()) {
+        paint.writable()->setAntiAlias(false);
     }
-    this->drawRect(*p, r);
+    this->drawRect(*paint, r);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -647,8 +607,8 @@
  would be faster.
  */
 static void setStrokeRectStrip(GrPoint verts[10], GrRect rect,
-                               GrScalar width) {
-    const GrScalar rad = GrScalarHalf(width);
+                               SkScalar width) {
+    const SkScalar rad = SkScalarHalf(width);
     rect.sort();
 
     verts[0].set(rect.fLeft + rad, rect.fTop + rad);
@@ -663,232 +623,23 @@
     verts[9] = verts[1];
 }
 
-static void setInsetFan(GrPoint* pts, size_t stride,
-                        const GrRect& r, GrScalar dx, GrScalar dy) {
-    pts->setRectFan(r.fLeft + dx, r.fTop + dy, 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,
-};
-
-int GrContext::aaFillRectIndexCount() const {
-    return GR_ARRAY_COUNT(gFillAARectIdx);
-}
-
-GrIndexBuffer* GrContext::aaFillRectIndexBuffer() {
-    if (NULL == fAAFillRectIndexBuffer) {
-        fAAFillRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gFillAARectIdx),
-                                                         false);
-        if (NULL != fAAFillRectIndexBuffer) {
-    #if GR_DEBUG
-            bool updated =
-    #endif
-            fAAFillRectIndexBuffer->updateData(gFillAARectIdx,
-                                               sizeof(gFillAARectIdx));
-            GR_DEBUGASSERT(updated);
-        }
-    }
-    return fAAFillRectIndexBuffer;
-}
-
-static const uint16_t gStrokeAARectIdx[] = {
-    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,
-};
-
-int GrContext::aaStrokeRectIndexCount() const {
-    return GR_ARRAY_COUNT(gStrokeAARectIdx);
-}
-
-GrIndexBuffer* GrContext::aaStrokeRectIndexBuffer() {
-    if (NULL == fAAStrokeRectIndexBuffer) {
-        fAAStrokeRectIndexBuffer = fGpu->createIndexBuffer(sizeof(gStrokeAARectIdx),
-                                                           false);
-        if (NULL != fAAStrokeRectIndexBuffer) {
-    #if GR_DEBUG
-            bool updated =
-    #endif
-            fAAStrokeRectIndexBuffer->updateData(gStrokeAARectIdx,
-                                                 sizeof(gStrokeAARectIdx));
-            GR_DEBUGASSERT(updated);
-        }
-    }
-    return fAAStrokeRectIndexBuffer;
-}
-
-static GrVertexLayout aa_rect_layout(const GrDrawTarget* target,
-                                     bool useCoverage) {
-    GrVertexLayout layout = 0;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (NULL != target->getDrawState().getTexture(s)) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
-        }
-    }
-    if (useCoverage) {
-        layout |= GrDrawTarget::kCoverage_VertexLayoutBit;
-    } else {
-        layout |= GrDrawTarget::kColor_VertexLayoutBit;
-    }
-    return layout;
-}
-
-void GrContext::fillAARect(GrDrawTarget* target,
-                           const GrRect& devRect,
-                           bool useVertexCoverage) {
-    GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage);
-
-    size_t vsize = GrDrawTarget::VertexSize(layout);
-
-    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 8, 0);
-    if (!geo.succeeded()) {
-        GrPrintf("Failed to get space for vertices!\n");
-        return;
-    }
-    GrIndexBuffer* indexBuffer = this->aaFillRectIndexBuffer();
-    if (NULL == indexBuffer) {
-        GrPrintf("Failed to create index buffer!\n");
-        return;
-    }
-
-    intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
-
-    GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
-    GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
-
-    setInsetFan(fan0Pos, vsize, devRect, -GR_ScalarHalf, -GR_ScalarHalf);
-    setInsetFan(fan1Pos, vsize, devRect,  GR_ScalarHalf,  GR_ScalarHalf);
-
-    verts += sizeof(GrPoint);
-    for (int i = 0; i < 4; ++i) {
-        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
-    }
-
-    GrColor innerColor;
-    if (useVertexCoverage) {
-        innerColor = 0xffffffff;
-    } else {
-        innerColor = target->getDrawState().getColor();
-    }
-
-    verts += 4 * vsize;
-    for (int i = 0; i < 4; ++i) {
-        *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
-    }
-
-    target->setIndexSourceToBuffer(indexBuffer);
-
-    target->drawIndexed(kTriangles_PrimitiveType, 0,
-                         0, 8, this->aaFillRectIndexCount());
-}
-
-void GrContext::strokeAARect(GrDrawTarget* target,
-                             const GrRect& devRect,
-                             const GrVec& devStrokeSize,
-                             bool useVertexCoverage) {
-    const GrScalar& dx = devStrokeSize.fX;
-    const GrScalar& dy = devStrokeSize.fY;
-    const GrScalar rx = GrMul(dx, GR_ScalarHalf);
-    const GrScalar ry = GrMul(dy, GR_ScalarHalf);
-
-    GrScalar spare;
-    {
-        GrScalar w = devRect.width() - dx;
-        GrScalar h = devRect.height() - dy;
-        spare = GrMin(w, h);
-    }
-
-    if (spare <= 0) {
-        GrRect r(devRect);
-        r.inset(-rx, -ry);
-        fillAARect(target, r, useVertexCoverage);
-        return;
-    }
-    GrVertexLayout layout = aa_rect_layout(target, useVertexCoverage);
-    size_t vsize = GrDrawTarget::VertexSize(layout);
-
-    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 16, 0);
-    if (!geo.succeeded()) {
-        GrPrintf("Failed to get space for vertices!\n");
-        return;
-    }
-    GrIndexBuffer* indexBuffer = this->aaStrokeRectIndexBuffer();
-    if (NULL == indexBuffer) {
-        GrPrintf("Failed to create index buffer!\n");
-        return;
-    }
-
-    intptr_t verts = reinterpret_cast<intptr_t>(geo.vertices());
-
-    GrPoint* fan0Pos = reinterpret_cast<GrPoint*>(verts);
-    GrPoint* fan1Pos = reinterpret_cast<GrPoint*>(verts + 4 * vsize);
-    GrPoint* fan2Pos = reinterpret_cast<GrPoint*>(verts + 8 * vsize);
-    GrPoint* fan3Pos = reinterpret_cast<GrPoint*>(verts + 12 * vsize);
-
-    setInsetFan(fan0Pos, vsize, devRect, -rx - GR_ScalarHalf, -ry - GR_ScalarHalf);
-    setInsetFan(fan1Pos, vsize, devRect, -rx + GR_ScalarHalf, -ry + GR_ScalarHalf);
-    setInsetFan(fan2Pos, vsize, devRect,  rx - GR_ScalarHalf,  ry - GR_ScalarHalf);
-    setInsetFan(fan3Pos, vsize, devRect,  rx + GR_ScalarHalf,  ry + GR_ScalarHalf);
-
-    verts += sizeof(GrPoint);
-    for (int i = 0; i < 4; ++i) {
-        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
-    }
-
-    GrColor innerColor;
-    if (useVertexCoverage) {
-        innerColor = 0xffffffff;
-    } else {
-        innerColor = target->getDrawState().getColor();
-    }
-    verts += 4 * vsize;
-    for (int i = 0; i < 8; ++i) {
-        *reinterpret_cast<GrColor*>(verts + i * vsize) = innerColor;
-    }
-
-    verts += 8 * vsize;
-    for (int i = 0; i < 8; ++i) {
-        *reinterpret_cast<GrColor*>(verts + i * vsize) = 0;
-    }
-
-    target->setIndexSourceToBuffer(indexBuffer);
-    target->drawIndexed(kTriangles_PrimitiveType,
-                        0, 0, 16, aaStrokeRectIndexCount());
-}
-
 /**
  * Returns true if the rects edges are integer-aligned.
  */
 static bool isIRect(const GrRect& r) {
-    return GrScalarIsInt(r.fLeft) && GrScalarIsInt(r.fTop) && 
-           GrScalarIsInt(r.fRight) && GrScalarIsInt(r.fBottom);
+    return SkScalarIsInt(r.fLeft) && SkScalarIsInt(r.fTop) &&
+           SkScalarIsInt(r.fRight) && SkScalarIsInt(r.fBottom);
 }
 
 static bool apply_aa_to_rect(GrDrawTarget* target,
                              const GrRect& rect,
-                             GrScalar width, 
-                             const GrMatrix* matrix,
-                             GrMatrix* combinedMatrix,
+                             SkScalar width,
+                             const SkMatrix* matrix,
+                             SkMatrix* combinedMatrix,
                              GrRect* devRect,
                              bool* useVertexCoverage) {
     // we use a simple coverage ramp to do aa on axis-aligned rects
-    // we check if the rect will be axis-aligned, and the rect won't land on 
+    // we check if the rect will be axis-aligned, and the rect won't land on
     // integer coords.
 
     // we are keeping around the "tweak the alpha" trick because
@@ -919,7 +670,7 @@
         return false;
     }
 
-    if (NULL != matrix && 
+    if (NULL != matrix &&
         !matrix->preservesAxisAlignment()) {
         return false;
     }
@@ -929,7 +680,7 @@
         combinedMatrix->preConcat(*matrix);
         GrAssert(combinedMatrix->preservesAxisAlignment());
     }
-    
+
     combinedMatrix->mapRect(devRect, rect);
     devRect->sort();
 
@@ -942,36 +693,41 @@
 
 void GrContext::drawRect(const GrPaint& paint,
                          const GrRect& rect,
-                         GrScalar width,
-                         const GrMatrix* matrix) {
+                         SkScalar width,
+                         const SkMatrix* matrix) {
     SK_TRACE_EVENT0("GrContext::drawRect");
 
-    GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
-    int stageMask = paint.getActiveStageMask();
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+    GrDrawState::AutoStageDisable atr(fDrawState);
 
     GrRect devRect = rect;
-    GrMatrix combinedMatrix;
+    SkMatrix combinedMatrix;
     bool useVertexCoverage;
-    bool needAA = paint.fAntiAlias &&
+    bool needAA = paint.isAntiAlias() &&
                   !this->getRenderTarget()->isMultisampled();
     bool doAA = needAA && apply_aa_to_rect(target, rect, width, matrix,
                                            &combinedMatrix, &devRect,
                                            &useVertexCoverage);
 
     if (doAA) {
-        GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
+        GrDrawState::AutoDeviceCoordDraw adcd(target->drawState());
+        if (!adcd.succeeded()) {
+            return;
+        }
         if (width >= 0) {
-            GrVec strokeSize;;
+            GrVec strokeSize;
             if (width > 0) {
                 strokeSize.set(width, width);
                 combinedMatrix.mapVectors(&strokeSize, 1);
                 strokeSize.setAbs(strokeSize);
             } else {
-                strokeSize.set(GR_Scalar1, GR_Scalar1);
+                strokeSize.set(SK_Scalar1, SK_Scalar1);
             }
-            strokeAARect(target, devRect, strokeSize, useVertexCoverage);
+            fAARectRenderer->strokeAARect(this->getGpu(), target, devRect,
+                                         strokeSize, useVertexCoverage);
         } else {
-            fillAARect(target, devRect, useVertexCoverage);
+            fAARectRenderer->fillAARect(this->getGpu(), target,
+                                       devRect, useVertexCoverage);
         }
         return;
     }
@@ -980,10 +736,9 @@
         // TODO: consider making static vertex buffers for these cases.
         // Hairline could be done by just adding closing vertex to
         // unitSquareVertexBuffer()
-        GrVertexLayout layout =  PaintStageVertexLayoutBits(paint, NULL);
 
         static const int worstCaseVertCount = 10;
-        GrDrawTarget::AutoReleaseGeometry geo(target, layout, worstCaseVertCount, 0);
+        GrDrawTarget::AutoReleaseGeometry geo(target, 0, worstCaseVertCount, 0);
 
         if (!geo.succeeded()) {
             GrPrintf("Failed to get space for vertices!\n");
@@ -996,12 +751,12 @@
 
         if (width > 0) {
             vertCount = 10;
-            primType = kTriangleStrip_PrimitiveType;
+            primType = kTriangleStrip_GrPrimitiveType;
             setStrokeRectStrip(vertex, rect, width);
         } else {
             // hairline
             vertCount = 5;
-            primType = kLineStrip_PrimitiveType;
+            primType = kLineStrip_GrPrimitiveType;
             vertex[0].set(rect.fLeft, rect.fTop);
             vertex[1].set(rect.fRight, rect.fTop);
             vertex[2].set(rect.fRight, rect.fBottom);
@@ -1012,37 +767,32 @@
         GrDrawState::AutoViewMatrixRestore avmr;
         if (NULL != matrix) {
             GrDrawState* drawState = target->drawState();
-            avmr.set(drawState);
-            drawState->preConcatViewMatrix(*matrix);
-            drawState->preConcatSamplerMatrices(stageMask, *matrix);
+            avmr.set(drawState, *matrix);
         }
 
         target->drawNonIndexed(primType, 0, vertCount);
     } else {
 #if GR_STATIC_RECT_VB
-            GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
             const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
             if (NULL == sqVB) {
                 GrPrintf("Failed to create static rect vb.\n");
                 return;
             }
-            target->setVertexSourceToBuffer(layout, sqVB);
+            target->setVertexSourceToBuffer(0, sqVB);
             GrDrawState* drawState = target->drawState();
-            GrDrawState::AutoViewMatrixRestore avmr(drawState);
-            GrMatrix m;
+            SkMatrix m;
             m.setAll(rect.width(),    0,             rect.fLeft,
                         0,            rect.height(), rect.fTop,
-                        0,            0,             GrMatrix::I()[8]);
+                        0,            0,             SkMatrix::I()[8]);
 
             if (NULL != matrix) {
                 m.postConcat(*matrix);
             }
-            drawState->preConcatViewMatrix(m);
-            drawState->preConcatSamplerMatrices(stageMask, m);
- 
-            target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
+            GrDrawState::AutoViewMatrixRestore avmr(drawState, m);
+
+            target->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
 #else
-            target->drawSimpleRect(rect, matrix, stageMask);
+            target->drawSimpleRect(rect, matrix);
 #endif
     }
 }
@@ -1050,71 +800,61 @@
 void GrContext::drawRectToRect(const GrPaint& paint,
                                const GrRect& dstRect,
                                const GrRect& srcRect,
-                               const GrMatrix* dstMatrix,
-                               const GrMatrix* srcMatrix) {
+                               const SkMatrix* dstMatrix,
+                               const SkMatrix* srcMatrix) {
     SK_TRACE_EVENT0("GrContext::drawRectToRect");
 
-    // srcRect refers to paint's first texture
-    if (NULL == paint.getTexture(0)) {
+    // srcRect refers to paint's first color stage
+    if (!paint.isColorStageEnabled(0)) {
         drawRect(paint, dstRect, -1, dstMatrix);
         return;
     }
 
-    GR_STATIC_ASSERT(!BATCH_RECT_TO_RECT || !GR_STATIC_RECT_VB);
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
 
 #if GR_STATIC_RECT_VB
-    GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+    GrDrawState::AutoStageDisable atr(fDrawState);
     GrDrawState* drawState = target->drawState();
-    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, NULL);
-    GrDrawState::AutoViewMatrixRestore avmr(drawState);
 
-    GrMatrix m;
+    SkMatrix m;
 
     m.setAll(dstRect.width(), 0,                dstRect.fLeft,
              0,               dstRect.height(), dstRect.fTop,
-             0,               0,                GrMatrix::I()[8]);
+             0,               0,                SkMatrix::I()[8]);
     if (NULL != dstMatrix) {
         m.postConcat(*dstMatrix);
     }
-    drawState->preConcatViewMatrix(m);
 
-    // srcRect refers to first stage
-    int otherStageMask = paint.getActiveStageMask() & 
-                         (~(1 << GrPaint::kFirstTextureStage));
-    if (otherStageMask) {
-        drawState->preConcatSamplerMatrices(otherStageMask, m);
-    }
+    // The first color stage's coords come from srcRect rather than applying a matrix to dstRect.
+    // We explicitly compute a matrix for that stage below, no need to adjust here.
+    static const uint32_t kExplicitCoordMask = 1 << GrPaint::kFirstColorStage;
+    GrDrawState::AutoViewMatrixRestore avmr(drawState, m, kExplicitCoordMask);
 
     m.setAll(srcRect.width(), 0,                srcRect.fLeft,
              0,               srcRect.height(), srcRect.fTop,
-             0,               0,                GrMatrix::I()[8]);
+             0,               0,                SkMatrix::I()[8]);
     if (NULL != srcMatrix) {
         m.postConcat(*srcMatrix);
     }
-    drawState->sampler(GrPaint::kFirstTextureStage)->preConcatMatrix(m);
+
+    drawState->preConcatStageMatrices(kExplicitCoordMask, m);
 
     const GrVertexBuffer* sqVB = fGpu->getUnitSquareVertexBuffer();
     if (NULL == sqVB) {
         GrPrintf("Failed to create static rect vb.\n");
         return;
     }
-    target->setVertexSourceToBuffer(layout, sqVB);
-    target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
+    target->setVertexSourceToBuffer(0, sqVB);
+    target->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
 #else
-
-    GrDrawTarget* target;
-#if BATCH_RECT_TO_RECT
-    target = this->prepareToDraw(paint, kBuffered_DrawCategory);
-#else
-    target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
-#endif
+    GrDrawState::AutoStageDisable atr(fDrawState);
 
     const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
-    const GrMatrix* srcMatrices[GrDrawState::kNumStages] = {NULL};
+    const SkMatrix* srcMatrices[GrDrawState::kNumStages] = {NULL};
     srcRects[0] = &srcRect;
     srcMatrices[0] = srcMatrix;
 
-    target->drawRect(dstRect, dstMatrix, 1, srcRects, srcMatrices);
+    target->drawRect(dstRect, dstMatrix, srcRects, srcMatrices);
 #endif
 }
 
@@ -1130,19 +870,17 @@
 
     GrDrawTarget::AutoReleaseGeometry geo;
 
-    GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+    GrDrawState::AutoStageDisable atr(fDrawState);
 
-    bool hasTexCoords[GrPaint::kTotalStages] = {
-        NULL != texCoords,   // texCoordSrc provides explicit stage 0 coords
-        0                    // remaining stages use positions
-    };
-
-    GrVertexLayout layout = PaintStageVertexLayoutBits(paint, hasTexCoords);
-
-    if (NULL != colors) {
-        layout |= GrDrawTarget::kColor_VertexLayoutBit;
+    GrVertexLayout layout = 0;
+    if (NULL != texCoords) {
+        layout |= GrDrawState::StageTexCoordVertexLayoutBit(0, 0);
     }
-    int vertexSize = GrDrawTarget::VertexSize(layout);
+    if (NULL != colors) {
+        layout |= GrDrawState::kColor_VertexLayoutBit;
+    }
+    int vertexSize = GrDrawState::VertexSize(layout);
 
     if (sizeof(GrPoint) != vertexSize) {
         if (!geo.set(target, layout, vertexCount, 0)) {
@@ -1151,7 +889,7 @@
         }
         int texOffsets[GrDrawState::kMaxTexCoords];
         int colorOffset;
-        GrDrawTarget::VertexSizeAndOffsetsByIdx(layout,
+        GrDrawState::VertexSizeAndOffsetsByIdx(layout,
                                                 texOffsets,
                                                 &colorOffset,
                                                 NULL,
@@ -1173,7 +911,7 @@
         target->setVertexSourceToArray(layout, positions, vertexCount);
     }
 
-    // we don't currently apply offscreen AA to this path. Need improved 
+    // we don't currently apply offscreen AA to this path. Need improved
     // management of GrDrawTarget's geometry to avoid copying points per-tile.
 
     if (NULL != indices) {
@@ -1185,188 +923,220 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-#include "SkDraw.h"
-#include "SkRasterClip.h"
-
 namespace {
 
-SkPath::FillType gr_fill_to_sk_fill(GrPathFill fill) {
-    switch (fill) {
-        case kWinding_PathFill:
-            return SkPath::kWinding_FillType;
-        case kEvenOdd_PathFill:
-            return SkPath::kEvenOdd_FillType;
-        case kInverseWinding_PathFill:
-            return SkPath::kInverseWinding_FillType;
-        case kInverseEvenOdd_PathFill:
-            return SkPath::kInverseEvenOdd_FillType;
-        default:
-            GrCrash("Unexpected fill.");
-            return SkPath::kWinding_FillType;
-    }
+struct CircleVertex {
+    GrPoint fPos;
+    GrPoint fCenter;
+    SkScalar fOuterRadius;
+    SkScalar fInnerRadius;
+};
+
+inline bool circleStaysCircle(const SkMatrix& m) {
+    return m.isSimilarity();
 }
 
-// gets device coord bounds of path (not considering the fill) and clip. The
-// path bounds will be a subset of the clip bounds. returns false if path bounds
-// would be empty.
-bool get_path_and_clip_bounds(const GrDrawTarget* target,
-                              const GrPath& path,
-                              const GrVec* translate,
-                              GrIRect* pathBounds,
-                              GrIRect* clipBounds) {
-    // compute bounds as intersection of rt size, clip, and path
-    const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
+}
+
+void GrContext::drawOval(const GrPaint& paint,
+                         const GrRect& oval,
+                         const SkStrokeRec& stroke) {
+
+    if (!canDrawOval(paint, oval, stroke)) {
+        SkPath path;
+        path.addOval(oval);
+        this->drawPath(paint, path, stroke);
+        return;
+    }
+
+    internalDrawOval(paint, oval, stroke);
+}
+
+bool GrContext::canDrawOval(const GrPaint& paint, const GrRect& oval, const SkStrokeRec& stroke) const {
+
+    if (!paint.isAntiAlias()) {
+        return false;
+    }
+
+    // we can draw circles in any style
+    bool isCircle = SkScalarNearlyEqual(oval.width(), oval.height())
+                    && circleStaysCircle(this->getMatrix());
+    // and for now, axis-aligned ellipses only with fill or stroke-and-fill
+    SkStrokeRec::Style style = stroke.getStyle();
+    bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
+    bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
+
+    return isCircle || isFilledAxisAlignedEllipse;
+}
+
+void GrContext::internalDrawOval(const GrPaint& paint,
+                                 const GrRect& oval,
+                                 const SkStrokeRec& stroke) {
+
+    SkScalar xRadius = SkScalarHalf(oval.width());
+    SkScalar yRadius = SkScalarHalf(oval.height());
+
+    SkScalar strokeWidth = stroke.getWidth();
+    SkStrokeRec::Style style = stroke.getStyle();
+
+    bool isCircle = SkScalarNearlyEqual(xRadius, yRadius) && circleStaysCircle(this->getMatrix());
+#ifdef SK_DEBUG
+    {
+        // we should have checked for this previously
+        bool isStroke = (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style);
+        bool isFilledAxisAlignedEllipse = this->getMatrix().rectStaysRect() && !isStroke;
+        SkASSERT(paint.isAntiAlias() && (isCircle || isFilledAxisAlignedEllipse));
+    }
+#endif
+
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+
+    GrDrawState* drawState = target->drawState();
+    GrDrawState::AutoStageDisable atr(fDrawState);
+    const SkMatrix vm = drawState->getViewMatrix();
+
+    const GrRenderTarget* rt = drawState->getRenderTarget();
     if (NULL == rt) {
-        return false;
+        return;
     }
-    *pathBounds = GrIRect::MakeWH(rt->width(), rt->height());
-    const GrClip& clip = target->getClip();
-    if (clip.hasConservativeBounds()) {
-        clip.getConservativeBounds().roundOut(clipBounds);
-        if (!pathBounds->intersect(*clipBounds)) {
-            return false;
-        }
-    } else {
-        // pathBounds is currently the rt extent, set clip bounds to that rect.
-        *clipBounds = *pathBounds;
+
+    GrDrawState::AutoDeviceCoordDraw adcd(drawState);
+    if (!adcd.succeeded()) {
+        return;
     }
-    GrRect pathSBounds = path.getBounds();
-    if (!pathSBounds.isEmpty()) {
-        if (NULL != translate) {
-            pathSBounds.offset(*translate);
-        }
-        target->getDrawState().getViewMatrix().mapRect(&pathSBounds,
-                                                        pathSBounds);
-        GrIRect pathIBounds;
-        pathSBounds.roundOut(&pathIBounds);
-        if (!pathBounds->intersect(pathIBounds)) {
-            return false;
-        }
-    } else {
-        return false;
+
+    GrVertexLayout layout = GrDrawState::kEdge_VertexLayoutBit;
+    GrAssert(sizeof(CircleVertex) == GrDrawState::VertexSize(layout));
+
+    GrDrawTarget::AutoReleaseGeometry geo(target, layout, 4, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
+        return;
     }
-    return true;
+
+    CircleVertex* verts = reinterpret_cast<CircleVertex*>(geo.vertices());
+
+    GrPoint center = GrPoint::Make(oval.centerX(), oval.centerY());
+    vm.mapPoints(&center, 1);
+
+    SkScalar L;
+    SkScalar R;
+    SkScalar T;
+    SkScalar B;
+
+    if (isCircle) {
+        drawState->setVertexEdgeType(GrDrawState::kCircle_EdgeType);
+
+        xRadius = vm.mapRadius(xRadius);
+
+        SkScalar outerRadius = xRadius;
+        SkScalar innerRadius = 0;
+        SkScalar halfWidth = 0;
+        if (style != SkStrokeRec::kFill_Style) {
+            strokeWidth = vm.mapRadius(strokeWidth);
+            if (SkScalarNearlyZero(strokeWidth)) {
+                halfWidth = SK_ScalarHalf;
+            } else {
+                halfWidth = SkScalarHalf(strokeWidth);
+            }
+
+            outerRadius += halfWidth;
+            if (style == SkStrokeRec::kStroke_Style || style == SkStrokeRec::kHairline_Style) {
+                innerRadius = SkMaxScalar(0, xRadius - halfWidth);
+            }
+        }
+
+        for (int i = 0; i < 4; ++i) {
+            verts[i].fCenter = center;
+            verts[i].fOuterRadius = outerRadius;
+            verts[i].fInnerRadius = innerRadius;
+        }
+
+        L = -outerRadius;
+        R = +outerRadius;
+        T = -outerRadius;
+        B = +outerRadius;
+    } else {  // is axis-aligned ellipse
+        drawState->setVertexEdgeType(GrDrawState::kEllipse_EdgeType);
+
+        SkRect xformedRect;
+        vm.mapRect(&xformedRect, oval);
+
+        xRadius = SkScalarHalf(xformedRect.width());
+        yRadius = SkScalarHalf(xformedRect.height());
+
+        if (style == SkStrokeRec::kStrokeAndFill_Style && strokeWidth > 0.0f) {
+            SkScalar halfWidth = SkScalarHalf(strokeWidth);
+            // do (potentially) anisotropic mapping
+            SkVector scaledStroke;
+            scaledStroke.set(halfWidth, halfWidth);
+            vm.mapVectors(&scaledStroke, 1);
+            // this is legit only if scale & translation (which should be the case at the moment)
+            xRadius += scaledStroke.fX;
+            yRadius += scaledStroke.fY;
+        }
+
+        SkScalar ratio = SkScalarDiv(xRadius, yRadius);
+
+        for (int i = 0; i < 4; ++i) {
+            verts[i].fCenter = center;
+            verts[i].fOuterRadius = xRadius;
+            verts[i].fInnerRadius = ratio;
+        }
+
+        L = -xRadius;
+        R = +xRadius;
+        T = -yRadius;
+        B = +yRadius;
+    }
+
+    // The fragment shader will extend the radius out half a pixel
+    // to antialias. Expand the drawn rect here so all the pixels
+    // will be captured.
+    L += center.fX - SK_ScalarHalf;
+    R += center.fX + SK_ScalarHalf;
+    T += center.fY - SK_ScalarHalf;
+    B += center.fY + SK_ScalarHalf;
+
+    verts[0].fPos = SkPoint::Make(L, T);
+    verts[1].fPos = SkPoint::Make(R, T);
+    verts[2].fPos = SkPoint::Make(L, B);
+    verts[3].fPos = SkPoint::Make(R, B);
+
+    target->drawNonIndexed(kTriangleStrip_GrPrimitiveType, 0, 4);
 }
 
-/**
- * sw rasterizes path to A8 mask using the context's matrix and uploads to a 
- * scratch texture.
- */
-
-bool sw_draw_path_to_mask_texture(const GrPath& clientPath,
-                                  const GrIRect& pathDevBounds,
-                                  GrPathFill fill,
-                                  GrContext* context,
-                                  const GrPoint* translate,
-                                  GrAutoScratchTexture* tex) {
-    SkPaint paint;
-    SkPath tmpPath;
-    const SkPath* pathToDraw = &clientPath;
-    if (kHairLine_PathFill == fill) {
-        paint.setStyle(SkPaint::kStroke_Style);
-        paint.setStrokeWidth(SK_Scalar1);
-    } else {
-        paint.setStyle(SkPaint::kFill_Style);
-        SkPath::FillType skfill = gr_fill_to_sk_fill(fill);
-        if (skfill != pathToDraw->getFillType()) {
-            tmpPath = *pathToDraw;
-            tmpPath.setFillType(skfill);
-            pathToDraw = &tmpPath;
-        }
-    }
-    paint.setAntiAlias(true);
-    paint.setColor(SK_ColorWHITE);
-
-    GrMatrix matrix = context->getMatrix();
-    if (NULL != translate) {
-        matrix.postTranslate(translate->fX, translate->fY);
-    }
-
-    matrix.postTranslate(-pathDevBounds.fLeft * SK_Scalar1,
-                         -pathDevBounds.fTop * SK_Scalar1);
-    GrIRect bounds = GrIRect::MakeWH(pathDevBounds.width(),
-                                     pathDevBounds.height());
-
-    SkBitmap bm;
-    bm.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
-    if (!bm.allocPixels()) {
-        return false;
-    }
-    sk_bzero(bm.getPixels(), bm.getSafeSize());
-
-    SkDraw  draw;
-    sk_bzero(&draw, sizeof(draw));
-    SkRasterClip rc(bounds);
-    draw.fRC    = &rc;
-    draw.fClip  = &rc.bwRgn();
-    draw.fMatrix = &matrix;
-    draw.fBitmap = &bm;
-    draw.drawPath(*pathToDraw, paint);
-
-    const GrTextureDesc desc = {
-        kNone_GrTextureFlags,
-        bounds.fRight,
-        bounds.fBottom,
-        kAlpha_8_GrPixelConfig,
-        {0} // samples
-    };
-
-    tex->set(context, desc);
-    GrTexture* texture = tex->texture();
-
-    if (NULL == texture) {
-        return false;
-    }
-    SkAutoLockPixels alp(bm);
-    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
-                         bm.getPixels(), bm.rowBytes());
-    return true;
-}
-
-void draw_around_inv_path(GrDrawTarget* target,
-                          GrDrawState::StageMask stageMask,
-                          const GrIRect& clipBounds,
-                          const GrIRect& pathBounds) {
-    GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
-    GrRect rect;
-    if (clipBounds.fTop < pathBounds.fTop) {
-        rect.iset(clipBounds.fLeft, clipBounds.fTop, 
-                    clipBounds.fRight, pathBounds.fTop);
-        target->drawSimpleRect(rect, NULL, stageMask);
-    }
-    if (clipBounds.fLeft < pathBounds.fLeft) {
-        rect.iset(clipBounds.fLeft, pathBounds.fTop, 
-                    pathBounds.fLeft, pathBounds.fBottom);
-        target->drawSimpleRect(rect, NULL, stageMask);
-    }
-    if (clipBounds.fRight > pathBounds.fRight) {
-        rect.iset(pathBounds.fRight, pathBounds.fTop, 
-                    clipBounds.fRight, pathBounds.fBottom);
-        target->drawSimpleRect(rect, NULL, stageMask);
-    }
-    if (clipBounds.fBottom > pathBounds.fBottom) {
-        rect.iset(clipBounds.fLeft, pathBounds.fBottom, 
-                    clipBounds.fRight, clipBounds.fBottom);
-        target->drawSimpleRect(rect, NULL, stageMask);
-    }
-}
-
-}
-
-void GrContext::drawPath(const GrPaint& paint, const GrPath& path,
-                         GrPathFill fill, const GrPoint* translate) {
+void GrContext::drawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke) {
 
     if (path.isEmpty()) {
-       if (GrIsFillInverted(fill)) {
+       if (path.isInverseFillType()) {
            this->drawPaint(paint);
        }
        return;
     }
 
-    GrDrawTarget* target = this->prepareToDraw(paint, kUnbuffered_DrawCategory);
-    GrDrawState::StageMask stageMask = paint.getActiveStageMask();
+    SkRect ovalRect;
+    bool isOval = path.isOval(&ovalRect);
 
-    bool prAA = paint.fAntiAlias && !this->getRenderTarget()->isMultisampled();
+    if (isOval && !path.isInverseFillType() && this->canDrawOval(paint, ovalRect, stroke)) {
+        this->drawOval(paint, ovalRect, stroke);
+        return;
+    }
+
+    this->internalDrawPath(paint, path, stroke);
+}
+
+void GrContext::internalDrawPath(const GrPaint& paint, const SkPath& path, const SkStrokeRec& stroke) {
+
+    // Note that below we may sw-rasterize the path into a scratch texture.
+    // Scratch textures can be recycled after they are returned to the texture
+    // cache. This presents a potential hazard for buffered drawing. However,
+    // the writePixels that uploads to the scratch will perform a flush so we're
+    // OK.
+    GrDrawTarget* target = this->prepareToDraw(&paint, DEFAULT_BUFFERING);
+    GrDrawState::AutoStageDisable atr(fDrawState);
+
+    bool prAA = paint.isAntiAlias() && !this->getRenderTarget()->isMultisampled();
 
     // An Assumption here is that path renderer would use some form of tweaking
     // the src color (either the input alpha or in the frag shader) to implement
@@ -1379,50 +1149,26 @@
         prAA = false;
     }
 
-    GrPathRenderer* pr = NULL;
-    if (prAA) {
-        pr = this->getPathRenderer(path, fill, target, true);
-        if (NULL == pr) {
-            GrAutoScratchTexture ast;
-            GrIRect pathBounds, clipBounds;
-            if (!get_path_and_clip_bounds(target, path, translate,
-                                          &pathBounds, &clipBounds)) {
-                return;
-            }
-            if (NULL == pr && sw_draw_path_to_mask_texture(path, pathBounds,
-                                                           fill, this,
-                                                           translate, &ast)) {
-                GrTexture* texture = ast.texture();
-                GrAssert(NULL != texture);
-                GrDrawTarget::AutoDeviceCoordDraw adcd(target, stageMask);
-                enum {
-                    kPathMaskStage = GrPaint::kTotalStages,
-                };
-                target->drawState()->setTexture(kPathMaskStage, texture);
-                target->drawState()->sampler(kPathMaskStage)->reset();
-                GrScalar w = GrIntToScalar(pathBounds.width());
-                GrScalar h = GrIntToScalar(pathBounds.height());
-                GrRect maskRect = GrRect::MakeWH(w / texture->width(),
-                                                 h / texture->height());
-                const GrRect* srcRects[GrDrawState::kNumStages] = {NULL};
-                srcRects[kPathMaskStage] = &maskRect;
-                stageMask |= 1 << kPathMaskStage;
-                GrRect dstRect = GrRect::MakeLTRB(
-                    SK_Scalar1* pathBounds.fLeft,
-                    SK_Scalar1* pathBounds.fTop,
-                    SK_Scalar1* pathBounds.fRight,
-                    SK_Scalar1* pathBounds.fBottom);
-                target->drawRect(dstRect, NULL, stageMask, srcRects, NULL);
-                target->drawState()->setTexture(kPathMaskStage, NULL);
-                if (GrIsFillInverted(fill)) {
-                    draw_around_inv_path(target, stageMask,
-                                         clipBounds, pathBounds);
-                }
-                return;
+    GrPathRendererChain::DrawType type = prAA ? GrPathRendererChain::kColorAntiAlias_DrawType :
+                                                GrPathRendererChain::kColor_DrawType;
+
+    const SkPath* pathPtr = &path;
+    SkPath tmpPath;
+    SkStrokeRec strokeRec(stroke);
+
+    // Try a 1st time without stroking the path and without allowing the SW renderer
+    GrPathRenderer* pr = this->getPathRenderer(*pathPtr, strokeRec, target, false, type);
+
+    if (NULL == pr) {
+        if (!strokeRec.isHairlineStyle()) {
+            // It didn't work the 1st time, so try again with the stroked path
+            if (strokeRec.applyToPath(&tmpPath, *pathPtr)) {
+                pathPtr = &tmpPath;
+                strokeRec.setFillStyle();
             }
         }
-    } else {
-        pr = this->getPathRenderer(path, fill, target, false);
+        // This time, allow SW renderer
+        pr = this->getPathRenderer(*pathPtr, strokeRec, target, true, type);
     }
 
     if (NULL == pr) {
@@ -1432,7 +1178,7 @@
         return;
     }
 
-    pr->drawPath(path, fill, translate, target, stageMask, prAA);
+    pr->drawPath(*pathPtr, strokeRec, target, prAA);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1448,61 +1194,55 @@
     }
 }
 
-void GrContext::flushText() {
-    if (kText_DrawCategory == fLastDrawCategory) {
-        flushDrawBuffer();
-    }
-}
-
 void GrContext::flushDrawBuffer() {
-#if BATCH_RECT_TO_RECT || DEFER_TEXT_RENDERING
     if (fDrawBuffer) {
-        fDrawBuffer->playback(fGpu);
-        fDrawBuffer->reset();
+        // With addition of the AA clip path, flushing the draw buffer can
+        // result in the generation of an AA clip mask. During this
+        // process the SW path renderer may be invoked which recusively
+        // calls this method (via internalWriteTexturePixels) creating
+        // infinite recursion
+        GrInOrderDrawBuffer* temp = fDrawBuffer;
+        fDrawBuffer = NULL;
+
+        temp->flushTo(fGpu);
+
+        fDrawBuffer = temp;
     }
-#endif
 }
 
-void GrContext::internalWriteTexturePixels(GrTexture* texture,
-                                           int left, int top,
-                                           int width, int height,
-                                           GrPixelConfig config,
-                                           const void* buffer,
-                                           size_t rowBytes,
-                                           uint32_t flags) {
+void GrContext::writeTexturePixels(GrTexture* texture,
+                                   int left, int top, int width, int height,
+                                   GrPixelConfig config, const void* buffer, size_t rowBytes,
+                                   uint32_t flags) {
     SK_TRACE_EVENT0("GrContext::writeTexturePixels");
     ASSERT_OWNED_RESOURCE(texture);
 
+    // TODO: use scratch texture to perform conversion
+    if (kUnpremul_PixelOpsFlag & flags) {
+        return;
+    }
     if (!(kDontFlush_PixelOpsFlag & flags)) {
         this->flush();
     }
-    // TODO: use scratch texture to perform conversion
-    if (GrPixelConfigIsUnpremultiplied(texture->config()) !=
-        GrPixelConfigIsUnpremultiplied(config)) {
-        return;
-    }
 
-    fGpu->writeTexturePixels(texture, left, top, width, height, 
+    fGpu->writeTexturePixels(texture, left, top, width, height,
                              config, buffer, rowBytes);
 }
 
-bool GrContext::internalReadTexturePixels(GrTexture* texture,
-                                          int left, int top,
-                                          int width, int height,
-                                          GrPixelConfig config,
-                                          void* buffer,
-                                          size_t rowBytes,
-                                          uint32_t flags) {
+bool GrContext::readTexturePixels(GrTexture* texture,
+                                  int left, int top, int width, int height,
+                                  GrPixelConfig config, void* buffer, size_t rowBytes,
+                                  uint32_t flags) {
     SK_TRACE_EVENT0("GrContext::readTexturePixels");
     ASSERT_OWNED_RESOURCE(texture);
 
     // TODO: code read pixels for textures that aren't also rendertargets
     GrRenderTarget* target = texture->asRenderTarget();
     if (NULL != target) {
-        return this->internalReadRenderTargetPixels(target,
-                                                    left, top, width, height,
-                                                    config, buffer, rowBytes,
-                                                    flags);
+        return this->readRenderTargetPixels(target,
+                                            left, top, width, height,
+                                            config, buffer, rowBytes,
+                                            flags);
     } else {
         return false;
     }
@@ -1517,38 +1257,57 @@
  * if the GrPixelConfig has no equivalent Config8888.
  */
 bool grconfig_to_config8888(GrPixelConfig config,
+                            bool unpremul,
                             SkCanvas::Config8888* config8888) {
     switch (config) {
-        case kRGBA_8888_PM_GrPixelConfig:
-            *config8888 = SkCanvas::kRGBA_Premul_Config8888;
+        case kRGBA_8888_GrPixelConfig:
+            if (unpremul) {
+                *config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
+            } else {
+                *config8888 = SkCanvas::kRGBA_Premul_Config8888;
+            }
             return true;
-        case kRGBA_8888_UPM_GrPixelConfig:
-            *config8888 = SkCanvas::kRGBA_Unpremul_Config8888;
-            return true;
-        case kBGRA_8888_PM_GrPixelConfig:
-            *config8888 = SkCanvas::kBGRA_Premul_Config8888;
-            return true;
-        case kBGRA_8888_UPM_GrPixelConfig:
-            *config8888 = SkCanvas::kBGRA_Unpremul_Config8888;
+        case kBGRA_8888_GrPixelConfig:
+            if (unpremul) {
+                *config8888 = SkCanvas::kBGRA_Unpremul_Config8888;
+            } else {
+                *config8888 = SkCanvas::kBGRA_Premul_Config8888;
+            }
             return true;
         default:
             return false;
     }
 }
+
+// It returns a configuration with where the byte position of the R & B components are swapped in
+// relation to the input config. This should only be called with the result of
+// grconfig_to_config8888 as it will fail for other configs.
+SkCanvas::Config8888 swap_config8888_red_and_blue(SkCanvas::Config8888 config8888) {
+    switch (config8888) {
+        case SkCanvas::kBGRA_Premul_Config8888:
+            return SkCanvas::kRGBA_Premul_Config8888;
+        case SkCanvas::kBGRA_Unpremul_Config8888:
+            return SkCanvas::kRGBA_Unpremul_Config8888;
+        case SkCanvas::kRGBA_Premul_Config8888:
+            return SkCanvas::kBGRA_Premul_Config8888;
+        case SkCanvas::kRGBA_Unpremul_Config8888:
+            return SkCanvas::kBGRA_Unpremul_Config8888;
+        default:
+            GrCrash("Unexpected input");
+            return SkCanvas::kBGRA_Unpremul_Config8888;;
+    }
+}
 }
 
-bool GrContext::internalReadRenderTargetPixels(GrRenderTarget* target,
-                                               int left, int top,
-                                               int width, int height,
-                                               GrPixelConfig config,
-                                               void* buffer,
-                                               size_t rowBytes,
-                                               uint32_t flags) {
+bool GrContext::readRenderTargetPixels(GrRenderTarget* target,
+                                       int left, int top, int width, int height,
+                                       GrPixelConfig config, void* buffer, size_t rowBytes,
+                                       uint32_t flags) {
     SK_TRACE_EVENT0("GrContext::readRenderTargetPixels");
     ASSERT_OWNED_RESOURCE(target);
 
-    if (NULL == target) { 
-        target = fGpu->drawState()->getRenderTarget();
+    if (NULL == target) {
+        target = fDrawState->getRenderTarget();
         if (NULL == target) {
             return false;
         }
@@ -1558,69 +1317,53 @@
         this->flush();
     }
 
-    if (!GrPixelConfigIsUnpremultiplied(target->config()) &&
-        GrPixelConfigIsUnpremultiplied(config) &&
-        !fGpu->canPreserveReadWriteUnpremulPixels()) {
-        SkCanvas::Config8888 srcConfig8888, dstConfig8888;
-        if (!grconfig_to_config8888(target->config(), &srcConfig8888) ||
-            !grconfig_to_config8888(config, &dstConfig8888)) {
-            return false;
-        }
-        // do read back using target's own config
-        this->internalReadRenderTargetPixels(target,
-                                             left, top,
-                                             width, height,
-                                             target->config(),
-                                             buffer, rowBytes,
-                                             kDontFlush_PixelOpsFlag);
-        // sw convert the pixels to unpremul config
-        uint32_t* pixels = reinterpret_cast<uint32_t*>(buffer);
-        SkConvertConfig8888Pixels(pixels, rowBytes, dstConfig8888,
-                                  pixels, rowBytes, srcConfig8888,
-                                  width, height);
-        return true;
-    }
+    // Determine which conversions have to be applied: flipY, swapRAnd, and/or unpremul.
 
-    GrTexture* src = target->asTexture();
-    bool swapRAndB = NULL != src &&
-                     fGpu->preferredReadPixelsConfig(config) ==
-                     GrPixelConfigSwapRAndB(config);
-
-    bool flipY = NULL != src &&
-                 fGpu->readPixelsWillPayForYFlip(target, left, top,
+    // If fGpu->readPixels would incur a y-flip cost then we will read the pixels upside down. We'll
+    // either do the flipY by drawing into a scratch with a matrix or on the cpu after the read.
+    bool flipY = fGpu->readPixelsWillPayForYFlip(target, left, top,
                                                  width, height, config,
                                                  rowBytes);
-    bool alphaConversion = (!GrPixelConfigIsUnpremultiplied(target->config()) &&
-                             GrPixelConfigIsUnpremultiplied(config));
+    bool swapRAndB = fGpu->preferredReadPixelsConfig(config) == GrPixelConfigSwapRAndB(config);
 
-    if (NULL == src && alphaConversion) {
-        // we should fallback to cpu conversion here. This could happen when
-        // we were given an external render target by the client that is not
-        // also a texture (e.g. FBO 0 in GL)
+    bool unpremul = SkToBool(kUnpremul_PixelOpsFlag & flags);
+
+    // flipY will get set to false when it is handled below using a scratch. However, in that case
+    // we still want to do the read upside down.
+    bool readUpsideDown = flipY;
+
+    if (unpremul && kRGBA_8888_GrPixelConfig != config && kBGRA_8888_GrPixelConfig != config) {
+        // The unpremul flag is only allowed for these two configs.
         return false;
     }
-    // we draw to a scratch texture if any of these conversion are applied
-    GrAutoScratchTexture ast;
-    if (flipY || swapRAndB || alphaConversion) {
-        GrAssert(NULL != src);
-        if (swapRAndB) {
-            config = GrPixelConfigSwapRAndB(config);
-            GrAssert(kUnknown_GrPixelConfig != config);
-        }
-        // Make the scratch a render target because we don't have a robust
-        // readTexturePixels as of yet (it calls this function).
-        const GrTextureDesc desc = {
-            kRenderTarget_GrTextureFlagBit,
-            width, height,
-            config,
-            {0}, // samples
-        };
 
-        // When a full readback is faster than a partial we could always make
-        // the scratch exactly 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.
+    GrPixelConfig readConfig;
+    if (swapRAndB) {
+        readConfig = GrPixelConfigSwapRAndB(config);
+        GrAssert(kUnknown_GrPixelConfig != config);
+    } else {
+        readConfig = config;
+    }
+
+    // If the src is a texture and we would have to do conversions after read pixels, we instead
+    // do the conversions by drawing the src to a scratch texture. If we handle any of the
+    // conversions in the draw we set the corresponding bool to false so that we don't reapply it
+    // on the read back pixels.
+    GrTexture* src = target->asTexture();
+    GrAutoScratchTexture ast;
+    if (NULL != src && (swapRAndB || unpremul || flipY)) {
+        // Make the scratch a render target because we don't have a robust readTexturePixels as of
+        // yet. It calls this function.
+        GrTextureDesc desc;
+        desc.fFlags = kRenderTarget_GrTextureFlagBit;
+        desc.fWidth = width;
+        desc.fHeight = height;
+        desc.fConfig = readConfig;
+
+        // When a full readback is faster than a partial we could always make the scratch exactly
+        // 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;
         if (0 == left &&
             0 == top &&
@@ -1631,38 +1374,114 @@
         }
         ast.set(this, desc, match);
         GrTexture* texture = ast.texture();
-        if (!texture) {
-            return false;
-        }
-        target = texture->asRenderTarget();
-        GrAssert(NULL != target);
+        if (texture) {
+            // compute a matrix to perform the draw
+            SkMatrix textureMatrix;
+            if (flipY) {
+                textureMatrix.setTranslate(SK_Scalar1 * left,
+                                    SK_Scalar1 * (top + height));
+                textureMatrix.set(SkMatrix::kMScaleY, -SK_Scalar1);
+            } else {
+                textureMatrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top);
+            }
+            textureMatrix.postIDiv(src->width(), src->height());
 
-        GrDrawTarget::AutoStateRestore asr(fGpu);
-        GrDrawState* drawState = fGpu->drawState();
-        drawState->reset();
-        drawState->setRenderTarget(target);
+            SkAutoTUnref<const GrEffectRef> effect;
+            if (unpremul) {
+                effect.reset(this->createPMToUPMEffect(src, swapRAndB, textureMatrix));
+                if (NULL != effect) {
+                    unpremul = false; // we no longer need to do this on CPU after the readback.
+                }
+            }
+            // If we failed to create a PM->UPM effect and have no other conversions to perform then
+            // there is no longer any point to using the scratch.
+            if (NULL != effect || flipY || swapRAndB) {
+                if (!effect) {
+                    effect.reset(GrConfigConversionEffect::Create(
+                                                    src,
+                                                    swapRAndB,
+                                                    GrConfigConversionEffect::kNone_PMConversion,
+                                                    textureMatrix));
+                }
+                swapRAndB = false; // we will handle the swap in the draw.
+                flipY = false; // we already incorporated the y flip in the matrix
 
-        GrMatrix matrix;
-        if (flipY) {
-            matrix.setTranslate(SK_Scalar1 * left,
-                                SK_Scalar1 * (top + height));
-            matrix.set(GrMatrix::kMScaleY, -GR_Scalar1);
-        } else {
-            matrix.setTranslate(SK_Scalar1 *left, SK_Scalar1 *top);
+                GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
+                GrDrawState* drawState = fGpu->drawState();
+                GrAssert(effect);
+                drawState->setEffect(0, effect);
+
+                drawState->setRenderTarget(texture->asRenderTarget());
+                GrRect rect = GrRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height));
+                fGpu->drawSimpleRect(rect, NULL);
+                // we want to read back from the scratch's origin
+                left = 0;
+                top = 0;
+                target = texture->asRenderTarget();
+            }
         }
-        matrix.postIDiv(src->width(), src->height());
-        drawState->sampler(0)->reset(matrix);
-        drawState->sampler(0)->setRAndBSwap(swapRAndB);
-        drawState->setTexture(0, src);
-        GrRect rect;
-        rect.setXYWH(0, 0, SK_Scalar1 * width, SK_Scalar1 * height);
-        fGpu->drawSimpleRect(rect, NULL, 0x1);
-        left = 0;
-        top = 0;
     }
-    return fGpu->readPixels(target,
-                            left, top, width, height,
-                            config, buffer, rowBytes, flipY);
+    if (!fGpu->readPixels(target,
+                          left, top, width, height,
+                          readConfig, buffer, rowBytes, readUpsideDown)) {
+        return false;
+    }
+    // Perform any conversions we weren't able to perform using a scratch texture.
+    if (unpremul || swapRAndB || flipY) {
+        // These are initialized to suppress a warning
+        SkCanvas::Config8888 srcC8888 = SkCanvas::kNative_Premul_Config8888;
+        SkCanvas::Config8888 dstC8888 = SkCanvas::kNative_Premul_Config8888;
+
+        bool c8888IsValid = grconfig_to_config8888(config, false, &srcC8888);
+        grconfig_to_config8888(config, unpremul, &dstC8888);
+
+        if (swapRAndB) {
+            GrAssert(c8888IsValid); // we should only do r/b swap on 8888 configs
+            srcC8888 = swap_config8888_red_and_blue(srcC8888);
+        }
+        if (flipY) {
+            size_t tightRB = width * GrBytesPerPixel(config);
+            if (0 == rowBytes) {
+                rowBytes = tightRB;
+            }
+            SkAutoSTMalloc<256, uint8_t> tempRow(tightRB);
+            intptr_t top = reinterpret_cast<intptr_t>(buffer);
+            intptr_t bot = top + (height - 1) * rowBytes;
+            while (top < bot) {
+                uint32_t* t = reinterpret_cast<uint32_t*>(top);
+                uint32_t* b = reinterpret_cast<uint32_t*>(bot);
+                uint32_t* temp = reinterpret_cast<uint32_t*>(tempRow.get());
+                memcpy(temp, t, tightRB);
+                if (c8888IsValid) {
+                    SkConvertConfig8888Pixels(t, tightRB, dstC8888,
+                                              b, tightRB, srcC8888,
+                                              width, 1);
+                    SkConvertConfig8888Pixels(b, tightRB, dstC8888,
+                                              temp, tightRB, srcC8888,
+                                              width, 1);
+                } else {
+                    memcpy(t, b, tightRB);
+                    memcpy(b, temp, tightRB);
+                }
+                top += rowBytes;
+                bot -= rowBytes;
+            }
+            // The above loop does nothing on the middle row when height is odd.
+            if (top == bot && c8888IsValid && dstC8888 != srcC8888) {
+                uint32_t* mid = reinterpret_cast<uint32_t*>(top);
+                SkConvertConfig8888Pixels(mid, tightRB, dstC8888, mid, tightRB, srcC8888, width, 1);
+            }
+        } else {
+            // if we aren't flipping Y then we have no reason to be here other than doing
+            // conversions for 8888 (r/b swap or upm).
+            GrAssert(c8888IsValid);
+            uint32_t* b32 = reinterpret_cast<uint32_t*>(buffer);
+            SkConvertConfig8888Pixels(b32, rowBytes, dstC8888,
+                                      b32, rowBytes, srcC8888,
+                                      width, height);
+        }
+    }
+    return true;
 }
 
 void GrContext::resolveRenderTarget(GrRenderTarget* target) {
@@ -1675,248 +1494,241 @@
     fGpu->resolveRenderTarget(target);
 }
 
-void GrContext::copyTexture(GrTexture* src, GrRenderTarget* dst) {
+void GrContext::copyTexture(GrTexture* src, GrRenderTarget* dst, const SkIPoint* topLeft) {
     if (NULL == src || NULL == dst) {
         return;
     }
     ASSERT_OWNED_RESOURCE(src);
 
-    GrDrawTarget::AutoStateRestore asr(fGpu);
+    // Writes pending to the source texture are not tracked, so a flush
+    // is required to ensure that the copy captures the most recent contents
+    // of the source texture. See similar behavior in
+    // GrContext::resolveRenderTarget.
+    this->flush();
+
+    GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
     GrDrawState* drawState = fGpu->drawState();
-    drawState->reset();
     drawState->setRenderTarget(dst);
-    GrMatrix sampleM;
+    SkMatrix sampleM;
     sampleM.setIDiv(src->width(), src->height());
-    drawState->setTexture(0, src);
-    drawState->sampler(0)->reset(sampleM);
-    SkRect rect = SkRect::MakeXYWH(0, 0,
-                                   SK_Scalar1 * src->width(),
-                                   SK_Scalar1 * src->height());
-    fGpu->drawSimpleRect(rect, NULL, 1 << 0);
+    SkIRect srcRect = SkIRect::MakeWH(dst->width(), dst->height());
+    if (NULL != topLeft) {
+        srcRect.offset(*topLeft);
+    }
+    SkIRect srcBounds = SkIRect::MakeWH(src->width(), src->height());
+    if (!srcRect.intersect(srcBounds)) {
+        return;
+    }
+    sampleM.preTranslate(SkIntToScalar(srcRect.fLeft), SkIntToScalar(srcRect.fTop));
+    drawState->createTextureEffect(0, src, sampleM);
+    SkRect dstR = SkRect::MakeWH(SkIntToScalar(srcRect.width()), SkIntToScalar(srcRect.height()));
+    fGpu->drawSimpleRect(dstR, NULL);
 }
 
-void GrContext::internalWriteRenderTargetPixels(GrRenderTarget* target, 
-                                                int left, int top,
-                                                int width, int height,
-                                                GrPixelConfig config,
-                                                const void* buffer,
-                                                size_t rowBytes,
-                                                uint32_t flags) {
+void GrContext::writeRenderTargetPixels(GrRenderTarget* target,
+                                        int left, int top, int width, int height,
+                                        GrPixelConfig config,
+                                        const void* buffer,
+                                        size_t rowBytes,
+                                        uint32_t flags) {
     SK_TRACE_EVENT0("GrContext::writeRenderTargetPixels");
     ASSERT_OWNED_RESOURCE(target);
 
-    if (NULL == target) { 
-        target = fGpu->drawState()->getRenderTarget();
+    if (NULL == target) {
+        target = fDrawState->getRenderTarget();
         if (NULL == target) {
             return;
         }
     }
 
-    // TODO: when underlying api has a direct way to do this we should use it
-    // (e.g. glDrawPixels on desktop GL).
+    // TODO: when underlying api has a direct way to do this we should use it (e.g. glDrawPixels on
+    // desktop GL).
 
-    // If the RT is also a texture and we don't have to do PM/UPM conversion
-    // then take the texture path, which we expect to be at least as fast or
-    // faster since it doesn't use an intermediate texture as we do below.
-    
+    // We will always call some form of writeTexturePixels and we will pass our flags on to it.
+    // Thus, we don't perform a flush here since that call will do it (if the kNoFlush flag isn't
+    // set.)
+
+    // If the RT is also a texture and we don't have to premultiply then take the texture path.
+    // We expect to be at least as fast or faster since it doesn't use an intermediate texture as
+    // we do below.
+
 #if !GR_MAC_BUILD
-    // At least some drivers on the Mac get confused when glTexImage2D is called
-    // on a texture attached to an FBO. The FBO still sees the old image. TODO:
-    // determine what OS versions and/or HW is affected.
-    if (NULL != target->asTexture() &&
-        GrPixelConfigIsUnpremultiplied(target->config()) ==
-        GrPixelConfigIsUnpremultiplied(config)) {
-
-        this->internalWriteTexturePixels(target->asTexture(),
-                                         left, top, width, height,
-                                         config, buffer, rowBytes, flags);
+    // At least some drivers on the Mac get confused when glTexImage2D is called on a texture
+    // attached to an FBO. The FBO still sees the old image. TODO: determine what OS versions and/or
+    // HW is affected.
+    if (NULL != target->asTexture() && !(kUnpremul_PixelOpsFlag & flags)) {
+        this->writeTexturePixels(target->asTexture(),
+                                 left, top, width, height,
+                                 config, buffer, rowBytes, flags);
         return;
     }
 #endif
-    if (!GrPixelConfigIsUnpremultiplied(target->config()) &&
-        GrPixelConfigIsUnpremultiplied(config) &&
-        !fGpu->canPreserveReadWriteUnpremulPixels()) {
-        SkCanvas::Config8888 srcConfig8888, dstConfig8888;
-        if (!grconfig_to_config8888(config, &srcConfig8888) ||
-            !grconfig_to_config8888(target->config(), &dstConfig8888)) {
-            return;
-        }
-        // allocate a tmp buffer and sw convert the pixels to premul
-        SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(width * height);
-        const uint32_t* src = reinterpret_cast<const uint32_t*>(buffer);
-        SkConvertConfig8888Pixels(tmpPixels.get(), 4 * width, dstConfig8888,
-                                  src, rowBytes, srcConfig8888,
-                                  width, height);
-        // upload the already premul pixels
-        this->internalWriteRenderTargetPixels(target,
-                                             left, top,
-                                             width, height,
-                                             target->config(),
-                                             tmpPixels, 4 * width, flags);
-        return;
-    }
 
-    bool swapRAndB = fGpu->preferredReadPixelsConfig(config) ==
-                     GrPixelConfigSwapRAndB(config);
+    bool swapRAndB = (fGpu->preferredReadPixelsConfig(config) == GrPixelConfigSwapRAndB(config));
+
+    GrPixelConfig textureConfig;
     if (swapRAndB) {
-        config = GrPixelConfigSwapRAndB(config);
+        textureConfig = GrPixelConfigSwapRAndB(config);
+    } else {
+        textureConfig = config;
     }
 
-    const GrTextureDesc desc = {
-        kNone_GrTextureFlags, width, height, config, {0}
-    };
+    GrTextureDesc desc;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = textureConfig;
     GrAutoScratchTexture ast(this, desc);
     GrTexture* texture = ast.texture();
     if (NULL == texture) {
         return;
     }
-    this->internalWriteTexturePixels(texture, 0, 0, width, height,
-                                     config, buffer, rowBytes, flags);
 
-    GrDrawTarget::AutoStateRestore  asr(fGpu);
+    SkAutoTUnref<const GrEffectRef> effect;
+    SkMatrix textureMatrix;
+    textureMatrix.setIDiv(texture->width(), texture->height());
+
+    // allocate a tmp buffer and sw convert the pixels to premul
+    SkAutoSTMalloc<128 * 128, uint32_t> tmpPixels(0);
+
+    if (kUnpremul_PixelOpsFlag & flags) {
+        if (kRGBA_8888_GrPixelConfig != config && kBGRA_8888_GrPixelConfig != config) {
+            return;
+        }
+        effect.reset(this->createUPMToPMEffect(texture, swapRAndB, textureMatrix));
+        if (NULL == effect) {
+            SkCanvas::Config8888 srcConfig8888, dstConfig8888;
+            GR_DEBUGCODE(bool success = )
+            grconfig_to_config8888(config, true, &srcConfig8888);
+            GrAssert(success);
+            GR_DEBUGCODE(success = )
+            grconfig_to_config8888(config, false, &dstConfig8888);
+            GrAssert(success);
+            const uint32_t* src = reinterpret_cast<const uint32_t*>(buffer);
+            tmpPixels.reset(width * height);
+            SkConvertConfig8888Pixels(tmpPixels.get(), 4 * width, dstConfig8888,
+                                      src, rowBytes, srcConfig8888,
+                                      width, height);
+            buffer = tmpPixels.get();
+            rowBytes = 4 * width;
+        }
+    }
+    if (NULL == effect) {
+        effect.reset(GrConfigConversionEffect::Create(texture,
+                                                      swapRAndB,
+                                                      GrConfigConversionEffect::kNone_PMConversion,
+                                                      textureMatrix));
+    }
+
+    this->writeTexturePixels(texture,
+                             0, 0, width, height,
+                             textureConfig, buffer, rowBytes,
+                             flags & ~kUnpremul_PixelOpsFlag);
+
+    GrDrawTarget::AutoStateRestore  asr(fGpu, GrDrawTarget::kReset_ASRInit);
     GrDrawState* drawState = fGpu->drawState();
-    drawState->reset();
+    GrAssert(effect);
+    drawState->setEffect(0, effect);
 
-    GrMatrix matrix;
-    matrix.setTranslate(GrIntToScalar(left), GrIntToScalar(top));
+    SkMatrix matrix;
+    matrix.setTranslate(SkIntToScalar(left), SkIntToScalar(top));
     drawState->setViewMatrix(matrix);
     drawState->setRenderTarget(target);
-    drawState->setTexture(0, texture);
 
-    matrix.setIDiv(texture->width(), texture->height());
-    drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
-                                 GrSamplerState::kNearest_Filter,
-                                 matrix);
-    drawState->sampler(0)->setRAndBSwap(swapRAndB);
-
-    GrVertexLayout layout = GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(0);
-    static const int VCOUNT = 4;
-    // TODO: Use GrGpu::drawRect here
-    GrDrawTarget::AutoReleaseGeometry geo(fGpu, layout, VCOUNT, 0);
-    if (!geo.succeeded()) {
-        GrPrintf("Failed to get space for vertices!\n");
-        return;
-    }
-    ((GrPoint*)geo.vertices())->setIRectFan(0, 0, width, height);
-    fGpu->drawNonIndexed(kTriangleFan_PrimitiveType, 0, VCOUNT);
+    fGpu->drawSimpleRect(GrRect::MakeWH(SkIntToScalar(width), SkIntToScalar(height)), NULL);
 }
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrContext::setPaint(const GrPaint& paint, GrDrawTarget* target) {
-    GrDrawState* drawState = target->drawState();
-
-    for (int i = 0; i < GrPaint::kMaxTextures; ++i) {
-        int s = i + GrPaint::kFirstTextureStage;
-        drawState->setTexture(s, paint.getTexture(i));
-        ASSERT_OWNED_RESOURCE(paint.getTexture(i));
-        if (paint.getTexture(i)) {
-            *drawState->sampler(s) = paint.getTextureSampler(i);
+GrDrawTarget* GrContext::prepareToDraw(const GrPaint* paint, BufferedDraw buffered) {
+    if (kNo_BufferedDraw == buffered && kYes_BufferedDraw == fLastDrawWasBuffered) {
+        this->flushDrawBuffer();
+        fLastDrawWasBuffered = kNo_BufferedDraw;
+    }
+    if (NULL != paint) {
+        GrAssert(fDrawState->stagesDisabled());
+        fDrawState->setFromPaint(*paint);
+#if GR_DEBUG_PARTIAL_COVERAGE_CHECK
+        if ((paint->hasMask() || 0xff != paint->fCoverage) &&
+            !fGpu->canApplyCoverage()) {
+            GrPrintf("Partial pixel coverage will be incorrectly blended.\n");
         }
-    }
-
-    drawState->setFirstCoverageStage(GrPaint::kFirstMaskStage);
-
-    for (int i = 0; i < GrPaint::kMaxMasks; ++i) {
-        int s = i + GrPaint::kFirstMaskStage;
-        drawState->setTexture(s, paint.getMask(i));
-        ASSERT_OWNED_RESOURCE(paint.getMask(i));
-        if (paint.getMask(i)) {
-            *drawState->sampler(s) = paint.getMaskSampler(i);
-        }
-    }
-
-    drawState->setColor(paint.fColor);
-
-    if (paint.fDither) {
-        drawState->enableState(GrDrawState::kDither_StateBit);
-    } else {
-        drawState->disableState(GrDrawState::kDither_StateBit);
-    }
-    if (paint.fAntiAlias) {
-        drawState->enableState(GrDrawState::kHWAntialias_StateBit);
-    } else {
-        drawState->disableState(GrDrawState::kHWAntialias_StateBit);
-    }
-    if (paint.fColorMatrixEnabled) {
-        drawState->enableState(GrDrawState::kColorMatrix_StateBit);
-    } else {
-        drawState->disableState(GrDrawState::kColorMatrix_StateBit);
-    }
-    drawState->setBlendFunc(paint.fSrcBlendCoeff, paint.fDstBlendCoeff);
-    drawState->setColorFilter(paint.fColorFilterColor, paint.fColorFilterXfermode);
-    drawState->setColorMatrix(paint.fColorMatrix);
-    drawState->setCoverage(paint.fCoverage);
-
-    if (paint.getActiveMaskStageMask() && !target->canApplyCoverage()) {
-        GrPrintf("Partial pixel coverage will be incorrectly blended.\n");
-    }
-}
-
-GrDrawTarget* GrContext::prepareToDraw(const GrPaint& paint,
-                                       DrawCategory category) {
-    if (category != fLastDrawCategory) {
-        flushDrawBuffer();
-        fLastDrawCategory = category;
-    }
-    this->setPaint(paint, fGpu);
-    GrDrawTarget* target = fGpu;
-    switch (category) {
-    case kText_DrawCategory:
-#if DEFER_TEXT_RENDERING
-        target = fDrawBuffer;
-        fDrawBuffer->initializeDrawStateAndClip(*fGpu);
-#else
-        target = fGpu;
 #endif
-        break;
-    case kUnbuffered_DrawCategory:
-        target = fGpu;
-        break;
-    case kBuffered_DrawCategory:
-        target = fDrawBuffer;
-        fDrawBuffer->initializeDrawStateAndClip(*fGpu);
-        break;
     }
-    return target;
+    if (kYes_BufferedDraw == buffered) {
+        fDrawBuffer->setClip(fGpu->getClip());
+        fLastDrawWasBuffered = kYes_BufferedDraw;
+        return fDrawBuffer;
+    } else {
+        GrAssert(kNo_BufferedDraw == buffered);
+        return fGpu;
+    }
 }
 
-GrPathRenderer* GrContext::getPathRenderer(const GrPath& path,
-                                           GrPathFill fill,
+/*
+ * This method finds a path renderer that can draw the specified path on
+ * the provided target.
+ * Due to its expense, the software path renderer has split out so it can
+ * can be individually allowed/disallowed via the "allowSW" boolean.
+ */
+GrPathRenderer* GrContext::getPathRenderer(const SkPath& path,
+                                           const SkStrokeRec& stroke,
                                            const GrDrawTarget* target,
-                                           bool antiAlias) {
+                                           bool allowSW,
+                                           GrPathRendererChain::DrawType drawType,
+                                           GrPathRendererChain::StencilSupport* stencilSupport) {
+
     if (NULL == fPathRendererChain) {
-        fPathRendererChain = 
-            new GrPathRendererChain(this, GrPathRendererChain::kNone_UsageFlag);
+        fPathRendererChain = SkNEW_ARGS(GrPathRendererChain, (this));
     }
-    return fPathRendererChain->getPathRenderer(path, fill, target, antiAlias);
+
+    GrPathRenderer* pr = fPathRendererChain->getPathRenderer(path,
+                                                             stroke,
+                                                             target,
+                                                             drawType,
+                                                             stencilSupport);
+
+    if (NULL == pr && allowSW) {
+        if (NULL == fSoftwarePathRenderer) {
+            fSoftwarePathRenderer = SkNEW_ARGS(GrSoftwarePathRenderer, (this));
+        }
+        pr = fSoftwarePathRenderer;
+    }
+
+    return pr;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrContext::setRenderTarget(GrRenderTarget* target) {
     ASSERT_OWNED_RESOURCE(target);
-    this->flush(false);
-    fGpu->drawState()->setRenderTarget(target);
+    fDrawState->setRenderTarget(target);
 }
 
 GrRenderTarget* GrContext::getRenderTarget() {
-    return fGpu->drawState()->getRenderTarget();
+    return fDrawState->getRenderTarget();
 }
 
 const GrRenderTarget* GrContext::getRenderTarget() const {
-    return fGpu->getDrawState().getRenderTarget();
+    return fDrawState->getRenderTarget();
 }
 
-const GrMatrix& GrContext::getMatrix() const {
-    return fGpu->getDrawState().getViewMatrix();
+bool GrContext::isConfigRenderable(GrPixelConfig config) const {
+    return fGpu->isConfigRenderable(config);
 }
 
-void GrContext::setMatrix(const GrMatrix& m) {
-    fGpu->drawState()->setViewMatrix(m);
+const SkMatrix& GrContext::getMatrix() const {
+    return fDrawState->getViewMatrix();
 }
 
-void GrContext::concatMatrix(const GrMatrix& m) const {
-    fGpu->drawState()->preConcatViewMatrix(m);
+void GrContext::setMatrix(const SkMatrix& m) {
+    fDrawState->setViewMatrix(m);
+}
+
+void GrContext::setIdentityMatrix() {
+    fDrawState->viewMatrix()->reset();
+}
+
+void GrContext::concatMatrix(const SkMatrix& m) const {
+    fDrawState->preConcatViewMatrix(m);
 }
 
 static inline intptr_t setOrClear(intptr_t bits, int shift, intptr_t pred) {
@@ -1929,38 +1741,34 @@
     return bits;
 }
 
-void GrContext::resetStats() {
-    fGpu->resetStats();
-}
-
-const GrGpuStats& GrContext::getStats() const {
-    return fGpu->getStats();
-}
-
-void GrContext::printStats() const {
-    fGpu->printStats();
-}
-
 GrContext::GrContext(GrGpu* gpu) {
+    ++THREAD_INSTANCE_COUNT;
+
     fGpu = gpu;
     fGpu->ref();
     fGpu->setContext(this);
 
+    fDrawState = SkNEW(GrDrawState);
+    fGpu->setDrawState(fDrawState);
+
     fPathRendererChain = NULL;
+    fSoftwarePathRenderer = NULL;
 
-    fTextureCache = new GrResourceCache(MAX_TEXTURE_CACHE_COUNT,
-                                        MAX_TEXTURE_CACHE_BYTES);
-    fFontCache = new GrFontCache(fGpu);
+    fTextureCache = SkNEW_ARGS(GrResourceCache,
+                               (MAX_TEXTURE_CACHE_COUNT,
+                                MAX_TEXTURE_CACHE_BYTES));
+    fFontCache = SkNEW_ARGS(GrFontCache, (fGpu));
 
-    fLastDrawCategory = kUnbuffered_DrawCategory;
+    fLastDrawWasBuffered = kNo_BufferedDraw;
 
     fDrawBuffer = NULL;
     fDrawBufferVBAllocPool = NULL;
     fDrawBufferIBAllocPool = NULL;
 
-    fAAFillRectIndexBuffer = NULL;
-    fAAStrokeRectIndexBuffer = NULL;
-    
+    fAARectRenderer = SkNEW(GrAARectRenderer);
+
+    fDidTestPMConversions = false;
+
     this->setupDrawBuffer();
 }
 
@@ -1970,87 +1778,210 @@
     GrAssert(NULL == fDrawBufferVBAllocPool);
     GrAssert(NULL == fDrawBufferIBAllocPool);
 
-#if DEFER_TEXT_RENDERING || BATCH_RECT_TO_RECT
     fDrawBufferVBAllocPool =
-        new GrVertexBufferAllocPool(fGpu, false,
+        SkNEW_ARGS(GrVertexBufferAllocPool, (fGpu, false,
                                     DRAW_BUFFER_VBPOOL_BUFFER_SIZE,
-                                    DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS);
+                                    DRAW_BUFFER_VBPOOL_PREALLOC_BUFFERS));
     fDrawBufferIBAllocPool =
-        new GrIndexBufferAllocPool(fGpu, false,
+        SkNEW_ARGS(GrIndexBufferAllocPool, (fGpu, false,
                                    DRAW_BUFFER_IBPOOL_BUFFER_SIZE,
-                                   DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS);
+                                   DRAW_BUFFER_IBPOOL_PREALLOC_BUFFERS));
 
-    fDrawBuffer = new GrInOrderDrawBuffer(fGpu,
+    fDrawBuffer = SkNEW_ARGS(GrInOrderDrawBuffer, (fGpu,
                                           fDrawBufferVBAllocPool,
-                                          fDrawBufferIBAllocPool);
-#endif
+                                          fDrawBufferIBAllocPool));
 
-#if BATCH_RECT_TO_RECT
-    fDrawBuffer->setQuadIndexBuffer(this->getQuadIndexBuffer());
-#endif
+    if (fDrawBuffer) {
+        fDrawBuffer->setAutoFlushTarget(fGpu);
+        fDrawBuffer->setDrawState(fDrawState);
+    }
 }
 
 GrDrawTarget* GrContext::getTextTarget(const GrPaint& paint) {
-    GrDrawTarget* target;
-#if DEFER_TEXT_RENDERING
-    target = prepareToDraw(paint, kText_DrawCategory);
-#else
-    target = prepareToDraw(paint, kUnbuffered_DrawCategory);
-#endif
-    this->setPaint(paint, target);
-    return target;
+    return prepareToDraw(&paint, DEFAULT_BUFFERING);
 }
 
 const GrIndexBuffer* GrContext::getQuadIndexBuffer() const {
     return fGpu->getQuadIndexBuffer();
 }
 
-void GrContext::convolve(GrTexture* texture,
-                         const SkRect& rect,
-                         const float* kernel,
-                         int kernelWidth,
-                         GrSamplerState::FilterDirection direction) {
-    ASSERT_OWNED_RESOURCE(texture);
-
-    GrDrawTarget::AutoStateRestore asr(fGpu);
-    GrDrawState* drawState = fGpu->drawState();
-    GrRenderTarget* target = drawState->getRenderTarget();
-    drawState->reset();
-    drawState->setRenderTarget(target);
-    GrMatrix sampleM;
-    sampleM.setIDiv(texture->width(), texture->height());
-    drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
-                                 GrSamplerState::kConvolution_Filter,
-                                 sampleM);
-    drawState->sampler(0)->setConvolutionParams(kernelWidth, kernel);
-    drawState->sampler(0)->setFilterDirection(direction);
-    drawState->setTexture(0, texture);
-    fGpu->drawSimpleRect(rect, NULL, 1 << 0);
+namespace {
+void test_pm_conversions(GrContext* ctx, int* pmToUPMValue, int* upmToPMValue) {
+    GrConfigConversionEffect::PMConversion pmToUPM;
+    GrConfigConversionEffect::PMConversion upmToPM;
+    GrConfigConversionEffect::TestForPreservingPMConversions(ctx, &pmToUPM, &upmToPM);
+    *pmToUPMValue = pmToUPM;
+    *upmToPMValue = upmToPM;
+}
 }
 
-void GrContext::applyMorphology(GrTexture* texture,
-                                const SkRect& rect,
-                                int radius,
-                                GrSamplerState::Filter filter,
-                                GrSamplerState::FilterDirection direction) {
-    ASSERT_OWNED_RESOURCE(texture);
-    GrAssert(filter == GrSamplerState::kErode_Filter ||
-             filter == GrSamplerState::kDilate_Filter);
+const GrEffectRef* GrContext::createPMToUPMEffect(GrTexture* texture,
+                                                  bool swapRAndB,
+                                                  const SkMatrix& matrix) {
+    if (!fDidTestPMConversions) {
+        test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
+        fDidTestPMConversions = true;
+    }
+    GrConfigConversionEffect::PMConversion pmToUPM =
+        static_cast<GrConfigConversionEffect::PMConversion>(fPMToUPMConversion);
+    if (GrConfigConversionEffect::kNone_PMConversion != pmToUPM) {
+        return GrConfigConversionEffect::Create(texture, swapRAndB, pmToUPM, matrix);
+    } else {
+        return NULL;
+    }
+}
 
-    GrDrawTarget::AutoStateRestore asr(fGpu);
-    GrDrawState* drawState = fGpu->drawState();
-    GrRenderTarget* target = drawState->getRenderTarget();
-    drawState->reset();
-    drawState->setRenderTarget(target);
-    GrMatrix sampleM;
-    sampleM.setIDiv(texture->width(), texture->height());
-    drawState->sampler(0)->reset(GrSamplerState::kClamp_WrapMode,
-                                 filter,
-                                 sampleM);
-    drawState->sampler(0)->setMorphologyRadius(radius);
-    drawState->sampler(0)->setFilterDirection(direction);
-    drawState->setTexture(0, texture);
-    fGpu->drawSimpleRect(rect, NULL, 1 << 0);
+const GrEffectRef* GrContext::createUPMToPMEffect(GrTexture* texture,
+                                                  bool swapRAndB,
+                                                  const SkMatrix& matrix) {
+    if (!fDidTestPMConversions) {
+        test_pm_conversions(this, &fPMToUPMConversion, &fUPMToPMConversion);
+        fDidTestPMConversions = true;
+    }
+    GrConfigConversionEffect::PMConversion upmToPM =
+        static_cast<GrConfigConversionEffect::PMConversion>(fUPMToPMConversion);
+    if (GrConfigConversionEffect::kNone_PMConversion != upmToPM) {
+        return GrConfigConversionEffect::Create(texture, swapRAndB, upmToPM, matrix);
+    } else {
+        return NULL;
+    }
+}
+
+GrTexture* GrContext::gaussianBlur(GrTexture* srcTexture,
+                                   bool canClobberSrc,
+                                   const SkRect& rect,
+                                   float sigmaX, float sigmaY) {
+    ASSERT_OWNED_RESOURCE(srcTexture);
+
+    AutoRenderTarget art(this);
+
+    AutoMatrix am;
+    am.setIdentity(this);
+
+    SkIRect clearRect;
+    int scaleFactorX, radiusX;
+    int scaleFactorY, radiusY;
+    sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX);
+    sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY);
+
+    SkRect srcRect(rect);
+    scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
+    srcRect.roundOut();
+    scale_rect(&srcRect, static_cast<float>(scaleFactorX),
+                         static_cast<float>(scaleFactorY));
+
+    AutoClip acs(this, srcRect);
+
+    GrAssert(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
+             kRGBA_8888_GrPixelConfig == srcTexture->config() ||
+             kAlpha_8_GrPixelConfig == srcTexture->config());
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fWidth = SkScalarFloorToInt(srcRect.width());
+    desc.fHeight = SkScalarFloorToInt(srcRect.height());
+    desc.fConfig = srcTexture->config();
+
+    GrAutoScratchTexture temp1, temp2;
+    GrTexture* dstTexture = temp1.set(this, desc);
+    GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(this, desc);
+    if (NULL == dstTexture || NULL == tempTexture) {
+        return NULL;
+    }
+
+    GrPaint paint;
+    paint.reset();
+
+    for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
+        SkMatrix matrix;
+        matrix.setIDiv(srcTexture->width(), srcTexture->height());
+        this->setRenderTarget(dstTexture->asRenderTarget());
+        SkRect dstRect(srcRect);
+        scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
+                             i < scaleFactorY ? 0.5f : 1.0f);
+
+        paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(srcTexture,
+                                                                     matrix,
+                                                                     true))->unref();
+        this->drawRectToRect(paint, dstRect, srcRect);
+        srcRect = dstRect;
+        srcTexture = dstTexture;
+        SkTSwap(dstTexture, tempTexture);
+    }
+
+    SkIRect srcIRect;
+    srcRect.roundOut(&srcIRect);
+
+    if (sigmaX > 0.0f) {
+        if (scaleFactorX > 1) {
+            // Clear out a radius to the right of the srcRect to prevent the
+            // X convolution from reading garbage.
+            clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
+                                          radiusX, srcIRect.height());
+            this->clear(&clearRect, 0x0);
+        }
+
+        this->setRenderTarget(dstTexture->asRenderTarget());
+        GrDrawTarget* target = this->prepareToDraw(NULL, DEFAULT_BUFFERING);
+        convolve_gaussian(target, srcTexture, srcRect, sigmaX, radiusX,
+                          Gr1DKernelEffect::kX_Direction);
+        srcTexture = dstTexture;
+        SkTSwap(dstTexture, tempTexture);
+    }
+
+    if (sigmaY > 0.0f) {
+        if (scaleFactorY > 1 || sigmaX > 0.0f) {
+            // Clear out a radius below the srcRect to prevent the Y
+            // convolution from reading garbage.
+            clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
+                                          srcIRect.width(), radiusY);
+            this->clear(&clearRect, 0x0);
+        }
+
+        this->setRenderTarget(dstTexture->asRenderTarget());
+        GrDrawTarget* target = this->prepareToDraw(NULL, DEFAULT_BUFFERING);
+        convolve_gaussian(target, srcTexture, srcRect, sigmaY, radiusY,
+                          Gr1DKernelEffect::kY_Direction);
+        srcTexture = dstTexture;
+        SkTSwap(dstTexture, tempTexture);
+    }
+
+    if (scaleFactorX > 1 || scaleFactorY > 1) {
+        // Clear one pixel to the right and below, to accommodate bilinear
+        // upsampling.
+        clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
+                                      srcIRect.width() + 1, 1);
+        this->clear(&clearRect, 0x0);
+        clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
+                                      1, srcIRect.height());
+        this->clear(&clearRect, 0x0);
+        SkMatrix matrix;
+        // FIXME:  This should be mitchell, not bilinear.
+        matrix.setIDiv(srcTexture->width(), srcTexture->height());
+        this->setRenderTarget(dstTexture->asRenderTarget());
+        paint.colorStage(0)->setEffect(GrSimpleTextureEffect::Create(srcTexture,
+                                                                     matrix,
+                                                                     true))->unref();
+        SkRect dstRect(srcRect);
+        scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
+        this->drawRectToRect(paint, dstRect, srcRect);
+        srcRect = dstRect;
+        srcTexture = dstTexture;
+        SkTSwap(dstTexture, tempTexture);
+    }
+    if (srcTexture == temp1.texture()) {
+        return temp1.detach();
+    } else if (srcTexture == temp2.texture()) {
+        return temp2.detach();
+    } else {
+        srcTexture->ref();
+        return srcTexture;
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+#if GR_CACHE_STATS
+void GrContext::printCacheStats() const {
+    fTextureCache->printStats();
+}
+#endif
diff --git a/src/gpu/GrDefaultPathRenderer.cpp b/src/gpu/GrDefaultPathRenderer.cpp
index 72b3c60..dbed783 100644
--- a/src/gpu/GrDefaultPathRenderer.cpp
+++ b/src/gpu/GrDefaultPathRenderer.cpp
@@ -12,6 +12,7 @@
 #include "GrDrawState.h"
 #include "GrPathUtils.h"
 #include "SkString.h"
+#include "SkStrokeRec.h"
 #include "SkTrace.h"
 
 
@@ -150,31 +151,36 @@
 
 #define STENCIL_OFF     0   // Always disable stencil (even when needed)
 
-static inline bool single_pass_path(const GrPath& path, GrPathFill fill) {
+static inline bool single_pass_path(const SkPath& path, const SkStrokeRec& stroke) {
 #if STENCIL_OFF
     return true;
 #else
-    if (kEvenOdd_PathFill == fill || kWinding_PathFill == fill) {
+    if (!stroke.isHairlineStyle() && !path.isInverseFillType()) {
         return path.isConvex();
     }
     return false;
 #endif
 }
 
-bool GrDefaultPathRenderer::requiresStencilPass(const SkPath& path,
-                                                GrPathFill fill,
-                                                const GrDrawTarget* target) const {
-    return !single_pass_path(path, fill);
+GrPathRenderer::StencilSupport GrDefaultPathRenderer::onGetStencilSupport(
+                                                            const SkPath& path,
+                                                            const SkStrokeRec& stroke,
+                                                            const GrDrawTarget*) const {
+    if (single_pass_path(path, stroke)) {
+        return GrPathRenderer::kNoRestriction_StencilSupport;
+    } else {
+        return GrPathRenderer::kStencilOnly_StencilSupport;
+    }
 }
 
-static inline void append_countour_edge_indices(GrPathFill fillType,
+static inline void append_countour_edge_indices(bool hairLine,
                                                 uint16_t fanCenterIdx,
                                                 uint16_t edgeV0Idx,
                                                 uint16_t** indices) {
     // when drawing lines we're appending line segments along
     // the contour. When applying the other fill rules we're
     // drawing triangle fans around fanCenterIdx.
-    if (kHairLine_PathFill != fillType) {
+    if (!hairLine) {
         *((*indices)++) = fanCenterIdx;
     }
     *((*indices)++) = edgeV0Idx;
@@ -182,18 +188,17 @@
 }
 
 bool GrDefaultPathRenderer::createGeom(const SkPath& path,
-                                       GrPathFill fill,
-                                       const GrVec* translate,
-                                       GrScalar srcSpaceTol,
+                                       const SkStrokeRec& stroke,
+                                       SkScalar srcSpaceTol,
                                        GrDrawTarget* target,
-                                       GrDrawState::StageMask stageMask,
                                        GrPrimitiveType* primType,
                                        int* vertexCnt,
-                                       int* indexCnt) {
+                                       int* indexCnt,
+                                       GrDrawTarget::AutoReleaseGeometry* arg) {
     {
     SK_TRACE_EVENT0("GrDefaultPathRenderer::createGeom");
 
-    GrScalar srcSpaceTolSqd = GrMul(srcSpaceTol, srcSpaceTol);
+    SkScalar srcSpaceTolSqd = SkScalarMul(srcSpaceTol, srcSpaceTol);
     int contourCnt;
     int maxPts = GrPathUtils::worstCasePointCount(path, &contourCnt,
                                                   srcSpaceTol);
@@ -207,50 +212,40 @@
     }
 
     GrVertexLayout layout = 0;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if ((1 << s) & stageMask) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
-        }
-    }
-
     bool indexed = contourCnt > 1;
 
+    const bool isHairline = stroke.isHairlineStyle();
+
     int maxIdxs = 0;
-    if (kHairLine_PathFill == fill) {
+    if (isHairline) {
         if (indexed) {
             maxIdxs = 2 * maxPts;
-            *primType = kLines_PrimitiveType;
+            *primType = kLines_GrPrimitiveType;
         } else {
-            *primType = kLineStrip_PrimitiveType;
+            *primType = kLineStrip_GrPrimitiveType;
         }
     } else {
         if (indexed) {
             maxIdxs = 3 * maxPts;
-            *primType = kTriangles_PrimitiveType;
+            *primType = kTriangles_GrPrimitiveType;
         } else {
-            *primType = kTriangleFan_PrimitiveType;
+            *primType = kTriangleFan_GrPrimitiveType;
         }
     }
 
-    GrPoint* base;
-    if (!target->reserveVertexSpace(layout, maxPts, (void**)&base)) {
+
+    if (!arg->set(target, layout, maxPts, maxIdxs)) {
         return false;
     }
+
+    uint16_t* idxBase = reinterpret_cast<uint16_t*>(arg->indices());
+    uint16_t* idx = idxBase;
+    uint16_t subpathIdxStart = 0;
+
+    GrPoint* base = reinterpret_cast<GrPoint*>(arg->vertices());
     GrAssert(NULL != base);
     GrPoint* vert = base;
 
-    uint16_t* idxBase = NULL;
-    uint16_t* idx = NULL;
-    uint16_t subpathIdxStart = 0;
-    if (indexed) {
-        if (!target->reserveIndexSpace(maxIdxs, (void**)&idxBase)) {
-            target->resetVertexSource();
-            return false;
-        }
-        GrAssert(NULL != idxBase);
-        idx = idxBase;
-    }
-
     GrPoint pts[4];
 
     bool first = true;
@@ -273,7 +268,7 @@
             case kLine_PathCmd:
                 if (indexed) {
                     uint16_t prevIdx = (uint16_t)(vert - base) - 1;
-                    append_countour_edge_indices(fill, subpathIdxStart,
+                    append_countour_edge_indices(isHairline, subpathIdxStart,
                                                  prevIdx, &idx);
                 }
                 *(vert++) = pts[1];
@@ -281,14 +276,14 @@
             case kQuadratic_PathCmd: {
                 // first pt of quad is the pt we ended on in previous step
                 uint16_t firstQPtIdx = (uint16_t)(vert - base) - 1;
-                uint16_t numPts =  (uint16_t) 
+                uint16_t numPts =  (uint16_t)
                     GrPathUtils::generateQuadraticPoints(
                             pts[0], pts[1], pts[2],
                             srcSpaceTolSqd, &vert,
                             GrPathUtils::quadraticPointCount(pts, srcSpaceTol));
                 if (indexed) {
                     for (uint16_t i = 0; i < numPts; ++i) {
-                        append_countour_edge_indices(fill, subpathIdxStart,
+                        append_countour_edge_indices(isHairline, subpathIdxStart,
                                                      firstQPtIdx + i, &idx);
                     }
                 }
@@ -303,7 +298,7 @@
                                 GrPathUtils::cubicPointCount(pts, srcSpaceTol));
                 if (indexed) {
                     for (uint16_t i = 0; i < numPts; ++i) {
-                        append_countour_edge_indices(fill, subpathIdxStart,
+                        append_countour_edge_indices(isHairline, subpathIdxStart,
                                                      firstCPtIdx + i, &idx);
                     }
                 }
@@ -312,7 +307,7 @@
             case kClose_PathCmd:
                 break;
             case kEnd_PathCmd:
-                uint16_t currIdx = (uint16_t) (vert - base);
+             // uint16_t currIdx = (uint16_t) (vert - base);
                 goto FINISHED;
         }
         first = false;
@@ -324,46 +319,37 @@
     *vertexCnt = vert - base;
     *indexCnt = idx - idxBase;
 
-    if (NULL != translate && 
-        (translate->fX || translate->fY)) {
-        int count = vert - base;
-        for (int i = 0; i < count; i++) {
-            base[i].offset(translate->fX, translate->fY);
-        }
-    }
     }
     return true;
 }
 
 bool GrDefaultPathRenderer::internalDrawPath(const SkPath& path,
-                                             GrPathFill fill,
-                                             const GrVec* translate,
+                                             const SkStrokeRec& stroke,
                                              GrDrawTarget* target,
-                                             GrDrawState::StageMask stageMask,
                                              bool stencilOnly) {
 
-    GrMatrix viewM = target->getDrawState().getViewMatrix();
-    GrScalar tol = GR_Scalar1;
+    SkMatrix viewM = target->getDrawState().getViewMatrix();
+    SkScalar tol = SK_Scalar1;
     tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds());
-    GrDrawState* drawState = target->drawState();
 
     int vertexCnt;
     int indexCnt;
     GrPrimitiveType primType;
+    GrDrawTarget::AutoReleaseGeometry arg;
     if (!this->createGeom(path,
-                          fill,
-                          translate,
+                          stroke,
                           tol,
                           target,
-                          stageMask,
                           &primType,
                           &vertexCnt,
-                          &indexCnt)) {
+                          &indexCnt,
+                          &arg)) {
         return false;
     }
 
     GrAssert(NULL != target);
-    GrDrawTarget::AutoStateRestore asr(target);
+    GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit);
+    GrDrawState* drawState = target->drawState();
     bool colorWritesWereDisabled = drawState->isColorWriteDisabled();
     // face culling doesn't make sense here
     GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace());
@@ -374,7 +360,7 @@
     bool                        reverse = false;
     bool                        lastPassIsBounds;
 
-    if (kHairLine_PathFill == fill) {
+    if (stroke.isHairlineStyle()) {
         passCount = 1;
         if (stencilOnly) {
             passes[0] = &gDirectToStencil;
@@ -384,7 +370,7 @@
         lastPassIsBounds = false;
         drawFace[0] = GrDrawState::kBoth_DrawFace;
     } else {
-        if (single_pass_path(path, fill)) {
+        if (single_pass_path(path, stroke)) {
             passCount = 1;
             if (stencilOnly) {
                 passes[0] = &gDirectToStencil;
@@ -394,11 +380,11 @@
             drawFace[0] = GrDrawState::kBoth_DrawFace;
             lastPassIsBounds = false;
         } else {
-            switch (fill) {
-                case kInverseEvenOdd_PathFill:
+            switch (path.getFillType()) {
+                case SkPath::kInverseEvenOdd_FillType:
                     reverse = true;
                     // fallthrough
-                case kEvenOdd_PathFill:
+                case SkPath::kEvenOdd_FillType:
                     passes[0] = &gEOStencilPass;
                     if (stencilOnly) {
                         passCount = 1;
@@ -415,10 +401,10 @@
                     drawFace[0] = drawFace[1] = GrDrawState::kBoth_DrawFace;
                     break;
 
-                case kInverseWinding_PathFill:
+                case SkPath::kInverseWinding_FillType:
                     reverse = true;
                     // fallthrough
-                case kWinding_PathFill:
+                case SkPath::kWinding_FillType:
                     if (fSeparateStencil) {
                         if (fStencilWrapOps) {
                             passes[0] = &gWindStencilSeparateWithWrap;
@@ -472,41 +458,32 @@
                 drawState->disableState(GrDrawState::kNoColorWrites_StateBit);
             }
             GrRect bounds;
+            GrDrawState::AutoDeviceCoordDraw adcd;
             if (reverse) {
                 GrAssert(NULL != drawState->getRenderTarget());
                 // draw over the whole world.
                 bounds.setLTRB(0, 0,
-                               GrIntToScalar(drawState->getRenderTarget()->width()),
-                               GrIntToScalar(drawState->getRenderTarget()->height()));
-                GrMatrix vmi;
+                               SkIntToScalar(drawState->getRenderTarget()->width()),
+                               SkIntToScalar(drawState->getRenderTarget()->height()));
+                SkMatrix vmi;
                 // mapRect through persp matrix may not be correct
                 if (!drawState->getViewMatrix().hasPerspective() &&
                     drawState->getViewInverse(&vmi)) {
                     vmi.mapRect(&bounds);
                 } else {
-                    if (stageMask) {
-                        if (!drawState->getViewInverse(&vmi)) {
-                            GrPrintf("Could not invert matrix.");
-                            return false;
-                        }
-                        drawState->preConcatSamplerMatrices(stageMask, vmi);
-                    }
-                    drawState->setViewMatrix(GrMatrix::I());
+                    adcd.set(drawState);
                 }
             } else {
                 bounds = path.getBounds();
-                if (NULL != translate) {
-                    bounds.offset(*translate);
-                }
             }
             GrDrawTarget::AutoGeometryPush agp(target);
-            target->drawSimpleRect(bounds, NULL, stageMask);
+            target->drawSimpleRect(bounds, NULL);
         } else {
             if (passCount > 1) {
                 drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
             }
             if (indexCnt) {
-                target->drawIndexed(primType, 0, 0, 
+                target->drawIndexed(primType, 0, 0,
                                     vertexCnt, indexCnt);
             } else {
                 target->drawNonIndexed(primType, 0, vertexCnt);
@@ -518,32 +495,27 @@
 }
 
 bool GrDefaultPathRenderer::canDrawPath(const SkPath& path,
-                                        GrPathFill fill,
+                                        const SkStrokeRec& stroke,
                                         const GrDrawTarget* target,
                                         bool antiAlias) const {
-    // this class can draw any path with any fill but doesn't do any 
-    // anti-aliasing.
-    return !antiAlias;
+    // this class can draw any path with any fill but doesn't do any anti-aliasing.
+    return (stroke.isFillStyle() || stroke.isHairlineStyle()) && !antiAlias;
 }
 
 bool GrDefaultPathRenderer::onDrawPath(const SkPath& path,
-                                       GrPathFill fill,
-                                       const GrVec* translate,
+                                       const SkStrokeRec& stroke,
                                        GrDrawTarget* target,
-                                       GrDrawState::StageMask stageMask,
                                        bool antiAlias) {
     return this->internalDrawPath(path,
-                                  fill,
-                                  translate,
+                                  stroke,
                                   target,
-                                  stageMask,
                                   false);
 }
 
-void GrDefaultPathRenderer::drawPathToStencil(const SkPath& path,
-                                              GrPathFill fill,
-                                              GrDrawTarget* target) {
-    GrAssert(kInverseEvenOdd_PathFill != fill);
-    GrAssert(kInverseWinding_PathFill != fill);
-    this->internalDrawPath(path, fill, NULL, target, 0, true);
+void GrDefaultPathRenderer::onStencilPath(const SkPath& path,
+                                          const SkStrokeRec& stroke,
+                                          GrDrawTarget* target) {
+    GrAssert(SkPath::kInverseEvenOdd_FillType != path.getFillType());
+    GrAssert(SkPath::kInverseWinding_FillType != path.getFillType());
+    this->internalDrawPath(path, stroke, target, true);
 }
diff --git a/src/gpu/GrDefaultPathRenderer.h b/src/gpu/GrDefaultPathRenderer.h
index a6a4cda..e602fae 100644
--- a/src/gpu/GrDefaultPathRenderer.h
+++ b/src/gpu/GrDefaultPathRenderer.h
@@ -12,53 +12,46 @@
 #include "SkTemplates.h"
 
 /**
- *  Subclass that renders the path using the stencil buffer to resolve fill
- *  rules (e.g. winding, even-odd)
+ *  Subclass that renders the path using the stencil buffer to resolve fill rules
+ * (e.g. winding, even-odd)
  */
 class GR_API GrDefaultPathRenderer : public GrPathRenderer {
 public:
-    GrDefaultPathRenderer(bool separateStencilSupport,
-                          bool stencilWrapOpsSupport);
+    GrDefaultPathRenderer(bool separateStencilSupport, bool stencilWrapOpsSupport);
 
-
-    virtual bool requiresStencilPass(const SkPath& path,
-                                     GrPathFill fill,
-                                     const GrDrawTarget* target) const SK_OVERRIDE;
-
-    virtual bool canDrawPath(const SkPath& path,
-                            GrPathFill fill,
-                            const GrDrawTarget* target,
-                            bool antiAlias) const SK_OVERRIDE;
-
-    virtual void drawPathToStencil(const SkPath& path,
-                                   GrPathFill fill,
-                                   GrDrawTarget* target) SK_OVERRIDE;
+    virtual bool canDrawPath(const SkPath&,
+                             const SkStrokeRec&,
+                             const GrDrawTarget*,
+                             bool antiAlias) const SK_OVERRIDE;
 
 private:
 
-    virtual bool onDrawPath(const SkPath& path,
-                            GrPathFill fill,
-                            const GrVec* translate,
-                            GrDrawTarget* target,
-                            GrDrawState::StageMask stageMask,
+    virtual StencilSupport onGetStencilSupport(const SkPath&,
+                                               const SkStrokeRec&,
+                                               const GrDrawTarget*) const SK_OVERRIDE;
+
+    virtual bool onDrawPath(const SkPath&,
+                            const SkStrokeRec&,
+                            GrDrawTarget*,
                             bool antiAlias) SK_OVERRIDE;
 
-    bool internalDrawPath(const SkPath& path,
-                          GrPathFill fill,
-                          const GrVec* translate,
-                          GrDrawTarget* target,
-                          GrDrawState::StageMask stageMask,
+    virtual void onStencilPath(const SkPath&,
+                               const SkStrokeRec&,
+                               GrDrawTarget*) SK_OVERRIDE;
+
+    bool internalDrawPath(const SkPath&,
+                          const SkStrokeRec&,
+                          GrDrawTarget*,
                           bool stencilOnly);
 
-    bool createGeom(const SkPath& path,
-                    GrPathFill fill,
-                    const GrVec* translate,
-                    GrScalar srcSpaceTol,
-                    GrDrawTarget* target,
-                    GrDrawState::StageMask stages,
-                    GrPrimitiveType* primType,
+    bool createGeom(const SkPath&,
+                    const SkStrokeRec&,
+                    SkScalar srcSpaceTol,
+                    GrDrawTarget*,
+                    GrPrimitiveType*,
                     int* vertexCnt,
-                    int* indexCnt);
+                    int* indexCnt,
+                    GrDrawTarget::AutoReleaseGeometry*);
 
     bool    fSeparateStencil;
     bool    fStencilWrapOps;
diff --git a/src/gpu/GrDrawState.cpp b/src/gpu/GrDrawState.cpp
new file mode 100644
index 0000000..b6c521f
--- /dev/null
+++ b/src/gpu/GrDrawState.cpp
@@ -0,0 +1,642 @@
+/*
+ * 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 "GrDrawState.h"
+
+#include "GrGpuVertex.h"
+#include "GrPaint.h"
+
+void GrDrawState::setFromPaint(const GrPaint& paint) {
+    for (int i = 0; i < GrPaint::kMaxColorStages; ++i) {
+        int s = i + GrPaint::kFirstColorStage;
+        if (paint.isColorStageEnabled(i)) {
+            fStages[s] = paint.getColorStage(i);
+        } else {
+            fStages[s].setEffect(NULL);
+        }
+    }
+
+    this->setFirstCoverageStage(GrPaint::kFirstCoverageStage);
+
+    for (int i = 0; i < GrPaint::kMaxCoverageStages; ++i) {
+        int s = i + GrPaint::kFirstCoverageStage;
+        if (paint.isCoverageStageEnabled(i)) {
+            fStages[s] = paint.getCoverageStage(i);
+        } else {
+            fStages[s].setEffect(NULL);
+        }
+    }
+
+    // disable all stages not accessible via the paint
+    for (int s = GrPaint::kTotalStages; s < GrDrawState::kNumStages; ++s) {
+        this->disableStage(s);
+    }
+
+    this->setColor(paint.getColor());
+
+    this->setState(GrDrawState::kDither_StateBit, paint.isDither());
+    this->setState(GrDrawState::kHWAntialias_StateBit, paint.isAntiAlias());
+
+    this->setBlendFunc(paint.getSrcBlendCoeff(), paint.getDstBlendCoeff());
+    this->setColorFilter(paint.getColorFilterColor(), paint.getColorFilterMode());
+    this->setCoverage(paint.getCoverage());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+/**
+ * This function generates some masks that we like to have known at compile
+ * time. When the number of stages or tex coords is bumped or the way bits
+ * are defined in GrDrawState.h changes this function should be rerun to
+ * generate the new masks. (We attempted to force the compiler to generate the
+ * masks using recursive templates but always wound up with static initializers
+ * under gcc, even if they were just a series of immediate->memory moves.)
+ *
+ */
+void gen_mask_arrays(GrVertexLayout* stageTexCoordMasks,
+                     GrVertexLayout* texCoordMasks) {
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        stageTexCoordMasks[s] = 0;
+        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+            stageTexCoordMasks[s] |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
+        }
+    }
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        texCoordMasks[t] = 0;
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            texCoordMasks[t] |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
+        }
+    }
+}
+
+/**
+ * Uncomment and run the gen_globals function to generate
+ * the code that declares the global masks.
+ *
+ * #if 0'ed out to avoid unused function warning.
+ */
+
+#if 0
+void gen_globals() {
+    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
+    GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
+    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
+
+    GrPrintf("const GrVertexLayout gStageTexCoordMasks[] = {\n");
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrPrintf("    0x%x,\n", stageTexCoordMasks[s]);
+    }
+    GrPrintf("};\n");
+    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));\n\n");
+    GrPrintf("const GrVertexLayout gTexCoordMasks[] = {\n");
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        GrPrintf("    0x%x,\n", texCoordMasks[t]);
+    }
+    GrPrintf("};\n");
+    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));\n");
+}
+#endif
+
+/* These values were generated by the above function */
+
+const GrVertexLayout gStageTexCoordMasks[] = {
+    0x108421,
+    0x210842,
+    0x421084,
+    0x842108,
+    0x1084210,
+};
+GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));
+
+const GrVertexLayout gTexCoordMasks[] = {
+    0x1f,
+    0x3e0,
+    0x7c00,
+    0xf8000,
+    0x1f00000,
+};
+GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));
+
+#ifdef SK_DEBUG
+bool check_layout(GrVertexLayout layout) {
+    // can only have 1 or 0 bits set for each stage.
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        int stageBits = layout & gStageTexCoordMasks[s];
+        if (stageBits && !GrIsPow2(stageBits)) {
+            return false;
+        }
+    }
+    return true;
+}
+#endif
+
+int num_tex_coords(GrVertexLayout layout) {
+    int cnt = 0;
+    // figure out how many tex coordinates are present
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (gTexCoordMasks[t] & layout) {
+            ++cnt;
+        }
+    }
+    return cnt;
+}
+
+} //unnamed namespace
+
+size_t GrDrawState::VertexSize(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    size_t vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                        sizeof(GrGpuTextVertex) :
+                        sizeof(GrPoint);
+
+    size_t size = vecSize; // position
+    size += num_tex_coords(vertexLayout) * vecSize;
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        size += sizeof(GrColor);
+    }
+    if (vertexLayout & kEdge_VertexLayoutBit) {
+        size += 4 * sizeof(SkScalar);
+    }
+    return size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Functions for computing offsets of various components from the layout
+ * bitfield.
+ *
+ * Order of vertex components:
+ * Position
+ * Tex Coord 0
+ * ...
+ * Tex Coord GrDrawState::kMaxTexCoords-1
+ * Color
+ * Coverage
+ */
+
+int GrDrawState::VertexStageCoordOffset(int stageIdx, GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (!StageUsesTexCoords(vertexLayout, stageIdx)) {
+        return 0;
+    }
+    int tcIdx = VertexTexCoordsForStage(stageIdx, vertexLayout);
+    if (tcIdx >= 0) {
+
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize; // position
+        // figure out how many tex coordinates are present and precede this one.
+        for (int t = 0; t < tcIdx; ++t) {
+            if (gTexCoordMasks[t] & vertexLayout) {
+                offset += vecSize;
+            }
+        }
+        return offset;
+    }
+
+    return -1;
+}
+
+int GrDrawState::VertexColorOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (vertexLayout & kColor_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+    }
+    return -1;
+}
+
+int GrDrawState::VertexCoverageOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    if (vertexLayout & kCoverage_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+
+        int offset = vecSize * (num_tex_coords(vertexLayout) + 1);
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        return offset;
+    }
+    return -1;
+}
+
+int GrDrawState::VertexEdgeOffset(GrVertexLayout vertexLayout) {
+    GrAssert(check_layout(vertexLayout));
+
+    // edge pts are after the pos, tex coords, and color
+    if (vertexLayout & kEdge_VertexLayoutBit) {
+        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                    sizeof(GrGpuTextVertex) :
+                                    sizeof(GrPoint);
+        int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
+        if (vertexLayout & kColor_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        if (vertexLayout & kCoverage_VertexLayoutBit) {
+            offset += sizeof(GrColor);
+        }
+        return offset;
+    }
+    return -1;
+}
+
+int GrDrawState::VertexSizeAndOffsetsByIdx(
+        GrVertexLayout vertexLayout,
+        int texCoordOffsetsByIdx[kMaxTexCoords],
+        int* colorOffset,
+        int* coverageOffset,
+        int* edgeOffset) {
+    GrAssert(check_layout(vertexLayout));
+
+    int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
+                                                    sizeof(GrGpuTextVertex) :
+                                                    sizeof(GrPoint);
+    int size = vecSize; // position
+
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        if (gTexCoordMasks[t] & vertexLayout) {
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = size;
+            }
+            size += vecSize;
+        } else {
+            if (NULL != texCoordOffsetsByIdx) {
+                texCoordOffsetsByIdx[t] = -1;
+            }
+        }
+    }
+    if (kColor_VertexLayoutBit & vertexLayout) {
+        if (NULL != colorOffset) {
+            *colorOffset = size;
+        }
+        size += sizeof(GrColor);
+    } else {
+        if (NULL != colorOffset) {
+            *colorOffset = -1;
+        }
+    }
+    if (kCoverage_VertexLayoutBit & vertexLayout) {
+        if (NULL != coverageOffset) {
+            *coverageOffset = size;
+        }
+        size += sizeof(GrColor);
+    } else {
+        if (NULL != coverageOffset) {
+            *coverageOffset = -1;
+        }
+    }
+    if (kEdge_VertexLayoutBit & vertexLayout) {
+        if (NULL != edgeOffset) {
+            *edgeOffset = size;
+        }
+        size += 4 * sizeof(SkScalar);
+    } else {
+        if (NULL != edgeOffset) {
+            *edgeOffset = -1;
+        }
+    }
+    return size;
+}
+
+int GrDrawState::VertexSizeAndOffsetsByStage(
+        GrVertexLayout vertexLayout,
+        int texCoordOffsetsByStage[GrDrawState::kNumStages],
+        int* colorOffset,
+        int* coverageOffset,
+        int* edgeOffset) {
+    GrAssert(check_layout(vertexLayout));
+
+    int texCoordOffsetsByIdx[kMaxTexCoords];
+    int size = VertexSizeAndOffsetsByIdx(vertexLayout,
+                                         (NULL == texCoordOffsetsByStage) ?
+                                               NULL :
+                                               texCoordOffsetsByIdx,
+                                         colorOffset,
+                                         coverageOffset,
+                                         edgeOffset);
+    if (NULL != texCoordOffsetsByStage) {
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            int tcIdx = VertexTexCoordsForStage(s, vertexLayout);
+            texCoordOffsetsByStage[s] =
+                tcIdx < 0 ? 0 : texCoordOffsetsByIdx[tcIdx];
+        }
+    }
+    return size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool GrDrawState::VertexUsesTexCoordIdx(int coordIndex,
+                                         GrVertexLayout vertexLayout) {
+    GrAssert(coordIndex < kMaxTexCoords);
+    GrAssert(check_layout(vertexLayout));
+    return !!(gTexCoordMasks[coordIndex] & vertexLayout);
+}
+
+int GrDrawState::VertexTexCoordsForStage(int stageIdx,
+                                          GrVertexLayout vertexLayout) {
+    GrAssert(stageIdx < GrDrawState::kNumStages);
+    GrAssert(check_layout(vertexLayout));
+    int bit = vertexLayout & gStageTexCoordMasks[stageIdx];
+    if (bit) {
+        // figure out which set of texture coordates is used
+        // bits are ordered T0S0, T0S1, T0S2, ..., T1S0, T1S1, ...
+        // and start at bit 0.
+        GR_STATIC_ASSERT(sizeof(GrVertexLayout) <= sizeof(uint32_t));
+        return (32 - SkCLZ(bit) - 1) / GrDrawState::kNumStages;
+    }
+    return -1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawState::VertexLayoutUnitTest() {
+    // Ensure that our globals mask arrays are correct
+    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
+    GrVertexLayout texCoordMasks[kMaxTexCoords];
+    gen_mask_arrays(stageTexCoordMasks, texCoordMasks);
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        GrAssert(stageTexCoordMasks[s] == gStageTexCoordMasks[s]);
+    }
+    for (int t = 0; t < kMaxTexCoords; ++t) {
+        GrAssert(texCoordMasks[t] == gTexCoordMasks[t]);
+    }
+
+    // not necessarily exhaustive
+    static bool run;
+    if (!run) {
+        run = true;
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+
+            GrVertexLayout stageMask = 0;
+            for (int t = 0; t < kMaxTexCoords; ++t) {
+                stageMask |= StageTexCoordVertexLayoutBit(s,t);
+            }
+            GrAssert(1 == kMaxTexCoords ||
+                     !check_layout(stageMask));
+            GrAssert(gStageTexCoordMasks[s] == stageMask);
+            GrAssert(!check_layout(stageMask));
+        }
+        for (int t = 0; t < kMaxTexCoords; ++t) {
+            GrVertexLayout tcMask = 0;
+            GrAssert(!VertexUsesTexCoordIdx(t, 0));
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                tcMask |= StageTexCoordVertexLayoutBit(s,t);
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+                GrAssert(VertexUsesTexCoordIdx(t, tcMask));
+                GrAssert(2*sizeof(GrPoint) == VertexSize(tcMask));
+                GrAssert(t == VertexTexCoordsForStage(s, tcMask));
+                for (int s2 = s + 1; s2 < GrDrawState::kNumStages; ++s2) {
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
+
+                #if GR_DEBUG
+                    GrVertexLayout posAsTex = tcMask;
+                #endif
+                    GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
+                    GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
+                    GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
+                    GrAssert(-1 == VertexEdgeOffset(posAsTex));
+                }
+                GrAssert(-1 == VertexEdgeOffset(tcMask));
+                GrAssert(-1 == VertexColorOffset(tcMask));
+                GrAssert(-1 == VertexCoverageOffset(tcMask));
+            #if GR_DEBUG
+                GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexCoverageOffset(withColor));
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
+            #if GR_DEBUG
+                GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexColorOffset(withEdge));
+                GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
+                GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
+            #if GR_DEBUG
+                GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
+            #endif
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
+                GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
+            #if GR_DEBUG
+                GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit;
+            #endif
+                GrAssert(-1 == VertexColorOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage));
+            #if GR_DEBUG
+                GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit |
+                                                      kColor_VertexLayoutBit;
+            #endif
+                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor));
+                GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor));
+            }
+            GrAssert(gTexCoordMasks[t] == tcMask);
+            GrAssert(check_layout(tcMask));
+
+            int stageOffsets[GrDrawState::kNumStages];
+            int colorOffset;
+            int edgeOffset;
+            int coverageOffset;
+            int size;
+            size = VertexSizeAndOffsetsByStage(tcMask,
+                                               stageOffsets, &colorOffset,
+                                               &coverageOffset, &edgeOffset);
+            GrAssert(2*sizeof(GrPoint) == size);
+            GrAssert(-1 == colorOffset);
+            GrAssert(-1 == coverageOffset);
+            GrAssert(-1 == edgeOffset);
+            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+                GrAssert(sizeof(GrPoint) == stageOffsets[s]);
+                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
+            }
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+bool GrDrawState::StageUsesTexCoords(GrVertexLayout layout, int stageIdx) {
+    return SkToBool(layout & gStageTexCoordMasks[stageIdx]);
+}
+
+bool GrDrawState::srcAlphaWillBeOne(GrVertexLayout layout) const {
+
+    uint32_t validComponentFlags;
+    GrColor  color;
+    // Check if per-vertex or constant color may have partial alpha
+    if (layout & kColor_VertexLayoutBit) {
+        validComponentFlags = 0;
+    } else {
+        validComponentFlags = GrEffect::kAll_ValidComponentFlags;
+        color = this->getColor();
+    }
+
+    // Run through the color stages
+    int stageCnt = getFirstCoverageStage();
+    for (int s = 0; s < stageCnt; ++s) {
+        const GrEffectRef* effect = this->getStage(s).getEffect();
+        if (NULL != effect) {
+            (*effect)->getConstantColorComponents(&color, &validComponentFlags);
+        }
+    }
+
+    // Check if the color filter could introduce an alpha.
+    // We could skip the above work when this is true, but it is rare and the right fix is to make
+    // the color filter a GrEffect and implement getConstantColorComponents() for it.
+    if (SkXfermode::kDst_Mode != this->getColorFilterMode()) {
+        validComponentFlags = 0;
+    }
+
+    // Check whether coverage is treated as color. If so we run through the coverage computation.
+    if (this->isCoverageDrawing()) {
+        GrColor coverageColor = this->getCoverage();
+        GrColor oldColor = color;
+        color = 0;
+        for (int c = 0; c < 4; ++c) {
+            if (validComponentFlags & (1 << c)) {
+                U8CPU a = (oldColor >> (c * 8)) & 0xff;
+                U8CPU b = (coverageColor >> (c * 8)) & 0xff;
+                color |= (SkMulDiv255Round(a, b) << (c * 8));
+            }
+        }
+        for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
+            const GrEffectRef* effect = this->getStage(s).getEffect();
+            if (NULL != effect) {
+                (*effect)->getConstantColorComponents(&color, &validComponentFlags);
+            }
+        }
+    }
+    return (GrEffect::kA_ValidComponentFlag & validComponentFlags) && 0xff == GrColorUnpackA(color);
+}
+
+bool GrDrawState::hasSolidCoverage(GrVertexLayout layout) const {
+    // If we're drawing coverage directly then coverage is effectively treated as color.
+    if (this->isCoverageDrawing()) {
+        return true;
+    }
+
+    GrColor coverage;
+    uint32_t validComponentFlags;
+    // Initialize to an unknown starting coverage if per-vertex coverage is specified.
+    if (layout & kCoverage_VertexLayoutBit) {
+        validComponentFlags = 0;
+    } else {
+        coverage = fCommon.fCoverage;
+        validComponentFlags = GrEffect::kAll_ValidComponentFlags;
+    }
+
+    // Run through the coverage stages and see if the coverage will be all ones at the end.
+    for (int s = this->getFirstCoverageStage(); s < GrDrawState::kNumStages; ++s) {
+        const GrEffectRef* effect = this->getStage(s).getEffect();
+        if (NULL != effect) {
+            (*effect)->getConstantColorComponents(&coverage, &validComponentFlags);
+        }
+    }
+    return (GrEffect::kAll_ValidComponentFlags == validComponentFlags)  && (0xffffffff == coverage);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawState::AutoViewMatrixRestore::restore() {
+    if (NULL != fDrawState) {
+        fDrawState->setViewMatrix(fViewMatrix);
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            if (fRestoreMask & (1 << s)) {
+                fDrawState->fStages[s].restoreCoordChange(fSavedCoordChanges[s]);
+            }
+        }
+    }
+    fDrawState = NULL;
+}
+
+void GrDrawState::AutoViewMatrixRestore::set(GrDrawState* drawState,
+                                             const SkMatrix& preconcatMatrix,
+                                             uint32_t explicitCoordStageMask) {
+    this->restore();
+
+    fDrawState = drawState;
+    if (NULL == drawState) {
+        return;
+    }
+
+    fRestoreMask = 0;
+    fViewMatrix = drawState->getViewMatrix();
+    drawState->preConcatViewMatrix(preconcatMatrix);
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if (!(explicitCoordStageMask & (1 << s)) && drawState->isStageEnabled(s)) {
+            fRestoreMask |= (1 << s);
+            fDrawState->fStages[s].saveCoordChange(&fSavedCoordChanges[s]);
+            drawState->fStages[s].preConcatCoordChange(preconcatMatrix);
+        }
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrDrawState::AutoDeviceCoordDraw::restore() {
+    if (NULL != fDrawState) {
+        fDrawState->setViewMatrix(fViewMatrix);
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            if (fRestoreMask & (1 << s)) {
+                fDrawState->fStages[s].restoreCoordChange(fSavedCoordChanges[s]);
+            }
+        }
+    }
+    fDrawState = NULL;
+}
+
+bool GrDrawState::AutoDeviceCoordDraw::set(GrDrawState* drawState,
+                                           uint32_t explicitCoordStageMask) {
+    GrAssert(NULL != drawState);
+
+    this->restore();
+
+    fDrawState = drawState;
+    if (NULL == fDrawState) {
+        return false;
+    }
+
+    fViewMatrix = drawState->getViewMatrix();
+    fRestoreMask = 0;
+    SkMatrix invVM;
+    bool inverted = false;
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if (!(explicitCoordStageMask & (1 << s)) && drawState->isStageEnabled(s)) {
+            if (!inverted && !fViewMatrix.invert(&invVM)) {
+                // sad trombone sound
+                fDrawState = NULL;
+                return false;
+            } else {
+                inverted = true;
+            }
+            fRestoreMask |= (1 << s);
+            GrEffectStage* stage = drawState->fStages + s;
+            stage->saveCoordChange(&fSavedCoordChanges[s]);
+            stage->preConcatCoordChange(invVM);
+        }
+    }
+    drawState->viewMatrix()->reset();
+    return true;
+}
diff --git a/src/gpu/GrDrawState.h b/src/gpu/GrDrawState.h
index 18dd6bf..0a0b0da 100644
--- a/src/gpu/GrDrawState.h
+++ b/src/gpu/GrDrawState.h
@@ -8,45 +8,60 @@
 #ifndef GrDrawState_DEFINED
 #define GrDrawState_DEFINED
 
+#include "GrBackendEffectFactory.h"
 #include "GrColor.h"
-#include "GrMatrix.h"
-#include "GrNoncopyable.h"
-#include "GrSamplerState.h"
+#include "GrEffectStage.h"
+#include "GrRefCnt.h"
+#include "GrRenderTarget.h"
 #include "GrStencil.h"
+#include "GrTemplates.h"
+#include "GrTexture.h"
+#include "effects/GrSimpleTextureEffect.h"
 
+#include "SkMatrix.h"
 #include "SkXfermode.h"
 
-class GrRenderTarget;
-class GrTexture;
+class GrPaint;
 
-struct GrDrawState {
+class GrDrawState : public GrRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(GrDrawState)
 
     /**
-     * Number of texture stages. Each stage takes as input a color and
-     * 2D texture coordinates. The color input to the first enabled stage is the
-     * per-vertex color or the constant color (setColor/setAlpha) if there are
-     * no per-vertex colors. For subsequent stages the input color is the output
-     * color from the previous enabled stage. The output color of each stage is
-     * the input color modulated with the result of a texture lookup. Texture
-     * lookups are specified by a texture a sampler (setSamplerState). Texture
-     * coordinates for each stage come from the vertices based on a
-     * GrVertexLayout bitfield. The output fragment color is the output color of
-     * the last enabled stage. The presence or absence of texture coordinates
-     * for each stage in the vertex layout indicates whether a stage is enabled
-     * or not.
+     * Total number of effect stages. Each stage can host a GrEffect. A stage is enabled if it has a
+     * GrEffect. The effect produces an output color in the fragment shader. It's inputs are the
+     * output from the previous enabled stage and a position. The position is either derived from
+     * the interpolated vertex positions or explicit per-vertex coords, depending upon the
+     * GrVertexLayout used to draw.
+     *
+     * The stages are divided into two sets, color-computing and coverage-computing. The final color
+     * stage produces the final pixel color. The coverage-computing stages function exactly as the
+     * color-computing but the output of the final coverage stage is treated as a fractional pixel
+     * coverage rather than as input to the src/dst color blend step.
+     *
+     * The input color to the first enabled color-stage is either the constant color or interpolated
+     * per-vertex colors, depending upon GrVertexLayout. The input to the first coverage stage is
+     * either a constant coverage (usually full-coverage), interpolated per-vertex coverage, or
+     * edge-AA computed coverage. (This latter is going away as soon as it can be rewritten as a
+     * GrEffect).
+     *
+     * See the documentation of kCoverageDrawing_StateBit for information about disabling the
+     * the color / coverage distinction.
+     *
+     * Stages 0 through GrPaint::kTotalStages-1 are reserved for stages copied from the client's
+     * GrPaint. Stages GrPaint::kTotalStages through kNumStages-2 are earmarked for use by
+     * GrTextContext and GrPathRenderer-derived classes. kNumStages-1 is earmarked for clipping
+     * by GrClipMaskManager.
      */
     enum {
-        kNumStages = 3,
+        kNumStages = 5,
         kMaxTexCoords = kNumStages
     };
 
-    /**
-     *  Bitfield used to indicate a set of stages.
-     */
-    typedef uint32_t StageMask;
-    GR_STATIC_ASSERT(sizeof(StageMask)*8 >= GrDrawState::kNumStages);
-
     GrDrawState() {
+#if GR_DEBUG
+        VertexLayoutUnitTest();
+#endif
         this->reset();
     }
 
@@ -54,44 +69,313 @@
         *this = state;
     }
 
-    /**
-     * Resets to the default state. Sampler states will not be modified.
-     */ 
-    void reset() {
-        // make sure any pad is zero for memcmp
-        // all GrDrawState members should default to something valid by the
-        // the memset except those initialized individually below. There should
-        // be no padding between the individually initialized members.
-        static const size_t kMemsetSize =
-            reinterpret_cast<intptr_t>(&fColor) -
-            reinterpret_cast<intptr_t>(this);
-        memset(this, 0, kMemsetSize);
-        // pedantic assertion that our ptrs will
-        // be NULL (0 ptr is mem addr 0)
-        GrAssert((intptr_t)(void*)NULL == 0LL);
-        GR_STATIC_ASSERT(0 == kBoth_DrawFace);
-        GrAssert(fStencilSettings.isDisabled());
-
-        // memset exceptions
-        fColor = 0xffffffff;
-        fCoverage = 0xffffffff;
-        fFirstCoverageStage = kNumStages;
-        fColorFilterMode = SkXfermode::kDst_Mode;
-        fSrcBlend = kOne_BlendCoeff;
-        fDstBlend = kZero_BlendCoeff;
-        fViewMatrix.reset();
-
-        // ensure values that will be memcmp'ed in == but not memset in reset()
-        // are tightly packed
-        GrAssert(kMemsetSize +  sizeof(fColor) + sizeof(fCoverage) +
-                 sizeof(fFirstCoverageStage) + sizeof(fColorFilterMode) +
-                 sizeof(fSrcBlend) + sizeof(fDstBlend) + sizeof(GrMatrix) ==
-                 reinterpret_cast<intptr_t>(&fEdgeAANumEdges) -
-                 reinterpret_cast<intptr_t>(this));
-
-        fEdgeAANumEdges = 0;
+    virtual ~GrDrawState() {
+        this->disableStages();
     }
 
+    /**
+     * Resets to the default state.
+     * GrEffects will be removed from all stages.
+     */
+    void reset() {
+
+        this->disableStages();
+
+        fRenderTarget.reset(NULL);
+
+        fCommon.fColor = 0xffffffff;
+        fCommon.fViewMatrix.reset();
+        fCommon.fSrcBlend = kOne_GrBlendCoeff;
+        fCommon.fDstBlend = kZero_GrBlendCoeff;
+        fCommon.fBlendConstant = 0x0;
+        fCommon.fFlagBits = 0x0;
+        fCommon.fVertexEdgeType = kHairLine_EdgeType;
+        fCommon.fStencilSettings.setDisabled();
+        fCommon.fFirstCoverageStage = kNumStages;
+        fCommon.fCoverage = 0xffffffff;
+        fCommon.fColorFilterMode = SkXfermode::kDst_Mode;
+        fCommon.fColorFilterColor = 0x0;
+        fCommon.fDrawFace = kBoth_DrawFace;
+    }
+
+    /**
+     * Initializes the GrDrawState based on a GrPaint. Note that GrDrawState
+     * encompasses more than GrPaint. Aspects of GrDrawState that have no
+     * GrPaint equivalents are not modified. GrPaint has fewer stages than
+     * GrDrawState. The extra GrDrawState stages are disabled.
+     */
+    void setFromPaint(const GrPaint& paint);
+
+    ///////////////////////////////////////////////////////////////////////////
+    /// @name Vertex Format
+    ////
+
+    /**
+     * The format of vertices is represented as a bitfield of flags.
+     * Flags that indicate the layout of vertex data. Vertices always contain
+     * positions and may also contain up to GrDrawState::kMaxTexCoords sets
+     * of 2D texture coordinates, per-vertex colors, and per-vertex coverage.
+     * Each stage can
+     * use any of the texture coordinates as its input texture coordinates or it
+     * may use the positions as texture coordinates.
+     *
+     * If no texture coordinates are specified for a stage then the stage is
+     * disabled.
+     *
+     * Only one type of texture coord can be specified per stage. For
+     * example StageTexCoordVertexLayoutBit(0, 2) and
+     * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified.
+     *
+     * The order in memory is always (position, texture coord 0, ..., color,
+     * coverage) with any unused fields omitted. Note that this means that if
+     * only texture coordinates 1 is referenced then there is no texture
+     * coordinates 0 and the order would be (position, texture coordinate 1
+     * [, color][, coverage]).
+     */
+
+    /**
+     * Generates a bit indicating that a texture stage uses texture coordinates
+     *
+     * @param stageIdx    the stage that will use texture coordinates.
+     * @param texCoordIdx the index of the texture coordinates to use
+     *
+     * @return the bit to add to a GrVertexLayout bitfield.
+     */
+    static int StageTexCoordVertexLayoutBit(int stageIdx, int texCoordIdx) {
+        GrAssert(stageIdx < kNumStages);
+        GrAssert(texCoordIdx < kMaxTexCoords);
+        return 1 << (stageIdx + (texCoordIdx * kNumStages));
+    }
+
+    static bool StageUsesTexCoords(GrVertexLayout layout, int stageIdx);
+
+private:
+    // non-stage bits start at this index.
+    static const int STAGE_BIT_CNT = kNumStages * kMaxTexCoords;
+public:
+
+    /**
+     * Additional Bits that can be specified in GrVertexLayout.
+     */
+    enum VertexLayoutBits {
+        /* vertices have colors (GrColor) */
+        kColor_VertexLayoutBit              = 1 << (STAGE_BIT_CNT + 0),
+        /* vertices have coverage (GrColor)
+         */
+        kCoverage_VertexLayoutBit           = 1 << (STAGE_BIT_CNT + 1),
+        /* Use text vertices. (Pos and tex coords may be a different type for
+         * text [GrGpuTextVertex vs GrPoint].)
+         */
+        kTextFormat_VertexLayoutBit         = 1 << (STAGE_BIT_CNT + 2),
+
+        /* Each vertex specificies an edge. Distance to the edge is used to
+         * compute a coverage. See GrDrawState::setVertexEdgeType().
+         */
+        kEdge_VertexLayoutBit               = 1 << (STAGE_BIT_CNT + 3),
+        // for below assert
+        kDummyVertexLayoutBit,
+        kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
+    };
+    // make sure we haven't exceeded the number of bits in GrVertexLayout.
+    GR_STATIC_ASSERT(kHighVertexLayoutBit < ((uint64_t)1 << 8*sizeof(GrVertexLayout)));
+
+    ////////////////////////////////////////////////////////////////////////////
+    // Helpers for picking apart vertex layouts
+
+    /**
+     * Helper function to compute the size of a vertex from a vertex layout
+     * @return size of a single vertex.
+     */
+    static size_t VertexSize(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function for determining the index of texture coordinates that
+     * is input for a texture stage. Note that a stage may instead use positions
+     * as texture coordinates, in which case the result of the function is
+     * indistinguishable from the case when the stage is disabled.
+     *
+     * @param stageIdx      the stage to query
+     * @param vertexLayout  layout to query
+     *
+     * @return the texture coordinate index or -1 if the stage doesn't use
+     *         separate (non-position) texture coordinates.
+     */
+    static int VertexTexCoordsForStage(int stageIdx, GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of texture coordinates in a vertex
+     * @return offset of texture coordinates in vertex layout or -1 if the
+     *         layout has no texture coordinates. Will be 0 if positions are
+     *         used as texture coordinates for the stage.
+     */
+    static int VertexStageCoordOffset(int stageIdx, GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of the color in a vertex
+     * @return offset of color in vertex layout or -1 if the
+     *         layout has no color.
+     */
+    static int VertexColorOffset(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the offset of the coverage in a vertex
+     * @return offset of coverage in vertex layout or -1 if the
+     *         layout has no coverage.
+     */
+    static int VertexCoverageOffset(GrVertexLayout vertexLayout);
+
+     /**
+      * Helper function to compute the offset of the edge pts in a vertex
+      * @return offset of edge in vertex layout or -1 if the
+      *         layout has no edge.
+      */
+     static int VertexEdgeOffset(GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to determine if vertex layout contains explicit texture
+     * coordinates of some index.
+     *
+     * @param coordIndex    the tex coord index to query
+     * @param vertexLayout  layout to query
+     *
+     * @return true if vertex specifies texture coordinates for the index,
+     *              false otherwise.
+     */
+    static bool VertexUsesTexCoordIdx(int coordIndex,
+                                      GrVertexLayout vertexLayout);
+
+    /**
+     * Helper function to compute the size of each vertex and the offsets of
+     * texture coordinates and color. Determines tex coord offsets by tex coord
+     * index rather than by stage. (Each stage can be mapped to any t.c. index
+     * by StageTexCoordVertexLayoutBit.)
+     *
+     * @param vertexLayout          the layout to query
+     * @param texCoordOffsetsByIdx  after return it is the offset of each
+     *                              tex coord index in the vertex or -1 if
+     *                              index isn't used. (optional)
+     * @param colorOffset           after return it is the offset of the
+     *                              color field in each vertex, or -1 if
+     *                              there aren't per-vertex colors. (optional)
+     * @param coverageOffset        after return it is the offset of the
+     *                              coverage field in each vertex, or -1 if
+     *                              there aren't per-vertex coeverages.
+     *                              (optional)
+     * @param edgeOffset            after return it is the offset of the
+     *                              edge eq field in each vertex, or -1 if
+     *                              there aren't per-vertex edge equations.
+     *                              (optional)
+     * @return size of a single vertex
+     */
+    static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
+                   int texCoordOffsetsByIdx[kMaxTexCoords],
+                   int *colorOffset,
+                   int *coverageOffset,
+                   int* edgeOffset);
+
+    /**
+     * Helper function to compute the size of each vertex and the offsets of
+     * texture coordinates and color. Determines tex coord offsets by stage
+     * rather than by index. (Each stage can be mapped to any t.c. index
+     * by StageTexCoordVertexLayoutBit.) If a stage uses positions for
+     * tex coords then that stage's offset will be 0 (positions are always at 0).
+     *
+     * @param vertexLayout              the layout to query
+     * @param texCoordOffsetsByStage    after return it is the offset of each
+     *                                  tex coord index in the vertex or -1 if
+     *                                  index isn't used. (optional)
+     * @param colorOffset               after return it is the offset of the
+     *                                  color field in each vertex, or -1 if
+     *                                  there aren't per-vertex colors.
+     *                                  (optional)
+     * @param coverageOffset            after return it is the offset of the
+     *                                  coverage field in each vertex, or -1 if
+     *                                  there aren't per-vertex coeverages.
+     *                                  (optional)
+     * @param edgeOffset                after return it is the offset of the
+     *                                  edge eq field in each vertex, or -1 if
+     *                                  there aren't per-vertex edge equations.
+     *                                  (optional)
+     * @return size of a single vertex
+     */
+    static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
+                   int texCoordOffsetsByStage[kNumStages],
+                   int* colorOffset,
+                   int* coverageOffset,
+                   int* edgeOffset);
+
+    /**
+     * Determines whether src alpha is guaranteed to be one for all src pixels
+     */
+    bool srcAlphaWillBeOne(GrVertexLayout) const;
+
+    /**
+     * Determines whether the output coverage is guaranteed to be one for all pixels hit by a draw.
+     */
+    bool hasSolidCoverage(GrVertexLayout) const;
+
+    /**
+     * Accessing positions, texture coords, or colors, of a vertex within an
+     * array is a hassle involving casts and simple math. These helpers exist
+     * to keep GrDrawTarget clients' code a bit nicer looking.
+     */
+
+    /**
+     * Gets a pointer to a GrPoint of a vertex's position or texture
+     * coordinate.
+     * @param vertices      the vetex array
+     * @param vertexIndex   the index of the vertex in the array
+     * @param vertexSize    the size of each vertex in the array
+     * @param offset        the offset in bytes of the vertex component.
+     *                      Defaults to zero (corresponding to vertex position)
+     * @return pointer to the vertex component as a GrPoint
+     */
+    static GrPoint* GetVertexPoint(void* vertices,
+                                   int vertexIndex,
+                                   int vertexSize,
+                                   int offset = 0) {
+        intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<GrPoint*>(start + offset +
+                                 vertexIndex * vertexSize);
+    }
+    static const GrPoint* GetVertexPoint(const void* vertices,
+                                         int vertexIndex,
+                                         int vertexSize,
+                                         int offset = 0) {
+        intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<const GrPoint*>(start + offset +
+                                       vertexIndex * vertexSize);
+    }
+
+    /**
+     * Gets a pointer to a GrColor inside a vertex within a vertex array.
+     * @param vertices      the vetex array
+     * @param vertexIndex   the index of the vertex in the array
+     * @param vertexSize    the size of each vertex in the array
+     * @param offset        the offset in bytes of the vertex color
+     * @return pointer to the vertex component as a GrColor
+     */
+    static GrColor* GetVertexColor(void* vertices,
+                                   int vertexIndex,
+                                   int vertexSize,
+                                   int offset) {
+        intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<GrColor*>(start + offset +
+                                 vertexIndex * vertexSize);
+    }
+    static const GrColor* GetVertexColor(const void* vertices,
+                                         int vertexIndex,
+                                         int vertexSize,
+                                         int offset) {
+        const intptr_t start = GrTCast<intptr_t>(vertices);
+        return GrTCast<const GrColor*>(start + offset +
+                                       vertexIndex * vertexSize);
+    }
+
+    static void VertexLayoutUnitTest();
+
+    /// @}
+
     ///////////////////////////////////////////////////////////////////////////
     /// @name Color
     ////
@@ -101,9 +385,9 @@
      *
      *  @param color    the color to set.
      */
-    void setColor(GrColor color) { fColor = color; }
+    void setColor(GrColor color) { fCommon.fColor = color; }
 
-    GrColor getColor() const { return fColor; }
+    GrColor getColor() const { return fCommon.fColor; }
 
     /**
      *  Sets the color to be used for the next draw to be
@@ -120,12 +404,44 @@
      * after color-computing texture stages.
      */
     void setColorFilter(GrColor c, SkXfermode::Mode mode) {
-        fColorFilterColor = c;
-        fColorFilterMode = mode;
+        fCommon.fColorFilterColor = c;
+        fCommon.fColorFilterMode = mode;
     }
 
-    GrColor getColorFilterColor() const { return fColorFilterColor; }
-    SkXfermode::Mode getColorFilterMode() const { return fColorFilterMode; }
+    GrColor getColorFilterColor() const { return fCommon.fColorFilterColor; }
+    SkXfermode::Mode getColorFilterMode() const { return fCommon.fColorFilterMode; }
+
+    /**
+     * Constructor sets the color to be 'color' which is undone by the destructor.
+     */
+    class AutoColorRestore : public ::GrNoncopyable {
+    public:
+        AutoColorRestore() : fDrawState(NULL) {}
+
+        AutoColorRestore(GrDrawState* drawState, GrColor color) {
+            fDrawState = NULL;
+            this->set(drawState, color);
+        }
+
+        void reset() {
+            if (NULL != fDrawState) {
+                fDrawState->setColor(fOldColor);
+                fDrawState = NULL;
+            }
+        }
+
+        void set(GrDrawState* drawState, GrColor color) {
+            this->reset();
+            fDrawState = drawState;
+            fOldColor = fDrawState->getColor();
+            fDrawState->setColor(color);
+        }
+
+        ~AutoColorRestore() { this->reset(); }
+    private:
+        GrDrawState*    fDrawState;
+        GrColor         fOldColor;
+    };
 
     /// @}
 
@@ -134,12 +450,12 @@
     ////
 
     /**
-     * Sets a constant fractional coverage to be applied to the draw. The 
+     * Sets a constant fractional coverage to be applied to the draw. The
      * initial value (after construction or reset()) is 0xff. The constant
      * coverage is ignored when per-vertex coverage is provided.
      */
     void setCoverage(uint8_t coverage) {
-        fCoverage = GrColorPackRGBA(coverage, coverage, coverage, coverage);
+        fCommon.fCoverage = GrColorPackRGBA(coverage, coverage, coverage, coverage);
     }
 
     /**
@@ -147,80 +463,118 @@
      * should be premultiplied.
      */
     void setCoverage4(GrColor coverage) {
-        fCoverage = coverage;
+        fCommon.fCoverage = coverage;
     }
 
     GrColor getCoverage() const {
-        return fCoverage;
+        return fCommon.fCoverage;
     }
 
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
-    /// @name Textures
+    /// @name Effect Stages
     ////
 
-    /**
-     * Sets the texture used at the next drawing call
-     *
-     * @param stage The texture stage for which the texture will be set
-     *
-     * @param texture The texture to set. Can be NULL though there is no
-     * advantage to settings a NULL texture if doing non-textured drawing
-     */
-    void setTexture(int stage, GrTexture* texture) {
-        GrAssert((unsigned)stage < kNumStages);
-        fTextures[stage] = texture;
+    const GrEffectRef* setEffect(int stageIdx, const GrEffectRef* effect) {
+        fStages[stageIdx].setEffect(effect);
+        return effect;
     }
 
     /**
-     * Retrieves the currently set texture.
-     *
-     * @return    The currently set texture. The return value will be NULL if no
-     *            texture has been set, NULL was most recently passed to
-     *            setTexture, or the last setTexture was destroyed.
+     * Creates a GrSimpleTextureEffect.
      */
-    const GrTexture* getTexture(int stage) const {
-        GrAssert((unsigned)stage < kNumStages);
-        return fTextures[stage];
+    void createTextureEffect(int stageIdx, GrTexture* texture, const SkMatrix& matrix) {
+        GrAssert(!this->getStage(stageIdx).getEffect());
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix);
+        this->setEffect(stageIdx, effect)->unref();
     }
-    GrTexture* getTexture(int stage) {
-        GrAssert((unsigned)stage < kNumStages);
-        return fTextures[stage];
+    void createTextureEffect(int stageIdx,
+                             GrTexture* texture,
+                             const SkMatrix& matrix,
+                             const GrTextureParams& params) {
+        GrAssert(!this->getStage(stageIdx).getEffect());
+        GrEffectRef* effect = GrSimpleTextureEffect::Create(texture, matrix, params);
+        this->setEffect(stageIdx, effect)->unref();
     }
 
-    /// @}
-
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Samplers
-    ////
-
-    /**
-     * Returns the current sampler for a stage.
-     */
-    const GrSamplerState& getSampler(int stage) const {
-        GrAssert((unsigned)stage < kNumStages);
-        return fSamplerStates[stage];
-    }
-
-    /**
-     * Writable pointer to a stage's sampler.
-     */
-    GrSamplerState* sampler(int stage) {
-        GrAssert((unsigned)stage < kNumStages);
-        return fSamplerStates + stage;
-    }
-
-    /**
-     * Preconcats the matrix of all samplers in the mask with the same matrix.
-     */
-    void preConcatSamplerMatrices(StageMask stageMask, const GrMatrix& matrix) {
-        GrAssert(!(stageMask & kIllegalStageMaskBits));
+    bool stagesDisabled() {
         for (int i = 0; i < kNumStages; ++i) {
-            if ((1 << i) & stageMask) {
-                fSamplerStates[i].preConcatMatrix(matrix);
+            if (NULL != fStages[i].getEffect()) {
+                return false;
             }
         }
+        return true;
+    }
+
+    void disableStage(int stageIdx) { this->setEffect(stageIdx, NULL); }
+
+    /**
+     * Release all the GrEffects referred to by this draw state.
+     */
+    void disableStages() {
+        for (int i = 0; i < kNumStages; ++i) {
+            this->disableStage(i);
+        }
+    }
+
+    class AutoStageDisable : public ::GrNoncopyable {
+    public:
+        AutoStageDisable(GrDrawState* ds) : fDrawState(ds) {}
+        ~AutoStageDisable() {
+            if (NULL != fDrawState) {
+                fDrawState->disableStages();
+            }
+        }
+    private:
+        GrDrawState* fDrawState;
+    };
+
+    /**
+     * Returns the current stage by index.
+     */
+    const GrEffectStage& getStage(int stageIdx) const {
+        GrAssert((unsigned)stageIdx < kNumStages);
+        return fStages[stageIdx];
+    }
+
+    /**
+     * Called when the source coord system is changing. preConcat gives the transformation from the
+     * old coord system to the new coord system.
+     */
+    void preConcatStageMatrices(const SkMatrix& preConcat) {
+        this->preConcatStageMatrices(~0U, preConcat);
+    }
+    /**
+     * Version of above that applies the update matrix selectively to stages via a mask.
+     */
+    void preConcatStageMatrices(uint32_t stageMask, const SkMatrix& preConcat) {
+        for (int i = 0; i < kNumStages; ++i) {
+            if (((1 << i) & stageMask) && this->isStageEnabled(i)) {
+                fStages[i].preConcatCoordChange(preConcat);
+            }
+        }
+    }
+
+    /**
+     * Called when the source coord system is changing. preConcatInverse is the inverse of the
+     * transformation from the old coord system to the new coord system. Returns false if the matrix
+     * cannot be inverted.
+     */
+    bool preConcatStageMatricesWithInverse(const SkMatrix& preConcatInverse) {
+        SkMatrix inv;
+        bool computed = false;
+        for (int i = 0; i < kNumStages; ++i) {
+            if (this->isStageEnabled(i)) {
+                if (!computed && !preConcatInverse.invert(&inv)) {
+                    return false;
+                } else {
+                    computed = true;
+                }
+                fStages[i].preConcatCoordChange(preConcatInverse);
+            }
+        }
+        return true;
     }
 
     /// @}
@@ -232,22 +586,22 @@
     /**
      * A common pattern is to compute a color with the initial stages and then
      * modulate that color by a coverage value in later stage(s) (AA, mask-
-     * filters, glyph mask, etc). Color-filters, xfermodes, etc should be 
-     * computed based on the pre-coverage-modulated color. The division of 
-     * stages between color-computing and coverage-computing is specified by 
+     * filters, glyph mask, etc). Color-filters, xfermodes, etc should be
+     * computed based on the pre-coverage-modulated color. The division of
+     * stages between color-computing and coverage-computing is specified by
      * this method. Initially this is kNumStages (all stages
      * are color-computing).
      */
     void setFirstCoverageStage(int firstCoverageStage) {
         GrAssert((unsigned)firstCoverageStage <= kNumStages);
-        fFirstCoverageStage = firstCoverageStage; 
+        fCommon.fFirstCoverageStage = firstCoverageStage;
     }
 
     /**
      * Gets the index of the first coverage-computing stage.
      */
     int getFirstCoverageStage() const {
-        return fFirstCoverageStage; 
+        return fCommon.fFirstCoverageStage;
     }
 
     ///@}
@@ -257,7 +611,7 @@
     ////
 
     /**
-     * Sets the blending function coeffecients.
+     * Sets the blending function coefficients.
      *
      * The blend function will be:
      *    D' = sat(S*srcCoef + D*dstCoef)
@@ -266,18 +620,18 @@
      *   color, and D' is the new destination color that will be written. sat()
      *   is the saturation function.
      *
-     * @param srcCoef coeffecient applied to the src color.
-     * @param dstCoef coeffecient applied to the dst color.
+     * @param srcCoef coefficient applied to the src color.
+     * @param dstCoef coefficient applied to the dst color.
      */
     void setBlendFunc(GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff) {
-        fSrcBlend = srcCoeff;
-        fDstBlend = dstCoeff;
+        fCommon.fSrcBlend = srcCoeff;
+        fCommon.fDstBlend = dstCoeff;
     #if GR_DEBUG
         switch (dstCoeff) {
-        case kDC_BlendCoeff:
-        case kIDC_BlendCoeff:
-        case kDA_BlendCoeff:
-        case kIDA_BlendCoeff:
+        case kDC_GrBlendCoeff:
+        case kIDC_GrBlendCoeff:
+        case kDA_GrBlendCoeff:
+        case kIDA_GrBlendCoeff:
             GrPrintf("Unexpected dst blend coeff. Won't work correctly with"
                      "coverage stages.\n");
             break;
@@ -285,10 +639,10 @@
             break;
         }
         switch (srcCoeff) {
-        case kSC_BlendCoeff:
-        case kISC_BlendCoeff:
-        case kSA_BlendCoeff:
-        case kISA_BlendCoeff:
+        case kSC_GrBlendCoeff:
+        case kISC_GrBlendCoeff:
+        case kSA_GrBlendCoeff:
+        case kISA_GrBlendCoeff:
             GrPrintf("Unexpected src blend coeff. Won't work correctly with"
                      "coverage stages.\n");
             break;
@@ -298,32 +652,32 @@
     #endif
     }
 
-    GrBlendCoeff getSrcBlendCoeff() const { return fSrcBlend; }
-    GrBlendCoeff getDstBlendCoeff() const { return fDstBlend; }
+    GrBlendCoeff getSrcBlendCoeff() const { return fCommon.fSrcBlend; }
+    GrBlendCoeff getDstBlendCoeff() const { return fCommon.fDstBlend; }
 
     void getDstBlendCoeff(GrBlendCoeff* srcBlendCoeff,
                           GrBlendCoeff* dstBlendCoeff) const {
-        *srcBlendCoeff = fSrcBlend;
-        *dstBlendCoeff = fDstBlend;
+        *srcBlendCoeff = fCommon.fSrcBlend;
+        *dstBlendCoeff = fCommon.fDstBlend;
     }
 
     /**
      * Sets the blending function constant referenced by the following blending
-     * coeffecients:
-     *      kConstC_BlendCoeff
-     *      kIConstC_BlendCoeff
-     *      kConstA_BlendCoeff
-     *      kIConstA_BlendCoeff
+     * coefficients:
+     *      kConstC_GrBlendCoeff
+     *      kIConstC_GrBlendCoeff
+     *      kConstA_GrBlendCoeff
+     *      kIConstA_GrBlendCoeff
      *
      * @param constant the constant to set
      */
-    void setBlendConstant(GrColor constant) { fBlendConstant = constant; }
+    void setBlendConstant(GrColor constant) { fCommon.fBlendConstant = constant; }
 
     /**
      * Retrieves the last value set by setBlendConstant()
      * @return the blending constant value
      */
-    GrColor getBlendConstant() const { return fBlendConstant; }
+    GrColor getBlendConstant() const { return fCommon.fBlendConstant; }
 
     /// @}
 
@@ -332,18 +686,18 @@
     ////
 
     /**
-     * Sets the matrix applied to veretx positions.
+     * Sets the matrix applied to vertex positions.
      *
      * In the post-view-matrix space the rectangle [0,w]x[0,h]
      * fully covers the render target. (w and h are the width and height of the
-     * the rendertarget.)
+     * the render-target.)
      */
-    void setViewMatrix(const GrMatrix& m) { fViewMatrix = m; }
+    void setViewMatrix(const SkMatrix& m) { fCommon.fViewMatrix = m; }
 
     /**
      * Gets a writable pointer to the view matrix.
      */
-    GrMatrix* viewMatrix() { return &fViewMatrix; }
+    SkMatrix* viewMatrix() { return &fCommon.fViewMatrix; }
 
     /**
      *  Multiplies the current view matrix by a matrix
@@ -355,7 +709,7 @@
      *
      *  @param m the matrix used to modify the view matrix.
      */
-    void preConcatViewMatrix(const GrMatrix& m) { fViewMatrix.preConcat(m); }
+    void preConcatViewMatrix(const SkMatrix& m) { fCommon.fViewMatrix.preConcat(m); }
 
     /**
      *  Multiplies the current view matrix by a matrix
@@ -367,13 +721,13 @@
      *
      *  @param m the matrix used to modify the view matrix.
      */
-    void postConcatViewMatrix(const GrMatrix& m) { fViewMatrix.postConcat(m); }
+    void postConcatViewMatrix(const SkMatrix& m) { fCommon.fViewMatrix.postConcat(m); }
 
     /**
      * Retrieves the current view matrix
      * @return the current view matrix.
      */
-    const GrMatrix& getViewMatrix() const { return fViewMatrix; }
+    const SkMatrix& getViewMatrix() const { return fCommon.fViewMatrix; }
 
     /**
      *  Retrieves the inverse of the current view matrix.
@@ -384,11 +738,11 @@
      *
      * @param matrix if not null, will receive a copy of the current inverse.
      */
-    bool getViewInverse(GrMatrix* matrix) const {
+    bool getViewInverse(SkMatrix* matrix) const {
         // TODO: determine whether we really need to leave matrix unmodified
         // at call sites when inversion fails.
-        GrMatrix inverse;
-        if (fViewMatrix.invert(&inverse)) {
+        SkMatrix inverse;
+        if (fCommon.fViewMatrix.invert(&inverse)) {
             if (matrix) {
                 *matrix = inverse;
             }
@@ -397,42 +751,95 @@
         return false;
     }
 
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Preconcats the current view matrix and restores the previous view matrix in the destructor.
+     * Effect matrices are automatically adjusted to compensate.
+     */
     class AutoViewMatrixRestore : public ::GrNoncopyable {
     public:
         AutoViewMatrixRestore() : fDrawState(NULL) {}
-        AutoViewMatrixRestore(GrDrawState* ds, const GrMatrix& newMatrix) {
+
+        AutoViewMatrixRestore(GrDrawState* ds,
+                              const SkMatrix& preconcatMatrix,
+                              uint32_t explicitCoordStageMask = 0) {
             fDrawState = NULL;
-            this->set(ds, newMatrix);
+            this->set(ds, preconcatMatrix, explicitCoordStageMask);
         }
-        AutoViewMatrixRestore(GrDrawState* ds) {
-            fDrawState = NULL;
-            this->set(ds);
-        }
-        ~AutoViewMatrixRestore() {
-            this->set(NULL, GrMatrix::I());
-        }
-        void set(GrDrawState* ds, const GrMatrix& newMatrix) {
-            if (NULL != fDrawState) {
-                fDrawState->setViewMatrix(fSavedMatrix);
-            }
-            if (NULL != ds) {
-                fSavedMatrix = ds->getViewMatrix();
-                ds->setViewMatrix(newMatrix);
-            }
-            fDrawState = ds;
-        }
-        void set(GrDrawState* ds) {
-            if (NULL != fDrawState) {
-                fDrawState->setViewMatrix(fSavedMatrix);
-            }
-            if (NULL != ds) {
-                fSavedMatrix = ds->getViewMatrix();
-            }
-            fDrawState = ds;
-        }
+
+        ~AutoViewMatrixRestore() { this->restore(); }
+
+        /**
+         * Can be called prior to destructor to restore the original matrix.
+         */
+        void restore();
+
+        void set(GrDrawState* drawState,
+                 const SkMatrix& preconcatMatrix,
+                 uint32_t explicitCoordStageMask = 0);
+
+        bool isSet() const { return NULL != fDrawState; }
+
     private:
-        GrDrawState* fDrawState;
-        GrMatrix fSavedMatrix;
+        GrDrawState*                        fDrawState;
+        SkMatrix                            fViewMatrix;
+        GrEffectStage::SavedCoordChange     fSavedCoordChanges[GrDrawState::kNumStages];
+        uint32_t                            fRestoreMask;
+    };
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    /**
+     * This sets the view matrix to identity and adjusts stage matrices to compensate. The
+     * destructor undoes the changes, restoring the view matrix that was set before the
+     * constructor. It is similar to passing the inverse of the current view matrix to
+     * AutoViewMatrixRestore, but lazily computes the inverse only if necessary.
+     */
+    class AutoDeviceCoordDraw : ::GrNoncopyable {
+    public:
+        AutoDeviceCoordDraw() : fDrawState(NULL) {}
+        /**
+         * If a stage's texture matrix is applied to explicit per-vertex coords, rather than to
+         * positions, then we don't want to modify its matrix. The explicitCoordStageMask is used
+         * to specify such stages.
+         */
+        AutoDeviceCoordDraw(GrDrawState* drawState,
+                            uint32_t explicitCoordStageMask = 0) {
+            fDrawState = NULL;
+            this->set(drawState, explicitCoordStageMask);
+        }
+
+        ~AutoDeviceCoordDraw() { this->restore(); }
+
+        bool set(GrDrawState* drawState, uint32_t explicitCoordStageMask = 0);
+
+        /**
+         * Returns true if this object was successfully initialized on to a GrDrawState. It may
+         * return false because a non-default constructor or set() were never called or because
+         * the view matrix was not invertible.
+         */
+        bool succeeded() const { return NULL != fDrawState; }
+
+        /**
+         * Returns the matrix that was set previously set on the drawState. This is only valid
+         * if succeeded returns true.
+         */
+        const SkMatrix& getOriginalMatrix() const {
+            GrAssert(this->succeeded());
+            return fViewMatrix;
+        }
+
+        /**
+         * Can be called prior to destructor to restore the original matrix.
+         */
+        void restore();
+
+    private:
+        GrDrawState*                        fDrawState;
+        SkMatrix                            fViewMatrix;
+        GrEffectStage::SavedCoordChange     fSavedCoordChanges[GrDrawState::kNumStages];
+        uint32_t                            fRestoreMask;
     };
 
     /// @}
@@ -442,37 +849,50 @@
     ////
 
     /**
-     * Sets the rendertarget used at the next drawing call
+     * Sets the render-target used at the next drawing call
      *
      * @param target  The render target to set.
      */
-    void setRenderTarget(GrRenderTarget* target) { fRenderTarget = target; }
+    void setRenderTarget(GrRenderTarget* target) {
+        fRenderTarget.reset(SkSafeRef(target));
+    }
 
     /**
-     * Retrieves the currently set rendertarget.
+     * Retrieves the currently set render-target.
      *
      * @return    The currently set render target.
      */
-    const GrRenderTarget* getRenderTarget() const { return fRenderTarget; }
-    GrRenderTarget* getRenderTarget() { return fRenderTarget; }
+    const GrRenderTarget* getRenderTarget() const { return fRenderTarget.get(); }
+    GrRenderTarget* getRenderTarget() { return fRenderTarget.get(); }
 
     class AutoRenderTargetRestore : public ::GrNoncopyable {
     public:
         AutoRenderTargetRestore() : fDrawState(NULL), fSavedTarget(NULL) {}
         AutoRenderTargetRestore(GrDrawState* ds, GrRenderTarget* newTarget) {
             fDrawState = NULL;
+            fSavedTarget = NULL;
             this->set(ds, newTarget);
         }
-        ~AutoRenderTargetRestore() { this->set(NULL, NULL); }
-        void set(GrDrawState* ds, GrRenderTarget* newTarget) {
+        ~AutoRenderTargetRestore() { this->restore(); }
+
+        void restore() {
             if (NULL != fDrawState) {
                 fDrawState->setRenderTarget(fSavedTarget);
+                fDrawState = NULL;
             }
+            GrSafeSetNull(fSavedTarget);
+        }
+
+        void set(GrDrawState* ds, GrRenderTarget* newTarget) {
+            this->restore();
+
             if (NULL != ds) {
+                GrAssert(NULL == fSavedTarget);
                 fSavedTarget = ds->getRenderTarget();
+                SkSafeRef(fSavedTarget);
                 ds->setRenderTarget(newTarget);
+                fDrawState = ds;
             }
-            fDrawState = ds;
         }
     private:
         GrDrawState* fDrawState;
@@ -493,121 +913,71 @@
      * @param settings  the stencil settings to use.
      */
     void setStencil(const GrStencilSettings& settings) {
-        fStencilSettings = settings;
+        fCommon.fStencilSettings = settings;
     }
 
     /**
      * Shortcut to disable stencil testing and ops.
      */
     void disableStencil() {
-        fStencilSettings.setDisabled();
+        fCommon.fStencilSettings.setDisabled();
     }
 
-    const GrStencilSettings& getStencil() const { return fStencilSettings; }
+    const GrStencilSettings& getStencil() const { return fCommon.fStencilSettings; }
 
-    GrStencilSettings* stencil() { return &fStencilSettings; }
-
-    /// @}
-
-    ///////////////////////////////////////////////////////////////////////////
-    /// @name Color Matrix
-    ////
-
-    /**
-     * Sets the color matrix to use for the next draw.
-     * @param matrix  the 5x4 matrix to apply to the incoming color
-     */
-    void setColorMatrix(const float matrix[20]) {
-        memcpy(fColorMatrix, matrix, sizeof(fColorMatrix));
-    }
-
-    const float* getColorMatrix() const { return fColorMatrix; }
+    GrStencilSettings* stencil() { return &fCommon.fStencilSettings; }
 
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
     // @name Edge AA
-    // There are two ways to perform antialiasing using edge equations. One
-    // is to specify an (linear or quadratic) edge eq per-vertex. This requires
-    // splitting vertices shared by primitives.
+    // Edge equations can be specified to perform anti-aliasing. Because the
+    // edges are specified as per-vertex data, vertices that are shared by
+    // multiple edges must be split.
     //
-    // The other is via setEdgeAAData which sets a set of edges and each
-    // is tested against all the edges.
     ////
 
     /**
      * When specifying edges as vertex data this enum specifies what type of
-     * edges are in use. The edges are always 4 GrScalars in memory, even when
+     * edges are in use. The edges are always 4 SkScalars in memory, even when
      * the edge type requires fewer than 4.
+     *
+     * TODO: Fix the fact that HairLine and Circle edge types use y-down coords.
+     *       (either adjust in VS or use origin_upper_left in GLSL)
      */
     enum VertexEdgeType {
         /* 1-pixel wide line
            2D implicit line eq (a*x + b*y +c = 0). 4th component unused */
         kHairLine_EdgeType,
-        /* Quadratic specified by u^2-v canonical coords (only 2 
+        /* Quadratic specified by u^2-v canonical coords (only 2
            components used). Coverage based on signed distance with negative
-           being inside, positive outside.*/
+           being inside, positive outside. Edge specified in window space
+           (y-down) */
         kQuad_EdgeType,
         /* Same as above but for hairline quadratics. Uses unsigned distance.
            Coverage is min(0, 1-distance). */
         kHairQuad_EdgeType,
+        /* Circle specified as center_x, center_y, outer_radius, inner_radius
+           all in window space (y-down). */
+        kCircle_EdgeType,
+        /* Axis-aligned ellipse specified as center_x, center_y, x_radius, x_radius/y_radius
+           all in window space (y-down). */
+        kEllipse_EdgeType,
 
         kVertexEdgeTypeCnt
     };
 
     /**
-     * Determines the interpretation per-vertex edge data when the 
+     * Determines the interpretation per-vertex edge data when the
      * kEdge_VertexLayoutBit is set (see GrDrawTarget). When per-vertex edges
      * are not specified the value of this setting has no effect.
      */
     void setVertexEdgeType(VertexEdgeType type) {
         GrAssert(type >=0 && type < kVertexEdgeTypeCnt);
-        fVertexEdgeType = type;
+        fCommon.fVertexEdgeType = type;
     }
 
-    VertexEdgeType getVertexEdgeType() const { return fVertexEdgeType; }
-
-    /**
-     * The absolute maximum number of edges that may be specified for
-     * a single draw call when performing edge antialiasing.  This is used for
-     * the size of several static buffers, so implementations of getMaxEdges()
-     * (below) should clamp to this value.
-     */
-    enum {
-        // TODO: this should be 32 when GrTesselatedPathRenderer is used
-        // Visual Studio 2010 does not permit a member array of size 0.
-        kMaxEdges = 1
-    };
-
-    class Edge {
-      public:
-        Edge() {}
-        Edge(float x, float y, float z) : fX(x), fY(y), fZ(z) {}
-        GrPoint intersect(const Edge& other) {
-            return GrPoint::Make(
-                SkFloatToScalar((fY * other.fZ - other.fY * fZ) /
-                                (fX * other.fY - other.fX * fY)),
-                SkFloatToScalar((fX * other.fZ - other.fX * fZ) /
-                                (other.fX * fY - fX * other.fY)));
-        }
-        float fX, fY, fZ;
-    };
-
-    /**
-     * Sets the edge data required for edge antialiasing.
-     *
-     * @param edges       3 * numEdges float values, representing the edge
-     *                    equations in Ax + By + C form
-     */
-    void setEdgeAAData(const Edge* edges, int numEdges) {
-        GrAssert(numEdges <= GrDrawState::kMaxEdges);
-        memcpy(fEdgeAAEdges, edges, numEdges * sizeof(GrDrawState::Edge));
-        fEdgeAANumEdges = numEdges;
-    }
-
-    int getNumAAEdges() const { return fEdgeAANumEdges; }
-
-    const Edge* getAAEdges() const { return fEdgeAAEdges; }
+    VertexEdgeType getVertexEdgeType() const { return fCommon.fVertexEdgeType; }
 
     /// @}
 
@@ -625,9 +995,9 @@
          */
         kDither_StateBit        = 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.
+         * 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,
         /**
@@ -639,17 +1009,16 @@
          * operations.
          */
         kNoColorWrites_StateBit = 0x08,
+
         /**
-         * Modifies the behavior of edge AA specified by setEdgeAA. If set, 
-         * will test edge pairs for convexity when rasterizing. Set this if the 
-         * source polygon is non-convex.
+         * Usually coverage is applied after color blending. The color is blended using the coeffs
+         * specified by setBlendFunc(). The blended color is then combined with dst using coeffs
+         * of src_coverage, 1-src_coverage. Sometimes we are explicitly drawing a coverage mask. In
+         * this case there is no distinction between coverage and color and the caller needs direct
+         * control over the blend coeffs. When set, there will be a single blend step controlled by
+         * setBlendFunc() which will use coverage*color as the src color.
          */
-        kEdgeAAConcave_StateBit = 0x10,
-        /**
-         * Draws will apply the color matrix, otherwise the color matrix is
-         * ignored.
-         */
-        kColorMatrix_StateBit   = 0x20,
+         kCoverageDrawing_StateBit = 0x10,
 
         // Users of the class may add additional bits to the vector
         kDummyStateBit,
@@ -657,53 +1026,63 @@
     };
 
     void resetStateFlags() {
-        fFlagBits = 0;
+        fCommon.fFlagBits = 0;
     }
 
     /**
      * Enable render state settings.
      *
-     * @param flags   bitfield of StateBits specifing the states to enable
+     * @param stateBits bitfield of StateBits specifying the states to enable
      */
     void enableState(uint32_t stateBits) {
-        fFlagBits |= stateBits;
+        fCommon.fFlagBits |= stateBits;
     }
 
     /**
      * Disable render state settings.
      *
-     * @param flags   bitfield of StateBits specifing the states to disable
+     * @param stateBits bitfield of StateBits specifying the states to disable
      */
     void disableState(uint32_t stateBits) {
-        fFlagBits &= ~(stateBits);
+        fCommon.fFlagBits &= ~(stateBits);
+    }
+
+    /**
+     * Enable or disable stateBits based on a boolean.
+     *
+     * @param stateBits bitfield of StateBits to enable or disable
+     * @param enable    if true enable stateBits, otherwise disable
+     */
+    void setState(uint32_t stateBits, bool enable) {
+        if (enable) {
+            this->enableState(stateBits);
+        } else {
+            this->disableState(stateBits);
+        }
     }
 
     bool isDitherState() const {
-        return 0 != (fFlagBits & kDither_StateBit);
+        return 0 != (fCommon.fFlagBits & kDither_StateBit);
     }
 
     bool isHWAntialiasState() const {
-        return 0 != (fFlagBits & kHWAntialias_StateBit);
+        return 0 != (fCommon.fFlagBits & kHWAntialias_StateBit);
     }
 
     bool isClipState() const {
-        return 0 != (fFlagBits & kClip_StateBit);
+        return 0 != (fCommon.fFlagBits & kClip_StateBit);
     }
 
     bool isColorWriteDisabled() const {
-        return 0 != (fFlagBits & kNoColorWrites_StateBit);
+        return 0 != (fCommon.fFlagBits & kNoColorWrites_StateBit);
     }
 
-    bool isConcaveEdgeAAState() const {
-        return 0 != (fFlagBits & kEdgeAAConcave_StateBit);
+    bool isCoverageDrawing() const {
+        return 0 != (fCommon.fFlagBits & kCoverageDrawing_StateBit);
     }
 
     bool isStateFlagEnabled(uint32_t stateBit) const {
-        return 0 != (stateBit & fFlagBits);
-    }
-
-    void copyStateFlags(const GrDrawState& ds) {
-        fFlagBits = ds.fFlagBits;
+        return 0 != (stateBit & fCommon.fFlagBits);
     }
 
     /// @}
@@ -713,6 +1092,8 @@
     ////
 
     enum DrawFace {
+        kInvalid_DrawFace = -1,
+
         kBoth_DrawFace,
         kCCW_DrawFace,
         kCW_DrawFace,
@@ -723,7 +1104,8 @@
      * @param face  the face(s) to draw.
      */
     void setDrawFace(DrawFace face) {
-        fDrawFace = face;
+        GrAssert(kInvalid_DrawFace != face);
+        fCommon.fDrawFace = face;
     }
 
     /**
@@ -731,88 +1113,156 @@
      * or both faces.
      * @return the current draw face(s).
      */
-    DrawFace getDrawFace() const { return fDrawFace; }
-    
+    DrawFace getDrawFace() const { return fCommon.fDrawFace; }
+
     /// @}
 
     ///////////////////////////////////////////////////////////////////////////
 
+    bool isStageEnabled(int s) const {
+        GrAssert((unsigned)s < kNumStages);
+        return (NULL != fStages[s].getEffect());
+    }
+
     // Most stages are usually not used, so conditionals here
     // reduce the expected number of bytes touched by 50%.
     bool operator ==(const GrDrawState& s) const {
-        if (memcmp(this, &s, this->leadingBytes())) return false;
+        if (fRenderTarget.get() != s.fRenderTarget.get() || fCommon != s.fCommon) {
+            return false;
+        }
 
         for (int i = 0; i < kNumStages; i++) {
-            if (fTextures[i] &&
-                memcmp(&this->fSamplerStates[i], &s.fSamplerStates[i],
-                       sizeof(GrSamplerState))) {
+            bool enabled = this->isStageEnabled(i);
+            if (enabled != s.isStageEnabled(i)) {
+                return false;
+            }
+            if (enabled && this->fStages[i] != s.fStages[i]) {
                 return false;
             }
         }
-
         return true;
     }
     bool operator !=(const GrDrawState& s) const { return !(*this == s); }
 
-    // Most stages are usually not used, so conditionals here 
-    // reduce the expected number of bytes touched by 50%.
-    GrDrawState& operator =(const GrDrawState& s) {
-        memcpy(this, &s, this->leadingBytes());
-
+    GrDrawState& operator= (const GrDrawState& s) {
+        this->setRenderTarget(s.fRenderTarget.get());
+        fCommon = s.fCommon;
         for (int i = 0; i < kNumStages; i++) {
-            if (s.fTextures[i]) {
-                memcpy(&this->fSamplerStates[i], &s.fSamplerStates[i],
-                       sizeof(GrSamplerState));
+            if (s.isStageEnabled(i)) {
+                this->fStages[i] = s.fStages[i];
             }
         }
-
         return *this;
     }
 
 private:
-    static const StageMask kIllegalStageMaskBits = ~((1 << kNumStages)-1);
-    // @{ these fields can be initialized with memset to 0
-    GrColor             fBlendConstant;
-    GrTexture*          fTextures[kNumStages];
-    GrColor             fColorFilterColor;
-    uint32_t            fFlagBits;
-    DrawFace            fDrawFace; 
-    VertexEdgeType      fVertexEdgeType;
-    GrStencilSettings   fStencilSettings;
-    float               fColorMatrix[20];       // 5 x 4 matrix
-    GrRenderTarget*     fRenderTarget;
-    // @}
 
-    // @{ Initialized to values other than zero
-    GrColor             fColor;
-    GrColor             fCoverage;
-    int                 fFirstCoverageStage;
-    SkXfermode::Mode    fColorFilterMode;
-    GrBlendCoeff        fSrcBlend;
-    GrBlendCoeff        fDstBlend;
-    GrMatrix            fViewMatrix;
-    // @}
+    /** Fields that are identical in GrDrawState and GrDrawState::DeferredState. */
+    struct CommonState {
+        // These fields are roughly sorted by decreasing likelihood of being different in op==
+        GrColor                         fColor;
+        SkMatrix                        fViewMatrix;
+        GrBlendCoeff                    fSrcBlend;
+        GrBlendCoeff                    fDstBlend;
+        GrColor                         fBlendConstant;
+        uint32_t                        fFlagBits;
+        VertexEdgeType                  fVertexEdgeType;
+        GrStencilSettings               fStencilSettings;
+        int                             fFirstCoverageStage;
+        GrColor                         fCoverage;
+        SkXfermode::Mode                fColorFilterMode;
+        GrColor                         fColorFilterColor;
+        DrawFace                        fDrawFace;
+        bool operator== (const CommonState& other) const {
+            return fColor == other.fColor &&
+                   fViewMatrix.cheapEqualTo(other.fViewMatrix) &&
+                   fSrcBlend == other.fSrcBlend &&
+                   fDstBlend == other.fDstBlend &&
+                   fBlendConstant == other.fBlendConstant &&
+                   fFlagBits == other.fFlagBits &&
+                   fVertexEdgeType == other.fVertexEdgeType &&
+                   fStencilSettings == other.fStencilSettings &&
+                   fFirstCoverageStage == other.fFirstCoverageStage &&
+                   fCoverage == other.fCoverage &&
+                   fColorFilterMode == other.fColorFilterMode &&
+                   fColorFilterColor == other.fColorFilterColor &&
+                   fDrawFace == other.fDrawFace;
+        }
+        bool operator!= (const CommonState& other) const { return !(*this == other); }
+    };
 
-    // @{ Data for GrTesselatedPathRenderer
-    // TODO: currently ignored in copying & comparison for performance.
-    // Must be considered if GrTesselatedPathRenderer is being used.
-    int                 fEdgeAANumEdges;
-    Edge                fEdgeAAEdges[kMaxEdges];
-    // @}
+    /** GrDrawState uses GrEffectStages to hold stage state which holds a ref on GrEffectRef.
+        DeferredState must directly reference GrEffects, however. */
+    struct SavedEffectStage {
+        SavedEffectStage() : fEffect(NULL) {}
+        const GrEffect*                    fEffect;
+        GrEffectStage::SavedCoordChange    fCoordChange;
+    };
 
-    // This field must be last; it will not be copied or compared
-    // if the corresponding fTexture[] is NULL.
-    GrSamplerState      fSamplerStates[kNumStages];
+public:
+    /**
+     * DeferredState contains all of the data of a GrDrawState but does not hold refs on GrResource
+     * objects. Resources are allowed to hit zero ref count while in DeferredStates. Their internal
+     * dispose mechanism returns them to the cache. This allows recycling resources through the
+     * the cache while they are in a deferred draw queue.
+     */
+    class DeferredState {
+    public:
+        DeferredState() : fRenderTarget(NULL) {
+            GR_DEBUGCODE(fInitialized = false;)
+        }
+        // TODO: Remove this when DeferredState no longer holds a ref to the RT
+        ~DeferredState() { SkSafeUnref(fRenderTarget); }
 
-    size_t leadingBytes() const {
-        // Can't use offsetof() with non-POD types, so stuck with pointer math.
-        // TODO: ignores GrTesselatedPathRenderer data structures. We don't
-        // have a compile-time flag that lets us know if it's being used, and
-        // checking at runtime seems to cost 5% performance.
-        return (size_t) ((unsigned char*)&fEdgeAANumEdges -
-                         (unsigned char*)&fBlendConstant);
-    }
+        void saveFrom(const GrDrawState& drawState) {
+            fCommon = drawState.fCommon;
+            // TODO: Here we will copy the GrRenderTarget pointer without taking a ref.
+            fRenderTarget = drawState.fRenderTarget.get();
+            SkSafeRef(fRenderTarget);
+            // Here we ref the effects directly rather than the effect-refs. TODO: When the effect-
+            // ref gets fully unref'ed it will cause the underlying effect to unref its resources
+            // and recycle them to the cache (if no one else is holding a ref to the resources).
+            for (int i = 0; i < kNumStages; ++i) {
+                fStages[i].saveFrom(drawState.fStages[i]);
+            }
+            GR_DEBUGCODE(fInitialized = true;)
+        }
 
+        void restoreTo(GrDrawState* drawState) {
+            GrAssert(fInitialized);
+            drawState->fCommon = fCommon;
+            drawState->setRenderTarget(fRenderTarget);
+            for (int i = 0; i < kNumStages; ++i) {
+                fStages[i].restoreTo(&drawState->fStages[i]);
+            }
+        }
+
+        bool isEqual(const GrDrawState& state) const {
+            if (fRenderTarget != state.fRenderTarget.get() || fCommon != state.fCommon) {
+                return false;
+            }
+            for (int i = 0; i < kNumStages; ++i) {
+                if (!fStages[i].isEqual(state.fStages[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+    private:
+        GrRenderTarget*                 fRenderTarget;
+        CommonState                     fCommon;
+        GrEffectStage::DeferredStage    fStages[kNumStages];
+
+        GR_DEBUGCODE(bool fInitialized;)
+    };
+
+private:
+    SkAutoTUnref<GrRenderTarget>    fRenderTarget;
+    CommonState                     fCommon;
+    GrEffectStage                   fStages[kNumStages];
+
+    typedef GrRefCnt INHERITED;
 };
 
 #endif
diff --git a/src/gpu/GrDrawTarget.cpp b/src/gpu/GrDrawTarget.cpp
index d68aa39..2cbcf19 100644
--- a/src/gpu/GrDrawTarget.cpp
+++ b/src/gpu/GrDrawTarget.cpp
@@ -9,460 +9,72 @@
 
 
 #include "GrDrawTarget.h"
-#include "GrGpuVertex.h"
-#include "GrIndexBuffer.h"
 #include "GrRenderTarget.h"
 #include "GrTexture.h"
 #include "GrVertexBuffer.h"
 
-namespace {
+#include "SkStrokeRec.h"
 
-/**
- * This function generates some masks that we like to have known at compile
- * time. When the number of stages or tex coords is bumped or the way bits
- * are defined in GrDrawTarget.h changes this funcion should be rerun to
- * generate the new masks. (We attempted to force the compiler to generate the
- * masks using recursive templates but always wound up with static initializers
- * under gcc, even if they were just a series of immediate->memory moves.)
- * 
- */
-void gen_mask_arrays(GrVertexLayout* stageTexCoordMasks,
-                     GrVertexLayout* stageMasks,
-                     GrVertexLayout* texCoordMasks) {
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        stageTexCoordMasks[s] = 0;
-        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-            stageTexCoordMasks[s] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
-        }
-        stageMasks[s] = stageTexCoordMasks[s] | GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
-    }
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        texCoordMasks[t] = 0;
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            texCoordMasks[t] |= GrDrawTarget::StageTexCoordVertexLayoutBit(s, t);
-        }
-    }
-}
-
-/** 
- * Run this function to generate the code that declares the global masks.
- */
-void gen_globals() {
-    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
-    GrVertexLayout stageMasks[GrDrawState::kNumStages];
-    GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
-    gen_mask_arrays(stageTexCoordMasks, stageMasks, texCoordMasks);
-    
-    GrPrintf("const GrVertexLayout gStageTexCoordMasks[] = {\n");
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrPrintf("    0x%x,\n", stageTexCoordMasks[s]);
-    }
-    GrPrintf("};\n");
-    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));\n\n");
-    GrPrintf("const GrVertexLayout gStageMasks[] = {\n");
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrPrintf("    0x%x,\n", stageMasks[s]);
-    }
-    GrPrintf("};\n");
-    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageMasks));\n\n");
-    GrPrintf("const GrVertexLayout gTexCoordMasks[] = {\n");
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        GrPrintf("    0x%x,\n", texCoordMasks[t]);
-    }
-    GrPrintf("};\n");
-    GrPrintf("GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));\n");
-}
-
-/* These values were generated by the above function */
-const GrVertexLayout gStageTexCoordMasks[] = {
-    0x49,
-    0x92,
-    0x124
-};
-
-GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageTexCoordMasks));
-const GrVertexLayout gStageMasks[] = {
-    0x249,
-    0x492,
-    0x924
-};
-
-GR_STATIC_ASSERT(GrDrawState::kNumStages == GR_ARRAY_COUNT(gStageMasks));
-const GrVertexLayout gTexCoordMasks[] = {
-    0x7,
-    0x38,
-    0x1c0,
-};
-GR_STATIC_ASSERT(GrDrawState::kMaxTexCoords == GR_ARRAY_COUNT(gTexCoordMasks));
-
-bool check_layout(GrVertexLayout layout) {
-    // can only have 1 or 0 bits set for each stage.
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        int stageBits = layout & gStageMasks[s];
-        if (stageBits && !GrIsPow2(stageBits)) {
-            return false;
-        }
-    }
-    return true;
-}
-
-int num_tex_coords(GrVertexLayout layout) {
-    int cnt = 0;
-    // figure out how many tex coordinates are present
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        if (gTexCoordMasks[t] & layout) {
-            ++cnt;
-        }
-    }
-    return cnt;
-}
-
-} //unnamed namespace
-
-size_t GrDrawTarget::VertexSize(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    size_t vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                        sizeof(GrGpuTextVertex) :
-                        sizeof(GrPoint);
-
-    size_t size = vecSize; // position
-    size += num_tex_coords(vertexLayout) * vecSize;
-    if (vertexLayout & kColor_VertexLayoutBit) {
-        size += sizeof(GrColor);
-    }
-    if (vertexLayout & kCoverage_VertexLayoutBit) {
-        size += sizeof(GrColor);
-    }
-    if (vertexLayout & kEdge_VertexLayoutBit) {
-        size += 4 * sizeof(GrScalar);
-    }
-    return size;
-}
+SK_DEFINE_INST_COUNT(GrDrawTarget)
 
 ////////////////////////////////////////////////////////////////////////////////
 
-/**
- * Functions for computing offsets of various components from the layout
- * bitfield.
- * 
- * Order of vertex components:
- * Position
- * Tex Coord 0
- * ...
- * Tex Coord GrDrawState::kMaxTexCoords-1
- * Color
- * Coverage
- */
+GrDrawTarget::DrawInfo& GrDrawTarget::DrawInfo::operator =(const DrawInfo& di) {
+    fPrimitiveType  = di.fPrimitiveType;
+    fStartVertex    = di.fStartVertex;
+    fStartIndex     = di.fStartIndex;
+    fVertexCount    = di.fVertexCount;
+    fIndexCount     = di.fIndexCount;
 
-int GrDrawTarget::VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-    if (StagePosAsTexCoordVertexLayoutBit(stage) & vertexLayout) {
-        return 0;
-    }
-    int tcIdx = VertexTexCoordsForStage(stage, vertexLayout);
-    if (tcIdx >= 0) {
+    fInstanceCount          = di.fInstanceCount;
+    fVerticesPerInstance    = di.fVerticesPerInstance;
+    fIndicesPerInstance     = di.fIndicesPerInstance;
 
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-        int offset = vecSize; // position
-        // figure out how many tex coordinates are present and precede this one.
-        for (int t = 0; t < tcIdx; ++t) {
-            if (gTexCoordMasks[t] & vertexLayout) {
-                offset += vecSize;
-            }
-        }
-        return offset;
-    }
-
-    return -1;
-}
-
-int GrDrawTarget::VertexColorOffset(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    if (vertexLayout & kColor_VertexLayoutBit) {
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-        return vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
-    }
-    return -1;
-}
-
-int GrDrawTarget::VertexCoverageOffset(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    if (vertexLayout & kCoverage_VertexLayoutBit) {
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-
-        int offset = vecSize * (num_tex_coords(vertexLayout) + 1);
-        if (vertexLayout & kColor_VertexLayoutBit) {
-            offset += sizeof(GrColor);
-        }
-        return offset;
-    }
-    return -1;
-}
-
-int GrDrawTarget::VertexEdgeOffset(GrVertexLayout vertexLayout) {
-    GrAssert(check_layout(vertexLayout));
-
-    // edge pts are after the pos, tex coords, and color
-    if (vertexLayout & kEdge_VertexLayoutBit) {
-        int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                    sizeof(GrGpuTextVertex) :
-                                    sizeof(GrPoint);
-        int offset = vecSize * (num_tex_coords(vertexLayout) + 1); //+1 for pos
-        if (vertexLayout & kColor_VertexLayoutBit) {
-            offset += sizeof(GrColor);
-        }
-        if (vertexLayout & kCoverage_VertexLayoutBit) {
-            offset += sizeof(GrColor);
-        }
-        return offset;
-    }
-    return -1;
-}
-
-int GrDrawTarget::VertexSizeAndOffsetsByIdx(
-        GrVertexLayout vertexLayout,
-        int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords],
-        int* colorOffset,
-        int* coverageOffset,
-        int* edgeOffset) {
-    GrAssert(check_layout(vertexLayout));
-
-    int vecSize = (vertexLayout & kTextFormat_VertexLayoutBit) ?
-                                                    sizeof(GrGpuTextVertex) :
-                                                    sizeof(GrPoint);
-    int size = vecSize; // position
-
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        if (gTexCoordMasks[t] & vertexLayout) {
-            if (NULL != texCoordOffsetsByIdx) {
-                texCoordOffsetsByIdx[t] = size;
-            }
-            size += vecSize;
-        } else {
-            if (NULL != texCoordOffsetsByIdx) {
-                texCoordOffsetsByIdx[t] = -1;
-            }
-        }
-    }
-    if (kColor_VertexLayoutBit & vertexLayout) {
-        if (NULL != colorOffset) {
-            *colorOffset = size;
-        }
-        size += sizeof(GrColor);
+    if (NULL != di.fDevBounds) {
+        GrAssert(di.fDevBounds == &di.fDevBoundsStorage);
+        fDevBoundsStorage = di.fDevBoundsStorage;
+        fDevBounds = &fDevBoundsStorage;
     } else {
-        if (NULL != colorOffset) {
-            *colorOffset = -1;
-        }
+        fDevBounds = NULL;
     }
-    if (kCoverage_VertexLayoutBit & vertexLayout) {
-        if (NULL != coverageOffset) {
-            *coverageOffset = size;
-        }
-        size += sizeof(GrColor);
+    return *this;
+}
+
+#if GR_DEBUG
+bool GrDrawTarget::DrawInfo::isInstanced() const {
+    if (fInstanceCount > 0) {
+        GrAssert(0 == fIndexCount % fIndicesPerInstance);
+        GrAssert(0 == fVertexCount % fVerticesPerInstance);
+        GrAssert(fIndexCount / fIndicesPerInstance == fInstanceCount);
+        GrAssert(fVertexCount / fVerticesPerInstance == fInstanceCount);
+        // there is no way to specify a non-zero start index to drawIndexedInstances().
+        GrAssert(0 == fStartIndex);
+        return true;
     } else {
-        if (NULL != coverageOffset) {
-            *coverageOffset = -1;
-        }
+        GrAssert(!fVerticesPerInstance);
+        GrAssert(!fIndicesPerInstance);
+        return false;
     }
-    if (kEdge_VertexLayoutBit & vertexLayout) {
-        if (NULL != edgeOffset) {
-            *edgeOffset = size;
-        }
-        size += 4 * sizeof(GrScalar);
-    } else {
-        if (NULL != edgeOffset) {
-            *edgeOffset = -1;
-        }
-    }
-    return size;
+}
+#endif
+
+void GrDrawTarget::DrawInfo::adjustInstanceCount(int instanceOffset) {
+    GrAssert(this->isInstanced());
+    GrAssert(instanceOffset + fInstanceCount >= 0);
+    fInstanceCount += instanceOffset;
+    fVertexCount = fVerticesPerInstance * fInstanceCount;
+    fIndexCount = fIndicesPerInstance * fInstanceCount;
 }
 
-int GrDrawTarget::VertexSizeAndOffsetsByStage(
-        GrVertexLayout vertexLayout,
-        int texCoordOffsetsByStage[GrDrawState::kNumStages],
-        int* colorOffset,
-        int* coverageOffset,
-        int* edgeOffset) {
-    GrAssert(check_layout(vertexLayout));
-
-    int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords];
-    int size = VertexSizeAndOffsetsByIdx(vertexLayout,
-                                         (NULL == texCoordOffsetsByStage) ?
-                                               NULL :
-                                               texCoordOffsetsByIdx,
-                                         colorOffset,
-                                         coverageOffset,
-                                         edgeOffset);
-    if (NULL != texCoordOffsetsByStage) {
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            int tcIdx;
-            if (StagePosAsTexCoordVertexLayoutBit(s) & vertexLayout) {
-                texCoordOffsetsByStage[s] = 0;
-            } else if ((tcIdx = VertexTexCoordsForStage(s, vertexLayout)) >= 0) {
-                texCoordOffsetsByStage[s] = texCoordOffsetsByIdx[tcIdx];
-            } else {
-                texCoordOffsetsByStage[s] = -1;
-            }
-        }
-    }
-    return size;
+void GrDrawTarget::DrawInfo::adjustStartVertex(int vertexOffset) {
+    fStartVertex += vertexOffset;
+    GrAssert(fStartVertex >= 0);
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
-bool GrDrawTarget::VertexUsesStage(int stage, GrVertexLayout vertexLayout) {
-    GrAssert(stage < GrDrawState::kNumStages);
-    GrAssert(check_layout(vertexLayout));
-    return !!(gStageMasks[stage] & vertexLayout);
-}
-
-bool GrDrawTarget::VertexUsesTexCoordIdx(int coordIndex,
-                                         GrVertexLayout vertexLayout) {
-    GrAssert(coordIndex < GrDrawState::kMaxTexCoords);
-    GrAssert(check_layout(vertexLayout));
-    return !!(gTexCoordMasks[coordIndex] & vertexLayout);
-}
-
-int GrDrawTarget::VertexTexCoordsForStage(int stage,
-                                          GrVertexLayout vertexLayout) {
-    GrAssert(stage < GrDrawState::kNumStages);
-    GrAssert(check_layout(vertexLayout));
-    int bit = vertexLayout & gStageTexCoordMasks[stage];
-    if (bit) {
-        // figure out which set of texture coordates is used
-        // bits are ordered T0S0, T0S1, T0S2, ..., T1S0, T1S1, ...
-        // and start at bit 0.
-        GR_STATIC_ASSERT(sizeof(GrVertexLayout) <= sizeof(uint32_t));
-        return (32 - Gr_clz(bit) - 1) / GrDrawState::kNumStages;
-    }
-    return -1;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-void GrDrawTarget::VertexLayoutUnitTest() {
-    // Ensure that our globals mask arrays are correct
-    GrVertexLayout stageTexCoordMasks[GrDrawState::kNumStages];
-    GrVertexLayout stageMasks[GrDrawState::kNumStages];
-    GrVertexLayout texCoordMasks[GrDrawState::kMaxTexCoords];
-    gen_mask_arrays(stageTexCoordMasks, stageMasks, texCoordMasks);
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrAssert(stageTexCoordMasks[s] == gStageTexCoordMasks[s]);
-        GrAssert(stageMasks[s] == gStageMasks[s]);
-    }
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        GrAssert(texCoordMasks[t] == gTexCoordMasks[t]);
-    }
-
-    // not necessarily exhaustive
-    static bool run;
-    if (!run) {
-        run = true;
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-
-            GrAssert(!VertexUsesStage(s, 0));
-            GrAssert(-1 == VertexStageCoordOffset(s, 0));
-            GrVertexLayout stageMask = 0;
-            for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-                stageMask |= StageTexCoordVertexLayoutBit(s,t);
-            }
-            GrAssert(1 == GrDrawState::kMaxTexCoords ||
-                     !check_layout(stageMask));
-            GrAssert(gStageTexCoordMasks[s] == stageMask);
-            stageMask |= StagePosAsTexCoordVertexLayoutBit(s);
-            GrAssert(gStageMasks[s] == stageMask);
-            GrAssert(!check_layout(stageMask));
-        }
-        for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-            GrVertexLayout tcMask = 0;
-            GrAssert(!VertexUsesTexCoordIdx(t, 0));
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                tcMask |= StageTexCoordVertexLayoutBit(s,t);
-                GrAssert(VertexUsesStage(s, tcMask));
-                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
-                GrAssert(VertexUsesTexCoordIdx(t, tcMask));
-                GrAssert(2*sizeof(GrPoint) == VertexSize(tcMask));
-                GrAssert(t == VertexTexCoordsForStage(s, tcMask));
-                for (int s2 = s + 1; s2 < GrDrawState::kNumStages; ++s2) {
-                    GrAssert(-1 == VertexStageCoordOffset(s2, tcMask));
-                    GrAssert(!VertexUsesStage(s2, tcMask));
-                    GrAssert(-1 == VertexTexCoordsForStage(s2, tcMask));
-
-                #if GR_DEBUG
-                    GrVertexLayout posAsTex = tcMask | StagePosAsTexCoordVertexLayoutBit(s2);
-                #endif
-                    GrAssert(0 == VertexStageCoordOffset(s2, posAsTex));
-                    GrAssert(VertexUsesStage(s2, posAsTex));
-                    GrAssert(2*sizeof(GrPoint) == VertexSize(posAsTex));
-                    GrAssert(-1 == VertexTexCoordsForStage(s2, posAsTex));
-                    GrAssert(-1 == VertexEdgeOffset(posAsTex));
-                }
-                GrAssert(-1 == VertexEdgeOffset(tcMask));
-                GrAssert(-1 == VertexColorOffset(tcMask));
-                GrAssert(-1 == VertexCoverageOffset(tcMask));
-            #if GR_DEBUG
-                GrVertexLayout withColor = tcMask | kColor_VertexLayoutBit;
-            #endif
-                GrAssert(-1 == VertexCoverageOffset(withColor));
-                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColor));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColor));
-            #if GR_DEBUG
-                GrVertexLayout withEdge = tcMask | kEdge_VertexLayoutBit;
-            #endif
-                GrAssert(-1 == VertexColorOffset(withEdge));
-                GrAssert(2*sizeof(GrPoint) == VertexEdgeOffset(withEdge));
-                GrAssert(4*sizeof(GrPoint) == VertexSize(withEdge));
-            #if GR_DEBUG
-                GrVertexLayout withColorAndEdge = withColor | kEdge_VertexLayoutBit;
-            #endif
-                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withColorAndEdge));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexEdgeOffset(withColorAndEdge));
-                GrAssert(4*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withColorAndEdge));
-            #if GR_DEBUG
-                GrVertexLayout withCoverage = tcMask | kCoverage_VertexLayoutBit;
-            #endif
-                GrAssert(-1 == VertexColorOffset(withCoverage));
-                GrAssert(2*sizeof(GrPoint) == VertexCoverageOffset(withCoverage));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexSize(withCoverage));
-            #if GR_DEBUG
-                GrVertexLayout withCoverageAndColor = tcMask | kCoverage_VertexLayoutBit |
-                                                      kColor_VertexLayoutBit;
-            #endif
-                GrAssert(2*sizeof(GrPoint) == VertexColorOffset(withCoverageAndColor));
-                GrAssert(2*sizeof(GrPoint) + sizeof(GrColor) == VertexCoverageOffset(withCoverageAndColor));
-                GrAssert(2*sizeof(GrPoint) + 2 * sizeof(GrColor) == VertexSize(withCoverageAndColor));
-            }
-            GrAssert(gTexCoordMasks[t] == tcMask);
-            GrAssert(check_layout(tcMask));
-
-            int stageOffsets[GrDrawState::kNumStages];
-            int colorOffset;
-            int edgeOffset;
-            int coverageOffset;
-            int size;
-            size = VertexSizeAndOffsetsByStage(tcMask,
-                                               stageOffsets, &colorOffset,
-                                               &coverageOffset, &edgeOffset);
-            GrAssert(2*sizeof(GrPoint) == size);
-            GrAssert(-1 == colorOffset);
-            GrAssert(-1 == coverageOffset);
-            GrAssert(-1 == edgeOffset);
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                GrAssert(VertexUsesStage(s, tcMask));
-                GrAssert(sizeof(GrPoint) == stageOffsets[s]);
-                GrAssert(sizeof(GrPoint) == VertexStageCoordOffset(s, tcMask));
-            }
-        }
-    }
+void GrDrawTarget::DrawInfo::adjustStartIndex(int indexOffset) {
+    GrAssert(this->isIndexed());
+    fStartIndex += indexOffset;
+    GrAssert(fStartIndex >= 0);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -470,10 +82,10 @@
 #define DEBUG_INVAL_BUFFER 0xdeadcafe
 #define DEBUG_INVAL_START_IDX -1
 
-GrDrawTarget::GrDrawTarget() {
-#if GR_DEBUG
-    VertexLayoutUnitTest();
-#endif
+GrDrawTarget::GrDrawTarget() : fClip(NULL) {
+    fDrawState = &fDefaultDrawState;
+    // We assume that fDrawState always owns a ref to the object it points at.
+    fDefaultDrawState.ref();
     GeometrySrcState& geoSrc = fGeoSrcStateStack.push_back();
 #if GR_DEBUG
     geoSrc.fVertexCount = DEBUG_INVAL_START_IDX;
@@ -487,9 +99,10 @@
 
 GrDrawTarget::~GrDrawTarget() {
     GrAssert(1 == fGeoSrcStateStack.count());
-    GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
+    SkDEBUGCODE(GeometrySrcState& geoSrc = fGeoSrcStateStack.back());
     GrAssert(kNone_GeometrySrcType == geoSrc.fIndexSrc);
     GrAssert(kNone_GeometrySrcType == geoSrc.fVertexSrc);
+    fDrawState->unref();
 }
 
 void GrDrawTarget::releaseGeometry() {
@@ -502,25 +115,25 @@
     this->resetIndexSource();
 }
 
-void GrDrawTarget::setClip(const GrClip& clip) {
+void GrDrawTarget::setClip(const GrClipData* clip) {
     clipWillBeSet(clip);
     fClip = clip;
 }
 
-const GrClip& GrDrawTarget::getClip() const {
+const GrClipData* GrDrawTarget::getClip() const {
     return fClip;
 }
 
-void GrDrawTarget::saveCurrentDrawState(SavedDrawState* state) const {
-    state->fState.set(fCurrDrawState);
-}
-
-void GrDrawTarget::restoreDrawState(const SavedDrawState& state) {
-    fCurrDrawState = *state.fState.get();
-}
-
-void GrDrawTarget::copyDrawState(const GrDrawTarget& srcTarget) {
-    fCurrDrawState = srcTarget.fCurrDrawState;
+void GrDrawTarget::setDrawState(GrDrawState*  drawState) {
+    GrAssert(NULL != fDrawState);
+    if (NULL == drawState) {
+        drawState = &fDefaultDrawState;
+    }
+    if (fDrawState != drawState) {
+        fDrawState->unref();
+        drawState->ref();
+        fDrawState = drawState;
+    }
 }
 
 bool GrDrawTarget::reserveVertexSpace(GrVertexLayout vertexLayout,
@@ -533,7 +146,7 @@
         this->releasePreviousVertexSource();
         geoSrc.fVertexSrc = kNone_GeometrySrcType;
 
-        acquired = this->onReserveVertexSpace(vertexLayout,
+        acquired = this->onReserveVertexSpace(GrDrawState::VertexSize(vertexLayout),
                                               vertexCount,
                                               vertices);
     }
@@ -555,7 +168,7 @@
         GrAssert(NULL != indices);
         this->releasePreviousIndexSource();
         geoSrc.fIndexSrc = kNone_GeometrySrcType;
-        
+
         acquired = this->onReserveIndexSpace(indexCount, indices);
     }
     if (acquired) {
@@ -565,10 +178,35 @@
         *indices = NULL;
     }
     return acquired;
-    
+
 }
 
-bool GrDrawTarget::geometryHints(GrVertexLayout vertexLayout,
+bool GrDrawTarget::reserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
+                                              int vertexCount,
+                                              int indexCount,
+                                              void** vertices,
+                                              void** indices) {
+    this->willReserveVertexAndIndexSpace(GrDrawState::VertexSize(vertexLayout), vertexCount, indexCount);
+    if (vertexCount) {
+        if (!this->reserveVertexSpace(vertexLayout, 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 vertexSize,
                                  int32_t* vertexCount,
                                  int32_t* indexCount) const {
     if (NULL != vertexCount) {
@@ -690,10 +328,9 @@
 }
 
 void GrDrawTarget::popGeometrySource() {
-    const GeometrySrcState& geoSrc = this->getGeomSrc();
     // if popping last element then pops are unbalanced with pushes
     GrAssert(fGeoSrcStateStack.count() > 1);
-    
+
     this->geometrySourceWillPop(fGeoSrcStateStack.fromBack(1));
     this->releasePreviousVertexSource();
     this->releasePreviousIndexSource();
@@ -705,6 +342,7 @@
 bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex,
                              int startIndex, int vertexCount,
                              int indexCount) const {
+    const GrDrawState& drawState = this->getDrawState();
 #if GR_DEBUG
     const GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
     int maxVertex = startVertex + vertexCount;
@@ -717,8 +355,7 @@
             maxValidVertex = geoSrc.fVertexCount;
             break;
         case kBuffer_GeometrySrcType:
-            maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() /
-                             VertexSize(geoSrc.fVertexLayout);
+            maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() / GrDrawState::VertexSize(geoSrc.fVertexLayout);
             break;
     }
     if (maxVertex > maxValidVertex) {
@@ -742,54 +379,82 @@
             GrCrash("Index reads outside valid index range.");
         }
     }
+
+    GrAssert(NULL != drawState.getRenderTarget());
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        if (drawState.isStageEnabled(s)) {
+            const GrEffectRef& effect = *drawState.getStage(s).getEffect();
+            int numTextures = effect->numTextures();
+            for (int t = 0; t < numTextures; ++t) {
+                GrTexture* texture = effect->texture(t);
+                GrAssert(texture->asRenderTarget() != drawState.getRenderTarget());
+            }
+        }
+    }
 #endif
-    const GrDrawState& drawState = this->getDrawState();
     if (NULL == drawState.getRenderTarget()) {
         return false;
     }
-    if (GrPixelConfigIsUnpremultiplied(drawState.getRenderTarget()->config())) {
-        if (kOne_BlendCoeff != drawState.getSrcBlendCoeff() ||
-            kZero_BlendCoeff != drawState.getDstBlendCoeff()) {
-            return false;
-        }
-    }
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        // We don't support using unpremultiplied textures with filters (other
-        // than nearest). Alpha-premulling is not distributive WRT to filtering.
-        // We'd have to filter each texel before filtering. We could do this for
-        // our custom filters but we would also have to disable bilerp and do
-        // a custom bilerp in the shader. Until Skia itself supports unpremul
-        // configs there is no pressure to implement this.
-        if (this->isStageEnabled(s) &&
-            GrPixelConfigIsUnpremultiplied(drawState.getTexture(s)->config()) &&
-            GrSamplerState::kNearest_Filter !=
-            drawState.getSampler(s).getFilter()) {
-            return false;
-        }
-    }
     return true;
 }
 
-void GrDrawTarget::drawIndexed(GrPrimitiveType type, int startVertex,
-                               int startIndex, int vertexCount,
-                               int indexCount) {
-    if (indexCount > 0 &&
-        this->checkDraw(type, startVertex, startIndex,
-                        vertexCount, indexCount)) {
-        this->onDrawIndexed(type, startVertex, startIndex,
-                            vertexCount, indexCount);
+void GrDrawTarget::drawIndexed(GrPrimitiveType type,
+                               int startVertex,
+                               int startIndex,
+                               int vertexCount,
+                               int indexCount,
+                               const SkRect* devBounds) {
+    if (indexCount > 0 && this->checkDraw(type, startVertex, startIndex, vertexCount, indexCount)) {
+        DrawInfo info;
+        info.fPrimitiveType = type;
+        info.fStartVertex   = startVertex;
+        info.fStartIndex    = startIndex;
+        info.fVertexCount   = vertexCount;
+        info.fIndexCount    = indexCount;
+
+        info.fInstanceCount         = 0;
+        info.fVerticesPerInstance   = 0;
+        info.fIndicesPerInstance    = 0;
+
+        if (NULL != devBounds) {
+            info.setDevBounds(*devBounds);
+        }
+        this->onDraw(info);
     }
 }
 
 void GrDrawTarget::drawNonIndexed(GrPrimitiveType type,
                                   int startVertex,
-                                  int vertexCount) {
-    if (vertexCount > 0 &&
-        this->checkDraw(type, startVertex, -1, vertexCount, -1)) {
-        this->onDrawNonIndexed(type, startVertex, vertexCount);
+                                  int vertexCount,
+                                  const SkRect* devBounds) {
+    if (vertexCount > 0 && this->checkDraw(type, startVertex, -1, vertexCount, -1)) {
+        DrawInfo info;
+        info.fPrimitiveType = type;
+        info.fStartVertex   = startVertex;
+        info.fStartIndex    = 0;
+        info.fVertexCount   = vertexCount;
+        info.fIndexCount    = 0;
+
+        info.fInstanceCount         = 0;
+        info.fVerticesPerInstance   = 0;
+        info.fIndicesPerInstance    = 0;
+
+        if (NULL != devBounds) {
+            info.setDevBounds(*devBounds);
+        }
+        this->onDraw(info);
     }
 }
 
+void GrDrawTarget::stencilPath(const GrPath* path, const SkStrokeRec& stroke, SkPath::FillType fill) {
+    // TODO: extract portions of checkDraw that are relevant to path stenciling.
+    GrAssert(NULL != path);
+    GrAssert(fCaps.pathStencilingSupport());
+    GrAssert(!stroke.isHairlineStyle());
+    GrAssert(!SkPath::IsInverseFillType(fill));
+    this->onStencilPath(path, stroke, fill);
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 
 // Some blend modes allow folding a partial coverage value into the color's
@@ -802,43 +467,22 @@
      * We want the blend to compute: f*Cs*S + (f*Cd + (1-f))D
      * By tweaking the source color's alpha we're replacing S with S'=fS. It's
      * obvious that that first term will always be ok. The second term can be
-     * rearranged as [1-(1-Cd)f]D. By substituing in the various possbilities
+     * rearranged as [1-(1-Cd)f]D. By substituting in the various possibilities
      * for Cd we find that only 1, ISA, and ISC produce the correct depth
-     * coeffecient in terms of S' and D.
+     * coefficient in terms of S' and D.
      */
     GrBlendCoeff dstCoeff = this->getDrawState().getDstBlendCoeff();
-    return kOne_BlendCoeff == dstCoeff ||
-           kISA_BlendCoeff == dstCoeff ||
-           kISC_BlendCoeff == dstCoeff;
+    return kOne_GrBlendCoeff == dstCoeff ||
+           kISA_GrBlendCoeff == dstCoeff ||
+           kISC_GrBlendCoeff == dstCoeff ||
+           this->getDrawState().isCoverageDrawing();
 }
 
-
-bool GrDrawTarget::srcAlphaWillBeOne() const {
-    const GrVertexLayout& layout = this->getGeomSrc().fVertexLayout;
-    const GrDrawState& drawState = this->getDrawState();
-
-    // Check if per-vertex or constant color may have partial alpha
-    if ((layout & kColor_VertexLayoutBit) ||
-        0xff != GrColorUnpackA(drawState.getColor())) {
-        return false;
-    }
-    // Check if color filter could introduce an alpha
-    // (TODO: Consider being more aggressive with regards to detecting 0xff
-    // final alpha from color filter).
-    if (SkXfermode::kDst_Mode != drawState.getColorFilterMode()) {
-        return false;
-    }
-    // Check if a color stage could create a partial alpha
-    for (int s = 0; s < drawState.getFirstCoverageStage(); ++s) {
-        if (StageWillBeUsed(s, layout, fCurrDrawState)) {
-            GrAssert(NULL != drawState.getTexture(s));
-            GrPixelConfig config = drawState.getTexture(s)->config();
-            if (!GrPixelConfigIsOpaque(config)) {
-                return false;
-            }
-        }
-    }
-    return true;
+namespace {
+GrVertexLayout default_blend_opts_vertex_layout() {
+    GrVertexLayout layout = 0;
+    return layout;
+}
 }
 
 GrDrawTarget::BlendOptFlags
@@ -846,7 +490,13 @@
                            GrBlendCoeff* srcCoeff,
                            GrBlendCoeff* dstCoeff) const {
 
-    const GrVertexLayout& layout = this->getGeomSrc().fVertexLayout;
+    GrVertexLayout layout;
+    if (kNone_GeometrySrcType == this->getGeomSrc().fVertexSrc) {
+        layout = default_blend_opts_vertex_layout();
+    } else {
+        layout = this->getVertexLayout();
+    }
+
     const GrDrawState& drawState = this->getDrawState();
 
     GrBlendCoeff bogusSrcCoeff, bogusDstCoeff;
@@ -860,35 +510,24 @@
     }
     *dstCoeff = drawState.getDstBlendCoeff();
 
-    // We don't ever expect source coeffecients to reference the source
-    GrAssert(kSA_BlendCoeff != *srcCoeff &&
-             kISA_BlendCoeff != *srcCoeff &&
-             kSC_BlendCoeff != *srcCoeff &&
-             kISC_BlendCoeff != *srcCoeff);
-    // same for dst
-    GrAssert(kDA_BlendCoeff != *dstCoeff &&
-             kIDA_BlendCoeff != *dstCoeff &&
-             kDC_BlendCoeff != *dstCoeff &&
-             kIDC_BlendCoeff != *dstCoeff);
-
     if (drawState.isColorWriteDisabled()) {
-        *srcCoeff = kZero_BlendCoeff;
-        *dstCoeff = kOne_BlendCoeff;
+        *srcCoeff = kZero_GrBlendCoeff;
+        *dstCoeff = kOne_GrBlendCoeff;
     }
 
-    bool srcAIsOne = this->srcAlphaWillBeOne();
-    bool dstCoeffIsOne = kOne_BlendCoeff == *dstCoeff ||
-                         (kSA_BlendCoeff == *dstCoeff && srcAIsOne);
-    bool dstCoeffIsZero = kZero_BlendCoeff == *dstCoeff ||
-                         (kISA_BlendCoeff == *dstCoeff && srcAIsOne);
+    bool srcAIsOne = drawState.srcAlphaWillBeOne(layout);
+    bool dstCoeffIsOne = kOne_GrBlendCoeff == *dstCoeff ||
+                         (kSA_GrBlendCoeff == *dstCoeff && srcAIsOne);
+    bool dstCoeffIsZero = kZero_GrBlendCoeff == *dstCoeff ||
+                         (kISA_GrBlendCoeff == *dstCoeff && srcAIsOne);
 
-
+    bool covIsZero = !drawState.isCoverageDrawing() &&
+                     !(layout & GrDrawState::kCoverage_VertexLayoutBit) &&
+                     0 == drawState.getCoverage();
     // When coeffs are (0,1) there is no reason to draw at all, unless
     // stenciling is enabled. Having color writes disabled is effectively
     // (0,1). The same applies when coverage is known to be 0.
-    if ((kZero_BlendCoeff == *srcCoeff && dstCoeffIsOne) ||
-        (!(layout & kCoverage_VertexLayoutBit) && 
-         0 == drawState.getCoverage())) {
+    if ((kZero_GrBlendCoeff == *srcCoeff && dstCoeffIsOne) || covIsZero) {
         if (drawState.getStencil().doesWrite()) {
             return kDisableBlend_BlendOptFlag |
                    kEmitTransBlack_BlendOptFlag;
@@ -898,16 +537,15 @@
     }
 
     // check for coverage due to constant coverage, per-vertex coverage,
-    // edge aa or coverage texture stage
+    // edge aa or coverage stage
     bool hasCoverage = forceCoverage ||
-                       0xffffffff != drawState.getCoverage() || 
-                       drawState.getNumAAEdges() > 0 ||
-                       (layout & kCoverage_VertexLayoutBit) ||
-                       (layout & kEdge_VertexLayoutBit);
+                       0xffffffff != drawState.getCoverage() ||
+                       (layout & GrDrawState::kCoverage_VertexLayoutBit) ||
+                       (layout & GrDrawState::kEdge_VertexLayoutBit);
     for (int s = drawState.getFirstCoverageStage();
          !hasCoverage && s < GrDrawState::kNumStages;
          ++s) {
-        if (StageWillBeUsed(s, layout, fCurrDrawState)) {
+        if (drawState.isStageEnabled(s)) {
             hasCoverage = true;
         }
     }
@@ -916,19 +554,21 @@
     // has to read at all. If not, we'll disable blending.
     if (!hasCoverage) {
         if (dstCoeffIsZero) {
-            if (kOne_BlendCoeff == *srcCoeff) {
+            if (kOne_GrBlendCoeff == *srcCoeff) {
                 // 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
                 return kDisableBlend_BlendOptFlag;
-            } else if (kZero_BlendCoeff == *srcCoeff) {
+            } else if (kZero_GrBlendCoeff == *srcCoeff) {
                 // if the op is "clear" then we don't need to emit a color
                 // or blend, just write transparent black into the dst.
-                *srcCoeff = kOne_BlendCoeff;
-                *dstCoeff = kZero_BlendCoeff;
-                return kDisableBlend_BlendOptFlag |
-                       kEmitTransBlack_BlendOptFlag;
+                *srcCoeff = kOne_GrBlendCoeff;
+                *dstCoeff = kZero_GrBlendCoeff;
+                return kDisableBlend_BlendOptFlag | kEmitTransBlack_BlendOptFlag;
             }
         }
+    } else if (drawState.isCoverageDrawing()) {
+        // we have coverage but we aren't distinguishing it from alpha by request.
+        return kCoverageAsAlpha_BlendOptFlag;
     } else {
         // check whether coverage can be safely rolled into alpha
         // of if we can skip color computation and just emit coverage
@@ -936,24 +576,24 @@
             return kCoverageAsAlpha_BlendOptFlag;
         }
         if (dstCoeffIsZero) {
-            if (kZero_BlendCoeff == *srcCoeff) {
+            if (kZero_GrBlendCoeff == *srcCoeff) {
                 // 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.
-                *dstCoeff = kISA_BlendCoeff;
+                *dstCoeff = kISA_GrBlendCoeff;
                 return  kEmitCoverage_BlendOptFlag;
             } 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 
+                // If Sa is 1 then we can replace Sa with c
                 // and set dst coeff to 1-Sa.
-                *dstCoeff = kISA_BlendCoeff;
+                *dstCoeff = kISA_GrBlendCoeff;
                 return  kCoverageAsAlpha_BlendOptFlag;
             }
         } else if (dstCoeffIsOne) {
             // the dst coeff is effectively one so blend works out to:
             // cS + (c)(1)D + (1-c)D = cS + D.
-            *dstCoeff = kOne_BlendCoeff;
+            *dstCoeff = kOne_GrBlendCoeff;
             return  kCoverageAsAlpha_BlendOptFlag;
         }
     }
@@ -965,7 +605,7 @@
     // premultiplied alpha. Smooth lines tweak the incoming alpha value
     // but not in a premul-alpha way. So we only use them when our alpha
     // is 0xff and tweaking the color for partial coverage is OK
-    if (!fCaps.fHWAALineSupport ||
+    if (!fCaps.hwAALineSupport() ||
         !this->getDrawState().isHWAntialiasState()) {
         return false;
     }
@@ -977,24 +617,79 @@
 bool GrDrawTarget::canApplyCoverage() const {
     // we can correctly apply coverage if a) we have dual source blending
     // or b) one of our blend optimizations applies.
-    return this->getCaps().fDualSourceBlendingSupport ||
+    return this->getCaps().dualSourceBlendingSupport() ||
            kNone_BlendOpt != this->getBlendOpts(true);
 }
 
-bool GrDrawTarget::drawWillReadDst() const {
-    return SkToBool((kDisableBlend_BlendOptFlag | kSkipDraw_BlendOptFlag) &
-                    this->getBlendOpts());
-}
+////////////////////////////////////////////////////////////////////////////////
 
+void GrDrawTarget::drawIndexedInstances(GrPrimitiveType type,
+                                        int instanceCount,
+                                        int verticesPerInstance,
+                                        int indicesPerInstance,
+                                        const SkRect* devBounds) {
+    if (!verticesPerInstance || !indicesPerInstance) {
+        return;
+    }
+
+    int maxInstancesPerDraw = this->indexCountInCurrentSource() / indicesPerInstance;
+    if (!maxInstancesPerDraw) {
+        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 (NULL != devBounds) {
+        info.setDevBounds(*devBounds);
+    }
+
+    while (instanceCount) {
+        info.fInstanceCount = GrMin(instanceCount, maxInstancesPerDraw);
+        info.fVertexCount = info.fInstanceCount * verticesPerInstance;
+        info.fIndexCount = info.fInstanceCount * indicesPerInstance;
+
+        if (this->checkDraw(type,
+                            info.fStartVertex,
+                            info.fStartIndex,
+                            info.fVertexCount,
+                            info.fIndexCount)) {
+            this->onDraw(info);
+        }
+        info.fStartVertex += info.fVertexCount;
+        instanceCount -= info.fInstanceCount;
+    }
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 
-void GrDrawTarget::drawRect(const GrRect& rect, 
-                            const GrMatrix* matrix,
-                            StageMask stageMask,
+void GrDrawTarget::drawRect(const GrRect& rect,
+                            const SkMatrix* matrix,
                             const GrRect* srcRects[],
-                            const GrMatrix* srcMatrices[]) {
-    GrVertexLayout layout = GetRectVertexLayout(stageMask, srcRects);
+                            const SkMatrix* srcMatrices[]) {
+    GrVertexLayout layout = 0;
+    uint32_t explicitCoordMask = 0;
+
+    if (NULL != srcRects) {
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            int numTC = 0;
+            if (NULL != srcRects[s]) {
+                layout |= GrDrawState::StageTexCoordVertexLayoutBit(s, numTC);
+                explicitCoordMask |= (1 << s);
+                ++numTC;
+            }
+        }
+    }
+
+    GrDrawState::AutoViewMatrixRestore avmr;
+    if (NULL != matrix) {
+        avmr.set(this->drawState(), *matrix, explicitCoordMask);
+    }
 
     AutoReleaseGeometry geo(this, layout, 4, 0);
     if (!geo.succeeded()) {
@@ -1002,64 +697,14 @@
         return;
     }
 
-    SetRectVertices(rect, matrix, srcRects, 
-                    srcMatrices, layout, geo.vertices());
-
-    drawNonIndexed(kTriangleFan_PrimitiveType, 0, 4);
-}
-
-GrVertexLayout GrDrawTarget::GetRectVertexLayout(StageMask stageMask, 
-                                                 const GrRect* srcRects[]) {
-    GrVertexLayout layout = 0;
-
-    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-        int numTC = 0;
-        if (stageMask & (1 << i)) {
-            if (NULL != srcRects && NULL != srcRects[i]) {
-                layout |= StageTexCoordVertexLayoutBit(i, numTC);
-                ++numTC;
-            } else {
-                layout |= StagePosAsTexCoordVertexLayoutBit(i);
-            }
-        }
-    }
-    return layout;
-}
-
-void GrDrawTarget::clipWillBeSet(const GrClip& clip) {
-}
-
-void GrDrawTarget::SetRectVertices(const GrRect& rect,
-                                   const GrMatrix* matrix, 
-                                   const GrRect* srcRects[], 
-                                   const GrMatrix* srcMatrices[],
-                                   GrVertexLayout layout, 
-                                   void* vertices) {
-#if GR_DEBUG
-    // check that the layout and srcRects agree
-    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-        if (VertexTexCoordsForStage(i, layout) >= 0) {
-            GR_DEBUGASSERT(NULL != srcRects && NULL != srcRects[i]);
-        } else {
-            GR_DEBUGASSERT(NULL == srcRects || NULL == srcRects[i]);
-        }
-    }
-#endif
-
     int stageOffsets[GrDrawState::kNumStages];
-    int vsize = VertexSizeAndOffsetsByStage(layout, stageOffsets,
-                                            NULL, NULL, NULL);
-
-    GrTCast<GrPoint*>(vertices)->setRectFan(rect.fLeft, rect.fTop,
-                                            rect.fRight, rect.fBottom,
-                                            vsize);
-    if (NULL != matrix) {
-        matrix->mapPointsWithStride(GrTCast<GrPoint*>(vertices), vsize, 4);
-    }
+    int vsize = GrDrawState::VertexSizeAndOffsetsByStage(layout, stageOffsets,  NULL, NULL, NULL);
+    geo.positions()->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize);
 
     for (int i = 0; i < GrDrawState::kNumStages; ++i) {
-        if (stageOffsets[i] > 0) {
-            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(vertices) +
+        if (explicitCoordMask & (1 << i)) {
+            GrAssert(0 != stageOffsets[i]);
+            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(geo.vertices()) +
                                                 stageOffsets[i]);
             coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop,
                                srcRects[i]->fRight, srcRects[i]->fBottom,
@@ -1067,8 +712,15 @@
             if (NULL != srcMatrices && NULL != srcMatrices[i]) {
                 srcMatrices[i]->mapPointsWithStride(coords, vsize, 4);
             }
+        } else {
+            GrAssert(0 == stageOffsets[i]);
         }
     }
+
+    this->drawNonIndexed(kTriangleFan_GrPrimitiveType, 0, 4);
+}
+
+void GrDrawTarget::clipWillBeSet(const GrClipData* clipData) {
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1077,67 +729,34 @@
     fDrawTarget = NULL;
 }
 
-GrDrawTarget::AutoStateRestore::AutoStateRestore(GrDrawTarget* target) {
-    fDrawTarget = target;
-    if (NULL != fDrawTarget) {
-        fDrawTarget->saveCurrentDrawState(&fDrawState);
-    }
+GrDrawTarget::AutoStateRestore::AutoStateRestore(GrDrawTarget* target,
+                                                 ASRInit init) {
+    fDrawTarget = NULL;
+    this->set(target, init);
 }
 
 GrDrawTarget::AutoStateRestore::~AutoStateRestore() {
     if (NULL != fDrawTarget) {
-        fDrawTarget->restoreDrawState(fDrawState);
+        fDrawTarget->setDrawState(fSavedState);
+        fSavedState->unref();
     }
 }
 
-void GrDrawTarget::AutoStateRestore::set(GrDrawTarget* target) {
-    if (target != fDrawTarget) {
-        if (NULL != fDrawTarget) {
-            fDrawTarget->restoreDrawState(fDrawState);
-        }
-        if (NULL != target) {
-            target->saveCurrentDrawState(&fDrawState);
-        }
-        fDrawTarget = target;
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-GrDrawTarget::AutoDeviceCoordDraw::AutoDeviceCoordDraw(
-                                            GrDrawTarget* target,
-                                            GrDrawState::StageMask stageMask) {
-    GrAssert(NULL != target);
-    GrDrawState* drawState = target->drawState();
-
+void GrDrawTarget::AutoStateRestore::set(GrDrawTarget* target, ASRInit init) {
+    GrAssert(NULL == fDrawTarget);
     fDrawTarget = target;
-    fViewMatrix = drawState->getViewMatrix();
-    fStageMask = stageMask;
-    if (fStageMask) {
-        GrMatrix invVM;
-        if (fViewMatrix.invert(&invVM)) {
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                if (fStageMask & (1 << s)) {
-                    fSamplerMatrices[s] = drawState->getSampler(s).getMatrix();
-                }
-            }
-            drawState->preConcatSamplerMatrices(fStageMask, invVM);
-        } else {
-            // sad trombone sound
-            fStageMask = 0;
-        }
+    fSavedState = target->drawState();
+    GrAssert(fSavedState);
+    fSavedState->ref();
+    if (kReset_ASRInit == init) {
+        // calls the default cons
+        fTempState.init();
+    } else {
+        GrAssert(kPreserve_ASRInit == init);
+        // calls the copy cons
+        fTempState.set(*fSavedState);
     }
-    drawState->setViewMatrix(GrMatrix::I());
-}
-
-GrDrawTarget::AutoDeviceCoordDraw::~AutoDeviceCoordDraw() {
-    GrDrawState* drawState = fDrawTarget->drawState();
-    drawState->setViewMatrix(fViewMatrix);
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (fStageMask & (1 << s)) {
-            *drawState->sampler(s)->matrix() = fSamplerMatrices[s];
-        }
-    }
+    target->setDrawState(fTempState.get());
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1150,7 +769,7 @@
     fTarget = NULL;
     this->set(target, vertexLayout, vertexCount, indexCount);
 }
-    
+
 GrDrawTarget::AutoReleaseGeometry::AutoReleaseGeometry() {
     fTarget = NULL;
 }
@@ -1168,19 +787,14 @@
     bool success = true;
     if (NULL != fTarget) {
         fTarget = target;
-        if (vertexCount > 0) {
-            success = target->reserveVertexSpace(vertexLayout, 
-                                                 vertexCount,
-                                                 &fVertices);
-            if (!success) {
-                this->reset();
-            }
-        }
-        if (success && indexCount > 0) {
-            success = target->reserveIndexSpace(indexCount, &fIndices);
-            if (!success) {
-                this->reset();
-            }
+        success = target->reserveVertexAndIndexSpace(vertexLayout,
+                                                     vertexCount,
+                                                     indexCount,
+                                                     &fVertices,
+                                                     &fIndices);
+        if (!success) {
+            fTarget = NULL;
+            this->reset();
         }
     }
     GrAssert(success == (NULL != fTarget));
@@ -1201,19 +815,28 @@
     fIndices = NULL;
 }
 
-void GrDrawTarget::Caps::print() const {
-    static const char* gNY[] = {"NO", "YES"};
-    GrPrintf("8 Bit Palette Support       : %s\n", gNY[f8BitPaletteSupport]);
-    GrPrintf("NPOT Texture Tile Support   : %s\n", gNY[fNPOTTextureTileSupport]);
-    GrPrintf("Two Sided Stencil Support   : %s\n", gNY[fTwoSidedStencilSupport]);
-    GrPrintf("Stencil Wrap Ops  Support   : %s\n", gNY[fStencilWrapOpsSupport]);
-    GrPrintf("HW AA Lines Support         : %s\n", gNY[fHWAALineSupport]);
-    GrPrintf("Shader Derivative Support   : %s\n", gNY[fShaderDerivativeSupport]);
-    GrPrintf("Geometry Shader Support     : %s\n", gNY[fGeometryShaderSupport]);
-    GrPrintf("FSAA Support                : %s\n", gNY[fFSAASupport]);
-    GrPrintf("Dual Source Blending Support: %s\n", gNY[fDualSourceBlendingSupport]);
-    GrPrintf("Buffer Lock Support         : %s\n", gNY[fBufferLockSupport]);
-    GrPrintf("Max Texture Size            : %d\n", fMaxTextureSize);
-    GrPrintf("Max Render Target Size      : %d\n", fMaxRenderTargetSize);
+GrDrawTarget::AutoClipRestore::AutoClipRestore(GrDrawTarget* target, const SkIRect& newClip) {
+    fTarget = target;
+    fClip = fTarget->getClip();
+    fStack.init();
+    fStack.get()->clipDevRect(newClip, SkRegion::kReplace_Op);
+    fReplacementClip.fClipStack = fStack.get();
+    target->setClip(&fReplacementClip);
 }
 
+void GrDrawTarget::Caps::print() const {
+    static const char* gNY[] = {"NO", "YES"};
+    GrPrintf("8 Bit Palette Support       : %s\n", gNY[fInternals.f8BitPaletteSupport]);
+    GrPrintf("NPOT Texture Tile Support   : %s\n", gNY[fInternals.fNPOTTextureTileSupport]);
+    GrPrintf("Two Sided Stencil Support   : %s\n", gNY[fInternals.fTwoSidedStencilSupport]);
+    GrPrintf("Stencil Wrap Ops  Support   : %s\n", gNY[fInternals.fStencilWrapOpsSupport]);
+    GrPrintf("HW AA Lines Support         : %s\n", gNY[fInternals.fHWAALineSupport]);
+    GrPrintf("Shader Derivative Support   : %s\n", gNY[fInternals.fShaderDerivativeSupport]);
+    GrPrintf("Geometry Shader Support     : %s\n", gNY[fInternals.fGeometryShaderSupport]);
+    GrPrintf("FSAA Support                : %s\n", gNY[fInternals.fFSAASupport]);
+    GrPrintf("Dual Source Blending Support: %s\n", gNY[fInternals.fDualSourceBlendingSupport]);
+    GrPrintf("Buffer Lock Support         : %s\n", gNY[fInternals.fBufferLockSupport]);
+    GrPrintf("Path Stenciling Support     : %s\n", gNY[fInternals.fPathStencilingSupport]);
+    GrPrintf("Max Texture Size            : %d\n", fInternals.fMaxTextureSize);
+    GrPrintf("Max Render Target Size      : %d\n", fInternals.fMaxRenderTargetSize);
+}
diff --git a/src/gpu/GrDrawTarget.h b/src/gpu/GrDrawTarget.h
index 6962a18..c1444b4 100644
--- a/src/gpu/GrDrawTarget.h
+++ b/src/gpu/GrDrawTarget.h
@@ -11,36 +11,29 @@
 #ifndef GrDrawTarget_DEFINED
 #define GrDrawTarget_DEFINED
 
-#include "GrClip.h"
-#include "GrColor.h"
+#include "GrClipData.h"
 #include "GrDrawState.h"
-#include "GrMatrix.h"
+#include "GrIndexBuffer.h"
+#include "SkMatrix.h"
 #include "GrRefCnt.h"
-#include "GrSamplerState.h"
-#include "GrStencil.h"
-#include "GrTexture.h"
 
-#include "SkXfermode.h"
+#include "SkClipStack.h"
+#include "SkPath.h"
 #include "SkTLazy.h"
+#include "SkTArray.h"
+#include "SkXfermode.h"
 
-class GrTexture;
-class GrClipIterator;
+class GrClipData;
+class GrPath;
 class GrVertexBuffer;
-class GrIndexBuffer;
+class SkStrokeRec;
 
 class GrDrawTarget : public GrRefCnt {
-public:
-    /**
-     * Represents the draw target capabilities.
-     */
-    struct Caps {
-        Caps() { memset(this, 0, sizeof(Caps)); }
-        Caps(const Caps& c) { *this = c; }
-        Caps& operator= (const Caps& c) {
-            memcpy(this, &c, sizeof(Caps));
-            return *this;
-        }
-        void print() const;
+protected:
+    /** This helper class allows GrDrawTarget subclasses to set the caps values without having to be
+        made a friend of GrDrawTarget::Caps. */
+    class CapsInternals {
+    public:
         bool f8BitPaletteSupport        : 1;
         bool fNPOTTextureTileSupport    : 1;
         bool fTwoSidedStencilSupport    : 1;
@@ -51,12 +44,47 @@
         bool fFSAASupport               : 1;
         bool fDualSourceBlendingSupport : 1;
         bool fBufferLockSupport         : 1;
+        bool fPathStencilingSupport     : 1;
         int fMaxRenderTargetSize;
         int fMaxTextureSize;
     };
 
-    // for convenience
-    typedef GrDrawState::StageMask StageMask;
+    class DrawInfo;
+
+public:
+    SK_DECLARE_INST_COUNT(GrDrawTarget)
+
+    /**
+     * Represents the draw target capabilities.
+     */
+    class Caps {
+    public:
+        Caps() { memset(this, 0, sizeof(Caps)); }
+        Caps(const Caps& c) { *this = c; }
+        Caps& operator= (const Caps& c) {
+            memcpy(this, &c, sizeof(Caps));
+            return *this;
+        }
+        void print() const;
+
+        bool eightBitPaletteSupport() const { return fInternals.f8BitPaletteSupport; }
+        bool npotTextureTileSupport() const { return fInternals.fNPOTTextureTileSupport; }
+        bool twoSidedStencilSupport() const { return fInternals.fTwoSidedStencilSupport; }
+        bool stencilWrapOpsSupport() const { return  fInternals.fStencilWrapOpsSupport; }
+        bool hwAALineSupport() const { return fInternals.fHWAALineSupport; }
+        bool shaderDerivativeSupport() const { return fInternals.fShaderDerivativeSupport; }
+        bool geometryShaderSupport() const { return fInternals.fGeometryShaderSupport; }
+        bool fsaaSupport() const { return fInternals.fFSAASupport; }
+        bool dualSourceBlendingSupport() const { return fInternals.fDualSourceBlendingSupport; }
+        bool bufferLockSupport() const { return fInternals.fBufferLockSupport; }
+        bool pathStencilingSupport() const { return fInternals.fPathStencilingSupport; }
+
+        int maxRenderTargetSize() const { return fInternals.fMaxRenderTargetSize; }
+        int maxTextureSize() const { return fInternals.fMaxTextureSize; }
+    private:
+        CapsInternals fInternals;
+        friend class GrDrawTarget; // to set values of fInternals
+    };
 
     ///////////////////////////////////////////////////////////////////////////
 
@@ -76,37 +104,33 @@
      *
      * @param description of the clipping region
      */
-    void setClip(const GrClip& clip);
+    void setClip(const GrClipData* clip);
 
     /**
      * Gets the current clip.
      *
      * @return the clip.
      */
-    const GrClip& getClip() const;
-
-    const GrDrawState& getDrawState() const { return fCurrDrawState; }
-    GrDrawState* drawState() { return &fCurrDrawState; }
+    const GrClipData* getClip() const;
 
     /**
-     * Shortcut for drawState()->preConcatSamplerMatrices() on all enabled
-     * stages
-     *
-     * @param matrix  the matrix to concat
+     * Sets the draw state object for the draw target. Note that this does not
+     * make a copy. The GrDrawTarget will take a reference to passed object.
+     * Passing NULL will cause the GrDrawTarget to use its own internal draw
+     * state object rather than an externally provided one.
      */
-    void preConcatEnabledSamplerMatrices(const GrMatrix& matrix) {
-        StageMask stageMask = this->enabledStages();
-        this->drawState()->preConcatSamplerMatrices(stageMask, matrix);
-    }
+    void setDrawState(GrDrawState*  drawState);
 
     /**
-     * Determines if blending will require a read of a dst given the current
-     * state set on the draw target
-     *
-     * @return true if the dst surface will be read at each pixel hit by the
-     *         a draw operation.
+     * Read-only access to the GrDrawTarget's current draw state.
      */
-    bool drawWillReadDst() const;
+    const GrDrawState& getDrawState() const { return *fDrawState; }
+
+    /**
+     * Read-write access to the GrDrawTarget's current draw state. Note that
+     * this doesn't ref.
+     */
+    GrDrawState* drawState() { return fDrawState; }
 
     /**
      * Color alpha and coverage are two inputs to the drawing pipeline. For some
@@ -115,237 +139,114 @@
      * Depending on features available in the underlying 3D API this may or may
      * not be possible.
      *
-     * This function looks at the current blend on the draw target and the draw
-     * target's capabilities to determine whether coverage can be handled
-     * correctly.
+     * This function considers the current draw state and the draw target's
+     * capabilities to determine whether coverage can be handled correctly. The
+     * following assumptions are made:
+     *    1. The caller intends to somehow specify coverage. This can be
+     *       specified either by enabling a coverage stage on the GrDrawState or
+     *       via the vertex layout.
+     *    2. Other than enabling coverage stages, the current configuration of
+     *       the target's GrDrawState is as it will be at draw time.
+     *    3. If a vertex source has not yet been specified then all stages with
+     *       non-NULL textures will be referenced by the vertex layout.
      */
     bool canApplyCoverage() const;
 
     /**
      * Determines whether incorporating partial pixel coverage into the constant
      * color specified by setColor or per-vertex colors will give the right
-     * blending result.
+     * blending result. If a vertex source has not yet been specified then
+     * the function assumes that all stages with non-NULL textures will be
+     * referenced by the vertex layout.
      */
     bool canTweakAlphaForCoverage() const;
 
     /**
-     * Given the current draw state, vertex layout, and hw support, will HW AA
-     * lines be used (if line primitive type is drawn)? (Note that lines are
-     * always 1 pixel wide)
+     * Given the current draw state and hw support, will HW AA lines be used
+     * (if line primitive type is drawn)? If a vertex source has not yet been
+     * specified then  the function assumes that all stages with non-NULL
+     * textures will be referenced by the vertex layout.
      */
     bool willUseHWAALines() const;
 
     /**
-     * Used to save and restore the GrGpu's drawing state
-     */
-    struct SavedDrawState {
-    private:
-        SkTLazy<GrDrawState> fState;
-        friend class GrDrawTarget;
-    };
-
-    /**
-     * Saves the current draw state. The state can be restored at a later time
-     * with restoreDrawState.
-     *
-     * See also AutoStateRestore class.
-     *
-     * @param   state will hold the state after the function returns.
-     */
-    void saveCurrentDrawState(SavedDrawState* state) const;
-
-    /**
-     * Restores previously saved draw state. The client guarantees that state
-     * was previously passed to saveCurrentDrawState and that the rendertarget
-     * and texture set at save are still valid.
-     *
-     * See also AutoStateRestore class.
-     *
-     * @param   state the previously saved state to restore.
-     */
-    void restoreDrawState(const SavedDrawState& state);
-
-    /**
-     * Copies the draw state from another target to this target.
-     *
-     * @param srcTarget     draw target used as src of the draw state.
-     */
-    void copyDrawState(const GrDrawTarget& srcTarget);
-
-    /**
-     * The format of vertices is represented as a bitfield of flags.
-     * Flags that indicate the layout of vertex data. Vertices always contain
-     * positions and may also contain up to GrDrawState::kMaxTexCoords sets
-     * of 2D texture * coordinates, per-vertex colors, and per-vertex coverage.
-     * Each stage can 
-     * use any of the texture coordinates as its input texture coordinates or it
-     * may use the positions as texture coordinates.
-     *
-     * If no texture coordinates are specified for a stage then the stage is
-     * disabled.
-     *
-     * Only one type of texture coord can be specified per stage. For
-     * example StageTexCoordVertexLayoutBit(0, 2) and
-     * StagePosAsTexCoordVertexLayoutBit(0) cannot both be specified.
-     *
-     * The order in memory is always (position, texture coord 0, ..., color, 
-     * coverage) with any unused fields omitted. Note that this means that if 
-     * only texture coordinates 1 is referenced then there is no texture
-     * coordinates 0 and the order would be (position, texture coordinate 1
-     * [, color][, coverage]).
-     */
-
-    /**
-     * Generates a bit indicating that a texture stage uses texture coordinates
-     *
-     * @param stage       the stage that will use texture coordinates.
-     * @param texCoordIdx the index of the texture coordinates to use
-     *
-     * @return the bit to add to a GrVertexLayout bitfield.
-     */
-    static int StageTexCoordVertexLayoutBit(int stage, int texCoordIdx) {
-        GrAssert(stage < GrDrawState::kNumStages);
-        GrAssert(texCoordIdx < GrDrawState::kMaxTexCoords);
-        return 1 << (stage + (texCoordIdx * GrDrawState::kNumStages));
-    }
-
-private:
-    static const int TEX_COORD_BIT_CNT = GrDrawState::kNumStages *
-                                         GrDrawState::kMaxTexCoords;
-
-public:
-    /**
-     * Generates a bit indicating that a texture stage uses the position
-     * as its texture coordinate.
-     *
-     * @param stage       the stage that will use position as texture
-     *                    coordinates.
-     *
-     * @return the bit to add to a GrVertexLayout bitfield.
-     */
-    static int StagePosAsTexCoordVertexLayoutBit(int stage) {
-        GrAssert(stage < GrDrawState::kNumStages);
-        return (1 << (TEX_COORD_BIT_CNT + stage));
-    }
-
-private:
-    static const int STAGE_BIT_CNT = TEX_COORD_BIT_CNT +
-        GrDrawState::kNumStages;
-
-public:
-
-    /**
-     * Additional Bits that can be specified in GrVertexLayout.
-     */
-    enum VertexLayoutBits {
-        /* vertices have colors (GrColor) */
-        kColor_VertexLayoutBit              = 1 << (STAGE_BIT_CNT + 0),
-        /* vertices have coverage (GrColor)
-         */
-        kCoverage_VertexLayoutBit           = 1 << (STAGE_BIT_CNT + 1),
-        /* Use text vertices. (Pos and tex coords may be a different type for
-         * text [GrGpuTextVertex vs GrPoint].)
-         */
-        kTextFormat_VertexLayoutBit         = 1 << (STAGE_BIT_CNT + 2),
-
-        /* Each vertex specificies an edge. Distance to the edge is used to
-         * compute a coverage. See GrDrawState::setVertexEdgeType().
-         */
-        kEdge_VertexLayoutBit               = 1 << (STAGE_BIT_CNT + 3),
-        // for below assert
-        kDummyVertexLayoutBit,
-        kHighVertexLayoutBit = kDummyVertexLayoutBit - 1
-    };
-    // make sure we haven't exceeded the number of bits in GrVertexLayout.
-    GR_STATIC_ASSERT(kHighVertexLayoutBit < ((uint64_t)1 << 8*sizeof(GrVertexLayout)));
-
-    /**
-     * There are three methods for specifying geometry (vertices and optionally
-     * indices) to the draw target. When indexed drawing the indices and vertices
-     * can use a different method. Once geometry is specified it can be used for
-     * multiple drawIndexed and drawNonIndexed calls.
+     * There are three types of "sources" of geometry (vertices and indices) for
+     * draw calls made on the target. When performing an indexed draw, the
+     * indices and vertices can use different source types. Once a source is
+     * 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. There are push
-     * pop methods
+     * 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.
      *
-     * 1. Provide a cpu array (set*SourceToArray). This is useful when the
-     *    caller's client has already provided vertex data in a format
-     *    the time compatible with a GrVertexLayout. The array must contain the
-     *    data at set*SourceToArray is called. The source stays in effect for
-     *    drawIndexed & drawNonIndexed calls until set*SourceToArray is called
-     *    again or one of the other two paths is chosen.
+     * 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 three types of sources are:
+     *
+     * 1. A cpu array (set*SourceToArray). This is useful when the caller
+     *    already provided vertex data in a format compatible with a
+     *    GrVertexLayout. The data in the array is consumed at the time that
+     *    set*SourceToArray is called and subsequent edits to the array will not
+     *    be reflected in draws.
      *
      * 2. 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, 
-     *    or pushGeometrySource At this point the data is frozen and the ptrs
-     *    are no longer valid.
+     *    The data is writable up until the next drawIndexed, drawNonIndexed,
+     *    drawIndexedInstances, 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.
      *
      * 3. Vertex and Index Buffers. This is most useful for geometry that will
-     *    is long-lived. SetVertexSourceToBuffer and SetIndexSourceToBuffer are
-     *    used to set the buffer and subsequent drawIndexed and drawNonIndexed 
-     *    calls use this source until another source is set.
+     *    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. Draw target will use reserved vertices at
-     * at the next draw.
+     * 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 succeeds:
-     *          if vertexCount > 0, *vertices will be the array
-     *          of vertices to be filled by caller. The next draw will read
-     *          these vertices.
+     * If the function returns true then the reserve suceeded and the vertices
+     * and indices pointers will point to the space created.
      *
-     * If a client does not already have a vertex buffer then this is the
-     * preferred way to allocate vertex data. It allows the subclass of
-     * GrDrawTarget to decide whether to put data in buffers, to group vertex
-     * data that uses the same state (e.g. for deferred rendering), etc.
+     * 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.
      *
-     * After the next draw or pushGeometrySource the vertices ptr is no longer
-     * valid and the geometry data cannot be further modified. The contents
-     * that were put in the reserved space can be drawn by multiple draws,
-     * however.
+     * The pointers to the space allocated for vertices and indices remain valid
+     * until a drawIndexed, drawNonIndexed, drawIndexedInstances, or push/
+     * popGeomtrySource is called. At that point logically a snapshot of the
+     * data is made and the pointers are invalid.
      *
      * @param vertexLayout the format of vertices (ignored if vertexCount == 0).
-     * @param vertexCount  the number of vertices to reserve space for. Can be 0.
+     * @param vertexCount  the number of vertices to reserve space for. Can be
+     *                     0.
+     * @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.
-     *
-     * @return  true if succeeded in allocating space for the vertices and false
-     *               if not.
-     */
-    bool reserveVertexSpace(GrVertexLayout vertexLayout,
-                            int vertexCount,
-                            void** vertices);
-    /**
-     * Reserves space for indices. Draw target will use the reserved indices at
-     * the next indexed draw.
-     *
-     * If succeeds:
-     *          if indexCount > 0, *indices will be the array
-     *          of indices to be filled by caller. The next draw will read
-     *          these indices.
-     *
-     * If a client does not already have a index buffer then this is the
-     * preferred way to allocate index data. It allows the subclass of
-     * GrDrawTarget to decide whether to put data in buffers, to group index
-     * data that uses the same state (e.g. for deferred rendering), etc.
-     *
-     * After the next indexed draw or pushGeometrySource the indices ptr is no
-     * longer valid and the geometry data cannot be further modified. The
-     * contents that were put in the reserved space can be drawn by multiple
-     * draws, however.
-     *
-     * @param indexCount   the number of indices to reserve space for. Can be 0.
      * @param indices      will point to reserved index space if indexCount is
      *                     non-zero. Illegal to pass NULL if indexCount > 0.
      */
+     bool reserveVertexAndIndexSpace(GrVertexLayout vertexLayout,
+                                     int vertexCount,
+                                     int indexCount,
+                                     void** vertices,
+                                     void** indices);
 
-    bool reserveIndexSpace(int indexCount, 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
@@ -354,7 +255,7 @@
      * Also may hint whether the draw target should be flushed first. This is
      * useful for deferred targets.
      *
-     * @param vertexLayout layout of vertices caller would like to reserve
+     * @param vertexSize   size of vertices caller would like to reserve
      * @param vertexCount  in: hint about how many vertices the caller would
      *                     like to allocate.
      *                     out: a hint about the number of vertices that can be
@@ -368,7 +269,7 @@
      *
      * @return  true if target should be flushed based on the input values.
      */
-    virtual bool geometryHints(GrVertexLayout vertexLayout,
+    virtual bool geometryHints(size_t vertexSize,
                                int* vertexCount,
                                int* indexCount) const;
 
@@ -395,7 +296,7 @@
 
     /**
      * Sets source of vertex data for the next draw. Data does not have to be
-     * in the buffer until drawIndexed or drawNonIndexed.
+     * in the buffer until drawIndexed, drawNonIndexed, or drawIndexedInstances.
      *
      * @param buffer        vertex buffer containing vertex data. Must be
      *                      unlocked before draw call.
@@ -406,13 +307,13 @@
 
     /**
      * Sets source of index data for the next indexed draw. Data does not have
-     * to be in the buffer until drawIndexed or drawNonIndexed.
+     * 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
@@ -420,14 +321,22 @@
      * 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(); 
+    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
@@ -440,7 +349,7 @@
      * Pops the vertex / index sources from the matching push.
      */
     void popGeometrySource();
-    
+
     /**
      * Draws indexed geometry using the current state and current vertex / index
      * sources.
@@ -453,12 +362,15 @@
      * @param indexCount   the number of index elements to read. The index count
      *                     is effectively trimmed to the last completely
      *                     specified primitive.
+     * @param devBounds    optional bounds hint. This is a promise from the caller,
+     *                     not a request for clipping.
      */
     void drawIndexed(GrPrimitiveType type,
                      int startVertex,
                      int startIndex,
                      int vertexCount,
-                     int indexCount);
+                     int indexCount,
+                     const SkRect* devBounds = NULL);
 
     /**
      * Draws non-indexed geometry using the current state and current vertex
@@ -468,23 +380,35 @@
      * @param startVertex  the vertex in the vertex array/buffer corresponding
      *                     to index 0
      * @param vertexCount  one greater than the max index.
+     * @param devBounds    optional bounds hint. This is a promise from the caller,
+     *                     not a request for clipping.
      */
     void drawNonIndexed(GrPrimitiveType type,
                         int startVertex,
-                        int vertexCount);
+                        int vertexCount,
+                        const SkRect* devBounds = NULL);
+
+    /**
+     * Draws path into the stencil buffer. The fill must be either even/odd or
+     * winding (not inverse or hairline). It will respect the HW antialias flag
+     * on the draw state (if possible in the 3D API).
+     */
+    void stencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill);
 
     /**
      * Helper function for drawing rects. This does not use the current index
      * and vertex sources. After returning, the vertex and index sources may
      * have changed. They should be reestablished before the next drawIndexed
      * or drawNonIndexed. This cannot be called between reserving and releasing
-     * geometry. The GrDrawTarget subclass may be able to perform additional
-     * optimizations if drawRect is used rather than drawIndexed or
-     * drawNonIndexed.
+     * geometry.
+     *
+     * A subclass may override this to perform more optimal rect rendering. Its
+     * draws should be funneled through one of the public GrDrawTarget draw methods
+     * (e.g. drawNonIndexed, drawIndexedInstances, ...). The base class draws a two
+     * triangle fan using drawNonIndexed from reserved vertex space.
+     *
      * @param rect      the rect to draw
      * @param matrix    optional matrix applied to rect (before viewMatrix)
-     * @param stageMask bitmask indicating which stages are enabled.
-     *                  Bit i indicates whether stage i is enabled.
      * @param srcRects  specifies rects for stages enabled by stageEnableMask.
      *                  if stageEnableMask bit i is 1, srcRects is not NULL,
      *                  and srcRects[i] is not NULL, then srcRects[i] will be
@@ -497,72 +421,135 @@
      *                      srcMatrices are desired.
      */
     virtual void drawRect(const GrRect& rect,
-                          const GrMatrix* matrix,
-                          StageMask stageMask,
+                          const SkMatrix* matrix,
                           const GrRect* srcRects[],
-                          const GrMatrix* srcMatrices[]);
-
+                          const SkMatrix* srcMatrices[]);
     /**
      * Helper for drawRect when the caller doesn't need separate src rects or
      * matrices.
      */
-    void drawSimpleRect(const GrRect& rect,
-                        const GrMatrix* matrix,
-                        StageMask stageEnableBitfield) {
-         drawRect(rect, matrix, stageEnableBitfield, NULL, NULL);
+    void drawSimpleRect(const GrRect& rect, const SkMatrix* matrix = NULL) {
+        drawRect(rect, matrix, NULL, NULL);
+    }
+    void drawSimpleRect(const GrIRect& irect, const SkMatrix* matrix = NULL) {
+        SkRect rect = SkRect::MakeFromIRect(irect);
+        this->drawRect(rect, matrix, NULL, NULL);
     }
 
     /**
-     * Clear the render target. Ignores the clip and all other draw state
-     * (blend mode, stages, etc). Clears the whole thing if rect is NULL,
-     * otherwise just the rect.
+     * 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.
      */
-    virtual void clear(const GrIRect* rect, GrColor color) = 0;
+    void drawIndexedInstances(GrPrimitiveType type,
+                              int instanceCount,
+                              int verticesPerInstance,
+                              int indicesPerInstance,
+                              const SkRect* devBounds = NULL);
 
     /**
-     * Returns the maximum number of edges that may be specified in a single
-     * draw call when performing edge antialiasing.  This is usually limited
-     * by the number of fragment uniforms which may be uploaded.  Must be a
-     * minimum of six, since a triangle's vertices each belong to two boundary
-     * edges which may be distinct.
+     * Clear the current render target if one isn't passed in. Ignores the
+     * clip and all other draw state (blend mode, stages, etc). Clears the
+     * whole thing if rect is NULL, otherwise just the rect.
      */
-    virtual int getMaxEdges() const { return 6; }
+    virtual void clear(const GrIRect* rect,
+                       GrColor color,
+                       GrRenderTarget* renderTarget = NULL) = 0;
+
+    /**
+     * Release any resources that are cached but not currently in use. This
+     * is intended to give an application some recourse when resources are low.
+     */
+    virtual void purgeResources() {};
+
+    /**
+     * For subclass internal use to invoke a call to onDraw(). See DrawInfo below.
+     */
+    void executeDraw(const DrawInfo& info) { this->onDraw(info); }
 
     ////////////////////////////////////////////////////////////////////////////
 
+    /**
+     * See AutoStateRestore below.
+     */
+    enum ASRInit {
+        kPreserve_ASRInit,
+        kReset_ASRInit
+    };
+
+    /**
+     * Saves off the current state and restores it in the destructor. It will
+     * install a new GrDrawState object on the target (setDrawState) and restore
+     * the previous one in the destructor. The caller should call drawState() to
+     * get the new draw state after the ASR is installed.
+     *
+     * GrDrawState* state = target->drawState();
+     * AutoStateRestore asr(target, GrDrawTarget::kReset_ASRInit).
+     * state->setRenderTarget(rt); // state refers to the GrDrawState set on
+     *                             // target before asr was initialized.
+     *                             // Therefore, rt is set on the GrDrawState
+     *                             // that will be restored after asr's
+     *                             // destructor rather than target's current
+     *                             // GrDrawState.
+     */
     class AutoStateRestore : ::GrNoncopyable {
     public:
+        /**
+         * Default ASR will have no effect unless set() is subsequently called.
+         */
         AutoStateRestore();
-        AutoStateRestore(GrDrawTarget* target);
+
+        /**
+         * Saves the state on target. The state will be restored when the ASR
+         * is destroyed. If this constructor is used do not call set().
+         *
+         * @param init  Should the newly installed GrDrawState be a copy of the
+         *              previous state or a default-initialized GrDrawState.
+         */
+        AutoStateRestore(GrDrawTarget* target, ASRInit init);
+
         ~AutoStateRestore();
 
         /**
-         * if this object is already saving state for param target then
-         * this does nothing. Otherise, it restores previously saved state on
-         * previous target (if any) and saves current state on param target.
+         * Saves the state on target. The state will be restored when the ASR
+         * is destroyed. This should only be called once per ASR object and only
+         * when the default constructor was used. For nested saves use multiple
+         * ASR objects.
+         *
+         * @param init  Should the newly installed GrDrawState be a copy of the
+         *              previous state or a default-initialized GrDrawState.
          */
-        void set(GrDrawTarget* target);
+        void set(GrDrawTarget* target, ASRInit init);
 
     private:
-        GrDrawTarget*       fDrawTarget;
-        SavedDrawState      fDrawState;
-    };
-
-    ////////////////////////////////////////////////////////////////////////////
-
-    /** 
-     * Sets the view matrix to I and preconcats all stage matrices enabled in
-     * mask by the view inverse. Destructor undoes these changes.
-     */
-    class AutoDeviceCoordDraw : ::GrNoncopyable {
-    public:
-        AutoDeviceCoordDraw(GrDrawTarget* target, StageMask stageMask);
-        ~AutoDeviceCoordDraw();
-    private:
-        GrDrawTarget*       fDrawTarget;
-        GrMatrix            fViewMatrix;
-        GrMatrix            fSamplerMatrices[GrDrawState::kNumStages];
-        int                 fStageMask;
+        GrDrawTarget*        fDrawTarget;
+        SkTLazy<GrDrawState> fTempState;
+        GrDrawState*         fSavedState;
     };
 
     ////////////////////////////////////////////////////////////////////////////
@@ -588,7 +575,7 @@
 
     private:
         void reset();
-        
+
         GrDrawTarget* fTarget;
         void*         fVertices;
         void*         fIndices;
@@ -603,16 +590,20 @@
             fClip = fTarget->getClip();
         }
 
+        AutoClipRestore(GrDrawTarget* target, const SkIRect& newClip);
+
         ~AutoClipRestore() {
             fTarget->setClip(fClip);
         }
     private:
-        GrDrawTarget* fTarget;
-        GrClip        fClip;
+        GrDrawTarget*           fTarget;
+        const GrClipData*       fClip;
+        SkTLazy<SkClipStack>    fStack;
+        GrClipData              fReplacementClip;
     };
-    
+
     ////////////////////////////////////////////////////////////////////////////
-    
+
     class AutoGeometryPush : ::GrNoncopyable {
     public:
         AutoGeometryPush(GrDrawTarget* target) {
@@ -627,203 +618,6 @@
         GrDrawTarget* fTarget;
     };
 
-    ////////////////////////////////////////////////////////////////////////////
-    // Helpers for picking apart vertex layouts
-
-    /**
-     * Helper function to compute the size of a vertex from a vertex layout
-     * @return size of a single vertex.
-     */
-    static size_t VertexSize(GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function for determining the index of texture coordinates that
-     * is input for a texture stage. Note that a stage may instead use positions
-     * as texture coordinates, in which case the result of the function is
-     * indistinguishable from the case when the stage is disabled.
-     *
-     * @param stage         the stage to query
-     * @param vertexLayout  layout to query
-     *
-     * @return the texture coordinate index or -1 if the stage doesn't use
-     *         separate (non-position) texture coordinates.
-     */
-    static int VertexTexCoordsForStage(int stage, GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the offset of texture coordinates in a vertex
-     * @return offset of texture coordinates in vertex layout or -1 if the
-     *         layout has no texture coordinates. Will be 0 if positions are
-     *         used as texture coordinates for the stage.
-     */
-    static int VertexStageCoordOffset(int stage, GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the offset of the color in a vertex
-     * @return offset of color in vertex layout or -1 if the
-     *         layout has no color.
-     */
-    static int VertexColorOffset(GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the offset of the coverage in a vertex
-     * @return offset of coverage in vertex layout or -1 if the
-     *         layout has no coverage.
-     */
-    static int VertexCoverageOffset(GrVertexLayout vertexLayout);
-
-     /**
-      * Helper function to compute the offset of the edge pts in a vertex
-      * @return offset of edge in vertex layout or -1 if the
-      *         layout has no edge.
-      */
-     static int VertexEdgeOffset(GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to determine if vertex layout contains explicit texture
-     * coordinates of some index.
-     *
-     * @param coordIndex    the tex coord index to query
-     * @param vertexLayout  layout to query
-     *
-     * @return true if vertex specifies texture coordinates for the index,
-     *              false otherwise.
-     */
-    static bool VertexUsesTexCoordIdx(int coordIndex,
-                                      GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to determine if vertex layout contains either explicit or
-     * implicit texture coordinates for a stage.
-     *
-     * @param stage         the stage to query
-     * @param vertexLayout  layout to query
-     *
-     * @return true if vertex specifies texture coordinates for the stage,
-     *              false otherwise.
-     */
-    static bool VertexUsesStage(int stage, GrVertexLayout vertexLayout);
-
-    /**
-     * Helper function to compute the size of each vertex and the offsets of
-     * texture coordinates and color. Determines tex coord offsets by tex coord
-     * index rather than by stage. (Each stage can be mapped to any t.c. index
-     * by StageTexCoordVertexLayoutBit.)
-     *
-     * @param vertexLayout          the layout to query
-     * @param texCoordOffsetsByIdx  after return it is the offset of each
-     *                              tex coord index in the vertex or -1 if
-     *                              index isn't used. (optional)
-     * @param colorOffset           after return it is the offset of the
-     *                              color field in each vertex, or -1 if
-     *                              there aren't per-vertex colors. (optional)
-     * @param coverageOffset        after return it is the offset of the
-     *                              coverage field in each vertex, or -1 if
-     *                              there aren't per-vertex coeverages.
-     *                              (optional)
-     * @param edgeOffset            after return it is the offset of the
-     *                              edge eq field in each vertex, or -1 if
-     *                              there aren't per-vertex edge equations.
-     *                              (optional)
-     * @return size of a single vertex
-     */
-    static int VertexSizeAndOffsetsByIdx(GrVertexLayout vertexLayout,
-                   int texCoordOffsetsByIdx[GrDrawState::kMaxTexCoords],
-                   int *colorOffset,
-                   int *coverageOffset,
-                   int* edgeOffset);
-
-    /**
-     * Helper function to compute the size of each vertex and the offsets of
-     * texture coordinates and color. Determines tex coord offsets by stage
-     * rather than by index. (Each stage can be mapped to any t.c. index
-     * by StageTexCoordVertexLayoutBit.) If a stage uses positions for
-     * tex coords then that stage's offset will be 0 (positions are always at 0).
-     *
-     * @param vertexLayout              the layout to query
-     * @param texCoordOffsetsByStage    after return it is the offset of each
-     *                                  tex coord index in the vertex or -1 if
-     *                                  index isn't used. (optional)
-     * @param colorOffset               after return it is the offset of the
-     *                                  color field in each vertex, or -1 if
-     *                                  there aren't per-vertex colors.
-     *                                  (optional)
-     * @param coverageOffset            after return it is the offset of the
-     *                                  coverage field in each vertex, or -1 if
-     *                                  there aren't per-vertex coeverages.
-     *                                  (optional)
-     * @param edgeOffset                after return it is the offset of the
-     *                                  edge eq field in each vertex, or -1 if
-     *                                  there aren't per-vertex edge equations.
-     *                                  (optional)
-     * @return size of a single vertex
-     */
-    static int VertexSizeAndOffsetsByStage(GrVertexLayout vertexLayout,
-                   int texCoordOffsetsByStage[GrDrawState::kNumStages],
-                   int* colorOffset,
-                   int* coverageOffset,
-                   int* edgeOffset);
-
-    /**
-     * Accessing positions, texture coords, or colors, of a vertex within an
-     * array is a hassle involving casts and simple math. These helpers exist
-     * to keep GrDrawTarget clients' code a bit nicer looking.
-     */
-
-    /**
-     * Gets a pointer to a GrPoint of a vertex's position or texture
-     * coordinate.
-     * @param vertices      the vetex array
-     * @param vertexIndex   the index of the vertex in the array
-     * @param vertexSize    the size of each vertex in the array
-     * @param offset        the offset in bytes of the vertex component.
-     *                      Defaults to zero (corresponding to vertex position)
-     * @return pointer to the vertex component as a GrPoint
-     */
-    static GrPoint* GetVertexPoint(void* vertices,
-                                   int vertexIndex,
-                                   int vertexSize,
-                                   int offset = 0) {
-        intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<GrPoint*>(start + offset +
-                                 vertexIndex * vertexSize);
-    }
-    static const GrPoint* GetVertexPoint(const void* vertices,
-                                         int vertexIndex,
-                                         int vertexSize,
-                                         int offset = 0) {
-        intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<const GrPoint*>(start + offset +
-                                       vertexIndex * vertexSize);
-    }
-
-    /**
-     * Gets a pointer to a GrColor inside a vertex within a vertex array.
-     * @param vertices      the vetex array
-     * @param vertexIndex   the index of the vertex in the array
-     * @param vertexSize    the size of each vertex in the array
-     * @param offset        the offset in bytes of the vertex color
-     * @return pointer to the vertex component as a GrColor
-     */
-    static GrColor* GetVertexColor(void* vertices,
-                                   int vertexIndex,
-                                   int vertexSize,
-                                   int offset) {
-        intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<GrColor*>(start + offset +
-                                 vertexIndex * vertexSize);
-    }
-    static const GrColor* GetVertexColor(const void* vertices,
-                                         int vertexIndex,
-                                         int vertexSize,
-                                         int offset) {
-        const intptr_t start = GrTCast<intptr_t>(vertices);
-        return GrTCast<const GrColor*>(start + offset +
-                                       vertexIndex * vertexSize);
-    }
-
-    static void VertexLayoutUnitTest();
-
 protected:
 
     /**
@@ -863,28 +657,24 @@
     };
     GR_DECL_BITFIELD_OPS_FRIENDS(BlendOptFlags);
 
-    // Determines what optimizations can be applied based on the blend.
-    // The coeffecients may have to be tweaked in order for the optimization
-    // to work. srcCoeff and dstCoeff are optional params that receive the
-    // tweaked coeffecients.
-    // Normally the function looks at the current state to see if coverage
-    // is enabled. By setting forceCoverage the caller can speculatively
-    // determine the blend optimizations that would be used if there was
-    // partial pixel coverage
+    /**
+     * Determines what optimizations can be applied based on the blend. The coefficients may have
+     * to be tweaked in order for the optimization to work. srcCoeff and dstCoeff are optional
+     * params that receive the tweaked coefficients. Normally the function looks at the current
+     * state to see if coverage is enabled. By setting forceCoverage the caller can speculatively
+     * determine the blend optimizations that would be used if there was partial pixel coverage.
+     */
     BlendOptFlags getBlendOpts(bool forceCoverage = false,
                                GrBlendCoeff* srcCoeff = NULL,
                                GrBlendCoeff* dstCoeff = NULL) const;
 
-    // determine if src alpha is guaranteed to be one for all src pixels
-    bool srcAlphaWillBeOne() const;
-
     enum GeometrySrcType {
         kNone_GeometrySrcType,     //<! src has not been specified
         kReserved_GeometrySrcType, //<! src was set using reserve*Space
         kArray_GeometrySrcType,    //<! src was set using set*SourceToArray
         kBuffer_GeometrySrcType    //<! src was set using set*SourceToBuffer
     };
-    
+
     struct GeometrySrcState {
         GeometrySrcType         fVertexSrc;
         union {
@@ -893,7 +683,7 @@
             // valid if src type is reserved or array
             int                     fVertexCount;
         };
-        
+
         GeometrySrcType         fIndexSrc;
         union {
             // valid if src type is buffer
@@ -901,100 +691,136 @@
             // valid if src type is reserved or array
             int                     fIndexCount;
         };
-        
+
         GrVertexLayout          fVertexLayout;
     };
-    
-    // given a vertex layout and a draw state, will a stage be used?
-    static bool StageWillBeUsed(int stage, GrVertexLayout layout, 
-                                const GrDrawState& state) {
-        return NULL != state.getTexture(stage) &&
-               VertexUsesStage(stage, layout);
-    }
 
-    bool isStageEnabled(int stage) const {
-        return StageWillBeUsed(stage, this->getGeomSrc().fVertexLayout, 
-                               fCurrDrawState);
-    }
-
-    StageMask enabledStages() const {
-        StageMask mask = 0;
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            mask |= this->isStageEnabled(s) ? 1 : 0;
+    int indexCountInCurrentSource() const {
+        const GeometrySrcState& src = this->getGeomSrc();
+        switch (src.fIndexSrc) {
+            case kNone_GeometrySrcType:
+                return 0;
+            case kReserved_GeometrySrcType:
+            case kArray_GeometrySrcType:
+                return src.fIndexCount;
+            case kBuffer_GeometrySrcType:
+                return src.fIndexBuffer->sizeInBytes() / sizeof(uint16_t);
+            default:
+                GrCrash("Unexpected Index Source.");
+                return 0;
         }
-        return mask;
     }
 
-    // Helpers for GrDrawTarget subclasses that won't have private access to
-    // SavedDrawState but need to peek at the state values.
-    static GrDrawState& accessSavedDrawState(SavedDrawState& sds) {
-        return *sds.fState.get();
+    // allows derived class to set the caps
+    CapsInternals* capsInternals() { return &fCaps.fInternals; }
+
+    // A subclass may override this function if it wishes to be notified when the clip is changed.
+    // The override should call INHERITED::clipWillBeSet().
+    virtual void clipWillBeSet(const GrClipData* clipData);
+
+    // subclasses must call this in their destructors to ensure all vertex
+    // and index sources have been released (including those held by
+    // pushGeometrySource())
+    void releaseGeometry();
+
+    // accessors for derived classes
+    const GeometrySrcState& getGeomSrc() const { return fGeoSrcStateStack.back(); }
+    // it is preferable to call this rather than getGeomSrc()->fVertexLayout because of the assert.
+    GrVertexLayout getVertexLayout() const {
+        // the vertex layout is only valid if a vertex source has been specified.
+        GrAssert(this->getGeomSrc().fVertexSrc != kNone_GeometrySrcType);
+        return this->getGeomSrc().fVertexLayout;
     }
-    static const GrDrawState& accessSavedDrawState(const SavedDrawState& sds){
-        return *sds.fState.get();
-    }
+
+    Caps fCaps;
+
+    /**
+     * Used to communicate draws to subclass's onDraw function.
+     */
+    class DrawInfo {
+    public:
+        DrawInfo(const DrawInfo& di) { (*this) = di; }
+        DrawInfo& operator =(const DrawInfo& di);
+
+        GrPrimitiveType primitiveType() const { return fPrimitiveType; }
+        int startVertex() const { return fStartVertex; }
+        int startIndex() const { return fStartIndex; }
+        int vertexCount() const { return fVertexCount; }
+        int indexCount() const { return fIndexCount; }
+        int verticesPerInstance() const { return fVerticesPerInstance; }
+        int indicesPerInstance() const { return fIndicesPerInstance; }
+        int instanceCount() const { return fInstanceCount; }
+
+        bool isIndexed() const { return fIndexCount > 0; }
+#if GR_DEBUG
+        bool isInstanced() const; // this version is longer because of asserts
+#else
+        bool isInstanced() const { return fInstanceCount > 0; }
+#endif
+
+        // adds or remove instances
+        void adjustInstanceCount(int instanceOffset);
+        // shifts the start vertex
+        void adjustStartVertex(int vertexOffset);
+        // shifts the start index
+        void adjustStartIndex(int indexOffset);
+
+        void setDevBounds(const SkRect& bounds) {
+            fDevBoundsStorage = bounds;
+            fDevBounds = &fDevBoundsStorage;
+        }
+        const SkRect* getDevBounds() const { return fDevBounds; }
+
+    private:
+        DrawInfo() { fDevBounds = NULL; }
+
+        friend class GrDrawTarget;
+
+        GrPrimitiveType fPrimitiveType;
+
+        int             fStartVertex;
+        int             fStartIndex;
+        int             fVertexCount;
+        int             fIndexCount;
+
+        int             fInstanceCount;
+        int             fVerticesPerInstance;
+        int             fIndicesPerInstance;
+
+        SkRect          fDevBoundsStorage;
+        SkRect*         fDevBounds;
+    };
+
+private:
+    // A subclass can optionally overload this function to be notified before
+    // vertex and index space is reserved.
+    virtual void willReserveVertexAndIndexSpace(size_t vertexSize, int vertexCount, int indexCount) {}
 
     // implemented by subclass to allocate space for reserved geom
-    virtual bool onReserveVertexSpace(GrVertexLayout vertexLayout,
-                                      int vertexCount,
-                                      void** vertices) = 0;
+    virtual bool onReserveVertexSpace(size_t vertexSize, int vertexCount, void** vertices) = 0;
     virtual bool onReserveIndexSpace(int indexCount, void** indices) = 0;
     // implemented by subclass to handle release of reserved geom space
     virtual void releaseReservedVertexSpace() = 0;
     virtual void releaseReservedIndexSpace() = 0;
     // subclass must consume array contents when set
-    virtual void onSetVertexSourceToArray(const void* vertexArray,
-                                          int vertexCount) = 0;
-    virtual void onSetIndexSourceToArray(const void* indexArray,
-                                         int indexCount) = 0;
+    virtual void onSetVertexSourceToArray(const void* vertexArray, int vertexCount) = 0;
+    virtual void onSetIndexSourceToArray(const void* indexArray, int indexCount) = 0;
     // subclass is notified that geom source will be set away from an array
     virtual void releaseVertexArray() = 0;
     virtual void releaseIndexArray() = 0;
-    // subclass overrides to be notified just before geo src state
-    // is pushed/popped.
+    // subclass overrides to be notified just before geo src state is pushed/popped.
     virtual void geometrySourceWillPush() = 0;
     virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) = 0;
     // subclass called to perform drawing
-    virtual void onDrawIndexed(GrPrimitiveType type,
-                               int startVertex,
-                               int startIndex,
-                               int vertexCount,
-                               int indexCount) = 0;
-    virtual void onDrawNonIndexed(GrPrimitiveType type,
-                                  int startVertex,
-                                  int vertexCount) = 0;
-    // subclass overrides to be notified when clip is set. Must call
-    // INHERITED::clipwillBeSet
-    virtual void clipWillBeSet(const GrClip& clip);
+    virtual void onDraw(const DrawInfo&) = 0;
+    virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType fill) = 0;
 
-    // Helpers for drawRect, protected so subclasses that override drawRect
-    // can use them.
-    static GrVertexLayout GetRectVertexLayout(StageMask stageEnableBitfield,
-                                              const GrRect* srcRects[]);
+    // helpers for reserving vertex and index space.
+    bool reserveVertexSpace(GrVertexLayout vertexLayout,
+                            int vertexCount,
+                            void** vertices);
+    bool reserveIndexSpace(int indexCount, void** indices);
 
-    static void SetRectVertices(const GrRect& rect,
-                                const GrMatrix* matrix,
-                                const GrRect* srcRects[],
-                                const GrMatrix* srcMatrices[],
-                                GrVertexLayout layout,
-                                void* vertices);
-
-    // accessor for derived classes
-    const GeometrySrcState& getGeomSrc() const {
-        return fGeoSrcStateStack.back();
-    }
-
-    GrClip fClip;
-
-    GrDrawState fCurrDrawState;
-
-    Caps fCaps;
-
-    // subclasses must call this in their destructors to ensure all vertex
-    // and index sources have been released (including those held by 
-    // pushGeometrySource())
-    void releaseGeometry();
-private:
     // called by drawIndexed and drawNonIndexed. Use a negative indexCount to
     // indicate non-indexed drawing.
     bool checkDraw(GrPrimitiveType type, int startVertex,
@@ -1003,13 +829,16 @@
     // called when setting a new vert/idx source to unref prev vb/ib
     void releasePreviousVertexSource();
     void releasePreviousIndexSource();
-    
+
     enum {
         kPreallocGeoSrcStateStackCnt = 4,
     };
-    SkSTArray<kPreallocGeoSrcStateStackCnt, 
-              GeometrySrcState, true>           fGeoSrcStateStack;
-    
+    SkSTArray<kPreallocGeoSrcStateStackCnt, GeometrySrcState, true> fGeoSrcStateStack;
+    const GrClipData*                                               fClip;
+    GrDrawState*                                                    fDrawState;
+    GrDrawState                                                     fDefaultDrawState;
+
+    typedef GrRefCnt INHERITED;
 };
 
 GR_MAKE_BITFIELD_OPS(GrDrawTarget::BlendOptFlags);
diff --git a/src/gpu/GrEffect.cpp b/src/gpu/GrEffect.cpp
new file mode 100644
index 0000000..40a519a
--- /dev/null
+++ b/src/gpu/GrEffect.cpp
@@ -0,0 +1,99 @@
+/*
+ * 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 "GrEffect.h"
+#include "GrBackendEffectFactory.h"
+#include "GrContext.h"
+#include "GrMemoryPool.h"
+#include "SkTLS.h"
+
+SK_DEFINE_INST_COUNT(GrEffect)
+
+#if SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+SkTArray<GrEffectTestFactory*, true>* GrEffectTestFactory::GetFactories() {
+    static SkTArray<GrEffectTestFactory*, true> gFactories;
+    return &gFactories;
+}
+#endif
+
+namespace GrEffectUnitTest {
+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, SkFloatToScalar(0.00013f));
+        gMatrices[4].set(SkMatrix::kMPersp1, SkFloatToScalar(-0.000039f));
+        gOnce = true;
+    }
+    return gMatrices[random->nextULessThan(static_cast<uint32_t>(SK_ARRAY_COUNT(gMatrices)))];
+}
+}
+
+class GrEffect_Globals {
+public:
+    static GrMemoryPool* GetTLS() {
+        return (GrMemoryPool*)SkTLS::Get(CreateTLS, DeleteTLS);
+    }
+
+private:
+    static void* CreateTLS() {
+        return SkNEW_ARGS(GrMemoryPool, (4096, 4096));
+    }
+
+    static void DeleteTLS(void* pool) {
+        SkDELETE(reinterpret_cast<GrMemoryPool*>(pool));
+    }
+};
+
+int32_t GrBackendEffectFactory::fCurrEffectClassID = GrBackendEffectFactory::kIllegalEffectClassID;
+
+///////////////////////////////////////////////////////////////////////////////
+
+SK_DEFINE_INST_COUNT(GrEffectRef)
+
+GrEffectRef::~GrEffectRef() {
+    GrAssert(1 == this->getRefCnt());
+    fEffect->EffectRefDestroyed();
+    fEffect->unref();
+}
+
+void* GrEffectRef::operator new(size_t size) {
+    return GrEffect_Globals::GetTLS()->allocate(size);
+}
+
+void GrEffectRef::operator delete(void* target) {
+    GrEffect_Globals::GetTLS()->release(target);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrEffect::~GrEffect() {
+    GrAssert(NULL == fEffectRef);
+}
+
+const char* GrEffect::name() const {
+    return this->getFactory().name();
+}
+
+void GrEffect::addTextureAccess(const GrTextureAccess* access) {
+    fTextureAccesses.push_back(access);
+}
+
+void* GrEffect::operator new(size_t size) {
+    return GrEffect_Globals::GetTLS()->allocate(size);
+}
+
+void GrEffect::operator delete(void* target) {
+    GrEffect_Globals::GetTLS()->release(target);
+}
diff --git a/src/gpu/GrGeometryBuffer.cpp b/src/gpu/GrGeometryBuffer.cpp
new file mode 100644
index 0000000..202d0c3
--- /dev/null
+++ b/src/gpu/GrGeometryBuffer.cpp
@@ -0,0 +1,10 @@
+/*
+ * 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 "GrGeometryBuffer.h"
+
+SK_DEFINE_INST_COUNT(GrGeometryBuffer)
diff --git a/src/gpu/GrGeometryBuffer.h b/src/gpu/GrGeometryBuffer.h
index c74b254..52318c1 100644
--- a/src/gpu/GrGeometryBuffer.h
+++ b/src/gpu/GrGeometryBuffer.h
@@ -19,6 +19,7 @@
  */
 class GrGeometryBuffer : public GrResource {
 public:
+    SK_DECLARE_INST_COUNT(GrGeometryBuffer);
 
     /**
      *Retrieves whether the buffer was created with the dynamic flag
@@ -75,8 +76,8 @@
     virtual size_t sizeInBytes() const { return fSizeInBytes; }
 
 protected:
-    GrGeometryBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
-        : INHERITED(gpu)
+    GrGeometryBuffer(GrGpu* gpu, bool isWrapped, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, isWrapped)
         , fSizeInBytes(sizeInBytes)
         , fDynamic(dynamic) {}
 
diff --git a/src/gpu/GrGpu.cpp b/src/gpu/GrGpu.cpp
index 32338ca..3da8219 100644
--- a/src/gpu/GrGpu.cpp
+++ b/src/gpu/GrGpu.cpp
@@ -10,10 +10,8 @@
 #include "GrGpu.h"
 
 #include "GrBufferAllocPool.h"
-#include "GrClipIterator.h"
 #include "GrContext.h"
 #include "GrIndexBuffer.h"
-#include "GrPathRenderer.h"
 #include "GrStencilBuffer.h"
 #include "GrVertexBuffer.h"
 
@@ -25,8 +23,6 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-extern void gr_run_unittests();
-
 #define DEBUG_INVAL_BUFFER    0xdeadcafe
 #define DEBUG_INVAL_START_IDX -1
 
@@ -37,16 +33,12 @@
     , fIndexPool(NULL)
     , fVertexPoolUseCnt(0)
     , fIndexPoolUseCnt(0)
-    , fQuadIndexBuffer(NULL)
     , fUnitSquareVertexBuffer(NULL)
-    , fPathRendererChain(NULL)
-    , fContextIsDirty(true)
-    , fResourceHead(NULL) {
+    , fQuadIndexBuffer(NULL)
+    , fContextIsDirty(true) {
 
-#if GR_DEBUG
-    //gr_run_unittests();
-#endif
-        
+    fClipMaskManager.setGpu(this);
+
     fGeomPoolStateStack.push_back();
 #if GR_DEBUG
     GeometryPoolState& poolState = fGeomPoolStateStack.back();
@@ -55,7 +47,10 @@
     poolState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
     poolState.fPoolStartIndex = DEBUG_INVAL_START_IDX;
 #endif
-    resetStats();
+
+    for (int i = 0; i < kGrPixelConfigCount; ++i) {
+        fConfigRenderSupport[i] = false;
+    };
 }
 
 GrGpu::~GrGpu() {
@@ -64,8 +59,10 @@
 
 void GrGpu::abandonResources() {
 
-    while (NULL != fResourceHead) {
-        fResourceHead->abandon();
+    fClipMaskManager.releaseResources();
+
+    while (NULL != fResourceList.head()) {
+        fResourceList.head()->abandon();
     }
 
     GrAssert(NULL == fQuadIndexBuffer || !fQuadIndexBuffer->isValid());
@@ -77,14 +74,14 @@
     fVertexPool = NULL;
     delete fIndexPool;
     fIndexPool = NULL;
-    // in case path renderer has any GrResources, start from scratch
-    GrSafeSetNull(fPathRendererChain);
 }
 
 void GrGpu::releaseResources() {
 
-    while (NULL != fResourceHead) {
-        fResourceHead->release();
+    fClipMaskManager.releaseResources();
+
+    while (NULL != fResourceList.head()) {
+        fResourceList.head()->release();
     }
 
     GrAssert(NULL == fQuadIndexBuffer || !fQuadIndexBuffer->isValid());
@@ -96,40 +93,20 @@
     fVertexPool = NULL;
     delete fIndexPool;
     fIndexPool = NULL;
-    // in case path renderer has any GrResources, start from scratch
-    GrSafeSetNull(fPathRendererChain);
 }
 
 void GrGpu::insertResource(GrResource* resource) {
     GrAssert(NULL != resource);
     GrAssert(this == resource->getGpu());
-    GrAssert(NULL == resource->fNext);
-    GrAssert(NULL == resource->fPrevious);
 
-    resource->fNext = fResourceHead;
-    if (NULL != fResourceHead) {
-        GrAssert(NULL == fResourceHead->fPrevious);
-        fResourceHead->fPrevious = resource;
-    }
-    fResourceHead = resource;
+    fResourceList.addToHead(resource);
 }
 
 void GrGpu::removeResource(GrResource* resource) {
     GrAssert(NULL != resource);
-    GrAssert(NULL != fResourceHead);
+    GrAssert(this == resource->getGpu());
 
-    if (fResourceHead == resource) {
-        GrAssert(NULL == resource->fPrevious);
-        fResourceHead = resource->fNext;
-    } else {
-        GrAssert(NULL != fResourceHead);
-        resource->fPrevious->fNext = resource->fNext;
-    }
-    if (NULL != resource->fNext) {
-        resource->fNext->fPrevious = resource->fPrevious;
-    }
-    resource->fNext = NULL;
-    resource->fPrevious = NULL;
+    fResourceList.remove(resource);
 }
 
 
@@ -143,9 +120,13 @@
 
 GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
                                 const void* srcData, size_t rowBytes) {
+    if (kUnknown_GrPixelConfig == desc.fConfig) {
+        return NULL;
+    }
+
     this->handleDirtyContext();
     GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes);
-    if (NULL != tex && 
+    if (NULL != tex &&
         (kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
         !(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
         GrAssert(NULL != tex->asRenderTarget());
@@ -160,7 +141,7 @@
 
 bool GrGpu::attachStencilBufferToRenderTarget(GrRenderTarget* rt) {
     GrAssert(NULL == rt->getStencilBuffer());
-    GrStencilBuffer* sb = 
+    GrStencilBuffer* sb =
         this->getContext()->findStencilBuffer(rt->width(),
                                               rt->height(),
                                               rt->numSamples());
@@ -174,9 +155,6 @@
     }
     if (this->createStencilBufferForRenderTarget(rt,
                                                  rt->width(), rt->height())) {
-        rt->getStencilBuffer()->ref();
-        rt->getStencilBuffer()->transferToCacheAndLock();
-
         // 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
@@ -193,9 +171,9 @@
     }
 }
 
-GrTexture* GrGpu::createPlatformTexture(const GrPlatformTextureDesc& desc) {
+GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc) {
     this->handleDirtyContext();
-    GrTexture* tex = this->onCreatePlatformTexture(desc);
+    GrTexture* tex = this->onWrapBackendTexture(desc);
     if (NULL == tex) {
         return NULL;
     }
@@ -210,9 +188,9 @@
     }
 }
 
-GrRenderTarget* GrGpu::createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
+GrRenderTarget* GrGpu::wrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
     this->handleDirtyContext();
-    return this->onCreatePlatformRenderTarget(desc);
+    return this->onWrapBackendRenderTarget(desc);
 }
 
 GrVertexBuffer* GrGpu::createVertexBuffer(uint32_t size, bool dynamic) {
@@ -225,7 +203,19 @@
     return this->onCreateIndexBuffer(size, dynamic);
 }
 
-void GrGpu::clear(const GrIRect* rect, GrColor color) {
+GrPath* GrGpu::createPath(const SkPath& path) {
+    GrAssert(fCaps.pathStencilingSupport());
+    this->handleDirtyContext();
+    return this->onCreatePath(path);
+}
+
+void GrGpu::clear(const GrIRect* rect,
+                  GrColor color,
+                  GrRenderTarget* renderTarget) {
+    GrDrawState::AutoRenderTargetRestore art;
+    if (NULL != renderTarget) {
+        art.set(this->drawState(), renderTarget);
+    }
     if (NULL == this->getDrawState().getRenderTarget()) {
         return;
     }
@@ -242,8 +232,6 @@
                        int left, int top, int width, int height,
                        GrPixelConfig config, void* buffer,
                        size_t rowBytes, bool invertY) {
-    GrAssert(GrPixelConfigIsUnpremultiplied(config) ==
-             GrPixelConfigIsUnpremultiplied(target->config()));
     this->handleDirtyContext();
     return this->onReadPixels(target, left, top, width, height,
                               config, buffer, rowBytes, invertY);
@@ -253,8 +241,6 @@
                                int left, int top, int width, int height,
                                GrPixelConfig config, const void* buffer,
                                size_t rowBytes) {
-    GrAssert(GrPixelConfigIsUnpremultiplied(config) ==
-             GrPixelConfigIsUnpremultiplied(texture->config()));
     this->handleDirtyContext();
     this->onWriteTexturePixels(texture, left, top, width, height,
                                config, buffer, rowBytes);
@@ -315,14 +301,14 @@
 
         static const GrPoint DATA[] = {
             { 0,            0 },
-            { GR_Scalar1,   0 },
-            { GR_Scalar1,   GR_Scalar1 },
-            { 0,            GR_Scalar1 }
+            { SK_Scalar1,   0 },
+            { SK_Scalar1,   SK_Scalar1 },
+            { 0,            SK_Scalar1 }
 #if 0
             GrPoint(0,         0),
-            GrPoint(GR_Scalar1,0),
-            GrPoint(GR_Scalar1,GR_Scalar1),
-            GrPoint(0,         GR_Scalar1)
+            GrPoint(SK_Scalar1,0),
+            GrPoint(SK_Scalar1,SK_Scalar1),
+            GrPoint(0,         SK_Scalar1)
 #endif
         };
         static const size_t SIZE = sizeof(DATA);
@@ -343,404 +329,19 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-const GrStencilSettings* GrGpu::GetClipStencilSettings(void) {
-    // stencil settings to use when clip is in stencil
-    GR_STATIC_CONST_SAME_STENCIL_STRUCT(sClipStencilSettings,
-        kKeep_StencilOp,
-        kKeep_StencilOp,
-        kAlwaysIfInClip_StencilFunc,
-        0x0000,
-        0x0000,
-        0x0000);
-    return GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&sClipStencilSettings);
-}
+bool GrGpu::setupClipAndFlushState(DrawType type) {
 
-// mapping of clip-respecting stencil funcs to normal stencil funcs
-// mapping depends on whether stencil-clipping is in effect.
-static const GrStencilFunc gGrClipToNormalStencilFunc[2][kClipStencilFuncCount] = {
-    {// Stencil-Clipping is DISABLED, effectively always inside the clip
-        // In the Clip Funcs
-        kAlways_StencilFunc,          // kAlwaysIfInClip_StencilFunc
-        kEqual_StencilFunc,           // kEqualIfInClip_StencilFunc
-        kLess_StencilFunc,            // kLessIfInClip_StencilFunc
-        kLEqual_StencilFunc,          // kLEqualIfInClip_StencilFunc
-        // Special in the clip func that forces user's ref to be 0.
-        kNotEqual_StencilFunc,        // kNonZeroIfInClip_StencilFunc
-                                      // make ref 0 and do normal nequal.
-    },
-    {// Stencil-Clipping is ENABLED
-        // In the Clip Funcs
-        kEqual_StencilFunc,           // kAlwaysIfInClip_StencilFunc
-                                      // eq stencil clip bit, mask
-                                      // out user bits.
-
-        kEqual_StencilFunc,           // kEqualIfInClip_StencilFunc
-                                      // add stencil bit to mask and ref
-
-        kLess_StencilFunc,            // kLessIfInClip_StencilFunc
-        kLEqual_StencilFunc,          // kLEqualIfInClip_StencilFunc
-                                      // for both of these we can add
-                                      // the clip bit to the mask and
-                                      // ref and compare as normal
-        // Special in the clip func that forces user's ref to be 0.
-        kLess_StencilFunc,            // kNonZeroIfInClip_StencilFunc
-                                      // make ref have only the clip bit set
-                                      // and make comparison be less
-                                      // 10..0 < 1..user_bits..
-    }
-};
-
-GrStencilFunc GrGpu::ConvertStencilFunc(bool stencilInClip, GrStencilFunc func) {
-    GrAssert(func >= 0);
-    if (func >= kBasicStencilFuncCount) {
-        GrAssert(func < kStencilFuncCount);
-        func = gGrClipToNormalStencilFunc[stencilInClip ? 1 : 0][func - kBasicStencilFuncCount];
-        GrAssert(func >= 0 && func < kBasicStencilFuncCount);
-    }
-    return func;
-}
-
-void GrGpu::ConvertStencilFuncAndMask(GrStencilFunc func,
-                                      bool clipInStencil,
-                                      unsigned int clipBit,
-                                      unsigned int userBits,
-                                      unsigned int* ref,
-                                      unsigned int* mask) {
-    if (func < kBasicStencilFuncCount) {
-        *mask &= userBits;
-        *ref &= userBits;
-    } else {
-        if (clipInStencil) {
-            switch (func) {
-                case kAlwaysIfInClip_StencilFunc:
-                    *mask = clipBit;
-                    *ref = clipBit;
-                    break;
-                case kEqualIfInClip_StencilFunc:
-                case kLessIfInClip_StencilFunc:
-                case kLEqualIfInClip_StencilFunc:
-                    *mask = (*mask & userBits) | clipBit;
-                    *ref = (*ref & userBits) | clipBit;
-                    break;
-                case kNonZeroIfInClip_StencilFunc:
-                    *mask = (*mask & userBits) | clipBit;
-                    *ref = clipBit;
-                    break;
-                default:
-                    GrCrash("Unknown stencil func");
-            }
-        } else {
-            *mask &= userBits;
-            *ref &= userBits;
-        }
-    }
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-#define VISUALIZE_COMPLEX_CLIP 0
-
-#if VISUALIZE_COMPLEX_CLIP
-    #include "GrRandom.h"
-    GrRandom gRandom;
-    #define SET_RANDOM_COLOR drawState->setColor(0xff000000 | gRandom.nextU());
-#else
-    #define SET_RANDOM_COLOR
-#endif
-
-namespace {
-// determines how many elements at the head of the clip can be skipped and
-// whether the initial clear should be to the inside- or outside-the-clip value,
-// and what op should be used to draw the first element that isn't skipped.
-int process_initial_clip_elements(const GrClip& clip,
-                                  const GrRect& bounds,
-                                  bool* clearToInside,
-                                  GrSetOp* startOp) {
-
-    // logically before the first element of the clip stack is 
-    // processed the clip is entirely open. However, depending on the
-    // first set op we may prefer to clear to 0 for performance. We may
-    // also be able to skip the initial clip paths/rects. We loop until
-    // we cannot skip an element.
-    int curr;
-    bool done = false;
-    *clearToInside = true;
-    int count = clip.getElementCount();
-
-    for (curr = 0; curr < count && !done; ++curr) {
-        switch (clip.getOp(curr)) {
-            case kReplace_SetOp:
-                // replace ignores everything previous
-                *startOp = kReplace_SetOp;
-                *clearToInside = false;
-                done = true;
-                break;
-            case kIntersect_SetOp:
-                // if this element contains the entire bounds then we
-                // can skip it.
-                if (kRect_ClipType == clip.getElementType(curr)
-                    && clip.getRect(curr).contains(bounds)) {
-                    break;
-                }
-                // if everything is initially clearToInside then intersect is
-                // same as clear to 0 and treat as a replace. Otherwise,
-                // set stays empty.
-                if (*clearToInside) {
-                    *startOp = kReplace_SetOp;
-                    *clearToInside = false;
-                    done = true;
-                }
-                break;
-                // we can skip a leading union.
-            case kUnion_SetOp:
-                // if everything is initially outside then union is
-                // same as replace. Otherwise, every pixel is still 
-                // clearToInside
-                if (!*clearToInside) {
-                    *startOp = kReplace_SetOp;
-                    done = true;
-                }
-                break;
-            case kXor_SetOp:
-                // xor is same as difference or replace both of which
-                // can be 1-pass instead of 2 for xor.
-                if (*clearToInside) {
-                    *startOp = kDifference_SetOp;
-                } else {
-                    *startOp = kReplace_SetOp;
-                }
-                done = true;
-                break;
-            case kDifference_SetOp:
-                // if all pixels are clearToInside then we have to process the
-                // difference, otherwise it has no effect and all pixels
-                // remain outside.
-                if (*clearToInside) {
-                    *startOp = kDifference_SetOp;
-                    done = true;
-                }
-                break;
-            case kReverseDifference_SetOp:
-                // if all pixels are clearToInside then reverse difference
-                // produces empty set. Otherise it is same as replace
-                if (*clearToInside) {
-                    *clearToInside = false;
-                } else {
-                    *startOp = kReplace_SetOp;
-                    done = true;
-                }
-                break;
-            default:
-                GrCrash("Unknown set op.");
-        }
-    }
-    return done ? curr-1 : count;
-}
-}
-
-bool GrGpu::setupClipAndFlushState(GrPrimitiveType type) {
-    const GrIRect* r = NULL;
-    GrIRect clipRect;
-
-    GrDrawState* drawState = this->drawState();
-    const GrRenderTarget* rt = drawState->getRenderTarget();
-
-    // GrDrawTarget should have filtered this for us
-    GrAssert(NULL != rt);
-
-    if (drawState->isClipState()) {
-
-        GrRect bounds;
-        GrRect rtRect;
-        rtRect.setLTRB(0, 0,
-                       GrIntToScalar(rt->width()), GrIntToScalar(rt->height()));
-        if (fClip.hasConservativeBounds()) {
-            bounds = fClip.getConservativeBounds();
-            if (!bounds.intersect(rtRect)) {
-                bounds.setEmpty();
-            }
-        } else {
-            bounds = rtRect;
-        }
-
-        bounds.roundOut(&clipRect);
-        if  (clipRect.isEmpty()) {
-            clipRect.setLTRB(0,0,0,0);
-        }
-        r = &clipRect;
-
-        // use the stencil clip if we can't represent the clip as a rectangle.
-        fClipInStencil = !fClip.isRect() && !fClip.isEmpty() && 
-                         !bounds.isEmpty();
-
-        // TODO: dynamically attach a SB when needed.
-        GrStencilBuffer* stencilBuffer = rt->getStencilBuffer();
-        if (fClipInStencil && NULL == stencilBuffer) {
-            return false;
-        }
-
-        if (fClipInStencil &&
-            stencilBuffer->mustRenderClip(fClip, rt->width(), rt->height())) {
-
-            stencilBuffer->setLastClip(fClip, rt->width(), rt->height());
-
-            // we set the current clip to the bounds so that our recursive
-            // draws are scissored to them. We use the copy of the complex clip
-            // we just stashed on the SB to render from. We set it back after
-            // we finish drawing it into the stencil.
-            const GrClip& clip = stencilBuffer->getLastClip();
-            fClip.setFromRect(bounds);
-
-            AutoStateRestore asr(this);
-            AutoGeometryPush agp(this);
-
-            drawState->setViewMatrix(GrMatrix::I());
-            this->flushScissor(NULL);
-#if !VISUALIZE_COMPLEX_CLIP
-            drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
-#else
-            drawState->disableState(GrDrawState::kNoColorWrites_StateBit);
-#endif
-            int count = clip.getElementCount();
-            int clipBit = stencilBuffer->bits();
-            SkASSERT((clipBit <= 16) &&
-                     "Ganesh only handles 16b or smaller stencil buffers");
-            clipBit = (1 << (clipBit-1));
-            
-            bool clearToInside;
-            GrSetOp startOp = kReplace_SetOp; // suppress warning
-            int start = process_initial_clip_elements(clip,
-                                                      rtRect,
-                                                      &clearToInside,
-                                                      &startOp);
-
-            this->clearStencilClip(clipRect, clearToInside);
-
-            // walk through each clip element and perform its set op
-            // with the existing clip.
-            for (int c = start; c < count; ++c) {
-                GrPathFill fill;
-                bool fillInverted;
-                // enabled at bottom of loop
-                drawState->disableState(kModifyStencilClip_StateBit);
-
-                bool canRenderDirectToStencil; // can the clip element be drawn
-                                               // directly to the stencil buffer
-                                               // with a non-inverted fill rule
-                                               // without extra passes to
-                                               // resolve in/out status.
-
-                GrPathRenderer* pr = NULL;
-                const GrPath* clipPath = NULL;
-                if (kRect_ClipType == clip.getElementType(c)) {
-                    canRenderDirectToStencil = true;
-                    fill = kEvenOdd_PathFill;
-                    fillInverted = false;
-                    // there is no point in intersecting a screen filling
-                    // rectangle.
-                    if (kIntersect_SetOp == clip.getOp(c) &&
-                        clip.getRect(c).contains(rtRect)) {
-                        continue;
-                    }
-                } else {
-                    fill = clip.getPathFill(c);
-                    fillInverted = GrIsFillInverted(fill);
-                    fill = GrNonInvertedFill(fill);
-                    clipPath = &clip.getPath(c);
-                    pr = this->getClipPathRenderer(*clipPath, fill);
-                    if (NULL == pr) {
-                        fClipInStencil = false;
-                        fClip = clip;
-                        return false;
-                    }
-                    canRenderDirectToStencil =
-                        !pr->requiresStencilPass(*clipPath, fill, this);
-                }
-
-                GrSetOp op = (c == start) ? startOp : clip.getOp(c);
-                int passes;
-                GrStencilSettings stencilSettings[GrStencilSettings::kMaxStencilClipPasses];
-
-                bool canDrawDirectToClip; // Given the renderer, the element,
-                                          // fill rule, and set operation can
-                                          // we render the element directly to
-                                          // stencil bit used for clipping.
-                canDrawDirectToClip =
-                    GrStencilSettings::GetClipPasses(op,
-                                                     canRenderDirectToStencil,
-                                                     clipBit,
-                                                     fillInverted,
-                                                     &passes, stencilSettings);
-
-                // draw the element to the client stencil bits if necessary
-                if (!canDrawDirectToClip) {
-                    GR_STATIC_CONST_SAME_STENCIL(gDrawToStencil,
-                        kIncClamp_StencilOp,
-                        kIncClamp_StencilOp,
-                        kAlways_StencilFunc,
-                        0xffff,
-                        0x0000,
-                        0xffff);
-                    SET_RANDOM_COLOR
-                    if (kRect_ClipType == clip.getElementType(c)) {
-                        *drawState->stencil() = gDrawToStencil;
-                        this->drawSimpleRect(clip.getRect(c), NULL, 0);
-                    } else {
-                        if (canRenderDirectToStencil) {
-                            *drawState->stencil() = gDrawToStencil;
-                            pr->drawPath(*clipPath, fill, NULL, this, 0, false);
-                        } else {
-                            pr->drawPathToStencil(*clipPath, fill, this);
-                        }
-                    }
-                }
-
-                // now we modify the clip bit by rendering either the clip
-                // element directly or a bounding rect of the entire clip.
-                drawState->enableState(kModifyStencilClip_StateBit);
-                for (int p = 0; p < passes; ++p) {
-                    *drawState->stencil() = stencilSettings[p];
-                    if (canDrawDirectToClip) {
-                        if (kRect_ClipType == clip.getElementType(c)) {
-                            SET_RANDOM_COLOR
-                            this->drawSimpleRect(clip.getRect(c), NULL, 0);
-                        } else {
-                            SET_RANDOM_COLOR
-                            pr->drawPath(*clipPath, fill, NULL, this, 0, false);
-                        }
-                    } else {
-                        SET_RANDOM_COLOR
-                        this->drawSimpleRect(bounds, NULL, 0);
-                    }
-                }
-            }
-            // restore clip
-            fClip = clip;
-            // recusive draws would have disabled this since they drew with
-            // the clip bounds as clip.
-            fClipInStencil = true;
-        }
+    if (!fClipMaskManager.setupClipping(this->getClip())) {
+        return false;
     }
 
-    // Must flush the scissor after graphics state
     if (!this->flushGraphicsState(type)) {
         return false;
     }
-    this->flushScissor(r);
+
     return true;
 }
 
-GrPathRenderer* GrGpu::getClipPathRenderer(const GrPath& path,
-                                           GrPathFill fill) {
-    if (NULL == fPathRendererChain) {
-        fPathRendererChain = 
-            new GrPathRendererChain(this->getContext(),
-                                    GrPathRendererChain::kNonAAOnly_UsageFlag);
-    }
-    return fPathRendererChain->getPathRenderer(path, fill, this, false);
-}
-
-
 ////////////////////////////////////////////////////////////////////////////////
 
 void GrGpu::geometrySourceWillPush() {
@@ -759,6 +360,8 @@
     newState.fPoolStartVertex = DEBUG_INVAL_START_IDX;
     newState.fPoolIndexBuffer = (GrIndexBuffer*)DEBUG_INVAL_BUFFER;
     newState.fPoolStartIndex = DEBUG_INVAL_START_IDX;
+#else
+    (void) newState; // silence compiler warning
 #endif
 }
 
@@ -768,49 +371,26 @@
     fGeomPoolStateStack.pop_back();
 }
 
-void GrGpu::onDrawIndexed(GrPrimitiveType type,
-                          int startVertex,
-                          int startIndex,
-                          int vertexCount,
-                          int indexCount) {
-
+void GrGpu::onDraw(const DrawInfo& info) {
     this->handleDirtyContext();
-
-    if (!this->setupClipAndFlushState(type)) {
+    if (!this->setupClipAndFlushState(PrimTypeToDrawType(info.primitiveType()))) {
         return;
     }
-
-#if GR_COLLECT_STATS
-    fStats.fVertexCnt += vertexCount;
-    fStats.fIndexCnt  += indexCount;
-    fStats.fDrawCnt   += 1;
-#endif
-
-    int sVertex = startVertex;
-    int sIndex = startIndex;
-    setupGeometry(&sVertex, &sIndex, vertexCount, indexCount);
-
-    this->onGpuDrawIndexed(type, sVertex, sIndex,
-                           vertexCount, indexCount);
+    this->onGpuDraw(info);
 }
 
-void GrGpu::onDrawNonIndexed(GrPrimitiveType type,
-                           int startVertex,
-                           int vertexCount) {
+void GrGpu::onStencilPath(const GrPath* path, const SkStrokeRec&, SkPath::FillType fill) {
     this->handleDirtyContext();
 
-    if (!this->setupClipAndFlushState(type)) {
+    // TODO: make this more efficient (don't copy and copy back)
+    GrAutoTRestore<GrStencilSettings> asr(this->drawState()->stencil());
+
+    this->setStencilPathSettings(*path, fill, this->drawState()->stencil());
+    if (!this->setupClipAndFlushState(kStencilPath_DrawType)) {
         return;
     }
-#if GR_COLLECT_STATS
-    fStats.fVertexCnt += vertexCount;
-    fStats.fDrawCnt   += 1;
-#endif
 
-    int sVertex = startVertex;
-    setupGeometry(&sVertex, NULL, vertexCount, 0);
-
-    this->onGpuDrawNonIndexed(type, sVertex, vertexCount);
+    this->onGpuStencilPath(path, fill);
 }
 
 void GrGpu::finalizeReservedVertices() {
@@ -826,9 +406,9 @@
 void GrGpu::prepareVertexPool() {
     if (NULL == fVertexPool) {
         GrAssert(0 == fVertexPoolUseCnt);
-        fVertexPool = new GrVertexBufferAllocPool(this, true,
+        fVertexPool = SkNEW_ARGS(GrVertexBufferAllocPool, (this, true,
                                                   VERTEX_POOL_VB_SIZE,
-                                                  VERTEX_POOL_VB_COUNT);
+                                                  VERTEX_POOL_VB_COUNT));
         fVertexPool->releaseGpuRef();
     } else if (!fVertexPoolUseCnt) {
         // the client doesn't have valid data in the pool
@@ -839,9 +419,9 @@
 void GrGpu::prepareIndexPool() {
     if (NULL == fIndexPool) {
         GrAssert(0 == fIndexPoolUseCnt);
-        fIndexPool = new GrIndexBufferAllocPool(this, true,
+        fIndexPool = SkNEW_ARGS(GrIndexBufferAllocPool, (this, true,
                                                 INDEX_POOL_IB_SIZE,
-                                                INDEX_POOL_IB_COUNT);
+                                                INDEX_POOL_IB_COUNT));
         fIndexPool->releaseGpuRef();
     } else if (!fIndexPoolUseCnt) {
         // the client doesn't have valid data in the pool
@@ -849,17 +429,17 @@
     }
 }
 
-bool GrGpu::onReserveVertexSpace(GrVertexLayout vertexLayout,
+bool GrGpu::onReserveVertexSpace(size_t vertexSize,
                                  int vertexCount,
                                  void** vertices) {
     GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
-    
+
     GrAssert(vertexCount > 0);
     GrAssert(NULL != vertices);
-    
+
     this->prepareVertexPool();
-    
-    *vertices = fVertexPool->makeSpace(vertexLayout,
+
+    *vertices = fVertexPool->makeSpace(vertexSize,
                                        vertexCount,
                                        &geomPoolState.fPoolVertexBuffer,
                                        &geomPoolState.fPoolStartVertex);
@@ -872,7 +452,7 @@
 
 bool GrGpu::onReserveIndexSpace(int indexCount, void** indices) {
     GeometryPoolState& geomPoolState = fGeomPoolStateStack.back();
-    
+
     GrAssert(indexCount > 0);
     GrAssert(NULL != indices);
 
@@ -891,7 +471,7 @@
 void GrGpu::releaseReservedVertexSpace() {
     const GeometrySrcState& geoSrc = this->getGeomSrc();
     GrAssert(kReserved_GeometrySrcType == geoSrc.fVertexSrc);
-    size_t bytes = geoSrc.fVertexCount * VertexSize(geoSrc.fVertexLayout);
+    size_t bytes = geoSrc.fVertexCount * GrDrawState::VertexSize(geoSrc.fVertexLayout);
     fVertexPool->putBack(bytes);
     --fVertexPoolUseCnt;
 }
@@ -910,7 +490,7 @@
 #if GR_DEBUG
     bool success =
 #endif
-    fVertexPool->appendVertices(this->getGeomSrc().fVertexLayout,
+    fVertexPool->appendVertices(GrDrawState::VertexSize(this->getVertexLayout()),
                                 vertexCount,
                                 vertexArray,
                                 &geomPoolState.fPoolVertexBuffer,
@@ -937,7 +517,7 @@
     // if vertex source was array, we stowed data in the pool
     const GeometrySrcState& geoSrc = this->getGeomSrc();
     GrAssert(kArray_GeometrySrcType == geoSrc.fVertexSrc);
-    size_t bytes = geoSrc.fVertexCount * VertexSize(geoSrc.fVertexLayout);
+    size_t bytes = geoSrc.fVertexCount * GrDrawState::VertexSize(geoSrc.fVertexLayout);
     fVertexPool->putBack(bytes);
     --fVertexPoolUseCnt;
 }
@@ -950,30 +530,3 @@
     fIndexPool->putBack(bytes);
     --fIndexPoolUseCnt;
 }
-
-////////////////////////////////////////////////////////////////////////////////
-
-const GrGpuStats& GrGpu::getStats() const {
-    return fStats;
-}
-
-void GrGpu::resetStats() {
-    memset(&fStats, 0, sizeof(fStats));
-}
-
-void GrGpu::printStats() const {
-    if (GR_COLLECT_STATS) {
-     GrPrintf(
-     "-v-------------------------GPU STATS----------------------------v-\n"
-     "Stats collection is: %s\n"
-     "Draws: %04d, Verts: %04d, Indices: %04d\n"
-     "ProgChanges: %04d, TexChanges: %04d, RTChanges: %04d\n"
-     "TexCreates: %04d, RTCreates:%04d\n"
-     "-^--------------------------------------------------------------^-\n",
-     (GR_COLLECT_STATS ? "ON" : "OFF"),
-    fStats.fDrawCnt, fStats.fVertexCnt, fStats.fIndexCnt,
-    fStats.fProgChngCnt, fStats.fTextureChngCnt, fStats.fRenderTargetChngCnt,
-    fStats.fTextureCreateCnt, fStats.fRenderTargetCreateCnt);
-    }
-}
-
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 52282ed..bcda257 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -13,69 +13,43 @@
 #include "GrDrawTarget.h"
 #include "GrRect.h"
 #include "GrRefCnt.h"
-#include "GrTexture.h"
+#include "GrClipMaskManager.h"
+
+#include "SkPath.h"
 
 class GrContext;
 class GrIndexBufferAllocPool;
+class GrPath;
 class GrPathRenderer;
 class GrPathRendererChain;
 class GrResource;
 class GrStencilBuffer;
 class GrVertexBufferAllocPool;
 
-/**
- * Gpu usage statistics.
- */
-struct GrGpuStats {
-    uint32_t fVertexCnt;  //<! Number of vertices drawn
-    uint32_t fIndexCnt;   //<! Number of indices drawn
-    uint32_t fDrawCnt;    //<! Number of draws
-
-    uint32_t fProgChngCnt;//<! Number of program changes
-
-    /**
-     *  Number of times the texture is set in 3D API
-     */
-    uint32_t fTextureChngCnt;
-    /**
-     *  Number of times the render target is set in 3D API
-     */
-    uint32_t fRenderTargetChngCnt;
-    /**
-     *  Number of textures created (includes textures that are rendertargets).
-     */
-    uint32_t fTextureCreateCnt;
-    /**
-     *  Number of rendertargets created.
-     */
-    uint32_t fRenderTargetCreateCnt;
-};
-
 class GrGpu : public GrDrawTarget {
 
 public:
 
     /**
-     * Additional blend coeffecients for dual source blending, not exposed
+     * Additional blend coefficients for dual source blending, not exposed
      * through GrPaint/GrContext.
      */
     enum ExtendedBlendCoeffs {
         // source 2 refers to second output color when
         // using dual source blending.
-        kS2C_BlendCoeff = kPublicBlendCoeffCount,
-        kIS2C_BlendCoeff,
-        kS2A_BlendCoeff,
-        kIS2A_BlendCoeff,
+        kS2C_GrBlendCoeff = kPublicGrBlendCoeffCount,
+        kIS2C_GrBlendCoeff,
+        kS2A_GrBlendCoeff,
+        kIS2A_GrBlendCoeff,
 
-        kTotalBlendCoeffCount
+        kTotalGrBlendCoeffCount
     };
 
     /**
-     *  Create an instance of GrGpu that matches the specified Engine backend.
-     *  If the requested engine is not supported (at compile-time or run-time)
-     *  this returns NULL.
+     * Create an instance of GrGpu that matches the specified backend. If the requested backend is
+     * not supported (at compile-time or run-time) this returns NULL.
      */
-    static GrGpu* Create(GrEngine, GrPlatform3DContext context3D);
+    static GrGpu* Create(GrBackend, GrBackendContext);
 
     ////////////////////////////////////////////////////////////////////////////
 
@@ -84,8 +58,9 @@
 
     // The GrContext sets itself as the owner of this Gpu object
     void setContext(GrContext* context) {
-        GrAssert(NULL == fContext); 
+        GrAssert(NULL == fContext);
         fContext = context;
+        fClipMaskManager.setContext(context);
     }
     GrContext* getContext() { return fContext; }
     const GrContext* getContext() const { return fContext; }
@@ -122,14 +97,14 @@
                              const void* srcData, size_t rowBytes);
 
     /**
-     * Implements GrContext::createPlatformTexture
+     * Implements GrContext::wrapBackendTexture
      */
-    GrTexture* createPlatformTexture(const GrPlatformTextureDesc& desc);
+    GrTexture* wrapBackendTexture(const GrBackendTextureDesc&);
 
     /**
-     * Implements GrContext::createPlatformTexture
+     * Implements GrContext::wrapBackendTexture
      */
-    GrRenderTarget* createPlatformRenderTarget(const GrPlatformRenderTargetDesc& desc);
+    GrRenderTarget* wrapBackendRenderTarget(const GrBackendRenderTargetDesc&);
 
     /**
      * Creates a vertex buffer.
@@ -156,10 +131,16 @@
     GrIndexBuffer* createIndexBuffer(uint32_t size, bool dynamic);
 
     /**
+     * Creates a path object that can be stenciled using stencilPath(). It is
+     * only legal to call this if the caps report support for path stenciling.
+     */
+    GrPath* createPath(const SkPath& path);
+
+    /**
      * 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_PrimitiveType
+     * Draw with kTriangles_GrPrimitiveType
      * @ return the quad index buffer
      */
     const GrIndexBuffer* getQuadIndexBuffer() const;
@@ -184,18 +165,10 @@
     void forceRenderTargetFlush();
 
     /**
-     * If this returns true then a sequence that reads unpremultiplied pixels
-     * from a surface, writes back the same values, and reads them again will
-     * give the same pixel values back in both reads.
-     */
-    virtual bool canPreserveReadWriteUnpremulPixels() = 0;
-
-    /**
      * readPixels with some configs may be slow. Given a desired config this
      * function returns a fast-path config. The returned config must have the
-     * same components, component sizes, and not require conversion between
-     * pre- and unpremultiplied alpha. The caller is free to ignore the result
-     * and call readPixels with the original config.
+     * same components and component sizes. The caller is free to ignore the
+     * result and call readPixels with the original config.
      */
     virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
                                                                         const {
@@ -217,7 +190,7 @@
      * However, the caller (GrContext) may have transformations to apply and can
      * simply fold in the y-flip for free. On the other hand, the subclass may
      * be able to do it for free itself. For example, the subclass may have to
-     * do memcpys to handle rowBytes that aren't tight. It could do the y-flip 
+     * do memcpys to handle rowBytes that aren't tight. It could do the y-flip
      * concurrently.
      *
      * This function returns true if a y-flip is required to put the pixels in
@@ -240,10 +213,7 @@
      virtual bool fullReadPixelsIsFasterThanPartial() const { return false; };
 
     /**
-     * Reads a rectangle of pixels from a render target. Fails if read requires
-     * conversion between premultiplied and unpremultiplied configs. The caller
-     * should do the conversion by rendering to a target with the desire config
-     * first.
+     * Reads a rectangle of pixels from a render target.
      *
      * @param renderTarget  the render target to read from. NULL means the
      *                      current render target.
@@ -276,7 +246,7 @@
      * @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 bewtween consecutive rows. Zero
+     * @param rowBytes      number of bytes between consecutive rows. Zero
      *                      means rows are tightly packed.
      */
     void writeTexturePixels(GrTexture* texture,
@@ -284,10 +254,6 @@
                             GrPixelConfig config, const void* buffer,
                             size_t rowBytes);
 
-    const GrGpuStats& getStats() const;
-    void resetStats();
-    void printStats() const;
-
     /**
      * Called to tell Gpu object that all GrResources have been lost and should
      * be abandoned. Overrides must call INHERITED::abandonResources().
@@ -314,7 +280,15 @@
     void removeResource(GrResource* resource);
 
     // GrDrawTarget overrides
-    virtual void clear(const GrIRect* rect, GrColor color);
+    virtual void clear(const GrIRect* rect,
+                       GrColor color,
+                       GrRenderTarget* renderTarget = NULL) SK_OVERRIDE;
+
+    virtual void purgeResources() SK_OVERRIDE {
+        // The clip mask manager can rebuild all its clip masks so just
+        // get rid of them all.
+        fClipMaskManager.releaseResources();
+    }
 
     // After the client interacts directly with the 3D context state the GrGpu
     // must resync its internal state and assumptions about 3D context state.
@@ -333,7 +307,43 @@
         return fResetTimestamp;
     }
 
-protected:
+    /**
+     * Can the provided configuration act as a color render target?
+     */
+    bool isConfigRenderable(GrPixelConfig config) const {
+        GrAssert(kGrPixelConfigCount > config);
+        return fConfigRenderSupport[config];
+    }
+
+    /**
+     * These methods are called by the clip manager's setupClipping function
+     * which (called as part of GrGpu's implementation of onDraw and
+     * onStencilPath member functions.) The GrGpu subclass should flush the
+     * stencil state to the 3D API in its implementation of flushGraphicsState.
+     */
+    void enableScissor(const GrIRect& rect) {
+        fScissorState.fEnabled = true;
+        fScissorState.fRect = rect;
+    }
+    void disableScissor() { fScissorState.fEnabled = false; }
+
+    /**
+     * Like the scissor methods above this is called by setupClipping and
+     * should be flushed by the GrGpu subclass in flushGraphicsState. These
+     * stencil settings should be used in place of those on the GrDrawState.
+     * They have been adjusted to account for any interactions between the
+     * GrDrawState's stencil settings and stencil clipping.
+     */
+    void setStencilSettings(const GrStencilSettings& settings) {
+        fStencilSettings = settings;
+    }
+    void disableStencil() { fStencilSettings.setDisabled(); }
+
+    // GrGpu subclass sets clip bit in the stencil buffer. The subclass is
+    // free to clear the remaining bits to zero if masked clears are more
+    // expensive than clearing all bits.
+    virtual void clearStencilClip(const GrIRect& rect, bool insideClip) = 0;
+
     enum PrivateDrawStateStateBits {
         kFirstBit = (GrDrawState::kLastPublicStateBit << 1),
 
@@ -342,12 +352,33 @@
                                                  // clipping.
     };
 
-    // keep track of whether we are using stencil clipping (as opposed to
-    // scissor).
-    bool    fClipInStencil;
+protected:
+    enum DrawType {
+        kDrawPoints_DrawType,
+        kDrawLines_DrawType,
+        kDrawTriangles_DrawType,
+        kStencilPath_DrawType,
+    };
+
+    DrawType PrimTypeToDrawType(GrPrimitiveType type) {
+        switch (type) {
+            case kTriangles_GrPrimitiveType:
+            case kTriangleStrip_GrPrimitiveType:
+            case kTriangleFan_GrPrimitiveType:
+                return kDrawTriangles_DrawType;
+            case kPoints_GrPrimitiveType:
+                return kDrawPoints_DrawType;
+            case kLines_GrPrimitiveType:
+            case kLineStrip_GrPrimitiveType:
+                return kDrawLines_DrawType;
+            default:
+                GrCrash("Unexpected primitive type");
+                return kDrawTriangles_DrawType;
+        }
+    }
 
     // prepares clip flushes gpu state before a draw
-    bool setupClipAndFlushState(GrPrimitiveType type);
+    bool setupClipAndFlushState(DrawType);
 
     // Functions used to map clip-respecting stencil tests into normal
     // stencil funcs supported by GPUs.
@@ -360,78 +391,84 @@
                                           unsigned int* ref,
                                           unsigned int* mask);
 
-    // stencil settings to clip drawing when stencil clipping is in effect
-    // and the client isn't using the stencil test.
-    static const GrStencilSettings* GetClipStencilSettings();
-
-    GrGpuStats fStats;
+    GrClipMaskManager           fClipMaskManager;
 
     struct GeometryPoolState {
         const GrVertexBuffer* fPoolVertexBuffer;
         int                   fPoolStartVertex;
-        
+
         const GrIndexBuffer*  fPoolIndexBuffer;
         int                   fPoolStartIndex;
     };
-    const GeometryPoolState& getGeomPoolState() { 
-        return fGeomPoolStateStack.back(); 
+    const GeometryPoolState& getGeomPoolState() {
+        return fGeomPoolStateStack.back();
     }
 
-    // GrDrawTarget overrides
-    virtual bool onReserveVertexSpace(GrVertexLayout vertexLayout,
-                                      int vertexCount,
-                                      void** vertices);
-    virtual bool onReserveIndexSpace(int indexCount, void** indices);
-    virtual void releaseReservedVertexSpace();
-    virtual void releaseReservedIndexSpace();    
-    virtual void onSetVertexSourceToArray(const void* vertexArray,
-                                          int vertexCount);
-    virtual void onSetIndexSourceToArray(const void* indexArray,
-                                         int indexCount);
-    virtual void releaseVertexArray();
-    virtual void releaseIndexArray();
-    virtual void geometrySourceWillPush();
-    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState);
+    // The state of the scissor is controlled by the clip manager
+    struct ScissorState {
+        bool    fEnabled;
+        GrIRect fRect;
+    } fScissorState;
+
+    // The final stencil settings to use as determined by the clip manager.
+    GrStencilSettings fStencilSettings;
+
+    // Derived classes need access to this so they can fill it out in their
+    // constructors
+    bool    fConfigRenderSupport[kGrPixelConfigCount];
 
     // Helpers for setting up geometry state
     void finalizeReservedVertices();
     void finalizeReservedIndices();
 
+private:
+    // GrDrawTarget overrides
+    virtual bool onReserveVertexSpace(size_t vSize, int vertexCount, void** vertices) SK_OVERRIDE;
+    virtual bool onReserveIndexSpace(int indexCount, void** indices) SK_OVERRIDE;
+    virtual void releaseReservedVertexSpace() SK_OVERRIDE;
+    virtual void releaseReservedIndexSpace() SK_OVERRIDE;
+    virtual void onSetVertexSourceToArray(const void* vertexArray, int vertexCount) SK_OVERRIDE;
+    virtual void onSetIndexSourceToArray(const void* indexArray, int indexCount) SK_OVERRIDE;
+    virtual void releaseVertexArray() SK_OVERRIDE;
+    virtual void releaseIndexArray() SK_OVERRIDE;
+    virtual void geometrySourceWillPush() SK_OVERRIDE;
+    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) SK_OVERRIDE;
+
+
     // called when the 3D context state is unknown. Subclass should emit any
-    // assumed 3D context state and dirty any state cache
+    // assumed 3D context state and dirty any state cache.
     virtual void onResetContext() = 0;
 
-    
-    // overridden by API-specific derived class to create objects.
+    // overridden by backend-specific derived class to create objects.
     virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
                                        const void* srcData,
                                        size_t rowBytes) = 0;
-    virtual GrTexture* onCreatePlatformTexture(const GrPlatformTextureDesc& desc) = 0;
-    virtual GrRenderTarget* onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) = 0;
-    virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
-                                                 bool dynamic) = 0;
-    virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
-                                               bool dynamic) = 0;
+    virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) = 0;
+    virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) = 0;
+    virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size, bool dynamic) = 0;
+    virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size, bool dynamic) = 0;
+    virtual GrPath* onCreatePath(const SkPath& path) = 0;
 
-    // overridden by API-specific derivated class to perform the clear and 
+    // overridden by backend-specific derived class to perform the clear and
     // clearRect. NULL rect means clear whole target.
     virtual void onClear(const GrIRect* rect, GrColor color) = 0;
 
-    // overridden by API-specific derived class to perform the draw call.
-    virtual void onGpuDrawIndexed(GrPrimitiveType type,
-                                  uint32_t startVertex,
-                                  uint32_t startIndex,
-                                  uint32_t vertexCount,
-                                  uint32_t indexCount) = 0;
+    // overridden by backend-specific derived class to perform the draw call.
+    virtual void onGpuDraw(const DrawInfo&) = 0;
+    // when GrDrawTarget::stencilPath is called the draw state's current stencil
+    // settings are ignored. Instead the GrGpu decides the stencil rules
+    // necessary to stencil the path. These are still subject to filtering by
+    // the clip mask manager.
+    virtual void setStencilPathSettings(const GrPath&,
+                                        SkPath::FillType,
+                                        GrStencilSettings* settings) = 0;
+    // overridden by backend-specific derived class to perform the path stenciling.
+    virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) = 0;
 
-    virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
-                                     uint32_t vertexCount,
-                                     uint32_t numVertices) = 0;
-
-    // overridden by API-specific derived class to perform flush
+    // overridden by backend-specific derived class to perform flush
     virtual void onForceRenderTargetFlush() = 0;
 
-    // overridden by API-specific derived class to perform the read pixels.
+    // overridden by backend-specific derived class to perform the read pixels.
     virtual bool onReadPixels(GrRenderTarget* target,
                               int left, int top, int width, int height,
                               GrPixelConfig,
@@ -439,105 +476,49 @@
                               size_t rowBytes,
                               bool invertY) = 0;
 
-    // overridden by API-specific derived class to perform the texture update
+    // overridden by backend-specific derived class to perform the texture update
     virtual void onWriteTexturePixels(GrTexture* texture,
                                       int left, int top, int width, int height,
                                       GrPixelConfig config, const void* buffer,
                                       size_t rowBytes) = 0;
 
-    // overridden by API-specific derived class to perform the resolve
+    // overridden by backend-specific derived class to perform the resolve
     virtual void onResolveRenderTarget(GrRenderTarget* target) = 0;
 
-    // called to program the vertex data, indexCount will be 0 if drawing non-
-    // indexed geometry. The subclass may adjust the startVertex and/or
-    // startIndex since it may have already accounted for these in the setup.
-    virtual void setupGeometry(int* startVertex,
-                               int* startIndex,
-                               int vertexCount,
-                               int indexCount) = 0;
-
     // width and height may be larger than rt (if underlying API allows it).
     // Should attach the SB to the RT. Returns false if compatible sb could
     // not be created.
-    virtual bool createStencilBufferForRenderTarget(GrRenderTarget* rt,
-                                                    int width, 
-                                                    int height) = 0;
+    virtual bool createStencilBufferForRenderTarget(GrRenderTarget*, int width, int height) = 0;
 
     // attaches an existing SB to an existing RT.
-    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
-                                                   GrRenderTarget* rt) = 0;
+    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer*, GrRenderTarget*) = 0;
 
     // The GrGpu typically records the clients requested state and then flushes
     // deltas from previous state at draw time. This function does the
-    // API-specific flush of the state
+    // backend-specific flush of the state
     // returns false if current state is unsupported.
-    virtual bool flushGraphicsState(GrPrimitiveType type) = 0;
-
-    // Sets the scissor rect, or disables if rect is NULL.
-    virtual void flushScissor(const GrIRect* rect) = 0;
-
-    // GrGpu subclass sets clip bit in the stencil buffer. The subclass is
-    // free to clear the remaining bits to zero if masked clears are more
-    // expensive than clearing all bits.
-    virtual void clearStencilClip(const GrIRect& rect, bool insideClip) = 0;
+    virtual bool flushGraphicsState(DrawType) = 0;
 
     // clears the entire stencil buffer to 0
     virtual void clearStencil() = 0;
 
-private:
-    GrContext*                  fContext; // not reffed (context refs gpu)
-    
-    ResetTimestamp              fResetTimestamp;
-
-    GrVertexBufferAllocPool*    fVertexPool;
-
-    GrIndexBufferAllocPool*     fIndexPool;
-    
-    // counts number of uses of vertex/index pool in the geometry stack
-    int                         fVertexPoolUseCnt;
-    int                         fIndexPoolUseCnt;
-    
-    enum {
-        kPreallocGeomPoolStateStackCnt = 4,
-    };
-    SkSTArray<kPreallocGeomPoolStateStackCnt,
-              GeometryPoolState, true>              fGeomPoolStateStack;
-    
-    mutable GrIndexBuffer*      fQuadIndexBuffer; // mutable so it can be
-                                                  // created on-demand
-
-    mutable GrVertexBuffer*     fUnitSquareVertexBuffer; // mutable so it can be
-                                                         // created on-demand
-
-    // must be instantiated after GrGpu object has been given its owning
-    // GrContext ptr. (GrGpu is constructed first then handed off to GrContext).
-    GrPathRendererChain*        fPathRendererChain;
-
-    bool                        fContextIsDirty;
-
-    GrResource*                 fResourceHead;
-
     // Given a rt, find or create a stencil buffer and attach it
     bool attachStencilBufferToRenderTarget(GrRenderTarget* target);
 
     // GrDrawTarget overrides
-    virtual void onDrawIndexed(GrPrimitiveType type,
-                               int startVertex,
-                               int startIndex,
-                               int vertexCount,
-                               int indexCount);
-    virtual void onDrawNonIndexed(GrPrimitiveType type,
-                                  int startVertex,
-                                  int vertexCount);
+    virtual void onDraw(const DrawInfo&) SK_OVERRIDE;
+    virtual void onStencilPath(const GrPath* path, const SkStrokeRec& stroke,
+                               SkPath::FillType) SK_OVERRIDE;
 
     // readies the pools to provide vertex/index data.
     void prepareVertexPool();
     void prepareIndexPool();
 
-    // determines the path renderer used to draw a clip path element.
-    GrPathRenderer* getClipPathRenderer(const SkPath& path, GrPathFill fill);
-
     void resetContext() {
+        // We call this because the client may have messed with the
+        // stencil buffer. Perhaps we should detect whether it is a
+        // internally created stencil buffer and if so skip the invalidate.
+        fClipMaskManager.invalidateStencilMask();
         this->onResetContext();
         ++fResetTimestamp;
     }
@@ -549,6 +530,24 @@
         }
     }
 
+    enum {
+        kPreallocGeomPoolStateStackCnt = 4,
+    };
+    typedef SkTInternalLList<GrResource> ResourceList;
+    SkSTArray<kPreallocGeomPoolStateStackCnt, GeometryPoolState, true>  fGeomPoolStateStack;
+    GrContext*                                                          fContext; // not reffed
+    ResetTimestamp                                                      fResetTimestamp;
+    GrVertexBufferAllocPool*                                            fVertexPool;
+    GrIndexBufferAllocPool*                                             fIndexPool;
+    // counts number of uses of vertex/index pool in the geometry stack
+    int                                                                 fVertexPoolUseCnt;
+    int                                                                 fIndexPoolUseCnt;
+    // these are mutable so they can be created on-demand
+    mutable GrVertexBuffer*                                             fUnitSquareVertexBuffer;
+    mutable GrIndexBuffer*                                              fQuadIndexBuffer;
+    bool                                                                fContextIsDirty;
+    ResourceList                                                        fResourceList;
+
     typedef GrDrawTarget INHERITED;
 };
 
diff --git a/src/gpu/GrGpuFactory.cpp b/src/gpu/GrGpuFactory.cpp
index 585f35f..63a73b7 100644
--- a/src/gpu/GrGpuFactory.cpp
+++ b/src/gpu/GrGpuFactory.cpp
@@ -9,23 +9,18 @@
 
 #include "GrTypes.h"
 
-// must be before GrGLConfig.h
-#if GR_WIN32_BUILD
-//    #include "GrGpuD3D9.h"
-#endif
-
 #include "gl/GrGLConfig.h"
 
 #include "GrGpu.h"
-#include "gl/GrGpuGLShaders.h"
+#include "gl/GrGpuGL.h"
 
-GrGpu* GrGpu::Create(GrEngine engine, GrPlatform3DContext context3D) {
+GrGpu* GrGpu::Create(GrBackend backend, GrBackendContext context) {
 
     const GrGLInterface* glInterface = NULL;
     SkAutoTUnref<const GrGLInterface> glInterfaceUnref;
 
-    if (kOpenGL_Shaders_GrEngine == engine) {
-        glInterface = reinterpret_cast<const GrGLInterface*>(context3D);
+    if (kOpenGL_GrBackend == backend) {
+        glInterface = reinterpret_cast<const GrGLInterface*>(context);
         if (NULL == glInterface) {
             glInterface = GrGLDefaultInterface();
             // By calling GrGLDefaultInterface we've taken a ref on the
@@ -41,7 +36,7 @@
         }
         GrGLContextInfo ctxInfo(glInterface);
         if (ctxInfo.isInitialized()) {
-            return new GrGpuGLShaders(ctxInfo);
+            return SkNEW_ARGS(GrGpuGL, (ctxInfo));
         }
     }
     return NULL;
diff --git a/src/gpu/GrGpuVertex.h b/src/gpu/GrGpuVertex.h
index d093e2d..19a0b01 100644
--- a/src/gpu/GrGpuVertex.h
+++ b/src/gpu/GrGpuVertex.h
@@ -15,17 +15,17 @@
 #include "GrPoint.h"
 
 #if GR_TEXT_SCALAR_IS_USHORT
-    typedef uint16_t                GrTextScalar;  
+    typedef uint16_t                GrTextScalar;
     #define GrIntToTextScalar(x)    ((uint16_t)x)
     #define GrFixedToTextScalar(x)  (x)
 #elif GR_TEXT_SCALAR_IS_FIXED
     typedef GrFixed                 GrTextScalar;
-    #define GrIntToTextScalar(x)    GrIntToFixed(x)
+    #define GrIntToTextScalar(x)    SkIntToFixed(x)
     #define GrFixedToTextScalar(x)  (x)
 #elif GR_TEXT_SCALAR_IS_FLOAT
-    typedef float                   GrTextScalar;    
+    typedef float                   GrTextScalar;
     #define GrIntToTextScalar(x)    ((GrTextScalar)x)
-    #define GrFixedToTextScalar(x)  GrFixedToFloat(x)
+    #define GrFixedToTextScalar(x)  SkFixedToFloat(x)
 #else
     #error "Text scalar type not defined"
 #endif
@@ -45,12 +45,12 @@
         fX = GrIntToTextScalar(x);
         fY = GrIntToTextScalar(y);
     }
-    
+
     void setX(GrFixed x, GrFixed y) {
         fX = GrFixedToTextScalar(x);
         fY = GrFixedToTextScalar(y);
     }
-    
+
     // rect fan is counter-clockwise
 
     void setRectFan(GrTextScalar l, GrTextScalar t, GrTextScalar r,
@@ -94,4 +94,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrInOrderDrawBuffer.cpp b/src/gpu/GrInOrderDrawBuffer.cpp
index 1127927..bd54967 100644
--- a/src/gpu/GrInOrderDrawBuffer.cpp
+++ b/src/gpu/GrInOrderDrawBuffer.cpp
@@ -7,26 +7,26 @@
  */
 
 
-
 #include "GrInOrderDrawBuffer.h"
+#include "GrBufferAllocPool.h"
+#include "GrGpu.h"
+#include "GrIndexBuffer.h"
+#include "GrPath.h"
 #include "GrRenderTarget.h"
 #include "GrTexture.h"
-#include "GrBufferAllocPool.h"
-#include "GrIndexBuffer.h"
 #include "GrVertexBuffer.h"
-#include "GrGpu.h"
 
 GrInOrderDrawBuffer::GrInOrderDrawBuffer(const GrGpu* gpu,
                                          GrVertexBufferAllocPool* vertexPool,
                                          GrIndexBufferAllocPool* indexPool)
-    : fClipSet(true)
-    , fLastRectVertexLayout(0)
-    , fQuadIndexBuffer(NULL)
-    , fMaxQuads(0)
-    , fCurrQuad(0)
+    : fAutoFlushTarget(NULL)
+    , fClipSet(true)
+    , fClipProxyState(kUnknown_ClipProxyState)
     , fVertexPool(*vertexPool)
-    , fIndexPool(*indexPool) {
+    , fIndexPool(*indexPool)
+    , fFlushing(false) {
 
+    fGpu.reset(SkRef(gpu));
     fCaps = gpu->getCaps();
 
     GrAssert(NULL != vertexPool);
@@ -41,299 +41,361 @@
     poolState.fPoolIndexBuffer = (GrIndexBuffer*)~0;
     poolState.fPoolStartIndex = ~0;
 #endif
+    this->reset();
 }
 
 GrInOrderDrawBuffer::~GrInOrderDrawBuffer() {
     this->reset();
     // This must be called by before the GrDrawTarget destructor
     this->releaseGeometry();
-    GrSafeUnref(fQuadIndexBuffer);
+    GrSafeUnref(fAutoFlushTarget);
 }
 
-void GrInOrderDrawBuffer::initializeDrawStateAndClip(const GrDrawTarget& target) {
-    this->copyDrawState(target);
-    this->setClip(target.getClip());
-}
+////////////////////////////////////////////////////////////////////////////////
 
-void GrInOrderDrawBuffer::setQuadIndexBuffer(const GrIndexBuffer* indexBuffer) {
-    bool newIdxBuffer = fQuadIndexBuffer != indexBuffer;
-    if (newIdxBuffer) {
-        GrSafeUnref(fQuadIndexBuffer);
-        fQuadIndexBuffer = indexBuffer;
-        GrSafeRef(fQuadIndexBuffer);
-        fCurrQuad = 0;
-        fMaxQuads = (NULL == indexBuffer) ? 0 : indexBuffer->maxQuads();
-    } else {
-        GrAssert((NULL == indexBuffer && 0 == fMaxQuads) ||
-                 (indexBuffer->maxQuads() == fMaxQuads));
+namespace {
+void get_vertex_bounds(const void* vertices,
+                       size_t vertexSize,
+                       int vertexCount,
+                       SkRect* bounds) {
+    GrAssert(vertexSize >= sizeof(GrPoint));
+    GrAssert(vertexCount > 0);
+    const GrPoint* point = static_cast<const GrPoint*>(vertices);
+    bounds->fLeft = bounds->fRight = point->fX;
+    bounds->fTop = bounds->fBottom = point->fY;
+    for (int i = 1; i < vertexCount; ++i) {
+        point = reinterpret_cast<GrPoint*>(reinterpret_cast<intptr_t>(point) + vertexSize);
+        bounds->growToInclude(point->fX, point->fY);
     }
 }
+}
 
 void GrInOrderDrawBuffer::drawRect(const GrRect& rect,
-                                   const GrMatrix* matrix,
-                                   StageMask stageMask,
+                                   const SkMatrix* matrix,
                                    const GrRect* srcRects[],
-                                   const GrMatrix* srcMatrices[]) {
+                                   const SkMatrix* srcMatrices[]) {
 
-    GrAssert(!(NULL == fQuadIndexBuffer && fCurrQuad));
-    GrAssert(!(fDraws.empty() && fCurrQuad));
-    GrAssert(!(0 != fMaxQuads && NULL == fQuadIndexBuffer));
+    GrVertexLayout layout = 0;
+    GrDrawState::AutoColorRestore acr;
+    GrColor color = this->drawState()->getColor();
 
-    GrDrawState* drawState = this->drawState();
+    // Using per-vertex colors allows batching across colors. (A lot of rects in a row differing
+    // only in color is a common occurrence in tables). However, having per-vertex colors disables
+    // blending optimizations because we don't know if the color will be solid or not. These
+    // optimizations help determine whether coverage and color can be blended correctly when
+    // dual-source blending isn't available. This comes into play when there is coverage. If colors
+    // were a stage it could take a hint that every vertex's color will be opaque.
+    if (this->getCaps().dualSourceBlendingSupport() ||
+        this->getDrawState().hasSolidCoverage(this->getGeomSrc().fVertexLayout)) {
+        layout |= GrDrawState::kColor_VertexLayoutBit;;
+        // We set the draw state's color to white here. This is done so that any batching performed
+        // in our subclass's onDraw() won't get a false from GrDrawState::op== due to a color
+        // mismatch. TODO: Once vertex layout is owned by GrDrawState it should skip comparing the
+        // constant color in its op== when the kColor layout bit is set and then we can remove this.
+        acr.set(this->drawState(), 0xFFFFFFFF);
+    }
 
-    // if we have a quad IB then either append to the previous run of
-    // rects or start a new run
-    if (fMaxQuads) {
+    uint32_t explicitCoordMask = 0;
+    if (NULL != srcRects) {
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            int numTC = 0;
+            if (NULL != srcRects[s]) {
+                layout |= GrDrawState::StageTexCoordVertexLayoutBit(s, numTC);
+                ++numTC;
+                explicitCoordMask |= (1 << s);
+            }
+        }
+    }
 
-        bool appendToPreviousDraw = false;
-        GrVertexLayout layout = GetRectVertexLayout(stageMask, srcRects);
-        AutoReleaseGeometry geo(this, layout, 4, 0);
-        if (!geo.succeeded()) {
-            GrPrintf("Failed to get space for vertices!\n");
+    AutoReleaseGeometry geo(this, layout, 4, 0);
+    if (!geo.succeeded()) {
+        GrPrintf("Failed to get space for vertices!\n");
+        return;
+    }
+
+    // Go to device coords to allow batching across matrix changes
+    SkMatrix combinedMatrix;
+    if (NULL != matrix) {
+        combinedMatrix = *matrix;
+    } else {
+        combinedMatrix.reset();
+    }
+    combinedMatrix.postConcat(this->drawState()->getViewMatrix());
+    // When the caller has provided an explicit source rects for a stage then we don't want to
+    // modify that stage's matrix. Otherwise if the effect is generating its source rect from
+    // the vertex positions then we have to account for the view matrix change.
+    GrDrawState::AutoDeviceCoordDraw adcd(this->drawState(), explicitCoordMask);
+    if (!adcd.succeeded()) {
+        return;
+    }
+
+    int stageOffsets[GrDrawState::kNumStages], colorOffset;
+    int vsize = GrDrawState::VertexSizeAndOffsetsByStage(layout, stageOffsets,
+                                                         &colorOffset, NULL, NULL);
+
+    geo.positions()->setRectFan(rect.fLeft, rect.fTop, rect.fRight, rect.fBottom, vsize);
+    combinedMatrix.mapPointsWithStride(geo.positions(), vsize, 4);
+
+    SkRect devBounds;
+    // since we already computed the dev verts, set the bounds hint. This will help us avoid
+    // unnecessary clipping in our onDraw().
+    get_vertex_bounds(geo.vertices(), vsize, 4, &devBounds);
+
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        if (explicitCoordMask & (1 << i)) {
+            GrAssert(0 != stageOffsets[i]);
+            GrPoint* coords = GrTCast<GrPoint*>(GrTCast<intptr_t>(geo.vertices()) +
+                                                stageOffsets[i]);
+            coords->setRectFan(srcRects[i]->fLeft, srcRects[i]->fTop,
+                               srcRects[i]->fRight, srcRects[i]->fBottom,
+                               vsize);
+            if (NULL != srcMatrices && NULL != srcMatrices[i]) {
+                srcMatrices[i]->mapPointsWithStride(coords, vsize, 4);
+            }
+        } else {
+            GrAssert(0 == stageOffsets[i]);
+        }
+    }
+
+    if (colorOffset >= 0) {
+        GrColor* vertColor = GrTCast<GrColor*>(GrTCast<intptr_t>(geo.vertices()) + colorOffset);
+        for (int i = 0; i < 4; ++i) {
+            *vertColor = color;
+            vertColor = (GrColor*) ((intptr_t) vertColor + vsize);
+        }
+    }
+
+    this->setIndexSourceToBuffer(fGpu->getQuadIndexBuffer());
+    this->drawIndexedInstances(kTriangles_GrPrimitiveType, 1, 4, 6, &devBounds);
+}
+
+bool GrInOrderDrawBuffer::quickInsideClip(const SkRect& devBounds) {
+    if (!this->getDrawState().isClipState()) {
+        return true;
+    }
+    if (kUnknown_ClipProxyState == fClipProxyState) {
+        SkIRect rect;
+        bool iior;
+        this->getClip()->getConservativeBounds(this->getDrawState().getRenderTarget(), &rect, &iior);
+        if (iior) {
+            // The clip is a rect. We will remember that in fProxyClip. It is common for an edge (or
+            // all edges) of the clip to be at the edge of the RT. However, we get that clipping for
+            // free via the viewport. We don't want to think that clipping must be enabled in this
+            // case. So we extend the clip outward from the edge to avoid these false negatives.
+            fClipProxyState = kValid_ClipProxyState;
+            fClipProxy = SkRect::MakeFromIRect(rect);
+
+            if (fClipProxy.fLeft <= 0) {
+                fClipProxy.fLeft = SK_ScalarMin;
+            }
+            if (fClipProxy.fTop <= 0) {
+                fClipProxy.fTop = SK_ScalarMin;
+            }
+            if (fClipProxy.fRight >= this->getDrawState().getRenderTarget()->width()) {
+                fClipProxy.fRight = SK_ScalarMax;
+            }
+            if (fClipProxy.fBottom >= this->getDrawState().getRenderTarget()->height()) {
+                fClipProxy.fBottom = SK_ScalarMax;
+            }
+        } else {
+            fClipProxyState = kInvalid_ClipProxyState;
+        }
+    }
+    if (kValid_ClipProxyState == fClipProxyState) {
+        return fClipProxy.contains(devBounds);
+    }
+    SkPoint originOffset = {SkIntToScalar(this->getClip()->fOrigin.fX),
+                            SkIntToScalar(this->getClip()->fOrigin.fY)};
+    SkRect clipSpaceBounds = devBounds;
+    clipSpaceBounds.offset(originOffset);
+    return this->getClip()->fClipStack->quickContains(clipSpaceBounds);
+}
+
+int GrInOrderDrawBuffer::concatInstancedDraw(const DrawInfo& info) {
+    GrAssert(info.isInstanced());
+
+    const GeometrySrcState& geomSrc = this->getGeomSrc();
+
+    // we only attempt to concat the case when reserved verts are used with a client-specified index
+    // buffer. To make this work with client-specified VBs we'd need to know if the VB was updated
+    // between draws.
+    if (kReserved_GeometrySrcType != geomSrc.fVertexSrc ||
+        kBuffer_GeometrySrcType != geomSrc.fIndexSrc) {
+        return 0;
+    }
+    // Check if there is a draw info that is compatible that uses the same VB from the pool and
+    // the same IB
+    if (kDraw_Cmd != fCmds.back()) {
+        return 0;
+    }
+
+    DrawRecord* draw = &fDraws.back();
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    const GrVertexBuffer* vertexBuffer = poolState.fPoolVertexBuffer;
+
+    if (!draw->isInstanced() ||
+        draw->verticesPerInstance() != info.verticesPerInstance() ||
+        draw->indicesPerInstance() != info.indicesPerInstance() ||
+        draw->fVertexBuffer != vertexBuffer ||
+        draw->fIndexBuffer != geomSrc.fIndexBuffer ||
+        draw->fVertexLayout != geomSrc.fVertexLayout) {
+        return 0;
+    }
+    // info does not yet account for the offset from the start of the pool's VB while the previous
+    // draw record does.
+    int adjustedStartVertex = poolState.fPoolStartVertex + info.startVertex();
+    if (draw->startVertex() + draw->vertexCount() != adjustedStartVertex) {
+        return 0;
+    }
+
+    GrAssert(poolState.fPoolStartVertex == draw->startVertex() + draw->vertexCount());
+
+    // how many instances can be concat'ed onto draw given the size of the index buffer
+    int instancesToConcat = this->indexCountInCurrentSource() / info.indicesPerInstance();
+    instancesToConcat -= draw->instanceCount();
+    instancesToConcat = GrMin(instancesToConcat, info.instanceCount());
+
+    // update the amount of reserved vertex data actually referenced in draws
+    size_t vertexBytes = instancesToConcat * info.verticesPerInstance() *
+                         GrDrawState::VertexSize(draw->fVertexLayout);
+    poolState.fUsedPoolVertexBytes = GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+
+    draw->adjustInstanceCount(instancesToConcat);
+    return instancesToConcat;
+}
+
+class AutoClipReenable {
+public:
+    AutoClipReenable() : fDrawState(NULL) {}
+    ~AutoClipReenable() {
+        if (NULL != fDrawState) {
+            fDrawState->enableState(GrDrawState::kClip_StateBit);
+        }
+    }
+    void set(GrDrawState* drawState) {
+        if (drawState->isClipState()) {
+            fDrawState = drawState;
+            drawState->disableState(GrDrawState::kClip_StateBit);
+        }
+    }
+private:
+    GrDrawState*    fDrawState;
+};
+
+void GrInOrderDrawBuffer::onDraw(const DrawInfo& info) {
+
+    GeometryPoolState& poolState = fGeoPoolStateStack.back();
+    AutoClipReenable acr;
+
+    if (this->getDrawState().isClipState() &&
+        NULL != info.getDevBounds() &&
+        this->quickInsideClip(*info.getDevBounds())) {
+        acr.set(this->drawState());
+    }
+
+    if (this->needsNewClip()) {
+       this->recordClip();
+    }
+    if (this->needsNewState()) {
+        this->recordState();
+    }
+
+    DrawRecord* draw;
+    if (info.isInstanced()) {
+        int instancesConcated = this->concatInstancedDraw(info);
+        if (info.instanceCount() > instancesConcated) {
+            draw = this->recordDraw(info);
+            draw->adjustInstanceCount(-instancesConcated);
+        } else {
             return;
         }
-        GrMatrix combinedMatrix = drawState->getViewMatrix();
-        GrDrawState::AutoViewMatrixRestore avmr(drawState, GrMatrix::I());
-        if (NULL != matrix) {
-            combinedMatrix.preConcat(*matrix);
-        }
-
-        SetRectVertices(rect, &combinedMatrix, srcRects, srcMatrices, layout, geo.vertices());
-
-        // we don't want to miss an opportunity to batch rects together
-        // simply because the clip has changed if the clip doesn't affect
-        // the rect.
-        bool disabledClip = false;
-        if (drawState->isClipState() && fClip.isRect()) {
-
-            GrRect clipRect = fClip.getRect(0);
-            // If the clip rect touches the edge of the viewport, extended it
-            // out (close) to infinity to avoid bogus intersections.
-            // We might consider a more exact clip to viewport if this
-            // conservative test fails.
-            const GrRenderTarget* target = drawState->getRenderTarget();
-            if (0 >= clipRect.fLeft) {
-                clipRect.fLeft = GR_ScalarMin;
-            }
-            if (target->width() <= clipRect.fRight) {
-                clipRect.fRight = GR_ScalarMax;
-            }
-            if (0 >= clipRect.top()) {
-                clipRect.fTop = GR_ScalarMin;
-            }
-            if (target->height() <= clipRect.fBottom) {
-                clipRect.fBottom = GR_ScalarMax;
-            }
-            int stride = VertexSize(layout);
-            bool insideClip = true;
-            for (int v = 0; v < 4; ++v) {
-                const GrPoint& p = *GetVertexPoint(geo.vertices(), v, stride);
-                if (!clipRect.contains(p)) {
-                    insideClip = false;
-                    break;
-                }
-            }
-            if (insideClip) {
-                drawState->disableState(GrDrawState::kClip_StateBit);
-                disabledClip = true;
-            }
-        }
-        if (!needsNewClip() && !needsNewState() && fCurrQuad > 0 &&
-            fCurrQuad < fMaxQuads && layout == fLastRectVertexLayout) {
-
-            int vsize = VertexSize(layout);
-
-            Draw& lastDraw = fDraws.back();
-
-            GrAssert(lastDraw.fIndexBuffer == fQuadIndexBuffer);
-            GrAssert(kTriangles_PrimitiveType == lastDraw.fPrimitiveType);
-            GrAssert(0 == lastDraw.fVertexCount % 4);
-            GrAssert(0 == lastDraw.fIndexCount % 6);
-            GrAssert(0 == lastDraw.fStartIndex);
-
-            GeometryPoolState& poolState = fGeoPoolStateStack.back();
-            bool clearSinceLastDraw =
-                            fClears.count() && 
-                            fClears.back().fBeforeDrawIdx == fDraws.count();
-
-            appendToPreviousDraw =  
-                !clearSinceLastDraw &&
-                lastDraw.fVertexBuffer == poolState.fPoolVertexBuffer &&
-                (fCurrQuad * 4 + lastDraw.fStartVertex) == poolState.fPoolStartVertex;
-
-            if (appendToPreviousDraw) {
-                lastDraw.fVertexCount += 4;
-                lastDraw.fIndexCount += 6;
-                fCurrQuad += 1;
-                // we reserved above, so we should be the first
-                // use of this vertex reserveation.
-                GrAssert(0 == poolState.fUsedPoolVertexBytes);
-                poolState.fUsedPoolVertexBytes = 4 * vsize;
-            }
-        }
-        if (!appendToPreviousDraw) {
-            this->setIndexSourceToBuffer(fQuadIndexBuffer);
-            drawIndexed(kTriangles_PrimitiveType, 0, 0, 4, 6);
-            fCurrQuad = 1;
-            fLastRectVertexLayout = layout;
-        }
-        if (disabledClip) {
-            drawState->enableState(GrDrawState::kClip_StateBit);
-        }
     } else {
-        INHERITED::drawRect(rect, matrix, stageMask, srcRects, srcMatrices);
+        draw = this->recordDraw(info);
     }
-}
+    draw->fVertexLayout = this->getVertexLayout();
 
-void GrInOrderDrawBuffer::onDrawIndexed(GrPrimitiveType primitiveType,
-                                        int startVertex,
-                                        int startIndex,
-                                        int vertexCount,
-                                        int indexCount) {
-
-    if (!vertexCount || !indexCount) {
-        return;
-    }
-
-    fCurrQuad = 0;
-
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-
-    Draw& draw = fDraws.push_back();
-    draw.fPrimitiveType = primitiveType;
-    draw.fStartVertex   = startVertex;
-    draw.fStartIndex    = startIndex;
-    draw.fVertexCount   = vertexCount;
-    draw.fIndexCount    = indexCount;
-
-    draw.fClipChanged = this->needsNewClip();
-    if (draw.fClipChanged) {
-       this->pushClip();
-    }
-
-    draw.fStateChanged = this->needsNewState();
-    if (draw.fStateChanged) {
-        this->pushState();
-    }
-
-    draw.fVertexLayout = this->getGeomSrc().fVertexLayout;
     switch (this->getGeomSrc().fVertexSrc) {
-    case kBuffer_GeometrySrcType:
-        draw.fVertexBuffer = this->getGeomSrc().fVertexBuffer;
-        break;
-    case kReserved_GeometrySrcType: // fallthrough
-    case kArray_GeometrySrcType: {
-        size_t vertexBytes = (vertexCount + startVertex) *
-                             VertexSize(this->getGeomSrc().fVertexLayout);
-        poolState.fUsedPoolVertexBytes = 
-                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
-        draw.fVertexBuffer = poolState.fPoolVertexBuffer;
-        draw.fStartVertex += poolState.fPoolStartVertex;
-        break;
+        case kBuffer_GeometrySrcType:
+            draw->fVertexBuffer = this->getGeomSrc().fVertexBuffer;
+            break;
+        case kReserved_GeometrySrcType: // fallthrough
+        case kArray_GeometrySrcType: {
+            size_t vertexBytes = (info.vertexCount() + info.startVertex()) *
+                                 GrDrawState::VertexSize(draw->fVertexLayout);
+            poolState.fUsedPoolVertexBytes = GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
+            draw->fVertexBuffer = poolState.fPoolVertexBuffer;
+            draw->adjustStartVertex(poolState.fPoolStartVertex);
+            break;
+        }
+        default:
+            GrCrash("unknown geom src type");
     }
-    default:
-        GrCrash("unknown geom src type");
-    }
-    draw.fVertexBuffer->ref();
+    draw->fVertexBuffer->ref();
 
-    switch (this->getGeomSrc().fIndexSrc) {
-    case kBuffer_GeometrySrcType:
-        draw.fIndexBuffer = this->getGeomSrc().fIndexBuffer;
-        break;
-    case kReserved_GeometrySrcType: // fallthrough 
-    case kArray_GeometrySrcType: {
-        size_t indexBytes = (indexCount + startIndex) * sizeof(uint16_t);
-        poolState.fUsedPoolIndexBytes = 
-                            GrMax(poolState.fUsedPoolIndexBytes, indexBytes);
-        draw.fIndexBuffer = poolState.fPoolIndexBuffer;
-        draw.fStartIndex += poolState.fPoolStartIndex;
-        break;
+    if (info.isIndexed()) {
+        switch (this->getGeomSrc().fIndexSrc) {
+            case kBuffer_GeometrySrcType:
+                draw->fIndexBuffer = this->getGeomSrc().fIndexBuffer;
+                break;
+            case kReserved_GeometrySrcType: // fallthrough
+            case kArray_GeometrySrcType: {
+                size_t indexBytes = (info.indexCount() + info.startIndex()) * sizeof(uint16_t);
+                poolState.fUsedPoolIndexBytes = GrMax(poolState.fUsedPoolIndexBytes, indexBytes);
+                draw->fIndexBuffer = poolState.fPoolIndexBuffer;
+                draw->adjustStartIndex(poolState.fPoolStartIndex);
+                break;
+            }
+            default:
+                GrCrash("unknown geom src type");
+        }
+        draw->fIndexBuffer->ref();
+    } else {
+        draw->fIndexBuffer = NULL;
     }
-    default:
-        GrCrash("unknown geom src type");
-    }
-    draw.fIndexBuffer->ref();
 }
 
-void GrInOrderDrawBuffer::onDrawNonIndexed(GrPrimitiveType primitiveType,
-                                           int startVertex,
-                                           int vertexCount) {
-    if (!vertexCount) {
-        return;
+GrInOrderDrawBuffer::StencilPath::StencilPath() : fStroke(SkStrokeRec::kFill_InitStyle) {}
+
+void GrInOrderDrawBuffer::onStencilPath(const GrPath* path, const SkStrokeRec& stroke,
+                                        SkPath::FillType fill) {
+    if (this->needsNewClip()) {
+        this->recordClip();
     }
-
-    fCurrQuad = 0;
-
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-
-    Draw& draw = fDraws.push_back();
-    draw.fPrimitiveType = primitiveType;
-    draw.fStartVertex   = startVertex;
-    draw.fStartIndex    = 0;
-    draw.fVertexCount   = vertexCount;
-    draw.fIndexCount    = 0;
-
-    draw.fClipChanged = this->needsNewClip();
-    if (draw.fClipChanged) {
-        this->pushClip();
+    // Only compare the subset of GrDrawState relevant to path stenciling?
+    if (this->needsNewState()) {
+        this->recordState();
     }
-
-    draw.fStateChanged = this->needsNewState();
-    if (draw.fStateChanged) {
-        this->pushState();
-    }
-
-    draw.fVertexLayout = this->getGeomSrc().fVertexLayout;
-    switch (this->getGeomSrc().fVertexSrc) {
-    case kBuffer_GeometrySrcType:
-        draw.fVertexBuffer = this->getGeomSrc().fVertexBuffer;
-        break;
-    case kReserved_GeometrySrcType: // fallthrough
-    case kArray_GeometrySrcType: {
-        size_t vertexBytes = (vertexCount + startVertex) *
-                             VertexSize(this->getGeomSrc().fVertexLayout);
-        poolState.fUsedPoolVertexBytes = 
-                            GrMax(poolState.fUsedPoolVertexBytes, vertexBytes);
-        draw.fVertexBuffer = poolState.fPoolVertexBuffer;
-        draw.fStartVertex += poolState.fPoolStartVertex;
-        break;
-    }
-    default:
-        GrCrash("unknown geom src type");
-    }
-    draw.fVertexBuffer->ref();
-    draw.fIndexBuffer = NULL;
+    StencilPath* sp = this->recordStencilPath();
+    sp->fPath.reset(path);
+    path->ref();
+    sp->fFill = fill;
+    sp->fStroke = stroke;
 }
 
-void GrInOrderDrawBuffer::clear(const GrIRect* rect, GrColor color) {
+void GrInOrderDrawBuffer::clear(const GrIRect* rect, GrColor color, GrRenderTarget* renderTarget) {
     GrIRect r;
+    if (NULL == renderTarget) {
+        renderTarget = this->drawState()->getRenderTarget();
+        GrAssert(NULL != renderTarget);
+    }
     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, 
-                  this->getDrawState().getRenderTarget()->width(), 
-                  this->getDrawState().getRenderTarget()->height());
+        r.setLTRB(0, 0, renderTarget->width(), renderTarget->height());
         rect = &r;
     }
-    Clear& clr = fClears.push_back();
-    clr.fColor = color;
-    clr.fBeforeDrawIdx = fDraws.count();
-    clr.fRect = *rect;
+    Clear* clr = this->recordClear();
+    clr->fColor = color;
+    clr->fRect = *rect;
+    clr->fRenderTarget = renderTarget;
+    renderTarget->ref();
 }
 
 void GrInOrderDrawBuffer::reset() {
     GrAssert(1 == fGeoPoolStateStack.count());
     this->resetVertexSource();
     this->resetIndexSource();
-    uint32_t numStates = fStates.count();
-    for (uint32_t i = 0; i < numStates; ++i) {
-        const GrDrawState& dstate = this->accessSavedDrawState(fStates[i]);
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            GrSafeUnref(dstate.getTexture(s));
-        }
-        GrSafeUnref(dstate.getRenderTarget());
-    }
     int numDraws = fDraws.count();
     for (int d = 0; d < numDraws; ++d) {
         // we always have a VB, but not always an IB
@@ -341,84 +403,145 @@
         fDraws[d].fVertexBuffer->unref();
         GrSafeUnref(fDraws[d].fIndexBuffer);
     }
+    fCmds.reset();
     fDraws.reset();
+    fStencilPaths.reset();
     fStates.reset();
-
     fClears.reset();
-
     fVertexPool.reset();
     fIndexPool.reset();
-
     fClips.reset();
-
-    fCurrQuad = 0;
+    fClipOrigins.reset();
+    fClipSet = true;
 }
 
-void GrInOrderDrawBuffer::playback(GrDrawTarget* target) {
+bool GrInOrderDrawBuffer::flushTo(GrDrawTarget* target) {
     GrAssert(kReserved_GeometrySrcType != this->getGeomSrc().fVertexSrc);
     GrAssert(kReserved_GeometrySrcType != this->getGeomSrc().fIndexSrc);
+
     GrAssert(NULL != target);
     GrAssert(target != this); // not considered and why?
 
-    int numDraws = fDraws.count();
-    if (!numDraws) {
-        return;
+    int numCmds = fCmds.count();
+    if (0 == numCmds) {
+        return false;
     }
 
     fVertexPool.unlock();
     fIndexPool.unlock();
 
-    GrDrawTarget::AutoStateRestore asr(target);
     GrDrawTarget::AutoClipRestore acr(target);
     AutoGeometryPush agp(target);
 
-    int currState = ~0;
-    int currClip  = ~0;
-    int currClear = 0;
+    GrDrawState playbackState;
+    GrDrawState* prevDrawState = target->drawState();
+    prevDrawState->ref();
+    target->setDrawState(&playbackState);
 
-    for (int i = 0; i < numDraws; ++i) {
-        while (currClear < fClears.count() && 
-               i == fClears[currClear].fBeforeDrawIdx) {
-            target->clear(&fClears[currClear].fRect, fClears[currClear].fColor);
-            ++currClear;
-        }
+    GrClipData clipData;
 
-        const Draw& draw = fDraws[i];
-        if (draw.fStateChanged) {
-            ++currState;
-            target->restoreDrawState(fStates[currState]);
-        }
-        if (draw.fClipChanged) {
-            ++currClip;
-            target->setClip(fClips[currClip]);
-        }
+    int currState       = 0;
+    int currClip        = 0;
+    int currClear       = 0;
+    int currDraw        = 0;
+    int currStencilPath = 0;
 
-        target->setVertexSourceToBuffer(draw.fVertexLayout, draw.fVertexBuffer);
 
-        if (draw.fIndexCount) {
-            target->setIndexSourceToBuffer(draw.fIndexBuffer);
-        }
+    for (int c = 0; c < numCmds; ++c) {
+        switch (fCmds[c]) {
+            case kDraw_Cmd: {
+                const DrawRecord& draw = fDraws[currDraw];
+                target->setVertexSourceToBuffer(draw.fVertexLayout, draw.fVertexBuffer);
+                if (draw.isIndexed()) {
+                    target->setIndexSourceToBuffer(draw.fIndexBuffer);
+                }
+                target->executeDraw(draw);
 
-        if (draw.fIndexCount) {
-            target->drawIndexed(draw.fPrimitiveType,
-                                draw.fStartVertex,
-                                draw.fStartIndex,
-                                draw.fVertexCount,
-                                draw.fIndexCount);
-        } else {
-            target->drawNonIndexed(draw.fPrimitiveType,
-                                   draw.fStartVertex,
-                                   draw.fVertexCount);
+                ++currDraw;
+                break;
+            }
+            case kStencilPath_Cmd: {
+                const StencilPath& sp = fStencilPaths[currStencilPath];
+                target->stencilPath(sp.fPath.get(), sp.fStroke, sp.fFill);
+                ++currStencilPath;
+                break;
+            }
+            case kSetState_Cmd:
+                fStates[currState].restoreTo(&playbackState);
+                ++currState;
+                break;
+            case kSetClip_Cmd:
+                clipData.fClipStack = &fClips[currClip];
+                clipData.fOrigin = fClipOrigins[currClip];
+                target->setClip(&clipData);
+                ++currClip;
+                break;
+            case kClear_Cmd:
+                target->clear(&fClears[currClear].fRect,
+                              fClears[currClear].fColor,
+                              fClears[currClear].fRenderTarget);
+                ++currClear;
+                break;
         }
     }
-    while (currClear < fClears.count()) {
-        GrAssert(fDraws.count() == fClears[currClear].fBeforeDrawIdx);
-        target->clear(&fClears[currClear].fRect, fClears[currClear].fColor);
-        ++currClear;
+    // we should have consumed all the states, clips, etc.
+    GrAssert(fStates.count() == currState);
+    GrAssert(fClips.count() == currClip);
+    GrAssert(fClipOrigins.count() == currClip);
+    GrAssert(fClears.count() == currClear);
+    GrAssert(fDraws.count()  == currDraw);
+
+    target->setDrawState(prevDrawState);
+    prevDrawState->unref();
+    this->reset();
+    return true;
+}
+
+void GrInOrderDrawBuffer::setAutoFlushTarget(GrDrawTarget* target) {
+    GrSafeAssign(fAutoFlushTarget, target);
+}
+
+void GrInOrderDrawBuffer::willReserveVertexAndIndexSpace(
+                                size_t vertexSize,
+                                int vertexCount,
+                                int indexCount) {
+    if (NULL != fAutoFlushTarget) {
+        // 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;
+
+        // we don't want to finalize any reserved geom on the target since
+        // we don't know that the client has finished writing to it.
+        bool targetHasReservedGeom =
+            fAutoFlushTarget->hasReservedVerticesOrIndices();
+
+        int vcount = vertexCount;
+        int icount = indexCount;
+
+        if (!insideGeoPush &&
+            !unreleasedVertexSpace &&
+            !unreleasedIndexSpace &&
+            !targetHasReservedGeom &&
+            this->geometryHints(vertexSize, &vcount, &icount)) {
+
+            this->flushTo(fAutoFlushTarget);
+        }
     }
 }
 
-bool GrInOrderDrawBuffer::geometryHints(GrVertexLayout vertexLayout,
+bool GrInOrderDrawBuffer::geometryHints(size_t vertexSize,
                                         int* vertexCount,
                                         int* indexCount) const {
     // we will recommend a flush if the data could fit in a single
@@ -436,10 +559,10 @@
         *indexCount = currIndices;
     }
     if (NULL != vertexCount) {
-        int32_t currVertices = fVertexPool.currentBufferVertices(vertexLayout);
+        int32_t currVertices = fVertexPool.currentBufferVertices(vertexSize);
         if (*vertexCount > currVertices &&
             (!fVertexPool.preallocatedBuffersRemaining() &&
-             *vertexCount <= fVertexPool.preallocatedBufferVertices(vertexLayout))) {
+             *vertexCount <= fVertexPool.preallocatedBufferVertices(vertexSize))) {
 
             flush = true;
         }
@@ -448,21 +571,21 @@
     return flush;
 }
 
-bool GrInOrderDrawBuffer::onReserveVertexSpace(GrVertexLayout vertexLayout,
+bool GrInOrderDrawBuffer::onReserveVertexSpace(size_t vertexSize,
                                                int vertexCount,
                                                void** vertices) {
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
     GrAssert(vertexCount > 0);
     GrAssert(NULL != vertices);
     GrAssert(0 == poolState.fUsedPoolVertexBytes);
-    
-    *vertices = fVertexPool.makeSpace(vertexLayout,
+
+    *vertices = fVertexPool.makeSpace(vertexSize,
                                       vertexCount,
                                       &poolState.fPoolVertexBuffer,
                                       &poolState.fPoolStartVertex);
     return NULL != *vertices;
 }
-    
+
 bool GrInOrderDrawBuffer::onReserveIndexSpace(int indexCount, void** indices) {
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
     GrAssert(indexCount > 0);
@@ -477,30 +600,44 @@
 
 void GrInOrderDrawBuffer::releaseReservedVertexSpace() {
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    const GeometrySrcState& geoSrc = this->getGeomSrc(); 
-    
-    GrAssert(kReserved_GeometrySrcType == geoSrc.fVertexSrc);
-    
-    size_t reservedVertexBytes = VertexSize(geoSrc.fVertexLayout) * 
+    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).
+    GrAssert(kReserved_GeometrySrcType == geoSrc.fVertexSrc ||
+             kArray_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 = GrDrawState::VertexSize(geoSrc.fVertexLayout) *
                                  geoSrc.fVertexCount;
-    fVertexPool.putBack(reservedVertexBytes - 
+    fVertexPool.putBack(reservedVertexBytes -
                         poolState.fUsedPoolVertexBytes);
     poolState.fUsedPoolVertexBytes = 0;
-    poolState.fPoolVertexBuffer = 0;
+    poolState.fPoolVertexBuffer = NULL;
+    poolState.fPoolStartVertex = 0;
 }
 
 void GrInOrderDrawBuffer::releaseReservedIndexSpace() {
     GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    const GeometrySrcState& geoSrc = this->getGeomSrc(); 
+    const GeometrySrcState& geoSrc = this->getGeomSrc();
 
-    GrAssert(kReserved_GeometrySrcType == geoSrc.fIndexSrc);
-    
+    // If we get a release index space call then our current source should either be reserved
+    // or array (which we copied into reserved space).
+    GrAssert(kReserved_GeometrySrcType == geoSrc.fIndexSrc ||
+             kArray_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.fPoolStartVertex = 0;
+    poolState.fPoolIndexBuffer = NULL;
+    poolState.fPoolStartIndex = 0;
 }
-    
+
 void GrInOrderDrawBuffer::onSetVertexSourceToArray(const void* vertexArray,
                                                    int vertexCount) {
 
@@ -509,7 +646,7 @@
 #if GR_DEBUG
     bool success =
 #endif
-    fVertexPool.appendVertices(this->getGeomSrc().fVertexLayout,
+    fVertexPool.appendVertices(GrDrawState::VertexSize(this->getVertexLayout()),
                                vertexCount,
                                vertexArray,
                                &poolState.fPoolVertexBuffer,
@@ -531,6 +668,18 @@
     GR_DEBUGASSERT(success);
 }
 
+void GrInOrderDrawBuffer::releaseVertexArray() {
+    // When the client provides an array as the vertex source we handled it
+    // by copying their array into reserved space.
+    this->GrInOrderDrawBuffer::releaseReservedVertexSpace();
+}
+
+void GrInOrderDrawBuffer::releaseIndexArray() {
+    // When the client provides an array as the index source we handled it
+    // by copying their array into reserved space.
+    this->GrInOrderDrawBuffer::releaseReservedIndexSpace();
+}
+
 void GrInOrderDrawBuffer::geometrySourceWillPush() {
     GeometryPoolState& poolState = fGeoPoolStateStack.push_back();
     poolState.fUsedPoolVertexBytes = 0;
@@ -543,27 +692,6 @@
 #endif
 }
 
-void GrInOrderDrawBuffer::releaseVertexArray() {
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    const GeometrySrcState& geoSrc = this->getGeomSrc(); 
-    
-    size_t reservedVertexBytes = VertexSize(geoSrc.fVertexLayout) * 
-    geoSrc.fVertexCount;
-    fVertexPool.putBack(reservedVertexBytes - poolState.fUsedPoolVertexBytes);
-    
-    poolState.fUsedPoolVertexBytes = 0;
-}
-
-void GrInOrderDrawBuffer::releaseIndexArray() {
-    GeometryPoolState& poolState = fGeoPoolStateStack.back();
-    const GeometrySrcState& geoSrc = this->getGeomSrc(); 
-    
-    size_t reservedIndexBytes = sizeof(uint16_t) * geoSrc.fIndexCount;
-    fIndexPool.putBack(reservedIndexBytes - poolState.fUsedPoolIndexBytes);
-    
-    poolState.fUsedPoolIndexBytes = 0;
-}
-
 void GrInOrderDrawBuffer::geometrySourceWillPop(
                                         const GeometrySrcState& restoredState) {
     GrAssert(fGeoPoolStateStack.count() > 1);
@@ -574,50 +702,63 @@
     // pool.
     if (kReserved_GeometrySrcType == restoredState.fVertexSrc ||
         kArray_GeometrySrcType == restoredState.fVertexSrc) {
-        poolState.fUsedPoolVertexBytes = 
-            VertexSize(restoredState.fVertexLayout) * 
+        poolState.fUsedPoolVertexBytes =
+            GrDrawState::VertexSize(restoredState.fVertexLayout) *
             restoredState.fVertexCount;
     }
     if (kReserved_GeometrySrcType == restoredState.fIndexSrc ||
         kArray_GeometrySrcType == restoredState.fIndexSrc) {
-        poolState.fUsedPoolVertexBytes = sizeof(uint16_t) * 
+        poolState.fUsedPoolIndexBytes = sizeof(uint16_t) *
                                          restoredState.fIndexCount;
     }
 }
 
 bool GrInOrderDrawBuffer::needsNewState() const {
-     if (fStates.empty()) {
-        return true;
-     } else {
-        const GrDrawState& old = this->accessSavedDrawState(fStates.back());
-        return old != fCurrDrawState;
-     }
+    return fStates.empty() || !fStates.back().isEqual(this->getDrawState());
 }
 
-void GrInOrderDrawBuffer::pushState() {
-    const GrDrawState& drawState = this->getDrawState();
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrSafeRef(drawState.getTexture(s));
-    }
-    GrSafeRef(drawState.getRenderTarget());
-    this->saveCurrentDrawState(&fStates.push_back());
- }
-
 bool GrInOrderDrawBuffer::needsNewClip() const {
-   if (this->getDrawState().isClipState()) {
-       if (fClips.empty() || (fClipSet && fClips.back() != fClip)) {
+    GrAssert(fClips.count() == fClipOrigins.count());
+    if (this->getDrawState().isClipState()) {
+       if (fClipSet &&
+           (fClips.empty() ||
+            fClips.back() != *this->getClip()->fClipStack ||
+            fClipOrigins.back() != this->getClip()->fOrigin)) {
            return true;
        }
     }
     return false;
 }
 
-void GrInOrderDrawBuffer::pushClip() {
-    fClips.push_back() = fClip;
+void GrInOrderDrawBuffer::recordClip() {
+    fClips.push_back() = *this->getClip()->fClipStack;
+    fClipOrigins.push_back() = this->getClip()->fOrigin;
     fClipSet = false;
+    fCmds.push_back(kSetClip_Cmd);
 }
 
-void GrInOrderDrawBuffer::clipWillBeSet(const GrClip& newClip) {
-    INHERITED::clipWillBeSet(newClip);
+void GrInOrderDrawBuffer::recordState() {
+    fStates.push_back().saveFrom(this->getDrawState());
+    fCmds.push_back(kSetState_Cmd);
+}
+
+GrInOrderDrawBuffer::DrawRecord* GrInOrderDrawBuffer::recordDraw(const DrawInfo& info) {
+    fCmds.push_back(kDraw_Cmd);
+    return &fDraws.push_back(info);
+}
+
+GrInOrderDrawBuffer::StencilPath* GrInOrderDrawBuffer::recordStencilPath() {
+    fCmds.push_back(kStencilPath_Cmd);
+    return &fStencilPaths.push_back();
+}
+
+GrInOrderDrawBuffer::Clear* GrInOrderDrawBuffer::recordClear() {
+    fCmds.push_back(kClear_Cmd);
+    return &fClears.push_back();
+}
+
+void GrInOrderDrawBuffer::clipWillBeSet(const GrClipData* newClipData) {
+    INHERITED::clipWillBeSet(newClipData);
     fClipSet = true;
+    fClipProxyState = kUnknown_ClipProxyState;
 }
diff --git a/src/gpu/GrInOrderDrawBuffer.h b/src/gpu/GrInOrderDrawBuffer.h
index 73746e9..daa5d06 100644
--- a/src/gpu/GrInOrderDrawBuffer.h
+++ b/src/gpu/GrInOrderDrawBuffer.h
@@ -14,24 +14,26 @@
 #include "GrDrawTarget.h"
 #include "GrAllocPool.h"
 #include "GrAllocator.h"
-#include "GrClip.h"
+#include "GrPath.h"
+
+#include "SkClipStack.h"
+#include "SkStrokeRec.h"
+#include "SkTemplates.h"
 
 class GrGpu;
 class GrIndexBufferAllocPool;
 class GrVertexBufferAllocPool;
 
 /**
- * GrInOrderDrawBuffer is an implementation of GrDrawTarget that queues up
- * draws for eventual playback into a GrGpu. In theory one draw buffer could
- * playback into another. When index or vertex buffers are used as geometry
- * sources it is the callers the draw buffer only holds references to the
- * buffers. It is the callers responsibility to ensure that the data is still
- * valid when the draw buffer is played back into a GrGpu. Similarly, it is the
- * caller's responsibility to ensure that all referenced textures, buffers,
- * and rendertargets are associated in the GrGpu object that the buffer is
- * played back into. The buffer requires VB and IB pools to store geometry.
+ * GrInOrderDrawBuffer is an implementation of GrDrawTarget that queues up draws for eventual
+ * playback into a GrGpu. In theory one draw buffer could playback into another. When index or
+ * vertex buffers are used as geometry sources it is the callers the draw buffer only holds
+ * references to the buffers. It is the callers responsibility to ensure that the data is still
+ * valid when the draw buffer is played back into a GrGpu. Similarly, it is the caller's
+ * responsibility to ensure that all referenced textures, buffers, and render-targets are associated
+ * in the GrGpu object that the buffer is played back into. The buffer requires VB and IB pools to
+ * store geometry.
  */
-
 class GrInOrderDrawBuffer : public GrDrawTarget {
 public:
 
@@ -53,115 +55,152 @@
     virtual ~GrInOrderDrawBuffer();
 
     /**
-     * Copies the draw state and clip from target to this draw buffer.
-     *
-     * @param target    the target whose clip and state should be copied.
-     */
-    void initializeDrawStateAndClip(const GrDrawTarget& target);
-
-    /**
-     * Provides the buffer with an index buffer that can be used for quad rendering.
-     * The buffer may be able to batch consecutive drawRects if this is provided.
-     * @param indexBuffer   index buffer with quad indices.
-     */
-    void setQuadIndexBuffer(const GrIndexBuffer* indexBuffer);
-
-    /**
-     * Empties the draw buffer of any queued up draws.
+     * Empties the draw buffer of any queued up draws. This must not be called while inside an
+     * unbalanced pushGeometrySource(). The current draw state and clip are preserved.
      */
     void reset();
 
     /**
-     * plays the queued up draws to another target. Does not empty this buffer so
-     * that it can be played back multiple times.
+     * This plays the queued up draws to another target. It also resets this object (i.e. flushing
+     * is destructive). This buffer must not have an active reserved vertex or index source. Any
+     * reserved geometry on the target will be finalized because it's geometry source will be pushed
+     * before flushing and popped afterwards.
+     *
+     * @return false if the playback trivially drew nothing because nothing was recorded.
+     *
      * @param target    the target to receive the playback
      */
-    void playback(GrDrawTarget* target);
-    
+    bool flushTo(GrDrawTarget* target);
+
+    /**
+     * This function allows the draw buffer to automatically flush itself to another target. This
+     * means the buffer may internally call this->flushTo(target) when it is safe to do so.
+     *
+     * When the auto flush target is set to NULL (as it initially is) the draw buffer will never
+     * automatically flush itself.
+     */
+    void setAutoFlushTarget(GrDrawTarget* target);
+
     // overrides from GrDrawTarget
-    virtual void drawRect(const GrRect& rect, 
-                          const GrMatrix* matrix = NULL,
-                          StageMask stageEnableMask = 0,
-                          const GrRect* srcRects[] = NULL,
-                          const GrMatrix* srcMatrices[] = NULL);
-
-    virtual bool geometryHints(GrVertexLayout vertexLayout,
+    virtual bool geometryHints(size_t vertexSize,
                                int* vertexCount,
-                               int* indexCount) const;
+                               int* indexCount) const SK_OVERRIDE;
+    virtual void clear(const GrIRect* rect,
+                       GrColor color,
+                       GrRenderTarget* renderTarget = NULL) SK_OVERRIDE;
+    virtual void drawRect(const GrRect& rect,
+                          const SkMatrix* matrix,
+                          const GrRect* srcRects[],
+                          const SkMatrix* srcMatrices[]) SK_OVERRIDE;
 
-    virtual void clear(const GrIRect* rect, GrColor color);
+protected:
+    virtual void clipWillBeSet(const GrClipData* newClip) SK_OVERRIDE;
 
 private:
+    enum Cmd {
+        kDraw_Cmd           = 1,
+        kStencilPath_Cmd    = 2,
+        kSetState_Cmd       = 3,
+        kSetClip_Cmd        = 4,
+        kClear_Cmd          = 5,
+    };
 
-    struct Draw {
-        GrPrimitiveType         fPrimitiveType;
-        int                     fStartVertex;
-        int                     fStartIndex;
-        int                     fVertexCount;
-        int                     fIndexCount;
-        bool                    fStateChanged;
-        bool                    fClipChanged;
+    class DrawRecord : public DrawInfo {
+    public:
+        DrawRecord(const DrawInfo& info) : DrawInfo(info) {}
         GrVertexLayout          fVertexLayout;
         const GrVertexBuffer*   fVertexBuffer;
         const GrIndexBuffer*    fIndexBuffer;
     };
 
+    struct StencilPath {
+        StencilPath();
+
+        SkAutoTUnref<const GrPath>  fPath;
+        SkStrokeRec                 fStroke;
+        SkPath::FillType            fFill;
+    };
+
     struct Clear {
-        int fBeforeDrawIdx;
-        GrIRect fRect;
-        GrColor fColor;
+        Clear() : fRenderTarget(NULL) {}
+        ~Clear() { GrSafeUnref(fRenderTarget); }
+
+        GrIRect         fRect;
+        GrColor         fColor;
+        GrRenderTarget* fRenderTarget;
     };
 
     // overrides from GrDrawTarget
-    virtual void onDrawIndexed(GrPrimitiveType primitiveType,
-                               int startVertex,
-                               int startIndex,
-                               int vertexCount,
-                               int indexCount);
-    virtual void onDrawNonIndexed(GrPrimitiveType primitiveType,
-                                  int startVertex,
-                                  int vertexCount);
-    virtual bool onReserveVertexSpace(GrVertexLayout layout, 
+    virtual void onDraw(const DrawInfo&) SK_OVERRIDE;
+    virtual void onStencilPath(const GrPath*, const SkStrokeRec& stroke, SkPath::FillType) SK_OVERRIDE;
+    virtual bool onReserveVertexSpace(size_t vertexSize,
                                       int vertexCount,
-                                      void** vertices);
-    virtual bool onReserveIndexSpace(int indexCount, void** indices);
-    virtual void releaseReservedVertexSpace();
-    virtual void releaseReservedIndexSpace();
+                                      void** vertices) SK_OVERRIDE;
+    virtual bool onReserveIndexSpace(int indexCount,
+                                     void** indices) SK_OVERRIDE;
+    virtual void releaseReservedVertexSpace() SK_OVERRIDE;
+    virtual void releaseReservedIndexSpace() SK_OVERRIDE;
     virtual void onSetVertexSourceToArray(const void* vertexArray,
-                                          int vertexCount);
+                                          int vertexCount) SK_OVERRIDE;
     virtual void onSetIndexSourceToArray(const void* indexArray,
-                                         int indexCount);
-    virtual void releaseVertexArray();
-    virtual void releaseIndexArray();
-    virtual void geometrySourceWillPush();
-    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState);
-    virtual void clipWillBeSet(const GrClip& newClip);
+                                         int indexCount) SK_OVERRIDE;
+    virtual void releaseVertexArray() SK_OVERRIDE;
+    virtual void releaseIndexArray() SK_OVERRIDE;
+    virtual void geometrySourceWillPush() SK_OVERRIDE;
+    virtual void geometrySourceWillPop(const GeometrySrcState& restoredState) SK_OVERRIDE;
+    virtual void willReserveVertexAndIndexSpace(size_t vertexSize,
+                                                int vertexCount,
+                                                int indexCount) SK_OVERRIDE;
+    bool quickInsideClip(const SkRect& devBounds);
 
+    // Attempts to concat instances from info onto the previous draw. info must represent an
+    // instanced draw. The caller must have already recorded a new draw state and clip if necessary.
+    int concatInstancedDraw(const DrawInfo& info);
+
+    // we lazily record state and clip changes in order to skip clips and states that have no
+    // effect.
     bool needsNewState() const;
     bool needsNewClip() const;
 
-    void pushState();
-    void pushClip();
-    
+    // these functions record a command
+    void            recordState();
+    void            recordClip();
+    DrawRecord*     recordDraw(const DrawInfo&);
+    StencilPath*    recordStencilPath();
+    Clear*          recordClear();
+
     enum {
+        kCmdPreallocCnt          = 32,
         kDrawPreallocCnt         = 8,
+        kStencilPathPreallocCnt  = 8,
         kStatePreallocCnt        = 8,
         kClipPreallocCnt         = 8,
         kClearPreallocCnt        = 4,
         kGeoPoolStatePreAllocCnt = 4,
     };
 
-    GrSTAllocator<kDrawPreallocCnt, Draw>               fDraws;
-    GrSTAllocator<kStatePreallocCnt, SavedDrawState>    fStates;
-    GrSTAllocator<kClearPreallocCnt, Clear>             fClears;
-    GrSTAllocator<kClipPreallocCnt, GrClip>             fClips;
-    
+    SkAutoTUnref<const GrGpu> fGpu;
+
+    SkSTArray<kCmdPreallocCnt, uint8_t, true>                          fCmds;
+    GrSTAllocator<kDrawPreallocCnt, DrawRecord>                        fDraws;
+    GrSTAllocator<kStatePreallocCnt, StencilPath>                      fStencilPaths;
+    GrSTAllocator<kStatePreallocCnt, GrDrawState::DeferredState>       fStates;
+    GrSTAllocator<kClearPreallocCnt, Clear>                            fClears;
+
+    GrSTAllocator<kClipPreallocCnt, SkClipStack>        fClips;
+    GrSTAllocator<kClipPreallocCnt, SkIPoint>           fClipOrigins;
+
+    GrDrawTarget*                   fAutoFlushTarget;
+
     bool                            fClipSet;
 
-    GrVertexLayout                  fLastRectVertexLayout;
-    const GrIndexBuffer*            fQuadIndexBuffer;
-    int                             fMaxQuads;
-    int                             fCurrQuad;
+    enum ClipProxyState {
+        kUnknown_ClipProxyState,
+        kValid_ClipProxyState,
+        kInvalid_ClipProxyState
+    };
+    ClipProxyState                  fClipProxyState;
+    SkRect                          fClipProxy;
 
     GrVertexBufferAllocPool&        fVertexPool;
 
@@ -180,6 +219,8 @@
     };
     SkSTArray<kGeoPoolStatePreAllocCnt, GeometryPoolState> fGeoPoolStateStack;
 
+    bool                            fFlushing;
+
     typedef GrDrawTarget INHERITED;
 };
 
diff --git a/src/gpu/GrIndexBuffer.h b/src/gpu/GrIndexBuffer.h
index faa5018..6e556d2 100644
--- a/src/gpu/GrIndexBuffer.h
+++ b/src/gpu/GrIndexBuffer.h
@@ -15,17 +15,17 @@
 
 class GrIndexBuffer : public GrGeometryBuffer {
 public:
-        /**
-         * Retrieves the maximum number of quads that could be rendered
-         * from the index buffer (using kTriangles_PrimitiveType).
-         * @return the maximum number of quads using full size of index buffer.
-         */
-        int maxQuads() const {
-            return this->sizeInBytes() / (sizeof(uint16_t) * 6);
-        }
+    /**
+     * Retrieves the maximum number of quads that could be rendered
+     * from the index buffer (using kTriangles_GrPrimitiveType).
+     * @return the maximum number of quads using full size of index buffer.
+     */
+    int maxQuads() const {
+        return this->sizeInBytes() / (sizeof(uint16_t) * 6);
+    }
 protected:
-    GrIndexBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
-        : INHERITED(gpu, sizeInBytes, dynamic) {}
+    GrIndexBuffer(GrGpu* gpu, bool isWrapped, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, isWrapped, sizeInBytes, dynamic) {}
 private:
     typedef GrGeometryBuffer INHERITED;
 };
diff --git a/src/gpu/GrMatrix.cpp b/src/gpu/GrMatrix.cpp
deleted file mode 100644
index e71636b..0000000
--- a/src/gpu/GrMatrix.cpp
+++ /dev/null
@@ -1,713 +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 "GrMatrix.h"
-#include "GrRect.h"
-#include <stddef.h>
-
-#if 0
-#if GR_SCALAR_IS_FLOAT
-    const GrScalar GrMatrix::gRESCALE(GR_Scalar1);
-#else
-    GR_STATIC_ASSERT(GR_SCALAR_IS_FIXED);
-    // fixed point isn't supported right now
-    GR_STATIC_ASSERT(false);
-const GrScalar GrMatrix::gRESCALE(1 << 30);
-#endif
-
-const GrMatrix::MapProc GrMatrix::gMapProcs[] = {
-// Scales are not both zero
-    &GrMatrix::mapIdentity,
-    &GrMatrix::mapScale,
-    &GrMatrix::mapTranslate,
-    &GrMatrix::mapScaleAndTranslate,
-    &GrMatrix::mapSkew,
-    &GrMatrix::mapScaleAndSkew,
-    &GrMatrix::mapSkewAndTranslate,
-    &GrMatrix::mapNonPerspective,
-    // no optimizations for perspective matrices
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapPerspective,
-
-// Scales are zero (every other is invalid because kScale_TypeBit must be set if
-// kZeroScale_TypeBit is set)
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapZero,
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapSetToTranslate,
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapSwappedScale,
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapSwappedScaleAndTranslate,
-
-    // no optimizations for perspective matrices
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapZero,
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapPerspective,
-    &GrMatrix::mapInvalid,
-    &GrMatrix::mapPerspective,
-};
-
-void GrMatrix::setIdentity() {
-    fM[0] = GR_Scalar1; fM[1] = 0;          fM[2] = 0;
-    fM[3] = 0;          fM[4] = GR_Scalar1; fM[5] = 0;
-    fM[6] = 0;          fM[7] = 0;          fM[8] = gRESCALE;
-    fTypeMask = 0;
-}
-
-void GrMatrix::setTranslate(GrScalar dx, GrScalar dy) {
-    fM[0] = GR_Scalar1; fM[1] = 0;          fM[2] = dx;
-    fM[3] = 0;          fM[4] = GR_Scalar1; fM[5] = dy;
-    fM[6] = 0;          fM[7] = 0;          fM[8] = gRESCALE;
-    fTypeMask = (0 != dx || 0 != dy) ? kTranslate_TypeBit : 0;
-}
-
-void GrMatrix::setScale(GrScalar sx, GrScalar sy) {
-    fM[0] = sx; fM[1] = 0;  fM[2] = 0;
-    fM[3] = 0;  fM[4] = sy; fM[5] = 0;
-    fM[6] = 0;  fM[7] = 0;  fM[8] = gRESCALE;
-    fTypeMask = (GR_Scalar1 != sx || GR_Scalar1 != sy) ? kScale_TypeBit : 0;
-}
-
-void GrMatrix::setSkew(GrScalar skx, GrScalar sky) {
-    fM[0] = GR_Scalar1; fM[1] = skx;        fM[2] = 0;
-    fM[3] = sky;        fM[4] = GR_Scalar1; fM[5] = 0;
-    fM[6] = 0;          fM[7] = 0;          fM[8] = gRESCALE;
-    fTypeMask = (0 != skx || 0 != sky) ? kSkew_TypeBit : 0;
-}
-
-void GrMatrix::setConcat(const GrMatrix& a, const GrMatrix& b) {
-    if (a.isIdentity()) {
-        if (this != &b) {
-            for (int i = 0; i < 9; ++i) {
-                fM[i] = b.fM[i];
-            }
-            fTypeMask = b.fTypeMask;
-        }
-        return;
-    }
-
-    if (b.isIdentity()) {
-        GrAssert(!a.isIdentity());
-        if (this != &a) {
-            for (int i = 0; i < 9; ++i) {
-                    fM[i] = a.fM[i];
-            }
-            fTypeMask = a.fTypeMask;
-        }
-        return;
-    }
-
-    // a and/or b could be this
-    GrMatrix tmp;
-
-    // could do more optimizations based on type bits. Hopefully this call is
-    // low frequency.
-    // TODO: make this work for fixed point
-    if (!((b.fTypeMask | a.fTypeMask) & kPerspective_TypeBit)) {
-        tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3];
-        tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4];
-        tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * gRESCALE;
-
-        tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3];
-        tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4];
-        tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * gRESCALE;
-
-        tmp.fM[6] = 0;
-        tmp.fM[7] = 0;
-        tmp.fM[8] = gRESCALE * gRESCALE;
-    } else {
-        tmp.fM[0] = a.fM[0] * b.fM[0] + a.fM[1] * b.fM[3] + a.fM[2] * b.fM[6];
-        tmp.fM[1] = a.fM[0] * b.fM[1] + a.fM[1] * b.fM[4] + a.fM[2] * b.fM[7];
-        tmp.fM[2] = a.fM[0] * b.fM[2] + a.fM[1] * b.fM[5] + a.fM[2] * b.fM[8];
-
-        tmp.fM[3] = a.fM[3] * b.fM[0] + a.fM[4] * b.fM[3] + a.fM[5] * b.fM[6];
-        tmp.fM[4] = a.fM[3] * b.fM[1] + a.fM[4] * b.fM[4] + a.fM[5] * b.fM[7];
-        tmp.fM[5] = a.fM[3] * b.fM[2] + a.fM[4] * b.fM[5] + a.fM[5] * b.fM[8];
-
-        tmp.fM[6] = a.fM[6] * b.fM[0] + a.fM[7] * b.fM[3] + a.fM[8] * b.fM[6];
-        tmp.fM[7] = a.fM[6] * b.fM[1] + a.fM[7] * b.fM[4] + a.fM[8] * b.fM[7];
-        tmp.fM[8] = a.fM[6] * b.fM[2] + a.fM[7] * b.fM[5] + a.fM[8] * b.fM[8];
-    }
-    *this = tmp;
-    this->computeTypeMask();
-}
-
-void GrMatrix::preConcat(const GrMatrix& m) {
-    setConcat(*this, m);
-}
-
-void GrMatrix::postConcat(const GrMatrix& m) {
-    setConcat(m, *this);
-}
-
-double GrMatrix::determinant() const {
-    if (fTypeMask & kPerspective_TypeBit) {
-        return  fM[0]*((double)fM[4]*fM[8] - (double)fM[5]*fM[7]) +
-                fM[1]*((double)fM[5]*fM[6] - (double)fM[3]*fM[8]) +
-                fM[2]*((double)fM[3]*fM[7] - (double)fM[4]*fM[6]);
-    } else {
-        return (double)fM[0]*fM[4]*gRESCALE -
-               (double)fM[1]*fM[3]*gRESCALE;
-    }
-}
-
-bool GrMatrix::invert(GrMatrix* inverted) const {
-
-    if (isIdentity()) {
-        if (inverted != this) {
-            inverted->setIdentity();
-        }
-        return true;
-    }
-    static const double MIN_DETERMINANT_SQUARED = 1.e-16;
-
-    // could do more optimizations based on type bits. Hopefully this call is
-    // low frequency.
-
-    double det = determinant();
-
-    // check if we can't be inverted
-    if (det*det <= MIN_DETERMINANT_SQUARED) {
-        return false;
-    } else if (NULL == inverted) {
-        return true;
-    }
-
-    double t[9];
-
-    if (fTypeMask & kPerspective_TypeBit) {
-        t[0] = ((double)fM[4]*fM[8] - (double)fM[5]*fM[7]);
-        t[1] = ((double)fM[2]*fM[7] - (double)fM[1]*fM[8]);
-        t[2] = ((double)fM[1]*fM[5] - (double)fM[2]*fM[4]);
-        t[3] = ((double)fM[5]*fM[6] - (double)fM[3]*fM[8]);
-        t[4] = ((double)fM[0]*fM[8] - (double)fM[2]*fM[6]);
-        t[5] = ((double)fM[2]*fM[3] - (double)fM[0]*fM[5]);
-        t[6] = ((double)fM[3]*fM[7] - (double)fM[4]*fM[6]);
-        t[7] = ((double)fM[1]*fM[6] - (double)fM[0]*fM[7]);
-        t[8] = ((double)fM[0]*fM[4] - (double)fM[1]*fM[3]);
-        det = 1.0 / det;
-        for (int i = 0; i < 9; ++i) {
-            inverted->fM[i] = (GrScalar)(t[i] * det);
-        }
-    } else {
-        t[0] =  (double)fM[4]*gRESCALE;
-        t[1] = -(double)fM[1]*gRESCALE;
-        t[2] =  (double)fM[1]*fM[5] - (double)fM[2]*fM[4];
-        t[3] = -(double)fM[3]*gRESCALE;
-        t[4] =  (double)fM[0]*gRESCALE;
-        t[5] =  (double)fM[2]*fM[3] - (double)fM[0]*fM[5];
-        //t[6] = 0.0;
-        //t[7] = 0.0;
-        t[8] = (double)fM[0]*fM[4] - (double)fM[1]*fM[3];
-        det = 1.0 / det;
-        for (int i = 0; i < 6; ++i) {
-            inverted->fM[i] = (GrScalar)(t[i] * det);
-        }
-        inverted->fM[6] = 0;
-        inverted->fM[7] = 0;
-        inverted->fM[8] = (GrScalar)(t[8] * det);
-    }
-    inverted->computeTypeMask();
-    return true;
-}
-
-void GrMatrix::mapRect(GrRect* dst, const GrRect& src) const {
-    GrPoint srcPts[4], dstPts[4];
-    srcPts[0].set(src.fLeft, src.fTop);
-    srcPts[1].set(src.fRight, src.fTop);
-    srcPts[2].set(src.fRight, src.fBottom);
-    srcPts[3].set(src.fLeft, src.fBottom);
-    this->mapPoints(dstPts, srcPts, 4);
-    dst->setBounds(dstPts, 4);
-}
-
-bool GrMatrix::hasPerspective() const {
-    GrAssert(!!(kPerspective_TypeBit & fTypeMask) ==
-             (fM[kPersp0] != 0 || fM[kPersp1] != 0 || fM[kPersp2] != gRESCALE));
-    return 0 != (kPerspective_TypeBit & fTypeMask);
-}
-
-bool GrMatrix::isIdentity() const {
-    GrAssert((0 == fTypeMask) ==
-             (GR_Scalar1 == fM[kScaleX] && 0          == fM[kSkewX]  && 0          == fM[kTransX] &&
-              0          == fM[kSkewY]  && GR_Scalar1 == fM[kScaleY] && 0          == fM[kTransY] &&
-              0          == fM[kPersp0] && 0          == fM[kPersp1] && gRESCALE == fM[kPersp2]));
-    return (0 == fTypeMask);
-}
-
-
-bool GrMatrix::preservesAxisAlignment() const {
-
-    // check if matrix is trans and scale only
-    static const int gAllowedMask1 = kScale_TypeBit | kTranslate_TypeBit;
-
-    if (!(~gAllowedMask1 & fTypeMask)) {
-        return true;
-    }
-
-    // check matrix is trans and skew only (0 scale)
-    static const int gAllowedMask2 = kScale_TypeBit | kSkew_TypeBit |
-                                     kTranslate_TypeBit | kZeroScale_TypeBit;
-
-    if (!(~gAllowedMask2 & fTypeMask) && (kZeroScale_TypeBit & fTypeMask)) {
-        return true;
-    }
-
-    return false;
-}
-
-GrScalar GrMatrix::getMaxStretch() const {
-
-    if (fTypeMask & kPerspective_TypeBit) {
-        return -GR_Scalar1;
-    }
-
-    GrScalar stretch;
-
-    if (isIdentity()) {
-        stretch = GR_Scalar1;
-    } else if (!(fTypeMask & kSkew_TypeBit)) {
-        stretch = GrMax(GrScalarAbs(fM[kScaleX]), GrScalarAbs(fM[kScaleY]));
-    } else if (fTypeMask & kZeroScale_TypeBit) {
-        stretch = GrMax(GrScalarAbs(fM[kSkewX]), GrScalarAbs(fM[kSkewY]));
-    } else {
-        // ignore the translation part of the matrix, just look at 2x2 portion.
-        // compute singular values, take largest abs value.
-        // [a b; b c] = A^T*A
-        GrScalar a = GrMul(fM[kScaleX], fM[kScaleX]) + GrMul(fM[kSkewY],  fM[kSkewY]);
-        GrScalar b = GrMul(fM[kScaleX], fM[kSkewX]) +  GrMul(fM[kScaleY], fM[kSkewY]);
-        GrScalar c = GrMul(fM[kSkewX],  fM[kSkewX]) +  GrMul(fM[kScaleY], fM[kScaleY]);
-        // eigenvalues of A^T*A are the squared singular values of A.
-        // characteristic equation is det((A^T*A) - l*I) = 0
-        // l^2 - (a + c)l + (ac-b^2)
-        // solve using quadratic equation (divisor is non-zero since l^2 has 1 coeff
-        // and roots are guaraunteed to be pos and real).
-        GrScalar largerRoot;
-        GrScalar bSqd = GrMul(b,b);
-        // TODO: fixed point tolerance value.
-        if (bSqd < 1e-10) { // will be true if upper left 2x2 is orthogonal, which is common, so save some math
-            largerRoot = GrMax(a, c);
-        } else {
-            GrScalar aminusc = a - c;
-            GrScalar apluscdiv2 = (a + c) / 2;
-            GrScalar x = sqrtf(GrMul(aminusc,aminusc) + GrMul(4,(bSqd))) / 2;
-            largerRoot = apluscdiv2 + x;
-        }
-
-        stretch = sqrtf(largerRoot);
-    }
-#if GR_DEBUG && 0
-    // test a bunch of vectors. None should be scaled by more than stretch
-    // (modulo some error) and we should find a vector that is scaled by almost
-    // stretch.
-    GrPoint pt;
-    GrScalar max = 0;
-    for (int i = 0; i < 1000; ++i) {
-        GrScalar x = (float)rand() / RAND_MAX;
-        GrScalar y = sqrtf(1 - (x*x));
-        pt.fX = fM[kScaleX]*x + fM[kSkewX]*y;
-        pt.fY = fM[kSkewY]*x + fM[kScaleY]*y;
-        GrScalar d = pt.distanceToOrigin();
-        GrAssert(d <= (1.0001 * stretch));
-        max = GrMax(max, pt.distanceToOrigin());
-    }
-    GrAssert((stretch - max) < .05*stretch);
-#endif
-    return stretch;
-}
-
-bool GrMatrix::operator == (const GrMatrix& m) const {
-    if (fTypeMask != m.fTypeMask) {
-        return false;
-    }
-    if (!fTypeMask) {
-        return true;
-    }
-    for (int i = 0; i < 9; ++i) {
-        if (m.fM[i] != fM[i]) {
-            return false;
-        }
-    }
-    return true;
-}
-
-bool GrMatrix::operator != (const GrMatrix& m) const {
-    return !(*this == m);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// Matrix transformation procs
-//////
-
-void GrMatrix::mapIdentity(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    if (src != dst) {
-        for (uint32_t i = 0; i < count; ++i) {
-            dst[i] = src[i];
-        }
-    }
-}
-
-void GrMatrix::mapScale(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    for (uint32_t i = 0; i < count; ++i) {
-        dst[i].fX = GrMul(src[i].fX, fM[kScaleX]);
-        dst[i].fY = GrMul(src[i].fY, fM[kScaleY]);
-    }
-}
-
-
-void GrMatrix::mapTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    for (uint32_t i = 0; i < count; ++i) {
-        dst[i].fX = src[i].fX + fM[kTransX];
-        dst[i].fY = src[i].fY + fM[kTransY];
-    }
-}
-
-void GrMatrix::mapScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    for (uint32_t i = 0; i < count; ++i) {
-        dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + fM[kTransX];
-        dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + fM[kTransY];
-    }
-}
-
-void GrMatrix::mapSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    if (src != dst) {
-        for (uint32_t i = 0; i < count; ++i) {
-            dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]);
-            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]);
-        }
-    } else {
-        for (uint32_t i = 0; i < count; ++i) {
-            GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]);
-            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]);
-            dst[i].fX = newX;
-        }
-    }
-}
-
-void GrMatrix::mapScaleAndSkew(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    if (src != dst) {
-        for (uint32_t i = 0; i < count; ++i) {
-            dst[i].fX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]);
-            dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]);
-        }
-    } else {
-        for (uint32_t i = 0; i < count; ++i) {
-            GrScalar newX = GrMul(src[i].fX, fM[kScaleX]) + GrMul(src[i].fY, fM[kSkewX]);
-            dst[i].fY = GrMul(src[i].fY, fM[kScaleY]) + GrMul(src[i].fX, fM[kSkewY]);
-            dst[i].fX = newX;
-        }
-    }
-}
-
-void GrMatrix::mapSkewAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    if (src != dst) {
-        for (uint32_t i = 0; i < count; ++i) {
-            dst[i].fX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
-            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
-        }
-    } else {
-        for (uint32_t i = 0; i < count; ++i) {
-            GrScalar newX = src[i].fX + GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
-            dst[i].fY = src[i].fY + GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
-            dst[i].fX = newX;
-        }
-    }
-}
-
-void GrMatrix::mapNonPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    if (src != dst) {
-        for (uint32_t i = 0; i < count; ++i) {
-            dst[i].fX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];
-            dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];
-        }
-    } else {
-        for (uint32_t i = 0; i < count; ++i) {
-            GrScalar newX = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];
-            dst[i].fY = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];
-            dst[i].fX = newX;
-        }
-    }
-}
-
-void GrMatrix::mapPerspective(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    for (uint32_t i = 0; i < count; ++i) {
-        GrScalar x, y, w;
-        x = GrMul(fM[kScaleX], src[i].fX) + GrMul(fM[kSkewX], src[i].fY) + fM[kTransX];
-        y = GrMul(fM[kSkewY], src[i].fX) + GrMul(fM[kScaleY], src[i].fY) + fM[kTransY];
-        w = GrMul(fM[kPersp0], src[i].fX) + GrMul(fM[kPersp1], src[i].fY) + fM[kPersp2];
-        // TODO need fixed point invert
-        if (w) {
-            w = 1 / w;
-        }
-        dst[i].fX = GrMul(x, w);
-        dst[i].fY = GrMul(y, w);
-    }
-}
-
-void GrMatrix::mapInvalid(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    GrAssert(0);
-}
-
-void GrMatrix::mapZero(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    memset(dst, 0, sizeof(GrPoint)*count);
-}
-
-void GrMatrix::mapSetToTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    for (uint32_t i = 0; i < count; ++i) {
-        dst[i].fX = fM[kTransX];
-        dst[i].fY = fM[kTransY];
-    }
-}
-
-void GrMatrix::mapSwappedScale(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    if (src != dst) {
-        for (uint32_t i = 0; i < count; ++i) {
-            dst[i].fX = GrMul(src[i].fY, fM[kSkewX]);
-            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]);
-        }
-    } else {
-        for (uint32_t i = 0; i < count; ++i) {
-            GrScalar newX = GrMul(src[i].fY, fM[kSkewX]);
-            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]);
-            dst[i].fX = newX;
-        }
-    }
-}
-
-void GrMatrix::mapSwappedScaleAndTranslate(GrPoint* dst, const GrPoint* src, uint32_t count) const {
-    if (src != dst) {
-        for (uint32_t i = 0; i < count; ++i) {
-            dst[i].fX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
-            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
-        }
-    } else {
-        for (uint32_t i = 0; i < count; ++i) {
-            GrScalar newX = GrMul(src[i].fY, fM[kSkewX]) + fM[kTransX];
-            dst[i].fY = GrMul(src[i].fX, fM[kSkewY]) + fM[kTransY];
-            dst[i].fX = newX;
-        }
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Unit test
-//////
-
-#include "GrRandom.h"
-
-#if GR_DEBUG
-enum MatrixType {
-    kRotate_MatrixType,
-    kScaleX_MatrixType,
-    kScaleY_MatrixType,
-    kSkewX_MatrixType,
-    kSkewY_MatrixType,
-    kTranslateX_MatrixType,
-    kTranslateY_MatrixType,
-    kSwapScaleXY_MatrixType,
-    kPersp_MatrixType,
-
-    kMatrixTypeCount
-};
-
-static void create_matrix(GrMatrix* matrix, GrRandom& rand) {
-    MatrixType type = (MatrixType)(rand.nextU() % kMatrixTypeCount);
-    switch (type) {
-        case kRotate_MatrixType: {
-            float angle = rand.nextF() * 2 *3.14159265358979323846f;
-            GrScalar cosa = GrFloatToScalar(cosf(angle));
-            GrScalar sina = GrFloatToScalar(sinf(angle));
-            matrix->setAll(cosa,      -sina,           0,
-                           sina,       cosa,           0,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kScaleX_MatrixType: {
-            GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2));
-            matrix->setAll(scale,      0,              0,
-                           0,          GR_Scalar1,     0,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kScaleY_MatrixType: {
-            GrScalar scale = GrFloatToScalar(rand.nextF(-2, 2));
-            matrix->setAll(GR_Scalar1, 0,              0,
-                           0,          scale,          0,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kSkewX_MatrixType: {
-            GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2));
-            matrix->setAll(GR_Scalar1, skew,           0,
-                           0,          GR_Scalar1,     0,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kSkewY_MatrixType: {
-            GrScalar skew = GrFloatToScalar(rand.nextF(-2, 2));
-            matrix->setAll(GR_Scalar1, 0,              0,
-                           skew,       GR_Scalar1,     0,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kTranslateX_MatrixType: {
-            GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10));
-            matrix->setAll(GR_Scalar1, 0,              trans,
-                           0,          GR_Scalar1,     0,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kTranslateY_MatrixType: {
-            GrScalar trans = GrFloatToScalar(rand.nextF(-10, 10));
-            matrix->setAll(GR_Scalar1, 0,              0,
-                           0,          GR_Scalar1,     trans,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kSwapScaleXY_MatrixType: {
-            GrScalar xy = GrFloatToScalar(rand.nextF(-2, 2));
-            GrScalar yx = GrFloatToScalar(rand.nextF(-2, 2));
-            matrix->setAll(0,          xy,             0,
-                           yx,         0,              0,
-                           0,          0,              GrMatrix::I()[8]);
-        } break;
-        case kPersp_MatrixType: {
-            GrScalar p0 = GrFloatToScalar(rand.nextF(-2, 2));
-            GrScalar p1 = GrFloatToScalar(rand.nextF(-2, 2));
-            GrScalar p2 = GrFloatToScalar(rand.nextF(-0.5f, 0.75f));
-            matrix->setAll(GR_Scalar1, 0,              0,
-                           0,          GR_Scalar1,     0,
-                           p0,         p1,             GrMul(p2,GrMatrix::I()[8]));
-        } break;
-        default:
-            GrAssert(0);
-            break;
-    }
-}
-#endif
-
-void GrMatrix::UnitTest() {
-    GrRandom rand;
-
-    // Create a bunch of matrices and test point mapping, max stretch calc,
-    // inversion and multiply-by-inverse.
-#if GR_DEBUG
-    for (int i = 0; i < 10000; ++i) {
-        GrMatrix a, b;
-        a.setIdentity();
-        int num = rand.nextU() % 6;
-        // force testing of I and swapXY
-        if (0 == i) {
-            num = 0;
-            GrAssert(a.isIdentity());
-        } else if (1 == i) {
-            num = 0;
-            a.setAll(0, GR_Scalar1, 0,
-                     GR_Scalar1, 0, 0,
-                     0, 0, I()[8]);
-        }
-        for (int j = 0; j < num; ++j) {
-            create_matrix(&b, rand);
-            a.preConcat(b);
-        }
-
-        GrScalar maxStretch = a.getMaxStretch();
-        if (maxStretch > 0) {
-            maxStretch = GrMul(GR_Scalar1 + GR_Scalar1 / 100, maxStretch);
-        }
-        GrPoint origin = a.mapPoint(GrPoint::Make(0,0));
-
-        for (int j = 0; j < 9; ++j) {
-            int mask, origMask = a.fTypeMask;
-            GrScalar old = a[j];
-
-            a.set(j, GR_Scalar1);
-            mask = a.fTypeMask;
-            a.computeTypeMask();
-            GrAssert(mask == a.fTypeMask);
-
-            a.set(j, 0);
-            mask = a.fTypeMask;
-            a.computeTypeMask();
-            GrAssert(mask == a.fTypeMask);
-
-            a.set(j, 10 * GR_Scalar1);
-            mask = a.fTypeMask;
-            a.computeTypeMask();
-            GrAssert(mask == a.fTypeMask);
-
-            a.set(j, old);
-            GrAssert(a.fTypeMask == origMask);
-        }
-
-        for (int j = 0; j < 100; ++j) {
-            GrPoint pt;
-            pt.fX = GrFloatToScalar(rand.nextF(-10, 10));
-            pt.fY = GrFloatToScalar(rand.nextF(-10, 10));
-
-            GrPoint t0, t1, t2;
-            t0 = a.mapPoint(pt);             // map to a new point
-            t1 = pt;
-            a.mapPoints(&t1, &t1, 1);        // in place
-            a.mapPerspective(&t2, &pt, 1);   // full mult
-            GrAssert(t0 == t1 && t1 == t2);
-            if (maxStretch >= 0.f) {
-                GrVec vec = origin - t0;
-//                vec.setBetween(t0, origin);
-                GrScalar stretch = vec.length() / pt.distanceToOrigin();
-                GrAssert(stretch <= maxStretch);
-            }
-        }
-        double det = a.determinant();
-        if (fabs(det) > 1e-3 && a.invert(&b)) {
-            GrMatrix c;
-            c.setConcat(a,b);
-            for (int i = 0; i < 9; ++i) {
-                GrScalar diff = GrScalarAbs(c[i] - I()[i]);
-                GrAssert(diff < (5*GR_Scalar1 / 100));
-            }
-        }
-    }
-#endif
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#endif
-
-int Gr_clz(uint32_t n) {
-    if (0 == n) {
-        return 32;
-    }
-
-    int count = 0;
-    if (0 == (n & 0xFFFF0000)) {
-        count += 16;
-        n <<= 16;
-    }
-    if (0 == (n & 0xFF000000)) {
-        count += 8;
-        n <<= 8;
-    }
-    if (0 == (n & 0xF0000000)) {
-        count += 4;
-        n <<= 4;
-    }
-    if (0 == (n & 0xC0000000)) {
-        count += 2;
-        n <<= 2;
-    }
-    if (0 == (n & 0x80000000)) {
-        count += 1;
-    }
-    return count;
-}
diff --git a/src/gpu/GrMemory.cpp b/src/gpu/GrMemory.cpp
index fa6ee2f..bf96b0b 100644
--- a/src/gpu/GrMemory.cpp
+++ b/src/gpu/GrMemory.cpp
@@ -9,6 +9,7 @@
 
 
 #include <stdlib.h>
+#include "GrTypes.h"
 
 void* GrMalloc(size_t bytes) {
     void* ptr = ::malloc(bytes);
@@ -23,5 +24,3 @@
         ::free(ptr);
     }
 }
-
-
diff --git a/src/gpu/GrMemoryPool.cpp b/src/gpu/GrMemoryPool.cpp
new file mode 100644
index 0000000..fc777e0
--- /dev/null
+++ b/src/gpu/GrMemoryPool.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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 "GrMemoryPool.h"
+
+#if GR_DEBUG
+    #define VALIDATE this->validate()
+#else
+    #define VALIDATE
+#endif
+
+GrMemoryPool::GrMemoryPool(size_t preallocSize, size_t minAllocSize) {
+    GR_DEBUGCODE(fAllocationCnt = 0);
+
+    minAllocSize = GrMax<size_t>(minAllocSize, 1 << 10);
+    fMinAllocSize = GrSizeAlignUp(minAllocSize + kPerAllocPad, kAlignment),
+    fPreallocSize = GrSizeAlignUp(preallocSize + kPerAllocPad, kAlignment);
+    fPreallocSize = GrMax(fPreallocSize, fMinAllocSize);
+
+    fHead = CreateBlock(fPreallocSize);
+    fTail = fHead;
+    fHead->fNext = NULL;
+    fHead->fPrev = NULL;
+    VALIDATE;
+};
+
+GrMemoryPool::~GrMemoryPool() {
+    VALIDATE;
+    GrAssert(0 == fAllocationCnt);
+    GrAssert(fHead == fTail);
+    GrAssert(0 == fHead->fLiveCount);
+    DeleteBlock(fHead);
+};
+
+void* GrMemoryPool::allocate(size_t size) {
+    VALIDATE;
+    size = GrSizeAlignUp(size, kAlignment);
+    size += kPerAllocPad;
+    if (fTail->fFreeSize < size) {
+        int blockSize = size;
+        blockSize = GrMax<size_t>(blockSize, fMinAllocSize);
+        BlockHeader* block = CreateBlock(blockSize);
+
+        block->fPrev = fTail;
+        block->fNext = NULL;
+        GrAssert(NULL == fTail->fNext);
+        fTail->fNext = block;
+        fTail = block;
+    }
+    GrAssert(fTail->fFreeSize >= size);
+    intptr_t ptr = fTail->fCurrPtr;
+    // We stash a pointer to the block header, just before the allocated space,
+    // so that we can decrement the live count on delete in constant time.
+    *reinterpret_cast<BlockHeader**>(ptr) = fTail;
+    ptr += kPerAllocPad;
+    fTail->fPrevPtr = fTail->fCurrPtr;
+    fTail->fCurrPtr += size;
+    fTail->fFreeSize -= size;
+    fTail->fLiveCount += 1;
+    GR_DEBUGCODE(++fAllocationCnt);
+    VALIDATE;
+    return reinterpret_cast<void*>(ptr);
+}
+
+void GrMemoryPool::release(void* p) {
+    VALIDATE;
+    intptr_t ptr = reinterpret_cast<intptr_t>(p) - kPerAllocPad;
+    BlockHeader* block = *reinterpret_cast<BlockHeader**>(ptr);
+    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->fLiveCount = 0;
+            fHead->fFreeSize = fPreallocSize;
+        } else {
+            BlockHeader* prev = block->fPrev;
+            BlockHeader* next = block->fNext;
+            GrAssert(prev);
+            prev->fNext = next;
+            if (next) {
+                next->fPrev = prev;
+            } else {
+                GrAssert(fTail == block);
+                fTail = prev;
+            }
+            DeleteBlock(block);
+        }
+    } else {
+        --block->fLiveCount;
+        // Trivial reclaim: if we're releasing the most recent allocation, reuse it
+        if (block->fPrevPtr == ptr) {
+            block->fFreeSize += (block->fCurrPtr - block->fPrevPtr);
+            block->fCurrPtr = block->fPrevPtr;
+        }
+    }
+    GR_DEBUGCODE(--fAllocationCnt);
+    VALIDATE;
+}
+
+GrMemoryPool::BlockHeader* GrMemoryPool::CreateBlock(size_t size) {
+    BlockHeader* block =
+        reinterpret_cast<BlockHeader*>(GrMalloc(size + kHeaderSize));
+    // we assume malloc gives us aligned memory
+    GrAssert(!(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.
+    return block;
+}
+
+void GrMemoryPool::DeleteBlock(BlockHeader* block) {
+    GrFree(block);
+}
+
+void GrMemoryPool::validate() {
+#ifdef SK_DEBUG
+    BlockHeader* block = fHead;
+    BlockHeader* prev = NULL;
+    GrAssert(block);
+    int allocCount = 0;
+    do {
+        allocCount += block->fLiveCount;
+        GrAssert(prev == block->fPrev);
+        if (NULL != prev) {
+            GrAssert(prev->fNext == block);
+        }
+
+        intptr_t b = reinterpret_cast<intptr_t>(block);
+        size_t ptrOffset = block->fCurrPtr - b;
+        size_t totalSize = ptrOffset + block->fFreeSize;
+        size_t userSize = totalSize - kHeaderSize;
+        intptr_t userStart = b + kHeaderSize;
+
+        GrAssert(!(b % kAlignment));
+        GrAssert(!(totalSize % kAlignment));
+        GrAssert(!(userSize % kAlignment));
+        GrAssert(!(block->fCurrPtr % kAlignment));
+        if (fHead != block) {
+            GrAssert(block->fLiveCount);
+            GrAssert(userSize >= fMinAllocSize);
+        } else {
+            GrAssert(userSize == fPreallocSize);
+        }
+        if (!block->fLiveCount) {
+            GrAssert(ptrOffset ==  kHeaderSize);
+            GrAssert(userStart == block->fCurrPtr);
+        } else {
+            GrAssert(block == *reinterpret_cast<BlockHeader**>(userStart));
+        }
+        prev = block;
+    } while ((block = block->fNext));
+    GrAssert(allocCount == fAllocationCnt);
+    GrAssert(prev == fTail);
+#endif
+}
diff --git a/src/gpu/GrMemoryPool.h b/src/gpu/GrMemoryPool.h
new file mode 100644
index 0000000..d327df4
--- /dev/null
+++ b/src/gpu/GrMemoryPool.h
@@ -0,0 +1,80 @@
+/*
+ * 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 GrMemoryPool_DEFINED
+#define GrMemoryPool_DEFINED
+
+#include "GrTypes.h"
+
+/**
+ * Allocates memory in blocks and parcels out space in the blocks for allocation
+ * requests. It is optimized for allocate / release speed over memory
+ * effeciency. The interface is designed to be used to implement operator new
+ * and delete overrides. All allocations are expected to be released before the
+ * pool's destructor is called. Allocations will be 8-byte aligned.
+ */
+class GrMemoryPool {
+public:
+    /**
+     * Prealloc size is the amount of space to make available at pool creation
+     * time and keep around until pool destruction. The min alloc size is the
+     * smallest allowed size of additional allocations.
+     */
+    GrMemoryPool(size_t preallocSize, size_t minAllocSize);
+
+    ~GrMemoryPool();
+
+    /**
+     * Allocates memory. The memory must be freed with release().
+     */
+    void* allocate(size_t size);
+
+    /**
+     * p must have been returned by allocate()
+     */
+    void release(void* p);
+
+    /**
+     * Returns true if there are no unreleased allocations.
+     */
+    bool isEmpty() const { return fTail == fHead && !fHead->fLiveCount; }
+
+private:
+    struct BlockHeader;
+
+    BlockHeader* CreateBlock(size_t size);
+
+    void DeleteBlock(BlockHeader* block);
+
+    void validate();
+
+    struct BlockHeader {
+        BlockHeader* fNext;      ///< doubly-linked list of blocks.
+        BlockHeader* fPrev;
+        int          fLiveCount; ///< number of outstanding allocations in the
+                                 ///< block.
+        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.
+    };
+
+    enum {
+        // We assume this alignment is good enough for everybody.
+        kAlignment    = 8,
+        kHeaderSize   = GR_CT_ALIGN_UP(sizeof(BlockHeader), kAlignment),
+        kPerAllocPad  = GR_CT_ALIGN_UP(sizeof(BlockHeader*), kAlignment),
+    };
+    size_t                            fPreallocSize;
+    size_t                            fMinAllocSize;
+    BlockHeader*                      fHead;
+    BlockHeader*                      fTail;
+#if GR_DEBUG
+    int                               fAllocationCnt;
+#endif
+};
+
+#endif
diff --git a/src/gpu/GrPath.cpp b/src/gpu/GrPath.cpp
new file mode 100644
index 0000000..afd2239
--- /dev/null
+++ b/src/gpu/GrPath.cpp
@@ -0,0 +1,10 @@
+/*
+ * 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 "GrPath.h"
+
+SK_DEFINE_INST_COUNT(GrPath)
diff --git a/src/gpu/GrPath.h b/src/gpu/GrPath.h
new file mode 100644
index 0000000..ad3a5c3
--- /dev/null
+++ b/src/gpu/GrPath.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrPath_DEFINED
+#define GrPath_DEFINED
+
+#include "GrResource.h"
+#include "GrRect.h"
+
+class GrPath : public GrResource {
+public:
+    SK_DECLARE_INST_COUNT(GrPath);
+
+    GrPath(GrGpu* gpu, bool isWrapped) : INHERITED(gpu, isWrapped) {}
+
+    const GrRect& getBounds() const { return fBounds; }
+
+protected:
+    GrRect fBounds;
+
+private:
+    typedef GrResource INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrPathRenderer.cpp b/src/gpu/GrPathRenderer.cpp
index 31e06a6..e0d2682 100644
--- a/src/gpu/GrPathRenderer.cpp
+++ b/src/gpu/GrPathRenderer.cpp
@@ -8,6 +8,7 @@
 
 #include "GrPathRenderer.h"
 
+SK_DEFINE_INST_COUNT(GrPathRenderer)
+
 GrPathRenderer::GrPathRenderer() {
 }
-
diff --git a/src/gpu/GrPathRenderer.h b/src/gpu/GrPathRenderer.h
index 6ffcade..ebd464f 100644
--- a/src/gpu/GrPathRenderer.h
+++ b/src/gpu/GrPathRenderer.h
@@ -12,7 +12,9 @@
 
 #include "GrDrawTarget.h"
 #include "GrPathRendererChain.h"
+#include "GrStencil.h"
 
+#include "SkStrokeRec.h"
 #include "SkTArray.h"
 
 class SkPath;
@@ -21,119 +23,159 @@
 
 /**
  *  Base class for drawing paths into a GrDrawTarget.
- *  Paths may be drawn multiple times as when tiling for supersampling. The 
- *  calls on GrPathRenderer to draw a path will look like this:
- *  
- *  pr->setPath(target, path, fill, aa, translate); // sets the path to draw
- *      pr->drawPath(...);  // draw the path
- *      pr->drawPath(...);
- *      ...
- *  pr->clearPath();  // finished with the path
+ *
+ *  Derived classes can use stages GrPaint::kTotalStages through GrDrawState::kNumStages-1. The
+ *  stages before GrPaint::kTotalStages are reserved for setting up the draw (i.e., textures and
+ *  filter masks).
  */
 class GR_API GrPathRenderer : public GrRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(GrPathRenderer)
 
     /**
-     * This is called to install custom path renderers in every GrContext at
-     * create time. The default implementation in GrCreatePathRenderer_none.cpp
-     * does not add any additional renderers. Link against another
-     * implementation to install your own. The first added is the most preferred
-     * path renderer, second is second most preferred, etc.
+     * This is called to install custom path renderers in every GrContext at create time. The
+     * default implementation in GrCreatePathRenderer_none.cpp does not add any additional
+     * renderers. Link against another implementation to install your own. The first added is the
+     * most preferred path renderer, second is second most preferred, etc.
      *
      * @param context   the context that will use the path renderer
-     * @param flags     flags indicating how path renderers will be used
      * @param prChain   the chain to add path renderers to.
      */
-    static void AddPathRenderers(GrContext* context,
-                                 GrPathRendererChain::UsageFlags flags,
-                                 GrPathRendererChain* prChain);
+    static void AddPathRenderers(GrContext* context, GrPathRendererChain* prChain);
 
 
     GrPathRenderer();
 
     /**
-     * For complex clips Gr uses the stencil buffer. The path renderer must be
-     * able to render paths into the stencil buffer. However, the path renderer
-     * itself may require the stencil buffer to resolve the path fill rule.
-     * This function queries whether the path render needs its own stencil
-     * pass. If this returns false then drawPath() should not modify the
-     * the target's stencil settings but use those already set on target. The
-     * target is passed as a param in case the answer depends upon draw state.
-     * The view matrix and render target set on the draw target may change 
-     * before setPath/drawPath is called and so shouldn't be considered.
+     * A caller may wish to use a path renderer to draw a path into the stencil buffer. However,
+     * the path renderer itself may require use of the stencil buffer. Also a path renderer may
+     * use a GrEffect coverage stage that sets coverage to zero to eliminate pixels that are covered
+     * by bounding geometry but outside the path. These exterior pixels would still be rendered into
+     * the stencil.
      *
-     * @param target target that the path will be rendered to
-     * @param path   the path that will be drawn
-     * @param fill   the fill rule that will be used, will never be an inverse
-     *               rule.
-     *
-     * @return false if this path renderer can generate interior-only fragments
-     *         without changing the stencil settings on the target. If it
-     *         returns true the drawPathToStencil will be used when rendering
-     *         clips.
+     * A GrPathRenderer can provide three levels of support for stenciling paths:
+     * 1) kNoRestriction: This is the most general. The caller sets up the GrDrawState on the target
+     *                    and calls drawPath(). The path is rendered exactly as the draw state
+     *                    indicates including support for simultaneous color and stenciling with
+     *                    arbitrary stenciling rules. Pixels partially covered by AA paths are
+     *                    affected by the stencil settings.
+     * 2) kStencilOnly: The path renderer cannot apply arbitrary stencil rules nor shade and stencil
+     *                  simultaneously. The path renderer does support the stencilPath() function
+     *                  which performs no color writes and writes a non-zero stencil value to pixels
+     *                  covered by the path.
+     * 3) kNoSupport: This path renderer cannot be used to stencil the path.
      */
-    virtual bool requiresStencilPass(const SkPath& path,
-                                     GrPathFill fill,
+    typedef GrPathRendererChain::StencilSupport StencilSupport;
+    static const StencilSupport kNoSupport_StencilSupport =
+        GrPathRendererChain::kNoSupport_StencilSupport;
+    static const StencilSupport kStencilOnly_StencilSupport =
+        GrPathRendererChain::kStencilOnly_StencilSupport;
+    static const StencilSupport kNoRestriction_StencilSupport =
+        GrPathRendererChain::kNoRestriction_StencilSupport;
+
+    /**
+     * This function is to get the stencil support for a particular path. The path's fill must
+     * not be an inverse type.
+     *
+     * @param target    target that the path will be rendered to
+     * @param path      the path that will be drawn
+     * @param stroke    the stroke information (width, join, cap).
+     */
+    StencilSupport getStencilSupport(const SkPath& path,
+                                     const SkStrokeRec& stroke,
                                      const GrDrawTarget* target) const {
-        return false;
+        GrAssert(!path.isInverseFillType());
+        return this->onGetStencilSupport(path, stroke, target);
     }
 
+    /**
+     * Returns true if this path renderer is able to render the path. Returning false allows the
+     * caller to fallback to another path renderer This function is called when searching for a path
+     * renderer capable of rendering a path.
+     *
+     * @param path       The path to draw
+     * @param stroke     The stroke information (width, join, cap)
+     * @param target     The target that the path will be rendered to
+     * @param antiAlias  True if anti-aliasing is required.
+     *
+     * @return  true if the path can be drawn by this object, false otherwise.
+     */
     virtual bool canDrawPath(const SkPath& path,
-                             GrPathFill fill,
+                             const SkStrokeRec& rec,
                              const GrDrawTarget* target,
                              bool antiAlias) const = 0;
     /**
-     * Draws the path into the draw target. If requiresStencilBuffer returned
-     * false then the target may be setup for stencil rendering (since the 
-     * path renderer didn't claim that it needs to use the stencil internally).
+     * Draws the path into the draw target. If getStencilSupport() would return kNoRestriction then
+     * the subclass must respect the stencil settings of the target's draw state.
      *
-     * @param stages                bitfield that indicates which stages are
-     *                              in use. All enabled stages expect positions
-     *                              as texture coordinates. The path renderer
-     *                              use the remaining stages for its path
-     *                              filling algorithm.
+     * @param path                  the path to draw.
+     * @param stroke                the stroke information (width, join, cap)
+     * @param target                target that the path will be rendered to
+     * @param antiAlias             true if anti-aliasing is required.
      */
-    virtual bool drawPath(const SkPath& path,
-                          GrPathFill fill,
-                          const GrVec* translate,
-                          GrDrawTarget* target,
-                          GrDrawState::StageMask stageMask,
-                          bool antiAlias) {
-        GrAssert(this->canDrawPath(path, fill, target, antiAlias));
-        return this->onDrawPath(path, fill, translate,
-                                target, stageMask, antiAlias);
+    bool drawPath(const SkPath& path,
+                  const SkStrokeRec& stroke,
+                  GrDrawTarget* target,
+                  bool antiAlias) {
+        GrAssert(this->canDrawPath(path, stroke, target, antiAlias));
+        GrAssert(target->drawState()->getStencil().isDisabled() ||
+                 kNoRestriction_StencilSupport == this->getStencilSupport(path, stroke, target));
+        return this->onDrawPath(path, stroke, target, antiAlias);
     }
 
     /**
-     * Draws the path to the stencil buffer. Assume the writable stencil bits
-     * are already initialized to zero. Fill will always be either
-     * kWinding_PathFill or kEvenOdd_PathFill.
+     * Draws the path to the stencil buffer. Assume the writable stencil bits are already
+     * initialized to zero. The pixels inside the path will have non-zero stencil values afterwards.
      *
-     * Only called if requiresStencilPass returns true for the same combo of
-     * target, path, and fill. Never called with an inverse fill.
-     *
-     * The default implementation assumes the path filling algorithm doesn't
-     * require a separate stencil pass and so crashes.
-     *
+     * @param path                  the path to draw.
+     * @param stroke                the stroke information (width, join, cap)
+     * @param target                target that the path will be rendered to
      */
-    virtual void drawPathToStencil(const SkPath& path,
-                                   GrPathFill fill,
-                                   GrDrawTarget* target) {
-        GrCrash("Unexpected call to drawPathToStencil.");
+    void stencilPath(const SkPath& path, const SkStrokeRec& stroke, GrDrawTarget* target) {
+        GrAssert(kNoSupport_StencilSupport != this->getStencilSupport(path, stroke, target));
+        this->onStencilPath(path, stroke, target);
     }
 
 protected:
+    /**
+     * Subclass overrides if it has any limitations of stenciling support.
+     */
+    virtual StencilSupport onGetStencilSupport(const SkPath&,
+                                               const SkStrokeRec&,
+                                               const GrDrawTarget*) const {
+        return kNoRestriction_StencilSupport;
+    }
+
+    /**
+     * Subclass implementation of drawPath()
+     */
     virtual bool onDrawPath(const SkPath& path,
-                            GrPathFill fill,
-                            const GrVec* translate,
+                            const SkStrokeRec& stroke,
                             GrDrawTarget* target,
-                            GrDrawState::StageMask stageMask,
                             bool antiAlias) = 0;
 
+    /**
+     * Subclass implementation of stencilPath(). Subclass must override iff it ever returns
+     * kStencilOnly in onGetStencilSupport().
+     */
+    virtual void onStencilPath(const SkPath& path,  const SkStrokeRec& stroke, GrDrawTarget* target) {
+        GrDrawTarget::AutoStateRestore asr(target, GrDrawTarget::kPreserve_ASRInit);
+        GrDrawState* drawState = target->drawState();
+        GR_STATIC_CONST_SAME_STENCIL(kIncrementStencil,
+                                     kReplace_StencilOp,
+                                     kReplace_StencilOp,
+                                     kAlways_StencilFunc,
+                                     0xffff,
+                                     0xffff,
+                                     0xffff);
+        drawState->setStencil(kIncrementStencil);
+        drawState->enableState(GrDrawState::kNoColorWrites_StateBit);
+        this->drawPath(path, stroke, target, false);
+    }
+
 private:
 
     typedef GrRefCnt INHERITED;
 };
 
 #endif
-
diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp
index 00f0b81..23f7576 100644
--- a/src/gpu/GrPathRendererChain.cpp
+++ b/src/gpu/GrPathRendererChain.cpp
@@ -13,10 +13,11 @@
 #include "GrDefaultPathRenderer.h"
 #include "GrGpu.h"
 
-GrPathRendererChain::GrPathRendererChain(GrContext* context, UsageFlags flags)
+SK_DEFINE_INST_COUNT(GrPathRendererChain)
+
+GrPathRendererChain::GrPathRendererChain(GrContext* context)
     : fInit(false)
-    , fOwner(context)
-    , fFlags(flags) {
+    , fOwner(context) {
 }
 
 GrPathRendererChain::~GrPathRendererChain() {
@@ -32,14 +33,43 @@
 }
 
 GrPathRenderer* GrPathRendererChain::getPathRenderer(const SkPath& path,
-                                                     GrPathFill fill,
+                                                     const SkStrokeRec& stroke,
                                                      const GrDrawTarget* target,
-                                                     bool antiAlias) {
+                                                     DrawType drawType,
+                                                     StencilSupport* stencilSupport) {
     if (!fInit) {
         this->init();
     }
+    bool antiAlias = (kColorAntiAlias_DrawType == drawType ||
+                      kStencilAndColorAntiAlias_DrawType == drawType);
+
+    GR_STATIC_ASSERT(GrPathRenderer::kNoSupport_StencilSupport <
+                     GrPathRenderer::kStencilOnly_StencilSupport);
+    GR_STATIC_ASSERT(GrPathRenderer::kStencilOnly_StencilSupport <
+                     GrPathRenderer::kNoRestriction_StencilSupport);
+    GrPathRenderer::StencilSupport minStencilSupport;
+    if (kStencilOnly_DrawType == drawType) {
+        minStencilSupport = GrPathRenderer::kStencilOnly_StencilSupport;
+    } else if (kStencilAndColor_DrawType == drawType ||
+               kStencilAndColorAntiAlias_DrawType == drawType) {
+        minStencilSupport = GrPathRenderer::kNoRestriction_StencilSupport;
+    } else {
+        minStencilSupport = GrPathRenderer::kNoSupport_StencilSupport;
+    }
+
+
     for (int i = 0; i < fChain.count(); ++i) {
-        if (fChain[i]->canDrawPath(path, fill, target, antiAlias)) {
+        if (fChain[i]->canDrawPath(path, stroke, target, antiAlias)) {
+            if (GrPathRenderer::kNoSupport_StencilSupport != minStencilSupport) {
+                GrPathRenderer::StencilSupport support = fChain[i]->getStencilSupport(path,
+                                                                                      stroke,
+                                                                                      target);
+                if (support < minStencilSupport) {
+                    continue;
+                } else if (NULL != stencilSupport) {
+                    *stencilSupport = support;
+                }
+            }
             return fChain[i];
         }
     }
@@ -49,9 +79,10 @@
 void GrPathRendererChain::init() {
     GrAssert(!fInit);
     GrGpu* gpu = fOwner->getGpu();
-    bool twoSided = gpu->getCaps().fTwoSidedStencilSupport;
-    bool wrapOp = gpu->getCaps().fStencilWrapOpsSupport;
-    GrPathRenderer::AddPathRenderers(fOwner, fFlags, this);
-    this->addPathRenderer(new GrDefaultPathRenderer(twoSided, wrapOp))->unref();
+    bool twoSided = gpu->getCaps().twoSidedStencilSupport();
+    bool wrapOp = gpu->getCaps().stencilWrapOpsSupport();
+    GrPathRenderer::AddPathRenderers(fOwner, this);
+    this->addPathRenderer(SkNEW_ARGS(GrDefaultPathRenderer,
+                                     (twoSided, wrapOp)))->unref();
     fInit = true;
 }
diff --git a/src/gpu/GrPathRendererChain.h b/src/gpu/GrPathRendererChain.h
deleted file mode 100644
index 54737cb..0000000
--- a/src/gpu/GrPathRendererChain.h
+++ /dev/null
@@ -1,65 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef GrPathRendererChain_DEFINED
-#define GrPathRendererChain_DEFINED
-
-#include "GrDrawTarget.h"
-#include "GrRefCnt.h"
-#include "SkTArray.h"
-
-class GrContext;
-
-class SkPath;
-class GrPathRenderer;
-
-/**
- * Keeps track of a ordered list of path renderers. When a path needs to be
- * drawn this list is scanned to find the most preferred renderer. To add your
- * path renderer to the list implement the GrPathRenderer::AddPathRenderers
- * function.
- */
-class GrPathRendererChain : public SkRefCnt {
-public:
-
-    enum UsageFlags {
-        kNone_UsageFlag      = 0,
-        kNonAAOnly_UsageFlag = 1,
-    };
-
-    GrPathRendererChain(GrContext* context, UsageFlags flags);
-
-    ~GrPathRendererChain();
-
-    // takes a ref and unrefs in destructor
-    GrPathRenderer* addPathRenderer(GrPathRenderer* pr);
-
-    GrPathRenderer* getPathRenderer(const SkPath& path,
-                                    GrPathFill fill,
-                                    const GrDrawTarget* target,
-                                    bool antiAlias);
-
-private:
-
-    GrPathRendererChain();
-
-    void init();
-
-    enum {
-        kPreAllocCount = 8,
-    };
-    bool fInit;
-    GrContext*                                          fOwner;
-    UsageFlags                                          fFlags;
-    SkSTArray<kPreAllocCount, GrPathRenderer*, true>    fChain;
-};
-
-GR_MAKE_BITFIELD_OPS(GrPathRendererChain::UsageFlags)
-
-#endif
diff --git a/src/gpu/GrPathUtils.cpp b/src/gpu/GrPathUtils.cpp
index c32ee8e..d3d784c 100644
--- a/src/gpu/GrPathUtils.cpp
+++ b/src/gpu/GrPathUtils.cpp
@@ -11,40 +11,40 @@
 #include "GrPoint.h"
 #include "SkGeometry.h"
 
-GrScalar GrPathUtils::scaleToleranceToSrc(GrScalar devTol,
-                                          const GrMatrix& viewM,
+SkScalar GrPathUtils::scaleToleranceToSrc(SkScalar devTol,
+                                          const SkMatrix& viewM,
                                           const GrRect& pathBounds) {
     // In order to tesselate the path we get a bound on how much the matrix can
     // stretch when mapping to screen coordinates.
-    GrScalar stretch = viewM.getMaxStretch();
-    GrScalar srcTol = devTol;
+    SkScalar stretch = viewM.getMaxStretch();
+    SkScalar srcTol = devTol;
 
     if (stretch < 0) {
         // take worst case mapRadius amoung four corners.
         // (less than perfect)
         for (int i = 0; i < 4; ++i) {
-            GrMatrix mat;
+            SkMatrix mat;
             mat.setTranslate((i % 2) ? pathBounds.fLeft : pathBounds.fRight,
                              (i < 2) ? pathBounds.fTop : pathBounds.fBottom);
             mat.postConcat(viewM);
             stretch = SkMaxScalar(stretch, mat.mapRadius(SK_Scalar1));
         }
     }
-    srcTol = GrScalarDiv(srcTol, stretch);
+    srcTol = SkScalarDiv(srcTol, stretch);
     return srcTol;
 }
 
 static const int MAX_POINTS_PER_CURVE = 1 << 10;
-static const GrScalar gMinCurveTol = GrFloatToScalar(0.0001f);
+static const SkScalar gMinCurveTol = SkFloatToScalar(0.0001f);
 
 uint32_t GrPathUtils::quadraticPointCount(const GrPoint points[],
-                                          GrScalar tol) {
+                                          SkScalar tol) {
     if (tol < gMinCurveTol) {
         tol = gMinCurveTol;
     }
     GrAssert(tol > 0);
 
-    GrScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
+    SkScalar d = points[1].distanceToLineSegmentBetween(points[0], points[2]);
     if (d <= tol) {
         return 1;
     } else {
@@ -67,7 +67,7 @@
 uint32_t GrPathUtils::generateQuadraticPoints(const GrPoint& p0,
                                               const GrPoint& p1,
                                               const GrPoint& p2,
-                                              GrScalar tolSqd,
+                                              SkScalar tolSqd,
                                               GrPoint** points,
                                               uint32_t pointsLeft) {
     if (pointsLeft < 2 ||
@@ -78,10 +78,10 @@
     }
 
     GrPoint q[] = {
-        { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
-        { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
+        { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
+        { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
     };
-    GrPoint r = { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) };
+    GrPoint r = { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) };
 
     pointsLeft >>= 1;
     uint32_t a = generateQuadraticPoints(p0, q[0], r, tolSqd, points, pointsLeft);
@@ -90,13 +90,13 @@
 }
 
 uint32_t GrPathUtils::cubicPointCount(const GrPoint points[],
-                                           GrScalar tol) {
+                                           SkScalar tol) {
     if (tol < gMinCurveTol) {
         tol = gMinCurveTol;
     }
     GrAssert(tol > 0);
 
-    GrScalar d = GrMax(
+    SkScalar d = GrMax(
         points[1].distanceToLineSegmentBetweenSqd(points[0], points[3]),
         points[2].distanceToLineSegmentBetweenSqd(points[0], points[3]));
     d = SkScalarSqrt(d);
@@ -119,7 +119,7 @@
                                           const GrPoint& p1,
                                           const GrPoint& p2,
                                           const GrPoint& p3,
-                                          GrScalar tolSqd,
+                                          SkScalar tolSqd,
                                           GrPoint** points,
                                           uint32_t pointsLeft) {
     if (pointsLeft < 2 ||
@@ -130,23 +130,23 @@
             return 1;
         }
     GrPoint q[] = {
-        { GrScalarAve(p0.fX, p1.fX), GrScalarAve(p0.fY, p1.fY) },
-        { GrScalarAve(p1.fX, p2.fX), GrScalarAve(p1.fY, p2.fY) },
-        { GrScalarAve(p2.fX, p3.fX), GrScalarAve(p2.fY, p3.fY) }
+        { SkScalarAve(p0.fX, p1.fX), SkScalarAve(p0.fY, p1.fY) },
+        { SkScalarAve(p1.fX, p2.fX), SkScalarAve(p1.fY, p2.fY) },
+        { SkScalarAve(p2.fX, p3.fX), SkScalarAve(p2.fY, p3.fY) }
     };
     GrPoint r[] = {
-        { GrScalarAve(q[0].fX, q[1].fX), GrScalarAve(q[0].fY, q[1].fY) },
-        { GrScalarAve(q[1].fX, q[2].fX), GrScalarAve(q[1].fY, q[2].fY) }
+        { SkScalarAve(q[0].fX, q[1].fX), SkScalarAve(q[0].fY, q[1].fY) },
+        { SkScalarAve(q[1].fX, q[2].fX), SkScalarAve(q[1].fY, q[2].fY) }
     };
-    GrPoint s = { GrScalarAve(r[0].fX, r[1].fX), GrScalarAve(r[0].fY, r[1].fY) };
+    GrPoint s = { SkScalarAve(r[0].fX, r[1].fX), SkScalarAve(r[0].fY, r[1].fY) };
     pointsLeft >>= 1;
     uint32_t a = generateCubicPoints(p0, q[0], r[0], s, tolSqd, points, pointsLeft);
     uint32_t b = generateCubicPoints(s, r[1], q[2], p3, tolSqd, points, pointsLeft);
     return a + b;
 }
 
-int GrPathUtils::worstCasePointCount(const GrPath& path, int* subpaths,
-                                     GrScalar tol) {
+int GrPathUtils::worstCasePointCount(const SkPath& path, int* subpaths,
+                                     SkScalar tol) {
     if (tol < gMinCurveTol) {
         tol = gMinCurveTol;
     }
@@ -187,56 +187,29 @@
     return pointCount;
 }
 
-namespace {
-// The matrix computed for quadDesignSpaceToUVCoordsMatrix should never really
-// have perspective and we really want to avoid perspective matrix muls.
-//  However, the first two entries of the perspective row may be really close to
-// 0 and the third may not be 1 due to a scale on the entire matrix.
-inline void fixup_matrix(GrMatrix* mat) {
-#ifndef SK_SCALAR_IS_FLOAT
-    GrCrash("Expected scalar is float.");
-#endif
-     static const GrScalar gTOL = 1.f / 100.f;
-    GrAssert(GrScalarAbs(mat->get(SkMatrix::kMPersp0)) < gTOL);
-    GrAssert(GrScalarAbs(mat->get(SkMatrix::kMPersp1)) < gTOL);
-    float m33 = mat->get(SkMatrix::kMPersp2);
-    if (1.f != m33) {
-        m33 = 1.f / m33;
-        mat->setAll(m33 * mat->get(SkMatrix::kMScaleX),
-                    m33 * mat->get(SkMatrix::kMSkewX),
-                    m33 * mat->get(SkMatrix::kMTransX),
-                    m33 * mat->get(SkMatrix::kMSkewY),
-                    m33 * mat->get(SkMatrix::kMScaleY),
-                    m33 * mat->get(SkMatrix::kMTransY),
-                    0.f, 0.f, 1.f);
-    } else {
-        mat->setPerspX(0);
-        mat->setPerspY(0);
-    }
-}
-}
-
-// Compute a matrix that goes from the 2d space coordinates to UV space where
-// u^2-v = 0 specifies the quad.
-void GrPathUtils::quadDesignSpaceToUVCoordsMatrix(const SkPoint qPts[3],
-                                                  GrMatrix* matrix) {
+void GrPathUtils::QuadUVMatrix::set(const GrPoint qPts[3]) {
     // can't make this static, no cons :(
     SkMatrix UVpts;
 #ifndef SK_SCALAR_IS_FLOAT
     GrCrash("Expected scalar is float.");
 #endif
+    SkMatrix m;
     // We want M such that M * xy_pt = uv_pt
     // We know M * control_pts = [0  1/2 1]
     //                           [0  0   1]
     //                           [1  1   1]
     // We invert the control pt matrix and post concat to both sides to get M.
-    UVpts.setAll(0,   0.5f,  1.f,
-                 0,   0,     1.f,
-                 1.f, 1.f,   1.f);
-    matrix->setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX,
-                   qPts[0].fY, qPts[1].fY, qPts[2].fY,
-                   1.f,        1.f,        1.f);
-    if (!matrix->invert(matrix)) {
+    UVpts.setAll(0,   SK_ScalarHalf,  SK_Scalar1,
+                 0,               0,  SK_Scalar1,
+                 SkScalarToPersp(SK_Scalar1),
+                 SkScalarToPersp(SK_Scalar1),
+                 SkScalarToPersp(SK_Scalar1));
+    m.setAll(qPts[0].fX, qPts[1].fX, qPts[2].fX,
+             qPts[0].fY, qPts[1].fY, qPts[2].fY,
+             SkScalarToPersp(SK_Scalar1),
+             SkScalarToPersp(SK_Scalar1),
+             SkScalarToPersp(SK_Scalar1));
+    if (!m.invert(&m)) {
         // The quad is degenerate. Hopefully this is rare. Find the pts that are
         // farthest apart to compute a line (unless it is really a pt).
         SkScalar maxD = qPts[0].distanceToSqd(qPts[1]);
@@ -260,77 +233,247 @@
             // case.
             lineVec.setOrthog(lineVec, GrPoint::kLeft_Side);
             lineVec.dot(qPts[0]);
-            matrix->setAll(0, 0, 0,
-                           lineVec.fX, lineVec.fY, -lineVec.dot(qPts[maxEdge]),
-                           0, 0, 1.f);
+            // first row
+            fM[0] = 0;
+            fM[1] = 0;
+            fM[2] = 0;
+            // second row
+            fM[3] = lineVec.fX;
+            fM[4] = lineVec.fY;
+            fM[5] = -lineVec.dot(qPts[maxEdge]);
         } else {
             // It's a point. It should cover zero area. Just set the matrix such
             // that (u, v) will always be far away from the quad.
-            matrix->setAll(0, 0, 100 * SK_Scalar1,
-                           0, 0, 100 * SK_Scalar1,
-                           0, 0, 1.f);
+            fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
+            fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
         }
     } else {
-        matrix->postConcat(UVpts);
-        fixup_matrix(matrix);
+        m.postConcat(UVpts);
+
+        // The matrix should not have perspective.
+        SkDEBUGCODE(static const SkScalar gTOL = SkFloatToScalar(1.f / 100.f));
+        GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp0)) < gTOL);
+        GrAssert(SkScalarAbs(m.get(SkMatrix::kMPersp1)) < gTOL);
+
+        // It may not be normalized to have 1.0 in the bottom right
+        float m33 = m.get(SkMatrix::kMPersp2);
+        if (1.f != m33) {
+            m33 = 1.f / m33;
+            fM[0] = m33 * m.get(SkMatrix::kMScaleX);
+            fM[1] = m33 * m.get(SkMatrix::kMSkewX);
+            fM[2] = m33 * m.get(SkMatrix::kMTransX);
+            fM[3] = m33 * m.get(SkMatrix::kMSkewY);
+            fM[4] = m33 * m.get(SkMatrix::kMScaleY);
+            fM[5] = m33 * m.get(SkMatrix::kMTransY);
+        } else {
+            fM[0] = m.get(SkMatrix::kMScaleX);
+            fM[1] = m.get(SkMatrix::kMSkewX);
+            fM[2] = m.get(SkMatrix::kMTransX);
+            fM[3] = m.get(SkMatrix::kMSkewY);
+            fM[4] = m.get(SkMatrix::kMScaleY);
+            fM[5] = m.get(SkMatrix::kMTransY);
+        }
     }
 }
 
 namespace {
+
+// a is the first control point of the cubic.
+// ab is the vector from a to the second control point.
+// dc is the vector from the fourth to the third control point.
+// d is the fourth control point.
+// p is the candidate quadratic control point.
+// this assumes that the cubic doesn't inflect and is simple
+bool is_point_within_cubic_tangents(const SkPoint& a,
+                                    const SkVector& ab,
+                                    const SkVector& dc,
+                                    const SkPoint& d,
+                                    SkPath::Direction dir,
+                                    const SkPoint p) {
+    SkVector ap = p - a;
+    SkScalar apXab = ap.cross(ab);
+    if (SkPath::kCW_Direction == dir) {
+        if (apXab > 0) {
+            return false;
+        }
+    } else {
+        GrAssert(SkPath::kCCW_Direction == dir);
+        if (apXab < 0) {
+            return false;
+        }
+    }
+
+    SkVector dp = p - d;
+    SkScalar dpXdc = dp.cross(dc);
+    if (SkPath::kCW_Direction == dir) {
+        if (dpXdc < 0) {
+            return false;
+        }
+    } else {
+        GrAssert(SkPath::kCCW_Direction == dir);
+        if (dpXdc > 0) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void convert_noninflect_cubic_to_quads(const SkPoint p[4],
-                                       SkScalar tolScale,
+                                       SkScalar toleranceSqd,
+                                       bool constrainWithinTangents,
+                                       SkPath::Direction dir,
                                        SkTArray<SkPoint, true>* quads,
                                        int sublevel = 0) {
-    SkVector ab = p[1];
-    ab -= p[0];
-    SkVector dc = p[2];
-    dc -= p[3];
 
-    static const SkScalar gLengthScale = 3 * SK_Scalar1 / 2;
-    // base tolerance is 2 pixels in dev coords.
-    const SkScalar distanceSqdTol = SkScalarMul(tolScale, 1 * SK_Scalar1);
+    // Notation: Point a is always p[0]. Point b is p[1] unless p[1] == p[0], in which case it is
+    // p[2]. Point d is always p[3]. Point c is p[2] unless p[2] == p[3], in which case it is p[1].
+
+    SkVector ab = p[1] - p[0];
+    SkVector dc = p[2] - p[3];
+
+    if (ab.isZero()) {
+        if (dc.isZero()) {
+            SkPoint* degQuad = quads->push_back_n(3);
+            degQuad[0] = p[0];
+            degQuad[1] = p[0];
+            degQuad[2] = p[3];
+            return;
+        }
+        ab = p[2] - p[0];
+    }
+    if (dc.isZero()) {
+        dc = p[1] - p[3];
+    }
+
+    // When the ab and cd tangents are nearly parallel with vector from d to a the constraint that
+    // the quad point falls between the tangents becomes hard to enforce and we are likely to hit
+    // the max subdivision count. However, in this case the cubic is approaching a line and the
+    // accuracy of the quad point isn't so important. We check if the two middle cubic control
+    // points are very close to the baseline vector. If so then we just pick quadratic points on the
+    // control polygon.
+
+    if (constrainWithinTangents) {
+        SkVector da = p[0] - p[3];
+        SkScalar invDALengthSqd = da.lengthSqd();
+        if (invDALengthSqd > SK_ScalarNearlyZero) {
+            invDALengthSqd = SkScalarInvert(invDALengthSqd);
+            // cross(ab, da)^2/length(da)^2 == sqd distance from b to line from d to a.
+            // same goed for point c using vector cd.
+            SkScalar detABSqd = ab.cross(da);
+            detABSqd = SkScalarSquare(detABSqd);
+            SkScalar detDCSqd = dc.cross(da);
+            detDCSqd = SkScalarSquare(detDCSqd);
+            if (SkScalarMul(detABSqd, invDALengthSqd) < toleranceSqd &&
+                SkScalarMul(detDCSqd, invDALengthSqd) < toleranceSqd) {
+                SkPoint b = p[0] + ab;
+                SkPoint c = p[3] + dc;
+                SkPoint mid = b + c;
+                mid.scale(SK_ScalarHalf);
+                // Insert two quadratics to cover the case when ab points away from d and/or dc
+                // points away from a.
+                if (SkVector::DotProduct(da, dc) < 0 || SkVector::DotProduct(ab,da) > 0) {
+                    SkPoint* qpts = quads->push_back_n(6);
+                    qpts[0] = p[0];
+                    qpts[1] = b;
+                    qpts[2] = mid;
+                    qpts[3] = mid;
+                    qpts[4] = c;
+                    qpts[5] = p[3];
+                } else {
+                    SkPoint* qpts = quads->push_back_n(3);
+                    qpts[0] = p[0];
+                    qpts[1] = mid;
+                    qpts[2] = p[3];
+                }
+                return;
+            }
+        }
+    }
+
+    static const SkScalar kLengthScale = 3 * SK_Scalar1 / 2;
     static const int kMaxSubdivs = 10;
 
-    ab.scale(gLengthScale);
-    dc.scale(gLengthScale);
+    ab.scale(kLengthScale);
+    dc.scale(kLengthScale);
 
+    // e0 and e1 are extrapolations along vectors ab and dc.
     SkVector c0 = p[0];
     c0 += ab;
     SkVector c1 = p[3];
     c1 += dc;
 
-    SkScalar dSqd = c0.distanceToSqd(c1);
-    if (sublevel > kMaxSubdivs || dSqd <= distanceSqdTol) {
+    SkScalar dSqd = sublevel > kMaxSubdivs ? 0 : c0.distanceToSqd(c1);
+    if (dSqd < toleranceSqd) {
         SkPoint cAvg = c0;
         cAvg += c1;
         cAvg.scale(SK_ScalarHalf);
 
-        SkPoint* pts = quads->push_back_n(3);
-        pts[0] = p[0];
-        pts[1] = cAvg;
-        pts[2] = p[3];
+        bool subdivide = false;
 
-        return;
-    } else {
-        SkPoint choppedPts[7];
-        SkChopCubicAtHalf(p, choppedPts);
-        convert_noninflect_cubic_to_quads(choppedPts + 0, tolScale, 
-                                          quads, sublevel + 1);
-        convert_noninflect_cubic_to_quads(choppedPts + 3, tolScale,
-                                          quads, sublevel + 1);
+        if (constrainWithinTangents &&
+            !is_point_within_cubic_tangents(p[0], ab, dc, p[3], dir, cAvg)) {
+            // choose a new cAvg that is the intersection of the two tangent lines.
+            ab.setOrthog(ab);
+            SkScalar z0 = -ab.dot(p[0]);
+            dc.setOrthog(dc);
+            SkScalar z1 = -dc.dot(p[3]);
+            cAvg.fX = SkScalarMul(ab.fY, z1) - SkScalarMul(z0, dc.fY);
+            cAvg.fY = SkScalarMul(z0, dc.fX) - SkScalarMul(ab.fX, z1);
+            SkScalar z = SkScalarMul(ab.fX, dc.fY) - SkScalarMul(ab.fY, dc.fX);
+            z = SkScalarInvert(z);
+            cAvg.fX *= z;
+            cAvg.fY *= z;
+            if (sublevel <= kMaxSubdivs) {
+                SkScalar d0Sqd = c0.distanceToSqd(cAvg);
+                SkScalar d1Sqd = c1.distanceToSqd(cAvg);
+                // We need to subdivide if d0 + d1 > tolerance but we have the sqd values. We know
+                // the distances and tolerance can't be negative.
+                // (d0 + d1)^2 > toleranceSqd
+                // d0Sqd + 2*d0*d1 + d1Sqd > toleranceSqd
+                SkScalar d0d1 = SkScalarSqrt(SkScalarMul(d0Sqd, d1Sqd));
+                subdivide = 2 * d0d1 + d0Sqd + d1Sqd > toleranceSqd;
+            }
+        }
+        if (!subdivide) {
+            SkPoint* pts = quads->push_back_n(3);
+            pts[0] = p[0];
+            pts[1] = cAvg;
+            pts[2] = p[3];
+            return;
+        }
     }
+    SkPoint choppedPts[7];
+    SkChopCubicAtHalf(p, choppedPts);
+    convert_noninflect_cubic_to_quads(choppedPts + 0,
+                                      toleranceSqd,
+                                      constrainWithinTangents,
+                                      dir,
+                                      quads,
+                                      sublevel + 1);
+    convert_noninflect_cubic_to_quads(choppedPts + 3,
+                                      toleranceSqd,
+                                      constrainWithinTangents,
+                                      dir,
+                                      quads,
+                                      sublevel + 1);
 }
 }
 
 void GrPathUtils::convertCubicToQuads(const GrPoint p[4],
                                       SkScalar tolScale,
+                                      bool constrainWithinTangents,
+                                      SkPath::Direction dir,
                                       SkTArray<SkPoint, true>* quads) {
     SkPoint chopped[10];
     int count = SkChopCubicAtInflections(p, chopped);
 
+    // base tolerance is 1 pixel.
+    static const SkScalar kTolerance = SK_Scalar1;
+    const SkScalar tolSqd = SkScalarSquare(SkScalarMul(tolScale, kTolerance));
+
     for (int i = 0; i < count; ++i) {
         SkPoint* cubic = chopped + 3*i;
-        convert_noninflect_cubic_to_quads(cubic, tolScale, quads);
+        convert_noninflect_cubic_to_quads(cubic, tolSqd, constrainWithinTangents, dir, quads);
     }
 
 }
diff --git a/src/gpu/GrPathUtils.h b/src/gpu/GrPathUtils.h
index df2e16c..2d6045e 100644
--- a/src/gpu/GrPathUtils.h
+++ b/src/gpu/GrPathUtils.h
@@ -10,52 +10,111 @@
 #ifndef GrPathUtils_DEFINED
 #define GrPathUtils_DEFINED
 
-#include "GrMatrix.h"
-#include "GrPath.h"
+#include "GrRect.h"
+#include "SkPath.h"
 #include "SkTArray.h"
 
+class SkMatrix;
+
 /**
  *  Utilities for evaluating paths.
  */
 namespace GrPathUtils {
-    GrScalar scaleToleranceToSrc(GrScalar devTol,
-                                 const GrMatrix& viewM,
+    SkScalar scaleToleranceToSrc(SkScalar devTol,
+                                 const SkMatrix& viewM,
                                  const GrRect& pathBounds);
 
     /// Since we divide by tol if we're computing exact worst-case bounds,
     /// very small tolerances will be increased to gMinCurveTol.
-    int worstCasePointCount(const GrPath&,
+    int worstCasePointCount(const SkPath&,
                             int* subpaths,
-                            GrScalar tol);
+                            SkScalar tol);
+
     /// Since we divide by tol if we're computing exact worst-case bounds,
     /// very small tolerances will be increased to gMinCurveTol.
-    uint32_t quadraticPointCount(const GrPoint points[], GrScalar tol);
+    uint32_t quadraticPointCount(const GrPoint points[], SkScalar tol);
+
     uint32_t generateQuadraticPoints(const GrPoint& p0,
                                      const GrPoint& p1,
                                      const GrPoint& p2,
-                                     GrScalar tolSqd,
+                                     SkScalar tolSqd,
                                      GrPoint** points,
                                      uint32_t pointsLeft);
+
     /// Since we divide by tol if we're computing exact worst-case bounds,
     /// very small tolerances will be increased to gMinCurveTol.
-    uint32_t cubicPointCount(const GrPoint points[], GrScalar tol);
+    uint32_t cubicPointCount(const GrPoint points[], SkScalar tol);
+
     uint32_t generateCubicPoints(const GrPoint& p0,
                                  const GrPoint& p1,
                                  const GrPoint& p2,
                                  const GrPoint& p3,
-                                 GrScalar tolSqd,
+                                 SkScalar tolSqd,
                                  GrPoint** points,
                                  uint32_t pointsLeft);
-    // Compute a matrix that goes from the 2d space coordinates to UV space
-    // where u^2-v = 0 specifies the quad.
-    void quadDesignSpaceToUVCoordsMatrix(const GrPoint qPts[3],
-                                         GrMatrix* matrix);
+
+    // A 2x3 matrix that goes from the 2d space coordinates to UV space where
+    // u^2-v = 0 specifies the quad. The matrix is determined by the control
+    // points of the quadratic.
+    class QuadUVMatrix {
+    public:
+        QuadUVMatrix() {};
+        // Initialize the matrix from the control pts
+        QuadUVMatrix(const GrPoint controlPts[3]) { this->set(controlPts); }
+        void set(const GrPoint controlPts[3]);
+
+        /**
+         * Applies the matrix to vertex positions to compute UV coords. This
+         * has been templated so that the compiler can easliy unroll the loop
+         * and reorder to avoid stalling for loads. The assumption is that a
+         * path renderer will have a small fixed number of vertices that it
+         * uploads for each quad.
+         *
+         * N is the number of vertices.
+         * STRIDE is the size of each vertex.
+         * UV_OFFSET is the offset of the UV values within each vertex.
+         * vertices is a pointer to the first vertex.
+         */
+        template <int N, size_t STRIDE, size_t UV_OFFSET>
+        void apply(const void* vertices) {
+            intptr_t xyPtr = reinterpret_cast<intptr_t>(vertices);
+            intptr_t uvPtr = reinterpret_cast<intptr_t>(vertices) + UV_OFFSET;
+            float sx = fM[0];
+            float kx = fM[1];
+            float tx = fM[2];
+            float ky = fM[3];
+            float sy = fM[4];
+            float ty = fM[5];
+            for (int i = 0; i < N; ++i) {
+                const GrPoint* xy = reinterpret_cast<const GrPoint*>(xyPtr);
+                GrPoint* uv = reinterpret_cast<GrPoint*>(uvPtr);
+                uv->fX = sx * xy->fX + kx * xy->fY + tx;
+                uv->fY = ky * xy->fX + sy * xy->fY + ty;
+                xyPtr += STRIDE;
+                uvPtr += STRIDE;
+            }
+        }
+    private:
+        float fM[6];
+    };
+
+
     // Converts a cubic into a sequence of quads. If working in device space
     // use tolScale = 1, otherwise set based on stretchiness of the matrix. The
     // result is sets of 3 points in quads (TODO: share endpoints in returned
     // array)
+    // When we approximate a cubic {a,b,c,d} with a quadratic we may have to
+    // ensure that the new control point lies between the lines ab and cd. The
+    // convex path renderer requires this. It starts with a path where all the
+    // control points taken together form a convex polygon. It relies on this
+    // property and the quadratic approximation of cubics step cannot alter it.
+    // Setting constrainWithinTangents to true enforces this property. When this
+    // is true the cubic must be simple and dir must specify the orientation of
+    // the cubic. Otherwise, dir is ignored.
     void convertCubicToQuads(const GrPoint p[4],
                              SkScalar tolScale,
+                             bool constrainWithinTangents,
+                             SkPath::Direction dir,
                              SkTArray<SkPoint, true>* quads);
 };
 #endif
diff --git a/src/gpu/GrPlotMgr.h b/src/gpu/GrPlotMgr.h
index 0e631a9..4f79a21 100644
--- a/src/gpu/GrPlotMgr.h
+++ b/src/gpu/GrPlotMgr.h
@@ -22,7 +22,7 @@
         if (needed <= sizeof(fStorage)) {
             fBusy = fStorage;
         } else {
-            fBusy = new char[needed];
+            fBusy = SkNEW_ARRAY(char, needed);
         }
         this->reset();
     }
@@ -32,7 +32,7 @@
             delete[] fBusy;
         }
     }
-    
+
     void reset() {
         Gr_bzero(fBusy, fDim.fX * fDim.fY);
     }
@@ -74,4 +74,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrPrintf_printf.cpp b/src/gpu/GrPrintf_printf.cpp
deleted file mode 100644
index 909a4f0..0000000
--- a/src/gpu/GrPrintf_printf.cpp
+++ /dev/null
@@ -1,29 +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 "GrTypes.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-void GrPrintf(const char format[], ...) {
-    const size_t MAX_BUFFER_SIZE = 2048;
-
-    char buffer[MAX_BUFFER_SIZE + 1];
-    va_list args;
-
-    va_start(args, format);
-    vsnprintf(buffer, MAX_BUFFER_SIZE, format, args);
-    va_end(args);
-
-    printf("%s", buffer);
-}
-
-
diff --git a/src/gpu/GrPrintf_skia.cpp b/src/gpu/GrPrintf_skia.cpp
deleted file mode 100644
index 9bab419..0000000
--- a/src/gpu/GrPrintf_skia.cpp
+++ /dev/null
@@ -1,32 +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 "GrTypes.h"
-
-#include <stdarg.h>
-#include <stdio.h>
-
-#include "SkTypes.h"
-
-void GrPrintf(const char format[], ...) {
-    const size_t MAX_BUFFER_SIZE = 2048;
-
-    char buffer[MAX_BUFFER_SIZE + 1];
-    va_list args;
-
-    va_start(args, format);
-    vsnprintf(buffer, MAX_BUFFER_SIZE, format, args);
-    va_end(args);
-
-    // skia has already mapped this to do the "right thing"
-    SkDebugf("%s", buffer);
-}
-
-
diff --git a/src/gpu/GrRandom.h b/src/gpu/GrRandom.h
deleted file mode 100644
index c98a8fb..0000000
--- a/src/gpu/GrRandom.h
+++ /dev/null
@@ -1,55 +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 GrRandom_DEFINED
-#define GrRandom_DEFINED
-
-class GrRandom {
-public:
-    GrRandom() : fSeed(0) {}
-    GrRandom(uint32_t seed) : fSeed(seed) {}
-
-    uint32_t seed() const { return fSeed; }
-
-    uint32_t nextU() {
-        fSeed = fSeed * kMUL + kADD;
-        return fSeed;
-    }
-
-    int32_t nextS() { return (int32_t)this->nextU(); }
-
-    /**
-     *  Returns value [0...1) as a float
-     */
-    float nextF() {
-        // const is 1 / (2^32 - 1)
-        return (float)(this->nextU() * 2.32830644e-10);
-    }
-
-    /**
-     *  Returns value [min...max) as a float
-     */
-    float nextF(float min, float max) {
-        return min + this->nextF() * (max - min);
-    }
-
-private:
-    /*
-     *  These constants taken from "Numerical Recipes in C", reprinted 1999
-     */
-    enum {
-        kMUL = 1664525,
-        kADD = 1013904223
-    };
-    uint32_t    fSeed;
-};
-
-#endif
-
diff --git a/src/gpu/GrRectanizer.cpp b/src/gpu/GrRectanizer.cpp
index 628b890..75ff92c 100644
--- a/src/gpu/GrRectanizer.cpp
+++ b/src/gpu/GrRectanizer.cpp
@@ -38,7 +38,7 @@
     struct Row {
         GrIPoint16  fLoc;
         int         fRowHeight;
-        
+
         bool canAddWidth(int width, int containerWidth) const {
             return fLoc.fX + width <= containerWidth;
         }
@@ -48,7 +48,7 @@
 
     static int HeightToRowIndex(int height) {
         GrAssert(height >= MIN_HEIGHT_POW2);
-        return 32 - Gr_clz(height - 1);
+        return 32 - SkCLZ(height - 1);
     }
 
     int fNextStripY;
@@ -117,7 +117,5 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 GrRectanizer* GrRectanizer::Factory(int width, int height) {
-    return new GrRectanizerPow2(width, height);
+    return SkNEW_ARGS(GrRectanizerPow2, (width, height));
 }
-
-
diff --git a/src/gpu/GrRectanizer.h b/src/gpu/GrRectanizer.h
index d1f5033..44d14a1 100644
--- a/src/gpu/GrRectanizer.h
+++ b/src/gpu/GrRectanizer.h
@@ -12,7 +12,6 @@
 #define GrRectanizer_DEFINED
 
 #include "GrRect.h"
-#include "GrTDArray.h"
 
 class GrRectanizerPurgeListener {
 public:
@@ -53,5 +52,3 @@
 };
 
 #endif
-
-
diff --git a/src/gpu/GrRectanizer_fifo.cpp b/src/gpu/GrRectanizer_fifo.cpp
index 3bfc46f..b6ef1d9 100644
--- a/src/gpu/GrRectanizer_fifo.cpp
+++ b/src/gpu/GrRectanizer_fifo.cpp
@@ -20,44 +20,44 @@
         fAreaSoFar = 0;
         Gr_bzero(fRows, sizeof(fRows));
     }
-    
+
     virtual ~GrRectanizerFIFO() {
     }
-    
+
     virtual bool addRect(int w, int h, GrIPoint16* loc);
-    
+
     virtual float percentFull() const {
         return fAreaSoFar / ((float)this->width() * this->height());
     }
-    
+
     virtual int stripToPurge(int height) const { return -1; }
     virtual void purgeStripAtY(int yCoord) { }
-    
+
     ///////////////////////////////////////////////////////////////////////////
-    
+
     struct Row {
         GrIPoint16  fLoc;
         int         fRowHeight;
-        
+
         bool canAddWidth(int width, int containerWidth) const {
             return fLoc.fX + width <= containerWidth;
         }
     };
-    
+
     Row fRows[16];
-    
+
     static int HeightToRowIndex(int height) {
         GrAssert(height >= MIN_HEIGHT_POW2);
         return 32 - Gr_clz(height - 1);
     }
-    
+
     int fNextStripY;
     int32_t fAreaSoFar;
-    
+
     bool canAddStrip(int height) const {
         return fNextStripY + height <= this->height();
     }
-    
+
     void initRow(Row* row, int rowHeight) {
         row->fLoc.set(0, fNextStripY);
         row->fRowHeight = rowHeight;
@@ -70,9 +70,9 @@
         (unsigned)height > (unsigned)this->height()) {
         return false;
     }
-    
+
     int32_t area = width * height;
-    
+
     /*
      We use bsearch, but there may be more than one row with the same height,
      so we actually search for height-1, which can only be a pow2 itself if
@@ -82,10 +82,10 @@
     if (height < MIN_HEIGHT_POW2) {
         height = MIN_HEIGHT_POW2;
     }
-    
+
     Row* row = &fRows[HeightToRowIndex(height)];
     GrAssert(row->fRowHeight == 0 || row->fRowHeight == height);
-    
+
     if (0 == row->fRowHeight) {
         if (!this->canAddStrip(height)) {
             return false;
@@ -101,12 +101,12 @@
             this->initRow(row, height);
         }
     }
-    
+
     GrAssert(row->fRowHeight == height);
     GrAssert(row->canAddWidth(width, this->width()));
     *loc = row->fLoc;
     row->fLoc.fX += width;
-    
+
     GrAssert(row->fLoc.fX <= this->width());
     GrAssert(row->fLoc.fY <= this->height());
     GrAssert(fNextStripY <= this->height());
@@ -117,7 +117,5 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 GrRectanizer* GrRectanizer::Factory(int width, int height) {
-    return new GrRectanizerFIFO(width, height);
+    return SkNEW_ARGS(GrRectanizerFIFO, (width, height));
 }
-
-
diff --git a/src/gpu/GrRedBlackTree.h b/src/gpu/GrRedBlackTree.h
index da5ae3e..2ccf77f 100644
--- a/src/gpu/GrRedBlackTree.h
+++ b/src/gpu/GrRedBlackTree.h
@@ -343,7 +343,7 @@
 
     ++fCount;
 
-    Node* x = new Node;
+    Node* x = SkNEW(Node);
     x->fChildren[kLeft_Child] = NULL;
     x->fChildren[kRight_Child] = NULL;
     x->fItem = t;
@@ -943,20 +943,20 @@
 }
 #endif
 
-#include "GrRandom.h"
+#include "SkRandom.h"
 
 template <typename T, typename C>
 void GrRedBlackTree<T,C>::UnitTest() {
     GrRedBlackTree<int> tree;
     typedef GrRedBlackTree<int>::Iter iter;
 
-    GrRandom r;
+    SkRandom r;
 
     int count[100] = {0};
     // add 10K ints
     for (int i = 0; i < 10000; ++i) {
         int x = r.nextU()%100;
-        Iter xi = tree.insert(x);
+        SkDEBUGCODE(Iter xi = ) tree.insert(x);
         GrAssert(*xi == x);
         ++count[x];
     }
diff --git a/src/gpu/GrReducedClip.cpp b/src/gpu/GrReducedClip.cpp
new file mode 100644
index 0000000..da42e8c
--- /dev/null
+++ b/src/gpu/GrReducedClip.cpp
@@ -0,0 +1,415 @@
+
+/*
+ * 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 "GrReducedClip.h"
+
+typedef SkClipStack::Element Element;
+////////////////////////////////////////////////////////////////////////////////
+
+namespace GrReducedClip {
+
+// helper function
+void reduced_stack_walker(const SkClipStack& stack,
+                          const SkRect& queryBounds,
+                          ElementList* result,
+                          InitialState* initialState,
+                          bool* requiresAA);
+
+/*
+There are plenty of optimizations that could be added here. Maybe flips could be folded into
+earlier operations. Or would inserting flips and reversing earlier ops ever be a win? Perhaps
+for the case where the bounds are kInsideOut_BoundsType. We could restrict earlier operations
+based on later intersect operations, and perhaps remove intersect-rects. We could optionally
+take a rect in case the caller knows a bound on what is to be drawn through this clip.
+*/
+void ReduceClipStack(const SkClipStack& stack,
+                     const SkIRect& queryBounds,
+                     ElementList* result,
+                     InitialState* initialState,
+                     SkIRect* tighterBounds,
+                     bool* requiresAA) {
+    result->reset();
+
+    if (stack.isWideOpen()) {
+        *initialState = kAllIn_InitialState;
+        return;
+    }
+
+
+    // We initially look at whether the bounds alone is sufficient. We also use the stack bounds to
+    // attempt to compute the tighterBounds.
+
+    SkClipStack::BoundsType stackBoundsType;
+    SkRect stackBounds;
+    bool iior;
+    stack.getBounds(&stackBounds, &stackBoundsType, &iior);
+
+    const SkIRect* bounds = &queryBounds;
+
+    SkRect scalarQueryBounds = SkRect::MakeFromIRect(queryBounds);
+
+    if (iior) {
+        SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
+        SkRect isectRect;
+        if (stackBounds.contains(scalarQueryBounds)) {
+            *initialState = kAllIn_InitialState;
+            if (NULL != tighterBounds) {
+                *tighterBounds = queryBounds;
+            }
+            if (NULL != requiresAA) {
+               *requiresAA = false;
+            }
+        } else if (isectRect.intersect(stackBounds, scalarQueryBounds)) {
+            if (NULL != tighterBounds) {
+                isectRect.roundOut(tighterBounds);
+                SkRect scalarTighterBounds = SkRect::MakeFromIRect(*tighterBounds);
+                if (scalarTighterBounds == isectRect) {
+                    // the round-out didn't add any area outside the clip rect.
+                    *requiresAA = false;
+                    *initialState = kAllIn_InitialState;
+                    return;
+                }
+                *initialState = kAllOut_InitialState;
+                // iior should only be true if aa/non-aa status matches among all elements.
+                SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
+                bool doAA = iter.prev()->isAA();
+                SkNEW_INSERT_AT_LLIST_HEAD(result, Element, (isectRect, SkRegion::kReplace_Op, doAA));
+                if (NULL != requiresAA) {
+                    *requiresAA = doAA;
+                }
+            }
+        } else {
+            *initialState = kAllOut_InitialState;
+             if (NULL != requiresAA) {
+                *requiresAA = false;
+             }
+        }
+        return;
+    } else {
+        if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
+            if (!SkRect::Intersects(stackBounds, scalarQueryBounds)) {
+                *initialState = kAllOut_InitialState;
+                if (NULL != requiresAA) {
+                   *requiresAA = false;
+                }
+                return;
+            }
+            if (NULL != tighterBounds) {
+                SkIRect stackIBounds;
+                stackBounds.roundOut(&stackIBounds);
+                tighterBounds->intersect(queryBounds, stackIBounds);
+                bounds = tighterBounds;
+            }
+        } else {
+            if (stackBounds.contains(scalarQueryBounds)) {
+                *initialState = kAllOut_InitialState;
+                if (NULL != requiresAA) {
+                   *requiresAA = false;
+                }
+                return;
+            }
+            if (NULL != tighterBounds) {
+                *tighterBounds = queryBounds;
+            }
+        }
+    }
+
+    SkRect scalarBounds = SkRect::MakeFromIRect(*bounds);
+
+    // Now that we have determined the bounds to use and filtered out the trivial cases, call the
+    // helper that actually walks the stack.
+    reduced_stack_walker(stack, scalarBounds, result, initialState, requiresAA);
+}
+
+void reduced_stack_walker(const SkClipStack& stack,
+                          const SkRect& queryBounds,
+                          ElementList* result,
+                          InitialState* initialState,
+                          bool* requiresAA) {
+
+    // walk backwards until we get to:
+    //  a) the beginning
+    //  b) an operation that is known to make the bounds all inside/outside
+    //  c) a replace operation
+
+    static const InitialState kUnknown_InitialState = static_cast<InitialState>(-1);
+    *initialState = kUnknown_InitialState;
+
+    // During our backwards walk, track whether we've seen ops that either grow or shrink the clip.
+    // TODO: track these per saved clip so that we can consider them on the forward pass.
+    bool embiggens = false;
+    bool emsmallens = false;
+
+    SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
+    int numAAElements = 0;
+    while ((kUnknown_InitialState == *initialState)) {
+        const Element* element = iter.prev();
+        if (NULL == element) {
+            *initialState = kAllIn_InitialState;
+            break;
+        }
+        if (SkClipStack::kEmptyGenID == element->getGenID()) {
+            *initialState = kAllOut_InitialState;
+            break;
+        }
+        if (SkClipStack::kWideOpenGenID == element->getGenID()) {
+            *initialState = kAllIn_InitialState;
+            break;
+        }
+
+        bool skippable = false;
+        bool isFlip = false; // does this op just flip the in/out state of every point in the bounds
+
+        switch (element->getOp()) {
+            case SkRegion::kDifference_Op:
+                // check if the shape subtracted either contains the entire bounds (and makes
+                // the clip empty) or is outside the bounds and therefore can be skipped.
+                if (element->isInverseFilled()) {
+                    if (element->contains(queryBounds)) {
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    }
+                } else {
+                    if (element->contains(queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        skippable = true;
+                    }
+                }
+                if (!skippable) {
+                    emsmallens = true;
+                }
+                break;
+            case SkRegion::kIntersect_Op:
+                // check if the shape intersected contains the entire bounds and therefore can
+                // be skipped or it is outside the entire bounds and therefore makes the clip
+                // empty.
+                if (element->isInverseFilled()) {
+                    if (element->contains(queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        skippable = true;
+                    }
+                } else {
+                    if (element->contains(queryBounds)) {
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    }
+                }
+                if (!skippable) {
+                    emsmallens = true;
+                }
+                break;
+            case SkRegion::kUnion_Op:
+                // If the union-ed shape contains the entire bounds then after this element
+                // the bounds is entirely inside the clip. If the union-ed shape is outside the
+                // bounds then this op can be skipped.
+                if (element->isInverseFilled()) {
+                    if (element->contains(queryBounds)) {
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        *initialState = kAllIn_InitialState;
+                        skippable = true;
+                    }
+                } else {
+                    if (element->contains(queryBounds)) {
+                        *initialState = kAllIn_InitialState;
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        skippable = true;
+                    }
+                }
+                if (!skippable) {
+                    embiggens = true;
+                }
+                break;
+            case SkRegion::kXOR_Op:
+                // If the bounds is entirely inside the shape being xor-ed then the effect is
+                // to flip the inside/outside state of every point in the bounds. We may be
+                // able to take advantage of this in the forward pass. If the xor-ed shape
+                // doesn't intersect the bounds then it can be skipped.
+                if (element->isInverseFilled()) {
+                    if (element->contains(queryBounds)) {
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        isFlip = true;
+                    }
+                } else {
+                    if (element->contains(queryBounds)) {
+                        isFlip = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        skippable = true;
+                    }
+                }
+                if (!skippable) {
+                    emsmallens = embiggens = true;
+                }
+                break;
+            case SkRegion::kReverseDifference_Op:
+                // When the bounds is entirely within the rev-diff shape then this behaves like xor
+                // and reverses every point inside the bounds. If the shape is completely outside
+                // the bounds then we know after this element is applied that the bounds will be
+                // all outside the current clip.B
+                if (element->isInverseFilled()) {
+                    if (element->contains(queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        isFlip = true;
+                    }
+                } else {
+                    if (element->contains(queryBounds)) {
+                        isFlip = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    }
+                }
+                if (!skippable) {
+                    emsmallens = embiggens = true;
+                }
+                break;
+            case SkRegion::kReplace_Op:
+                // Replace will always terminate our walk. We will either begin the forward walk
+                // at the replace op or detect here than the shape is either completely inside
+                // or completely outside the bounds. In this latter case it can be skipped by
+                // setting the correct value for initialState.
+                if (element->isInverseFilled()) {
+                    if (element->contains(queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        *initialState = kAllIn_InitialState;
+                        skippable = true;
+                    }
+                } else {
+                    if (element->contains(queryBounds)) {
+                        *initialState = kAllIn_InitialState;
+                        skippable = true;
+                    } else if (!SkRect::Intersects(element->getBounds(), queryBounds)) {
+                        *initialState = kAllOut_InitialState;
+                        skippable = true;
+                    }
+                }
+                if (!skippable) {
+                    *initialState = kAllOut_InitialState;
+                    embiggens = emsmallens = true;
+                }
+                break;
+            default:
+                SkDEBUGFAIL("Unexpected op.");
+                break;
+        }
+        if (!skippable) {
+            // if it is a flip, change it to a bounds-filling rect
+            if (isFlip) {
+                SkASSERT(SkRegion::kXOR_Op == element->getOp() ||
+                         SkRegion::kReverseDifference_Op == element->getOp());
+                SkNEW_INSERT_AT_LLIST_HEAD(result,
+                                           Element,
+                                           (queryBounds, SkRegion::kReverseDifference_Op, false));
+            } else {
+                Element* newElement = result->addToHead(*element);
+                if (newElement->isAA()) {
+                    ++numAAElements;
+                }
+                // Intersecting an inverse shape is the same as differencing the non-inverse shape.
+                // Replacing with an inverse shape is the same as setting initialState=kAllIn and
+                // differencing the non-inverse shape.
+                bool isReplace = SkRegion::kReplace_Op == newElement->getOp();
+                if (newElement->isInverseFilled() &&
+                    (SkRegion::kIntersect_Op == newElement->getOp() || isReplace)) {
+                    newElement->invertShapeFillType();
+                    newElement->setOp(SkRegion::kDifference_Op);
+                    if (isReplace) {
+                        SkASSERT(kAllOut_InitialState == *initialState);
+                        *initialState = kAllIn_InitialState;
+                    }
+                }
+            }
+        }
+    }
+
+    if ((kAllOut_InitialState == *initialState && !embiggens) ||
+        (kAllIn_InitialState == *initialState && !emsmallens)) {
+        result->reset();
+    } else {
+        Element* element = result->headIter().get();
+        while (NULL != element) {
+            bool skippable = false;
+            switch (element->getOp()) {
+                case SkRegion::kDifference_Op:
+                    // subtracting from the empty set yields the empty set.
+                    skippable = kAllOut_InitialState == *initialState;
+                    break;
+                case SkRegion::kIntersect_Op:
+                    // intersecting with the empty set yields the empty set
+                    skippable = kAllOut_InitialState == *initialState;
+                    break;
+                case SkRegion::kUnion_Op:
+                    if (kAllIn_InitialState == *initialState) {
+                        // unioning the infinite plane with anything is a no-op.
+                        skippable = true;
+                    } else {
+                        // unioning the empty set with a shape is the shape.
+                        element->setOp(SkRegion::kReplace_Op);
+                    }
+                    break;
+                case SkRegion::kXOR_Op:
+                    if (kAllOut_InitialState == *initialState) {
+                        // xor could be changed to diff in the kAllIn case, not sure it's a win.
+                        element->setOp(SkRegion::kReplace_Op);
+                    }
+                    break;
+                case SkRegion::kReverseDifference_Op:
+                    if (kAllIn_InitialState == *initialState) {
+                        // subtracting the whole plane will yield the empty set.
+                        skippable = true;
+                        *initialState = kAllOut_InitialState;
+                    } else {
+                        // this picks up flips inserted in the backwards pass.
+                        skippable = element->isInverseFilled() ?
+                            !SkRect::Intersects(element->getBounds(), queryBounds) :
+                            element->contains(queryBounds);
+                        if (skippable) {
+                            *initialState = kAllIn_InitialState;
+                        } else {
+                            element->setOp(SkRegion::kReplace_Op);
+                        }
+                    }
+                    break;
+                case SkRegion::kReplace_Op:
+                    skippable = false; // we would have skipped it in the backwards walk if we
+                                       // could've.
+                    break;
+                default:
+                    SkDEBUGFAIL("Unexpected op.");
+                    break;
+            }
+            if (!skippable) {
+                break;
+            } else {
+                if (element->isAA()) {
+                    --numAAElements;
+                }
+                result->popHead();
+                element = result->headIter().get();
+            }
+        }
+    }
+    if (NULL != requiresAA) {
+        *requiresAA = numAAElements > 0;
+    }
+}
+} // namespace GrReducedClip
diff --git a/src/gpu/GrReducedClip.h b/src/gpu/GrReducedClip.h
new file mode 100644
index 0000000..abfc244
--- /dev/null
+++ b/src/gpu/GrReducedClip.h
@@ -0,0 +1,40 @@
+
+/*
+ * 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 "SkClipStack.h"
+#include "SkTLList.h"
+
+namespace GrReducedClip {
+
+typedef SkTLList<SkClipStack::Element> ElementList;
+
+enum InitialState {
+    kAllIn_InitialState,
+    kAllOut_InitialState,
+};
+
+/**
+ * This function takes a clip stack and a query rectangle and it produces a reduced set of
+ * SkClipStack::Elements that are equivalent to applying the full stack to the rectangle. The
+ * initial state of the query rectangle before the first clip element is applied is returned via
+ * initialState. Optionally, the caller can request a tighter bounds on the clip be returned via
+ * tighterBounds. If not NULL, tighterBounds will always be contained by queryBounds after return.
+ * If tighterBounds is specified then it is assumed that the caller will implicitly clip against it.
+ * If the caller specifies non-NULL for requiresAA then it will indicate whether anti-aliasing is
+ * required to process any of the elements in the result.
+ *
+ * This may become a member function of SkClipStack when its interface is determined to be stable.
+ */
+void ReduceClipStack(const SkClipStack& stack,
+                     const SkIRect& queryBounds,
+                     ElementList* result,
+                     InitialState* initialState,
+                     SkIRect* tighterBounds = NULL,
+                     bool* requiresAA = NULL);
+
+} // namespace GrReducedClip
diff --git a/src/gpu/GrRenderTarget.cpp b/src/gpu/GrRenderTarget.cpp
index ed1a018..5630d58 100644
--- a/src/gpu/GrRenderTarget.cpp
+++ b/src/gpu/GrRenderTarget.cpp
@@ -13,32 +13,38 @@
 #include "GrGpu.h"
 #include "GrStencilBuffer.h"
 
+SK_DEFINE_INST_COUNT(GrRenderTarget)
+
 bool GrRenderTarget::readPixels(int left, int top, int width, int height,
-                                GrPixelConfig config, void* buffer,
-                                size_t rowBytes) {
+                                GrPixelConfig config,
+                                void* buffer,
+                                size_t rowBytes,
+                                uint32_t pixelOpsFlags) {
     // go through context so that all necessary flushing occurs
     GrContext* context = this->getContext();
     if (NULL == context) {
         return false;
     }
     return context->readRenderTargetPixels(this,
-                                           left, top,
-                                           width, height,
-                                           config, buffer, rowBytes);
+                                           left, top, width, height,
+                                           config, buffer, rowBytes,
+                                           pixelOpsFlags);
 }
 
 void GrRenderTarget::writePixels(int left, int top, int width, int height,
-                                 GrPixelConfig config, const void* buffer,
-                                 size_t rowBytes) {
+                                 GrPixelConfig config,
+                                 const void* buffer,
+                                 size_t rowBytes,
+                                 uint32_t pixelOpsFlags) {
     // go through context so that all necessary flushing occurs
     GrContext* context = this->getContext();
     if (NULL == context) {
         return;
     }
     context->writeRenderTargetPixels(this,
-                                     left, top,
-                                     width, height,
-                                     config, buffer, rowBytes);
+                                     left, top, width, height,
+                                     config, buffer, rowBytes,
+                                     pixelOpsFlags);
 }
 
 void GrRenderTarget::resolve() {
@@ -52,15 +58,15 @@
 
 size_t GrRenderTarget::sizeInBytes() const {
     int colorBits;
-    if (kUnknown_GrPixelConfig == fConfig) {
+    if (kUnknown_GrPixelConfig == fDesc.fConfig) {
         colorBits = 32; // don't know, make a guess
     } else {
-        colorBits = GrBytesPerPixel(fConfig);
+        colorBits = GrBytesPerPixel(fDesc.fConfig);
     }
-    uint64_t size = fWidth;
-    size *= fHeight;
+    uint64_t size = fDesc.fWidth;
+    size *= fDesc.fHeight;
     size *= colorBits;
-    size *= GrMax(1,fSampleCnt);
+    size *= GrMax(1, fDesc.fSampleCnt);
     return (size_t)(size / 8);
 }
 
@@ -89,13 +95,38 @@
 }
 
 void GrRenderTarget::setStencilBuffer(GrStencilBuffer* stencilBuffer) {
-    if (NULL != fStencilBuffer) {
-        fStencilBuffer->wasDetachedFromRenderTarget(this);
-        fStencilBuffer->unref();
+    if (stencilBuffer == fStencilBuffer) {
+        return;
     }
-    fStencilBuffer = stencilBuffer;
+
     if (NULL != fStencilBuffer) {
-        fStencilBuffer->wasAttachedToRenderTarget(this);
+        fStencilBuffer->unref();
+
+        GrContext* context = this->getContext();
+        if (NULL != context) {
+            context->purgeCache();
+        }
+
+        if (NULL != context) {
+            context->purgeCache();
+        }
+    }
+
+    fStencilBuffer = stencilBuffer;
+
+    if (NULL != fStencilBuffer) {
         fStencilBuffer->ref();
     }
 }
+
+void GrRenderTarget::onRelease() {
+    this->setStencilBuffer(NULL);
+
+    INHERITED::onRelease();
+}
+
+void GrRenderTarget::onAbandon() {
+    this->setStencilBuffer(NULL);
+
+    INHERITED::onAbandon();
+}
diff --git a/src/gpu/GrResource.cpp b/src/gpu/GrResource.cpp
index 63d2e7b..8fb21e8 100644
--- a/src/gpu/GrResource.cpp
+++ b/src/gpu/GrResource.cpp
@@ -10,13 +10,26 @@
 #include "GrResource.h"
 #include "GrGpu.h"
 
-GrResource::GrResource(GrGpu* gpu) {
-    fGpu        = gpu;
-    fNext       = NULL;
-    fPrevious   = NULL;
+SK_DEFINE_INST_COUNT(GrResource)
+
+GrResource::GrResource(GrGpu* gpu, bool isWrapped) {
+    fGpu              = gpu;
+    fCacheEntry       = NULL;
+    fDeferredRefCount = 0;
+    if (isWrapped) {
+        fFlags = kWrapped_Flag;
+    } else {
+        fFlags = 0;
+    }
     fGpu->insertResource(this);
 }
 
+GrResource::~GrResource() {
+    // subclass should have released this.
+    GrAssert(0 == fDeferredRefCount);
+    GrAssert(!this->isValid());
+}
+
 void GrResource::release() {
     if (NULL != fGpu) {
         this->onRelease();
@@ -48,4 +61,3 @@
         return NULL;
     }
 }
-
diff --git a/src/gpu/GrResourceCache.cpp b/src/gpu/GrResourceCache.cpp
index ba6452d..45b999f 100644
--- a/src/gpu/GrResourceCache.cpp
+++ b/src/gpu/GrResourceCache.cpp
@@ -11,23 +11,36 @@
 #include "GrResourceCache.h"
 #include "GrResource.h"
 
+
+GrResourceKey::ResourceType GrResourceKey::GenerateResourceType() {
+    static int32_t gNextType = 0;
+
+    int32_t type = sk_atomic_inc(&gNextType);
+    if (type >= (1 << 8 * sizeof(ResourceType))) {
+        GrCrash("Too many Resource Types");
+    }
+
+    return static_cast<ResourceType>(type);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 GrResourceEntry::GrResourceEntry(const GrResourceKey& key, GrResource* resource)
         : fKey(key), fResource(resource) {
-    fLockCount = 0;
-    fPrev = fNext = NULL;
-
     // we assume ownership of the resource, and will unref it when we die
     GrAssert(resource);
+    resource->ref();
 }
 
 GrResourceEntry::~GrResourceEntry() {
+    fResource->setCacheEntry(NULL);
     fResource->unref();
 }
 
 #if GR_DEBUG
 void GrResourceEntry::validate() const {
-    GrAssert(fLockCount >= 0);
     GrAssert(fResource);
+    GrAssert(fResource->getCacheEntry() == this);
     fResource->validate();
 }
 #endif
@@ -37,20 +50,38 @@
 GrResourceCache::GrResourceCache(int maxCount, size_t maxBytes) :
         fMaxCount(maxCount),
         fMaxBytes(maxBytes) {
-    fEntryCount          = 0;
-    fUnlockedEntryCount  = 0;
-    fEntryBytes          = 0;
-    fClientDetachedCount = 0;
-    fClientDetachedBytes = 0;
+#if GR_CACHE_STATS
+    fHighWaterEntryCount          = 0;
+    fHighWaterEntryBytes          = 0;
+    fHighWaterClientDetachedCount = 0;
+    fHighWaterClientDetachedBytes = 0;
+#endif
 
-    fHead = fTail = NULL;
+    fEntryCount                   = 0;
+    fEntryBytes                   = 0;
+    fClientDetachedCount          = 0;
+    fClientDetachedBytes          = 0;
+
     fPurging = false;
 }
 
 GrResourceCache::~GrResourceCache() {
     GrAutoResourceCacheValidate atcv(this);
 
-    this->removeAll();
+    EntryList::Iter iter;
+
+    // Unlike the removeAll, here we really remove everything, including locked resources.
+    while (GrResourceEntry* entry = fList.head()) {
+        GrAutoResourceCacheValidate atcv(this);
+
+        // remove from our cache
+        fCache.remove(entry->fKey, entry);
+
+        // remove from our llist
+        this->internalDetach(entry);
+
+        delete entry;
+    }
 }
 
 void GrResourceCache::getLimits(int* maxResources, size_t* maxResourceBytes) const{
@@ -74,110 +105,102 @@
 }
 
 void GrResourceCache::internalDetach(GrResourceEntry* entry,
-                                    bool clientDetach) {
-    GrResourceEntry* prev = entry->fPrev;
-    GrResourceEntry* next = entry->fNext;
-
-    if (prev) {
-        prev->fNext = next;
-    } else {
-        fHead = next;
-    }
-    if (next) {
-        next->fPrev = prev;
-    } else {
-        fTail = prev;
-    }
-    if (!entry->isLocked()) {
-        --fUnlockedEntryCount;
-    }
+                                     BudgetBehaviors behavior) {
+    fList.remove(entry);
 
     // update our stats
-    if (clientDetach) {
+    if (kIgnore_BudgetBehavior == behavior) {
         fClientDetachedCount += 1;
         fClientDetachedBytes += entry->resource()->sizeInBytes();
+
+#if GR_CACHE_STATS
+        if (fHighWaterClientDetachedCount < fClientDetachedCount) {
+            fHighWaterClientDetachedCount = fClientDetachedCount;
+        }
+        if (fHighWaterClientDetachedBytes < fClientDetachedBytes) {
+            fHighWaterClientDetachedBytes = fClientDetachedBytes;
+        }
+#endif
+
     } else {
+        GrAssert(kAccountFor_BudgetBehavior == behavior);
+
         fEntryCount -= 1;
         fEntryBytes -= entry->resource()->sizeInBytes();
     }
 }
 
 void GrResourceCache::attachToHead(GrResourceEntry* entry,
-                                  bool clientReattach) {
-    entry->fPrev = NULL;
-    entry->fNext = fHead;
-    if (fHead) {
-        fHead->fPrev = entry;
-    }
-    fHead = entry;
-    if (NULL == fTail) {
-        fTail = entry;
-    }
-    if (!entry->isLocked()) {
-        ++fUnlockedEntryCount;
-    }
+                                   BudgetBehaviors behavior) {
+    fList.addToHead(entry);
 
     // update our stats
-    if (clientReattach) {
+    if (kIgnore_BudgetBehavior == behavior) {
         fClientDetachedCount -= 1;
         fClientDetachedBytes -= entry->resource()->sizeInBytes();
     } else {
+        GrAssert(kAccountFor_BudgetBehavior == behavior);
+
         fEntryCount += 1;
         fEntryBytes += entry->resource()->sizeInBytes();
+
+#if GR_CACHE_STATS
+        if (fHighWaterEntryCount < fEntryCount) {
+            fHighWaterEntryCount = fEntryCount;
+        }
+        if (fHighWaterEntryBytes < fEntryBytes) {
+            fHighWaterEntryBytes = fEntryBytes;
+        }
+#endif
     }
 }
 
-class GrResourceCache::Key {
-    typedef GrResourceEntry T;
-
-    const GrResourceKey& fKey;
+// This functor just searches for an entry with only a single ref (from
+// the texture cache itself). Presumably in this situation no one else
+// is relying on the texture.
+class GrTFindUnreffedFunctor {
 public:
-    Key(const GrResourceKey& key) : fKey(key) {}
-
-    uint32_t getHash() const { return fKey.hashIndex(); }
-
-    static bool LT(const T& entry, const Key& key) {
-        return entry.key() < key.fKey;
+    bool operator()(const GrResourceEntry* entry) const {
+        return 1 == entry->resource()->getRefCnt();
     }
-    static bool EQ(const T& entry, const Key& key) {
-        return entry.key() == key.fKey;
-    }
-#if GR_DEBUG
-    static uint32_t GetHash(const T& entry) {
-        return entry.key().hashIndex();
-    }
-    static bool LT(const T& a, const T& b) {
-        return a.key() < b.key();
-    }
-    static bool EQ(const T& a, const T& b) {
-        return a.key() == b.key();
-    }
-#endif
 };
 
-GrResourceEntry* GrResourceCache::findAndLock(const GrResourceKey& key,
-                                              LockType type) {
+GrResource* GrResourceCache::find(const GrResourceKey& key, uint32_t ownershipFlags) {
     GrAutoResourceCacheValidate atcv(this);
 
-    GrResourceEntry* entry = fCache.find(key);
-    if (entry) {
-        this->internalDetach(entry, false);
-        // mark the entry as "busy" so it doesn't get purged
-        // do this between detach and attach for locked count tracking
-        if (kNested_LockType == type || !entry->isLocked()) {
-            entry->lock();
-        }
-        this->attachToHead(entry, false);
+    GrResourceEntry* entry = NULL;
+
+    if (ownershipFlags & kNoOtherOwners_OwnershipFlag) {
+        GrTFindUnreffedFunctor functor;
+
+        entry = fCache.find<GrTFindUnreffedFunctor>(key, functor);
+    } else {
+        entry = fCache.find(key);
     }
-    return entry;
+
+    if (NULL == entry) {
+        return NULL;
+    }
+
+    if (ownershipFlags & kHide_OwnershipFlag) {
+        this->makeExclusive(entry);
+    } else {
+        // Make this resource MRU
+        this->internalDetach(entry);
+        this->attachToHead(entry);
+    }
+
+    return entry->fResource;
 }
 
 bool GrResourceCache::hasKey(const GrResourceKey& key) const {
     return NULL != fCache.find(key);
 }
 
-GrResourceEntry* GrResourceCache::createAndLock(const GrResourceKey& key,
-                                              GrResource* resource) {
+void GrResourceCache::addResource(const GrResourceKey& key,
+                                  GrResource* resource,
+                                  uint32_t ownershipFlags) {
+    GrAssert(NULL == resource->getCacheEntry());
     // we don't expect to create new resources during a purge. In theory
     // this could cause purgeAsNeeded() into an infinite loop (e.g.
     // each resource destroyed creates and locks 2 resources and
@@ -185,13 +208,10 @@
     GrAssert(!fPurging);
     GrAutoResourceCacheValidate atcv(this);
 
-    GrResourceEntry* entry = new GrResourceEntry(key, resource);
+    GrResourceEntry* entry = SkNEW_ARGS(GrResourceEntry, (key, resource));
+    resource->setCacheEntry(entry);
 
-    // mark the entry as "busy" so it doesn't get purged
-    // do this before attach for locked count tracking
-    entry->lock();
-
-    this->attachToHead(entry, false);
+    this->attachToHead(entry);
     fCache.insert(key, entry);
 
 #if GR_DUMP_TEXTURE_UPLOAD
@@ -199,52 +219,58 @@
              entry, fEntryCount, resource->sizeInBytes(), fEntryBytes);
 #endif
 
-    this->purgeAsNeeded();
-    return entry;
+    if (ownershipFlags & kHide_OwnershipFlag) {
+        this->makeExclusive(entry);
+    }
+
 }
 
-void GrResourceCache::detach(GrResourceEntry* entry) {
+void GrResourceCache::makeExclusive(GrResourceEntry* entry) {
     GrAutoResourceCacheValidate atcv(this);
-    internalDetach(entry, true);
-    fCache.remove(entry->fKey, entry);
+
+    // When scratch textures are detached (to hide them from future finds) they
+    // still count against the resource budget
+    this->internalDetach(entry, kIgnore_BudgetBehavior);
+    fCache.remove(entry->key(), entry);
+
+#if GR_DEBUG
+    fExclusiveList.addToHead(entry);
+#endif
 }
 
-void GrResourceCache::reattachAndUnlock(GrResourceEntry* entry) {
+void GrResourceCache::removeInvalidResource(GrResourceEntry* entry) {
+    // If the resource went invalid while it was detached then purge it
+    // This can happen when a 3D context was lost,
+    // the client called GrContext::contextDestroyed() to notify Gr,
+    // and then later an SkGpuDevice's destructor releases its backing
+    // texture (which was invalidated at contextDestroyed time).
+    fClientDetachedCount -= 1;
+    fEntryCount -= 1;
+    size_t size = entry->resource()->sizeInBytes();
+    fClientDetachedBytes -= size;
+    fEntryBytes -= size;
+}
+
+void GrResourceCache::makeNonExclusive(GrResourceEntry* entry) {
     GrAutoResourceCacheValidate atcv(this);
+
+#if GR_DEBUG
+    fExclusiveList.remove(entry);
+#endif
+
     if (entry->resource()->isValid()) {
-        attachToHead(entry, true);
+        // Since scratch textures still count against the cache budget even
+        // when they have been removed from the cache, re-adding them doesn't
+        // alter the budget information.
+        attachToHead(entry, kIgnore_BudgetBehavior);
         fCache.insert(entry->key(), entry);
     } else {
-        // If the resource went invalid while it was detached then purge it
-        // This can happen when a 3D context was lost,
-        // the client called GrContext::contextDestroyed() to notify Gr,
-        // and then later an SkGpuDevice's destructor releases its backing
-        // texture (which was invalidated at contextDestroyed time).
-        fClientDetachedCount -= 1;
-        fEntryCount -= 1;
-        size_t size = entry->resource()->sizeInBytes();
-        fClientDetachedBytes -= size;
-        fEntryBytes -= size;
+        this->removeInvalidResource(entry);
     }
-    this->unlock(entry);
-}
-
-void GrResourceCache::unlock(GrResourceEntry* entry) {
-    GrAutoResourceCacheValidate atcv(this);
-
-    GrAssert(entry);
-    GrAssert(entry->isLocked());
-    GrAssert(fCache.find(entry->key()));
-
-    entry->unlock();
-    if (!entry->isLocked()) {
-        ++fUnlockedEntryCount;
-    }
-    this->purgeAsNeeded();
 }
 
 /**
- * Destroying a resource may potentially trigger the unlock of additional 
+ * Destroying a resource may potentially trigger the unlock of additional
  * resources which in turn will trigger a nested purge. We block the nested
  * purge using the fPurging variable. However, the initial purge will keep
  * looping until either all resources in the cache are unlocked or we've met
@@ -257,22 +283,38 @@
     if (!fPurging) {
         fPurging = true;
         bool withinBudget = false;
+        bool changed = false;
+
+        // The purging process is repeated several times since one pass
+        // may free up other resources
         do {
-            GrResourceEntry* entry = fTail;
-            while (entry && fUnlockedEntryCount) {
+            EntryList::Iter iter;
+
+            changed = false;
+
+            // Note: the following code relies on the fact that the
+            // doubly linked list doesn't invalidate its data/pointers
+            // outside of the specific area where a deletion occurs (e.g.,
+            // in internalDetach)
+            GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
+
+            while (NULL != entry) {
                 GrAutoResourceCacheValidate atcv(this);
+
                 if (fEntryCount <= fMaxCount && fEntryBytes <= fMaxBytes) {
                     withinBudget = true;
                     break;
                 }
 
-                GrResourceEntry* prev = entry->fPrev;
-                if (!entry->isLocked()) {
+                GrResourceEntry* prev = iter.prev();
+                if (1 == entry->fResource->getRefCnt()) {
+                    changed = true;
+
                     // remove from our cache
-                    fCache.remove(entry->fKey, entry);
+                    fCache.remove(entry->key(), entry);
 
                     // remove from our llist
-                    this->internalDetach(entry, false);
+                    this->internalDetach(entry);
 
         #if GR_DUMP_TEXTURE_UPLOAD
                     GrPrintf("--- ~resource from cache %p [%d %d]\n",
@@ -280,40 +322,39 @@
                              entry->resource()->width(),
                              entry->resource()->height());
         #endif
+
                     delete entry;
                 }
                 entry = prev;
             }
-        } while (!withinBudget && fUnlockedEntryCount);
+        } while (!withinBudget && changed);
         fPurging = false;
     }
 }
 
-void GrResourceCache::removeAll() {
+void GrResourceCache::purgeAllUnlocked() {
     GrAutoResourceCacheValidate atcv(this);
 
-    GrResourceEntry* entry = fHead;
-
     // we can have one GrResource holding a lock on another
     // so we don't want to just do a simple loop kicking each
     // entry out. Instead change the budget and purge.
 
     int savedMaxBytes = fMaxBytes;
     int savedMaxCount = fMaxCount;
-    fMaxBytes = -1;
+    fMaxBytes = (size_t) -1;
     fMaxCount = 0;
     this->purgeAsNeeded();
 
 #if GR_DEBUG
-    GrAssert(!fUnlockedEntryCount);
+    GrAssert(fExclusiveList.countEntries() == fClientDetachedCount);
+    GrAssert(countBytes(fExclusiveList) == fClientDetachedBytes);
     if (!fCache.count()) {
         // Items may have been detached from the cache (such as the backing
         // texture for an SkGpuDevice). The above purge would not have removed
         // them.
         GrAssert(fEntryCount == fClientDetachedCount);
         GrAssert(fEntryBytes == fClientDetachedBytes);
-        GrAssert(NULL == fHead);
-        GrAssert(NULL == fTail);
+        GrAssert(fList.isEmpty());
     }
 #endif
 
@@ -324,26 +365,27 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 #if GR_DEBUG
-static int countMatches(const GrResourceEntry* head, const GrResourceEntry* target) {
-    const GrResourceEntry* entry = head;
-    int count = 0;
-    while (entry) {
-        if (target == entry) {
-            count += 1;
-        }
-        entry = entry->next();
+size_t GrResourceCache::countBytes(const EntryList& list) {
+    size_t bytes = 0;
+
+    EntryList::Iter iter;
+
+    const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(list),
+                                             EntryList::Iter::kTail_IterStart);
+
+    for ( ; NULL != entry; entry = iter.prev()) {
+        bytes += entry->resource()->sizeInBytes();
     }
-    return count;
+    return bytes;
 }
 
-#if GR_DEBUG
 static bool both_zero_or_nonzero(int count, size_t bytes) {
     return (count == 0 && bytes == 0) || (count > 0 && bytes > 0);
 }
-#endif
 
 void GrResourceCache::validate() const {
-    GrAssert(!fHead == !fTail);
+    fList.validate();
+    fExclusiveList.validate();
     GrAssert(both_zero_or_nonzero(fEntryCount, fEntryBytes));
     GrAssert(both_zero_or_nonzero(fClientDetachedCount, fClientDetachedBytes));
     GrAssert(fClientDetachedBytes <= fEntryBytes);
@@ -352,33 +394,66 @@
 
     fCache.validate();
 
-    GrResourceEntry* entry = fHead;
+
+    EntryList::Iter iter;
+
+    // check that the exclusively held entries are okay
+    const GrResourceEntry* entry = iter.init(const_cast<EntryList&>(fExclusiveList),
+                                             EntryList::Iter::kHead_IterStart);
+
+    for ( ; NULL != entry; entry = iter.next()) {
+        entry->validate();
+    }
+
+    // check that the shareable entries are okay
+    entry = iter.init(const_cast<EntryList&>(fList), EntryList::Iter::kHead_IterStart);
+
     int count = 0;
-    int unlockCount = 0;
-    size_t bytes = 0;
-    while (entry) {
+    for ( ; NULL != entry; entry = iter.next()) {
         entry->validate();
         GrAssert(fCache.find(entry->key()));
         count += 1;
-        bytes += entry->resource()->sizeInBytes();
-        if (!entry->isLocked()) {
-            unlockCount += 1;
-        }
-        entry = entry->fNext;
     }
     GrAssert(count == fEntryCount - fClientDetachedCount);
+
+    size_t bytes = countBytes(fList);
     GrAssert(bytes == fEntryBytes  - fClientDetachedBytes);
-    GrAssert(unlockCount == fUnlockedEntryCount);
 
-    count = 0;
-    for (entry = fTail; entry; entry = entry->fPrev) {
-        count += 1;
-    }
-    GrAssert(count == fEntryCount - fClientDetachedCount);
+    bytes = countBytes(fExclusiveList);
+    GrAssert(bytes == fClientDetachedBytes);
 
-    for (int i = 0; i < count; i++) {
-        int matches = countMatches(fHead, fCache.getArray()[i]);
-        GrAssert(1 == matches);
-    }
+    GrAssert(fList.countEntries() == fEntryCount - fClientDetachedCount);
+
+    GrAssert(fExclusiveList.countEntries() == fClientDetachedCount);
 }
+#endif // GR_DEBUG
+
+#if GR_CACHE_STATS
+
+void GrResourceCache::printStats() {
+    int locked = 0;
+
+    EntryList::Iter iter;
+
+    GrResourceEntry* entry = iter.init(fList, EntryList::Iter::kTail_IterStart);
+
+    for ( ; NULL != entry; entry = iter.prev()) {
+        if (entry->fResource->getRefCnt() > 1) {
+            ++locked;
+        }
+    }
+
+    SkDebugf("Budget: %d items %d bytes\n", fMaxCount, fMaxBytes);
+    SkDebugf("\t\tEntry Count: current %d (%d locked) high %d\n",
+                fEntryCount, locked, fHighWaterEntryCount);
+    SkDebugf("\t\tEntry Bytes: current %d high %d\n",
+                fEntryBytes, fHighWaterEntryBytes);
+    SkDebugf("\t\tDetached Entry Count: current %d high %d\n",
+                fClientDetachedCount, fHighWaterClientDetachedCount);
+    SkDebugf("\t\tDetached Bytes: current %d high %d\n",
+                fClientDetachedBytes, fHighWaterClientDetachedBytes);
+}
+
 #endif
+
+///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrResourceCache.h b/src/gpu/GrResourceCache.h
index f86fcd2..dd8ba31 100644
--- a/src/gpu/GrResourceCache.h
+++ b/src/gpu/GrResourceCache.h
@@ -11,28 +11,15 @@
 #ifndef GrResourceCache_DEFINED
 #define GrResourceCache_DEFINED
 
+#include "GrConfig.h"
 #include "GrTypes.h"
 #include "GrTHashCache.h"
+#include "GrBinHashKey.h"
+#include "SkTInternalLList.h"
 
 class GrResource;
+class GrResourceEntry;
 
-// return true if a<b, or false if b<a
-//
-#define RET_IF_LT_OR_GT(a, b)   \
-    do {                        \
-        if ((a) < (b)) {        \
-            return true;        \
-        }                       \
-        if ((b) < (a)) {        \
-            return false;       \
-        }                       \
-    } while (0)
-
-/**
- *  Helper class for GrResourceCache, the Key is used to identify src data for
- *  a resource. It is identified by 2 32bit data fields which can hold any
- *  data (uninterpreted by the cache) and a width/height.
- */
 class GrResourceKey {
 public:
     enum {
@@ -41,79 +28,117 @@
         kHashMask   = kHashCount - 1
     };
 
-    GrResourceKey(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3) {
-        fP[0] = p0;
-        fP[1] = p1;
-        fP[2] = p2;
-        fP[3] = p3;
-        this->computeHashIndex();
+    static GrCacheID::Domain ScratchDomain() {
+        static const GrCacheID::Domain gDomain = GrCacheID::GenerateDomain();
+        return gDomain;
     }
 
-    GrResourceKey(uint32_t v[4]) {
-        memcpy(fP, v, 4 * sizeof(uint32_t));
-        this->computeHashIndex();
-    }
+    /** Uniquely identifies the GrResource subclass in the key to avoid collisions
+        across resource types. */
+    typedef uint8_t ResourceType;
+
+    /** Flags set by the GrResource subclass. */
+    typedef uint8_t ResourceFlags;
+
+    /** Generate a unique ResourceType */
+    static ResourceType GenerateResourceType();
+
+    /** Creates a key for resource */
+    GrResourceKey(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
+        this->init(id.getDomain(), id.getKey(), type, flags);
+    };
 
     GrResourceKey(const GrResourceKey& src) {
-        memcpy(fP, src.fP, 4 * sizeof(uint32_t));
-#if GR_DEBUG
-        this->computeHashIndex();
-        GrAssert(fHashIndex == src.fHashIndex);
-#endif
-        fHashIndex = src.fHashIndex;
+        fKey = src.fKey;
+    }
+
+    GrResourceKey() {
+        fKey.fHashedKey.reset();
+    }
+
+    void reset(const GrCacheID& id, ResourceType type, ResourceFlags flags) {
+        this->init(id.getDomain(), id.getKey(), type, flags);
     }
 
     //!< returns hash value [0..kHashMask] for the key
-    int hashIndex() const { return fHashIndex; }
-
-    friend bool operator==(const GrResourceKey& a, const GrResourceKey& b) {
-        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
-        return 0 == memcmp(a.fP, b.fP, 4 * sizeof(uint32_t));
+    int getHash() const {
+        return fKey.fHashedKey.getHash() & kHashMask;
     }
 
-    friend bool operator!=(const GrResourceKey& a, const GrResourceKey& b) {
-        GR_DEBUGASSERT(-1 != a.fHashIndex && -1 != b.fHashIndex);
-        return !(a == b);
+    bool isScratch() const {
+        return ScratchDomain() ==
+            *reinterpret_cast<const GrCacheID::Domain*>(fKey.fHashedKey.getData() +
+                                                        kCacheIDDomainOffset);
     }
 
-    friend bool operator<(const GrResourceKey& a, const GrResourceKey& b) {
-        RET_IF_LT_OR_GT(a.fP[0], b.fP[0]);
-        RET_IF_LT_OR_GT(a.fP[1], b.fP[1]);
-        RET_IF_LT_OR_GT(a.fP[2], b.fP[2]);
-        return a.fP[3] < b.fP[3];
+    ResourceType getResourceType() const {
+        return *reinterpret_cast<const ResourceType*>(fKey.fHashedKey.getData() +
+                                                      kResourceTypeOffset);
     }
 
-    uint32_t getValue32(int i) const {
-        GrAssert(i >=0 && i < 4);
-        return fP[i];
+    ResourceFlags getResourceFlags() const {
+        return *reinterpret_cast<const ResourceFlags*>(fKey.fHashedKey.getData() +
+                                                       kResourceFlagsOffset);
     }
+
+    int compare(const GrResourceKey& other) const {
+        return fKey.fHashedKey.compare(other.fKey.fHashedKey);
+    }
+
+    static bool LT(const GrResourceKey& a, const GrResourceKey& b) {
+        return a.compare(b) < 0;
+    }
+
+    static bool EQ(const GrResourceKey& a, const GrResourceKey& b) {
+        return 0 == a.compare(b);
+    }
+
+    inline static bool LT(const GrResourceEntry& entry, const GrResourceKey& key);
+    inline static bool EQ(const GrResourceEntry& entry, const GrResourceKey& key);
+    inline static bool LT(const GrResourceEntry& a, const GrResourceEntry& b);
+    inline static bool EQ(const GrResourceEntry& a, const GrResourceEntry& b);
+
 private:
+    enum {
+        kCacheIDKeyOffset = 0,
+        kCacheIDDomainOffset = kCacheIDKeyOffset + sizeof(GrCacheID::Key),
+        kResourceTypeOffset = kCacheIDDomainOffset + sizeof(GrCacheID::Domain),
+        kResourceFlagsOffset = kResourceTypeOffset + sizeof(ResourceType),
+        kPadOffset = kResourceFlagsOffset + sizeof(ResourceFlags),
+        kKeySize = SkAlign4(kPadOffset),
+        kPadSize = kKeySize - kPadOffset
+    };
 
-    static uint32_t rol(uint32_t x) {
-        return (x >> 24) | (x << 8);
-    }
-    static uint32_t ror(uint32_t x) {
-        return (x >> 8) | (x << 24);
-    }
-    static uint32_t rohalf(uint32_t x) {
-        return (x >> 16) | (x << 16);
+    void init(const GrCacheID::Domain domain,
+              const GrCacheID::Key& key,
+              ResourceType type,
+              ResourceFlags flags) {
+        union {
+            uint8_t  fKey8[kKeySize];
+            uint32_t fKey32[kKeySize / 4];
+        } keyData;
+
+        uint8_t* k = keyData.fKey8;
+        memcpy(k + kCacheIDKeyOffset, key.fData8, sizeof(GrCacheID::Key));
+        memcpy(k + kCacheIDDomainOffset, &domain, sizeof(GrCacheID::Domain));
+        memcpy(k + kResourceTypeOffset, &type, sizeof(ResourceType));
+        memcpy(k + kResourceFlagsOffset, &flags, sizeof(ResourceFlags));
+        memset(k + kPadOffset, 0, kPadSize);
+        fKey.fHashedKey.setKeyData(keyData.fKey32);
     }
 
-    void computeHashIndex() {
-        uint32_t hash = fP[0] ^ rol(fP[1]) ^ ror(fP[2]) ^ rohalf(fP[3]);
-        // this way to mix and reduce hash to its index may have to change
-        // depending on how many bits we allocate to the index
-        hash ^= hash >> 16;
-        hash ^= hash >> 8;
-        fHashIndex = hash & kHashMask;
-    }
+    struct Key;
+    typedef GrTBinHashKey<Key, kKeySize> HashedKey;
 
-    uint32_t    fP[4];
+    struct Key {
+        int compare(const HashedKey& hashedKey) const {
+            return fHashedKey.compare(fHashedKey);
+        }
 
-    // this is computed from the fP... fields
-    int         fHashIndex;
+        HashedKey fHashedKey;
+    };
 
-    friend class GrContext;
+    Key fKey;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -124,11 +149,6 @@
     const GrResourceKey& key() const { return fKey; }
 
 #if GR_DEBUG
-    GrResourceEntry* next() const { return fNext; }
-    GrResourceEntry* prev() const { return fPrev; }
-#endif
-
-#if GR_DEBUG
     void validate() const;
 #else
     void validate() const {}
@@ -138,27 +158,32 @@
     GrResourceEntry(const GrResourceKey& key, GrResource* resource);
     ~GrResourceEntry();
 
-    bool isLocked() const { return fLockCount != 0; }
-    void lock() { ++fLockCount; }
-    void unlock() {
-        GrAssert(fLockCount > 0);
-        --fLockCount;
-    }
-
     GrResourceKey    fKey;
     GrResource*      fResource;
 
-    // track if we're in use, used when we need to purge
-    // we only purge unlocked entries
-    int fLockCount;
-
-    // we're a dlinklist
-    GrResourceEntry* fPrev;
-    GrResourceEntry* fNext;
+    // we're a linked list
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(GrResourceEntry);
 
     friend class GrResourceCache;
+    friend class GrDLinkedList;
 };
 
+bool GrResourceKey::LT(const GrResourceEntry& entry, const GrResourceKey& key) {
+    return LT(entry.key(), key);
+}
+
+bool GrResourceKey::EQ(const GrResourceEntry& entry, const GrResourceKey& key) {
+    return EQ(entry.key(), key);
+}
+
+bool GrResourceKey::LT(const GrResourceEntry& a, const GrResourceEntry& b) {
+    return LT(a.key(), b.key());
+}
+
+bool GrResourceKey::EQ(const GrResourceEntry& a, const GrResourceEntry& b) {
+    return EQ(a.key(), b.key());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "GrTHashCache.h"
@@ -190,7 +215,7 @@
     /**
      *  Return the current resource cache limits.
      *
-     *  @param maxResource If non-null, returns maximum number of resources 
+     *  @param maxResource If non-null, returns maximum number of resources
      *                     that can be held in the cache.
      *  @param maxBytes    If non-null, returns maximum number of bytes of
      *                         gpu memory that can be held in the cache.
@@ -213,28 +238,41 @@
      */
     size_t getCachedResourceBytes() const { return fEntryBytes; }
 
-    /**
-     * Controls whether locks should be nestable or not.
-     */
-    enum LockType {
-        kNested_LockType,
-        kSingle_LockType,
+    // For a found or added resource to be completely exclusive to the caller
+    // both the kNoOtherOwners and kHide flags need to be specified
+    enum OwnershipFlags {
+        kNoOtherOwners_OwnershipFlag = 0x1, // found/added resource has no other owners
+        kHide_OwnershipFlag = 0x2  // found/added resource is hidden from future 'find's
     };
 
     /**
-     *  Search for an entry with the same Key. If found, "lock" it and return it.
+     *  Search for an entry with the same Key. If found, return it.
      *  If not found, return null.
+     *  If ownershipFlags includes kNoOtherOwners and a resource is returned
+     *  then that resource has no other refs to it.
+     *  If ownershipFlags includes kHide and a resource is returned then that
+     *  resource will not be returned from future 'find' calls until it is
+     *  'freed' (and recycled) or makeNonExclusive is called.
+     *  For a resource to be completely exclusive to a caller both kNoOtherOwners
+     *  and kHide must be specified.
      */
-    GrResourceEntry* findAndLock(const GrResourceKey&, LockType style);
+    GrResource* find(const GrResourceKey& key,
+                     uint32_t ownershipFlags = 0);
 
     /**
-     *  Create a new entry, based on the specified key and resource, and return
-     *  its "locked" entry.
+     *  Add the new resource to the cache (by creating a new cache entry based
+     *  on the provided key and resource).
      *
-     *  Ownership of the resource is transferred to the Entry, which will unref()
-     *  it when we are purged or deleted.
+     *  Ownership of the resource is transferred to the resource cache,
+     *  which will unref() it when it is purged or deleted.
+     *
+     *  If ownershipFlags includes kHide, subsequent calls to 'find' will not
+     *  return 'resource' until it is 'freed' (and recycled) or makeNonExclusive
+     *  is called.
      */
-    GrResourceEntry* createAndLock(const GrResourceKey&, GrResource*);
+    void addResource(const GrResourceKey& key,
+                     GrResource* resource,
+                     uint32_t ownershipFlags = 0);
 
     /**
      * Determines if the cache contains an entry matching a key. If a matching
@@ -243,27 +281,30 @@
     bool hasKey(const GrResourceKey& key) const;
 
     /**
-     * Detach removes an entry from the cache. This prevents the entry from
-     * being found by a subsequent findAndLock() until it is reattached. The
-     * entry still counts against the cache's budget and should be reattached
-     * when exclusive access is no longer needed.
+     * Hide 'entry' so that future searches will not find it. Such
+     * hidden entries will not be purged. The entry still counts against
+     * the cache's budget and should be made non-exclusive when exclusive access
+     * is no longer needed.
      */
-    void detach(GrResourceEntry*);
+    void makeExclusive(GrResourceEntry* entry);
 
     /**
-     * Reattaches a resource to the cache and unlocks it. Allows it to be found
-     * by a subsequent findAndLock or be purged (provided its lock count is
-     * now 0.)
+     * Restore 'entry' so that it can be found by future searches. 'entry'
+     * will also be purgeable (provided its lock count is now 0.)
      */
-    void reattachAndUnlock(GrResourceEntry*);
+    void makeNonExclusive(GrResourceEntry* entry);
 
     /**
-     *  When done with an entry, call unlock(entry) on it, which returns it to
-     *  a purgable state.
+     * Removes every resource in the cache that isn't locked.
      */
-    void unlock(GrResourceEntry*);
+    void purgeAllUnlocked();
 
-    void removeAll();
+    /**
+     * Allow cache to purge unused resources to obey resource limitations
+     * Note: this entry point will be hidden (again) once totally ref-driven
+     * cache maintenance is implemented
+     */
+    void purgeAsNeeded();
 
 #if GR_DEBUG
     void validate() const;
@@ -271,31 +312,55 @@
     void validate() const {}
 #endif
 
+#if GR_CACHE_STATS
+    void printStats();
+#endif
+
 private:
-    void internalDetach(GrResourceEntry*, bool);
-    void attachToHead(GrResourceEntry*, bool);
-    void purgeAsNeeded();
+    enum BudgetBehaviors {
+        kAccountFor_BudgetBehavior,
+        kIgnore_BudgetBehavior
+    };
 
-    class Key;
-    GrTHashTable<GrResourceEntry, Key, 8> fCache;
+    void internalDetach(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
+    void attachToHead(GrResourceEntry*, BudgetBehaviors behavior = kAccountFor_BudgetBehavior);
 
-    // manage the dlink list
-    GrResourceEntry* fHead;
-    GrResourceEntry* fTail;
+    void removeInvalidResource(GrResourceEntry* entry);
+
+    GrTHashTable<GrResourceEntry, GrResourceKey, 8> fCache;
+
+    // We're an internal doubly linked list
+    typedef SkTInternalLList<GrResourceEntry> EntryList;
+    EntryList    fList;
+
+#if GR_DEBUG
+    // These objects cannot be returned by a search
+    EntryList    fExclusiveList;
+#endif
 
     // our budget, used in purgeAsNeeded()
     int fMaxCount;
     size_t fMaxBytes;
 
     // our current stats, related to our budget
+#if GR_CACHE_STATS
+    int fHighWaterEntryCount;
+    size_t fHighWaterEntryBytes;
+    int fHighWaterClientDetachedCount;
+    size_t fHighWaterClientDetachedBytes;
+#endif
+
     int fEntryCount;
-    int fUnlockedEntryCount;
     size_t fEntryBytes;
     int fClientDetachedCount;
     size_t fClientDetachedBytes;
-    
+
     // prevents recursive purging
     bool fPurging;
+
+#if GR_DEBUG
+    static size_t countBytes(const SkTInternalLList<GrResourceEntry>& list);
+#endif
 };
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/GrSWMaskHelper.cpp b/src/gpu/GrSWMaskHelper.cpp
new file mode 100644
index 0000000..c052091
--- /dev/null
+++ b/src/gpu/GrSWMaskHelper.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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 "GrSWMaskHelper.h"
+#include "GrDrawState.h"
+#include "GrGpu.h"
+
+#include "SkStrokeRec.h"
+
+// TODO: try to remove this #include
+#include "GrContext.h"
+
+namespace {
+/*
+ * Convert a boolean operation into a transfer mode code
+ */
+SkXfermode::Mode op_to_mode(SkRegion::Op op) {
+
+    static const SkXfermode::Mode modeMap[] = {
+        SkXfermode::kDstOut_Mode,   // kDifference_Op
+        SkXfermode::kModulate_Mode, // kIntersect_Op
+        SkXfermode::kSrcOver_Mode,  // kUnion_Op
+        SkXfermode::kXor_Mode,      // kXOR_Op
+        SkXfermode::kClear_Mode,    // kReverseDifference_Op
+        SkXfermode::kSrc_Mode,      // kReplace_Op
+    };
+
+    return modeMap[op];
+}
+
+}
+
+/**
+ * Draw a single rect element of the clip stack into the accumulation bitmap
+ */
+void GrSWMaskHelper::draw(const GrRect& rect, SkRegion::Op op,
+                          bool antiAlias, uint8_t alpha) {
+    SkPaint paint;
+
+    SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
+
+    paint.setXfermode(mode);
+    paint.setAntiAlias(antiAlias);
+    paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
+
+    fDraw.drawRect(rect, paint);
+
+    SkSafeUnref(mode);
+}
+
+/**
+ * Draw a single path element of the clip stack into the accumulation bitmap
+ */
+void GrSWMaskHelper::draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
+                          bool antiAlias, uint8_t alpha) {
+
+    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());
+        }
+    }
+
+    SkXfermode* mode = SkXfermode::Create(op_to_mode(op));
+
+    paint.setXfermode(mode);
+    paint.setAntiAlias(antiAlias);
+    paint.setColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
+
+    fDraw.drawPath(path, paint);
+
+    SkSafeUnref(mode);
+}
+
+bool GrSWMaskHelper::init(const GrIRect& resultBounds,
+                          const SkMatrix* matrix) {
+    if (NULL != matrix) {
+        fMatrix = *matrix;
+    } else {
+        fMatrix.setIdentity();
+    }
+
+    // Now translate so the bound's UL corner is at the origin
+    fMatrix.postTranslate(-resultBounds.fLeft * SK_Scalar1,
+                          -resultBounds.fTop * SK_Scalar1);
+    GrIRect bounds = GrIRect::MakeWH(resultBounds.width(),
+                                     resultBounds.height());
+
+    fBM.setConfig(SkBitmap::kA8_Config, bounds.fRight, bounds.fBottom);
+    if (!fBM.allocPixels()) {
+        return false;
+    }
+    sk_bzero(fBM.getPixels(), fBM.getSafeSize());
+
+    sk_bzero(&fDraw, sizeof(fDraw));
+    fRasterClip.setRect(bounds);
+    fDraw.fRC    = &fRasterClip;
+    fDraw.fClip  = &fRasterClip.bwRgn();
+    fDraw.fMatrix = &fMatrix;
+    fDraw.fBitmap = &fBM;
+    return true;
+}
+
+/**
+ * Get a texture (from the texture cache) of the correct size & format.
+ * Return true on success; false on failure.
+ */
+bool GrSWMaskHelper::getTexture(GrAutoScratchTexture* texture) {
+    GrTextureDesc desc;
+    desc.fWidth = fBM.width();
+    desc.fHeight = fBM.height();
+    desc.fConfig = kAlpha_8_GrPixelConfig;
+
+    texture->set(fContext, desc);
+    return NULL != texture->texture();
+}
+
+/**
+ * Move the result of the software mask generation back to the gpu
+ */
+void GrSWMaskHelper::toTexture(GrTexture *texture, uint8_t alpha) {
+    SkAutoLockPixels alp(fBM);
+
+    // The destination texture is almost always larger than "fBM". Clear
+    // it appropriately so we don't get mask artifacts outside of the path's
+    // bounding box
+
+    // "texture" needs to be installed as the render target for the clear
+    // and the texture upload but cannot remain the render target upon
+    // return. Callers typically use it as a texture and it would then
+    // be both source and dest.
+    GrDrawState::AutoRenderTargetRestore artr(fContext->getGpu()->drawState(),
+                                              texture->asRenderTarget());
+
+    fContext->getGpu()->clear(NULL, GrColorPackRGBA(alpha, alpha, alpha, alpha));
+
+    texture->writePixels(0, 0, fBM.width(), fBM.height(),
+                         kAlpha_8_GrPixelConfig,
+                         fBM.getPixels(), fBM.rowBytes());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+/**
+ * Software rasterizes path to A8 mask (possibly using the context's matrix)
+ * and uploads the result to a scratch texture. Returns the resulting
+ * texture on success; NULL on failure.
+ */
+GrTexture* GrSWMaskHelper::DrawPathMaskToTexture(GrContext* context,
+                                                 const SkPath& path,
+                                                 const SkStrokeRec& stroke,
+                                                 const GrIRect& resultBounds,
+                                                 bool antiAlias,
+                                                 SkMatrix* matrix) {
+    GrAutoScratchTexture ast;
+
+    GrSWMaskHelper helper(context);
+
+    if (!helper.init(resultBounds, matrix)) {
+        return NULL;
+    }
+
+    helper.draw(path, stroke, SkRegion::kReplace_Op, antiAlias, 0xFF);
+
+    if (!helper.getTexture(&ast)) {
+        return NULL;
+    }
+
+    helper.toTexture(ast.texture(), 0x00);
+
+    return ast.detach();
+}
+
+void GrSWMaskHelper::DrawToTargetWithPathMask(GrTexture* texture,
+                                              GrDrawTarget* target,
+                                              const GrIRect& rect) {
+    GrDrawState* drawState = target->drawState();
+
+    GrDrawState::AutoDeviceCoordDraw adcd(drawState);
+    if (!adcd.succeeded()) {
+        return;
+    }
+    enum {
+        // the SW path renderer shares this stage with glyph
+        // rendering (kGlyphMaskStage in GrBatchedTextContext)
+        kPathMaskStage = GrPaint::kTotalStages,
+    };
+    GrAssert(!drawState->isStageEnabled(kPathMaskStage));
+    drawState->createTextureEffect(kPathMaskStage, texture, SkMatrix::I());
+    SkScalar w = SkIntToScalar(rect.width());
+    SkScalar h = SkIntToScalar(rect.height());
+    GrRect maskRect = GrRect::MakeWH(w / texture->width(),
+                                     h / texture->height());
+
+    const GrRect* srcRects[GrDrawState::kNumStages] = { NULL };
+    srcRects[kPathMaskStage] = &maskRect;
+    GrRect dstRect = GrRect::MakeLTRB(
+                            SK_Scalar1 * rect.fLeft,
+                            SK_Scalar1 * rect.fTop,
+                            SK_Scalar1 * rect.fRight,
+                            SK_Scalar1 * rect.fBottom);
+    target->drawRect(dstRect, NULL, srcRects, NULL);
+    drawState->disableStage(kPathMaskStage);
+}
diff --git a/src/gpu/GrSWMaskHelper.h b/src/gpu/GrSWMaskHelper.h
new file mode 100644
index 0000000..daf3111
--- /dev/null
+++ b/src/gpu/GrSWMaskHelper.h
@@ -0,0 +1,108 @@
+/*
+ * 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 GrSWMaskHelper_DEFINED
+#define GrSWMaskHelper_DEFINED
+
+#include "GrColor.h"
+#include "SkMatrix.h"
+#include "GrNoncopyable.h"
+#include "SkBitmap.h"
+#include "SkDraw.h"
+#include "SkRasterClip.h"
+#include "SkRegion.h"
+#include "GrDrawState.h"
+
+class GrAutoScratchTexture;
+class GrContext;
+class GrTexture;
+class SkPath;
+class SkStrokeRec;
+class GrDrawTarget;
+
+/**
+ * The GrSWMaskHelper helps generate clip masks using the software rendering
+ * path. It is intended to be used as:
+ *
+ *   GrSWMaskHelper helper(context);
+ *   helper.init(...);
+ *
+ *      draw one or more paths/rects specifying the required boolean ops
+ *
+ *   toTexture();   // to get it from the internal bitmap to the GPU
+ *
+ * The result of this process will be the final mask (on the GPU) in the
+ * upper left hand corner of the texture.
+ */
+class GrSWMaskHelper : public GrNoncopyable {
+public:
+    GrSWMaskHelper(GrContext* context)
+    : fContext(context) {
+    }
+
+    // set up the internal state in preparation for draws. Since many masks
+    // may be accumulated in the helper during creation, "resultBounds"
+    // allows the caller to specify the region of interest - to limit the
+    // amount of work.
+    bool init(const GrIRect& resultBounds, const SkMatrix* matrix);
+
+    // Draw a single rect into the accumulation bitmap using the specified op
+    void draw(const GrRect& rect, SkRegion::Op op,
+              bool antiAlias, uint8_t alpha);
+
+    // Draw a single path into the accumuation bitmap using the specified op
+    void draw(const SkPath& path, const SkStrokeRec& stroke, SkRegion::Op op,
+              bool antiAlias, uint8_t alpha);
+
+    // Helper function to get a scratch texture suitable for capturing the
+    // result (i.e., right size & format)
+    bool getTexture(GrAutoScratchTexture* texture);
+
+    // Move the mask generation results from the internal bitmap to the gpu.
+    // The space outside of the mask is cleared using "alpha"
+    void toTexture(GrTexture* texture, uint8_t alpha);
+
+    // Reset the internal bitmap
+    void clear(uint8_t alpha) {
+        fBM.eraseColor(SkColorSetARGB(alpha, alpha, alpha, alpha));
+    }
+
+    // Canonical usage utility that draws a single path and uploads it
+    // to the GPU. The result is returned in "result".
+    static GrTexture* DrawPathMaskToTexture(GrContext* context,
+                                            const SkPath& path,
+                                            const SkStrokeRec& stroke,
+                                            const GrIRect& resultBounds,
+                                            bool antiAlias,
+                                            SkMatrix* matrix);
+
+    // This utility routine is used to add a path's mask to some other draw.
+    // The ClipMaskManager uses it to accumulate clip masks while the
+    // GrSoftwarePathRenderer uses it to fulfill a drawPath call.
+    // It draws with "texture" as a path mask into "target" using "rect" as
+    // geometry and the current drawState. The current drawState is altered to
+    // accommodate the mask.
+    // Note that this method assumes that the GrPaint::kTotalStages slot in
+    // the draw state can be used to hold the mask texture stage.
+    // This method is really only intended to be used with the
+    // output of DrawPathMaskToTexture.
+    static void DrawToTargetWithPathMask(GrTexture* texture,
+                                         GrDrawTarget* target,
+                                         const GrIRect& rect);
+
+protected:
+private:
+    GrContext*      fContext;
+    SkMatrix        fMatrix;
+    SkBitmap        fBM;
+    SkDraw          fDraw;
+    SkRasterClip    fRasterClip;
+
+    typedef GrNoncopyable INHERITED;
+};
+
+#endif // GrSWMaskHelper_DEFINED
diff --git a/src/gpu/GrSoftwarePathRenderer.cpp b/src/gpu/GrSoftwarePathRenderer.cpp
new file mode 100644
index 0000000..36bdcff
--- /dev/null
+++ b/src/gpu/GrSoftwarePathRenderer.cpp
@@ -0,0 +1,153 @@
+
+/*
+ * 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 "GrSoftwarePathRenderer.h"
+#include "GrContext.h"
+#include "GrSWMaskHelper.h"
+
+////////////////////////////////////////////////////////////////////////////////
+bool GrSoftwarePathRenderer::canDrawPath(const SkPath&,
+                                         const SkStrokeRec&,
+                                         const GrDrawTarget*,
+                                         bool antiAlias) const {
+    if (!antiAlias || NULL == fContext) {
+        // TODO: We could allow the SW path to also handle non-AA paths but
+        // this would mean that GrDefaultPathRenderer would never be called
+        // (since it appears after the SW renderer in the path renderer
+        // chain). Some testing would need to be done r.e. performance
+        // and consistency of the resulting images before removing
+        // the "!antiAlias" clause from the above test
+        return false;
+    }
+
+    return true;
+}
+
+GrPathRenderer::StencilSupport GrSoftwarePathRenderer::onGetStencilSupport(
+                                                                        const SkPath&,
+                                                                        const SkStrokeRec&,
+                                                                        const GrDrawTarget*) const {
+    return GrPathRenderer::kNoSupport_StencilSupport;
+}
+
+namespace {
+
+////////////////////////////////////////////////////////////////////////////////
+// gets device coord bounds of path (not considering the fill) and clip. The
+// path bounds will be a subset of the clip bounds. returns false if
+// path bounds would be empty.
+bool get_path_and_clip_bounds(const GrDrawTarget* target,
+                              const SkPath& path,
+                              const SkMatrix& matrix,
+                              GrIRect* devPathBounds,
+                              GrIRect* devClipBounds) {
+    // compute bounds as intersection of rt size, clip, and path
+    const GrRenderTarget* rt = target->getDrawState().getRenderTarget();
+    if (NULL == rt) {
+        return false;
+    }
+    *devPathBounds = GrIRect::MakeWH(rt->width(), rt->height());
+
+    target->getClip()->getConservativeBounds(rt, devClipBounds);
+
+    // TODO: getConservativeBounds already intersects with the
+    // render target's bounding box. Remove this next line
+    if (!devPathBounds->intersect(*devClipBounds)) {
+        return false;
+    }
+
+    if (!path.getBounds().isEmpty()) {
+        GrRect pathSBounds;
+        matrix.mapRect(&pathSBounds, path.getBounds());
+        GrIRect pathIBounds;
+        pathSBounds.roundOut(&pathIBounds);
+        if (!devPathBounds->intersect(pathIBounds)) {
+            // set the correct path bounds, as this would be used later.
+            *devPathBounds = pathIBounds;
+            return false;
+        }
+    } else {
+        *devPathBounds = GrIRect::EmptyIRect();
+        return false;
+    }
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+void draw_around_inv_path(GrDrawTarget* target,
+                          const GrIRect& devClipBounds,
+                          const GrIRect& devPathBounds) {
+    GrDrawState::AutoDeviceCoordDraw adcd(target->drawState());
+    if (!adcd.succeeded()) {
+        return;
+    }
+    GrRect rect;
+    if (devClipBounds.fTop < devPathBounds.fTop) {
+        rect.iset(devClipBounds.fLeft, devClipBounds.fTop,
+                  devClipBounds.fRight, devPathBounds.fTop);
+        target->drawSimpleRect(rect, NULL);
+    }
+    if (devClipBounds.fLeft < devPathBounds.fLeft) {
+        rect.iset(devClipBounds.fLeft, devPathBounds.fTop,
+                  devPathBounds.fLeft, devPathBounds.fBottom);
+        target->drawSimpleRect(rect, NULL);
+    }
+    if (devClipBounds.fRight > devPathBounds.fRight) {
+        rect.iset(devPathBounds.fRight, devPathBounds.fTop,
+                  devClipBounds.fRight, devPathBounds.fBottom);
+        target->drawSimpleRect(rect, NULL);
+    }
+    if (devClipBounds.fBottom > devPathBounds.fBottom) {
+        rect.iset(devClipBounds.fLeft, devPathBounds.fBottom,
+                  devClipBounds.fRight, devClipBounds.fBottom);
+        target->drawSimpleRect(rect, NULL);
+    }
+}
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// return true on success; false on failure
+bool GrSoftwarePathRenderer::onDrawPath(const SkPath& path,
+                                        const SkStrokeRec& stroke,
+                                        GrDrawTarget* target,
+                                        bool antiAlias) {
+
+    if (NULL == fContext) {
+        return false;
+    }
+
+    GrDrawState* drawState = target->drawState();
+
+    SkMatrix vm = drawState->getViewMatrix();
+
+    GrIRect devPathBounds, devClipBounds;
+    if (!get_path_and_clip_bounds(target, path, vm,
+                                  &devPathBounds, &devClipBounds)) {
+        if (path.isInverseFillType()) {
+            draw_around_inv_path(target, devClipBounds, devPathBounds);
+        }
+        return true;
+    }
+
+    SkAutoTUnref<GrTexture> texture(
+            GrSWMaskHelper::DrawPathMaskToTexture(fContext, path, stroke,
+                                                  devPathBounds,
+                                                  antiAlias, &vm));
+    if (NULL == texture) {
+        return false;
+    }
+
+    GrSWMaskHelper::DrawToTargetWithPathMask(texture, target, devPathBounds);
+
+    if (path.isInverseFillType()) {
+        draw_around_inv_path(target, devClipBounds, devPathBounds);
+    }
+
+    return true;
+}
diff --git a/src/gpu/GrSoftwarePathRenderer.h b/src/gpu/GrSoftwarePathRenderer.h
new file mode 100644
index 0000000..f8c5620
--- /dev/null
+++ b/src/gpu/GrSoftwarePathRenderer.h
@@ -0,0 +1,47 @@
+
+/*
+ * 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 GrSoftwarePathRenderer_DEFINED
+#define GrSoftwarePathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+
+class GrContext;
+class GrAutoScratchTexture;
+
+/**
+ * This class uses the software side to render a path to an SkBitmap and
+ * then uploads the result to the gpu
+ */
+class GrSoftwarePathRenderer : public GrPathRenderer {
+public:
+    GrSoftwarePathRenderer(GrContext* context)
+        : fContext(context) {
+    }
+
+    virtual bool canDrawPath(const SkPath&,
+                             const SkStrokeRec&,
+                             const GrDrawTarget*,
+                             bool antiAlias) const SK_OVERRIDE;
+protected:
+    virtual StencilSupport onGetStencilSupport(const SkPath&,
+                                               const SkStrokeRec&,
+                                               const GrDrawTarget*) const SK_OVERRIDE;
+
+    virtual bool onDrawPath(const SkPath&,
+                            const SkStrokeRec&,
+                            GrDrawTarget*,
+                            bool antiAlias) SK_OVERRIDE;
+
+private:
+    GrContext*     fContext;
+
+    typedef GrPathRenderer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrStencil.cpp b/src/gpu/GrStencil.cpp
index 6624942..7677260 100644
--- a/src/gpu/GrStencil.cpp
+++ b/src/gpu/GrStencil.cpp
@@ -167,7 +167,9 @@
     0x0000,          // set clip bit
     0xffff);
 
-GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiff,
+// We are looking for stencil values that are all zero. The first pass sets the
+// clip bit if the stencil is all zeros. The second pass clears the user bits.
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiffPass0,
     kInvert_StencilOp,
     kZero_StencilOp,
     kEqual_StencilFunc,
@@ -175,6 +177,16 @@
     0x0000,
     0x0000           // set clip bit
 );
+
+GR_STATIC_CONST_SAME_STENCIL(gInvUserToClipRDiffPass1,
+    kZero_StencilOp,
+    kZero_StencilOp,
+    kAlways_StencilFunc,
+    0xffff,
+    0x0000,
+    0xffff           // unset clip bit
+);
+
 ///////
 // Direct to Stencil
 
@@ -183,7 +195,7 @@
 // only modify the in/out status of samples covered by the clip element.
 
 // this one only works if used right after stencil clip was cleared.
-// Our GrClip doesn't allow midstream replace ops.
+// Our clip mask creation code doesn't allow midstream replace ops.
 GR_STATIC_CONST_SAME_STENCIL(gReplaceClip,
     kReplace_StencilOp,
     kReplace_StencilOp,
@@ -220,28 +232,29 @@
     0x0000            // set clip bit
 );
 
-bool GrStencilSettings::GetClipPasses(GrSetOp op, 
-                                      bool canBeDirect,
-                                      unsigned int stencilClipMask,
-                                      bool invertedFill,
-                                      int* numPasses,
-                                      GrStencilSettings settings[kMaxStencilClipPasses]) {
+bool GrStencilSettings::GetClipPasses(
+                            SkRegion::Op op,
+                            bool canBeDirect,
+                            unsigned int stencilClipMask,
+                            bool invertedFill,
+                            int* numPasses,
+                            GrStencilSettings settings[kMaxStencilClipPasses]) {
     if (canBeDirect && !invertedFill) {
         *numPasses = 0;
         switch (op) {
-            case kReplace_SetOp:
+            case SkRegion::kReplace_Op:
                 *numPasses = 1;
                 settings[0] = gReplaceClip;
                 break;
-            case kUnion_SetOp:
+            case SkRegion::kUnion_Op:
                 *numPasses = 1;
                 settings[0] = gUnionClip;
                 break;
-            case kXor_SetOp:
+            case SkRegion::kXOR_Op:
                 *numPasses = 1;
                 settings[0] = gXorClip;
                 break;
-            case kDifference_SetOp:
+            case SkRegion::kDifference_Op:
                 *numPasses = 1;
                 settings[0] = gDiffClip;
                 break;
@@ -249,10 +262,12 @@
                 break;
         }
         if (1 == *numPasses) {
-            settings[0].fFrontFuncRef |= stencilClipMask;
-            settings[0].fFrontWriteMask |= stencilClipMask;
-            settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
-            settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+            settings[0].fFuncRefs[kFront_Face]   |= stencilClipMask;
+            settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
+            settings[0].fFuncRefs[kBack_Face] =
+                settings[0].fFuncRefs[kFront_Face];
+            settings[0].fWriteMasks[kBack_Face] =
+                settings[0].fWriteMasks[kFront_Face];
             return true;
         }
     }
@@ -260,92 +275,117 @@
         // if we make the path renderer go to stencil we always give it a
         // non-inverted fill and we use the stencil rules on the client->clipbit
         // pass to select either the zeros or nonzeros.
-        case kReplace_SetOp:
+        case SkRegion::kReplace_Op:
             *numPasses= 1;
-            settings[0] = invertedFill ? gInvUserToClipReplace : gUserToClipReplace;
-            settings[0].fFrontFuncMask &= ~stencilClipMask;
-            settings[0].fFrontFuncRef |= stencilClipMask;
-            settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
-            settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+            settings[0] = invertedFill ? gInvUserToClipReplace :
+                                         gUserToClipReplace;
+            settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+            settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
+            settings[0].fFuncMasks[kBack_Face] =
+                settings[0].fFuncMasks[kFront_Face];
+            settings[0].fFuncRefs[kBack_Face] =
+                settings[0].fFuncRefs[kFront_Face];
             break;
-        case kIntersect_SetOp:
+        case SkRegion::kIntersect_Op:
             *numPasses = 1;
             settings[0] = invertedFill ? gInvUserToClipIsect : gUserToClipIsect;
-            settings[0].fFrontFuncRef = stencilClipMask;
-            settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+            settings[0].fFuncRefs[kFront_Face] = stencilClipMask;
+            settings[0].fFuncRefs[kBack_Face] =
+                settings[0].fFuncRefs[kFront_Face];
             break;
-        case kUnion_SetOp:
+        case SkRegion::kUnion_Op:
             *numPasses = 2;
             if (invertedFill) {
                 settings[0] = gInvUserToClipUnionPass0;
-                settings[0].fFrontFuncMask &= ~stencilClipMask;
-                settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
-                settings[0].fFrontFuncRef |= stencilClipMask;
-                settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
-                settings[0].fFrontWriteMask |= stencilClipMask;
-                settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+                settings[0].fFuncMasks[kBack_Face] =
+                    settings[0].fFuncMasks[kFront_Face];
+                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[0].fFuncRefs[kBack_Face] =
+                    settings[0].fFuncRefs[kFront_Face];
+                settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
+                settings[0].fWriteMasks[kBack_Face] =
+                    settings[0].fWriteMasks[kFront_Face];
 
                 settings[1] = gInvUserToClipUnionPass1;
-                settings[1].fFrontWriteMask &= ~stencilClipMask;
-                settings[1].fBackWriteMask &= settings[1].fFrontWriteMask;
+                settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask;
+                settings[1].fWriteMasks[kBack_Face] &=
+                    settings[1].fWriteMasks[kFront_Face];
 
             } else {
                 settings[0] = gUserToClipUnionPass0;
-                settings[0].fFrontFuncMask &= ~stencilClipMask;
-                settings[0].fFrontFuncRef |= stencilClipMask;
-                settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
-                settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[0].fFuncMasks[kBack_Face] =
+                    settings[0].fFuncMasks[kFront_Face];
+                settings[0].fFuncRefs[kBack_Face] =
+                    settings[0].fFuncRefs[kFront_Face];
 
                 settings[1] = gUserToClipUnionPass1;
-                settings[1].fFrontFuncRef |= stencilClipMask;
-                settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncRefs[kBack_Face] =
+                    settings[1].fFuncRefs[kFront_Face];
             }
             break;
-        case kXor_SetOp:
+        case SkRegion::kXOR_Op:
             *numPasses = 2;
             if (invertedFill) {
                 settings[0] = gInvUserToClipXorPass0;
-                settings[0].fFrontFuncMask &= ~stencilClipMask;
-                settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+                settings[0].fFuncMasks[kBack_Face] =
+                    settings[0].fFuncMasks[kFront_Face];
 
                 settings[1] = gInvUserToClipXorPass1;
-                settings[1].fFrontFuncRef |= stencilClipMask;
-                settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncRefs[kBack_Face] =
+                    settings[1].fFuncRefs[kFront_Face];
             } else {
                 settings[0] = gUserToClipXorPass0;
-                settings[0].fFrontFuncMask &= ~stencilClipMask;
-                settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
+                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+                settings[0].fFuncMasks[kBack_Face] =
+                    settings[0].fFuncMasks[kFront_Face];
 
                 settings[1] = gUserToClipXorPass1;
-                settings[1].fFrontFuncRef |= stencilClipMask;
-                settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncRefs[kBack_Face] =
+                    settings[1].fFuncRefs[kFront_Face];
             }
             break;
-        case kDifference_SetOp:
+        case SkRegion::kDifference_Op:
             *numPasses = 1;
             settings[0] = invertedFill ? gInvUserToClipDiff : gUserToClipDiff;
-            settings[0].fFrontFuncRef |= stencilClipMask;
-            settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+            settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
+            settings[0].fFuncRefs[kBack_Face] =
+                settings[0].fFuncRefs[kFront_Face];
             break;
-        case kReverseDifference_SetOp:
+        case SkRegion::kReverseDifference_Op:
             if (invertedFill) {
-                *numPasses = 1;
-                settings[0] = gInvUserToClipRDiff;
-                settings[0].fFrontWriteMask |= stencilClipMask;
-                settings[0].fBackWriteMask = settings[0].fFrontWriteMask;
+                *numPasses = 2;
+                settings[0] = gInvUserToClipRDiffPass0;
+                settings[0].fWriteMasks[kFront_Face] |= stencilClipMask;
+                settings[0].fWriteMasks[kBack_Face] =
+                    settings[0].fWriteMasks[kFront_Face];
+                settings[1] = gInvUserToClipRDiffPass1;
+                settings[1].fWriteMasks[kFront_Face] &= ~stencilClipMask;
+                settings[1].fWriteMasks[kBack_Face] =
+                    settings[1].fWriteMasks[kFront_Face];
             } else {
                 *numPasses = 2;
                 settings[0] = gUserToClipRDiffPass0;
-                settings[0].fFrontFuncMask &= ~stencilClipMask;
-                settings[0].fBackFuncMask = settings[0].fFrontFuncMask;
-                settings[0].fFrontFuncRef |= stencilClipMask;
-                settings[0].fBackFuncRef = settings[0].fFrontFuncRef;
+                settings[0].fFuncMasks[kFront_Face] &= ~stencilClipMask;
+                settings[0].fFuncMasks[kBack_Face] =
+                    settings[0].fFuncMasks[kFront_Face];
+                settings[0].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[0].fFuncRefs[kBack_Face] =
+                    settings[0].fFuncRefs[kFront_Face];
 
                 settings[1] = gUserToClipRDiffPass1;
-                settings[1].fFrontFuncMask |= stencilClipMask;
-                settings[1].fFrontFuncRef |= stencilClipMask;
-                settings[1].fBackFuncMask = settings[1].fFrontFuncMask;
-                settings[1].fBackFuncRef = settings[1].fFrontFuncRef;
+                settings[1].fFuncMasks[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncRefs[kFront_Face] |= stencilClipMask;
+                settings[1].fFuncMasks[kBack_Face] =
+                    settings[1].fFuncMasks[kFront_Face];
+                settings[1].fFuncRefs[kBack_Face] =
+                    settings[1].fFuncRefs[kFront_Face];
             }
             break;
         default:
diff --git a/src/gpu/GrStencil.h b/src/gpu/GrStencil.h
index 143e525..1af98e6 100644
--- a/src/gpu/GrStencil.h
+++ b/src/gpu/GrStencil.h
@@ -11,6 +11,8 @@
 #define GrStencil_DEFINED
 
 #include "GrTypes.h"
+#include "SkRegion.h"
+
 /**
  * Gr uses the stencil buffer to implement complex clipping inside the
  * GrDrawTarget class. The GrDrawTarget makes a subset of the stencil buffer
@@ -101,20 +103,14 @@
  * GrStencilSettings. (We hang our heads in shame.)
  */
 struct GrStencilSettingsStruct {
-    GrStencilOp fFrontPassOp : 8;    // op to perform when front faces pass
-    GrStencilOp fBackPassOp : 8;     // op to perform when back faces pass
-    GrStencilOp fFrontFailOp : 8;    // op to perform when front faces fail
-    GrStencilOp fBackFailOp : 8;     // op to perform when back faces fail
-    GrStencilFunc fFrontFunc : 8;    // test function for front faces
-    GrStencilFunc fBackFunc : 8;     // test function for back faces
-    int fPad0 : 8;
-    int fPad1 : 8;
-    unsigned short fFrontFuncMask;   // mask for front face test
-    unsigned short fBackFuncMask;    // mask for back face test
-    unsigned short fFrontFuncRef;    // reference value for front face test
-    unsigned short fBackFuncRef;     // reference value for back face test
-    unsigned short fFrontWriteMask;  // stencil write mask for front faces
-    unsigned short fBackWriteMask;   // stencil write mask for back faces
+    uint8_t fPassOps[2];     // op to perform when faces pass (GrStencilOp)
+    uint8_t fFailOps[2];     // op to perform when faces fail (GrStencilOp)
+    uint8_t fFuncs[2];       // test function for faces (GrStencilFunc)
+    uint8_t fPad0;
+    uint8_t fPad1;
+    uint16_t fFuncMasks[2];  // mask for face tests
+    uint16_t fFuncRefs[2];   // reference values for face tests
+    uint16_t fWriteMasks[2]; // stencil write masks
     mutable uint32_t fFlags;
 };
 // We rely on this being packed and aligned (memcmp'ed and memcpy'ed)
@@ -123,9 +119,9 @@
                  4*sizeof(uint8_t) + // ops
                  2*sizeof(uint8_t) + // funcs
                  2*sizeof(uint8_t) + // pads
-                 2*sizeof(unsigned short) + // func masks
-                 2*sizeof(unsigned short) + // ref values
-                 2*sizeof(unsigned short) + // write masks
+                 2*sizeof(uint16_t) + // func masks
+                 2*sizeof(uint16_t) + // ref values
+                 2*sizeof(uint16_t) + // write masks
                  sizeof(uint32_t)); // flags
 
 // This macro is used to compute the GrStencilSettingsStructs flags
@@ -173,36 +169,39 @@
 class GrStencilSettings : private GrStencilSettingsStruct {
 
 public:
+    enum Face {
+        kFront_Face = 0,
+        kBack_Face  = 1,
+    };
+
     GrStencilSettings() {
         fPad0 = fPad1 = 0;
         this->setDisabled();
     }
-    
-    GrStencilOp frontPassOp() const { return fFrontPassOp; }
-    GrStencilOp backPassOp() const { return fBackPassOp; }
-    GrStencilOp frontFailOp() const { return fFrontFailOp; }
-    GrStencilOp backFailOp() const { return fBackFailOp; }
-    GrStencilFunc frontFunc() const { return fFrontFunc; }
-    GrStencilFunc backFunc() const { return fBackFunc; }
-    unsigned short frontFuncMask() const { return fFrontFuncMask; }
-    unsigned short backFuncMask() const { return fBackFuncMask; }
-    unsigned short frontFuncRef() const { return fFrontFuncRef; }
-    unsigned short backFuncRef() const { return fBackFuncRef; }
-    unsigned short frontWriteMask() const {return fFrontWriteMask; }
-    unsigned short backWriteMask() const { return fBackWriteMask; }
 
-    void setFrontPassOp(GrStencilOp op) { fFrontPassOp = op; fFlags = 0;}
-    void setBackPassOp(GrStencilOp op) { fBackPassOp = op; fFlags = 0;}
-    void setFrontFailOp(GrStencilOp op) {fFrontFailOp = op; fFlags = 0;}
-    void setBackFailOp(GrStencilOp op) { fBackFailOp = op; fFlags = 0;}
-    void setFrontFunc(GrStencilFunc func) { fFrontFunc = func; fFlags = 0;}
-    void setBackFunc(GrStencilFunc func) { fBackFunc = func; fFlags = 0;}
-    void setFrontFuncMask(unsigned short mask) { fFrontFuncMask = mask; }
-    void setBackFuncMask(unsigned short mask) { fBackFuncMask = mask; }
-    void setFrontFuncRef(unsigned short ref) { fFrontFuncRef = ref; }
-    void setBackFuncRef(unsigned short ref) { fBackFuncRef = ref; }
-    void setFrontWriteMask(unsigned short writeMask) { fFrontWriteMask = writeMask; }
-    void setBackWriteMask(unsigned short writeMask) { fBackWriteMask = writeMask; }
+    GrStencilOp passOp(Face f) const { return static_cast<GrStencilOp>(fPassOps[f]); }
+    GrStencilOp failOp(Face f) const { return static_cast<GrStencilOp>(fFailOps[f]); }
+    GrStencilFunc func(Face f) const { return static_cast<GrStencilFunc>(fFuncs[f]); }
+    uint16_t funcMask(Face f) const  { return fFuncMasks[f]; }
+    uint16_t funcRef(Face f) const   { return fFuncRefs[f]; }
+    uint16_t writeMask(Face f) const { return fWriteMasks[f]; }
+
+    void setPassOp(Face f, GrStencilOp op) { fPassOps[f] = op; fFlags = 0;}
+    void setFailOp(Face f, GrStencilOp op) { fFailOps[f] = op; fFlags = 0;}
+    void setFunc(Face f, GrStencilFunc func) { fFuncs[f] = func; fFlags = 0;}
+    void setFuncMask(Face f, unsigned short mask) { fFuncMasks[f] = mask; }
+    void setFuncRef(Face f, unsigned short ref) { fFuncRefs[f] = ref; }
+    void setWriteMask(Face f, unsigned short writeMask) { fWriteMasks[f] = writeMask; }
+
+    void copyFrontSettingsToBack() {
+        fPassOps[kBack_Face]    = fPassOps[kFront_Face];
+        fFailOps[kBack_Face]    = fFailOps[kFront_Face];
+        fFuncs[kBack_Face]      = fFuncs[kFront_Face];
+        fFuncMasks[kBack_Face]  = fFuncMasks[kFront_Face];
+        fFuncRefs[kBack_Face]   = fFuncRefs[kFront_Face];
+        fWriteMasks[kBack_Face] = fWriteMasks[kFront_Face];
+        fFlags = 0;
+    }
 
     void setSame(GrStencilOp passOp,
                  GrStencilOp failOp,
@@ -210,18 +209,12 @@
                  unsigned short funcMask,
                  unsigned short funcRef,
                  unsigned short writeMask) {
-        fFrontPassOp        = passOp;
-        fBackPassOp         = passOp;
-        fFrontFailOp        = failOp;
-        fBackFailOp         = failOp;
-        fFrontFunc          = func;
-        fBackFunc           = func;
-        fFrontFuncMask      = funcMask;
-        fBackFuncMask       = funcMask;
-        fFrontFuncRef       = funcRef;
-        fBackFuncRef        = funcRef;
-        fFrontWriteMask     = writeMask;
-        fBackWriteMask      = writeMask;
+        fPassOps[kFront_Face]    = fPassOps[kBack_Face]    = passOp;
+        fFailOps[kFront_Face]    = fFailOps[kBack_Face]    = failOp;
+        fFuncs[kFront_Face]      = fFuncs[kBack_Face]      = func;
+        fFuncMasks[kFront_Face]  = fFuncMasks[kBack_Face]  = funcMask;
+        fFuncRefs[kFront_Face]   = fFuncRefs[kBack_Face]   = funcRef;
+        fWriteMasks[kFront_Face] = fWriteMasks[kBack_Face] = writeMask;
         fFlags = 0;
     }
 
@@ -232,6 +225,26 @@
         fFlags = kIsDisabled_StencilFlag | kDoesNotWrite_StencilFlag;
     }
 
+    bool isTwoSided() const {
+        return fPassOps[kFront_Face]    != fPassOps[kBack_Face]   ||
+               fFailOps[kFront_Face]    != fFailOps[kBack_Face]   ||
+               fFuncs[kFront_Face]      != fFuncs[kBack_Face]     ||
+               fFuncMasks[kFront_Face]  != fFuncMasks[kBack_Face] ||
+               fFuncRefs[kFront_Face]   != fFuncRefs[kBack_Face]  ||
+               fWriteMasks[kFront_Face] != fWriteMasks[kBack_Face];
+    }
+
+    bool usesWrapOp() const {
+        return kIncWrap_StencilOp == fPassOps[kFront_Face] ||
+               kDecWrap_StencilOp == fPassOps[kFront_Face] ||
+               kIncWrap_StencilOp == fPassOps[kBack_Face]  ||
+               kDecWrap_StencilOp == fPassOps[kBack_Face]  ||
+               kIncWrap_StencilOp == fFailOps[kFront_Face] ||
+               kDecWrap_StencilOp == fFailOps[kFront_Face] ||
+               kIncWrap_StencilOp == fFailOps[kBack_Face]  ||
+               kDecWrap_StencilOp == fFailOps[kBack_Face];
+    }
+
     bool isDisabled() const {
         if (fFlags & kIsDisabled_StencilFlag) {
             return true;
@@ -240,9 +253,9 @@
             return false;
         }
         bool disabled = GR_STENCIL_SETTINGS_IS_DISABLED(
-                            fFrontPassOp, fBackPassOp,
-                            fFrontFailOp, fBackFailOp,
-                            fFrontFunc ,fBackFunc);
+                            fPassOps[kFront_Face], fPassOps[kBack_Face],
+                            fFailOps[kFront_Face], fFailOps[kBack_Face],
+                            fFuncs[kFront_Face],   fFuncs[kBack_Face]);
         fFlags |= disabled ? kIsDisabled_StencilFlag : kNotDisabled_StencilFlag;
         return disabled;
     }
@@ -255,43 +268,44 @@
             return false;
         }
         bool writes = GR_STENCIL_SETTINGS_DOES_WRITE(
-                        fFrontPassOp, fBackPassOp,
-                        fFrontFailOp, fBackFailOp,
-                        fFrontFunc, fBackFunc);
+                            fPassOps[kFront_Face], fPassOps[kBack_Face],
+                            fFailOps[kFront_Face], fFailOps[kBack_Face],
+                            fFuncs[kFront_Face],   fFuncs[kBack_Face]);
         fFlags |= writes ? kDoesWrite_StencilFlag : kDoesNotWrite_StencilFlag;
         return writes;
     }
-    
+
     void invalidate()  {
         // write an illegal value to the first member
-        fFrontPassOp = (GrStencilOp)(uint8_t)-1;
+        fPassOps[0] = (GrStencilOp)(uint8_t)-1;
         fFlags = 0;
     }
 
     bool operator == (const GrStencilSettings& s) const {
         static const size_t gCompareSize = sizeof(GrStencilSettings) -
                                            sizeof(fFlags);
-        GrAssert((const char*)&fFlags + sizeof(fFlags) == 
+        GrAssert((const char*)&fFlags + sizeof(fFlags) ==
                  (const char*)this + sizeof(GrStencilSettings));
         if (this->isDisabled() & s.isDisabled()) { // using & not &&
             return true;
         }
         return 0 == memcmp(this, &s, gCompareSize);
     }
-    
+
     bool operator != (const GrStencilSettings& s) const {
         return !(*this == s);
     }
-    
+
     GrStencilSettings& operator =(const GrStencilSettings& s) {
         memcpy(this, &s, sizeof(GrStencilSettings));
         return *this;
     }
 
 private:
-    friend class GrGpu;
+    friend class GrClipMaskManager;
+
     enum {
-        kMaxStencilClipPasses = 2  // maximum number of passes to add a clip 
+        kMaxStencilClipPasses = 2  // maximum number of passes to add a clip
                                    // element to the stencil buffer.
     };
 
@@ -302,15 +316,15 @@
      *      needs to be drawn to the client portion of the stencil first.
      *      2. How many passes are needed.
      *      3. What those passes are.
-     *      4. The fill rule that should actually be used to render (will 
+     *      4. The fill rule that should actually be used to render (will
      *         always be non-inverted).
      *
-     * @param op                the set op to combine this element with the 
+     * @param op                the set op to combine this element with the
      *                          existing clip
      * @param stencilClipMask   mask with just the stencil bit used for clipping
      *                          enabled.
      * @param invertedFill      is this path inverted
-     * @param numPasses         out: the number of passes needed to add the 
+     * @param numPasses         out: the number of passes needed to add the
      *                               element to the clip.
      * @param settings          out: the stencil settings to use for each pass
      *
@@ -318,7 +332,7 @@
      *         stencil clip bit. Will only be true if canBeDirect is true.
      *         numPasses will be 1 if return value is true.
      */
-    static bool GetClipPasses(GrSetOp op, 
+    static bool GetClipPasses(SkRegion::Op op,
                               bool canBeDirect,
                               unsigned int stencilClipMask,
                               bool invertedFill,
@@ -336,13 +350,13 @@
     FRONT_REF,        BACK_REF,                                              \
     FRONT_WRITE_MASK, BACK_WRITE_MASK)                                       \
     static const GrStencilSettingsStruct STRUCT_NAME = {                     \
-        (FRONT_PASS_OP),    (BACK_PASS_OP),                                  \
-        (FRONT_FAIL_OP),    (BACK_FAIL_OP),                                  \
-        (FRONT_FUNC),       (BACK_FUNC),                                     \
+       {(FRONT_PASS_OP),    (BACK_PASS_OP)   },                              \
+       {(FRONT_FAIL_OP),    (BACK_FAIL_OP)   },                              \
+       {(FRONT_FUNC),       (BACK_FUNC)      },                              \
         (0),                (0),                                             \
-        (FRONT_MASK),       (BACK_MASK),                                     \
-        (FRONT_REF),        (BACK_REF),                                      \
-        (FRONT_WRITE_MASK), (BACK_WRITE_MASK),                               \
+       {(FRONT_MASK),       (BACK_MASK)      },                              \
+       {(FRONT_REF),        (BACK_REF)       },                              \
+       {(FRONT_WRITE_MASK), (BACK_WRITE_MASK)},                              \
         GR_STENCIL_SETTINGS_DEFAULT_FLAGS(                                   \
             FRONT_PASS_OP, BACK_PASS_OP, FRONT_FAIL_OP, BACK_FAIL_OP,        \
             FRONT_FUNC, BACK_FUNC)                                           \
diff --git a/src/gpu/GrStencilAndCoverPathRenderer.cpp b/src/gpu/GrStencilAndCoverPathRenderer.cpp
new file mode 100644
index 0000000..44da3f9
--- /dev/null
+++ b/src/gpu/GrStencilAndCoverPathRenderer.cpp
@@ -0,0 +1,122 @@
+
+/*
+ * 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 "GrStencilAndCoverPathRenderer.h"
+#include "GrContext.h"
+#include "GrGpu.h"
+#include "GrPath.h"
+#include "SkStrokeRec.h"
+
+GrPathRenderer* GrStencilAndCoverPathRenderer::Create(GrContext* context) {
+    GrAssert(NULL != context);
+    GrAssert(NULL != context->getGpu());
+    if (context->getGpu()->getCaps().pathStencilingSupport()) {
+        return SkNEW_ARGS(GrStencilAndCoverPathRenderer, (context->getGpu()));
+    } else {
+        return NULL;
+    }
+}
+
+GrStencilAndCoverPathRenderer::GrStencilAndCoverPathRenderer(GrGpu* gpu) {
+    GrAssert(gpu->getCaps().pathStencilingSupport());
+    fGpu = gpu;
+    gpu->ref();
+}
+
+GrStencilAndCoverPathRenderer::~GrStencilAndCoverPathRenderer() {
+    fGpu->unref();
+}
+
+bool GrStencilAndCoverPathRenderer::canDrawPath(const SkPath& path,
+                                                const SkStrokeRec& stroke,
+                                                const GrDrawTarget* target,
+                                                bool antiAlias) const {
+    return stroke.isFillStyle() &&
+           !antiAlias && // doesn't do per-path AA, relies on the target having MSAA
+           target->getDrawState().getStencil().isDisabled();
+}
+
+GrPathRenderer::StencilSupport GrStencilAndCoverPathRenderer::onGetStencilSupport(
+                                                        const SkPath&,
+                                                        const SkStrokeRec& ,
+                                                        const GrDrawTarget*) const {
+    return GrPathRenderer::kStencilOnly_StencilSupport;
+}
+
+void GrStencilAndCoverPathRenderer::onStencilPath(const SkPath& path,
+                                                  const SkStrokeRec& stroke,
+                                                  GrDrawTarget* target) {
+    GrAssert(!path.isInverseFillType());
+    SkAutoTUnref<GrPath> p(fGpu->createPath(path));
+    target->stencilPath(p, stroke, path.getFillType());
+}
+
+bool GrStencilAndCoverPathRenderer::onDrawPath(const SkPath& path,
+                                               const SkStrokeRec& stroke,
+                                               GrDrawTarget* target,
+                                               bool antiAlias) {
+    GrAssert(!antiAlias);
+    GrAssert(!stroke.isHairlineStyle());
+
+    GrDrawState* drawState = target->drawState();
+    GrAssert(drawState->getStencil().isDisabled());
+
+    SkAutoTUnref<GrPath> p(fGpu->createPath(path));
+
+    SkPath::FillType nonInvertedFill = SkPath::ConvertToNonInverseFillType(path.getFillType());
+    target->stencilPath(p, stroke, nonInvertedFill);
+
+    // TODO: Use built in cover operation rather than a rect draw. This will require making our
+    // fragment shaders be able to eat varyings generated by a matrix.
+
+    // fill the path, zero out the stencil
+    GrRect bounds = p->getBounds();
+    SkScalar bloat = drawState->getViewMatrix().getMaxStretch() * SK_ScalarHalf;
+    GrDrawState::AutoDeviceCoordDraw adcd;
+
+    if (nonInvertedFill == path.getFillType()) {
+        GR_STATIC_CONST_SAME_STENCIL(kStencilPass,
+            kZero_StencilOp,
+            kZero_StencilOp,
+            kNotEqual_StencilFunc,
+            0xffff,
+            0x0000,
+            0xffff);
+        *drawState->stencil() = kStencilPass;
+    } else {
+        GR_STATIC_CONST_SAME_STENCIL(kInvertedStencilPass,
+            kZero_StencilOp,
+            kZero_StencilOp,
+            // We know our rect will hit pixels outside the clip and the user bits will be 0
+            // outside the clip. So we can't just fill where the user bits are 0. We also need to
+            // check that the clip bit is set.
+            kEqualIfInClip_StencilFunc,
+            0xffff,
+            0x0000,
+            0xffff);
+        SkMatrix vmi;
+        bounds.setLTRB(0, 0,
+                       SkIntToScalar(drawState->getRenderTarget()->width()),
+                       SkIntToScalar(drawState->getRenderTarget()->height()));
+        // mapRect through persp matrix may not be correct
+        if (!drawState->getViewMatrix().hasPerspective() && drawState->getViewInverse(&vmi)) {
+            vmi.mapRect(&bounds);
+            // theoretically could set bloat = 0, instead leave it because of matrix inversion
+            // precision.
+        } else {
+            adcd.set(drawState);
+            bloat = 0;
+        }
+        *drawState->stencil() = kInvertedStencilPass;
+    }
+    bounds.outset(bloat, bloat);
+    target->drawSimpleRect(bounds, NULL);
+    target->drawState()->stencil()->setDisabled();
+    return true;
+}
diff --git a/src/gpu/GrStencilAndCoverPathRenderer.h b/src/gpu/GrStencilAndCoverPathRenderer.h
new file mode 100644
index 0000000..9ebcec9
--- /dev/null
+++ b/src/gpu/GrStencilAndCoverPathRenderer.h
@@ -0,0 +1,55 @@
+
+/*
+ * 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 GrBuiltInPathRenderer_DEFINED
+#define GrBuiltInPathRenderer_DEFINED
+
+#include "GrPathRenderer.h"
+
+class GrContext;
+class GrGpu;
+
+/**
+ * Uses GrGpu::stencilPath followed by a cover rectangle. This subclass doesn't apply AA; it relies
+ * on the target having MSAA if AA is desired.
+ */
+class GrStencilAndCoverPathRenderer : public GrPathRenderer {
+public:
+
+    static GrPathRenderer* Create(GrContext*);
+
+    virtual ~GrStencilAndCoverPathRenderer();
+
+    virtual bool canDrawPath(const SkPath&,
+                             const SkStrokeRec&,
+                             const GrDrawTarget*,
+                             bool antiAlias) const SK_OVERRIDE;
+
+protected:
+    virtual StencilSupport onGetStencilSupport(const SkPath&,
+                                               const SkStrokeRec&,
+                                               const GrDrawTarget*) const SK_OVERRIDE;
+
+    virtual bool onDrawPath(const SkPath&,
+                            const SkStrokeRec&,
+                            GrDrawTarget*,
+                            bool antiAlias) SK_OVERRIDE;
+
+    virtual void onStencilPath(const SkPath&,
+                               const SkStrokeRec&,
+                               GrDrawTarget*) SK_OVERRIDE;
+
+private:
+    GrStencilAndCoverPathRenderer(GrGpu*);
+
+    GrGpu* fGpu;
+
+    typedef GrPathRenderer INHERITED;
+};
+
+#endif
diff --git a/src/gpu/GrStencilBuffer.cpp b/src/gpu/GrStencilBuffer.cpp
index 4b08e23..865961a 100644
--- a/src/gpu/GrStencilBuffer.cpp
+++ b/src/gpu/GrStencilBuffer.cpp
@@ -10,46 +10,40 @@
 
 #include "GrContext.h"
 #include "GrGpu.h"
+#include "GrResourceCache.h"
 
-void GrStencilBuffer::wasDetachedFromRenderTarget(const GrRenderTarget* rt) {
-    GrAssert(fRTAttachmentCnt > 0);
-    if (0 == --fRTAttachmentCnt) {
-        this->unlockInCache();
-        // At this point we could be deleted!
-    }
+SK_DEFINE_INST_COUNT(GrStencilBuffer)
+
+void GrStencilBuffer::transferToCache() {
+    GrAssert(NULL == this->getCacheEntry());
+
+    this->getGpu()->getContext()->addStencilBuffer(this);
 }
 
-void GrStencilBuffer::transferToCacheAndLock() {
-    GrAssert(NULL == fCacheEntry);
-    fCacheEntry = 
-        this->getGpu()->getContext()->addAndLockStencilBuffer(this);
+namespace {
+// we should never have more than one stencil buffer with same combo of (width,height,samplecount)
+void gen_cache_id(int width, int height, int sampleCnt, GrCacheID* cacheID) {
+    static const GrCacheID::Domain gStencilBufferDomain = GrCacheID::GenerateDomain();
+    GrCacheID::Key key;
+    uint32_t* keyData = key.fData32;
+    keyData[0] = width;
+    keyData[1] = height;
+    keyData[2] = sampleCnt;
+    memset(keyData + 3, 0, sizeof(key) - 3 * sizeof(uint32_t));
+    GR_STATIC_ASSERT(sizeof(key) >= 3 * sizeof(uint32_t));
+    cacheID->reset(gStencilBufferDomain, key);
+}
 }
 
-void GrStencilBuffer::onRelease() {
-    // When the GrGpu rips through its list of resources and releases
-    // them it may release an SB before it releases its attached RTs.
-    // In that case when GrStencilBuffer sees its last detach it no
-    // long has a gpu ptr (gets nulled in GrResource::release()) and can't
-    // access the cache to unlock itself. So if we're being released and still
-    // have attachments go ahead and unlock now.
-    if (fRTAttachmentCnt) {
-        this->unlockInCache();
-        // we shouldn't be deleted here because some RT still has a ref on us.
-    }
-    fCacheEntry = NULL;
-}
+GrResourceKey GrStencilBuffer::ComputeKey(int width,
+                                          int height,
+                                          int sampleCnt) {
+    // All SBs are created internally to attach to RTs so they all use the same domain.
+    static const GrResourceKey::ResourceType gStencilBufferResourceType =
+        GrResourceKey::GenerateResourceType();
+    GrCacheID id;
+    gen_cache_id(width, height, sampleCnt, &id);
 
-void GrStencilBuffer::onAbandon() {
-    // we can use the same behavior as release.
-    this->onRelease();
-}
-
-void GrStencilBuffer::unlockInCache() {
-    if (NULL != fCacheEntry) {
-        GrGpu* gpu = this->getGpu();
-        if (NULL != gpu) {
-            GrAssert(NULL != gpu->getContext());
-            gpu->getContext()->unlockStencilBuffer(fCacheEntry);
-        }
-    }
+    // we don't use any flags for SBs currently.
+    return GrResourceKey(id, gStencilBufferResourceType, 0);
 }
diff --git a/src/gpu/GrStencilBuffer.h b/src/gpu/GrStencilBuffer.h
index 5249ce8..3765a4c 100644
--- a/src/gpu/GrStencilBuffer.h
+++ b/src/gpu/GrStencilBuffer.h
@@ -10,18 +10,19 @@
 #ifndef GrStencilBuffer_DEFINED
 #define GrStencilBuffer_DEFINED
 
-#include "GrClip.h"
+#include "GrClipData.h"
 #include "GrResource.h"
 
 class GrRenderTarget;
 class GrResourceEntry;
+class GrResourceKey;
 
 class GrStencilBuffer : public GrResource {
 public:
+    SK_DECLARE_INST_COUNT(GrStencilBuffer);
+
     virtual ~GrStencilBuffer() {
-        // currently each rt that has attached this sb keeps a ref
         // TODO: allow SB to be purged and detach itself from rts
-        GrAssert(0 == fRTAttachmentCnt);
     }
 
     int width() const { return fWidth; }
@@ -30,75 +31,50 @@
     int numSamples() const { return fSampleCnt; }
 
     // called to note the last clip drawn to this buffer.
-    void setLastClip(const GrClip& clip, int width, int height) {
-        fLastClip = clip;
-        fLastClipWidth = width;
-        fLastClipHeight = height;
-        GrAssert(width <= fWidth);
-        GrAssert(height <= fHeight);
+    void setLastClip(int32_t clipStackGenID,
+                     const SkIRect& clipSpaceRect,
+                     const SkIPoint clipSpaceToStencilOffset) {
+        fLastClipStackGenID = clipStackGenID;
+        fLastClipStackRect = clipSpaceRect;
+        fLastClipSpaceOffset = clipSpaceToStencilOffset;
     }
 
     // called to determine if we have to render the clip into SB.
-    bool mustRenderClip(const GrClip& clip, int width, int height) const {
-        // The clip is in device space. That is it doesn't scale to fit a
-        // smaller RT. It is just truncated on the right / bottom edges.
-        // Note that this assumes that the viewport origin never moves within
-        // the stencil buffer. This is valid today.
-        return width > fLastClipWidth ||
-               height > fLastClipHeight ||
-               clip != fLastClip;
+    bool mustRenderClip(int32_t clipStackGenID,
+                        const SkIRect& clipSpaceRect,
+                        const SkIPoint clipSpaceToStencilOffset) const {
+        return SkClipStack::kInvalidGenID == clipStackGenID ||
+               fLastClipStackGenID != clipStackGenID ||
+               fLastClipSpaceOffset != clipSpaceToStencilOffset ||
+               !fLastClipStackRect.contains(clipSpaceRect);
     }
 
-    const GrClip& getLastClip() const {
-        return fLastClip;
-    }
+    // Places the sb in the cache. The cache takes a ref of the stencil buffer.
+    void transferToCache();
 
-    // places the sb in the cache and locks it. Caller transfers
-    // a ref to the the cache which will unref when purged.
-    void transferToCacheAndLock();
-
-    void wasAttachedToRenderTarget(const GrRenderTarget* rt) {
-        ++fRTAttachmentCnt;
-    }
-
-    void wasDetachedFromRenderTarget(const GrRenderTarget* rt);
+    static GrResourceKey ComputeKey(int width, int height, int sampleCnt);
 
 protected:
-    GrStencilBuffer(GrGpu* gpu, int width, int height, int bits, int sampleCnt)
-        : GrResource(gpu)
+    GrStencilBuffer(GrGpu* gpu, bool isWrapped, int width, int height, int bits, int sampleCnt)
+        : GrResource(gpu, isWrapped)
         , fWidth(width)
         , fHeight(height)
         , fBits(bits)
         , fSampleCnt(sampleCnt)
-        , fLastClip()
-        , fLastClipWidth(-1)
-        , fLastClipHeight(-1)
-        , fCacheEntry(NULL)
-        , fRTAttachmentCnt(0) {
+        , fLastClipStackGenID(SkClipStack::kInvalidGenID) {
+        fLastClipStackRect.setEmpty();
     }
 
-    // GrResource overrides
-
-    // subclass override must call INHERITED::onRelease
-    virtual void onRelease();
-    // subclass override must call INHERITED::onAbandon
-    virtual void onAbandon();
-
 private:
 
-    void unlockInCache();
-
     int fWidth;
     int fHeight;
     int fBits;
     int fSampleCnt;
 
-    GrClip     fLastClip;
-    int        fLastClipWidth;
-    int        fLastClipHeight;
-
-    GrResourceEntry* fCacheEntry;
-    int              fRTAttachmentCnt;
+    int32_t     fLastClipStackGenID;
+    SkIRect     fLastClipStackRect;
+    SkIPoint    fLastClipSpaceOffset;
 
     typedef GrResource INHERITED;
 };
diff --git a/src/gpu/GrStringBuilder.h b/src/gpu/GrStringBuilder.h
deleted file mode 100644
index 558d041..0000000
--- a/src/gpu/GrStringBuilder.h
+++ /dev/null
@@ -1,19 +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 GrStringBuilder_DEFINED
-#define GrStringBuilder_DEFINED
-
-#include "SkString.h"
-
-typedef SkString GrStringBuilder;
-
-#endif
-
diff --git a/src/gpu/GrSurface.cpp b/src/gpu/GrSurface.cpp
new file mode 100644
index 0000000..47d9959
--- /dev/null
+++ b/src/gpu/GrSurface.cpp
@@ -0,0 +1,10 @@
+/*
+ * 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 "GrSurface.h"
+
+SK_DEFINE_INST_COUNT(GrSurface)
diff --git a/src/gpu/GrTBSearch.h b/src/gpu/GrTBSearch.h
index 2697f37..cee4f2a 100644
--- a/src/gpu/GrTBSearch.h
+++ b/src/gpu/GrTBSearch.h
@@ -18,7 +18,7 @@
         // we should insert it at 0
         return ~0;
     }
-    
+
     int high = count - 1;
     int low = 0;
     while (high > low) {
@@ -29,12 +29,12 @@
             high = index;
         }
     }
-    
+
     // check if we found it
     if (EQ(array[high], target)) {
         return high;
     }
-    
+
     // now return the ~ of where we should insert it
     if (LT(array[high], target)) {
         high += 1;
@@ -43,4 +43,3 @@
 }
 
 #endif
-
diff --git a/src/gpu/GrTDArray.h b/src/gpu/GrTDArray.h
deleted file mode 100644
index 731001a..0000000
--- a/src/gpu/GrTDArray.h
+++ /dev/null
@@ -1,216 +0,0 @@
-
-/*
- * Copyright 2010 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-
-#ifndef GrTDArray_DEFINED
-#define GrTDArray_DEFINED
-
-#include "GrTypes.h"
-#include "GrRefCnt.h"
-
-static int GrInitialArrayAllocationCount() {
-    return 4;
-}
-
-static int GrNextArrayAllocationCount(int count) {
-    return count + ((count + 1) >> 1);
-}
-
-template <typename T> class GrTDArray {
-public:
-    GrTDArray() : fArray(NULL), fAllocated(0), fCount(0) {}
-    GrTDArray(const GrTDArray& src) {
-        fCount = fAllocated = src.fCount;
-        fArray = (T*)GrMalloc(fAllocated * sizeof(T));
-        memcpy(fArray, src.fArray, fCount * sizeof(T));
-    }
-    ~GrTDArray() {
-        if (fArray) {
-            GrFree(fArray);
-        }
-    }
-
-    bool isEmpty() const { return 0 == fCount; }
-    int count() const { return fCount; }
-
-    const T& at(int index) const {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        return fArray[index];
-    }
-    T& at(int index) {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        return fArray[index];
-    }
-
-    const T& operator[](int index) const { return this->at(index); }
-    T& operator[](int index) { return this->at(index); }
-
-    GrTDArray& operator=(const GrTDArray& src) {
-        if (fAllocated < src.fCount) {
-            fAllocated = src.fCount;
-            GrFree(fArray);
-            fArray = (T*)GrMalloc(fAllocated * sizeof(T));
-        }
-        fCount = src.fCount;
-        memcpy(fArray, src.fArray, fCount * sizeof(T));
-        return *this;
-    }
-
-    void reset() {
-        if (fArray) {
-            GrFree(fArray);
-            fArray = NULL;
-        }
-        fAllocated = fCount = 0;
-    }
-
-    T* begin() const { return fArray; }
-    T* end() const { return fArray + fCount; }
-    T* back() const { GrAssert(fCount); return fArray + (fCount - 1); } 
-
-    T* prepend() {
-        this->growAt(0);
-        return fArray;
-    }
-
-    T* append() {
-        this->growAt(fCount);
-        return fArray + fCount - 1;
-    }
-
-    /**
-     *  index may be [0..count], so that you can insert at the end (like append)
-     */
-    T* insert(int index) {
-        GrAssert((unsigned)index <= (unsigned)fCount);
-        this->growAt(index);
-        return fArray + index;
-    }
-
-    void remove(int index) {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        fCount -= 1;
-        if (index < fCount) {
-            int remaining = fCount - index;
-            memmove(fArray + index, fArray + index + 1, remaining * sizeof(T));
-        }
-    }
-
-    void removeShuffle(int index) {
-        GrAssert((unsigned)index < (unsigned)fCount);
-        fCount -= 1;
-        if (index < fCount) {
-            memmove(fArray + index, fArray + fCount, sizeof(T));
-        }
-    }
-
-    // Utility iterators
-
-    /**
-     *  Calls GrFree() on each element. Assumes each is NULL or was allocated
-     *  with GrMalloc().
-     */
-    void freeAll() {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            GrFree(*curr);
-        }
-        this->reset();
-    }
-
-    /**
-     *  Calls delete on each element. Assumes each is NULL or was allocated
-     *  with new.
-     */
-    void deleteAll() {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            delete *curr;
-        }
-        this->reset();
-    }
-
-    /**
-     *  Calls GrSafeUnref() on each element. Assumes each is NULL or is a
-     *  subclass of GrRefCnt.
-     */
-    void unrefAll() {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            GrSafeUnref(*curr);
-        }
-        this->reset();
-    }
-
-    void visit(void visitor(T&)) const {
-        T* stop = this->end();
-        for (T* curr = this->begin(); curr < stop; curr++) {
-            if (*curr) {
-                visitor(*curr);
-            }
-        }
-    }
-
-    int find(const T& elem) const {
-        int count = this->count();
-        T* curr = this->begin();
-        for (int i = 0; i < count; i++) {
-            if (elem == curr[i]) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    friend bool operator==(const GrTDArray<T>& a, const GrTDArray<T>& b) {
-        return a.count() == b.count() &&
-               (0 == a.count() ||
-                0 == memcmp(a.begin(), b.begin(), a.count() * sizeof(T)));
-    }
-    friend bool operator!=(const GrTDArray<T>& a, const GrTDArray<T>& b) {
-        return !(a == b);
-    }
-
-private:
-    T*  fArray;
-    int fAllocated, fCount;
-
-    // growAt will increment fCount, reallocate fArray (as needed), and slide
-    // the contents of fArray to make a hole for new data at index.
-    void growAt(int index) {
-        GrAssert(fCount <= fAllocated);
-        if (0 == fAllocated) {
-            fAllocated = GrInitialArrayAllocationCount();
-            fArray = (T*)GrMalloc(fAllocated * sizeof(T));
-        } else if (fCount == fAllocated) {
-            fAllocated = GrNextArrayAllocationCount(fAllocated);
-            T* newArray = (T*)GrMalloc(fAllocated * sizeof(T));
-            memcpy(newArray, fArray, index * sizeof(T));
-            memcpy(newArray + index + 1, fArray + index,
-                   (fCount - index) * sizeof(T));
-            GrFree(fArray);
-            fArray = newArray;
-        } else {
-            // check that we're not just appending
-            if (index < fCount) {
-                memmove(fArray + index + 1, fArray + index,
-                        (fCount - index) * sizeof(T));
-            }
-        }
-        GrAssert(fCount < fAllocated);
-        fCount += 1;
-    }
-};
-
-extern void* GrTDArray_growAt(void*, int* allocated, int& count, int index,
-                              size_t);
-
-
-#endif
-
diff --git a/src/gpu/GrTHashCache.h b/src/gpu/GrTHashCache.h
index 8651c35..b73b574 100644
--- a/src/gpu/GrTHashCache.h
+++ b/src/gpu/GrTHashCache.h
@@ -11,7 +11,17 @@
 #ifndef GrTHashCache_DEFINED
 #define GrTHashCache_DEFINED
 
-#include "GrTDArray.h"
+#include "GrTypes.h"
+#include "SkTDArray.h"
+
+// GrTDefaultFindFunctor implements the default find behavior for
+// GrTHashTable (i.e., return the first resource that matches the
+// provided key)
+template <typename T> class GrTDefaultFindFunctor {
+public:
+    // always accept the first element examined
+    bool operator()(const T* elem) const { return true; }
+};
 
 /**
  *  Key needs
@@ -29,6 +39,7 @@
 
     int count() const { return fSorted.count(); }
     T*  find(const Key&) const;
+    template <typename FindFuncType> T*  find(const Key&, const FindFuncType&) const;
     // return true if key was unique when inserted.
     bool insert(const Key&, T*);
     void remove(const Key&, const T*);
@@ -48,7 +59,7 @@
 #endif
 
     // testing
-    const GrTDArray<T*>& getArray() const { return fSorted; }
+    const SkTDArray<T*>& getArray() const { return fSorted; }
 private:
     enum {
         kHashCount = 1 << kHashBits,
@@ -63,7 +74,7 @@
     }
 
     mutable T* fHash[kHashCount];
-    GrTDArray<T*> fSorted;
+    SkTDArray<T*> fSorted;
 
     // search fSorted, and return the found index, or ~index of where it
     // should be inserted
@@ -109,20 +120,43 @@
 
 template <typename T, typename Key, size_t kHashBits>
 T* GrTHashTable<T, Key, kHashBits>::find(const Key& key) const {
+    GrTDefaultFindFunctor<T> find;
+
+    return this->find(key, find);
+}
+
+template <typename T, typename Key, size_t kHashBits>
+template <typename FindFuncType>
+T* GrTHashTable<T, Key, kHashBits>::find(const Key& key, const FindFuncType& findFunc) const {
+
     int hashIndex = hash2Index(key.getHash());
     T* elem = fHash[hashIndex];
 
-    if (NULL == elem || !Key::EQ(*elem, key)) {
-        // bsearch for the key in our sorted array
-        int index = this->searchArray(key);
-        if (index < 0) {
-            return NULL;
-        }
-        elem = fSorted[index];
-        // update the hash
-        fHash[hashIndex] = elem;
+    if (NULL != elem && Key::EQ(*elem, key) && findFunc(elem)) {
+        return elem;
     }
-    return elem;
+
+    // bsearch for the key in our sorted array
+    int index = this->searchArray(key);
+    if (index < 0) {
+        return NULL;
+    }
+
+    const T* const* array = fSorted.begin();
+
+    // above search should have found the first occurrence if there
+    // are multiple.
+    GrAssert(0 == index || Key::LT(*array[index - 1], key));
+
+    for ( ; index < count() && Key::EQ(*array[index], key); ++index) {
+        if (findFunc(fSorted[index])) {
+            // update the hash
+            fHash[hashIndex] = fSorted[index];
+            return fSorted[index];
+        }
+    }
+
+    return NULL;
 }
 
 template <typename T, typename Key, size_t kHashBits>
@@ -193,13 +227,6 @@
 #if GR_DEBUG
 template <typename T, typename Key, size_t kHashBits>
 void GrTHashTable<T, Key, kHashBits>::validate() const {
-    for (size_t i = 0; i < GR_ARRAY_COUNT(fHash); i++) {
-        if (fHash[i]) {
-            unsigned hashIndex = hash2Index(Key::GetHash(*fHash[i]));
-            GrAssert(hashIndex == i);
-        }
-    }
-
     int count = fSorted.count();
     for (int i = 1; i < count; i++) {
         GrAssert(Key::LT(*fSorted[i - 1], *fSorted[i]) ||
@@ -216,4 +243,3 @@
 #endif
 
 #endif
-
diff --git a/src/gpu/GrTLList.h b/src/gpu/GrTLList.h
deleted file mode 100644
index ea310ac..0000000
--- a/src/gpu/GrTLList.h
+++ /dev/null
@@ -1,54 +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 GrTLList_DEFINED
-#define GrTLList_DEFINED
-
-#include "GrNoncopyable.h"
-
-template <typename T> class GrTLList : GrNoncopyable {
-public:
-    class Entry {
-        Entry*  fPrev;
-        Entry*  fNext;
-    };
-
-    GrTLList() : fHead(NULL), fTail(NULL) {}
-#if GR_DEBUG
-    ~GrTLList() {
-        GrAssert(NULL == fHead);
-        GrAssert(NULL == ftail);
-    }
-#endif
-
-    T*  head() const { return fHead; }
-    T*  tail() const { return fTail; }
-    
-    void addToHead(T*);
-    void addToTail(T*);
-    void removeFromList(T*);
-
-private:
-    Entry*  fHead;
-    Entry*  fTail;
-
-    friend class Entry;
-};
-
-
-class Parent {
-    GrTDLList<Child>    fList;
-};
-
-class Child : public GrTLList::Entry<Child> {
-};
-
-#endif
-
diff --git a/src/gpu/GrTemplates.h b/src/gpu/GrTemplates.h
new file mode 100644
index 0000000..69720dc
--- /dev/null
+++ b/src/gpu/GrTemplates.h
@@ -0,0 +1,70 @@
+
+/*
+ * 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 GrTemplates_DEFINED
+#define GrTemplates_DEFINED
+
+#include "GrNoncopyable.h"
+
+/**
+ *  Use to cast a ptr to a different type, and maintain strict-aliasing
+ */
+template <typename Dst, typename Src> Dst GrTCast(Src src) {
+    union {
+        Src src;
+        Dst dst;
+    } data;
+    data.src = src;
+    return data.dst;
+}
+
+/**
+ * takes a T*, saves the value it points to,  in and restores the value in the
+ * destructor
+ * e.g.:
+ * {
+ *      GrAutoTRestore<int*> autoCountRestore;
+ *      if (useExtra) {
+ *          autoCountRestore.reset(&fCount);
+ *          fCount += fExtraCount;
+ *      }
+ *      ...
+ * }  // fCount is restored
+ */
+template <typename T> class GrAutoTRestore : public GrNoncopyable {
+public:
+    GrAutoTRestore() : fPtr(NULL), fVal() {}
+
+    GrAutoTRestore(T* ptr) {
+        fPtr = ptr;
+        if (NULL != ptr) {
+            fVal = *ptr;
+        }
+    }
+
+    ~GrAutoTRestore() {
+        if (NULL != fPtr) {
+            *fPtr = fVal;
+        }
+    }
+
+    // restores previously saved value (if any) and saves value for passed T*
+    void reset(T* ptr) {
+        if (NULL != fPtr) {
+            *fPtr = fVal;
+        }
+        fPtr = ptr;
+        fVal = *ptr;
+    }
+private:
+    T* fPtr;
+    T  fVal;
+};
+
+#endif
diff --git a/src/gpu/GrTesselatedPathRenderer.cpp b/src/gpu/GrTesselatedPathRenderer.cpp
deleted file mode 100644
index 3823bbd..0000000
--- a/src/gpu/GrTesselatedPathRenderer.cpp
+++ /dev/null
@@ -1,609 +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 "GrTesselatedPathRenderer.h"
-
-#include "GrDrawState.h"
-#include "GrPathUtils.h"
-#include "GrPoint.h"
-#include "GrRenderTarget.h"
-#include "GrTDArray.h"
-
-#include "SkTemplates.h"
-
-#include <limits.h>
-#include <sk_glu.h>
-
-typedef GrTDArray<GrDrawState::Edge> GrEdgeArray;
-typedef GrTDArray<GrPoint> GrPointArray;
-typedef GrTDArray<uint16_t> GrIndexArray;
-typedef void (*TESSCB)();
-
-// limit the allowable vertex range to approximately half of the representable
-// IEEE exponent in order to avoid overflow when doing multiplies between
-// vertex components,
-const float kMaxVertexValue = 1e18f;
-
-static inline GrDrawState::Edge computeEdge(const GrPoint& p,
-                                             const GrPoint& q,
-                                             float sign) {
-    GrVec tangent = GrVec::Make(p.fY - q.fY, q.fX - p.fX);
-    float scale = sign / tangent.length();
-    float cross2 = p.fX * q.fY - q.fX * p.fY;
-    return GrDrawState::Edge(tangent.fX * scale,
-                              tangent.fY * scale,
-                              cross2 * scale);
-}
-
-static inline GrPoint sanitizePoint(const GrPoint& pt) {
-    GrPoint r;
-    r.fX = SkScalarPin(pt.fX, -kMaxVertexValue, kMaxVertexValue);
-    r.fY = SkScalarPin(pt.fY, -kMaxVertexValue, kMaxVertexValue);
-    return r;
-}
-
-class GrTess {
-public:
-    GrTess(int count, unsigned winding_rule) {
-        fTess = Sk_gluNewTess();
-        Sk_gluTessProperty(fTess, GLU_TESS_WINDING_RULE, winding_rule);
-        Sk_gluTessNormal(fTess, 0.0f, 0.0f, 1.0f);
-        Sk_gluTessCallback(fTess, GLU_TESS_BEGIN_DATA, (TESSCB) &beginCB);
-        Sk_gluTessCallback(fTess, GLU_TESS_VERTEX_DATA, (TESSCB) &vertexCB);
-        Sk_gluTessCallback(fTess, GLU_TESS_END_DATA, (TESSCB) &endCB);
-        Sk_gluTessCallback(fTess, GLU_TESS_EDGE_FLAG_DATA, (TESSCB) &edgeFlagCB);
-        Sk_gluTessCallback(fTess, GLU_TESS_COMBINE_DATA, (TESSCB) &combineCB);
-        fInVertices = new double[count * 3];
-    }
-    virtual ~GrTess() {
-        Sk_gluDeleteTess(fTess);
-        delete[] fInVertices;
-    }
-    void addVertex(const GrPoint& pt, int index) {
-        if (index > USHRT_MAX) return;
-        double* inVertex = &fInVertices[index * 3];
-        inVertex[0] = pt.fX;
-        inVertex[1] = pt.fY;
-        inVertex[2] = 0.0;
-        *fVertices.append() = pt;
-        Sk_gluTessVertex(fTess, inVertex, reinterpret_cast<void*>(index));
-    }
-    void addVertices(const GrPoint* points, const uint16_t* contours, int numContours) {
-        Sk_gluTessBeginPolygon(fTess, this);
-        size_t i = 0;
-        for (int j = 0; j < numContours; ++j) {
-            Sk_gluTessBeginContour(fTess);
-            size_t end = i + contours[j];
-            for (; i < end; ++i) {
-                addVertex(points[i], i);
-            }
-            Sk_gluTessEndContour(fTess);
-        }
-        Sk_gluTessEndPolygon(fTess);
-    }
-    GLUtesselator* tess() { return fTess; }
-    const GrPointArray& vertices() const { return fVertices; }
-protected:
-    virtual void begin(GLenum type) = 0;
-    virtual void vertex(int index) = 0;
-    virtual void edgeFlag(bool flag) = 0;
-    virtual void end() = 0;
-    virtual int combine(GLdouble coords[3], int vertexIndices[4], 
-                         GLfloat weight[4]) = 0;
-    static void beginCB(GLenum type, void* data) {
-        static_cast<GrTess*>(data)->begin(type);
-    }
-    static void vertexCB(void* vertexData, void* data) {
-        static_cast<GrTess*>(data)->vertex(reinterpret_cast<long>(vertexData));
-    }
-    static void edgeFlagCB(GLboolean flag, void* data) {
-        static_cast<GrTess*>(data)->edgeFlag(flag != 0);
-    }
-    static void endCB(void* data) {
-        static_cast<GrTess*>(data)->end();
-    }
-    static void combineCB(GLdouble coords[3], void* vertexData[4],
-                          GLfloat weight[4], void **outData, void* data) {
-        int vertexIndex[4];
-        vertexIndex[0] = reinterpret_cast<long>(vertexData[0]);
-        vertexIndex[1] = reinterpret_cast<long>(vertexData[1]);
-        vertexIndex[2] = reinterpret_cast<long>(vertexData[2]);
-        vertexIndex[3] = reinterpret_cast<long>(vertexData[3]);
-        GrTess* tess = static_cast<GrTess*>(data);
-        int outIndex = tess->combine(coords, vertexIndex, weight);
-        *reinterpret_cast<long*>(outData) = outIndex;
-    }
-protected:
-    GLUtesselator* fTess;
-    GrPointArray fVertices;
-    double* fInVertices;
-};
-
-class GrPolygonTess : public GrTess {
-public:
-    GrPolygonTess(int count, unsigned winding_rule)
-      : GrTess(count, winding_rule) {
-    }
-    ~GrPolygonTess() {
-    }
-    const GrIndexArray& indices() const { return fIndices; }
-protected:
-    virtual void begin(GLenum type) {
-        GR_DEBUGASSERT(type == GL_TRIANGLES);
-    }
-    virtual void vertex(int index) {
-        *fIndices.append() = index;
-    }
-    virtual void edgeFlag(bool flag) {}
-    virtual void end() {}
-    virtual int combine(GLdouble coords[3], int vertexIndices[4],
-                         GLfloat weight[4]) {
-        int index = fVertices.count();
-        GrPoint p = GrPoint::Make(static_cast<float>(coords[0]),
-                                  static_cast<float>(coords[1]));
-        *fVertices.append() = p;
-        return index;
-    }
-protected:
-    GrIndexArray fIndices;
-};
-
-class GrEdgePolygonTess : public GrPolygonTess {
-public:
-    GrEdgePolygonTess(int count, unsigned winding_rule, const SkMatrix& matrix)
-      : GrPolygonTess(count, winding_rule),
-        fMatrix(matrix),
-        fEdgeFlag(false),
-        fEdgeVertex(-1),
-        fTriStartVertex(-1),
-        fEdges(NULL) {
-    }
-    ~GrEdgePolygonTess() {
-        delete[] fEdges;
-    }
-    const GrDrawState::Edge* edges() const { return fEdges; }
-private:
-    void addEdge(int index0, int index1) {
-        GrPoint p = fVertices[index0];
-        GrPoint q = fVertices[index1];
-        fMatrix.mapPoints(&p, 1);
-        fMatrix.mapPoints(&q, 1);
-        p = sanitizePoint(p);
-        q = sanitizePoint(q);
-        if (p == q) return;
-        GrDrawState::Edge edge = computeEdge(p, q, 1.0f);
-        fEdges[index0 * 2 + 1] = edge;
-        fEdges[index1 * 2] = edge;
-    }
-    virtual void begin(GLenum type) {
-        GR_DEBUGASSERT(type == GL_TRIANGLES);
-        int count = fVertices.count() * 2;
-        fEdges = new GrDrawState::Edge[count];
-        memset(fEdges, 0, count * sizeof(GrDrawState::Edge));
-    }
-    virtual void edgeFlag(bool flag) {
-        fEdgeFlag = flag;
-    }
-    virtual void vertex(int index) {
-        bool triStart = fIndices.count() % 3 == 0;
-        GrPolygonTess::vertex(index);
-        if (fEdgeVertex != -1) {
-            if (triStart) {
-                addEdge(fEdgeVertex, fTriStartVertex);
-            } else {
-                addEdge(fEdgeVertex, index);
-            }
-        }
-        if (triStart) {
-            fTriStartVertex = index;
-        }
-        if (fEdgeFlag) {
-            fEdgeVertex = index;
-        } else {
-            fEdgeVertex = -1;
-        }
-    }
-    virtual void end() {
-        if (fEdgeVertex != -1) {
-            addEdge(fEdgeVertex, fTriStartVertex);
-        }
-    }
-    GrMatrix fMatrix;
-    bool fEdgeFlag;
-    int fEdgeVertex, fTriStartVertex;
-    GrDrawState::Edge* fEdges;
-};
-
-class GrBoundaryTess : public GrTess {
-public:
-    GrBoundaryTess(int count, unsigned winding_rule)
-      : GrTess(count, winding_rule),
-        fContourStart(0) {
-        Sk_gluTessProperty(fTess, GLU_TESS_BOUNDARY_ONLY, 1);
-    }
-    ~GrBoundaryTess() {
-    }
-    GrPointArray& contourPoints() { return fContourPoints; }
-    const GrIndexArray& contours() const { return fContours; }
-private:
-    virtual void begin(GLenum type) {
-        fContourStart = fContourPoints.count();
-    }
-    virtual void vertex(int index) {
-        *fContourPoints.append() = fVertices.at(index);
-    }
-    virtual void edgeFlag(bool flag) {}
-    virtual void end() {
-        *fContours.append() = fContourPoints.count() - fContourStart;
-    }
-    virtual int combine(GLdouble coords[3], int vertexIndices[4],
-                        GLfloat weight[4]) {
-        int index = fVertices.count();
-        *fVertices.append() = GrPoint::Make(static_cast<float>(coords[0]),
-                                            static_cast<float>(coords[1]));
-        return index;
-    }
-    GrPointArray fContourPoints;
-    GrIndexArray fContours;
-    size_t fContourStart;
-};
-
-static bool nearlyEqual(float a, float b) {
-    return fabsf(a - b) < 0.0001f;
-}
-
-static bool nearlyEqual(const GrPoint& a, const GrPoint& b) {
-    return nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY);
-}
-
-static bool parallel(const GrDrawState::Edge& a, const GrDrawState::Edge& b) {
-    return (nearlyEqual(a.fX, b.fX) && nearlyEqual(a.fY, b.fY)) ||
-           (nearlyEqual(a.fX, -b.fX) && nearlyEqual(a.fY, -b.fY));
-}
-
-static unsigned fill_type_to_glu_winding_rule(GrPathFill fill) {
-    switch (fill) {
-        case kWinding_PathFill:
-            return GLU_TESS_WINDING_NONZERO;
-        case kEvenOdd_PathFill:
-            return GLU_TESS_WINDING_ODD;
-        case kInverseWinding_PathFill:
-            return GLU_TESS_WINDING_POSITIVE;
-        case kInverseEvenOdd_PathFill:
-            return GLU_TESS_WINDING_ODD;
-        case kHairLine_PathFill:
-            return GLU_TESS_WINDING_NONZERO;  // FIXME:  handle this
-        default:
-            GrAssert(!"Unknown path fill!");
-            return 0;
-    }
-}
-
-GrTesselatedPathRenderer::GrTesselatedPathRenderer() {
-}
-
-static bool isCCW(const GrPoint* pts, int count) {
-    GrVec v1, v2;
-    do {
-        v1 = pts[1] - pts[0];
-        v2 = pts[2] - pts[1];
-        pts++;
-        count--;
-    } while (nearlyEqual(v1, v2) && count > 3);
-    return v1.cross(v2) < 0;
-}
-
-static bool validEdge(const GrDrawState::Edge& edge) {
-    return !(edge.fX == 0.0f && edge.fY == 0.0f && edge.fZ == 0.0f);
-}
-
-static size_t computeEdgesAndIntersect(const GrMatrix& matrix,
-                                       const GrMatrix& inverse,
-                                       GrPoint* vertices,
-                                       size_t numVertices,
-                                       GrEdgeArray* edges,
-                                       float sign) {
-    if (numVertices < 3) {
-        return 0;
-    }
-    matrix.mapPoints(vertices, numVertices);
-    if (sign == 0.0f) {
-        sign = isCCW(vertices, numVertices) ? -1.0f : 1.0f;
-    }
-    GrPoint p = sanitizePoint(vertices[numVertices - 1]);
-    for (size_t i = 0; i < numVertices; ++i) {
-        GrPoint q = sanitizePoint(vertices[i]);
-        if (p == q) {
-            continue;
-        }
-        GrDrawState::Edge edge = computeEdge(p, q, sign);
-        edge.fZ += 0.5f;    // Offset by half a pixel along the tangent.
-        *edges->append() = edge;
-        p = q;
-    }
-    int count = edges->count();
-    if (count == 0) {
-        return 0;
-    }
-    GrDrawState::Edge prev_edge = edges->at(0);
-    for (int i = 0; i < count; ++i) {
-        GrDrawState::Edge edge = edges->at(i < count - 1 ? i + 1 : 0);
-        if (parallel(edge, prev_edge)) {
-            // 3 points are collinear; offset by half the tangent instead
-            vertices[i].fX -= edge.fX * 0.5f;
-            vertices[i].fY -= edge.fY * 0.5f;
-        } else {
-            vertices[i] = prev_edge.intersect(edge);
-        }
-        inverse.mapPoints(&vertices[i], 1);
-        prev_edge = edge;
-    }
-    return edges->count();
-}
-
-bool GrTesselatedPathRenderer::onDrawPath(const SkPath& path,
-                                          GrPathFill fill,
-                                          const GrVec* translate,
-                                          GrDrawTarget* target,
-                                          GrDrawState::StageMask stageMask,
-                                          bool antiAlias) {
-
-    GrDrawTarget::AutoStateRestore asr(target);
-    GrDrawState* drawState = target->drawState();
-    // face culling doesn't make sense here
-    GrAssert(GrDrawState::kBoth_DrawFace == drawState->getDrawFace());
-
-    GrMatrix viewM = drawState->getViewMatrix();
-
-    GrScalar tol = GR_Scalar1;
-    tol = GrPathUtils::scaleToleranceToSrc(tol, viewM, path.getBounds());
-    GrScalar tolSqd = GrMul(tol, tol);
-
-    int subpathCnt;
-    int maxPts = GrPathUtils::worstCasePointCount(path, &subpathCnt, tol);
-
-    GrVertexLayout layout = 0;
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if ((1 << s) & stageMask) {
-            layout |= GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s);
-        }
-    }
-
-    bool inverted = GrIsFillInverted(fill);
-    if (inverted) {
-        maxPts += 4;
-        subpathCnt++;
-    }
-    if (maxPts > USHRT_MAX) {
-        return false;
-    }
-    SkAutoSTMalloc<8, GrPoint> baseMem(maxPts);
-    GrPoint* base = baseMem;
-    GrPoint* vert = base;
-    GrPoint* subpathBase = base;
-
-    SkAutoSTMalloc<8, uint16_t> subpathVertCount(subpathCnt);
-
-    GrPoint pts[4];
-    SkPath::Iter iter(path, false);
-
-    bool first = true;
-    int subpath = 0;
-
-    for (;;) {
-        switch (iter.next(pts)) {
-            case kMove_PathCmd:
-                if (!first) {
-                    subpathVertCount[subpath] = vert-subpathBase;
-                    subpathBase = vert;
-                    ++subpath;
-                }
-                *vert = pts[0];
-                vert++;
-                break;
-            case kLine_PathCmd:
-                *vert = pts[1];
-                vert++;
-                break;
-            case kQuadratic_PathCmd: {
-                GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
-                                                     tolSqd, &vert,
-                                                     GrPathUtils::quadraticPointCount(pts, tol));
-                break;
-            }
-            case kCubic_PathCmd: {
-                GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3],
-                                                 tolSqd, &vert,
-                                                 GrPathUtils::cubicPointCount(pts, tol));
-                break;
-            }
-            case kClose_PathCmd:
-                break;
-            case kEnd_PathCmd:
-                subpathVertCount[subpath] = vert-subpathBase;
-                ++subpath; // this could be only in debug
-                goto FINISHED;
-        }
-        first = false;
-    }
-FINISHED:
-    if (NULL != translate && 0 != translate->fX && 0 != translate->fY) {
-        for (int i = 0; i < vert - base; i++) {
-            base[i].offset(translate->fX, translate->fY);
-        }
-    }
-
-    if (inverted) {
-        GrRect bounds;
-        GrAssert(NULL != drawState->getRenderTarget());
-        bounds.setLTRB(0, 0,
-                       GrIntToScalar(drawState->getRenderTarget()->width()),
-                       GrIntToScalar(drawState->getRenderTarget()->height()));
-        GrMatrix vmi;
-        if (drawState->getViewInverse(&vmi)) {
-            vmi.mapRect(&bounds);
-        }
-        *vert++ = GrPoint::Make(bounds.fLeft, bounds.fTop);
-        *vert++ = GrPoint::Make(bounds.fLeft, bounds.fBottom);
-        *vert++ = GrPoint::Make(bounds.fRight, bounds.fBottom);
-        *vert++ = GrPoint::Make(bounds.fRight, bounds.fTop);
-        subpathVertCount[subpath++] = 4;
-    }
-
-    GrAssert(subpath == subpathCnt);
-    GrAssert((vert - base) <= maxPts);
-
-    size_t count = vert - base;
-
-    if (count < 3) {
-        return true;
-    }
-
-    if (subpathCnt == 1 && !inverted && path.isConvex()) {
-        if (antiAlias) {
-            GrEdgeArray edges;
-            GrMatrix inverse, matrix = drawState->getViewMatrix();
-            drawState->getViewInverse(&inverse);
-
-            count = computeEdgesAndIntersect(matrix, inverse, base, count, &edges, 0.0f);
-            size_t maxEdges = target->getMaxEdges();
-            if (count == 0) {
-                return true;
-            }
-            if (count <= maxEdges) {
-                // All edges fit; upload all edges and draw all verts as a fan
-                target->setVertexSourceToArray(layout, base, count);
-                drawState->setEdgeAAData(&edges[0], count);
-                target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
-            } else {
-                // Upload "maxEdges" edges and verts at a time, and draw as
-                // separate fans
-                for (size_t i = 0; i < count - 2; i += maxEdges - 2) {
-                    edges[i] = edges[0];
-                    base[i] = base[0];
-                    int size = GR_CT_MIN(count - i, maxEdges);
-                    target->setVertexSourceToArray(layout, &base[i], size);
-                    drawState->setEdgeAAData(&edges[i], size);
-                    target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, size);
-                }
-            }
-            drawState->setEdgeAAData(NULL, 0);
-        } else {
-            target->setVertexSourceToArray(layout, base, count);
-            target->drawNonIndexed(kTriangleFan_PrimitiveType, 0, count);
-        }
-        return true;
-    }
-
-    if (antiAlias) {
-        // Run the tesselator once to get the boundaries.
-        GrBoundaryTess btess(count, fill_type_to_glu_winding_rule(fill));
-        btess.addVertices(base, subpathVertCount, subpathCnt);
-
-        GrMatrix inverse, matrix = drawState->getViewMatrix();
-        if (!drawState->getViewInverse(&inverse)) {
-            return false;
-        }
-
-        if (btess.vertices().count() > USHRT_MAX) {
-            return false;
-        }
-
-        // Inflate the boundary, and run the tesselator again to generate
-        // interior polys.
-        const GrPointArray& contourPoints = btess.contourPoints();
-        const GrIndexArray& contours = btess.contours();
-        GrEdgePolygonTess ptess(contourPoints.count(), GLU_TESS_WINDING_NONZERO, matrix);
-
-        size_t i = 0;
-        Sk_gluTessBeginPolygon(ptess.tess(), &ptess);
-        for (int contour = 0; contour < contours.count(); ++contour) {
-            int count = contours[contour];
-            GrEdgeArray edges;
-            int newCount = computeEdgesAndIntersect(matrix, inverse, &btess.contourPoints()[i], count, &edges, 1.0f);
-            Sk_gluTessBeginContour(ptess.tess());
-            for (int j = 0; j < newCount; j++) {
-                ptess.addVertex(contourPoints[i + j], ptess.vertices().count());
-            }
-            i += count;
-            Sk_gluTessEndContour(ptess.tess());
-        }
-
-        Sk_gluTessEndPolygon(ptess.tess());
-
-        if (ptess.vertices().count() > USHRT_MAX) {
-            return false;
-        }
-
-        // Draw the resulting polys and upload their edge data.
-        drawState->enableState(GrDrawState::kEdgeAAConcave_StateBit);
-        const GrPointArray& vertices = ptess.vertices();
-        const GrIndexArray& indices = ptess.indices();
-        const GrDrawState::Edge* edges = ptess.edges();
-        GR_DEBUGASSERT(indices.count() % 3 == 0);
-        for (int i = 0; i < indices.count(); i += 3) {
-            GrPoint tri_verts[3];
-            int index0 = indices[i];
-            int index1 = indices[i + 1];
-            int index2 = indices[i + 2];
-            tri_verts[0] = vertices[index0];
-            tri_verts[1] = vertices[index1];
-            tri_verts[2] = vertices[index2];
-            GrDrawState::Edge tri_edges[6];
-            int t = 0;
-            const GrDrawState::Edge& edge0 = edges[index0 * 2];
-            const GrDrawState::Edge& edge1 = edges[index0 * 2 + 1];
-            const GrDrawState::Edge& edge2 = edges[index1 * 2];
-            const GrDrawState::Edge& edge3 = edges[index1 * 2 + 1];
-            const GrDrawState::Edge& edge4 = edges[index2 * 2];
-            const GrDrawState::Edge& edge5 = edges[index2 * 2 + 1];
-            if (validEdge(edge0) && validEdge(edge1)) {
-                tri_edges[t++] = edge0;
-                tri_edges[t++] = edge1;
-            }
-            if (validEdge(edge2) && validEdge(edge3)) {
-                tri_edges[t++] = edge2;
-                tri_edges[t++] = edge3;
-            }
-            if (validEdge(edge4) && validEdge(edge5)) {
-                tri_edges[t++] = edge4;
-                tri_edges[t++] = edge5;
-            }
-            drawState->setEdgeAAData(&tri_edges[0], t);
-            target->setVertexSourceToArray(layout, &tri_verts[0], 3);
-            target->drawNonIndexed(kTriangles_PrimitiveType, 0, 3);
-        }
-        drawState->setEdgeAAData(NULL, 0);
-        drawState->disableState(GrDrawState::kEdgeAAConcave_StateBit);
-        return true;
-    }
-
-    GrPolygonTess ptess(count, fill_type_to_glu_winding_rule(fill));
-    ptess.addVertices(base, subpathVertCount, subpathCnt);
-    const GrPointArray& vertices = ptess.vertices();
-    const GrIndexArray& indices = ptess.indices();
-    if (indices.count() > 0) {
-        target->setVertexSourceToArray(layout, vertices.begin(), vertices.count());
-        target->setIndexSourceToArray(indices.begin(), indices.count());
-        target->drawIndexed(kTriangles_PrimitiveType,
-                            0,
-                            0,
-                            vertices.count(),
-                            indices.count());
-    }
-    return true;
-}
-
-bool GrTesselatedPathRenderer::canDrawPath(const SkPath& path,
-                                           GrPathFill fill,
-                                           const GrDrawTarget* target,
-                                           bool antiAlias) const {
-    return kHairLine_PathFill != fill;
-}
-
diff --git a/src/gpu/GrTesselatedPathRenderer.h b/src/gpu/GrTesselatedPathRenderer.h
deleted file mode 100644
index 3d12ae9..0000000
--- a/src/gpu/GrTesselatedPathRenderer.h
+++ /dev/null
@@ -1,32 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-#ifndef GrTesselatedPathRenderer_DEFINED
-#define GrTesselatedPathRenderer_DEFINED
-
-#include "GrPathRenderer.h"
-
-class GrTesselatedPathRenderer : public GrPathRenderer {
-public:
-    GrTesselatedPathRenderer();
-
-    virtual bool canDrawPath(const SkPath& path,
-                             GrPathFill fill,
-                             const GrDrawTarget* target,
-                             bool antiAlias) const SK_OVERRIDE;
-
-    virtual bool onDrawPath(const SkPath& path,
-                            GrPathFill fill,
-                            const GrVec* translate,
-                            GrDrawTarget* target,
-                            GrDrawState::StageMask stageMask,
-                            bool antiAlias) SK_OVERRIDE;
-};
-
-#endif
diff --git a/src/gpu/GrTextContext.cpp b/src/gpu/GrTextContext.cpp
index c22b203..97e92fa 100644
--- a/src/gpu/GrTextContext.cpp
+++ b/src/gpu/GrTextContext.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2010 Google Inc.
  *
@@ -11,153 +10,100 @@
 #include "GrTextContext.h"
 #include "GrAtlas.h"
 #include "GrContext.h"
+#include "GrDrawTarget.h"
+#include "GrFontScaler.h"
+#include "GrGpuVertex.h"
+#include "GrIndexBuffer.h"
 #include "GrTextStrike.h"
 #include "GrTextStrike_impl.h"
-#include "GrFontScaler.h"
-#include "GrIndexBuffer.h"
-#include "GrGpuVertex.h"
-#include "GrDrawTarget.h"
+#include "SkPath.h"
+#include "SkStrokeRec.h"
 
 enum {
     kGlyphMaskStage = GrPaint::kTotalStages,
 };
 
 void GrTextContext::flushGlyphs() {
+    if (NULL == fDrawTarget) {
+        return;
+    }
+    GrDrawState* drawState = fDrawTarget->drawState();
     if (fCurrVertex > 0) {
-        GrDrawTarget::AutoStateRestore asr(fDrawTarget);
-        GrDrawState* drawState = fDrawTarget->drawState();
         // setup our sampler state for our text texture/atlas
-        GrSamplerState::Filter filter;
-        if (fExtMatrix.isIdentity()) {
-            filter = GrSamplerState::kNearest_Filter;
-        } else {
-            filter = GrSamplerState::kBilinear_Filter;
-        }
-        drawState->sampler(kGlyphMaskStage)->reset(
-            GrSamplerState::kRepeat_WrapMode,filter);
-
         GrAssert(GrIsALIGN4(fCurrVertex));
-        int nIndices = fCurrVertex + (fCurrVertex >> 1);
         GrAssert(fCurrTexture);
-        drawState->setTexture(kGlyphMaskStage, fCurrTexture);
+        GrTextureParams params(SkShader::kRepeat_TileMode, false);
+        drawState->createTextureEffect(kGlyphMaskStage, fCurrTexture, SkMatrix::I(), params);
 
         if (!GrPixelConfigIsAlphaOnly(fCurrTexture->config())) {
-            if (kOne_BlendCoeff != fPaint.fSrcBlendCoeff ||
-                kISA_BlendCoeff != fPaint.fDstBlendCoeff ||
-                fPaint.hasTexture()) {
+            if (kOne_GrBlendCoeff != fPaint.getSrcBlendCoeff() ||
+                kISA_GrBlendCoeff != fPaint.getDstBlendCoeff() ||
+                fPaint.hasColorStage()) {
                 GrPrintf("LCD Text will not draw correctly.\n");
             }
             // setup blend so that we get mask * paintColor + (1-mask)*dstColor
-            drawState->setBlendConstant(fPaint.fColor);
-            drawState->setBlendFunc(kConstC_BlendCoeff, kISC_BlendCoeff);
+            drawState->setBlendConstant(fPaint.getColor());
+            drawState->setBlendFunc(kConstC_GrBlendCoeff, kISC_GrBlendCoeff);
             // don't modulate by the paint's color in the frag since we're
             // already doing it via the blend const.
             drawState->setColor(0xffffffff);
         } else {
             // set back to normal in case we took LCD path previously.
-            drawState->setBlendFunc(fPaint.fSrcBlendCoeff, fPaint.fDstBlendCoeff);
-            drawState->setColor(fPaint.fColor);
+            drawState->setBlendFunc(fPaint.getSrcBlendCoeff(), fPaint.getDstBlendCoeff());
+            drawState->setColor(fPaint.getColor());
         }
 
+        int nGlyphs = fCurrVertex / 4;
         fDrawTarget->setIndexSourceToBuffer(fContext->getQuadIndexBuffer());
-
-        fDrawTarget->drawIndexed(kTriangles_PrimitiveType,
-                                 0, 0, fCurrVertex, nIndices);
+        fDrawTarget->drawIndexedInstances(kTriangles_GrPrimitiveType,
+                                          nGlyphs,
+                                          4, 6);
         fDrawTarget->resetVertexSource();
         fVertices = NULL;
         fMaxVertices = 0;
         fCurrVertex = 0;
-        fCurrTexture->unref();
-        fCurrTexture = NULL;
+        GrSafeSetNull(fCurrTexture);
     }
+    drawState->disableStages();
+    fDrawTarget = NULL;
 }
 
-GrTextContext::GrTextContext(GrContext* context,
-                             const GrPaint& paint,
-                             const GrMatrix* extMatrix) : fPaint(paint) {
+GrTextContext::GrTextContext(GrContext* context, const GrPaint& paint) : fPaint(paint) {
     fContext = context;
     fStrike = NULL;
 
     fCurrTexture = NULL;
     fCurrVertex = 0;
 
-    if (NULL != extMatrix) {
-        fExtMatrix = *extMatrix;
-    } else {
-        fExtMatrix = GrMatrix::I();
-    }
-    if (context->getClip().hasConservativeBounds()) {
-        if (!fExtMatrix.isIdentity()) {
-            GrMatrix inverse;
-            GrRect r = context->getClip().getConservativeBounds();
-            if (fExtMatrix.invert(&inverse)) {
-                inverse.mapRect(&r);
-                r.roundOut(&fClipRect);
-            }
-        } else {
-            context->getClip().getConservativeBounds().roundOut(&fClipRect);
-        }
-    } else {
-        fClipRect.setLargest();
-    }
+    const GrClipData* clipData = context->getClip();
 
-    // save the context's original matrix off and restore in destructor
-    // this must be done before getTextTarget.
-    fOrigViewMatrix = fContext->getMatrix();
-    fContext->setMatrix(fExtMatrix);
+    GrRect devConservativeBound;
+    clipData->fClipStack->getConservativeBounds(
+                                     -clipData->fOrigin.fX,
+                                     -clipData->fOrigin.fY,
+                                     context->getRenderTarget()->width(),
+                                     context->getRenderTarget()->height(),
+                                     &devConservativeBound);
 
-    /*
-     We need to call preConcatMatrix with our viewmatrix's inverse, for each
-     texture and mask in the paint. However, computing the inverse can be 
-     expensive, and its possible we may not have any textures or masks, so these
-     two loops are written such that we only compute the inverse (once) if we
-     need it. We do this on our copy of the paint rather than directly on the 
-     draw target because we re-provide the paint to the context when we have
-     to flush our glyphs or draw a glyph as a path midstream.
-    */
-    bool invVMComputed = false;
-    GrMatrix invVM;
-    for (int t = 0; t < GrPaint::kMaxTextures; ++t) {
-        if (NULL != fPaint.getTexture(t)) {
-            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
-                invVMComputed = true;
-                fPaint.textureSampler(t)->preConcatMatrix(invVM);
-            }
-        }
-    }
-    for (int m = 0; m < GrPaint::kMaxMasks; ++m) {
-        if (NULL != fPaint.getMask(m)) {
-            if (invVMComputed || fOrigViewMatrix.invert(&invVM)) {
-                invVMComputed = true;
-                fPaint.maskSampler(m)->preConcatMatrix(invVM);
-            }
-        }
-    }
+    devConservativeBound.roundOut(&fClipRect);
 
-    fDrawTarget = fContext->getTextTarget(fPaint);
+    fAutoMatrix.setIdentity(fContext, &fPaint);
+
+    fDrawTarget = NULL;
 
     fVertices = NULL;
     fMaxVertices = 0;
 
-    fVertexLayout = 
-        GrDrawTarget::kTextFormat_VertexLayoutBit |
-        GrDrawTarget::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
-
-    int stageMask = paint.getActiveStageMask();
-    if (stageMask) {
-        for (int i = 0; i < GrPaint::kTotalStages; ++i) {
-            if ((1 << i) & stageMask) {
-                fVertexLayout |= 
-                    GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(i);
-                GrAssert(i != kGlyphMaskStage);
-            }
-        }
-    }
+    fVertexLayout =
+        GrDrawState::kTextFormat_VertexLayoutBit |
+        GrDrawState::StageTexCoordVertexLayoutBit(kGlyphMaskStage, 0);
 }
 
 GrTextContext::~GrTextContext() {
     this->flushGlyphs();
-    fContext->setMatrix(fOrigViewMatrix);
+    if (fDrawTarget) {
+        fDrawTarget->drawState()->disableStages();
+    }
 }
 
 void GrTextContext::flush() {
@@ -184,8 +130,8 @@
         return;
     }
 
-    vx += GrIntToFixed(glyph->fBounds.fLeft);
-    vy += GrIntToFixed(glyph->fBounds.fTop);
+    vx += SkIntToFixed(glyph->fBounds.fLeft);
+    vy += SkIntToFixed(glyph->fBounds.fTop);
 
     // keep them as ints until we've done the clip-test
     GrFixed width = glyph->fBounds.width();
@@ -196,7 +142,7 @@
         int x = vx >> 16;
         int y = vy >> 16;
         if (fClipRect.quickReject(x, y, x + width, y + height)) {
-//            Gr_clz(3);    // so we can set a break-point in the debugger
+//            SkCLZ(3);    // so we can set a break-point in the debugger
             return;
         }
     }
@@ -208,7 +154,7 @@
 
         // before we purge the cache, we must flush any accumulated draws
         this->flushGlyphs();
-        fContext->flushText();
+        fContext->flush();
 
         // try to purge
         fContext->getFontCache()->purgeExceptFor(fStrike);
@@ -217,7 +163,7 @@
         }
 
         if (NULL == glyph->fPath) {
-            GrPath* path = new GrPath;
+            SkPath* path = SkNEW(SkPath);
             if (!scaler->getGlyphPath(glyph->glyphID(), path)) {
                 // flag the glyph as being dead?
                 delete path;
@@ -226,11 +172,14 @@
             glyph->fPath = path;
         }
 
-        GrPoint translate;
-        translate.set(GrFixedToScalar(vx - GrIntToFixed(glyph->fBounds.fLeft)),
-                      GrFixedToScalar(vy - GrIntToFixed(glyph->fBounds.fTop)));
-        fContext->drawPath(fPaint, *glyph->fPath, kWinding_PathFill,
-                           &translate);
+        GrContext::AutoMatrix am;
+        SkMatrix translate;
+        translate.setTranslate(SkFixedToScalar(vx - SkIntToFixed(glyph->fBounds.fLeft)),
+                               SkFixedToScalar(vy - SkIntToFixed(glyph->fBounds.fTop)));
+        GrPaint tmpPaint(fPaint);
+        am.setPreConcat(fContext, translate, &tmpPaint);
+        SkStrokeRec stroke(SkStrokeRec::kFill_InitStyle);
+        fContext->drawPath(tmpPaint, *glyph->fPath, stroke);
         return;
     }
 
@@ -238,8 +187,8 @@
     GrAssert(glyph->fAtlas);
 
     // now promote them to fixed
-    width = GrIntToFixed(width);
-    height = GrIntToFixed(height);
+    width = SkIntToFixed(width);
+    height = SkIntToFixed(height);
 
     GrTexture* texture = glyph->fAtlas->texture();
     GrAssert(texture);
@@ -254,19 +203,20 @@
         // If we need to reserve vertices allow the draw target to suggest
         // a number of verts to reserve and whether to perform a flush.
         fMaxVertices = kMinRequestedVerts;
-        bool flush = fDrawTarget->geometryHints(fVertexLayout,
-                                               &fMaxVertices,
-                                               NULL);
+        bool flush = (NULL != fDrawTarget) &&
+                     fDrawTarget->geometryHints(GrDrawState::VertexSize(fVertexLayout),
+                                                &fMaxVertices,
+                                                NULL);
         if (flush) {
             this->flushGlyphs();
-            fContext->flushText();
-            fDrawTarget = fContext->getTextTarget(fPaint);
-            fMaxVertices = kDefaultRequestedVerts;
-            // ignore return, no point in flushing again.
-            fDrawTarget->geometryHints(fVertexLayout,
-                                       &fMaxVertices,
-                                       NULL);
+            fContext->flush();
         }
+        fDrawTarget = fContext->getTextTarget(fPaint);
+        fMaxVertices = kDefaultRequestedVerts;
+        // ignore return, no point in flushing again.
+        fDrawTarget->geometryHints(GrDrawState::VertexSize(fVertexLayout),
+                                   &fMaxVertices,
+                                   NULL);
 
         int maxQuadVertices = 4 * fContext->getQuadIndexBuffer()->maxQuads();
         if (fMaxVertices < kMinRequestedVerts) {
@@ -275,16 +225,19 @@
             // don't exceed the limit of the index buffer
             fMaxVertices = maxQuadVertices;
         }
-        bool success = fDrawTarget->reserveVertexSpace(fVertexLayout, 
+        bool success = fDrawTarget->reserveVertexAndIndexSpace(
+                                                   fVertexLayout,
                                                    fMaxVertices,
-                                                   GrTCast<void**>(&fVertices));
+                                                   0,
+                                                   GrTCast<void**>(&fVertices),
+                                                   NULL);
         GrAlwaysAssert(success);
     }
 
-    GrFixed tx = GrIntToFixed(glyph->fAtlasLocation.fX);
-    GrFixed ty = GrIntToFixed(glyph->fAtlasLocation.fY);
+    GrFixed tx = SkIntToFixed(glyph->fAtlasLocation.fX);
+    GrFixed ty = SkIntToFixed(glyph->fAtlasLocation.fY);
 
-#if GR_GL_TEXT_TEXTURE_NORMALIZED
+#if GR_TEXT_SCALAR_IS_USHORT
     int x = vx >> 16;
     int y = vy >> 16;
     int w = width >> 16;
@@ -308,5 +261,3 @@
 #endif
     fCurrVertex += 4;
 }
-
-
diff --git a/src/gpu/GrTextStrike.cpp b/src/gpu/GrTextStrike.cpp
index b8762ad..4b4298a 100644
--- a/src/gpu/GrTextStrike.cpp
+++ b/src/gpu/GrTextStrike.cpp
@@ -15,6 +15,11 @@
 #include "GrTextStrike_impl.h"
 #include "GrRect.h"
 
+SK_DEFINE_INST_COUNT(GrFontScaler)
+SK_DEFINE_INST_COUNT(GrKey)
+
+///////////////////////////////////////////////////////////////////////////////
+
 GrFontCache::GrFontCache(GrGpu* gpu) : fGpu(gpu) {
     gpu->ref();
     fAtlasMgr = NULL;
@@ -31,10 +36,11 @@
 GrTextStrike* GrFontCache::generateStrike(GrFontScaler* scaler,
                                           const Key& key) {
     if (NULL == fAtlasMgr) {
-        fAtlasMgr = new GrAtlasMgr(fGpu);
+        fAtlasMgr = SkNEW_ARGS(GrAtlasMgr, (fGpu));
     }
-    GrTextStrike* strike = new GrTextStrike(this, scaler->getKey(),
-                                            scaler->getMaskFormat(), fAtlasMgr);
+    GrTextStrike* strike = SkNEW_ARGS(GrTextStrike,
+                                      (this, scaler->getKey(),
+                                       scaler->getMaskFormat(), fAtlasMgr));
     fCache.insert(key, strike);
 
     if (fHead) {
@@ -143,7 +149,7 @@
 GrTextStrike::~GrTextStrike() {
     GrAtlas::FreeLList(fAtlas);
     fFontScalerKey->unref();
-    fCache.getArray().visit(FreeGlyph);
+    fCache.getArray().visitAll(FreeGlyph);
 
 #if GR_DEBUG
     gCounter -= 1;
@@ -201,5 +207,3 @@
     glyph->fAtlas = fAtlas = atlas;
     return true;
 }
-
-
diff --git a/src/gpu/GrTextStrike.h b/src/gpu/GrTextStrike.h
index 701acea..25ced6d 100644
--- a/src/gpu/GrTextStrike.h
+++ b/src/gpu/GrTextStrike.h
@@ -112,4 +112,3 @@
 };
 
 #endif
-
diff --git a/src/gpu/GrTextStrike_impl.h b/src/gpu/GrTextStrike_impl.h
index 1b6392c..48323bc 100644
--- a/src/gpu/GrTextStrike_impl.h
+++ b/src/gpu/GrTextStrike_impl.h
@@ -16,16 +16,16 @@
     Key(GrFontScaler* scaler) {
         fFontScalerKey = scaler->getKey();
     }
-    
+
     uint32_t getHash() const { return fFontScalerKey->getHash(); }
-    
+
     static bool LT(const GrTextStrike& strike, const Key& key) {
         return *strike.getFontScalerKey() < *key.fFontScalerKey;
     }
     static bool EQ(const GrTextStrike& strike, const Key& key) {
         return *strike.getFontScalerKey() == *key.fFontScalerKey;
     }
-    
+
 private:
     const GrKey* fFontScalerKey;
 };
@@ -50,7 +50,7 @@
 
 GrTextStrike* GrFontCache::getStrike(GrFontScaler* scaler) {
     this->validate();
-    
+
     Key key(scaler);
     GrTextStrike* strike = fCache.find(key);
     if (NULL == strike) {
@@ -79,16 +79,16 @@
 class GrTextStrike::Key {
 public:
     Key(GrGlyph::PackedID id) : fPackedID(id) {}
-    
+
     uint32_t getHash() const { return fPackedID; }
-    
+
     static bool LT(const GrGlyph& glyph, const Key& key) {
         return glyph.fPackedID < key.fPackedID;
     }
     static bool EQ(const GrGlyph& glyph, const Key& key) {
         return glyph.fPackedID == key.fPackedID;
     }
-    
+
 private:
     GrGlyph::PackedID fPackedID;
 };
@@ -103,4 +103,3 @@
 }
 
 #endif
-
diff --git a/src/gpu/GrTexture.cpp b/src/gpu/GrTexture.cpp
index c145c71..614d771 100644
--- a/src/gpu/GrTexture.cpp
+++ b/src/gpu/GrTexture.cpp
@@ -12,47 +12,176 @@
 #include "GrContext.h"
 #include "GrGpu.h"
 #include "GrRenderTarget.h"
+#include "GrResourceCache.h"
+
+SK_DEFINE_INST_COUNT(GrTexture)
+
+/**
+ * This method allows us to interrupt the normal deletion process and place
+ * textures back in the texture cache when their ref count goes to zero.
+ */
+void GrTexture::internal_dispose() const {
+
+    if (this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit) &&
+        NULL != this->INHERITED::getContext()) {
+        GrTexture* nonConstThis = const_cast<GrTexture *>(this);
+        this->fRefCnt = 1;      // restore ref count to initial setting
+
+        nonConstThis->resetFlag((GrTextureFlags) kReturnToCache_FlagBit);
+        nonConstThis->INHERITED::getContext()->addExistingTextureToCache(nonConstThis);
+
+        // Note: "this" texture might be freed inside addExistingTextureToCache
+        // if it is purged.
+        return;
+    }
+
+    this->INHERITED::internal_dispose();
+}
 
 bool GrTexture::readPixels(int left, int top, int width, int height,
                            GrPixelConfig config, void* buffer,
-                           size_t rowBytes) {
+                           size_t rowBytes, uint32_t pixelOpsFlags) {
     // go through context so that all necessary flushing occurs
     GrContext* context = this->getContext();
     if (NULL == context) {
         return false;
     }
     return context->readTexturePixels(this,
-                                      left, top,
-                                      width, height,
-                                      config, buffer, rowBytes);
+                                      left, top, width, height,
+                                      config, buffer, rowBytes,
+                                      pixelOpsFlags);
 }
 
 void GrTexture::writePixels(int left, int top, int width, int height,
                             GrPixelConfig config, const void* buffer,
-                            size_t rowBytes) {
+                            size_t rowBytes, uint32_t pixelOpsFlags) {
     // go through context so that all necessary flushing occurs
     GrContext* context = this->getContext();
     if (NULL == context) {
         return;
     }
     context->writeTexturePixels(this,
-                                left, top,
-                                width, height,
-                                config, buffer, rowBytes);
+                                left, top, width, height,
+                                config, buffer, rowBytes,
+                                pixelOpsFlags);
 }
 
 void GrTexture::releaseRenderTarget() {
     if (NULL != fRenderTarget) {
         GrAssert(fRenderTarget->asTexture() == this);
+        GrAssert(fDesc.fFlags & kRenderTarget_GrTextureFlagBit);
+
         fRenderTarget->onTextureReleaseRenderTarget();
         fRenderTarget->unref();
         fRenderTarget = NULL;
+
+        fDesc.fFlags = fDesc.fFlags &
+            ~(kRenderTarget_GrTextureFlagBit|kNoStencil_GrTextureFlagBit);
+        fDesc.fSampleCnt = 0;
     }
 }
 
+void GrTexture::onRelease() {
+    GrAssert(!this->isSetFlag((GrTextureFlags) kReturnToCache_FlagBit));
+    this->releaseRenderTarget();
+
+    INHERITED::onRelease();
+}
+
 void GrTexture::onAbandon() {
     if (NULL != fRenderTarget) {
         fRenderTarget->abandon();
     }
+
+    INHERITED::onAbandon();
 }
 
+void GrTexture::validateDesc() const {
+    if (NULL != this->asRenderTarget()) {
+        // This texture has a render target
+        GrAssert(0 != (fDesc.fFlags & kRenderTarget_GrTextureFlagBit));
+
+        if (NULL != this->asRenderTarget()->getStencilBuffer()) {
+            GrAssert(0 != (fDesc.fFlags & kNoStencil_GrTextureFlagBit));
+        } else {
+            GrAssert(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit));
+        }
+
+        GrAssert(fDesc.fSampleCnt == this->asRenderTarget()->numSamples());
+    } else {
+        GrAssert(0 == (fDesc.fFlags & kRenderTarget_GrTextureFlagBit));
+        GrAssert(0 == (fDesc.fFlags & kNoStencil_GrTextureFlagBit));
+        GrAssert(0 == fDesc.fSampleCnt);
+    }
+}
+
+// These flags need to fit in a GrResourceKey::ResourceFlags so they can be folded into the texture
+// key
+enum TextureFlags {
+    /**
+     * The kStretchToPOT bit is set when the texture is NPOT and is being repeated but the
+     * hardware doesn't support that feature.
+     */
+    kStretchToPOT_TextureFlag = 0x1,
+    /**
+     * The kFilter bit can only be set when the kStretchToPOT flag is set and indicates whether the
+     * stretched texture should be bilerp filtered or point sampled.
+     */
+    kFilter_TextureFlag       = 0x2,
+};
+
+namespace {
+GrResourceKey::ResourceFlags get_texture_flags(const GrGpu* gpu,
+                                               const GrTextureParams* params,
+                                               const GrTextureDesc& desc) {
+    GrResourceKey::ResourceFlags flags = 0;
+    bool tiled = NULL != params && params->isTiled();
+    if (tiled && !gpu->getCaps().npotTextureTileSupport()) {
+        if (!GrIsPow2(desc.fWidth) || !GrIsPow2(desc.fHeight)) {
+            flags |= kStretchToPOT_TextureFlag;
+            if (params->isBilerp()) {
+                flags |= kFilter_TextureFlag;
+            }
+        }
+    }
+    return flags;
+}
+
+GrResourceKey::ResourceType texture_resource_type() {
+    static const GrResourceKey::ResourceType gType = GrResourceKey::GenerateResourceType();
+    return gType;
+}
+}
+
+GrResourceKey GrTexture::ComputeKey(const GrGpu* gpu,
+                                    const GrTextureParams* params,
+                                    const GrTextureDesc& desc,
+                                    const GrCacheID& cacheID) {
+    GrResourceKey::ResourceFlags flags = get_texture_flags(gpu, params, desc);
+    return GrResourceKey(cacheID, texture_resource_type(), flags);
+}
+
+GrResourceKey GrTexture::ComputeScratchKey(const GrTextureDesc& desc) {
+    GrCacheID::Key idKey;
+    // Instead of a client-provided key of the texture contents we create a key from the
+    // descriptor.
+    GR_STATIC_ASSERT(sizeof(idKey) >= 12);
+    GrAssert(desc.fHeight < (1 << 16));
+    GrAssert(desc.fWidth < (1 << 16));
+    idKey.fData32[0] = (desc.fWidth) | (desc.fHeight << 16);
+    idKey.fData32[1] = desc.fConfig | desc.fSampleCnt << 16;
+    idKey.fData32[2] = desc.fFlags;
+    static const int kPadSize = sizeof(idKey) - 12;
+    memset(idKey.fData8 + 12, 0, kPadSize);
+
+    GrCacheID cacheID(GrResourceKey::ScratchDomain(), idKey);
+    return GrResourceKey(cacheID, texture_resource_type(), 0);
+}
+
+bool GrTexture::NeedsResizing(const GrResourceKey& key) {
+    return SkToBool(key.getResourceFlags() & kStretchToPOT_TextureFlag);
+}
+
+bool GrTexture::NeedsFiltering(const GrResourceKey& key) {
+    return SkToBool(key.getResourceFlags() & kFilter_TextureFlag);
+}
diff --git a/src/gpu/GrTextureAccess.cpp b/src/gpu/GrTextureAccess.cpp
new file mode 100644
index 0000000..5c3a36d
--- /dev/null
+++ b/src/gpu/GrTextureAccess.cpp
@@ -0,0 +1,107 @@
+/*
+ * 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 "GrTextureAccess.h"
+
+#include "GrTexture.h"
+
+GrTextureAccess::GrTextureAccess() {
+#if GR_DEBUG
+    memcpy(fSwizzle, "void", 5);
+    fSwizzleMask = 0xbeeffeed;
+#endif
+}
+
+GrTextureAccess::GrTextureAccess(GrTexture* texture, const GrTextureParams& params) {
+    this->reset(texture, params);
+}
+
+GrTextureAccess::GrTextureAccess(GrTexture* texture,
+                                 bool bilerp,
+                                 SkShader::TileMode tileXAndY) {
+    this->reset(texture, bilerp, tileXAndY);
+}
+
+GrTextureAccess::GrTextureAccess(GrTexture* texture,
+                                 const char* swizzle,
+                                 const GrTextureParams& params) {
+    this->reset(texture, swizzle, params);
+}
+
+GrTextureAccess::GrTextureAccess(GrTexture* texture,
+                                 const char* swizzle,
+                                 bool bilerp,
+                                 SkShader::TileMode tileXAndY) {
+    this->reset(texture, swizzle, bilerp, tileXAndY);
+}
+
+void GrTextureAccess::reset(GrTexture* texture,
+                            const char* swizzle,
+                            const GrTextureParams& params) {
+    GrAssert(NULL != texture);
+    GrAssert(strlen(swizzle) >= 1 && strlen(swizzle) <= 4);
+
+    fParams = params;
+    fTexture.reset(SkRef(texture));
+    this->setSwizzle(swizzle);
+}
+
+void GrTextureAccess::reset(GrTexture* texture,
+                            const char* swizzle,
+                            bool bilerp,
+                            SkShader::TileMode tileXAndY) {
+    GrAssert(NULL != texture);
+    GrAssert(strlen(swizzle) >= 1 && strlen(swizzle) <= 4);
+
+    fParams.reset(tileXAndY, bilerp);
+    fTexture.reset(SkRef(texture));
+    this->setSwizzle(swizzle);
+}
+
+void GrTextureAccess::reset(GrTexture* texture,
+                            const GrTextureParams& params) {
+    GrAssert(NULL != texture);
+    fTexture.reset(SkRef(texture));
+    fParams = params;
+    memcpy(fSwizzle, "rgba", 5);
+    fSwizzleMask = (kRGB_SwizzleMask | kA_SwizzleFlag);
+}
+
+void GrTextureAccess::reset(GrTexture* texture,
+                            bool bilerp,
+                            SkShader::TileMode tileXAndY) {
+    GrAssert(NULL != texture);
+    fTexture.reset(SkRef(texture));
+    fParams.reset(tileXAndY, bilerp);
+    memcpy(fSwizzle, "rgba", 5);
+    fSwizzleMask = (kRGB_SwizzleMask | kA_SwizzleFlag);
+}
+
+void GrTextureAccess::setSwizzle(const char* swizzle) {
+    fSwizzleMask = 0;
+    memset(fSwizzle, '\0', 5);
+    for (int i = 0; i < 4 && '\0' != swizzle[i]; ++i) {
+        fSwizzle[i] = swizzle[i];
+        switch (swizzle[i]) {
+            case 'r':
+                fSwizzleMask |= kR_SwizzleFlag;
+                break;
+            case 'g':
+                fSwizzleMask |= kG_SwizzleFlag;
+                break;
+            case 'b':
+                fSwizzleMask |= kB_SwizzleFlag;
+                break;
+            case 'a':
+                fSwizzleMask |= kA_SwizzleFlag;
+                break;
+            default:
+                GrCrash("Unexpected swizzle string character.");
+                break;
+        }
+    }
+}
diff --git a/src/gpu/GrVertexBuffer.h b/src/gpu/GrVertexBuffer.h
index bda235c..b53cbf0 100644
--- a/src/gpu/GrVertexBuffer.h
+++ b/src/gpu/GrVertexBuffer.h
@@ -15,8 +15,8 @@
 
 class GrVertexBuffer : public GrGeometryBuffer {
 protected:
-    GrVertexBuffer(GrGpu* gpu, size_t sizeInBytes, bool dynamic)
-        : INHERITED(gpu, sizeInBytes, dynamic) {}
+    GrVertexBuffer(GrGpu* gpu, bool isWrapped, size_t sizeInBytes, bool dynamic)
+        : INHERITED(gpu, isWrapped, sizeInBytes, dynamic) {}
 private:
     typedef GrGeometryBuffer INHERITED;
 };
diff --git a/src/gpu/SkGpuCanvas.cpp b/src/gpu/SkGpuCanvas.cpp
deleted file mode 100644
index fa57335..0000000
--- a/src/gpu/SkGpuCanvas.cpp
+++ /dev/null
@@ -1,46 +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 "GrContext.h"
-
-#include "SkGpuCanvas.h"
-#include "SkGpuDevice.h"
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkGpuCanvas::SkGpuCanvas(GrContext* context, GrRenderTarget* renderTarget) {
-    SkASSERT(context);
-    fContext = context;
-    fContext->ref();
-
-    this->setDevice(new SkGpuDevice(context, renderTarget))->unref();
-}
-
-SkGpuCanvas::~SkGpuCanvas() {
-    // call this now, while our override of restore() is in effect
-    this->restoreToCount(1);
-    fContext->flush(false);
-    fContext->unref();
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-bool SkGpuCanvas::getViewport(SkIPoint* size) const {
-    if (size) {
-        SkDevice* device = this->getDevice();
-        if (device) {
-            size->set(device->width(), device->height());
-        } else {
-            size->set(0, 0);
-        }
-    }
-    return true;
-}
-
diff --git a/src/gpu/SkGpuDevice.cpp b/src/gpu/SkGpuDevice.cpp
index af59699..18f66b5 100644
--- a/src/gpu/SkGpuDevice.cpp
+++ b/src/gpu/SkGpuDevice.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,42 +5,46 @@
  * found in the LICENSE file.
  */
 
+#include "SkGpuDevice.h"
 
+#include "effects/GrTextureDomainEffect.h"
+#include "effects/GrSimpleTextureEffect.h"
 
 #include "GrContext.h"
 #include "GrTextContext.h"
 
-#include "SkGpuDevice.h"
 #include "SkGrTexturePixelRef.h"
 
 #include "SkColorFilter.h"
+#include "SkDeviceImageFilterProxy.h"
 #include "SkDrawProcs.h"
 #include "SkGlyphCache.h"
 #include "SkImageFilter.h"
-#include "SkTLazy.h"
+#include "SkPathEffect.h"
+#include "SkStroke.h"
 #include "SkUtils.h"
 
-#define CACHE_LAYER_TEXTURES 1
+#define CACHE_COMPATIBLE_DEVICE_TEXTURES 1
 
 #if 0
     extern bool (*gShouldDrawProc)();
-    #define CHECK_SHOULD_DRAW(draw)                             \
+    #define CHECK_SHOULD_DRAW(draw, forceI)                     \
         do {                                                    \
             if (gShouldDrawProc && !gShouldDrawProc()) return;  \
-            this->prepareRenderTarget(draw);                    \
+            this->prepareDraw(draw, forceI);                    \
         } while (0)
 #else
-    #define CHECK_SHOULD_DRAW(draw) this->prepareRenderTarget(draw)
+    #define CHECK_SHOULD_DRAW(draw, forceI) this->prepareDraw(draw, forceI)
 #endif
 
 // we use the same texture slot on GrPaint for bitmaps and shaders
 // (since drawBitmap, drawSprite, and drawDevice ignore skia's shader)
 enum {
     kBitmapTextureIdx = 0,
-    kShaderTextureIdx = 0
+    kShaderTextureIdx = 0,
+    kColorFilterTextureIdx = 1
 };
 
-
 #define MAX_BLUR_SIGMA 4.0f
 // FIXME:  This value comes from from SkBlurMaskFilter.cpp.
 // Should probably be put in a common header someplace.
@@ -54,48 +57,72 @@
 // point we should probably get rid of these scaling constants and rebaseline
 // all the blur tests.
 #define BLUR_SIGMA_SCALE 0.6f
-///////////////////////////////////////////////////////////////////////////////
+// This constant represents the screen alignment criterion in texels for
+// requiring texture domain clamping to prevent color bleeding when drawing
+// a sub region of a larger source image.
+#define COLOR_BLEED_TOLERANCE SkFloatToScalar(0.001f)
 
-SkGpuDevice::SkAutoCachedTexture::
-             SkAutoCachedTexture(SkGpuDevice* device,
-                                 const SkBitmap& bitmap,
-                                 const GrSamplerState* sampler,
-                                 GrTexture** texture) {
-    GrAssert(texture);
-    *texture = this->set(device, bitmap, sampler);
-}
-
-SkGpuDevice::SkAutoCachedTexture::SkAutoCachedTexture() {
-}
-
-GrTexture* SkGpuDevice::SkAutoCachedTexture::set(SkGpuDevice* device,
-                                                 const SkBitmap& bitmap,
-                                                 const GrSamplerState* sampler) {
-    if (fTex.texture()) {
-        fDevice->unlockCachedTexture(fTex);
-    }
-    fDevice = device;
-    GrTexture* texture = (GrTexture*)bitmap.getTexture();
-    if (texture) {
-        // return the native texture
-        fTex.reset();
-    } else {
-        // look it up in our cache
-        fTex = device->lockCachedTexture(bitmap, sampler);
-        texture = fTex.texture();
-    }
-    return texture;
-}
-
-SkGpuDevice::SkAutoCachedTexture::~SkAutoCachedTexture() {
-    if (fTex.texture()) {
-        fDevice->unlockCachedTexture(fTex);
-    }
-}
+#define DO_DEFERRED_CLEAR()             \
+    do {                                \
+        if (fNeedClear) {               \
+            this->clear(SK_ColorTRANSPARENT); \
+        }                               \
+    } while (false)                     \
 
 ///////////////////////////////////////////////////////////////////////////////
 
-bool gDoTraceDraw;
+#define CHECK_FOR_NODRAW_ANNOTATION(paint) \
+    do { if (paint.isNoDrawAnnotation()) { return; } } while (0)
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+class SkGpuDevice::SkAutoCachedTexture : public ::SkNoncopyable {
+public:
+    SkAutoCachedTexture()
+        : fDevice(NULL)
+        , fTexture(NULL) {
+    }
+
+    SkAutoCachedTexture(SkGpuDevice* device,
+                        const SkBitmap& bitmap,
+                        const GrTextureParams* params,
+                        GrTexture** texture)
+        : fDevice(NULL)
+        , fTexture(NULL) {
+        GrAssert(NULL != texture);
+        *texture = this->set(device, bitmap, params);
+    }
+
+    ~SkAutoCachedTexture() {
+        if (NULL != fTexture) {
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
+        }
+    }
+
+    GrTexture* set(SkGpuDevice* device,
+                   const SkBitmap& bitmap,
+                   const GrTextureParams* params) {
+        if (NULL != fTexture) {
+            GrUnlockAndUnrefCachedBitmapTexture(fTexture);
+            fTexture = NULL;
+        }
+        fDevice = device;
+        GrTexture* result = (GrTexture*)bitmap.getTexture();
+        if (NULL == result) {
+            // Cannot return the native texture so look it up in our cache
+            fTexture = GrLockAndRefCachedBitmapTexture(device->context(), bitmap, params);
+            result = fTexture;
+        }
+        return result;
+    }
+
+private:
+    SkGpuDevice* fDevice;
+    GrTexture*   fTexture;
+};
+
+///////////////////////////////////////////////////////////////////////////////
 
 struct GrSkDrawProcs : public SkDrawProcs {
 public:
@@ -141,100 +168,78 @@
 
 SkGpuDevice::SkGpuDevice(GrContext* context, GrTexture* texture)
 : SkDevice(make_bitmap(context, texture->asRenderTarget())) {
-    this->initFromRenderTarget(context, texture->asRenderTarget());
+    this->initFromRenderTarget(context, texture->asRenderTarget(), false);
 }
 
 SkGpuDevice::SkGpuDevice(GrContext* context, GrRenderTarget* renderTarget)
 : SkDevice(make_bitmap(context, renderTarget)) {
-    this->initFromRenderTarget(context, renderTarget);
+    this->initFromRenderTarget(context, renderTarget, false);
 }
 
 void SkGpuDevice::initFromRenderTarget(GrContext* context,
-                                       GrRenderTarget* renderTarget) {
-    fNeedPrepareRenderTarget = false;
+                                       GrRenderTarget* renderTarget,
+                                       bool cached) {
     fDrawProcs = NULL;
 
     fContext = context;
     fContext->ref();
 
-    fTexture = NULL;
     fRenderTarget = NULL;
     fNeedClear = false;
 
     GrAssert(NULL != renderTarget);
     fRenderTarget = renderTarget;
     fRenderTarget->ref();
-    // if this RT is also a texture, hold a ref on it
-    fTexture = fRenderTarget->asTexture();
-    SkSafeRef(fTexture);
-    
-    // Create a pixel ref for the underlying SkBitmap. We prefer a texture pixel
-    // ref to a render target pixel reft. The pixel ref may get ref'ed outside
-    // the device via accessBitmap. This external ref may outlive the device.
-    // Since textures own their render targets (but not vice-versa) we
-    // are ensuring that both objects will live as long as the pixel ref.
-    SkPixelRef* pr;
-    if (fTexture) {
-        pr = new SkGrTexturePixelRef(fTexture);
-    } else {
-        pr = new SkGrRenderTargetPixelRef(fRenderTarget);
+
+    // Hold onto to the texture in the pixel ref (if there is one) because the texture holds a ref
+    // on the RT but not vice-versa.
+    // TODO: Remove this trickery once we figure out how to make SkGrPixelRef do this without
+    // busting chrome (for a currently unknown reason).
+    GrSurface* surface = fRenderTarget->asTexture();
+    if (NULL == surface) {
+        surface = fRenderTarget;
     }
+    SkPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (surface, cached));
+
     this->setPixelRef(pr, 0)->unref();
 }
 
-SkGpuDevice::SkGpuDevice(GrContext* context, SkBitmap::Config config, int width,
-                         int height, Usage usage)
-: SkDevice(config, width, height, false /*isOpaque*/) {
-    fNeedPrepareRenderTarget = false;
+SkGpuDevice::SkGpuDevice(GrContext* context,
+                         SkBitmap::Config config,
+                         int width,
+                         int height,
+                         int sampleCount)
+    : SkDevice(config, width, height, false /*isOpaque*/) {
+
     fDrawProcs = NULL;
 
     fContext = context;
     fContext->ref();
 
-    fTexture = NULL;
     fRenderTarget = NULL;
     fNeedClear = false;
 
     if (config != SkBitmap::kRGB_565_Config) {
         config = SkBitmap::kARGB_8888_Config;
     }
-    SkBitmap bm;
-    bm.setConfig(config, width, height);
 
-#if CACHE_LAYER_TEXTURES
-    TexType type = (kSaveLayer_Usage == usage) ?
-                            kSaveLayerDeviceRenderTarget_TexType :
-                            kDeviceRenderTarget_TexType;
-    fCache = this->lockCachedTexture(bm, NULL, type);
-    fTexture = fCache.texture();
-    if (fTexture) {
-        SkASSERT(NULL != fTexture->asRenderTarget());
-        // hold a ref directly on fTexture (even though fCache has one) to match
-        // other constructor paths. Simplifies cleanup.
-        fTexture->ref();
-    }
-#else
-    const GrTextureDesc desc = {
-        kRenderTarget_GrTextureFlagBit,
-        width,
-        height,
-        SkGr::Bitmap2PixelConfig(bm),
-        {0} // samples
-    };
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(config);
+    desc.fSampleCnt = sampleCount;
 
-    fTexture = fContext->createUncachedTexture(desc, NULL, 0);
-#endif
-    if (NULL != fTexture) {
-        fRenderTarget = fTexture->asRenderTarget();
+    SkAutoTUnref<GrTexture> texture(fContext->createUncachedTexture(desc, NULL, 0));
+
+    if (NULL != texture) {
+        fRenderTarget = texture->asRenderTarget();
         fRenderTarget->ref();
 
         GrAssert(NULL != fRenderTarget);
 
-        // we defer the actual clear until our gainFocus()
-        fNeedClear = true;
-
         // wrap the bitmap with a pixelref to expose our texture
-        SkGrTexturePixelRef* pr = new SkGrTexturePixelRef(fTexture);
+        SkGrPixelRef* pr = SkNEW_ARGS(SkGrPixelRef, (texture));
         this->setPixelRef(pr, 0)->unref();
     } else {
         GrPrintf("--- failed to create gpu-offscreen [%d %d]\n",
@@ -248,43 +253,53 @@
         delete fDrawProcs;
     }
 
-    SkSafeUnref(fTexture);
-    SkSafeUnref(fRenderTarget);
-    if (fCache.texture()) {
-        GrAssert(NULL != fTexture);
-        GrAssert(fRenderTarget == fTexture->asRenderTarget());
-        fContext->unlockTexture(fCache);
+    // The GrContext takes a ref on the target. We don't want to cause the render
+    // target to be unnecessarily kept alive.
+    if (fContext->getRenderTarget() == fRenderTarget) {
+        fContext->setRenderTarget(NULL);
     }
+
+    if (fContext->getClip() == &fClipData) {
+        fContext->setClip(NULL);
+    }
+
+    SkSafeUnref(fRenderTarget);
     fContext->unref();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkGpuDevice::makeRenderTargetCurrent() {
+    DO_DEFERRED_CLEAR();
     fContext->setRenderTarget(fRenderTarget);
-    fContext->flush(true);
-    fNeedPrepareRenderTarget = true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 namespace {
-GrPixelConfig config8888_to_gr_config(SkCanvas::Config8888 config8888) {
+GrPixelConfig config8888_to_grconfig_and_flags(SkCanvas::Config8888 config8888, uint32_t* flags) {
     switch (config8888) {
         case SkCanvas::kNative_Premul_Config8888:
-            return kSkia8888_PM_GrPixelConfig;
+            *flags = 0;
+            return kSkia8888_GrPixelConfig;
         case SkCanvas::kNative_Unpremul_Config8888:
-            return kSkia8888_UPM_GrPixelConfig;
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kSkia8888_PM_GrPixelConfig;
         case SkCanvas::kBGRA_Premul_Config8888:
-            return kBGRA_8888_PM_GrPixelConfig;
+            *flags = 0;
+            return kBGRA_8888_GrPixelConfig;
         case SkCanvas::kBGRA_Unpremul_Config8888:
-            return kBGRA_8888_UPM_GrPixelConfig;
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kBGRA_8888_GrPixelConfig;
         case SkCanvas::kRGBA_Premul_Config8888:
-            return kRGBA_8888_PM_GrPixelConfig;
+            *flags = 0;
+            return kRGBA_8888_GrPixelConfig;
         case SkCanvas::kRGBA_Unpremul_Config8888:
-            return kRGBA_8888_UPM_GrPixelConfig;
+            *flags = GrContext::kUnpremul_PixelOpsFlag;
+            return kRGBA_8888_GrPixelConfig;
         default:
             GrCrash("Unexpected Config8888.");
+            *flags = 0; // suppress warning
             return kSkia8888_PM_GrPixelConfig;
     }
 }
@@ -293,20 +308,23 @@
 bool SkGpuDevice::onReadPixels(const SkBitmap& bitmap,
                                int x, int y,
                                SkCanvas::Config8888 config8888) {
+    DO_DEFERRED_CLEAR();
     SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
     SkASSERT(!bitmap.isNull());
     SkASSERT(SkIRect::MakeWH(this->width(), this->height()).contains(SkIRect::MakeXYWH(x, y, bitmap.width(), bitmap.height())));
 
     SkAutoLockPixels alp(bitmap);
     GrPixelConfig config;
-    config = config8888_to_gr_config(config8888);
+    uint32_t flags;
+    config = config8888_to_grconfig_and_flags(config8888, &flags);
     return fContext->readRenderTargetPixels(fRenderTarget,
                                             x, y,
                                             bitmap.width(),
                                             bitmap.height(),
                                             config,
                                             bitmap.getPixels(),
-                                            bitmap.rowBytes());
+                                            bitmap.rowBytes(),
+                                            flags);
 }
 
 void SkGpuDevice::writePixels(const SkBitmap& bitmap, int x, int y,
@@ -317,81 +335,116 @@
     }
 
     GrPixelConfig config;
+    uint32_t flags;
     if (SkBitmap::kARGB_8888_Config == bitmap.config()) {
-        config = config8888_to_gr_config(config8888);
+        config = config8888_to_grconfig_and_flags(config8888, &flags);
     } else {
-        config= SkGr::BitmapConfig2PixelConfig(bitmap.config(),
-                                               bitmap.isOpaque());
+        flags = 0;
+        config= SkBitmapConfig2GrPixelConfig(bitmap.config());
     }
 
     fRenderTarget->writePixels(x, y, bitmap.width(), bitmap.height(),
-                               config, bitmap.getPixels(), bitmap.rowBytes());
+                               config, bitmap.getPixels(), bitmap.rowBytes(), flags);
 }
 
+namespace {
+void purgeClipCB(int genID, void* ) {
+
+    if (SkClipStack::kInvalidGenID == genID ||
+        SkClipStack::kEmptyGenID == genID ||
+        SkClipStack::kWideOpenGenID == genID) {
+        // none of these cases will have a cached clip mask
+        return;
+    }
+
+}
+};
+
+void SkGpuDevice::onAttachToCanvas(SkCanvas* canvas) {
+    INHERITED::onAttachToCanvas(canvas);
+
+    // Canvas promises that this ptr is valid until onDetachFromCanvas is called
+    fClipData.fClipStack = canvas->getClipStack();
+
+    fClipData.fClipStack->addPurgeClipCallback(purgeClipCB, fContext);
+}
+
+void SkGpuDevice::onDetachFromCanvas() {
+    INHERITED::onDetachFromCanvas();
+
+    // TODO: iterate through the clip stack and clean up any cached clip masks
+    fClipData.fClipStack->removePurgeClipCallback(purgeClipCB, fContext);
+
+    fClipData.fClipStack = NULL;
+}
+
+#ifdef SK_DEBUG
+static void check_bounds(const GrClipData& clipData,
+                         const SkRegion& clipRegion,
+                         int renderTargetWidth,
+                         int renderTargetHeight) {
+
+    SkIRect devBound;
+
+    devBound.setLTRB(0, 0, renderTargetWidth, renderTargetHeight);
+
+    SkClipStack::BoundsType boundType;
+    SkRect canvTemp;
+
+    clipData.fClipStack->getBounds(&canvTemp, &boundType);
+    if (SkClipStack::kNormal_BoundsType == boundType) {
+        SkIRect devTemp;
+
+        canvTemp.roundOut(&devTemp);
+
+        devTemp.offset(-clipData.fOrigin.fX, -clipData.fOrigin.fY);
+
+        if (!devBound.intersect(devTemp)) {
+            devBound.setEmpty();
+        }
+    }
+
+    GrAssert(devBound.contains(clipRegion.getBounds()));
+}
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
-static void convert_matrixclip(GrContext* context, const SkMatrix& matrix,
-                               const SkClipStack& clipStack,
-                               const SkRegion& clipRegion,
-                               const SkIPoint& origin) {
-    context->setMatrix(matrix);
-
-    SkGrClipIterator iter;
-    iter.reset(clipStack);
-    const SkIRect& skBounds = clipRegion.getBounds();
-    GrRect bounds;
-    bounds.setLTRB(GrIntToScalar(skBounds.fLeft),
-                   GrIntToScalar(skBounds.fTop),
-                   GrIntToScalar(skBounds.fRight),
-                   GrIntToScalar(skBounds.fBottom));
-    GrClip grc(&iter, GrIntToScalar(-origin.x()), GrIntToScalar(-origin.y()),
-               &bounds);
-    context->setClip(grc);
-}
-
-// call this ever each draw call, to ensure that the context reflects our state,
+// call this every draw call, to ensure that the context reflects our state,
 // and not the state from some other canvas/device
-void SkGpuDevice::prepareRenderTarget(const SkDraw& draw) {
-    if (fNeedPrepareRenderTarget ||
-        fContext->getRenderTarget() != fRenderTarget) {
-
-        fContext->setRenderTarget(fRenderTarget);
-        SkASSERT(draw.fClipStack);
-        convert_matrixclip(fContext, *draw.fMatrix,
-                           *draw.fClipStack, *draw.fClip, this->getOrigin());
-        fNeedPrepareRenderTarget = false;
-    }
-}
-
-void SkGpuDevice::setMatrixClip(const SkMatrix& matrix, const SkRegion& clip,
-                                const SkClipStack& clipStack) {
-    this->INHERITED::setMatrixClip(matrix, clip, clipStack);
-    // We don't need to set them now because the context may not reflect this device.
-    fNeedPrepareRenderTarget = true;
-}
-
-void SkGpuDevice::gainFocus(SkCanvas* canvas, const SkMatrix& matrix,
-                            const SkRegion& clip, const SkClipStack& clipStack) {
+void SkGpuDevice::prepareDraw(const SkDraw& draw, bool forceIdentity) {
+    GrAssert(NULL != fClipData.fClipStack);
 
     fContext->setRenderTarget(fRenderTarget);
 
-    this->INHERITED::gainFocus(canvas, matrix, clip, clipStack);
+    SkASSERT(draw.fClipStack && draw.fClipStack == fClipData.fClipStack);
 
-    convert_matrixclip(fContext, matrix, clipStack, clip, this->getOrigin());
-
-    if (fNeedClear) {
-        fContext->clear(NULL, 0x0);
-        fNeedClear = false;
+    if (forceIdentity) {
+        fContext->setIdentityMatrix();
+    } else {
+        fContext->setMatrix(*draw.fMatrix);
     }
+    fClipData.fOrigin = this->getOrigin();
+
+#ifdef SK_DEBUG
+    check_bounds(fClipData, *draw.fClip, fRenderTarget->width(), fRenderTarget->height());
+#endif
+
+    fContext->setClip(&fClipData);
+
+    DO_DEFERRED_CLEAR();
 }
 
 SkGpuRenderTarget* SkGpuDevice::accessRenderTarget() {
+    DO_DEFERRED_CLEAR();
     return (SkGpuRenderTarget*)fRenderTarget;
 }
 
 bool SkGpuDevice::bindDeviceAsTexture(GrPaint* paint) {
-    if (NULL != fTexture) {
-        paint->setTexture(kBitmapTextureIdx, fTexture);
+    GrTexture* texture = fRenderTarget->asTexture();
+    if (NULL != texture) {
+        paint->colorStage(kBitmapTextureIdx)->setEffect(
+            GrSimpleTextureEffect::Create(texture, SkMatrix::I()))->unref();
         return true;
     }
     return false;
@@ -405,24 +458,27 @@
 SK_COMPILE_ASSERT(SkShader::kSweep_BitmapType == 3, shader_type_mismatch);
 SK_COMPILE_ASSERT(SkShader::kTwoPointRadial_BitmapType == 4,
                   shader_type_mismatch);
-SK_COMPILE_ASSERT(SkShader::kLast_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);
 
-static const GrSamplerState::SampleMode sk_bmp_type_to_sample_mode[] = {
-    (GrSamplerState::SampleMode) -1,                    // kNone_BitmapType
-    GrSamplerState::kNormal_SampleMode,                 // kDefault_BitmapType
-    GrSamplerState::kRadial_SampleMode,                 // kRadial_BitmapType
-    GrSamplerState::kSweep_SampleMode,                  // kSweep_BitmapType
-    GrSamplerState::kRadial2_SampleMode,                // kTwoPointRadial_BitmapType
-};
+namespace {
 
-bool SkGpuDevice::skPaint2GrPaintNoShader(const SkPaint& skPaint,
-                                          bool justAlpha,
-                                          GrPaint* grPaint,
-                                          bool constantColor) {
+// converts a SkPaint to a GrPaint, ignoring the skPaint's shader
+// justAlpha indicates that skPaint's alpha should be used rather than the color
+// 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.
+inline bool skPaint2GrPaintNoShader(SkGpuDevice* dev,
+                                    const SkPaint& skPaint,
+                                    bool justAlpha,
+                                    bool constantColor,
+                                    GrPaint* grPaint) {
 
-    grPaint->fDither    = skPaint.isDither();
-    grPaint->fAntiAlias = skPaint.isAntiAlias();
-    grPaint->fCoverage = 0xFF;
+    grPaint->setDither(skPaint.isDither());
+    grPaint->setAntiAlias(skPaint.isAntiAlias());
 
     SkXfermode::Coeff sm = SkXfermode::kOne_Coeff;
     SkXfermode::Coeff dm = SkXfermode::kISA_Coeff;
@@ -436,148 +492,96 @@
 #endif
         }
     }
-    grPaint->fSrcBlendCoeff = sk_blend_to_grblend(sm);
-    grPaint->fDstBlendCoeff = sk_blend_to_grblend(dm);
+    grPaint->setBlendFunc(sk_blend_to_grblend(sm), sk_blend_to_grblend(dm));
 
     if (justAlpha) {
         uint8_t alpha = skPaint.getAlpha();
-        grPaint->fColor = GrColorPackRGBA(alpha, alpha, alpha, alpha);
+        grPaint->setColor(GrColorPackRGBA(alpha, alpha, alpha, alpha));
         // justAlpha is currently set to true only if there is a texture,
         // so constantColor should not also be true.
         GrAssert(!constantColor);
     } else {
-        grPaint->fColor = SkGr::SkColor2GrColor(skPaint.getColor());
-        grPaint->setTexture(kShaderTextureIdx, NULL);
+        grPaint->setColor(SkColor2GrColor(skPaint.getColor()));
+        GrAssert(!grPaint->isColorStageEnabled(kShaderTextureIdx));
     }
+
     SkColorFilter* colorFilter = skPaint.getColorFilter();
-    SkColor color;
-    SkXfermode::Mode filterMode;
-    SkScalar matrix[20];
-    if (colorFilter != NULL && colorFilter->asColorMode(&color, &filterMode)) {
-        grPaint->fColorMatrixEnabled = false;
-        if (!constantColor) {
-            grPaint->fColorFilterColor = SkGr::SkColor2GrColor(color);
-            grPaint->fColorFilterXfermode = filterMode;
-        } else {
+    if (NULL != colorFilter) {
+        // if the source color is a constant then apply the filter here once rather than per pixel
+        // in a shader.
+        if (constantColor) {
             SkColor filtered = colorFilter->filterColor(skPaint.getColor());
-            grPaint->fColor = SkGr::SkColor2GrColor(filtered);
-            grPaint->resetColorFilter();
+            grPaint->setColor(SkColor2GrColor(filtered));
+        } else {
+            SkAutoTUnref<GrEffectRef> effect(colorFilter->asNewEffect(dev->context()));
+            if (NULL != effect.get()) {
+                grPaint->colorStage(kColorFilterTextureIdx)->setEffect(effect);
+            } else {
+                // TODO: rewrite this using asNewEffect()
+                SkColor color;
+                SkXfermode::Mode filterMode;
+                if (colorFilter->asColorMode(&color, &filterMode)) {
+                    grPaint->setXfermodeColorFilter(filterMode, SkColor2GrColor(color));
+                }
+            }
         }
-    } else if (colorFilter != NULL && colorFilter->asColorMatrix(matrix)) {
-        grPaint->fColorMatrixEnabled = true;
-        memcpy(grPaint->fColorMatrix, matrix, sizeof(matrix));
-        grPaint->fColorFilterXfermode = SkXfermode::kDst_Mode;
-    } else {
-        grPaint->resetColorFilter();
     }
+
     return true;
 }
 
-bool SkGpuDevice::skPaint2GrPaintShader(const SkPaint& skPaint,
-                                        SkAutoCachedTexture* act,
-                                        const SkMatrix& ctm,
-                                        GrPaint* grPaint,
-                                        bool constantColor) {
-
-    SkASSERT(NULL != act);
-
+// This function is similar to skPaint2GrPaintNoShader but also converts
+// skPaint's shader to a GrTexture/GrEffectStage if possible. The texture to
+// be used is set on grPaint and returned in param act. constantColor has the
+// same meaning as in skPaint2GrPaintNoShader.
+inline bool skPaint2GrPaintShader(SkGpuDevice* dev,
+                                  const SkPaint& skPaint,
+                                  bool constantColor,
+                                  GrPaint* grPaint) {
     SkShader* shader = skPaint.getShader();
     if (NULL == shader) {
-        return this->skPaint2GrPaintNoShader(skPaint,
-                                             false,
-                                             grPaint,
-                                             constantColor);
-    } else if (!this->skPaint2GrPaintNoShader(skPaint, true, grPaint, false)) {
+        return skPaint2GrPaintNoShader(dev, skPaint, false, constantColor, grPaint);
+    } else if (!skPaint2GrPaintNoShader(dev, skPaint, true, false, grPaint)) {
         return false;
     }
 
-    SkBitmap bitmap;
-    SkMatrix* matrix = grPaint->textureSampler(kShaderTextureIdx)->matrix();
-    SkShader::TileMode tileModes[2];
-    SkScalar twoPointParams[3];
-    SkShader::BitmapType bmptype = shader->asABitmap(&bitmap, matrix,
-                                                     tileModes, twoPointParams);
-
-    GrSamplerState::SampleMode sampleMode = sk_bmp_type_to_sample_mode[bmptype];
-    if (-1 == sampleMode) {
-        SkShader::GradientInfo info;
-        SkColor                color;
-
-        info.fColors = &color;
-        info.fColorOffsets = NULL;
-        info.fColorCount = 1;
-        if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
-            SkPaint copy(skPaint);
-            copy.setShader(NULL);
-            // modulate the paint alpha by the shader's solid color alpha
-            U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
-            copy.setColor(SkColorSetA(color, newA));
-            return this->skPaint2GrPaintNoShader(copy,
-                                                 false,
-                                                 grPaint,
-                                                 constantColor);
-        }
-        return false;
-    }
-    GrSamplerState* sampler = grPaint->textureSampler(kShaderTextureIdx);
-    sampler->setSampleMode(sampleMode);
-    if (skPaint.isFilterBitmap()) {
-        sampler->setFilter(GrSamplerState::kBilinear_Filter);
-    } else {
-        sampler->setFilter(GrSamplerState::kNearest_Filter);
-    }
-    sampler->setWrapX(sk_tile_mode_to_grwrap(tileModes[0]));
-    sampler->setWrapY(sk_tile_mode_to_grwrap(tileModes[1]));
-    if (GrSamplerState::kRadial2_SampleMode == sampleMode) {
-        sampler->setRadial2Params(twoPointParams[0],
-                                  twoPointParams[1],
-                                  twoPointParams[2] < 0);
+    SkAutoTUnref<GrEffectRef> effect(shader->asNewEffect(dev->context(), skPaint));
+    if (NULL != effect.get()) {
+        grPaint->colorStage(kShaderTextureIdx)->setEffect(effect);
+        return true;
     }
 
-    GrTexture* texture = act->set(this, bitmap, sampler);
-    if (NULL == texture) {
-        SkDebugf("Couldn't convert bitmap to texture.\n");
-        return false;
-    }
-    grPaint->setTexture(kShaderTextureIdx, texture);
+    // We still don't have SkColorShader::asNewEffect() implemented.
+    SkShader::GradientInfo info;
+    SkColor                color;
 
-    // since our texture coords will be in local space, we wack the texture
-    // matrix to map them back into 0...1 before we load it
-    SkMatrix localM;
-    if (shader->getLocalMatrix(&localM)) {
-        SkMatrix inverse;
-        if (localM.invert(&inverse)) {
-            matrix->preConcat(inverse);
-        }
+    info.fColors = &color;
+    info.fColorOffsets = NULL;
+    info.fColorCount = 1;
+    if (SkShader::kColor_GradientType == shader->asAGradient(&info)) {
+        SkPaint copy(skPaint);
+        copy.setShader(NULL);
+        // modulate the paint alpha by the shader's solid color alpha
+        U8CPU newA = SkMulDiv255Round(SkColorGetA(color), copy.getAlpha());
+        copy.setColor(SkColorSetA(color, newA));
+        return skPaint2GrPaintNoShader(dev, copy, false, constantColor, grPaint);
     }
-    if (SkShader::kDefault_BitmapType == bmptype) {
-        GrScalar sx = GrFixedToScalar(GR_Fixed1 / bitmap.width());
-        GrScalar sy = GrFixedToScalar(GR_Fixed1 / bitmap.height());
-        matrix->postScale(sx, sy);
-    } else if (SkShader::kRadial_BitmapType == bmptype) {
-        GrScalar s = GrFixedToScalar(GR_Fixed1 / bitmap.width());
-        matrix->postScale(s, s);
-    }
-
-    return true;
+    return false;
+}
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-
 void SkGpuDevice::clear(SkColor color) {
-    fContext->clear(NULL, color);
+    SkIRect rect = SkIRect::MakeWH(this->width(), this->height());
+    fContext->clear(&rect, SkColor2GrColor(color), fRenderTarget);
+    fNeedClear = false;
 }
 
 void SkGpuDevice::drawPaint(const SkDraw& draw, const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+    CHECK_SHOULD_DRAW(draw, false);
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint,
-                                     &act,
-                                     *draw.fMatrix,
-                                     &grPaint,
-                                     true)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
 
@@ -586,33 +590,29 @@
 
 // must be in SkCanvas::PointMode order
 static const GrPrimitiveType gPointMode2PrimtiveType[] = {
-    kPoints_PrimitiveType,
-    kLines_PrimitiveType,
-    kLineStrip_PrimitiveType
+    kPoints_GrPrimitiveType,
+    kLines_GrPrimitiveType,
+    kLineStrip_GrPrimitiveType
 };
 
 void SkGpuDevice::drawPoints(const SkDraw& draw, SkCanvas::PointMode mode,
                              size_t count, const SkPoint pts[], const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+    CHECK_SHOULD_DRAW(draw, false);
 
     SkScalar width = paint.getStrokeWidth();
     if (width < 0) {
         return;
     }
 
-    // we only handle hairlines here, else we let the SkDraw call our drawPath()
-    if (width > 0) {
+    // we only handle hairlines and paints without path effects or mask filters,
+    // else we let the SkDraw call our drawPath()
+    if (width > 0 || paint.getPathEffect() || paint.getMaskFilter()) {
         draw.drawPoints(mode, count, pts, paint, true);
         return;
     }
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint,
-                                     &act,
-                                     *draw.fMatrix,
-                                     &grPaint,
-                                     true)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
 
@@ -629,8 +629,9 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkGpuDevice::drawRect(const SkDraw& draw, const SkRect& rect,
-                          const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+                           const SkPaint& paint) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
 
     bool doStroke = paint.getStyle() != SkPaint::kFill_Style;
     SkScalar width = paint.getStrokeWidth();
@@ -641,12 +642,12 @@
      */
     bool usePath = doStroke && width > 0 &&
                     paint.getStrokeJoin() != SkPaint::kMiter_Join;
-    // another reason we might need to call drawPath...
-    if (paint.getMaskFilter()) {
+    // another two reasons we might need to call drawPath...
+    if (paint.getMaskFilter() || paint.getPathEffect()) {
         usePath = true;
     }
     // until we aa rotated rects...
-    if (!usePath && paint.isAntiAlias() && !draw.fMatrix->rectStaysRect()) {
+    if (!usePath && paint.isAntiAlias() && !fContext->getMatrix().rectStaysRect()) {
         usePath = true;
     }
     // small miter limit means right angles show bevel...
@@ -668,256 +669,90 @@
     }
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint,
-                                     &act,
-                                     *draw.fMatrix,
-                                     &grPaint,
-                                     true)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
     fContext->drawRect(grPaint, rect, doStroke ? width : -1);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+void SkGpuDevice::drawOval(const SkDraw& draw, const SkRect& oval,
+                           const SkPaint& paint) {
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
+
+    bool usePath = false;
+    // some basic reasons we might need to call drawPath...
+    if (paint.getMaskFilter() || paint.getPathEffect()) {
+        usePath = true;
+    }
+
+    if (usePath) {
+        SkPath path;
+        path.addOval(oval);
+        this->drawPath(draw, path, paint, NULL, true);
+        return;
+    }
+
+    GrPaint grPaint;
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
+        return;
+    }
+    SkStrokeRec stroke(paint);
+
+    fContext->drawOval(grPaint, oval, stroke);
+}
+
 #include "SkMaskFilter.h"
 #include "SkBounder.h"
 
-static GrPathFill skToGrFillType(SkPath::FillType fillType) {
-    switch (fillType) {
-        case SkPath::kWinding_FillType:
-            return kWinding_PathFill;
-        case SkPath::kEvenOdd_FillType:
-            return kEvenOdd_PathFill;
-        case SkPath::kInverseWinding_FillType:
-            return kInverseWinding_PathFill;
-        case SkPath::kInverseEvenOdd_FillType:
-            return kInverseEvenOdd_PathFill;
-        default:
-            SkDebugf("Unsupported path fill type\n");
-            return kHairLine_PathFill;
+///////////////////////////////////////////////////////////////////////////////
+
+// helpers for applying mask filters
+namespace {
+
+// We prefer to blur small rect with small radius via CPU.
+#define MIN_GPU_BLUR_SIZE SkIntToScalar(64)
+#define MIN_GPU_BLUR_RADIUS SkIntToScalar(32)
+inline bool shouldDrawBlurWithCPU(const SkRect& rect, SkScalar radius) {
+    if (rect.width() <= MIN_GPU_BLUR_SIZE &&
+        rect.height() <= MIN_GPU_BLUR_SIZE &&
+        radius <= MIN_GPU_BLUR_RADIUS) {
+        return true;
     }
-}
-
-static GrTexture* applyMorphology(GrContext* context, GrTexture* texture,
-                                  const GrRect& srcRect,
-                                  GrTexture* temp1, GrTexture* temp2,
-                                  GrSamplerState::Filter filter,
-                                  SkISize radius) {
-    GrRenderTarget* oldRenderTarget = context->getRenderTarget();
-    GrAutoMatrix avm(context, GrMatrix::I());
-    GrClip oldClip = context->getClip();
-    context->setClip(GrRect::MakeWH(texture->width(), texture->height()));
-    if (radius.fWidth > 0) {
-        context->setRenderTarget(temp1->asRenderTarget());
-        context->applyMorphology(texture, srcRect, radius.fWidth, filter,
-                                 GrSamplerState::kX_FilterDirection);
-        SkIRect clearRect = SkIRect::MakeXYWH(
-            srcRect.fLeft, srcRect.fBottom,
-            srcRect.width(), radius.fHeight);
-        context->clear(&clearRect, 0x0);
-        texture = temp1;
-    }
-    if (radius.fHeight > 0) {
-        context->setRenderTarget(temp2->asRenderTarget());
-        context->applyMorphology(texture, srcRect, radius.fHeight, filter,
-                                 GrSamplerState::kY_FilterDirection);
-        texture = temp2;
-    }
-    context->setRenderTarget(oldRenderTarget);
-    context->setClip(oldClip);
-    return texture;
-}
-
-static void buildKernel(float sigma, float* kernel, int kernelWidth) {
-    int halfWidth = (kernelWidth - 1) / 2;
-    float sum = 0.0f;
-    float denom = 1.0f / (2.0f * sigma * sigma);
-    for (int i = 0; i < kernelWidth; ++i) {
-        float x = static_cast<float>(i - halfWidth);
-        // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
-        // is dropped here, since we renormalize the kernel below.
-        kernel[i] = sk_float_exp(- x * x * denom);
-        sum += kernel[i];
-    }
-    // Normalize the kernel
-    float scale = 1.0f / sum;
-    for (int i = 0; i < kernelWidth; ++i)
-        kernel[i] *= scale;
-}
-
-static void scaleRect(SkRect* rect, float xScale, float yScale) {
-    rect->fLeft *= xScale;
-    rect->fTop *= yScale;
-    rect->fRight *= xScale;
-    rect->fBottom *= yScale;
-}
-
-static float adjustSigma(float sigma, int *scaleFactor, int *halfWidth,
-                         int *kernelWidth) {
-    *scaleFactor = 1;
-    while (sigma > MAX_BLUR_SIGMA) {
-        *scaleFactor *= 2;
-        sigma *= 0.5f;
-    }
-    *halfWidth = static_cast<int>(ceilf(sigma * 3.0f));
-    *kernelWidth = *halfWidth * 2 + 1;
-    return sigma;
-}
-
-// Apply a Gaussian blur to srcTexture by sigmaX and sigmaY, within the given
-// rect.
-// temp1 and temp2 are used for allocation of intermediate textures.
-// If temp2 is non-NULL, srcTexture will be untouched, and the return
-// value will be either temp1 or temp2.
-// If temp2 is NULL, srcTexture will be overwritten with intermediate
-// results, and the return value will either be temp1 or srcTexture.
-static GrTexture* gaussianBlur(GrContext* context, GrTexture* srcTexture,
-                               GrAutoScratchTexture* temp1,
-                               GrAutoScratchTexture* temp2,
-                               const SkRect& rect,
-                               float sigmaX, float sigmaY) {
-
-    GrRenderTarget* oldRenderTarget = context->getRenderTarget();
-    GrClip oldClip = context->getClip();
-    GrTexture* origTexture = srcTexture;
-    GrAutoMatrix avm(context, GrMatrix::I());
-    SkIRect clearRect;
-    int scaleFactorX, halfWidthX, kernelWidthX;
-    int scaleFactorY, halfWidthY, kernelWidthY;
-    sigmaX = adjustSigma(sigmaX, &scaleFactorX, &halfWidthX, &kernelWidthX);
-    sigmaY = adjustSigma(sigmaY, &scaleFactorY, &halfWidthY, &kernelWidthY);
-
-    SkRect srcRect(rect);
-    scaleRect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
-    srcRect.roundOut();
-    scaleRect(&srcRect, scaleFactorX, scaleFactorY);
-    context->setClip(srcRect);
-
-    const GrTextureDesc desc = {
-        kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit,
-        srcRect.width(),
-        srcRect.height(),
-        kRGBA_8888_GrPixelConfig,
-        {0} // samples 
-    };
-
-    temp1->set(context, desc);
-    if (temp2) temp2->set(context, desc);
-
-    GrTexture* dstTexture = temp1->texture();
-    GrPaint paint;
-    paint.reset();
-    paint.textureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter);
-
-    for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
-        paint.textureSampler(0)->matrix()->setIDiv(srcTexture->width(),
-                                                   srcTexture->height());
-        context->setRenderTarget(dstTexture->asRenderTarget());
-        SkRect dstRect(srcRect);
-        scaleRect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
-                            i < scaleFactorY ? 0.5f : 1.0f);
-        paint.setTexture(0, srcTexture);
-        context->drawRectToRect(paint, dstRect, srcRect);
-        srcRect = dstRect;
-        SkTSwap(srcTexture, dstTexture);
-        // If temp2 is non-NULL, don't render back to origTexture
-        if (temp2 && dstTexture == origTexture) dstTexture = temp2->texture();
-    }
-
-    if (sigmaX > 0.0f) {
-        SkAutoTMalloc<float> kernelStorageX(kernelWidthX);
-        float* kernelX = kernelStorageX.get();
-        buildKernel(sigmaX, kernelX, kernelWidthX);
-
-        if (scaleFactorX > 1) {
-            // Clear out a halfWidth to the right of the srcRect to prevent the
-            // X convolution from reading garbage.
-            clearRect = SkIRect::MakeXYWH(
-                srcRect.fRight, srcRect.fTop, halfWidthX, srcRect.height());
-            context->clear(&clearRect, 0x0);
-        }
-
-        context->setRenderTarget(dstTexture->asRenderTarget());
-        context->convolve(srcTexture, srcRect, kernelX, kernelWidthX,
-                          GrSamplerState::kX_FilterDirection);
-        SkTSwap(srcTexture, dstTexture);
-        if (temp2 && dstTexture == origTexture) dstTexture = temp2->texture();
-    }
-
-    if (sigmaY > 0.0f) {
-        SkAutoTMalloc<float> kernelStorageY(kernelWidthY);
-        float* kernelY = kernelStorageY.get();
-        buildKernel(sigmaY, kernelY, kernelWidthY);
-
-        if (scaleFactorY > 1 || sigmaX > 0.0f) {
-            // Clear out a halfWidth below the srcRect to prevent the Y
-            // convolution from reading garbage.
-            clearRect = SkIRect::MakeXYWH(
-                srcRect.fLeft, srcRect.fBottom, srcRect.width(), halfWidthY);
-            context->clear(&clearRect, 0x0);
-        }
-
-        context->setRenderTarget(dstTexture->asRenderTarget());
-        context->convolve(srcTexture, srcRect, kernelY, kernelWidthY,
-                          GrSamplerState::kY_FilterDirection);
-        SkTSwap(srcTexture, dstTexture);
-        if (temp2 && dstTexture == origTexture) dstTexture = temp2->texture();
-    }
-
-    if (scaleFactorX > 1 || scaleFactorY > 1) {
-        // Clear one pixel to the right and below, to accommodate bilinear
-        // upsampling.
-        clearRect = SkIRect::MakeXYWH(
-            srcRect.fLeft, srcRect.fBottom, srcRect.width() + 1, 1);
-        context->clear(&clearRect, 0x0);
-        clearRect = SkIRect::MakeXYWH(
-            srcRect.fRight, srcRect.fTop, 1, srcRect.height());
-        context->clear(&clearRect, 0x0);
-        // FIXME:  This should be mitchell, not bilinear.
-        paint.textureSampler(0)->setFilter(GrSamplerState::kBilinear_Filter);
-        paint.textureSampler(0)->matrix()->setIDiv(srcTexture->width(),
-                                                   srcTexture->height());
-        context->setRenderTarget(dstTexture->asRenderTarget());
-        paint.setTexture(0, srcTexture);
-        SkRect dstRect(srcRect);
-        scaleRect(&dstRect, scaleFactorX, scaleFactorY);
-        context->drawRectToRect(paint, dstRect, srcRect);
-        srcRect = dstRect;
-        SkTSwap(srcTexture, dstTexture);
-    }
-    context->setRenderTarget(oldRenderTarget);
-    context->setClip(oldClip);
-    return srcTexture;
-}
-
-static bool drawWithGPUMaskFilter(GrContext* context, const SkPath& path,
-                                  SkMaskFilter* filter, const SkMatrix& matrix,
-                                  const SkRegion& clip, SkBounder* bounder,
-                                  GrPaint* grp) {
-#ifdef SK_DISABLE_GPU_BLUR
     return false;
-#endif
+}
+
+bool drawWithGPUMaskFilter(GrContext* context, const SkPath& devPath, const SkStrokeRec& stroke,
+                           SkMaskFilter* filter, const SkRegion& clip,
+                           SkBounder* bounder, GrPaint* grp) {
     SkMaskFilter::BlurInfo info;
     SkMaskFilter::BlurType blurType = filter->asABlur(&info);
     if (SkMaskFilter::kNone_BlurType == blurType) {
         return false;
     }
     SkScalar radius = info.fIgnoreTransform ? info.fRadius
-                                            : matrix.mapRadius(info.fRadius);
+                                            : context->getMatrix().mapRadius(info.fRadius);
     radius = SkMinScalar(radius, MAX_BLUR_RADIUS);
     if (radius <= 0) {
         return false;
     }
+
+    SkRect srcRect = devPath.getBounds();
+    if (shouldDrawBlurWithCPU(srcRect, radius)) {
+        return false;
+    }
+
     float sigma = SkScalarToFloat(radius) * BLUR_SIGMA_SCALE;
     float sigma3 = sigma * 3.0f;
 
-    SkRect srcRect = path.getBounds();
     SkRect clipRect;
     clipRect.set(clip.getBounds());
 
     // Outset srcRect and clipRect by 3 * sigma, to compute affected blur area.
-    srcRect.inset(-sigma3, -sigma3);
-    clipRect.inset(-sigma3, -sigma3);
+    srcRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
+    clipRect.inset(SkFloatToScalar(-sigma3), SkFloatToScalar(-sigma3));
     srcRect.intersect(clipRect);
     SkRect finalRect = srcRect;
     SkIRect finalIRect;
@@ -930,118 +765,118 @@
     }
     GrPoint offset = GrPoint::Make(-srcRect.fLeft, -srcRect.fTop);
     srcRect.offset(offset);
-    const GrTextureDesc desc = {
-        kRenderTarget_GrTextureFlagBit,
-        srcRect.width(),
-        srcRect.height(),
-        // We actually only need A8, but it often isn't supported as a
-        // render target
-        kRGBA_8888_PM_GrPixelConfig,
-        {0} // samples
-    };
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = SkScalarCeilToInt(srcRect.width());
+    desc.fHeight = SkScalarCeilToInt(srcRect.height());
+    // We actually only need A8, but it often isn't supported as a
+    // render target so default to RGBA_8888
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    if (context->isConfigRenderable(kAlpha_8_GrPixelConfig)) {
+        desc.fConfig = kAlpha_8_GrPixelConfig;
+    }
 
     GrAutoScratchTexture pathEntry(context, desc);
     GrTexture* pathTexture = pathEntry.texture();
     if (NULL == pathTexture) {
         return false;
     }
-    GrRenderTarget* oldRenderTarget = context->getRenderTarget();
-    // Once this code moves into GrContext, this should be changed to use
-    // an AutoClipRestore.
-    GrClip oldClip = context->getClip();
-    context->setRenderTarget(pathTexture->asRenderTarget());
-    context->setClip(srcRect);
-    context->clear(NULL, 0);
-    GrPaint tempPaint;
-    tempPaint.reset();
 
-    GrAutoMatrix avm(context, GrMatrix::I());
-    tempPaint.fAntiAlias = grp->fAntiAlias;
-    if (tempPaint.fAntiAlias) {
-        // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
-        // blend coeff of zero requires dual source blending support in order
-        // to properly blend partially covered pixels. This means the AA
-        // code path may not be taken. So we use a dst blend coeff of ISA. We
-        // could special case AA draws to a dst surface with known alpha=0 to
-        // use a zero dst coeff when dual source blending isn't available.
-        tempPaint.fSrcBlendCoeff = kOne_BlendCoeff;
-        tempPaint.fDstBlendCoeff = kISC_BlendCoeff;
-    }
-    // Draw hard shadow to pathTexture with path topleft at origin 0,0.
-    context->drawPath(tempPaint, path, skToGrFillType(path.getFillType()), &offset);
+    SkAutoTUnref<GrTexture> blurTexture;
 
-    GrAutoScratchTexture temp1, temp2;
-    // If we're doing a normal blur, we can clobber the pathTexture in the
-    // gaussianBlur.  Otherwise, we need to save it for later compositing.
-    bool isNormalBlur = blurType == SkMaskFilter::kNormal_BlurType;
-    GrTexture* blurTexture = gaussianBlur(context, pathTexture,
-                                          &temp1, isNormalBlur ? NULL : &temp2,
-                                          srcRect, sigma, sigma);
+    {
+        GrContext::AutoRenderTarget art(context, pathTexture->asRenderTarget());
+        GrContext::AutoClip ac(context, srcRect);
 
-    if (!isNormalBlur) {
-        GrPaint paint;
-        paint.reset();
-        paint.textureSampler(0)->setFilter(GrSamplerState::kNearest_Filter);
-        paint.textureSampler(0)->matrix()->setIDiv(pathTexture->width(),
-                                                   pathTexture->height());
-        // Blend pathTexture over blurTexture.
-        context->setRenderTarget(blurTexture->asRenderTarget());
-        paint.setTexture(0, pathTexture);
-        if (SkMaskFilter::kInner_BlurType == blurType) {
-            // inner:  dst = dst * src
-            paint.fSrcBlendCoeff = kDC_BlendCoeff;
-            paint.fDstBlendCoeff = kZero_BlendCoeff;
-        } else if (SkMaskFilter::kSolid_BlurType == blurType) {
-            // solid:  dst = src + dst - src * dst
-            //             = (1 - dst) * src + 1 * dst
-            paint.fSrcBlendCoeff = kIDC_BlendCoeff;
-            paint.fDstBlendCoeff = kOne_BlendCoeff;
-        } else if (SkMaskFilter::kOuter_BlurType == blurType) {
-            // outer:  dst = dst * (1 - src)
-            //             = 0 * src + (1 - src) * dst
-            paint.fSrcBlendCoeff = kZero_BlendCoeff;
-            paint.fDstBlendCoeff = kISC_BlendCoeff;
+        context->clear(NULL, 0);
+
+        GrPaint tempPaint;
+        if (grp->isAntiAlias()) {
+            tempPaint.setAntiAlias(true);
+            // AA uses the "coverage" stages on GrDrawTarget. Coverage with a dst
+            // blend coeff of zero requires dual source blending support in order
+            // to properly blend partially covered pixels. This means the AA
+            // code path may not be taken. So we use a dst blend coeff of ISA. We
+            // could special case AA draws to a dst surface with known alpha=0 to
+            // use a zero dst coeff when dual source blending isn't available.f
+            tempPaint.setBlendFunc(kOne_GrBlendCoeff, kISC_GrBlendCoeff);
         }
-        context->drawRect(paint, srcRect);
-    }
-    context->setRenderTarget(oldRenderTarget);
-    context->setClip(oldClip);
 
-    if (grp->hasTextureOrMask()) {
-        GrMatrix inverse;
-        if (!matrix.invert(&inverse)) {
+        GrContext::AutoMatrix am;
+
+        // Draw hard shadow to pathTexture with path top-left at origin using tempPaint.
+        SkMatrix translate;
+        translate.setTranslate(offset.fX, offset.fY);
+        am.set(context, translate);
+        context->drawPath(tempPaint, devPath, stroke);
+
+        // If we're doing a normal blur, we can clobber the pathTexture in the
+        // gaussianBlur.  Otherwise, we need to save it for later compositing.
+        bool isNormalBlur = blurType == SkMaskFilter::kNormal_BlurType;
+        blurTexture.reset(context->gaussianBlur(pathTexture, isNormalBlur,
+                                                srcRect, sigma, sigma));
+        if (NULL == blurTexture) {
             return false;
         }
-        grp->preConcatActiveSamplerMatrices(inverse);
+
+        if (!isNormalBlur) {
+            context->setIdentityMatrix();
+            GrPaint paint;
+            SkMatrix matrix;
+            matrix.setIDiv(pathTexture->width(), pathTexture->height());
+            // Blend pathTexture over blurTexture.
+            context->setRenderTarget(blurTexture->asRenderTarget());
+            paint.colorStage(0)->setEffect(
+                GrSimpleTextureEffect::Create(pathTexture, matrix))->unref();
+            if (SkMaskFilter::kInner_BlurType == blurType) {
+                // inner:  dst = dst * src
+                paint.setBlendFunc(kDC_GrBlendCoeff, kZero_GrBlendCoeff);
+            } else if (SkMaskFilter::kSolid_BlurType == blurType) {
+                // solid:  dst = src + dst - src * dst
+                //             = (1 - dst) * src + 1 * dst
+                paint.setBlendFunc(kIDC_GrBlendCoeff, kOne_GrBlendCoeff);
+            } else if (SkMaskFilter::kOuter_BlurType == blurType) {
+                // outer:  dst = dst * (1 - src)
+                //             = 0 * src + (1 - src) * dst
+                paint.setBlendFunc(kZero_GrBlendCoeff, kISC_GrBlendCoeff);
+            }
+            context->drawRect(paint, srcRect);
+        }
     }
 
-    static const int MASK_IDX = GrPaint::kMaxMasks - 1;
-    // we assume the last mask index is available for use
-    GrAssert(NULL == grp->getMask(MASK_IDX));
-    grp->setMask(MASK_IDX, blurTexture);
-    grp->maskSampler(MASK_IDX)->reset();
+    GrContext::AutoMatrix am;
+    if (!am.setIdentity(context, grp)) {
+        return false;
+    }
 
-    grp->maskSampler(MASK_IDX)->matrix()->setTranslate(-finalRect.fLeft,
-                                                       -finalRect.fTop);
-    grp->maskSampler(MASK_IDX)->matrix()->postIDiv(blurTexture->width(),
-                                                   blurTexture->height());
+    static const int MASK_IDX = GrPaint::kMaxCoverageStages - 1;
+    // we assume the last mask index is available for use
+    GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
+
+    SkMatrix matrix;
+    matrix.setTranslate(-finalRect.fLeft, -finalRect.fTop);
+    matrix.postIDiv(blurTexture->width(), blurTexture->height());
+
+    grp->coverageStage(MASK_IDX)->reset();
+    grp->coverageStage(MASK_IDX)->setEffect(
+        GrSimpleTextureEffect::Create(blurTexture, matrix))->unref();
     context->drawRect(*grp, finalRect);
     return true;
 }
 
-static bool drawWithMaskFilter(GrContext* context, const SkPath& path,
-                               SkMaskFilter* filter, const SkMatrix& matrix,
-                               const SkRegion& clip, SkBounder* bounder,
-                               GrPaint* grp) {
+bool drawWithMaskFilter(GrContext* context, const SkPath& devPath,
+                        SkMaskFilter* filter, const SkRegion& clip, SkBounder* bounder,
+                        GrPaint* grp, SkPaint::Style style) {
     SkMask  srcM, dstM;
 
-    if (!SkDraw::DrawToMask(path, &clip.getBounds(), filter, &matrix, &srcM,
-                            SkMask::kComputeBoundsAndRenderImage_CreateMode)) {
+    if (!SkDraw::DrawToMask(devPath, &clip.getBounds(), filter, &context->getMatrix(), &srcM,
+                            SkMask::kComputeBoundsAndRenderImage_CreateMode, style)) {
         return false;
     }
     SkAutoMaskFreeImage autoSrc(srcM.fImage);
 
-    if (!filter->filterMask(&dstM, srcM, matrix, NULL)) {
+    if (!filter->filterMask(&dstM, srcM, context->getMatrix(), NULL)) {
         return false;
     }
     // this will free-up dstM when we're done (allocated in filterMask())
@@ -1055,20 +890,14 @@
     }
 
     // we now have a device-aligned 8bit mask in dstM, ready to be drawn using
-    // the current clip (and identity matrix) and grpaint settings
+    // the current clip (and identity matrix) and GrPaint settings
+    GrContext::AutoMatrix am;
+    am.setIdentity(context, grp);
 
-    // used to compute inverse view, if necessary
-    GrMatrix ivm = context->getMatrix();
-
-    GrAutoMatrix avm(context, GrMatrix::I());
-
-    const GrTextureDesc desc = {
-        kNone_GrTextureFlags,
-        dstM.fBounds.width(),
-        dstM.fBounds.height(),
-        kAlpha_8_GrPixelConfig,
-        {0}, // samples
-    };
+    GrTextureDesc desc;
+    desc.fWidth = dstM.fBounds.width();
+    desc.fHeight = dstM.fBounds.height();
+    desc.fConfig = kAlpha_8_GrPixelConfig;
 
     GrAutoScratchTexture ast(context, desc);
     GrTexture* texture = ast.texture();
@@ -1079,61 +908,53 @@
     texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
                                dstM.fImage, dstM.fRowBytes);
 
-    if (grp->hasTextureOrMask() && ivm.invert(&ivm)) {
-        grp->preConcatActiveSamplerMatrices(ivm);
-    }
-
-    static const int MASK_IDX = GrPaint::kMaxMasks - 1;
+    static const int MASK_IDX = GrPaint::kMaxCoverageStages - 1;
     // we assume the last mask index is available for use
-    GrAssert(NULL == grp->getMask(MASK_IDX));
-    grp->setMask(MASK_IDX, texture);
-    grp->maskSampler(MASK_IDX)->reset();
+    GrAssert(!grp->isCoverageStageEnabled(MASK_IDX));
 
+    SkMatrix m;
+    m.setTranslate(-dstM.fBounds.fLeft*SK_Scalar1, -dstM.fBounds.fTop*SK_Scalar1);
+    m.postIDiv(texture->width(), texture->height());
+
+    grp->coverageStage(MASK_IDX)->setEffect(GrSimpleTextureEffect::Create(texture, m))->unref();
     GrRect d;
-    d.setLTRB(GrIntToScalar(dstM.fBounds.fLeft),
-              GrIntToScalar(dstM.fBounds.fTop),
-              GrIntToScalar(dstM.fBounds.fRight),
-              GrIntToScalar(dstM.fBounds.fBottom));
+    d.setLTRB(SkIntToScalar(dstM.fBounds.fLeft),
+              SkIntToScalar(dstM.fBounds.fTop),
+              SkIntToScalar(dstM.fBounds.fRight),
+              SkIntToScalar(dstM.fBounds.fBottom));
 
-    GrMatrix* m = grp->maskSampler(MASK_IDX)->matrix();
-    m->setTranslate(-dstM.fBounds.fLeft*SK_Scalar1,
-                         -dstM.fBounds.fTop*SK_Scalar1);
-    m->postIDiv(texture->width(), texture->height());
     context->drawRect(*grp, d);
     return true;
 }
 
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 void SkGpuDevice::drawPath(const SkDraw& draw, const SkPath& origSrcPath,
                            const SkPaint& paint, const SkMatrix* prePathMatrix,
                            bool pathIsMutable) {
-    CHECK_SHOULD_DRAW(draw);
-
-    bool             doFill = true;
-
-    SkScalar coverage = SK_Scalar1;
-    // can we cheat, and threat a thin stroke as a hairline w/ coverage
-    // if we can, we draw lots faster (raster device does this same test)
-    if (SkDrawTreatAsHairline(paint, *draw.fMatrix, &coverage)) {
-        doFill = false;
-    }
+    CHECK_FOR_NODRAW_ANNOTATION(paint);
+    CHECK_SHOULD_DRAW(draw, false);
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
-    if (!this->skPaint2GrPaintShader(paint,
-                                     &act,
-                                     *draw.fMatrix,
-                                     &grPaint,
-                                     true)) {
+    if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
         return;
     }
 
-    grPaint.fCoverage = SkScalarRoundToInt(coverage * grPaint.fCoverage);
+    // can we cheat, and treat a thin stroke as a hairline w/ coverage
+    // if we can, we draw lots faster (raster device does this same test)
+    SkScalar hairlineCoverage;
+    bool doHairLine = SkDrawTreatAsHairline(paint, fContext->getMatrix(), &hairlineCoverage);
+    if (doHairLine) {
+        grPaint.setCoverage(SkScalarRoundToInt(hairlineCoverage * grPaint.getCoverage()));
+    }
 
     // If we have a prematrix, apply it to the path, optimizing for the case
     // where the original path can in fact be modified in place (even though
     // its parameter type is const).
     SkPath* pathPtr = const_cast<SkPath*>(&origSrcPath);
-    SkPath  tmpPath;
+    SkPath  tmpPath, effectPath;
 
     if (prePathMatrix) {
         SkPath* result = pathPtr;
@@ -1150,54 +971,42 @@
     // at this point we're done with prePathMatrix
     SkDEBUGCODE(prePathMatrix = (const SkMatrix*)0x50FF8001;)
 
-    if (paint.getPathEffect() ||
-        (doFill && paint.getStyle() != SkPaint::kFill_Style)) {
-        // it is safe to use tmpPath here, even if we already used it for the
-        // prepathmatrix, since getFillPath can take the same object for its
-        // input and output safely.
-        doFill = paint.getFillPath(*pathPtr, &tmpPath);
-        pathPtr = &tmpPath;
+    SkStrokeRec stroke(paint);
+    SkPathEffect* pathEffect = paint.getPathEffect();
+    const SkRect* cullRect = NULL;  // TODO: what is our bounds?
+    if (pathEffect && pathEffect->filterPath(&effectPath, *pathPtr, &stroke,
+                                             cullRect)) {
+        pathPtr = &effectPath;
+    }
+
+    if (!pathEffect && doHairLine) {
+        stroke.setHairlineStyle();
     }
 
     if (paint.getMaskFilter()) {
+        if (!stroke.isHairlineStyle()) {
+            if (stroke.applyToPath(&tmpPath, *pathPtr)) {
+                pathPtr = &tmpPath;
+                stroke.setFillStyle();
+            }
+        }
+
         // avoid possibly allocating a new path in transform if we can
         SkPath* devPathPtr = pathIsMutable ? pathPtr : &tmpPath;
 
         // transform the path into device space
-        pathPtr->transform(*draw.fMatrix, devPathPtr);
-        if (!drawWithGPUMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
-                                   *draw.fMatrix, *draw.fClip, draw.fBounder,
-                                   &grPaint)) {
+        pathPtr->transform(fContext->getMatrix(), devPathPtr);
+        if (!drawWithGPUMaskFilter(fContext, *devPathPtr, stroke, paint.getMaskFilter(),
+                                   *draw.fClip, draw.fBounder, &grPaint)) {
+            SkPaint::Style style = stroke.isHairlineStyle() ? SkPaint::kStroke_Style :
+                                                              SkPaint::kFill_Style;
             drawWithMaskFilter(fContext, *devPathPtr, paint.getMaskFilter(),
-                               *draw.fMatrix, *draw.fClip, draw.fBounder,
-                               &grPaint);
+                               *draw.fClip, draw.fBounder, &grPaint, style);
         }
         return;
     }
 
-    GrPathFill fill = kHairLine_PathFill;
-
-    if (doFill) {
-        switch (pathPtr->getFillType()) {
-            case SkPath::kWinding_FillType:
-                fill = kWinding_PathFill;
-                break;
-            case SkPath::kEvenOdd_FillType:
-                fill = kEvenOdd_PathFill;
-                break;
-            case SkPath::kInverseWinding_FillType:
-                fill = kInverseWinding_PathFill;
-                break;
-            case SkPath::kInverseEvenOdd_FillType:
-                fill = kInverseEvenOdd_PathFill;
-                break;
-            default:
-                SkDebugf("Unsupported path fill type\n");
-                return;
-        }
-    }
-
-    fContext->drawPath(grPaint, *pathPtr, fill);
+    fContext->drawPath(grPaint, *pathPtr, stroke);
 }
 
 namespace {
@@ -1209,7 +1018,7 @@
 }
 
 inline int determine_tile_size(const SkBitmap& bitmap,
-                               const SkIRect* srcRectPtr,
+                               const SkRect& src,
                                int maxTextureSize) {
     static const int kSmallTileSize = 1 << 10;
     if (maxTextureSize <= kSmallTileSize) {
@@ -1219,23 +1028,20 @@
     size_t maxTexTotalTileSize;
     size_t smallTotalTileSize;
 
-    if (NULL == srcRectPtr) {
-        int w = bitmap.width();
-        int h = bitmap.height();
-        maxTexTotalTileSize = get_tile_count(0, 0, w, h, maxTextureSize);
-        smallTotalTileSize = get_tile_count(0, 0, w, h, kSmallTileSize);
-    } else {
-        maxTexTotalTileSize = get_tile_count(srcRectPtr->fLeft,
-                                             srcRectPtr->fTop,
-                                             srcRectPtr->fRight,
-                                             srcRectPtr->fBottom,
-                                             maxTextureSize);
-        smallTotalTileSize = get_tile_count(srcRectPtr->fLeft,
-                                            srcRectPtr->fTop,
-                                            srcRectPtr->fRight,
-                                            srcRectPtr->fBottom,
-                                            kSmallTileSize);
-    }
+    SkIRect iSrc;
+    src.roundOut(&iSrc);
+
+    maxTexTotalTileSize = get_tile_count(iSrc.fLeft,
+                                         iSrc.fTop,
+                                         iSrc.fRight,
+                                         iSrc.fBottom,
+                                         maxTextureSize);
+    smallTotalTileSize = get_tile_count(iSrc.fLeft,
+                                        iSrc.fTop,
+                                        iSrc.fRight,
+                                        iSrc.fBottom,
+                                        kSmallTileSize);
+
     maxTexTotalTileSize *= maxTextureSize * maxTextureSize;
     smallTotalTileSize *= kSmallTileSize * kSmallTileSize;
 
@@ -1248,11 +1054,8 @@
 }
 
 bool SkGpuDevice::shouldTileBitmap(const SkBitmap& bitmap,
-                                   const GrSamplerState& sampler,
-                                   const SkIRect* srcRectPtr,
-                                   int* tileSize) const {
-    SkASSERT(NULL != tileSize);
-
+                                   const GrTextureParams& params,
+                                   const SkRect* srcRectPtr) const {
     // if bitmap is explictly texture backed then just use the texture
     if (NULL != bitmap.getTexture()) {
         return false;
@@ -1262,7 +1065,6 @@
     const int maxTextureSize = fContext->getMaxTextureSize();
     if (bitmap.width() > maxTextureSize ||
         bitmap.height() > maxTextureSize) {
-        *tileSize = determine_tile_size(bitmap, srcRectPtr, maxTextureSize);
         return true;
     }
     // if we are going to have to draw the whole thing, then don't tile
@@ -1270,7 +1072,7 @@
         return false;
     }
     // if the entire texture is already in our cache then no reason to tile it
-    if (this->isBitmapInTextureCache(bitmap, sampler)) {
+    if (GrIsBitmapInCache(fContext, bitmap, &params)) {
         return false;
     }
 
@@ -1288,11 +1090,9 @@
         return false;
     }
 
-    SkFixed fracUsed =
-        SkFixedMul((srcRectPtr->width() << 16) / bitmap.width(),
-                   (srcRectPtr->height() << 16) / bitmap.height());
-    if (fracUsed <= SK_FixedHalf) {
-        *tileSize = determine_tile_size(bitmap, srcRectPtr, maxTextureSize);
+    SkScalar fracUsed = SkScalarMul(srcRectPtr->width() / bitmap.width(),
+                                    srcRectPtr->height() / bitmap.height());
+    if (fracUsed <= SK_ScalarHalf) {
         return true;
     } else {
         return false;
@@ -1304,11 +1104,30 @@
                              const SkIRect* srcRectPtr,
                              const SkMatrix& m,
                              const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
 
-    SkIRect srcRect;
+    SkRect  tmp;
+    SkRect* tmpPtr = NULL;
+
+    // convert from SkIRect to SkRect
+    if (NULL != srcRectPtr) {
+        tmp.set(*srcRectPtr);
+        tmpPtr = &tmp;
+    }
+
+    // We cannot call drawBitmapRect here since 'm' could be anything
+    this->drawBitmapCommon(draw, bitmap, tmpPtr, m, paint);
+}
+
+void SkGpuDevice::drawBitmapCommon(const SkDraw& draw,
+                                   const SkBitmap& bitmap,
+                                   const SkRect* srcRectPtr,
+                                   const SkMatrix& m,
+                                   const SkPaint& paint) {
+    CHECK_SHOULD_DRAW(draw, false);
+
+    SkRect srcRect;
     if (NULL == srcRectPtr) {
-        srcRect.set(0, 0, bitmap.width(), bitmap.height());
+        srcRect.set(0, 0, SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
     } else {
         srcRect = *srcRectPtr;
     }
@@ -1316,104 +1135,159 @@
     if (paint.getMaskFilter()){
         // Convert the bitmap to a shader so that the rect can be drawn
         // through drawRect, which supports mask filters.
+        SkMatrix        newM(m);
         SkBitmap        tmp;    // subset of bitmap, if necessary
         const SkBitmap* bitmapPtr = &bitmap;
-        if (srcRectPtr) {
-            if (!bitmap.extractSubset(&tmp, srcRect)) {
+        if (NULL != srcRectPtr) {
+            SkIRect iSrc;
+            srcRect.roundOut(&iSrc);
+            if (!bitmap.extractSubset(&tmp, iSrc)) {
                 return;     // extraction failed
             }
             bitmapPtr = &tmp;
-            srcRect.set(0,0, srcRect.width(), srcRect.height());
+            srcRect.offset(SkIntToScalar(-iSrc.fLeft), SkIntToScalar(-iSrc.fTop));
+            // The source rect has changed so update the matrix
+            newM.preTranslate(SkIntToScalar(iSrc.fLeft), SkIntToScalar(iSrc.fTop));
         }
-        SkPaint paintWithTexture(paint);
-        paintWithTexture.setShader(SkShader::CreateBitmapShader( *bitmapPtr,
-            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
-        SkRect ScalarRect;
-        ScalarRect.set(srcRect);
 
-        // Transform 'm' needs to be concatenated to the draw matrix,
-        // rather than transforming the primitive directly, so that 'm' will
+        SkPaint paintWithTexture(paint);
+        paintWithTexture.setShader(SkShader::CreateBitmapShader(*bitmapPtr,
+            SkShader::kClamp_TileMode, SkShader::kClamp_TileMode))->unref();
+
+        // Transform 'newM' needs to be concatenated to the current matrix,
+        // rather than transforming the primitive directly, so that 'newM' will
         // also affect the behavior of the mask filter.
         SkMatrix drawMatrix;
-        drawMatrix.setConcat(*draw.fMatrix, m);
+        drawMatrix.setConcat(fContext->getMatrix(), newM);
         SkDraw transformedDraw(draw);
         transformedDraw.fMatrix = &drawMatrix;
 
-        this->drawRect(transformedDraw, ScalarRect, paintWithTexture);
+        this->drawRect(transformedDraw, srcRect, paintWithTexture);
 
         return;
     }
 
     GrPaint grPaint;
-    if (!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
+
+    bool alphaOnly = !(SkBitmap::kA8_Config == bitmap.config());
+    if (!skPaint2GrPaintNoShader(this, paint, alphaOnly, false, &grPaint)) {
         return;
     }
-    GrSamplerState* sampler = grPaint.textureSampler(kBitmapTextureIdx);
-    if (paint.isFilterBitmap()) {
-        sampler->setFilter(GrSamplerState::kBilinear_Filter);
-    } else {
-        sampler->setFilter(GrSamplerState::kNearest_Filter);
-    }
+    GrTextureParams params;
+    params.setBilerp(paint.isFilterBitmap());
 
-    int tileSize;
-    if (!this->shouldTileBitmap(bitmap, *sampler, srcRectPtr, &tileSize)) {
+    if (!this->shouldTileBitmap(bitmap, params, srcRectPtr)) {
         // take the simple case
-        this->internalDrawBitmap(draw, bitmap, srcRect, m, &grPaint);
-        return;
+        this->internalDrawBitmap(bitmap, srcRect, m, params, &grPaint);
+    } else {
+        this->drawTiledBitmap(bitmap, srcRect, m, params, &grPaint);
     }
+}
 
-    // undo the translate done by SkCanvas
-    int DX = SkMax32(0, srcRect.fLeft);
-    int DY = SkMax32(0, srcRect.fTop);
+// Break 'bitmap' into several tiles to draw it since it has already
+// been determined to be too large to fit in VRAM
+void SkGpuDevice::drawTiledBitmap(const SkBitmap& bitmap,
+                                  const SkRect& srcRect,
+                                  const SkMatrix& m,
+                                  const GrTextureParams& params,
+                                  GrPaint* grPaint) {
+    const int maxTextureSize = fContext->getMaxTextureSize();
+
+    int tileSize = determine_tile_size(bitmap, srcRect, maxTextureSize);
+
     // compute clip bounds in local coordinates
-    SkIRect clipRect;
+    SkRect clipRect;
     {
-        SkRect r;
-        r.set(draw.fClip->getBounds());
+        const GrRenderTarget* rt = fContext->getRenderTarget();
+        clipRect.setWH(SkIntToScalar(rt->width()), SkIntToScalar(rt->height()));
+        if (!fContext->getClip()->fClipStack->intersectRectWithClip(&clipRect)) {
+            return;
+        }
         SkMatrix matrix, inverse;
-        matrix.setConcat(*draw.fMatrix, m);
+        matrix.setConcat(fContext->getMatrix(), m);
         if (!matrix.invert(&inverse)) {
             return;
         }
-        inverse.mapRect(&r);
-        r.roundOut(&clipRect);
-        // apply the canvas' translate to our local clip
-        clipRect.offset(DX, DY);
+        inverse.mapRect(&clipRect);
     }
 
     int nx = bitmap.width() / tileSize;
     int ny = bitmap.height() / tileSize;
     for (int x = 0; x <= nx; x++) {
         for (int y = 0; y <= ny; y++) {
-            SkIRect tileR;
-            tileR.set(x * tileSize, y * tileSize,
-                      (x + 1) * tileSize, (y + 1) * tileSize);
-            if (!SkIRect::Intersects(tileR, clipRect)) {
+            SkRect tileR;
+            tileR.set(SkIntToScalar(x * tileSize),
+                      SkIntToScalar(y * tileSize),
+                      SkIntToScalar((x + 1) * tileSize),
+                      SkIntToScalar((y + 1) * tileSize));
+
+            if (!SkRect::Intersects(tileR, clipRect)) {
                 continue;
             }
 
-            SkIRect srcR = tileR;
-            if (!srcR.intersect(srcRect)) {
+            if (!tileR.intersect(srcRect)) {
                 continue;
             }
 
             SkBitmap tmpB;
-            if (bitmap.extractSubset(&tmpB, tileR)) {
+            SkIRect iTileR;
+            tileR.roundOut(&iTileR);
+            if (bitmap.extractSubset(&tmpB, iTileR)) {
                 // now offset it to make it "local" to our tmp bitmap
-                srcR.offset(-tileR.fLeft, -tileR.fTop);
-
+                tileR.offset(SkIntToScalar(-iTileR.fLeft), SkIntToScalar(-iTileR.fTop));
                 SkMatrix tmpM(m);
-                {
-                    int dx = tileR.fLeft - DX + SkMax32(0, srcR.fLeft);
-                    int dy = tileR.fTop -  DY + SkMax32(0, srcR.fTop);
-                    tmpM.preTranslate(SkIntToScalar(dx), SkIntToScalar(dy));
-                }
-                this->internalDrawBitmap(draw, tmpB, srcR, tmpM, &grPaint);
+                tmpM.preTranslate(SkIntToScalar(iTileR.fLeft),
+                                  SkIntToScalar(iTileR.fTop));
+
+                this->internalDrawBitmap(tmpB, tileR, tmpM, params, grPaint);
             }
         }
     }
 }
 
+namespace {
+
+bool hasAlignedSamples(const SkRect& srcRect, const SkRect& transformedRect) {
+    // detect pixel disalignment
+    if (SkScalarAbs(SkScalarRoundToScalar(transformedRect.left()) -
+            transformedRect.left()) < COLOR_BLEED_TOLERANCE &&
+        SkScalarAbs(SkScalarRoundToScalar(transformedRect.top()) -
+            transformedRect.top()) < COLOR_BLEED_TOLERANCE &&
+        SkScalarAbs(transformedRect.width() - srcRect.width()) <
+            COLOR_BLEED_TOLERANCE &&
+        SkScalarAbs(transformedRect.height() - srcRect.height()) <
+            COLOR_BLEED_TOLERANCE) {
+        return true;
+    }
+    return false;
+}
+
+bool mayColorBleed(const SkRect& srcRect, const SkRect& transformedRect,
+                   const SkMatrix& m) {
+    // Only gets called if hasAlignedSamples returned false.
+    // So we can assume that sampling is axis aligned but not texel aligned.
+    GrAssert(!hasAlignedSamples(srcRect, transformedRect));
+    SkRect innerSrcRect(srcRect), innerTransformedRect,
+        outerTransformedRect(transformedRect);
+    innerSrcRect.inset(SK_ScalarHalf, SK_ScalarHalf);
+    m.mapRect(&innerTransformedRect, innerSrcRect);
+
+    // The gap between outerTransformedRect and innerTransformedRect
+    // represents the projection of the source border area, which is
+    // problematic for color bleeding.  We must check whether any
+    // destination pixels sample the border area.
+    outerTransformedRect.inset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
+    innerTransformedRect.outset(COLOR_BLEED_TOLERANCE, COLOR_BLEED_TOLERANCE);
+    SkIRect outer, inner;
+    outerTransformedRect.round(&outer);
+    innerTransformedRect.round(&inner);
+    // If the inner and outer rects round to the same result, it means the
+    // border does not overlap any pixel centers. Yay!
+    return inner != outer;
+}
+
+} // unnamed namespace
+
 /*
  *  This is called by drawBitmap(), which has to handle images that may be too
  *  large to be represented by a single texture.
@@ -1421,10 +1295,10 @@
  *  internalDrawBitmap assumes that the specified bitmap will fit in a texture
  *  and that non-texture portion of the GrPaint has already been setup.
  */
-void SkGpuDevice::internalDrawBitmap(const SkDraw& draw,
-                                     const SkBitmap& bitmap,
-                                     const SkIRect& srcRect,
+void SkGpuDevice::internalDrawBitmap(const SkBitmap& bitmap,
+                                     const SkRect& srcRect,
                                      const SkMatrix& m,
+                                     const GrTextureParams& params,
                                      GrPaint* grPaint) {
     SkASSERT(bitmap.width() <= fContext->getMaxTextureSize() &&
              bitmap.height() <= fContext->getMaxTextureSize());
@@ -1435,62 +1309,142 @@
         return;
     }
 
-    GrSamplerState* sampler = grPaint->textureSampler(kBitmapTextureIdx);
-
-    sampler->setWrapX(GrSamplerState::kClamp_WrapMode);
-    sampler->setWrapY(GrSamplerState::kClamp_WrapMode);
-    sampler->setSampleMode(GrSamplerState::kNormal_SampleMode);
-    sampler->matrix()->reset();
-
     GrTexture* texture;
-    SkAutoCachedTexture act(this, bitmap, sampler, &texture);
+    SkAutoCachedTexture act(this, bitmap, &params, &texture);
     if (NULL == texture) {
         return;
     }
 
-    grPaint->setTexture(kBitmapTextureIdx, texture);
-
-    GrRect dstRect = SkRect::MakeWH(GrIntToScalar(srcRect.width()),
-                                    GrIntToScalar(srcRect.height()));
+    GrRect dstRect(srcRect);
     GrRect paintRect;
-    paintRect.setLTRB(GrFixedToScalar((srcRect.fLeft << 16) / bitmap.width()),
-                      GrFixedToScalar((srcRect.fTop << 16) / bitmap.height()),
-                      GrFixedToScalar((srcRect.fRight << 16) / bitmap.width()),
-                      GrFixedToScalar((srcRect.fBottom << 16) / bitmap.height()));
+    SkScalar wInv = SkScalarInvert(SkIntToScalar(bitmap.width()));
+    SkScalar hInv = SkScalarInvert(SkIntToScalar(bitmap.height()));
+    paintRect.setLTRB(SkScalarMul(srcRect.fLeft,   wInv),
+                      SkScalarMul(srcRect.fTop,    hInv),
+                      SkScalarMul(srcRect.fRight,  wInv),
+                      SkScalarMul(srcRect.fBottom, hInv));
 
-    if (GrSamplerState::kNearest_Filter != sampler->getFilter() &&
-        (srcRect.width() < bitmap.width() ||
-         srcRect.height() < bitmap.height())) {
-        // If drawing a subrect of the bitmap and filtering is enabled,
-        // use a constrained texture domain to avoid color bleeding
-        GrScalar left, top, right, bottom;
-        if (srcRect.width() > 1) {
-            GrScalar border = GR_ScalarHalf / bitmap.width();
+    bool needsTextureDomain = false;
+    if (params.isBilerp()) {
+        // Need texture domain if drawing a sub rect.
+        needsTextureDomain = srcRect.width() < bitmap.width() ||
+                             srcRect.height() < bitmap.height();
+        if (m.rectStaysRect() && fContext->getMatrix().rectStaysRect()) {
+            // sampling is axis-aligned
+            GrRect transformedRect;
+            SkMatrix srcToDeviceMatrix(m);
+            srcToDeviceMatrix.postConcat(fContext->getMatrix());
+            srcToDeviceMatrix.mapRect(&transformedRect, srcRect);
+
+            if (hasAlignedSamples(srcRect, transformedRect)) {
+                // We could also turn off filtering here (but we already did a cache lookup with
+                // params).
+                needsTextureDomain = false;
+            } else {
+                needsTextureDomain = needsTextureDomain &&
+                    mayColorBleed(srcRect, transformedRect, m);
+            }
+        }
+    }
+
+    GrRect textureDomain = GrRect::MakeEmpty();
+    SkAutoTUnref<GrEffectRef> effect;
+    if (needsTextureDomain) {
+        // Use a constrained texture domain to avoid color bleeding
+        SkScalar left, top, right, bottom;
+        if (srcRect.width() > SK_Scalar1) {
+            SkScalar border = SK_ScalarHalf / bitmap.width();
             left = paintRect.left() + border;
             right = paintRect.right() - border;
         } else {
-            left = right = GrScalarHalf(paintRect.left() + paintRect.right());
+            left = right = SkScalarHalf(paintRect.left() + paintRect.right());
         }
-        if (srcRect.height() > 1) {
-            GrScalar border = GR_ScalarHalf / bitmap.height();
+        if (srcRect.height() > SK_Scalar1) {
+            SkScalar border = SK_ScalarHalf / bitmap.height();
             top = paintRect.top() + border;
             bottom = paintRect.bottom() - border;
         } else {
-            top = bottom = GrScalarHalf(paintRect.top() + paintRect.bottom());
+            top = bottom = SkScalarHalf(paintRect.top() + paintRect.bottom());
         }
-        GrRect textureDomain;
         textureDomain.setLTRB(left, top, right, bottom);
-        sampler->setTextureDomain(textureDomain);
+        effect.reset(GrTextureDomainEffect::Create(texture,
+                                                   SkMatrix::I(),
+                                                   textureDomain,
+                                                   GrTextureDomainEffect::kClamp_WrapMode,
+                                                   params.isBilerp()));
+    } else {
+        effect.reset(GrSimpleTextureEffect::Create(texture, SkMatrix::I(), params));
     }
-
+    grPaint->colorStage(kBitmapTextureIdx)->setEffect(effect);
     fContext->drawRectToRect(*grPaint, dstRect, paintRect, &m);
 }
 
-void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
-                            int left, int top, const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+namespace {
 
-    SkAutoLockPixels alp(bitmap);
+void apply_effect(GrContext* context,
+                  GrTexture* srcTexture,
+                  GrTexture* dstTexture,
+                  const GrRect& rect,
+                  GrEffectRef* effect) {
+    SkASSERT(srcTexture && srcTexture->getContext() == context);
+    GrContext::AutoMatrix am;
+    am.setIdentity(context);
+    GrContext::AutoRenderTarget art(context, dstTexture->asRenderTarget());
+    GrContext::AutoClip acs(context, rect);
+
+    GrPaint paint;
+    paint.colorStage(0)->setEffect(effect);
+    context->drawRect(paint, rect);
+}
+
+};
+
+static SkBitmap wrap_texture(GrTexture* texture) {
+    SkBitmap result;
+    bool dummy;
+    SkBitmap::Config config = grConfig2skConfig(texture->config(), &dummy);
+    result.setConfig(config, texture->width(), texture->height());
+    result.setPixelRef(SkNEW_ARGS(SkGrPixelRef, (texture)))->unref();
+    return result;
+}
+
+static bool filter_texture(SkDevice* device, GrContext* context,
+                           GrTexture* texture, SkImageFilter* filter,
+                           int w, int h, SkBitmap* result) {
+    GrAssert(filter);
+    SkDeviceImageFilterProxy proxy(device);
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit,
+    desc.fWidth = w;
+    desc.fHeight = h;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+    GrEffectRef* effect;
+
+    if (filter->canFilterImageGPU()) {
+        // Save the render target and set it to NULL, so we don't accidentally draw to it in the
+        // filter.  Also set the clip wide open and the matrix to identity.
+        GrContext::AutoWideOpenIdentityDraw awo(context, NULL);
+        return filter->filterImageGPU(&proxy, wrap_texture(texture), result);
+    } else if (filter->asNewEffect(&effect, texture)) {
+        GrAutoScratchTexture dst(context, desc);
+        SkRect r = SkRect::MakeWH(SkIntToScalar(w), SkIntToScalar(h));
+        apply_effect(context, texture, dst.texture(), r, effect);
+        SkAutoTUnref<GrTexture> resultTex(dst.detach());
+        effect->unref();
+        *result = wrap_texture(resultTex.get());
+        return true;
+    } else {
+        return false;
+    }
+}
+
+void SkGpuDevice::drawSprite(const SkDraw& draw, const SkBitmap& bitmap,
+                             int left, int top, const SkPaint& paint) {
+    // drawSprite is defined to be in device coords.
+    CHECK_SHOULD_DRAW(draw, true);
+
+    SkAutoLockPixels alp(bitmap, !bitmap.getTexture());
     if (!bitmap.getTexture() && !bitmap.readyToDraw()) {
         return;
     }
@@ -1499,134 +1453,159 @@
     int h = bitmap.height();
 
     GrPaint grPaint;
-    if(!this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
+    if(!skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
         return;
     }
 
-    GrAutoMatrix avm(fContext, GrMatrix::I());
-
-    GrSamplerState* sampler = grPaint.textureSampler(kBitmapTextureIdx);
+    GrEffectStage* stage = grPaint.colorStage(kBitmapTextureIdx);
 
     GrTexture* texture;
-    sampler->reset();
-    SkAutoCachedTexture act(this, bitmap, sampler, &texture);
+    stage->reset();
+    // draw sprite uses the default texture params
+    SkAutoCachedTexture act(this, bitmap, NULL, &texture);
+    grPaint.colorStage(kBitmapTextureIdx)->setEffect(
+        GrSimpleTextureEffect::Create(texture, SkMatrix::I()))->unref();
 
-    SkImageFilter* imageFilter = paint.getImageFilter();
-    SkSize blurSize;
-    SkISize radius;
-    if (NULL != imageFilter && imageFilter->asABlur(&blurSize)) {
-        GrAutoScratchTexture temp1, temp2;
-        GrTexture* blurTexture = gaussianBlur(fContext,
-                                              texture, &temp1, &temp2,
-                                              GrRect::MakeWH(w, h),
-                                              blurSize.width(),
-                                              blurSize.height());
-        texture = blurTexture;
-        grPaint.setTexture(kBitmapTextureIdx, texture);
-    } else if (NULL != imageFilter && imageFilter->asADilate(&radius)) {
-        const GrTextureDesc desc = {
-            kRenderTarget_GrTextureFlagBit,
-            w,
-            h,
-            kRGBA_8888_PM_GrPixelConfig,
-            {0} // samples
-        };
-        GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc);
-        texture = applyMorphology(fContext, texture, GrRect::MakeWH(w, h),
-                                  temp1.texture(), temp2.texture(),
-                                  GrSamplerState::kDilate_Filter, radius);
-        grPaint.setTexture(kBitmapTextureIdx, texture);
-    } else if (NULL != imageFilter && imageFilter->asAnErode(&radius)) {
-        const GrTextureDesc desc = {
-            kRenderTarget_GrTextureFlagBit,
-            w,
-            h,
-            kRGBA_8888_PM_GrPixelConfig,
-            {0} // samples
-        };
-        GrAutoScratchTexture temp1(fContext, desc), temp2(fContext, desc);
-        texture = applyMorphology(fContext, texture, GrRect::MakeWH(w, h),
-                                  temp1.texture(), temp2.texture(),
-                                  GrSamplerState::kErode_Filter, radius);
-        grPaint.setTexture(kBitmapTextureIdx, texture);
-    } else {
-        grPaint.setTexture(kBitmapTextureIdx, texture);
+    SkImageFilter* filter = paint.getImageFilter();
+    if (NULL != filter) {
+        SkBitmap filterBitmap;
+        if (filter_texture(this, fContext, texture, filter, w, h, &filterBitmap)) {
+            grPaint.colorStage(kBitmapTextureIdx)->setEffect(
+                GrSimpleTextureEffect::Create((GrTexture*) filterBitmap.getTexture(), SkMatrix::I()))->unref();
+            texture = (GrTexture*) filterBitmap.getTexture();
+            w = filterBitmap.width();
+            h = filterBitmap.height();
+        }
     }
 
     fContext->drawRectToRect(grPaint,
-                            GrRect::MakeXYWH(GrIntToScalar(left),
-                                            GrIntToScalar(top),
-                                            GrIntToScalar(w),
-                                            GrIntToScalar(h)),
-                            GrRect::MakeWH(GR_Scalar1 * w / texture->width(),
-                                        GR_Scalar1 * h / texture->height()));
+                            GrRect::MakeXYWH(SkIntToScalar(left),
+                                            SkIntToScalar(top),
+                                            SkIntToScalar(w),
+                                            SkIntToScalar(h)),
+                            GrRect::MakeWH(SK_Scalar1 * w / texture->width(),
+                                        SK_Scalar1 * h / texture->height()));
 }
 
-void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* dev,
-                            int x, int y, const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+void SkGpuDevice::drawBitmapRect(const SkDraw& draw, const SkBitmap& bitmap,
+                                 const SkRect* src, const SkRect& dst,
+                                 const SkPaint& paint) {
+    SkMatrix    matrix;
+    SkRect      bitmapBounds, tmpSrc;
+
+    bitmapBounds.set(0, 0,
+                     SkIntToScalar(bitmap.width()),
+                     SkIntToScalar(bitmap.height()));
+
+    // Compute matrix from the two rectangles
+    if (NULL != src) {
+        tmpSrc = *src;
+    } else {
+        tmpSrc = bitmapBounds;
+    }
+    matrix.setRectToRect(tmpSrc, dst, SkMatrix::kFill_ScaleToFit);
+
+    // clip the tmpSrc to the bounds of the bitmap. No check needed if src==null.
+    if (NULL != src) {
+        if (!bitmapBounds.contains(tmpSrc)) {
+            if (!tmpSrc.intersect(bitmapBounds)) {
+                return; // nothing to draw
+            }
+        }
+    }
+
+    this->drawBitmapCommon(draw, bitmap, &tmpSrc, matrix, paint);
+}
+
+void SkGpuDevice::drawDevice(const SkDraw& draw, SkDevice* device,
+                             int x, int y, const SkPaint& paint) {
+    // clear of the source device must occur before CHECK_SHOULD_DRAW
+    SkGpuDevice* dev = static_cast<SkGpuDevice*>(device);
+    if (dev->fNeedClear) {
+        // TODO: could check here whether we really need to draw at all
+        dev->clear(0x0);
+    }
+
+    // drawDevice is defined to be in device coords.
+    CHECK_SHOULD_DRAW(draw, true);
 
     GrPaint grPaint;
-    if (!((SkGpuDevice*)dev)->bindDeviceAsTexture(&grPaint) ||
-        !this->skPaint2GrPaintNoShader(paint, true, &grPaint, false)) {
+    grPaint.colorStage(kBitmapTextureIdx)->reset();
+    if (!dev->bindDeviceAsTexture(&grPaint) ||
+        !skPaint2GrPaintNoShader(this, paint, true, false, &grPaint)) {
         return;
     }
 
-    GrTexture* devTex = grPaint.getTexture(0);
+    GrTexture* devTex = (*grPaint.getColorStage(kBitmapTextureIdx).getEffect())->texture(0);
     SkASSERT(NULL != devTex);
 
     const SkBitmap& bm = dev->accessBitmap(false);
     int w = bm.width();
     int h = bm.height();
 
-    GrAutoMatrix avm(fContext, GrMatrix::I());
+    SkImageFilter* filter = paint.getImageFilter();
+    if (NULL != filter) {
+        SkBitmap filterBitmap;
+        if (filter_texture(this, fContext, devTex, filter, w, h, &filterBitmap)) {
+            grPaint.colorStage(kBitmapTextureIdx)->setEffect(
+                GrSimpleTextureEffect::Create((GrTexture*) filterBitmap.getTexture(), SkMatrix::I()))->unref();
+            devTex = (GrTexture*) filterBitmap.getTexture();
+            w = filterBitmap.width();
+            h = filterBitmap.height();
+        }
+    }
 
-    grPaint.textureSampler(kBitmapTextureIdx)->reset();
-
-    GrRect dstRect = GrRect::MakeXYWH(GrIntToScalar(x),
-                                      GrIntToScalar(y),
-                                      GrIntToScalar(w),
-                                      GrIntToScalar(h));
+    GrRect dstRect = GrRect::MakeXYWH(SkIntToScalar(x),
+                                      SkIntToScalar(y),
+                                      SkIntToScalar(w),
+                                      SkIntToScalar(h));
 
     // The device being drawn may not fill up its texture (saveLayer uses
     // the approximate ).
-    GrRect srcRect = GrRect::MakeWH(GR_Scalar1 * w / devTex->width(),
-                                    GR_Scalar1 * h / devTex->height());
+    GrRect srcRect = GrRect::MakeWH(SK_Scalar1 * w / devTex->width(),
+                                    SK_Scalar1 * h / devTex->height());
 
     fContext->drawRectToRect(grPaint, dstRect, srcRect);
 }
 
+bool SkGpuDevice::canHandleImageFilter(SkImageFilter* filter) {
+    if (!filter->asNewEffect(NULL, NULL) &&
+        !filter->canFilterImageGPU()) {
+        return false;
+    }
+    return true;
+}
+
 bool SkGpuDevice::filterImage(SkImageFilter* filter, const SkBitmap& src,
                               const SkMatrix& ctm,
                               SkBitmap* result, SkIPoint* offset) {
-    SkSize size;
-    SkISize radius;
-    if (!filter->asABlur(&size) && !filter->asADilate(&radius) && !filter->asAnErode(&radius)) {
+    // want explicitly our impl, so guard against a subclass of us overriding it
+    if (!this->SkGpuDevice::canHandleImageFilter(filter)) {
         return false;
     }
-    SkDevice* dev = this->createCompatibleDevice(SkBitmap::kARGB_8888_Config,
-                                                 src.width(),
-                                                 src.height(),
-                                                 false);
-    if (NULL == dev) {
+
+    SkAutoLockPixels alp(src, !src.getTexture());
+    if (!src.getTexture() && !src.readyToDraw()) {
         return false;
     }
-    SkAutoUnref aur(dev);
-    SkCanvas canvas(dev);
-    SkPaint paint;
-    paint.setImageFilter(filter);
-    canvas.drawSprite(src, 0, 0, &paint);
-    *result = dev->accessBitmap(false);
-    return true;
+
+    GrPaint paint;
+
+    GrTexture* texture;
+    // We assume here that the filter will not attempt to tile the src. Otherwise, this cache lookup
+    // must be pushed upstack.
+    SkAutoCachedTexture act(this, src, NULL, &texture);
+
+    return filter_texture(this, fContext, texture, filter, src.width(), src.height(), result);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 // must be in SkCanvas::VertexMode order
 static const GrPrimitiveType gVertexMode2PrimitiveType[] = {
-    kTriangles_PrimitiveType,
-    kTriangleStrip_PrimitiveType,
-    kTriangleFan_PrimitiveType,
+    kTriangles_GrPrimitiveType,
+    kTriangleStrip_GrPrimitiveType,
+    kTriangleFan_GrPrimitiveType,
 };
 
 void SkGpuDevice::drawVertices(const SkDraw& draw, SkCanvas::VertexMode vmode,
@@ -1635,29 +1614,22 @@
                               SkXfermode* xmode,
                               const uint16_t indices[], int indexCount,
                               const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+    CHECK_SHOULD_DRAW(draw, false);
 
     GrPaint grPaint;
-    SkAutoCachedTexture act;
     // we ignore the shader if texs is null.
     if (NULL == texs) {
-        if (!this->skPaint2GrPaintNoShader(paint,
-                                           false,
-                                           &grPaint,
-                                           NULL == colors)) {
+        if (!skPaint2GrPaintNoShader(this, paint, false, NULL == colors, &grPaint)) {
             return;
         }
     } else {
-        if (!this->skPaint2GrPaintShader(paint, &act,
-                                         *draw.fMatrix,
-                                         &grPaint,
-                                         NULL == colors)) {
+        if (!skPaint2GrPaintShader(this, paint, NULL == colors, &grPaint)) {
             return;
         }
     }
 
     if (NULL != xmode && NULL != texs && NULL != colors) {
-        if (!SkXfermode::IsMode(xmode, SkXfermode::kMultiply_Mode)) {
+        if (!SkXfermode::IsMode(xmode, SkXfermode::kModulate_Mode)) {
             SkDebugf("Unsupported vertex-color/texture xfer mode.\n");
 #if 0
             return
@@ -1670,7 +1642,7 @@
         // need to convert byte order and from non-PM to PM
         convertedColors.reset(vertexCount);
         for (int i = 0; i < vertexCount; ++i) {
-            convertedColors[i] = SkGr::SkColor2GrColor(colors[i]);
+            convertedColors[i] = SkColor2GrColor(colors[i]);
         }
         colors = convertedColors.get();
     }
@@ -1687,7 +1659,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static void GlyphCacheAuxProc(void* data) {
-    delete (GrFontScaler*)data;
+    GrFontScaler* scaler = (GrFontScaler*)data;
+    SkSafeUnref(scaler);
 }
 
 static GrFontScaler* get_gr_font_scaler(SkGlyphCache* cache) {
@@ -1697,7 +1670,7 @@
         scaler = (GrFontScaler*)auxData;
     }
     if (NULL == scaler) {
-        scaler = new SkGrFontScaler(cache);
+        scaler = SkNEW_ARGS(SkGrFontScaler, (cache));
         cache->setAuxProc(GlyphCacheAuxProc, scaler);
     }
     return scaler;
@@ -1726,7 +1699,7 @@
 
     // deferred allocation
     if (NULL == fDrawProcs) {
-        fDrawProcs = new GrSkDrawProcs;
+        fDrawProcs = SkNEW(GrSkDrawProcs);
         fDrawProcs->fD1GProc = SkGPU_Draw1Glyph;
         fDrawProcs->fContext = fContext;
     }
@@ -1740,25 +1713,19 @@
 void SkGpuDevice::drawText(const SkDraw& draw, const void* text,
                           size_t byteLength, SkScalar x, SkScalar y,
                           const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+    CHECK_SHOULD_DRAW(draw, false);
 
-    if (draw.fMatrix->hasPerspective()) {
+    if (fContext->getMatrix().hasPerspective()) {
         // this guy will just call our drawPath()
         draw.drawText((const char*)text, byteLength, x, y, paint);
     } else {
         SkDraw myDraw(draw);
 
         GrPaint grPaint;
-        SkAutoCachedTexture act;
-
-        if (!this->skPaint2GrPaintShader(paint,
-                                         &act,
-                                         *draw.fMatrix,
-                                         &grPaint,
-                                         true)) {
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
             return;
         }
-        GrTextContext context(fContext, grPaint, draw.fExtMatrix);
+        GrTextContext context(fContext, grPaint);
         myDraw.fProcs = this->initDrawForText(&context);
         this->INHERITED::drawText(myDraw, text, byteLength, x, y, paint);
     }
@@ -1768,9 +1735,9 @@
                              size_t byteLength, const SkScalar pos[],
                              SkScalar constY, int scalarsPerPos,
                              const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+    CHECK_SHOULD_DRAW(draw, false);
 
-    if (draw.fMatrix->hasPerspective()) {
+    if (fContext->getMatrix().hasPerspective()) {
         // this guy will just call our drawPath()
         draw.drawPosText((const char*)text, byteLength, pos, constY,
                          scalarsPerPos, paint);
@@ -1778,16 +1745,10 @@
         SkDraw myDraw(draw);
 
         GrPaint grPaint;
-        SkAutoCachedTexture act;
-        if (!this->skPaint2GrPaintShader(paint,
-                                         &act,
-                                         *draw.fMatrix,
-                                         &grPaint,
-                                         true)) {
+        if (!skPaint2GrPaintShader(this, paint, true, &grPaint)) {
             return;
         }
-
-        GrTextContext context(fContext, grPaint, draw.fExtMatrix);
+        GrTextContext context(fContext, grPaint);
         myDraw.fProcs = this->initDrawForText(&context);
         this->INHERITED::drawPosText(myDraw, text, byteLength, pos, constY,
                                      scalarsPerPos, paint);
@@ -1797,7 +1758,7 @@
 void SkGpuDevice::drawTextOnPath(const SkDraw& draw, const void* text,
                                 size_t len, const SkPath& path,
                                 const SkMatrix* m, const SkPaint& paint) {
-    CHECK_SHOULD_DRAW(draw);
+    CHECK_SHOULD_DRAW(draw, false);
 
     SkASSERT(draw.fDevice == this);
     draw.drawTextOnPath((const char*)text, len, path, m, paint);
@@ -1829,78 +1790,53 @@
 }
 
 void SkGpuDevice::flush() {
+    DO_DEFERRED_CLEAR();
     fContext->resolveRenderTarget(fRenderTarget);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkGpuDevice::TexCache SkGpuDevice::lockCachedTexture(const SkBitmap& bitmap,
-                                            const GrSamplerState* sampler,
-                                            TexType type) {
-    GrContext::TextureCacheEntry entry;
-    GrContext* ctx = this->context();
-
-    if (kBitmap_TexType != type) {
-        const GrTextureDesc desc = {
-            kRenderTarget_GrTextureFlagBit,
-            bitmap.width(),
-            bitmap.height(),
-            SkGr::Bitmap2PixelConfig(bitmap),
-            {0} // samples
-        };
-        GrContext::ScratchTexMatch match;
-        if (kSaveLayerDeviceRenderTarget_TexType == type) {
-            // we know layers will only be drawn through drawDevice.
-            // drawDevice has been made to work with content embedded in a
-            // larger texture so its okay to use the approximate version.
-            match = GrContext::kApprox_ScratchTexMatch;
-        } else {
-            SkASSERT(kDeviceRenderTarget_TexType == type);
-            match = GrContext::kExact_ScratchTexMatch;
-        }
-        entry = ctx->lockScratchTexture(desc, match);
-    } else {
-        if (!bitmap.isVolatile()) {
-            GrContext::TextureKey key = bitmap.getGenerationID();
-            key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
-
-            entry = ctx->findAndLockTexture(key, bitmap.width(),
-                                            bitmap.height(), sampler);
-            if (NULL == entry.texture()) {
-                entry = sk_gr_create_bitmap_texture(ctx, key, sampler,
-                                                    bitmap);
-            }
-        } else {
-            entry = sk_gr_create_bitmap_texture(ctx, gUNCACHED_KEY,
-                                                sampler, bitmap);
-        }
-        if (NULL == entry.texture()) {
-            GrPrintf("---- failed to create texture for cache [%d %d]\n",
-                     bitmap.width(), bitmap.height());
-        }
-    }
-    return entry;
-}
-
-void SkGpuDevice::unlockCachedTexture(TexCache cache) {
-    this->context()->unlockTexture(cache);
-}
-
-bool SkGpuDevice::isBitmapInTextureCache(const SkBitmap& bitmap,
-                                         const GrSamplerState& sampler) const {
-    GrContext::TextureKey key = bitmap.getGenerationID();
-    key |= ((uint64_t) bitmap.pixelRefOffset()) << 32;
-    return this->context()->isTextureInCache(key, bitmap.width(),
-                                             bitmap.height(), &sampler);
-
-}
-
-
 SkDevice* SkGpuDevice::onCreateCompatibleDevice(SkBitmap::Config config,
                                                 int width, int height,
                                                 bool isOpaque,
                                                 Usage usage) {
-    return SkNEW_ARGS(SkGpuDevice,(this->context(), config,
-                                   width, height, usage));
+    GrTextureDesc desc;
+    desc.fConfig = fRenderTarget->config();
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = width;
+    desc.fHeight = height;
+    desc.fSampleCnt = fRenderTarget->numSamples();
+
+    SkAutoTUnref<GrTexture> texture;
+    // Skia's convention is to only clear a device if it is non-opaque.
+    bool needClear = !isOpaque;
+
+#if CACHE_COMPATIBLE_DEVICE_TEXTURES
+    // layers are never draw in repeat modes, so we can request an approx
+    // match and ignore any padding.
+    const GrContext::ScratchTexMatch match = (kSaveLayer_Usage == usage) ?
+                                                GrContext::kApprox_ScratchTexMatch :
+                                                GrContext::kExact_ScratchTexMatch;
+    texture.reset(fContext->lockAndRefScratchTexture(desc, match));
+#else
+    texture.reset(fContext->createUncachedTexture(desc, NULL, 0));
+#endif
+    if (NULL != texture.get()) {
+        return SkNEW_ARGS(SkGpuDevice,(fContext, texture, needClear));
+    } else {
+        GrPrintf("---- failed to create compatible device texture [%d %d]\n", width, height);
+        return NULL;
+    }
 }
 
+SkGpuDevice::SkGpuDevice(GrContext* context,
+                         GrTexture* texture,
+                         bool needClear)
+    : SkDevice(make_bitmap(context, texture->asRenderTarget())) {
+
+    GrAssert(texture && texture->asRenderTarget());
+    // This constructor is called from onCreateCompatibleDevice. It has locked the RT in the texture
+    // cache. We pass true for the third argument so that it will get unlocked.
+    this->initFromRenderTarget(context, texture->asRenderTarget(), true);
+    fNeedClear = needClear;
+}
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp
index 519475c..2eb3800 100644
--- a/src/gpu/SkGr.cpp
+++ b/src/gpu/SkGr.cpp
@@ -56,33 +56,55 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-GrContext::TextureCacheEntry sk_gr_create_bitmap_texture(GrContext* ctx,
-                                                GrContext::TextureKey key,
-                                                const GrSamplerState* sampler,
-                                                const SkBitmap& origBitmap) {
+static void generate_bitmap_cache_id(const SkBitmap& bitmap, GrCacheID* id) {
+    // Our id includes the offset, width, and height so that bitmaps created by extractSubset()
+    // are unique.
+    uint32_t genID = bitmap.getGenerationID();
+    size_t offset = bitmap.pixelRefOffset();
+    int16_t width = static_cast<int16_t>(bitmap.width());
+    int16_t height = static_cast<int16_t>(bitmap.height());
+
+    GrCacheID::Key key;
+    memcpy(key.fData8, &genID, 4);
+    memcpy(key.fData8 + 4, &width, 2);
+    memcpy(key.fData8 + 6, &height, 2);
+    memcpy(key.fData8 + 8, &offset, sizeof(size_t));
+    static const size_t kKeyDataSize = 8 + sizeof(size_t);
+    memset(key.fData8 + kKeyDataSize, 0, sizeof(key) - kKeyDataSize);
+    GR_STATIC_ASSERT(sizeof(key) >= 8 + sizeof(size_t));
+    static const GrCacheID::Domain gBitmapTextureDomain = GrCacheID::GenerateDomain();
+    id->reset(gBitmapTextureDomain, key);
+}
+
+static void generate_bitmap_texture_desc(const SkBitmap& bitmap, GrTextureDesc* desc) {
+    desc->fFlags = kNone_GrTextureFlags;
+    desc->fWidth = bitmap.width();
+    desc->fHeight = bitmap.height();
+    desc->fConfig = SkBitmapConfig2GrPixelConfig(bitmap.config());
+    desc->fSampleCnt = 0;
+}
+
+static GrTexture* sk_gr_create_bitmap_texture(GrContext* ctx,
+                                              bool cache,
+                                              const GrTextureParams* params,
+                                              const SkBitmap& origBitmap) {
     SkAutoLockPixels alp(origBitmap);
-    GrContext::TextureCacheEntry entry;
 
     if (!origBitmap.readyToDraw()) {
-        return entry;
+        return NULL;
     }
 
     SkBitmap tmpBitmap;
 
     const SkBitmap* bitmap = &origBitmap;
 
-    GrTextureDesc desc = {
-        kNone_GrTextureFlags,
-        bitmap->width(),
-        bitmap->height(),
-        SkGr::Bitmap2PixelConfig(*bitmap),
-        {0} // samples
-    };
+    GrTextureDesc desc;
+    generate_bitmap_texture_desc(*bitmap, &desc);
 
     if (SkBitmap::kIndex8_Config == bitmap->config()) {
         // build_compressed_data doesn't do npot->pot expansion
         // and paletted textures can't be sub-updated
-        if (ctx->supportsIndex8PixelConfig(sampler,
+        if (ctx->supportsIndex8PixelConfig(params,
                                            bitmap->width(), bitmap->height())) {
             size_t imagesize = bitmap->width() * bitmap->height() +
                                 kGrColorTableSize;
@@ -92,115 +114,97 @@
 
             // our compressed data will be trimmed, so pass width() for its
             // "rowBytes", since they are the same now.
-            
-            if (gUNCACHED_KEY != key) {
-                return ctx->createAndLockTexture(key, sampler, desc, storage.get(),
-                                                 bitmap->width());
-            } else {
-                entry = ctx->lockScratchTexture(desc,
-                                        GrContext::kExact_ScratchTexMatch);
-                entry.texture()->writePixels(0, 0, bitmap->width(), 
-                                             bitmap->height(), desc.fConfig,
-                                             storage.get(), 0);
-                return entry;
-            }
 
+            if (cache) {
+                GrCacheID cacheID;
+                generate_bitmap_cache_id(origBitmap, &cacheID);
+                return ctx->createTexture(params, desc, cacheID, storage.get(), bitmap->width());
+            } else {
+                GrTexture* result = ctx->lockAndRefScratchTexture(desc,
+                                                            GrContext::kExact_ScratchTexMatch);
+                result->writePixels(0, 0, bitmap->width(),
+                                    bitmap->height(), desc.fConfig,
+                                    storage.get());
+                return result;
+            }
         } else {
             origBitmap.copyTo(&tmpBitmap, SkBitmap::kARGB_8888_Config);
             // now bitmap points to our temp, which has been promoted to 32bits
             bitmap = &tmpBitmap;
+            desc.fConfig = SkBitmapConfig2GrPixelConfig(bitmap->config());
         }
     }
 
-    desc.fConfig = SkGr::Bitmap2PixelConfig(*bitmap);
-    if (gUNCACHED_KEY != key) {
-        return ctx->createAndLockTexture(key, sampler, desc,
-                                         bitmap->getPixels(),
-                                         bitmap->rowBytes());
+    if (cache) {
+        // This texture is likely to be used again so leave it in the cache
+        GrCacheID cacheID;
+        generate_bitmap_cache_id(origBitmap, &cacheID);
+        return ctx->createTexture(params, desc, cacheID, bitmap->getPixels(), bitmap->rowBytes());
     } else {
-        entry = ctx->lockScratchTexture(desc,
-                                        GrContext::kExact_ScratchTexMatch);
-        entry.texture()->writePixels(0, 0,
-                                     bitmap->width(), bitmap->height(),
-                                     desc.fConfig,
-                                     bitmap->getPixels(),
-                                     bitmap->rowBytes());
-        return entry;
+        // This texture is unlikely to be used again (in its present form) so
+        // just use a scratch texture. This will remove the texture from the
+        // cache so no one else can find it. Additionally, once unlocked, the
+        // scratch texture will go to the end of the list for purging so will
+        // likely be available for this volatile bitmap the next time around.
+        GrTexture* result = ctx->lockAndRefScratchTexture(desc, GrContext::kExact_ScratchTexMatch);
+        result->writePixels(0, 0,
+                            bitmap->width(), bitmap->height(),
+                            desc.fConfig,
+                            bitmap->getPixels(),
+                            bitmap->rowBytes());
+        return result;
     }
 }
 
+bool GrIsBitmapInCache(const GrContext* ctx,
+                       const SkBitmap& bitmap,
+                       const GrTextureParams* params) {
+    GrCacheID cacheID;
+    generate_bitmap_cache_id(bitmap, &cacheID);
+
+    GrTextureDesc desc;
+    generate_bitmap_texture_desc(bitmap, &desc);
+    return ctx->isTextureInCache(desc, cacheID, params);
+}
+
+GrTexture* GrLockAndRefCachedBitmapTexture(GrContext* ctx,
+                                           const SkBitmap& bitmap,
+                                           const GrTextureParams* params) {
+    GrTexture* result = NULL;
+
+    bool cache = !bitmap.isVolatile();
+
+    if (cache) {
+        // If the bitmap isn't changing try to find a cached copy first.
+
+        GrCacheID cacheID;
+        generate_bitmap_cache_id(bitmap, &cacheID);
+
+        GrTextureDesc desc;
+        generate_bitmap_texture_desc(bitmap, &desc);
+
+        result = ctx->findAndRefTexture(desc, cacheID, params);
+    }
+    if (NULL == result) {
+        result = sk_gr_create_bitmap_texture(ctx, cache, params, bitmap);
+    }
+    if (NULL == result) {
+        GrPrintf("---- failed to create texture for cache [%d %d]\n",
+                    bitmap.width(), bitmap.height());
+    }
+    return result;
+}
+
+void GrUnlockAndUnrefCachedBitmapTexture(GrTexture* texture) {
+    GrAssert(NULL != texture->getContext());
+
+    texture->getContext()->unlockScratchTexture(texture);
+    texture->unref();
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkGrClipIterator::reset(const SkClipStack& clipStack) {
-    fClipStack = &clipStack;
-    fIter.reset(clipStack);
-    // Gr has no notion of replace, skip to the
-    // last replace in the clip stack.
-    int lastReplace = 0;
-    int curr = 0;
-    while (NULL != (fCurr = fIter.next())) {
-        if (SkRegion::kReplace_Op == fCurr->fOp) {
-            lastReplace = curr;
-        }
-        ++curr;
-    }
-    fIter.reset(clipStack);
-    for (int i = 0; i < lastReplace+1; ++i) {
-        fCurr = fIter.next();
-    }
-}
-
-GrClipType SkGrClipIterator::getType() const {
-    GrAssert(!this->isDone());
-    if (NULL == fCurr->fPath) {
-        return kRect_ClipType;
-    } else {
-        return kPath_ClipType;
-    }
-}
-
-GrSetOp SkGrClipIterator::getOp() const {
-    // we skipped to the last "replace" op
-    // when this iter was reset.
-    // GrClip doesn't allow replace, so treat it as
-    // intersect.
-    GrSetOp skToGrOps[] = {
-        kDifference_SetOp,         // kDifference_Op
-        kIntersect_SetOp,          // kIntersect_Op
-        kUnion_SetOp,              // kUnion_Op
-        kXor_SetOp,                // kXOR_Op
-        kReverseDifference_SetOp,  // kReverseDifference_Op
-        kIntersect_SetOp           // kReplace_op
-    };
-    GR_STATIC_ASSERT(0 == SkRegion::kDifference_Op);
-    GR_STATIC_ASSERT(1 == SkRegion::kIntersect_Op);
-    GR_STATIC_ASSERT(2 == SkRegion::kUnion_Op);
-    GR_STATIC_ASSERT(3 == SkRegion::kXOR_Op);
-    GR_STATIC_ASSERT(4 == SkRegion::kReverseDifference_Op);
-    GR_STATIC_ASSERT(5 == SkRegion::kReplace_Op);
-    return skToGrOps[fCurr->fOp];
-}
-
-GrPathFill SkGrClipIterator::getPathFill() const {
-    switch (fCurr->fPath->getFillType()) {
-        case SkPath::kWinding_FillType:
-            return kWinding_PathFill;
-        case SkPath::kEvenOdd_FillType:
-            return  kEvenOdd_PathFill;
-        case SkPath::kInverseWinding_FillType:
-            return kInverseWinding_PathFill;
-        case SkPath::kInverseEvenOdd_FillType:
-            return kInverseEvenOdd_PathFill;
-        default:
-            GrCrash("Unsupported path fill in clip.");
-            return kWinding_PathFill; // suppress warning
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-GrPixelConfig SkGr::BitmapConfig2PixelConfig(SkBitmap::Config config,
-                                                    bool isOpaque) {
+GrPixelConfig SkBitmapConfig2GrPixelConfig(SkBitmap::Config config) {
     switch (config) {
         case SkBitmap::kA8_Config:
             return kAlpha_8_GrPixelConfig;
@@ -213,7 +217,7 @@
         case SkBitmap::kARGB_8888_Config:
             return kSkia8888_PM_GrPixelConfig;
         default:
+            // kNo_Config, kA1_Config missing, and kRLE_Index8_Config
             return kUnknown_GrPixelConfig;
     }
 }
-
diff --git a/src/gpu/SkGrFontScaler.cpp b/src/gpu/SkGrFontScaler.cpp
index 95c29ff..acecbd9 100644
--- a/src/gpu/SkGrFontScaler.cpp
+++ b/src/gpu/SkGrFontScaler.cpp
@@ -7,7 +7,7 @@
  */
 
 
-
+#include "GrTemplates.h"
 #include "SkGr.h"
 #include "SkDescriptor.h"
 #include "SkGlyphCache.h"
@@ -95,7 +95,7 @@
 
 const GrKey* SkGrFontScaler::getKey() {
     if (NULL == fKey) {
-        fKey = new SkGrDescKey(fStrike->getDescriptor());
+        fKey = SkNEW_ARGS(SkGrDescKey, (fStrike->getDescriptor()));
     }
     return fKey;
 }
@@ -110,17 +110,31 @@
 
 }
 
-static void bits_to_bytes(const uint8_t bits[], uint8_t bytes[], int count) {
-    while (count > 0) {
-        unsigned mask = *bits++;
-        for (int i = 7; i >= 0; --i) {
-            *bytes++ = (mask & (1 << i)) ? 0xFF : 0;
-            if (--count == 0) {
-                return;
+namespace {
+// expands each bit in a bitmask to 0 or ~0 of type INT_TYPE. Used to expand a BW glyph mask to
+// A8, RGB565, or RGBA8888.
+template <typename INT_TYPE>
+void expand_bits(INT_TYPE* dst,
+                 const uint8_t* src,
+                 int width,
+                 int height,
+                 int dstRowBytes,
+                 int srcRowBytes) {
+    for (int i = 0; i < height; ++i) {
+        int rowWritesLeft = width;
+        const uint8_t* s = src;
+        INT_TYPE* d = dst;
+        while (rowWritesLeft > 0) {
+            unsigned mask = *s++;
+            for (int i = 7; i >= 0 && rowWritesLeft; --i, --rowWritesLeft) {
+                *d++ = (mask & (1 << i)) ? (INT_TYPE)(~0UL) : 0;
             }
         }
+        dst = reinterpret_cast<INT_TYPE*>(reinterpret_cast<intptr_t>(dst) + dstRowBytes);
+        src += srcRowBytes;
     }
 }
+}
 
 bool SkGrFontScaler::getPackedGlyphImage(GrGlyph::PackedID packed,
                                          int width, int height,
@@ -136,14 +150,30 @@
     }
 
     int srcRB = glyph.rowBytes();
-    if (SkMask::kBW_Format == fStrike->getMaskFormat()) {
-        // expand bits to bytes
+    // The windows font host sometimes has BW glyphs in a non-BW strike. So it is important here to
+    // check the glyph's format, not the strike's format, and to be able to convert to any of the
+    // GrMaskFormats.
+    if (SkMask::kBW_Format == glyph.fMaskFormat) {
+        // expand bits to our mask type
         const uint8_t* bits = reinterpret_cast<const uint8_t*>(src);
-        uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
-        for (int y = 0; y < height; y++) {
-            bits_to_bytes(bits, bytes, width);
-            bits += srcRB;
-            bytes += dstRB;
+        switch (this->getMaskFormat()) {
+            case kA8_GrMaskFormat:{
+                uint8_t* bytes = reinterpret_cast<uint8_t*>(dst);
+                expand_bits(bytes, bits, width, height, dstRB, srcRB);
+                break;
+            }
+            case kA565_GrMaskFormat: {
+                uint16_t* rgb565 = reinterpret_cast<uint16_t*>(dst);
+                expand_bits(rgb565, bits, width, height, dstRB, srcRB);
+                break;
+            }
+            case kA888_GrMaskFormat: {
+                uint32_t* rgba8888 = reinterpret_cast<uint32_t*>(dst);
+                expand_bits(rgba8888, bits, width, height, dstRB, srcRB);
+                break;
+            }
+           default:
+             GrCrash("Unknown GrMaskFormat");
         }
     } else if (srcRB == dstRB) {
         memcpy(dst, src, dstRB * height);
@@ -159,7 +189,7 @@
 }
 
 // we should just return const SkPath* (NULL means false)
-bool SkGrFontScaler::getGlyphPath(uint16_t glyphID, GrPath* path) {
+bool SkGrFontScaler::getGlyphPath(uint16_t glyphID, SkPath* path) {
 
     const SkGlyph& glyph = fStrike->getGlyphIDMetrics(glyphID);
     const SkPath* skPath = fStrike->findPath(glyph);
@@ -169,6 +199,3 @@
     }
     return false;
 }
-
-
-
diff --git a/src/gpu/SkGrPixelRef.cpp b/src/gpu/SkGrPixelRef.cpp
new file mode 100644
index 0000000..f8160ab
--- /dev/null
+++ b/src/gpu/SkGrPixelRef.cpp
@@ -0,0 +1,179 @@
+
+/*
+ * 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 "SkGrPixelRef.h"
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "SkGr.h"
+#include "SkRect.h"
+
+// since we call lockPixels recursively on fBitmap, we need a distinct mutex,
+// to avoid deadlock with the default one provided by SkPixelRef.
+SK_DECLARE_STATIC_MUTEX(gROLockPixelsPixelRefMutex);
+
+SkROLockPixelsPixelRef::SkROLockPixelsPixelRef() : INHERITED(&gROLockPixelsPixelRefMutex) {
+}
+
+SkROLockPixelsPixelRef::~SkROLockPixelsPixelRef() {
+}
+
+void* SkROLockPixelsPixelRef::onLockPixels(SkColorTable** ctable) {
+    if (ctable) {
+        *ctable = NULL;
+    }
+    fBitmap.reset();
+//    SkDebugf("---------- calling readpixels in support of lockpixels\n");
+    if (!this->onReadPixels(&fBitmap, NULL)) {
+        SkDebugf("SkROLockPixelsPixelRef::onLockPixels failed!\n");
+        return NULL;
+    }
+    fBitmap.lockPixels();
+    return fBitmap.getPixels();
+}
+
+void SkROLockPixelsPixelRef::onUnlockPixels() {
+    fBitmap.unlockPixels();
+}
+
+bool SkROLockPixelsPixelRef::onLockPixelsAreWritable() const {
+    return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkGrPixelRef* copyToTexturePixelRef(GrTexture* texture, SkBitmap::Config dstConfig,
+                                           const SkIRect* subset) {
+    if (NULL == texture) {
+        return NULL;
+    }
+    GrContext* context = texture->getContext();
+    if (NULL == context) {
+        return NULL;
+    }
+    GrTextureDesc desc;
+
+    SkIPoint pointStorage;
+    SkIPoint* topLeft;
+    if (subset != NULL) {
+        SkASSERT(SkIRect::MakeWH(texture->width(), texture->height()).contains(*subset));
+        // Create a new texture that is the size of subset.
+        desc.fWidth = subset->width();
+        desc.fHeight = subset->height();
+        pointStorage.set(subset->x(), subset->y());
+        topLeft = &pointStorage;
+    } else {
+        desc.fWidth  = texture->width();
+        desc.fHeight = texture->height();
+        topLeft = NULL;
+    }
+    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(dstConfig);
+
+    GrTexture* dst = context->createUncachedTexture(desc, NULL, 0);
+    if (NULL == dst) {
+        return NULL;
+    }
+
+    context->copyTexture(texture, dst->asRenderTarget(), topLeft);
+
+    // TODO: figure out if this is responsible for Chrome canvas errors
+#if 0
+    // The render texture we have created (to perform the copy) isn't fully
+    // functional (since it doesn't have a stencil buffer). Release it here
+    // so the caller doesn't try to render to it.
+    // TODO: we can undo this release when dynamic stencil buffer attach/
+    // detach has been implemented
+    dst->releaseRenderTarget();
+#endif
+
+    SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (dst));
+    GrSafeUnref(dst);
+    return pixelRef;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGrPixelRef::SkGrPixelRef(GrSurface* surface, bool transferCacheLock) {
+    // TODO: figure out if this is responsible for Chrome canvas errors
+#if 0
+    // The GrTexture has a ref to the GrRenderTarget but not vice versa.
+    // If the GrTexture exists take a ref to that (rather than the render
+    // target)
+    fSurface = surface->asTexture();
+#else
+    fSurface = NULL;
+#endif
+    if (NULL == fSurface) {
+        fSurface = surface;
+    }
+    fUnlock = transferCacheLock;
+    GrSafeRef(surface);
+}
+
+SkGrPixelRef::~SkGrPixelRef() {
+    if (fUnlock) {
+        GrContext* context = fSurface->getContext();
+        GrTexture* texture = fSurface->asTexture();
+        if (NULL != context && NULL != texture) {
+            context->unlockScratchTexture(texture);
+        }
+    }
+    GrSafeUnref(fSurface);
+}
+
+SkGpuTexture* SkGrPixelRef::getTexture() {
+    if (NULL != fSurface) {
+        return (SkGpuTexture*) fSurface->asTexture();
+    }
+    return NULL;
+}
+
+SkPixelRef* SkGrPixelRef::deepCopy(SkBitmap::Config dstConfig, const SkIRect* subset) {
+    if (NULL == fSurface) {
+        return NULL;
+    }
+
+    // Note that when copying a render-target-backed pixel ref, we
+    // return a texture-backed pixel ref instead.  This is because
+    // render-target pixel refs are usually created in conjunction with
+    // a GrTexture owned elsewhere (e.g., SkGpuDevice), and cannot live
+    // independently of that texture.  Texture-backed pixel refs, on the other
+    // hand, own their GrTextures, and are thus self-contained.
+    return copyToTexturePixelRef(fSurface->asTexture(), dstConfig, subset);
+}
+
+bool SkGrPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
+    if (NULL == fSurface || !fSurface->isValid()) {
+        return false;
+    }
+
+    int left, top, width, height;
+    if (NULL != subset) {
+        left = subset->fLeft;
+        width = subset->width();
+        top = subset->fTop;
+        height = subset->height();
+    } else {
+        left = 0;
+        width = fSurface->width();
+        top = 0;
+        height = fSurface->height();
+    }
+    dst->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    if (!dst->allocPixels()) {
+        SkDebugf("SkGrPixelRef::onReadPixels failed to alloc bitmap for result!\n");
+        return false;
+    }
+    SkAutoLockPixels al(*dst);
+    void* buffer = dst->getPixels();
+    return fSurface->readPixels(left, top, width, height,
+                                kSkia8888_PM_GrPixelConfig,
+                                buffer, dst->rowBytes());
+}
diff --git a/src/gpu/SkGrTexturePixelRef.cpp b/src/gpu/SkGrTexturePixelRef.cpp
index c81e9c0..ae2bbdd 100644
--- a/src/gpu/SkGrTexturePixelRef.cpp
+++ b/src/gpu/SkGrTexturePixelRef.cpp
@@ -9,172 +9,3 @@
 
 
 #include "SkGrTexturePixelRef.h"
-#include "GrContext.h"
-#include "GrTexture.h"
-#include "SkGr.h"
-#include "SkRect.h"
-
-// since we call lockPixels recursively on fBitmap, we need a distinct mutex,
-// to avoid deadlock with the default one provided by SkPixelRef.
-SK_DECLARE_STATIC_MUTEX(gROLockPixelsPixelRefMutex);
-
-SkROLockPixelsPixelRef::SkROLockPixelsPixelRef() : INHERITED(&gROLockPixelsPixelRefMutex) {
-}
-
-SkROLockPixelsPixelRef::~SkROLockPixelsPixelRef() {
-}
-
-void* SkROLockPixelsPixelRef::onLockPixels(SkColorTable** ctable) {
-    if (ctable) {
-        *ctable = NULL;
-    }
-    fBitmap.reset();
-//    SkDebugf("---------- calling readpixels in support of lockpixels\n");
-    if (!this->onReadPixels(&fBitmap, NULL)) {
-        SkDebugf("SkROLockPixelsPixelRef::onLockPixels failed!\n");
-        return NULL;
-    }
-    fBitmap.lockPixels();
-    return fBitmap.getPixels();
-}
-
-void SkROLockPixelsPixelRef::onUnlockPixels() {
-    fBitmap.unlockPixels();
-}
-
-bool SkROLockPixelsPixelRef::onLockPixelsAreWritable() const {
-    return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static SkGrTexturePixelRef* copyToTexturePixelRef(GrTexture* texture,
-                                                  SkBitmap::Config dstConfig) {
-    if (NULL == texture) {
-        return NULL;
-    }
-    GrContext* context = texture->getContext();
-    if (NULL == context) {
-        return NULL;
-    }
-    GrTextureDesc desc;
-
-    desc.fWidth  = texture->width();
-    desc.fHeight = texture->height();
-    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
-    desc.fConfig = SkGr::BitmapConfig2PixelConfig(dstConfig, false);
-    desc.fSampleCnt = 0;
-
-    GrTexture* dst = context->createUncachedTexture(desc, NULL, 0);
-    if (NULL == dst) {
-        return NULL;
-    }
-
-    context->copyTexture(texture, dst->asRenderTarget());
-    SkGrTexturePixelRef* pixelRef = new SkGrTexturePixelRef(dst);
-    GrSafeUnref(dst);
-    return pixelRef;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkGrTexturePixelRef::SkGrTexturePixelRef(GrTexture* tex) {
-    fTexture = tex;
-    GrSafeRef(tex);
-}
-
-SkGrTexturePixelRef::~SkGrTexturePixelRef() {
-    GrSafeUnref(fTexture);
-}
-
-SkGpuTexture* SkGrTexturePixelRef::getTexture() {
-    return (SkGpuTexture*)fTexture;
-}
-
-SkPixelRef* SkGrTexturePixelRef::deepCopy(SkBitmap::Config dstConfig) {
-    return copyToTexturePixelRef(fTexture, dstConfig);
-}
-
-bool SkGrTexturePixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
-    if (NULL != fTexture && fTexture->isValid()) {
-        int left, top, width, height;
-        if (NULL != subset) {
-            left = subset->fLeft;
-            width = subset->width();
-            top = subset->fTop;
-            height = subset->height();
-        } else {
-            left = 0;
-            width = fTexture->width();
-            top = 0;
-            height = fTexture->height();
-        }
-        dst->setConfig(SkBitmap::kARGB_8888_Config, width, height);
-        dst->allocPixels();
-        SkAutoLockPixels al(*dst);
-        void* buffer = dst->getPixels();
-        return fTexture->readPixels(left, top, width, height,
-                                    kSkia8888_PM_GrPixelConfig,
-                                    buffer, dst->rowBytes());
-    } else {
-        return false;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkGrRenderTargetPixelRef::SkGrRenderTargetPixelRef(GrRenderTarget* rt) {
-    fRenderTarget = rt;
-    GrSafeRef(fRenderTarget);
-}
-
-SkGrRenderTargetPixelRef::~SkGrRenderTargetPixelRef() {
-    GrSafeUnref(fRenderTarget);
-}
-
-SkGpuTexture* SkGrRenderTargetPixelRef::getTexture() { 
-    if (NULL != fRenderTarget) {
-        return (SkGpuTexture*) fRenderTarget->asTexture();
-    }
-    return NULL;
-}
-
-SkPixelRef* SkGrRenderTargetPixelRef::deepCopy(SkBitmap::Config dstConfig) {
-    if (NULL == fRenderTarget) {
-        return NULL;
-    }
-    // Note that when copying an SkGrRenderTargetPixelRef, we actually 
-    // return an SkGrTexturePixelRef instead.  This is because
-    // SkGrRenderTargetPixelRef is usually created in conjunction with
-    // GrTexture owned elsewhere (e.g., SkGpuDevice), and cannot live
-    // independently of that texture.  SkGrTexturePixelRef, on the other
-    // hand, owns its own GrTexture, and is thus self-contained.
-    return copyToTexturePixelRef(fRenderTarget->asTexture(), dstConfig);
-}
-
-bool SkGrRenderTargetPixelRef::onReadPixels(SkBitmap* dst, const SkIRect* subset) {
-    if (NULL != fRenderTarget && fRenderTarget->isValid()) {
-        int left, top, width, height;
-        if (NULL != subset) {
-            left = subset->fLeft;
-            width = subset->width();
-            top = subset->fTop;
-            height = subset->height();
-        } else {
-            left = 0;
-            width = fRenderTarget->width();
-            top = 0;
-            height = fRenderTarget->height();
-        }
-        dst->setConfig(SkBitmap::kARGB_8888_Config, width, height);
-        dst->allocPixels();
-        SkAutoLockPixels al(*dst);
-        void* buffer = dst->getPixels();
-        return fRenderTarget->readPixels(left, top, width, height,
-                                         kSkia8888_PM_GrPixelConfig,
-                                         buffer, dst->rowBytes());
-    } else {
-        return false;
-    }
-}
-
diff --git a/src/gpu/android/GrGLCreateNativeInterface_android.cpp b/src/gpu/android/GrGLCreateNativeInterface_android.cpp
deleted file mode 100644
index db629f2..0000000
--- a/src/gpu/android/GrGLCreateNativeInterface_android.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-// Modified from chromium/src/webkit/glue/gl_bindings_skia_cmd_buffer.cc
-
-// Copyright (c) 2011 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 "gl/GrGLInterface.h"
-
-#ifndef GL_GLEXT_PROTOTYPES
-#define GL_GLEXT_PROTOTYPES
-#endif
-
-#include <GLES2/gl2.h>
-#include <GLES2/gl2ext.h>
-
-const GrGLInterface* GrGLCreateNativeInterface() {
-    static SkAutoTUnref<GrGLInterface> glInterface;
-    if (!glInterface.get()) {
-        GrGLInterface* interface = new GrGLInterface;
-        glInterface.reset(interface);
-        interface->fBindingsExported = kES2_GrGLBinding;
-        interface->fActiveTexture = glActiveTexture;
-        interface->fAttachShader = glAttachShader;
-        interface->fBindAttribLocation = glBindAttribLocation;
-        interface->fBindBuffer = glBindBuffer;
-        interface->fBindTexture = glBindTexture;
-        interface->fBlendColor = glBlendColor;
-        interface->fBlendFunc = glBlendFunc;
-        interface->fBufferData = glBufferData;
-        interface->fBufferSubData = glBufferSubData;
-        interface->fClear = glClear;
-        interface->fClearColor = glClearColor;
-        interface->fClearStencil = glClearStencil;
-        interface->fColorMask = glColorMask;
-        interface->fCompileShader = glCompileShader;
-        interface->fCompressedTexImage2D = glCompressedTexImage2D;
-        interface->fCreateProgram = glCreateProgram;
-        interface->fCreateShader = glCreateShader;
-        interface->fCullFace = glCullFace;
-        interface->fDeleteBuffers = glDeleteBuffers;
-        interface->fDeleteProgram = glDeleteProgram;
-        interface->fDeleteShader = glDeleteShader;
-        interface->fDeleteTextures = glDeleteTextures;
-        interface->fDepthMask = glDepthMask;
-        interface->fDisable = glDisable;
-        interface->fDisableVertexAttribArray = glDisableVertexAttribArray;
-        interface->fDrawArrays = glDrawArrays;
-        interface->fDrawElements = glDrawElements;
-        interface->fEnable = glEnable;
-        interface->fEnableVertexAttribArray = glEnableVertexAttribArray;
-        interface->fFinish = glFinish;
-        interface->fFlush = glFlush;
-        interface->fFrontFace = glFrontFace;
-        interface->fGenBuffers = glGenBuffers;
-        interface->fGenTextures = glGenTextures;
-        interface->fGetBufferParameteriv = glGetBufferParameteriv;
-        interface->fGetError = glGetError;
-        interface->fGetIntegerv = glGetIntegerv;
-        interface->fGetProgramInfoLog = glGetProgramInfoLog;
-        interface->fGetProgramiv = glGetProgramiv;
-        interface->fGetShaderInfoLog = glGetShaderInfoLog;
-        interface->fGetShaderiv = glGetShaderiv;
-        interface->fGetString = glGetString;
-        interface->fGetUniformLocation = glGetUniformLocation;
-        interface->fLineWidth = glLineWidth;
-        interface->fLinkProgram = glLinkProgram;
-        interface->fPixelStorei = glPixelStorei;
-        interface->fReadPixels = glReadPixels;
-        interface->fScissor = glScissor;
-        interface->fShaderSource = glShaderSource;
-        interface->fStencilFunc = glStencilFunc;
-        interface->fStencilFuncSeparate = glStencilFuncSeparate;
-        interface->fStencilMask = glStencilMask;
-        interface->fStencilMaskSeparate = glStencilMaskSeparate;
-        interface->fStencilOp = glStencilOp;
-        interface->fStencilOpSeparate = glStencilOpSeparate;
-        interface->fTexImage2D = glTexImage2D;
-        interface->fTexParameteri = glTexParameteri;
-        interface->fTexSubImage2D = glTexSubImage2D;
-#if GL_ARB_texture_storage
-        interface->fTexStorage2D = glTexStorage2D;
-#elif GL_EXT_texture_storage
-        interface->fTexStorage2D = glTexStorage2DEXT;
-#endif
-        interface->fUniform1f = glUniform1f;
-        interface->fUniform1i = glUniform1i;
-        interface->fUniform1fv = glUniform1fv;
-        interface->fUniform1iv = glUniform1iv;
-        interface->fUniform2f = glUniform2f;
-        interface->fUniform2i = glUniform2i;
-        interface->fUniform2fv = glUniform2fv;
-        interface->fUniform2iv = glUniform2iv;
-        interface->fUniform3f = glUniform3f;
-        interface->fUniform3i = glUniform3i;
-        interface->fUniform3fv = glUniform3fv;
-        interface->fUniform3iv = glUniform3iv;
-        interface->fUniform4f = glUniform4f;
-        interface->fUniform4i = glUniform4i;
-        interface->fUniform4fv = glUniform4fv;
-        interface->fUniform4iv = glUniform4iv;
-        interface->fUniformMatrix2fv = glUniformMatrix2fv;
-        interface->fUniformMatrix3fv = glUniformMatrix3fv;
-        interface->fUniformMatrix4fv = glUniformMatrix4fv;
-        interface->fUseProgram = glUseProgram;
-        interface->fVertexAttrib4fv = glVertexAttrib4fv;
-        interface->fVertexAttribPointer = glVertexAttribPointer;
-        interface->fViewport = glViewport;
-        interface->fBindFramebuffer = glBindFramebuffer;
-        interface->fBindRenderbuffer = glBindRenderbuffer;
-        interface->fCheckFramebufferStatus = glCheckFramebufferStatus;
-        interface->fDeleteFramebuffers = glDeleteFramebuffers;
-        interface->fDeleteRenderbuffers = glDeleteRenderbuffers;
-        interface->fFramebufferRenderbuffer = glFramebufferRenderbuffer;
-        interface->fFramebufferTexture2D = glFramebufferTexture2D;
-        interface->fGenFramebuffers = glGenFramebuffers;
-        interface->fGenRenderbuffers = glGenRenderbuffers;
-        interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
-        interface->fGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
-        interface->fRenderbufferStorage = glRenderbufferStorage;
-    #if GL_OES_mapbuffer
-        interface->fMapBuffer = glMapBufferOES;
-        interface->fUnmapBuffer = glUnmapBufferOES;
-    #endif
-    }
-    glInterface.get()->ref();
-    return glInterface.get();
-}
diff --git a/src/gpu/android/SkNativeGLContext_android.cpp b/src/gpu/android/SkNativeGLContext_android.cpp
deleted file mode 100644
index dd444df..0000000
--- a/src/gpu/android/SkNativeGLContext_android.cpp
+++ /dev/null
@@ -1,104 +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 "gl/SkNativeGLContext.h"
-
-SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
-    fOldEGLContext = eglGetCurrentContext();
-    fOldDisplay = eglGetCurrentDisplay();
-    fOldSurface = eglGetCurrentSurface(EGL_DRAW);
-
-}
-
-SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
-    if (NULL != fOldDisplay) {
-        eglMakeCurrent(fOldDisplay, fOldSurface, fOldSurface, fOldEGLContext);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkNativeGLContext::SkNativeGLContext() 
-    : fContext(EGL_NO_CONTEXT)
-    , fDisplay(EGL_NO_DISPLAY)
-    , fSurface(EGL_NO_SURFACE) {
-}
-
-SkNativeGLContext::~SkNativeGLContext() {
-    this->destroyGLContext();
-}
-
-void SkNativeGLContext::destroyGLContext() {
-    if (fDisplay) {
-        eglMakeCurrent(fDisplay, 0, 0, 0);
-
-        if (fContext) {
-            eglDestroyContext(fDisplay, fContext);
-            fContext = EGL_NO_CONTEXT;
-        }
-
-        if (fSurface) {
-            eglDestroySurface(fDisplay, fSurface);
-            fSurface = EGL_NO_SURFACE;
-        }
-
-        //TODO should we close the display?
-        fDisplay = EGL_NO_DISPLAY;
-    }
-}
-
-const GrGLInterface* SkNativeGLContext::createGLContext() {
-    fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-
-    EGLint majorVersion;
-    EGLint minorVersion;
-    eglInitialize(fDisplay, &majorVersion, &minorVersion);
-
-    EGLint numConfigs;
-    static const EGLint configAttribs[] = {
-        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
-        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-        EGL_RED_SIZE, 8,
-        EGL_GREEN_SIZE, 8,
-        EGL_BLUE_SIZE, 8,
-        EGL_ALPHA_SIZE, 8,
-        EGL_NONE
-    };
-
-    EGLConfig surfaceConfig;
-    eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs);
-
-    static const EGLint contextAttribs[] = {
-        EGL_CONTEXT_CLIENT_VERSION, 2,
-        EGL_NONE
-    };
-    fContext = eglCreateContext(fDisplay, surfaceConfig, NULL, contextAttribs);
-
-
-    static const EGLint surfaceAttribs[] = {
-            EGL_WIDTH, 1,
-            EGL_HEIGHT, 1,
-            EGL_NONE
-        };
-    fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs);
-
-    eglMakeCurrent(fDisplay, fSurface, fSurface, fContext);
-
-    const GrGLInterface* interface = GrGLCreateNativeInterface();
-    if (!interface) {
-        SkDebugf("Failed to create gl interface");
-        this->destroyGLContext();
-        return NULL;
-    }
-    return interface;
-}
-
-void SkNativeGLContext::makeCurrent() const {
-    if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
-        SkDebugf("Could not set the context.\n");
-    }
-}
diff --git a/src/gpu/app-android.cpp b/src/gpu/app-android.cpp
deleted file mode 100644
index 2dad1f9..0000000
--- a/src/gpu/app-android.cpp
+++ /dev/null
@@ -1,393 +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 <jni.h>
-#include <sys/time.h>
-#include <time.h>
-#include <android/log.h>
-#include <stdint.h>
-
-#include "GrContext.h"
-#include "SkGpuCanvas.h"
-#include "SkPaint.h"
-#include "SkString.h"
-#include "SkTime.h"
-
-#include "gl/GrGLConfig.h"
-
-static GrContext* make_context() {
-    SkDebugf("---- before create\n");
-    GrContext* ctx = GrContext::Create(GrGpu::kOpenGL_Shaders_Engine, 0);
-    SkDebugf("---- after create %p\n", ctx);
-    return ctx;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void gr_run_unittests() {}
-
-#include "FlingState.h"
-#include "SkTouchGesture.h"
-#include "SkView.h"
-
-typedef SkView* (*SkViewFactory)();
-
-// these values must match those in Ganesh.java
-enum TouchState {
-    kUnknown_TouchState,
-    kDown_TouchState,
-    kMoved_TouchState,
-    kUp_TouchState
-};
-
-struct State {
-    State();
-    ~State();
-
-    int countSlides() const { return fFactory.count(); }
-    const char* getSlideTitle(int index) const;
-    void chooseSlide(int index) {
-        SkDebugf("----- index %d\n", index);
-        if (index < fFactory.count()) {
-            this->setView(fFactory[index]());
-        }
-    }
-
-    void setViewport(int w, int h);
-    int getWidth() const { return fViewport.fX; }
-    int getHeight() const { return fViewport.fY; }
-    
-    void handleTouch(void*, TouchState, float x, float y);
-    void applyMatrix(SkCanvas*);
-
-    SkView* getView() const { return fView; }
-
-private:
-    SkView*     fView;
-    SkIPoint    fViewport;
-    
-    SkTouchGesture fGesture;
-
-    SkTDArray<SkViewFactory> fFactory;
-
-    void setView(SkView* view) {
-        SkSafeUnref(fView);
-        fView = view;
-
-        view->setVisibleP(true);
-        view->setClipToBounds(false);
-        view->setSize(SkIntToScalar(fViewport.fX),
-                      SkIntToScalar(fViewport.fY));
-    }
-};
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SampleCode.h"
-
-SkViewRegister* SkViewRegister::gHead;
-SkViewRegister::SkViewRegister(SkViewFactory fact) : fFact(fact) {
-    static bool gOnce;
-    if (!gOnce) {
-        gHead = NULL;
-        gOnce = true;
-    }
-    
-    fChain = gHead;
-    gHead = this;
-}
-
-static const char gCharEvtName[] = "SampleCode_Char_Event";
-static const char gKeyEvtName[] = "SampleCode_Key_Event";
-static const char gTitleEvtName[] = "SampleCode_Title_Event";
-static const char gPrefSizeEvtName[] = "SampleCode_PrefSize_Event";
-static const char gFastTextEvtName[] = "SampleCode_FastText_Event";
-
-bool SampleCode::CharQ(const SkEvent& evt, SkUnichar* outUni) {
-    if (evt.isType(gCharEvtName, sizeof(gCharEvtName) - 1)) {
-        if (outUni) {
-            *outUni = evt.getFast32();
-        }
-        return true;
-    }
-    return false;
-}
-
-bool SampleCode::KeyQ(const SkEvent& evt, SkKey* outKey) {
-    if (evt.isType(gKeyEvtName, sizeof(gKeyEvtName) - 1)) {
-        if (outKey) {
-            *outKey = (SkKey)evt.getFast32();
-        }
-        return true;
-    }
-    return false;
-}
-
-bool SampleCode::TitleQ(const SkEvent& evt) {
-    return evt.isType(gTitleEvtName, sizeof(gTitleEvtName) - 1);
-}
-
-void SampleCode::TitleR(SkEvent* evt, const char title[]) {
-    GrAssert(evt && TitleQ(*evt));
-    evt->setString(gTitleEvtName, title);
-}
-
-bool SampleCode::PrefSizeQ(const SkEvent& evt) {
-    return evt.isType(gPrefSizeEvtName, sizeof(gPrefSizeEvtName) - 1);
-}
-
-void SampleCode::PrefSizeR(SkEvent* evt, SkScalar width, SkScalar height) {
-    GrAssert(evt && PrefSizeQ(*evt));
-    SkScalar size[2];
-    size[0] = width;
-    size[1] = height;
-    evt->setScalars(gPrefSizeEvtName, 2, size);
-}
-
-bool SampleCode::FastTextQ(const SkEvent& evt) {
-    return evt.isType(gFastTextEvtName, sizeof(gFastTextEvtName) - 1);
-}
-
-static SkMSec gAnimTime;
-static SkMSec gAnimTimePrev;
-
-SkMSec SampleCode::GetAnimTime() { return gAnimTime; }
-SkMSec SampleCode::GetAnimTimeDelta() { return gAnimTime - gAnimTimePrev; }
-SkScalar SampleCode::GetAnimSecondsDelta() {
-    return SkDoubleToScalar(GetAnimTimeDelta() / 1000.0);
-}
-
-SkScalar SampleCode::GetAnimScalar(SkScalar speed, SkScalar period) {
-    // since gAnimTime can be up to 32 bits, we can't convert it to a float
-    // or we'll lose the low bits. Hence we use doubles for the intermediate
-    // calculations
-    double seconds = (double)gAnimTime / 1000.0;
-    double value = SkScalarToDouble(speed) * seconds;
-    if (period) {
-        value = ::fmod(value, SkScalarToDouble(period));
-    }
-    return SkDoubleToScalar(value);
-}
-
-static void drawIntoCanvas(State* state, SkCanvas* canvas) {
-    gAnimTime = SkTime::GetMSecs();
-    SkView* view = state->getView();
-    view->draw(canvas);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void resetGpuState();
-
-State::State() {
-    fViewport.set(0, 0);
-
-    const SkViewRegister* reg = SkViewRegister::Head();
-    while (reg) {
-        *fFactory.append() = reg->factory();
-        reg = reg->next();
-    }
-
-    SkDebugf("----- %d slides\n", fFactory.count());
-    fView = NULL;
-    this->chooseSlide(0);
-}
-
-State::~State() {
-    SkSafeUnref(fView);
-}
-
-void State::setViewport(int w, int h) {
-    fViewport.set(w, h);
-    if (fView) {
-        fView->setSize(SkIntToScalar(w), SkIntToScalar(h));
-    }
-    resetGpuState();
-}
-
-const char* State::getSlideTitle(int index) const {
-    SkEvent evt(gTitleEvtName);
-    evt.setFast32(index);
-    {
-        SkView* view = fFactory[index]();
-        view->doQuery(&evt);
-        view->unref();
-    }
-    return evt.findString(gTitleEvtName);
-}
-
-void State::handleTouch(void* owner, TouchState state, float x, float y) {
-    switch (state) {
-        case kDown_TouchState:
-            fGesture.touchBegin(owner, x, y);
-            break;
-        case kMoved_TouchState:
-            fGesture.touchMoved(owner, x, y);
-            break;
-        case kUp_TouchState:
-            fGesture.touchEnd(owner);
-            break;
-    }
-}
-
-void State::applyMatrix(SkCanvas* canvas) {
-    const SkMatrix& localM = fGesture.localM();
-    if (localM.getType() & SkMatrix::kScale_Mask) {
-        canvas->setExternalMatrix(&localM);
-    }
-    canvas->concat(localM);
-    canvas->concat(fGesture.globalM());
-}
-
-static State* get_state() {
-    static State* gState;
-    if (NULL == gState) {
-        gState = new State;
-    }
-    return gState;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static GrContext* gContext;
-static int gWidth;
-static int gHeight;
-static float gX, gY;
-
-static void resetGpuState() {
-    if (NULL == gContext) {
-        SkDebugf("creating context for first time\n");
-        gContext = make_context();
-    } else {
-        SkDebugf("------ gContext refcnt=%d\n", gContext->refcnt());
-        gContext->abandonAllTextures();
-        gContext->unref();
-        gContext = make_context();
-    }
-}
-
-static void doDraw() {
-    if (NULL == gContext) {
-        gContext = make_context();
-    }
-
-    State* state = get_state();
-    SkBitmap viewport;
-    viewport.setConfig(SkBitmap::kARGB_8888_Config,
-                       state->getWidth(), state->getHeight());
-
-    SkGpuCanvas canvas(gContext);
-
-    canvas.setBitmapDevice(viewport);
-    state->applyMatrix(&canvas);
-
-    drawIntoCanvas(state, &canvas);
-
-    GrGLCheckErr();
-    GrGLClearErr();
-//    gContext->checkError();
-//    gContext->clearError();
-
-    if (true) {
-        static const int FRAME_COUNT = 32;
-        static SkMSec gDuration;
-
-        static SkMSec gNow;
-        static int gFrameCounter;
-        if (++gFrameCounter == FRAME_COUNT) {
-            gFrameCounter = 0;
-            SkMSec now = SkTime::GetMSecs();
-
-            gDuration = now - gNow;
-            gNow = now;
-        }
-
-        int fps = (FRAME_COUNT * 1000) / gDuration;
-        SkString str;
-        str.printf("FPS=%3d MS=%3d", fps, gDuration / FRAME_COUNT);
-        
-        SkGpuCanvas c(gContext);
-        c.setBitmapDevice(viewport);
-
-        SkPaint p;
-        p.setAntiAlias(true);
-        SkRect r = { 0, 0, 110, 16 };
-        p.setColor(SK_ColorWHITE);
-        c.drawRect(r, p);
-        p.setColor(SK_ColorBLACK);
-        c.drawText(str.c_str(), str.size(), 4, 12, p);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-extern "C" {
-    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeSurfaceCreated(
-                                                           JNIEnv*, jobject);
-    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeViewport(JNIEnv*, jobject,
-                                                                             jint w, jint h);
-    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeDrawFrame(JNIEnv*, jobject);
-    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeTouch(JNIEnv*, jobject,
-                                        jint id, jint type, jfloat x, jfloat y);
-
-    JNIEXPORT int JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeCountSlides(JNIEnv*, jobject);
-    JNIEXPORT jobject JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeGetSlideTitle(JNIEnv*, jobject, jint index);
-    JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeChooseSlide(JNIEnv*, jobject, jint index);
-}
-
-JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeSurfaceCreated(
-                                                            JNIEnv*, jobject) {
-    SkDebugf("------ nativeSurfaceCreated\n");
-    resetGpuState();
-    SkDebugf("------ end nativeSurfaceCreated\n");
-}
-
-JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeViewport(JNIEnv*, jobject,
-                                                       jint w, jint h) {
-    State* state = get_state();
-    SkDebugf("---- state.setviewport %p %d %d\n", state, w, h);
-    state->setViewport(w, h);
-    SkDebugf("---- end setviewport\n");
-}
-
-JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeDrawFrame(JNIEnv*, jobject) {
-    doDraw();
-}
-
-union IntPtr {
-    jint    fInt;
-    void*   fPtr;
-};
-static void* int2ptr(jint n) {
-    IntPtr data;
-    data.fInt = n;
-    return data.fPtr;
-}
-
-JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeTouch(JNIEnv*, jobject,
-                                      jint id, jint type, jfloat x, jfloat y) {
-    get_state()->handleTouch(int2ptr(id), (TouchState)type, x, y);
-}
-
-////////////
-
-JNIEXPORT int JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeCountSlides(JNIEnv*, jobject) {
-    return get_state()->countSlides();
-}
-
-JNIEXPORT jobject JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeGetSlideTitle(JNIEnv* env, jobject, jint index) {
-    return env->NewStringUTF(get_state()->getSlideTitle(index));
-}
-
-JNIEXPORT void JNICALL Java_com_tetrark_ganesh_MyRenderer_nativeChooseSlide(JNIEnv*, jobject, jint index) {
-    get_state()->chooseSlide(index);
-}
-
-
-
-
-
diff --git a/src/gpu/effects/Gr1DKernelEffect.h b/src/gpu/effects/Gr1DKernelEffect.h
new file mode 100644
index 0000000..1712733
--- /dev/null
+++ b/src/gpu/effects/Gr1DKernelEffect.h
@@ -0,0 +1,54 @@
+/*
+ * 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 Gr1DKernelEffect_DEFINED
+#define Gr1DKernelEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+#include "SkMatrix.h"
+
+/**
+ * Base class for 1D kernel effects. The kernel operates either in X or Y and
+ * has a pixel radius. The kernel is specified in the src texture's space
+ * and the kernel center is pinned to a texel's center. The radius specifies
+ * the number of texels on either side of the center texel in X or Y that are
+ * read. Since the center pixel is also read, the total width is one larger than
+ * two times the radius.
+ */
+
+class Gr1DKernelEffect : public GrSingleTextureEffect {
+
+public:
+    enum Direction {
+        kX_Direction,
+        kY_Direction,
+    };
+
+    Gr1DKernelEffect(GrTexture* texture,
+                     Direction direction,
+                     int radius)
+        : GrSingleTextureEffect(texture, MakeDivByTextureWHMatrix(texture))
+        , fDirection(direction)
+        , fRadius(radius) {}
+
+    virtual ~Gr1DKernelEffect() {};
+
+    static int WidthFromRadius(int radius) { return 2 * radius + 1; }
+
+    int radius() const { return fRadius; }
+    int width() const { return WidthFromRadius(fRadius); }
+    Direction direction() const { return fDirection; }
+
+private:
+
+    Direction       fDirection;
+    int             fRadius;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrConfigConversionEffect.cpp b/src/gpu/effects/GrConfigConversionEffect.cpp
new file mode 100644
index 0000000..92df82d
--- /dev/null
+++ b/src/gpu/effects/GrConfigConversionEffect.cpp
@@ -0,0 +1,286 @@
+/*
+ * 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 "GrConfigConversionEffect.h"
+#include "GrContext.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrSimpleTextureEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "SkMatrix.h"
+
+class GrGLConfigConversionEffect : public GrGLEffect {
+public:
+    GrGLConfigConversionEffect(const GrBackendEffectFactory& factory,
+                               const GrEffectRef& s) : INHERITED (factory) {
+        const GrConfigConversionEffect& effect = CastEffect<GrConfigConversionEffect>(s);
+        fSwapRedAndBlue = effect.swapsRedAndBlue();
+        fPMConversion = effect.pmConversion();
+    }
+
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrEffectStage&,
+                          EffectKey key,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+        const char* coords;
+        GrSLType coordsType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coords);
+        builder->fFSCode.appendf("\t\t%s = ", outputColor);
+        builder->appendTextureLookup(&builder->fFSCode, samplers[0], coords, coordsType);
+        builder->fFSCode.append(";\n");
+        if (GrConfigConversionEffect::kNone_PMConversion == fPMConversion) {
+            GrAssert(fSwapRedAndBlue);
+            builder->fFSCode.appendf("\t%s = %s.bgra;\n", outputColor, outputColor);
+        } else {
+            const char* swiz = fSwapRedAndBlue ? "bgr" : "rgb";
+            switch (fPMConversion) {
+                case GrConfigConversionEffect::kMulByAlpha_RoundUp_PMConversion:
+                    builder->fFSCode.appendf(
+                        "\t\t%s = vec4(ceil(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
+                        outputColor, outputColor, swiz, outputColor, outputColor);
+                    break;
+                case GrConfigConversionEffect::kMulByAlpha_RoundDown_PMConversion:
+                    builder->fFSCode.appendf(
+                        "\t\t%s = vec4(floor(%s.%s * %s.a * 255.0) / 255.0, %s.a);\n",
+                        outputColor, outputColor, swiz, outputColor, outputColor);
+                    break;
+                case GrConfigConversionEffect::kDivByAlpha_RoundUp_PMConversion:
+                    builder->fFSCode.appendf("\t\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.%s / %s.a * 255.0) / 255.0, %s.a);\n",
+                        outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
+                    break;
+                case GrConfigConversionEffect::kDivByAlpha_RoundDown_PMConversion:
+                    builder->fFSCode.appendf("\t\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.%s / %s.a * 255.0) / 255.0, %s.a);\n",
+                        outputColor, outputColor, outputColor, swiz, outputColor, outputColor);
+                    break;
+                default:
+                    GrCrash("Unknown conversion op.");
+                    break;
+            }
+        }
+        GrGLSLMulVarBy4f(&builder->fFSCode, 2, outputColor, inputColor);
+    }
+
+    void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+        const GrConfigConversionEffect& effect =
+            GetEffectFromStage<GrConfigConversionEffect>(stage);
+        fEffectMatrix.setData(uman,
+                              effect.getMatrix(),
+                              stage.getCoordChangeMatrix(),
+                              effect.texture(0));
+    }
+
+    static inline EffectKey GenKey(const GrEffectStage& s, const GrGLCaps&) {
+        const GrConfigConversionEffect& effect = GetEffectFromStage<GrConfigConversionEffect>(s);
+        EffectKey key = static_cast<EffectKey>(effect.swapsRedAndBlue()) |
+                        (effect.pmConversion() << 1);
+        key <<= GrGLEffectMatrix::kKeyBits;
+        EffectKey matrixKey =  GrGLEffectMatrix::GenKey(effect.getMatrix(),
+                                                        s.getCoordChangeMatrix(),
+                                                        effect.texture(0));
+        GrAssert(!(matrixKey & key));
+        return matrixKey | key;
+    }
+
+private:
+    bool                                    fSwapRedAndBlue;
+    GrConfigConversionEffect::PMConversion  fPMConversion;
+    GrGLEffectMatrix                        fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrConfigConversionEffect::GrConfigConversionEffect(GrTexture* texture,
+                                                   bool swapRedAndBlue,
+                                                   PMConversion pmConversion,
+                                                   const SkMatrix& matrix)
+    : GrSingleTextureEffect(texture, matrix)
+    , fSwapRedAndBlue(swapRedAndBlue)
+    , fPMConversion(pmConversion) {
+    GrAssert(kRGBA_8888_GrPixelConfig == texture->config() ||
+             kBGRA_8888_GrPixelConfig == texture->config());
+    // Why did we pollute our texture cache instead of using a GrSingleTextureEffect?
+    GrAssert(swapRedAndBlue || kNone_PMConversion != pmConversion);
+}
+
+const GrBackendEffectFactory& GrConfigConversionEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrConfigConversionEffect>::getInstance();
+}
+
+bool GrConfigConversionEffect::onIsEqual(const GrEffect& s) const {
+    const GrConfigConversionEffect& other = CastEffect<GrConfigConversionEffect>(s);
+    return this->texture(0) == s.texture(0) &&
+           other.fSwapRedAndBlue == fSwapRedAndBlue &&
+           other.fPMConversion == fPMConversion;
+}
+
+void GrConfigConversionEffect::getConstantColorComponents(GrColor* color,
+                                                          uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrConfigConversionEffect);
+
+GrEffectRef* GrConfigConversionEffect::TestCreate(SkRandom* random,
+                                                  GrContext* context,
+                                                  GrTexture* textures[]) {
+    PMConversion pmConv = static_cast<PMConversion>(random->nextULessThan(kPMConversionCnt));
+    bool swapRB;
+    if (kNone_PMConversion == pmConv) {
+        swapRB = true;
+    } else {
+        swapRB = random->nextBool();
+    }
+    AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect,
+                                      (textures[GrEffectUnitTest::kSkiaPMTextureIdx],
+                                       swapRB,
+                                       pmConv,
+                                       GrEffectUnitTest::TestMatrix(random))));
+    return CreateEffectRef(effect);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void GrConfigConversionEffect::TestForPreservingPMConversions(GrContext* context,
+                                                              PMConversion* pmToUPMRule,
+                                                              PMConversion* upmToPMRule) {
+    *pmToUPMRule = kNone_PMConversion;
+    *upmToPMRule = kNone_PMConversion;
+    SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
+    uint32_t* srcData = data.get();
+    uint32_t* firstRead = data.get() + 256 * 256;
+    uint32_t* secondRead = data.get() + 2 * 256 * 256;
+
+    // Fill with every possible premultiplied A, color channel value. There will be 256-y duplicate
+    // values in row y. We set r,g, and b to the same value since they are handled identically.
+    for (int y = 0; y < 256; ++y) {
+        for (int x = 0; x < 256; ++x) {
+            uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
+            color[3] = y;
+            color[2] = GrMin(x, y);
+            color[1] = GrMin(x, y);
+            color[0] = GrMin(x, y);
+        }
+    }
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit |
+                  kNoStencil_GrTextureFlagBit;
+    desc.fWidth = 256;
+    desc.fHeight = 256;
+    desc.fConfig = kRGBA_8888_GrPixelConfig;
+
+    SkAutoTUnref<GrTexture> readTex(context->createUncachedTexture(desc, NULL, 0));
+    if (!readTex.get()) {
+        return;
+    }
+    SkAutoTUnref<GrTexture> tempTex(context->createUncachedTexture(desc, NULL, 0));
+    if (!tempTex.get()) {
+        return;
+    }
+    desc.fFlags = kNone_GrTextureFlags;
+    SkAutoTUnref<GrTexture> dataTex(context->createUncachedTexture(desc, data, 0));
+    if (!dataTex.get()) {
+        return;
+    }
+
+    static const PMConversion kConversionRules[][2] = {
+        {kDivByAlpha_RoundDown_PMConversion, kMulByAlpha_RoundUp_PMConversion},
+        {kDivByAlpha_RoundUp_PMConversion, kMulByAlpha_RoundDown_PMConversion},
+    };
+
+    GrContext::AutoWideOpenIdentityDraw awoid(context, NULL);
+
+    bool failed = true;
+
+    for (size_t i = 0; i < GR_ARRAY_COUNT(kConversionRules) && failed; ++i) {
+        *pmToUPMRule = kConversionRules[i][0];
+        *upmToPMRule = kConversionRules[i][1];
+
+        static const GrRect kDstRect = GrRect::MakeWH(SkIntToScalar(256), SkIntToScalar(256));
+        static const GrRect kSrcRect = GrRect::MakeWH(SK_Scalar1, SK_Scalar1);
+        // We do a PM->UPM draw from dataTex to readTex and read the data. Then we do a UPM->PM draw
+        // from readTex to tempTex followed by a PM->UPM draw to readTex and finally read the data.
+        // We then verify that two reads produced the same values.
+
+        GrPaint paint;
+        AutoEffectUnref pmToUPM1(SkNEW_ARGS(GrConfigConversionEffect, (dataTex,
+                                                                       false,
+                                                                       *pmToUPMRule,
+                                                                       SkMatrix::I())));
+        AutoEffectUnref upmToPM(SkNEW_ARGS(GrConfigConversionEffect, (readTex,
+                                                                      false,
+                                                                      *upmToPMRule,
+                                                                      SkMatrix::I())));
+        AutoEffectUnref pmToUPM2(SkNEW_ARGS(GrConfigConversionEffect, (tempTex,
+                                                                       false,
+                                                                       *pmToUPMRule,
+                                                                       SkMatrix::I())));
+
+        SkAutoTUnref<GrEffectRef> pmToUPMEffect1(CreateEffectRef(pmToUPM1));
+        SkAutoTUnref<GrEffectRef> upmToPMEffect(CreateEffectRef(upmToPM));
+        SkAutoTUnref<GrEffectRef> pmToUPMEffect2(CreateEffectRef(pmToUPM2));
+
+        context->setRenderTarget(readTex->asRenderTarget());
+        paint.colorStage(0)->setEffect(pmToUPMEffect1);
+        context->drawRectToRect(paint, kDstRect, kSrcRect);
+
+        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, firstRead);
+
+        context->setRenderTarget(tempTex->asRenderTarget());
+        paint.colorStage(0)->setEffect(upmToPMEffect);
+        context->drawRectToRect(paint, kDstRect, kSrcRect);
+        context->setRenderTarget(readTex->asRenderTarget());
+        paint.colorStage(0)->setEffect(pmToUPMEffect2);
+        context->drawRectToRect(paint, kDstRect, kSrcRect);
+
+        readTex->readPixels(0, 0, 256, 256, kRGBA_8888_GrPixelConfig, secondRead);
+
+        failed = false;
+        for (int y = 0; y < 256 && !failed; ++y) {
+            for (int x = 0; x <= y; ++x) {
+                if (firstRead[256 * y + x] != secondRead[256 * y + x]) {
+                    failed = true;
+                    break;
+                }
+            }
+        }
+    }
+    if (failed) {
+        *pmToUPMRule = kNone_PMConversion;
+        *upmToPMRule = kNone_PMConversion;
+    }
+}
+
+const GrEffectRef* GrConfigConversionEffect::Create(GrTexture* texture,
+                                                    bool swapRedAndBlue,
+                                                    PMConversion pmConversion,
+                                                    const SkMatrix& matrix) {
+    if (!swapRedAndBlue && kNone_PMConversion == pmConversion) {
+        // If we returned a GrConfigConversionEffect that was equivalent to a GrSimpleTextureEffect
+        // then we may pollute our texture cache with redundant shaders. So in the case that no
+        // conversions were requested we instead return a GrSimpleTextureEffect.
+        return GrSimpleTextureEffect::Create(texture, matrix);
+    } else {
+        if (kRGBA_8888_GrPixelConfig != texture->config() &&
+            kBGRA_8888_GrPixelConfig != texture->config() &&
+            kNone_PMConversion != pmConversion) {
+            // The PM conversions assume colors are 0..255
+            return NULL;
+        }
+        AutoEffectUnref effect(SkNEW_ARGS(GrConfigConversionEffect, (texture,
+                                                                     swapRedAndBlue,
+                                                                     pmConversion,
+                                                                     matrix)));
+        return CreateEffectRef(effect);
+    }
+}
diff --git a/src/gpu/effects/GrConfigConversionEffect.h b/src/gpu/effects/GrConfigConversionEffect.h
new file mode 100644
index 0000000..169c3d7
--- /dev/null
+++ b/src/gpu/effects/GrConfigConversionEffect.h
@@ -0,0 +1,78 @@
+/*
+ * 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 GrConfigConversionEffect_DEFINED
+#define GrConfigConversionEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+
+class GrEffectStage;
+class GrGLConfigConversionEffect;
+
+/**
+ * This class is used to perform config conversions. Clients may want to read/write data that is
+ * unpremultiplied. Also on some systems reading/writing BGRA or RGBA is faster. In those cases we
+ * read/write using the faster path and perform an R/B swap in the shader if the client data is in
+ * the slower config.
+ */
+class GrConfigConversionEffect : public GrSingleTextureEffect {
+public:
+    /**
+     * The PM->UPM or UPM->PM conversions to apply.
+     */
+    enum PMConversion {
+        kNone_PMConversion = 0,
+        kMulByAlpha_RoundUp_PMConversion,
+        kMulByAlpha_RoundDown_PMConversion,
+        kDivByAlpha_RoundUp_PMConversion,
+        kDivByAlpha_RoundDown_PMConversion,
+
+        kPMConversionCnt
+    };
+
+    // Installs an effect in the GrEffectStage to perform a config conversion.
+    static const GrEffectRef* Create(GrTexture*,
+                                     bool swapRedAndBlue,
+                                     PMConversion pmConversion,
+                                     const SkMatrix& matrix);
+
+    static const char* Name() { return "Config Conversion"; }
+    typedef GrGLConfigConversionEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    bool swapsRedAndBlue() const { return fSwapRedAndBlue; }
+    PMConversion  pmConversion() const { return fPMConversion; }
+
+    // This function determines whether it is possible to choose PM->UPM and UPM->PM conversions
+    // for which in any PM->UPM->PM->UPM sequence the two UPM values are the same. This means that
+    // if pixels are read back to a UPM buffer, written back to PM to the GPU, and read back again
+    // both reads will produce the same result. This test is quite expensive and should not be run
+    // multiple times for a given context.
+    static void TestForPreservingPMConversions(GrContext* context,
+                                               PMConversion* PMToUPMRule,
+                                               PMConversion* UPMToPMRule);
+
+private:
+    GrConfigConversionEffect(GrTexture*,
+                            bool swapRedAndBlue,
+                            PMConversion pmConversion,
+                            const SkMatrix& matrix);
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    bool            fSwapRedAndBlue;
+    PMConversion    fPMConversion;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrConvolutionEffect.cpp b/src/gpu/effects/GrConvolutionEffect.cpp
new file mode 100644
index 0000000..8968857
--- /dev/null
+++ b/src/gpu/effects/GrConvolutionEffect.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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 "GrConvolutionEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrTBackendEffectFactory.h"
+
+// For brevity
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+
+class GrGLConvolutionEffect : public GrGLEffect {
+public:
+    GrGLConvolutionEffect(const GrBackendEffectFactory&, const GrEffectRef&);
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+private:
+    int width() const { return Gr1DKernelEffect::WidthFromRadius(fRadius); }
+
+    int                 fRadius;
+    UniformHandle       fKernelUni;
+    UniformHandle       fImageIncrementUni;
+    GrGLEffectMatrix    fEffectMatrix;
+
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLConvolutionEffect::GrGLConvolutionEffect(const GrBackendEffectFactory& factory,
+                                             const GrEffectRef& effect)
+    : INHERITED(factory)
+    , fKernelUni(kInvalidUniformHandle)
+    , fImageIncrementUni(kInvalidUniformHandle) {
+    const GrConvolutionEffect& c = CastEffect<GrConvolutionEffect>(effect);
+    fRadius = c.radius();
+}
+
+void GrGLConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
+                                     const GrEffectStage&,
+                                     EffectKey key,
+                                     const char* vertexCoords,
+                                     const char* outputColor,
+                                     const char* inputColor,
+                                     const TextureSamplerArray& samplers) {
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+    fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                             kVec2f_GrSLType, "ImageIncrement");
+    fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_ShaderType,
+                                          kFloat_GrSLType, "Kernel", this->width());
+    SkString* code = &builder->fFSCode;
+
+    code->appendf("\t\t%s = vec4(0, 0, 0, 0);\n", outputColor);
+
+    int width = this ->width();
+    const GrGLShaderVar& kernel = builder->getUniformVariable(fKernelUni);
+    const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
+
+    code->appendf("\t\tvec2 coord = %s - %d.0 * %s;\n", coords, fRadius, imgInc);
+
+    // Manually unroll loop because some drivers don't; yields 20-30% speedup.
+    for (int i = 0; i < width; i++) {
+        SkString index;
+        SkString kernelIndex;
+        index.appendS32(i);
+        kernel.appendArrayAccess(index.c_str(), &kernelIndex);
+        code->appendf("\t\t%s += ", outputColor);
+        builder->appendTextureLookup(&builder->fFSCode, samplers[0], "coord");
+        code->appendf(" * %s;\n", kernelIndex.c_str());
+        code->appendf("\t\tcoord += %s;\n", imgInc);
+    }
+    GrGLSLMulVarBy4f(&builder->fFSCode, 2, outputColor, inputColor);
+}
+
+void GrGLConvolutionEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrConvolutionEffect& conv = GetEffectFromStage<GrConvolutionEffect>(stage);
+    GrTexture& texture = *conv.texture(0);
+    // the code we generated was for a specific kernel radius
+    GrAssert(conv.radius() == fRadius);
+    float imageIncrement[2] = { 0 };
+    switch (conv.direction()) {
+        case Gr1DKernelEffect::kX_Direction:
+            imageIncrement[0] = 1.0f / texture.width();
+            break;
+        case Gr1DKernelEffect::kY_Direction:
+            imageIncrement[1] = 1.0f / texture.height();
+            break;
+        default:
+            GrCrash("Unknown filter direction.");
+    }
+    uman.set2fv(fImageIncrementUni, 0, 1, imageIncrement);
+    uman.set1fv(fKernelUni, 0, this->width(), conv.kernel());
+    fEffectMatrix.setData(uman, conv.getMatrix(), stage.getCoordChangeMatrix(), conv.texture(0));
+}
+
+GrGLEffect::EffectKey GrGLConvolutionEffect::GenKey(const GrEffectStage& s, const GrGLCaps&) {
+    const GrConvolutionEffect& conv = GetEffectFromStage<GrConvolutionEffect>(s);
+    EffectKey key = conv.radius();
+    key <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey matrixKey = GrGLEffectMatrix::GenKey(conv.getMatrix(),
+                                                   s.getCoordChangeMatrix(),
+                                                   conv.texture(0));
+    return key | matrixKey;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
+                                         Direction direction,
+                                         int radius,
+                                         const float* kernel)
+    : Gr1DKernelEffect(texture, direction, radius) {
+    GrAssert(radius <= kMaxKernelRadius);
+    GrAssert(NULL != kernel);
+    int width = this->width();
+    for (int i = 0; i < width; i++) {
+        fKernel[i] = kernel[i];
+    }
+}
+
+GrConvolutionEffect::GrConvolutionEffect(GrTexture* texture,
+                                         Direction direction,
+                                         int radius,
+                                         float gaussianSigma)
+    : Gr1DKernelEffect(texture, direction, radius) {
+    GrAssert(radius <= kMaxKernelRadius);
+    int width = this->width();
+
+    float sum = 0.0f;
+    float denom = 1.0f / (2.0f * gaussianSigma * gaussianSigma);
+    for (int i = 0; i < width; ++i) {
+        float x = static_cast<float>(i - this->radius());
+        // Note that the constant term (1/(sqrt(2*pi*sigma^2)) of the Gaussian
+        // is dropped here, since we renormalize the kernel below.
+        fKernel[i] = sk_float_exp(- x * x * denom);
+        sum += fKernel[i];
+    }
+    // Normalize the kernel
+    float scale = 1.0f / sum;
+    for (int i = 0; i < width; ++i) {
+        fKernel[i] *= scale;
+    }
+}
+
+GrConvolutionEffect::~GrConvolutionEffect() {
+}
+
+const GrBackendEffectFactory& GrConvolutionEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrConvolutionEffect>::getInstance();
+}
+
+bool GrConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrConvolutionEffect& s = CastEffect<GrConvolutionEffect>(sBase);
+    return (this->texture(0) == s.texture(0) &&
+            this->radius() == s.radius() &&
+            this->direction() == s.direction() &&
+            0 == memcmp(fKernel, s.fKernel, this->width() * sizeof(float)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrConvolutionEffect);
+
+GrEffectRef* GrConvolutionEffect::TestCreate(SkRandom* random,
+                                             GrContext* context,
+                                             GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    Direction dir = random->nextBool() ? kX_Direction : kY_Direction;
+    int radius = random->nextRangeU(1, kMaxKernelRadius);
+    float kernel[kMaxKernelRadius];
+    for (int i = 0; i < kMaxKernelRadius; ++i) {
+        kernel[i] = random->nextSScalar1();
+    }
+
+    return GrConvolutionEffect::Create(textures[texIdx], dir, radius,kernel);
+}
diff --git a/src/gpu/effects/GrConvolutionEffect.h b/src/gpu/effects/GrConvolutionEffect.h
new file mode 100644
index 0000000..b2b24e5
--- /dev/null
+++ b/src/gpu/effects/GrConvolutionEffect.h
@@ -0,0 +1,92 @@
+/*
+ * 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 GrConvolutionEffect_DEFINED
+#define GrConvolutionEffect_DEFINED
+
+#include "Gr1DKernelEffect.h"
+
+class GrGLConvolutionEffect;
+
+/**
+ * A convolution effect. The kernel is specified as an array of 2 * half-width
+ * + 1 weights. Each texel is multiplied by it's weight and summed to determine
+ * the output color. The output color is modulated by the input color.
+ */
+class GrConvolutionEffect : public Gr1DKernelEffect {
+
+public:
+
+    /// Convolve with an arbitrary user-specified kernel
+    static GrEffectRef* Create(GrTexture* tex, Direction dir, int halfWidth, const float* kernel) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
+                                                                dir,
+                                                                halfWidth,
+                                                                kernel)));
+        return CreateEffectRef(effect);
+    }
+
+    /// Convolve with a Gaussian kernel
+    static GrEffectRef* CreateGaussian(GrTexture* tex,
+                                       Direction dir,
+                                       int halfWidth,
+                                       float gaussianSigma) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrConvolutionEffect, (tex,
+                                                                dir,
+                                                                halfWidth,
+                                                                gaussianSigma)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrConvolutionEffect();
+
+    const float* kernel() const { return fKernel; }
+
+    static const char* Name() { return "Convolution"; }
+
+    typedef GrGLConvolutionEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+        // If the texture was opaque we could know that the output color if we knew the sum of the
+        // kernel values.
+        *validFlags = 0;
+    }
+
+    enum {
+        // This was decided based on the min allowed value for the max texture
+        // samples per fragment program run in DX9SM2 (32). A sigma param of 4.0
+        // on a blur filter gives a kernel width of 25 while a sigma of 5.0
+        // would exceed a 32 wide kernel.
+        kMaxKernelRadius = 12,
+        // With a C++11 we could have a constexpr version of WidthFromRadius()
+        // and not have to duplicate this calculation.
+        kMaxKernelWidth = 2 * kMaxKernelRadius + 1,
+    };
+
+protected:
+
+    float fKernel[kMaxKernelWidth];
+
+private:
+    GrConvolutionEffect(GrTexture*, Direction,
+                        int halfWidth, const float* kernel);
+
+    /// Convolve with a Gaussian kernel
+    GrConvolutionEffect(GrTexture*, Direction,
+                        int halfWidth,
+                        float gaussianSigma);
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef Gr1DKernelEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrSimpleTextureEffect.cpp b/src/gpu/effects/GrSimpleTextureEffect.cpp
new file mode 100644
index 0000000..b674879
--- /dev/null
+++ b/src/gpu/effects/GrSimpleTextureEffect.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrSimpleTextureEffect.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLTexture.h"
+#include "GrTBackendEffectFactory.h"
+#include "GrTexture.h"
+
+class GrGLSimpleTextureEffect : public GrGLEffect {
+public:
+    GrGLSimpleTextureEffect(const GrBackendEffectFactory& factory, const GrEffectRef&)
+    : INHERITED (factory) {}
+
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrEffectStage&,
+                          EffectKey key,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray& samplers) SK_OVERRIDE {
+        const char* coordName;
+        GrSLType coordType = fEffectMatrix.emitCode(builder, key, vertexCoords, &coordName);
+        builder->fFSCode.appendf("\t%s = ", outputColor);
+        builder->appendTextureLookupAndModulate(&builder->fFSCode,
+                                                inputColor,
+                                                samplers[0],
+                                                coordName,
+                                                coordType);
+        builder->fFSCode.append(";\n");
+    }
+
+    static inline EffectKey GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+        const GrSimpleTextureEffect& ste = GetEffectFromStage<GrSimpleTextureEffect>(stage);
+        return GrGLEffectMatrix::GenKey(ste.getMatrix(),
+                                        stage.getCoordChangeMatrix(),
+                                        ste.texture(0));
+    }
+
+    virtual void setData(const GrGLUniformManager& uman, const GrEffectStage& stage) SK_OVERRIDE {
+        const GrSimpleTextureEffect& ste = GetEffectFromStage<GrSimpleTextureEffect>(stage);
+        fEffectMatrix.setData(uman, ste.getMatrix(), stage.getCoordChangeMatrix(), ste.texture(0));
+    }
+
+private:
+    GrGLEffectMatrix fEffectMatrix;
+    typedef GrGLEffect INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrSimpleTextureEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    this->updateConstantColorComponentsForModulation(color, validFlags);
+}
+
+const GrBackendEffectFactory& GrSimpleTextureEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrSimpleTextureEffect>::getInstance();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrSimpleTextureEffect);
+
+GrEffectRef* GrSimpleTextureEffect::TestCreate(SkRandom* random,
+                                               GrContext* context,
+                                               GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
+    return GrSimpleTextureEffect::Create(textures[texIdx], matrix);
+}
diff --git a/src/gpu/effects/GrSimpleTextureEffect.h b/src/gpu/effects/GrSimpleTextureEffect.h
new file mode 100644
index 0000000..ffc05e5
--- /dev/null
+++ b/src/gpu/effects/GrSimpleTextureEffect.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrSimpleTextureEffect_DEFINED
+#define GrSimpleTextureEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+
+class GrGLSimpleTextureEffect;
+
+/**
+ * The output color of this effect is a modulation of the input color and a sample from a texture.
+ * The coord to sample the texture is determine by a matrix. It allows explicit specification of
+ * the filtering and wrap modes (GrTextureParams).
+ */
+class GrSimpleTextureEffect : public GrSingleTextureEffect {
+public:
+    /* unfiltered, clamp mode */
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix)));
+        return CreateEffectRef(effect);
+    }
+
+    /* clamp mode */
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, bool bilerp) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, bilerp)));
+        return CreateEffectRef(effect);
+    }
+
+    static GrEffectRef* Create(GrTexture* tex, const SkMatrix& matrix, const GrTextureParams& p) {
+        AutoEffectUnref effect(SkNEW_ARGS(GrSimpleTextureEffect, (tex, matrix, p)));
+        return CreateEffectRef(effect);
+    }
+
+    virtual ~GrSimpleTextureEffect() {}
+
+    static const char* Name() { return "Texture"; }
+
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    typedef GrGLSimpleTextureEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+
+private:
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix)
+        : GrSingleTextureEffect(texture, matrix) {}
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix, bool bilerp)
+        : GrSingleTextureEffect(texture, matrix, bilerp) {}
+    GrSimpleTextureEffect(GrTexture* texture, const SkMatrix& matrix, const GrTextureParams& params)
+        : GrSingleTextureEffect(texture, matrix, params) {}
+
+    virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE {
+        const GrSimpleTextureEffect& ste = CastEffect<GrSimpleTextureEffect>(other);
+        return this->hasSameTextureParamsAndMatrix(ste);
+    }
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrSingleTextureEffect.cpp b/src/gpu/effects/GrSingleTextureEffect.cpp
new file mode 100644
index 0000000..7183ba3
--- /dev/null
+++ b/src/gpu/effects/GrSingleTextureEffect.cpp
@@ -0,0 +1,31 @@
+/*
+ * 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 "effects/GrSingleTextureEffect.h"
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const SkMatrix& m)
+    : fTextureAccess(texture)
+    , fMatrix(m) {
+    this->addTextureAccess(&fTextureAccess);
+}
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture, const SkMatrix& m, bool bilerp)
+    : fTextureAccess(texture, bilerp)
+    , fMatrix(m) {
+    this->addTextureAccess(&fTextureAccess);
+}
+
+GrSingleTextureEffect::GrSingleTextureEffect(GrTexture* texture,
+                                             const SkMatrix& m,
+                                             const GrTextureParams& params)
+    : fTextureAccess(texture, params)
+    , fMatrix(m) {
+    this->addTextureAccess(&fTextureAccess);
+}
+
+GrSingleTextureEffect::~GrSingleTextureEffect() {
+}
diff --git a/src/gpu/effects/GrSingleTextureEffect.h b/src/gpu/effects/GrSingleTextureEffect.h
new file mode 100644
index 0000000..4f25ddb
--- /dev/null
+++ b/src/gpu/effects/GrSingleTextureEffect.h
@@ -0,0 +1,62 @@
+/*
+ * 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 GrSingleTextureEffect_DEFINED
+#define GrSingleTextureEffect_DEFINED
+
+#include "GrEffect.h"
+#include "SkMatrix.h"
+
+class GrTexture;
+
+/**
+ * A base class for effects that draw a single texture with a texture matrix.
+ */
+class GrSingleTextureEffect : public GrEffect {
+public:
+    virtual ~GrSingleTextureEffect();
+
+    const SkMatrix& getMatrix() const { return fMatrix; }
+
+protected:
+    GrSingleTextureEffect(GrTexture*, const SkMatrix&); /* unfiltered, clamp mode */
+    GrSingleTextureEffect(GrTexture*, const SkMatrix&, bool bilerp); /* clamp mode */
+    GrSingleTextureEffect(GrTexture*, const SkMatrix&, const GrTextureParams&);
+
+    /**
+     * Helper for subclass onIsEqual() functions.
+     */
+    bool hasSameTextureParamsAndMatrix(const GrSingleTextureEffect& other) const {
+        const GrTextureAccess& otherAccess = other.fTextureAccess;
+        // We don't have to check the accesses' swizzles because they are inferred from the texture.
+        return fTextureAccess.getTexture() == otherAccess.getTexture() &&
+               fTextureAccess.getParams() == otherAccess.getParams() &&
+               this->getMatrix().cheapEqualTo(other.getMatrix());
+    }
+
+    /**
+     * Can be used as a helper to implement subclass getConstantColorComponents(). It assumes that
+     * the subclass output color will be a modulation of the input color with a value read from the
+     * texture.
+     */
+    void updateConstantColorComponentsForModulation(GrColor* color, uint32_t* validFlags) const {
+        if ((*validFlags & kA_ValidComponentFlag) && 0xFF == GrColorUnpackA(*color) &&
+            GrPixelConfigIsOpaque(this->texture(0)->config())) {
+            *validFlags = kA_ValidComponentFlag;
+        } else {
+            *validFlags = 0;
+        }
+    }
+
+private:
+    GrTextureAccess fTextureAccess;
+    SkMatrix        fMatrix;
+
+    typedef GrEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrTextureDomainEffect.cpp b/src/gpu/effects/GrTextureDomainEffect.cpp
new file mode 100644
index 0000000..833c198
--- /dev/null
+++ b/src/gpu/effects/GrTextureDomainEffect.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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 "GrTextureDomainEffect.h"
+#include "GrSimpleTextureEffect.h"
+#include "GrTBackendEffectFactory.h"
+#include "gl/GrGLEffect.h"
+#include "gl/GrGLEffectMatrix.h"
+#include "SkFloatingPoint.h"
+
+class GrGLTextureDomainEffect : public GrGLEffect {
+public:
+    GrGLTextureDomainEffect(const GrBackendEffectFactory&, const GrEffectRef&);
+
+    virtual void emitCode(GrGLShaderBuilder*,
+                          const GrEffectStage&,
+                          EffectKey,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray&) SK_OVERRIDE;
+
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&) SK_OVERRIDE;
+
+    static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
+
+private:
+    GrGLUniformManager::UniformHandle fNameUni;
+    GrGLEffectMatrix                  fEffectMatrix;
+    GrGLfloat                         fPrevDomain[4];
+
+    typedef GrGLEffect INHERITED;
+};
+
+GrGLTextureDomainEffect::GrGLTextureDomainEffect(const GrBackendEffectFactory& factory,
+                                                 const GrEffectRef&)
+    : INHERITED(factory)
+    , fNameUni(GrGLUniformManager::kInvalidUniformHandle) {
+    fPrevDomain[0] = SK_FloatNaN;
+}
+
+void GrGLTextureDomainEffect::emitCode(GrGLShaderBuilder* builder,
+                                       const GrEffectStage& stage,
+                                       EffectKey key,
+                                       const char* vertexCoords,
+                                       const char* outputColor,
+                                       const char* inputColor,
+                                       const TextureSamplerArray& samplers) {
+    const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage);
+
+    const char* coords;
+    fEffectMatrix.emitCodeMakeFSCoords2D(builder, key, vertexCoords, &coords);
+    const char* domain;
+    fNameUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                    kVec4f_GrSLType, "TexDom", &domain);
+    if (GrTextureDomainEffect::kClamp_WrapMode == effect.wrapMode()) {
+
+        builder->fFSCode.appendf("\tvec2 clampCoord = clamp(%s, %s.xy, %s.zw);\n",
+                               coords, domain, domain);
+
+        builder->fFSCode.appendf("\t%s = ", outputColor);
+        builder->appendTextureLookupAndModulate(&builder->fFSCode,
+                                                inputColor,
+                                                samplers[0],
+                                                "clampCoord");
+        builder->fFSCode.append(";\n");
+    } else {
+        GrAssert(GrTextureDomainEffect::kDecal_WrapMode == effect.wrapMode());
+        builder->fFSCode.append("\tbvec4 outside;\n");
+        builder->fFSCode.appendf("\toutside.xy = lessThan(%s, %s.xy);\n", coords, domain);
+        builder->fFSCode.appendf("\toutside.zw = greaterThan(%s, %s.zw);\n", coords, domain);
+        builder->fFSCode.appendf("\t%s = any(outside) ? vec4(0.0, 0.0, 0.0, 0.0) : ", outputColor);
+        builder->appendTextureLookupAndModulate(&builder->fFSCode, inputColor, samplers[0], coords);
+        builder->fFSCode.append(";\n");
+    }
+}
+
+void GrGLTextureDomainEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
+    const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage);
+    const GrRect& domain = effect.domain();
+
+    float values[4] = {
+        SkScalarToFloat(domain.left()),
+        SkScalarToFloat(domain.top()),
+        SkScalarToFloat(domain.right()),
+        SkScalarToFloat(domain.bottom())
+    };
+    // vertical flip if necessary
+    if (kBottomLeft_GrSurfaceOrigin == effect.texture(0)->origin()) {
+        values[1] = 1.0f - values[1];
+        values[3] = 1.0f - values[3];
+        // The top and bottom were just flipped, so correct the ordering
+        // of elements so that values = (l, t, r, b).
+        SkTSwap(values[1], values[3]);
+    }
+    if (0 != memcmp(values, fPrevDomain, 4 * sizeof(GrGLfloat))) {
+        uman.set4fv(fNameUni, 0, 1, values);
+    }
+    fEffectMatrix.setData(uman,
+                          effect.getMatrix(),
+                          stage.getCoordChangeMatrix(),
+                          effect.texture(0));
+}
+
+GrGLEffect::EffectKey GrGLTextureDomainEffect::GenKey(const GrEffectStage& stage, const GrGLCaps&) {
+    const GrTextureDomainEffect& effect = GetEffectFromStage<GrTextureDomainEffect>(stage);
+    EffectKey key = effect.wrapMode();
+    key <<= GrGLEffectMatrix::kKeyBits;
+    EffectKey matrixKey = GrGLEffectMatrix::GenKey(effect.getMatrix(),
+                                                   stage.getCoordChangeMatrix(),
+                                                   effect.texture(0));
+    return key | matrixKey;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+
+GrEffectRef* GrTextureDomainEffect::Create(GrTexture* texture,
+                                           const SkMatrix& matrix,
+                                           const GrRect& domain,
+                                           WrapMode wrapMode,
+                                           bool bilerp) {
+    static const SkRect kFullRect = {0, 0, SK_Scalar1, SK_Scalar1};
+    if (kClamp_WrapMode == wrapMode && domain.contains(kFullRect)) {
+        return GrSimpleTextureEffect::Create(texture, matrix, bilerp);
+    } else {
+        SkRect clippedDomain;
+        // We don't currently handle domains that are empty or don't intersect the texture.
+        // It is OK if the domain rect is a line or point, but it should not be inverted. We do not
+        // handle rects that do not intersect the [0..1]x[0..1] rect.
+        GrAssert(domain.fLeft <= domain.fRight);
+        GrAssert(domain.fTop <= domain.fBottom);
+        clippedDomain.fLeft = SkMaxScalar(domain.fLeft, kFullRect.fLeft);
+        clippedDomain.fRight = SkMinScalar(domain.fRight, kFullRect.fRight);
+        clippedDomain.fTop = SkMaxScalar(domain.fTop, kFullRect.fTop);
+        clippedDomain.fBottom = SkMinScalar(domain.fBottom, kFullRect.fBottom);
+        GrAssert(clippedDomain.fLeft <= clippedDomain.fRight);
+        GrAssert(clippedDomain.fTop <= clippedDomain.fBottom);
+
+        AutoEffectUnref effect(SkNEW_ARGS(GrTextureDomainEffect, (texture,
+                                                                  matrix,
+                                                                  clippedDomain,
+                                                                  wrapMode,
+                                                                  bilerp)));
+        return CreateEffectRef(effect);
+
+    }
+}
+
+GrTextureDomainEffect::GrTextureDomainEffect(GrTexture* texture,
+                                             const SkMatrix& matrix,
+                                             const GrRect& domain,
+                                             WrapMode wrapMode,
+                                             bool bilerp)
+    : GrSingleTextureEffect(texture, matrix, bilerp)
+    , fWrapMode(wrapMode)
+    , fTextureDomain(domain) {
+}
+
+GrTextureDomainEffect::~GrTextureDomainEffect() {
+
+}
+
+const GrBackendEffectFactory& GrTextureDomainEffect::getFactory() const {
+    return GrTBackendEffectFactory<GrTextureDomainEffect>::getInstance();
+}
+
+bool GrTextureDomainEffect::onIsEqual(const GrEffect& sBase) const {
+    const GrTextureDomainEffect& s = CastEffect<GrTextureDomainEffect>(sBase);
+    return this->hasSameTextureParamsAndMatrix(s) && this->fTextureDomain == s.fTextureDomain;
+}
+
+void GrTextureDomainEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
+    if (kDecal_WrapMode == fWrapMode) {
+        *validFlags = 0;
+    } else {
+        this->updateConstantColorComponentsForModulation(color, validFlags);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+GR_DEFINE_EFFECT_TEST(GrTextureDomainEffect);
+
+GrEffectRef* GrTextureDomainEffect::TestCreate(SkRandom* random,
+                                               GrContext* context,
+                                               GrTexture* textures[]) {
+    int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
+                                      GrEffectUnitTest::kAlphaTextureIdx;
+    GrRect domain;
+    domain.fLeft = random->nextUScalar1();
+    domain.fRight = random->nextRangeScalar(domain.fLeft, SK_Scalar1);
+    domain.fTop = random->nextUScalar1();
+    domain.fBottom = random->nextRangeScalar(domain.fTop, SK_Scalar1);
+    WrapMode wrapMode = random->nextBool() ? kClamp_WrapMode : kDecal_WrapMode;
+    const SkMatrix& matrix = GrEffectUnitTest::TestMatrix(random);
+    return GrTextureDomainEffect::Create(textures[texIdx], matrix, domain, wrapMode);
+}
diff --git a/src/gpu/effects/GrTextureDomainEffect.h b/src/gpu/effects/GrTextureDomainEffect.h
new file mode 100644
index 0000000..b7f665c
--- /dev/null
+++ b/src/gpu/effects/GrTextureDomainEffect.h
@@ -0,0 +1,87 @@
+/*
+ * 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 GrTextureDomainEffect_DEFINED
+#define GrTextureDomainEffect_DEFINED
+
+#include "GrSingleTextureEffect.h"
+#include "GrRect.h"
+
+class GrGLTextureDomainEffect;
+
+/**
+ * Limits a texture's lookup coordinates to a domain. Samples outside the domain are either clamped
+ * the edge of the domain or result in a vec4 of zeros. The domain is clipped to normalized texture
+ * coords ([0,1]x[0,1] square). Bilinear filtering can cause texels outside the domain to affect the
+ * read value unless the caller considers this when calculating the domain. TODO: This should be a
+ * helper that can assist an effect rather than effect unto itself.
+ */
+class GrTextureDomainEffect : public GrSingleTextureEffect {
+
+public:
+    /**
+     * If SkShader::kDecal_TileMode sticks then this enum could be replaced by SkShader::TileMode.
+     * We could also consider replacing/augmenting Decal mode with Border mode where the color
+     * outside of the domain is user-specifiable. Decal mode currently has a hard (non-lerped)
+     * transition between the border and the interior.
+     */
+    enum WrapMode {
+        kClamp_WrapMode,
+        kDecal_WrapMode,
+    };
+
+    static GrEffectRef* Create(GrTexture*,
+                               const SkMatrix&,
+                               const SkRect& domain,
+                               WrapMode,
+                               bool bilerp = false);
+
+    virtual ~GrTextureDomainEffect();
+
+    static const char* Name() { return "TextureDomain"; }
+
+    typedef GrGLTextureDomainEffect GLEffect;
+
+    virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
+    virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
+
+    const SkRect& domain() const { return fTextureDomain; }
+    WrapMode wrapMode() const { return fWrapMode; }
+
+    /* Computes a domain that bounds all the texels in texelRect. Note that with bilerp enabled
+       texels neighboring the domain may be read. */
+    static const SkRect MakeTexelDomain(const GrTexture* texture, const SkIRect& texelRect) {
+        SkScalar wInv = SK_Scalar1 / texture->width();
+        SkScalar hInv = SK_Scalar1 / texture->height();
+        SkRect result = {
+            texelRect.fLeft * wInv,
+            texelRect.fTop * hInv,
+            texelRect.fRight * wInv,
+            texelRect.fBottom * hInv
+        };
+        return result;
+    }
+
+protected:
+    WrapMode fWrapMode;
+    SkRect   fTextureDomain;
+
+private:
+    GrTextureDomainEffect(GrTexture*,
+                          const SkMatrix&,
+                          const GrRect& domain,
+                          WrapMode,
+                          bool bilerp);
+
+    virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
+
+    GR_DECLARE_EFFECT_TEST;
+
+    typedef GrSingleTextureEffect INHERITED;
+};
+
+#endif
diff --git a/src/gpu/effects/GrTextureStripAtlas.cpp b/src/gpu/effects/GrTextureStripAtlas.cpp
new file mode 100644
index 0000000..9081fb6
--- /dev/null
+++ b/src/gpu/effects/GrTextureStripAtlas.cpp
@@ -0,0 +1,346 @@
+
+/*
+ * 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 "GrTextureStripAtlas.h"
+#include "SkPixelRef.h"
+#include "SkTSearch.h"
+#include "GrTexture.h"
+
+#ifdef SK_DEBUG
+    #define VALIDATE this->validate()
+#else
+    #define VALIDATE
+#endif
+
+int32_t GrTextureStripAtlas::gCacheCount = 0;
+
+GrTHashTable<GrTextureStripAtlas::AtlasEntry,
+                GrTextureStripAtlas::AtlasHashKey, 8>*
+                            GrTextureStripAtlas::gAtlasCache = NULL;
+
+GrTHashTable<GrTextureStripAtlas::AtlasEntry, GrTextureStripAtlas::AtlasHashKey, 8>*
+GrTextureStripAtlas::GetCache() {
+
+    if (NULL == gAtlasCache) {
+        gAtlasCache = SkNEW((GrTHashTable<AtlasEntry, AtlasHashKey, 8>));
+    }
+
+    return gAtlasCache;
+}
+
+// Remove the specified atlas from the cache
+void GrTextureStripAtlas::CleanUp(const GrContext* context, void* info) {
+    GrAssert(NULL != info);
+
+    AtlasEntry* entry = static_cast<AtlasEntry*>(info);
+
+    // remove the cache entry
+    GetCache()->remove(entry->fKey, entry);
+
+    // remove the actual entry
+    SkDELETE(entry);
+
+    if (0 == GetCache()->count()) {
+        SkDELETE(gAtlasCache);
+        gAtlasCache = NULL;
+    }
+}
+
+GrTextureStripAtlas* GrTextureStripAtlas::GetAtlas(const GrTextureStripAtlas::Desc& desc) {
+    AtlasHashKey key;
+    key.setKeyData(desc.asKey());
+    AtlasEntry* entry = GetCache()->find(key);
+    if (NULL == entry) {
+        entry = SkNEW(AtlasEntry);
+
+        entry->fAtlas = SkNEW_ARGS(GrTextureStripAtlas, (desc));
+        entry->fKey = key;
+
+        desc.fContext->addCleanUp(CleanUp, entry);
+
+        GetCache()->insert(key, entry);
+    }
+
+    return entry->fAtlas;
+}
+
+GrTextureStripAtlas::GrTextureStripAtlas(GrTextureStripAtlas::Desc desc)
+    : fCacheKey(sk_atomic_inc(&gCacheCount))
+    , fLockedRows(0)
+    , fDesc(desc)
+    , fNumRows(desc.fHeight / desc.fRowHeight)
+    , fTexture(NULL)
+    , fRows(SkNEW_ARRAY(AtlasRow, fNumRows))
+    , fLRUFront(NULL)
+    , fLRUBack(NULL) {
+    GrAssert(fNumRows * fDesc.fRowHeight == fDesc.fHeight);
+    this->initLRU();
+    VALIDATE;
+}
+
+GrTextureStripAtlas::~GrTextureStripAtlas() {
+    SkDELETE_ARRAY(fRows);
+}
+
+int GrTextureStripAtlas::lockRow(const SkBitmap& data) {
+    VALIDATE;
+    if (0 == fLockedRows) {
+        this->lockTexture();
+    }
+
+    int key = data.getGenerationID();
+    int rowNumber = -1;
+    int index = this->searchByKey(key);
+
+    if (index >= 0) {
+        // We already have the data in a row, so we can just return that row
+        AtlasRow* row = fKeyTable[index];
+        if (0 == row->fLocks) {
+            this->removeFromLRU(row);
+        }
+        ++row->fLocks;
+        ++fLockedRows;
+
+        // Since all the rows are always stored in a contiguous array, we can save the memory
+        // required for storing row numbers and just compute it with some pointer arithmetic
+        rowNumber = static_cast<int>(row - fRows);
+    } else {
+        // ~index is the index where we will insert the new key to keep things sorted
+        index = ~index;
+
+        // We don't have this data cached, so pick the least recently used row to copy into
+        AtlasRow* row = this->getLRU();
+
+        ++fLockedRows;
+
+        if (NULL == row) {
+            // force a flush, which should unlock all the rows; then try again
+            fDesc.fContext->flush();
+            row = this->getLRU();
+            if (NULL == row) {
+                --fLockedRows;
+                return -1;
+            }
+        }
+
+        this->removeFromLRU(row);
+
+        uint32_t oldKey = row->fKey;
+
+        // If we are writing into a row that already held bitmap data, we need to remove the
+        // reference to that genID which is stored in our sorted table of key values.
+        if (oldKey != kEmptyAtlasRowKey) {
+
+            // Find the entry in the list; if it's before the index where we plan on adding the new
+            // entry, we decrement since it will shift elements ahead of it back by one.
+            int oldIndex = this->searchByKey(oldKey);
+            if (oldIndex < index) {
+                --index;
+            }
+
+            fKeyTable.remove(oldIndex);
+        }
+
+        row->fKey = key;
+        row->fLocks = 1;
+        fKeyTable.insert(index, 1, &row);
+        rowNumber = static_cast<int>(row - fRows);
+
+        SkAutoLockPixels lock(data);
+
+        // Pass in the kDontFlush flag, since we know we're writing to a part of this texture
+        // that is not currently in use
+        fDesc.fContext->writeTexturePixels(fTexture,
+                                           0,  rowNumber * fDesc.fRowHeight,
+                                           fDesc.fWidth, fDesc.fRowHeight,
+                                           SkBitmapConfig2GrPixelConfig(data.config()),
+                                           data.getPixels(),
+                                           data.rowBytes(),
+                                           GrContext::kDontFlush_PixelOpsFlag);
+    }
+
+    GrAssert(rowNumber >= 0);
+    VALIDATE;
+    return rowNumber;
+}
+
+void GrTextureStripAtlas::unlockRow(int row) {
+    VALIDATE;
+    --fRows[row].fLocks;
+    --fLockedRows;
+    GrAssert(fRows[row].fLocks >= 0 && fLockedRows >= 0);
+    if (0 == fRows[row].fLocks) {
+        this->appendLRU(fRows + row);
+    }
+    if (0 == fLockedRows) {
+        this->unlockTexture();
+    }
+    VALIDATE;
+}
+
+GrTextureStripAtlas::AtlasRow* GrTextureStripAtlas::getLRU() {
+    // Front is least-recently-used
+    AtlasRow* row = fLRUFront;
+    return row;
+}
+
+void GrTextureStripAtlas::lockTexture() {
+    GrTextureParams params;
+    GrTextureDesc texDesc;
+    texDesc.fWidth = fDesc.fWidth;
+    texDesc.fHeight = fDesc.fHeight;
+    texDesc.fConfig = fDesc.fConfig;
+
+    static const GrCacheID::Domain gTextureStripAtlasDomain = GrCacheID::GenerateDomain();
+    GrCacheID::Key key;
+    *key.fData32 = fCacheKey;
+    memset(key.fData32 + 1, 0, sizeof(key) - sizeof(uint32_t));
+    GrCacheID cacheID(gTextureStripAtlasDomain, key);
+
+    fTexture = fDesc.fContext->findAndRefTexture(texDesc, cacheID, &params);
+    if (NULL == fTexture) {
+        fTexture = fDesc.fContext->createTexture(&params, texDesc, cacheID, NULL, 0);
+        // This is a new texture, so all of our cache info is now invalid
+        this->initLRU();
+        fKeyTable.rewind();
+    }
+    GrAssert(NULL != fTexture);
+}
+
+void GrTextureStripAtlas::unlockTexture() {
+    GrAssert(NULL != fTexture && 0 == fLockedRows);
+    fTexture->unref();
+    fTexture = NULL;
+    fDesc.fContext->purgeCache();
+}
+
+void GrTextureStripAtlas::initLRU() {
+    fLRUFront = NULL;
+    fLRUBack = NULL;
+    // Initially all the rows are in the LRU list
+    for (int i = 0; i < fNumRows; ++i) {
+        fRows[i].fKey = kEmptyAtlasRowKey;
+        fRows[i].fNext = NULL;
+        fRows[i].fPrev = NULL;
+        this->appendLRU(fRows + i);
+    }
+    GrAssert(NULL == fLRUFront->fPrev && NULL == fLRUBack->fNext);
+}
+
+void GrTextureStripAtlas::appendLRU(AtlasRow* row) {
+    GrAssert(NULL == row->fPrev && NULL == row->fNext);
+    if (NULL == fLRUFront && NULL == fLRUBack) {
+        fLRUFront = row;
+        fLRUBack = row;
+    } else {
+        row->fPrev = fLRUBack;
+        fLRUBack->fNext = row;
+        fLRUBack = row;
+    }
+}
+
+void GrTextureStripAtlas::removeFromLRU(AtlasRow* row) {
+    GrAssert(NULL != row);
+    if (NULL != row->fNext && NULL != row->fPrev) {
+        row->fPrev->fNext = row->fNext;
+        row->fNext->fPrev = row->fPrev;
+    } else {
+        if (NULL == row->fNext) {
+            GrAssert(row == fLRUBack);
+            fLRUBack = row->fPrev;
+            if (fLRUBack) {
+                fLRUBack->fNext = NULL;
+            }
+        }
+        if (NULL == row->fPrev) {
+            GrAssert(row == fLRUFront);
+            fLRUFront = row->fNext;
+            if (fLRUFront) {
+                fLRUFront->fPrev = NULL;
+            }
+        }
+    }
+    row->fNext = NULL;
+    row->fPrev = NULL;
+}
+
+int GrTextureStripAtlas::searchByKey(uint32_t key) {
+    AtlasRow target;
+    target.fKey = key;
+    return SkTSearch<AtlasRow, GrTextureStripAtlas::compareKeys>((const AtlasRow**)fKeyTable.begin(),
+                                                                 fKeyTable.count(),
+                                                                 &target,
+                                                                 sizeof(AtlasRow*));
+}
+
+#ifdef SK_DEBUG
+void GrTextureStripAtlas::validate() {
+
+    // Our key table should be sorted
+    uint32_t prev = 1 > fKeyTable.count() ? 0 : fKeyTable[0]->fKey;
+    for (int i = 1; i < fKeyTable.count(); ++i) {
+        GrAssert(prev < fKeyTable[i]->fKey);
+        GrAssert(fKeyTable[i]->fKey != kEmptyAtlasRowKey);
+        prev = fKeyTable[i]->fKey;
+    }
+
+    int lruCount = 0;
+    // Validate LRU pointers, and count LRU entries
+    GrAssert(NULL == fLRUFront || NULL == fLRUFront->fPrev);
+    GrAssert(NULL == fLRUBack  || NULL == fLRUBack->fNext);
+    for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
+        if (NULL == r->fNext) {
+            GrAssert(r == fLRUBack);
+        } else {
+            GrAssert(r->fNext->fPrev == r);
+        }
+        ++lruCount;
+    }
+
+    int rowLocks = 0;
+    int freeRows = 0;
+
+    for (int i = 0; i < fNumRows; ++i) {
+        rowLocks += fRows[i].fLocks;
+        if (0 == fRows[i].fLocks) {
+            ++freeRows;
+            bool inLRU = false;
+            // Step through the LRU and make sure it's present
+            for (AtlasRow* r = fLRUFront; r != NULL; r = r->fNext) {
+                if (r == &fRows[i]) {
+                    inLRU = true;
+                    break;
+                }
+            }
+            GrAssert(inLRU);
+        } else {
+            // If we are locked, we should have a key
+            GrAssert(kEmptyAtlasRowKey != fRows[i].fKey);
+        }
+
+        // If we have a key != kEmptyAtlasRowKey, it should be in the key table
+        GrAssert(fRows[i].fKey == kEmptyAtlasRowKey || this->searchByKey(fRows[i].fKey) >= 0);
+    }
+
+    // Our count of locks should equal the sum of row locks, unless we ran out of rows and flushed,
+    // in which case we'll have one more lock than recorded in the rows (to represent the pending
+    // lock of a row; which ensures we don't unlock the texture prematurely).
+    GrAssert(rowLocks == fLockedRows || rowLocks + 1 == fLockedRows);
+
+    // We should have one lru entry for each free row
+    GrAssert(freeRows == lruCount);
+
+    // If we have locked rows, we should have a locked texture, otherwise
+    // it should be unlocked
+    if (fLockedRows == 0) {
+        GrAssert(NULL == fTexture);
+    } else {
+        GrAssert(NULL != fTexture);
+    }
+}
+#endif
diff --git a/src/gpu/effects/GrTextureStripAtlas.h b/src/gpu/effects/GrTextureStripAtlas.h
new file mode 100644
index 0000000..9ac356b
--- /dev/null
+++ b/src/gpu/effects/GrTextureStripAtlas.h
@@ -0,0 +1,181 @@
+
+/*
+ * 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 GrTextureStripAtlas_DEFINED
+#define GrTextureStripAtlas_DEFINED
+
+#include "SkBitmap.h"
+#include "GrTHashCache.h"
+#include "SkGr.h"
+#include "SkTDArray.h"
+#include "GrBinHashKey.h"
+
+/**
+ * Maintains a single large texture whose rows store many textures of a small fixed height,
+ * stored in rows across the x-axis such that we can safely wrap/repeat them horizontally.
+ */
+class GrTextureStripAtlas {
+public:
+    /**
+     * Descriptor struct which we'll use as a hash table key
+     **/
+    struct Desc {
+        Desc() { memset(this, 0, sizeof(*this)); }
+        uint16_t fWidth, fHeight, fRowHeight;
+        GrPixelConfig fConfig;
+        GrContext* fContext;
+        const uint32_t* asKey() const { return reinterpret_cast<const uint32_t*>(this); }
+    };
+
+    /**
+     * Try to find an atlas with the required parameters, creates a new one if necessary
+     */
+    static GrTextureStripAtlas* GetAtlas(const Desc& desc);
+
+    ~GrTextureStripAtlas();
+
+    /**
+     * Add a texture to the atlas
+     *  @param data Bitmap data to copy into the row
+     *  @return The row index we inserted into, or -1 if we failed to find an open row. The caller
+     *      is responsible for calling unlockRow() with this row index when it's done with it.
+     */
+    int lockRow(const SkBitmap& data);
+    void unlockRow(int row);
+
+    /**
+     * These functions help turn an integer row index in [0, 1, 2, ... numRows] into a scalar y
+     * texture coordinate in [0, 1] that we can use in a shader.
+     *
+     * If a regular texture access without using the atlas looks like:
+     *
+     *      texture2D(sampler, vec2(x, y))
+     *
+     * Then when using the atlas we'd replace it with:
+     *
+     *       texture2D(sampler, vec2(x, yOffset + y * scaleFactor))
+     *
+     * Where yOffset, returned by getYOffset(), is the offset to the start of the row within the
+     * atlas and scaleFactor, returned by getVerticalScaleFactor(), is the y-scale of the row,
+     * relative to the height of the overall atlas texture.
+     */
+    SkScalar getYOffset(int row) const { return SkIntToScalar(row) / fNumRows; }
+    SkScalar getVerticalScaleFactor() const { return SkIntToScalar(fDesc.fRowHeight) / fDesc.fHeight; }
+
+    GrContext* getContext() const { return fDesc.fContext; }
+    GrTexture* getTexture() const { return fTexture; }
+
+private:
+
+    // Key to indicate an atlas row without any meaningful data stored in it
+    const static uint32_t kEmptyAtlasRowKey = 0xffffffff;
+
+    /**
+     * The state of a single row in our cache, next/prev pointers allow these to be chained
+     * together to represent LRU status
+     */
+    struct AtlasRow : public GrNoncopyable {
+        AtlasRow() : fKey(kEmptyAtlasRowKey), fLocks(0), fNext(NULL), fPrev(NULL) { }
+        // GenerationID of the bitmap that is represented by this row, 0xffffffff means "empty"
+        uint32_t fKey;
+        // How many times this has been locked (0 == unlocked)
+        int32_t fLocks;
+        // We maintain an LRU linked list between unlocked nodes with these pointers
+        AtlasRow* fNext;
+        AtlasRow* fPrev;
+    };
+
+    /**
+     * We'll only allow construction via the static GrTextureStripAtlas::GetAtlas
+     */
+    GrTextureStripAtlas(Desc desc);
+
+    void lockTexture();
+    void unlockTexture();
+
+    /**
+     * Initialize our LRU list (if one already exists, clear it and start anew)
+     */
+    void initLRU();
+
+    /**
+     * Grabs the least recently used free row out of the LRU list, returns NULL if no rows are free.
+     */
+    AtlasRow* getLRU();
+
+    void appendLRU(AtlasRow* row);
+    void removeFromLRU(AtlasRow* row);
+
+    /**
+     * Searches the key table for a key and returns the index if found; if not found, it returns
+     * the bitwise not of the index at which we could insert the key to maintain a sorted list.
+     **/
+    int searchByKey(uint32_t key);
+
+    /**
+     * Compare two atlas rows by key, so we can sort/search by key
+     */
+    static int compareKeys(const AtlasRow* lhs, const AtlasRow* rhs) {
+        return lhs->fKey - rhs->fKey;
+    }
+
+#ifdef SK_DEBUG
+    void validate();
+#endif
+
+    /**
+     * Clean up callback registered with GrContext. Allows this class to
+     * free up any allocated AtlasEntry and GrTextureStripAtlas objects
+     */
+    static void CleanUp(const GrContext* context, void* info);
+
+    // Hash table entry for atlases
+    class AtlasEntry;
+    typedef GrTBinHashKey<AtlasEntry, sizeof(GrTextureStripAtlas::Desc)> AtlasHashKey;
+    class AtlasEntry : public ::GrNoncopyable {
+    public:
+        AtlasEntry() : fAtlas(NULL) {}
+        ~AtlasEntry() { SkDELETE(fAtlas); }
+        int compare(const AtlasHashKey& key) const { return fKey.compare(key); }
+        AtlasHashKey fKey;
+        GrTextureStripAtlas* fAtlas;
+    };
+
+    static GrTHashTable<AtlasEntry, AtlasHashKey, 8>* gAtlasCache;
+
+    static GrTHashTable<AtlasEntry, AtlasHashKey, 8>* GetCache();
+
+    // We increment gCacheCount for each atlas
+    static int32_t gCacheCount;
+
+    // A unique ID for this texture (formed with: gCacheCount++), so we can be sure that if we
+    // get a texture back from the texture cache, that it's the same one we last used.
+    const int32_t fCacheKey;
+
+    // Total locks on all rows (when this reaches zero, we can unlock our texture)
+    int32_t fLockedRows;
+
+    const Desc fDesc;
+    const uint16_t fNumRows;
+    GrTexture* fTexture;
+
+    // Array of AtlasRows which store the state of all our rows. Stored in a contiguous array, in
+    // order that they appear in our texture, this means we can subtract this pointer from a row
+    // pointer to get its index in the texture, and can save storing a row number in AtlasRow.
+    AtlasRow* fRows;
+
+    // Head and tail for linked list of least-recently-used rows (front = least recently used).
+    // Note that when a texture is locked, it gets removed from this list until it is unlocked.
+    AtlasRow* fLRUFront;
+    AtlasRow* fLRUBack;
+
+    // A list of pointers to AtlasRows that currently contain cached images, sorted by key
+    SkTDArray<AtlasRow*> fKeyTable;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp
index d690ff3..b140a10 100644
--- a/src/gpu/gl/GrGLCaps.cpp
+++ b/src/gpu/gl/GrGLCaps.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2012 Google Inc.
  *
@@ -9,6 +8,7 @@
 
 #include "GrGLCaps.h"
 #include "GrGLContextInfo.h"
+#include "SkTSearch.h"
 
 GrGLCaps::GrGLCaps() {
     this->reset();
@@ -19,7 +19,10 @@
     fStencilFormats.reset();
     fStencilVerifiedColorConfigs.reset();
     fMSFBOType = kNone_MSFBOType;
+    fMaxSampleCount = 0;
+    fCoverageAAType = kNone_CoverageAAType;
     fMaxFragmentUniformVectors = 0;
+    fMaxVertexAttributes = 0;
     fRGBA8RenderbufferSupport = false;
     fBGRAFormatSupport = false;
     fBGRAIsInternalFormat = false;
@@ -30,6 +33,10 @@
     fPackFlipYSupport = false;
     fTextureUsageSupport = false;
     fTexStorageSupport = false;
+    fTextureRedSupport = false;
+    fImagingSupport = false;
+    fTwoFormatLimit = false;
+    fFragCoordsConventionSupport = false;
 }
 
 GrGLCaps::GrGLCaps(const GrGLCaps& caps) {
@@ -41,7 +48,11 @@
     fStencilFormats = caps.fStencilFormats;
     fStencilVerifiedColorConfigs = caps.fStencilVerifiedColorConfigs;
     fMaxFragmentUniformVectors = caps.fMaxFragmentUniformVectors;
+    fMaxVertexAttributes = caps.fMaxVertexAttributes;
     fMSFBOType = caps.fMSFBOType;
+    fMaxSampleCount = caps.fMaxSampleCount;
+    fCoverageAAType = caps.fCoverageAAType;
+    fMSAACoverageModes = caps.fMSAACoverageModes;
     fRGBA8RenderbufferSupport = caps.fRGBA8RenderbufferSupport;
     fBGRAFormatSupport = caps.fBGRAFormatSupport;
     fBGRAIsInternalFormat = caps.fBGRAIsInternalFormat;
@@ -52,6 +63,10 @@
     fPackFlipYSupport = caps.fPackFlipYSupport;
     fTextureUsageSupport = caps.fTextureUsageSupport;
     fTexStorageSupport = caps.fTexStorageSupport;
+    fTextureRedSupport = caps.fTextureRedSupport;
+    fImagingSupport = caps.fImagingSupport;
+    fTwoFormatLimit = caps.fTwoFormatLimit;
+    fFragCoordsConventionSupport = caps.fFragCoordsConventionSupport;
 
     return *this;
 }
@@ -76,6 +91,7 @@
         GR_GL_GetIntegerv(gli, GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS, &max);
         fMaxFragmentUniformVectors = max / 4;
     }
+    GR_GL_GetIntegerv(gli, GR_GL_MAX_VERTEX_ATTRIBS, &fMaxVertexAttributes);
 
     if (kDesktop_GrGLBinding == binding) {
         fRGBA8RenderbufferSupport = true;
@@ -88,7 +104,6 @@
         fBGRAFormatSupport = version >= GR_GL_VER(1,2) ||
                              ctxInfo.hasExtension("GL_EXT_bgra");
     } else {
-        bool hasBGRAExt = false;
         if (ctxInfo.hasExtension("GL_APPLE_texture_format_BGRA8888")) {
             fBGRAFormatSupport = true;
         } else if (ctxInfo.hasExtension("GL_EXT_texture_format_BGRA8888")) {
@@ -96,7 +111,7 @@
             fBGRAIsInternalFormat = true;
         }
         GrAssert(fBGRAFormatSupport ||
-                 kSkia8888_PM_GrPixelConfig != kBGRA_8888_PM_GrPixelConfig);
+                 kSkia8888_GrPixelConfig != kBGRA_8888_GrPixelConfig);
     }
 
     if (kDesktop_GrGLBinding == binding) {
@@ -129,10 +144,78 @@
                          ctxInfo.hasExtension("GL_ARB_texture_storage") ||
                          ctxInfo.hasExtension("GL_EXT_texture_storage");
 
+    // ARB_texture_rg is part of OpenGL 3.0
+    if (kDesktop_GrGLBinding == binding) {
+        fTextureRedSupport = version >= GR_GL_VER(3,0) ||
+                             ctxInfo.hasExtension("GL_ARB_texture_rg");
+    } else {
+        fTextureRedSupport = ctxInfo.hasExtension("GL_EXT_texture_rg");
+    }
+
+    fImagingSupport = kDesktop_GrGLBinding == binding &&
+                      ctxInfo.hasExtension("GL_ARB_imaging");
+
+    // ES 2 only guarantees RGBA/uchar + one other format/type combo for
+    // ReadPixels. The other format has to checked at run-time since it
+    // can change based on which render target is bound
+    fTwoFormatLimit = kES2_GrGLBinding == binding;
+
+    // Known issue on at least some Intel platforms:
+    // http://code.google.com/p/skia/issues/detail?id=946
+    if (kIntel_GrGLVendor != ctxInfo.vendor()) {
+        fFragCoordsConventionSupport = ctxInfo.glslGeneration() >= k150_GrGLSLGeneration ||
+                                       ctxInfo.hasExtension("GL_ARB_fragment_coord_conventions");
+    }
+
     this->initFSAASupport(ctxInfo);
     this->initStencilFormats(ctxInfo);
 }
 
+bool GrGLCaps::readPixelsSupported(const GrGLInterface* intf,
+                                   GrGLenum format,
+                                   GrGLenum type) const {
+    if (GR_GL_RGBA == format && GR_GL_UNSIGNED_BYTE == type) {
+        // ES 2 guarantees this format is supported
+        return true;
+    }
+
+    if (!fTwoFormatLimit) {
+        // not limited by ES 2's constraints
+        return true;
+    }
+
+    GrGLint otherFormat = GR_GL_RGBA;
+    GrGLint otherType = GR_GL_UNSIGNED_BYTE;
+
+    // The other supported format/type combo supported for ReadPixels
+    // can change based on which render target is bound
+    GR_GL_GetIntegerv(intf,
+                      GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT,
+                      &otherFormat);
+
+    GR_GL_GetIntegerv(intf,
+                      GR_GL_IMPLEMENTATION_COLOR_READ_TYPE,
+                      &otherType);
+
+    return (GrGLenum)otherFormat == format && (GrGLenum)otherType == type;
+}
+
+namespace {
+int coverage_mode_compare(const GrGLCaps::MSAACoverageMode* left,
+                          const GrGLCaps::MSAACoverageMode* right) {
+    if (left->fCoverageSampleCnt < right->fCoverageSampleCnt) {
+        return -1;
+    } else if (right->fCoverageSampleCnt < left->fCoverageSampleCnt) {
+        return 1;
+    } else if (left->fColorSampleCnt < right->fColorSampleCnt) {
+        return -1;
+    } else if (right->fColorSampleCnt < left->fColorSampleCnt) {
+        return 1;
+    }
+    return 0;
+}
+}
+
 void GrGLCaps::initFSAASupport(const GrGLContextInfo& ctxInfo) {
 
     fMSFBOType = kNone_MSFBOType;
@@ -142,8 +225,8 @@
            // and fbo_blit extensions.
            fMSFBOType = kDesktopEXT_MSFBOType;
        } else if (ctxInfo.hasExtension("GL_APPLE_framebuffer_multisample")) {
-            fMSFBOType = kAppleES_MSFBOType;
-        }
+           fMSFBOType = kAppleES_MSFBOType;
+       }
     } else {
         if ((ctxInfo.version() >= GR_GL_VER(3,0)) ||
             ctxInfo.hasExtension("GL_ARB_framebuffer_object")) {
@@ -152,6 +235,55 @@
                    ctxInfo.hasExtension("GL_EXT_framebuffer_blit")) {
             fMSFBOType = GrGLCaps::kDesktopEXT_MSFBOType;
         }
+        // TODO: We could populate fMSAACoverageModes using GetInternalformativ
+        // on GL 4.2+. It's format-specific, though. See also
+        // http://code.google.com/p/skia/issues/detail?id=470 about using actual
+        // rather than requested sample counts in cache key.
+        if (ctxInfo.hasExtension("GL_NV_framebuffer_multisample_coverage")) {
+            fCoverageAAType = kNVDesktop_CoverageAAType;
+            GrGLint count;
+            GR_GL_GetIntegerv(ctxInfo.interface(),
+                              GR_GL_MAX_MULTISAMPLE_COVERAGE_MODES,
+                              &count);
+            fMSAACoverageModes.setCount(count);
+            GR_GL_GetIntegerv(ctxInfo.interface(),
+                              GR_GL_MULTISAMPLE_COVERAGE_MODES,
+                              (int*)&fMSAACoverageModes[0]);
+            // The NV driver seems to return the modes already sorted but the
+            // spec doesn't require this. So we sort.
+            qsort(&fMSAACoverageModes[0],
+                    count,
+                    sizeof(MSAACoverageMode),
+                    SkCastForQSort(coverage_mode_compare));
+        }
+    }
+    if (kNone_MSFBOType != fMSFBOType) {
+        GR_GL_GetIntegerv(ctxInfo.interface(),
+                          GR_GL_MAX_SAMPLES,
+                          &fMaxSampleCount);
+    }
+}
+
+const GrGLCaps::MSAACoverageMode& GrGLCaps::getMSAACoverageMode(
+                                            int desiredSampleCount) const {
+    static const MSAACoverageMode kNoneMode = {0, 0};
+    if (0 == fMSAACoverageModes.count()) {
+        return kNoneMode;
+    } else {
+        GrAssert(kNone_CoverageAAType != fCoverageAAType);
+        int max = (fMSAACoverageModes.end() - 1)->fCoverageSampleCnt;
+        desiredSampleCount = GrMin(desiredSampleCount, max);
+        MSAACoverageMode desiredMode = {desiredSampleCount, 0};
+        int idx = SkTSearch<MSAACoverageMode>(&fMSAACoverageModes[0],
+                                              fMSAACoverageModes.count(),
+                                              desiredMode,
+                                              sizeof(MSAACoverageMode),
+                                              &coverage_mode_compare);
+        if (idx < 0) {
+            idx = ~idx;
+        }
+        GrAssert(idx >= 0 && idx < fMSAACoverageModes.count());
+        return fMSAACoverageModes[idx];
     }
 }
 
@@ -173,12 +305,12 @@
         gS16   = {GR_GL_STENCIL_INDEX16,  16,               16,               false},
         gD24S8 = {GR_GL_DEPTH24_STENCIL8, 8,                32,               true },
         gS4    = {GR_GL_STENCIL_INDEX4,   4,                4,                false},
-        gS     = {GR_GL_STENCIL_INDEX,    kUnknownBitCount, kUnknownBitCount, false},
+    //  gS     = {GR_GL_STENCIL_INDEX,    kUnknownBitCount, kUnknownBitCount, false},
         gDS    = {GR_GL_DEPTH_STENCIL,    kUnknownBitCount, kUnknownBitCount, true };
 
     if (kDesktop_GrGLBinding == ctxInfo.binding()) {
         bool supportsPackedDS =
-            ctxInfo.version() >= GR_GL_VER(3,0) || 
+            ctxInfo.version() >= GR_GL_VER(3,0) ||
             ctxInfo.hasExtension("GL_EXT_packed_depth_stencil") ||
             ctxInfo.hasExtension("GL_ARB_framebuffer_object");
 
@@ -291,5 +423,6 @@
              (fPackRowLengthSupport ? "YES": "NO"));
     GrPrintf("Pack Flip Y support: %s\n",
              (fPackFlipYSupport ? "YES": "NO"));
+    GrPrintf("Two Format Limit: %s\n", (fTwoFormatLimit ? "YES": "NO"));
+    GrPrintf("Fragment coord conventions support: %s\n", (fFragCoordsConventionSupport ? "YES": "NO"));
 }
-
diff --git a/src/gpu/gl/GrGLCaps.h b/src/gpu/gl/GrGLCaps.h
index a5318eb..9dfbf23 100644
--- a/src/gpu/gl/GrGLCaps.h
+++ b/src/gpu/gl/GrGLCaps.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2012 Google Inc.
  *
@@ -10,6 +9,8 @@
 #ifndef GrGLCaps_DEFINED
 #define GrGLCaps_DEFINED
 
+#include "SkTArray.h"
+#include "SkTDArray.h"
 #include "GrGLStencilBuffer.h"
 
 class GrGLContextInfo;
@@ -24,6 +25,23 @@
     typedef GrGLStencilBuffer::Format StencilFormat;
 
     /**
+     * Represents a supported multisampling/coverage-sampling mode.
+     */
+    struct MSAACoverageMode {
+        // "Coverage samples" includes samples that actually have color, depth,
+        // stencil, ... as well as those that don't (coverage only). All samples
+        // are coverage samples. (We're using the word "coverage sample" to
+        // match the NV extension language.)
+        int fCoverageSampleCnt;
+
+        // Color samples are samples that store data values (color, stencil,
+        // depth) rather than just representing coverage. They are a subset
+        // of coverage samples. (Again the wording was chosen to match the
+        // extension.)
+        int fColorSampleCnt;
+    };
+
+    /**
      * The type of MSAA for FBOs supported. Different extensions have different
      * semantics of how / when a resolve is performed.
      */
@@ -31,7 +49,7 @@
         /**
          * no support for MSAA FBOs
          */
-        kNone_MSFBOType = 0,  
+        kNone_MSFBOType = 0,
         /**
          * GL3.0-style MSAA FBO (GL_ARB_framebuffer_object)
          */
@@ -46,6 +64,18 @@
         kAppleES_MSFBOType,
     };
 
+    enum CoverageAAType {
+        /**
+         * No coverage sample support
+         */
+        kNone_CoverageAAType,
+
+        /**
+         * GL_NV_framebuffer_multisample_coverage
+         */
+        kNVDesktop_CoverageAAType,
+    };
+
     /**
      * Creates a GrGLCaps that advertises no support for any extensions,
      * formats, etc. Call init to initialize from a GrGLContextInfo.
@@ -108,6 +138,25 @@
     MSFBOType msFBOType() const { return fMSFBOType; }
 
     /**
+     * Reports the maximum number of samples supported.
+     */
+    int maxSampleCount() const { return fMaxSampleCount; }
+
+    /**
+     * Reports the type of coverage sample AA support.
+     */
+    CoverageAAType coverageAAType() const { return fCoverageAAType; }
+
+    /**
+     * Chooses a supported coverage mode based on a desired sample count. The
+     * desired sample count is rounded up the next supported coverage sample
+     * count unless a it is larger than the max in which case it is rounded
+     * down. Once a coverage sample count is decided, the supported mode with
+     * the fewest color samples is chosen.
+     */
+    const MSAACoverageMode& getMSAACoverageMode(int desiredSampleCount) const;
+
+    /**
      * Prints the caps info using GrPrintf.
      */
     void print() const;
@@ -124,6 +173,9 @@
     /// The maximum number of fragment uniform vectors (GLES has min. 16).
     int maxFragmentUniformVectors() const { return fMaxFragmentUniformVectors; }
 
+    // maximum number of attribute values per vertex
+    int maxVertexAttributes() const { return fMaxVertexAttributes; }
+
     /// ES requires an extension to support RGBA8 in RenderBufferStorage
     bool rgba8RenderbufferSupport() const { return fRGBA8RenderbufferSupport; }
 
@@ -158,6 +210,20 @@
     /// Is there support for glTexStorage
     bool texStorageSupport() const { return fTexStorageSupport; }
 
+    /// Is there support for GL_RED and GL_R8
+    bool textureRedSupport() const { return fTextureRedSupport; }
+
+    /// Is GL_ARB_IMAGING supported
+    bool imagingSupport() const { return fImagingSupport; }
+
+    /// Is GL_ARB_fragment_coord_conventions supported?
+    bool fragCoordConventionsSupport() const { return fFragCoordsConventionSupport; }
+
+    // Does ReadPixels support the provided format/type combo?
+    bool readPixelsSupported(const GrGLInterface* intf,
+                             GrGLenum format,
+                             GrGLenum type) const;
+
 private:
     /**
      * Maintains a bit per GrPixelConfig. It is used to avoid redundantly
@@ -210,7 +276,12 @@
     SkTArray<VerifiedColorConfigs, true> fStencilVerifiedColorConfigs;
 
     int fMaxFragmentUniformVectors;
+    int fMaxVertexAttributes;
+
     MSFBOType fMSFBOType;
+    int fMaxSampleCount;
+    CoverageAAType fCoverageAAType;
+    SkTDArray<MSAACoverageMode> fMSAACoverageModes;
 
     bool fRGBA8RenderbufferSupport : 1;
     bool fBGRAFormatSupport : 1;
@@ -222,6 +293,10 @@
     bool fPackFlipYSupport : 1;
     bool fTextureUsageSupport : 1;
     bool fTexStorageSupport : 1;
+    bool fTextureRedSupport : 1;
+    bool fImagingSupport  : 1;
+    bool fTwoFormatLimit : 1;
+    bool fFragCoordsConventionSupport : 1;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLContextInfo.cpp b/src/gpu/gl/GrGLContextInfo.cpp
index 33e19ab..7a33ac7 100644
--- a/src/gpu/gl/GrGLContextInfo.cpp
+++ b/src/gpu/gl/GrGLContextInfo.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2012 Google Inc.
  *
@@ -31,6 +30,7 @@
     fBindingInUse = ctx.fBindingInUse;
     fGLVersion = ctx.fGLVersion;
     fGLSLGeneration = ctx.fGLSLGeneration;
+    fVendor = ctx.fVendor;
     fExtensionString = ctx.fExtensionString;
     fGLCaps = ctx.fGLCaps;
     return *this;
@@ -41,6 +41,7 @@
     fBindingInUse = kNone_GrGLBinding;
     fGLVersion = GR_GL_VER(0, 0);
     fGLSLGeneration = static_cast<GrGLSLGeneration>(0);
+    fVendor = kOther_GrGLVendor;
     fExtensionString = "";
     fGLCaps.reset();
 }
@@ -56,7 +57,7 @@
         const char* ver = reinterpret_cast<const char*>(verUByte);
         GrGLBinding binding = GrGLGetBindingInUseFromString(ver);
 
-        if (!interface->validate(fBindingInUse)) {
+        if (interface->validate(binding)) {
 
             fInterface = interface;
             interface->ref();
@@ -71,7 +72,7 @@
             const GrGLubyte* ext;
             GR_GL_CALL_RET(interface, ext, GetString(GR_GL_EXTENSIONS));
             fExtensionString = reinterpret_cast<const char*>(ext);
-
+            fVendor = GrGLGetVendor(interface);
             fGLCaps.init(*this);
             return true;
         }
@@ -82,4 +83,3 @@
 bool GrGLContextInfo::isInitialized() const {
     return kNone_GrGLBinding != fBindingInUse;
 }
-
diff --git a/src/gpu/gl/GrGLContextInfo.h b/src/gpu/gl/GrGLContextInfo.h
index c37b11d..a6c997f 100644
--- a/src/gpu/gl/GrGLContextInfo.h
+++ b/src/gpu/gl/GrGLContextInfo.h
@@ -9,9 +9,10 @@
 #ifndef GrGLContextInfo_DEFINED
 #define GrGLContextInfo_DEFINED
 
-#include "GrGLCaps.h"
 #include "gl/GrGLInterface.h"
+#include "GrGLCaps.h"
 #include "GrGLSL.h"
+#include "GrGLUtil.h"
 
 #include "SkString.h"
 
@@ -30,7 +31,7 @@
 
     /**
      * Creates a GrGLContextInfo from a GrGLInterface and the currently
-     * bound OpenGL context accesible by the GrGLInterface.
+     * bound OpenGL context accessible by the GrGLInterface.
      */
     explicit GrGLContextInfo(const GrGLInterface* interface);
 
@@ -57,6 +58,7 @@
     GrGLBinding binding() const { return fBindingInUse; }
     GrGLVersion version() const { return fGLVersion; }
     GrGLSLGeneration glslGeneration() const { return fGLSLGeneration; }
+    GrGLVendor vendor() const { return fVendor; }
     const GrGLCaps& caps() const { return fGLCaps; }
     GrGLCaps& caps() { return fGLCaps; }
 
@@ -78,6 +80,7 @@
     GrGLBinding          fBindingInUse;
     GrGLVersion          fGLVersion;
     GrGLSLGeneration     fGLSLGeneration;
+    GrGLVendor           fVendor;
     SkString             fExtensionString;
     GrGLCaps             fGLCaps;
 };
diff --git a/src/gpu/gl/GrGLCreateNativeInterface_none.cpp b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
index 914ed51..e0c72c5 100644
--- a/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
+++ b/src/gpu/gl/GrGLCreateNativeInterface_none.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
diff --git a/src/gpu/gl/GrGLCreateNullInterface.cpp b/src/gpu/gl/GrGLCreateNullInterface.cpp
index 5095079..f450cc8 100644
--- a/src/gpu/gl/GrGLCreateNullInterface.cpp
+++ b/src/gpu/gl/GrGLCreateNullInterface.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -8,7 +7,10 @@
 
 
 #include "gl/GrGLInterface.h"
-#include "../GrTDArray.h"
+#include "GrGLDefines.h"
+#include "SkTDArray.h"
+
+namespace { // added to suppress 'no previous prototype' warning
 
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLActiveTexture(GrGLenum texture) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLAttachShader(GrGLuint program, GrGLuint shader) {}
@@ -47,7 +49,11 @@
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadBuffer(GrGLenum src) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLReadPixels(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, GrGLvoid* pixels) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLScissor(GrGLint x, GrGLint y, GrGLsizei width, GrGLsizei height) {}
+#if GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLShaderSource(GrGLuint shader, GrGLsizei count, const char* const * str, const GrGLint* length) {}
+#else
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLShaderSource(GrGLuint shader, GrGLsizei count, const char** str, const GrGLint* length) {}
+#endif
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilFunc(GrGLenum func, GrGLint ref, GrGLuint mask) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilFuncSeparate(GrGLenum face, GrGLenum func, GrGLint ref, GrGLuint mask) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilMask(GrGLuint mask) {}
@@ -56,6 +62,7 @@
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLStencilOpSeparate(GrGLenum face, GrGLenum fail, GrGLenum zfail, GrGLenum zpass) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexImage2D(GrGLenum target, GrGLint level, GrGLint internalformat, GrGLsizei width, GrGLsizei height, GrGLint border, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexParameteri(GrGLenum target, GrGLenum pname, GrGLint param) {}
+GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexParameteriv(GrGLenum target, GrGLenum pname, const GrGLint* params) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexStorage2D(GrGLenum target, GrGLsizei levels, GrGLenum internalformat, GrGLsizei width, GrGLsizei height) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLTexSubImage2D(GrGLenum target, GrGLint level, GrGLint xoffset, GrGLint yoffset, GrGLsizei width, GrGLsizei height, GrGLenum format, GrGLenum type, const GrGLvoid* pixels) {}
 GrGLvoid GR_GL_FUNCTION_TYPE nullGLUniform1f(GrGLint location, GrGLfloat v0) {}
@@ -125,7 +132,7 @@
 
 // In debug builds we do asserts that ensure we agree with GL about when a buffer
 // is mapped.
-static GrTDArray<GrGLuint> gMappedBuffers;
+static SkTDArray<GrGLuint> gMappedBuffers;
 static GrGLuint gCurrArrayBuffer;
 static GrGLuint gCurrElementArrayBuffer;
 
@@ -210,7 +217,7 @@
                     break;
                 case GR_GL_ELEMENT_ARRAY_BUFFER:
                     buf = gCurrElementArrayBuffer;
-                    break;  
+                    break;
             }
             if (buf) {
                 for (int i = 0; i < gMappedBuffers.count(); ++i) {
@@ -274,9 +281,6 @@
         case GR_GL_MAX_VERTEX_ATTRIBS:
             *params = 16;
             break;
-        case GR_GL_MAX_TEXTURE_UNITS:
-            *params = 8;
-            break;
         default:
             GrCrash("Unexpected pname to GetIntegerv");
     }
@@ -360,6 +364,10 @@
             return (const GrGLubyte*)"4.0 Null GL";
         case GR_GL_SHADING_LANGUAGE_VERSION:
             return (const GrGLubyte*)"4.20.8 Null GLSL";
+        case GR_GL_VENDOR:
+            return (const GrGLubyte*)"Null Vendor";
+        case GR_GL_RENDERER:
+            return (const GrGLubyte*)"The Null (Non-)Renderer";
         default:
             GrCrash("Unexpected name to GetString");
             return NULL;
@@ -377,12 +385,14 @@
     return ++gUniLocation;
 }
 
+} // end anonymous namespace
+
 const GrGLInterface* GrGLCreateNullInterface() {
-    // The gl functions are not context-specific so we create one global 
+    // The gl functions are not context-specific so we create one global
     // interface
     static SkAutoTUnref<GrGLInterface> glInterface;
     if (!glInterface.get()) {
-        GrGLInterface* interface = new GrGLInterface;
+        GrGLInterface* interface = SkNEW(GrGLInterface);
         glInterface.reset(interface);
         interface->fBindingsExported = kDesktop_GrGLBinding;
         interface->fActiveTexture = nullGLActiveTexture;
@@ -457,6 +467,7 @@
         interface->fStencilOpSeparate = nullGLStencilOpSeparate;
         interface->fTexImage2D = nullGLTexImage2D;
         interface->fTexParameteri = nullGLTexParameteri;
+        interface->fTexParameteriv = nullGLTexParameteriv;
         interface->fTexSubImage2D = nullGLTexSubImage2D;
         interface->fTexStorage2D = nullGLTexStorage2D;
         interface->fUniform1f = nullGLUniform1f;
diff --git a/src/gpu/gl/GrGLDefaultInterface_native.cpp b/src/gpu/gl/GrGLDefaultInterface_native.cpp
index 13988c0..e695f15 100644
--- a/src/gpu/gl/GrGLDefaultInterface_native.cpp
+++ b/src/gpu/gl/GrGLDefaultInterface_native.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
diff --git a/src/gpu/gl/GrGLDefaultInterface_none.cpp b/src/gpu/gl/GrGLDefaultInterface_none.cpp
index 183c477..84c7f7c 100644
--- a/src/gpu/gl/GrGLDefaultInterface_none.cpp
+++ b/src/gpu/gl/GrGLDefaultInterface_none.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
diff --git a/src/gpu/gl/GrGLDefines.h b/src/gpu/gl/GrGLDefines.h
new file mode 100644
index 0000000..852c025
--- /dev/null
+++ b/src/gpu/gl/GrGLDefines.h
@@ -0,0 +1,872 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+
+#ifndef GrGLDefines_DEFINED
+#define GrGLDefines_DEFINED
+
+// The following constants consist of the intersection of GL constants
+// exported by GLES 1.0, GLES 2.0, and desktop GL required by the system.
+
+#define GR_GL_DEPTH_BUFFER_BIT               0x00000100
+#define GR_GL_STENCIL_BUFFER_BIT             0x00000400
+#define GR_GL_COLOR_BUFFER_BIT               0x00004000
+
+/* Boolean */
+#define GR_GL_FALSE                          0
+#define GR_GL_TRUE                           1
+
+/* BeginMode */
+#define GR_GL_POINTS                         0x0000
+#define GR_GL_LINES                          0x0001
+#define GR_GL_LINE_LOOP                      0x0002
+#define GR_GL_LINE_STRIP                     0x0003
+#define GR_GL_TRIANGLES                      0x0004
+#define GR_GL_TRIANGLE_STRIP                 0x0005
+#define GR_GL_TRIANGLE_FAN                   0x0006
+
+/* AlphaFunction (not supported in ES20) */
+/*      GL_NEVER */
+/*      GL_LESS */
+/*      GL_EQUAL */
+/*      GL_LEQUAL */
+/*      GL_GREATER */
+/*      GL_NOTEQUAL */
+/*      GL_GEQUAL */
+/*      GL_ALWAYS */
+
+/* BlendingFactorDest */
+#define GR_GL_ZERO                           0
+#define GR_GL_ONE                            1
+#define GR_GL_SRC_COLOR                      0x0300
+#define GR_GL_ONE_MINUS_SRC_COLOR            0x0301
+#define GR_GL_SRC_ALPHA                      0x0302
+#define GR_GL_ONE_MINUS_SRC_ALPHA            0x0303
+#define GR_GL_DST_ALPHA                      0x0304
+#define GR_GL_ONE_MINUS_DST_ALPHA            0x0305
+
+/* BlendingFactorSrc */
+/*      GL_ZERO */
+/*      GL_ONE */
+#define GR_GL_DST_COLOR                      0x0306
+#define GR_GL_ONE_MINUS_DST_COLOR            0x0307
+#define GR_GL_SRC_ALPHA_SATURATE             0x0308
+/*      GL_SRC_ALPHA */
+/*      GL_ONE_MINUS_SRC_ALPHA */
+/*      GL_DST_ALPHA */
+/*      GL_ONE_MINUS_DST_ALPHA */
+
+/* ExtendedBlendFactors */
+#define GR_GL_SRC1_COLOR                     0x88F9
+#define GR_GL_ONE_MINUS_SRC1_COLOR           0x88FA
+/*      GL_SRC1_ALPHA */
+#define GR_GL_ONE_MINUS_SRC1_ALPHA           0x88FB
+
+/* Separate Blend Functions */
+#define GR_GL_BLEND_DST_RGB                  0x80C8
+#define GR_GL_BLEND_SRC_RGB                  0x80C9
+#define GR_GL_BLEND_DST_ALPHA                0x80CA
+#define GR_GL_BLEND_SRC_ALPHA                0x80CB
+#define GR_GL_CONSTANT_COLOR                 0x8001
+#define GR_GL_ONE_MINUS_CONSTANT_COLOR       0x8002
+#define GR_GL_CONSTANT_ALPHA                 0x8003
+#define GR_GL_ONE_MINUS_CONSTANT_ALPHA       0x8004
+#define GR_GL_BLEND_COLOR                    0x8005
+
+/* Buffer Objects */
+#define GR_GL_ARRAY_BUFFER                   0x8892
+#define GR_GL_ELEMENT_ARRAY_BUFFER           0x8893
+#define GR_GL_ARRAY_BUFFER_BINDING           0x8894
+#define GR_GL_ELEMENT_ARRAY_BUFFER_BINDING   0x8895
+
+#define GR_GL_STREAM_DRAW                    0x88E0
+#define GR_GL_STATIC_DRAW                    0x88E4
+#define GR_GL_DYNAMIC_DRAW                   0x88E8
+
+#define GR_GL_BUFFER_SIZE                    0x8764
+#define GR_GL_BUFFER_USAGE                   0x8765
+
+#define GR_GL_CURRENT_VERTEX_ATTRIB          0x8626
+
+/* CullFaceMode */
+#define GR_GL_FRONT                          0x0404
+#define GR_GL_BACK                           0x0405
+#define GR_GL_FRONT_AND_BACK                 0x0408
+
+/* DepthFunction */
+/*      GL_NEVER */
+/*      GL_LESS */
+/*      GL_EQUAL */
+/*      GL_LEQUAL */
+/*      GL_GREATER */
+/*      GL_NOTEQUAL */
+/*      GL_GEQUAL */
+/*      GL_ALWAYS */
+
+/* EnableCap */
+#define GR_GL_TEXTURE_2D                     0x0DE1
+#define GR_GL_CULL_FACE                      0x0B44
+#define GR_GL_BLEND                          0x0BE2
+#define GR_GL_DITHER                         0x0BD0
+#define GR_GL_STENCIL_TEST                   0x0B90
+#define GR_GL_DEPTH_TEST                     0x0B71
+#define GR_GL_SCISSOR_TEST                   0x0C11
+#define GR_GL_POLYGON_OFFSET_FILL            0x8037
+#define GR_GL_SAMPLE_ALPHA_TO_COVERAGE       0x809E
+#define GR_GL_SAMPLE_COVERAGE                0x80A0
+#define GR_GL_POLYGON_OFFSET_FILL            0x8037
+#define GR_GL_POLYGON_SMOOTH                 0x0B41
+#define GR_GL_POLYGON_STIPPLE                0x0B42
+#define GR_GL_COLOR_LOGIC_OP                 0x0BF2
+#define GR_GL_COLOR_TABLE                    0x80D0
+#define GR_GL_INDEX_LOGIC_OP                 0x0BF1
+#define GR_GL_VERTEX_PROGRAM_POINT_SIZE      0x8642
+#define GR_GL_LINE_STIPPLE                   0x0B24
+
+/* ErrorCode */
+#define GR_GL_NO_ERROR                       0
+#define GR_GL_INVALID_ENUM                   0x0500
+#define GR_GL_INVALID_VALUE                  0x0501
+#define GR_GL_INVALID_OPERATION              0x0502
+#define GR_GL_OUT_OF_MEMORY                  0x0505
+#define GR_GL_CONTEXT_LOST                   0x300E  // TODO(gman): What value?
+
+/* FrontFaceDirection */
+#define GR_GL_CW                             0x0900
+#define GR_GL_CCW                            0x0901
+
+/* GetPName */
+#define GR_GL_LINE_WIDTH                     0x0B21
+#define GR_GL_ALIASED_POINT_SIZE_RANGE       0x846D
+#define GR_GL_ALIASED_LINE_WIDTH_RANGE       0x846E
+#define GR_GL_CULL_FACE_MODE                 0x0B45
+#define GR_GL_FRONT_FACE                     0x0B46
+#define GR_GL_DEPTH_RANGE                    0x0B70
+#define GR_GL_DEPTH_WRITEMASK                0x0B72
+#define GR_GL_DEPTH_CLEAR_VALUE              0x0B73
+#define GR_GL_DEPTH_FUNC                     0x0B74
+#define GR_GL_STENCIL_CLEAR_VALUE            0x0B91
+#define GR_GL_STENCIL_FUNC                   0x0B92
+#define GR_GL_STENCIL_FAIL                   0x0B94
+#define GR_GL_STENCIL_PASS_DEPTH_FAIL        0x0B95
+#define GR_GL_STENCIL_PASS_DEPTH_PASS        0x0B96
+#define GR_GL_STENCIL_REF                    0x0B97
+#define GR_GL_STENCIL_VALUE_MASK             0x0B93
+#define GR_GL_STENCIL_WRITEMASK              0x0B98
+#define GR_GL_STENCIL_BACK_FUNC              0x8800
+#define GR_GL_STENCIL_BACK_FAIL              0x8801
+#define GR_GL_STENCIL_BACK_PASS_DEPTH_FAIL   0x8802
+#define GR_GL_STENCIL_BACK_PASS_DEPTH_PASS   0x8803
+#define GR_GL_STENCIL_BACK_REF               0x8CA3
+#define GR_GL_STENCIL_BACK_VALUE_MASK        0x8CA4
+#define GR_GL_STENCIL_BACK_WRITEMASK         0x8CA5
+#define GR_GL_VIEWPORT                       0x0BA2
+#define GR_GL_SCISSOR_BOX                    0x0C10
+/*      GL_SCISSOR_TEST */
+#define GR_GL_COLOR_CLEAR_VALUE              0x0C22
+#define GR_GL_COLOR_WRITEMASK                0x0C23
+#define GR_GL_UNPACK_ALIGNMENT               0x0CF5
+#define GR_GL_UNPACK_FLIP_Y                  0x9240
+#define GR_GL_PACK_ALIGNMENT                 0x0D05
+#define GR_GL_PACK_REVERSE_ROW_ORDER         0x93A4
+#define GR_GL_MAX_TEXTURE_SIZE               0x0D33
+#define GR_GL_MAX_VIEWPORT_DIMS              0x0D3A
+#define GR_GL_SUBPIXEL_BITS                  0x0D50
+#define GR_GL_RED_BITS                       0x0D52
+#define GR_GL_GREEN_BITS                     0x0D53
+#define GR_GL_BLUE_BITS                      0x0D54
+#define GR_GL_ALPHA_BITS                     0x0D55
+#define GR_GL_DEPTH_BITS                     0x0D56
+#define GR_GL_STENCIL_BITS                   0x0D57
+#define GR_GL_POLYGON_OFFSET_UNITS           0x2A00
+/*      GL_POLYGON_OFFSET_FILL */
+#define GR_GL_POLYGON_OFFSET_FACTOR          0x8038
+#define GR_GL_TEXTURE_BINDING_2D             0x8069
+#define GR_GL_SAMPLE_BUFFERS                 0x80A8
+#define GR_GL_SAMPLES                        0x80A9
+#define GR_GL_SAMPLE_COVERAGE_VALUE          0x80AA
+#define GR_GL_SAMPLE_COVERAGE_INVERT         0x80AB
+#define GR_GL_RENDERBUFFER_COVERAGE_SAMPLES  0x8CAB
+#define GR_GL_RENDERBUFFER_COLOR_SAMPLES     0x8E10
+#define GR_GL_MAX_MULTISAMPLE_COVERAGE_MODES 0x8E11
+#define GR_GL_MULTISAMPLE_COVERAGE_MODES     0x8E12
+
+/* GetTextureParameter */
+/*      GL_TEXTURE_MAG_FILTER */
+/*      GL_TEXTURE_MIN_FILTER */
+/*      GL_TEXTURE_WRAP_S */
+/*      GL_TEXTURE_WRAP_T */
+
+#define GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS 0x86A2
+#define GR_GL_COMPRESSED_TEXTURE_FORMATS     0x86A3
+
+/* HintMode */
+#define GR_GL_DONT_CARE                      0x1100
+#define GR_GL_FASTEST                        0x1101
+#define GR_GL_NICEST                         0x1102
+
+/* HintTarget */
+#define GR_GL_GENERATE_MIPMAP_HINT            0x8192
+
+/* DataType */
+#define GR_GL_BYTE                           0x1400
+#define GR_GL_UNSIGNED_BYTE                  0x1401
+#define GR_GL_SHORT                          0x1402
+#define GR_GL_UNSIGNED_SHORT                 0x1403
+#define GR_GL_INT                            0x1404
+#define GR_GL_UNSIGNED_INT                   0x1405
+#define GR_GL_FLOAT                          0x1406
+#define GR_GL_FIXED                          0x140C
+
+/* Lighting */
+#define GR_GL_LIGHTING                       0x0B50
+#define GR_GL_LIGHT0                         0x4000
+#define GR_GL_LIGHT1                         0x4001
+#define GR_GL_LIGHT2                         0x4002
+#define GR_GL_LIGHT3                         0x4003
+#define GR_GL_LIGHT4                         0x4004
+#define GR_GL_LIGHT5                         0x4005
+#define GR_GL_LIGHT6                         0x4006
+#define GR_GL_LIGHT7                         0x4007
+#define GR_GL_SPOT_EXPONENT                  0x1205
+#define GR_GL_SPOT_CUTOFF                    0x1206
+#define GR_GL_CONSTANT_ATTENUATION           0x1207
+#define GR_GL_LINEAR_ATTENUATION             0x1208
+#define GR_GL_QUADRATIC_ATTENUATION          0x1209
+#define GR_GL_AMBIENT                        0x1200
+#define GR_GL_DIFFUSE                        0x1201
+#define GR_GL_SPECULAR                       0x1202
+#define GR_GL_SHININESS                      0x1601
+#define GR_GL_EMISSION                       0x1600
+#define GR_GL_POSITION                       0x1203
+#define GR_GL_SPOT_DIRECTION                 0x1204
+#define GR_GL_AMBIENT_AND_DIFFUSE            0x1602
+#define GR_GL_COLOR_INDEXES                  0x1603
+#define GR_GL_LIGHT_MODEL_TWO_SIDE           0x0B52
+#define GR_GL_LIGHT_MODEL_LOCAL_VIEWER       0x0B51
+#define GR_GL_LIGHT_MODEL_AMBIENT            0x0B53
+#define GR_GL_FRONT_AND_BACK                 0x0408
+#define GR_GL_SHADE_MODEL                    0x0B54
+#define GR_GL_FLAT                           0x1D00
+#define GR_GL_SMOOTH                         0x1D01
+#define GR_GL_COLOR_MATERIAL                 0x0B57
+#define GR_GL_COLOR_MATERIAL_FACE            0x0B55
+#define GR_GL_COLOR_MATERIAL_PARAMETER       0x0B56
+#define GR_GL_NORMALIZE                      0x0BA1
+
+/* Matrix Mode */
+#define GR_GL_MATRIX_MODE                    0x0BA0
+#define GR_GL_MODELVIEW                      0x1700
+#define GR_GL_PROJECTION                     0x1701
+#define GR_GL_TEXTURE                        0x1702
+
+/* multisample */
+#define GR_GL_MULTISAMPLE                    0x809D
+
+/* Points */
+#define GR_GL_POINT_SMOOTH                   0x0B10
+#define GR_GL_POINT_SIZE                     0x0B11
+#define GR_GL_POINT_SIZE_GRANULARITY         0x0B13
+#define GR_GL_POINT_SIZE_RANGE               0x0B12
+
+/* Lines */
+#define GR_GL_LINE_SMOOTH                    0x0B20
+#define GR_GL_LINE_STIPPLE                   0x0B24
+#define GR_GL_LINE_STIPPLE_PATTERN           0x0B25
+#define GR_GL_LINE_STIPPLE_REPEAT            0x0B26
+#define GR_GL_LINE_WIDTH                     0x0B21
+#define GR_GL_LINE_WIDTH_GRANULARITY         0x0B23
+#define GR_GL_LINE_WIDTH_RANGE               0x0B22
+
+/* PixelFormat */
+#define GR_GL_DEPTH_COMPONENT                0x1902
+#define GR_GL_RED                            0x1903
+#define GR_GL_GREEN                          0x1904
+#define GR_GL_BLUE                           0x1905
+#define GR_GL_ALPHA                          0x1906
+#define GR_GL_RGB                            0x1907
+#define GR_GL_RGBA                           0x1908
+#define GR_GL_BGRA                           0x80E1
+#define GR_GL_LUMINANCE                      0x1909
+#define GR_GL_LUMINANCE_ALPHA                0x190A
+#define GR_GL_PALETTE8_RGBA8                 0x8B96
+#define GR_GL_ALPHA8                         0x803C
+
+#define GR_GL_R8                             0x8229
+
+/* PixelType */
+/*      GL_UNSIGNED_BYTE */
+#define GR_GL_UNSIGNED_SHORT_4_4_4_4         0x8033
+#define GR_GL_UNSIGNED_SHORT_5_5_5_1         0x8034
+#define GR_GL_UNSIGNED_SHORT_5_6_5           0x8363
+
+/* Shaders */
+#define GR_GL_FRAGMENT_SHADER                  0x8B30
+#define GR_GL_VERTEX_SHADER                    0x8B31
+#define GR_GL_GEOMETRY_SHADER                  0x8DD9
+#define GR_GL_MAX_VERTEX_ATTRIBS               0x8869
+#define GR_GL_MAX_VERTEX_UNIFORM_VECTORS       0x8DFB
+#define GR_GL_MAX_VARYING_VECTORS              0x8DFC
+#define GR_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS 0x8B4D
+#define GR_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS   0x8B4C
+#define GR_GL_MAX_TEXTURE_IMAGE_UNITS          0x8872
+#define GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS     0x8DFD
+#define GR_GL_SHADER_TYPE                      0x8B4F
+#define GR_GL_DELETE_STATUS                    0x8B80
+#define GR_GL_LINK_STATUS                      0x8B82
+#define GR_GL_VALIDATE_STATUS                  0x8B83
+#define GR_GL_ATTACHED_SHADERS                 0x8B85
+#define GR_GL_ACTIVE_UNIFORMS                  0x8B86
+#define GR_GL_ACTIVE_UNIFORM_MAX_LENGTH        0x8B87
+#define GR_GL_ACTIVE_ATTRIBUTES                0x8B89
+#define GR_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH      0x8B8A
+#define GR_GL_SHADING_LANGUAGE_VERSION         0x8B8C
+#define GR_GL_CURRENT_PROGRAM                  0x8B8D
+#define GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS  0x8B49
+#define GR_GL_MAX_VERTEX_UNIFORM_COMPONENTS    0x8B4A
+
+/* StencilFunction */
+#define GR_GL_NEVER                          0x0200
+#define GR_GL_LESS                           0x0201
+#define GR_GL_EQUAL                          0x0202
+#define GR_GL_LEQUAL                         0x0203
+#define GR_GL_GREATER                        0x0204
+#define GR_GL_NOTEQUAL                       0x0205
+#define GR_GL_GEQUAL                         0x0206
+#define GR_GL_ALWAYS                         0x0207
+
+/* StencilOp */
+/*      GL_ZERO */
+#define GR_GL_KEEP                           0x1E00
+#define GR_GL_REPLACE                        0x1E01
+#define GR_GL_INCR                           0x1E02
+#define GR_GL_DECR                           0x1E03
+#define GR_GL_INVERT                         0x150A
+#define GR_GL_INCR_WRAP                      0x8507
+#define GR_GL_DECR_WRAP                      0x8508
+
+/* StringName */
+#define GR_GL_VENDOR                         0x1F00
+#define GR_GL_RENDERER                       0x1F01
+#define GR_GL_VERSION                        0x1F02
+#define GR_GL_EXTENSIONS                     0x1F03
+
+/* Pixel Mode / Transfer */
+#define GR_GL_UNPACK_ROW_LENGTH              0x0CF2
+#define GR_GL_PACK_ROW_LENGTH                0x0D02
+
+
+/* TextureMagFilter */
+#define GR_GL_NEAREST                        0x2600
+#define GR_GL_LINEAR                         0x2601
+
+/* TextureMinFilter */
+/*      GL_NEAREST */
+/*      GL_LINEAR */
+#define GR_GL_NEAREST_MIPMAP_NEAREST         0x2700
+#define GR_GL_LINEAR_MIPMAP_NEAREST          0x2701
+#define GR_GL_NEAREST_MIPMAP_LINEAR          0x2702
+#define GR_GL_LINEAR_MIPMAP_LINEAR           0x2703
+
+/* TextureUsage */
+#define GR_GL_FRAMEBUFFER_ATTACHMENT         0x93A3
+
+/* TextureParameterName */
+#define GR_GL_TEXTURE_MAG_FILTER             0x2800
+#define GR_GL_TEXTURE_MIN_FILTER             0x2801
+#define GR_GL_TEXTURE_WRAP_S                 0x2802
+#define GR_GL_TEXTURE_WRAP_T                 0x2803
+#define GR_GL_TEXTURE_USAGE                  0x93A2
+
+/* TextureTarget */
+/*      GL_TEXTURE_2D */
+#define GR_GL_TEXTURE                        0x1702
+#define GR_GL_TEXTURE_CUBE_MAP               0x8513
+#define GR_GL_TEXTURE_BINDING_CUBE_MAP       0x8514
+#define GR_GL_TEXTURE_CUBE_MAP_POSITIVE_X    0x8515
+#define GR_GL_TEXTURE_CUBE_MAP_NEGATIVE_X    0x8516
+#define GR_GL_TEXTURE_CUBE_MAP_POSITIVE_Y    0x8517
+#define GR_GL_TEXTURE_CUBE_MAP_NEGATIVE_Y    0x8518
+#define GR_GL_TEXTURE_CUBE_MAP_POSITIVE_Z    0x8519
+#define GR_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z    0x851A
+#define GR_GL_MAX_CUBE_MAP_TEXTURE_SIZE      0x851C
+
+/* TextureUnit */
+#define GR_GL_TEXTURE0                       0x84C0
+#define GR_GL_TEXTURE1                       0x84C1
+#define GR_GL_TEXTURE2                       0x84C2
+#define GR_GL_TEXTURE3                       0x84C3
+#define GR_GL_TEXTURE4                       0x84C4
+#define GR_GL_TEXTURE5                       0x84C5
+#define GR_GL_TEXTURE6                       0x84C6
+#define GR_GL_TEXTURE7                       0x84C7
+#define GR_GL_TEXTURE8                       0x84C8
+#define GR_GL_TEXTURE9                       0x84C9
+#define GR_GL_TEXTURE10                      0x84CA
+#define GR_GL_TEXTURE11                      0x84CB
+#define GR_GL_TEXTURE12                      0x84CC
+#define GR_GL_TEXTURE13                      0x84CD
+#define GR_GL_TEXTURE14                      0x84CE
+#define GR_GL_TEXTURE15                      0x84CF
+#define GR_GL_TEXTURE16                      0x84D0
+#define GR_GL_TEXTURE17                      0x84D1
+#define GR_GL_TEXTURE18                      0x84D2
+#define GR_GL_TEXTURE19                      0x84D3
+#define GR_GL_TEXTURE20                      0x84D4
+#define GR_GL_TEXTURE21                      0x84D5
+#define GR_GL_TEXTURE22                      0x84D6
+#define GR_GL_TEXTURE23                      0x84D7
+#define GR_GL_TEXTURE24                      0x84D8
+#define GR_GL_TEXTURE25                      0x84D9
+#define GR_GL_TEXTURE26                      0x84DA
+#define GR_GL_TEXTURE27                      0x84DB
+#define GR_GL_TEXTURE28                      0x84DC
+#define GR_GL_TEXTURE29                      0x84DD
+#define GR_GL_TEXTURE30                      0x84DE
+#define GR_GL_TEXTURE31                      0x84DF
+#define GR_GL_ACTIVE_TEXTURE                 0x84E0
+#define GR_GL_MAX_TEXTURE_UNITS              0x84E2
+
+/* TextureWrapMode */
+#define GR_GL_REPEAT                         0x2901
+#define GR_GL_CLAMP_TO_EDGE                  0x812F
+#define GR_GL_MIRRORED_REPEAT                0x8370
+
+/* Texture Swizzle */
+#define GR_GL_TEXTURE_SWIZZLE_R              0x8E42
+#define GR_GL_TEXTURE_SWIZZLE_G              0x8E43
+#define GR_GL_TEXTURE_SWIZZLE_B              0x8E44
+#define GR_GL_TEXTURE_SWIZZLE_A              0x8E45
+#define GR_GL_TEXTURE_SWIZZLE_RGBA           0x8E46
+
+/* Texture mapping */
+#define GR_GL_TEXTURE_ENV                    0x2300
+#define GR_GL_TEXTURE_ENV_MODE               0x2200
+#define GR_GL_TEXTURE_1D                     0x0DE0
+/* GL_TEXTURE_2D */
+/* GL_TEXTURE_WRAP_S */
+/* GL_TEXTURE_WRAP_T */
+/* GL_TEXTURE_MAG_FILTER */
+/* GL_TEXTURE_MIN_FILTER */
+#define GR_GL_TEXTURE_ENV_COLOR             0x2201
+#define GR_GL_TEXTURE_GEN_S                 0x0C60
+#define GR_GL_TEXTURE_GEN_T                 0x0C61
+#define GR_GL_TEXTURE_GEN_MODE              0x2500
+#define GR_GL_TEXTURE_BORDER_COLOR          0x1004
+#define GR_GL_TEXTURE_WIDTH                 0x1000
+#define GR_GL_TEXTURE_HEIGHT                0x1001
+#define GR_GL_TEXTURE_BORDER                0x1005
+#define GR_GL_TEXTURE_COMPONENTS            0x1003
+#define GR_GL_TEXTURE_RED_SIZE              0x805C
+#define GR_GL_TEXTURE_GREEN_SIZE            0x805D
+#define GR_GL_TEXTURE_BLUE_SIZE             0x805E
+#define GR_GL_TEXTURE_ALPHA_SIZE            0x805F
+#define GR_GL_TEXTURE_LUMINANCE_SIZE        0x8060
+#define GR_GL_TEXTURE_INTENSITY_SIZE        0x8061
+#define GR_GL_TEXTURE_INTERNAL_FORMAT       0x1003
+/* GL_NEAREST_MIPMAP_NEAREST */
+/* GL_NEAREST_MIPMAP_LINEAR */
+/* GL_LINEAR_MIPMAP_NEAREST */
+/* GL_LINEAR_MIPMAP_LINEAR */
+#define GR_GL_OBJECT_LINEAR                 0x2401
+#define GR_GL_OBJECT_PLANE                  0x2501
+#define GR_GL_EYE_LINEAR                    0x2400
+#define GR_GL_EYE_PLANE                     0x2502
+#define GR_GL_SPHERE_MAP                    0x2402
+#define GR_GL_DECAL                         0x2101
+#define GR_GL_MODULATE                      0x2100
+/* GL_NEAREST */
+/* GL_REPEAT */
+#define GR_GL_CLAMP                         0x2900
+#define GR_GL_S                             0x2000
+#define GR_GL_T                             0x2001
+#define GR_GL_R                             0x2002
+#define GR_GL_Q                             0x2003
+#define GR_GL_TEXTURE_GEN_R                 0x0C62
+#define GR_GL_TEXTURE_GEN_Q                 0x0C63
+
+/* texture_env_combine */
+#define GR_GL_COMBINE                       0x8570
+#define GR_GL_COMBINE_RGB                   0x8571
+#define GR_GL_COMBINE_ALPHA                 0x8572
+#define GR_GL_SOURCE0_RGB                   0x8580
+#define GR_GL_SOURCE1_RGB                   0x8581
+#define GR_GL_SOURCE2_RGB                   0x8582
+#define GR_GL_SOURCE0_ALPHA                 0x8588
+#define GR_GL_SOURCE1_ALPHA                 0x8589
+#define GR_GL_SOURCE2_ALPHA                 0x858A
+#define GR_GL_OPERAND0_RGB                  0x8590
+#define GR_GL_OPERAND1_RGB                  0x8591
+#define GR_GL_OPERAND2_RGB                  0x8592
+#define GR_GL_OPERAND0_ALPHA                0x8598
+#define GR_GL_OPERAND1_ALPHA                0x8599
+#define GR_GL_OPERAND2_ALPHA                0x859A
+#define GR_GL_RGB_SCALE                     0x8573
+#define GR_GL_ADD_SIGNED                    0x8574
+#define GR_GL_INTERPOLATE                   0x8575
+#define GR_GL_SUBTRACT                      0x84E7
+#define GR_GL_CONSTANT                      0x8576
+#define GR_GL_PRIMARY_COLOR                 0x8577
+#define GR_GL_PREVIOUS                      0x8578
+#define GR_GL_SRC0_RGB                      0x8580
+#define GR_GL_SRC1_RGB                      0x8581
+#define GR_GL_SRC2_RGB                      0x8582
+#define GR_GL_SRC0_ALPHA                    0x8588
+#define GR_GL_SRC1_ALPHA                    0x8589
+#define GR_GL_SRC2_ALPHA                    0x858A
+
+/* Uniform Types */
+#define GR_GL_FLOAT_VEC2                     0x8B50
+#define GR_GL_FLOAT_VEC3                     0x8B51
+#define GR_GL_FLOAT_VEC4                     0x8B52
+#define GR_GL_INT_VEC2                       0x8B53
+#define GR_GL_INT_VEC3                       0x8B54
+#define GR_GL_INT_VEC4                       0x8B55
+#define GR_GL_BOOL                           0x8B56
+#define GR_GL_BOOL_VEC2                      0x8B57
+#define GR_GL_BOOL_VEC3                      0x8B58
+#define GR_GL_BOOL_VEC4                      0x8B59
+#define GR_GL_FLOAT_MAT2                     0x8B5A
+#define GR_GL_FLOAT_MAT3                     0x8B5B
+#define GR_GL_FLOAT_MAT4                     0x8B5C
+#define GR_GL_SAMPLER_2D                     0x8B5E
+#define GR_GL_SAMPLER_CUBE                   0x8B60
+
+/* Vertex Arrays */
+#define GR_GL_VERTEX_ATTRIB_ARRAY_ENABLED        0x8622
+#define GR_GL_VERTEX_ATTRIB_ARRAY_SIZE           0x8623
+#define GR_GL_VERTEX_ATTRIB_ARRAY_STRIDE         0x8624
+#define GR_GL_VERTEX_ATTRIB_ARRAY_TYPE           0x8625
+#define GR_GL_VERTEX_ATTRIB_ARRAY_NORMALIZED     0x886A
+#define GR_GL_VERTEX_ATTRIB_ARRAY_POINTER        0x8645
+#define GR_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING 0x889F
+#define GR_GL_VERTEX_ARRAY                       0x8074
+#define GR_GL_NORMAL_ARRAY                       0x8075
+#define GR_GL_COLOR_ARRAY                        0x8076
+#define GR_GL_INDEX_ARRAY                        0x8077
+#define GR_GL_TEXTURE_COORD_ARRAY                0x8078
+#define GR_GL_EDGE_FLAG_ARRAY                    0x8079
+#define GR_GL_VERTEX_ARRAY_SIZE                  0x807A
+#define GR_GL_VERTEX_ARRAY_TYPE                  0x807B
+#define GR_GL_VERTEX_ARRAY_STRIDE                0x807C
+#define GR_GL_NORMAL_ARRAY_TYPE                  0x807E
+#define GR_GL_NORMAL_ARRAY_STRIDE                0x807F
+#define GR_GL_COLOR_ARRAY_SIZE                   0x8081
+#define GR_GL_COLOR_ARRAY_TYPE                   0x8082
+#define GR_GL_COLOR_ARRAY_STRIDE                 0x8083
+#define GR_GL_INDEX_ARRAY_TYPE                   0x8085
+#define GR_GL_INDEX_ARRAY_STRIDE                 0x8086
+#define GR_GL_TEXTURE_COORD_ARRAY_SIZE           0x8088
+#define GR_GL_TEXTURE_COORD_ARRAY_TYPE           0x8089
+#define GR_GL_TEXTURE_COORD_ARRAY_STRIDE         0x808A
+#define GR_GL_EDGE_FLAG_ARRAY_STRIDE             0x808C
+#define GR_GL_VERTEX_ARRAY_POINTER               0x808E
+#define GR_GL_NORMAL_ARRAY_POINTER               0x808F
+#define GR_GL_COLOR_ARRAY_POINTER                0x8090
+#define GR_GL_INDEX_ARRAY_POINTER                0x8091
+#define GR_GL_TEXTURE_COORD_ARRAY_POINTER        0x8092
+#define GR_GL_EDGE_FLAG_ARRAY_POINTER            0x8093
+#define GR_GL_V2F                                0x2A20
+#define GR_GL_V3F                                0x2A21
+#define GR_GL_C4UB_V2F                           0x2A22
+#define GR_GL_C4UB_V3F                           0x2A23
+#define GR_GL_C3F_V3F                            0x2A24
+#define GR_GL_N3F_V3F                            0x2A25
+#define GR_GL_C4F_N3F_V3F                        0x2A26
+#define GR_GL_T2F_V3F                            0x2A27
+#define GR_GL_T4F_V4F                            0x2A28
+#define GR_GL_T2F_C4UB_V3F                       0x2A29
+#define GR_GL_T2F_C3F_V3F                        0x2A2A
+#define GR_GL_T2F_N3F_V3F                        0x2A2B
+#define GR_GL_T2F_C4F_N3F_V3F                    0x2A2C
+#define GR_GL_T4F_C4F_N3F_V4F                    0x2A2D
+
+/* Vertex Buffer Object */
+#define GR_GL_WRITE_ONLY                         0x88B9
+#define GR_GL_BUFFER_MAPPED                      0x88BC
+/* Read Format */
+#define GR_GL_IMPLEMENTATION_COLOR_READ_TYPE   0x8B9A
+#define GR_GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B
+
+/* Shader Source */
+#define GR_GL_COMPILE_STATUS                 0x8B81
+#define GR_GL_INFO_LOG_LENGTH                0x8B84
+#define GR_GL_SHADER_SOURCE_LENGTH           0x8B88
+#define GR_GL_SHADER_COMPILER                0x8DFA
+
+/* Shader Binary */
+#define GR_GL_SHADER_BINARY_FORMATS          0x8DF8
+#define GR_GL_NUM_SHADER_BINARY_FORMATS      0x8DF9
+
+/* Shader Precision-Specified Types */
+#define GR_GL_LOW_FLOAT                      0x8DF0
+#define GR_GL_MEDIUM_FLOAT                   0x8DF1
+#define GR_GL_HIGH_FLOAT                     0x8DF2
+#define GR_GL_LOW_INT                        0x8DF3
+#define GR_GL_MEDIUM_INT                     0x8DF4
+#define GR_GL_HIGH_INT                       0x8DF5
+
+/* Queries */
+#define GR_GL_QUERY_COUNTER_BITS             0x8864
+#define GR_GL_CURRENT_QUERY                  0x8865
+#define GR_GL_QUERY_RESULT                   0x8866
+#define GR_GL_QUERY_RESULT_AVAILABLE         0x8867
+#define GR_GL_SAMPLES_PASSED                 0x8914
+#define GR_GL_ANY_SAMPLES_PASSED             0x8C2F
+#define GR_GL_TIME_ELAPSED                   0x88BF
+#define GR_GL_TIMESTAMP                      0x8E28
+#define GR_GL_PRIMITIVES_GENERATED           0x8C87
+#define GR_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN 0x8C88
+
+
+/* Framebuffer Object. */
+#define GR_GL_FRAMEBUFFER                    0x8D40
+#define GR_GL_READ_FRAMEBUFFER               0x8CA8
+#define GR_GL_DRAW_FRAMEBUFFER               0x8CA9
+
+#define GR_GL_RENDERBUFFER                   0x8D41
+
+#define GR_GL_RGBA4                          0x8056
+#define GR_GL_RGB5_A1                        0x8057
+#define GR_GL_RGB565                         0x8D62
+#define GR_GL_RGBA8                          0x8058
+#define GR_GL_RGB8                           0x8051
+#define GR_GL_BGRA8                          0x93A1
+#define GR_GL_SRGB                           0x8C40
+#define GR_GL_SRGB8                          0x8C41
+#define GR_GL_SRGB_ALPHA                     0x8C42
+#define GR_GL_SRGB8_ALPHA8                   0x8C43
+#define GR_GL_DEPTH_COMPONENT16              0x81A5
+#define GR_GL_STENCIL_INDEX                  0x1901
+#define GR_GL_STENCIL_INDEX4                 0x8D47
+#define GR_GL_STENCIL_INDEX8                 0x8D48
+#define GR_GL_STENCIL_INDEX16                0x8D49
+#define GR_GL_DEPTH_STENCIL                  0x84F9
+#define GR_GL_DEPTH24_STENCIL8               0x88F0
+
+#define GR_GL_MAX_SAMPLES                    0x8D57
+
+#define GR_GL_RENDERBUFFER_WIDTH             0x8D42
+#define GR_GL_RENDERBUFFER_HEIGHT            0x8D43
+#define GR_GL_RENDERBUFFER_INTERNAL_FORMAT   0x8D44
+#define GR_GL_RENDERBUFFER_RED_SIZE          0x8D50
+#define GR_GL_RENDERBUFFER_GREEN_SIZE        0x8D51
+#define GR_GL_RENDERBUFFER_BLUE_SIZE         0x8D52
+#define GR_GL_RENDERBUFFER_ALPHA_SIZE        0x8D53
+#define GR_GL_RENDERBUFFER_DEPTH_SIZE        0x8D54
+#define GR_GL_RENDERBUFFER_STENCIL_SIZE      0x8D55
+
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE           0x8CD0
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME           0x8CD1
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LEVEL         0x8CD2
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_CUBE_MAP_FACE 0x8CD3
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_TEXTURE_LAYER         0x8CD4
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE              0x8212
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE            0x8213
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE             0x8214
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE            0x8215
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE            0x8216
+#define GR_GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE          0x8217
+
+#define GR_GL_COLOR_ATTACHMENT0              0x8CE0
+#define GR_GL_DEPTH_ATTACHMENT               0x8D00
+#define GR_GL_STENCIL_ATTACHMENT             0x8D20
+
+#define GR_GL_NONE                           0
+
+#define GR_GL_FRAMEBUFFER_COMPLETE                      0x8CD5
+#define GR_GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT         0x8CD6
+#define GR_GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
+#define GR_GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS         0x8CD9
+#define GR_GL_FRAMEBUFFER_UNSUPPORTED                   0x8CDD
+
+#define GR_GL_FRAMEBUFFER_BINDING            0x8CA6
+#define GR_GL_RENDERBUFFER_BINDING           0x8CA7
+#define GR_GL_MAX_RENDERBUFFER_SIZE          0x84E8
+
+#define GR_GL_INVALID_FRAMEBUFFER_OPERATION  0x0506
+
+/* Path Rendering */
+// commands
+#define GR_GL_CLOSE_PATH                                    0x00
+#define GR_GL_MOVE_TO                                       0x02
+#define GR_GL_RELATIVE_MOVE_TO                              0x03
+#define GR_GL_LINE_TO                                       0x04
+#define GR_GL_RELATIVE_LINE_TO                              0x05
+#define GR_GL_HORIZONTAL_LINE_TO                            0x06
+#define GR_GL_RELATIVE_HORIZONTAL_LINE_TO                   0x07
+#define GR_GL_VERTICAL_LINE_TO                              0x08
+#define GR_GL_RELATIVE_VERTICAL_LINE_TO                     0x09
+#define GR_GL_QUADRATIC_CURVE_TO                            0x0A
+#define GR_GL_RELATIVE_QUADRATIC_CURVE_TO                   0x0B
+#define GR_GL_CUBIC_CURVE_TO                                0x0C
+#define GR_GL_RELATIVE_CUBIC_CURVE_TO                       0x0D
+#define GR_GL_SMOOTH_QUADRATIC_CURVE_TO                     0x0E
+#define GR_GL_RELATIVE_SMOOTH_QUADRATIC_CURVE_TO            0x0F
+#define GR_GL_SMOOTH_CUBIC_CURVE_TO                         0x10
+#define GR_GL_RELATIVE_SMOOTH_CUBIC_CURVE_TO                0x11
+#define GR_GL_SMALL_CCW_ARC_TO                              0x12
+#define GR_GL_RELATIVE_SMALL_CCW_ARC_TO                     0x13
+#define GR_GL_SMALL_CW_ARC_TO                               0x14
+#define GR_GL_RELATIVE_SMALL_CW_ARC_TO                      0x15
+#define GR_GL_LARGE_CCW_ARC_TO                              0x16
+#define GR_GL_RELATIVE_LARGE_CCW_ARC_TO                     0x17
+#define GR_GL_LARGE_CW_ARC_TO                               0x18
+#define GR_GL_RELATIVE_LARGE_CW_ARC_TO                      0x19
+#define GR_GL_CIRCULAR_CCW_ARC_TO                           0xF8
+#define GR_GL_CIRCULAR_CW_ARC_TO                            0xFA
+#define GR_GL_CIRCULAR_TANGENT_ARC_TO                       0xFC
+#define GR_GL_ARC_TO                                        0xFE
+#define GR_GL_RELATIVE_ARC_TO                               0xFF
+
+// path string formats
+#define GR_GL_PATH_FORMAT_SVG                               0x9070
+#define GR_GL_PATH_FORMAT_PS                                0x9071
+
+// font targets
+#define GR_GL_STANDARD_FONT_NAME                            0x9072
+#define GR_GL_SYSTEM_FONT_NAME                              0x9073
+#define GR_GL_FILE_NAME                                     0x9074
+
+// handle missing glyphs
+#define GR_GL_SKIP_MISSING_GLYPH                            0x90A9
+#define GR_GL_USE_MISSING_GLYPH                             0x90AA
+
+// path parameters
+#define GR_GL_PATH_STROKE_WIDTH                             0x9075
+#define GR_GL_PATH_INITIAL_END_CAP                          0x9077
+#define GR_GL_PATH_TERMINAL_END_CAP                         0x9078
+#define GR_GL_PATH_JOIN_STYLE                               0x9079
+#define GR_GL_PATH_MITER_LIMIT                              0x907A
+#define GR_GL_PATH_INITIAL_DASH_CAP                         0x907C
+#define GR_GL_PATH_TERMINAL_DASH_CAP                        0x907D
+#define GR_GL_PATH_DASH_OFFSET                              0x907E
+#define GR_GL_PATH_CLIENT_LENGTH                            0x907F
+#define GR_GL_PATH_DASH_OFFSET_RESET                        0x90B4
+#define GR_GL_PATH_FILL_MODE                                0x9080
+#define GR_GL_PATH_FILL_MASK                                0x9081
+#define GR_GL_PATH_FILL_COVER_MODE                          0x9082
+#define GR_GL_PATH_STROKE_COVER_MODE                        0x9083
+#define GR_GL_PATH_STROKE_MASK                              0x9084
+#define GR_GL_PATH_END_CAPS                                 0x9076
+#define GR_GL_PATH_DASH_CAPS                                0x907B
+#define GR_GL_PATH_COMMAND_COUNT                            0x909D
+#define GR_GL_PATH_COORD_COUNT                              0x909E
+#define GR_GL_PATH_DASH_ARRAY_COUNT                         0x909F
+#define GR_GL_PATH_FILL_BOUNDING_BOX                        0x90A1
+#define GR_GL_PATH_STROKE_BOUNDING_BOX                      0x90A2
+
+// fill modes
+/*      GL_INVERT */
+#define GR_GL_COUNT_UP                                      0x9088
+#define GR_GL_COUNT_DOWN                                    0x9089
+/*      GL_PATH_FILL_MODE_NV */
+
+// path color gen
+/*      GL_PRIMARY_COLOR */
+#define GR_GL_SECONDARY_COLOR                               0x852D
+
+// gen mode
+/*      GL_NONE */
+/*      GL_EYE_LINEAR */
+/*      GL_OBJECT_LINEAR */
+#define GR_GL_PATH_OBJECT_BOUNDING_BOX                      0x908A
+
+// cover mode
+#define GR_GL_CONVEX_HULL                                   0x908B
+#define GR_GL_BOUNDING_BOX                                  0x908D
+#define GR_GL_BOUNDING_BOX_OF_BOUNDING_BOXES                0x909C
+/*      GL_PATH_FILL_COVER_MODE_NV */
+
+// transform type
+/*      GL_NONE */
+#define GR_GL_TRANSLATE_X                                   0x908E
+#define GR_GL_TRANSLATE_Y                                   0x908F
+#define GR_GL_TRANSLATE_2D                                  0x9090
+#define GR_GL_TRANSLATE_3D                                  0x9091
+#define GR_GL_AFFINE_2D                                     0x9092
+#define GR_GL_AFFINE_3D                                     0x9094
+#define GR_GL_TRANSPOSE_AFFINE_2D                           0x9096
+#define GR_GL_TRANSPOSE_AFFINE_3D                           0x9098
+
+// path string types
+#define GR_GL_UTF8                                          0x909A
+#define GR_GL_UTF16                                         0x909B
+
+#define GR_GL_PATH_COMPUTED_LENGTH                          0x90A0
+
+// cap/dash values
+/*      GL_FLAT */
+#define GR_GL_SQUARE                                        0x90A3
+#define GR_GL_ROUND                                         0x90A4
+#define GR_GL_TRIANGULAR                                    0x90A5
+
+// join values
+/*      GL_NONE */
+/*      GL_ROUND_NV  */
+#define GR_GL_BEVEL                                         0x90A6
+#define GR_GL_MITER_REVERT                                  0x90A7
+#define GR_GL_MITER_TRUNCATE                                0x90A8
+
+// path dash reset values
+#define GR_GL_MOVE_TO_RESETS                                0x90B5
+#define GR_GL_MOVE_TO_CONTINUES                             0x90B6
+
+// font styles
+/*      GL_NONE */
+#define GR_GL_BOLD_BIT                                      0x01
+#define GR_GL_ITALIC_BIT                                    0x02
+
+// pnames for glGet
+#define GR_GL_PATH_ERROR_POSITION                           0x90AB
+#define GR_GL_PATH_FOG_GEN_MODE                             0x90AC
+#define GR_GL_PATH_STENCIL_FUNC                             0x90B7
+#define GR_GL_PATH_STENCIL_REF                              0x90B8
+#define GR_GL_PATH_STENCIL_VALUE_MASK                       0x90B9
+#define GR_GL_PATH_STENCIL_DEPTH_OFFSET_FACTOR              0x90BD
+#define GR_GL_PATH_STENCIL_DEPTH_OFFSET_UNITS               0x90BE
+#define GR_GL_PATH_COVER_DEPTH_FUNC                         0x90BF
+
+// per-glyph metrics bits in metric mask query
+#define GR_GL_GLYPH_WIDTH_BIT                               0x01
+#define GR_GL_GLYPH_HEIGHT_BIT                              0x02
+#define GR_GL_GLYPH_HORIZONTAL_BEARING_X_BIT                0x04
+#define GR_GL_GLYPH_HORIZONTAL_BEARING_Y_BIT                0x08
+#define GR_GL_GLYPH_HORIZONTAL_BEARING_ADVANCE_BIT          0x10
+#define GR_GL_GLYPH_VERTICAL_BEARING_X_BIT                  0x20
+#define GR_GL_GLYPH_VERTICAL_BEARING_Y_BIT                  0x40
+#define GR_GL_GLYPH_VERTICAL_BEARING_ADVANCE_BIT            0x80
+#define GR_GL_GLYPH_HAS_KERNING                             0x100
+
+// per-font face metrics in metric mask query
+#define GR_GL_FONT_X_MIN_BOUNDS                             0x00010000
+#define GR_GL_FONT_Y_MIN_BOUNDS                             0x00020000
+#define GR_GL_FONT_X_MAX_BOUNDS                             0x00040000
+#define GR_GL_FONT_Y_MAX_BOUNDS                             0x00080000
+#define GR_GL_FONT_UNITS_PER_EM                             0x00100000
+#define GR_GL_FONT_ASCENDER                                 0x00200000
+#define GR_GL_FONT_DESCENDER                                0x00400000
+#define GR_GL_FONT_HEIGHT                                   0x00800000
+#define GR_GL_FONT_MAX_ADVANCE_WIDTH                        0x01000000
+#define GR_GL_FONT_MAX_ADVANCE_HEIGHT                       0x02000000
+#define GR_GL_FONT_UNDERLINE_POSITION                       0x04000000
+#define GR_GL_FONT_UNDERLINE_THICKNESS                      0x08000000
+#define GR_GL_FONT_HAS_KERNING                              0x10000000
+
+// path list modes (glGetPathSpacing)
+#define GR_GL_ACCUM_ADJACENT_PAIRS                          0x90AD
+#define GR_GL_ADJACENT_PAIRS                                0x90AE
+#define GR_GL_FIRST_TO_REST                                 0x90AF
+
+//path gen modes
+#define GR_GL_PATH_GEN_MODE                                 0x90B0
+#define GR_GL_PATH_GEN_COEFF                                0x90B1
+#define GR_GL_PATH_GEN_COLOR_FORMAT                         0x90B2
+#define GR_GL_PATH_GEN_COMPONENTS                           0x90B3
+
+#endif
diff --git a/src/gpu/gl/GrGLEffect.cpp b/src/gpu/gl/GrGLEffect.cpp
new file mode 100644
index 0000000..ccea269
--- /dev/null
+++ b/src/gpu/gl/GrGLEffect.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "GrGLSL.h"
+#include "GrGLEffect.h"
+
+GrGLEffect::GrGLEffect(const GrBackendEffectFactory& factory)
+    : fFactory(factory) {
+}
+
+GrGLEffect::~GrGLEffect() {
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLEffect::setData(const GrGLUniformManager&, const GrEffectStage&) {
+}
+
+GrGLEffect::EffectKey GrGLEffect::GenTextureKey(const GrEffectRef* effect,
+                                                const GrGLCaps& caps) {
+    EffectKey key = 0;
+    for (int index = 0; index < (*effect)->numTextures(); ++index) {
+        const GrTextureAccess& access = (*effect)->textureAccess(index);
+        EffectKey value = GrGLShaderBuilder::KeyForTextureAccess(access, caps) << index;
+        GrAssert(0 == (value & key)); // keys for each access ought not to overlap
+        key |= value;
+    }
+    return key;
+}
diff --git a/src/gpu/gl/GrGLEffect.h b/src/gpu/gl/GrGLEffect.h
new file mode 100644
index 0000000..76f865e
--- /dev/null
+++ b/src/gpu/gl/GrGLEffect.h
@@ -0,0 +1,116 @@
+/*
+ * 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 GrGLEffect_DEFINED
+#define GrGLEffect_DEFINED
+
+#include "GrBackendEffectFactory.h"
+#include "GrGLShaderBuilder.h"
+#include "GrGLShaderVar.h"
+#include "GrGLSL.h"
+#include "GrEffectStage.h"
+
+class GrGLTexture;
+
+/** @file
+    This file contains specializations for OpenGL of the shader stages declared in
+    include/gpu/GrEffect.h. Objects of type GrGLEffect are responsible for emitting the
+    GLSL code that implements a GrEffect and for uploading uniforms at draw time. They also
+    must have a function:
+        static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&)
+    that is used to implement a program cache. When two GrEffects produce the same key this means
+    that their GrGLEffects would emit the same GLSL code.
+
+    These objects are created by the factory object returned by the GrEffect::getFactory().
+*/
+
+class GrGLEffect {
+
+public:
+    typedef GrBackendEffectFactory::EffectKey EffectKey;
+
+    enum {
+        kNoEffectKey = GrBackendEffectFactory::kNoEffectKey,
+        // the number of bits in EffectKey available to GenKey
+        kEffectKeyBits = GrBackendEffectFactory::kEffectKeyBits,
+    };
+
+    typedef GrGLShaderBuilder::TextureSamplerArray TextureSamplerArray;
+
+    GrGLEffect(const GrBackendEffectFactory&);
+
+    virtual ~GrGLEffect();
+
+    /** Called when the program stage should insert its code into the shaders. The code in each
+        shader will be in its own block ({}) and so locally scoped names will not collide across
+        stages.
+
+        @param builder      Interface used to emit code in the shaders.
+        @param stage        The effect stage that generated this program stage.
+        @param key          The key that was computed by GenKey() from the generating GrEffect.
+                            Only the bits indicated by GrBackendEffectFactory::kEffectKeyBits are
+                            guaranteed to match the value produced by GenKey();
+        @param vertexCoords A vec2 in the VS that holds the position in local coords. This is either
+                            the pre-view-matrix vertex position or if explicit per-vertex texture
+                            coords are used with a stage then it is those coordinates. See
+                            GrVertexLayout.
+        @param outputColor  A predefined vec4 in the FS in which the stage should place its output
+                            color (or coverage).
+        @param inputColor   A vec4 that holds the input color to the stage in the FS. This may be
+                            NULL in which case the implied input is solid white (all ones).
+                            TODO: Better system for communicating optimization info (e.g. input
+                            color is solid white, trans black, known to be opaque, etc.) that allows
+                            the effect to communicate back similar known info about its output.
+        @param samplers     One entry for each GrTextureAccess of the GrEffect that generated the
+                            GrGLEffect. These can be passed to the builder to emit texture
+                            reads in the generated code.
+        */
+    virtual void emitCode(GrGLShaderBuilder* builder,
+                          const GrEffectStage& stage,
+                          EffectKey key,
+                          const char* vertexCoords,
+                          const char* outputColor,
+                          const char* inputColor,
+                          const TextureSamplerArray& samplers) = 0;
+
+    /** A GrGLEffect instance can be reused with any GrEffect that produces the same stage
+        key; this function reads data from a stage and uploads any uniform variables required
+        by the shaders created in emitCode(). The GrEffect installed in the GrEffectStage is
+        guaranteed to be of the same type that created this GrGLEffect and to have an identical
+        EffectKey as the one that created this GrGLEffect. */
+    virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
+
+    const char* name() const { return fFactory.name(); }
+
+    static EffectKey GenTextureKey(const GrEffectRef*, const GrGLCaps&);
+
+   /**
+    * GrGLEffect subclasses get passed a GrEffectStage in their emitCode and setData functions.
+    * The GrGLEffect usually needs to cast the stage's effect to the GrEffect subclass that
+    * generated the GrGLEffect. This helper does just that.
+    */
+    template <typename T>
+    static const T& GetEffectFromStage(const GrEffectStage& effectStage) {
+        GrAssert(NULL != effectStage.getEffect());
+        return CastEffect<T>(*effectStage.getEffect());
+    }
+
+   /**
+    * Extracts the GrEffect from a GrEffectRef and down-casts to a GrEffect subclass. Usually used
+    * in a GrGLEffect subclass's constructor (which takes const GrEffectRef&).
+    */
+    template <typename T>
+    static const T& CastEffect(const GrEffectRef& effectRef) {
+        GrAssert(NULL != effectRef.get());
+        return *static_cast<const T*>(effectRef.get());
+    }
+
+protected:
+    const GrBackendEffectFactory& fFactory;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLEffectMatrix.cpp b/src/gpu/gl/GrGLEffectMatrix.cpp
new file mode 100644
index 0000000..5fdde2c
--- /dev/null
+++ b/src/gpu/gl/GrGLEffectMatrix.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 "GrGLEffectMatrix.h"
+#include "GrTexture.h"
+
+GrGLEffect::EffectKey GrGLEffectMatrix::GenKey(const SkMatrix& effectMatrix,
+                                               const SkMatrix& coordChangeMatrix,
+                                               const GrTexture* texture) {
+    SkMatrix::TypeMask type0 = effectMatrix.getType();
+    SkMatrix::TypeMask type1 = coordChangeMatrix.getType();
+
+    static const int kNonTransMask = SkMatrix::kAffine_Mask |
+                                     SkMatrix::kScale_Mask  |
+                                     SkMatrix::kPerspective_Mask;
+    int combinedTypes = type0 | type1;
+
+    bool reverseY = (NULL != texture) && kBottomLeft_GrSurfaceOrigin == texture->origin();
+
+    if (SkMatrix::kPerspective_Mask & combinedTypes) {
+        return kGeneral_Key;
+    } else if ((kNonTransMask & combinedTypes) || reverseY) {
+        return kNoPersp_Key;
+    } else if (kTrans_Key & combinedTypes) {
+        return kTrans_Key;
+    } else {
+        GrAssert(effectMatrix.isIdentity() && coordChangeMatrix.isIdentity());
+        return kIdentity_Key;
+    }
+}
+
+GrSLType GrGLEffectMatrix::emitCode(GrGLShaderBuilder* builder,
+                                    EffectKey key,
+                                    const char* vertexCoords,
+                                    const char** fsCoordName,
+                                    const char** vsCoordName,
+                                    const char* suffix) {
+    GrSLType varyingType;
+    const char* uniName;
+    key &= kKeyMask;
+    switch (key) {
+        case kIdentity_Key:
+            fUniType = kVoid_GrSLType;
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kTrans_Key:
+            fUniType = kVec2f_GrSLType;
+            uniName = "StageTranslate";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kNoPersp_Key:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec2f_GrSLType;
+            break;
+        case kGeneral_Key:
+            fUniType = kMat33f_GrSLType;
+            uniName = "StageMatrix";
+            varyingType = kVec3f_GrSLType;
+            break;
+        default:
+            GrCrash("Unexpected key.");
+    }
+    SkString suffixedUniName;
+    if (NULL != suffix) {
+        suffixedUniName.append(uniName);
+        suffixedUniName.append(suffix);
+        uniName = suffixedUniName.c_str();
+    }
+    if (kVoid_GrSLType != fUniType) {
+        fUni = builder->addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                   fUniType,
+                                   uniName,
+                                   &uniName);
+    }
+
+    const char* varyingName = "StageCoord";
+    SkString suffixedVaryingName;
+    if (NULL != suffix) {
+        suffixedVaryingName.append(varyingName);
+        suffixedVaryingName.append(suffix);
+        varyingName = suffixedVaryingName.c_str();
+    }
+    const char* vsVaryingName;
+    const char* fsVaryingName;
+    builder->addVarying(varyingType, varyingName, &vsVaryingName, &fsVaryingName);
+
+    // varying = matrix * vertex-coords (logically)
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            GrAssert(kVec2f_GrSLType == varyingType);
+            builder->fVSCode.appendf("\t%s = %s;\n", vsVaryingName, vertexCoords);
+            break;
+        case kVec2f_GrSLType:
+            GrAssert(kVec2f_GrSLType == varyingType);
+            builder->fVSCode.appendf("\t%s = %s + %s;\n", vsVaryingName, uniName, vertexCoords);
+            break;
+        case kMat33f_GrSLType: {
+            GrAssert(kVec2f_GrSLType == varyingType || kVec3f_GrSLType == varyingType);
+            if (kVec2f_GrSLType == varyingType) {
+                builder->fVSCode.appendf("\t%s = (%s * vec3(%s, 1)).xy;\n",
+                                         vsVaryingName, uniName, vertexCoords);
+            } else {
+                builder->fVSCode.appendf("\t%s = %s * vec3(%s, 1);\n",
+                                         vsVaryingName, uniName, vertexCoords);
+            }
+            break;
+        }
+        default:
+            GrCrash("Unexpected uniform type.");
+    }
+    if (NULL != vsCoordName) {
+        *vsCoordName = vsVaryingName;
+    }
+    if (NULL != fsCoordName) {
+        *fsCoordName = fsVaryingName;
+    }
+    return varyingType;
+}
+
+/**
+    * This is similar to emitCode except that it performs perspective division in the FS if the
+    * texture coordinates have a w coordinate. The fsCoordName always refers to a vec2f.
+    */
+void GrGLEffectMatrix::emitCodeMakeFSCoords2D(GrGLShaderBuilder* builder,
+                                              EffectKey key,
+                                              const char* vertexCoords,
+                                              const char** fsCoordName,
+                                              const char** vsVaryingName,
+                                              GrSLType* vsVaryingType,
+                                              const char* suffix) {
+    const char* fsVaryingName;
+
+    GrSLType varyingType = this->emitCode(builder,
+                                          key,
+                                          vertexCoords,
+                                          &fsVaryingName,
+                                          vsVaryingName,
+                                          suffix);
+    if (kVec3f_GrSLType == varyingType) {
+
+        const char* coordName = "coords2D";
+        SkString suffixedCoordName;
+        if (NULL != suffix) {
+            suffixedCoordName.append(coordName);
+            suffixedCoordName.append(suffix);
+            coordName = suffixedCoordName.c_str();
+        }
+        builder->fFSCode.appendf("\tvec2 %s = %s.xy / %s.z;",
+                                    coordName, fsVaryingName, fsVaryingName);
+        if (NULL != fsCoordName) {
+            *fsCoordName = coordName;
+        }
+    } else if(NULL != fsCoordName) {
+        *fsCoordName = fsVaryingName;
+    }
+    if (NULL != vsVaryingType) {
+        *vsVaryingType = varyingType;
+    }
+}
+
+void GrGLEffectMatrix::setData(const GrGLUniformManager& uniformManager,
+                              const SkMatrix& matrix,
+                              const SkMatrix& coordChangeMatrix,
+                              const GrTexture* texture) {
+    GrAssert((GrGLUniformManager::kInvalidUniformHandle == fUni) ==
+                (kVoid_GrSLType == fUniType));
+    switch (fUniType) {
+        case kVoid_GrSLType:
+            GrAssert(matrix.isIdentity());
+            GrAssert(coordChangeMatrix.isIdentity());
+            GrAssert(NULL == texture || kTopLeft_GrSurfaceOrigin == texture->origin());
+            return;
+        case kVec2f_GrSLType: {
+            GrAssert(SkMatrix::kTranslate_Mask == (matrix.getType() | coordChangeMatrix.getType()));
+            GrAssert(NULL == texture || kTopLeft_GrSurfaceOrigin == texture->origin());
+            SkScalar tx = matrix[SkMatrix::kMTransX] + coordChangeMatrix[SkMatrix::kMTransX];
+            SkScalar ty = matrix[SkMatrix::kMTransY] + coordChangeMatrix[SkMatrix::kMTransY];
+            if (fPrevMatrix.get(SkMatrix::kMTransX) != tx ||
+                fPrevMatrix.get(SkMatrix::kMTransY) != ty) {
+                uniformManager.set2f(fUni, tx, ty);
+                fPrevMatrix.set(SkMatrix::kMTransX, tx);
+                fPrevMatrix.set(SkMatrix::kMTransY, ty);
+            }
+            break;
+        }
+        case kMat33f_GrSLType: {
+            SkMatrix combined;
+            combined.setConcat(matrix, coordChangeMatrix);
+            if (NULL != texture && kBottomLeft_GrSurfaceOrigin == texture->origin()) {
+                // combined.postScale(1,-1);
+                // combined.postTranslate(0,1);
+                combined.set(SkMatrix::kMSkewY,
+                    combined[SkMatrix::kMPersp0] - combined[SkMatrix::kMSkewY]);
+                combined.set(SkMatrix::kMScaleY,
+                    combined[SkMatrix::kMPersp1] - combined[SkMatrix::kMScaleY]);
+                combined.set(SkMatrix::kMTransY,
+                    combined[SkMatrix::kMPersp2] - combined[SkMatrix::kMTransY]);
+            }
+            if (!fPrevMatrix.cheapEqualTo(combined)) {
+                uniformManager.setSkMatrix(fUni, combined);
+                fPrevMatrix = combined;
+            }
+            break;
+        }
+        default:
+            GrCrash("Unexpected uniform type.");
+    }
+}
diff --git a/src/gpu/gl/GrGLEffectMatrix.h b/src/gpu/gl/GrGLEffectMatrix.h
new file mode 100644
index 0000000..49ddceb
--- /dev/null
+++ b/src/gpu/gl/GrGLEffectMatrix.h
@@ -0,0 +1,97 @@
+/*
+ * 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 GrGLEffectMatrix_DEFINED
+#define GrGLEffectMatrix_DEFINED
+
+#include "GrGLEffect.h"
+#include "SkMatrix.h"
+
+class GrTexture;
+class SkRandom;
+
+/**
+ * This is a helper to implement a texture matrix in a GrGLEffect.
+ */
+class GrGLEffectMatrix {
+public:
+    typedef GrGLEffect::EffectKey EffectKey;
+    /**
+     * The matrix uses kKeyBits of the effect's EffectKey. A GrGLEffect may place these bits at an
+     * arbitrary shift in its final key. However, when GrGLEffectMatrix::emitCode*() code is called
+     * the relevant bits must be in the lower kKeyBits of the key parameter.
+     */
+    enum {
+        kKeyBits = 2,
+        kKeyMask = (1 << kKeyBits) - 1,
+    };
+
+    GrGLEffectMatrix() : fUni(GrGLUniformManager::kInvalidUniformHandle) {
+        fPrevMatrix = SkMatrix::InvalidMatrix();
+    }
+
+    /**
+     * Generates the key for the portion of the code emitted by this class's emitCode() function.
+     * Pass a texture to make GrGLEffectMatrix automatically adjust for the texture's origin. Pass
+     * NULL when not using the EffectMatrix for a texture lookups, or if the GrGLEffect subclass
+     * wants to handle origin adjustments in some other manner. coordChangeMatrix is the matrix
+     * from GrEffectStage.
+     */
+    static EffectKey GenKey(const SkMatrix& effectMatrix,
+                            const SkMatrix& coordChangeMatrix,
+                            const GrTexture*);
+
+    /**
+     * Emits code to implement the matrix in the VS. A varying is added as an output of the VS and
+     * input to the FS. The varying may be either a vec2f or vec3f depending upon whether
+     * perspective interpolation is required or not. The names of the varying in the VS and FS are
+     * are returned as output parameters and the type of the varying is the return value. The suffix
+     * is an optional parameter that can be used to make all variables emitted by the object
+     * unique within a stage. It is only necessary if multiple GrGLEffectMatrix objects are used by
+     * a GrGLEffect.
+     */
+    GrSLType emitCode(GrGLShaderBuilder*,
+                      EffectKey,
+                      const char* vertexCoords,
+                      const char** fsCoordName, /* optional */
+                      const char** vsCoordName = NULL,
+                      const char* suffix = NULL);
+
+    /**
+     * This is similar to emitCode except that it performs perspective division in the FS if the
+     * texture coordinates have a w coordinate. The fsCoordName always refers to a vec2f.
+     */
+    void emitCodeMakeFSCoords2D(GrGLShaderBuilder*,
+                                EffectKey,
+                                const char* vertexCoords,
+                                const char** fsCoordName, /* optional */
+                                const char** vsVaryingName = NULL,
+                                GrSLType* vsVaryingType = NULL,
+                                const char* suffix = NULL);
+    /**
+     * Call from a GrGLEffect's subclass to update the texture matrix. The matrix,
+     * coordChangeMatrix, and texture params should match those used with GenKey.
+     */
+    void setData(const GrGLUniformManager& uniformManager,
+                 const SkMatrix& effectMatrix,
+                 const SkMatrix& coordChangeMatrix,
+                 const GrTexture*);
+
+private:
+    enum {
+        kIdentity_Key   = 0,
+        kTrans_Key      = 1,
+        kNoPersp_Key    = 2,
+        kGeneral_Key    = 3,
+    };
+
+    GrGLUniformManager::UniformHandle fUni;
+    GrSLType                          fUniType;
+    SkMatrix                          fPrevMatrix;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLIRect.h b/src/gpu/gl/GrGLIRect.h
index aee5fb4..038520d 100644
--- a/src/gpu/gl/GrGLIRect.h
+++ b/src/gpu/gl/GrGLIRect.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -12,6 +11,7 @@
 #define GrGLIRect_DEFINED
 
 #include "gl/GrGLInterface.h"
+#include "GrGLUtil.h"
 
 /**
  * Helper struct for dealing with the fact that Ganesh and GL use different
diff --git a/src/gpu/gl/GrGLIndexBuffer.cpp b/src/gpu/gl/GrGLIndexBuffer.cpp
index 60bd9a9..561133a 100644
--- a/src/gpu/gl/GrGLIndexBuffer.cpp
+++ b/src/gpu/gl/GrGLIndexBuffer.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -16,10 +15,11 @@
 #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
 
 GrGLIndexBuffer::GrGLIndexBuffer(GrGpuGL* gpu,
+                                 bool isWrapped,
                                  GrGLuint id,
                                  size_t sizeInBytes,
                                  bool dynamic)
-    : INHERITED(gpu, sizeInBytes, dynamic)
+    : INHERITED(gpu, isWrapped, sizeInBytes, dynamic)
     , fBufferID(id)
     , fLockPtr(NULL) {
 
@@ -27,16 +27,20 @@
 
 void GrGLIndexBuffer::onRelease() {
     // make sure we've not been abandoned
-    if (fBufferID) {
+    if (fBufferID && !this->isWrapped()) {
         GPUGL->notifyIndexBufferDelete(this);
         GL_CALL(DeleteBuffers(1, &fBufferID));
         fBufferID = 0;
     }
+
+    INHERITED::onRelease();
 }
 
 void GrGLIndexBuffer::onAbandon() {
     fBufferID = 0;
     fLockPtr = NULL;
+
+    INHERITED::onAbandon();
 }
 
 void GrGLIndexBuffer::bind() const {
@@ -51,7 +55,7 @@
 void* GrGLIndexBuffer::lock() {
     GrAssert(fBufferID);
     GrAssert(!isLocked());
-    if (this->getGpu()->getCaps().fBufferLockSupport) {
+    if (this->getGpu()->getCaps().bufferLockSupport()) {
         this->bind();
         // Let driver know it can discard the old data
         GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
@@ -76,7 +80,7 @@
 void GrGLIndexBuffer::unlock() {
     GrAssert(fBufferID);
     GrAssert(isLocked());
-    GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
+    GrAssert(this->getGpu()->getCaps().bufferLockSupport());
 
     this->bind();
     GL_CALL(UnmapBuffer(GR_GL_ELEMENT_ARRAY_BUFFER));
@@ -84,7 +88,8 @@
 }
 
 bool GrGLIndexBuffer::isLocked() const {
-#if GR_DEBUG
+    // this check causes a lot of noise in the gl log
+#if 0
     if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
         this->bind();
         GrGLint mapped;
@@ -125,9 +130,8 @@
     // Note that we're cheating on the size here. Currently no methods
     // allow a partial update that preserves contents of non-updated
     // portions of the buffer (lock() does a glBufferData(..size, NULL..))
-    GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER, 
+    GL_CALL(BufferData(GR_GL_ELEMENT_ARRAY_BUFFER,
                        srcSizeInBytes, src, usage));
 #endif
     return true;
 }
-
diff --git a/src/gpu/gl/GrGLIndexBuffer.h b/src/gpu/gl/GrGLIndexBuffer.h
index 68c165f..936e650 100644
--- a/src/gpu/gl/GrGLIndexBuffer.h
+++ b/src/gpu/gl/GrGLIndexBuffer.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -11,7 +10,7 @@
 #ifndef GrGLIndexBuffer_DEFINED
 #define GrGLIndexBuffer_DEFINED
 
-#include "../GrIndexBuffer.h"
+#include "GrIndexBuffer.h"
 #include "gl/GrGLInterface.h"
 
 class GrGpuGL;
@@ -33,13 +32,14 @@
 
 protected:
     GrGLIndexBuffer(GrGpuGL* gpu,
+                    bool isWrapped,
                     GrGLuint id,
                     size_t sizeInBytes,
                     bool dynamic);
 
     // overrides of GrResource
-    virtual void onAbandon();
-    virtual void onRelease();
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
 
 private:
     void bind() const;
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index 6475306..61020bc 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -7,265 +6,22 @@
  */
 
 
-#include "GrTypes.h"
 #include "gl/GrGLInterface.h"
-#include "gl/GrGLDefines.h"
+#include "GrGLUtil.h"
 
 #include <stdio.h>
 
+SK_DEFINE_INST_COUNT(GrGLInterface)
+
 #if GR_GL_PER_GL_FUNC_CALLBACK
 namespace {
 void GrGLDefaultInterfaceCallback(const GrGLInterface*) {}
 }
 #endif
 
-GrGLBinding GrGLGetBindingInUseFromString(const char* versionString) {
-    if (NULL == versionString) {
-        GrAssert(!"NULL GL version string.");
-        return kNone_GrGLBinding;
-    }
-
-    int major, minor;
-
-    // check for desktop
-    int n = sscanf(versionString, "%d.%d", &major, &minor);
-    if (2 == n) {
-        return kDesktop_GrGLBinding;
-    }
-
-    // check for ES 1
-    char profile[2];
-    n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
-               &major, &minor);
-    if (4 == n) {
-        // we no longer support ES1.
-        return kNone_GrGLBinding;
-    }
-
-    // check for ES2
-    n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
-    if (2 == n) {
-        return kES2_GrGLBinding;
-    }
-    return kNone_GrGLBinding;
-}
-
-GrGLVersion GrGLGetVersionFromString(const char* versionString) {
-    if (NULL == versionString) {
-        GrAssert(!"NULL GL version string.");
-        return 0;
-    }
-
-    int major, minor;
-
-    int n = sscanf(versionString, "%d.%d", &major, &minor);
-    if (2 == n) {
-        return GR_GL_VER(major, minor);
-    }
-
-    char profile[2];
-    n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
-               &major, &minor);
-    if (4 == n) {
-        return GR_GL_VER(major, minor);
-    }
-    
-    n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
-    if (2 == n) {
-        return GR_GL_VER(major, minor);
-    }
-
-    return 0;
-}
-
-GrGLSLVersion GrGLGetGLSLVersionFromString(const char* versionString) {
-    if (NULL == versionString) {
-        GrAssert(!"NULL GLSL version string.");
-        return 0;
-    }
-
-    int major, minor;
-
-    int n = sscanf(versionString, "%d.%d", &major, &minor);
-    if (2 == n) {
-        return GR_GLSL_VER(major, minor);
-    }
-    
-    n = sscanf(versionString, "OpenGL ES GLSL ES %d.%d", &major, &minor);
-    if (2 == n) {
-        return GR_GLSL_VER(major, minor);
-    }
-
-    // android hack
-    n = sscanf(versionString, "OpenGL ES GLSL %d.%d", &major, &minor);
-    if (2 == n) {
-        return GR_GLSL_VER(major, minor);
-    }
-
-    return 0;
-}
-
-bool GrGLHasExtensionFromString(const char* ext, const char* extensionString) {
-    int extLength = strlen(ext);
-
-    while (true) {
-        int n = strcspn(extensionString, " ");
-        if (n == extLength && 0 == strncmp(ext, extensionString, n)) {
-            return true;
-        }
-        if (0 == extensionString[n]) {
-            return false;
-        }
-        extensionString += n+1;
-    }
-
-    return false;
-}
-
-bool GrGLHasExtension(const GrGLInterface* gl, const char* ext) {
-    const GrGLubyte* glstr;
-    GR_GL_CALL_RET(gl, glstr, GetString(GR_GL_EXTENSIONS));
-    return GrGLHasExtensionFromString(ext, (const char*) glstr);
-}
-
-GrGLBinding GrGLGetBindingInUse(const GrGLInterface* gl) {
-    const GrGLubyte* v;
-    GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
-    return GrGLGetBindingInUseFromString((const char*) v);
-}
-
-GrGLVersion GrGLGetVersion(const GrGLInterface* gl) {
-    const GrGLubyte* v;
-    GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
-    return GrGLGetVersionFromString((const char*) v);
-}
-
-GrGLSLVersion GrGLGetGLSLVersion(const GrGLInterface* gl) {
-    const GrGLubyte* v;
-    GR_GL_CALL_RET(gl, v, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
-    return GrGLGetGLSLVersionFromString((const char*) v);
-}
-
 GrGLInterface::GrGLInterface() {
     fBindingsExported = kNone_GrGLBinding;
 
-    fActiveTexture = NULL;
-    fAttachShader = NULL;
-    fBeginQuery = NULL;
-    fBindAttribLocation = NULL;
-    fBindBuffer = NULL;
-    fBindFragDataLocation = NULL;
-    fBindTexture = NULL;
-    fBlendColor = NULL;
-    fBlendFunc = NULL;
-    fBufferData = NULL;
-    fBufferSubData = NULL;
-    fClear = NULL;
-    fClearColor = NULL;
-    fClearStencil = NULL;
-    fColorMask = NULL;
-    fColorPointer = NULL;
-    fCompileShader = NULL;
-    fCompressedTexImage2D = NULL;
-    fCreateProgram = NULL;
-    fCreateShader = NULL;
-    fCullFace = NULL;
-    fDeleteBuffers = NULL;
-    fDeleteProgram = NULL;
-    fDeleteQueries = NULL;
-    fDeleteShader = NULL;
-    fDeleteTextures = NULL;
-    fDepthMask = NULL;
-    fDisable = NULL;
-    fDisableVertexAttribArray = NULL;
-    fDrawArrays = NULL;
-    fDrawBuffer = NULL;
-    fDrawBuffers = NULL;
-    fDrawElements = NULL;
-    fEndQuery = NULL;
-    fFinish = NULL;
-    fFlush = NULL;
-    fEnable = NULL;
-    fEnableVertexAttribArray = NULL;
-    fFrontFace = NULL;
-    fGenBuffers = NULL;
-    fGenQueries = NULL;
-    fGenTextures = NULL;
-    fGetBufferParameteriv = NULL;
-    fGetError = NULL;
-    fGetIntegerv = NULL;
-    fGetQueryiv = NULL;
-    fGetQueryObjecti64v = NULL;
-    fGetQueryObjectiv = NULL;
-    fGetQueryObjectui64v = NULL;
-    fGetQueryObjectuiv = NULL;
-    fGetProgramInfoLog = NULL;
-    fGetProgramiv = NULL;
-    fGetShaderInfoLog = NULL;
-    fGetShaderiv = NULL;
-    fGetString = NULL;
-    fGetTexLevelParameteriv = NULL;
-    fGetUniformLocation = NULL;
-    fLineWidth = NULL;
-    fLinkProgram = NULL;
-    fPixelStorei = NULL;
-    fQueryCounter = NULL;
-    fReadBuffer = NULL;
-    fReadPixels = NULL;
-    fScissor = NULL;
-    fShaderSource = NULL;
-    fStencilFunc = NULL;
-    fStencilFuncSeparate = NULL;
-    fStencilMask = NULL;
-    fStencilMaskSeparate = NULL;
-    fStencilOp = NULL;
-    fStencilOpSeparate = NULL;
-    fTexImage2D = NULL;
-    fTexParameteri = NULL;
-    fTexStorage2D = NULL;
-    fTexSubImage2D = NULL;
-    fUniform1f = NULL;
-    fUniform1i = NULL;
-    fUniform1fv = NULL;
-    fUniform1iv = NULL;
-    fUniform2f = NULL;
-    fUniform2i = NULL;
-    fUniform2fv = NULL;
-    fUniform2iv = NULL;
-    fUniform3f = NULL;
-    fUniform3i = NULL;
-    fUniform3fv = NULL;
-    fUniform3iv = NULL;
-    fUniform4f = NULL;
-    fUniform4i = NULL;
-    fUniform4fv = NULL;
-    fUniform4iv = NULL;
-    fUniformMatrix2fv = NULL;
-    fUniformMatrix3fv = NULL;
-    fUniformMatrix4fv = NULL;
-    fUseProgram = NULL;
-    fVertexAttrib4fv = NULL;
-    fVertexAttribPointer = NULL;
-    fViewport = NULL;
-    fBindFramebuffer = NULL;
-    fBindRenderbuffer = NULL;
-    fCheckFramebufferStatus = NULL;
-    fDeleteFramebuffers = NULL;
-    fDeleteRenderbuffers = NULL;
-    fFramebufferRenderbuffer = NULL;
-    fFramebufferTexture2D = NULL;
-    fGenFramebuffers = NULL;
-    fGenRenderbuffers = NULL;
-    fGetFramebufferAttachmentParameteriv = NULL;
-    fGetRenderbufferParameteriv = NULL;
-    fRenderbufferStorage = NULL;
-    fRenderbufferStorageMultisample = NULL;
-    fBlitFramebuffer = NULL;
-    fResolveMultisampleFramebuffer = NULL;
-    fMapBuffer = NULL;
-    fUnmapBuffer = NULL;
-    fBindFragDataLocationIndexed = NULL;
-
 #if GR_GL_PER_GL_FUNC_CALLBACK
     fCallback = GrGLDefaultInterfaceCallback;
     fCallbackData = 0;
@@ -289,6 +45,7 @@
         NULL == fBindBuffer ||
         NULL == fBindTexture ||
         NULL == fBlendFunc ||
+        NULL == fBlendColor ||      // -> GL >= 1.4, ES >= 2.0 or extension
         NULL == fBufferData ||
         NULL == fBufferSubData ||
         NULL == fClear ||
@@ -332,6 +89,7 @@
         NULL == fStencilOp ||
         NULL == fTexImage2D ||
         NULL == fTexParameteri ||
+        NULL == fTexParameteriv ||
         NULL == fTexSubImage2D ||
         NULL == fUniform1f ||
         NULL == fUniform1i ||
@@ -385,13 +143,13 @@
     // On the desktop we assume they are available if the extension
     // is present or GL version is high enough.
     if (kES2_GrGLBinding == binding) {
-        if (NULL == fBlendColor ||
-            NULL == fStencilFuncSeparate ||
+        if (NULL == fStencilFuncSeparate ||
             NULL == fStencilMaskSeparate ||
             NULL == fStencilOpSeparate) {
             return false;
         }
     } else if (kDesktop_GrGLBinding == binding) {
+
         if (glVer >= GR_GL_VER(2,0)) {
             if (NULL == fStencilFuncSeparate ||
                 NULL == fStencilMaskSeparate ||
@@ -408,12 +166,7 @@
                 return false;
             }
         }
-        if (glVer >= GR_GL_VER(1,4) ||
-            GrGLHasExtensionFromString("GL_EXT_blend_color", ext)) {
-            if (NULL == fBlendColor) {
-                return false;
-            }
-        }
+
         if (glVer >= GR_GL_VER(1,5) ||
             GrGLHasExtensionFromString("GL_ARB_occlusion_query", ext)) {
             if (NULL == fGenQueries ||
@@ -440,6 +193,68 @@
                 return false;
             }
         }
+        // The below two blocks are checks for functions used with
+        // GL_NV_path_rendering. We're not enforcing that they be non-NULL
+        // because they aren't actually called at this time.
+        if (false &&
+            (NULL == fMatrixMode ||
+             NULL == fLoadIdentity ||
+             NULL == fLoadMatrixf)) {
+            return false;
+        }
+        if (false && GrGLHasExtensionFromString("GL_NV_path_rendering", ext)) {
+            if (NULL == fPathCommands ||
+                NULL == fPathCoords ||
+                NULL == fPathSubCommands ||
+                NULL == fPathSubCoords ||
+                NULL == fPathString ||
+                NULL == fPathGlyphs ||
+                NULL == fPathGlyphRange ||
+                NULL == fWeightPaths ||
+                NULL == fCopyPath ||
+                NULL == fInterpolatePaths ||
+                NULL == fTransformPath ||
+                NULL == fPathParameteriv ||
+                NULL == fPathParameteri ||
+                NULL == fPathParameterfv ||
+                NULL == fPathParameterf ||
+                NULL == fPathDashArray ||
+                NULL == fGenPaths ||
+                NULL == fDeletePaths ||
+                NULL == fIsPath ||
+                NULL == fPathStencilFunc ||
+                NULL == fPathStencilDepthOffset ||
+                NULL == fStencilFillPath ||
+                NULL == fStencilStrokePath ||
+                NULL == fStencilFillPathInstanced ||
+                NULL == fStencilStrokePathInstanced ||
+                NULL == fPathCoverDepthFunc ||
+                NULL == fPathColorGen ||
+                NULL == fPathTexGen ||
+                NULL == fPathFogGen ||
+                NULL == fCoverFillPath ||
+                NULL == fCoverStrokePath ||
+                NULL == fCoverFillPathInstanced ||
+                NULL == fCoverStrokePathInstanced ||
+                NULL == fGetPathParameteriv ||
+                NULL == fGetPathParameterfv ||
+                NULL == fGetPathCommands ||
+                NULL == fGetPathCoords ||
+                NULL == fGetPathDashArray ||
+                NULL == fGetPathMetrics ||
+                NULL == fGetPathMetricRange ||
+                NULL == fGetPathSpacing ||
+                NULL == fGetPathColorGeniv ||
+                NULL == fGetPathColorGenfv ||
+                NULL == fGetPathTexGeniv ||
+                NULL == fGetPathTexGenfv ||
+                NULL == fIsPointInFillPath ||
+                NULL == fIsPointInStrokePath ||
+                NULL == fGetPathLength ||
+                NULL == fPointAlongPath) {
+                return false;
+            }
+        }
     }
 
     // optional function on desktop before 1.3
@@ -512,7 +327,7 @@
     // On ES buffer mapping is an extension. On Desktop
     // buffer mapping was part of original VBO extension
     // which we require.
-    if (kDesktop_GrGLBinding == binding || 
+    if (kDesktop_GrGLBinding == binding ||
         GrGLHasExtensionFromString("GL_OES_mapbuffer", ext)) {
         if (NULL == fMapBuffer ||
             NULL == fUnmapBuffer) {
@@ -522,7 +337,7 @@
 
     // Dual source blending
     if (kDesktop_GrGLBinding == binding &&
-        (glVer >= GR_GL_VER(3,3) || 
+        (glVer >= GR_GL_VER(3,3) ||
          GrGLHasExtensionFromString("GL_ARB_blend_func_extended", ext))) {
         if (NULL == fBindFragDataLocationIndexed) {
             return false;
@@ -531,4 +346,3 @@
 
     return true;
 }
-
diff --git a/src/gpu/gl/GrGLPath.cpp b/src/gpu/gl/GrGLPath.cpp
new file mode 100644
index 0000000..8c6e11e
--- /dev/null
+++ b/src/gpu/gl/GrGLPath.cpp
@@ -0,0 +1,109 @@
+
+/*
+ * 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 "GrGLPath.h"
+#include "GrGpuGL.h"
+
+#define GPUGL static_cast<GrGpuGL*>(this->getGpu())
+
+#define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
+#define GL_CALL_RET(R, X) GR_GL_CALL_RET(GPUGL->glInterface(), R, X)
+
+namespace {
+inline GrGLubyte verb_to_gl_path_cmd(const SkPath::Verb verb) {
+    static const GrGLubyte gTable[] = {
+        GR_GL_MOVE_TO,
+        GR_GL_LINE_TO,
+        GR_GL_QUADRATIC_CURVE_TO,
+        GR_GL_CUBIC_CURVE_TO,
+        GR_GL_CLOSE_PATH,
+    };
+    GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
+    GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
+    GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
+    GR_STATIC_ASSERT(3 == SkPath::kCubic_Verb);
+    GR_STATIC_ASSERT(4 == SkPath::kClose_Verb);
+
+    GrAssert(verb >= 0 && (size_t)verb < GR_ARRAY_COUNT(gTable));
+    return gTable[verb];
+}
+
+#ifdef SK_DEBUG
+inline int num_pts(const SkPath::Verb verb) {
+    static const int gTable[] = {
+        1, // move
+        1, // line
+        2, // quad
+        3, // cubic
+        0, // close
+    };
+    GR_STATIC_ASSERT(0 == SkPath::kMove_Verb);
+    GR_STATIC_ASSERT(1 == SkPath::kLine_Verb);
+    GR_STATIC_ASSERT(2 == SkPath::kQuad_Verb);
+    GR_STATIC_ASSERT(3 == SkPath::kCubic_Verb);
+    GR_STATIC_ASSERT(4 == SkPath::kClose_Verb);
+
+    GrAssert(verb >= 0 && (size_t)verb < GR_ARRAY_COUNT(gTable));
+    return gTable[verb];
+}
+#endif
+}
+
+static const bool kIsWrapped = false; // The constructor creates the GL path object.
+
+GrGLPath::GrGLPath(GrGpuGL* gpu, const SkPath& path) : INHERITED(gpu, kIsWrapped) {
+    GL_CALL_RET(fPathID, GenPaths(1));
+    SkPath::Iter iter(path, true);
+
+    SkSTArray<16, GrGLubyte, true> pathCommands;
+#ifndef SK_SCALAR_IS_FLOAT
+    GrCrash("Assumes scalar is float.");
+#endif
+    SkSTArray<16, SkPoint, true> pathPoints;
+
+    int verbCnt = path.countVerbs();
+    int pointCnt = path.countPoints();
+    pathCommands.resize_back(verbCnt);
+    pathPoints.resize_back(pointCnt);
+
+    // TODO: Direct access to path points since we could pass them on directly.
+    path.getPoints(&pathPoints[0], pointCnt);
+    path.getVerbs(&pathCommands[0], verbCnt);
+
+    GR_DEBUGCODE(int numPts = 0);
+    for (int i = 0; i < verbCnt; ++i) {
+        SkPath::Verb v = static_cast<SkPath::Verb>(pathCommands[i]);
+        pathCommands[i] = verb_to_gl_path_cmd(v);
+        GR_DEBUGCODE(numPts += num_pts(v));
+    }
+    GrAssert(pathPoints.count() == numPts);
+
+    GL_CALL(PathCommands(fPathID,
+                         verbCnt, &pathCommands[0],
+                         2 * pointCnt, GR_GL_FLOAT, &pathPoints[0]));
+    fBounds = path.getBounds();
+}
+
+GrGLPath::~GrGLPath() {
+    this->release();
+}
+
+void GrGLPath::onRelease() {
+    if (0 != fPathID && !this->isWrapped()) {
+        GL_CALL(DeletePaths(fPathID, 1));
+        fPathID = 0;
+    }
+
+    INHERITED::onRelease();
+}
+
+void GrGLPath::onAbandon() {
+    fPathID = 0;
+
+    INHERITED::onAbandon();
+}
diff --git a/src/gpu/gl/GrGLPath.h b/src/gpu/gl/GrGLPath.h
new file mode 100644
index 0000000..dfe3405
--- /dev/null
+++ b/src/gpu/gl/GrGLPath.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 GrGLPath_DEFINED
+#define GrGLPath_DEFINED
+
+#include "../GrPath.h"
+#include "gl/GrGLFunctions.h"
+
+class GrGpuGL;
+class SkPath;
+
+/**
+ * Currently this represents a path built using GL_NV_path_rendering. If we
+ * support other GL path extensions then this would have to have a type enum
+ * and/or be subclassed.
+ */
+
+class GrGLPath : public GrPath {
+public:
+    GrGLPath(GrGpuGL* gpu, const SkPath& path);
+    virtual ~GrGLPath();
+    GrGLuint pathID() const { return fPathID; }
+    // TODO: Figure out how to get an approximate size of the path in Gpu
+    // memory.
+    virtual size_t sizeInBytes() const SK_OVERRIDE { return 100; }
+
+protected:
+    virtual void onRelease() SK_OVERRIDE;
+    virtual void onAbandon() SK_OVERRIDE;
+
+private:
+    GrGLuint fPathID;
+
+    typedef GrPath INHERITED;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLProgram.cpp b/src/gpu/gl/GrGLProgram.cpp
index 2925213..2aa7236 100644
--- a/src/gpu/gl/GrGLProgram.cpp
+++ b/src/gpu/gl/GrGLProgram.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,194 +5,113 @@
  * found in the LICENSE file.
  */
 
-
 #include "GrGLProgram.h"
 
-#include "../GrAllocator.h"
+#include "GrAllocator.h"
+#include "GrEffect.h"
+#include "GrGLEffect.h"
+#include "GrGpuGL.h"
 #include "GrGLShaderVar.h"
+#include "GrBackendEffectFactory.h"
 #include "SkTrace.h"
 #include "SkXfermode.h"
 
-namespace {
+#include "SkRTConf.h"
 
-enum {
-    /// Used to mark a StageUniLocation field that should be bound
-    /// to a uniform during getUniformLocationsAndInitCache().
-    kUseUniform = 2000
-};
+SK_DEFINE_INST_COUNT(GrGLProgram)
 
-}  // namespace
+#define GL_CALL(X) GR_GL_CALL(fContextInfo.interface(), X)
+#define GL_CALL_RET(R, X) GR_GL_CALL_RET(fContextInfo.interface(), R, X)
 
-#define PRINT_SHADERS 0
+SK_CONF_DECLARE(bool, c_PrintShaders, "gpu.printShaders", false, "Print the source code for all shaders generated.");
 
-typedef GrTAllocator<GrGLShaderVar> VarArray;
-
-// number of each input/output type in a single allocation block
-static const int gVarsPerBlock = 8;
-// except FS outputs where we expect 2 at most.
-static const int gMaxFSOutputs = 2;
-
-struct ShaderCodeSegments {
-    ShaderCodeSegments() 
-    : fVSUnis(gVarsPerBlock)
-    , fVSAttrs(gVarsPerBlock)
-    , fVSOutputs(gVarsPerBlock)
-    , fGSInputs(gVarsPerBlock)
-    , fGSOutputs(gVarsPerBlock)
-    , fFSInputs(gVarsPerBlock)
-    , fFSUnis(gVarsPerBlock)
-    , fFSOutputs(gMaxFSOutputs)
-    , fUsesGS(false) {}
-    GrStringBuilder fHeader; // VS+FS, GLSL version, etc
-    VarArray        fVSUnis;
-    VarArray        fVSAttrs;
-    VarArray        fVSOutputs;
-    VarArray        fGSInputs;
-    VarArray        fGSOutputs;
-    VarArray        fFSInputs;
-    GrStringBuilder fGSHeader; // layout qualifiers specific to GS
-    VarArray        fFSUnis;
-    VarArray        fFSOutputs;
-    GrStringBuilder fFSFunctions;
-    GrStringBuilder fVSCode;
-    GrStringBuilder fGSCode;
-    GrStringBuilder fFSCode;
-
-    bool            fUsesGS;
-};
-
-typedef GrGLProgram::ProgramDesc::StageDesc StageDesc;
-
-#if GR_GL_ATTRIBUTE_MATRICES
-    #define VIEW_MATRIX_NAME "aViewM"
-#else
-    #define VIEW_MATRIX_NAME "uViewM"
-#endif
-
-#define POS_ATTR_NAME "aPosition"
 #define COL_ATTR_NAME "aColor"
 #define COV_ATTR_NAME "aCoverage"
 #define EDGE_ATTR_NAME "aEdge"
-#define COL_UNI_NAME "uColor"
-#define COV_UNI_NAME "uCoverage"
-#define EDGES_UNI_NAME "uEdges"
-#define COL_FILTER_UNI_NAME "uColorFilter"
-#define COL_MATRIX_UNI_NAME "uColorMatrix"
-#define COL_MATRIX_VEC_UNI_NAME "uColorMatrixVec"
 
 namespace {
-inline void tex_attr_name(int coordIdx, GrStringBuilder* s) {
+inline void tex_attr_name(int coordIdx, SkString* s) {
     *s = "aTexCoord";
     s->appendS32(coordIdx);
 }
 
-inline GrGLShaderVar::Type float_vector_type(int count) {
-    GR_STATIC_ASSERT(GrGLShaderVar::kFloat_Type == 0);
-    GR_STATIC_ASSERT(GrGLShaderVar::kVec2f_Type == 1);
-    GR_STATIC_ASSERT(GrGLShaderVar::kVec3f_Type == 2);
-    GR_STATIC_ASSERT(GrGLShaderVar::kVec4f_Type == 3);
-    GrAssert(count > 0 && count <= 4);
-    return (GrGLShaderVar::Type)(count - 1);
-}
-
-inline const char* float_vector_type_str(int count) {
-    return GrGLShaderVar::TypeString(float_vector_type(count));
-}
-
-inline const char* vector_homog_coord(int count) {
-    static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
-    return HOMOGS[count];
-}
-
-inline const char* vector_nonhomog_coords(int count) {
-    static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS));
-    return NONHOMOGS[count];
-}
-
-inline const char* vector_all_coords(int count) {
-    static const char* ALL[] = {"ERROR", "", ".xy", ".xyz", ".xyzw"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ALL));
-    return ALL[count];
-}
-
-inline const char* all_ones_vec(int count) {
-    static const char* ONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
-                                    "vec3(1,1,1)", "vec4(1,1,1,1)"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ONESVEC));
-    return ONESVEC[count];
-}
-
-inline const char* all_zeros_vec(int count) {
-    static const char* ZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
-                                    "vec3(0,0,0)", "vec4(0,0,0,0)"};
-    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(ZEROSVEC));
-    return ZEROSVEC[count];
-}
-
 inline const char* declared_color_output_name() { return "fsColorOut"; }
 inline const char* dual_source_output_name() { return "dualSourceOut"; }
 
-inline void tex_matrix_name(int stage, GrStringBuilder* s) {
-#if GR_GL_ATTRIBUTE_MATRICES
-    *s = "aTexM";
-#else
-    *s = "uTexM";
-#endif
-    s->appendS32(stage);
 }
 
-inline void normalized_texel_size_name(int stage, GrStringBuilder* s) {
-    *s = "uTexelSize";
-    s->appendS32(stage);
+GrGLProgram* GrGLProgram::Create(const GrGLContextInfo& gl,
+                                 const Desc& desc,
+                                 const GrEffectStage* stages[]) {
+    GrGLProgram* program = SkNEW_ARGS(GrGLProgram, (gl, desc, stages));
+    if (!program->succeeded()) {
+        delete program;
+        program = NULL;
+    }
+    return program;
 }
 
-inline void sampler_name(int stage, GrStringBuilder* s) {
-    *s = "uSampler";
-    s->appendS32(stage);
-}
+GrGLProgram::GrGLProgram(const GrGLContextInfo& gl,
+                         const Desc& desc,
+                         const GrEffectStage* stages[])
+: fContextInfo(gl)
+, fUniformManager(gl) {
+    fDesc = desc;
+    fVShaderID = 0;
+    fGShaderID = 0;
+    fFShaderID = 0;
+    fProgramID = 0;
 
-inline void radial2_param_name(int stage, GrStringBuilder* s) {
-    *s = "uRadial2Params";
-    s->appendS32(stage);
-}
+    fViewMatrix = SkMatrix::InvalidMatrix();
+    fViewportSize.set(-1, -1);
+    fColor = GrColor_ILLEGAL;
+    fColorFilterColor = GrColor_ILLEGAL;
+    fRTHeight = -1;
 
-inline void convolve_param_names(int stage, GrStringBuilder* k, GrStringBuilder* i) {
-    *k = "uKernel";
-    k->appendS32(stage);
-    *i = "uImageIncrement";
-    i->appendS32(stage);
-}
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+        fEffects[s] = NULL;
+    }
 
-inline void image_increment_param_name(int stage, GrStringBuilder* i) {
-    *i = "uImageIncrement";
-    i->appendS32(stage);
-}
-
-inline void tex_domain_name(int stage, GrStringBuilder* s) {
-    *s = "uTexDom";
-    s->appendS32(stage);
-}
-}
-
-GrGLProgram::GrGLProgram() {
+    this->genProgram(stages);
 }
 
 GrGLProgram::~GrGLProgram() {
+    if (fVShaderID) {
+        GL_CALL(DeleteShader(fVShaderID));
+    }
+    if (fGShaderID) {
+        GL_CALL(DeleteShader(fGShaderID));
+    }
+    if (fFShaderID) {
+        GL_CALL(DeleteShader(fFShaderID));
+    }
+    if (fProgramID) {
+        GL_CALL(DeleteProgram(fProgramID));
+    }
+
+    for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+        delete fEffects[i];
+    }
+}
+
+void GrGLProgram::abandon() {
+    fVShaderID = 0;
+    fGShaderID = 0;
+    fFShaderID = 0;
+    fProgramID = 0;
 }
 
 void GrGLProgram::overrideBlend(GrBlendCoeff* srcCoeff,
                                 GrBlendCoeff* dstCoeff) const {
-    switch (fProgramDesc.fDualSrcOutput) {
-        case ProgramDesc::kNone_DualSrcOutput:
+    switch (fDesc.fDualSrcOutput) {
+        case Desc::kNone_DualSrcOutput:
             break;
         // the prog will write a coverage value to the secondary
         // output and the dst is blended by one minus that value.
-        case ProgramDesc::kCoverage_DualSrcOutput:
-        case ProgramDesc::kCoverageISA_DualSrcOutput:
-        case ProgramDesc::kCoverageISC_DualSrcOutput:
-        *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_BlendCoeff;
+        case Desc::kCoverage_DualSrcOutput:
+        case Desc::kCoverageISA_DualSrcOutput:
+        case Desc::kCoverageISC_DualSrcOutput:
+        *dstCoeff = (GrBlendCoeff)GrGpu::kIS2C_GrBlendCoeff;
         break;
         default:
             GrCrash("Unexpected dual source blend output");
@@ -201,68 +119,14 @@
     }
 }
 
-// assigns modulation of two vars to an output var
-// vars can be vec4s or floats (or one of each)
-// result is always vec4
-// if either var is "" then assign to the other var
-// if both are "" then assign all ones
-static inline void modulate_helper(const char* outputVar,
-                                   const char* var0,
-                                   const char* var1,
-                                   GrStringBuilder* code) {
-    GrAssert(NULL != outputVar);
-    GrAssert(NULL != var0);
-    GrAssert(NULL != var1);
-    GrAssert(NULL != code);
-
-    bool has0 = '\0' != *var0;
-    bool has1 = '\0' != *var1;
-
-    if (!has0 && !has1) {
-        code->appendf("\t%s = %s;\n", outputVar, all_ones_vec(4));
-    } else if (!has0) {
-        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
-    } else if (!has1) {
-        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
-    } else {
-        code->appendf("\t%s = vec4(%s * %s);\n", outputVar, var0, var1);
-    }
-}
-
-// assigns addition of two vars to an output var
-// vars can be vec4s or floats (or one of each)
-// result is always vec4
-// if either var is "" then assign to the other var
-// if both are "" then assign all zeros
-static inline void add_helper(const char* outputVar,
-                              const char* var0,
-                              const char* var1,
-                              GrStringBuilder* code) {
-    GrAssert(NULL != outputVar);
-    GrAssert(NULL != var0);
-    GrAssert(NULL != var1);
-    GrAssert(NULL != code);
-
-    bool has0 = '\0' != *var0;
-    bool has1 = '\0' != *var1;
-
-    if (!has0 && !has1) {
-        code->appendf("\t%s = %s;\n", outputVar, all_zeros_vec(4));
-    } else if (!has0) {
-        code->appendf("\t%s = vec4(%s);\n", outputVar, var1);
-    } else if (!has1) {
-        code->appendf("\t%s = vec4(%s);\n", outputVar, var0);
-    } else {
-        code->appendf("\t%s = vec4(%s + %s);\n", outputVar, var0, var1);
-    }
-}
+namespace {
 
 // given two blend coeffecients determine whether the src
 // and/or dst computation can be omitted.
-static inline void needBlendInputs(SkXfermode::Coeff srcCoeff,
-                                   SkXfermode::Coeff dstCoeff,
-                                   bool* needSrcValue,
-                                   bool* needDstValue) {
+inline void need_blend_inputs(SkXfermode::Coeff srcCoeff,
+                              SkXfermode::Coeff dstCoeff,
+                              bool* needSrcValue,
+                              bool* needDstValue) {
     if (SkXfermode::kZero_Coeff == srcCoeff) {
         switch (dstCoeff) {
             // these all read the src
@@ -301,9 +165,9 @@
  * Create a blend_coeff * value string to be used in shader code. Sets empty
  * string if result is trivially zero.
  */
-static void blendTermString(GrStringBuilder* str, SkXfermode::Coeff coeff,
-                             const char* src, const char* dst,
-                             const char* value) {
+inline void blend_term_string(SkString* str, SkXfermode::Coeff coeff,
+                       const char* src, const char* dst,
+                       const char* value) {
     switch (coeff) {
     case SkXfermode::kZero_Coeff:    /** 0 */
         *str = "";
@@ -315,13 +179,13 @@
         str->printf("(%s * %s)", src, value);
         break;
     case SkXfermode::kISC_Coeff:
-        str->printf("((%s - %s) * %s)", all_ones_vec(4), src, value);
+        str->printf("((%s - %s) * %s)", GrGLSLOnesVecf(4), src, value);
         break;
     case SkXfermode::kDC_Coeff:
         str->printf("(%s * %s)", dst, value);
         break;
     case SkXfermode::kIDC_Coeff:
-        str->printf("((%s - %s) * %s)", all_ones_vec(4), dst, value);
+        str->printf("((%s - %s) * %s)", GrGLSLOnesVecf(4), dst, value);
         break;
     case SkXfermode::kSA_Coeff:      /** src alpha */
         str->printf("(%s.a * %s)", src, value);
@@ -344,220 +208,118 @@
  * Adds a line to the fragment shader code which modifies the color by
  * the specified color filter.
  */
-static void addColorFilter(GrStringBuilder* fsCode, const char * outputVar,
-                           SkXfermode::Coeff uniformCoeff,
-                           SkXfermode::Coeff colorCoeff,
-                           const char* inColor) {
-    GrStringBuilder colorStr, constStr;
-    blendTermString(&colorStr, colorCoeff, COL_FILTER_UNI_NAME,
-                    inColor, inColor);
-    blendTermString(&constStr, uniformCoeff, COL_FILTER_UNI_NAME,
-                    inColor, COL_FILTER_UNI_NAME);
+void add_color_filter(SkString* fsCode, const char * outputVar,
+                      SkXfermode::Coeff uniformCoeff,
+                      SkXfermode::Coeff colorCoeff,
+                      const char* filterColor,
+                      const char* inColor) {
+    SkString colorStr, constStr;
+    blend_term_string(&colorStr, colorCoeff, filterColor, inColor, inColor);
+    blend_term_string(&constStr, uniformCoeff, filterColor, inColor, filterColor);
 
-    add_helper(outputVar, colorStr.c_str(), constStr.c_str(), fsCode);
-}
-/**
- * Adds code to the fragment shader code which modifies the color by
- * the specified color matrix.
- */
-static void addColorMatrix(GrStringBuilder* fsCode, const char * outputVar,
-                           const char* inColor) {
-    fsCode->appendf("\t%s = %s * vec4(%s.rgb / %s.a, %s.a) + %s;\n", outputVar, COL_MATRIX_UNI_NAME, inColor, inColor, inColor, COL_MATRIX_VEC_UNI_NAME);
-    fsCode->appendf("\t%s.rgb *= %s.a;\n", outputVar, outputVar);
-}
-
-namespace {
-
-// Adds a var that is computed in the VS and read in FS.
-// If there is a GS it will just pass it through.
-void append_varying(GrGLShaderVar::Type type,
-                    const char* name,
-                    ShaderCodeSegments* segments,
-                    const char** vsOutName = NULL,
-                    const char** fsInName = NULL) {
-    segments->fVSOutputs.push_back();
-    segments->fVSOutputs.back().setType(type);
-    segments->fVSOutputs.back().setTypeModifier(
-        GrGLShaderVar::kOut_TypeModifier);
-    segments->fVSOutputs.back().accessName()->printf("v%s", name);
-    if (vsOutName) {
-        *vsOutName = segments->fVSOutputs.back().getName().c_str();
-    }
-    // input to FS comes either from VS or GS
-    const GrStringBuilder* fsName;
-    if (segments->fUsesGS) {
-        // if we have a GS take each varying in as an array
-        // and output as non-array.
-        segments->fGSInputs.push_back();
-        segments->fGSInputs.back().setType(type);
-        segments->fGSInputs.back().setTypeModifier(
-            GrGLShaderVar::kIn_TypeModifier);
-        segments->fGSInputs.back().setUnsizedArray();
-        *segments->fGSInputs.back().accessName() =
-            segments->fVSOutputs.back().getName();
-        segments->fGSOutputs.push_back();
-        segments->fGSOutputs.back().setType(type);
-        segments->fGSOutputs.back().setTypeModifier(
-            GrGLShaderVar::kOut_TypeModifier);
-        segments->fGSOutputs.back().accessName()->printf("g%s", name);
-        fsName = segments->fGSOutputs.back().accessName();
-    } else {
-        fsName = segments->fVSOutputs.back().accessName();
-    }
-    segments->fFSInputs.push_back();
-    segments->fFSInputs.back().setType(type);
-    segments->fFSInputs.back().setTypeModifier(
-        GrGLShaderVar::kIn_TypeModifier);
-    segments->fFSInputs.back().setName(*fsName);
-    if (fsInName) {
-        *fsInName = fsName->c_str();
-    }
-}
-
-// version of above that adds a stage number to the
-// the var name (for uniqueness)
-void append_varying(GrGLShaderVar::Type type,
-                    const char* name,
-                    int stageNum,
-                    ShaderCodeSegments* segments,
-                    const char** vsOutName = NULL,
-                    const char** fsInName = NULL) {
-    GrStringBuilder nameWithStage(name);
-    nameWithStage.appendS32(stageNum);
-    append_varying(type, nameWithStage.c_str(), segments, vsOutName, fsInName);
+    fsCode->appendf("\t%s = ", outputVar);
+    GrGLSLAdd4f(fsCode, colorStr.c_str(), constStr.c_str());
+    fsCode->append(";\n");
 }
 }
 
-void GrGLProgram::genEdgeCoverage(const GrGLContextInfo& gl,
-                                  GrVertexLayout layout,
-                                  CachedData* programData,
-                                  GrStringBuilder* coverageVar,
-                                  ShaderCodeSegments* segments) const {
-    if (fProgramDesc.fEdgeAANumEdges > 0) {
-        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec3f_Type,
-                                          GrGLShaderVar::kUniform_TypeModifier,
-                                          EDGES_UNI_NAME,
-                                          fProgramDesc.fEdgeAANumEdges);
-        programData->fUniLocations.fEdgesUni = kUseUniform;
-        int count = fProgramDesc.fEdgeAANumEdges;
-        segments->fFSCode.append(
-            "\tvec3 pos = vec3(gl_FragCoord.xy, 1);\n");
-        for (int i = 0; i < count; i++) {
-            segments->fFSCode.append("\tfloat a");
-            segments->fFSCode.appendS32(i);
-            segments->fFSCode.append(" = clamp(dot(" EDGES_UNI_NAME "[");
-            segments->fFSCode.appendS32(i);
-            segments->fFSCode.append("], pos), 0.0, 1.0);\n");
-        }
-        if (fProgramDesc.fEdgeAAConcave && (count & 0x01) == 0) {
-            // For concave polys, we consider the edges in pairs.
-            segments->fFSFunctions.append("float cross2(vec2 a, vec2 b) {\n");
-            segments->fFSFunctions.append("\treturn dot(a, vec2(b.y, -b.x));\n");
-            segments->fFSFunctions.append("}\n");
-            for (int i = 0; i < count; i += 2) {
-                segments->fFSCode.appendf("\tfloat eb%d;\n", i / 2);
-                segments->fFSCode.appendf("\tif (cross2(" EDGES_UNI_NAME "[%d].xy, " EDGES_UNI_NAME "[%d].xy) < 0.0) {\n", i, i + 1);
-                segments->fFSCode.appendf("\t\teb%d = a%d * a%d;\n", i / 2, i, i + 1);
-                segments->fFSCode.append("\t} else {\n");
-                segments->fFSCode.appendf("\t\teb%d = a%d + a%d - a%d * a%d;\n", i / 2, i, i + 1, i, i + 1);
-                segments->fFSCode.append("\t}\n");
-            }
-            segments->fFSCode.append("\tfloat edgeAlpha = ");
-            for (int i = 0; i < count / 2 - 1; i++) {
-                segments->fFSCode.appendf("min(eb%d, ", i);
-            }
-            segments->fFSCode.appendf("eb%d", count / 2 - 1);
-            for (int i = 0; i < count / 2 - 1; i++) {
-                segments->fFSCode.append(")");
-            }
-            segments->fFSCode.append(";\n");
-        } else {
-            segments->fFSCode.append("\tfloat edgeAlpha = ");
-            for (int i = 0; i < count - 1; i++) {
-                segments->fFSCode.appendf("min(a%d * a%d, ", i, i + 1);
-            }
-            segments->fFSCode.appendf("a%d * a0", count - 1);
-            for (int i = 0; i < count - 1; i++) {
-                segments->fFSCode.append(")");
-            }
-            segments->fFSCode.append(";\n");
-        }
-        *coverageVar = "edgeAlpha";
-    } else  if (layout & GrDrawTarget::kEdge_VertexLayoutBit) {
+bool GrGLProgram::genEdgeCoverage(SkString* coverageVar,
+                                  GrGLShaderBuilder* builder) const {
+    if (fDesc.fVertexLayout & GrDrawState::kEdge_VertexLayoutBit) {
         const char *vsName, *fsName;
-        append_varying(GrGLShaderVar::kVec4f_Type, "Edge", segments,
-            &vsName, &fsName);
-        segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
-            GrGLShaderVar::kAttribute_TypeModifier, EDGE_ATTR_NAME);
-        segments->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
-        if (GrDrawState::kHairLine_EdgeType == fProgramDesc.fVertexEdgeType) {
-            segments->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(gl_FragCoord.xy,1), %s.xyz));\n", fsName);
-            segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
-        } else if (GrDrawState::kQuad_EdgeType == fProgramDesc.fVertexEdgeType) {
-            segments->fFSCode.append("\tfloat edgeAlpha;\n");
-            // keep the derivative instructions outside the conditional 
-            segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
-            segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
-            segments->fFSCode.appendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
+        builder->addVarying(kVec4f_GrSLType, "Edge", &vsName, &fsName);
+        builder->fVSAttrs.push_back().set(kVec4f_GrSLType,
+                                          GrGLShaderVar::kAttribute_TypeModifier,
+                                          EDGE_ATTR_NAME);
+        builder->fVSCode.appendf("\t%s = " EDGE_ATTR_NAME ";\n", vsName);
+        switch (fDesc.fVertexEdgeType) {
+        case GrDrawState::kHairLine_EdgeType:
+            builder->fFSCode.appendf("\tfloat edgeAlpha = abs(dot(vec3(%s.xy,1), %s.xyz));\n", builder->fragmentPosition(), fsName);
+            builder->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+            break;
+        case GrDrawState::kQuad_EdgeType:
+            builder->fFSCode.append("\tfloat edgeAlpha;\n");
+            // keep the derivative instructions outside the conditional
+            builder->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+            builder->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+            builder->fFSCode.appendf("\tif (%s.z > 0.0 && %s.w > 0.0) {\n", fsName, fsName);
             // today we know z and w are in device space. We could use derivatives
-            segments->fFSCode.appendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, fsName);
-            segments->fFSCode.append ("\t} else {\n");
-            segments->fFSCode.appendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
-                                      "\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
-                                      fsName, fsName);
-            segments->fFSCode.appendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
-            segments->fFSCode.append("\t\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n"
-                                      "\t}\n");
-            if (kES2_GrGLBinding == gl.binding()) {
-                segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+            builder->fFSCode.appendf("\t\tedgeAlpha = min(min(%s.z, %s.w) + 0.5, 1.0);\n", fsName, fsName);
+            builder->fFSCode.append ("\t} else {\n");
+            builder->fFSCode.appendf("\t\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+                                     "\t\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
+                                     fsName, fsName);
+            builder->fFSCode.appendf("\t\tedgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
+            builder->fFSCode.append("\t\tedgeAlpha = clamp(0.5 - edgeAlpha / length(gF), 0.0, 1.0);\n"
+                                    "\t}\n");
+            if (kES2_GrGLBinding == fContextInfo.binding()) {
+                builder->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
             }
-        } else {
-            GrAssert(GrDrawState::kHairQuad_EdgeType == fProgramDesc.fVertexEdgeType);
-            segments->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
-            segments->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
-            segments->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
-                                      "\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
-                                      fsName, fsName);
-            segments->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
-            segments->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
-            segments->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
-            if (kES2_GrGLBinding == gl.binding()) {
-                segments->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
+            break;
+        case GrDrawState::kHairQuad_EdgeType:
+            builder->fFSCode.appendf("\tvec2 duvdx = dFdx(%s.xy);\n", fsName);
+            builder->fFSCode.appendf("\tvec2 duvdy = dFdy(%s.xy);\n", fsName);
+            builder->fFSCode.appendf("\tvec2 gF = vec2(2.0*%s.x*duvdx.x - duvdx.y,\n"
+                                     "\t               2.0*%s.x*duvdy.x - duvdy.y);\n",
+                                     fsName, fsName);
+            builder->fFSCode.appendf("\tfloat edgeAlpha = (%s.x*%s.x - %s.y);\n", fsName, fsName, fsName);
+            builder->fFSCode.append("\tedgeAlpha = sqrt(edgeAlpha*edgeAlpha / dot(gF, gF));\n");
+            builder->fFSCode.append("\tedgeAlpha = max(1.0 - edgeAlpha, 0.0);\n");
+            if (kES2_GrGLBinding == fContextInfo.binding()) {
+                builder->fHeader.printf("#extension GL_OES_standard_derivatives: enable\n");
             }
+            break;
+        case GrDrawState::kCircle_EdgeType:
+            builder->fFSCode.append("\tfloat edgeAlpha;\n");
+            builder->fFSCode.appendf("\tfloat d = distance(%s.xy, %s.xy);\n", builder->fragmentPosition(), fsName);
+            builder->fFSCode.appendf("\tfloat outerAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
+            builder->fFSCode.appendf("\tfloat innerAlpha = %s.w == 0.0 ? 1.0 : smoothstep(%s.w - 0.5, %s.w + 0.5, d);\n", fsName, fsName, fsName);
+            builder->fFSCode.append("\tedgeAlpha = outerAlpha * innerAlpha;\n");
+            break;
+        case GrDrawState::kEllipse_EdgeType:
+            builder->fFSCode.append("\tfloat edgeAlpha;\n");
+            builder->fFSCode.appendf("\tvec2 offset = (%s.xy - %s.xy);\n", builder->fragmentPosition(), fsName);
+            builder->fFSCode.appendf("\toffset.y *= %s.w;\n", fsName);
+            builder->fFSCode.append("\tfloat d = length(offset);\n");
+            builder->fFSCode.appendf("\tedgeAlpha = smoothstep(d - 0.5, d + 0.5, %s.z);\n", fsName);
+            break;
+        default:
+            GrCrash("Unknown Edge Type!");
+            break;
+        }
+        if (fDesc.fDiscardIfOutsideEdge) {
+            builder->fFSCode.appendf("\tif (edgeAlpha <= 0.0) {\n\t\tdiscard;\n\t}\n");
         }
         *coverageVar = "edgeAlpha";
+        return true;
     } else {
         coverageVar->reset();
+        return false;
     }
 }
 
-namespace {
-
-void genInputColor(GrGLProgram::ProgramDesc::ColorInput colorInput,
-                   GrGLProgram::CachedData* programData,
-                   ShaderCodeSegments* segments,
-                   GrStringBuilder* inColor) {
-    switch (colorInput) {
-        case GrGLProgram::ProgramDesc::kAttribute_ColorInput: {
-            segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+void GrGLProgram::genInputColor(GrGLShaderBuilder* builder, SkString* inColor) {
+    switch (fDesc.fColorInput) {
+        case GrGLProgram::Desc::kAttribute_ColorInput: {
+            builder->fVSAttrs.push_back().set(kVec4f_GrSLType,
                 GrGLShaderVar::kAttribute_TypeModifier,
                 COL_ATTR_NAME);
             const char *vsName, *fsName;
-            append_varying(GrGLShaderVar::kVec4f_Type, "Color", segments, &vsName, &fsName);
-            segments->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
+            builder->addVarying(kVec4f_GrSLType, "Color", &vsName, &fsName);
+            builder->fVSCode.appendf("\t%s = " COL_ATTR_NAME ";\n", vsName);
             *inColor = fsName;
             } break;
-        case GrGLProgram::ProgramDesc::kUniform_ColorInput:
-            segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
-                GrGLShaderVar::kUniform_TypeModifier,
-                COL_UNI_NAME);
-            programData->fUniLocations.fColorUni = kUseUniform;
-            *inColor = COL_UNI_NAME;
+        case GrGLProgram::Desc::kUniform_ColorInput: {
+            const char* name;
+            fUniformHandles.fColorUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                            kVec4f_GrSLType, "Color", &name);
+            *inColor = name;
             break;
-        case GrGLProgram::ProgramDesc::kTransBlack_ColorInput:
+        }
+        case GrGLProgram::Desc::kTransBlack_ColorInput:
             GrAssert(!"needComputedColor should be false.");
             break;
-        case GrGLProgram::ProgramDesc::kSolidWhite_ColorInput:
+        case GrGLProgram::Desc::kSolidWhite_ColorInput:
             break;
         default:
             GrCrash("Unknown color type.");
@@ -565,14 +327,27 @@
     }
 }
 
-void genAttributeCoverage(ShaderCodeSegments* segments,
-                          GrStringBuilder* inOutCoverage) {
-    segments->fVSAttrs.push_back().set(GrGLShaderVar::kVec4f_Type,
+void GrGLProgram::genUniformCoverage(GrGLShaderBuilder* builder, SkString* inOutCoverage) {
+    const char* covUniName;
+    fUniformHandles.fCoverageUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                       kVec4f_GrSLType, "Coverage", &covUniName);
+    if (inOutCoverage->size()) {
+        builder->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
+                                  covUniName, inOutCoverage->c_str());
+        *inOutCoverage = "uniCoverage";
+    } else {
+        *inOutCoverage = covUniName;
+    }
+}
+
+namespace {
+void gen_attribute_coverage(GrGLShaderBuilder* segments,
+                            SkString* inOutCoverage) {
+    segments->fVSAttrs.push_back().set(kVec4f_GrSLType,
                                        GrGLShaderVar::kAttribute_TypeModifier,
                                        COV_ATTR_NAME);
     const char *vsName, *fsName;
-    append_varying(GrGLShaderVar::kVec4f_Type, "Coverage", 
-                   segments, &vsName, &fsName);
+    segments->addVarying(kVec4f_GrSLType, "Coverage", &vsName, &fsName);
     segments->fVSCode.appendf("\t%s = " COV_ATTR_NAME ";\n", vsName);
     if (inOutCoverage->size()) {
         segments->fFSCode.appendf("\tvec4 attrCoverage = %s * %s;\n",
@@ -582,36 +357,17 @@
         *inOutCoverage = fsName;
     }
 }
-    
-void genUniformCoverage(ShaderCodeSegments* segments,
-                        GrGLProgram::CachedData* programData,
-                        GrStringBuilder* inOutCoverage) {
-    segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
-                                      GrGLShaderVar::kUniform_TypeModifier,
-                                      COV_UNI_NAME);
-    programData->fUniLocations.fCoverageUni = kUseUniform;
-    if (inOutCoverage->size()) {
-        segments->fFSCode.appendf("\tvec4 uniCoverage = %s * %s;\n",
-                                  COV_UNI_NAME, inOutCoverage->c_str());
-        *inOutCoverage = "uniCoverage";
-    } else {
-        *inOutCoverage = COV_UNI_NAME;
-    }
 }
 
-}
-
-void GrGLProgram::genGeometryShader(const GrGLContextInfo& gl,
-                                    ShaderCodeSegments* segments) const {
+void GrGLProgram::genGeometryShader(GrGLShaderBuilder* segments) const {
 #if GR_GL_EXPERIMENTAL_GS
-    if (fProgramDesc.fExperimentalGS) {
-        GrAssert(gl.glslGeneration() >= k150_GrGLSLGeneration);
+    if (fDesc.fExperimentalGS) {
+        GrAssert(fContextInfo.glslGeneration() >= k150_GrGLSLGeneration);
         segments->fGSHeader.append("layout(triangles) in;\n"
                                    "layout(triangle_strip, max_vertices = 6) out;\n");
-        segments->fGSCode.append("void main() {\n"
-                                 "\tfor (int i = 0; i < 3; ++i) {\n"
-                                  "\t\tgl_Position = gl_in[i].gl_Position;\n");
-        if (this->fProgramDesc.fEmitsPointSize) {
+        segments->fGSCode.append("\tfor (int i = 0; i < 3; ++i) {\n"
+                                 "\t\tgl_Position = gl_in[i].gl_Position;\n");
+        if (fDesc.fEmitsPointSize) {
             segments->fGSCode.append("\t\tgl_PointSize = 1.0;\n");
         }
         GrAssert(segments->fGSInputs.count() == segments->fGSOutputs.count());
@@ -623,44 +379,143 @@
         }
         segments->fGSCode.append("\t\tEmitVertex();\n"
                                  "\t}\n"
-                                 "\tEndPrimitive();\n"
-                                 "}\n");
+                                 "\tEndPrimitive();\n");
     }
 #endif
 }
 
-const char* GrGLProgram::adjustInColor(const GrStringBuilder& inColor) const {
+const char* GrGLProgram::adjustInColor(const SkString& inColor) const {
     if (inColor.size()) {
           return inColor.c_str();
     } else {
-        if (ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput) {
-            return all_ones_vec(4);
+        if (Desc::kSolidWhite_ColorInput == fDesc.fColorInput) {
+            return GrGLSLOnesVecf(4);
         } else {
-            return all_zeros_vec(4);
+            return GrGLSLZerosVecf(4);
         }
     }
 }
 
+namespace {
+// prints a shader using params similar to glShaderSource
+void print_shader(GrGLint stringCnt,
+                  const GrGLchar** strings,
+                  GrGLint* stringLengths) {
+    for (int i = 0; i < stringCnt; ++i) {
+        if (NULL == stringLengths || stringLengths[i] < 0) {
+            GrPrintf(strings[i]);
+        } else {
+            GrPrintf("%.*s", stringLengths[i], strings[i]);
+        }
+    }
+}
 
-bool GrGLProgram::genProgram(const GrGLContextInfo& gl,
-                             GrGLProgram::CachedData* programData) const {
+// Compiles a GL shader, returns shader ID or 0 if failed params have same meaning as glShaderSource
+GrGLuint compile_shader(const GrGLContextInfo& gl,
+                        GrGLenum type,
+                        int stringCnt,
+                        const char** strings,
+                        int* stringLengths) {
+    SK_TRACE_EVENT1("GrGLProgram::CompileShader",
+                    "stringCount", SkStringPrintf("%i", stringCnt).c_str());
 
-    ShaderCodeSegments segments;
-    const uint32_t& layout = fProgramDesc.fVertexLayout;
+    GrGLuint shader;
+    GR_GL_CALL_RET(gl.interface(), shader, CreateShader(type));
+    if (0 == shader) {
+        return 0;
+    }
 
-    programData->fUniLocations.reset();
+    const GrGLInterface* gli = gl.interface();
+    GrGLint compiled = GR_GL_INIT_ZERO;
+    GR_GL_CALL(gli, ShaderSource(shader, stringCnt, strings, stringLengths));
+    GR_GL_CALL(gli, CompileShader(shader));
+    GR_GL_CALL(gli, GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
+
+    if (!compiled) {
+        GrGLint infoLen = GR_GL_INIT_ZERO;
+        GR_GL_CALL(gli, GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
+        SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
+        if (infoLen > 0) {
+            // retrieve length even though we don't need it to workaround bug in chrome cmd buffer
+            // param validation.
+            GrGLsizei length = GR_GL_INIT_ZERO;
+            GR_GL_CALL(gli, GetShaderInfoLog(shader, infoLen+1,
+                                             &length, (char*)log.get()));
+            print_shader(stringCnt, strings, stringLengths);
+            GrPrintf("\n%s", log.get());
+        }
+        GrAssert(!"Shader compilation failed!");
+        GR_GL_CALL(gli, DeleteShader(shader));
+        return 0;
+    }
+    return shader;
+}
+
+// helper version of above for when shader is already flattened into a single SkString
+GrGLuint compile_shader(const GrGLContextInfo& gl, GrGLenum type, const SkString& shader) {
+    const GrGLchar* str = shader.c_str();
+    int length = shader.size();
+    return compile_shader(gl, type, 1, &str, &length);
+}
+
+}
+
+// compiles all the shaders from builder and stores the shader IDs
+bool GrGLProgram::compileShaders(const GrGLShaderBuilder& builder) {
+
+    SkString shader;
+
+    builder.getShader(GrGLShaderBuilder::kVertex_ShaderType, &shader);
+    if (c_PrintShaders) {
+        GrPrintf(shader.c_str());
+        GrPrintf("\n");
+    }
+
+    if (!(fVShaderID = compile_shader(fContextInfo, GR_GL_VERTEX_SHADER, shader))) {
+        return false;
+    }
+
+    if (builder.fUsesGS) {
+        builder.getShader(GrGLShaderBuilder::kGeometry_ShaderType, &shader);
+        if (c_PrintShaders) {
+            GrPrintf(shader.c_str());
+            GrPrintf("\n");
+        }
+        if (!(fGShaderID = compile_shader(fContextInfo, GR_GL_GEOMETRY_SHADER, shader))) {
+            return false;
+        }
+    } else {
+        fGShaderID = 0;
+    }
+
+    builder.getShader(GrGLShaderBuilder::kFragment_ShaderType, &shader);
+    if (c_PrintShaders) {
+        GrPrintf(shader.c_str());
+        GrPrintf("\n");
+    }
+    if (!(fFShaderID = compile_shader(fContextInfo, GR_GL_FRAGMENT_SHADER, shader))) {
+        return false;
+    }
+
+    return true;
+}
+
+bool GrGLProgram::genProgram(const GrEffectStage* stages[]) {
+    GrAssert(0 == fProgramID);
+
+    GrGLShaderBuilder builder(fContextInfo, fUniformManager);
+    const uint32_t& layout = fDesc.fVertexLayout;
 
 #if GR_GL_EXPERIMENTAL_GS
-    segments.fUsesGS = fProgramDesc.fExperimentalGS;
+    builder.fUsesGS = fDesc.fExperimentalGS;
 #endif
 
     SkXfermode::Coeff colorCoeff, uniformCoeff;
-    bool applyColorMatrix = SkToBool(fProgramDesc.fColorMatrixEnabled);
     // The rest of transfer mode color filters have not been implemented
-    if (fProgramDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
+    if (fDesc.fColorFilterXfermode < SkXfermode::kCoeffModesCnt) {
         GR_DEBUGCODE(bool success =)
             SkXfermode::ModeAsCoeff(static_cast<SkXfermode::Mode>
-                                    (fProgramDesc.fColorFilterXfermode),
+                                    (fDesc.fColorFilterXfermode),
                                     &uniformCoeff, &colorCoeff);
         GR_DEBUGASSERT(success);
     } else {
@@ -668,19 +523,17 @@
         uniformCoeff = SkXfermode::kZero_Coeff;
     }
 
-    // no need to do the color filter / matrix at all if coverage is 0. The
-    // output color is scaled by the coverage. All the dual source outputs are
-    // scaled by the coverage as well.
-    if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fCoverageInput) {
+    // no need to do the color filter if coverage is 0. The output color is scaled by the coverage.
+    // All the dual source outputs are scaled by the coverage as well.
+    if (Desc::kTransBlack_ColorInput == fDesc.fCoverageInput) {
         colorCoeff = SkXfermode::kZero_Coeff;
         uniformCoeff = SkXfermode::kZero_Coeff;
-        applyColorMatrix = false;
     }
 
     // If we know the final color is going to be all zeros then we can
-    // simplify the color filter coeffecients. needComputedColor will then
+    // simplify the color filter coefficients. needComputedColor will then
     // come out false below.
-    if (ProgramDesc::kTransBlack_ColorInput == fProgramDesc.fColorInput) {
+    if (Desc::kTransBlack_ColorInput == fDesc.fColorInput) {
         colorCoeff = SkXfermode::kZero_Coeff;
         if (SkXfermode::kDC_Coeff == uniformCoeff ||
             SkXfermode::kDA_Coeff == uniformCoeff) {
@@ -693,61 +546,50 @@
 
     bool needColorFilterUniform;
     bool needComputedColor;
-    needBlendInputs(uniformCoeff, colorCoeff,
-                    &needColorFilterUniform, &needComputedColor);
+    need_blend_inputs(uniformCoeff, colorCoeff,
+                      &needColorFilterUniform, &needComputedColor);
 
     // the dual source output has no canonical var name, have to
     // declare an output, which is incompatible with gl_FragColor/gl_FragData.
     bool dualSourceOutputWritten = false;
-    segments.fHeader.printf(GrGetGLSLVersionDecl(gl.binding(),
-                                                 gl.glslGeneration()));
+    builder.fHeader.append(GrGetGLSLVersionDecl(fContextInfo.binding(),
+                                                fContextInfo.glslGeneration()));
 
     GrGLShaderVar colorOutput;
-    bool isColorDeclared = GrGLSLSetupFSColorOuput(gl.glslGeneration(),
+    bool isColorDeclared = GrGLSLSetupFSColorOuput(fContextInfo.glslGeneration(),
                                                    declared_color_output_name(),
                                                    &colorOutput);
     if (isColorDeclared) {
-        segments.fFSOutputs.push_back(colorOutput);
+        builder.fFSOutputs.push_back(colorOutput);
     }
 
-#if GR_GL_ATTRIBUTE_MATRICES
-    segments.fVSAttrs.push_back().set(GrGLShaderVar::kMat33f_Type,
-        GrGLShaderVar::kAttribute_TypeModifier, VIEW_MATRIX_NAME);
-    programData->fUniLocations.fViewMatrixUni = kSetAsAttribute;
-#else
-    segments.fVSUnis.push_back().set(GrGLShaderVar::kMat33f_Type,
-        GrGLShaderVar::kUniform_TypeModifier, VIEW_MATRIX_NAME);
-    programData->fUniLocations.fViewMatrixUni = kUseUniform;
-#endif
-    segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
-        GrGLShaderVar::kAttribute_TypeModifier, POS_ATTR_NAME);
+    const char* viewMName;
+    fUniformHandles.fViewMatrixUni = builder.addUniform(GrGLShaderBuilder::kVertex_ShaderType,
+                                                        kMat33f_GrSLType, "ViewM", &viewMName);
 
-    segments.fVSCode.append(
-        "void main() {\n"
-            "\tvec3 pos3 = " VIEW_MATRIX_NAME " * vec3("POS_ATTR_NAME", 1);\n"
-            "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n");
+
+    builder.fVSCode.appendf("\tvec3 pos3 = %s * vec3(%s, 1);\n"
+                            "\tgl_Position = vec4(pos3.xy, 0, pos3.z);\n",
+                            viewMName, builder.positionAttribute().getName().c_str());
 
     // incoming color to current stage being processed.
-    GrStringBuilder inColor;
+    SkString inColor;
 
     if (needComputedColor) {
-        genInputColor((ProgramDesc::ColorInput) fProgramDesc.fColorInput,
-                      programData, &segments, &inColor);
+        this->genInputColor(&builder, &inColor);
     }
 
     // we output point size in the GS if present
-    if (fProgramDesc.fEmitsPointSize && !segments.fUsesGS){
-        segments.fVSCode.append("\tgl_PointSize = 1.0;\n");
+    if (fDesc.fEmitsPointSize && !builder.fUsesGS){
+        builder.fVSCode.append("\tgl_PointSize = 1.0;\n");
     }
 
-    segments.fFSCode.append("void main() {\n");
-
     // add texture coordinates that are used to the list of vertex attr decls
-    GrStringBuilder texCoordAttrs[GrDrawState::kMaxTexCoords];
+    SkString texCoordAttrs[GrDrawState::kMaxTexCoords];
     for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        if (GrDrawTarget::VertexUsesTexCoordIdx(t, layout)) {
+        if (GrDrawState::VertexUsesTexCoordIdx(t, layout)) {
             tex_attr_name(t, texCoordAttrs + t);
-            segments.fVSAttrs.push_back().set(GrGLShaderVar::kVec2f_Type,
+            builder.fVSAttrs.push_back().set(kVec2f_GrSLType,
                 GrGLShaderVar::kAttribute_TypeModifier,
                 texCoordAttrs[t].c_str());
         }
@@ -759,35 +601,33 @@
     // if we have color stages string them together, feeding the output color
     // of each to the next and generating code for each stage.
     if (needComputedColor) {
-        GrStringBuilder outColor;
-        for (int s = 0; s < fProgramDesc.fFirstCoverageStage; ++s) {
-            if (fProgramDesc.fStages[s].isEnabled()) {
+        SkString outColor;
+        for (int s = 0; s < fDesc.fFirstCoverageStage; ++s) {
+            if (GrGLEffect::kNoEffectKey != fDesc.fEffectKeys[s]) {
                 // create var to hold stage result
                 outColor = "color";
                 outColor.appendS32(s);
-                segments.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
+                builder.fFSCode.appendf("\tvec4 %s;\n", outColor.c_str());
 
                 const char* inCoords;
                 // figure out what our input coords are
-                if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
-                    layout) {
-                    inCoords = POS_ATTR_NAME;
+                int tcIdx = GrDrawState::VertexTexCoordsForStage(s, layout);
+                if (tcIdx < 0) {
+                    inCoords = builder.positionAttribute().c_str();
                 } else {
-                    int tcIdx = GrDrawTarget::VertexTexCoordsForStage(s, layout);
-                     // we better have input tex coordinates if stage is enabled.
-                    GrAssert(tcIdx >= 0);
+                    // must have input tex coordinates if stage is enabled.
                     GrAssert(texCoordAttrs[tcIdx].size());
                     inCoords = texCoordAttrs[tcIdx].c_str();
                 }
 
-                this->genStageCode(gl,
-                                   s,
-                                   fProgramDesc.fStages[s],
-                                   inColor.size() ? inColor.c_str() : NULL,
-                                   outColor.c_str(),
-                                   inCoords,
-                                   &segments,
-                                   &programData->fUniLocations.fStages[s]);
+                builder.setCurrentStage(s);
+                fEffects[s] = builder.createAndEmitGLEffect(*stages[s],
+                                                            fDesc.fEffectKeys[s],
+                                                            inColor.size() ? inColor.c_str() : NULL,
+                                                            outColor.c_str(),
+                                                            inCoords,
+                                                            &fUniformHandles.fSamplerUnis[s]);
+                builder.setNonStage();
                 inColor = outColor;
             }
         }
@@ -796,134 +636,121 @@
     // if have all ones or zeros for the "dst" input to the color filter then we
     // may be able to make additional optimizations.
     if (needColorFilterUniform && needComputedColor && !inColor.size()) {
-        GrAssert(ProgramDesc::kSolidWhite_ColorInput == fProgramDesc.fColorInput);
+        GrAssert(Desc::kSolidWhite_ColorInput == fDesc.fColorInput);
         bool uniformCoeffIsZero = SkXfermode::kIDC_Coeff == uniformCoeff ||
                                   SkXfermode::kIDA_Coeff == uniformCoeff;
         if (uniformCoeffIsZero) {
             uniformCoeff = SkXfermode::kZero_Coeff;
             bool bogus;
-            needBlendInputs(SkXfermode::kZero_Coeff, colorCoeff,
-                            &needColorFilterUniform, &bogus);
+            need_blend_inputs(SkXfermode::kZero_Coeff, colorCoeff,
+                              &needColorFilterUniform, &bogus);
         }
     }
+    const char* colorFilterColorUniName = NULL;
     if (needColorFilterUniform) {
-        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
-                                         GrGLShaderVar::kUniform_TypeModifier,
-                                         COL_FILTER_UNI_NAME);
-        programData->fUniLocations.fColorFilterUni = kUseUniform;
+        fUniformHandles.fColorFilterUni = builder.addUniform(
+                                                        GrGLShaderBuilder::kFragment_ShaderType,
+                                                        kVec4f_GrSLType, "FilterColor",
+                                                        &colorFilterColorUniName);
     }
     bool wroteFragColorZero = false;
     if (SkXfermode::kZero_Coeff == uniformCoeff &&
-        SkXfermode::kZero_Coeff == colorCoeff &&
-        !applyColorMatrix) {
-        segments.fFSCode.appendf("\t%s = %s;\n",
-                                 colorOutput.getName().c_str(),
-                                 all_zeros_vec(4));
+        SkXfermode::kZero_Coeff == colorCoeff) {
+        builder.fFSCode.appendf("\t%s = %s;\n",
+                                colorOutput.getName().c_str(),
+                                GrGLSLZerosVecf(4));
         wroteFragColorZero = true;
-    } else if (SkXfermode::kDst_Mode != fProgramDesc.fColorFilterXfermode) {
-        segments.fFSCode.append("\tvec4 filteredColor;\n");
+    } else if (SkXfermode::kDst_Mode != fDesc.fColorFilterXfermode) {
+        builder.fFSCode.append("\tvec4 filteredColor;\n");
         const char* color = adjustInColor(inColor);
-        addColorFilter(&segments.fFSCode, "filteredColor", uniformCoeff,
-                       colorCoeff, color);
+        add_color_filter(&builder.fFSCode, "filteredColor", uniformCoeff,
+                       colorCoeff, colorFilterColorUniName, color);
         inColor = "filteredColor";
     }
-    if (applyColorMatrix) {
-        segments.fFSUnis.push_back().set(GrGLShaderVar::kMat44f_Type,
-                                         GrGLShaderVar::kUniform_TypeModifier,
-                                         COL_MATRIX_UNI_NAME);
-        segments.fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
-                                         GrGLShaderVar::kUniform_TypeModifier,
-                                         COL_MATRIX_VEC_UNI_NAME);
-        programData->fUniLocations.fColorMatrixUni = kUseUniform;
-        programData->fUniLocations.fColorMatrixVecUni = kUseUniform;
-        segments.fFSCode.append("\tvec4 matrixedColor;\n");
-        const char* color = adjustInColor(inColor);
-        addColorMatrix(&segments.fFSCode, "matrixedColor", color);
-        inColor = "matrixedColor";
-    }
 
     ///////////////////////////////////////////////////////////////////////////
     // compute the partial coverage (coverage stages and edge aa)
 
-    GrStringBuilder inCoverage;
-    bool coverageIsZero = ProgramDesc::kTransBlack_ColorInput ==
-                          fProgramDesc.fCoverageInput;
+    SkString inCoverage;
+    bool coverageIsZero = Desc::kTransBlack_ColorInput == fDesc.fCoverageInput;
     // we don't need to compute coverage at all if we know the final shader
     // output will be zero and we don't have a dual src blend output.
-    if (!wroteFragColorZero ||
-        ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
+    if (!wroteFragColorZero || Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
 
         if (!coverageIsZero) {
-            this->genEdgeCoverage(gl,
-                                  layout,
-                                  programData,
-                                  &inCoverage,
-                                  &segments);
+            bool inCoverageIsScalar  = this->genEdgeCoverage(&inCoverage, &builder);
 
-            switch (fProgramDesc.fCoverageInput) {
-                case ProgramDesc::kSolidWhite_ColorInput:
+            switch (fDesc.fCoverageInput) {
+                case Desc::kSolidWhite_ColorInput:
                     // empty string implies solid white
                     break;
-                case ProgramDesc::kAttribute_ColorInput:
-                    genAttributeCoverage(&segments, &inCoverage);
+                case Desc::kAttribute_ColorInput:
+                    gen_attribute_coverage(&builder, &inCoverage);
+                    inCoverageIsScalar = false;
                     break;
-                case ProgramDesc::kUniform_ColorInput:
-                    genUniformCoverage(&segments, programData, &inCoverage);
+                case Desc::kUniform_ColorInput:
+                    this->genUniformCoverage(&builder, &inCoverage);
+                    inCoverageIsScalar = false;
                     break;
                 default:
                     GrCrash("Unexpected input coverage.");
             }
 
-            GrStringBuilder outCoverage;
-            const int& startStage = fProgramDesc.fFirstCoverageStage;
+            SkString outCoverage;
+            const int& startStage = fDesc.fFirstCoverageStage;
             for (int s = startStage; s < GrDrawState::kNumStages; ++s) {
-                if (fProgramDesc.fStages[s].isEnabled()) {
+                if (fDesc.fEffectKeys[s]) {
                     // create var to hold stage output
                     outCoverage = "coverage";
                     outCoverage.appendS32(s);
-                    segments.fFSCode.appendf("\tvec4 %s;\n",
-                                             outCoverage.c_str());
+                    builder.fFSCode.appendf("\tvec4 %s;\n", outCoverage.c_str());
 
                     const char* inCoords;
                     // figure out what our input coords are
-                    if (GrDrawTarget::StagePosAsTexCoordVertexLayoutBit(s) &
-                        layout) {
-                        inCoords = POS_ATTR_NAME;
+                    int tcIdx =
+                        GrDrawState::VertexTexCoordsForStage(s, layout);
+                    if (tcIdx < 0) {
+                        inCoords = builder.positionAttribute().c_str();
                     } else {
-                        int tcIdx =
-                            GrDrawTarget::VertexTexCoordsForStage(s, layout);
-                        // we better have input tex coordinates if stage is
+                        // must have input tex coordinates if stage is
                         // enabled.
-                        GrAssert(tcIdx >= 0);
                         GrAssert(texCoordAttrs[tcIdx].size());
                         inCoords = texCoordAttrs[tcIdx].c_str();
                     }
 
-                    genStageCode(gl, s,
-                                 fProgramDesc.fStages[s],
-                                 inCoverage.size() ? inCoverage.c_str() : NULL,
-                                 outCoverage.c_str(),
-                                 inCoords,
-                                 &segments,
-                                 &programData->fUniLocations.fStages[s]);
+                    // stages don't know how to deal with a scalar input. (Maybe they should. We
+                    // could pass a GrGLShaderVar)
+                    if (inCoverageIsScalar) {
+                        builder.fFSCode.appendf("\tvec4 %s4 = vec4(%s);\n",
+                                                inCoverage.c_str(), inCoverage.c_str());
+                        inCoverage.append("4");
+                    }
+                    builder.setCurrentStage(s);
+                    fEffects[s] = builder.createAndEmitGLEffect(
+                                                    *stages[s],
+                                                    fDesc.fEffectKeys[s],
+                                                    inCoverage.size() ? inCoverage.c_str() : NULL,
+                                                    outCoverage.c_str(),
+                                                    inCoords,
+                                                    &fUniformHandles.fSamplerUnis[s]);
+                    builder.setNonStage();
                     inCoverage = outCoverage;
                 }
             }
         }
-        if (ProgramDesc::kNone_DualSrcOutput != fProgramDesc.fDualSrcOutput) {
-            segments.fFSOutputs.push_back().set(GrGLShaderVar::kVec4f_Type,
-                GrGLShaderVar::kOut_TypeModifier,
-                dual_source_output_name());
+
+        if (Desc::kNone_DualSrcOutput != fDesc.fDualSrcOutput) {
+            builder.fFSOutputs.push_back().set(kVec4f_GrSLType,
+                                               GrGLShaderVar::kOut_TypeModifier,
+                                               dual_source_output_name());
             bool outputIsZero = coverageIsZero;
-            GrStringBuilder coeff;
+            SkString coeff;
             if (!outputIsZero &&
-                ProgramDesc::kCoverage_DualSrcOutput !=
-                fProgramDesc.fDualSrcOutput && !wroteFragColorZero) {
+                Desc::kCoverage_DualSrcOutput != fDesc.fDualSrcOutput && !wroteFragColorZero) {
                 if (!inColor.size()) {
                     outputIsZero = true;
                 } else {
-                    if (fProgramDesc.fDualSrcOutput ==
-                        ProgramDesc::kCoverageISA_DualSrcOutput) {
+                    if (Desc::kCoverageISA_DualSrcOutput == fDesc.fDualSrcOutput) {
                         coeff.printf("(1 - %s.a)", inColor.c_str());
                     } else {
                         coeff.printf("(vec4(1,1,1,1) - %s)", inColor.c_str());
@@ -931,14 +758,13 @@
                 }
             }
             if (outputIsZero) {
-                segments.fFSCode.appendf("\t%s = %s;\n",
-                                         dual_source_output_name(),
-                                         all_zeros_vec(4));
+                builder.fFSCode.appendf("\t%s = %s;\n",
+                                        dual_source_output_name(),
+                                        GrGLSLZerosVecf(4));
             } else {
-                modulate_helper(dual_source_output_name(),
-                                coeff.c_str(),
-                                inCoverage.c_str(),
-                                &segments.fFSCode);
+                builder.fFSCode.appendf("\t%s =", dual_source_output_name());
+                GrGLSLModulate4f(&builder.fFSCode, coeff.c_str(), inCoverage.c_str());
+                builder.fFSCode.append(";\n");
             }
             dualSourceOutputWritten = true;
         }
@@ -949,1028 +775,151 @@
 
     if (!wroteFragColorZero) {
         if (coverageIsZero) {
-            segments.fFSCode.appendf("\t%s = %s;\n",
-                                     colorOutput.getName().c_str(),
-                                     all_zeros_vec(4));
+            builder.fFSCode.appendf("\t%s = %s;\n",
+                                    colorOutput.getName().c_str(),
+                                    GrGLSLZerosVecf(4));
         } else {
-            modulate_helper(colorOutput.getName().c_str(),
-                            inColor.c_str(),
-                            inCoverage.c_str(),
-                            &segments.fFSCode);
-        }
-        if (ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig ==
-            fProgramDesc.fOutputConfig) {
-            segments.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(floor(%s.rgb / %s.a * 255.0)/255.0, %s.a);\n",
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str());
-        } else if (ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig ==
-                   fProgramDesc.fOutputConfig) {
-            segments.fFSCode.appendf("\t%s = %s.a <= 0.0 ? vec4(0,0,0,0) : vec4(ceil(%s.rgb / %s.a * 255.0)/255.0, %s.a);\n",
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str(),
-                                        colorOutput.getName().c_str());
+            builder.fFSCode.appendf("\t%s = ", colorOutput.getName().c_str());
+            GrGLSLModulate4f(&builder.fFSCode, inColor.c_str(), inCoverage.c_str());
+            builder.fFSCode.append(";\n");
         }
     }
 
-    segments.fVSCode.append("}\n");
-    segments.fFSCode.append("}\n");
-
     ///////////////////////////////////////////////////////////////////////////
     // insert GS
 #if GR_DEBUG
-    this->genGeometryShader(gl, &segments);
+    this->genGeometryShader(&builder);
 #endif
 
     ///////////////////////////////////////////////////////////////////////////
     // compile and setup attribs and unis
 
-    if (!CompileShaders(gl, segments, programData)) {
+    if (!this->compileShaders(builder)) {
         return false;
     }
 
-    if (!this->bindOutputsAttribsAndLinkProgram(gl, texCoordAttrs,
+    if (!this->bindOutputsAttribsAndLinkProgram(builder,
+                                                texCoordAttrs,
                                                 isColorDeclared,
-                                                dualSourceOutputWritten,
-                                                programData)) {
+                                                dualSourceOutputWritten)) {
         return false;
     }
 
-    this->getUniformLocationsAndInitCache(gl, programData);
+    builder.finished(fProgramID);
+    this->initSamplerUniforms();
+    fUniformHandles.fRTHeightUni = builder.getRTHeightUniform();
 
     return true;
 }
 
-namespace {
-
-inline void expand_decls(const VarArray& vars,
-                         const GrGLContextInfo& gl,
-                         GrStringBuilder* string) {
-    const int count = vars.count();
-    for (int i = 0; i < count; ++i) {
-        vars[i].appendDecl(gl, string);
-    }
-}
-
-inline void print_shader(int stringCnt,
-                         const char** strings,
-                         int* stringLengths) {
-    for (int i = 0; i < stringCnt; ++i) {
-        if (NULL == stringLengths || stringLengths[i] < 0) {
-            GrPrintf(strings[i]);
-        } else {
-            GrPrintf("%.*s", stringLengths[i], strings[i]);
-        }
-    }
-}
-
-typedef SkTArray<const char*, true>         StrArray;
-#define PREALLOC_STR_ARRAY(N) SkSTArray<(N), const char*, true>
-
-typedef SkTArray<int, true>                 LengthArray;
-#define PREALLOC_LENGTH_ARRAY(N) SkSTArray<(N), int, true>
-
-// these shouldn't relocate
-typedef GrTAllocator<GrStringBuilder>       TempArray;
-#define PREALLOC_TEMP_ARRAY(N) GrSTAllocator<(N), GrStringBuilder>
-
-inline void append_string(const GrStringBuilder& str,
-                          StrArray* strings,
-                          LengthArray* lengths) {
-    int length = (int) str.size();
-    if (length) {
-        strings->push_back(str.c_str());
-        lengths->push_back(length);
-    }
-    GrAssert(strings->count() == lengths->count());
-}
-
-inline void append_decls(const VarArray& vars,
-                         const GrGLContextInfo& gl,
-                         StrArray* strings,
-                         LengthArray* lengths,
-                         TempArray* temp) {
-    expand_decls(vars, gl, &temp->push_back());
-    append_string(temp->back(), strings, lengths);
-}
-
-}
-
-bool GrGLProgram::CompileShaders(const GrGLContextInfo& gl,
-                                 const ShaderCodeSegments& segments,
-                                 CachedData* programData) {
-    enum { kPreAllocStringCnt = 8 };
-
-    PREALLOC_STR_ARRAY(kPreAllocStringCnt)    strs;
-    PREALLOC_LENGTH_ARRAY(kPreAllocStringCnt) lengths;
-    PREALLOC_TEMP_ARRAY(kPreAllocStringCnt)   temps;
-
-    GrStringBuilder unis;
-    GrStringBuilder inputs;
-    GrStringBuilder outputs;
-
-    append_string(segments.fHeader, &strs, &lengths);
-    append_decls(segments.fVSUnis, gl, &strs, &lengths, &temps);
-    append_decls(segments.fVSAttrs, gl, &strs, &lengths, &temps);
-    append_decls(segments.fVSOutputs, gl, &strs, &lengths, &temps);
-    append_string(segments.fVSCode, &strs, &lengths);
-
-#if PRINT_SHADERS
-    print_shader(strs.count(), &strs[0], &lengths[0]);
-    GrPrintf("\n");
-#endif
-
-    programData->fVShaderID =
-        CompileShader(gl, GR_GL_VERTEX_SHADER, strs.count(),
-                      &strs[0], &lengths[0]);
-
-    if (!programData->fVShaderID) {
-        return false;
-    }
-    if (segments.fUsesGS) {
-        strs.reset();
-        lengths.reset();
-        temps.reset();
-        append_string(segments.fHeader, &strs, &lengths);
-        append_string(segments.fGSHeader, &strs, &lengths);
-        append_decls(segments.fGSInputs, gl, &strs, &lengths, &temps);
-        append_decls(segments.fGSOutputs, gl, &strs, &lengths, &temps);
-        append_string(segments.fGSCode, &strs, &lengths);
-#if PRINT_SHADERS
-        print_shader(strs.count(), &strs[0], &lengths[0]);
-        GrPrintf("\n");
-#endif
-        programData->fGShaderID =
-            CompileShader(gl, GR_GL_GEOMETRY_SHADER, strs.count(),
-                          &strs[0], &lengths[0]);
-    } else {
-        programData->fGShaderID = 0;
-    }
-
-    strs.reset();
-    lengths.reset();
-    temps.reset();
-
-    append_string(segments.fHeader, &strs, &lengths);
-    GrStringBuilder precisionStr(GrGetGLSLShaderPrecisionDecl(gl.binding()));
-    append_string(precisionStr, &strs, &lengths);
-    append_decls(segments.fFSUnis, gl, &strs, &lengths, &temps);
-    append_decls(segments.fFSInputs, gl, &strs, &lengths, &temps);
-    // We shouldn't have declared outputs on 1.10
-    GrAssert(k110_GrGLSLGeneration != gl.glslGeneration() ||
-             segments.fFSOutputs.empty());
-    append_decls(segments.fFSOutputs, gl, &strs, &lengths, &temps);
-    append_string(segments.fFSFunctions, &strs, &lengths);
-    append_string(segments.fFSCode, &strs, &lengths);
-
-#if PRINT_SHADERS
-    print_shader(strs.count(), &strs[0], &lengths[0]);
-    GrPrintf("\n");
-#endif
-
-    programData->fFShaderID =
-        CompileShader(gl, GR_GL_FRAGMENT_SHADER, strs.count(),
-                      &strs[0], &lengths[0]);
-
-    if (!programData->fFShaderID) {
+bool GrGLProgram::bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& builder,
+                                                   SkString texCoordAttrNames[],
+                                                   bool bindColorOut,
+                                                   bool bindDualSrcOut) {
+    GL_CALL_RET(fProgramID, CreateProgram());
+    if (!fProgramID) {
         return false;
     }
 
-    return true;
-}
-
-#define GL_CALL(X) GR_GL_CALL(gl.interface(), X)
-#define GL_CALL_RET(R, X) GR_GL_CALL_RET(gl.interface(), R, X)
-
-GrGLuint GrGLProgram::CompileShader(const GrGLContextInfo& gl,
-                                    GrGLenum type,
-                                    int stringCnt,
-                                    const char** strings,
-                                    int* stringLengths) {
-    SK_TRACE_EVENT1("GrGLProgram::CompileShader",
-                    "stringCount", SkStringPrintf("%i", stringCnt).c_str());
-
-    GrGLuint shader;
-    GL_CALL_RET(shader, CreateShader(type));
-    if (0 == shader) {
-        return 0;
+    GL_CALL(AttachShader(fProgramID, fVShaderID));
+    if (fGShaderID) {
+        GL_CALL(AttachShader(fProgramID, fGShaderID));
     }
-
-    GrGLint compiled = GR_GL_INIT_ZERO;
-    GL_CALL(ShaderSource(shader, stringCnt, strings, stringLengths));
-    GL_CALL(CompileShader(shader));
-    GL_CALL(GetShaderiv(shader, GR_GL_COMPILE_STATUS, &compiled));
-
-    if (!compiled) {
-        GrGLint infoLen = GR_GL_INIT_ZERO;
-        GL_CALL(GetShaderiv(shader, GR_GL_INFO_LOG_LENGTH, &infoLen));
-        SkAutoMalloc log(sizeof(char)*(infoLen+1)); // outside if for debugger
-        if (infoLen > 0) {
-            // retrieve length even though we don't need it to workaround
-            // bug in chrome cmd buffer param validation.
-            GrGLsizei length = GR_GL_INIT_ZERO;
-            GL_CALL(GetShaderInfoLog(shader, infoLen+1, 
-                                         &length, (char*)log.get()));
-            print_shader(stringCnt, strings, stringLengths);
-            GrPrintf("\n%s", log.get());
-        }
-        GrAssert(!"Shader compilation failed!");
-        GL_CALL(DeleteShader(shader));
-        return 0;
-    }
-    return shader;
-}
-
-bool GrGLProgram::bindOutputsAttribsAndLinkProgram(
-                                        const GrGLContextInfo& gl,
-                                        GrStringBuilder texCoordAttrNames[],
-                                        bool bindColorOut,
-                                        bool bindDualSrcOut,
-                                        CachedData* programData) const {
-    GL_CALL_RET(programData->fProgramID, CreateProgram());
-    if (!programData->fProgramID) {
-        return false;
-    }
-    const GrGLint& progID = programData->fProgramID;
-
-    GL_CALL(AttachShader(progID, programData->fVShaderID));
-    if (programData->fGShaderID) {
-        GL_CALL(AttachShader(progID, programData->fGShaderID));
-    }
-    GL_CALL(AttachShader(progID, programData->fFShaderID));
+    GL_CALL(AttachShader(fProgramID, fFShaderID));
 
     if (bindColorOut) {
-        GL_CALL(BindFragDataLocation(programData->fProgramID,
-                                     0, declared_color_output_name()));
+        GL_CALL(BindFragDataLocation(fProgramID, 0, declared_color_output_name()));
     }
     if (bindDualSrcOut) {
-        GL_CALL(BindFragDataLocationIndexed(programData->fProgramID,
-                                            0, 1, dual_source_output_name()));
+        GL_CALL(BindFragDataLocationIndexed(fProgramID, 0, 1, dual_source_output_name()));
     }
 
     // Bind the attrib locations to same values for all shaders
-    GL_CALL(BindAttribLocation(progID, PositionAttributeIdx(), POS_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID,
+                               PositionAttributeIdx(),
+                               builder.positionAttribute().c_str()));
     for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
         if (texCoordAttrNames[t].size()) {
-            GL_CALL(BindAttribLocation(progID,
+            GL_CALL(BindAttribLocation(fProgramID,
                                        TexCoordAttributeIdx(t),
                                        texCoordAttrNames[t].c_str()));
         }
     }
 
-    if (kSetAsAttribute == programData->fUniLocations.fViewMatrixUni) {
-        GL_CALL(BindAttribLocation(progID,
-                                   ViewMatrixAttributeIdx(),
-                                   VIEW_MATRIX_NAME));
-    }
+    GL_CALL(BindAttribLocation(fProgramID, ColorAttributeIdx(), COL_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, CoverageAttributeIdx(), COV_ATTR_NAME));
+    GL_CALL(BindAttribLocation(fProgramID, EdgeAttributeIdx(), EDGE_ATTR_NAME));
 
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        const StageUniLocations& unis = programData->fUniLocations.fStages[s];
-        if (kSetAsAttribute == unis.fTextureMatrixUni) {
-            GrStringBuilder matName;
-            tex_matrix_name(s, &matName);
-            GL_CALL(BindAttribLocation(progID,
-                                       TextureMatrixAttributeIdx(s),
-                                       matName.c_str()));
-        }
-    }
-
-    GL_CALL(BindAttribLocation(progID, ColorAttributeIdx(), COL_ATTR_NAME));
-    GL_CALL(BindAttribLocation(progID, CoverageAttributeIdx(), COV_ATTR_NAME));
-    GL_CALL(BindAttribLocation(progID, EdgeAttributeIdx(), EDGE_ATTR_NAME));
-
-    GL_CALL(LinkProgram(progID));
+    GL_CALL(LinkProgram(fProgramID));
 
     GrGLint linked = GR_GL_INIT_ZERO;
-    GL_CALL(GetProgramiv(progID, GR_GL_LINK_STATUS, &linked));
+    GL_CALL(GetProgramiv(fProgramID, GR_GL_LINK_STATUS, &linked));
     if (!linked) {
         GrGLint infoLen = GR_GL_INIT_ZERO;
-        GL_CALL(GetProgramiv(progID, GR_GL_INFO_LOG_LENGTH, &infoLen));
+        GL_CALL(GetProgramiv(fProgramID, GR_GL_INFO_LOG_LENGTH, &infoLen));
         SkAutoMalloc log(sizeof(char)*(infoLen+1));  // outside if for debugger
         if (infoLen > 0) {
             // retrieve length even though we don't need it to workaround
             // bug in chrome cmd buffer param validation.
             GrGLsizei length = GR_GL_INIT_ZERO;
-            GL_CALL(GetProgramInfoLog(progID,
+            GL_CALL(GetProgramInfoLog(fProgramID,
                                       infoLen+1,
                                       &length,
                                       (char*)log.get()));
             GrPrintf((char*)log.get());
         }
         GrAssert(!"Error linking program");
-        GL_CALL(DeleteProgram(progID));
-        programData->fProgramID = 0;
+        GL_CALL(DeleteProgram(fProgramID));
+        fProgramID = 0;
         return false;
     }
     return true;
 }
 
-void GrGLProgram::getUniformLocationsAndInitCache(const GrGLContextInfo& gl,
-                                                  CachedData* programData) const {
-    const GrGLint& progID = programData->fProgramID;
-
-    if (kUseUniform == programData->fUniLocations.fViewMatrixUni) {
-        GL_CALL_RET(programData->fUniLocations.fViewMatrixUni,
-                    GetUniformLocation(progID, VIEW_MATRIX_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fViewMatrixUni);
-    }
-    if (kUseUniform == programData->fUniLocations.fColorUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorUni,
-                    GetUniformLocation(progID, COL_UNI_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fColorUni);
-    }
-    if (kUseUniform == programData->fUniLocations.fColorFilterUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorFilterUni, 
-                    GetUniformLocation(progID, COL_FILTER_UNI_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fColorFilterUni);
-    }
-
-    if (kUseUniform == programData->fUniLocations.fColorMatrixUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorMatrixUni,
-                    GetUniformLocation(progID, COL_MATRIX_UNI_NAME));
-    }
-
-    if (kUseUniform == programData->fUniLocations.fColorMatrixVecUni) {
-        GL_CALL_RET(programData->fUniLocations.fColorMatrixVecUni,
-                    GetUniformLocation(progID, COL_MATRIX_VEC_UNI_NAME));
-    }
-    if (kUseUniform == programData->fUniLocations.fCoverageUni) {
-        GL_CALL_RET(programData->fUniLocations.fCoverageUni,
-                    GetUniformLocation(progID, COV_UNI_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fCoverageUni);
-    }
-
-    if (kUseUniform == programData->fUniLocations.fEdgesUni) {
-        GL_CALL_RET(programData->fUniLocations.fEdgesUni,
-                    GetUniformLocation(progID, EDGES_UNI_NAME));
-        GrAssert(kUnusedUniform != programData->fUniLocations.fEdgesUni);
-    } else {
-        programData->fUniLocations.fEdgesUni = kUnusedUniform;
-    }
-
+void GrGLProgram::initSamplerUniforms() {
+    GL_CALL(UseProgram(fProgramID));
+    // We simply bind the uniforms to successive texture units beginning at 0. setData() assumes this
+    // behavior.
+    GrGLint texUnitIdx = 0;
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        StageUniLocations& locations = programData->fUniLocations.fStages[s];
-        if (fProgramDesc.fStages[s].isEnabled()) {
-            if (kUseUniform == locations.fTextureMatrixUni) {
-                GrStringBuilder texMName;
-                tex_matrix_name(s, &texMName);
-                GL_CALL_RET(locations.fTextureMatrixUni,
-                            GetUniformLocation(progID, texMName.c_str()));
-                GrAssert(kUnusedUniform != locations.fTextureMatrixUni);
-            }
-
-            if (kUseUniform == locations.fSamplerUni) {
-                GrStringBuilder samplerName;
-                sampler_name(s, &samplerName);
-                GL_CALL_RET(locations.fSamplerUni,
-                            GetUniformLocation(progID,samplerName.c_str()));
-                GrAssert(kUnusedUniform != locations.fSamplerUni);
-            }
-
-            if (kUseUniform == locations.fNormalizedTexelSizeUni) {
-                GrStringBuilder texelSizeName;
-                normalized_texel_size_name(s, &texelSizeName);
-                GL_CALL_RET(locations.fNormalizedTexelSizeUni,
-                            GetUniformLocation(progID, texelSizeName.c_str()));
-                GrAssert(kUnusedUniform != locations.fNormalizedTexelSizeUni);
-            }
-
-            if (kUseUniform == locations.fRadial2Uni) {
-                GrStringBuilder radial2ParamName;
-                radial2_param_name(s, &radial2ParamName);
-                GL_CALL_RET(locations.fRadial2Uni,
-                            GetUniformLocation(progID, radial2ParamName.c_str()));
-                GrAssert(kUnusedUniform != locations.fRadial2Uni);
-            }
-
-            if (kUseUniform == locations.fTexDomUni) {
-                GrStringBuilder texDomName;
-                tex_domain_name(s, &texDomName);
-                GL_CALL_RET(locations.fTexDomUni,
-                            GetUniformLocation(progID, texDomName.c_str()));
-                GrAssert(kUnusedUniform != locations.fTexDomUni);
-            }
-
-            GrStringBuilder kernelName, imageIncrementName;
-            convolve_param_names(s, &kernelName, &imageIncrementName);
-            if (kUseUniform == locations.fKernelUni) {
-                GL_CALL_RET(locations.fKernelUni,
-                            GetUniformLocation(progID, kernelName.c_str()));
-                GrAssert(kUnusedUniform != locations.fKernelUni);
-            }
-
-            if (kUseUniform == locations.fImageIncrementUni) {
-                GL_CALL_RET(locations.fImageIncrementUni, 
-                            GetUniformLocation(progID, 
-                                               imageIncrementName.c_str()));
-                GrAssert(kUnusedUniform != locations.fImageIncrementUni);
+        int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+        for (int u = 0; u < numSamplers; ++u) {
+            UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+            if (GrGLUniformManager::kInvalidUniformHandle != handle) {
+                fUniformManager.setSampler(handle, texUnitIdx);
+                ++texUnitIdx;
             }
         }
     }
-    GL_CALL(UseProgram(progID));
+}
 
-    // init sampler unis and set bogus values for state tracking
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGLProgram::setData(GrGpuGL* gpu) {
+    const GrDrawState& drawState = gpu->getDrawState();
+
+    int rtHeight = drawState.getRenderTarget()->height();
+    if (GrGLUniformManager::kInvalidUniformHandle != fUniformHandles.fRTHeightUni &&
+        fRTHeight != rtHeight) {
+        fUniformManager.set1f(fUniformHandles.fRTHeightUni, SkIntToScalar(rtHeight));
+        fRTHeight = rtHeight;
+    }
+    GrGLint texUnitIdx = 0;
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (kUnusedUniform != programData->fUniLocations.fStages[s].fSamplerUni) {
-            GL_CALL(Uniform1i(programData->fUniLocations.fStages[s].fSamplerUni, s));
-        }
-        programData->fTextureMatrices[s] = GrMatrix::InvalidMatrix();
-        programData->fRadial2CenterX1[s] = GR_ScalarMax;
-        programData->fRadial2Radius0[s] = -GR_ScalarMax;
-        programData->fTextureWidth[s] = -1;
-        programData->fTextureHeight[s] = -1;
-    }
-    programData->fViewMatrix = GrMatrix::InvalidMatrix();
-    programData->fColor = GrColor_ILLEGAL;
-    programData->fColorFilterColor = GrColor_ILLEGAL;
-}
-
-//============================================================================
-// Stage code generation
-//============================================================================
-
-namespace {
-
-bool isRadialMapping(GrGLProgram::StageDesc::CoordMapping mapping) {
-    return
-       (GrGLProgram::StageDesc::kRadial2Gradient_CoordMapping == mapping ||
-        GrGLProgram::StageDesc::kRadial2GradientDegenerate_CoordMapping == mapping);
-}
-
-GrGLShaderVar* genRadialVS(int stageNum,
-                        ShaderCodeSegments* segments,
-                        GrGLProgram::StageUniLocations* locations,
-                        const char** radial2VaryingVSName,
-                        const char** radial2VaryingFSName,
-                        const char* varyingVSName,
-                        int varyingDims, int coordDims) {
-
-    GrGLShaderVar* radial2FSParams = &segments->fFSUnis.push_back();
-    radial2FSParams->setType(GrGLShaderVar::kFloat_Type);
-    radial2FSParams->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
-    radial2FSParams->setArrayCount(6);
-    radial2_param_name(stageNum, radial2FSParams->accessName());
-    segments->fVSUnis.push_back(*radial2FSParams).setEmitPrecision(true);
-
-    locations->fRadial2Uni = kUseUniform;
-
-    // for radial grads without perspective we can pass the linear
-    // part of the quadratic as a varying.
-    if (varyingDims == coordDims) {
-        GrAssert(2 == coordDims);
-        append_varying(GrGLShaderVar::kFloat_Type,
-                       "Radial2BCoeff",
-                       stageNum,
-                       segments,
-                       radial2VaryingVSName,
-                       radial2VaryingFSName);
-
-        GrStringBuilder radial2p2;
-        GrStringBuilder radial2p3;
-        radial2FSParams->appendArrayAccess(2, &radial2p2);
-        radial2FSParams->appendArrayAccess(3, &radial2p3);
-
-        // r2Var = 2 * (r2Parm[2] * varCoord.x - r2Param[3])
-        const char* r2ParamName = radial2FSParams->getName().c_str();
-        segments->fVSCode.appendf("\t%s = 2.0 *(%s * %s.x - %s);\n",
-                                  *radial2VaryingVSName, radial2p2.c_str(),
-                                  varyingVSName, radial2p3.c_str());
-    }
-
-    return radial2FSParams;
-}
-
-bool genRadial2GradientCoordMapping(int stageNum,
-                                    ShaderCodeSegments* segments,
-                                    const char* radial2VaryingFSName,
-                                    GrGLShaderVar* radial2Params,
-                                    GrStringBuilder& sampleCoords,
-                                    GrStringBuilder& fsCoordName,
-                                    int varyingDims,
-                                    int coordDims) {
-    GrStringBuilder cName("c");
-    GrStringBuilder ac4Name("ac4");
-    GrStringBuilder rootName("root");
-
-    cName.appendS32(stageNum);
-    ac4Name.appendS32(stageNum);
-    rootName.appendS32(stageNum);
-
-    GrStringBuilder radial2p0;
-    GrStringBuilder radial2p1;
-    GrStringBuilder radial2p2;
-    GrStringBuilder radial2p3;
-    GrStringBuilder radial2p4;
-    GrStringBuilder radial2p5;
-    radial2Params->appendArrayAccess(0, &radial2p0);
-    radial2Params->appendArrayAccess(1, &radial2p1);
-    radial2Params->appendArrayAccess(2, &radial2p2);
-    radial2Params->appendArrayAccess(3, &radial2p3);
-    radial2Params->appendArrayAccess(4, &radial2p4);
-    radial2Params->appendArrayAccess(5, &radial2p5);
-
-    // if we were able to interpolate the linear component bVar is the varying
-    // otherwise compute it
-    GrStringBuilder bVar;
-    if (coordDims == varyingDims) {
-        bVar = radial2VaryingFSName;
-        GrAssert(2 == varyingDims);
-    } else {
-        GrAssert(3 == varyingDims);
-        bVar = "b";
-        bVar.appendS32(stageNum);
-        segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
-                                    bVar.c_str(), radial2p2.c_str(),
-                                    fsCoordName.c_str(), radial2p3.c_str());
-    }
-
-    // c = (x^2)+(y^2) - params[4]
-    segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s;\n",
-                              cName.c_str(), fsCoordName.c_str(),
-                              fsCoordName.c_str(),
-                              radial2p4.c_str());
-    // ac4 = 4.0 * params[0] * c
-    segments->fFSCode.appendf("\tfloat %s = %s * 4.0 * %s;\n",
-                              ac4Name.c_str(), radial2p0.c_str(),
-                              cName.c_str());
-
-    // root = sqrt(b^2-4ac)
-    // (abs to avoid exception due to fp precision)
-    segments->fFSCode.appendf("\tfloat %s = sqrt(abs(%s*%s - %s));\n",
-                              rootName.c_str(), bVar.c_str(), bVar.c_str(),
-                              ac4Name.c_str());
-
-    // x coord is: (-b + params[5] * sqrt(b^2-4ac)) * params[1]
-    // y coord is 0.5 (texture is effectively 1D)
-    sampleCoords.printf("vec2((-%s + %s * %s) * %s, 0.5)",
-                        bVar.c_str(), radial2p5.c_str(),
-                        rootName.c_str(), radial2p1.c_str());
-    return true;
-}
-
-bool genRadial2GradientDegenerateCoordMapping(int stageNum,
-                                              ShaderCodeSegments* segments,
-                                              const char* radial2VaryingFSName,
-                                              GrGLShaderVar* radial2Params,
-                                              GrStringBuilder& sampleCoords,
-                                              GrStringBuilder& fsCoordName,
-                                              int varyingDims,
-                                              int coordDims) {
-    GrStringBuilder cName("c");
-
-    cName.appendS32(stageNum);
-
-    GrStringBuilder radial2p2;
-    GrStringBuilder radial2p3;
-    GrStringBuilder radial2p4;
-    radial2Params->appendArrayAccess(2, &radial2p2);
-    radial2Params->appendArrayAccess(3, &radial2p3);
-    radial2Params->appendArrayAccess(4, &radial2p4);
-
-    // if we were able to interpolate the linear component bVar is the varying
-    // otherwise compute it
-    GrStringBuilder bVar;
-    if (coordDims == varyingDims) {
-        bVar = radial2VaryingFSName;
-        GrAssert(2 == varyingDims);
-    } else {
-        GrAssert(3 == varyingDims);
-        bVar = "b";
-        bVar.appendS32(stageNum);
-        segments->fFSCode.appendf("\tfloat %s = 2.0 * (%s * %s.x - %s);\n",
-                                    bVar.c_str(), radial2p2.c_str(),
-                                    fsCoordName.c_str(), radial2p3.c_str());
-    }
-
-    // c = (x^2)+(y^2) - params[4]
-    segments->fFSCode.appendf("\tfloat %s = dot(%s, %s) - %s;\n",
-                              cName.c_str(), fsCoordName.c_str(),
-                              fsCoordName.c_str(),
-                              radial2p4.c_str());
-
-    // x coord is: -c/b
-    // y coord is 0.5 (texture is effectively 1D)
-    sampleCoords.printf("vec2((-%s / %s), 0.5)", cName.c_str(), bVar.c_str());
-    return true;
-}
-
-void gen2x2FS(int stageNum,
-              ShaderCodeSegments* segments,
-              GrGLProgram::StageUniLocations* locations,
-              GrStringBuilder* sampleCoords,
-              const char* samplerName,
-              const char* texelSizeName,
-              const char* swizzle,
-              const char* fsOutColor,
-              GrStringBuilder& texFunc,
-              GrStringBuilder& modulate,
-              bool complexCoord,
-              int coordDims) {
-    locations->fNormalizedTexelSizeUni = kUseUniform;
-    if (complexCoord) {
-        // assign the coord to a var rather than compute 4x.
-        GrStringBuilder coordVar("tCoord");
-        coordVar.appendS32(stageNum);
-        segments->fFSCode.appendf("\t%s %s = %s;\n",
-                            float_vector_type_str(coordDims),
-                            coordVar.c_str(), sampleCoords->c_str());
-        *sampleCoords = coordVar;
-    }
-    GrAssert(2 == coordDims);
-    GrStringBuilder accumVar("accum");
-    accumVar.appendS32(stageNum);
-    segments->fFSCode.appendf("\tvec4 %s  = %s(%s, %s + vec2(-%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
-    segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,-%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
-    segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(-%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
-    segments->fFSCode.appendf("\t%s += %s(%s, %s + vec2(+%s.x,+%s.y))%s;\n", accumVar.c_str(), texFunc.c_str(), samplerName, sampleCoords->c_str(), texelSizeName, texelSizeName, swizzle);
-    segments->fFSCode.appendf("\t%s = .25 * %s%s;\n", fsOutColor, accumVar.c_str(), modulate.c_str());
-
-}
-
-void genConvolutionVS(int stageNum,
-                      const StageDesc& desc,
-                      ShaderCodeSegments* segments,
-                      GrGLProgram::StageUniLocations* locations,
-                      GrGLShaderVar** kernel,
-                      const char** imageIncrementName,
-                      const char* varyingVSName) {
-    //GrGLShaderVar* kernel = &segments->fFSUnis.push_back();
-    *kernel = &segments->fFSUnis.push_back();
-    (*kernel)->setType(GrGLShaderVar::kFloat_Type);
-    (*kernel)->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
-    (*kernel)->setArrayCount(desc.fKernelWidth);
-    GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
-    imgInc->setType(GrGLShaderVar::kVec2f_Type);
-    imgInc->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
-
-    convolve_param_names(stageNum,
-                         (*kernel)->accessName(),
-                         imgInc->accessName());
-    *imageIncrementName = imgInc->getName().c_str();
-
-    // need image increment in both VS and FS
-    segments->fVSUnis.push_back(*imgInc).setEmitPrecision(true);
-
-    locations->fKernelUni = kUseUniform;
-    locations->fImageIncrementUni = kUseUniform;
-    float scale = (desc.fKernelWidth - 1) * 0.5f;
-    segments->fVSCode.appendf("\t%s -= vec2(%g, %g) * %s;\n",
-                                  varyingVSName, scale, scale,
-                                  *imageIncrementName);
-}
-
-void genConvolutionFS(int stageNum,
-                      const StageDesc& desc,
-                      ShaderCodeSegments* segments,
-                      const char* samplerName,
-                      GrGLShaderVar* kernel,
-                      const char* swizzle,
-                      const char* imageIncrementName,
-                      const char* fsOutColor,
-                      GrStringBuilder& sampleCoords,
-                      GrStringBuilder& texFunc,
-                      GrStringBuilder& modulate) {
-    GrStringBuilder sumVar("sum");
-    sumVar.appendS32(stageNum);
-    GrStringBuilder coordVar("coord");
-    coordVar.appendS32(stageNum);
-
-    GrStringBuilder kernelIndex;
-    kernel->appendArrayAccess("i", &kernelIndex);
-
-    segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n",
-                              sumVar.c_str());
-    segments->fFSCode.appendf("\tvec2 %s = %s;\n", 
-                              coordVar.c_str(),
-                              sampleCoords.c_str());
-    segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n",
-                              desc.fKernelWidth);
-    segments->fFSCode.appendf("\t\t%s += %s(%s, %s)%s * %s;\n",
-                              sumVar.c_str(), texFunc.c_str(),
-                              samplerName, coordVar.c_str(), swizzle,
-                              kernelIndex.c_str());
-    segments->fFSCode.appendf("\t\t%s += %s;\n",
-                              coordVar.c_str(),
-                              imageIncrementName);
-    segments->fFSCode.append("\t}\n");
-    segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
-                              sumVar.c_str(), modulate.c_str());
-}
- 
-void genMorphologyVS(int stageNum,
-                     const StageDesc& desc,
-                     ShaderCodeSegments* segments,
-                     GrGLProgram::StageUniLocations* locations,
-                     const char** imageIncrementName,
-                     const char* varyingVSName) {
-    GrGLShaderVar* imgInc = &segments->fFSUnis.push_back();
-    imgInc->setType(GrGLShaderVar::kVec2f_Type);
-    imgInc->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
-
-    image_increment_param_name(stageNum, imgInc->accessName());
-    *imageIncrementName = imgInc->getName().c_str();
-
-    // need image increment in both VS and FS
-    segments->fVSUnis.push_back(*imgInc).setEmitPrecision(true);
-
-    locations->fImageIncrementUni = kUseUniform;
-    segments->fVSCode.appendf("\t%s -= vec2(%d, %d) * %s;\n",
-                                  varyingVSName, desc.fKernelWidth,
-                                  desc.fKernelWidth, *imageIncrementName);
-}
- 
-void genMorphologyFS(int stageNum,
-                     const StageDesc& desc,
-                     ShaderCodeSegments* segments,
-                     const char* samplerName,
-                     const char* swizzle,
-                     const char* imageIncrementName,
-                     const char* fsOutColor,
-                     GrStringBuilder& sampleCoords,
-                     GrStringBuilder& texFunc,
-                     GrStringBuilder& modulate) {
-    GrStringBuilder valueVar("value");
-    valueVar.appendS32(stageNum);
-    GrStringBuilder coordVar("coord");
-    coordVar.appendS32(stageNum);
-    bool isDilate = StageDesc::kDilate_FetchMode == desc.fFetchMode;
-
-   if (isDilate) {
-        segments->fFSCode.appendf("\tvec4 %s = vec4(0, 0, 0, 0);\n",
-                                  valueVar.c_str());
-    } else {
-        segments->fFSCode.appendf("\tvec4 %s = vec4(1, 1, 1, 1);\n",
-                                  valueVar.c_str());
-    }
-    segments->fFSCode.appendf("\tvec2 %s = %s;\n", 
-                              coordVar.c_str(),
-                              sampleCoords.c_str());
-    segments->fFSCode.appendf("\tfor (int i = 0; i < %d; i++) {\n",
-                              desc.fKernelWidth * 2 + 1);
-    segments->fFSCode.appendf("\t\t%s = %s(%s, %s(%s, %s)%s);\n",
-                              valueVar.c_str(), isDilate ? "max" : "min",
-                              valueVar.c_str(), texFunc.c_str(),
-                              samplerName, coordVar.c_str(), swizzle);
-    segments->fFSCode.appendf("\t\t%s += %s;\n",
-                              coordVar.c_str(),
-                              imageIncrementName);
-    segments->fFSCode.appendf("\t}\n");
-    segments->fFSCode.appendf("\t%s = %s%s;\n", fsOutColor,
-                              valueVar.c_str(), modulate.c_str());
-}
-
-}
-
-void GrGLProgram::genStageCode(const GrGLContextInfo& gl,
-                               int stageNum,
-                               const GrGLProgram::StageDesc& desc,
-                               const char* fsInColor, // NULL means no incoming color
-                               const char* fsOutColor,
-                               const char* vsInCoord,
-                               ShaderCodeSegments* segments,
-                               StageUniLocations* locations) const {
-
-    GrAssert(stageNum >= 0 && stageNum <= GrDrawState::kNumStages);
-    GrAssert((desc.fInConfigFlags & StageDesc::kInConfigBitMask) ==
-             desc.fInConfigFlags);
-
-    // First decide how many coords are needed to access the texture
-    // Right now it's always 2 but we could start using 1D textures for
-    // gradients.
-    static const int coordDims = 2;
-    int varyingDims;
-    /// Vertex Shader Stuff
-
-    // decide whether we need a matrix to transform texture coords
-    // and whether the varying needs a perspective coord.
-    const char* matName = NULL;
-    if (desc.fOptFlags & StageDesc::kIdentityMatrix_OptFlagBit) {
-        varyingDims = coordDims;
-    } else {
-        GrGLShaderVar* mat;
-    #if GR_GL_ATTRIBUTE_MATRICES
-        mat = &segments->fVSAttrs.push_back();
-        mat->setTypeModifier(GrGLShaderVar::kAttribute_TypeModifier);
-        locations->fTextureMatrixUni = kSetAsAttribute;
-    #else
-        mat = &segments->fVSUnis.push_back();
-        mat->setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
-        locations->fTextureMatrixUni = kUseUniform;
-    #endif
-        tex_matrix_name(stageNum, mat->accessName());
-        mat->setType(GrGLShaderVar::kMat33f_Type);
-        matName = mat->getName().c_str();
-
-        if (desc.fOptFlags & StageDesc::kNoPerspective_OptFlagBit) {
-            varyingDims = coordDims;
-        } else {
-            varyingDims = coordDims + 1;
-        }
-    }
-
-    segments->fFSUnis.push_back().set(GrGLShaderVar::kSampler2D_Type,
-        GrGLShaderVar::kUniform_TypeModifier, "");
-    sampler_name(stageNum, segments->fFSUnis.back().accessName());
-    locations->fSamplerUni = kUseUniform;
-    const char* samplerName = segments->fFSUnis.back().getName().c_str();
-
-    const char* texelSizeName = NULL;
-    if (StageDesc::k2x2_FetchMode == desc.fFetchMode) {
-        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec2f_Type,
-            GrGLShaderVar::kUniform_TypeModifier, "");
-        normalized_texel_size_name(stageNum, segments->fFSUnis.back().accessName());
-        texelSizeName = segments->fFSUnis.back().getName().c_str();
-    }
-
-    const char *varyingVSName, *varyingFSName;
-    append_varying(float_vector_type(varyingDims),
-                    "Stage",
-                   stageNum,
-                   segments,
-                   &varyingVSName,
-                   &varyingFSName);
-
-    if (!matName) {
-        GrAssert(varyingDims == coordDims);
-        segments->fVSCode.appendf("\t%s = %s;\n", varyingVSName, vsInCoord);
-    } else {
-        // varying = texMatrix * texCoord
-        segments->fVSCode.appendf("\t%s = (%s * vec3(%s, 1))%s;\n",
-                                  varyingVSName, matName, vsInCoord,
-                                  vector_all_coords(varyingDims));
-    }
-
-    GrGLShaderVar* radial2Params = NULL;
-    const char* radial2VaryingVSName = NULL;
-    const char* radial2VaryingFSName = NULL;
-
-    if (isRadialMapping((StageDesc::CoordMapping) desc.fCoordMapping)) {
-        radial2Params = genRadialVS(stageNum, segments,
-                                    locations,
-                                    &radial2VaryingVSName,
-                                    &radial2VaryingFSName,
-                                    varyingVSName,
-                                    varyingDims, coordDims);
-    }
-
-    GrGLShaderVar* kernel = NULL;
-    const char* imageIncrementName = NULL;
-    if (StageDesc::kConvolution_FetchMode == desc.fFetchMode) {
-        genConvolutionVS(stageNum, desc, segments, locations,
-                         &kernel, &imageIncrementName, varyingVSName);
-    } else if (StageDesc::kDilate_FetchMode == desc.fFetchMode ||
-               StageDesc::kErode_FetchMode == desc.fFetchMode) {
-        genMorphologyVS(stageNum, desc, segments, locations,
-                        &imageIncrementName, varyingVSName);
-    }
-
-    /// Fragment Shader Stuff
-    GrStringBuilder fsCoordName;
-    // function used to access the shader, may be made projective
-    GrStringBuilder texFunc("texture2D");
-    if (desc.fOptFlags & (StageDesc::kIdentityMatrix_OptFlagBit |
-                          StageDesc::kNoPerspective_OptFlagBit)) {
-        GrAssert(varyingDims == coordDims);
-        fsCoordName = varyingFSName;
-    } else {
-        // if we have to do some special op on the varyings to get
-        // our final tex coords then when in perspective we have to
-        // do an explicit divide. Otherwise, we can use a Proj func.
-        if  (StageDesc::kIdentity_CoordMapping == desc.fCoordMapping &&
-             StageDesc::kSingle_FetchMode == desc.fFetchMode) {
-            texFunc.append("Proj");
-            fsCoordName = varyingFSName;
-        } else {
-            fsCoordName = "inCoord";
-            fsCoordName.appendS32(stageNum);
-            segments->fFSCode.appendf("\t%s %s = %s%s / %s%s;\n",
-                                GrGLShaderVar::TypeString(float_vector_type(coordDims)),
-                                fsCoordName.c_str(),
-                                varyingFSName,
-                                vector_nonhomog_coords(varyingDims),
-                                varyingFSName,
-                                vector_homog_coord(varyingDims));
-        }
-    }
-
-    GrStringBuilder sampleCoords;
-    bool complexCoord = false;
-    switch (desc.fCoordMapping) {
-    case StageDesc::kIdentity_CoordMapping:
-        sampleCoords = fsCoordName;
-        break;
-    case StageDesc::kSweepGradient_CoordMapping:
-        sampleCoords.printf("vec2(atan(- %s.y, - %s.x) * 0.1591549430918 + 0.5, 0.5)", fsCoordName.c_str(), fsCoordName.c_str());
-        complexCoord = true;
-        break;
-    case StageDesc::kRadialGradient_CoordMapping:
-        sampleCoords.printf("vec2(length(%s.xy), 0.5)", fsCoordName.c_str());
-        complexCoord = true;
-        break;
-    case StageDesc::kRadial2Gradient_CoordMapping:
-        complexCoord = genRadial2GradientCoordMapping(
-                           stageNum, segments,
-                           radial2VaryingFSName, radial2Params,
-                           sampleCoords, fsCoordName,
-                           varyingDims, coordDims);
-
-        break;
-    case StageDesc::kRadial2GradientDegenerate_CoordMapping:
-        complexCoord = genRadial2GradientDegenerateCoordMapping(
-                           stageNum, segments,
-                           radial2VaryingFSName, radial2Params,
-                           sampleCoords, fsCoordName,
-                           varyingDims, coordDims);
-        break;
-
-    };
-
-    static const uint32_t kMulByAlphaMask =
-        (StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
-         StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag);
-
-    const char* swizzle = "";
-    if (desc.fInConfigFlags & StageDesc::kSwapRAndB_InConfigFlag) {
-        GrAssert(!(desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag));
-        swizzle = ".bgra";
-    } else if (desc.fInConfigFlags & StageDesc::kSmearAlpha_InConfigFlag) {
-        GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
-        swizzle = ".aaaa";
-    } 
-
-    GrStringBuilder modulate;
-    if (NULL != fsInColor) {
-        modulate.printf(" * %s", fsInColor);
-    }
-
-    if (desc.fOptFlags &
-        StageDesc::kCustomTextureDomain_OptFlagBit) {
-        GrStringBuilder texDomainName;
-        tex_domain_name(stageNum, &texDomainName);
-        segments->fFSUnis.push_back().set(GrGLShaderVar::kVec4f_Type,
-            GrGLShaderVar::kUniform_TypeModifier, texDomainName);
-        GrStringBuilder coordVar("clampCoord");
-        segments->fFSCode.appendf("\t%s %s = clamp(%s, %s.xy, %s.zw);\n",
-                                  float_vector_type_str(coordDims),
-                                  coordVar.c_str(),
-                                  sampleCoords.c_str(),
-                                  texDomainName.c_str(),
-                                  texDomainName.c_str());
-        sampleCoords = coordVar;
-        locations->fTexDomUni = kUseUniform;
-    }
-
-    switch (desc.fFetchMode) {
-    case StageDesc::k2x2_FetchMode:
-        GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
-        gen2x2FS(stageNum, segments, locations, &sampleCoords,
-            samplerName, texelSizeName, swizzle, fsOutColor,
-            texFunc, modulate, complexCoord, coordDims);
-        break;
-    case StageDesc::kConvolution_FetchMode:
-        GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
-        genConvolutionFS(stageNum, desc, segments,
-            samplerName, kernel, swizzle, imageIncrementName, fsOutColor,
-            sampleCoords, texFunc, modulate);
-        break;
-    case StageDesc::kDilate_FetchMode:
-    case StageDesc::kErode_FetchMode:
-        GrAssert(!(desc.fInConfigFlags & kMulByAlphaMask));
-        genMorphologyFS(stageNum, desc, segments,
-            samplerName, swizzle, imageIncrementName, fsOutColor,
-            sampleCoords, texFunc, modulate);
-        break;
-    default:
-        if (desc.fInConfigFlags & kMulByAlphaMask) {
-            // only one of the mul by alpha flags should be set
-            GrAssert(GrIsPow2(kMulByAlphaMask & desc.fInConfigFlags));
-            GrAssert(!(desc.fInConfigFlags & 
-                       StageDesc::kSmearAlpha_InConfigFlag));
-            segments->fFSCode.appendf("\t%s = %s(%s, %s)%s;\n",
-                                      fsOutColor, texFunc.c_str(), 
-                                      samplerName, sampleCoords.c_str(),
-                                      swizzle);
-            if (desc.fInConfigFlags &
-                StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag) {
-                segments->fFSCode.appendf("\t%s = vec4(ceil(%s.rgb*%s.a*255.0)/255.0,%s.a)%s;\n",
-                                          fsOutColor, fsOutColor, fsOutColor,
-                                          fsOutColor, modulate.c_str());
-            } else {
-                segments->fFSCode.appendf("\t%s = vec4(floor(%s.rgb*%s.a*255.0)/255.0,%s.a)%s;\n",
-                                          fsOutColor, fsOutColor, fsOutColor,
-                                          fsOutColor, modulate.c_str());
+        if (NULL != fEffects[s]) {
+            const GrEffectStage& stage = drawState.getStage(s);
+            GrAssert(NULL != stage.getEffect());
+            fEffects[s]->setData(fUniformManager, stage);
+            int numSamplers = fUniformHandles.fSamplerUnis[s].count();
+            for (int u = 0; u < numSamplers; ++u) {
+                UniformHandle handle = fUniformHandles.fSamplerUnis[s][u];
+                if (GrGLUniformManager::kInvalidUniformHandle != handle) {
+                    const GrTextureAccess& access = (*stage.getEffect())->textureAccess(u);
+                    GrGLTexture* texture = static_cast<GrGLTexture*>(access.getTexture());
+                    gpu->bindTexture(texUnitIdx, access.getParams(), texture);
+                    ++texUnitIdx;
+                }
             }
-        } else {
-            segments->fFSCode.appendf("\t%s = %s(%s, %s)%s%s;\n",
-                                      fsOutColor, texFunc.c_str(), 
-                                      samplerName, sampleCoords.c_str(),
-                                      swizzle, modulate.c_str());
         }
     }
 }
-
-
diff --git a/src/gpu/gl/GrGLProgram.h b/src/gpu/gl/GrGLProgram.h
index 76f9c90..513bf75 100644
--- a/src/gpu/gl/GrGLProgram.h
+++ b/src/gpu/gl/GrGLProgram.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -10,17 +9,19 @@
 #ifndef GrGLProgram_DEFINED
 #define GrGLProgram_DEFINED
 
-#include "../GrDrawState.h"
+#include "GrDrawState.h"
+#include "GrGLEffect.h"
 #include "GrGLContextInfo.h"
 #include "GrGLSL.h"
-#include "../GrStringBuilder.h"
-#include "../GrGpu.h"
+#include "GrGLTexture.h"
+#include "GrGLUniformManager.h"
 
+#include "SkString.h"
 #include "SkXfermode.h"
 
 class GrBinHashKeyBuilder;
-
-struct ShaderCodeSegments;
+class GrGLEffect;
+class GrGLShaderBuilder;
 
 // optionally compile the experimental GS code. Set to GR_DEBUG
 // so that debug build bots will execute the code.
@@ -35,159 +36,61 @@
  * Uniforms are program-local so we can't rely on fHWState to hold the
  * previous uniform state after a program change.
  */
-class GrGLProgram {
+class GrGLProgram : public GrRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(GrGLProgram)
 
-    class CachedData;
+    struct Desc;
 
-    GrGLProgram();
-    ~GrGLProgram();
+    static GrGLProgram* Create(const GrGLContextInfo& gl,
+                               const Desc& desc,
+                               const GrEffectStage* stages[]);
+
+    virtual ~GrGLProgram();
 
     /**
-     *  This is the heavy initilization routine for building a GLProgram.
-     *  The result of heavy init is not stored in datamembers of GrGLProgam,
-     *  but in a separate cacheable container.
+     * Call to abandon GL objects owned by this program.
      */
-    bool genProgram(const GrGLContextInfo& gl,
-                    CachedData* programData) const;
-
-     /**
-      * The shader may modify the blend coeffecients. Params are in/out
-      */
-     void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
+    void abandon();
 
     /**
-     * Attribute indices. These should not overlap. Matrices consume 3 slots.
+     * The shader may modify the blend coefficients. Params are in/out
+     */
+    void overrideBlend(GrBlendCoeff* srcCoeff, GrBlendCoeff* dstCoeff) const;
+
+    const Desc& getDesc() { return fDesc; }
+
+    /**
+     * Attribute indices. These should not overlap.
      */
     static int PositionAttributeIdx() { return 0; }
-    static int TexCoordAttributeIdx(int tcIdx) { return 1 + tcIdx; }
-    static int ColorAttributeIdx() { return 1 + GrDrawState::kMaxTexCoords; }
-    static int CoverageAttributeIdx() {
-        return 2 + GrDrawState::kMaxTexCoords;
-    }
-    static int EdgeAttributeIdx() { return 3 + GrDrawState::kMaxTexCoords; }
+    static int ColorAttributeIdx() { return 1; }
+    static int CoverageAttributeIdx() { return 2; }
+    static int EdgeAttributeIdx() { return 3; }
+    static int TexCoordAttributeIdx(int tcIdx) { return 4 + tcIdx; }
 
-    static int ViewMatrixAttributeIdx() {
-        return 4 + GrDrawState::kMaxTexCoords;
-    }
-    static int TextureMatrixAttributeIdx(int stage) {
-        return 7 + GrDrawState::kMaxTexCoords + 3 * stage;
-    }
-
-public:
+    /**
+     * This function uploads uniforms and calls each GrGLEffect's setData. It is called before a
+     * draw occurs using the program after the program has already been bound. It also uses the
+     * GrGpuGL object to bind the textures required by the GrGLEffects.
+     */
+    void setData(GrGpuGL*);
 
     // Parameters that affect code generation
-    // These structs should be kept compact; they are the input to an
-    // expensive hash key generator.
-    struct ProgramDesc {
-        ProgramDesc() {
-            // since we use this as part of a key we can't have any unitialized
+    // This structs should be kept compact; it is input to an expensive hash key generator.
+    struct Desc {
+        Desc() {
+            // since we use this as part of a key we can't have any uninitialized
             // padding
-            memset(this, 0, sizeof(ProgramDesc));
+            memset(this, 0, sizeof(Desc));
         }
 
-        enum OutputConfig {
-            // PM-color OR color with no alpha channel
-            kPremultiplied_OutputConfig,
-            // nonPM-color with alpha channel. Round components up after
-            // dividing by alpha. Assumes output is 8 bits for r, g, and b
-            kUnpremultiplied_RoundUp_OutputConfig,
-            // nonPM-color with alpha channel. Round components down after
-            // dividing by alpha. Assumes output is 8 bits for r, g, and b
-            kUnpremultiplied_RoundDown_OutputConfig,
+        // returns this as a uint32_t array to be used as a key in the program cache
+        const uint32_t* asKey() const {
+            return reinterpret_cast<const uint32_t*>(this);
+        }
 
-            kOutputConfigCnt
-        };
-
-        struct StageDesc {
-            enum OptFlagBits {
-                kNoPerspective_OptFlagBit       = 1 << 0,
-                kIdentityMatrix_OptFlagBit      = 1 << 1,
-                kCustomTextureDomain_OptFlagBit = 1 << 2,
-                kIsEnabled_OptFlagBit           = 1 << 7
-            };
-            enum FetchMode {
-                kSingle_FetchMode,
-                k2x2_FetchMode,
-                kConvolution_FetchMode,
-                kErode_FetchMode,
-                kDilate_FetchMode,
-
-                kFetchModeCnt,
-            };
-            /**
-              Flags set based on a src texture's pixel config. The operations
-              described are performed after reading a texel.
-             */
-            enum InConfigFlags {
-                kNone_InConfigFlag                      = 0x0,
-
-                /**
-                  Swap the R and B channels. This is incompatible with
-                  kSmearAlpha. It is prefereable to perform the swizzle outside
-                  the shader using GL_ARB_texture_swizzle if possible rather
-                  than setting this flag.
-                 */
-                kSwapRAndB_InConfigFlag                 = 0x1,
-
-                /**
-                 Smear alpha across all four channels. This is incompatible with
-                 kSwapRAndB and kMulRGBByAlpha*. It is prefereable to perform
-                 the smear outside the shader using GL_ARB_texture_swizzle if
-                 possible rather than setting this flag.
-                */
-                kSmearAlpha_InConfigFlag                = 0x2,
-
-                /**
-                 Multiply r,g,b by a after texture reads. This flag incompatible
-                 with kSmearAlpha and may only be used with FetchMode kSingle.
-
-                 It is assumed the src texture has 8bit color components. After
-                 reading the texture one version rounds up to the next multiple
-                 of 1/255.0 and the other rounds down. At most one of these
-                 flags may be set.
-                 */
-                kMulRGBByAlpha_RoundUp_InConfigFlag     =  0x4,
-                kMulRGBByAlpha_RoundDown_InConfigFlag   =  0x8,
-
-                kDummyInConfigFlag,
-                kInConfigBitMask = (kDummyInConfigFlag-1) |
-                                   (kDummyInConfigFlag-2)
-            };
-            enum CoordMapping {
-                kIdentity_CoordMapping,
-                kRadialGradient_CoordMapping,
-                kSweepGradient_CoordMapping,
-                kRadial2Gradient_CoordMapping,
-                // need different shader computation when quadratic
-                // eq describing the gradient degenerates to a linear eq.
-                kRadial2GradientDegenerate_CoordMapping,
-                kCoordMappingCnt
-            };
-
-            uint8_t fOptFlags;
-            uint8_t fInConfigFlags; // bitfield of InConfigFlags values
-            uint8_t fFetchMode;     // casts to enum FetchMode
-            uint8_t fCoordMapping;  // casts to enum CoordMapping
-            uint8_t fKernelWidth;
-
-            GR_STATIC_ASSERT((InConfigFlags)(uint8_t)kInConfigBitMask ==
-                             kInConfigBitMask);
-
-            inline bool isEnabled() const {
-                return SkToBool(fOptFlags & kIsEnabled_OptFlagBit);
-            }
-            inline void setEnabled(bool newValue) {
-                if (newValue) {
-                    fOptFlags |= kIsEnabled_OptFlagBit;
-                } else {
-                    fOptFlags &= ~kIsEnabled_OptFlagBit;
-                }
-            }
-        };
-
-        // Specifies where the intitial color comes from before the stages are
-        // applied.
+        // Specifies where the initial color comes from before the stages are applied.
         enum ColorInput {
             kSolidWhite_ColorInput,
             kTransBlack_ColorInput,
@@ -197,7 +100,7 @@
             kColorInputCnt
         };
         // Dual-src blending makes use of a secondary output color that can be
-        // used as a per-pixel blend coeffecient. This controls whether a
+        // used as a per-pixel blend coefficient. This controls whether a
         // secondary source is output and what value it holds.
         enum DualSrcOutput {
             kNone_DualSrcOutput,
@@ -208,192 +111,116 @@
             kDualSrcOutputCnt
         };
 
+        // TODO: remove these two members when edge-aa can be rewritten as a GrEffect.
         GrDrawState::VertexEdgeType fVertexEdgeType;
+        // should the FS discard if the edge-aa coverage is zero (to avoid stencil manipulation)
+        bool                        fDiscardIfOutsideEdge;
 
-        // stripped of bits that don't affect prog generation
-        GrVertexLayout fVertexLayout;
+        // stripped of bits that don't affect program generation
+        GrVertexLayout              fVertexLayout;
 
-        StageDesc fStages[GrDrawState::kNumStages];
+        /** Non-zero if this stage has an effect */
+        GrGLEffect::EffectKey       fEffectKeys[GrDrawState::kNumStages];
 
         // To enable experimental geometry shader code (not for use in
         // production)
 #if GR_GL_EXPERIMENTAL_GS
-        bool fExperimentalGS;
+        bool                        fExperimentalGS;
 #endif
-
-        uint8_t fColorInput;        // casts to enum ColorInput
-        uint8_t fCoverageInput;     // casts to enum CoverageInput
-        uint8_t fOutputConfig;      // casts to enum OutputConfig
-        uint8_t fDualSrcOutput;     // casts to enum DualSrcOutput
-        int8_t fFirstCoverageStage;
-        SkBool8 fEmitsPointSize;
-        SkBool8 fEdgeAAConcave;
-        SkBool8 fColorMatrixEnabled;
-
-        int8_t fEdgeAANumEdges;
-        uint8_t fColorFilterXfermode;  // casts to enum SkXfermode::Mode
-        int8_t fPadding[3];
-
-    } fProgramDesc;
-    GR_STATIC_ASSERT(!(sizeof(ProgramDesc) % 4));
-
-    // for code readability
-    typedef ProgramDesc::StageDesc StageDesc;
-
+        uint8_t                     fColorInput;            // casts to enum ColorInput
+        uint8_t                     fCoverageInput;         // casts to enum ColorInput
+        uint8_t                     fDualSrcOutput;         // casts to enum DualSrcOutput
+        int8_t                      fFirstCoverageStage;
+        SkBool8                     fEmitsPointSize;
+        uint8_t                     fColorFilterXfermode;   // casts to enum SkXfermode::Mode
+    };
 private:
+    GrGLProgram(const GrGLContextInfo& gl,
+                const Desc& desc,
+                const GrEffectStage* stages[]);
 
-    const ProgramDesc& getDesc() { return fProgramDesc; }
-    const char* adjustInColor(const GrStringBuilder& inColor) const;
+    bool succeeded() const { return 0 != fProgramID; }
 
-public:
-    enum {
-        kUnusedUniform = -1,
-        kSetAsAttribute = 1000,
-    };
+    /**
+     *  This is the heavy initialization routine for building a GLProgram.
+     */
+    bool genProgram(const GrEffectStage* stages[]);
 
-    struct StageUniLocations {
-        GrGLint fTextureMatrixUni;
-        GrGLint fNormalizedTexelSizeUni;
-        GrGLint fSamplerUni;
-        GrGLint fRadial2Uni;
-        GrGLint fTexDomUni;
-        GrGLint fKernelUni;
-        GrGLint fImageIncrementUni;
-        void reset() {
-            fTextureMatrixUni = kUnusedUniform;
-            fNormalizedTexelSizeUni = kUnusedUniform;
-            fSamplerUni = kUnusedUniform;
-            fRadial2Uni = kUnusedUniform;
-            fTexDomUni = kUnusedUniform;
-            fKernelUni = kUnusedUniform;
-            fImageIncrementUni = kUnusedUniform;
+    void genInputColor(GrGLShaderBuilder* builder, SkString* inColor);
+
+    void genGeometryShader(GrGLShaderBuilder* segments) const;
+
+    typedef GrGLUniformManager::UniformHandle UniformHandle;
+
+    void genUniformCoverage(GrGLShaderBuilder* segments, SkString* inOutCoverage);
+
+    // generates code to compute coverage based on edge AA. Returns true if edge coverage was
+    // inserted in which case coverageVar will be updated to refer to a scalar. Otherwise,
+    // coverageVar is set to an empty string.
+    bool genEdgeCoverage(SkString* coverageVar, GrGLShaderBuilder* builder) const;
+
+    // Creates a GL program ID, binds shader attributes to GL vertex attrs, and links the program
+    bool bindOutputsAttribsAndLinkProgram(const GrGLShaderBuilder& builder,
+                                          SkString texCoordAttrNames[GrDrawState::kMaxTexCoords],
+                                          bool bindColorOut,
+                                          bool bindDualSrcOut);
+
+    // Sets the texture units for samplers
+    void initSamplerUniforms();
+
+    bool compileShaders(const GrGLShaderBuilder& builder);
+
+    const char* adjustInColor(const SkString& inColor) const;
+
+    typedef SkSTArray<4, UniformHandle, true> SamplerUniSArray;
+
+    struct UniformHandles {
+        UniformHandle       fViewMatrixUni;
+        UniformHandle       fColorUni;
+        UniformHandle       fCoverageUni;
+        UniformHandle       fColorFilterUni;
+        // We use the render target height to provide a y-down frag coord when specifying
+        // origin_upper_left is not supported.
+        UniformHandle       fRTHeightUni;
+        // An array of sampler uniform handles for each effect.
+        SamplerUniSArray    fSamplerUnis[GrDrawState::kNumStages];
+
+        UniformHandles() {
+            fViewMatrixUni = GrGLUniformManager::kInvalidUniformHandle;
+            fColorUni = GrGLUniformManager::kInvalidUniformHandle;
+            fCoverageUni = GrGLUniformManager::kInvalidUniformHandle;
+            fColorFilterUni = GrGLUniformManager::kInvalidUniformHandle;
+            fRTHeightUni = GrGLUniformManager::kInvalidUniformHandle;
         }
     };
 
-    struct UniLocations {
-        GrGLint fViewMatrixUni;
-        GrGLint fColorUni;
-        GrGLint fCoverageUni;
-        GrGLint fEdgesUni;
-        GrGLint fColorFilterUni;
-        GrGLint fColorMatrixUni;
-        GrGLint fColorMatrixVecUni;
-        StageUniLocations fStages[GrDrawState::kNumStages];
-        void reset() {
-            fViewMatrixUni = kUnusedUniform;
-            fColorUni = kUnusedUniform;
-            fCoverageUni = kUnusedUniform;
-            fEdgesUni = kUnusedUniform;
-            fColorFilterUni = kUnusedUniform;
-            fColorMatrixUni = kUnusedUniform;
-            fColorMatrixVecUni = kUnusedUniform;
-            for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-                fStages[s].reset();
-            }
-        }
-    };
+    // GL IDs
+    GrGLuint                    fVShaderID;
+    GrGLuint                    fGShaderID;
+    GrGLuint                    fFShaderID;
+    GrGLuint                    fProgramID;
+    // The matrix sent to GL is determined by both the client's matrix and
+    // the size of the viewport.
+    SkMatrix                    fViewMatrix;
+    SkISize                     fViewportSize;
 
-    class CachedData : public ::GrNoncopyable {
-    public:
-        CachedData() {
-        }
+    // these reflect the current values of uniforms (GL uniform values travel with program)
+    GrColor                     fColor;
+    GrColor                     fCoverage;
+    GrColor                     fColorFilterColor;
+    int                         fRTHeight;
 
-        ~CachedData() {
-        }
+    GrGLEffect*                 fEffects[GrDrawState::kNumStages];
 
-        void copyAndTakeOwnership(CachedData& other) {
-            memcpy(this, &other, sizeof(*this));
-        }
+    Desc                        fDesc;
+    const GrGLContextInfo&      fContextInfo;
 
-    public:
+    GrGLUniformManager          fUniformManager;
+    UniformHandles              fUniformHandles;
 
-        // IDs
-        GrGLuint    fVShaderID;
-        GrGLuint    fGShaderID;
-        GrGLuint    fFShaderID;
-        GrGLuint    fProgramID;
-        // shader uniform locations (-1 if shader doesn't use them)
-        UniLocations fUniLocations;
+    friend class GrGpuGL; // TODO: remove this by adding getters and moving functionality.
 
-        GrMatrix  fViewMatrix;
-
-        // these reflect the current values of uniforms
-        // (GL uniform values travel with program)
-        GrColor                     fColor;
-        GrColor                     fCoverage;
-        GrColor                     fColorFilterColor;
-        GrMatrix                    fTextureMatrices[GrDrawState::kNumStages];
-        // width and height used for normalized texel size
-        int                         fTextureWidth[GrDrawState::kNumStages];
-        int                         fTextureHeight[GrDrawState::kNumStages]; 
-        GrScalar                    fRadial2CenterX1[GrDrawState::kNumStages];
-        GrScalar                    fRadial2Radius0[GrDrawState::kNumStages];
-        bool                        fRadial2PosRoot[GrDrawState::kNumStages];
-        GrRect                      fTextureDomain[GrDrawState::kNumStages];
-
-    private:
-        enum Constants {
-            kUniLocationPreAllocSize = 8
-        };
-
-    }; // CachedData
-
-    enum Constants {
-        kProgramKeySize = sizeof(ProgramDesc)
-    };
-
-    // Provide an opaque ProgramDesc
-    const uint32_t* keyData() const{
-        return reinterpret_cast<const uint32_t*>(&fProgramDesc);
-    }
-
-private:
-
-    // Determines which uniforms will need to be bound.
-    void genStageCode(const GrGLContextInfo& gl,
-                      int stageNum,
-                      const ProgramDesc::StageDesc& desc,
-                      const char* fsInColor, // NULL means no incoming color
-                      const char* fsOutColor,
-                      const char* vsInCoord,
-                      ShaderCodeSegments* segments,
-                      StageUniLocations* locations) const;
-
-    void genGeometryShader(const GrGLContextInfo& gl,
-                           ShaderCodeSegments* segments) const;
-
-    // generates code to compute coverage based on edge AA.
-    void genEdgeCoverage(const GrGLContextInfo& gl,
-                         GrVertexLayout layout,
-                         CachedData* programData,
-                         GrStringBuilder* coverageVar,
-                         ShaderCodeSegments* segments) const;
-
-    static bool CompileShaders(const GrGLContextInfo& gl,
-                               const ShaderCodeSegments& segments, 
-                               CachedData* programData);
-
-    // Compiles a GL shader, returns shader ID or 0 if failed
-    // params have same meaning as glShaderSource
-    static GrGLuint CompileShader(const GrGLContextInfo& gl,
-                                  GrGLenum type, int stringCnt,
-                                  const char** strings,
-                                  int* stringLengths);
-
-    // Creates a GL program ID, binds shader attributes to GL vertex attrs, and
-    // links the program
-    bool bindOutputsAttribsAndLinkProgram(
-                const GrGLContextInfo& gl,
-                GrStringBuilder texCoordAttrNames[GrDrawState::kMaxTexCoords],
-                bool bindColorOut,
-                bool bindDualSrcOut,
-                CachedData* programData) const;
-
-    // Binds uniforms; initializes cache to invalid values.
-    void getUniformLocationsAndInitCache(const GrGLContextInfo& gl,
-                                         CachedData* programData) const;
-
-    friend class GrGpuGLShaders;
+    typedef GrRefCnt INHERITED;
 };
 
 #endif
diff --git a/src/gpu/gl/GrGLRenderTarget.cpp b/src/gpu/gl/GrGLRenderTarget.cpp
index c98914a..47128e7 100644
--- a/src/gpu/gl/GrGLRenderTarget.cpp
+++ b/src/gpu/gl/GrGLRenderTarget.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,7 +5,6 @@
  * found in the LICENSE file.
  */
 
-
 #include "GrGLRenderTarget.h"
 
 #include "GrGpuGL.h"
@@ -22,22 +20,38 @@
     fTexFBOID               = desc.fTexFBOID;
     fMSColorRenderbufferID  = desc.fMSColorRenderbufferID;
     fViewport               = viewport;
-    fOwnIDs                 = desc.fOwnIDs;
     fTexIDObj               = texID;
     GrSafeRef(fTexIDObj);
 }
 
+namespace {
+GrTextureDesc MakeDesc(GrTextureFlags flags,
+                       int width, int height,
+                       GrPixelConfig config, int sampleCnt) {
+    GrTextureDesc temp;
+    temp.fFlags = flags;
+    temp.fWidth = width;
+    temp.fHeight = height;
+    temp.fConfig = config;
+    temp.fSampleCnt = sampleCnt;
+    return temp;
+}
+
+};
+
 GrGLRenderTarget::GrGLRenderTarget(GrGpuGL* gpu,
                                    const Desc& desc,
                                    const GrGLIRect& viewport,
                                    GrGLTexID* texID,
                                    GrGLTexture* texture)
     : INHERITED(gpu,
+                desc.fIsWrapped,
                 texture,
-                viewport.fWidth,
-                viewport.fHeight,
-                desc.fConfig,
-                desc.fSampleCnt) {
+                MakeDesc(kNone_GrTextureFlags,
+                         viewport.fWidth, viewport.fHeight,
+                         desc.fConfig, desc.fSampleCnt),
+                texture->origin()) {
+    GrAssert(kBottomLeft_GrSurfaceOrigin == texture->origin());
     GrAssert(NULL != texID);
     GrAssert(NULL != texture);
     // FBO 0 can't also be a texture, right?
@@ -55,17 +69,18 @@
                                    const Desc& desc,
                                    const GrGLIRect& viewport)
     : INHERITED(gpu,
+                desc.fIsWrapped,
                 NULL,
-                viewport.fWidth,
-                viewport.fHeight,
-                desc.fConfig,
-                desc.fSampleCnt) {
+                MakeDesc(kNone_GrTextureFlags,
+                         viewport.fWidth, viewport.fHeight,
+                         desc.fConfig, desc.fSampleCnt),
+                kBottomLeft_GrSurfaceOrigin) {
     this->init(desc, viewport, NULL);
 }
 
 void GrGLRenderTarget::onRelease() {
     GPUGL->notifyRenderTargetDelete(this);
-    if (fOwnIDs) {
+    if (!this->isWrapped()) {
         if (fTexFBOID) {
             GL_CALL(DeleteFramebuffers(1, &fTexFBOID));
         }
@@ -81,7 +96,7 @@
     fMSColorRenderbufferID  = 0;
     GrSafeUnref(fTexIDObj);
     fTexIDObj = NULL;
-    this->setStencilBuffer(NULL);
+    INHERITED::onRelease();
 }
 
 void GrGLRenderTarget::onAbandon() {
@@ -92,6 +107,5 @@
         fTexIDObj->abandon();
         fTexIDObj = NULL;
     }
-    this->setStencilBuffer(NULL);
+    INHERITED::onAbandon();
 }
-
diff --git a/src/gpu/gl/GrGLRenderTarget.h b/src/gpu/gl/GrGLRenderTarget.h
index eb817df..9a39ca1 100644
--- a/src/gpu/gl/GrGLRenderTarget.h
+++ b/src/gpu/gl/GrGLRenderTarget.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -12,7 +11,7 @@
 
 #include "GrGLIRect.h"
 #include "GrRenderTarget.h"
-#include "GrScalar.h"
+#include "SkScalar.h"
 
 class GrGpuGL;
 class GrGLTexture;
@@ -29,7 +28,7 @@
         GrGLuint      fRTFBOID;
         GrGLuint      fTexFBOID;
         GrGLuint      fMSColorRenderbufferID;
-        bool          fOwnIDs;
+        bool          fIsWrapped;
         GrPixelConfig fConfig;
         int           fSampleCnt;
     };
@@ -51,19 +50,19 @@
     void setViewport(const GrGLIRect& rect) { fViewport = rect; }
     const GrGLIRect& getViewport() const { return fViewport; }
 
-    // The following two functions return the same ID when a 
-    // texture-rendertarget is multisampled, and different IDs when
+    // 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 
-    virtual intptr_t getRenderTargetHandle() const {
-        return this->renderFBOID(); 
+    // override of GrRenderTarget
+    virtual GrBackendObject getRenderTargetHandle() const {
+        return this->renderFBOID();
     }
-    virtual intptr_t getRenderTargetResolvedHandle() const {
+    virtual GrBackendObject getRenderTargetResolvedHandle() const {
         return this->textureFBOID();
     }
     virtual ResolveType getResolveType() const {
@@ -81,8 +80,8 @@
 
 protected:
     // override of GrResource
-    virtual void onAbandon();
-    virtual void onRelease();
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
 
 private:
     GrGLuint      fRTFBOID;
@@ -90,11 +89,7 @@
 
     GrGLuint      fMSColorRenderbufferID;
 
-    // Should this object delete IDs when it is destroyed or does someone
-    // else own them.
-    bool        fOwnIDs;
-
-    // when we switch to this rendertarget we want to set the viewport to
+    // when we switch to this render target we want to set the viewport to
     // only render to to content area (as opposed to the whole allocation) and
     // we want the rendering to be at top left (GL has origin in bottom left)
     GrGLIRect fViewport;
diff --git a/src/gpu/gl/GrGLSL.cpp b/src/gpu/gl/GrGLSL.cpp
index e933ee8..64b6c11 100644
--- a/src/gpu/gl/GrGLSL.cpp
+++ b/src/gpu/gl/GrGLSL.cpp
@@ -7,6 +7,7 @@
 
 #include "GrGLSL.h"
 #include "GrGLShaderVar.h"
+#include "SkString.h"
 
 GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
                                    const GrGLInterface* gl) {
@@ -16,6 +17,8 @@
             GrAssert(ver >= GR_GLSL_VER(1,10));
             if (ver >= GR_GLSL_VER(1,50)) {
                 return k150_GrGLSLGeneration;
+            } else if (ver >= GR_GLSL_VER(1,40)) {
+                return k140_GrGLSLGeneration;
             } else if (ver >= GR_GLSL_VER(1,30)) {
                 return k130_GrGLSLGeneration;
             } else {
@@ -46,6 +49,9 @@
         case k130_GrGLSLGeneration:
             GrAssert(kDesktop_GrGLBinding == binding);
             return "#version 130\n";
+        case k140_GrGLSLGeneration:
+            GrAssert(kDesktop_GrGLBinding == binding);
+            return "#version 140\n";
         case k150_GrGLSLGeneration:
             GrAssert(kDesktop_GrGLBinding == binding);
             return "#version 150\n";
@@ -55,28 +61,155 @@
     }
 }
 
-const char* GrGetGLSLVarPrecisionDeclType(GrGLBinding binding) {
-    if (kES2_GrGLBinding == binding) {
-        return "mediump";
-    } else {
-        return " ";
-    }
-}
-
-const char* GrGetGLSLShaderPrecisionDecl(GrGLBinding binding) {
-    if (kES2_GrGLBinding == binding) {
-        return "precision mediump float;\n";
-    } else {
-        return "";
-    }
-}
-
 bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
                              const char* nameIfDeclared,
                              GrGLShaderVar* var) {
     bool declaredOutput = k110_GrGLSLGeneration != gen;
-    var->set(GrGLShaderVar::kVec4f_Type,
+    var->set(kVec4f_GrSLType,
              GrGLShaderVar::kOut_TypeModifier,
              declaredOutput ? nameIfDeclared : "gl_FragColor");
     return declaredOutput;
 }
+
+GrSLType GrSLFloatVectorType (int count) {
+    GR_STATIC_ASSERT(kFloat_GrSLType == 1);
+    GR_STATIC_ASSERT(kVec2f_GrSLType == 2);
+    GR_STATIC_ASSERT(kVec3f_GrSLType == 3);
+    GR_STATIC_ASSERT(kVec4f_GrSLType == 4);
+    GrAssert(count > 0 && count <= 4);
+    return (GrSLType)(count);
+}
+
+const char* GrGLSLVectorHomogCoord(int count) {
+    static const char* HOMOGS[] = {"ERROR", "", ".y", ".z", ".w"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(HOMOGS));
+    return HOMOGS[count];
+}
+
+const char* GrGLSLVectorHomogCoord(GrSLType type) {
+    return GrGLSLVectorHomogCoord(GrSLTypeToVecLength(type));
+}
+
+const char* GrGLSLVectorNonhomogCoords(int count) {
+    static const char* NONHOMOGS[] = {"ERROR", "", ".x", ".xy", ".xyz"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(NONHOMOGS));
+    return NONHOMOGS[count];
+}
+
+const char* GrGLSLVectorNonhomogCoords(GrSLType type) {
+    return GrGLSLVectorNonhomogCoords(GrSLTypeToVecLength(type));
+}
+
+GrSLConstantVec GrGLSLModulate4f(SkString* outAppend,
+                                 const char* in0,
+                                 const char* in1,
+                                 GrSLConstantVec default0,
+                                 GrSLConstantVec default1) {
+    GrAssert(NULL != outAppend);
+
+    bool has0 = NULL != in0 && '\0' != *in0;
+    bool has1 = NULL != in1 && '\0' != *in1;
+
+    GrAssert(has0 || kNone_GrSLConstantVec != default0);
+    GrAssert(has1 || kNone_GrSLConstantVec != default1);
+
+    if (!has0 && !has1) {
+        GrAssert(kZeros_GrSLConstantVec == default0 || kOnes_GrSLConstantVec == default0);
+        GrAssert(kZeros_GrSLConstantVec == default1 || kOnes_GrSLConstantVec == default1);
+        if (kZeros_GrSLConstantVec == default0 || kZeros_GrSLConstantVec == default1) {
+            outAppend->append(GrGLSLZerosVecf(4));
+            return kZeros_GrSLConstantVec;
+        } else {
+            // both inputs are ones vectors
+            outAppend->append(GrGLSLOnesVecf(4));
+            return kOnes_GrSLConstantVec;
+        }
+    } else if (!has0) {
+        GrAssert(kZeros_GrSLConstantVec == default0 || kOnes_GrSLConstantVec == default0);
+        if (kZeros_GrSLConstantVec == default0) {
+            outAppend->append(GrGLSLZerosVecf(4));
+            return kZeros_GrSLConstantVec;
+        } else {
+            outAppend->appendf("vec4(%s)", in1);
+            return kNone_GrSLConstantVec;
+        }
+    } else if (!has1) {
+        GrAssert(kZeros_GrSLConstantVec == default1 || kOnes_GrSLConstantVec == default1);
+        if (kZeros_GrSLConstantVec == default1) {
+            outAppend->append(GrGLSLZerosVecf(4));
+            return kZeros_GrSLConstantVec;
+        } else {
+            outAppend->appendf("vec4(%s)", in0);
+            return kNone_GrSLConstantVec;
+        }
+    } else {
+        outAppend->appendf("vec4(%s * %s)", in0, in1);
+        return kNone_GrSLConstantVec;
+    }
+}
+
+namespace {
+void append_tabs(SkString* outAppend, int tabCnt) {
+    static const char kTabs[] = "\t\t\t\t\t\t\t\t";
+    while (tabCnt) {
+        int cnt = GrMin((int)GR_ARRAY_COUNT(kTabs), tabCnt);
+        outAppend->append(kTabs, cnt);
+        tabCnt -= cnt;
+    }
+}
+}
+
+GrSLConstantVec GrGLSLMulVarBy4f(SkString* outAppend,
+                                 int tabCnt,
+                                 const char* vec4VarName,
+                                 const char* mulFactor,
+                                 GrSLConstantVec mulFactorDefault) {
+    bool haveFactor = NULL != mulFactor && '\0' != *mulFactor;
+
+    GrAssert(NULL != outAppend);
+    GrAssert(NULL != vec4VarName);
+    GrAssert(kNone_GrSLConstantVec != mulFactorDefault || haveFactor);
+
+    if (!haveFactor) {
+        if (kOnes_GrSLConstantVec == mulFactorDefault) {
+            return kNone_GrSLConstantVec;
+        } else {
+            GrAssert(kZeros_GrSLConstantVec == mulFactorDefault);
+            append_tabs(outAppend, tabCnt);
+            outAppend->appendf("%s = vec4(0, 0, 0, 0);\n", vec4VarName);
+            return kZeros_GrSLConstantVec;
+        }
+    }
+    append_tabs(outAppend, tabCnt);
+    outAppend->appendf("%s *= %s;\n", vec4VarName, mulFactor);
+    return kNone_GrSLConstantVec;
+}
+
+GrSLConstantVec GrGLSLAdd4f(SkString* outAppend,
+                            const char* in0,
+                            const char* in1,
+                            GrSLConstantVec default0,
+                            GrSLConstantVec default1) {
+    GrAssert(NULL != outAppend);
+
+    bool has0 = NULL != in0 && '\0' != *in0;
+    bool has1 = NULL != in1 && '\0' != *in1;
+
+    if (!has0 && !has1) {
+        GrAssert(kZeros_GrSLConstantVec == default0);
+        GrAssert(kZeros_GrSLConstantVec == default1);
+        outAppend->append(GrGLSLZerosVecf(4));
+        return kZeros_GrSLConstantVec;
+    } else if (!has0) {
+        GrAssert(kZeros_GrSLConstantVec == default0);
+        outAppend->appendf("vec4(%s)", in1);
+        return kNone_GrSLConstantVec;
+    } else if (!has1) {
+        GrAssert(kZeros_GrSLConstantVec == default1);
+        outAppend->appendf("vec4(%s)", in0);
+        return kNone_GrSLConstantVec;
+    } else {
+        outAppend->appendf("(vec4(%s) + vec4(%s))", in0, in1);
+        return kNone_GrSLConstantVec;
+    }
+}
diff --git a/src/gpu/gl/GrGLSL.h b/src/gpu/gl/GrGLSL.h
index a3d3921..4559fdd 100644
--- a/src/gpu/gl/GrGLSL.h
+++ b/src/gpu/gl/GrGLSL.h
@@ -11,12 +11,13 @@
 #include "gl/GrGLInterface.h"
 
 class GrGLShaderVar;
+class SkString;
 
 // Limited set of GLSL versions we build shaders for. Caller should round
 // down the GLSL version to one of these enums.
 enum GrGLSLGeneration {
     /**
-     * Desktop GLSL 1.10 and ES2 shading lang (based on desktop GLSL 1.20)
+     * Desktop GLSL 1.10 and ES2 shading language (based on desktop GLSL 1.20)
      */
     k110_GrGLSLGeneration,
     /**
@@ -24,43 +25,88 @@
      */
     k130_GrGLSLGeneration,
     /**
-     * Dekstop GLSL 1.50
+     * Desktop GLSL 1.40
+     */
+    k140_GrGLSLGeneration,
+    /**
+     * Desktop GLSL 1.50
      */
     k150_GrGLSLGeneration,
 };
 
 /**
+ * Types of shader-language-specific boxed variables we can create.
+ * (Currently only GrGLShaderVars, but should be applicable to other shader
+ * languages.)
+ */
+enum GrSLType {
+    kVoid_GrSLType,
+    kFloat_GrSLType,
+    kVec2f_GrSLType,
+    kVec3f_GrSLType,
+    kVec4f_GrSLType,
+    kMat33f_GrSLType,
+    kMat44f_GrSLType,
+    kSampler2D_GrSLType
+};
+
+enum GrSLConstantVec {
+    kZeros_GrSLConstantVec,
+    kOnes_GrSLConstantVec,
+    kNone_GrSLConstantVec,
+};
+
+namespace {
+static inline int GrSLTypeToVecLength(GrSLType type) {
+    static const int kVecLengths[] = {
+        0, // kVoid_GrSLType
+        1, // kFloat_GrSLType
+        2, // kVec2f_GrSLType
+        3, // kVec3f_GrSLType
+        4, // kVec4f_GrSLType
+        1, // kMat33f_GrSLType
+        1, // kMat44f_GrSLType
+        1, // kSampler2D_GrSLType
+    };
+    GrAssert((size_t) type < GR_ARRAY_COUNT(kVecLengths));
+    return kVecLengths[type];
+}
+
+static inline const char* GrGLSLOnesVecf(int count) {
+    static const char* kONESVEC[] = {"ERROR", "1.0", "vec2(1,1)",
+                                     "vec3(1,1,1)", "vec4(1,1,1,1)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(kONESVEC));
+    return kONESVEC[count];
+}
+
+static inline const char* GrGLSLZerosVecf(int count) {
+    static const char* kZEROSVEC[] = {"ERROR", "0.0", "vec2(0,0)",
+                                      "vec3(0,0,0)", "vec4(0,0,0,0)"};
+    GrAssert(count >= 1 && count < (int)GR_ARRAY_COUNT(kZEROSVEC));
+    return kZEROSVEC[count];
+}
+}
+
+/**
  * Gets the most recent GLSL Generation compatible with the OpenGL context.
  */
 GrGLSLGeneration GrGetGLSLGeneration(GrGLBinding binding,
                                      const GrGLInterface* gl);
 
 /**
- * Returns a string to include at the begining of a shader to declare the GLSL
+ * Returns a string to include at the beginning of a shader to declare the GLSL
  * version.
  */
 const char* GrGetGLSLVersionDecl(GrGLBinding binding,
                                  GrGLSLGeneration v);
 
 /**
- * Returns a string to include in a variable decleration to set the fp precision
- * or an emptry string if precision is not required.
- */
-const char* GrGetGLSLVarPrecisionDeclType(GrGLBinding binding);
-
-/**
- * Returns a string to set the default fp precision for an entire shader, or
- * an emptry string if precision is not required.
- */
-const char* GrGetGLSLShaderPrecisionDecl(GrGLBinding binding);
-
-/**
  * Depending on the GLSL version being emitted there may be an assumed output
  * variable from the fragment shader for the color. Otherwise, the shader must
  * declare an output variable for the color. If this function returns true:
  *    * Parameter var's name will be set to nameIfDeclared
  *    * The variable must be declared in the fragment shader
- *    * The variable has to be bound as the color output 
+ *    * The variable has to be bound as the color output
  *      (using glBindFragDataLocation)
  *    If the function returns false:
  *    * Parameter var's name will be set to the GLSL built-in color output name.
@@ -69,8 +115,66 @@
  * In either case var is initialized to represent the color output in the
  * shader.
  */
- bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
+bool GrGLSLSetupFSColorOuput(GrGLSLGeneration gen,
                              const char* nameIfDeclared,
                              GrGLShaderVar* var);
 
+/** Convert a count of 1..n floats into the corresponding type enum,
+    e.g. 1 -> kFloat_GrSLType, 2 -> kVec2_GrSLType, ... */
+GrSLType GrSLFloatVectorType(int count);
+
+/** Return the GLSL swizzle operator for a homogenous component of a vector
+    with the given number of coordinates, e.g. 2 -> ".y", 3 -> ".z" */
+const char* GrGLSLVectorHomogCoord(int count);
+const char* GrGLSLVectorHomogCoord(GrSLType type);
+
+/** Return the GLSL swizzle operator for a nonhomogenous components of a vector
+    with the given number of coordinates, e.g. 2 -> ".x", 3 -> ".xy" */
+const char* GrGLSLVectorNonhomogCoords(int count);
+const char* GrGLSLVectorNonhomogCoords(GrSLType type);
+
+/**
+  * Produces a string that is the result of modulating two inputs. The inputs must be vec4 or
+  * float. The result is always a vec4. The inputs may be expressions, not just identifier names.
+  * Either can be NULL or "" in which case the default params control whether vec4(1,1,1,1) or
+  * vec4(0,0,0,0) is assumed. It is an error to pass kNone for default<i> if in<i> is NULL or "".
+  * Note that when if function determines that the result is a zeros or ones vec then any expression
+  * represented by in0 or in1 will not be emitted. The return value indicates whether a zeros, ones
+  * or neither was appended.
+  */
+GrSLConstantVec GrGLSLModulate4f(SkString* outAppend,
+                                 const char* in0,
+                                 const char* in1,
+                                 GrSLConstantVec default0 = kOnes_GrSLConstantVec,
+                                 GrSLConstantVec default1 = kOnes_GrSLConstantVec);
+
+/**
+ * Does an inplace mul, *=, of vec4VarName by mulFactor. If mulFactorDefault is not kNone then
+ * mulFactor may be either "" or NULL. In this case either nothing will be appended (kOnes) or an
+ * assignment of vec(0,0,0,0) will be appended (kZeros). The assignment is prepended by tabCnt tabs.
+ * A semicolon and newline are added after the assignment. (TODO: Remove tabCnt when we auto-insert
+ * tabs to GrGLEffect-generated lines.) If a zeros vec is assigned then the return value is
+ * kZeros, otherwise kNone.
+ */
+GrSLConstantVec GrGLSLMulVarBy4f(SkString* outAppend,
+                                 int tabCnt,
+                                 const char* vec4VarName,
+                                 const char* mulFactor,
+                                 GrSLConstantVec mulFactorDefault = kOnes_GrSLConstantVec);
+
+/**
+  * Produces a string that is the result of adding two inputs. The inputs must be vec4 or float.
+  * The result is always a vec4. The inputs may be expressions, not just identifier names. Either
+  * can be NULL or "" in which case if the default is kZeros then vec4(0,0,0,0) is assumed. It is an
+  * error to pass kOnes for either default or to pass kNone for default<i> if in<i> is NULL or "".
+  * Note that if the function determines that the result is a zeros vec any expression represented
+  * by in0 or in1 will not be emitted. The return value indicates whether a zeros vec was appended
+  * or not.
+  */
+GrSLConstantVec GrGLSLAdd4f(SkString* outAppend,
+                            const char* in0,
+                            const char* in1,
+                            GrSLConstantVec default0 = kZeros_GrSLConstantVec,
+                            GrSLConstantVec default1 = kZeros_GrSLConstantVec);
+
 #endif
diff --git a/src/gpu/gl/GrGLShaderBuilder.cpp b/src/gpu/gl/GrGLShaderBuilder.cpp
new file mode 100644
index 0000000..a8514ad
--- /dev/null
+++ b/src/gpu/gl/GrGLShaderBuilder.cpp
@@ -0,0 +1,450 @@
+/*
+ * 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 "gl/GrGLShaderBuilder.h"
+#include "gl/GrGLProgram.h"
+#include "gl/GrGLUniformHandle.h"
+#include "GrTexture.h"
+
+// number of each input/output type in a single allocation block
+static const int kVarsPerBlock = 8;
+
+// except FS outputs where we expect 2 at most.
+static const int kMaxFSOutputs = 2;
+
+// ES2 FS only guarantees mediump and lowp support
+static const GrGLShaderVar::Precision kDefaultFragmentPrecision = GrGLShaderVar::kMedium_Precision;
+
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+///////////////////////////////////////////////////////////////////////////////
+
+namespace {
+
+inline const char* sample_function_name(GrSLType type) {
+    if (kVec2f_GrSLType == type) {
+        return "texture2D";
+    } else {
+        GrAssert(kVec3f_GrSLType == type);
+        return "texture2DProj";
+    }
+}
+
+/**
+ * Do we need to either map r,g,b->a or a->r.
+ */
+inline bool swizzle_requires_alpha_remapping(const GrGLCaps& caps,
+                                             const GrTextureAccess& access) {
+    if (GrPixelConfigIsAlphaOnly(access.getTexture()->config())) {
+        if (caps.textureRedSupport() && (GrTextureAccess::kA_SwizzleFlag & access.swizzleMask())) {
+            return true;
+        }
+        if (GrTextureAccess::kRGB_SwizzleMask & access.swizzleMask()) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void append_swizzle(SkString* outAppend,
+                    const GrTextureAccess& access,
+                    const GrGLCaps& caps) {
+    const char* swizzle = access.getSwizzle();
+    char mangledSwizzle[5];
+
+    // The swizzling occurs using texture params instead of shader-mangling if ARB_texture_swizzle
+    // is available.
+    if (!caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(access.getTexture()->config())) {
+        char alphaChar = caps.textureRedSupport() ? 'r' : 'a';
+        int i;
+        for (i = 0; '\0' != swizzle[i]; ++i) {
+            mangledSwizzle[i] = alphaChar;
+        }
+        mangledSwizzle[i] ='\0';
+        swizzle = mangledSwizzle;
+    }
+    // For shader prettiness we omit the swizzle rather than appending ".rgba".
+    if (memcmp(swizzle, "rgba", 4)) {
+        outAppend->appendf(".%s", swizzle);
+    }
+}
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// Architectural assumption: always 2-d input coords.
+// Likely to become non-constant and non-static, perhaps even
+// varying by stage, if we use 1D textures for gradients!
+//const int GrGLShaderBuilder::fCoordDims = 2;
+
+GrGLShaderBuilder::GrGLShaderBuilder(const GrGLContextInfo& ctx, GrGLUniformManager& uniformManager)
+    : fUniforms(kVarsPerBlock)
+    , fVSAttrs(kVarsPerBlock)
+    , fVSOutputs(kVarsPerBlock)
+    , fGSInputs(kVarsPerBlock)
+    , fGSOutputs(kVarsPerBlock)
+    , fFSInputs(kVarsPerBlock)
+    , fFSOutputs(kMaxFSOutputs)
+    , fUsesGS(false)
+    , fContext(ctx)
+    , fUniformManager(uniformManager)
+    , fCurrentStageIdx(kNonStageIdx)
+    , fSetupFragPosition(false)
+    , fRTHeightUniform(GrGLUniformManager::kInvalidUniformHandle) {
+
+    fPositionVar = &fVSAttrs.push_back();
+    fPositionVar->set(kVec2f_GrSLType, GrGLShaderVar::kAttribute_TypeModifier, "aPosition");
+}
+
+void GrGLShaderBuilder::appendTextureLookup(SkString* out,
+                                            const GrGLShaderBuilder::TextureSampler& sampler,
+                                            const char* coordName,
+                                            GrSLType varyingType) const {
+    GrAssert(NULL != sampler.textureAccess());
+    GrAssert(NULL != coordName);
+
+    out->appendf("%s(%s, %s)",
+                 sample_function_name(varyingType),
+                 this->getUniformCStr(sampler.fSamplerUniform),
+                 coordName);
+    append_swizzle(out, *sampler.textureAccess(), fContext.caps());
+}
+
+void GrGLShaderBuilder::appendTextureLookupAndModulate(
+                                            SkString* out,
+                                            const char* modulation,
+                                            const GrGLShaderBuilder::TextureSampler& sampler,
+                                            const char* coordName,
+                                            GrSLType varyingType) const {
+    GrAssert(NULL != out);
+    SkString lookup;
+    this->appendTextureLookup(&lookup, sampler, coordName, varyingType);
+    GrGLSLModulate4f(out, modulation, lookup.c_str());
+}
+
+GrBackendEffectFactory::EffectKey GrGLShaderBuilder::KeyForTextureAccess(
+                                                            const GrTextureAccess& access,
+                                                            const GrGLCaps& caps) {
+    GrBackendEffectFactory::EffectKey key = 0;
+
+    // Assume that swizzle support implies that we never have to modify a shader to adjust
+    // for texture format/swizzle settings.
+    if (!caps.textureSwizzleSupport() && swizzle_requires_alpha_remapping(caps, access)) {
+        key = 1;
+    }
+#if GR_DEBUG
+    // Assert that key is set iff the swizzle will be modified.
+    SkString origString(access.getSwizzle());
+    origString.prepend(".");
+    SkString modifiedString;
+    append_swizzle(&modifiedString, access, caps);
+    if (!modifiedString.size()) {
+        modifiedString = ".rgba";
+    }
+    GrAssert(SkToBool(key) == (modifiedString != origString));
+#endif
+    return key;
+}
+
+const GrGLenum* GrGLShaderBuilder::GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps) {
+    if (caps.textureSwizzleSupport() && GrPixelConfigIsAlphaOnly(config)) {
+        if (caps.textureRedSupport()) {
+            static const GrGLenum gRedSmear[] = { GR_GL_RED, GR_GL_RED, GR_GL_RED, GR_GL_RED };
+            return gRedSmear;
+        } else {
+            static const GrGLenum gAlphaSmear[] = { GR_GL_ALPHA, GR_GL_ALPHA,
+                                                    GR_GL_ALPHA, GR_GL_ALPHA };
+            return gAlphaSmear;
+        }
+    } else {
+        static const GrGLenum gStraight[] = { GR_GL_RED, GR_GL_GREEN, GR_GL_BLUE, GR_GL_ALPHA };
+        return gStraight;
+    }
+}
+
+GrGLUniformManager::UniformHandle GrGLShaderBuilder::addUniformArray(uint32_t visibility,
+                                                                     GrSLType type,
+                                                                     const char* name,
+                                                                     int count,
+                                                                     const char** outName) {
+    GrAssert(name && strlen(name));
+    SkDEBUGCODE(static const uint32_t kVisibilityMask = kVertex_ShaderType | kFragment_ShaderType);
+    GrAssert(0 == (~kVisibilityMask & visibility));
+    GrAssert(0 != visibility);
+
+    BuilderUniform& uni = fUniforms.push_back();
+    UniformHandle h = index_to_handle(fUniforms.count() - 1);
+    GR_DEBUGCODE(UniformHandle h2 =)
+    fUniformManager.appendUniform(type, count);
+    // We expect the uniform manager to initially have no uniforms and that all uniforms are added
+    // by this function. Therefore, the handles should match.
+    GrAssert(h2 == h);
+    uni.fVariable.setType(type);
+    uni.fVariable.setTypeModifier(GrGLShaderVar::kUniform_TypeModifier);
+    SkString* uniName = uni.fVariable.accessName();
+    if (kNonStageIdx == fCurrentStageIdx) {
+        uniName->printf("u%s", name);
+    } else {
+        uniName->printf("u%s%d", name, fCurrentStageIdx);
+    }
+    uni.fVariable.setArrayCount(count);
+    uni.fVisibility = visibility;
+
+    // If it is visible in both the VS and FS, the precision must match.
+    // We declare a default FS precision, but not a default VS. So set the var
+    // to use the default FS precision.
+    if ((kVertex_ShaderType | kFragment_ShaderType) == visibility) {
+        // the fragment and vertex precisions must match
+        uni.fVariable.setPrecision(kDefaultFragmentPrecision);
+    }
+
+    if (NULL != outName) {
+        *outName = uni.fVariable.c_str();
+    }
+
+    return h;
+}
+
+const GrGLShaderVar& GrGLShaderBuilder::getUniformVariable(UniformHandle u) const {
+    return fUniforms[handle_to_index(u)].fVariable;
+}
+
+void GrGLShaderBuilder::addVarying(GrSLType type,
+                                   const char* name,
+                                   const char** vsOutName,
+                                   const char** fsInName) {
+    fVSOutputs.push_back();
+    fVSOutputs.back().setType(type);
+    fVSOutputs.back().setTypeModifier(GrGLShaderVar::kOut_TypeModifier);
+    if (kNonStageIdx == fCurrentStageIdx) {
+        fVSOutputs.back().accessName()->printf("v%s", name);
+    } else {
+        fVSOutputs.back().accessName()->printf("v%s%d", name, fCurrentStageIdx);
+    }
+    if (vsOutName) {
+        *vsOutName = fVSOutputs.back().getName().c_str();
+    }
+    // input to FS comes either from VS or GS
+    const SkString* fsName;
+    if (fUsesGS) {
+        // if we have a GS take each varying in as an array
+        // and output as non-array.
+        fGSInputs.push_back();
+        fGSInputs.back().setType(type);
+        fGSInputs.back().setTypeModifier(GrGLShaderVar::kIn_TypeModifier);
+        fGSInputs.back().setUnsizedArray();
+        *fGSInputs.back().accessName() = fVSOutputs.back().getName();
+        fGSOutputs.push_back();
+        fGSOutputs.back().setType(type);
+        fGSOutputs.back().setTypeModifier(GrGLShaderVar::kOut_TypeModifier);
+        if (kNonStageIdx == fCurrentStageIdx) {
+            fGSOutputs.back().accessName()->printf("g%s", name);
+        } else {
+            fGSOutputs.back().accessName()->printf("g%s%d", name, fCurrentStageIdx);
+        }
+        fsName = fGSOutputs.back().accessName();
+    } else {
+        fsName = fVSOutputs.back().accessName();
+    }
+    fFSInputs.push_back();
+    fFSInputs.back().setType(type);
+    fFSInputs.back().setTypeModifier(GrGLShaderVar::kIn_TypeModifier);
+    fFSInputs.back().setName(*fsName);
+    if (fsInName) {
+        *fsInName = fsName->c_str();
+    }
+}
+
+const char* GrGLShaderBuilder::fragmentPosition() {
+    if (fContext.caps().fragCoordConventionsSupport()) {
+        if (!fSetupFragPosition) {
+            fFSHeader.append("#extension GL_ARB_fragment_coord_conventions: require\n");
+            fFSInputs.push_back().set(kVec4f_GrSLType,
+                                      GrGLShaderVar::kIn_TypeModifier,
+                                      "gl_FragCoord",
+                                      GrGLShaderVar::kDefault_Precision,
+                                      GrGLShaderVar::kUpperLeft_Origin);
+            fSetupFragPosition = true;
+        }
+        return "gl_FragCoord";
+    } else {
+        static const char* kCoordName = "fragCoordYDown";
+        if (!fSetupFragPosition) {
+            GrAssert(GrGLUniformManager::kInvalidUniformHandle == fRTHeightUniform);
+            const char* rtHeightName;
+
+            // temporarily change the stage index because we're inserting a uniform whose name
+            // shouldn't be mangled to be stage-specific.
+            int oldStageIdx = fCurrentStageIdx;
+            fCurrentStageIdx = kNonStageIdx;
+            fRTHeightUniform = this->addUniform(kFragment_ShaderType,
+                                                kFloat_GrSLType,
+                                                "RTHeight",
+                                                &rtHeightName);
+            fCurrentStageIdx = oldStageIdx;
+
+            this->fFSCode.prependf("\tvec4 %s = vec4(gl_FragCoord.x, %s - gl_FragCoord.y, gl_FragCoord.zw);\n",
+                                   kCoordName, rtHeightName);
+            fSetupFragPosition = true;
+        }
+        GrAssert(GrGLUniformManager::kInvalidUniformHandle != fRTHeightUniform);
+        return kCoordName;
+    }
+}
+
+
+void GrGLShaderBuilder::emitFunction(ShaderType shader,
+                                     GrSLType returnType,
+                                     const char* name,
+                                     int argCnt,
+                                     const GrGLShaderVar* args,
+                                     const char* body,
+                                     SkString* outName) {
+    GrAssert(kFragment_ShaderType == shader);
+    fFSFunctions.append(GrGLShaderVar::TypeString(returnType));
+    if (kNonStageIdx != fCurrentStageIdx) {
+        outName->printf(" %s_%d", name, fCurrentStageIdx);
+    } else {
+        *outName = name;
+    }
+    fFSFunctions.append(*outName);
+    fFSFunctions.append("(");
+    for (int i = 0; i < argCnt; ++i) {
+        args[i].appendDecl(fContext, &fFSFunctions);
+        if (i < argCnt - 1) {
+            fFSFunctions.append(", ");
+        }
+    }
+    fFSFunctions.append(") {\n");
+    fFSFunctions.append(body);
+    fFSFunctions.append("}\n\n");
+}
+
+namespace {
+
+inline void append_default_precision_qualifier(GrGLShaderVar::Precision p,
+                                               GrGLBinding binding,
+                                               SkString* str) {
+    // Desktop GLSL has added precision qualifiers but they don't do anything.
+    if (kES2_GrGLBinding == binding) {
+        switch (p) {
+            case GrGLShaderVar::kHigh_Precision:
+                str->append("precision highp float;\n");
+                break;
+            case GrGLShaderVar::kMedium_Precision:
+                str->append("precision mediump float;\n");
+                break;
+            case GrGLShaderVar::kLow_Precision:
+                str->append("precision lowp float;\n");
+                break;
+            case GrGLShaderVar::kDefault_Precision:
+                GrCrash("Default precision now allowed.");
+            default:
+                GrCrash("Unknown precision value.");
+        }
+    }
+}
+}
+
+void GrGLShaderBuilder::appendDecls(const VarArray& vars, SkString* out) const {
+    for (int i = 0; i < vars.count(); ++i) {
+        vars[i].appendDecl(fContext, out);
+        out->append(";\n");
+    }
+}
+
+void GrGLShaderBuilder::appendUniformDecls(ShaderType stype, SkString* out) const {
+    for (int i = 0; i < fUniforms.count(); ++i) {
+        if (fUniforms[i].fVisibility & stype) {
+            fUniforms[i].fVariable.appendDecl(fContext, out);
+            out->append(";\n");
+        }
+    }
+}
+
+void GrGLShaderBuilder::getShader(ShaderType type, SkString* shaderStr) const {
+    switch (type) {
+        case kVertex_ShaderType:
+            *shaderStr = fHeader;
+            this->appendUniformDecls(kVertex_ShaderType, shaderStr);
+            this->appendDecls(fVSAttrs, shaderStr);
+            this->appendDecls(fVSOutputs, shaderStr);
+            shaderStr->append("void main() {\n");
+            shaderStr->append(fVSCode);
+            shaderStr->append("}\n");
+            break;
+        case kGeometry_ShaderType:
+            if (fUsesGS) {
+                *shaderStr = fHeader;
+                shaderStr->append(fGSHeader);
+                this->appendDecls(fGSInputs, shaderStr);
+                this->appendDecls(fGSOutputs, shaderStr);
+                shaderStr->append("void main() {\n");
+                shaderStr->append(fGSCode);
+                shaderStr->append("}\n");
+            } else {
+                shaderStr->reset();
+            }
+            break;
+        case kFragment_ShaderType:
+            *shaderStr = fHeader;
+            append_default_precision_qualifier(kDefaultFragmentPrecision,
+                                               fContext.binding(),
+                                               shaderStr);
+            shaderStr->append(fFSHeader);
+            this->appendUniformDecls(kFragment_ShaderType, shaderStr);
+            this->appendDecls(fFSInputs, shaderStr);
+            // We shouldn't have declared outputs on 1.10
+            GrAssert(k110_GrGLSLGeneration != fContext.glslGeneration() || fFSOutputs.empty());
+            this->appendDecls(fFSOutputs, shaderStr);
+            shaderStr->append(fFSFunctions);
+            shaderStr->append("void main() {\n");
+            shaderStr->append(fFSCode);
+            shaderStr->append("}\n");
+            break;
+    }
+ }
+
+void GrGLShaderBuilder::finished(GrGLuint programID) {
+    fUniformManager.getUniformLocations(programID, fUniforms);
+}
+
+GrGLEffect* GrGLShaderBuilder::createAndEmitGLEffect(
+                                const GrEffectStage& stage,
+                                GrGLEffect::EffectKey key,
+                                const char* fsInColor,
+                                const char* fsOutColor,
+                                const char* vsInCoord,
+                                SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles) {
+    GrAssert(NULL != stage.getEffect());
+
+    const GrEffectRef& effect = *stage.getEffect();
+    int numTextures = effect->numTextures();
+    SkSTArray<8, GrGLShaderBuilder::TextureSampler> textureSamplers;
+    textureSamplers.push_back_n(numTextures);
+    for (int i = 0; i < numTextures; ++i) {
+        textureSamplers[i].init(this, &effect->textureAccess(i), i);
+        samplerHandles->push_back(textureSamplers[i].fSamplerUniform);
+    }
+
+    GrGLEffect* glEffect = effect->getFactory().createGLInstance(effect);
+
+    // Enclose custom code in a block to avoid namespace conflicts
+    this->fVSCode.appendf("\t{ // %s\n", glEffect->name());
+    this->fFSCode.appendf("\t{ // %s \n", glEffect->name());
+    glEffect->emitCode(this,
+                       stage,
+                       key,
+                       vsInCoord,
+                       fsOutColor,
+                       fsInColor,
+                       textureSamplers);
+    this->fVSCode.appendf("\t}\n");
+    this->fFSCode.appendf("\t}\n");
+
+    return glEffect;
+}
diff --git a/src/gpu/gl/GrGLShaderBuilder.h b/src/gpu/gl/GrGLShaderBuilder.h
new file mode 100644
index 0000000..87136cd
--- /dev/null
+++ b/src/gpu/gl/GrGLShaderBuilder.h
@@ -0,0 +1,232 @@
+/*
+ * 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 GrGLShaderBuilder_DEFINED
+#define GrGLShaderBuilder_DEFINED
+
+#include "GrAllocator.h"
+#include "GrBackendEffectFactory.h"
+#include "GrEffect.h"
+#include "gl/GrGLSL.h"
+#include "gl/GrGLUniformManager.h"
+
+class GrGLContextInfo;
+
+/**
+  Contains all the incremental state of a shader as it is being built,as well as helpers to
+  manipulate that state.
+*/
+class GrGLShaderBuilder {
+public:
+    /**
+     * Passed to GrGLEffects to add texture reads to their shader code.
+     */
+    class TextureSampler {
+    public:
+        TextureSampler()
+            : fTextureAccess(NULL)
+            , fSamplerUniform(GrGLUniformManager::kInvalidUniformHandle) {}
+
+        TextureSampler(const TextureSampler& other) { *this = other; }
+
+        TextureSampler& operator= (const TextureSampler& other) {
+            GrAssert(NULL == fTextureAccess);
+            GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform);
+
+            fTextureAccess = other.fTextureAccess;
+            fSamplerUniform = other.fSamplerUniform;
+            return *this;
+        }
+
+        const GrTextureAccess* textureAccess() const { return fTextureAccess; }
+
+    private:
+        // The idx param is used to ensure multiple samplers within a single effect have unique
+        // uniform names.
+        void init(GrGLShaderBuilder* builder, const GrTextureAccess* access, int idx) {
+            GrAssert(NULL == fTextureAccess);
+            GrAssert(GrGLUniformManager::kInvalidUniformHandle == fSamplerUniform);
+
+            GrAssert(NULL != builder);
+            GrAssert(NULL != access);
+            SkString name;
+            name.printf("Sampler%d_", idx);
+            fSamplerUniform = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
+                                                  kSampler2D_GrSLType,
+                                                  name.c_str());
+            GrAssert(GrGLUniformManager::kInvalidUniformHandle != fSamplerUniform);
+
+            fTextureAccess = access;
+        }
+
+        const GrTextureAccess*            fTextureAccess;
+        GrGLUniformManager::UniformHandle fSamplerUniform;
+
+        friend class GrGLShaderBuilder; // to access fSamplerUniform
+        friend class GrGLProgram;       // to construct these and access fSamplerUniform.
+    };
+
+    typedef SkTArray<TextureSampler> TextureSamplerArray;
+
+    enum ShaderType {
+        kVertex_ShaderType   = 0x1,
+        kGeometry_ShaderType = 0x2,
+        kFragment_ShaderType = 0x4,
+    };
+
+    GrGLShaderBuilder(const GrGLContextInfo&, GrGLUniformManager&);
+
+    /** Appends a 2D texture sample with projection if necessary. coordType must either be Vec2f or
+        Vec3f. The latter is interpreted as projective texture coords. The vec length and swizzle
+        order of the result depends on the GrTextureAccess associated with the TextureSampler. */
+    void appendTextureLookup(SkString* out,
+                             const TextureSampler&,
+                             const char* coordName,
+                             GrSLType coordType = kVec2f_GrSLType) const;
+
+    /** Does the work of appendTextureLookup and modulates the result by modulation. The result is
+        always a vec4. modulation and the swizzle specified by TextureSampler must both be vec4 or
+        float. If modulation is "" or NULL it this function acts as though appendTextureLookup were
+        called. */
+    void appendTextureLookupAndModulate(SkString* out,
+                                        const char* modulation,
+                                        const TextureSampler&,
+                                        const char* coordName,
+                                        GrSLType coordType = kVec2f_GrSLType) const;
+
+    /** Emits a helper function outside of main(). Currently ShaderType must be
+        kFragment_ShaderType. */
+    void emitFunction(ShaderType shader,
+                      GrSLType returnType,
+                      const char* name,
+                      int argCnt,
+                      const GrGLShaderVar* args,
+                      const char* body,
+                      SkString* outName);
+
+    /** Generates a EffectKey for the shader code based on the texture access parameters and the
+        capabilities of the GL context.  This is useful for keying the shader programs that may
+        have multiple representations, based on the type/format of textures used. */
+    static GrBackendEffectFactory::EffectKey KeyForTextureAccess(const GrTextureAccess&,
+                                                                 const GrGLCaps&);
+
+    /** If texture swizzling is available using tex parameters then it is preferred over mangling
+        the generated shader code. This potentially allows greater reuse of cached shaders. */
+    static const GrGLenum* GetTexParamSwizzle(GrPixelConfig config, const GrGLCaps& caps);
+
+    /** Add a uniform variable to the current program, that has visibility in one or more shaders.
+        visibility is a bitfield of ShaderType values indicating from which shaders the uniform
+        should be accessible. At least one bit must be set. Geometry shader uniforms are not
+        supported at this time. The actual uniform name will be mangled. If outName is not NULL then
+        it will refer to the final uniform name after return. Use the addUniformArray variant to add
+        an array of uniforms.
+    */
+    GrGLUniformManager::UniformHandle addUniform(uint32_t visibility,
+                                                 GrSLType type,
+                                                 const char* name,
+                                                 const char** outName = NULL) {
+        return this->addUniformArray(visibility, type, name, GrGLShaderVar::kNonArray, outName);
+    }
+    GrGLUniformManager::UniformHandle addUniformArray(uint32_t visibility,
+                                                      GrSLType type,
+                                                      const char* name,
+                                                      int arrayCount,
+                                                      const char** outName = NULL);
+
+    const GrGLShaderVar& getUniformVariable(GrGLUniformManager::UniformHandle) const;
+
+    /**
+     * Shortcut for getUniformVariable(u).c_str()
+     */
+    const char* getUniformCStr(GrGLUniformManager::UniformHandle u) const {
+        return this->getUniformVariable(u).c_str();
+    }
+
+    /** Add a varying variable to the current program to pass values between vertex and fragment
+        shaders. If the last two parameters are non-NULL, they are filled in with the name
+        generated. */
+    void addVarying(GrSLType type,
+                    const char* name,
+                    const char** vsOutName = NULL,
+                    const char** fsInName = NULL);
+
+    /** Returns a variable name that represents the position of the fragment in the FS. The position
+        is in device space (e.g. 0,0 is the top left and pixel centers are at half-integers). */
+    const char* fragmentPosition();
+
+    /** Returns a vertex attribute that represents the vertex position in the VS. This is the
+        pre-matrix position and is commonly used by effects to compute texture coords via a matrix.
+      */
+    const GrGLShaderVar& positionAttribute() const { return *fPositionVar; }
+
+    /**
+     * Interfaces used by GrGLProgram.
+     * TODO: Hide these from the GrEffects using friend or splitting this into two related classes.
+     * Also, GrGLProgram's shader string construction should be moved to this class.
+     */
+
+    /** Called after building is complete to get the final shader string. */
+    void getShader(ShaderType, SkString*) const;
+
+    void setCurrentStage(int stageIdx) { fCurrentStageIdx = stageIdx; }
+    void setNonStage() { fCurrentStageIdx = kNonStageIdx; }
+    // TODO: move remainder of shader code generation to this class and call this privately
+    // Handles of sampler uniforms generated for the effect are appended to samplerHandles.
+    GrGLEffect* createAndEmitGLEffect(
+                                const GrEffectStage& stage,
+                                GrBackendEffectFactory::EffectKey key,
+                                const char* fsInColor, // NULL means no incoming color
+                                const char* fsOutColor,
+                                const char* vsInCoord,
+                                SkTArray<GrGLUniformManager::UniformHandle, true>* samplerHandles);
+    GrGLUniformManager::UniformHandle getRTHeightUniform() const { return fRTHeightUniform; }
+    // TODO: Make this do all the compiling, linking, etc.
+    void finished(GrGLuint programID);
+
+private:
+    typedef GrTAllocator<GrGLShaderVar> VarArray;
+
+    void appendDecls(const VarArray&, SkString*) const;
+    void appendUniformDecls(ShaderType, SkString*) const;
+
+    typedef GrGLUniformManager::BuilderUniform BuilderUniform;
+    GrGLUniformManager::BuilderUniformArray fUniforms;
+
+    // TODO: Everything below here private.
+public:
+
+    SkString    fHeader; // VS+FS, GLSL version, etc
+    VarArray    fVSAttrs;
+    VarArray    fVSOutputs;
+    VarArray    fGSInputs;
+    VarArray    fGSOutputs;
+    VarArray    fFSInputs;
+    SkString    fGSHeader; // layout qualifiers specific to GS
+    VarArray    fFSOutputs;
+    SkString    fVSCode;
+    SkString    fGSCode;
+    SkString    fFSCode;
+    bool        fUsesGS;
+
+private:
+    enum {
+        kNonStageIdx = -1,
+    };
+
+    const GrGLContextInfo&              fContext;
+    GrGLUniformManager&                 fUniformManager;
+    int                                 fCurrentStageIdx;
+    SkString                            fFSFunctions;
+    SkString                            fFSHeader;
+
+    bool                                fSetupFragPosition;
+    GrGLUniformManager::UniformHandle   fRTHeightUniform;
+
+    GrGLShaderVar*                      fPositionVar;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLShaderVar.h b/src/gpu/gl/GrGLShaderVar.h
index dc7d52b..89fa087 100644
--- a/src/gpu/gl/GrGLShaderVar.h
+++ b/src/gpu/gl/GrGLShaderVar.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -11,7 +10,7 @@
 
 #include "GrGLContextInfo.h"
 #include "GrGLSL.h"
-#include "../GrStringBuilder.h"
+#include "SkString.h"
 
 #define USE_UNIFORM_FLOAT_ARRAYS true
 
@@ -21,16 +20,6 @@
 class GrGLShaderVar {
 public:
 
-    enum Type {
-        kFloat_Type,
-        kVec2f_Type,
-        kVec3f_Type,
-        kVec4f_Type,
-        kMat33f_Type,
-        kMat44f_Type,
-        kSampler2D_Type,
-    };
-
     /**
      * Early versions of GLSL have Varying and Attribute; those are later
      * deprecated, but we still need to know whether a Varying variable
@@ -44,24 +33,58 @@
         kAttribute_TypeModifier
     };
 
+    enum Precision {
+        kLow_Precision,         // lowp
+        kMedium_Precision,      // mediump
+        kHigh_Precision,        // highp
+        kDefault_Precision,     // Default for the current context. We make
+                                // fragment shaders default to mediump on ES2
+                                // because highp support is not guaranteed (and
+                                // we haven't been motivated to test for it).
+                                // Otherwise, highp.
+    };
+
+    /**
+     * See GL_ARB_fragment_coord_conventions.
+     */
+    enum Origin {
+        kDefault_Origin,        // when set to kDefault the origin field is ignored.
+        kUpperLeft_Origin,      // only used to declare vec4 in gl_FragCoord.
+    };
+
     /**
      * Defaults to a float with no precision specifier
      */
     GrGLShaderVar() {
-        fType = kFloat_Type;
+        fType = kFloat_GrSLType;
         fTypeModifier = kNone_TypeModifier;
         fCount = kNonArray;
-        fEmitPrecision = false;
+        fPrecision = kDefault_Precision;
+        fOrigin = kDefault_Origin;
         fUseUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS;
     }
 
+    GrGLShaderVar(const char* name, GrSLType type, int arrayCount = kNonArray) {
+        GrAssert(kVoid_GrSLType != type);
+        fType = type;
+        fTypeModifier = kNone_TypeModifier;
+        fCount = arrayCount;
+        fPrecision = kDefault_Precision;
+        fOrigin = kDefault_Origin;
+        fUseUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS;
+        fName = name;
+    }
+
     GrGLShaderVar(const GrGLShaderVar& var)
         : fType(var.fType)
         , fTypeModifier(var.fTypeModifier)
         , fName(var.fName)
         , fCount(var.fCount)
-        , fEmitPrecision(var.fEmitPrecision)
-        , fUseUniformFloatArrays(var.fUseUniformFloatArrays) {}
+        , fPrecision(var.fPrecision)
+        , fOrigin(var.fOrigin)
+        , fUseUniformFloatArrays(var.fUseUniformFloatArrays) {
+        GrAssert(kVoid_GrSLType != var.fType);
+    }
 
     /**
      * Values for array count that have special meaning. We allow 1-sized arrays.
@@ -74,66 +97,78 @@
     /**
      * Sets as a non-array.
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
-             const GrStringBuilder& name,
-             bool emitPrecision = false,
+             const SkString& name,
+             Precision precision = kDefault_Precision,
+             Origin origin = kDefault_Origin,
              bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
         fType = type;
         fTypeModifier = typeModifier;
         fName = name;
         fCount = kNonArray;
-        fEmitPrecision = emitPrecision;
+        fPrecision = precision;
+        fOrigin = origin;
         fUseUniformFloatArrays = useUniformFloatArrays;
     }
 
     /**
      * Sets as a non-array.
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
              const char* name,
-             bool specifyPrecision = false,
+             Precision precision = kDefault_Precision,
+             Origin origin = kDefault_Origin,
              bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
         fType = type;
         fTypeModifier = typeModifier;
         fName = name;
         fCount = kNonArray;
-        fEmitPrecision = specifyPrecision;
+        fPrecision = precision;
+        fOrigin = origin;
         fUseUniformFloatArrays = useUniformFloatArrays;
     }
 
     /**
      * Set all var options
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
-             const GrStringBuilder& name,
+             const SkString& name,
              int count,
-             bool specifyPrecision = false,
+             Precision precision = kDefault_Precision,
+             Origin origin = kDefault_Origin,
              bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
         fType = type;
         fTypeModifier = typeModifier;
         fName = name;
         fCount = count;
-        fEmitPrecision = specifyPrecision;
+        fPrecision = precision;
+        fOrigin = origin;
         fUseUniformFloatArrays = useUniformFloatArrays;
     }
 
     /**
      * Set all var options
      */
-    void set(Type type,
+    void set(GrSLType type,
              TypeModifier typeModifier,
              const char* name,
              int count,
-             bool specifyPrecision = false,
+             Precision precision = kDefault_Precision,
+             Origin origin = kDefault_Origin,
              bool useUniformFloatArrays = USE_UNIFORM_FLOAT_ARRAYS) {
+        GrAssert(kVoid_GrSLType != type);
         fType = type;
         fTypeModifier = typeModifier;
         fName = name;
         fCount = count;
-        fEmitPrecision = specifyPrecision;
+        fPrecision = precision;
+        fOrigin = origin;
         fUseUniformFloatArrays = useUniformFloatArrays;
     }
 
@@ -165,60 +200,79 @@
     /**
      * Access the var name as a writable string
      */
-    GrStringBuilder* accessName() { return &fName; }
+    SkString* accessName() { return &fName; }
     /**
      * Set the var name
      */
-    void setName(const GrStringBuilder& n) { fName = n; }
+    void setName(const SkString& n) { fName = n; }
     void setName(const char* n) { fName = n; }
+
     /**
      * Get the var name.
      */
-    const GrStringBuilder& getName() const { return fName; }
+    const SkString& getName() const { return fName; }
+
+    /**
+     * Shortcut for this->getName().c_str();
+     */
+    const char* c_str() const { return this->getName().c_str(); }
 
     /**
      * Get the type of the var
      */
-    Type getType() const { return fType; }
+    GrSLType getType() const { return fType; }
     /**
      * Set the type of the var
      */
-    void setType(Type type) { fType = type; }
+    void setType(GrSLType type) { fType = type; }
 
     TypeModifier getTypeModifier() const { return fTypeModifier; }
     void setTypeModifier(TypeModifier type) { fTypeModifier = type; }
 
     /**
-     * Must the variable declaration emit a precision specifier
+     * Get the precision of the var
      */
-    bool emitsPrecision() const { return fEmitPrecision; }
+    Precision getPrecision() const { return fPrecision; }
+
     /**
-     * Specify whether the declaration should specify precision
+     * Set the precision of the var
      */
-    void setEmitPrecision(bool p) { fEmitPrecision = p; }
+    void setPrecision(Precision p) { fPrecision = p; }
+
+    /**
+     * Get the origin of the var
+     */
+    Origin getOrigin() const { return fOrigin; }
+
+    /**
+     * Set the origin of the var
+     */
+    void setOrigin(Origin origin) { fOrigin = origin; }
 
     /**
      * Write a declaration of this variable to out.
      */
-    void appendDecl(const GrGLContextInfo& gl, GrStringBuilder* out) const {
+    void appendDecl(const GrGLContextInfo& gl, SkString* out) const {
+        if (kUpperLeft_Origin == fOrigin) {
+            // this is the only place where we specify a layout modifier. If we use other layout
+            // modifiers in the future then they should be placed in a list.
+            out->append("layout(origin_upper_left) ");
+        }
         if (this->getTypeModifier() != kNone_TypeModifier) {
            out->append(TypeModifierString(this->getTypeModifier(),
                                           gl.glslGeneration()));
            out->append(" ");
         }
-        if (this->emitsPrecision()) {
-            out->append(GrGetGLSLVarPrecisionDeclType(gl.binding()));
-            out->append(" ");
-        }
-        Type effectiveType = this->getType();
+        out->append(PrecisionString(fPrecision, gl.binding()));
+        GrSLType effectiveType = this->getType();
         if (this->isArray()) {
             if (this->isUnsizedArray()) {
-                out->appendf("%s %s[]", 
-                             TypeString(effectiveType), 
+                out->appendf("%s %s[]",
+                             TypeString(effectiveType),
                              this->getName().c_str());
             } else {
                 GrAssert(this->getArrayCount() > 0);
-                out->appendf("%s %s[%d]", 
+                out->appendf("%s %s[%d]",
                              TypeString(effectiveType),
                              this->getName().c_str(),
                              this->getArrayCount());
@@ -228,24 +282,25 @@
                          TypeString(effectiveType),
                          this->getName().c_str());
         }
-        out->append(";\n");
     }
 
-    static const char* TypeString(Type t) {
+    static const char* TypeString(GrSLType t) {
         switch (t) {
-            case kFloat_Type:
+            case kVoid_GrSLType:
+                return "void";
+            case kFloat_GrSLType:
                 return "float";
-            case kVec2f_Type:
+            case kVec2f_GrSLType:
                 return "vec2";
-            case kVec3f_Type:
+            case kVec3f_GrSLType:
                 return "vec3";
-            case kVec4f_Type:
+            case kVec4f_GrSLType:
                 return "vec4";
-            case kMat33f_Type:
+            case kMat33f_GrSLType:
                 return "mat3";
-            case kMat44f_Type:
+            case kMat44f_GrSLType:
                 return "mat4";
-            case kSampler2D_Type:
+            case kSampler2D_GrSLType:
                 return "sampler2D";
             default:
                 GrCrash("Unknown shader var type.");
@@ -253,14 +308,14 @@
         }
     }
 
-    void appendArrayAccess(int index, GrStringBuilder* out) {
+    void appendArrayAccess(int index, SkString* out) const {
         out->appendf("%s[%d]%s",
                      this->getName().c_str(),
                      index,
                      fUseUniformFloatArrays ? "" : ".x");
     }
 
-    void appendArrayAccess(const char* indexName, GrStringBuilder* out) {
+    void appendArrayAccess(const char* indexName, SkString* out) const {
         out->appendf("%s[%s]%s",
                      this->getName().c_str(),
                      indexName,
@@ -268,8 +323,7 @@
     }
 
 private:
-    static const char* TypeModifierString(TypeModifier t,
-                                          GrGLSLGeneration gen) {
+    static const char* TypeModifierString(TypeModifier t, GrGLSLGeneration gen) {
         switch (t) {
             case kNone_TypeModifier:
                 return "";
@@ -287,11 +341,31 @@
         }
     }
 
-    Type            fType;
+    static const char* PrecisionString(Precision p, GrGLBinding binding) {
+        // Desktop GLSL has added precision qualifiers but they don't do anything.
+        if (kES2_GrGLBinding == binding) {
+            switch (p) {
+                case kLow_Precision:
+                    return "lowp ";
+                case kMedium_Precision:
+                    return "mediump ";
+                case kHigh_Precision:
+                    return "highp ";
+                case kDefault_Precision:
+                    return "";
+                default:
+                    GrCrash("Unexpected precision type.");
+            }
+        }
+        return "";
+    }
+
+    GrSLType        fType;
     TypeModifier    fTypeModifier;
-    GrStringBuilder fName;
+    SkString        fName;
     int             fCount;
-    bool            fEmitPrecision;
+    Precision       fPrecision;
+    Origin          fOrigin;
     /// Work around driver bugs on some hardware that don't correctly
     /// support uniform float []
     bool            fUseUniformFloatArrays;
diff --git a/src/gpu/gl/GrGLStencilBuffer.cpp b/src/gpu/gl/GrGLStencilBuffer.cpp
index bc0bb36..d9322c2 100644
--- a/src/gpu/gl/GrGLStencilBuffer.cpp
+++ b/src/gpu/gl/GrGLStencilBuffer.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -23,18 +22,18 @@
 }
 
 void GrGLStencilBuffer::onRelease() {
-    if (0 != fRenderbufferID) {
+    if (0 != fRenderbufferID && !this->isWrapped()) {
         GrGpuGL* gpuGL = (GrGpuGL*) this->getGpu();
         const GrGLInterface* gl = gpuGL->glInterface();
         GR_GL_CALL(gl, DeleteRenderbuffers(1, &fRenderbufferID));
         fRenderbufferID = 0;
     }
+
     INHERITED::onRelease();
 }
 
 void GrGLStencilBuffer::onAbandon() {
     fRenderbufferID = 0;
+
     INHERITED::onAbandon();
 }
-
-
diff --git a/src/gpu/gl/GrGLStencilBuffer.h b/src/gpu/gl/GrGLStencilBuffer.h
index 908921a..2bf33ef 100644
--- a/src/gpu/gl/GrGLStencilBuffer.h
+++ b/src/gpu/gl/GrGLStencilBuffer.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -11,12 +10,12 @@
 #define GrGLStencilBuffer_DEFINED
 
 #include "gl/GrGLInterface.h"
-#include "../GrStencilBuffer.h"
+#include "GrStencilBuffer.h"
 
 class GrGLStencilBuffer : public GrStencilBuffer {
 public:
-    static const GrGLenum kUnknownInternalFormat = ~0;
-    static const GrGLuint kUnknownBitCount = ~0;
+    static const GrGLenum kUnknownInternalFormat = ~0U;
+    static const GrGLuint kUnknownBitCount = ~0U;
     struct Format {
         GrGLenum  fInternalFormat;
         GrGLuint  fStencilBits;
@@ -24,18 +23,20 @@
         bool      fPacked;
     };
 
-    GrGLStencilBuffer(GrGpu* gpu, GrGLint rbid, 
+    GrGLStencilBuffer(GrGpu* gpu,
+                      bool isWrapped,
+                      GrGLint rbid,
                       int width, int height,
                       int sampleCnt,
-                      const Format& format) 
-        : GrStencilBuffer(gpu, width, height, format.fStencilBits, sampleCnt)
+                      const Format& format)
+        : GrStencilBuffer(gpu, isWrapped, width, height, format.fStencilBits, sampleCnt)
         , fFormat(format)
         , fRenderbufferID(rbid) {
     }
 
     virtual ~GrGLStencilBuffer();
 
-    virtual size_t sizeInBytes() const;
+    virtual size_t sizeInBytes() const SK_OVERRIDE;
 
     GrGLuint renderbufferID() const {
         return fRenderbufferID;
@@ -44,9 +45,9 @@
     const Format& format() const { return fFormat; }
 
 protected:
-    virtual void onRelease();
-
-    virtual void onAbandon();
+    // overrides of GrResource
+    virtual void onRelease() SK_OVERRIDE;
+    virtual void onAbandon() SK_OVERRIDE;
 
 private:
     Format fFormat;
diff --git a/src/gpu/gl/GrGLTexture.cpp b/src/gpu/gl/GrGLTexture.cpp
index 0a38da3..f798b31 100644
--- a/src/gpu/gl/GrGLTexture.cpp
+++ b/src/gpu/gl/GrGLTexture.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,24 +5,15 @@
  * found in the LICENSE file.
  */
 
-
 #include "GrGLTexture.h"
-
 #include "GrGpuGL.h"
 
+SK_DEFINE_INST_COUNT(GrGLTexID)
+
 #define GPUGL static_cast<GrGpuGL*>(getGpu())
 
 #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
 
-const GrGLenum* GrGLTexture::WrapMode2GLWrap() {
-    static const GrGLenum repeatModes[] = {
-        GR_GL_CLAMP_TO_EDGE,
-        GR_GL_REPEAT,
-        GR_GL_MIRRORED_REPEAT
-    };
-    return repeatModes;
-};
-
 void GrGLTexture::init(GrGpuGL* gpu,
                        const Desc& textureDesc,
                        const GrGLRenderTarget::Desc* rtDesc) {
@@ -32,59 +22,55 @@
 
     fTexParams.invalidate();
     fTexParamsTimestamp = GrGpu::kExpiredTimestamp;
-    fTexIDObj           = new GrGLTexID(GPUGL->glInterface(),
-                                        textureDesc.fTextureID,
-                                        textureDesc.fOwnsID);
-    fOrientation        = textureDesc.fOrientation;
+    fTexIDObj           = SkNEW_ARGS(GrGLTexID,
+                                     (GPUGL->glInterface(),
+                                      textureDesc.fTextureID,
+                                      textureDesc.fIsWrapped));
 
     if (NULL != rtDesc) {
-        // we render to the top left
+        GrAssert(kBottomLeft_GrSurfaceOrigin == textureDesc.fOrigin);
         GrGLIRect vp;
         vp.fLeft   = 0;
         vp.fWidth  = textureDesc.fWidth;
         vp.fBottom = 0;
         vp.fHeight = textureDesc.fHeight;
 
-        fRenderTarget = new GrGLRenderTarget(gpu, *rtDesc, vp, fTexIDObj, this);
+        fRenderTarget = SkNEW_ARGS(GrGLRenderTarget,
+                                   (gpu, *rtDesc, vp, fTexIDObj, this));
     }
 }
 
 GrGLTexture::GrGLTexture(GrGpuGL* gpu,
-                         const Desc& textureDesc) 
-    : INHERITED(gpu,
-                textureDesc.fWidth,
-                textureDesc.fHeight,
-                textureDesc.fConfig) {
+                         const Desc& textureDesc)
+    : INHERITED(gpu, textureDesc.fIsWrapped, textureDesc, textureDesc.fOrigin) {
     this->init(gpu, textureDesc, NULL);
 }
 
 GrGLTexture::GrGLTexture(GrGpuGL* gpu,
                          const Desc& textureDesc,
                          const GrGLRenderTarget::Desc& rtDesc)
-    : INHERITED(gpu,
-                textureDesc.fWidth,
-                textureDesc.fHeight,
-                textureDesc.fConfig) {
+    : INHERITED(gpu, textureDesc.fIsWrapped, textureDesc, textureDesc.fOrigin) {
     this->init(gpu, textureDesc, &rtDesc);
 }
 
 void GrGLTexture::onRelease() {
-    INHERITED::onRelease();
     GPUGL->notifyTextureDelete(this);
     if (NULL != fTexIDObj) {
         fTexIDObj->unref();
         fTexIDObj = NULL;
     }
+
+    INHERITED::onRelease();
 }
 
 void GrGLTexture::onAbandon() {
-    INHERITED::onAbandon();
     if (NULL != fTexIDObj) {
         fTexIDObj->abandon();
     }
+
+    INHERITED::onAbandon();
 }
 
-intptr_t GrGLTexture::getTextureHandle() const {
+GrBackendObject GrGLTexture::getTextureHandle() const {
     return fTexIDObj->id();
 }
-
diff --git a/src/gpu/gl/GrGLTexture.h b/src/gpu/gl/GrGLTexture.h
index d13fc44..2314821 100644
--- a/src/gpu/gl/GrGLTexture.h
+++ b/src/gpu/gl/GrGLTexture.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -10,23 +9,24 @@
 #ifndef GrGLTexture_DEFINED
 #define GrGLTexture_DEFINED
 
-#include "../GrGpu.h"
+#include "GrGpu.h"
 #include "GrGLRenderTarget.h"
 
 /**
  * A ref counted tex id that deletes the texture in its destructor.
  */
 class GrGLTexID : public GrRefCnt {
-
 public:
-    GrGLTexID(const GrGLInterface* gl, GrGLuint texID, bool ownsID)
+    SK_DECLARE_INST_COUNT(GrGLTexID)
+
+    GrGLTexID(const GrGLInterface* gl, GrGLuint texID, bool isWrapped)
         : fGL(gl)
         , fTexID(texID)
-        , fOwnsID(ownsID) {
+        , fIsWrapped(isWrapped) {
     }
 
     virtual ~GrGLTexID() {
-        if (0 != fTexID && fOwnsID) {
+        if (0 != fTexID && !fIsWrapped) {
             GR_GL_CALL(fGL, DeleteTextures(1, &fTexID));
         }
     }
@@ -37,7 +37,9 @@
 private:
     const GrGLInterface* fGL;
     GrGLuint             fTexID;
-    bool                 fOwnsID;
+    bool                 fIsWrapped;
+
+    typedef GrRefCnt INHERITED;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -46,11 +48,6 @@
 class GrGLTexture : public GrTexture {
 
 public:
-    enum Orientation {
-        kBottomUp_Orientation,
-        kTopDown_Orientation,
-    };
-
     struct TexParams {
         GrGLenum fFilter;
         GrGLenum fWrapS;
@@ -59,13 +56,10 @@
         void invalidate() { memset(this, 0xff, sizeof(TexParams)); }
     };
 
-    struct Desc {
-        int             fWidth;
-        int             fHeight;
-        GrPixelConfig   fConfig;
+    struct Desc : public GrTextureDesc {
         GrGLuint        fTextureID;
-        bool            fOwnsID;
-        Orientation     fOrientation;
+        bool            fIsWrapped;
+        GrSurfaceOrigin fOrigin;
     };
 
     // creates a texture that is also an RT
@@ -80,9 +74,11 @@
 
     virtual ~GrGLTexture() { this->release(); }
 
-    virtual intptr_t getTextureHandle() const;
+    virtual GrBackendObject getTextureHandle() const SK_OVERRIDE;
 
-    // these functions 
+    virtual void invalidateCachedState() SK_OVERRIDE { fTexParams.invalidate(); }
+
+    // these functions
     const TexParams& getCachedTexParams(GrGpu::ResetTimestamp* timestamp) const {
         *timestamp = fTexParamsTimestamp;
         return fTexParams;
@@ -94,29 +90,16 @@
     }
     GrGLuint textureID() const { return fTexIDObj->id(); }
 
-    // Ganesh assumes texture coordinates have their origin
-    // in the top-left corner of the image. OpenGL, however,
-    // has the origin in the lower-left corner. For content that
-    // is loaded by Ganesh we just push the content "upside down"
-    // (by GL's understanding of the world) in glTex*Image and the
-    // addressing just works out. However, content generated by GL
-    // (FBO or externally imported texture) will be updside down
-    // and it is up to the GrGpuGL derivative to handle y-mirroing.
-    Orientation orientation() const { return fOrientation; }
-
-    static const GrGLenum* WrapMode2GLWrap();
-
 protected:
 
     // overrides of GrTexture
-    virtual void onAbandon();
-    virtual void onRelease();
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
 
 private:
     TexParams                       fTexParams;
     GrGpu::ResetTimestamp           fTexParamsTimestamp;
     GrGLTexID*                      fTexIDObj;
-    Orientation                     fOrientation;
 
     void init(GrGpuGL* gpu,
               const Desc& textureDesc,
diff --git a/src/gpu/gl/GrGLUniformHandle.h b/src/gpu/gl/GrGLUniformHandle.h
new file mode 100644
index 0000000..231dd31
--- /dev/null
+++ b/src/gpu/gl/GrGLUniformHandle.h
@@ -0,0 +1,16 @@
+/*
+ * 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 GrUniformHandle_DEFINED
+#define GrUniformHandle_DEFINED
+
+namespace {
+inline int handle_to_index(GrGLUniformManager::UniformHandle h) { return ~h; }
+inline GrGLUniformManager::UniformHandle index_to_handle(int i) { return ~i; }
+}
+
+#endif
diff --git a/src/gpu/gl/GrGLUniformManager.cpp b/src/gpu/gl/GrGLUniformManager.cpp
new file mode 100644
index 0000000..da6726b
--- /dev/null
+++ b/src/gpu/gl/GrGLUniformManager.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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 "gl/GrGLShaderBuilder.h"
+#include "gl/GrGLProgram.h"
+#include "gl/GrGLUniformHandle.h"
+#include "SkMatrix.h"
+
+#define ASSERT_ARRAY_UPLOAD_IN_BOUNDS(UNI, OFFSET, COUNT) \
+         GrAssert(offset + arrayCount <= uni.fArrayCount || \
+                  (0 == offset && 1 == arrayCount && GrGLShaderVar::kNonArray == uni.fArrayCount))
+
+GrGLUniformManager::UniformHandle GrGLUniformManager::appendUniform(GrSLType type, int arrayCount) {
+    int idx = fUniforms.count();
+    Uniform& uni = fUniforms.push_back();
+    GrAssert(GrGLShaderVar::kNonArray == arrayCount || arrayCount > 0);
+    uni.fArrayCount = arrayCount;
+    uni.fType = type;
+    uni.fVSLocation = kUnusedUniform;
+    uni.fFSLocation = kUnusedUniform;
+    return index_to_handle(idx);
+}
+
+void GrGLUniformManager::setSampler(UniformHandle u, GrGLint texUnit) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kSampler2D_GrSLType);
+    GrAssert(GrGLShaderVar::kNonArray == uni.fArrayCount);
+    // 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.
+    // GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform1i(uni.fFSLocation, texUnit));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform1i(uni.fVSLocation, texUnit));
+    }
+}
+
+void GrGLUniformManager::set1f(UniformHandle u, GrGLfloat v0) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kFloat_GrSLType);
+    GrAssert(GrGLShaderVar::kNonArray == uni.fArrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform1f(uni.fFSLocation, v0));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform1f(uni.fVSLocation, v0));
+    }
+}
+
+void GrGLUniformManager::set1fv(UniformHandle u,
+                                int offset,
+                                int arrayCount,
+                                const GrGLfloat v[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kFloat_GrSLType);
+    GrAssert(arrayCount > 0);
+    ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, offset, arrayCount);
+    // 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.
+    //GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform1fv(uni.fFSLocation + offset, arrayCount, v));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform1fv(uni.fVSLocation + offset, arrayCount, v));
+    }
+}
+
+void GrGLUniformManager::set2f(UniformHandle u, GrGLfloat v0, GrGLfloat v1) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kVec2f_GrSLType);
+    GrAssert(GrGLShaderVar::kNonArray == uni.fArrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform2f(uni.fFSLocation, v0, v1));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform2f(uni.fVSLocation, v0, v1));
+    }
+}
+
+void GrGLUniformManager::set2fv(UniformHandle u,
+                                int offset,
+                                int arrayCount,
+                                const GrGLfloat v[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kVec2f_GrSLType);
+    GrAssert(arrayCount > 0);
+    ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, offset, arrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform2fv(uni.fFSLocation + offset, arrayCount, v));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform2fv(uni.fVSLocation + offset, arrayCount, v));
+    }
+}
+
+void GrGLUniformManager::set3f(UniformHandle u, GrGLfloat v0, GrGLfloat v1, GrGLfloat v2) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kVec3f_GrSLType);
+    GrAssert(GrGLShaderVar::kNonArray == uni.fArrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform3f(uni.fFSLocation, v0, v1, v2));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform3f(uni.fVSLocation, v0, v1, v2));
+    }
+}
+
+void GrGLUniformManager::set3fv(UniformHandle u,
+                                int offset,
+                                int arrayCount,
+                                const GrGLfloat v[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kVec3f_GrSLType);
+    GrAssert(arrayCount > 0);
+    ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, offset, arrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform3fv(uni.fFSLocation + offset, arrayCount, v));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform3fv(uni.fVSLocation + offset, arrayCount, v));
+    }
+}
+
+void GrGLUniformManager::set4f(UniformHandle u,
+                               GrGLfloat v0,
+                               GrGLfloat v1,
+                               GrGLfloat v2,
+                               GrGLfloat v3) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kVec4f_GrSLType);
+    GrAssert(GrGLShaderVar::kNonArray == uni.fArrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform4f(uni.fFSLocation, v0, v1, v2, v3));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform4f(uni.fVSLocation, v0, v1, v2, v3));
+    }
+}
+
+void GrGLUniformManager::set4fv(UniformHandle u,
+                                int offset,
+                                int arrayCount,
+                                const GrGLfloat v[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kVec4f_GrSLType);
+    GrAssert(arrayCount > 0);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform4fv(uni.fFSLocation + offset, arrayCount, v));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), Uniform4fv(uni.fVSLocation + offset, arrayCount, v));
+    }
+}
+
+void GrGLUniformManager::setMatrix3f(UniformHandle u, const GrGLfloat matrix[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kMat33f_GrSLType);
+    GrAssert(GrGLShaderVar::kNonArray == uni.fArrayCount);
+    // TODO: Re-enable this assert once texture matrices aren't forced on all effects
+    // GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), UniformMatrix3fv(uni.fFSLocation, 1, false, matrix));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), UniformMatrix3fv(uni.fVSLocation, 1, false, matrix));
+    }
+}
+
+void GrGLUniformManager::setMatrix4f(UniformHandle u, const GrGLfloat matrix[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kMat44f_GrSLType);
+    GrAssert(GrGLShaderVar::kNonArray == uni.fArrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), UniformMatrix4fv(uni.fFSLocation, 1, false, matrix));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(), UniformMatrix4fv(uni.fVSLocation, 1, false, matrix));
+    }
+}
+
+void GrGLUniformManager::setMatrix3fv(UniformHandle u,
+                                      int offset,
+                                      int arrayCount,
+                                      const GrGLfloat matrices[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kMat33f_GrSLType);
+    GrAssert(arrayCount > 0);
+    ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, offset, arrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(),
+                   UniformMatrix3fv(uni.fFSLocation + offset, arrayCount, false, matrices));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(),
+                   UniformMatrix3fv(uni.fVSLocation + offset, arrayCount, false, matrices));
+    }
+}
+
+void GrGLUniformManager::setMatrix4fv(UniformHandle u,
+                                      int offset,
+                                      int arrayCount,
+                                      const GrGLfloat matrices[]) const {
+    const Uniform& uni = fUniforms[handle_to_index(u)];
+    GrAssert(uni.fType == kMat44f_GrSLType);
+    GrAssert(arrayCount > 0);
+    ASSERT_ARRAY_UPLOAD_IN_BOUNDS(uni, offset, arrayCount);
+    GrAssert(kUnusedUniform != uni.fFSLocation || kUnusedUniform != uni.fVSLocation);
+    if (kUnusedUniform != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(),
+                   UniformMatrix4fv(uni.fFSLocation + offset, arrayCount, false, matrices));
+    }
+    if (kUnusedUniform != uni.fVSLocation && uni.fVSLocation != uni.fFSLocation) {
+        GR_GL_CALL(fContext.interface(),
+                   UniformMatrix4fv(uni.fVSLocation + offset, arrayCount, false, matrices));
+    }
+}
+
+void GrGLUniformManager::setSkMatrix(UniformHandle u, const SkMatrix& matrix) const {
+//    GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
+    GrGLfloat mt[] = {
+        matrix.get(SkMatrix::kMScaleX),
+        matrix.get(SkMatrix::kMSkewY),
+        matrix.get(SkMatrix::kMPersp0),
+        matrix.get(SkMatrix::kMSkewX),
+        matrix.get(SkMatrix::kMScaleY),
+        matrix.get(SkMatrix::kMPersp1),
+        matrix.get(SkMatrix::kMTransX),
+        matrix.get(SkMatrix::kMTransY),
+        matrix.get(SkMatrix::kMPersp2),
+    };
+    this->setMatrix3f(u, mt);
+}
+
+
+void GrGLUniformManager::getUniformLocations(GrGLuint programID, const BuilderUniformArray& uniforms) {
+    GrAssert(uniforms.count() == fUniforms.count());
+    int count = fUniforms.count();
+    for (int i = 0; i < count; ++i) {
+        GrAssert(uniforms[i].fVariable.getType() == fUniforms[i].fType);
+        GrAssert(uniforms[i].fVariable.getArrayCount() == fUniforms[i].fArrayCount);
+        GrGLint location;
+        // TODO: Move the Xoom uniform array in both FS and VS bug workaround here.
+        GR_GL_CALL_RET(fContext.interface(), location,
+                       GetUniformLocation(programID, uniforms[i].fVariable.c_str()));
+        if (GrGLShaderBuilder::kVertex_ShaderType & uniforms[i].fVisibility) {
+            fUniforms[i].fVSLocation = location;
+        }
+        if (GrGLShaderBuilder::kFragment_ShaderType & uniforms[i].fVisibility) {
+            fUniforms[i].fFSLocation = location;
+        }
+    }
+}
diff --git a/src/gpu/gl/GrGLUniformManager.h b/src/gpu/gl/GrGLUniformManager.h
new file mode 100644
index 0000000..8f435d3
--- /dev/null
+++ b/src/gpu/gl/GrGLUniformManager.h
@@ -0,0 +1,84 @@
+/*
+ * 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 GrGLUniformManager_DEFINED
+#define GrGLUniformManager_DEFINED
+
+#include "gl/GrGLShaderVar.h"
+#include "gl/GrGLSL.h"
+#include "GrAllocator.h"
+
+#include "SkTArray.h"
+
+class GrGLContextInfo;
+class SkMatrix;
+
+/** Manages a program's uniforms.
+*/
+class GrGLUniformManager {
+public:
+    // Opaque handle to a uniform
+    typedef int UniformHandle;
+    static const UniformHandle kInvalidUniformHandle = 0;
+
+    GrGLUniformManager(const GrGLContextInfo& context) : fContext(context) {}
+
+    UniformHandle appendUniform(GrSLType type, int arrayCount = GrGLShaderVar::kNonArray);
+
+    /** Functions for uploading uniform values. The varities ending in v can be used to upload to an
+     *  array of uniforms. offset + arrayCount must be <= the array count of the uniform.
+     */
+    void setSampler(UniformHandle, GrGLint texUnit) const;
+    void set1f(UniformHandle, GrGLfloat v0) const;
+    void set1fv(UniformHandle, int offset, int arrayCount, const GrGLfloat v[]) const;
+    void set2f(UniformHandle, GrGLfloat, GrGLfloat) const;
+    void set2fv(UniformHandle, int offset, int arrayCount, const GrGLfloat v[]) const;
+    void set3f(UniformHandle, GrGLfloat, GrGLfloat, GrGLfloat) const;
+    void set3fv(UniformHandle, int offset, int arrayCount, const GrGLfloat v[]) const;
+    void set4f(UniformHandle, GrGLfloat, GrGLfloat, GrGLfloat, GrGLfloat) const;
+    void set4fv(UniformHandle, int offset, int arrayCount, const GrGLfloat v[]) const;
+    // matrices are column-major, the first three upload a single matrix, the latter three upload
+    // arrayCount matrices into a uniform array.
+    void setMatrix3f(UniformHandle, const GrGLfloat matrix[]) const;
+    void setMatrix4f(UniformHandle, const GrGLfloat matrix[]) const;
+    void setMatrix3fv(UniformHandle, int offset, int arrayCount, const GrGLfloat matrices[]) const;
+    void setMatrix4fv(UniformHandle, int offset, int arrayCount, const GrGLfloat matrices[]) const;
+
+    // convenience method for uploading a SkMatrix to a 3x3 matrix uniform
+    void setSkMatrix(UniformHandle, const SkMatrix&) const;
+
+    struct BuilderUniform {
+        GrGLShaderVar fVariable;
+        uint32_t      fVisibility;
+    };
+    // This uses an allocator rather than array so that the GrGLShaderVars don't move in memory
+    // after they are inserted. Users of GrGLShaderBuilder get refs to the vars and ptrs to their
+    // name strings. Otherwise, we'd have to hand out copies.
+    typedef GrTAllocator<BuilderUniform> BuilderUniformArray;
+
+    /**
+     * Called by the GrGLShaderBuilder to get GL locations for all uniforms.
+     */
+    void getUniformLocations(GrGLuint programID, const BuilderUniformArray& uniforms);
+
+private:
+    enum {
+        kUnusedUniform = -1,
+    };
+
+    struct Uniform {
+        GrGLint     fVSLocation;
+        GrGLint     fFSLocation;
+        GrSLType    fType;
+        int         fArrayCount;
+    };
+
+    SkTArray<Uniform, true> fUniforms;
+    const GrGLContextInfo&  fContext;
+};
+
+#endif
diff --git a/src/gpu/gl/GrGLUtil.cpp b/src/gpu/gl/GrGLUtil.cpp
index 23ed5b4..3f110cc 100644
--- a/src/gpu/gl/GrGLUtil.cpp
+++ b/src/gpu/gl/GrGLUtil.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -7,19 +6,39 @@
  */
 
 
-#include "gl/GrGLConfig.h"
-#include "gl/GrGLInterface.h"
+#include "GrGLUtil.h"
+
 
 void GrGLClearErr(const GrGLInterface* gl) {
     while (GR_GL_NO_ERROR != gl->fGetError()) {}
 }
 
+namespace {
+const char *get_error_string(uint32_t err) {
+    switch (err) {
+    case GR_GL_NO_ERROR:
+        return "";
+    case GR_GL_INVALID_ENUM:
+        return "Invalid Enum";
+    case GR_GL_INVALID_VALUE:
+        return "Invalid Value";
+    case GR_GL_INVALID_OPERATION:
+        return "Invalid Operation";
+    case GR_GL_OUT_OF_MEMORY:
+        return "Out of Memory";
+    case GR_GL_CONTEXT_LOST:
+        return "Context Lost";
+    }
+    return "Unknown";
+}
+}
+
 void GrGLCheckErr(const GrGLInterface* gl,
                   const char* location,
                   const char* call) {
     uint32_t err = GR_GL_GET_ERROR(gl);
     if (GR_GL_NO_ERROR != err) {
-        GrPrintf("---- glGetError %x", err);
+        GrPrintf("---- glGetError 0x%x(%s)", err, get_error_string(err));
         if (NULL != location) {
             GrPrintf(" at\n\t%s", location);
         }
@@ -40,3 +59,149 @@
     bool gCheckErrorGL = !!(GR_GL_CHECK_ERROR_START);
 #endif
 
+///////////////////////////////////////////////////////////////////////////////
+
+GrGLBinding GrGLGetBindingInUseFromString(const char* versionString) {
+    if (NULL == versionString) {
+        GrAssert(!"NULL GL version string.");
+        return kNone_GrGLBinding;
+    }
+
+    int major, minor;
+
+    // check for desktop
+    int n = sscanf(versionString, "%d.%d", &major, &minor);
+    if (2 == n) {
+        return kDesktop_GrGLBinding;
+    }
+
+    // check for ES 1
+    char profile[2];
+    n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
+               &major, &minor);
+    if (4 == n) {
+        // we no longer support ES1.
+        return kNone_GrGLBinding;
+    }
+
+    // check for ES2
+    n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
+    if (2 == n) {
+        return kES2_GrGLBinding;
+    }
+    return kNone_GrGLBinding;
+}
+
+GrGLVersion GrGLGetVersionFromString(const char* versionString) {
+    if (NULL == versionString) {
+        GrAssert(!"NULL GL version string.");
+        return 0;
+    }
+
+    int major, minor;
+
+    int n = sscanf(versionString, "%d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GL_VER(major, minor);
+    }
+
+    char profile[2];
+    n = sscanf(versionString, "OpenGL ES-%c%c %d.%d", profile, profile+1,
+               &major, &minor);
+    if (4 == n) {
+        return GR_GL_VER(major, minor);
+    }
+
+    n = sscanf(versionString, "OpenGL ES %d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GL_VER(major, minor);
+    }
+
+    return 0;
+}
+
+GrGLSLVersion GrGLGetGLSLVersionFromString(const char* versionString) {
+    if (NULL == versionString) {
+        GrAssert(!"NULL GLSL version string.");
+        return 0;
+    }
+
+    int major, minor;
+
+    int n = sscanf(versionString, "%d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GLSL_VER(major, minor);
+    }
+
+    n = sscanf(versionString, "OpenGL ES GLSL ES %d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GLSL_VER(major, minor);
+    }
+
+#ifdef SK_BUILD_FOR_ANDROID
+    // android hack until the gpu vender updates their drivers
+    n = sscanf(versionString, "OpenGL ES GLSL %d.%d", &major, &minor);
+    if (2 == n) {
+        return GR_GLSL_VER(major, minor);
+    }
+#endif
+
+    return 0;
+}
+
+bool GrGLHasExtensionFromString(const char* ext, const char* extensionString) {
+    int extLength = strlen(ext);
+
+    while (true) {
+        int n = strcspn(extensionString, " ");
+        if (n == extLength && 0 == strncmp(ext, extensionString, n)) {
+            return true;
+        }
+        if (0 == extensionString[n]) {
+            return false;
+        }
+        extensionString += n+1;
+    }
+
+    return false;
+}
+
+GrGLVendor GrGLGetVendorFromString(const char* vendorString) {
+    if (NULL != vendorString) {
+        if (0 == strcmp(vendorString, "Intel")) {
+            return kIntel_GrGLVendor;
+        }
+    }
+
+    return kOther_GrGLVendor;
+}
+
+bool GrGLHasExtension(const GrGLInterface* gl, const char* ext) {
+    const GrGLubyte* glstr;
+    GR_GL_CALL_RET(gl, glstr, GetString(GR_GL_EXTENSIONS));
+    return GrGLHasExtensionFromString(ext, (const char*) glstr);
+}
+
+GrGLBinding GrGLGetBindingInUse(const GrGLInterface* gl) {
+    const GrGLubyte* v;
+    GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
+    return GrGLGetBindingInUseFromString((const char*) v);
+}
+
+GrGLVersion GrGLGetVersion(const GrGLInterface* gl) {
+    const GrGLubyte* v;
+    GR_GL_CALL_RET(gl, v, GetString(GR_GL_VERSION));
+    return GrGLGetVersionFromString((const char*) v);
+}
+
+GrGLSLVersion GrGLGetGLSLVersion(const GrGLInterface* gl) {
+    const GrGLubyte* v;
+    GR_GL_CALL_RET(gl, v, GetString(GR_GL_SHADING_LANGUAGE_VERSION));
+    return GrGLGetGLSLVersionFromString((const char*) v);
+}
+
+GrGLVendor GrGLGetVendor(const GrGLInterface* gl) {
+    const GrGLubyte* v;
+    GR_GL_CALL_RET(gl, v, GetString(GR_GL_VENDOR));
+    return GrGLGetVendorFromString((const char*) v);
+}
diff --git a/src/gpu/gl/GrGLUtil.h b/src/gpu/gl/GrGLUtil.h
new file mode 100644
index 0000000..997207a
--- /dev/null
+++ b/src/gpu/gl/GrGLUtil.h
@@ -0,0 +1,160 @@
+/*
+ * 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 GrGLUtil_DEFINED
+#define GrGLUtil_DEFINED
+
+#include "gl/GrGLInterface.h"
+#include "GrGLDefines.h"
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef uint32_t GrGLVersion;
+typedef uint32_t GrGLSLVersion;
+
+/**
+ * This list is lazily updated as required.
+ */
+enum GrGLVendor {
+    kIntel_GrGLVendor,
+    kOther_GrGLVendor,
+};
+
+#define GR_GL_VER(major, minor) ((static_cast<int>(major) << 16) | \
+                                 static_cast<int>(minor))
+#define GR_GLSL_VER(major, minor) ((static_cast<int>(major) << 16) | \
+                                   static_cast<int>(minor))
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ *  Some drivers want the var-int arg to be zero-initialized on input.
+ */
+#define GR_GL_INIT_ZERO     0
+#define GR_GL_GetIntegerv(gl, e, p)                                            \
+    do {                                                                       \
+        *(p) = GR_GL_INIT_ZERO;                                                \
+        GR_GL_CALL(gl, GetIntegerv(e, p));                                     \
+    } while (0)
+
+#define GR_GL_GetFramebufferAttachmentParameteriv(gl, t, a, pname, p)          \
+    do {                                                                       \
+        *(p) = GR_GL_INIT_ZERO;                                                \
+        GR_GL_CALL(gl, GetFramebufferAttachmentParameteriv(t, a, pname, p));   \
+    } while (0)
+
+#define GR_GL_GetRenderbufferParameteriv(gl, t, pname, p)                      \
+    do {                                                                       \
+        *(p) = GR_GL_INIT_ZERO;                                                \
+        GR_GL_CALL(gl, GetRenderbufferParameteriv(t, pname, p));               \
+    } while (0)
+#define GR_GL_GetTexLevelParameteriv(gl, t, l, pname, p)                       \
+    do {                                                                       \
+        *(p) = GR_GL_INIT_ZERO;                                                \
+        GR_GL_CALL(gl, GetTexLevelParameteriv(t, l, pname, p));                \
+    } while (0)
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Helpers for glGetString()
+ */
+
+// these variants assume caller already has a string from glGetString()
+GrGLVersion GrGLGetVersionFromString(const char* versionString);
+GrGLBinding GrGLGetBindingInUseFromString(const char* versionString);
+GrGLSLVersion GrGLGetGLSLVersionFromString(const char* versionString);
+bool GrGLHasExtensionFromString(const char* ext, const char* extensionString);
+GrGLVendor GrGLGetVendorFromString(const char* vendorString);
+
+// these variants call glGetString()
+bool GrGLHasExtension(const GrGLInterface*, const char* ext);
+GrGLBinding GrGLGetBindingInUse(const GrGLInterface*);
+GrGLVersion GrGLGetVersion(const GrGLInterface*);
+GrGLSLVersion GrGLGetGLSLVersion(const GrGLInterface*);
+GrGLVendor GrGLGetVendor(const GrGLInterface*);
+
+/**
+ * Helpers for glGetError()
+ */
+
+void GrGLCheckErr(const GrGLInterface* gl,
+                  const char* location,
+                  const char* call);
+
+void GrGLClearErr(const GrGLInterface* gl);
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Macros for using GrGLInterface to make GL calls
+ */
+
+// internal macro to conditionally call glGetError based on compile-time and
+// run-time flags.
+#if GR_GL_CHECK_ERROR
+    extern bool gCheckErrorGL;
+    #define GR_GL_CHECK_ERROR_IMPL(IFACE, X)                    \
+        if (gCheckErrorGL)                                      \
+            GrGLCheckErr(IFACE, GR_FILE_AND_LINE_STR, #X)
+#else
+    #define GR_GL_CHECK_ERROR_IMPL(IFACE, X)
+#endif
+
+// internal macro to conditionally log the gl call using GrPrintf based on
+// compile-time and run-time flags.
+#if GR_GL_LOG_CALLS
+    extern bool gLogCallsGL;
+    #define GR_GL_LOG_CALLS_IMPL(X)                             \
+        if (gLogCallsGL)                                        \
+            GrPrintf(GR_FILE_AND_LINE_STR "GL: " #X "\n")
+#else
+    #define GR_GL_LOG_CALLS_IMPL(X)
+#endif
+
+// internal macro that does the per-GL-call callback (if necessary)
+#if GR_GL_PER_GL_FUNC_CALLBACK
+    #define GR_GL_CALLBACK_IMPL(IFACE) (IFACE)->fCallback(IFACE)
+#else
+    #define GR_GL_CALLBACK_IMPL(IFACE)
+#endif
+
+// makes a GL call on the interface and does any error checking and logging
+#define GR_GL_CALL(IFACE, X)                                    \
+    do {                                                        \
+        GR_GL_CALL_NOERRCHECK(IFACE, X);                        \
+        GR_GL_CHECK_ERROR_IMPL(IFACE, X);                       \
+    } while (false)
+
+// Variant of above that always skips the error check. This is useful when
+// the caller wants to do its own glGetError() call and examine the error value.
+#define GR_GL_CALL_NOERRCHECK(IFACE, X)                         \
+    do {                                                        \
+        GR_GL_CALLBACK_IMPL(IFACE);                             \
+        (IFACE)->f##X;                                          \
+        GR_GL_LOG_CALLS_IMPL(X);                                \
+    } while (false)
+
+// same as GR_GL_CALL but stores the return value of the gl call in RET
+#define GR_GL_CALL_RET(IFACE, RET, X)                           \
+    do {                                                        \
+        GR_GL_CALL_RET_NOERRCHECK(IFACE, RET, X);               \
+        GR_GL_CHECK_ERROR_IMPL(IFACE, X);                       \
+    } while (false)
+
+// same as GR_GL_CALL_RET but always skips the error check.
+#define GR_GL_CALL_RET_NOERRCHECK(IFACE, RET, X)                \
+    do {                                                        \
+        GR_GL_CALLBACK_IMPL(IFACE);                             \
+        (RET) = (IFACE)->f##X;                                  \
+        GR_GL_LOG_CALLS_IMPL(X);                                \
+    } while (false)
+
+// call glGetError without doing a redundant error check or logging.
+#define GR_GL_GET_ERROR(IFACE) (IFACE)->fGetError()
+
+#endif
diff --git a/src/gpu/gl/GrGLVertexBuffer.cpp b/src/gpu/gl/GrGLVertexBuffer.cpp
index 48479dc..1512fef 100644
--- a/src/gpu/gl/GrGLVertexBuffer.cpp
+++ b/src/gpu/gl/GrGLVertexBuffer.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -16,26 +15,31 @@
 #define GL_CALL(X) GR_GL_CALL(GPUGL->glInterface(), X)
 
 GrGLVertexBuffer::GrGLVertexBuffer(GrGpuGL* gpu,
+                                   bool isWrapped,
                                    GrGLuint id,
                                    size_t sizeInBytes,
                                    bool dynamic)
-    : INHERITED(gpu, sizeInBytes, dynamic)
+    : INHERITED(gpu, isWrapped, sizeInBytes, dynamic)
     , fBufferID(id)
     , fLockPtr(NULL) {
 }
 
 void GrGLVertexBuffer::onRelease() {
     // make sure we've not been abandoned
-    if (fBufferID) {
+    if (fBufferID && !this->isWrapped()) {
         GPUGL->notifyVertexBufferDelete(this);
         GL_CALL(DeleteBuffers(1, &fBufferID));
         fBufferID = 0;
     }
+
+    INHERITED::onRelease();
 }
 
 void GrGLVertexBuffer::onAbandon() {
     fBufferID = 0;
     fLockPtr = NULL;
+
+    INHERITED::onAbandon();
 }
 
 void GrGLVertexBuffer::bind() const {
@@ -50,7 +54,7 @@
 void* GrGLVertexBuffer::lock() {
     GrAssert(fBufferID);
     GrAssert(!isLocked());
-    if (this->getGpu()->getCaps().fBufferLockSupport) {
+    if (this->getGpu()->getCaps().bufferLockSupport()) {
         this->bind();
         // Let driver know it can discard the old data
         GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, this->sizeInBytes(), NULL,
@@ -72,7 +76,7 @@
 
     GrAssert(fBufferID);
     GrAssert(isLocked());
-    GrAssert(this->getGpu()->getCaps().fBufferLockSupport);
+    GrAssert(this->getGpu()->getCaps().bufferLockSupport());
 
     this->bind();
     GL_CALL(UnmapBuffer(GR_GL_ARRAY_BUFFER));
@@ -81,11 +85,12 @@
 
 bool GrGLVertexBuffer::isLocked() const {
     GrAssert(!this->isValid() || fBufferID);
-#if GR_DEBUG
+    // this check causes a lot of noise in the gl log
+#if 0
     if (this->isValid() && this->getGpu()->getCaps().fBufferLockSupport) {
         GrGLint mapped;
         this->bind();
-        GL_CALL(GetBufferParameteriv(GR_GL_ARRAY_BUFFER, 
+        GL_CALL(GetBufferParameteriv(GR_GL_ARRAY_BUFFER,
                                      GR_GL_BUFFER_MAPPED, &mapped));
         GrAssert(!!mapped == !!fLockPtr);
     }
@@ -112,7 +117,7 @@
         // draws that reference the old contents. With this hint it can
         // assign a different allocation for the new contents to avoid
         // flushing the gpu past draws consuming the old contents.
-        GL_CALL(BufferData(GR_GL_ARRAY_BUFFER, 
+        GL_CALL(BufferData(GR_GL_ARRAY_BUFFER,
                            this->sizeInBytes(), NULL, usage));
         GL_CALL(BufferSubData(GR_GL_ARRAY_BUFFER, 0, srcSizeInBytes, src));
     }
@@ -142,4 +147,3 @@
 #endif
     return true;
 }
-
diff --git a/src/gpu/gl/GrGLVertexBuffer.h b/src/gpu/gl/GrGLVertexBuffer.h
index 5d2ba30..17b0283 100644
--- a/src/gpu/gl/GrGLVertexBuffer.h
+++ b/src/gpu/gl/GrGLVertexBuffer.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -11,7 +10,7 @@
 #ifndef GrGLVertexBuffer_DEFINED
 #define GrGLVertexBuffer_DEFINED
 
-#include "../GrVertexBuffer.h"
+#include "GrVertexBuffer.h"
 #include "gl/GrGLInterface.h"
 
 class GrGpuGL;
@@ -30,13 +29,14 @@
 
 protected:
     GrGLVertexBuffer(GrGpuGL* gpu,
+                     bool isWrapped,
                      GrGLuint id,
                      size_t sizeInBytes,
                      bool dynamic);
 
     // overrides of GrResource
-    virtual void onAbandon();
-    virtual void onRelease();
+    virtual void onAbandon() SK_OVERRIDE;
+    virtual void onRelease() SK_OVERRIDE;
 
 private:
     void bind() const;
diff --git a/src/gpu/gl/GrGpuGL.cpp b/src/gpu/gl/GrGpuGL.cpp
index 69880e5..7ca07a0 100644
--- a/src/gpu/gl/GrGpuGL.cpp
+++ b/src/gpu/gl/GrGpuGL.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -9,10 +8,13 @@
 
 #include "GrGpuGL.h"
 #include "GrGLStencilBuffer.h"
+#include "GrGLPath.h"
+#include "GrGLShaderBuilder.h"
+#include "GrTemplates.h"
 #include "GrTypes.h"
 #include "SkTemplates.h"
 
-static const GrGLuint GR_MAX_GLUINT = ~0;
+static const GrGLuint GR_MAX_GLUINT = ~0U;
 static const GrGLint  GR_INVAL_GLINT = ~0;
 
 #define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
@@ -28,7 +30,7 @@
     #define CLEAR_ERROR_BEFORE_ALLOC(iface)   GrGLClearErr(iface)
     #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL_NOERRCHECK(iface, call)
     #define CHECK_ALLOC_ERROR(iface)          GR_GL_GET_ERROR(iface)
-#else 
+#else
     #define CLEAR_ERROR_BEFORE_ALLOC(iface)
     #define GL_ALLOC_CALL(iface, call)        GR_GL_CALL(iface, call)
     #define CHECK_ALLOC_ERROR(iface)          GR_GL_NO_ERROR
@@ -84,64 +86,32 @@
         false,
     };
     return gCoeffReferencesBlendConst[coeff];
-    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
+    GR_STATIC_ASSERT(kTotalGrBlendCoeffCount ==
+                     GR_ARRAY_COUNT(gCoeffReferencesBlendConst));
 
-    GR_STATIC_ASSERT(0 == kZero_BlendCoeff);
-    GR_STATIC_ASSERT(1 == kOne_BlendCoeff);
-    GR_STATIC_ASSERT(2 == kSC_BlendCoeff);
-    GR_STATIC_ASSERT(3 == kISC_BlendCoeff);
-    GR_STATIC_ASSERT(4 == kDC_BlendCoeff);
-    GR_STATIC_ASSERT(5 == kIDC_BlendCoeff);
-    GR_STATIC_ASSERT(6 == kSA_BlendCoeff);
-    GR_STATIC_ASSERT(7 == kISA_BlendCoeff);
-    GR_STATIC_ASSERT(8 == kDA_BlendCoeff);
-    GR_STATIC_ASSERT(9 == kIDA_BlendCoeff);
-    GR_STATIC_ASSERT(10 == kConstC_BlendCoeff);
-    GR_STATIC_ASSERT(11 == kIConstC_BlendCoeff);
-    GR_STATIC_ASSERT(12 == kConstA_BlendCoeff);
-    GR_STATIC_ASSERT(13 == kIConstA_BlendCoeff);
+    GR_STATIC_ASSERT(0 == kZero_GrBlendCoeff);
+    GR_STATIC_ASSERT(1 == kOne_GrBlendCoeff);
+    GR_STATIC_ASSERT(2 == kSC_GrBlendCoeff);
+    GR_STATIC_ASSERT(3 == kISC_GrBlendCoeff);
+    GR_STATIC_ASSERT(4 == kDC_GrBlendCoeff);
+    GR_STATIC_ASSERT(5 == kIDC_GrBlendCoeff);
+    GR_STATIC_ASSERT(6 == kSA_GrBlendCoeff);
+    GR_STATIC_ASSERT(7 == kISA_GrBlendCoeff);
+    GR_STATIC_ASSERT(8 == kDA_GrBlendCoeff);
+    GR_STATIC_ASSERT(9 == kIDA_GrBlendCoeff);
+    GR_STATIC_ASSERT(10 == kConstC_GrBlendCoeff);
+    GR_STATIC_ASSERT(11 == kIConstC_GrBlendCoeff);
+    GR_STATIC_ASSERT(12 == kConstA_GrBlendCoeff);
+    GR_STATIC_ASSERT(13 == kIConstA_GrBlendCoeff);
 
-    GR_STATIC_ASSERT(14 == kS2C_BlendCoeff);
-    GR_STATIC_ASSERT(15 == kIS2C_BlendCoeff);
-    GR_STATIC_ASSERT(16 == kS2A_BlendCoeff);
-    GR_STATIC_ASSERT(17 == kIS2A_BlendCoeff);
+    GR_STATIC_ASSERT(14 == kS2C_GrBlendCoeff);
+    GR_STATIC_ASSERT(15 == kIS2C_GrBlendCoeff);
+    GR_STATIC_ASSERT(16 == kS2A_GrBlendCoeff);
+    GR_STATIC_ASSERT(17 == kIS2A_GrBlendCoeff);
 
     // assertion for gXfermodeCoeff2Blend have to be in GrGpu scope
-    GR_STATIC_ASSERT(kTotalBlendCoeffCount == GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void GrGpuGL::AdjustTextureMatrix(const GrGLTexture* texture,
-                                  GrSamplerState::SampleMode mode,
-                                  GrMatrix* matrix) {
-    GrAssert(NULL != texture);
-    GrAssert(NULL != matrix);
-    GrGLTexture::Orientation orientation = texture->orientation();
-    if (GrGLTexture::kBottomUp_Orientation == orientation) {
-        GrMatrix invY;
-        invY.setAll(GR_Scalar1, 0,           0,
-                    0,          -GR_Scalar1, GR_Scalar1,
-                    0,          0,           GrMatrix::I()[8]);
-        matrix->postConcat(invY);
-    } else {
-        GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
-    }
-}
-
-bool GrGpuGL::TextureMatrixIsIdentity(const GrGLTexture* texture,
-                                      const GrSamplerState& sampler) {
-    GrAssert(NULL != texture);
-    if (!sampler.getMatrix().isIdentity()) {
-        return false;
-    }
-    GrGLTexture::Orientation orientation = texture->orientation();
-    if (GrGLTexture::kBottomUp_Orientation == orientation) {
-        return false;
-    } else {
-        GrAssert(GrGLTexture::kTopDown_Orientation == orientation);
-    }
-    return true;
+    GR_STATIC_ASSERT(kTotalGrBlendCoeffCount ==
+                     GR_ARRAY_COUNT(gXfermodeCoeff2Blend));
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -181,6 +151,8 @@
 
     GrAssert(ctxInfo.isInitialized());
 
+    fillInConfigRenderableTable();
+
     fPrintedCaps = false;
 
     GrGLClearErr(fGLContextInfo.interface());
@@ -202,15 +174,26 @@
         GrPrintf("------ EXTENSIONS\n %s \n", ext);
     }
 
-    this->resetDirtyFlags();
-
     this->initCaps();
 
+    fProgramCache = SkNEW_ARGS(ProgramCache, (this->glContextInfo()));
+
     fLastSuccessfulStencilFmtIdx = 0;
-    fCanPreserveUnpremulRoundtrip = kUnknown_CanPreserveUnpremulRoundtrip;
+    if (false) { // avoid bit rot, suppress warning
+        fbo_test(this->glInterface(), 0, 0);
+    }
 }
 
 GrGpuGL::~GrGpuGL() {
+    if (0 != fHWProgramID) {
+        // detach the current program so there is no confusion on OpenGL's part
+        // that we want it to be deleted
+        GrAssert(fHWProgramID == fCurrentProgram->fProgramID);
+        GL_CALL(UseProgram(0));
+    }
+
+    delete fProgramCache;
+
     // This must be called by before the GrDrawTarget destructor
     this->releaseGeometry();
     // This subclass must do this before the base class destructor runs
@@ -228,10 +211,8 @@
     const GrGLInterface* gl = this->glInterface();
     GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits);
     GrAssert(maxTextureUnits > GrDrawState::kNumStages);
-    if (kES2_GrGLBinding != this->glBinding()) {
-        GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_UNITS, &maxTextureUnits);
-        GrAssert(maxTextureUnits > GrDrawState::kNumStages);
-    }
+
+    CapsInternals* caps = this->capsInternals();
 
     GrGLint numFormats;
     GR_GL_GetIntegerv(gl, GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numFormats);
@@ -239,7 +220,7 @@
     GR_GL_GetIntegerv(gl, GR_GL_COMPRESSED_TEXTURE_FORMATS, formats);
     for (int i = 0; i < numFormats; ++i) {
         if (formats[i] == GR_GL_PALETTE8_RGBA8) {
-            fCaps.f8BitPaletteSupport = true;
+            caps->f8BitPaletteSupport = true;
             break;
         }
     }
@@ -248,127 +229,119 @@
         // we could also look for GL_ATI_separate_stencil extension or
         // GL_EXT_stencil_two_side but they use different function signatures
         // than GL2.0+ (and than each other).
-        fCaps.fTwoSidedStencilSupport = (this->glVersion() >= GR_GL_VER(2,0));
+        caps->fTwoSidedStencilSupport = (this->glVersion() >= GR_GL_VER(2,0));
         // supported on GL 1.4 and higher or by extension
-        fCaps.fStencilWrapOpsSupport = (this->glVersion() >= GR_GL_VER(1,4)) ||
+        caps->fStencilWrapOpsSupport = (this->glVersion() >= GR_GL_VER(1,4)) ||
                                        this->hasExtension("GL_EXT_stencil_wrap");
     } else {
         // ES 2 has two sided stencil and stencil wrap
-        fCaps.fTwoSidedStencilSupport = true;
-        fCaps.fStencilWrapOpsSupport = true;
+        caps->fTwoSidedStencilSupport = true;
+        caps->fStencilWrapOpsSupport = true;
     }
 
     if (kDesktop_GrGLBinding == this->glBinding()) {
-        fCaps.fBufferLockSupport = true; // we require VBO support and the desktop VBO
+        caps->fBufferLockSupport = true; // we require VBO support and the desktop VBO
                                          // extension includes glMapBuffer.
     } else {
-        fCaps.fBufferLockSupport = this->hasExtension("GL_OES_mapbuffer");
+        caps->fBufferLockSupport = this->hasExtension("GL_OES_mapbuffer");
     }
 
     if (kDesktop_GrGLBinding == this->glBinding()) {
-        if (this->glVersion() >= GR_GL_VER(2,0) || 
+        if (this->glVersion() >= GR_GL_VER(2,0) ||
             this->hasExtension("GL_ARB_texture_non_power_of_two")) {
-            fCaps.fNPOTTextureTileSupport = true;
+            caps->fNPOTTextureTileSupport = true;
         } else {
-            fCaps.fNPOTTextureTileSupport = false;
+            caps->fNPOTTextureTileSupport = false;
         }
     } else {
         // Unextended ES2 supports NPOT textures with clamp_to_edge and non-mip filters only
-        fCaps.fNPOTTextureTileSupport = this->hasExtension("GL_OES_texture_npot");
+        caps->fNPOTTextureTileSupport = this->hasExtension("GL_OES_texture_npot");
     }
 
-    fCaps.fHWAALineSupport = (kDesktop_GrGLBinding == this->glBinding());
+    caps->fHWAALineSupport = (kDesktop_GrGLBinding == this->glBinding());
 
-    ////////////////////////////////////////////////////////////////////////////
-    // Experiments to determine limitations that can't be queried.
-    // TODO: Make these a preprocess that generate some compile time constants.
-    // TODO: probe once at startup, rather than once per context creation.
-
-    GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_SIZE, &fCaps.fMaxTextureSize);
-    GR_GL_GetIntegerv(gl, GR_GL_MAX_RENDERBUFFER_SIZE, &fCaps.fMaxRenderTargetSize);
+    GR_GL_GetIntegerv(gl, GR_GL_MAX_TEXTURE_SIZE, &caps->fMaxTextureSize);
+    GR_GL_GetIntegerv(gl, GR_GL_MAX_RENDERBUFFER_SIZE, &caps->fMaxRenderTargetSize);
     // Our render targets are always created with textures as the color
     // attachment, hence this min:
-    fCaps.fMaxRenderTargetSize = GrMin(fCaps.fMaxTextureSize, fCaps.fMaxRenderTargetSize);
+    caps->fMaxRenderTargetSize = GrMin(caps->fMaxTextureSize, caps->fMaxRenderTargetSize);
 
-    fCaps.fFSAASupport = GrGLCaps::kNone_MSFBOType != this->glCaps().msFBOType();
+    caps->fFSAASupport = GrGLCaps::kNone_MSFBOType != this->glCaps().msFBOType();
+    caps->fPathStencilingSupport = GR_GL_USE_NV_PATH_RENDERING &&
+                                   this->hasExtension("GL_NV_path_rendering");
+
+    // Enable supported shader-related caps
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        caps->fDualSourceBlendingSupport =
+                            this->glVersion() >= GR_GL_VER(3,3) ||
+                            this->hasExtension("GL_ARB_blend_func_extended");
+        caps->fShaderDerivativeSupport = true;
+        // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS
+        caps->fGeometryShaderSupport =
+                                this->glVersion() >= GR_GL_VER(3,2) &&
+                                this->glslGeneration() >= k150_GrGLSLGeneration;
+    } else {
+        caps->fShaderDerivativeSupport =
+                            this->hasExtension("GL_OES_standard_derivatives");
+    }
 }
 
-bool GrGpuGL::canPreserveReadWriteUnpremulPixels() {
-    if (kUnknown_CanPreserveUnpremulRoundtrip ==
-        fCanPreserveUnpremulRoundtrip) {
+void GrGpuGL::fillInConfigRenderableTable() {
 
-        SkAutoTMalloc<uint32_t> data(256 * 256 * 3);
-        uint32_t* srcData = data.get();
-        uint32_t* firstRead = data.get() + 256 * 256;
-        uint32_t* secondRead = data.get() + 2 * 256 * 256;
+    // OpenGL < 3.0
+    //  no support for render targets unless the GL_ARB_framebuffer_object
+    //  extension is supported (in which case we get ALPHA, RED, RG, RGB,
+    //  RGBA (ALPHA8, RGBA4, RGBA8) for OpenGL > 1.1). Note that we
+    //  probably don't get R8 in this case.
 
-        for (int y = 0; y < 256; ++y) {
-            for (int x = 0; x < 256; ++x) {
-                uint8_t* color = reinterpret_cast<uint8_t*>(&srcData[256*y + x]);
-                color[3] = y;
-                color[2] = x;
-                color[1] = x;
-                color[0] = x;
-            }
+    // OpenGL 3.0
+    //  base color renderable: ALPHA, RED, RG, RGB, and RGBA
+    //  sized derivatives: ALPHA8, R8, RGBA4, RGBA8
+
+    // >= OpenGL 3.1
+    //  base color renderable: RED, RG, RGB, and RGBA
+    //  sized derivatives: R8, RGBA4, RGBA8
+    //  if the GL_ARB_compatibility extension is supported then we get back
+    //  support for GL_ALPHA and ALPHA8
+
+    // GL_EXT_bgra adds BGRA render targets to any version
+
+    // ES 2.0
+    //  color renderable: RGBA4, RGB5_A1, RGB565
+    //  GL_EXT_texture_rg adds support for R8 as a color render target
+    //  GL_OES_rgb8_rgba8 and/or GL_ARM_rgba8 adds support for RGBA8
+    //  GL_EXT_texture_format_BGRA8888 and/or GL_APPLE_texture_format_BGRA8888
+    //          added BGRA support
+
+    if (kDesktop_GrGLBinding == this->glBinding()) {
+        // Post 3.0 we will get R8
+        // Prior to 3.0 we will get ALPHA8 (with GL_ARB_framebuffer_object)
+        if (this->glVersion() >= GR_GL_VER(3,0) ||
+            this->hasExtension("GL_ARB_framebuffer_object")) {
+            fConfigRenderSupport[kAlpha_8_GrPixelConfig] = true;
         }
-
-        // We have broader support for read/write pixels on render targets
-        // than on textures.
-        GrTextureDesc dstDesc;
-        dstDesc.fFlags = kRenderTarget_GrTextureFlagBit |
-                         kNoStencil_GrTextureFlagBit;
-        dstDesc.fWidth = 256;
-        dstDesc.fHeight = 256;
-        dstDesc.fConfig = kRGBA_8888_GrPixelConfig;
-        dstDesc.fSampleCnt = 0;
-
-        SkAutoTUnref<GrTexture> dstTex(this->createTexture(dstDesc, NULL, 0));
-        if (!dstTex.get()) {
-            return false;
-        }
-        GrRenderTarget* rt = dstTex.get()->asRenderTarget();
-        GrAssert(NULL != rt);
-
-        bool failed = true;
-        static const UnpremulConversion gMethods[] = {
-            kUpOnWrite_DownOnRead_UnpremulConversion,
-            kDownOnWrite_UpOnRead_UnpremulConversion,
-        };
-
-        // pretend that we can do the roundtrip to avoid recursive calls to
-        // this function
-        fCanPreserveUnpremulRoundtrip = kYes_CanPreserveUnpremulRoundtrip;
-        for (size_t i = 0; i < GR_ARRAY_COUNT(gMethods) && failed; ++i) {
-            fUnpremulConversion = gMethods[i];
-            rt->writePixels(0, 0,
-                            256, 256,
-                            kRGBA_8888_UPM_GrPixelConfig, srcData, 0);
-            rt->readPixels(0, 0,
-                           256, 256,
-                           kRGBA_8888_UPM_GrPixelConfig, firstRead, 0);
-            rt->writePixels(0, 0,
-                            256, 256,
-                            kRGBA_8888_UPM_GrPixelConfig, firstRead, 0);
-            rt->readPixels(0, 0,
-                           256, 256,
-                           kRGBA_8888_UPM_GrPixelConfig, secondRead, 0);
-            failed = false;
-            for (int j = 0; j < 256 * 256; ++j) {
-                if (firstRead[j] != secondRead[j]) {
-                    failed = true;
-                    break;
-                }
-            }
-        }
-        fCanPreserveUnpremulRoundtrip = failed ? 
-                        kNo_CanPreserveUnpremulRoundtrip :
-                        kYes_CanPreserveUnpremulRoundtrip;
+    } else {
+        // On ES we can only hope for R8
+        fConfigRenderSupport[kAlpha_8_GrPixelConfig] =
+                                this->glCaps().textureRedSupport();
     }
 
-    if (kYes_CanPreserveUnpremulRoundtrip == fCanPreserveUnpremulRoundtrip) {
-        return true;
-    } else {
-        return false;
+    if (kDesktop_GrGLBinding != this->glBinding()) {
+        // only available in ES
+        fConfigRenderSupport[kRGB_565_GrPixelConfig] = true;
+    }
+
+    // Pre 3.0, Ganesh relies on either GL_ARB_framebuffer_object or
+    // GL_EXT_framebuffer_object for FBO support. Both of these
+    // allow RGBA4 render targets so this is always supported.
+    fConfigRenderSupport[kRGBA_4444_GrPixelConfig] = true;
+
+    if (this->glCaps().rgba8RenderbufferSupport()) {
+        fConfigRenderSupport[kRGBA_8888_GrPixelConfig] = true;
+    }
+
+    if (this->glCaps().bgraFormatSupport()) {
+        fConfigRenderSupport[kBGRA_8888_GrPixelConfig] = true;
     }
 }
 
@@ -399,71 +372,68 @@
         this->glCaps().print();
     }
 
-    // We detect cases when blending is effectively off
-    fHWBlendDisabled = false;
-    GL_CALL(Enable(GR_GL_BLEND));
-
     // we don't use the zb at all
     GL_CALL(Disable(GR_GL_DEPTH_TEST));
     GL_CALL(DepthMask(GR_GL_FALSE));
 
-    GL_CALL(Disable(GR_GL_CULL_FACE));
-    GL_CALL(FrontFace(GR_GL_CCW));
-    fHWDrawState.setDrawFace(GrDrawState::kBoth_DrawFace);
+    fHWDrawFace = GrDrawState::kInvalid_DrawFace;
+    fHWDitherEnabled = kUnknown_TriState;
 
-    GL_CALL(Disable(GR_GL_DITHER));
     if (kDesktop_GrGLBinding == this->glBinding()) {
-        GL_CALL(Disable(GR_GL_LINE_SMOOTH));
+        // Desktop-only state that we never change
         GL_CALL(Disable(GR_GL_POINT_SMOOTH));
-        GL_CALL(Disable(GR_GL_MULTISAMPLE));
-        fHWAAState.fMSAAEnabled = false;
-        fHWAAState.fSmoothLineEnabled = false;
-    }
+        GL_CALL(Disable(GR_GL_LINE_SMOOTH));
+        GL_CALL(Disable(GR_GL_POLYGON_SMOOTH));
+        GL_CALL(Disable(GR_GL_POLYGON_STIPPLE));
+        GL_CALL(Disable(GR_GL_COLOR_LOGIC_OP));
+        if (this->glCaps().imagingSupport()) {
+            GL_CALL(Disable(GR_GL_COLOR_TABLE));
+        }
+        GL_CALL(Disable(GR_GL_INDEX_LOGIC_OP));
+        GL_CALL(Disable(GR_GL_POLYGON_OFFSET_FILL));
+        // Since ES doesn't support glPointSize at all we always use the VS to
+        // set the point size
+        GL_CALL(Enable(GR_GL_VERTEX_PROGRAM_POINT_SIZE));
 
-    GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
-    fHWDrawState.resetStateFlags();
+        // We should set glPolygonMode(FRONT_AND_BACK,FILL) here, too. It isn't
+        // currently part of our gl interface. There are probably others as
+        // well.
+    }
+    fHWAAState.invalidate();
+    fHWWriteToColor = kUnknown_TriState;
 
     // we only ever use lines in hairline mode
     GL_CALL(LineWidth(1));
 
     // invalid
-    fActiveTextureUnitIdx = -1;
+    fHWActiveTextureUnitIdx = -1;
 
-    // illegal values
-    fHWDrawState.setBlendFunc((GrBlendCoeff)0xFF, (GrBlendCoeff)0xFF);
-
-    fHWDrawState.setBlendConstant(0x00000000);
-    GL_CALL(BlendColor(0,0,0,0));
-
-    fHWDrawState.setColor(GrColor_ILLEGAL);
-
-    fHWDrawState.setViewMatrix(GrMatrix::InvalidMatrix());
+    fHWBlendState.invalidate();
 
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        fHWDrawState.setTexture(s, NULL);
-        fHWDrawState.sampler(s)->setRadial2Params(-GR_ScalarMax,
-                                                  -GR_ScalarMax,
-                                                  true);
-        *fHWDrawState.sampler(s)->matrix() = GrMatrix::InvalidMatrix();
-        fHWDrawState.sampler(s)->setConvolutionParams(0, NULL);
+        fHWBoundTextures[s] = NULL;
     }
 
-    fHWBounds.fScissorRect.invalidate();
-    fHWBounds.fScissorEnabled = false;
-    GL_CALL(Disable(GR_GL_SCISSOR_TEST));
-    fHWBounds.fViewportRect.invalidate();
+    fHWScissorSettings.invalidate();
 
-    fHWDrawState.stencil()->invalidate();
-    fHWStencilClip = false;
-    fClipInStencil = false;
+    fHWViewport.invalidate();
+
+    fHWStencilSettings.invalidate();
+    fHWStencilTestEnabled = kUnknown_TriState;
 
     fHWGeometryState.fIndexBuffer = NULL;
     fHWGeometryState.fVertexBuffer = NULL;
-    
+
     fHWGeometryState.fArrayPtrsDirty = true;
 
-    GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
-    fHWDrawState.setRenderTarget(NULL);
+    fHWBoundRenderTarget = NULL;
+
+    fHWPathMatrixState.invalidate();
+    if (fCaps.pathStencilingSupport()) {
+        // we don't use the model view matrix.
+        GL_CALL(MatrixMode(GR_GL_MODELVIEW));
+        GL_CALL(LoadIdentity());
+    }
 
     // we assume these values
     if (this->glCaps().unpackRowLengthSupport()) {
@@ -478,28 +448,71 @@
     if (this->glCaps().packFlipYSupport()) {
         GL_CALL(PixelStorei(GR_GL_PACK_REVERSE_ROW_ORDER, GR_GL_FALSE));
     }
+
+    fHWGeometryState.fVertexOffset = ~0U;
+
+    // Third party GL code may have left vertex attributes enabled. Some GL
+    // implementations (osmesa) may read vetex attributes that are not required
+    // by the current shader. Therefore, we have to ensure that only the
+    // attributes we require for the current draw are enabled or we may cause an
+    // invalid read.
+
+    // Disable all vertex layout bits so that next flush will assume all
+    // optional vertex attributes are disabled.
+    fHWGeometryState.fVertexLayout = 0;
+
+    // We always use the this attribute and assume it is always enabled.
+    int posAttrIdx = GrGLProgram::PositionAttributeIdx();
+    GL_CALL(EnableVertexAttribArray(posAttrIdx));
+    // Disable all other vertex attributes.
+    for  (int va = 0; va < this->glCaps().maxVertexAttributes(); ++va) {
+        if (va != posAttrIdx) {
+            GL_CALL(DisableVertexAttribArray(va));
+        }
+    }
+
+    fHWProgramID = 0;
+    fHWConstAttribColor = GrColor_ILLEGAL;
+    fHWConstAttribCoverage = GrColor_ILLEGAL;
 }
 
-GrTexture* GrGpuGL::onCreatePlatformTexture(const GrPlatformTextureDesc& desc) {
-    GrGLTexture::Desc glTexDesc;
-    if (!configToGLFormats(desc.fConfig, false, NULL, NULL, NULL)) {
+GrTexture* GrGpuGL::onWrapBackendTexture(const GrBackendTextureDesc& desc) {
+    if (!this->configToGLFormats(desc.fConfig, false, NULL, NULL, NULL)) {
         return NULL;
     }
 
+    if (0 == desc.fTextureHandle) {
+        return NULL;
+    }
+
+    // FIXME:  add support for TopLeft RT's by flipping all draws.
+    if (desc.fFlags & kRenderTarget_GrBackendTextureFlag &&
+        kBottomLeft_GrSurfaceOrigin != desc.fOrigin) {
+        return NULL;
+    }
+
+    int maxSize = this->getCaps().maxTextureSize();
+    if (desc.fWidth > maxSize || desc.fHeight > maxSize) {
+        return NULL;
+    }
+
+    GrGLTexture::Desc glTexDesc;
+    // next line relies on GrBackendTextureDesc's flags matching GrTexture's
+    glTexDesc.fFlags = (GrTextureFlags) desc.fFlags;
     glTexDesc.fWidth = desc.fWidth;
     glTexDesc.fHeight = desc.fHeight;
     glTexDesc.fConfig = desc.fConfig;
+    glTexDesc.fSampleCnt = desc.fSampleCnt;
     glTexDesc.fTextureID = static_cast<GrGLuint>(desc.fTextureHandle);
-    glTexDesc.fOwnsID = false;
-    glTexDesc.fOrientation = GrGLTexture::kBottomUp_Orientation;
+    glTexDesc.fIsWrapped = true;
+    glTexDesc.fOrigin = desc.fOrigin;
 
     GrGLTexture* texture = NULL;
-    if (desc.fFlags & kRenderTarget_GrPlatformTextureFlag) {
+    if (desc.fFlags & kRenderTarget_GrBackendTextureFlag) {
         GrGLRenderTarget::Desc glRTDesc;
         glRTDesc.fRTFBOID = 0;
         glRTDesc.fTexFBOID = 0;
         glRTDesc.fMSColorRenderbufferID = 0;
-        glRTDesc.fOwnIDs = true;
         glRTDesc.fConfig = desc.fConfig;
         glRTDesc.fSampleCnt = desc.fSampleCnt;
         if (!this->createRenderTargetObjects(glTexDesc.fWidth,
@@ -508,45 +521,49 @@
                                              &glRTDesc)) {
             return NULL;
         }
-        texture = new GrGLTexture(this, glTexDesc, glRTDesc);
+        texture = SkNEW_ARGS(GrGLTexture, (this, glTexDesc, glRTDesc));
     } else {
-        texture = new GrGLTexture(this, glTexDesc);
+        texture = SkNEW_ARGS(GrGLTexture, (this, glTexDesc));
     }
     if (NULL == texture) {
         return NULL;
     }
-    
+
     this->setSpareTextureUnit();
     return texture;
 }
 
-GrRenderTarget* GrGpuGL::onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) {
+GrRenderTarget* GrGpuGL::onWrapBackendRenderTarget(const GrBackendRenderTargetDesc& desc) {
     GrGLRenderTarget::Desc glDesc;
     glDesc.fConfig = desc.fConfig;
     glDesc.fRTFBOID = static_cast<GrGLuint>(desc.fRenderTargetHandle);
     glDesc.fMSColorRenderbufferID = 0;
     glDesc.fTexFBOID = GrGLRenderTarget::kUnresolvableFBOID;
     glDesc.fSampleCnt = desc.fSampleCnt;
-    glDesc.fOwnIDs = false;
+    glDesc.fIsWrapped = true;
     GrGLIRect viewport;
     viewport.fLeft   = 0;
     viewport.fBottom = 0;
     viewport.fWidth  = desc.fWidth;
     viewport.fHeight = desc.fHeight;
-    
-    GrRenderTarget* tgt = new GrGLRenderTarget(this, glDesc, viewport);
+
+    GrRenderTarget* tgt = SkNEW_ARGS(GrGLRenderTarget,
+                                     (this, glDesc, viewport));
     if (desc.fStencilBits) {
         GrGLStencilBuffer::Format format;
         format.fInternalFormat = GrGLStencilBuffer::kUnknownInternalFormat;
         format.fPacked = false;
         format.fStencilBits = desc.fStencilBits;
         format.fTotalBits = desc.fStencilBits;
-        GrGLStencilBuffer* sb = new GrGLStencilBuffer(this,
-                                                      0,
-                                                      desc.fWidth,
-                                                      desc.fHeight,
-                                                      desc.fSampleCnt,
-                                                      format);
+        static const bool kIsSBWrapped = false;
+        GrGLStencilBuffer* sb = SkNEW_ARGS(GrGLStencilBuffer,
+                                           (this,
+                                            kIsSBWrapped,
+                                            0,
+                                            desc.fWidth,
+                                            desc.fHeight,
+                                            desc.fSampleCnt,
+                                            format));
         tgt->setStencilBuffer(sb);
         sb->unref();
     }
@@ -567,14 +584,16 @@
     this->setSpareTextureUnit();
     GL_CALL(BindTexture(GR_GL_TEXTURE_2D, glTex->textureID()));
     GrGLTexture::Desc desc;
-    desc.fConfig = glTex->config();
+    desc.fFlags = glTex->desc().fFlags;
     desc.fWidth = glTex->width();
     desc.fHeight = glTex->height();
-    desc.fOrientation = glTex->orientation();
+    desc.fConfig = glTex->config();
+    desc.fSampleCnt = glTex->desc().fSampleCnt;
     desc.fTextureID = glTex->textureID();
+    desc.fOrigin = glTex->origin();
 
     this->uploadTexData(desc, false,
-                        left, top, width, height, 
+                        left, top, width, height,
                         config, buffer, rowBytes);
 }
 
@@ -624,20 +643,17 @@
     // in case we need a temporary, trimmed copy of the src pixels
     SkAutoSMalloc<128 * 128> tempStorage;
 
+    // paletted textures cannot be partially updated
     bool useTexStorage = isNewTexture &&
+                         desc.fConfig != kIndex_8_GrPixelConfig &&
                          this->glCaps().texStorageSupport();
-    if (useTexStorage) {
-        if (kDesktop_GrGLBinding == this->glBinding()) {
-            // 565 is not a sized internal format on desktop GL. So on desktop
-            // with 565 we always use an unsized internal format to let the
-            // system pick the best sized format to convert the 565 data to.
-            // Since glTexStorage only allows sized internal formats we will
-            // instead fallback to glTexImage2D.
-            useTexStorage = desc.fConfig != kRGB_565_GrPixelConfig;
-        } else {
-            // ES doesn't allow paletted textures to be used with tex storage
-            useTexStorage = desc.fConfig != kIndex_8_GrPixelConfig;
-        }
+
+    if (useTexStorage && kDesktop_GrGLBinding == this->glBinding()) {
+        // 565 is not a sized internal format on desktop GL. So on desktop with
+        // 565 we always use an unsized internal format to let the system pick
+        // the best sized format to convert the 565 data to. Since TexStorage
+        // only allows sized internal formats we will instead use TexImage2D.
+        useTexStorage = desc.fConfig != kRGB_565_GrPixelConfig;
     }
 
     GrGLenum internalFormat;
@@ -665,7 +681,7 @@
     bool swFlipY = false;
     bool glFlipY = false;
     if (NULL != data) {
-        if (GrGLTexture::kBottomUp_Orientation == desc.fOrientation) {
+        if (kBottomLeft_GrSurfaceOrigin == desc.fOrigin) {
             if (this->glCaps().unpackFlipYSupport()) {
                 glFlipY = true;
             } else {
@@ -707,7 +723,7 @@
         GL_CALL(PixelStorei(GR_GL_UNPACK_ALIGNMENT, static_cast<GrGLint>(bpp)));
     }
     bool succeeded = true;
-    if (isNewTexture && 
+    if (isNewTexture &&
         0 == left && 0 == top &&
         desc.fWidth == width && desc.fHeight == height) {
         CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
@@ -778,16 +794,49 @@
     return succeeded;
 }
 
+namespace {
+bool renderbuffer_storage_msaa(GrGLContextInfo& ctxInfo,
+                               int sampleCount,
+                               GrGLenum format,
+                               int width, int height) {
+    CLEAR_ERROR_BEFORE_ALLOC(ctxInfo.interface());
+    GrAssert(GrGLCaps::kNone_MSFBOType != ctxInfo.caps().msFBOType());
+    bool created = false;
+    if (GrGLCaps::kNVDesktop_CoverageAAType ==
+        ctxInfo.caps().coverageAAType()) {
+        const GrGLCaps::MSAACoverageMode& mode =
+            ctxInfo.caps().getMSAACoverageMode(sampleCount);
+        GL_ALLOC_CALL(ctxInfo.interface(),
+                      RenderbufferStorageMultisampleCoverage(GR_GL_RENDERBUFFER,
+                                                        mode.fCoverageSampleCnt,
+                                                        mode.fColorSampleCnt,
+                                                        format,
+                                                        width, height));
+        created = (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(ctxInfo.interface()));
+    }
+    if (!created) {
+        // glRBMS will fail if requested samples is > max samples.
+        sampleCount = GrMin(sampleCount, ctxInfo.caps().maxSampleCount());
+        GL_ALLOC_CALL(ctxInfo.interface(),
+                      RenderbufferStorageMultisample(GR_GL_RENDERBUFFER,
+                                                     sampleCount,
+                                                     format,
+                                                     width, height));
+        created = (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(ctxInfo.interface()));
+    }
+    return created;
+}
+}
+
 bool GrGpuGL::createRenderTargetObjects(int width, int height,
                                         GrGLuint texID,
                                         GrGLRenderTarget::Desc* desc) {
     desc->fMSColorRenderbufferID = 0;
     desc->fRTFBOID = 0;
     desc->fTexFBOID = 0;
-    desc->fOwnIDs = true;
+    desc->fIsWrapped = false;
 
     GrGLenum status;
-    GrGLint err;
 
     GrGLenum msColorFormat = 0; // suppress warning
 
@@ -795,7 +844,7 @@
     if (!desc->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.
@@ -806,7 +855,7 @@
         GL_CALL(GenFramebuffers(1, &desc->fRTFBOID));
         GL_CALL(GenRenderbuffers(1, &desc->fMSColorRenderbufferID));
         if (!desc->fRTFBOID ||
-            !desc->fMSColorRenderbufferID || 
+            !desc->fMSColorRenderbufferID ||
             !this->configToGLFormats(desc->fConfig,
                                      // GLES requires sized internal formats
                                      kES2_GrGLBinding == this->glBinding(),
@@ -818,23 +867,19 @@
     }
 
     // below here we may bind the FBO
-    fHWDrawState.setRenderTarget(NULL);
+    fHWBoundRenderTarget = NULL;
     if (desc->fRTFBOID != desc->fTexFBOID) {
-        GrAssert(desc->fSampleCnt > 1);
+        GrAssert(desc->fSampleCnt > 0);
         GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER,
                                desc->fMSColorRenderbufferID));
-        CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
-        GL_ALLOC_CALL(this->glInterface(),
-                      RenderbufferStorageMultisample(GR_GL_RENDERBUFFER, 
-                                                     desc->fSampleCnt,
-                                                     msColorFormat,
-                                                     width, height));
-        err = CHECK_ALLOC_ERROR(this->glInterface());
-        if (err != GR_GL_NO_ERROR) {
+        if (!renderbuffer_storage_msaa(fGLContextInfo,
+                                       desc->fSampleCnt,
+                                       msColorFormat,
+                                       width, height)) {
             goto FAILED;
         }
         GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, desc->fRTFBOID));
-        GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER, 
+        GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                       GR_GL_COLOR_ATTACHMENT0,
                                       GR_GL_RENDERBUFFER,
                                       desc->fMSColorRenderbufferID));
@@ -882,7 +927,7 @@
     return NULL;
 }
 
-#if GR_DEBUG
+#if 0 && GR_DEBUG
 static size_t as_size_t(int x) {
     return x;
 }
@@ -892,25 +937,23 @@
                                     const void* srcData,
                                     size_t rowBytes) {
 
-#if GR_COLLECT_STATS
-    ++fStats.fTextureCreateCnt;
-#endif
-
     GrGLTexture::Desc glTexDesc;
     GrGLRenderTarget::Desc  glRTDesc;
 
     // Attempt to catch un- or wrongly initialized sample counts;
     GrAssert(desc.fSampleCnt >= 0 && desc.fSampleCnt <= 64);
 
+    glTexDesc.fFlags  = desc.fFlags;
     glTexDesc.fWidth  = desc.fWidth;
     glTexDesc.fHeight = desc.fHeight;
     glTexDesc.fConfig = desc.fConfig;
-    glTexDesc.fOwnsID = true;
+    glTexDesc.fSampleCnt = desc.fSampleCnt;
+    glTexDesc.fIsWrapped = false;
 
     glRTDesc.fMSColorRenderbufferID = 0;
     glRTDesc.fRTFBOID = 0;
     glRTDesc.fTexFBOID = 0;
-    glRTDesc.fOwnIDs = true;
+    glRTDesc.fIsWrapped = false;
     glRTDesc.fConfig = glTexDesc.fConfig;
 
     bool renderTarget = 0 != (desc.fFlags & kRenderTarget_GrTextureFlagBit);
@@ -920,18 +963,18 @@
     // We keep GrRenderTargets in GL's normal orientation so that they
     // can be drawn to by the outside world without the client having
     // to render upside down.
-    glTexDesc.fOrientation = renderTarget ? GrGLTexture::kBottomUp_Orientation :
-                                            GrGLTexture::kTopDown_Orientation;
+    glTexDesc.fOrigin = renderTarget ? kBottomLeft_GrSurfaceOrigin : kTopLeft_GrSurfaceOrigin;
 
     glRTDesc.fSampleCnt = desc.fSampleCnt;
     if (GrGLCaps::kNone_MSFBOType == this->glCaps().msFBOType() &&
         desc.fSampleCnt) {
-        GrPrintf("MSAA RT requested but not supported on this platform.");
+        //GrPrintf("MSAA RT requested but not supported on this platform.");
+        return return_null_texture();
     }
 
     if (renderTarget) {
-        if (glTexDesc.fWidth > caps.fMaxRenderTargetSize ||
-            glTexDesc.fHeight > caps.fMaxRenderTargetSize) {
+        if (glTexDesc.fWidth > caps.maxRenderTargetSize() ||
+            glTexDesc.fHeight > caps.maxRenderTargetSize()) {
             return return_null_texture();
         }
     }
@@ -980,9 +1023,9 @@
 
     GrGLTexture* tex;
     if (renderTarget) {
-#if GR_COLLECT_STATS
-        ++fStats.fRenderTargetCreateCnt;
-#endif
+        // unbind the texture from the texture unit before binding it to the frame buffer
+        GL_CALL(BindTexture(GR_GL_TEXTURE_2D, 0));
+
         if (!this->createRenderTargetObjects(glTexDesc.fWidth,
                                              glTexDesc.fHeight,
                                              glTexDesc.fTextureID,
@@ -990,9 +1033,9 @@
             GL_CALL(DeleteTextures(1, &glTexDesc.fTextureID));
             return return_null_texture();
         }
-        tex = new GrGLTexture(this, glTexDesc, glRTDesc);
+        tex = SkNEW_ARGS(GrGLTexture, (this, glTexDesc, glRTDesc));
     } else {
-        tex = new GrGLTexture(this, glTexDesc);
+        tex = SkNEW_ARGS(GrGLTexture, (this, glTexDesc));
     }
     tex->setCachedTexParams(initialTexParams, this->getResetTimestamp());
 #ifdef TRACE_TEXTURE_CREATION
@@ -1007,7 +1050,7 @@
 const GrGLuint kUnknownBitCount = GrGLStencilBuffer::kUnknownBitCount;
 
 void inline get_stencil_rb_sizes(const GrGLInterface* gl,
-                                 GrGLuint rb, 
+                                 GrGLuint rb,
                                  GrGLStencilBuffer::Format* format) {
     // we shouldn't ever know one size and not the other
     GrAssert((kUnknownBitCount == format->fStencilBits) ==
@@ -1032,7 +1075,7 @@
                                                  int width, int height) {
 
     // All internally created RTs are also textures. We don't create
-    // SBs for a client's standalone RT (that is RT that isnt also a texture).
+    // SBs for a client's standalone RT (that is a RT that isn't also a texture).
     GrAssert(rt->asTexture());
     GrAssert(width >= rt->width());
     GrAssert(height >= rt->height());
@@ -1044,8 +1087,6 @@
         return false;
     }
 
-    GrGLStencilBuffer* sb = NULL;
-
     int stencilFmtCnt = this->glCaps().stencilFormats().count();
     for (int i = 0; i < stencilFmtCnt; ++i) {
         GL_CALL(BindRenderbuffer(GR_GL_RENDERBUFFER, sbID));
@@ -1058,35 +1099,36 @@
         CLEAR_ERROR_BEFORE_ALLOC(this->glInterface());
         // we do this "if" so that we don't call the multisample
         // version on a GL that doesn't have an MSAA extension.
-        if (samples > 1) {
-            GL_ALLOC_CALL(this->glInterface(),
-                          RenderbufferStorageMultisample(GR_GL_RENDERBUFFER,
-                                                         samples,
-                                                         sFmt.fInternalFormat,
-                                                         width, height));
+        bool created;
+        if (samples > 0) {
+            created = renderbuffer_storage_msaa(fGLContextInfo,
+                                                samples,
+                                                sFmt.fInternalFormat,
+                                                width, height);
         } else {
             GL_ALLOC_CALL(this->glInterface(),
                           RenderbufferStorage(GR_GL_RENDERBUFFER,
                                               sFmt.fInternalFormat,
                                               width, height));
+            created =
+                (GR_GL_NO_ERROR == CHECK_ALLOC_ERROR(this->glInterface()));
         }
-
-        GrGLenum err = CHECK_ALLOC_ERROR(this->glInterface());
-        if (err == GR_GL_NO_ERROR) {
-            // After sized formats we attempt an unsized format and take whatever
-            // sizes GL gives us. In that case we query for the size.
+        if (created) {
+            // 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;
             get_stencil_rb_sizes(this->glInterface(), sbID, &format);
-            sb = new GrGLStencilBuffer(this, sbID, width, height, 
-                                       samples, format);
+            static const bool kIsWrapped = false;
+            SkAutoTUnref<GrStencilBuffer> sb(SkNEW_ARGS(GrGLStencilBuffer,
+                                                  (this, kIsWrapped, sbID, width, height,
+                                                  samples, format)));
             if (this->attachStencilBufferToRenderTarget(sb, rt)) {
                 fLastSuccessfulStencilFmtIdx = sIdx;
+                sb->transferToCache();
                 rt->setStencilBuffer(sb);
-                sb->unref();
                 return true;
            }
            sb->abandon(); // otherwise we lose sbID
-           sb->unref();
         }
     }
     GL_CALL(DeleteRenderbuffers(1, &sbID));
@@ -1118,7 +1160,7 @@
         GrGLStencilBuffer* glsb = (GrGLStencilBuffer*) sb;
         GrGLuint rb = glsb->renderbufferID();
 
-        fHWDrawState.setRenderTarget(NULL);
+        fHWBoundRenderTarget = NULL;
         GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, fbo));
         GL_CALL(FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                       GR_GL_STENCIL_ATTACHMENT,
@@ -1179,8 +1221,10 @@
             fHWGeometryState.fVertexBuffer = NULL;
             return NULL;
         }
-        GrGLVertexBuffer* vertexBuffer = new GrGLVertexBuffer(this, id,
-                                                              size, dynamic);
+        static const bool kIsWrapped = false;
+        GrGLVertexBuffer* vertexBuffer = SkNEW_ARGS(GrGLVertexBuffer,
+                                                    (this, kIsWrapped, id,
+                                                     size, dynamic));
         fHWGeometryState.fVertexBuffer = vertexBuffer;
         return vertexBuffer;
     }
@@ -1206,15 +1250,21 @@
             fHWGeometryState.fIndexBuffer = NULL;
             return NULL;
         }
-        GrIndexBuffer* indexBuffer = new GrGLIndexBuffer(this, id,
-                                                         size, dynamic);
+        static const bool kIsWrapped = false;
+        GrIndexBuffer* indexBuffer = SkNEW_ARGS(GrGLIndexBuffer,
+                                                (this, kIsWrapped, id, size, dynamic));
         fHWGeometryState.fIndexBuffer = indexBuffer;
         return indexBuffer;
     }
     return NULL;
 }
 
-void GrGpuGL::flushScissor(const GrIRect* rect) {
+GrPath* GrGpuGL::onCreatePath(const SkPath& inPath) {
+    GrAssert(fCaps.pathStencilingSupport());
+    return SkNEW_ARGS(GrGLPath, (this, inPath));
+}
+
+void GrGpuGL::flushScissor() {
     const GrDrawState& drawState = this->getDrawState();
     const GrGLRenderTarget* rt =
         static_cast<const GrGLRenderTarget*>(drawState.getRenderTarget());
@@ -1222,29 +1272,31 @@
     GrAssert(NULL != rt);
     const GrGLIRect& vp = rt->getViewport();
 
-    GrGLIRect scissor;
-    if (NULL != rect) {
-        scissor.setRelativeTo(vp, rect->fLeft, rect->fTop,
-                              rect->width(), rect->height());
-        if (scissor.contains(vp)) {
-            rect = NULL;
+    if (fScissorState.fEnabled) {
+        GrGLIRect scissor;
+        scissor.setRelativeTo(vp,
+                              fScissorState.fRect.fLeft,
+                              fScissorState.fRect.fTop,
+                              fScissorState.fRect.width(),
+                              fScissorState.fRect.height());
+        // if the scissor fully contains the viewport then we fall through and
+        // disable the scissor test.
+        if (!scissor.contains(vp)) {
+            if (fHWScissorSettings.fRect != scissor) {
+                scissor.pushToGLScissor(this->glInterface());
+                fHWScissorSettings.fRect = scissor;
+            }
+            if (kYes_TriState != fHWScissorSettings.fEnabled) {
+                GL_CALL(Enable(GR_GL_SCISSOR_TEST));
+                fHWScissorSettings.fEnabled = kYes_TriState;
+            }
+            return;
         }
     }
-
-    if (NULL != rect) {
-        if (fHWBounds.fScissorRect != scissor) {
-            scissor.pushToGLScissor(this->glInterface());
-            fHWBounds.fScissorRect = scissor;
-        }
-        if (!fHWBounds.fScissorEnabled) {
-            GL_CALL(Enable(GR_GL_SCISSOR_TEST));
-            fHWBounds.fScissorEnabled = true;
-        }
-    } else {
-        if (fHWBounds.fScissorEnabled) {
-            GL_CALL(Disable(GR_GL_SCISSOR_TEST));
-            fHWBounds.fScissorEnabled = false;
-        }
+    if (kNo_TriState != fHWScissorSettings.fEnabled) {
+        GL_CALL(Disable(GR_GL_SCISSOR_TEST));
+        fHWScissorSettings.fEnabled = kNo_TriState;
+        return;
     }
 }
 
@@ -1266,21 +1318,23 @@
         }
     }
     this->flushRenderTarget(rect);
-    this->flushScissor(rect);
+    GrAutoTRestore<ScissorState> asr(&fScissorState);
+    fScissorState.fEnabled = (NULL != rect);
+    if (fScissorState.fEnabled) {
+        fScissorState.fRect = *rect;
+    }
+    this->flushScissor();
 
     GrGLfloat r, g, b, a;
     static const GrGLfloat scale255 = 1.f / 255.f;
     a = GrColorUnpackA(color) * scale255;
     GrGLfloat scaleRGB = scale255;
-    if (GrPixelConfigIsUnpremultiplied(rt->config())) {
-        scaleRGB *= a;
-    }
     r = GrColorUnpackR(color) * scaleRGB;
     g = GrColorUnpackG(color) * scaleRGB;
     b = GrColorUnpackB(color) * scaleRGB;
 
     GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
-    fHWDrawState.disableState(GrDrawState::kNoColorWrites_StateBit);
+    fHWWriteToColor = kYes_TriState;
     GL_CALL(ClearColor(r, g, b, a));
     GL_CALL(Clear(GR_GL_COLOR_BUFFER_BIT));
 }
@@ -1289,17 +1343,17 @@
     if (NULL == this->getDrawState().getRenderTarget()) {
         return;
     }
-    
+
     this->flushRenderTarget(&GrIRect::EmptyIRect());
 
-    if (fHWBounds.fScissorEnabled) {
-        GL_CALL(Disable(GR_GL_SCISSOR_TEST));
-        fHWBounds.fScissorEnabled = false;
-    }
+    GrAutoTRestore<ScissorState> asr(&fScissorState);
+    fScissorState.fEnabled = false;
+    this->flushScissor();
+
     GL_CALL(StencilMask(0xffffffff));
     GL_CALL(ClearStencil(0));
     GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
-    fHWDrawState.stencil()->invalidate();
+    fHWStencilSettings.invalidate();
 }
 
 void GrGpuGL::clearStencilClip(const GrIRect& rect, bool insideClip) {
@@ -1329,11 +1383,16 @@
         value = 0;
     }
     this->flushRenderTarget(&GrIRect::EmptyIRect());
-    this->flushScissor(&rect);
-    GL_CALL(StencilMask(clipStencilMask));
+
+    GrAutoTRestore<ScissorState> asr(&fScissorState);
+    fScissorState.fEnabled = true;
+    fScissorState.fRect = rect;
+    this->flushScissor();
+
+    GL_CALL(StencilMask((uint32_t) clipStencilMask));
     GL_CALL(ClearStencil(value));
     GL_CALL(Clear(GR_GL_STENCIL_BUFFER_BIT));
-    fHWDrawState.stencil()->invalidate();
+    fHWStencilSettings.invalidate();
 }
 
 void GrGpuGL::onForceRenderTargetFlush() {
@@ -1411,14 +1470,14 @@
     // the read rect is viewport-relative
     GrGLIRect readRect;
     readRect.setRelativeTo(glvp, left, top, width, height);
-    
+
     size_t tightRowBytes = bpp * width;
     if (0 == rowBytes) {
         rowBytes = tightRowBytes;
     }
     size_t readDstRowBytes = tightRowBytes;
     void* readDst = buffer;
-    
+
     // determine if GL can read using the passed rowBytes or if we need
     // a scratch buffer.
     SkAutoSMalloc<32 * sizeof(GrColor)> scratch;
@@ -1470,7 +1529,7 @@
     } else {
         GrAssert(readDst != buffer);        GrAssert(rowBytes != tightRowBytes);
         // copy from readDst to buffer while flipping y
-        const int halfY = height >> 1;
+        // const int halfY = height >> 1;
         const char* src = reinterpret_cast<const char*>(readDst);
         char* dst = reinterpret_cast<char*>(buffer);
         if (!invertY) {
@@ -1495,11 +1554,8 @@
         static_cast<GrGLRenderTarget*>(this->drawState()->getRenderTarget());
     GrAssert(NULL != rt);
 
-    if (fHWDrawState.getRenderTarget() != rt) {
+    if (fHWBoundRenderTarget != rt) {
         GL_CALL(BindFramebuffer(GR_GL_FRAMEBUFFER, rt->renderFBOID()));
-    #if GR_COLLECT_STATS
-        ++fStats.fRenderTargetChngCnt;
-    #endif
     #if GR_DEBUG
         GrGLenum status;
         GL_CALL_RET(status, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
@@ -1507,12 +1563,11 @@
             GrPrintf("GrGpuGL::flushRenderTarget glCheckFramebufferStatus %x\n", status);
         }
     #endif
-        fDirtyFlags.fRenderTargetChanged = true;
-        fHWDrawState.setRenderTarget(rt);
+        fHWBoundRenderTarget = rt;
         const GrGLIRect& vp = rt->getViewport();
-        if (fHWBounds.fViewportRect != vp) {
+        if (fHWViewport != vp) {
             vp.pushToGLViewport(this->glInterface());
-            fHWBounds.fViewportRect = vp;
+            fHWViewport = vp;
         }
     }
     if (NULL == bound || !bound->isEmpty()) {
@@ -1535,6 +1590,7 @@
     #if GR_MAC_BUILD
         #include <AGL/agl.h>
     #elif GR_WIN32_BUILD
+        #include <gl/GL.h>
         void SwapBuf() {
             DWORD procID = GetCurrentProcessId();
             HWND hwnd = GetTopWindow(GetDesktopWindow());
@@ -1550,24 +1606,28 @@
     #endif
 #endif
 
-void GrGpuGL::onGpuDrawIndexed(GrPrimitiveType type,
-                               uint32_t startVertex,
-                               uint32_t startIndex,
-                               uint32_t vertexCount,
-                               uint32_t indexCount) {
-    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+void GrGpuGL::onGpuDraw(const DrawInfo& info) {
+    int extraStartIndexOffset;
+    this->setupGeometry(info, &extraStartIndexOffset);
 
-    GrGLvoid* indices = (GrGLvoid*)(sizeof(uint16_t) * startIndex);
-
-    GrAssert(NULL != fHWGeometryState.fIndexBuffer);
+    GrAssert((size_t)info.primitiveType() < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
     GrAssert(NULL != fHWGeometryState.fVertexBuffer);
 
-    // our setupGeometry better have adjusted this to zero since
-    // DrawElements always draws from the begining of the arrays for idx 0.
-    GrAssert(0 == startVertex);
-
-    GL_CALL(DrawElements(gPrimitiveType2GLMode[type], indexCount,
-                         GR_GL_UNSIGNED_SHORT, indices));
+    if (info.isIndexed()) {
+        GrAssert(NULL != fHWGeometryState.fIndexBuffer);
+        GrGLvoid* indices = (GrGLvoid*)(sizeof(uint16_t) * (info.startIndex() +
+                                                            extraStartIndexOffset));
+        // info.startVertex() was accounted for by setupGeometry.
+        GL_CALL(DrawElements(gPrimitiveType2GLMode[info.primitiveType()],
+                             info.indexCount(),
+                             GR_GL_UNSIGNED_SHORT,
+                             indices));
+    } else {
+        // Pass 0 for parameter first. We have to adjust glVertexAttribPointer() to account for
+        // startVertex in the DrawElements case. So we always rely on setupGeometry to have
+        // accounted for startVertex.
+        GL_CALL(DrawArrays(gPrimitiveType2GLMode[info.primitiveType()], 0, info.vertexCount()));
+    }
 #if SWAP_PER_DRAW
     glFlush();
     #if GR_MAC_BUILD
@@ -1582,33 +1642,79 @@
 #endif
 }
 
-void GrGpuGL::onGpuDrawNonIndexed(GrPrimitiveType type,
-                                  uint32_t startVertex,
-                                  uint32_t vertexCount) {
-    GrAssert((size_t)type < GR_ARRAY_COUNT(gPrimitiveType2GLMode));
+namespace {
 
-    GrAssert(NULL != fHWGeometryState.fVertexBuffer);
+static const uint16_t kOnes16 = static_cast<uint16_t>(~0);
+const GrStencilSettings& winding_nv_path_stencil_settings() {
+    GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
+        kIncClamp_StencilOp,
+        kIncClamp_StencilOp,
+        kAlwaysIfInClip_StencilFunc,
+        kOnes16, kOnes16, kOnes16);
+    return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
+}
+const GrStencilSettings& even_odd_nv_path_stencil_settings() {
+    GR_STATIC_CONST_SAME_STENCIL_STRUCT(gSettings,
+        kInvert_StencilOp,
+        kInvert_StencilOp,
+        kAlwaysIfInClip_StencilFunc,
+        kOnes16, kOnes16, kOnes16);
+    return *GR_CONST_STENCIL_SETTINGS_PTR_FROM_STRUCT_PTR(&gSettings);
+}
+}
 
-    // our setupGeometry better have adjusted this to zero.
-    // DrawElements doesn't take an offset so we always adjus the startVertex.
-    GrAssert(0 == startVertex);
+void GrGpuGL::setStencilPathSettings(const GrPath&,
+                                     SkPath::FillType fill,
+                                     GrStencilSettings* settings) {
+    switch (fill) {
+        case SkPath::kEvenOdd_FillType:
+            *settings = even_odd_nv_path_stencil_settings();
+            return;
+        case SkPath::kWinding_FillType:
+            *settings = winding_nv_path_stencil_settings();
+            return;
+        default:
+            GrCrash("Unexpected path fill.");
+    }
+}
 
-    // pass 0 for parameter first. We have to adjust gl*Pointer() to
-    // account for startVertex in the DrawElements case. So we always
-    // rely on setupGeometry to have accounted for startVertex.
-    GL_CALL(DrawArrays(gPrimitiveType2GLMode[type], 0, vertexCount));
-#if SWAP_PER_DRAW
-    glFlush();
-    #if GR_MAC_BUILD
-        aglSwapBuffers(aglGetCurrentContext());
-        int set_a_break_pt_here = 9;
-        aglSwapBuffers(aglGetCurrentContext());
-    #elif GR_WIN32_BUILD
-        SwapBuf();
-        int set_a_break_pt_here = 9;
-        SwapBuf();
-    #endif
-#endif
+void GrGpuGL::onGpuStencilPath(const GrPath* path, SkPath::FillType fill) {
+    GrAssert(fCaps.pathStencilingSupport());
+
+    GrGLuint id = static_cast<const GrGLPath*>(path)->pathID();
+    GrDrawState* drawState = this->drawState();
+    GrAssert(NULL != drawState->getRenderTarget());
+    if (NULL == drawState->getRenderTarget()->getStencilBuffer()) {
+        return;
+    }
+
+    // Decide how to manipulate the stencil buffer based on the fill rule.
+    // Also, assert that the stencil settings we set in setStencilPathSettings
+    // are present.
+    GrAssert(!fStencilSettings.isTwoSided());
+    GrGLenum fillMode;
+    switch (fill) {
+        case SkPath::kWinding_FillType:
+            fillMode = GR_GL_COUNT_UP;
+            GrAssert(kIncClamp_StencilOp ==
+                     fStencilSettings.passOp(GrStencilSettings::kFront_Face));
+            GrAssert(kIncClamp_StencilOp ==
+                     fStencilSettings.failOp(GrStencilSettings::kFront_Face));
+            break;
+        case SkPath::kEvenOdd_FillType:
+            fillMode = GR_GL_INVERT;
+            GrAssert(kInvert_StencilOp ==
+                     fStencilSettings.passOp(GrStencilSettings::kFront_Face));
+            GrAssert(kInvert_StencilOp ==
+                fStencilSettings.failOp(GrStencilSettings::kFront_Face));
+            break;
+        default:
+            // Only the above two fill rules are allowed.
+            GrCrash("Unexpected path fill.");
+            return; // suppress unused var warning.
+    }
+    GrGLint writeMask = fStencilSettings.writeMask(GrStencilSettings::kFront_Face);
+    GL_CALL(StencilFillPath(id, fillMode, writeMask));
 }
 
 void GrGpuGL::onResolveRenderTarget(GrRenderTarget* target) {
@@ -1622,32 +1728,31 @@
                                 rt->renderFBOID()));
         GL_CALL(BindFramebuffer(GR_GL_DRAW_FRAMEBUFFER,
                                 rt->textureFBOID()));
-    #if GR_COLLECT_STATS
-        ++fStats.fRenderTargetChngCnt;
-    #endif
         // make sure we go through flushRenderTarget() since we've modified
         // the bound DRAW FBO ID.
-        fHWDrawState.setRenderTarget(NULL);
+        fHWBoundRenderTarget = NULL;
         const GrGLIRect& vp = rt->getViewport();
         const GrIRect dirtyRect = rt->getResolveRect();
         GrGLIRect r;
-        r.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop, 
+        r.setRelativeTo(vp, dirtyRect.fLeft, dirtyRect.fTop,
                         dirtyRect.width(), dirtyRect.height());
 
+        GrAutoTRestore<ScissorState> asr;
         if (GrGLCaps::kAppleES_MSFBOType == this->glCaps().msFBOType()) {
             // Apple's extension uses the scissor as the blit bounds.
-            GL_CALL(Enable(GR_GL_SCISSOR_TEST));
-            GL_CALL(Scissor(r.fLeft, r.fBottom,
-                            r.fWidth, r.fHeight));
+            asr.reset(&fScissorState);
+            fScissorState.fEnabled = true;
+            fScissorState.fRect = dirtyRect;
+            this->flushScissor();
             GL_CALL(ResolveMultisampleFramebuffer());
-            fHWBounds.fScissorRect.invalidate();
-            fHWBounds.fScissorEnabled = true;
         } else {
             if (GrGLCaps::kDesktopARB_MSFBOType != this->glCaps().msFBOType()) {
                 // this respects the scissor during the blit, so disable it.
                 GrAssert(GrGLCaps::kDesktopEXT_MSFBOType ==
                          this->glCaps().msFBOType());
-                this->flushScissor(NULL);
+                asr.reset(&fScissorState);
+                fScissorState.fEnabled = false;
+                this->flushScissor();
             }
             int right = r.fLeft + r.fWidth;
             int top = r.fBottom + r.fHeight;
@@ -1659,444 +1764,341 @@
     }
 }
 
-static const GrGLenum grToGLStencilFunc[] = {
-    GR_GL_ALWAYS,           // kAlways_StencilFunc
-    GR_GL_NEVER,            // kNever_StencilFunc
-    GR_GL_GREATER,          // kGreater_StencilFunc
-    GR_GL_GEQUAL,           // kGEqual_StencilFunc
-    GR_GL_LESS,             // kLess_StencilFunc
-    GR_GL_LEQUAL,           // kLEqual_StencilFunc,
-    GR_GL_EQUAL,            // kEqual_StencilFunc,
-    GR_GL_NOTEQUAL,         // kNotEqual_StencilFunc,
-};
-GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilFunc) == kBasicStencilFuncCount);
-GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
-GR_STATIC_ASSERT(1 == kNever_StencilFunc);
-GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
-GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
-GR_STATIC_ASSERT(4 == kLess_StencilFunc);
-GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
-GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
-GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
+namespace {
 
-static const GrGLenum grToGLStencilOp[] = {
-    GR_GL_KEEP,        // kKeep_StencilOp
-    GR_GL_REPLACE,     // kReplace_StencilOp
-    GR_GL_INCR_WRAP,   // kIncWrap_StencilOp
-    GR_GL_INCR,        // kIncClamp_StencilOp
-    GR_GL_DECR_WRAP,   // kDecWrap_StencilOp
-    GR_GL_DECR,        // kDecClamp_StencilOp
-    GR_GL_ZERO,        // kZero_StencilOp
-    GR_GL_INVERT,      // kInvert_StencilOp
-};
-GR_STATIC_ASSERT(GR_ARRAY_COUNT(grToGLStencilOp) == kStencilOpCount);
-GR_STATIC_ASSERT(0 == kKeep_StencilOp);
-GR_STATIC_ASSERT(1 == kReplace_StencilOp);
-GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
-GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
-GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
-GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
-GR_STATIC_ASSERT(6 == kZero_StencilOp);
-GR_STATIC_ASSERT(7 == kInvert_StencilOp);
+GrGLenum gr_to_gl_stencil_func(GrStencilFunc basicFunc) {
+    static const GrGLenum gTable[] = {
+        GR_GL_ALWAYS,           // kAlways_StencilFunc
+        GR_GL_NEVER,            // kNever_StencilFunc
+        GR_GL_GREATER,          // kGreater_StencilFunc
+        GR_GL_GEQUAL,           // kGEqual_StencilFunc
+        GR_GL_LESS,             // kLess_StencilFunc
+        GR_GL_LEQUAL,           // kLEqual_StencilFunc,
+        GR_GL_EQUAL,            // kEqual_StencilFunc,
+        GR_GL_NOTEQUAL,         // kNotEqual_StencilFunc,
+    };
+    GR_STATIC_ASSERT(GR_ARRAY_COUNT(gTable) == kBasicStencilFuncCount);
+    GR_STATIC_ASSERT(0 == kAlways_StencilFunc);
+    GR_STATIC_ASSERT(1 == kNever_StencilFunc);
+    GR_STATIC_ASSERT(2 == kGreater_StencilFunc);
+    GR_STATIC_ASSERT(3 == kGEqual_StencilFunc);
+    GR_STATIC_ASSERT(4 == kLess_StencilFunc);
+    GR_STATIC_ASSERT(5 == kLEqual_StencilFunc);
+    GR_STATIC_ASSERT(6 == kEqual_StencilFunc);
+    GR_STATIC_ASSERT(7 == kNotEqual_StencilFunc);
+    GrAssert((unsigned) basicFunc < kBasicStencilFuncCount);
 
-void GrGpuGL::flushStencil() {
-    const GrDrawState& drawState = this->getDrawState();
+    return gTable[basicFunc];
+}
 
-    const GrStencilSettings* settings = &drawState.getStencil();
+GrGLenum gr_to_gl_stencil_op(GrStencilOp op) {
+    static const GrGLenum gTable[] = {
+        GR_GL_KEEP,        // kKeep_StencilOp
+        GR_GL_REPLACE,     // kReplace_StencilOp
+        GR_GL_INCR_WRAP,   // kIncWrap_StencilOp
+        GR_GL_INCR,        // kIncClamp_StencilOp
+        GR_GL_DECR_WRAP,   // kDecWrap_StencilOp
+        GR_GL_DECR,        // kDecClamp_StencilOp
+        GR_GL_ZERO,        // kZero_StencilOp
+        GR_GL_INVERT,      // kInvert_StencilOp
+    };
+    GR_STATIC_ASSERT(GR_ARRAY_COUNT(gTable) == kStencilOpCount);
+    GR_STATIC_ASSERT(0 == kKeep_StencilOp);
+    GR_STATIC_ASSERT(1 == kReplace_StencilOp);
+    GR_STATIC_ASSERT(2 == kIncWrap_StencilOp);
+    GR_STATIC_ASSERT(3 == kIncClamp_StencilOp);
+    GR_STATIC_ASSERT(4 == kDecWrap_StencilOp);
+    GR_STATIC_ASSERT(5 == kDecClamp_StencilOp);
+    GR_STATIC_ASSERT(6 == kZero_StencilOp);
+    GR_STATIC_ASSERT(7 == kInvert_StencilOp);
+    GrAssert((unsigned) op < kStencilOpCount);
+    return gTable[op];
+}
 
-    // use stencil for clipping if clipping is enabled and the clip
-    // has been written into the stencil.
-    bool stencilClip = fClipInStencil && drawState.isClipState();
-    bool drawClipToStencil =
-        drawState.isStateFlagEnabled(kModifyStencilClip_StateBit);
-    bool stencilChange = (fHWDrawState.getStencil() != *settings) ||
-                         (fHWStencilClip != stencilClip) ||
-                         (fHWDrawState.isStateFlagEnabled(kModifyStencilClip_StateBit) !=
-                          drawClipToStencil);
+void set_gl_stencil(const GrGLInterface* gl,
+                    const GrStencilSettings& settings,
+                    GrGLenum glFace,
+                    GrStencilSettings::Face grFace) {
+    GrGLenum glFunc = gr_to_gl_stencil_func(settings.func(grFace));
+    GrGLenum glFailOp = gr_to_gl_stencil_op(settings.failOp(grFace));
+    GrGLenum glPassOp = gr_to_gl_stencil_op(settings.passOp(grFace));
 
-    if (stencilChange) {
+    GrGLint ref = settings.funcRef(grFace);
+    GrGLint mask = settings.funcMask(grFace);
+    GrGLint writeMask = settings.writeMask(grFace);
 
-        // we can't simultaneously perform stencil-clipping and 
-        // modify the stencil clip
-        GrAssert(!stencilClip || !drawClipToStencil);
+    if (GR_GL_FRONT_AND_BACK == glFace) {
+        // we call the combined func just in case separate stencil is not
+        // supported.
+        GR_GL_CALL(gl, StencilFunc(glFunc, ref, mask));
+        GR_GL_CALL(gl, StencilMask(writeMask));
+        GR_GL_CALL(gl, StencilOp(glFailOp, glPassOp, glPassOp));
+    } else {
+        GR_GL_CALL(gl, StencilFuncSeparate(glFace, glFunc, ref, mask));
+        GR_GL_CALL(gl, StencilMaskSeparate(glFace, writeMask));
+        GR_GL_CALL(gl, StencilOpSeparate(glFace, glFailOp, glPassOp, glPassOp));
+    }
+}
+}
 
-        if (settings->isDisabled()) {
-            if (stencilClip) {
-                settings = GetClipStencilSettings();
+void GrGpuGL::flushStencil(DrawType type) {
+    if (kStencilPath_DrawType == type) {
+        GrAssert(!fStencilSettings.isTwoSided());
+        // Just the func, ref, and mask is set here. The op and write mask are params to the call
+        // that draws the path to the SB (glStencilFillPath)
+        GrGLenum func =
+            gr_to_gl_stencil_func(fStencilSettings.func(GrStencilSettings::kFront_Face));
+        GL_CALL(PathStencilFunc(func,
+                                fStencilSettings.funcRef(GrStencilSettings::kFront_Face),
+                                fStencilSettings.funcMask(GrStencilSettings::kFront_Face)));
+    } else if (fHWStencilSettings != fStencilSettings) {
+        if (fStencilSettings.isDisabled()) {
+            if (kNo_TriState != fHWStencilTestEnabled) {
+                GL_CALL(Disable(GR_GL_STENCIL_TEST));
+                fHWStencilTestEnabled = kNo_TriState;
             }
-        }
-
-        if (settings->isDisabled()) {
-            GL_CALL(Disable(GR_GL_STENCIL_TEST));
         } else {
-            GL_CALL(Enable(GR_GL_STENCIL_TEST));
-    #if GR_DEBUG
-            if (!this->getCaps().fStencilWrapOpsSupport) {
-                GrAssert(settings->frontPassOp() != kIncWrap_StencilOp);
-                GrAssert(settings->frontPassOp() != kDecWrap_StencilOp);
-                GrAssert(settings->frontFailOp() != kIncWrap_StencilOp);
-                GrAssert(settings->backFailOp() != kDecWrap_StencilOp);
-                GrAssert(settings->backPassOp() != kIncWrap_StencilOp);
-                GrAssert(settings->backPassOp() != kDecWrap_StencilOp);
-                GrAssert(settings->backFailOp() != kIncWrap_StencilOp);
-                GrAssert(settings->frontFailOp() != kDecWrap_StencilOp);
-            }
-    #endif
-            int stencilBits = 0;
-            GrStencilBuffer* stencilBuffer =
-                drawState.getRenderTarget()->getStencilBuffer();
-            if (NULL != stencilBuffer) {
-                stencilBits = stencilBuffer->bits();
-            }
-            // TODO: dynamically attach a stencil buffer
-            GrAssert(stencilBits || settings->isDisabled());
-
-            GrGLuint clipStencilMask = 0;
-            GrGLuint userStencilMask = ~0;
-            if (stencilBits > 0) {
-                clipStencilMask =  1 << (stencilBits - 1);
-                userStencilMask = clipStencilMask - 1;
-            }
-
-            unsigned int frontRef  = settings->frontFuncRef();
-            unsigned int frontMask = settings->frontFuncMask();
-            unsigned int frontWriteMask = settings->frontWriteMask();
-            GrGLenum frontFunc;
-
-            if (drawClipToStencil) {
-                GrAssert(settings->frontFunc() < kBasicStencilFuncCount);
-                frontFunc = grToGLStencilFunc[settings->frontFunc()];
-            } else {
-                frontFunc = grToGLStencilFunc[ConvertStencilFunc(
-                        stencilClip, settings->frontFunc())];
-
-                ConvertStencilFuncAndMask(settings->frontFunc(),
-                                          stencilClip,
-                                          clipStencilMask,
-                                          userStencilMask,
-                                          &frontRef,
-                                          &frontMask);
-                frontWriteMask &= userStencilMask;
-            }
-            GrAssert((size_t)
-                settings->frontFailOp() < GR_ARRAY_COUNT(grToGLStencilOp));
-            GrAssert((size_t)
-                settings->frontPassOp() < GR_ARRAY_COUNT(grToGLStencilOp));
-            GrAssert((size_t)
-                settings->backFailOp() < GR_ARRAY_COUNT(grToGLStencilOp));
-            GrAssert((size_t)
-                settings->backPassOp() < GR_ARRAY_COUNT(grToGLStencilOp));
-            if (this->getCaps().fTwoSidedStencilSupport) {
-                GrGLenum backFunc;
-
-                unsigned int backRef  = settings->backFuncRef();
-                unsigned int backMask = settings->backFuncMask();
-                unsigned int backWriteMask = settings->backWriteMask();
-
-
-                if (drawClipToStencil) {
-                    GrAssert(settings->backFunc() < kBasicStencilFuncCount);
-                    backFunc = grToGLStencilFunc[settings->backFunc()];
-                } else {
-                    backFunc = grToGLStencilFunc[ConvertStencilFunc(
-                        stencilClip, settings->backFunc())];
-                    ConvertStencilFuncAndMask(settings->backFunc(),
-                                              stencilClip,
-                                              clipStencilMask,
-                                              userStencilMask,
-                                              &backRef,
-                                              &backMask);
-                    backWriteMask &= userStencilMask;
-                }
-
-                GL_CALL(StencilFuncSeparate(GR_GL_FRONT, frontFunc,
-                                            frontRef, frontMask));
-                GL_CALL(StencilMaskSeparate(GR_GL_FRONT, frontWriteMask));
-                GL_CALL(StencilFuncSeparate(GR_GL_BACK, backFunc,
-                                            backRef, backMask));
-                GL_CALL(StencilMaskSeparate(GR_GL_BACK, backWriteMask));
-                GL_CALL(StencilOpSeparate(GR_GL_FRONT,
-                                    grToGLStencilOp[settings->frontFailOp()],
-                                    grToGLStencilOp[settings->frontPassOp()],
-                                    grToGLStencilOp[settings->frontPassOp()]));
-
-                GL_CALL(StencilOpSeparate(GR_GL_BACK,
-                                    grToGLStencilOp[settings->backFailOp()],
-                                    grToGLStencilOp[settings->backPassOp()],
-                                    grToGLStencilOp[settings->backPassOp()]));
-            } else {
-                GL_CALL(StencilFunc(frontFunc, frontRef, frontMask));
-                GL_CALL(StencilMask(frontWriteMask));
-                GL_CALL(StencilOp(grToGLStencilOp[settings->frontFailOp()],
-                                grToGLStencilOp[settings->frontPassOp()],
-                                grToGLStencilOp[settings->frontPassOp()]));
+            if (kYes_TriState != fHWStencilTestEnabled) {
+                GL_CALL(Enable(GR_GL_STENCIL_TEST));
+                fHWStencilTestEnabled = kYes_TriState;
             }
         }
-        *fHWDrawState.stencil() = *settings;
-        fHWStencilClip = stencilClip;
+        if (!fStencilSettings.isDisabled()) {
+            if (this->getCaps().twoSidedStencilSupport()) {
+                set_gl_stencil(this->glInterface(),
+                               fStencilSettings,
+                               GR_GL_FRONT,
+                               GrStencilSettings::kFront_Face);
+                set_gl_stencil(this->glInterface(),
+                               fStencilSettings,
+                               GR_GL_BACK,
+                               GrStencilSettings::kBack_Face);
+            } else {
+                set_gl_stencil(this->glInterface(),
+                               fStencilSettings,
+                               GR_GL_FRONT_AND_BACK,
+                               GrStencilSettings::kFront_Face);
+            }
+        }
+        fHWStencilSettings = fStencilSettings;
     }
 }
 
-void GrGpuGL::flushAAState(GrPrimitiveType type) {
+void GrGpuGL::flushAAState(DrawType type) {
     const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
     if (kDesktop_GrGLBinding == this->glBinding()) {
         // ES doesn't support toggling GL_MULTISAMPLE and doesn't have
         // smooth lines.
-
         // we prefer smooth lines over multisampled lines
-        // msaa should be disabled if drawing smooth lines.
-        if (GrIsPrimTypeLines(type)) {
-            bool smooth = this->willUseHWAALines();
-            if (!fHWAAState.fSmoothLineEnabled && smooth) {
-                GL_CALL(Enable(GR_GL_LINE_SMOOTH));
-                fHWAAState.fSmoothLineEnabled = true;
-            } else if (fHWAAState.fSmoothLineEnabled && !smooth) {
-                GL_CALL(Disable(GR_GL_LINE_SMOOTH));
-                fHWAAState.fSmoothLineEnabled = false;
-            }
-            if (rt->isMultisampled() && 
-                fHWAAState.fMSAAEnabled) {
-                GL_CALL(Disable(GR_GL_MULTISAMPLE));
-                fHWAAState.fMSAAEnabled = false;
-            }
-        } else if (rt->isMultisampled() &&
-                   this->getDrawState().isHWAntialiasState() !=
-                   fHWAAState.fMSAAEnabled) {
-            if (fHWAAState.fMSAAEnabled) {
-                GL_CALL(Disable(GR_GL_MULTISAMPLE));
-                fHWAAState.fMSAAEnabled = false;
+        bool smoothLines = false;
+
+        if (kDrawLines_DrawType == type) {
+            smoothLines = this->willUseHWAALines();
+            if (smoothLines) {
+                if (kYes_TriState != fHWAAState.fSmoothLineEnabled) {
+                    GL_CALL(Enable(GR_GL_LINE_SMOOTH));
+                    fHWAAState.fSmoothLineEnabled = kYes_TriState;
+                    // must disable msaa to use line smoothing
+                    if (rt->isMultisampled() &&
+                        kNo_TriState != fHWAAState.fMSAAEnabled) {
+                        GL_CALL(Disable(GR_GL_MULTISAMPLE));
+                        fHWAAState.fMSAAEnabled = kNo_TriState;
+                    }
+                }
             } else {
-                GL_CALL(Enable(GR_GL_MULTISAMPLE));
-                fHWAAState.fMSAAEnabled = true;
+                if (kNo_TriState != fHWAAState.fSmoothLineEnabled) {
+                    GL_CALL(Disable(GR_GL_LINE_SMOOTH));
+                    fHWAAState.fSmoothLineEnabled = kNo_TriState;
+                }
+            }
+        }
+        if (!smoothLines && rt->isMultisampled()) {
+            // FIXME: GL_NV_pr doesn't seem to like MSAA disabled. The paths
+            // convex hulls of each segment appear to get filled.
+            bool enableMSAA = kStencilPath_DrawType == type ||
+                              this->getDrawState().isHWAntialiasState();
+            if (enableMSAA) {
+                if (kYes_TriState != fHWAAState.fMSAAEnabled) {
+                    GL_CALL(Enable(GR_GL_MULTISAMPLE));
+                    fHWAAState.fMSAAEnabled = kYes_TriState;
+                }
+            } else {
+                if (kNo_TriState != fHWAAState.fMSAAEnabled) {
+                    GL_CALL(Disable(GR_GL_MULTISAMPLE));
+                    fHWAAState.fMSAAEnabled = kNo_TriState;
+                }
             }
         }
     }
 }
 
-void GrGpuGL::flushBlend(GrPrimitiveType type,
+void GrGpuGL::flushBlend(bool isLines,
                          GrBlendCoeff srcCoeff,
                          GrBlendCoeff dstCoeff) {
-    if (GrIsPrimTypeLines(type) && this->willUseHWAALines()) {
-        if (fHWBlendDisabled) {
+    if (isLines && this->willUseHWAALines()) {
+        if (kYes_TriState != fHWBlendState.fEnabled) {
             GL_CALL(Enable(GR_GL_BLEND));
-            fHWBlendDisabled = false;
+            fHWBlendState.fEnabled = kYes_TriState;
         }
-        if (kSA_BlendCoeff != fHWDrawState.getSrcBlendCoeff() ||
-            kISA_BlendCoeff != fHWDrawState.getDstBlendCoeff()) {
-            GL_CALL(BlendFunc(gXfermodeCoeff2Blend[kSA_BlendCoeff],
-                              gXfermodeCoeff2Blend[kISA_BlendCoeff]));
-            fHWDrawState.setBlendFunc(kSA_BlendCoeff, kISA_BlendCoeff);
+        if (kSA_GrBlendCoeff != fHWBlendState.fSrcCoeff ||
+            kISA_GrBlendCoeff != fHWBlendState.fDstCoeff) {
+            GL_CALL(BlendFunc(gXfermodeCoeff2Blend[kSA_GrBlendCoeff],
+                              gXfermodeCoeff2Blend[kISA_GrBlendCoeff]));
+            fHWBlendState.fSrcCoeff = kSA_GrBlendCoeff;
+            fHWBlendState.fDstCoeff = kISA_GrBlendCoeff;
         }
     } else {
         // any optimization to disable blending should
         // have already been applied and tweaked the coeffs
         // to (1, 0).
-        bool blendOff = kOne_BlendCoeff == srcCoeff &&
-                        kZero_BlendCoeff == dstCoeff;
-        if (fHWBlendDisabled != blendOff) {
-            if (blendOff) {
+        bool blendOff = kOne_GrBlendCoeff == srcCoeff &&
+                        kZero_GrBlendCoeff == dstCoeff;
+        if (blendOff) {
+            if (kNo_TriState != fHWBlendState.fEnabled) {
                 GL_CALL(Disable(GR_GL_BLEND));
-            } else {
-                GL_CALL(Enable(GR_GL_BLEND));
+                fHWBlendState.fEnabled = kNo_TriState;
             }
-            fHWBlendDisabled = blendOff;
-        }
-        if (!blendOff) {
-            if (fHWDrawState.getSrcBlendCoeff() != srcCoeff ||
-                fHWDrawState.getDstBlendCoeff() != dstCoeff) {
+        } 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]));
-                fHWDrawState.setBlendFunc(srcCoeff, dstCoeff);
+                fHWBlendState.fSrcCoeff = srcCoeff;
+                fHWBlendState.fDstCoeff = dstCoeff;
             }
-            GrColor blendConst = fCurrDrawState.getBlendConstant();
+            GrColor blendConst = this->getDrawState().getBlendConstant();
             if ((BlendCoeffReferencesConstant(srcCoeff) ||
                  BlendCoeffReferencesConstant(dstCoeff)) &&
-                fHWDrawState.getBlendConstant() != blendConst) {
-
-                float c[] = {
-                    GrColorUnpackR(blendConst) / 255.f,
-                    GrColorUnpackG(blendConst) / 255.f,
-                    GrColorUnpackB(blendConst) / 255.f,
-                    GrColorUnpackA(blendConst) / 255.f
-                };
+                (!fHWBlendState.fConstColorValid ||
+                 fHWBlendState.fConstColor != blendConst)) {
+                GrGLfloat c[4];
+                GrColorToRGBAFloat(blendConst, c);
                 GL_CALL(BlendColor(c[0], c[1], c[2], c[3]));
-                fHWDrawState.setBlendConstant(blendConst);
+                fHWBlendState.fConstColor = blendConst;
+                fHWBlendState.fConstColorValid = true;
             }
         }
     }
 }
-
 namespace {
 
-unsigned gr_to_gl_filter(GrSamplerState::Filter filter) {
-    switch (filter) {
-        case GrSamplerState::kBilinear_Filter:
-        case GrSamplerState::k4x4Downsample_Filter:
-            return GR_GL_LINEAR;
-        case GrSamplerState::kNearest_Filter:
-        case GrSamplerState::kConvolution_Filter:
-        case GrSamplerState::kErode_Filter:
-        case GrSamplerState::kDilate_Filter:
-            return GR_GL_NEAREST;
-        default:
-            GrAssert(!"Unknown filter type");
-            return GR_GL_LINEAR;
-    }
+inline void set_tex_swizzle(GrGLenum swizzle[4], const GrGLInterface* gl) {
+    GR_GL_CALL(gl, TexParameteriv(GR_GL_TEXTURE_2D,
+                                  GR_GL_TEXTURE_SWIZZLE_RGBA,
+                                  reinterpret_cast<const GrGLint*>(swizzle)));
 }
 
-const GrGLenum* get_swizzle(GrPixelConfig config,
-                            const GrSamplerState& sampler) {
-    if (GrPixelConfigIsAlphaOnly(config)) {
-        static const GrGLenum gAlphaSmear[] = { GR_GL_ALPHA, GR_GL_ALPHA,
-                                                GR_GL_ALPHA, GR_GL_ALPHA };
-        return gAlphaSmear;
-    } else if (sampler.swapsRAndB()) {
-        static const GrGLenum gRedBlueSwap[] = { GR_GL_BLUE, GR_GL_GREEN,
-                                                 GR_GL_RED,  GR_GL_ALPHA };
-        return gRedBlueSwap;
-    } else {
-        static const GrGLenum gStraight[] = { GR_GL_RED, GR_GL_GREEN,
-                                              GR_GL_BLUE,  GR_GL_ALPHA };
-        return gStraight;
-    }
+inline GrGLenum tile_to_gl_wrap(SkShader::TileMode tm) {
+    static const GrGLenum gWrapModes[] = {
+        GR_GL_CLAMP_TO_EDGE,
+        GR_GL_REPEAT,
+        GR_GL_MIRRORED_REPEAT
+    };
+    GrAssert((unsigned) tm <= SK_ARRAY_COUNT(gWrapModes));
+    GR_STATIC_ASSERT(0 == SkShader::kClamp_TileMode);
+    GR_STATIC_ASSERT(1 == SkShader::kRepeat_TileMode);
+    GR_STATIC_ASSERT(2 == SkShader::kMirror_TileMode);
+    return gWrapModes[tm];
 }
 
-void set_tex_swizzle(GrGLenum swizzle[4], const GrGLInterface* gl) {
-    // should add texparameteri to interface to make 1 instead of 4 calls here
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
-                                 GR_GL_TEXTURE_SWIZZLE_R,
-                                 swizzle[0]));
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
-                                 GR_GL_TEXTURE_SWIZZLE_G,
-                                 swizzle[1]));
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
-                                 GR_GL_TEXTURE_SWIZZLE_B,
-                                 swizzle[2]));
-    GR_GL_CALL(gl, TexParameteri(GR_GL_TEXTURE_2D,
-                                 GR_GL_TEXTURE_SWIZZLE_A,
-                                 swizzle[3]));
-}
 }
 
-bool GrGpuGL::flushGLStateCommon(GrPrimitiveType type) {
+void GrGpuGL::bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture) {
+    GrAssert(NULL != texture);
 
-    GrDrawState* drawState = this->drawState();
-    // GrGpu::setupClipAndFlushState should have already checked this
-    // and bailed if not true.
-    GrAssert(NULL != drawState->getRenderTarget());
-
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        // bind texture and set sampler state
-        if (this->isStageEnabled(s)) {
-            GrGLTexture* nextTexture = 
-                static_cast<GrGLTexture*>(drawState->getTexture(s));
-
-            // true for now, but maybe not with GrEffect.
-            GrAssert(NULL != nextTexture);
-            // if we created a rt/tex and rendered to it without using a
-            // texture and now we're texuring from the rt it will still be
-            // the last bound texture, but it needs resolving. So keep this
-            // out of the "last != next" check.
-            GrGLRenderTarget* texRT = 
-                static_cast<GrGLRenderTarget*>(nextTexture->asRenderTarget());
-            if (NULL != texRT) {
-                this->onResolveRenderTarget(texRT);
-            }
-
-            if (fHWDrawState.getTexture(s) != nextTexture) {
-                setTextureUnit(s);
-                GL_CALL(BindTexture(GR_GL_TEXTURE_2D, nextTexture->textureID()));
-            #if GR_COLLECT_STATS
-                ++fStats.fTextureChngCnt;
-            #endif
-                //GrPrintf("---- bindtexture %d\n", nextTexture->textureID());
-                fHWDrawState.setTexture(s, nextTexture);
-                // The texture matrix has to compensate for texture width/height
-                // and NPOT-embedded-in-POT
-                fDirtyFlags.fTextureChangedMask |= (1 << s);
-            }
-
-            const GrSamplerState& sampler = drawState->getSampler(s);
-            ResetTimestamp timestamp;
-            const GrGLTexture::TexParams& oldTexParams =
-                                    nextTexture->getCachedTexParams(&timestamp);
-            bool setAll = timestamp < this->getResetTimestamp();
-            GrGLTexture::TexParams newTexParams;
-
-            newTexParams.fFilter = gr_to_gl_filter(sampler.getFilter());
-
-            const GrGLenum* wraps =  GrGLTexture::WrapMode2GLWrap();
-            newTexParams.fWrapS = wraps[sampler.getWrapX()];
-            newTexParams.fWrapT = wraps[sampler.getWrapY()];
-            memcpy(newTexParams.fSwizzleRGBA,
-                   get_swizzle(nextTexture->config(), sampler),
-                   sizeof(newTexParams.fSwizzleRGBA));
-            if (setAll || newTexParams.fFilter != oldTexParams.fFilter) {
-                setTextureUnit(s);
-                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
-                                        GR_GL_TEXTURE_MAG_FILTER,
-                                        newTexParams.fFilter));
-                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
-                                        GR_GL_TEXTURE_MIN_FILTER,
-                                        newTexParams.fFilter));
-            }
-            if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) {
-                setTextureUnit(s);
-                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
-                                        GR_GL_TEXTURE_WRAP_S,
-                                        newTexParams.fWrapS));
-            }
-            if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) {
-                setTextureUnit(s);
-                GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
-                                        GR_GL_TEXTURE_WRAP_T,
-                                        newTexParams.fWrapT));
-            }
-            if (this->glCaps().textureSwizzleSupport() &&
-                (setAll ||
-                 memcmp(newTexParams.fSwizzleRGBA,
-                        oldTexParams.fSwizzleRGBA,
-                        sizeof(newTexParams.fSwizzleRGBA)))) {
-                setTextureUnit(s);
-                set_tex_swizzle(newTexParams.fSwizzleRGBA,
-                                this->glInterface());
-            }
-            nextTexture->setCachedTexParams(newTexParams,
-                                            this->getResetTimestamp());
-        }
+    // If we created a rt/tex and rendered to it without using a texture and now we're texturing
+    // from the rt it will still be the last bound texture, but it needs resolving. So keep this
+    // out of the "last != next" check.
+    GrGLRenderTarget* texRT =  static_cast<GrGLRenderTarget*>(texture->asRenderTarget());
+    if (NULL != texRT) {
+        this->onResolveRenderTarget(texRT);
     }
 
-    GrIRect* rect = NULL;
-    GrIRect clipBounds;
-    if (drawState->isClipState() &&
-        fClip.hasConservativeBounds()) {
-        fClip.getConservativeBounds().roundOut(&clipBounds);
-        rect = &clipBounds;
+    if (fHWBoundTextures[unitIdx] != texture) {
+        this->setTextureUnit(unitIdx);
+        GL_CALL(BindTexture(GR_GL_TEXTURE_2D, texture->textureID()));
+        fHWBoundTextures[unitIdx] = texture;
     }
-    this->flushRenderTarget(rect);
-    this->flushAAState(type);
-    
-    if (drawState->isDitherState() != fHWDrawState.isDitherState()) {
-        if (drawState->isDitherState()) {
+
+    ResetTimestamp timestamp;
+    const GrGLTexture::TexParams& oldTexParams = texture->getCachedTexParams(&timestamp);
+    bool setAll = timestamp < this->getResetTimestamp();
+    GrGLTexture::TexParams newTexParams;
+
+    newTexParams.fFilter = params.isBilerp() ? GR_GL_LINEAR : GR_GL_NEAREST;
+
+    newTexParams.fWrapS = tile_to_gl_wrap(params.getTileModeX());
+    newTexParams.fWrapT = tile_to_gl_wrap(params.getTileModeY());
+    memcpy(newTexParams.fSwizzleRGBA,
+           GrGLShaderBuilder::GetTexParamSwizzle(texture->config(), this->glCaps()),
+           sizeof(newTexParams.fSwizzleRGBA));
+    if (setAll || newTexParams.fFilter != oldTexParams.fFilter) {
+        this->setTextureUnit(unitIdx);
+        GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                              GR_GL_TEXTURE_MAG_FILTER,
+                              newTexParams.fFilter));
+        GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                              GR_GL_TEXTURE_MIN_FILTER,
+                              newTexParams.fFilter));
+    }
+    if (setAll || newTexParams.fWrapS != oldTexParams.fWrapS) {
+        this->setTextureUnit(unitIdx);
+        GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                              GR_GL_TEXTURE_WRAP_S,
+                              newTexParams.fWrapS));
+    }
+    if (setAll || newTexParams.fWrapT != oldTexParams.fWrapT) {
+        this->setTextureUnit(unitIdx);
+        GL_CALL(TexParameteri(GR_GL_TEXTURE_2D,
+                              GR_GL_TEXTURE_WRAP_T,
+                              newTexParams.fWrapT));
+    }
+    if (this->glCaps().textureSwizzleSupport() &&
+        (setAll || memcmp(newTexParams.fSwizzleRGBA,
+                          oldTexParams.fSwizzleRGBA,
+                          sizeof(newTexParams.fSwizzleRGBA)))) {
+        this->setTextureUnit(unitIdx);
+        set_tex_swizzle(newTexParams.fSwizzleRGBA,
+                        this->glInterface());
+    }
+    texture->setCachedTexParams(newTexParams, this->getResetTimestamp());
+}
+
+void GrGpuGL::flushMiscFixedFunctionState() {
+
+    const GrDrawState& drawState = this->getDrawState();
+
+    if (drawState.isDitherState()) {
+        if (kYes_TriState != fHWDitherEnabled) {
             GL_CALL(Enable(GR_GL_DITHER));
-        } else {
+            fHWDitherEnabled = kYes_TriState;
+        }
+    } else {
+        if (kNo_TriState != fHWDitherEnabled) {
             GL_CALL(Disable(GR_GL_DITHER));
+            fHWDitherEnabled = kNo_TriState;
         }
     }
 
-    if (drawState->isColorWriteDisabled() !=
-        fHWDrawState.isColorWriteDisabled()) {
-        GrGLenum mask;
-        if (drawState->isColorWriteDisabled()) {
-            mask = GR_GL_FALSE;
-        } else {
-            mask = GR_GL_TRUE;
+    if (drawState.isColorWriteDisabled()) {
+        if (kNo_TriState != fHWWriteToColor) {
+            GL_CALL(ColorMask(GR_GL_FALSE, GR_GL_FALSE,
+                              GR_GL_FALSE, GR_GL_FALSE));
+            fHWWriteToColor = kNo_TriState;
         }
-        GL_CALL(ColorMask(mask, mask, mask, mask));
+    } else {
+        if (kYes_TriState != fHWWriteToColor) {
+            GL_CALL(ColorMask(GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE, GR_GL_TRUE));
+            fHWWriteToColor = kYes_TriState;
+        }
     }
 
-    if (fHWDrawState.getDrawFace() != drawState->getDrawFace()) {
-        switch (fCurrDrawState.getDrawFace()) {
+    if (fHWDrawFace != drawState.getDrawFace()) {
+        switch (this->getDrawState().getDrawFace()) {
             case GrDrawState::kCCW_DrawFace:
                 GL_CALL(Enable(GR_GL_CULL_FACE));
                 GL_CALL(CullFace(GR_GL_BACK));
@@ -2111,27 +2113,8 @@
             default:
                 GrCrash("Unknown draw face.");
         }
-        fHWDrawState.setDrawFace(drawState->getDrawFace());
+        fHWDrawFace = drawState.getDrawFace();
     }
-
-#if GR_DEBUG
-    // check for circular rendering
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrAssert(!this->isStageEnabled(s) ||
-                 NULL == drawState->getRenderTarget() ||
-                 NULL == drawState->getTexture(s) ||
-                 drawState->getTexture(s)->asRenderTarget() !=
-                    drawState->getRenderTarget());
-    }
-#endif
-
-    this->flushStencil();
-
-    // This copy must happen after flushStencil() is called. flushStencil()
-    // relies on detecting when the kModifyStencilClip_StateBit state has
-    // changed since the last draw.
-    fHWDrawState.copyStateFlags(*drawState);
-    return true;
 }
 
 void GrGpuGL::notifyVertexBufferBind(const GrGLVertexBuffer* buffer) {
@@ -2162,24 +2145,16 @@
 
 void GrGpuGL::notifyRenderTargetDelete(GrRenderTarget* renderTarget) {
     GrAssert(NULL != renderTarget);
-    GrDrawState* drawState = this->drawState();
-    if (drawState->getRenderTarget() == renderTarget) {
-        drawState->setRenderTarget(NULL);
-    }
-    if (fHWDrawState.getRenderTarget() == renderTarget) {
-        fHWDrawState.setRenderTarget(NULL);
+    if (fHWBoundRenderTarget == renderTarget) {
+        fHWBoundRenderTarget = NULL;
     }
 }
 
 void GrGpuGL::notifyTextureDelete(GrGLTexture* texture) {
     for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        GrDrawState* drawState = this->drawState();
-        if (drawState->getTexture(s) == texture) {
-            fCurrDrawState.setTexture(s, NULL);
-        }
-        if (fHWDrawState.getTexture(s) == texture) {
+        if (fHWBoundTextures[s] == texture) {
             // deleting bound texture does implied bind to 0
-            fHWDrawState.setTexture(s, NULL);
+            fHWBoundTextures[s] = NULL;
        }
     }
 }
@@ -2201,8 +2176,7 @@
     }
 
     switch (config) {
-        case kRGBA_8888_PM_GrPixelConfig:
-        case kRGBA_8888_UPM_GrPixelConfig:
+        case kRGBA_8888_GrPixelConfig:
             *internalFormat = GR_GL_RGBA;
             *externalFormat = GR_GL_RGBA;
             if (getSizedInternalFormat) {
@@ -2212,8 +2186,7 @@
             }
             *externalType = GR_GL_UNSIGNED_BYTE;
             break;
-        case kBGRA_8888_PM_GrPixelConfig:
-        case kBGRA_8888_UPM_GrPixelConfig:
+        case kBGRA_8888_GrPixelConfig:
             if (!this->glCaps().bgraFormatSupport()) {
                 return false;
             }
@@ -2258,7 +2231,7 @@
             *externalType = GR_GL_UNSIGNED_SHORT_4_4_4_4;
             break;
         case kIndex_8_GrPixelConfig:
-            if (this->getCaps().f8BitPaletteSupport) {
+            if (this->getCaps().eightBitPaletteSupport()) {
                 *internalFormat = GR_GL_PALETTE8_RGBA8;
                 // glCompressedTexImage doesn't take external params
                 *externalFormat = GR_GL_PALETTE8_RGBA8;
@@ -2271,14 +2244,25 @@
             }
             break;
         case kAlpha_8_GrPixelConfig:
-            *internalFormat = GR_GL_ALPHA;
-            *externalFormat = GR_GL_ALPHA;
-            if (getSizedInternalFormat) {
-                *internalFormat = GR_GL_ALPHA8;
+            if (this->glCaps().textureRedSupport()) {
+                *internalFormat = GR_GL_RED;
+                *externalFormat = GR_GL_RED;
+                if (getSizedInternalFormat) {
+                    *internalFormat = GR_GL_R8;
+                } else {
+                    *internalFormat = GR_GL_RED;
+                }
+                *externalType = GR_GL_UNSIGNED_BYTE;
             } else {
                 *internalFormat = GR_GL_ALPHA;
+                *externalFormat = GR_GL_ALPHA;
+                if (getSizedInternalFormat) {
+                    *internalFormat = GR_GL_ALPHA8;
+                } else {
+                    *internalFormat = GR_GL_ALPHA;
+                }
+                *externalType = GR_GL_UNSIGNED_BYTE;
             }
-            *externalType = GR_GL_UNSIGNED_BYTE;
             break;
         default:
             return false;
@@ -2288,23 +2272,19 @@
 
 void GrGpuGL::setTextureUnit(int unit) {
     GrAssert(unit >= 0 && unit < GrDrawState::kNumStages);
-    if (fActiveTextureUnitIdx != unit) {
+    if (fHWActiveTextureUnitIdx != unit) {
         GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + unit));
-        fActiveTextureUnitIdx = unit;
+        fHWActiveTextureUnitIdx = unit;
     }
 }
 
 void GrGpuGL::setSpareTextureUnit() {
-    if (fActiveTextureUnitIdx != (GR_GL_TEXTURE0 + SPARE_TEX_UNIT)) {
+    if (fHWActiveTextureUnitIdx != (GR_GL_TEXTURE0 + SPARE_TEX_UNIT)) {
         GL_CALL(ActiveTexture(GR_GL_TEXTURE0 + SPARE_TEX_UNIT));
-        fActiveTextureUnitIdx = SPARE_TEX_UNIT;
+        fHWActiveTextureUnitIdx = SPARE_TEX_UNIT;
     }
 }
 
-void GrGpuGL::resetDirtyFlags() {
-    Gr_bzero(&fDirtyFlags, sizeof(fDirtyFlags));
-}
-
 void GrGpuGL::setBuffers(bool indexed,
                          int* extraVertexOffset,
                          int* extraIndexOffset) {
@@ -2366,11 +2346,3 @@
         }
     }
 }
-
-int GrGpuGL::getMaxEdges() const {
-    // FIXME:  This is a pessimistic estimate based on how many other things
-    // want to add uniforms.  This should be centralized somewhere.
-    return GR_CT_MIN(this->glCaps().maxFragmentUniformVectors() - 8,
-                     GrDrawState::kMaxEdges);
-}
-
diff --git a/src/gpu/gl/GrGpuGL.h b/src/gpu/gl/GrGpuGL.h
index 398a2fc..3b8c16f 100644
--- a/src/gpu/gl/GrGpuGL.h
+++ b/src/gpu/gl/GrGpuGL.h
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -11,20 +10,24 @@
 #ifndef GrGpuGL_DEFINED
 #define GrGpuGL_DEFINED
 
-#include "../GrDrawState.h"
-#include "../GrGpu.h"
+#include "GrBinHashKey.h"
+#include "GrDrawState.h"
+#include "GrGpu.h"
 #include "GrGLContextInfo.h"
 #include "GrGLIndexBuffer.h"
 #include "GrGLIRect.h"
+#include "GrGLProgram.h"
 #include "GrGLStencilBuffer.h"
 #include "GrGLTexture.h"
 #include "GrGLVertexBuffer.h"
+#include "../GrTHashCache.h"
 
 class GrGpuGL : public GrGpu {
 public:
+    GrGpuGL(const GrGLContextInfo& ctxInfo);
     virtual ~GrGpuGL();
 
-    const GrGLInterface* glInterface() const { 
+    const GrGLInterface* glInterface() const {
         return fGLContextInfo.interface();
     }
     GrGLBinding glBinding() const { return fGLContextInfo.binding(); }
@@ -33,6 +36,11 @@
         return fGLContextInfo.glslGeneration();
     }
 
+    // Used by GrGLProgram to bind necessary textures for GrGLEffects.
+    void bindTexture(int unitIdx, const GrTextureParams& params, GrGLTexture* texture);
+
+    bool programUnitTest();
+
     // GrGpu overrides
     virtual GrPixelConfig preferredReadPixelsConfig(GrPixelConfig config)
                                                             const SK_OVERRIDE;
@@ -46,80 +54,37 @@
                                     size_t rowBytes) const SK_OVERRIDE;
     virtual bool fullReadPixelsIsFasterThanPartial() const SK_OVERRIDE;
 
-    virtual bool canPreserveReadWriteUnpremulPixels() SK_OVERRIDE;
+    virtual void abandonResources() SK_OVERRIDE;
 
-protected:
-    GrGpuGL(const GrGLContextInfo& ctxInfo);
-
-    struct {
-        size_t                  fVertexOffset;
-        GrVertexLayout          fVertexLayout;
-        const GrVertexBuffer*   fVertexBuffer;
-        const GrIndexBuffer*    fIndexBuffer;
-        bool                    fArrayPtrsDirty;
-    } fHWGeometryState;
-
-    struct AAState {
-        bool fMSAAEnabled;
-        bool fSmoothLineEnabled;
-    } fHWAAState;
-
-    enum UnpremulConversion {
-        kUpOnWrite_DownOnRead_UnpremulConversion,
-        kDownOnWrite_UpOnRead_UnpremulConversion
-    } fUnpremulConversion;
-
-    GrDrawState fHWDrawState;
-    bool        fHWStencilClip;
-
-    // As flush of GL state proceeds it updates fHDrawState
-    // to reflect the new state. Later parts of the state flush
-    // may perform cascaded changes but cannot refer to fHWDrawState.
-    // These code paths can refer to the dirty flags. Subclass should
-    // call resetDirtyFlags after its flush is complete
-    struct {
-        bool fRenderTargetChanged : 1;
-        int  fTextureChangedMask;
-    } fDirtyFlags;
-    GR_STATIC_ASSERT(8 * sizeof(int) >= GrDrawState::kNumStages);
-
-    // clears the dirty flags
-    void resetDirtyFlags();
-
-    // last scissor / viewport scissor state seen by the GL.
-    struct {
-        bool        fScissorEnabled;
-        GrGLIRect   fScissorRect;
-        GrGLIRect   fViewportRect;
-    } fHWBounds;
-
-    const GrGLCaps& glCaps() const { return fGLContextInfo.caps(); }
-
+private:
     // GrGpu overrides
     virtual void onResetContext() SK_OVERRIDE;
 
     virtual GrTexture* onCreateTexture(const GrTextureDesc& desc,
                                        const void* srcData,
-                                       size_t rowBytes);
+                                       size_t rowBytes) SK_OVERRIDE;
     virtual GrVertexBuffer* onCreateVertexBuffer(uint32_t size,
-                                                 bool dynamic);
+                                                 bool dynamic) SK_OVERRIDE;
     virtual GrIndexBuffer* onCreateIndexBuffer(uint32_t size,
-                                               bool dynamic);
-    virtual GrTexture* onCreatePlatformTexture(const GrPlatformTextureDesc& desc) SK_OVERRIDE;
-    virtual GrRenderTarget* onCreatePlatformRenderTarget(const GrPlatformRenderTargetDesc& desc) SK_OVERRIDE;
+                                               bool dynamic) SK_OVERRIDE;
+    virtual GrPath* onCreatePath(const SkPath&) SK_OVERRIDE;
+    virtual GrTexture* onWrapBackendTexture(const GrBackendTextureDesc&) SK_OVERRIDE;
+    virtual GrRenderTarget* onWrapBackendRenderTarget(const GrBackendRenderTargetDesc&) SK_OVERRIDE;
     virtual bool createStencilBufferForRenderTarget(GrRenderTarget* rt,
-                                                    int width, int height);
-    virtual bool attachStencilBufferToRenderTarget(GrStencilBuffer* sb,
-                                                   GrRenderTarget* rt);
+                                                    int width,
+                                                    int height) SK_OVERRIDE;
+    virtual bool attachStencilBufferToRenderTarget(
+        GrStencilBuffer* sb,
+        GrRenderTarget* rt) SK_OVERRIDE;
 
-    virtual void onClear(const GrIRect* rect, GrColor color);
+    virtual void onClear(const GrIRect* rect, GrColor color) SK_OVERRIDE;
 
-    virtual void onForceRenderTargetFlush();
+    virtual void onForceRenderTargetFlush() SK_OVERRIDE;
 
     virtual bool onReadPixels(GrRenderTarget* target,
-                              int left, int top, 
+                              int left, int top,
                               int width, int height,
-                              GrPixelConfig, 
+                              GrPixelConfig,
                               void* buffer,
                               size_t rowBytes,
                               bool invertY) SK_OVERRIDE;
@@ -131,45 +96,38 @@
 
     virtual void onResolveRenderTarget(GrRenderTarget* target) SK_OVERRIDE;
 
+    virtual void onGpuDraw(const DrawInfo&) SK_OVERRIDE;
 
-    virtual void onGpuDrawIndexed(GrPrimitiveType type,
-                                  uint32_t startVertex,
-                                  uint32_t startIndex,
-                                  uint32_t vertexCount,
-                                  uint32_t indexCount);
-    virtual void onGpuDrawNonIndexed(GrPrimitiveType type,
-                                     uint32_t vertexCount,
-                                     uint32_t numVertices);
-    virtual void flushScissor(const GrIRect* rect);
-    virtual void clearStencil();
-    virtual void clearStencilClip(const GrIRect& rect, bool insideClip);
-    virtual int getMaxEdges() const;
+    virtual void setStencilPathSettings(const GrPath&,
+                                        SkPath::FillType,
+                                        GrStencilSettings* settings)
+                                        SK_OVERRIDE;
+    virtual void onGpuStencilPath(const GrPath*, SkPath::FillType) SK_OVERRIDE;
+
+    virtual void clearStencil() SK_OVERRIDE;
+    virtual void clearStencilClip(const GrIRect& rect,
+                                  bool insideClip) SK_OVERRIDE;
+    virtual bool flushGraphicsState(DrawType) SK_OVERRIDE;
+
+    const GrGLCaps& glCaps() const { return fGLContextInfo.caps(); }
 
     // binds texture unit in GL
     void setTextureUnit(int unitIdx);
 
-    // binds appropriate vertex and index buffers, also returns any extra
-    // extra verts or indices to offset by.
-    void setBuffers(bool indexed,
-                    int* extraVertexOffset,
-                    int* extraIndexOffset);
-
-    // flushes state that is common to fixed and programmable GL
-    // dither
-    // line smoothing
-    // texture binding
-    // sampler state (filtering, tiling)
-    // FBO binding
-    // line width
-    bool flushGLStateCommon(GrPrimitiveType type);
+    // Sets up vertex attribute pointers and strides. On return startIndexOffset specifies an
+    // offset into the index buffer to the first index to be read (in addition to
+    // info.startIndex()). It accounts for the fact that index buffer pool may have provided space
+    // in the middle of a larger index buffer.
+    void setupGeometry(const DrawInfo& info, int* startIndexOffset);
+    // binds appropriate vertex and index buffers, also returns any extra verts or indices to
+    // offset by based on how space was allocated in pool VB/IBs.
+    void setBuffers(bool indexed, int* extraVertexOffset, int* extraIndexOffset);
 
     // Subclasses should call this to flush the blend state.
-    // The params should be the final coeffecients to apply
+    // The params should be the final coefficients to apply
     // (after any blending optimizations or dual source blending considerations
     // have been accounted for).
-    void flushBlend(GrPrimitiveType type,
-                    GrBlendCoeff srcCoeff,
-                    GrBlendCoeff dstCoeff);
+    void flushBlend(bool isLines, GrBlendCoeff srcCoeff, GrBlendCoeff dstCoeff);
 
     bool hasExtension(const char* ext) const {
         return fGLContextInfo.hasExtension(ext);
@@ -177,21 +135,80 @@
 
     const GrGLContextInfo& glContextInfo() const { return fGLContextInfo; }
 
-    // adjusts texture matrix to account for orientation
-    static void AdjustTextureMatrix(const GrGLTexture* texture,
-                                    GrSamplerState::SampleMode mode,
-                                    GrMatrix* matrix);
-
-    // subclass may try to take advantage of identity tex matrices.
-    // This helper determines if matrix will be identity after all
-    // adjustments are applied.
-    static bool TextureMatrixIsIdentity(const GrGLTexture* texture,
-                                        const GrSamplerState& sampler);
-
     static bool BlendCoeffReferencesConstant(GrBlendCoeff coeff);
 
-private:
-    // Inits GrDrawTarget::Caps, sublcass may enable additional caps.
+    // for readability of function impls
+    typedef GrGLProgram::Desc        ProgramDesc;
+
+    class ProgramCache : public ::GrNoncopyable {
+    public:
+        ProgramCache(const GrGLContextInfo& gl);
+
+        void abandon();
+        GrGLProgram* getProgram(const GrGLProgram::Desc& desc, const GrEffectStage* stages[]);
+    private:
+        enum {
+            kKeySize = sizeof(ProgramDesc),
+            // We may actually have kMaxEntries+1 shaders in the GL context because we create a new
+            // shader before evicting from the cache.
+            kMaxEntries = 32
+        };
+
+        class Entry;
+        // The value of the hash key is based on the ProgramDesc.
+        typedef GrTBinHashKey<Entry, kKeySize> ProgramHashKey;
+
+        class Entry : public ::GrNoncopyable {
+        public:
+            Entry() : fProgram(NULL), fLRUStamp(0) {}
+            Entry& operator = (const Entry& entry) {
+                GrSafeRef(entry.fProgram.get());
+                fProgram.reset(entry.fProgram.get());
+                fKey = entry.fKey;
+                fLRUStamp = entry.fLRUStamp;
+                return *this;
+            }
+            int compare(const ProgramHashKey& key) const {
+                return fKey.compare(key);
+            }
+
+        public:
+            SkAutoTUnref<GrGLProgram>   fProgram;
+            ProgramHashKey              fKey;
+            unsigned int                fLRUStamp; // Move outside entry?
+        };
+
+        GrTHashTable<Entry, ProgramHashKey, 8> fHashCache;
+
+        Entry                       fEntries[kMaxEntries];
+        int                         fCount;
+        unsigned int                fCurrLRUStamp;
+        const GrGLContextInfo&      fGL;
+    };
+
+    // sets the color specified by GrDrawState::setColor()
+    void flushColor(GrColor color);
+
+    // sets the color specified by GrDrawState::setCoverage()
+    void flushCoverage(GrColor color);
+
+    // sets the MVP matrix uniform for currently bound program
+    void flushViewMatrix(DrawType type);
+
+
+    // flushes dithering, color-mask, and face culling stat
+    void flushMiscFixedFunctionState();
+
+    // flushes the scissor. see the note on flushBoundTextureAndParams about
+    // flushing the scissor after that function is called.
+    void flushScissor();
+
+    void buildProgram(bool isPoints,
+                      BlendOptFlags blendOpts,
+                      GrBlendCoeff dstCoeff,
+                      ProgramDesc* desc);
+
+    // Inits GrDrawTarget::Caps, subclass may enable additional caps.
     void initCaps();
 
     void initFSAASupport();
@@ -213,8 +230,8 @@
     // bound is region that may be modified and therefore has to be resolved.
     // NULL means whole target. Can be an empty rect.
     void flushRenderTarget(const GrIRect* bound);
-    void flushStencil();
-    void flushAAState(GrPrimitiveType type);
+    void flushStencil(DrawType);
+    void flushAAState(DrawType);
 
     bool configToGLFormats(GrPixelConfig config,
                            bool getSizedInternal,
@@ -233,6 +250,8 @@
                                    GrGLuint texID,
                                    GrGLRenderTarget::Desc* desc);
 
+    void fillInConfigRenderableTable();
+
     friend class GrGLVertexBuffer;
     friend class GrGLIndexBuffer;
     friend class GrGLTexture;
@@ -240,29 +259,94 @@
 
     GrGLContextInfo fGLContextInfo;
 
-    // we want to clear stencil buffers when they are created. We want to clear
-    // the entire buffer even if it is larger than the color attachment. We
-    // attach it to this fbo with no color attachment to do the initial clear.
-    GrGLuint fStencilClearFBO;
+    // GL program-related state
+    ProgramCache*               fProgramCache;
+    SkAutoTUnref<GrGLProgram>   fCurrentProgram;
 
-    bool fHWBlendDisabled;
+    ///////////////////////////////////////////////////////////////////////////
+    ///@name Caching of GL State
+    ///@{
+    int                         fHWActiveTextureUnitIdx;
+    GrGLuint                    fHWProgramID;
+    GrColor                     fHWConstAttribColor;
+    GrColor                     fHWConstAttribCoverage;
 
-    int fActiveTextureUnitIdx;
+    enum TriState {
+        kNo_TriState,
+        kYes_TriState,
+        kUnknown_TriState
+    };
+
+    // last scissor / viewport scissor state seen by the GL.
+    struct {
+        TriState    fEnabled;
+        GrGLIRect   fRect;
+        void invalidate() {
+            fEnabled = kUnknown_TriState;
+            fRect.invalidate();
+        }
+    } fHWScissorSettings;
+
+    GrGLIRect   fHWViewport;
+
+    struct {
+        size_t                  fVertexOffset;
+        GrVertexLayout          fVertexLayout;
+        const GrVertexBuffer*   fVertexBuffer;
+        const GrIndexBuffer*    fIndexBuffer;
+        bool                    fArrayPtrsDirty;
+    } fHWGeometryState;
+
+    struct {
+        GrBlendCoeff    fSrcCoeff;
+        GrBlendCoeff    fDstCoeff;
+        GrColor         fConstColor;
+        bool            fConstColorValid;
+        TriState        fEnabled;
+
+        void invalidate() {
+            fSrcCoeff = kInvalid_GrBlendCoeff;
+            fDstCoeff = kInvalid_GrBlendCoeff;
+            fConstColorValid = false;
+            fEnabled = kUnknown_TriState;
+        }
+    } fHWBlendState;
+
+    struct {
+        TriState fMSAAEnabled;
+        TriState fSmoothLineEnabled;
+        void invalidate() {
+            fMSAAEnabled = kUnknown_TriState;
+            fSmoothLineEnabled = kUnknown_TriState;
+        }
+    } fHWAAState;
+
+    struct {
+        SkMatrix    fViewMatrix;
+        SkISize     fRTSize;
+        void invalidate() {
+            fViewMatrix = SkMatrix::InvalidMatrix();
+            fRTSize.fWidth = -1; // just make the first value compared illegal.
+        }
+    } fHWPathMatrixState;
+
+    GrStencilSettings       fHWStencilSettings;
+    TriState                fHWStencilTestEnabled;
+
+    GrDrawState::DrawFace   fHWDrawFace;
+    TriState                fHWWriteToColor;
+    TriState                fHWDitherEnabled;
+    GrRenderTarget*         fHWBoundRenderTarget;
+    GrTexture*              fHWBoundTextures[GrDrawState::kNumStages];
+    ///@}
 
     // we record what stencil format worked last time to hopefully exit early
     // from our loop that tries stencil formats and calls check fb status.
     int fLastSuccessfulStencilFmtIdx;
 
-    enum CanPreserveUnpremulRoundtrip {
-        kUnknown_CanPreserveUnpremulRoundtrip,
-        kNo_CanPreserveUnpremulRoundtrip,
-        kYes_CanPreserveUnpremulRoundtrip,
-    } fCanPreserveUnpremulRoundtrip;
-
     bool fPrintedCaps;
 
     typedef GrGpu INHERITED;
 };
 
 #endif
-
diff --git a/src/gpu/gl/GrGpuGLShaders.cpp b/src/gpu/gl/GrGpuGLShaders.cpp
deleted file mode 100644
index db7e3a7..0000000
--- a/src/gpu/gl/GrGpuGLShaders.cpp
+++ /dev/null
@@ -1,1231 +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 "../GrBinHashKey.h"
-#include "GrGLProgram.h"
-#include "GrGLSL.h"
-#include "GrGpuGLShaders.h"
-#include "../GrGpuVertex.h"
-#include "GrNoncopyable.h"
-#include "../GrStringBuilder.h"
-#include "../GrRandom.h"
-
-#define SKIP_CACHE_CHECK    true
-#define GR_UINT32_MAX   static_cast<uint32_t>(-1)
-
-#include "../GrTHashCache.h"
-
-class GrGpuGLShaders::ProgramCache : public ::GrNoncopyable {
-private:
-    class Entry;
-
-    typedef GrBinHashKey<Entry, GrGLProgram::kProgramKeySize> ProgramHashKey;
-
-    class Entry : public ::GrNoncopyable {
-    public:
-        Entry() {}
-        void copyAndTakeOwnership(Entry& entry) {
-            fProgramData.copyAndTakeOwnership(entry.fProgramData);
-            fKey = entry.fKey; // ownership transfer
-            fLRUStamp = entry.fLRUStamp;
-        }
-
-    public:
-        int compare(const ProgramHashKey& key) const { return fKey.compare(key); }
-
-    public:
-        GrGLProgram::CachedData fProgramData;
-        ProgramHashKey          fKey;
-        unsigned int            fLRUStamp;
-    };
-
-    GrTHashTable<Entry, ProgramHashKey, 8> fHashCache;
-
-    // We may have kMaxEntries+1 shaders in the GL context because
-    // we create a new shader before evicting from the cache.
-    enum {
-        kMaxEntries = 32
-    };
-    Entry                       fEntries[kMaxEntries];
-    int                         fCount;
-    unsigned int                fCurrLRUStamp;
-    const GrGLContextInfo&      fGL;
-
-public:
-    ProgramCache(const GrGLContextInfo& gl)
-        : fCount(0)
-        , fCurrLRUStamp(0)
-        , fGL(gl) {
-    }
-
-    ~ProgramCache() {
-        for (int i = 0; i < fCount; ++i) {
-            GrGpuGLShaders::DeleteProgram(fGL.interface(),
-                                          &fEntries[i].fProgramData);
-        }
-    }
-
-    void abandon() {
-        fCount = 0;
-    }
-
-    void invalidateViewMatrices() {
-        for (int i = 0; i < fCount; ++i) {
-            // set to illegal matrix
-            fEntries[i].fProgramData.fViewMatrix = GrMatrix::InvalidMatrix();
-        }
-    }
-
-    GrGLProgram::CachedData* getProgramData(const GrGLProgram& desc) {
-        Entry newEntry;
-        newEntry.fKey.setKeyData(desc.keyData());
-        
-        Entry* entry = fHashCache.find(newEntry.fKey);
-        if (NULL == entry) {
-            if (!desc.genProgram(fGL, &newEntry.fProgramData)) {
-                return NULL;
-            }
-            if (fCount < kMaxEntries) {
-                entry = fEntries + fCount;
-                ++fCount;
-            } else {
-                GrAssert(kMaxEntries == fCount);
-                entry = fEntries;
-                for (int i = 1; i < kMaxEntries; ++i) {
-                    if (fEntries[i].fLRUStamp < entry->fLRUStamp) {
-                        entry = fEntries + i;
-                    }
-                }
-                fHashCache.remove(entry->fKey, entry);
-                GrGpuGLShaders::DeleteProgram(fGL.interface(),
-                                              &entry->fProgramData);
-            }
-            entry->copyAndTakeOwnership(newEntry);
-            fHashCache.insert(entry->fKey, entry);
-        }
-
-        entry->fLRUStamp = fCurrLRUStamp;
-        if (GR_UINT32_MAX == fCurrLRUStamp) {
-            // wrap around! just trash our LRU, one time hit.
-            for (int i = 0; i < fCount; ++i) {
-                fEntries[i].fLRUStamp = 0;
-            }
-        }
-        ++fCurrLRUStamp;
-        return &entry->fProgramData;
-    }
-};
-
-void GrGpuGLShaders::abandonResources(){
-    INHERITED::abandonResources();
-    fProgramCache->abandon();
-}
-
-void GrGpuGLShaders::DeleteProgram(const GrGLInterface* gl,
-                                   CachedData* programData) {
-    GR_GL_CALL(gl, DeleteShader(programData->fVShaderID));
-    if (programData->fGShaderID) {
-        GR_GL_CALL(gl, DeleteShader(programData->fGShaderID));
-    }
-    GR_GL_CALL(gl, DeleteShader(programData->fFShaderID));
-    GR_GL_CALL(gl, DeleteProgram(programData->fProgramID));
-    GR_DEBUGCODE(memset(programData, 0, sizeof(*programData));)
-}
-
-////////////////////////////////////////////////////////////////////////////////
-
-#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
-
-namespace {
-
-// GrRandoms nextU() values have patterns in the low bits
-// So using nextU() % array_count might never take some values.
-int random_int(GrRandom* r, int count) {
-    return (int)(r->nextF() * count);
-}
-
-// min is inclusive, max is exclusive
-int random_int(GrRandom* r, int min, int max) {
-    return (int)(r->nextF() * (max-min)) + min;
-}
-
-bool random_bool(GrRandom* r) {
-    return r->nextF() > .5f;
-}
-
-}
-
-bool GrGpuGLShaders::programUnitTest() {
-
-    GrGLSLGeneration glslGeneration = 
-            GrGetGLSLGeneration(this->glBinding(), this->glInterface());
-    static const int STAGE_OPTS[] = {
-        0,
-        StageDesc::kNoPerspective_OptFlagBit,
-        StageDesc::kIdentity_CoordMapping
-    };
-    static const int IN_CONFIG_FLAGS[] = {
-        StageDesc::kNone_InConfigFlag,
-        StageDesc::kSwapRAndB_InConfigFlag,
-        StageDesc::kSwapRAndB_InConfigFlag |
-        StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag,
-        StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag,
-        StageDesc::kSmearAlpha_InConfigFlag,
-    };
-    GrGLProgram program;
-    ProgramDesc& pdesc = program.fProgramDesc;
-
-    static const int NUM_TESTS = 512;
-
-    GrRandom random;
-    for (int t = 0; t < NUM_TESTS; ++t) {
-
-#if 0
-        GrPrintf("\nTest Program %d\n-------------\n", t);
-        static const int stop = -1;
-        if (t == stop) {
-            int breakpointhere = 9;
-        }
-#endif
-
-        pdesc.fVertexLayout = 0;
-        pdesc.fEmitsPointSize = random.nextF() > .5f;
-        pdesc.fColorInput = random_int(&random, ProgramDesc::kColorInputCnt);
-        pdesc.fCoverageInput = random_int(&random, ProgramDesc::kColorInputCnt);
-
-        pdesc.fColorFilterXfermode = random_int(&random, SkXfermode::kCoeffModesCnt);
-
-        pdesc.fFirstCoverageStage = random_int(&random, GrDrawState::kNumStages);
-
-        pdesc.fVertexLayout |= random_bool(&random) ?
-                                    GrDrawTarget::kCoverage_VertexLayoutBit :
-                                    0;
-
-#if GR_GL_EXPERIMENTAL_GS
-        pdesc.fExperimentalGS = this->getCaps().fGeometryShaderSupport &&
-                                random_bool(&random);
-#endif
-        pdesc.fOutputConfig =  random_int(&random, ProgramDesc::kOutputConfigCnt);
-
-        bool edgeAA = random_bool(&random);
-        if (edgeAA) {
-            bool vertexEdgeAA = random_bool(&random);
-            if (vertexEdgeAA) {
-                pdesc.fVertexLayout |= GrDrawTarget::kEdge_VertexLayoutBit;
-                if (this->getCaps().fShaderDerivativeSupport) {
-                    pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
-                } else {
-                    pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
-                }
-                pdesc.fEdgeAANumEdges = 0;
-            } else {
-                pdesc.fEdgeAANumEdges = random_int(&random, 1, this->getMaxEdges());
-                pdesc.fEdgeAAConcave = random_bool(&random);
-            }
-        } else {
-            pdesc.fEdgeAANumEdges = 0;
-        }
-
-        pdesc.fColorMatrixEnabled = random_bool(&random);
-
-        if (this->getCaps().fDualSourceBlendingSupport) {
-            pdesc.fDualSrcOutput = random_int(&random, ProgramDesc::kDualSrcOutputCnt);
-        } else {
-            pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
-        }
-
-        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-            // enable the stage?
-            if (random_bool(&random)) {
-                // use separate tex coords?
-                if (random_bool(&random)) {
-                    int t = random_int(&random, GrDrawState::kMaxTexCoords);
-                    pdesc.fVertexLayout |= StageTexCoordVertexLayoutBit(s, t);
-                } else {
-                    pdesc.fVertexLayout |= StagePosAsTexCoordVertexLayoutBit(s);
-                }
-            }
-            // use text-formatted verts?
-            if (random_bool(&random)) {
-                pdesc.fVertexLayout |= kTextFormat_VertexLayoutBit;
-            }
-            StageDesc& stage = pdesc.fStages[s];
-            stage.fOptFlags = STAGE_OPTS[random_int(&random, GR_ARRAY_COUNT(STAGE_OPTS))];
-            stage.fInConfigFlags = IN_CONFIG_FLAGS[random_int(&random, GR_ARRAY_COUNT(IN_CONFIG_FLAGS))];
-            stage.fCoordMapping =  random_int(&random, StageDesc::kCoordMappingCnt);
-            stage.fFetchMode = random_int(&random, StageDesc::kFetchModeCnt);
-            // convolution shaders don't work with persp tex matrix
-            if (stage.fFetchMode == StageDesc::kConvolution_FetchMode ||
-                stage.fFetchMode == StageDesc::kDilate_FetchMode ||
-                stage.fFetchMode == StageDesc::kErode_FetchMode) {
-                stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
-            }
-            stage.setEnabled(VertexUsesStage(s, pdesc.fVertexLayout));
-            static const uint32_t kMulByAlphaMask =
-                StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag |
-                StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
-            switch (stage.fFetchMode) {
-                case StageDesc::kSingle_FetchMode:
-                    stage.fKernelWidth = 0;
-                    break;
-                case StageDesc::kConvolution_FetchMode:
-                case StageDesc::kDilate_FetchMode:
-                case StageDesc::kErode_FetchMode:
-                    stage.fKernelWidth = random_int(&random, 2, 8);
-                    stage.fInConfigFlags &= ~kMulByAlphaMask;
-                    break;
-                case StageDesc::k2x2_FetchMode:
-                    stage.fKernelWidth = 0;
-                    stage.fInConfigFlags &= ~kMulByAlphaMask;
-                    break;
-            }
-        }
-        CachedData cachedData;
-        if (!program.genProgram(this->glContextInfo(), &cachedData)) {
-            return false;
-        }
-        DeleteProgram(this->glInterface(), &cachedData);
-    }
-    return true;
-}
-
-GrGpuGLShaders::GrGpuGLShaders(const GrGLContextInfo& ctxInfo)
-    : GrGpuGL(ctxInfo) {
-
-    // Enable supported shader-related caps
-    if (kDesktop_GrGLBinding == this->glBinding()) {
-        fCaps.fDualSourceBlendingSupport =
-                            this->glVersion() >= GR_GL_VER(3,3) ||
-                            this->hasExtension("GL_ARB_blend_func_extended");
-        fCaps.fShaderDerivativeSupport = true;
-        // we don't support GL_ARB_geometry_shader4, just GL 3.2+ GS
-        fCaps.fGeometryShaderSupport = 
-                                this->glVersion() >= GR_GL_VER(3,2) &&
-                                this->glslGeneration() >= k150_GrGLSLGeneration;
-    } else {
-        fCaps.fShaderDerivativeSupport =
-                            this->hasExtension("GL_OES_standard_derivatives");
-    }
-
-    GR_GL_GetIntegerv(this->glInterface(),
-                      GR_GL_MAX_VERTEX_ATTRIBS,
-                      &fMaxVertexAttribs);
-
-    fProgramData = NULL;
-    fProgramCache = new ProgramCache(this->glContextInfo());
-
-#if 0
-    this->programUnitTest();
-#endif
-}
-
-GrGpuGLShaders::~GrGpuGLShaders() {
-    delete fProgramCache;
-}
-
-const GrMatrix& GrGpuGLShaders::getHWViewMatrix() {
-    GrAssert(fProgramData);
-
-    if (GrGLProgram::kSetAsAttribute == 
-        fProgramData->fUniLocations.fViewMatrixUni) {
-        return fHWDrawState.getViewMatrix();
-    } else {
-        return fProgramData->fViewMatrix;
-    }
-}
-
-void GrGpuGLShaders::recordHWViewMatrix(const GrMatrix& matrix) {
-    GrAssert(fProgramData);
-    if (GrGLProgram::kSetAsAttribute == 
-        fProgramData->fUniLocations.fViewMatrixUni) {
-        fHWDrawState.setViewMatrix(matrix);
-    } else {
-        fProgramData->fViewMatrix = matrix;
-    }
-}
-
-const GrMatrix& GrGpuGLShaders::getHWSamplerMatrix(int stage) {
-    GrAssert(fProgramData);
-
-    if (GrGLProgram::kSetAsAttribute == 
-        fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
-        return fHWDrawState.getSampler(stage).getMatrix();
-    } else {
-        return fProgramData->fTextureMatrices[stage];
-    }
-}
-
-void GrGpuGLShaders::recordHWSamplerMatrix(int stage, const GrMatrix& matrix) {
-    GrAssert(fProgramData);
-    if (GrGLProgram::kSetAsAttribute == 
-        fProgramData->fUniLocations.fStages[stage].fTextureMatrixUni) {
-        *fHWDrawState.sampler(stage)->matrix() = matrix;
-    } else {
-        fProgramData->fTextureMatrices[stage] = matrix;
-    }
-}
-
-void GrGpuGLShaders::onResetContext() {
-    INHERITED::onResetContext();
-
-    fHWGeometryState.fVertexOffset = ~0;
-
-    // Third party GL code may have left vertex attributes enabled. Some GL
-    // implementations (osmesa) may read vetex attributes that are not required
-    // by the current shader. Therefore, we have to ensure that only the
-    // attributes we require for the current draw are enabled or we may cause an
-    // invalid read.
-
-    // Disable all vertex layout bits so that next flush will assume all
-    // optional vertex attributes are disabled.
-    fHWGeometryState.fVertexLayout = 0;
-
-    // We always use the this attribute and assume it is always enabled.
-    int posAttrIdx = GrGLProgram::PositionAttributeIdx();
-    GL_CALL(EnableVertexAttribArray(posAttrIdx));
-    // Disable all other vertex attributes.
-    for  (int va = 0; va < fMaxVertexAttribs; ++va) {
-        if (va != posAttrIdx) {
-            GL_CALL(DisableVertexAttribArray(va));
-        }
-    }
-
-    fHWProgramID = 0;
-}
-
-void GrGpuGLShaders::flushViewMatrix() {
-    const GrMatrix& vm = this->getDrawState().getViewMatrix();
-    if (GrGpuGLShaders::getHWViewMatrix() != vm) {
-
-        const GrRenderTarget* rt = this->getDrawState().getRenderTarget();
-        GrAssert(NULL != rt);
-        GrMatrix m;
-        m.setAll(
-            GrIntToScalar(2) / rt->width(), 0, -GR_Scalar1,
-            0,-GrIntToScalar(2) / rt->height(), GR_Scalar1,
-            0, 0, GrMatrix::I()[8]);
-        m.setConcat(m, vm);
-
-        // ES doesn't allow you to pass true to the transpose param,
-        // so do our own transpose
-        GrGLfloat mt[]  = {
-            GrScalarToFloat(m[GrMatrix::kMScaleX]),
-            GrScalarToFloat(m[GrMatrix::kMSkewY]),
-            GrScalarToFloat(m[GrMatrix::kMPersp0]),
-            GrScalarToFloat(m[GrMatrix::kMSkewX]),
-            GrScalarToFloat(m[GrMatrix::kMScaleY]),
-            GrScalarToFloat(m[GrMatrix::kMPersp1]),
-            GrScalarToFloat(m[GrMatrix::kMTransX]),
-            GrScalarToFloat(m[GrMatrix::kMTransY]),
-            GrScalarToFloat(m[GrMatrix::kMPersp2])
-        };
-
-        if (GrGLProgram::kSetAsAttribute ==  
-            fProgramData->fUniLocations.fViewMatrixUni) {
-            int baseIdx = GrGLProgram::ViewMatrixAttributeIdx();
-            GL_CALL(VertexAttrib4fv(baseIdx + 0, mt+0));
-            GL_CALL(VertexAttrib4fv(baseIdx + 1, mt+3));
-            GL_CALL(VertexAttrib4fv(baseIdx + 2, mt+6));
-        } else {
-            GrAssert(GrGLProgram::kUnusedUniform != 
-                     fProgramData->fUniLocations.fViewMatrixUni);
-            GL_CALL(UniformMatrix3fv(fProgramData->fUniLocations.fViewMatrixUni,
-                                     1, false, mt));
-        }
-        this->recordHWViewMatrix(vm);
-    }
-}
-
-void GrGpuGLShaders::flushTextureDomain(int s) {
-    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTexDomUni;
-    const GrDrawState& drawState = this->getDrawState();
-    if (GrGLProgram::kUnusedUniform != uni) {
-        const GrRect &texDom = drawState.getSampler(s).getTextureDomain();
-
-        if (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
-            fProgramData->fTextureDomain[s] != texDom) {
-
-            fProgramData->fTextureDomain[s] = texDom;
-
-            float values[4] = {
-                GrScalarToFloat(texDom.left()),
-                GrScalarToFloat(texDom.top()),
-                GrScalarToFloat(texDom.right()),
-                GrScalarToFloat(texDom.bottom())
-            };
-
-            const GrGLTexture* texture =
-                static_cast<const GrGLTexture*>(drawState.getTexture(s));
-            GrGLTexture::Orientation orientation = texture->orientation();
-
-            // vertical flip if necessary
-            if (GrGLTexture::kBottomUp_Orientation == orientation) {
-                values[1] = 1.0f - values[1];
-                values[3] = 1.0f - values[3];
-                // The top and bottom were just flipped, so correct the ordering
-                // of elements so that values = (l, t, r, b).
-                SkTSwap(values[1], values[3]);
-            }
-
-            GL_CALL(Uniform4fv(uni, 1, values));
-        }
-    }
-}
-
-void GrGpuGLShaders::flushTextureMatrix(int s) {
-    const GrGLint& uni = fProgramData->fUniLocations.fStages[s].fTextureMatrixUni;
-    const GrDrawState& drawState = this->getDrawState();
-    const GrGLTexture* texture =
-        static_cast<const GrGLTexture*>(drawState.getTexture(s));
-    if (NULL != texture) {
-        if (GrGLProgram::kUnusedUniform != uni &&
-            (((1 << s) & fDirtyFlags.fTextureChangedMask) ||
-            this->getHWSamplerMatrix(s) != drawState.getSampler(s).getMatrix())) {
-
-            GrMatrix m = drawState.getSampler(s).getMatrix();
-            GrSamplerState::SampleMode mode =
-                drawState.getSampler(s).getSampleMode();
-            AdjustTextureMatrix(texture, mode, &m);
-
-            // ES doesn't allow you to pass true to the transpose param,
-            // so do our own transpose
-            GrGLfloat mt[]  = {
-                GrScalarToFloat(m[GrMatrix::kMScaleX]),
-                GrScalarToFloat(m[GrMatrix::kMSkewY]),
-                GrScalarToFloat(m[GrMatrix::kMPersp0]),
-                GrScalarToFloat(m[GrMatrix::kMSkewX]),
-                GrScalarToFloat(m[GrMatrix::kMScaleY]),
-                GrScalarToFloat(m[GrMatrix::kMPersp1]),
-                GrScalarToFloat(m[GrMatrix::kMTransX]),
-                GrScalarToFloat(m[GrMatrix::kMTransY]),
-                GrScalarToFloat(m[GrMatrix::kMPersp2])
-            };
-
-            if (GrGLProgram::kSetAsAttribute ==
-                fProgramData->fUniLocations.fStages[s].fTextureMatrixUni) {
-                int baseIdx = GrGLProgram::TextureMatrixAttributeIdx(s);
-                GL_CALL(VertexAttrib4fv(baseIdx + 0, mt+0));
-                GL_CALL(VertexAttrib4fv(baseIdx + 1, mt+3));
-                GL_CALL(VertexAttrib4fv(baseIdx + 2, mt+6));
-            } else {
-                GL_CALL(UniformMatrix3fv(uni, 1, false, mt));
-            }
-            this->recordHWSamplerMatrix(s, drawState.getSampler(s).getMatrix());
-        }
-    }
-}
-
-void GrGpuGLShaders::flushRadial2(int s) {
-
-    const int &uni = fProgramData->fUniLocations.fStages[s].fRadial2Uni;
-    const GrSamplerState& sampler = this->getDrawState().getSampler(s);
-    if (GrGLProgram::kUnusedUniform != uni &&
-        (fProgramData->fRadial2CenterX1[s] != sampler.getRadial2CenterX1() ||
-         fProgramData->fRadial2Radius0[s]  != sampler.getRadial2Radius0()  ||
-         fProgramData->fRadial2PosRoot[s]  != sampler.isRadial2PosRoot())) {
-
-        GrScalar centerX1 = sampler.getRadial2CenterX1();
-        GrScalar radius0 = sampler.getRadial2Radius0();
-
-        GrScalar a = GrMul(centerX1, centerX1) - GR_Scalar1;
-
-        // when were 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] = {
-            GrScalarToFloat(a),
-            1 / (2.f * GrScalarToFloat(a)),
-            GrScalarToFloat(centerX1),
-            GrScalarToFloat(radius0),
-            GrScalarToFloat(GrMul(radius0, radius0)),
-            sampler.isRadial2PosRoot() ? 1.f : -1.f
-        };
-        GL_CALL(Uniform1fv(uni, 6, values));
-        fProgramData->fRadial2CenterX1[s] = sampler.getRadial2CenterX1();
-        fProgramData->fRadial2Radius0[s]  = sampler.getRadial2Radius0();
-        fProgramData->fRadial2PosRoot[s]  = sampler.isRadial2PosRoot();
-    }
-}
-
-void GrGpuGLShaders::flushConvolution(int s) {
-    const GrSamplerState& sampler = this->getDrawState().getSampler(s);
-    int kernelUni = fProgramData->fUniLocations.fStages[s].fKernelUni;
-    if (GrGLProgram::kUnusedUniform != kernelUni) {
-        GL_CALL(Uniform1fv(kernelUni, sampler.getKernelWidth(),
-                           sampler.getKernel()));
-    }
-    int imageIncrementUni = fProgramData->fUniLocations.fStages[s].fImageIncrementUni;
-    if (GrGLProgram::kUnusedUniform != imageIncrementUni) {
-        const GrGLTexture* texture =
-            static_cast<const GrGLTexture*>(this->getDrawState().getTexture(s));
-        float imageIncrement[2] = { 0 };
-        switch (sampler.getFilterDirection()) {
-            case GrSamplerState::kX_FilterDirection:
-                imageIncrement[0] = 1.0f / texture->width();
-                break;
-            case GrSamplerState::kY_FilterDirection:
-                imageIncrement[1] = 1.0f / texture->height();
-                break;
-            default:
-                GrCrash("Unknown filter direction.");
-        }
-        GL_CALL(Uniform2fv(imageIncrementUni, 1, imageIncrement));
-    }
-}
-
-void GrGpuGLShaders::flushTexelSize(int s) {
-    const int& uni = fProgramData->fUniLocations.fStages[s].fNormalizedTexelSizeUni;
-    if (GrGLProgram::kUnusedUniform != uni) {
-        const GrGLTexture* texture =
-            static_cast<const GrGLTexture*>(this->getDrawState().getTexture(s));
-        if (texture->width() != fProgramData->fTextureWidth[s] ||
-            texture->height() != fProgramData->fTextureHeight[s]) {
-
-            float texelSize[] = {1.f / texture->width(),
-                                 1.f / texture->height()};
-            GL_CALL(Uniform2fv(uni, 1, texelSize));
-            fProgramData->fTextureWidth[s] = texture->width();
-            fProgramData->fTextureHeight[s] = texture->height();
-        }
-    }
-}
-
-void GrGpuGLShaders::flushEdgeAAData() {
-    const int& uni = fProgramData->fUniLocations.fEdgesUni;
-    if (GrGLProgram::kUnusedUniform != uni) {
-        int count = this->getDrawState().getNumAAEdges();
-        GrDrawState::Edge edges[GrDrawState::kMaxEdges];
-        // Flip the edges in Y
-        float height = 
-            static_cast<float>(this->getDrawState().getRenderTarget()->height());
-        for (int i = 0; i < count; ++i) {
-            edges[i] = this->getDrawState().getAAEdges()[i];
-            float b = edges[i].fY;
-            edges[i].fY = -b;
-            edges[i].fZ += b * height;
-        }
-        GL_CALL(Uniform3fv(uni, count, &edges[0].fX));
-    }
-}
-
-void GrGpuGLShaders::flushColorMatrix() {
-    const ProgramDesc& desc = fCurrentProgram.getDesc();
-    int matrixUni = fProgramData->fUniLocations.fColorMatrixUni;
-    int vecUni = fProgramData->fUniLocations.fColorMatrixVecUni;
-    if (GrGLProgram::kUnusedUniform != matrixUni
-     && GrGLProgram::kUnusedUniform != vecUni) {
-        const float* m = this->getDrawState().getColorMatrix();
-        GrGLfloat mt[]  = {
-            m[0], m[5], m[10], m[15],
-            m[1], m[6], m[11], m[16],
-            m[2], m[7], m[12], m[17],
-            m[3], m[8], m[13], m[18],
-        };
-        static float scale = 1.0f / 255.0f;
-        GrGLfloat vec[] = {
-            m[4] * scale, m[9] * scale, m[14] * scale, m[19] * scale,
-        };
-        GL_CALL(UniformMatrix4fv(matrixUni, 1, false, mt));
-        GL_CALL(Uniform4fv(vecUni, 1, vec));
-    }
-}
-
-static const float ONE_OVER_255 = 1.f / 255.f;
-
-#define GR_COLOR_TO_VEC4(color) {\
-    GrColorUnpackR(color) * ONE_OVER_255,\
-    GrColorUnpackG(color) * ONE_OVER_255,\
-    GrColorUnpackB(color) * ONE_OVER_255,\
-    GrColorUnpackA(color) * ONE_OVER_255 \
-}
-
-void GrGpuGLShaders::flushColor(GrColor color) {
-    const ProgramDesc& desc = fCurrentProgram.getDesc();
-    const GrDrawState& drawState = this->getDrawState();
-
-    if (this->getGeomSrc().fVertexLayout & kColor_VertexLayoutBit) {
-        // color will be specified per-vertex as an attribute
-        // invalidate the const vertex attrib color
-        fHWDrawState.setColor(GrColor_ILLEGAL);
-    } else {
-        switch (desc.fColorInput) {
-            case ProgramDesc::kAttribute_ColorInput:
-                if (fHWDrawState.getColor() != color) {
-                    // OpenGL ES only supports the float varieties of
-                    // glVertexAttrib
-                    float c[] = GR_COLOR_TO_VEC4(color);
-                    GL_CALL(VertexAttrib4fv(GrGLProgram::ColorAttributeIdx(), 
-                                            c));
-                    fHWDrawState.setColor(color);
-                }
-                break;
-            case ProgramDesc::kUniform_ColorInput:
-                if (fProgramData->fColor != color) {
-                    // OpenGL ES doesn't support unsigned byte varieties of
-                    // glUniform
-                    float c[] = GR_COLOR_TO_VEC4(color);
-                    GrAssert(GrGLProgram::kUnusedUniform != 
-                             fProgramData->fUniLocations.fColorUni);
-                    GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorUni,
-                                        1, c));
-                    fProgramData->fColor = color;
-                }
-                break;
-            case ProgramDesc::kSolidWhite_ColorInput:
-            case ProgramDesc::kTransBlack_ColorInput:
-                break;
-            default:
-                GrCrash("Unknown color type.");
-        }
-    }
-    if (fProgramData->fUniLocations.fColorFilterUni
-                != GrGLProgram::kUnusedUniform
-            && fProgramData->fColorFilterColor
-                != drawState.getColorFilterColor()) {
-        float c[] = GR_COLOR_TO_VEC4(drawState.getColorFilterColor());
-        GL_CALL(Uniform4fv(fProgramData->fUniLocations.fColorFilterUni, 1, c));
-        fProgramData->fColorFilterColor = drawState.getColorFilterColor();
-    }
-}
-
-void GrGpuGLShaders::flushCoverage(GrColor coverage) {
-    const ProgramDesc& desc = fCurrentProgram.getDesc();
-    const GrDrawState& drawState = this->getDrawState();
-
-
-    if (this->getGeomSrc().fVertexLayout & kCoverage_VertexLayoutBit) {
-        // coverage will be specified per-vertex as an attribute
-        // invalidate the const vertex attrib coverage
-        fHWDrawState.setCoverage4(GrColor_ILLEGAL);
-    } else {
-        switch (desc.fCoverageInput) {
-            case ProgramDesc::kAttribute_ColorInput:
-                if (fHWDrawState.getCoverage() != coverage) {
-                    // OpenGL ES only supports the float varieties of
-                    // glVertexAttrib
-                    float c[] = GR_COLOR_TO_VEC4(coverage);
-                    GL_CALL(VertexAttrib4fv(GrGLProgram::CoverageAttributeIdx(), 
-                                            c));
-                    fHWDrawState.setCoverage(coverage);
-                }
-                break;
-            case ProgramDesc::kUniform_ColorInput:
-                if (fProgramData->fCoverage != coverage) {
-                    // OpenGL ES doesn't support unsigned byte varieties of
-                    // glUniform
-                    float c[] = GR_COLOR_TO_VEC4(coverage);
-                    GrAssert(GrGLProgram::kUnusedUniform != 
-                             fProgramData->fUniLocations.fCoverageUni);
-                    GL_CALL(Uniform4fv(fProgramData->fUniLocations.fCoverageUni,
-                                        1, c));
-                    fProgramData->fCoverage = coverage;
-                }
-                break;
-            case ProgramDesc::kSolidWhite_ColorInput:
-            case ProgramDesc::kTransBlack_ColorInput:
-                break;
-            default:
-                GrCrash("Unknown coverage type.");
-        }
-    }
-}
-
-bool GrGpuGLShaders::flushGraphicsState(GrPrimitiveType type) {
-    if (!flushGLStateCommon(type)) {
-        return false;
-    }
-
-    const GrDrawState& drawState = this->getDrawState();
-
-    if (fDirtyFlags.fRenderTargetChanged) {
-        // our coords are in pixel space and the GL matrices map to NDC
-        // so if the viewport changed, our matrix is now wrong.
-        fHWDrawState.setViewMatrix(GrMatrix::InvalidMatrix());
-        // we assume all shader matrices may be wrong after viewport changes
-        fProgramCache->invalidateViewMatrices();
-    }
-
-    GrBlendCoeff srcCoeff;
-    GrBlendCoeff dstCoeff;
-    BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
-    if (kSkipDraw_BlendOptFlag & blendOpts) {
-        return false;
-    }
-
-    this->buildProgram(type, blendOpts, dstCoeff);
-    fProgramData = fProgramCache->getProgramData(fCurrentProgram);
-    if (NULL == fProgramData) {
-        GrAssert(!"Failed to create program!");
-        return false;
-    }
-
-    if (fHWProgramID != fProgramData->fProgramID) {
-        GL_CALL(UseProgram(fProgramData->fProgramID));
-        fHWProgramID = fProgramData->fProgramID;
-    }
-    fCurrentProgram.overrideBlend(&srcCoeff, &dstCoeff);
-    this->flushBlend(type, srcCoeff, dstCoeff);
-
-    GrColor color;
-    GrColor coverage;
-    if (blendOpts & kEmitTransBlack_BlendOptFlag) {
-        color = 0;
-        coverage = 0;
-    } else if (blendOpts & kEmitCoverage_BlendOptFlag) {
-        color = 0xffffffff;
-        coverage = drawState.getCoverage();
-    } else {
-        color = drawState.getColor();
-        coverage = drawState.getCoverage();
-    }
-    this->flushColor(color);
-    this->flushCoverage(coverage);
-
-    this->flushViewMatrix();
-
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        if (this->isStageEnabled(s)) {
-            this->flushTextureMatrix(s);
-
-            this->flushRadial2(s);
-
-            this->flushConvolution(s);
-
-            this->flushTexelSize(s);
-
-            this->flushTextureDomain(s);
-        }
-    }
-    this->flushEdgeAAData();
-    this->flushColorMatrix();
-    resetDirtyFlags();
-    return true;
-}
-
-void GrGpuGLShaders::postDraw() {
-}
-
-void GrGpuGLShaders::setupGeometry(int* startVertex,
-                                    int* startIndex,
-                                    int vertexCount,
-                                    int indexCount) {
-
-    int newColorOffset;
-    int newCoverageOffset;
-    int newTexCoordOffsets[GrDrawState::kMaxTexCoords];
-    int newEdgeOffset;
-
-    GrGLsizei newStride = VertexSizeAndOffsetsByIdx(
-                                            this->getGeomSrc().fVertexLayout,
-                                            newTexCoordOffsets,
-                                            &newColorOffset,
-                                            &newCoverageOffset,
-                                            &newEdgeOffset);
-    int oldColorOffset;
-    int oldCoverageOffset;
-    int oldTexCoordOffsets[GrDrawState::kMaxTexCoords];
-    int oldEdgeOffset;
-
-    GrGLsizei oldStride = VertexSizeAndOffsetsByIdx(
-                                            fHWGeometryState.fVertexLayout,
-                                            oldTexCoordOffsets,
-                                            &oldColorOffset,
-                                            &oldCoverageOffset,
-                                            &oldEdgeOffset);
-    bool indexed = NULL != startIndex;
-
-    int extraVertexOffset;
-    int extraIndexOffset;
-    this->setBuffers(indexed, &extraVertexOffset, &extraIndexOffset);
-
-    GrGLenum scalarType;
-    bool texCoordNorm;
-    if (this->getGeomSrc().fVertexLayout & kTextFormat_VertexLayoutBit) {
-        scalarType = GrGLTextType;
-        texCoordNorm = GR_GL_TEXT_TEXTURE_NORMALIZED;
-    } else {
-        scalarType = GrGLType;
-        texCoordNorm = false;
-    }
-
-    size_t vertexOffset = (*startVertex + extraVertexOffset) * newStride;
-    *startVertex = 0;
-    if (indexed) {
-        *startIndex += extraIndexOffset;
-    }
-
-    // all the Pointers must be set if any of these are true
-    bool allOffsetsChange =  fHWGeometryState.fArrayPtrsDirty ||
-                             vertexOffset != fHWGeometryState.fVertexOffset ||
-                             newStride != oldStride;
-
-    // position and tex coord offsets change if above conditions are true
-    // or the type/normalization changed based on text vs nontext type coords.
-    bool posAndTexChange = allOffsetsChange ||
-                           (((GrGLTextType != GrGLType) || GR_GL_TEXT_TEXTURE_NORMALIZED) &&
-                                (kTextFormat_VertexLayoutBit &
-                                  (fHWGeometryState.fVertexLayout ^
-                                   this->getGeomSrc().fVertexLayout)));
-
-    if (posAndTexChange) {
-        int idx = GrGLProgram::PositionAttributeIdx();
-        GL_CALL(VertexAttribPointer(idx, 2, scalarType, false, newStride, 
-                                  (GrGLvoid*)vertexOffset));
-        fHWGeometryState.fVertexOffset = vertexOffset;
-    }
-
-    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
-        if (newTexCoordOffsets[t] > 0) {
-            GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]);
-            int idx = GrGLProgram::TexCoordAttributeIdx(t);
-            if (oldTexCoordOffsets[t] <= 0) {
-                GL_CALL(EnableVertexAttribArray(idx));
-                GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, 
-                                          newStride, texCoordOffset));
-            } else if (posAndTexChange ||
-                       newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
-                GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm, 
-                                          newStride, texCoordOffset));
-            }
-        } else if (oldTexCoordOffsets[t] > 0) {
-            GL_CALL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
-        }
-    }
-
-    if (newColorOffset > 0) {
-        GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset);
-        int idx = GrGLProgram::ColorAttributeIdx();
-        if (oldColorOffset <= 0) {
-            GL_CALL(EnableVertexAttribArray(idx));
-            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
-                                      true, newStride, colorOffset));
-        } else if (allOffsetsChange || newColorOffset != oldColorOffset) {
-            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
-                                      true, newStride, colorOffset));
-        }
-    } else if (oldColorOffset > 0) {
-        GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
-    }
-
-    if (newCoverageOffset > 0) {
-        GrGLvoid* coverageOffset = (int8_t*)(vertexOffset + newCoverageOffset);
-        int idx = GrGLProgram::CoverageAttributeIdx();
-        if (oldCoverageOffset <= 0) {
-            GL_CALL(EnableVertexAttribArray(idx));
-            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
-                                        true, newStride, coverageOffset));
-        } else if (allOffsetsChange || newCoverageOffset != oldCoverageOffset) {
-            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
-                                        true, newStride, coverageOffset));
-        }
-    } else if (oldCoverageOffset > 0) {
-        GL_CALL(DisableVertexAttribArray(GrGLProgram::CoverageAttributeIdx()));
-    }
-
-    if (newEdgeOffset > 0) {
-        GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset);
-        int idx = GrGLProgram::EdgeAttributeIdx();
-        if (oldEdgeOffset <= 0) {
-            GL_CALL(EnableVertexAttribArray(idx));
-            GL_CALL(VertexAttribPointer(idx, 4, scalarType,
-                                        false, newStride, edgeOffset));
-        } else if (allOffsetsChange || newEdgeOffset != oldEdgeOffset) {
-            GL_CALL(VertexAttribPointer(idx, 4, scalarType,
-                                        false, newStride, edgeOffset));
-        }
-    } else if (oldEdgeOffset > 0) {
-        GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
-    }
-
-    fHWGeometryState.fVertexLayout = this->getGeomSrc().fVertexLayout;
-    fHWGeometryState.fArrayPtrsDirty = false;
-}
-
-void GrGpuGLShaders::buildProgram(GrPrimitiveType type,
-                                  BlendOptFlags blendOpts,
-                                  GrBlendCoeff dstCoeff) {
-    ProgramDesc& desc = fCurrentProgram.fProgramDesc;
-    const GrDrawState& drawState = this->getDrawState();
-
-    // This should already have been caught
-    GrAssert(!(kSkipDraw_BlendOptFlag & blendOpts));
-
-    bool skipCoverage = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
-
-    bool skipColor = SkToBool(blendOpts & (kEmitTransBlack_BlendOptFlag |
-                                           kEmitCoverage_BlendOptFlag));
-
-    // The descriptor is used as a cache key. Thus when a field of the
-    // descriptor will not affect program generation (because of the vertex
-    // layout in use or other descriptor field settings) it should be set
-    // to a canonical value to avoid duplicate programs with different keys.
-
-    // Must initialize all fields or cache will have false negatives!
-    desc.fVertexLayout = this->getGeomSrc().fVertexLayout;
-
-    desc.fEmitsPointSize = kPoints_PrimitiveType == type;
-
-    bool requiresAttributeColors = 
-        !skipColor && SkToBool(desc.fVertexLayout & kColor_VertexLayoutBit);
-    bool requiresAttributeCoverage = 
-        !skipCoverage && SkToBool(desc.fVertexLayout &
-                                  kCoverage_VertexLayoutBit);
-
-    // fColorInput/fCoverageInput records how colors are specified for the.
-    // program. So we strip the bits from the layout to avoid false negatives
-    // when searching for an existing program in the cache.
-    desc.fVertexLayout &= ~(kColor_VertexLayoutBit | kCoverage_VertexLayoutBit);
-
-    desc.fColorFilterXfermode = skipColor ?
-                                SkXfermode::kDst_Mode :
-                                drawState.getColorFilterMode();
-
-    desc.fColorMatrixEnabled = drawState.isStateFlagEnabled(GrDrawState::kColorMatrix_StateBit);
-
-    // no reason to do edge aa or look at per-vertex coverage if coverage is
-    // ignored
-    if (skipCoverage) {
-        desc.fVertexLayout &= ~(kEdge_VertexLayoutBit |
-                                kCoverage_VertexLayoutBit);
-    }
-
-    bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
-    bool colorIsSolidWhite = (blendOpts & kEmitCoverage_BlendOptFlag) ||
-                             (!requiresAttributeColors &&
-                              0xffffffff == drawState.getColor());
-    if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) {
-        desc.fColorInput = ProgramDesc::kTransBlack_ColorInput;
-    } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) {
-        desc.fColorInput = ProgramDesc::kSolidWhite_ColorInput;
-    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) {
-        desc.fColorInput = ProgramDesc::kUniform_ColorInput;
-    } else {
-        desc.fColorInput = ProgramDesc::kAttribute_ColorInput;
-    }
-    
-    bool covIsSolidWhite = !requiresAttributeCoverage &&
-                           0xffffffff == drawState.getCoverage();
-    
-    if (skipCoverage) {
-        desc.fCoverageInput = ProgramDesc::kTransBlack_ColorInput;
-    } else if (covIsSolidWhite) {
-        desc.fCoverageInput = ProgramDesc::kSolidWhite_ColorInput;
-    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeCoverage) {
-        desc.fCoverageInput = ProgramDesc::kUniform_ColorInput;
-    } else {
-        desc.fCoverageInput = ProgramDesc::kAttribute_ColorInput;
-    }
-
-    desc.fEdgeAANumEdges = skipCoverage ? 0 : drawState.getNumAAEdges();
-    desc.fEdgeAAConcave = desc.fEdgeAANumEdges > 0 &&
-                          drawState.isConcaveEdgeAAState();
-
-    int lastEnabledStage = -1;
-
-    if (!skipCoverage && (desc.fVertexLayout &
-                          GrDrawTarget::kEdge_VertexLayoutBit)) {
-        desc.fVertexEdgeType = drawState.getVertexEdgeType();
-    } else {
-        // use canonical value when not set to avoid cache misses
-        desc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
-    }
-
-    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
-        StageDesc& stage = desc.fStages[s];
-
-        stage.fOptFlags = 0;
-        stage.setEnabled(this->isStageEnabled(s));
-
-        bool skip = s < drawState.getFirstCoverageStage() ? skipColor :
-                                                             skipCoverage;
-
-        if (!skip && stage.isEnabled()) {
-            lastEnabledStage = s;
-            const GrGLTexture* texture =
-                static_cast<const GrGLTexture*>(drawState.getTexture(s));
-            GrAssert(NULL != texture);
-            const GrSamplerState& sampler = drawState.getSampler(s);
-            // we matrix to invert when orientation is TopDown, so make sure
-            // we aren't in that case before flagging as identity.
-            if (TextureMatrixIsIdentity(texture, sampler)) {
-                stage.fOptFlags |= StageDesc::kIdentityMatrix_OptFlagBit;
-            } else if (!sampler.getMatrix().hasPerspective()) {
-                stage.fOptFlags |= StageDesc::kNoPerspective_OptFlagBit;
-            }
-            switch (sampler.getSampleMode()) {
-                case GrSamplerState::kNormal_SampleMode:
-                    stage.fCoordMapping = StageDesc::kIdentity_CoordMapping;
-                    break;
-                case GrSamplerState::kRadial_SampleMode:
-                    stage.fCoordMapping = StageDesc::kRadialGradient_CoordMapping;
-                    break;
-                case GrSamplerState::kRadial2_SampleMode:
-                    if (sampler.radial2IsDegenerate()) {
-                        stage.fCoordMapping =
-                            StageDesc::kRadial2GradientDegenerate_CoordMapping;
-                    } else {
-                        stage.fCoordMapping =
-                            StageDesc::kRadial2Gradient_CoordMapping;
-                    }
-                    break;
-                case GrSamplerState::kSweep_SampleMode:
-                    stage.fCoordMapping = StageDesc::kSweepGradient_CoordMapping;
-                    break;
-                default:
-                    GrCrash("Unexpected sample mode!");
-                    break;
-            }
-
-            switch (sampler.getFilter()) {
-                // these both can use a regular texture2D()
-                case GrSamplerState::kNearest_Filter:
-                case GrSamplerState::kBilinear_Filter:
-                    stage.fFetchMode = StageDesc::kSingle_FetchMode;
-                    break;
-                // performs 4 texture2D()s
-                case GrSamplerState::k4x4Downsample_Filter:
-                    stage.fFetchMode = StageDesc::k2x2_FetchMode;
-                    break;
-                // performs fKernelWidth texture2D()s
-                case GrSamplerState::kConvolution_Filter:
-                    stage.fFetchMode = StageDesc::kConvolution_FetchMode;
-                    break;
-                case GrSamplerState::kDilate_Filter:
-                    stage.fFetchMode = StageDesc::kDilate_FetchMode;
-                    break;
-                case GrSamplerState::kErode_Filter:
-                    stage.fFetchMode = StageDesc::kErode_FetchMode;
-                    break;
-                default:
-                    GrCrash("Unexpected filter!");
-                    break;
-            }
-
-            if (sampler.hasTextureDomain()) {
-                GrAssert(GrSamplerState::kClamp_WrapMode ==
-                            sampler.getWrapX() &&
-                         GrSamplerState::kClamp_WrapMode ==
-                            sampler.getWrapY());
-                stage.fOptFlags |= StageDesc::kCustomTextureDomain_OptFlagBit;
-            }
-
-            stage.fInConfigFlags = 0;
-            if (!this->glCaps().textureSwizzleSupport()) {
-                if (GrPixelConfigIsAlphaOnly(texture->config())) {
-                    // if we don't have texture swizzle support then
-                    // the shader must do an alpha smear after reading
-                    // the texture
-                    stage.fInConfigFlags |= StageDesc::kSmearAlpha_InConfigFlag;
-                } else if (sampler.swapsRAndB()) {
-                    stage.fInConfigFlags |= StageDesc::kSwapRAndB_InConfigFlag;
-                }
-            }
-            if (GrPixelConfigIsUnpremultiplied(texture->config())) {
-                // The shader generator assumes that color channels are bytes
-                // when rounding.
-                GrAssert(4 == GrBytesPerPixel(texture->config()));
-                if (kUpOnWrite_DownOnRead_UnpremulConversion ==
-                    fUnpremulConversion) {
-                    stage.fInConfigFlags |=
-                        StageDesc::kMulRGBByAlpha_RoundDown_InConfigFlag;
-                } else {
-                    stage.fInConfigFlags |=
-                        StageDesc::kMulRGBByAlpha_RoundUp_InConfigFlag;
-                }
-            }
-
-            if (sampler.getFilter() == GrSamplerState::kConvolution_Filter ||
-                sampler.getFilter() == GrSamplerState::kDilate_Filter ||
-                sampler.getFilter() == GrSamplerState::kErode_Filter) {
-                stage.fKernelWidth = sampler.getKernelWidth();
-            } else {
-                stage.fKernelWidth = 0;
-            }
-        } else {
-            stage.fOptFlags         = 0;
-            stage.fCoordMapping     = (StageDesc::CoordMapping) 0;
-            stage.fInConfigFlags    = 0;
-            stage.fFetchMode        = (StageDesc::FetchMode) 0;
-            stage.fKernelWidth      = 0;
-        }
-    }
-
-    if (GrPixelConfigIsUnpremultiplied(drawState.getRenderTarget()->config())) {
-        // The shader generator assumes that color channels are bytes
-        // when rounding.
-        GrAssert(4 == GrBytesPerPixel(drawState.getRenderTarget()->config()));
-        if (kUpOnWrite_DownOnRead_UnpremulConversion == fUnpremulConversion) {
-            desc.fOutputConfig =
-                ProgramDesc::kUnpremultiplied_RoundUp_OutputConfig;
-        } else {
-            desc.fOutputConfig =
-                ProgramDesc::kUnpremultiplied_RoundDown_OutputConfig;
-        }
-    } else {
-        desc.fOutputConfig = ProgramDesc::kPremultiplied_OutputConfig;
-    }
-
-    desc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
-
-    // currently the experimental GS will only work with triangle prims
-    // (and it doesn't do anything other than pass through values from
-    // the VS to the FS anyway).
-#if 0 && GR_GL_EXPERIMENTAL_GS
-    desc.fExperimentalGS = this->getCaps().fGeometryShaderSupport;
-#endif
-
-    // we want to avoid generating programs with different "first cov stage"
-    // values when they would compute the same result.
-    // We set field in the desc to kNumStages when either there are no 
-    // coverage stages or the distinction between coverage and color is
-    // immaterial.
-    int firstCoverageStage = GrDrawState::kNumStages;
-    desc.fFirstCoverageStage = GrDrawState::kNumStages;
-    bool hasCoverage = drawState.getFirstCoverageStage() <= lastEnabledStage;
-    if (hasCoverage) {
-        firstCoverageStage = drawState.getFirstCoverageStage();
-    }
-
-    // other coverage inputs
-    if (!hasCoverage) {
-        hasCoverage =
-               desc.fEdgeAANumEdges ||
-               requiresAttributeCoverage ||
-               (desc.fVertexLayout & GrDrawTarget::kEdge_VertexLayoutBit);
-    }
-
-    if (hasCoverage) {
-        // color filter is applied between color/coverage computation
-        if (SkXfermode::kDst_Mode != desc.fColorFilterXfermode) {
-            desc.fFirstCoverageStage = firstCoverageStage;
-        }
-
-        if (this->getCaps().fDualSourceBlendingSupport &&
-            !(blendOpts & (kEmitCoverage_BlendOptFlag |
-                           kCoverageAsAlpha_BlendOptFlag))) {
-            if (kZero_BlendCoeff == dstCoeff) {
-                // write the coverage value to second color
-                desc.fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
-                desc.fFirstCoverageStage = firstCoverageStage;
-            } else if (kSA_BlendCoeff == dstCoeff) {
-                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially 
-                // cover
-                desc.fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
-                desc.fFirstCoverageStage = firstCoverageStage;
-            } else if (kSC_BlendCoeff == dstCoeff) {
-                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially
-                // cover
-                desc.fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
-                desc.fFirstCoverageStage = firstCoverageStage;
-            }
-        }
-    }
-}
diff --git a/src/gpu/gl/GrGpuGLShaders.h b/src/gpu/gl/GrGpuGLShaders.h
deleted file mode 100644
index 39bc974..0000000
--- a/src/gpu/gl/GrGpuGLShaders.h
+++ /dev/null
@@ -1,103 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-
-#ifndef GrGpuGLShaders_DEFINED
-#define GrGpuGLShaders_DEFINED
-
-#include "GrGpuGL.h"
-#include "GrGLProgram.h"
-
-class GrGpuGLProgram;
-
-// Programmable OpenGL or OpenGL ES 2.0
-class GrGpuGLShaders : public GrGpuGL {
-public:
-             GrGpuGLShaders(const GrGLContextInfo& ctxInfo);
-    virtual ~GrGpuGLShaders();
-
-    virtual void abandonResources();
-
-    bool programUnitTest();
-
-protected:
-    // overrides from GrGpu
-    virtual void onResetContext() SK_OVERRIDE;
-    virtual bool flushGraphicsState(GrPrimitiveType type);
-    virtual void setupGeometry(int* startVertex,
-                               int* startIndex,
-                               int vertexCount,
-                               int indexCount);
-    virtual void postDraw();
-
-private:
-
-    // for readability of function impls
-    typedef GrGLProgram::ProgramDesc ProgramDesc;
-    typedef ProgramDesc::StageDesc   StageDesc;
-    typedef GrGLProgram::CachedData  CachedData;
-
-    class ProgramCache;
-
-    // Helpers to make code more readable
-    const GrMatrix& getHWViewMatrix();
-    void recordHWViewMatrix(const GrMatrix& matrix);
-    const GrMatrix& getHWSamplerMatrix(int stage);
-    void recordHWSamplerMatrix(int stage, const GrMatrix& matrix);
-
-    // sets the texture matrix uniform for currently bound program
-    void flushTextureMatrix(int stage);
-
-    // sets the texture domain uniform for currently bound program
-    void flushTextureDomain(int stage);
-
-    // sets the color specified by GrDrawState::setColor()
-    void flushColor(GrColor color);
-
-    // sets the color specified by GrDrawState::setCoverage()
-    void flushCoverage(GrColor color);
-
-    // sets the MVP matrix uniform for currently bound program
-    void flushViewMatrix();
-
-    // flushes the parameters to two point radial gradient
-    void flushRadial2(int stage);
-
-    // flushes the parameters for convolution
-    void flushConvolution(int stage);
-
-    // flushes the normalized texel size
-    void flushTexelSize(int stage);
-
-    // flushes the edges for edge AA
-    void flushEdgeAAData();
-
-    // flushes the color matrix
-    void flushColorMatrix();
-
-    static void DeleteProgram(const GrGLInterface* gl,
-                              CachedData* programData);
-
-    void buildProgram(GrPrimitiveType typeBlend,
-                      BlendOptFlags blendOpts,
-                      GrBlendCoeff dstCoeff);
-
-    ProgramCache*               fProgramCache;
-    CachedData*                 fProgramData;
-    GrGLuint                    fHWProgramID;
-    GrGLProgram                 fCurrentProgram;
-    // If we get rid of fixed function subclass this should move
-    // to the GLCaps struct in parent class
-    GrGLint                     fMaxVertexAttribs;
-
-    typedef GrGpuGL INHERITED;
-};
-
-#endif
-
diff --git a/src/gpu/gl/GrGpuGL_program.cpp b/src/gpu/gl/GrGpuGL_program.cpp
new file mode 100644
index 0000000..d5e8fbd
--- /dev/null
+++ b/src/gpu/gl/GrGpuGL_program.cpp
@@ -0,0 +1,609 @@
+/*
+ * 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 "GrGpuGL.h"
+
+#include "GrEffect.h"
+#include "GrGLEffect.h"
+#include "GrGpuVertex.h"
+
+typedef GrGLUniformManager::UniformHandle UniformHandle;
+static const UniformHandle kInvalidUniformHandle = GrGLUniformManager::kInvalidUniformHandle;
+
+#define SKIP_CACHE_CHECK    true
+#define GR_UINT32_MAX   static_cast<uint32_t>(-1)
+
+GrGpuGL::ProgramCache::ProgramCache(const GrGLContextInfo& gl)
+    : fCount(0)
+    , fCurrLRUStamp(0)
+    , fGL(gl) {
+}
+
+void GrGpuGL::ProgramCache::abandon() {
+    for (int i = 0; i < fCount; ++i) {
+        GrAssert(NULL != fEntries[i].fProgram.get());
+        fEntries[i].fProgram->abandon();
+        fEntries[i].fProgram.reset(NULL);
+    }
+    fCount = 0;
+}
+
+GrGLProgram* GrGpuGL::ProgramCache::getProgram(const ProgramDesc& desc,
+                                               const GrEffectStage* stages[]) {
+    Entry newEntry;
+    newEntry.fKey.setKeyData(desc.asKey());
+
+    Entry* entry = fHashCache.find(newEntry.fKey);
+    if (NULL == entry) {
+        newEntry.fProgram.reset(GrGLProgram::Create(fGL, desc, stages));
+        if (NULL == newEntry.fProgram.get()) {
+            return NULL;
+        }
+        if (fCount < kMaxEntries) {
+            entry = fEntries + fCount;
+            ++fCount;
+        } else {
+            GrAssert(kMaxEntries == fCount);
+            entry = fEntries;
+            for (int i = 1; i < kMaxEntries; ++i) {
+                if (fEntries[i].fLRUStamp < entry->fLRUStamp) {
+                    entry = fEntries + i;
+                }
+            }
+            fHashCache.remove(entry->fKey, entry);
+        }
+        *entry = newEntry;
+        fHashCache.insert(entry->fKey, entry);
+    }
+
+    entry->fLRUStamp = fCurrLRUStamp;
+    if (GR_UINT32_MAX == fCurrLRUStamp) {
+        // wrap around! just trash our LRU, one time hit.
+        for (int i = 0; i < fCount; ++i) {
+            fEntries[i].fLRUStamp = 0;
+        }
+    }
+    ++fCurrLRUStamp;
+    return entry->fProgram;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::abandonResources(){
+    INHERITED::abandonResources();
+    fProgramCache->abandon();
+    fHWProgramID = 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define GL_CALL(X) GR_GL_CALL(this->glInterface(), X)
+
+void GrGpuGL::flushViewMatrix(DrawType type) {
+    const GrGLRenderTarget* rt = static_cast<const GrGLRenderTarget*>(this->getDrawState().getRenderTarget());
+    SkISize viewportSize;
+    const GrGLIRect& viewport = rt->getViewport();
+    viewportSize.set(viewport.fWidth, viewport.fHeight);
+
+    const SkMatrix& vm = this->getDrawState().getViewMatrix();
+
+    if (kStencilPath_DrawType == type) {
+        if (fHWPathMatrixState.fViewMatrix != vm ||
+            fHWPathMatrixState.fRTSize != viewportSize) {
+            // rescale the coords from skia's "device" coords to GL's normalized coords,
+            // and perform a y-flip.
+            SkMatrix m;
+            m.setScale(SkIntToScalar(2) / rt->width(), SkIntToScalar(-2) / rt->height());
+            m.postTranslate(-SK_Scalar1, SK_Scalar1);
+            m.preConcat(vm);
+
+            // GL wants a column-major 4x4.
+            GrGLfloat mv[]  = {
+                // col 0
+                SkScalarToFloat(m[SkMatrix::kMScaleX]),
+                SkScalarToFloat(m[SkMatrix::kMSkewY]),
+                0,
+                SkScalarToFloat(m[SkMatrix::kMPersp0]),
+
+                // col 1
+                SkScalarToFloat(m[SkMatrix::kMSkewX]),
+                SkScalarToFloat(m[SkMatrix::kMScaleY]),
+                0,
+                SkScalarToFloat(m[SkMatrix::kMPersp1]),
+
+                // col 2
+                0, 0, 0, 0,
+
+                // col3
+                SkScalarToFloat(m[SkMatrix::kMTransX]),
+                SkScalarToFloat(m[SkMatrix::kMTransY]),
+                0.0f,
+                SkScalarToFloat(m[SkMatrix::kMPersp2])
+            };
+            GL_CALL(MatrixMode(GR_GL_PROJECTION));
+            GL_CALL(LoadMatrixf(mv));
+            fHWPathMatrixState.fViewMatrix = vm;
+            fHWPathMatrixState.fRTSize = viewportSize;
+        }
+    } else if (!fCurrentProgram->fViewMatrix.cheapEqualTo(vm) ||
+               fCurrentProgram->fViewportSize != viewportSize) {
+        SkMatrix m;
+        m.setAll(
+            SkIntToScalar(2) / viewportSize.fWidth, 0, -SK_Scalar1,
+            0,-SkIntToScalar(2) / viewportSize.fHeight, SK_Scalar1,
+            0, 0, SkMatrix::I()[8]);
+        m.setConcat(m, vm);
+
+        // ES doesn't allow you to pass true to the transpose param,
+        // so do our own transpose
+        GrGLfloat mt[]  = {
+            SkScalarToFloat(m[SkMatrix::kMScaleX]),
+            SkScalarToFloat(m[SkMatrix::kMSkewY]),
+            SkScalarToFloat(m[SkMatrix::kMPersp0]),
+            SkScalarToFloat(m[SkMatrix::kMSkewX]),
+            SkScalarToFloat(m[SkMatrix::kMScaleY]),
+            SkScalarToFloat(m[SkMatrix::kMPersp1]),
+            SkScalarToFloat(m[SkMatrix::kMTransX]),
+            SkScalarToFloat(m[SkMatrix::kMTransY]),
+            SkScalarToFloat(m[SkMatrix::kMPersp2])
+        };
+        fCurrentProgram->fUniformManager.setMatrix3f(
+                                            fCurrentProgram->fUniformHandles.fViewMatrixUni,
+                                            mt);
+        fCurrentProgram->fViewMatrix = vm;
+        fCurrentProgram->fViewportSize = viewportSize;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void GrGpuGL::flushColor(GrColor color) {
+    const ProgramDesc& desc = fCurrentProgram->getDesc();
+    const GrDrawState& drawState = this->getDrawState();
+
+    if (this->getVertexLayout() & GrDrawState::kColor_VertexLayoutBit) {
+        // color will be specified per-vertex as an attribute
+        // invalidate the const vertex attrib color
+        fHWConstAttribColor = GrColor_ILLEGAL;
+    } else {
+        switch (desc.fColorInput) {
+            case ProgramDesc::kAttribute_ColorInput:
+                if (fHWConstAttribColor != color) {
+                    // OpenGL ES only supports the float varieties of glVertexAttrib
+                    GrGLfloat c[4];
+                    GrColorToRGBAFloat(color, c);
+                    GL_CALL(VertexAttrib4fv(GrGLProgram::ColorAttributeIdx(), c));
+                    fHWConstAttribColor = color;
+                }
+                break;
+            case ProgramDesc::kUniform_ColorInput:
+                if (fCurrentProgram->fColor != color) {
+                    // OpenGL ES doesn't support unsigned byte varieties of glUniform
+                    GrGLfloat c[4];
+                    GrColorToRGBAFloat(color, c);
+                    GrAssert(kInvalidUniformHandle !=  fCurrentProgram->fUniformHandles.fColorUni);
+                    fCurrentProgram->fUniformManager.set4fv(
+                                                        fCurrentProgram->fUniformHandles.fColorUni,
+                                                        0, 1, c);
+                    fCurrentProgram->fColor = color;
+                }
+                break;
+            case ProgramDesc::kSolidWhite_ColorInput:
+            case ProgramDesc::kTransBlack_ColorInput:
+                break;
+            default:
+                GrCrash("Unknown color type.");
+        }
+    }
+    UniformHandle filterColorUni = fCurrentProgram->fUniformHandles.fColorFilterUni;
+    if (kInvalidUniformHandle != filterColorUni &&
+        fCurrentProgram->fColorFilterColor != drawState.getColorFilterColor()) {
+        GrGLfloat c[4];
+        GrColorToRGBAFloat(drawState.getColorFilterColor(), c);
+        fCurrentProgram->fUniformManager.set4fv(filterColorUni, 0, 1, c);
+        fCurrentProgram->fColorFilterColor = drawState.getColorFilterColor();
+    }
+}
+
+void GrGpuGL::flushCoverage(GrColor coverage) {
+    const ProgramDesc& desc = fCurrentProgram->getDesc();
+    // const GrDrawState& drawState = this->getDrawState();
+
+
+    if (this->getVertexLayout() & GrDrawState::kCoverage_VertexLayoutBit) {
+        // coverage will be specified per-vertex as an attribute
+        // invalidate the const vertex attrib coverage
+        fHWConstAttribCoverage = GrColor_ILLEGAL;
+    } else {
+        switch (desc.fCoverageInput) {
+            case ProgramDesc::kAttribute_ColorInput:
+                if (fHWConstAttribCoverage != coverage) {
+                    // OpenGL ES only supports the float varieties of
+                    // glVertexAttrib
+                    GrGLfloat c[4];
+                    GrColorToRGBAFloat(coverage, c);
+                    GL_CALL(VertexAttrib4fv(GrGLProgram::CoverageAttributeIdx(),
+                                            c));
+                    fHWConstAttribCoverage = coverage;
+                }
+                break;
+            case ProgramDesc::kUniform_ColorInput:
+                if (fCurrentProgram->fCoverage != coverage) {
+                    // OpenGL ES doesn't support unsigned byte varieties of
+                    // glUniform
+                    GrGLfloat c[4];
+                    GrColorToRGBAFloat(coverage, c);
+                    GrAssert(kInvalidUniformHandle !=
+                             fCurrentProgram->fUniformHandles.fCoverageUni);
+                    fCurrentProgram->fUniformManager.set4fv(
+                                                    fCurrentProgram->fUniformHandles.fCoverageUni,
+                                                    0, 1, c);
+                    fCurrentProgram->fCoverage = coverage;
+                }
+                break;
+            case ProgramDesc::kSolidWhite_ColorInput:
+            case ProgramDesc::kTransBlack_ColorInput:
+                break;
+            default:
+                GrCrash("Unknown coverage type.");
+        }
+    }
+}
+
+bool GrGpuGL::flushGraphicsState(DrawType type) {
+    const GrDrawState& drawState = this->getDrawState();
+
+    // GrGpu::setupClipAndFlushState should have already checked this
+    // and bailed if not true.
+    GrAssert(NULL != drawState.getRenderTarget());
+
+    if (kStencilPath_DrawType != type) {
+        this->flushMiscFixedFunctionState();
+
+        GrBlendCoeff srcCoeff;
+        GrBlendCoeff dstCoeff;
+        BlendOptFlags blendOpts = this->getBlendOpts(false, &srcCoeff, &dstCoeff);
+        if (kSkipDraw_BlendOptFlag & blendOpts) {
+            return false;
+        }
+
+        const GrEffectStage* stages[GrDrawState::kNumStages];
+        for (int i = 0; i < GrDrawState::kNumStages; ++i) {
+            stages[i] = drawState.isStageEnabled(i) ? &drawState.getStage(i) : NULL;
+        }
+        GrGLProgram::Desc desc;
+        this->buildProgram(kDrawPoints_DrawType == type, blendOpts, dstCoeff, &desc);
+
+        fCurrentProgram.reset(fProgramCache->getProgram(desc, stages));
+        if (NULL == fCurrentProgram.get()) {
+            GrAssert(!"Failed to create program!");
+            return false;
+        }
+        fCurrentProgram.get()->ref();
+
+        if (fHWProgramID != fCurrentProgram->fProgramID) {
+            GL_CALL(UseProgram(fCurrentProgram->fProgramID));
+            fHWProgramID = fCurrentProgram->fProgramID;
+        }
+        fCurrentProgram->overrideBlend(&srcCoeff, &dstCoeff);
+        this->flushBlend(kDrawLines_DrawType == type, srcCoeff, dstCoeff);
+
+        GrColor color;
+        GrColor coverage;
+        if (blendOpts & kEmitTransBlack_BlendOptFlag) {
+            color = 0;
+            coverage = 0;
+        } else if (blendOpts & kEmitCoverage_BlendOptFlag) {
+            color = 0xffffffff;
+            coverage = drawState.getCoverage();
+        } else {
+            color = drawState.getColor();
+            coverage = drawState.getCoverage();
+        }
+        this->flushColor(color);
+        this->flushCoverage(coverage);
+
+        fCurrentProgram->setData(this);
+    }
+    this->flushStencil(type);
+    this->flushViewMatrix(type);
+    this->flushScissor();
+    this->flushAAState(type);
+
+    GrIRect* devRect = NULL;
+    GrIRect devClipBounds;
+    if (drawState.isClipState()) {
+        this->getClip()->getConservativeBounds(drawState.getRenderTarget(), &devClipBounds);
+        devRect = &devClipBounds;
+    }
+    // This must come after textures are flushed because a texture may need
+    // to be msaa-resolved (which will modify bound FBO state).
+    this->flushRenderTarget(devRect);
+
+    return true;
+}
+
+#if GR_TEXT_SCALAR_IS_USHORT
+    #define TEXT_COORDS_GL_TYPE          GR_GL_UNSIGNED_SHORT
+    #define TEXT_COORDS_ARE_NORMALIZED   1
+#elif GR_TEXT_SCALAR_IS_FLOAT
+    #define TEXT_COORDS_GL_TYPE          GR_GL_FLOAT
+    #define TEXT_COORDS_ARE_NORMALIZED   0
+#elif GR_TEXT_SCALAR_IS_FIXED
+    #define TEXT_COORDS_GL_TYPE          GR_GL_FIXED
+    #define TEXT_COORDS_ARE_NORMALIZED   0
+#else
+    #error "unknown GR_TEXT_SCALAR type"
+#endif
+
+void GrGpuGL::setupGeometry(const DrawInfo& info, int* startIndexOffset) {
+
+    int newColorOffset;
+    int newCoverageOffset;
+    int newTexCoordOffsets[GrDrawState::kMaxTexCoords];
+    int newEdgeOffset;
+
+    GrVertexLayout currLayout = this->getVertexLayout();
+
+    GrGLsizei newStride = GrDrawState::VertexSizeAndOffsetsByIdx(currLayout,
+                                                                 newTexCoordOffsets,
+                                                                 &newColorOffset,
+                                                                 &newCoverageOffset,
+                                                                 &newEdgeOffset);
+    int oldColorOffset;
+    int oldCoverageOffset;
+    int oldTexCoordOffsets[GrDrawState::kMaxTexCoords];
+    int oldEdgeOffset;
+
+    GrGLsizei oldStride = GrDrawState::VertexSizeAndOffsetsByIdx(fHWGeometryState.fVertexLayout,
+                                                                 oldTexCoordOffsets,
+                                                                 &oldColorOffset,
+                                                                 &oldCoverageOffset,
+                                                                 &oldEdgeOffset);
+
+    int extraVertexOffset;
+    this->setBuffers(info.isIndexed(), &extraVertexOffset, startIndexOffset);
+
+    GrGLenum scalarType;
+    bool texCoordNorm;
+    if (currLayout & GrDrawState::kTextFormat_VertexLayoutBit) {
+        scalarType = TEXT_COORDS_GL_TYPE;
+        texCoordNorm = SkToBool(TEXT_COORDS_ARE_NORMALIZED);
+    } else {
+        scalarType = GR_GL_FLOAT;
+        texCoordNorm = false;
+    }
+
+    size_t vertexOffset = (info.startVertex() + extraVertexOffset) * newStride;
+
+    // all the Pointers must be set if any of these are true
+    bool allOffsetsChange =  fHWGeometryState.fArrayPtrsDirty ||
+                             vertexOffset != fHWGeometryState.fVertexOffset ||
+                             newStride != oldStride;
+
+    // position and tex coord offsets change if above conditions are true
+    // or the type/normalization changed based on text vs nontext type coords.
+    bool posAndTexChange = allOffsetsChange ||
+                           (((TEXT_COORDS_GL_TYPE != GR_GL_FLOAT) || TEXT_COORDS_ARE_NORMALIZED) &&
+                                (GrDrawState::kTextFormat_VertexLayoutBit &
+                                  (fHWGeometryState.fVertexLayout ^ currLayout)));
+
+    if (posAndTexChange) {
+        int idx = GrGLProgram::PositionAttributeIdx();
+        GL_CALL(VertexAttribPointer(idx, 2, scalarType, false, newStride,
+                                  (GrGLvoid*)vertexOffset));
+        fHWGeometryState.fVertexOffset = vertexOffset;
+    }
+
+    for (int t = 0; t < GrDrawState::kMaxTexCoords; ++t) {
+        if (newTexCoordOffsets[t] > 0) {
+            GrGLvoid* texCoordOffset = (GrGLvoid*)(vertexOffset + newTexCoordOffsets[t]);
+            int idx = GrGLProgram::TexCoordAttributeIdx(t);
+            if (oldTexCoordOffsets[t] <= 0) {
+                GL_CALL(EnableVertexAttribArray(idx));
+                GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm,
+                                          newStride, texCoordOffset));
+            } else if (posAndTexChange ||
+                       newTexCoordOffsets[t] != oldTexCoordOffsets[t]) {
+                GL_CALL(VertexAttribPointer(idx, 2, scalarType, texCoordNorm,
+                                          newStride, texCoordOffset));
+            }
+        } else if (oldTexCoordOffsets[t] > 0) {
+            GL_CALL(DisableVertexAttribArray(GrGLProgram::TexCoordAttributeIdx(t)));
+        }
+    }
+
+    if (newColorOffset > 0) {
+        GrGLvoid* colorOffset = (int8_t*)(vertexOffset + newColorOffset);
+        int idx = GrGLProgram::ColorAttributeIdx();
+        if (oldColorOffset <= 0) {
+            GL_CALL(EnableVertexAttribArray(idx));
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                      true, newStride, colorOffset));
+        } else if (allOffsetsChange || newColorOffset != oldColorOffset) {
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                      true, newStride, colorOffset));
+        }
+    } else if (oldColorOffset > 0) {
+        GL_CALL(DisableVertexAttribArray(GrGLProgram::ColorAttributeIdx()));
+    }
+
+    if (newCoverageOffset > 0) {
+        GrGLvoid* coverageOffset = (int8_t*)(vertexOffset + newCoverageOffset);
+        int idx = GrGLProgram::CoverageAttributeIdx();
+        if (oldCoverageOffset <= 0) {
+            GL_CALL(EnableVertexAttribArray(idx));
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                        true, newStride, coverageOffset));
+        } else if (allOffsetsChange || newCoverageOffset != oldCoverageOffset) {
+            GL_CALL(VertexAttribPointer(idx, 4, GR_GL_UNSIGNED_BYTE,
+                                        true, newStride, coverageOffset));
+        }
+    } else if (oldCoverageOffset > 0) {
+        GL_CALL(DisableVertexAttribArray(GrGLProgram::CoverageAttributeIdx()));
+    }
+
+    if (newEdgeOffset > 0) {
+        GrGLvoid* edgeOffset = (int8_t*)(vertexOffset + newEdgeOffset);
+        int idx = GrGLProgram::EdgeAttributeIdx();
+        if (oldEdgeOffset <= 0) {
+            GL_CALL(EnableVertexAttribArray(idx));
+            GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+                                        false, newStride, edgeOffset));
+        } else if (allOffsetsChange || newEdgeOffset != oldEdgeOffset) {
+            GL_CALL(VertexAttribPointer(idx, 4, scalarType,
+                                        false, newStride, edgeOffset));
+        }
+    } else if (oldEdgeOffset > 0) {
+        GL_CALL(DisableVertexAttribArray(GrGLProgram::EdgeAttributeIdx()));
+    }
+
+    fHWGeometryState.fVertexLayout = currLayout;
+    fHWGeometryState.fArrayPtrsDirty = false;
+}
+
+void GrGpuGL::buildProgram(bool isPoints,
+                           BlendOptFlags blendOpts,
+                           GrBlendCoeff dstCoeff,
+                           ProgramDesc* desc) {
+    const GrDrawState& drawState = this->getDrawState();
+
+    // This should already have been caught
+    GrAssert(!(kSkipDraw_BlendOptFlag & blendOpts));
+
+    bool skipCoverage = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+
+    bool skipColor = SkToBool(blendOpts & (kEmitTransBlack_BlendOptFlag |
+                                           kEmitCoverage_BlendOptFlag));
+
+    // The descriptor is used as a cache key. Thus when a field of the
+    // descriptor will not affect program generation (because of the vertex
+    // layout in use or other descriptor field settings) it should be set
+    // to a canonical value to avoid duplicate programs with different keys.
+
+    // Must initialize all fields or cache will have false negatives!
+    desc->fVertexLayout = this->getVertexLayout();
+
+    desc->fEmitsPointSize = isPoints;
+
+    bool requiresAttributeColors = !skipColor &&
+                                   SkToBool(desc->fVertexLayout & GrDrawState::kColor_VertexLayoutBit);
+    bool requiresAttributeCoverage = !skipCoverage &&
+                                     SkToBool(desc->fVertexLayout & GrDrawState::kCoverage_VertexLayoutBit);
+
+    // fColorInput/fCoverageInput records how colors are specified for the.
+    // program. So we strip the bits from the layout to avoid false negatives
+    // when searching for an existing program in the cache.
+    desc->fVertexLayout &= ~(GrDrawState::kColor_VertexLayoutBit | GrDrawState::kCoverage_VertexLayoutBit);
+
+    desc->fColorFilterXfermode = skipColor ?
+                                SkXfermode::kDst_Mode :
+                                drawState.getColorFilterMode();
+
+    // no reason to do edge aa or look at per-vertex coverage if coverage is
+    // ignored
+    if (skipCoverage) {
+        desc->fVertexLayout &= ~(GrDrawState::kEdge_VertexLayoutBit | GrDrawState::kCoverage_VertexLayoutBit);
+    }
+
+    bool colorIsTransBlack = SkToBool(blendOpts & kEmitTransBlack_BlendOptFlag);
+    bool colorIsSolidWhite = (blendOpts & kEmitCoverage_BlendOptFlag) ||
+                             (!requiresAttributeColors && 0xffffffff == drawState.getColor());
+    if (GR_AGGRESSIVE_SHADER_OPTS && colorIsTransBlack) {
+        desc->fColorInput = ProgramDesc::kTransBlack_ColorInput;
+    } else if (GR_AGGRESSIVE_SHADER_OPTS && colorIsSolidWhite) {
+        desc->fColorInput = ProgramDesc::kSolidWhite_ColorInput;
+    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeColors) {
+        desc->fColorInput = ProgramDesc::kUniform_ColorInput;
+    } else {
+        desc->fColorInput = ProgramDesc::kAttribute_ColorInput;
+    }
+
+    bool covIsSolidWhite = !requiresAttributeCoverage && 0xffffffff == drawState.getCoverage();
+
+    if (skipCoverage) {
+        desc->fCoverageInput = ProgramDesc::kTransBlack_ColorInput;
+    } else if (covIsSolidWhite) {
+        desc->fCoverageInput = ProgramDesc::kSolidWhite_ColorInput;
+    } else if (GR_GL_NO_CONSTANT_ATTRIBUTES && !requiresAttributeCoverage) {
+        desc->fCoverageInput = ProgramDesc::kUniform_ColorInput;
+    } else {
+        desc->fCoverageInput = ProgramDesc::kAttribute_ColorInput;
+    }
+
+    int lastEnabledStage = -1;
+
+    if (!skipCoverage && (desc->fVertexLayout &GrDrawState::kEdge_VertexLayoutBit)) {
+        desc->fVertexEdgeType = drawState.getVertexEdgeType();
+        desc->fDiscardIfOutsideEdge = drawState.getStencil().doesWrite();
+    } else {
+        // Use canonical values when edge-aa is not enabled to avoid program cache misses.
+        desc->fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+        desc->fDiscardIfOutsideEdge = false;
+    }
+
+    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+
+        bool skip = s < drawState.getFirstCoverageStage() ? skipColor : skipCoverage;
+        if (!skip && drawState.isStageEnabled(s)) {
+            lastEnabledStage = s;
+            const GrEffectRef& effect = *drawState.getStage(s).getEffect();
+            const GrBackendEffectFactory& factory = effect->getFactory();
+            desc->fEffectKeys[s] = factory.glEffectKey(drawState.getStage(s), this->glCaps());
+        } else {
+            desc->fEffectKeys[s] = 0;
+        }
+    }
+
+    desc->fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+
+    // Currently the experimental GS will only work with triangle prims (and it doesn't do anything
+    // other than pass through values from the VS to the FS anyway).
+#if 0 && GR_GL_EXPERIMENTAL_GS
+    desc->fExperimentalGS = this->getCaps().fGeometryShaderSupport;
+#endif
+
+    // We want to avoid generating programs with different "first cov stage" values when they would
+    // compute the same result. We set field in the desc to kNumStages when either there are no
+    // coverage stages or the distinction between coverage and color is immaterial.
+    int firstCoverageStage = GrDrawState::kNumStages;
+    desc->fFirstCoverageStage = GrDrawState::kNumStages;
+    bool hasCoverage = drawState.getFirstCoverageStage() <= lastEnabledStage;
+    if (hasCoverage) {
+        firstCoverageStage = drawState.getFirstCoverageStage();
+    }
+
+    // other coverage inputs
+    if (!hasCoverage) {
+        hasCoverage = requiresAttributeCoverage ||
+                      (desc->fVertexLayout & GrDrawState::kEdge_VertexLayoutBit);
+    }
+
+    if (hasCoverage) {
+        // color filter is applied between color/coverage computation
+        if (SkXfermode::kDst_Mode != desc->fColorFilterXfermode) {
+            desc->fFirstCoverageStage = firstCoverageStage;
+        }
+
+        if (this->getCaps().dualSourceBlendingSupport() &&
+            !(blendOpts & (kEmitCoverage_BlendOptFlag | kCoverageAsAlpha_BlendOptFlag))) {
+            if (kZero_GrBlendCoeff == dstCoeff) {
+                // write the coverage value to second color
+                desc->fDualSrcOutput =  ProgramDesc::kCoverage_DualSrcOutput;
+                desc->fFirstCoverageStage = firstCoverageStage;
+            } else if (kSA_GrBlendCoeff == dstCoeff) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
+                desc->fDualSrcOutput = ProgramDesc::kCoverageISA_DualSrcOutput;
+                desc->fFirstCoverageStage = firstCoverageStage;
+            } else if (kSC_GrBlendCoeff == dstCoeff) {
+                // SA dst coeff becomes 1-(1-SA)*coverage when dst is partially covered.
+                desc->fDualSrcOutput = ProgramDesc::kCoverageISC_DualSrcOutput;
+                desc->fFirstCoverageStage = firstCoverageStage;
+            }
+        }
+    }
+}
diff --git a/src/gpu/gl/SkGLContext.cpp b/src/gpu/gl/SkGLContext.cpp
index 525afe8..c5069b2 100644
--- a/src/gpu/gl/SkGLContext.cpp
+++ b/src/gpu/gl/SkGLContext.cpp
@@ -6,13 +6,25 @@
  * found in the LICENSE file.
  */
 #include "gl/SkGLContext.h"
+#include "gl/GrGLUtil.h"
+
+SK_DEFINE_INST_COUNT(SkGLContext)
 
 SkGLContext::SkGLContext()
     : fFBO(0)
+    , fColorBufferID(0)
+    , fDepthStencilBufferID(0)
     , fGL(NULL) {
 }
 
 SkGLContext::~SkGLContext() {
+
+    if (fGL) {
+        SK_GL(*this, DeleteFramebuffers(1, &fFBO));
+        SK_GL(*this, DeleteRenderbuffers(1, &fColorBufferID));
+        SK_GL(*this, DeleteRenderbuffers(1, &fDepthStencilBufferID));
+    }
+
     SkSafeUnref(fGL);
 }
 
@@ -42,15 +54,12 @@
             error = SK_GL(*this, GetError());
         } while (GR_GL_NO_ERROR != error);
 
-        GrGLuint cbID;
-        GrGLuint dsID;
-
         GrGLBinding bindingInUse = GrGLGetBindingInUse(this->gl());
 
         SK_GL(*this, GenFramebuffers(1, &fFBO));
         SK_GL(*this, BindFramebuffer(GR_GL_FRAMEBUFFER, fFBO));
-        SK_GL(*this, GenRenderbuffers(1, &cbID));
-        SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, cbID));
+        SK_GL(*this, GenRenderbuffers(1, &fColorBufferID));
+        SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, fColorBufferID));
         if (kES2_GrGLBinding == bindingInUse) {
             SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
                                              GR_GL_RGBA8,
@@ -62,17 +71,17 @@
         }
         SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                              GR_GL_COLOR_ATTACHMENT0,
-                                             GR_GL_RENDERBUFFER, 
-                                             cbID));
-        SK_GL(*this, GenRenderbuffers(1, &dsID));
-        SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, dsID));
+                                             GR_GL_RENDERBUFFER,
+                                             fColorBufferID));
+        SK_GL(*this, GenRenderbuffers(1, &fDepthStencilBufferID));
+        SK_GL(*this, BindRenderbuffer(GR_GL_RENDERBUFFER, fDepthStencilBufferID));
 
         // Some drivers that support packed depth stencil will only succeed
         // in binding a packed format an FBO. However, we can't rely on packed
         // depth stencil being available.
         bool supportsPackedDepthStencil;
         if (kES2_GrGLBinding == bindingInUse) {
-            supportsPackedDepthStencil = 
+            supportsPackedDepthStencil =
                     this->hasExtension("GL_OES_packed_depth_stencil");
         } else {
             supportsPackedDepthStencil = version >= GR_GL_VER(3,0) ||
@@ -83,7 +92,7 @@
         if (supportsPackedDepthStencil) {
             // ES2 requires sized internal formats for RenderbufferStorage
             // On Desktop we let the driver decide.
-            GrGLenum format = kES2_GrGLBinding == bindingInUse ? 
+            GrGLenum format = kES2_GrGLBinding == bindingInUse ?
                                     GR_GL_DEPTH24_STENCIL8 :
                                     GR_GL_DEPTH_STENCIL;
             SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
@@ -92,9 +101,9 @@
             SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                                  GR_GL_DEPTH_ATTACHMENT,
                                                  GR_GL_RENDERBUFFER,
-                                                 dsID));
+                                                 fDepthStencilBufferID));
         } else {
-            GrGLenum format = kES2_GrGLBinding == bindingInUse ? 
+            GrGLenum format = kES2_GrGLBinding == bindingInUse ?
                                     GR_GL_STENCIL_INDEX8 :
                                     GR_GL_STENCIL_INDEX;
             SK_GL(*this, RenderbufferStorage(GR_GL_RENDERBUFFER,
@@ -104,11 +113,11 @@
         SK_GL(*this, FramebufferRenderbuffer(GR_GL_FRAMEBUFFER,
                                              GR_GL_STENCIL_ATTACHMENT,
                                              GR_GL_RENDERBUFFER,
-                                             dsID));
+                                             fDepthStencilBufferID));
         SK_GL(*this, Viewport(0, 0, width, height));
         SK_GL(*this, ClearStencil(0));
         SK_GL(*this, Clear(GR_GL_STENCIL_BUFFER_BIT));
-        
+
         error = SK_GL(*this, GetError());
         GrGLenum status =
             SK_GL(*this, CheckFramebufferStatus(GR_GL_FRAMEBUFFER));
@@ -116,6 +125,8 @@
         if (GR_GL_FRAMEBUFFER_COMPLETE != status ||
             GR_GL_NO_ERROR != error) {
             fFBO = 0;
+            fColorBufferID = 0;
+            fDepthStencilBufferID = 0;
             fGL->unref();
             fGL = NULL;
             this->destroyGLContext();
diff --git a/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
new file mode 100644
index 0000000..10792d9
--- /dev/null
+++ b/src/gpu/gl/android/GrGLCreateNativeInterface_android.cpp
@@ -0,0 +1,139 @@
+// Modified from chromium/src/webkit/glue/gl_bindings_skia_cmd_buffer.cc
+
+// Copyright (c) 2011 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 "gl/GrGLInterface.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <EGL/egl.h>
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+    static SkAutoTUnref<GrGLInterface> glInterface;
+    if (!glInterface.get()) {
+        GrGLInterface* interface = new GrGLInterface;
+        glInterface.reset(interface);
+        interface->fBindingsExported = kES2_GrGLBinding;
+        interface->fActiveTexture = glActiveTexture;
+        interface->fAttachShader = glAttachShader;
+        interface->fBindAttribLocation = glBindAttribLocation;
+        interface->fBindBuffer = glBindBuffer;
+        interface->fBindTexture = glBindTexture;
+        interface->fBlendColor = glBlendColor;
+        interface->fBlendFunc = glBlendFunc;
+        interface->fBufferData = glBufferData;
+        interface->fBufferSubData = glBufferSubData;
+        interface->fClear = glClear;
+        interface->fClearColor = glClearColor;
+        interface->fClearStencil = glClearStencil;
+        interface->fColorMask = glColorMask;
+        interface->fCompileShader = glCompileShader;
+        interface->fCompressedTexImage2D = glCompressedTexImage2D;
+        interface->fCreateProgram = glCreateProgram;
+        interface->fCreateShader = glCreateShader;
+        interface->fCullFace = glCullFace;
+        interface->fDeleteBuffers = glDeleteBuffers;
+        interface->fDeleteProgram = glDeleteProgram;
+        interface->fDeleteShader = glDeleteShader;
+        interface->fDeleteTextures = glDeleteTextures;
+        interface->fDepthMask = glDepthMask;
+        interface->fDisable = glDisable;
+        interface->fDisableVertexAttribArray = glDisableVertexAttribArray;
+        interface->fDrawArrays = glDrawArrays;
+        interface->fDrawElements = glDrawElements;
+        interface->fEnable = glEnable;
+        interface->fEnableVertexAttribArray = glEnableVertexAttribArray;
+        interface->fFinish = glFinish;
+        interface->fFlush = glFlush;
+        interface->fFrontFace = glFrontFace;
+        interface->fGenBuffers = glGenBuffers;
+        interface->fGenTextures = glGenTextures;
+        interface->fGetBufferParameteriv = glGetBufferParameteriv;
+        interface->fGetError = glGetError;
+        interface->fGetIntegerv = glGetIntegerv;
+        interface->fGetProgramInfoLog = glGetProgramInfoLog;
+        interface->fGetProgramiv = glGetProgramiv;
+        interface->fGetShaderInfoLog = glGetShaderInfoLog;
+        interface->fGetShaderiv = glGetShaderiv;
+        interface->fGetString = glGetString;
+        interface->fGetUniformLocation = glGetUniformLocation;
+        interface->fLineWidth = glLineWidth;
+        interface->fLinkProgram = glLinkProgram;
+        interface->fPixelStorei = glPixelStorei;
+        interface->fReadPixels = glReadPixels;
+        interface->fScissor = glScissor;
+#if GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE
+        interface->fShaderSource = (GrGLShaderSourceProc) glShaderSource;
+#else
+        interface->fShaderSource = glShaderSource;
+#endif
+        interface->fStencilFunc = glStencilFunc;
+        interface->fStencilFuncSeparate = glStencilFuncSeparate;
+        interface->fStencilMask = glStencilMask;
+        interface->fStencilMaskSeparate = glStencilMaskSeparate;
+        interface->fStencilOp = glStencilOp;
+        interface->fStencilOpSeparate = glStencilOpSeparate;
+        interface->fTexImage2D = glTexImage2D;
+        interface->fTexParameteri = glTexParameteri;
+        interface->fTexParameteriv = glTexParameteriv;
+        interface->fTexSubImage2D = glTexSubImage2D;
+#if GL_ARB_texture_storage
+        interface->fTexStorage2D = glTexStorage2D;
+#elif GL_EXT_texture_storage
+        interface->fTexStorage2D = glTexStorage2DEXT;
+#else
+        interface->fTexStorage2D = (GrGLTexStorage2DProc) eglGetProcAddress("glTexStorage2DEXT");
+#endif
+        interface->fUniform1f = glUniform1f;
+        interface->fUniform1i = glUniform1i;
+        interface->fUniform1fv = glUniform1fv;
+        interface->fUniform1iv = glUniform1iv;
+        interface->fUniform2f = glUniform2f;
+        interface->fUniform2i = glUniform2i;
+        interface->fUniform2fv = glUniform2fv;
+        interface->fUniform2iv = glUniform2iv;
+        interface->fUniform3f = glUniform3f;
+        interface->fUniform3i = glUniform3i;
+        interface->fUniform3fv = glUniform3fv;
+        interface->fUniform3iv = glUniform3iv;
+        interface->fUniform4f = glUniform4f;
+        interface->fUniform4i = glUniform4i;
+        interface->fUniform4fv = glUniform4fv;
+        interface->fUniform4iv = glUniform4iv;
+        interface->fUniformMatrix2fv = glUniformMatrix2fv;
+        interface->fUniformMatrix3fv = glUniformMatrix3fv;
+        interface->fUniformMatrix4fv = glUniformMatrix4fv;
+        interface->fUseProgram = glUseProgram;
+        interface->fVertexAttrib4fv = glVertexAttrib4fv;
+        interface->fVertexAttribPointer = glVertexAttribPointer;
+        interface->fViewport = glViewport;
+        interface->fBindFramebuffer = glBindFramebuffer;
+        interface->fBindRenderbuffer = glBindRenderbuffer;
+        interface->fCheckFramebufferStatus = glCheckFramebufferStatus;
+        interface->fDeleteFramebuffers = glDeleteFramebuffers;
+        interface->fDeleteRenderbuffers = glDeleteRenderbuffers;
+        interface->fFramebufferRenderbuffer = glFramebufferRenderbuffer;
+        interface->fFramebufferTexture2D = glFramebufferTexture2D;
+        interface->fGenFramebuffers = glGenFramebuffers;
+        interface->fGenRenderbuffers = glGenRenderbuffers;
+        interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
+        interface->fGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
+        interface->fRenderbufferStorage = glRenderbufferStorage;
+#if GL_OES_mapbuffer
+        interface->fMapBuffer = glMapBufferOES;
+        interface->fUnmapBuffer = glUnmapBufferOES;
+#else
+        interface->fMapBuffer = (GrGLMapBufferProc) eglGetProcAddress("glMapBufferOES");
+        interface->fUnmapBuffer = (GrGLUnmapBufferProc) eglGetProcAddress("glUnmapBufferOES");
+#endif
+    }
+    glInterface.get()->ref();
+    return glInterface.get();
+}
diff --git a/src/gpu/gl/android/SkNativeGLContext_android.cpp b/src/gpu/gl/android/SkNativeGLContext_android.cpp
new file mode 100644
index 0000000..f21eed8
--- /dev/null
+++ b/src/gpu/gl/android/SkNativeGLContext_android.cpp
@@ -0,0 +1,104 @@
+
+/*
+ * 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 "gl/SkNativeGLContext.h"
+
+SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
+    fOldEGLContext = eglGetCurrentContext();
+    fOldDisplay = eglGetCurrentDisplay();
+    fOldSurface = eglGetCurrentSurface(EGL_DRAW);
+
+}
+
+SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
+    if (NULL != fOldDisplay) {
+        eglMakeCurrent(fOldDisplay, fOldSurface, fOldSurface, fOldEGLContext);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkNativeGLContext::SkNativeGLContext()
+    : fContext(EGL_NO_CONTEXT)
+    , fDisplay(EGL_NO_DISPLAY)
+    , fSurface(EGL_NO_SURFACE) {
+}
+
+SkNativeGLContext::~SkNativeGLContext() {
+    this->destroyGLContext();
+}
+
+void SkNativeGLContext::destroyGLContext() {
+    if (fDisplay) {
+        eglMakeCurrent(fDisplay, 0, 0, 0);
+
+        if (fContext) {
+            eglDestroyContext(fDisplay, fContext);
+            fContext = EGL_NO_CONTEXT;
+        }
+
+        if (fSurface) {
+            eglDestroySurface(fDisplay, fSurface);
+            fSurface = EGL_NO_SURFACE;
+        }
+
+        //TODO should we close the display?
+        fDisplay = EGL_NO_DISPLAY;
+    }
+}
+
+const GrGLInterface* SkNativeGLContext::createGLContext() {
+    fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    EGLint majorVersion;
+    EGLint minorVersion;
+    eglInitialize(fDisplay, &majorVersion, &minorVersion);
+
+    EGLint numConfigs;
+    static const EGLint configAttribs[] = {
+        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_RED_SIZE, 8,
+        EGL_GREEN_SIZE, 8,
+        EGL_BLUE_SIZE, 8,
+        EGL_ALPHA_SIZE, 8,
+        EGL_NONE
+    };
+
+    EGLConfig surfaceConfig;
+    eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs);
+
+    static const EGLint contextAttribs[] = {
+        EGL_CONTEXT_CLIENT_VERSION, 2,
+        EGL_NONE
+    };
+    fContext = eglCreateContext(fDisplay, surfaceConfig, NULL, contextAttribs);
+
+
+    static const EGLint surfaceAttribs[] = {
+            EGL_WIDTH, 1,
+            EGL_HEIGHT, 1,
+            EGL_NONE
+        };
+    fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs);
+
+    eglMakeCurrent(fDisplay, fSurface, fSurface, fContext);
+
+    const GrGLInterface* interface = GrGLCreateNativeInterface();
+    if (!interface) {
+        SkDebugf("Failed to create gl interface");
+        this->destroyGLContext();
+        return NULL;
+    }
+    return interface;
+}
+
+void SkNativeGLContext::makeCurrent() const {
+    if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+        SkDebugf("Could not set the context.\n");
+    }
+}
diff --git a/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
new file mode 100644
index 0000000..bc1e0ee
--- /dev/null
+++ b/src/gpu/gl/angle/GrGLCreateANGLEInterface.cpp
@@ -0,0 +1,155 @@
+
+/*
+ * 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 "gl/GrGLInterface.h"
+
+#ifndef GL_GLEXT_PROTOTYPES
+#define GL_GLEXT_PROTOTYPES
+#endif
+
+#include "GLES2/gl2.h"
+#include "GLES2/gl2ext.h"
+#include "EGL/egl.h"
+
+#define GR_GET_PROC(procType, baseName)             \
+    interface->f ## baseName = (procType) GetProcAddress(ghANGLELib, "gl" #baseName);
+
+const GrGLInterface* GrGLCreateANGLEInterface() {
+
+    static SkAutoTUnref<GrGLInterface> glInterface;
+    static HMODULE ghANGLELib = NULL;
+
+    if (NULL == ghANGLELib) {
+        // We load the ANGLE library and never let it go
+        ghANGLELib = LoadLibrary("libGLESv2.dll");
+    }
+    if (NULL == ghANGLELib) {
+        // We can't setup the interface correctly w/o the DLL
+        return NULL;
+    }
+
+    if (!glInterface.get()) {
+        GrGLInterface* interface = new GrGLInterface;
+        glInterface.reset(interface);
+        interface->fBindingsExported = kES2_GrGLBinding;
+
+        GR_GET_PROC(GrGLActiveTextureProc,      ActiveTexture);
+        GR_GET_PROC(GrGLAttachShaderProc,       AttachShader);
+        GR_GET_PROC(GrGLBindAttribLocationProc, BindAttribLocation);
+        GR_GET_PROC(GrGLBindBufferProc,         BindBuffer);
+        GR_GET_PROC(GrGLBindTextureProc,        BindTexture);
+        GR_GET_PROC(GrGLBlendColorProc,         BlendColor);
+        GR_GET_PROC(GrGLBlendFuncProc,          BlendFunc);
+        GR_GET_PROC(GrGLBufferDataProc,         BufferData);
+        GR_GET_PROC(GrGLBufferSubDataProc,      BufferSubData);
+        GR_GET_PROC(GrGLClearProc,              Clear);
+        GR_GET_PROC(GrGLClearColorProc,         ClearColor);
+        GR_GET_PROC(GrGLClearStencilProc,       ClearStencil);
+        GR_GET_PROC(GrGLColorMaskProc,          ColorMask);
+        GR_GET_PROC(GrGLCompileShaderProc,      CompileShader);
+        GR_GET_PROC(GrGLCompressedTexImage2DProc, CompressedTexImage2D);
+        GR_GET_PROC(GrGLCreateProgramProc,      CreateProgram);
+        GR_GET_PROC(GrGLCreateShaderProc,       CreateShader);
+        GR_GET_PROC(GrGLCullFaceProc,           CullFace);
+        GR_GET_PROC(GrGLDeleteBuffersProc,      DeleteBuffers);
+        GR_GET_PROC(GrGLDeleteProgramProc,      DeleteProgram);
+        GR_GET_PROC(GrGLDeleteShaderProc,       DeleteShader);
+        GR_GET_PROC(GrGLDeleteTexturesProc,     DeleteTextures);
+        GR_GET_PROC(GrGLDepthMaskProc,          DepthMask);
+        GR_GET_PROC(GrGLDisableProc,            Disable);
+        GR_GET_PROC(GrGLDisableVertexAttribArrayProc, DisableVertexAttribArray);
+        GR_GET_PROC(GrGLDrawArraysProc,         DrawArrays);
+        GR_GET_PROC(GrGLDrawElementsProc,       DrawElements);
+        GR_GET_PROC(GrGLEnableProc,             Enable);
+        GR_GET_PROC(GrGLEnableVertexAttribArrayProc, EnableVertexAttribArray);
+        GR_GET_PROC(GrGLFinishProc,             Finish);
+        GR_GET_PROC(GrGLFlushProc,              Flush);
+        GR_GET_PROC(GrGLFrontFaceProc,          FrontFace);
+        GR_GET_PROC(GrGLGenBuffersProc,         GenBuffers);
+        GR_GET_PROC(GrGLGenTexturesProc,        GenTextures);
+        GR_GET_PROC(GrGLGetBufferParameterivProc, GetBufferParameteriv);
+        GR_GET_PROC(GrGLGetErrorProc,           GetError);
+        GR_GET_PROC(GrGLGetIntegervProc,        GetIntegerv);
+        GR_GET_PROC(GrGLGetProgramInfoLogProc,  GetProgramInfoLog);
+        GR_GET_PROC(GrGLGetProgramivProc,       GetProgramiv);
+        GR_GET_PROC(GrGLGetShaderInfoLogProc,   GetShaderInfoLog);
+        GR_GET_PROC(GrGLGetShaderivProc,        GetShaderiv);
+        GR_GET_PROC(GrGLGetStringProc,          GetString);
+        GR_GET_PROC(GrGLGetUniformLocationProc, GetUniformLocation);
+        GR_GET_PROC(GrGLLineWidthProc,          LineWidth);
+        GR_GET_PROC(GrGLLinkProgramProc,        LinkProgram);
+        GR_GET_PROC(GrGLPixelStoreiProc,        PixelStorei);
+        GR_GET_PROC(GrGLReadPixelsProc,         ReadPixels);
+        GR_GET_PROC(GrGLScissorProc,            Scissor);
+        GR_GET_PROC(GrGLShaderSourceProc,       ShaderSource);
+        GR_GET_PROC(GrGLStencilFuncProc,        StencilFunc);
+        GR_GET_PROC(GrGLStencilFuncSeparateProc, StencilFuncSeparate);
+        GR_GET_PROC(GrGLStencilMaskProc,        StencilMask);
+        GR_GET_PROC(GrGLStencilMaskSeparateProc, StencilMaskSeparate);
+        GR_GET_PROC(GrGLStencilOpProc,          StencilOp);
+        GR_GET_PROC(GrGLStencilOpSeparateProc,  StencilOpSeparate);
+        GR_GET_PROC(GrGLTexImage2DProc,         TexImage2D);
+        GR_GET_PROC(GrGLTexParameteriProc,      TexParameteri);
+        GR_GET_PROC(GrGLTexParameterivProc,     TexParameteriv);
+        GR_GET_PROC(GrGLTexSubImage2DProc,      TexSubImage2D);
+#if GL_ARB_texture_storage
+        GR_GET_PROC(GrGLTexStorage2DProc,       TexStorage2D);
+#elif GL_EXT_texture_storage
+        interface->fTexStorage2D = (GrGLTexStorage2DProc)
+                                            GetProcAddress(ghANGLELib,
+                                            "glTexStorage2DEXT");
+#endif
+        GR_GET_PROC(GrGLUniform1fProc,          Uniform1f);
+        GR_GET_PROC(GrGLUniform1iProc,          Uniform1i);
+        GR_GET_PROC(GrGLUniform1fvProc,         Uniform1fv);
+        GR_GET_PROC(GrGLUniform1ivProc,         Uniform1iv);
+
+        GR_GET_PROC(GrGLUniform2fProc,          Uniform2f);
+        GR_GET_PROC(GrGLUniform2iProc,          Uniform2i);
+        GR_GET_PROC(GrGLUniform2fvProc,         Uniform2fv);
+        GR_GET_PROC(GrGLUniform2ivProc,         Uniform2iv);
+
+        GR_GET_PROC(GrGLUniform3fProc,          Uniform3f);
+        GR_GET_PROC(GrGLUniform3iProc,          Uniform3i);
+        GR_GET_PROC(GrGLUniform3fvProc,         Uniform3fv);
+        GR_GET_PROC(GrGLUniform3ivProc,         Uniform3iv);
+
+        GR_GET_PROC(GrGLUniform4fProc,          Uniform4f);
+        GR_GET_PROC(GrGLUniform4iProc,          Uniform4i);
+        GR_GET_PROC(GrGLUniform4fvProc,         Uniform4fv);
+        GR_GET_PROC(GrGLUniform4ivProc,         Uniform4iv);
+
+        GR_GET_PROC(GrGLUniformMatrix2fvProc,   UniformMatrix2fv);
+        GR_GET_PROC(GrGLUniformMatrix3fvProc,   UniformMatrix3fv);
+        GR_GET_PROC(GrGLUniformMatrix4fvProc,   UniformMatrix4fv);
+        GR_GET_PROC(GrGLUseProgramProc,         UseProgram);
+        GR_GET_PROC(GrGLVertexAttrib4fvProc,    VertexAttrib4fv);
+        GR_GET_PROC(GrGLVertexAttribPointerProc, VertexAttribPointer);
+        GR_GET_PROC(GrGLViewportProc,           Viewport);
+        GR_GET_PROC(GrGLBindFramebufferProc,    BindFramebuffer);
+        GR_GET_PROC(GrGLBindRenderbufferProc,   BindRenderbuffer);
+        GR_GET_PROC(GrGLCheckFramebufferStatusProc, CheckFramebufferStatus);
+        GR_GET_PROC(GrGLDeleteFramebuffersProc, DeleteFramebuffers);
+        GR_GET_PROC(GrGLDeleteRenderbuffersProc, DeleteRenderbuffers);
+        GR_GET_PROC(GrGLFramebufferRenderbufferProc, FramebufferRenderbuffer);
+        GR_GET_PROC(GrGLFramebufferTexture2DProc, FramebufferTexture2D);
+        GR_GET_PROC(GrGLGenFramebuffersProc,    GenFramebuffers);
+        GR_GET_PROC(GrGLGenRenderbuffersProc,   GenRenderbuffers);
+        GR_GET_PROC(GrGLGetFramebufferAttachmentParameterivProc,
+                                GetFramebufferAttachmentParameteriv);
+        GR_GET_PROC(GrGLGetRenderbufferParameterivProc,
+                                GetRenderbufferParameteriv);
+        GR_GET_PROC(GrGLRenderbufferStorageProc, RenderbufferStorage);
+
+        interface->fMapBuffer = (PFNGLMAPBUFFEROESPROC) eglGetProcAddress("glMapBufferOES");
+        interface->fUnmapBuffer = (PFNGLUNMAPBUFFEROESPROC) eglGetProcAddress("glUnmapBufferOES");
+    }
+    glInterface.get()->ref();
+    return glInterface.get();
+}
diff --git a/src/gpu/gl/angle/SkANGLEGLContext.cpp b/src/gpu/gl/angle/SkANGLEGLContext.cpp
new file mode 100644
index 0000000..fea2762
--- /dev/null
+++ b/src/gpu/gl/angle/SkANGLEGLContext.cpp
@@ -0,0 +1,107 @@
+
+/*
+ * 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 "gl/SkANGLEGLContext.h"
+
+SkANGLEGLContext::AutoContextRestore::AutoContextRestore() {
+    fOldEGLContext = eglGetCurrentContext();
+    fOldDisplay = eglGetCurrentDisplay();
+    fOldSurface = eglGetCurrentSurface(EGL_DRAW);
+
+}
+
+SkANGLEGLContext::AutoContextRestore::~AutoContextRestore() {
+    if (NULL != fOldDisplay) {
+        eglMakeCurrent(fOldDisplay, fOldSurface, fOldSurface, fOldEGLContext);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkANGLEGLContext::SkANGLEGLContext()
+    : fContext(EGL_NO_CONTEXT)
+    , fDisplay(EGL_NO_DISPLAY)
+    , fSurface(EGL_NO_SURFACE) {
+}
+
+SkANGLEGLContext::~SkANGLEGLContext() {
+    this->destroyGLContext();
+}
+
+void SkANGLEGLContext::destroyGLContext() {
+    if (fDisplay) {
+        eglMakeCurrent(fDisplay, 0, 0, 0);
+
+        if (fContext) {
+            eglDestroyContext(fDisplay, fContext);
+            fContext = EGL_NO_CONTEXT;
+        }
+
+        if (fSurface) {
+            eglDestroySurface(fDisplay, fSurface);
+            fSurface = EGL_NO_SURFACE;
+        }
+
+        //TODO should we close the display?
+        fDisplay = EGL_NO_DISPLAY;
+    }
+}
+
+const GrGLInterface* SkANGLEGLContext::createGLContext() {
+
+    fDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+
+    EGLint majorVersion;
+    EGLint minorVersion;
+    eglInitialize(fDisplay, &majorVersion, &minorVersion);
+
+    EGLint numConfigs;
+    static const EGLint configAttribs[] = {
+        EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,
+        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+        EGL_RED_SIZE, 8,
+        EGL_GREEN_SIZE, 8,
+        EGL_BLUE_SIZE, 8,
+        EGL_ALPHA_SIZE, 8,
+        EGL_NONE
+    };
+
+    EGLConfig surfaceConfig;
+    eglChooseConfig(fDisplay, configAttribs, &surfaceConfig, 1, &numConfigs);
+
+    static const EGLint contextAttribs[] = {
+        EGL_CONTEXT_CLIENT_VERSION, 2,
+        EGL_NONE
+    };
+    fContext = eglCreateContext(fDisplay, surfaceConfig, NULL, contextAttribs);
+
+
+    static const EGLint surfaceAttribs[] = {
+            EGL_WIDTH, 1,
+            EGL_HEIGHT, 1,
+            EGL_NONE
+        };
+    fSurface = eglCreatePbufferSurface(fDisplay, surfaceConfig, surfaceAttribs);
+
+    eglMakeCurrent(fDisplay, fSurface, fSurface, fContext);
+
+    const GrGLInterface* interface = GrGLCreateANGLEInterface();
+    if (NULL == interface) {
+        SkDebugf("Could not create ANGLE GL interface!\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    return interface;
+}
+
+void SkANGLEGLContext::makeCurrent() const {
+    if (!eglMakeCurrent(fDisplay, fSurface, fSurface, fContext)) {
+        SkDebugf("Could not set the context.\n");
+    }
+}
diff --git a/src/gpu/gl/debug/GrBufferObj.cpp b/src/gpu/gl/debug/GrBufferObj.cpp
new file mode 100644
index 0000000..7d605eb
--- /dev/null
+++ b/src/gpu/gl/debug/GrBufferObj.cpp
@@ -0,0 +1,31 @@
+
+/*
+ * 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 "GrBufferObj.h"
+
+void GrBufferObj::allocate(GrGLint size, const GrGLchar *dataPtr) {
+    GrAlwaysAssert(size >= 0);
+
+    // delete pre-existing data
+    delete[] fDataPtr;
+
+    fSize = size;
+    fDataPtr = new GrGLchar[size];
+    if (dataPtr) {
+        memcpy(fDataPtr, dataPtr, fSize);
+    }
+    // TODO: w/ no dataPtr the data is unitialized - this could be tracked
+}
+
+void GrBufferObj::deleteAction() {
+
+    // buffers are automatically unmapped when deleted
+    this->resetMapped();
+
+    this->INHERITED::deleteAction();
+}
diff --git a/src/gpu/gl/debug/GrBufferObj.h b/src/gpu/gl/debug/GrBufferObj.h
new file mode 100644
index 0000000..faa2398
--- /dev/null
+++ b/src/gpu/gl/debug/GrBufferObj.h
@@ -0,0 +1,68 @@
+
+/*
+ * 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 GrBufferObj_DEFINED
+#define GrBufferObj_DEFINED
+
+#include "GrFakeRefObj.h"
+#include "../GrGLDefines.h"
+
+////////////////////////////////////////////////////////////////////////////////
+class GrBufferObj : public GrFakeRefObj {
+    GR_DEFINE_CREATOR(GrBufferObj);
+
+public:
+    GrBufferObj()
+        : GrFakeRefObj()
+        , fDataPtr(NULL)
+        , fMapped(false)
+        , fBound(false)
+        , fSize(0)
+        , fUsage(GR_GL_STATIC_DRAW) {
+    }
+    virtual ~GrBufferObj() {
+        delete[] fDataPtr;
+    }
+
+    void access() {
+        // cannot access the buffer if it is currently mapped
+        GrAlwaysAssert(!fMapped);
+    }
+
+    void setMapped()             { fMapped = true; }
+    void resetMapped()           { fMapped = false; }
+    bool getMapped() const       { return fMapped; }
+
+    void setBound()              { fBound = true; }
+    void resetBound()            { fBound = false; }
+    bool getBound() const        { return fBound; }
+
+    void allocate(GrGLint size, const GrGLchar *dataPtr);
+    GrGLint getSize() const      { return fSize; }
+    GrGLchar *getDataPtr()       { return fDataPtr; }
+
+    void setUsage(GrGLint usage) { fUsage = usage; }
+    GrGLint getUsage() const     { return fUsage; }
+
+    virtual void deleteAction() SK_OVERRIDE;
+
+protected:
+private:
+
+    GrGLchar*   fDataPtr;
+    bool        fMapped;       // is the buffer object mapped via "glMapBuffer"?
+    bool        fBound;        // is the buffer object bound via "glBindBuffer"?
+    GrGLint     fSize;         // size in bytes
+    GrGLint     fUsage;        // one of: GL_STREAM_DRAW,
+                               //         GL_STATIC_DRAW,
+                               //         GL_DYNAMIC_DRAW
+
+    typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrBufferObj_DEFINED
diff --git a/src/gpu/gl/debug/GrDebugGL.cpp b/src/gpu/gl/debug/GrDebugGL.cpp
new file mode 100644
index 0000000..9e2e510
--- /dev/null
+++ b/src/gpu/gl/debug/GrDebugGL.cpp
@@ -0,0 +1,201 @@
+
+/*
+ * 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 "GrDebugGL.h"
+#include "GrTextureObj.h"
+#include "GrBufferObj.h"
+#include "GrRenderBufferObj.h"
+#include "GrFrameBufferObj.h"
+#include "GrShaderObj.h"
+#include "GrProgramObj.h"
+#include "GrTextureUnitObj.h"
+
+
+GrDebugGL* GrDebugGL::gObj = NULL;
+int GrDebugGL::gStaticRefCount = 0;
+GrDebugGL::Create GrDebugGL::gFactoryFunc[kObjTypeCount] = {
+    GrTextureObj::createGrTextureObj,
+    GrBufferObj::createGrBufferObj,
+    GrRenderBufferObj::createGrRenderBufferObj,
+    GrFrameBufferObj::createGrFrameBufferObj,
+    GrShaderObj::createGrShaderObj,
+    GrProgramObj::createGrProgramObj,
+    GrTextureUnitObj::createGrTextureUnitObj,
+};
+
+
+GrDebugGL::GrDebugGL()
+    : fPackRowLength(0)
+    , fUnPackRowLength(0)
+    , fCurTextureUnit(0)
+    , fArrayBuffer(NULL)
+    , fElementArrayBuffer(NULL)
+    , fFrameBuffer(NULL)
+    , fRenderBuffer(NULL)
+    , fProgram(NULL)
+    , fTexture(NULL) {
+
+    for (int i = 0; i < kDefaultMaxTextureUnits; ++i) {
+
+        fTextureUnits[i] = reinterpret_cast<GrTextureUnitObj *>(
+                            createObj(GrDebugGL::kTextureUnit_ObjTypes));
+        fTextureUnits[i]->ref();
+
+        fTextureUnits[i]->setNumber(i);
+    }
+}
+
+GrDebugGL::~GrDebugGL() {
+    // unref & delete the texture units first so they don't show up on the leak report
+    for (int i = 0; i < kDefaultMaxTextureUnits; ++i) {
+        fTextureUnits[i]->unref();
+        fTextureUnits[i]->deleteAction();
+    }
+
+    this->report();
+
+    for (int i = 0; i < fObjects.count(); ++i) {
+        delete fObjects[i];
+    }
+    fObjects.reset();
+
+    fArrayBuffer = NULL;
+    fElementArrayBuffer = NULL;
+    fFrameBuffer = NULL;
+    fRenderBuffer = NULL;
+    fProgram = NULL;
+    fTexture = NULL;
+}
+
+GrFakeRefObj *GrDebugGL::findObject(GrGLuint ID, GrObjTypes type) {
+    for (int i = 0; i < fObjects.count(); ++i) {
+        if (fObjects[i]->getID() == ID) { // && fObjects[i]->getType() == type) {
+            // The application shouldn't be accessing objects
+            // that (as far as OpenGL knows) were already deleted
+            GrAlwaysAssert(!fObjects[i]->getDeleted());
+            GrAlwaysAssert(!fObjects[i]->getMarkedForDeletion());
+            return fObjects[i];
+        }
+    }
+
+    return NULL;
+}
+
+void GrDebugGL::setArrayBuffer(GrBufferObj *arrayBuffer) {
+    if (fArrayBuffer) {
+        // automatically break the binding of the old buffer
+        GrAlwaysAssert(fArrayBuffer->getBound());
+        fArrayBuffer->resetBound();
+
+        GrAlwaysAssert(!fArrayBuffer->getDeleted());
+        fArrayBuffer->unref();
+    }
+
+    fArrayBuffer = arrayBuffer;
+
+    if (fArrayBuffer) {
+        GrAlwaysAssert(!fArrayBuffer->getDeleted());
+        fArrayBuffer->ref();
+
+        GrAlwaysAssert(!fArrayBuffer->getBound());
+        fArrayBuffer->setBound();
+    }
+}
+
+void GrDebugGL::setElementArrayBuffer(GrBufferObj *elementArrayBuffer) {
+    if (fElementArrayBuffer) {
+        // automatically break the binding of the old buffer
+        GrAlwaysAssert(fElementArrayBuffer->getBound());
+        fElementArrayBuffer->resetBound();
+
+        GrAlwaysAssert(!fElementArrayBuffer->getDeleted());
+        fElementArrayBuffer->unref();
+    }
+
+    fElementArrayBuffer = elementArrayBuffer;
+
+    if (fElementArrayBuffer) {
+        GrAlwaysAssert(!fElementArrayBuffer->getDeleted());
+        fElementArrayBuffer->ref();
+
+        GrAlwaysAssert(!fElementArrayBuffer->getBound());
+        fElementArrayBuffer->setBound();
+    }
+}
+
+void GrDebugGL::setTexture(GrTextureObj *texture)  {
+    fTextureUnits[fCurTextureUnit]->setTexture(texture);
+}
+
+void GrDebugGL::setFrameBuffer(GrFrameBufferObj *frameBuffer)  {
+    if (fFrameBuffer) {
+        GrAlwaysAssert(fFrameBuffer->getBound());
+        fFrameBuffer->resetBound();
+
+        GrAlwaysAssert(!fFrameBuffer->getDeleted());
+        fFrameBuffer->unref();
+    }
+
+    fFrameBuffer = frameBuffer;
+
+    if (fFrameBuffer) {
+        GrAlwaysAssert(!fFrameBuffer->getDeleted());
+        fFrameBuffer->ref();
+
+        GrAlwaysAssert(!fFrameBuffer->getBound());
+        fFrameBuffer->setBound();
+    }
+}
+
+void GrDebugGL::setRenderBuffer(GrRenderBufferObj *renderBuffer)  {
+    if (fRenderBuffer) {
+        GrAlwaysAssert(fRenderBuffer->getBound());
+        fRenderBuffer->resetBound();
+
+        GrAlwaysAssert(!fRenderBuffer->getDeleted());
+        fRenderBuffer->unref();
+    }
+
+    fRenderBuffer = renderBuffer;
+
+    if (fRenderBuffer) {
+        GrAlwaysAssert(!fRenderBuffer->getDeleted());
+        fRenderBuffer->ref();
+
+        GrAlwaysAssert(!fRenderBuffer->getBound());
+        fRenderBuffer->setBound();
+    }
+}
+
+void GrDebugGL::useProgram(GrProgramObj *program) {
+    if (fProgram) {
+        GrAlwaysAssert(fProgram->getInUse());
+        fProgram->resetInUse();
+
+        GrAlwaysAssert(!fProgram->getDeleted());
+        fProgram->unref();
+    }
+
+    fProgram = program;
+
+    if (fProgram) {
+        GrAlwaysAssert(!fProgram->getDeleted());
+        fProgram->ref();
+
+        GrAlwaysAssert(!fProgram->getInUse());
+        fProgram->setInUse();
+    }
+}
+
+void GrDebugGL::report() const {
+    for (int i = 0; i < fObjects.count(); ++i) {
+        GrAlwaysAssert(0 == fObjects[i]->getRefCount());
+        GrAlwaysAssert(0 < fObjects[i]->getHighRefCount());
+        GrAlwaysAssert(fObjects[i]->getDeleted());
+    }
+}
diff --git a/src/gpu/gl/debug/GrDebugGL.h b/src/gpu/gl/debug/GrDebugGL.h
new file mode 100644
index 0000000..409f13d
--- /dev/null
+++ b/src/gpu/gl/debug/GrDebugGL.h
@@ -0,0 +1,155 @@
+
+/*
+ * 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 GrDebugGL_DEFINED
+#define GrDebugGL_DEFINED
+
+#include "SkTArray.h"
+#include "gl/GrGLInterface.h"
+
+class GrFakeRefObj;
+class GrTextureUnitObj;
+class GrBufferObj;
+class GrTextureObj;
+class GrFrameBufferObj;
+class GrRenderBufferObj;
+class GrProgramObj;
+
+////////////////////////////////////////////////////////////////////////////////
+// This is the main debugging object. It is a singleton and keeps track of
+// all the other debug objects.
+class GrDebugGL {
+public:
+    enum GrObjTypes {
+        kTexture_ObjTypes = 0,
+        kBuffer_ObjTypes,
+        kRenderBuffer_ObjTypes,
+        kFrameBuffer_ObjTypes,
+        kShader_ObjTypes,
+        kProgram_ObjTypes,
+        kTextureUnit_ObjTypes,
+        kObjTypeCount
+    };
+
+    GrFakeRefObj *createObj(GrObjTypes type) {
+        GrFakeRefObj *temp = (*gFactoryFunc[type])();
+
+        fObjects.push_back(temp);
+
+        return temp;
+    }
+
+    GrFakeRefObj *findObject(GrGLuint ID, GrObjTypes type);
+
+    GrGLuint getMaxTextureUnits() const { return kDefaultMaxTextureUnits; }
+
+    void setCurTextureUnit(GrGLuint curTextureUnit) { fCurTextureUnit = curTextureUnit; }
+    GrGLuint getCurTextureUnit() const { return fCurTextureUnit; }
+
+    GrTextureUnitObj *getTextureUnit(int iUnit) {
+        GrAlwaysAssert(0 <= iUnit && kDefaultMaxTextureUnits > iUnit);
+
+        return fTextureUnits[iUnit];
+    }
+
+    void setArrayBuffer(GrBufferObj *arrayBuffer);
+    GrBufferObj *getArrayBuffer()                   { return fArrayBuffer; }
+
+    void setElementArrayBuffer(GrBufferObj *elementArrayBuffer);
+    GrBufferObj *getElementArrayBuffer()                            { return fElementArrayBuffer; }
+
+    void setTexture(GrTextureObj *texture);
+
+    void setFrameBuffer(GrFrameBufferObj *frameBuffer);
+    GrFrameBufferObj *getFrameBuffer()                  { return fFrameBuffer; }
+
+    void setRenderBuffer(GrRenderBufferObj *renderBuffer);
+    GrRenderBufferObj *getRenderBuffer()                  { return fRenderBuffer; }
+
+    void useProgram(GrProgramObj *program);
+
+    void setPackRowLength(GrGLint packRowLength) {
+        fPackRowLength = packRowLength;
+    }
+    GrGLint getPackRowLength() const { return fPackRowLength; }
+
+    void setUnPackRowLength(GrGLint unPackRowLength) {
+        fUnPackRowLength = unPackRowLength;
+    }
+    GrGLint getUnPackRowLength() const { return fUnPackRowLength; }
+
+    static GrDebugGL *getInstance() {
+        // someone should admit to actually using this class
+        GrAssert(0 < gStaticRefCount);
+
+        if (NULL == gObj) {
+            gObj = SkNEW(GrDebugGL);
+        }
+
+        return gObj;
+    }
+
+    void report() const;
+
+    static void staticRef() {
+        gStaticRefCount++;
+    }
+
+    static void staticUnRef() {
+        GrAssert(gStaticRefCount > 0);
+        gStaticRefCount--;
+        if (0 == gStaticRefCount) {
+            SkDELETE(gObj);
+            gObj = NULL;
+        }
+    }
+
+protected:
+
+private:
+    // the OpenGLES 2.0 spec says this must be >= 2
+    static const GrGLint kDefaultMaxTextureUnits = 8;
+
+    GrGLint         fPackRowLength;
+    GrGLint         fUnPackRowLength;
+    GrGLuint        fMaxTextureUnits;
+    GrGLuint        fCurTextureUnit;
+    GrBufferObj *   fArrayBuffer;
+    GrBufferObj *   fElementArrayBuffer;
+    GrFrameBufferObj *fFrameBuffer;
+    GrRenderBufferObj *fRenderBuffer;
+    GrProgramObj *  fProgram;
+    GrTextureObj *  fTexture;
+    GrTextureUnitObj *fTextureUnits[kDefaultMaxTextureUnits];
+
+    typedef GrFakeRefObj *(*Create)();
+
+    static Create gFactoryFunc[kObjTypeCount];
+
+    static GrDebugGL* gObj;
+    static int gStaticRefCount;
+
+    // global store of all objects
+    SkTArray<GrFakeRefObj *> fObjects;
+
+    GrDebugGL();
+    ~GrDebugGL();
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Helper macro to make creating an object (where you need to get back a derived
+// type) easier
+#define GR_CREATE(className, classEnum)                     \
+    reinterpret_cast<className *>(GrDebugGL::getInstance()->createObj(classEnum))
+
+////////////////////////////////////////////////////////////////////////////////
+// Helper macro to make finding objects less painful
+#define GR_FIND(id, className, classEnum)                   \
+    reinterpret_cast<className *>(GrDebugGL::getInstance()->findObject(id, classEnum))
+
+#endif // GrDebugGL_DEFINED
diff --git a/src/gpu/gl/debug/GrFBBindableObj.h b/src/gpu/gl/debug/GrFBBindableObj.h
new file mode 100644
index 0000000..e2b43a6
--- /dev/null
+++ b/src/gpu/gl/debug/GrFBBindableObj.h
@@ -0,0 +1,88 @@
+
+/*
+ * 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 GrFBBindableObj_DEFINED
+#define GrFBBindableObj_DEFINED
+
+#include "SkTDArray.h"
+#include "GrFakeRefObj.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// A common base class for render buffers and textures
+class GrFBBindableObj : public GrFakeRefObj {
+
+public:
+    GrFBBindableObj()
+        : GrFakeRefObj() {
+    }
+
+    virtual ~GrFBBindableObj() {
+        GrAlwaysAssert(0 == fColorReferees.count());
+        GrAlwaysAssert(0 == fDepthReferees.count());
+        GrAlwaysAssert(0 == fStencilReferees.count());
+    }
+
+    void setColorBound(GrFakeRefObj *referee) {
+        fColorReferees.append(1, &referee);
+    }
+    void resetColorBound(GrFakeRefObj *referee) {
+        int index = fColorReferees.find(referee);
+        GrAlwaysAssert(0 <= index);
+        fColorReferees.removeShuffle(index);
+    }
+    bool getColorBound(GrFakeRefObj *referee) const {
+        int index = fColorReferees.find(referee);
+        return 0 <= index;
+    }
+    bool getColorBound() const {
+        return 0 != fColorReferees.count();
+    }
+
+    void setDepthBound(GrFakeRefObj *referee) {
+        fDepthReferees.append(1, &referee);
+    }
+    void resetDepthBound(GrFakeRefObj *referee) {
+        int index = fDepthReferees.find(referee);
+        GrAlwaysAssert(0 <= index);
+        fDepthReferees.removeShuffle(index);
+    }
+    bool getDepthBound(GrFakeRefObj *referee) const {
+        int index = fDepthReferees.find(referee);
+        return 0 <= index;
+    }
+    bool getDepthBound() const {
+        return 0 != fDepthReferees.count();
+    }
+
+    void setStencilBound(GrFakeRefObj *referee) {
+        fStencilReferees.append(1, &referee);
+    }
+    void resetStencilBound(GrFakeRefObj *referee) {
+        int index = fStencilReferees.find(referee);
+        GrAlwaysAssert(0 <= index);
+        fStencilReferees.removeShuffle(index);
+    }
+    bool getStencilBound(GrFakeRefObj *referee) const {
+        int index = fStencilReferees.find(referee);
+        return 0 <= index;
+    }
+    bool getStencilBound() const {
+        return 0 != fStencilReferees.count();
+    }
+
+
+protected:
+private:
+    SkTDArray<GrFakeRefObj *> fColorReferees;   // frame buffers that use this as a color buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D")
+    SkTDArray<GrFakeRefObj *> fDepthReferees;   // frame buffers that use this as a depth buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D")
+    SkTDArray<GrFakeRefObj *> fStencilReferees; // frame buffers that use this as a stencil buffer (via "glFramebufferRenderbuffer" or "glFramebufferTexture2D")
+
+    typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrFBBindableObj_DEFINED
diff --git a/src/gpu/gl/debug/GrFakeRefObj.h b/src/gpu/gl/debug/GrFakeRefObj.h
new file mode 100644
index 0000000..7f21c94
--- /dev/null
+++ b/src/gpu/gl/debug/GrFakeRefObj.h
@@ -0,0 +1,95 @@
+
+/*
+ * 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 GrFakeRefObj_DEFINED
+#define GrFakeRefObj_DEFINED
+
+#include "gl/GrGLInterface.h"
+#include "GrNoncopyable.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// This object is used to track the OpenGL objects. We don't use real
+// reference counting (i.e., we don't free the objects when their ref count
+// goes to 0) so that we can detect invalid memory accesses. The refs we
+// are tracking in this class are actually OpenGL's references to the objects
+// not "ours"
+// Each object also gets a unique globally identifying ID
+class GrFakeRefObj : public GrNoncopyable {
+public:
+    GrFakeRefObj()
+        : fRef(0)
+        , fHighRefCount(0)
+        , fMarkedForDeletion(false)
+        , fDeleted(false) {
+
+        // source for globally unique IDs - 0 is reserved!
+        static int fNextID = 0;
+
+        fID = ++fNextID;
+    }
+    virtual ~GrFakeRefObj() {};
+
+    void ref() {
+        fRef++;
+        if (fHighRefCount < fRef) {
+            fHighRefCount = fRef;
+        }
+    }
+    void unref() {
+        fRef--;
+        GrAlwaysAssert(fRef >= 0);
+
+        // often in OpenGL a given object may still be in use when the
+        // delete call is made. In these cases the object is marked
+        // for deletion and then freed when it is no longer in use
+        if (0 == fRef && fMarkedForDeletion) {
+            this->deleteAction();
+        }
+    }
+    int getRefCount() const             { return fRef; }
+    int getHighRefCount() const         { return fHighRefCount; }
+
+    GrGLuint getID() const              { return fID; }
+
+    void setMarkedForDeletion()         { fMarkedForDeletion = true; }
+    bool getMarkedForDeletion() const   { return fMarkedForDeletion; }
+
+    bool getDeleted() const             { return fDeleted; }
+
+    // The deleteAction fires if the object has been marked for deletion but
+    // couldn't be deleted earlier due to refs
+    virtual void deleteAction() {
+        this->setDeleted();
+    }
+
+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
+    // It is obviously still allocated w/in this framework
+    bool        fDeleted;
+
+    // setDeleted should only ever appear in the deleteAction method!
+    void setDeleted()                   { fDeleted = true; }
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Each class derived from GrFakeRefObj should use this macro to add a
+// factory creation entry point. This entry point is used by the GrGLDebug
+// object to instantiate the various objects
+// all globally unique IDs
+#define GR_DEFINE_CREATOR(className)                        \
+    public:                                                 \
+    static GrFakeRefObj *create ## className() {            \
+        return SkNEW(className);                            \
+    }
+
+#endif // GrFakeRefObj_DEFINED
diff --git a/src/gpu/gl/debug/GrFrameBufferObj.cpp b/src/gpu/gl/debug/GrFrameBufferObj.cpp
new file mode 100644
index 0000000..24ab120
--- /dev/null
+++ b/src/gpu/gl/debug/GrFrameBufferObj.cpp
@@ -0,0 +1,67 @@
+
+/*
+ * 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 "GrFrameBufferObj.h"
+#include "GrFBBindableObj.h"
+
+void GrFrameBufferObj::setColor(GrFBBindableObj *buffer) {
+    if (fColorBuffer) {
+        // automatically break the binding of the old buffer
+        GrAlwaysAssert(fColorBuffer->getColorBound(this));
+        fColorBuffer->resetColorBound(this);
+
+        GrAlwaysAssert(!fColorBuffer->getDeleted());
+        fColorBuffer->unref();
+    }
+    fColorBuffer = buffer;
+    if (fColorBuffer) {
+        GrAlwaysAssert(!fColorBuffer->getDeleted());
+        fColorBuffer->ref();
+
+        GrAlwaysAssert(!fColorBuffer->getColorBound(this));
+        fColorBuffer->setColorBound(this);
+    }
+}
+
+void GrFrameBufferObj::setDepth(GrFBBindableObj *buffer) {
+    if (fDepthBuffer) {
+        // automatically break the binding of the old buffer
+        GrAlwaysAssert(fDepthBuffer->getDepthBound(this));
+        fDepthBuffer->resetDepthBound(this);
+
+        GrAlwaysAssert(!fDepthBuffer->getDeleted());
+        fDepthBuffer->unref();
+    }
+    fDepthBuffer = buffer;
+    if (fDepthBuffer) {
+        GrAlwaysAssert(!fDepthBuffer->getDeleted());
+        fDepthBuffer->ref();
+
+        GrAlwaysAssert(!fDepthBuffer->getDepthBound(this));
+        fDepthBuffer->setDepthBound(this);
+    }
+}
+
+void GrFrameBufferObj::setStencil(GrFBBindableObj *buffer) {
+    if (fStencilBuffer) {
+        // automatically break the binding of the old buffer
+        GrAlwaysAssert(fStencilBuffer->getStencilBound(this));
+        fStencilBuffer->resetStencilBound(this);
+
+        GrAlwaysAssert(!fStencilBuffer->getDeleted());
+        fStencilBuffer->unref();
+    }
+    fStencilBuffer = buffer;
+    if (fStencilBuffer) {
+        GrAlwaysAssert(!fStencilBuffer->getDeleted());
+        fStencilBuffer->ref();
+
+        GrAlwaysAssert(!fStencilBuffer->getStencilBound(this));
+        fStencilBuffer->setStencilBound(this);
+    }
+}
diff --git a/src/gpu/gl/debug/GrFrameBufferObj.h b/src/gpu/gl/debug/GrFrameBufferObj.h
new file mode 100644
index 0000000..33af4f7
--- /dev/null
+++ b/src/gpu/gl/debug/GrFrameBufferObj.h
@@ -0,0 +1,68 @@
+
+/*
+ * 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 GrFrameBufferObj_DEFINED
+#define GrFrameBufferObj_DEFINED
+
+#include "GrFakeRefObj.h"
+class GrFBBindableObj;
+
+////////////////////////////////////////////////////////////////////////////////
+// TODO: when a framebuffer obj is bound the GL_SAMPLES query must return 0
+// TODO: GL_STENCIL_BITS must also be redirected to the framebuffer
+class GrFrameBufferObj : public GrFakeRefObj {
+    GR_DEFINE_CREATOR(GrFrameBufferObj);
+
+public:
+    GrFrameBufferObj()
+        : GrFakeRefObj()
+        , fBound(false)
+        , fColorBuffer(NULL)
+        , fDepthBuffer(NULL)
+        , fStencilBuffer(NULL) {
+    }
+
+    virtual ~GrFrameBufferObj() {
+        fColorBuffer = NULL;
+        fDepthBuffer = NULL;
+        fStencilBuffer = NULL;
+    }
+
+    void setBound()         { fBound = true; }
+    void resetBound()       { fBound = false; }
+    bool getBound() const   { return fBound; }
+
+    void setColor(GrFBBindableObj *buffer);
+    GrFBBindableObj *getColor()       { return fColorBuffer; }
+
+    void setDepth(GrFBBindableObj *buffer);
+    GrFBBindableObj *getDepth()       { return fDepthBuffer; }
+
+    void setStencil(GrFBBindableObj *buffer);
+    GrFBBindableObj *getStencil()     { return fStencilBuffer; }
+
+    virtual void deleteAction() SK_OVERRIDE {
+
+        setColor(NULL);
+        setDepth(NULL);
+        setStencil(NULL);
+
+        this->INHERITED::deleteAction();
+    }
+
+protected:
+private:
+    bool fBound;        // is this frame buffer currently bound via "glBindFramebuffer"?
+    GrFBBindableObj * fColorBuffer;
+    GrFBBindableObj * fDepthBuffer;
+    GrFBBindableObj * fStencilBuffer;
+
+    typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrFrameBufferObj_DEFINED
diff --git a/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
new file mode 100644
index 0000000..1af17b2
--- /dev/null
+++ b/src/gpu/gl/debug/GrGLCreateDebugInterface.cpp
@@ -0,0 +1,1461 @@
+
+/*
+ * 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 "gl/GrGLInterface.h"
+#include "GrDebugGL.h"
+#include "GrShaderObj.h"
+#include "GrProgramObj.h"
+#include "GrBufferObj.h"
+#include "GrTextureUnitObj.h"
+#include "GrTextureObj.h"
+#include "GrFrameBufferObj.h"
+#include "GrRenderBufferObj.h"
+#include "SkFloatingPoint.h"
+
+// the OpenGLES 2.0 spec says this must be >= 128
+static const GrGLint kDefaultMaxVertexUniformVectors = 128;
+
+// the OpenGLES 2.0 spec says this must be >=16
+static const GrGLint kDefaultMaxFragmentUniformVectors = 16;
+
+// the OpenGLES 2.0 spec says this must be >= 8
+static const GrGLint kDefaultMaxVertexAttribs = 8;
+
+// the OpenGLES 2.0 spec says this must be >= 8
+static const GrGLint kDefaultMaxVaryingVectors = 8;
+
+namespace { // suppress no previsous prototype warning
+
+////////////////////////////////////////////////////////////////////////////////
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLActiveTexture(GrGLenum texture) {
+
+    // Ganesh offsets the texture unit indices
+    texture -= GR_GL_TEXTURE0;
+    GrAlwaysAssert(texture < GrDebugGL::getInstance()->getMaxTextureUnits());
+
+    GrDebugGL::getInstance()->setCurTextureUnit(texture);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLAttachShader(GrGLuint programID,
+                                                 GrGLuint shaderID) {
+
+    GrProgramObj *program = GR_FIND(programID, GrProgramObj,
+                                    GrDebugGL::kProgram_ObjTypes);
+    GrAlwaysAssert(program);
+
+    GrShaderObj *shader = GR_FIND(shaderID,
+                                  GrShaderObj,
+                                  GrDebugGL::kShader_ObjTypes);
+    GrAlwaysAssert(shader);
+
+    program->AttachShader(shader);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBeginQuery(GrGLenum target, GrGLuint id) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBindAttribLocation(GrGLuint program,
+                                                       GrGLuint index,
+                                                       const char* name) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBindTexture(GrGLenum target,
+                                                GrGLuint textureID) {
+
+    // we don't use cube maps
+    GrAlwaysAssert(target == GR_GL_TEXTURE_2D);
+                                    // || target == GR_GL_TEXTURE_CUBE_MAP);
+
+    // a textureID of 0 is acceptable - it binds to the default texture target
+    GrTextureObj *texture = GR_FIND(textureID, GrTextureObj,
+                                    GrDebugGL::kTexture_ObjTypes);
+
+    GrDebugGL::getInstance()->setTexture(texture);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBlendColor(GrGLclampf red,
+                                               GrGLclampf green,
+                                               GrGLclampf blue,
+                                               GrGLclampf alpha) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBindFragDataLocation(GrGLuint program,
+                                                         GrGLuint colorNumber,
+                                                         const GrGLchar* name) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBlendFunc(GrGLenum sfactor,
+                                              GrGLenum dfactor) {
+}
+
+////////////////////////////////////////////////////////////////////////////////
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBufferData(GrGLenum target,
+                                               GrGLsizeiptr size,
+                                               const GrGLvoid* data,
+                                               GrGLenum usage) {
+    GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+                   GR_GL_ELEMENT_ARRAY_BUFFER == target);
+    GrAlwaysAssert(size >= 0);
+    GrAlwaysAssert(GR_GL_STREAM_DRAW == usage ||
+                   GR_GL_STATIC_DRAW == usage ||
+                   GR_GL_DYNAMIC_DRAW == usage);
+
+    GrBufferObj *buffer = NULL;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getArrayBuffer();
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getElementArrayBuffer();
+            break;
+        default:
+            GrCrash("Unexpected target to glBufferData");
+            break;
+    }
+
+    GrAlwaysAssert(buffer);
+    GrAlwaysAssert(buffer->getBound());
+
+    buffer->allocate(size, reinterpret_cast<const GrGLchar *>(data));
+    buffer->setUsage(usage);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBufferSubData(GrGLenum target,
+                                                  GrGLintptr offset,
+                                                  GrGLsizeiptr size,
+                                                  const GrGLvoid* data) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLClear(GrGLbitfield mask) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLClearColor(GrGLclampf red,
+                                               GrGLclampf green,
+                                               GrGLclampf blue,
+                                               GrGLclampf alpha) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLClearStencil(GrGLint s) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLColorMask(GrGLboolean red,
+                                              GrGLboolean green,
+                                              GrGLboolean blue,
+                                              GrGLboolean alpha) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLCompileShader(GrGLuint shader) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLCompressedTexImage2D(GrGLenum target,
+                                                         GrGLint level,
+                                                         GrGLenum internalformat,
+                                                         GrGLsizei width,
+                                                         GrGLsizei height,
+                                                         GrGLint border,
+                                                         GrGLsizei imageSize,
+                                                         const GrGLvoid* data) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLCullFace(GrGLenum mode) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDepthMask(GrGLboolean flag) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDisable(GrGLenum cap) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDisableVertexAttribArray(GrGLuint index) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDrawArrays(GrGLenum mode,
+                                               GrGLint first,
+                                               GrGLsizei count) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDrawBuffer(GrGLenum mode) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDrawBuffers(GrGLsizei n,
+                                                const GrGLenum* bufs) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDrawElements(GrGLenum mode,
+                                                 GrGLsizei count,
+                                                 GrGLenum type,
+                                                 const GrGLvoid* indices) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLEnable(GrGLenum cap) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLEnableVertexAttribArray(GrGLuint index) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLEndQuery(GrGLenum target) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLFinish() {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLFlush() {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLFrontFace(GrGLenum mode) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLLineWidth(GrGLfloat width) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLLinkProgram(GrGLuint program) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLPixelStorei(GrGLenum pname,
+                                                GrGLint param) {
+
+    switch (pname) {
+        case GR_GL_UNPACK_ROW_LENGTH:
+            GrDebugGL::getInstance()->setUnPackRowLength(param);
+            break;
+        case GR_GL_PACK_ROW_LENGTH:
+            GrDebugGL::getInstance()->setPackRowLength(param);
+            break;
+        case GR_GL_UNPACK_ALIGNMENT:
+            break;
+        case GR_GL_PACK_ALIGNMENT:
+            GrAlwaysAssert(false);
+            break;
+        default:
+            GrAlwaysAssert(false);
+            break;
+    }
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLQueryCounter(GrGLuint id,
+                                                 GrGLenum target) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLReadBuffer(GrGLenum src) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLReadPixels(GrGLint x,
+                                               GrGLint y,
+                                               GrGLsizei width,
+                                               GrGLsizei height,
+                                               GrGLenum format,
+                                               GrGLenum type,
+                                               GrGLvoid* pixels) {
+
+    GrGLint pixelsInRow = width;
+    if (0 < GrDebugGL::getInstance()->getPackRowLength()) {
+        pixelsInRow = GrDebugGL::getInstance()->getPackRowLength();
+    }
+
+    GrGLint componentsPerPixel = 0;
+
+    switch (format) {
+        case GR_GL_RGBA:
+            // fallthrough
+        case GR_GL_BGRA:
+            componentsPerPixel = 4;
+            break;
+        case GR_GL_RGB:
+            componentsPerPixel = 3;
+            break;
+        case GR_GL_RED:
+            componentsPerPixel = 1;
+            break;
+        default:
+            GrAlwaysAssert(false);
+            break;
+    }
+
+    GrGLint alignment = 4;  // the pack alignment (one of 1, 2, 4 or 8)
+    // Ganesh currently doesn't support setting GR_GL_PACK_ALIGNMENT
+
+    GrGLint componentSize = 0;  // size (in bytes) of a single component
+
+    switch (type) {
+        case GR_GL_UNSIGNED_BYTE:
+            componentSize = 1;
+            break;
+        default:
+            GrAlwaysAssert(false);
+            break;
+    }
+
+    GrGLint rowStride = 0;  // number of components (not bytes) to skip
+    if (componentSize >= alignment) {
+        rowStride = componentsPerPixel * pixelsInRow;
+    } else {
+        float fTemp =
+            sk_float_ceil(componentSize * componentsPerPixel * pixelsInRow /
+                          static_cast<float>(alignment));
+        rowStride = static_cast<GrGLint>(alignment * fTemp / componentSize);
+    }
+
+    GrGLchar *scanline = static_cast<GrGLchar *>(pixels);
+    for (int y = 0; y < height; ++y) {
+        memset(scanline, 0, componentsPerPixel * componentSize * width);
+        scanline += rowStride;
+    }
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLScissor(GrGLint x,
+                                            GrGLint y,
+                                            GrGLsizei width,
+                                            GrGLsizei height) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLShaderSource(GrGLuint shader,
+                                                 GrGLsizei count,
+#if GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE
+                                                 const char* const * str,
+#else
+                                                 const char** str,
+#endif
+                                                 const GrGLint* length) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLStencilFunc(GrGLenum func,
+                                                GrGLint ref,
+                                                GrGLuint mask) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLStencilFuncSeparate(GrGLenum face,
+                                                        GrGLenum func,
+                                                        GrGLint ref,
+                                                        GrGLuint mask) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLStencilMask(GrGLuint mask) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLStencilMaskSeparate(GrGLenum face,
+                                                        GrGLuint mask) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLStencilOp(GrGLenum fail,
+                                              GrGLenum zfail,
+                                              GrGLenum zpass) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLStencilOpSeparate(GrGLenum face,
+                                                      GrGLenum fail,
+                                                      GrGLenum zfail,
+                                                      GrGLenum zpass) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLTexImage2D(GrGLenum target,
+                                               GrGLint level,
+                                               GrGLint internalformat,
+                                               GrGLsizei width,
+                                               GrGLsizei height,
+                                               GrGLint border,
+                                               GrGLenum format,
+                                               GrGLenum type,
+                                               const GrGLvoid* pixels) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLTexParameteri(GrGLenum target,
+                                                  GrGLenum pname,
+                                                  GrGLint param) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLTexParameteriv(GrGLenum target,
+                                                   GrGLenum pname,
+                                                   const GrGLint* params) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLTexStorage2D(GrGLenum target,
+                                                 GrGLsizei levels,
+                                                 GrGLenum internalformat,
+                                                 GrGLsizei width,
+                                                 GrGLsizei height) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLTexSubImage2D(GrGLenum target,
+                                                  GrGLint level,
+                                                  GrGLint xoffset,
+                                                  GrGLint yoffset,
+                                                  GrGLsizei width,
+                                                  GrGLsizei height,
+                                                  GrGLenum format,
+                                                  GrGLenum type,
+                                                  const GrGLvoid* pixels) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform1f(GrGLint location,
+                                              GrGLfloat v0) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform1i(GrGLint location,
+                                              GrGLint v0) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform1fv(GrGLint location,
+                                               GrGLsizei count,
+                                               const GrGLfloat* v) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform1iv(GrGLint location,
+                                               GrGLsizei count,
+                                               const GrGLint* v) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform2f(GrGLint location,
+                                              GrGLfloat v0,
+                                              GrGLfloat v1) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform2i(GrGLint location,
+                                              GrGLint v0,
+                                              GrGLint v1) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform2fv(GrGLint location,
+                                               GrGLsizei count,
+                                               const GrGLfloat* v) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform2iv(GrGLint location,
+                                               GrGLsizei count,
+                                               const GrGLint* v) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform3f(GrGLint location,
+                                              GrGLfloat v0,
+                                              GrGLfloat v1,
+                                              GrGLfloat v2) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform3i(GrGLint location,
+                                              GrGLint v0,
+                                              GrGLint v1,
+                                              GrGLint v2) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform3fv(GrGLint location,
+                                               GrGLsizei count,
+                                               const GrGLfloat* v) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform3iv(GrGLint location,
+                                               GrGLsizei count,
+                                               const GrGLint* v) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform4f(GrGLint location,
+                                              GrGLfloat v0,
+                                              GrGLfloat v1,
+                                              GrGLfloat v2,
+                                              GrGLfloat v3) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform4i(GrGLint location,
+                                              GrGLint v0,
+                                              GrGLint v1,
+                                              GrGLint v2,
+                                              GrGLint v3) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform4fv(GrGLint location,
+                                               GrGLsizei count,
+                                               const GrGLfloat* v) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniform4iv(GrGLint location,
+                                                GrGLsizei count,
+                                                const GrGLint* v) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniformMatrix2fv(GrGLint location,
+                                                      GrGLsizei count,
+                                                      GrGLboolean transpose,
+                                                      const GrGLfloat* value) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniformMatrix3fv(GrGLint location,
+                                                      GrGLsizei count,
+                                                      GrGLboolean transpose,
+                                                      const GrGLfloat* value) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLUniformMatrix4fv(GrGLint location,
+                                                      GrGLsizei count,
+                                                      GrGLboolean transpose,
+                                                      const GrGLfloat* value) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLUseProgram(GrGLuint programID) {
+
+     // A programID of 0 is legal
+     GrProgramObj *program = GR_FIND(programID,
+                                     GrProgramObj,
+                                     GrDebugGL::kProgram_ObjTypes);
+
+     GrDebugGL::getInstance()->useProgram(program);
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLVertexAttrib4fv(GrGLuint indx,
+                                                     const GrGLfloat* values) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLVertexAttribPointer(GrGLuint indx,
+                                                         GrGLint size,
+                                                         GrGLenum type,
+                                                         GrGLboolean normalized,
+                                                         GrGLsizei stride,
+                                                         const GrGLvoid* ptr) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLViewport(GrGLint x,
+                                              GrGLint y,
+                                              GrGLsizei width,
+                                              GrGLsizei height) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLBindFramebuffer(GrGLenum target,
+                                                     GrGLuint frameBufferID) {
+
+     GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
+
+     // a frameBufferID of 0 is acceptable - it binds to the default
+     // frame buffer
+     GrFrameBufferObj *frameBuffer = GR_FIND(frameBufferID,
+                                             GrFrameBufferObj,
+                                             GrDebugGL::kFrameBuffer_ObjTypes);
+
+     GrDebugGL::getInstance()->setFrameBuffer(frameBuffer);
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLBindRenderbuffer(GrGLenum target,
+                                                      GrGLuint renderBufferID) {
+
+     GrAlwaysAssert(GR_GL_RENDERBUFFER == target);
+
+     // a renderBufferID of 0 is acceptable - it unbinds the bound render buffer
+     GrRenderBufferObj *renderBuffer = GR_FIND(renderBufferID,
+                                               GrRenderBufferObj,
+                                               GrDebugGL::kRenderBuffer_ObjTypes);
+
+     GrDebugGL::getInstance()->setRenderBuffer(renderBuffer);
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLDeleteTextures(GrGLsizei n,
+                                                    const GrGLuint* textures) {
+
+     // first potentially unbind the texture
+     // TODO: move this into GrDebugGL as unBindTexture?
+     for (unsigned int i = 0;
+          i < GrDebugGL::getInstance()->getMaxTextureUnits();
+          ++i) {
+         GrTextureUnitObj *pTU = GrDebugGL::getInstance()->getTextureUnit(i);
+
+         if (pTU->getTexture()) {
+             for (int j = 0; j < n; ++j) {
+
+                 if (textures[j] == pTU->getTexture()->getID()) {
+                     // this ID is the current texture - revert the binding to 0
+                     pTU->setTexture(NULL);
+                 }
+             }
+         }
+     }
+
+     // TODO: fuse the following block with DeleteRenderBuffers?
+     // Open GL will remove a deleted render buffer from the active
+     // frame buffer but not from any other frame buffer
+     if (GrDebugGL::getInstance()->getFrameBuffer()) {
+
+         GrFrameBufferObj *frameBuffer = GrDebugGL::getInstance()->getFrameBuffer();
+
+         for (int i = 0; i < n; ++i) {
+
+             if (NULL != frameBuffer->getColor() &&
+                 textures[i] == frameBuffer->getColor()->getID()) {
+                 frameBuffer->setColor(NULL);
+             }
+             if (NULL != frameBuffer->getDepth() &&
+                 textures[i] == frameBuffer->getDepth()->getID()) {
+                 frameBuffer->setDepth(NULL);
+             }
+             if (NULL != frameBuffer->getStencil() &&
+                 textures[i] == frameBuffer->getStencil()->getID()) {
+                 frameBuffer->setStencil(NULL);
+             }
+         }
+     }
+
+     // then actually "delete" the buffers
+     for (int i = 0; i < n; ++i) {
+         GrTextureObj *buffer = GR_FIND(textures[i],
+                                        GrTextureObj,
+                                        GrDebugGL::kTexture_ObjTypes);
+         GrAlwaysAssert(buffer);
+
+         // OpenGL gives no guarantees if a texture is deleted while attached to
+         // something other than the currently bound frame buffer
+         GrAlwaysAssert(!buffer->getBound());
+
+         GrAlwaysAssert(!buffer->getDeleted());
+         buffer->deleteAction();
+     }
+
+ }
+
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLDeleteFramebuffers(GrGLsizei n,
+                                                        const GrGLuint *frameBuffers) {
+
+     // first potentially unbind the buffers
+     if (GrDebugGL::getInstance()->getFrameBuffer()) {
+         for (int i = 0; i < n; ++i) {
+
+             if (frameBuffers[i] ==
+                 GrDebugGL::getInstance()->getFrameBuffer()->getID()) {
+                 // this ID is the current frame buffer - rebind to the default
+                 GrDebugGL::getInstance()->setFrameBuffer(NULL);
+             }
+         }
+     }
+
+     // then actually "delete" the buffers
+     for (int i = 0; i < n; ++i) {
+         GrFrameBufferObj *buffer = GR_FIND(frameBuffers[i],
+                                            GrFrameBufferObj,
+                                            GrDebugGL::kFrameBuffer_ObjTypes);
+         GrAlwaysAssert(buffer);
+
+         GrAlwaysAssert(!buffer->getDeleted());
+         buffer->deleteAction();
+     }
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLDeleteRenderbuffers(GrGLsizei n,
+                                                         const GrGLuint *renderBuffers) {
+
+     // first potentially unbind the buffers
+     if (GrDebugGL::getInstance()->getRenderBuffer()) {
+         for (int i = 0; i < n; ++i) {
+
+             if (renderBuffers[i] ==
+                 GrDebugGL::getInstance()->getRenderBuffer()->getID()) {
+                 // this ID is the current render buffer - make no
+                 // render buffer be bound
+                 GrDebugGL::getInstance()->setRenderBuffer(NULL);
+             }
+         }
+     }
+
+     // TODO: fuse the following block with DeleteTextures?
+     // Open GL will remove a deleted render buffer from the active frame
+     // buffer but not from any other frame buffer
+     if (GrDebugGL::getInstance()->getFrameBuffer()) {
+
+         GrFrameBufferObj *frameBuffer =
+                               GrDebugGL::getInstance()->getFrameBuffer();
+
+         for (int i = 0; i < n; ++i) {
+
+             if (NULL != frameBuffer->getColor() &&
+                 renderBuffers[i] == frameBuffer->getColor()->getID()) {
+                 frameBuffer->setColor(NULL);
+             }
+             if (NULL != frameBuffer->getDepth() &&
+                 renderBuffers[i] == frameBuffer->getDepth()->getID()) {
+                 frameBuffer->setDepth(NULL);
+             }
+             if (NULL != frameBuffer->getStencil() &&
+                 renderBuffers[i] == frameBuffer->getStencil()->getID()) {
+                 frameBuffer->setStencil(NULL);
+             }
+         }
+     }
+
+     // then actually "delete" the buffers
+     for (int i = 0; i < n; ++i) {
+         GrRenderBufferObj *buffer = GR_FIND(renderBuffers[i],
+                                             GrRenderBufferObj,
+                                             GrDebugGL::kRenderBuffer_ObjTypes);
+         GrAlwaysAssert(buffer);
+
+         // OpenGL gives no guarantees if a render buffer is deleted
+         // while attached to something other than the currently
+         // bound frame buffer
+         GrAlwaysAssert(!buffer->getColorBound());
+         GrAlwaysAssert(!buffer->getDepthBound());
+         GrAlwaysAssert(!buffer->getStencilBound());
+
+         GrAlwaysAssert(!buffer->getDeleted());
+         buffer->deleteAction();
+     }
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLFramebufferRenderbuffer(GrGLenum target,
+                                                             GrGLenum attachment,
+                                                             GrGLenum renderbuffertarget,
+                                                             GrGLuint renderBufferID) {
+
+     GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
+     GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment ||
+                    GR_GL_DEPTH_ATTACHMENT == attachment ||
+                    GR_GL_STENCIL_ATTACHMENT == attachment);
+     GrAlwaysAssert(GR_GL_RENDERBUFFER == renderbuffertarget);
+
+     GrFrameBufferObj *framebuffer = GrDebugGL::getInstance()->getFrameBuffer();
+     // A render buffer cannot be attached to the default framebuffer
+     GrAlwaysAssert(NULL != framebuffer);
+
+     // a renderBufferID of 0 is acceptable - it unbinds the current
+     // render buffer
+     GrRenderBufferObj *renderbuffer = GR_FIND(renderBufferID,
+                                               GrRenderBufferObj,
+                                               GrDebugGL::kRenderBuffer_ObjTypes);
+
+     switch (attachment) {
+     case GR_GL_COLOR_ATTACHMENT0:
+         framebuffer->setColor(renderbuffer);
+         break;
+     case GR_GL_DEPTH_ATTACHMENT:
+         framebuffer->setDepth(renderbuffer);
+         break;
+     case GR_GL_STENCIL_ATTACHMENT:
+         framebuffer->setStencil(renderbuffer);
+         break;
+     default:
+         GrAlwaysAssert(false);
+         break;
+     };
+
+ }
+
+ ////////////////////////////////////////////////////////////////////////////////
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLFramebufferTexture2D(GrGLenum target,
+                                                          GrGLenum attachment,
+                                                          GrGLenum textarget,
+                                                          GrGLuint textureID,
+                                                          GrGLint level) {
+
+     GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
+     GrAlwaysAssert(GR_GL_COLOR_ATTACHMENT0 == attachment ||
+                    GR_GL_DEPTH_ATTACHMENT == attachment ||
+                    GR_GL_STENCIL_ATTACHMENT == attachment);
+     GrAlwaysAssert(GR_GL_TEXTURE_2D == textarget);
+
+     GrFrameBufferObj *framebuffer = GrDebugGL::getInstance()->getFrameBuffer();
+     // A texture cannot be attached to the default framebuffer
+     GrAlwaysAssert(NULL != framebuffer);
+
+     // A textureID of 0 is allowed - it unbinds the currently bound texture
+     GrTextureObj *texture = GR_FIND(textureID, GrTextureObj,
+                                     GrDebugGL::kTexture_ObjTypes);
+     if (texture) {
+         // The texture shouldn't be bound to a texture unit - this
+         // could lead to a feedback loop
+         GrAlwaysAssert(!texture->getBound());
+     }
+
+     GrAlwaysAssert(0 == level);
+
+     switch (attachment) {
+     case GR_GL_COLOR_ATTACHMENT0:
+         framebuffer->setColor(texture);
+         break;
+     case GR_GL_DEPTH_ATTACHMENT:
+         framebuffer->setDepth(texture);
+         break;
+     case GR_GL_STENCIL_ATTACHMENT:
+         framebuffer->setStencil(texture);
+         break;
+     default:
+         GrAlwaysAssert(false);
+         break;
+     };
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetFramebufferAttachmentParameteriv(GrGLenum target,
+                                                                         GrGLenum attachment,
+                                                                         GrGLenum pname,
+                                                                         GrGLint* params) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetRenderbufferParameteriv(GrGLenum target,
+                                                                GrGLenum pname,
+                                                                GrGLint* params) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLRenderbufferStorage(GrGLenum target,
+                                                         GrGLenum internalformat,
+                                                         GrGLsizei width,
+                                                         GrGLsizei height) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLRenderbufferStorageMultisample(GrGLenum target,
+                                                                    GrGLsizei samples,
+                                                                    GrGLenum internalformat,
+                                                                    GrGLsizei width,
+                                                                    GrGLsizei height) {
+ }
+
+ GrGLvoid GR_GL_FUNCTION_TYPE debugGLBlitFramebuffer(GrGLint srcX0,
+                                                     GrGLint srcY0,
+                                                     GrGLint srcX1,
+                                                     GrGLint srcY1,
+                                                     GrGLint dstX0,
+                                                    GrGLint dstY0,
+                                                    GrGLint dstX1,
+                                                    GrGLint dstY1,
+                                                    GrGLbitfield mask,
+                                                    GrGLenum filter) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLResolveMultisampleFramebuffer() {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBindFragDataLocationIndexed(GrGLuint program,
+                                                                GrGLuint colorNumber,
+                                                                GrGLuint index,
+                                                                const GrGLchar * name) {
+}
+
+GrGLenum GR_GL_FUNCTION_TYPE debugGLCheckFramebufferStatus(GrGLenum target) {
+
+    GrAlwaysAssert(GR_GL_FRAMEBUFFER == target);
+
+    return GR_GL_FRAMEBUFFER_COMPLETE;
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE debugGLCreateProgram() {
+
+    GrProgramObj *program = GR_CREATE(GrProgramObj,
+                                      GrDebugGL::kProgram_ObjTypes);
+
+    return program->getID();
+}
+
+GrGLuint GR_GL_FUNCTION_TYPE debugGLCreateShader(GrGLenum type) {
+
+    GrAlwaysAssert(GR_GL_VERTEX_SHADER == type ||
+                   GR_GL_FRAGMENT_SHADER == type);
+
+    GrShaderObj *shader = GR_CREATE(GrShaderObj, GrDebugGL::kShader_ObjTypes);
+    shader->setType(type);
+
+    return shader->getID();
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDeleteProgram(GrGLuint programID) {
+
+    GrProgramObj *program = GR_FIND(programID,
+                                    GrProgramObj,
+                                    GrDebugGL::kProgram_ObjTypes);
+    GrAlwaysAssert(program);
+
+    if (program->getRefCount()) {
+        // someone is still using this program so we can't delete it here
+        program->setMarkedForDeletion();
+    } else {
+        program->deleteAction();
+    }
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDeleteShader(GrGLuint shaderID) {
+
+    GrShaderObj *shader = GR_FIND(shaderID,
+                                  GrShaderObj,
+                                  GrDebugGL::kShader_ObjTypes);
+    GrAlwaysAssert(shader);
+
+    if (shader->getRefCount()) {
+        // someone is still using this shader so we can't delete it here
+        shader->setMarkedForDeletion();
+    } else {
+        shader->deleteAction();
+    }
+}
+
+// same function used for all glGen*(GLsize i, GLuint*) functions
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGenIds(GrGLsizei n, GrGLuint* ids) {
+    static int gCurrID = 1;
+    for (int i = 0; i < n; ++i) {
+        ids[i] = ++gCurrID;
+    }
+}
+
+GrGLvoid debugGenObjs(GrDebugGL::GrObjTypes type,
+                      GrGLsizei n,
+                      GrGLuint* ids) {
+
+   for (int i = 0; i < n; ++i) {
+        GrFakeRefObj *obj = GrDebugGL::getInstance()->createObj(type);
+        GrAlwaysAssert(obj);
+        ids[i] = obj->getID();
+    }
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGenBuffers(GrGLsizei n, GrGLuint* ids) {
+
+    debugGenObjs(GrDebugGL::kBuffer_ObjTypes, n, ids);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGenFramebuffers(GrGLsizei n,
+                                                    GrGLuint* ids) {
+
+    debugGenObjs(GrDebugGL::kFrameBuffer_ObjTypes, n, ids);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGenRenderbuffers(GrGLsizei n,
+                                                     GrGLuint* ids) {
+
+    debugGenObjs(GrDebugGL::kRenderBuffer_ObjTypes, n, ids);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGenTextures(GrGLsizei n, GrGLuint* ids) {
+
+    debugGenObjs(GrDebugGL::kTexture_ObjTypes, n, ids);
+}
+
+// same delete function for all glDelete*(GLsize i, const GLuint*) except
+// buffers
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDeleteIds(GrGLsizei n,
+                                              const GrGLuint* ids) {
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLBindBuffer(GrGLenum target,
+                                               GrGLuint bufferID) {
+
+    GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+                   GR_GL_ELEMENT_ARRAY_BUFFER == target);
+
+    GrBufferObj *buffer = GR_FIND(bufferID,
+                                  GrBufferObj,
+                                  GrDebugGL::kBuffer_ObjTypes);
+    // 0 is a permissable bufferID - it unbinds the current buffer
+
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            GrDebugGL::getInstance()->setArrayBuffer(buffer);
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            GrDebugGL::getInstance()->setElementArrayBuffer(buffer);
+            break;
+        default:
+            GrCrash("Unexpected target to glBindBuffer");
+            break;
+    }
+}
+
+// deleting a bound buffer has the side effect of binding 0
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLDeleteBuffers(GrGLsizei n,
+                                                  const GrGLuint* ids) {
+    // first potentially unbind the buffers
+    for (int i = 0; i < n; ++i) {
+
+        if (GrDebugGL::getInstance()->getArrayBuffer() &&
+            ids[i] == GrDebugGL::getInstance()->getArrayBuffer()->getID()) {
+            // this ID is the current array buffer
+            GrDebugGL::getInstance()->setArrayBuffer(NULL);
+        }
+        if (GrDebugGL::getInstance()->getElementArrayBuffer() &&
+            ids[i] ==
+                GrDebugGL::getInstance()->getElementArrayBuffer()->getID()) {
+            // this ID is the current element array buffer
+            GrDebugGL::getInstance()->setElementArrayBuffer(NULL);
+        }
+    }
+
+    // then actually "delete" the buffers
+    for (int i = 0; i < n; ++i) {
+        GrBufferObj *buffer = GR_FIND(ids[i],
+                                      GrBufferObj,
+                                      GrDebugGL::kBuffer_ObjTypes);
+        GrAlwaysAssert(buffer);
+
+        GrAlwaysAssert(!buffer->getDeleted());
+        buffer->deleteAction();
+    }
+}
+
+// map a buffer to the caller's address space
+GrGLvoid* GR_GL_FUNCTION_TYPE debugGLMapBuffer(GrGLenum target,
+                                               GrGLenum access) {
+
+    GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+                   GR_GL_ELEMENT_ARRAY_BUFFER == target);
+    // GR_GL_READ_ONLY == access ||  || GR_GL_READ_WRIT == access);
+    GrAlwaysAssert(GR_GL_WRITE_ONLY == access);
+
+    GrBufferObj *buffer = NULL;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getArrayBuffer();
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getElementArrayBuffer();
+            break;
+        default:
+            GrCrash("Unexpected target to glMapBuffer");
+            break;
+    }
+
+    if (buffer) {
+        GrAlwaysAssert(!buffer->getMapped());
+        buffer->setMapped();
+        return buffer->getDataPtr();
+    }
+
+    GrAlwaysAssert(false);
+    return NULL;        // no buffer bound to the target
+}
+
+// remove a buffer from the caller's address space
+// TODO: check if the "access" method from "glMapBuffer" was honored
+GrGLboolean GR_GL_FUNCTION_TYPE debugGLUnmapBuffer(GrGLenum target) {
+
+    GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+                   GR_GL_ELEMENT_ARRAY_BUFFER == target);
+
+    GrBufferObj *buffer = NULL;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getArrayBuffer();
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getElementArrayBuffer();
+            break;
+        default:
+            GrCrash("Unexpected target to glUnmapBuffer");
+            break;
+    }
+
+    if (buffer) {
+        GrAlwaysAssert(buffer->getMapped());
+        buffer->resetMapped();
+        return GR_GL_TRUE;
+    }
+
+    GrAlwaysAssert(false);
+    return GR_GL_FALSE; // GR_GL_INVALID_OPERATION;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetBufferParameteriv(GrGLenum target,
+                                                         GrGLenum value,
+                                                         GrGLint* params) {
+
+    GrAlwaysAssert(GR_GL_ARRAY_BUFFER == target ||
+                   GR_GL_ELEMENT_ARRAY_BUFFER == target);
+    GrAlwaysAssert(GR_GL_BUFFER_SIZE == value ||
+                   GR_GL_BUFFER_USAGE == value);
+
+    GrBufferObj *buffer = NULL;
+    switch (target) {
+        case GR_GL_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getArrayBuffer();
+            break;
+        case GR_GL_ELEMENT_ARRAY_BUFFER:
+            buffer = GrDebugGL::getInstance()->getElementArrayBuffer();
+            break;
+    }
+
+    GrAlwaysAssert(buffer);
+
+    switch (value) {
+        case GR_GL_BUFFER_MAPPED:
+            *params = GR_GL_FALSE;
+            if (buffer)
+                *params = buffer->getMapped() ? GR_GL_TRUE : GR_GL_FALSE;
+            break;
+        case GR_GL_BUFFER_SIZE:
+            *params = 0;
+            if (buffer)
+                *params = buffer->getSize();
+            break;
+        case GR_GL_BUFFER_USAGE:
+            *params = GR_GL_STATIC_DRAW;
+            if (buffer)
+                *params = buffer->getUsage();
+            break;
+        default:
+            GrCrash("Unexpected value to glGetBufferParamateriv");
+            break;
+    }
+};
+
+GrGLenum GR_GL_FUNCTION_TYPE debugGLGetError() {
+    return GR_GL_NO_ERROR;
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetIntegerv(GrGLenum pname,
+                                                GrGLint* params) {
+    // TODO: remove from Ganesh the #defines for gets we don't use.
+    // We would like to minimize gets overall due to performance issues
+    switch (pname) {
+        case GR_GL_STENCIL_BITS:
+            *params = 8;
+            break;
+        case GR_GL_SAMPLES:
+            *params = 1;
+            break;
+        case GR_GL_FRAMEBUFFER_BINDING:
+            *params = 0;
+            break;
+        case GR_GL_VIEWPORT:
+            params[0] = 0;
+            params[1] = 0;
+            params[2] = 800;
+            params[3] = 600;
+            break;
+        case GR_GL_MAX_TEXTURE_IMAGE_UNITS:
+            *params = 8;
+            break;
+        case GR_GL_MAX_VERTEX_UNIFORM_VECTORS:
+            *params = kDefaultMaxVertexUniformVectors;
+            break;
+        case GR_GL_MAX_FRAGMENT_UNIFORM_VECTORS:
+            *params = kDefaultMaxFragmentUniformVectors;
+            break;
+        case GR_GL_MAX_FRAGMENT_UNIFORM_COMPONENTS:
+            *params = 16 * 4;
+            break;
+        case GR_GL_NUM_COMPRESSED_TEXTURE_FORMATS:
+            *params = 0;
+            break;
+        case GR_GL_COMPRESSED_TEXTURE_FORMATS:
+            break;
+        case GR_GL_MAX_TEXTURE_SIZE:
+            *params = 8192;
+            break;
+        case GR_GL_MAX_RENDERBUFFER_SIZE:
+            *params = 8192;
+            break;
+        case GR_GL_MAX_SAMPLES:
+            *params = 32;
+            break;
+        case GR_GL_MAX_VERTEX_ATTRIBS:
+            *params = kDefaultMaxVertexAttribs;
+            break;
+        case GR_GL_MAX_VARYING_VECTORS:
+            *params = kDefaultMaxVaryingVectors;
+            break;
+        default:
+            GrCrash("Unexpected pname to GetIntegerv");
+    }
+}
+// used for both the program and shader info logs
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetInfoLog(GrGLuint program,
+                                               GrGLsizei bufsize,
+                                               GrGLsizei* length,
+                                               char* infolog) {
+    if (length) {
+        *length = 0;
+    }
+    if (bufsize > 0) {
+        *infolog = 0;
+    }
+}
+
+// used for both the program and shader params
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetShaderOrProgramiv(GrGLuint program,
+                                                         GrGLenum pname,
+                                                         GrGLint* params) {
+    switch (pname) {
+        case GR_GL_LINK_STATUS:  // fallthru
+        case GR_GL_COMPILE_STATUS:
+            *params = GR_GL_TRUE;
+            break;
+        case GR_GL_INFO_LOG_LENGTH:
+            *params = 0;
+            break;
+        // we don't expect any other pnames
+        default:
+            GrCrash("Unexpected pname to GetProgramiv");
+            break;
+    }
+}
+
+namespace {
+template <typename T>
+void query_result(GrGLenum GLtarget, GrGLenum pname, T *params) {
+    switch (pname) {
+        case GR_GL_QUERY_RESULT_AVAILABLE:
+            *params = GR_GL_TRUE;
+            break;
+        case GR_GL_QUERY_RESULT:
+            *params = 0;
+            break;
+        default:
+            GrCrash("Unexpected pname passed to GetQueryObject.");
+            break;
+    }
+}
+}
+
+// Queries on the null GL just don't do anything at all. We could potentially
+// make the timers work.
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetQueryiv(GrGLenum GLtarget,
+                                               GrGLenum pname,
+                                               GrGLint *params) {
+    switch (pname) {
+        case GR_GL_CURRENT_QUERY:
+            *params = 0;
+            break;
+        case GR_GL_QUERY_COUNTER_BITS:
+            *params = 32;
+            break;
+        default:
+            GrCrash("Unexpected pname passed GetQueryiv.");
+    }
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetQueryObjecti64v(GrGLuint id,
+                                                       GrGLenum pname,
+                                                       GrGLint64 *params) {
+    query_result(id, pname, params);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetQueryObjectiv(GrGLuint id,
+                                                     GrGLenum pname,
+                                                     GrGLint *params) {
+    query_result(id, pname, params);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetQueryObjectui64v(GrGLuint id,
+                                                        GrGLenum pname,
+                                                        GrGLuint64 *params) {
+    query_result(id, pname, params);
+}
+
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetQueryObjectuiv(GrGLuint id,
+                                                      GrGLenum pname,
+                                                      GrGLuint *params) {
+    query_result(id, pname, params);
+}
+
+const GrGLubyte* GR_GL_FUNCTION_TYPE debugGLGetString(GrGLenum name) {
+    switch (name) {
+        case GR_GL_EXTENSIONS:
+            return (const GrGLubyte*)"GL_ARB_framebuffer_object GL_ARB_blend_func_extended GL_ARB_timer_query GL_ARB_draw_buffers GL_ARB_occlusion_query GL_EXT_blend_color GL_EXT_stencil_wrap";
+        case GR_GL_VERSION:
+            return (const GrGLubyte*)"4.0 Debug GL";
+        case GR_GL_SHADING_LANGUAGE_VERSION:
+            return (const GrGLubyte*)"4.20.8 Debug GLSL";
+        case GR_GL_VENDOR:
+            return (const GrGLubyte*)"Debug Vendor";
+        case GR_GL_RENDERER:
+            return (const GrGLubyte*)"The Debug (Non-)Renderer";
+        default:
+            GrCrash("Unexpected name to GetString");
+            return NULL;
+    }
+}
+
+// we used to use this to query stuff about externally created textures,
+// now we just require clients to tell us everything about the texture.
+GrGLvoid GR_GL_FUNCTION_TYPE debugGLGetTexLevelParameteriv(GrGLenum target,
+                                                           GrGLint level,
+                                                           GrGLenum pname,
+                                                           GrGLint* params) {
+    GrCrash("Should never query texture parameters.");
+}
+
+GrGLint GR_GL_FUNCTION_TYPE debugGLGetUniformLocation(GrGLuint program,
+                                                      const char* name) {
+    static int gUniLocation = 0;
+    return ++gUniLocation;
+}
+
+} // end of namespace
+
+////////////////////////////////////////////////////////////////////////////////
+struct GrDebugGLInterface : public GrGLInterface {
+
+public:
+    SK_DECLARE_INST_COUNT(GrDebugGLInterface)
+
+    GrDebugGLInterface()
+        : fWrapped(NULL) {
+        GrDebugGL::staticRef();
+    }
+
+    virtual ~GrDebugGLInterface() {
+        GrDebugGL::staticUnRef();
+    }
+
+    void setWrapped(GrGLInterface *interface) {
+        fWrapped.reset(interface);
+    }
+
+    // TODO: there are some issues w/ wrapping another GL interface inside the
+    // debug interface:
+    //      Since none of the "gl" methods are member functions they don't get
+    //      a "this" pointer through which to access "fWrapped"
+    //      This could be worked around by having all of them access the
+    //      "glInterface" pointer - i.e., treating the debug interface as a
+    //      true singleton
+    //
+    //      The problem with this is that we also want to handle OpenGL
+    //      contexts. The natural way to do this is to have multiple debug
+    //      interfaces. Each of which represents a separate context. The
+    //      static ID count would still uniquify IDs across all of them.
+    //      The problem then is that we couldn't treat the debug GL
+    //      interface as a singleton (since there would be one for each
+    //      context).
+    //
+    //      The solution to this is probably to alter SkDebugGlContext's
+    //      "makeCurrent" method to make a call like "makeCurrent(this)" to
+    //      the debug GL interface (assuming that the application will create
+    //      multiple SkGLContext's) to let it switch between the active
+    //      context. Everything in the GrDebugGL object would then need to be
+    //      moved to a GrContextObj and the GrDebugGL object would just switch
+    //      between them. Note that this approach would also require that
+    //      SkDebugGLContext wrap an arbitrary other context
+    //      and then pass the wrapped interface to the debug GL interface.
+
+protected:
+private:
+
+    SkAutoTUnref<GrGLInterface> fWrapped;
+
+    typedef GrGLInterface INHERITED;
+};
+
+SK_DEFINE_INST_COUNT(GrDebugGLInterface)
+
+////////////////////////////////////////////////////////////////////////////////
+const GrGLInterface* GrGLCreateDebugInterface() {
+    GrGLInterface* interface = SkNEW(GrDebugGLInterface);
+
+    interface->fBindingsExported = kDesktop_GrGLBinding;
+    interface->fActiveTexture = debugGLActiveTexture;
+    interface->fAttachShader = debugGLAttachShader;
+    interface->fBeginQuery = debugGLBeginQuery;
+    interface->fBindAttribLocation = debugGLBindAttribLocation;
+    interface->fBindBuffer = debugGLBindBuffer;
+    interface->fBindFragDataLocation = debugGLBindFragDataLocation;
+    interface->fBindTexture = debugGLBindTexture;
+    interface->fBlendColor = debugGLBlendColor;
+    interface->fBlendFunc = debugGLBlendFunc;
+    interface->fBufferData = debugGLBufferData;
+    interface->fBufferSubData = debugGLBufferSubData;
+    interface->fClear = debugGLClear;
+    interface->fClearColor = debugGLClearColor;
+    interface->fClearStencil = debugGLClearStencil;
+    interface->fColorMask = debugGLColorMask;
+    interface->fCompileShader = debugGLCompileShader;
+    interface->fCompressedTexImage2D = debugGLCompressedTexImage2D;
+    interface->fCreateProgram = debugGLCreateProgram;
+    interface->fCreateShader = debugGLCreateShader;
+    interface->fCullFace = debugGLCullFace;
+    interface->fDeleteBuffers = debugGLDeleteBuffers;
+    interface->fDeleteProgram = debugGLDeleteProgram;
+    interface->fDeleteQueries = debugGLDeleteIds;
+    interface->fDeleteShader = debugGLDeleteShader;
+    interface->fDeleteTextures = debugGLDeleteTextures;
+    interface->fDepthMask = debugGLDepthMask;
+    interface->fDisable = debugGLDisable;
+    interface->fDisableVertexAttribArray = debugGLDisableVertexAttribArray;
+    interface->fDrawArrays = debugGLDrawArrays;
+    interface->fDrawBuffer = debugGLDrawBuffer;
+    interface->fDrawBuffers = debugGLDrawBuffers;
+    interface->fDrawElements = debugGLDrawElements;
+    interface->fEnable = debugGLEnable;
+    interface->fEnableVertexAttribArray = debugGLEnableVertexAttribArray;
+    interface->fEndQuery = debugGLEndQuery;
+    interface->fFinish = debugGLFinish;
+    interface->fFlush = debugGLFlush;
+    interface->fFrontFace = debugGLFrontFace;
+    interface->fGenBuffers = debugGLGenBuffers;
+    interface->fGenQueries = debugGLGenIds;
+    interface->fGenTextures = debugGLGenTextures;
+    interface->fGetBufferParameteriv = debugGLGetBufferParameteriv;
+    interface->fGetError = debugGLGetError;
+    interface->fGetIntegerv = debugGLGetIntegerv;
+    interface->fGetQueryObjecti64v = debugGLGetQueryObjecti64v;
+    interface->fGetQueryObjectiv = debugGLGetQueryObjectiv;
+    interface->fGetQueryObjectui64v = debugGLGetQueryObjectui64v;
+    interface->fGetQueryObjectuiv = debugGLGetQueryObjectuiv;
+    interface->fGetQueryiv = debugGLGetQueryiv;
+    interface->fGetProgramInfoLog = debugGLGetInfoLog;
+    interface->fGetProgramiv = debugGLGetShaderOrProgramiv;
+    interface->fGetShaderInfoLog = debugGLGetInfoLog;
+    interface->fGetShaderiv = debugGLGetShaderOrProgramiv;
+    interface->fGetString = debugGLGetString;
+    interface->fGetTexLevelParameteriv = debugGLGetTexLevelParameteriv;
+    interface->fGetUniformLocation = debugGLGetUniformLocation;
+    interface->fLineWidth = debugGLLineWidth;
+    interface->fLinkProgram = debugGLLinkProgram;
+    interface->fPixelStorei = debugGLPixelStorei;
+    interface->fQueryCounter = debugGLQueryCounter;
+    interface->fReadBuffer = debugGLReadBuffer;
+    interface->fReadPixels = debugGLReadPixels;
+    interface->fScissor = debugGLScissor;
+    interface->fShaderSource = debugGLShaderSource;
+    interface->fStencilFunc = debugGLStencilFunc;
+    interface->fStencilFuncSeparate = debugGLStencilFuncSeparate;
+    interface->fStencilMask = debugGLStencilMask;
+    interface->fStencilMaskSeparate = debugGLStencilMaskSeparate;
+    interface->fStencilOp = debugGLStencilOp;
+    interface->fStencilOpSeparate = debugGLStencilOpSeparate;
+    interface->fTexImage2D = debugGLTexImage2D;
+    interface->fTexParameteri = debugGLTexParameteri;
+    interface->fTexParameteriv = debugGLTexParameteriv;
+    interface->fTexSubImage2D = debugGLTexSubImage2D;
+    interface->fTexStorage2D = debugGLTexStorage2D;
+    interface->fUniform1f = debugGLUniform1f;
+    interface->fUniform1i = debugGLUniform1i;
+    interface->fUniform1fv = debugGLUniform1fv;
+    interface->fUniform1iv = debugGLUniform1iv;
+    interface->fUniform2f = debugGLUniform2f;
+    interface->fUniform2i = debugGLUniform2i;
+    interface->fUniform2fv = debugGLUniform2fv;
+    interface->fUniform2iv = debugGLUniform2iv;
+    interface->fUniform3f = debugGLUniform3f;
+    interface->fUniform3i = debugGLUniform3i;
+    interface->fUniform3fv = debugGLUniform3fv;
+    interface->fUniform3iv = debugGLUniform3iv;
+    interface->fUniform4f = debugGLUniform4f;
+    interface->fUniform4i = debugGLUniform4i;
+    interface->fUniform4fv = debugGLUniform4fv;
+    interface->fUniform4iv = debugGLUniform4iv;
+    interface->fUniformMatrix2fv = debugGLUniformMatrix2fv;
+    interface->fUniformMatrix3fv = debugGLUniformMatrix3fv;
+    interface->fUniformMatrix4fv = debugGLUniformMatrix4fv;
+    interface->fUseProgram = debugGLUseProgram;
+    interface->fVertexAttrib4fv = debugGLVertexAttrib4fv;
+    interface->fVertexAttribPointer = debugGLVertexAttribPointer;
+    interface->fViewport = debugGLViewport;
+    interface->fBindFramebuffer = debugGLBindFramebuffer;
+    interface->fBindRenderbuffer = debugGLBindRenderbuffer;
+    interface->fCheckFramebufferStatus = debugGLCheckFramebufferStatus;
+    interface->fDeleteFramebuffers = debugGLDeleteFramebuffers;
+    interface->fDeleteRenderbuffers = debugGLDeleteRenderbuffers;
+    interface->fFramebufferRenderbuffer = debugGLFramebufferRenderbuffer;
+    interface->fFramebufferTexture2D = debugGLFramebufferTexture2D;
+    interface->fGenFramebuffers = debugGLGenFramebuffers;
+    interface->fGenRenderbuffers = debugGLGenRenderbuffers;
+    interface->fGetFramebufferAttachmentParameteriv =
+                                    debugGLGetFramebufferAttachmentParameteriv;
+    interface->fGetRenderbufferParameteriv = debugGLGetRenderbufferParameteriv;
+    interface->fRenderbufferStorage = debugGLRenderbufferStorage;
+    interface->fRenderbufferStorageMultisample =
+                                    debugGLRenderbufferStorageMultisample;
+    interface->fBlitFramebuffer = debugGLBlitFramebuffer;
+    interface->fResolveMultisampleFramebuffer =
+                                    debugGLResolveMultisampleFramebuffer;
+    interface->fMapBuffer = debugGLMapBuffer;
+    interface->fUnmapBuffer = debugGLUnmapBuffer;
+    interface->fBindFragDataLocationIndexed =
+                                    debugGLBindFragDataLocationIndexed;
+
+    return interface;
+}
diff --git a/src/gpu/gl/debug/GrProgramObj.cpp b/src/gpu/gl/debug/GrProgramObj.cpp
new file mode 100644
index 0000000..d6cc36b
--- /dev/null
+++ b/src/gpu/gl/debug/GrProgramObj.cpp
@@ -0,0 +1,27 @@
+
+/*
+ * 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 "GrProgramObj.h"
+#include "GrShaderObj.h"
+
+void GrProgramObj::AttachShader(GrShaderObj *shader) {
+    shader->ref();
+    fShaders.push_back(shader);
+}
+
+void GrProgramObj::deleteAction() {
+
+    // shaders are automatically detached from a deleted program. They will only be
+    // deleted if they were marked for deletion by a prior call to glDeleteShader
+    for (int i = 0; i < fShaders.count(); ++i) {
+        fShaders[i]->unref();
+    }
+    fShaders.reset();
+
+    this->INHERITED::deleteAction();
+}
diff --git a/src/gpu/gl/debug/GrProgramObj.h b/src/gpu/gl/debug/GrProgramObj.h
new file mode 100644
index 0000000..eebcc88
--- /dev/null
+++ b/src/gpu/gl/debug/GrProgramObj.h
@@ -0,0 +1,43 @@
+
+/*
+ * 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 GrProgramObj_DEFINED
+#define GrProgramObj_DEFINED
+
+#include "SkTArray.h"
+#include "GrFakeRefObj.h"
+class GrShaderObj;
+
+////////////////////////////////////////////////////////////////////////////////
+class GrProgramObj : public GrFakeRefObj {
+    GR_DEFINE_CREATOR(GrProgramObj);
+
+public:
+    GrProgramObj()
+        : GrFakeRefObj()
+        , fInUse(false) {}
+
+    void AttachShader(GrShaderObj *shader);
+
+    virtual void deleteAction() SK_OVERRIDE;
+
+    // TODO: this flag system won't work w/ multiple contexts!
+    void setInUse()         { fInUse = true; }
+    void resetInUse()       { fInUse = false; }
+    bool getInUse() const   { return fInUse; }
+
+protected:
+
+private:
+    SkTArray<GrShaderObj *> fShaders;
+    bool fInUse;            // has this program been activated by a glUseProgram call?
+
+    typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrProgramObj_DEFINED
diff --git a/src/gpu/gl/debug/GrRenderBufferObj.h b/src/gpu/gl/debug/GrRenderBufferObj.h
new file mode 100644
index 0000000..344b90e
--- /dev/null
+++ b/src/gpu/gl/debug/GrRenderBufferObj.h
@@ -0,0 +1,40 @@
+
+/*
+ * 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 GrRenderBufferObj_DEFINED
+#define GrRenderBufferObj_DEFINED
+
+#include "GrFBBindableObj.h"
+
+////////////////////////////////////////////////////////////////////////////////
+class GrRenderBufferObj : public GrFBBindableObj {
+    GR_DEFINE_CREATOR(GrRenderBufferObj);
+
+public:
+    GrRenderBufferObj()
+        : GrFBBindableObj()
+        , fBound(false) {
+    }
+
+    void setBound()         { fBound = true; }
+    void resetBound()       { fBound = false; }
+    bool getBound() const   { return fBound; }
+
+    virtual void deleteAction() SK_OVERRIDE {
+
+        this->INHERITED::deleteAction();
+    }
+
+protected:
+private:
+    bool fBound;           // is this render buffer currently bound via "glBindRenderbuffer"?
+
+    typedef GrFBBindableObj INHERITED;
+};
+
+#endif // GrRenderBufferObj_DEFINED
diff --git a/src/gpu/gl/debug/GrShaderObj.cpp b/src/gpu/gl/debug/GrShaderObj.cpp
new file mode 100644
index 0000000..8d3caa1
--- /dev/null
+++ b/src/gpu/gl/debug/GrShaderObj.cpp
@@ -0,0 +1,14 @@
+
+/*
+ * 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 "GrShaderObj.h"
+
+void GrShaderObj::deleteAction() {
+
+    this->INHERITED::deleteAction();
+}
diff --git a/src/gpu/gl/debug/GrShaderObj.h b/src/gpu/gl/debug/GrShaderObj.h
new file mode 100644
index 0000000..0b888fa
--- /dev/null
+++ b/src/gpu/gl/debug/GrShaderObj.h
@@ -0,0 +1,36 @@
+
+/*
+ * 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 GrShaderObj_DEFINED
+#define GrShaderObj_DEFINED
+
+#include "GrFakeRefObj.h"
+#include "../GrGLDefines.h"
+
+////////////////////////////////////////////////////////////////////////////////
+class GrShaderObj : public GrFakeRefObj {
+    GR_DEFINE_CREATOR(GrShaderObj);
+
+public:
+    GrShaderObj()
+        : GrFakeRefObj()
+        , fType(GR_GL_VERTEX_SHADER)    {}
+
+    void setType(GrGLenum type)         { fType = type; }
+    GrGLenum getType()                  { return fType; }
+
+    virtual void deleteAction() SK_OVERRIDE;
+
+protected:
+private:
+    GrGLenum fType;  // either GR_GL_VERTEX_SHADER or GR_GL_FRAGMENT_SHADER
+
+    typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrShaderObj_DEFINED
diff --git a/src/gpu/gl/debug/GrTextureObj.cpp b/src/gpu/gl/debug/GrTextureObj.cpp
new file mode 100644
index 0000000..86063fb
--- /dev/null
+++ b/src/gpu/gl/debug/GrTextureObj.cpp
@@ -0,0 +1,14 @@
+
+/*
+ * 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 "GrTextureObj.h"
+
+void GrTextureObj::deleteAction() {
+
+    this->INHERITED::deleteAction();
+}
diff --git a/src/gpu/gl/debug/GrTextureObj.h b/src/gpu/gl/debug/GrTextureObj.h
new file mode 100644
index 0000000..7673cd1
--- /dev/null
+++ b/src/gpu/gl/debug/GrTextureObj.h
@@ -0,0 +1,57 @@
+
+/*
+ * 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 GrTextureObj_DEFINED
+#define GrTextureObj_DEFINED
+
+#include "GrFBBindableObj.h"
+
+class GrTextureUnitObj;
+
+////////////////////////////////////////////////////////////////////////////////
+class GrTextureObj : public GrFBBindableObj {
+    GR_DEFINE_CREATOR(GrTextureObj);
+
+public:
+    GrTextureObj()
+        : GrFBBindableObj() {
+    }
+
+    virtual ~GrTextureObj() {
+        GrAlwaysAssert(0 == fTextureUnitReferees.count());
+    }
+
+    void setBound(GrTextureUnitObj *referee) {
+        fTextureUnitReferees.append(1, &referee);
+    }
+
+    void resetBound(GrTextureUnitObj *referee) {
+        int index = fTextureUnitReferees.find(referee);
+        GrAlwaysAssert(0 <= index);
+        fTextureUnitReferees.removeShuffle(index);
+    }
+    bool getBound(GrTextureUnitObj *referee) const {
+        int index = fTextureUnitReferees.find(referee);
+        return 0 <= index;
+    }
+    bool getBound() const {
+        return 0 != fTextureUnitReferees.count();
+    }
+
+    virtual void deleteAction() SK_OVERRIDE;
+
+protected:
+
+private:
+    // texture units that bind this texture (via "glBindTexture")
+    SkTDArray<GrTextureUnitObj *> fTextureUnitReferees;
+
+    typedef GrFBBindableObj INHERITED;
+};
+
+#endif // GrTextureObj_DEFINED
diff --git a/src/gpu/gl/debug/GrTextureUnitObj.cpp b/src/gpu/gl/debug/GrTextureUnitObj.cpp
new file mode 100644
index 0000000..316dcec
--- /dev/null
+++ b/src/gpu/gl/debug/GrTextureUnitObj.cpp
@@ -0,0 +1,31 @@
+
+/*
+ * 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 "GrTextureUnitObj.h"
+#include "GrTextureObj.h"
+
+void GrTextureUnitObj::setTexture(GrTextureObj *texture)  {
+
+    if (fTexture) {
+        GrAlwaysAssert(fTexture->getBound(this));
+        fTexture->resetBound(this);
+
+        GrAlwaysAssert(!fTexture->getDeleted());
+        fTexture->unref();
+    }
+
+    fTexture = texture;
+
+    if (fTexture) {
+        GrAlwaysAssert(!fTexture->getDeleted());
+        fTexture->ref();
+
+        GrAlwaysAssert(!fTexture->getBound(this));
+        fTexture->setBound(this);
+    }
+}
diff --git a/src/gpu/gl/debug/GrTextureUnitObj.h b/src/gpu/gl/debug/GrTextureUnitObj.h
new file mode 100644
index 0000000..a1dab11
--- /dev/null
+++ b/src/gpu/gl/debug/GrTextureUnitObj.h
@@ -0,0 +1,45 @@
+
+/*
+ * 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 GrTextreUnitObj_DEFINED
+#define GrTextureUnitObj_DEFINED
+
+#include "GrFakeRefObj.h"
+class GrTextureObj;
+
+////////////////////////////////////////////////////////////////////////////////
+// Although texture unit objects are allocated & deallocated like the other
+// GL emulation objects they are derived from GrFakeRefObj to provide some
+// uniformity in how the GrDebugGL class manages resources
+class GrTextureUnitObj : public GrFakeRefObj {
+    GR_DEFINE_CREATOR(GrTextureUnitObj);
+
+public:
+    GrTextureUnitObj()
+        : GrFakeRefObj()
+        , fNumber(0)
+        , fTexture(NULL) {
+    }
+
+    void setNumber(GrGLenum number) {
+        fNumber = number;
+    }
+    GrGLenum getNumber() const { return fNumber; }
+
+    void setTexture(GrTextureObj *texture);
+    GrTextureObj *getTexture()                  { return fTexture; }
+
+protected:
+private:
+    GrGLenum fNumber;
+    GrTextureObj *fTexture;
+
+    typedef GrFakeRefObj INHERITED;
+};
+
+#endif // GrTextureUnitObj_DEFINED
diff --git a/src/gpu/gl/debug/SkDebugGLContext.cpp b/src/gpu/gl/debug/SkDebugGLContext.cpp
new file mode 100644
index 0000000..ae42227
--- /dev/null
+++ b/src/gpu/gl/debug/SkDebugGLContext.cpp
@@ -0,0 +1,13 @@
+
+/*
+ * 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 "gl/SkDebugGLContext.h"
+
+const GrGLInterface* SkDebugGLContext::createGLContext() {
+    return GrGLCreateDebugInterface();
+};
diff --git a/src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp b/src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp
new file mode 100644
index 0000000..2b4dc64
--- /dev/null
+++ b/src/gpu/gl/iOS/GrGLCreateNativeInterface_iOS.cpp
@@ -0,0 +1,142 @@
+
+/*
+ * 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 "gl/GrGLInterface.h"
+
+#import <OpenGLES/ES2/gl.h>
+#import <OpenGLES/ES2/glext.h>
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+    static SkAutoTUnref<GrGLInterface> glInterface;
+    if (!glInterface.get()) {
+        GrGLInterface* interface = SkNEW(GrGLInterface);
+        glInterface.reset(interface);
+
+        interface->fActiveTexture = glActiveTexture;
+        interface->fAttachShader = glAttachShader;
+        interface->fBindAttribLocation = glBindAttribLocation;
+        interface->fBindBuffer = glBindBuffer;
+        interface->fBindTexture = glBindTexture;
+        interface->fBlendColor = glBlendColor;
+        interface->fBlendFunc = glBlendFunc;
+        interface->fBufferData = (GrGLBufferDataProc)glBufferData;
+        interface->fBufferSubData = (GrGLBufferSubDataProc)glBufferSubData;
+        interface->fClear = glClear;
+        interface->fClearColor = glClearColor;
+        interface->fClearStencil = glClearStencil;
+        interface->fColorMask = glColorMask;
+        interface->fCompileShader = glCompileShader;
+        interface->fCompressedTexImage2D = glCompressedTexImage2D;
+        interface->fCreateProgram = glCreateProgram;
+        interface->fCreateShader = glCreateShader;
+        interface->fCullFace = glCullFace;
+        interface->fDeleteBuffers = glDeleteBuffers;
+        interface->fDeleteProgram = glDeleteProgram;
+        interface->fDeleteShader = glDeleteShader;
+        interface->fDeleteTextures = glDeleteTextures;
+        interface->fDepthMask = glDepthMask;
+        interface->fDisable = glDisable;
+        interface->fDisableVertexAttribArray = glDisableVertexAttribArray;
+        interface->fDrawArrays = glDrawArrays;
+        interface->fDrawBuffer = NULL;
+        interface->fDrawBuffers = NULL;
+        interface->fDrawElements = glDrawElements;
+        interface->fEnable = glEnable;
+        interface->fEnableVertexAttribArray = glEnableVertexAttribArray;
+        interface->fFinish = glFinish;
+        interface->fFlush = glFlush;
+        interface->fFrontFace = glFrontFace;
+        interface->fGenBuffers = glGenBuffers;
+        interface->fGetBufferParameteriv = glGetBufferParameteriv;
+        interface->fGetError = glGetError;
+        interface->fGetIntegerv = glGetIntegerv;
+        interface->fGetProgramInfoLog = glGetProgramInfoLog;
+        interface->fGetProgramiv = glGetProgramiv;
+        interface->fGetShaderInfoLog = glGetShaderInfoLog;
+        interface->fGetShaderiv = glGetShaderiv;
+        interface->fGetString = glGetString;
+        interface->fGenTextures = glGenTextures;
+        interface->fGetUniformLocation = glGetUniformLocation;
+        interface->fLineWidth = glLineWidth;
+        interface->fLinkProgram = glLinkProgram;
+        interface->fPixelStorei = glPixelStorei;
+        interface->fReadBuffer = NULL;
+        interface->fReadPixels = glReadPixels;
+        interface->fScissor = glScissor;
+        interface->fShaderSource = glShaderSource;
+        interface->fStencilFunc = glStencilFunc;
+        interface->fStencilFuncSeparate = glStencilFuncSeparate;
+        interface->fStencilMask = glStencilMask;
+        interface->fStencilMaskSeparate = glStencilMaskSeparate;
+        interface->fStencilOp = glStencilOp;
+        interface->fStencilOpSeparate = glStencilOpSeparate;
+        // mac uses GLenum for internalFormat param (non-standard)
+        // amounts to int vs. uint.
+        interface->fTexImage2D = (GrGLTexImage2DProc)glTexImage2D;
+    #if GL_ARB_texture_storage
+        interface->fTexStorage2D = glTexStorage2D;
+    #elif GL_EXT_texture_storage
+        interface->fTexStorage2D = glTexStorage2DEXT;
+    #endif
+        interface->fTexParameteri = glTexParameteri;
+        interface->fTexParameteriv = glTexParameteriv;
+        interface->fTexSubImage2D = glTexSubImage2D;
+        interface->fUniform1f = glUniform1f;
+        interface->fUniform1i = glUniform1i;
+        interface->fUniform1fv = glUniform1fv;
+        interface->fUniform1iv = glUniform1iv;
+        interface->fUniform2f = glUniform2f;
+        interface->fUniform2i = glUniform2i;
+        interface->fUniform2fv = glUniform2fv;
+        interface->fUniform2iv = glUniform2iv;
+        interface->fUniform3f = glUniform3f;
+        interface->fUniform3i = glUniform3i;
+        interface->fUniform3fv = glUniform3fv;
+        interface->fUniform3iv = glUniform3iv;
+        interface->fUniform4f = glUniform4f;
+        interface->fUniform4i = glUniform4i;
+        interface->fUniform4fv = glUniform4fv;
+        interface->fUniform4iv = glUniform4iv;
+        interface->fUniform4fv = glUniform4fv;
+        interface->fUniformMatrix2fv = glUniformMatrix2fv;
+        interface->fUniformMatrix3fv = glUniformMatrix3fv;
+        interface->fUniformMatrix4fv = glUniformMatrix4fv;
+        interface->fUseProgram = glUseProgram;
+        interface->fVertexAttrib4fv = glVertexAttrib4fv;
+        interface->fVertexAttribPointer = glVertexAttribPointer;
+        interface->fViewport = glViewport;
+        interface->fGenFramebuffers = glGenFramebuffers;
+        interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
+        interface->fGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
+        interface->fBindFramebuffer = glBindFramebuffer;
+        interface->fFramebufferTexture2D = glFramebufferTexture2D;
+        interface->fCheckFramebufferStatus = glCheckFramebufferStatus;
+        interface->fDeleteFramebuffers = glDeleteFramebuffers;
+        interface->fRenderbufferStorage = glRenderbufferStorage;
+        interface->fGenRenderbuffers = glGenRenderbuffers;
+        interface->fDeleteRenderbuffers = glDeleteRenderbuffers;
+        interface->fFramebufferRenderbuffer = glFramebufferRenderbuffer;
+        interface->fBindRenderbuffer = glBindRenderbuffer;
+
+    #if GL_OES_mapbuffer
+        interface->fMapBuffer = glMapBufferOES;
+        interface->fUnmapBuffer = glUnmapBufferOES;
+    #endif
+
+    #if GL_APPLE_framebuffer_multisample
+        interface->fRenderbufferStorageMultisample = glRenderbufferStorageMultisampleAPPLE;
+        interface->fResolveMultisampleFramebuffer = glResolveMultisampleFramebufferAPPLE;
+    #endif
+        interface->fBindFragDataLocationIndexed = NULL;
+
+        interface->fBindingsExported = kES2_GrGLBinding;
+    }
+    glInterface.get()->ref();
+    return glInterface.get();
+}
diff --git a/src/gpu/gl/iOS/SkNativeGLContext_iOS.mm b/src/gpu/gl/iOS/SkNativeGLContext_iOS.mm
new file mode 100644
index 0000000..46bd03a
--- /dev/null
+++ b/src/gpu/gl/iOS/SkNativeGLContext_iOS.mm
@@ -0,0 +1,62 @@
+
+/*
+ * 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 "gl/SkNativeGLContext.h"
+#import <OpenGLES/EAGL.h>
+
+#define EAGLCTX ((EAGLContext*)(fEAGLContext))
+
+SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
+    fEAGLContext = [EAGLContext currentContext];
+    if (EAGLCTX) {
+        [EAGLCTX retain];
+    }
+}
+
+SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
+    if (EAGLCTX) {
+        [EAGLContext setCurrentContext:EAGLCTX];
+        [EAGLCTX release];
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkNativeGLContext::SkNativeGLContext()
+    : fEAGLContext(NULL) {
+}
+
+SkNativeGLContext::~SkNativeGLContext() {
+    this->destroyGLContext();
+}
+
+void SkNativeGLContext::destroyGLContext() {
+    if ([EAGLContext currentContext] == EAGLCTX) {
+        [EAGLContext setCurrentContext:nil];
+    }
+    [EAGLCTX release];
+}
+
+const GrGLInterface* SkNativeGLContext::createGLContext() {
+    fEAGLContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
+    [EAGLContext setCurrentContext:EAGLCTX];
+    
+    const GrGLInterface* interface = GrGLCreateNativeInterface();
+    if (!interface) {
+        SkDebugf("Failed to create gl interface");
+        this->destroyGLContext();
+        return NULL;
+    }
+    return interface;
+}
+
+void SkNativeGLContext::makeCurrent() const {
+    if (![EAGLContext setCurrentContext:EAGLCTX]) {
+        SkDebugf("Could not set the context.\n");
+    }
+}
diff --git a/src/gpu/gl/mac/GrGLCreateNativeInterface_mac.cpp b/src/gpu/gl/mac/GrGLCreateNativeInterface_mac.cpp
new file mode 100644
index 0000000..fce96f4
--- /dev/null
+++ b/src/gpu/gl/mac/GrGLCreateNativeInterface_mac.cpp
@@ -0,0 +1,280 @@
+
+/*
+ * 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 "gl/GrGLInterface.h"
+#include "../GrGLUtil.h"
+
+#include <OpenGL/gl.h>
+#include <OpenGL/glext.h>
+
+#include <dlfcn.h>
+
+static void* GetProcAddress(const char* name) {
+    return dlsym(RTLD_DEFAULT, name);
+}
+
+#define GET_PROC(name) (interface->f ## name = ((GrGL ## name ## Proc) GetProcAddress("gl" #name)))
+#define GET_PROC_SUFFIX(name, suffix) (interface->f ## name = ((GrGL ## name ## Proc) GetProcAddress("gl" #name #suffix)))
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+    // The gl functions are not context-specific so we create one global
+    // interface
+    static SkAutoTUnref<GrGLInterface> glInterface;
+    if (!glInterface.get()) {
+        GrGLInterface* interface = new GrGLInterface;
+        glInterface.reset(interface);
+        const char* verStr = (const char*) glGetString(GL_VERSION);
+        GrGLVersion ver = GrGLGetVersionFromString(verStr);
+        const char* extStr = (const char*) glGetString(GL_EXTENSIONS);
+
+        interface->fBindingsExported = kDesktop_GrGLBinding;
+        interface->fActiveTexture = glActiveTexture;
+        interface->fAttachShader = glAttachShader;
+        interface->fBeginQuery = glBeginQuery;
+        interface->fBindAttribLocation = glBindAttribLocation;
+        interface->fBindBuffer = glBindBuffer;
+        if (ver >= GR_GL_VER(3,0)) {
+            #if GL_VERSION_3_0
+                interface->fBindFragDataLocation = glBindFragDataLocation;
+            #else
+                interface->fBindFragDataLocation = GET_PROC(BindFragDataLocation);
+            #endif
+        }
+        interface->fBindTexture = glBindTexture;
+        interface->fBlendFunc = glBlendFunc;
+
+        if (ver >= GR_GL_VER(1,4)) {
+            interface->fBlendColor = glBlendColor;
+        } else if (GrGLHasExtensionFromString("GL_ARB_imaging", extStr) ||
+                   GrGLHasExtensionFromString("GL_EXT_blend_color", extStr)) {
+            GET_PROC(BlendColor);
+        }
+
+        interface->fBufferData = glBufferData;
+        interface->fBufferSubData = glBufferSubData;
+        interface->fClear = glClear;
+        interface->fClearColor = glClearColor;
+        interface->fClearStencil = glClearStencil;
+        interface->fColorMask = glColorMask;
+        interface->fCompileShader = glCompileShader;
+        interface->fCompressedTexImage2D = glCompressedTexImage2D;
+        interface->fCreateProgram = glCreateProgram;
+        interface->fCreateShader = glCreateShader;
+        interface->fCullFace = glCullFace;
+        interface->fDeleteBuffers = glDeleteBuffers;
+        interface->fDeleteProgram = glDeleteProgram;
+        interface->fDeleteQueries = glDeleteQueries;
+        interface->fDeleteShader = glDeleteShader;
+        interface->fDeleteTextures = glDeleteTextures;
+        interface->fDepthMask = glDepthMask;
+        interface->fDisable = glDisable;
+        interface->fDisableVertexAttribArray =
+                                            glDisableVertexAttribArray;
+        interface->fDrawArrays = glDrawArrays;
+        interface->fDrawBuffer = glDrawBuffer;
+        interface->fDrawBuffers = glDrawBuffers;
+        interface->fDrawElements = glDrawElements;
+        interface->fEnable = glEnable;
+        interface->fEnableVertexAttribArray = glEnableVertexAttribArray;
+        interface->fEndQuery = glEndQuery;
+        interface->fFinish = glFinish;
+        interface->fFlush = glFlush;
+        interface->fFrontFace = glFrontFace;
+        interface->fGenBuffers = glGenBuffers;
+        interface->fGenQueries = glGenQueries;
+        interface->fGetBufferParameteriv = glGetBufferParameteriv;
+        interface->fGetError = glGetError;
+        interface->fGetIntegerv = glGetIntegerv;
+        interface->fGetProgramInfoLog = glGetProgramInfoLog;
+        interface->fGetProgramiv = glGetProgramiv;
+        interface->fGetQueryiv = glGetQueryiv;
+        interface->fGetQueryObjectiv = glGetQueryObjectiv;
+        interface->fGetQueryObjectuiv = glGetQueryObjectuiv;
+        interface->fGetShaderInfoLog = glGetShaderInfoLog;
+        interface->fGetShaderiv = glGetShaderiv;
+        interface->fGetString = glGetString;
+        interface->fGetTexLevelParameteriv = glGetTexLevelParameteriv;
+        interface->fGenTextures = glGenTextures;
+        interface->fGetUniformLocation = glGetUniformLocation;
+        interface->fLineWidth = glLineWidth;
+        interface->fLinkProgram = glLinkProgram;
+        interface->fMapBuffer = glMapBuffer;
+        interface->fPixelStorei = glPixelStorei;
+        interface->fReadBuffer = glReadBuffer;
+        interface->fReadPixels = glReadPixels;
+        interface->fScissor = glScissor;
+    // The new OpenGLES2 header has an extra "const" in it.  :(
+#if GR_USE_NEW_GL_SHADER_SOURCE_SIGNATURE
+        interface->fShaderSource = (GrGLShaderSourceProc) glShaderSource;
+#else
+        interface->fShaderSource = glShaderSource;
+#endif
+        interface->fStencilFunc = glStencilFunc;
+        interface->fStencilFuncSeparate = glStencilFuncSeparate;
+        interface->fStencilMask = glStencilMask;
+        interface->fStencilMaskSeparate = glStencilMaskSeparate;
+        interface->fStencilOp = glStencilOp;
+        interface->fStencilOpSeparate = glStencilOpSeparate;
+        // mac uses GLenum for internalFormat param (non-standard)
+        // amounts to int vs. uint.
+        interface->fTexImage2D = (GrGLTexImage2DProc)glTexImage2D;
+        interface->fTexParameteri = glTexParameteri;
+        interface->fTexParameteriv = glTexParameteriv;
+    #if GL_ARB_texture_storage || GL_VERSION_4_2
+        interface->fTexStorage2D = glTexStorage2D
+    #elif GL_EXT_texture_storage
+        interface->fTexStorage2D = glTexStorage2DEXT;
+    #else
+        if (ver >= GR_GL_VER(4,2) ||
+            GrGLHasExtensionFromString("GL_ARB_texture_storage", extStr)) {
+            GET_PROC(TexStorage2D);
+        } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", extStr)) {
+            GET_PROC_SUFFIX(TexStorage2D, EXT);
+        }
+    #endif
+        interface->fTexSubImage2D = glTexSubImage2D;
+        interface->fUniform1f = glUniform1f;
+        interface->fUniform1i = glUniform1i;
+        interface->fUniform1fv = glUniform1fv;
+        interface->fUniform1iv = glUniform1iv;
+        interface->fUniform2f = glUniform2f;
+        interface->fUniform2i = glUniform2i;
+        interface->fUniform2fv = glUniform2fv;
+        interface->fUniform2iv = glUniform2iv;
+        interface->fUniform3f = glUniform3f;
+        interface->fUniform3i = glUniform3i;
+        interface->fUniform3fv = glUniform3fv;
+        interface->fUniform3iv = glUniform3iv;
+        interface->fUniform4f = glUniform4f;
+        interface->fUniform4i = glUniform4i;
+        interface->fUniform4fv = glUniform4fv;
+        interface->fUniform4iv = glUniform4iv;
+        interface->fUniform4fv = glUniform4fv;
+        interface->fUniformMatrix2fv = glUniformMatrix2fv;
+        interface->fUniformMatrix3fv = glUniformMatrix3fv;
+        interface->fUniformMatrix4fv = glUniformMatrix4fv;
+        interface->fUnmapBuffer = glUnmapBuffer;
+        interface->fUseProgram = glUseProgram;
+        interface->fVertexAttrib4fv = glVertexAttrib4fv;
+        interface->fVertexAttribPointer = glVertexAttribPointer;
+        interface->fViewport = glViewport;
+
+        if (ver >= GR_GL_VER(3,3) || GrGLHasExtensionFromString("GL_ARB_timer_query", extStr)) {
+            // ARB extension doesn't use the ARB suffix on the function name
+            #if GL_ARB_timer_query || GL_VERSION_3_3
+                interface->fQueryCounter = glQueryCounter;
+                interface->fGetQueryObjecti64v = glGetQueryObjecti64v;
+                interface->fGetQueryObjectui64v = glGetQueryObjectui64v;
+            #else
+                interface->fQueryCounter = GET_PROC(QueryCounter);
+                interface->fGetQueryObjecti64v = GET_PROC(GetQueryObjecti64v);
+                interface->fGetQueryObjectui64v = GET_PROC(GetQueryObjectui64v);
+            #endif
+        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extStr)) {
+            #if GL_EXT_timer_query
+                interface->fGetQueryObjecti64v = glGetQueryObjecti64vEXT;
+                interface->fGetQueryObjectui64v = glGetQueryObjectui64vEXT;
+            #else
+                interface->fGetQueryObjecti64v = GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
+                interface->fGetQueryObjectui64v = GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
+            #endif
+        }
+
+        if (ver >= GR_GL_VER(3,0) || GrGLHasExtensionFromString("GL_ARB_framebuffer_object", extStr)) {
+            // ARB extension doesn't use the ARB suffix on the function names
+            #if GL_VERSION_3_0 || GL_ARB_framebuffer_object
+                interface->fGenFramebuffers = glGenFramebuffers;
+                interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
+                interface->fGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
+                interface->fBindFramebuffer = glBindFramebuffer;
+                interface->fFramebufferTexture2D = glFramebufferTexture2D;
+                interface->fCheckFramebufferStatus = glCheckFramebufferStatus;
+                interface->fDeleteFramebuffers = glDeleteFramebuffers;
+                interface->fRenderbufferStorage = glRenderbufferStorage;
+                interface->fGenRenderbuffers = glGenRenderbuffers;
+                interface->fDeleteRenderbuffers = glDeleteRenderbuffers;
+                interface->fFramebufferRenderbuffer = glFramebufferRenderbuffer;
+                interface->fBindRenderbuffer = glBindRenderbuffer;
+                interface->fRenderbufferStorageMultisample = glRenderbufferStorageMultisample;
+                interface->fBlitFramebuffer = glBlitFramebuffer;
+            #else
+                interface->fGenFramebuffers = GET_PROC(GenFramebuffers);
+                interface->fGetFramebufferAttachmentParameteriv = GET_PROC(GetFramebufferAttachmentParameteriv);
+                interface->fGetRenderbufferParameteriv = GET_PROC(GetRenderbufferParameteriv);
+                interface->fBindFramebuffer = GET_PROC(BindFramebuffer);
+                interface->fFramebufferTexture2D = GET_PROC(FramebufferTexture2D);
+                interface->fCheckFramebufferStatus = GET_PROC(CheckFramebufferStatus);
+                interface->fDeleteFramebuffers = GET_PROC(DeleteFramebuffers);
+                interface->fRenderbufferStorage = GET_PROC(RenderbufferStorage);
+                interface->fGenRenderbuffers = GET_PROC(GenRenderbuffers);
+                interface->fDeleteRenderbuffers = GET_PROC(DeleteRenderbuffers);
+                interface->fFramebufferRenderbuffer = GET_PROC(FramebufferRenderbuffer);
+                interface->fBindRenderbuffer = GET_PROC(BindRenderbuffer);
+                interface->fRenderbufferStorageMultisample = GET_PROC(RenderbufferStorageMultisample);
+                interface->fBlitFramebuffer = GET_PROC(BlitFramebuffer);
+            #endif
+        } else {
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object", extStr)) {
+                #if GL_EXT_framebuffer_object
+                    interface->fGenFramebuffers = glGenFramebuffersEXT;
+                    interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameterivEXT;
+                    interface->fGetRenderbufferParameteriv = glGetRenderbufferParameterivEXT;
+                    interface->fBindFramebuffer = glBindFramebufferEXT;
+                    interface->fFramebufferTexture2D = glFramebufferTexture2DEXT;
+                    interface->fCheckFramebufferStatus = glCheckFramebufferStatusEXT;
+                    interface->fDeleteFramebuffers = glDeleteFramebuffersEXT;
+                    interface->fRenderbufferStorage = glRenderbufferStorageEXT;
+                    interface->fGenRenderbuffers = glGenRenderbuffersEXT;
+                    interface->fDeleteRenderbuffers = glDeleteRenderbuffersEXT;
+                    interface->fFramebufferRenderbuffer = glFramebufferRenderbufferEXT;
+                    interface->fBindRenderbuffer = glBindRenderbufferEXT;
+                #else
+                    interface->fGenFramebuffers = GET_PROC_SUFFIX(GenFramebuffers, EXT);
+                    interface->fGetFramebufferAttachmentParameteriv = GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
+                    interface->fGetRenderbufferParameteriv = GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
+                    interface->fBindFramebuffer = GET_PROC_SUFFIX(BindFramebuffer, EXT);
+                    interface->fFramebufferTexture2D = GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
+                    interface->fCheckFramebufferStatus = GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
+                    interface->fDeleteFramebuffers = GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
+                    interface->fRenderbufferStorage = GET_PROC_SUFFIX(RenderbufferStorage, EXT);
+                    interface->fGenRenderbuffers = GET_PROC_SUFFIX(GenRenderbuffers, EXT);
+                    interface->fDeleteRenderbuffers = GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
+                    interface->fFramebufferRenderbuffer = GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
+                    interface->fBindRenderbuffer = GET_PROC_SUFFIX(BindRenderbuffer, EXT);
+                #endif
+            }
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample", extStr)) {
+                #if GL_EXT_framebuffer_multisample
+                    interface->fRenderbufferStorageMultisample = glRenderbufferStorageMultisampleEXT;
+                #else
+                    interface->fRenderbufferStorageMultisample = GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
+                #endif
+            }
+            if (GrGLHasExtensionFromString("", extStr)) {
+                #if GL_EXT_framebuffer_blit
+                    interface->fBlitFramebuffer = glBlitFramebufferEXT;
+                #else
+                    interface->fBlitFramebuffer = GET_PROC_SUFFIX(BlitFramebuffer, EXT);
+                #endif
+            }
+        }
+        if (ver >= GR_GL_VER(3,3) || GrGLHasExtensionFromString("GL_ARB_blend_func_extended", extStr)) {
+            // ARB extension doesn't use the ARB suffix on the function name
+            #if GL_VERSION_3_3 || GL_ARB_blend_func_extended
+                interface->fBindFragDataLocationIndexed = glBindFragDataLocationIndexed;
+            #else
+                interface->fBindFragDataLocationIndexed = GET_PROC(BindFragDataLocationIndexed);
+            #endif
+        }
+
+        interface->fBindingsExported = kDesktop_GrGLBinding;
+    }
+    glInterface.get()->ref();
+    return glInterface.get();
+}
diff --git a/src/gpu/gl/mac/SkNativeGLContext_mac.cpp b/src/gpu/gl/mac/SkNativeGLContext_mac.cpp
new file mode 100644
index 0000000..f0cb2b6
--- /dev/null
+++ b/src/gpu/gl/mac/SkNativeGLContext_mac.cpp
@@ -0,0 +1,74 @@
+
+/*
+ * 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 "gl/SkNativeGLContext.h"
+
+SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
+    fOldAGLContext = aglGetCurrentContext();
+}
+
+SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
+    aglSetCurrentContext(fOldAGLContext);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkNativeGLContext::SkNativeGLContext()
+    : fContext(NULL) {
+}
+
+SkNativeGLContext::~SkNativeGLContext() {
+    this->destroyGLContext();
+}
+
+void SkNativeGLContext::destroyGLContext() {
+    if (fContext) {
+        aglDestroyContext(fContext);
+    }
+}
+
+const GrGLInterface* SkNativeGLContext::createGLContext() {
+    GLint major, minor;
+    // AGLContext ctx;
+
+    aglGetVersion(&major, &minor);
+    //SkDebugf("---- agl version %d %d\n", major, minor);
+
+    const GLint pixelAttrs[] = {
+        AGL_RGBA,
+        AGL_ACCELERATED,
+        AGL_NONE
+    };
+    AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs);
+    if (NULL == format) {
+        SkDebugf("Format could not be found.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+    fContext = aglCreateContext(format, NULL);
+    if (NULL == fContext) {
+        SkDebugf("Context could not be created.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+    aglDestroyPixelFormat(format);
+
+    aglSetCurrentContext(fContext);
+
+    const GrGLInterface* interface = GrGLCreateNativeInterface();
+    if (NULL == interface) {
+        SkDebugf("Context could not create GL interface.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    return interface;
+}
+
+void SkNativeGLContext::makeCurrent() const {
+    aglSetCurrentContext(fContext);
+}
diff --git a/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp b/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp
new file mode 100644
index 0000000..1b4b7f6
--- /dev/null
+++ b/src/gpu/gl/mesa/GrGLCreateMesaInterface.cpp
@@ -0,0 +1,205 @@
+
+/*
+ * 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 "gl/GrGLInterface.h"
+#include "../GrGLUtil.h"
+
+#define GL_GLEXT_PROTOTYPES
+#include "osmesa_wrapper.h"
+
+#define GR_GL_GET_PROC(F) interface->f ## F = (GrGL ## F ## Proc) \
+        OSMesaGetProcAddress("gl" #F);
+#define GR_GL_GET_PROC_SUFFIX(F, S) interface->f ## F = (GrGL ## F ## Proc) \
+        OSMesaGetProcAddress("gl" #F #S);
+
+// We use OSMesaGetProcAddress for every gl function to avoid accidentally using
+// non-Mesa gl functions.
+
+const GrGLInterface* GrGLCreateMesaInterface() {
+    if (NULL != OSMesaGetCurrentContext()) {
+        GrGLGetStringProc getString = (GrGLGetStringProc) OSMesaGetProcAddress("glGetString");
+        const char* versionString = (const char*) getString(GL_VERSION);
+        const char* extString = (const char*) getString(GL_EXTENSIONS);
+        GrGLVersion glVer = GrGLGetVersionFromString(versionString);
+
+        if (glVer < GR_GL_VER(1,5)) {
+            // We must have array and element_array buffer objects.
+            return NULL;
+        }
+        GrGLInterface* interface = new GrGLInterface();
+
+        GR_GL_GET_PROC(ActiveTexture);
+        GR_GL_GET_PROC(BeginQuery);
+        GR_GL_GET_PROC(AttachShader);
+        GR_GL_GET_PROC(BindAttribLocation);
+        GR_GL_GET_PROC(BindBuffer);
+        GR_GL_GET_PROC(BindFragDataLocation);
+        GR_GL_GET_PROC(BindTexture);
+        GR_GL_GET_PROC(BlendFunc);
+
+        if (glVer >= GR_GL_VER(1,4) ||
+            GrGLHasExtensionFromString("GL_ARB_imaging", extString) ||
+            GrGLHasExtensionFromString("GL_EXT_blend_color", extString)) {
+            GR_GL_GET_PROC(BlendColor);
+        }
+
+        GR_GL_GET_PROC(BufferData);
+        GR_GL_GET_PROC(BufferSubData);
+        GR_GL_GET_PROC(Clear);
+        GR_GL_GET_PROC(ClearColor);
+        GR_GL_GET_PROC(ClearStencil);
+        GR_GL_GET_PROC(ColorMask);
+        GR_GL_GET_PROC(CompileShader);
+        GR_GL_GET_PROC(CompressedTexImage2D);
+        GR_GL_GET_PROC(CreateProgram);
+        GR_GL_GET_PROC(CreateShader);
+        GR_GL_GET_PROC(CullFace);
+        GR_GL_GET_PROC(DeleteBuffers);
+        GR_GL_GET_PROC(DeleteProgram);
+        GR_GL_GET_PROC(DeleteQueries);
+        GR_GL_GET_PROC(DeleteShader);
+        GR_GL_GET_PROC(DeleteTextures);
+        GR_GL_GET_PROC(DepthMask);
+        GR_GL_GET_PROC(Disable);
+        GR_GL_GET_PROC(DisableVertexAttribArray);
+        GR_GL_GET_PROC(DrawArrays);
+        GR_GL_GET_PROC(DrawBuffer);
+        GR_GL_GET_PROC(DrawBuffers);
+        GR_GL_GET_PROC(DrawElements);
+        GR_GL_GET_PROC(Enable);
+        GR_GL_GET_PROC(EnableVertexAttribArray);
+        GR_GL_GET_PROC(EndQuery);
+        GR_GL_GET_PROC(Finish);
+        GR_GL_GET_PROC(Flush);
+        GR_GL_GET_PROC(FrontFace);
+        GR_GL_GET_PROC(GenBuffers);
+        GR_GL_GET_PROC(GenQueries);
+        GR_GL_GET_PROC(GetBufferParameteriv);
+        GR_GL_GET_PROC(GetError);
+        GR_GL_GET_PROC(GetIntegerv);
+        GR_GL_GET_PROC(GetProgramInfoLog);
+        GR_GL_GET_PROC(GetProgramiv);
+        if (glVer >= GR_GL_VER(3,3) ||
+            GrGLHasExtensionFromString("GL_ARB_timer_query", extString)) {
+            GR_GL_GET_PROC(GetQueryObjecti64v);
+            GR_GL_GET_PROC(GetQueryObjectui64v)
+            GR_GL_GET_PROC(QueryCounter);
+        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extString)) {
+            GR_GL_GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
+        }
+        GR_GL_GET_PROC(GetQueryObjectiv);
+        GR_GL_GET_PROC(GetQueryObjectuiv);
+        GR_GL_GET_PROC(GetQueryiv);
+        GR_GL_GET_PROC(GetShaderInfoLog);
+        GR_GL_GET_PROC(GetShaderiv);
+        GR_GL_GET_PROC(GetString);
+        GR_GL_GET_PROC(GetTexLevelParameteriv);
+        GR_GL_GET_PROC(GenTextures);
+        GR_GL_GET_PROC(GetUniformLocation);
+        GR_GL_GET_PROC(LineWidth);
+        GR_GL_GET_PROC(LinkProgram);
+        GR_GL_GET_PROC(MapBuffer);
+        GR_GL_GET_PROC(PixelStorei);
+        GR_GL_GET_PROC(ReadBuffer);
+        GR_GL_GET_PROC(ReadPixels);
+        GR_GL_GET_PROC(Scissor);
+        GR_GL_GET_PROC(ShaderSource);
+        GR_GL_GET_PROC(StencilFunc);
+        GR_GL_GET_PROC(StencilFuncSeparate);
+        GR_GL_GET_PROC(StencilMask);
+        GR_GL_GET_PROC(StencilMaskSeparate);
+        GR_GL_GET_PROC(StencilOp);
+        GR_GL_GET_PROC(StencilOpSeparate);
+        GR_GL_GET_PROC(TexImage2D)
+        GR_GL_GET_PROC(TexParameteri);
+        GR_GL_GET_PROC(TexParameteriv);
+        GR_GL_GET_PROC(TexStorage2D);
+        if (NULL == interface->fTexStorage2D) {
+            GR_GL_GET_PROC_SUFFIX(TexStorage2D, EXT);
+        }
+        GR_GL_GET_PROC(TexSubImage2D);
+        GR_GL_GET_PROC(Uniform1f);
+        GR_GL_GET_PROC(Uniform1i);
+        GR_GL_GET_PROC(Uniform1fv);
+        GR_GL_GET_PROC(Uniform1iv);
+        GR_GL_GET_PROC(Uniform2f);
+        GR_GL_GET_PROC(Uniform2i);
+        GR_GL_GET_PROC(Uniform2fv);
+        GR_GL_GET_PROC(Uniform2iv);
+        GR_GL_GET_PROC(Uniform3f);
+        GR_GL_GET_PROC(Uniform3i);
+        GR_GL_GET_PROC(Uniform3fv);
+        GR_GL_GET_PROC(Uniform3iv);
+        GR_GL_GET_PROC(Uniform4f);
+        GR_GL_GET_PROC(Uniform4i);
+        GR_GL_GET_PROC(Uniform4fv);
+        GR_GL_GET_PROC(Uniform4iv);
+        GR_GL_GET_PROC(UniformMatrix2fv);
+        GR_GL_GET_PROC(UniformMatrix3fv);
+        GR_GL_GET_PROC(UniformMatrix4fv);
+        GR_GL_GET_PROC(UnmapBuffer);
+        GR_GL_GET_PROC(UseProgram);
+        GR_GL_GET_PROC(VertexAttrib4fv);
+        GR_GL_GET_PROC(VertexAttribPointer);
+        GR_GL_GET_PROC(Viewport);
+
+        // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
+        // GL_ARB_framebuffer_object doesn't use ARB suffix.)
+        if (glVer >= GR_GL_VER(3,0) ||
+            GrGLHasExtensionFromString("GL_ARB_framebuffer_object",
+                                        extString)) {
+            GR_GL_GET_PROC(GenFramebuffers);
+            GR_GL_GET_PROC(GetFramebufferAttachmentParameteriv);
+            GR_GL_GET_PROC(GetRenderbufferParameteriv);
+            GR_GL_GET_PROC(BindFramebuffer);
+            GR_GL_GET_PROC(FramebufferTexture2D);
+            GR_GL_GET_PROC(CheckFramebufferStatus);
+            GR_GL_GET_PROC(DeleteFramebuffers);
+            GR_GL_GET_PROC(RenderbufferStorage);
+            GR_GL_GET_PROC(GenRenderbuffers);
+            GR_GL_GET_PROC(DeleteRenderbuffers);
+            GR_GL_GET_PROC(FramebufferRenderbuffer);
+            GR_GL_GET_PROC(BindRenderbuffer);
+            GR_GL_GET_PROC(RenderbufferStorageMultisample);
+            GR_GL_GET_PROC(BlitFramebuffer);
+        } else if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object",
+                                              extString)) {
+            GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
+            GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT);
+            GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
+            GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
+            GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT);
+            GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
+            GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT);
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample",
+                                           extString)) {
+                GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
+            }
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit",
+                                           extString)) {
+                GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
+            }
+        } else {
+            // we must have FBOs
+            delete interface;
+            return NULL;
+        }
+        GR_GL_GET_PROC(BindFragDataLocationIndexed);
+        interface->fBindingsExported = kDesktop_GrGLBinding;
+        return interface;
+    } else {
+        return NULL;
+    }
+}
diff --git a/src/gpu/gl/mesa/SkMesaGLContext.cpp b/src/gpu/gl/mesa/SkMesaGLContext.cpp
new file mode 100644
index 0000000..6c6e422
--- /dev/null
+++ b/src/gpu/gl/mesa/SkMesaGLContext.cpp
@@ -0,0 +1,104 @@
+
+/*
+ * 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 <GL/osmesa.h>
+
+#include "gl/SkMesaGLContext.h"
+#include "gl/GrGLDefines.h"
+
+SkMesaGLContext::AutoContextRestore::AutoContextRestore() {
+    fOldContext = (Context)OSMesaGetCurrentContext();
+    if (NULL != (OSMesaContext)fOldContext) {
+        OSMesaGetColorBuffer((OSMesaContext)fOldContext,
+                              &fOldWidth, &fOldHeight,
+                              &fOldFormat, &fOldImage);
+    }
+}
+
+SkMesaGLContext::AutoContextRestore::~AutoContextRestore() {
+    if (NULL != (OSMesaContext)fOldContext) {
+        OSMesaMakeCurrent((OSMesaContext)fOldContext, fOldImage,
+                          fOldFormat, fOldWidth, fOldHeight);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkMesaGLContext::SkMesaGLContext()
+    : fContext(static_cast<Context>(NULL))
+    , fImage(NULL) {
+    GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext));
+}
+
+SkMesaGLContext::~SkMesaGLContext() {
+    this->destroyGLContext();
+}
+
+void SkMesaGLContext::destroyGLContext() {
+    if (fImage) {
+        sk_free(fImage);
+    }
+
+    if (fContext) {
+        OSMesaDestroyContext((OSMesaContext)fContext);
+    }
+}
+
+static const GrGLint gBOGUS_SIZE = 16;
+
+const GrGLInterface* SkMesaGLContext::createGLContext() {
+    /* Create an RGBA-mode context */
+#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
+    /* specify Z, stencil, accum sizes */
+    fContext = (Context)OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, NULL);
+#else
+    fContext = (Context)OSMesaCreateContext(OSMESA_BGRA, NULL);
+#endif
+    if (!fContext) {
+        SkDebugf("OSMesaCreateContext failed!\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+    // Allocate the image buffer
+    fImage = (GrGLubyte *) sk_malloc_throw(gBOGUS_SIZE * gBOGUS_SIZE *
+                                           4 * sizeof(GrGLubyte));
+    if (!fImage) {
+        SkDebugf("Alloc image buffer failed!\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    // Bind the buffer to the context and make it current
+    if (!OSMesaMakeCurrent((OSMesaContext)fContext,
+                           fImage,
+                           GR_GL_UNSIGNED_BYTE,
+                           gBOGUS_SIZE,
+                           gBOGUS_SIZE)) {
+        SkDebugf("OSMesaMakeCurrent failed!\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    const GrGLInterface* interface = GrGLCreateMesaInterface();
+    if (!interface) {
+        SkDebugf("Could not create GL interface!\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+    return interface;
+
+}
+
+void SkMesaGLContext::makeCurrent() const {
+    if (fContext) {
+        if (!OSMesaMakeCurrent((OSMesaContext)fContext, fImage,
+                               GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) {
+            SkDebugf("Could not make MESA context current.");
+        }
+    }
+}
diff --git a/src/gpu/gl/mesa/osmesa_wrapper.h b/src/gpu/gl/mesa/osmesa_wrapper.h
new file mode 100644
index 0000000..70de993
--- /dev/null
+++ b/src/gpu/gl/mesa/osmesa_wrapper.h
@@ -0,0 +1,16 @@
+
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// Older versions of XQuartz have a bug where a header included by osmesa.h
+// defines GL_GLEXT_PROTOTYPES. This will cause a redefinition warning if
+// the file that includes osmesa.h already defined it. XCode 3 uses a version
+// of gcc (4.2.1) that does not support the diagnostic pragma to disable a
+// warning (added in 4.2.4). So we use the system_header pragma to shut GCC
+// up about warnings in osmesa.h
+#pragma GCC system_header
+#include <GL/osmesa.h>
diff --git a/src/gpu/gl/nacl/SkNativeGLContext_nacl.cpp b/src/gpu/gl/nacl/SkNativeGLContext_nacl.cpp
new file mode 100644
index 0000000..59ed2bf
--- /dev/null
+++ b/src/gpu/gl/nacl/SkNativeGLContext_nacl.cpp
@@ -0,0 +1,34 @@
+
+/*
+ * 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 "gl/SkNativeGLContext.h"
+
+SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
+}
+
+SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
+}
+
+SkNativeGLContext::SkNativeGLContext()
+    : fContext(NULL)
+    , fDisplay(NULL)
+{
+}
+
+SkNativeGLContext::~SkNativeGLContext() {
+    this->destroyGLContext();
+}
+
+void SkNativeGLContext::destroyGLContext() {
+}
+
+const GrGLInterface* SkNativeGLContext::createGLContext() {
+    return NULL;
+}
+
+void SkNativeGLContext::makeCurrent() const {
+}
diff --git a/src/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp b/src/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp
new file mode 100644
index 0000000..04d35d1
--- /dev/null
+++ b/src/gpu/gl/unix/GrGLCreateNativeInterface_unix.cpp
@@ -0,0 +1,211 @@
+
+/*
+ * 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 "gl/GrGLInterface.h"
+#include "../GrGLUtil.h"
+
+#include <GL/glx.h>
+#include <GL/gl.h>
+#include <GL/glext.h>
+#include <GL/glu.h>
+
+#define GR_GL_GET_PROC(F) interface->f ## F = (GrGL ## F ## Proc) \
+        glXGetProcAddress(reinterpret_cast<const GLubyte*>("gl" #F));
+#define GR_GL_GET_PROC_SUFFIX(F, S) interface->f ## F = (GrGL ## F ## Proc) \
+        glXGetProcAddress(reinterpret_cast<const GLubyte*>("gl" #F #S));
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+    if (NULL != glXGetCurrentContext()) {
+        const char* versionString = (const char*) glGetString(GL_VERSION);
+        const char* extString = (const char*) glGetString(GL_EXTENSIONS);
+        GrGLVersion glVer = GrGLGetVersionFromString(versionString);
+
+        if (glVer < GR_GL_VER(1,5)) {
+            // We must have array and element_array buffer objects.
+            return NULL;
+        }
+
+        GrGLInterface* interface = new GrGLInterface();
+
+        interface->fActiveTexture = glActiveTexture;
+        GR_GL_GET_PROC(AttachShader);
+        GR_GL_GET_PROC(BindAttribLocation);
+        GR_GL_GET_PROC(BindBuffer);
+        GR_GL_GET_PROC(BindFragDataLocation);
+        GR_GL_GET_PROC(BeginQuery);
+        interface->fBindTexture = glBindTexture;
+        interface->fBlendFunc = glBlendFunc;
+
+        if (glVer >= GR_GL_VER(1,4) ||
+            GrGLHasExtensionFromString("GL_ARB_imaging", extString) ||
+            GrGLHasExtensionFromString("GL_EXT_blend_color", extString)) {
+            GR_GL_GET_PROC(BlendColor);
+        }
+
+        GR_GL_GET_PROC(BufferData);
+        GR_GL_GET_PROC(BufferSubData);
+        interface->fClear = glClear;
+        interface->fClearColor = glClearColor;
+        interface->fClearStencil = glClearStencil;
+        interface->fColorMask = glColorMask;
+        GR_GL_GET_PROC(CompileShader);
+        interface->fCompressedTexImage2D = glCompressedTexImage2D;
+        GR_GL_GET_PROC(CreateProgram);
+        GR_GL_GET_PROC(CreateShader);
+        interface->fCullFace = glCullFace;
+        GR_GL_GET_PROC(DeleteBuffers);
+        GR_GL_GET_PROC(DeleteProgram);
+        GR_GL_GET_PROC(DeleteQueries);
+        GR_GL_GET_PROC(DeleteShader);
+        interface->fDeleteTextures = glDeleteTextures;
+        interface->fDepthMask = glDepthMask;
+        interface->fDisable = glDisable;
+        GR_GL_GET_PROC(DisableVertexAttribArray);
+        interface->fDrawArrays = glDrawArrays;
+        interface->fDrawBuffer = glDrawBuffer;
+        GR_GL_GET_PROC(DrawBuffers);
+        interface->fDrawElements = glDrawElements;
+        interface->fEnable = glEnable;
+        GR_GL_GET_PROC(EnableVertexAttribArray);
+        GR_GL_GET_PROC(EndQuery);
+        interface->fFinish = glFinish;
+        interface->fFlush = glFlush;
+        interface->fFrontFace = glFrontFace;
+        GR_GL_GET_PROC(GenBuffers);
+        GR_GL_GET_PROC(GetBufferParameteriv);
+        interface->fGetError = glGetError;
+        interface->fGetIntegerv = glGetIntegerv;
+        GR_GL_GET_PROC(GetQueryObjectiv);
+        GR_GL_GET_PROC(GetQueryObjectuiv);
+        if (glVer >= GR_GL_VER(3,3) ||
+            GrGLHasExtensionFromString("GL_ARB_timer_query", extString)) {
+            GR_GL_GET_PROC(GetQueryObjecti64v);
+            GR_GL_GET_PROC(GetQueryObjectui64v);
+            GR_GL_GET_PROC(QueryCounter);
+        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extString)) {
+            GR_GL_GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
+        }
+        GR_GL_GET_PROC(GetQueryiv);
+        GR_GL_GET_PROC(GetProgramInfoLog);
+        GR_GL_GET_PROC(GetProgramiv);
+        GR_GL_GET_PROC(GetShaderInfoLog);
+        GR_GL_GET_PROC(GetShaderiv);
+        interface->fGetString = glGetString;
+        interface->fGetTexLevelParameteriv = glGetTexLevelParameteriv;
+        GR_GL_GET_PROC(GenQueries);
+        interface->fGenTextures = glGenTextures;
+        GR_GL_GET_PROC(GetUniformLocation);
+        interface->fLineWidth = glLineWidth;
+        GR_GL_GET_PROC(LinkProgram);
+        GR_GL_GET_PROC(MapBuffer);
+        interface->fPixelStorei = glPixelStorei;
+        interface->fReadBuffer = glReadBuffer;
+        interface->fReadPixels = glReadPixels;
+        if (GrGLHasExtensionFromString("GL_NV_framebuffer_multisample_coverage",
+                                       extString)) {
+            GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisampleCoverage, NV);
+        }
+        interface->fScissor = glScissor;
+        GR_GL_GET_PROC(ShaderSource);
+        interface->fStencilFunc = glStencilFunc;
+        GR_GL_GET_PROC(StencilFuncSeparate);
+        interface->fStencilMask = glStencilMask;
+        GR_GL_GET_PROC(StencilMaskSeparate);
+        interface->fStencilOp = glStencilOp;
+        GR_GL_GET_PROC(StencilOpSeparate);
+        interface->fTexImage2D = glTexImage2D;
+        interface->fTexParameteri = glTexParameteri;
+        interface->fTexParameteriv = glTexParameteriv;
+        if (glVer >= GR_GL_VER(4,2) ||
+            GrGLHasExtensionFromString("GL_ARB_texture_storage", extString)) {
+            GR_GL_GET_PROC(TexStorage2D);
+        } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", extString)) {
+            GR_GL_GET_PROC_SUFFIX(TexStorage2D, EXT);
+        }
+        interface->fTexSubImage2D = glTexSubImage2D;
+        GR_GL_GET_PROC(Uniform1f);
+        GR_GL_GET_PROC(Uniform1i);
+        GR_GL_GET_PROC(Uniform1fv);
+        GR_GL_GET_PROC(Uniform1iv);
+        GR_GL_GET_PROC(Uniform2f);
+        GR_GL_GET_PROC(Uniform2i);
+        GR_GL_GET_PROC(Uniform2fv);
+        GR_GL_GET_PROC(Uniform2iv);
+        GR_GL_GET_PROC(Uniform3f);
+        GR_GL_GET_PROC(Uniform3i);
+        GR_GL_GET_PROC(Uniform3fv);
+        GR_GL_GET_PROC(Uniform3iv);
+        GR_GL_GET_PROC(Uniform4f);
+        GR_GL_GET_PROC(Uniform4i);
+        GR_GL_GET_PROC(Uniform4fv);
+        GR_GL_GET_PROC(Uniform4iv);
+        GR_GL_GET_PROC(UniformMatrix2fv);
+        GR_GL_GET_PROC(UniformMatrix3fv);
+        GR_GL_GET_PROC(UniformMatrix4fv);
+        GR_GL_GET_PROC(UnmapBuffer);
+        GR_GL_GET_PROC(UseProgram);
+        GR_GL_GET_PROC(VertexAttrib4fv);
+        GR_GL_GET_PROC(VertexAttribPointer);
+        interface->fViewport = glViewport;
+        GR_GL_GET_PROC(BindFragDataLocationIndexed);
+
+        // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
+        // GL_ARB_framebuffer_object doesn't use ARB suffix.)
+        if (glVer >= GR_GL_VER(3,0) ||
+            GrGLHasExtensionFromString("GL_ARB_framebuffer_object",
+                                       extString)) {
+            GR_GL_GET_PROC(GenFramebuffers);
+            GR_GL_GET_PROC(GetFramebufferAttachmentParameteriv);
+            GR_GL_GET_PROC(GetRenderbufferParameteriv);
+            GR_GL_GET_PROC(BindFramebuffer);
+            GR_GL_GET_PROC(FramebufferTexture2D);
+            GR_GL_GET_PROC(CheckFramebufferStatus);
+            GR_GL_GET_PROC(DeleteFramebuffers);
+            GR_GL_GET_PROC(RenderbufferStorage);
+            GR_GL_GET_PROC(GenRenderbuffers);
+            GR_GL_GET_PROC(DeleteRenderbuffers);
+            GR_GL_GET_PROC(FramebufferRenderbuffer);
+            GR_GL_GET_PROC(BindRenderbuffer);
+            GR_GL_GET_PROC(RenderbufferStorageMultisample);
+            GR_GL_GET_PROC(BlitFramebuffer);
+        } else if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object",
+                                              extString)) {
+            GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
+            GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT);
+            GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
+            GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
+            GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT);
+            GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
+            GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT);
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample",
+                                             extString)) {
+                GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
+            }
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit",
+                                             extString)) {
+                GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
+            }
+        } else {
+            // we must have FBOs
+            delete interface;
+            return NULL;
+        }
+        interface->fBindingsExported = kDesktop_GrGLBinding;
+
+        return interface;
+    } else {
+        return NULL;
+    }
+}
diff --git a/src/gpu/gl/unix/SkNativeGLContext_unix.cpp b/src/gpu/gl/unix/SkNativeGLContext_unix.cpp
new file mode 100644
index 0000000..e6fae8e
--- /dev/null
+++ b/src/gpu/gl/unix/SkNativeGLContext_unix.cpp
@@ -0,0 +1,287 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "gl/SkNativeGLContext.h"
+
+#include <GL/glu.h>
+
+#define GLX_1_3 1
+
+SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
+    fOldGLXContext = glXGetCurrentContext();
+    fOldDisplay = glXGetCurrentDisplay();
+    fOldDrawable = glXGetCurrentDrawable();
+}
+
+SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
+    if (NULL != fOldDisplay) {
+        glXMakeCurrent(fOldDisplay, fOldDrawable, fOldGLXContext);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static bool ctxErrorOccurred = false;
+static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
+    ctxErrorOccurred = true;
+    return 0;
+}
+
+SkNativeGLContext::SkNativeGLContext()
+    : fContext(NULL)
+    , fDisplay(NULL)
+    , fPixmap(0)
+    , fGlxPixmap(0) {
+}
+
+SkNativeGLContext::~SkNativeGLContext() {
+    this->destroyGLContext();
+}
+
+void SkNativeGLContext::destroyGLContext() {
+    if (fDisplay) {
+        glXMakeCurrent(fDisplay, 0, 0);
+
+        if (fContext) {
+            glXDestroyContext(fDisplay, fContext);
+            fContext = NULL;
+        }
+
+        if (fGlxPixmap) {
+            glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
+            fGlxPixmap = 0;
+        }
+
+        if (fPixmap) {
+            XFreePixmap(fDisplay, fPixmap);
+            fPixmap = 0;
+        }
+
+        XCloseDisplay(fDisplay);
+        fDisplay = NULL;
+    }
+}
+
+const GrGLInterface* SkNativeGLContext::createGLContext() {
+    fDisplay = XOpenDisplay(0);
+
+    if (!fDisplay) {
+        SkDebugf("Failed to open X display.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    // Get a matching FB config
+    static int visual_attribs[] = {
+        GLX_X_RENDERABLE    , True,
+        GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
+        None
+    };
+
+#ifdef GLX_1_3
+    //SkDebugf("Getting matching framebuffer configs.\n");
+    int fbcount;
+    GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
+                                          visual_attribs, &fbcount);
+    if (!fbc) {
+        SkDebugf("Failed to retrieve a framebuffer config.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+    //SkDebugf("Found %d matching FB configs.\n", fbcount);
+
+    // Pick the FB config/visual with the most samples per pixel
+    //SkDebugf("Getting XVisualInfos.\n");
+    int best_fbc = -1, best_num_samp = -1;
+
+    int i;
+    for (i = 0; i < fbcount; ++i) {
+        XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
+        if (vi) {
+            int samp_buf, samples;
+            glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+            glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
+
+            //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
+            //       " SAMPLES = %d\n",
+            //        i, (unsigned int)vi->visualid, samp_buf, samples);
+
+            if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
+                best_fbc = i, best_num_samp = samples;
+        }
+        XFree(vi);
+    }
+
+    GLXFBConfig bestFbc = fbc[best_fbc];
+
+    // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
+    XFree(fbc);
+
+    // Get a visual
+    XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
+    //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
+#else
+    int numVisuals;
+    XVisualInfo visTemplate, *visReturn;
+
+    visReturn = XGetVisualInfo(fDisplay, VisualNoMask, &visTemplate, &numVisuals);
+    if (NULL == visReturn)
+    {
+        SkDebugf("Failed to get visual information.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    int best = -1, best_num_samp = -1;
+
+    for (int i = 0; i < numVisuals; ++i)
+    {
+        int samp_buf, samples;
+
+        glXGetConfig(fDisplay, &visReturn[i], GLX_SAMPLE_BUFFERS, &samp_buf);
+        glXGetConfig(fDisplay, &visReturn[i], GLX_SAMPLES, &samples);
+
+        if (best < 0 || (samp_buf && samples > best_num_samp))
+            best = i, best_num_samp = samples;
+    }
+
+    XVisualInfo temp = visReturn[best];
+    XVisualInfo *vi = &temp;
+
+    XFree(visReturn);
+#endif
+
+    fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
+
+    if (!fPixmap) {
+        SkDebugf("Failed to create pixmap.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
+
+#ifdef GLX_1_3
+    // Done with the visual info data
+    XFree(vi);
+#endif
+
+    // Create the context
+
+    // Install an X error handler so the application won't exit if GL 3.0
+    // context allocation fails.
+    //
+    // Note this error handler is global.
+    // All display connections in all threads of a process use the same
+    // error handler, so be sure to guard against other threads issuing
+    // X commands while this code is running.
+    ctxErrorOccurred = false;
+    int (*oldHandler)(Display*, XErrorEvent*) =
+        XSetErrorHandler(&ctxErrorHandler);
+
+    // Get the default screen's GLX extension list
+    const char *glxExts = glXQueryExtensionsString(
+        fDisplay, DefaultScreen(fDisplay)
+    );
+    // Check for the GLX_ARB_create_context extension string and the function.
+    // If either is not present, use GLX 1.3 context creation method.
+    if (!gluCheckExtension(
+          reinterpret_cast<const GLubyte*>("GLX_ARB_create_context")
+          , reinterpret_cast<const GLubyte*>(glxExts)))
+    {
+        //SkDebugf("GLX_ARB_create_context not found."
+        //       " Using old-style GLX context.\n");
+#ifdef GLX_1_3
+        fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True);
+#else
+        fContext = glXCreateContext(fDisplay, vi, 0, True);
+#endif
+
+    }
+#ifdef GLX_1_3
+    else {
+        //SkDebugf("Creating context.\n");
+
+        PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB =
+            (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
+        int context_attribs[] = {
+            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
+            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
+            //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
+            None
+        };
+        fContext = glXCreateContextAttribsARB(
+            fDisplay, bestFbc, 0, True, context_attribs
+        );
+
+        // Sync to ensure any errors generated are processed.
+        XSync(fDisplay, False);
+        if (!ctxErrorOccurred && fContext) {
+           //SkDebugf( "Created GL 3.0 context.\n" );
+        } else {
+            // Couldn't create GL 3.0 context.
+            // Fall back to old-style 2.x context.
+            // When a context version below 3.0 is requested,
+            // implementations will return the newest context version compatible
+            // with OpenGL versions less than version 3.0.
+
+            // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
+            context_attribs[1] = 1;
+            // GLX_CONTEXT_MINOR_VERSION_ARB = 0
+            context_attribs[3] = 0;
+
+            ctxErrorOccurred = false;
+
+            //SkDebugf("Failed to create GL 3.0 context."
+            //       " Using old-style GLX context.\n");
+            fContext = glXCreateContextAttribsARB(
+                fDisplay, bestFbc, 0, True, context_attribs
+            );
+        }
+    }
+#endif
+
+    // Sync to ensure any errors generated are processed.
+    XSync(fDisplay, False);
+
+    // Restore the original error handler
+    XSetErrorHandler(oldHandler);
+
+    if (ctxErrorOccurred || !fContext) {
+        SkDebugf("Failed to create an OpenGL context.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    // Verify that context is a direct context
+    if (!glXIsDirect(fDisplay, fContext)) {
+        //SkDebugf("Indirect GLX rendering context obtained.\n");
+    } else {
+        //SkDebugf("Direct GLX rendering context obtained.\n");
+    }
+
+    //SkDebugf("Making context current.\n");
+    if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
+      SkDebugf("Could not set the context.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    const GrGLInterface* interface = GrGLCreateNativeInterface();
+    if (!interface) {
+        SkDebugf("Failed to create gl interface");
+        this->destroyGLContext();
+        return NULL;
+    }
+    return interface;
+}
+
+void SkNativeGLContext::makeCurrent() const {
+    if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
+        SkDebugf("Could not set the context.\n");
+    }
+}
diff --git a/src/gpu/gl/win/GrGLCreateNativeInterface_win.cpp b/src/gpu/gl/win/GrGLCreateNativeInterface_win.cpp
new file mode 100644
index 0000000..ef8eccc
--- /dev/null
+++ b/src/gpu/gl/win/GrGLCreateNativeInterface_win.cpp
@@ -0,0 +1,271 @@
+
+/*
+ * 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 "gl/GrGLInterface.h"
+#include "../GrGLUtil.h"
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <GL/GL.h>
+
+/*
+ * Windows makes the GL funcs all be __stdcall instead of __cdecl :(
+ * This implementation will only work if GR_GL_FUNCTION_TYPE is __stdcall.
+ * Otherwise, a springboard would be needed that hides the calling convention.
+ */
+
+#define GR_GL_GET_PROC(F) interface->f ## F = (GrGL ## F ## Proc) wglGetProcAddress("gl" #F);
+#define GR_GL_GET_PROC_SUFFIX(F, S) interface->f ## F = (GrGL ## F ## Proc) wglGetProcAddress("gl" #F #S);
+
+const GrGLInterface* GrGLCreateNativeInterface() {
+    // wglGetProcAddress requires a context.
+    // GL Function pointers retrieved in one context may not be valid in another
+    // context. For that reason we create a new GrGLInterface each time we're
+    // called.
+    if (NULL != wglGetCurrentContext()) {
+        const char* versionString = (const char*) glGetString(GL_VERSION);
+        const char* extString = (const char*) glGetString(GL_EXTENSIONS);
+        GrGLVersion glVer = GrGLGetVersionFromString(versionString);
+
+        if (glVer < GR_GL_VER(1,5)) {
+            // We must have array and element_array buffer objects.
+            return NULL;
+        }
+        GrGLInterface* interface = new GrGLInterface();
+
+        // Functions that are part of GL 1.1 will return NULL in
+        // wglGetProcAddress
+        interface->fBindTexture = glBindTexture;
+        interface->fBlendFunc = glBlendFunc;
+
+        if (glVer >= GR_GL_VER(1,4) ||
+            GrGLHasExtensionFromString("GL_ARB_imaging", extString) ||
+            GrGLHasExtensionFromString("GL_EXT_blend_color", extString)) {
+            GR_GL_GET_PROC(BlendColor);
+        }
+
+        interface->fClear = glClear;
+        interface->fClearColor = glClearColor;
+        interface->fClearStencil = glClearStencil;
+        interface->fColorMask = glColorMask;
+        interface->fCullFace = glCullFace;
+        interface->fDeleteTextures = glDeleteTextures;
+        interface->fDepthMask = glDepthMask;
+        interface->fDisable = glDisable;
+        interface->fDrawArrays = glDrawArrays;
+        interface->fDrawElements = glDrawElements;
+        interface->fDrawBuffer = glDrawBuffer;
+        interface->fEnable = glEnable;
+        interface->fFrontFace = glFrontFace;
+        interface->fFinish = glFinish;
+        interface->fFlush = glFlush;
+        interface->fGenTextures = glGenTextures;
+        interface->fGetError = glGetError;
+        interface->fGetIntegerv = glGetIntegerv;
+        interface->fGetString = glGetString;
+        interface->fGetTexLevelParameteriv = glGetTexLevelParameteriv;
+        interface->fLineWidth = glLineWidth;
+        interface->fLoadIdentity = glLoadIdentity;
+        interface->fLoadMatrixf = glLoadMatrixf;
+        interface->fMatrixMode = glMatrixMode;
+        interface->fPixelStorei = glPixelStorei;
+        interface->fReadBuffer = glReadBuffer;
+        interface->fReadPixels = glReadPixels;
+        interface->fScissor = glScissor;
+        interface->fStencilFunc = glStencilFunc;
+        interface->fStencilMask = glStencilMask;
+        interface->fStencilOp = glStencilOp;
+        interface->fTexImage2D = glTexImage2D;
+        interface->fTexParameteri = glTexParameteri;
+        interface->fTexParameteriv = glTexParameteriv;
+        if (glVer >= GR_GL_VER(4,2) ||
+            GrGLHasExtensionFromString("GL_ARB_texture_storage", extString)) {
+            GR_GL_GET_PROC(TexStorage2D);
+        } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", extString)) {
+            GR_GL_GET_PROC_SUFFIX(TexStorage2D, EXT);
+        }
+        interface->fTexSubImage2D = glTexSubImage2D;
+        interface->fViewport = glViewport;
+
+        GR_GL_GET_PROC(ActiveTexture);
+        GR_GL_GET_PROC(AttachShader);
+        GR_GL_GET_PROC(BeginQuery);
+        GR_GL_GET_PROC(BindAttribLocation);
+        GR_GL_GET_PROC(BindBuffer);
+        GR_GL_GET_PROC(BindFragDataLocation);
+        GR_GL_GET_PROC(BufferData);
+        GR_GL_GET_PROC(BufferSubData);
+        GR_GL_GET_PROC(CompileShader);
+        GR_GL_GET_PROC(CompressedTexImage2D);
+        GR_GL_GET_PROC(CreateProgram);
+        GR_GL_GET_PROC(CreateShader);
+        GR_GL_GET_PROC(DeleteBuffers);
+        GR_GL_GET_PROC(DeleteQueries);
+        GR_GL_GET_PROC(DeleteProgram);
+        GR_GL_GET_PROC(DeleteShader);
+        GR_GL_GET_PROC(DisableVertexAttribArray);
+        GR_GL_GET_PROC(DrawBuffers);
+        GR_GL_GET_PROC(EnableVertexAttribArray);
+        GR_GL_GET_PROC(EndQuery);
+        GR_GL_GET_PROC(GenBuffers);
+        GR_GL_GET_PROC(GenQueries);
+        GR_GL_GET_PROC(GetBufferParameteriv);
+        GR_GL_GET_PROC(GetQueryiv);
+        GR_GL_GET_PROC(GetQueryObjectiv);
+        GR_GL_GET_PROC(GetQueryObjectuiv);
+        if (glVer > GR_GL_VER(3,3) ||
+            GrGLHasExtensionFromString("GL_ARB_timer_query", extString)) {
+            GR_GL_GET_PROC(GetQueryObjecti64v);
+            GR_GL_GET_PROC(GetQueryObjectui64v);
+            GR_GL_GET_PROC(QueryCounter);
+        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extString)) {
+            GR_GL_GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
+        }
+        GR_GL_GET_PROC(GetProgramInfoLog);
+        GR_GL_GET_PROC(GetProgramiv);
+        GR_GL_GET_PROC(GetShaderInfoLog);
+        GR_GL_GET_PROC(GetShaderiv);
+        GR_GL_GET_PROC(GetUniformLocation);
+        GR_GL_GET_PROC(LinkProgram);
+        if (GrGLHasExtensionFromString("GL_NV_framebuffer_multisample_coverage", extString)) {
+            GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisampleCoverage, NV);
+        }
+        GR_GL_GET_PROC(ShaderSource);
+        GR_GL_GET_PROC(StencilFuncSeparate);
+        GR_GL_GET_PROC(StencilMaskSeparate);
+        GR_GL_GET_PROC(StencilOpSeparate);
+        GR_GL_GET_PROC(Uniform1f);
+        GR_GL_GET_PROC(Uniform1i);
+        GR_GL_GET_PROC(Uniform1fv);
+        GR_GL_GET_PROC(Uniform1iv);
+        GR_GL_GET_PROC(Uniform2f);
+        GR_GL_GET_PROC(Uniform2i);
+        GR_GL_GET_PROC(Uniform2fv);
+        GR_GL_GET_PROC(Uniform2iv);
+        GR_GL_GET_PROC(Uniform3f);
+        GR_GL_GET_PROC(Uniform3i);
+        GR_GL_GET_PROC(Uniform3fv);
+        GR_GL_GET_PROC(Uniform3iv);
+        GR_GL_GET_PROC(Uniform4f);
+        GR_GL_GET_PROC(Uniform4i);
+        GR_GL_GET_PROC(Uniform4fv);
+        GR_GL_GET_PROC(Uniform4iv);
+        GR_GL_GET_PROC(UniformMatrix2fv);
+        GR_GL_GET_PROC(UniformMatrix3fv);
+        GR_GL_GET_PROC(UniformMatrix4fv);
+        GR_GL_GET_PROC(UseProgram);
+        GR_GL_GET_PROC(VertexAttrib4fv);
+        GR_GL_GET_PROC(VertexAttribPointer);
+        GR_GL_GET_PROC(BindFragDataLocationIndexed);
+
+        // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
+        // GL_ARB_framebuffer_object doesn't use ARB suffix.)
+        if (glVer > GR_GL_VER(3,0) ||
+            GrGLHasExtensionFromString("GL_ARB_framebuffer_object", extString)) {
+            GR_GL_GET_PROC(GenFramebuffers);
+            GR_GL_GET_PROC(GetFramebufferAttachmentParameteriv);
+            GR_GL_GET_PROC(GetRenderbufferParameteriv);
+            GR_GL_GET_PROC(BindFramebuffer);
+            GR_GL_GET_PROC(FramebufferTexture2D);
+            GR_GL_GET_PROC(CheckFramebufferStatus);
+            GR_GL_GET_PROC(DeleteFramebuffers);
+            GR_GL_GET_PROC(RenderbufferStorage);
+            GR_GL_GET_PROC(GenRenderbuffers);
+            GR_GL_GET_PROC(DeleteRenderbuffers);
+            GR_GL_GET_PROC(FramebufferRenderbuffer);
+            GR_GL_GET_PROC(BindRenderbuffer);
+            GR_GL_GET_PROC(RenderbufferStorageMultisample);
+            GR_GL_GET_PROC(BlitFramebuffer);
+        } else if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object",
+                   extString)) {
+            GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
+            GR_GL_GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
+            GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT);
+            GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
+            GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
+            GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT);
+            GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
+            GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
+            GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT);
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample", extString)) {
+                GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
+            }
+            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit", extString)) {
+                GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
+            }
+        } else {
+            // we must have FBOs
+            delete interface;
+            return NULL;
+        }
+        GR_GL_GET_PROC(MapBuffer);
+        GR_GL_GET_PROC(UnmapBuffer);
+
+        if (GrGLHasExtensionFromString("GL_NV_path_rendering", extString)) {
+            GR_GL_GET_PROC_SUFFIX(PathCommands, NV);
+            GR_GL_GET_PROC_SUFFIX(PathCoords, NV);
+            GR_GL_GET_PROC_SUFFIX(PathSubCommands, NV);
+            GR_GL_GET_PROC_SUFFIX(PathSubCoords, NV);
+            GR_GL_GET_PROC_SUFFIX(PathString, NV);
+            GR_GL_GET_PROC_SUFFIX(PathGlyphs, NV);
+            GR_GL_GET_PROC_SUFFIX(PathGlyphRange, NV);
+            GR_GL_GET_PROC_SUFFIX(WeightPaths, NV);
+            GR_GL_GET_PROC_SUFFIX(CopyPath, NV);
+            GR_GL_GET_PROC_SUFFIX(InterpolatePaths, NV);
+            GR_GL_GET_PROC_SUFFIX(TransformPath, NV);
+            GR_GL_GET_PROC_SUFFIX(PathParameteriv, NV);
+            GR_GL_GET_PROC_SUFFIX(PathParameteri, NV);
+            GR_GL_GET_PROC_SUFFIX(PathParameterfv, NV);
+            GR_GL_GET_PROC_SUFFIX(PathParameterf, NV);
+            GR_GL_GET_PROC_SUFFIX(PathDashArray, NV);
+            GR_GL_GET_PROC_SUFFIX(GenPaths, NV);
+            GR_GL_GET_PROC_SUFFIX(DeletePaths, NV);
+            GR_GL_GET_PROC_SUFFIX(IsPath, NV);
+            GR_GL_GET_PROC_SUFFIX(PathStencilFunc, NV);
+            GR_GL_GET_PROC_SUFFIX(PathStencilDepthOffset, NV);
+            GR_GL_GET_PROC_SUFFIX(StencilFillPath, NV);
+            GR_GL_GET_PROC_SUFFIX(StencilStrokePath, NV);
+            GR_GL_GET_PROC_SUFFIX(StencilFillPathInstanced, NV);
+            GR_GL_GET_PROC_SUFFIX(StencilStrokePathInstanced, NV);
+            GR_GL_GET_PROC_SUFFIX(PathCoverDepthFunc, NV);
+            GR_GL_GET_PROC_SUFFIX(PathColorGen, NV);
+            GR_GL_GET_PROC_SUFFIX(PathTexGen, NV);
+            GR_GL_GET_PROC_SUFFIX(PathFogGen, NV);
+            GR_GL_GET_PROC_SUFFIX(CoverFillPath, NV);
+            GR_GL_GET_PROC_SUFFIX(CoverStrokePath, NV);
+            GR_GL_GET_PROC_SUFFIX(CoverFillPathInstanced, NV);
+            GR_GL_GET_PROC_SUFFIX(CoverStrokePathInstanced, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathParameteriv, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathParameterfv, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathCommands, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathCoords, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathDashArray, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathMetrics, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathMetricRange, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathSpacing, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathColorGeniv, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathColorGenfv, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathTexGeniv, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathTexGenfv, NV);
+            GR_GL_GET_PROC_SUFFIX(IsPointInFillPath, NV);
+            GR_GL_GET_PROC_SUFFIX(IsPointInStrokePath, NV);
+            GR_GL_GET_PROC_SUFFIX(GetPathLength, NV);
+            GR_GL_GET_PROC_SUFFIX(PointAlongPath, NV);
+        }
+
+        interface->fBindingsExported = kDesktop_GrGLBinding;
+
+        return interface;
+    } else {
+        return NULL;
+    }
+}
diff --git a/src/gpu/gl/win/SkNativeGLContext_win.cpp b/src/gpu/gl/win/SkNativeGLContext_win.cpp
new file mode 100644
index 0000000..0082920
--- /dev/null
+++ b/src/gpu/gl/win/SkNativeGLContext_win.cpp
@@ -0,0 +1,137 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "gl/SkNativeGLContext.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+
+SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
+    fOldHGLRC = wglGetCurrentContext();
+    fOldHDC = wglGetCurrentDC();
+}
+
+SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
+    wglMakeCurrent(fOldHDC, fOldHGLRC);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+ATOM SkNativeGLContext::gWC = 0;
+
+SkNativeGLContext::SkNativeGLContext()
+    : fWindow(NULL)
+    , fDeviceContext(NULL)
+    , fGlRenderContext(0) {
+}
+
+SkNativeGLContext::~SkNativeGLContext() {
+    this->destroyGLContext();
+}
+
+void SkNativeGLContext::destroyGLContext() {
+    if (fGlRenderContext) {
+        wglDeleteContext(fGlRenderContext);
+    }
+    if (fWindow && fDeviceContext) {
+        ReleaseDC(fWindow, fDeviceContext);
+    }
+    if (fWindow) {
+        DestroyWindow(fWindow);
+    }
+}
+
+const GrGLInterface* SkNativeGLContext::createGLContext() {
+    HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
+
+    if (!gWC) {
+        WNDCLASS wc;
+        wc.cbClsExtra = 0;
+        wc.cbWndExtra = 0;
+        wc.hbrBackground = NULL;
+        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+        wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+        wc.hInstance = hInstance;
+        wc.lpfnWndProc = (WNDPROC) DefWindowProc;
+        wc.lpszClassName = TEXT("Griffin");
+        wc.lpszMenuName = NULL;
+        wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+
+        gWC = RegisterClass(&wc);
+        if (!gWC) {
+            SkDebugf("Could not register window class.\n");
+            return NULL;
+        }
+    }
+
+    if (!(fWindow = CreateWindow(TEXT("Griffin"),
+                                 TEXT("The Invisible Man"),
+                                 WS_OVERLAPPEDWINDOW,
+                                 0, 0, 1, 1,
+                                 NULL, NULL,
+                                 hInstance, NULL))) {
+        SkDebugf("Could not create window.\n");
+        return NULL;
+    }
+
+    if (!(fDeviceContext = GetDC(fWindow))) {
+        SkDebugf("Could not get device context.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    PIXELFORMATDESCRIPTOR pfd;
+    ZeroMemory(&pfd, sizeof(pfd));
+    pfd.nSize = sizeof(pfd);
+    pfd.nVersion = 1;
+    pfd.dwFlags = PFD_SUPPORT_OPENGL;
+    pfd.iPixelType = PFD_TYPE_RGBA;
+    pfd.cColorBits = 32;
+    pfd.cDepthBits = 0;
+    pfd.cStencilBits = 0;
+    pfd.iLayerType = PFD_MAIN_PLANE;
+
+    int pixelFormat = 0;
+    if (!(pixelFormat = ChoosePixelFormat(fDeviceContext, &pfd))) {
+        SkDebugf("No matching pixel format descriptor.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    if (!SetPixelFormat(fDeviceContext, pixelFormat, &pfd)) {
+        SkDebugf("Could not set the pixel format %d.\n", pixelFormat);
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    if (!(fGlRenderContext = wglCreateContext(fDeviceContext))) {
+        SkDebugf("Could not create rendering context.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    if (!(wglMakeCurrent(fDeviceContext, fGlRenderContext))) {
+        SkDebugf("Could not set the context.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+    const GrGLInterface* interface = GrGLCreateNativeInterface();
+    if (NULL == interface) {
+        SkDebugf("Could not create GL interface.\n");
+        this->destroyGLContext();
+        return NULL;
+    }
+
+    return interface;
+}
+
+void SkNativeGLContext::makeCurrent() const {
+    if (!wglMakeCurrent(fDeviceContext, fGlRenderContext)) {
+        SkDebugf("Could not create rendering context.\n");
+    }
+}
diff --git a/src/gpu/gr_hello_world.cpp b/src/gpu/gr_hello_world.cpp
deleted file mode 100644
index b19f9b4..0000000
--- a/src/gpu/gr_hello_world.cpp
+++ /dev/null
@@ -1,37 +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 "gl/SkGLCanvas.h"
-#include "SkBitmap.h"
-#include "SkPaint.h"
-#include "gl/SkGpuGLShaders.h"
-
-extern "C" {
-    void gr_hello_world();
-}
-
-void gr_hello_world() {
-    static GrGpu* gGpu;
-    if (NULL == gGpu) {
-        gGpu = new SkGpuGLShaders;
-    }
-
-    SkGLCanvas canvas(gGpu);
-    SkBitmap bm;
-
-    bm.setConfig(SkBitmap::kARGB_8888_Config, WIDTH, HEIGHT);
-    canvas.setBitmapDevice(bm);
-
-    canvas.drawColor(SK_ColorWHITE);
-
-    SkPaint paint;
-    paint.setAntiAlias(true);
-    paint.setTextSize(30);
-    canvas.drawText("Hello Kno", 9, 40, 40, paint);
-}
-
-
diff --git a/src/gpu/gr_unittests.cpp b/src/gpu/gr_unittests.cpp
index 6e51e19..7f5c7e9 100644
--- a/src/gpu/gr_unittests.cpp
+++ b/src/gpu/gr_unittests.cpp
@@ -6,14 +6,13 @@
  * found in the LICENSE file.
  */
 
-
-
 #include "GrBinHashKey.h"
 #include "GrDrawTarget.h"
-#include "GrMatrix.h"
-#include "GrPath.h"
+#include "SkMatrix.h"
 #include "GrRedBlackTree.h"
-#include "GrTDArray.h"
+
+// FIXME: needs to be in a header
+void gr_run_unittests();
 
 // If we aren't inheriting these as #defines from elsewhere,
 // clang demands they be declared before we #include the template
@@ -26,37 +25,6 @@
 }
 #include "GrTBSearch.h"
 
-static void dump(const GrTDArray<int>& array) {
-#if 0
-    for (int i = 0; i < array.count(); i++) {
-        printf(" %d", array[i]);
-    }
-    printf("\n");
-#endif
-}
-
-static void test_tdarray() {
-    GrTDArray<int> array;
-
-    *array.append() = 0; dump(array);
-    *array.append() = 2; dump(array);
-    *array.append() = 4; dump(array);
-    *array.append() = 6; dump(array);
-    GrAssert(array.count() == 4);
-
-    *array.insert(0) = -1; dump(array);
-    *array.insert(2) = 1; dump(array);
-    *array.insert(4) = 3; dump(array);
-    *array.insert(7) = 7; dump(array);
-    GrAssert(array.count() == 8);
-    array.remove(3); dump(array);
-    array.remove(0); dump(array);
-    array.removeShuffle(4); dump(array);
-    array.removeShuffle(1); dump(array);
-    GrAssert(array.count() == 4);
-}
-
-
 static void test_bsearch() {
     const int array[] = {
         1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99
@@ -85,10 +53,10 @@
         kDataLenUsedForKey = 8
     };
 
-    GrBinHashKey<BogusEntry, kDataLenUsedForKey> keyA;
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA;
     keyA.setKeyData(testStringA);
     // test copy constructor and comparison
-    GrBinHashKey<BogusEntry, kDataLenUsedForKey> keyA2(keyA);
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyA2(keyA);
     GrAssert(keyA.compare(keyA2) == 0);
     GrAssert(keyA.getHash() == keyA2.getHash());
     // test re-init
@@ -96,136 +64,16 @@
     GrAssert(keyA.compare(keyA2) == 0);
     GrAssert(keyA.getHash() == keyA2.getHash());
     // test sorting
-    GrBinHashKey<BogusEntry, kDataLenUsedForKey> keyB;
+    GrTBinHashKey<BogusEntry, kDataLenUsedForKey> keyB;
     keyB.setKeyData(testStringB);
     GrAssert(keyA.compare(keyB) < 0);
-    GrAssert(keyA.getHash() != keyB.getHash());    
+    GrAssert(keyA.getHash() != keyB.getHash());
 }
 
-static void test_convex() {
-#if 0
-    GrPath testPath;
-    GrPath::Iter testIter;
-    
-    GrPath pt;
-    pt.moveTo(0, 0);
-    pt.close();
-    
-    testIter.reset(pt);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-    
-    GrPath line;
-    line.moveTo(GrIntToScalar(12), GrIntToScalar(20));
-    line.lineTo(GrIntToScalar(-12), GrIntToScalar(-20));
-    line.close();
-    
-    testIter.reset(line);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-    
-    GrPath triLeft;
-    triLeft.moveTo(0, 0);
-    triLeft.lineTo(1, 0);
-    triLeft.lineTo(1, 1);
-    triLeft.close();
-    
-    testIter.reset(triLeft);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-    
-    GrPath triRight;
-    triRight.moveTo(0, 0);
-    triRight.lineTo(-1, 0);
-    triRight.lineTo(1, 1);
-    triRight.close();
-    
-    testIter.reset(triRight);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-    
-    GrPath square;
-    square.moveTo(0, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 1);
-    square.lineTo(0, 1);
-    square.close();
-    
-    testIter.reset(square);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-    
-    GrPath redundantSquare;
-    square.moveTo(0, 0);
-    square.lineTo(0, 0);
-    square.lineTo(0, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 0);
-    square.lineTo(1, 1);
-    square.lineTo(1, 1);
-    square.lineTo(1, 1);
-    square.lineTo(0, 1);
-    square.lineTo(0, 1);
-    square.lineTo(0, 1);
-    square.close();
-    
-    testIter.reset(redundantSquare);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConvex_ConvexHint == testPath.getConvexHint());
-    
-    GrPath bowTie;
-    bowTie.moveTo(0, 0);
-    bowTie.lineTo(0, 0);
-    bowTie.lineTo(0, 0);
-    bowTie.lineTo(1, 1);
-    bowTie.lineTo(1, 1);
-    bowTie.lineTo(1, 1);
-    bowTie.lineTo(1, 0);
-    bowTie.lineTo(1, 0);
-    bowTie.lineTo(1, 0);
-    bowTie.lineTo(0, 1);
-    bowTie.lineTo(0, 1);
-    bowTie.lineTo(0, 1);
-    bowTie.close();
-    
-    testIter.reset(bowTie);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-    
-    GrPath spiral;
-    spiral.moveTo(0, 0);
-    spiral.lineTo(1, 0);
-    spiral.lineTo(1, 1);
-    spiral.lineTo(0, 1);
-    spiral.lineTo(0,.5);
-    spiral.lineTo(.5,.5);
-    spiral.lineTo(.5,.75);
-    spiral.close();
-    
-    testIter.reset(spiral);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-    
-    GrPath dent;
-    dent.moveTo(0, 0);
-    dent.lineTo(1, 1);
-    dent.lineTo(0, 1);
-    dent.lineTo(-.5,2);
-    dent.lineTo(-2, 1);
-    dent.close();
-    
-    testIter.reset(dent);
-    testPath.resetFromIter(&testIter);
-    GrAssert(kConcave_ConvexHint == testPath.getConvexHint());
-#endif
-}
 
 void gr_run_unittests() {
-    test_tdarray();
     test_bsearch();
     test_binHashKey();
-    test_convex();
     GrRedBlackTree<int>::UnitTest();
-    GrDrawTarget::VertexLayoutUnitTest();
+    GrDrawState::VertexLayoutUnitTest();
 }
diff --git a/src/gpu/ios/GrGLDefaultInterface_iOS.cpp b/src/gpu/ios/GrGLDefaultInterface_iOS.cpp
deleted file mode 100644
index 9fc953f..0000000
--- a/src/gpu/ios/GrGLDefaultInterface_iOS.cpp
+++ /dev/null
@@ -1,140 +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 "gl/GrGLInterface.h"
-
-#import <OpenGLES/ES2/gl.h>
-#import <OpenGLES/ES2/glext.h>
-
-const GrGLInterface* GrGLDefaultInterface() {
-    static SkAutoTUnref<GrGLInterface> glInterface;
-    if (!glInterface.get()) {
-        GrGLInteface* interface = new GrGLInterface;
-        glInterface.reset(interface);
-
-        interface->fActiveTexture = glActiveTexture;
-        interface->fAttachShader = glAttachShader;
-        interface->fBindAttribLocation = glBindAttribLocation;
-        interface->fBindBuffer = glBindBuffer;
-        interface->fBindTexture = glBindTexture;
-        interface->fBlendColor = glBlendColor;
-        interface->fBlendFunc = glBlendFunc;
-        interface->fBufferData = (GrGLBufferDataProc)glBufferData;
-        interface->fBufferSubData = (GrGLBufferSubDataProc)glBufferSubData;
-        interface->fClear = glClear;
-        interface->fClearColor = glClearColor;
-        interface->fClearStencil = glClearStencil;
-        interface->fColorMask = glColorMask;
-        interface->fColorPointer = glColorPointer;
-        interface->fCompileShader = glCompileShader;
-        interface->fCompressedTexImage2D = glCompressedTexImage2D;
-        interface->fCreateProgram = glCreateProgram;
-        interface->fCreateShader = glCreateShader;
-        interface->fCullFace = glCullFace;
-        interface->fDeleteBuffers = glDeleteBuffers;
-        interface->fDeleteProgram = glDeleteProgram;
-        interface->fDeleteShader = glDeleteShader;
-        interface->fDeleteTextures = glDeleteTextures;
-        interface->fDepthMask = glDepthMask;
-        interface->fDisable = glDisable;
-        interface->fDisableVertexAttribArray = glDisableVertexAttribArray;
-        interface->fDrawArrays = glDrawArrays;
-        interface->fDrawBuffer = NULL;
-        interface->fDrawBuffers = NULL;
-        interface->fDrawElements = glDrawElements;
-        interface->fEnable = glEnable;
-        interface->fEnableVertexAttribArray = glEnableVertexAttribArray;
-        interface->fFrontFace = glFrontFace;
-        interface->fGenBuffers = glGenBuffers;
-        interface->fGetBufferParameteriv = glGetBufferParameteriv;
-        interface->fGetError = glGetError;
-        interface->fGetIntegerv = glGetIntegerv;
-        interface->fGetProgramInfoLog = glGetProgramInfoLog;
-        interface->fGetProgramiv = glGetProgramiv;
-        interface->fGetShaderInfoLog = glGetShaderInfoLog;
-        interface->fGetShaderiv = glGetShaderiv;
-        interface->fGetString = glGetString;
-        interface->fGenTextures = glGenTextures;
-        interface->fGetUniformLocation = glGetUniformLocation;
-        interface->fLineWidth = glLineWidth;
-        interface->fLinkProgram = glLinkProgram;
-        interface->fPixelStorei = glPixelStorei;
-        interface->fReadBuffer = NULL;
-        interface->fReadPixels = glReadPixels;
-        interface->fScissor = glScissor;
-        interface->fShaderSource = glShaderSource;
-        interface->fStencilFunc = glStencilFunc;
-        interface->fStencilFuncSeparate = glStencilFuncSeparate;
-        interface->fStencilMask = glStencilMask;
-        interface->fStencilMaskSeparate = glStencilMaskSeparate;
-        interface->fStencilOp = glStencilOp;
-        interface->fStencilOpSeparate = glStencilOpSeparate;
-        // mac uses GLenum for internalFormat param (non-standard)
-        // amounts to int vs. uint.
-        interface->fTexImage2D = (GrGLTexImage2DProc)glTexImage2D;
-    #if GL_ARB_texture_storage
-        interface->fTexStorage2D = glTexStorage2D;
-    #elif GL_EXT_texture_storage
-        interface->fTexStorage2D = glTexStorage2DEXT;
-    #endif
-        interface->fTexParameteri = glTexParameteri;
-        interface->fTexSubImage2D = glTexSubImage2D;
-        interface->fUniform1f = glUniform1f;
-        interface->fUniform1i = glUniform1i;
-        interface->fUniform1fv = glUniform1fv;
-        interface->fUniform1iv = glUniform1iv;
-        interface->fUniform2f = glUniform2f;
-        interface->fUniform2i = glUniform2i;
-        interface->fUniform2fv = glUniform2fv;
-        interface->fUniform2iv = glUniform2iv;
-        interface->fUniform3f = glUniform3f;
-        interface->fUniform3i = glUniform3i;
-        interface->fUniform3fv = glUniform3fv;
-        interface->fUniform3iv = glUniform3iv;
-        interface->fUniform4f = glUniform4f;
-        interface->fUniform4i = glUniform4i;
-        interface->fUniform4fv = glUniform4fv;
-        interface->fUniform4iv = glUniform4iv;
-        interface->fUniform4fv = glUniform4fv;
-        interface->fUniformMatrix2fv = glUniformMatrix2fv;
-        interface->fUniformMatrix3fv = glUniformMatrix3fv;
-        interface->fUniformMatrix4fv = glUniformMatrix4fv;
-        interface->fUseProgram = glUseProgram;
-        interface->fVertexAttrib4fv = glVertexAttrib4fv;
-        interface->fVertexAttribPointer = glVertexAttribPointer;
-        interface->fViewport = glViewport;
-        interface->fGenFramebuffers = glGenFramebuffers;
-        interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
-        interface->fGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
-        interface->fBindFramebuffer = glBindFramebuffer;
-        interface->fFramebufferTexture2D = glFramebufferTexture2D;
-        interface->fCheckFramebufferStatus = glCheckFramebufferStatus;
-        interface->fDeleteFramebuffers = glDeleteFramebuffers;
-        interface->fRenderbufferStorage = glRenderbufferStorage;
-        interface->fGenRenderbuffers = glGenRenderbuffers;
-        interface->fDeleteRenderbuffers = glDeleteRenderbuffers;
-        interface->fFramebufferRenderbuffer = glFramebufferRenderbuffer;
-        interface->fBindRenderbuffer = glBindRenderbuffer;
-       
-    #if GL_OES_mapbuffer
-        interface->fMapBuffer = glMapBufferOES;
-        interface->fUnmapBuffer = glUnmapBufferOES;
-    #endif
-        
-    #if GL_APPLE_framebuffer_multisample
-        interface->fRenderbufferStorageMultisample = glRenderbufferStorageMultisampleAPPLE;
-        interface->fResolveMultisampleFramebuffer = glResolveMultisampleFramebufferAPPLE;
-    #endif
-        interface->fBindFragDataLocationIndexed = NULL;
-        
-        interface->fBindingsExported = kES2_GrGLBinding;
-    }
-    glInterface.get()->ref();
-    return glInterface.get();
-}
diff --git a/src/gpu/mac/GrGLCreateNativeInterface_mac.cpp b/src/gpu/mac/GrGLCreateNativeInterface_mac.cpp
deleted file mode 100644
index 5ae214a..0000000
--- a/src/gpu/mac/GrGLCreateNativeInterface_mac.cpp
+++ /dev/null
@@ -1,273 +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 "gl/GrGLInterface.h"
-
-#include <OpenGL/gl.h>
-#include <OpenGL/glext.h>
-
-#include <mach-o/dyld.h>
-
-
-// This uses deprecated functions, should rewrite using dlopen, dlsym, dlclose
-void* GetProcAddress(const char* name) {
-    NSSymbol symbol = NULL;
-    if (NSIsSymbolNameDefined(name)) {
-        symbol = NSLookupAndBindSymbol(name);
-    }
-    return NULL == symbol ? NULL : NSAddressOfSymbol(symbol); 
-}
-
-#define GET_PROC(name) ((GrGL ## name ## Proc) GetProcAddress("_gl" #name))
-#define GET_PROC_SUFFIX(name, suffix) ((GrGL ## name ## Proc) GetProcAddress("_gl" #name #suffix))
-
-const GrGLInterface* GrGLCreateNativeInterface() {
-    // The gl functions are not context-specific so we create one global 
-    // interface
-    static SkAutoTUnref<GrGLInterface> glInterface;
-    if (!glInterface.get()) {
-        GrGLInterface* interface = new GrGLInterface;
-        glInterface.reset(interface);
-        const char* verStr = (const char*) glGetString(GL_VERSION);
-        GrGLVersion ver = GrGLGetVersionFromString(verStr);
-        const char* extStr = (const char*) glGetString(GL_EXTENSIONS);
-        
-        interface->fBindingsExported = kDesktop_GrGLBinding;
-        interface->fActiveTexture = glActiveTexture;
-        interface->fAttachShader = glAttachShader;
-        interface->fBeginQuery = glBeginQuery;
-        interface->fBindAttribLocation = glBindAttribLocation;
-        interface->fBindBuffer = glBindBuffer;
-        if (ver >= GR_GL_VER(3,0)) {
-            #if GL_VERSION_3_0
-                interface->fBindFragDataLocation = glBindFragDataLocation;
-            #else
-                interface->fBindFragDataLocation = GET_PROC(BindFragDataLocation);
-            #endif
-        }
-        interface->fBindTexture = glBindTexture;
-        interface->fBlendColor = glBlendColor;
-        interface->fBlendFunc = glBlendFunc;
-        interface->fBufferData = glBufferData;
-        interface->fBufferSubData = glBufferSubData;
-        interface->fClear = glClear;
-        interface->fClearColor = glClearColor;
-        interface->fClearStencil = glClearStencil;
-        interface->fColorMask = glColorMask;
-        interface->fColorPointer = glColorPointer;
-        interface->fCompileShader = glCompileShader;
-        interface->fCompressedTexImage2D = glCompressedTexImage2D;
-        interface->fCreateProgram = glCreateProgram;
-        interface->fCreateShader = glCreateShader;
-        interface->fCullFace = glCullFace;
-        interface->fDeleteBuffers = glDeleteBuffers;
-        interface->fDeleteProgram = glDeleteProgram;
-        interface->fDeleteQueries = glDeleteQueries;
-        interface->fDeleteShader = glDeleteShader;
-        interface->fDeleteTextures = glDeleteTextures;
-        interface->fDepthMask = glDepthMask;
-        interface->fDisable = glDisable;
-        interface->fDisableVertexAttribArray =
-                                            glDisableVertexAttribArray;
-        interface->fDrawArrays = glDrawArrays;
-        interface->fDrawBuffer = glDrawBuffer;
-        interface->fDrawBuffers = glDrawBuffers;
-        interface->fDrawElements = glDrawElements;
-        interface->fEnable = glEnable;
-        interface->fEnableVertexAttribArray = glEnableVertexAttribArray;
-        interface->fEndQuery = glEndQuery;
-        interface->fFinish = glFinish;
-        interface->fFlush = glFlush;
-        interface->fFrontFace = glFrontFace;
-        interface->fGenBuffers = glGenBuffers;
-        interface->fGenQueries = glGenQueries;
-        interface->fGetBufferParameteriv = glGetBufferParameteriv;
-        interface->fGetError = glGetError;
-        interface->fGetIntegerv = glGetIntegerv;
-        interface->fGetProgramInfoLog = glGetProgramInfoLog;
-        interface->fGetProgramiv = glGetProgramiv;
-        interface->fGetQueryiv = glGetQueryiv;
-        interface->fGetQueryObjectiv = glGetQueryObjectiv;
-        interface->fGetQueryObjectuiv = glGetQueryObjectuiv;
-        interface->fGetShaderInfoLog = glGetShaderInfoLog;
-        interface->fGetShaderiv = glGetShaderiv;
-        interface->fGetString = glGetString;
-        interface->fGetTexLevelParameteriv = glGetTexLevelParameteriv;
-        interface->fGenTextures = glGenTextures;
-        interface->fGetUniformLocation = glGetUniformLocation;
-        interface->fLineWidth = glLineWidth;
-        interface->fLinkProgram = glLinkProgram;
-        interface->fMapBuffer = glMapBuffer;
-        interface->fPixelStorei = glPixelStorei;
-        interface->fReadBuffer = glReadBuffer;
-        interface->fReadPixels = glReadPixels;
-        interface->fScissor = glScissor;
-        interface->fShaderSource = glShaderSource;
-        interface->fStencilFunc = glStencilFunc;
-        interface->fStencilFuncSeparate = glStencilFuncSeparate;
-        interface->fStencilMask = glStencilMask;
-        interface->fStencilMaskSeparate = glStencilMaskSeparate;
-        interface->fStencilOp = glStencilOp;
-        interface->fStencilOpSeparate = glStencilOpSeparate;
-        // mac uses GLenum for internalFormat param (non-standard)
-        // amounts to int vs. uint.
-        interface->fTexImage2D = (GrGLTexImage2DProc)glTexImage2D;
-        interface->fTexParameteri = glTexParameteri;
-    #if GL_ARB_texture_storage || GL_VERSION_4_2
-        interface->fTexStorage2D = glTexStorage2D
-    #elif GL_EXT_texture_storage
-        interface->fTexStorage2D = glTexStorage2DEXT;
-    #else
-        if (ver >= GR_GL_VER(4,2) ||
-            GrGLHasExtensionFromString("GL_ARB_texture_storage", extStr)) {
-            GET_PROC(TexStorage2D);
-        } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", extStr)) {
-            GET_PROC_SUFFIX(TexStorage2D, EXT);
-        }
-    #endif
-        interface->fTexSubImage2D = glTexSubImage2D;
-        interface->fUniform1f = glUniform1f;
-        interface->fUniform1i = glUniform1i;
-        interface->fUniform1fv = glUniform1fv;
-        interface->fUniform1iv = glUniform1iv;
-        interface->fUniform2f = glUniform2f;
-        interface->fUniform2i = glUniform2i;
-        interface->fUniform2fv = glUniform2fv;
-        interface->fUniform2iv = glUniform2iv;
-        interface->fUniform3f = glUniform3f;
-        interface->fUniform3i = glUniform3i;
-        interface->fUniform3fv = glUniform3fv;
-        interface->fUniform3iv = glUniform3iv;
-        interface->fUniform4f = glUniform4f;
-        interface->fUniform4i = glUniform4i;
-        interface->fUniform4fv = glUniform4fv;
-        interface->fUniform4iv = glUniform4iv;
-        interface->fUniform4fv = glUniform4fv;
-        interface->fUniformMatrix2fv = glUniformMatrix2fv;
-        interface->fUniformMatrix3fv = glUniformMatrix3fv;
-        interface->fUniformMatrix4fv = glUniformMatrix4fv;
-        interface->fUnmapBuffer = glUnmapBuffer;
-        interface->fUseProgram = glUseProgram;
-        interface->fVertexAttrib4fv = glVertexAttrib4fv;
-        interface->fVertexAttribPointer = glVertexAttribPointer;
-        interface->fViewport = glViewport;
-
-        if (ver >= GR_GL_VER(3,3) || GrGLHasExtensionFromString("GL_ARB_timer_query", extStr)) {
-            // ARB extension doesn't use the ARB suffix on the function name
-            #if GL_ARB_timer_query || GL_VERSION_3_3
-                interface->fQueryCounter = glQueryCounter;
-                interface->fGetQueryObjecti64v = glGetQueryObjecti64v;
-                interface->fGetQueryObjectui64v = glGetQueryObjectui64v;
-            #else
-                interface->fQueryCounter = GET_PROC(QueryCounter);
-                interface->fGetQueryObjecti64v = GET_PROC(GetQueryObjecti64v);
-                interface->fGetQueryObjectui64v = GET_PROC(GetQueryObjectui64v);
-            #endif
-        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extStr)) {
-            #if GL_EXT_timer_query
-                interface->fGetQueryObjecti64v = glGetQueryObjecti64vEXT;
-                interface->fGetQueryObjectui64v = glGetQueryObjectui64vEXT;
-            #else
-                interface->fGetQueryObjecti64v = GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
-                interface->fGetQueryObjectui64v = GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
-            #endif            
-        }
-            
-        if (ver >= GR_GL_VER(3,0) || GrGLHasExtensionFromString("GL_ARB_framebuffer_object", extStr)) {
-            // ARB extension doesn't use the ARB suffix on the function names
-            #if GL_VERSION_3_0 || GL_ARB_framebuffer_object
-                interface->fGenFramebuffers = glGenFramebuffers;
-                interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameteriv;
-                interface->fGetRenderbufferParameteriv = glGetRenderbufferParameteriv;
-                interface->fBindFramebuffer = glBindFramebuffer;
-                interface->fFramebufferTexture2D = glFramebufferTexture2D;
-                interface->fCheckFramebufferStatus = glCheckFramebufferStatus;
-                interface->fDeleteFramebuffers = glDeleteFramebuffers;
-                interface->fRenderbufferStorage = glRenderbufferStorage;
-                interface->fGenRenderbuffers = glGenRenderbuffers;
-                interface->fDeleteRenderbuffers = glDeleteRenderbuffers;
-                interface->fFramebufferRenderbuffer = glFramebufferRenderbuffer;
-                interface->fBindRenderbuffer = glBindRenderbuffer;
-                interface->fRenderbufferStorageMultisample = glRenderbufferStorageMultisample;
-                interface->fBlitFramebuffer = glBlitFramebuffer;
-            #else
-                interface->fGenFramebuffers = GET_PROC(GenFramebuffers);
-                interface->fGetFramebufferAttachmentParameteriv = GET_PROC(GetFramebufferAttachmentParameteriv);
-                interface->fGetRenderbufferParameteriv = GET_PROC(GetRenderbufferParameteriv);
-                interface->fBindFramebuffer = GET_PROC(BindFramebuffer);
-                interface->fFramebufferTexture2D = GET_PROC(FramebufferTexture2D);
-                interface->fCheckFramebufferStatus = GET_PROC(CheckFramebufferStatus);
-                interface->fDeleteFramebuffers = GET_PROC(DeleteFramebuffers);
-                interface->fRenderbufferStorage = GET_PROC(RenderbufferStorage);
-                interface->fGenRenderbuffers = GET_PROC(GenRenderbuffers);
-                interface->fDeleteRenderbuffers = GET_PROC(DeleteRenderbuffers);
-                interface->fFramebufferRenderbuffer = GET_PROC(FramebufferRenderbuffer);
-                interface->fBindRenderbuffer = GET_PROC(BindRenderbuffer);
-                interface->fRenderbufferStorageMultisample = GET_PROC(RenderbufferStorageMultisample);
-                interface->fBlitFramebuffer = GET_PROC(BlitFramebuffer);
-            #endif
-        } else {
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object", extStr)) {
-                #if GL_EXT_framebuffer_object
-                    interface->fGenFramebuffers = glGenFramebuffersEXT;
-                    interface->fGetFramebufferAttachmentParameteriv = glGetFramebufferAttachmentParameterivEXT;
-                    interface->fGetRenderbufferParameteriv = glGetRenderbufferParameterivEXT;
-                    interface->fBindFramebuffer = glBindFramebufferEXT;
-                    interface->fFramebufferTexture2D = glFramebufferTexture2DEXT;
-                    interface->fCheckFramebufferStatus = glCheckFramebufferStatusEXT;
-                    interface->fDeleteFramebuffers = glDeleteFramebuffersEXT;
-                    interface->fRenderbufferStorage = glRenderbufferStorageEXT;
-                    interface->fGenRenderbuffers = glGenRenderbuffersEXT;
-                    interface->fDeleteRenderbuffers = glDeleteRenderbuffersEXT;
-                    interface->fFramebufferRenderbuffer = glFramebufferRenderbufferEXT;
-                    interface->fBindRenderbuffer = glBindRenderbufferEXT;
-                #else
-                    interface->fGenFramebuffers = GET_PROC_SUFFIX(GenFramebuffers, EXT);
-                    interface->fGetFramebufferAttachmentParameteriv = GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
-                    interface->fGetRenderbufferParameteriv = GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
-                    interface->fBindFramebuffer = GET_PROC_SUFFIX(BindFramebuffer, EXT);
-                    interface->fFramebufferTexture2D = GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
-                    interface->fCheckFramebufferStatus = GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
-                    interface->fDeleteFramebuffers = GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
-                    interface->fRenderbufferStorage = GET_PROC_SUFFIX(RenderbufferStorage, EXT);
-                    interface->fGenRenderbuffers = GET_PROC_SUFFIX(GenRenderbuffers, EXT);
-                    interface->fDeleteRenderbuffers = GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
-                    interface->fFramebufferRenderbuffer = GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
-                    interface->fBindRenderbuffer = GET_PROC_SUFFIX(BindRenderbuffer, EXT);
-                #endif
-            }
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample", extStr)) {
-                #if GL_EXT_framebuffer_multisample
-                    interface->fRenderbufferStorageMultisample = glRenderbufferStorageMultisampleEXT;
-                #else
-                    interface->fRenderbufferStorageMultisample = GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
-                #endif
-            }
-            if (GrGLHasExtensionFromString("", extStr)) {
-                #if GL_EXT_framebuffer_blit
-                    interface->fBlitFramebuffer = glBlitFramebufferEXT;
-                #else
-                    interface->fBlitFramebuffer = GET_PROC_SUFFIX(BlitFramebuffer, EXT);
-                #endif
-            }
-        }
-        if (ver >= GR_GL_VER(3,3) || GrGLHasExtensionFromString("GL_ARB_blend_func_extended", extStr)) {
-            // ARB extension doesn't use the ARB suffix on the function name
-            #if GL_VERSION_3_3 || GL_ARB_blend_func_extended
-                interface->fBindFragDataLocationIndexed = glBindFragDataLocationIndexed;
-            #else
-                interface->fBindFragDataLocationIndexed = GET_PROC(BindFragDataLocationIndexed);
-            #endif
-        }
-
-        interface->fBindingsExported = kDesktop_GrGLBinding;
-    }
-    glInterface.get()->ref();
-    return glInterface.get();
-}
diff --git a/src/gpu/mac/SkNativeGLContext_mac.cpp b/src/gpu/mac/SkNativeGLContext_mac.cpp
deleted file mode 100644
index 18b36a5..0000000
--- a/src/gpu/mac/SkNativeGLContext_mac.cpp
+++ /dev/null
@@ -1,74 +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 "gl/SkNativeGLContext.h"
-
-SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
-    fOldAGLContext = aglGetCurrentContext();
-}
-
-SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
-    aglSetCurrentContext(fOldAGLContext);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkNativeGLContext::SkNativeGLContext() 
-    : fContext(NULL) {
-}
-
-SkNativeGLContext::~SkNativeGLContext() {
-    this->destroyGLContext();
-}
-
-void SkNativeGLContext::destroyGLContext() {
-    if (fContext) {
-        aglDestroyContext(fContext);
-    }
-}
-
-const GrGLInterface* SkNativeGLContext::createGLContext() {
-    GLint major, minor;
-    AGLContext ctx;
-
-    aglGetVersion(&major, &minor);
-    //SkDebugf("---- agl version %d %d\n", major, minor);
-
-    const GLint pixelAttrs[] = {
-        AGL_RGBA,
-        AGL_ACCELERATED,
-        AGL_NONE
-    };
-    AGLPixelFormat format = aglChoosePixelFormat(NULL, 0, pixelAttrs);
-    if (NULL == format) {
-        SkDebugf("Format could not be found.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    fContext = aglCreateContext(format, NULL);
-    if (NULL == fContext) {
-        SkDebugf("Context could not be created.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    aglDestroyPixelFormat(format);
-
-    aglSetCurrentContext(fContext);
-    
-    const GrGLInterface* interface = GrGLCreateNativeInterface();
-    if (NULL == interface) {
-        SkDebugf("Context could not create GL interface.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    
-    return interface;
-}
-
-void SkNativeGLContext::makeCurrent() const {
-    aglSetCurrentContext(fContext);
-}
diff --git a/src/gpu/mesa/GrGLCreateMesaInterface.cpp b/src/gpu/mesa/GrGLCreateMesaInterface.cpp
deleted file mode 100644
index 4686438..0000000
--- a/src/gpu/mesa/GrGLCreateMesaInterface.cpp
+++ /dev/null
@@ -1,197 +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 "gl/GrGLInterface.h"
-
-#define GL_GLEXT_PROTOTYPES
-#include <GL/osmesa.h>
-
-#define GR_GL_GET_PROC(F) interface->f ## F = (GrGL ## F ## Proc) \
-        OSMesaGetProcAddress("gl" #F);
-#define GR_GL_GET_PROC_SUFFIX(F, S) interface->f ## F = (GrGL ## F ## Proc) \
-        OSMesaGetProcAddress("gl" #F #S);
-
-// We use OSMesaGetProcAddress for every gl function to avoid accidentally using
-// non-Mesa gl functions.
-
-const GrGLInterface* GrGLCreateMesaInterface() {
-    if (NULL != OSMesaGetCurrentContext()) {
-        GrGLGetStringProc getString = (GrGLGetStringProc) OSMesaGetProcAddress("glGetString");
-        const char* versionString = (const char*) getString(GL_VERSION);
-        const char* extString = (const char*) getString(GL_EXTENSIONS);
-        GrGLVersion glVer = GrGLGetVersionFromString(versionString);
-
-        if (glVer < GR_GL_VER(1,5)) {
-            // We must have array and element_array buffer objects.
-            return NULL;
-        }
-        GrGLInterface* interface = new GrGLInterface();
-
-        GR_GL_GET_PROC(ActiveTexture);
-        GR_GL_GET_PROC(BeginQuery);
-        GR_GL_GET_PROC(AttachShader);
-        GR_GL_GET_PROC(BindAttribLocation);
-        GR_GL_GET_PROC(BindBuffer);
-        GR_GL_GET_PROC(BindFragDataLocation);
-        GR_GL_GET_PROC(BindTexture);
-        GR_GL_GET_PROC(BlendColor);
-        GR_GL_GET_PROC(BlendFunc);
-        GR_GL_GET_PROC(BufferData);
-        GR_GL_GET_PROC(BufferSubData);
-        GR_GL_GET_PROC(Clear);
-        GR_GL_GET_PROC(ClearColor);
-        GR_GL_GET_PROC(ClearStencil);
-        GR_GL_GET_PROC(ColorMask);
-        GR_GL_GET_PROC(CompileShader);
-        GR_GL_GET_PROC(CompressedTexImage2D);
-        GR_GL_GET_PROC(CreateProgram);
-        GR_GL_GET_PROC(CreateShader);
-        GR_GL_GET_PROC(CullFace);
-        GR_GL_GET_PROC(DeleteBuffers);
-        GR_GL_GET_PROC(DeleteProgram);
-        GR_GL_GET_PROC(DeleteQueries);
-        GR_GL_GET_PROC(DeleteShader);
-        GR_GL_GET_PROC(DeleteTextures);
-        GR_GL_GET_PROC(DepthMask);
-        GR_GL_GET_PROC(Disable);
-        GR_GL_GET_PROC(DisableVertexAttribArray);
-        GR_GL_GET_PROC(DrawArrays);
-        GR_GL_GET_PROC(DrawBuffer);
-        GR_GL_GET_PROC(DrawBuffers);
-        GR_GL_GET_PROC(DrawElements);
-        GR_GL_GET_PROC(Enable);
-        GR_GL_GET_PROC(EnableVertexAttribArray);
-        GR_GL_GET_PROC(EndQuery);
-        GR_GL_GET_PROC(Finish);
-        GR_GL_GET_PROC(Flush);
-        GR_GL_GET_PROC(FrontFace);
-        GR_GL_GET_PROC(GenBuffers);
-        GR_GL_GET_PROC(GenQueries);
-        GR_GL_GET_PROC(GetBufferParameteriv);
-        GR_GL_GET_PROC(GetError);
-        GR_GL_GET_PROC(GetIntegerv);
-        GR_GL_GET_PROC(GetProgramInfoLog);
-        GR_GL_GET_PROC(GetProgramiv);
-        if (glVer >= GR_GL_VER(3,3) ||
-            GrGLHasExtensionFromString("GL_ARB_timer_query", extString)) {
-            GR_GL_GET_PROC(GetQueryObjecti64v);
-            GR_GL_GET_PROC(GetQueryObjectui64v)
-            GR_GL_GET_PROC(QueryCounter);
-        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extString)) {
-            GR_GL_GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
-        }
-        GR_GL_GET_PROC(GetQueryObjectiv);
-        GR_GL_GET_PROC(GetQueryObjectuiv);
-        GR_GL_GET_PROC(GetQueryiv);
-        GR_GL_GET_PROC(GetShaderInfoLog);
-        GR_GL_GET_PROC(GetShaderiv);
-        GR_GL_GET_PROC(GetString);
-        GR_GL_GET_PROC(GetTexLevelParameteriv);
-        GR_GL_GET_PROC(GenTextures);
-        GR_GL_GET_PROC(GetUniformLocation);
-        GR_GL_GET_PROC(LineWidth);
-        GR_GL_GET_PROC(LinkProgram);
-        GR_GL_GET_PROC(MapBuffer);
-        GR_GL_GET_PROC(PixelStorei);
-        GR_GL_GET_PROC(ReadBuffer);
-        GR_GL_GET_PROC(ReadPixels);
-        GR_GL_GET_PROC(Scissor);
-        GR_GL_GET_PROC(ShaderSource);
-        GR_GL_GET_PROC(StencilFunc);
-        GR_GL_GET_PROC(StencilFuncSeparate);
-        GR_GL_GET_PROC(StencilMask);
-        GR_GL_GET_PROC(StencilMaskSeparate);
-        GR_GL_GET_PROC(StencilOp);
-        GR_GL_GET_PROC(StencilOpSeparate);
-        GR_GL_GET_PROC(TexImage2D)
-        GR_GL_GET_PROC(TexParameteri);
-        GR_GL_GET_PROC(TexStorage2D);
-        if (NULL == interface->fTexStorage2D) {
-            GR_GL_GET_PROC_SUFFIX(TexStorage2D, EXT);
-        }
-        GR_GL_GET_PROC(TexSubImage2D);
-        GR_GL_GET_PROC(Uniform1f);
-        GR_GL_GET_PROC(Uniform1i);
-        GR_GL_GET_PROC(Uniform1fv);
-        GR_GL_GET_PROC(Uniform1iv);
-        GR_GL_GET_PROC(Uniform2f);
-        GR_GL_GET_PROC(Uniform2i);
-        GR_GL_GET_PROC(Uniform2fv);
-        GR_GL_GET_PROC(Uniform2iv);
-        GR_GL_GET_PROC(Uniform3f);
-        GR_GL_GET_PROC(Uniform3i);
-        GR_GL_GET_PROC(Uniform3fv);
-        GR_GL_GET_PROC(Uniform3iv);
-        GR_GL_GET_PROC(Uniform4f);
-        GR_GL_GET_PROC(Uniform4i);
-        GR_GL_GET_PROC(Uniform4fv);
-        GR_GL_GET_PROC(Uniform4iv);
-        GR_GL_GET_PROC(UniformMatrix2fv);
-        GR_GL_GET_PROC(UniformMatrix3fv);
-        GR_GL_GET_PROC(UniformMatrix4fv);
-        GR_GL_GET_PROC(UnmapBuffer);
-        GR_GL_GET_PROC(UseProgram);
-        GR_GL_GET_PROC(VertexAttrib4fv);
-        GR_GL_GET_PROC(VertexAttribPointer);
-        GR_GL_GET_PROC(Viewport);
-
-        // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
-        // GL_ARB_framebuffer_object doesn't use ARB suffix.)
-        if (glVer >= GR_GL_VER(3,0) ||
-            GrGLHasExtensionFromString("GL_ARB_framebuffer_object",
-                                        extString)) {
-            GR_GL_GET_PROC(GenFramebuffers);
-            GR_GL_GET_PROC(GetFramebufferAttachmentParameteriv);
-            GR_GL_GET_PROC(GetRenderbufferParameteriv);
-            GR_GL_GET_PROC(BindFramebuffer);
-            GR_GL_GET_PROC(FramebufferTexture2D);
-            GR_GL_GET_PROC(CheckFramebufferStatus);
-            GR_GL_GET_PROC(DeleteFramebuffers);
-            GR_GL_GET_PROC(RenderbufferStorage);
-            GR_GL_GET_PROC(GenRenderbuffers);
-            GR_GL_GET_PROC(DeleteRenderbuffers);
-            GR_GL_GET_PROC(FramebufferRenderbuffer);
-            GR_GL_GET_PROC(BindRenderbuffer);
-            GR_GL_GET_PROC(RenderbufferStorageMultisample);
-            GR_GL_GET_PROC(BlitFramebuffer);
-        } else if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object",
-                                              extString)) {
-            GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
-            GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT);
-            GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
-            GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
-            GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT);
-            GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
-            GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT);
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample",
-                                           extString)) {
-                GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
-            }
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit",
-                                           extString)) {
-                GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
-            }
-        } else {
-            // we must have FBOs
-            delete interface;
-            return NULL;
-        }
-        GR_GL_GET_PROC(BindFragDataLocationIndexed);
-        interface->fBindingsExported = kDesktop_GrGLBinding;
-        return interface;
-    } else {
-        return NULL;
-    }
-}
diff --git a/src/gpu/mesa/SkMesaGLContext.cpp b/src/gpu/mesa/SkMesaGLContext.cpp
deleted file mode 100644
index c4f84cf..0000000
--- a/src/gpu/mesa/SkMesaGLContext.cpp
+++ /dev/null
@@ -1,103 +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 <GL/osmesa.h>
-
-#include "gl/SkMesaGLContext.h"
-
-SkMesaGLContext::AutoContextRestore::AutoContextRestore() {
-    fOldContext = (Context)OSMesaGetCurrentContext();
-    if (NULL != (OSMesaContext)fOldContext) {
-        OSMesaGetColorBuffer((OSMesaContext)fOldContext,
-                              &fOldWidth, &fOldHeight,
-                              &fOldFormat, &fOldImage); 
-    }
-}
-
-SkMesaGLContext::AutoContextRestore::~AutoContextRestore() {
-    if (NULL != (OSMesaContext)fOldContext) {
-        OSMesaMakeCurrent((OSMesaContext)fOldContext, fOldImage, 
-                          fOldFormat, fOldWidth, fOldHeight);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-SkMesaGLContext::SkMesaGLContext()
-    : fContext(NULL)
-    , fImage(NULL) {
-    GR_STATIC_ASSERT(sizeof(Context) == sizeof(OSMesaContext));
-}
-
-SkMesaGLContext::~SkMesaGLContext() {
-    this->destroyGLContext();
-}
-
-void SkMesaGLContext::destroyGLContext() {
-    if (fImage) {
-        sk_free(fImage);
-    }
-    
-    if (fContext) {
-        OSMesaDestroyContext((OSMesaContext)fContext);
-    }
-}
-
-static const GrGLint gBOGUS_SIZE = 16;
-
-const GrGLInterface* SkMesaGLContext::createGLContext() {
-    /* Create an RGBA-mode context */
-#if OSMESA_MAJOR_VERSION * 100 + OSMESA_MINOR_VERSION >= 305
-    /* specify Z, stencil, accum sizes */
-    fContext = (Context)OSMesaCreateContextExt(OSMESA_BGRA, 0, 0, 0, NULL);
-#else
-    fContext = (Context)OSMesaCreateContext(OSMESA_BGRA, NULL);
-#endif
-    if (!fContext) {
-        SkDebugf("OSMesaCreateContext failed!\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    // Allocate the image buffer
-    fImage = (GrGLubyte *) sk_malloc_throw(gBOGUS_SIZE * gBOGUS_SIZE *
-                                           4 * sizeof(GrGLubyte));
-    if (!fImage) {
-        SkDebugf("Alloc image buffer failed!\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    
-    // Bind the buffer to the context and make it current
-    if (!OSMesaMakeCurrent((OSMesaContext)fContext, 
-                           fImage, 
-                           GR_GL_UNSIGNED_BYTE, 
-                           gBOGUS_SIZE, 
-                           gBOGUS_SIZE)) {
-        SkDebugf("OSMesaMakeCurrent failed!\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    
-    const GrGLInterface* interface = GrGLCreateMesaInterface();
-    if (!interface) {
-        SkDebugf("Could not create GL interface!\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    return interface;
-    
-}
-
-void SkMesaGLContext::makeCurrent() const {
-    if (fContext) {
-        if (!OSMesaMakeCurrent((OSMesaContext)fContext, fImage, 
-                               GR_GL_UNSIGNED_BYTE, gBOGUS_SIZE, gBOGUS_SIZE)) {
-            SkDebugf("Could not make MESA context current.");
-        }
-    }
-}
diff --git a/src/gpu/unix/GrGLCreateNativeInterface_unix.cpp b/src/gpu/unix/GrGLCreateNativeInterface_unix.cpp
deleted file mode 100644
index 1e9f2e0..0000000
--- a/src/gpu/unix/GrGLCreateNativeInterface_unix.cpp
+++ /dev/null
@@ -1,200 +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 "gl/GrGLInterface.h"
-
-#include <GL/glx.h>
-#include <GL/gl.h>
-#include <GL/glext.h>
-#include <GL/glu.h>
-
-#define GR_GL_GET_PROC(F) interface->f ## F = (GrGL ## F ## Proc) \
-        glXGetProcAddress(reinterpret_cast<const GLubyte*>("gl" #F));
-#define GR_GL_GET_PROC_SUFFIX(F, S) interface->f ## F = (GrGL ## F ## Proc) \
-        glXGetProcAddress(reinterpret_cast<const GLubyte*>("gl" #F #S));
-
-const GrGLInterface* GrGLCreateNativeInterface() {
-    if (NULL != glXGetCurrentContext()) {
-        const char* versionString = (const char*) glGetString(GL_VERSION);
-        const char* extString = (const char*) glGetString(GL_EXTENSIONS);
-        GrGLVersion glVer = GrGLGetVersionFromString(versionString);
-
-        if (glVer < GR_GL_VER(1,5)) {
-            // We must have array and element_array buffer objects.
-            return NULL;
-        }
-
-        GrGLInterface* interface = new GrGLInterface();
-
-        interface->fActiveTexture = glActiveTexture;
-        GR_GL_GET_PROC(AttachShader);
-        GR_GL_GET_PROC(BindAttribLocation);
-        GR_GL_GET_PROC(BindBuffer);
-        GR_GL_GET_PROC(BindFragDataLocation);
-        GR_GL_GET_PROC(BeginQuery);
-        interface->fBindTexture = glBindTexture;
-        interface->fBlendColor = glBlendColor;
-        interface->fBlendFunc = glBlendFunc;
-        GR_GL_GET_PROC(BufferData);
-        GR_GL_GET_PROC(BufferSubData);
-        interface->fClear = glClear;
-        interface->fClearColor = glClearColor;
-        interface->fClearStencil = glClearStencil;
-        interface->fColorMask = glColorMask;
-        interface->fColorPointer = glColorPointer;
-        GR_GL_GET_PROC(CompileShader);
-        interface->fCompressedTexImage2D = glCompressedTexImage2D;
-        GR_GL_GET_PROC(CreateProgram);
-        GR_GL_GET_PROC(CreateShader);
-        interface->fCullFace = glCullFace;
-        GR_GL_GET_PROC(DeleteBuffers);
-        GR_GL_GET_PROC(DeleteProgram);
-        GR_GL_GET_PROC(DeleteQueries);
-        GR_GL_GET_PROC(DeleteShader);
-        interface->fDeleteTextures = glDeleteTextures;
-        interface->fDepthMask = glDepthMask;
-        interface->fDisable = glDisable;
-        GR_GL_GET_PROC(DisableVertexAttribArray);
-        interface->fDrawArrays = glDrawArrays;
-        interface->fDrawBuffer = glDrawBuffer;
-        GR_GL_GET_PROC(DrawBuffers);
-        interface->fDrawElements = glDrawElements;
-        interface->fEnable = glEnable;
-        GR_GL_GET_PROC(EnableVertexAttribArray);
-        GR_GL_GET_PROC(EndQuery);
-        interface->fFinish = glFinish;
-        interface->fFlush = glFlush;
-        interface->fFrontFace = glFrontFace;
-        GR_GL_GET_PROC(GenBuffers);
-        GR_GL_GET_PROC(GetBufferParameteriv);
-        interface->fGetError = glGetError;
-        interface->fGetIntegerv = glGetIntegerv;
-        GR_GL_GET_PROC(GetQueryObjectiv);
-        GR_GL_GET_PROC(GetQueryObjectuiv);
-        if (glVer >= GR_GL_VER(3,3) ||
-            GrGLHasExtensionFromString("GL_ARB_timer_query", extString)) {
-            GR_GL_GET_PROC(GetQueryObjecti64v);
-            GR_GL_GET_PROC(GetQueryObjectui64v);
-            GR_GL_GET_PROC(QueryCounter);
-        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extString)) {
-            GR_GL_GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
-        }
-        GR_GL_GET_PROC(GetQueryiv);
-        GR_GL_GET_PROC(GetProgramInfoLog);
-        GR_GL_GET_PROC(GetProgramiv);
-        GR_GL_GET_PROC(GetShaderInfoLog);
-        GR_GL_GET_PROC(GetShaderiv);
-        interface->fGetString = glGetString;
-        interface->fGetTexLevelParameteriv = glGetTexLevelParameteriv;
-        GR_GL_GET_PROC(GenQueries);
-        interface->fGenTextures = glGenTextures;
-        GR_GL_GET_PROC(GetUniformLocation);
-        interface->fLineWidth = glLineWidth;
-        GR_GL_GET_PROC(LinkProgram);
-        GR_GL_GET_PROC(MapBuffer);
-        interface->fPixelStorei = glPixelStorei;
-        interface->fReadBuffer = glReadBuffer;
-        interface->fReadPixels = glReadPixels;
-        interface->fScissor = glScissor;
-        GR_GL_GET_PROC(ShaderSource);
-        interface->fStencilFunc = glStencilFunc;
-        GR_GL_GET_PROC(StencilFuncSeparate);
-        interface->fStencilMask = glStencilMask;
-        GR_GL_GET_PROC(StencilMaskSeparate);
-        interface->fStencilOp = glStencilOp;
-        GR_GL_GET_PROC(StencilOpSeparate);
-        interface->fTexImage2D = glTexImage2D;
-        interface->fTexParameteri = glTexParameteri;
-        if (glVer >= GR_GL_VER(4,2) ||
-            GrGLHasExtensionFromString("GL_ARB_texture_storage", extString)) {
-            GR_GL_GET_PROC(TexStorage2D);
-        } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", extString)) {
-            GR_GL_GET_PROC_SUFFIX(TexStorage2D, EXT);
-        }
-        interface->fTexSubImage2D = glTexSubImage2D;
-        GR_GL_GET_PROC(Uniform1f);
-        GR_GL_GET_PROC(Uniform1i);
-        GR_GL_GET_PROC(Uniform1fv);
-        GR_GL_GET_PROC(Uniform1iv);
-        GR_GL_GET_PROC(Uniform2f);
-        GR_GL_GET_PROC(Uniform2i);
-        GR_GL_GET_PROC(Uniform2fv);
-        GR_GL_GET_PROC(Uniform2iv);
-        GR_GL_GET_PROC(Uniform3f);
-        GR_GL_GET_PROC(Uniform3i);
-        GR_GL_GET_PROC(Uniform3fv);
-        GR_GL_GET_PROC(Uniform3iv);
-        GR_GL_GET_PROC(Uniform4f);
-        GR_GL_GET_PROC(Uniform4i);
-        GR_GL_GET_PROC(Uniform4fv);
-        GR_GL_GET_PROC(Uniform4iv);
-        GR_GL_GET_PROC(UniformMatrix2fv);
-        GR_GL_GET_PROC(UniformMatrix3fv);
-        GR_GL_GET_PROC(UniformMatrix4fv);
-        GR_GL_GET_PROC(UnmapBuffer);
-        GR_GL_GET_PROC(UseProgram);
-        GR_GL_GET_PROC(VertexAttrib4fv);
-        GR_GL_GET_PROC(VertexAttribPointer);
-        interface->fViewport = glViewport;
-        GR_GL_GET_PROC(BindFragDataLocationIndexed);
-
-        // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
-        // GL_ARB_framebuffer_object doesn't use ARB suffix.)
-        if (glVer >= GR_GL_VER(3,0) ||
-            GrGLHasExtensionFromString("GL_ARB_framebuffer_object",
-                                       extString)) {
-            GR_GL_GET_PROC(GenFramebuffers);
-            GR_GL_GET_PROC(GetFramebufferAttachmentParameteriv);
-            GR_GL_GET_PROC(GetRenderbufferParameteriv);
-            GR_GL_GET_PROC(BindFramebuffer);
-            GR_GL_GET_PROC(FramebufferTexture2D);
-            GR_GL_GET_PROC(CheckFramebufferStatus);
-            GR_GL_GET_PROC(DeleteFramebuffers);
-            GR_GL_GET_PROC(RenderbufferStorage);
-            GR_GL_GET_PROC(GenRenderbuffers);
-            GR_GL_GET_PROC(DeleteRenderbuffers);
-            GR_GL_GET_PROC(FramebufferRenderbuffer);
-            GR_GL_GET_PROC(BindRenderbuffer);
-            GR_GL_GET_PROC(RenderbufferStorageMultisample);
-            GR_GL_GET_PROC(BlitFramebuffer);
-        } else if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object",
-                                              extString)) {
-            GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
-            GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT);
-            GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
-            GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
-            GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT);
-            GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
-            GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT);
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample",
-                                             extString)) {
-                GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
-            }
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit",
-                                             extString)) {
-                GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
-            }
-        } else {
-            // we must have FBOs
-            delete interface;
-            return NULL;
-        }
-        interface->fBindingsExported = kDesktop_GrGLBinding;
-
-        return interface;
-    } else {
-        return NULL;
-    }
-}
diff --git a/src/gpu/unix/SkNativeGLContext_unix.cpp b/src/gpu/unix/SkNativeGLContext_unix.cpp
deleted file mode 100644
index f4199eb..0000000
--- a/src/gpu/unix/SkNativeGLContext_unix.cpp
+++ /dev/null
@@ -1,258 +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 "gl/SkNativeGLContext.h"
-
-#include <GL/glu.h>
-
-SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
-    fOldGLXContext = glXGetCurrentContext();
-    fOldDisplay = glXGetCurrentDisplay();
-    fOldDrawable = glXGetCurrentDrawable();
-}
-
-SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
-    if (NULL != fOldDisplay) {
-        glXMakeCurrent(fOldDisplay, fOldDrawable, fOldGLXContext);
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static bool ctxErrorOccurred = false;
-static int ctxErrorHandler(Display *dpy, XErrorEvent *ev) {
-    ctxErrorOccurred = true;
-    return 0;
-}
-
-SkNativeGLContext::SkNativeGLContext() 
-    : fContext(NULL)
-    , fDisplay(NULL)
-    , fPixmap(0)
-    , fGlxPixmap(0) {
-}
-
-SkNativeGLContext::~SkNativeGLContext() {
-    this->destroyGLContext();
-}
-
-void SkNativeGLContext::destroyGLContext() {
-    if (fDisplay) {
-        glXMakeCurrent(fDisplay, 0, 0);
-
-        if (fContext) {
-            glXDestroyContext(fDisplay, fContext);
-            fContext = NULL;
-        }
-
-        if (fGlxPixmap) {
-            glXDestroyGLXPixmap(fDisplay, fGlxPixmap);
-            fGlxPixmap = 0;
-        }
-
-        if (fPixmap) {
-            XFreePixmap(fDisplay, fPixmap);
-            fPixmap = 0;
-        }
-
-        XCloseDisplay(fDisplay);
-        fDisplay = NULL;
-    }
-}
-
-const GrGLInterface* SkNativeGLContext::createGLContext() {
-    fDisplay = XOpenDisplay(0);
-
-    if (!fDisplay) {
-        SkDebugf("Failed to open X display.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-
-    // Get a matching FB config
-    static int visual_attribs[] = {
-        GLX_X_RENDERABLE    , True,
-        GLX_DRAWABLE_TYPE   , GLX_PIXMAP_BIT,
-        None
-    };
-
-    int glx_major, glx_minor;
-
-    // FBConfigs were added in GLX version 1.3.
-    if (!glXQueryVersion(fDisplay, &glx_major, &glx_minor) ||
-            ( (glx_major == 1) && (glx_minor < 3) ) || (glx_major < 1))
-    {
-        SkDebugf("Invalid GLX version.");
-        this->destroyGLContext();
-        return NULL;
-    }
-
-    //SkDebugf("Getting matching framebuffer configs.\n");
-    int fbcount;
-    GLXFBConfig *fbc = glXChooseFBConfig(fDisplay, DefaultScreen(fDisplay),
-                                          visual_attribs, &fbcount);
-    if (!fbc) {
-        SkDebugf("Failed to retrieve a framebuffer config.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    //SkDebugf("Found %d matching FB configs.\n", fbcount);
-
-    // Pick the FB config/visual with the most samples per pixel
-    //SkDebugf("Getting XVisualInfos.\n");
-    int best_fbc = -1, worst_fbc = -1, best_num_samp = -1, worst_num_samp = 999;
-
-    int i;
-    for (i = 0; i < fbcount; ++i) {
-        XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, fbc[i]);
-        if (vi) {
-            int samp_buf, samples;
-            glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLE_BUFFERS, &samp_buf);
-            glXGetFBConfigAttrib(fDisplay, fbc[i], GLX_SAMPLES, &samples);
-
-            //SkDebugf("  Matching fbconfig %d, visual ID 0x%2x: SAMPLE_BUFFERS = %d,"
-            //       " SAMPLES = %d\n",
-            //        i, (unsigned int)vi->visualid, samp_buf, samples);
-
-            if (best_fbc < 0 || (samp_buf && samples > best_num_samp))
-                best_fbc = i, best_num_samp = samples;
-            if (worst_fbc < 0 || !samp_buf || samples < worst_num_samp)
-                worst_fbc = i, worst_num_samp = samples;
-        }
-        XFree(vi);
-    }
-
-    GLXFBConfig bestFbc = fbc[best_fbc];
-
-    // Be sure to free the FBConfig list allocated by glXChooseFBConfig()
-    XFree(fbc);
-
-    // Get a visual
-    XVisualInfo *vi = glXGetVisualFromFBConfig(fDisplay, bestFbc);
-    //SkDebugf("Chosen visual ID = 0x%x\n", (unsigned int)vi->visualid);
-
-    fPixmap = XCreatePixmap(fDisplay, RootWindow(fDisplay, vi->screen), 10, 10, vi->depth);
-
-    if (!fPixmap) {
-        SkDebugf("Failed to create pixmap.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-
-    fGlxPixmap = glXCreateGLXPixmap(fDisplay, vi, fPixmap);
-
-    // Done with the visual info data
-    XFree(vi);
-
-    // Create the context
-
-    // Install an X error handler so the application won't exit if GL 3.0
-    // context allocation fails.
-    //
-    // Note this error handler is global.
-    // All display connections in all threads of a process use the same
-    // error handler, so be sure to guard against other threads issuing
-    // X commands while this code is running.
-    ctxErrorOccurred = false;
-    int (*oldHandler)(Display*, XErrorEvent*) =
-        XSetErrorHandler(&ctxErrorHandler);
-
-    // Get the default screen's GLX extension list
-    const char *glxExts = glXQueryExtensionsString(
-        fDisplay, DefaultScreen(fDisplay)
-    );
-    // Check for the GLX_ARB_create_context extension string and the function.
-    // If either is not present, use GLX 1.3 context creation method.
-    if (!gluCheckExtension(
-          reinterpret_cast<const GLubyte*>("GLX_ARB_create_context")
-          , reinterpret_cast<const GLubyte*>(glxExts)))
-    {
-        //SkDebugf("GLX_ARB_create_context not found."
-        //       " Using old-style GLX context.\n");
-        fContext = glXCreateNewContext(fDisplay, bestFbc, GLX_RGBA_TYPE, 0, True);
-
-    } else {
-        //SkDebugf("Creating context.\n");
-
-        PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribsARB = 
-            (PFNGLXCREATECONTEXTATTRIBSARBPROC) glXGetProcAddressARB((GrGLubyte*)"glXCreateContextAttribsARB");
-        int context_attribs[] = {
-            GLX_CONTEXT_MAJOR_VERSION_ARB, 3,
-            GLX_CONTEXT_MINOR_VERSION_ARB, 0,
-            //GLX_CONTEXT_FLAGS_ARB        , GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB,
-            None
-        };
-        fContext = glXCreateContextAttribsARB(
-            fDisplay, bestFbc, 0, True, context_attribs
-        );
-
-        // Sync to ensure any errors generated are processed.
-        XSync(fDisplay, False);
-        if (!ctxErrorOccurred && fContext) {
-           //SkDebugf( "Created GL 3.0 context.\n" );
-        } else {
-            // Couldn't create GL 3.0 context.
-            // Fall back to old-style 2.x context.
-            // When a context version below 3.0 is requested,
-            // implementations will return the newest context version compatible
-            // with OpenGL versions less than version 3.0.
-
-            // GLX_CONTEXT_MAJOR_VERSION_ARB = 1
-            context_attribs[1] = 1;
-            // GLX_CONTEXT_MINOR_VERSION_ARB = 0
-            context_attribs[3] = 0;
-
-            ctxErrorOccurred = false;
-
-            //SkDebugf("Failed to create GL 3.0 context."
-            //       " Using old-style GLX context.\n");
-            fContext = glXCreateContextAttribsARB(
-                fDisplay, bestFbc, 0, True, context_attribs
-            );
-        }
-    }
-
-    // Sync to ensure any errors generated are processed.
-    XSync(fDisplay, False);
-
-    // Restore the original error handler
-    XSetErrorHandler(oldHandler);
-
-    if (ctxErrorOccurred || !fContext) {
-        SkDebugf("Failed to create an OpenGL context.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-
-    // Verify that context is a direct context
-    if (!glXIsDirect(fDisplay, fContext)) {
-        //SkDebugf("Indirect GLX rendering context obtained.\n");
-    } else {
-        //SkDebugf("Direct GLX rendering context obtained.\n");
-    }
-
-    //SkDebugf("Making context current.\n");
-    if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
-      SkDebugf("Could not set the context.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-
-    const GrGLInterface* interface = GrGLCreateNativeInterface();
-    if (!interface) {
-        SkDebugf("Failed to create gl interface");
-        this->destroyGLContext();
-        return NULL;
-    }
-    return interface;
-}
-
-void SkNativeGLContext::makeCurrent() const {
-    if (!glXMakeCurrent(fDisplay, fGlxPixmap, fContext)) {
-        SkDebugf("Could not set the context.\n");
-    }
-}
diff --git a/src/gpu/win/GrGLCreateNativeInterface_win.cpp b/src/gpu/win/GrGLCreateNativeInterface_win.cpp
deleted file mode 100644
index f050a6f..0000000
--- a/src/gpu/win/GrGLCreateNativeInterface_win.cpp
+++ /dev/null
@@ -1,206 +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 "gl/GrGLInterface.h"
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-#include <GL/GL.h>
-
-/*
- * Windows makes the GL funcs all be __stdcall instead of __cdecl :(
- * This implementation will only work if GR_GL_FUNCTION_TYPE is __stdcall.
- * Otherwise, a springboard would be needed that hides the calling convention.
- */
-
-#define GR_GL_GET_PROC(F) interface->f ## F = (GrGL ## F ## Proc) wglGetProcAddress("gl" #F);
-#define GR_GL_GET_PROC_SUFFIX(F, S) interface->f ## F = (GrGL ## F ## Proc) wglGetProcAddress("gl" #F #S);
-
-const GrGLInterface* GrGLCreateNativeInterface() {
-    // wglGetProcAddress requires a context.
-    // GL Function pointers retrieved in one context may not be valid in another
-    // context. For that reason we create a new GrGLInterface each time we're 
-    // called.
-    if (NULL != wglGetCurrentContext()) {
-        const char* versionString = (const char*) glGetString(GL_VERSION);
-        const char* extString = (const char*) glGetString(GL_EXTENSIONS);
-        GrGLVersion glVer = GrGLGetVersionFromString(versionString);
-
-        if (glVer < GR_GL_VER(1,5)) {
-            // We must have array and element_array buffer objects.
-            return NULL;
-        }
-        GrGLInterface* interface = new GrGLInterface();
-
-        // Functions that are part of GL 1.1 will return NULL in
-        // wglGetProcAddress
-        interface->fBindTexture = glBindTexture;
-        interface->fBlendFunc = glBlendFunc;
-        interface->fClear = glClear;
-        interface->fClearColor = glClearColor;
-        interface->fClearStencil = glClearStencil;
-        interface->fColorMask = glColorMask;
-        interface->fColorPointer = glColorPointer;
-        interface->fCullFace = glCullFace;
-        interface->fDeleteTextures = glDeleteTextures;
-        interface->fDepthMask = glDepthMask;
-        interface->fDisable = glDisable;
-        interface->fDrawArrays = glDrawArrays;
-        interface->fDrawElements = glDrawElements;
-        interface->fDrawBuffer = glDrawBuffer;
-        interface->fEnable = glEnable;
-        interface->fFrontFace = glFrontFace;
-        interface->fFinish = glFinish;
-        interface->fFlush = glFlush;
-        interface->fGenTextures = glGenTextures;
-        interface->fGetError = glGetError;
-        interface->fGetIntegerv = glGetIntegerv;
-        interface->fGetString = glGetString;
-        interface->fGetTexLevelParameteriv = glGetTexLevelParameteriv;
-        interface->fLineWidth = glLineWidth;
-        interface->fPixelStorei = glPixelStorei;
-        interface->fReadBuffer = glReadBuffer;
-        interface->fReadPixels = glReadPixels;
-        interface->fScissor = glScissor;
-        interface->fStencilFunc = glStencilFunc;
-        interface->fStencilMask = glStencilMask;
-        interface->fStencilOp = glStencilOp;
-        interface->fTexImage2D = glTexImage2D;
-        interface->fTexParameteri = glTexParameteri;
-        if (glVer >= GR_GL_VER(4,2) ||
-            GrGLHasExtensionFromString("GL_ARB_texture_storage", extString)) {
-            GR_GL_GET_PROC(TexStorage2D);
-        } else if (GrGLHasExtensionFromString("GL_EXT_texture_storage", extString)) {
-            GR_GL_GET_PROC_SUFFIX(TexStorage2D, EXT);
-        }
-        interface->fTexSubImage2D = glTexSubImage2D;
-        interface->fViewport = glViewport;
-
-        GR_GL_GET_PROC(ActiveTexture);
-        GR_GL_GET_PROC(AttachShader);
-        GR_GL_GET_PROC(BeginQuery);
-        GR_GL_GET_PROC(BindAttribLocation);
-        GR_GL_GET_PROC(BindBuffer);
-        GR_GL_GET_PROC(BindFragDataLocation);
-        GR_GL_GET_PROC(BlendColor);
-        GR_GL_GET_PROC(BufferData);
-        GR_GL_GET_PROC(BufferSubData);
-        GR_GL_GET_PROC(CompileShader);
-        GR_GL_GET_PROC(CompressedTexImage2D);
-        GR_GL_GET_PROC(CreateProgram);
-        GR_GL_GET_PROC(CreateShader);
-        GR_GL_GET_PROC(DeleteBuffers);
-        GR_GL_GET_PROC(DeleteQueries);
-        GR_GL_GET_PROC(DeleteProgram);
-        GR_GL_GET_PROC(DeleteShader);
-        GR_GL_GET_PROC(DisableVertexAttribArray);
-        GR_GL_GET_PROC(DrawBuffers);
-        GR_GL_GET_PROC(EnableVertexAttribArray);
-        GR_GL_GET_PROC(EndQuery);
-        GR_GL_GET_PROC(GenBuffers);
-        GR_GL_GET_PROC(GenQueries);
-        GR_GL_GET_PROC(GetBufferParameteriv);
-        GR_GL_GET_PROC(GetQueryiv);
-        GR_GL_GET_PROC(GetQueryObjectiv);
-        GR_GL_GET_PROC(GetQueryObjectuiv);
-        if (glVer > GR_GL_VER(3,3) || 
-            GrGLHasExtensionFromString("GL_ARB_timer_query", extString)) {
-            GR_GL_GET_PROC(GetQueryObjecti64v);
-            GR_GL_GET_PROC(GetQueryObjectui64v);
-            GR_GL_GET_PROC(QueryCounter);
-        } else if (GrGLHasExtensionFromString("GL_EXT_timer_query", extString)) {
-            GR_GL_GET_PROC_SUFFIX(GetQueryObjecti64v, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetQueryObjectui64v, EXT);
-        }
-        GR_GL_GET_PROC(GetProgramInfoLog);
-        GR_GL_GET_PROC(GetProgramiv);
-        GR_GL_GET_PROC(GetShaderInfoLog);
-        GR_GL_GET_PROC(GetShaderiv);
-        GR_GL_GET_PROC(GetUniformLocation);
-        GR_GL_GET_PROC(LinkProgram);
-        GR_GL_GET_PROC(ShaderSource);
-        GR_GL_GET_PROC(StencilFuncSeparate);
-        GR_GL_GET_PROC(StencilMaskSeparate);
-        GR_GL_GET_PROC(StencilOpSeparate);
-        GR_GL_GET_PROC(Uniform1f);
-        GR_GL_GET_PROC(Uniform1i);
-        GR_GL_GET_PROC(Uniform1fv);
-        GR_GL_GET_PROC(Uniform1iv);
-        GR_GL_GET_PROC(Uniform2f);
-        GR_GL_GET_PROC(Uniform2i);
-        GR_GL_GET_PROC(Uniform2fv);
-        GR_GL_GET_PROC(Uniform2iv);
-        GR_GL_GET_PROC(Uniform3f);
-        GR_GL_GET_PROC(Uniform3i);
-        GR_GL_GET_PROC(Uniform3fv);
-        GR_GL_GET_PROC(Uniform3iv);
-        GR_GL_GET_PROC(Uniform4f);
-        GR_GL_GET_PROC(Uniform4i);
-        GR_GL_GET_PROC(Uniform4fv);
-        GR_GL_GET_PROC(Uniform4iv);
-        GR_GL_GET_PROC(UniformMatrix2fv);
-        GR_GL_GET_PROC(UniformMatrix3fv);
-        GR_GL_GET_PROC(UniformMatrix4fv);
-        GR_GL_GET_PROC(UseProgram);
-        GR_GL_GET_PROC(VertexAttrib4fv);
-        GR_GL_GET_PROC(VertexAttribPointer);
-        GR_GL_GET_PROC(BindFragDataLocationIndexed);
-
-        // First look for GL3.0 FBO or GL_ARB_framebuffer_object (same since
-        // GL_ARB_framebuffer_object doesn't use ARB suffix.)
-        if (glVer > GR_GL_VER(3,0) || 
-            GrGLHasExtensionFromString("GL_ARB_framebuffer_object", extString)) {
-            GR_GL_GET_PROC(GenFramebuffers);
-            GR_GL_GET_PROC(GetFramebufferAttachmentParameteriv);
-            GR_GL_GET_PROC(GetRenderbufferParameteriv);
-            GR_GL_GET_PROC(BindFramebuffer);
-            GR_GL_GET_PROC(FramebufferTexture2D);
-            GR_GL_GET_PROC(CheckFramebufferStatus);
-            GR_GL_GET_PROC(DeleteFramebuffers);
-            GR_GL_GET_PROC(RenderbufferStorage);
-            GR_GL_GET_PROC(GenRenderbuffers);
-            GR_GL_GET_PROC(DeleteRenderbuffers);
-            GR_GL_GET_PROC(FramebufferRenderbuffer);
-            GR_GL_GET_PROC(BindRenderbuffer);
-            GR_GL_GET_PROC(RenderbufferStorageMultisample);
-            GR_GL_GET_PROC(BlitFramebuffer);
-        } else if (GrGLHasExtensionFromString("GL_EXT_framebuffer_object",
-                   extString)) {
-            GR_GL_GET_PROC_SUFFIX(GenFramebuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetFramebufferAttachmentParameteriv, EXT);
-            GR_GL_GET_PROC_SUFFIX(GetRenderbufferParameteriv, EXT);
-            GR_GL_GET_PROC_SUFFIX(BindFramebuffer, EXT);
-            GR_GL_GET_PROC_SUFFIX(FramebufferTexture2D, EXT);
-            GR_GL_GET_PROC_SUFFIX(CheckFramebufferStatus, EXT);
-            GR_GL_GET_PROC_SUFFIX(DeleteFramebuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(RenderbufferStorage, EXT);
-            GR_GL_GET_PROC_SUFFIX(GenRenderbuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(DeleteRenderbuffers, EXT);
-            GR_GL_GET_PROC_SUFFIX(FramebufferRenderbuffer, EXT);
-            GR_GL_GET_PROC_SUFFIX(BindRenderbuffer, EXT);
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_multisample", extString)) {
-                GR_GL_GET_PROC_SUFFIX(RenderbufferStorageMultisample, EXT);
-            }
-            if (GrGLHasExtensionFromString("GL_EXT_framebuffer_blit", extString)) {
-                GR_GL_GET_PROC_SUFFIX(BlitFramebuffer, EXT);
-            }
-        } else {
-            // we must have FBOs
-            delete interface;
-            return NULL;
-        }
-        GR_GL_GET_PROC(MapBuffer);
-        GR_GL_GET_PROC(UnmapBuffer);
-
-        interface->fBindingsExported = kDesktop_GrGLBinding;
-
-        return interface;
-    } else {
-        return NULL;
-    }
-}
diff --git a/src/gpu/win/SkNativeGLContext_win.cpp b/src/gpu/win/SkNativeGLContext_win.cpp
deleted file mode 100644
index 9650bc1..0000000
--- a/src/gpu/win/SkNativeGLContext_win.cpp
+++ /dev/null
@@ -1,137 +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 "gl/SkNativeGLContext.h"
-
-#define WIN32_LEAN_AND_MEAN
-#include <Windows.h>
-
-SkNativeGLContext::AutoContextRestore::AutoContextRestore() {
-    fOldHGLRC = wglGetCurrentContext();
-    fOldHDC = wglGetCurrentDC();
-}
-
-SkNativeGLContext::AutoContextRestore::~AutoContextRestore() {
-    wglMakeCurrent(fOldHDC, fOldHGLRC);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-ATOM SkNativeGLContext::gWC = 0;
-
-SkNativeGLContext::SkNativeGLContext()
-    : fWindow(NULL)
-    , fDeviceContext(NULL)
-    , fGlRenderContext(0) {
-}
-
-SkNativeGLContext::~SkNativeGLContext() {
-    this->destroyGLContext();
-}
-
-void SkNativeGLContext::destroyGLContext() {
-    if (fGlRenderContext) {
-        wglDeleteContext(fGlRenderContext);
-    }
-    if (fWindow && fDeviceContext) {
-        ReleaseDC(fWindow, fDeviceContext);
-    }
-    if (fWindow) {
-        DestroyWindow(fWindow);
-    }
-}
-
-const GrGLInterface* SkNativeGLContext::createGLContext() {
-    HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL);
-
-    if (!gWC) {
-        WNDCLASS wc;
-        wc.cbClsExtra = 0;
-        wc.cbWndExtra = 0;
-        wc.hbrBackground = NULL;
-        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
-        wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
-        wc.hInstance = hInstance;
-        wc.lpfnWndProc = (WNDPROC) DefWindowProc;
-        wc.lpszClassName = TEXT("Griffin");
-        wc.lpszMenuName = NULL;
-        wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
-
-        gWC = RegisterClass(&wc);
-        if (!gWC) {
-            SkDebugf("Could not register window class.\n");
-            return NULL;
-        }
-    }
-
-    if (!(fWindow = CreateWindow(TEXT("Griffin"),
-                                 TEXT("The Invisible Man"),
-                                 WS_OVERLAPPEDWINDOW,
-                                 0, 0, 1, 1,
-                                 NULL, NULL,
-                                 hInstance, NULL))) {
-        SkDebugf("Could not create window.\n");
-        return NULL;
-    }
-
-    if (!(fDeviceContext = GetDC(fWindow))) {
-        SkDebugf("Could not get device context.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    
-    PIXELFORMATDESCRIPTOR pfd;
-    ZeroMemory(&pfd, sizeof(pfd));
-    pfd.nSize = sizeof(pfd);
-    pfd.nVersion = 1;
-    pfd.dwFlags = PFD_SUPPORT_OPENGL;
-    pfd.iPixelType = PFD_TYPE_RGBA;
-    pfd.cColorBits = 32;
-    pfd.cDepthBits = 0;
-    pfd.cStencilBits = 0;
-    pfd.iLayerType = PFD_MAIN_PLANE;
-    
-    int pixelFormat = 0;
-    if (!(pixelFormat = ChoosePixelFormat(fDeviceContext, &pfd))) {
-        SkDebugf("No matching pixel format descriptor.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    
-    if (!SetPixelFormat(fDeviceContext, pixelFormat, &pfd)) {
-        SkDebugf("Could not set the pixel format %d.\n", pixelFormat);
-        this->destroyGLContext();
-        return NULL;
-    }
-    
-    if (!(fGlRenderContext = wglCreateContext(fDeviceContext))) {
-        SkDebugf("Could not create rendering context.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-
-    if (!(wglMakeCurrent(fDeviceContext, fGlRenderContext))) {
-        SkDebugf("Could not set the context.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-    const GrGLInterface* interface = GrGLCreateNativeInterface();
-    if (NULL == interface) {
-        SkDebugf("Could not create GL interface.\n");
-        this->destroyGLContext();
-        return NULL;
-    }
-
-    return interface;
-}
-
-void SkNativeGLContext::makeCurrent() const {
-    if (!wglMakeCurrent(fDeviceContext, fGlRenderContext)) {
-        SkDebugf("Could not create rendering context.\n");
-    }
-}
diff --git a/src/image/SkDataPixelRef.cpp b/src/image/SkDataPixelRef.cpp
new file mode 100644
index 0000000..980b4f1
--- /dev/null
+++ b/src/image/SkDataPixelRef.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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 "SkDataPixelRef.h"
+#include "SkData.h"
+#include "SkFlattenableBuffers.h"
+
+SkDataPixelRef::SkDataPixelRef(SkData* data) : fData(data) {
+    fData->ref();
+    this->setPreLocked(const_cast<void*>(fData->data()), NULL);
+}
+
+SkDataPixelRef::~SkDataPixelRef() {
+    fData->unref();
+}
+
+void* SkDataPixelRef::onLockPixels(SkColorTable** ct) {
+    *ct = NULL;
+    return const_cast<void*>(fData->data());
+}
+
+void SkDataPixelRef::onUnlockPixels() {
+    // nothing to do
+}
+
+void SkDataPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeFlattenable(fData);
+}
+
+SkDataPixelRef::SkDataPixelRef(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer, NULL) {
+    fData = (SkData*)buffer.readFlattenable();
+    this->setPreLocked(const_cast<void*>(fData->data()), NULL);
+}
diff --git a/src/image/SkDataPixelRef.h b/src/image/SkDataPixelRef.h
new file mode 100644
index 0000000..6b15802
--- /dev/null
+++ b/src/image/SkDataPixelRef.h
@@ -0,0 +1,35 @@
+/*
+ * 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 SkDataPixelRef_DEFINED
+#define SkDataPixelRef_DEFINED
+
+#include "SkPixelRef.h"
+
+class SkData;
+
+class SkDataPixelRef : public SkPixelRef {
+public:
+            SkDataPixelRef(SkData* data);
+    virtual ~SkDataPixelRef();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkDataPixelRef)
+
+protected:
+    virtual void* onLockPixels(SkColorTable**) SK_OVERRIDE;
+    virtual void onUnlockPixels() SK_OVERRIDE;
+
+    SkDataPixelRef(SkFlattenableReadBuffer& buffer);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+private:
+    SkData* fData;
+
+    typedef SkPixelRef INHERITED;
+};
+
+#endif
diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp
new file mode 100644
index 0000000..b388d58
--- /dev/null
+++ b/src/image/SkImage.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+
+SK_DEFINE_INST_COUNT(SkImage)
+
+static SkImage_Base* asIB(SkImage* image) {
+    return static_cast<SkImage_Base*>(image);
+}
+
+uint32_t SkImage::NextUniqueID() {
+    static int32_t gUniqueID;
+
+    // never return 0;
+    uint32_t id;
+    do {
+        id = sk_atomic_inc(&gUniqueID) + 1;
+    } while (0 == id);
+    return id;
+}
+
+void SkImage::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                   const SkPaint* paint) {
+    asIB(this)->onDraw(canvas, x, y, paint);
+}
diff --git a/src/image/SkImagePriv.cpp b/src/image/SkImagePriv.cpp
new file mode 100644
index 0000000..c44e439
--- /dev/null
+++ b/src/image/SkImagePriv.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 "SkImagePriv.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+
+SkBitmap::Config SkImageInfoToBitmapConfig(const SkImage::Info& info,
+                                           bool* isOpaque) {
+    switch (info.fColorType) {
+        case SkImage::kAlpha_8_ColorType:
+            switch (info.fAlphaType) {
+                case SkImage::kIgnore_AlphaType:
+                    // makes no sense
+                    return SkBitmap::kNo_Config;
+
+                case SkImage::kOpaque_AlphaType:
+                    *isOpaque = true;
+                    return SkBitmap::kA8_Config;
+
+                case SkImage::kPremul_AlphaType:
+                case SkImage::kUnpremul_AlphaType:
+                    *isOpaque = false;
+                    return SkBitmap::kA8_Config;
+            }
+            break;
+
+        case SkImage::kRGB_565_ColorType:
+            // we ignore fAlpahType, though some would not make sense
+            *isOpaque = true;
+            return SkBitmap::kRGB_565_Config;
+
+        case SkImage::kRGBA_8888_ColorType:
+        case SkImage::kBGRA_8888_ColorType:
+            // not supported yet
+            return SkBitmap::kNo_Config;
+
+        case SkImage::kPMColor_ColorType:
+            switch (info.fAlphaType) {
+                case SkImage::kIgnore_AlphaType:
+                case SkImage::kUnpremul_AlphaType:
+                    // not supported yet
+                    return SkBitmap::kNo_Config;
+                case SkImage::kOpaque_AlphaType:
+                    *isOpaque = true;
+                    return SkBitmap::kARGB_8888_Config;
+                case SkImage::kPremul_AlphaType:
+                    *isOpaque = false;
+                    return SkBitmap::kARGB_8888_Config;
+            }
+            break;
+    }
+    SkASSERT(!"how did we get here");
+    return SkBitmap::kNo_Config;
+}
+
+int SkImageBytesPerPixel(SkImage::ColorType ct) {
+    static const uint8_t gColorTypeBytesPerPixel[] = {
+        1,  // kAlpha_8_ColorType
+        2,  // kRGB_565_ColorType
+        4,  // kRGBA_8888_ColorType
+        4,  // kBGRA_8888_ColorType
+        4,  // kPMColor_ColorType
+    };
+
+    SkASSERT((size_t)ct < SK_ARRAY_COUNT(gColorTypeBytesPerPixel));
+    return gColorTypeBytesPerPixel[ct];
+}
+
+bool SkBitmapToImageInfo(const SkBitmap& bm, SkImage::Info* info) {
+    switch (bm.config()) {
+        case SkBitmap::kA8_Config:
+            info->fColorType = SkImage::kAlpha_8_ColorType;
+            break;
+
+        case SkBitmap::kRGB_565_Config:
+            info->fColorType = SkImage::kRGB_565_ColorType;
+            break;
+
+        case SkBitmap::kARGB_8888_Config:
+            info->fColorType = SkImage::kPMColor_ColorType;
+            break;
+
+        default:
+            return false;
+    }
+
+    info->fWidth = bm.width();
+    info->fHeight = bm.height();
+    info->fAlphaType = bm.isOpaque() ? SkImage::kOpaque_AlphaType :
+                                       SkImage::kPremul_AlphaType;
+    return true;
+}
+
+SkImage* SkNewImageFromBitmap(const SkBitmap& bm, bool canSharePixelRef) {
+    SkImage::Info info;
+    if (!SkBitmapToImageInfo(bm, &info)) {
+        return NULL;
+    }
+
+    SkImage* image = NULL;
+    if (canSharePixelRef || bm.isImmutable()) {
+        image = SkNewImageFromPixelRef(info, bm.pixelRef(), bm.rowBytes());
+    } else {
+        bm.lockPixels();
+        if (bm.getPixels()) {
+            image = SkImage::NewRasterCopy(info, bm.getPixels(), bm.rowBytes());
+        }
+        bm.unlockPixels();
+    }
+    return image;
+}
+
+static bool needs_layer(const SkPaint& paint) {
+    return  0xFF != paint.getAlpha() ||
+    paint.getColorFilter() ||
+    paint.getImageFilter() ||
+    SkXfermode::IsMode(paint.getXfermode(), SkXfermode::kSrcOver_Mode);
+}
+
+void SkImagePrivDrawPicture(SkCanvas* canvas, SkPicture* picture,
+                            SkScalar x, SkScalar y, const SkPaint* paint) {
+    int saveCount = canvas->getSaveCount();
+
+    if (paint && needs_layer(*paint)) {
+        SkRect bounds;
+        bounds.set(x, y,
+                   x + SkIntToScalar(picture->width()),
+                   y + SkIntToScalar(picture->height()));
+        canvas->saveLayer(&bounds, paint);
+        canvas->translate(x, y);
+    } else if (x || y) {
+        canvas->save();
+        canvas->translate(x, y);
+    }
+
+    canvas->drawPicture(*picture);
+    canvas->restoreToCount(saveCount);
+}
diff --git a/src/image/SkImagePriv.h b/src/image/SkImagePriv.h
new file mode 100644
index 0000000..3210cae
--- /dev/null
+++ b/src/image/SkImagePriv.h
@@ -0,0 +1,68 @@
+/*
+ * 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 SkImagePriv_DEFINED
+#define SkImagePriv_DEFINED
+
+#include "SkBitmap.h"
+#include "SkImage.h"
+
+class SkPicture;
+
+extern SkBitmap::Config SkImageInfoToBitmapConfig(const SkImage::Info&,
+                                                  bool* isOpaque);
+
+extern int SkImageBytesPerPixel(SkImage::ColorType);
+
+extern bool SkBitmapToImageInfo(const SkBitmap&, SkImage::Info*);
+
+// Call this if you explicitly want to use/share this pixelRef in the image
+extern SkImage* SkNewImageFromPixelRef(const SkImage::Info&, SkPixelRef*,
+                                       size_t rowBytes);
+
+/**
+ *  Examines the bitmap to decide if it can share the existing pixelRef, or
+ *  if it needs to make a deep-copy of the pixels. The bitmap's pixelref will
+ *  be shared if either the bitmap is marked as immutable, or canSharePixelRef
+ *  is true.
+ *
+ *  If the bitmap's config cannot be converted into a corresponding
+ *  SkImage::Info, or the bitmap's pixels cannot be accessed, this will return
+ *  NULL.
+ */
+extern SkImage* SkNewImageFromBitmap(const SkBitmap&, bool canSharePixelRef);
+
+extern void SkImagePrivDrawPicture(SkCanvas*, SkPicture*,
+                                   SkScalar x, SkScalar y, const SkPaint*);
+
+/**
+ *  Return an SkImage whose contents are those of the specified picture. Note:
+ *  The picture itself is unmodified, and may continue to be used for recording
+ */
+extern SkImage* SkNewImageFromPicture(const SkPicture*);
+
+static inline size_t SkImageMinRowBytes(const SkImage::Info& info) {
+    size_t rb = info.fWidth * SkImageBytesPerPixel(info.fColorType);
+    return SkAlign4(rb);
+}
+
+// Given an image created from SkNewImageFromBitmap, return its pixelref. This
+// may be called to see if the surface and the image share the same pixelref,
+// in which case the surface may need to perform a copy-on-write.
+extern SkPixelRef* SkBitmapImageGetPixelRef(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* rasterImage);
+
+// Update the texture wrapped by an image created with NewTexture. This
+// is called when a surface and image share the same GrTexture and the
+// surface needs to perform a copy-on-write
+extern void SkTextureImageSetTexture(SkImage* image, GrTexture* texture);
+
+#endif
diff --git a/src/image/SkImage_Base.h b/src/image/SkImage_Base.h
new file mode 100644
index 0000000..2687025
--- /dev/null
+++ b/src/image/SkImage_Base.h
@@ -0,0 +1,23 @@
+/*
+ * 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 SkImage_Base_DEFINED
+#define SkImage_Base_DEFINED
+
+#include "SkImage.h"
+
+class SkImage_Base : public SkImage {
+public:
+    SkImage_Base(int width, int height) : INHERITED(width, height) {}
+
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) = 0;
+
+private:
+    typedef SkImage INHERITED;
+};
+
+#endif
diff --git a/src/image/SkImage_Codec.cpp b/src/image/SkImage_Codec.cpp
new file mode 100644
index 0000000..9d81dc9
--- /dev/null
+++ b/src/image/SkImage_Codec.cpp
@@ -0,0 +1,66 @@
+/*
+ * 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 "SkImage_Base.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "../images/SkImageDecoder.h"
+
+class SkImage_Codec : public SkImage_Base {
+public:
+    static SkImage* NewEmpty();
+
+    SkImage_Codec(SkData* encodedData, int width, int height);
+    virtual ~SkImage_Codec();
+
+    virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) SK_OVERRIDE;
+
+private:
+    SkData*     fEncodedData;
+    SkBitmap    fBitmap;
+
+    typedef SkImage_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage_Codec::SkImage_Codec(SkData* data, int width, int height) : INHERITED(width, height) {
+    fEncodedData = data;
+    fEncodedData->ref();
+}
+
+SkImage_Codec::~SkImage_Codec() {
+    fEncodedData->unref();
+}
+
+void SkImage_Codec::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) {
+    if (!fBitmap.pixelRef()) {
+        if (!SkImageDecoder::DecodeMemory(fEncodedData->bytes(), fEncodedData->size(),
+                                          &fBitmap)) {
+            return;
+        }
+    }
+    canvas->drawBitmap(fBitmap, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage* SkImage::NewEncodedData(SkData* data) {
+    if (NULL == data) {
+        return NULL;
+    }
+
+    SkBitmap bitmap;
+    if (!SkImageDecoder::DecodeMemory(data->bytes(), data->size(), &bitmap,
+                                      SkBitmap::kNo_Config,
+                                      SkImageDecoder::kDecodeBounds_Mode)) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkImage_Codec, (data, bitmap.width(), bitmap.height()));
+}
diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp
new file mode 100644
index 0000000..c721077
--- /dev/null
+++ b/src/image/SkImage_Gpu.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "GrContext.h"
+#include "GrTexture.h"
+#include "SkGrPixelRef.h"
+
+class SkImage_Gpu : public SkImage_Base {
+public:
+    SK_DECLARE_INST_COUNT(SkImage_Gpu)
+
+    SkImage_Gpu(GrTexture*);
+    virtual ~SkImage_Gpu();
+
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*) SK_OVERRIDE;
+
+    GrTexture* getTexture() { return fTexture; }
+
+    void setTexture(GrTexture* texture);
+
+private:
+    GrTexture*  fTexture;
+    SkBitmap    fBitmap;
+
+    typedef SkImage_Base INHERITED;
+};
+
+SK_DEFINE_INST_COUNT(SkImage_Gpu)
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage_Gpu::SkImage_Gpu(GrTexture* texture)
+    : INHERITED(texture->width(), texture->height())
+    , fTexture(texture) {
+
+    SkASSERT(NULL != fTexture);
+    fTexture->ref();
+    fBitmap.setConfig(SkBitmap::kARGB_8888_Config, fTexture->width(), fTexture->height());
+    fBitmap.setPixelRef(new SkGrPixelRef(fTexture))->unref();
+}
+
+SkImage_Gpu::~SkImage_Gpu() {
+    SkSafeUnref(fTexture);
+}
+
+void SkImage_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                         const SkPaint* paint) {
+    canvas->drawBitmap(fBitmap, x, y, paint);
+}
+
+void SkImage_Gpu::setTexture(GrTexture* texture) {
+
+    if (texture == fTexture) {
+        return;
+    }
+
+    SkRefCnt_SafeAssign(fTexture, texture);
+    fBitmap.setPixelRef(new SkGrPixelRef(texture))->unref();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage* SkImage::NewTexture(GrTexture* texture) {
+    if (NULL == texture) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkImage_Gpu, (texture));
+}
+
+GrTexture* SkTextureImageGetTexture(SkImage* image) {
+    return ((SkImage_Gpu*)image)->getTexture();
+}
+
+void SkTextureImageSetTexture(SkImage* image, GrTexture* texture) {
+    ((SkImage_Gpu*)image)->setTexture(texture);
+}
diff --git a/src/image/SkImage_Picture.cpp b/src/image/SkImage_Picture.cpp
new file mode 100644
index 0000000..447299f
--- /dev/null
+++ b/src/image/SkImage_Picture.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkPicture.h"
+
+class SkImage_Picture : public SkImage_Base {
+public:
+    SkImage_Picture(SkPicture*);
+    virtual ~SkImage_Picture();
+
+    virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) SK_OVERRIDE;
+
+private:
+    SkPicture*  fPicture;
+
+    typedef SkImage_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage_Picture::SkImage_Picture(SkPicture* pict) : INHERITED(pict->width(), pict->height()) {
+    pict->endRecording();
+    pict->ref();
+    fPicture = pict;
+}
+
+SkImage_Picture::~SkImage_Picture() {
+    fPicture->unref();
+}
+
+void SkImage_Picture::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                             const SkPaint* paint) {
+    SkImagePrivDrawPicture(canvas, fPicture, x, y, paint);
+}
+
+SkImage* SkNewImageFromPicture(const SkPicture* srcPicture) {
+    /**
+     *  We want to snapshot the playback status of the picture, w/o affecting
+     *  its ability to continue recording (if needed).
+     *
+     *  Optimally this will shared as much data/buffers as it can with
+     *  srcPicture, and srcPicture will perform a copy-on-write as needed if it
+     *  needs to mutate them later on.
+     */
+    SkAutoTUnref<SkPicture> playback(SkNEW_ARGS(SkPicture, (*srcPicture)));
+
+    return SkNEW_ARGS(SkImage_Picture, (playback));
+}
diff --git a/src/image/SkImage_Raster.cpp b/src/image/SkImage_Raster.cpp
new file mode 100644
index 0000000..d4ed171
--- /dev/null
+++ b/src/image/SkImage_Raster.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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 "SkImage_Base.h"
+#include "SkImagePriv.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkDataPixelRef.h"
+
+class SkImage_Raster : public SkImage_Base {
+public:
+    static bool ValidArgs(const Info& info, size_t rowBytes) {
+        const int maxDimension = SK_MaxS32 >> 2;
+        const size_t kMaxPixelByteSize = SK_MaxS32;
+
+        if (info.fWidth < 0 || info.fHeight < 0) {
+            return false;
+        }
+        if (info.fWidth > maxDimension || info.fHeight > maxDimension) {
+            return false;
+        }
+        if ((unsigned)info.fColorType > (unsigned)kLastEnum_ColorType) {
+            return false;
+        }
+        if ((unsigned)info.fAlphaType > (unsigned)kLastEnum_AlphaType) {
+            return false;
+        }
+
+        bool isOpaque;
+        if (SkImageInfoToBitmapConfig(info, &isOpaque) == SkBitmap::kNo_Config) {
+            return false;
+        }
+
+        // TODO: check colorspace
+
+        if (rowBytes < SkImageMinRowBytes(info)) {
+            return false;
+        }
+
+        int64_t size = (int64_t)info.fHeight * rowBytes;
+        if (size > (int64_t)kMaxPixelByteSize) {
+            return false;
+        }
+        return true;
+    }
+
+    static SkImage* NewEmpty();
+
+    SkImage_Raster(const SkImage::Info&, SkData*, size_t rb);
+    virtual ~SkImage_Raster();
+
+    virtual void onDraw(SkCanvas*, SkScalar, SkScalar, const SkPaint*) SK_OVERRIDE;
+
+    // exposed for SkSurface_Raster via SkNewImageFromPixelRef
+    SkImage_Raster(const SkImage::Info&, SkPixelRef*, size_t rowBytes);
+
+    SkPixelRef* getPixelRef() const { return fBitmap.pixelRef(); }
+
+private:
+    SkImage_Raster() : INHERITED(0, 0) {}
+
+    SkBitmap    fBitmap;
+
+    typedef SkImage_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage* SkImage_Raster::NewEmpty() {
+    // Returns lazily created singleton
+    static SkImage* gEmpty;
+    if (NULL == gEmpty) {
+        gEmpty = SkNEW(SkImage_Raster);
+    }
+    gEmpty->ref();
+    return gEmpty;
+}
+
+SkImage_Raster::SkImage_Raster(const Info& info, SkData* data, size_t rowBytes)
+        : INHERITED(info.fWidth, info.fHeight) {
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    fBitmap.setConfig(config, info.fWidth, info.fHeight, rowBytes);
+    fBitmap.setPixelRef(SkNEW_ARGS(SkDataPixelRef, (data)))->unref();
+    fBitmap.setIsOpaque(isOpaque);
+    fBitmap.setImmutable();
+}
+
+SkImage_Raster::SkImage_Raster(const Info& info, SkPixelRef* pr, size_t rowBytes)
+: INHERITED(info.fWidth, info.fHeight) {
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    fBitmap.setConfig(config, info.fWidth, info.fHeight, rowBytes);
+    fBitmap.setPixelRef(pr);
+    fBitmap.setIsOpaque(isOpaque);
+    fBitmap.setImmutable();
+}
+
+SkImage_Raster::~SkImage_Raster() {}
+
+void SkImage_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y, const SkPaint* paint) {
+    canvas->drawBitmap(fBitmap, x, y, paint);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkImage* SkImage::NewRasterCopy(const SkImage::Info& info, const void* pixels, size_t rowBytes) {
+    if (!SkImage_Raster::ValidArgs(info, rowBytes)) {
+        return NULL;
+    }
+    if (0 == info.fWidth && 0 == info.fHeight) {
+        return SkImage_Raster::NewEmpty();
+    }
+    // check this after empty-check
+    if (NULL == pixels) {
+        return NULL;
+    }
+
+    // Here we actually make a copy of the caller's pixel data
+    SkAutoDataUnref data(SkData::NewWithCopy(pixels, info.fHeight * rowBytes));
+    return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes));
+}
+
+
+SkImage* SkImage::NewRasterData(const SkImage::Info& info, SkData* pixelData, size_t rowBytes) {
+    if (!SkImage_Raster::ValidArgs(info, rowBytes)) {
+        return NULL;
+    }
+    if (0 == info.fWidth && 0 == info.fHeight) {
+        return SkImage_Raster::NewEmpty();
+    }
+    // check this after empty-check
+    if (NULL == pixelData) {
+        return NULL;
+    }
+
+    // did they give us enough data?
+    size_t size = info.fHeight * rowBytes;
+    if (pixelData->size() < size) {
+        return NULL;
+    }
+
+    SkAutoDataUnref data(pixelData);
+    return SkNEW_ARGS(SkImage_Raster, (info, data, rowBytes));
+}
+
+SkImage* SkNewImageFromPixelRef(const SkImage::Info& info, SkPixelRef* pr,
+                                size_t rowBytes) {
+    return SkNEW_ARGS(SkImage_Raster, (info, pr, rowBytes));
+}
+
+SkPixelRef* SkBitmapImageGetPixelRef(SkImage* image) {
+    return ((SkImage_Raster*)image)->getPixelRef();
+}
diff --git a/src/image/SkSurface.cpp b/src/image/SkSurface.cpp
new file mode 100644
index 0000000..37995b1
--- /dev/null
+++ b/src/image/SkSurface.cpp
@@ -0,0 +1,133 @@
+/*
+ * 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 "SkSurface_Base.h"
+#include "SkImagePriv.h"
+#include "SkCanvas.h"
+
+SK_DEFINE_INST_COUNT(SkSurface)
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkSurface_Base::installIntoCanvasForDirtyNotification() {
+    if (fCachedCanvas) {
+        fCachedCanvas->setSurfaceBase(this);
+    }
+}
+
+SkSurface_Base::SkSurface_Base(int width, int height) : INHERITED(width, height) {
+    fCachedCanvas = NULL;
+    fCachedImage = NULL;
+}
+
+SkSurface_Base::~SkSurface_Base() {
+    // in case the canvas outsurvives us, we null the callback
+    if (fCachedCanvas) {
+        fCachedCanvas->setSurfaceBase(NULL);
+    }
+
+    SkSafeUnref(fCachedImage);
+    SkSafeUnref(fCachedCanvas);
+}
+
+void SkSurface_Base::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                            const SkPaint* paint) {
+    SkImage* image = this->newImageShapshot();
+    if (image) {
+        image->draw(canvas, x, y, paint);
+        image->unref();
+    }
+}
+
+SkCanvas* SkSurface_Base::getCachedCanvas() {
+    if (NULL == fCachedCanvas) {
+        fCachedCanvas = this->onNewCanvas();
+        this->installIntoCanvasForDirtyNotification();
+    }
+    return fCachedCanvas;
+}
+
+SkImage* SkSurface_Base::getCachedImage() {
+    if (NULL == fCachedImage) {
+        fCachedImage = this->onNewImageShapshot();
+        this->installIntoCanvasForDirtyNotification();
+    }
+    return fCachedImage;
+}
+
+void SkSurface_Base::aboutToDraw(SkCanvas* canvas) {
+    this->dirtyGenerationID();
+
+    if (canvas) {
+        SkASSERT(canvas == fCachedCanvas);
+        SkASSERT(canvas->getSurfaceBase() == this);
+        canvas->setSurfaceBase(NULL);
+    }
+
+    if (fCachedImage) {
+        // the surface may need to fork its backend, if its sharing it with
+        // the cached image. Note: we only call if there is an outstanding owner
+        // on the image (besides us).
+        if (fCachedImage->getRefCnt() > 1) {
+            this->onCopyOnWrite(fCachedImage, canvas);
+        }
+
+        // regardless of copy-on-write, we must drop our cached image now, so
+        // that the next request will get our new contents.
+        fCachedImage->unref();
+        fCachedImage = NULL;
+    }
+}
+
+uint32_t SkSurface_Base::newGenerationID() {
+    this->installIntoCanvasForDirtyNotification();
+
+    static int32_t gID;
+    return sk_atomic_inc(&gID) + 1;
+}
+
+static SkSurface_Base* asSB(SkSurface* surface) {
+    return static_cast<SkSurface_Base*>(surface);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSurface::SkSurface(int width, int height) : fWidth(width), fHeight(height) {
+    SkASSERT(width >= 0);
+    SkASSERT(height >= 0);
+    fGenerationID = 0;
+}
+
+uint32_t SkSurface::generationID() {
+    if (0 == fGenerationID) {
+        fGenerationID = asSB(this)->newGenerationID();
+    }
+    return fGenerationID;
+}
+
+void SkSurface::notifyContentChanged() {
+    asSB(this)->aboutToDraw(NULL);
+}
+
+SkCanvas* SkSurface::getCanvas() {
+    return asSB(this)->getCachedCanvas();
+}
+
+SkImage* SkSurface::newImageShapshot() {
+    SkImage* image = asSB(this)->getCachedImage();
+    SkSafeRef(image);   // the caller will call unref() to balance this
+    return image;
+}
+
+SkSurface* SkSurface::newSurface(const SkImage::Info& info) {
+    return asSB(this)->onNewSurface(info);
+}
+
+void SkSurface::draw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                     const SkPaint* paint) {
+    return asSB(this)->onDraw(canvas, x, y, paint);
+}
diff --git a/src/image/SkSurface_Base.h b/src/image/SkSurface_Base.h
new file mode 100644
index 0000000..b9c4890
--- /dev/null
+++ b/src/image/SkSurface_Base.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSurface_Base_DEFINED
+#define SkSurface_Base_DEFINED
+
+#include "SkSurface.h"
+
+class SkSurface_Base : public SkSurface {
+public:
+    SkSurface_Base(int width, int height);
+    virtual ~SkSurface_Base();
+
+    /**
+     *  Allocate a canvas that will draw into this surface. We will cache this
+     *  canvas, to return the same object to the caller multiple times. We
+     *  take ownership, and will call unref() on the canvas when we go out of
+     *  scope.
+     */
+    virtual SkCanvas* onNewCanvas() = 0;
+
+    virtual SkSurface* onNewSurface(const SkImage::Info&) = 0;
+
+    /**
+     *  Allocate an SkImage that represents the current contents of the surface.
+     *  This needs to be able to outlive the surface itself (if need be), and
+     *  must faithfully represent the current contents, even if the surface
+     *  is chaged after this calle (e.g. it is drawn to via its canvas).
+     */
+    virtual SkImage* onNewImageShapshot() = 0;
+
+    /**
+     *  Default implementation:
+     *
+     *  image = this->newImageSnapshot();
+     *  if (image) {
+     *      image->draw(canvas, ...);
+     *      image->unref();
+     *  }
+     */
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y, const SkPaint*);
+
+    /**
+     *  If the surface is about to change, we call this so that our subclass
+     *  can optionally fork their backend (copy-on-write) in case it was
+     *  being shared with the cachedImage.
+     *
+     *  The default implementation does nothing.
+     */
+    virtual void onCopyOnWrite(SkImage* cachedImage, SkCanvas*) = 0;
+
+    inline SkCanvas* getCachedCanvas();
+    inline SkImage* getCachedImage();
+
+    // called by SkSurface to compute a new genID
+    uint32_t newGenerationID();
+
+private:
+    SkCanvas*   fCachedCanvas;
+    SkImage*    fCachedImage;
+
+    void aboutToDraw(SkCanvas*);
+    friend class SkCanvas;
+    friend class SkSurface;
+
+    inline void installIntoCanvasForDirtyNotification();
+
+    typedef SkSurface INHERITED;
+};
+
+#endif
diff --git a/src/image/SkSurface_Gpu.cpp b/src/image/SkSurface_Gpu.cpp
new file mode 100644
index 0000000..c497577
--- /dev/null
+++ b/src/image/SkSurface_Gpu.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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 "SkSurface_Base.h"
+#include "SkImagePriv.h"
+#include "SkCanvas.h"
+#include "SkGpuDevice.h"
+
+class SkSurface_Gpu : public SkSurface_Base {
+public:
+    SK_DECLARE_INST_COUNT(SkSurface_Gpu)
+
+    SkSurface_Gpu(GrContext*, const SkImage::Info&, int sampleCount);
+    SkSurface_Gpu(GrContext*, GrRenderTarget*);
+    virtual ~SkSurface_Gpu();
+
+    virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
+    virtual SkSurface* onNewSurface(const SkImage::Info&) SK_OVERRIDE;
+    virtual SkImage* onNewImageShapshot() SK_OVERRIDE;
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
+                        const SkPaint*) SK_OVERRIDE;
+    virtual void onCopyOnWrite(SkImage*, SkCanvas*) SK_OVERRIDE;
+
+private:
+    SkGpuDevice* fDevice;
+
+    typedef SkSurface_Base INHERITED;
+};
+
+SK_DEFINE_INST_COUNT(SkSurface_Gpu)
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSurface_Gpu::SkSurface_Gpu(GrContext* ctx, const SkImage::Info& info,
+                             int sampleCount)
+        : INHERITED(info.fWidth, info.fHeight) {
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    fDevice = SkNEW_ARGS(SkGpuDevice, (ctx, config, info.fWidth, info.fHeight, sampleCount));
+
+    if (!isOpaque) {
+        fDevice->clear(0x0);
+    }
+}
+
+SkSurface_Gpu::SkSurface_Gpu(GrContext* ctx, GrRenderTarget* renderTarget)
+        : INHERITED(renderTarget->width(), renderTarget->height()) {
+    fDevice = SkNEW_ARGS(SkGpuDevice, (ctx, renderTarget));
+
+    if (kRGB_565_GrPixelConfig != renderTarget->config()) {
+        fDevice->clear(0x0);
+    }
+}
+
+SkSurface_Gpu::~SkSurface_Gpu() {
+    SkSafeUnref(fDevice);
+}
+
+SkCanvas* SkSurface_Gpu::onNewCanvas() {
+    return SkNEW_ARGS(SkCanvas, (fDevice));
+}
+
+SkSurface* SkSurface_Gpu::onNewSurface(const SkImage::Info& info) {
+    GrRenderTarget* rt = (GrRenderTarget*) fDevice->accessRenderTarget();
+    int sampleCount = rt->numSamples();
+    return SkSurface::NewRenderTarget(fDevice->context(), info, sampleCount);
+}
+
+SkImage* SkSurface_Gpu::onNewImageShapshot() {
+
+    GrRenderTarget* rt = (GrRenderTarget*) fDevice->accessRenderTarget();
+
+    return SkImage::NewTexture(rt->asTexture());
+}
+
+void SkSurface_Gpu::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                              const SkPaint* paint) {
+    canvas->drawBitmap(fDevice->accessBitmap(false), x, y, paint);
+}
+
+// Copy the contents of the SkGpuDevice into a new texture and give that
+// texture to the SkImage. Note that this flushes the SkGpuDevice but
+// doesn't force an OpenGL flush.
+void SkSurface_Gpu::onCopyOnWrite(SkImage* image, SkCanvas* canvas) {
+    GrRenderTarget* rt = (GrRenderTarget*) fDevice->accessRenderTarget();
+
+    // are we sharing our render target with the image?
+    if (rt->asTexture() == SkTextureImageGetTexture(image)) {
+        GrTextureDesc desc;
+        // copyTexture requires a render target as the destination
+        desc.fFlags = kRenderTarget_GrTextureFlagBit;
+        desc.fWidth = fDevice->width();
+        desc.fHeight = fDevice->height();
+        desc.fConfig = SkBitmapConfig2GrPixelConfig(fDevice->config());
+        desc.fSampleCnt = 0;
+
+        SkAutoTUnref<GrTexture> tex(fDevice->context()->createUncachedTexture(desc, NULL, 0));
+        if (NULL == tex) {
+            SkTextureImageSetTexture(image, NULL);
+            return;
+        }
+
+        fDevice->context()->copyTexture(rt->asTexture(), tex->asRenderTarget());
+
+        SkTextureImageSetTexture(image, tex);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSurface* SkSurface::NewRenderTargetDirect(GrContext* ctx,
+                                            GrRenderTarget* target) {
+    if (NULL == ctx || NULL == target) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkSurface_Gpu, (ctx, target));
+}
+
+SkSurface* SkSurface::NewRenderTarget(GrContext* ctx, const SkImage::Info& info, int sampleCount) {
+    if (NULL == ctx) {
+        return NULL;
+    }
+
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = info.fWidth;
+    desc.fHeight = info.fHeight;
+    desc.fConfig = SkBitmapConfig2GrPixelConfig(config);
+    desc.fSampleCnt = sampleCount;
+
+    SkAutoTUnref<GrTexture> tex(ctx->createUncachedTexture(desc, NULL, 0));
+    if (NULL == tex) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkSurface_Gpu, (ctx, tex->asRenderTarget()));
+}
diff --git a/src/image/SkSurface_Picture.cpp b/src/image/SkSurface_Picture.cpp
new file mode 100644
index 0000000..ba3ee63
--- /dev/null
+++ b/src/image/SkSurface_Picture.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 "SkSurface_Base.h"
+#include "SkCanvas.h"
+#include "SkImagePriv.h"
+#include "SkPicture.h"
+
+/**
+ *  What does it mean to ask for more than one canvas from a picture?
+ *  How do we return an Image and then "continue" recording?
+ */
+class SkSurface_Picture : public SkSurface_Base {
+public:
+    SkSurface_Picture(int width, int height);
+    virtual ~SkSurface_Picture();
+
+    virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
+    virtual SkSurface* onNewSurface(const SkImage::Info&) SK_OVERRIDE;
+    virtual SkImage* onNewImageShapshot() SK_OVERRIDE;
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
+                        const SkPaint*) SK_OVERRIDE;
+    virtual void onCopyOnWrite(SkImage*, SkCanvas*) SK_OVERRIDE;
+
+private:
+    SkPicture*  fPicture;
+    SkPicture*  fRecordingPicture;
+
+    typedef SkSurface_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSurface_Picture::SkSurface_Picture(int width, int height) : INHERITED(width, height) {
+    fPicture = NULL;
+}
+
+SkSurface_Picture::~SkSurface_Picture() {
+    SkSafeUnref(fPicture);
+}
+
+SkCanvas* SkSurface_Picture::onNewCanvas() {
+    if (!fPicture) {
+        fPicture = SkNEW(SkPicture);
+    }
+    SkCanvas* canvas = fPicture->beginRecording(this->width(), this->height());
+    canvas->ref();  // our caller will call unref()
+    return canvas;
+}
+
+SkSurface* SkSurface_Picture::onNewSurface(const SkImage::Info& info) {
+    return SkSurface::NewPicture(info.fWidth, info.fHeight);
+}
+
+SkImage* SkSurface_Picture::onNewImageShapshot() {
+    if (fPicture) {
+        return SkNewImageFromPicture(fPicture);
+    } else {
+        SkImage::Info info;
+        info.fWidth = info.fHeight = 0;
+        info.fColorType = SkImage::kPMColor_ColorType;
+        info.fAlphaType = SkImage::kOpaque_AlphaType;
+        return SkImage::NewRasterCopy(info, NULL, 0);
+    }
+}
+
+void SkSurface_Picture::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                               const SkPaint* paint) {
+    if (!fPicture) {
+        return;
+    }
+    SkImagePrivDrawPicture(canvas, fPicture, x, y, paint);
+}
+
+void SkSurface_Picture::onCopyOnWrite(SkImage* cachedImage, SkCanvas*) {
+    // We always spawn a copy of the recording picture when we
+    // are asked for a snapshot, so we never need to do anything here.
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+
+SkSurface* SkSurface::NewPicture(int width, int height) {
+    if ((width | height) < 0) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkSurface_Picture, (width, height));
+}
diff --git a/src/image/SkSurface_Raster.cpp b/src/image/SkSurface_Raster.cpp
new file mode 100644
index 0000000..6e972dd
--- /dev/null
+++ b/src/image/SkSurface_Raster.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 "SkSurface_Base.h"
+#include "SkImagePriv.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkMallocPixelRef.h"
+
+static const size_t kIgnoreRowBytesValue = (size_t)~0;
+
+class SkSurface_Raster : public SkSurface_Base {
+public:
+    static bool Valid(const SkImage::Info&, size_t rb = kIgnoreRowBytesValue);
+
+    SkSurface_Raster(const SkImage::Info&, void*, size_t rb);
+    SkSurface_Raster(const SkImage::Info&, SkPixelRef*, size_t rb);
+
+    virtual SkCanvas* onNewCanvas() SK_OVERRIDE;
+    virtual SkSurface* onNewSurface(const SkImage::Info&) SK_OVERRIDE;
+    virtual SkImage* onNewImageShapshot() SK_OVERRIDE;
+    virtual void onDraw(SkCanvas*, SkScalar x, SkScalar y,
+                        const SkPaint*) SK_OVERRIDE;
+    virtual void onCopyOnWrite(SkImage*, SkCanvas*) SK_OVERRIDE;
+
+private:
+    SkBitmap    fBitmap;
+    bool        fWeOwnThePixels;
+
+    typedef SkSurface_Base INHERITED;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
+bool SkSurface_Raster::Valid(const SkImage::Info& info, size_t rowBytes) {
+    static const size_t kMaxTotalSize = SK_MaxS32;
+
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    int shift = 0;
+    switch (config) {
+        case SkBitmap::kA8_Config:
+            shift = 0;
+            break;
+        case SkBitmap::kRGB_565_Config:
+            shift = 1;
+            break;
+        case SkBitmap::kARGB_8888_Config:
+            shift = 2;
+            break;
+        default:
+            return false;
+    }
+
+    // TODO: examine colorspace
+
+    if (kIgnoreRowBytesValue == rowBytes) {
+        return true;
+    }
+
+    uint64_t minRB = (uint64_t)info.fWidth << shift;
+    if (minRB > rowBytes) {
+        return false;
+    }
+
+    size_t alignedRowBytes = rowBytes >> shift << shift;
+    if (alignedRowBytes != rowBytes) {
+        return false;
+    }
+
+    uint64_t size = (uint64_t)info.fHeight * rowBytes;
+    if (size > kMaxTotalSize) {
+        return false;
+    }
+
+    return true;
+}
+
+SkSurface_Raster::SkSurface_Raster(const SkImage::Info& info, void* pixels, size_t rb)
+        : INHERITED(info.fWidth, info.fHeight) {
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    fBitmap.setConfig(config, info.fWidth, info.fHeight, rb);
+    fBitmap.setPixels(pixels);
+    fBitmap.setIsOpaque(isOpaque);
+    fWeOwnThePixels = false;    // We are "Direct"
+}
+
+SkSurface_Raster::SkSurface_Raster(const SkImage::Info& info, SkPixelRef* pr, size_t rb)
+        : INHERITED(info.fWidth, info.fHeight) {
+    bool isOpaque;
+    SkBitmap::Config config = SkImageInfoToBitmapConfig(info, &isOpaque);
+
+    fBitmap.setConfig(config, info.fWidth, info.fHeight, rb);
+    fBitmap.setPixelRef(pr);
+    fBitmap.setIsOpaque(isOpaque);
+    fWeOwnThePixels = true;
+
+    if (!isOpaque) {
+        fBitmap.eraseColor(SK_ColorTRANSPARENT);
+    }
+}
+
+SkCanvas* SkSurface_Raster::onNewCanvas() {
+    return SkNEW_ARGS(SkCanvas, (fBitmap));
+}
+
+SkSurface* SkSurface_Raster::onNewSurface(const SkImage::Info& info) {
+    return SkSurface::NewRaster(info);
+}
+
+void SkSurface_Raster::onDraw(SkCanvas* canvas, SkScalar x, SkScalar y,
+                              const SkPaint* paint) {
+    canvas->drawBitmap(fBitmap, x, y, paint);
+}
+
+SkImage* SkSurface_Raster::onNewImageShapshot() {
+    return SkNewImageFromBitmap(fBitmap, fWeOwnThePixels);
+}
+
+void SkSurface_Raster::onCopyOnWrite(SkImage* image, SkCanvas* canvas) {
+    // are we sharing pixelrefs with the image?
+    if (SkBitmapImageGetPixelRef(image) == fBitmap.pixelRef()) {
+        SkASSERT(fWeOwnThePixels);
+        SkBitmap prev(fBitmap);
+        prev.deepCopyTo(&fBitmap, prev.config());
+        // Now fBitmap is a deep copy of itself (and therefore different from
+        // what is being used by the image. Next we update the canvas to use
+        // this as its backend, so we can't modify the image's pixels anymore.
+        canvas->getDevice()->replaceBitmapBackendForRasterSurface(fBitmap);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkSurface* SkSurface::NewRasterDirect(const SkImage::Info& info, void* pixels, size_t rowBytes) {
+    if (!SkSurface_Raster::Valid(info, rowBytes)) {
+        return NULL;
+    }
+    if (NULL == pixels) {
+        return NULL;
+    }
+
+    return SkNEW_ARGS(SkSurface_Raster, (info, pixels, rowBytes));
+}
+
+SkSurface* SkSurface::NewRaster(const SkImage::Info& info) {
+    if (!SkSurface_Raster::Valid(info)) {
+        return NULL;
+    }
+
+    static const size_t kMaxTotalSize = SK_MaxS32;
+    size_t rowBytes = SkImageMinRowBytes(info);
+    uint64_t size64 = (uint64_t)info.fHeight * rowBytes;
+    if (size64 > kMaxTotalSize) {
+        return NULL;
+    }
+
+    size_t size = (size_t)size64;
+    void* pixels = sk_malloc_throw(size);
+    if (NULL == pixels) {
+        return NULL;
+    }
+
+    SkAutoTUnref<SkPixelRef> pr(SkNEW_ARGS(SkMallocPixelRef, (pixels, size, NULL, true)));
+    return SkNEW_ARGS(SkSurface_Raster, (info, pr, rowBytes));
+}
diff --git a/src/images/SkBitmapFactory.cpp b/src/images/SkBitmapFactory.cpp
new file mode 100644
index 0000000..4e5d90b
--- /dev/null
+++ b/src/images/SkBitmapFactory.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "SkBitmapFactory.h"
+
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkImageDecoder.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+bool SkBitmapFactory::DecodeBitmap(SkBitmap* dst, const SkData* data, Constraints constraint) {
+    if (NULL == data || data->size() == 0 || dst == NULL) {
+        return false;
+    }
+
+    SkMemoryStream stream(data->data(), data->size());
+    SkAutoTDelete<SkImageDecoder> decoder (SkImageDecoder::Factory(&stream));
+    if (decoder.get() == NULL) {
+        return false;
+    }
+
+    SkBitmap tmp;
+    SkImageDecoder::Mode mode;
+    if (kDecodeBoundsOnly_Constraint == constraint) {
+        mode = SkImageDecoder::kDecodeBounds_Mode;
+    } else {
+        mode = SkImageDecoder::kDecodePixels_Mode;
+    }
+
+    if (decoder->decode(&stream, &tmp, mode)) {
+        tmp.swap(*dst);
+        return true;
+    } else {
+        return false;
+    }
+}
diff --git a/src/images/SkBitmap_RLEPixels.h b/src/images/SkBitmap_RLEPixels.h
deleted file mode 100644
index dfe83a2..0000000
--- a/src/images/SkBitmap_RLEPixels.h
+++ /dev/null
@@ -1,26 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkBitmap_RLEPixels_DEFINED
-#define SkBitmap_RLEPixels_DEFINED
-
-#include "SkChunkAlloc.h"
-
-class SkBitmap_RLEPixels {
-public:
-    SkBitmap_RLEPixels(int width, int height);
-    ~SkBitmap_RLEPixels();
-    
-    uint8_t* yptrs() const { return fYPtrs; }
-    uint8_t* allocChunk(size_t chunk);
-    
-private:
-    SkChunkAlloc    fChunk;
-    uint8_t**       fYPtrs;
-};
-
-#endif
diff --git a/src/images/SkCreateRLEPixelRef.cpp b/src/images/SkCreateRLEPixelRef.cpp
deleted file mode 100644
index 44652be..0000000
--- a/src/images/SkCreateRLEPixelRef.cpp
+++ /dev/null
@@ -1,127 +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 "SkChunkAlloc.h"
-#include "SkPackBits.h"
-#include "SkBitmap.h"
-#include "SkPixelRef.h"
-
-class RLEPixelRef : public SkPixelRef {
-public:
-    RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable);
-    virtual ~RLEPixelRef();
-    
-protected:
-    // overrides from SkPixelRef
-    virtual void* onLockPixels(SkColorTable**);
-    virtual void onUnlockPixels();
-    
-private:
-    SkBitmap::RLEPixels* fRLEPixels;
-    SkColorTable*        fCTable;
-};
-
-RLEPixelRef::RLEPixelRef(SkBitmap::RLEPixels* rlep, SkColorTable* ctable)
-        : SkPixelRef(NULL) {
-    fRLEPixels = rlep;  // we now own this ptr
-    fCTable = ctable;
-    SkSafeRef(ctable);
-}
-
-RLEPixelRef::~RLEPixelRef() {
-    SkDELETE(fRLEPixels);
-    SkSafeUnref(fCTable);
-}
-
-void* RLEPixelRef::onLockPixels(SkColorTable** ct) {
-    *ct = fCTable;
-    return fRLEPixels;
-}
-
-void RLEPixelRef::onUnlockPixels() {
-    // nothing to do
-}
-
-/////////////////////////////////////////////////////////////////////////////
-
-class ChunkRLEPixels : public SkBitmap::RLEPixels {
-public:
-    ChunkRLEPixels(int width, int height, size_t chunkSize)
-        : SkBitmap::RLEPixels(width, height), fStorage(chunkSize) {
-    }
-    
-    SkChunkAlloc fStorage;
-};
-
-SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src);
-SkPixelRef* SkCreateRLEPixelRef(const SkBitmap& src) {
-    
-    if (SkBitmap::kIndex8_Config != src.config() &&
-            SkBitmap::kA8_Config != src.config()) {
-        return NULL;
-    }
-    
-    size_t maxPacked = SkPackBits::ComputeMaxSize8(src.width());
-
-    // estimate the rle size based on the original size
-    size_t size = src.getSize() >> 3;
-    if (size < maxPacked) {
-        size = maxPacked;
-    }
-
-    ChunkRLEPixels* rlePixels = SkNEW_ARGS(ChunkRLEPixels,
-                                           (src.width(), src.height(), size));
-
-    uint8_t* dstRow = NULL;
-    size_t free = 0;
-    size_t totalPacked = 0;
-
-    for (int y = 0; y < src.height(); y++) {
-        const uint8_t* srcRow = src.getAddr8(0, y);
-        
-        if (free < maxPacked) {
-            dstRow = (uint8_t*)rlePixels->fStorage.allocThrow(size);
-            free = size;
-        }
-        size_t packedSize = SkPackBits::Pack8(srcRow, src.width(), dstRow);
-        SkASSERT(packedSize <= free);
-        rlePixels->setPackedAtY(y, dstRow);
-        
-        dstRow += packedSize;
-        free -= packedSize;
-        
-        totalPacked += packedSize;
-    }
-    
-//#ifdef SK_DEBUG
-#if 0
-    // test
-    uint8_t* buffer = new uint8_t[src.width()];
-    for (int y = 0; y < src.height(); y++) {
-        const uint8_t* srcRow = src.getAddr8(0, y);
-        SkPackBits::Unpack8(buffer, 0, src.width(), rlePixels->packedAtY(y));
-        int n = memcmp(buffer, srcRow, src.width());
-        if (n) {
-            SkDebugf("----- memcmp returned %d on line %d\n", n, y);
-        }
-        SkASSERT(n == 0);
-    }
-    delete[] buffer;
-
-    size_t totalAlloc = src.height() * sizeof(uint8_t*) + totalPacked;
-    
-    SkDebugf("--- RLE: orig [%d %d] %d, rle %d %d savings %g\n",
-             src.width(), src.height(), src.getSize(),
-             src.height() * sizeof(uint8_t*), totalPacked,
-             (float)totalAlloc / src.getSize());
-
-#endif
-
-    // transfer ownership of rlePixels to our pixelref
-    return SkNEW_ARGS(RLEPixelRef, (rlePixels, src.getColorTable()));
-}
-        
diff --git a/src/images/SkFDStream.cpp b/src/images/SkFDStream.cpp
index e1e214a..d38813c 100644
--- a/src/images/SkFDStream.cpp
+++ b/src/images/SkFDStream.cpp
@@ -57,7 +57,7 @@
 #endif
                 return 0;
             }
-            return size;
+            return (size_t) size;
         } else if (NULL == buffer) {        // skip
             off_t oldCurr = ::lseek(fFD, 0, SEEK_CUR);
             if (oldCurr < 0) {
@@ -74,7 +74,7 @@
                 return 0;   // error;
             }
             // return the actual amount we skipped
-            return newCurr - oldCurr;
+            return (size_t) (newCurr - oldCurr);
         } else {                            // read
             ssize_t actual = ::read(fFD, buffer, size);
             // our API can't return an error, so we return 0
@@ -89,4 +89,3 @@
     }
     return 0;
 }
-
diff --git a/src/images/SkFlipPixelRef.cpp b/src/images/SkFlipPixelRef.cpp
index e81c83c..11076cf 100644
--- a/src/images/SkFlipPixelRef.cpp
+++ b/src/images/SkFlipPixelRef.cpp
@@ -60,29 +60,6 @@
     fMutex.release();
 }
 
-void SkFlipPixelRef::flatten(SkFlattenableWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    
-    buffer.write32(fSize);
-    // only need to write page0
-    buffer.writePad(fPage0, fSize);
-}
-
-SkFlipPixelRef::SkFlipPixelRef(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer, NULL) {
-    fSize = buffer.readU32();
-    fStorage = sk_malloc_throw(fSize << 1);
-    fPage0 = fStorage;
-    fPage1 = (char*)fStorage + fSize;
-    buffer.read(fPage0, fSize);
-}
-
-SkPixelRef* SkFlipPixelRef::Create(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkFlipPixelRef, (buffer));
-}
-
-SK_DEFINE_PIXEL_REF_REGISTRAR(SkFlipPixelRef)
-
 ///////////////////////////////////////////////////////////////////////////////
 
 static void copyRect(const SkBitmap& dst, const SkIRect& rect,
diff --git a/src/images/SkImageDecoder.cpp b/src/images/SkImageDecoder.cpp
index c4eac9c..077aeed 100644
--- a/src/images/SkImageDecoder.cpp
+++ b/src/images/SkImageDecoder.cpp
@@ -14,6 +14,10 @@
 #include "SkTemplates.h"
 #include "SkCanvas.h"
 
+SK_DEFINE_INST_COUNT(SkImageDecoder::Peeker)
+SK_DEFINE_INST_COUNT(SkImageDecoder::Chooser)
+SK_DEFINE_INST_COUNT(SkImageDecoderFactory)
+
 SkVMMemoryReporter::~SkVMMemoryReporter() {
 }
 
diff --git a/src/images/SkImageDecoder_libbmp.cpp b/src/images/SkImageDecoder_libbmp.cpp
index 6f82def..5332949 100644
--- a/src/images/SkImageDecoder_libbmp.cpp
+++ b/src/images/SkImageDecoder_libbmp.cpp
@@ -27,7 +27,11 @@
     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
 };
 
-static SkImageDecoder* Factory(SkStream* stream) {
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(BMPImageDecoder);
+///////////////////////////////////////////////////////////////////////////////
+
+static SkImageDecoder* sk_libbmp_dfactory(SkStream* stream) {
     static const char kBmpMagic[] = { 'B', 'M' };
     
     size_t len = stream->getLength();
@@ -41,7 +45,7 @@
     return NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libbmp_dfactory);
 
 ///////////////////////////////////////////////////////////////////////////////
 
diff --git a/src/images/SkImageDecoder_libgif.cpp b/src/images/SkImageDecoder_libgif.cpp
index 2975478..53df37f 100644
--- a/src/images/SkImageDecoder_libgif.cpp
+++ b/src/images/SkImageDecoder_libgif.cpp
@@ -99,7 +99,11 @@
 
 void CheckFreeExtension(SavedImage* Image) {
     if (Image->ExtensionBlocks) {
+#if GIFLIB_MAJOR < 5
         FreeExtension(Image);
+#else
+        GifFreeExtensions(&Image->ExtensionBlockCount, &Image->ExtensionBlocks);
+#endif
     }
 }
 
@@ -151,7 +155,11 @@
 }
 
 bool SkGIFImageDecoder::onDecode(SkStream* sk_stream, SkBitmap* bm, Mode mode) {
+#if GIFLIB_MAJOR < 5
     GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc);
+#else
+    GifFileType* gif = DGifOpen(sk_stream, DecodeCallBackProc, NULL);
+#endif
     if (NULL == gif) {
         return error_return(gif, *bm, "DGifOpen");
     }
@@ -166,6 +174,9 @@
     int width, height;
     GifRecordType recType;
     GifByteType *extData;
+#if GIFLIB_MAJOR >= 5
+    int extFunction;
+#endif
     int transpIndex = -1;   // -1 means we don't have it (yet)
     
     do {
@@ -304,21 +315,35 @@
             } break;
             
         case EXTENSION_RECORD_TYPE:
+#if GIFLIB_MAJOR < 5
             if (DGifGetExtension(gif, &temp_save.Function,
                                  &extData) == GIF_ERROR) {
+#else
+            if (DGifGetExtension(gif, &extFunction, &extData) == GIF_ERROR) {
+#endif
                 return error_return(gif, *bm, "DGifGetExtension");
             }
 
             while (extData != NULL) {
                 /* Create an extension block with our data */
+#if GIFLIB_MAJOR < 5
                 if (AddExtensionBlock(&temp_save, extData[0],
                                       &extData[1]) == GIF_ERROR) {
+#else
+                if (GifAddExtensionBlock(&gif->ExtensionBlockCount,
+                                         &gif->ExtensionBlocks,
+                                         extFunction,
+                                         extData[0],
+                                         &extData[1]) == GIF_ERROR) {
+#endif
                     return error_return(gif, *bm, "AddExtensionBlock");
                 }
                 if (DGifGetExtensionNext(gif, &extData) == GIF_ERROR) {
                     return error_return(gif, *bm, "DGifGetExtensionNext");
                 }
+#if GIFLIB_MAJOR < 5
                 temp_save.Function = 0;
+#endif
             }
             break;
             
@@ -335,10 +360,12 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(GIFImageDecoder);
+///////////////////////////////////////////////////////////////////////////////
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* Factory(SkStream* stream) {
+static SkImageDecoder* sk_libgif_dfactory(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 ||
@@ -350,4 +377,4 @@
     return NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libgif_dfactory);
diff --git a/src/images/SkImageDecoder_libico.cpp b/src/images/SkImageDecoder_libico.cpp
index 25a5078..3dc6974 100644
--- a/src/images/SkImageDecoder_libico.cpp
+++ b/src/images/SkImageDecoder_libico.cpp
@@ -373,11 +373,13 @@
     *address = SkPreMultiplyARGB(alpha, red, green, blue);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(ICOImageDecoder);
 /////////////////////////////////////////////////////////////////////////////////////////
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* Factory(SkStream* stream) {
+static SkImageDecoder* sk_libico_dfactory(SkStream* stream) {
     // Check to see if the first four bytes are 0,0,1,0
     // FIXME: Is that required and sufficient?
     SkAutoMalloc autoMal(4);
@@ -392,5 +394,4 @@
     return SkNEW(SkICOImageDecoder);
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
-
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libico_dfactory);
diff --git a/src/images/SkImageDecoder_libjpeg.cpp b/src/images/SkImageDecoder_libjpeg.cpp
index 33f222c..2f72c2d 100644
--- a/src/images/SkImageDecoder_libjpeg.cpp
+++ b/src/images/SkImageDecoder_libjpeg.cpp
@@ -986,11 +986,14 @@
 };
 
 ///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(JPEGImageDecoder);
+DEFINE_ENCODER_CREATOR(JPEGImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* DFactory(SkStream* stream) {
-    static const char gHeader[] = { 0xFF, 0xD8, 0xFF };
+static SkImageDecoder* sk_libjpeg_dfactory(SkStream* stream) {
+    static const unsigned char gHeader[] = { 0xFF, 0xD8, 0xFF };
     static const size_t HEADER_SIZE = sizeof(gHeader);
 
     char buffer[HEADER_SIZE];
@@ -1005,9 +1008,10 @@
     return SkNEW(SkJPEGImageDecoder);
 }
 
-static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
+static SkImageEncoder* sk_libjpeg_efactory(SkImageEncoder::Type t) {
     return (SkImageEncoder::kJPEG_Type == t) ? SkNEW(SkJPEGImageEncoder) : NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(DFactory);
-static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(EFactory);
+
+static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libjpeg_dfactory);
+static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libjpeg_efactory);
diff --git a/src/images/SkImageDecoder_libpng.cpp b/src/images/SkImageDecoder_libpng.cpp
index 351b9cf..0b8b41a 100644
--- a/src/images/SkImageDecoder_libpng.cpp
+++ b/src/images/SkImageDecoder_libpng.cpp
@@ -17,6 +17,7 @@
 #include "SkStream.h"
 #include "SkTemplates.h"
 #include "SkUtils.h"
+#include "transform_scanline.h"
 
 extern "C" {
 #include "png.h"
@@ -761,7 +762,7 @@
             for (int i = 0; i < number_passes; i++) {
                 png_configure_decoder(png_ptr, &actualTop, i);
                 for (int j = 0; j < rect.fTop - actualTop; j++) {
-                    uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
+                    uint8_t* bmRow = (uint8_t*)decodedBitmap->getPixels();
                     png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
                 }
                 uint8_t* row = base;
@@ -785,7 +786,7 @@
             skip_src_rows(png_ptr, srcRow, sampler.srcY0());
 
             for (int i = 0; i < rect.fTop - actualTop; i++) {
-                uint8_t* bmRow = decodedBitmap->getAddr8(0, 0);
+                uint8_t* bmRow = (uint8_t*)decodedBitmap->getPixels();
                 png_read_rows(png_ptr, &bmRow, png_bytepp_NULL, 1);
             }
             for (int y = 0; y < height; y++) {
@@ -824,99 +825,6 @@
     }
 }
 
-typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
-                                        int width, char* SK_RESTRICT dst);
-
-static void transform_scanline_565(const char* SK_RESTRICT src, int width,
-                                   char* SK_RESTRICT dst) {
-    const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;    
-    for (int i = 0; i < width; i++) {
-        unsigned c = *srcP++;
-        *dst++ = SkPacked16ToR32(c);
-        *dst++ = SkPacked16ToG32(c);
-        *dst++ = SkPacked16ToB32(c);
-    }
-}
-
-static void transform_scanline_888(const char* SK_RESTRICT src, int width,
-                                   char* SK_RESTRICT dst) {
-    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;    
-    for (int i = 0; i < width; i++) {
-        SkPMColor c = *srcP++;
-        *dst++ = SkGetPackedR32(c);
-        *dst++ = SkGetPackedG32(c);
-        *dst++ = SkGetPackedB32(c);
-    }
-}
-
-static void transform_scanline_444(const char* SK_RESTRICT src, int width,
-                                   char* SK_RESTRICT dst) {
-    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;    
-    for (int i = 0; i < width; i++) {
-        SkPMColor16 c = *srcP++;
-        *dst++ = SkPacked4444ToR32(c);
-        *dst++ = SkPacked4444ToG32(c);
-        *dst++ = SkPacked4444ToB32(c);
-    }
-}
-
-static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
-                                    char* SK_RESTRICT dst) {
-    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
-    const SkUnPreMultiply::Scale* SK_RESTRICT table = 
-                                              SkUnPreMultiply::GetScaleTable();
-
-    for (int i = 0; i < width; i++) {
-        SkPMColor c = *srcP++;
-        unsigned a = SkGetPackedA32(c);
-        unsigned r = SkGetPackedR32(c);
-        unsigned g = SkGetPackedG32(c);
-        unsigned b = SkGetPackedB32(c);
-
-        if (0 != a && 255 != a) {
-            SkUnPreMultiply::Scale scale = table[a];
-            r = SkUnPreMultiply::ApplyScale(scale, r);
-            g = SkUnPreMultiply::ApplyScale(scale, g);
-            b = SkUnPreMultiply::ApplyScale(scale, b);
-        }
-        *dst++ = r;
-        *dst++ = g;
-        *dst++ = b;
-        *dst++ = a;
-    }
-}
-
-static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
-                                    char* SK_RESTRICT dst) {
-    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
-    const SkUnPreMultiply::Scale* SK_RESTRICT table = 
-                                              SkUnPreMultiply::GetScaleTable();
-
-    for (int i = 0; i < width; i++) {
-        SkPMColor16 c = *srcP++;
-        unsigned a = SkPacked4444ToA32(c);
-        unsigned r = SkPacked4444ToR32(c);
-        unsigned g = SkPacked4444ToG32(c);
-        unsigned b = SkPacked4444ToB32(c);
-
-        if (0 != a && 255 != a) {
-            SkUnPreMultiply::Scale scale = table[a];
-            r = SkUnPreMultiply::ApplyScale(scale, r);
-            g = SkUnPreMultiply::ApplyScale(scale, g);
-            b = SkUnPreMultiply::ApplyScale(scale, b);
-        }
-        *dst++ = r;
-        *dst++ = g;
-        *dst++ = b;
-        *dst++ = a;
-    }
-}
-
-static void transform_scanline_index8(const char* SK_RESTRICT src, int width,
-                                      char* SK_RESTRICT dst) {
-    memcpy(dst, src, width);
-}
-
 static transform_scanline_proc choose_proc(SkBitmap::Config config,
                                            bool hasAlpha) {
     // we don't care about search on alpha if we're kIndex8, since only the
@@ -935,7 +843,7 @@
         { SkBitmap::kARGB_8888_Config,  true,   transform_scanline_8888 },
         { SkBitmap::kARGB_4444_Config,  false,  transform_scanline_444 },
         { SkBitmap::kARGB_4444_Config,  true,   transform_scanline_4444 },
-        { SkBitmap::kIndex8_Config,     false,   transform_scanline_index8 },
+        { SkBitmap::kIndex8_Config,     false,  transform_scanline_memcpy },
     };
 
     for (int i = SK_ARRAY_COUNT(gMap) - 1; i >= 0; --i) {
@@ -1173,6 +1081,9 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(PNGImageDecoder);
+DEFINE_ENCODER_CREATOR(PNGImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
 
 #include "SkTRegistry.h"
 
diff --git a/src/images/SkImageDecoder_libwebp.cpp b/src/images/SkImageDecoder_libwebp.cpp
index 3ca1254..656ec3d 100644
--- a/src/images/SkImageDecoder_libwebp.cpp
+++ b/src/images/SkImageDecoder_libwebp.cpp
@@ -560,10 +560,13 @@
 
 
 ///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(WEBPImageDecoder);
+DEFINE_ENCODER_CREATOR(WEBPImageEncoder);
+///////////////////////////////////////////////////////////////////////////////
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* DFactory(SkStream* stream) {
+static SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
     int width, height, hasAlpha;
     if (!webp_parse_header(stream, &width, &height, &hasAlpha)) {
         return NULL;
@@ -573,17 +576,9 @@
     return SkNEW(SkWEBPImageDecoder);
 }
 
-SkImageDecoder* sk_libwebp_dfactory(SkStream* stream) {
-    return DFactory(stream);
-}
-
-static SkImageEncoder* EFactory(SkImageEncoder::Type t) {
+static SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
       return (SkImageEncoder::kWEBP_Type == t) ? SkNEW(SkWEBPImageEncoder) : NULL;
 }
 
-SkImageEncoder* sk_libwebp_efactory(SkImageEncoder::Type t) {
-    return EFactory(t);
-}
-
 static SkTRegistry<SkImageDecoder*, SkStream*> gDReg(sk_libwebp_dfactory);
 static SkTRegistry<SkImageEncoder*, SkImageEncoder::Type> gEReg(sk_libwebp_efactory);
diff --git a/src/images/SkImageDecoder_wbmp.cpp b/src/images/SkImageDecoder_wbmp.cpp
index 1ec82d9..2fd6c91 100644
--- a/src/images/SkImageDecoder_wbmp.cpp
+++ b/src/images/SkImageDecoder_wbmp.cpp
@@ -96,8 +96,6 @@
     }
 }
 
-#define SkAlign8(x)     (((x) + 7) & ~7)
-
 bool SkWBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* decodedBitmap,
                                   Mode mode)
 {
@@ -157,10 +155,12 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+DEFINE_DECODER_CREATOR(WBMPImageDecoder);
+///////////////////////////////////////////////////////////////////////////////
 
 #include "SkTRegistry.h"
 
-static SkImageDecoder* Factory(SkStream* stream) {
+static SkImageDecoder* sk_wbmp_dfactory(SkStream* stream) {
     wbmp_head   head;
 
     if (head.init(stream)) {
@@ -169,5 +169,4 @@
     return NULL;
 }
 
-static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
-
+static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_wbmp_dfactory);
diff --git a/src/images/SkImageEncoder.cpp b/src/images/SkImageEncoder.cpp
index e05a28c..4b52fcd 100644
--- a/src/images/SkImageEncoder.cpp
+++ b/src/images/SkImageEncoder.cpp
@@ -38,4 +38,3 @@
     SkAutoTDelete<SkImageEncoder> enc(SkImageEncoder::Create(t));
     return enc.get() && enc.get()->encodeStream(stream, bm, quality);
 }
-
diff --git a/src/images/SkImageRef.cpp b/src/images/SkImageRef.cpp
index 1d6b270..299166c 100644
--- a/src/images/SkImageRef.cpp
+++ b/src/images/SkImageRef.cpp
@@ -7,7 +7,7 @@
  */
 #include "SkImageRef.h"
 #include "SkBitmap.h"
-#include "SkFlattenable.h"
+#include "SkFlattenableBuffers.h"
 #include "SkImageDecoder.h"
 #include "SkStream.h"
 #include "SkTemplates.h"
@@ -15,14 +15,12 @@
 
 //#define DUMP_IMAGEREF_LIFECYCLE
 
-// can't be static, as SkImageRef_Pool needs to see it
-SK_DECLARE_GLOBAL_MUTEX(gImageRefMutex);
 
 ///////////////////////////////////////////////////////////////////////////////
 
 SkImageRef::SkImageRef(SkStream* stream, SkBitmap::Config config,
-                       int sampleSize)
-        : SkPixelRef(&gImageRefMutex), fErrorInDecoding(false) {
+                       int sampleSize, SkBaseMutex* mutex)
+        : SkPixelRef(mutex), fErrorInDecoding(false) {
     SkASSERT(stream);
     stream->ref();
     fStream = stream;
@@ -39,7 +37,6 @@
 }
 
 SkImageRef::~SkImageRef() {
-    SkASSERT(&gImageRefMutex == this->mutex());
 
 #ifdef DUMP_IMAGEREF_LIFECYCLE
     SkDebugf("delete ImageRef %p [%d] data=%d\n",
@@ -51,7 +48,7 @@
 }
 
 bool SkImageRef::getInfo(SkBitmap* bitmap) {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(this->mutex());
 
     if (!this->prepareBitmap(SkImageDecoder::kDecodeBounds_Mode)) {
         return false;
@@ -89,7 +86,6 @@
 }
 
 bool SkImageRef::prepareBitmap(SkImageDecoder::Mode mode) {
-    SkASSERT(&gImageRefMutex == this->mutex());
 
     if (fErrorInDecoding) {
         return false;
@@ -144,8 +140,6 @@
 }
 
 void* SkImageRef::onLockPixels(SkColorTable** ct) {
-    SkASSERT(&gImageRefMutex == this->mutex());
-
     if (NULL == fBitmap.getPixels()) {
         (void)this->prepareBitmap(SkImageDecoder::kDecodePixels_Mode);
     }
@@ -156,11 +150,6 @@
     return fBitmap.getPixels();
 }
 
-void SkImageRef::onUnlockPixels() {
-    // we're already have the mutex locked
-    SkASSERT(&gImageRefMutex == this->mutex());
-}
-
 size_t SkImageRef::ramUsed() const {
     size_t size = 0;
 
@@ -175,15 +164,15 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer, &gImageRefMutex), fErrorInDecoding(false) {
-    fConfig = (SkBitmap::Config)buffer.readU8();
-    fSampleSize = buffer.readU8();
+SkImageRef::SkImageRef(SkFlattenableReadBuffer& buffer, SkBaseMutex* mutex)
+        : INHERITED(buffer, mutex), fErrorInDecoding(false) {
+    fConfig = (SkBitmap::Config)buffer.readUInt();
+    fSampleSize = buffer.readInt();
     fDoDither = buffer.readBool();
 
-    size_t length = buffer.readU32();
+    size_t length = buffer.getArrayCount();
     fStream = SkNEW_ARGS(SkMemoryStream, (length));
-    buffer.read((void*)fStream->getMemoryBase(), length);
+    buffer.readByteArray((void*)fStream->getMemoryBase());
 
     fPrev = fNext = NULL;
     fFactory = NULL;
@@ -192,12 +181,9 @@
 void SkImageRef::flatten(SkFlattenableWriteBuffer& buffer) const {
     this->INHERITED::flatten(buffer);
 
-    buffer.write8(fConfig);
-    buffer.write8(fSampleSize);
+    buffer.writeUInt(fConfig);
+    buffer.writeInt(fSampleSize);
     buffer.writeBool(fDoDither);
-    size_t length = fStream->getLength();
-    buffer.write32(length);
     fStream->rewind();
-    buffer.readFromStream(fStream, length);
+    buffer.writeStream(fStream, fStream->getLength());
 }
-
diff --git a/src/images/SkImageRefPool.cpp b/src/images/SkImageRefPool.cpp
index c24dba0..0a3d7bf 100644
--- a/src/images/SkImageRefPool.cpp
+++ b/src/images/SkImageRefPool.cpp
@@ -56,7 +56,7 @@
 
 void SkImageRefPool::setRAMUsed(size_t limit) {
     SkImageRef* ref = fTail;
-    
+
     while (NULL != ref && fRAMUsed > limit) {
         // only purge it if its pixels are unlocked
         if (!ref->isLocked() && ref->fBitmap.getPixels()) {
@@ -71,7 +71,7 @@
                      ref->fBitmap.bytesPerPixel(),
                      (int)size, (int)fRAMUsed);
 #endif
-            
+
             // remember the bitmap config (don't call reset),
             // just clear the pixel memory
             ref->fBitmap.setPixels(NULL);
@@ -86,44 +86,44 @@
 void SkImageRefPool::addToHead(SkImageRef* ref) {
     ref->fNext = fHead;
     ref->fPrev = NULL;
-    
+
     if (fHead) {
         SkASSERT(NULL == fHead->fPrev);
         fHead->fPrev = ref;
     }
     fHead = ref;
-    
+
     if (NULL == fTail) {
         fTail = ref;
     }
     fCount += 1;
     SkASSERT(computeCount() == fCount);
-    
+
     fRAMUsed += ref->ramUsed();
 }
 
 void SkImageRefPool::addToTail(SkImageRef* ref) {
     ref->fNext = NULL;
     ref->fPrev = fTail;
-    
+
     if (fTail) {
         SkASSERT(NULL == fTail->fNext);
         fTail->fNext = ref;
     }
     fTail = ref;
-    
+
     if (NULL == fHead) {
         fHead = ref;
     }
     fCount += 1;
     SkASSERT(computeCount() == fCount);
-    
+
     fRAMUsed += ref->ramUsed();
 }
 
 void SkImageRefPool::detach(SkImageRef* ref) {
     SkASSERT(fCount > 0);
-    
+
     if (fHead == ref) {
         fHead = ref->fNext;
     }
@@ -136,12 +136,12 @@
     if (ref->fNext) {
         ref->fNext->fPrev = ref->fPrev;
     }
-    
+
     ref->fNext = ref->fPrev = NULL;
-    
+
     fCount -= 1;
     SkASSERT(computeCount() == fCount);
-    
+
     SkASSERT(fRAMUsed >= ref->ramUsed());
     fRAMUsed -= ref->ramUsed();
 }
@@ -149,23 +149,23 @@
 int SkImageRefPool::computeCount() const {
     SkImageRef* ref = fHead;
     int count = 0;
-    
+
     while (ref != NULL) {
         count += 1;
         ref = ref->fNext;
     }
-    
+
 #ifdef SK_DEBUG
     ref = fTail;
     int count2 = 0;
-    
+
     while (ref != NULL) {
         count2 += 1;
         ref = ref->fPrev;
     }
     SkASSERT(count2 == count);
 #endif
-    
+
     return count;
 }
 
@@ -177,17 +177,16 @@
 #if defined(SK_DEBUG) || defined(DUMP_IMAGEREF_LIFECYCLE)
     SkDebugf("ImagePool dump: bugdet: %d used: %d count: %d\n",
              (int)fRAMBudget, (int)fRAMUsed, fCount);
-    
+
     SkImageRef* ref = fHead;
-    
+
     while (ref != NULL) {
         SkDebugf("  [%3d %3d %d] ram=%d data=%d locked=%d %s\n", ref->fBitmap.width(),
                  ref->fBitmap.height(), ref->fBitmap.config(),
                  ref->ramUsed(), (int)ref->fStream->getLength(),
                  ref->isLocked(), ref->getURI());
-        
+
         ref = ref->fNext;
     }
 #endif
 }
-
diff --git a/src/images/SkImageRefPool.h b/src/images/SkImageRefPool.h
index d29b8db..1e74a6d 100644
--- a/src/images/SkImageRefPool.h
+++ b/src/images/SkImageRefPool.h
@@ -17,34 +17,33 @@
 public:
     SkImageRefPool();
     ~SkImageRefPool();
-    
+
     size_t  getRAMBudget() const { return fRAMBudget; }
     void    setRAMBudget(size_t);
-    
+
     size_t  getRAMUsed() const { return fRAMUsed; }
     void    setRAMUsed(size_t limit);
-    
+
     void addToHead(SkImageRef*);
     void addToTail(SkImageRef*);
     void detach(SkImageRef*);
 
     void dump() const;
-    
+
 private:
     size_t fRAMBudget;
     size_t fRAMUsed;
-    
+
     int         fCount;
     SkImageRef* fHead, *fTail;
-    
+
     int computeCount() const;
-    
+
     friend class SkImageRef_GlobalPool;
-    
+
     void justAddedPixels(SkImageRef*);
     void canLosePixels(SkImageRef*);
     void purgeIfNeeded();
 };
 
 #endif
-
diff --git a/src/images/SkImageRef_GlobalPool.cpp b/src/images/SkImageRef_GlobalPool.cpp
index b774023..6af8653 100644
--- a/src/images/SkImageRef_GlobalPool.cpp
+++ b/src/images/SkImageRef_GlobalPool.cpp
@@ -9,7 +9,7 @@
 #include "SkImageRefPool.h"
 #include "SkThread.h"
 
-extern SkBaseMutex gImageRefMutex;
+SK_DECLARE_STATIC_MUTEX(gGlobalPoolMutex);
 
 /*
  *  This returns the lazily-allocated global pool. It must be called
@@ -27,16 +27,16 @@
 SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkStream* stream,
                                              SkBitmap::Config config,
                                              int sampleSize)
-        : SkImageRef(stream, config, sampleSize) {
-    this->mutex()->acquire();
+        : SkImageRef(stream, config, sampleSize, &gGlobalPoolMutex) {
+    SkASSERT(&gGlobalPoolMutex == this->mutex());
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->addToHead(this);
-    this->mutex()->release();
 }
 
 SkImageRef_GlobalPool::~SkImageRef_GlobalPool() {
-    this->mutex()->acquire();
+    SkASSERT(&gGlobalPoolMutex == this->mutex());
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->detach(this);
-    this->mutex()->release();
 }
 
 /*  By design, onUnlockPixels() already is inside the mutex-lock,
@@ -56,51 +56,45 @@
     }
     return true;
 }
-    
+
 void SkImageRef_GlobalPool::onUnlockPixels() {
     this->INHERITED::onUnlockPixels();
-    
+
     // by design, onUnlockPixels() already is inside the mutex-lock
     GetGlobalPool()->canLosePixels(this);
 }
 
 SkImageRef_GlobalPool::SkImageRef_GlobalPool(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer) {
-    this->mutex()->acquire();
+        : INHERITED(buffer, &gGlobalPoolMutex) {
+    SkASSERT(&gGlobalPoolMutex == this->mutex());
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->addToHead(this);
-    this->mutex()->release();
 }
 
-SkPixelRef* SkImageRef_GlobalPool::Create(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkImageRef_GlobalPool, (buffer));
-}
-
-SK_DEFINE_PIXEL_REF_REGISTRAR(SkImageRef_GlobalPool)
-
 ///////////////////////////////////////////////////////////////////////////////
 // global imagerefpool wrappers
 
 size_t SkImageRef_GlobalPool::GetRAMBudget() {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     return GetGlobalPool()->getRAMBudget();
 }
 
 void SkImageRef_GlobalPool::SetRAMBudget(size_t size) {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->setRAMBudget(size);
 }
 
 size_t SkImageRef_GlobalPool::GetRAMUsed() {
-    SkAutoMutexAcquire ac(gImageRefMutex);    
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     return GetGlobalPool()->getRAMUsed();
 }
 
 void SkImageRef_GlobalPool::SetRAMUsed(size_t usage) {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->setRAMUsed(usage);
 }
 
 void SkImageRef_GlobalPool::DumpPool() {
-    SkAutoMutexAcquire ac(gImageRefMutex);
+    SkAutoMutexAcquire ac(gGlobalPoolMutex);
     GetGlobalPool()->dump();
 }
diff --git a/src/images/SkImageRef_ashmem.cpp b/src/images/SkImageRef_ashmem.cpp
new file mode 100644
index 0000000..dc60465
--- /dev/null
+++ b/src/images/SkImageRef_ashmem.cpp
@@ -0,0 +1,231 @@
+
+/*
+ * 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 "SkImageRef_ashmem.h"
+#include "SkImageDecoder.h"
+#include "SkFlattenableBuffers.h"
+#include "SkThread.h"
+
+#include "android/ashmem.h"
+
+#include <sys/mman.h>
+#include <unistd.h>
+
+//#define TRACE_ASH_PURGE     // just trace purges
+
+#ifdef DUMP_IMAGEREF_LIFECYCLE
+    #define DUMP_ASHMEM_LIFECYCLE
+#else
+//    #define DUMP_ASHMEM_LIFECYCLE
+#endif
+
+// ashmem likes lengths on page boundaries
+static size_t roundToPageSize(size_t size) {
+    const size_t mask = getpagesize() - 1;
+    size_t newsize = (size + mask) & ~mask;
+//    SkDebugf("---- oldsize %d newsize %d\n", size, newsize);
+    return newsize;
+}
+
+SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream,
+                                             SkBitmap::Config config,
+                                             int sampleSize)
+        : SkImageRef(stream, config, sampleSize) {
+
+    fRec.fFD = -1;
+    fRec.fAddr = NULL;
+    fRec.fSize = 0;
+    fRec.fPinned = false;
+
+    fCT = NULL;
+}
+
+SkImageRef_ashmem::~SkImageRef_ashmem() {
+    SkSafeUnref(fCT);
+    this->closeFD();
+}
+
+void SkImageRef_ashmem::closeFD() {
+    if (-1 != fRec.fFD) {
+#ifdef DUMP_ASHMEM_LIFECYCLE
+        SkDebugf("=== ashmem close %d\n", fRec.fFD);
+#endif
+        SkASSERT(fRec.fAddr);
+        SkASSERT(fRec.fSize);
+        munmap(fRec.fAddr, fRec.fSize);
+        close(fRec.fFD);
+        fRec.fFD = -1;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class AshmemAllocator : public SkBitmap::Allocator {
+public:
+    AshmemAllocator(SkAshmemRec* rec, const char name[])
+        : fRec(rec), fName(name) {}
+
+    virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
+        const size_t size = roundToPageSize(bm->getSize());
+        int fd = fRec->fFD;
+        void* addr = fRec->fAddr;
+
+        SkASSERT(!fRec->fPinned);
+
+        if (-1 == fd) {
+            SkASSERT(NULL == addr);
+            SkASSERT(0 == fRec->fSize);
+
+            fd = ashmem_create_region(fName, size);
+#ifdef DUMP_ASHMEM_LIFECYCLE
+            SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd);
+#endif
+            if (-1 == fd) {
+                SkDebugf("------- imageref_ashmem create failed <%s> %d\n",
+                         fName, size);
+                return false;
+            }
+
+            int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
+            if (err) {
+                SkDebugf("------ ashmem_set_prot_region(%d) failed %d\n",
+                         fd, err);
+                close(fd);
+                return false;
+            }
+
+            addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
+            if (-1 == (long)addr) {
+                SkDebugf("---------- mmap failed for imageref_ashmem size=%d\n",
+                         size);
+                close(fd);
+                return false;
+            }
+
+            fRec->fFD = fd;
+            fRec->fAddr = addr;
+            fRec->fSize = size;
+        } else {
+            SkASSERT(addr);
+            SkASSERT(size == fRec->fSize);
+            (void)ashmem_pin_region(fd, 0, 0);
+        }
+
+        bm->setPixels(addr, ct);
+        fRec->fPinned = true;
+        return true;
+    }
+
+private:
+    // we just point to our caller's memory, these are not copies
+    SkAshmemRec* fRec;
+    const char*  fName;
+};
+
+bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStream* stream,
+                                 SkBitmap* bitmap, SkBitmap::Config config,
+                                 SkImageDecoder::Mode mode) {
+
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return this->INHERITED::onDecode(codec, stream, bitmap, config, mode);
+    }
+
+    AshmemAllocator alloc(&fRec, this->getURI());
+
+    codec->setAllocator(&alloc);
+    bool success = this->INHERITED::onDecode(codec, stream, bitmap, config,
+                                             mode);
+    // remove the allocator, since its on the stack
+    codec->setAllocator(NULL);
+
+    if (success) {
+        // remember the colortable (if any)
+        SkRefCnt_SafeAssign(fCT, bitmap->getColorTable());
+        return true;
+    } else {
+        if (fRec.fPinned) {
+            ashmem_unpin_region(fRec.fFD, 0, 0);
+            fRec.fPinned = false;
+        }
+        this->closeFD();
+        return false;
+    }
+}
+
+void* SkImageRef_ashmem::onLockPixels(SkColorTable** ct) {
+    SkASSERT(fBitmap.getPixels() == NULL);
+    SkASSERT(fBitmap.getColorTable() == NULL);
+
+    // fast case: check if we can just pin and get the cached data
+    if (-1 != fRec.fFD) {
+        SkASSERT(fRec.fAddr);
+        SkASSERT(!fRec.fPinned);
+        int pin = ashmem_pin_region(fRec.fFD, 0, 0);
+
+        if (ASHMEM_NOT_PURGED == pin) { // yea, fast case!
+            fBitmap.setPixels(fRec.fAddr, fCT);
+            fRec.fPinned = true;
+        } else if (ASHMEM_WAS_PURGED == pin) {
+            ashmem_unpin_region(fRec.fFD, 0, 0);
+            // let go of our colortable if we lost the pixels. Well get it back
+            // again when we re-decode
+            if (fCT) {
+                fCT->unref();
+                fCT = NULL;
+            }
+#if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE)
+            SkDebugf("===== ashmem purged %d\n", fBitmap.getSize());
+#endif
+        } else {
+            SkDebugf("===== ashmem pin_region(%d) returned %d\n", fRec.fFD, pin);
+            // return null result for failure
+            if (ct) {
+                *ct = NULL;
+            }
+            return NULL;
+        }
+    } else {
+        // no FD, will create an ashmem region in allocator
+    }
+
+    return this->INHERITED::onLockPixels(ct);
+}
+
+void SkImageRef_ashmem::onUnlockPixels() {
+    this->INHERITED::onUnlockPixels();
+
+    if (-1 != fRec.fFD) {
+        SkASSERT(fRec.fAddr);
+        SkASSERT(fRec.fPinned);
+
+        ashmem_unpin_region(fRec.fFD, 0, 0);
+        fRec.fPinned = false;
+    }
+
+    // we clear this with or without an error, since we've either closed or
+    // unpinned the region
+    fBitmap.setPixels(NULL, NULL);
+}
+
+void SkImageRef_ashmem::flatten(SkFlattenableWriteBuffer& buffer) const {
+    this->INHERITED::flatten(buffer);
+    buffer.writeString(getURI());
+}
+
+SkImageRef_ashmem::SkImageRef_ashmem(SkFlattenableReadBuffer& buffer)
+        : INHERITED(buffer) {
+    fRec.fFD = -1;
+    fRec.fAddr = NULL;
+    fRec.fSize = 0;
+    fRec.fPinned = false;
+    fCT = NULL;
+    char* uri = buffer.readString();
+    if (uri) {
+        setURI(uri);
+        sk_free(uri);
+    }
+}
diff --git a/src/images/SkImageRef_ashmem.h b/src/images/SkImageRef_ashmem.h
new file mode 100644
index 0000000..f98507a
--- /dev/null
+++ b/src/images/SkImageRef_ashmem.h
@@ -0,0 +1,47 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef SkImageRef_ashmem_DEFINED
+#define SkImageRef_ashmem_DEFINED
+
+#include "SkImageRef.h"
+
+struct SkAshmemRec {
+    int     fFD;
+    void*   fAddr;
+    size_t  fSize;
+    bool    fPinned;
+};
+
+class SkImageRef_ashmem : public SkImageRef {
+public:
+    SkImageRef_ashmem(SkStream*, SkBitmap::Config, int sampleSize = 1);
+    virtual ~SkImageRef_ashmem();
+
+    SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(SkImageRef_ashmem)
+
+protected:
+    SkImageRef_ashmem(SkFlattenableReadBuffer&);
+    virtual void flatten(SkFlattenableWriteBuffer&) const SK_OVERRIDE;
+
+    virtual bool onDecode(SkImageDecoder* codec, SkStream* stream,
+                          SkBitmap* bitmap, SkBitmap::Config config,
+                          SkImageDecoder::Mode mode);
+
+    virtual void* onLockPixels(SkColorTable**);
+    virtual void onUnlockPixels();
+
+private:
+    void closeFD();
+
+    SkColorTable* fCT;
+    SkAshmemRec fRec;
+
+    typedef SkImageRef INHERITED;
+};
+
+#endif
diff --git a/src/images/SkImages.cpp b/src/images/SkImages.cpp
new file mode 100644
index 0000000..5b6bf6b
--- /dev/null
+++ b/src/images/SkImages.cpp
@@ -0,0 +1,21 @@
+/*
+ * 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 "SkFlattenable.h"
+#include "SkImageRef_GlobalPool.h"
+#include "SkImages.h"
+
+#ifdef SK_BUILD_FOR_ANDROID
+#include "SkImageRef_ashmem.h"
+#endif
+
+void SkImages::InitializeFlattenables() {
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageRef_GlobalPool)
+#ifdef SK_BUILD_FOR_ANDROID
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkImageRef_ashmem)
+#endif
+}
diff --git a/src/images/SkJpegUtility.cpp b/src/images/SkJpegUtility.cpp
index aa5237f..d135099 100644
--- a/src/images/SkJpegUtility.cpp
+++ b/src/images/SkJpegUtility.cpp
@@ -93,6 +93,7 @@
 static void sk_term_source(j_decompress_ptr /*cinfo*/) {}
 
 
+#if 0 // UNUSED
 static void skmem_init_source(j_decompress_ptr cinfo) {
     skjpeg_source_mgr*  src = (skjpeg_source_mgr*)cinfo->src;
     src->next_input_byte = (const JOCTET*)src->fMemoryBase;
@@ -119,6 +120,7 @@
 }
 
 static void skmem_term_source(j_decompress_ptr /*cinfo*/) {}
+#endif
 
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/images/SkMovie.cpp b/src/images/SkMovie.cpp
index 81820a5..ea4a76c 100644
--- a/src/images/SkMovie.cpp
+++ b/src/images/SkMovie.cpp
@@ -9,6 +9,8 @@
 #include "SkCanvas.h"
 #include "SkPaint.h"
 
+SK_DEFINE_INST_COUNT(SkMovie)
+
 // We should never see this in normal operation since our time values are
 // 0-based. So we use it as a sentinal.
 #define UNINITIALIZED_MSEC ((SkMSec)-1)
@@ -55,7 +57,7 @@
     SkMSec dur = this->duration();
     if (time > dur)
         time = dur;
-        
+
     bool changed = false;
     if (time != fCurrTime)
     {
@@ -105,4 +107,3 @@
 
     return movie;
 }
-
diff --git a/src/images/SkMovie_gif.cpp b/src/images/SkMovie_gif.cpp
index 91d3591..cda1525 100644
--- a/src/images/SkMovie_gif.cpp
+++ b/src/images/SkMovie_gif.cpp
@@ -25,7 +25,7 @@
     virtual bool onGetInfo(Info*);
     virtual bool onSetTime(SkMSec);
     virtual bool onGetBitmap(SkBitmap*);
-    
+
 private:
     GifFileType* fGIF;
     int fCurrIndex;
@@ -40,7 +40,11 @@
 
 SkGIFMovie::SkGIFMovie(SkStream* stream)
 {
+#if GIFLIB_MAJOR < 5
     fGIF = DGifOpen( stream, Decode );
+#else
+    fGIF = DGifOpen( stream, Decode, NULL );
+#endif
     if (NULL == fGIF)
         return;
 
diff --git a/src/images/SkPageFlipper.cpp b/src/images/SkPageFlipper.cpp
index 8e51e63..a53f47b 100644
--- a/src/images/SkPageFlipper.cpp
+++ b/src/images/SkPageFlipper.cpp
@@ -32,7 +32,7 @@
 void SkPageFlipper::resize(int width, int height) {
     fWidth = width;
     fHeight = height;
-    
+
     // this is the opposite of the constructors
     fDirty1->setRect(0, 0, width, height);
     fDirty0->setEmpty();
@@ -74,5 +74,3 @@
     fDirty1->setEmpty();
     return *fDirty0;
 }
-
-
diff --git a/src/images/SkScaledBitmapSampler.cpp b/src/images/SkScaledBitmapSampler.cpp
index 1af433d..25b32fd 100644
--- a/src/images/SkScaledBitmapSampler.cpp
+++ b/src/images/SkScaledBitmapSampler.cpp
@@ -295,10 +295,14 @@
 
 SkScaledBitmapSampler::SkScaledBitmapSampler(int width, int height,
                                              int sampleSize) {
+    fCTable = NULL;
+    fDstRow = NULL;
+    fRowProc = NULL;
+
     if (width <= 0 || height <= 0) {
         sk_throw();
     }
-    
+
     if (sampleSize <= 1) {
         fScaledWidth = width;
         fScaledHeight = height;
@@ -306,30 +310,27 @@
         fDX = fDY = 1;
         return;
     }
-    
+
     int dx = SkMin32(sampleSize, width);
     int dy = SkMin32(sampleSize, height);
-    
+
     fScaledWidth = width / dx;
     fScaledHeight = height / dy;
-    
+
     SkASSERT(fScaledWidth > 0);
     SkASSERT(fScaledHeight > 0);
-    
+
     fX0 = dx >> 1;
     fY0 = dy >> 1;
-    
+
     SkASSERT(fX0 >= 0 && fX0 < width);
     SkASSERT(fY0 >= 0 && fY0 < height);
-    
+
     fDX = dx;
     fDY = dy;
-    
+
     SkASSERT(fDX > 0 && (fX0 + fDX * (fScaledWidth - 1)) < width);
     SkASSERT(fDY > 0 && (fY0 + fDY * (fScaledHeight - 1)) < height);
-    
-    fRowProc = NULL;
-    fCTable = NULL;
 }
 
 bool SkScaledBitmapSampler::begin(SkBitmap* dst, SrcConfig sc, bool dither,
@@ -412,7 +413,7 @@
         default:
             return false;
     }
-    
+
     fRowProc = gProcs[index];
     fDstRow = (char*)dst->getPixels();
     fDstRowBytes = dst->rowBytes();
diff --git a/src/images/SkScaledBitmapSampler.h b/src/images/SkScaledBitmapSampler.h
index 8a735df..f6de4cc 100644
--- a/src/images/SkScaledBitmapSampler.h
+++ b/src/images/SkScaledBitmapSampler.h
@@ -16,10 +16,10 @@
 class SkScaledBitmapSampler {
 public:
     SkScaledBitmapSampler(int origWidth, int origHeight, int cellSize);
-    
+
     int scaledWidth() const { return fScaledWidth; }
     int scaledHeight() const { return fScaledHeight; }
-    
+
     int srcY0() const { return fY0; }
     int srcDY() const { return fDY; }
 
@@ -59,7 +59,7 @@
     char*   fDstRow; // points into bitmap's pixels
     int     fDstRowBytes;
     int     fCurrY; // used for dithering
-    int     fSrcPixelSize;  // 1, 3, 4    
+    int     fSrcPixelSize;  // 1, 3, 4
     RowProc fRowProc;
 
     // optional reference to the src colors if the src is a palette model
diff --git a/src/images/bmpdecoderhelper.h b/src/images/bmpdecoderhelper.h
index f3324ee..f2f4109 100644
--- a/src/images/bmpdecoderhelper.h
+++ b/src/images/bmpdecoderhelper.h
@@ -32,14 +32,14 @@
   ~scoped_array() {
     delete[] ptr_;
   }
-  
+
   void reset(T* p = 0) {
     if (p != ptr_) {
       delete[] ptr_;
       ptr_ = p;
     }
   }
-  
+
   T& operator[](int i) const {
     return ptr_[i];
   }
@@ -53,7 +53,7 @@
  public:
   BmpDecoderCallback() { }
   virtual ~BmpDecoderCallback() {}
-  
+
   /**
    * This is called once for an image. It is passed the width and height and
    * should return the address of a buffer that is large enough to store
@@ -62,7 +62,7 @@
    * valid dimensions.
    */
   virtual uint8* SetSize(int width, int height) = 0;
-   
+
  private:
   DISALLOW_EVIL_CONSTRUCTORS(BmpDecoderCallback);
 };
@@ -110,7 +110,7 @@
   uint8* output_;
   bool inverted_;
 };
-  
+
 } // namespace
 
 #endif
diff --git a/src/images/transform_scanline.h b/src/images/transform_scanline.h
new file mode 100644
index 0000000..36efdd8
--- /dev/null
+++ b/src/images/transform_scanline.h
@@ -0,0 +1,140 @@
+
+/*
+ * Copyright 2012 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.
+ */
+
+/**
+ * Functions to transform scanlines between packed-pixel formats.
+ */
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkPreConfig.h"
+#include "SkUnPreMultiply.h"
+
+/**
+ * Function template for transforming scanlines.
+ * Transform 'width' pixels from 'src' buffer into 'dst' buffer,
+ * repacking color channel data as appropriate for the given transformation.
+ */
+typedef void (*transform_scanline_proc)(const char* SK_RESTRICT src,
+                                        int width, char* SK_RESTRICT dst);
+
+/**
+ * Identity transformation: just copy bytes from src to dst.
+ */
+static void transform_scanline_memcpy(const char* SK_RESTRICT src, int width,
+                                      char* SK_RESTRICT dst) {
+    memcpy(dst, src, width);
+}
+
+/**
+ * Transform from kRGB_565_Config to 3-bytes-per-pixel RGB.
+ * Alpha channel data is not present in kRGB_565_Config format, so there is no
+ * alpha channel data to preserve.
+ */
+static void transform_scanline_565(const char* SK_RESTRICT src, int width,
+                                   char* SK_RESTRICT dst) {
+    const uint16_t* SK_RESTRICT srcP = (const uint16_t*)src;
+    for (int i = 0; i < width; i++) {
+        unsigned c = *srcP++;
+        *dst++ = SkPacked16ToR32(c);
+        *dst++ = SkPacked16ToG32(c);
+        *dst++ = SkPacked16ToB32(c);
+    }
+}
+
+/**
+ * Transform from kARGB_8888_Config to 3-bytes-per-pixel RGB.
+ * Alpha channel data, if any, is abandoned.
+ */
+static void transform_scanline_888(const char* SK_RESTRICT src, int width,
+                                   char* SK_RESTRICT dst) {
+    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
+    for (int i = 0; i < width; i++) {
+        SkPMColor c = *srcP++;
+        *dst++ = SkGetPackedR32(c);
+        *dst++ = SkGetPackedG32(c);
+        *dst++ = SkGetPackedB32(c);
+    }
+}
+
+/**
+ * Transform from kARGB_4444_Config to 3-bytes-per-pixel RGB.
+ * Alpha channel data, if any, is abandoned.
+ */
+static void transform_scanline_444(const char* SK_RESTRICT src, int width,
+                                   char* SK_RESTRICT dst) {
+    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
+    for (int i = 0; i < width; i++) {
+        SkPMColor16 c = *srcP++;
+        *dst++ = SkPacked4444ToR32(c);
+        *dst++ = SkPacked4444ToG32(c);
+        *dst++ = SkPacked4444ToB32(c);
+    }
+}
+
+/**
+ * Transform from kARGB_8888_Config to 4-bytes-per-pixel RGBA.
+ * (This would be the identity transformation, except for byte-order and
+ * scaling of RGB based on alpha channel).
+ */
+static void transform_scanline_8888(const char* SK_RESTRICT src, int width,
+                                    char* SK_RESTRICT dst) {
+    const SkPMColor* SK_RESTRICT srcP = (const SkPMColor*)src;
+    const SkUnPreMultiply::Scale* SK_RESTRICT table =
+                                              SkUnPreMultiply::GetScaleTable();
+
+    for (int i = 0; i < width; i++) {
+        SkPMColor c = *srcP++;
+        unsigned a = SkGetPackedA32(c);
+        unsigned r = SkGetPackedR32(c);
+        unsigned g = SkGetPackedG32(c);
+        unsigned b = SkGetPackedB32(c);
+
+        if (0 != a && 255 != a) {
+            SkUnPreMultiply::Scale scale = table[a];
+            r = SkUnPreMultiply::ApplyScale(scale, r);
+            g = SkUnPreMultiply::ApplyScale(scale, g);
+            b = SkUnPreMultiply::ApplyScale(scale, b);
+        }
+        *dst++ = r;
+        *dst++ = g;
+        *dst++ = b;
+        *dst++ = a;
+    }
+}
+
+/**
+ * Transform from kARGB_8888_Config to 4-bytes-per-pixel RGBA,
+ * with scaling of RGB based on alpha channel.
+ */
+static void transform_scanline_4444(const char* SK_RESTRICT src, int width,
+                                    char* SK_RESTRICT dst) {
+    const SkPMColor16* SK_RESTRICT srcP = (const SkPMColor16*)src;
+    const SkUnPreMultiply::Scale* SK_RESTRICT table =
+                                              SkUnPreMultiply::GetScaleTable();
+
+    for (int i = 0; i < width; i++) {
+        SkPMColor16 c = *srcP++;
+        unsigned a = SkPacked4444ToA32(c);
+        unsigned r = SkPacked4444ToR32(c);
+        unsigned g = SkPacked4444ToG32(c);
+        unsigned b = SkPacked4444ToB32(c);
+
+        if (0 != a && 255 != a) {
+            SkUnPreMultiply::Scale scale = table[a];
+            r = SkUnPreMultiply::ApplyScale(scale, r);
+            g = SkUnPreMultiply::ApplyScale(scale, g);
+            b = SkUnPreMultiply::ApplyScale(scale, b);
+        }
+        *dst++ = r;
+        *dst++ = g;
+        *dst++ = b;
+        *dst++ = a;
+    }
+}
diff --git a/src/opts/SkBitmapProcState_arm_neon.cpp b/src/opts/SkBitmapProcState_arm_neon.cpp
new file mode 100644
index 0000000..d50707d
--- /dev/null
+++ b/src/opts/SkBitmapProcState_arm_neon.cpp
@@ -0,0 +1,92 @@
+
+/*
+ * 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 "SkBitmapProcState.h"
+#include "SkBitmapProcState_filter.h"
+#include "SkColorPriv.h"
+#include "SkFilterProc.h"
+#include "SkPaint.h"
+#include "SkShader.h"   // for tilemodes
+#include "SkUtilsArm.h"
+
+// Required to ensure the table is part of the final binary.
+extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[];
+extern const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[];
+
+#define   NAME_WRAP(x)  x ## _neon
+#include "SkBitmapProcState_filter_neon.h"
+#include "SkBitmapProcState_procs.h"
+
+const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[] = {
+    S32_opaque_D32_nofilter_DXDY_neon,
+    S32_alpha_D32_nofilter_DXDY_neon,
+    S32_opaque_D32_nofilter_DX_neon,
+    S32_alpha_D32_nofilter_DX_neon,
+    S32_opaque_D32_filter_DXDY_neon,
+    S32_alpha_D32_filter_DXDY_neon,
+    S32_opaque_D32_filter_DX_neon,
+    S32_alpha_D32_filter_DX_neon,
+
+    S16_opaque_D32_nofilter_DXDY_neon,
+    S16_alpha_D32_nofilter_DXDY_neon,
+    S16_opaque_D32_nofilter_DX_neon,
+    S16_alpha_D32_nofilter_DX_neon,
+    S16_opaque_D32_filter_DXDY_neon,
+    S16_alpha_D32_filter_DXDY_neon,
+    S16_opaque_D32_filter_DX_neon,
+    S16_alpha_D32_filter_DX_neon,
+
+    SI8_opaque_D32_nofilter_DXDY_neon,
+    SI8_alpha_D32_nofilter_DXDY_neon,
+    SI8_opaque_D32_nofilter_DX_neon,
+    SI8_alpha_D32_nofilter_DX_neon,
+    SI8_opaque_D32_filter_DXDY_neon,
+    SI8_alpha_D32_filter_DXDY_neon,
+    SI8_opaque_D32_filter_DX_neon,
+    SI8_alpha_D32_filter_DX_neon,
+
+    S4444_opaque_D32_nofilter_DXDY_neon,
+    S4444_alpha_D32_nofilter_DXDY_neon,
+    S4444_opaque_D32_nofilter_DX_neon,
+    S4444_alpha_D32_nofilter_DX_neon,
+    S4444_opaque_D32_filter_DXDY_neon,
+    S4444_alpha_D32_filter_DXDY_neon,
+    S4444_opaque_D32_filter_DX_neon,
+    S4444_alpha_D32_filter_DX_neon,
+
+    // A8 treats alpha/opauqe the same (equally efficient)
+    SA8_alpha_D32_nofilter_DXDY_neon,
+    SA8_alpha_D32_nofilter_DXDY_neon,
+    SA8_alpha_D32_nofilter_DX_neon,
+    SA8_alpha_D32_nofilter_DX_neon,
+    SA8_alpha_D32_filter_DXDY_neon,
+    SA8_alpha_D32_filter_DXDY_neon,
+    SA8_alpha_D32_filter_DX_neon,
+    SA8_alpha_D32_filter_DX_neon
+};
+
+const SkBitmapProcState::SampleProc16 gSkBitmapProcStateSample16_neon[] = {
+    S32_D16_nofilter_DXDY_neon,
+    S32_D16_nofilter_DX_neon,
+    S32_D16_filter_DXDY_neon,
+    S32_D16_filter_DX_neon,
+
+    S16_D16_nofilter_DXDY_neon,
+    S16_D16_nofilter_DX_neon,
+    S16_D16_filter_DXDY_neon,
+    S16_D16_filter_DX_neon,
+
+    SI8_D16_nofilter_DXDY_neon,
+    SI8_D16_nofilter_DX_neon,
+    SI8_D16_filter_DXDY_neon,
+    SI8_D16_filter_DX_neon,
+
+    // Don't support 4444 -> 565
+    NULL, NULL, NULL, NULL,
+    // Don't support A8 -> 565
+    NULL, NULL, NULL, NULL
+};
diff --git a/src/opts/SkBitmapProcState_filter_neon.h b/src/opts/SkBitmapProcState_filter_neon.h
new file mode 100644
index 0000000..699f856
--- /dev/null
+++ b/src/opts/SkBitmapProcState_filter_neon.h
@@ -0,0 +1,88 @@
+
+/*
+ * Copyright 2012 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 "SkColorPriv.h"
+
+/*
+    Filter_32_opaque
+
+    There is no hard-n-fast rule that the filtering must produce
+    exact results for the color components, but if the 4 incoming colors are
+    all opaque, then the output color must also be opaque. Subsequent parts of
+    the drawing pipeline may rely on this (e.g. which blitrow proc to use).
+ */
+
+static inline void Filter_32_opaque_neon(unsigned x, unsigned y,
+                                         SkPMColor a00, SkPMColor a01,
+                                         SkPMColor a10, SkPMColor a11,
+                                         SkPMColor *dst) {
+    asm volatile(
+                 "vdup.8         d0, %[y]                \n\t"   // duplicate y into d0
+                 "vmov.u8        d16, #16                \n\t"   // set up constant in d16
+                 "vsub.u8        d1, d16, d0             \n\t"   // d1 = 16-y
+
+                 "vdup.32        d4, %[a00]              \n\t"   // duplicate a00 into d4
+                 "vdup.32        d5, %[a10]              \n\t"   // duplicate a10 into d5
+                 "vmov.32        d4[1], %[a01]           \n\t"   // set top of d4 to a01
+                 "vmov.32        d5[1], %[a11]           \n\t"   // set top of d5 to a11
+
+                 "vmull.u8       q3, d4, d1              \n\t"   // q3 = [a01|a00] * (16-y)
+                 "vmull.u8       q0, d5, d0              \n\t"   // q0 = [a11|a10] * y
+
+                 "vdup.16        d5, %[x]                \n\t"   // duplicate x into d5
+                 "vmov.u16       d16, #16                \n\t"   // set up constant in d16
+                 "vsub.u16       d3, d16, d5             \n\t"   // d3 = 16-x
+
+                 "vmul.i16       d4, d7, d5              \n\t"   // d4  = a01 * x
+                 "vmla.i16       d4, d1, d5              \n\t"   // d4 += a11 * x
+                 "vmla.i16       d4, d6, d3              \n\t"   // d4 += a00 * (16-x)
+                 "vmla.i16       d4, d0, d3              \n\t"   // d4 += a10 * (16-x)
+                 "vshrn.i16      d0, q2, #8              \n\t"   // shift down result by 8
+                 "vst1.32        {d0[0]}, [%[dst]]       \n\t"   // store result
+                 :
+                 : [x] "r" (x), [y] "r" (y), [a00] "r" (a00), [a01] "r" (a01), [a10] "r" (a10), [a11] "r" (a11), [dst] "r" (dst)
+                 : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16"
+                 );
+}
+
+static inline void Filter_32_alpha_neon(unsigned x, unsigned y,
+                                        SkPMColor a00, SkPMColor a01,
+                                        SkPMColor a10, SkPMColor a11,
+                                        SkPMColor *dst, uint16_t scale) {
+    asm volatile(
+                 "vdup.8         d0, %[y]                \n\t"   // duplicate y into d0
+                 "vmov.u8        d16, #16                \n\t"   // set up constant in d16
+                 "vsub.u8        d1, d16, d0             \n\t"   // d1 = 16-y
+
+                 "vdup.32        d4, %[a00]              \n\t"   // duplicate a00 into d4
+                 "vdup.32        d5, %[a10]              \n\t"   // duplicate a10 into d5
+                 "vmov.32        d4[1], %[a01]           \n\t"   // set top of d4 to a01
+                 "vmov.32        d5[1], %[a11]           \n\t"   // set top of d5 to a11
+
+                 "vmull.u8       q3, d4, d1              \n\t"   // q3 = [a01|a00] * (16-y)
+                 "vmull.u8       q0, d5, d0              \n\t"   // q0 = [a11|a10] * y
+
+                 "vdup.16        d5, %[x]                \n\t"   // duplicate x into d5
+                 "vmov.u16       d16, #16                \n\t"   // set up constant in d16
+                 "vsub.u16       d3, d16, d5             \n\t"   // d3 = 16-x
+
+                 "vmul.i16       d4, d7, d5              \n\t"   // d4  = a01 * x
+                 "vmla.i16       d4, d1, d5              \n\t"   // d4 += a11 * x
+                 "vmla.i16       d4, d6, d3              \n\t"   // d4 += a00 * (16-x)
+                 "vmla.i16       d4, d0, d3              \n\t"   // d4 += a10 * (16-x)
+                 "vdup.16        d3, %[scale]            \n\t"   // duplicate scale into d3
+                 "vshr.u16       d4, d4, #8              \n\t"   // shift down result by 8
+                 "vmul.i16       d4, d4, d3              \n\t"   // multiply result by scale
+                 "vshrn.i16      d0, q2, #8              \n\t"   // shift down result by 8
+                 "vst1.32        {d0[0]}, [%[dst]]       \n\t"   // store result
+                 :
+                 : [x] "r" (x), [y] "r" (y), [a00] "r" (a00), [a01] "r" (a01), [a10] "r" (a10), [a11] "r" (a11), [dst] "r" (dst), [scale] "r" (scale)
+                 : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16"
+                 );
+}
diff --git a/src/opts/SkBitmapProcState_matrixProcs_neon.cpp b/src/opts/SkBitmapProcState_matrixProcs_neon.cpp
new file mode 100644
index 0000000..e9684e2
--- /dev/null
+++ b/src/opts/SkBitmapProcState_matrixProcs_neon.cpp
@@ -0,0 +1,145 @@
+/* NEON optimized code (C) COPYRIGHT 2009 Motorola
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkBitmapProcState.h"
+#include "SkPerspIter.h"
+#include "SkShader.h"
+#include "SkUtilsArm.h"
+
+extern const SkBitmapProcState::MatrixProc ClampX_ClampY_Procs_neon[];
+extern const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs_neon[];
+
+static void decal_nofilter_scale_neon(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
+static void decal_filter_scale_neon(uint32_t dst[], SkFixed fx, SkFixed dx, int count);
+
+static unsigned SK_USHIFT16(unsigned x) {
+    return x >> 16;
+}
+
+#define MAKENAME(suffix)        ClampX_ClampY ## suffix ## _neon
+#define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
+#define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
+#define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+#define CHECK_FOR_DECAL
+#include "SkBitmapProcState_matrix_clamp_neon.h"
+
+#define MAKENAME(suffix)        RepeatX_RepeatY ## suffix ## _neon
+#define TILEX_PROCF(fx, max)    SK_USHIFT16(((fx) & 0xFFFF) * ((max) + 1))
+#define TILEY_PROCF(fy, max)    SK_USHIFT16(((fy) & 0xFFFF) * ((max) + 1))
+#define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+#define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+#include "SkBitmapProcState_matrix_repeat_neon.h"
+
+
+void decal_nofilter_scale_neon(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
+{
+    int i;
+
+    if (count >= 8) {
+        /* SkFixed is 16.16 fixed point */
+        SkFixed dx2 = dx+dx;
+        SkFixed dx4 = dx2+dx2;
+        SkFixed dx8 = dx4+dx4;
+
+        /* now build fx/fx+dx/fx+2dx/fx+3dx */
+        SkFixed fx1, fx2, fx3;
+        int32x2_t lower, upper;
+        int32x4_t lbase, hbase;
+        uint16_t *dst16 = (uint16_t *)dst;
+
+        fx1 = fx+dx;
+        fx2 = fx1+dx;
+        fx3 = fx2+dx;
+
+        /* avoid an 'lbase unitialized' warning */
+        lbase = vdupq_n_s32(fx);
+        lbase = vsetq_lane_s32(fx1, lbase, 1);
+        lbase = vsetq_lane_s32(fx2, lbase, 2);
+        lbase = vsetq_lane_s32(fx3, lbase, 3);
+        hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));
+
+        /* take upper 16 of each, store, and bump everything */
+        do {
+            int32x4_t lout, hout;
+            uint16x8_t hi16;
+
+            lout = lbase;
+            hout = hbase;
+            /* gets hi's of all louts then hi's of all houts */
+            asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
+            hi16 = vreinterpretq_u16_s32(hout);
+            vst1q_u16(dst16, hi16);
+
+            /* on to the next */
+            lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
+            hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
+            dst16 += 8;
+            count -= 8;
+            fx += dx8;
+        } while (count >= 8);
+        dst = (uint32_t *) dst16;
+    }
+
+    uint16_t* xx = (uint16_t*)dst;
+    for (i = count; i > 0; --i) {
+        *xx++ = SkToU16(fx >> 16); fx += dx;
+    }
+}
+
+void decal_filter_scale_neon(uint32_t dst[], SkFixed fx, SkFixed dx, int count)
+{
+    if (count >= 8) {
+        int32x4_t wide_fx;
+        int32x4_t wide_fx2;
+        int32x4_t wide_dx8 = vdupq_n_s32(dx*8);
+
+        wide_fx = vdupq_n_s32(fx);
+        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
+        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
+        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
+
+        wide_fx2 = vaddq_s32(wide_fx, vdupq_n_s32(dx+dx+dx+dx));
+
+        while (count >= 8) {
+            int32x4_t wide_out;
+            int32x4_t wide_out2;
+
+            wide_out = vshlq_n_s32(vshrq_n_s32(wide_fx, 12), 14);
+            wide_out = vorrq_s32(wide_out,
+            vaddq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(1)));
+
+            wide_out2 = vshlq_n_s32(vshrq_n_s32(wide_fx2, 12), 14);
+            wide_out2 = vorrq_s32(wide_out2,
+            vaddq_s32(vshrq_n_s32(wide_fx2,16), vdupq_n_s32(1)));
+
+            vst1q_u32(dst, vreinterpretq_u32_s32(wide_out));
+            vst1q_u32(dst+4, vreinterpretq_u32_s32(wide_out2));
+
+            dst += 8;
+            fx += dx*8;
+            wide_fx = vaddq_s32(wide_fx, wide_dx8);
+            wide_fx2 = vaddq_s32(wide_fx2, wide_dx8);
+            count -= 8;
+        }
+    }
+
+    if (count & 1)
+    {
+        SkASSERT((fx >> (16 + 14)) == 0);
+        *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+        fx += dx;
+    }
+    while ((count -= 2) >= 0)
+    {
+        SkASSERT((fx >> (16 + 14)) == 0);
+        *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+        fx += dx;
+
+        *dst++ = (fx >> 12 << 14) | ((fx >> 16) + 1);
+        fx += dx;
+    }
+}
diff --git a/src/opts/SkBitmapProcState_matrix_clamp_neon.h b/src/opts/SkBitmapProcState_matrix_clamp_neon.h
new file mode 100644
index 0000000..4f47d47
--- /dev/null
+++ b/src/opts/SkBitmapProcState_matrix_clamp_neon.h
@@ -0,0 +1,920 @@
+/* NEON optimized code (C) COPYRIGHT 2009 Motorola
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Modifications done in-house at Motorola
+ *
+ * this is a clone of SkBitmapProcState_matrix.h
+ * and has been tuned to work with the NEON unit.
+ *
+ * Still going back and forth between whether this approach
+ * (clone the entire SkBitmapProcState_matrix.h file or
+ * if I should put just the modified routines in here and
+ * then use a construct like #define DONT_DO_THIS_FUNCTION or
+ * something like that...
+ *
+ * This is for the ClampX_ClampY instance
+ *
+ */
+
+
+#include <arm_neon.h>
+
+/*
+ * This has been modified on the knowledge that (at the time)
+ * we had the following macro definitions in the parent file
+ *
+ * #define MAKENAME(suffix)        ClampX_ClampY ## suffix
+ * #define TILEX_PROCF(fx, max)    SkClampMax((fx) >> 16, max)
+ * #define TILEY_PROCF(fy, max)    SkClampMax((fy) >> 16, max)
+ * #define TILEX_LOW_BITS(fx, max) (((fx) >> 12) & 0xF)
+ * #define TILEY_LOW_BITS(fy, max) (((fy) >> 12) & 0xF)
+ * #define CHECK_FOR_DECAL
+ */
+
+/* SkClampMax(val,max) -- bound to 0..max */
+
+#define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale_neon)
+#define SCALE_FILTER_NAME       MAKENAME(_filter_scale_neon)
+#define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine_neon)
+#define AFFINE_FILTER_NAME      MAKENAME(_filter_affine_neon)
+#define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp_neon)
+#define PERSP_FILTER_NAME       MAKENAME(_filter_persp_neon)
+
+#define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
+#define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)
+
+#ifndef PREAMBLE
+    #define PREAMBLE(state)
+    #define PREAMBLE_PARAM_X
+    #define PREAMBLE_PARAM_Y
+    #define PREAMBLE_ARG_X
+    #define PREAMBLE_ARG_Y
+#endif
+
+static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+
+    PREAMBLE(s);
+    // we store y, x, x, x, x, x
+
+    const unsigned maxX = s.fBitmap->width() - 1;
+    SkFixed fx;
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        fx = SkScalarToFixed(pt.fY);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        *xy++ = TILEY_PROCF(fx, maxY);
+        fx = SkScalarToFixed(pt.fX);
+    }
+
+    if (0 == maxX) {
+        // all of the following X values must be 0
+        memset(xy, 0, count * sizeof(uint16_t));
+        return;
+    }
+
+    const SkFixed dx = s.fInvSx;
+
+#ifdef CHECK_FOR_DECAL
+    // test if we don't need to apply the tile proc
+    if ((unsigned)(fx >> 16) <= maxX &&
+        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
+        decal_nofilter_scale_neon(xy, fx, dx, count);
+        return;
+    }
+#endif
+
+    int i;
+
+    /* very much like done in decal_nofilter, but with
+     * an extra clamping function applied.
+     * TILEX_PROCF(fx,max) SkClampMax((fx)>>16, max)
+     */
+    if (count >= 8) {
+        /* SkFixed is 16.16 fixed point */
+        SkFixed dx2 = dx+dx;
+        SkFixed dx4 = dx2+dx2;
+        SkFixed dx8 = dx4+dx4;
+
+        /* now build fx/fx+dx/fx+2dx/fx+3dx */
+        SkFixed fx1, fx2, fx3;
+        int32x2_t lower, upper;
+        int32x4_t lbase, hbase;
+        int16_t *dst16 = (int16_t *)xy;
+
+        fx1 = fx+dx;
+        fx2 = fx1+dx;
+        fx3 = fx2+dx;
+
+        /* build my template(s) */
+        /* avoid the 'lbase unitialized' warning */
+        lbase = vdupq_n_s32(fx);
+        lbase = vsetq_lane_s32(fx1, lbase, 1);
+        lbase = vsetq_lane_s32(fx2, lbase, 2);
+        lbase = vsetq_lane_s32(fx3, lbase, 3);
+
+        hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));
+
+        /* store & bump */
+        do {
+            int32x4_t lout;
+            int32x4_t hout;
+            int16x8_t hi16;
+
+            /* get the hi 16s of all those 32s */
+            lout = lbase;
+            hout = hbase;
+            /* this sets up all lout's then all hout's in hout */
+            asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
+            hi16 = vreinterpretq_s16_s32(hout);
+
+            /* clamp & output */
+            hi16 = vmaxq_s16(hi16, vdupq_n_s16(0));
+            hi16 = vminq_s16(hi16, vdupq_n_s16(maxX));
+            vst1q_s16(dst16, hi16);
+
+            /* but preserving base & on to the next */
+            lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
+            hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
+            dst16 += 8;
+            count -= 8;
+            fx += dx8;
+        } while (count >= 8);
+        xy = (uint32_t *) dst16;
+    }
+
+    uint16_t* xx = (uint16_t*)xy;
+    for (i = count; i > 0; --i) {
+        *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
+    }
+}
+
+// note: we could special-case on a matrix which is skewed in X but not Y.
+// this would require a more general setup thatn SCALE does, but could use
+// SCALE's inner loop that only looks at dx
+
+static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                 uint32_t xy[], int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask |
+                             SkMatrix::kAffine_Mask)) == 0);
+
+    PREAMBLE(s);
+    SkPoint srcPt;
+    s.fInvProc(*s.fInvMatrix,
+               SkIntToScalar(x) + SK_ScalarHalf,
+               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+    SkFixed fx = SkScalarToFixed(srcPt.fX);
+    SkFixed fy = SkScalarToFixed(srcPt.fY);
+    SkFixed dx = s.fInvSx;
+    SkFixed dy = s.fInvKy;
+    int maxX = s.fBitmap->width() - 1;
+    int maxY = s.fBitmap->height() - 1;
+
+    /* NEON lets us do an 8x unrolling */
+    if (count >= 8) {
+        /* SkFixed is 16.16 fixed point */
+        SkFixed dx4 = dx * 4;
+        SkFixed dy4 = dy * 4;
+        SkFixed dx8 = dx * 8;
+        SkFixed dy8 = dy * 8;
+
+        int32x4_t xbase, ybase;
+        int32x4_t x2base, y2base;
+        int16_t *dst16 = (int16_t *) xy;
+
+        /* my sets of maxx/maxy for clamping */
+        int32_t maxpair = (maxX&0xffff) | ((maxY&0xffff)<<16);
+        int16x8_t maxXY = vreinterpretq_s16_s32(vdupq_n_s32(maxpair));
+
+        /* now build fx/fx+dx/fx+2dx/fx+3dx */
+        /* avoid the 'xbase unitialized' warning...*/
+        xbase = vdupq_n_s32(fx);
+        xbase = vsetq_lane_s32(fx+dx, xbase, 1);
+        xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2);
+        xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3);
+
+        /* same for fy */
+        /* avoid the 'ybase unitialized' warning...*/
+        ybase = vdupq_n_s32(fy);
+        ybase = vsetq_lane_s32(fy+dy, ybase, 1);
+        ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2);
+        ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3);
+
+        x2base = vaddq_s32(xbase, vdupq_n_s32(dx4));
+        y2base = vaddq_s32(ybase, vdupq_n_s32(dy4));
+
+        /* store & bump */
+        do {
+            int32x4_t xout, yout;
+            int32x4_t x2out, y2out;
+            int16x8_t hi16, hi16_2;
+
+            xout = xbase;
+            yout = ybase;
+
+            /* overlay y's low16 with hi16 from x */
+            /* so we properly shifted xyxyxyxy */
+            yout = vsriq_n_s32(yout, xout, 16);
+            hi16 = vreinterpretq_s16_s32 (yout);
+
+            /* do the clamping; both guys get 0's */
+            hi16 = vmaxq_s16 (hi16, vdupq_n_s16(0));
+            hi16 = vminq_s16 (hi16, maxXY);
+
+            vst1q_s16 (dst16, hi16);
+
+            /* and for the other 4 pieces of this iteration */
+            x2out = x2base;
+            y2out = y2base;
+
+            /* overlay y's low16 with hi16 from x */
+            /* so we properly shifted xyxyxyxy */
+            y2out = vsriq_n_s32(y2out, x2out, 16);
+            hi16_2 = vreinterpretq_s16_s32 (y2out);
+
+            /* do the clamping; both guys get 0's */
+            hi16_2 = vmaxq_s16 (hi16_2, vdupq_n_s16(0));
+            hi16_2 = vminq_s16 (hi16_2, maxXY);
+
+            /* RBE: gcc regenerates dst16+8 all the time instead
+             * of folding it into an addressing mode. *sigh* */
+            vst1q_s16 (dst16+8, hi16_2);
+
+            /* moving base and on to the next */
+            xbase = vaddq_s32 (xbase, vdupq_n_s32 (dx8));
+            ybase = vaddq_s32 (ybase, vdupq_n_s32 (dy8));
+            x2base = vaddq_s32 (x2base, vdupq_n_s32 (dx8));
+            y2base = vaddq_s32 (y2base, vdupq_n_s32 (dy8));
+
+            dst16 += 16;        /* 8x32 aka 16x16 */
+            count -= 8;
+            fx += dx8;
+            fy += dy8;
+        } while (count >= 8);
+        xy = (uint32_t *) dst16;
+    }
+
+    for (int i = count; i > 0; --i) {
+        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
+        fx += dx; fy += dy;
+    }
+}
+
+#undef    DEBUG_PERSP_NOFILTER
+
+static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t* SK_RESTRICT xy,
+                                int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+
+    PREAMBLE(s);
+    /* max{X,Y} are int here, but later shown/assumed to fit in 16 bits */
+    int maxX = s.fBitmap->width() - 1;
+    int maxY = s.fBitmap->height() - 1;
+
+    SkPerspIter   iter(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf, count);
+
+    while ((count = iter.next()) != 0) {
+        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+
+#if defined(DEBUG_PERSP_NOFILTER)
+    /* debugging stuff */
+    const SkFixed *end_srcXY = srcXY + (count*2);
+    uint32_t *end_xy = xy + (count);
+    const SkFixed *base_srcXY = srcXY;
+    uint32_t *base_xy = xy;
+    int base_count = count;
+#endif
+
+#if 1
+        // 2009/9/30: crashes in ApiDemos - Views - Animation - 3D Transition
+    // 2009/10/9: reworked to avoid illegal (but allowed by gas) insn
+
+        /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1...
+         * but we immediately discard the low 16 bits...
+         * so what we're going to do is vld4, which will give us
+         * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo'
+         * parts....
+         */
+        if (count >= 8) {
+            int16_t *mysrc = (int16_t *) srcXY;
+            int16_t *mydst = (int16_t *) xy;
+            int16x4_t maxX4 = vdup_n_s16((int16_t)maxX);
+            int16x4_t maxY4 = vdup_n_s16((int16_t)maxY);
+            int16x4_t zero4 = vdup_n_s16(0);
+
+        /* The constructs with local blocks for register assignments
+         * and asm() instructions is to make keep any hard register
+         * assignments to as small a scope as possible. and to avoid
+         * burning call-preserved hard registers on the vld/vst
+         * instructions.
+         */
+
+            do {
+                int16x4_t xlo, xhi, ylo, yhi;
+                int16x4_t x2lo, x2hi, y2lo, y2hi;
+
+                /* vld4 does the de-interleaving for us */
+        {
+                    register int16x4_t t_xlo asm("d0");
+                    register int16x4_t t_xhi asm("d1");
+                    register int16x4_t t_ylo asm("d2");
+                    register int16x4_t t_yhi asm("d3");
+
+                    asm ("vld4.16    {d0-d3},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
+                        : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
+                        : "r" (mysrc)
+                    );
+            xlo = t_xlo;
+            xhi = t_xhi;
+            ylo = t_ylo;
+            yhi = t_yhi;
+        }
+
+                /* clamp X>>16 (aka xhi) to 0..maxX */
+                xhi = vmax_s16(xhi, zero4);    /* now 0.. */
+                xhi = vmin_s16(xhi, maxX4);    /* now 0..maxX */
+
+                /* clamp Y>>16 (aka yhi) to 0..maxY */
+                yhi = vmax_s16(yhi, zero4);    /* now 0.. */
+                yhi = vmin_s16(yhi, maxY4);    /* now 0..maxY */
+
+        /* deal with the second set of numbers */
+        {
+                    register int16x4_t t_xlo asm("d4");
+                    register int16x4_t t_xhi asm("d5");
+                    register int16x4_t t_ylo asm("d6");
+                    register int16x4_t t_yhi asm("d7");
+
+                    /* offset == 256 bits == 32 bytes == 8 longs == 16 shorts */
+                    asm ("vld4.16    {d4-d7},[%4]  /* xlo=%P0 xhi=%P1 ylo=%P2 yhi=%P3 */"
+                        : "=w" (t_xlo), "=w" (t_xhi), "=w" (t_ylo), "=w" (t_yhi)
+                        : "r" (mysrc+16)
+                    );
+            x2lo = t_xlo;
+            x2hi = t_xhi;
+            y2lo = t_ylo;
+            y2hi = t_yhi;
+        }
+
+                /* clamp the second 4 here */
+
+        if (0) { extern void rbe(void); rbe(); }
+
+                /* clamp X>>16 (aka xhi) to 0..maxX */
+                x2hi = vmax_s16(x2hi, zero4);    /* now 0.. */
+                x2hi = vmin_s16(x2hi, maxX4);    /* now 0..maxX */
+
+                /* clamp Y>>16 (aka yhi) to 0..maxY */
+                y2hi = vmax_s16(y2hi, zero4);    /* now 0.. */
+                y2hi = vmin_s16(y2hi, maxY4);    /* now 0..maxY */
+
+                /* we're storing as {x,y}s: x is [0], y is [1] */
+                /* we'll use vst2 to make this happen */
+
+        {
+                    register int16x4_t out_x asm("d16") = xhi;
+                    register int16x4_t out_y asm("d17") = yhi;
+
+                    asm ("vst2.16    {d16-d17},[%2]  /* xlo=%P0 xhi=%P1 */"
+            :
+            : "w" (out_x), "w" (out_y), "r" (mydst)
+            );
+        }
+        {
+                    register int16x4_t out_x asm("d18") = x2hi;
+                    register int16x4_t out_y asm("d19") = y2hi;
+
+                    asm ("vst2.16    {d18-d19},[%2]  /* xlo=%P0 xhi=%P1 */"
+            :
+            : "w" (out_x), "w" (out_y), "r" (mydst+8)
+            );
+        }
+
+                /* XXX: gcc isn't interleaving these with the NEON ops
+                 * but i think that all the scoreboarding works out */
+                count -= 8;    /* 8 iterations */
+                mysrc += 32;    /* 16 longs, aka 32 shorts */
+                mydst += 16;    /* 16 shorts, aka 8 longs */
+            } while (count >= 8);
+            /* get xy and srcXY fixed up */
+            srcXY = (const SkFixed *) mysrc;
+            xy = (uint32_t *) mydst;
+        }
+#endif
+
+        while (--count >= 0) {
+            *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
+                     TILEX_PROCF(srcXY[0], maxX);
+            srcXY += 2;
+        }
+
+#if defined(DEBUG_PERSP_NOFILTER)
+    /* for checking our NEON-produced results against vanilla code */
+    {
+        int bad = (-1);
+        for (int i = 0; i < base_count; i++) {
+            uint32_t val;
+            val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
+                    TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);
+
+            if (val != base_xy[i]) {
+                bad = i;
+                break;
+            }
+        }
+        if (bad >= 0) {
+            SkDebugf("clamp-nofilter-persp failed piece %d\n", bad);
+            SkDebugf("    maxX %08x maxY %08x\n", maxX, maxY);
+            bad -= (bad & 0x7);           /* align */
+            for (int i = bad; i < bad + 8; i++) {
+                uint32_t val;
+                val = (TILEY_PROCF (base_srcXY[i * 2 + 1], maxY) << 16) |
+                TILEX_PROCF (base_srcXY[i * 2 + 0], maxX);
+
+                SkDebugf("%d: got %08x want %08x srcXY[0] %08x srcXY[1] %08x\n",
+                          i, base_xy[i], val, base_srcXY[i * 2 + 0],
+                 base_srcXY[i * 2 + 1]);
+            }
+            SkDebugf ("---\n");
+        }
+
+        if (end_xy != xy) {
+            SkDebugf("xy ended at %08x, should be %08x\n", xy, end_xy);
+        }
+        if (end_srcXY != srcXY) {
+            SkDebugf("srcXY ended at %08x, should be %08x\n", srcXY,
+                      end_srcXY);
+        }
+    }
+#endif
+    }
+}
+
+#undef    DEBUG_PERSP_NOFILTER
+
+//////////////////////////////////////////////////////////////////////////////
+
+static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
+                                          SkFixed one PREAMBLE_PARAM_Y) {
+    unsigned i = TILEY_PROCF(f, max);
+    i = (i << 4) | TILEY_LOW_BITS(f, max);
+    return (i << 14) | (TILEY_PROCF((f + one), max));
+}
+
+static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
+                                          SkFixed one PREAMBLE_PARAM_X) {
+    unsigned i = TILEX_PROCF(f, max);
+    i = (i << 4) | TILEX_LOW_BITS(f, max);
+    return (i << 14) | (TILEX_PROCF((f + one), max));
+}
+
+static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+
+    PREAMBLE(s);
+
+    const unsigned maxX = s.fBitmap->width() - 1;
+    const SkFixed one = s.fFilterOneX;
+    const SkFixed dx = s.fInvSx;
+    SkFixed fx;
+
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        // compute our two Y values up front
+        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
+        // now initialize fx
+        fx = SkScalarToFixed(pt.fX) - (one >> 1);
+    }
+
+#ifdef CHECK_FOR_DECAL
+    // test if we don't need to apply the tile proc
+    if (dx > 0 &&
+            (unsigned)(fx >> 16) <= maxX &&
+            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
+        decal_filter_scale_neon(xy, fx, dx, count);
+    } else
+#endif
+
+    if (count >= 4) {
+        int32x4_t wide_dx, wide_one;
+        int32x4_t wide_fx, wide_fx1, wide_i, wide_lo;
+    #if 0
+        /* verification hooks -- see below */
+        SkFixed debug_fx = fx;
+        int count_done = 0;
+    #endif
+
+        wide_fx = vdupq_n_s32(fx);
+        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
+        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
+        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
+
+        wide_dx = vdupq_n_s32(dx);
+        wide_one = vdupq_n_s32(one);
+
+        while (count >= 4) {
+            /* original expands to:
+             * unsigned i = SkClampMax((f) >> 16, max);
+             * i = (i << 4) | (((f) >> 12) & 0xF);
+             * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
+             */
+
+            /* i = SkClampMax(f>>16, maxX) */
+            wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
+            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));
+
+            /* i<<4 | TILEX_LOW_BITS(fx) */
+            wide_lo = vshrq_n_s32(wide_fx, 12);
+            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
+
+            /* i<<14 */
+            wide_i = vshlq_n_s32(wide_i, 14);
+
+            /* SkClampMax(((f + one)) >> 16, max) */
+            wide_fx1 = vaddq_s32(wide_fx, wide_one);
+            wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
+            wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));
+
+            /* final combination */
+            wide_i = vorrq_s32(wide_i, wide_fx1);
+
+            vst1q_u32(xy, vreinterpretq_u32_s32(wide_i));
+
+    #if 0
+            /* having a verification hook is a good idea */
+            /* use debug_fx, debug_fx+dx, etc. */
+
+            for (int i=0;i<4;i++) {
+            uint32_t want = PACK_FILTER_X_NAME(debug_fx, maxX, one PREAMBLE_ARG_X);
+                    if (xy[i] != want)
+                {
+                /* print a nastygram */
+                SkDebugf("clamp-filter-scale fails\n");
+                SkDebugf("got %08x want %08x\n", xy[i], want);
+                SkDebugf("fx %08x debug_fx %08x dx %08x done %d\n",
+                fx, debug_fx, dx, count_done);
+                SkDebugf(" maxX %08x one %08x\n", maxX, one);
+
+                }
+            debug_fx += dx;
+            count_done++;
+            }
+    #endif
+            wide_fx += vdupq_n_s32(dx+dx+dx+dx);
+            fx += dx+dx+dx+dx;
+            xy += 4;
+            count -= 4;
+        }
+    }
+
+    while (--count >= 0) {
+        *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
+        fx += dx;
+    }
+}
+
+static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
+                               uint32_t xy[], int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask |
+                             SkMatrix::kAffine_Mask)) == 0);
+
+    PREAMBLE(s);
+    SkPoint srcPt;
+    s.fInvProc(*s.fInvMatrix,
+               SkIntToScalar(x) + SK_ScalarHalf,
+               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+    SkFixed oneX = s.fFilterOneX;
+    SkFixed oneY = s.fFilterOneY;
+    SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
+    SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
+    SkFixed dx = s.fInvSx;
+    SkFixed dy = s.fInvKy;
+    unsigned maxX = s.fBitmap->width() - 1;
+    unsigned maxY = s.fBitmap->height() - 1;
+
+    if (count >= 4) {
+        int32x4_t wide_one, wide_i, wide_lo;
+        int32x4_t wide_dx, wide_fx, wide_onex, wide_fx1;
+        int32x4_t wide_dy, wide_fy, wide_oney, wide_fy1;
+
+    #undef    AFFINE_DEBUG
+    #if    defined(AFFINE_DEBUG)
+        SkFixed fyp = fy;
+        SkFixed fxp = fx;
+        uint32_t *xyp = xy;
+        int count_done = 0;
+    #endif
+
+        wide_fx = vdupq_n_s32(fx);
+        wide_fx = vsetq_lane_s32(fx+dx, wide_fx, 1);
+        wide_fx = vsetq_lane_s32(fx+dx+dx, wide_fx, 2);
+        wide_fx = vsetq_lane_s32(fx+dx+dx+dx, wide_fx, 3);
+        wide_dx = vdupq_n_s32(dx);
+
+        wide_fy = vdupq_n_s32(fy);
+        wide_fy = vsetq_lane_s32(fy+dy, wide_fy, 1);
+        wide_fy = vsetq_lane_s32(fy+dy+dy, wide_fy, 2);
+        wide_fy = vsetq_lane_s32(fy+dy+dy+dy, wide_fy, 3);
+        wide_dy = vdupq_n_s32(dy);
+
+        wide_onex = vdupq_n_s32(oneX);
+        wide_oney = vdupq_n_s32(oneY);
+
+        while (count >= 4) {
+            int32x4_t wide_x;
+            int32x4_t wide_y;
+
+            /* do the X side, then the Y side, then interleave them */
+
+            /* original expands to:
+             * unsigned i = SkClampMax((f) >> 16, max);
+             * i = (i << 4) | (((f) >> 12) & 0xF);
+             * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
+             */
+
+            /* i = SkClampMax(f>>16, maxX) */
+            wide_i = vmaxq_s32(vshrq_n_s32(wide_fx,16), vdupq_n_s32(0));
+            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxX));
+
+            /* i<<4 | TILEX_LOW_BITS(fx) */
+            wide_lo = vshrq_n_s32(wide_fx, 12);
+            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
+
+            /* i<<14 */
+            wide_i = vshlq_n_s32(wide_i, 14);
+
+            /* SkClampMax(((f + one)) >> 16, max) */
+            wide_fx1 = vaddq_s32(wide_fx, wide_onex);
+            wide_fx1 = vmaxq_s32(vshrq_n_s32(wide_fx1,16), vdupq_n_s32(0));
+            wide_fx1 = vminq_s32(wide_fx1, vdupq_n_s32(maxX));
+
+            /* final combination */
+            wide_x = vorrq_s32(wide_i, wide_fx1);
+
+            /* And now the Y side */
+
+            /* i = SkClampMax(f>>16, maxX) */
+            wide_i = vmaxq_s32(vshrq_n_s32(wide_fy,16), vdupq_n_s32(0));
+            wide_i = vminq_s32(wide_i, vdupq_n_s32(maxY));
+
+            /* i<<4 | TILEX_LOW_BITS(fx) */
+            wide_lo = vshrq_n_s32(wide_fy, 12);
+            wide_i = vsliq_n_s32(wide_lo, wide_i, 4);
+
+            /* i<<14 */
+            wide_i = vshlq_n_s32(wide_i, 14);
+
+            /* SkClampMax(((f + one)) >> 16, max) */
+            wide_fy1 = vaddq_s32(wide_fy, wide_oney);
+            wide_fy1 = vmaxq_s32(vshrq_n_s32(wide_fy1,16), vdupq_n_s32(0));
+            wide_fy1 = vminq_s32(wide_fy1, vdupq_n_s32(maxY));
+
+            /* final combination */
+            wide_y = vorrq_s32(wide_i, wide_fy1);
+
+            /* interleave as YXYXYXYX as part of the storing */
+        {
+                /* vst2.32 needs side-by-side registers */
+                register int32x4_t t_x asm("q1");
+                register int32x4_t t_y asm("q0");
+
+        t_x = wide_x; t_y = wide_y;
+                asm ("vst2.32    {q0-q1},[%2]  /* y=%q0 x=%q1 */"
+                    :
+                    : "w" (t_y), "w" (t_x), "r" (xy)
+                    );
+        }
+
+    #if    defined(AFFINE_DEBUG)
+            /* make sure we're good here -- check the 4 we just output */
+            for (int i = 0; i<4;i++) {
+            uint32_t val;
+            val = PACK_FILTER_Y_NAME(fyp, maxY, oneY PREAMBLE_ARG_Y);
+            if (val != xy[i*2+0]) {
+                /* print a nastygram */
+                SkDebugf("clamp-filter-affine fails\n");
+                SkDebugf("[bad-y] got %08x want %08x\n", xy[i*2+0], val);
+                SkDebugf("fy %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
+                fy, fxp, fyp, dx, dy, count_done);
+                SkDebugf(" maxY %08x oneY %08x\n", maxY, oneY);
+                }
+            val = PACK_FILTER_X_NAME(fxp, maxX, oneX PREAMBLE_ARG_X);
+            if (val != xy[i*2+1]) {
+                /* print a nastygram */
+                SkDebugf("clamp-filter-affine fails\n");
+                SkDebugf("[bad-x] got %08x want %08x\n", xy[i*2+1], val);
+                SkDebugf("fx %08x fxp %08x fyp %08x dx %08x dy %08x done %d\n",
+                fx, fxp, fyp, dx, dy, count_done);
+                SkDebugf(" maxX %08x one %08x\n", maxX, oneX);
+            }
+            fyp += dy;
+            fxp += dx;
+            count_done++;
+            }
+    #endif
+
+            wide_fx += vdupq_n_s32(dx+dx+dx+dx);
+            fx += dx+dx+dx+dx;
+            wide_fy += vdupq_n_s32(dy+dy+dy+dy);
+            fy += dy+dy+dy+dy;
+            xy += 8;        /* 4 x's, 4 y's */
+            count -= 4;
+        }
+    }
+
+    while (--count >= 0) {
+        /* NB: writing Y/X */
+        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
+        fy += dy;
+        *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
+        fx += dx;
+    }
+}
+
+static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t* SK_RESTRICT xy, int count,
+                              int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+
+    PREAMBLE(s);
+    unsigned maxX = s.fBitmap->width() - 1;
+    unsigned maxY = s.fBitmap->height() - 1;
+    SkFixed oneX = s.fFilterOneX;
+    SkFixed oneY = s.fFilterOneY;
+
+    SkPerspIter   iter(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf, count);
+
+    while ((count = iter.next()) != 0) {
+        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+
+        if (count >= 4) {
+            int32x4_t wide_one, wide_i, wide_lo;
+            int32x4_t wide_fx1;
+            int32x4_t wide_fy1;
+            int32x4_t wide_x, wide_y;
+
+            while (count >= 4) {
+                /* RBE: it's good, but:
+                 * -- we spill a constant that could be easily regnerated
+                 *    [perhaps tweak gcc's NEON constant costs?]
+                 */
+
+                /* load src:  x-y-x-y-x-y-x-y */
+        {
+            register int32x4_t q0 asm ("q0");
+            register int32x4_t q1 asm ("q1");
+                    asm ("vld2.32    {q0-q1},[%2]  /* x=%q0 y=%q1 */"
+                         : "=w" (q0), "=w" (q1)
+                         : "r" (srcXY));
+            wide_x = q0; wide_y = q1;
+        }
+
+                /* do the X side, then the Y side, then interleave them */
+
+                wide_x = vsubq_s32(wide_x, vdupq_n_s32 (oneX>>1));
+
+                /* original expands to:
+                 * unsigned i = SkClampMax((f) >> 16, max);
+                 * i = (i << 4) | (((f) >> 12) & 0xF);
+                 * return (i << 14) | (SkClampMax(((f + one)) >> 16, max));
+                 */
+
+                /* i = SkClampMax(f>>16, maxX) */
+                wide_i = vmaxq_s32 (vshrq_n_s32 (wide_x, 16), vdupq_n_s32 (0));
+                wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxX));
+
+                /* i<<4 | TILEX_LOW_BITS(fx) */
+                wide_lo = vshrq_n_s32 (wide_x, 12);
+                wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);
+
+                /* i<<14 */
+                wide_i = vshlq_n_s32 (wide_i, 14);
+
+                /* SkClampMax(((f + one)) >> 16, max) */
+                wide_fx1 = vaddq_s32 (wide_x, vdupq_n_s32(oneX));
+                wide_fx1 = vmaxq_s32 (vshrq_n_s32 (wide_fx1, 16), vdupq_n_s32 (0));
+                wide_fx1 = vminq_s32 (wide_fx1, vdupq_n_s32 (maxX));
+
+                /* final combination */
+                wide_x = vorrq_s32 (wide_i, wide_fx1);
+
+
+                /* And now the Y side */
+
+                wide_y = vsubq_s32(wide_y, vdupq_n_s32 (oneY>>1));
+
+                /* i = SkClampMax(f>>16, maxX) */
+                wide_i = vmaxq_s32 (vshrq_n_s32 (wide_y, 16), vdupq_n_s32 (0));
+                wide_i = vminq_s32 (wide_i, vdupq_n_s32 (maxY));
+
+                /* i<<4 | TILEX_LOW_BITS(fx) */
+                wide_lo = vshrq_n_s32 (wide_y, 12);
+                wide_i = vsliq_n_s32 (wide_lo, wide_i, 4);
+
+                /* i<<14 */
+                wide_i = vshlq_n_s32 (wide_i, 14);
+
+                /* SkClampMax(((f + one)) >> 16, max) */
+
+                /* wide_fy1_1 and wide_fy1_2 are just temporary variables to
+                 * work-around an ICE in debug */
+                int32x4_t wide_fy1_1 = vaddq_s32 (wide_y, vdupq_n_s32(oneY));
+                int32x4_t wide_fy1_2 = vmaxq_s32 (vshrq_n_s32 (wide_fy1_1, 16),
+                                                  vdupq_n_s32 (0));
+                wide_fy1 = vminq_s32 (wide_fy1_2, vdupq_n_s32 (maxY));
+
+                /* final combination */
+                wide_y = vorrq_s32 (wide_i, wide_fy1);
+
+                /* switch them around; have to do it this way to get them
+                 * in the proper registers to match our instruction */
+
+                /* iteration bookkeeping, ahead of the asm() for scheduling */
+                srcXY += 2*4;
+                count -= 4;
+
+                /* store interleaved as y-x-y-x-y-x-y-x (NB != read order) */
+        {
+            register int32x4_t q0 asm ("q0") = wide_y;
+            register int32x4_t q1 asm ("q1") = wide_x;
+
+                    asm ("vst2.32    {q0-q1},[%2]  /* y=%q0 x=%q1 */"
+                        :
+                        : "w" (q0), "w" (q1), "r" (xy));
+        }
+
+                /* on to the next iteration */
+                /* count, srcXY are handled above */
+                xy += 2*4;
+            }
+        }
+
+        /* was do-while; NEON code invalidates original count>0 assumption */
+        while (--count >= 0) {
+        /* NB: we read x/y, we write y/x */
+            *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
+                                       oneY PREAMBLE_ARG_Y);
+            *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
+                                       oneX PREAMBLE_ARG_X);
+            srcXY += 2;
+        }
+    }
+}
+
+const SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
+    SCALE_NOFILTER_NAME,
+    SCALE_FILTER_NAME,
+    AFFINE_NOFILTER_NAME,
+    AFFINE_FILTER_NAME,
+    PERSP_NOFILTER_NAME,
+    PERSP_FILTER_NAME
+};
+
+#undef MAKENAME
+#undef TILEX_PROCF
+#undef TILEY_PROCF
+#ifdef CHECK_FOR_DECAL
+    #undef CHECK_FOR_DECAL
+#endif
+
+#undef SCALE_NOFILTER_NAME
+#undef SCALE_FILTER_NAME
+#undef AFFINE_NOFILTER_NAME
+#undef AFFINE_FILTER_NAME
+#undef PERSP_NOFILTER_NAME
+#undef PERSP_FILTER_NAME
+
+#undef PREAMBLE
+#undef PREAMBLE_PARAM_X
+#undef PREAMBLE_PARAM_Y
+#undef PREAMBLE_ARG_X
+#undef PREAMBLE_ARG_Y
+
+#undef TILEX_LOW_BITS
+#undef TILEY_LOW_BITS
diff --git a/src/opts/SkBitmapProcState_matrix_repeat_neon.h b/src/opts/SkBitmapProcState_matrix_repeat_neon.h
new file mode 100644
index 0000000..615f3ff
--- /dev/null
+++ b/src/opts/SkBitmapProcState_matrix_repeat_neon.h
@@ -0,0 +1,544 @@
+/* NEON optimized code (C) COPYRIGHT 2009 Motorola
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/*
+ * Modifications done in-house at Motorola
+ *
+ * this is a clone of SkBitmapProcState_matrix.h
+ * and has been tuned to work with the NEON unit.
+ *
+ * Still going back and forth between whether this approach
+ * (clone the entire SkBitmapProcState_matrix.h file or
+ * if I should put just the modified routines in here and
+ * then use a construct like #define DONT_DO_THIS_FUNCTION or
+ * something like that...
+ *
+ * This is for the RepeatX_RepeatY part of the world
+ */
+
+
+#include <arm_neon.h>
+
+/*
+ * This has been modified on the knowledge that (at the time)
+ * we had the following macro definitions in the parent file
+ *
+ * #define MAKENAME(suffix)        RepeatX_RepeatY ## suffix
+ * #define TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
+ * #define TILEY_PROCF(fy, max)    (((fy) & 0xFFFF) * ((max) + 1) >> 16)
+ * #define TILEX_LOW_BITS(fx, max) ((((fx) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+ * #define TILEY_LOW_BITS(fy, max) ((((fy) & 0xFFFF) * ((max) + 1) >> 12) & 0xF)
+ */
+
+/* SkClampMax(val,max) -- bound to 0..max */
+
+#define SCALE_NOFILTER_NAME     MAKENAME(_nofilter_scale)
+#define SCALE_FILTER_NAME       MAKENAME(_filter_scale)
+#define AFFINE_NOFILTER_NAME    MAKENAME(_nofilter_affine)
+#define AFFINE_FILTER_NAME      MAKENAME(_filter_affine)
+#define PERSP_NOFILTER_NAME     MAKENAME(_nofilter_persp)
+#define PERSP_FILTER_NAME       MAKENAME(_filter_persp)
+
+#define PACK_FILTER_X_NAME  MAKENAME(_pack_filter_x)
+#define PACK_FILTER_Y_NAME  MAKENAME(_pack_filter_y)
+
+#ifndef PREAMBLE
+    #define PREAMBLE(state)
+    #define PREAMBLE_PARAM_X
+    #define PREAMBLE_PARAM_Y
+    #define PREAMBLE_ARG_X
+    #define PREAMBLE_ARG_Y
+#endif
+
+static void SCALE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+
+    PREAMBLE(s);
+    // we store y, x, x, x, x, x
+
+    const unsigned maxX = s.fBitmap->width() - 1;
+    SkFixed fx;
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        fx = SkScalarToFixed(pt.fY);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        *xy++ = TILEY_PROCF(fx, maxY);
+        fx = SkScalarToFixed(pt.fX);
+    }
+
+    if (0 == maxX) {
+        // all of the following X values must be 0
+        memset(xy, 0, count * sizeof(uint16_t));
+        return;
+    }
+
+    const SkFixed dx = s.fInvSx;
+
+#ifdef CHECK_FOR_DECAL
+    // test if we don't need to apply the tile proc
+    if ((unsigned)(fx >> 16) <= maxX &&
+        (unsigned)((fx + dx * (count - 1)) >> 16) <= maxX) {
+        decal_nofilter_scale_neon(xy, fx, dx, count);
+    } else
+#endif
+    {
+        int i;
+
+    /* RBE: very much like done in decal_nofilter ,
+     * but some processing of the 'fx' information
+         * TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
+     */
+    if (count >= 8) {
+        /* SkFixed is 16.16 fixed point */
+        SkFixed dx2 = dx+dx;
+        SkFixed dx4 = dx2+dx2;
+        SkFixed dx8 = dx4+dx4;
+
+        /* now build fx/fx+dx/fx+2dx/fx+3dx */
+        SkFixed fx1, fx2, fx3;
+        int32x2_t lower, upper;
+        int32x4_t lbase, hbase;
+        int16_t *dst16 = (int16_t *)xy;
+
+        fx1 = fx+dx;
+        fx2 = fx1+dx;
+        fx3 = fx2+dx;
+
+        lbase = vdupq_n_s32(fx);
+        lbase = vsetq_lane_s32(fx1, lbase, 1);
+        lbase = vsetq_lane_s32(fx2, lbase, 2);
+        lbase = vsetq_lane_s32(fx3, lbase, 3);
+        hbase = vaddq_s32(lbase, vdupq_n_s32(dx4));
+
+        /* store & bump */
+        do
+        {
+            int32x4_t lout;
+        int32x4_t hout;
+        int16x8_t hi16;
+
+             /* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */
+        /* mask to low 16 [would like to use uzp tricks) */
+            lout = vandq_s32(lbase, vdupq_n_s32(0xffff));
+            hout = vandq_s32(hbase, vdupq_n_s32(0xffff));
+        /* bare multiplication, not SkFixedMul */
+        lout = vmulq_s32(lout, vdupq_n_s32(maxX+1));
+        hout = vmulq_s32(hout, vdupq_n_s32(maxX+1));
+
+        /* extraction, using uzp */
+        /* this is ok -- we want all hi(lout)s then all hi(hout)s */
+        asm ("vuzpq.16 %q0, %q1" : "+w" (lout), "+w" (hout));
+        hi16 = vreinterpretq_s16_s32(hout);
+        vst1q_s16(dst16, hi16);
+
+        /* bump our base on to the next */
+        lbase = vaddq_s32 (lbase, vdupq_n_s32(dx8));
+        hbase = vaddq_s32 (hbase, vdupq_n_s32(dx8));
+        dst16 += 8;
+        count -= 8;
+        fx += dx8;
+        } while (count >= 8);
+        xy = (uint32_t *) dst16;
+    }
+        uint16_t* xx = (uint16_t*)xy;
+        for (i = count; i > 0; --i) {
+            *xx++ = TILEX_PROCF(fx, maxX); fx += dx;
+        }
+    }
+}
+
+// note: we could special-case on a matrix which is skewed in X but not Y.
+// this would require a more general setup thatn SCALE does, but could use
+// SCALE's inner loop that only looks at dx
+
+
+static void AFFINE_NOFILTER_NAME(const SkBitmapProcState& s,
+                                 uint32_t xy[], int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask |
+                             SkMatrix::kAffine_Mask)) == 0);
+
+    PREAMBLE(s);
+    SkPoint srcPt;
+    s.fInvProc(*s.fInvMatrix,
+               SkIntToScalar(x) + SK_ScalarHalf,
+               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+    SkFixed fx = SkScalarToFixed(srcPt.fX);
+    SkFixed fy = SkScalarToFixed(srcPt.fY);
+    SkFixed dx = s.fInvSx;
+    SkFixed dy = s.fInvKy;
+    int maxX = s.fBitmap->width() - 1;
+    int maxY = s.fBitmap->height() - 1;
+
+#if 1
+    int ocount = count;
+    uint32_t *oxy = xy;
+    SkFixed bfx = fx, bfy=fy, bdx=dx, bdy=dy;
+#endif
+
+
+    if (0) { extern void rbe(void); rbe(); }
+
+    /* RBE: benchmarks show this eats up time; can we neonize it? */
+    /* RBE: very much like done in decal_nofilter ,
+     * but some processing of the 'fx' information
+         * TILEX_PROCF(fx, max)    (((fx) & 0xFFFF) * ((max) + 1) >> 16)
+     */
+    if (count >= 4) {
+        /* SkFixed is 16.16 fixed point */
+        SkFixed dx4 = dx*4;
+        SkFixed dy4 = dy*4;
+
+        /* now build fx/fx+dx/fx+2dx/fx+3dx */
+        int32x2_t lower, upper;
+        int32x4_t xbase, ybase;
+        int16_t *dst16 = (int16_t *)xy;
+
+        /* synthesize 4x for both X and Y */
+        xbase = vdupq_n_s32(fx);
+        xbase = vsetq_lane_s32(fx+dx, xbase, 1);
+        xbase = vsetq_lane_s32(fx+dx+dx, xbase, 2);
+        xbase = vsetq_lane_s32(fx+dx+dx+dx, xbase, 3);
+
+        ybase = vdupq_n_s32(fy);
+        ybase = vsetq_lane_s32(fy+dy, ybase, 1);
+        ybase = vsetq_lane_s32(fy+dy+dy, ybase, 2);
+        ybase = vsetq_lane_s32(fy+dy+dy+dy, ybase, 3);
+
+        /* store & bump */
+        do {
+            int32x4_t xout;
+            int32x4_t yout;
+            int16x8_t hi16;
+
+             /* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */
+        /* mask to low 16 [would like to use uzp tricks) */
+            xout = vandq_s32(xbase, vdupq_n_s32(0xffff));
+            yout = vandq_s32(ybase, vdupq_n_s32(0xffff));
+        /* bare multiplication, not SkFixedMul */
+        xout = vmulq_s32(xout, vdupq_n_s32(maxX+1));
+        yout = vmulq_s32(yout, vdupq_n_s32(maxY+1));
+
+        /* put hi16 from xout over low16 from yout */
+        yout = vsriq_n_s32(yout, xout, 16);
+
+        /* and then yout has the interleaved upper 16's */
+        hi16 = vreinterpretq_s16_s32(yout);
+        vst1q_s16(dst16, hi16);
+
+        /* bump preserved base & on to the next */
+        xbase = vaddq_s32 (xbase, vdupq_n_s32(dx4));
+        ybase = vaddq_s32 (ybase, vdupq_n_s32(dy4));
+        dst16 += 8;    /* 8 x16 aka 4x32 */
+        count -= 4;
+        fx += dx4;
+        fy += dy4;
+        } while (count >= 4);
+        xy = (uint32_t *) dst16;
+    }
+
+#if 0
+    /* diagnostics... see whether we agree with the NEON code */
+    int bad = 0;
+    uint32_t *myxy = oxy;
+    int myi = (-1);
+    SkFixed ofx = bfx, ofy= bfy, odx= bdx, ody= bdy;
+    for (myi = ocount; myi > 0; --myi) {
+    uint32_t val = (TILEY_PROCF(ofy, maxY) << 16) | TILEX_PROCF(ofx, maxX);
+    if (val != *myxy++) {
+        bad++;
+        break;
+    }
+        ofx += odx; ofy += ody;
+    }
+    if (bad) {
+        SkDebugf("repeat-nofilter-affine fails\n");
+        SkDebugf("count %d myi %d\n", ocount, myi);
+        SkDebugf(" bfx %08x, bdx %08x, bfy %08x bdy %08x\n",
+                bfx, bdx, bfy, bdy);
+        SkDebugf("maxX %08x maxY %08x\n", maxX, maxY);
+    }
+#endif
+
+    for (int i = count; i > 0; --i) {
+    /* fx, fy, dx, dy are all 32 bit 16.16 fixed point */
+    /* (((fx) & 0xFFFF) * ((max) + 1) >> 16) */
+        *xy++ = (TILEY_PROCF(fy, maxY) << 16) | TILEX_PROCF(fx, maxX);
+        fx += dx; fy += dy;
+    }
+}
+
+static void PERSP_NOFILTER_NAME(const SkBitmapProcState& s,
+                                uint32_t* SK_RESTRICT xy,
+                                int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+
+    PREAMBLE(s);
+    int maxX = s.fBitmap->width() - 1;
+    int maxY = s.fBitmap->height() - 1;
+
+    SkPerspIter   iter(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf, count);
+
+    while ((count = iter.next()) != 0) {
+        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+
+    /* RBE: */
+    /* TILEX_PROCF(fx, max) (((fx) & 0xFFFF) * ((max) + 1) >> 16) */
+    /* it's a little more complicated than what I did for the
+     * clamp case -- where I could immediately snip to the top
+     * 16 bits and do my min/max games there.
+     * ... might only be able to get 4x unrolling here
+     */
+
+    /* vld2 to get a set of 32x4's ... */
+    /* do the tile[xy]_procf operations */
+    /* which includes doing vuzp to get hi16's */
+    /* store it */
+    /* -- inner loop (other than vld2) can be had from above */
+
+    /* srcXY is a batch of 32 bit numbers X0,Y0,X1,Y1...
+     * but we immediately discard the low 16 bits...
+     * so what we're going to do is vld4, which will give us
+     * xlo,xhi,ylo,yhi distribution and we can ignore the 'lo'
+     * parts....
+     */
+    if (0) { extern void rbe(void); rbe(); }
+    if (count >= 8) {
+        int32_t *mysrc = (int32_t *) srcXY;
+        int16_t *mydst = (int16_t *) xy;
+        do {
+        int32x4_t x, y, x2, y2;
+        int16x8_t hi, hi2;
+
+        /* read array of x,y,x,y,x,y */
+            /* vld2 does the de-interleaving for us */
+        /* isolate reg-bound scopes; gcc will minimize register
+         * motion if possible; this ensures that we don't lose
+         * a register across a debugging call because it happens
+         * to be bound into a call-clobbered register
+         */
+        {
+            register int32x4_t q0 asm("q0");
+            register int32x4_t q1 asm("q1");
+            asm ("vld2.32    {q0-q1},[%2]  /* x=%q0 y=%q1 */"
+                : "=w" (q0), "=w" (q1)
+                : "r" (mysrc)
+                );
+            x = q0; y = q1;
+        }
+
+        /* offset == 256 bits == 32 bytes == 8 longs */
+        {
+            register int32x4_t q2 asm("q2");
+            register int32x4_t q3 asm("q3");
+            asm ("vld2.32    {q2-q3},[%2]  /* x=%q0 y=%q1 */"
+                : "=w" (q2), "=w" (q3)
+                : "r" (mysrc+8)
+                );
+            x2 = q2; y2 = q3;
+        }
+
+             /* TILEX_PROCF(fx, max) (((fx)&0xFFFF)*((max)+1)>> 16) */
+        /* mask to low 16 [would like to use uzp tricks) */
+        /* bare multiplication, not SkFixedMul */
+            x = vandq_s32(x, vdupq_n_s32(0xffff));
+        x = vmulq_s32(x, vdupq_n_s32(maxX+1));
+            y = vandq_s32(y, vdupq_n_s32(0xffff));
+        y = vmulq_s32(y, vdupq_n_s32(maxY+1));
+
+            x2 = vandq_s32(x2, vdupq_n_s32(0xffff));
+        x2 = vmulq_s32(x2, vdupq_n_s32(maxX+1));
+            y2 = vandq_s32(y2, vdupq_n_s32(0xffff));
+        y2 = vmulq_s32(y2, vdupq_n_s32(maxY+1));
+
+        /* now collect interleaved high 16's */
+        /* (hi-x, hi-y)4  (hi-x2; hi-y2)4 */
+
+        /* extraction, using uzp, leaves hi16's in y */
+        y = vsriq_n_s32(y, x, 16);
+        hi = vreinterpretq_s16_s32(y);
+        vst1q_s16(mydst, hi);
+
+        /* and likewise for the second 8 entries */
+        y2 = vsriq_n_s32(y2, x2, 16);
+        hi2 = vreinterpretq_s16_s32(y2);
+        vst1q_s16(mydst+8, hi2);
+
+        /* XXX: gcc isn't interleaving these with the NEON ops
+         * but i think that all the scoreboarding works out */
+        count -= 8;    /* 8 iterations */
+        mysrc += 16;    /* 16 longs */
+        mydst += 16;    /* 16 shorts, aka 8 longs */
+        } while (count >= 8);
+        /* get xy and srcXY fixed up */
+        srcXY = (const SkFixed *) mysrc;
+        xy = (uint32_t *) mydst;
+    }
+        while (--count >= 0) {
+            *xy++ = (TILEY_PROCF(srcXY[1], maxY) << 16) |
+                     TILEX_PROCF(srcXY[0], maxX);
+            srcXY += 2;
+        }
+    }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+static inline uint32_t PACK_FILTER_Y_NAME(SkFixed f, unsigned max,
+                                          SkFixed one PREAMBLE_PARAM_Y) {
+    unsigned i = TILEY_PROCF(f, max);
+    i = (i << 4) | TILEY_LOW_BITS(f, max);
+    return (i << 14) | (TILEY_PROCF((f + one), max));
+}
+
+static inline uint32_t PACK_FILTER_X_NAME(SkFixed f, unsigned max,
+                                          SkFixed one PREAMBLE_PARAM_X) {
+    unsigned i = TILEX_PROCF(f, max);
+    i = (i << 4) | TILEX_LOW_BITS(f, max);
+    return (i << 14) | (TILEX_PROCF((f + one), max));
+}
+
+static void SCALE_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t xy[], int count, int x, int y) {
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask)) == 0);
+    SkASSERT(s.fInvKy == 0);
+
+    PREAMBLE(s);
+
+    const unsigned maxX = s.fBitmap->width() - 1;
+    const SkFixed one = s.fFilterOneX;
+    const SkFixed dx = s.fInvSx;
+    SkFixed fx;
+
+    {
+        SkPoint pt;
+        s.fInvProc(*s.fInvMatrix, SkIntToScalar(x) + SK_ScalarHalf,
+                                  SkIntToScalar(y) + SK_ScalarHalf, &pt);
+        const SkFixed fy = SkScalarToFixed(pt.fY) - (s.fFilterOneY >> 1);
+        const unsigned maxY = s.fBitmap->height() - 1;
+        // compute our two Y values up front
+        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, s.fFilterOneY PREAMBLE_ARG_Y);
+        // now initialize fx
+        fx = SkScalarToFixed(pt.fX) - (one >> 1);
+    }
+
+#ifdef CHECK_FOR_DECAL
+    // test if we don't need to apply the tile proc
+    if (dx > 0 &&
+            (unsigned)(fx >> 16) <= maxX &&
+            (unsigned)((fx + dx * (count - 1)) >> 16) < maxX) {
+        decal_filter_scale_neon(xy, fx, dx, count);
+    } else
+#endif
+    {
+        do {
+            *xy++ = PACK_FILTER_X_NAME(fx, maxX, one PREAMBLE_ARG_X);
+            fx += dx;
+        } while (--count != 0);
+    }
+}
+
+static void AFFINE_FILTER_NAME(const SkBitmapProcState& s,
+                               uint32_t xy[], int count, int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kAffine_Mask);
+    SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
+                             SkMatrix::kScale_Mask |
+                             SkMatrix::kAffine_Mask)) == 0);
+
+    PREAMBLE(s);
+    SkPoint srcPt;
+    s.fInvProc(*s.fInvMatrix,
+               SkIntToScalar(x) + SK_ScalarHalf,
+               SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
+
+    SkFixed oneX = s.fFilterOneX;
+    SkFixed oneY = s.fFilterOneY;
+    SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
+    SkFixed fy = SkScalarToFixed(srcPt.fY) - (oneY >> 1);
+    SkFixed dx = s.fInvSx;
+    SkFixed dy = s.fInvKy;
+    unsigned maxX = s.fBitmap->width() - 1;
+    unsigned maxY = s.fBitmap->height() - 1;
+
+    do {
+        *xy++ = PACK_FILTER_Y_NAME(fy, maxY, oneY PREAMBLE_ARG_Y);
+        fy += dy;
+        *xy++ = PACK_FILTER_X_NAME(fx, maxX, oneX PREAMBLE_ARG_X);
+        fx += dx;
+    } while (--count != 0);
+}
+
+static void PERSP_FILTER_NAME(const SkBitmapProcState& s,
+                              uint32_t* SK_RESTRICT xy, int count,
+                              int x, int y) {
+    SkASSERT(s.fInvType & SkMatrix::kPerspective_Mask);
+
+    extern void rbe(void);
+
+    PREAMBLE(s);
+    unsigned maxX = s.fBitmap->width() - 1;
+    unsigned maxY = s.fBitmap->height() - 1;
+    SkFixed oneX = s.fFilterOneX;
+    SkFixed oneY = s.fFilterOneY;
+
+
+
+    SkPerspIter   iter(*s.fInvMatrix,
+                       SkIntToScalar(x) + SK_ScalarHalf,
+                       SkIntToScalar(y) + SK_ScalarHalf, count);
+
+    while ((count = iter.next()) != 0) {
+        const SkFixed* SK_RESTRICT srcXY = iter.getXY();
+        do {
+            *xy++ = PACK_FILTER_Y_NAME(srcXY[1] - (oneY >> 1), maxY,
+                                       oneY PREAMBLE_ARG_Y);
+            *xy++ = PACK_FILTER_X_NAME(srcXY[0] - (oneX >> 1), maxX,
+                                       oneX PREAMBLE_ARG_X);
+            srcXY += 2;
+        } while (--count != 0);
+    }
+}
+
+const SkBitmapProcState::MatrixProc MAKENAME(_Procs)[] = {
+    SCALE_NOFILTER_NAME,
+    SCALE_FILTER_NAME,
+    AFFINE_NOFILTER_NAME,
+    AFFINE_FILTER_NAME,
+    PERSP_NOFILTER_NAME,
+    PERSP_FILTER_NAME
+};
+
+#undef MAKENAME
+#undef TILEX_PROCF
+#undef TILEY_PROCF
+#ifdef CHECK_FOR_DECAL
+    #undef CHECK_FOR_DECAL
+#endif
+
+#undef SCALE_NOFILTER_NAME
+#undef SCALE_FILTER_NAME
+#undef AFFINE_NOFILTER_NAME
+#undef AFFINE_FILTER_NAME
+#undef PERSP_NOFILTER_NAME
+#undef PERSP_FILTER_NAME
+
+#undef PREAMBLE
+#undef PREAMBLE_PARAM_X
+#undef PREAMBLE_PARAM_Y
+#undef PREAMBLE_ARG_X
+#undef PREAMBLE_ARG_Y
+
+#undef TILEX_LOW_BITS
+#undef TILEY_LOW_BITS
diff --git a/src/opts/SkBitmapProcState_opts_SSE2.cpp b/src/opts/SkBitmapProcState_opts_SSE2.cpp
index 1852c66..c2de259 100644
--- a/src/opts/SkBitmapProcState_opts_SSE2.cpp
+++ b/src/opts/SkBitmapProcState_opts_SSE2.cpp
@@ -57,7 +57,7 @@
 
         // (0, 0, 0, 0, 0, 0, 0, x)
         __m128i allX = _mm_cvtsi32_si128((XX >> 14) & 0x0F);
-        
+
         // (0, 0, 0, 0, x, x, x, x)
         allX = _mm_shufflelo_epi16(allX, 0);
 
@@ -167,7 +167,7 @@
 
         // (0, 0, 0, 0, 0, 0, 0, x)
         __m128i allX = _mm_cvtsi32_si128((XX >> 14) & 0x0F);
-        
+
         // (0, 0, 0, 0, x, x, x, x)
         allX = _mm_shufflelo_epi16(allX, 0);
 
@@ -248,7 +248,7 @@
     SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask |
                              SkMatrix::kScale_Mask)) == 0);
     SkASSERT(s.fInvKy == 0);
-    
+
     const unsigned maxX = s.fBitmap->width() - 1;
     const SkFixed one = s.fFilterOneX;
     const SkFixed dx = s.fInvSx;
@@ -282,14 +282,14 @@
                                               fx + dx, fx);
 
             while (count >= 4) {
-                __m128i wide_out; 
-    
+                __m128i wide_out;
+
                 wide_out = _mm_slli_epi32(_mm_srai_epi32(wide_fx, 12), 14);
                 wide_out = _mm_or_si128(wide_out, _mm_add_epi32(
-                                        _mm_srai_epi32(wide_fx, 16), wide_1)); 
-                
+                                        _mm_srai_epi32(wide_fx, 16), wide_1));
+
                 _mm_store_si128(reinterpret_cast<__m128i*>(xy), wide_out);
-      
+
                 xy += 4;
                 fx += dx * 4;
                 wide_fx  = _mm_add_epi32(wide_fx, wide_dx4);
@@ -305,7 +305,7 @@
     } else {
         // SSE2 only support 16bit interger max & min, so only process the case
         // maxX less than the max 16bit interger. Actually maxX is the bitmap's
-        // height, there should be rare bitmap whose height will be greater 
+        // height, there should be rare bitmap whose height will be greater
         // than max 16bit interger in the real world.
         if ((count >= 4) && (maxX <= 0xFFFF)) {
             while (((size_t)xy & 0x0F) != 0) {
@@ -313,12 +313,12 @@
                 fx += dx;
                 count--;
             }
-    
+
             __m128i wide_fx   = _mm_set_epi32(fx + dx * 3, fx + dx * 2,
                                               fx + dx, fx);
             __m128i wide_dx4  = _mm_set1_epi32(dx * 4);
             __m128i wide_one  = _mm_set1_epi32(one);
-            __m128i wide_maxX = _mm_set1_epi32(maxX); 
+            __m128i wide_maxX = _mm_set1_epi32(maxX);
             __m128i wide_mask = _mm_set1_epi32(0xF);
 
              while (count >= 4) {
@@ -327,31 +327,31 @@
                 __m128i wide_fx1;
 
                 // i = SkClampMax(f>>16,maxX)
-                wide_i = _mm_max_epi16(_mm_srli_epi32(wide_fx, 16), 
+                wide_i = _mm_max_epi16(_mm_srli_epi32(wide_fx, 16),
                                        _mm_setzero_si128());
                 wide_i = _mm_min_epi16(wide_i, wide_maxX);
-    
+
                 // i<<4 | TILEX_LOW_BITS(fx)
                 wide_lo = _mm_srli_epi32(wide_fx, 12);
                 wide_lo = _mm_and_si128(wide_lo, wide_mask);
-                wide_i  = _mm_slli_epi32(wide_i, 4);         
-                wide_i  = _mm_or_si128(wide_i, wide_lo);     
-    
+                wide_i  = _mm_slli_epi32(wide_i, 4);
+                wide_i  = _mm_or_si128(wide_i, wide_lo);
+
                 // i<<14
                 wide_i = _mm_slli_epi32(wide_i, 14);
-    
+
                 // SkClampMax(((f+one))>>16,max)
                 wide_fx1 = _mm_add_epi32(wide_fx, wide_one);
-                wide_fx1 = _mm_max_epi16(_mm_srli_epi32(wide_fx1, 16), 
+                wide_fx1 = _mm_max_epi16(_mm_srli_epi32(wide_fx1, 16),
                                                         _mm_setzero_si128());
                 wide_fx1 = _mm_min_epi16(wide_fx1, wide_maxX);
-                    
+
                 // final combination
                 wide_i = _mm_or_si128(wide_i, wide_fx1);
-                _mm_store_si128(reinterpret_cast<__m128i*>(xy), wide_i); 
-    
+                _mm_store_si128(reinterpret_cast<__m128i*>(xy), wide_i);
+
                 wide_fx = _mm_add_epi32(wide_fx, wide_dx4);
-                fx += dx * 4;   
+                fx += dx * 4;
                 xy += 4;
                 count -= 4;
             } // while count >= 4
@@ -382,7 +382,7 @@
     const unsigned maxY = s.fBitmap->height() - 1;
     *xy++ = SkClampMax(fx >> 16, maxY);
     fx = SkScalarToFixed(pt.fX);
-    
+
     if (0 == maxX) {
         // all of the following X values must be 0
         memset(xy, 0, count * sizeof(uint16_t));
@@ -416,7 +416,7 @@
                 __m128i wide_result = _mm_packs_epi32(wide_out_low,
                                                       wide_out_high);
                 _mm_store_si128(reinterpret_cast<__m128i*>(xy), wide_result);
-            
+
                 wide_low = _mm_add_epi32(wide_low, wide_dx8);
                 wide_high = _mm_add_epi32(wide_high, wide_dx8);
 
@@ -434,12 +434,12 @@
     } else {
         // SSE2 only support 16bit interger max & min, so only process the case
         // maxX less than the max 16bit interger. Actually maxX is the bitmap's
-        // height, there should be rare bitmap whose height will be greater 
+        // height, there should be rare bitmap whose height will be greater
         // than max 16bit interger in the real world.
         if ((count >= 8) && (maxX <= 0xFFFF)) {
             while (((size_t)xy & 0x0F) != 0) {
-                *xy++ = SkClampMax((fx + dx) >> 16, maxX) | 
-                                   SkClampMax(fx >> 16, maxX);
+                *xy++ = pack_two_shorts(SkClampMax((fx + dx) >> 16, maxX),
+                                        SkClampMax(fx >> 16, maxX));
                 fx += 2 * dx;
                 count -= 2;
             }
@@ -456,7 +456,7 @@
                 __m128i wide_out_low = _mm_srli_epi32(wide_low, 16);
                 __m128i wide_out_high = _mm_srli_epi32(wide_high, 16);
 
-                wide_out_low  = _mm_max_epi16(wide_out_low, 
+                wide_out_low  = _mm_max_epi16(wide_out_low,
                                               _mm_setzero_si128());
                 wide_out_low  = _mm_min_epi16(wide_out_low, wide_maxX);
                 wide_out_high = _mm_max_epi16(wide_out_high,
@@ -493,7 +493,7 @@
     s.fInvProc(*s.fInvMatrix,
                SkIntToScalar(x) + SK_ScalarHalf,
                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
+
     SkFixed oneX = s.fFilterOneX;
     SkFixed oneY = s.fFilterOneY;
     SkFixed fx = SkScalarToFixed(srcPt.fX) - (oneX >> 1);
@@ -510,37 +510,37 @@
         __m128i wide_f = _mm_set_epi32(fx + dx, fy + dy, fx, fy);
         __m128i wide_d2  = _mm_set_epi32(dx2, dy2, dx2, dy2);
         __m128i wide_one  = _mm_set_epi32(oneX, oneY, oneX, oneY);
-        __m128i wide_max = _mm_set_epi32(maxX, maxY, maxX, maxY); 
+        __m128i wide_max = _mm_set_epi32(maxX, maxY, maxX, maxY);
         __m128i wide_mask = _mm_set1_epi32(0xF);
 
         while (count >= 2) {
             // i = SkClampMax(f>>16,maxX)
-            __m128i wide_i = _mm_max_epi16(_mm_srli_epi32(wide_f, 16), 
+            __m128i wide_i = _mm_max_epi16(_mm_srli_epi32(wide_f, 16),
                                            _mm_setzero_si128());
             wide_i = _mm_min_epi16(wide_i, wide_max);
-    
+
             // i<<4 | TILEX_LOW_BITS(f)
             __m128i wide_lo = _mm_srli_epi32(wide_f, 12);
             wide_lo = _mm_and_si128(wide_lo, wide_mask);
-            wide_i  = _mm_slli_epi32(wide_i, 4);         
-            wide_i  = _mm_or_si128(wide_i, wide_lo);     
-    
+            wide_i  = _mm_slli_epi32(wide_i, 4);
+            wide_i  = _mm_or_si128(wide_i, wide_lo);
+
             // i<<14
             wide_i = _mm_slli_epi32(wide_i, 14);
-    
+
             // SkClampMax(((f+one))>>16,max)
             __m128i wide_f1 = _mm_add_epi32(wide_f, wide_one);
-            wide_f1 = _mm_max_epi16(_mm_srli_epi32(wide_f1, 16), 
+            wide_f1 = _mm_max_epi16(_mm_srli_epi32(wide_f1, 16),
                                                    _mm_setzero_si128());
             wide_f1 = _mm_min_epi16(wide_f1, wide_max);
-                    
+
             // final combination
             wide_i = _mm_or_si128(wide_i, wide_f1);
-            _mm_storeu_si128(reinterpret_cast<__m128i*>(xy), wide_i); 
-    
+            _mm_storeu_si128(reinterpret_cast<__m128i*>(xy), wide_i);
+
             wide_f = _mm_add_epi32(wide_f, wide_d2);
 
-            fx += dx2; 
+            fx += dx2;
             fy += dy2;
             xy += 4;
             count -= 2;
@@ -551,7 +551,7 @@
         *xy++ = ClampX_ClampY_pack_filter(fy, maxY, oneY);
         fy += dy;
         *xy++ = ClampX_ClampY_pack_filter(fx, maxX, oneX);
-        fx += dx;          
+        fx += dx;
     }
 }
 
@@ -569,7 +569,7 @@
     s.fInvProc(*s.fInvMatrix,
                SkIntToScalar(x) + SK_ScalarHalf,
                SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
-    
+
     SkFixed fx = SkScalarToFixed(srcPt.fX);
     SkFixed fy = SkScalarToFixed(srcPt.fY);
     SkFixed dx = s.fInvSx;
@@ -579,7 +579,7 @@
 
     if (count >= 4 && (maxX <= 0xFFFF)) {
         while (((size_t)xy & 0x0F) != 0) {
-            *xy++ = (SkClampMax(fy >> 16, maxY) << 16) | 
+            *xy++ = (SkClampMax(fy >> 16, maxY) << 16) |
                                   SkClampMax(fx >> 16, maxX);
             fx += dx;
             fy += dy;
@@ -596,29 +596,29 @@
         __m128i wide_dx4  = _mm_set1_epi32(dx4);
         __m128i wide_dy4  = _mm_set1_epi32(dy4);
 
-        __m128i wide_maxX = _mm_set1_epi32(maxX); 
-        __m128i wide_maxY = _mm_set1_epi32(maxY); 
+        __m128i wide_maxX = _mm_set1_epi32(maxX);
+        __m128i wide_maxY = _mm_set1_epi32(maxY);
 
         while (count >= 4) {
             // SkClampMax(fx>>16,maxX)
-            __m128i wide_lo = _mm_max_epi16(_mm_srli_epi32(wide_fx, 16), 
+            __m128i wide_lo = _mm_max_epi16(_mm_srli_epi32(wide_fx, 16),
                                             _mm_setzero_si128());
             wide_lo = _mm_min_epi16(wide_lo, wide_maxX);
-    
+
             // SkClampMax(fy>>16,maxY)
-            __m128i wide_hi = _mm_max_epi16(_mm_srli_epi32(wide_fy, 16), 
+            __m128i wide_hi = _mm_max_epi16(_mm_srli_epi32(wide_fy, 16),
                                             _mm_setzero_si128());
             wide_hi = _mm_min_epi16(wide_hi, wide_maxY);
-                    
+
             // final combination
             __m128i wide_i = _mm_or_si128(_mm_slli_epi32(wide_hi, 16),
                                           wide_lo);
-            _mm_store_si128(reinterpret_cast<__m128i*>(xy), wide_i); 
- 
+            _mm_store_si128(reinterpret_cast<__m128i*>(xy), wide_i);
+
             wide_fx = _mm_add_epi32(wide_fx, wide_dx4);
             wide_fy = _mm_add_epi32(wide_fy, wide_dy4);
 
-            fx += dx4; 
+            fx += dx4;
             fy += dy4;
             xy += 4;
             count -= 4;
@@ -629,6 +629,137 @@
         *xy++ = (SkClampMax(fy >> 16, maxY) << 16) |
                               SkClampMax(fx >> 16, maxX);
         fx += dx;
-        fy += dy;           
+        fy += dy;
     }
 }
+
+/*  SSE version of S32_D16_filter_DX_SSE2
+ *  Definition is in section of "D16 functions for SRC == 8888" in SkBitmapProcState.cpp
+ *  It combines S32_opaque_D32_filter_DX_SSE2 and SkPixel32ToPixel16
+ */
+void S32_D16_filter_DX_SSE2(const SkBitmapProcState& s,
+                                   const uint32_t* xy,
+                                   int count, uint16_t* colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fDoFilter);
+    SkASSERT(s.fBitmap->config() == SkBitmap::kARGB_8888_Config);
+    SkASSERT(s.fBitmap->isOpaque());
+
+    SkPMColor dstColor;
+    const char* srcAddr = static_cast<const char*>(s.fBitmap->getPixels());
+    unsigned rb = s.fBitmap->rowBytes();
+    uint32_t XY = *xy++;
+    unsigned y0 = XY >> 14;
+    const uint32_t* row0 = reinterpret_cast<const uint32_t*>(srcAddr + (y0 >> 4) * rb);
+    const uint32_t* row1 = reinterpret_cast<const uint32_t*>(srcAddr + (XY & 0x3FFF) * rb);
+    unsigned subY = y0 & 0xF;
+
+    // ( 0,  0,  0,  0,  0,  0,  0, 16)
+    __m128i sixteen = _mm_cvtsi32_si128(16);
+
+    // ( 0,  0,  0,  0, 16, 16, 16, 16)
+    sixteen = _mm_shufflelo_epi16(sixteen, 0);
+
+    // ( 0,  0,  0,  0,  0,  0,  0,  y)
+    __m128i allY = _mm_cvtsi32_si128(subY);
+
+    // ( 0,  0,  0,  0,  y,  y,  y,  y)
+    allY = _mm_shufflelo_epi16(allY, 0);
+
+    // ( 0,  0,  0,  0, 16-y, 16-y, 16-y, 16-y)
+    __m128i negY = _mm_sub_epi16(sixteen, allY);
+
+    // (16-y, 16-y, 16-y, 16-y, y, y, y, y)
+    allY = _mm_unpacklo_epi64(allY, negY);
+
+    // (16, 16, 16, 16, 16, 16, 16, 16 )
+    sixteen = _mm_shuffle_epi32(sixteen, 0);
+
+    // ( 0,  0,  0,  0,  0,  0,  0,  0)
+    __m128i zero = _mm_setzero_si128();
+
+    do {
+        uint32_t XX = *xy++;    // x0:14 | 4 | x1:14
+        unsigned x0 = XX >> 18;
+        unsigned x1 = XX & 0x3FFF;
+
+        // (0, 0, 0, 0, 0, 0, 0, x)
+        __m128i allX = _mm_cvtsi32_si128((XX >> 14) & 0x0F);
+
+        // (0, 0, 0, 0, x, x, x, x)
+        allX = _mm_shufflelo_epi16(allX, 0);
+
+        // (x, x, x, x, x, x, x, x)
+        allX = _mm_shuffle_epi32(allX, 0);
+
+        // (16-x, 16-x, 16-x, 16-x, 16-x, 16-x, 16-x)
+        __m128i negX = _mm_sub_epi16(sixteen, allX);
+
+        // Load 4 samples (pixels).
+        __m128i a00 = _mm_cvtsi32_si128(row0[x0]);
+        __m128i a01 = _mm_cvtsi32_si128(row0[x1]);
+        __m128i a10 = _mm_cvtsi32_si128(row1[x0]);
+        __m128i a11 = _mm_cvtsi32_si128(row1[x1]);
+
+        // (0, 0, a00, a10)
+        __m128i a00a10 = _mm_unpacklo_epi32(a10, a00);
+
+        // Expand to 16 bits per component.
+        a00a10 = _mm_unpacklo_epi8(a00a10, zero);
+
+        // ((a00 * (16-y)), (a10 * y)).
+        a00a10 = _mm_mullo_epi16(a00a10, allY);
+
+        // (a00 * (16-y) * (16-x), a10 * y * (16-x)).
+        a00a10 = _mm_mullo_epi16(a00a10, negX);
+
+        // (0, 0, a01, a10)
+        __m128i a01a11 = _mm_unpacklo_epi32(a11, a01);
+
+        // Expand to 16 bits per component.
+        a01a11 = _mm_unpacklo_epi8(a01a11, zero);
+
+        // (a01 * (16-y)), (a11 * y)
+        a01a11 = _mm_mullo_epi16(a01a11, allY);
+
+        // (a01 * (16-y) * x), (a11 * y * x)
+        a01a11 = _mm_mullo_epi16(a01a11, allX);
+
+        // (a00*w00 + a01*w01, a10*w10 + a11*w11)
+        __m128i sum = _mm_add_epi16(a00a10, a01a11);
+
+        // (DC, a00*w00 + a01*w01)
+        __m128i shifted = _mm_shuffle_epi32(sum, 0xEE);
+
+        // (DC, a00*w00 + a01*w01 + a10*w10 + a11*w11)
+        sum = _mm_add_epi16(sum, shifted);
+
+        // Divide each 16 bit component by 256.
+        sum = _mm_srli_epi16(sum, 8);
+
+        // Pack lower 4 16 bit values of sum into lower 4 bytes.
+        sum = _mm_packus_epi16(sum, zero);
+
+        // Extract low int and store.
+        dstColor = _mm_cvtsi128_si32(sum);
+
+        //*colors++ = SkPixel32ToPixel16(dstColor);
+        // below is much faster than the above. It's tested for Android benchmark--Softweg
+        __m128i _m_temp1 = _mm_set1_epi32(dstColor);
+        __m128i _m_temp2 = _mm_srli_epi32(_m_temp1, 3);
+
+        unsigned int r32 = _mm_cvtsi128_si32(_m_temp2);
+        unsigned r = (r32 & ((1<<5) -1)) << 11;
+
+        _m_temp2 = _mm_srli_epi32(_m_temp2, 7);
+        unsigned int g32 = _mm_cvtsi128_si32(_m_temp2);
+        unsigned g = (g32 & ((1<<6) -1)) << 5;
+
+        _m_temp2 = _mm_srli_epi32(_m_temp2, 9);
+        unsigned int b32 = _mm_cvtsi128_si32(_m_temp2);
+        unsigned b = (b32 & ((1<<5) -1));
+
+        *colors++ = r | g | b;
+
+    } while (--count > 0);
+}
diff --git a/src/opts/SkBitmapProcState_opts_SSE2.h b/src/opts/SkBitmapProcState_opts_SSE2.h
index 3fdf696..46e35a0 100644
--- a/src/opts/SkBitmapProcState_opts_SSE2.h
+++ b/src/opts/SkBitmapProcState_opts_SSE2.h
@@ -25,3 +25,6 @@
                                       uint32_t xy[], int count, int x, int y);
 void ClampX_ClampY_nofilter_affine_SSE2(const SkBitmapProcState& s,
                                        uint32_t xy[], int count, int x, int y);
+void S32_D16_filter_DX_SSE2(const SkBitmapProcState& s,
+                                  const uint32_t* xy,
+                                  int count, uint16_t* colors);
diff --git a/src/opts/SkBitmapProcState_opts_SSSE3.cpp b/src/opts/SkBitmapProcState_opts_SSSE3.cpp
index 98b3445..59b2386 100644
--- a/src/opts/SkBitmapProcState_opts_SSSE3.cpp
+++ b/src/opts/SkBitmapProcState_opts_SSSE3.cpp
@@ -67,6 +67,57 @@
     *sixteen_minus_x = _mm_sub_epi8(sixteen_8bit, all_x);
 }
 
+// Prepare all necessary constants for a round of processing for two pixel
+// pairs.
+// @param xy is the location where the xy parameters for four pixels should be
+//           read from. It is identical in concept with argument two of
+//           S32_{opaque}_D32_filter_DXDY methods.
+// @param mask_3FFF vector of 32 bit constants containing 3FFF,
+//                  suitable to mask the bottom 14 bits of a XY value.
+// @param mask_000F vector of 32 bit constants containing 000F,
+//                  suitable to mask the bottom 4 bits of a XY value.
+// @param sixteen_8bit vector of 8 bit components containing the value 16.
+// @param mask_dist_select vector of 8 bit components containing the shuffling
+//                         parameters to reorder x[0-3] parameters.
+// @param all_xy_result vector of 8 bit components that will contain the
+//              (4x(y1), 4x(y0), 4x(x1), 4x(x0)) upon return.
+// @param sixteen_minus_x vector of 8 bit components, containing
+//              (4x(16-y1), 4x(16-y0), 4x(16-x1), 4x(16-x0)).
+inline void PrepareConstantsTwoPixelPairsDXDY(const uint32_t* xy,
+                                              const __m128i& mask_3FFF,
+                                              const __m128i& mask_000F,
+                                              const __m128i& sixteen_8bit,
+                                              const __m128i& mask_dist_select,
+                                              __m128i* all_xy_result,
+                                              __m128i* sixteen_minus_xy,
+                                              int* xy0, int* xy1) {
+    const __m128i xy_wide =
+                        _mm_loadu_si128(reinterpret_cast<const __m128i *>(xy));
+
+    // (x10, y10, x00, y00)
+    __m128i xy0_wide = _mm_srli_epi32(xy_wide, 18);
+    // (y10, y00, x10, x00)
+    xy0_wide =  _mm_shuffle_epi32(xy0_wide, _MM_SHUFFLE(2, 0, 3, 1));
+    // (x11, y11, x01, y01)
+    __m128i xy1_wide = _mm_and_si128(xy_wide, mask_3FFF);
+    // (y11, y01, x11, x01)
+    xy1_wide = _mm_shuffle_epi32(xy1_wide, _MM_SHUFFLE(2, 0, 3, 1));
+
+    _mm_storeu_si128(reinterpret_cast<__m128i *>(xy0), xy0_wide);
+    _mm_storeu_si128(reinterpret_cast<__m128i *>(xy1), xy1_wide);
+
+    // (x1, y1, x0, y0)
+    __m128i all_xy = _mm_and_si128(_mm_srli_epi32(xy_wide, 14), mask_000F);
+    // (y1, y0, x1, x0)
+    all_xy = _mm_shuffle_epi32(all_xy, _MM_SHUFFLE(2, 0, 3, 1));
+    // (4x(y1), 4x(y0), 4x(x1), 4x(x0))
+    all_xy = _mm_shuffle_epi8(all_xy, mask_dist_select);
+
+    *all_xy_result = all_xy;
+    // (4x(16-y1), 4x(16-y0), 4x(16-x1), 4x(16-x0))
+    *sixteen_minus_xy = _mm_sub_epi8(sixteen_8bit, all_xy);
+}
+
 // Helper function used when processing one pixel pair.
 // @param pixel0..3 are the four input pixels
 // @param scale_x vector of 8 bit components to multiply the pixel[0:3]. This
@@ -252,6 +303,39 @@
     return ScaleFourPixels<has_alpha, 8>(&sum0, alpha);
 }
 
+// Similar to ProcessTwoPixelPairs except the pixel indexes.
+template<bool has_alpha>
+inline __m128i ProcessTwoPixelPairsDXDY(const uint32_t* row00,
+                                        const uint32_t* row01,
+                                        const uint32_t* row10,
+                                        const uint32_t* row11,
+                                        const int* xy0,
+                                        const int* xy1,
+                                        const __m128i& scale_x,
+                                        const __m128i& all_y,
+                                        const __m128i& neg_y,
+                                        const __m128i& alpha) {
+    // first row
+    __m128i sum0 = ProcessPixelPair(
+        row00[xy0[0]], row00[xy1[0]], row10[xy0[1]], row10[xy1[1]],
+        scale_x, neg_y);
+    // second row
+    __m128i sum1 = ProcessPixelPair(
+        row01[xy0[0]], row01[xy1[0]], row11[xy0[1]], row11[xy1[1]],
+        scale_x, all_y);
+
+    // 2 samples fully summed.
+    // ((16-y1) * (Aa2 * (16-x1) + Aa3 * x1) +
+    //  y0 * (Aa2' * (16-x1) + Aa3' * x1),
+    // ...
+    //  (16-y0) * (Ra0 * (16 - x0) + Ra1 * x0)) +
+    //  y0 * (Ra0' * (16-x0) + Ra1' * x0))
+    // Each component, again can be at most 256 * 255 = 65280, so no overflow.
+    sum0 = _mm_add_epi16(sum0, sum1);
+
+    return ScaleFourPixels<has_alpha, 8>(&sum0, alpha);
+}
+
 
 // Same as ProcessPixelPair, except that performing the math one output pixel
 // at a time. This means that only the bottom four 16 bit components are set.
@@ -331,7 +415,7 @@
     // (0, 0, 0, 0, 0, 0, 0, 0)
     const __m128i zero = _mm_setzero_si128();
 
-    __m128i alpha;
+    __m128i alpha = _mm_setzero_si128();
     if (has_alpha)
         // 8x(alpha)
         alpha = _mm_set1_epi16(s.fAlphaScale);
@@ -482,6 +566,136 @@
         }
     }
 }
+
+/*
+ * Similar to S32_generic_D32_filter_DX_SSSE3, we do not need to handle the
+ * special case suby == 0 as suby is changing in every loop.
+ */
+template<bool has_alpha>
+void S32_generic_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s,
+                                       const uint32_t* xy,
+                                       int count, uint32_t* colors) {
+    SkASSERT(count > 0 && colors != NULL);
+    SkASSERT(s.fDoFilter);
+    SkASSERT(s.fBitmap->config() == SkBitmap::kARGB_8888_Config);
+    if (has_alpha) {
+        SkASSERT(s.fAlphaScale < 256);
+    } else {
+        SkASSERT(s.fAlphaScale == 256);
+    }
+
+    const uint8_t* src_addr =
+                        static_cast<const uint8_t*>(s.fBitmap->getPixels());
+    const unsigned rb = s.fBitmap->rowBytes();
+
+    // vector constants
+    const __m128i mask_dist_select = _mm_set_epi8(12, 12, 12, 12,
+                                                  8,  8,  8,  8,
+                                                  4,  4,  4,  4,
+                                                  0,  0,  0,  0);
+    const __m128i mask_3FFF = _mm_set1_epi32(0x3FFF);
+    const __m128i mask_000F = _mm_set1_epi32(0x000F);
+    const __m128i sixteen_8bit = _mm_set1_epi8(16);
+
+    __m128i alpha;
+    if (has_alpha) {
+        // 8x(alpha)
+        alpha = _mm_set1_epi16(s.fAlphaScale);
+    }
+
+    // Unroll 2x, interleave bytes, use pmaddubsw (all_x is small)
+    while (count >= 2) {
+        int xy0[4];
+        int xy1[4];
+        __m128i all_xy, sixteen_minus_xy;
+        PrepareConstantsTwoPixelPairsDXDY(xy, mask_3FFF, mask_000F,
+                                          sixteen_8bit, mask_dist_select,
+                                         &all_xy, &sixteen_minus_xy, xy0, xy1);
+
+        // (4x(x1, 16-x1), 4x(x0, 16-x0))
+        __m128i scale_x = _mm_unpacklo_epi8(sixteen_minus_xy, all_xy);
+        // (4x(0, y1), 4x(0, y0))
+        __m128i all_y = _mm_unpackhi_epi8(all_xy, _mm_setzero_si128());
+        __m128i neg_y = _mm_sub_epi16(_mm_set1_epi16(16), all_y);
+
+        const uint32_t* row00 =
+                    reinterpret_cast<const uint32_t*>(src_addr + xy0[2] * rb);
+        const uint32_t* row01 =
+                    reinterpret_cast<const uint32_t*>(src_addr + xy1[2] * rb);
+        const uint32_t* row10 =
+                    reinterpret_cast<const uint32_t*>(src_addr + xy0[3] * rb);
+        const uint32_t* row11 =
+                    reinterpret_cast<const uint32_t*>(src_addr + xy1[3] * rb);
+
+        __m128i sum0 = ProcessTwoPixelPairsDXDY<has_alpha>(
+                                        row00, row01, row10, row11, xy0, xy1,
+                                        scale_x, all_y, neg_y, alpha);
+
+        // Pack lower 4 16 bit values of sum into lower 4 bytes.
+        sum0 = _mm_packus_epi16(sum0, _mm_setzero_si128());
+
+        // Extract low int and store.
+        _mm_storel_epi64(reinterpret_cast<__m128i *>(colors), sum0);
+
+        xy += 4;
+        colors += 2;
+        count -= 2;
+    }
+
+    // Handle the remainder
+    while (count-- > 0) {
+        uint32_t data = *xy++;
+        unsigned y0 = data >> 14;
+        unsigned y1 = data & 0x3FFF;
+        unsigned subY = y0 & 0xF;
+        y0 >>= 4;
+
+        data = *xy++;
+        unsigned x0 = data >> 14;
+        unsigned x1 = data & 0x3FFF;
+        unsigned subX = x0 & 0xF;
+        x0 >>= 4;
+
+        const uint32_t* row0 =
+                        reinterpret_cast<const uint32_t*>(src_addr + y0 * rb);
+        const uint32_t* row1 =
+                        reinterpret_cast<const uint32_t*>(src_addr + y1 * rb);
+
+        // 16x(x)
+        const __m128i all_x = _mm_set1_epi8(subX);
+
+        // 16x (16-x)
+        __m128i scale_x = _mm_sub_epi8(sixteen_8bit, all_x);
+
+        // (8x (x, 16-x))
+        scale_x = _mm_unpacklo_epi8(scale_x, all_x);
+
+        // 8x(16)
+        const __m128i sixteen_16bit = _mm_set1_epi16(16);
+
+        // 8x (y)
+        const __m128i all_y = _mm_set1_epi16(subY);
+
+        // 8x (16-y)
+        const __m128i neg_y = _mm_sub_epi16(sixteen_16bit, all_y);
+
+        // first row.
+        __m128i sum0 = ProcessOnePixel(row0[x0], row0[x1], scale_x, neg_y);
+        // second row.
+        __m128i sum1 = ProcessOnePixel(row1[x0], row1[x1], scale_x, all_y);
+
+        // Add both rows for full sample
+        sum0 = _mm_add_epi16(sum0, sum1);
+
+        sum0 = ScaleFourPixels<has_alpha, 8>(&sum0, alpha);
+
+        // Pack lower 4 16 bit values of sum into lower 4 bytes.
+        sum0 = _mm_packus_epi16(sum0, _mm_setzero_si128());
+
+        // Extract low int and store.
+        *colors++ = _mm_cvtsi128_si32(sum0);
+    }
+}
 }  // namepace
 
 void S32_opaque_D32_filter_DX_SSSE3(const SkBitmapProcState& s,
@@ -495,3 +709,15 @@
                                    int count, uint32_t* colors) {
     S32_generic_D32_filter_DX_SSSE3<true>(s, xy, count, colors);
 }
+
+void S32_opaque_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s,
+                                    const uint32_t* xy,
+                                    int count, uint32_t* colors) {
+    S32_generic_D32_filter_DXDY_SSSE3<false>(s, xy, count, colors);
+}
+
+void S32_alpha_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s,
+                                   const uint32_t* xy,
+                                   int count, uint32_t* colors) {
+    S32_generic_D32_filter_DXDY_SSSE3<true>(s, xy, count, colors);
+}
diff --git a/src/opts/SkBitmapProcState_opts_SSSE3.h b/src/opts/SkBitmapProcState_opts_SSSE3.h
index d21e7e4..176f2bf 100644
--- a/src/opts/SkBitmapProcState_opts_SSSE3.h
+++ b/src/opts/SkBitmapProcState_opts_SSSE3.h
@@ -13,3 +13,9 @@
 void S32_alpha_D32_filter_DX_SSSE3(const SkBitmapProcState& s,
                                    const uint32_t* xy,
                                    int count, uint32_t* colors);
+void S32_opaque_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s,
+                                    const uint32_t* xy,
+                                    int count, uint32_t* colors);
+void S32_alpha_D32_filter_DXDY_SSSE3(const SkBitmapProcState& s,
+                                   const uint32_t* xy,
+                                   int count, uint32_t* colors);
diff --git a/src/opts/SkBitmapProcState_opts_arm.cpp b/src/opts/SkBitmapProcState_opts_arm.cpp
index 20d62e1..e044ad8 100644
--- a/src/opts/SkBitmapProcState_opts_arm.cpp
+++ b/src/opts/SkBitmapProcState_opts_arm.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2009 The Android Open Source Project
  *
@@ -9,14 +8,15 @@
 
 #include "SkBitmapProcState.h"
 #include "SkColorPriv.h"
+#include "SkTypes.h"
 #include "SkUtils.h"
 
-#if __ARM_ARCH__ >= 6 && !defined(SK_CPU_BENDIAN)
+#if SK_ARM_ARCH >= 6 && !defined(SK_CPU_BENDIAN)
 void SI8_D16_nofilter_DX_arm(
     const SkBitmapProcState& s,
     const uint32_t* SK_RESTRICT xy,
     int count,
-    uint16_t* SK_RESTRICT colors) __attribute__((optimize("O1")));
+    uint16_t* SK_RESTRICT colors) SK_ATTRIBUTE_OPTIMIZE_O1;
 
 void SI8_D16_nofilter_DX_arm(const SkBitmapProcState& s,
                              const uint32_t* SK_RESTRICT xy,
@@ -24,18 +24,18 @@
     SkASSERT(count > 0 && colors != NULL);
     SkASSERT(s.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask));
     SkASSERT(s.fDoFilter == false);
-    
+
     const uint16_t* SK_RESTRICT table = s.fBitmap->getColorTable()->lock16BitCache();
     const uint8_t* SK_RESTRICT srcAddr = (const uint8_t*)s.fBitmap->getPixels();
-    
+
     // buffer is y32, x16, x16, x16, x16, x16
     // bump srcAddr to the proper row, since we're told Y never changes
     SkASSERT((unsigned)xy[0] < (unsigned)s.fBitmap->height());
     srcAddr = (const uint8_t*)((const char*)srcAddr +
                                xy[0] * s.fBitmap->rowBytes());
-    
+
     uint8_t src;
-    
+
     if (1 == s.fBitmap->width()) {
         src = srcAddr[0];
         uint16_t dstValue = table[src];
@@ -44,7 +44,7 @@
         int i;
         int count8 = count >> 3;
         const uint16_t* SK_RESTRICT xx = (const uint16_t*)(xy + 1);
-        
+
         asm volatile (
                       "cmp        %[count8], #0                   \n\t"   // compare loop counter with 0
                       "beq        2f                              \n\t"   // if loop counter == 0, exit
@@ -94,20 +94,20 @@
                       : [table] "r" (table), [srcAddr] "r" (srcAddr)
                       : "memory", "cc", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11"
                       );
-        
+
         for (i = (count & 7); i > 0; --i) {
             src = srcAddr[*xx++]; *colors++ = table[src];
         }
     }
 
-    s.fBitmap->getColorTable()->unlock16BitCache(); 
+    s.fBitmap->getColorTable()->unlock16BitCache();
 }
 
 void SI8_opaque_D32_nofilter_DX_arm(
     const SkBitmapProcState& s,
     const uint32_t* SK_RESTRICT xy,
     int count,
-    SkPMColor* SK_RESTRICT colors) __attribute__((optimize("O1")));
+    SkPMColor* SK_RESTRICT colors) SK_ATTRIBUTE_OPTIMIZE_O1;
 
 void SI8_opaque_D32_nofilter_DX_arm(const SkBitmapProcState& s,
                                     const uint32_t* SK_RESTRICT xy,
@@ -182,7 +182,7 @@
 
     s.fBitmap->getColorTable()->unlockColors(false);
 }
-#endif //__ARM_ARCH__ >= 6 && !defined(SK_CPU_BENDIAN)
+#endif // SK_ARM_ARCH >= 6 && !defined(SK_CPU_BENDIAN)
 
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -200,7 +200,7 @@
 
     switch (fBitmap->config()) {
         case SkBitmap::kIndex8_Config:
-#if __ARM_ARCH__ >= 6 && !defined(SK_CPU_BENDIAN)
+#if SK_ARM_ARCH >= 6 && !defined(SK_CPU_BENDIAN)
             if (justDx && !doFilter) {
 #if 0   /* crashing on android device */
                 fSampleProc16 = SI8_D16_nofilter_DX_arm;
@@ -218,4 +218,3 @@
             break;
     }
 }
-
diff --git a/src/opts/SkBitmapProcState_opts_none.cpp b/src/opts/SkBitmapProcState_opts_none.cpp
index 82be4ea..3a186b5 100644
--- a/src/opts/SkBitmapProcState_opts_none.cpp
+++ b/src/opts/SkBitmapProcState_opts_none.cpp
@@ -21,5 +21,3 @@
 
 // empty implementation just uses default supplied function pointers
 void SkBitmapProcState::platformProcs() {}
-
-
diff --git a/src/opts/SkBlitRect_opts_SSE2.cpp b/src/opts/SkBlitRect_opts_SSE2.cpp
new file mode 100644
index 0000000..cb61813
--- /dev/null
+++ b/src/opts/SkBlitRect_opts_SSE2.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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 "SkBlitRect_opts_SSE2.h"
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+
+#include <emmintrin.h>
+
+/** Simple blitting of opaque rectangles less than 31 pixels wide:
+    inlines and merges sections of Color32_SSE2 and sk_memset32_SSE2.
+*/
+static void BlitRect32_OpaqueNarrow_SSE2(SkPMColor* SK_RESTRICT destination,
+                                  int width, int height,
+                                  size_t rowBytes, uint32_t color) {
+    SkASSERT(255 == SkGetPackedA32(color));
+    SkASSERT(width > 0);
+    SkASSERT(width < 31);
+
+    while (--height >= 0) {
+        SkPMColor* dst = destination;
+        int count = width;
+
+        while (count > 4) {
+            *dst++ = color;
+            *dst++ = color;
+            *dst++ = color;
+            *dst++ = color;
+            count -= 4;
+        }
+
+        while (count > 0) {
+            *dst++ = color;
+            --count;
+        }
+
+        destination = (uint32_t*)((char*)destination + rowBytes);
+    }
+}
+
+/**
+  Fast blitting of opaque rectangles at least 31 pixels wide:
+  inlines and merges sections of Color32_SSE2 and sk_memset32_SSE2.
+  A 31 pixel rectangle is guaranteed to have at least one
+  16-pixel aligned span that can take advantage of mm_store.
+*/
+static void BlitRect32_OpaqueWide_SSE2(SkPMColor* SK_RESTRICT destination,
+                                int width, int height,
+                                size_t rowBytes, uint32_t color) {
+    SkASSERT(255 == SkGetPackedA32(color));
+    SkASSERT(width >= 31);
+
+    __m128i color_wide = _mm_set1_epi32(color);
+    while (--height >= 0) {
+        // Prefetching one row ahead to L1 cache can equal hardware
+        // performance for large/tall rects, but never *beats*
+        // hardware performance.
+        SkPMColor* dst = destination;
+        int count = width;
+
+        while (((size_t)dst) & 0x0F) {
+            *dst++ = color;
+            --count;
+        }
+        __m128i *d = reinterpret_cast<__m128i*>(dst);
+
+        // Googling suggests _mm_stream is only going to beat _mm_store
+        // for things that wouldn't fit in L2 cache anyway, typically
+        // >500kB, and precisely fill cache lines.  For us, with
+        // arrays > 100k elements _mm_stream is still 100%+ slower than
+        // mm_store.
+
+        // Unrolling to count >= 64 is a break-even for most
+        // input patterns; we seem to be saturating the bus and having
+        // low enough overhead at 32.
+
+        while (count >= 32) {
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            count -= 32;
+        }
+        if (count >= 16) {
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            _mm_store_si128(d++, color_wide);
+            count -= 16;
+        }
+        dst = reinterpret_cast<uint32_t*>(d);
+
+        // Unrolling the loop in the Narrow code is a significant performance
+        // gain, but unrolling this loop appears to make no difference in
+        // benchmarks with either mm_store_si128 or individual sets.
+
+        while (count > 0) {
+            *dst++ = color;
+            --count;
+        }
+
+        destination = (uint32_t*)((char*)destination + rowBytes);
+    }
+}
+
+void ColorRect32_SSE2(SkPMColor* destination,
+                      int width, int height,
+                      size_t rowBytes, uint32_t color) {
+    if (0 == height || 0 == width || 0 == color) {
+        return;
+    }
+    unsigned colorA = SkGetPackedA32(color);
+    if (false && 255 == colorA) { // disabled but compilable to suppress warning
+        if (width < 31) {
+            BlitRect32_OpaqueNarrow_SSE2(destination, width, height,
+                                         rowBytes, color);
+        } else {
+            BlitRect32_OpaqueWide_SSE2(destination, width, height,
+                                       rowBytes, color);
+        }
+    } else {
+        SkBlitRow::ColorRect32(destination, width, height, rowBytes, color);
+    }
+}
diff --git a/src/opts/SkBlitRect_opts_SSE2.h b/src/opts/SkBlitRect_opts_SSE2.h
new file mode 100644
index 0000000..4d2f74a
--- /dev/null
+++ b/src/opts/SkBlitRect_opts_SSE2.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkBlitRect_opts_SSE2_DEFINED
+#define SkBlitRect_opts_SSE2_DEFINED
+
+/*
+  These functions' implementations copy sections of both
+  SkBlitRow_opts_SSE2 and SkUtils_opts_SSE2.
+*/
+
+#include "SkColor.h"
+
+void ColorRect32_SSE2(SkPMColor* SK_RESTRICT dst,
+                      int width, int height,
+                      size_t rowBytes, uint32_t color);
+
+
+#endif
diff --git a/src/opts/SkBlitRow_opts_SSE2.cpp b/src/opts/SkBlitRow_opts_SSE2.cpp
index 8e4dd1d..27ce1e5 100644
--- a/src/opts/SkBlitRow_opts_SSE2.cpp
+++ b/src/opts/SkBlitRow_opts_SSE2.cpp
@@ -7,6 +7,7 @@
 
 
 #include "SkBlitRow_opts_SSE2.h"
+#include "SkBitmapProcState_opts_SSE2.h"
 #include "SkColorPriv.h"
 #include "SkUtils.h"
 
@@ -512,25 +513,53 @@
     } while (--height != 0);
 }
 
+// The following (left) shifts cause the top 5 bits of the mask components to
+// line up with the corresponding components in an SkPMColor.
+// Note that the mask's RGB16 order may differ from the SkPMColor order.
+#define SK_R16x5_R32x5_SHIFT (SK_R32_SHIFT - SK_R16_SHIFT - SK_R16_BITS + 5)
+#define SK_G16x5_G32x5_SHIFT (SK_G32_SHIFT - SK_G16_SHIFT - SK_G16_BITS + 5)
+#define SK_B16x5_B32x5_SHIFT (SK_B32_SHIFT - SK_B16_SHIFT - SK_B16_BITS + 5)
+
+#if SK_R16x5_R32x5_SHIFT == 0
+    #define SkPackedR16x5ToUnmaskedR32x5_SSE2(x) (x)
+#elif SK_R16x5_R32x5_SHIFT > 0
+    #define SkPackedR16x5ToUnmaskedR32x5_SSE2(x) (_mm_slli_epi32(x, SK_R16x5_R32x5_SHIFT))
+#else
+    #define SkPackedR16x5ToUnmaskedR32x5_SSE2(x) (_mm_srli_epi32(x, -SK_R16x5_R32x5_SHIFT))
+#endif
+
+#if SK_G16x5_G32x5_SHIFT == 0
+    #define SkPackedG16x5ToUnmaskedG32x5_SSE2(x) (x)
+#elif SK_G16x5_G32x5_SHIFT > 0
+    #define SkPackedG16x5ToUnmaskedG32x5_SSE2(x) (_mm_slli_epi32(x, SK_G16x5_G32x5_SHIFT))
+#else
+    #define SkPackedG16x5ToUnmaskedG32x5_SSE2(x) (_mm_srli_epi32(x, -SK_G16x5_G32x5_SHIFT))
+#endif
+
+#if SK_B16x5_B32x5_SHIFT == 0
+    #define SkPackedB16x5ToUnmaskedB32x5_SSE2(x) (x)
+#elif SK_B16x5_B32x5_SHIFT > 0
+    #define SkPackedB16x5ToUnmaskedB32x5_SSE2(x) (_mm_slli_epi32(x, SK_B16x5_B32x5_SHIFT))
+#else
+    #define SkPackedB16x5ToUnmaskedB32x5_SSE2(x) (_mm_srli_epi32(x, -SK_B16x5_B32x5_SHIFT))
+#endif
+
 static __m128i SkBlendLCD16_SSE2(__m128i &srci, __m128i &dst,
                                  __m128i &mask, __m128i &scale) {
     // Get the R,G,B of each 16bit mask pixel, we want all of them in 5 bits.
-    __m128i r = _mm_and_si128(_mm_slli_epi32(mask,
-                              16-SK_R16_SHIFT-(SK_R16_BITS-5)),
-                              _mm_set1_epi32(0x001F0000));
+    __m128i r = _mm_and_si128(SkPackedR16x5ToUnmaskedR32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_R32_SHIFT));
 
-    __m128i g = _mm_and_si128(_mm_slli_epi32(mask,
-                              8-SK_G16_SHIFT-(SK_G16_BITS-5)),
-                              _mm_set1_epi32(0x00001F00));
+    __m128i g = _mm_and_si128(SkPackedG16x5ToUnmaskedG32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_G32_SHIFT));
 
-    __m128i b = _mm_and_si128(_mm_slli_epi32(mask,
-                              SK_B16_BITS-5),
-                              _mm_set1_epi32(0x0000001F));
-            
+    __m128i b = _mm_and_si128(SkPackedB16x5ToUnmaskedB32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_B32_SHIFT));
+
     // Pack the 4 16bit mask pixels into 4 32bit pixels, (p0, p1, p2, p3)
     mask = _mm_or_si128(_mm_or_si128(r, g), b);
 
-    // Interleave R,G,B into the lower byte of word. 
+    // Interleave R,G,B into the lower byte of word.
     __m128i maskLo, maskHi;
     maskLo = _mm_unpacklo_epi8(mask, _mm_setzero_si128());
     maskHi = _mm_unpackhi_epi8(mask, _mm_setzero_si128());
@@ -563,24 +592,22 @@
     return _mm_packus_epi16(resultLo, resultHi);
 }
 
-static __m128i SkBlendLCD16Opaque_SSE2(__m128i &srci, __m128i &dst, 
+static __m128i SkBlendLCD16Opaque_SSE2(__m128i &srci, __m128i &dst,
                                        __m128i &mask) {
     // Get the R,G,B of each 16bit mask pixel, we want all of them in 5 bits.
-    __m128i r = _mm_and_si128(_mm_slli_epi32(mask,
-                              16-SK_R16_SHIFT-(SK_R16_BITS-5)),
-                              _mm_set1_epi32(0x001F0000));
+    __m128i r = _mm_and_si128(SkPackedR16x5ToUnmaskedR32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_R32_SHIFT));
 
-    __m128i g = _mm_and_si128(_mm_slli_epi32(mask,
-                              8-SK_G16_SHIFT-(SK_G16_BITS-5)),
-                              _mm_set1_epi32(0x00001F00));
+    __m128i g = _mm_and_si128(SkPackedG16x5ToUnmaskedG32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_G32_SHIFT));
 
-    __m128i b = _mm_and_si128(_mm_slli_epi32(mask, SK_B16_BITS-5),
-                              _mm_set1_epi32(0x0000001F));
-            
+    __m128i b = _mm_and_si128(SkPackedB16x5ToUnmaskedB32x5_SSE2(mask),
+                              _mm_set1_epi32(0x1F << SK_B32_SHIFT));
+
     // Pack the 4 16bit mask pixels into 4 32bit pixels, (p0, p1, p2, p3)
     mask = _mm_or_si128(_mm_or_si128(r, g), b);
 
-    // Interleave R,G,B into the lower byte of word. 
+    // Interleave R,G,B into the lower byte of word.
     __m128i maskLo, maskHi;
     maskLo = _mm_unpacklo_epi8(mask, _mm_setzero_si128());
     maskHi = _mm_unpackhi_epi8(mask, _mm_setzero_si128());
@@ -603,8 +630,9 @@
     __m128i resultLo = _mm_add_epi16(dstLo, maskLo);
     __m128i resultHi = _mm_add_epi16(dstHi, maskHi);
 
-    // Pack into 4 32bit dst pixels
-    return _mm_packus_epi16(resultLo, resultHi);
+    // Pack into 4 32bit dst pixels and force opaque.
+    return _mm_or_si128(_mm_packus_epi16(resultLo, resultHi),
+                        _mm_set1_epi32(SK_A32_MASK << SK_A32_SHIFT));
 }
 
 void SkBlitLCD16Row_SSE2(SkPMColor dst[], const uint16_t src[],
@@ -617,7 +645,7 @@
     int srcR = SkColorGetR(color);
     int srcG = SkColorGetG(color);
     int srcB = SkColorGetB(color);
-    
+
     srcA = SkAlpha255To256(srcA);
 
     if (width >= 4) {
@@ -646,14 +674,14 @@
 
             // if mask pixels are not all zero, we will blend the dst pixels
             if (pack_cmp != 0xFFFF) {
-                // Unpack 4 16bit mask pixels to 
+                // Unpack 4 16bit mask pixels to
                 // (p0, 0, p1, 0, p2, 0, p3, 0)
                 mask_pixel = _mm_unpacklo_epi16(mask_pixel,
                                                 _mm_setzero_si128());
 
                 // Process 4 32bit dst pixels
                 __m128i result = SkBlendLCD16_SSE2(srci, dst_pixel,
-                                                   mask_pixel, scale); 
+                                                   mask_pixel, scale);
                 _mm_store_si128(d, result);
             }
 
@@ -669,7 +697,7 @@
         *dst = SkBlendLCD16(srcA, srcR, srcG, srcB, *dst, *src);
         src++;
         dst++;
-        width--;        
+        width--;
     }
 }
 
@@ -708,14 +736,14 @@
 
             // if mask pixels are not all zero, we will blend the dst pixels
             if (pack_cmp != 0xFFFF) {
-                // Unpack 4 16bit mask pixels to 
+                // Unpack 4 16bit mask pixels to
                 // (p0, 0, p1, 0, p2, 0, p3, 0)
                 mask_pixel = _mm_unpacklo_epi16(mask_pixel,
                                                 _mm_setzero_si128());
 
                 // Process 4 32bit dst pixels
                 __m128i result = SkBlendLCD16Opaque_SSE2(srci, dst_pixel,
-                                                         mask_pixel); 
+                                                         mask_pixel);
                 _mm_store_si128(d, result);
             }
 
@@ -731,6 +759,6 @@
         *dst = SkBlendLCD16Opaque(srcR, srcG, srcB, *dst, *src, opaqueDst);
         src++;
         dst++;
-        width--;        
+        width--;
     }
 }
diff --git a/src/opts/SkBlitRow_opts_arm.cpp b/src/opts/SkBlitRow_opts_arm.cpp
index dd8e406..f6e6ba2 100644
--- a/src/opts/SkBlitRow_opts_arm.cpp
+++ b/src/opts/SkBlitRow_opts_arm.cpp
@@ -1,413 +1,26 @@
 /*
- * Copyright 2009 The Android Open Source Project
+ * Copyright 2012 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 "SkBlitRow_opts_arm.h"
 
-#include "SkBlitRow.h"
 #include "SkBlitMask.h"
+#include "SkBlitRow.h"
 #include "SkColorPriv.h"
 #include "SkDither.h"
+#include "SkMathPriv.h"
+#include "SkUtils.h"
 
-#if defined(__ARM_HAVE_NEON)
-#include <arm_neon.h>
-#endif
+#include "SkCachePreload_arm.h"
 
-#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
-static void S32A_D565_Opaque_neon(uint16_t* SK_RESTRICT dst,
-                                  const SkPMColor* SK_RESTRICT src, int count,
-                                  U8CPU alpha, int /*x*/, int /*y*/) {
-    SkASSERT(255 == alpha);
+#if USE_ARM_CODE
 
-    if (count >= 8) {
-        uint16_t* SK_RESTRICT keep_dst;
-        
-        asm volatile (
-                      "ands       ip, %[count], #7            \n\t"
-                      "vmov.u8    d31, #1<<7                  \n\t"
-                      "vld1.16    {q12}, [%[dst]]             \n\t"
-                      "vld4.8     {d0-d3}, [%[src]]           \n\t"
-                      "moveq      ip, #8                      \n\t"
-                      "mov        %[keep_dst], %[dst]         \n\t"
-                      
-                      "add        %[src], %[src], ip, LSL#2   \n\t"
-                      "add        %[dst], %[dst], ip, LSL#1   \n\t"
-                      "subs       %[count], %[count], ip      \n\t"
-                      "b          9f                          \n\t"
-                      // LOOP
-                      "2:                                         \n\t"
-                      
-                      "vld1.16    {q12}, [%[dst]]!            \n\t"
-                      "vld4.8     {d0-d3}, [%[src]]!          \n\t"
-                      "vst1.16    {q10}, [%[keep_dst]]        \n\t"
-                      "sub        %[keep_dst], %[dst], #8*2   \n\t"
-                      "subs       %[count], %[count], #8      \n\t"
-                      "9:                                         \n\t"
-                      "pld        [%[dst],#32]                \n\t"
-                      // expand 0565 q12 to 8888 {d4-d7}
-                      "vmovn.u16  d4, q12                     \n\t"
-                      "vshr.u16   q11, q12, #5                \n\t"
-                      "vshr.u16   q10, q12, #6+5              \n\t"
-                      "vmovn.u16  d5, q11                     \n\t"
-                      "vmovn.u16  d6, q10                     \n\t"
-                      "vshl.u8    d4, d4, #3                  \n\t"
-                      "vshl.u8    d5, d5, #2                  \n\t"
-                      "vshl.u8    d6, d6, #3                  \n\t"
-                      
-                      "vmovl.u8   q14, d31                    \n\t"
-                      "vmovl.u8   q13, d31                    \n\t"
-                      "vmovl.u8   q12, d31                    \n\t"
-                      
-                      // duplicate in 4/2/1 & 8pix vsns
-                      "vmvn.8     d30, d3                     \n\t"
-                      "vmlal.u8   q14, d30, d6                \n\t"
-                      "vmlal.u8   q13, d30, d5                \n\t"
-                      "vmlal.u8   q12, d30, d4                \n\t"
-                      "vshr.u16   q8, q14, #5                 \n\t"
-                      "vshr.u16   q9, q13, #6                 \n\t"
-                      "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
-                      
-                      "vqadd.u8   d5, d5, d1                  \n\t"
-                      "vqadd.u8   d4, d4, d2                  \n\t"
-                      
-                      // pack 8888 {d4-d6} to 0565 q10
-                      "vshll.u8   q10, d6, #8                 \n\t"
-                      "vshll.u8   q3, d5, #8                  \n\t"
-                      "vshll.u8   q2, d4, #8                  \n\t"
-                      "vsri.u16   q10, q3, #5                 \n\t"
-                      "vsri.u16   q10, q2, #11                \n\t"
-                      
-                      "bne        2b                          \n\t"
-                      
-                      "1:                                         \n\t"
-                      "vst1.16      {q10}, [%[keep_dst]]      \n\t"
-                      : [count] "+r" (count)
-                      : [dst] "r" (dst), [keep_dst] "r" (keep_dst), [src] "r" (src) 
-                      : "ip", "cc", "memory", "d0","d1","d2","d3","d4","d5","d6","d7",
-                      "d16","d17","d18","d19","d20","d21","d22","d23","d24","d25","d26","d27","d28","d29",
-                      "d30","d31"
-                      );
-    }
-    else 
-    {   // handle count < 8
-        uint16_t* SK_RESTRICT keep_dst;
-        
-        asm volatile (
-                      "vmov.u8    d31, #1<<7                  \n\t"
-                      "mov        %[keep_dst], %[dst]         \n\t"
-                      
-                      "tst        %[count], #4                \n\t"
-                      "beq        14f                         \n\t"
-                      "vld1.16    {d25}, [%[dst]]!            \n\t"
-                      "vld1.32    {q1}, [%[src]]!             \n\t"
-                      
-                      "14:                                        \n\t"
-                      "tst        %[count], #2                \n\t"
-                      "beq        12f                         \n\t"
-                      "vld1.32    {d24[1]}, [%[dst]]!         \n\t"
-                      "vld1.32    {d1}, [%[src]]!             \n\t"
-                      
-                      "12:                                        \n\t"
-                      "tst        %[count], #1                \n\t"
-                      "beq        11f                         \n\t"
-                      "vld1.16    {d24[1]}, [%[dst]]!         \n\t"
-                      "vld1.32    {d0[1]}, [%[src]]!          \n\t"
-                      
-                      "11:                                        \n\t"
-                      // unzips achieve the same as a vld4 operation
-                      "vuzpq.u16  q0, q1                      \n\t"
-                      "vuzp.u8    d0, d1                      \n\t"
-                      "vuzp.u8    d2, d3                      \n\t"
-                      // expand 0565 q12 to 8888 {d4-d7}
-                      "vmovn.u16  d4, q12                     \n\t"
-                      "vshr.u16   q11, q12, #5                \n\t"
-                      "vshr.u16   q10, q12, #6+5              \n\t"
-                      "vmovn.u16  d5, q11                     \n\t"
-                      "vmovn.u16  d6, q10                     \n\t"
-                      "vshl.u8    d4, d4, #3                  \n\t"
-                      "vshl.u8    d5, d5, #2                  \n\t"
-                      "vshl.u8    d6, d6, #3                  \n\t"
-                      
-                      "vmovl.u8   q14, d31                    \n\t"
-                      "vmovl.u8   q13, d31                    \n\t"
-                      "vmovl.u8   q12, d31                    \n\t"
-                      
-                      // duplicate in 4/2/1 & 8pix vsns
-                      "vmvn.8     d30, d3                     \n\t"
-                      "vmlal.u8   q14, d30, d6                \n\t"
-                      "vmlal.u8   q13, d30, d5                \n\t"
-                      "vmlal.u8   q12, d30, d4                \n\t"
-                      "vshr.u16   q8, q14, #5                 \n\t"
-                      "vshr.u16   q9, q13, #6                 \n\t"
-                      "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
-                      
-                      "vqadd.u8   d5, d5, d1                  \n\t"
-                      "vqadd.u8   d4, d4, d2                  \n\t"
-                      
-                      // pack 8888 {d4-d6} to 0565 q10
-                      "vshll.u8   q10, d6, #8                 \n\t"
-                      "vshll.u8   q3, d5, #8                  \n\t"
-                      "vshll.u8   q2, d4, #8                  \n\t"
-                      "vsri.u16   q10, q3, #5                 \n\t"
-                      "vsri.u16   q10, q2, #11                \n\t"
-                      
-                      // store
-                      "tst        %[count], #4                \n\t"
-                      "beq        24f                         \n\t"
-                      "vst1.16    {d21}, [%[keep_dst]]!       \n\t"
-                      
-                      "24:                                        \n\t"
-                      "tst        %[count], #2                \n\t"
-                      "beq        22f                         \n\t"
-                      "vst1.32    {d20[1]}, [%[keep_dst]]!    \n\t"
-                      
-                      "22:                                        \n\t"
-                      "tst        %[count], #1                \n\t"
-                      "beq        21f                         \n\t"
-                      "vst1.16    {d20[1]}, [%[keep_dst]]!    \n\t"
-                      
-                      "21:                                        \n\t"
-                      : [count] "+r" (count)
-                      : [dst] "r" (dst), [keep_dst] "r" (keep_dst), [src] "r" (src)
-                      : "ip", "cc", "memory", "d0","d1","d2","d3","d4","d5","d6","d7",
-                      "d16","d17","d18","d19","d20","d21","d22","d23","d24","d25","d26","d27","d28","d29",
-                      "d30","d31"
-                      );
-    }
-}
-
-static void S32A_D565_Blend_neon(uint16_t* SK_RESTRICT dst,
-                                 const SkPMColor* SK_RESTRICT src, int count,
-                                 U8CPU alpha, int /*x*/, int /*y*/) {
-
-    U8CPU alpha_for_asm = alpha;
-
-    asm volatile (
-    /* This code implements a Neon version of S32A_D565_Blend. The output differs from
-     * the original in two respects:
-     *  1. The results have a few mismatches compared to the original code. These mismatches
-     *     never exceed 1. It's possible to improve accuracy vs. a floating point
-     *     implementation by introducing rounding right shifts (vrshr) for the final stage.
-     *     Rounding is not present in the code below, because although results would be closer
-     *     to a floating point implementation, the number of mismatches compared to the 
-     *     original code would be far greater.
-     *  2. On certain inputs, the original code can overflow, causing colour channels to
-     *     mix. Although the Neon code can also overflow, it doesn't allow one colour channel
-     *     to affect another.
-     */
-                  
-#if 1
-		/* reflects SkAlpha255To256()'s change from a+a>>7 to a+1 */
-                  "add        %[alpha], %[alpha], #1         \n\t"   // adjust range of alpha 0-256
-#else
-                  "add        %[alpha], %[alpha], %[alpha], lsr #7    \n\t"   // adjust range of alpha 0-256
-#endif
-                  "vmov.u16   q3, #255                        \n\t"   // set up constant
-                  "movs       r4, %[count], lsr #3            \n\t"   // calc. count>>3
-                  "vmov.u16   d2[0], %[alpha]                 \n\t"   // move alpha to Neon
-                  "beq        2f                              \n\t"   // if count8 == 0, exit
-                  "vmov.u16   q15, #0x1f                      \n\t"   // set up blue mask
-                  
-                  "1:                                             \n\t"
-                  "vld1.u16   {d0, d1}, [%[dst]]              \n\t"   // load eight dst RGB565 pixels
-                  "subs       r4, r4, #1                      \n\t"   // decrement loop counter
-                  "vld4.u8    {d24, d25, d26, d27}, [%[src]]! \n\t"   // load eight src ABGR32 pixels
-                  //  and deinterleave
-                  
-                  "vshl.u16   q9, q0, #5                      \n\t"   // shift green to top of lanes
-                  "vand       q10, q0, q15                    \n\t"   // extract blue
-                  "vshr.u16   q8, q0, #11                     \n\t"   // extract red
-                  "vshr.u16   q9, q9, #10                     \n\t"   // extract green
-                  // dstrgb = {q8, q9, q10}
-                  
-                  "vshr.u8    d24, d24, #3                    \n\t"   // shift red to 565 range
-                  "vshr.u8    d25, d25, #2                    \n\t"   // shift green to 565 range
-                  "vshr.u8    d26, d26, #3                    \n\t"   // shift blue to 565 range
-                  
-                  "vmovl.u8   q11, d24                        \n\t"   // widen red to 16 bits
-                  "vmovl.u8   q12, d25                        \n\t"   // widen green to 16 bits
-                  "vmovl.u8   q14, d27                        \n\t"   // widen alpha to 16 bits
-                  "vmovl.u8   q13, d26                        \n\t"   // widen blue to 16 bits
-                  // srcrgba = {q11, q12, q13, q14}
-                  
-                  "vmul.u16   q2, q14, d2[0]                  \n\t"   // sa * src_scale
-                  "vmul.u16   q11, q11, d2[0]                 \n\t"   // red result = src_red * src_scale
-                  "vmul.u16   q12, q12, d2[0]                 \n\t"   // grn result = src_grn * src_scale
-                  "vmul.u16   q13, q13, d2[0]                 \n\t"   // blu result = src_blu * src_scale
-                  
-                  "vshr.u16   q2, q2, #8                      \n\t"   // sa * src_scale >> 8
-                  "vsub.u16   q2, q3, q2                      \n\t"   // 255 - (sa * src_scale >> 8)
-                  // dst_scale = q2
-                  
-                  "vmla.u16   q11, q8, q2                     \n\t"   // red result += dst_red * dst_scale
-                  "vmla.u16   q12, q9, q2                     \n\t"   // grn result += dst_grn * dst_scale
-                  "vmla.u16   q13, q10, q2                    \n\t"   // blu result += dst_blu * dst_scale
-
-#if 1
-	// trying for a better match with SkDiv255Round(a)
-	// C alg is:  a+=128; (a+a>>8)>>8
-	// we'll use just a rounding shift [q2 is available for scratch]
-                  "vrshr.u16   q11, q11, #8                    \n\t"   // shift down red
-                  "vrshr.u16   q12, q12, #8                    \n\t"   // shift down green
-                  "vrshr.u16   q13, q13, #8                    \n\t"   // shift down blue
-#else
-	// arm's original "truncating divide by 256"
-                  "vshr.u16   q11, q11, #8                    \n\t"   // shift down red
-                  "vshr.u16   q12, q12, #8                    \n\t"   // shift down green
-                  "vshr.u16   q13, q13, #8                    \n\t"   // shift down blue
-#endif
-                  
-                  "vsli.u16   q13, q12, #5                    \n\t"   // insert green into blue
-                  "vsli.u16   q13, q11, #11                   \n\t"   // insert red into green/blue
-                  "vst1.16    {d26, d27}, [%[dst]]!           \n\t"   // write pixel back to dst, update ptr
-                  
-                  "bne        1b                              \n\t"   // if counter != 0, loop
-                  "2:                                             \n\t"   // exit
-                  
-                  : [src] "+r" (src), [dst] "+r" (dst), [count] "+r" (count), [alpha] "+r" (alpha_for_asm)
-                  :
-                  : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
-                  );
-
-    count &= 7;
-    if (count > 0) {
-        do {
-            SkPMColor sc = *src++;
-            if (sc) {
-                uint16_t dc = *dst;
-                unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
-                unsigned dr = SkMulS16(SkPacked32ToR16(sc), alpha) + SkMulS16(SkGetPackedR16(dc), dst_scale);
-                unsigned dg = SkMulS16(SkPacked32ToG16(sc), alpha) + SkMulS16(SkGetPackedG16(dc), dst_scale);
-                unsigned db = SkMulS16(SkPacked32ToB16(sc), alpha) + SkMulS16(SkGetPackedB16(dc), dst_scale);
-                *dst = SkPackRGB16(SkDiv255Round(dr), SkDiv255Round(dg), SkDiv255Round(db));
-            }
-            dst += 1;
-        } while (--count != 0);
-    }
-}
-
-/* dither matrix for Neon, derived from gDitherMatrix_3Bit_16.
- * each dither value is spaced out into byte lanes, and repeated
- * to allow an 8-byte load from offsets 0, 1, 2 or 3 from the
- * start of each row.
- */
-static const uint8_t gDitherMatrix_Neon[48] = {
-    0, 4, 1, 5, 0, 4, 1, 5, 0, 4, 1, 5,
-    6, 2, 7, 3, 6, 2, 7, 3, 6, 2, 7, 3,
-    1, 5, 0, 4, 1, 5, 0, 4, 1, 5, 0, 4,
-    7, 3, 6, 2, 7, 3, 6, 2, 7, 3, 6, 2,
-    
-};
-
-static void S32_D565_Blend_Dither_neon(uint16_t *dst, const SkPMColor *src,
-                                       int count, U8CPU alpha, int x, int y)
-{
-    /* select row and offset for dither array */
-    const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
-    
-    /* rescale alpha to range 0 - 256 */
-    int scale = SkAlpha255To256(alpha);
-    
-    asm volatile (
-                  "vld1.8         {d31}, [%[dstart]]              \n\t"   // load dither values
-                  "vshr.u8        d30, d31, #1                    \n\t"   // calc. green dither values
-                  "vdup.16        d6, %[scale]                    \n\t"   // duplicate scale into neon reg
-                  "vmov.i8        d29, #0x3f                      \n\t"   // set up green mask
-                  "vmov.i8        d28, #0x1f                      \n\t"   // set up blue mask
-                  "1:                                                 \n\t"
-                  "vld4.8         {d0, d1, d2, d3}, [%[src]]!     \n\t"   // load 8 pixels and split into argb
-                  "vshr.u8        d22, d0, #5                     \n\t"   // calc. red >> 5
-                  "vshr.u8        d23, d1, #6                     \n\t"   // calc. green >> 6
-                  "vshr.u8        d24, d2, #5                     \n\t"   // calc. blue >> 5
-                  "vaddl.u8       q8, d0, d31                     \n\t"   // add in dither to red and widen
-                  "vaddl.u8       q9, d1, d30                     \n\t"   // add in dither to green and widen
-                  "vaddl.u8       q10, d2, d31                    \n\t"   // add in dither to blue and widen
-                  "vsubw.u8       q8, q8, d22                     \n\t"   // sub shifted red from result
-                  "vsubw.u8       q9, q9, d23                     \n\t"   // sub shifted green from result
-                  "vsubw.u8       q10, q10, d24                   \n\t"   // sub shifted blue from result
-                  "vshrn.i16      d22, q8, #3                     \n\t"   // shift right and narrow to 5 bits
-                  "vshrn.i16      d23, q9, #2                     \n\t"   // shift right and narrow to 6 bits
-                  "vshrn.i16      d24, q10, #3                    \n\t"   // shift right and narrow to 5 bits
-                  // load 8 pixels from dst, extract rgb
-                  "vld1.16        {d0, d1}, [%[dst]]              \n\t"   // load 8 pixels
-                  "vshrn.i16      d17, q0, #5                     \n\t"   // shift green down to bottom 6 bits
-                  "vmovn.i16      d18, q0                         \n\t"   // narrow to get blue as bytes
-                  "vshr.u16       q0, q0, #11                     \n\t"   // shift down to extract red
-                  "vand           d17, d17, d29                   \n\t"   // and green with green mask
-                  "vand           d18, d18, d28                   \n\t"   // and blue with blue mask
-                  "vmovn.i16      d16, q0                         \n\t"   // narrow to get red as bytes
-                  // src = {d22 (r), d23 (g), d24 (b)}
-                  // dst = {d16 (r), d17 (g), d18 (b)}
-                  // subtract dst from src and widen
-                  "vsubl.s8       q0, d22, d16                    \n\t"   // subtract red src from dst
-                  "vsubl.s8       q1, d23, d17                    \n\t"   // subtract green src from dst
-                  "vsubl.s8       q2, d24, d18                    \n\t"   // subtract blue src from dst
-                  // multiply diffs by scale and shift
-                  "vmul.i16       q0, q0, d6[0]                   \n\t"   // multiply red by scale
-                  "vmul.i16       q1, q1, d6[0]                   \n\t"   // multiply blue by scale
-                  "vmul.i16       q2, q2, d6[0]                   \n\t"   // multiply green by scale
-                  "subs           %[count], %[count], #8          \n\t"   // decrement loop counter
-                  "vshrn.i16      d0, q0, #8                      \n\t"   // shift down red by 8 and narrow
-                  "vshrn.i16      d2, q1, #8                      \n\t"   // shift down green by 8 and narrow
-                  "vshrn.i16      d4, q2, #8                      \n\t"   // shift down blue by 8 and narrow
-                  // add dst to result
-                  "vaddl.s8       q0, d0, d16                     \n\t"   // add dst to red
-                  "vaddl.s8       q1, d2, d17                     \n\t"   // add dst to green
-                  "vaddl.s8       q2, d4, d18                     \n\t"   // add dst to blue
-                  // put result into 565 format
-                  "vsli.i16       q2, q1, #5                      \n\t"   // shift up green and insert into blue
-                  "vsli.i16       q2, q0, #11                     \n\t"   // shift up red and insert into blue
-                  "vst1.16        {d4, d5}, [%[dst]]!             \n\t"   // store result
-                  "bgt            1b                              \n\t"   // loop if count > 0
-                  : [src] "+r" (src), [dst] "+r" (dst), [count] "+r" (count)
-                  : [dstart] "r" (dstart), [scale] "r" (scale)
-                  : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d28", "d29", "d30", "d31"
-                  );
-    
-    DITHER_565_SCAN(y);
-    
-    while((count & 7) > 0)
-    {
-        SkPMColor c = *src++;
-        
-        int dither = DITHER_VALUE(x);
-        int sr = SkGetPackedR32(c);
-        int sg = SkGetPackedG32(c);
-        int sb = SkGetPackedB32(c);
-        sr = SkDITHER_R32To565(sr, dither);
-        sg = SkDITHER_G32To565(sg, dither);
-        sb = SkDITHER_B32To565(sb, dither);
-        
-        uint16_t d = *dst;
-        *dst++ = SkPackRGB16(SkAlphaBlend(sr, SkGetPackedR16(d), scale),
-                             SkAlphaBlend(sg, SkGetPackedG16(d), scale),
-                             SkAlphaBlend(sb, SkGetPackedB16(d), scale));
-        DITHER_INC_X(x);
-        count--;
-    }
-}
-
-#define S32A_D565_Opaque_PROC       S32A_D565_Opaque_neon
-#define S32A_D565_Blend_PROC        S32A_D565_Blend_neon
-#define S32_D565_Blend_Dither_PROC  S32_D565_Blend_Dither_neon
-#elif __ARM_ARCH__ >= 7 && !defined(SK_CPU_BENDIAN)
-static void S32A_D565_Opaque_v7(uint16_t* SK_RESTRICT dst,
-                                  const SkPMColor* SK_RESTRICT src, int count,
-                                  U8CPU alpha, int /*x*/, int /*y*/) {
+static void S32A_D565_Opaque(uint16_t* SK_RESTRICT dst,
+                             const SkPMColor* SK_RESTRICT src, int count,
+                             U8CPU alpha, int /*x*/, int /*y*/) {
     SkASSERT(255 == alpha);
 
     asm volatile (
@@ -432,14 +45,25 @@
                   "ldrh    r4, [%[dst]]                 \n\t"
                   "rsb     r7, r7, #255                 \n\t"
                   "and     r6, r4, #0x001f              \n\t"
+#if SK_ARM_ARCH == 6
+                  "lsl     r5, r4, #21                  \n\t"
+                  "lsr     r5, r5, #26                  \n\t"
+#else
                   "ubfx    r5, r4, #5, #6               \n\t"
+#endif
                   "pld     [r0, #16]                    \n\t"
                   "lsr     r4, r4, #11                  \n\t"
+#ifdef SK_ARM_HAS_EDSP
                   "smulbb  r6, r6, r7                   \n\t"
                   "smulbb  r5, r5, r7                   \n\t"
                   "smulbb  r4, r4, r7                   \n\t"
-                  "ubfx    r7, r3, #16, #8              \n\t"
-                  "ubfx    ip, r3, #8, #8               \n\t"
+#else
+                  "mul     r6, r6, r7                   \n\t"
+                  "mul     r5, r5, r7                   \n\t"
+                  "mul     r4, r4, r7                   \n\t"
+#endif
+                  "uxtb    r7, r3, ROR #16              \n\t"
+                  "uxtb    ip, r3, ROR #8               \n\t"
                   "and     r3, r3, #0xff                \n\t"
                   "add     r6, r6, #16                  \n\t"
                   "add     r5, r5, #32                  \n\t"
@@ -470,629 +94,6 @@
                   : "memory", "cc", "r3", "r4", "r5", "r6", "r7", "ip"
                   );
 }
-#define S32A_D565_Opaque_PROC       S32A_D565_Opaque_v7
-#define S32A_D565_Blend_PROC        NULL
-#define S32_D565_Blend_Dither_PROC  NULL
-#else
-#define S32A_D565_Opaque_PROC       NULL
-#define S32A_D565_Blend_PROC        NULL
-#define S32_D565_Blend_Dither_PROC  NULL
-#endif
-
-/* Don't have a special version that assumes each src is opaque, but our S32A
-    is still faster than the default, so use it here
- */
-#define S32_D565_Opaque_PROC    S32A_D565_Opaque_PROC
-#define S32_D565_Blend_PROC     S32A_D565_Blend_PROC
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN) && defined(TEST_SRC_ALPHA)
-
-static void S32A_Opaque_BlitRow32_neon_test_alpha(SkPMColor* SK_RESTRICT dst,
-                                  const SkPMColor* SK_RESTRICT src,
-                                  int count, U8CPU alpha) {
-	SkASSERT(255 == alpha);
-	if (count <= 0)
-	return;
-
-	/* Use these to check if src is transparent or opaque */
-	const unsigned int ALPHA_OPAQ  = 0xFF000000;
-	const unsigned int ALPHA_TRANS = 0x00FFFFFF;
-
-#define UNROLL  4
-	const SkPMColor* SK_RESTRICT src_end = src + count - (UNROLL + 1);
-	const SkPMColor* SK_RESTRICT src_temp = src;
-
-	/* set up the NEON variables */
-	uint8x8_t alpha_mask;
-	static const uint8_t alpha_mask_setup[] = {3,3,3,3,7,7,7,7};
-	alpha_mask = vld1_u8(alpha_mask_setup);
-
-	uint8x8_t src_raw, dst_raw, dst_final;
-	uint8x8_t src_raw_2, dst_raw_2, dst_final_2;
-	uint8x8_t dst_cooked;
-	uint16x8_t dst_wide;
-	uint8x8_t alpha_narrow;
-	uint16x8_t alpha_wide;
-
-	/* choose the first processing type */
-	if( src >= src_end)
-		goto TAIL;
-	if(*src <= ALPHA_TRANS)
-		goto ALPHA_0;
-	if(*src >= ALPHA_OPAQ)
-		goto ALPHA_255;
-	/* fall-thru */
-
-ALPHA_1_TO_254:
-	do {
-
-		/* get the source */
-		src_raw = vreinterpret_u8_u32(vld1_u32(src));
-		src_raw_2 = vreinterpret_u8_u32(vld1_u32(src+2));
-
-		/* get and hold the dst too */
-		dst_raw = vreinterpret_u8_u32(vld1_u32(dst));
-		dst_raw_2 = vreinterpret_u8_u32(vld1_u32(dst+2));
-
-
-		/* get the alphas spread out properly */
-		alpha_narrow = vtbl1_u8(src_raw, alpha_mask);
-		/* reflect SkAlpha255To256() semantics a+1 vs a+a>>7 */
-		/* we collapsed (255-a)+1 ... */
-		alpha_wide = vsubw_u8(vdupq_n_u16(256), alpha_narrow);
-
-		/* spread the dest */
-		dst_wide = vmovl_u8(dst_raw);
-
-		/* alpha mul the dest */
-		dst_wide = vmulq_u16 (dst_wide, alpha_wide);
-		dst_cooked = vshrn_n_u16(dst_wide, 8);
-
-		/* sum -- ignoring any byte lane overflows */
-		dst_final = vadd_u8(src_raw, dst_cooked);
-
-		alpha_narrow = vtbl1_u8(src_raw_2, alpha_mask);
-		/* reflect SkAlpha255To256() semantics a+1 vs a+a>>7 */
-		/* we collapsed (255-a)+1 ... */
-		alpha_wide = vsubw_u8(vdupq_n_u16(256), alpha_narrow);
-
-		/* spread the dest */
-		dst_wide = vmovl_u8(dst_raw_2);
-
-		/* alpha mul the dest */
-		dst_wide = vmulq_u16 (dst_wide, alpha_wide);
-		dst_cooked = vshrn_n_u16(dst_wide, 8);
-
-		/* sum -- ignoring any byte lane overflows */
-		dst_final_2 = vadd_u8(src_raw_2, dst_cooked);
-
-		vst1_u32(dst, vreinterpret_u32_u8(dst_final));
-		vst1_u32(dst+2, vreinterpret_u32_u8(dst_final_2));
-
-		src += UNROLL;
-		dst += UNROLL;
-
-		/* if 2 of the next pixels aren't between 1 and 254
-		it might make sense to go to the optimized loops */
-		if((src[0] <= ALPHA_TRANS && src[1] <= ALPHA_TRANS) || (src[0] >= ALPHA_OPAQ && src[1] >= ALPHA_OPAQ))
-			break;
-
-	} while(src < src_end);
-
-	if (src >= src_end)
-		goto TAIL;
-
-	if(src[0] >= ALPHA_OPAQ && src[1] >= ALPHA_OPAQ)
-		goto ALPHA_255;
-
-	/*fall-thru*/
-
-ALPHA_0:
-
-	/*In this state, we know the current alpha is 0 and
-	 we optimize for the next alpha also being zero. */
-	src_temp = src;  //so we don't have to increment dst every time
-	do {
-		if(*(++src) > ALPHA_TRANS)
-			break;
-		if(*(++src) > ALPHA_TRANS)
-			break;
-		if(*(++src) > ALPHA_TRANS)
-			break;
-		if(*(++src) > ALPHA_TRANS)
-			break;
-	} while(src < src_end);
-
-	dst += (src - src_temp);
-
-	/* no longer alpha 0, so determine where to go next. */
-	if( src >= src_end)
-		goto TAIL;
-	if(*src >= ALPHA_OPAQ)
-		goto ALPHA_255;
-	else
-		goto ALPHA_1_TO_254;
-
-ALPHA_255:
-	while((src[0] & src[1] & src[2] & src[3]) >= ALPHA_OPAQ) {
-		dst[0]=src[0];
-		dst[1]=src[1];
-		dst[2]=src[2];
-		dst[3]=src[3];
-		src+=UNROLL;
-		dst+=UNROLL;
-		if(src >= src_end)
-			goto TAIL;
-	}
-
-	//Handle remainder.
-	if(*src >= ALPHA_OPAQ) { *dst++ = *src++;
-		if(*src >= ALPHA_OPAQ) { *dst++ = *src++;
-			if(*src >= ALPHA_OPAQ) { *dst++ = *src++; }
-		}
-	}
-
-	if( src >= src_end)
-		goto TAIL;
-	if(*src <= ALPHA_TRANS)
-		goto ALPHA_0;
-	else
-		goto ALPHA_1_TO_254;
-
-TAIL:
-	/* do any residual iterations */
-	src_end += UNROLL + 1;  //goto the real end
-	while(src != src_end) {
-		if( *src != 0 ) {
-			if( *src >= ALPHA_OPAQ ) {
-				*dst = *src;
-			}
-			else {
-				*dst = SkPMSrcOver(*src, *dst);
-			}
-		}
-		src++;
-		dst++;
-	}
-	return;
-}
-
-#define S32A_Opaque_BlitRow32_PROC  S32A_Opaque_BlitRow32_neon_test_alpha
-
-#elif defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
-
-static void S32A_Opaque_BlitRow32_neon(SkPMColor* SK_RESTRICT dst,
-                                  const SkPMColor* SK_RESTRICT src,
-                                  int count, U8CPU alpha) {
-
-    SkASSERT(255 == alpha);
-    if (count > 0) {
-
-
-	uint8x8_t alpha_mask;
-
-	static const uint8_t alpha_mask_setup[] = {3,3,3,3,7,7,7,7};
-	alpha_mask = vld1_u8(alpha_mask_setup);
-
-	/* do the NEON unrolled code */
-#define	UNROLL	4
-	while (count >= UNROLL) {
-	    uint8x8_t src_raw, dst_raw, dst_final;
-	    uint8x8_t src_raw_2, dst_raw_2, dst_final_2;
-
-	    /* get the source */
-	    src_raw = vreinterpret_u8_u32(vld1_u32(src));
-#if	UNROLL > 2
-	    src_raw_2 = vreinterpret_u8_u32(vld1_u32(src+2));
-#endif
-
-	    /* get and hold the dst too */
-	    dst_raw = vreinterpret_u8_u32(vld1_u32(dst));
-#if	UNROLL > 2
-	    dst_raw_2 = vreinterpret_u8_u32(vld1_u32(dst+2));
-#endif
-
-	/* 1st and 2nd bits of the unrolling */
-	{
-	    uint8x8_t dst_cooked;
-	    uint16x8_t dst_wide;
-	    uint8x8_t alpha_narrow;
-	    uint16x8_t alpha_wide;
-
-	    /* get the alphas spread out properly */
-	    alpha_narrow = vtbl1_u8(src_raw, alpha_mask);
-#if 1
-	    /* reflect SkAlpha255To256() semantics a+1 vs a+a>>7 */
-	    /* we collapsed (255-a)+1 ... */
-	    alpha_wide = vsubw_u8(vdupq_n_u16(256), alpha_narrow);
-#else
-	    alpha_wide = vsubw_u8(vdupq_n_u16(255), alpha_narrow);
-	    alpha_wide = vaddq_u16(alpha_wide, vshrq_n_u16(alpha_wide,7));
-#endif
-
-	    /* spread the dest */
-	    dst_wide = vmovl_u8(dst_raw);
-
-	    /* alpha mul the dest */
-	    dst_wide = vmulq_u16 (dst_wide, alpha_wide);
-	    dst_cooked = vshrn_n_u16(dst_wide, 8);
-
-	    /* sum -- ignoring any byte lane overflows */
-	    dst_final = vadd_u8(src_raw, dst_cooked);
-	}
-
-#if	UNROLL > 2
-	/* the 3rd and 4th bits of our unrolling */
-	{
-	    uint8x8_t dst_cooked;
-	    uint16x8_t dst_wide;
-	    uint8x8_t alpha_narrow;
-	    uint16x8_t alpha_wide;
-
-	    alpha_narrow = vtbl1_u8(src_raw_2, alpha_mask);
-#if 1
-	    /* reflect SkAlpha255To256() semantics a+1 vs a+a>>7 */
-	    /* we collapsed (255-a)+1 ... */
-	    alpha_wide = vsubw_u8(vdupq_n_u16(256), alpha_narrow);
-#else
-	    alpha_wide = vsubw_u8(vdupq_n_u16(255), alpha_narrow);
-	    alpha_wide = vaddq_u16(alpha_wide, vshrq_n_u16(alpha_wide,7));
-#endif
-
-	    /* spread the dest */
-	    dst_wide = vmovl_u8(dst_raw_2);
-
-	    /* alpha mul the dest */
-	    dst_wide = vmulq_u16 (dst_wide, alpha_wide);
-	    dst_cooked = vshrn_n_u16(dst_wide, 8);
-
-	    /* sum -- ignoring any byte lane overflows */
-	    dst_final_2 = vadd_u8(src_raw_2, dst_cooked);
-	}
-#endif
-
-	    vst1_u32(dst, vreinterpret_u32_u8(dst_final));
-#if	UNROLL > 2
-	    vst1_u32(dst+2, vreinterpret_u32_u8(dst_final_2));
-#endif
-
-	    src += UNROLL;
-	    dst += UNROLL;
-	    count -= UNROLL;
-	}
-#undef	UNROLL
-
-	/* do any residual iterations */
-        while (--count >= 0) {
-#ifdef TEST_SRC_ALPHA
-            SkPMColor sc = *src;
-            if (sc) {
-                unsigned srcA = SkGetPackedA32(sc);
-                SkPMColor result = sc;
-                if (srcA != 255) {
-                    result = SkPMSrcOver(sc, *dst);
-                }
-                *dst = result;
-            }
-#else
-            *dst = SkPMSrcOver(*src, *dst);
-#endif
-            src += 1;
-            dst += 1;
-        }
-    }
-}
-
-#define	S32A_Opaque_BlitRow32_PROC	S32A_Opaque_BlitRow32_neon
-
-#elif defined (__ARM_ARCH__) /* #if defined(__ARM_HAVE_NEON) && defined... */
-
-#if defined(TEST_SRC_ALPHA)
-
-static void __attribute__((naked)) S32A_Opaque_BlitRow32_arm_test_alpha
-                                        (SkPMColor* SK_RESTRICT dst,
-                                         const SkPMColor* SK_RESTRICT src,
-                                         int count, U8CPU alpha) {
-
-/* Optimizes for alpha == 0, alpha == 255, and 1 < alpha < 255 cases individually */
-/* Predicts that the next pixel will have the same alpha type as the current pixel */
-
-asm volatile (
-
-    "\tSTMDB  r13!, {r4-r12, r14}        \n" /* saving r4-r12, lr on the stack */
-                                             /* we should not save r0-r3 according to ABI */
-
-    "\tCMP    r2, #0                     \n" /* if (count == 0) */
-    "\tBEQ    9f                         \n" /* go to EXIT */
-
-    "\tMOV    r12, #0xff                 \n" /* load the 0xff mask in r12 */
-    "\tORR    r12, r12, r12, LSL #16     \n" /* convert it to 0xff00ff in r12 */
-
-    "\tMOV    r14, #255                  \n" /* r14 = 255 */
-                                             /* will be used later for left-side comparison */
-
-    "\tADD    r2, %[src], r2, LSL #2     \n" /* r2 points to last array element which can be used */
-    "\tSUB    r2, r2, #16                \n" /* as a base for 4-way processing algorithm */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] array pointer is bigger than */
-    "\tBGT    8f                         \n" /* calculated marker for 4-way -> */
-                                             /* use simple one-by-one processing */
-
-    /* START OF DISPATCHING BLOCK */
-
-    "\t0:                                \n"
-
-    "\tLDM    %[src]!, {r3, r4, r5, r6}  \n" /* 4-way loading of source values to r3-r6 */
-
-    "\tLSR    r7, r3, #24                \n" /* if not all src alphas of 4-way block are equal -> */
-    "\tCMP    r7, r4, LSR #24            \n"
-    "\tCMPEQ  r7, r5, LSR #24            \n"
-    "\tCMPEQ  r7, r6, LSR #24            \n"
-    "\tBNE    1f                         \n" /* -> go to general 4-way processing routine */
-
-    "\tCMP    r14, r7                    \n" /* if all src alphas are equal to 255 */
-    "\tBEQ    3f                         \n" /* go to alpha == 255 optimized routine */
-
-    "\tCMP    r7,  #0                    \n" /* if all src alphas are equal to 0 */
-    "\tBEQ    6f                         \n" /* go to alpha == 0 optimized routine */
-
-    /* END OF DISPATCHING BLOCK */
-
-    /* START OF BLOCK OPTIMIZED FOR 0 < ALPHA < 255 */
-
-    "\t1:                                \n"
-                                             /* we do not have enough registers to make */
-                                             /* 4-way [dst] loading -> we are using 2 * 2-way */
-
-    "\tLDM    %[dst], {r7, r8}           \n" /* 1st 2-way loading of dst values to r7-r8 */
-
-    /* PROCESSING BLOCK 1 */
-    /* r3 = src, r7 = dst */
-
-    "\tLSR    r11, r3,  #24              \n" /* extracting alpha from source and storing to r11 */
-    "\tAND    r9,  r12, r7               \n" /* r9 = br masked by r12 (0xff00ff) */
-    "\tRSB    r11, r11, #256             \n" /* subtracting the alpha from 255 -> r11 = scale */
-    "\tAND    r10, r12, r7, LSR #8       \n" /* r10 = ag masked by r12 (0xff00ff) */
-    "\tMUL    r9,  r9,  r11              \n" /* br = br * scale */
-    "\tAND    r9,  r12, r9, LSR #8       \n" /* lsr br by 8 and mask it */
-    "\tMUL    r10, r10, r11              \n" /* ag = ag * scale */
-    "\tAND    r10, r10, r12, LSL #8      \n" /* mask ag with reverse mask */
-    "\tORR    r7,  r9,  r10              \n" /* br | ag */
-    "\tADD    r7,  r3,  r7               \n" /* dst = src + calc dest(r8) */
-
-    /* PROCESSING BLOCK 2 */
-    /* r4 = src, r8 = dst */
-
-    "\tLSR    r11, r4,  #24              \n" /* see PROCESSING BLOCK 1 */
-    "\tAND    r9,  r12, r8               \n"
-    "\tRSB    r11, r11, #256             \n"
-    "\tAND    r10, r12, r8, LSR #8       \n"
-    "\tMUL    r9,  r9,  r11              \n"
-    "\tAND    r9,  r12, r9, LSR #8       \n"
-    "\tMUL    r10, r10, r11              \n"
-    "\tAND    r10, r10, r12, LSL #8      \n"
-    "\tORR    r8,  r9,  r10              \n"
-    "\tADD    r8,  r4,  r8               \n"
-
-    "\tSTM    %[dst]!, {r7, r8}          \n" /* 1st 2-way storing of processed dst values */
-
-    "\tLDM    %[dst], {r9, r10}          \n" /* 2nd 2-way loading of dst values to r9-r10 */
-
-    /* PROCESSING BLOCK 3 */
-    /* r5 = src, r9 = dst */
-
-    "\tLSR    r11, r5,  #24              \n" /* see PROCESSING BLOCK 1 */
-    "\tAND    r7,  r12, r9               \n"
-    "\tRSB    r11, r11, #256             \n"
-    "\tAND    r8,  r12, r9, LSR #8       \n"
-    "\tMUL    r7,  r7,  r11              \n"
-    "\tAND    r7,  r12, r7, LSR #8       \n"
-    "\tMUL    r8,  r8,  r11              \n"
-    "\tAND    r8,  r8,  r12, LSL #8      \n"
-    "\tORR    r9,  r7,  r8               \n"
-    "\tADD    r9,  r5,  r9               \n"
-
-    /* PROCESSING BLOCK 4 */
-    /* r6 = src, r10 = dst */
-
-    "\tLSR    r11, r6,  #24              \n" /* see PROCESSING BLOCK 1 */
-    "\tAND    r7,  r12, r10              \n"
-    "\tRSB    r11, r11, #256             \n"
-    "\tAND    r8,  r12, r10, LSR #8      \n"
-    "\tMUL    r7,  r7,  r11              \n"
-    "\tAND    r7,  r12, r7, LSR #8       \n"
-    "\tMUL    r8,  r8,  r11              \n"
-    "\tAND    r8,  r8,  r12, LSL #8      \n"
-    "\tORR    r10, r7,  r8               \n"
-    "\tADD    r10, r6,  r10              \n"
-
-    "\tSTM    %[dst]!, {r9, r10}         \n" /* 2nd 2-way storing of processed dst values */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] pointer <= calculated marker */
-    "\tBLE    0b                         \n" /* we could run 4-way processing -> go to dispatcher */
-    "\tBGT    8f                         \n" /* else -> use simple one-by-one processing */
-
-    /* END OF BLOCK OPTIMIZED FOR 0 < ALPHA < 255 */
-
-    /* START OF BLOCK OPTIMIZED FOR ALPHA == 255 */
-
-    "\t2:                                \n" /* ENTRY 1: LOADING [src] to registers */
-
-    "\tLDM    %[src]!, {r3, r4, r5, r6}  \n" /* 4-way loading of source values to r3-r6 */
-
-    "\tAND    r7, r3, r4                 \n" /* if not all alphas == 255 -> */
-    "\tAND    r8, r5, r6                 \n"
-    "\tAND    r9, r7, r8                 \n"
-    "\tCMP    r14, r9, LSR #24           \n"
-    "\tBNE    4f                         \n" /* -> go to alpha == 0 check */
-
-    "\t3:                                \n" /* ENTRY 2: [src] already loaded by DISPATCHER */
-
-    "\tSTM    %[dst]!, {r3, r4, r5, r6}  \n" /* all alphas == 255 -> 4-way copy [src] to [dst] */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] array pointer <= marker */
-    "\tBLE    2b                         \n" /* we could run 4-way processing */
-                                             /* because now we're in ALPHA == 255 state */
-                                             /* run next cycle with priority alpha == 255 checks */
-
-    "\tBGT    8f                         \n" /* if our current [src] array pointer > marker */
-                                             /* use simple one-by-one processing */
-
-    "\t4:                                \n"
-
-    "\tORR    r7, r3, r4                 \n" /* if not all alphas == 0 -> */
-    "\tORR    r8, r5, r6                 \n"
-    "\tORR    r9, r7, r8                 \n"
-    "\tLSRS   r9, #24                    \n"
-    "\tBNE    1b                         \n" /* -> go to general processing mode */
-                                             /* (we already checked for alpha == 255) */
-
-    "\tADD    %[dst], %[dst], #16        \n" /* all src alphas == 0 -> do not change dst values */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] array pointer <= marker */
-    "\tBLE    5f                         \n" /* we could run 4-way processing one more time */
-                                             /* because now we're in ALPHA == 0 state */
-                                             /* run next cycle with priority alpha == 0 checks */
-
-    "\tBGT    8f                         \n" /* if our current [src] array pointer > marker */
-                                             /* use simple one-by-one processing */
-
-    /* END OF BLOCK OPTIMIZED FOR ALPHA == 255 */
-
-    /* START OF BLOCK OPTIMIZED FOR ALPHA == 0 */
-
-    "\t5:                                \n" /* ENTRY 1: LOADING [src] to registers */
-
-    "\tLDM    %[src]!, {r3, r4, r5, r6}  \n" /* 4-way loading of source values to r3-r6 */
-
-    "\tORR    r7, r3, r4                 \n" /* if not all alphas == 0 -> */
-    "\tORR    r8, r5, r6                 \n"
-    "\tORR    r9, r7, r8                 \n"
-    "\tLSRS   r9, #24                    \n"
-    "\tBNE    7f                         \n" /* -> go to alpha == 255 check */
-
-    "\t6:                                \n" /* ENTRY 2: [src] already loaded by DISPATCHER */
-
-    "\tADD    %[dst], %[dst], #16        \n" /* all src alphas == 0 -> do not change dst values */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] array pointer <= marker */
-    "\tBLE    5b                         \n" /* we could run 4-way processing one more time */
-                                             /* because now we're in ALPHA == 0 state */
-                                             /* run next cycle with priority alpha == 0 checks */
-
-    "\tBGT    8f                         \n" /* if our current [src] array pointer > marker */
-                                             /* use simple one-by-one processing */
-    "\t7:                                \n"
-
-    "\tAND    r7, r3, r4                 \n" /* if not all alphas == 255 -> */
-    "\tAND    r8, r5, r6                 \n"
-    "\tAND    r9, r7, r8                 \n"
-    "\tCMP    r14, r9, LSR #24           \n"
-    "\tBNE    1b                         \n" /* -> go to general processing mode */
-                                             /* (we already checked for alpha == 0) */
-
-    "\tSTM    %[dst]!, {r3, r4, r5, r6}  \n" /* all alphas == 255 -> 4-way copy [src] to [dst] */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] array pointer <= marker */
-    "\tBLE    2b                         \n" /* we could run 4-way processing one more time */
-                                             /* because now we're in ALPHA == 255 state */
-                                             /* run next cycle with priority alpha == 255 checks */
-
-    "\tBGT    8f                         \n" /* if our current [src] array pointer > marker */
-                                             /* use simple one-by-one processing */
-
-    /* END OF BLOCK OPTIMIZED FOR ALPHA == 0 */
-
-    /* START OF TAIL BLOCK */
-    /* (used when array is too small to be processed with 4-way algorithm)*/
-
-    "\t8:                                \n"
-
-    "\tADD    r2, r2, #16                \n" /* now r2 points to the element just after array */
-                                             /* we've done r2 = r2 - 16 at procedure start */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] array pointer > final marker */
-    "\tBEQ    9f                         \n" /* goto EXIT */
-
-    /* TAIL PROCESSING BLOCK 1 */
-
-    "\tLDR    r3, [%[src]], #4           \n" /* r3 = *src, src++ */
-    "\tLDR    r7, [%[dst]]               \n" /* r7 = *dst */
-
-    "\tLSR    r11, r3,  #24              \n" /* extracting alpha from source */
-    "\tAND    r9,  r12, r7               \n" /* r9 = br masked by r12 (0xff00ff) */
-    "\tRSB    r11, r11, #256             \n" /* subtracting the alpha from 255 -> r11 = scale */
-    "\tAND    r10, r12, r7, LSR #8       \n" /* r10 = ag masked by r12 (0xff00ff) */
-    "\tMUL    r9,  r9,  r11              \n" /* br = br * scale */
-    "\tAND    r9,  r12, r9, LSR #8       \n" /* lsr br by 8 and mask it */
-    "\tMUL    r10, r10, r11              \n" /* ag = ag * scale */
-    "\tAND    r10, r10, r12, LSL #8      \n" /* mask ag with reverse mask */
-    "\tORR    r7,  r9,  r10              \n" /* br | ag */
-    "\tADD    r7,  r3,  r7               \n" /* dst = src + calc dest(r8) */
-
-    "\tSTR    r7, [%[dst]], #4           \n" /* *dst = r7; dst++ */
-
-    "\tCMP    %[src], r2                 \n" /* if our current [src] array pointer > final marker */
-    "\tBEQ    9f                         \n" /* goto EXIT */
-
-    /* TAIL PROCESSING BLOCK 2 */
-
-    "\tLDR    r3, [%[src]], #4           \n" /* see TAIL PROCESSING BLOCK 1 */
-    "\tLDR    r7, [%[dst]]               \n"
-
-    "\tLSR    r11, r3,  #24              \n"
-    "\tAND    r9,  r12, r7               \n"
-    "\tRSB    r11, r11, #256             \n"
-    "\tAND    r10, r12, r7, LSR #8       \n"
-    "\tMUL    r9,  r9,  r11              \n"
-    "\tAND    r9,  r12, r9, LSR #8       \n"
-    "\tMUL    r10, r10, r11              \n"
-    "\tAND    r10, r10, r12, LSL #8      \n"
-    "\tORR    r7,  r9,  r10              \n"
-    "\tADD    r7,  r3,  r7               \n"
-
-    "\tSTR    r7, [%[dst]], #4           \n"
-
-    "\tCMP    %[src], r2                 \n"
-    "\tBEQ    9f                         \n"
-
-    /* TAIL PROCESSING BLOCK 3 */
-
-    "\tLDR    r3, [%[src]], #4           \n" /* see TAIL PROCESSING BLOCK 1 */
-    "\tLDR    r7, [%[dst]]               \n"
-
-    "\tLSR    r11, r3,  #24              \n"
-    "\tAND    r9,  r12, r7               \n"
-    "\tRSB    r11, r11, #256             \n"
-    "\tAND    r10, r12, r7, LSR #8       \n"
-    "\tMUL    r9,  r9,  r11              \n"
-    "\tAND    r9,  r12, r9, LSR #8       \n"
-    "\tMUL    r10, r10, r11              \n"
-    "\tAND    r10, r10, r12, LSL #8      \n"
-    "\tORR    r7,  r9,  r10              \n"
-    "\tADD    r7,  r3,  r7               \n"
-
-    "\tSTR    r7, [%[dst]], #4           \n"
-
-    /* END OF TAIL BLOCK */
-
-    "\t9:                                \n" /* EXIT */
-
-    "\tLDMIA  r13!, {r4-r12, r14}        \n" /* restoring r4-r12, lr from stack */
-    "\tBX     lr                         \n" /* return */
-
-    : [dst] "+r" (dst), [src] "+r" (src)
-    :
-    : "cc", "r2", "r3", "memory"
-
-    );
-
-}
-
-#define	S32A_Opaque_BlitRow32_PROC S32A_Opaque_BlitRow32_arm_test_alpha
-#else /* !defined(TEST_SRC_ALPHA) */
 
 static void S32A_Opaque_BlitRow32_arm(SkPMColor* SK_RESTRICT dst,
                                   const SkPMColor* SK_RESTRICT src,
@@ -1100,7 +101,6 @@
 
     SkASSERT(255 == alpha);
 
-    /* Does not support the TEST_SRC_ALPHA case */
     asm volatile (
                   "cmp    %[count], #0               \n\t" /* comparing count with 0 */
                   "beq    3f                         \n\t" /* if zero exit */
@@ -1185,18 +185,15 @@
                   : "cc", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "ip", "memory"
                   );
 }
-#define	S32A_Opaque_BlitRow32_PROC	S32A_Opaque_BlitRow32_arm
-#endif /* !defined(TEST_SRC_ALPHA) */
-#else /* ... #elif defined (__ARM_ARCH__) */
-#define	S32A_Opaque_BlitRow32_PROC	NULL
-#endif
+#endif // USE_ARM_CODE
 
 /*
  * ARM asm version of S32A_Blend_BlitRow32
  */
-static void S32A_Blend_BlitRow32_arm(SkPMColor* SK_RESTRICT dst,
-                                 const SkPMColor* SK_RESTRICT src,
-                                 int count, U8CPU alpha) {
+// This version is also used by the NEON procs table, so always compile it
+void S32A_Blend_BlitRow32_arm(SkPMColor* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src,
+                              int count, U8CPU alpha) {
     asm volatile (
                   "cmp    %[count], #0               \n\t" /* comparing count with 0 */
                   "beq    3f                         \n\t" /* if zero exit */
@@ -1218,8 +215,13 @@
                   /* dst1_scale and dst2_scale*/
                   "lsr    r9, r5, #24                \n\t" /* src >> 24 */
                   "lsr    r10, r6, #24               \n\t" /* src >> 24 */
+#ifdef SK_ARM_HAS_EDSP
                   "smulbb r9, r9, %[alpha]           \n\t" /* r9 = SkMulS16 r9 with src_scale */
                   "smulbb r10, r10, %[alpha]         \n\t" /* r10 = SkMulS16 r10 with src_scale */
+#else
+                  "mul    r9, r9, %[alpha]           \n\t" /* r9 = SkMulS16 r9 with src_scale */
+                  "mul    r10, r10, %[alpha]         \n\t" /* r10 = SkMulS16 r10 with src_scale */
+#endif
                   "lsr    r9, r9, #8                 \n\t" /* r9 >> 8 */
                   "lsr    r10, r10, #8               \n\t" /* r10 >> 8 */
                   "rsb    r9, r9, #256               \n\t" /* dst1_scale = r9 = 255 - r9 + 1 */
@@ -1288,7 +290,11 @@
 
                   "lsr    r6, r5, #24                \n\t" /* src >> 24 */
                   "and    r8, r12, r5, lsr #8        \n\t" /* ag = r8 = r5 masked by r12 lsr by #8 */
+#ifdef SK_ARM_HAS_EDSP
                   "smulbb r6, r6, %[alpha]           \n\t" /* r6 = SkMulS16 with src_scale */
+#else
+                  "mul    r6, r6, %[alpha]           \n\t" /* r6 = SkMulS16 with src_scale */
+#endif
                   "and    r9, r12, r5                \n\t" /* rb = r9 = r5 masked by r12 */
                   "lsr    r6, r6, #8                 \n\t" /* r6 >> 8 */
                   "mul    r8, r8, %[alpha]           \n\t" /* ag = r8 times scale */
@@ -1322,506 +328,34 @@
                   );
 
 }
-#define	S32A_Blend_BlitRow32_PROC	S32A_Blend_BlitRow32_arm
-
-/* Neon version of S32_Blend_BlitRow32()
- * portable version is in src/core/SkBlitRow_D32.cpp
- */
-#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
-static void S32_Blend_BlitRow32_neon(SkPMColor* SK_RESTRICT dst,
-                                const SkPMColor* SK_RESTRICT src,
-                                int count, U8CPU alpha) {
-    SkASSERT(alpha <= 255);
-    if (count > 0) {
-        uint16_t src_scale = SkAlpha255To256(alpha);
-        uint16_t dst_scale = 256 - src_scale;
-
-	/* run them N at a time through the NEON unit */
-	/* note that each 1 is 4 bytes, each treated exactly the same,
-	 * so we can work under that guise. We *do* know that the src&dst
-	 * will be 32-bit aligned quantities, so we can specify that on
-	 * the load/store ops and do a neon 'reinterpret' to get us to
-	 * byte-sized (pun intended) pieces that we widen/multiply/shift
-	 * we're limited at 128 bits in the wide ops, which is 8x16bits
-	 * or a pair of 32 bit src/dsts.
-	 */
-	/* we *could* manually unroll this loop so that we load 128 bits
-	 * (as a pair of 64s) from each of src and dst, processing them
-	 * in pieces. This might give us a little better management of
-	 * the memory latency, but my initial attempts here did not
-	 * produce an instruction stream that looked all that nice.
-	 */
-#define	UNROLL	2
-	while (count >= UNROLL) {
-	    uint8x8_t  src_raw, dst_raw, dst_final;
-	    uint16x8_t  src_wide, dst_wide;
-
-	    /* get 64 bits of src, widen it, multiply by src_scale */
-	    src_raw = vreinterpret_u8_u32(vld1_u32(src));
-	    src_wide = vmovl_u8(src_raw);
-	    /* gcc hoists vdupq_n_u16(), better than using vmulq_n_u16() */
-	    src_wide = vmulq_u16 (src_wide, vdupq_n_u16(src_scale));
-
-	    /* ditto with dst */
-	    dst_raw = vreinterpret_u8_u32(vld1_u32(dst));
-	    dst_wide = vmovl_u8(dst_raw);
-
-	    /* combine add with dst multiply into mul-accumulate */
-	    dst_wide = vmlaq_u16(src_wide, dst_wide, vdupq_n_u16(dst_scale));
-
-	    dst_final = vshrn_n_u16(dst_wide, 8);
-	    vst1_u32(dst, vreinterpret_u32_u8(dst_final));
-
-	    src += UNROLL;
-	    dst += UNROLL;
-	    count -= UNROLL;
-	}
-	/* RBE: well, i don't like how gcc manages src/dst across the above
-	 * loop it's constantly calculating src+bias, dst+bias and it only
-	 * adjusts the real ones when we leave the loop. Not sure why
-	 * it's "hoisting down" (hoisting implies above in my lexicon ;))
-	 * the adjustments to src/dst/count, but it does...
-	 * (might be SSA-style internal logic...
-	 */
-
-#if	UNROLL == 2
-	if (count == 1) {
-            *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
-	}
-#else
-	if (count > 0) {
-            do {
-                *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
-                src += 1;
-                dst += 1;
-            } while (--count > 0);
-	}
-#endif
-
-#undef	UNROLL
-    }
-}
-
-#define	S32_Blend_BlitRow32_PROC	S32_Blend_BlitRow32_neon
-#else
-#define	S32_Blend_BlitRow32_PROC	NULL
-#endif
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
-
-#undef	DEBUG_OPAQUE_DITHER
-
-#if	defined(DEBUG_OPAQUE_DITHER)
-static void showme8(char *str, void *p, int len)
-{
-	static char buf[256];
-	char tbuf[32];
-	int i;
-	char *pc = (char*) p;
-	sprintf(buf,"%8s:", str);
-	for(i=0;i<len;i++) {
-	    sprintf(tbuf, "   %02x", pc[i]);
-	    strcat(buf, tbuf);
-	}
-	SkDebugf("%s\n", buf);
-}
-static void showme16(char *str, void *p, int len)
-{
-	static char buf[256];
-	char tbuf[32];
-	int i;
-	uint16_t *pc = (uint16_t*) p;
-	sprintf(buf,"%8s:", str);
-	len = (len / sizeof(uint16_t));	/* passed as bytes */
-	for(i=0;i<len;i++) {
-	    sprintf(tbuf, " %04x", pc[i]);
-	    strcat(buf, tbuf);
-	}
-	SkDebugf("%s\n", buf);
-}
-#endif
-
-static void S32A_D565_Opaque_Dither_neon (uint16_t * SK_RESTRICT dst,
-                                      const SkPMColor* SK_RESTRICT src,
-                                      int count, U8CPU alpha, int x, int y) {
-    SkASSERT(255 == alpha);
-
-#define	UNROLL	8
-
-    if (count >= UNROLL) {
-	uint8x8_t dbase;
-
-#if	defined(DEBUG_OPAQUE_DITHER)
-	uint16_t tmpbuf[UNROLL];
-	int td[UNROLL];
-	int tdv[UNROLL];
-	int ta[UNROLL];
-	int tap[UNROLL];
-	uint16_t in_dst[UNROLL];
-	int offset = 0;
-	int noisy = 0;
-#endif
-
-	const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
-	dbase = vld1_u8(dstart);
-
-        do {
-	    uint8x8_t sr, sg, sb, sa, d;
-	    uint16x8_t dst8, scale8, alpha8;
-	    uint16x8_t dst_r, dst_g, dst_b;
-
-#if	defined(DEBUG_OPAQUE_DITHER)
-	/* calculate 8 elements worth into a temp buffer */
-	{
-	  int my_y = y;
-	  int my_x = x;
-	  SkPMColor* my_src = (SkPMColor*)src;
-	  uint16_t* my_dst = dst;
-	  int i;
-
-          DITHER_565_SCAN(my_y);
-          for(i=0;i<UNROLL;i++) {
-            SkPMColor c = *my_src++;
-            SkPMColorAssert(c);
-            if (c) {
-                unsigned a = SkGetPackedA32(c);
-                
-                int d = SkAlphaMul(DITHER_VALUE(my_x), SkAlpha255To256(a));
-		tdv[i] = DITHER_VALUE(my_x);
-		ta[i] = a;
-		tap[i] = SkAlpha255To256(a);
-		td[i] = d;
-                
-                unsigned sr = SkGetPackedR32(c);
-                unsigned sg = SkGetPackedG32(c);
-                unsigned sb = SkGetPackedB32(c);
-                sr = SkDITHER_R32_FOR_565(sr, d);
-                sg = SkDITHER_G32_FOR_565(sg, d);
-                sb = SkDITHER_B32_FOR_565(sb, d);
-                
-                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
-                uint32_t dst_expanded = SkExpand_rgb_16(*my_dst);
-                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
-                // now src and dst expanded are in g:11 r:10 x:1 b:10
-                tmpbuf[i] = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
-		td[i] = d;
-
-            } else {
-		tmpbuf[i] = *my_dst;
-		ta[i] = tdv[i] = td[i] = 0xbeef;
-	    }
-	    in_dst[i] = *my_dst;
-            my_dst += 1;
-            DITHER_INC_X(my_x);
-          }
-	}
-#endif
-
-	    /* source is in ABGR */
-	    {
-		register uint8x8_t d0 asm("d0");
-		register uint8x8_t d1 asm("d1");
-		register uint8x8_t d2 asm("d2");
-		register uint8x8_t d3 asm("d3");
-
-		asm ("vld4.8	{d0-d3},[%4]  /* r=%P0 g=%P1 b=%P2 a=%P3 */"
-		    : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3)
-		    : "r" (src)
-                    );
-		    sr = d0; sg = d1; sb = d2; sa = d3;
-	    }
-
-	    /* calculate 'd', which will be 0..7 */
-	    /* dbase[] is 0..7; alpha is 0..256; 16 bits suffice */
-#if defined(SK_BUILD_FOR_ANDROID)
-	    /* SkAlpha255To256() semantic a+1 vs a+a>>7 */
-	    alpha8 = vaddw_u8(vmovl_u8(sa), vdup_n_u8(1));
-#else
-	    alpha8 = vaddw_u8(vmovl_u8(sa), vshr_n_u8(sa, 7));
-#endif
-	    alpha8 = vmulq_u16(alpha8, vmovl_u8(dbase)); 
-	    d = vshrn_n_u16(alpha8, 8);	/* narrowing too */
-	    
-	    /* sr = sr - (sr>>5) + d */
-	    /* watching for 8-bit overflow.  d is 0..7; risky range of
-	     * sr is >248; and then (sr>>5) is 7 so it offsets 'd';
-	     * safe  as long as we do ((sr-sr>>5) + d) */
-	    sr = vsub_u8(sr, vshr_n_u8(sr, 5));
-	    sr = vadd_u8(sr, d);
-
-	    /* sb = sb - (sb>>5) + d */
-	    sb = vsub_u8(sb, vshr_n_u8(sb, 5));
-	    sb = vadd_u8(sb, d);
-
-	    /* sg = sg - (sg>>6) + d>>1; similar logic for overflows */
-	    sg = vsub_u8(sg, vshr_n_u8(sg, 6));
-	    sg = vadd_u8(sg, vshr_n_u8(d,1));
-
-	    /* need to pick up 8 dst's -- at 16 bits each, 128 bits */
-	    dst8 = vld1q_u16(dst);
-	    dst_b = vandq_u16(dst8, vdupq_n_u16(0x001F));
-	    dst_g = vandq_u16(vshrq_n_u16(dst8,5), vdupq_n_u16(0x003F));
-	    dst_r = vshrq_n_u16(dst8,11);	/* clearing hi bits */
-
-	    /* blend */
-#if 1
-	    /* SkAlpha255To256() semantic a+1 vs a+a>>7 */
-	    /* originally 255-sa + 1 */
-	    scale8 = vsubw_u8(vdupq_n_u16(256), sa);
-#else
-	    scale8 = vsubw_u8(vdupq_n_u16(255), sa);
-	    scale8 = vaddq_u16(scale8, vshrq_n_u16(scale8, 7));
-#endif
-
-#if 1
-	    /* combine the addq and mul, save 3 insns */
-	    scale8 = vshrq_n_u16(scale8, 3);
-	    dst_b = vmlaq_u16(vshll_n_u8(sb,2), dst_b, scale8);
-	    dst_g = vmlaq_u16(vshll_n_u8(sg,3), dst_g, scale8);
-	    dst_r = vmlaq_u16(vshll_n_u8(sr,2), dst_r, scale8);
-#else
-	    /* known correct, but +3 insns over above */
-	    scale8 = vshrq_n_u16(scale8, 3);
-	    dst_b = vmulq_u16(dst_b, scale8);
-	    dst_g = vmulq_u16(dst_g, scale8);
-	    dst_r = vmulq_u16(dst_r, scale8);
-
-	    /* combine */
-	    /* NB: vshll widens, need to preserve those bits */
-	    dst_b = vaddq_u16(dst_b, vshll_n_u8(sb,2));
-	    dst_g = vaddq_u16(dst_g, vshll_n_u8(sg,3));
-	    dst_r = vaddq_u16(dst_r, vshll_n_u8(sr,2));
-#endif
-
-	    /* repack to store */
-	    dst8 = vandq_u16(vshrq_n_u16(dst_b, 5), vdupq_n_u16(0x001F));
-	    dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dst_g, 5), 5);
-	    dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dst_r,5), 11);
-
-	    vst1q_u16(dst, dst8);
-
-#if	defined(DEBUG_OPAQUE_DITHER)
-	    /* verify my 8 elements match the temp buffer */
-	{
-	   int i, bad=0;
-	   static int invocation;
-
-	   for (i=0;i<UNROLL;i++)
-		if (tmpbuf[i] != dst[i]) bad=1;
-	   if (bad) {
-		SkDebugf("BAD S32A_D565_Opaque_Dither_neon(); invocation %d offset %d\n",
-			invocation, offset);
-		SkDebugf("  alpha 0x%x\n", alpha);
-		for (i=0;i<UNROLL;i++)
-		    SkDebugf("%2d: %s %04x w %04x id %04x s %08x d %04x %04x %04x %04x\n",
-			i, ((tmpbuf[i] != dst[i])?"BAD":"got"),
-			dst[i], tmpbuf[i], in_dst[i], src[i], td[i], tdv[i], tap[i], ta[i]);
-
-		showme16("alpha8", &alpha8, sizeof(alpha8));
-		showme16("scale8", &scale8, sizeof(scale8));
-		showme8("d", &d, sizeof(d));
-		showme16("dst8", &dst8, sizeof(dst8));
-		showme16("dst_b", &dst_b, sizeof(dst_b));
-		showme16("dst_g", &dst_g, sizeof(dst_g));
-		showme16("dst_r", &dst_r, sizeof(dst_r));
-		showme8("sb", &sb, sizeof(sb));
-		showme8("sg", &sg, sizeof(sg));
-		showme8("sr", &sr, sizeof(sr));
-
-		/* cop out */
-		return;
-	   }
-	   offset += UNROLL;
-	   invocation++;
-	}
-#endif
-
-            dst += UNROLL;
-	    src += UNROLL;
-	    count -= UNROLL;
-	    /* skip x += UNROLL, since it's unchanged mod-4 */
-        } while (count >= UNROLL);
-    }
-#undef	UNROLL
-
-    /* residuals */
-    if (count > 0) {
-        DITHER_565_SCAN(y);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            if (c) {
-                unsigned a = SkGetPackedA32(c);
-                
-                // dither and alpha are just temporary variables to work-around
-                // an ICE in debug.
-                unsigned dither = DITHER_VALUE(x);
-                unsigned alpha = SkAlpha255To256(a);
-                int d = SkAlphaMul(dither, alpha);
-                
-                unsigned sr = SkGetPackedR32(c);
-                unsigned sg = SkGetPackedG32(c);
-                unsigned sb = SkGetPackedB32(c);
-                sr = SkDITHER_R32_FOR_565(sr, d);
-                sg = SkDITHER_G32_FOR_565(sg, d);
-                sb = SkDITHER_B32_FOR_565(sb, d);
-                
-                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
-                uint32_t dst_expanded = SkExpand_rgb_16(*dst);
-                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
-                // now src and dst expanded are in g:11 r:10 x:1 b:10
-                *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
-            }
-            dst += 1;
-            DITHER_INC_X(x);
-        } while (--count != 0);
-    }
-}
-
-#define	S32A_D565_Opaque_Dither_PROC S32A_D565_Opaque_Dither_neon
-#else
-#define	S32A_D565_Opaque_Dither_PROC NULL
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-#if	defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
-/* 2009/10/27: RBE says "a work in progress"; debugging says ok;
- * speedup untested, but ARM version is 26 insns/iteration and
- * this NEON version is 21 insns/iteration-of-8 (2.62insns/element)
- * which is 10x the native version; that's pure instruction counts,
- * not accounting for any instruction or memory latencies.
- */
-
-#undef	DEBUG_S32_OPAQUE_DITHER
-
-static void S32_D565_Opaque_Dither_neon(uint16_t* SK_RESTRICT dst,
-                                     const SkPMColor* SK_RESTRICT src,
-                                     int count, U8CPU alpha, int x, int y) {
-    SkASSERT(255 == alpha);
-
-#define	UNROLL	8
-    if (count >= UNROLL) {
-	uint8x8_t d;
-	const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
-	d = vld1_u8(dstart);
-
-	while (count >= UNROLL) {
-	    uint8x8_t sr, sg, sb, sa;
-	    uint16x8_t dr, dg, db, da;
-	    uint16x8_t dst8;
-
-	    /* source is in ABGR ordering (R == lsb) */
-	    {
-		register uint8x8_t d0 asm("d0");
-		register uint8x8_t d1 asm("d1");
-		register uint8x8_t d2 asm("d2");
-		register uint8x8_t d3 asm("d3");
-
-		asm ("vld4.8	{d0-d3},[%4]  /* r=%P0 g=%P1 b=%P2 a=%P3 */"
-		    : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3)
-		    : "r" (src)
-                    );
-		    sr = d0; sg = d1; sb = d2; sa = d3;
-	    }
-	    /* XXX: if we want to prefetch, hide it in the above asm()
-	     * using the gcc __builtin_prefetch(), the prefetch will
-	     * fall to the bottom of the loop -- it won't stick up
-	     * at the top of the loop, just after the vld4.
-	     */
-
-	    /* sr = sr - (sr>>5) + d */
-	    sr = vsub_u8(sr, vshr_n_u8(sr, 5));
-	    dr = vaddl_u8(sr, d);
-
-	    /* sb = sb - (sb>>5) + d */
-	    sb = vsub_u8(sb, vshr_n_u8(sb, 5));
-	    db = vaddl_u8(sb, d);
-
-	    /* sg = sg - (sg>>6) + d>>1; similar logic for overflows */
-	    sg = vsub_u8(sg, vshr_n_u8(sg, 6));
-	    dg = vaddl_u8(sg, vshr_n_u8(d,1));
-	    /* XXX: check that the "d>>1" here is hoisted */
-
-	    /* pack high bits of each into 565 format  (rgb, b is lsb) */
-	    dst8 = vshrq_n_u16(db, 3);
-	    dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dg, 2), 5);
-	    dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dr,3), 11);
-
-	    /* store it */
-	    vst1q_u16(dst, dst8);
-
-#if	defined(DEBUG_S32_OPAQUE_DITHER)
-	    /* always good to know if we generated good results */
-	    {
-		int i, myx = x, myy = y;
-		DITHER_565_SCAN(myy);
-		for (i=0;i<UNROLL;i++) {
-		    SkPMColor c = src[i];
-		    unsigned dither = DITHER_VALUE(myx);
-		    uint16_t val = SkDitherRGB32To565(c, dither);
-		    if (val != dst[i]) {
-			SkDebugf("RBE: src %08x dither %02x, want %04x got %04x dbas[i] %02x\n",
-			    c, dither, val, dst[i], dstart[i]);
-		    }
-		    DITHER_INC_X(myx);
-		}
-	    }
-#endif
-
-	    dst += UNROLL;
-	    src += UNROLL;
-	    count -= UNROLL;
-	    x += UNROLL;		/* probably superfluous */
-	}
-    }
-#undef	UNROLL
-
-    /* residuals */
-    if (count > 0) {
-        DITHER_565_SCAN(y);
-        do {
-            SkPMColor c = *src++;
-            SkPMColorAssert(c);
-            SkASSERT(SkGetPackedA32(c) == 255);
-
-            unsigned dither = DITHER_VALUE(x);
-            *dst++ = SkDitherRGB32To565(c, dither);
-            DITHER_INC_X(x);
-        } while (--count != 0);
-    }
-}
-
-#define	S32_D565_Opaque_Dither_PROC S32_D565_Opaque_Dither_neon
-#else
-#define	S32_D565_Opaque_Dither_PROC NULL
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-static const SkBlitRow::Proc platform_565_procs[] = {
+#if USE_ARM_CODE
+const SkBlitRow::Proc sk_blitrow_platform_565_procs_arm[] = {
     // no dither
-    S32_D565_Opaque_PROC,
-    S32_D565_Blend_PROC,
-    S32A_D565_Opaque_PROC,
-    S32A_D565_Blend_PROC,
-    
+    // NOTE: For the functions below, we don't have a special version
+    //       that assumes that each source pixel is opaque. But our S32A is
+    //       still faster than the default, so use it.
+    S32A_D565_Opaque,   // S32_D565_Opaque
+    NULL,               // S32_D565_Blend
+    S32A_D565_Opaque,   // S32A_D565_Opaque
+    NULL,               // S32A_D565_Blend
+
     // dither
-    S32_D565_Opaque_Dither_PROC,
-    S32_D565_Blend_Dither_PROC,
-    S32A_D565_Opaque_Dither_PROC,
+    NULL,   // S32_D565_Opaque_Dither
+    NULL,   // S32_D565_Blend_Dither
+    NULL,   // S32A_D565_Opaque_Dither
     NULL,   // S32A_D565_Blend_Dither
 };
 
-static const SkBlitRow::Proc platform_4444_procs[] = {
+const SkBlitRow::Proc sk_blitrow_platform_4444_procs_arm[] = {
     // no dither
     NULL,   // S32_D4444_Opaque,
     NULL,   // S32_D4444_Blend,
     NULL,   // S32A_D4444_Opaque,
     NULL,   // S32A_D4444_Blend,
-    
+
     // dither
     NULL,   // S32_D4444_Opaque_Dither,
     NULL,   // S32_D4444_Blend_Dither,
@@ -1829,30 +363,31 @@
     NULL,   // S32A_D4444_Blend_Dither
 };
 
-static const SkBlitRow::Proc32 platform_32_procs[] = {
+const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm[] = {
     NULL,   // S32_Opaque,
-    S32_Blend_BlitRow32_PROC,		// S32_Blend,
-    S32A_Opaque_BlitRow32_PROC,		// S32A_Opaque,
-    S32A_Blend_BlitRow32_PROC		// S32A_Blend
+    NULL,   // S32_Blend,
+    S32A_Opaque_BlitRow32_arm,   // S32A_Opaque,
+    S32A_Blend_BlitRow32_arm     // S32A_Blend
 };
+#endif
 
 SkBlitRow::Proc SkBlitRow::PlatformProcs4444(unsigned flags) {
-    return platform_4444_procs[flags];
+    return SK_ARM_NEON_WRAP(sk_blitrow_platform_4444_procs_arm)[flags];
 }
 
 SkBlitRow::Proc SkBlitRow::PlatformProcs565(unsigned flags) {
-    return platform_565_procs[flags];
+    return SK_ARM_NEON_WRAP(sk_blitrow_platform_565_procs_arm)[flags];
 }
 
 SkBlitRow::Proc32 SkBlitRow::PlatformProcs32(unsigned flags) {
-    return platform_32_procs[flags];
-}
-
-SkBlitRow::ColorProc SkBlitRow::PlatformColorProc() {
-    return NULL;
+    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);
+}
 
 SkBlitMask::ColorProc SkBlitMask::PlatformColorProcs(SkBitmap::Config dstConfig,
                                                      SkMask::Format maskFormat,
diff --git a/src/opts/SkBlitRow_opts_arm.h b/src/opts/SkBlitRow_opts_arm.h
new file mode 100644
index 0000000..d929814
--- /dev/null
+++ b/src/opts/SkBlitRow_opts_arm.h
@@ -0,0 +1,41 @@
+/*
+ * 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 SkBlitRow_opts_arm_DEFINED
+#define SkBlitRow_opts_arm_DEFINED
+
+#include "SkBlitRow.h"
+#include "SkUtilsArm.h"
+
+// Define USE_NEON_CODE to indicate that we need to build NEON routines
+#define USE_NEON_CODE  (!SK_ARM_NEON_IS_NONE)
+
+// Define USE_ARM_CODE to indicate that we need to build ARM routines
+#define USE_ARM_CODE   (!SK_ARM_NEON_IS_ALWAYS)
+
+#if USE_NEON_CODE
+// These are defined in SkBlitRow_opts_arm_neon.cpp
+extern const SkBlitRow::Proc sk_blitrow_platform_565_procs_arm_neon[];
+extern const SkBlitRow::Proc sk_blitrow_platform_4444_procs_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
+
+#if USE_ARM_CODE
+// These are defined in SkBlitRow_opts_arm.cpp
+extern const SkBlitRow::Proc sk_blitrow_platform_565_procs_arm[];
+extern const SkBlitRow::Proc sk_blitrow_platform_4444_procs_arm[];
+extern const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm[];
+#endif
+
+// Defined in SkBlitRow_opts_arm.cpp, used in all cases.
+extern void S32A_Blend_BlitRow32_arm(SkPMColor* SK_RESTRICT dst,
+                                     const SkPMColor* SK_RESTRICT src,
+                                     int count, U8CPU alpha);
+
+#endif
diff --git a/src/opts/SkBlitRow_opts_arm_neon.cpp b/src/opts/SkBlitRow_opts_arm_neon.cpp
new file mode 100644
index 0000000..14d5968
--- /dev/null
+++ b/src/opts/SkBlitRow_opts_arm_neon.cpp
@@ -0,0 +1,1112 @@
+/*
+ * Copyright 2012 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 "SkBlitRow_opts_arm.h"
+
+#include "SkBlitMask.h"
+#include "SkBlitRow.h"
+#include "SkColorPriv.h"
+#include "SkDither.h"
+#include "SkMathPriv.h"
+#include "SkUtils.h"
+
+#include "SkCachePreload_arm.h"
+
+#include <arm_neon.h>
+
+void S32A_D565_Opaque_neon(uint16_t* SK_RESTRICT dst,
+                           const SkPMColor* SK_RESTRICT src, int count,
+                           U8CPU alpha, int /*x*/, int /*y*/) {
+    SkASSERT(255 == alpha);
+
+    if (count >= 8) {
+        uint16_t* SK_RESTRICT keep_dst;
+
+        asm volatile (
+                      "ands       ip, %[count], #7            \n\t"
+                      "vmov.u8    d31, #1<<7                  \n\t"
+                      "vld1.16    {q12}, [%[dst]]             \n\t"
+                      "vld4.8     {d0-d3}, [%[src]]           \n\t"
+                      // Thumb does not support the standard ARM conditional
+                      // instructions but instead requires the 'it' instruction
+                      // to signal conditional execution
+                      "it eq                                  \n\t"
+                      "moveq      ip, #8                      \n\t"
+                      "mov        %[keep_dst], %[dst]         \n\t"
+
+                      "add        %[src], %[src], ip, LSL#2   \n\t"
+                      "add        %[dst], %[dst], ip, LSL#1   \n\t"
+                      "subs       %[count], %[count], ip      \n\t"
+                      "b          9f                          \n\t"
+                      // LOOP
+                      "2:                                         \n\t"
+
+                      "vld1.16    {q12}, [%[dst]]!            \n\t"
+                      "vld4.8     {d0-d3}, [%[src]]!          \n\t"
+                      "vst1.16    {q10}, [%[keep_dst]]        \n\t"
+                      "sub        %[keep_dst], %[dst], #8*2   \n\t"
+                      "subs       %[count], %[count], #8      \n\t"
+                      "9:                                         \n\t"
+                      "pld        [%[dst],#32]                \n\t"
+                      // expand 0565 q12 to 8888 {d4-d7}
+                      "vmovn.u16  d4, q12                     \n\t"
+                      "vshr.u16   q11, q12, #5                \n\t"
+                      "vshr.u16   q10, q12, #6+5              \n\t"
+                      "vmovn.u16  d5, q11                     \n\t"
+                      "vmovn.u16  d6, q10                     \n\t"
+                      "vshl.u8    d4, d4, #3                  \n\t"
+                      "vshl.u8    d5, d5, #2                  \n\t"
+                      "vshl.u8    d6, d6, #3                  \n\t"
+
+                      "vmovl.u8   q14, d31                    \n\t"
+                      "vmovl.u8   q13, d31                    \n\t"
+                      "vmovl.u8   q12, d31                    \n\t"
+
+                      // duplicate in 4/2/1 & 8pix vsns
+                      "vmvn.8     d30, d3                     \n\t"
+                      "vmlal.u8   q14, d30, d6                \n\t"
+                      "vmlal.u8   q13, d30, d5                \n\t"
+                      "vmlal.u8   q12, d30, d4                \n\t"
+                      "vshr.u16   q8, q14, #5                 \n\t"
+                      "vshr.u16   q9, q13, #6                 \n\t"
+                      "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
+
+                      "vqadd.u8   d5, d5, d1                  \n\t"
+                      "vqadd.u8   d4, d4, d2                  \n\t"
+
+                      // pack 8888 {d4-d6} to 0565 q10
+                      "vshll.u8   q10, d6, #8                 \n\t"
+                      "vshll.u8   q3, d5, #8                  \n\t"
+                      "vshll.u8   q2, d4, #8                  \n\t"
+                      "vsri.u16   q10, q3, #5                 \n\t"
+                      "vsri.u16   q10, q2, #11                \n\t"
+
+                      "bne        2b                          \n\t"
+
+                      "1:                                         \n\t"
+                      "vst1.16      {q10}, [%[keep_dst]]      \n\t"
+                      : [count] "+r" (count)
+                      : [dst] "r" (dst), [keep_dst] "r" (keep_dst), [src] "r" (src)
+                      : "ip", "cc", "memory", "d0","d1","d2","d3","d4","d5","d6","d7",
+                      "d16","d17","d18","d19","d20","d21","d22","d23","d24","d25","d26","d27","d28","d29",
+                      "d30","d31"
+                      );
+    }
+    else
+    {   // handle count < 8
+        uint16_t* SK_RESTRICT keep_dst;
+
+        asm volatile (
+                      "vmov.u8    d31, #1<<7                  \n\t"
+                      "mov        %[keep_dst], %[dst]         \n\t"
+
+                      "tst        %[count], #4                \n\t"
+                      "beq        14f                         \n\t"
+                      "vld1.16    {d25}, [%[dst]]!            \n\t"
+                      "vld1.32    {q1}, [%[src]]!             \n\t"
+
+                      "14:                                        \n\t"
+                      "tst        %[count], #2                \n\t"
+                      "beq        12f                         \n\t"
+                      "vld1.32    {d24[1]}, [%[dst]]!         \n\t"
+                      "vld1.32    {d1}, [%[src]]!             \n\t"
+
+                      "12:                                        \n\t"
+                      "tst        %[count], #1                \n\t"
+                      "beq        11f                         \n\t"
+                      "vld1.16    {d24[1]}, [%[dst]]!         \n\t"
+                      "vld1.32    {d0[1]}, [%[src]]!          \n\t"
+
+                      "11:                                        \n\t"
+                      // unzips achieve the same as a vld4 operation
+                      "vuzpq.u16  q0, q1                      \n\t"
+                      "vuzp.u8    d0, d1                      \n\t"
+                      "vuzp.u8    d2, d3                      \n\t"
+                      // expand 0565 q12 to 8888 {d4-d7}
+                      "vmovn.u16  d4, q12                     \n\t"
+                      "vshr.u16   q11, q12, #5                \n\t"
+                      "vshr.u16   q10, q12, #6+5              \n\t"
+                      "vmovn.u16  d5, q11                     \n\t"
+                      "vmovn.u16  d6, q10                     \n\t"
+                      "vshl.u8    d4, d4, #3                  \n\t"
+                      "vshl.u8    d5, d5, #2                  \n\t"
+                      "vshl.u8    d6, d6, #3                  \n\t"
+
+                      "vmovl.u8   q14, d31                    \n\t"
+                      "vmovl.u8   q13, d31                    \n\t"
+                      "vmovl.u8   q12, d31                    \n\t"
+
+                      // duplicate in 4/2/1 & 8pix vsns
+                      "vmvn.8     d30, d3                     \n\t"
+                      "vmlal.u8   q14, d30, d6                \n\t"
+                      "vmlal.u8   q13, d30, d5                \n\t"
+                      "vmlal.u8   q12, d30, d4                \n\t"
+                      "vshr.u16   q8, q14, #5                 \n\t"
+                      "vshr.u16   q9, q13, #6                 \n\t"
+                      "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
+
+                      "vqadd.u8   d5, d5, d1                  \n\t"
+                      "vqadd.u8   d4, d4, d2                  \n\t"
+
+                      // pack 8888 {d4-d6} to 0565 q10
+                      "vshll.u8   q10, d6, #8                 \n\t"
+                      "vshll.u8   q3, d5, #8                  \n\t"
+                      "vshll.u8   q2, d4, #8                  \n\t"
+                      "vsri.u16   q10, q3, #5                 \n\t"
+                      "vsri.u16   q10, q2, #11                \n\t"
+
+                      // store
+                      "tst        %[count], #4                \n\t"
+                      "beq        24f                         \n\t"
+                      "vst1.16    {d21}, [%[keep_dst]]!       \n\t"
+
+                      "24:                                        \n\t"
+                      "tst        %[count], #2                \n\t"
+                      "beq        22f                         \n\t"
+                      "vst1.32    {d20[1]}, [%[keep_dst]]!    \n\t"
+
+                      "22:                                        \n\t"
+                      "tst        %[count], #1                \n\t"
+                      "beq        21f                         \n\t"
+                      "vst1.16    {d20[1]}, [%[keep_dst]]!    \n\t"
+
+                      "21:                                        \n\t"
+                      : [count] "+r" (count)
+                      : [dst] "r" (dst), [keep_dst] "r" (keep_dst), [src] "r" (src)
+                      : "ip", "cc", "memory", "d0","d1","d2","d3","d4","d5","d6","d7",
+                      "d16","d17","d18","d19","d20","d21","d22","d23","d24","d25","d26","d27","d28","d29",
+                      "d30","d31"
+                      );
+    }
+}
+
+void S32A_D565_Blend_neon(uint16_t* SK_RESTRICT dst,
+                          const SkPMColor* SK_RESTRICT src, int count,
+                          U8CPU alpha, int /*x*/, int /*y*/) {
+
+    U8CPU alpha_for_asm = alpha;
+
+    asm volatile (
+    /* This code implements a Neon version of S32A_D565_Blend. The output differs from
+     * the original in two respects:
+     *  1. The results have a few mismatches compared to the original code. These mismatches
+     *     never exceed 1. It's possible to improve accuracy vs. a floating point
+     *     implementation by introducing rounding right shifts (vrshr) for the final stage.
+     *     Rounding is not present in the code below, because although results would be closer
+     *     to a floating point implementation, the number of mismatches compared to the
+     *     original code would be far greater.
+     *  2. On certain inputs, the original code can overflow, causing colour channels to
+     *     mix. Although the Neon code can also overflow, it doesn't allow one colour channel
+     *     to affect another.
+     */
+
+#if 1
+        /* reflects SkAlpha255To256()'s change from a+a>>7 to a+1 */
+                  "add        %[alpha], %[alpha], #1         \n\t"   // adjust range of alpha 0-256
+#else
+                  "add        %[alpha], %[alpha], %[alpha], lsr #7    \n\t"   // adjust range of alpha 0-256
+#endif
+                  "vmov.u16   q3, #255                        \n\t"   // set up constant
+                  "movs       r4, %[count], lsr #3            \n\t"   // calc. count>>3
+                  "vmov.u16   d2[0], %[alpha]                 \n\t"   // move alpha to Neon
+                  "beq        2f                              \n\t"   // if count8 == 0, exit
+                  "vmov.u16   q15, #0x1f                      \n\t"   // set up blue mask
+
+                  "1:                                             \n\t"
+                  "vld1.u16   {d0, d1}, [%[dst]]              \n\t"   // load eight dst RGB565 pixels
+                  "subs       r4, r4, #1                      \n\t"   // decrement loop counter
+                  "vld4.u8    {d24, d25, d26, d27}, [%[src]]! \n\t"   // load eight src ABGR32 pixels
+                  //  and deinterleave
+
+                  "vshl.u16   q9, q0, #5                      \n\t"   // shift green to top of lanes
+                  "vand       q10, q0, q15                    \n\t"   // extract blue
+                  "vshr.u16   q8, q0, #11                     \n\t"   // extract red
+                  "vshr.u16   q9, q9, #10                     \n\t"   // extract green
+                  // dstrgb = {q8, q9, q10}
+
+                  "vshr.u8    d24, d24, #3                    \n\t"   // shift red to 565 range
+                  "vshr.u8    d25, d25, #2                    \n\t"   // shift green to 565 range
+                  "vshr.u8    d26, d26, #3                    \n\t"   // shift blue to 565 range
+
+                  "vmovl.u8   q11, d24                        \n\t"   // widen red to 16 bits
+                  "vmovl.u8   q12, d25                        \n\t"   // widen green to 16 bits
+                  "vmovl.u8   q14, d27                        \n\t"   // widen alpha to 16 bits
+                  "vmovl.u8   q13, d26                        \n\t"   // widen blue to 16 bits
+                  // srcrgba = {q11, q12, q13, q14}
+
+                  "vmul.u16   q2, q14, d2[0]                  \n\t"   // sa * src_scale
+                  "vmul.u16   q11, q11, d2[0]                 \n\t"   // red result = src_red * src_scale
+                  "vmul.u16   q12, q12, d2[0]                 \n\t"   // grn result = src_grn * src_scale
+                  "vmul.u16   q13, q13, d2[0]                 \n\t"   // blu result = src_blu * src_scale
+
+                  "vshr.u16   q2, q2, #8                      \n\t"   // sa * src_scale >> 8
+                  "vsub.u16   q2, q3, q2                      \n\t"   // 255 - (sa * src_scale >> 8)
+                  // dst_scale = q2
+
+                  "vmla.u16   q11, q8, q2                     \n\t"   // red result += dst_red * dst_scale
+                  "vmla.u16   q12, q9, q2                     \n\t"   // grn result += dst_grn * dst_scale
+                  "vmla.u16   q13, q10, q2                    \n\t"   // blu result += dst_blu * dst_scale
+
+#if 1
+    // trying for a better match with SkDiv255Round(a)
+    // C alg is:  a+=128; (a+a>>8)>>8
+    // we'll use just a rounding shift [q2 is available for scratch]
+                  "vrshr.u16   q11, q11, #8                    \n\t"   // shift down red
+                  "vrshr.u16   q12, q12, #8                    \n\t"   // shift down green
+                  "vrshr.u16   q13, q13, #8                    \n\t"   // shift down blue
+#else
+    // arm's original "truncating divide by 256"
+                  "vshr.u16   q11, q11, #8                    \n\t"   // shift down red
+                  "vshr.u16   q12, q12, #8                    \n\t"   // shift down green
+                  "vshr.u16   q13, q13, #8                    \n\t"   // shift down blue
+#endif
+
+                  "vsli.u16   q13, q12, #5                    \n\t"   // insert green into blue
+                  "vsli.u16   q13, q11, #11                   \n\t"   // insert red into green/blue
+                  "vst1.16    {d26, d27}, [%[dst]]!           \n\t"   // write pixel back to dst, update ptr
+
+                  "bne        1b                              \n\t"   // if counter != 0, loop
+                  "2:                                             \n\t"   // exit
+
+                  : [src] "+r" (src), [dst] "+r" (dst), [count] "+r" (count), [alpha] "+r" (alpha_for_asm)
+                  :
+                  : "cc", "memory", "r4", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31"
+                  );
+
+    count &= 7;
+    if (count > 0) {
+        do {
+            SkPMColor sc = *src++;
+            if (sc) {
+                uint16_t dc = *dst;
+                unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
+                unsigned dr = SkMulS16(SkPacked32ToR16(sc), alpha) + SkMulS16(SkGetPackedR16(dc), dst_scale);
+                unsigned dg = SkMulS16(SkPacked32ToG16(sc), alpha) + SkMulS16(SkGetPackedG16(dc), dst_scale);
+                unsigned db = SkMulS16(SkPacked32ToB16(sc), alpha) + SkMulS16(SkGetPackedB16(dc), dst_scale);
+                *dst = SkPackRGB16(SkDiv255Round(dr), SkDiv255Round(dg), SkDiv255Round(db));
+            }
+            dst += 1;
+        } while (--count != 0);
+    }
+}
+
+/* dither matrix for Neon, derived from gDitherMatrix_3Bit_16.
+ * each dither value is spaced out into byte lanes, and repeated
+ * to allow an 8-byte load from offsets 0, 1, 2 or 3 from the
+ * start of each row.
+ */
+static const uint8_t gDitherMatrix_Neon[48] = {
+    0, 4, 1, 5, 0, 4, 1, 5, 0, 4, 1, 5,
+    6, 2, 7, 3, 6, 2, 7, 3, 6, 2, 7, 3,
+    1, 5, 0, 4, 1, 5, 0, 4, 1, 5, 0, 4,
+    7, 3, 6, 2, 7, 3, 6, 2, 7, 3, 6, 2,
+
+};
+
+void S32_D565_Blend_Dither_neon(uint16_t *dst, const SkPMColor *src,
+                                int count, U8CPU alpha, int x, int y)
+{
+    /* select row and offset for dither array */
+    const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
+
+    /* rescale alpha to range 0 - 256 */
+    int scale = SkAlpha255To256(alpha);
+
+    asm volatile (
+                  "vld1.8         {d31}, [%[dstart]]              \n\t"   // load dither values
+                  "vshr.u8        d30, d31, #1                    \n\t"   // calc. green dither values
+                  "vdup.16        d6, %[scale]                    \n\t"   // duplicate scale into neon reg
+                  "vmov.i8        d29, #0x3f                      \n\t"   // set up green mask
+                  "vmov.i8        d28, #0x1f                      \n\t"   // set up blue mask
+                  "1:                                                 \n\t"
+                  "vld4.8         {d0, d1, d2, d3}, [%[src]]!     \n\t"   // load 8 pixels and split into argb
+                  "vshr.u8        d22, d0, #5                     \n\t"   // calc. red >> 5
+                  "vshr.u8        d23, d1, #6                     \n\t"   // calc. green >> 6
+                  "vshr.u8        d24, d2, #5                     \n\t"   // calc. blue >> 5
+                  "vaddl.u8       q8, d0, d31                     \n\t"   // add in dither to red and widen
+                  "vaddl.u8       q9, d1, d30                     \n\t"   // add in dither to green and widen
+                  "vaddl.u8       q10, d2, d31                    \n\t"   // add in dither to blue and widen
+                  "vsubw.u8       q8, q8, d22                     \n\t"   // sub shifted red from result
+                  "vsubw.u8       q9, q9, d23                     \n\t"   // sub shifted green from result
+                  "vsubw.u8       q10, q10, d24                   \n\t"   // sub shifted blue from result
+                  "vshrn.i16      d22, q8, #3                     \n\t"   // shift right and narrow to 5 bits
+                  "vshrn.i16      d23, q9, #2                     \n\t"   // shift right and narrow to 6 bits
+                  "vshrn.i16      d24, q10, #3                    \n\t"   // shift right and narrow to 5 bits
+                  // load 8 pixels from dst, extract rgb
+                  "vld1.16        {d0, d1}, [%[dst]]              \n\t"   // load 8 pixels
+                  "vshrn.i16      d17, q0, #5                     \n\t"   // shift green down to bottom 6 bits
+                  "vmovn.i16      d18, q0                         \n\t"   // narrow to get blue as bytes
+                  "vshr.u16       q0, q0, #11                     \n\t"   // shift down to extract red
+                  "vand           d17, d17, d29                   \n\t"   // and green with green mask
+                  "vand           d18, d18, d28                   \n\t"   // and blue with blue mask
+                  "vmovn.i16      d16, q0                         \n\t"   // narrow to get red as bytes
+                  // src = {d22 (r), d23 (g), d24 (b)}
+                  // dst = {d16 (r), d17 (g), d18 (b)}
+                  // subtract dst from src and widen
+                  "vsubl.s8       q0, d22, d16                    \n\t"   // subtract red src from dst
+                  "vsubl.s8       q1, d23, d17                    \n\t"   // subtract green src from dst
+                  "vsubl.s8       q2, d24, d18                    \n\t"   // subtract blue src from dst
+                  // multiply diffs by scale and shift
+                  "vmul.i16       q0, q0, d6[0]                   \n\t"   // multiply red by scale
+                  "vmul.i16       q1, q1, d6[0]                   \n\t"   // multiply blue by scale
+                  "vmul.i16       q2, q2, d6[0]                   \n\t"   // multiply green by scale
+                  "subs           %[count], %[count], #8          \n\t"   // decrement loop counter
+                  "vshrn.i16      d0, q0, #8                      \n\t"   // shift down red by 8 and narrow
+                  "vshrn.i16      d2, q1, #8                      \n\t"   // shift down green by 8 and narrow
+                  "vshrn.i16      d4, q2, #8                      \n\t"   // shift down blue by 8 and narrow
+                  // add dst to result
+                  "vaddl.s8       q0, d0, d16                     \n\t"   // add dst to red
+                  "vaddl.s8       q1, d2, d17                     \n\t"   // add dst to green
+                  "vaddl.s8       q2, d4, d18                     \n\t"   // add dst to blue
+                  // put result into 565 format
+                  "vsli.i16       q2, q1, #5                      \n\t"   // shift up green and insert into blue
+                  "vsli.i16       q2, q0, #11                     \n\t"   // shift up red and insert into blue
+                  "vst1.16        {d4, d5}, [%[dst]]!             \n\t"   // store result
+                  "bgt            1b                              \n\t"   // loop if count > 0
+                  : [src] "+r" (src), [dst] "+r" (dst), [count] "+r" (count)
+                  : [dstart] "r" (dstart), [scale] "r" (scale)
+                  : "cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23", "d24", "d28", "d29", "d30", "d31"
+                  );
+
+    DITHER_565_SCAN(y);
+
+    while((count & 7) > 0)
+    {
+        SkPMColor c = *src++;
+
+        int dither = DITHER_VALUE(x);
+        int sr = SkGetPackedR32(c);
+        int sg = SkGetPackedG32(c);
+        int sb = SkGetPackedB32(c);
+        sr = SkDITHER_R32To565(sr, dither);
+        sg = SkDITHER_G32To565(sg, dither);
+        sb = SkDITHER_B32To565(sb, dither);
+
+        uint16_t d = *dst;
+        *dst++ = SkPackRGB16(SkAlphaBlend(sr, SkGetPackedR16(d), scale),
+                             SkAlphaBlend(sg, SkGetPackedG16(d), scale),
+                             SkAlphaBlend(sb, SkGetPackedB16(d), scale));
+        DITHER_INC_X(x);
+        count--;
+    }
+}
+
+void S32A_Opaque_BlitRow32_neon(SkPMColor* SK_RESTRICT dst,
+                                const SkPMColor* SK_RESTRICT src,
+                                int count, U8CPU alpha) {
+
+    SkASSERT(255 == alpha);
+    if (count > 0) {
+
+
+    uint8x8_t alpha_mask;
+
+    static const uint8_t alpha_mask_setup[] = {3,3,3,3,7,7,7,7};
+    alpha_mask = vld1_u8(alpha_mask_setup);
+
+    /* do the NEON unrolled code */
+#define    UNROLL    4
+    while (count >= UNROLL) {
+        uint8x8_t src_raw, dst_raw, dst_final;
+        uint8x8_t src_raw_2, dst_raw_2, dst_final_2;
+
+        /* get the source */
+        src_raw = vreinterpret_u8_u32(vld1_u32(src));
+#if    UNROLL > 2
+        src_raw_2 = vreinterpret_u8_u32(vld1_u32(src+2));
+#endif
+
+        /* get and hold the dst too */
+        dst_raw = vreinterpret_u8_u32(vld1_u32(dst));
+#if    UNROLL > 2
+        dst_raw_2 = vreinterpret_u8_u32(vld1_u32(dst+2));
+#endif
+
+    /* 1st and 2nd bits of the unrolling */
+    {
+        uint8x8_t dst_cooked;
+        uint16x8_t dst_wide;
+        uint8x8_t alpha_narrow;
+        uint16x8_t alpha_wide;
+
+        /* get the alphas spread out properly */
+        alpha_narrow = vtbl1_u8(src_raw, alpha_mask);
+#if 1
+        /* reflect SkAlpha255To256() semantics a+1 vs a+a>>7 */
+        /* we collapsed (255-a)+1 ... */
+        alpha_wide = vsubw_u8(vdupq_n_u16(256), alpha_narrow);
+#else
+        alpha_wide = vsubw_u8(vdupq_n_u16(255), alpha_narrow);
+        alpha_wide = vaddq_u16(alpha_wide, vshrq_n_u16(alpha_wide,7));
+#endif
+
+        /* spread the dest */
+        dst_wide = vmovl_u8(dst_raw);
+
+        /* alpha mul the dest */
+        dst_wide = vmulq_u16 (dst_wide, alpha_wide);
+        dst_cooked = vshrn_n_u16(dst_wide, 8);
+
+        /* sum -- ignoring any byte lane overflows */
+        dst_final = vadd_u8(src_raw, dst_cooked);
+    }
+
+#if    UNROLL > 2
+    /* the 3rd and 4th bits of our unrolling */
+    {
+        uint8x8_t dst_cooked;
+        uint16x8_t dst_wide;
+        uint8x8_t alpha_narrow;
+        uint16x8_t alpha_wide;
+
+        alpha_narrow = vtbl1_u8(src_raw_2, alpha_mask);
+#if 1
+        /* reflect SkAlpha255To256() semantics a+1 vs a+a>>7 */
+        /* we collapsed (255-a)+1 ... */
+        alpha_wide = vsubw_u8(vdupq_n_u16(256), alpha_narrow);
+#else
+        alpha_wide = vsubw_u8(vdupq_n_u16(255), alpha_narrow);
+        alpha_wide = vaddq_u16(alpha_wide, vshrq_n_u16(alpha_wide,7));
+#endif
+
+        /* spread the dest */
+        dst_wide = vmovl_u8(dst_raw_2);
+
+        /* alpha mul the dest */
+        dst_wide = vmulq_u16 (dst_wide, alpha_wide);
+        dst_cooked = vshrn_n_u16(dst_wide, 8);
+
+        /* sum -- ignoring any byte lane overflows */
+        dst_final_2 = vadd_u8(src_raw_2, dst_cooked);
+    }
+#endif
+
+        vst1_u32(dst, vreinterpret_u32_u8(dst_final));
+#if    UNROLL > 2
+        vst1_u32(dst+2, vreinterpret_u32_u8(dst_final_2));
+#endif
+
+        src += UNROLL;
+        dst += UNROLL;
+        count -= UNROLL;
+    }
+#undef    UNROLL
+
+    /* do any residual iterations */
+        while (--count >= 0) {
+            *dst = SkPMSrcOver(*src, *dst);
+            src += 1;
+            dst += 1;
+        }
+    }
+}
+
+
+/* Neon version of S32_Blend_BlitRow32()
+ * portable version is in src/core/SkBlitRow_D32.cpp
+ */
+void S32_Blend_BlitRow32_neon(SkPMColor* SK_RESTRICT dst,
+                              const SkPMColor* SK_RESTRICT src,
+                              int count, U8CPU alpha) {
+    SkASSERT(alpha <= 255);
+    if (count > 0) {
+        uint16_t src_scale = SkAlpha255To256(alpha);
+        uint16_t dst_scale = 256 - src_scale;
+
+    /* run them N at a time through the NEON unit */
+    /* note that each 1 is 4 bytes, each treated exactly the same,
+     * so we can work under that guise. We *do* know that the src&dst
+     * will be 32-bit aligned quantities, so we can specify that on
+     * the load/store ops and do a neon 'reinterpret' to get us to
+     * byte-sized (pun intended) pieces that we widen/multiply/shift
+     * we're limited at 128 bits in the wide ops, which is 8x16bits
+     * or a pair of 32 bit src/dsts.
+     */
+    /* we *could* manually unroll this loop so that we load 128 bits
+     * (as a pair of 64s) from each of src and dst, processing them
+     * in pieces. This might give us a little better management of
+     * the memory latency, but my initial attempts here did not
+     * produce an instruction stream that looked all that nice.
+     */
+#define    UNROLL    2
+    while (count >= UNROLL) {
+        uint8x8_t  src_raw, dst_raw, dst_final;
+        uint16x8_t  src_wide, dst_wide;
+
+        /* get 64 bits of src, widen it, multiply by src_scale */
+        src_raw = vreinterpret_u8_u32(vld1_u32(src));
+        src_wide = vmovl_u8(src_raw);
+        /* gcc hoists vdupq_n_u16(), better than using vmulq_n_u16() */
+        src_wide = vmulq_u16 (src_wide, vdupq_n_u16(src_scale));
+
+        /* ditto with dst */
+        dst_raw = vreinterpret_u8_u32(vld1_u32(dst));
+        dst_wide = vmovl_u8(dst_raw);
+
+        /* combine add with dst multiply into mul-accumulate */
+        dst_wide = vmlaq_u16(src_wide, dst_wide, vdupq_n_u16(dst_scale));
+
+        dst_final = vshrn_n_u16(dst_wide, 8);
+        vst1_u32(dst, vreinterpret_u32_u8(dst_final));
+
+        src += UNROLL;
+        dst += UNROLL;
+        count -= UNROLL;
+    }
+    /* RBE: well, i don't like how gcc manages src/dst across the above
+     * loop it's constantly calculating src+bias, dst+bias and it only
+     * adjusts the real ones when we leave the loop. Not sure why
+     * it's "hoisting down" (hoisting implies above in my lexicon ;))
+     * the adjustments to src/dst/count, but it does...
+     * (might be SSA-style internal logic...
+     */
+
+#if    UNROLL == 2
+    if (count == 1) {
+            *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
+    }
+#else
+    if (count > 0) {
+            do {
+                *dst = SkAlphaMulQ(*src, src_scale) + SkAlphaMulQ(*dst, dst_scale);
+                src += 1;
+                dst += 1;
+            } while (--count > 0);
+    }
+#endif
+
+#undef    UNROLL
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#undef    DEBUG_OPAQUE_DITHER
+
+#if    defined(DEBUG_OPAQUE_DITHER)
+static void showme8(char *str, void *p, int len)
+{
+    static char buf[256];
+    char tbuf[32];
+    int i;
+    char *pc = (char*) p;
+    sprintf(buf,"%8s:", str);
+    for(i=0;i<len;i++) {
+        sprintf(tbuf, "   %02x", pc[i]);
+        strcat(buf, tbuf);
+    }
+    SkDebugf("%s\n", buf);
+}
+static void showme16(char *str, void *p, int len)
+{
+    static char buf[256];
+    char tbuf[32];
+    int i;
+    uint16_t *pc = (uint16_t*) p;
+    sprintf(buf,"%8s:", str);
+    len = (len / sizeof(uint16_t));    /* passed as bytes */
+    for(i=0;i<len;i++) {
+        sprintf(tbuf, " %04x", pc[i]);
+        strcat(buf, tbuf);
+    }
+    SkDebugf("%s\n", buf);
+}
+#endif
+
+void S32A_D565_Opaque_Dither_neon (uint16_t * SK_RESTRICT dst,
+                                   const SkPMColor* SK_RESTRICT src,
+                                   int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 == alpha);
+
+#define    UNROLL    8
+
+    if (count >= UNROLL) {
+    uint8x8_t dbase;
+
+#if    defined(DEBUG_OPAQUE_DITHER)
+    uint16_t tmpbuf[UNROLL];
+    int td[UNROLL];
+    int tdv[UNROLL];
+    int ta[UNROLL];
+    int tap[UNROLL];
+    uint16_t in_dst[UNROLL];
+    int offset = 0;
+    int noisy = 0;
+#endif
+
+    const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
+    dbase = vld1_u8(dstart);
+
+        do {
+        uint8x8_t sr, sg, sb, sa, d;
+        uint16x8_t dst8, scale8, alpha8;
+        uint16x8_t dst_r, dst_g, dst_b;
+
+#if    defined(DEBUG_OPAQUE_DITHER)
+    /* calculate 8 elements worth into a temp buffer */
+    {
+      int my_y = y;
+      int my_x = x;
+      SkPMColor* my_src = (SkPMColor*)src;
+      uint16_t* my_dst = dst;
+      int i;
+
+          DITHER_565_SCAN(my_y);
+          for(i=0;i<UNROLL;i++) {
+            SkPMColor c = *my_src++;
+            SkPMColorAssert(c);
+            if (c) {
+                unsigned a = SkGetPackedA32(c);
+
+                int d = SkAlphaMul(DITHER_VALUE(my_x), SkAlpha255To256(a));
+        tdv[i] = DITHER_VALUE(my_x);
+        ta[i] = a;
+        tap[i] = SkAlpha255To256(a);
+        td[i] = d;
+
+                unsigned sr = SkGetPackedR32(c);
+                unsigned sg = SkGetPackedG32(c);
+                unsigned sb = SkGetPackedB32(c);
+                sr = SkDITHER_R32_FOR_565(sr, d);
+                sg = SkDITHER_G32_FOR_565(sg, d);
+                sb = SkDITHER_B32_FOR_565(sb, d);
+
+                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
+                uint32_t dst_expanded = SkExpand_rgb_16(*my_dst);
+                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
+                // now src and dst expanded are in g:11 r:10 x:1 b:10
+                tmpbuf[i] = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
+        td[i] = d;
+
+            } else {
+        tmpbuf[i] = *my_dst;
+        ta[i] = tdv[i] = td[i] = 0xbeef;
+        }
+        in_dst[i] = *my_dst;
+            my_dst += 1;
+            DITHER_INC_X(my_x);
+          }
+    }
+#endif
+
+        /* source is in ABGR */
+        {
+        register uint8x8_t d0 asm("d0");
+        register uint8x8_t d1 asm("d1");
+        register uint8x8_t d2 asm("d2");
+        register uint8x8_t d3 asm("d3");
+
+        asm ("vld4.8    {d0-d3},[%4]  /* r=%P0 g=%P1 b=%P2 a=%P3 */"
+            : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3)
+            : "r" (src)
+                    );
+            sr = d0; sg = d1; sb = d2; sa = d3;
+        }
+
+        /* calculate 'd', which will be 0..7 */
+        /* dbase[] is 0..7; alpha is 0..256; 16 bits suffice */
+#if defined(SK_BUILD_FOR_ANDROID)
+        /* SkAlpha255To256() semantic a+1 vs a+a>>7 */
+        alpha8 = vaddw_u8(vmovl_u8(sa), vdup_n_u8(1));
+#else
+        alpha8 = vaddw_u8(vmovl_u8(sa), vshr_n_u8(sa, 7));
+#endif
+        alpha8 = vmulq_u16(alpha8, vmovl_u8(dbase));
+        d = vshrn_n_u16(alpha8, 8);    /* narrowing too */
+
+        /* sr = sr - (sr>>5) + d */
+        /* watching for 8-bit overflow.  d is 0..7; risky range of
+         * sr is >248; and then (sr>>5) is 7 so it offsets 'd';
+         * safe  as long as we do ((sr-sr>>5) + d) */
+        sr = vsub_u8(sr, vshr_n_u8(sr, 5));
+        sr = vadd_u8(sr, d);
+
+        /* sb = sb - (sb>>5) + d */
+        sb = vsub_u8(sb, vshr_n_u8(sb, 5));
+        sb = vadd_u8(sb, d);
+
+        /* sg = sg - (sg>>6) + d>>1; similar logic for overflows */
+        sg = vsub_u8(sg, vshr_n_u8(sg, 6));
+        sg = vadd_u8(sg, vshr_n_u8(d,1));
+
+        /* need to pick up 8 dst's -- at 16 bits each, 128 bits */
+        dst8 = vld1q_u16(dst);
+        dst_b = vandq_u16(dst8, vdupq_n_u16(0x001F));
+        dst_g = vandq_u16(vshrq_n_u16(dst8,5), vdupq_n_u16(0x003F));
+        dst_r = vshrq_n_u16(dst8,11);    /* clearing hi bits */
+
+        /* blend */
+#if 1
+        /* SkAlpha255To256() semantic a+1 vs a+a>>7 */
+        /* originally 255-sa + 1 */
+        scale8 = vsubw_u8(vdupq_n_u16(256), sa);
+#else
+        scale8 = vsubw_u8(vdupq_n_u16(255), sa);
+        scale8 = vaddq_u16(scale8, vshrq_n_u16(scale8, 7));
+#endif
+
+#if 1
+        /* combine the addq and mul, save 3 insns */
+        scale8 = vshrq_n_u16(scale8, 3);
+        dst_b = vmlaq_u16(vshll_n_u8(sb,2), dst_b, scale8);
+        dst_g = vmlaq_u16(vshll_n_u8(sg,3), dst_g, scale8);
+        dst_r = vmlaq_u16(vshll_n_u8(sr,2), dst_r, scale8);
+#else
+        /* known correct, but +3 insns over above */
+        scale8 = vshrq_n_u16(scale8, 3);
+        dst_b = vmulq_u16(dst_b, scale8);
+        dst_g = vmulq_u16(dst_g, scale8);
+        dst_r = vmulq_u16(dst_r, scale8);
+
+        /* combine */
+        /* NB: vshll widens, need to preserve those bits */
+        dst_b = vaddq_u16(dst_b, vshll_n_u8(sb,2));
+        dst_g = vaddq_u16(dst_g, vshll_n_u8(sg,3));
+        dst_r = vaddq_u16(dst_r, vshll_n_u8(sr,2));
+#endif
+
+        /* repack to store */
+        dst8 = vandq_u16(vshrq_n_u16(dst_b, 5), vdupq_n_u16(0x001F));
+        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dst_g, 5), 5);
+        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dst_r,5), 11);
+
+        vst1q_u16(dst, dst8);
+
+#if    defined(DEBUG_OPAQUE_DITHER)
+        /* verify my 8 elements match the temp buffer */
+    {
+       int i, bad=0;
+       static int invocation;
+
+       for (i=0;i<UNROLL;i++)
+        if (tmpbuf[i] != dst[i]) bad=1;
+       if (bad) {
+        SkDebugf("BAD S32A_D565_Opaque_Dither_neon(); invocation %d offset %d\n",
+            invocation, offset);
+        SkDebugf("  alpha 0x%x\n", alpha);
+        for (i=0;i<UNROLL;i++)
+            SkDebugf("%2d: %s %04x w %04x id %04x s %08x d %04x %04x %04x %04x\n",
+            i, ((tmpbuf[i] != dst[i])?"BAD":"got"),
+            dst[i], tmpbuf[i], in_dst[i], src[i], td[i], tdv[i], tap[i], ta[i]);
+
+        showme16("alpha8", &alpha8, sizeof(alpha8));
+        showme16("scale8", &scale8, sizeof(scale8));
+        showme8("d", &d, sizeof(d));
+        showme16("dst8", &dst8, sizeof(dst8));
+        showme16("dst_b", &dst_b, sizeof(dst_b));
+        showme16("dst_g", &dst_g, sizeof(dst_g));
+        showme16("dst_r", &dst_r, sizeof(dst_r));
+        showme8("sb", &sb, sizeof(sb));
+        showme8("sg", &sg, sizeof(sg));
+        showme8("sr", &sr, sizeof(sr));
+
+        /* cop out */
+        return;
+       }
+       offset += UNROLL;
+       invocation++;
+    }
+#endif
+
+            dst += UNROLL;
+        src += UNROLL;
+        count -= UNROLL;
+        /* skip x += UNROLL, since it's unchanged mod-4 */
+        } while (count >= UNROLL);
+    }
+#undef    UNROLL
+
+    /* residuals */
+    if (count > 0) {
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            if (c) {
+                unsigned a = SkGetPackedA32(c);
+
+                // dither and alpha are just temporary variables to work-around
+                // an ICE in debug.
+                unsigned dither = DITHER_VALUE(x);
+                unsigned alpha = SkAlpha255To256(a);
+                int d = SkAlphaMul(dither, alpha);
+
+                unsigned sr = SkGetPackedR32(c);
+                unsigned sg = SkGetPackedG32(c);
+                unsigned sb = SkGetPackedB32(c);
+                sr = SkDITHER_R32_FOR_565(sr, d);
+                sg = SkDITHER_G32_FOR_565(sg, d);
+                sb = SkDITHER_B32_FOR_565(sb, d);
+
+                uint32_t src_expanded = (sg << 24) | (sr << 13) | (sb << 2);
+                uint32_t dst_expanded = SkExpand_rgb_16(*dst);
+                dst_expanded = dst_expanded * (SkAlpha255To256(255 - a) >> 3);
+                // now src and dst expanded are in g:11 r:10 x:1 b:10
+                *dst = SkCompact_rgb_16((src_expanded + dst_expanded) >> 5);
+            }
+            dst += 1;
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+/* 2009/10/27: RBE says "a work in progress"; debugging says ok;
+ * speedup untested, but ARM version is 26 insns/iteration and
+ * this NEON version is 21 insns/iteration-of-8 (2.62insns/element)
+ * which is 10x the native version; that's pure instruction counts,
+ * not accounting for any instruction or memory latencies.
+ */
+
+#undef    DEBUG_S32_OPAQUE_DITHER
+
+void S32_D565_Opaque_Dither_neon(uint16_t* SK_RESTRICT dst,
+                                 const SkPMColor* SK_RESTRICT src,
+                                 int count, U8CPU alpha, int x, int y) {
+    SkASSERT(255 == alpha);
+
+#define    UNROLL    8
+    if (count >= UNROLL) {
+    uint8x8_t d;
+    const uint8_t *dstart = &gDitherMatrix_Neon[(y&3)*12 + (x&3)];
+    d = vld1_u8(dstart);
+
+    while (count >= UNROLL) {
+        uint8x8_t sr, sg, sb, sa;
+        uint16x8_t dr, dg, db, da;
+        uint16x8_t dst8;
+
+        /* source is in ABGR ordering (R == lsb) */
+        {
+        register uint8x8_t d0 asm("d0");
+        register uint8x8_t d1 asm("d1");
+        register uint8x8_t d2 asm("d2");
+        register uint8x8_t d3 asm("d3");
+
+        asm ("vld4.8    {d0-d3},[%4]  /* r=%P0 g=%P1 b=%P2 a=%P3 */"
+            : "=w" (d0), "=w" (d1), "=w" (d2), "=w" (d3)
+            : "r" (src)
+                    );
+            sr = d0; sg = d1; sb = d2; sa = d3;
+        }
+        /* XXX: if we want to prefetch, hide it in the above asm()
+         * using the gcc __builtin_prefetch(), the prefetch will
+         * fall to the bottom of the loop -- it won't stick up
+         * at the top of the loop, just after the vld4.
+         */
+
+        /* sr = sr - (sr>>5) + d */
+        sr = vsub_u8(sr, vshr_n_u8(sr, 5));
+        dr = vaddl_u8(sr, d);
+
+        /* sb = sb - (sb>>5) + d */
+        sb = vsub_u8(sb, vshr_n_u8(sb, 5));
+        db = vaddl_u8(sb, d);
+
+        /* sg = sg - (sg>>6) + d>>1; similar logic for overflows */
+        sg = vsub_u8(sg, vshr_n_u8(sg, 6));
+        dg = vaddl_u8(sg, vshr_n_u8(d,1));
+        /* XXX: check that the "d>>1" here is hoisted */
+
+        /* pack high bits of each into 565 format  (rgb, b is lsb) */
+        dst8 = vshrq_n_u16(db, 3);
+        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dg, 2), 5);
+        dst8 = vsliq_n_u16(dst8, vshrq_n_u16(dr,3), 11);
+
+        /* store it */
+        vst1q_u16(dst, dst8);
+
+#if    defined(DEBUG_S32_OPAQUE_DITHER)
+        /* always good to know if we generated good results */
+        {
+        int i, myx = x, myy = y;
+        DITHER_565_SCAN(myy);
+        for (i=0;i<UNROLL;i++) {
+            SkPMColor c = src[i];
+            unsigned dither = DITHER_VALUE(myx);
+            uint16_t val = SkDitherRGB32To565(c, dither);
+            if (val != dst[i]) {
+            SkDebugf("RBE: src %08x dither %02x, want %04x got %04x dbas[i] %02x\n",
+                c, dither, val, dst[i], dstart[i]);
+            }
+            DITHER_INC_X(myx);
+        }
+        }
+#endif
+
+        dst += UNROLL;
+        src += UNROLL;
+        count -= UNROLL;
+        x += UNROLL;        /* probably superfluous */
+    }
+    }
+#undef    UNROLL
+
+    /* residuals */
+    if (count > 0) {
+        DITHER_565_SCAN(y);
+        do {
+            SkPMColor c = *src++;
+            SkPMColorAssert(c);
+            SkASSERT(SkGetPackedA32(c) == 255);
+
+            unsigned dither = DITHER_VALUE(x);
+            *dst++ = SkDitherRGB32To565(c, dither);
+            DITHER_INC_X(x);
+        } while (--count != 0);
+    }
+}
+
+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);
+    } else {
+        unsigned scale = 256 - SkAlpha255To256(colorA);
+
+        if (count >= 8) {
+            // at the end of this assembly, count will have been decremented
+            // to a negative value. That is, if count mod 8 = x, it will be
+            // -8 +x coming out.
+            asm volatile (
+                PLD128(src, 0)
+
+                "vdup.32    q0, %[color]                \n\t"
+
+                PLD128(src, 128)
+
+                // scale numerical interval [0-255], so load as 8 bits
+                "vdup.8     d2, %[scale]                \n\t"
+
+                PLD128(src, 256)
+
+                "subs       %[count], %[count], #8      \n\t"
+
+                PLD128(src, 384)
+
+                "Loop_Color32:                          \n\t"
+
+                // load src color, 8 pixels, 4 64 bit registers
+                // (and increment src).
+                "vld1.32    {d4-d7}, [%[src]]!          \n\t"
+
+                PLD128(src, 384)
+
+                // multiply long by scale, 64 bits at a time,
+                // destination into a 128 bit register.
+                "vmull.u8   q4, d4, d2                  \n\t"
+                "vmull.u8   q5, d5, d2                  \n\t"
+                "vmull.u8   q6, d6, d2                  \n\t"
+                "vmull.u8   q7, d7, d2                  \n\t"
+
+                // shift the 128 bit registers, containing the 16
+                // bit scaled values back to 8 bits, narrowing the
+                // results to 64 bit registers.
+                "vshrn.i16  d8, q4, #8                  \n\t"
+                "vshrn.i16  d9, q5, #8                  \n\t"
+                "vshrn.i16  d10, q6, #8                 \n\t"
+                "vshrn.i16  d11, q7, #8                 \n\t"
+
+                // adding back the color, using 128 bit registers.
+                "vadd.i8    q6, q4, q0                  \n\t"
+                "vadd.i8    q7, q5, q0                  \n\t"
+
+                // store back the 8 calculated pixels (2 128 bit
+                // registers), and increment dst.
+                "vst1.32    {d12-d15}, [%[dst]]!        \n\t"
+
+                "subs       %[count], %[count], #8      \n\t"
+                "bge        Loop_Color32                \n\t"
+                : [src] "+r" (src), [dst] "+r" (dst), [count] "+r" (count)
+                : [color] "r" (color), [scale] "r" (scale)
+                : "cc", "memory",
+                  "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7",
+                  "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15"
+                          );
+            // At this point, if we went through the inline assembly, count is
+            // a negative value:
+            // if the value is -8, there is no pixel left to process.
+            // if the value is -7, there is one pixel left to process
+            // ...
+            // And'ing it with 7 will give us the number of pixels
+            // left to process.
+            count = count & 0x7;
+        }
+
+        while (count > 0) {
+            *dst = color + SkAlphaMulQ(*src, scale);
+            src += 1;
+            dst += 1;
+            count--;
+        }
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+const SkBlitRow::Proc sk_blitrow_platform_565_procs_arm_neon[] = {
+    // no dither
+    // NOTE: For the two functions below, we don't have a special version
+    //       that assumes that each source pixel is opaque. But our S32A is
+    //       still faster than the default, so use it.
+    S32A_D565_Opaque_neon,  // really S32_D565_Opaque
+    S32A_D565_Blend_neon,   // really S32_D565_Blend
+    S32A_D565_Opaque_neon,
+    S32A_D565_Blend_neon,
+
+    // dither
+    S32_D565_Opaque_Dither_neon,
+    S32_D565_Blend_Dither_neon,
+    S32A_D565_Opaque_Dither_neon,
+    NULL,   // S32A_D565_Blend_Dither
+};
+
+const SkBlitRow::Proc sk_blitrow_platform_4444_procs_arm_neon[] = {
+    // no dither
+    NULL,   // S32_D4444_Opaque,
+    NULL,   // S32_D4444_Blend,
+    NULL,   // S32A_D4444_Opaque,
+    NULL,   // S32A_D4444_Blend,
+
+    // dither
+    NULL,   // S32_D4444_Opaque_Dither,
+    NULL,   // S32_D4444_Blend_Dither,
+    NULL,   // S32A_D4444_Opaque_Dither,
+    NULL,   // S32A_D4444_Blend_Dither
+};
+
+const SkBlitRow::Proc32 sk_blitrow_platform_32_procs_arm_neon[] = {
+    NULL,   // S32_Opaque,
+    S32_Blend_BlitRow32_neon,        // S32_Blend,
+    S32A_Opaque_BlitRow32_neon,        // S32A_Opaque,
+    S32A_Blend_BlitRow32_arm        // S32A_Blend
+};
diff --git a/src/opts/SkBlitRow_opts_none.cpp b/src/opts/SkBlitRow_opts_none.cpp
index 5f4598e..88e1af3 100644
--- a/src/opts/SkBlitRow_opts_none.cpp
+++ b/src/opts/SkBlitRow_opts_none.cpp
@@ -43,4 +43,3 @@
                                                  RowFlags flags) {
     return NULL;
 }
-
diff --git a/src/opts/SkCachePreload_arm.h b/src/opts/SkCachePreload_arm.h
new file mode 100644
index 0000000..cff8c2a
--- /dev/null
+++ b/src/opts/SkCachePreload_arm.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2012 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 SkCachePreload_arm_DEFINED
+#define SkCachePreload_arm_DEFINED
+
+// This file defines macros for preload instructions for ARM. These macros
+// are designed to be embedded inside GNU inline assembly.
+// For the use of these macros, __ARM_USE_PLD needs to be enabled. The cache
+// line size also needs to be known (and needs to be contained inside
+// __ARM_CACHE_LINE_SIZE).
+#if defined(__ARM_USE_PLD)
+
+#define PLD(x, n)           "pld        [%["#x"], #("#n")]\n\t"
+
+#if __ARM_CACHE_LINE_SIZE == 32
+    #define PLD64(x, n)      PLD(x, n) PLD(x, (n) + 32)
+#elif __ARM_CACHE_LINE_SIZE == 64
+    #define PLD64(x, n)      PLD(x, n)
+#else
+    #error "unknown __ARM_CACHE_LINE_SIZE."
+#endif
+#else
+    // PLD is disabled, all macros become empty.
+    #define PLD(x, n)
+    #define PLD64(x, n)
+#endif
+
+#define PLD128(x, n)         PLD64(x, n) PLD64(x, (n) + 64)
+
+#endif  // SkCachePreload_arm_DEFINED
diff --git a/src/opts/SkUtils_opts_SSE2.cpp b/src/opts/SkUtils_opts_SSE2.cpp
index 63e7f2c..08e4f66 100644
--- a/src/opts/SkUtils_opts_SSE2.cpp
+++ b/src/opts/SkUtils_opts_SSE2.cpp
@@ -9,7 +9,7 @@
 
 #include <emmintrin.h>
 #include "SkUtils_opts_SSE2.h"
- 
+
 void sk_memset16_SSE2(uint16_t *dst, uint16_t value, int count)
 {
     SkASSERT(dst != NULL && count >= 0);
@@ -38,7 +38,7 @@
         --count;
     }
 }
- 
+
 void sk_memset32_SSE2(uint32_t *dst, uint32_t value, int count)
 {
     SkASSERT(dst != NULL && count >= 0);
diff --git a/src/opts/SkUtils_opts_SSE2.h b/src/opts/SkUtils_opts_SSE2.h
index 771656f..ed24c1f 100644
--- a/src/opts/SkUtils_opts_SSE2.h
+++ b/src/opts/SkUtils_opts_SSE2.h
@@ -8,6 +8,6 @@
 
 
 #include "SkTypes.h"
- 
+
 void sk_memset16_SSE2(uint16_t *dst, uint16_t value, int count);
 void sk_memset32_SSE2(uint32_t *dst, uint32_t value, int count);
diff --git a/src/opts/SkUtils_opts_none.cpp b/src/opts/SkUtils_opts_none.cpp
index 286f10d..d905425 100644
--- a/src/opts/SkUtils_opts_none.cpp
+++ b/src/opts/SkUtils_opts_none.cpp
@@ -7,6 +7,7 @@
  */
 
 
+#include "SkBlitRow.h"
 #include "SkUtils.h"
 
 SkMemset16Proc SkMemset16GetPlatformProc() {
@@ -16,3 +17,7 @@
 SkMemset32Proc SkMemset32GetPlatformProc() {
     return NULL;
 }
+
+SkBlitRow::ColorRectProc PlatformColorRectProcFactory() {
+    return NULL;
+}
diff --git a/src/opts/memset.arm.S b/src/opts/memset.arm.S
index 1248631..44b75e3 100644
--- a/src/opts/memset.arm.S
+++ b/src/opts/memset.arm.S
@@ -14,7 +14,8 @@
  */
 
     .text
-    .align
+    .align 4
+    .syntax unified
 
     .global arm_memset32
     .type   arm_memset32, %function
@@ -41,11 +42,11 @@
         mov         r2, r2, lsl #1
 
         /* expand the data to 32 bits */
-        orr         r1, r1, lsl #16
+        orr         r1, r1, r1, lsl #16
 
         /* align to 32 bits */
         tst         r0, #2
-        strneh      r1, [r0], #2
+        strhne      r1, [r0], #2
         subne       r2, r2, #2
 
         /* Now jump into the main loop below. */
@@ -81,9 +82,9 @@
         /* (Optionally) write any unaligned leading bytes.
          * (0-28 bytes, length in r3) */
         movs        r3, r3, lsl #28
-        stmcsia     r0!, {r1, lr}
-        stmcsia     r0!, {r1, lr}
-        stmmiia     r0!, {r1, lr}
+        stmiacs     r0!, {r1, lr}
+        stmiacs     r0!, {r1, lr}
+        stmiami     r0!, {r1, lr}
         movs        r3, r3, lsl #2
         strcs       r1, [r0], #4
 
@@ -91,19 +92,19 @@
 .Laligned32:
         mov         r3, r1
 1:      subs        r2, r2, #32
-        stmhsia     r0!, {r1,r3,ip,lr}
-        stmhsia     r0!, {r1,r3,ip,lr}
+        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
-        stmcsia     r0!, {r1,r3,ip,lr}
-        stmmiia     r0!, {r1,lr}
+        stmiacs     r0!, {r1,r3,ip,lr}
+        stmiami     r0!, {r1,lr}
         movs        r2, r2, lsl #2
         strcs       r1, [r0], #4
-        strmih      lr, [r0], #2
+        strhmi      lr, [r0], #2
 
 .Lfinish:
         pop         {pc}
diff --git a/src/opts/opts_check_SSE2.cpp b/src/opts/opts_check_SSE2.cpp
index be1b4a1..6370058 100644
--- a/src/opts/opts_check_SSE2.cpp
+++ b/src/opts/opts_check_SSE2.cpp
@@ -8,10 +8,16 @@
 #include "SkBitmapProcState_opts_SSE2.h"
 #include "SkBitmapProcState_opts_SSSE3.h"
 #include "SkBlitMask.h"
+#include "SkBlitRow.h"
+#include "SkBlitRect_opts_SSE2.h"
 #include "SkBlitRow_opts_SSE2.h"
 #include "SkUtils_opts_SSE2.h"
 #include "SkUtils.h"
 
+#if defined(_MSC_VER) && defined(_WIN64)
+#include <intrin.h>
+#endif
+
 /* This file must *not* be compiled with -msse or -msse2, otherwise
    gcc may generate sse2 even for scalar ops (and thus give an invalid
    instruction on Pentium3 on the code below).  Only files named *_SSE2.cpp
@@ -20,6 +26,9 @@
 
 #ifdef _MSC_VER
 static inline void getcpuid(int info_type, int info[4]) {
+#if defined(_WIN64)
+    __cpuid(info, info_type);
+#else
     __asm {
         mov    eax, [info_type]
         cpuid
@@ -29,6 +38,7 @@
         mov    [edi+8], ecx
         mov    [edi+12], edx
     }
+#endif
 }
 #else
 #if defined(__x86_64__)
@@ -54,8 +64,8 @@
 #endif
 #endif
 
-#if defined(__x86_64__) || defined(_WIN64)
-/* All x86_64 machines have SSE2, so don't even bother checking. */
+#if defined(__x86_64__) || defined(_WIN64) || SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2
+/* All x86_64 machines have SSE2, or we know it's supported at compile time,  so don't even bother checking. */
 static inline bool hasSSE2() {
     return true;
 }
@@ -68,11 +78,19 @@
 }
 #endif
 
+#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3
+/* If we know SSSE3 is supported at compile time, don't even bother checking. */
+static inline bool hasSSSE3() {
+    return true;
+}
+#else
+
 static inline bool hasSSSE3() {
     int cpu_info[4] = { 0 };
     getcpuid(1, cpu_info);
     return (cpu_info[2] & 0x200) != 0;
 }
+#endif
 
 static bool cachedHasSSE2() {
     static bool gHasSSE2 = hasSSE2();
@@ -86,17 +104,30 @@
 
 void SkBitmapProcState::platformProcs() {
     if (cachedHasSSSE3()) {
+#if !defined(SK_BUILD_FOR_ANDROID)
+        // Disable SSSE3 optimization for Android x86
         if (fSampleProc32 == S32_opaque_D32_filter_DX) {
             fSampleProc32 = S32_opaque_D32_filter_DX_SSSE3;
         } else if (fSampleProc32 == S32_alpha_D32_filter_DX) {
             fSampleProc32 = S32_alpha_D32_filter_DX_SSSE3;
         }
+
+        if (fSampleProc32 == S32_opaque_D32_filter_DXDY) {
+            fSampleProc32 = S32_opaque_D32_filter_DXDY_SSSE3;
+        } else if (fSampleProc32 == S32_alpha_D32_filter_DXDY) {
+            fSampleProc32 = S32_alpha_D32_filter_DXDY_SSSE3;
+        }
+#endif
     } else if (cachedHasSSE2()) {
         if (fSampleProc32 == S32_opaque_D32_filter_DX) {
             fSampleProc32 = S32_opaque_D32_filter_DX_SSE2;
         } else if (fSampleProc32 == S32_alpha_D32_filter_DX) {
             fSampleProc32 = S32_alpha_D32_filter_DX_SSE2;
         }
+
+        if (fSampleProc16 == S32_D16_filter_DX) {
+            fSampleProc16 = S32_D16_filter_DX_SSE2;
+        }
     }
 
     if (cachedHasSSSE3() || cachedHasSSE2()) {
@@ -152,7 +183,7 @@
     if (SkMask::kA8_Format != maskFormat) {
         return NULL;
     }
-    
+
     ColorProc proc = NULL;
     if (cachedHasSSE2()) {
         switch (dstConfig) {
@@ -203,3 +234,13 @@
         return NULL;
     }
 }
+
+SkBlitRow::ColorRectProc PlatformColorRectProcFactory(); // suppress warning
+
+SkBlitRow::ColorRectProc PlatformColorRectProcFactory() {
+    if (cachedHasSSE2()) {
+        return ColorRect32_SSE2;
+    } else {
+        return NULL;
+    }
+}
diff --git a/src/opts/opts_check_arm.cpp b/src/opts/opts_check_arm.cpp
index 20ec8a1..ba407d7 100644
--- a/src/opts/opts_check_arm.cpp
+++ b/src/opts/opts_check_arm.cpp
@@ -13,9 +13,12 @@
  *    available in the core
  */
 
+#include "SkBlitRow.h"
 #include "SkUtils.h"
 
-#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
+#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
@@ -26,21 +29,39 @@
 #endif
 
 SkMemset16Proc SkMemset16GetPlatformProc() {
-#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
-    return memset16_neon;
-#elif defined(SK_CPU_LENDIAN)
-    return arm_memset16;
-#else
+    // FIXME: memset.arm.S is using syntax incompatible with XCode
+#if !defined(SK_CPU_LENDIAN) || defined(SK_BUILD_FOR_IOS)
     return NULL;
+#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;
+#else
+    return arm_memset16;
 #endif
 }
 
 SkMemset32Proc SkMemset32GetPlatformProc() {
-#if defined(__ARM_HAVE_NEON) && defined(SK_CPU_LENDIAN)
-    return memset32_neon;
-#elif defined(SK_CPU_LENDIAN)
-    return arm_memset32;
-#else
+    // FIXME: memset.arm.S is using syntax incompatible with XCode
+#if !defined(SK_CPU_LENDIAN) || defined(SK_BUILD_FOR_IOS)
     return NULL;
+#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;
+#else
+    return arm_memset32;
 #endif
 }
+
+SkBlitRow::ColorRectProc PlatformColorRectProcFactory() {
+    return NULL;
+}
diff --git a/src/pdf/SkPDFCatalog.cpp b/src/pdf/SkPDFCatalog.cpp
index bc22e6a..c0f6fb0 100644
--- a/src/pdf/SkPDFCatalog.cpp
+++ b/src/pdf/SkPDFCatalog.cpp
@@ -38,7 +38,7 @@
     return obj;
 }
 
-size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, size_t offset) {
+size_t SkPDFCatalog::setFileOffset(SkPDFObject* obj, off_t offset) {
     int objIndex = assignObjNum(obj) - 1;
     SkASSERT(fCatalog[objIndex].fObjNumAssigned);
     SkASSERT(fCatalog[objIndex].fFileOffset == 0);
diff --git a/src/pdf/SkPDFCatalog.h b/src/pdf/SkPDFCatalog.h
new file mode 100644
index 0000000..d5825ac
--- /dev/null
+++ b/src/pdf/SkPDFCatalog.h
@@ -0,0 +1,137 @@
+
+/*
+ * 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 "SkPDFDocument.h"
+#include "SkPDFTypes.h"
+#include "SkRefCnt.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.
+     */
+    explicit SkPDFCatalog(SkPDFDocument::Flags flags);
+    ~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.  Returns
+     *  the object's size.
+     *  @param obj         The object to add.
+     *  @param offset      The byte offset in the output stream of this object.
+     */
+    size_t setFileOffset(SkPDFObject* obj, off_t offset);
+
+    /** Output the object number for the passed object.
+     *  @param obj         The object of interest.
+     *  @param stream      The writable output stream to send the output to.
+     */
+    void emitObjectNumber(SkWStream* stream, SkPDFObject* obj);
+
+    /** Return the number of bytes that would be emitted for the passed
+     *  object's object number.
+     *  @param obj         The object of interest
+     */
+    size_t getObjectNumberSize(SkPDFObject* obj);
+
+    /** Return the document flags in effect for this catalog/document.
+     */
+    SkPDFDocument::Flags getDocumentFlags() const { return fDocumentFlags; }
+
+    /** 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);
+
+    /** Set file offsets for the resources of substitute objects.
+     *  @param fileOffset Accumulated offset of current document.
+     *  @param firstPage  Indicate whether this is for the first page only.
+     *  @return           Total size of resources of substitute objects.
+     */
+    off_t setSubstituteResourcesOffsets(off_t fileOffset, bool firstPage);
+
+    /** Emit the resources of substitute objects.
+     */
+    void emitSubstituteResources(SkWStream* stream, bool firstPage);
+
+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<struct Rec> fCatalog;
+
+    // TODO(arthurhsu): Make this a hash if it's a performance problem.
+    SkTDArray<SubstituteMapping> fSubstituteMap;
+    SkTDArray<SkPDFObject*> fSubstituteResourcesFirstPage;
+    SkTDArray<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;
+
+    SkPDFDocument::Flags fDocumentFlags;
+
+    int findObjectIndex(SkPDFObject* obj) const;
+
+    int assignObjNum(SkPDFObject* obj);
+
+    SkTDArray<SkPDFObject*>* getSubstituteList(bool firstPage);
+};
+
+#endif
diff --git a/src/pdf/SkPDFDevice.cpp b/src/pdf/SkPDFDevice.cpp
index ba88956..e253a5f 100644
--- a/src/pdf/SkPDFDevice.cpp
+++ b/src/pdf/SkPDFDevice.cpp
@@ -9,10 +9,12 @@
 
 #include "SkPDFDevice.h"
 
+#include "SkAnnotation.h"
 #include "SkColor.h"
 #include "SkClipStack.h"
 #include "SkData.h"
 #include "SkDraw.h"
+#include "SkFontHost.h"
 #include "SkGlyphCache.h"
 #include "SkPaint.h"
 #include "SkPath.h"
@@ -27,6 +29,7 @@
 #include "SkRect.h"
 #include "SkString.h"
 #include "SkTextFormatParams.h"
+#include "SkTemplates.h"
 #include "SkTypeface.h"
 #include "SkTypes.h"
 
@@ -74,7 +77,7 @@
 
     SkMatrix ident;
     ident.reset();
-    SkAutoGlyphCache autoCache(paint, &ident);
+    SkAutoGlyphCache autoCache(paint, NULL, &ident);
     SkGlyphCache* cache = autoCache.getCache();
 
     const char* start = reinterpret_cast<const char*>(glyphs);
@@ -101,6 +104,69 @@
     *y = *y - yAdj;
 }
 
+static size_t max_glyphid_for_typeface(const SkTypeface* typeface) {
+    SkAdvancedTypefaceMetrics* metrics;
+    metrics = SkFontHost::GetAdvancedTypefaceMetrics(
+            SkTypeface::UniqueID(typeface),
+            SkAdvancedTypefaceMetrics::kNo_PerGlyphInfo,
+            NULL, 0);
+
+    int lastGlyphID = 0;
+    if (metrics) {
+        lastGlyphID = metrics->fLastGlyphID;
+        metrics->unref();
+    }
+    return lastGlyphID;
+}
+
+typedef SkAutoSTMalloc<128, uint16_t> SkGlyphStorage;
+
+static size_t force_glyph_encoding(const SkPaint& paint, const void* text,
+                                   size_t len, SkGlyphStorage* storage,
+                                   uint16_t** glyphIDs) {
+    // Make sure we have a glyph id encoding.
+    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
+        size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
+        storage->reset(numGlyphs);
+        paint.textToGlyphs(text, len, storage->get());
+        *glyphIDs = storage->get();
+        return numGlyphs;
+    }
+
+    // For user supplied glyph ids we need to validate them.
+    SkASSERT((len & 1) == 0);
+    size_t numGlyphs = len / 2;
+    const uint16_t* input =
+        reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
+
+    int maxGlyphID = max_glyphid_for_typeface(paint.getTypeface());
+    size_t validated;
+    for (validated = 0; validated < numGlyphs; ++validated) {
+        if (input[validated] > maxGlyphID) {
+            break;
+        }
+    }
+    if (validated >= numGlyphs) {
+        *glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
+        return numGlyphs;
+    }
+
+    // Silently drop anything out of range.
+    storage->reset(numGlyphs);
+    if (validated > 0) {
+        memcpy(storage->get(), input, validated * sizeof(uint16_t));
+    }
+
+    for (size_t i = validated; i < numGlyphs; ++i) {
+        storage->get()[i] = input[i];
+        if (input[i] > maxGlyphID) {
+            storage->get()[i] = 0;
+        }
+    }
+    *glyphIDs = storage->get();
+    return numGlyphs;
+}
+
 static void set_text_transform(SkScalar x, SkScalar y, SkScalar textSkewX,
                                SkWStream* content) {
     // Flip the text about the x-axis to account for origin swap and include
@@ -216,38 +282,30 @@
     fStackDepth--;
 }
 
-// This function initializes iter to be an interator on the "stack" argument
+// This function initializes iter to be an iterator on the "stack" argument
 // and then skips over the leading entries as specified in prefix.  It requires
 // and asserts that "prefix" will be a prefix to "stack."
 static void skip_clip_stack_prefix(const SkClipStack& prefix,
                                    const SkClipStack& stack,
-                                   SkClipStack::B2FIter* iter) {
-    SkClipStack::B2FIter prefixIter(prefix);
-    iter->reset(stack);
+                                   SkClipStack::Iter* iter) {
+    SkClipStack::B2TIter prefixIter(prefix);
+    iter->reset(stack, SkClipStack::Iter::kBottom_IterStart);
 
-    const SkClipStack::B2FIter::Clip* prefixEntry;
-    const SkClipStack::B2FIter::Clip* iterEntry;
+    const SkClipStack::Element* prefixEntry;
+    const SkClipStack::Element* iterEntry;
 
-    int count = 0;
     for (prefixEntry = prefixIter.next(); prefixEntry;
-            prefixEntry = prefixIter.next(), count++) {
+            prefixEntry = prefixIter.next()) {
         iterEntry = iter->next();
         SkASSERT(iterEntry);
         // Because of SkClipStack does internal intersection, the last clip
         // entry may differ.
         if (*prefixEntry != *iterEntry) {
-            SkASSERT(prefixEntry->fOp == SkRegion::kIntersect_Op);
-            SkASSERT(iterEntry->fOp == SkRegion::kIntersect_Op);
-            SkASSERT((iterEntry->fRect == NULL) ==
-                    (prefixEntry->fRect == NULL));
-            SkASSERT((iterEntry->fPath == NULL) ==
-                    (prefixEntry->fPath == NULL));
-            // We need to back up the iterator by one but don't have that
-            // function, so reset and go forward by one less.
-            iter->reset(stack);
-            for (int i = 0; i < count; i++) {
-                iter->next();
-            }
+            SkASSERT(prefixEntry->getOp() == SkRegion::kIntersect_Op);
+            SkASSERT(iterEntry->getOp() == SkRegion::kIntersect_Op);
+            SkASSERT(iterEntry->getType() == prefixEntry->getType());
+            // back up the iterator by one
+            iter->prev();
             prefixEntry = prefixIter.next();
             break;
         }
@@ -262,7 +320,7 @@
 
     SkPath::FillType clipFill;
     if (clipPath) {
-        SkPDFUtils::EmitPath(*clipPath, contentStream);
+        SkPDFUtils::EmitPath(*clipPath, SkPaint::kFill_Style, contentStream);
         clipFill = clipPath->getFillType();
     } else {
         SkPDFUtils::AppendRectangle(*clipRect, contentStream);
@@ -304,16 +362,15 @@
     // initial clip on the parent layer.  (This means there's a bug if the user
     // expands the clip and then uses any xfer mode that uses dst:
     // http://code.google.com/p/skia/issues/detail?id=228 )
-    SkClipStack::B2FIter iter;
+    SkClipStack::Iter iter;
     skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
 
     // If the clip stack does anything other than intersect or if it uses
     // an inverse fill type, we have to fall back to the clip region.
     bool needRegion = false;
-    const SkClipStack::B2FIter::Clip* clipEntry;
+    const SkClipStack::Element* clipEntry;
     for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
-        if (clipEntry->fOp != SkRegion::kIntersect_Op ||
-                (clipEntry->fPath && clipEntry->fPath->isInverseFillType())) {
+        if (clipEntry->getOp() != SkRegion::kIntersect_Op || clipEntry->isInverseFilled()) {
             needRegion = true;
             break;
         }
@@ -327,19 +384,24 @@
         skip_clip_stack_prefix(fEntries[0].fClipStack, clipStack, &iter);
         SkMatrix transform;
         transform.setTranslate(translation.fX, translation.fY);
-        const SkClipStack::B2FIter::Clip* clipEntry;
+        const SkClipStack::Element* clipEntry;
         for (clipEntry = iter.next(); clipEntry; clipEntry = iter.next()) {
-            SkASSERT(clipEntry->fOp == SkRegion::kIntersect_Op);
-            if (clipEntry->fRect) {
-                SkRect translatedClip;
-                transform.mapRect(&translatedClip, *clipEntry->fRect);
-                emit_clip(NULL, &translatedClip, fContentStream);
-            } else if (clipEntry->fPath) {
-                SkPath translatedPath;
-                clipEntry->fPath->transform(transform, &translatedPath);
-                emit_clip(&translatedPath, NULL, fContentStream);
-            } else {
-                SkASSERT(false);
+            SkASSERT(clipEntry->getOp() == SkRegion::kIntersect_Op);
+            switch (clipEntry->getType()) {
+                case SkClipStack::Element::kRect_Type: {
+                    SkRect translatedClip;
+                    transform.mapRect(&translatedClip, clipEntry->getRect());
+                    emit_clip(NULL, &translatedClip, fContentStream);
+                    break;
+                }
+                case SkClipStack::Element::kPath_Type: {
+                    SkPath translatedPath;
+                    clipEntry->getPath().transform(transform, &translatedPath);
+                    emit_clip(&translatedPath, NULL, fContentStream);
+                    break;
+                }
+                default:
+                    SkASSERT(false);
             }
         }
     }
@@ -457,8 +519,9 @@
 
     ~ScopedContentEntry() {
         if (fContentEntry) {
-            fDevice->finishContentEntry(fXfermode, fDstFormXObject.get());
+            fDevice->finishContentEntry(fXfermode, fDstFormXObject);
         }
+        SkSafeUnref(fDstFormXObject);
     }
 
     ContentEntry* entry() { return fContentEntry; }
@@ -466,10 +529,11 @@
     SkPDFDevice* fDevice;
     ContentEntry* fContentEntry;
     SkXfermode::Mode fXfermode;
-    SkRefPtr<SkPDFFormXObject> fDstFormXObject;
+    SkPDFFormXObject* fDstFormXObject;
 
     void init(const SkClipStack* clipStack, const SkRegion& clipRegion,
               const SkMatrix& matrix, const SkPaint& paint, bool hasText) {
+        fDstFormXObject = NULL;
         if (paint.getXfermode()) {
             paint.getXfermode()->asMode(&fXfermode);
         }
@@ -490,7 +554,11 @@
         SkMatrix inverse;
         drawingSize.set(SkIntToScalar(contentSize.fWidth),
                         SkIntToScalar(contentSize.fHeight));
-        initialTransform->invert(&inverse);
+        if (!initialTransform->invert(&inverse)) {
+            // This shouldn't happen, initial transform should be invertible.
+            SkASSERT(false);
+            inverse.reset();
+        }
         inverse.mapVectors(&drawingSize, 1);
         SkISize size = SkSize::Make(drawingSize.fX, drawingSize.fY).toRound();
         bitmap.setConfig(SkBitmap::kNo_Config, abs(size.fWidth),
@@ -510,7 +578,8 @@
       fPageSize(pageSize),
       fContentSize(contentSize),
       fLastContentEntry(NULL),
-      fLastMarginContentEntry(NULL) {
+      fLastMarginContentEntry(NULL),
+      fClipStack(NULL) {
     // Skia generally uses the top left as the origin but PDF natively has the
     // origin at the bottom left. This matrix corrects for that.  But that only
     // needs to be done once, we don't do it when layering.
@@ -534,7 +603,8 @@
       fExistingClipStack(existingClipStack),
       fExistingClipRegion(existingClipRegion),
       fLastContentEntry(NULL),
-      fLastMarginContentEntry(NULL) {
+      fLastMarginContentEntry(NULL),
+      fClipStack(NULL) {
     fInitialTransform.reset();
     this->init();
 }
@@ -544,6 +614,7 @@
 }
 
 void SkPDFDevice::init() {
+    fAnnotations = NULL;
     fResourceDict = NULL;
     fContentEntries.reset();
     fLastContentEntry = NULL;
@@ -560,6 +631,9 @@
     fXObjectResources.unrefAll();
     fFontResources.unrefAll();
     fShaderResources.unrefAll();
+    SkSafeUnref(fAnnotations);
+    SkSafeUnref(fResourceDict);
+
     if (clearFontUsage) {
         fFontGlyphUsage->reset();
     }
@@ -600,8 +674,9 @@
     SkMatrix totalTransform = fInitialTransform;
     totalTransform.preConcat(contentEntry->fState.fMatrix);
     SkMatrix inverse;
-    inverse.reset();
-    totalTransform.invert(&inverse);
+    if (!totalTransform.invert(&inverse)) {
+        return;
+    }
     inverse.mapRect(&bbox);
 
     SkPDFUtils::AppendRectangle(bbox, &contentEntry->fContent);
@@ -704,6 +779,10 @@
         return;
     }
 
+    if (handleAnnotations(r, *d.fMatrix, paint)) {
+        return;
+    }
+
     ScopedContentEntry content(this, d, paint);
     if (!content.entry()) {
         return;
@@ -756,11 +835,16 @@
         return;
     }
 
+    if (handleAnnotations(pathPtr->getBounds(), *d.fMatrix, paint)) {
+        return;
+    }
+
     ScopedContentEntry content(this, d, paint);
     if (!content.entry()) {
         return;
     }
-    SkPDFUtils::EmitPath(*pathPtr, &content.entry()->fContent);
+    SkPDFUtils::EmitPath(*pathPtr, paint.getStyle(),
+                         &content.entry()->fContent);
     SkPDFUtils::PaintPath(paint.getStyle(), pathPtr->getFillType(),
                           &content.entry()->fContent);
 }
@@ -797,20 +881,11 @@
         return;
     }
 
-    // We want the text in glyph id encoding and a writable buffer, so we end
-    // up making a copy either way.
-    size_t numGlyphs = paint.textToGlyphs(text, len, NULL);
-    uint16_t* glyphIDs = reinterpret_cast<uint16_t*>(
-            sk_malloc_flags(numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
-    SkAutoFree autoFreeGlyphIDs(glyphIDs);
-    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
-        paint.textToGlyphs(text, len, glyphIDs);
-        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    } else {
-        SkASSERT((len & 1) == 0);
-        SkASSERT(len / 2 == numGlyphs);
-        memcpy(glyphIDs, text, len);
-    }
+    SkGlyphStorage storage(0);
+    uint16_t* glyphIDs = NULL;
+    size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
+                                            &glyphIDs);
+    textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
     align_text(glyphCacheProc, textPaint, glyphIDs, numGlyphs, &x, &y);
@@ -846,22 +921,11 @@
         return;
     }
 
-    // Make sure we have a glyph id encoding.
-    SkAutoFree glyphStorage;
-    uint16_t* glyphIDs;
-    size_t numGlyphs;
-    if (paint.getTextEncoding() != SkPaint::kGlyphID_TextEncoding) {
-        numGlyphs = paint.textToGlyphs(text, len, NULL);
-        glyphIDs = reinterpret_cast<uint16_t*>(sk_malloc_flags(
-                numGlyphs * 2, SK_MALLOC_TEMP | SK_MALLOC_THROW));
-        glyphStorage.set(glyphIDs);
-        paint.textToGlyphs(text, len, glyphIDs);
-        textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
-    } else {
-        SkASSERT((len & 1) == 0);
-        numGlyphs = len / 2;
-        glyphIDs = reinterpret_cast<uint16_t*>(const_cast<void*>((text)));
-    }
+    SkGlyphStorage storage(0);
+    uint16_t* glyphIDs = NULL;
+    size_t numGlyphs = force_glyph_encoding(paint, text, len, &storage,
+                                            &glyphIDs);
+    textPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
 
     SkDrawCacheProc glyphCacheProc = textPaint.getDrawCacheProc();
     content.entry()->fContent.writeText("BT\n");
@@ -939,6 +1003,19 @@
     fFontGlyphUsage->merge(pdfDevice->getFontGlyphUsage());
 }
 
+void SkPDFDevice::onAttachToCanvas(SkCanvas* canvas) {
+    INHERITED::onAttachToCanvas(canvas);
+
+    // Canvas promises that this ptr is valid until onDetachFromCanvas is called
+    fClipStack = canvas->getClipStack();
+}
+
+void SkPDFDevice::onDetachFromCanvas() {
+    INHERITED::onDetachFromCanvas();
+
+    fClipStack = NULL;
+}
+
 ContentEntry* SkPDFDevice::getLastContentEntry() {
     if (fDrawingArea == kContent_DrawingArea) {
         return fLastContentEntry;
@@ -970,13 +1047,11 @@
 }
 
 SkPDFDict* SkPDFDevice::getResourceDict() {
-    if (fResourceDict.get() == NULL) {
-        fResourceDict = new SkPDFDict;
-        fResourceDict->unref();  // SkRefPtr and new both took a reference.
+    if (NULL == fResourceDict) {
+        fResourceDict = SkNEW(SkPDFDict);
 
         if (fGraphicStateResources.count()) {
-            SkRefPtr<SkPDFDict> extGState = new SkPDFDict();
-            extGState->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> extGState(new SkPDFDict());
             for (int i = 0; i < fGraphicStateResources.count(); i++) {
                 SkString nameString("G");
                 nameString.appendS32(i);
@@ -988,8 +1063,7 @@
         }
 
         if (fXObjectResources.count()) {
-            SkRefPtr<SkPDFDict> xObjects = new SkPDFDict();
-            xObjects->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> xObjects(new SkPDFDict());
             for (int i = 0; i < fXObjectResources.count(); i++) {
                 SkString nameString("X");
                 nameString.appendS32(i);
@@ -1001,8 +1075,7 @@
         }
 
         if (fFontResources.count()) {
-            SkRefPtr<SkPDFDict> fonts = new SkPDFDict();
-            fonts->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> fonts(new SkPDFDict());
             for (int i = 0; i < fFontResources.count(); i++) {
                 SkString nameString("F");
                 nameString.appendS32(i);
@@ -1013,8 +1086,7 @@
         }
 
         if (fShaderResources.count()) {
-            SkRefPtr<SkPDFDict> patterns = new SkPDFDict();
-            patterns->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFDict> patterns(new SkPDFDict());
             for (int i = 0; i < fShaderResources.count(); i++) {
                 SkString nameString("P");
                 nameString.appendS32(i);
@@ -1027,17 +1099,17 @@
         // For compatibility, add all proc sets (only used for output to PS
         // devices).
         const char procs[][7] = {"PDF", "Text", "ImageB", "ImageC", "ImageI"};
-        SkRefPtr<SkPDFArray> procSets = new SkPDFArray();
-        procSets->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFArray> procSets(new SkPDFArray());
         procSets->reserve(SK_ARRAY_COUNT(procs));
         for (size_t i = 0; i < SK_ARRAY_COUNT(procs); i++)
             procSets->appendName(procs[i]);
         fResourceDict->insert("ProcSet", procSets.get());
     }
-    return fResourceDict.get();
+    return fResourceDict;
 }
 
-void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList) const {
+void SkPDFDevice::getResources(SkTDArray<SkPDFObject*>* resourceList,
+                               bool recursive) const {
     resourceList->setReserve(resourceList->count() +
                              fGraphicStateResources.count() +
                              fXObjectResources.count() +
@@ -1046,22 +1118,30 @@
     for (int i = 0; i < fGraphicStateResources.count(); i++) {
         resourceList->push(fGraphicStateResources[i]);
         fGraphicStateResources[i]->ref();
-        fGraphicStateResources[i]->getResources(resourceList);
+        if (recursive) {
+            fGraphicStateResources[i]->getResources(resourceList);
+        }
     }
     for (int i = 0; i < fXObjectResources.count(); i++) {
         resourceList->push(fXObjectResources[i]);
         fXObjectResources[i]->ref();
-        fXObjectResources[i]->getResources(resourceList);
+        if (recursive) {
+            fXObjectResources[i]->getResources(resourceList);
+        }
     }
     for (int i = 0; i < fFontResources.count(); i++) {
         resourceList->push(fFontResources[i]);
         fFontResources[i]->ref();
-        fFontResources[i]->getResources(resourceList);
+        if (recursive) {
+            fFontResources[i]->getResources(resourceList);
+        }
     }
     for (int i = 0; i < fShaderResources.count(); i++) {
         resourceList->push(fShaderResources[i]);
         fShaderResources[i]->ref();
-        fShaderResources[i]->getResources(resourceList);
+        if (recursive) {
+            fShaderResources[i]->getResources(resourceList);
+        }
     }
 }
 
@@ -1069,12 +1149,11 @@
     return fFontResources;
 }
 
-SkRefPtr<SkPDFArray> SkPDFDevice::getMediaBox() const {
-    SkRefPtr<SkPDFInt> zero = new SkPDFInt(0);
-    zero->unref();  // SkRefPtr and new both took a reference.
+SkPDFArray* SkPDFDevice::copyMediaBox() const {
+    // should this be a singleton?
+    SkAutoTUnref<SkPDFInt> zero(SkNEW_ARGS(SkPDFInt, (0)));
 
-    SkRefPtr<SkPDFArray> mediaBox = new SkPDFArray();
-    mediaBox->unref();  // SkRefPtr and new both took a reference.
+    SkPDFArray* mediaBox = SkNEW(SkPDFArray);
     mediaBox->reserve(4);
     mediaBox->append(zero.get());
     mediaBox->append(zero.get());
@@ -1104,7 +1183,7 @@
         gsState.updateDrawingState(entry->fState);
 
         SkAutoDataUnref copy(entry->fContent.copyToData());
-        data->write(copy.data(), copy.size());
+        data->write(copy->data(), copy->size());
         entry = entry->fNext.get();
     }
     gsState.drainStack();
@@ -1128,7 +1207,8 @@
     // we have to clip to the content area; we've already applied the
     // initial transform, so just clip to the device size.
     if (fPageSize != fContentSize) {
-        SkRect r = SkRect::MakeWH(this->width(), this->height());
+        SkRect r = SkRect::MakeWH(SkIntToScalar(this->width()),
+                                  SkIntToScalar(this->height()));
         emit_clip(NULL, &r, &data);
     }
 
@@ -1139,15 +1219,62 @@
     return data.copyToData();
 }
 
-void SkPDFDevice::createFormXObjectFromDevice(
-        SkRefPtr<SkPDFFormXObject>* xobject) {
-    *xobject = new SkPDFFormXObject(this);
-    (*xobject)->unref();  // SkRefPtr and new both took a reference.
+bool SkPDFDevice::handleAnnotations(const SkRect& r, const SkMatrix& matrix,
+                                    const SkPaint& p) {
+    SkAnnotation* annotationInfo = p.getAnnotation();
+    if (!annotationInfo) {
+        return false;
+    }
+    SkData* urlData = annotationInfo->find(SkAnnotationKeys::URL_Key());
+    if (!urlData) {
+        return false;
+    }
+
+    SkString url(static_cast<const char *>(urlData->data()),
+                 urlData->size() - 1);
+    SkMatrix transform = matrix;
+    transform.postConcat(fInitialTransform);
+    SkRect translatedRect;
+    transform.mapRect(&translatedRect, r);
+
+    if (NULL == fAnnotations) {
+        fAnnotations = SkNEW(SkPDFArray);
+    }
+    SkAutoTUnref<SkPDFDict> annotation(new SkPDFDict("Annot"));
+    annotation->insertName("Subtype", "Link");
+    fAnnotations->append(annotation.get());
+
+    SkAutoTUnref<SkPDFArray> border(new 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());
+
+    SkAutoTUnref<SkPDFArray> rect(new SkPDFArray);
+    rect->reserve(4);
+    rect->appendScalar(translatedRect.fLeft);
+    rect->appendScalar(translatedRect.fTop);
+    rect->appendScalar(translatedRect.fRight);
+    rect->appendScalar(translatedRect.fBottom);
+    annotation->insert("Rect", rect.get());
+
+    SkAutoTUnref<SkPDFDict> action(new SkPDFDict("Action"));
+    action->insertName("S", "URI");
+    action->insert("URI", new SkPDFString(url))->unref();
+    annotation->insert("A", action.get());
+
+    return p.isNoDrawAnnotation();
+}
+
+SkPDFFormXObject* SkPDFDevice::createFormXObjectFromDevice() {
+    SkPDFFormXObject* xobject = SkNEW_ARGS(SkPDFFormXObject, (this));
     // We always draw the form xobjects that we create back into the device, so
     // we simply preserve the font usage instead of pulling it out and merging
     // it back in later.
     cleanUp(false);  // Reset this device to have no content.
     init();
+    return xobject;
 }
 
 void SkPDFDevice::clearClipFromContent(const SkClipStack* clipStack,
@@ -1155,11 +1282,10 @@
     if (clipRegion.isEmpty() || isContentEmpty()) {
         return;
     }
-    SkRefPtr<SkPDFFormXObject> curContent;
-    createFormXObjectFromDevice(&curContent);
+    SkAutoTUnref<SkPDFFormXObject> curContent(createFormXObjectFromDevice());
 
     // Redraw what we already had, but with the clip as a mask.
-    drawFormXObjectWithClip(curContent.get(), clipStack, clipRegion, true);
+    drawFormXObjectWithClip(curContent, clipStack, clipRegion, true);
 }
 
 void SkPDFDevice::drawFormXObjectWithClip(SkPDFFormXObject* xobject,
@@ -1179,12 +1305,9 @@
     draw.fClipStack = clipStack;
     SkPaint stockPaint;
     this->drawPaint(draw, stockPaint);
-    SkRefPtr<SkPDFFormXObject> maskFormXObject;
-    createFormXObjectFromDevice(&maskFormXObject);
-    SkRefPtr<SkPDFGraphicState> sMaskGS =
-        SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject.get(),
-                                                invertClip);
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    SkAutoTUnref<SkPDFFormXObject> maskFormXObject(createFormXObjectFromDevice());
+    SkAutoTUnref<SkPDFGraphicState> sMaskGS(
+        SkPDFGraphicState::GetSMaskGraphicState(maskFormXObject, invertClip));
 
     // Draw the xobject with the clip as a mask.
     ScopedContentEntry content(this, &fExistingClipStack, fExistingClipRegion,
@@ -1199,8 +1322,7 @@
     fXObjectResources.push(xobject);
     xobject->ref();
 
-    sMaskGS = SkPDFGraphicState::GetNoSMaskGraphicState();
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &content.entry()->fContent);
 }
@@ -1210,7 +1332,8 @@
                                              const SkMatrix& matrix,
                                              const SkPaint& paint,
                                              bool hasText,
-                                             SkRefPtr<SkPDFFormXObject>* dst) {
+                                             SkPDFFormXObject** dst) {
+    *dst = NULL;
     if (clipRegion.isEmpty()) {
         return NULL;
     }
@@ -1251,7 +1374,7 @@
         if (isContentEmpty()) {
             return NULL;
         } else {
-            createFormXObjectFromDevice(dst);
+            *dst = createFormXObjectFromDevice();
         }
     }
     // TODO(vandebo): Figure out how/if we can handle the following modes:
@@ -1314,9 +1437,9 @@
     SkClipStack clipStack = contentEntries->fState.fClipStack;
     SkRegion clipRegion = contentEntries->fState.fClipRegion;
 
-    SkRefPtr<SkPDFFormXObject> srcFormXObject;
+    SkAutoTUnref<SkPDFFormXObject> srcFormXObject;
     if (!isContentEmpty()) {
-        createFormXObjectFromDevice(&srcFormXObject);
+        srcFormXObject.reset(createFormXObjectFromDevice());
     }
 
     drawFormXObjectWithClip(dst, &clipStack, clipRegion, true);
@@ -1336,27 +1459,25 @@
         return;
     }
 
-    SkRefPtr<SkPDFGraphicState> sMaskGS;
+    SkAutoTUnref<SkPDFGraphicState> sMaskGS;
     if (xfermode == SkXfermode::kSrcIn_Mode ||
             xfermode == SkXfermode::kSrcOut_Mode) {
-        sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
-                dst, xfermode == SkXfermode::kSrcOut_Mode);
+        sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
+                dst, xfermode == SkXfermode::kSrcOut_Mode));
         fXObjectResources.push(srcFormXObject.get());
-        srcFormXObject->ref();
+        srcFormXObject.get()->ref();
     } else {
-        sMaskGS = SkPDFGraphicState::GetSMaskGraphicState(
-                srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode);
+        sMaskGS.reset(SkPDFGraphicState::GetSMaskGraphicState(
+                srcFormXObject.get(), xfermode == SkXfermode::kDstOut_Mode));
         // dst already added to fXObjectResources in drawFormXObjectWithClip.
     }
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &inClipContentEntry.entry()->fContent);
 
     SkPDFUtils::DrawFormXObject(fXObjectResources.count() - 1,
                                 &inClipContentEntry.entry()->fContent);
 
-    sMaskGS = SkPDFGraphicState::GetNoSMaskGraphicState();
-    sMaskGS->unref();  // SkRefPtr and getSMaskGraphicState both took a ref.
+    sMaskGS.reset(SkPDFGraphicState::GetNoSMaskGraphicState());
     SkPDFUtils::ApplyGraphicState(addGraphicStateResource(sMaskGS.get()),
                                   &inClipContentEntry.entry()->fContent);
 }
@@ -1385,9 +1506,11 @@
     entry->fMatrix = matrix;
     entry->fClipStack = clipStack;
     entry->fClipRegion = clipRegion;
+    entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
+    entry->fShaderIndex = -1;
 
     // PDF treats a shader as a color, so we only set one or the other.
-    SkRefPtr<SkPDFObject> pdfShader;
+    SkAutoTUnref<SkPDFObject> pdfShader;
     const SkShader* shader = paint.getShader();
     SkColor color = paint.getColor();
     if (shader) {
@@ -1399,8 +1522,15 @@
         // PDF doesn't support kClamp_TileMode, so we simulate it by making
         // a pattern the size of the current clip.
         SkIRect bounds = clipRegion.getBounds();
-        pdfShader = SkPDFShader::GetPDFShader(*shader, transform, bounds);
-        SkSafeUnref(pdfShader.get());  // getShader and SkRefPtr both took a ref
+
+        // We need to apply the initial transform to bounds in order to get
+        // bounds in a consistent coordinate system.
+        SkRect boundsTemp;
+        boundsTemp.set(bounds);
+        fInitialTransform.mapRect(&boundsTemp);
+        boundsTemp.roundOut(&bounds);
+
+        pdfShader.reset(SkPDFShader::GetPDFShader(*shader, transform, bounds));
 
         if (pdfShader.get()) {
             // pdfShader has been canonicalized so we can directly compare
@@ -1409,17 +1539,12 @@
             if (resourceIndex < 0) {
                 resourceIndex = fShaderResources.count();
                 fShaderResources.push(pdfShader.get());
-                pdfShader->ref();
+                pdfShader.get()->ref();
             }
             entry->fShaderIndex = resourceIndex;
         } else {
             // A color shader is treated as an invalid shader so we don't have
             // to set a shader just for a color.
-            entry->fShaderIndex = -1;
-            entry->fColor = 0;
-            color = 0;
-
-            // Check for a color shader.
             SkShader::GradientInfo gradientInfo;
             SkColor gradientColor;
             gradientInfo.fColors = &gradientColor;
@@ -1431,21 +1556,18 @@
                 color = gradientColor;
             }
         }
-    } else {
-        entry->fShaderIndex = -1;
-        entry->fColor = SkColorSetA(paint.getColor(), 0xFF);
-        color = paint.getColor();
     }
 
-    SkRefPtr<SkPDFGraphicState> newGraphicState;
+    SkAutoTUnref<SkPDFGraphicState> newGraphicState;
     if (color == paint.getColor()) {
-        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(paint);
+        newGraphicState.reset(
+                SkPDFGraphicState::GetGraphicStateForPaint(paint));
     } else {
         SkPaint newPaint = paint;
         newPaint.setColor(color);
-        newGraphicState = SkPDFGraphicState::GetGraphicStateForPaint(newPaint);
+        newGraphicState.reset(
+                SkPDFGraphicState::GetGraphicStateForPaint(newPaint));
     }
-    newGraphicState->unref();  // getGraphicState and SkRefPtr both took a ref.
     int resourceIndex = addGraphicStateResource(newGraphicState.get());
     entry->fGraphicStateIndex = resourceIndex;
 
@@ -1486,13 +1608,12 @@
 }
 
 int SkPDFDevice::getFontResourceIndex(SkTypeface* typeface, uint16_t glyphID) {
-    SkRefPtr<SkPDFFont> newFont = SkPDFFont::GetFontResource(typeface, glyphID);
-    newFont->unref();  // getFontResource and SkRefPtr both took a ref.
+    SkAutoTUnref<SkPDFFont> newFont(SkPDFFont::GetFontResource(typeface, glyphID));
     int resourceIndex = fFontResources.find(newFont.get());
     if (resourceIndex < 0) {
         resourceIndex = fFontResources.count();
         fFontResources.push(newFont.get());
-        newFont->ref();
+        newFont.get()->ref();
     }
     return resourceIndex;
 }
@@ -1539,4 +1660,3 @@
 bool SkPDFDevice::allowImageFilter(SkImageFilter*) {
     return false;
 }
-
diff --git a/src/pdf/SkPDFDocument.cpp b/src/pdf/SkPDFDocument.cpp
index cb87d8f..c7266d8 100644
--- a/src/pdf/SkPDFDocument.cpp
+++ b/src/pdf/SkPDFDocument.cpp
@@ -10,13 +10,14 @@
 #include "SkPDFCatalog.h"
 #include "SkPDFDevice.h"
 #include "SkPDFDocument.h"
-#include "SkPDFPage.h"
 #include "SkPDFFont.h"
+#include "SkPDFPage.h"
+#include "SkPDFTypes.h"
 #include "SkStream.h"
 
 // Add the resources, starting at firstIndex to the catalog, removing any dupes.
 // A hash table would be really nice here.
-void addResourcesToCatalog(int firstIndex, bool firstPage,
+static void addResourcesToCatalog(int firstIndex, bool firstPage,
                           SkTDArray<SkPDFObject*>* resourceList,
                           SkPDFCatalog* catalog) {
     for (int i = firstIndex; i < resourceList->count(); i++) {
@@ -56,11 +57,11 @@
 
 SkPDFDocument::SkPDFDocument(Flags flags)
         : fXRefFileOffset(0),
-          fSecondPageFirstResourceIndex(0) {
+          fSecondPageFirstResourceIndex(0),
+          fTrailerDict(NULL) {
     fCatalog.reset(new SkPDFCatalog(flags));
-    fDocCatalog = new SkPDFDict("Catalog");
-    fDocCatalog->unref();  // SkRefPtr and new both took a reference.
-    fCatalog->addObject(fDocCatalog.get(), true);
+    fDocCatalog = SkNEW_ARGS(SkPDFDict, ("Catalog"));
+    fCatalog->addObject(fDocCatalog, true);
 }
 
 SkPDFDocument::~SkPDFDocument() {
@@ -74,6 +75,9 @@
     fPageTree.safeUnrefAll();
     fPageResources.safeUnrefAll();
     fSubstitutes.safeUnrefAll();
+
+    fDocCatalog->unref();
+    SkSafeUnref(fTrailerDict);
 }
 
 bool SkPDFDocument::emitPDF(SkWStream* stream) {
@@ -94,13 +98,11 @@
         fDocCatalog->insert("Pages", new SkPDFObjRef(pageTreeRoot))->unref();
 
         /* TODO(vandebo): output intent
-        SkRefPtr<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
-        outputIntent->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFDict> outputIntent = new SkPDFDict("OutputIntent");
         outputIntent->insert("S", new SkPDFName("GTS_PDFA1"))->unref();
         outputIntent->insert("OutputConditionIdentifier",
                              new SkPDFString("sRGB"))->unref();
-        SkRefPtr<SkPDFArray> intentArray = new SkPDFArray;
-        intentArray->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFArray> intentArray = new SkPDFArray;
         intentArray->append(outputIntent.get());
         fDocCatalog->insert("OutputIntent", intentArray.get());
         */
@@ -122,9 +124,10 @@
 
         // Figure out the size of things and inform the catalog of file offsets.
         off_t fileOffset = headerSize();
-        fileOffset += fCatalog->setFileOffset(fDocCatalog.get(), fileOffset);
+        fileOffset += fCatalog->setFileOffset(fDocCatalog, fileOffset);
         fileOffset += fCatalog->setFileOffset(fPages[0], fileOffset);
-        fileOffset += fPages[0]->getPageSize(fCatalog.get(), fileOffset);
+        fileOffset += fPages[0]->getPageSize(fCatalog.get(),
+                (size_t) fileOffset);
         for (int i = 0; i < fSecondPageFirstResourceIndex; i++) {
             fileOffset += fCatalog->setFileOffset(fPageResources[i],
                                                   fileOffset);
@@ -222,8 +225,23 @@
     return true;
 }
 
-const SkTDArray<SkPDFPage*>& SkPDFDocument::getPages() {
-    return fPages;
+void SkPDFDocument::getCountOfFontTypes(
+        int counts[SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1]) const {
+    sk_bzero(counts, sizeof(int) *
+                     (SkAdvancedTypefaceMetrics::kNotEmbeddable_Font + 1));
+    SkTDArray<SkFontID> seenFonts;
+
+    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);
+            }
+        }
+    }
 }
 
 void SkPDFDocument::emitHeader(SkWStream* stream) {
@@ -241,15 +259,13 @@
 }
 
 void SkPDFDocument::emitFooter(SkWStream* stream, int64_t objCount) {
-    if (fTrailerDict.get() == NULL) {
-        fTrailerDict = new SkPDFDict();
-        fTrailerDict->unref();  // SkRefPtr and new both took a reference.
+    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", objCount);
-        fTrailerDict->insert("Root",
-                             new SkPDFObjRef(fDocCatalog.get()))->unref();
+        fTrailerDict->insertInt("Size", int(objCount));
+        fTrailerDict->insert("Root", new SkPDFObjRef(fDocCatalog))->unref();
     }
 
     stream->writeText("trailer\n");
diff --git a/src/pdf/SkPDFFont.cpp b/src/pdf/SkPDFFont.cpp
index 3aea4b8..89b4fc9 100644
--- a/src/pdf/SkPDFFont.cpp
+++ b/src/pdf/SkPDFFont.cpp
@@ -155,13 +155,12 @@
     // if the data was NUL terminated so that we can use strstr() to search it.
     // Make as few copies as possible given these constraints.
     SkDynamicMemoryWStream dynamicStream;
-    SkRefPtr<SkMemoryStream> staticStream;
+    SkAutoTUnref<SkMemoryStream> staticStream;
     SkData* data = NULL;
     const uint8_t* src;
     size_t srcLen;
     if ((srcLen = srcStream->getLength()) > 0) {
-        staticStream = new SkMemoryStream(srcLen + 1);
-        staticStream->unref();  // new and SkRefPtr both took a ref.
+        staticStream.reset(new SkMemoryStream(srcLen + 1));
         src = (const uint8_t*)staticStream->getMemoryBase();
         if (srcStream->getMemoryBase() != NULL) {
             memcpy((void *)src, srcStream->getMemoryBase(), srcLen);
@@ -318,8 +317,7 @@
                 break;
             }
             case SkAdvancedTypefaceMetrics::WidthRange::kRange: {
-                SkRefPtr<SkPDFArray> advanceArray = new SkPDFArray();
-                advanceArray->unref();  // SkRefPtr and new both took a ref.
+                SkAutoTUnref<SkPDFArray> advanceArray(new SkPDFArray());
                 for (int j = 0; j < advanceInfo->fAdvance.count(); j++)
                     appendAdvance(advanceInfo->fAdvance[j], emSize,
                                   advanceArray.get());
@@ -463,6 +461,13 @@
 // For the worst case (having 65536 continuous unicode and we use every other
 // one of them), the possible savings by aggressive optimization is 416KB
 // pre-compressed and does not provide enough motivation for implementation.
+
+// FIXME: this should be in a header so that it is separately testable
+// ( see caller in tests/ToUnicode.cpp )
+void append_cmap_sections(const SkTDArray<SkUnichar>& glyphToUnicode,
+                          const SkPDFGlyphSet* subset,
+                          SkDynamicMemoryWStream* cmap);
+
 void append_cmap_sections(const SkTDArray<SkUnichar>& glyphToUnicode,
                           const SkPDFGlyphSet* subset,
                           SkDynamicMemoryWStream* cmap) {
@@ -523,24 +528,24 @@
     append_tounicode_header(&cmap);
     append_cmap_sections(glyphToUnicode, subset, &cmap);
     append_cmap_footer(&cmap);
-    SkRefPtr<SkMemoryStream> cmapStream = new SkMemoryStream();
-    cmapStream->unref();  // SkRefPtr and new took a reference.
-    cmapStream->setData(cmap.copyToData());
+    SkAutoTUnref<SkMemoryStream> cmapStream(new SkMemoryStream());
+    cmapStream->setData(cmap.copyToData())->unref();
     return new SkPDFStream(cmapStream.get());
 }
 
+#if defined (SK_SFNTLY_SUBSETTER)
 static void sk_delete_array(const void* ptr, size_t, void*) {
     // Use C-style cast to cast away const and cast type simultaneously.
     delete[] (unsigned char*)ptr;
 }
+#endif
 
 static int get_subset_font_stream(const char* fontName,
                                   const SkTypeface* typeface,
                                   const SkTDArray<uint32_t>& subset,
                                   SkPDFStream** fontStream) {
-    SkRefPtr<SkStream> fontData =
-            SkFontHost::OpenStream(SkTypeface::UniqueID(typeface));
-    fontData->unref();  // SkRefPtr and OpenStream both took a ref.
+    SkAutoTUnref<SkStream> fontData(
+            SkFontHost::OpenStream(SkTypeface::UniqueID(typeface)));
 
     int fontSize = fontData->getLength();
 
@@ -692,9 +697,20 @@
 
 SkPDFFont::~SkPDFFont() {
     SkAutoMutexAcquire lock(CanonicalFontsMutex());
-    int index;
-    if (Find(SkTypeface::UniqueID(fTypeface.get()), fFirstGlyphID, &index) &&
-            CanonicalFonts()[index].fFont == this) {
+    int index = -1;
+    for (int i = 0 ; i < CanonicalFonts().count() ; i++) {
+        if (CanonicalFonts()[i].fFont == this) {
+            index = i;
+        }
+    }
+
+    SkDEBUGCODE(int indexFound;)
+    SkASSERT(index == -1 ||
+             (Find(SkTypeface::UniqueID(fTypeface.get()),
+                   fFirstGlyphID,
+                   &indexFound) &&
+             index == indexFound));
+    if (index >= 0) {
         CanonicalFonts().removeShuffle(index);
     }
     fResources.unrefAll();
@@ -746,12 +762,26 @@
         return CanonicalFonts()[relatedFontIndex].fFont;
     }
 
-    SkRefPtr<SkAdvancedTypefaceMetrics> fontMetrics;
+    SkAutoTUnref<SkAdvancedTypefaceMetrics> fontMetrics;
     SkPDFDict* relatedFontDescriptor = NULL;
     if (relatedFontIndex >= 0) {
         SkPDFFont* relatedFont = CanonicalFonts()[relatedFontIndex].fFont;
-        fontMetrics = relatedFont->fontInfo();
+        fontMetrics.reset(relatedFont->fontInfo());
+        SkSafeRef(fontMetrics.get());
         relatedFontDescriptor = relatedFont->getFontDescriptor();
+
+        // This only is to catch callers who pass invalid glyph ids.
+        // If glyph id is invalid, then we will create duplicate entries
+        // for True Type fonts.
+        SkAdvancedTypefaceMetrics::FontType fontType =
+            fontMetrics.get() ? fontMetrics.get()->fType :
+                                SkAdvancedTypefaceMetrics::kOther_Font;
+
+        if (fontType == SkAdvancedTypefaceMetrics::kType1CID_Font ||
+            fontType == SkAdvancedTypefaceMetrics::kTrueType_Font) {
+            CanonicalFonts()[relatedFontIndex].fFont->ref();
+            return CanonicalFonts()[relatedFontIndex].fFont;
+        }
     } else {
         SkAdvancedTypefaceMetrics::PerGlyphInfo info;
         info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo;
@@ -761,19 +791,16 @@
         info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
                   info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
 #endif
-        fontMetrics =
-            SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0);
+        fontMetrics.reset(
+            SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0));
 #if defined (SK_SFNTLY_SUBSETTER)
-        SkASSERT(fontMetrics);
-        SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref.
-        if (fontMetrics &&
+        if (fontMetrics.get() &&
             fontMetrics->fType != SkAdvancedTypefaceMetrics::kTrueType_Font) {
             // Font does not support subsetting, get new info with advance.
             info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
                       info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
-            fontMetrics =
-                SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0);
-            SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref
+            fontMetrics.reset(
+                SkFontHost::GetAdvancedTypefaceMetrics(fontID, info, NULL, 0));
         }
 #endif
     }
@@ -824,6 +851,8 @@
           fFirstGlyphID(1),
           fLastGlyphID(info ? info->fLastGlyphID : 0),
           fFontInfo(info) {
+    SkSafeRef(typeface);
+    SkSafeRef(info);
     if (info == NULL) {
         fFontType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
     } else if (info->fMultiMaster) {
@@ -874,7 +903,8 @@
     if (info == NULL || info == fFontInfo.get()) {
         return;
     }
-    fFontInfo = info;
+    fFontInfo.reset(info);
+    SkSafeRef(info);
 }
 
 uint16_t SkPDFFont::firstGlyphID() const {
@@ -892,6 +922,7 @@
 void SkPDFFont::addResource(SkPDFObject* object) {
     SkASSERT(object != NULL);
     fResources.push(object);
+    object->ref();
 }
 
 SkPDFDict* SkPDFFont::getFontDescriptor() {
@@ -899,7 +930,8 @@
 }
 
 void SkPDFFont::setFontDescriptor(SkPDFDict* descriptor) {
-    fDescriptor = descriptor;
+    fDescriptor.reset(descriptor);
+    SkSafeRef(descriptor);
 }
 
 bool SkPDFFont::addCommonFontDescriptorEntries(int16_t defaultWidth) {
@@ -970,9 +1002,9 @@
     if (fFontInfo == NULL || fFontInfo->fGlyphToUnicode.begin() == NULL) {
         return;
     }
-    SkRefPtr<SkPDFStream> pdfCmap =
-        generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset);
-    addResource(pdfCmap.get());  // Pass reference from new.
+    SkAutoTUnref<SkPDFStream> pdfCmap(
+        generate_tounicode_cmap(fFontInfo->fGlyphToUnicode, subset));
+    addResource(pdfCmap.get());
     insert("ToUnicode", new SkPDFObjRef(pdfCmap.get()))->unref();
 }
 
@@ -1007,14 +1039,11 @@
     insertName("BaseFont", fontInfo()->fFontName);
     insertName("Encoding", "Identity-H");
 
-    SkPDFCIDFont* newCIDFont;
-    newCIDFont = new SkPDFCIDFont(fontInfo(), typeface(), subset);
-
-    // Pass ref new created to fResources.
-    addResource(newCIDFont);
-    SkRefPtr<SkPDFArray> descendantFonts = new SkPDFArray();
-    descendantFonts->unref();  // SkRefPtr and new took a reference.
-    descendantFonts->append(new SkPDFObjRef(newCIDFont))->unref();
+    SkAutoTUnref<SkPDFCIDFont> newCIDFont(
+            new SkPDFCIDFont(fontInfo(), typeface(), subset));
+    addResource(newCIDFont.get());
+    SkAutoTUnref<SkPDFArray> descendantFonts(new SkPDFArray());
+    descendantFonts->append(new SkPDFObjRef(newCIDFont.get()))->unref();
     insert("DescendantFonts", descendantFonts.get());
 
     populateToUnicodeTable(subset);
@@ -1037,9 +1066,9 @@
 
 bool SkPDFCIDFont::addFontDescriptor(int16_t defaultWidth,
                                      const SkTDArray<uint32_t>* subset) {
-    SkRefPtr<SkPDFDict> descriptor = new SkPDFDict("FontDescriptor");
-    descriptor->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFDict> descriptor(new SkPDFDict("FontDescriptor"));
     setFontDescriptor(descriptor.get());
+    addResource(descriptor.get());
 
     switch (getType()) {
         case SkAdvancedTypefaceMetrics::kTrueType_Font: {
@@ -1052,22 +1081,20 @@
                                                   &rawStream);
             SkASSERT(fontSize);
             SkASSERT(rawStream);
-            SkRefPtr<SkPDFStream> fontStream = rawStream;
-            // SkRefPtr and new both ref()'d fontStream, pass one.
+            SkAutoTUnref<SkPDFStream> fontStream(rawStream);
             addResource(fontStream.get());
 
             fontStream->insertInt("Length1", fontSize);
             descriptor->insert("FontFile2",
-                                new SkPDFObjRef(fontStream.get()))->unref();
+                               new SkPDFObjRef(fontStream.get()))->unref();
             break;
         }
         case SkAdvancedTypefaceMetrics::kCFF_Font:
         case SkAdvancedTypefaceMetrics::kType1CID_Font: {
-            SkRefPtr<SkStream> fontData =
-                SkFontHost::OpenStream(SkTypeface::UniqueID(typeface()));
-            fontData->unref();  // SkRefPtr and OpenStream both took a ref.
-            SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData.get());
-            // SkRefPtr and new both ref()'d fontStream, pass one.
+            SkAutoTUnref<SkStream> fontData(
+                SkFontHost::OpenStream(SkTypeface::UniqueID(typeface())));
+            SkAutoTUnref<SkPDFStream> fontStream(
+                new SkPDFStream(fontData.get()));
             addResource(fontStream.get());
 
             if (getType() == SkAdvancedTypefaceMetrics::kCFF_Font) {
@@ -1083,9 +1110,6 @@
             SkASSERT(false);
     }
 
-    addResource(descriptor.get());
-    descriptor->ref();
-
     insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
     return addCommonFontDescriptorEntries(defaultWidth);
 }
@@ -1100,20 +1124,18 @@
             subset->exportTo(&glyphIDs);
         }
 
-        SkRefPtr<SkAdvancedTypefaceMetrics> fontMetrics;
         SkAdvancedTypefaceMetrics::PerGlyphInfo info;
         info = SkAdvancedTypefaceMetrics::kGlyphNames_PerGlyphInfo;
         info = SkTBitOr<SkAdvancedTypefaceMetrics::PerGlyphInfo>(
                   info, SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo);
         uint32_t* glyphs = (glyphIDs.count() == 1) ? NULL : glyphIDs.begin();
         uint32_t glyphsCount = glyphs ? glyphIDs.count() : 0;
-        fontMetrics =
+        SkAutoTUnref<SkAdvancedTypefaceMetrics> fontMetrics(
             SkFontHost::GetAdvancedTypefaceMetrics(
                     SkTypeface::UniqueID(typeface()),
                     info,
                     glyphs,
-                    glyphsCount);
-        SkSafeUnref(fontMetrics.get());  // SkRefPtr and Get both took a ref
+                    glyphsCount));
         setFontInfo(fontMetrics.get());
         addFontDescriptor(0, &glyphIDs);
     } else {
@@ -1132,8 +1154,7 @@
         SkASSERT(false);
     }
 
-    SkRefPtr<SkPDFDict> sysInfo = new SkPDFDict;
-    sysInfo->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> sysInfo(new SkPDFDict);
     sysInfo->insert("Registry", new SkPDFString("Adobe"))->unref();
     sysInfo->insert("Ordering", new SkPDFString("Identity"))->unref();
     sysInfo->insertInt("Supplement", 0);
@@ -1141,11 +1162,10 @@
 
     if (fontInfo()->fGlyphWidths.get()) {
         int16_t defaultWidth = 0;
-        SkRefPtr<SkPDFArray> widths =
+        SkAutoTUnref<SkPDFArray> widths(
             composeAdvanceData(fontInfo()->fGlyphWidths.get(),
                                fontInfo()->fEmSize, &appendWidth,
-                               &defaultWidth);
-        widths->unref();  // SkRefPtr and compose both took a reference.
+                               &defaultWidth));
         if (widths->size())
             insert("W", widths.get());
         if (defaultWidth != 0) {
@@ -1158,11 +1178,10 @@
         defaultAdvance.fVerticalAdvance = 0;
         defaultAdvance.fOriginXDisp = 0;
         defaultAdvance.fOriginYDisp = 0;
-        SkRefPtr<SkPDFArray> advances =
+        SkAutoTUnref<SkPDFArray> advances(
             composeAdvanceData(fontInfo()->fVerticalMetrics.get(),
                                fontInfo()->fEmSize, &appendVerticalAdvance,
-                               &defaultAdvance);
-        advances->unref();  // SkRefPtr and compose both took a ref.
+                               &defaultAdvance));
         if (advances->size())
             insert("W2", advances.get());
         if (defaultAdvance.fVerticalAdvance ||
@@ -1192,31 +1211,27 @@
 SkPDFType1Font::~SkPDFType1Font() {}
 
 bool SkPDFType1Font::addFontDescriptor(int16_t defaultWidth) {
-    SkRefPtr<SkPDFDict> descriptor = getFontDescriptor();
-    if (descriptor.get() != NULL) {
-        addResource(descriptor.get());
-        descriptor->ref();
-        insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
+    if (getFontDescriptor() != NULL) {
+        SkPDFDict* descriptor = getFontDescriptor();
+        addResource(descriptor);
+        insert("FontDescriptor", new SkPDFObjRef(descriptor))->unref();
         return true;
     }
 
-    descriptor = new SkPDFDict("FontDescriptor");
-    descriptor->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFDict> descriptor(new SkPDFDict("FontDescriptor"));
     setFontDescriptor(descriptor.get());
 
     size_t header SK_INIT_TO_AVOID_WARNING;
     size_t data SK_INIT_TO_AVOID_WARNING;
     size_t trailer SK_INIT_TO_AVOID_WARNING;
-    SkRefPtr<SkStream> rawFontData =
-        SkFontHost::OpenStream(SkTypeface::UniqueID(typeface()));
-    rawFontData->unref();  // SkRefPtr and OpenStream both took a ref.
+    SkAutoTUnref<SkStream> rawFontData(
+        SkFontHost::OpenStream(SkTypeface::UniqueID(typeface())));
     SkStream* fontData = handleType1Stream(rawFontData.get(), &header, &data,
                                            &trailer);
     if (fontData == NULL) {
         return false;
     }
-    SkRefPtr<SkPDFStream> fontStream = new SkPDFStream(fontData);
-    // SkRefPtr and new both ref()'d fontStream, pass one.
+    SkAutoTUnref<SkPDFStream> fontStream(new SkPDFStream(fontData));
     addResource(fontStream.get());
     fontStream->insertInt("Length1", header);
     fontStream->insertInt("Length2", data);
@@ -1224,7 +1239,6 @@
     descriptor->insert("FontFile", new SkPDFObjRef(fontStream.get()))->unref();
 
     addResource(descriptor.get());
-    descriptor->ref();
     insert("FontDescriptor", new SkPDFObjRef(descriptor.get()))->unref();
 
     return addCommonFontDescriptorEntries(defaultWidth);
@@ -1265,12 +1279,10 @@
 
     addWidthInfoFromRange(defaultWidth, widthRangeEntry);
 
-    SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
-    encoding->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> encoding(new SkPDFDict("Encoding"));
     insert("Encoding", encoding.get());
 
-    SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
-    encDiffs->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> encDiffs(new SkPDFArray);
     encoding->insert("Differences", encDiffs.get());
 
     encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
@@ -1285,8 +1297,7 @@
 void SkPDFType1Font::addWidthInfoFromRange(
         int16_t defaultWidth,
         const SkAdvancedTypefaceMetrics::WidthRange* widthRangeEntry) {
-    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
-    widthArray->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFArray> widthArray(new SkPDFArray());
     int firstChar = 0;
     if (widthRangeEntry) {
         const uint16_t emSize = fontInfo()->fEmSize;
@@ -1330,7 +1341,7 @@
     SkPaint paint;
     paint.setTypeface(typeface());
     paint.setTextSize(1000);
-    SkAutoGlyphCache autoCache(paint, NULL);
+    SkAutoGlyphCache autoCache(paint, NULL, NULL);
     SkGlyphCache* cache = autoCache.getCache();
     // If fLastGlyphID isn't set (because there is not fFontInfo), look it up.
     if (lastGlyphID() == 0) {
@@ -1345,22 +1356,18 @@
     fontMatrix.setScale(SkScalarInvert(1000), -SkScalarInvert(1000));
     insert("FontMatrix", SkPDFUtils::MatrixToArray(fontMatrix))->unref();
 
-    SkRefPtr<SkPDFDict> charProcs = new SkPDFDict;
-    charProcs->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> charProcs(new SkPDFDict);
     insert("CharProcs", charProcs.get());
 
-    SkRefPtr<SkPDFDict> encoding = new SkPDFDict("Encoding");
-    encoding->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> encoding(new SkPDFDict("Encoding"));
     insert("Encoding", encoding.get());
 
-    SkRefPtr<SkPDFArray> encDiffs = new SkPDFArray;
-    encDiffs->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> encDiffs(new SkPDFArray);
     encoding->insert("Differences", encDiffs.get());
     encDiffs->reserve(lastGlyphID() - firstGlyphID() + 2);
     encDiffs->appendInt(1);
 
-    SkRefPtr<SkPDFArray> widthArray = new SkPDFArray();
-    widthArray->unref();  // SkRefPtr and new both took a ref.
+    SkAutoTUnref<SkPDFArray> widthArray(new SkPDFArray());
 
     SkIRect bbox = SkIRect::MakeEmpty();
     for (int gID = firstGlyphID(); gID <= lastGlyphID(); gID++) {
@@ -1379,17 +1386,15 @@
                                     &content);
         const SkPath* path = cache->findPath(glyph);
         if (path) {
-            SkPDFUtils::EmitPath(*path, &content);
+            SkPDFUtils::EmitPath(*path, paint.getStyle(), &content);
             SkPDFUtils::PaintPath(paint.getStyle(), path->getFillType(),
                                   &content);
         }
-        SkRefPtr<SkMemoryStream> glyphStream = new SkMemoryStream();
-        glyphStream->unref();  // SkRefPtr and new both took a ref.
+        SkAutoTUnref<SkMemoryStream> glyphStream(new SkMemoryStream());
         glyphStream->setData(content.copyToData())->unref();
 
-        SkRefPtr<SkPDFStream> glyphDescription =
-            new SkPDFStream(glyphStream.get());
-        // SkRefPtr and new both ref()'d charProcs, pass one.
+        SkAutoTUnref<SkPDFStream> glyphDescription(
+            new SkPDFStream(glyphStream.get()));
         addResource(glyphDescription.get());
         charProcs->insert(characterName.c_str(),
                           new SkPDFObjRef(glyphDescription.get()))->unref();
diff --git a/src/pdf/SkPDFFont.h b/src/pdf/SkPDFFont.h
new file mode 100644
index 0000000..693b911
--- /dev/null
+++ b/src/pdf/SkPDFFont.h
@@ -0,0 +1,202 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkPDFFont_DEFINED
+#define SkPDFFont_DEFINED
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkBitSet.h"
+#include "SkPDFTypes.h"
+#include "SkTDArray.h"
+#include "SkThread.h"
+#include "SkTypeface.h"
+
+class SkPaint;
+class SkPDFCatalog;
+class SkPDFFont;
+
+class SkPDFGlyphSet : public SkNoncopyable {
+public:
+    SkPDFGlyphSet();
+
+    void set(const uint16_t* glyphIDs, int numGlyphs);
+    bool has(uint16_t glyphID) const;
+    void merge(const SkPDFGlyphSet& usage);
+    void exportTo(SkTDArray<uint32_t>* glyphIDs) const;
+
+private:
+    SkBitSet fBitSet;
+};
+
+class SkPDFGlyphSetMap : public SkNoncopyable {
+public:
+    struct FontGlyphSetPair {
+        FontGlyphSetPair(SkPDFFont* font, SkPDFGlyphSet* glyphSet);
+
+        SkPDFFont* fFont;
+        SkPDFGlyphSet* fGlyphSet;
+    };
+
+    SkPDFGlyphSetMap();
+    ~SkPDFGlyphSetMap();
+
+    class F2BIter {
+    public:
+        explicit F2BIter(const SkPDFGlyphSetMap& map);
+        FontGlyphSetPair* next() const;
+        void reset(const SkPDFGlyphSetMap& map);
+
+    private:
+        const SkTDArray<FontGlyphSetPair>* fMap;
+        mutable int fIndex;
+    };
+
+    void merge(const SkPDFGlyphSetMap& usage);
+    void reset();
+
+    void noteGlyphUsage(SkPDFFont* font, const uint16_t* glyphIDs,
+                        int numGlyphs);
+
+private:
+    SkPDFGlyphSet* getGlyphSetForFont(SkPDFFont* font);
+
+    SkTDArray<FontGlyphSetPair> fMap;
+};
+
+
+/** \class SkPDFFont
+    A PDF Object class representing a font.  The font may have resources
+    attached to it in order to embed the font.  SkPDFFonts are canonicalized
+    so that resource deduplication will only include one copy of a font.
+    This class uses the same pattern as SkPDFGraphicState, a static weak
+    reference to each instantiated class.
+*/
+class SkPDFFont : public SkPDFDict {
+public:
+    virtual ~SkPDFFont();
+
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+    /** Returns the typeface represented by this class. Returns NULL for the
+     *  default typeface.
+     */
+    SkTypeface* typeface();
+
+    /** Returns the font type represented in this font.  For Type0 fonts,
+     *  returns the type of the decendant font.
+     */
+    virtual SkAdvancedTypefaceMetrics::FontType getType();
+
+    /** Returns true if this font encoding supports glyph IDs above 255.
+     */
+    virtual bool multiByteGlyphs() const = 0;
+
+    /** Return true if this font has an encoding for the passed glyph id.
+     */
+    bool hasGlyph(uint16_t glyphID);
+
+    /** Convert (in place) the input glyph IDs into the font encoding.  If the
+     *  font has more glyphs than can be encoded (like a type 1 font with more
+     *  than 255 glyphs) this method only converts up to the first out of range
+     *  glyph ID.
+     *  @param glyphIDs       The input text as glyph IDs.
+     *  @param numGlyphs      The number of input glyphs.
+     *  @return               Returns the number of glyphs consumed.
+     */
+    size_t glyphsToPDFFontEncoding(uint16_t* glyphIDs, size_t numGlyphs);
+
+    /** Get the font resource for the passed typeface and glyphID. 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.
+     *  @param typeface  The typeface to find.
+     *  @param glyphID   Specify which section of a large font is of interest.
+     */
+    static SkPDFFont* GetFontResource(SkTypeface* typeface,
+                                             uint16_t glyphID);
+
+    /** Subset the font based on usage set. Returns a SkPDFFont instance with
+     *  subset.
+     *  @param usage  Glyph subset requested.
+     *  @return       NULL if font does not support subsetting, a new instance
+     *                of SkPDFFont otherwise.
+     */
+    virtual SkPDFFont* getFontSubset(const SkPDFGlyphSet* usage);
+
+protected:
+    // Common constructor to handle common members.
+    SkPDFFont(SkAdvancedTypefaceMetrics* fontInfo, SkTypeface* typeface,
+              uint16_t glyphID, bool descendantFont);
+
+    // Accessors for subclass.
+    SkAdvancedTypefaceMetrics* fontInfo();
+    void setFontInfo(SkAdvancedTypefaceMetrics* info);
+    uint16_t firstGlyphID() const;
+    uint16_t lastGlyphID() const;
+    void setLastGlyphID(uint16_t glyphID);
+
+    // Add object to resource list.
+    void addResource(SkPDFObject* object);
+
+    // Accessors for FontDescriptor associated with this object.
+    SkPDFDict* getFontDescriptor();
+    void setFontDescriptor(SkPDFDict* descriptor);
+
+    // Add common entries to FontDescriptor.
+    bool addCommonFontDescriptorEntries(int16_t defaultWidth);
+
+    /** Set fFirstGlyphID and fLastGlyphID to span at most 255 glyphs,
+     *  including the passed glyphID.
+     */
+    void adjustGlyphRangeForSingleByteEncoding(int16_t glyphID);
+
+    // Generate ToUnicode table according to glyph usage subset.
+    // If subset is NULL, all available glyph ids will be used.
+    void populateToUnicodeTable(const SkPDFGlyphSet* subset);
+
+    // Create instances of derived types based on fontInfo.
+    static SkPDFFont* Create(SkAdvancedTypefaceMetrics* fontInfo,
+                             SkTypeface* typeface, uint16_t glyphID,
+                             SkPDFDict* fontDescriptor);
+
+    static bool Find(uint32_t fontID, uint16_t glyphID, int* index);
+
+private:
+    class FontRec {
+    public:
+        SkPDFFont* fFont;
+        uint32_t fFontID;
+        uint16_t fGlyphID;
+
+        // A fGlyphID of 0 with no fFont always matches.
+        bool operator==(const FontRec& b) const;
+        FontRec(SkPDFFont* font, uint32_t fontID, uint16_t fGlyphID);
+    };
+
+    SkAutoTUnref<SkTypeface> fTypeface;
+
+    // The glyph IDs accessible with this font.  For Type1 (non CID) fonts,
+    // this will be a subset if the font has more than 255 glyphs.
+    uint16_t fFirstGlyphID;
+    uint16_t fLastGlyphID;
+    // The font info is only kept around after construction for large
+    // Type1 (non CID) fonts that need multiple "fonts" to access all glyphs.
+    SkAutoTUnref<SkAdvancedTypefaceMetrics> fFontInfo;
+    SkTDArray<SkPDFObject*> fResources;
+    SkAutoTUnref<SkPDFDict> fDescriptor;
+
+    SkAdvancedTypefaceMetrics::FontType fFontType;
+
+    // This should be made a hash table if performance is a problem.
+    static SkTDArray<FontRec>& CanonicalFonts();
+    static SkBaseMutex& CanonicalFontsMutex();
+};
+
+#endif
diff --git a/src/pdf/SkPDFFormXObject.cpp b/src/pdf/SkPDFFormXObject.cpp
index 5ac93e4..5e33995 100644
--- a/src/pdf/SkPDFFormXObject.cpp
+++ b/src/pdf/SkPDFFormXObject.cpp
@@ -20,15 +20,23 @@
     // 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).
-    device->getResources(&fResources);
+    device->getResources(&fResources, false);
 
-    SkRefPtr<SkStream> content = device->content();
-    content->unref();  // SkRefPtr and content() both took a reference.
+    // Fail fast if in the tree of resources a child references a parent.
+    // If there is an issue, getResources will end up consuming all memory.
+    // TODO: A better approach might be for all SkPDFObject to keep track
+    // of possible cycles.
+#ifdef SK_DEBUG
+    SkTDArray<SkPDFObject*> dummy_resourceList;
+    getResources(&dummy_resourceList);
+#endif
+
+    SkAutoTUnref<SkStream> content(device->content());
     setData(content.get());
 
     insertName("Type", "XObject");
     insertName("Subtype", "Form");
-    insert("BBox", device->getMediaBox().get());
+    SkSafeUnref(this->insert("BBox", device->copyMediaBox()));
     insert("Resources", device->getResourceDict());
 
     // We invert the initial transform and apply that to the xobject so that
@@ -36,15 +44,17 @@
     // embedded in things like shaders and images.
     if (!device->initialTransform().isIdentity()) {
         SkMatrix inverse;
-        inverse.reset();
-        device->initialTransform().invert(&inverse);
+        if (!device->initialTransform().invert(&inverse)) {
+            // The initial transform should be invertible.
+            SkASSERT(false);
+            inverse.reset();
+        }
         insert("Matrix", SkPDFUtils::MatrixToArray(inverse))->unref();
     }
 
     // Right now SkPDFFormXObject is only used for saveLayer, which implies
     // isolated blending.  Do this conditionally if that changes.
-    SkRefPtr<SkPDFDict> group = new SkPDFDict("Group");
-    group->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> group(new SkPDFDict("Group"));
     group->insertName("S", "Transparency");
     group->insert("I", new SkPDFBool(true))->unref();  // Isolated.
     insert("Group", group.get());
diff --git a/include/pdf/SkPDFFormXObject.h b/src/pdf/SkPDFFormXObject.h
similarity index 100%
rename from include/pdf/SkPDFFormXObject.h
rename to src/pdf/SkPDFFormXObject.h
diff --git a/src/pdf/SkPDFGraphicState.cpp b/src/pdf/SkPDFGraphicState.cpp
index ec9b0e7..ea8621f 100644
--- a/src/pdf/SkPDFGraphicState.cpp
+++ b/src/pdf/SkPDFGraphicState.cpp
@@ -16,7 +16,7 @@
 static const char* blend_mode_from_xfermode(SkXfermode::Mode mode) {
     switch (mode) {
         case SkXfermode::kSrcOver_Mode:    return "Normal";
-        case SkXfermode::kMultiply_Mode:   return "Multiply";
+        case SkXfermode::kModulate_Mode:   return "Multiply";
         case SkXfermode::kScreen_Mode:     return "Screen";
         case SkXfermode::kOverlay_Mode:    return "Overlay";
         case SkXfermode::kDarken_Mode:     return "Darken";
@@ -113,16 +113,14 @@
     if (!invertFunction) {
         // Acrobat crashes if we use a type 0 function, kpdf crashes if we use
         // a type 2 function, so we use a type 4 function.
-        SkRefPtr<SkPDFArray> domainAndRange = new SkPDFArray;
-        domainAndRange->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFArray> domainAndRange(new SkPDFArray);
         domainAndRange->reserve(2);
         domainAndRange->appendInt(0);
         domainAndRange->appendInt(1);
 
         static const char psInvert[] = "{1 exch sub}";
-        SkRefPtr<SkMemoryStream> psInvertStream =
-            new SkMemoryStream(&psInvert, strlen(psInvert), true);
-        psInvertStream->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkMemoryStream> psInvertStream(
+            new SkMemoryStream(&psInvert, strlen(psInvert), true));
 
         invertFunction = new SkPDFStream(psInvertStream.get());
         invertFunction->insertInt("FunctionType", 4);
@@ -139,8 +137,7 @@
     // enough that it's not worth canonicalizing.
     SkAutoMutexAcquire lock(CanonicalPaintsMutex());
 
-    SkRefPtr<SkPDFDict> sMaskDict = new SkPDFDict("Mask");
-    sMaskDict->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> sMaskDict(new SkPDFDict("Mask"));
     sMaskDict->insertName("S", "Alpha");
     sMaskDict->insert("G", new SkPDFObjRef(sMask))->unref();
 
@@ -200,9 +197,8 @@
         fPopulated = true;
         insertName("Type", "ExtGState");
 
-        SkRefPtr<SkPDFScalar> alpha =
-            new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF));
-        alpha->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFScalar> alpha(
+            new SkPDFScalar(SkScalarDiv(fPaint.getAlpha(), 0xFF)));
         insert("CA", alpha.get());
         insert("ca", alpha.get());
 
diff --git a/include/pdf/SkPDFGraphicState.h b/src/pdf/SkPDFGraphicState.h
similarity index 100%
rename from include/pdf/SkPDFGraphicState.h
rename to src/pdf/SkPDFGraphicState.h
diff --git a/src/pdf/SkPDFImage.cpp b/src/pdf/SkPDFImage.cpp
index 72379b1..1b93f6e 100644
--- a/src/pdf/SkPDFImage.cpp
+++ b/src/pdf/SkPDFImage.cpp
@@ -305,8 +305,7 @@
     if (!doingAlpha && alphaOnly) {
         // For alpha only images, we stretch a single pixel of black for
         // the color/shape part.
-        SkRefPtr<SkPDFInt> one = new SkPDFInt(1);
-        one->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFInt> one(new SkPDFInt(1));
         insert("Width", one.get());
         insert("Height", one.get());
     } else {
@@ -335,16 +334,12 @@
     insertInt("BitsPerComponent", bitsPerComp);
 
     if (config == SkBitmap::kRGB_565_Config) {
-        SkRefPtr<SkPDFInt> zeroVal = new SkPDFInt(0);
-        zeroVal->unref();  // SkRefPtr and new both took a reference.
-        SkRefPtr<SkPDFScalar> scale5Val =
-                new SkPDFScalar(8.2258f);  // 255/2^5-1
-        scale5Val->unref();  // SkRefPtr and new both took a reference.
-        SkRefPtr<SkPDFScalar> scale6Val =
-                new SkPDFScalar(4.0476f);  // 255/2^6-1
-        scale6Val->unref();  // SkRefPtr and new both took a reference.
-        SkRefPtr<SkPDFArray> decodeValue = new SkPDFArray();
-        decodeValue->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFInt> zeroVal(new SkPDFInt(0));
+        SkAutoTUnref<SkPDFScalar> scale5Val(
+                new SkPDFScalar(SkFloatToScalar(8.2258f)));  // 255/2^5-1
+        SkAutoTUnref<SkPDFScalar> scale6Val(
+                new SkPDFScalar(SkFloatToScalar(4.0476f)));  // 255/2^6-1
+        SkAutoTUnref<SkPDFArray> decodeValue(new SkPDFArray());
         decodeValue->reserve(6);
         decodeValue->append(zeroVal.get());
         decodeValue->append(scale5Val.get());
diff --git a/include/pdf/SkPDFImage.h b/src/pdf/SkPDFImage.h
similarity index 100%
rename from include/pdf/SkPDFImage.h
rename to src/pdf/SkPDFImage.h
diff --git a/src/pdf/SkPDFPage.cpp b/src/pdf/SkPDFPage.cpp
index 136ef44..f47f8ff 100644
--- a/src/pdf/SkPDFPage.cpp
+++ b/src/pdf/SkPDFPage.cpp
@@ -15,6 +15,7 @@
 SkPDFPage::SkPDFPage(SkPDFDevice* content)
     : SkPDFDict("Page"),
       fDevice(content) {
+  SkSafeRef(content);
 }
 
 SkPDFPage::~SkPDFPage() {}
@@ -23,16 +24,21 @@
                              SkTDArray<SkPDFObject*>* resourceObjects) {
     if (fContentStream.get() == NULL) {
         insert("Resources", fDevice->getResourceDict());
-        insert("MediaBox", fDevice->getMediaBox().get());
+        SkSafeUnref(this->insert("MediaBox", fDevice->copyMediaBox()));
+        if (!SkToBool(catalog->getDocumentFlags() &
+                      SkPDFDocument::kNoLinks_Flags)) {
+            SkPDFArray* annots = fDevice->getAnnotations();
+            if (annots && annots->size() > 0) {
+                insert("Annots", annots);
+            }
+        }
 
-        SkRefPtr<SkStream> content = fDevice->content();
-        content->unref();  // SkRefPtr and content() both took a reference.
-        fContentStream = new SkPDFStream(content.get());
-        fContentStream->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkStream> content(fDevice->content());
+        fContentStream.reset(new SkPDFStream(content.get()));
         insert("Contents", new SkPDFObjRef(fContentStream.get()))->unref();
     }
     catalog->addObject(fContentStream.get(), firstPage);
-    fDevice->getResources(resourceObjects);
+    fDevice->getResources(resourceObjects, true);
 }
 
 off_t SkPDFPage::getPageSize(SkPDFCatalog* catalog, off_t fileOffset) {
@@ -60,12 +66,9 @@
     // one child.
     static const int kNodeSize = 8;
 
-    SkRefPtr<SkPDFName> kidsName = new SkPDFName("Kids");
-    kidsName->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFName> countName = new SkPDFName("Count");
-    countName->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFName> parentName = new SkPDFName("Parent");
-    parentName->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> kidsName(new SkPDFName("Kids"));
+    SkAutoTUnref<SkPDFName> countName(new SkPDFName("Count"));
+    SkAutoTUnref<SkPDFName> parentName(new SkPDFName("Parent"));
 
     // curNodes takes a reference to its items, which it passes to pageTree.
     SkTDArray<SkPDFDict*> curNodes;
@@ -88,11 +91,9 @@
             }
 
             SkPDFDict* newNode = new SkPDFDict("Pages");
-            SkRefPtr<SkPDFObjRef> newNodeRef = new SkPDFObjRef(newNode);
-            newNodeRef->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFObjRef> newNodeRef(new SkPDFObjRef(newNode));
 
-            SkRefPtr<SkPDFArray> kids = new SkPDFArray;
-            kids->unref();  // SkRefPtr and new both took a reference.
+            SkAutoTUnref<SkPDFArray> kids(new SkPDFArray);
             kids->reserve(kNodeSize);
 
             int count = 0;
@@ -111,12 +112,19 @@
                 }
             }
 
-            newNode->insert(kidsName.get(), kids.get());
+            // treeCapacity is the number of leaf nodes possible for the
+            // current set of subtrees being generated. (i.e. 8, 64, 512, ...).
+            // It is hard to count the number of leaf nodes in the current
+            // subtree. However, by construction, we know that unless it's the
+            // last subtree for the current depth, the leaf count will be
+            // treeCapacity, otherwise it's what ever is left over after
+            // consuming treeCapacity chunks.
             int pageCount = treeCapacity;
-            if (count < kNodeSize) {
-                pageCount = pages.count() % treeCapacity;
+            if (i == curNodes.count()) {
+                pageCount = ((pages.count() - 1) % treeCapacity) + 1;
             }
             newNode->insert(countName.get(), new SkPDFInt(pageCount))->unref();
+            newNode->insert(kidsName.get(), kids.get());
             nextRoundNodes.push(newNode);  // Transfer reference.
         }
 
diff --git a/src/pdf/SkPDFPage.h b/src/pdf/SkPDFPage.h
new file mode 100644
index 0000000..72ba335
--- /dev/null
+++ b/src/pdf/SkPDFPage.h
@@ -0,0 +1,100 @@
+
+/*
+ * 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 {
+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 resourceObjects 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.
+     */
+    void finalizePage(SkPDFCatalog* catalog, bool firstPage,
+                      SkTDArray<SkPDFObject*>* resourceObjects);
+
+    /** Determine the size of the page content and store to the catalog
+     *  the offsets of all nonresource-indirect objects that make up the page
+     *  content.  This must be called before emitPage(), but after finalizePage.
+     *  @param catalog    The catalog to add the object offsets to.
+     *  @param fileOffset The file offset where the page content will be
+     *                    emitted.
+     */
+    off_t getPageSize(SkPDFCatalog* catalog, off_t fileOffset);
+
+    /** Output the page content to the passed stream.
+     *  @param stream     The writable output stream to send the content to.
+     *  @param catalog    The active object catalog.
+     */
+    void emitPage(SkWStream* stream, SkPDFCatalog* catalog);
+
+    /** 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;
+
+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;
+};
+
+#endif
diff --git a/src/pdf/SkPDFShader.cpp b/src/pdf/SkPDFShader.cpp
index 183a4ff..7958de3 100644
--- a/src/pdf/SkPDFShader.cpp
+++ b/src/pdf/SkPDFShader.cpp
@@ -21,11 +21,13 @@
 #include "SkThread.h"
 #include "SkTypes.h"
 
-static void transformBBox(const SkMatrix& matrix, SkRect* bbox) {
+static bool transformBBox(const SkMatrix& matrix, SkRect* bbox) {
     SkMatrix inverse;
-    inverse.reset();
-    matrix.invert(&inverse);
+    if (!matrix.invert(&inverse)) {
+        return false;
+    }
     inverse.mapRect(bbox);
+    return true;
 }
 
 static void unitToPointsMatrix(const SkPoint pts[2], SkMatrix* matrix) {
@@ -72,8 +74,9 @@
     }
 
     for (int i = 0; i < components; i++) {
-        // If the next components needs t, make a copy.
-        if (dupInput[i]) {
+        // If the next components needs t and this component will consume a
+        // copy, make another copy.
+        if (dupInput[i] && multiplier[i] != 0) {
             result->append("dup ");
         }
 
@@ -272,6 +275,118 @@
     return function;
 }
 
+/* Conical gradient shader, based on the Canvas spec for radial gradients
+   See: http://www.w3.org/TR/2dcontext/#dom-context-2d-createradialgradient
+ */
+static SkString twoPointConicalCode(const SkShader::GradientInfo& info) {
+    SkScalar dx = info.fPoint[1].fX - info.fPoint[0].fX;
+    SkScalar dy = info.fPoint[1].fY - info.fPoint[0].fY;
+    SkScalar r0 = info.fRadius[0];
+    SkScalar dr = info.fRadius[1] - info.fRadius[0];
+    SkScalar a = SkScalarMul(dx, dx) + SkScalarMul(dy, dy) -
+                 SkScalarMul(dr, dr);
+
+    // First compute t, if the pixel falls outside the cone, then we'll end
+    // with 'false' on the stack, otherwise we'll push 'true' with t below it
+
+    // 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("2 copy ");
+
+    // Calculate b and b^2; b = -2 * (y * dy + x * dx + r0 * dr).
+    function.appendScalar(dy);
+    function.append(" mul exch ");
+    function.appendScalar(dx);
+    function.append(" mul add ");
+    function.appendScalar(SkScalarMul(r0, dr));
+    function.append(" add -2 mul dup dup mul\n");
+
+    // c = x^2 + y^2 + radius0^2
+    function.append("4 2 roll dup mul exch dup mul add ");
+    function.appendScalar(SkScalarMul(r0, r0));
+    function.append(" sub dup 4 1 roll\n");
+
+    // Contents of the stack at this point: c, b, b^2, c
+
+    // if a = 0, then we collapse to a simpler linear case
+    if (a == 0) {
+
+        // t = -c/b
+        function.append("pop pop div neg dup ");
+
+        // compute radius(t)
+        function.appendScalar(dr);
+        function.append(" mul ");
+        function.appendScalar(r0);
+        function.append(" add\n");
+
+        // if r(t) < 0, then it's outside the cone
+        function.append("0 lt {pop false} {true} ifelse\n");
+
+    } else {
+
+        // quadratic case: the Canvas spec wants the largest
+        // root t for which radius(t) > 0
+
+        // compute the discriminant (b^2 - 4ac)
+        function.appendScalar(SkScalarMul(SkIntToScalar(4), a));
+        function.append(" mul sub dup\n");
+
+        // if d >= 0, proceed
+        function.append("0 ge {\n");
+
+        // an intermediate value we'll use to compute the roots:
+        // q = -0.5 * (b +/- sqrt(d))
+        function.append("sqrt exch dup 0 lt {exch -1 mul} if");
+        function.append(" add -0.5 mul dup\n");
+
+        // first root = q / a
+        function.appendScalar(a);
+        function.append(" div\n");
+
+        // second root = c / q
+        function.append("3 1 roll div\n");
+
+        // put the larger root on top of the stack
+        function.append("2 copy gt {exch} if\n");
+
+        // compute radius(t) for larger root
+        function.append("dup ");
+        function.appendScalar(dr);
+        function.append(" mul ");
+        function.appendScalar(r0);
+        function.append(" add\n");
+
+        // if r(t) > 0, we have our t, pop off the smaller root and we're done
+        function.append(" 0 gt {exch pop true}\n");
+
+        // otherwise, throw out the larger one and try the smaller root
+        function.append("{pop dup\n");
+        function.appendScalar(dr);
+        function.append(" mul ");
+        function.appendScalar(r0);
+        function.append(" add\n");
+
+        // if r(t) < 0, push false, otherwise the smaller root is our t
+        function.append("0 le {pop false} {true} ifelse\n");
+        function.append("} ifelse\n");
+
+        // d < 0, clear the stack and push false
+        function.append("} {pop pop pop false} ifelse\n");
+    }
+
+    // if the pixel is in the cone, proceed to compute a color
+    function.append("{");
+    tileModeCode(info.fTileMode, &function);
+    gradientFunctionCode(info, &function);
+
+    // otherwise, just write black
+    function.append("} {0 0 0} ifelse }");
+
+    return function;
+}
+
 static SkString sweepCode(const SkShader::GradientInfo& info) {
     SkString function("{exch atan 360 div\n");
     tileModeCode(info.fTileMode, &function);
@@ -301,14 +416,14 @@
 class SkPDFFunctionShader : public SkPDFDict, public SkPDFShader {
 public:
     explicit SkPDFFunctionShader(SkPDFShader::State* state);
-    ~SkPDFFunctionShader() {
+    virtual ~SkPDFFunctionShader() {
         if (isValid()) {
             RemoveShader(this);
         }
         fResources.unrefAll();
     }
 
-    bool isValid() { return fResources.count() > 0; }
+    virtual bool isValid() { return fResources.count() > 0; }
 
     void getResources(SkTDArray<SkPDFObject*>* resourceList) {
         GetResourcesHelper(&fResources, resourceList);
@@ -326,11 +441,13 @@
 class SkPDFImageShader : public SkPDFStream, public SkPDFShader {
 public:
     explicit SkPDFImageShader(SkPDFShader::State* state);
-    ~SkPDFImageShader() {
+    virtual ~SkPDFImageShader() {
         RemoveShader(this);
         fResources.unrefAll();
     }
 
+    virtual bool isValid() { return size() > 0; }
+
     void getResources(SkTDArray<SkPDFObject*>* resourceList) {
         GetResourcesHelper(&fResources, resourceList);
     }
@@ -358,6 +475,14 @@
     SkPDFObject* result;
     SkAutoMutexAcquire lock(CanonicalShadersMutex());
     SkAutoTDelete<State> shaderState(new State(shader, matrix, surfaceBBox));
+    if (shaderState.get()->fType == SkShader::kNone_GradientType &&
+            shaderState.get()->fImage.isNull()) {
+        // TODO(vandebo) This drops SKComposeShader on the floor.  We could
+        // handle compose shader by pulling things up to a layer, drawing with
+        // the first shader, applying the xfer mode and drawing again with the
+        // second shader, then applying the layer to the original drawing.
+        return NULL;
+    }
 
     ShaderCanonicalEntry entry(NULL, shaderState.get());
     int index = CanonicalShaders().find(entry);
@@ -366,18 +491,24 @@
         result->ref();
         return result;
     }
+
+    bool valid = false;
     // The PDFShader takes ownership of the shaderSate.
     if (shaderState.get()->fType == SkShader::kNone_GradientType) {
-        result = new SkPDFImageShader(shaderState.detach());
+        SkPDFImageShader* imageShader =
+            new SkPDFImageShader(shaderState.detach());
+        valid = imageShader->isValid();
+        result = imageShader;
     } else {
         SkPDFFunctionShader* functionShader =
             new SkPDFFunctionShader(shaderState.detach());
-        if (!functionShader->isValid()) {
-            delete functionShader;
-            return NULL;
-        }
+        valid = functionShader->isValid();
         result = functionShader;
     }
+    if (!valid) {
+        delete result;
+        return NULL;
+    }
     entry.fPDFShader = result;
     CanonicalShaders().push(entry);
     return result;  // return the reference that came from new.
@@ -449,9 +580,15 @@
             codeFunction = &twoPointRadialCode;
             break;
         }
+        case SkShader::kConical_GradientType: {
+            transformPoints[1] = transformPoints[0];
+            transformPoints[1].fX += SK_Scalar1;
+            codeFunction = &twoPointConicalCode;
+            break;
+        }
         case SkShader::kSweep_GradientType:
             transformPoints[1] = transformPoints[0];
-            transformPoints[1].fX += 1;
+            transformPoints[1].fX += SK_Scalar1;
             codeFunction = &sweepCode;
             break;
         case SkShader::kColor_GradientType:
@@ -471,10 +608,11 @@
     finalMatrix.preConcat(fState.get()->fShaderTransform);
     SkRect bbox;
     bbox.set(fState.get()->fBBox);
-    transformBBox(finalMatrix, &bbox);
+    if (!transformBBox(finalMatrix, &bbox)) {
+        return;
+    }
 
-    SkRefPtr<SkPDFArray> domain = new SkPDFArray;
-    domain->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> domain(new SkPDFArray);
     domain->reserve(4);
     domain->appendScalar(bbox.fLeft);
     domain->appendScalar(bbox.fRight);
@@ -488,7 +626,9 @@
     if (fState.get()->fType == SkShader::kRadial2_GradientType) {
         SkShader::GradientInfo twoPointRadialInfo = *info;
         SkMatrix inverseMapperMatrix;
-        mapperMatrix.invert(&inverseMapperMatrix);
+        if (!mapperMatrix.invert(&inverseMapperMatrix)) {
+            return;
+        }
         inverseMapperMatrix.mapPoints(twoPointRadialInfo.fPoint, 2);
         twoPointRadialInfo.fRadius[0] =
             inverseMapperMatrix.mapRadius(info->fRadius[0]);
@@ -499,16 +639,14 @@
         functionCode = codeFunction(*info);
     }
 
-    SkRefPtr<SkPDFStream> function = makePSFunction(functionCode, domain.get());
-    // Pass one reference to fResources, SkRefPtr and new both took a reference.
-    fResources.push(function.get());
-
-    SkRefPtr<SkPDFDict> pdfShader = new SkPDFDict;
-    pdfShader->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> pdfShader(new SkPDFDict);
     pdfShader->insertInt("ShadingType", 1);
     pdfShader->insertName("ColorSpace", "DeviceRGB");
     pdfShader->insert("Domain", domain.get());
-    pdfShader->insert("Function", new SkPDFObjRef(function.get()))->unref();
+
+    SkPDFStream* function = makePSFunction(functionCode, domain.get());
+    pdfShader->insert("Function", new SkPDFObjRef(function))->unref();
+    fResources.push(function);  // Pass ownership to resource list.
 
     insertInt("PatternType", 2);
     insert("Matrix", SkPDFUtils::MatrixToArray(finalMatrix))->unref();
@@ -522,10 +660,12 @@
     finalMatrix.preConcat(fState.get()->fShaderTransform);
     SkRect surfaceBBox;
     surfaceBBox.set(fState.get()->fBBox);
-    transformBBox(finalMatrix, &surfaceBBox);
+    if (!transformBBox(finalMatrix, &surfaceBBox)) {
+        return;
+    }
 
     SkMatrix unflip;
-    unflip.setTranslate(0, SkScalarRound(surfaceBBox.height()));
+    unflip.setTranslate(0, SkScalarRoundToScalar(surfaceBBox.height()));
     unflip.preScale(SK_Scalar1, -SK_Scalar1);
     SkISize size = SkISize::Make(SkScalarRound(surfaceBBox.width()),
                                  SkScalarRound(surfaceBBox.height()));
@@ -535,8 +675,8 @@
     finalMatrix.preTranslate(surfaceBBox.fLeft, surfaceBBox.fTop);
 
     const SkBitmap* image = &fState.get()->fImage;
-    int width = image->width();
-    int height = image->height();
+    SkScalar width = SkIntToScalar(image->width());
+    SkScalar height = SkIntToScalar(image->height());
     SkShader::TileMode tileModes[2];
     tileModes[0] = fState.get()->fImageTileModes[0];
     tileModes[1] = fState.get()->fImageTileModes[1];
@@ -585,28 +725,29 @@
 
         rect = SkRect::MakeLTRB(width, surfaceBBox.fTop, surfaceBBox.fRight, 0);
         if (!rect.isEmpty()) {
-            paint.setColor(image->getColor(width - 1, 0));
+            paint.setColor(image->getColor(image->width() - 1, 0));
             canvas.drawRect(rect, paint);
         }
 
         rect = SkRect::MakeLTRB(width, height, surfaceBBox.fRight,
                                 surfaceBBox.fBottom);
         if (!rect.isEmpty()) {
-            paint.setColor(image->getColor(width - 1, height - 1));
+            paint.setColor(image->getColor(image->width() - 1,
+                                           image->height() - 1));
             canvas.drawRect(rect, paint);
         }
 
         rect = SkRect::MakeLTRB(surfaceBBox.fLeft, height, 0,
                                 surfaceBBox.fBottom);
         if (!rect.isEmpty()) {
-            paint.setColor(image->getColor(0, height - 1));
+            paint.setColor(image->getColor(0, image->height() - 1));
             canvas.drawRect(rect, paint);
         }
     }
 
     // Then expand the left, right, top, then bottom.
     if (tileModes[0] == SkShader::kClamp_TileMode) {
-        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, height);
+        SkIRect subset = SkIRect::MakeXYWH(0, 0, 1, image->height());
         if (surfaceBBox.fLeft < 0) {
             SkBitmap left;
             SkAssertResult(image->extractSubset(&left, subset));
@@ -626,7 +767,7 @@
 
         if (surfaceBBox.fRight > width) {
             SkBitmap right;
-            subset.offset(width - 1, 0);
+            subset.offset(image->width() - 1, 0);
             SkAssertResult(image->extractSubset(&right, subset));
 
             SkMatrix rightMatrix;
@@ -644,7 +785,7 @@
     }
 
     if (tileModes[1] == SkShader::kClamp_TileMode) {
-        SkIRect subset = SkIRect::MakeXYWH(0, 0, width, 1);
+        SkIRect subset = SkIRect::MakeXYWH(0, 0, image->width(), 1);
         if (surfaceBBox.fTop < 0) {
             SkBitmap top;
             SkAssertResult(image->extractSubset(&top, subset));
@@ -664,7 +805,7 @@
 
         if (surfaceBBox.fBottom > height) {
             SkBitmap bottom;
-            subset.offset(0, height - 1);
+            subset.offset(0, image->height() - 1);
             SkAssertResult(image->extractSubset(&bottom, subset));
 
             SkMatrix bottomMatrix;
@@ -681,8 +822,7 @@
         }
     }
 
-    SkRefPtr<SkPDFArray> patternBBoxArray = new SkPDFArray;
-    patternBBoxArray->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFArray> patternBBoxArray(new SkPDFArray);
     patternBBoxArray->reserve(4);
     patternBBoxArray->appendScalar(patternBBox.fLeft);
     patternBBoxArray->appendScalar(patternBBox.fTop);
@@ -690,11 +830,10 @@
     patternBBoxArray->appendScalar(patternBBox.fBottom);
 
     // Put the canvas into the pattern stream (fContent).
-    SkRefPtr<SkStream> content = pattern.content();
-    content->unref();  // SkRefPtr and content() both took a reference.
-    pattern.getResources(&fResources);
-
+    SkAutoTUnref<SkStream> content(pattern.content());
     setData(content.get());
+    pattern.getResources(&fResources, false);
+
     insertName("Type", "Pattern");
     insertInt("PatternType", 1);
     insertInt("PaintType", 1);
@@ -769,6 +908,7 @@
                 }
                 break;
             case SkShader::kRadial2_GradientType:
+            case SkShader::kConical_GradientType:
                 if (fInfo.fPoint[1] != b.fInfo.fPoint[1] ||
                         fInfo.fRadius[0] != b.fInfo.fRadius[0] ||
                         fInfo.fRadius[1] != b.fInfo.fRadius[1]) {
@@ -792,7 +932,7 @@
     fInfo.fColorCount = 0;
     fInfo.fColors = NULL;
     fInfo.fColorOffsets = NULL;
-    shader.getLocalMatrix(&fShaderTransform);
+    fShaderTransform = shader.getLocalMatrix();
     fImageTileModes[0] = fImageTileModes[1] = SkShader::kClamp_TileMode;
 
     fType = shader.asAGradient(&fInfo);
@@ -800,7 +940,7 @@
     if (fType == SkShader::kNone_GradientType) {
         SkShader::BitmapType bitmapType;
         SkMatrix matrix;
-        bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes, NULL);
+        bitmapType = shader.asABitmap(&fImage, &matrix, fImageTileModes);
         if (bitmapType != SkShader::kDefault_BitmapType) {
             fImage.reset();
             return;
diff --git a/src/pdf/SkPDFShader.h b/src/pdf/SkPDFShader.h
new file mode 100644
index 0000000..f41bdf0
--- /dev/null
+++ b/src/pdf/SkPDFShader.h
@@ -0,0 +1,68 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkPDFShader_DEFINED
+#define SkPDFShader_DEFINED
+
+#include "SkPDFStream.h"
+#include "SkPDFTypes.h"
+#include "SkMatrix.h"
+#include "SkRefCnt.h"
+#include "SkShader.h"
+
+class SkObjRef;
+class SkPDFCatalog;
+
+/** \class SkPDFShader
+
+    In PDF parlance, this is a pattern, used in place of a color when the
+    pattern color space is selected.
+*/
+
+class SkPDFShader {
+public:
+    /** Get the PDF shader for the passed SkShader. If the SkShader is
+     *  invalid in some way, returns NULL. 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.
+     *  @param shader     The SkShader to emulate.
+     *  @param matrix     The current transform. (PDF shaders are absolutely
+     *                    positioned, relative to where the page is drawn.)
+     *  @param surfceBBox The bounding box of the drawing surface (with matrix
+     *                    already applied).
+     */
+    static SkPDFObject* GetPDFShader(const SkShader& shader,
+                                     const SkMatrix& matrix,
+                                     const SkIRect& surfaceBBox);
+
+protected:
+    class State;
+
+    class ShaderCanonicalEntry {
+    public:
+        ShaderCanonicalEntry(SkPDFObject* pdfShader, const State* state);
+        bool operator==(const ShaderCanonicalEntry& b) const;
+
+        SkPDFObject* fPDFShader;
+        const State* fState;
+    };
+    // This should be made a hash table if performance is a problem.
+    static SkTDArray<ShaderCanonicalEntry>& CanonicalShaders();
+    static SkBaseMutex& CanonicalShadersMutex();
+    static void RemoveShader(SkPDFObject* shader);
+
+    SkPDFShader();
+    virtual ~SkPDFShader() {};
+
+    virtual bool isValid() = 0;
+};
+
+#endif
diff --git a/src/pdf/SkPDFStream.cpp b/src/pdf/SkPDFStream.cpp
index 0bd63f3..38e874e 100644
--- a/src/pdf/SkPDFStream.cpp
+++ b/src/pdf/SkPDFStream.cpp
@@ -14,25 +14,27 @@
 #include "SkStream.h"
 
 static bool skip_compression(SkPDFCatalog* catalog) {
-    return catalog->getDocumentFlags() & SkPDFDocument::kNoCompression_Flag;
+    return SkToBool(catalog->getDocumentFlags() &
+                    SkPDFDocument::kNoCompression_Flags);
 }
 
 SkPDFStream::SkPDFStream(SkStream* stream)
     : fState(kUnused_State),
       fData(stream) {
+    SkSafeRef(stream);
 }
 
 SkPDFStream::SkPDFStream(SkData* data) : fState(kUnused_State) {
     SkMemoryStream* stream = new SkMemoryStream;
     stream->setData(data);
-    fData = stream;
-    fData->unref();  // SkRefPtr and new both took a reference.
+    fData.reset(stream);  // Transfer ownership.
 }
 
 SkPDFStream::SkPDFStream(const SkPDFStream& pdfStream)
         : SkPDFDict(),
           fState(kUnused_State),
-          fData(pdfStream.fData) {
+          fData(pdfStream.fData.get()) {
+    fData.get()->ref();
     bool removeLength = true;
     // Don't uncompress an already compressed stream, but we could.
     if (pdfStream.fState == kCompressed_State) {
@@ -83,7 +85,8 @@
 SkPDFStream::SkPDFStream() : fState(kUnused_State) {}
 
 void SkPDFStream::setData(SkStream* stream) {
-    fData = stream;
+    fData.reset(stream);
+    SkSafeRef(stream);
 }
 
 bool SkPDFStream::populate(SkPDFCatalog* catalog) {
@@ -94,9 +97,8 @@
             SkAssertResult(SkFlate::Deflate(fData.get(), &compressedData));
             if (compressedData.getOffset() < fData->getLength()) {
                 SkMemoryStream* stream = new SkMemoryStream;
-                stream->setData(compressedData.copyToData());
-                fData = stream;
-                fData->unref();  // SkRefPtr and new both took a reference.
+                stream->setData(compressedData.copyToData())->unref();
+                fData.reset(stream);  // Transfer ownership.
                 insertName("Filter", "FlateDecode");
             }
             fState = kCompressed_State;
@@ -107,8 +109,7 @@
     } else if (fState == kNoCompression_State && !skip_compression(catalog) &&
                SkFlate::HaveFlate()) {
         if (!fSubstitute.get()) {
-            fSubstitute = new SkPDFStream(*this);
-            fSubstitute->unref();  // SkRefPtr and new both took a reference.
+            fSubstitute.reset(new SkPDFStream(*this));
             catalog->setSubstitute(this, fSubstitute.get());
         }
         return false;
diff --git a/src/pdf/SkPDFStream.h b/src/pdf/SkPDFStream.h
new file mode 100644
index 0000000..6f7a08e
--- /dev/null
+++ b/src/pdf/SkPDFStream.h
@@ -0,0 +1,75 @@
+
+/*
+ * 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 SkPDFStream_DEFINED
+#define SkPDFStream_DEFINED
+
+#include "SkPDFTypes.h"
+#include "SkRefCnt.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+class SkPDFCatalog;
+
+/** \class SkPDFStream
+
+    A stream object in a PDF.  Note, all streams must be indirect objects (via
+    SkObjRef).
+*/
+class SkPDFStream : public SkPDFDict {
+public:
+    /** Create a PDF stream. A Length entry is automatically added to the
+     *  stream dictionary. The stream may be retained (stream->ref() may be
+     *  called) so its contents must not be changed after calling this.
+     *  @param data  The data part of the stream.
+     */
+    explicit SkPDFStream(SkData* data);
+    /** Deprecated constructor. */
+    explicit SkPDFStream(SkStream* stream);
+    /** Create a PDF stream with the same content and dictionary entries
+     *  as the passed one.
+     */
+    explicit SkPDFStream(const SkPDFStream& pdfStream);
+    virtual ~SkPDFStream();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+protected:
+    /* Create a PDF stream with no data.  The setData method must be called to
+     * set the data.
+     */
+    SkPDFStream();
+
+    void setData(SkStream* stream);
+
+private:
+    enum State {
+        kUnused_State,         //!< The stream hasn't been requested yet.
+        kNoCompression_State,  //!< The stream's been requested in an
+                               //   uncompressed form.
+        kCompressed_State,     //!< The stream's already been compressed.
+    };
+    // Indicates what form (or if) the stream has been requested.
+    State fState;
+
+    // TODO(vandebo): Use SkData (after removing deprecated constructor).
+    SkAutoTUnref<SkStream> fData;
+    SkAutoTUnref<SkPDFStream> fSubstitute;
+
+    typedef SkPDFDict INHERITED;
+
+    // Populate the stream dictionary.  This method returns false if
+    // fSubstitute should be used.
+    bool populate(SkPDFCatalog* catalog);
+};
+
+#endif
diff --git a/src/pdf/SkPDFTypes.cpp b/src/pdf/SkPDFTypes.cpp
index f97f21b..59250c8 100644
--- a/src/pdf/SkPDFTypes.cpp
+++ b/src/pdf/SkPDFTypes.cpp
@@ -17,8 +17,17 @@
     #define SNPRINTF    snprintf
 #endif
 
-SkPDFObject::SkPDFObject() {}
-SkPDFObject::~SkPDFObject() {}
+SK_DEFINE_INST_COUNT(SkPDFArray)
+SK_DEFINE_INST_COUNT(SkPDFBool)
+SK_DEFINE_INST_COUNT(SkPDFDict)
+SK_DEFINE_INST_COUNT(SkPDFInt)
+SK_DEFINE_INST_COUNT(SkPDFName)
+SK_DEFINE_INST_COUNT(SkPDFObject)
+SK_DEFINE_INST_COUNT(SkPDFObjRef)
+SK_DEFINE_INST_COUNT(SkPDFScalar)
+SK_DEFINE_INST_COUNT(SkPDFString)
+
+///////////////////////////////////////////////////////////////////////////////
 
 void SkPDFObject::emit(SkWStream* stream, SkPDFCatalog* catalog,
                        bool indirect) {
@@ -64,7 +73,10 @@
     }
 }
 
-SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {}
+SkPDFObjRef::SkPDFObjRef(SkPDFObject* obj) : fObj(obj) {
+    SkSafeRef(obj);
+}
+
 SkPDFObjRef::~SkPDFObjRef() {}
 
 void SkPDFObjRef::emitObject(SkWStream* stream, SkPDFCatalog* catalog,
@@ -292,12 +304,15 @@
 // 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] < '!' || input[i] == '#') {
+        if (input[i] & 0x80 || input[i] < '!' || strchr(escaped, input[i])) {
             result.append("#");
-            result.appendHex(input[i], 2);
+            // Mask with 0xFF to avoid sign extension. i.e. #FFFFFF81
+            result.appendHex(input[i] & 0xFF, 2);
         } else {
             result.append(input.c_str() + i, 1);
         }
diff --git a/src/pdf/SkPDFTypes.h b/src/pdf/SkPDFTypes.h
new file mode 100644
index 0000000..03799d0
--- /dev/null
+++ b/src/pdf/SkPDFTypes.h
@@ -0,0 +1,434 @@
+
+/*
+ * 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 SkPDFTypes_DEFINED
+#define SkPDFTypes_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkScalar.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+#include "SkTypes.h"
+
+class SkPDFCatalog;
+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)
+
+    /** Return the size (number of bytes) of this object in the final output
+     *  file. Compound objects or objects that are computationally intensive
+     *  to output should override this method.
+     *  @param catalog  The object catalog to use.
+     *  @param indirect If true, output an object identifier with the object.
+     */
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    /** For non-primitive objects (i.e. objects defined outside this file),
+     *  this method will add to resourceList any objects that this method
+     *  depends on.  This operates recursively so if this object depends on
+     *  another object and that object depends on two more, all three objects
+     *  will be added.
+     *  @param resourceList  The list to append dependant resources to.
+     */
+    virtual void getResources(SkTDArray<SkPDFObject*>* resourceList);
+
+    /** Emit this object unless the catalog has a substitute object, in which
+     *  case emit that.
+     *  @see emitObject
+     */
+    void emit(SkWStream* stream, SkPDFCatalog* catalog, bool indirect);
+
+    /** Helper function to output an indirect object.
+     *  @param catalog The object catalog to use.
+     *  @param stream  The writable output stream to send the output to.
+     */
+    void emitIndirectObject(SkWStream* stream, SkPDFCatalog* catalog);
+
+    /** Helper function to find the size of an indirect object.
+     *  @param catalog The object catalog to use.
+     */
+    size_t getIndirectOutputSize(SkPDFCatalog* catalog);
+
+    /** Static helper function to add a resource to a list.  The list takes
+     *  a reference.
+     * @param resource  The resource to add.
+     * @param list      The list to add the resource to.
+     */
+    static void AddResourceHelper(SkPDFObject* resource,
+                                  SkTDArray<SkPDFObject*>* list);
+
+    /** Static helper function to copy and reference the resources (and all
+     *   their subresources) into a new list.
+     * @param resources The resource list.
+     * @param result    The list to add to.
+     */
+    static void GetResourcesHelper(SkTDArray<SkPDFObject*>* resources,
+                                   SkTDArray<SkPDFObject*>* result);
+
+protected:
+    /** Subclasses must implement this method to print the object to the
+     *  PDF file.
+     *  @param catalog  The object catalog to use.
+     *  @param indirect If true, output an object identifier with the object.
+     *  @param stream   The writable output stream to send the output to.
+     */
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect) = 0;
+
+        typedef SkRefCnt INHERITED;
+};
+
+/** \class SkPDFObjRef
+
+    An indirect reference to a PDF object.
+*/
+class SkPDFObjRef : public SkPDFObject {
+public:
+    SK_DECLARE_INST_COUNT(SkPDFObjRef)
+
+    /** Create a reference to an existing SkPDFObject.
+     *  @param obj The object to reference.
+     */
+    explicit SkPDFObjRef(SkPDFObject* obj);
+    virtual ~SkPDFObjRef();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+    SkAutoTUnref<SkPDFObject> fObj;
+
+    typedef SkPDFObject INHERITED;
+};
+
+/** \class SkPDFInt
+
+    An integer object in a PDF.
+*/
+class SkPDFInt : 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,
+                            bool indirect);
+
+private:
+    int32_t fValue;
+
+    typedef SkPDFObject INHERITED;
+};
+
+/** \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,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+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,
+                            bool indirect);
+
+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,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    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,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+private:
+    static const size_t kMaxLen = 127;
+
+    const SkString fValue;
+
+    static SkString FormatName(const SkString& input);
+
+    typedef SkPDFObject INHERITED;
+};
+
+/** \class SkPDFArray
+
+    An array object in a PDF.
+*/
+class SkPDFArray : public SkPDFObject {
+public:
+    SK_DECLARE_INST_COUNT(SkPDFArray)
+
+    /** Create a PDF array. Maximum length is 8191.
+     */
+    SkPDFArray();
+    virtual ~SkPDFArray();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    /** The size of the array.
+     */
+    int size() { return fValue.count(); }
+
+    /** 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.
+     *  @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[]);
+
+private:
+    static const int kMaxLen = 8191;
+    SkTDArray<SkPDFObject*> fValue;
+
+    typedef SkPDFObject INHERITED;
+};
+
+/** \class SkPDFDict
+
+    A dictionary object in a PDF.
+*/
+class SkPDFDict : public SkPDFObject {
+public:
+    SK_DECLARE_INST_COUNT(SkPDFDict)
+
+    /** Create a PDF dictionary. Maximum number of entries is 4095.
+     */
+    SkPDFDict();
+
+    /** Create a PDF dictionary with a Type entry.
+     *  @param type   The value of the Type entry.
+     */
+    explicit SkPDFDict(const char type[]);
+
+    virtual ~SkPDFDict();
+
+    // The SkPDFObject interface.
+    virtual void emitObject(SkWStream* stream, SkPDFCatalog* catalog,
+                            bool indirect);
+    virtual size_t getOutputSize(SkPDFCatalog* catalog, bool indirect);
+
+    /** The size of the dictionary.
+     */
+    int size() { return fValue.count(); }
+
+    /** 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.
+     *  @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);
+
+    /** Add the int 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.
+     */
+    void insertInt(const char key[], int32_t 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 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());
+    }
+
+    /** Remove all entries from the dictionary.
+     */
+    void clear();
+
+private:
+    struct Rec {
+      SkPDFName* key;
+      SkPDFObject* value;
+    };
+
+public:
+    class Iter {
+    public:
+        explicit Iter(const SkPDFDict& dict);
+        SkPDFName* next(SkPDFObject** value);
+
+    private:
+        Rec* fIter;
+        Rec* fStop;
+    };
+
+private:
+    static const int kMaxLen = 4095;
+
+    SkTDArray<struct Rec> fValue;
+
+    typedef SkPDFObject INHERITED;
+};
+
+#endif
diff --git a/src/pdf/SkPDFUtils.cpp b/src/pdf/SkPDFUtils.cpp
index b596a27..8cd3a90 100644
--- a/src/pdf/SkPDFUtils.cpp
+++ b/src/pdf/SkPDFUtils.cpp
@@ -7,6 +7,7 @@
  */
 
 
+#include "SkData.h"
 #include "SkGeometry.h"
 #include "SkPaint.h"
 #include "SkPath.h"
@@ -98,7 +99,23 @@
 }
 
 // static
-void SkPDFUtils::EmitPath(const SkPath& path, SkWStream* content) {
+void SkPDFUtils::EmitPath(const SkPath& path, SkPaint::Style paintStyle,
+                          SkWStream* content) {
+    // Filling a path with no area results in a drawing in PDF renderers but
+    // Chrome expects to be able to draw some such entities with no visible
+    // result, so we detect those cases and discard the drawing for them.
+    // Specifically: moveTo(X), lineTo(Y) and moveTo(X), lineTo(X), lineTo(Y).
+    enum SkipFillState {
+        kEmpty_SkipFillState         = 0,
+        kSingleLine_SkipFillState    = 1,
+        kNonSingleLine_SkipFillState = 2,
+    };
+    SkipFillState fillState = kEmpty_SkipFillState;
+    if (paintStyle != SkPaint::kFill_Style) {
+        fillState = kNonSingleLine_SkipFillState;
+    }
+    SkPoint lastMovePt;
+    SkDynamicMemoryWStream currentSegment;
     SkPoint args[4];
     SkPath::Iter iter(path, false);
     for (SkPath::Verb verb = iter.next(args);
@@ -107,30 +124,52 @@
         // args gets all the points, even the implicit first point.
         switch (verb) {
             case SkPath::kMove_Verb:
-                MoveTo(args[0].fX, args[0].fY, content);
+                MoveTo(args[0].fX, args[0].fY, &currentSegment);
+                lastMovePt = args[0];
+                fillState = kEmpty_SkipFillState;
                 break;
             case SkPath::kLine_Verb:
-                AppendLine(args[1].fX, args[1].fY, content);
+                AppendLine(args[1].fX, args[1].fY, &currentSegment);
+                if (fillState == kEmpty_SkipFillState) {
+                   if (args[0] != lastMovePt) {
+                       fillState = kSingleLine_SkipFillState;
+                   }
+                } else if (fillState == kSingleLine_SkipFillState) {
+                    fillState = kNonSingleLine_SkipFillState;
+                }
                 break;
             case SkPath::kQuad_Verb: {
                 SkPoint cubic[4];
                 SkConvertQuadToCubic(args, cubic);
                 AppendCubic(cubic[1].fX, cubic[1].fY, cubic[2].fX, cubic[2].fY,
-                            cubic[3].fX, cubic[3].fY, content);
+                            cubic[3].fX, cubic[3].fY, &currentSegment);
+                fillState = kNonSingleLine_SkipFillState;
                 break;
             }
             case SkPath::kCubic_Verb:
                 AppendCubic(args[1].fX, args[1].fY, args[2].fX, args[2].fY,
-                            args[3].fX, args[3].fY, content);
+                            args[3].fX, args[3].fY, &currentSegment);
+                fillState = kNonSingleLine_SkipFillState;
                 break;
             case SkPath::kClose_Verb:
-                ClosePath(content);
+                if (fillState != kSingleLine_SkipFillState) {
+                    ClosePath(&currentSegment);
+                    SkData* data = currentSegment.copyToData();
+                    content->write(data->data(), data->size());
+                    data->unref();
+                }
+                currentSegment.reset();
                 break;
             default:
                 SkASSERT(false);
                 break;
         }
     }
+    if (currentSegment.bytesWritten() > 0) {
+        SkData* data = currentSegment.copyToData();
+        content->write(data->data(), data->size());
+        data->unref();
+    }
 }
 
 // static
diff --git a/src/pdf/SkPDFUtils.h b/src/pdf/SkPDFUtils.h
new file mode 100644
index 0000000..1bdbf78
--- /dev/null
+++ b/src/pdf/SkPDFUtils.h
@@ -0,0 +1,57 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#ifndef SkPDFUtils_DEFINED
+#define SkPDFUtils_DEFINED
+
+#include "SkPaint.h"
+#include "SkPath.h"
+
+class SkMatrix;
+class SkPath;
+class SkPDFArray;
+struct SkRect;
+class SkWStream;
+
+#if 0
+#define PRINT_NOT_IMPL(str) fprintf(stderr, str)
+#else
+#define PRINT_NOT_IMPL(str)
+#endif
+
+#define NOT_IMPLEMENTED(condition, assert)                         \
+    do {                                                           \
+        if (condition) {                                           \
+            PRINT_NOT_IMPL("NOT_IMPLEMENTED: " #condition "\n");   \
+            SkDEBUGCODE(SkASSERT(!assert);)                        \
+        }                                                          \
+    } while (0)
+
+class SkPDFUtils {
+public:
+    static SkPDFArray* MatrixToArray(const SkMatrix& matrix);
+    static void AppendTransform(const SkMatrix& matrix, SkWStream* content);
+
+    static void MoveTo(SkScalar x, SkScalar y, SkWStream* content);
+    static void AppendLine(SkScalar x, SkScalar y, SkWStream* content);
+    static void AppendCubic(SkScalar ctl1X, SkScalar ctl1Y,
+                            SkScalar ctl2X, SkScalar ctl2Y,
+                            SkScalar dstX, SkScalar dstY, SkWStream* content);
+    static void AppendRectangle(const SkRect& rect, SkWStream* content);
+    static void EmitPath(const SkPath& path, SkPaint::Style paintStyle,
+                         SkWStream* content);
+    static void ClosePath(SkWStream* content);
+    static void PaintPath(SkPaint::Style style, SkPath::FillType fill,
+                          SkWStream* content);
+    static void StrokePath(SkWStream* content);
+    static void DrawFormXObject(int objectIndex, SkWStream* content);
+    static void ApplyGraphicState(int objectIndex, SkWStream* content);
+};
+
+#endif
diff --git a/src/pipe/SkGPipePriv.h b/src/pipe/SkGPipePriv.h
index 2baf75a..b563652 100644
--- a/src/pipe/SkGPipePriv.h
+++ b/src/pipe/SkGPipePriv.h
@@ -23,6 +23,7 @@
     kPathEffect_PaintFlat,
     kRasterizer_PaintFlat,
     kShader_PaintFlat,
+    kImageFilter_PaintFlat,
     kXfermode_PaintFlat,
 
     kLast_PaintFlat = kXfermode_PaintFlat
@@ -36,12 +37,15 @@
     kClipPath_DrawOp,
     kClipRegion_DrawOp,
     kClipRect_DrawOp,
+    kClipRRect_DrawOp,
     kConcat_DrawOp,
     kDrawBitmap_DrawOp,
     kDrawBitmapMatrix_DrawOp,
-    kDrawBitmapRect_DrawOp,
+    kDrawBitmapNine_DrawOp,
+    kDrawBitmapRectToRect_DrawOp,
     kDrawClear_DrawOp,
     kDrawData_DrawOp,
+    kDrawOval_DrawOp,
     kDrawPaint_DrawOp,
     kDrawPath_DrawOp,
     kDrawPicture_DrawOp,
@@ -49,6 +53,7 @@
     kDrawPosText_DrawOp,
     kDrawPosTextH_DrawOp,
     kDrawRect_DrawOp,
+    kDrawRRect_DrawOp,
     kDrawSprite_DrawOp,
     kDrawText_DrawOp,
     kDrawTextOnPath_DrawOp,
@@ -63,11 +68,16 @@
     kTranslate_DrawOp,
 
     kPaintOp_DrawOp,
+    kSetTypeface_DrawOp,
 
     kDef_Typeface_DrawOp,
     kDef_Flattenable_DrawOp,
+    kDef_Bitmap_DrawOp,
+    kDef_Factory_DrawOp,
 
     // these are signals to playback, not drawing verbs
+    kReportFlags_DrawOp,
+    kShareBitmapHeap_DrawOp,
     kDone_DrawOp,
 };
 
@@ -92,19 +102,19 @@
 #define DRAWOPS_FLAG_MASK   ((1 << DRAWOPS_FLAG_BITS) - 1)
 #define DRAWOPS_DATA_MASK   ((1 << DRAWOPS_DATA_BITS) - 1)
 
-static unsigned DrawOp_unpackOp(uint32_t op32) {
+static inline unsigned DrawOp_unpackOp(uint32_t op32) {
     return (op32 >> (DRAWOPS_FLAG_BITS + DRAWOPS_DATA_BITS));
 }
 
-static unsigned DrawOp_unpackFlags(uint32_t op32) {
+static inline unsigned DrawOp_unpackFlags(uint32_t op32) {
     return (op32 >> DRAWOPS_DATA_BITS) & DRAWOPS_FLAG_MASK;
 }
 
-static unsigned DrawOp_unpackData(uint32_t op32) {
+static inline unsigned DrawOp_unpackData(uint32_t op32) {
     return op32 & DRAWOPS_DATA_MASK;
 }
 
-static uint32_t DrawOp_packOpFlagData(DrawOps op, unsigned flags, unsigned data) {
+static inline uint32_t DrawOp_packOpFlagData(DrawOps op, unsigned flags, unsigned data) {
     SkASSERT(0 == (op & ~DRAWOPS_OP_MASK));
     SkASSERT(0 == (flags & ~DRAWOPS_FLAG_MASK));
     SkASSERT(0 == (data & ~DRAWOPS_DATA_MASK));
@@ -132,12 +142,80 @@
     kDrawVertices_HasColors_DrawOpFlag   = 1 << 1,
     kDrawVertices_HasIndices_DrawOpFlag  = 1 << 2,
 };
+enum {
+    kDrawBitmap_HasPaint_DrawOpFlag   = 1 << 0,
+    // Specific to drawBitmapRect, but needs to be different from HasPaint,
+    // which is used for all drawBitmap calls, so include it here.
+    kDrawBitmap_HasSrcRect_DrawOpFlag = 1 << 1,
+};
+enum {
+    kClip_HasAntiAlias_DrawOpFlag = 1 << 0,
+};
+///////////////////////////////////////////////////////////////////////////////
+
+class BitmapInfo : SkNoncopyable {
+public:
+    BitmapInfo(SkBitmap* bitmap, uint32_t genID, int toBeDrawnCount)
+        : fBitmap(bitmap)
+        , fGenID(genID)
+        , fBytesAllocated(0)
+        , fMoreRecentlyUsed(NULL)
+        , fLessRecentlyUsed(NULL)
+        , fToBeDrawnCount(toBeDrawnCount)
+    {}
+
+    ~BitmapInfo() {
+        SkASSERT(0 == fToBeDrawnCount);
+        SkDELETE(fBitmap);
+    }
+
+    void addDraws(int drawsToAdd) {
+        if (0 == fToBeDrawnCount) {
+            // The readers will only ever decrement the count, so once the
+            // count is zero, the writer will be the only one modifying it,
+            // so it does not need to be an atomic operation.
+            fToBeDrawnCount = drawsToAdd;
+        } else {
+            sk_atomic_add(&fToBeDrawnCount, drawsToAdd);
+        }
+    }
+
+    void decDraws() {
+        sk_atomic_dec(&fToBeDrawnCount);
+    }
+
+    int drawCount() const {
+        return fToBeDrawnCount;
+    }
+
+    SkBitmap* fBitmap;
+    // Store the generation ID of the original bitmap, since copying does
+    // not copy this field, so fBitmap's generation ID will not be useful
+    // for comparing.
+    // FIXME: Is it reasonable to make copying a bitmap/pixelref copy the
+    // generation ID?
+    uint32_t fGenID;
+    // Keep track of the bytes allocated for this bitmap. When replacing the
+    // bitmap or removing this BitmapInfo we know how much memory has been
+    // reclaimed.
+    size_t fBytesAllocated;
+    // TODO: Generalize the LRU caching mechanism
+    BitmapInfo* fMoreRecentlyUsed;
+    BitmapInfo* fLessRecentlyUsed;
+private:
+    int      fToBeDrawnCount;
+};
+
+static inline bool shouldFlattenBitmaps(uint32_t flags) {
+    return SkToBool(flags & SkGPipeWriter::kCrossProcess_Flag
+            && !(flags & SkGPipeWriter::kSharedAddressSpace_Flag));
+}
 
 ///////////////////////////////////////////////////////////////////////////////
 
 enum PaintOps {
     kReset_PaintOp,     // no arg
-    
+
     kFlags_PaintOp,     // arg inline
     kColor_PaintOp,     // arg 32
     kStyle_PaintOp,     // arg inline
@@ -145,7 +223,7 @@
     kCap_PaintOp,       // arg inline
     kWidth_PaintOp,     // arg scalar
     kMiter_PaintOp,// arg scalar
-    
+
     kEncoding_PaintOp,  // arg inline - text
     kHinting_PaintOp,   // arg inline - text
     kAlign_PaintOp,     // arg inline - text
@@ -165,36 +243,36 @@
 #define PAINTOPS_FLAG_MASK   ((1 << PAINTOPS_FLAG_BITS) - 1)
 #define PAINTOPS_DATA_MASK   ((1 << PAINTOPS_DATA_BITS) - 1)
 
-static unsigned PaintOp_unpackOp(uint32_t op32) {
+static inline unsigned PaintOp_unpackOp(uint32_t op32) {
     return (op32 >> (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS));
 }
 
-static unsigned PaintOp_unpackFlags(uint32_t op32) {
+static inline unsigned PaintOp_unpackFlags(uint32_t op32) {
     return (op32 >> PAINTOPS_DATA_BITS) & PAINTOPS_FLAG_MASK;
 }
 
-static unsigned PaintOp_unpackData(uint32_t op32) {
+static inline unsigned PaintOp_unpackData(uint32_t op32) {
     return op32 & PAINTOPS_DATA_MASK;
 }
 
-static uint32_t PaintOp_packOp(PaintOps op) {
+static inline uint32_t PaintOp_packOp(PaintOps op) {
     SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
-    
+
     return op << (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS);
 }
 
-static uint32_t PaintOp_packOpData(PaintOps op, unsigned data) {
+static inline uint32_t PaintOp_packOpData(PaintOps op, unsigned data) {
     SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
     SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
-    
+
     return (op << (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS)) | data;
 }
 
-static uint32_t PaintOp_packOpFlagData(PaintOps op, unsigned flags, unsigned data) {
+static inline uint32_t PaintOp_packOpFlagData(PaintOps op, unsigned flags, unsigned data) {
     SkASSERT(0 == (op & ~PAINTOPS_OP_MASK));
     SkASSERT(0 == (flags & ~PAINTOPS_FLAG_MASK));
     SkASSERT(0 == (data & ~PAINTOPS_DATA_MASK));
-    
+
     return (op << (PAINTOPS_FLAG_BITS + PAINTOPS_DATA_BITS)) |
     (flags << PAINTOPS_DATA_BITS) |
     data;
diff --git a/src/pipe/SkGPipeRead.cpp b/src/pipe/SkGPipeRead.cpp
index f30f105..6cffb1b 100644
--- a/src/pipe/SkGPipeRead.cpp
+++ b/src/pipe/SkGPipeRead.cpp
@@ -7,7 +7,7 @@
  */
 
 
-
+#include "SkBitmapHeap.h"
 #include "SkCanvas.h"
 #include "SkPaint.h"
 #include "SkGPipe.h"
@@ -18,8 +18,10 @@
 #include "SkColorFilter.h"
 #include "SkDrawLooper.h"
 #include "SkMaskFilter.h"
+#include "SkOrderedReadBuffer.h"
 #include "SkPathEffect.h"
 #include "SkRasterizer.h"
+#include "SkRRect.h"
 #include "SkShader.h"
 #include "SkTypeface.h"
 #include "SkXfermode.h"
@@ -45,6 +47,9 @@
         case kShader_PaintFlat:
             paint->setShader((SkShader*)obj);
             break;
+        case kImageFilter_PaintFlat:
+            paint->setImageFilter((SkImageFilter*)obj);
+            break;
         case kXfermode_PaintFlat:
             paint->setXfermode((SkXfermode*)obj);
             break;
@@ -58,19 +63,38 @@
     ~SkRefCntTDArray() { this->unrefAll(); }
 };
 
-class SkGPipeState {
+class SkGPipeState : public SkBitmapHeapReader {
 public:
     SkGPipeState();
     ~SkGPipeState();
 
-    void setReader(SkFlattenableReadBuffer* reader) {
+    void setSilent(bool silent) {
+        fSilent = silent;
+    }
+
+    bool shouldDraw() {
+        return !fSilent;
+    }
+
+    void setFlags(unsigned flags) {
+        if (fFlags != flags) {
+            fFlags = flags;
+            this->updateReader();
+        }
+    }
+
+    unsigned getFlags() const {
+        return fFlags;
+    }
+
+    void setReader(SkOrderedReadBuffer* reader) {
         fReader = reader;
-        fReader->setFactoryArray(&fFactoryArray);
+        this->updateReader();
     }
 
     const SkPaint& paint() const { return fPaint; }
     SkPaint* editPaint() { return &fPaint; }
-    
+
     SkFlattenable* getFlat(unsigned index) const {
         if (0 == index) {
             return NULL;
@@ -79,28 +103,99 @@
     }
 
     void defFlattenable(PaintFlats pf, int index) {
-        SkASSERT(index == fFlatArray.count() + 1);
+        index--;
         SkFlattenable* obj = fReader->readFlattenable();
-        *fFlatArray.append() = obj;
+        if (fFlatArray.count() == index) {
+            *fFlatArray.append() = obj;
+        } else {
+            SkSafeUnref(fFlatArray[index]);
+            fFlatArray[index] = obj;
+        }
+    }
+
+    void defFactory(const char* name) {
+        SkFlattenable::Factory factory = SkFlattenable::NameToFactory(name);
+        if (factory) {
+            SkASSERT(fFactoryArray.find(factory) < 0);
+            *fFactoryArray.append() = factory;
+        }
+    }
+
+    void addBitmap(int index) {
+        SkBitmap* bm;
+        if(fBitmaps.count() == index) {
+            bm = SkNEW(SkBitmap);
+            *fBitmaps.append() = bm;
+        } else {
+            bm = fBitmaps[index];
+        }
+        bm->unflatten(*fReader);
+    }
+
+    /**
+     * Override of SkBitmapHeapReader, so that SkOrderedReadBuffer can use
+     * these SkBitmaps for bitmap shaders.
+     */
+    virtual SkBitmap* getBitmap(int32_t index) const SK_OVERRIDE {
+        return fBitmaps[index];
+    }
+
+    /**
+     * Needed to be a non-abstract subclass of SkBitmapHeapReader.
+     */
+    virtual void releaseRef(int32_t) SK_OVERRIDE {}
+
+    void setSharedHeap(SkBitmapHeap* heap) {
+        SkASSERT(!shouldFlattenBitmaps(fFlags) || NULL == heap);
+        SkRefCnt_SafeAssign(fSharedHeap, heap);
+        this->updateReader();
+    }
+
+    SkBitmapHeap* getSharedHeap() const {
+        return fSharedHeap;
     }
 
     void addTypeface() {
-        size_t size = fReader->readU32();
+        size_t size = fReader->read32();
         const void* data = fReader->skip(SkAlign4(size));
         SkMemoryStream stream(data, size, false);
         *fTypefaces.append() = SkTypeface::Deserialize(&stream);
-    }    
+    }
+
     void setTypeface(SkPaint* paint, unsigned id) {
         paint->setTypeface(id ? fTypefaces[id - 1] : NULL);
     }
-    
-    SkFlattenableReadBuffer* fReader;
 
 private:
+    void updateReader() {
+        if (NULL == fReader) {
+            return;
+        }
+        bool crossProcess = SkToBool(fFlags & SkGPipeWriter::kCrossProcess_Flag);
+        fReader->setFlags(SkSetClearMask(fReader->getFlags(), crossProcess,
+                                         SkFlattenableReadBuffer::kCrossProcess_Flag));
+        if (crossProcess) {
+            fReader->setFactoryArray(&fFactoryArray);
+        } else {
+            fReader->setFactoryArray(NULL);
+        }
+
+        if (shouldFlattenBitmaps(fFlags)) {
+            fReader->setBitmapStorage(this);
+        } else {
+            fReader->setBitmapStorage(fSharedHeap);
+        }
+    }
+    SkOrderedReadBuffer*      fReader;
     SkPaint                   fPaint;
     SkTDArray<SkFlattenable*> fFlatArray;
     SkTDArray<SkTypeface*>    fTypefaces;
     SkTDArray<SkFlattenable::Factory> fFactoryArray;
+    SkTDArray<SkBitmap*>      fBitmaps;
+    bool                      fSilent;
+    // Only used when sharing bitmaps with the writer.
+    SkBitmapHeap*             fSharedHeap;
+    unsigned                  fFlags;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -124,20 +219,31 @@
 static void clipPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                         SkGPipeState* state) {
     SkPath path;
-    path.unflatten(*reader);
-    canvas->clipPath(path, (SkRegion::Op)DrawOp_unpackData(op32));
+    reader->readPath(&path);
+    bool doAA = SkToBool(DrawOp_unpackFlags(op32) & kClip_HasAntiAlias_DrawOpFlag);
+    canvas->clipPath(path, (SkRegion::Op)DrawOp_unpackData(op32), doAA);
 }
 
 static void clipRegion_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                           SkGPipeState* state) {
     SkRegion rgn;
-    SkReadRegion(reader, &rgn);
+    reader->readRegion(&rgn);
     canvas->clipRegion(rgn, (SkRegion::Op)DrawOp_unpackData(op32));
 }
 
 static void clipRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                         SkGPipeState* state) {
-    canvas->clipRect(*skip<SkRect>(reader), (SkRegion::Op)DrawOp_unpackData(op32));
+    const SkRect* rect = skip<SkRect>(reader);
+    bool doAA = SkToBool(DrawOp_unpackFlags(op32) & kClip_HasAntiAlias_DrawOpFlag);
+    canvas->clipRect(*rect, (SkRegion::Op)DrawOp_unpackData(op32), doAA);
+}
+
+static void clipRRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    SkRRect rrect;
+    reader->readRRect(&rrect);
+    bool doAA = SkToBool(DrawOp_unpackFlags(op32) & kClip_HasAntiAlias_DrawOpFlag);
+    canvas->clipRRect(rrect, (SkRegion::Op)DrawOp_unpackData(op32), doAA);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -145,14 +251,14 @@
 static void setMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                       SkGPipeState* state) {
     SkMatrix matrix;
-    SkReadMatrix(reader, &matrix);
+    reader->readMatrix(&matrix);
     canvas->setMatrix(matrix);
 }
 
 static void concat_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                       SkGPipeState* state) {
     SkMatrix matrix;
-    SkReadMatrix(reader, &matrix);
+    reader->readMatrix(&matrix);
     canvas->concat(matrix);
 }
 
@@ -220,7 +326,9 @@
 
 static void drawPaint_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                          SkGPipeState* state) {
-    canvas->drawPaint(state->paint());
+    if (state->shouldDraw()) {
+        canvas->drawPaint(state->paint());
+    }
 }
 
 static void drawPoints_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -228,19 +336,43 @@
     SkCanvas::PointMode mode = (SkCanvas::PointMode)DrawOp_unpackFlags(op32);
     size_t count = reader->readU32();
     const SkPoint* pts = skip<SkPoint>(reader, count);
-    canvas->drawPoints(mode, count, pts, state->paint());
+    if (state->shouldDraw()) {
+        canvas->drawPoints(mode, count, pts, state->paint());
+    }
+}
+
+static void drawOval_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                        SkGPipeState* state) {
+    const SkRect* rect = skip<SkRect>(reader);
+    if (state->shouldDraw()) {
+        canvas->drawOval(*rect, state->paint());
+    }
 }
 
 static void drawRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                         SkGPipeState* state) {
-    canvas->drawRect(*skip<SkRect>(reader), state->paint());
+    const SkRect* rect = skip<SkRect>(reader);
+    if (state->shouldDraw()) {
+        canvas->drawRect(*rect, state->paint());
+    }
+}
+
+static void drawRRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
+                         SkGPipeState* state) {
+    SkRRect rrect;
+    reader->readRRect(&rrect);
+    if (state->shouldDraw()) {
+        canvas->drawRRect(rrect, state->paint());
+    }
 }
 
 static void drawPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                         SkGPipeState* state) {
     SkPath path;
-    path.unflatten(*reader);
-    canvas->drawPath(path, state->paint());
+    reader->readPath(&path);
+    if (state->shouldDraw()) {
+        canvas->drawPath(path, state->paint());
+    }
 }
 
 static void drawVertices_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -270,9 +402,10 @@
         indexCount = reader->readU32();
         indices = skipAlign<uint16_t>(reader, indexCount);
     }
-
-    canvas->drawVertices(mode, vertexCount, verts, texs, colors, xfer,
-                         indices, indexCount, state->paint());
+    if (state->shouldDraw()) {
+        canvas->drawVertices(mode, vertexCount, verts, texs, colors, xfer,
+                             indices, indexCount, state->paint());
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -282,7 +415,9 @@
     size_t len = reader->readU32();
     const void* text = reader->skip(SkAlign4(len));
     const SkScalar* xy = skip<SkScalar>(reader, 2);
-    canvas->drawText(text, len, xy[0], xy[1], state->paint());
+    if (state->shouldDraw()) {
+        canvas->drawText(text, len, xy[0], xy[1], state->paint());
+    }
 }
 
 static void drawPosText_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -291,7 +426,9 @@
     const void* text = reader->skip(SkAlign4(len));
     size_t posCount = reader->readU32();    // compute by our writer
     const SkPoint* pos = skip<SkPoint>(reader, posCount);
-    canvas->drawPosText(text, len, pos, state->paint());
+    if (state->shouldDraw()) {
+        canvas->drawPosText(text, len, pos, state->paint());
+    }
 }
 
 static void drawPosTextH_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -301,7 +438,9 @@
     size_t posCount = reader->readU32();    // compute by our writer
     const SkScalar* xpos = skip<SkScalar>(reader, posCount);
     SkScalar constY = reader->readScalar();
-    canvas->drawPosTextH(text, len, xpos, constY, state->paint());
+    if (state->shouldDraw()) {
+        canvas->drawPosTextH(text, len, xpos, constY, state->paint());
+    }
 }
 
 static void drawTextOnPath_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -310,38 +449,130 @@
     const void* text = reader->skip(SkAlign4(len));
 
     SkPath path;
-    path.unflatten(*reader);
+    reader->readPath(&path);
 
     SkMatrix matrixStorage;
     const SkMatrix* matrix = NULL;
     if (DrawOp_unpackFlags(op32) & kDrawTextOnPath_HasMatrix_DrawOpFlag) {
-        SkReadMatrix(reader, &matrixStorage);
+        reader->readMatrix(&matrixStorage);
         matrix = &matrixStorage;
     }
-
-    canvas->drawTextOnPath(text, len, path, matrix, state->paint());
+    if (state->shouldDraw()) {
+        canvas->drawTextOnPath(text, len, path, matrix, state->paint());
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+class BitmapHolder : SkNoncopyable {
+public:
+    BitmapHolder(SkReader32* reader, uint32_t op32, SkGPipeState* state);
+    ~BitmapHolder() {
+        if (fHeapEntry != NULL) {
+            fHeapEntry->releaseRef();
+        }
+    }
+    const SkBitmap* getBitmap() {
+        return fBitmap;
+    }
+private:
+    SkBitmapHeapEntry* fHeapEntry;
+    const SkBitmap*    fBitmap;
+    SkBitmap           fBitmapStorage;
+};
+
+BitmapHolder::BitmapHolder(SkReader32* reader, uint32_t op32,
+                           SkGPipeState* state) {
+    const unsigned flags = state->getFlags();
+    const unsigned index = DrawOp_unpackData(op32);
+    if (shouldFlattenBitmaps(flags)) {
+        fHeapEntry = NULL;
+        fBitmap = state->getBitmap(index);
+    } else {
+        SkBitmapHeapEntry* entry = state->getSharedHeap()->getEntry(index);
+        if (SkToBool(flags & SkGPipeWriter::kSimultaneousReaders_Flag)) {
+            // Make a shallow copy for thread safety. Each thread will point to the same SkPixelRef,
+            // which is thread safe.
+            fBitmapStorage = *entry->getBitmap();
+            fBitmap = &fBitmapStorage;
+            // Release the ref on the bitmap now, since we made our own copy.
+            entry->releaseRef();
+            fHeapEntry = NULL;
+        } else {
+            SkASSERT(!shouldFlattenBitmaps(flags));
+            SkASSERT(!SkToBool(flags & SkGPipeWriter::kSimultaneousReaders_Flag));
+            fHeapEntry = entry;
+            fBitmap = fHeapEntry->getBitmap();
+        }
+    }
+}
+
 static void drawBitmap_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                           SkGPipeState* state) {
-    UNIMPLEMENTED
+    BitmapHolder holder(reader, op32, state);
+    bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag);
+    SkScalar left = reader->readScalar();
+    SkScalar top = reader->readScalar();
+    const SkBitmap* bitmap = holder.getBitmap();
+    if (state->shouldDraw()) {
+        canvas->drawBitmap(*bitmap, left, top, hasPaint ? &state->paint() : NULL);
+    }
 }
 
 static void drawBitmapMatrix_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                                 SkGPipeState* state) {
-    UNIMPLEMENTED
+    BitmapHolder holder(reader, op32, state);
+    bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag);
+    SkMatrix matrix;
+    reader->readMatrix(&matrix);
+    const SkBitmap* bitmap = holder.getBitmap();
+    if (state->shouldDraw()) {
+        canvas->drawBitmapMatrix(*bitmap, matrix,
+                                 hasPaint ? &state->paint() : NULL);
+    }
 }
 
-static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
-                              SkGPipeState* state) {
-    UNIMPLEMENTED
+static void drawBitmapNine_rp(SkCanvas* canvas, SkReader32* reader,
+                              uint32_t op32, SkGPipeState* state) {
+    BitmapHolder holder(reader, op32, state);
+    bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag);
+    const SkIRect* center = skip<SkIRect>(reader);
+    const SkRect* dst = skip<SkRect>(reader);
+    const SkBitmap* bitmap = holder.getBitmap();
+    if (state->shouldDraw()) {
+        canvas->drawBitmapNine(*bitmap, *center, *dst,
+                               hasPaint ? &state->paint() : NULL);
+    }
+}
+
+static void drawBitmapRect_rp(SkCanvas* canvas, SkReader32* reader,
+                              uint32_t op32, SkGPipeState* state) {
+    BitmapHolder holder(reader, op32, state);
+    unsigned flags = DrawOp_unpackFlags(op32);
+    bool hasPaint = SkToBool(flags & kDrawBitmap_HasPaint_DrawOpFlag);
+    bool hasSrc = SkToBool(flags & kDrawBitmap_HasSrcRect_DrawOpFlag);
+    const SkRect* src;
+    if (hasSrc) {
+        src = skip<SkRect>(reader);
+    } else {
+        src = NULL;
+    }
+    const SkRect* dst = skip<SkRect>(reader);
+    const SkBitmap* bitmap = holder.getBitmap();
+    if (state->shouldDraw()) {
+        canvas->drawBitmapRectToRect(*bitmap, src, *dst, hasPaint ? &state->paint() : NULL);
+    }
 }
 
 static void drawSprite_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
                           SkGPipeState* state) {
-    UNIMPLEMENTED
+    BitmapHolder holder(reader, op32, state);
+    bool hasPaint = SkToBool(DrawOp_unpackFlags(op32) & kDrawBitmap_HasPaint_DrawOpFlag);
+    const SkIPoint* point = skip<SkIPoint>(reader);
+    const SkBitmap* bitmap = holder.getBitmap();
+    if (state->shouldDraw()) {
+        canvas->drawSprite(*bitmap, point->fX, point->fY, hasPaint ? &state->paint() : NULL);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -354,7 +585,9 @@
         size = reader->readU32();
     }
     const void* data = reader->skip(SkAlign4(size));
-    canvas->drawData(data, size);
+    if (state->shouldDraw()) {
+        canvas->drawData(data, size);
+    }
 }
 
 static void drawPicture_rp(SkCanvas* canvas, SkReader32* reader, uint32_t op32,
@@ -402,13 +635,23 @@
                 break;
             }
 
-            case kTypeface_PaintOp: state->setTypeface(p, data); break;
+            case kTypeface_PaintOp:
+                SkASSERT(SkToBool(state->getFlags() &
+                                  SkGPipeWriter::kCrossProcess_Flag));
+                state->setTypeface(p, data); break;
             default: SkDEBUGFAIL("bad paintop"); return;
         }
         SkASSERT(reader->offset() <= stop);
     } while (reader->offset() < stop);
 }
 
+static void typeface_rp(SkCanvas*, SkReader32* reader, uint32_t,
+                        SkGPipeState* state) {
+    SkASSERT(!SkToBool(state->getFlags() & SkGPipeWriter::kCrossProcess_Flag));
+    SkPaint* p = state->editPaint();
+    p->setTypeface(static_cast<SkTypeface*>(reader->readPtr()));
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static void def_Typeface_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState* state) {
@@ -422,6 +665,17 @@
     state->defFlattenable(pf, index);
 }
 
+static void def_Bitmap_rp(SkCanvas*, SkReader32*, uint32_t op32,
+                          SkGPipeState* state) {
+    unsigned index = DrawOp_unpackData(op32);
+    state->addBitmap(index);
+}
+
+static void def_Factory_rp(SkCanvas*, SkReader32* reader, uint32_t,
+                           SkGPipeState* state) {
+    state->defFactory(reader->readString());
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 static void skip_rp(SkCanvas*, SkReader32* reader, uint32_t op32, SkGPipeState*) {
@@ -429,6 +683,17 @@
     (void)reader->skip(bytes);
 }
 
+static void reportFlags_rp(SkCanvas*, SkReader32*, uint32_t op32,
+                           SkGPipeState* state) {
+    unsigned flags = DrawOp_unpackFlags(op32);
+    state->setFlags(flags);
+}
+
+static void shareBitmapHeap_rp(SkCanvas*, SkReader32* reader, uint32_t,
+                           SkGPipeState* state) {
+    state->setSharedHeap(static_cast<SkBitmapHeap*>(reader->readPtr()));
+}
+
 static void done_rp(SkCanvas*, SkReader32*, uint32_t, SkGPipeState*) {}
 
 typedef void (*ReadProc)(SkCanvas*, SkReader32*, uint32_t op32, SkGPipeState*);
@@ -438,12 +703,15 @@
     clipPath_rp,
     clipRegion_rp,
     clipRect_rp,
+    clipRRect_rp,
     concat_rp,
     drawBitmap_rp,
     drawBitmapMatrix_rp,
+    drawBitmapNine_rp,
     drawBitmapRect_rp,
     drawClear_rp,
     drawData_rp,
+    drawOval_rp,
     drawPaint_rp,
     drawPath_rp,
     drawPicture_rp,
@@ -451,6 +719,7 @@
     drawPosText_rp,
     drawPosTextH_rp,
     drawRect_rp,
+    drawRRect_rp,
     drawSprite_rp,
     drawText_rp,
     drawTextOnPath_rp,
@@ -465,38 +734,60 @@
     translate_rp,
 
     paintOp_rp,
+    typeface_rp,
     def_Typeface_rp,
     def_PaintFlat_rp,
+    def_Bitmap_rp,
+    def_Factory_rp,
 
+    reportFlags_rp,
+    shareBitmapHeap_rp,
     done_rp
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkGPipeState::SkGPipeState() {}
+SkGPipeState::SkGPipeState()
+    : fReader(0)
+    , fSilent(false)
+    , fSharedHeap(NULL)
+    , fFlags(0) {
+
+}
 
 SkGPipeState::~SkGPipeState() {
     fTypefaces.safeUnrefAll();
     fFlatArray.safeUnrefAll();
+    fBitmaps.deleteAll();
+    SkSafeUnref(fSharedHeap);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkGPipe.h"
 
-SkGPipeReader::SkGPipeReader(SkCanvas* target) {
-    SkSafeRef(target);
-    fCanvas = target;
+SkGPipeReader::SkGPipeReader() {
+    fCanvas = NULL;
     fState = NULL;
 }
 
+SkGPipeReader::SkGPipeReader(SkCanvas* target) {
+    fCanvas = NULL;
+    this->setCanvas(target);
+    fState = NULL;
+}
+
+void SkGPipeReader::setCanvas(SkCanvas *target) {
+    SkRefCnt_SafeAssign(fCanvas, target);
+}
+
 SkGPipeReader::~SkGPipeReader() {
     SkSafeUnref(fCanvas);
     delete fState;
 }
 
 SkGPipeReader::Status SkGPipeReader::playback(const void* data, size_t length,
-                                              size_t* bytesRead, bool readAtom) {
+                                              uint32_t playbackFlags, size_t* bytesRead) {
     if (NULL == fCanvas) {
         return kError_Status;
     }
@@ -505,19 +796,21 @@
         fState = new SkGPipeState;
     }
 
+    fState->setSilent(playbackFlags & kSilent_PlaybackFlag);
+
     SkASSERT(SK_ARRAY_COUNT(gReadTable) == (kDone_DrawOp + 1));
 
     const ReadProc* table = gReadTable;
-    SkFlattenableReadBuffer reader(data, length);
+    SkOrderedReadBuffer reader(data, length);
     SkCanvas* canvas = fCanvas;
     Status status = kEOF_Status;
 
     fState->setReader(&reader);
     while (!reader.eof()) {
-        uint32_t op32 = reader.readU32();
+        uint32_t op32 = reader.readUInt();
         unsigned op = DrawOp_unpackOp(op32);
-        SkDEBUGCODE(DrawOps drawOp = (DrawOps)op;)
-        
+        // SkDEBUGCODE(DrawOps drawOp = (DrawOps)op;)
+
         if (op >= SK_ARRAY_COUNT(gReadTable)) {
             SkDebugf("---- bad op during GPipeState::playback\n");
             status = kError_Status;
@@ -527,11 +820,12 @@
             status = kDone_Status;
             break;
         }
-        table[op](canvas, &reader, op32, fState);
-        if (readAtom && 
+        table[op](canvas, reader.getReader32(), op32, fState);
+        if ((playbackFlags & kReadAtom_PlaybackFlag) &&
             (table[op] != paintOp_rp &&
              table[op] != def_Typeface_rp &&
-             table[op] != def_PaintFlat_rp
+             table[op] != def_PaintFlat_rp &&
+             table[op] != def_Bitmap_rp
              )) {
                 status = kReadAtom_Status;
                 break;
@@ -543,5 +837,3 @@
     }
     return status;
 }
-
-
diff --git a/src/pipe/SkGPipeWrite.cpp b/src/pipe/SkGPipeWrite.cpp
index ee6e2c7..cfd1e7c 100644
--- a/src/pipe/SkGPipeWrite.cpp
+++ b/src/pipe/SkGPipeWrite.cpp
@@ -6,23 +6,35 @@
  * found in the LICENSE file.
  */
 
-
-
+#include "SkBitmapHeap.h"
 #include "SkCanvas.h"
+#include "SkColorFilter.h"
 #include "SkData.h"
+#include "SkDrawLooper.h"
 #include "SkDevice.h"
-#include "SkPaint.h"
 #include "SkGPipe.h"
 #include "SkGPipePriv.h"
+#include "SkImageFilter.h"
+#include "SkMaskFilter.h"
+#include "SkOrderedWriteBuffer.h"
+#include "SkPaint.h"
+#include "SkPathEffect.h"
+#include "SkPictureFlat.h"
+#include "SkRasterizer.h"
+#include "SkRRect.h"
+#include "SkShader.h"
 #include "SkStream.h"
 #include "SkTSearch.h"
 #include "SkTypeface.h"
 #include "SkWriter32.h"
-#include "SkColorFilter.h"
-#include "SkDrawLooper.h"
-#include "SkMaskFilter.h"
-#include "SkRasterizer.h"
-#include "SkShader.h"
+
+enum {
+    kSizeOfFlatRRect = sizeof(SkRect) + 4 * sizeof(SkVector)
+};
+
+static bool isCrossProcess(uint32_t flags) {
+    return SkToBool(flags & SkGPipeWriter::kCrossProcess_Flag);
+}
 
 static SkFlattenable* get_paintflat(const SkPaint& paint, unsigned paintFlat) {
     SkASSERT(paintFlat < kCount_PaintFlats);
@@ -33,28 +45,13 @@
         case kPathEffect_PaintFlat:     return paint.getPathEffect();
         case kRasterizer_PaintFlat:     return paint.getRasterizer();
         case kShader_PaintFlat:         return paint.getShader();
+        case kImageFilter_PaintFlat:    return paint.getImageFilter();
         case kXfermode_PaintFlat:       return paint.getXfermode();
     }
     SkDEBUGFAIL("never gets here");
     return NULL;
 }
 
-static size_t estimateFlattenSize(const SkPath& path) {
-    int n = path.countPoints();
-    size_t bytes = 3 * sizeof(int32_t);
-    bytes += n * sizeof(SkPoint);
-    bytes += SkAlign4(n + 2);    // verbs: add 2 for move/close extras
-
-#ifdef SK_DEBUG
-    {
-        SkWriter32 writer(1024);
-        path.flatten(writer);
-        SkASSERT(writer.size() <= bytes);
-    }
-#endif
-    return bytes;
-}
-
 static size_t writeTypeface(SkWriter32* writer, SkTypeface* typeface) {
     SkASSERT(typeface);
     SkDynamicMemoryWStream stream;
@@ -63,16 +60,116 @@
     if (writer) {
         writer->write32(size);
         SkAutoDataUnref data(stream.copyToData());
-        writer->write(data.data(), size);
+        writer->writePad(data->data(), size);
     }
-    return 4 + size;
+    return 4 + SkAlign4(size);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+class FlattenableHeap : public SkFlatController {
+public:
+    FlattenableHeap(int numFlatsToKeep, SkNamedFactorySet* fset, bool isCrossProcess)
+    : fNumFlatsToKeep(numFlatsToKeep) {
+        SkASSERT((isCrossProcess && fset != NULL) || (!isCrossProcess && NULL == fset));
+        if (isCrossProcess) {
+            this->setNamedFactorySet(fset);
+            this->setWriteBufferFlags(SkFlattenableWriteBuffer::kCrossProcess_Flag);
+        }
+    }
+
+    ~FlattenableHeap() {
+        fPointers.freeAll();
+    }
+
+    virtual void* allocThrow(size_t bytes) SK_OVERRIDE;
+
+    virtual void unalloc(void* ptr) SK_OVERRIDE;
+
+    void setBitmapStorage(SkBitmapHeap* heap) {
+        this->setBitmapHeap(heap);
+    }
+
+    const SkFlatData* flatToReplace() const;
+
+    // Mark an SkFlatData as one that should not be returned by flatToReplace.
+    // Takes the result of SkFlatData::index() as its parameter.
+    void markFlatForKeeping(int index) {
+        *fFlatsThatMustBeKept.append() = index;
+    }
+
+    void markAllFlatsSafeToDelete() {
+        fFlatsThatMustBeKept.reset();
+    }
+
+private:
+    // Keep track of the indices (i.e. the result of SkFlatData::index()) of
+    // flats that must be kept, since they are on the current paint.
+    SkTDArray<int>   fFlatsThatMustBeKept;
+    SkTDArray<void*> fPointers;
+    const int        fNumFlatsToKeep;
+};
+
+void FlattenableHeap::unalloc(void* ptr) {
+    int indexToRemove = fPointers.rfind(ptr);
+    if (indexToRemove >= 0) {
+        sk_free(ptr);
+        fPointers.remove(indexToRemove);
+    }
+}
+
+void* FlattenableHeap::allocThrow(size_t bytes) {
+    void* ptr = sk_malloc_throw(bytes);
+    *fPointers.append() = ptr;
+    return ptr;
+}
+
+const SkFlatData* FlattenableHeap::flatToReplace() const {
+    // First, determine whether we should replace one.
+    if (fPointers.count() > fNumFlatsToKeep) {
+        // Look through the flattenable heap.
+        // TODO: Return the LRU flat.
+        for (int i = 0; i < fPointers.count(); i++) {
+            SkFlatData* potential = (SkFlatData*)fPointers[i];
+            // Make sure that it is not one that must be kept.
+            bool mustKeep = false;
+            for (int j = 0; j < fFlatsThatMustBeKept.count(); j++) {
+                if (potential->index() == fFlatsThatMustBeKept[j]) {
+                    mustKeep = true;
+                    break;
+                }
+            }
+            if (!mustKeep) {
+                return potential;
+            }
+        }
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class FlatDictionary : public SkFlatDictionary<SkFlattenable> {
+public:
+    FlatDictionary(FlattenableHeap* heap)
+            : SkFlatDictionary<SkFlattenable>(heap) {
+        fFlattenProc = &flattenFlattenableProc;
+        // No need to define fUnflattenProc since the writer will never
+        // unflatten the data.
+    }
+    static void flattenFlattenableProc(SkOrderedWriteBuffer& buffer,
+                                       const void* obj) {
+        buffer.writeFlattenable((SkFlattenable*)obj);
+    }
+
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 class SkGPipeCanvas : public SkCanvas {
 public:
-    SkGPipeCanvas(SkGPipeController*, SkWriter32*, SkFactorySet*);
+    SkGPipeCanvas(SkGPipeController*, SkWriter32*, uint32_t flags,
+                  uint32_t width, uint32_t height);
     virtual ~SkGPipeCanvas();
 
     void finish() {
@@ -80,64 +177,103 @@
             if (this->needOpBytes()) {
                 this->writeOp(kDone_DrawOp);
                 this->doNotify();
+                if (shouldFlattenBitmaps(fFlags)) {
+                    // In this case, a BitmapShuttle is reffed by the SkBitmapHeap
+                    // and refs this canvas. Unref the SkBitmapHeap to end the
+                    // circular reference. When shouldFlattenBitmaps is false,
+                    // there is no circular reference, so the SkBitmapHeap can be
+                    // safely unreffed in the destructor.
+                    fBitmapHeap->unref();
+                    // This eliminates a similar circular reference (Canvas owns
+                    // the FlattenableHeap which holds a ref to the SkBitmapHeap).
+                    fFlattenableHeap.setBitmapStorage(NULL);
+                    fBitmapHeap = NULL;
+                }
             }
             fDone = true;
         }
     }
 
+    void flushRecording(bool detachCurrentBlock);
+    size_t freeMemoryIfPossible(size_t bytesToFree);
+
+    size_t storageAllocatedForRecording() {
+        return (NULL == fBitmapHeap) ? 0 : fBitmapHeap->bytesAllocated();
+    }
+
     // overrides from SkCanvas
-    virtual int save(SaveFlags);
-    virtual int saveLayer(const SkRect* bounds, const SkPaint*, SaveFlags);
-    virtual void restore();
-    virtual bool translate(SkScalar dx, SkScalar dy);
-    virtual bool scale(SkScalar sx, SkScalar sy);
-    virtual bool rotate(SkScalar degrees);
-    virtual bool skew(SkScalar sx, SkScalar sy);
-    virtual bool concat(const SkMatrix& matrix);
-    virtual void setMatrix(const SkMatrix& matrix);
-    virtual bool clipRect(const SkRect& rect, SkRegion::Op op);
-    virtual bool clipPath(const SkPath& path, SkRegion::Op op);
-    virtual bool clipRegion(const SkRegion& region, SkRegion::Op op);
-    virtual void clear(SkColor);
-    virtual void drawPaint(const SkPaint& paint);
+    virtual int save(SaveFlags) SK_OVERRIDE;
+    virtual int saveLayer(const SkRect* bounds, const SkPaint*,
+                          SaveFlags) SK_OVERRIDE;
+    virtual void restore() SK_OVERRIDE;
+    virtual bool isDrawingToLayer() const SK_OVERRIDE;
+    virtual bool translate(SkScalar dx, SkScalar dy) SK_OVERRIDE;
+    virtual bool scale(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+    virtual bool rotate(SkScalar degrees) SK_OVERRIDE;
+    virtual bool skew(SkScalar sx, SkScalar sy) SK_OVERRIDE;
+    virtual bool concat(const SkMatrix& matrix) SK_OVERRIDE;
+    virtual void setMatrix(const SkMatrix& matrix) SK_OVERRIDE;
+    virtual bool clipRect(const SkRect&, SkRegion::Op op, bool doAntiAlias = false) SK_OVERRIDE;
+    virtual bool clipRRect(const SkRRect&, SkRegion::Op op, bool doAntiAlias = false) SK_OVERRIDE;
+    virtual bool clipPath(const SkPath& path, SkRegion::Op op,
+                          bool doAntiAlias = false) SK_OVERRIDE;
+    virtual bool clipRegion(const SkRegion& region, SkRegion::Op op) SK_OVERRIDE;
+    virtual void clear(SkColor) SK_OVERRIDE;
+    virtual void drawPaint(const SkPaint& paint) SK_OVERRIDE;
     virtual void drawPoints(PointMode, size_t count, const SkPoint pts[],
-                            const SkPaint&);
-    virtual void drawRect(const SkRect& rect, const SkPaint&);
-    virtual void drawPath(const SkPath& path, const SkPaint&);
+                            const SkPaint&) SK_OVERRIDE;
+    virtual void drawOval(const SkRect&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawRect(const SkRect& rect, const SkPaint&) SK_OVERRIDE;
+    virtual void drawRRect(const SkRRect&, const SkPaint&) SK_OVERRIDE;
+    virtual void drawPath(const SkPath& path, const SkPaint&) SK_OVERRIDE;
     virtual void drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
-                            const SkPaint*);
-    virtual void drawBitmapRect(const SkBitmap&, const SkIRect* src,
-                                const SkRect& dst, const SkPaint*);
+                            const SkPaint*) SK_OVERRIDE;
+    virtual void drawBitmapRectToRect(const SkBitmap&, const SkRect* src,
+                                const SkRect& dst, const SkPaint*) SK_OVERRIDE;
     virtual void drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
-                                  const SkPaint*);
+                                  const SkPaint*) SK_OVERRIDE;
+    virtual void drawBitmapNine(const SkBitmap& bitmap, const SkIRect& center,
+                                const SkRect& dst, const SkPaint* paint = NULL) SK_OVERRIDE;
     virtual void drawSprite(const SkBitmap&, int left, int top,
-                            const SkPaint*);
+                            const SkPaint*) SK_OVERRIDE;
     virtual void drawText(const void* text, size_t byteLength, SkScalar x,
-                          SkScalar y, const SkPaint&);
+                          SkScalar y, const SkPaint&) SK_OVERRIDE;
     virtual void drawPosText(const void* text, size_t byteLength,
-                             const SkPoint pos[], const SkPaint&);
+                             const SkPoint pos[], const SkPaint&) SK_OVERRIDE;
     virtual void drawPosTextH(const void* text, size_t byteLength,
-                      const SkScalar xpos[], SkScalar constY, const SkPaint&);
+                              const SkScalar xpos[], SkScalar constY,
+                              const SkPaint&) SK_OVERRIDE;
     virtual void drawTextOnPath(const void* text, size_t byteLength,
                             const SkPath& path, const SkMatrix* matrix,
-                                const SkPaint&);
-    virtual void drawPicture(SkPicture& picture);
+                                const SkPaint&) SK_OVERRIDE;
+    virtual void drawPicture(SkPicture& picture) SK_OVERRIDE;
     virtual void drawVertices(VertexMode, int vertexCount,
                           const SkPoint vertices[], const SkPoint texs[],
                           const SkColor colors[], SkXfermode*,
                           const uint16_t indices[], int indexCount,
-                              const SkPaint&);
-    virtual void drawData(const void*, size_t);
+                              const SkPaint&) SK_OVERRIDE;
+    virtual void drawData(const void*, size_t) SK_OVERRIDE;
 
+    /**
+     * Flatten an SkBitmap to send to the reader, where it will be referenced
+     * according to slot.
+     */
+    bool shuttleBitmap(const SkBitmap&, int32_t slot);
 private:
-    SkFactorySet* fFactorySet;  // optional, only used if cross-process
+    enum {
+        kNoSaveLayer = -1,
+    };
+    SkNamedFactorySet* fFactorySet;
+    int                fFirstSaveLayerStackLevel;
+    SkBitmapHeap*      fBitmapHeap;
     SkGPipeController* fController;
-    SkWriter32& fWriter;
-    size_t      fBlockSize; // amount allocated for writer
-    size_t      fBytesNotified;
-    bool        fDone;
+    SkWriter32&        fWriter;
+    size_t             fBlockSize; // amount allocated for writer
+    size_t             fBytesNotified;
+    bool               fDone;
+    const uint32_t     fFlags;
 
-    SkRefCntSet fTypefaceSet;
+    SkRefCntSet        fTypefaceSet;
 
     uint32_t getTypefaceID(SkTypeface*);
 
@@ -154,25 +290,27 @@
     inline void doNotify() {
         if (!fDone) {
             size_t bytes = fWriter.size() - fBytesNotified;
-            fController->notifyWritten(bytes);
-            fBytesNotified += bytes;
+            if (bytes > 0) {
+                fController->notifyWritten(bytes);
+                fBytesNotified += bytes;
+            }
         }
     }
 
-    struct FlatData {
-        uint32_t    fIndex; // always > 0
-        uint32_t    fSize;
+    // Should be called after any calls to an SkFlatDictionary::findAndReplace
+    // if a new SkFlatData was added when in cross process mode
+    void flattenFactoryNames();
 
-        void*       data() { return (char*)this + sizeof(*this); }
-
-        static int Compare(const FlatData* a, const FlatData* b) {
-            return memcmp(&a->fSize, &b->fSize, a->fSize + sizeof(a->fSize));
-        }
-    };
-    SkTDArray<FlatData*> fFlatArray;
+    FlattenableHeap fFlattenableHeap;
+    FlatDictionary  fFlatDictionary;
     int fCurrFlatIndex[kCount_PaintFlats];
     int flattenToIndex(SkFlattenable* obj, PaintFlats);
 
+    // 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);
+
     SkPaint fPaint;
     void writePaint(const SkPaint&);
 
@@ -188,71 +326,134 @@
     typedef SkCanvas INHERITED;
 };
 
+void SkGPipeCanvas::flattenFactoryNames() {
+    const char* name;
+    while ((name = fFactorySet->getNextAddedFactoryName()) != NULL) {
+        size_t len = strlen(name);
+        if (this->needOpBytes(len)) {
+            this->writeOp(kDef_Factory_DrawOp);
+            fWriter.writeString(name, len);
+        }
+    }
+}
+
+bool SkGPipeCanvas::shuttleBitmap(const SkBitmap& bm, int32_t slot) {
+    SkASSERT(shouldFlattenBitmaps(fFlags));
+    SkOrderedWriteBuffer buffer(1024);
+    buffer.setNamedFactoryRecorder(fFactorySet);
+    bm.flatten(buffer);
+    this->flattenFactoryNames();
+    uint32_t size = buffer.size();
+    if (this->needOpBytes(size)) {
+        this->writeOp(kDef_Bitmap_DrawOp, 0, slot);
+        void* dst = static_cast<void*>(fWriter.reserve(size));
+        buffer.writeToMemory(dst);
+        return true;
+    }
+    return false;
+}
+
 // return 0 for NULL (or unflattenable obj), or index-base-1
+// return ~(index-base-1) if an old flattenable was replaced
 int SkGPipeCanvas::flattenToIndex(SkFlattenable* obj, PaintFlats paintflat) {
+    SkASSERT(!fDone && fBitmapHeap != NULL);
     if (NULL == obj) {
         return 0;
     }
 
-    SkFlattenableWriteBuffer tmpWriter(1024);
-    tmpWriter.setFlags(SkFlattenableWriteBuffer::kInlineFactoryNames_Flag);
-    tmpWriter.setFactoryRecorder(fFactorySet);
-
-    tmpWriter.writeFlattenable(obj);
-    size_t len = tmpWriter.size();
-    size_t allocSize = len + sizeof(FlatData);
-
-    SkAutoSMalloc<1024> storage(allocSize);
-    FlatData* flat = (FlatData*)storage.get();
-    flat->fSize = len;
-    tmpWriter.flatten(flat->data());
-
-    int index = SkTSearch<FlatData>((const FlatData**)fFlatArray.begin(),
-                                    fFlatArray.count(), flat, sizeof(flat),
-                                    &FlatData::Compare);
-    if (index < 0) {
-        index = ~index;
-        FlatData* copy = (FlatData*)sk_malloc_throw(allocSize);
-        memcpy(copy, flat, allocSize);
-        *fFlatArray.insert(index) = copy;
-        // call this after the insert, so that count() will have been grown
-        copy->fIndex = fFlatArray.count();
-//        SkDebugf("--- add flattenable[%d] size=%d index=%d\n", paintflat, len, copy->fIndex);
-
-        if (this->needOpBytes(len)) {
-            this->writeOp(kDef_Flattenable_DrawOp, paintflat, copy->fIndex);
-            fWriter.write(copy->data(), len);
+    fBitmapHeap->deferAddingOwners();
+    bool added, replaced;
+    const SkFlatData* flat = fFlatDictionary.findAndReplace(*obj, fFlattenableHeap.flatToReplace(),
+                                                            &added, &replaced);
+    fBitmapHeap->endAddingOwnersDeferral(added);
+    int index = flat->index();
+    if (added) {
+        if (isCrossProcess(fFlags)) {
+            this->flattenFactoryNames();
+        }
+        size_t flatSize = flat->flatSize();
+        if (this->needOpBytes(flatSize)) {
+            this->writeOp(kDef_Flattenable_DrawOp, paintflat, index);
+            fWriter.write(flat->data(), flatSize);
         }
     }
-    return fFlatArray[index]->fIndex;
+    if (replaced) {
+        index = ~index;
+    }
+    return index;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+/**
+ * If SkBitmaps are to be flattened to send to the reader, this class is
+ * provided to the SkBitmapHeap to tell the SkGPipeCanvas to do so.
+ */
+class BitmapShuttle : public SkBitmapHeap::ExternalStorage {
+public:
+    BitmapShuttle(SkGPipeCanvas*);
+
+    ~BitmapShuttle();
+
+    virtual bool insert(const SkBitmap& bitmap, int32_t slot) SK_OVERRIDE;
+
+private:
+    SkGPipeCanvas*    fCanvas;
+};
+
+///////////////////////////////////////////////////////////////////////////////
+
 #define MIN_BLOCK_SIZE  (16 * 1024)
+#define BITMAPS_TO_KEEP 5
+#define FLATTENABLES_TO_KEEP 10
 
 SkGPipeCanvas::SkGPipeCanvas(SkGPipeController* controller,
-                             SkWriter32* writer, SkFactorySet* fset)
-        : fWriter(*writer) {
-    fFactorySet = fset;
+                             SkWriter32* writer, uint32_t flags,
+                             uint32_t width, uint32_t height)
+: fFactorySet(isCrossProcess(flags) ? SkNEW(SkNamedFactorySet) : NULL)
+, fWriter(*writer)
+, fFlags(flags)
+, fFlattenableHeap(FLATTENABLES_TO_KEEP, fFactorySet, isCrossProcess(flags))
+, fFlatDictionary(&fFlattenableHeap) {
     fController = controller;
     fDone = false;
     fBlockSize = 0; // need first block from controller
+    fBytesNotified = 0;
+    fFirstSaveLayerStackLevel = kNoSaveLayer;
     sk_bzero(fCurrFlatIndex, sizeof(fCurrFlatIndex));
 
     // we need a device to limit our clip
-    // should the caller give us the bounds?
     // We don't allocate pixels for the bitmap
     SkBitmap bitmap;
-    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 32767, 32767);
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height);
     SkDevice* device = SkNEW_ARGS(SkDevice, (bitmap));
     this->setDevice(device)->unref();
+
+    // Tell the reader the appropriate flags to use.
+    if (this->needOpBytes()) {
+        this->writeOp(kReportFlags_DrawOp, fFlags, 0);
+    }
+
+    if (shouldFlattenBitmaps(flags)) {
+        BitmapShuttle* shuttle = SkNEW_ARGS(BitmapShuttle, (this));
+        fBitmapHeap = SkNEW_ARGS(SkBitmapHeap, (shuttle, BITMAPS_TO_KEEP));
+        shuttle->unref();
+    } else {
+        fBitmapHeap = SkNEW_ARGS(SkBitmapHeap,
+                                 (BITMAPS_TO_KEEP, controller->numberOfReaders()));
+        if (this->needOpBytes(sizeof(void*))) {
+            this->writeOp(kShareBitmapHeap_DrawOp);
+            fWriter.writePtr(static_cast<void*>(fBitmapHeap));
+        }
+    }
+    fFlattenableHeap.setBitmapStorage(fBitmapHeap);
+    this->doNotify();
 }
 
 SkGPipeCanvas::~SkGPipeCanvas() {
     this->finish();
-
-    fFlatArray.freeAll();
+    SkSafeUnref(fFactorySet);
+    SkSafeUnref(fBitmapHeap);
 }
 
 bool SkGPipeCanvas::needOpBytes(size_t needed) {
@@ -262,11 +463,16 @@
 
     needed += 4;  // size of DrawOp atom
     if (fWriter.size() + needed > fBlockSize) {
-        void* block = fController->requestBlock(MIN_BLOCK_SIZE, &fBlockSize);
+        // Before we wipe out any data that has already been written, read it
+        // out.
+        this->doNotify();
+        size_t blockSize = SkMax32(MIN_BLOCK_SIZE, needed);
+        void* block = fController->requestBlock(blockSize, &fBlockSize);
         if (NULL == block) {
             fDone = true;
             return false;
         }
+        SkASSERT(SkIsAlign4(fBlockSize));
         fWriter.reset(block, fBlockSize);
         fBytesNotified = 0;
     }
@@ -324,6 +530,9 @@
         }
     }
 
+    if (kNoSaveLayer == fFirstSaveLayerStackLevel){
+        fFirstSaveLayerStackLevel = this->getSaveCount();
+    }
     // we just pass on the save, so we don't create a layer
     return this->INHERITED::save(saveFlags);
 }
@@ -333,7 +542,16 @@
     if (this->needOpBytes()) {
         this->writeOp(kRestore_DrawOp);
     }
+
     this->INHERITED::restore();
+
+    if (this->getSaveCount() == fFirstSaveLayerStackLevel){
+        fFirstSaveLayerStackLevel = kNoSaveLayer;
+    }
+}
+
+bool SkGPipeCanvas::isDrawingToLayer() const {
+    return kNoSaveLayer != fFirstSaveLayerStackLevel;
 }
 
 bool SkGPipeCanvas::translate(SkScalar dx, SkScalar dy) {
@@ -386,9 +604,9 @@
 bool SkGPipeCanvas::concat(const SkMatrix& matrix) {
     if (!matrix.isIdentity()) {
         NOTIFY_SETUP(this);
-        if (this->needOpBytes(matrix.flatten(NULL))) {
+        if (this->needOpBytes(matrix.writeToMemory(NULL))) {
             this->writeOp(kConcat_DrawOp);
-            SkWriteMatrix(&fWriter, matrix);
+            fWriter.writeMatrix(matrix);
         }
     }
     return this->INHERITED::concat(matrix);
@@ -396,37 +614,52 @@
 
 void SkGPipeCanvas::setMatrix(const SkMatrix& matrix) {
     NOTIFY_SETUP(this);
-    if (this->needOpBytes(matrix.flatten(NULL))) {
+    if (this->needOpBytes(matrix.writeToMemory(NULL))) {
         this->writeOp(kSetMatrix_DrawOp);
-        SkWriteMatrix(&fWriter, matrix);
+        fWriter.writeMatrix(matrix);
     }
     this->INHERITED::setMatrix(matrix);
 }
 
-bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp) {
+bool SkGPipeCanvas::clipRect(const SkRect& rect, SkRegion::Op rgnOp,
+                             bool doAntiAlias) {
     NOTIFY_SETUP(this);
     if (this->needOpBytes(sizeof(SkRect))) {
-        this->writeOp(kClipRect_DrawOp, 0, rgnOp);
+        unsigned flags = doAntiAlias & kClip_HasAntiAlias_DrawOpFlag;
+        this->writeOp(kClipRect_DrawOp, flags, rgnOp);
         fWriter.writeRect(rect);
     }
-    return this->INHERITED::clipRect(rect, rgnOp);
+    return this->INHERITED::clipRect(rect, rgnOp, doAntiAlias);
 }
 
-bool SkGPipeCanvas::clipPath(const SkPath& path, SkRegion::Op rgnOp) {
+bool SkGPipeCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op rgnOp,
+                              bool doAntiAlias) {
     NOTIFY_SETUP(this);
-    if (this->needOpBytes(estimateFlattenSize(path))) {
-        this->writeOp(kClipPath_DrawOp, 0, rgnOp);
-        path.flatten(fWriter);
+    if (this->needOpBytes(kSizeOfFlatRRect)) {
+        unsigned flags = doAntiAlias & kClip_HasAntiAlias_DrawOpFlag;
+        this->writeOp(kClipRRect_DrawOp, flags, rgnOp);
+        fWriter.writeRRect(rrect);
+    }
+    return this->INHERITED::clipRRect(rrect, rgnOp, doAntiAlias);
+}
+
+bool SkGPipeCanvas::clipPath(const SkPath& path, SkRegion::Op rgnOp,
+                             bool doAntiAlias) {
+    NOTIFY_SETUP(this);
+    if (this->needOpBytes(path.writeToMemory(NULL))) {
+        unsigned flags = doAntiAlias & kClip_HasAntiAlias_DrawOpFlag;
+        this->writeOp(kClipPath_DrawOp, flags, rgnOp);
+        fWriter.writePath(path);
     }
     // we just pass on the bounds of the path
-    return this->INHERITED::clipRect(path.getBounds(), rgnOp);
+    return this->INHERITED::clipRect(path.getBounds(), rgnOp, doAntiAlias);
 }
 
 bool SkGPipeCanvas::clipRegion(const SkRegion& region, SkRegion::Op rgnOp) {
     NOTIFY_SETUP(this);
-    if (this->needOpBytes(region.flatten(NULL))) {
+    if (this->needOpBytes(region.writeToMemory(NULL))) {
         this->writeOp(kClipRegion_DrawOp, 0, rgnOp);
-        SkWriteRegion(&fWriter, region);
+        fWriter.writeRegion(region);
     }
     return this->INHERITED::clipRegion(region, rgnOp);
 }
@@ -468,6 +701,15 @@
     }
 }
 
+void SkGPipeCanvas::drawOval(const SkRect& rect, const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes(sizeof(SkRect))) {
+        this->writeOp(kDrawOval_DrawOp);
+        fWriter.writeRect(rect);
+    }
+}
+
 void SkGPipeCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
     NOTIFY_SETUP(this);
     this->writePaint(paint);
@@ -477,33 +719,109 @@
     }
 }
 
-void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+void SkGPipeCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
     NOTIFY_SETUP(this);
     this->writePaint(paint);
-    if (this->needOpBytes(estimateFlattenSize(path))) {
-        this->writeOp(kDrawPath_DrawOp);
-        path.flatten(fWriter);
+    if (this->needOpBytes(kSizeOfFlatRRect)) {
+        this->writeOp(kDrawRRect_DrawOp);
+        fWriter.writeRRect(rrect);
     }
 }
 
-void SkGPipeCanvas::drawBitmap(const SkBitmap&, SkScalar left, SkScalar top,
-                                   const SkPaint*) {
-    UNIMPLEMENTED
+void SkGPipeCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
+    NOTIFY_SETUP(this);
+    this->writePaint(paint);
+    if (this->needOpBytes(path.writeToMemory(NULL))) {
+        this->writeOp(kDrawPath_DrawOp);
+        fWriter.writePath(path);
+    }
 }
 
-void SkGPipeCanvas::drawBitmapRect(const SkBitmap&, const SkIRect* src,
-                                       const SkRect& dst, const SkPaint*) {
-    UNIMPLEMENTED
+bool SkGPipeCanvas::commonDrawBitmap(const SkBitmap& bm, DrawOps op,
+                                     unsigned flags,
+                                     size_t opBytesNeeded,
+                                     const SkPaint* paint) {
+    if (paint != NULL) {
+        flags |= kDrawBitmap_HasPaint_DrawOpFlag;
+        this->writePaint(*paint);
+    }
+    if (this->needOpBytes(opBytesNeeded)) {
+        SkASSERT(fBitmapHeap != NULL);
+        int32_t bitmapIndex = fBitmapHeap->insert(bm);
+        if (SkBitmapHeap::INVALID_SLOT == bitmapIndex) {
+            return false;
+        }
+        this->writeOp(op, flags, bitmapIndex);
+        return true;
+    }
+    return false;
 }
 
-void SkGPipeCanvas::drawBitmapMatrix(const SkBitmap&, const SkMatrix&,
-                                         const SkPaint*) {
-    UNIMPLEMENTED
+void SkGPipeCanvas::drawBitmap(const SkBitmap& bm, SkScalar left, SkScalar top,
+                               const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    size_t opBytesNeeded = sizeof(SkScalar) * 2;
+
+    if (this->commonDrawBitmap(bm, kDrawBitmap_DrawOp, 0, opBytesNeeded, paint)) {
+        fWriter.writeScalar(left);
+        fWriter.writeScalar(top);
+    }
 }
 
-void SkGPipeCanvas::drawSprite(const SkBitmap&, int left, int top,
-                                   const SkPaint*) {
-    UNIMPLEMENTED
+void SkGPipeCanvas::drawBitmapRectToRect(const SkBitmap& bm, const SkRect* src,
+                                   const SkRect& dst, const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    size_t opBytesNeeded = sizeof(SkRect);
+    bool hasSrc = src != NULL;
+    unsigned flags;
+    if (hasSrc) {
+        flags = kDrawBitmap_HasSrcRect_DrawOpFlag;
+        opBytesNeeded += sizeof(int32_t) * 4;
+    } else {
+        flags = 0;
+    }
+
+    if (this->commonDrawBitmap(bm, kDrawBitmapRectToRect_DrawOp, flags, opBytesNeeded, paint)) {
+        if (hasSrc) {
+            fWriter.writeRect(*src);
+        }
+        fWriter.writeRect(dst);
+    }
+}
+
+void SkGPipeCanvas::drawBitmapMatrix(const SkBitmap& bm, const SkMatrix& matrix,
+                                     const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    size_t opBytesNeeded = matrix.writeToMemory(NULL);
+
+    if (this->commonDrawBitmap(bm, kDrawBitmapMatrix_DrawOp, 0, opBytesNeeded, paint)) {
+        fWriter.writeMatrix(matrix);
+    }
+}
+
+void SkGPipeCanvas::drawBitmapNine(const SkBitmap& bm, const SkIRect& center,
+                                   const SkRect& dst, const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    size_t opBytesNeeded = sizeof(int32_t) * 4 + sizeof(SkRect);
+
+    if (this->commonDrawBitmap(bm, kDrawBitmapNine_DrawOp, 0, opBytesNeeded, paint)) {
+        fWriter.write32(center.fLeft);
+        fWriter.write32(center.fTop);
+        fWriter.write32(center.fRight);
+        fWriter.write32(center.fBottom);
+        fWriter.writeRect(dst);
+    }
+}
+
+void SkGPipeCanvas::drawSprite(const SkBitmap& bm, int left, int top,
+                                   const SkPaint* paint) {
+    NOTIFY_SETUP(this);
+    size_t opBytesNeeded = sizeof(int32_t) * 2;
+
+    if (this->commonDrawBitmap(bm, kDrawSprite_DrawOp, 0, opBytesNeeded, paint)) {
+        fWriter.write32(left);
+        fWriter.write32(top);
+    }
 }
 
 void SkGPipeCanvas::drawText(const void* text, size_t byteLength, SkScalar x,
@@ -561,10 +879,10 @@
     if (byteLength) {
         NOTIFY_SETUP(this);
         unsigned flags = 0;
-        size_t size = 4 + SkAlign4(byteLength) + estimateFlattenSize(path);
+        size_t size = 4 + SkAlign4(byteLength) + path.writeToMemory(NULL);
         if (matrix) {
             flags |= kDrawTextOnPath_HasMatrix_DrawOpFlag;
-            size += matrix->flatten(NULL);
+            size += matrix->writeToMemory(NULL);
         }
         this->writePaint(paint);
         if (this->needOpBytes(size)) {
@@ -573,9 +891,9 @@
             fWriter.write32(byteLength);
             fWriter.writePad(text, byteLength);
 
-            path.flatten(fWriter);
+            fWriter.writePath(path);
             if (matrix) {
-                SkWriteMatrix(&fWriter, *matrix);
+                fWriter.writeMatrix(*matrix);
             }
         }
     }
@@ -650,6 +968,18 @@
     }
 }
 
+void SkGPipeCanvas::flushRecording(bool detachCurrentBlock) {
+    doNotify();
+    if (detachCurrentBlock) {
+        // force a new block to be requested for the next recorded command
+        fBlockSize = 0;
+    }
+}
+
+size_t SkGPipeCanvas::freeMemoryIfPossible(size_t bytesToFree) {
+    return (NULL == fBitmapHeap) ? 0 : fBitmapHeap->freeMemoryIfPossible(bytesToFree);
+}
+
 ///////////////////////////////////////////////////////////////////////////////
 
 template <typename T> uint32_t castToU32(T value) {
@@ -662,6 +992,9 @@
 }
 
 void SkGPipeCanvas::writePaint(const SkPaint& paint) {
+    if (fDone) {
+        return;
+    }
     SkPaint& base = fPaint;
     uint32_t storage[32];
     uint32_t* ptr = storage;
@@ -726,15 +1059,35 @@
     }
 
     if (!SkTypeface::Equal(base.getTypeface(), paint.getTypeface())) {
-        uint32_t id = this->getTypefaceID(paint.getTypeface());
-        *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id);
+        if (isCrossProcess(fFlags)) {
+            uint32_t id = this->getTypefaceID(paint.getTypeface());
+            *ptr++ = PaintOp_packOpData(kTypeface_PaintOp, id);
+        } else if (this->needOpBytes(sizeof(void*))) {
+            // Add to the set for ref counting.
+            fTypefaceSet.add(paint.getTypeface());
+            // It is safe to write the typeface to the stream before the rest
+            // of the paint unless we ever send a kReset_PaintOp, which we
+            // currently never do.
+            this->writeOp(kSetTypeface_DrawOp);
+            fWriter.writePtr(paint.getTypeface());
+        }
         base.setTypeface(paint.getTypeface());
     }
 
+    // This is a new paint, so all old flats can be safely purged, if necessary.
+    fFlattenableHeap.markAllFlatsSafeToDelete();
     for (int i = 0; i < kCount_PaintFlats; i++) {
         int index = this->flattenToIndex(get_paintflat(paint, i), (PaintFlats)i);
-        SkASSERT(index >= 0 && index <= fFlatArray.count());
-        if (index != fCurrFlatIndex[i]) {
+        bool replaced = index < 0;
+        if (replaced) {
+            index = ~index;
+        }
+        // Store the index of any flat that needs to be kept. 0 means no flat.
+        if (index > 0) {
+            fFlattenableHeap.markFlatForKeeping(index);
+        }
+        SkASSERT(index >= 0 && index <= fFlatDictionary.count());
+        if (index != fCurrFlatIndex[i] || replaced) {
             *ptr++ = PaintOp_packOpFlagData(kFlatIndex_PaintOp, i, index);
             fCurrFlatIndex[i] = index;
         }
@@ -754,24 +1107,32 @@
 
 #include "SkGPipe.h"
 
-SkGPipeWriter::SkGPipeWriter() : fWriter(0) {
+SkGPipeController::~SkGPipeController() {
+    SkSafeUnref(fCanvas);
+}
+
+void SkGPipeController::setCanvas(SkGPipeCanvas* canvas) {
+    SkRefCnt_SafeAssign(fCanvas, canvas);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkGPipeWriter::SkGPipeWriter()
+: fWriter(0) {
     fCanvas = NULL;
 }
 
 SkGPipeWriter::~SkGPipeWriter() {
     this->endRecording();
-    SkSafeUnref(fCanvas);
 }
 
-SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller,
-                                        uint32_t flags) {
+SkCanvas* SkGPipeWriter::startRecording(SkGPipeController* controller, uint32_t flags,
+                                        uint32_t width, uint32_t height) {
     if (NULL == fCanvas) {
         fWriter.reset(NULL, 0);
-        fFactorySet.reset();
-        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter,
-                                             (flags & kCrossProcess_Flag) ?
-                                             &fFactorySet : NULL));
+        fCanvas = SkNEW_ARGS(SkGPipeCanvas, (controller, &fWriter, flags, width, height));
     }
+    controller->setCanvas(fCanvas);
     return fCanvas;
 }
 
@@ -783,3 +1144,35 @@
     }
 }
 
+void SkGPipeWriter::flushRecording(bool detachCurrentBlock) {
+    if (fCanvas) {
+        fCanvas->flushRecording(detachCurrentBlock);
+    }
+}
+
+size_t SkGPipeWriter::freeMemoryIfPossible(size_t bytesToFree) {
+    if (fCanvas) {
+        return fCanvas->freeMemoryIfPossible(bytesToFree);
+    }
+    return 0;
+}
+
+size_t SkGPipeWriter::storageAllocatedForRecording() const {
+    return NULL == fCanvas ? 0 : fCanvas->storageAllocatedForRecording();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+BitmapShuttle::BitmapShuttle(SkGPipeCanvas* canvas) {
+    SkASSERT(canvas != NULL);
+    fCanvas = canvas;
+    fCanvas->ref();
+}
+
+BitmapShuttle::~BitmapShuttle() {
+    fCanvas->unref();
+}
+
+bool BitmapShuttle::insert(const SkBitmap& bitmap, int32_t slot) {
+    return fCanvas->shuttleBitmap(bitmap, slot);
+}
diff --git a/src/pipe/utils/SamplePipeControllers.cpp b/src/pipe/utils/SamplePipeControllers.cpp
new file mode 100644
index 0000000..98fdff3
--- /dev/null
+++ b/src/pipe/utils/SamplePipeControllers.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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 "SamplePipeControllers.h"
+
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGPipe.h"
+#include "SkMatrix.h"
+
+PipeController::PipeController(SkCanvas* target)
+:fReader(target) {
+    fBlock = NULL;
+    fBlockSize = fBytesWritten = 0;
+}
+
+PipeController::~PipeController() {
+    sk_free(fBlock);
+}
+
+void* PipeController::requestBlock(size_t minRequest, size_t *actual) {
+    sk_free(fBlock);
+    fBlockSize = minRequest * 4;
+    fBlock = sk_malloc_throw(fBlockSize);
+    fBytesWritten = 0;
+    *actual = fBlockSize;
+    return fBlock;
+}
+
+void PipeController::notifyWritten(size_t bytes) {
+    fStatus = fReader.playback(this->getData(), bytes);
+    SkASSERT(SkGPipeReader::kError_Status != fStatus);
+    fBytesWritten += bytes;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+TiledPipeController::TiledPipeController(const SkBitmap& bitmap,
+                                         const SkMatrix* initial)
+: INHERITED(NULL) {
+    int32_t top = 0;
+    int32_t bottom;
+    int32_t height = bitmap.height() / NumberOfTiles;
+    SkIRect rect;
+    for (int i = 0; i < NumberOfTiles; i++) {
+        bottom = i + 1 == NumberOfTiles ? bitmap.height() : top + height;
+        rect.setLTRB(0, top, bitmap.width(), bottom);
+        top = bottom;
+
+        SkDEBUGCODE(bool extracted = )bitmap.extractSubset(&fBitmaps[i], rect);
+        SkASSERT(extracted);
+        SkDevice* device = new SkDevice(fBitmaps[i]);
+        SkCanvas* canvas = new SkCanvas(device);
+        device->unref();
+        if (initial != NULL) {
+            canvas->setMatrix(*initial);
+        }
+        canvas->translate(SkIntToScalar(-rect.left()),
+                          SkIntToScalar(-rect.top()));
+        if (0 == i) {
+            fReader.setCanvas(canvas);
+        } else {
+            fReaders[i - 1].setCanvas(canvas);
+        }
+        canvas->unref();
+    }
+}
+
+void TiledPipeController::notifyWritten(size_t bytes) {
+    for (int i = 0; i < NumberOfTiles - 1; i++) {
+        fReaders[i].playback(this->getData(), bytes);
+    }
+    this->INHERITED::notifyWritten(bytes);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ThreadSafePipeController::ThreadSafePipeController(int numberOfReaders)
+: fAllocator(kMinBlockSize)
+, fNumberOfReaders(numberOfReaders) {
+    fBlock = NULL;
+    fBytesWritten = 0;
+}
+
+void* ThreadSafePipeController::requestBlock(size_t minRequest, size_t *actual) {
+    if (fBlock) {
+        // Save the previous block for later
+        PipeBlock previousBloc(fBlock, fBytesWritten);
+        fBlockList.push(previousBloc);
+    }
+    int32_t blockSize = SkMax32(minRequest, kMinBlockSize);
+    fBlock = fAllocator.allocThrow(blockSize);
+    fBytesWritten = 0;
+    *actual = blockSize;
+    return fBlock;
+}
+
+void ThreadSafePipeController::notifyWritten(size_t bytes) {
+    fBytesWritten += bytes;
+}
+
+void ThreadSafePipeController::draw(SkCanvas* target) {
+    SkGPipeReader reader(target);
+    for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
+        reader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fBytes);
+    }
+
+    if (fBlock) {
+        reader.playback(fBlock, fBytesWritten);
+    }
+}
diff --git a/src/pipe/utils/SamplePipeControllers.h b/src/pipe/utils/SamplePipeControllers.h
new file mode 100644
index 0000000..5efd6f0
--- /dev/null
+++ b/src/pipe/utils/SamplePipeControllers.h
@@ -0,0 +1,85 @@
+/*
+ * 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 "SkBitmap.h"
+#include "SkChunkAlloc.h"
+#include "SkGPipe.h"
+#include "SkTDArray.h"
+
+class SkCanvas;
+class SkMatrix;
+
+class PipeController : public SkGPipeController {
+public:
+    PipeController(SkCanvas* target);
+    virtual ~PipeController();
+    virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
+    virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+protected:
+    const void* getData() { return (const char*) fBlock + fBytesWritten; }
+    SkGPipeReader fReader;
+private:
+    void* fBlock;
+    size_t fBlockSize;
+    size_t fBytesWritten;
+    SkGPipeReader::Status fStatus;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class TiledPipeController : public PipeController {
+public:
+    TiledPipeController(const SkBitmap&, const SkMatrix* initialMatrix = NULL);
+    virtual ~TiledPipeController() {};
+    virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+    virtual int numberOfReaders() const SK_OVERRIDE { return NumberOfTiles; }
+private:
+    enum {
+        NumberOfTiles = 10
+    };
+    SkGPipeReader fReaders[NumberOfTiles - 1];
+    SkBitmap fBitmaps[NumberOfTiles];
+    typedef PipeController INHERITED;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Borrowed (and modified) from SkDeferredCanvas.cpp::DeferredPipeController.
+ * Allows playing back from multiple threads, but does not do the threading itself.
+ */
+class ThreadSafePipeController : public SkGPipeController {
+public:
+    ThreadSafePipeController(int numberOfReaders);
+    virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
+    virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+    virtual int numberOfReaders() const SK_OVERRIDE { return fNumberOfReaders; }
+
+    /**
+     * Play the stored drawing commands to the specified canvas. If SkGPipeWriter::startRecording
+     * used the flag SkGPipeWriter::kSimultaneousReaders_Flag, this can be called from different
+     * threads simultaneously.
+     */
+    void draw(SkCanvas*);
+private:
+    enum {
+        kMinBlockSize = 4096
+    };
+    struct PipeBlock {
+        PipeBlock(void* block, size_t bytes) { fBlock = block, fBytes = bytes; }
+        // Stream of draw commands written by the SkGPipeWriter. Allocated by fAllocator, which will
+        // handle freeing it.
+        void* fBlock;
+        // Number of bytes that were written to fBlock.
+        size_t fBytes;
+    };
+    void* fBlock;
+    size_t fBytesWritten;
+    SkChunkAlloc fAllocator;
+    SkTDArray<PipeBlock> fBlockList;
+    int fNumberOfReaders;
+};
diff --git a/src/ports/FontHostConfiguration_android.cpp b/src/ports/FontHostConfiguration_android.cpp
index 420ad1c..98928c6 100644
--- a/src/ports/FontHostConfiguration_android.cpp
+++ b/src/ports/FontHostConfiguration_android.cpp
@@ -1,19 +1,9 @@
-/* libs/graphics/ports/FontHostConfiguration_android.cpp
-**
-** Copyright 2011, 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.
-*/
+/*
+ * Copyright 2011 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 "FontHostConfiguration_android.h"
 #include "SkLanguage.h"
diff --git a/src/ports/FontHostConfiguration_android.h b/src/ports/FontHostConfiguration_android.h
index 6734b08..9f558bc 100644
--- a/src/ports/FontHostConfiguration_android.h
+++ b/src/ports/FontHostConfiguration_android.h
@@ -1,19 +1,10 @@
-/* libs/graphics/ports/FontHostConfiguration_android.h
-**
-** Copyright 2011, 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.
-*/
+/*
+ * Copyright 2011 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 FONTHOSTCONFIGURATION_ANDROID_H_
 #define FONTHOSTCONFIGURATION_ANDROID_H_
 
diff --git a/src/ports/SkDebug_android.cpp b/src/ports/SkDebug_android.cpp
index 8e7d1d4..b9b5665 100644
--- a/src/ports/SkDebug_android.cpp
+++ b/src/ports/SkDebug_android.cpp
@@ -14,9 +14,22 @@
 #define LOG_TAG "skia"
 #include <android/log.h>
 
+static bool gSkDebugToStdOut = false;
+
+extern "C" void AndroidSkDebugToStdOut(bool debugToStdOut) {
+    gSkDebugToStdOut = debugToStdOut;
+}
+
 void SkDebugf(const char format[], ...) {
     va_list args;
     va_start(args, format);
     __android_log_vprint(ANDROID_LOG_DEBUG, LOG_TAG, format, args);
+
+    // Print debug output to stdout as well.  This is useful for command
+    // line applications (e.g. skia_launcher)
+    if (gSkDebugToStdOut) {
+        vprintf(format, args);
+    }
+
     va_end(args);
 }
diff --git a/src/ports/SkDebug_nacl.cpp b/src/ports/SkDebug_nacl.cpp
new file mode 100644
index 0000000..6e35f09
--- /dev/null
+++ b/src/ports/SkDebug_nacl.cpp
@@ -0,0 +1,38 @@
+
+/*
+ * 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 "SkTypes.h"
+
+static const size_t kBufferSize = 2048;
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "ppapi/cpp/instance.h"
+#include "ppapi/cpp/var.h"
+
+extern pp::Instance* gPluginInstance;
+
+namespace {
+static const char* kLogPrefix = "SkDebugf:";
+}
+
+void SkDebugf(const char format[], ...) {
+    if (gPluginInstance) {
+        char buffer[kBufferSize + 1];
+        va_list args;
+        va_start(args, format);
+        sprintf(buffer, kLogPrefix);
+        vsnprintf(buffer + strlen(kLogPrefix), kBufferSize, format, args);
+        va_end(args);
+        pp::Var msg = pp::Var(buffer);
+        gPluginInstance->PostMessage(msg);
+    }
+}
diff --git a/src/ports/SkDebug_stdio.cpp b/src/ports/SkDebug_stdio.cpp
index bc3d98b..318269e 100644
--- a/src/ports/SkDebug_stdio.cpp
+++ b/src/ports/SkDebug_stdio.cpp
@@ -22,4 +22,3 @@
     va_end(args);
     fprintf(stderr, "%s", buffer);
 }
-
diff --git a/src/ports/SkDebug_win.cpp b/src/ports/SkDebug_win.cpp
index b3077f1..4b340b0 100644
--- a/src/ports/SkDebug_win.cpp
+++ b/src/ports/SkDebug_win.cpp
@@ -26,4 +26,3 @@
     OutputDebugStringA(buffer);
     printf(buffer);
 }
-
diff --git a/src/ports/SkFontHost_FONTPATH.cpp b/src/ports/SkFontHost_FONTPATH.cpp
index bd8f102..3b5e993 100644
--- a/src/ports/SkFontHost_FONTPATH.cpp
+++ b/src/ports/SkFontHost_FONTPATH.cpp
@@ -14,14 +14,14 @@
 #include <stdio.h>
 
 /* define this if we can use mmap() to access fonts from the filesystem */
-#define SK_CAN_USE_MMAP 
+#define SK_CAN_USE_MMAP
 
 #ifndef SK_FONTPATH
     #define SK_FONTPATH "the complete path for a font file"
 #endif
 
 struct FontFaceRec {
-    const char* fFileName;    
+    const char* fFileName;
     uint8_t     fFamilyIndex;
     SkBool8     fBold;
     SkBool8     fItalic;
@@ -39,7 +39,7 @@
                                          int isBold, int isItalic)
 {
     SkASSERT(count > 0);
-    
+
     int i;
 
     // look for an exact match
@@ -63,7 +63,7 @@
 
 enum {
     DEFAULT_FAMILY_INDEX,
-    
+
     FAMILY_INDEX_COUNT
 };
 
@@ -233,11 +233,10 @@
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style)
 {
     const FontFamilyRec* family;
-    
+
     if (familyFace)
         family = &gFamilies[
                     ((FontFaceRec_Typeface*)familyFace)->fFace.fFamilyIndex];
@@ -264,7 +263,9 @@
 // static
 SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
         uint32_t fontID,
-        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
     sk_throw();  // not implemented
     return NULL;
 }
@@ -319,4 +320,3 @@
 
     return SkFontHost::CreateScalerContext(desc);
 }
-
diff --git a/src/ports/SkFontHost_FreeType.cpp b/src/ports/SkFontHost_FreeType.cpp
index 6c03bf9..2ed97cd 100644
--- a/src/ports/SkFontHost_FreeType.cpp
+++ b/src/ports/SkFontHost_FreeType.cpp
@@ -6,14 +6,17 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkColorPriv.h"
 #include "SkDescriptor.h"
 #include "SkFDot6.h"
+#include "SkFloatingPoint.h"
 #include "SkFontHost.h"
+#include "SkFontHost_FreeType_common.h"
+#include "SkGlyph.h"
 #include "SkMask.h"
+#include "SkMaskGamma.h"
 #include "SkAdvancedTypefaceMetrics.h"
 #include "SkScalerContext.h"
 #include "SkStream.h"
@@ -21,6 +24,9 @@
 #include "SkTemplates.h"
 #include "SkThread.h"
 
+#if defined(SK_CAN_USE_DLOPEN)
+#include <dlfcn.h>
+#endif
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_OUTLINE_H
@@ -56,33 +62,6 @@
 
 //#define SK_GAMMA_APPLY_TO_A8
 
-#ifndef SK_GAMMA_CONTRAST
-    #define SK_GAMMA_CONTRAST   0x66
-#endif
-#ifndef SK_GAMMA_EXPONENT
-    #define SK_GAMMA_EXPONENT   2.2
-#endif
-
-// hand-tuned value to reduce outline embolden strength
-#ifndef SK_OUTLINE_EMBOLDEN_DIVISOR
-    #ifdef SK_BUILD_FOR_ANDROID
-        #define SK_OUTLINE_EMBOLDEN_DIVISOR   34
-    #else
-        #define SK_OUTLINE_EMBOLDEN_DIVISOR   24
-    #endif
-#endif
-
-
-#ifdef SK_DEBUG
-    #define SkASSERT_CONTINUE(pred)                                                         \
-        do {                                                                                \
-            if (!(pred))                                                                    \
-                SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__);    \
-        } while (false)
-#else
-    #define SkASSERT_CONTINUE(pred)
-#endif
-
 using namespace skia_advanced_typeface_metrics_utils;
 
 static bool isLCD(const SkScalerContext::Rec& rec) {
@@ -107,34 +86,55 @@
 static bool         gLCDSupport;  // true iff LCD is supported by the runtime.
 static int          gLCDExtra;  // number of extra pixels for filtering.
 
-static const uint8_t* gGammaTables[2];
-
 /////////////////////////////////////////////////////////////////////////
 
-// See http://freetype.sourceforge.net/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden
-// This value was chosen by eyeballing the result in Firefox and trying to match it.
-static const FT_Pos kBitmapEmboldenStrength = 1 << 6;
+// FT_Library_SetLcdFilterWeights was introduced in FreeType 2.4.0.
+// The following platforms provide FreeType of at least 2.4.0.
+// Ubuntu >= 11.04 (previous deprecated April 2013)
+// Debian >= 6.0 (good)
+// OpenSuse >= 11.4 (previous deprecated January 2012 / Nov 2013 for Evergreen 11.2)
+// Fedora >= 14 (good)
+// Android >= Gingerbread (good)
+typedef FT_Error (*FT_Library_SetLcdFilterWeightsProc)(FT_Library, unsigned char*);
 
-// convert from Skia's fixed (16.16) to FreeType's fixed (26.6) representation
-static inline int FixedToDot6(SkFixed x) { return x >> 10; }
-// convert from FreeType's fixed (26.6) to Skia's fixed (16.16) representation
-static inline SkFixed Dot6ToFixed(int x) { return x << 10; }
-
-static bool
-InitFreetype() {
+// Caller must lock gFTMutex before calling this function.
+static bool InitFreetype() {
     FT_Error err = FT_Init_FreeType(&gFTLibrary);
     if (err) {
         return false;
     }
 
-    // Setup LCD filtering. This reduces colour fringes for LCD rendered
-    // glyphs.
+    // Setup LCD filtering. This reduces color fringes for LCD smoothed glyphs.
 #ifdef FT_LCD_FILTER_H
-//    err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT);
-    err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_LIGHT);
-    gLCDSupport = err == 0;
-    if (gLCDSupport) {
-        gLCDExtra = 2; //DEFAULT and LIGHT add one pixel to each side.
+    // Use default { 0x10, 0x40, 0x70, 0x40, 0x10 }, as it adds up to 0x110, simulating ink spread.
+    // SetLcdFilter must be called before SetLcdFilterWeights.
+    err = FT_Library_SetLcdFilter(gFTLibrary, FT_LCD_FILTER_DEFAULT);
+    if (0 == err) {
+        gLCDSupport = true;
+        gLCDExtra = 2; //Using a filter adds one full pixel to each side.
+
+#ifdef SK_FONTHOST_FREETYPE_USE_NORMAL_LCD_FILTER
+        // This also adds to 0x110 simulating ink spread, but provides better results than default.
+        static unsigned char gGaussianLikeHeavyWeights[] = { 0x1A, 0x43, 0x56, 0x43, 0x1A, };
+
+#if defined(SK_FONTHOST_FREETYPE_RUNTIME_VERSION) && \
+            SK_FONTHOST_FREETYPE_RUNTIME_VERSION > 0x020400
+        err = FT_Library_SetLcdFilterWeights(gFTLibrary, gGaussianLikeHeavyWeights);
+#elif defined(SK_CAN_USE_DLOPEN) && SK_CAN_USE_DLOPEN == 1
+        //The FreeType library is already loaded, so symbols are available in process.
+        void* self = dlopen(NULL, RTLD_LAZY);
+        if (NULL != self) {
+            FT_Library_SetLcdFilterWeightsProc setLcdFilterWeights;
+            //The following cast is non-standard, but safe for POSIX.
+            *reinterpret_cast<void**>(&setLcdFilterWeights) = dlsym(self, "FT_Library_SetLcdFilterWeights");
+            dlclose(self);
+
+            if (NULL != setLcdFilterWeights) {
+                err = setLcdFilterWeights(gFTLibrary, gGaussianLikeHeavyWeights);
+            }
+        }
+#endif
+#endif
     }
 #else
     gLCDSupport = false;
@@ -144,7 +144,20 @@
     return true;
 }
 
-class SkScalerContext_FreeType : public SkScalerContext {
+// Lazy, once, wrapper to ask the FreeType Library if it can support LCD text
+static bool is_lcd_supported() {
+    if (!gLCDSupportValid) {
+        SkAutoMutexAcquire  ac(gFTMutex);
+
+        if (!gLCDSupportValid) {
+            InitFreetype();
+            FT_Done_FreeType(gFTLibrary);
+        }
+    }
+    return gLCDSupport;
+}
+
+class SkScalerContext_FreeType : public SkScalerContext_FreeType_Base {
 public:
     SkScalerContext_FreeType(const SkDescriptor* desc);
     virtual ~SkScalerContext_FreeType();
@@ -156,15 +169,15 @@
     }
 
 protected:
-    virtual unsigned generateGlyphCount();
-    virtual uint16_t generateCharToGlyph(SkUnichar uni);
-    virtual void generateAdvance(SkGlyph* glyph);
-    virtual void generateMetrics(SkGlyph* glyph);
-    virtual void generateImage(const SkGlyph& glyph);
-    virtual void generatePath(const SkGlyph& glyph, SkPath* path);
+    virtual unsigned generateGlyphCount() SK_OVERRIDE;
+    virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
+    virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
     virtual void generateFontMetrics(SkPaint::FontMetrics* mx,
-                                     SkPaint::FontMetrics* my);
-    virtual SkUnichar generateGlyphToChar(uint16_t glyph);
+                                     SkPaint::FontMetrics* my) SK_OVERRIDE;
+    virtual SkUnichar generateGlyphToChar(uint16_t glyph) SK_OVERRIDE;
 
 private:
     SkFaceRec*  fFaceRec;
@@ -174,12 +187,12 @@
     FT_Matrix   fMatrix22;
     uint32_t    fLoadGlyphFlags;
     bool        fDoLinearMetrics;
-    bool        fUseVertMetrics;
+    bool        fLCDIsVert;
 
     FT_Error setupSize();
-    void emboldenOutline(FT_Outline* outline);
     void getBBoxForCurrentGlyph(SkGlyph* glyph, FT_BBox* bbox,
                                 bool snapToPixelBoundary = false);
+    // Caller must lock gFTMutex before calling this function.
     void updateGlyphIfLCD(SkGlyph* glyph);
 };
 
@@ -235,7 +248,7 @@
 }
 
 SkFaceRec::SkFaceRec(SkStream* strm, uint32_t fontID)
-        : fSkStream(strm), fFontID(fontID) {
+        : fNext(NULL), fSkStream(strm), fRefCnt(1), fFontID(fontID) {
 //    SkDEBUGF(("SkFaceRec: opening %s (%p)\n", key.c_str(), strm));
 
     sk_bzero(&fFTStream, sizeof(fFTStream));
@@ -246,6 +259,7 @@
 }
 
 // Will return 0 on failure
+// Caller must lock gFTMutex before calling this function.
 static SkFaceRec* ref_ft_face(uint32_t fontID) {
     SkFaceRec* rec = gFaceRecHead;
     while (rec) {
@@ -295,11 +309,11 @@
         //fprintf(stderr, "Opened font '%s'\n", filename.c_str());
         rec->fNext = gFaceRecHead;
         gFaceRecHead = rec;
-        rec->fRefCnt = 1;
         return rec;
     }
 }
 
+// Caller must lock gFTMutex before calling this function.
 static void unref_ft_face(FT_Face face) {
     SkFaceRec*  rec = gFaceRecHead;
     SkFaceRec*  prev = NULL;
@@ -574,7 +588,7 @@
                 getAdvances(face, gID, advanceCount, FT_LOAD_NO_SCALE,
                             advances);
                 for (int i = 0; i < advanceCount; i++) {
-                    int16_t advance = advances[gID + i];
+                    int16_t advance = advances[i];
                     info->fGlyphWidths->fAdvance.append(1, &advance);
                 }
             }
@@ -639,13 +653,15 @@
             bothZero(rec.fPost2x2[0][0], rec.fPost2x2[1][1]));
 }
 
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
-    if (!gLCDSupportValid) {
-        InitFreetype();
-        FT_Done_FreeType(gFTLibrary);
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
+    //BOGUS: http://code.google.com/p/chromium/issues/detail?id=121119
+    //Cap the requested size as larger sizes give bogus values.
+    //Remove when http://code.google.com/p/skia/issues/detail?id=554 is fixed.
+    if (rec->fTextSize > SkIntToScalar(1 << 14)) {
+        rec->fTextSize = SkIntToScalar(1 << 14);
     }
 
-    if (!gLCDSupport && isLCD(*rec)) {
+    if (!is_lcd_supported() && isLCD(*rec)) {
         // If the runtime Freetype library doesn't support LCD mode, we disable
         // it here.
         rec->fMaskFormat = SkMask::kA8_Format;
@@ -656,39 +672,21 @@
         // collapse full->normal hinting if we're not doing LCD
         h = SkPaint::kNormal_Hinting;
     }
-    if ((rec->fFlags & SkScalerContext::kSubpixelPositioning_Flag) || isLCD(*rec)) {
+    if ((rec->fFlags & SkScalerContext::kSubpixelPositioning_Flag)) {
         if (SkPaint::kNo_Hinting != h) {
             h = SkPaint::kSlight_Hinting;
         }
     }
 
-#ifndef SK_IGNORE_ROTATED_FREETYPE_FIX
     // rotated text looks bad with hinting, so we disable it as needed
     if (!isAxisAligned(*rec)) {
         h = SkPaint::kNo_Hinting;
     }
-#endif
     rec->setHinting(h);
 
-#ifndef SK_USE_COLOR_LUMINANCE
-    // for compatibility at the moment, discretize luminance to 3 settings
-    // black, white, gray. This helps with fontcache utilization, since we
-    // won't create multiple entries that in the end map to the same results.
-    {
-        unsigned lum = rec->getLuminanceByte();
-        if (gGammaTables[0] || gGammaTables[1]) {
-            if (lum <= BLACK_LUMINANCE_LIMIT) {
-                lum = 0;
-            } else if (lum >= WHITE_LUMINANCE_LIMIT) {
-                lum = SkScalerContext::kLuminance_Max;
-            } else {
-                lum = SkScalerContext::kLuminance_Max >> 1;
-            }
-        } else {
-            lum = 0;    // no gamma correct, so use 0 since SkPaint uses that
-                        // when measuring text w/o regard for luminance
-        }
-        rec->setLuminanceBits(lum);
+#ifndef SK_GAMMA_APPLY_TO_A8
+    if (!isLCD(*rec)) {
+      rec->ignorePreBlend();
     }
 #endif
 }
@@ -716,14 +714,13 @@
 #endif
 
 SkScalerContext_FreeType::SkScalerContext_FreeType(const SkDescriptor* desc)
-        : SkScalerContext(desc) {
+        : SkScalerContext_FreeType_Base(desc) {
     SkAutoMutexAcquire  ac(gFTMutex);
 
     if (gFTCount == 0) {
         if (!InitFreetype()) {
             sk_throw();
         }
-        SkFontHost::GetGammaTables(gGammaTables);
     }
     ++gFTCount;
 
@@ -774,14 +771,32 @@
         fMatrix22.xy = fMatrix22.yx = 0;
     }
 
+#ifdef SK_SUPPORT_HINTING_SCALE_FACTOR
+    if (fRec.getHinting() == SkPaint::kNo_Hinting) {
+        fScaleX = SkScalarToFixed(sx);
+        fScaleY = SkScalarToFixed(sy);
+    } else {
+        SkScalar hintingScaleFactor = fRec.fHintingScaleFactor;
+
+        fScaleX = SkScalarToFixed(sx / hintingScaleFactor);
+        fScaleY = SkScalarToFixed(sy / hintingScaleFactor);
+
+        fMatrix22.xx *= hintingScaleFactor;
+        fMatrix22.xy *= hintingScaleFactor;
+        fMatrix22.yx *= hintingScaleFactor;
+        fMatrix22.yy *= hintingScaleFactor;
+    }
+#else
     fScaleX = SkScalarToFixed(sx);
     fScaleY = SkScalarToFixed(sy);
+#endif
+
+    fLCDIsVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
 
     // compute the flags we send to Load_Glyph
-    fUseVertMetrics = false;
     {
         FT_Int32 loadFlags = FT_LOAD_DEFAULT;
-        bool linearMetrics = false;
+        bool linearMetrics = SkToBool(fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag);
 
         if (SkMask::kBW_Format == fRec.fMaskFormat) {
             // See http://code.google.com/p/chromium/issues/detail?id=43252#c24
@@ -798,7 +813,6 @@
                 break;
             case SkPaint::kSlight_Hinting:
                 loadFlags = FT_LOAD_TARGET_LIGHT;  // This implies FORCE_AUTOHINT
-                linearMetrics = true;
                 break;
             case SkPaint::kNormal_Hinting:
                 if (fRec.fFlags & SkScalerContext::kAutohinting_Flag)
@@ -813,7 +827,7 @@
                 }
                 loadFlags = FT_LOAD_TARGET_NORMAL;
                 if (isLCD(fRec)) {
-                    if (fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag) {
+                    if (fLCDIsVert) {
                         loadFlags = FT_LOAD_TARGET_LCD_V;
                     } else {
                         loadFlags = FT_LOAD_TARGET_LCD;
@@ -835,10 +849,9 @@
         // See http://code.google.com/p/skia/issues/detail?id=222.
         loadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
 
-        // Use vertical layout if requested and supported.
-        if ((fRec.fFlags & SkScalerContext::kVertical_Flag) && FT_HAS_VERTICAL(fFace)) {
+        // Use vertical layout if requested.
+        if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
             loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
-            fUseVertMetrics = true;
         }
 
         fLoadGlyphFlags = loadFlags;
@@ -880,12 +893,12 @@
 }
 
 SkScalerContext_FreeType::~SkScalerContext_FreeType() {
+    SkAutoMutexAcquire  ac(gFTMutex);
+
     if (fFTSize != NULL) {
         FT_Done_Size(fFTSize);
     }
 
-    SkAutoMutexAcquire  ac(gFTMutex);
-
     if (fFace != NULL) {
         unref_ft_face(fFace);
     }
@@ -914,13 +927,6 @@
     return err;
 }
 
-void SkScalerContext_FreeType::emboldenOutline(FT_Outline* outline) {
-    FT_Pos strength;
-    strength = FT_MulFix(fFace->units_per_EM, fFace->size->metrics.y_scale)
-               / SK_OUTLINE_EMBOLDEN_DIVISOR;
-    FT_Outline_Embolden(outline, strength);
-}
-
 unsigned SkScalerContext_FreeType::generateGlyphCount() {
     return fFace->num_glyphs;
 }
@@ -944,16 +950,6 @@
     return 0;
 }
 
-static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
-    switch (format) {
-        case SkMask::kBW_Format:
-            return FT_PIXEL_MODE_MONO;
-        case SkMask::kA8_Format:
-        default:
-            return FT_PIXEL_MODE_GRAY;
-    }
-}
-
 void SkScalerContext_FreeType::generateAdvance(SkGlyph* glyph) {
 #ifdef FT_ADVANCES_H
    /* unhinted and light hinted text have linearly scaled advances
@@ -976,8 +972,8 @@
         if (0 == error) {
             glyph->fRsbDelta = 0;
             glyph->fLsbDelta = 0;
-            glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, advance);  // advance *2/3; //DEBUG
-            glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, advance);
+            glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, advance);
+            glyph->fAdvanceY = - SkFixedMul(fMatrix22.yx, advance);
             return;
         }
     }
@@ -994,8 +990,8 @@
     FT_Outline_Get_CBox(&fFace->glyph->outline, bbox);
 
     if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
-        int dx = FixedToDot6(glyph->getSubXFixed());
-        int dy = FixedToDot6(glyph->getSubYFixed());
+        int dx = SkFixedToFDot6(glyph->getSubXFixed());
+        int dy = SkFixedToFDot6(glyph->getSubYFixed());
         // negate dy since freetype-y-goes-up and skia-y-goes-down
         bbox->xMin += dx;
         bbox->yMin -= dy;
@@ -1014,7 +1010,7 @@
     // Must come after snapToPixelBoundary so that the width and height are
     // consistent. Otherwise asserts will fire later on when generating the
     // glyph image.
-    if (fUseVertMetrics) {
+    if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
         FT_Vector vector;
         vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
         vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;
@@ -1028,8 +1024,13 @@
 
 void SkScalerContext_FreeType::updateGlyphIfLCD(SkGlyph* glyph) {
     if (isLCD(fRec)) {
-        glyph->fWidth += gLCDExtra;
-        glyph->fLeft -= gLCDExtra >> 1;
+        if (fLCDIsVert) {
+            glyph->fHeight += gLCDExtra;
+            glyph->fTop -= gLCDExtra >> 1;
+        } else {
+            glyph->fWidth += gLCDExtra;
+            glyph->fLeft -= gLCDExtra >> 1;
+        }
     }
 }
 
@@ -1047,15 +1048,15 @@
 
     err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags );
     if (err != 0) {
-        SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
+#if 0
+        SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%x) returned 0x%x\n",
                     fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err));
+#endif
     ERROR:
         glyph->zeroMetrics();
         return;
     }
 
-    SkFixed vLeft, vTop;
-
     switch ( fFace->glyph->format ) {
       case FT_GLYPH_FORMAT_OUTLINE: {
         FT_BBox bbox;
@@ -1068,21 +1069,16 @@
             break;
         }
 
-        if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
-            emboldenOutline(&fFace->glyph->outline);
+        if (fRec.fFlags & kEmbolden_Flag && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
+            emboldenOutline(fFace, &fFace->glyph->outline);
         }
 
         getBBoxForCurrentGlyph(glyph, &bbox, true);
 
-        glyph->fWidth   = SkToU16((bbox.xMax - bbox.xMin) >> 6);
-        glyph->fHeight  = SkToU16((bbox.yMax - bbox.yMin) >> 6);
-        glyph->fTop     = -SkToS16(bbox.yMax >> 6);
-        glyph->fLeft    = SkToS16(bbox.xMin >> 6);
-
-        if ((fRec.fFlags & SkScalerContext::kVertical_Flag)) {
-            vLeft = Dot6ToFixed(bbox.xMin);
-            vTop = Dot6ToFixed(bbox.yMax);
-        }
+        glyph->fWidth   = SkToU16(SkFDot6Floor(bbox.xMax - bbox.xMin));
+        glyph->fHeight  = SkToU16(SkFDot6Floor(bbox.yMax - bbox.yMin));
+        glyph->fTop     = -SkToS16(SkFDot6Floor(bbox.yMax));
+        glyph->fLeft    = SkToS16(SkFDot6Floor(bbox.xMin));
 
         updateGlyphIfLCD(glyph);
 
@@ -1095,7 +1091,7 @@
             FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0);
         }
 
-        if (fUseVertMetrics) {
+        if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
             FT_Vector vector;
             vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
             vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;
@@ -1115,19 +1111,7 @@
         goto ERROR;
     }
 
-    if ((fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) == 0) {
-        glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
-        glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
-        if (fRec.fFlags & kDevKernText_Flag) {
-            glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta);
-            glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta);
-        }
-    } else {
-        glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance);
-        glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance);
-    }
-
-    if (fUseVertMetrics) {
+    if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
         if (fDoLinearMetrics) {
             glyph->fAdvanceX = -SkFixedMul(fMatrix22.xy, fFace->glyph->linearVertAdvance);
             glyph->fAdvanceY = SkFixedMul(fMatrix22.yy, fFace->glyph->linearVertAdvance);
@@ -1135,60 +1119,19 @@
             glyph->fAdvanceX = -SkFDot6ToFixed(fFace->glyph->advance.x);
             glyph->fAdvanceY = SkFDot6ToFixed(fFace->glyph->advance.y);
         }
+    } else {
+        if (fDoLinearMetrics) {
+            glyph->fAdvanceX = SkFixedMul(fMatrix22.xx, fFace->glyph->linearHoriAdvance);
+            glyph->fAdvanceY = -SkFixedMul(fMatrix22.yx, fFace->glyph->linearHoriAdvance);
+        } else {
+            glyph->fAdvanceX = SkFDot6ToFixed(fFace->glyph->advance.x);
+            glyph->fAdvanceY = -SkFDot6ToFixed(fFace->glyph->advance.y);
 
-    } else if ((fRec.fFlags & SkScalerContext::kVertical_Flag)
-                && fFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE) {
-
-        //TODO: do we need to specially handle SubpixelPositioning and Kerning?
-
-        FT_Matrix identityMatrix;
-        identityMatrix.xx = identityMatrix.yy = SK_Fixed1;
-        identityMatrix.xy = identityMatrix.yx = 0;
-
-        // if the matrix is not the identity matrix then we need to re-load the
-        // glyph with the identity matrix to get the necessary bounding box
-        if (memcmp(&fMatrix22, &identityMatrix, sizeof(FT_Matrix)) != 0) {
-
-            FT_Set_Transform(fFace, &identityMatrix, NULL);
-
-            err = FT_Load_Glyph( fFace, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags );
-            if (err != 0) {
-                SkDEBUGF(("SkScalerContext_FreeType::generateMetrics(%x): FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
-                            fFaceRec->fFontID, glyph->getGlyphID(fBaseGlyphCount), fLoadGlyphFlags, err));
-                goto ERROR;
-            }
-
-            if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
-                emboldenOutline(&fFace->glyph->outline);
+            if (fRec.fFlags & kDevKernText_Flag) {
+                glyph->fRsbDelta = SkToS8(fFace->glyph->rsb_delta);
+                glyph->fLsbDelta = SkToS8(fFace->glyph->lsb_delta);
             }
         }
-
-        // bounding box of the unskewed and unscaled glyph
-        FT_BBox bbox;
-        getBBoxForCurrentGlyph(glyph, &bbox);
-
-        // compute the vertical gap above and below the glyph if the glyph were
-        // centered within the linearVertAdvance
-        SkFixed vGap = (fFace->glyph->linearVertAdvance - Dot6ToFixed(bbox.yMax - bbox.yMin)) / 2;
-
-        // the origin point of the glyph when rendered vertically
-        FT_Vector vOrigin;
-        vOrigin.x = fFace->glyph->linearHoriAdvance / 2;
-        vOrigin.y = vGap + Dot6ToFixed(bbox.yMax);
-
-        // transform the vertical origin based on the matrix of the actual glyph
-        FT_Vector_Transform(&vOrigin, &fMatrix22);
-
-        // compute a new offset vector for the glyph by subtracting the vertical
-        // origin from the original horizontal offset vector
-        glyph->fLeft = SkFixedRoundToInt(vLeft - vOrigin.x);
-        glyph->fTop =  -SkFixedRoundToInt(vTop - vOrigin.y);
-
-        updateGlyphIfLCD(glyph);
-
-        // use the vertical advance values computed by freetype
-        glyph->fAdvanceX = -SkFixedMul(fMatrix22.xy, fFace->glyph->linearVertAdvance);
-        glyph->fAdvanceY = SkFixedMul(fMatrix22.yy, fFace->glyph->linearVertAdvance);
     }
 
 
@@ -1198,151 +1141,6 @@
 #endif
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-static int apply_contrast(int srca, int contrast) {
-    return srca + (((255 - srca) * contrast * srca) / (255*255));
-}
-
-static void build_power_table(uint8_t table[], float ee) {
-    for (int i = 0; i < 256; i++) {
-        float x = i / 255.f;
-        x = powf(x, ee);
-        int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
-        table[i] = SkToU8(xx);
-    }
-}
-
-static void build_gamma_table(uint8_t table[256], int src, int dst) {
-    static bool gInit;
-    static uint8_t powTable[256], invPowTable[256];
-    if (!gInit) {
-        const float g = SK_GAMMA_EXPONENT;
-        build_power_table(powTable, g);
-        build_power_table(invPowTable, 1/g);
-        gInit = true;
-    }
-
-    const int linSrc = powTable[src];
-    const int linDst = powTable[dst];
-    // have our contrast value taper off to 0 as the src luminance becomes white
-    const int contrast = SK_GAMMA_CONTRAST * (255 - linSrc) / 255;
-    
-    for (int i = 0; i < 256; ++i) {
-        int srca = apply_contrast(i, contrast);
-        SkASSERT((unsigned)srca <= 255);
-        int dsta = 255 - srca;
-
-        //Calculate the output we want.
-        int linOut = (linSrc * srca + dsta * linDst) / 255;
-        SkASSERT((unsigned)linOut <= 255);
-        int out = invPowTable[linOut];
-
-        //Undo what the blit blend will do.
-        int result = ((255 * out) - (255 * dst)) / (src - dst);
-        SkASSERT((unsigned)result <= 255);
-
-        table[i] = result;
-    }
-}
-
-static const uint8_t* getGammaTable(U8CPU luminance) {
-    static uint8_t gGammaTables[4][256];
-    static bool gInited;
-    if (!gInited) {
-        build_gamma_table(gGammaTables[0], 0x00, 0xFF);
-        build_gamma_table(gGammaTables[1], 0x66, 0x99);
-        build_gamma_table(gGammaTables[2], 0x99, 0x66);
-        build_gamma_table(gGammaTables[3], 0xFF, 0x00);
-
-        gInited = true;
-    }
-    SkASSERT(0 == (luminance >> 8));
-    return gGammaTables[luminance >> 6];
-}
-
-#ifndef SK_USE_COLOR_LUMINANCE
-static const uint8_t* getIdentityTable() {
-    static bool gOnce;
-    static uint8_t gIdentityTable[256];
-    if (!gOnce) {
-        for (int i = 0; i < 256; ++i) {
-            gIdentityTable[i] = i;
-        }
-        gOnce = true;
-    }
-    return gIdentityTable;
-}
-#endif
-
-static uint16_t packTriple(unsigned r, unsigned g, unsigned b) {
-    return SkPackRGB16(r >> 3, g >> 2, b >> 3);
-}
-
-static uint16_t grayToRGB16(U8CPU gray) {
-    SkASSERT(gray <= 255);
-    return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3);
-}
-
-static int bittst(const uint8_t data[], int bitOffset) {
-    SkASSERT(bitOffset >= 0);
-    int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7);
-    return lowBit & 1;
-}
-
-static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
-                         int lcdIsBGR, const uint8_t* tableR,
-                         const uint8_t* tableG, const uint8_t* tableB) {
-    SkASSERT(glyph.fHeight == bitmap.rows);
-    uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
-    const size_t dstRB = glyph.rowBytes();
-    const int width = glyph.fWidth;
-    const uint8_t* src = bitmap.buffer;
-
-    switch (bitmap.pixel_mode) {
-        case FT_PIXEL_MODE_MONO: {
-            for (int y = 0; y < glyph.fHeight; ++y) {
-                for (int x = 0; x < width; ++x) {
-                    dst[x] = -bittst(src, x);
-                }
-                dst = (uint16_t*)((char*)dst + dstRB);
-                src += bitmap.pitch;
-            }
-        } break;
-        case FT_PIXEL_MODE_GRAY: {
-            for (int y = 0; y < glyph.fHeight; ++y) {
-                for (int x = 0; x < width; ++x) {
-                    dst[x] = grayToRGB16(src[x]);
-                }
-                dst = (uint16_t*)((char*)dst + dstRB);
-                src += bitmap.pitch;
-            }
-        } break;
-        default: {
-            SkASSERT(glyph.fWidth * 3 == bitmap.width);
-            for (int y = 0; y < glyph.fHeight; y++) {
-                const uint8_t* triple = src;
-                if (lcdIsBGR) {
-                    for (int x = 0; x < width; x++) {
-                        dst[x] = packTriple(tableR[triple[2]], 
-                                            tableG[triple[1]],
-                                            tableB[triple[0]]);
-                        triple += 3;
-                    }
-                } else {
-                    for (int x = 0; x < width; x++) {
-                        dst[x] = packTriple(tableR[triple[0]], 
-                                            tableG[triple[1]],
-                                            tableB[triple[2]]);
-                        triple += 3;
-                    }
-                }
-                src += bitmap.pitch;
-                dst = (uint16_t*)((char*)dst + dstRB);
-            }
-        } break;
-    }
-}
 
 void SkScalerContext_FreeType::generateImage(const SkGlyph& glyph) {
     SkAutoMutexAcquire  ac(gFTMutex);
@@ -1362,193 +1160,9 @@
         return;
     }
 
-#ifdef SK_USE_COLOR_LUMINANCE
-    SkColor lumColor = fRec.getLuminanceColor();
-    const uint8_t* tableR = getGammaTable(SkColorGetR(lumColor));
-    const uint8_t* tableG = getGammaTable(SkColorGetG(lumColor));
-    const uint8_t* tableB = getGammaTable(SkColorGetB(lumColor));
-#else
-    unsigned lum = fRec.getLuminanceByte();
-    const uint8_t* tableR;
-    const uint8_t* tableG;
-    const uint8_t* tableB;
-
-    bool isWhite = lum >= WHITE_LUMINANCE_LIMIT;
-    bool isBlack = lum <= BLACK_LUMINANCE_LIMIT;
-    if ((gGammaTables[0] || gGammaTables[1]) && (isBlack || isWhite)) {
-        tableR = tableG = tableB = gGammaTables[isBlack ? 0 : 1];
-    } else {
-        tableR = tableG = tableB = getIdentityTable();
-    }
-#endif
-
-    switch ( fFace->glyph->format ) {
-        case FT_GLYPH_FORMAT_OUTLINE: {
-            FT_Outline* outline = &fFace->glyph->outline;
-            FT_BBox     bbox;
-            FT_Bitmap   target;
-
-            if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
-                emboldenOutline(outline);
-            }
-
-            int dx = 0, dy = 0;
-            if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
-                dx = glyph.getSubXFixed() >> 10;
-                dy = glyph.getSubYFixed() >> 10;
-                // negate dy since freetype-y-goes-up and skia-y-goes-down
-                dy = -dy;
-            }
-            FT_Outline_Get_CBox(outline, &bbox);
-            /*
-                what we really want to do for subpixel is
-                    offset(dx, dy)
-                    compute_bounds
-                    offset(bbox & !63)
-                but that is two calls to offset, so we do the following, which
-                achieves the same thing with only one offset call.
-            */
-            FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
-                                          dy - ((bbox.yMin + dy) & ~63));
-
-            if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
-                FT_Render_Glyph(fFace->glyph, FT_RENDER_MODE_LCD);
-                copyFT2LCD16(glyph, fFace->glyph->bitmap,
-                             fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag,
-                             tableR, tableG, tableB);
-            } else {
-                target.width = glyph.fWidth;
-                target.rows = glyph.fHeight;
-                target.pitch = glyph.rowBytes();
-                target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
-                target.pixel_mode = compute_pixel_mode(
-                                                (SkMask::Format)fRec.fMaskFormat);
-                target.num_grays = 256;
-
-                memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
-                FT_Outline_Get_Bitmap(gFTLibrary, outline, &target);
-            }
-        } break;
-
-        case FT_GLYPH_FORMAT_BITMAP: {
-            if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
-                FT_GlyphSlot_Own_Bitmap(fFace->glyph);
-                FT_Bitmap_Embolden(gFTLibrary, &fFace->glyph->bitmap, kBitmapEmboldenStrength, 0);
-            }
-            SkASSERT_CONTINUE(glyph.fWidth == fFace->glyph->bitmap.width);
-            SkASSERT_CONTINUE(glyph.fHeight == fFace->glyph->bitmap.rows);
-            SkASSERT_CONTINUE(glyph.fTop == -fFace->glyph->bitmap_top);
-            SkASSERT_CONTINUE(glyph.fLeft == fFace->glyph->bitmap_left);
-
-            const uint8_t*  src = (const uint8_t*)fFace->glyph->bitmap.buffer;
-            uint8_t*        dst = (uint8_t*)glyph.fImage;
-
-            if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY ||
-                (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
-                 glyph.fMaskFormat == SkMask::kBW_Format)) {
-                unsigned    srcRowBytes = fFace->glyph->bitmap.pitch;
-                unsigned    dstRowBytes = glyph.rowBytes();
-                unsigned    minRowBytes = SkMin32(srcRowBytes, dstRowBytes);
-                unsigned    extraRowBytes = dstRowBytes - minRowBytes;
-
-                for (int y = fFace->glyph->bitmap.rows - 1; y >= 0; --y) {
-                    memcpy(dst, src, minRowBytes);
-                    memset(dst + minRowBytes, 0, extraRowBytes);
-                    src += srcRowBytes;
-                    dst += dstRowBytes;
-                }
-            } else if (fFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
-                       glyph.fMaskFormat == SkMask::kA8_Format) {
-                for (int y = 0; y < fFace->glyph->bitmap.rows; ++y) {
-                    uint8_t byte = 0;
-                    int bits = 0;
-                    const uint8_t* src_row = src;
-                    uint8_t* dst_row = dst;
-
-                    for (int x = 0; x < fFace->glyph->bitmap.width; ++x) {
-                        if (!bits) {
-                            byte = *src_row++;
-                            bits = 8;
-                        }
-
-                        *dst_row++ = byte & 0x80 ? 0xff : 0;
-                        bits--;
-                        byte <<= 1;
-                    }
-
-                    src += fFace->glyph->bitmap.pitch;
-                    dst += glyph.rowBytes();
-                }
-            } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
-                copyFT2LCD16(glyph, fFace->glyph->bitmap,
-                             fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag,
-                             tableR, tableG, tableB);
-            } else {
-                SkDEBUGFAIL("unknown glyph bitmap transform needed");
-            }
-        } break;
-
-    default:
-        SkDEBUGFAIL("unknown glyph format");
-        goto ERROR;
-    }
-
-// We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,
-// it is optional
-#if defined(SK_GAMMA_APPLY_TO_A8) || !defined(SK_USE_COLOR_LUMINANCE)
-    if (SkMask::kA8_Format == glyph.fMaskFormat) {
-        SkASSERT(tableR == tableG && tableR == tableB);
-        const uint8_t* table = tableR;
-        uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
-        unsigned rowBytes = glyph.rowBytes();
-        
-        for (int y = glyph.fHeight - 1; y >= 0; --y) {
-            for (int x = glyph.fWidth - 1; x >= 0; --x) {
-                dst[x] = table[dst[x]];
-            }
-            dst += rowBytes;
-        }
-    }
-#endif
+    generateGlyphImage(fFace, glyph);
 }
 
-///////////////////////////////////////////////////////////////////////////////
-
-#define ft2sk(x)    SkFixedToScalar((x) << 10)
-
-#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 2
-    #define CONST_PARAM const
-#else   // older freetype doesn't use const here
-    #define CONST_PARAM
-#endif
-
-static int move_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
-    SkPath* path = (SkPath*)ctx;
-    path->close();  // to close the previous contour (if any)
-    path->moveTo(ft2sk(pt->x), -ft2sk(pt->y));
-    return 0;
-}
-
-static int line_proc(CONST_PARAM FT_Vector* pt, void* ctx) {
-    SkPath* path = (SkPath*)ctx;
-    path->lineTo(ft2sk(pt->x), -ft2sk(pt->y));
-    return 0;
-}
-
-static int quad_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
-                     void* ctx) {
-    SkPath* path = (SkPath*)ctx;
-    path->quadTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x), -ft2sk(pt1->y));
-    return 0;
-}
-
-static int cubic_proc(CONST_PARAM FT_Vector* pt0, CONST_PARAM FT_Vector* pt1,
-                      CONST_PARAM FT_Vector* pt2, void* ctx) {
-    SkPath* path = (SkPath*)ctx;
-    path->cubicTo(ft2sk(pt0->x), -ft2sk(pt0->y), ft2sk(pt1->x),
-                  -ft2sk(pt1->y), ft2sk(pt2->x), -ft2sk(pt2->y));
-    return 0;
-}
 
 void SkScalerContext_FreeType::generatePath(const SkGlyph& glyph,
                                             SkPath* path) {
@@ -1574,37 +1188,17 @@
         return;
     }
 
-    if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
-        emboldenOutline(&fFace->glyph->outline);
-    }
+    generateGlyphPath(fFace, glyph, path);
 
-    if (fUseVertMetrics) {
+    // The path's origin from FreeType is always the horizontal layout origin.
+    // Offset the path so that it is relative to the vertical origin if needed.
+    if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
         FT_Vector vector;
         vector.x = fFace->glyph->metrics.vertBearingX - fFace->glyph->metrics.horiBearingX;
         vector.y = -fFace->glyph->metrics.vertBearingY - fFace->glyph->metrics.horiBearingY;
         FT_Vector_Transform(&vector, &fMatrix22);
-        FT_Outline_Translate(&fFace->glyph->outline, vector.x, vector.y);
+        path->offset(SkFDot6ToScalar(vector.x), -SkFDot6ToScalar(vector.y));
     }
-
-    FT_Outline_Funcs    funcs;
-
-    funcs.move_to   = move_proc;
-    funcs.line_to   = line_proc;
-    funcs.conic_to  = quad_proc;
-    funcs.cubic_to  = cubic_proc;
-    funcs.shift     = 0;
-    funcs.delta     = 0;
-
-    err = FT_Outline_Decompose(&fFace->glyph->outline, &funcs, path);
-
-    if (err != 0) {
-        SkDEBUGF(("SkScalerContext_FreeType::generatePath: FT_Load_Glyph(glyph:%d flags:%d) returned 0x%x\n",
-                    glyph.getGlyphID(fBaseGlyphCount), flags, err));
-        path->reset();
-        return;
-    }
-
-    path->close();
 }
 
 void SkScalerContext_FreeType::generateFontMetrics(SkPaint::FontMetrics* mx,
@@ -1665,7 +1259,7 @@
             FT_BBox bbox;
             FT_Load_Glyph(fFace, x_glyph, fLoadGlyphFlags);
             if ((fRec.fFlags & kEmbolden_Flag) && !(fFace->style_flags & FT_STYLE_FLAG_BOLD)) {
-                emboldenOutline(&fFace->glyph->outline);
+                emboldenOutline(fFace, &fFace->glyph->outline);
             }
             FT_Outline_Get_CBox(&fFace->glyph->outline, &bbox);
             x_height = SkFixedToScalar(SkFDot6ToFixed(bbox.yMax));
diff --git a/src/ports/SkFontHost_FreeType_common.cpp b/src/ports/SkFontHost_FreeType_common.cpp
new file mode 100644
index 0000000..cc94802
--- /dev/null
+++ b/src/ports/SkFontHost_FreeType_common.cpp
@@ -0,0 +1,339 @@
+/*
+ * Copyright 2006-2012 The Android Open Source Project
+ * Copyright 2012 Mozilla Foundation
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorPriv.h"
+#include "SkFDot6.h"
+#include "SkFontHost_FreeType_common.h"
+#include "SkPath.h"
+
+#include <ft2build.h>
+#include FT_OUTLINE_H
+#include FT_BITMAP_H
+// In the past, FT_GlyphSlot_Own_Bitmap was defined in this header file.
+#include FT_SYNTHESIS_H
+
+static FT_Pixel_Mode compute_pixel_mode(SkMask::Format format) {
+    switch (format) {
+        case SkMask::kBW_Format:
+            return FT_PIXEL_MODE_MONO;
+        case SkMask::kA8_Format:
+        default:
+            return FT_PIXEL_MODE_GRAY;
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+// hand-tuned value to reduce outline embolden strength
+#ifndef SK_OUTLINE_EMBOLDEN_DIVISOR
+    #ifdef SK_BUILD_FOR_ANDROID
+        #define SK_OUTLINE_EMBOLDEN_DIVISOR   34
+    #else
+        #define SK_OUTLINE_EMBOLDEN_DIVISOR   24
+    #endif
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+
+static uint16_t packTriple(unsigned r, unsigned g, unsigned b) {
+    return SkPackRGB16(r >> 3, g >> 2, b >> 3);
+}
+
+static uint16_t grayToRGB16(U8CPU gray) {
+    SkASSERT(gray <= 255);
+    return SkPackRGB16(gray >> 3, gray >> 2, gray >> 3);
+}
+
+static int bittst(const uint8_t data[], int bitOffset) {
+    SkASSERT(bitOffset >= 0);
+    int lowBit = data[bitOffset >> 3] >> (~bitOffset & 7);
+    return lowBit & 1;
+}
+
+template<bool APPLY_PREBLEND>
+static void copyFT2LCD16(const SkGlyph& glyph, const FT_Bitmap& bitmap,
+                         int lcdIsBGR, bool lcdIsVert, const uint8_t* tableR,
+                         const uint8_t* tableG, const uint8_t* tableB) {
+    if (lcdIsVert) {
+        SkASSERT(3 * glyph.fHeight == bitmap.rows);
+    } else {
+        SkASSERT(glyph.fHeight == bitmap.rows);
+    }
+
+    uint16_t* dst = reinterpret_cast<uint16_t*>(glyph.fImage);
+    const size_t dstRB = glyph.rowBytes();
+    const int width = glyph.fWidth;
+    const uint8_t* src = bitmap.buffer;
+
+    switch (bitmap.pixel_mode) {
+        case FT_PIXEL_MODE_MONO: {
+            for (int y = 0; y < glyph.fHeight; ++y) {
+                for (int x = 0; x < width; ++x) {
+                    dst[x] = -bittst(src, x);
+                }
+                dst = (uint16_t*)((char*)dst + dstRB);
+                src += bitmap.pitch;
+            }
+        } break;
+        case FT_PIXEL_MODE_GRAY: {
+            for (int y = 0; y < glyph.fHeight; ++y) {
+                for (int x = 0; x < width; ++x) {
+                    dst[x] = grayToRGB16(src[x]);
+                }
+                dst = (uint16_t*)((char*)dst + dstRB);
+                src += bitmap.pitch;
+            }
+        } break;
+        default: {
+            SkASSERT(lcdIsVert || (glyph.fWidth * 3 == bitmap.width));
+            for (int y = 0; y < glyph.fHeight; y++) {
+                if (lcdIsVert) {    // vertical stripes
+                    const uint8_t* srcR = src;
+                    const uint8_t* srcG = srcR + bitmap.pitch;
+                    const uint8_t* srcB = srcG + bitmap.pitch;
+                    if (lcdIsBGR) {
+                        SkTSwap(srcR, srcB);
+                    }
+                    for (int x = 0; x < width; x++) {
+                        dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(*srcR++, tableR),
+                                            sk_apply_lut_if<APPLY_PREBLEND>(*srcG++, tableG),
+                                            sk_apply_lut_if<APPLY_PREBLEND>(*srcB++, tableB));
+                    }
+                    src += 3 * bitmap.pitch;
+                } else {            // horizontal stripes
+                    const uint8_t* triple = src;
+                    if (lcdIsBGR) {
+                        for (int x = 0; x < width; x++) {
+                            dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableR),
+                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
+                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableB));
+                            triple += 3;
+                        }
+                    } else {
+                        for (int x = 0; x < width; x++) {
+                            dst[x] = packTriple(sk_apply_lut_if<APPLY_PREBLEND>(triple[0], tableR),
+                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[1], tableG),
+                                                sk_apply_lut_if<APPLY_PREBLEND>(triple[2], tableB));
+                            triple += 3;
+                        }
+                    }
+                    src += bitmap.pitch;
+                }
+                dst = (uint16_t*)((char*)dst + dstRB);
+            }
+        } break;
+    }
+}
+
+void SkScalerContext_FreeType_Base::generateGlyphImage(FT_Face face, const SkGlyph& glyph) {
+    const bool doBGR = SkToBool(fRec.fFlags & SkScalerContext::kLCD_BGROrder_Flag);
+    const bool doVert = SkToBool(fRec.fFlags & SkScalerContext::kLCD_Vertical_Flag);
+
+    switch ( face->glyph->format ) {
+        case FT_GLYPH_FORMAT_OUTLINE: {
+            FT_Outline* outline = &face->glyph->outline;
+            FT_BBox     bbox;
+            FT_Bitmap   target;
+
+            if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) {
+                emboldenOutline(face, outline);
+            }
+
+            int dx = 0, dy = 0;
+            if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
+                dx = SkFixedToFDot6(glyph.getSubXFixed());
+                dy = SkFixedToFDot6(glyph.getSubYFixed());
+                // negate dy since freetype-y-goes-up and skia-y-goes-down
+                dy = -dy;
+            }
+            FT_Outline_Get_CBox(outline, &bbox);
+            /*
+                what we really want to do for subpixel is
+                    offset(dx, dy)
+                    compute_bounds
+                    offset(bbox & !63)
+                but that is two calls to offset, so we do the following, which
+                achieves the same thing with only one offset call.
+            */
+            FT_Outline_Translate(outline, dx - ((bbox.xMin + dx) & ~63),
+                                          dy - ((bbox.yMin + dy) & ~63));
+
+            if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+                FT_Render_Glyph(face->glyph, doVert ? FT_RENDER_MODE_LCD_V : FT_RENDER_MODE_LCD);
+                if (fPreBlend.isApplicable()) {
+                    copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                       fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                } else {
+                    copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                }
+            } else {
+                target.width = glyph.fWidth;
+                target.rows = glyph.fHeight;
+                target.pitch = glyph.rowBytes();
+                target.buffer = reinterpret_cast<uint8_t*>(glyph.fImage);
+                target.pixel_mode = compute_pixel_mode(
+                                                (SkMask::Format)fRec.fMaskFormat);
+                target.num_grays = 256;
+
+                memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
+                FT_Outline_Get_Bitmap(face->glyph->library, outline, &target);
+            }
+        } break;
+
+        case FT_GLYPH_FORMAT_BITMAP: {
+            if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) {
+                FT_GlyphSlot_Own_Bitmap(face->glyph);
+                FT_Bitmap_Embolden(face->glyph->library, &face->glyph->bitmap, kBitmapEmboldenStrength, 0);
+            }
+            SkASSERT_CONTINUE(glyph.fWidth == face->glyph->bitmap.width);
+            SkASSERT_CONTINUE(glyph.fHeight == face->glyph->bitmap.rows);
+            SkASSERT_CONTINUE(glyph.fTop == -face->glyph->bitmap_top);
+            SkASSERT_CONTINUE(glyph.fLeft == face->glyph->bitmap_left);
+
+            const uint8_t*  src = (const uint8_t*)face->glyph->bitmap.buffer;
+            uint8_t*        dst = (uint8_t*)glyph.fImage;
+
+            if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_GRAY ||
+                (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
+                 glyph.fMaskFormat == SkMask::kBW_Format)) {
+                unsigned    srcRowBytes = face->glyph->bitmap.pitch;
+                unsigned    dstRowBytes = glyph.rowBytes();
+                unsigned    minRowBytes = SkMin32(srcRowBytes, dstRowBytes);
+                unsigned    extraRowBytes = dstRowBytes - minRowBytes;
+
+                for (int y = face->glyph->bitmap.rows - 1; y >= 0; --y) {
+                    memcpy(dst, src, minRowBytes);
+                    memset(dst + minRowBytes, 0, extraRowBytes);
+                    src += srcRowBytes;
+                    dst += dstRowBytes;
+                }
+            } else if (face->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO &&
+                       glyph.fMaskFormat == SkMask::kA8_Format) {
+                for (int y = 0; y < face->glyph->bitmap.rows; ++y) {
+                    uint8_t byte = 0;
+                    int bits = 0;
+                    const uint8_t* src_row = src;
+                    uint8_t* dst_row = dst;
+
+                    for (int x = 0; x < face->glyph->bitmap.width; ++x) {
+                        if (!bits) {
+                            byte = *src_row++;
+                            bits = 8;
+                        }
+
+                        *dst_row++ = byte & 0x80 ? 0xff : 0;
+                        bits--;
+                        byte <<= 1;
+                    }
+
+                    src += face->glyph->bitmap.pitch;
+                    dst += glyph.rowBytes();
+                }
+            } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+                if (fPreBlend.isApplicable()) {
+                    copyFT2LCD16<true>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                       fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                } else {
+                    copyFT2LCD16<false>(glyph, face->glyph->bitmap, doBGR, doVert,
+                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                }
+            } else {
+                SkDEBUGFAIL("unknown glyph bitmap transform needed");
+            }
+        } break;
+
+    default:
+        SkDEBUGFAIL("unknown glyph format");
+        memset(glyph.fImage, 0, glyph.rowBytes() * glyph.fHeight);
+        return;
+    }
+
+// We used to always do this pre-USE_COLOR_LUMINANCE, but with colorlum,
+// it is optional
+#if defined(SK_GAMMA_APPLY_TO_A8)
+    if (SkMask::kA8_Format == glyph.fMaskFormat && fPreBlend.isApplicable()) {
+        uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
+        unsigned rowBytes = glyph.rowBytes();
+
+        for (int y = glyph.fHeight - 1; y >= 0; --y) {
+            for (int x = glyph.fWidth - 1; x >= 0; --x) {
+                dst[x] = fPreBlend.fG[dst[x]];
+            }
+            dst += rowBytes;
+        }
+    }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int move_proc(const FT_Vector* pt, void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->close();  // to close the previous contour (if any)
+    path->moveTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y));
+    return 0;
+}
+
+static int line_proc(const FT_Vector* pt, void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->lineTo(SkFDot6ToScalar(pt->x), -SkFDot6ToScalar(pt->y));
+    return 0;
+}
+
+static int quad_proc(const FT_Vector* pt0, const FT_Vector* pt1,
+                     void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->quadTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
+                 SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y));
+    return 0;
+}
+
+static int cubic_proc(const FT_Vector* pt0, const FT_Vector* pt1,
+                      const FT_Vector* pt2, void* ctx) {
+    SkPath* path = (SkPath*)ctx;
+    path->cubicTo(SkFDot6ToScalar(pt0->x), -SkFDot6ToScalar(pt0->y),
+                  SkFDot6ToScalar(pt1->x), -SkFDot6ToScalar(pt1->y),
+                  SkFDot6ToScalar(pt2->x), -SkFDot6ToScalar(pt2->y));
+    return 0;
+}
+
+void SkScalerContext_FreeType_Base::generateGlyphPath(FT_Face face,
+                                                      const SkGlyph& glyph,
+                                                      SkPath* path)
+{
+    if (fRec.fFlags & SkScalerContext::kEmbolden_Flag && !(face->style_flags & FT_STYLE_FLAG_BOLD)) {
+        emboldenOutline(face, &face->glyph->outline);
+    }
+
+    FT_Outline_Funcs    funcs;
+
+    funcs.move_to   = move_proc;
+    funcs.line_to   = line_proc;
+    funcs.conic_to  = quad_proc;
+    funcs.cubic_to  = cubic_proc;
+    funcs.shift     = 0;
+    funcs.delta     = 0;
+
+    FT_Error err = FT_Outline_Decompose(&face->glyph->outline, &funcs, path);
+
+    if (err != 0) {
+        path->reset();
+        return;
+    }
+
+    path->close();
+}
+
+void SkScalerContext_FreeType_Base::emboldenOutline(FT_Face face, FT_Outline* outline)
+{
+    FT_Pos strength;
+    strength = FT_MulFix(face->units_per_EM, face->size->metrics.y_scale)
+               / SK_OUTLINE_EMBOLDEN_DIVISOR;
+    FT_Outline_Embolden(outline, strength);
+}
diff --git a/src/ports/SkFontHost_FreeType_common.h b/src/ports/SkFontHost_FreeType_common.h
new file mode 100644
index 0000000..01100de
--- /dev/null
+++ b/src/ports/SkFontHost_FreeType_common.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2006-2012 The Android Open Source Project
+ * Copyright 2012 Mozilla Foundation
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKFONTHOST_FREETYPE_COMMON_H_
+#define SKFONTHOST_FREETYPE_COMMON_H_
+
+#include "SkGlyph.h"
+#include "SkScalerContext.h"
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#ifdef SK_DEBUG
+    #define SkASSERT_CONTINUE(pred)                                                         \
+        do {                                                                                \
+            if (!(pred))                                                                    \
+                SkDebugf("file %s:%d: assert failed '" #pred "'\n", __FILE__, __LINE__);    \
+        } while (false)
+#else
+    #define SkASSERT_CONTINUE(pred)
+#endif
+
+
+class SkScalerContext_FreeType_Base : public SkScalerContext {
+public:
+    // See http://freetype.sourceforge.net/freetype2/docs/reference/ft2-bitmap_handling.html#FT_Bitmap_Embolden
+    // This value was chosen by eyeballing the result in Firefox and trying to match it.
+    static const FT_Pos kBitmapEmboldenStrength = 1 << 6;
+
+    SkScalerContext_FreeType_Base(const SkDescriptor *desc)
+        : SkScalerContext(desc)
+    {}
+
+protected:
+    void generateGlyphImage(FT_Face face, const SkGlyph& glyph);
+    void generateGlyphPath(FT_Face face, const SkGlyph& glyph, SkPath* path);
+    void emboldenOutline(FT_Face face, FT_Outline* outline);
+};
+
+#endif // SKFONTHOST_FREETYPE_COMMON_H_
diff --git a/src/ports/SkFontHost_android.cpp b/src/ports/SkFontHost_android.cpp
index bfd1dac..6dd9cab 100644
--- a/src/ports/SkFontHost_android.cpp
+++ b/src/ports/SkFontHost_android.cpp
@@ -1,21 +1,13 @@
-/* libs/graphics/ports/SkFontHost_android.cpp
-**
-** Copyright 2006, 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.
-*/
+/*
+ * Copyright 2006 The Android Open Source Project
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
 
 #include "SkFontHost.h"
+#include "SkFontDescriptor.h"
+#include "SkGlyphCache.h"
 #include "SkGraphics.h"
 #include "SkDescriptor.h"
 #include "SkMMapStream.h"
@@ -75,8 +67,7 @@
 
 static SkTypeface* deserializeLocked(SkStream* stream);
 static SkTypeface* createTypefaceLocked(const SkTypeface* familyFace,
-        const char familyName[], const void* data, size_t bytelength,
-        SkTypeface::Style style);
+        const char familyName[], SkTypeface::Style style);
 static SkStream* openStreamLocked(uint32_t fontID);
 static size_t getFileNameLocked(SkFontID fontID, char path[], size_t length, int32_t* index);
 static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec);
@@ -910,7 +901,7 @@
                     for (int j = i; j >= 0; --j) {
                         if (gSystemFonts[j].fNames != NULL) {
                             return createTypefaceLocked(NULL,
-                                    gSystemFonts[j].fNames[0], NULL, 0,
+                                    gSystemFonts[j].fNames[0],
                                     (SkTypeface::Style)style);
                         }
                     }
@@ -925,15 +916,13 @@
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style) {
     SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
-    return createTypefaceLocked(familyFace, familyName, data, bytelength, style);
+    return createTypefaceLocked(familyFace, familyName, style);
 }
 
 static SkTypeface* createTypefaceLocked(const SkTypeface* familyFace,
-        const char familyName[], const void* data, size_t bytelength,
-        SkTypeface::Style style) {
+        const char familyName[], SkTypeface::Style style) {
     loadSystemFontsLocked();
 
     // clip to legal style bits
@@ -997,12 +986,12 @@
     }
 }
 
-SkFontID SkFontHost::NextLogicalFont(const SkScalerContext::Rec& rec) {
+SkFontID SkFontHost::NextLogicalFont(const SkScalerContextRec& rec) {
     SkAutoMutexAcquire  ac(gFamilyHeadAndNameListMutex);
     return nextLogicalFontLocked(rec);
 }
 
-static SkFontID nextLogicalFontLocked(const SkScalerContext::Rec& rec) {
+static SkFontID nextLogicalFontLocked(const SkScalerContextRec& rec) {
     loadSystemFontsLocked();
 
     const SkTypeface* origTypeface = findFromUniqueIDLocked(rec.fOrigFontID);
@@ -1106,7 +1095,7 @@
     paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
     paint.setFontVariant(fontVariant);
 
-    SkAutoGlyphCache autoCache(paint, NULL);
+    SkAutoGlyphCache autoCache(paint, NULL, NULL);
     SkGlyphCache*    cache = autoCache.getCache();
     SkFontID         fontID = 0;
 
@@ -1197,16 +1186,16 @@
 SK_DECLARE_STATIC_MUTEX(gTypefaceTableMutex);  // This is the mutex for gTypefaceTable
 static SkTDArray<TypefaceLookupStruct> gTypefaceTable;  // This is protected by gTypefaceTableMutex
 
-static int typefaceLookupCompare(const TypefaceLookupStruct& first,
-        const TypefaceLookupStruct& second) {
-    if (first.script != second.script) {
-        return (first.script > second.script) ? 1 : -1;
+static int typefaceLookupCompare(const TypefaceLookupStruct* first,
+        const TypefaceLookupStruct* second) {
+    if (first->script != second->script) {
+        return (first->script > second->script) ? 1 : -1;
     }
-    if (first.style != second.style) {
-        return (first.style > second.style) ? 1 : -1;
+    if (first->style != second->style) {
+        return (first->style > second->style) ? 1 : -1;
     }
-    if (first.fontVariant != second.fontVariant) {
-        return (first.fontVariant > second.fontVariant) ? 1 : -1;
+    if (first->fontVariant != second->fontVariant) {
+        return (first->fontVariant > second->fontVariant) ? 1 : -1;
     }
     return 0;
 }
diff --git a/src/ports/SkFontHost_ascender.cpp b/src/ports/SkFontHost_ascender.cpp
index ccfedef..d20cc82 100644
--- a/src/ports/SkFontHost_ascender.cpp
+++ b/src/ports/SkFontHost_ascender.cpp
@@ -52,20 +52,20 @@
 {
     int size = aca_Get_FontHandleRec_Size();
     fHandle = (aca_FontHandle)sk_malloc_throw(size);
-    
+
     // get the pointer to the font
-    
+
     fFontStream = new SkMMAPStream("/UcsGB2312-Hei-H.FDL");
     fHintStream = new SkMMAPStream("/genv6-23.bin");
-    
+
     void* hints = sk_malloc_throw(fHintStream->getLength());
     memcpy(hints, fHintStream->getMemoryBase(), fHintStream->getLength());
-    
+
     aca_Create_Font_Handle(fHandle,
                            (void*)fFontStream->getMemoryBase(), fFontStream->getLength(),
                            "fred",
                            hints, fHintStream->getLength());
-    
+
     // compute our factors from the record
 
     SkMatrix    m;
@@ -75,17 +75,17 @@
     //  now compute our scale factors
     SkScalar    sx = m.getScaleX();
     SkScalar    sy = m.getScaleY();
-    
+
     int ppemX = SkScalarRound(sx);
     int ppemY = SkScalarRound(sy);
-    
+
     size = aca_Find_Font_Memory_Required(fHandle, ppemX, ppemY);
     size *= 8;  // Jeff suggests this :)
     fWorkspace = sk_malloc_throw(size);
     aca_Set_Font_Memory(fHandle, (uint8_t*)fWorkspace, size);
 
     aca_GlyphAttribsRec rec;
-    
+
     memset(&rec, 0, sizeof(rec));
     rec.xSize = ppemX;
     rec.ySize = ppemY;
@@ -95,7 +95,7 @@
     rec.doInterpolate = true;
     rec.grayMode = 2;
     aca_Set_Font_Attributes(fHandle, &rec, &size);
-    
+
     fGlyphWorkspace = sk_malloc_throw(size);
     aca_Set_Glyph_Memory(fHandle, fGlyphWorkspace);
 }
@@ -123,10 +123,10 @@
 {
     glyph->fRsbDelta = 0;
     glyph->fLsbDelta = 0;
-    
+
     aca_GlyphImageRec   rec;
     aca_Vector          topLeft;
-    
+
     int adv = aca_Get_Adv_Width(fHandle, glyph->getGlyphID());
     if (aca_GLYPH_NOT_PRESENT == adv)
         goto ERROR;
@@ -144,7 +144,7 @@
         glyph->fAdvanceY = 0;
         return;
     }
-    
+
     glyph->fWidth = rec.width;
     glyph->fHeight = rec.rows;
     glyph->fRowBytes = rec.width;
@@ -158,13 +158,13 @@
 {
     aca_GlyphImageRec   rec;
     aca_Vector          topLeft;
-    
+
     aca_Rasterize(glyph.getGlyphID(), fHandle, &rec, &topLeft);
-    
+
     const uint8_t* src = (const uint8_t*)rec.buffer;
     uint8_t* dst = (uint8_t*)glyph.fImage;
     int height = glyph.fHeight;
-    
+
     src += rec.y0 * rec.pitch + rec.x0;
     while (--height >= 0)
     {
@@ -179,7 +179,7 @@
 void SkScalerContext_Ascender::generatePath(const SkGlyph& glyph, SkPath* path)
 {
     SkRect r;
-    
+
     r.set(0, 0, SkIntToScalar(4), SkIntToScalar(4));
     path->reset();
     path->addRect(r);
@@ -227,4 +227,3 @@
 {
     return SkNEW_ARGS(SkScalerContext_Ascender, (desc));
 }
-
diff --git a/src/ports/SkFontHost_fontconfig.cpp b/src/ports/SkFontHost_fontconfig.cpp
index 775a7c7..355ea79 100644
--- a/src/ports/SkFontHost_fontconfig.cpp
+++ b/src/ports/SkFontHost_fontconfig.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2008 Google Inc.
  *
@@ -6,16 +5,6 @@
  * found in the LICENSE file.
  */
 
-
-// -----------------------------------------------------------------------------
-// This file provides implementations of the font resolution members of
-// SkFontHost by using the fontconfig[1] library. Fontconfig is usually found
-// on Linux systems and handles configuration, parsing and caching issues
-// involved with enumerating and matching fonts.
-//
-// [1] http://fontconfig.org
-// -----------------------------------------------------------------------------
-
 #include <map>
 #include <string>
 
@@ -24,81 +13,69 @@
 #include "SkFontHost.h"
 #include "SkStream.h"
 
-// This is an extern from SkFontHost_FreeType
+/** An extern from SkFontHost_FreeType. */
 SkTypeface::Style find_name_and_style(SkStream* stream, SkString* name);
 
-// -----------------------------------------------------------------------------
-// The rest of Skia requires that fonts be identified by a unique unsigned id
-// and that we be able to load them given the id. What we actually get from
-// fontconfig is the filename of the font so we keep a locked map from
-// filenames to fileid numbers and back.
-//
-// Note that there's also a unique id in the SkTypeface. This is unique over
-// both filename and style. Thus we encode that id as (fileid << 8) | style.
-// Although truetype fonts can support multiple faces in a single file, at the
-// moment Skia doesn't.
-// -----------------------------------------------------------------------------
+/** This lock must be held while modifying global_fc_* globals. */
 SK_DECLARE_STATIC_MUTEX(global_fc_map_lock);
+
+/** Map from file names to file ids. */
 static std::map<std::string, unsigned> global_fc_map;
+/** Map from file ids to file names. */
 static std::map<unsigned, std::string> global_fc_map_inverted;
-static std::map<uint32_t, SkTypeface *> global_fc_typefaces;
+/** The next file id. */
 static unsigned global_fc_map_next_id = 0;
 
-static unsigned UniqueIdToFileId(unsigned uniqueid)
-{
+/**
+ * Check to see if the filename has already been assigned a fileid and, if so, use it.
+ * Otherwise, assign one. Return the resulting fileid.
+ */
+static unsigned FileIdFromFilename(const char* filename) {
+    SkAutoMutexAcquire ac(global_fc_map_lock);
+
+    std::map<std::string, unsigned>::const_iterator i = global_fc_map.find(filename);
+    if (i == global_fc_map.end()) {
+        const unsigned fileid = global_fc_map_next_id++;
+        global_fc_map[filename] = fileid;
+        global_fc_map_inverted[fileid] = filename;
+        return fileid;
+    } else {
+        return i->second;
+    }
+}
+
+static unsigned FileIdFromUniqueId(unsigned uniqueid) {
     return uniqueid >> 8;
 }
 
-static SkTypeface::Style UniqueIdToStyle(unsigned uniqueid)
-{
+static SkTypeface::Style StyleFromUniqueId(unsigned uniqueid) {
     return static_cast<SkTypeface::Style>(uniqueid & 0xff);
 }
 
-static unsigned FileIdAndStyleToUniqueId(unsigned fileid,
-                                         SkTypeface::Style style)
-{
+static unsigned UniqueIdFromFileIdAndStyle(unsigned fileid, SkTypeface::Style style) {
     SkASSERT((style & 0xff) == style);
     return (fileid << 8) | static_cast<int>(style);
 }
 
-// -----------------------------------------------------------------------------
-// Normally we only return exactly the font asked for. In last-resort cases,
-// the request is for one of the basic font names "Sans", "Serif" or
-// "Monospace". This function tells you whether a given request is for such a
-// fallback.
-// -----------------------------------------------------------------------------
-static bool IsFallbackFontAllowed(const char* request)
-{
-    return strcmp(request, "Sans") == 0 ||
-           strcmp(request, "Serif") == 0 ||
-           strcmp(request, "Monospace") == 0;
-}
-
 class FontConfigTypeface : public SkTypeface {
 public:
-    FontConfigTypeface(Style style, uint32_t id)
-        : SkTypeface(style, id)
-    { }
+    FontConfigTypeface(Style style, uint32_t id) : SkTypeface(style, id) { }
 };
 
-// -----------------------------------------------------------------------------
-// Find a matching font where @type (one of FC_*) is equal to @value. For a
-// list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
-// The variable arguments are a list of triples, just like the first three
-// arguments, and must be NULL terminated.
-//
-// For example,
-//   FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf",
-//                   NULL);
-// -----------------------------------------------------------------------------
-static FcPattern* FontMatch(const char* type, FcType vtype, const void* value,
-                            ...)
-{
+/**
+ * Find a matching font where @type (one of FC_*) is equal to @value. For a
+ * list of types, see http://fontconfig.org/fontconfig-devel/x19.html#AEN27.
+ * The variable arguments are a list of triples, just like the first three
+ * arguments, and must be NULL terminated.
+ *
+ * For example,
+ *   FontMatchString(FC_FILE, FcTypeString, "/usr/share/fonts/myfont.ttf", NULL);
+ */
+static FcPattern* FontMatch(const char* type, FcType vtype, const void* value, ...) {
     va_list ap;
     va_start(ap, value);
 
     FcPattern* pattern = FcPatternCreate();
-    const char* family_requested = NULL;
 
     for (;;) {
         FcValue fcvalue;
@@ -113,10 +90,7 @@
             default:
                 SkDEBUGFAIL("FontMatch unhandled type");
         }
-        FcPatternAdd(pattern, type, fcvalue, 0);
-
-        if (vtype == FcTypeString && strcmp(type, FC_FAMILY) == 0)
-            family_requested = (const char*) value;
+        FcPatternAdd(pattern, type, fcvalue, FcFalse);
 
         type = va_arg(ap, const char *);
         if (!type)
@@ -127,88 +101,19 @@
     };
     va_end(ap);
 
-    FcConfigSubstitute(0, pattern, FcMatchPattern);
+    FcConfigSubstitute(NULL, pattern, FcMatchPattern);
     FcDefaultSubstitute(pattern);
 
-    // Font matching:
-    // CSS often specifies a fallback list of families:
-    //    font-family: a, b, c, serif;
-    // However, fontconfig will always do its best to find *a* font when asked
-    // for something so we need a way to tell if the match which it has found is
-    // "good enough" for us. Otherwise, we can return NULL which gets piped up
-    // and lets WebKit know to try the next CSS family name. However, fontconfig
-    // configs allow substitutions (mapping "Arial -> Helvetica" etc) and we
-    // wish to support that.
-    //
-    // Thus, if a specific family is requested we set @family_requested. Then we
-    // record two strings: the family name after config processing and the
-    // family name after resolving. If the two are equal, it's a good match.
-    //
-    // So consider the case where a user has mapped Arial to Helvetica in their
-    // config.
-    //    requested family: "Arial"
-    //    post_config_family: "Helvetica"
-    //    post_match_family: "Helvetica"
-    //      -> good match
-    //
-    // and for a missing font:
-    //    requested family: "Monaco"
-    //    post_config_family: "Monaco"
-    //    post_match_family: "Times New Roman"
-    //      -> BAD match
-    //
-    // However, we special-case fallback fonts; see IsFallbackFontAllowed().
-    FcChar8* post_config_family;
-    FcPatternGetString(pattern, FC_FAMILY, 0, &post_config_family);
-
     FcResult result;
-    FcPattern* match = FcFontMatch(0, pattern, &result);
-    if (!match) {
-        FcPatternDestroy(pattern);
-        return NULL;
-    }
-
-    FcChar8* post_match_family;
-    FcPatternGetString(match, FC_FAMILY, 0, &post_match_family);
-    const bool family_names_match =
-        !family_requested ?
-        true :
-        strcasecmp((char *)post_config_family, (char *)post_match_family) == 0;
-
+    FcPattern* match = FcFontMatch(NULL, pattern, &result);
     FcPatternDestroy(pattern);
 
-    if (!family_names_match && !IsFallbackFontAllowed(family_requested)) {
-        FcPatternDestroy(match);
-        return NULL;
-    }
-
     return match;
 }
 
-// -----------------------------------------------------------------------------
-// Check to see if the filename has already been assigned a fileid and, if so,
-// use it. Otherwise, assign one. Return the resulting fileid.
-// -----------------------------------------------------------------------------
-static unsigned FileIdFromFilename(const char* filename)
-{
-    SkAutoMutexAcquire ac(global_fc_map_lock);
-
-    std::map<std::string, unsigned>::const_iterator i =
-        global_fc_map.find(filename);
-    if (i == global_fc_map.end()) {
-        const unsigned fileid = global_fc_map_next_id++;
-        global_fc_map[filename] = fileid;
-        global_fc_map_inverted[fileid] = filename;
-        return fileid;
-    } else {
-        return i->second;
-    }
-}
-
 // static
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style)
 {
     const char* resolved_family_name = NULL;
@@ -216,7 +121,9 @@
 
     {
         SkAutoMutexAcquire ac(global_fc_map_lock);
-        FcInit();
+        if (FcTrue != FcInit()) {
+            SkASSERT(false && "Could not initialize fontconfig.");
+        }
     }
 
     if (familyFace) {
@@ -225,18 +132,17 @@
         // familyname of the font.
         SkAutoMutexAcquire ac(global_fc_map_lock);
 
-        const unsigned fileid = UniqueIdToFileId(familyFace->uniqueID());
-        std::map<unsigned, std::string>::const_iterator i =
-            global_fc_map_inverted.find(fileid);
-        if (i == global_fc_map_inverted.end())
+        const unsigned fileid = FileIdFromUniqueId(familyFace->uniqueID());
+        std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
+        if (i == global_fc_map_inverted.end()) {
             return NULL;
+        }
 
-        FcInit();
-        face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(),
-                               NULL);
-
-        if (!face_match)
+        face_match = FontMatch(FC_FILE, FcTypeString, i->second.c_str(), NULL);
+        if (!face_match) {
             return NULL;
+        }
+
         FcChar8* family;
         if (FcPatternGetString(face_match, FC_FAMILY, 0, &family)) {
             FcPatternDestroy(face_match);
@@ -248,21 +154,23 @@
         resolved_family_name = reinterpret_cast<char*>(family);
     } else if (familyName) {
         resolved_family_name = familyName;
-    } else {
-        return NULL;
     }
 
-    // At this point, we have a resolved_family_name from somewhere
-    SkASSERT(resolved_family_name);
+    const int bold = (style & SkTypeface::kBold) ? FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
+    const int italic = (style & SkTypeface::kItalic) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
 
-    const int bold = style & SkTypeface::kBold ?
-                     FC_WEIGHT_BOLD : FC_WEIGHT_NORMAL;
-    const int italic = style & SkTypeface::kItalic ?
-                       FC_SLANT_ITALIC : FC_SLANT_ROMAN;
-    FcPattern* match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
-                                 FC_WEIGHT, FcTypeInteger, bold,
-                                 FC_SLANT, FcTypeInteger, italic,
-                                 NULL);
+    FcPattern* match;
+    if (resolved_family_name) {
+        match = FontMatch(FC_FAMILY, FcTypeString, resolved_family_name,
+                          FC_WEIGHT, FcTypeInteger, bold,
+                          FC_SLANT, FcTypeInteger, italic,
+                          NULL);
+    } else {
+        match = FontMatch(FC_WEIGHT, FcTypeInteger, reinterpret_cast<void*>(bold),
+                          FC_SLANT, FcTypeInteger, italic,
+                          NULL);
+    }
+
     if (face_match)
         FcPatternDestroy(face_match);
 
@@ -277,53 +185,43 @@
     // Now @filename is pointing into @match
 
     const unsigned fileid = FileIdFromFilename(reinterpret_cast<char*>(filename));
-    const unsigned id = FileIdAndStyleToUniqueId(fileid, style);
+    const unsigned id = UniqueIdFromFileIdAndStyle(fileid, style);
     SkTypeface* typeface = SkNEW_ARGS(FontConfigTypeface, (style, id));
     FcPatternDestroy(match);
 
-    {
-        SkAutoMutexAcquire ac(global_fc_map_lock);
-        global_fc_typefaces[id] = typeface;
-    }
-
     return typeface;
 }
 
 // static
-SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream)
-{
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromStream unimplemented");
     return NULL;
 }
 
 // static
-SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[])
-{
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
     SkDEBUGFAIL("SkFontHost::CreateTypefaceFromFile unimplemented");
     return NULL;
 }
 
 // static
-SkStream* SkFontHost::OpenStream(uint32_t id)
-{
+SkStream* SkFontHost::OpenStream(uint32_t id) {
     SkAutoMutexAcquire ac(global_fc_map_lock);
-    const unsigned fileid = UniqueIdToFileId(id);
+    const unsigned fileid = FileIdFromUniqueId(id);
 
-    std::map<unsigned, std::string>::const_iterator i =
-        global_fc_map_inverted.find(fileid);
-    if (i == global_fc_map_inverted.end())
+    std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
+    if (i == global_fc_map_inverted.end()) {
         return NULL;
+    }
 
     return SkNEW_ARGS(SkFILEStream, (i->second.c_str()));
 }
 
-size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
-                               int32_t* index) {
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
     SkAutoMutexAcquire ac(global_fc_map_lock);
-    const unsigned fileid = UniqueIdToFileId(fontID);
+    const unsigned fileid = FileIdFromUniqueId(fontID);
 
-    std::map<unsigned, std::string>::const_iterator i =
-    global_fc_map_inverted.find(fileid);
+    std::map<unsigned, std::string>::const_iterator i = global_fc_map_inverted.find(fileid);
     if (i == global_fc_map_inverted.end()) {
         return 0;
     }
@@ -351,4 +249,3 @@
     // We don't handle font fallback, WebKit does.
     return 0;
 }
-
diff --git a/src/ports/SkFontHost_freetype_mac.cpp b/src/ports/SkFontHost_freetype_mac.cpp
index e51f802..bc197d6 100644
--- a/src/ports/SkFontHost_freetype_mac.cpp
+++ b/src/ports/SkFontHost_freetype_mac.cpp
@@ -22,7 +22,7 @@
         fStream->unref();
     }
 
-    SkStream* fStream;    
+    SkStream* fStream;
 };
 
 static FTMacTypeface* create_from_path(const char path[]) {
@@ -42,7 +42,7 @@
     if (NULL == gDef) {
         gDef = create_from_path(FONT_PATH);
     }
-    
+
     gDef->ref();
     return gDef;
 }
@@ -51,7 +51,6 @@
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style) {
     return ref_default_typeface();
 }
@@ -104,4 +103,3 @@
     SkDEBUGFAIL("Not supported");
     return NULL;
 }
-
diff --git a/src/ports/SkFontHost_gamma.cpp b/src/ports/SkFontHost_gamma.cpp
deleted file mode 100644
index 0d15414..0000000
--- a/src/ports/SkFontHost_gamma.cpp
+++ /dev/null
@@ -1,107 +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 "SkFontHost.h"
-#include <math.h>
-
-// define this to use pre-compiled tables for gamma. This is slightly faster,
-// and doesn't create any RW global memory, but means we cannot change the
-// gamma at runtime.
-//#define USE_PREDEFINED_GAMMA_TABLES
-
-#ifndef USE_PREDEFINED_GAMMA_TABLES
-    // define this if you want to spew out the "C" code for the tables, given
-    // the current values for SK_BLACK_GAMMA and SK_WHITE_GAMMA.
-    #define DUMP_GAMMA_TABLESx
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-#include "SkGraphics.h"
-
-// declared here, so we can link against it elsewhere
-void skia_set_text_gamma(float blackGamma, float whiteGamma);
-
-#ifdef USE_PREDEFINED_GAMMA_TABLES
-
-#include "sk_predefined_gamma.h"
-
-void skia_set_text_gamma(float blackGamma, float whiteGamma) {}
-
-#else   // use writable globals for gamma tables
-
-static void build_power_table(uint8_t table[], float ee) {
-//    SkDebugf("------ build_power_table %g\n", ee);
-    for (int i = 0; i < 256; i++) {
-        float x = i / 255.f;
-        //   printf(" %d %g", i, x);
-        x = powf(x, ee);
-        //   printf(" %g", x);
-        int xx = SkScalarRound(SkFloatToScalar(x * 255));
-        //   printf(" %d\n", xx);
-        table[i] = SkToU8(xx);
-    }
-}
-
-static bool gGammaIsBuilt;
-static uint8_t gBlackGamma[256], gWhiteGamma[256];
-
-static float gBlackGammaCoeff = 1.4f;
-static float gWhiteGammaCoeff = 1/1.4f;
-
-void skia_set_text_gamma(float blackGamma, float whiteGamma) {
-    gBlackGammaCoeff = blackGamma;
-    gWhiteGammaCoeff = whiteGamma;
-    gGammaIsBuilt = false;
-    SkGraphics::PurgeFontCache();
-    build_power_table(gBlackGamma, gBlackGammaCoeff);
-    build_power_table(gWhiteGamma, gWhiteGammaCoeff);
-}
-
-#ifdef DUMP_GAMMA_TABLES
-
-#include "SkString.h"
-
-static void dump_a_table(const char name[], const uint8_t table[],
-                         float gamma) {
-    SkDebugf("\n");
-    SkDebugf("\/\/ Gamma table for %g\n", gamma);
-    SkDebugf("static const uint8_t %s[] = {\n", name);
-    for (int y = 0; y < 16; y++) {
-        SkString line, tmp;
-        for (int x = 0; x < 16; x++) {
-            tmp.printf("0x%02X, ", *table++);
-            line.append(tmp);
-        }
-        SkDebugf("    %s\n", line.c_str());
-    }
-    SkDebugf("};\n");
-}
-
-#endif
-
-#endif
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
-#ifndef USE_PREDEFINED_GAMMA_TABLES
-    if (!gGammaIsBuilt) {
-        build_power_table(gBlackGamma, gBlackGammaCoeff);
-        build_power_table(gWhiteGamma, gWhiteGammaCoeff);
-        gGammaIsBuilt = true;
-
-#ifdef DUMP_GAMMA_TABLES
-        dump_a_table("gBlackGamma", gBlackGamma, gBlackGammaCoeff);
-        dump_a_table("gWhiteGamma", gWhiteGamma, gWhiteGammaCoeff);
-#endif
-    }
-#endif
-    tables[0] = gBlackGamma;
-    tables[1] = gWhiteGamma;
-}
-
diff --git a/src/ports/SkFontHost_gamma_none.cpp b/src/ports/SkFontHost_gamma_none.cpp
deleted file mode 100644
index 18f113c..0000000
--- a/src/ports/SkFontHost_gamma_none.cpp
+++ /dev/null
@@ -1,23 +0,0 @@
-
-/*
- * Copyright 2008 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-
-// -----------------------------------------------------------------------------
-// This is a noop gamma implementation for systems where gamma is already
-// corrected, or dealt with in a system wide fashion. For example, on X windows
-// one uses the xgamma utility to set the server-wide gamma correction value.
-// -----------------------------------------------------------------------------
-
-#include "SkFontHost.h"
-
-void SkFontHost::GetGammaTables(const uint8_t* tables[2])
-{
-    tables[0] = NULL;
-    tables[1] = NULL;
-}
-
diff --git a/src/ports/SkFontHost_linux.cpp b/src/ports/SkFontHost_linux.cpp
index be99576..07235c8 100644
--- a/src/ports/SkFontHost_linux.cpp
+++ b/src/ports/SkFontHost_linux.cpp
@@ -8,6 +8,7 @@
 
 
 #include "SkFontHost.h"
+#include "SkFontDescriptor.h"
 #include "SkDescriptor.h"
 #include "SkMMapStream.h"
 #include "SkOSFile.h"
@@ -16,21 +17,17 @@
 #include "SkStream.h"
 #include "SkThread.h"
 #include "SkTSearch.h"
-#include <stdio.h>
 
 #ifndef SK_FONT_FILE_PREFIX
-    #define SK_FONT_FILE_PREFIX      "/usr/share/fonts/truetype/msttcorefonts/"
+    #define SK_FONT_FILE_PREFIX "/usr/share/fonts/truetype/"
+#endif
+#ifndef SK_FONT_FILE_DIR_SEPERATOR
+    #define SK_FONT_FILE_DIR_SEPERATOR "/"
 #endif
 
 bool find_name_and_attributes(SkStream* stream, SkString* name,
                               SkTypeface::Style* style, bool* isFixedWidth);
 
-static void GetFullPathForSysFonts(SkString* full, const char name[])
-{
-    full->append(SK_FONT_FILE_PREFIX);
-    full->append(name);
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
 struct FamilyRec;
@@ -43,7 +40,7 @@
 struct NameFamilyPair {
     const char* fName;      // we own this
     FamilyRec*  fFamily;    // we don't own this, we just reference it
-    
+
     void construct(const char name[], FamilyRec* family)
     {
         fName = strdup(name);
@@ -67,7 +64,7 @@
 struct FamilyRec {
     FamilyRec*  fNext;
     SkTypeface* fFaces[4];
-    
+
     FamilyRec()
     {
         fNext = gFamilyHead;
@@ -79,7 +76,7 @@
 static SkTypeface* find_best_face(const FamilyRec* family,
                                   SkTypeface::Style style) {
     SkTypeface* const* faces = family->fFaces;
-    
+
     if (faces[style] != NULL) { // exact match
         return faces[style];
     }
@@ -130,10 +127,6 @@
     return NULL;
 }
 
-static bool valid_uniqueID(uint32_t uniqueID) {
-    return find_from_uniqueID(uniqueID) != NULL;
-}
-
 /*  Remove reference to this face from its family. If the resulting family
  is empty (has no faces), return that family, otherwise return NULL
  */
@@ -141,7 +134,7 @@
     FamilyRec* family = find_family(face);
     SkASSERT(family->fFaces[face->style()] == face);
     family->fFaces[face->style()] = NULL;
-    
+
     for (int i = 0; i < 4; i++) {
         if (family->fFaces[i] != NULL) {    // family is non-empty
             return NULL;
@@ -154,7 +147,7 @@
 static void detach_and_delete_family(FamilyRec* family) {
     FamilyRec* curr = gFamilyHead;
     FamilyRec* prev = NULL;
-    
+
     while (curr != NULL) {
         FamilyRec* next = curr->fNext;
         if (curr == family) {
@@ -172,8 +165,18 @@
     SkDEBUGFAIL("Yikes, couldn't find family in our list to remove/delete");
 }
 
+static const char* find_family_name(const SkTypeface* familyMember) {
+    const FamilyRec* familyRec = find_family(familyMember);
+    for (int i = 0; i < gNameList.count(); i++) {
+        if (gNameList[i].fFamily == familyRec) {
+            return gNameList[i].fName;
+        }
+    }
+    return NULL;
+}
+
 static FamilyRec* find_familyrec(const char name[]) {
-    const NameFamilyPair* list = gNameList.begin();    
+    const NameFamilyPair* list = gNameList.begin();
     int index = SkStrLCSearch(&list[0].fName, gNameList.count(), name,
                               sizeof(list[0]));
     return index >= 0 ? list[index].fFamily : NULL;
@@ -193,12 +196,12 @@
 static void add_name(const char name[], FamilyRec* family) {
     SkAutoAsciiToLC tolc(name);
     name = tolc.lc();
-    
+
     NameFamilyPair* list = gNameList.begin();
     int             count = gNameList.count();
-    
+
     int index = SkStrLCSearch(&list[0].fName, count, name, sizeof(list[0]));
-    
+
     if (index < 0) {
         list = gNameList.insert(~index);
         list->construct(name, family);
@@ -211,9 +214,9 @@
         SkASSERT(emptyFamily->fFaces[i] == NULL);
     }
 #endif
-    
+
     SkTDArray<NameFamilyPair>& list = gNameList;
-    
+
     // must go backwards when removing
     for (int i = list.count() - 1; i >= 0; --i) {
         NameFamilyPair* pair = &list[i];
@@ -231,19 +234,19 @@
     FamilyTypeface(Style style, bool sysFont, FamilyRec* family, bool isFixedWidth)
     : SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1, isFixedWidth) {
         fIsSysFont = sysFont;
-        
+
         SkAutoMutexAcquire  ac(gFamilyMutex);
-        
+
         if (NULL == family) {
             family = SkNEW(FamilyRec);
         }
         family->fFaces[style] = this;
         fFamilyRec = family;    // just record it so we can return it if asked
     }
-    
+
     virtual ~FamilyTypeface() {
         SkAutoMutexAcquire  ac(gFamilyMutex);
-        
+
         // remove us from our family. If the family is now empty, we return
         // that and then remove that family from the name list
         FamilyRec* family = remove_from_family(this);
@@ -252,17 +255,17 @@
             detach_and_delete_family(family);
         }
     }
-    
+
     bool isSysFont() const { return fIsSysFont; }
     FamilyRec* getFamily() const { return fFamilyRec; }
     // openStream returns a SkStream that has been ref-ed
     virtual SkStream* openStream() = 0;
     virtual const char* getUniqueString() const = 0;
-    
+
 private:
     FamilyRec*  fFamilyRec; // we don't own this, just point to it
     bool        fIsSysFont;
-    
+
     typedef SkTypeface INHERITED;
 };
 
@@ -275,11 +278,11 @@
 class EmptyTypeface : public FamilyTypeface {
 public:
     EmptyTypeface() : INHERITED(SkTypeface::kNormal, true, NULL, false) {}
-    
+
     // overrides
     virtual SkStream* openStream() { return NULL; }
     virtual const char* getUniqueString() const { return NULL; }
-    
+
 private:
     typedef FamilyTypeface INHERITED;
 };
@@ -295,19 +298,19 @@
     virtual ~StreamTypeface() {
         fStream->unref();
     }
-    
+
     // overrides
-    virtual SkStream* openStream() 
-    { 
+    virtual SkStream* openStream()
+    {
       // openStream returns a refed stream.
       fStream->ref();
       return fStream;
     }
     virtual const char* getUniqueString() const { return NULL; }
-    
+
 private:
     SkStream* fStream;
-    
+
     typedef FamilyTypeface INHERITED;
 };
 
@@ -318,12 +321,12 @@
         : INHERITED(style, sysFont, family, isFixedWidth) {
         fPath.set(path);
     }
-    
+
     // overrides
     virtual SkStream* openStream()
     {
         SkStream* stream = SkNEW_ARGS(SkMMAPStream, (fPath.c_str()));
-        
+
         // check for failure
         if (stream->getLength() <= 0) {
             SkDELETE(stream);
@@ -344,10 +347,10 @@
         }
         return str;
     }
-    
+
 private:
     SkString fPath;
-    
+
     typedef FamilyTypeface INHERITED;
 };
 
@@ -355,7 +358,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 static bool get_name_and_style(const char path[], SkString* name,
-                               SkTypeface::Style* style, bool* isFixedWidth) {    
+                               SkTypeface::Style* style, bool* isFixedWidth) {
     SkMMAPStream stream(path);
     if (stream.getLength() > 0) {
         return find_name_and_attributes(&stream, name, style, isFixedWidth);
@@ -366,7 +369,7 @@
             return find_name_and_attributes(&stream, name, style, isFixedWidth);
         }
     }
-    
+
     SkDebugf("---- failed to open <%s> as a font\n", path);
     return false;
 }
@@ -376,36 +379,25 @@
 static FamilyRec* gDefaultFamily;
 static SkTypeface* gDefaultNormal;
 
-static void load_system_fonts() {
-    // check if we've already be called
-    if (NULL != gDefaultNormal) {
-//        printf("---- default font %p\n", gDefaultNormal);
-        return;
-    }
-
-    SkOSFile::Iter  iter(SK_FONT_FILE_PREFIX, ".ttf");
+static void load_directory_fonts(const SkString& directory, unsigned int* count) {
+    SkOSFile::Iter  iter(directory.c_str(), ".ttf");
     SkString        name;
-    int             count = 0;
 
     while (iter.next(&name, false)) {
-        SkString filename;
-        GetFullPathForSysFonts(&filename, name.c_str());
+        SkString filename(directory);
+        filename.append(name);
 
         bool isFixedWidth;
         SkString realname;
         SkTypeface::Style style = SkTypeface::kNormal; // avoid uninitialized warning
-        
+
         if (!get_name_and_style(filename.c_str(), &realname, &style, &isFixedWidth)) {
             SkDebugf("------ can't load <%s> as a font\n", filename.c_str());
             continue;
         }
 
-//        SkDebugf("font: <%s> %d <%s>\n", realname.c_str(), style, filename.c_str());
-  
         FamilyRec* family = find_familyrec(realname.c_str());
         if (family && family->fFaces[style]) {
-//            SkDebugf("---- skipping duplicate typeface %s style %d\n",
-//                     realname.c_str(), style);
             continue;
         }
 
@@ -421,9 +413,31 @@
         if (NULL == family) {
             add_name(realname.c_str(), tf->getFamily());
         }
-        count += 1;
+        *count += 1;
     }
 
+    SkOSFile::Iter  dirIter(directory.c_str());
+    while (dirIter.next(&name, true)) {
+        if (name.startsWith(".")) {
+            continue;
+        }
+        SkString dirname(directory);
+        dirname.append(name);
+        dirname.append(SK_FONT_FILE_DIR_SEPERATOR);
+        load_directory_fonts(dirname, count);
+    }
+}
+
+static void load_system_fonts() {
+    // check if we've already be called
+    if (NULL != gDefaultNormal) {
+        return;
+    }
+
+    SkString baseDirectory(SK_FONT_FILE_PREFIX);
+    unsigned int count = 0;
+    load_directory_fonts(baseDirectory, &count);
+
     if (0 == count) {
         SkNEW(EmptyTypeface);
     }
@@ -455,98 +469,92 @@
     if (NULL == gDefaultNormal) {
         sk_throw();
     }
-    gFallBackTypeface = gDefaultNormal;    
+    gFallBackTypeface = gDefaultNormal;
     gDefaultFamily = find_family(gDefaultNormal);
-
-//    SkDebugf("---- default %p head %p family %p\n", gDefaultNormal, gFamilyHead, gDefaultFamily);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
-#if 0
-    const char* name = ((FamilyTypeface*)face)->getUniqueString();
-    
-    stream->write8((uint8_t)face->getStyle());
-    
-    if (NULL == name || 0 == *name) {
-        stream->writePackedUInt(0);
-        //        SkDebugf("--- fonthost serialize null\n");
+
+    SkFontDescriptor descriptor;
+    descriptor.setFamilyName(find_family_name(face));
+    descriptor.setStyle(face->style());
+    descriptor.setFontFileName(((FamilyTypeface*)face)->getUniqueString());
+
+    descriptor.serialize(stream);
+
+    const bool isCustomFont = !((FamilyTypeface*)face)->isSysFont();
+    if (isCustomFont) {
+        // store the entire font in the fontData
+        SkStream* fontStream = ((FamilyTypeface*)face)->openStream();
+        const uint32_t length = fontStream->getLength();
+
+        stream->writePackedUInt(length);
+        stream->writeStream(fontStream, length);
+
+        fontStream->unref();
     } else {
-        uint32_t len = strlen(name);
-        stream->writePackedUInt(len);
-        stream->write(name, len);
-        //      SkDebugf("--- fonthost serialize <%s> %d\n", name, face->getStyle());
+        stream->writePackedUInt(0);
     }
-#endif
-    sk_throw();
 }
 
 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
-#if 0
     load_system_fonts();
-    
-    int style = stream->readU8();
-    
-    int len = stream->readPackedUInt();
-    if (len > 0) {
-        SkString str;
-        str.resize(len);
-        stream->read(str.writable_str(), len);
-        
-        const FontInitRec* rec = gSystemFonts;
-        for (size_t i = 0; i < SK_ARRAY_COUNT(gSystemFonts); i++) {
-            if (strcmp(rec[i].fFileName, str.c_str()) == 0) {
-                // backup until we hit the fNames
-                for (int j = i; j >= 0; --j) {
-                    if (rec[j].fNames != NULL) {
-                        return SkFontHost::CreateTypeface(NULL, rec[j].fNames[0], NULL, 0,
-                                                          (SkTypeface::Style)style);
-                    }
-                }
-            }
-        }
+
+    SkFontDescriptor descriptor(stream);
+    const char* familyName = descriptor.getFamilyName();
+    const SkTypeface::Style style = descriptor.getStyle();
+
+    const uint32_t customFontDataLength = stream->readPackedUInt();
+    if (customFontDataLength > 0) {
+
+        // generate a new stream to store the custom typeface
+        SkMemoryStream* fontStream = new SkMemoryStream(customFontDataLength - 1);
+        stream->read((void*)fontStream->getMemoryBase(), customFontDataLength - 1);
+
+        SkTypeface* face = CreateTypefaceFromStream(fontStream);
+
+        fontStream->unref();
+        return face;
     }
-    return SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, (SkTypeface::Style)style);
-#endif
-    sk_throw();
-    return NULL;
+
+    return SkFontHost::CreateTypeface(NULL, familyName, style);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style) {
     load_system_fonts();
-    
+
     SkAutoMutexAcquire  ac(gFamilyMutex);
-    
+
     // clip to legal style bits
     style = (SkTypeface::Style)(style & SkTypeface::kBoldItalic);
-    
+
     SkTypeface* tf = NULL;
-    
+
     if (NULL != familyFace) {
         tf = find_typeface(familyFace, style);
     } else if (NULL != familyName) {
         //        SkDebugf("======= familyName <%s>\n", familyName);
         tf = find_typeface(familyName, style);
     }
-    
+
     if (NULL == tf) {
         tf = find_best_face(gDefaultFamily, style);
     }
-   
-    SkSafeRef(tf); 
+
+    SkSafeRef(tf);
     return tf;
 }
 
 SkStream* SkFontHost::OpenStream(uint32_t fontID) {
     FamilyTypeface* tf = (FamilyTypeface*)find_from_uniqueID(fontID);
     SkStream* stream = tf ? tf->openStream() : NULL;
-    
+
     if (stream && stream->getLength() == 0) {
         stream->unref();
         stream = NULL;
@@ -556,7 +564,7 @@
 
 size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
                                int32_t* index) {
-    SkDebugf("SkFontHost::GetFileName unimplemented\n");
+//    SkDebugf("SkFontHost::GetFileName unimplemented\n");
     return 0;
 }
 
@@ -591,4 +599,3 @@
     stream->unref();
     return face;
 }
-
diff --git a/src/ports/SkFontHost_mac.cpp b/src/ports/SkFontHost_mac.cpp
index 7d12f53..79c5541 100755
--- a/src/ports/SkFontHost_mac.cpp
+++ b/src/ports/SkFontHost_mac.cpp
@@ -35,5 +35,3 @@
 #else
     #include "SkFontHost_mac_atsui.cpp"
 #endif
-
-
diff --git a/src/ports/SkFontHost_mac_atsui.cpp b/src/ports/SkFontHost_mac_atsui.cpp
index ae32036..84f7f0b 100644
--- a/src/ports/SkFontHost_mac_atsui.cpp
+++ b/src/ports/SkFontHost_mac_atsui.cpp
@@ -97,7 +97,7 @@
     static OSStatus Close(void *cb);
 };
 
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
     // we only support 2 levels of hinting
     SkPaint::Hinting h = rec->getHinting();
     if (SkPaint::kSlight_Hinting == h) {
@@ -494,7 +494,6 @@
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                             const char familyName[],
-                            const void* data, size_t bytelength,
                             SkTypeface::Style style) {
     // todo: we don't know how to respect style bits
     if (NULL == familyName && NULL != familyFace) {
diff --git a/src/ports/SkFontHost_mac_coretext.cpp b/src/ports/SkFontHost_mac_coretext.cpp
index a612555..ceb9a04 100644
--- a/src/ports/SkFontHost_mac_coretext.cpp
+++ b/src/ports/SkFontHost_mac_coretext.cpp
@@ -19,10 +19,21 @@
 
 #include "SkFontHost.h"
 #include "SkCGUtils.h"
+#include "SkColorPriv.h"
 #include "SkDescriptor.h"
 #include "SkEndian.h"
+#include "SkFontDescriptor.h"
 #include "SkFloatingPoint.h"
+#include "SkGlyph.h"
+#include "SkMaskGamma.h"
+#include "SkSFNTHeader.h"
+#include "SkOTTable_glyf.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_hhea.h"
+#include "SkOTTable_loca.h"
+#include "SkOTUtils.h"
 #include "SkPaint.h"
+#include "SkPath.h"
 #include "SkString.h"
 #include "SkStream.h"
 #include "SkThread.h"
@@ -32,19 +43,60 @@
 
 class SkScalerContext_Mac;
 
-static void CFSafeRelease(CFTypeRef obj) {
-    if (obj) {
-        CFRelease(obj);
+// Being templated and taking const T* prevents calling
+// CFSafeRelease(autoCFRelease) through implicit conversion.
+template <typename T> static void CFSafeRelease(/*CFTypeRef*/const T* cfTypeRef) {
+    if (cfTypeRef) {
+        CFRelease(cfTypeRef);
     }
 }
 
-class AutoCFRelease : SkNoncopyable {
+// Being templated and taking const T* prevents calling
+// CFSafeRetain(autoCFRelease) through implicit conversion.
+template <typename T> static void CFSafeRetain(/*CFTypeRef*/const T* cfTypeRef) {
+    if (cfTypeRef) {
+        CFRetain(cfTypeRef);
+    }
+}
+
+/** Acts like a CFRef, but calls CFSafeRelease when it goes out of scope. */
+template<typename CFRef> class AutoCFRelease : private SkNoncopyable {
 public:
-    AutoCFRelease(CFTypeRef obj) : fObj(obj) {}
-    ~AutoCFRelease() { CFSafeRelease(fObj); }
-    
+    explicit AutoCFRelease(CFRef cfRef = NULL) : fCFRef(cfRef) { }
+    ~AutoCFRelease() { CFSafeRelease(fCFRef); }
+
+    void reset(CFRef that = NULL) {
+        CFSafeRetain(that);
+        CFSafeRelease(fCFRef);
+        fCFRef = that;
+    }
+
+    AutoCFRelease& operator =(CFRef that) {
+        reset(that);
+        return *this;
+    }
+
+    operator CFRef() const { return fCFRef; }
+    CFRef get() const { return fCFRef; }
+
 private:
-    CFTypeRef fObj;
+    CFRef fCFRef;
+};
+
+template<typename T> class AutoCGTable : SkNoncopyable {
+public:
+    AutoCGTable(CGFontRef font)
+    //Undocumented: the tag parameter in this call is expected in machine order and not BE order.
+    : fCFData(CGFontCopyTableForTag(font, SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)))
+    , fData(fCFData ? reinterpret_cast<const T*>(CFDataGetBytePtr(fCFData)) : NULL)
+    { }
+
+    const T* operator->() const { return fData; }
+
+private:
+    AutoCFRelease<CFDataRef> fCFData;
+public:
+    const T* fData;
 };
 
 // inline versions of these rect helpers
@@ -80,14 +132,10 @@
     return rect.size.width;
 }
 
-static CGFloat CGRectGetHeight(const CGRect& rect) {
-    return rect.size.height;
-}
-
 ///////////////////////////////////////////////////////////////////////////////
 
-static void sk_memset_rect32(uint32_t* ptr, uint32_t value, size_t width,
-                             size_t height, size_t rowBytes) {
+static void sk_memset_rect32(uint32_t* ptr, uint32_t value,
+                             size_t width, size_t height, size_t rowBytes) {
     SkASSERT(width);
     SkASSERT(width * sizeof(uint32_t) <= rowBytes);
 
@@ -130,19 +178,6 @@
     }
 }
 
-// Potentially this should be made (1) public (2) optimized when width is small.
-// Also might want 16 and 32 bit version
-//
-static void sk_memset_rect(void* ptr, U8CPU byte, size_t width, size_t height,
-                           size_t rowBytes) {
-    uint8_t* dst = (uint8_t*)ptr;
-    while (height) {
-        memset(dst, byte, width);
-        dst += rowBytes;
-        height -= 1;
-    }
-}
-
 #include <sys/utsname.h>
 
 typedef uint32_t CGRGBPixel;
@@ -159,27 +194,17 @@
 //   cd ApplicationServices.framework/Frameworks/CoreGraphics.framework/
 //   nm CoreGraphics | grep CGContextSetShouldSubpixelQuantizeFonts
 
-#if !defined(MAC_OS_X_VERSION_10_6) || \
-        MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
-    CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context,
-        bool allowsFontSmoothing);
-    CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(
-        CGContextRef context,
-        bool allowsFontSubpixelPositioning);
-    CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context,
-        bool shouldSubpixelPositionFonts);
-    CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(
-        CGContextRef context,
-        bool allowsFontSubpixelQuantization);
-    CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(
-        CGContextRef context,
-        bool shouldSubpixelQuantizeFonts);
+#if !defined(MAC_OS_X_VERSION_10_6) || (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6)
+CG_EXTERN void CGContextSetAllowsFontSmoothing(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetAllowsFontSubpixelPositioning(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetShouldSubpixelPositionFonts(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetAllowsFontSubpixelQuantization(CGContextRef context, bool value);
+CG_EXTERN void CGContextSetShouldSubpixelQuantizeFonts(CGContextRef context, bool value);
 #endif
 
-static const char FONT_DEFAULT_NAME[]           = "Lucida Sans";
+static const char FONT_DEFAULT_NAME[] = "Lucida Sans";
 
-// see Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal 
-// for original source
+// See Source/WebKit/chromium/base/mac/mac_util.mm DarwinMajorVersionInternal for original source.
 static int readVersion() {
     struct utsname info;
     if (uname(&info) != 0) {
@@ -219,6 +244,10 @@
     return darwinVersion() == 11;
 }
 
+static bool isMountainLion() {
+    return darwinVersion() == 12;
+}
+
 static bool isLCDFormat(unsigned format) {
     return SkMask::kLCD16_Format == format || SkMask::kLCD32_Format == format;
 }
@@ -228,7 +257,7 @@
         return SkScalarToFloat(scalar);
     } else {
         SkASSERT(sizeof(CGFloat) == sizeof(double));
-        return SkScalarToDouble(scalar);
+        return (CGFloat) SkScalarToDouble(scalar);
     }
 }
 
@@ -242,20 +271,14 @@
 }
 
 static CGAffineTransform MatrixToCGAffineTransform(const SkMatrix& matrix,
-                                                   float sx = 1, float sy = 1) {
-    return CGAffineTransformMake(ScalarToCG(matrix[SkMatrix::kMScaleX]) * sx,
-                                 -ScalarToCG(matrix[SkMatrix::kMSkewY]) * sy,
-                                 -ScalarToCG(matrix[SkMatrix::kMSkewX]) * sx,
-                                 ScalarToCG(matrix[SkMatrix::kMScaleY]) * sy,
-                                 ScalarToCG(matrix[SkMatrix::kMTransX]) * sx,
-                                 ScalarToCG(matrix[SkMatrix::kMTransY]) * sy);
-}
-
-static void CGAffineTransformToMatrix(const CGAffineTransform& xform, SkMatrix* matrix) {
-    matrix->setAll(
-                   CGToScalar(xform.a), CGToScalar(xform.c), CGToScalar(xform.tx),
-                   CGToScalar(xform.b), CGToScalar(xform.d), CGToScalar(xform.ty),
-                   0, 0, SK_Scalar1);
+                                                   SkScalar sx = SK_Scalar1,
+                                                   SkScalar sy = SK_Scalar1) {
+    return CGAffineTransformMake( ScalarToCG(matrix[SkMatrix::kMScaleX] * sx),
+                                 -ScalarToCG(matrix[SkMatrix::kMSkewY]  * sy),
+                                 -ScalarToCG(matrix[SkMatrix::kMSkewX]  * sx),
+                                  ScalarToCG(matrix[SkMatrix::kMScaleY] * sy),
+                                  ScalarToCG(matrix[SkMatrix::kMTransX] * sx),
+                                  ScalarToCG(matrix[SkMatrix::kMTransY] * sy));
 }
 
 static SkScalar getFontScale(CGFontRef cgFont) {
@@ -265,30 +288,56 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
-#define BITMAP_INFO_RGB     (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
-#define BITMAP_INFO_GRAY    (kCGImageAlphaNone)
+#define BITMAP_INFO_RGB (kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
+#define BITMAP_INFO_GRAY (kCGImageAlphaNone)
+
+/**
+ * There does not appear to be a publicly accessable API for determining if lcd
+ * font smoothing will be applied if we request it. The main issue is that if
+ * smoothing is applied a gamma of 2.0 will be used, if not a gamma of 1.0.
+ */
+static bool supports_LCD() {
+    static int gSupportsLCD = -1;
+    if (gSupportsLCD >= 0) {
+        return (bool) gSupportsLCD;
+    }
+    uint32_t rgb = 0;
+    AutoCFRelease<CGColorSpaceRef> colorspace(CGColorSpaceCreateDeviceRGB());
+    AutoCFRelease<CGContextRef> cgContext(CGBitmapContextCreate(&rgb, 1, 1, 8, 4,
+                                                                colorspace, BITMAP_INFO_RGB));
+    CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
+    CGContextSetShouldSmoothFonts(cgContext, true);
+    CGContextSetShouldAntialias(cgContext, true);
+    CGContextSetTextDrawingMode(cgContext, kCGTextFill);
+    CGContextSetGrayFillColor(cgContext, 1, 1);
+    CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
+    uint32_t r = (rgb >> 16) & 0xFF;
+    uint32_t g = (rgb >>  8) & 0xFF;
+    uint32_t b = (rgb >>  0) & 0xFF;
+    gSupportsLCD = (r != g || r != b);
+    return (bool) gSupportsLCD;
+}
 
 class Offscreen {
 public:
     Offscreen();
-    ~Offscreen();
 
     CGRGBPixel* getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
-                      bool fgColorIsWhite, CGGlyph glyphID, size_t* rowBytesPtr);
-    
+                      CGGlyph glyphID, size_t* rowBytesPtr,
+                      bool generateA8FromLCD);
+
 private:
     enum {
         kSize = 32 * 32 * sizeof(CGRGBPixel)
     };
     SkAutoSMalloc<kSize> fImageStorage;
-    CGColorSpaceRef fRGBSpace;
+    AutoCFRelease<CGColorSpaceRef> fRGBSpace;
 
     // cached state
-    CGContextRef    fCG;
-    SkISize         fSize;
-    bool            fFgColorIsWhite;
-    bool            fDoAA;
-    bool            fDoLCD;
+    AutoCFRelease<CGContextRef> fCG;
+    SkISize fSize;
+    bool fDoAA;
+    bool fDoLCD;
 
     static int RoundSize(int dimension) {
         return SkNextPow2(dimension);
@@ -296,12 +345,7 @@
 };
 
 Offscreen::Offscreen() : fRGBSpace(NULL), fCG(NULL) {
-    fSize.set(0,0);
-}
-
-Offscreen::~Offscreen() {
-    CFSafeRelease(fCG);
-    CFSafeRelease(fRGBSpace);
+    fSize.set(0, 0);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -322,31 +366,24 @@
     return (SkTypeface::Style)style;
 }
 
-class AutoCFDataRelease {
-public:
-    AutoCFDataRelease(CFDataRef obj) : fObj(obj) {}
-    const uint16_t* getShortPtr() { 
-        return fObj ? (const uint16_t*) CFDataGetBytePtr(fObj) : NULL; 
-    }
-    ~AutoCFDataRelease() { CFSafeRelease(fObj); }
-private:
-    CFDataRef fObj;
-};
-
 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);
-    SkFontID id = (SkFontID)ats;
+    id = (SkFontID)ats;
     if (id != 0) {
         id &= 0x3FFFFFFF; // make top two bits 00
         return id;
     }
-    // CTFontGetPlatformFont returns NULL if the font is local 
+#endif
+    // CTFontGetPlatformFont returns NULL if the font is local
     // (e.g., was created by a CSS3 @font-face rule).
-    CGFontRef cgFont = CTFontCopyGraphicsFont(fontRef, NULL);
-    AutoCFDataRelease headRef(CGFontCopyTableForTag(cgFont, 'head'));
-    const uint16_t* headData = headRef.getShortPtr();
-    if (headData) {
-        id = (SkFontID) (headData[4] | headData[5] << 16); // checksum
+    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.
@@ -354,7 +391,6 @@
         id = (SkFontID) (uintptr_t) fontRef;
         id = (id & 0x3FFFFFFF) | 0x80000000; // make top two bits 10
     }
-    CGFontRelease(cgFont);
     return id;
 }
 
@@ -362,18 +398,22 @@
 public:
     SkTypeface_Mac(SkTypeface::Style style, SkFontID fontID, bool isMonospace,
                    CTFontRef fontRef, const char name[])
-    : SkTypeface(style, fontID, isMonospace) {
+    : SkTypeface(style, fontID, isMonospace)
+    , fName(name)
+    , fFontRef(fontRef) // caller has already called CFRetain for us
+    {
         SkASSERT(fontRef);
-        fFontRef = fontRef; // caller has already called CFRetain for us
-        fName.set(name);
     }
 
-    virtual ~SkTypeface_Mac() { CFRelease(fFontRef); }
-
-    SkString    fName;
-    CTFontRef   fFontRef;
+    SkString fName;
+    AutoCFRelease<CTFontRef> fFontRef;
 };
 
+static CTFontRef typeface_to_fontref(const SkTypeface* face) {
+    const SkTypeface_Mac* macface = reinterpret_cast<const SkTypeface_Mac*>(face);
+    return macface->fFontRef;
+}
+
 static SkTypeface* NewFromFontRef(CTFontRef fontRef, const char name[]) {
     SkASSERT(fontRef);
     bool isMonospace;
@@ -383,69 +423,61 @@
     return new SkTypeface_Mac(style, fontID, isMonospace, fontRef, name);
 }
 
-static SkTypeface* NewFromName(const char familyName[],
-                               SkTypeface::Style theStyle) {
-    CFMutableDictionaryRef      cfAttributes, cfTraits;
-    CFNumberRef                 cfFontTraits;
-    CTFontSymbolicTraits        ctFontTraits;
-    CTFontDescriptorRef         ctFontDesc;
-    CFStringRef                 cfFontName;
-    CTFontRef                   ctFont;
+static SkTypeface* NewFromName(const char familyName[], SkTypeface::Style theStyle) {
+    CTFontRef ctFont = NULL;
 
-
-    // Get the state we need
-    ctFontDesc   = NULL;
-    ctFont       = NULL;
-    ctFontTraits = 0;
-
+    CTFontSymbolicTraits ctFontTraits = 0;
     if (theStyle & SkTypeface::kBold) {
         ctFontTraits |= kCTFontBoldTrait;
     }
-
     if (theStyle & SkTypeface::kItalic) {
         ctFontTraits |= kCTFontItalicTrait;
     }
 
     // Create the font info
-    cfFontName   = CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8);
-    cfFontTraits = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits);
-    cfAttributes = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
-    cfTraits     = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    AutoCFRelease<CFStringRef> cfFontName(
+            CFStringCreateWithCString(NULL, familyName, kCFStringEncodingUTF8));
 
+    AutoCFRelease<CFNumberRef> cfFontTraits(
+            CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &ctFontTraits));
+
+    AutoCFRelease<CFMutableDictionaryRef> cfAttributes(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                      &kCFTypeDictionaryKeyCallBacks,
+                                      &kCFTypeDictionaryValueCallBacks));
+
+    AutoCFRelease<CFMutableDictionaryRef> cfTraits(
+            CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
+                                      &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);
+        CFDictionaryAddValue(cfAttributes, kCTFontTraitsAttribute, cfTraits);
 
-        ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
+        AutoCFRelease<CTFontDescriptorRef> ctFontDesc(
+                CTFontDescriptorCreateWithAttributes(cfAttributes));
+
         if (ctFontDesc != NULL) {
             if (isLeopard()) {
                 // CTFontCreateWithFontDescriptor on Leopard ignores the name
-                CTFontRef ctNamed = CTFontCreateWithName(cfFontName, 1, NULL);
-                ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL,
-                                                        ctFontDesc);
-                CFSafeRelease(ctNamed);
+                AutoCFRelease<CTFontRef> ctNamed(CTFontCreateWithName(cfFontName, 1, NULL));
+                ctFont = CTFontCreateCopyWithAttributes(ctNamed, 1, NULL, ctFontDesc);
             } else {
                 ctFont = CTFontCreateWithFontDescriptor(ctFontDesc, 0, NULL);
             }
         }
     }
 
-    CFSafeRelease(cfFontName);
-    CFSafeRelease(cfFontTraits);
-    CFSafeRelease(cfAttributes);
-    CFSafeRelease(cfTraits);
-    CFSafeRelease(ctFontDesc);
-
     return ctFont ? NewFromFontRef(ctFont, familyName) : NULL;
 }
 
 static CTFontRef GetFontRefFromFontID(SkFontID fontID) {
     SkTypeface_Mac* face = reinterpret_cast<SkTypeface_Mac*>(SkTypefaceCache::FindByID(fontID));
-    return face ? face->fFontRef : 0;
+    return face ? face->fFontRef.get() : NULL;
 }
 
 static SkTypeface* GetDefaultFace() {
@@ -466,7 +498,7 @@
 extern CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face);
 CTFontRef SkTypeface_GetCTFontRef(const SkTypeface* face) {
     const SkTypeface_Mac* macface = (const SkTypeface_Mac*)face;
-    return macface ? macface->fFontRef : NULL;
+    return macface ? macface->fFontRef.get() : NULL;
 }
 
 /*  This function is visible on the outside. It first searches the cache, and if
@@ -522,7 +554,6 @@
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style) {
     if (familyName) {
         familyName = map_css_names(familyName);
@@ -570,57 +601,49 @@
 
 class SkScalerContext_Mac : public SkScalerContext {
 public:
-                                        SkScalerContext_Mac(const SkDescriptor* desc);
-    virtual                            ~SkScalerContext_Mac(void);
+    SkScalerContext_Mac(const SkDescriptor* desc);
+    virtual ~SkScalerContext_Mac(void);
 
 
 protected:
-    unsigned                            generateGlyphCount(void);
-    uint16_t                            generateCharToGlyph(SkUnichar uni);
-    void                                generateAdvance(SkGlyph* glyph);
-    void                                generateMetrics(SkGlyph* glyph);
-    void                                generateImage(const SkGlyph& glyph);
-    void                                generatePath( const SkGlyph& glyph, SkPath* path);
-    void                                generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
-
+    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* mX, SkPaint::FontMetrics* mY) SK_OVERRIDE;
 
 private:
-    static void                         CTPathElement(void *info, const CGPathElement *element);
-    uint16_t                            getAdjustStart();
-    void                                getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const;
-    bool                                generateBBoxes();
+    static void CTPathElement(void *info, const CGPathElement *element);
+    uint16_t getFBoundingBoxesGlyphOffset();
+    void getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const;
+    bool generateBBoxes();
 
-private:
-    CGAffineTransform                   fTransform;
-    SkMatrix                            fUnitMatrix; // without font size
-    SkMatrix                            fVerticalMatrix; // unit rotated
-    SkMatrix                            fMatrix; // with font size
-    SkMatrix                            fAdjustBadMatrix; // lion-specific fix
-#ifdef SK_USE_COLOR_LUMINANCE
-    Offscreen                           fBlackScreen;
-    Offscreen                           fWhiteScreen;
-#else
-    Offscreen                           fOffscreen;
-#endif
-    CTFontRef                           fCTFont;
-    CTFontRef                           fCTVerticalFont; // for vertical advance
-    CGFontRef                           fCGFont;
-    GlyphRect*                          fAdjustBad;
-    uint16_t                            fAdjustStart;
-    uint16_t                            fGlyphCount;
-    bool                                fGeneratedBBoxes;
-    bool                                fDoSubPosition;
-    bool                                fVertical;
+    CGAffineTransform fTransform;
+    SkMatrix fUnitMatrix; // without font size
+    SkMatrix fVerticalMatrix; // unit rotated
+    SkMatrix fMatrix; // with font size
+    SkMatrix fFBoundingBoxesMatrix; // lion-specific fix
+    Offscreen fOffscreen;
+    AutoCFRelease<CTFontRef> fCTFont;
+    AutoCFRelease<CTFontRef> fCTVerticalFont; // for vertical advance
+    AutoCFRelease<CGFontRef> fCGFont;
+    GlyphRect* fFBoundingBoxes;
+    uint16_t fFBoundingBoxesGlyphOffset;
+    uint16_t fGlyphCount;
+    bool fGeneratedFBoundingBoxes;
+    bool fDoSubPosition;
+    bool fVertical;
 
-    friend class                        Offscreen;
+    friend class Offscreen;
 };
 
 SkScalerContext_Mac::SkScalerContext_Mac(const SkDescriptor* desc)
         : SkScalerContext(desc)
-        , fCTVerticalFont(NULL)
-        , fAdjustBad(NULL)
-        , fAdjustStart(0)
-        , fGeneratedBBoxes(false)
+        , fFBoundingBoxes(NULL)
+        , fFBoundingBoxesGlyphOffset(0)
+        , fGeneratedFBoundingBoxes(false)
 {
     CTFontRef ctFont = GetFontRefFromFontID(fRec.fFontID);
     CFIndex numGlyphs = CTFontGetGlyphCount(ctFont);
@@ -651,31 +674,26 @@
     }
     flip(&fUnitMatrix); // flip to fix up bounds later
     fVertical = SkToBool(fRec.fFlags & kVertical_Flag);
-    CTFontDescriptorRef ctFontDesc = NULL;
+    AutoCFRelease<CTFontDescriptorRef> ctFontDesc;
     if (fVertical) {
-        CFMutableDictionaryRef cfAttributes = CFDictionaryCreateMutable(
-                kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
-                &kCFTypeDictionaryValueCallBacks);
+        AutoCFRelease<CFMutableDictionaryRef> cfAttributes(CFDictionaryCreateMutable(
+                kCFAllocatorDefault, 0,
+                &kCFTypeDictionaryKeyCallBacks,
+                &kCFTypeDictionaryValueCallBacks));
         if (cfAttributes) {
             CTFontOrientation ctOrientation = kCTFontVerticalOrientation;
-            CFNumberRef cfVertical = CFNumberCreate(kCFAllocatorDefault,
-                    kCFNumberSInt32Type, &ctOrientation);
-            CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute,
-                    cfVertical);
-            CFSafeRelease(cfVertical);
+            AutoCFRelease<CFNumberRef> cfVertical(CFNumberCreate(
+                    kCFAllocatorDefault, kCFNumberSInt32Type, &ctOrientation));
+            CFDictionaryAddValue(cfAttributes, kCTFontOrientationAttribute, cfVertical);
             ctFontDesc = CTFontDescriptorCreateWithAttributes(cfAttributes);
-            CFRelease(cfAttributes);
         }
     }
-    fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform,
-            ctFontDesc);
-    CFSafeRelease(ctFontDesc);
+    fCTFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, ctFontDesc);
     fCGFont = CTFontCopyGraphicsFont(fCTFont, NULL);
     if (fVertical) {
         CGAffineTransform rotateLeft = CGAffineTransformMake(0, -1, 1, 0, 0, 0);
         transform = CGAffineTransformConcat(rotateLeft, transform);
-        fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize,
-                &transform, NULL);
+        fCTVerticalFont = CTFontCreateCopyWithAttributes(ctFont, unitFontSize, &transform, NULL);
         fVerticalMatrix = fUnitMatrix;
         if (isSnowLeopard()) {
             SkScalar scale = SkScalarMul(fRec.fTextSize, getFontScale(fCGFont));
@@ -690,15 +708,16 @@
 }
 
 SkScalerContext_Mac::~SkScalerContext_Mac() {
-    delete[] fAdjustBad;
-    CFSafeRelease(fCTFont);
-    CFSafeRelease(fCTVerticalFont);
-    CFSafeRelease(fCGFont);
+    delete[] fFBoundingBoxes;
 }
 
 CGRGBPixel* Offscreen::getCG(const SkScalerContext_Mac& context, const SkGlyph& glyph,
-                             bool fgColorIsWhite, CGGlyph glyphID, size_t* rowBytesPtr) {
+                             CGGlyph glyphID, size_t* rowBytesPtr,
+                             bool generateA8FromLCD) {
     if (!fRGBSpace) {
+        //It doesn't appear to matter what color space is specified.
+        //Regular blends and antialiased text are always (s*a + d*(1-a))
+        //and smoothed text is always g=2.0.
         fRGBSpace = CGColorSpaceCreateDeviceRGB();
     }
 
@@ -706,23 +725,19 @@
     bool doAA = false;
     bool doLCD = false;
 
-    switch (glyph.fMaskFormat) {
-        case SkMask::kLCD16_Format:
-        case SkMask::kLCD32_Format:
-            doLCD = true;
-            doAA = true;
-            break;
-        case SkMask::kA8_Format:
-            doLCD = false;
-            doAA = true;
-            break;
-        default:
-            break;
+    if (SkMask::kBW_Format != glyph.fMaskFormat) {
+        doLCD = true;
+        doAA = true;
+    }
+
+    // FIXME: lcd smoothed un-hinted rasterization unsupported.
+    if (!generateA8FromLCD && SkMask::kA8_Format == glyph.fMaskFormat) {
+        doLCD = false;
+        doAA = true;
     }
 
     size_t rowBytes = fSize.fWidth * sizeof(CGRGBPixel);
     if (!fCG || fSize.fWidth < glyph.fWidth || fSize.fHeight < glyph.fHeight) {
-        CFSafeRelease(fCG);
         if (fSize.fWidth < glyph.fWidth) {
             fSize.fWidth = RoundSize(glyph.fWidth);
         }
@@ -747,11 +762,14 @@
 
         CGContextSetAllowsFontSubpixelPositioning(fCG, context.fDoSubPosition);
         CGContextSetShouldSubpixelPositionFonts(fCG, context.fDoSubPosition);
-        
+
+        // Draw white on black to create mask.
+        // TODO: Draw black on white and invert, CG has a special case codepath.
+        CGContextSetGrayFillColor(fCG, 1.0f, 1.0f);
+
         // force our checks below to happen
         fDoAA = !doAA;
         fDoLCD = !doLCD;
-        fFgColorIsWhite = !fgColorIsWhite;
     }
 
     if (fDoAA != doAA) {
@@ -762,23 +780,13 @@
         CGContextSetShouldSmoothFonts(fCG, doLCD);
         fDoLCD = doLCD;
     }
-    if (fFgColorIsWhite != fgColorIsWhite) {
-        CGContextSetGrayFillColor(fCG, fgColorIsWhite ? 1.0 : 0, 1.0);
-        fFgColorIsWhite = fgColorIsWhite;
-    }
 
     CGRGBPixel* image = (CGRGBPixel*)fImageStorage.get();
     // skip rows based on the glyph's height
     image += (fSize.fHeight - glyph.fHeight) * fSize.fWidth;
 
-    // erase with the "opposite" of the fgColor
-    uint32_t erase = fgColorIsWhite ? 0 : ~0;
-#if 0
-    sk_memset_rect(image, erase, glyph.fWidth * sizeof(CGRGBPixel),
-                   glyph.fHeight, rowBytes);
-#else
-    sk_memset_rect32(image, erase, glyph.fWidth, glyph.fHeight, rowBytes);
-#endif
+    // erase to black
+    sk_memset_rect32(image, 0, glyph.fWidth, glyph.fHeight, rowBytes);
 
     float subX = 0;
     float subY = 0;
@@ -804,8 +812,8 @@
 void SkScalerContext_Mac::getVerticalOffset(CGGlyph glyphID, SkIPoint* offset) const {
     CGSize vertOffset;
     CTFontGetVerticalTranslationsForGlyphs(fCTVerticalFont, &glyphID, &vertOffset, 1);
-    const SkPoint trans = {SkFloatToScalar(vertOffset.width),
-                           SkFloatToScalar(vertOffset.height)};
+    const SkPoint trans = {CGToScalar(vertOffset.width),
+                           CGToScalar(vertOffset.height)};
     SkPoint floatOffset;
     fVerticalMatrix.mapPoints(&floatOffset, &trans, 1);
     if (!isSnowLeopard()) {
@@ -819,48 +827,16 @@
     offset->fY = SkScalarRound(floatOffset.fY);
 }
 
-/* from http://developer.apple.com/fonts/TTRefMan/RM06/Chap6loca.html
- * There are two versions of this table, the short and the long. The version
- * used is specified in the Font Header ('head') table in the indexToLocFormat
- * field. The choice of long or short offsets is dependent on the maximum
- * possible offset distance.
- *
- * 'loca' short version: The actual local offset divided by 2 is stored. 
- * 'loca' long version: The actual local offset is stored.
- * 
- * The result is a offset into a table of 2 byte (16 bit) entries.
- */
-static uint32_t getLocaTableEntry(const uint16_t*& locaPtr, int locaFormat) {
-    uint32_t data = SkEndian_SwapBE16(*locaPtr++); // short
-    if (locaFormat) {
-        data = data << 15 | SkEndian_SwapBE16(*locaPtr++) >> 1; // long
+uint16_t SkScalerContext_Mac::getFBoundingBoxesGlyphOffset() {
+    if (fFBoundingBoxesGlyphOffset) {
+        return fFBoundingBoxesGlyphOffset;
     }
-    return data;
-}
-
-// see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6hhea.html
-static uint16_t getNumLongMetrics(const uint16_t* hheaData) {
-    const int kNumOfLongHorMetrics = 17;
-    return SkEndian_SwapBE16(hheaData[kNumOfLongHorMetrics]);
-}
-
-// see http://developer.apple.com/fonts/TTRefMan/RM06/Chap6head.html
-static int getLocaFormat(const uint16_t* headData) {
-    const int kIndexToLocFormat = 25;
-    return SkEndian_SwapBE16(headData[kIndexToLocFormat]);
-}
-
-uint16_t SkScalerContext_Mac::getAdjustStart() {
-    if (fAdjustStart) {
-        return fAdjustStart;
+    fFBoundingBoxesGlyphOffset = fGlyphCount; // fallback for all fonts
+    AutoCGTable<SkOTTableHorizontalHeader> hheaTable(fCGFont);
+    if (hheaTable.fData) {
+        fFBoundingBoxesGlyphOffset = SkEndian_SwapBE16(hheaTable->numberOfHMetrics);
     }
-    fAdjustStart = fGlyphCount; // fallback for all fonts
-    AutoCFDataRelease hheaRef(CGFontCopyTableForTag(fCGFont, 'hhea'));
-    const uint16_t* hheaData = hheaRef.getShortPtr();
-    if (hheaData) {
-        fAdjustStart = getNumLongMetrics(hheaData);
-    }
-    return fAdjustStart;
+    return fFBoundingBoxesGlyphOffset;
 }
 
 /*
@@ -869,78 +845,73 @@
  * glyph count. This workaround reads the glyph bounds from the font directly.
  *
  * The table is computed only if the font is a TrueType font, if the glyph
- * value is >= fAdjustStart. (called only if fAdjustStart < fGlyphCount).
+ * value is >= fFBoundingBoxesGlyphOffset. (called only if fFBoundingBoxesGlyphOffset < fGlyphCount).
  *
- * TODO: A future optimization will compute fAdjustBad once per CGFont, and
- * compute fAdjustBadMatrix once per font context.
+ * TODO: A future optimization will compute fFBoundingBoxes once per CGFont, and
+ * compute fFBoundingBoxesMatrix once per font context.
  */
 bool SkScalerContext_Mac::generateBBoxes() {
-    if (fGeneratedBBoxes) {
-        return NULL != fAdjustBad;
+    if (fGeneratedFBoundingBoxes) {
+        return NULL != fFBoundingBoxes;
     }
-    fGeneratedBBoxes = true;
-    AutoCFDataRelease headRef(CGFontCopyTableForTag(fCGFont, 'head'));
-    const uint16_t* headData = headRef.getShortPtr();
-    if (!headData) {
+    fGeneratedFBoundingBoxes = true;
+
+    AutoCGTable<SkOTTableHead> headTable(fCGFont);
+    if (!headTable.fData) {
         return false;
     }
-    AutoCFDataRelease locaRef(CGFontCopyTableForTag(fCGFont, 'loca'));
-    const uint16_t* locaData = locaRef.getShortPtr();
-    if (!locaData) {
+
+    AutoCGTable<SkOTTableIndexToLocation> locaTable(fCGFont);
+    if (!locaTable.fData) {
         return false;
     }
-    AutoCFDataRelease glyfRef(CGFontCopyTableForTag(fCGFont, 'glyf'));
-    const uint16_t* glyfData = glyfRef.getShortPtr();
-    if (!glyfData) {
+
+    AutoCGTable<SkOTTableGlyph> glyfTable(fCGFont);
+    if (!glyfTable.fData) {
         return false;
     }
-    CFIndex entries = fGlyphCount - fAdjustStart;
-    fAdjustBad = new GlyphRect[entries];
-    int locaFormat = getLocaFormat(headData);
-    const uint16_t* locaPtr = &locaData[fAdjustStart << locaFormat];
-    uint32_t last = getLocaTableEntry(locaPtr, locaFormat);
-    for (CFIndex index = 0; index < entries; ++index) {
-        uint32_t offset = getLocaTableEntry(locaPtr, locaFormat);
-        GlyphRect& rect = fAdjustBad[index];
-        if (offset != last) {
-            rect.fMinX = SkEndian_SwapBE16(glyfData[last + 1]);
-            rect.fMinY = SkEndian_SwapBE16(glyfData[last + 2]);
-            rect.fMaxX = SkEndian_SwapBE16(glyfData[last + 3]);
-            rect.fMaxY = SkEndian_SwapBE16(glyfData[last + 4]);
-        } else {
-            sk_bzero(&rect, sizeof(GlyphRect));
-        }
-        last = offset;
+
+    uint16_t entries = fGlyphCount - fFBoundingBoxesGlyphOffset;
+    fFBoundingBoxes = new GlyphRect[entries];
+
+    SkOTTableHead::IndexToLocFormat locaFormat = headTable->indexToLocFormat;
+    SkOTTableGlyph::Iterator glyphDataIter(*glyfTable.fData, *locaTable.fData, locaFormat);
+    glyphDataIter.advance(fFBoundingBoxesGlyphOffset);
+    for (uint16_t boundingBoxesIndex = 0; boundingBoxesIndex < entries; ++boundingBoxesIndex) {
+        const SkOTTableGlyphData* glyphData = glyphDataIter.next();
+        GlyphRect& rect = fFBoundingBoxes[boundingBoxesIndex];
+        rect.fMinX = SkEndian_SwapBE16(glyphData->xMin);
+        rect.fMinY = SkEndian_SwapBE16(glyphData->yMin);
+        rect.fMaxX = SkEndian_SwapBE16(glyphData->xMax);
+        rect.fMaxY = SkEndian_SwapBE16(glyphData->yMax);
     }
-    fAdjustBadMatrix = fMatrix;
-    flip(&fAdjustBadMatrix);
+    fFBoundingBoxesMatrix = fMatrix;
+    flip(&fFBoundingBoxesMatrix);
     SkScalar fontScale = getFontScale(fCGFont);
-    fAdjustBadMatrix.preScale(fontScale, fontScale);
+    fFBoundingBoxesMatrix.preScale(fontScale, fontScale);
     return true;
 }
 
-unsigned SkScalerContext_Mac::generateGlyphCount(void)
-{
-    return(fGlyphCount);
+unsigned SkScalerContext_Mac::generateGlyphCount(void) {
+    return fGlyphCount;
 }
 
-uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni)
-{   CGGlyph     cgGlyph;
+uint16_t SkScalerContext_Mac::generateCharToGlyph(SkUnichar uni) {
+    CGGlyph     cgGlyph;
     UniChar     theChar;
 
-
     // Validate our parameters and state
-    SkASSERT(uni             <= 0x0000FFFF);
+    SkASSERT(uni <= 0x0000FFFF);
     SkASSERT(sizeof(CGGlyph) <= sizeof(uint16_t));
 
-
     // Get the glyph
     theChar = (UniChar) uni;
 
-    if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1))
+    if (!CTFontGetGlyphsForCharacters(fCTFont, &theChar, &cgGlyph, 1)) {
         cgGlyph = 0;
+    }
 
-    return(cgGlyph);
+    return cgGlyph;
 }
 
 void SkScalerContext_Mac::generateAdvance(SkGlyph* glyph) {
@@ -948,32 +919,31 @@
 }
 
 void SkScalerContext_Mac::generateMetrics(SkGlyph* glyph) {
-    CGSize      theAdvance;
-    CGRect      theBounds;
-    CGGlyph     cgGlyph;
+    CGSize advance;
+    CGRect bounds;
+    CGGlyph cgGlyph;
 
     // Get the state we need
     cgGlyph = (CGGlyph) glyph->getGlyphID(fBaseGlyphCount);
 
     if (fVertical) {
         if (!isSnowLeopard()) {
-        // Lion and Leopard respect the vertical font metrics.
-            CTFontGetBoundingRectsForGlyphs(fCTVerticalFont,
-                                            kCTFontVerticalOrientation, 
-                                            &cgGlyph, &theBounds,  1);
+            // Lion and Leopard respect the vertical font metrics.
+            CTFontGetBoundingRectsForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
+                                            &cgGlyph, &bounds, 1);
         } else {
-        // Snow Leopard and earlier respect the vertical font metrics for
-        // advances, but not bounds, so use the default box and adjust it below.
+            // Snow Leopard and earlier respect the vertical font metrics for
+            // advances, but not bounds, so use the default box and adjust it below.
             CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
-                                            &cgGlyph, &theBounds,  1);
+                                            &cgGlyph, &bounds, 1);
         }
         CTFontGetAdvancesForGlyphs(fCTVerticalFont, kCTFontVerticalOrientation,
-                                   &cgGlyph, &theAdvance, 1);
+                                   &cgGlyph, &advance, 1);
     } else {
         CTFontGetBoundingRectsForGlyphs(fCTFont, kCTFontDefaultOrientation,
-                                        &cgGlyph, &theBounds, 1);
+                                        &cgGlyph, &bounds, 1);
         CTFontGetAdvancesForGlyphs(fCTFont, kCTFontDefaultOrientation,
-                                   &cgGlyph, &theAdvance, 1);
+                                   &cgGlyph, &advance, 1);
     }
 
     // BUG?
@@ -981,24 +951,21 @@
     // it should be empty. So, if we see a zero-advance, we check if it has an
     // empty path or not, and if so, we jam the bounds to 0. Hopefully a zero-advance
     // is rare, so we won't incur a big performance cost for this extra check.
-    if (0 == theAdvance.width && 0 == theAdvance.height) {
-        CGPathRef path = CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL);
+    if (0 == advance.width && 0 == advance.height) {
+        AutoCFRelease<CGPathRef> path(CTFontCreatePathForGlyph(fCTFont, cgGlyph, NULL));
         if (NULL == path || CGPathIsEmpty(path)) {
-            theBounds = CGRectMake(0, 0, 0, 0);
-        }
-        if (path) {
-            CGPathRelease(path);
+            bounds = CGRectMake(0, 0, 0, 0);
         }
     }
-    
-    glyph->zeroMetrics();
-    glyph->fAdvanceX =  SkFloatToFixed(theAdvance.width);
-    glyph->fAdvanceY = -SkFloatToFixed(theAdvance.height);
 
-    if (CGRectIsEmpty_inline(theBounds)) {
+    glyph->zeroMetrics();
+    glyph->fAdvanceX =  SkFloatToFixed_Check(advance.width);
+    glyph->fAdvanceY = -SkFloatToFixed_Check(advance.height);
+
+    if (CGRectIsEmpty_inline(bounds)) {
         return;
     }
-    
+
     if (isLeopard() && !fVertical) {
         // Leopard does not consider the matrix skew in its bounds.
         // Run the bounding rectangle through the skew matrix to determine
@@ -1006,13 +973,13 @@
         // FIXME (Leopard): If the font has synthetic italic (e.g., matrix skew)
         // and the font is vertical, the bounds need to be recomputed.
         SkRect glyphBounds = SkRect::MakeXYWH(
-                theBounds.origin.x, theBounds.origin.y,
-                theBounds.size.width, theBounds.size.height);
+                bounds.origin.x, bounds.origin.y,
+                bounds.size.width, bounds.size.height);
         fUnitMatrix.mapRect(&glyphBounds);
-        theBounds.origin.x = glyphBounds.fLeft;
-        theBounds.origin.y = glyphBounds.fTop;
-        theBounds.size.width = glyphBounds.width();
-        theBounds.size.height = glyphBounds.height();
+        bounds.origin.x = glyphBounds.fLeft;
+        bounds.origin.y = glyphBounds.fTop;
+        bounds.size.width = glyphBounds.width();
+        bounds.size.height = glyphBounds.height();
     }
     // Adjust the bounds
     //
@@ -1020,34 +987,33 @@
     // to transform the bounding box ourselves.
     //
     // The bounds are also expanded by 1 pixel, to give CG room for anti-aliasing.
-    CGRectInset_inline(&theBounds, -1, -1);
+    CGRectInset_inline(&bounds, -1, -1);
 
     // Get the metrics
     bool lionAdjustedMetrics = false;
-    if (isLion()) {
-        if (cgGlyph < fGlyphCount && cgGlyph >= getAdjustStart() 
-                    && generateBBoxes()) {
+    if (isLion() || isMountainLion()) {
+        if (cgGlyph < fGlyphCount && cgGlyph >= getFBoundingBoxesGlyphOffset() && generateBBoxes()){
             lionAdjustedMetrics = true;
             SkRect adjust;
-            const GlyphRect& gRect = fAdjustBad[cgGlyph - fAdjustStart];
+            const GlyphRect& gRect = fFBoundingBoxes[cgGlyph - fFBoundingBoxesGlyphOffset];
             adjust.set(gRect.fMinX, gRect.fMinY, gRect.fMaxX, gRect.fMaxY);
-            fAdjustBadMatrix.mapRect(&adjust);
-            theBounds.origin.x = SkScalarToFloat(adjust.fLeft) - 1;
-            theBounds.origin.y = SkScalarToFloat(adjust.fTop) - 1;
+            fFBoundingBoxesMatrix.mapRect(&adjust);
+            bounds.origin.x = SkScalarToFloat(adjust.fLeft) - 1;
+            bounds.origin.y = SkScalarToFloat(adjust.fTop) - 1;
         }
         // Lion returns fractions in the bounds
-        glyph->fWidth = sk_float_ceil2int(theBounds.size.width);
-        glyph->fHeight = sk_float_ceil2int(theBounds.size.height);
+        glyph->fWidth = SkToU16(sk_float_ceil2int(bounds.size.width));
+        glyph->fHeight = SkToU16(sk_float_ceil2int(bounds.size.height));
     } else {
-        glyph->fWidth = sk_float_round2int(theBounds.size.width);
-        glyph->fHeight = sk_float_round2int(theBounds.size.height);
+        glyph->fWidth = SkToU16(sk_float_round2int(bounds.size.width));
+        glyph->fHeight = SkToU16(sk_float_round2int(bounds.size.height));
     }
-    glyph->fTop      = -sk_float_round2int(CGRectGetMaxY_inline(theBounds));
-    glyph->fLeft     =  sk_float_round2int(CGRectGetMinX_inline(theBounds));
+    glyph->fTop = SkToS16(-sk_float_round2int(CGRectGetMaxY_inline(bounds)));
+    glyph->fLeft = SkToS16(sk_float_round2int(CGRectGetMinX_inline(bounds)));
     SkIPoint offset;
     if (fVertical && (isSnowLeopard() || lionAdjustedMetrics)) {
-    // SnowLeopard doesn't respect vertical metrics, so compute them manually.
-    // Also compute them for Lion when the metrics were computed by hand.
+        // SnowLeopard doesn't respect vertical metrics, so compute them manually.
+        // Also compute them for Lion when the metrics were computed by hand.
         getVerticalOffset(cgGlyph, &offset);
         glyph->fLeft += offset.fX;
         glyph->fTop += offset.fY;
@@ -1059,60 +1025,27 @@
 static void build_power_table(uint8_t table[], float ee) {
     for (int i = 0; i < 256; i++) {
         float x = i / 255.f;
-        x = powf(x, ee);
+        x = sk_float_pow(x, ee);
         int xx = SkScalarRoundToInt(SkFloatToScalar(x * 255));
         table[i] = SkToU8(xx);
     }
 }
 
-static const uint8_t* getInverseTable(bool isWhite) {
-    static uint8_t gWhiteTable[256];
-    static uint8_t gTable[256];
+/**
+ *  This will invert the gamma applied by CoreGraphics, so we can get linear
+ *  values.
+ *
+ *  CoreGraphics obscurely defaults to 2.0 as the smoothing gamma value.
+ *  The color space used does not appear to affect this choice.
+ */
+static const uint8_t* getInverseGammaTableCoreGraphicSmoothing() {
     static bool gInited;
+    static uint8_t gTableCoreGraphicsSmoothing[256];
     if (!gInited) {
-        build_power_table(gWhiteTable, 1.5f);
-        build_power_table(gTable, 2.2f);
+        build_power_table(gTableCoreGraphicsSmoothing, 2.0f);
         gInited = true;
     }
-    return isWhite ? gWhiteTable : gTable;
-}
-
-static const uint8_t* getGammaTable(U8CPU luminance) {
-    static uint8_t gGammaTables[4][256];
-    static bool gInited;
-    if (!gInited) {
-#if 1
-        float start = 1.1;
-        float stop = 2.1;
-        for (int i = 0; i < 4; ++i) {
-            float g = start + (stop - start) * i / 3;
-            build_power_table(gGammaTables[i], 1/g);
-        }
-#else
-        build_power_table(gGammaTables[0], 1);
-        build_power_table(gGammaTables[1], 1);
-        build_power_table(gGammaTables[2], 1);
-        build_power_table(gGammaTables[3], 1);
-#endif
-        gInited = true;
-    }
-    SkASSERT(0 == (luminance >> 8));
-    return gGammaTables[luminance >> 6];
-}
-
-static void invertGammaMask(bool isWhite, CGRGBPixel rgb[], int width,
-                            int height, size_t rb) {
-    const uint8_t* table = getInverseTable(isWhite);
-    for (int y = 0; y < height; ++y) {
-        for (int x = 0; x < width; ++x) {
-            uint32_t c = rgb[x];
-            int r = (c >> 16) & 0xFF;
-            int g = (c >>  8) & 0xFF;
-            int b = (c >>  0) & 0xFF;
-            rgb[x] = (table[r] << 16) | (table[g] << 8) | table[b];
-        }
-        rgb = (CGRGBPixel*)((char*)rgb + rb);
-    }
+    return gTableCoreGraphicsSmoothing;
 }
 
 static void cgpixels_to_bits(uint8_t dst[], const CGRGBPixel src[], int count) {
@@ -1128,236 +1061,162 @@
     }
 }
 
-static int lerpScale(int dst, int src, int scale) {
-    return dst + (scale * (src - dst) >> 23);
+template<bool APPLY_PREBLEND>
+static inline uint8_t rgb_to_a8(CGRGBPixel rgb, const uint8_t* table8) {
+    U8CPU r = (rgb >> 16) & 0xFF;
+    U8CPU g = (rgb >>  8) & 0xFF;
+    U8CPU b = (rgb >>  0) & 0xFF;
+    return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
 }
+template<bool APPLY_PREBLEND>
+static void rgb_to_a8(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes,
+                      const SkGlyph& glyph, const uint8_t* table8) {
+    const int width = glyph.fWidth;
+    size_t dstRB = glyph.rowBytes();
+    uint8_t* SK_RESTRICT dst = (uint8_t*)glyph.fImage;
 
-static CGRGBPixel lerpPixel(CGRGBPixel dst, CGRGBPixel src,
-                            int scaleR, int scaleG, int scaleB) {
-    int sr = (src >> 16) & 0xFF;
-    int sg = (src >>  8) & 0xFF;
-    int sb = (src >>  0) & 0xFF;
-    int dr = (dst >> 16) & 0xFF;
-    int dg = (dst >>  8) & 0xFF;
-    int db = (dst >>  0) & 0xFF;
-
-    int rr = lerpScale(dr, sr, scaleR);
-    int rg = lerpScale(dg, sg, scaleG);
-    int rb = lerpScale(db, sb, scaleB);
-    return (rr << 16) | (rg << 8) | rb;
-}
-
-static void lerpPixels(CGRGBPixel dst[], const CGRGBPixel src[], int width,
-                       int height, int rowBytes, int lumBits) {
-#ifdef SK_USE_COLOR_LUMINANCE
-    int scaleR = (1 << 23) * SkColorGetR(lumBits) / 0xFF;
-    int scaleG = (1 << 23) * SkColorGetG(lumBits) / 0xFF;
-    int scaleB = (1 << 23) * SkColorGetB(lumBits) / 0xFF;
-#else
-    int scale = (1 << 23) * lumBits / SkScalerContext::kLuminance_Max;
-    int scaleR = scale;
-    int scaleG = scale;
-    int scaleB = scale;
-#endif
-
-    for (int y = 0; y < height; ++y) {
-        for (int x = 0; x < width; ++x) {
-            // bit-not the src, since it was drawn from black, so we need the
-            // compliment of those bits
-            dst[x] = lerpPixel(dst[x], ~src[x], scaleR, scaleG, scaleB);
+    for (int y = 0; y < glyph.fHeight; y++) {
+        for (int i = 0; i < width; ++i) {
+            dst[i] = rgb_to_a8<APPLY_PREBLEND>(cgPixels[i], table8);
         }
-        src = (CGRGBPixel*)((char*)src + rowBytes);
-        dst = (CGRGBPixel*)((char*)dst + rowBytes);
+        cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+        dst += dstRB;
     }
 }
 
-#if 1
-static inline int r32_to_16(int x) { return SkR32ToR16(x); }
-static inline int g32_to_16(int x) { return SkG32ToG16(x); }
-static inline int b32_to_16(int x) { return SkB32ToB16(x); }
-#else
-static inline int round8to5(int x) {
-    return (x + 3 - (x >> 5) + (x >> 7)) >> 3;
+template<bool APPLY_PREBLEND>
+static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb, const uint8_t* tableR,
+                                                    const uint8_t* tableG,
+                                                    const uint8_t* tableB) {
+    U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+    U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  8) & 0xFF, tableG);
+    U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  0) & 0xFF, tableB);
+    return SkPack888ToRGB16(r, g, b);
 }
-static inline int round8to6(int x) {
-    int xx = (x + 1 - (x >> 6) + (x >> 7)) >> 2;
-    SkASSERT((unsigned)xx <= 63);
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd16(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
+                         const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+    const int width = glyph.fWidth;
+    size_t dstRB = glyph.rowBytes();
+    uint16_t* SK_RESTRICT dst = (uint16_t*)glyph.fImage;
 
-    int ix = x >> 2;
-    SkASSERT(SkAbs32(xx - ix) <= 1);
-    return xx;
+    for (int y = 0; y < glyph.fHeight; y++) {
+        for (int i = 0; i < width; i++) {
+            dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+        }
+        cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+        dst = (uint16_t*)((char*)dst + dstRB);
+    }
 }
 
-static inline int r32_to_16(int x) { return round8to5(x); }
-static inline int g32_to_16(int x) { return round8to6(x); }
-static inline int b32_to_16(int x) { return round8to5(x); }
-#endif
-
-static inline uint16_t rgb_to_lcd16(CGRGBPixel rgb) {
-    int r = (rgb >> 16) & 0xFF;
-    int g = (rgb >>  8) & 0xFF;
-    int b = (rgb >>  0) & 0xFF;
-
-    return SkPackRGB16(r32_to_16(r), g32_to_16(g), b32_to_16(b));
-}
-
-static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb) {
-    int r = (rgb >> 16) & 0xFF;
-    int g = (rgb >>  8) & 0xFF;
-    int b = (rgb >>  0) & 0xFF;
-
+template<bool APPLY_PREBLEND>
+static inline uint32_t rgb_to_lcd32(CGRGBPixel rgb, const uint8_t* tableR,
+                                                    const uint8_t* tableG,
+                                                    const uint8_t* tableB) {
+    U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+    U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  8) & 0xFF, tableG);
+    U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  0) & 0xFF, tableB);
     return SkPackARGB32(0xFF, r, g, b);
 }
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd32(const CGRGBPixel* SK_RESTRICT cgPixels, size_t cgRowBytes, const SkGlyph& glyph,
+                         const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
+    const int width = glyph.fWidth;
+    size_t dstRB = glyph.rowBytes();
+    uint32_t* SK_RESTRICT dst = (uint32_t*)glyph.fImage;
+    for (int y = 0; y < glyph.fHeight; y++) {
+        for (int i = 0; i < width; i++) {
+            dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(cgPixels[i], tableR, tableG, tableB);
+        }
+        cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+        dst = (uint32_t*)((char*)dst + dstRB);
+    }
+}
 
-#define BLACK_LUMINANCE_LIMIT   0x40
-#define WHITE_LUMINANCE_LIMIT   0xA0
+template <typename T> T* SkTAddByteOffset(T* ptr, size_t byteOffset) {
+    return (T*)((char*)ptr + byteOffset);
+}
 
 void SkScalerContext_Mac::generateImage(const SkGlyph& glyph) {
     CGGlyph cgGlyph = (CGGlyph) glyph.getGlyphID(fBaseGlyphCount);
 
-    const bool isLCD = isLCDFormat(glyph.fMaskFormat);
-    const bool isBW = SkMask::kBW_Format == glyph.fMaskFormat;
-    const bool isA8 = !isLCD && !isBW;
-    
-#ifdef SK_USE_COLOR_LUMINANCE
-    unsigned lumBits = fRec.getLuminanceColor();
-    uint32_t xorMask = 0;
-
-    if (isA8) {
-        // for A8, we just want a component (they're all the same)
-        lumBits = SkColorGetR(lumBits);
-    }
-#else
-    bool fgColorIsWhite = true;
-    bool isWhite = fRec.getLuminanceByte() >= WHITE_LUMINANCE_LIMIT;
-    bool isBlack = fRec.getLuminanceByte() <= BLACK_LUMINANCE_LIMIT;
-    uint32_t xorMask;
-    bool invertGamma = false;
-
-    /*  For LCD16, we first create a temp offscreen cg-context in 32bit,
-     *  erase to white, and then draw a black glyph into it. Then we can
-     *  extract the r,g,b values, invert-them, and now we have the original
-     *  src mask components, which we pack into our 16bit mask.
-     */
-    if (isLCD) {
-        if (isBlack) {
-            xorMask = ~0;
-            fgColorIsWhite = false;
-        } else {    /* white or neutral */
-            xorMask = 0;
-            invertGamma = true;
-        }
-    }
-#endif
-
-    size_t cgRowBytes;
-#ifdef SK_USE_COLOR_LUMINANCE
-    CGRGBPixel* cgPixels;
-    const uint8_t* gammaTable = NULL;
-    
-    if (isLCD) {
-        CGRGBPixel* wtPixels = NULL;
-        CGRGBPixel* bkPixels = NULL;
-        bool needBlack = true;
-        bool needWhite = true;
-
-        if (SK_ColorWHITE == lumBits) {
-            needBlack = false;
-        } else if (SK_ColorBLACK == lumBits) {
-            needWhite = false;
-        }
-        
-        if (needBlack) {
-            bkPixels = fBlackScreen.getCG(*this, glyph, false, cgGlyph, &cgRowBytes);
-            cgPixels = bkPixels;
-            xorMask = ~0;
-        }
-        if (needWhite) {
-            wtPixels = fWhiteScreen.getCG(*this, glyph, true, cgGlyph, &cgRowBytes);
-            cgPixels = wtPixels;
-            xorMask = 0;
-        }
-
-        if (wtPixels && bkPixels) {
-            lerpPixels(wtPixels, bkPixels, glyph.fWidth, glyph.fHeight, cgRowBytes,
-                       ~lumBits);
-        }
-    } else {    // isA8 or isBW
-        cgPixels = fWhiteScreen.getCG(*this, glyph, true, cgGlyph, &cgRowBytes);
-        if (isA8) {
-            gammaTable = getGammaTable(lumBits);
-        }
-    }
-#else
-    CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, fgColorIsWhite, cgGlyph,
-                                            &cgRowBytes);
-#endif
+    // FIXME: lcd smoothed un-hinted rasterization unsupported.
+    bool generateA8FromLCD = fRec.getHinting() != SkPaint::kNo_Hinting;
 
     // Draw the glyph
-    if (cgPixels != NULL) {
+    size_t cgRowBytes;
+    CGRGBPixel* cgPixels = fOffscreen.getCG(*this, glyph, cgGlyph, &cgRowBytes, generateA8FromLCD);
+    if (cgPixels == NULL) {
+        return;
+    }
 
-#ifdef SK_USE_COLOR_LUMINANCE
-#else
-        if (invertGamma) {
-            invertGammaMask(isWhite, (uint32_t*)cgPixels,
-                            glyph.fWidth, glyph.fHeight, cgRowBytes);
-        }
-#endif
+    //TODO: see if drawing black on white and inverting is faster (at least in
+    //lcd case) as core graphics appears to have special case code for drawing
+    //black text.
 
-        int width = glyph.fWidth;
-        switch (glyph.fMaskFormat) {
-            case SkMask::kLCD32_Format: {
-                uint32_t* dst = (uint32_t*)glyph.fImage;
-                size_t dstRB = glyph.rowBytes();
-                for (int y = 0; y < glyph.fHeight; y++) {
-                    for (int i = 0; i < width; i++) {
-                        dst[i] = rgb_to_lcd32(cgPixels[i] ^ xorMask);
-                    }
-                    cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
-                    dst = (uint32_t*)((char*)dst + dstRB);
-                }
-            } break;
-            case SkMask::kLCD16_Format: {
-                // downsample from rgba to rgb565
-                uint16_t* dst = (uint16_t*)glyph.fImage;
-                size_t dstRB = glyph.rowBytes();
-                for (int y = 0; y < glyph.fHeight; y++) {
-                    for (int i = 0; i < width; i++) {
-                        dst[i] = rgb_to_lcd16(cgPixels[i] ^ xorMask);
-                    }
-                    cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
-                    dst = (uint16_t*)((char*)dst + dstRB);
-                }
-            } break;
-            case SkMask::kA8_Format: {
-                uint8_t* dst = (uint8_t*)glyph.fImage;
-                size_t dstRB = glyph.rowBytes();
-                for (int y = 0; y < glyph.fHeight; y++) {
-                    for (int i = 0; i < width; ++i) {
-                        unsigned alpha8 = CGRGBPixel_getAlpha(cgPixels[i]);
-#ifdef SK_USE_COLOR_LUMINANCE
-                        alpha8 = gammaTable[alpha8];
-#endif
-                        dst[i] = alpha8;
-                    }
-                    cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
-                    dst += dstRB;
-                }
-            } break;
-            case SkMask::kBW_Format: {
-                uint8_t* dst = (uint8_t*)glyph.fImage;
-                size_t dstRB = glyph.rowBytes();
-                for (int y = 0; y < glyph.fHeight; y++) {
-                    cgpixels_to_bits(dst, cgPixels, width);
-                    cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
-                    dst += dstRB;
-                }
-            } break;
-            default:
-                SkDEBUGFAIL("unexpected mask format");
-                break;
+    // Fix the glyph
+    const bool isLCD = isLCDFormat(glyph.fMaskFormat);
+    if (isLCD || (glyph.fMaskFormat == SkMask::kA8_Format && supports_LCD() && generateA8FromLCD)) {
+        const uint8_t* table = getInverseGammaTableCoreGraphicSmoothing();
+
+        //Note that the following cannot really be integrated into the
+        //pre-blend, since we may not be applying the pre-blend; when we aren't
+        //applying the pre-blend it means that a filter wants linear anyway.
+        //Other code may also be applying the pre-blend, so we'd need another
+        //one with this and one without.
+        CGRGBPixel* addr = cgPixels;
+        for (int y = 0; y < glyph.fHeight; ++y) {
+            for (int x = 0; x < glyph.fWidth; ++x) {
+                int r = (addr[x] >> 16) & 0xFF;
+                int g = (addr[x] >>  8) & 0xFF;
+                int b = (addr[x] >>  0) & 0xFF;
+                addr[x] = (table[r] << 16) | (table[g] << 8) | table[b];
+            }
+            addr = SkTAddByteOffset(addr, cgRowBytes);
         }
     }
+
+    // Convert glyph to mask
+    switch (glyph.fMaskFormat) {
+        case SkMask::kLCD32_Format: {
+            if (fPreBlend.isApplicable()) {
+                rgb_to_lcd32<true>(cgPixels, cgRowBytes, glyph,
+                                   fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            } else {
+                rgb_to_lcd32<false>(cgPixels, cgRowBytes, glyph,
+                                    fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            }
+        } break;
+        case SkMask::kLCD16_Format: {
+            if (fPreBlend.isApplicable()) {
+                rgb_to_lcd16<true>(cgPixels, cgRowBytes, glyph,
+                                   fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            } else {
+                rgb_to_lcd16<false>(cgPixels, cgRowBytes, glyph,
+                                    fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+            }
+        } break;
+        case SkMask::kA8_Format: {
+            if (fPreBlend.isApplicable()) {
+                rgb_to_a8<true>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
+            } else {
+                rgb_to_a8<false>(cgPixels, cgRowBytes, glyph, fPreBlend.fG);
+            }
+        } break;
+        case SkMask::kBW_Format: {
+            const int width = glyph.fWidth;
+            size_t dstRB = glyph.rowBytes();
+            uint8_t* dst = (uint8_t*)glyph.fImage;
+            for (int y = 0; y < glyph.fHeight; y++) {
+                cgpixels_to_bits(dst, cgPixels, width);
+                cgPixels = (CGRGBPixel*)((char*)cgPixels + cgRowBytes);
+                dst += dstRB;
+            }
+        } break;
+        default:
+            SkDEBUGFAIL("unexpected mask format");
+            break;
+    }
 }
 
 /*
@@ -1365,12 +1224,12 @@
  *  seems sufficient, and possibly even correct, to allow the hinted outline
  *  to be subpixel positioned.
  */
-#define kScaleForSubPixelPositionHinting  4
+#define kScaleForSubPixelPositionHinting (4.0f)
 
 void SkScalerContext_Mac::generatePath(const SkGlyph& glyph, SkPath* path) {
     CTFontRef font = fCTFont;
-    float scaleX = 1;
-    float scaleY = 1;
+    SkScalar scaleX = SK_Scalar1;
+    SkScalar scaleY = SK_Scalar1;
 
     /*
      *  For subpixel positioning, we want to return an unhinted outline, so it
@@ -1386,14 +1245,14 @@
         fRec.getSingleMatrix(&m);
 
         // start out by assuming that we want no hining in X and Y
-        scaleX = scaleY = kScaleForSubPixelPositionHinting;
+        scaleX = scaleY = SkFloatToScalar(kScaleForSubPixelPositionHinting);
         // now see if we need to restore hinting for axis-aligned baselines
         switch (SkComputeAxisAlignmentForHText(m)) {
             case kX_SkAxisAlignment:
-                scaleY = 1; // want hinting in the Y direction
+                scaleY = SK_Scalar1; // want hinting in the Y direction
                 break;
             case kY_SkAxisAlignment:
-                scaleX = 1; // want hinting in the X direction
+                scaleX = SK_Scalar1; // want hinting in the X direction
                 break;
             default:
                 break;
@@ -1403,22 +1262,21 @@
         // need to release font when we're done
         font = CTFontCreateCopyWithAttributes(fCTFont, 1, &xform, NULL);
     }
-    
-    CGGlyph   cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
-    CGPathRef cgPath  = CTFontCreatePathForGlyph(font, cgGlyph, NULL);
+
+    CGGlyph cgGlyph = (CGGlyph)glyph.getGlyphID(fBaseGlyphCount);
+    AutoCFRelease<CGPathRef> cgPath(CTFontCreatePathForGlyph(font, cgGlyph, NULL));
 
     path->reset();
     if (cgPath != NULL) {
         CGPathApply(cgPath, path, SkScalerContext_Mac::CTPathElement);
-        CFRelease(cgPath);
     }
 
     if (fRec.fFlags & SkScalerContext::kSubpixelPositioning_Flag) {
         SkMatrix m;
-        m.setScale(SkFloatToScalar(1 / scaleX), SkFloatToScalar(1 / scaleY));
+        m.setScale(SkScalarInvert(scaleX), SkScalarInvert(scaleY));
         path->transform(m);
         // balance the call to CTFontCreateCopyWithAttributes
-        CFRelease(font);
+        CFSafeRelease(font);
     }
     if (fRec.fFlags & SkScalerContext::kVertical_Flag) {
         SkIPoint offset;
@@ -1450,23 +1308,22 @@
     }
 }
 
-void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element)
-{   SkPath      *skPath = (SkPath *) info;
-
+void SkScalerContext_Mac::CTPathElement(void *info, const CGPathElement *element) {
+    SkPath* skPath = (SkPath*)info;
 
     // Process the path element
     switch (element->type) {
         case kCGPathElementMoveToPoint:
-            skPath->moveTo( element->points[0].x, -element->points[0].y);
+            skPath->moveTo(element->points[0].x, -element->points[0].y);
             break;
 
         case kCGPathElementAddLineToPoint:
-            skPath->lineTo( element->points[0].x, -element->points[0].y);
+            skPath->lineTo(element->points[0].x, -element->points[0].y);
             break;
 
         case kCGPathElementAddQuadCurveToPoint:
-            skPath->quadTo( element->points[0].x, -element->points[0].y,
-                            element->points[1].x, -element->points[1].y);
+            skPath->quadTo(element->points[0].x, -element->points[0].y,
+                           element->points[1].x, -element->points[1].y);
             break;
 
         case kCGPathElementAddCurveToPoint:
@@ -1491,47 +1348,35 @@
 // Returns NULL on failure
 // Call must still manage its ownership of provider
 static SkTypeface* create_from_dataProvider(CGDataProviderRef provider) {
-    CGFontRef cg = CGFontCreateWithDataProvider(provider);
+    AutoCFRelease<CGFontRef> cg(CGFontCreateWithDataProvider(provider));
     if (NULL == cg) {
         return NULL;
     }
     CTFontRef ct = CTFontCreateWithGraphicsFont(cg, 0, NULL, NULL);
-    CGFontRelease(cg);
     return cg ? SkCreateTypefaceFromCTFont(ct) : NULL;
 }
 
-class AutoCGDataProviderRelease : SkNoncopyable {
-public:
-    AutoCGDataProviderRelease(CGDataProviderRef provider) : fProvider(provider) {}
-    ~AutoCGDataProviderRelease() { CGDataProviderRelease(fProvider); }
-    
-private:
-    CGDataProviderRef fProvider;
-};
-
 SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
-    CGDataProviderRef provider = SkCreateDataProviderFromStream(stream);
+    AutoCFRelease<CGDataProviderRef> provider(SkCreateDataProviderFromStream(stream));
     if (NULL == provider) {
         return NULL;
     }
-    AutoCGDataProviderRelease ar(provider);
     return create_from_dataProvider(provider);
 }
 
 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
-    CGDataProviderRef provider = CGDataProviderCreateWithFilename(path);
+    AutoCFRelease<CGDataProviderRef> provider(CGDataProviderCreateWithFilename(path));
     if (NULL == provider) {
         return NULL;
     }
-    AutoCGDataProviderRelease ar(provider);
     return create_from_dataProvider(provider);
 }
 
 // Web fonts added to the the CTFont registry do not return their character set.
 // Iterate through the font in this case. The existing caller caches the result,
 // so the performance impact isn't too bad.
-static void populate_glyph_to_unicode_slow(CTFontRef ctFont,
-        unsigned glyphCount, SkTDArray<SkUnichar>* glyphToUnicode) {
+static void populate_glyph_to_unicode_slow(CTFontRef ctFont, CFIndex glyphCount,
+                                           SkTDArray<SkUnichar>* glyphToUnicode) {
     glyphToUnicode->setCount(glyphCount);
     SkUnichar* out = glyphToUnicode->begin();
     sk_bzero(out, glyphCount * sizeof(SkUnichar));
@@ -1551,21 +1396,21 @@
 // Construct Glyph to Unicode table.
 // Unicode code points that require conjugate pairs in utf16 are not
 // supported.
-static void populate_glyph_to_unicode(CTFontRef ctFont,
-        const unsigned glyphCount, SkTDArray<SkUnichar>* glyphToUnicode) {
-    CFCharacterSetRef charSet = CTFontCopyCharacterSet(ctFont);
+static void populate_glyph_to_unicode(CTFontRef ctFont, CFIndex glyphCount,
+                                      SkTDArray<SkUnichar>* glyphToUnicode) {
+    AutoCFRelease<CFCharacterSetRef> charSet(CTFontCopyCharacterSet(ctFont));
     if (!charSet) {
         populate_glyph_to_unicode_slow(ctFont, glyphCount, glyphToUnicode);
         return;
     }
-    CFDataRef bitmap = CFCharacterSetCreateBitmapRepresentation(
-        kCFAllocatorDefault, charSet);
+
+    AutoCFRelease<CFDataRef> bitmap(CFCharacterSetCreateBitmapRepresentation(kCFAllocatorDefault,
+                                                                             charSet));
     if (!bitmap) {
         return;
     }
     CFIndex length = CFDataGetLength(bitmap);
     if (!length) {
-        CFSafeRelease(bitmap);
         return;
     }
     if (length > 8192) {
@@ -1587,45 +1432,50 @@
         for (int j = 0; j < 8; j++) {
             CGGlyph glyph;
             UniChar unichar = static_cast<UniChar>((i << 3) + j);
-            if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont,
-                    &unichar, &glyph, 1)) {
+            if (mask & (1 << j) && CTFontGetGlyphsForCharacters(ctFont, &unichar, &glyph, 1)) {
                 out[glyph] = unichar;
             }
         }
     }
-    CFSafeRelease(bitmap);
 }
 
 static bool getWidthAdvance(CTFontRef ctFont, int gId, int16_t* data) {
     CGSize advance;
     advance.width = 0;
     CGGlyph glyph = gId;
-    CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph,
-        &advance, 1);
+    CTFontGetAdvancesForGlyphs(ctFont, kCTFontHorizontalOrientation, &glyph, &advance, 1);
     *data = sk_float_round2int(advance.width);
     return true;
 }
 
+// we might move this into our CGUtils...
+static void CFStringToSkString(CFStringRef src, SkString* dst) {
+    // Reserve enough room for the worst-case string,
+    // plus 1 byte for the trailing null.
+    CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(src),
+                                                       kCFStringEncodingUTF8) + 1;
+    dst->resize(length);
+    CFStringGetCString(src, dst->writable_str(), length, kCFStringEncodingUTF8);
+    // Resize to the actual UTF-8 length used, stripping the null character.
+    dst->resize(strlen(dst->c_str()));
+}
+
 // static
 SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
         uint32_t fontID,
         SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
         const uint32_t* glyphIDs,
         uint32_t glyphIDsCount) {
-    CTFontRef ctFont = GetFontRefFromFontID(fontID);
-    ctFont = CTFontCreateCopyWithAttributes(ctFont, CTFontGetUnitsPerEm(ctFont),
-                                            NULL, NULL);
+    CTFontRef originalCTFont = GetFontRefFromFontID(fontID);
+    AutoCFRelease<CTFontRef> ctFont(CTFontCreateCopyWithAttributes(
+            originalCTFont, CTFontGetUnitsPerEm(originalCTFont), NULL, NULL));
     SkAdvancedTypefaceMetrics* info = new SkAdvancedTypefaceMetrics;
-    CFStringRef fontName = CTFontCopyPostScriptName(ctFont);
-    // Reserve enough room for the worst-case string,
-    // plus 1 byte for the trailing null.
-    int length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(
-        fontName), kCFStringEncodingUTF8) + 1;
-    info->fFontName.resize(length);
-    CFStringGetCString(fontName, info->fFontName.writable_str(), length,
-        kCFStringEncodingUTF8);
-    // Resize to the actual UTF-8 length used, stripping the null character.
-    info->fFontName.resize(strlen(info->fFontName.c_str()));
+
+    {
+        AutoCFRelease<CFStringRef> fontName(CTFontCopyPostScriptName(ctFont));
+        CFStringToSkString(fontName, &info->fFontName);
+    }
+
     info->fMultiMaster = false;
     CFIndex glyphCount = CTFontGetGlyphCount(ctFont);
     info->fLastGlyphID = SkToU16(glyphCount - 1);
@@ -1640,7 +1490,7 @@
     // If it's not a truetype font, mark it as 'other'. Assume that TrueType
     // fonts always have both glyf and loca tables. At the least, this is what
     // sfntly needs to subset the font. CTFontCopyAttribute() does not always
-    // succeed in determining this directly. 
+    // succeed in determining this directly.
     if (!GetTableSize(fontID, 'glyf') || !GetTableSize(fontID, 'loca')) {
         info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
         info->fItalicAngle = 0;
@@ -1649,7 +1499,6 @@
         info->fStemV = 0;
         info->fCapHeight = 0;
         info->fBBox = SkIRect::MakeEmpty();
-        CFSafeRelease(ctFont);
         return info;
     }
 
@@ -1661,24 +1510,28 @@
     if (symbolicTraits & kCTFontItalicTrait) {
         info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
     }
-    CTFontStylisticClass stylisticClass = symbolicTraits &
-            kCTFontClassMaskTrait;
+    CTFontStylisticClass stylisticClass = symbolicTraits & kCTFontClassMaskTrait;
     if (stylisticClass & kCTFontSymbolicClass) {
         info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
     }
-    if (stylisticClass >= kCTFontOldStyleSerifsClass
-            && stylisticClass <= kCTFontSlabSerifsClass) {
+    if (stylisticClass >= kCTFontOldStyleSerifsClass && stylisticClass <= kCTFontSlabSerifsClass) {
         info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
     } else if (stylisticClass & kCTFontScriptsClass) {
         info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
     }
-    info->fItalicAngle = CTFontGetSlantAngle(ctFont);
-    info->fAscent = CTFontGetAscent(ctFont);
-    info->fDescent = CTFontGetDescent(ctFont);
-    info->fCapHeight = CTFontGetCapHeight(ctFont);
+    info->fItalicAngle = (int16_t) CTFontGetSlantAngle(ctFont);
+    info->fAscent = (int16_t) CTFontGetAscent(ctFont);
+    info->fDescent = (int16_t) CTFontGetDescent(ctFont);
+    info->fCapHeight = (int16_t) CTFontGetCapHeight(ctFont);
     CGRect bbox = CTFontGetBoundingBox(ctFont);
-    info->fBBox = SkIRect::MakeXYWH(bbox.origin.x, bbox.origin.y,
-        bbox.size.width, bbox.size.height);
+
+    SkRect r;
+    r.set( CGToScalar(CGRectGetMinX_inline(bbox)),   // Left
+           CGToScalar(CGRectGetMaxY_inline(bbox)),   // Top
+           CGToScalar(CGRectGetMaxX_inline(bbox)),   // Right
+           CGToScalar(CGRectGetMinY_inline(bbox)));  // Bottom
+
+    r.roundOut(&(info->fBBox));
 
     // Figure out a good guess for StemV - Min width of i, I, !, 1.
     // This probably isn't very good with an italic font.
@@ -1690,9 +1543,9 @@
     CGRect boundingRects[count];
     if (CTFontGetGlyphsForCharacters(ctFont, stem_chars, glyphs, count)) {
         CTFontGetBoundingRectsForGlyphs(ctFont, kCTFontHorizontalOrientation,
-            glyphs, boundingRects, count);
+                                        glyphs, boundingRects, count);
         for (size_t i = 0; i < count; i++) {
-            int16_t width = boundingRects[i].size.width;
+            int16_t width = (int16_t) boundingRects[i].size.width;
             if (width > 0 && width < min_width) {
                 min_width = width;
                 info->fStemV = min_width;
@@ -1703,8 +1556,7 @@
     if (false) { // TODO: haven't figured out how to know if font is embeddable
         // (information is in the OS/2 table)
         info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
-    } else if (perGlyphInfo &
-               SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+    } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
         if (info->fStyle & SkAdvancedTypefaceMetrics::kFixedPitch_Style) {
             skia_advanced_typeface_metrics_utils::appendRange(&info->fGlyphWidths, 0);
             info->fGlyphWidths->fAdvance.append(1, &min_width);
@@ -1712,57 +1564,68 @@
                         SkAdvancedTypefaceMetrics::WidthRange::kDefault);
         } else {
             info->fGlyphWidths.reset(
-                skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont,
+                skia_advanced_typeface_metrics_utils::getAdvanceData(ctFont.get(),
                                glyphCount,
                                glyphIDs,
                                glyphIDsCount,
                                &getWidthAdvance));
         }
     }
-
-    CFSafeRelease(ctFont);
     return info;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-struct FontHeader {
-    SkFixed fVersion;
-    uint16_t fNumTables;
-    uint16_t fSearchRange;
-    uint16_t fEntrySelector;
-    uint16_t fRangeShift;
-};
-
-struct TableEntry {
-    uint32_t fTag;
-    uint32_t fCheckSum;
-    uint32_t fOffset;
-    uint32_t fLength;
-};
-
-static uint32_t CalcTableCheckSum(uint32_t *table, uint32_t numberOfBytesInTable) {
-    uint32_t sum = 0;
-    uint32_t nLongs = (numberOfBytesInTable + 3) / 4;
-
-    while (nLongs-- > 0) {
-        sum += SkEndian_SwapBE32(*table++);
+static SK_SFNT_ULONG get_font_type_tag(SkFontID uniqueID) {
+    CTFontRef ctFont = GetFontRefFromFontID(uniqueID);
+    AutoCFRelease<CFNumberRef> fontFormatRef(
+            static_cast<CFNumberRef>(CTFontCopyAttribute(ctFont, kCTFontFormatAttribute)));
+    if (!fontFormatRef) {
+        return 0;
     }
-    return sum;
+
+    SInt32 fontFormatValue;
+    if (!CFNumberGetValue(fontFormatRef, kCFNumberSInt32Type, &fontFormatValue)) {
+        return 0;
+    }
+
+    switch (fontFormatValue) {
+        case kCTFontFormatOpenTypePostScript:
+            return SkSFNTHeader::fontType_OpenTypeCFF::TAG;
+        case kCTFontFormatOpenTypeTrueType:
+            return SkSFNTHeader::fontType_WindowsTrueType::TAG;
+        case kCTFontFormatTrueType:
+            return SkSFNTHeader::fontType_MacTrueType::TAG;
+        case kCTFontFormatPostScript:
+            return SkSFNTHeader::fontType_PostScript::TAG;
+        case kCTFontFormatBitmap:
+            return SkSFNTHeader::fontType_MacTrueType::TAG;
+        case kCTFontFormatUnrecognized:
+        default:
+            //CT seems to be unreliable in being able to obtain the type,
+            //even if all we want is the first four bytes of the font resource.
+            //Just the presence of the FontForge 'FFTM' table seems to throw it off.
+            return SkSFNTHeader::fontType_WindowsTrueType::TAG;
+    }
 }
 
 SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+    SK_SFNT_ULONG fontType = get_font_type_tag(uniqueID);
+    if (0 == fontType) {
+        return NULL;
+    }
+
     // get table tags
-    int tableCount = CountTables(uniqueID);
+    int numTables = CountTables(uniqueID);
     SkTDArray<SkFontTableTag> tableTags;
-    tableTags.setCount(tableCount);
+    tableTags.setCount(numTables);
     GetTableTags(uniqueID, tableTags.begin());
 
     // calc total size for font, save sizes
     SkTDArray<size_t> tableSizes;
-    size_t totalSize = sizeof(FontHeader) + sizeof(TableEntry) * tableCount;
-    for (int index = 0; index < tableCount; ++index) {
-        size_t tableSize = GetTableSize(uniqueID, tableTags[index]);
+    size_t totalSize = sizeof(SkSFNTHeader) + sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+    for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+        size_t tableSize = GetTableSize(uniqueID, tableTags[tableIndex]);
         totalSize += (tableSize + 3) & ~3;
         *tableSizes.append() = tableSize;
     }
@@ -1776,33 +1639,34 @@
     // compute font header entries
     uint16_t entrySelector = 0;
     uint16_t searchRange = 1;
-    while (searchRange < tableCount >> 1) {
+    while (searchRange < numTables >> 1) {
         entrySelector++;
         searchRange <<= 1;
     }
     searchRange <<= 4;
-    uint16_t rangeShift = (tableCount << 4) - searchRange;
+    uint16_t rangeShift = (numTables << 4) - searchRange;
 
-    // write font header (also called sfnt header, offset subtable)
-    FontHeader* offsetTable = (FontHeader*)dataPtr;
-    offsetTable->fVersion = SkEndian_SwapBE32(SK_Fixed1);
-    offsetTable->fNumTables = SkEndian_SwapBE16(tableCount);
-    offsetTable->fSearchRange = SkEndian_SwapBE16(searchRange);
-    offsetTable->fEntrySelector = SkEndian_SwapBE16(entrySelector);
-    offsetTable->fRangeShift = SkEndian_SwapBE16(rangeShift);
-    dataPtr += sizeof(FontHeader);
+    // write font header
+    SkSFNTHeader* header = (SkSFNTHeader*)dataPtr;
+    header->fontType = fontType;
+    header->numTables = SkEndian_SwapBE16(numTables);
+    header->searchRange = SkEndian_SwapBE16(searchRange);
+    header->entrySelector = SkEndian_SwapBE16(entrySelector);
+    header->rangeShift = SkEndian_SwapBE16(rangeShift);
+    dataPtr += sizeof(SkSFNTHeader);
 
     // write tables
-    TableEntry* entry = (TableEntry*)dataPtr;
-    dataPtr += sizeof(TableEntry) * tableCount;
-    for (int index = 0; index < tableCount; ++index) {
-        size_t tableSize = tableSizes[index];
-        GetTableData(uniqueID, tableTags[index], 0, tableSize, dataPtr);
-        entry->fTag = SkEndian_SwapBE32(tableTags[index]);
-        entry->fCheckSum = SkEndian_SwapBE32(CalcTableCheckSum(
-            (uint32_t*)dataPtr, tableSize));
-        entry->fOffset = SkEndian_SwapBE32(dataPtr - dataStart);
-        entry->fLength = SkEndian_SwapBE32(tableSize);
+    SkSFNTHeader::TableDirectoryEntry* entry = (SkSFNTHeader::TableDirectoryEntry*)dataPtr;
+    dataPtr += sizeof(SkSFNTHeader::TableDirectoryEntry) * numTables;
+    for (int tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+        size_t tableSize = tableSizes[tableIndex];
+        GetTableData(uniqueID, tableTags[tableIndex], 0, tableSize, dataPtr);
+        entry->tag = SkEndian_SwapBE32(tableTags[tableIndex]);
+        entry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum((SK_OT_ULONG*)dataPtr,
+                                                                         tableSize));
+        entry->offset = SkEndian_SwapBE32(dataPtr - dataStart);
+        entry->logicalLength = SkEndian_SwapBE32(tableSize);
+
         dataPtr += (tableSize + 3) & ~3;
         ++entry;
     }
@@ -1810,28 +1674,47 @@
     return stream;
 }
 
-size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
-                               int32_t* index) {
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length, int32_t* index) {
     SkDEBUGFAIL("SkFontHost::GetFileName unimplemented");
-    return(0);
+    return 0;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "SkStream.h"
 
+// we take ownership of the ref
+static const char* get_str(CFStringRef ref, SkString* str) {
+    CFStringToSkString(ref, str);
+    CFSafeRelease(ref);
+    return str->c_str();
+}
+
 void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
-    // hack: need a real name or something from CG
-    uint32_t fontID = face->uniqueID();
-    stream->write(&fontID, 4);
+    CTFontRef ctFont = typeface_to_fontref(face);
+    SkFontDescriptor desc(face->style());
+    SkString tmpStr;
+
+    desc.setFamilyName(get_str(CTFontCopyFamilyName(ctFont), &tmpStr));
+    desc.setFullName(get_str(CTFontCopyFullName(ctFont), &tmpStr));
+    desc.setPostscriptName(get_str(CTFontCopyPostScriptName(ctFont), &tmpStr));
+
+    desc.serialize(stream);
+
+    // by convention, we also write out the actual sfnt data, preceeded by
+    // a packed-length. For now we skip that, so we just write the zero.
+    stream->writePackedUInt(0);
 }
 
 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
-    // hack: need a real name or something from CG
-    SkFontID fontID = stream->readU32();
-    SkTypeface* face = SkTypefaceCache::FindByID(fontID);
-    SkSafeRef(face);
-    return face;
+    SkFontDescriptor desc(stream);
+
+    // by convention, Serialize will have also written the actual sfnt data.
+    // for now, we just want to skip it.
+    size_t size = stream->readPackedUInt();
+    stream->skip(size);
+
+    return SkFontHost::CreateTypeface(NULL, desc.getFamilyName(), desc.getStyle());
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -1849,106 +1732,92 @@
     return nextFontID;
 }
 
-static bool supports_LCD() {
-    static int gSupportsLCD = -1;
-    if (gSupportsLCD >= 0) {
-        return (bool) gSupportsLCD;
-    }
-    int rgb = 0;
-    CGColorSpaceRef colorspace = CGColorSpaceCreateDeviceRGB();
-    CGContextRef cgContext = CGBitmapContextCreate(&rgb, 1, 1, 8, 4, colorspace,
-                                                   BITMAP_INFO_RGB);
-    CGContextSelectFont(cgContext, "Helvetica", 16, kCGEncodingMacRoman);
-    CGContextSetShouldSmoothFonts(cgContext, true);
-    CGContextSetShouldAntialias(cgContext, true);
-    CGContextSetTextDrawingMode(cgContext, kCGTextFill);
-    CGContextSetGrayFillColor(  cgContext, 1, 1.0);
-    CGContextShowTextAtPoint(cgContext, -1, 0, "|", 1);
-    CFSafeRelease(colorspace);
-    CFSafeRelease(cgContext);
-    int r = (rgb >> 16) & 0xFF;
-    int g = (rgb >>  8) & 0xFF;
-    int b = (rgb >>  0) & 0xFF;
-    gSupportsLCD = r != g || r != b;
-    return (bool) gSupportsLCD;
-}
-
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
     unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
                                   SkScalerContext::kAutohinting_Flag;
 
     rec->fFlags &= ~flagsWeDontSupport;
 
-    // we only support 2 levels of hinting
-    SkPaint::Hinting h = rec->getHinting();
-    if (SkPaint::kSlight_Hinting == h) {
-        h = SkPaint::kNo_Hinting;
-    } else if (SkPaint::kFull_Hinting == h) {
-        h = SkPaint::kNormal_Hinting;
-    }
-    rec->setHinting(h);
+    bool lcdSupport = supports_LCD();
 
-#ifdef SK_USE_COLOR_LUMINANCE
+    // Only two levels of hinting are supported.
+    // kNo_Hinting means avoid CoreGraphics outline dilation.
+    // kNormal_Hinting means CoreGraphics outline dilation is allowed.
+    // If there is no lcd support, hinting (dilation) cannot be supported.
+    SkPaint::Hinting hinting = rec->getHinting();
+    if (SkPaint::kSlight_Hinting == hinting || !lcdSupport) {
+        hinting = SkPaint::kNo_Hinting;
+    } else if (SkPaint::kFull_Hinting == hinting) {
+        hinting = SkPaint::kNormal_Hinting;
+    }
+    rec->setHinting(hinting);
+
+    // FIXME: lcd smoothed un-hinted rasterization unsupported.
+    // Tracked by http://code.google.com/p/skia/issues/detail?id=915 .
+    // There is no current means to honor a request for unhinted lcd,
+    // so arbitrarilly ignore the hinting request and honor lcd.
+
+    // Hinting and smoothing should be orthogonal, but currently they are not.
+    // CoreGraphics has no API to influence hinting. However, its lcd smoothed
+    // output is drawn from auto-dilated outlines (the amount of which is
+    // determined by AppleFontSmoothing). Its regular anti-aliased output is
+    // drawn from un-dilated outlines.
+
+    // The behavior of Skia is as follows:
+    // [AA][no-hint]: generate AA using CoreGraphic's AA output.
+    // [AA][yes-hint]: use CoreGraphic's LCD output and reduce it to a single
+    // channel. This matches [LCD][yes-hint] in weight.
+    // [LCD][no-hint]: curently unable to honor, and must pick which to respect.
+    // Currenly side with LCD, effectively ignoring the hinting setting.
+    // [LCD][yes-hint]: generate LCD using CoreGraphic's LCD output.
+
     if (isLCDFormat(rec->fMaskFormat)) {
-        SkColor c = rec->getLuminanceColor();
-        // apply our chosen scaling between Black and White cg output
-        int r = SkColorGetR(c)*2/3;
-        int g = SkColorGetG(c)*2/3;
-        int b = SkColorGetB(c)*2/3;
-        rec->setLuminanceColor(SkColorSetRGB(r, g, b));
-    }
-#else
-    {
-        unsigned lum = rec->getLuminanceByte();
-        if (lum <= BLACK_LUMINANCE_LIMIT) {
-            lum = 0;
-        } else if (lum >= WHITE_LUMINANCE_LIMIT) {
-            lum = SkScalerContext::kLuminance_Max;
-        } else {
-            lum = SkScalerContext::kLuminance_Max >> 1;
-        }
-        rec->setLuminanceBits(lum);
-    }
-#endif
-
-    if (SkMask::kLCD16_Format == rec->fMaskFormat
-            || SkMask::kLCD32_Format == rec->fMaskFormat) {
-        if (supports_LCD()) {
-            rec->fMaskFormat = SkMask::kLCD32_Format;
+        if (lcdSupport) {
+            //CoreGraphics creates 555 masks for smoothed text anyway.
+            rec->fMaskFormat = SkMask::kLCD16_Format;
+            rec->setHinting(SkPaint::kNormal_Hinting);
         } else {
             rec->fMaskFormat = SkMask::kA8_Format;
         }
     }
+
+    // Unhinted A8 masks (those not derived from LCD masks) must respect SK_GAMMA_APPLY_TO_A8.
+    // All other masks can use regular gamma.
+    if (SkMask::kA8_Format == rec->fMaskFormat && SkPaint::kNo_Hinting == hinting) {
+#ifndef SK_GAMMA_APPLY_TO_A8
+        rec->ignorePreBlend();
+#endif
+    } else {
+        //CoreGraphics dialates smoothed text as needed.
+        rec->setContrast(0);
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////
 
 int SkFontHost::CountTables(SkFontID fontID) {
     CTFontRef ctFont = GetFontRefFromFontID(fontID);
-    CFArrayRef cfArray = CTFontCopyAvailableTables(ctFont,
-                                                   kCTFontTableOptionNoOptions);
+    AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(ctFont,
+                                                                kCTFontTableOptionNoOptions));
     if (NULL == cfArray) {
         return 0;
     }
-    
-    AutoCFRelease ar(cfArray);
     return CFArrayGetCount(cfArray);
 }
 
 int SkFontHost::GetTableTags(SkFontID fontID, SkFontTableTag tags[]) {
     CTFontRef ctFont = GetFontRefFromFontID(fontID);
-    CFArrayRef cfArray = CTFontCopyAvailableTables(ctFont,
-                                                   kCTFontTableOptionNoOptions);
+    AutoCFRelease<CFArrayRef> cfArray(CTFontCopyAvailableTables(ctFont,
+                                                                kCTFontTableOptionNoOptions));
     if (NULL == cfArray) {
         return 0;
     }
 
-    AutoCFRelease ar(cfArray);
-
     int count = CFArrayGetCount(cfArray);
     if (tags) {
         for (int i = 0; i < count; ++i) {
-            tags[i] = (SkFontTableTag)CFArrayGetValueAtIndex(cfArray, i);
+            uintptr_t fontTag = reinterpret_cast<uintptr_t>(CFArrayGetValueAtIndex(cfArray, i));
+            tags[i] = static_cast<SkFontTableTag>(fontTag);
         }
     }
     return count;
@@ -1958,37 +1827,31 @@
 // the CGFont data may work. While the CGFont may always provide the
 // right result, leave the CTFont code path to minimize disruption.
 static CFDataRef copyTableFromFont(CTFontRef ctFont, SkFontTableTag tag) {
-    CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag,
-                                     kCTFontTableOptionNoOptions);
+    CFDataRef data = CTFontCopyTable(ctFont, (CTFontTableTag) tag, kCTFontTableOptionNoOptions);
     if (NULL == data) {
-        CGFontRef cgFont = CTFontCopyGraphicsFont(ctFont, NULL);
+        AutoCFRelease<CGFontRef> cgFont(CTFontCopyGraphicsFont(ctFont, NULL));
         data = CGFontCopyTableForTag(cgFont, tag);
-        CGFontRelease(cgFont);
     }
     return data;
 }
 
 size_t SkFontHost::GetTableSize(SkFontID fontID, SkFontTableTag tag) {
     CTFontRef ctFont = GetFontRefFromFontID(fontID);
-    CFDataRef srcData = copyTableFromFont(ctFont, tag);
+    AutoCFRelease<CFDataRef> srcData(copyTableFromFont(ctFont, tag));
     if (NULL == srcData) {
         return 0;
     }
-
-    AutoCFRelease ar(srcData);
     return CFDataGetLength(srcData);
 }
 
 size_t SkFontHost::GetTableData(SkFontID fontID, SkFontTableTag tag,
                                 size_t offset, size_t length, void* dst) {
     CTFontRef ctFont = GetFontRefFromFontID(fontID);
-    CFDataRef srcData = copyTableFromFont(ctFont, tag);
+    AutoCFRelease<CFDataRef> srcData(copyTableFromFont(ctFont, tag));
     if (NULL == srcData) {
         return 0;
     }
 
-    AutoCFRelease ar(srcData);
-
     size_t srcSize = CFDataGetLength(srcData);
     if (offset >= srcSize) {
         return 0;
diff --git a/src/ports/SkFontHost_none.cpp b/src/ports/SkFontHost_none.cpp
index e79926d..6475c09 100644
--- a/src/ports/SkFontHost_none.cpp
+++ b/src/ports/SkFontHost_none.cpp
@@ -8,10 +8,10 @@
 
 
 #include "SkFontHost.h"
+#include "SkScalerContext.h"
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                      const char famillyName[],
-                                     const void* data, size_t bytelength,
                                      SkTypeface::Style style) {
     SkDEBUGFAIL("SkFontHost::FindTypeface unimplemented");
     return NULL;
@@ -30,12 +30,14 @@
 // static
 SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
         uint32_t fontID,
-        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
     SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
     return NULL;
 }
 
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -72,5 +74,3 @@
 SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
     return 0;
 }
-
-
diff --git a/src/ports/SkFontHost_simple.cpp b/src/ports/SkFontHost_simple.cpp
index eef4dd7..101d576 100644
--- a/src/ports/SkFontHost_simple.cpp
+++ b/src/ports/SkFontHost_simple.cpp
@@ -519,7 +519,7 @@
                 for (int j = i; j >= 0; --j) {
                     if (rec[j].fNames != NULL) {
                         return SkFontHost::CreateTypeface(NULL,
-                                    rec[j].fNames[0], NULL, 0, (SkTypeface::Style)style);
+                                    rec[j].fNames[0], (SkTypeface::Style)style);
                     }
                 }
             }
@@ -532,7 +532,6 @@
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style) {
     load_system_fonts();
 
@@ -575,7 +574,9 @@
 #if 0
 SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
         uint32_t fontID,
-        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
     SkDEBUGFAIL("SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
     return NULL;
 }
@@ -641,4 +642,3 @@
     stream->unref();
     return face;
 }
-
diff --git a/src/ports/SkFontHost_tables.cpp b/src/ports/SkFontHost_tables.cpp
index 9878119..38d2e60 100644
--- a/src/ports/SkFontHost_tables.cpp
+++ b/src/ports/SkFontHost_tables.cpp
@@ -203,4 +203,3 @@
     }
     return 0;
 }
-
diff --git a/src/ports/SkFontHost_win.cpp b/src/ports/SkFontHost_win.cpp
index ec7e1b0..4928971 100755
--- a/src/ports/SkFontHost_win.cpp
+++ b/src/ports/SkFontHost_win.cpp
@@ -6,23 +6,87 @@
  * found in the LICENSE file.
  */
 
-
-#include "SkColorFilter.h"
-#include "SkString.h"
-#include "SkEndian.h"
-#include "SkFontHost.h"
-#include "SkDescriptor.h"
 #include "SkAdvancedTypefaceMetrics.h"
+#include "SkBase64.h"
+#include "SkColorPriv.h"
+#include "SkData.h"
+#include "SkDescriptor.h"
+#include "SkFontDescriptor.h"
+#include "SkFontHost.h"
+#include "SkGlyph.h"
+#include "SkMaskGamma.h"
+#include "SkOTUtils.h"
+#include "SkPath.h"
 #include "SkStream.h"
+#include "SkString.h"
 #include "SkThread.h"
 #include "SkTypeface_win.h"
 #include "SkTypefaceCache.h"
 #include "SkUtils.h"
 
-#ifdef WIN32
-#include "windows.h"
-#include "tchar.h"
-#include "usp10.h"
+#include "SkTypes.h"
+#include <tchar.h>
+#include <usp10.h>
+#include <objbase.h>
+
+static bool compute_bounds_outset(const LOGFONT& lf, SkIRect* outset) {
+
+    static const struct {
+        const char* fUCName;    // UTF8 encoded, ascii is upper-case
+        SkIRect     fOutset;    // these are deltas for the glyph's bounds
+    } gData[] = {
+        // http://code.google.com/p/chromium/issues/detail?id=130842
+        { "DOTUM", { 0, 0, 0, 1 } },
+        { "DOTUMCHE", { 0, 0, 0, 1 } },
+        { "\xEB\x8F\x8B\xEC\x9B\x80", { 0, 0, 0, 1 } },
+        { "\xEB\x8F\x8B\xEC\x9B\x80\xEC\xB2\xB4", { 0, 0, 0, 1 } },
+        { "MS UI GOTHIC", { 1, 0, 0, 0 } },
+    };
+
+    /**
+     *  We convert the target name into upper-case (for ascii chars) UTF8.
+     *  Our database is already stored in this fashion, and it allows us to
+     *  search it with straight memcmp, since everyone is in this canonical
+     *  form.
+     */
+
+    // temp storage is max # TCHARs * max expantion for UTF8 + null
+    char name[kMaxBytesInUTF8Sequence * LF_FACESIZE + 1];
+    int index = 0;
+    for (int i = 0; i < LF_FACESIZE; ++i) {
+        uint16_t c = lf.lfFaceName[i];
+        if (c >= 'a' && c <= 'z') {
+            c = c - 'a' + 'A';
+        }
+        size_t n = SkUTF16_ToUTF8(&c, 1, &name[index]);
+        index += n;
+        if (0 == c) {
+            break;
+        }
+    }
+
+    for (size_t j = 0; j < SK_ARRAY_COUNT(gData); ++j) {
+        if (!strcmp(gData[j].fUCName, name)) {
+            *outset = gData[j].fOutset;
+            return true;
+        }
+    }
+    return false;
+}
+
+// outset isn't really a rect, but 4 (non-negative) values to outset the
+// glyph's metrics by. For "normal" fonts, all these values should be 0.
+static void apply_outset(SkGlyph* glyph, const SkIRect& outset) {
+    SkASSERT(outset.fLeft >= 0);
+    SkASSERT(outset.fTop >= 0);
+    SkASSERT(outset.fRight >= 0);
+    SkASSERT(outset.fBottom >= 0);
+
+    glyph->fLeft -= outset.fLeft;
+    glyph->fTop -= outset.fTop;
+    glyph->fWidth += outset.fLeft + outset.fRight;
+    glyph->fHeight += outset.fTop + outset.fBottom;
+}
 
 // always packed xxRRGGBB
 typedef uint32_t SkGdiRGB;
@@ -109,12 +173,15 @@
 static inline FIXED SkFixedToFIXED(SkFixed x) {
     return *(FIXED*)(&x);
 }
+static inline SkFixed SkFIXEDToFixed(FIXED x) {
+    return *(SkFixed*)(&x);
+}
 
 static inline FIXED SkScalarToFIXED(SkScalar x) {
     return SkFixedToFIXED(SkScalarToFixed(x));
 }
 
-static unsigned calculateGlyphCount(HDC hdc) {
+static unsigned calculateOutlineGlyphCount(HDC hdc) {
     // The 'maxp' table stores the number of glyphs at offset 4, in 2 bytes.
     const DWORD maxpTag =
         SkEndian_SwapBE32(SkSetFourByteTag('m', 'a', 'x', 'p'));
@@ -143,10 +210,39 @@
 
 class LogFontTypeface : public SkTypeface {
 public:
-    LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf) :
-      SkTypeface(style, fontID, false), fLogFont(lf) {}
+    LogFontTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, bool serializeAsStream = false) :
+        SkTypeface(style, fontID, false), fLogFont(lf), fSerializeAsStream(serializeAsStream) {
+
+        // If the font has cubic outlines, it will not be rendered with ClearType.
+        HFONT font = CreateFontIndirect(&lf);
+
+        HDC deviceContext = ::CreateCompatibleDC(NULL);
+        HFONT savefont = (HFONT)SelectObject(deviceContext, font);
+
+        TEXTMETRIC textMetric;
+        if (0 == GetTextMetrics(deviceContext, &textMetric)) {
+            SkFontHost::EnsureTypefaceAccessible(*this);
+            if (0 == GetTextMetrics(deviceContext, &textMetric)) {
+                textMetric.tmPitchAndFamily = TMPF_TRUETYPE;
+            }
+        }
+        if (deviceContext) {
+            ::SelectObject(deviceContext, savefont);
+            ::DeleteDC(deviceContext);
+        }
+        if (font) {
+            ::DeleteObject(font);
+        }
+
+        // Used a logfont on a memory context, should never get a device font.
+        // Therefore all TMPF_DEVICE will be PostScript (cubic) fonts.
+        fCanBeLCD = !((textMetric.tmPitchAndFamily & TMPF_VECTOR) &&
+                      (textMetric.tmPitchAndFamily & TMPF_DEVICE));
+    }
 
     LOGFONT fLogFont;
+    bool fSerializeAsStream;
+    bool fCanBeLCD;
 
     static LogFontTypeface* Create(const LOGFONT& lf) {
         SkTypeface::Style style = get_style(lf);
@@ -155,16 +251,48 @@
     }
 };
 
+class FontMemResourceTypeface : public LogFontTypeface {
+public:
+    /**
+     *  Takes ownership of fontMemResource.
+     */
+    FontMemResourceTypeface(SkTypeface::Style style, SkFontID fontID, const LOGFONT& lf, HANDLE fontMemResource) :
+        LogFontTypeface(style, fontID, lf, true), fFontMemResource(fontMemResource) {
+    }
+
+    HANDLE fFontMemResource;
+
+    /**
+     *  The created FontMemResourceTypeface takes ownership of fontMemResource.
+     */
+    static FontMemResourceTypeface* Create(const LOGFONT& lf, HANDLE fontMemResource) {
+        SkTypeface::Style style = get_style(lf);
+        SkFontID fontID = SkTypefaceCache::NewFontID();
+        return new FontMemResourceTypeface(style, fontID, lf, fontMemResource);
+    }
+
+protected:
+    virtual void weak_dispose() const SK_OVERRIDE {
+        RemoveFontMemResourceEx(fFontMemResource);
+        //SkTypefaceCache::Remove(this);
+        INHERITED::weak_dispose();
+    }
+
+private:
+    typedef LogFontTypeface INHERITED;
+};
+
 static const LOGFONT& get_default_font() {
     static LOGFONT gDefaultFont;
     return gDefaultFont;
 }
 
 static bool FindByLogFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
-    LogFontTypeface* lface = reinterpret_cast<LogFontTypeface*>(face);
+    LogFontTypeface* lface = static_cast<LogFontTypeface*>(face);
     const LOGFONT* lf = reinterpret_cast<const LOGFONT*>(ctx);
 
-    return get_style(lface->fLogFont) == requestedStyle &&
+    return lface &&
+           get_style(lface->fLogFont) == requestedStyle &&
            !memcmp(&lface->fLogFont, lf, sizeof(LOGFONT));
 }
 
@@ -184,13 +312,24 @@
 }
 
 /**
+ *  The created SkTypeface takes ownership of fontMemResource.
+ */
+SkTypeface* SkCreateFontMemResourceTypefaceFromLOGFONT(const LOGFONT& origLF, HANDLE fontMemResource) {
+    LOGFONT lf = origLF;
+    make_canonical(&lf);
+    FontMemResourceTypeface* face = FontMemResourceTypeface::Create(lf, fontMemResource);
+    SkTypefaceCache::Add(face, get_style(lf), false);
+    return face;
+}
+
+/**
  *  This guy is public
  */
 void SkLOGFONTFromTypeface(const SkTypeface* face, LOGFONT* lf) {
     if (NULL == face) {
         *lf = get_default_font();
     } else {
-        *lf = ((const LogFontTypeface*)face)->fLogFont;
+        *lf = static_cast<const LogFontTypeface*>(face)->fLogFont;
     }
 }
 
@@ -202,14 +341,14 @@
 }
 
 static void ensure_typeface_accessible(SkFontID fontID) {
-    LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
+    LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
     if (face) {
         SkFontHost::EnsureTypefaceAccessible(*face);
     }
 }
 
 static void GetLogFontByID(SkFontID fontID, LOGFONT* lf) {
-    LogFontTypeface* face = (LogFontTypeface*)SkTypefaceCache::FindByID(fontID);
+    LogFontTypeface* face = static_cast<LogFontTypeface*>(SkTypefaceCache::FindByID(fontID));
     if (face) {
         *lf = face->fLogFont;
     } else {
@@ -288,7 +427,6 @@
         fBits = NULL;
         fWidth = fHeight = 0;
         fIsBW = false;
-        fColor = kInvalid_Color;
     }
 
     ~HDCOffscreen() {
@@ -305,8 +443,7 @@
         fXform = xform;
     }
 
-    const void* draw(const SkGlyph&, bool isBW, SkGdiRGB fgColor,
-                     size_t* srcRBPtr);
+    const void* draw(const SkGlyph&, bool isBW, size_t* srcRBPtr);
 
 private:
     HDC     fDC;
@@ -314,7 +451,6 @@
     HFONT   fFont;
     XFORM   fXform;
     void*   fBits;  // points into fBM
-    COLORREF fColor;
     int     fWidth;
     int     fHeight;
     bool    fIsBW;
@@ -327,7 +463,7 @@
 };
 
 const void* HDCOffscreen::draw(const SkGlyph& glyph, bool isBW,
-                               SkGdiRGB fgColor, size_t* srcRBPtr) {
+                               size_t* srcRBPtr) {
     if (0 == fDC) {
         fDC = CreateCompatibleDC(0);
         if (0 == fDC) {
@@ -337,7 +473,10 @@
         SetBkMode(fDC, TRANSPARENT);
         SetTextAlign(fDC, TA_LEFT | TA_BASELINE);
         SelectObject(fDC, fFont);
-        fColor = kInvalid_Color;
+
+        COLORREF color = 0x00FFFFFF;
+        COLORREF prev = SetTextColor(fDC, color);
+        SkASSERT(prev != CLR_INVALID);
     }
 
     if (fBM && (fIsBW != isBW || fWidth < glyph.fWidth || fHeight < glyph.fHeight)) {
@@ -346,16 +485,6 @@
     }
     fIsBW = isBW;
 
-    COLORREF color = fgColor;
-    if (fIsBW) {
-        color = 0xFFFFFF;
-    }
-    if (fColor != color) {
-        fColor = color;
-        COLORREF prev = SetTextColor(fDC, color);
-        SkASSERT(prev != CLR_INVALID);
-    }
-
     fWidth = SkMax32(fWidth, glyph.fWidth);
     fHeight = SkMax32(fHeight, glyph.fHeight);
 
@@ -389,8 +518,7 @@
     // erase
     size_t srcRB = isBW ? (biWidth >> 3) : (fWidth << 2);
     size_t size = fHeight * srcRB;
-    unsigned bg = (0 == color) ? 0xFF : 0;
-    memset(fBits, bg, size);
+    memset(fBits, 0, size);
 
     XFORM xform = fXform;
     xform.eDx = (float)-glyph.fLeft;
@@ -408,7 +536,7 @@
     return (const char*)fBits + (fHeight - glyph.fHeight) * srcRB;
 }
 
-//////////////////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
 
 class SkScalerContext_Windows : public SkScalerContext {
 public:
@@ -416,13 +544,14 @@
     virtual ~SkScalerContext_Windows();
 
 protected:
-    virtual unsigned generateGlyphCount();
-    virtual uint16_t generateCharToGlyph(SkUnichar uni);
-    virtual void generateAdvance(SkGlyph* glyph);
-    virtual void generateMetrics(SkGlyph* glyph);
-    virtual void generateImage(const SkGlyph& glyph);
-    virtual void generatePath(const SkGlyph& glyph, SkPath* path);
-    virtual void generateFontMetrics(SkPaint::FontMetrics* mX, SkPaint::FontMetrics* mY);
+    virtual unsigned generateGlyphCount() SK_OVERRIDE;
+    virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
+    virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
+                                     SkPaint::FontMetrics* mY) SK_OVERRIDE;
 
 private:
     HDCOffscreen fOffscreen;
@@ -435,9 +564,21 @@
     SCRIPT_CACHE fSC;
     int          fGlyphCount;
 
+    /**
+     *  Some fonts need extra pixels added to avoid clipping, as the bounds
+     *  returned by getOutlineMetrics does not match what GDI draws. Since
+     *  this costs more RAM and therefore slower blits, we have a table to
+     *  only do this for known "bad" fonts.
+     */
+    SkIRect      fOutset;
+
     HFONT        fHiResFont;
     MAT2         fMat22Identity;
     SkMatrix     fHiResMatrix;
+    enum Type {
+        kTrueType_Type, kBitmap_Type,
+    } fType;
+    TEXTMETRIC fTM;
 };
 
 static float mul2float(SkScalar a, SkScalar b) {
@@ -481,20 +622,6 @@
         , fGlyphCount(-1) {
     SkAutoMutexAcquire  ac(gFTMutex);
 
-    fScale = fRec.fTextSize / gCanonicalTextSize;
-
-    fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
-    fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
-    fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
-    fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
-    fXform.eDx = 0;
-    fXform.eDy = 0;
-
-    fMat22.eM11 = float2FIXED(fXform.eM11);
-    fMat22.eM12 = float2FIXED(fXform.eM12);
-    fMat22.eM21 = float2FIXED(-fXform.eM21);
-    fMat22.eM22 = float2FIXED(-fXform.eM22);
-
     fDDC = ::CreateCompatibleDC(NULL);
     SetGraphicsMode(fDDC, GM_ADVANCED);
     SetBkMode(fDDC, TRANSPARENT);
@@ -507,6 +634,10 @@
     lf.lfQuality = compute_quality(fRec);
     fFont = CreateFontIndirect(&lf);
 
+    if (!compute_bounds_outset(lf, &fOutset)) {
+        fOutset.setEmpty();
+    }
+
     // if we're rotated, or want fractional widths, create a hires font
     fHiResFont = 0;
     if (needHiResMetrics(fRec.fPost2x2)) {
@@ -523,8 +654,72 @@
     }
     fSavefont = (HFONT)SelectObject(fDDC, fFont);
 
-    if (needToRenderWithSkia(fRec)) {
-        this->forceGenerateImageFromPath();
+    if (0 == GetTextMetrics(fDDC, &fTM)) {
+        ensure_typeface_accessible(fRec.fFontID);
+        if (0 == GetTextMetrics(fDDC, &fTM)) {
+            fTM.tmPitchAndFamily = TMPF_TRUETYPE;
+        }
+    }
+    // Used a logfont on a memory context, should never get a device font.
+    // Therefore all TMPF_DEVICE will be PostScript fonts.
+
+    // If TMPF_VECTOR is set, one of TMPF_TRUETYPE or TMPF_DEVICE must be set,
+    // otherwise we have a vector FON, which we don't support.
+    // This was determined by testing with Type1 PFM/PFB and OpenTypeCFF OTF,
+    // as well as looking at Wine bugs and sources.
+    SkASSERT(!(fTM.tmPitchAndFamily & TMPF_VECTOR) ||
+              (fTM.tmPitchAndFamily & (TMPF_TRUETYPE | TMPF_DEVICE)));
+
+    if (fTM.tmPitchAndFamily & TMPF_VECTOR) {
+        // Truetype or PostScript.
+        // Stroked FON also gets here (TMPF_VECTOR), but we don't handle it.
+        fType = SkScalerContext_Windows::kTrueType_Type;
+        fScale = fRec.fTextSize / gCanonicalTextSize;
+
+        fXform.eM11 = mul2float(fScale, fRec.fPost2x2[0][0]);
+        fXform.eM12 = mul2float(fScale, fRec.fPost2x2[1][0]);
+        fXform.eM21 = mul2float(fScale, fRec.fPost2x2[0][1]);
+        fXform.eM22 = mul2float(fScale, fRec.fPost2x2[1][1]);
+        fXform.eDx = 0;
+        fXform.eDy = 0;
+
+        fMat22.eM11 = float2FIXED(fXform.eM11);
+        fMat22.eM12 = float2FIXED(fXform.eM12);
+        fMat22.eM21 = float2FIXED(-fXform.eM21);
+        fMat22.eM22 = float2FIXED(-fXform.eM22);
+
+        if (needToRenderWithSkia(fRec)) {
+            this->forceGenerateImageFromPath();
+        }
+
+    } else {
+        // Assume bitmap
+        fType = SkScalerContext_Windows::kBitmap_Type;
+        fScale = SK_Scalar1;
+
+        fXform.eM11 = 1.0f;
+        fXform.eM12 = 0.0f;
+        fXform.eM21 = 0.0f;
+        fXform.eM22 = 1.0f;
+        fXform.eDx = 0.0f;
+        fXform.eDy = 0.0f;
+
+        fMat22.eM11 = SkScalarToFIXED(fRec.fPost2x2[0][0]);
+        fMat22.eM12 = SkScalarToFIXED(fRec.fPost2x2[1][0]);
+        fMat22.eM21 = SkScalarToFIXED(-fRec.fPost2x2[0][1]);
+        fMat22.eM22 = SkScalarToFIXED(-fRec.fPost2x2[1][1]);
+
+        lf.lfHeight = -SkScalarCeilToInt(fRec.fTextSize);
+        HFONT bitmapFont = CreateFontIndirect(&lf);
+        SelectObject(fDDC, bitmapFont);
+        ::DeleteObject(fFont);
+        fFont = bitmapFont;
+
+        if (0 == GetTextMetrics(fDDC, &fTM)) {
+            ensure_typeface_accessible(fRec.fFontID);
+            //if the following fails, we'll just draw at gCanonicalTextSize.
+            GetTextMetrics(fDDC, &fTM);
+        }
     }
 
     fOffscreen.init(fFont, fXform);
@@ -548,7 +743,10 @@
 
 unsigned SkScalerContext_Windows::generateGlyphCount() {
     if (fGlyphCount < 0) {
-        fGlyphCount = calculateGlyphCount(fDDC);
+        if (fType == SkScalerContext_Windows::kBitmap_Type) {
+           return fTM.tmLastChar;
+        }
+        fGlyphCount = calculateOutlineGlyphCount(fDDC);
     }
     return fGlyphCount;
 }
@@ -586,6 +784,29 @@
 
     SkASSERT(fDDC);
 
+    if (fType == SkScalerContext_Windows::kBitmap_Type) {
+        SIZE size;
+        WORD glyphs = glyph->getGlyphID(0);
+        if (0 == GetTextExtentPointI(fDDC, &glyphs, 1, &size)) {
+            glyph->fWidth = SkToS16(fTM.tmMaxCharWidth);
+        } else {
+            glyph->fWidth = SkToS16(size.cx);
+        }
+        glyph->fHeight = SkToS16(size.cy);
+
+        glyph->fTop = SkToS16(-fTM.tmAscent);
+        glyph->fLeft = SkToS16(0);
+        glyph->fAdvanceX = SkIntToFixed(glyph->fWidth);
+        glyph->fAdvanceY = 0;
+
+        //Apply matrix to values.
+        glyph->fAdvanceY = SkFixedMul(SkFIXEDToFixed(fMat22.eM21), glyph->fAdvanceX);
+        glyph->fAdvanceX = SkFixedMul(SkFIXEDToFixed(fMat22.eM11), glyph->fAdvanceX);
+
+        apply_outset(glyph, fOutset);
+        return;
+    }
+
     GLYPHMETRICS gm;
     sk_bzero(&gm, sizeof(gm));
 
@@ -627,6 +848,8 @@
             glyph->fHeight += 4;
             glyph->fTop -= 2;
             glyph->fLeft -= 2;
+
+            apply_outset(glyph, fOutset);
         }
 
         if (fHiResFont) {
@@ -642,7 +865,7 @@
             SelectObject(fDDC, fFont);
         }
     } else {
-        glyph->fWidth = 0;
+        glyph->zeroMetrics();
     }
 }
 
@@ -654,6 +877,27 @@
 
     SkASSERT(fDDC);
 
+    if (fType == SkScalerContext_Windows::kBitmap_Type) {
+        if (mx) {
+            mx->fTop = SkIntToScalar(-fTM.tmAscent);
+            mx->fAscent = SkIntToScalar(-fTM.tmAscent);
+            mx->fDescent = -SkIntToScalar(fTM.tmDescent);
+            mx->fBottom = SkIntToScalar(fTM.tmDescent);
+            mx->fLeading = SkIntToScalar(fTM.tmInternalLeading
+                                         + fTM.tmExternalLeading);
+        }
+
+        if (my) {
+            my->fTop = SkIntToScalar(-fTM.tmAscent);
+            my->fAscent = SkIntToScalar(-fTM.tmAscent);
+            my->fDescent = SkIntToScalar(-fTM.tmDescent);
+            my->fBottom = SkIntToScalar(fTM.tmDescent);
+            my->fLeading = SkIntToScalar(fTM.tmInternalLeading
+                                         + fTM.tmExternalLeading);
+        }
+        return;
+    }
+
     OUTLINETEXTMETRIC otm;
 
     uint32_t ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
@@ -662,7 +906,7 @@
         ret = GetOutlineTextMetrics(fDDC, sizeof(otm), &otm);
     }
     if (sizeof(otm) != ret) {
-      return;
+        return;
     }
 
     if (mx) {
@@ -689,59 +933,84 @@
 static void build_power_table(uint8_t table[], float ee) {
     for (int i = 0; i < 256; i++) {
         float x = i / 255.f;
-        x = powf(x, ee);
+        x = sk_float_pow(x, ee);
         int xx = SkScalarRound(SkFloatToScalar(x * 255));
         table[i] = SkToU8(xx);
     }
 }
 
-// This will invert the gamma applied by GDI, so we can sort-of get linear values.
-// Needed when we draw non-black, non-white text, and don't know how to bias it.
-static const uint8_t* getInverseGammaTable() {
+/**
+ *  This will invert the gamma applied by GDI (gray-scale antialiased), so we
+ *  can get linear values.
+ *
+ *  GDI grayscale appears to use a hard-coded gamma of 2.3.
+ *
+ *  GDI grayscale appears to draw using the black and white rasterizer at four
+ *  times the size and then downsamples to compute the coverage mask. As a
+ *  result there are only seventeen total grays. This lack of fidelity means
+ *  that shifting into other color spaces is imprecise.
+ */
+static const uint8_t* getInverseGammaTableGDI() {
     static bool gInited;
-    static uint8_t gTable[256];
+    static uint8_t gTableGdi[256];
+    if (!gInited) {
+        build_power_table(gTableGdi, 2.3f);
+        gInited = true;
+    }
+    return gTableGdi;
+}
+
+/**
+ *  This will invert the gamma applied by GDI ClearType, so we can get linear
+ *  values.
+ *
+ *  GDI ClearType uses SPI_GETFONTSMOOTHINGCONTRAST / 1000 as the gamma value.
+ *  If this value is not specified, the default is a gamma of 1.4.
+ */
+static const uint8_t* getInverseGammaTableClearType() {
+    static bool gInited;
+    static uint8_t gTableClearType[256];
     if (!gInited) {
         UINT level = 0;
         if (!SystemParametersInfo(SPI_GETFONTSMOOTHINGCONTRAST, 0, &level, 0) || !level) {
             // can't get the data, so use a default
             level = 1400;
         }
-        build_power_table(gTable, level / 1000.0f);
+        build_power_table(gTableClearType, level / 1000.0f);
         gInited = true;
     }
-    return gTable;
+    return gTableClearType;
 }
 
 #include "SkColorPriv.h"
 
-// gdi's bitmap is upside-down, so we reverse dst walking in Y
-// whenever we copy it into skia's buffer
-
-static int compute_luminance(int r, int g, int b) {
-//    return (r * 2 + g * 5 + b) >> 3;
-    return (r * 27 + g * 92 + b * 9) >> 7;
+//Cannot assume that the input rgb is gray due to possible setting of kGenA8FromLCD_Flag.
+template<bool APPLY_PREBLEND>
+static inline uint8_t rgb_to_a8(SkGdiRGB rgb, const uint8_t* table8) {
+    U8CPU r = (rgb >> 16) & 0xFF;
+    U8CPU g = (rgb >>  8) & 0xFF;
+    U8CPU b = (rgb >>  0) & 0xFF;
+    return sk_apply_lut_if<APPLY_PREBLEND>(SkComputeLuminance(r, g, b), table8);
 }
 
-static inline uint8_t rgb_to_a8(SkGdiRGB rgb) {
-    int r = (rgb >> 16) & 0xFF;
-    int g = (rgb >>  8) & 0xFF;
-    int b = (rgb >>  0) & 0xFF;
-    return compute_luminance(r, g, b);
+template<bool APPLY_PREBLEND>
+static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb, const uint8_t* tableR,
+                                                  const uint8_t* tableG,
+                                                  const uint8_t* tableB) {
+    U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+    U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  8) & 0xFF, tableG);
+    U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  0) & 0xFF, tableB);
+    return SkPack888ToRGB16(r, g, b);
 }
 
-static inline uint16_t rgb_to_lcd16(SkGdiRGB rgb) {
-    int r = (rgb >> 16) & 0xFF;
-    int g = (rgb >>  8) & 0xFF;
-    int b = (rgb >>  0) & 0xFF;
-    return SkPackRGB16(SkR32ToR16(r), SkG32ToG16(g), SkB32ToB16(b));
-}
-
-static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb) {
-    int r = (rgb >> 16) & 0xFF;
-    int g = (rgb >>  8) & 0xFF;
-    int b = (rgb >>  0) & 0xFF;
-    int a = SkMax32(r, SkMax32(g, b));
-    return SkPackARGB32(a, r, g, b);
+template<bool APPLY_PREBLEND>
+static inline SkPMColor rgb_to_lcd32(SkGdiRGB rgb, const uint8_t* tableR,
+                                                   const uint8_t* tableG,
+                                                   const uint8_t* tableB) {
+    U8CPU r = sk_apply_lut_if<APPLY_PREBLEND>((rgb >> 16) & 0xFF, tableR);
+    U8CPU g = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  8) & 0xFF, tableG);
+    U8CPU b = sk_apply_lut_if<APPLY_PREBLEND>((rgb >>  0) & 0xFF, tableB);
+    return SkPackARGB32(0xFF, r, g, b);
 }
 
 // Is this GDI color neither black nor white? If so, we have to keep this
@@ -768,8 +1037,10 @@
     return true;
 }
 
+// gdi's bitmap is upside-down, so we reverse dst walking in Y
+// whenever we copy it into skia's buffer
 static void rgb_to_bw(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
-                      const SkGlyph& glyph, int32_t xorMask) {
+                      const SkGlyph& glyph) {
     const int width = glyph.fWidth;
     const size_t dstRB = (width + 7) >> 3;
     uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
@@ -785,14 +1056,14 @@
         if (byteCount > 0) {
             for (int i = 0; i < byteCount; ++i) {
                 unsigned byte = 0;
-                byte |= (src[0] ^ xorMask) & (1 << 7);
-                byte |= (src[1] ^ xorMask) & (1 << 6);
-                byte |= (src[2] ^ xorMask) & (1 << 5);
-                byte |= (src[3] ^ xorMask) & (1 << 4);
-                byte |= (src[4] ^ xorMask) & (1 << 3);
-                byte |= (src[5] ^ xorMask) & (1 << 2);
-                byte |= (src[6] ^ xorMask) & (1 << 1);
-                byte |= (src[7] ^ xorMask) & (1 << 0);
+                byte |= src[0] & (1 << 7);
+                byte |= src[1] & (1 << 6);
+                byte |= src[2] & (1 << 5);
+                byte |= src[3] & (1 << 4);
+                byte |= src[4] & (1 << 3);
+                byte |= src[5] & (1 << 2);
+                byte |= src[6] & (1 << 1);
+                byte |= src[7] & (1 << 0);
                 dst[i] = byte;
                 src += 8;
             }
@@ -801,7 +1072,7 @@
             unsigned byte = 0;
             unsigned mask = 0x80;
             for (int i = 0; i < bitCount; i++) {
-                byte |= (src[i] ^ xorMask) & mask;
+                byte |= src[i] & mask;
                 mask >>= 1;
             }
             dst[byteCount] = byte;
@@ -811,48 +1082,51 @@
     }
 }
 
+template<bool APPLY_PREBLEND>
 static void rgb_to_a8(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
-                      const SkGlyph& glyph, int32_t xorMask) {
+                      const SkGlyph& glyph, const uint8_t* table8) {
     const size_t dstRB = glyph.rowBytes();
     const int width = glyph.fWidth;
     uint8_t* SK_RESTRICT dst = (uint8_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
 
     for (int y = 0; y < glyph.fHeight; y++) {
         for (int i = 0; i < width; i++) {
-            dst[i] = rgb_to_a8(src[i] ^ xorMask);
+            dst[i] = rgb_to_a8<APPLY_PREBLEND>(src[i], table8);
         }
         src = SkTAddByteOffset(src, srcRB);
         dst -= dstRB;
     }
 }
 
-static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
-                         const SkGlyph& glyph, int32_t xorMask) {
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd16(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
+                         const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
     const size_t dstRB = glyph.rowBytes();
     const int width = glyph.fWidth;
     uint16_t* SK_RESTRICT dst = (uint16_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
 
     for (int y = 0; y < glyph.fHeight; y++) {
         for (int i = 0; i < width; i++) {
-            dst[i] = rgb_to_lcd16(src[i] ^ xorMask);
+            dst[i] = rgb_to_lcd16<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
         }
         src = SkTAddByteOffset(src, srcRB);
         dst = (uint16_t*)((char*)dst - dstRB);
     }
 }
 
-static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB,
-                         const SkGlyph& glyph, int32_t xorMask) {
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd32(const SkGdiRGB* SK_RESTRICT src, size_t srcRB, const SkGlyph& glyph,
+                         const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) {
     const size_t dstRB = glyph.rowBytes();
     const int width = glyph.fWidth;
-    SkPMColor* SK_RESTRICT dst = (SkPMColor*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
+    uint32_t* SK_RESTRICT dst = (uint32_t*)((char*)glyph.fImage + (glyph.fHeight - 1) * dstRB);
 
     for (int y = 0; y < glyph.fHeight; y++) {
         for (int i = 0; i < width; i++) {
-            dst[i] = rgb_to_lcd32(src[i] ^ xorMask);
+            dst[i] = rgb_to_lcd32<APPLY_PREBLEND>(src[i], tableR, tableG, tableB);
         }
         src = SkTAddByteOffset(src, srcRB);
-        dst = (SkPMColor*)((char*)dst - dstRB);
+        dst = (uint32_t*)((char*)dst - dstRB);
     }
 }
 
@@ -861,46 +1135,38 @@
     return x - (x >> 8);
 }
 
-#define WHITE_LUMINANCE_LIMIT   0xA0
-#define BLACK_LUMINANCE_LIMIT   0x40
-
 void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
-    SkAutoMutexAcquire  ac(gFTMutex);
-
+    SkAutoMutexAcquire ac(gFTMutex);
     SkASSERT(fDDC);
 
     const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
     const bool isAA = !isLCD(fRec);
-    bool isWhite = fRec.getLuminanceByte() >= WHITE_LUMINANCE_LIMIT;
-    bool isBlack = fRec.getLuminanceByte() <= BLACK_LUMINANCE_LIMIT;
-
-    SkGdiRGB fgColor;
-    uint32_t rgbXOR;
-    const uint8_t* table = NULL;
-    if (isBW || isWhite) {
-        fgColor = 0x00FFFFFF;
-        rgbXOR = 0;
-    } else if (isBlack) {
-        fgColor = 0;
-        rgbXOR = ~0;
-    } else {
-        table = getInverseGammaTable();
-        fgColor = 0x00FFFFFF;
-        rgbXOR = 0;
-    }
 
     size_t srcRB;
-    const void* bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
+    const void* bits = fOffscreen.draw(glyph, isBW, &srcRB);
     if (NULL == bits) {
         ensure_typeface_accessible(fRec.fFontID);
-        bits = fOffscreen.draw(glyph, isBW, fgColor, &srcRB);
+        bits = fOffscreen.draw(glyph, isBW, &srcRB);
         if (NULL == bits) {
             sk_bzero(glyph.fImage, glyph.computeImageSize());
             return;
         }
     }
 
-    if (table) {
+    if (!isBW) {
+        const uint8_t* table;
+        //The offscreen contains a GDI blit if isAA and kGenA8FromLCD_Flag is not set.
+        //Otherwise the offscreen contains a ClearType blit.
+        if (isAA && !(fRec.fFlags & SkScalerContext::kGenA8FromLCD_Flag)) {
+            table = getInverseGammaTableGDI();
+        } else {
+            table = getInverseGammaTableClearType();
+        }
+        //Note that the following cannot really be integrated into the
+        //pre-blend, since we may not be applying the pre-blend; when we aren't
+        //applying the pre-blend it means that a filter wants linear anyway.
+        //Other code may also be applying the pre-blend, so we'd need another
+        //one with this and one without.
         SkGdiRGB* addr = (SkGdiRGB*)bits;
         for (int y = 0; y < glyph.fHeight; ++y) {
             for (int x = 0; x < glyph.fWidth; ++x) {
@@ -927,18 +1193,34 @@
         // since the caller may require A8 for maskfilters, we can't check for BW
         // ... until we have the caller tell us that explicitly
         const SkGdiRGB* src = (const SkGdiRGB*)bits;
-        rgb_to_a8(src, srcRB, glyph, rgbXOR);
+        if (fPreBlend.isApplicable()) {
+            rgb_to_a8<true>(src, srcRB, glyph, fPreBlend.fG);
+        } else {
+            rgb_to_a8<false>(src, srcRB, glyph, fPreBlend.fG);
+        }
     } else {    // LCD16
         const SkGdiRGB* src = (const SkGdiRGB*)bits;
         if (is_rgb_really_bw(src, width, glyph.fHeight, srcRB)) {
-            rgb_to_bw(src, srcRB, glyph, rgbXOR);
+            rgb_to_bw(src, srcRB, glyph);
             ((SkGlyph*)&glyph)->fMaskFormat = SkMask::kBW_Format;
         } else {
             if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
-                rgb_to_lcd16(src, srcRB, glyph, rgbXOR);
+                if (fPreBlend.isApplicable()) {
+                    rgb_to_lcd16<true>(src, srcRB, glyph,
+                                       fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                } else {
+                    rgb_to_lcd16<false>(src, srcRB, glyph,
+                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                }
             } else {
                 SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
-                rgb_to_lcd32(src, srcRB, glyph, rgbXOR);
+                if (fPreBlend.isApplicable()) {
+                    rgb_to_lcd32<true>(src, srcRB, glyph,
+                                       fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                } else {
+                    rgb_to_lcd32<false>(src, srcRB, glyph,
+                                        fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+                }
             }
         }
     }
@@ -1015,13 +1297,103 @@
     //OutputDebugString(buf);
 }
 
-void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
-    SkDEBUGFAIL("SkFontHost::Serialize unimplemented");
+static void logfont_for_name(const char* familyName, LOGFONT& lf) {
+        memset(&lf, 0, sizeof(LOGFONT));
+#ifdef UNICODE
+        // Get the buffer size needed first.
+        size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
+                                                -1, NULL, 0);
+        // Allocate a buffer (str_len already has terminating null
+        // accounted for).
+        wchar_t *wideFamilyName = new wchar_t[str_len];
+        // Now actually convert the string.
+        ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
+                                wideFamilyName, str_len);
+        ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE - 1);
+        delete [] wideFamilyName;
+        lf.lfFaceName[LF_FACESIZE-1] = L'\0';
+#else
+        ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE - 1);
+        lf.lfFaceName[LF_FACESIZE - 1] = '\0';
+#endif
+}
+
+static void tchar_to_skstring(const TCHAR* t, SkString* s) {
+#ifdef UNICODE
+    size_t sSize = WideCharToMultiByte(CP_UTF8, 0, t, -1, NULL, 0, NULL, NULL);
+    s->resize(sSize);
+    WideCharToMultiByte(CP_UTF8, 0, t, -1, s->writable_str(), sSize, NULL, NULL);
+#else
+    s->set(t);
+#endif
+}
+
+void SkFontHost::Serialize(const SkTypeface* rawFace, SkWStream* stream) {
+    const LogFontTypeface* face = static_cast<const LogFontTypeface*>(rawFace);
+    SkFontDescriptor descriptor(face->style());
+
+    // Get the actual name of the typeface. The logfont may not know this.
+    HFONT font = CreateFontIndirect(&face->fLogFont);
+
+    HDC deviceContext = ::CreateCompatibleDC(NULL);
+    HFONT savefont = (HFONT)SelectObject(deviceContext, font);
+
+    int fontNameLen; //length of fontName in TCHARS.
+    if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
+        SkFontHost::EnsureTypefaceAccessible(*rawFace);
+        if (0 == (fontNameLen = GetTextFace(deviceContext, 0, NULL))) {
+            fontNameLen = 0;
+        }
+    }
+
+    SkAutoSTArray<LF_FULLFACESIZE, TCHAR> fontName(fontNameLen+1);
+    if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
+        SkFontHost::EnsureTypefaceAccessible(*rawFace);
+        if (0 == GetTextFace(deviceContext, fontNameLen, fontName.get())) {
+            fontName[0] = 0;
+        }
+    }
+
+    if (deviceContext) {
+        ::SelectObject(deviceContext, savefont);
+        ::DeleteDC(deviceContext);
+    }
+    if (font) {
+        ::DeleteObject(font);
+    }
+
+    SkString familyName;
+    tchar_to_skstring(fontName.get(), &familyName);
+    descriptor.setFamilyName(familyName.c_str());
+    //TODO: FileName and PostScriptName currently unsupported.
+
+    descriptor.serialize(stream);
+
+    if (face->fSerializeAsStream) {
+        // store the entire font in the fontData
+        SkAutoTUnref<SkStream> fontStream(SkFontHost::OpenStream(face->uniqueID()));
+        const uint32_t length = fontStream->getLength();
+
+        stream->writePackedUInt(length);
+        stream->writeStream(fontStream, length);
+    } else {
+        stream->writePackedUInt(0);
+    }
 }
 
 SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
-    SkDEBUGFAIL("SkFontHost::Deserialize unimplemented");
-    return NULL;
+    SkFontDescriptor descriptor(stream);
+
+    const uint32_t customFontDataLength = stream->readPackedUInt();
+    if (customFontDataLength > 0) {
+        // generate a new stream to store the custom typeface
+        SkAutoTUnref<SkMemoryStream> fontStream(SkNEW_ARGS(SkMemoryStream, (customFontDataLength - 1)));
+        stream->read((void*)fontStream->getMemoryBase(), customFontDataLength - 1);
+
+        return CreateTypefaceFromStream(fontStream.get());
+    }
+
+    return SkFontHost::CreateTypeface(NULL, descriptor.getFamilyName(), descriptor.getStyle());
 }
 
 static bool getWidthAdvance(HDC hdc, int gId, int16_t* advance) {
@@ -1074,33 +1446,21 @@
     if (!GetOutlineTextMetrics(hdc, sizeof(otm), &otm)) {
         goto Error;
     }
-    glyphCount = calculateGlyphCount(hdc);
+    glyphCount = calculateOutlineGlyphCount(hdc);
 
     info = new SkAdvancedTypefaceMetrics;
     info->fEmSize = otm.otmEMSquare;
     info->fMultiMaster = false;
     info->fLastGlyphID = SkToU16(glyphCount - 1);
     info->fStyle = 0;
-#ifdef UNICODE
-    // Get the buffer size needed first.
-    size_t str_len = WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, NULL,
-                                         0, NULL, NULL);
-    // Allocate a buffer (str_len already has terminating null accounted for).
-    char *familyName = new char[str_len];
-    // Now actually convert the string.
-    WideCharToMultiByte(CP_UTF8, 0, lf.lfFaceName, -1, familyName, str_len,
-                          NULL, NULL);
-    info->fFontName.set(familyName);
-    delete [] familyName;
-#else
-    info->fFontName.set(lf.lfFaceName);
-#endif
+    tchar_to_skstring(lf.lfFaceName, &info->fFontName);
 
     if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
         populate_glyph_to_unicode(hdc, glyphCount, &(info->fGlyphToUnicode));
     }
 
-    if (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE) {
+    if (glyphCount > 0 &&
+        (otm.otmTextMetrics.tmPitchAndFamily & TMPF_TRUETYPE)) {
         info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
     } else {
         info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
@@ -1187,11 +1547,100 @@
     return info;
 }
 
-SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+//Dummy representation of a Base64 encoded GUID from create_unique_font_name.
+#define BASE64_GUID_ID "XXXXXXXXXXXXXXXXXXXXXXXX"
+//Length of GUID representation from create_id, including NULL terminator.
+#define BASE64_GUID_ID_LEN SK_ARRAY_COUNT(BASE64_GUID_ID)
 
-    //Should not be used on Windows, keep linker happy
-    SkASSERT(false);
-    return SkCreateTypefaceFromLOGFONT(get_default_font());
+SK_COMPILE_ASSERT(BASE64_GUID_ID_LEN < LF_FACESIZE, GUID_longer_than_facesize);
+
+/**
+   NameID 6 Postscript names cannot have the character '/'.
+   It would be easier to hex encode the GUID, but that is 32 bytes,
+   and many systems have issues with names longer than 28 bytes.
+   The following need not be any standard base64 encoding.
+   The encoded value is never decoded.
+*/
+static const char postscript_safe_base64_encode[] =
+    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+    "abcdefghijklmnopqrstuvwxyz"
+    "0123456789-_=";
+
+/**
+   Formats a GUID into Base64 and places it into buffer.
+   buffer should have space for at least BASE64_GUID_ID_LEN characters.
+   The string will always be null terminated.
+   XXXXXXXXXXXXXXXXXXXXXXXX0
+ */
+static void format_guid_b64(const GUID& guid, char* buffer, size_t bufferSize) {
+    SkASSERT(bufferSize >= BASE64_GUID_ID_LEN);
+    size_t written = SkBase64::Encode(&guid, sizeof(guid), buffer, postscript_safe_base64_encode);
+    SkASSERT(written < LF_FACESIZE);
+    buffer[written] = '\0';
+}
+
+/**
+   Creates a Base64 encoded GUID and places it into buffer.
+   buffer should have space for at least BASE64_GUID_ID_LEN characters.
+   The string will always be null terminated.
+   XXXXXXXXXXXXXXXXXXXXXXXX0
+ */
+static HRESULT create_unique_font_name(char* buffer, size_t bufferSize) {
+    GUID guid = {};
+    if (FAILED(CoCreateGuid(&guid))) {
+        return E_UNEXPECTED;
+    }
+    format_guid_b64(guid, buffer, bufferSize);
+
+    return S_OK;
+}
+
+/**
+   Introduces a font to GDI. On failure will return NULL. The returned handle
+   should eventually be passed to RemoveFontMemResourceEx.
+*/
+static HANDLE activate_font(SkData* fontData) {
+    DWORD numFonts = 0;
+    //AddFontMemResourceEx just copies the data, but does not specify const.
+    HANDLE fontHandle = AddFontMemResourceEx(const_cast<void*>(fontData->data()),
+                                             fontData->size(),
+                                             0,
+                                             &numFonts);
+
+    if (fontHandle != NULL && numFonts < 1) {
+        RemoveFontMemResourceEx(fontHandle);
+        return NULL;
+    }
+
+    return fontHandle;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    // Create a unique and unpredictable font name.
+    // Avoids collisions and access from CSS.
+    char familyName[BASE64_GUID_ID_LEN];
+    const int familyNameSize = SK_ARRAY_COUNT(familyName);
+    if (FAILED(create_unique_font_name(familyName, familyNameSize))) {
+        return NULL;
+    }
+
+    // Change the name of the font.
+    SkAutoTUnref<SkData> rewrittenFontData(SkOTUtils::RenameFont(stream, familyName, familyNameSize-1));
+    if (NULL == rewrittenFontData.get()) {
+        return NULL;
+    }
+
+    // Register the font with GDI.
+    HANDLE fontReference = activate_font(rewrittenFontData.get());
+    if (NULL == fontReference) {
+        return NULL;
+    }
+
+    // Create the typeface.
+    LOGFONT lf;
+    logfont_for_name(familyName, lf);
+
+    return SkCreateFontMemResourceTypefaceFromLOGFONT(lf, fontReference);
 }
 
 SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
@@ -1246,7 +1695,6 @@
 
 SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
                                        const char familyName[],
-                                       const void* data, size_t bytelength,
                                        SkTypeface::Style style) {
     LOGFONT lf;
     if (NULL == familyFace && NULL == familyName) {
@@ -1255,34 +1703,23 @@
         LogFontTypeface* face = (LogFontTypeface*)familyFace;
         lf = face->fLogFont;
     } else {
-        memset(&lf, 0, sizeof(LOGFONT));
-#ifdef UNICODE
-        // Get the buffer size needed first.
-        size_t str_len = ::MultiByteToWideChar(CP_UTF8, 0, familyName,
-                                                -1, NULL, 0);
-        // Allocate a buffer (str_len already has terminating null
-        // accounted for).
-        wchar_t *wideFamilyName = new wchar_t[str_len];
-        // Now actually convert the string.
-        ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
-                                wideFamilyName, str_len);
-        ::wcsncpy(lf.lfFaceName, wideFamilyName, LF_FACESIZE);
-        delete [] wideFamilyName;
-#else
-        ::strncpy(lf.lfFaceName, familyName, LF_FACESIZE);
-#endif
-        lf.lfFaceName[LF_FACESIZE-1] = '\0';
+        logfont_for_name(familyName, lf);
     }
     setStyle(&lf, style);
     return SkCreateTypefaceFromLOGFONT(lf);
 }
 
 SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
-    printf("SkFontHost::CreateTypefaceFromFile unimplemented");
-    return NULL;
+    SkTypeface* face = NULL;
+    SkAutoTUnref<SkFILEStream> stream(SkNEW_ARGS(SkFILEStream, (path)));
+
+    if (stream->isValid()) {
+        face = CreateTypefaceFromStream(stream);
+    }
+    return face;
 }
 
-void SkFontHost::FilterRec(SkScalerContext::Rec* rec) {
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface* typeface) {
     unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
                                   SkScalerContext::kAutohinting_Flag |
                                   SkScalerContext::kEmbeddedBitmapText_Flag |
@@ -1314,21 +1751,6 @@
 #endif
     rec->setHinting(h);
 
-    // for compatibility at the moment, discretize luminance to 3 settings
-    // black, white, gray. This helps with fontcache utilization, since we
-    // won't create multiple entries that in the end map to the same results.
-    {
-        unsigned lum = rec->getLuminanceByte();
-        if (lum <= BLACK_LUMINANCE_LIMIT) {
-            lum = 0;
-        } else if (lum >= WHITE_LUMINANCE_LIMIT) {
-            lum = SkScalerContext::kLuminance_Max;
-        } else {
-            lum = SkScalerContext::kLuminance_Max >> 1;
-        }
-        rec->setLuminanceBits(lum);
-    }
-
 // turn this off since GDI might turn A8 into BW! Need a bigger fix.
 #if 0
     // Disable LCD when rotated, since GDI's output is ugly
@@ -1337,11 +1759,9 @@
     }
 #endif
 
-#if 0
-    if (SkMask::kLCD16_Format == rec->fMaskFormat) {
-        rec->fMaskFormat = SkMask::kLCD32_Format;
+    LogFontTypeface* logfontTypeface = static_cast<LogFontTypeface*>(typeface);
+    if (!logfontTypeface->fCanBeLCD && isLCD(*rec)) {
+        rec->fMaskFormat = SkMask::kA8_Format;
+        rec->fFlags &= ~SkScalerContext::kGenA8FromLCD_Flag;
     }
-#endif
 }
-
-#endif // WIN32
diff --git a/src/ports/SkFontHost_win_dw.cpp b/src/ports/SkFontHost_win_dw.cpp
new file mode 100644
index 0000000..1f257eb
--- /dev/null
+++ b/src/ports/SkFontHost_win_dw.cpp
@@ -0,0 +1,1559 @@
+/*
+ * 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 "SkTypes.h"
+#undef GetGlyphIndices
+
+#include "SkAdvancedTypefaceMetrics.h"
+#include "SkColorFilter.h"
+#include "SkDWriteFontFileStream.h"
+#include "SkDWriteGeometrySink.h"
+#include "SkDescriptor.h"
+#include "SkEndian.h"
+#include "SkFontDescriptor.h"
+#include "SkFontHost.h"
+#include "SkGlyph.h"
+#include "SkHRESULT.h"
+#include "SkMaskGamma.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_hhea.h"
+#include "SkOTTable_OS_2.h"
+#include "SkOTTable_post.h"
+#include "SkPath.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTScopedComPtr.h"
+#include "SkThread.h"
+#include "SkTypeface_win.h"
+#include "SkTypefaceCache.h"
+#include "SkUtils.h"
+
+#include <dwrite.h>
+
+SK_DECLARE_STATIC_MUTEX(gFTMutex);
+
+static bool isLCD(const SkScalerContext::Rec& rec) {
+    return SkMask::kLCD16_Format == rec.fMaskFormat ||
+           SkMask::kLCD32_Format == rec.fMaskFormat;
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+  // Zero means that we don't have any fallback fonts for this fontID.
+  // This function is implemented on Android, but doesn't have much
+  // meaning here.
+  return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class DWriteOffscreen {
+public:
+    DWriteOffscreen() : fWidth(0), fHeight(0) {
+    }
+
+    void init(IDWriteFontFace* fontFace, const DWRITE_MATRIX& xform, FLOAT fontSize) {
+        fFontFace = fontFace;
+        fFontSize = fontSize;
+        fXform = xform;
+    }
+
+    const void* draw(const SkGlyph&, bool isBW);
+
+private:
+    uint16_t fWidth;
+    uint16_t fHeight;
+    IDWriteFontFace* fFontFace;
+    FLOAT fFontSize;
+    DWRITE_MATRIX fXform;
+    SkTDArray<uint8_t> fBits;
+};
+
+typedef HRESULT (WINAPI *DWriteCreateFactoryProc)(
+    __in DWRITE_FACTORY_TYPE factoryType,
+    __in REFIID iid,
+    __out IUnknown **factory
+);
+
+static HRESULT get_dwrite_factory(IDWriteFactory** factory) {
+    static IDWriteFactory* gDWriteFactory = NULL;
+
+    if (gDWriteFactory != NULL) {
+        *factory = gDWriteFactory;
+        return S_OK;
+    }
+
+    DWriteCreateFactoryProc dWriteCreateFactoryProc =
+        reinterpret_cast<DWriteCreateFactoryProc>(
+            GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory")
+        )
+    ;
+
+    if (!dWriteCreateFactoryProc) {
+        return E_UNEXPECTED;
+    }
+
+    HRM(dWriteCreateFactoryProc(DWRITE_FACTORY_TYPE_SHARED,
+                                __uuidof(IDWriteFactory),
+                                reinterpret_cast<IUnknown**>(&gDWriteFactory)),
+        "Could not create DirectWrite factory.");
+
+    *factory = gDWriteFactory;
+    return S_OK;
+}
+
+const void* DWriteOffscreen::draw(const SkGlyph& glyph, bool isBW) {
+    IDWriteFactory* factory;
+    HRNM(get_dwrite_factory(&factory), "Could not get factory.");
+
+    if (fWidth < glyph.fWidth || fHeight < glyph.fHeight) {
+        fWidth = SkMax32(fWidth, glyph.fWidth);
+        fHeight = SkMax32(fHeight, glyph.fHeight);
+
+        if (isBW) {
+            fBits.setCount(fWidth * fHeight);
+        } else {
+            fBits.setCount(fWidth * fHeight * 3);
+        }
+    }
+
+    // erase
+    memset(fBits.begin(), 0, fBits.count());
+
+    fXform.dx = SkFixedToFloat(glyph.getSubXFixed());
+    fXform.dy = SkFixedToFloat(glyph.getSubYFixed());
+
+    FLOAT advance = 0.0f;
+
+    UINT16 index = glyph.getGlyphID();
+
+    DWRITE_GLYPH_OFFSET offset;
+    offset.advanceOffset = 0.0f;
+    offset.ascenderOffset = 0.0f;
+
+    DWRITE_GLYPH_RUN run;
+    run.glyphCount = 1;
+    run.glyphAdvances = &advance;
+    run.fontFace = fFontFace;
+    run.fontEmSize = fFontSize;
+    run.bidiLevel = 0;
+    run.glyphIndices = &index;
+    run.isSideways = FALSE;
+    run.glyphOffsets = &offset;
+
+    DWRITE_RENDERING_MODE renderingMode;
+    DWRITE_TEXTURE_TYPE textureType;
+    if (isBW) {
+        renderingMode = DWRITE_RENDERING_MODE_ALIASED;
+        textureType = DWRITE_TEXTURE_ALIASED_1x1;
+    } else {
+        renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+        textureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+    }
+    SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
+    HRNM(factory->CreateGlyphRunAnalysis(&run,
+                                         1.0f, // pixelsPerDip,
+                                         &fXform,
+                                         renderingMode,
+                                         DWRITE_MEASURING_MODE_NATURAL,
+                                         0.0f, // baselineOriginX,
+                                         0.0f, // baselineOriginY,
+                                         &glyphRunAnalysis),
+         "Could not create glyph run analysis.");
+
+    //NOTE: this assumes that the glyph has already been measured
+    //with an exact same glyph run analysis.
+    RECT bbox;
+    bbox.left = glyph.fLeft;
+    bbox.top = glyph.fTop;
+    bbox.right = glyph.fLeft + glyph.fWidth;
+    bbox.bottom = glyph.fTop + glyph.fHeight;
+    HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType,
+                                              &bbox,
+                                              fBits.begin(),
+                                              fBits.count()),
+         "Could not draw mask.");
+    return fBits.begin();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class StreamFontFileLoader : public IDWriteFontFileLoader {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontFileLoader methods
+    virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey(
+        void const* fontFileReferenceKey,
+        UINT32 fontFileReferenceKeySize,
+        IDWriteFontFileStream** fontFileStream);
+
+    static HRESULT Create(SkStream* stream, StreamFontFileLoader** streamFontFileLoader) {
+        *streamFontFileLoader = new StreamFontFileLoader(stream);
+        if (NULL == streamFontFileLoader) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+
+    SkAutoTUnref<SkStream> fStream;
+
+private:
+    StreamFontFileLoader(SkStream* stream) : fRefCount(1), fStream(stream) {
+        stream->ref();
+    }
+
+    ULONG fRefCount;
+};
+
+HRESULT StreamFontFileLoader::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG StreamFontFileLoader::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG StreamFontFileLoader::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT StreamFontFileLoader::CreateStreamFromKey(
+    void const* fontFileReferenceKey,
+    UINT32 fontFileReferenceKeySize,
+    IDWriteFontFileStream** fontFileStream)
+{
+    SkTScopedComPtr<SkDWriteFontFileStreamWrapper> stream;
+    HR(SkDWriteFontFileStreamWrapper::Create(fStream, &stream));
+    *fontFileStream = stream.release();
+    return S_OK;
+}
+
+class StreamFontFileEnumerator : public IDWriteFontFileEnumerator {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontFileEnumerator methods
+    virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasCurrentFile);
+    virtual HRESULT STDMETHODCALLTYPE GetCurrentFontFile(IDWriteFontFile** fontFile);
+
+    static HRESULT Create(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader,
+                          StreamFontFileEnumerator** streamFontFileEnumerator) {
+        *streamFontFileEnumerator = new StreamFontFileEnumerator(factory, fontFileLoader);
+        if (NULL == streamFontFileEnumerator) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+private:
+    StreamFontFileEnumerator(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader);
+    ULONG fRefCount;
+
+    SkTScopedComPtr<IDWriteFactory> fFactory;
+    SkTScopedComPtr<IDWriteFontFile> fCurrentFile;
+    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
+    bool fHasNext;
+};
+
+StreamFontFileEnumerator::StreamFontFileEnumerator(IDWriteFactory* factory,
+                                                   IDWriteFontFileLoader* fontFileLoader)
+    : fRefCount(1)
+    , fFactory(factory)
+    , fCurrentFile()
+    , fFontFileLoader(fontFileLoader)
+    , fHasNext(true)
+{
+    factory->AddRef();
+    fontFileLoader->AddRef();
+}
+
+HRESULT StreamFontFileEnumerator::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG StreamFontFileEnumerator::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG StreamFontFileEnumerator::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT StreamFontFileEnumerator::MoveNext(BOOL* hasCurrentFile) {
+    *hasCurrentFile = FALSE;
+
+    if (!fHasNext) {
+        return S_OK;
+    }
+    fHasNext = false;
+
+    UINT32 dummy = 0;
+    HR(fFactory->CreateCustomFontFileReference(
+            &dummy, //cannot be NULL
+            sizeof(dummy), //even if this is 0
+            fFontFileLoader.get(),
+            &fCurrentFile));
+
+    *hasCurrentFile = TRUE;
+    return S_OK;
+}
+
+HRESULT StreamFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** fontFile) {
+    if (fCurrentFile.get() == NULL) {
+        *fontFile = NULL;
+        return E_FAIL;
+    }
+
+    fCurrentFile.get()->AddRef();
+    *fontFile = fCurrentFile.get();
+    return  S_OK;
+}
+
+class StreamFontCollectionLoader : public IDWriteFontCollectionLoader {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontCollectionLoader methods
+    virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey(
+        IDWriteFactory* factory,
+        void const* collectionKey,
+        UINT32 collectionKeySize,
+        IDWriteFontFileEnumerator** fontFileEnumerator);
+
+    static HRESULT Create(IDWriteFontFileLoader* fontFileLoader,
+                          StreamFontCollectionLoader** streamFontCollectionLoader) {
+        *streamFontCollectionLoader = new StreamFontCollectionLoader(fontFileLoader);
+        if (NULL == streamFontCollectionLoader) {
+            return E_OUTOFMEMORY;
+        }
+        return S_OK;
+    }
+private:
+    StreamFontCollectionLoader(IDWriteFontFileLoader* fontFileLoader)
+        : fRefCount(1)
+        , fFontFileLoader(fontFileLoader)
+    {
+        fontFileLoader->AddRef();
+    }
+
+    ULONG fRefCount;
+    SkTScopedComPtr<IDWriteFontFileLoader> fFontFileLoader;
+};
+
+HRESULT StreamFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG StreamFontCollectionLoader::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG StreamFontCollectionLoader::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT StreamFontCollectionLoader::CreateEnumeratorFromKey(
+    IDWriteFactory* factory,
+    void const* collectionKey,
+    UINT32 collectionKeySize,
+    IDWriteFontFileEnumerator** fontFileEnumerator)
+{
+    SkTScopedComPtr<StreamFontFileEnumerator> enumerator;
+    HR(StreamFontFileEnumerator::Create(factory, fFontFileLoader.get(), &enumerator));
+    *fontFileEnumerator = enumerator.release();
+    return S_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static SkTypeface::Style get_style(IDWriteFont* font) {
+    int style = SkTypeface::kNormal;
+    DWRITE_FONT_WEIGHT weight = font->GetWeight();
+    if (DWRITE_FONT_WEIGHT_DEMI_BOLD <= weight) {
+        style |= SkTypeface::kBold;
+    }
+    DWRITE_FONT_STYLE angle = font->GetStyle();
+    if (DWRITE_FONT_STYLE_OBLIQUE == angle || DWRITE_FONT_STYLE_ITALIC == angle) {
+        style |= SkTypeface::kItalic;
+    }
+    return static_cast<SkTypeface::Style>(style);
+}
+
+class DWriteFontTypeface : public SkTypeface {
+private:
+    DWriteFontTypeface(SkTypeface::Style style, SkFontID fontID,
+                       IDWriteFontFace* fontFace,
+                       IDWriteFont* font,
+                       IDWriteFontFamily* fontFamily,
+                       StreamFontFileLoader* fontFileLoader = NULL,
+                       IDWriteFontCollectionLoader* fontCollectionLoader = NULL)
+        : SkTypeface(style, fontID, false)
+        , fDWriteFontCollectionLoader(fontCollectionLoader)
+        , fDWriteFontFileLoader(fontFileLoader)
+        , fDWriteFontFamily(fontFamily)
+        , fDWriteFont(font)
+        , fDWriteFontFace(fontFace) {
+
+        if (fontCollectionLoader != NULL) {
+            fontCollectionLoader->AddRef();
+        }
+        if (fontFileLoader != NULL) {
+            fontFileLoader->AddRef();
+        }
+        fontFamily->AddRef();
+        font->AddRef();
+        fontFace->AddRef();
+    }
+
+public:
+    SkTScopedComPtr<IDWriteFontCollectionLoader> fDWriteFontCollectionLoader;
+    SkTScopedComPtr<StreamFontFileLoader> fDWriteFontFileLoader;
+    SkTScopedComPtr<IDWriteFontFamily> fDWriteFontFamily;
+    SkTScopedComPtr<IDWriteFont> fDWriteFont;
+    SkTScopedComPtr<IDWriteFontFace> fDWriteFontFace;
+
+    static DWriteFontTypeface* Create(IDWriteFontFace* fontFace,
+                                      IDWriteFont* font,
+                                      IDWriteFontFamily* fontFamily,
+                                      StreamFontFileLoader* fontFileLoader = NULL,
+                                      IDWriteFontCollectionLoader* fontCollectionLoader = NULL) {
+        SkTypeface::Style style = get_style(font);
+        SkFontID fontID = SkTypefaceCache::NewFontID();
+        return SkNEW_ARGS(DWriteFontTypeface, (style, fontID,
+                                               fontFace, font, fontFamily,
+                                               fontFileLoader, fontCollectionLoader));
+    }
+
+    ~DWriteFontTypeface() {
+        if (fDWriteFontCollectionLoader.get() == NULL) return;
+
+        IDWriteFactory* factory;
+        HRVM(get_dwrite_factory(&factory), "Could not get factory.");
+        HRV(factory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get()));
+        HRV(factory->UnregisterFontFileLoader(fDWriteFontFileLoader.get()));
+    }
+};
+
+class SkScalerContext_Windows : public SkScalerContext {
+public:
+    SkScalerContext_Windows(const SkDescriptor* desc);
+    virtual ~SkScalerContext_Windows();
+
+protected:
+    virtual unsigned generateGlyphCount() SK_OVERRIDE;
+    virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE;
+    virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE;
+    virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE;
+    virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE;
+    virtual void generateFontMetrics(SkPaint::FontMetrics* mX,
+                                     SkPaint::FontMetrics* mY) SK_OVERRIDE;
+
+private:
+    DWriteOffscreen fOffscreen;
+    DWRITE_MATRIX fXform;
+    SkAutoTUnref<DWriteFontTypeface> fTypeface;
+    int fGlyphCount;
+};
+
+#define SK_DWRITE_DEFAULT_FONT_NAMED 1
+#define SK_DWRITE_DEFAULT_FONT_MESSAGE 2
+#define SK_DWRITE_DEFAULT_FONT_THEME 3
+#define SK_DWRITE_DEFAULT_FONT_SHELLDLG 4
+#define SK_DWRITE_DEFAULT_FONT_GDI 5
+#define SK_DWRITE_DEFAULT_FONT_STRATEGY SK_DWRITE_DEFAULT_FONT_MESSAGE
+
+static HRESULT get_default_font(IDWriteFont** font) {
+    IDWriteFactory* factory;
+    HRM(get_dwrite_factory(&factory), "Could not get factory.");
+
+#if SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_NAMED
+    SkTScopedComPtr<IDWriteFontCollection> sysFonts;
+    HRM(factory->GetSystemFontCollection(&sysFonts, false),
+        "Could not get system font collection.");
+
+    UINT32 index;
+    BOOL exists;
+    //hr = sysFonts->FindFamilyName(L"Georgia", &index, &exists);
+    HRM(sysFonts->FindFamilyName(L"Microsoft Sans Serif", &index, &exists),
+        "Could not access family names.");
+
+    if (!exists) {
+        SkDEBUGF(("The hard coded font family does not exist."));
+        return E_UNEXPECTED;
+    }
+
+    SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+    HRM(sysFonts->GetFontFamily(index, &fontFamily),
+        "Could not load the requested font family.");
+
+    HRM(fontFamily->GetFont(0, font), "Could not get first font from family.");
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_MESSAGE
+    SkTScopedComPtr<IDWriteGdiInterop> gdi;
+    HRM(factory->GetGdiInterop(&gdi), "Could not get GDI interop.");
+
+    NONCLIENTMETRICSW metrics;
+    metrics.cbSize = sizeof(metrics);
+    if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
+                                   sizeof(metrics),
+                                   &metrics,
+                                   0)) {
+        return E_UNEXPECTED;
+    }
+    HRM(gdi->CreateFontFromLOGFONT(&metrics.lfMessageFont, font),
+        "Could not create DWrite font from LOGFONT.");
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_THEME
+    //Theme body font?
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_SHELLDLG
+    //"MS Shell Dlg" or "MS Shell Dlg 2"?
+
+#elif SK_DWRITE_DEFAULT_FONT_STRATEGY == SK_DWRITE_DEFAULT_FONT_GDI
+    //Never works.
+    SkTScopedComPtr<IDWriteGdiInterop> gdi;
+    HRM(factory->GetGdiInterop(&gdi), "Could not get GDI interop.");
+
+    static LOGFONTW gDefaultFont = {};
+    gDefaultFont.lfFaceName
+    HRM(gdi->CreateFontFromLOGFONT(&gDefaultFont, font),
+        "Could not create DWrite font from LOGFONT.";
+#endif
+    return S_OK;
+}
+
+static bool are_same(IUnknown* a, IUnknown* b) {
+    SkTScopedComPtr<IUnknown> iunkA;
+    if (FAILED(a->QueryInterface(&iunkA))) {
+        return false;
+    }
+
+    SkTScopedComPtr<IUnknown> iunkB;
+    if (FAILED(b->QueryInterface(&iunkB))) {
+        return false;
+    }
+
+    return iunkA.get() == iunkB.get();
+}
+static bool FindByDWriteFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) {
+    //Check to see if the two fonts are identical.
+    DWriteFontTypeface* dwFace = reinterpret_cast<DWriteFontTypeface*>(face);
+    IDWriteFont* dwFont = reinterpret_cast<IDWriteFont*>(ctx);
+    if (are_same(dwFace->fDWriteFont.get(), dwFont)) {
+        return true;
+    }
+
+    //Check if the two fonts share the same loader and have the same key.
+    SkTScopedComPtr<IDWriteFontFace> dwFaceFontFace;
+    SkTScopedComPtr<IDWriteFontFace> dwFontFace;
+    HRB(dwFace->fDWriteFont->CreateFontFace(&dwFaceFontFace));
+    HRB(dwFont->CreateFontFace(&dwFontFace));
+    if (are_same(dwFaceFontFace.get(), dwFontFace.get())) {
+        return true;
+    }
+
+    UINT32 dwFaceNumFiles;
+    UINT32 dwNumFiles;
+    HRB(dwFaceFontFace->GetFiles(&dwFaceNumFiles, NULL));
+    HRB(dwFontFace->GetFiles(&dwNumFiles, NULL));
+    if (dwFaceNumFiles != dwNumFiles) {
+        return false;
+    }
+
+    SkTScopedComPtr<IDWriteFontFile> dwFaceFontFile;
+    SkTScopedComPtr<IDWriteFontFile> dwFontFile;
+    HRB(dwFaceFontFace->GetFiles(&dwFaceNumFiles, &dwFaceFontFile));
+    HRB(dwFontFace->GetFiles(&dwNumFiles, &dwFontFile));
+
+    //for (each file) { //we currently only admit fonts from one file.
+    SkTScopedComPtr<IDWriteFontFileLoader> dwFaceFontFileLoader;
+    SkTScopedComPtr<IDWriteFontFileLoader> dwFontFileLoader;
+    HRB(dwFaceFontFile->GetLoader(&dwFaceFontFileLoader));
+    HRB(dwFontFile->GetLoader(&dwFontFileLoader));
+    if (!are_same(dwFaceFontFileLoader.get(), dwFontFileLoader.get())) {
+        return false;
+    }
+    //}
+
+    const void* dwFaceFontRefKey;
+    UINT32 dwFaceFontRefKeySize;
+    const void* dwFontRefKey;
+    UINT32 dwFontRefKeySize;
+    HRB(dwFaceFontFile->GetReferenceKey(&dwFaceFontRefKey, &dwFaceFontRefKeySize));
+    HRB(dwFontFile->GetReferenceKey(&dwFontRefKey, &dwFontRefKeySize));
+    if (dwFaceFontRefKeySize != dwFontRefKeySize) {
+        return false;
+    }
+    if (0 != memcmp(dwFaceFontRefKey, dwFontRefKey, dwFontRefKeySize)) {
+        return false;
+    }
+
+    //TODO: better means than comparing name strings?
+    //NOTE: .tfc and fake bold/italic will end up here.
+    SkTScopedComPtr<IDWriteFontFamily> dwFaceFontFamily;
+    SkTScopedComPtr<IDWriteFontFamily> dwFontFamily;
+    HRB(dwFace->fDWriteFont->GetFontFamily(&dwFaceFontFamily));
+    HRB(dwFont->GetFontFamily(&dwFontFamily));
+
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFaceFontFamilyNames;
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFaceFontNames;
+    HRB(dwFaceFontFamily->GetFamilyNames(&dwFaceFontFamilyNames));
+    HRB(dwFace->fDWriteFont->GetFaceNames(&dwFaceFontNames));
+
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFontFamilyNames;
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFontNames;
+    HRB(dwFontFamily->GetFamilyNames(&dwFontFamilyNames));
+    HRB(dwFont->GetFaceNames(&dwFontNames));
+
+    UINT32 dwFaceFontFamilyNameLength;
+    UINT32 dwFaceFontNameLength;
+    HRB(dwFaceFontFamilyNames->GetStringLength(0, &dwFaceFontFamilyNameLength));
+    HRB(dwFaceFontNames->GetStringLength(0, &dwFaceFontNameLength));
+
+    UINT32 dwFontFamilyNameLength;
+    UINT32 dwFontNameLength;
+    HRB(dwFontFamilyNames->GetStringLength(0, &dwFontFamilyNameLength));
+    HRB(dwFontNames->GetStringLength(0, &dwFontNameLength));
+
+    if (dwFaceFontFamilyNameLength != dwFontFamilyNameLength ||
+        dwFaceFontNameLength != dwFontNameLength)
+    {
+        return false;
+    }
+
+    SkTDArray<wchar_t> dwFaceFontFamilyNameChar(new wchar_t[dwFaceFontFamilyNameLength+1], dwFaceFontFamilyNameLength+1);
+    SkTDArray<wchar_t> dwFaceFontNameChar(new wchar_t[dwFaceFontNameLength+1], dwFaceFontNameLength+1);
+    HRB(dwFaceFontFamilyNames->GetString(0, dwFaceFontFamilyNameChar.begin(), dwFaceFontFamilyNameChar.count()));
+    HRB(dwFaceFontNames->GetString(0, dwFaceFontNameChar.begin(), dwFaceFontNameChar.count()));
+
+    SkTDArray<wchar_t> dwFontFamilyNameChar(new wchar_t[dwFontFamilyNameLength+1], dwFontFamilyNameLength+1);
+    SkTDArray<wchar_t> dwFontNameChar(new wchar_t[dwFontNameLength+1], dwFontNameLength+1);
+    HRB(dwFontFamilyNames->GetString(0, dwFontFamilyNameChar.begin(), dwFontFamilyNameChar.count()));
+    HRB(dwFontNames->GetString(0, dwFontNameChar.begin(), dwFontNameChar.count()));
+
+    return wcscmp(dwFaceFontFamilyNameChar.begin(), dwFontFamilyNameChar.begin()) == 0 &&
+           wcscmp(dwFaceFontNameChar.begin(), dwFontNameChar.begin()) == 0;
+}
+
+SkTypeface* SkCreateTypefaceFromDWriteFont(IDWriteFontFace* fontFace,
+                                           IDWriteFont* font,
+                                           IDWriteFontFamily* fontFamily,
+                                           StreamFontFileLoader* fontFileLoader = NULL,
+                                           IDWriteFontCollectionLoader* fontCollectionLoader = NULL) {
+    SkTypeface* face = SkTypefaceCache::FindByProcAndRef(FindByDWriteFont, font);
+    if (NULL == face) {
+        face = DWriteFontTypeface::Create(fontFace, font, fontFamily,
+                                          fontFileLoader, fontCollectionLoader);
+        SkTypefaceCache::Add(face, get_style(font), fontCollectionLoader != NULL);
+    }
+    return face;
+}
+
+void SkDWriteFontFromTypeface(const SkTypeface* face, IDWriteFont** font) {
+    if (NULL == face) {
+        HRVM(get_default_font(font), "Could not get default font.");
+    } else {
+        *font = static_cast<const DWriteFontTypeface*>(face)->fDWriteFont.get();
+        (*font)->AddRef();
+    }
+}
+static DWriteFontTypeface* GetDWriteFontByID(SkFontID fontID) {
+    return static_cast<DWriteFontTypeface*>(SkTypefaceCache::FindByID(fontID));
+}
+
+SkScalerContext_Windows::SkScalerContext_Windows(const SkDescriptor* desc)
+        : SkScalerContext(desc)
+        , fGlyphCount(-1) {
+    SkAutoMutexAcquire ac(gFTMutex);
+
+    fXform.m11 = SkScalarToFloat(fRec.fPost2x2[0][0]);
+    fXform.m12 = SkScalarToFloat(fRec.fPost2x2[1][0]);
+    fXform.m21 = SkScalarToFloat(fRec.fPost2x2[0][1]);
+    fXform.m22 = SkScalarToFloat(fRec.fPost2x2[1][1]);
+    fXform.dx = 0;
+    fXform.dy = 0;
+
+    fTypeface.reset(GetDWriteFontByID(fRec.fFontID));
+    fTypeface.get()->ref();
+
+    fOffscreen.init(fTypeface->fDWriteFontFace.get(), fXform, SkScalarToFloat(fRec.fTextSize));
+}
+
+SkScalerContext_Windows::~SkScalerContext_Windows() {
+}
+
+unsigned SkScalerContext_Windows::generateGlyphCount() {
+    if (fGlyphCount < 0) {
+        fGlyphCount = fTypeface->fDWriteFontFace->GetGlyphCount();
+    }
+    return fGlyphCount;
+}
+
+uint16_t SkScalerContext_Windows::generateCharToGlyph(SkUnichar uni) {
+    uint16_t index = 0;
+    fTypeface->fDWriteFontFace->GetGlyphIndices(reinterpret_cast<UINT32*>(&uni), 1, &index);
+    return index;
+}
+
+void SkScalerContext_Windows::generateAdvance(SkGlyph* glyph) {
+    //Delta is the difference between the right/left side bearing metric
+    //and where the right/left side bearing ends up after hinting.
+    //DirectWrite does not provide this information.
+    glyph->fRsbDelta = 0;
+    glyph->fLsbDelta = 0;
+
+    glyph->fAdvanceX = 0;
+    glyph->fAdvanceY = 0;
+
+    uint16_t glyphId = glyph->getGlyphID();
+    DWRITE_GLYPH_METRICS gm;
+    HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm),
+         "Could not get design metrics.");
+
+    DWRITE_FONT_METRICS dwfm;
+    fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
+
+    SkScalar advanceX = SkScalarMulDiv(fRec.fTextSize,
+                                       SkIntToScalar(gm.advanceWidth),
+                                       SkIntToScalar(dwfm.designUnitsPerEm));
+
+    if (!(fRec.fFlags & kSubpixelPositioning_Flag)) {
+        advanceX = SkScalarRoundToScalar(advanceX);
+    }
+
+    SkVector vecs[1] = { { advanceX, 0 } };
+    SkMatrix mat;
+    fRec.getMatrixFrom2x2(&mat);
+    mat.mapVectors(vecs, SK_ARRAY_COUNT(vecs));
+
+    glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX);
+    glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY);
+}
+
+void SkScalerContext_Windows::generateMetrics(SkGlyph* glyph) {
+    glyph->fWidth = 0;
+
+    this->generateAdvance(glyph);
+
+    //Measure raster size.
+    fXform.dx = SkFixedToFloat(glyph->getSubXFixed());
+    fXform.dy = SkFixedToFloat(glyph->getSubYFixed());
+
+    FLOAT advance = 0;
+
+    UINT16 glyphId = glyph->getGlyphID();
+
+    DWRITE_GLYPH_OFFSET offset;
+    offset.advanceOffset = 0.0f;
+    offset.ascenderOffset = 0.0f;
+
+    DWRITE_GLYPH_RUN run;
+    run.glyphCount = 1;
+    run.glyphAdvances = &advance;
+    run.fontFace = fTypeface->fDWriteFontFace.get();
+    run.fontEmSize = SkScalarToFloat(fRec.fTextSize);
+    run.bidiLevel = 0;
+    run.glyphIndices = &glyphId;
+    run.isSideways = FALSE;
+    run.glyphOffsets = &offset;
+
+    IDWriteFactory* factory;
+    HRVM(get_dwrite_factory(&factory), "Could not get factory.");
+
+    const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
+    DWRITE_RENDERING_MODE renderingMode;
+    DWRITE_TEXTURE_TYPE textureType;
+    if (isBW) {
+        renderingMode = DWRITE_RENDERING_MODE_ALIASED;
+        textureType = DWRITE_TEXTURE_ALIASED_1x1;
+    } else {
+        renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
+        textureType = DWRITE_TEXTURE_CLEARTYPE_3x1;
+    }
+
+    SkTScopedComPtr<IDWriteGlyphRunAnalysis> glyphRunAnalysis;
+    HRVM(factory->CreateGlyphRunAnalysis(&run,
+                                         1.0f, // pixelsPerDip,
+                                         &fXform,
+                                         renderingMode,
+                                         DWRITE_MEASURING_MODE_NATURAL,
+                                         0.0f, // baselineOriginX,
+                                         0.0f, // baselineOriginY,
+                                         &glyphRunAnalysis),
+         "Could not create glyph run analysis.");
+
+    RECT bbox;
+    HRVM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, &bbox),
+         "Could not get texture bounds.");
+
+    glyph->fWidth = SkToU16(bbox.right - bbox.left);
+    glyph->fHeight = SkToU16(bbox.bottom - bbox.top);
+    glyph->fLeft = SkToS16(bbox.left);
+    glyph->fTop = SkToS16(bbox.top);
+}
+
+void SkScalerContext_Windows::generateFontMetrics(SkPaint::FontMetrics* mx,
+                                                  SkPaint::FontMetrics* my) {
+    if (!(mx || my))
+      return;
+
+    DWRITE_FONT_METRICS dwfm;
+    fTypeface->fDWriteFontFace->GetMetrics(&dwfm);
+
+    if (mx) {
+        mx->fTop = SkScalarMulDiv(-fRec.fTextSize,
+                                  SkIntToScalar(dwfm.ascent),
+                                  SkIntToScalar(dwfm.designUnitsPerEm));
+        mx->fAscent = mx->fTop;
+        mx->fDescent = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.descent),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+        mx->fBottom = mx->fDescent;
+        //TODO, can be less than zero
+        mx->fLeading = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.lineGap),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+    }
+
+    if (my) {
+        my->fTop = SkScalarMulDiv(-fRec.fTextSize,
+                                  SkIntToScalar(dwfm.ascent),
+                                  SkIntToScalar(dwfm.designUnitsPerEm));
+        my->fAscent = my->fTop;
+        my->fDescent = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.descent),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+        my->fBottom = my->fDescent;
+        //TODO, can be less than zero
+        my->fLeading = SkScalarMulDiv(fRec.fTextSize,
+                                      SkIntToScalar(dwfm.lineGap),
+                                      SkIntToScalar(dwfm.designUnitsPerEm));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkColorPriv.h"
+
+static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) {
+    const int width = glyph.fWidth;
+    const size_t dstRB = (width + 7) >> 3;
+    uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
+
+    int byteCount = width >> 3;
+    int bitCount = width & 7;
+
+    for (int y = 0; y < glyph.fHeight; ++y) {
+        if (byteCount > 0) {
+            for (int i = 0; i < byteCount; ++i) {
+                unsigned byte = 0;
+                byte |= src[0] & (1 << 7);
+                byte |= src[1] & (1 << 6);
+                byte |= src[2] & (1 << 5);
+                byte |= src[3] & (1 << 4);
+                byte |= src[4] & (1 << 3);
+                byte |= src[5] & (1 << 2);
+                byte |= src[6] & (1 << 1);
+                byte |= src[7] & (1 << 0);
+                dst[i] = byte;
+                src += 8;
+            }
+        }
+        if (bitCount > 0) {
+            unsigned byte = 0;
+            unsigned mask = 0x80;
+            for (int i = 0; i < bitCount; i++) {
+                byte |= (src[i]) & mask;
+                mask >>= 1;
+            }
+            dst[byteCount] = byte;
+        }
+        src += bitCount;
+        dst += dstRB;
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) {
+    const size_t dstRB = glyph.rowBytes();
+    const U16CPU width = glyph.fWidth;
+    uint8_t* SK_RESTRICT dst = static_cast<uint8_t*>(glyph.fImage);
+
+    for (U16CPU y = 0; y < glyph.fHeight; y++) {
+        for (U16CPU i = 0; i < width; i++) {
+            U8CPU r = *(src++);
+            U8CPU g = *(src++);
+            U8CPU b = *(src++);
+            dst[i] = sk_apply_lut_if<APPLY_PREBLEND>((r + g + b) / 3, table8);
+        }
+        dst = (uint8_t*)((char*)dst + dstRB);
+    }
+}
+
+template<bool APPLY_PREBLEND>
+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();
+    const U16CPU width = glyph.fWidth;
+    uint16_t* SK_RESTRICT dst = static_cast<uint16_t*>(glyph.fImage);
+
+    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);
+            dst[i] = SkPack888ToRGB16(r, g, b);
+        }
+        dst = (uint16_t*)((char*)dst + dstRB);
+    }
+}
+
+template<bool APPLY_PREBLEND>
+static void rgb_to_lcd32(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();
+    const U16CPU width = glyph.fWidth;
+    SkPMColor* SK_RESTRICT dst = static_cast<SkPMColor*>(glyph.fImage);
+
+    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);
+            dst[i] = SkPackARGB32(0xFF, r, g, b);
+        }
+        dst = (SkPMColor*)((char*)dst + dstRB);
+    }
+}
+
+void SkScalerContext_Windows::generateImage(const SkGlyph& glyph) {
+    SkAutoMutexAcquire ac(gFTMutex);
+
+    const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat;
+    const bool isAA = !isLCD(fRec);
+
+    //Create the mask.
+    const void* bits = fOffscreen.draw(glyph, isBW);
+    if (!bits) {
+        sk_bzero(glyph.fImage, glyph.computeImageSize());
+        return;
+    }
+
+    //Copy the mask into the glyph.
+    int width = glyph.fWidth;
+    size_t dstRB = glyph.rowBytes();
+    const uint8_t* src = (const uint8_t*)bits;
+    if (isBW) {
+        bilevel_to_bw(src, glyph);
+    } else if (isAA) {
+        if (fPreBlend.isApplicable()) {
+            rgb_to_a8<true>(src, glyph, fPreBlend.fG);
+        } else {
+            rgb_to_a8<false>(src, glyph, fPreBlend.fG);
+        }
+    } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) {
+        if (fPreBlend.isApplicable()) {
+            rgb_to_lcd16<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+        } else {
+            rgb_to_lcd16<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+        }
+    } else {
+        SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat);
+        if (fPreBlend.isApplicable()) {
+            rgb_to_lcd32<true>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+        } else {
+            rgb_to_lcd32<false>(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB);
+        }
+    }
+}
+
+void SkScalerContext_Windows::generatePath(const SkGlyph& glyph, SkPath* path) {
+    SkAutoMutexAcquire ac(gFTMutex);
+
+    SkASSERT(&glyph && path);
+
+    path->reset();
+
+    SkTScopedComPtr<IDWriteGeometrySink> geometryToPath;
+    HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath),
+         "Could not create geometry to path converter.");
+    uint16_t glyphId = glyph.getGlyphID();
+    //TODO: convert to<->from DIUs? This would make a difference if hinting.
+    //It may not be needed, it appears that DirectWrite only hints at em size.
+    HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fRec.fTextSize),
+                                       &glyphId,
+                                       NULL, //advances
+                                       NULL, //offsets
+                                       1, //num glyphs
+                                       FALSE, //sideways
+                                       FALSE, //rtl
+                                       geometryToPath.get()),
+         "Could not create glyph outline.");
+
+    SkMatrix mat;
+    fRec.getMatrixFrom2x2(&mat);
+    path->transform(mat);
+}
+
+void SkFontHost::Serialize(const SkTypeface* rawFace, SkWStream* stream) {
+    const DWriteFontTypeface* face = static_cast<const DWriteFontTypeface*>(rawFace);
+    SkFontDescriptor descriptor(face->style());
+
+    // Get the family name.
+    SkTScopedComPtr<IDWriteLocalizedStrings> dwFamilyNames;
+    HRV(face->fDWriteFontFamily->GetFamilyNames(&dwFamilyNames));
+
+    UINT32 dwFamilyNamesLength;
+    HRV(dwFamilyNames->GetStringLength(0, &dwFamilyNamesLength));
+
+    SkTDArray<wchar_t> dwFamilyNameChar(new wchar_t[dwFamilyNamesLength+1], dwFamilyNamesLength+1);
+    HRV(dwFamilyNames->GetString(0, dwFamilyNameChar.begin(), dwFamilyNameChar.count()));
+
+    // Convert the family name to utf8.
+    // Get the buffer size needed first.
+    int str_len = WideCharToMultiByte(CP_UTF8, 0, dwFamilyNameChar.begin(), -1,
+                                      NULL, 0, NULL, NULL);
+    // Allocate a buffer (str_len already has terminating null accounted for).
+    SkTDArray<char> utf8FamilyName(new char[str_len], str_len);
+    // Now actually convert the string.
+    str_len = WideCharToMultiByte(CP_UTF8, 0, dwFamilyNameChar.begin(), -1,
+                                  utf8FamilyName.begin(), str_len, NULL, NULL);
+
+    descriptor.setFamilyName(utf8FamilyName.begin());
+    //TODO: FileName and PostScriptName currently unsupported.
+
+    descriptor.serialize(stream);
+
+    if (NULL != face->fDWriteFontFileLoader.get()) {
+        // store the entire font in the fontData
+        SkStream* fontStream = face->fDWriteFontFileLoader->fStream.get();
+        const uint32_t length = fontStream->getLength();
+
+        stream->writePackedUInt(length);
+        stream->writeStream(fontStream, length);
+    } else {
+        stream->writePackedUInt(0);
+    }
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    SkFontDescriptor descriptor(stream);
+
+    const uint32_t customFontDataLength = stream->readPackedUInt();
+    if (customFontDataLength > 0) {
+        // generate a new stream to store the custom typeface
+        SkAutoTUnref<SkMemoryStream> fontStream(SkNEW_ARGS(SkMemoryStream, (customFontDataLength - 1)));
+        stream->read((void*)fontStream->getMemoryBase(), customFontDataLength - 1);
+
+        return CreateTypefaceFromStream(fontStream.get());
+    }
+
+    return SkFontHost::CreateTypeface(NULL, descriptor.getFamilyName(), descriptor.getStyle());
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    IDWriteFactory* factory;
+    HRN(get_dwrite_factory(&factory));
+
+    SkTScopedComPtr<StreamFontFileLoader> fontFileLoader;
+    HRN(StreamFontFileLoader::Create(stream, &fontFileLoader));
+    HRN(factory->RegisterFontFileLoader(fontFileLoader.get()));
+
+    SkTScopedComPtr<StreamFontCollectionLoader> streamFontCollectionLoader;
+    HRN(StreamFontCollectionLoader::Create(fontFileLoader.get(), &streamFontCollectionLoader));
+    HRN(factory->RegisterFontCollectionLoader(streamFontCollectionLoader.get()));
+
+    SkTScopedComPtr<IDWriteFontCollection> streamFontCollection;
+    HRN(factory->CreateCustomFontCollection(streamFontCollectionLoader.get(), NULL, 0,
+                                            &streamFontCollection));
+
+    SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+    HRN(streamFontCollection->GetFontFamily(0, &fontFamily));
+
+    SkTScopedComPtr<IDWriteFont> font;
+    HRN(fontFamily->GetFont(0, &font));
+
+    SkTScopedComPtr<IDWriteFontFace> fontFace;
+    HRN(font->CreateFontFace(&fontFace));
+
+    return SkCreateTypefaceFromDWriteFont(fontFace.get(), font.get(), fontFamily.get(),
+                                          fontFileLoader.get(), streamFontCollectionLoader.get());
+}
+
+SkStream* SkFontHost::OpenStream(SkFontID uniqueID) {
+    DWriteFontTypeface* typeface = GetDWriteFontByID(uniqueID);
+    if (NULL == typeface) {
+        return NULL;
+    }
+
+    UINT32 numFiles;
+    HRNM(typeface->fDWriteFontFace->GetFiles(&numFiles, NULL),
+         "Could not get number of font files.");
+    if (numFiles != 1) {
+        return NULL;
+    }
+
+    SkTScopedComPtr<IDWriteFontFile> fontFile;
+    HRNM(typeface->fDWriteFontFace->GetFiles(&numFiles, &fontFile), "Could not get font files.");
+
+    const void* fontFileKey;
+    UINT32 fontFileKeySize;
+    HRNM(fontFile->GetReferenceKey(&fontFileKey, &fontFileKeySize),
+         "Could not get font file reference key.");
+
+    SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
+    HRNM(fontFile->GetLoader(&fontFileLoader), "Could not get font file loader.");
+
+    SkTScopedComPtr<IDWriteFontFileStream> fontFileStream;
+    HRNM(fontFileLoader->CreateStreamFromKey(fontFileKey, fontFileKeySize,
+                                             &fontFileStream),
+         "Could not create font file stream.");
+
+    return SkNEW_ARGS(SkDWriteFontFileStream, (fontFileStream.get()));
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    return SkNEW_ARGS(SkScalerContext_Windows, (desc));
+}
+
+static HRESULT get_by_family_name(const char familyName[], IDWriteFontFamily** fontFamily) {
+    IDWriteFactory* factory;
+    HR(get_dwrite_factory(&factory));
+
+    SkTScopedComPtr<IDWriteFontCollection> sysFontCollection;
+    HR(factory->GetSystemFontCollection(&sysFontCollection, FALSE));
+
+    // Get the buffer size needed first.
+    int wlen = ::MultiByteToWideChar(CP_UTF8, 0, familyName,-1, NULL, 0);
+    if (0 == wlen) {
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+    // Allocate a buffer
+    SkTDArray<wchar_t> wideFamilyName(new wchar_t[wlen], wlen);
+    // Now actually convert the string.
+    wlen = ::MultiByteToWideChar(CP_UTF8, 0, familyName, -1,
+                                    wideFamilyName.begin(), wlen);
+    if (0 == wlen) {
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+
+    UINT32 index;
+    BOOL exists;
+    HR(sysFontCollection->FindFamilyName(wideFamilyName.begin(), &index, &exists));
+
+    if (exists) {
+        HR(sysFontCollection->GetFontFamily(index, fontFamily));
+        return S_OK;
+    }
+    return S_FALSE;
+}
+
+/** Return the closest matching typeface given either an existing family
+ (specified by a typeface in that family) or by a familyName, and a
+ requested style.
+ 1) If familyFace is null, use familyName.
+ 2) If familyName is null, use familyFace.
+ 3) If both are null, return the default font that best matches style
+ This MUST not return NULL.
+ */
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                       const char familyName[],
+                                       SkTypeface::Style style) {
+    HRESULT hr;
+    SkTScopedComPtr<IDWriteFontFamily> fontFamily;
+    SkTScopedComPtr<IDWriteFontCollectionLoader> fontCollectionLoader;
+    SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
+    if (familyFace) {
+        const DWriteFontTypeface* face = static_cast<const DWriteFontTypeface*>(familyFace);
+        face->fDWriteFontFamily.get()->AddRef();
+        *(&fontFamily) = face->fDWriteFontFamily.get();
+
+        if (face->fDWriteFontCollectionLoader.get() != NULL) {
+            face->fDWriteFontCollectionLoader.get()->AddRef();
+            *(&fontCollectionLoader) = face->fDWriteFontCollectionLoader.get();
+
+            face->fDWriteFontFileLoader.get()->AddRef();
+            *(&fontFileLoader) = face->fDWriteFontFileLoader.get();
+        }
+
+    } else if (familyName) {
+        hr = get_by_family_name(familyName, &fontFamily);
+    }
+
+    if (NULL == fontFamily.get()) {
+        //No good family found, go with default.
+        SkTScopedComPtr<IDWriteFont> font;
+        hr = get_default_font(&font);
+        hr = font->GetFontFamily(&fontFamily);
+    }
+
+    SkTScopedComPtr<IDWriteFont> font;
+    DWRITE_FONT_WEIGHT weight = (style & SkTypeface::kBold)
+                                 ? DWRITE_FONT_WEIGHT_BOLD
+                                 : DWRITE_FONT_WEIGHT_NORMAL;
+    DWRITE_FONT_STRETCH stretch = DWRITE_FONT_STRETCH_UNDEFINED;
+    DWRITE_FONT_STYLE italic = (style & SkTypeface::kItalic)
+                                ? DWRITE_FONT_STYLE_ITALIC
+                                : DWRITE_FONT_STYLE_NORMAL;
+    hr = fontFamily->GetFirstMatchingFont(weight, stretch, italic, &font);
+
+    SkTScopedComPtr<IDWriteFontFace> fontFace;
+    hr = font->CreateFontFace(&fontFace);
+
+    return SkCreateTypefaceFromDWriteFont(fontFace.get(), font.get(), fontFamily.get());
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(const char path[]) {
+    printf("SkFontHost::CreateTypefaceFromFile unimplemented");
+    return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
+    unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag |
+                                  SkScalerContext::kAutohinting_Flag |
+                                  SkScalerContext::kEmbeddedBitmapText_Flag |
+                                  SkScalerContext::kEmbolden_Flag |
+                                  SkScalerContext::kLCD_BGROrder_Flag |
+                                  SkScalerContext::kLCD_Vertical_Flag;
+    rec->fFlags &= ~flagsWeDontSupport;
+
+    SkPaint::Hinting h = rec->getHinting();
+    // DirectWrite does not provide for hinting hints.
+    h = SkPaint::kSlight_Hinting;
+    rec->setHinting(h);
+
+#if SK_FONT_HOST_USE_SYSTEM_SETTINGS
+    IDWriteFactory* factory;
+    if (SUCCEEDED(get_dwrite_factory(&factory))) {
+        SkTScopedComPtr<IDWriteRenderingParams> defaultRenderingParams;
+        if (SUCCEEDED(factory->CreateRenderingParams(&defaultRenderingParams))) {
+            float gamma = defaultRenderingParams->GetGamma();
+            rec->setDeviceGamma(SkFloatToScalar(gamma));
+            rec->setPaintGamma(SkFloatToScalar(gamma));
+
+            rec->setContrast(SkFloatToScalar(defaultRenderingParams->GetEnhancedContrast()));
+        }
+    }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//PDF Support
+
+using namespace skia_advanced_typeface_metrics_utils;
+
+// Construct Glyph to Unicode table.
+// Unicode code points that require conjugate pairs in utf16 are not
+// supported.
+// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may
+// require parsing the TTF cmap table (platform 4, encoding 12) directly instead
+// of calling GetFontUnicodeRange().
+// TODO(bungeman): This never does what anyone wants.
+// What is really wanted is the text to glyphs mapping
+static void populate_glyph_to_unicode(IDWriteFontFace* fontFace,
+                                      const unsigned glyphCount,
+                                      SkTDArray<SkUnichar>* glyphToUnicode) {
+    HRESULT hr = S_OK;
+
+    //Do this like free type instead
+    UINT32 count = 0;
+    for (UINT32 c = 0; c < 0x10FFFF; ++c) {
+        UINT16 glyph;
+        hr = fontFace->GetGlyphIndices(&c, 1, &glyph);
+        if (glyph > 0) {
+            ++count;
+        }
+    }
+
+    SkAutoTArray<UINT32> chars(count);
+    count = 0;
+    for (UINT32 c = 0; c < 0x10FFFF; ++c) {
+        UINT16 glyph;
+        hr = fontFace->GetGlyphIndices(&c, 1, &glyph);
+        if (glyph > 0) {
+            chars[count] = c;
+            ++count;
+        }
+    }
+
+    SkAutoTArray<UINT16> glyph(count);
+    fontFace->GetGlyphIndices(chars.get(), count, glyph.get());
+
+    USHORT maxGlyph = 0;
+    for (USHORT j = 0; j < count; ++j) {
+        if (glyph[j] > maxGlyph) maxGlyph = glyph[j];
+    }
+
+    glyphToUnicode->setCount(maxGlyph+1);
+    for (size_t j = 0; j < maxGlyph+1u; ++j) {
+        (*glyphToUnicode)[j] = 0;
+    }
+
+    //'invert'
+    for (USHORT j = 0; j < count; ++j) {
+        if (glyph[j] < glyphCount && (*glyphToUnicode)[glyph[j]] == 0) {
+            (*glyphToUnicode)[glyph[j]] = chars[j];
+        }
+    }
+}
+
+static bool getWidthAdvance(IDWriteFontFace* fontFace, int gId, int16_t* advance) {
+    SkASSERT(advance);
+
+    UINT16 glyphId = gId;
+    DWRITE_GLYPH_METRICS gm;
+    HRESULT hr = fontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm);
+
+    if (FAILED(hr)) {
+        *advance = 0;
+        return false;
+    }
+
+    *advance = gm.advanceWidth;
+    return true;
+}
+
+template<typename T>
+class AutoDWriteTable {
+public:
+    AutoDWriteTable(IDWriteFontFace* fontFace)
+        : fFontFace(fontFace)
+        , fExists(FALSE) {
+
+        //fontFace->AddRef();
+        const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0,
+                                                    T::TAG1,
+                                                    T::TAG2,
+                                                    T::TAG3);
+        HRESULT hr = fontFace->TryGetFontTable(tag,
+            reinterpret_cast<const void **>(&fData), &fSize, &fLock, &fExists);
+    }
+    ~AutoDWriteTable() {
+        if (fExists) {
+            fFontFace->ReleaseFontTable(fLock);
+        }
+    }
+    const T* operator->() const { return fData; }
+
+    const T* fData;
+    UINT32 fSize;
+    BOOL fExists;
+private:
+    //SkTScopedComPtr<IDWriteFontFace> fFontFace;
+    IDWriteFontFace* fFontFace;
+    void* fLock;
+};
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+        uint32_t fontID,
+        SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo,
+        const uint32_t* glyphIDs,
+        uint32_t glyphIDsCount) {
+
+    SkAdvancedTypefaceMetrics* info = NULL;
+
+    HRESULT hr = S_OK;
+
+    DWriteFontTypeface* typeface = GetDWriteFontByID(fontID);
+
+    const unsigned glyphCount = typeface->fDWriteFontFace->GetGlyphCount();
+
+    DWRITE_FONT_METRICS dwfm;
+    typeface->fDWriteFontFace->GetMetrics(&dwfm);
+
+    info = new SkAdvancedTypefaceMetrics;
+    info->fEmSize = dwfm.designUnitsPerEm;
+    info->fMultiMaster = false;
+    info->fLastGlyphID = SkToU16(glyphCount - 1);
+    info->fStyle = 0;
+
+
+    SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
+    SkTScopedComPtr<IDWriteLocalizedStrings> faceNames;
+    hr = typeface->fDWriteFontFamily->GetFamilyNames(&familyNames);
+    hr = typeface->fDWriteFont->GetFaceNames(&faceNames);
+
+    UINT32 familyNameLength;
+    hr = familyNames->GetStringLength(0, &familyNameLength);
+
+    UINT32 faceNameLength;
+    hr = faceNames->GetStringLength(0, &faceNameLength);
+
+    size_t size = familyNameLength+1+faceNameLength+1;
+    SkTDArray<wchar_t> wFamilyName(new wchar_t[size], size);
+    hr = familyNames->GetString(0, wFamilyName.begin(), size);
+    wFamilyName[familyNameLength] = L' ';
+    hr = faceNames->GetString(0, &wFamilyName[familyNameLength+1], size - faceNameLength + 1);
+
+    size_t str_len = WideCharToMultiByte(CP_UTF8, 0, wFamilyName.begin(), -1, NULL, 0, NULL, NULL);
+    if (0 == str_len) {
+        //TODO: error
+    }
+    SkTDArray<char> familyName(new char[str_len], str_len);
+    str_len = WideCharToMultiByte(CP_UTF8, 0, wFamilyName.begin(), -1, familyName.begin(), str_len, NULL, NULL);
+    if (0 == str_len) {
+        //TODO: error
+    }
+    info->fFontName.set(familyName.begin(), str_len);
+
+    if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) {
+        populate_glyph_to_unicode(typeface->fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode));
+    }
+
+    DWRITE_FONT_FACE_TYPE fontType = typeface->fDWriteFontFace->GetType();
+    if (fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE ||
+        fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) {
+        info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font;
+    } else {
+        info->fType = SkAdvancedTypefaceMetrics::kOther_Font;
+        info->fItalicAngle = 0;
+        info->fAscent = dwfm.ascent;;
+        info->fDescent = dwfm.descent;
+        info->fStemV = 0;
+        info->fCapHeight = dwfm.capHeight;
+        info->fBBox = SkIRect::MakeEmpty();
+        return info;
+    }
+
+    AutoDWriteTable<SkOTTableHead> headTable(typeface->fDWriteFontFace.get());
+    AutoDWriteTable<SkOTTablePostScript> postTable(typeface->fDWriteFontFace.get());
+    AutoDWriteTable<SkOTTableHorizontalHeader> hheaTable(typeface->fDWriteFontFace.get());
+    AutoDWriteTable<SkOTTableOS2> os2Table(typeface->fDWriteFontFace.get());
+    if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) {
+        info->fItalicAngle = 0;
+        info->fAscent = dwfm.ascent;;
+        info->fDescent = dwfm.descent;
+        info->fStemV = 0;
+        info->fCapHeight = dwfm.capHeight;
+        info->fBBox = SkIRect::MakeEmpty();
+        return info;
+    }
+
+    //There exist CJK fonts which set the IsFixedPitch and Monospace bits,
+    //but have full width, latin half-width, and half-width kana.
+    bool fixedWidth = (postTable->isFixedPitch &&
+                      (1 == SkEndian_SwapBE16(hheaTable->numberOfHMetrics)));
+    //Monospace
+    if (fixedWidth) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style;
+    }
+    //Italic
+    if (os2Table->version.v0.fsSelection.field.Italic) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style;
+    }
+    //Symbolic (uses more than base latin).
+    info->fStyle |= SkAdvancedTypefaceMetrics::kSymbolic_Style;
+    //Script
+    if (SkPanose::FamilyType::Script == os2Table->version.v0.panose.bFamilyType.value) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style;
+    //Serif
+    } else if (SkPanose::FamilyType::TextAndDisplay == os2Table->version.v0.panose.bFamilyType.value &&
+               SkPanose::Data::TextAndDisplay::SerifStyle::Triangle <= os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value &&
+               SkPanose::Data::TextAndDisplay::SerifStyle::NoFit != os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value) {
+        info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style;
+    }
+
+    info->fItalicAngle = SkEndian_SwapBE32(postTable->italicAngle) >> 16;
+
+    info->fAscent = SkToS16(dwfm.ascent);
+    info->fDescent = SkToS16(dwfm.descent);
+    info->fCapHeight = SkToS16(dwfm.capHeight);
+
+    info->fBBox = SkIRect::MakeLTRB((int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMin),
+                                    (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMax),
+                                    (int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMax),
+                                    (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin));
+
+    //TODO: is this even desired? It seems PDF only wants this value for Type1
+    //fonts, and we only get here for TrueType fonts.
+    info->fStemV = 0;
+    /*
+    // Figure out a good guess for StemV - Min width of i, I, !, 1.
+    // This probably isn't very good with an italic font.
+    int16_t min_width = SHRT_MAX;
+    info->fStemV = 0;
+    char stem_chars[] = {'i', 'I', '!', '1'};
+    for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) {
+        ABC abcWidths;
+        if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) {
+            int16_t width = abcWidths.abcB;
+            if (width > 0 && width < min_width) {
+                min_width = width;
+                info->fStemV = min_width;
+            }
+        }
+    }
+    */
+
+    // If Restricted, the font may not be embedded in a document.
+    // If not Restricted, the font can be embedded.
+    // If PreviewPrint, the embedding is read-only.
+    if (os2Table->version.v0.fsType.field.Restricted) {
+        info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font;
+    } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) {
+        if (fixedWidth) {
+            appendRange(&info->fGlyphWidths, 0);
+            int16_t advance;
+            getWidthAdvance(typeface->fDWriteFontFace.get(), 1, &advance);
+            info->fGlyphWidths->fAdvance.append(1, &advance);
+            finishRange(info->fGlyphWidths.get(), 0,
+                        SkAdvancedTypefaceMetrics::WidthRange::kDefault);
+        } else {
+            info->fGlyphWidths.reset(
+                getAdvanceData(typeface->fDWriteFontFace.get(),
+                               glyphCount,
+                               glyphIDs,
+                               glyphIDsCount,
+                               getWidthAdvance));
+        }
+    }
+
+    return info;
+}
diff --git a/src/ports/SkGlobalInitialization_chromium.cpp b/src/ports/SkGlobalInitialization_chromium.cpp
index 6a7b213..d05af70 100644
--- a/src/ports/SkGlobalInitialization_chromium.cpp
+++ b/src/ports/SkGlobalInitialization_chromium.cpp
@@ -15,19 +15,20 @@
 #include "SkLayerDrawLooper.h"
 #include "SkMallocPixelRef.h"
 #include "SkXfermode.h"
+#include "SkMagnifierImageFilter.h"
 
 void SkFlattenable::InitializeFlattenables() {
-    SkBitmapProcShader::Init();
-    SkBlurImageFilter::Init();
-    SkBlurMaskFilter::Init();
-    SkColorFilter::Init();
-    SkCornerPathEffect::Init();
-    SkDashPathEffect::Init();
-    SkGradientShader::Init();
-    SkLayerDrawLooper::Init();
-    SkXfermode::Init();
-}
 
-void SkPixelRef::InitializeFlattenables() {
-    SkMallocPixelRef::Init();
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMallocPixelRef)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter)
+
+    SkBlurMaskFilter::InitializeFlattenables();
+    SkColorFilter::InitializeFlattenables();
+    SkGradientShader::InitializeFlattenables();
+    SkXfermode::InitializeFlattenables();
 }
diff --git a/src/ports/SkGlobalInitialization_default.cpp b/src/ports/SkGlobalInitialization_default.cpp
index 6be776a..26a61ca 100644
--- a/src/ports/SkGlobalInitialization_default.cpp
+++ b/src/ports/SkGlobalInitialization_default.cpp
@@ -4,34 +4,98 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
- 
+
 #include "SkTypes.h"
 
-#if !SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
-
 #include "SkBitmapProcShader.h"
-#include "SkEffects.h"
-#include "SkFlipPixelRef.h"
-#include "SkImageRef_ashmem.h"
-#include "SkImageRef_GlobalPool.h"
 #include "SkMallocPixelRef.h"
 #include "SkPathEffect.h"
 #include "SkPixelRef.h"
-#include "SkShape.h"
 #include "SkXfermode.h"
 
+#include "Sk1DPathEffect.h"
+#include "Sk2DPathEffect.h"
+#include "SkAvoidXfermode.h"
+#include "SkBicubicImageFilter.h"
+#include "SkBitmapSource.h"
+#include "SkBlendImageFilter.h"
+#include "SkBlurDrawLooper.h"
+#include "SkBlurImageFilter.h"
+#include "SkBlurMaskFilter.h"
+#include "SkColorFilter.h"
+#include "SkColorFilterImageFilter.h"
+#include "SkColorMatrixFilter.h"
+#include "SkColorShader.h"
+#include "SkColorTable.h"
+#include "SkComposeShader.h"
+#include "SkCornerPathEffect.h"
+#include "SkDashPathEffect.h"
+#include "SkDiscretePathEffect.h"
+#include "SkDisplacementMapEffect.h"
+#include "SkEmptyShader.h"
+#include "SkEmbossMaskFilter.h"
+#include "SkFlattenable.h"
+#include "SkGradientShader.h"
+#include "SkImages.h"
+#include "SkLayerDrawLooper.h"
+#include "SkLayerRasterizer.h"
+#include "SkLightingImageFilter.h"
+#include "SkMagnifierImageFilter.h"
+#include "SkMatrixConvolutionImageFilter.h"
+#include "SkMergeImageFilter.h"
+#include "SkMorphologyImageFilter.h"
+#include "SkOffsetImageFilter.h"
+#include "SkPixelXorXfermode.h"
+#include "SkStippleMaskFilter.h"
+#include "SkTableColorFilter.h"
+#include "SkTestImageFilters.h"
+
 void SkFlattenable::InitializeFlattenables() {
-    SkBitmapProcShader::Init();
-    SkEffects::Init();
-    SkPathEffect::Init();
-    SkShape::Init();
-    SkXfermode::Init();
-}
 
-void SkPixelRef::InitializeFlattenables() {
-    SkFlipPixelRef::Init();
-    SkImageRef_GlobalPool::Init();
-    SkMallocPixelRef::Init();
-}
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkAvoidXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBicubicImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapProcShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBitmapSource)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlendImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurDrawLooper)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkBlurImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorMatrixFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorTable)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposePathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkCornerPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDashPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDilateImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDiscretePathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDisplacementMapEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmbossMaskFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkEmptyShader)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkErodeImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerDrawLooper)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLayerRasterizer)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath1DPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(Sk2DPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkLine2DPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPath2DPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkPixelXorXfermode)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkStippleMaskFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkSumPathEffect)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMagnifierImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMatrixConvolutionImageFilter)
 
-#endif
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkOffsetImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkComposeImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMergeImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkColorFilterImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkDownSampleImageFilter)
+    SK_DEFINE_FLATTENABLE_REGISTRAR_ENTRY(SkMallocPixelRef)
+
+    SkBlurMaskFilter::InitializeFlattenables();
+    SkColorFilter::InitializeFlattenables();
+    SkGradientShader::InitializeFlattenables();
+    SkImages::InitializeFlattenables();
+    SkLightingImageFilter::InitializeFlattenables();
+    SkTableColorFilter::InitializeFlattenables();
+    SkXfermode::InitializeFlattenables();
+}
diff --git a/src/ports/SkHarfBuzzFont.cpp b/src/ports/SkHarfBuzzFont.cpp
index 12f37f5..b5cd0f7 100644
--- a/src/ports/SkHarfBuzzFont.cpp
+++ b/src/ports/SkHarfBuzzFont.cpp
@@ -185,4 +185,3 @@
     SkFontHost::GetTableData(uniqueID, tag, 0, tableSize, buffer);
     return HB_Err_Ok;
 }
-
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
index 4e2bcc9..f8ec108 100644
--- a/src/ports/SkImageDecoder_CG.cpp
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 
+#include "SkColorPriv.h"
 
 #include "SkImageDecoder.h"
 #include "SkImageEncoder.h"
@@ -20,6 +21,8 @@
 
 #ifdef SK_BUILD_FOR_IOS
 #include <CoreGraphics/CoreGraphics.h>
+#include <ImageIO/ImageIO.h>
+#include <MobileCoreServices/MobileCoreServices.h>
 #endif
 
 static void malloc_release_proc(void* info, const void* data, size_t size) {
@@ -31,7 +34,7 @@
     size_t len = stream->getLength();
     void* data = sk_malloc_throw(len);
     stream->read(data, len);
-    
+
     return CGDataProviderCreateWithData(data, data, len, malloc_release_proc);
 }
 
@@ -56,34 +59,51 @@
         return false;
     }
     SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc);
-    
+
     CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL);
     if (NULL == image) {
         return false;
     }
     SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image);
-    
+
     const int width = CGImageGetWidth(image);
     const int height = CGImageGetHeight(image);
     bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
         return true;
     }
-    
+
     if (!this->allocPixelRef(bm, NULL)) {
         return false;
     }
-    
+
     bm->lockPixels();
-    bm->eraseColor(0);
+    bm->eraseColor(SK_ColorTRANSPARENT);
 
     // use the same colorspace, so we don't change the pixels at all
     CGColorSpaceRef cs = CGImageGetColorSpace(image);
-    CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height,
-                                            8, bm->rowBytes(), cs, BITMAP_INFO);
+    CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO);
+    if (NULL == cg) {
+        // perhaps the image's colorspace does not work for a context, so try just rgb
+        cs = CGColorSpaceCreateDeviceRGB();
+        cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO);
+        CFRelease(cs);
+    }
     CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
     CGContextRelease(cg);
 
+    CGImageAlphaInfo info = CGImageGetAlphaInfo(image);
+    switch (info) {
+        case kCGImageAlphaNone:
+        case kCGImageAlphaNoneSkipLast:
+        case kCGImageAlphaNoneSkipFirst:
+            SkASSERT(SkBitmap::ComputeIsOpaque(*bm));
+            bm->setIsOpaque(true);
+            break;
+        default:
+            // we don't know if we're opaque or not, so compute it.
+            bm->computeAndSetOpaquePredicate();
+    }
     bm->unlockPixels();
     return true;
 }
@@ -127,7 +147,7 @@
         return NULL;
     }
     SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer);
-    
+
     return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL);
 }
 
@@ -137,7 +157,7 @@
 
 protected:
     virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality);
-    
+
 private:
     Type fType;
 };
@@ -148,25 +168,38 @@
  */
 bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm,
                                  int quality) {
+    // Used for converting a bitmap to 8888.
+    const SkBitmap* bmPtr = &bm;
+    SkBitmap bitmap8888;
+
     CFStringRef type;
     switch (fType) {
         case kJPEG_Type:
             type = kUTTypeJPEG;
             break;
         case kPNG_Type:
+            // PNG encoding an ARGB_4444 bitmap gives the following errors in GM:
+            // <Error>: CGImageDestinationAddImage image could not be converted to destination
+            // format.
+            // <Error>: CGImageDestinationFinalize image destination does not have enough images
+            // So instead we copy to 8888.
+            if (bm.getConfig() == SkBitmap::kARGB_4444_Config) {
+                bm.copyTo(&bitmap8888, SkBitmap::kARGB_8888_Config);
+                bmPtr = &bitmap8888;
+            }
             type = kUTTypePNG;
             break;
         default:
             return false;
     }
-    
+
     CGImageDestinationRef dst = SkStreamToImageDestination(stream, type);
     if (NULL == dst) {
         return false;
     }
     SkAutoTCallVProc<const void, CFRelease> ardst(dst);
 
-    CGImageRef image = SkCreateCGImageRef(bm);
+    CGImageRef image = SkCreateCGImageRef(*bmPtr);
     if (NULL == image) {
         return false;
     }
@@ -186,4 +219,3 @@
     }
     return SkNEW_ARGS(SkImageEncoder_CG, (t));
 }
-
diff --git a/src/ports/SkImageDecoder_WIC.cpp b/src/ports/SkImageDecoder_WIC.cpp
index a69ed4a..4b869dd 100644
--- a/src/ports/SkImageDecoder_WIC.cpp
+++ b/src/ports/SkImageDecoder_WIC.cpp
@@ -17,6 +17,16 @@
 #include "SkMovie.h"
 #include "SkStream.h"
 #include "SkTScopedComPtr.h"
+#include "SkUnPreMultiply.h"
+
+//All Windows SDKs back to XPSP2 export the CLSID_WICImagingFactory symbol.
+//In the Windows8 SDK the CLSID_WICImagingFactory symbol is still exported
+//but CLSID_WICImagingFactory is then #defined to CLSID_WICImagingFactory2.
+//Undo this #define if it has been done so that we link against the symbols
+//we intended to link against on all SDKs.
+#if defined(CLSID_WICImagingFactory)
+#undef CLSID_WICImagingFactory
+#endif
 
 class SkImageDecoder_WIC : public SkImageDecoder {
 protected:
@@ -29,9 +39,9 @@
     if (!scopedCo.succeeded()) {
         return false;
     }
-    
+
     HRESULT hr = S_OK;
-    
+
     //Create Windows Imaging Component ImagingFactory.
     SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
     if (SUCCEEDED(hr)) {
@@ -42,19 +52,19 @@
             , IID_PPV_ARGS(&piImagingFactory)
         );
     }
-    
+
     //Convert SkStream to IStream.
     SkTScopedComPtr<IStream> piStream;
     if (SUCCEEDED(hr)) {
         hr = SkIStream::CreateFromSkStream(stream, false, &piStream);
     }
-    
+
     //Make sure we're at the beginning of the stream.
     if (SUCCEEDED(hr)) {
         LARGE_INTEGER liBeginning = { 0 };
         hr = piStream->Seek(liBeginning, STREAM_SEEK_SET, NULL);
     }
-    
+
     //Create the decoder from the stream content.
     SkTScopedComPtr<IWICBitmapDecoder> piBitmapDecoder;
     if (SUCCEEDED(hr)) {
@@ -65,13 +75,13 @@
             , &piBitmapDecoder                //Pointer to the decoder
         );
     }
-    
+
     //Get the first frame from the decoder.
     SkTScopedComPtr<IWICBitmapFrameDecode> piBitmapFrameDecode;
     if (SUCCEEDED(hr)) {
         hr = piBitmapDecoder->GetFrame(0, &piBitmapFrameDecode);
     }
-    
+
     //Get the BitmapSource interface of the frame.
     SkTScopedComPtr<IWICBitmapSource> piBitmapSourceOriginal;
     if (SUCCEEDED(hr)) {
@@ -79,14 +89,14 @@
             IID_PPV_ARGS(&piBitmapSourceOriginal)
         );
     }
-    
+
     //Get the size of the bitmap.
     UINT width;
     UINT height;
     if (SUCCEEDED(hr)) {
         hr = piBitmapSourceOriginal->GetSize(&width, &height);
     }
-    
+
     //Exit early if we're only looking for the bitmap bounds.
     if (SUCCEEDED(hr)) {
         bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
@@ -97,13 +107,13 @@
             return false;
         }
     }
-    
+
     //Create a format converter.
     SkTScopedComPtr<IWICFormatConverter> piFormatConverter;
     if (SUCCEEDED(hr)) {
         hr = piImagingFactory->CreateFormatConverter(&piFormatConverter);
     }
-    
+
     if (SUCCEEDED(hr)) {
         hr = piFormatConverter->Initialize(
             piBitmapSourceOriginal.get()      //Input bitmap to convert
@@ -114,7 +124,7 @@
             , WICBitmapPaletteTypeCustom      //Palette translation type
         );
     }
-    
+
     //Get the BitmapSource interface of the format converter.
     SkTScopedComPtr<IWICBitmapSource> piBitmapSourceConverted;
     if (SUCCEEDED(hr)) {
@@ -122,11 +132,11 @@
             IID_PPV_ARGS(&piBitmapSourceConverted)
         );
     }
-    
+
     //Copy the pixels into the bitmap.
     if (SUCCEEDED(hr)) {
         SkAutoLockPixels alp(*bm);
-        bm->eraseColor(0);
+        bm->eraseColor(SK_ColorTRANSPARENT);
         const int stride = bm->rowBytes();
         hr = piBitmapSourceConverted->CopyPixels(
             NULL,                             //Get all the pixels
@@ -134,8 +144,11 @@
             stride * height,
             reinterpret_cast<BYTE *>(bm->getPixels())
         );
+
+        // Note: we don't need to premultiply here since we specified PBGRA
+        bm->computeAndSetOpaquePredicate();
     }
-    
+
     return SUCCEEDED(hr);
 }
 
@@ -183,7 +196,7 @@
     //Convert to 8888 if needed.
     const SkBitmap* bitmap;
     SkBitmap bitmapCopy;
-    if (SkBitmap::kARGB_8888_Config == bitmapOrig.config()) {
+    if (SkBitmap::kARGB_8888_Config == bitmapOrig.config() && bitmapOrig.isOpaque()) {
         bitmap = &bitmapOrig;
     } else {
         if (!bitmapOrig.copyTo(&bitmapCopy, SkBitmap::kARGB_8888_Config)) {
@@ -192,14 +205,31 @@
         bitmap = &bitmapCopy;
     }
 
+    // We cannot use PBGRA so we need to unpremultiply ourselves
+    if (!bitmap->isOpaque()) {
+        SkAutoLockPixels alp(*bitmap);
+
+        uint8_t* pixels = reinterpret_cast<uint8_t*>(bitmap->getPixels());
+        for (int y = 0; y < bitmap->height(); ++y) {
+            for (int x = 0; x < bitmap->width(); ++x) {
+                uint8_t* bytes = pixels + y * bitmap->rowBytes() + x * bitmap->bytesPerPixel();
+
+                SkPMColor* src = reinterpret_cast<SkPMColor*>(bytes);
+                SkColor* dst = reinterpret_cast<SkColor*>(bytes);
+
+                *dst = SkUnPreMultiply::PMColorToColor(*src);
+            }
+        }
+    }
+
     //Initialize COM.
     SkAutoCoInitialize scopedCo;
     if (!scopedCo.succeeded()) {
         return false;
     }
-    
+
     HRESULT hr = S_OK;
-    
+
     //Create Windows Imaging Component ImagingFactory.
     SkTScopedComPtr<IWICImagingFactory> piImagingFactory;
     if (SUCCEEDED(hr)) {
@@ -210,41 +240,41 @@
             , IID_PPV_ARGS(&piImagingFactory)
         );
     }
-    
+
     //Convert the SkWStream to an IStream.
     SkTScopedComPtr<IStream> piStream;
     if (SUCCEEDED(hr)) {
         hr = SkWIStream::CreateFromSkWStream(stream, &piStream);
     }
-    
+
     //Create an encode of the appropriate type.
     SkTScopedComPtr<IWICBitmapEncoder> piEncoder;
     if (SUCCEEDED(hr)) {
         hr = piImagingFactory->CreateEncoder(type, NULL, &piEncoder);
     }
-    
+
     if (SUCCEEDED(hr)) {
         hr = piEncoder->Initialize(piStream.get(), WICBitmapEncoderNoCache);
     }
-    
+
     //Create a the frame.
     SkTScopedComPtr<IWICBitmapFrameEncode> piBitmapFrameEncode;
     SkTScopedComPtr<IPropertyBag2> piPropertybag;
     if (SUCCEEDED(hr)) {
         hr = piEncoder->CreateNewFrame(&piBitmapFrameEncode, &piPropertybag);
     }
-    
+
     if (SUCCEEDED(hr)) {
         PROPBAG2 name = { 0 };
         name.dwType = PROPBAG2_TYPE_DATA;
         name.vt = VT_R4;
         name.pstrName = L"ImageQuality";
-    
+
         VARIANT value;
         VariantInit(&value);
         value.vt = VT_R4;
         value.fltVal = (FLOAT)(quality / 100.0);
-        
+
         //Ignore result code.
         //  This returns E_FAIL if the named property is not in the bag.
         //TODO(bungeman) enumerate the properties,
@@ -254,14 +284,14 @@
     if (SUCCEEDED(hr)) {
         hr = piBitmapFrameEncode->Initialize(piPropertybag.get());
     }
-    
+
     //Set the size of the frame.
     const UINT width = bitmap->width();
     const UINT height = bitmap->height();
     if (SUCCEEDED(hr)) {
         hr = piBitmapFrameEncode->SetSize(width, height);
     }
-    
+
     //Set the pixel format of the frame.
     const WICPixelFormatGUID formatDesired = GUID_WICPixelFormat32bppBGRA;
     WICPixelFormatGUID formatGUID = formatDesired;
@@ -272,7 +302,7 @@
         //Be sure the image format is the one requested.
         hr = IsEqualGUID(formatGUID, formatDesired) ? S_OK : E_FAIL;
     }
-    
+
     //Write the pixels into the frame.
     if (SUCCEEDED(hr)) {
         SkAutoLockPixels alp(*bitmap);
@@ -282,15 +312,15 @@
             , bitmap->rowBytes()*height
             , reinterpret_cast<BYTE*>(bitmap->getPixels()));
     }
-    
+
     if (SUCCEEDED(hr)) {
         hr = piBitmapFrameEncode->Commit();
     }
-    
+
     if (SUCCEEDED(hr)) {
         hr = piEncoder->Commit();
     }
-    
+
     return SUCCEEDED(hr);
 }
 
@@ -304,4 +334,3 @@
     }
     return SkNEW_ARGS(SkImageEncoder_WIC, (t));
 }
-
diff --git a/src/ports/SkImageDecoder_empty.cpp b/src/ports/SkImageDecoder_empty.cpp
index e4079d0..410eef1 100644
--- a/src/ports/SkImageDecoder_empty.cpp
+++ b/src/ports/SkImageDecoder_empty.cpp
@@ -8,92 +8,64 @@
 
 
 #include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
 #include "SkMovie.h"
-#include "SkStream.h"
 
-extern SkImageDecoder* SkImageDecoder_GIF_Factory(SkStream*);
-extern SkImageDecoder* SkImageDecoder_BMP_Factory(SkStream*);
-extern SkImageDecoder* SkImageDecoder_ICO_Factory(SkStream*);
-extern SkImageDecoder* SkImageDecoder_PNG_Factory(SkStream*);
-extern SkImageDecoder* SkImageDecoder_WBMP_Factory(SkStream*);
-extern SkImageDecoder* SkImageDecoder_JPEG_Factory(SkStream*);
-
-typedef SkImageDecoder* (*SkImageDecoderFactoryProc)(SkStream*);
-
-struct CodecFormat {
-    SkImageDecoderFactoryProc   fProc;
-    SkImageDecoder::Format      fFormat;
-};
-
-static const CodecFormat gPairs[] = {
-#if 0
-    { SkImageDecoder_GIF_Factory,   SkImageDecoder::kGIF_Format },
-    { SkImageDecoder_PNG_Factory,   SkImageDecoder::kPNG_Format },
-    { SkImageDecoder_ICO_Factory,   SkImageDecoder::kICO_Format },
-    { SkImageDecoder_WBMP_Factory,  SkImageDecoder::kWBMP_Format },
-    { SkImageDecoder_BMP_Factory,   SkImageDecoder::kBMP_Format },
-    { SkImageDecoder_JPEG_Factory,  SkImageDecoder::kJPEG_Format }
-#endif
-};
+class SkBitmap;
+class SkStream;
 
 SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
-        SkImageDecoder* codec = gPairs[i].fProc(stream);
-        stream->rewind();
-        if (NULL != codec) {
-            return codec;
-        }
-    }
     return NULL;
 }
 
-bool SkImageDecoder::SupportsFormat(Format format) {
-    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
-        if (gPairs[i].fFormat == format) {
-            return true;
-        }
-    }
+bool SkImageDecoder::DecodeFile(const char file[], SkBitmap*, SkBitmap::Config,
+                                SkImageDecoder::Mode, SkImageDecoder::Format*) {
+    return false;
+}
+
+bool SkImageDecoder::decode(SkStream*, SkBitmap* bitmap, SkBitmap::Config pref, Mode) {
+    return false;
+}
+
+bool SkImageDecoder::DecodeStream(SkStream*, SkBitmap*, SkBitmap::Config, SkImageDecoder::Mode,
+                                  SkImageDecoder::Format*) {
+    return false;
+}
+
+bool SkImageDecoder::DecodeMemory(const void*, size_t, SkBitmap*, SkBitmap::Config,
+                                  SkImageDecoder::Mode, SkImageDecoder::Format*) {
+    return false;
+}
+
+SkImageDecoder* CreateJPEGImageDecoder() {
+    return NULL;
+}
+/////////////////////////////////////////////////////////////////////////
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+    return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+    return NULL;
+}
+
+bool SkImageEncoder::EncodeFile(const char file[], const SkBitmap&, Type, int quality) {
+    return false;
+}
+
+bool SkImageEncoder::EncodeStream(SkWStream*, const SkBitmap&, SkImageEncoder::Type, int) {
+    return false;
+}
+
+bool SkImageEncoder::encodeStream(SkWStream*, const SkBitmap&, int) {
     return false;
 }
 
 /////////////////////////////////////////////////////////////////////////
 
-typedef SkMovie* (*SkMovieFactoryProc)(SkStream*);
+#include "SkImages.h"
 
-extern SkMovie* SkMovie_GIF_Factory(SkStream*);
-
-static const SkMovieFactoryProc gMovieProcs[] = {
-#if 0
-    SkMovie_GIF_Factory
-#endif
-};
-
-SkMovie* SkMovie::DecodeStream(SkStream* stream) {
-    for (unsigned i = 0; i < SK_ARRAY_COUNT(gMovieProcs); i++) {
-        SkMovie* movie = gMovieProcs[i](stream);
-        if (NULL != movie) {
-            return movie;
-        }
-        stream->rewind();
-    }
-    return NULL;
-}
-
-/////////////////////////////////////////////////////////////////////////
-
-extern SkImageEncoder* SkImageEncoder_JPEG_Factory();
-extern SkImageEncoder* SkImageEncoder_PNG_Factory();
-
-SkImageEncoder* SkImageEncoder::Create(Type t) {
-    switch (t) {
-#if 0
-        case kJPEG_Type:
-            return SkImageEncoder_JPEG_Factory();
-        case kPNG_Type:
-            return SkImageEncoder_PNG_Factory();
-#endif
-        default:
-            return NULL;
-    }
-}
-
+void SkImages::InitializeFlattenables() {}
diff --git a/src/ports/SkImageRef_ashmem.cpp b/src/ports/SkImageRef_ashmem.cpp
deleted file mode 100644
index f9c6aff..0000000
--- a/src/ports/SkImageRef_ashmem.cpp
+++ /dev/null
@@ -1,244 +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 "SkImageRef_ashmem.h"
-#include "SkImageDecoder.h"
-#include "SkFlattenable.h"
-#include "SkThread.h"
-
-#include <sys/mman.h>
-#include <unistd.h>
-#include <cutils/ashmem.h>
-
-//#define TRACE_ASH_PURGE     // just trace purges
-
-#ifdef DUMP_IMAGEREF_LIFECYCLE
-    #define DUMP_ASHMEM_LIFECYCLE
-#else
-//    #define DUMP_ASHMEM_LIFECYCLE
-#endif
-
-// ashmem likes lengths on page boundaries
-static size_t roundToPageSize(size_t size) {
-    const size_t mask = getpagesize() - 1;
-    size_t newsize = (size + mask) & ~mask;
-//    SkDebugf("---- oldsize %d newsize %d\n", size, newsize);
-    return newsize;
-}
-
-SkImageRef_ashmem::SkImageRef_ashmem(SkStream* stream,
-                                             SkBitmap::Config config,
-                                             int sampleSize)
-        : SkImageRef(stream, config, sampleSize) {
-            
-    fRec.fFD = -1;
-    fRec.fAddr = NULL;
-    fRec.fSize = 0;
-    fRec.fPinned = false;
-            
-    fCT = NULL;
-}
-
-SkImageRef_ashmem::~SkImageRef_ashmem() {
-    SkSafeUnref(fCT);
-    this->closeFD();
-}
-
-void SkImageRef_ashmem::closeFD() {
-    if (-1 != fRec.fFD) {
-#ifdef DUMP_ASHMEM_LIFECYCLE
-        SkDebugf("=== ashmem close %d\n", fRec.fFD);
-#endif
-        SkASSERT(fRec.fAddr);
-        SkASSERT(fRec.fSize);
-        munmap(fRec.fAddr, fRec.fSize);
-        close(fRec.fFD);
-        fRec.fFD = -1;
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-class AshmemAllocator : public SkBitmap::Allocator {
-public:
-    AshmemAllocator(SkAshmemRec* rec, const char name[])
-        : fRec(rec), fName(name) {}
-
-    virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) {
-        const size_t size = roundToPageSize(bm->getSize());
-        int fd = fRec->fFD;
-        void* addr = fRec->fAddr;
-
-        SkASSERT(!fRec->fPinned);
-
-        if (-1 == fd) {
-            SkASSERT(NULL == addr);
-            SkASSERT(0 == fRec->fSize);
-            
-            fd = ashmem_create_region(fName, size);
-#ifdef DUMP_ASHMEM_LIFECYCLE
-            SkDebugf("=== ashmem_create_region %s size=%d fd=%d\n", fName, size, fd);
-#endif
-            if (-1 == fd) {
-                SkDebugf("------- imageref_ashmem create failed <%s> %d\n",
-                         fName, size);
-                return false;
-            }
-            
-            int err = ashmem_set_prot_region(fd, PROT_READ | PROT_WRITE);
-            if (err) {
-                SkDebugf("------ ashmem_set_prot_region(%d) failed %d\n",
-                         fd, err);
-                close(fd);
-                return false;
-            }
-            
-            addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
-            if (-1 == (long)addr) {
-                SkDebugf("---------- mmap failed for imageref_ashmem size=%d\n",
-                         size);
-                close(fd);
-                return false;
-            }
-            
-            fRec->fFD = fd;
-            fRec->fAddr = addr;
-            fRec->fSize = size;
-        } else {
-            SkASSERT(addr);
-            SkASSERT(size == fRec->fSize);
-            (void)ashmem_pin_region(fd, 0, 0);
-        }
-
-        bm->setPixels(addr, ct);
-        fRec->fPinned = true;
-        return true;
-    }
-    
-private:
-    // we just point to our caller's memory, these are not copies
-    SkAshmemRec* fRec;
-    const char*  fName;
-};
-
-bool SkImageRef_ashmem::onDecode(SkImageDecoder* codec, SkStream* stream,
-                                 SkBitmap* bitmap, SkBitmap::Config config,
-                                 SkImageDecoder::Mode mode) {
-    
-    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
-        return this->INHERITED::onDecode(codec, stream, bitmap, config, mode);
-    }
-
-    AshmemAllocator alloc(&fRec, this->getURI());
-    
-    codec->setAllocator(&alloc);
-    bool success = this->INHERITED::onDecode(codec, stream, bitmap, config,
-                                             mode);
-    // remove the allocator, since its on the stack
-    codec->setAllocator(NULL);
-
-    if (success) {
-        // remember the colortable (if any)
-        SkRefCnt_SafeAssign(fCT, bitmap->getColorTable());
-        return true;
-    } else {
-        if (fRec.fPinned) {
-            ashmem_unpin_region(fRec.fFD, 0, 0);
-            fRec.fPinned = false;
-        }
-        this->closeFD();
-        return false;
-    }
-}
-
-void* SkImageRef_ashmem::onLockPixels(SkColorTable** ct) {
-    SkASSERT(fBitmap.getPixels() == NULL);
-    SkASSERT(fBitmap.getColorTable() == NULL);
-
-    // fast case: check if we can just pin and get the cached data
-    if (-1 != fRec.fFD) {
-        SkASSERT(fRec.fAddr);
-        SkASSERT(!fRec.fPinned);
-        int pin = ashmem_pin_region(fRec.fFD, 0, 0);
-
-        if (ASHMEM_NOT_PURGED == pin) { // yea, fast case!
-            fBitmap.setPixels(fRec.fAddr, fCT);
-            fRec.fPinned = true;
-        } else if (ASHMEM_WAS_PURGED == pin) {
-            ashmem_unpin_region(fRec.fFD, 0, 0);
-            // let go of our colortable if we lost the pixels. Well get it back
-            // again when we re-decode
-            if (fCT) {
-                fCT->unref();
-                fCT = NULL;
-            }
-#if defined(DUMP_ASHMEM_LIFECYCLE) || defined(TRACE_ASH_PURGE)
-            SkDebugf("===== ashmem purged %d\n", fBitmap.getSize());
-#endif
-        } else {
-            SkDebugf("===== ashmem pin_region(%d) returned %d\n", fRec.fFD, pin);
-            // return null result for failure
-            if (ct) {
-                *ct = NULL;
-            }
-            return NULL;
-        }
-    } else {
-        // no FD, will create an ashmem region in allocator
-    }
-    
-    return this->INHERITED::onLockPixels(ct);
-}
-
-void SkImageRef_ashmem::onUnlockPixels() {
-    this->INHERITED::onUnlockPixels();
-    
-    if (-1 != fRec.fFD) {
-        SkASSERT(fRec.fAddr);
-        SkASSERT(fRec.fPinned);
-        
-        ashmem_unpin_region(fRec.fFD, 0, 0);
-        fRec.fPinned = false;
-    }
-    
-    // we clear this with or without an error, since we've either closed or
-    // unpinned the region
-    fBitmap.setPixels(NULL, NULL);
-}
-
-void SkImageRef_ashmem::flatten(SkFlattenableWriteBuffer& buffer) const {
-    this->INHERITED::flatten(buffer);
-    const char* uri = getURI();
-    if (uri) {
-        size_t len = strlen(uri);
-        buffer.write32(len);
-        buffer.writePad(uri, len);
-    } else {
-        buffer.write32(0);
-    }
-}
-
-SkImageRef_ashmem::SkImageRef_ashmem(SkFlattenableReadBuffer& buffer)
-        : INHERITED(buffer) {
-    fRec.fFD = -1;
-    fRec.fAddr = NULL;
-    fRec.fSize = 0;
-    fRec.fPinned = false;
-    fCT = NULL;
-    size_t length = buffer.readU32();
-    if (length) {
-        char* buf = (char*) malloc(length);
-        buffer.read(buf, length);
-        setURI(buf, length);
-    }
-}
-
-SkPixelRef* SkImageRef_ashmem::Create(SkFlattenableReadBuffer& buffer) {
-    return SkNEW_ARGS(SkImageRef_ashmem, (buffer));
-}
-
-SK_DEFINE_PIXEL_REF_REGISTRAR(SkImageRef_ashmem)
diff --git a/src/ports/SkImageRef_ashmem.h b/src/ports/SkImageRef_ashmem.h
deleted file mode 100644
index f50ea80..0000000
--- a/src/ports/SkImageRef_ashmem.h
+++ /dev/null
@@ -1,51 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SkImageRef_ashmem_DEFINED
-#define SkImageRef_ashmem_DEFINED
-
-#include "SkImageRef.h"
-
-struct SkAshmemRec {
-    int     fFD;
-    void*   fAddr;
-    size_t  fSize;
-    bool    fPinned;
-};
-
-class SkImageRef_ashmem : public SkImageRef {
-public:
-    SkImageRef_ashmem(SkStream*, SkBitmap::Config, int sampleSize = 1);
-    virtual ~SkImageRef_ashmem();
-    
-    // overrides
-    virtual void flatten(SkFlattenableWriteBuffer&) const;
-    virtual Factory getFactory() const {
-        return Create;
-    }
-    static SkPixelRef* Create(SkFlattenableReadBuffer&);
-
-    SK_DECLARE_PIXEL_REF_REGISTRAR()
-protected:
-    virtual bool onDecode(SkImageDecoder* codec, SkStream* stream,
-                          SkBitmap* bitmap, SkBitmap::Config config,
-                          SkImageDecoder::Mode mode);
-    
-    virtual void* onLockPixels(SkColorTable**);
-    virtual void onUnlockPixels();
-    
-private:
-    SkImageRef_ashmem(SkFlattenableReadBuffer&);
-    void closeFD();
-
-    SkColorTable* fCT;
-    SkAshmemRec fRec;
-
-    typedef SkImageRef INHERITED;
-};
-
-#endif
diff --git a/src/ports/SkMemory_malloc.cpp b/src/ports/SkMemory_malloc.cpp
index 44e43ad..73b5607 100644
--- a/src/ports/SkMemory_malloc.cpp
+++ b/src/ports/SkMemory_malloc.cpp
@@ -49,4 +49,3 @@
     }
     return p;
 }
-
diff --git a/src/ports/SkMemory_mozalloc.cpp b/src/ports/SkMemory_mozalloc.cpp
new file mode 100644
index 0000000..2c049b2
--- /dev/null
+++ b/src/ports/SkMemory_mozalloc.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Google Inc.
+ * Copyright 2012 Mozilla Foundation
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkTypes.h"
+
+#include "mozilla/mozalloc.h"
+#include "mozilla/mozalloc_abort.h"
+#include "mozilla/mozalloc_oom.h"
+
+void sk_throw() {
+    SkDEBUGFAIL("sk_throw");
+    mozalloc_abort("Abort from sk_throw");
+}
+
+void sk_out_of_memory(void) {
+    SkDEBUGFAIL("sk_out_of_memory");
+    mozalloc_handle_oom(0);
+}
+
+void* sk_malloc_throw(size_t size) {
+    return sk_malloc_flags(size, SK_MALLOC_THROW);
+}
+
+void* sk_realloc_throw(void* addr, size_t size) {
+    return moz_xrealloc(addr, size);
+}
+
+void sk_free(void* p) {
+    moz_free(p);
+}
+
+void* sk_malloc_flags(size_t size, unsigned flags) {
+    return (flags & SK_MALLOC_THROW) ? moz_xmalloc(size) : moz_malloc(size);
+}
diff --git a/src/ports/SkOSFile_stdio.cpp b/src/ports/SkOSFile_stdio.cpp
index 764b466..1d27c6b 100644
--- a/src/ports/SkOSFile_stdio.cpp
+++ b/src/ports/SkOSFile_stdio.cpp
@@ -9,8 +9,17 @@
 
 #include "SkOSFile.h"
 
-#include <stdio.h>
 #include <errno.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+#include <direct.h>
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
 
 SkFILE* sk_fopen(const char path[], SkFILE_Flags flags)
 {
@@ -32,14 +41,30 @@
     return f;
 }
 
+char* sk_fgets(char* str, int size, SkFILE* f) {
+    return ::fgets(str, size, (FILE *)f);
+}
+
+
+int sk_feof(SkFILE *f) {
+    // no :: namespace qualifier because it breaks android
+    return feof((FILE *)f);
+}
+
 size_t sk_fgetsize(SkFILE* f)
 {
     SkASSERT(f);
 
-    size_t  curr = ::ftell((FILE*)f);       // remember where we are
+    long curr = ::ftell((FILE*)f);       // remember where we are
+    if (curr < 0) {
+        return 0;
+    }
     ::fseek((FILE*)f, 0, SEEK_END);         // go to the end
-    size_t size = ::ftell((FILE*)f);        // record the size
-    ::fseek((FILE*)f, (long)curr, SEEK_SET);        // go back to our prev loc
+    long size = ::ftell((FILE*)f);        // record the size
+    if (size < 0) {
+        size = 0;
+    }
+    ::fseek((FILE*)f, curr, SEEK_SET);        // go back to our prev loc
     return size;
 }
 
@@ -92,3 +117,46 @@
     ::fclose((FILE*)f);
 }
 
+bool sk_exists(const char *path)
+{
+#ifdef _WIN32
+    return (0 == _access(path, 0));
+#else
+    return (0 == access(path, 0));
+#endif
+}
+
+bool sk_isdir(const char *path)
+{
+    struct stat status;
+    if (0 != stat(path, &status)) {
+        return false;
+    }
+    return SkToBool(status.st_mode & S_IFDIR);
+}
+
+bool sk_mkdir(const char* path)
+{
+    if (sk_isdir(path)) {
+        return true;
+    }
+    if (sk_exists(path)) {
+        fprintf(stderr,
+                "sk_mkdir: path '%s' already exists but is not a directory\n",
+                path);
+        return false;
+    }
+
+    int retval;
+#ifdef _WIN32
+    retval = _mkdir(path);
+#else
+    retval = mkdir(path, 0777);
+#endif
+    if (0 == retval) {
+        return true;
+    } else {
+        fprintf(stderr, "sk_mkdir: error %d creating dir '%s'\n", errno, path);
+        return false;
+    }
+}
diff --git a/src/ports/SkThread_none.cpp b/src/ports/SkThread_none.cpp
index 8361021..1122c95 100644
--- a/src/ports/SkThread_none.cpp
+++ b/src/ports/SkThread_none.cpp
@@ -8,6 +8,7 @@
 
 
 #include "SkThread.h"
+#include "SkTLS.h"
 
 int32_t sk_atomic_inc(int32_t* addr) {
     int32_t value = *addr;
@@ -15,17 +16,43 @@
     return value;
 }
 
+int32_t sk_atomic_add(int32_t* addr, int32_t inc) {
+    int32_t value = *addr;
+    *addr = value + inc;
+    return value;
+}
+
 int32_t sk_atomic_dec(int32_t* addr) {
     int32_t value = *addr;
     *addr = value - 1;
     return value;
 }
+void sk_membar_aquire__after_atomic_dec() { }
+
+int32_t sk_atomic_conditional_inc(int32_t* addr) {
+    int32_t value = *addr;
+    if (value != 0) ++*addr;
+    return value;
+}
+void sk_membar_aquire__after_atomic_conditional_inc() { }
 
 SkMutex::SkMutex() {}
 
 SkMutex::~SkMutex() {}
 
+#ifndef SK_USE_POSIX_THREADS
 void SkMutex::acquire() {}
-
 void SkMutex::release() {}
+#endif
 
+//////////////////////////////////////////////////////////////////////////
+
+static void* gSpecific;
+
+void* SkTLS::PlatformGetSpecific(bool) {
+    return gSpecific;
+}
+
+void SkTLS::PlatformSetSpecific(void* ptr) {
+    gSpecific = ptr;
+}
diff --git a/src/ports/SkThread_pthread.cpp b/src/ports/SkThread_pthread.cpp
index 4750d4f..370f673 100644
--- a/src/ports/SkThread_pthread.cpp
+++ b/src/ports/SkThread_pthread.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 #include "SkThread.h"
+#include "SkTLS.h"
 
 #include <pthread.h>
 #include <errno.h>
@@ -14,7 +15,7 @@
 
 /**
  We prefer the GCC intrinsic implementation of the atomic operations over the
- SkMutex-based implementation. The SkMutex version suffers from static 
+ SkMutex-based implementation. The SkMutex version suffers from static
  destructor ordering problems.
  Note clang also defines the GCC version macros and implements the intrinsics.
  TODO: Verify that gcc-style __sync_* intrinsics work on ARM
@@ -34,10 +35,36 @@
     return __sync_fetch_and_add(addr, 1);
 }
 
+int32_t sk_atomic_add(int32_t* addr, int32_t inc)
+{
+    return __sync_fetch_and_add(addr, inc);
+}
+
 int32_t sk_atomic_dec(int32_t* addr)
 {
     return __sync_fetch_and_add(addr, -1);
 }
+void sk_membar_aquire__after_atomic_dec() { }
+
+int32_t sk_atomic_conditional_inc(int32_t* addr)
+{
+    int32_t value = *addr;
+
+    while (true) {
+        if (value == 0) {
+            return 0;
+        }
+
+        int32_t before = __sync_val_compare_and_swap(addr, value, value + 1);
+
+        if (before == value) {
+            return value;
+        } else {
+            value = before;
+        }
+    }
+}
+void sk_membar_aquire__after_atomic_conditional_inc() { }
 
 #else
 
@@ -52,6 +79,15 @@
     return value;
 }
 
+int32_t sk_atomic_add(int32_t* addr, int32_t inc)
+{
+    SkAutoMutexAcquire ac(gAtomicMutex);
+
+    int32_t value = *addr;
+    *addr = value + inc;
+    return value;
+}
+
 int32_t sk_atomic_dec(int32_t* addr)
 {
     SkAutoMutexAcquire ac(gAtomicMutex);
@@ -60,6 +96,17 @@
     *addr = value - 1;
     return value;
 }
+void sk_membar_aquire__after_atomic_dec() { }
+
+int32_t sk_atomic_conditional_inc(int32_t* addr)
+{
+    SkAutoMutexAcquire ac(gAtomicMutex);
+
+    int32_t value = *addr;
+    if (value != 0) ++*addr;
+    return value;
+}
+void sk_membar_aquire__after_atomic_conditional_inc() { }
 
 #endif
 
@@ -119,7 +166,7 @@
     status = pthread_mutexattr_init(&attr);
     print_pthread_error(status);
     SkASSERT(0 == status);
-    
+
     status = pthread_mutex_init((pthread_mutex_t*)fStorage, &attr);
     print_pthread_error(status);
     SkASSERT(0 == status);
@@ -149,3 +196,25 @@
 }
 
 #endif // !SK_USE_POSIX_THREADS
+
+///////////////////////////////////////////////////////////////////////////////
+
+static pthread_key_t gSkTLSKey;
+static pthread_once_t gSkTLSKey_Once = PTHREAD_ONCE_INIT;
+
+static void sk_tls_make_key() {
+    (void)pthread_key_create(&gSkTLSKey, SkTLS::Destructor);
+}
+
+void* SkTLS::PlatformGetSpecific(bool forceCreateTheSlot) {
+    // should we use forceCreateTheSlot to potentially skip calling pthread_once
+    // and just return NULL if we've never been called with
+    // forceCreateTheSlot==true ?
+
+    (void)pthread_once(&gSkTLSKey_Once, sk_tls_make_key);
+    return pthread_getspecific(gSkTLSKey);
+}
+
+void SkTLS::PlatformSetSpecific(void* ptr) {
+    (void)pthread_setspecific(gSkTLSKey, ptr);
+}
diff --git a/src/ports/SkThread_win.cpp b/src/ports/SkThread_win.cpp
index 70b8e11..bc038b2 100644
--- a/src/ports/SkThread_win.cpp
+++ b/src/ports/SkThread_win.cpp
@@ -10,21 +10,44 @@
 #include <windows.h>
 #include <intrin.h>
 #include "SkThread.h"
+#include "SkTLS.h"
 
 //MSDN says in order to declare an interlocked function for use as an
 //intrinsic, include intrin.h and put the function in a #pragma intrinsic
 //directive.
 //The pragma appears to be unnecessary, but doesn't hurt.
-#pragma intrinsic(_InterlockedIncrement, _InterlockedDecrement)
+#pragma intrinsic(_InterlockedIncrement, _InterlockedExchangeAdd, _InterlockedDecrement)
+#pragma intrinsic(_InterlockedCompareExchange)
 
 int32_t sk_atomic_inc(int32_t* addr) {
     // InterlockedIncrement returns the new value, we want to return the old.
     return _InterlockedIncrement(reinterpret_cast<LONG*>(addr)) - 1;
 }
 
+int32_t sk_atomic_add(int32_t* addr, int32_t inc) {
+    return _InterlockedExchangeAdd(reinterpret_cast<LONG*>(addr),
+                                   static_cast<LONG>(inc));
+}
+
 int32_t sk_atomic_dec(int32_t* addr) {
     return _InterlockedDecrement(reinterpret_cast<LONG*>(addr)) + 1;
 }
+void sk_membar_aquire__after_atomic_dec() { }
+
+int32_t sk_atomic_conditional_inc(int32_t* addr) {
+    while (true) {
+        LONG value = static_cast<LONG const volatile&>(*addr);
+        if (value == 0) {
+            return 0;
+        }
+        if (_InterlockedCompareExchange(reinterpret_cast<LONG*>(addr),
+                                        value + 1,
+                                        value) == value) {
+            return value;
+        }
+    }
+}
+void sk_membar_aquire__after_atomic_conditional_inc() { }
 
 SkMutex::SkMutex() {
     SK_COMPILE_ASSERT(sizeof(fStorage) > sizeof(CRITICAL_SECTION),
@@ -44,3 +67,70 @@
     LeaveCriticalSection(reinterpret_cast<CRITICAL_SECTION*>(&fStorage));
 }
 
+///////////////////////////////////////////////////////////////////////////
+
+static bool gOnce;
+static DWORD gTlsIndex;
+SK_DECLARE_STATIC_MUTEX(gMutex);
+
+void* SkTLS::PlatformGetSpecific(bool forceCreateTheSlot) {
+    if (!forceCreateTheSlot && !gOnce) {
+        return NULL;
+    }
+
+    if (!gOnce) {
+        SkAutoMutexAcquire tmp(gMutex);
+        if (!gOnce) {
+            gTlsIndex = TlsAlloc();
+            gOnce = true;
+        }
+    }
+    return TlsGetValue(gTlsIndex);
+}
+
+void SkTLS::PlatformSetSpecific(void* ptr) {
+    SkASSERT(gOnce);
+    (void)TlsSetValue(gTlsIndex, ptr);
+}
+
+// Call TLS destructors on thread exit. Code based on Chromium's
+// base/threading/thread_local_storage_win.cc
+#ifdef _WIN64
+
+#pragma comment(linker, "/INCLUDE:_tls_used")
+#pragma comment(linker, "/INCLUDE:skia_tls_callback")
+
+#else
+
+#pragma comment(linker, "/INCLUDE:__tls_used")
+#pragma comment(linker, "/INCLUDE:_skia_tls_callback")
+
+#endif
+
+void NTAPI onTLSCallback(PVOID unused, DWORD reason, PVOID unused2) {
+    if ((DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) && gOnce) {
+        void* ptr = TlsGetValue(gTlsIndex);
+        if (ptr != NULL) {
+            SkTLS::Destructor(ptr);
+            TlsSetValue(gTlsIndex, NULL);
+        }
+    }
+}
+
+extern "C" {
+
+#ifdef _WIN64
+
+#pragma const_seg(".CRT$XLB")
+extern const PIMAGE_TLS_CALLBACK skia_tls_callback;
+const PIMAGE_TLS_CALLBACK skia_tls_callback = onTLSCallback;
+#pragma const_seg()
+
+#else
+
+#pragma data_seg(".CRT$XLB")
+PIMAGE_TLS_CALLBACK skia_tls_callback = onTLSCallback;
+#pragma data_seg()
+
+#endif
+}
diff --git a/src/ports/SkXMLParser_expat.cpp b/src/ports/SkXMLParser_expat.cpp
index c78dc35..afc9f79 100644
--- a/src/ports/SkXMLParser_expat.cpp
+++ b/src/ports/SkXMLParser_expat.cpp
@@ -28,7 +28,7 @@
 inline const char* ConvertUnicodeToChar(const short* ch16, size_t len, SkAutoMalloc& ch8Malloc) {
     char* ch8 = (char*) ch8Malloc.get();
     int index;
-    for (index = 0; index < len; index++) 
+    for (index = 0; index < len; index++)
         ch8[index] = (char) ch16[index];
     ch8[index] = '\0';
     return ch8;
@@ -138,4 +138,3 @@
     if (str)
         str->set(XML_ErrorString((XML_Error) error));
 }
-
diff --git a/src/ports/SkXMLParser_tinyxml.cpp b/src/ports/SkXMLParser_tinyxml.cpp
index 2e308aa..f357592 100644
--- a/src/ports/SkXMLParser_tinyxml.cpp
+++ b/src/ports/SkXMLParser_tinyxml.cpp
@@ -22,12 +22,12 @@
     while (attr)
     {
         //printf("walk_elem_attr(%s=\"%s\") ", attr->Name(), attr->Value());
-    
+
         parser->addAttribute(attr->Name(), attr->Value());
         attr = attr->Next();
     }
     //printf("\n");
-    
+
     const TiXmlNode* node = elem->FirstChild();
     while (node)
     {
@@ -37,7 +37,7 @@
             parser->text(node->Value(), strlen(node->Value()));
         node = node->NextSibling();
     }
-    
+
     parser->endElement(elem->Value());
 }
 
@@ -51,7 +51,7 @@
         printf("tinyxml error: <%s> row[%d] col[%d]\n", doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
         return false;
     }
-    
+
     walk_elem(parser, doc.RootElement());
     return true;
 }
@@ -59,13 +59,13 @@
 bool SkXMLParser::parse(SkStream& stream)
 {
     size_t size = stream.read(NULL, 0);
-    
+
     SkAutoMalloc    buffer(size + 1);
     char*           buf = (char*)buffer.get();
-    
+
     stream.read(buf, size);
     buf[size] = 0;
-    
+
     return load_buf(this, buf);
 }
 
@@ -73,10 +73,10 @@
 {
     SkAutoMalloc    buffer(len + 1);
     char*           buf = (char*)buffer.get();
-    
+
     memcpy(buf, doc, len);
     buf[len] = 0;
-    
+
     return load_buf(this, buf);
 }
 
@@ -85,4 +85,3 @@
     if (str)
         str->set("GetNativeErrorString not implemented for TinyXml");
 }
-
diff --git a/src/ports/SkXMLPullParser_expat.cpp b/src/ports/SkXMLPullParser_expat.cpp
index 06cdc65..1d1615b 100644
--- a/src/ports/SkXMLPullParser_expat.cpp
+++ b/src/ports/SkXMLPullParser_expat.cpp
@@ -18,8 +18,8 @@
 {
     SkASSERT(src);
     char*   dst = (char*)chunk.alloc(len + 1, SkChunkAlloc::kThrow_AllocFailType);
-    
-    memcpy(dst, src, len);    
+
+    memcpy(dst, src, len);
     dst[len] = 0;
     return dst;
 }
@@ -37,11 +37,11 @@
 
 struct Data {
     Data() : fAlloc(2048), fState(NORMAL) {}
-    
+
     XML_Parser              fParser;
     SkXMLPullParser::Curr*  fCurr;
     SkChunkAlloc            fAlloc;
-    
+
     enum State {
         NORMAL,
         MISSED_START_TAG,
@@ -58,7 +58,7 @@
     SkChunkAlloc&           alloc = p->fAlloc;
 
     c->fName = dupstr(alloc, el, strlen(el));
-    
+
     int n = count_pairs(attr);
     SkXMLPullParser::AttrInfo* info = (SkXMLPullParser::AttrInfo*)alloc.alloc(n * sizeof(SkXMLPullParser::AttrInfo),
                                                                               SkChunkAlloc::kThrow_AllocFailType);
@@ -88,7 +88,7 @@
             so we set a flag to notify them of the missed start_tag
         */
         p->fState = Data::MISSED_START_TAG;
-        
+
         SkASSERT(c->fName != NULL);
         SkASSERT(strcmp(c->fName, el) == 0);
     }
@@ -134,7 +134,7 @@
     XML_Error code = XML_GetErrorCode(parser);
     int lineNumber = XML_GetCurrentLineNumber(parser);
     const char* msg = XML_ErrorString(code);
-    
+
     printf("-------- XML error [%d] on line %d, %s\n", code, lineNumber, msg);
 }
 
@@ -177,13 +177,13 @@
         return SkXMLPullParser::END_TAG;
     }
 
-    fImpl->fData.fAlloc.reuse();
+    fImpl->fData.fAlloc.reset();
 
     XML_Parser p = fImpl->fData.fParser;
     XML_Status status;
 
     status = XML_ResumeParser(p);
-    
+
 CHECK_STATUS:
     switch (status) {
     case XML_STATUS_OK:
@@ -204,11 +204,10 @@
             // return a start_tag, and clear the flag so we return end_tag next
             SkASSERT(SkXMLPullParser::END_TAG == fCurr.fEventType);
             fImpl->fData.fState = Data::RETURN_END_TAG;
-            fImpl->fData.fEndTag = fCurr.fName;  // save this pointer            
+            fImpl->fData.fEndTag = fCurr.fName;  // save this pointer
             return SkXMLPullParser::START_TAG;
         }
         break;
     }
     return fCurr.fEventType;
 }
-
diff --git a/src/ports/sk_predefined_gamma.h b/src/ports/sk_predefined_gamma.h
deleted file mode 100644
index d363594..0000000
--- a/src/ports/sk_predefined_gamma.h
+++ /dev/null
@@ -1,51 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#ifndef SK_PREDEFINED_GAMMA_H
-#define SK_PREDEFINED_GAMMA_H
-
-// Gamma table for 1.4
-static const uint8_t gBlackGamma[] = {
-    0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 
-    0x05, 0x06, 0x06, 0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D, 
-    0x0E, 0x0F, 0x0F, 0x10, 0x10, 0x11, 0x12, 0x12, 0x13, 0x14, 0x14, 0x15, 0x16, 0x16, 0x17, 0x18, 
-    0x19, 0x19, 0x1A, 0x1B, 0x1C, 0x1C, 0x1D, 0x1E, 0x1F, 0x1F, 0x20, 0x21, 0x22, 0x22, 0x23, 0x24, 
-    0x25, 0x26, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x31, 
-    0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 
-    0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 
-    0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 
-    0x61, 0x62, 0x63, 0x64, 0x65, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 
-    0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x84, 
-    0x85, 0x86, 0x87, 0x88, 0x89, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x91, 0x92, 0x93, 0x94, 0x95, 0x97, 
-    0x98, 0x99, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA3, 0xA4, 0xA5, 0xA6, 0xA8, 0xA9, 0xAA, 
-    0xAB, 0xAD, 0xAE, 0xAF, 0xB0, 0xB2, 0xB3, 0xB4, 0xB5, 0xB7, 0xB8, 0xB9, 0xBB, 0xBC, 0xBD, 0xBE, 
-    0xC0, 0xC1, 0xC2, 0xC4, 0xC5, 0xC6, 0xC8, 0xC9, 0xCA, 0xCB, 0xCD, 0xCE, 0xCF, 0xD1, 0xD2, 0xD3, 
-    0xD5, 0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDD, 0xDE, 0xDF, 0xE1, 0xE2, 0xE3, 0xE5, 0xE6, 0xE8, 0xE9, 
-    0xEA, 0xEC, 0xED, 0xEE, 0xF0, 0xF1, 0xF2, 0xF4, 0xF5, 0xF7, 0xF8, 0xF9, 0xFB, 0xFC, 0xFE, 0xFF, 
-};
-
-// Gamma table for 0.714286
-static const uint8_t gWhiteGamma[] = {
-    0x00, 0x05, 0x08, 0x0B, 0x0D, 0x0F, 0x12, 0x14, 0x16, 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20, 0x22, 
-    0x23, 0x25, 0x26, 0x28, 0x29, 0x2B, 0x2C, 0x2E, 0x2F, 0x31, 0x32, 0x33, 0x35, 0x36, 0x37, 0x39, 
-    0x3A, 0x3B, 0x3C, 0x3E, 0x3F, 0x40, 0x41, 0x43, 0x44, 0x45, 0x46, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 
-    0x4D, 0x4E, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 
-    0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 
-    0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 
-    0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 
-    0x8E, 0x8F, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x97, 0x98, 0x99, 0x9A, 0x9B, 
-    0x9C, 0x9D, 0x9E, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 
-    0xAA, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB4, 0xB5, 0xB6, 
-    0xB7, 0xB8, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBC, 0xBD, 0xBE, 0xBF, 0xC0, 0xC0, 0xC1, 0xC2, 0xC3, 
-    0xC4, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCC, 0xCD, 0xCE, 0xCF, 0xCF, 
-    0xD0, 0xD1, 0xD2, 0xD3, 0xD3, 0xD4, 0xD5, 0xD6, 0xD6, 0xD7, 0xD8, 0xD9, 0xD9, 0xDA, 0xDB, 0xDC, 
-    0xDC, 0xDD, 0xDE, 0xDF, 0xDF, 0xE0, 0xE1, 0xE2, 0xE2, 0xE3, 0xE4, 0xE5, 0xE5, 0xE6, 0xE7, 0xE8, 
-    0xE8, 0xE9, 0xEA, 0xEB, 0xEB, 0xEC, 0xED, 0xEE, 0xEE, 0xEF, 0xF0, 0xF1, 0xF1, 0xF2, 0xF3, 0xF3, 
-    0xF4, 0xF5, 0xF6, 0xF6, 0xF7, 0xF8, 0xF9, 0xF9, 0xFA, 0xFB, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFF, 
-};
-
-#endif
diff --git a/src/sfnt/SkIBMFamilyClass.h b/src/sfnt/SkIBMFamilyClass.h
new file mode 100644
index 0000000..45d9822
--- /dev/null
+++ b/src/sfnt/SkIBMFamilyClass.h
@@ -0,0 +1,174 @@
+/*
+ * 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 SkIBMFamilyClass_DEFINED
+#define SkIBMFamilyClass_DEFINED
+
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkIBMFamilyClass {
+    SK_TYPED_ENUM(Class, SK_OT_BYTE,
+        ((NoClassification, 0))
+        ((OldstyleSerifs, 1))
+        ((TransitionalSerifs, 2))
+        ((ModernSerifs, 3))
+        ((ClarendonSerifs, 4))
+        ((SlabSerifs, 5))
+        //6 reserved for future use
+        ((FreeformSerifs, 7))
+        ((SansSerif, 8))
+        ((Ornamentals, 9))
+        ((Scripts, 10))
+        //11 reserved for future use
+        ((Symbolic, 12))
+        //13-15 reserved for future use
+        SK_SEQ_END,
+    (familyClass)SK_SEQ_END)
+    union SubClass {
+        struct OldstyleSerifs {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((IBMRoundedLegibility, 1))
+                ((Garalde, 2))
+                ((Venetian, 3))
+                ((ModifiedVenetian, 4))
+                ((DutchModern, 5))
+                ((DutchTraditional, 6))
+                ((Contemporary, 7))
+                ((Calligraphic, 8))
+                //9-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } oldstyleSerifs;
+        struct TransitionalSerifs {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((DirectLine, 1))
+                ((Script, 2))
+                //3-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } transitionalSerifs;
+        struct ModernSerifs {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((Italian, 1))
+                ((Script, 2))
+                //3-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } modernSerifs;
+        struct ClarendonSerifs {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((Clarendon, 1))
+                ((Modern, 2))
+                ((Traditional, 3))
+                ((Newspaper, 4))
+                ((StubSerif, 5))
+                ((Monotone, 6))
+                ((Typewriter, 7))
+                //8-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } clarendonSerifs;
+        struct SlabSerifs {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((Monotone, 1))
+                ((Humanist, 2))
+                ((Geometric, 3))
+                ((Swiss, 4))
+                ((Typewriter, 5))
+                //6-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } slabSerifs;
+        struct FreeformSerifs {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((Modern, 1))
+                //2-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } freeformSerifs;
+        struct SansSerif {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((IBMNeoGrotesqueGothic, 1))
+                ((Humanist, 2))
+                ((LowXRoundGeometric, 3))
+                ((HighXRoundGeometric, 4))
+                ((NeoGrotesqueGothic, 5))
+                ((ModifiedNeoGrotesqueGothic, 6))
+                //7-8 reserved for future use
+                ((TypewriterGothic, 9))
+                ((Matrix, 10))
+                //11-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } sansSerif;
+        struct Ornamentals {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((Engraver, 1))
+                ((BlackLetter, 2))
+                ((Decorative, 3))
+                ((ThreeDimensional, 4))
+                //5-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } ornamentals;
+        struct Scripts {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                ((Uncial, 1))
+                ((Brush_Joined, 2))
+                ((Formal_Joined, 3))
+                ((Monotone_Joined, 4))
+                ((Calligraphic, 5))
+                ((Brush_Unjoined, 6))
+                ((Formal_Unjoined, 7))
+                ((Monotone_Unjoined, 8))
+                //9-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } scripts;
+        struct Symbolic {
+            SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                ((NoClassification, 0))
+                //1-2 reserved for future use
+                ((MixedSerif, 3))
+                //4-5 reserved for future use
+                ((OldstyleSerif, 6))
+                ((NeoGrotesqueSansSerif, 7))
+                //8-14 reserved for future use
+                ((Miscellaneous, 15))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } symbolic;
+    } familySubClass;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkIBMFamilyClass) == 2, sizeof_SkIBMFamilyClass_not_2);
+
+#endif
diff --git a/src/sfnt/SkOTTableTypes.h b/src/sfnt/SkOTTableTypes.h
new file mode 100644
index 0000000..9adec9b
--- /dev/null
+++ b/src/sfnt/SkOTTableTypes.h
@@ -0,0 +1,48 @@
+/*
+ * 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 SkOTTableTypes_DEFINED
+#define SkOTTableTypes_DEFINED
+
+#include "SkTypes.h"
+#include "SkEndian.h"
+
+//All SK_OT_ prefixed types should be considered as big endian.
+typedef uint8_t SK_OT_BYTE;
+#if CHAR_BIT == 8
+typedef signed char SK_OT_CHAR; //easier to debug
+#else
+typedef int8_t SK_OT_CHAR;
+#endif
+typedef uint16_t SK_OT_SHORT;
+typedef uint16_t SK_OT_USHORT;
+typedef uint32_t SK_OT_ULONG;
+typedef uint32_t SK_OT_LONG;
+//16.16 Signed fixed point representation.
+typedef int32_t SK_OT_Fixed;
+//2.14 Signed fixed point representation.
+typedef uint16_t SK_OT_F2DOT14;
+//F units are the units of measurement in em space.
+typedef uint16_t SK_OT_FWORD;
+typedef uint16_t SK_OT_UFWORD;
+//Number of seconds since 12:00 midnight, January 1, 1904.
+typedef uint64_t SK_OT_LONGDATETIME;
+
+#define SK_OT_BYTE_BITFIELD SK_UINT8_BITFIELD
+
+template<typename T> class SkOTTableTAG {
+public:
+    /**
+     * SkOTTableTAG<T>::value is the big endian value of an OpenType table tag.
+     * It may be directly compared with raw big endian table data.
+     */
+    static const SK_OT_ULONG value = SkTEndian_SwapBE32(
+        SkSetFourByteTag(T::TAG0, T::TAG1, T::TAG2, T::TAG3)
+    );
+};
+
+#endif
diff --git a/src/sfnt/SkOTTable_OS_2.h b/src/sfnt/SkOTTable_OS_2.h
new file mode 100644
index 0000000..c4dbc53
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2.h
@@ -0,0 +1,52 @@
+/*
+ * 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 SkOTTable_OS_2_DEFINED
+#define SkOTTable_OS_2_DEFINED
+
+#include "SkOTTable_OS_2_VA.h"
+#include "SkOTTable_OS_2_V0.h"
+#include "SkOTTable_OS_2_V1.h"
+#include "SkOTTable_OS_2_V2.h"
+#include "SkOTTable_OS_2_V3.h"
+#include "SkOTTable_OS_2_V4.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableOS2 {
+    static const SK_OT_CHAR TAG0 = 'O';
+    static const SK_OT_CHAR TAG1 = 'S';
+    static const SK_OT_CHAR TAG2 = '/';
+    static const SK_OT_CHAR TAG3 = '2';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableOS2>::value;
+
+    union Version {
+        SK_OT_USHORT version;
+
+        //original V0 TT
+        struct VA : SkOTTableOS2_VA { } vA;
+        struct V0 : SkOTTableOS2_V0 { } v0;
+        struct V1 : SkOTTableOS2_V1 { } v1;
+        struct V2 : SkOTTableOS2_V2 { } v2;
+        //makes fsType 0-3 exclusive
+        struct V3 : SkOTTableOS2_V3 { } v3;
+        //defines fsSelection bits 7-9
+        struct V4 : SkOTTableOS2_V4 { } v4;
+    } version;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2::Version::VA) == 68, sizeof_SkOTTableOS2__VA_not_68);
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2::Version::V0) == 78, sizeof_SkOTTableOS2__V0_not_78);
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2::Version::V1) == 86, sizeof_SkOTTableOS2__V1_not_86);
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2::Version::V2) == 96, sizeof_SkOTTableOS2__V2_not_96);
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2::Version::V3) == 96, sizeof_SkOTTableOS2__V3_not_96);
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2::Version::V4) == 96, sizeof_SkOTTableOS2__V4_not_96);
+
+#endif
diff --git a/src/sfnt/SkOTTable_OS_2_V0.h b/src/sfnt/SkOTTable_OS_2_V0.h
new file mode 100644
index 0000000..14d322f
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V0.h
@@ -0,0 +1,149 @@
+/*
+ * 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 SkOTTable_OS_2_V0_DEFINED
+#define SkOTTable_OS_2_V0_DEFINED
+
+#include "SkEndian.h"
+#include "SkIBMFamilyClass.h"
+#include "SkOTTableTypes.h"
+#include "SkPanose.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableOS2_V0 {
+    SK_OT_USHORT version;
+    //SkOTTableOS2_VA::VERSION and SkOTTableOS2_V0::VERSION are both 0.
+    //The only way to differentiate these two versions is by the size of the table.
+    static const SK_OT_USHORT VERSION = SkTEndian_SwapBE16(0);
+
+    SK_OT_SHORT xAvgCharWidth;
+    struct WeightClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((Thin, SkTEndian_SwapBE16(100)))
+            ((ExtraLight, SkTEndian_SwapBE16(200)))
+            ((Light, SkTEndian_SwapBE16(300)))
+            ((Normal, SkTEndian_SwapBE16(400)))
+            ((Medium, SkTEndian_SwapBE16(500)))
+            ((SemiBold, SkTEndian_SwapBE16(600)))
+            ((Bold, SkTEndian_SwapBE16(700)))
+            ((ExtraBold, SkTEndian_SwapBE16(800)))
+            ((Black, SkTEndian_SwapBE16(900)))
+            SK_SEQ_END,
+        SK_SEQ_END)
+        SK_OT_USHORT value;
+    } usWeightClass;
+    struct WidthClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((UltraCondensed, SkTEndian_SwapBE16(1)))
+            ((ExtraCondensed, SkTEndian_SwapBE16(2)))
+            ((Condensed, SkTEndian_SwapBE16(3)))
+            ((SemiCondensed, SkTEndian_SwapBE16(4)))
+            ((Medium, SkTEndian_SwapBE16(5)))
+            ((SemiExpanded, SkTEndian_SwapBE16(6)))
+            ((Expanded, SkTEndian_SwapBE16(7)))
+            ((ExtraExpanded, SkTEndian_SwapBE16(8)))
+            ((UltraExpanded, SkTEndian_SwapBE16(9)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } usWidthClass;
+    union Type {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved00,
+                Restricted,
+                PreviewPrint,
+                Editable,
+                Reserved04,
+                Reserved05,
+                Reserved06,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT Installable = SkTEndian_SwapBE16(0);
+            static const SK_OT_USHORT RestrictedMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT PreviewPrintMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT EditableMask = SkTEndian_SwapBE16(1 << 3);
+            SK_OT_USHORT value;
+        } raw;
+    } fsType;
+    SK_OT_SHORT ySubscriptXSize;
+    SK_OT_SHORT ySubscriptYSize;
+    SK_OT_SHORT ySubscriptXOffset;
+    SK_OT_SHORT ySubscriptYOffset;
+    SK_OT_SHORT ySuperscriptXSize;
+    SK_OT_SHORT ySuperscriptYSize;
+    SK_OT_SHORT ySuperscriptXOffset;
+    SK_OT_SHORT ySuperscriptYOffset;
+    SK_OT_SHORT yStrikeoutSize;
+    SK_OT_SHORT yStrikeoutPosition;
+    SkIBMFamilyClass sFamilyClass;
+    SkPanose panose;
+    SK_OT_ULONG ulCharRange[4];
+    SK_OT_CHAR achVendID[4];
+    union Selection {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Italic,
+                Underscore,
+                Negative,
+                Outlined,
+                Strikeout,
+                Bold,
+                Regular,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT ItalicMask = SkTEndian_SwapBE16(1 << 0);
+            static const SK_OT_USHORT UnderscoreMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT NegativeMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT OutlinedMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT StrikeoutMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT BoldMask = SkTEndian_SwapBE16(1 << 5);
+            static const SK_OT_USHORT RegularMask = SkTEndian_SwapBE16(1 << 6);
+            SK_OT_USHORT value;
+        } raw;
+    } fsSelection;
+    SK_OT_USHORT usFirstCharIndex;
+    SK_OT_USHORT usLastCharIndex;
+    //version0
+    SK_OT_SHORT sTypoAscender;
+    SK_OT_SHORT sTypoDescender;
+    SK_OT_SHORT sTypoLineGap;
+    SK_OT_USHORT usWinAscent;
+    SK_OT_USHORT usWinDescent;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2_V0) == 78, sizeof_SkOTTableOS2_V0_not_78);
+
+#endif
diff --git a/src/sfnt/SkOTTable_OS_2_V1.h b/src/sfnt/SkOTTable_OS_2_V1.h
new file mode 100644
index 0000000..52a60c0
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V1.h
@@ -0,0 +1,518 @@
+/*
+ * 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 SkOTTable_OS_2_V1_DEFINED
+#define SkOTTable_OS_2_V1_DEFINED
+
+#include "SkEndian.h"
+#include "SkIBMFamilyClass.h"
+#include "SkOTTableTypes.h"
+#include "SkPanose.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableOS2_V1 {
+    SK_OT_USHORT version;
+    static const SK_OT_USHORT VERSION = SkTEndian_SwapBE16(1);
+
+    SK_OT_SHORT xAvgCharWidth;
+    struct WeightClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((Thin, SkTEndian_SwapBE16(100)))
+            ((ExtraLight, SkTEndian_SwapBE16(200)))
+            ((Light, SkTEndian_SwapBE16(300)))
+            ((Normal, SkTEndian_SwapBE16(400)))
+            ((Medium, SkTEndian_SwapBE16(500)))
+            ((SemiBold, SkTEndian_SwapBE16(600)))
+            ((Bold, SkTEndian_SwapBE16(700)))
+            ((ExtraBold, SkTEndian_SwapBE16(800)))
+            ((Black, SkTEndian_SwapBE16(900)))
+            SK_SEQ_END,
+        SK_SEQ_END)
+        SK_OT_USHORT value;
+    } usWeightClass;
+    struct WidthClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((UltraCondensed, SkTEndian_SwapBE16(1)))
+            ((ExtraCondensed, SkTEndian_SwapBE16(2)))
+            ((Condensed, SkTEndian_SwapBE16(3)))
+            ((SemiCondensed, SkTEndian_SwapBE16(4)))
+            ((Medium, SkTEndian_SwapBE16(5)))
+            ((SemiExpanded, SkTEndian_SwapBE16(6)))
+            ((Expanded, SkTEndian_SwapBE16(7)))
+            ((ExtraExpanded, SkTEndian_SwapBE16(8)))
+            ((UltraExpanded, SkTEndian_SwapBE16(9)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } usWidthClass;
+    union Type {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved00,
+                Restricted,
+                PreviewPrint,
+                Editable,
+                Reserved04,
+                Reserved05,
+                Reserved06,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT Installable = SkTEndian_SwapBE16(0);
+            static const SK_OT_USHORT RestrictedMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT PreviewPrintMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT EditableMask = SkTEndian_SwapBE16(1 << 3);
+            SK_OT_USHORT value;
+        } raw;
+    } fsType;
+    SK_OT_SHORT ySubscriptXSize;
+    SK_OT_SHORT ySubscriptYSize;
+    SK_OT_SHORT ySubscriptXOffset;
+    SK_OT_SHORT ySubscriptYOffset;
+    SK_OT_SHORT ySuperscriptXSize;
+    SK_OT_SHORT ySuperscriptYSize;
+    SK_OT_SHORT ySuperscriptXOffset;
+    SK_OT_SHORT ySuperscriptYOffset;
+    SK_OT_SHORT yStrikeoutSize;
+    SK_OT_SHORT yStrikeoutPosition;
+    SkIBMFamilyClass sFamilyClass;
+    SkPanose panose;
+    union UnicodeRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Thai,
+                Lao,
+                BasicGeorgian,
+                GeorgianExtended,
+                HangulJamo,
+                LatinExtendedAdditional,
+                GreekExtended,
+                GeneralPunctuation)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Bengali,
+                Gurmukhi,
+                Gujarati,
+                Oriya,
+                Tamil,
+                Telugu,
+                Kannada,
+                Malayalam)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                GreekSymbolsAndCoptic,
+                Cyrillic,
+                Armenian,
+                BasicHebrew,
+                HebrewExtendedAB,
+                BasicArabic,
+                ArabicExtended,
+                Devanagari)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                BasicLatin,
+                Latin1Supplement,
+                LatinExtendedA,
+                LatinExtendedB,
+                IPAExtensions,
+                SpacingModifierLetters,
+                CombiningDiacriticalMarks,
+                BasicGreek)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                Hangul,
+                Reserved057,
+                Reserved058,
+                CJKUnifiedIdeographs,
+                PrivateUseArea,
+                CJKCompatibilityIdeographs,
+                AlphabeticPresentationForms,
+                ArabicPresentationFormsA)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                CJKSymbolsAndPunctuation,
+                Hiragana,
+                Katakana,
+                Bopomofo,
+                HangulCompatibilityJamo,
+                CJKMiscellaneous,
+                EnclosedCJKLettersAndMonths,
+                CJKCompatibility)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                ControlPictures,
+                OpticalCharacterRecognition,
+                EnclosedAlphanumerics,
+                BoxDrawing,
+                BlockElements,
+                GeometricShapes,
+                MiscellaneousSymbols,
+                Dingbats)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                SuperscriptsAndSubscripts,
+                CurrencySymbols,
+                CombiningDiacriticalMarksForSymbols,
+                LetterlikeSymbols,
+                NumberForms,
+                Arrows,
+                MathematicalOperators,
+                MiscellaneousTechnical)
+
+            //l2 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved088,
+                Reserved089,
+                Reserved090,
+                Reserved091,
+                Reserved092,
+                Reserved093,
+                Reserved094,
+                Reserved095)
+            //l2 16-23
+            SK_OT_BYTE_BITFIELD(
+                Reserved080,
+                Reserved081,
+                Reserved082,
+                Reserved083,
+                Reserved084,
+                Reserved085,
+                Reserved086,
+                Reserved087)
+            //l2 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved072,
+                Reserved073,
+                Reserved074,
+                Reserved075,
+                Reserved076,
+                Reserved077,
+                Reserved078,
+                Reserved079)
+            //l2 0-7
+            SK_OT_BYTE_BITFIELD(
+                CombiningHalfMarks,
+                CJKCompatibilityForms,
+                SmallFormVariants,
+                ArabicPresentationFormsB,
+                HalfwidthAndFullwidthForms,
+                Specials,
+                Reserved70,
+                Reserved71)
+
+            //l3 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved120,
+                Reserved121,
+                Reserved122,
+                Reserved123,
+                Reserved124,
+                Reserved125,
+                Reserved126,
+                Reserved127)
+            //l3 16-23
+            SK_OT_BYTE_BITFIELD(
+                Reserved112,
+                Reserved113,
+                Reserved114,
+                Reserved115,
+                Reserved116,
+                Reserved117,
+                Reserved118,
+                Reserved119)
+            //l3 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved104,
+                Reserved105,
+                Reserved106,
+                Reserved107,
+                Reserved108,
+                Reserved109,
+                Reserved110,
+                Reserved111)
+            //l3 0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved096,
+                Reserved097,
+                Reserved098,
+                Reserved099,
+                Reserved100,
+                Reserved101,
+                Reserved102,
+                Reserved103)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG BasicLatinMask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin1SupplementMask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG LatinExtendedAMask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG LatinExtendedBMask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG IPAExtensionsMask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG SpacingModifierLettersMask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG CombiningDiacriticalMarksMask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG BasicGreekMask = SkTEndian_SwapBE32(1 << 7);
+                static const SK_OT_ULONG GreekSymbolsAndCCopticMask = SkTEndian_SwapBE32(1 << 8);
+                static const SK_OT_ULONG CyrillicMask = SkTEndian_SwapBE32(1 << 9);
+                static const SK_OT_ULONG ArmenianMask = SkTEndian_SwapBE32(1 << 10);
+                static const SK_OT_ULONG BasicHebrewMask = SkTEndian_SwapBE32(1 << 11);
+                static const SK_OT_ULONG HebrewExtendedABMask = SkTEndian_SwapBE32(1 << 12);
+                static const SK_OT_ULONG BasicArabicMask = SkTEndian_SwapBE32(1 << 13);
+                static const SK_OT_ULONG ArabicExtendedMask = SkTEndian_SwapBE32(1 << 14);
+                static const SK_OT_ULONG DevanagariMask = SkTEndian_SwapBE32(1 << 15);
+                static const SK_OT_ULONG BengaliMask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG GurmukhiMask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG GujaratiMask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG OriyaMask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG TamilMask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG TeluguMask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG KannadaMask = SkTEndian_SwapBE32(1 << 22);
+                static const SK_OT_ULONG MalayalamMask = SkTEndian_SwapBE32(1 << 23);
+                static const SK_OT_ULONG ThaiMask = SkTEndian_SwapBE32(1 << 24);
+                static const SK_OT_ULONG LaoMask = SkTEndian_SwapBE32(1 << 25);
+                static const SK_OT_ULONG BasicGeorgianMask = SkTEndian_SwapBE32(1 << 26);
+                static const SK_OT_ULONG GeorgianExtendedMask = SkTEndian_SwapBE32(1 << 27);
+                static const SK_OT_ULONG HangulJamoMask = SkTEndian_SwapBE32(1 << 28);
+                static const SK_OT_ULONG LatinExtendedAdditionalMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG GreekExtendedMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG GeneralPunctuationMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG SuperscriptsAndSubscriptsMask = SkTEndian_SwapBE32(1 << (32 - 32));
+                static const SK_OT_ULONG CurrencySymbolsMask = SkTEndian_SwapBE32(1 << (33 - 32));
+                static const SK_OT_ULONG CombiningDiacriticalMarksForSymbolsMask = SkTEndian_SwapBE32(1 << (34 - 32));
+                static const SK_OT_ULONG LetterlikeSymbolsMask = SkTEndian_SwapBE32(1 << (35 - 32));
+                static const SK_OT_ULONG NumberFormsMask = SkTEndian_SwapBE32(1 << (36 - 32));
+                static const SK_OT_ULONG ArrowsMask = SkTEndian_SwapBE32(1 << (37 - 32));
+                static const SK_OT_ULONG MathematicalOperatorsMask = SkTEndian_SwapBE32(1 << (38 - 32));
+                static const SK_OT_ULONG MiscellaneousTechnicalMask = SkTEndian_SwapBE32(1 << (39 - 32));
+                static const SK_OT_ULONG ControlPicturesMask = SkTEndian_SwapBE32(1 << (40 - 32));
+                static const SK_OT_ULONG OpticalCharacterRecognitionMask = SkTEndian_SwapBE32(1 << (41 - 32));
+                static const SK_OT_ULONG EnclosedAlphanumericsMask = SkTEndian_SwapBE32(1 << (42 - 32));
+                static const SK_OT_ULONG BoxDrawingMask = SkTEndian_SwapBE32(1 << (43 - 32));
+                static const SK_OT_ULONG BlockElementsMask = SkTEndian_SwapBE32(1 << (44 - 32));
+                static const SK_OT_ULONG GeometricShapesMask = SkTEndian_SwapBE32(1 << (45 - 32));
+                static const SK_OT_ULONG MiscellaneousSymbolsMask = SkTEndian_SwapBE32(1 << (46 - 32));
+                static const SK_OT_ULONG DingbatsMask = SkTEndian_SwapBE32(1 << (47 - 32));
+                static const SK_OT_ULONG CJKSymbolsAndPunctuationMask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG HiraganaMask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG KatakanaMask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG BopomofoMask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG HangulCompatibilityJamoMask = SkTEndian_SwapBE32(1 << (52 - 32));
+                static const SK_OT_ULONG CJKMiscellaneousMask = SkTEndian_SwapBE32(1 << (53 - 32));
+                static const SK_OT_ULONG EnclosedCJKLettersAndMonthsMask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG CJKCompatibilityMask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG HangulMask = SkTEndian_SwapBE32(1 << (56 - 32));
+                //Reserved
+                //Reserved
+                static const SK_OT_ULONG CJKUnifiedIdeographsMask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG PrivateUseAreaMask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG CJKCompatibilityIdeographsMask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG AlphabeticPresentationFormsMask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG ArabicPresentationFormsAMask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            struct l2 {
+                static const SK_OT_ULONG CombiningHalfMarksMask = SkTEndian_SwapBE32(1 << (64 - 64));
+                static const SK_OT_ULONG CJKCompatibilityFormsMask = SkTEndian_SwapBE32(1 << (65 - 64));
+                static const SK_OT_ULONG SmallFormVariantsMask = SkTEndian_SwapBE32(1 << (66 - 64));
+                static const SK_OT_ULONG ArabicPresentationFormsBMask = SkTEndian_SwapBE32(1 << (67 - 64));
+                static const SK_OT_ULONG HalfwidthAndFullwidthFormsMask = SkTEndian_SwapBE32(1 << (68 - 64));
+                static const SK_OT_ULONG SpecialsMask = SkTEndian_SwapBE32(1 << (69 - 64));
+            };
+            SK_OT_ULONG value[4];
+        } raw;
+    } ulUnicodeRange;
+    SK_OT_CHAR achVendID[4];
+    union Selection {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Italic,
+                Underscore,
+                Negative,
+                Outlined,
+                Strikeout,
+                Bold,
+                Regular,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT ItalicMask = SkTEndian_SwapBE16(1 << 0);
+            static const SK_OT_USHORT UnderscoreMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT NegativeMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT OutlinedMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT StrikeoutMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT BoldMask = SkTEndian_SwapBE16(1 << 5);
+            static const SK_OT_USHORT RegularMask = SkTEndian_SwapBE16(1 << 6);
+            SK_OT_USHORT value;
+        } raw;
+    } fsSelection;
+    SK_OT_USHORT usFirstCharIndex;
+    SK_OT_USHORT usLastCharIndex;
+    //version0
+    SK_OT_SHORT sTypoAscender;
+    SK_OT_SHORT sTypoDescender;
+    SK_OT_SHORT sTypoLineGap;
+    SK_OT_USHORT usWinAscent;
+    SK_OT_USHORT usWinDescent;
+    //version1
+    union CodePageRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved24,
+                Reserved25,
+                Reserved26,
+                Reserved27,
+                Reserved28,
+                MacintoshCharacterSet,
+                OEMCharacterSet,
+                SymbolCharacterSet)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Thai_874,
+                JISJapan_932,
+                ChineseSimplified_936,
+                KoreanWansung_949,
+                ChineseTraditional_950,
+                KoreanJohab_1361,
+                Reserved22,
+                Reserved23)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                Latin1_1252,
+                Latin2EasternEurope_1250,
+                Cyrillic_1251,
+                Greek_1253,
+                Turkish_1254,
+                Hebrew_1255,
+                Arabic_1256,
+                WindowsBaltic_1257)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                IBMTurkish_857,
+                IBMCyrillic_855,
+                Latin2_852,
+                MSDOSBaltic_775,
+                Greek_737,
+                Arabic_708,
+                WELatin1_850,
+                US_437)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                IBMGreek_869,
+                MSDOSRussian_866,
+                MSDOSNordic_865,
+                Arabic_864,
+                MSDOSCanadianFrench_863,
+                Hebrew_862,
+                MSDOSIcelandic_861,
+                MSDOSPortuguese_860)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved40,
+                Reserved41,
+                Reserved42,
+                Reserved43,
+                Reserved44,
+                Reserved45,
+                Reserved46,
+                Reserved47)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved32,
+                Reserved33,
+                Reserved34,
+                Reserved35,
+                Reserved36,
+                Reserved37,
+                Reserved38,
+                Reserved39)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG Latin1_1252Mask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin2EasternEurope_1250Mask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG Cyrillic_1251Mask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG Greek_1253Mask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG Turkish_1254Mask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG Hebrew_1255Mask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG Arabic_1256Mask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG WindowsBaltic_1257Mask = SkTEndian_SwapBE32(1 << 7);
+                static const SK_OT_ULONG Thai_874Mask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG JISJapan_932Mask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG ChineseSimplified_936Mask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG KoreanWansung_949Mask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG ChineseTraditional_950Mask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG KoreanJohab_1361Mask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG MacintoshCharacterSetMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG OEMCharacterSetMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG SymbolCharacterSetMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG IBMGreek_869Mask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG MSDOSRussian_866Mask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG MSDOSNordic_865Mask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG Arabic_864Mask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG MSDOSCanadianFrench_863Mask = SkTEndian_SwapBE32(1 << (52 - 32));
+                static const SK_OT_ULONG Hebrew_862Mask = SkTEndian_SwapBE32(1 << (53 - 32));
+                static const SK_OT_ULONG MSDOSIcelandic_861Mask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG MSDOSPortuguese_860Mask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG IBMTurkish_857Mask = SkTEndian_SwapBE32(1 << (56 - 32));
+                static const SK_OT_ULONG IBMCyrillic_855Mask = SkTEndian_SwapBE32(1 << (57 - 32));
+                static const SK_OT_ULONG Latin2_852Mask = SkTEndian_SwapBE32(1 << (58 - 32));
+                static const SK_OT_ULONG MSDOSBaltic_775Mask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG Greek_737Mask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG Arabic_708Mask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG WELatin1_850Mask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG US_437Mask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            SK_OT_ULONG value[2];
+        } raw;
+    } ulCodePageRange;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2_V1) == 86, sizeof_SkOTTableOS2_V1_not_86);
+
+#endif
diff --git a/src/sfnt/SkOTTable_OS_2_V2.h b/src/sfnt/SkOTTable_OS_2_V2.h
new file mode 100644
index 0000000..4be2e19
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V2.h
@@ -0,0 +1,540 @@
+/*
+ * 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 SkOTTable_OS_2_V2_DEFINED
+#define SkOTTable_OS_2_V2_DEFINED
+
+#include "SkEndian.h"
+#include "SkIBMFamilyClass.h"
+#include "SkOTTableTypes.h"
+#include "SkPanose.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableOS2_V2 {
+    SK_OT_USHORT version;
+    static const SK_OT_USHORT VERSION = SkTEndian_SwapBE16(2);
+
+    SK_OT_SHORT xAvgCharWidth;
+    struct WeightClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((Thin, SkTEndian_SwapBE16(100)))
+            ((ExtraLight, SkTEndian_SwapBE16(200)))
+            ((Light, SkTEndian_SwapBE16(300)))
+            ((Normal, SkTEndian_SwapBE16(400)))
+            ((Medium, SkTEndian_SwapBE16(500)))
+            ((SemiBold, SkTEndian_SwapBE16(600)))
+            ((Bold, SkTEndian_SwapBE16(700)))
+            ((ExtraBold, SkTEndian_SwapBE16(800)))
+            ((Black, SkTEndian_SwapBE16(900)))
+            SK_SEQ_END,
+        SK_SEQ_END)
+        SK_OT_USHORT value;
+    } usWeightClass;
+    struct WidthClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((UltraCondensed, SkTEndian_SwapBE16(1)))
+            ((ExtraCondensed, SkTEndian_SwapBE16(2)))
+            ((Condensed, SkTEndian_SwapBE16(3)))
+            ((SemiCondensed, SkTEndian_SwapBE16(4)))
+            ((Medium, SkTEndian_SwapBE16(5)))
+            ((SemiExpanded, SkTEndian_SwapBE16(6)))
+            ((Expanded, SkTEndian_SwapBE16(7)))
+            ((ExtraExpanded, SkTEndian_SwapBE16(8)))
+            ((UltraExpanded, SkTEndian_SwapBE16(9))),
+        (value)SK_SEQ_END)
+    } usWidthClass;
+    union Type {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                NoSubsetting,
+                Bitmap,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved00,
+                Restricted,
+                PreviewPrint,
+                Editable,
+                Reserved04,
+                Reserved05,
+                Reserved06,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT Installable = SkTEndian_SwapBE16(0);
+            static const SK_OT_USHORT RestrictedMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT PreviewPrintMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT EditableMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT NoSubsettingMask = SkTEndian_SwapBE16(1 << 8);
+            static const SK_OT_USHORT BitmapMask = SkTEndian_SwapBE16(1 << 9);
+            SK_OT_USHORT value;
+        } raw;
+    } fsType;
+    SK_OT_SHORT ySubscriptXSize;
+    SK_OT_SHORT ySubscriptYSize;
+    SK_OT_SHORT ySubscriptXOffset;
+    SK_OT_SHORT ySubscriptYOffset;
+    SK_OT_SHORT ySuperscriptXSize;
+    SK_OT_SHORT ySuperscriptYSize;
+    SK_OT_SHORT ySuperscriptXOffset;
+    SK_OT_SHORT ySuperscriptYOffset;
+    SK_OT_SHORT yStrikeoutSize;
+    SK_OT_SHORT yStrikeoutPosition;
+    SkIBMFamilyClass sFamilyClass;
+    SkPanose panose;
+    union UnicodeRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Thai,
+                Lao,
+                Georgian,
+                Reserved027,
+                HangulJamo,
+                LatinExtendedAdditional,
+                GreekExtended,
+                GeneralPunctuation)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Bengali,
+                Gurmukhi,
+                Gujarati,
+                Oriya,
+                Tamil,
+                Telugu,
+                Kannada,
+                Malayalam)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved008,
+                Cyrillic,
+                Armenian,
+                Hebrew,
+                Reserved012,
+                Arabic,
+                Reserved014,
+                Devanagari)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                BasicLatin,
+                Latin1Supplement,
+                LatinExtendedA,
+                LatinExtendedB,
+                IPAExtensions,
+                SpacingModifierLetters,
+                CombiningDiacriticalMarks,
+                Greek)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                Hangul,
+                Surrogates,
+                Reserved058,
+                CJKUnifiedIdeographs,
+                PrivateUseArea,
+                CJKCompatibilityIdeographs,
+                AlphabeticPresentationForms,
+                ArabicPresentationFormsA)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                CJKSymbolsAndPunctuation,
+                Hiragana,
+                Katakana,
+                Bopomofo,
+                HangulCompatibilityJamo,
+                CJKMiscellaneous,
+                EnclosedCJKLettersAndMonths,
+                CJKCompatibility)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                ControlPictures,
+                OpticalCharacterRecognition,
+                EnclosedAlphanumerics,
+                BoxDrawing,
+                BlockElements,
+                GeometricShapes,
+                MiscellaneousSymbols,
+                Dingbats)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                SuperscriptsAndSubscripts,
+                CurrencySymbols,
+                CombiningDiacriticalMarksForSymbols,
+                LetterlikeSymbols,
+                NumberForms,
+                Arrows,
+                MathematicalOperators,
+                MiscellaneousTechnical)
+
+            //l2 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved088,
+                Reserved089,
+                Reserved090,
+                Reserved091,
+                Reserved092,
+                Reserved093,
+                Reserved094,
+                Reserved095)
+            //l2 16-23
+            SK_OT_BYTE_BITFIELD(
+                Khmer,
+                Mongolian,
+                Braille,
+                Yi,
+                Reserved084,
+                Reserved085,
+                Reserved086,
+                Reserved087)
+            //l2 8-15
+            SK_OT_BYTE_BITFIELD(
+                Thaana,
+                Sinhala,
+                Myanmar,
+                Ethiopic,
+                Cherokee,
+                UnifiedCanadianSyllabics,
+                Ogham,
+                Runic)
+            //l2 0-7
+            SK_OT_BYTE_BITFIELD(
+                CombiningHalfMarks,
+                CJKCompatibilityForms,
+                SmallFormVariants,
+                ArabicPresentationFormsB,
+                HalfwidthAndFullwidthForms,
+                Specials,
+                Tibetan,
+                Syriac)
+
+            //l3 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved120,
+                Reserved121,
+                Reserved122,
+                Reserved123,
+                Reserved124,
+                Reserved125,
+                Reserved126,
+                Reserved127)
+            //l3 16-23
+            SK_OT_BYTE_BITFIELD(
+                Reserved112,
+                Reserved113,
+                Reserved114,
+                Reserved115,
+                Reserved116,
+                Reserved117,
+                Reserved118,
+                Reserved119)
+            //l3 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved104,
+                Reserved105,
+                Reserved106,
+                Reserved107,
+                Reserved108,
+                Reserved109,
+                Reserved110,
+                Reserved111)
+            //l3 0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved096,
+                Reserved097,
+                Reserved098,
+                Reserved099,
+                Reserved100,
+                Reserved101,
+                Reserved102,
+                Reserved103)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG BasicLatinMask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin1SupplementMask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG LatinExtendedAMask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG LatinExtendedBMask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG IPAExtensionsMask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG SpacingModifierLettersMask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG CombiningDiacriticalMarksMask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG GreekMask = SkTEndian_SwapBE32(1 << 7);
+                //Reserved
+                static const SK_OT_ULONG CyrillicMask = SkTEndian_SwapBE32(1 << 9);
+                static const SK_OT_ULONG ArmenianMask = SkTEndian_SwapBE32(1 << 10);
+                static const SK_OT_ULONG HebrewMask = SkTEndian_SwapBE32(1 << 11);
+                //Reserved
+                static const SK_OT_ULONG ArabicMask = SkTEndian_SwapBE32(1 << 13);
+                //Reserved
+                static const SK_OT_ULONG DevanagariMask = SkTEndian_SwapBE32(1 << 15);
+                static const SK_OT_ULONG BengaliMask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG GurmukhiMask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG GujaratiMask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG OriyaMask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG TamilMask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG TeluguMask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG KannadaMask = SkTEndian_SwapBE32(1 << 22);
+                static const SK_OT_ULONG MalayalamMask = SkTEndian_SwapBE32(1 << 23);
+                static const SK_OT_ULONG ThaiMask = SkTEndian_SwapBE32(1 << 24);
+                static const SK_OT_ULONG LaoMask = SkTEndian_SwapBE32(1 << 25);
+                static const SK_OT_ULONG GeorgianMask = SkTEndian_SwapBE32(1 << 26);
+                //Reserved
+                static const SK_OT_ULONG HangulJamoMask = SkTEndian_SwapBE32(1 << 28);
+                static const SK_OT_ULONG LatinExtendedAdditionalMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG GreekExtendedMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG GeneralPunctuationMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG SuperscriptsAndSubscriptsMask = SkTEndian_SwapBE32(1 << (32 - 32));
+                static const SK_OT_ULONG CurrencySymbolsMask = SkTEndian_SwapBE32(1 << (33 - 32));
+                static const SK_OT_ULONG CombiningDiacriticalMarksForSymbolsMask = SkTEndian_SwapBE32(1 << (34 - 32));
+                static const SK_OT_ULONG LetterlikeSymbolsMask = SkTEndian_SwapBE32(1 << (35 - 32));
+                static const SK_OT_ULONG NumberFormsMask = SkTEndian_SwapBE32(1 << (36 - 32));
+                static const SK_OT_ULONG ArrowsMask = SkTEndian_SwapBE32(1 << (37 - 32));
+                static const SK_OT_ULONG MathematicalOperatorsMask = SkTEndian_SwapBE32(1 << (38 - 32));
+                static const SK_OT_ULONG MiscellaneousTechnicalMask = SkTEndian_SwapBE32(1 << (39 - 32));
+                static const SK_OT_ULONG ControlPicturesMask = SkTEndian_SwapBE32(1 << (40 - 32));
+                static const SK_OT_ULONG OpticalCharacterRecognitionMask = SkTEndian_SwapBE32(1 << (41 - 32));
+                static const SK_OT_ULONG EnclosedAlphanumericsMask = SkTEndian_SwapBE32(1 << (42 - 32));
+                static const SK_OT_ULONG BoxDrawingMask = SkTEndian_SwapBE32(1 << (43 - 32));
+                static const SK_OT_ULONG BlockElementsMask = SkTEndian_SwapBE32(1 << (44 - 32));
+                static const SK_OT_ULONG GeometricShapesMask = SkTEndian_SwapBE32(1 << (45 - 32));
+                static const SK_OT_ULONG MiscellaneousSymbolsMask = SkTEndian_SwapBE32(1 << (46 - 32));
+                static const SK_OT_ULONG DingbatsMask = SkTEndian_SwapBE32(1 << (47 - 32));
+                static const SK_OT_ULONG CJKSymbolsAndPunctuationMask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG HiraganaMask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG KatakanaMask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG BopomofoMask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG HangulCompatibilityJamoMask = SkTEndian_SwapBE32(1 << (52 - 32));
+                static const SK_OT_ULONG CJKMiscellaneousMask = SkTEndian_SwapBE32(1 << (53 - 32));
+                static const SK_OT_ULONG EnclosedCJKLettersAndMonthsMask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG CJKCompatibilityMask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG HangulMask = SkTEndian_SwapBE32(1 << (56 - 32));
+                static const SK_OT_ULONG SurrogatesMask = SkTEndian_SwapBE32(1 << (57 - 32));
+                //Reserved
+                static const SK_OT_ULONG CJKUnifiedIdeographsMask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG PrivateUseAreaMask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG CJKCompatibilityIdeographsMask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG AlphabeticPresentationFormsMask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG ArabicPresentationFormsAMask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            struct l2 {
+                static const SK_OT_ULONG CombiningHalfMarksMask = SkTEndian_SwapBE32(1 << (64 - 64));
+                static const SK_OT_ULONG CJKCompatibilityFormsMask = SkTEndian_SwapBE32(1 << (65 - 64));
+                static const SK_OT_ULONG SmallFormVariantsMask = SkTEndian_SwapBE32(1 << (66 - 64));
+                static const SK_OT_ULONG ArabicPresentationFormsBMask = SkTEndian_SwapBE32(1 << (67 - 64));
+                static const SK_OT_ULONG HalfwidthAndFullwidthFormsMask = SkTEndian_SwapBE32(1 << (68 - 64));
+                static const SK_OT_ULONG SpecialsMask = SkTEndian_SwapBE32(1 << (69 - 64));
+                static const SK_OT_ULONG TibetanMask = SkTEndian_SwapBE32(1 << (70 - 64));
+                static const SK_OT_ULONG SyriacMask = SkTEndian_SwapBE32(1 << (71 - 64));
+                static const SK_OT_ULONG ThaanaMask = SkTEndian_SwapBE32(1 << (72 - 64));
+                static const SK_OT_ULONG SinhalaMask = SkTEndian_SwapBE32(1 << (73 - 64));
+                static const SK_OT_ULONG MyanmarMask = SkTEndian_SwapBE32(1 << (74 - 64));
+                static const SK_OT_ULONG EthiopicMask = SkTEndian_SwapBE32(1 << (75 - 64));
+                static const SK_OT_ULONG CherokeeMask = SkTEndian_SwapBE32(1 << (76 - 64));
+                static const SK_OT_ULONG UnifiedCanadianSyllabicsMask = SkTEndian_SwapBE32(1 << (77 - 64));
+                static const SK_OT_ULONG OghamMask = SkTEndian_SwapBE32(1 << (78 - 64));
+                static const SK_OT_ULONG RunicMask = SkTEndian_SwapBE32(1 << (79 - 64));
+                static const SK_OT_ULONG KhmerMask = SkTEndian_SwapBE32(1 << (80 - 64));
+                static const SK_OT_ULONG MongolianMask = SkTEndian_SwapBE32(1 << (81 - 64));
+                static const SK_OT_ULONG BrailleMask = SkTEndian_SwapBE32(1 << (82 - 64));
+                static const SK_OT_ULONG YiMask = SkTEndian_SwapBE32(1 << (83 - 64));
+            };
+            SK_OT_ULONG value[4];
+        } raw;
+    } ulUnicodeRange;
+    SK_OT_CHAR achVendID[4];
+    union Selection {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Italic,
+                Underscore,
+                Negative,
+                Outlined,
+                Strikeout,
+                Bold,
+                Regular,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT ItalicMask = SkTEndian_SwapBE16(1 << 0);
+            static const SK_OT_USHORT UnderscoreMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT NegativeMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT OutlinedMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT StrikeoutMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT BoldMask = SkTEndian_SwapBE16(1 << 5);
+            static const SK_OT_USHORT RegularMask = SkTEndian_SwapBE16(1 << 6);
+            SK_OT_USHORT value;
+        } raw;
+    } fsSelection;
+    SK_OT_USHORT usFirstCharIndex;
+    SK_OT_USHORT usLastCharIndex;
+    //version0
+    SK_OT_SHORT sTypoAscender;
+    SK_OT_SHORT sTypoDescender;
+    SK_OT_SHORT sTypoLineGap;
+    SK_OT_USHORT usWinAscent;
+    SK_OT_USHORT usWinDescent;
+    //version1
+    union CodePageRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved24,
+                Reserved25,
+                Reserved26,
+                Reserved27,
+                Reserved28,
+                MacintoshCharacterSet,
+                OEMCharacterSet,
+                SymbolCharacterSet)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Thai_874,
+                JISJapan_932,
+                ChineseSimplified_936,
+                KoreanWansung_949,
+                ChineseTraditional_950,
+                KoreanJohab_1361,
+                Reserved22,
+                Reserved23)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                Vietnamese,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                Latin1_1252,
+                Latin2EasternEurope_1250,
+                Cyrillic_1251,
+                Greek_1253,
+                Turkish_1254,
+                Hebrew_1255,
+                Arabic_1256,
+                WindowsBaltic_1257)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                IBMTurkish_857,
+                IBMCyrillic_855,
+                Latin2_852,
+                MSDOSBaltic_775,
+                Greek_737,
+                Arabic_708,
+                WELatin1_850,
+                US_437)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                IBMGreek_869,
+                MSDOSRussian_866,
+                MSDOSNordic_865,
+                Arabic_864,
+                MSDOSCanadianFrench_863,
+                Hebrew_862,
+                MSDOSIcelandic_861,
+                MSDOSPortuguese_860)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved40,
+                Reserved41,
+                Reserved42,
+                Reserved43,
+                Reserved44,
+                Reserved45,
+                Reserved46,
+                Reserved47)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved32,
+                Reserved33,
+                Reserved34,
+                Reserved35,
+                Reserved36,
+                Reserved37,
+                Reserved38,
+                Reserved39)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG Latin1_1252Mask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin2EasternEurope_1250Mask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG Cyrillic_1251Mask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG Greek_1253Mask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG Turkish_1254Mask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG Hebrew_1255Mask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG Arabic_1256Mask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG WindowsBaltic_1257Mask = SkTEndian_SwapBE32(1 << 7);
+                static const SK_OT_ULONG Vietnamese_1258Mask = SkTEndian_SwapBE32(1 << 8);
+                static const SK_OT_ULONG Thai_874Mask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG JISJapan_932Mask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG ChineseSimplified_936Mask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG KoreanWansung_949Mask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG ChineseTraditional_950Mask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG KoreanJohab_1361Mask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG MacintoshCharacterSetMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG OEMCharacterSetMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG SymbolCharacterSetMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG IBMGreek_869Mask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG MSDOSRussian_866Mask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG MSDOSNordic_865Mask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG Arabic_864Mask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG MSDOSCanadianFrench_863Mask = SkTEndian_SwapBE32(1 << (52 - 32));
+                static const SK_OT_ULONG Hebrew_862Mask = SkTEndian_SwapBE32(1 << (53 - 32));
+                static const SK_OT_ULONG MSDOSIcelandic_861Mask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG MSDOSPortuguese_860Mask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG IBMTurkish_857Mask = SkTEndian_SwapBE32(1 << (56 - 32));
+                static const SK_OT_ULONG IBMCyrillic_855Mask = SkTEndian_SwapBE32(1 << (57 - 32));
+                static const SK_OT_ULONG Latin2_852Mask = SkTEndian_SwapBE32(1 << (58 - 32));
+                static const SK_OT_ULONG MSDOSBaltic_775Mask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG Greek_737Mask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG Arabic_708Mask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG WELatin1_850Mask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG US_437Mask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            SK_OT_ULONG value[2];
+        } raw;
+    } ulCodePageRange;
+    //version2
+    SK_OT_SHORT sxHeight;
+    SK_OT_SHORT sCapHeight;
+    SK_OT_USHORT usDefaultChar;
+    SK_OT_USHORT usBreakChar;
+    SK_OT_USHORT usMaxContext;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2_V2) == 96, sizeof_SkOTTableOS2_V2_not_96);
+
+#endif
diff --git a/src/sfnt/SkOTTable_OS_2_V3.h b/src/sfnt/SkOTTable_OS_2_V3.h
new file mode 100644
index 0000000..637eb37
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V3.h
@@ -0,0 +1,550 @@
+/*
+ * 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 SkOTTable_OS_2_V3_DEFINED
+#define SkOTTable_OS_2_V3_DEFINED
+
+#include "SkEndian.h"
+#include "SkIBMFamilyClass.h"
+#include "SkOTTableTypes.h"
+#include "SkPanose.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableOS2_V3 {
+    SK_OT_USHORT version;
+    static const SK_OT_USHORT VERSION = SkTEndian_SwapBE16(3);
+
+    SK_OT_SHORT xAvgCharWidth;
+    struct WeightClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((Thin, SkTEndian_SwapBE16(100)))
+            ((ExtraLight, SkTEndian_SwapBE16(200)))
+            ((Light, SkTEndian_SwapBE16(300)))
+            ((Normal, SkTEndian_SwapBE16(400)))
+            ((Medium, SkTEndian_SwapBE16(500)))
+            ((SemiBold, SkTEndian_SwapBE16(600)))
+            ((Bold, SkTEndian_SwapBE16(700)))
+            ((ExtraBold, SkTEndian_SwapBE16(800)))
+            ((Black, SkTEndian_SwapBE16(900)))
+            SK_SEQ_END,
+        SK_SEQ_END)
+        SK_OT_USHORT value;
+    } usWeightClass;
+    struct WidthClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((UltraCondensed, SkTEndian_SwapBE16(1)))
+            ((ExtraCondensed, SkTEndian_SwapBE16(2)))
+            ((Condensed, SkTEndian_SwapBE16(3)))
+            ((SemiCondensed, SkTEndian_SwapBE16(4)))
+            ((Medium, SkTEndian_SwapBE16(5)))
+            ((SemiExpanded, SkTEndian_SwapBE16(6)))
+            ((Expanded, SkTEndian_SwapBE16(7)))
+            ((ExtraExpanded, SkTEndian_SwapBE16(8)))
+            ((UltraExpanded, SkTEndian_SwapBE16(9)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } usWidthClass;
+    union Type {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                NoSubsetting,
+                Bitmap,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved00,
+                Restricted,
+                PreviewPrint,
+                Editable,
+                Reserved04,
+                Reserved05,
+                Reserved06,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT Installable = SkTEndian_SwapBE16(0);
+            static const SK_OT_USHORT RestrictedMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT PreviewPrintMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT EditableMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT NoSubsettingMask = SkTEndian_SwapBE16(1 << 8);
+            static const SK_OT_USHORT BitmapMask = SkTEndian_SwapBE16(1 << 9);
+            SK_OT_USHORT value;
+        } raw;
+    } fsType;
+    SK_OT_SHORT ySubscriptXSize;
+    SK_OT_SHORT ySubscriptYSize;
+    SK_OT_SHORT ySubscriptXOffset;
+    SK_OT_SHORT ySubscriptYOffset;
+    SK_OT_SHORT ySuperscriptXSize;
+    SK_OT_SHORT ySuperscriptYSize;
+    SK_OT_SHORT ySuperscriptXOffset;
+    SK_OT_SHORT ySuperscriptYOffset;
+    SK_OT_SHORT yStrikeoutSize;
+    SK_OT_SHORT yStrikeoutPosition;
+    SkIBMFamilyClass sFamilyClass;
+    SkPanose panose;
+    union UnicodeRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Thai,
+                Lao,
+                Georgian,
+                Reserved027,
+                HangulJamo,
+                LatinExtendedAdditional,
+                GreekExtended,
+                GeneralPunctuation)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Bengali,
+                Gurmukhi,
+                Gujarati,
+                Oriya,
+                Tamil,
+                Telugu,
+                Kannada,
+                Malayalam)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved008,
+                Cyrillic,
+                Armenian,
+                Hebrew,
+                Reserved012,
+                Arabic,
+                Reserved014,
+                Devanagari)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                BasicLatin,
+                Latin1Supplement,
+                LatinExtendedA,
+                LatinExtendedB,
+                IPAExtensions,
+                SpacingModifierLetters,
+                CombiningDiacriticalMarks,
+                GreekAndCoptic)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                Hangul,
+                NonPlane0,
+                Reserved058,
+                CJKUnifiedIdeographs,
+                PrivateUseArea,
+                CJKCompatibilityIdeographs,
+                AlphabeticPresentationForms,
+                ArabicPresentationFormsA)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                CJKSymbolsAndPunctuation,
+                Hiragana,
+                Katakana,
+                Bopomofo,
+                HangulCompatibilityJamo,
+                Reserved053,
+                EnclosedCJKLettersAndMonths,
+                CJKCompatibility)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                ControlPictures,
+                OpticalCharacterRecognition,
+                EnclosedAlphanumerics,
+                BoxDrawing,
+                BlockElements,
+                GeometricShapes,
+                MiscellaneousSymbols,
+                Dingbats)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                SuperscriptsAndSubscripts,
+                CurrencySymbols,
+                CombiningDiacriticalMarksForSymbols,
+                LetterlikeSymbols,
+                NumberForms,
+                Arrows,
+                MathematicalOperators,
+                MiscellaneousTechnical)
+
+            //l2 24-31
+            SK_OT_BYTE_BITFIELD(
+                MusicalSymbols,
+                MathematicalAlphanumericSymbols,
+                PrivateUse,
+                VariationSelectors,
+                Tags,
+                Reserved093,
+                Reserved094,
+                Reserved095)
+            //l2 16-23
+            SK_OT_BYTE_BITFIELD(
+                Khmer,
+                Mongolian,
+                Braille,
+                Yi,
+                Tagalog_Hanunoo_Buhid_Tagbanwa,
+                OldItalic,
+                Gothic,
+                Deseret)
+            //l2 8-15
+            SK_OT_BYTE_BITFIELD(
+                Thaana,
+                Sinhala,
+                Myanmar,
+                Ethiopic,
+                Cherokee,
+                UnifiedCanadianSyllabics,
+                Ogham,
+                Runic)
+            //l2 0-7
+            SK_OT_BYTE_BITFIELD(
+                CombiningHalfMarks,
+                CJKCompatibilityForms,
+                SmallFormVariants,
+                ArabicPresentationFormsB,
+                HalfwidthAndFullwidthForms,
+                Specials,
+                Tibetan,
+                Syriac)
+
+            //l3 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved120,
+                Reserved121,
+                Reserved122,
+                Reserved123,
+                Reserved124,
+                Reserved125,
+                Reserved126,
+                Reserved127)
+            //l3 16-23
+            SK_OT_BYTE_BITFIELD(
+                Reserved112,
+                Reserved113,
+                Reserved114,
+                Reserved115,
+                Reserved116,
+                Reserved117,
+                Reserved118,
+                Reserved119)
+            //l3 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved104,
+                Reserved105,
+                Reserved106,
+                Reserved107,
+                Reserved108,
+                Reserved109,
+                Reserved110,
+                Reserved111)
+            //l3 0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved096,
+                Reserved097,
+                Reserved098,
+                Reserved099,
+                Reserved100,
+                Reserved101,
+                Reserved102,
+                Reserved103)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG BasicLatinMask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin1SupplementMask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG LatinExtendedAMask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG LatinExtendedBMask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG IPAExtensionsMask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG SpacingModifierLettersMask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG CombiningDiacriticalMarksMask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG GreekAndCopticMask = SkTEndian_SwapBE32(1 << 7);
+                //Reserved
+                static const SK_OT_ULONG CyrillicMask = SkTEndian_SwapBE32(1 << 9);
+                static const SK_OT_ULONG ArmenianMask = SkTEndian_SwapBE32(1 << 10);
+                static const SK_OT_ULONG HebrewMask = SkTEndian_SwapBE32(1 << 11);
+                //Reserved
+                static const SK_OT_ULONG ArabicMask = SkTEndian_SwapBE32(1 << 13);
+                //Reserved
+                static const SK_OT_ULONG DevanagariMask = SkTEndian_SwapBE32(1 << 15);
+                static const SK_OT_ULONG BengaliMask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG GurmukhiMask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG GujaratiMask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG OriyaMask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG TamilMask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG TeluguMask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG KannadaMask = SkTEndian_SwapBE32(1 << 22);
+                static const SK_OT_ULONG MalayalamMask = SkTEndian_SwapBE32(1 << 23);
+                static const SK_OT_ULONG ThaiMask = SkTEndian_SwapBE32(1 << 24);
+                static const SK_OT_ULONG LaoMask = SkTEndian_SwapBE32(1 << 25);
+                static const SK_OT_ULONG GeorgianMask = SkTEndian_SwapBE32(1 << 26);
+                //Reserved
+                static const SK_OT_ULONG HangulJamoMask = SkTEndian_SwapBE32(1 << 28);
+                static const SK_OT_ULONG LatinExtendedAdditionalMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG GreekExtendedMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG GeneralPunctuationMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG SuperscriptsAndSubscriptsMask = SkTEndian_SwapBE32(1 << (32 - 32));
+                static const SK_OT_ULONG CurrencySymbolsMask = SkTEndian_SwapBE32(1 << (33 - 32));
+                static const SK_OT_ULONG CombiningDiacriticalMarksForSymbolsMask = SkTEndian_SwapBE32(1 << (34 - 32));
+                static const SK_OT_ULONG LetterlikeSymbolsMask = SkTEndian_SwapBE32(1 << (35 - 32));
+                static const SK_OT_ULONG NumberFormsMask = SkTEndian_SwapBE32(1 << (36 - 32));
+                static const SK_OT_ULONG ArrowsMask = SkTEndian_SwapBE32(1 << (37 - 32));
+                static const SK_OT_ULONG MathematicalOperatorsMask = SkTEndian_SwapBE32(1 << (38 - 32));
+                static const SK_OT_ULONG MiscellaneousTechnicalMask = SkTEndian_SwapBE32(1 << (39 - 32));
+                static const SK_OT_ULONG ControlPicturesMask = SkTEndian_SwapBE32(1 << (40 - 32));
+                static const SK_OT_ULONG OpticalCharacterRecognitionMask = SkTEndian_SwapBE32(1 << (41 - 32));
+                static const SK_OT_ULONG EnclosedAlphanumericsMask = SkTEndian_SwapBE32(1 << (42 - 32));
+                static const SK_OT_ULONG BoxDrawingMask = SkTEndian_SwapBE32(1 << (43 - 32));
+                static const SK_OT_ULONG BlockElementsMask = SkTEndian_SwapBE32(1 << (44 - 32));
+                static const SK_OT_ULONG GeometricShapesMask = SkTEndian_SwapBE32(1 << (45 - 32));
+                static const SK_OT_ULONG MiscellaneousSymbolsMask = SkTEndian_SwapBE32(1 << (46 - 32));
+                static const SK_OT_ULONG DingbatsMask = SkTEndian_SwapBE32(1 << (47 - 32));
+                static const SK_OT_ULONG CJKSymbolsAndPunctuationMask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG HiraganaMask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG KatakanaMask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG BopomofoMask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG HangulCompatibilityJamoMask = SkTEndian_SwapBE32(1 << (52 - 32));
+                //Reserved
+                static const SK_OT_ULONG EnclosedCJKLettersAndMonthsMask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG CJKCompatibilityMask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG HangulMask = SkTEndian_SwapBE32(1 << (56 - 32));
+                static const SK_OT_ULONG NonPlane0Mask = SkTEndian_SwapBE32(1 << (57 - 32));
+                //Reserved
+                static const SK_OT_ULONG CJKUnifiedIdeographsMask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG PrivateUseAreaMask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG CJKCompatibilityIdeographsMask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG AlphabeticPresentationFormsMask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG ArabicPresentationFormsAMask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            struct l2 {
+                static const SK_OT_ULONG CombiningHalfMarksMask = SkTEndian_SwapBE32(1 << (64 - 64));
+                static const SK_OT_ULONG CJKCompatibilityFormsMask = SkTEndian_SwapBE32(1 << (65 - 64));
+                static const SK_OT_ULONG SmallFormVariantsMask = SkTEndian_SwapBE32(1 << (66 - 64));
+                static const SK_OT_ULONG ArabicPresentationFormsBMask = SkTEndian_SwapBE32(1 << (67 - 64));
+                static const SK_OT_ULONG HalfwidthAndFullwidthFormsMask = SkTEndian_SwapBE32(1 << (68 - 64));
+                static const SK_OT_ULONG SpecialsMask = SkTEndian_SwapBE32(1 << (69 - 64));
+                static const SK_OT_ULONG TibetanMask = SkTEndian_SwapBE32(1 << (70 - 64));
+                static const SK_OT_ULONG SyriacMask = SkTEndian_SwapBE32(1 << (71 - 64));
+                static const SK_OT_ULONG ThaanaMask = SkTEndian_SwapBE32(1 << (72 - 64));
+                static const SK_OT_ULONG SinhalaMask = SkTEndian_SwapBE32(1 << (73 - 64));
+                static const SK_OT_ULONG MyanmarMask = SkTEndian_SwapBE32(1 << (74 - 64));
+                static const SK_OT_ULONG EthiopicMask = SkTEndian_SwapBE32(1 << (75 - 64));
+                static const SK_OT_ULONG CherokeeMask = SkTEndian_SwapBE32(1 << (76 - 64));
+                static const SK_OT_ULONG UnifiedCanadianSyllabicsMask = SkTEndian_SwapBE32(1 << (77 - 64));
+                static const SK_OT_ULONG OghamMask = SkTEndian_SwapBE32(1 << (78 - 64));
+                static const SK_OT_ULONG RunicMask = SkTEndian_SwapBE32(1 << (79 - 64));
+                static const SK_OT_ULONG KhmerMask = SkTEndian_SwapBE32(1 << (80 - 64));
+                static const SK_OT_ULONG MongolianMask = SkTEndian_SwapBE32(1 << (81 - 64));
+                static const SK_OT_ULONG BrailleMask = SkTEndian_SwapBE32(1 << (82 - 64));
+                static const SK_OT_ULONG YiMask = SkTEndian_SwapBE32(1 << (83 - 64));
+                static const SK_OT_ULONG Tagalog_Hanunoo_Buhid_TagbanwaMask = SkTEndian_SwapBE32(1 << (84 - 64));
+                static const SK_OT_ULONG OldItalicMask = SkTEndian_SwapBE32(1 << (85 - 64));
+                static const SK_OT_ULONG GothicMask = SkTEndian_SwapBE32(1 << (86 - 64));
+                static const SK_OT_ULONG DeseretMask = SkTEndian_SwapBE32(1 << (87 - 64));
+                static const SK_OT_ULONG MusicalSymbolsMask = SkTEndian_SwapBE32(1 << (88 - 64));
+                static const SK_OT_ULONG MathematicalAlphanumericSymbolsMask = SkTEndian_SwapBE32(1 << (89 - 64));
+                static const SK_OT_ULONG PrivateUseMask = SkTEndian_SwapBE32(1 << (90 - 64));
+                static const SK_OT_ULONG VariationSelectorsMask = SkTEndian_SwapBE32(1 << (91 - 64));
+                static const SK_OT_ULONG TagsMask = SkTEndian_SwapBE32(1 << (92 - 64));
+            };
+            SK_OT_ULONG value[4];
+        } raw;
+    } ulUnicodeRange;
+    SK_OT_CHAR achVendID[4];
+    union Selection {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Italic,
+                Underscore,
+                Negative,
+                Outlined,
+                Strikeout,
+                Bold,
+                Regular,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT ItalicMask = SkTEndian_SwapBE16(1 << 0);
+            static const SK_OT_USHORT UnderscoreMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT NegativeMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT OutlinedMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT StrikeoutMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT BoldMask = SkTEndian_SwapBE16(1 << 5);
+            static const SK_OT_USHORT RegularMask = SkTEndian_SwapBE16(1 << 6);
+            SK_OT_USHORT value;
+        } raw;
+    } fsSelection;
+    SK_OT_USHORT usFirstCharIndex;
+    SK_OT_USHORT usLastCharIndex;
+    //version0
+    SK_OT_SHORT sTypoAscender;
+    SK_OT_SHORT sTypoDescender;
+    SK_OT_SHORT sTypoLineGap;
+    SK_OT_USHORT usWinAscent;
+    SK_OT_USHORT usWinDescent;
+    //version1
+    union CodePageRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved24,
+                Reserved25,
+                Reserved26,
+                Reserved27,
+                Reserved28,
+                MacintoshCharacterSet,
+                OEMCharacterSet,
+                SymbolCharacterSet)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Thai_874,
+                JISJapan_932,
+                ChineseSimplified_936,
+                KoreanWansung_949,
+                ChineseTraditional_950,
+                KoreanJohab_1361,
+                Reserved22,
+                Reserved23)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                Vietnamese,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                Latin1_1252,
+                Latin2EasternEurope_1250,
+                Cyrillic_1251,
+                Greek_1253,
+                Turkish_1254,
+                Hebrew_1255,
+                Arabic_1256,
+                WindowsBaltic_1257)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                IBMTurkish_857,
+                IBMCyrillic_855,
+                Latin2_852,
+                MSDOSBaltic_775,
+                Greek_737,
+                Arabic_708,
+                WELatin1_850,
+                US_437)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                IBMGreek_869,
+                MSDOSRussian_866,
+                MSDOSNordic_865,
+                Arabic_864,
+                MSDOSCanadianFrench_863,
+                Hebrew_862,
+                MSDOSIcelandic_861,
+                MSDOSPortuguese_860)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved40,
+                Reserved41,
+                Reserved42,
+                Reserved43,
+                Reserved44,
+                Reserved45,
+                Reserved46,
+                Reserved47)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved32,
+                Reserved33,
+                Reserved34,
+                Reserved35,
+                Reserved36,
+                Reserved37,
+                Reserved38,
+                Reserved39)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG Latin1_1252Mask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin2EasternEurope_1250Mask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG Cyrillic_1251Mask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG Greek_1253Mask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG Turkish_1254Mask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG Hebrew_1255Mask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG Arabic_1256Mask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG WindowsBaltic_1257Mask = SkTEndian_SwapBE32(1 << 7);
+                static const SK_OT_ULONG Vietnamese_1258Mask = SkTEndian_SwapBE32(1 << 8);
+                static const SK_OT_ULONG Thai_874Mask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG JISJapan_932Mask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG ChineseSimplified_936Mask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG KoreanWansung_949Mask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG ChineseTraditional_950Mask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG KoreanJohab_1361Mask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG MacintoshCharacterSetMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG OEMCharacterSetMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG SymbolCharacterSetMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG IBMGreek_869Mask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG MSDOSRussian_866Mask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG MSDOSNordic_865Mask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG Arabic_864Mask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG MSDOSCanadianFrench_863Mask = SkTEndian_SwapBE32(1 << (52 - 32));
+                static const SK_OT_ULONG Hebrew_862Mask = SkTEndian_SwapBE32(1 << (53 - 32));
+                static const SK_OT_ULONG MSDOSIcelandic_861Mask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG MSDOSPortuguese_860Mask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG IBMTurkish_857Mask = SkTEndian_SwapBE32(1 << (56 - 32));
+                static const SK_OT_ULONG IBMCyrillic_855Mask = SkTEndian_SwapBE32(1 << (57 - 32));
+                static const SK_OT_ULONG Latin2_852Mask = SkTEndian_SwapBE32(1 << (58 - 32));
+                static const SK_OT_ULONG MSDOSBaltic_775Mask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG Greek_737Mask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG Arabic_708Mask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG WELatin1_850Mask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG US_437Mask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            SK_OT_ULONG value[2];
+        } raw;
+    } ulCodePageRange;
+    //version2
+    SK_OT_SHORT sxHeight;
+    SK_OT_SHORT sCapHeight;
+    SK_OT_USHORT usDefaultChar;
+    SK_OT_USHORT usBreakChar;
+    SK_OT_USHORT usMaxContext;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2_V3) == 96, sizeof_SkOTTableOS2_V3_not_96);
+
+#endif
diff --git a/src/sfnt/SkOTTable_OS_2_V4.h b/src/sfnt/SkOTTable_OS_2_V4.h
new file mode 100644
index 0000000..fc6ed5d
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_V4.h
@@ -0,0 +1,585 @@
+/*
+ * 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 SkOTTable_OS_2_V4_DEFINED
+#define SkOTTable_OS_2_V4_DEFINED
+
+#include "SkEndian.h"
+#include "SkIBMFamilyClass.h"
+#include "SkOTTableTypes.h"
+#include "SkPanose.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableOS2_V4 {
+    SK_OT_USHORT version;
+    static const SK_OT_USHORT VERSION = SkTEndian_SwapBE16(4);
+
+    SK_OT_SHORT xAvgCharWidth;
+    struct WeightClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((Thin, SkTEndian_SwapBE16(100)))
+            ((ExtraLight, SkTEndian_SwapBE16(200)))
+            ((Light, SkTEndian_SwapBE16(300)))
+            ((Normal, SkTEndian_SwapBE16(400)))
+            ((Medium, SkTEndian_SwapBE16(500)))
+            ((SemiBold, SkTEndian_SwapBE16(600)))
+            ((Bold, SkTEndian_SwapBE16(700)))
+            ((ExtraBold, SkTEndian_SwapBE16(800)))
+            ((Black, SkTEndian_SwapBE16(900)))
+            SK_SEQ_END,
+        SK_SEQ_END)
+        SK_OT_USHORT value;
+    } usWeightClass;
+    struct WidthClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((UltraCondensed, SkTEndian_SwapBE16(1)))
+            ((ExtraCondensed, SkTEndian_SwapBE16(2)))
+            ((Condensed, SkTEndian_SwapBE16(3)))
+            ((SemiCondensed, SkTEndian_SwapBE16(4)))
+            ((Medium, SkTEndian_SwapBE16(5)))
+            ((SemiExpanded, SkTEndian_SwapBE16(6)))
+            ((Expanded, SkTEndian_SwapBE16(7)))
+            ((ExtraExpanded, SkTEndian_SwapBE16(8)))
+            ((UltraExpanded, SkTEndian_SwapBE16(9)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } usWidthClass;
+    union Type {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                NoSubsetting,
+                Bitmap,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved00,
+                Restricted,
+                PreviewPrint,
+                Editable,
+                Reserved04,
+                Reserved05,
+                Reserved06,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT Installable = SkTEndian_SwapBE16(0);
+            static const SK_OT_USHORT RestrictedMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT PreviewPrintMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT EditableMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT NoSubsettingMask = SkTEndian_SwapBE16(1 << 8);
+            static const SK_OT_USHORT BitmapMask = SkTEndian_SwapBE16(1 << 9);
+            SK_OT_USHORT value;
+        } raw;
+    } fsType;
+    SK_OT_SHORT ySubscriptXSize;
+    SK_OT_SHORT ySubscriptYSize;
+    SK_OT_SHORT ySubscriptXOffset;
+    SK_OT_SHORT ySubscriptYOffset;
+    SK_OT_SHORT ySuperscriptXSize;
+    SK_OT_SHORT ySuperscriptYSize;
+    SK_OT_SHORT ySuperscriptXOffset;
+    SK_OT_SHORT ySuperscriptYOffset;
+    SK_OT_SHORT yStrikeoutSize;
+    SK_OT_SHORT yStrikeoutPosition;
+    SkIBMFamilyClass sFamilyClass;
+    SkPanose panose;
+    union UnicodeRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Thai,
+                Lao,
+                Georgian,
+                Balinese,
+                HangulJamo,
+                LatinExtendedAdditional,
+                GreekExtended,
+                GeneralPunctuation)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Bengali,
+                Gurmukhi,
+                Gujarati,
+                Oriya,
+                Tamil,
+                Telugu,
+                Kannada,
+                Malayalam)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                Coptic,
+                Cyrillic,
+                Armenian,
+                Hebrew,
+                Vai,
+                Arabic,
+                NKo,
+                Devanagari)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                BasicLatin,
+                Latin1Supplement,
+                LatinExtendedA,
+                LatinExtendedB,
+                IPAExtensions,
+                SpacingModifierLetters,
+                CombiningDiacriticalMarks,
+                GreekAndCoptic)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                Hangul,
+                NonPlane0,
+                Phoenician,
+                CJKUnifiedIdeographs,
+                PrivateUseArea,
+                CJKCompatibilityIdeographs,
+                AlphabeticPresentationForms,
+                ArabicPresentationFormsA)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                CJKSymbolsAndPunctuation,
+                Hiragana,
+                Katakana,
+                Bopomofo,
+                HangulCompatibilityJamo,
+                PhagsPa,
+                EnclosedCJKLettersAndMonths,
+                CJKCompatibility)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                ControlPictures,
+                OpticalCharacterRecognition,
+                EnclosedAlphanumerics,
+                BoxDrawing,
+                BlockElements,
+                GeometricShapes,
+                MiscellaneousSymbols,
+                Dingbats)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                SuperscriptsAndSubscripts,
+                CurrencySymbols,
+                CombiningDiacriticalMarksForSymbols,
+                LetterlikeSymbols,
+                NumberForms,
+                Arrows,
+                MathematicalOperators,
+                MiscellaneousTechnical)
+
+            //l2 24-31
+            SK_OT_BYTE_BITFIELD(
+                MusicalSymbols,
+                MathematicalAlphanumericSymbols,
+                PrivateUse,
+                VariationSelectors,
+                Tags,
+                Limbu,
+                TaiLe,
+                NewTaiLue)
+            //l2 16-23
+            SK_OT_BYTE_BITFIELD(
+                Khmer,
+                Mongolian,
+                Braille,
+                Yi,
+                Tagalog_Hanunoo_Buhid_Tagbanwa,
+                OldItalic,
+                Gothic,
+                Deseret)
+            //l2 8-15
+            SK_OT_BYTE_BITFIELD(
+                Thaana,
+                Sinhala,
+                Myanmar,
+                Ethiopic,
+                Cherokee,
+                UnifiedCanadianSyllabics,
+                Ogham,
+                Runic)
+            //l2 0-7
+            SK_OT_BYTE_BITFIELD(
+                CombiningHalfMarks,
+                CJKCompatibilityForms,
+                SmallFormVariants,
+                ArabicPresentationFormsB,
+                HalfwidthAndFullwidthForms,
+                Specials,
+                Tibetan,
+                Syriac)
+
+            //l3 24-31
+            SK_OT_BYTE_BITFIELD(
+                PhaistosDisc,
+                Carian_Lycian_Lydian,
+                DominoTiles_MahjongTiles,
+                Reserved123,
+                Reserved124,
+                Reserved125,
+                Reserved126,
+                Reserved127)
+            //l3 16-23
+            SK_OT_BYTE_BITFIELD(
+                Sundanese,
+                Lepcha,
+                OlChiki,
+                Saurashtra,
+                KayahLi,
+                Rejang,
+                Cham,
+                AncientSymbols)
+            //l3 8-15
+            SK_OT_BYTE_BITFIELD(
+                OldPersian,
+                Shavian,
+                Osmanya,
+                CypriotSyllabary,
+                Kharoshthi,
+                TaiXuanJingSymbols,
+                Cuneiform,
+                CountingRodNumerals)
+            //l3 0-7
+            SK_OT_BYTE_BITFIELD(
+                Buginese,
+                Glagolitic,
+                Tifinagh,
+                YijingHexagramSymbols,
+                SylotiNagri,
+                LinearB_AegeanNumbers,
+                AncientGreekNumbers,
+                Ugaritic)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG BasicLatinMask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin1SupplementMask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG LatinExtendedAMask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG LatinExtendedBMask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG IPAExtensionsMask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG SpacingModifierLettersMask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG CombiningDiacriticalMarksMask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG GreekAndCopticMask = SkTEndian_SwapBE32(1 << 7);
+                static const SK_OT_ULONG CopticMask = SkTEndian_SwapBE32(1 << 8);
+                static const SK_OT_ULONG CyrillicMask = SkTEndian_SwapBE32(1 << 9);
+                static const SK_OT_ULONG ArmenianMask = SkTEndian_SwapBE32(1 << 10);
+                static const SK_OT_ULONG HebrewMask = SkTEndian_SwapBE32(1 << 11);
+                static const SK_OT_ULONG VaiMask = SkTEndian_SwapBE32(1 << 12);
+                static const SK_OT_ULONG ArabicMask = SkTEndian_SwapBE32(1 << 13);
+                static const SK_OT_ULONG NKoMask = SkTEndian_SwapBE32(1 << 14);
+                static const SK_OT_ULONG DevanagariMask = SkTEndian_SwapBE32(1 << 15);
+                static const SK_OT_ULONG BengaliMask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG GurmukhiMask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG GujaratiMask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG OriyaMask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG TamilMask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG TeluguMask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG KannadaMask = SkTEndian_SwapBE32(1 << 22);
+                static const SK_OT_ULONG MalayalamMask = SkTEndian_SwapBE32(1 << 23);
+                static const SK_OT_ULONG ThaiMask = SkTEndian_SwapBE32(1 << 24);
+                static const SK_OT_ULONG LaoMask = SkTEndian_SwapBE32(1 << 25);
+                static const SK_OT_ULONG GeorgianMask = SkTEndian_SwapBE32(1 << 26);
+                static const SK_OT_ULONG BalineseMask = SkTEndian_SwapBE32(1 << 27);
+                static const SK_OT_ULONG HangulJamoMask = SkTEndian_SwapBE32(1 << 28);
+                static const SK_OT_ULONG LatinExtendedAdditionalMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG GreekExtendedMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG GeneralPunctuationMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG SuperscriptsAndSubscriptsMask = SkTEndian_SwapBE32(1 << (32 - 32));
+                static const SK_OT_ULONG CurrencySymbolsMask = SkTEndian_SwapBE32(1 << (33 - 32));
+                static const SK_OT_ULONG CombiningDiacriticalMarksForSymbolsMask = SkTEndian_SwapBE32(1 << (34 - 32));
+                static const SK_OT_ULONG LetterlikeSymbolsMask = SkTEndian_SwapBE32(1 << (35 - 32));
+                static const SK_OT_ULONG NumberFormsMask = SkTEndian_SwapBE32(1 << (36 - 32));
+                static const SK_OT_ULONG ArrowsMask = SkTEndian_SwapBE32(1 << (37 - 32));
+                static const SK_OT_ULONG MathematicalOperatorsMask = SkTEndian_SwapBE32(1 << (38 - 32));
+                static const SK_OT_ULONG MiscellaneousTechnicalMask = SkTEndian_SwapBE32(1 << (39 - 32));
+                static const SK_OT_ULONG ControlPicturesMask = SkTEndian_SwapBE32(1 << (40 - 32));
+                static const SK_OT_ULONG OpticalCharacterRecognitionMask = SkTEndian_SwapBE32(1 << (41 - 32));
+                static const SK_OT_ULONG EnclosedAlphanumericsMask = SkTEndian_SwapBE32(1 << (42 - 32));
+                static const SK_OT_ULONG BoxDrawingMask = SkTEndian_SwapBE32(1 << (43 - 32));
+                static const SK_OT_ULONG BlockElementsMask = SkTEndian_SwapBE32(1 << (44 - 32));
+                static const SK_OT_ULONG GeometricShapesMask = SkTEndian_SwapBE32(1 << (45 - 32));
+                static const SK_OT_ULONG MiscellaneousSymbolsMask = SkTEndian_SwapBE32(1 << (46 - 32));
+                static const SK_OT_ULONG DingbatsMask = SkTEndian_SwapBE32(1 << (47 - 32));
+                static const SK_OT_ULONG CJKSymbolsAndPunctuationMask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG HiraganaMask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG KatakanaMask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG BopomofoMask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG HangulCompatibilityJamoMask = SkTEndian_SwapBE32(1 << (52 - 32));
+                static const SK_OT_ULONG PhagsPaMask = SkTEndian_SwapBE32(1 << (53 - 32));
+                static const SK_OT_ULONG EnclosedCJKLettersAndMonthsMask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG CJKCompatibilityMask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG HangulMask = SkTEndian_SwapBE32(1 << (56 - 32));
+                static const SK_OT_ULONG NonPlane0Mask = SkTEndian_SwapBE32(1 << (57 - 32));
+                static const SK_OT_ULONG PhoenicianMask = SkTEndian_SwapBE32(1 << (58 - 32));
+                static const SK_OT_ULONG CJKUnifiedIdeographsMask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG PrivateUseAreaMask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG CJKCompatibilityIdeographsMask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG AlphabeticPresentationFormsMask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG ArabicPresentationFormsAMask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            struct l2 {
+                static const SK_OT_ULONG CombiningHalfMarksMask = SkTEndian_SwapBE32(1 << (64 - 64));
+                static const SK_OT_ULONG CJKCompatibilityFormsMask = SkTEndian_SwapBE32(1 << (65 - 64));
+                static const SK_OT_ULONG SmallFormVariantsMask = SkTEndian_SwapBE32(1 << (66 - 64));
+                static const SK_OT_ULONG ArabicPresentationFormsBMask = SkTEndian_SwapBE32(1 << (67 - 64));
+                static const SK_OT_ULONG HalfwidthAndFullwidthFormsMask = SkTEndian_SwapBE32(1 << (68 - 64));
+                static const SK_OT_ULONG SpecialsMask = SkTEndian_SwapBE32(1 << (69 - 64));
+                static const SK_OT_ULONG TibetanMask = SkTEndian_SwapBE32(1 << (70 - 64));
+                static const SK_OT_ULONG SyriacMask = SkTEndian_SwapBE32(1 << (71 - 64));
+                static const SK_OT_ULONG ThaanaMask = SkTEndian_SwapBE32(1 << (72 - 64));
+                static const SK_OT_ULONG SinhalaMask = SkTEndian_SwapBE32(1 << (73 - 64));
+                static const SK_OT_ULONG MyanmarMask = SkTEndian_SwapBE32(1 << (74 - 64));
+                static const SK_OT_ULONG EthiopicMask = SkTEndian_SwapBE32(1 << (75 - 64));
+                static const SK_OT_ULONG CherokeeMask = SkTEndian_SwapBE32(1 << (76 - 64));
+                static const SK_OT_ULONG UnifiedCanadianSyllabicsMask = SkTEndian_SwapBE32(1 << (77 - 64));
+                static const SK_OT_ULONG OghamMask = SkTEndian_SwapBE32(1 << (78 - 64));
+                static const SK_OT_ULONG RunicMask = SkTEndian_SwapBE32(1 << (79 - 64));
+                static const SK_OT_ULONG KhmerMask = SkTEndian_SwapBE32(1 << (80 - 64));
+                static const SK_OT_ULONG MongolianMask = SkTEndian_SwapBE32(1 << (81 - 64));
+                static const SK_OT_ULONG BrailleMask = SkTEndian_SwapBE32(1 << (82 - 64));
+                static const SK_OT_ULONG YiMask = SkTEndian_SwapBE32(1 << (83 - 64));
+                static const SK_OT_ULONG Tagalog_Hanunoo_Buhid_TagbanwaMask = SkTEndian_SwapBE32(1 << (84 - 64));
+                static const SK_OT_ULONG OldItalicMask = SkTEndian_SwapBE32(1 << (85 - 64));
+                static const SK_OT_ULONG GothicMask = SkTEndian_SwapBE32(1 << (86 - 64));
+                static const SK_OT_ULONG DeseretMask = SkTEndian_SwapBE32(1 << (87 - 64));
+                static const SK_OT_ULONG MusicalSymbolsMask = SkTEndian_SwapBE32(1 << (88 - 64));
+                static const SK_OT_ULONG MathematicalAlphanumericSymbolsMask = SkTEndian_SwapBE32(1 << (89 - 64));
+                static const SK_OT_ULONG PrivateUseMask = SkTEndian_SwapBE32(1 << (90 - 64));
+                static const SK_OT_ULONG VariationSelectorsMask = SkTEndian_SwapBE32(1 << (91 - 64));
+                static const SK_OT_ULONG TagsMask = SkTEndian_SwapBE32(1 << (92 - 64));
+                static const SK_OT_ULONG LimbuMask = SkTEndian_SwapBE32(1 << (93 - 64));
+                static const SK_OT_ULONG TaiLeMask = SkTEndian_SwapBE32(1 << (94 - 64));
+                static const SK_OT_ULONG NewTaiLueMask = SkTEndian_SwapBE32(1 << (95 - 64));
+            };
+            struct l3 {
+                static const SK_OT_ULONG BugineseMask = SkTEndian_SwapBE32(1 << (96 - 96));
+                static const SK_OT_ULONG GlagoliticMask = SkTEndian_SwapBE32(1 << (97 - 96));
+                static const SK_OT_ULONG TifinaghMask = SkTEndian_SwapBE32(1 << (98 - 96));
+                static const SK_OT_ULONG YijingHexagramSymbolsMask = SkTEndian_SwapBE32(1 << (99 - 96));
+                static const SK_OT_ULONG SylotiNagriMask = SkTEndian_SwapBE32(1 << (100 - 96));
+                static const SK_OT_ULONG LinearB_AegeanNumbersMask = SkTEndian_SwapBE32(1 << (101 - 96));
+                static const SK_OT_ULONG AncientGreekNumbersMask = SkTEndian_SwapBE32(1 << (102 - 96));
+                static const SK_OT_ULONG UgariticMask = SkTEndian_SwapBE32(1 << (103 - 96));
+                static const SK_OT_ULONG OldPersianMask = SkTEndian_SwapBE32(1 << (104 - 96));
+                static const SK_OT_ULONG ShavianMask = SkTEndian_SwapBE32(1 << (105 - 96));
+                static const SK_OT_ULONG OsmanyaMask = SkTEndian_SwapBE32(1 << (106 - 96));
+                static const SK_OT_ULONG CypriotSyllabaryMask = SkTEndian_SwapBE32(1 << (107 - 96));
+                static const SK_OT_ULONG KharoshthiMask = SkTEndian_SwapBE32(1 << (108 - 96));
+                static const SK_OT_ULONG TaiXuanJingSymbolsMask = SkTEndian_SwapBE32(1 << (109 - 96));
+                static const SK_OT_ULONG CuneiformMask = SkTEndian_SwapBE32(1 << (110 - 96));
+                static const SK_OT_ULONG CountingRodNumeralsMask = SkTEndian_SwapBE32(1 << (111 - 96));
+                static const SK_OT_ULONG SundaneseMask = SkTEndian_SwapBE32(1 << (112 - 96));
+                static const SK_OT_ULONG LepchaMask = SkTEndian_SwapBE32(1 << (113 - 96));
+                static const SK_OT_ULONG OlChikiMask = SkTEndian_SwapBE32(1 << (114 - 96));
+                static const SK_OT_ULONG SaurashtraMask = SkTEndian_SwapBE32(1 << (115 - 96));
+                static const SK_OT_ULONG KayahLiMask = SkTEndian_SwapBE32(1 << (116 - 96));
+                static const SK_OT_ULONG RejangMask = SkTEndian_SwapBE32(1 << (117 - 96));
+                static const SK_OT_ULONG ChamMask = SkTEndian_SwapBE32(1 << (118 - 96));
+                static const SK_OT_ULONG AncientSymbolsMask = SkTEndian_SwapBE32(1 << (119 - 96));
+                static const SK_OT_ULONG PhaistosDiscMask = SkTEndian_SwapBE32(1 << (120 - 96));
+                static const SK_OT_ULONG Carian_Lycian_LydianMask = SkTEndian_SwapBE32(1 << (121 - 96));
+                static const SK_OT_ULONG DominoTiles_MahjongTilesMask = SkTEndian_SwapBE32(1 << (122 - 96));
+            };
+            SK_OT_ULONG value[4];
+        } raw;
+    } ulUnicodeRange;
+    SK_OT_CHAR achVendID[4];
+    union Selection {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                WWS,
+                Oblique,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Italic,
+                Underscore,
+                Negative,
+                Outlined,
+                Strikeout,
+                Bold,
+                Regular,
+                UseTypoMetrics)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT ItalicMask = SkTEndian_SwapBE16(1 << 0);
+            static const SK_OT_USHORT UnderscoreMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT NegativeMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT OutlinedMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT StrikeoutMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT BoldMask = SkTEndian_SwapBE16(1 << 5);
+            static const SK_OT_USHORT RegularMask = SkTEndian_SwapBE16(1 << 6);
+            static const SK_OT_USHORT UseTypoMetricsMask = SkTEndian_SwapBE16(1 << 7);
+            static const SK_OT_USHORT WWSMask = SkTEndian_SwapBE16(1 << 8);
+            static const SK_OT_USHORT ObliqueMask = SkTEndian_SwapBE16(1 << 9);
+            SK_OT_USHORT value;
+        } raw;
+    } fsSelection;
+    SK_OT_USHORT usFirstCharIndex;
+    SK_OT_USHORT usLastCharIndex;
+    //version0
+    SK_OT_SHORT sTypoAscender;
+    SK_OT_SHORT sTypoDescender;
+    SK_OT_SHORT sTypoLineGap;
+    SK_OT_USHORT usWinAscent;
+    SK_OT_USHORT usWinDescent;
+    //version1
+    union CodePageRange {
+        struct Field {
+            //l0 24-31
+            SK_OT_BYTE_BITFIELD(
+                Reserved24,
+                Reserved25,
+                Reserved26,
+                Reserved27,
+                Reserved28,
+                MacintoshCharacterSet,
+                OEMCharacterSet,
+                SymbolCharacterSet)
+            //l0 16-23
+            SK_OT_BYTE_BITFIELD(
+                Thai_874,
+                JISJapan_932,
+                ChineseSimplified_936,
+                KoreanWansung_949,
+                ChineseTraditional_950,
+                KoreanJohab_1361,
+                Reserved22,
+                Reserved23)
+            //l0 8-15
+            SK_OT_BYTE_BITFIELD(
+                Vietnamese,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //l0 0-7
+            SK_OT_BYTE_BITFIELD(
+                Latin1_1252,
+                Latin2EasternEurope_1250,
+                Cyrillic_1251,
+                Greek_1253,
+                Turkish_1254,
+                Hebrew_1255,
+                Arabic_1256,
+                WindowsBaltic_1257)
+
+            //l1 24-31
+            SK_OT_BYTE_BITFIELD(
+                IBMTurkish_857,
+                IBMCyrillic_855,
+                Latin2_852,
+                MSDOSBaltic_775,
+                Greek_737,
+                Arabic_708,
+                WELatin1_850,
+                US_437)
+            //l1 16-23
+            SK_OT_BYTE_BITFIELD(
+                IBMGreek_869,
+                MSDOSRussian_866,
+                MSDOSNordic_865,
+                Arabic_864,
+                MSDOSCanadianFrench_863,
+                Hebrew_862,
+                MSDOSIcelandic_861,
+                MSDOSPortuguese_860)
+            //l1 8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved40,
+                Reserved41,
+                Reserved42,
+                Reserved43,
+                Reserved44,
+                Reserved45,
+                Reserved46,
+                Reserved47)
+            //l1 0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved32,
+                Reserved33,
+                Reserved34,
+                Reserved35,
+                Reserved36,
+                Reserved37,
+                Reserved38,
+                Reserved39)
+        } field;
+        struct Raw {
+            struct l0 {
+                static const SK_OT_ULONG Latin1_1252Mask = SkTEndian_SwapBE32(1 << 0);
+                static const SK_OT_ULONG Latin2EasternEurope_1250Mask = SkTEndian_SwapBE32(1 << 1);
+                static const SK_OT_ULONG Cyrillic_1251Mask = SkTEndian_SwapBE32(1 << 2);
+                static const SK_OT_ULONG Greek_1253Mask = SkTEndian_SwapBE32(1 << 3);
+                static const SK_OT_ULONG Turkish_1254Mask = SkTEndian_SwapBE32(1 << 4);
+                static const SK_OT_ULONG Hebrew_1255Mask = SkTEndian_SwapBE32(1 << 5);
+                static const SK_OT_ULONG Arabic_1256Mask = SkTEndian_SwapBE32(1 << 6);
+                static const SK_OT_ULONG WindowsBaltic_1257Mask = SkTEndian_SwapBE32(1 << 7);
+                static const SK_OT_ULONG Vietnamese_1258Mask = SkTEndian_SwapBE32(1 << 8);
+                static const SK_OT_ULONG Thai_874Mask = SkTEndian_SwapBE32(1 << 16);
+                static const SK_OT_ULONG JISJapan_932Mask = SkTEndian_SwapBE32(1 << 17);
+                static const SK_OT_ULONG ChineseSimplified_936Mask = SkTEndian_SwapBE32(1 << 18);
+                static const SK_OT_ULONG KoreanWansung_949Mask = SkTEndian_SwapBE32(1 << 19);
+                static const SK_OT_ULONG ChineseTraditional_950Mask = SkTEndian_SwapBE32(1 << 20);
+                static const SK_OT_ULONG KoreanJohab_1361Mask = SkTEndian_SwapBE32(1 << 21);
+                static const SK_OT_ULONG MacintoshCharacterSetMask = SkTEndian_SwapBE32(1 << 29);
+                static const SK_OT_ULONG OEMCharacterSetMask = SkTEndian_SwapBE32(1 << 30);
+                static const SK_OT_ULONG SymbolCharacterSetMask = SkTEndian_SwapBE32(1 << 31);
+            };
+            struct l1 {
+                static const SK_OT_ULONG IBMGreek_869Mask = SkTEndian_SwapBE32(1 << (48 - 32));
+                static const SK_OT_ULONG MSDOSRussian_866Mask = SkTEndian_SwapBE32(1 << (49 - 32));
+                static const SK_OT_ULONG MSDOSNordic_865Mask = SkTEndian_SwapBE32(1 << (50 - 32));
+                static const SK_OT_ULONG Arabic_864Mask = SkTEndian_SwapBE32(1 << (51 - 32));
+                static const SK_OT_ULONG MSDOSCanadianFrench_863Mask = SkTEndian_SwapBE32(1 << (52 - 32));
+                static const SK_OT_ULONG Hebrew_862Mask = SkTEndian_SwapBE32(1 << (53 - 32));
+                static const SK_OT_ULONG MSDOSIcelandic_861Mask = SkTEndian_SwapBE32(1 << (54 - 32));
+                static const SK_OT_ULONG MSDOSPortuguese_860Mask = SkTEndian_SwapBE32(1 << (55 - 32));
+                static const SK_OT_ULONG IBMTurkish_857Mask = SkTEndian_SwapBE32(1 << (56 - 32));
+                static const SK_OT_ULONG IBMCyrillic_855Mask = SkTEndian_SwapBE32(1 << (57 - 32));
+                static const SK_OT_ULONG Latin2_852Mask = SkTEndian_SwapBE32(1 << (58 - 32));
+                static const SK_OT_ULONG MSDOSBaltic_775Mask = SkTEndian_SwapBE32(1 << (59 - 32));
+                static const SK_OT_ULONG Greek_737Mask = SkTEndian_SwapBE32(1 << (60 - 32));
+                static const SK_OT_ULONG Arabic_708Mask = SkTEndian_SwapBE32(1 << (61 - 32));
+                static const SK_OT_ULONG WELatin1_850Mask = SkTEndian_SwapBE32(1 << (62 - 32));
+                static const SK_OT_ULONG US_437Mask = SkTEndian_SwapBE32(1 << (63 - 32));
+            };
+            SK_OT_ULONG value[2];
+        } raw;
+    } ulCodePageRange;
+    //version2
+    SK_OT_SHORT sxHeight;
+    SK_OT_SHORT sCapHeight;
+    SK_OT_USHORT usDefaultChar;
+    SK_OT_USHORT usBreakChar;
+    SK_OT_USHORT usMaxContext;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2_V4) == 96, sizeof_SkOTTableOS2_V4_not_96);
+
+#endif
diff --git a/src/sfnt/SkOTTable_OS_2_VA.h b/src/sfnt/SkOTTable_OS_2_VA.h
new file mode 100644
index 0000000..146e83b
--- /dev/null
+++ b/src/sfnt/SkOTTable_OS_2_VA.h
@@ -0,0 +1,142 @@
+/*
+ * 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 SkOTTable_OS_2_VA_DEFINED
+#define SkOTTable_OS_2_VA_DEFINED
+
+#include "SkEndian.h"
+#include "SkIBMFamilyClass.h"
+#include "SkOTTableTypes.h"
+#include "SkPanose.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+//Original V0 TT
+struct SkOTTableOS2_VA {
+    SK_OT_USHORT version;
+    //SkOTTableOS2_VA::VERSION and SkOTTableOS2_V0::VERSION are both 0.
+    //The only way to differentiate these two versions is by the size of the table.
+    static const SK_OT_USHORT VERSION = SkTEndian_SwapBE16(0);
+
+    SK_OT_SHORT xAvgCharWidth;
+    struct WeightClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((UltraLight, SkTEndian_SwapBE16(1)))
+            ((ExtraLight, SkTEndian_SwapBE16(2)))
+            ((Light, SkTEndian_SwapBE16(3)))
+            ((SemiLight, SkTEndian_SwapBE16(4)))
+            ((Medium, SkTEndian_SwapBE16(5)))
+            ((SemiBold, SkTEndian_SwapBE16(6)))
+            ((Bold, SkTEndian_SwapBE16(7)))
+            ((ExtraBold, SkTEndian_SwapBE16(8)))
+            ((UltraBold, SkTEndian_SwapBE16(9)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } usWeightClass;
+    struct WidthClass {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((UltraCondensed, SkTEndian_SwapBE16(1)))
+            ((ExtraCondensed, SkTEndian_SwapBE16(2)))
+            ((Condensed, SkTEndian_SwapBE16(3)))
+            ((SemiCondensed, SkTEndian_SwapBE16(4)))
+            ((Medium, SkTEndian_SwapBE16(5)))
+            ((SemiExpanded, SkTEndian_SwapBE16(6)))
+            ((Expanded, SkTEndian_SwapBE16(7)))
+            ((ExtraExpanded, SkTEndian_SwapBE16(8)))
+            ((UltraExpanded, SkTEndian_SwapBE16(9)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } usWidthClass;
+    union Type {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Reserved00,
+                Restricted,
+                PreviewPrint,
+                Editable,
+                Reserved04,
+                Reserved05,
+                Reserved06,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT Installable = SkTEndian_SwapBE16(0);
+            static const SK_OT_USHORT RestrictedMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT PreviewPrintMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT EditableMask = SkTEndian_SwapBE16(1 << 3);
+            SK_OT_USHORT value;
+        } raw;
+    } fsType;
+    SK_OT_SHORT ySubscriptXSize;
+    SK_OT_SHORT ySubscriptYSize;
+    SK_OT_SHORT ySubscriptXOffset;
+    SK_OT_SHORT ySubscriptYOffset;
+    SK_OT_SHORT ySuperscriptXSize;
+    SK_OT_SHORT ySuperscriptYSize;
+    SK_OT_SHORT ySuperscriptXOffset;
+    SK_OT_SHORT ySuperscriptYOffset;
+    SK_OT_SHORT yStrikeoutSize;
+    SK_OT_SHORT yStrikeoutPosition;
+    SkIBMFamilyClass sFamilyClass;
+    SkPanose panose;
+    SK_OT_ULONG ulCharRange[4];
+    SK_OT_CHAR achVendID[4];
+    union Selection {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Italic,
+                Underscore,
+                Negative,
+                Outlined,
+                Strikeout,
+                Bold,
+                Reserved06,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT ItalicMask = SkTEndian_SwapBE16(1 << 0);
+            static const SK_OT_USHORT UnderscoreMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT NegativeMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT OutlinedMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT StrikeoutMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT BoldMask = SkTEndian_SwapBE16(1 << 5);
+            SK_OT_USHORT value;
+        } raw;
+    } fsSelection;
+    SK_OT_USHORT usFirstCharIndex;
+    SK_OT_USHORT usLastCharIndex;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableOS2_VA) == 68, sizeof_SkOTTableOS2_VA_not_68);
+
+#endif
diff --git a/src/sfnt/SkOTTable_glyf.h b/src/sfnt/SkOTTable_glyf.h
new file mode 100644
index 0000000..ac34d7b
--- /dev/null
+++ b/src/sfnt/SkOTTable_glyf.h
@@ -0,0 +1,214 @@
+/*
+ * 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 SkOTTable_glyf_DEFINED
+#define SkOTTable_glyf_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_loca.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableGlyphData;
+
+extern uint8_t const * const SK_OT_GlyphData_NoOutline;
+
+struct SkOTTableGlyph {
+    static const SK_OT_CHAR TAG0 = 'g';
+    static const SK_OT_CHAR TAG1 = 'l';
+    static const SK_OT_CHAR TAG2 = 'y';
+    static const SK_OT_CHAR TAG3 = 'f';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableGlyph>::value;
+
+    class Iterator {
+    public:
+        Iterator(const SkOTTableGlyph& glyf,
+                 const SkOTTableIndexToLocation& loca,
+                 SkOTTableHead::IndexToLocFormat locaFormat)
+        : fGlyf(glyf)
+        , fLocaFormat(SkOTTableHead::IndexToLocFormat::ShortOffsets == locaFormat.value ? 0 : 1)
+        , fCurrentGlyphOffset(0)
+        { fLocaPtr.shortOffset = reinterpret_cast<const SK_OT_USHORT*>(&loca); }
+
+        void advance(uint16_t num) {
+            fLocaPtr.shortOffset += num << fLocaFormat;
+            fCurrentGlyphOffset = fLocaFormat ? SkEndian_SwapBE32(*fLocaPtr.longOffset)
+                                              : uint32_t(SkEndian_SwapBE16(*fLocaPtr.shortOffset) << 1);
+        }
+        const SkOTTableGlyphData* next() {
+            uint32_t previousGlyphOffset = fCurrentGlyphOffset;
+            advance(1);
+            if (previousGlyphOffset == fCurrentGlyphOffset) {
+                return reinterpret_cast<const SkOTTableGlyphData*>(&SK_OT_GlyphData_NoOutline);
+            } else {
+                return reinterpret_cast<const SkOTTableGlyphData*>(
+                    reinterpret_cast<const SK_OT_BYTE*>(&fGlyf) + previousGlyphOffset
+                );
+            }
+        }
+    private:
+        const SkOTTableGlyph& fGlyf;
+        uint16_t fLocaFormat; //0 or 1
+        uint32_t fCurrentGlyphOffset;
+        union LocaPtr {
+            const SK_OT_USHORT* shortOffset;
+            const SK_OT_ULONG* longOffset;
+        } fLocaPtr;
+    };
+};
+
+struct SkOTTableGlyphData {
+    SK_OT_SHORT numberOfContours; //== -1 Composite, > 0 Simple
+    SK_OT_FWORD xMin;
+    SK_OT_FWORD yMin;
+    SK_OT_FWORD xMax;
+    SK_OT_FWORD yMax;
+
+    struct Simple {
+        SK_OT_USHORT endPtsOfContours[1/*numberOfContours*/];
+
+        struct Instructions {
+            SK_OT_USHORT length;
+            SK_OT_BYTE data[1/*length*/];
+        };
+
+        union Flags {
+            struct Field {
+                SK_OT_BYTE_BITFIELD(
+                    OnCurve,
+                    xShortVector,
+                    yShortVector,
+                    Repeat,
+                    xIsSame_xShortVectorPositive,
+                    yIsSame_yShortVectorPositive,
+                    Reserved6,
+                    Reserved7)
+            } field;
+            struct Raw {
+                static const SK_OT_USHORT OnCurveMask = SkTEndian_SwapBE16(1 << 0);
+                static const SK_OT_USHORT xShortVectorMask = SkTEndian_SwapBE16(1 << 1);
+                static const SK_OT_USHORT yShortVectorMask = SkTEndian_SwapBE16(1 << 2);
+                static const SK_OT_USHORT RepeatMask = SkTEndian_SwapBE16(1 << 3);
+                static const SK_OT_USHORT xIsSame_xShortVectorPositiveMask = SkTEndian_SwapBE16(1 << 4);
+                static const SK_OT_USHORT yIsSame_yShortVectorPositiveMask = SkTEndian_SwapBE16(1 << 5);
+                SK_OT_BYTE value;
+            } raw;
+        };
+
+        //xCoordinates
+        //yCoordinates
+    };
+
+    struct Composite {
+        struct Component {
+            union Flags {
+                struct Field {
+                    //8-15
+                    SK_OT_BYTE_BITFIELD(
+                        WE_HAVE_INSTRUCTIONS,
+                        USE_MY_METRICS,
+                        OVERLAP_COMPOUND,
+                        SCALED_COMPONENT_OFFSET,
+                        UNSCALED_COMPONENT_OFFSET,
+                        Reserved13,
+                        Reserved14,
+                        Reserved15)
+                    //0-7
+                    SK_OT_BYTE_BITFIELD(
+                        ARG_1_AND_2_ARE_WORDS,
+                        ARGS_ARE_XY_VALUES,
+                        ROUND_XY_TO_GRID,
+                        WE_HAVE_A_SCALE,
+                        RESERVED,
+                        MORE_COMPONENTS,
+                        WE_HAVE_AN_X_AND_Y_SCALE,
+                        WE_HAVE_A_TWO_BY_TWO)
+                } field;
+                struct Raw {
+                    static const SK_OT_USHORT ARG_1_AND_2_ARE_WORDS_Mask = SkTEndian_SwapBE16(1 << 0);
+                    static const SK_OT_USHORT ARGS_ARE_XY_VALUES_Mask = SkTEndian_SwapBE16(1 << 1);
+                    static const SK_OT_USHORT ROUND_XY_TO_GRID_Mask = SkTEndian_SwapBE16(1 << 2);
+                    static const SK_OT_USHORT WE_HAVE_A_SCALE_Mask = SkTEndian_SwapBE16(1 << 3);
+                    static const SK_OT_USHORT RESERVED_Mask = SkTEndian_SwapBE16(1 << 4);
+                    static const SK_OT_USHORT MORE_COMPONENTS_Mask = SkTEndian_SwapBE16(1 << 5);
+                    static const SK_OT_USHORT WE_HAVE_AN_X_AND_Y_SCALE_Mask = SkTEndian_SwapBE16(1 << 6);
+                    static const SK_OT_USHORT WE_HAVE_A_TWO_BY_TWO_Mask = SkTEndian_SwapBE16(1 << 7);
+
+                    static const SK_OT_USHORT WE_HAVE_INSTRUCTIONS_Mask = SkTEndian_SwapBE16(1 << 8);
+                    static const SK_OT_USHORT USE_MY_METRICS_Mask = SkTEndian_SwapBE16(1 << 9);
+                    static const SK_OT_USHORT OVERLAP_COMPOUND_Mask = SkTEndian_SwapBE16(1 << 10);
+                    static const SK_OT_USHORT SCALED_COMPONENT_OFFSET_Mask = SkTEndian_SwapBE16(1 << 11);
+                    static const SK_OT_USHORT UNSCALED_COMPONENT_OFFSET_mask = SkTEndian_SwapBE16(1 << 12);
+                    //Reserved
+                    //Reserved
+                    //Reserved
+                    SK_OT_USHORT value;
+                } raw;
+            } flags;
+            SK_OT_USHORT glyphIndex;
+            union Transform {
+                union Matrix {
+                    /** !WE_HAVE_A_SCALE & !WE_HAVE_AN_X_AND_Y_SCALE & !WE_HAVE_A_TWO_BY_TWO */
+                    struct None { } none;
+                    /** WE_HAVE_A_SCALE */
+                    struct Scale {
+                        SK_OT_F2DOT14 a_d;
+                    } scale;
+                    /** WE_HAVE_AN_X_AND_Y_SCALE */
+                    struct ScaleXY {
+                        SK_OT_F2DOT14 a;
+                        SK_OT_F2DOT14 d;
+                    } scaleXY;
+                    /** WE_HAVE_A_TWO_BY_TWO */
+                    struct TwoByTwo {
+                        SK_OT_F2DOT14 a;
+                        SK_OT_F2DOT14 b;
+                        SK_OT_F2DOT14 c;
+                        SK_OT_F2DOT14 d;
+                    } twoByTwo;
+                };
+                /** ARG_1_AND_2_ARE_WORDS & ARGS_ARE_XY_VALUES */
+                struct WordValue {
+                    SK_OT_FWORD e;
+                    SK_OT_FWORD f;
+                    SkOTTableGlyphData::Composite::Component::Transform::Matrix matrix;
+                } wordValue;
+                /** !ARG_1_AND_2_ARE_WORDS & ARGS_ARE_XY_VALUES */
+                struct ByteValue {
+                    SK_OT_CHAR e;
+                    SK_OT_CHAR f;
+                    SkOTTableGlyphData::Composite::Component::Transform::Matrix matrix;
+                } byteValue;
+                /** ARG_1_AND_2_ARE_WORDS & !ARGS_ARE_XY_VALUES */
+                struct WordIndex {
+                    SK_OT_USHORT compoundPointIndex;
+                    SK_OT_USHORT componentPointIndex;
+                    SkOTTableGlyphData::Composite::Component::Transform::Matrix matrix;
+                } wordIndex;
+                /** !ARG_1_AND_2_ARE_WORDS & !ARGS_ARE_XY_VALUES */
+                struct ByteIndex {
+                    SK_OT_BYTE compoundPointIndex;
+                    SK_OT_BYTE componentPointIndex;
+                    SkOTTableGlyphData::Composite::Component::Transform::Matrix matrix;
+                } byteIndex;
+            } transform;
+        } component;//[] last element does not set MORE_COMPONENTS
+
+        /** Comes after the last Component if the last component has WE_HAVE_INSTR. */
+        struct Instructions {
+            SK_OT_USHORT length;
+            SK_OT_BYTE data[1/*length*/];
+        };
+    };
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/src/sfnt/SkOTTable_head.h b/src/sfnt/SkOTTable_head.h
new file mode 100644
index 0000000..3d2c0f1
--- /dev/null
+++ b/src/sfnt/SkOTTable_head.h
@@ -0,0 +1,150 @@
+/*
+ * 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 SkOTTable_head_DEFINED
+#define SkOTTable_head_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableHead {
+    static const SK_OT_CHAR TAG0 = 'h';
+    static const SK_OT_CHAR TAG1 = 'e';
+    static const SK_OT_CHAR TAG2 = 'a';
+    static const SK_OT_CHAR TAG3 = 'd';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableHead>::value;
+
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed version1 = SkTEndian_SwapBE32(0x00010000);
+    SK_OT_Fixed fontRevision;
+    static const uint32_t fontChecksum = 0xB1B0AFBA; //checksum of all TT fonts
+    SK_OT_ULONG checksumAdjustment;
+    SK_OT_ULONG magicNumber;
+    static const SK_OT_ULONG magicNumberConst = SkTEndian_SwapBE32(0x5F0F3CF5);
+    union Flags {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                GXMetamorphosis_Apple,
+                HasStrongRTL_Apple,
+                HasIndicStyleRearrangement,
+                AgfaMicroTypeExpressProcessed,
+                FontConverted,
+                DesignedForClearType,
+                LastResort,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                BaselineAtY0,
+                LeftSidebearingAtX0,
+                InstructionsDependOnPointSize,
+                IntegerScaling,
+                InstructionsAlterAdvanceWidth,
+                VerticalCenteredGlyphs_Apple,
+                Reserved06,
+                RequiresLayout_Apple)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT BaselineAtY0Mask = SkTEndian_SwapBE16(1 << 0);
+            static const SK_OT_USHORT LeftSidebearingAtX0Mask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT InstructionsDependOnPointSizeMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT IntegerScalingMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT InstructionsAlterAdvanceWidthMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT VerticalCenteredGlyphs_AppleMask = SkTEndian_SwapBE16(1 << 5);
+            //Reserved
+            static const SK_OT_USHORT RequiresLayout_AppleMask = SkTEndian_SwapBE16(1 << 7);
+
+            static const SK_OT_USHORT GXMetamorphosis_AppleMask = SkTEndian_SwapBE16(1 << 8);
+            static const SK_OT_USHORT HasStrongRTL_AppleMask = SkTEndian_SwapBE16(1 << 9);
+            static const SK_OT_USHORT HasIndicStyleRearrangementMask = SkTEndian_SwapBE16(1 << 10);
+            static const SK_OT_USHORT AgfaMicroTypeExpressProcessedMask = SkTEndian_SwapBE16(1 << 11);
+            static const SK_OT_USHORT FontConvertedMask = SkTEndian_SwapBE16(1 << 12);
+            static const SK_OT_USHORT DesignedForClearTypeMask = SkTEndian_SwapBE16(1 << 13);
+            static const SK_OT_USHORT LastResortMask = SkTEndian_SwapBE16(1 << 14);
+            //Reserved
+            SK_OT_USHORT value;
+        } raw;
+    } flags;
+    SK_OT_USHORT unitsPerEm;
+    SK_OT_LONGDATETIME created;
+    SK_OT_LONGDATETIME modified;
+    SK_OT_SHORT xMin;
+    SK_OT_SHORT yMin;
+    SK_OT_SHORT xMax;
+    SK_OT_SHORT yMax;
+    union MacStyle {
+        struct Field {
+            //8-15
+            SK_OT_BYTE_BITFIELD(
+                Reserved08,
+                Reserved09,
+                Reserved10,
+                Reserved11,
+                Reserved12,
+                Reserved13,
+                Reserved14,
+                Reserved15)
+            //0-7
+            SK_OT_BYTE_BITFIELD(
+                Bold,
+                Italic,
+                Underline,
+                Outline,
+                Shadow,
+                Condensed,
+                Extended,
+                Reserved07)
+        } field;
+        struct Raw {
+            static const SK_OT_USHORT BoldMask = SkTEndian_SwapBE16(1);
+            static const SK_OT_USHORT ItalicMask = SkTEndian_SwapBE16(1 << 1);
+            static const SK_OT_USHORT UnderlineMask = SkTEndian_SwapBE16(1 << 2);
+            static const SK_OT_USHORT OutlineMask = SkTEndian_SwapBE16(1 << 3);
+            static const SK_OT_USHORT ShadowMask = SkTEndian_SwapBE16(1 << 4);
+            static const SK_OT_USHORT CondensedMask = SkTEndian_SwapBE16(1 << 5);
+            static const SK_OT_USHORT ExtendedMask = SkTEndian_SwapBE16(1 << 6);
+
+            SK_OT_USHORT value;
+        } raw;
+    } macStyle;
+    SK_OT_USHORT lowestRecPPEM;
+    struct FontDirectionHint {
+        SK_TYPED_ENUM(Value, SK_OT_SHORT,
+            ((FullyMixedDirectionalGlyphs, SkTEndian_SwapBE16(0)))
+            ((OnlyStronglyLTR, SkTEndian_SwapBE16(1)))
+            ((StronglyLTR, SkTEndian_SwapBE16(2)))
+            ((OnlyStronglyRTL, static_cast<SK_OT_SHORT>(SkTEndian_SwapBE16((uint16_t)-1))))
+            ((StronglyRTL, static_cast<SK_OT_SHORT>(SkTEndian_SwapBE16((uint16_t)-2))))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } fontDirectionHint;
+    struct IndexToLocFormat {
+        SK_TYPED_ENUM(Value, SK_OT_SHORT,
+            ((ShortOffsets, SkTEndian_SwapBE16(0)))
+            ((LongOffsets, SkTEndian_SwapBE16(1)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } indexToLocFormat;
+    struct GlyphDataFormat {
+        SK_TYPED_ENUM(Value, SK_OT_SHORT,
+            ((CurrentFormat, SkTEndian_SwapBE16(0)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } glyphDataFormat;
+};
+
+#pragma pack(pop)
+
+
+#include <stddef.h>
+SK_COMPILE_ASSERT(offsetof(SkOTTableHead, glyphDataFormat) == 52, SkOTTableHead_glyphDataFormat_not_at_52);
+SK_COMPILE_ASSERT(sizeof(SkOTTableHead) == 54, sizeof_SkOTTableHead_not_54);
+
+#endif
diff --git a/src/sfnt/SkOTTable_hhea.h b/src/sfnt/SkOTTable_hhea.h
new file mode 100644
index 0000000..b8a2070
--- /dev/null
+++ b/src/sfnt/SkOTTable_hhea.h
@@ -0,0 +1,56 @@
+/*
+ * 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 SkOTTable_hhea_DEFINED
+#define SkOTTable_hhea_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableHorizontalHeader {
+    static const SK_OT_CHAR TAG0 = 'h';
+    static const SK_OT_CHAR TAG1 = 'h';
+    static const SK_OT_CHAR TAG2 = 'e';
+    static const SK_OT_CHAR TAG3 = 'a';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableHorizontalHeader>::value;
+
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed version1 = SkTEndian_SwapBE32(0x00010000);
+    SK_OT_FWORD Ascender;
+    SK_OT_FWORD Descender;
+    SK_OT_FWORD LineGap;
+    SK_OT_UFWORD advanceWidthMax;
+    SK_OT_FWORD minLeftSideBearing;
+    SK_OT_FWORD minRightSideBearing;
+    SK_OT_FWORD xMaxExtent;
+    SK_OT_SHORT caretSlopeRise;
+    SK_OT_SHORT caretSlopeRun;
+    SK_OT_SHORT caretOffset;
+    SK_OT_SHORT Reserved24;
+    SK_OT_SHORT Reserved26;
+    SK_OT_SHORT Reserved28;
+    SK_OT_SHORT Reserved30;
+    struct MetricDataFormat {
+        SK_TYPED_ENUM(Value, SK_OT_SHORT,
+            ((CurrentFormat, SkTEndian_SwapBE16(0)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } metricDataFormat;
+    SK_OT_USHORT numberOfHMetrics;
+};
+
+#pragma pack(pop)
+
+
+#include <stddef.h>
+SK_COMPILE_ASSERT(offsetof(SkOTTableHorizontalHeader, numberOfHMetrics) == 34, SkOTTableHorizontalHeader_numberOfHMetrics_not_at_34);
+SK_COMPILE_ASSERT(sizeof(SkOTTableHorizontalHeader) == 36, sizeof_SkOTTableHorizontalHeader_not_36);
+
+#endif
diff --git a/src/sfnt/SkOTTable_loca.h b/src/sfnt/SkOTTable_loca.h
new file mode 100644
index 0000000..586daf1
--- /dev/null
+++ b/src/sfnt/SkOTTable_loca.h
@@ -0,0 +1,31 @@
+/*
+ * 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 SkOTTable_loca_DEFINED
+#define SkOTTable_loca_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableIndexToLocation {
+    static const SK_OT_CHAR TAG0 = 'l';
+    static const SK_OT_CHAR TAG1 = 'o';
+    static const SK_OT_CHAR TAG2 = 'c';
+    static const SK_OT_CHAR TAG3 = 'a';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableIndexToLocation>::value;
+
+    union Offsets {
+        SK_OT_USHORT shortOffset[1];
+        SK_OT_ULONG longOffset[1];
+    } offsets;
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/src/sfnt/SkOTTable_maxp.h b/src/sfnt/SkOTTable_maxp.h
new file mode 100644
index 0000000..d7feac6
--- /dev/null
+++ b/src/sfnt/SkOTTable_maxp.h
@@ -0,0 +1,34 @@
+/*
+ * 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 SkOTTable_maxp_DEFINED
+#define SkOTTable_maxp_DEFINED
+
+#include "SkOTTableTypes.h"
+#include "SkOTTable_maxp_CFF.h"
+#include "SkOTTable_maxp_TT.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableMaximumProfile {
+    static const SK_OT_CHAR TAG0 = 'm';
+    static const SK_OT_CHAR TAG1 = 'a';
+    static const SK_OT_CHAR TAG2 = 'x';
+    static const SK_OT_CHAR TAG3 = 'p';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableMaximumProfile>::value;
+
+    union Version {
+        SK_OT_Fixed version;
+
+        struct CFF : SkOTTableMaximumProfile_CFF { } cff;
+        struct TT : SkOTTableMaximumProfile_TT { } tt;
+    } version;
+};
+
+#pragma pack(pop)
+
+#endif
diff --git a/src/sfnt/SkOTTable_maxp_CFF.h b/src/sfnt/SkOTTable_maxp_CFF.h
new file mode 100644
index 0000000..873fb66
--- /dev/null
+++ b/src/sfnt/SkOTTable_maxp_CFF.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkOTTable_maxp_CFF_DEFINED
+#define SkOTTable_maxp_CFF_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableMaximumProfile_CFF {
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed VERSION = SkTEndian_SwapBE32(0x00005000);
+
+    SK_OT_USHORT numGlyphs;
+};
+
+#pragma pack(pop)
+
+
+#include <stddef.h>
+SK_COMPILE_ASSERT(offsetof(SkOTTableMaximumProfile_CFF, numGlyphs) == 4, SkOTTableHead_glyphDataFormat_not_at_2);
+SK_COMPILE_ASSERT(sizeof(SkOTTableMaximumProfile_CFF) == 6, sizeof_SkOTTableHead_not_4);
+
+#endif
diff --git a/src/sfnt/SkOTTable_maxp_TT.h b/src/sfnt/SkOTTable_maxp_TT.h
new file mode 100644
index 0000000..ad472a1
--- /dev/null
+++ b/src/sfnt/SkOTTable_maxp_TT.h
@@ -0,0 +1,49 @@
+/*
+ * 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 SkOTTable_maxp_TT_DEFINED
+#define SkOTTable_maxp_TT_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableMaximumProfile_TT {
+    SK_OT_Fixed version;
+    static const SK_OT_Fixed VERSION = SkTEndian_SwapBE32(0x00010000);
+
+    SK_OT_USHORT numGlyphs;
+    SK_OT_USHORT maxPoints;
+    SK_OT_USHORT maxContours;
+    SK_OT_USHORT maxCompositePoints;
+    SK_OT_USHORT maxCompositeContours;
+    struct MaxZones {
+        SK_TYPED_ENUM(Value, SK_OT_SHORT,
+            ((DoesNotUseTwilightZone, SkTEndian_SwapBE16(1)))
+            ((UsesTwilightZone, SkTEndian_SwapBE16(2)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } maxZones;
+    SK_OT_USHORT maxTwilightPoints;
+    SK_OT_USHORT maxStorage;
+    SK_OT_USHORT maxFunctionDefs;
+    SK_OT_USHORT maxInstructionDefs;
+    SK_OT_USHORT maxStackElements;
+    SK_OT_USHORT maxSizeOfInstructions;
+    SK_OT_USHORT maxComponentDepth;
+};
+
+#pragma pack(pop)
+
+
+#include <stddef.h>
+SK_COMPILE_ASSERT(offsetof(SkOTTableMaximumProfile_TT, maxComponentDepth) == 28, SkOTTableMaximumProfile_TT_maxComponentDepth_not_at_26);
+SK_COMPILE_ASSERT(sizeof(SkOTTableMaximumProfile_TT) == 30, sizeof_SkOTTableMaximumProfile_TT_not_28);
+
+#endif
diff --git a/src/sfnt/SkOTTable_name.h b/src/sfnt/SkOTTable_name.h
new file mode 100644
index 0000000..3ee72f2
--- /dev/null
+++ b/src/sfnt/SkOTTable_name.h
@@ -0,0 +1,510 @@
+/*
+ * 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 SkOTTable_name_DEFINED
+#define SkOTTable_name_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTableName {
+    static const SK_OT_CHAR TAG0 = 'n';
+    static const SK_OT_CHAR TAG1 = 'a';
+    static const SK_OT_CHAR TAG2 = 'm';
+    static const SK_OT_CHAR TAG3 = 'e';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTableName>::value;
+
+    SK_OT_USHORT format;
+    static const SK_OT_USHORT format_0 = SkTEndian_SwapBE16(0);
+    static const SK_OT_USHORT format_1 = SkTEndian_SwapBE16(1);
+    SK_OT_USHORT count;
+    SK_OT_USHORT stringOffset; //Offset to start of storage area (from start of table).
+    //SkOTTableNameRecord nameRecord[count];
+};
+struct SkOTTableNameF1 {
+    SK_OT_USHORT langTagCount;
+    //SkOTTableNameLangTagRecord langTagRecord[langTagCount];
+};
+
+struct SkOTTableNameLangTagRecord {
+    SK_OT_USHORT length;
+    SK_OT_USHORT offset; //From start of storage area.
+    //The string is always UTF-16BE from IETF specification BCP 47.
+};
+
+struct SkOTTableNameRecord {
+    //The platform ID specifies how to interpret the encoding and language ID.
+    struct PlatformID {
+        SK_TYPED_ENUM(Value, SK_OT_USHORT,
+            ((Unicode, SkTEndian_SwapBE16(0)))
+            ((Macintosh, SkTEndian_SwapBE16(1)))
+            ((ISO, SkTEndian_SwapBE16(2))) //deprecated, use Unicode instead
+            ((Windows, SkTEndian_SwapBE16(3)))
+            ((Custom, SkTEndian_SwapBE16(4)))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } platformID;
+    union EncodingID {
+        //Always UTF-16BE
+        struct Unicode {
+            SK_TYPED_ENUM(Value, SK_OT_USHORT,
+                ((Unicode10, SkTEndian_SwapBE16(0)))
+                ((Unicode11, SkTEndian_SwapBE16(1)))
+                ((ISO10646, SkTEndian_SwapBE16(2))) //deprecated, use Unicode11
+                ((Unicode20BMP, SkTEndian_SwapBE16(3)))
+                ((Unicode20, SkTEndian_SwapBE16(4)))
+                ((UnicodeVariationSequences, SkTEndian_SwapBE16(5)))
+                ((UnicodeFull, SkTEndian_SwapBE16(6)))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } unicode;
+        struct Macintosh {
+            SK_TYPED_ENUM(Value, SK_OT_USHORT,
+                ((Roman, SkTEndian_SwapBE16(0)))
+                ((Japanese, SkTEndian_SwapBE16(1)))
+                ((ChineseTraditional, SkTEndian_SwapBE16(2)))
+                ((Korean, SkTEndian_SwapBE16(3)))
+                ((Arabic, SkTEndian_SwapBE16(4)))
+                ((Hebrew, SkTEndian_SwapBE16(5)))
+                ((Greek, SkTEndian_SwapBE16(6)))
+                ((Russian, SkTEndian_SwapBE16(7)))
+                ((RSymbol, SkTEndian_SwapBE16(8)))
+                ((Devanagari, SkTEndian_SwapBE16(9)))
+                ((Gurmukhi, SkTEndian_SwapBE16(10)))
+                ((Gujarati, SkTEndian_SwapBE16(11)))
+                ((Oriya, SkTEndian_SwapBE16(12)))
+                ((Bengali, SkTEndian_SwapBE16(13)))
+                ((Tamil, SkTEndian_SwapBE16(14)))
+                ((Telugu, SkTEndian_SwapBE16(15)))
+                ((Kannada, SkTEndian_SwapBE16(16)))
+                ((Malayalam, SkTEndian_SwapBE16(17)))
+                ((Sinhalese, SkTEndian_SwapBE16(18)))
+                ((Burmese, SkTEndian_SwapBE16(19)))
+                ((Khmer, SkTEndian_SwapBE16(20)))
+                ((Thai, SkTEndian_SwapBE16(21)))
+                ((Laotian, SkTEndian_SwapBE16(22)))
+                ((Georgian, SkTEndian_SwapBE16(23)))
+                ((Armenian, SkTEndian_SwapBE16(24)))
+                ((ChineseSimplified, SkTEndian_SwapBE16(25)))
+                ((Tibetan, SkTEndian_SwapBE16(26)))
+                ((Mongolian, SkTEndian_SwapBE16(27)))
+                ((Geez, SkTEndian_SwapBE16(28)))
+                ((Slavic, SkTEndian_SwapBE16(29)))
+                ((Vietnamese, SkTEndian_SwapBE16(30)))
+                ((Sindhi, SkTEndian_SwapBE16(31)))
+                ((Uninterpreted, SkTEndian_SwapBE16(32)))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } macintosh;
+        //deprecated, use Unicode instead
+        struct ISO {
+            SK_TYPED_ENUM(Value, SK_OT_USHORT,
+                ((ASCII7, SkTEndian_SwapBE16(0)))
+                ((ISO10646, SkTEndian_SwapBE16(1)))
+                ((ISO88591, SkTEndian_SwapBE16(2)))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } iso;
+        struct Windows {
+            SK_TYPED_ENUM(Value, SK_OT_USHORT,
+                ((Symbol, SkTEndian_SwapBE16(0)))
+                ((UnicodeBMPUCS2, SkTEndian_SwapBE16(1))) //Windows default
+                ((ShiftJIS, SkTEndian_SwapBE16(2)))
+                ((PRC, SkTEndian_SwapBE16(3)))
+                ((Big5, SkTEndian_SwapBE16(4)))
+                ((Wansung, SkTEndian_SwapBE16(5)))
+                ((Johab, SkTEndian_SwapBE16(6)))
+                ((UnicodeUCS4, SkTEndian_SwapBE16(10)))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } windows;
+        SK_OT_USHORT custom;
+    } encodingID;
+    union LanguageID {
+        struct Macintosh {
+            SK_TYPED_ENUM(Value, SK_OT_USHORT,
+                ((English, SkTEndian_SwapBE16(0)))
+                ((French, SkTEndian_SwapBE16(1)))
+                ((German, SkTEndian_SwapBE16(2)))
+                ((Italian, SkTEndian_SwapBE16(3)))
+                ((Dutch, SkTEndian_SwapBE16(4)))
+                ((Swedish, SkTEndian_SwapBE16(5)))
+                ((Spanish, SkTEndian_SwapBE16(6)))
+                ((Danish, SkTEndian_SwapBE16(7)))
+                ((Portuguese, SkTEndian_SwapBE16(8)))
+                ((Norwegian, SkTEndian_SwapBE16(9)))
+                ((Hebrew, SkTEndian_SwapBE16(10)))
+                ((Japanese, SkTEndian_SwapBE16(11)))
+                ((Arabic, SkTEndian_SwapBE16(12)))
+                ((Finnish, SkTEndian_SwapBE16(13)))
+                ((Greek, SkTEndian_SwapBE16(14)))
+                ((Icelandic, SkTEndian_SwapBE16(15)))
+                ((Maltese, SkTEndian_SwapBE16(16)))
+                ((Turkish, SkTEndian_SwapBE16(17)))
+                ((Croatian, SkTEndian_SwapBE16(18)))
+                ((ChineseTraditional, SkTEndian_SwapBE16(19)))
+                ((Urdu, SkTEndian_SwapBE16(20)))
+                ((Hindi, SkTEndian_SwapBE16(21)))
+                ((Thai, SkTEndian_SwapBE16(22)))
+                ((Korean, SkTEndian_SwapBE16(23)))
+                ((Lithuanian, SkTEndian_SwapBE16(24)))
+                ((Polish, SkTEndian_SwapBE16(25)))
+                ((Hungarian, SkTEndian_SwapBE16(26)))
+                ((Estonian, SkTEndian_SwapBE16(27)))
+                ((Latvian, SkTEndian_SwapBE16(28)))
+                ((Sami, SkTEndian_SwapBE16(29)))
+                ((Faroese, SkTEndian_SwapBE16(30)))
+                ((Farsi_Persian, SkTEndian_SwapBE16(31)))
+                ((Russian, SkTEndian_SwapBE16(32)))
+                ((ChineseSimplified, SkTEndian_SwapBE16(33)))
+                ((Flemish, SkTEndian_SwapBE16(34)))
+                ((IrishGaelic, SkTEndian_SwapBE16(35)))
+                ((Albanian, SkTEndian_SwapBE16(36)))
+                ((Romanian, SkTEndian_SwapBE16(37)))
+                ((Czech, SkTEndian_SwapBE16(38)))
+                ((Slovak, SkTEndian_SwapBE16(39)))
+                ((Slovenian, SkTEndian_SwapBE16(40)))
+                ((Yiddish, SkTEndian_SwapBE16(41)))
+                ((Serbian, SkTEndian_SwapBE16(42)))
+                ((Macedonian, SkTEndian_SwapBE16(43)))
+                ((Bulgarian, SkTEndian_SwapBE16(44)))
+                ((Ukrainian, SkTEndian_SwapBE16(45)))
+                ((Byelorussian, SkTEndian_SwapBE16(46)))
+                ((Uzbek, SkTEndian_SwapBE16(47)))
+                ((Kazakh, SkTEndian_SwapBE16(48)))
+                ((AzerbaijaniCyrillic, SkTEndian_SwapBE16(49)))
+                ((AzerbaijaniArabic, SkTEndian_SwapBE16(50)))
+                ((Armenian, SkTEndian_SwapBE16(51)))
+                ((Georgian, SkTEndian_SwapBE16(52)))
+                ((Moldavian, SkTEndian_SwapBE16(53)))
+                ((Kirghiz, SkTEndian_SwapBE16(54)))
+                ((Tajiki, SkTEndian_SwapBE16(55)))
+                ((Turkmen, SkTEndian_SwapBE16(56)))
+                ((MongolianTraditional, SkTEndian_SwapBE16(57)))
+                ((MongolianCyrillic, SkTEndian_SwapBE16(58)))
+                ((Pashto, SkTEndian_SwapBE16(59)))
+                ((Kurdish, SkTEndian_SwapBE16(60)))
+                ((Kashmiri, SkTEndian_SwapBE16(61)))
+                ((Sindhi, SkTEndian_SwapBE16(62)))
+                ((Tibetan, SkTEndian_SwapBE16(63)))
+                ((Nepali, SkTEndian_SwapBE16(64)))
+                ((Sanskrit, SkTEndian_SwapBE16(65)))
+                ((Marathi, SkTEndian_SwapBE16(66)))
+                ((Bengali, SkTEndian_SwapBE16(67)))
+                ((Assamese, SkTEndian_SwapBE16(68)))
+                ((Gujarati, SkTEndian_SwapBE16(69)))
+                ((Punjabi, SkTEndian_SwapBE16(70)))
+                ((Oriya, SkTEndian_SwapBE16(71)))
+                ((Malayalam, SkTEndian_SwapBE16(72)))
+                ((Kannada, SkTEndian_SwapBE16(73)))
+                ((Tamil, SkTEndian_SwapBE16(74)))
+                ((Telugu, SkTEndian_SwapBE16(75)))
+                ((Sinhalese, SkTEndian_SwapBE16(76)))
+                ((Burmese, SkTEndian_SwapBE16(77)))
+                ((Khmer, SkTEndian_SwapBE16(78)))
+                ((Lao, SkTEndian_SwapBE16(79)))
+                ((Vietnamese, SkTEndian_SwapBE16(80)))
+                ((Indonesian, SkTEndian_SwapBE16(81)))
+                ((Tagalong, SkTEndian_SwapBE16(82)))
+                ((MalayRoman, SkTEndian_SwapBE16(83)))
+                ((MalayArabic, SkTEndian_SwapBE16(84)))
+                ((Amharic, SkTEndian_SwapBE16(85)))
+                ((Tigrinya, SkTEndian_SwapBE16(86)))
+                ((Galla, SkTEndian_SwapBE16(87)))
+                ((Somali, SkTEndian_SwapBE16(88)))
+                ((Swahili, SkTEndian_SwapBE16(89)))
+                ((Kinyarwanda_Ruanda, SkTEndian_SwapBE16(90)))
+                ((Rundi, SkTEndian_SwapBE16(91)))
+                ((Nyanja_Chewa, SkTEndian_SwapBE16(92)))
+                ((Malagasy, SkTEndian_SwapBE16(93)))
+                ((Esperanto, SkTEndian_SwapBE16(94)))
+                ((Welsh, SkTEndian_SwapBE16(128)))
+                ((Basque, SkTEndian_SwapBE16(129)))
+                ((Catalan, SkTEndian_SwapBE16(130)))
+                ((Latin, SkTEndian_SwapBE16(131)))
+                ((Quenchua, SkTEndian_SwapBE16(132)))
+                ((Guarani, SkTEndian_SwapBE16(133)))
+                ((Aymara, SkTEndian_SwapBE16(134)))
+                ((Tatar, SkTEndian_SwapBE16(135)))
+                ((Uighur, SkTEndian_SwapBE16(136)))
+                ((Dzongkha, SkTEndian_SwapBE16(137)))
+                ((JavaneseRoman, SkTEndian_SwapBE16(138)))
+                ((SundaneseRoman, SkTEndian_SwapBE16(139)))
+                ((Galician, SkTEndian_SwapBE16(140)))
+                ((Afrikaans, SkTEndian_SwapBE16(141)))
+                ((Breton, SkTEndian_SwapBE16(142)))
+                ((Inuktitut, SkTEndian_SwapBE16(143)))
+                ((ScottishGaelic, SkTEndian_SwapBE16(144)))
+                ((ManxGaelic, SkTEndian_SwapBE16(145)))
+                ((IrishGaelicWithLenition, SkTEndian_SwapBE16(146)))
+                ((Tongan, SkTEndian_SwapBE16(147)))
+                ((GreekPolytonic, SkTEndian_SwapBE16(148)))
+                ((Greenlandic, SkTEndian_SwapBE16(149)))
+                ((AzerbaijaniRoman, SkTEndian_SwapBE16(150)))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } macintosh;
+        struct Windows {
+            SK_TYPED_ENUM(Value, SK_OT_USHORT,
+                ((Afrikaans_SouthAfrica, SkTEndian_SwapBE16(0x0436)))
+                ((Albanian_Albania, SkTEndian_SwapBE16(0x041C)))
+                ((Alsatian_France, SkTEndian_SwapBE16(0x0484)))
+                ((Amharic_Ethiopia, SkTEndian_SwapBE16(0x045E)))
+                ((Arabic_Algeria, SkTEndian_SwapBE16(0x1401)))
+                ((Arabic_Bahrain, SkTEndian_SwapBE16(0x3C01)))
+                ((Arabic_Egypt, SkTEndian_SwapBE16(0x0C01)))
+                ((Arabic_Iraq, SkTEndian_SwapBE16(0x0801)))
+                ((Arabic_Jordan, SkTEndian_SwapBE16(0x2C01)))
+                ((Arabic_Kuwait, SkTEndian_SwapBE16(0x3401)))
+                ((Arabic_Lebanon, SkTEndian_SwapBE16(0x3001)))
+                ((Arabic_Libya, SkTEndian_SwapBE16(0x1001)))
+                ((Arabic_Morocco, SkTEndian_SwapBE16(0x1801)))
+                ((Arabic_Oman, SkTEndian_SwapBE16(0x2001)))
+                ((Arabic_Qatar, SkTEndian_SwapBE16(0x4001)))
+                ((Arabic_SaudiArabia, SkTEndian_SwapBE16(0x0401)))
+                ((Arabic_Syria, SkTEndian_SwapBE16(0x2801)))
+                ((Arabic_Tunisia, SkTEndian_SwapBE16(0x1C01)))
+                ((Arabic_UAE, SkTEndian_SwapBE16(0x3801)))
+                ((Arabic_Yemen, SkTEndian_SwapBE16(0x2401)))
+                ((Armenian_Armenia, SkTEndian_SwapBE16(0x042B)))
+                ((Assamese_India, SkTEndian_SwapBE16(0x044D)))
+                ((AzeriCyrillic_Azerbaijan, SkTEndian_SwapBE16(0x082C)))
+                ((AzeriLatin_Azerbaijan, SkTEndian_SwapBE16(0x042C)))
+                ((Bashkir_Russia, SkTEndian_SwapBE16(0x046D)))
+                ((Basque_Basque, SkTEndian_SwapBE16(0x042D)))
+                ((Belarusian_Belarus, SkTEndian_SwapBE16(0x0423)))
+                ((Bengali_Bangladesh, SkTEndian_SwapBE16(0x0845)))
+                ((Bengali_India, SkTEndian_SwapBE16(0x0445)))
+                ((BosnianCyrillic_BosniaAndHerzegovina, SkTEndian_SwapBE16(0x201A)))
+                ((BosnianLatin_BosniaAndHerzegovina, SkTEndian_SwapBE16(0x141A)))
+                ((Breton_France, SkTEndian_SwapBE16(0x047E)))
+                ((Bulgarian_Bulgaria, SkTEndian_SwapBE16(0x0402)))
+                ((Catalan_Catalan, SkTEndian_SwapBE16(0x0403)))
+                ((Chinese_HongKongSAR, SkTEndian_SwapBE16(0x0C04)))
+                ((Chinese_MacaoSAR, SkTEndian_SwapBE16(0x1404)))
+                ((Chinese_PeoplesRepublicOfChina, SkTEndian_SwapBE16(0x0804)))
+                ((Chinese_Singapore, SkTEndian_SwapBE16(0x1004)))
+                ((Chinese_Taiwan, SkTEndian_SwapBE16(0x0404)))
+                ((Corsican_France, SkTEndian_SwapBE16(0x0483)))
+                ((Croatian_Croatia, SkTEndian_SwapBE16(0x041A)))
+                ((CroatianLatin_BosniaAndHerzegovina, SkTEndian_SwapBE16(0x101A)))
+                ((Czech_CzechRepublic, SkTEndian_SwapBE16(0x0405)))
+                ((Danish_Denmark, SkTEndian_SwapBE16(0x0406)))
+                ((Dari_Afghanistan, SkTEndian_SwapBE16(0x048C)))
+                ((Divehi_Maldives, SkTEndian_SwapBE16(0x0465)))
+                ((Dutch_Belgium, SkTEndian_SwapBE16(0x0813)))
+                ((Dutch_Netherlands, SkTEndian_SwapBE16(0x0413)))
+                ((English_Australia, SkTEndian_SwapBE16(0x0C09)))
+                ((English_Belize, SkTEndian_SwapBE16(0x2809)))
+                ((English_Canada, SkTEndian_SwapBE16(0x1009)))
+                ((English_Caribbean, SkTEndian_SwapBE16(0x2409)))
+                ((English_India, SkTEndian_SwapBE16(0x4009)))
+                ((English_Ireland, SkTEndian_SwapBE16(0x1809)))
+                ((English_Jamaica, SkTEndian_SwapBE16(0x2009)))
+                ((English_Malaysia, SkTEndian_SwapBE16(0x4409)))
+                ((English_NewZealand, SkTEndian_SwapBE16(0x1409)))
+                ((English_RepublicOfThePhilippines, SkTEndian_SwapBE16(0x3409)))
+                ((English_Singapore, SkTEndian_SwapBE16(0x4809)))
+                ((English_SouthAfrica, SkTEndian_SwapBE16(0x1C09)))
+                ((English_TrinidadAndTobago, SkTEndian_SwapBE16(0x2C09)))
+                ((English_UnitedKingdom, SkTEndian_SwapBE16(0x0809)))
+                ((English_UnitedStates, SkTEndian_SwapBE16(0x0409)))
+                ((English_Zimbabwe, SkTEndian_SwapBE16(0x3009)))
+                ((Estonian_Estonia, SkTEndian_SwapBE16(0x0425)))
+                ((Faroese_FaroeIslands, SkTEndian_SwapBE16(0x0438)))
+                ((Filipino_Philippines, SkTEndian_SwapBE16(0x0464)))
+                ((Finnish_Finland, SkTEndian_SwapBE16(0x040B)))
+                ((French_Belgium, SkTEndian_SwapBE16(0x080C)))
+                ((French_Canada, SkTEndian_SwapBE16(0x0C0C)))
+                ((French_France, SkTEndian_SwapBE16(0x040C)))
+                ((French_Luxembourg, SkTEndian_SwapBE16(0x140c)))
+                ((French_PrincipalityOfMonoco, SkTEndian_SwapBE16(0x180C)))
+                ((French_Switzerland, SkTEndian_SwapBE16(0x100C)))
+                ((Frisian_Netherlands, SkTEndian_SwapBE16(0x0462)))
+                ((Galician_Galician, SkTEndian_SwapBE16(0x0456)))
+                ((Georgian_Georgia, SkTEndian_SwapBE16(0x0437)))
+                ((German_Austria, SkTEndian_SwapBE16(0x0C07)))
+                ((German_Germany, SkTEndian_SwapBE16(0x0407)))
+                ((German_Liechtenstein, SkTEndian_SwapBE16(0x1407)))
+                ((German_Luxembourg, SkTEndian_SwapBE16(0x1007)))
+                ((German_Switzerland, SkTEndian_SwapBE16(0x0807)))
+                ((Greek_Greece, SkTEndian_SwapBE16(0x0408)))
+                ((Greenlandic_Greenland, SkTEndian_SwapBE16(0x046F)))
+                ((Gujarati_India, SkTEndian_SwapBE16(0x0447)))
+                ((HausaLatin_Nigeria, SkTEndian_SwapBE16(0x0468)))
+                ((Hebrew_Israel, SkTEndian_SwapBE16(0x040D)))
+                ((Hindi_India, SkTEndian_SwapBE16(0x0439)))
+                ((Hungarian_Hungary, SkTEndian_SwapBE16(0x040E)))
+                ((Icelandic_Iceland, SkTEndian_SwapBE16(0x040F)))
+                ((Igbo_Nigeria, SkTEndian_SwapBE16(0x0470)))
+                ((Indonesian_Indonesia, SkTEndian_SwapBE16(0x0421)))
+                ((Inuktitut_Canada, SkTEndian_SwapBE16(0x045D)))
+                ((InuktitutLatin_Canada, SkTEndian_SwapBE16(0x085D)))
+                ((Irish_Ireland, SkTEndian_SwapBE16(0x083C)))
+                ((isiXhosa_SouthAfrica, SkTEndian_SwapBE16(0x0434)))
+                ((isiZulu_SouthAfrica, SkTEndian_SwapBE16(0x0435)))
+                ((Italian_Italy, SkTEndian_SwapBE16(0x0410)))
+                ((Italian_Switzerland, SkTEndian_SwapBE16(0x0810)))
+                ((Japanese_Japan, SkTEndian_SwapBE16(0x0411)))
+                ((Kannada_India, SkTEndian_SwapBE16(0x044B)))
+                ((Kazakh_Kazakhstan, SkTEndian_SwapBE16(0x043F)))
+                ((Khmer_Cambodia, SkTEndian_SwapBE16(0x0453)))
+                ((Kiche_Guatemala, SkTEndian_SwapBE16(0x0486)))
+                ((Kinyarwanda_Rwanda, SkTEndian_SwapBE16(0x0487)))
+                ((Kiswahili_Kenya, SkTEndian_SwapBE16(0x0441)))
+                ((Konkani_India, SkTEndian_SwapBE16(0x0457)))
+                ((Korean_Korea, SkTEndian_SwapBE16(0x0412)))
+                ((Kyrgyz_Kyrgyzstan, SkTEndian_SwapBE16(0x0440)))
+                ((Lao_LaoPDR, SkTEndian_SwapBE16(0x0454)))
+                ((Latvian_Latvia, SkTEndian_SwapBE16(0x0426)))
+                ((Lithuanian_Lithuania, SkTEndian_SwapBE16(0x0427)))
+                ((LowerSorbian_Germany, SkTEndian_SwapBE16(0x082E)))
+                ((Luxembourgish_Luxembourg, SkTEndian_SwapBE16(0x046E)))
+                ((MacedonianFYROM_FormerYugoslavRepublicOfMacedonia, SkTEndian_SwapBE16(0x042F)))
+                ((Malay_BruneiDarussalam, SkTEndian_SwapBE16(0x083E)))
+                ((Malay_Malaysia, SkTEndian_SwapBE16(0x043E)))
+                ((Malayalam_India, SkTEndian_SwapBE16(0x044C)))
+                ((Maltese_Malta, SkTEndian_SwapBE16(0x043A)))
+                ((Maori_NewZealand, SkTEndian_SwapBE16(0x0481)))
+                ((Mapudungun_Chile, SkTEndian_SwapBE16(0x047A)))
+                ((Marathi_India, SkTEndian_SwapBE16(0x044E)))
+                ((Mohawk_Mohawk, SkTEndian_SwapBE16(0x047C)))
+                ((MongolianCyrillic_Mongolia, SkTEndian_SwapBE16(0x0450)))
+                ((MongolianTraditional_PeoplesRepublicOfChina, SkTEndian_SwapBE16(0x0850)))
+                ((Nepali_Nepal, SkTEndian_SwapBE16(0x0461)))
+                ((NorwegianBokmal_Norway, SkTEndian_SwapBE16(0x0414)))
+                ((NorwegianNynorsk_Norway, SkTEndian_SwapBE16(0x0814)))
+                ((Occitan_France, SkTEndian_SwapBE16(0x0482)))
+                ((Odia_India, SkTEndian_SwapBE16(0x0448)))
+                ((Pashto_Afghanistan, SkTEndian_SwapBE16(0x0463)))
+                ((Polish_Poland, SkTEndian_SwapBE16(0x0415)))
+                ((Portuguese_Brazil, SkTEndian_SwapBE16(0x0416)))
+                ((Portuguese_Portugal, SkTEndian_SwapBE16(0x0816)))
+                ((Punjabi_India, SkTEndian_SwapBE16(0x0446)))
+                ((Quechua_Bolivia, SkTEndian_SwapBE16(0x046B)))
+                ((Quechua_Ecuador, SkTEndian_SwapBE16(0x086B)))
+                ((Quechua_Peru, SkTEndian_SwapBE16(0x0C6B)))
+                ((Romanian_Romania, SkTEndian_SwapBE16(0x0418)))
+                ((Romansh_Switzerland, SkTEndian_SwapBE16(0x0417)))
+                ((Russian_Russia, SkTEndian_SwapBE16(0x0419)))
+                ((SamiInari_Finland, SkTEndian_SwapBE16(0x243B)))
+                ((SamiLule_Norway, SkTEndian_SwapBE16(0x103B)))
+                ((SamiLule_Sweden, SkTEndian_SwapBE16(0x143B)))
+                ((SamiNorthern_Finland, SkTEndian_SwapBE16(0x0C3B)))
+                ((SamiNorthern_Norway, SkTEndian_SwapBE16(0x043B)))
+                ((SamiNorthern_Sweden, SkTEndian_SwapBE16(0x083B)))
+                ((SamiSkolt_Finland, SkTEndian_SwapBE16(0x203B)))
+                ((SamiSouthern_Norway, SkTEndian_SwapBE16(0x183B)))
+                ((SamiSouthern_Sweden, SkTEndian_SwapBE16(0x1C3B)))
+                ((Sanskrit_India, SkTEndian_SwapBE16(0x044F)))
+                ((SerbianCyrillic_BosniaAndHerzegovina, SkTEndian_SwapBE16(0x1C1A)))
+                ((SerbianCyrillic_Serbia, SkTEndian_SwapBE16(0x0C1A)))
+                ((SerbianLatin_BosniaAndHerzegovina, SkTEndian_SwapBE16(0x181A)))
+                ((SerbianLatin_Serbia, SkTEndian_SwapBE16(0x081A)))
+                ((SesothoSaLeboa_SouthAfrica, SkTEndian_SwapBE16(0x046C)))
+                ((Setswana_SouthAfrica, SkTEndian_SwapBE16(0x0432)))
+                ((Sinhala_SriLanka, SkTEndian_SwapBE16(0x045B)))
+                ((Slovak_Slovakia, SkTEndian_SwapBE16(0x041B)))
+                ((Slovenian_Slovenia, SkTEndian_SwapBE16(0x0424)))
+                ((Spanish_Argentina, SkTEndian_SwapBE16(0x2C0A)))
+                ((Spanish_Bolivia, SkTEndian_SwapBE16(0x400A)))
+                ((Spanish_Chile, SkTEndian_SwapBE16(0x340A)))
+                ((Spanish_Colombia, SkTEndian_SwapBE16(0x240A)))
+                ((Spanish_CostaRica, SkTEndian_SwapBE16(0x140A)))
+                ((Spanish_DominicanRepublic, SkTEndian_SwapBE16(0x1C0A)))
+                ((Spanish_Ecuador, SkTEndian_SwapBE16(0x300A)))
+                ((Spanish_ElSalvador, SkTEndian_SwapBE16(0x440A)))
+                ((Spanish_Guatemala, SkTEndian_SwapBE16(0x100A)))
+                ((Spanish_Honduras, SkTEndian_SwapBE16(0x480A)))
+                ((Spanish_Mexico, SkTEndian_SwapBE16(0x080A)))
+                ((Spanish_Nicaragua, SkTEndian_SwapBE16(0x4C0A)))
+                ((Spanish_Panama, SkTEndian_SwapBE16(0x180A)))
+                ((Spanish_Paraguay, SkTEndian_SwapBE16(0x3C0A)))
+                ((Spanish_Peru, SkTEndian_SwapBE16(0x280A)))
+                ((Spanish_PuertoRico, SkTEndian_SwapBE16(0x500A)))
+                ((SpanishModernSort_Spain, SkTEndian_SwapBE16(0x0C0A)))
+                ((SpanishTraditionalSort_Spain, SkTEndian_SwapBE16(0x040A)))
+                ((Spanish_UnitedStates, SkTEndian_SwapBE16(0x540A)))
+                ((Spanish_Uruguay, SkTEndian_SwapBE16(0x380A)))
+                ((Spanish_Venezuela, SkTEndian_SwapBE16(0x200A)))
+                ((Sweden_Finland, SkTEndian_SwapBE16(0x081D)))
+                ((Swedish_Sweden, SkTEndian_SwapBE16(0x041D)))
+                ((Syriac_Syria, SkTEndian_SwapBE16(0x045A)))
+                ((TajikCyrillic_Tajikistan, SkTEndian_SwapBE16(0x0428)))
+                ((TamazightLatin_Algeria, SkTEndian_SwapBE16(0x085F)))
+                ((Tamil_India, SkTEndian_SwapBE16(0x0449)))
+                ((Tatar_Russia, SkTEndian_SwapBE16(0x0444)))
+                ((Telugu_India, SkTEndian_SwapBE16(0x044A)))
+                ((Thai_Thailand, SkTEndian_SwapBE16(0x041E)))
+                ((Tibetan_PRC, SkTEndian_SwapBE16(0x0451)))
+                ((Turkish_Turkey, SkTEndian_SwapBE16(0x041F)))
+                ((Turkmen_Turkmenistan, SkTEndian_SwapBE16(0x0442)))
+                ((Uighur_PRC, SkTEndian_SwapBE16(0x0480)))
+                ((Ukrainian_Ukraine, SkTEndian_SwapBE16(0x0422)))
+                ((UpperSorbian_Germany, SkTEndian_SwapBE16(0x042E)))
+                ((Urdu_IslamicRepublicOfPakistan, SkTEndian_SwapBE16(0x0420)))
+                ((UzbekCyrillic_Uzbekistan, SkTEndian_SwapBE16(0x0843)))
+                ((UzbekLatin_Uzbekistan, SkTEndian_SwapBE16(0x0443)))
+                ((Vietnamese_Vietnam, SkTEndian_SwapBE16(0x042A)))
+                ((Welsh_UnitedKingdom, SkTEndian_SwapBE16(0x0452)))
+                ((Wolof_Senegal, SkTEndian_SwapBE16(0x0488)))
+                ((Yakut_Russia, SkTEndian_SwapBE16(0x0485)))
+                ((Yi_PRC, SkTEndian_SwapBE16(0x0478)))
+                ((Yoruba_Nigeria, SkTEndian_SwapBE16(0x046A)))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } windows;
+        //languageTagID - 0x8000 is an index into the langTagRecord array.
+        SK_OT_USHORT languageTagID;
+    } languageID;
+    union NameID {
+       struct Predefined {
+            SK_TYPED_ENUM(Value, SK_OT_USHORT,
+                ((CopyrightNotice, SkTEndian_SwapBE16(0)))
+                ((FontFamilyName, SkTEndian_SwapBE16(1)))
+                ((FontSubfamilyName, SkTEndian_SwapBE16(2)))
+                ((UniqueFontIdentifier, SkTEndian_SwapBE16(3)))
+                ((FullFontName, SkTEndian_SwapBE16(4)))
+                ((VersionString, SkTEndian_SwapBE16(5))) //Version <number>.<number>
+                ((PostscriptName, SkTEndian_SwapBE16(6))) //See spec for constraints.
+                ((Trademark, SkTEndian_SwapBE16(7)))
+                ((ManufacturerName, SkTEndian_SwapBE16(8)))
+                ((Designer, SkTEndian_SwapBE16(9)))
+                ((Description, SkTEndian_SwapBE16(10)))
+                ((URLVendor, SkTEndian_SwapBE16(11)))
+                ((URLDesigner, SkTEndian_SwapBE16(12)))
+                ((LicenseDescription, SkTEndian_SwapBE16(13)))
+                ((LicenseInfoURL, SkTEndian_SwapBE16(14)))
+                ((PreferredFamily, SkTEndian_SwapBE16(16)))
+                ((PreferredSubfamily, SkTEndian_SwapBE16(17)))
+                ((CompatibleFullName, SkTEndian_SwapBE16(18)))
+                ((SampleText, SkTEndian_SwapBE16(19)))
+                ((PostscriptCIDFindfontName, SkTEndian_SwapBE16(20)))
+                ((WWSFamilyName, SkTEndian_SwapBE16(21)))
+                ((WWSSubfamilyName, SkTEndian_SwapBE16(22)))
+                SK_SEQ_END,
+            (value)SK_SEQ_END)
+        } predefined;
+        //values > 256 are font specific strings.
+        SK_OT_USHORT fontSpecific;
+    } nameID;
+    SK_OT_USHORT length;
+    SK_OT_USHORT offset; //From start of storage area.
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkOTTableName) == 6, sizeof_SkOTTableName_not_6);
+SK_COMPILE_ASSERT(sizeof(SkOTTableNameF1) == 2, sizeof_SkOTTableNameF1_not_2);
+SK_COMPILE_ASSERT(sizeof(SkOTTableNameLangTagRecord) == 4, sizeof_SkOTTableNameLangTagRecord_not_4);
+SK_COMPILE_ASSERT(sizeof(SkOTTableNameRecord) == 12, sizeof_SkOTTableNameRecord_not_12);
+
+#endif
diff --git a/src/sfnt/SkOTTable_post.h b/src/sfnt/SkOTTable_post.h
new file mode 100644
index 0000000..f563b08
--- /dev/null
+++ b/src/sfnt/SkOTTable_post.h
@@ -0,0 +1,52 @@
+/*
+ * 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 SkOTTable_post_DEFINED
+#define SkOTTable_post_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkOTTablePostScript {
+    static const SK_OT_CHAR TAG0 = 'p';
+    static const SK_OT_CHAR TAG1 = 'o';
+    static const SK_OT_CHAR TAG2 = 's';
+    static const SK_OT_CHAR TAG3 = 't';
+    static const SK_OT_ULONG TAG = SkOTTableTAG<SkOTTablePostScript>::value;
+
+    struct Format {
+        SK_TYPED_ENUM(Value, SK_OT_Fixed,
+            ((version1, SkTEndian_SwapBE32(0x00010000)))
+            ((version2, SkTEndian_SwapBE32(0x00020000)))
+            ((version2_5, SkTEndian_SwapBE32(0x00025000)))
+            ((version3, SkTEndian_SwapBE32(0x00030000)))
+            ((version4, SkTEndian_SwapBE32(0x00040000)))
+            SK_SEQ_END,
+        SK_SEQ_END)
+        SK_OT_Fixed value;
+    } format;
+    SK_OT_Fixed italicAngle;
+    SK_OT_FWORD underlinePosition;
+    SK_OT_FWORD underlineThickness;
+    SK_OT_ULONG isFixedPitch;
+    SK_OT_ULONG minMemType42;
+    SK_OT_ULONG maxMemType42;
+    SK_OT_ULONG minMemType1;
+    SK_OT_ULONG maxMemType1;
+};
+
+#pragma pack(pop)
+
+
+#include <stddef.h>
+SK_COMPILE_ASSERT(offsetof(SkOTTablePostScript, maxMemType1) == 28, SkOTTablePostScript_maxMemType1_not_at_28);
+SK_COMPILE_ASSERT(sizeof(SkOTTablePostScript) == 32, sizeof_SkOTTablePostScript_not_32);
+
+#endif
diff --git a/src/sfnt/SkOTUtils.cpp b/src/sfnt/SkOTUtils.cpp
new file mode 100644
index 0000000..0247cda
--- /dev/null
+++ b/src/sfnt/SkOTUtils.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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 "SkData.h"
+#include "SkEndian.h"
+#include "SkSFNTHeader.h"
+#include "SkStream.h"
+#include "SkOTTable_head.h"
+#include "SkOTTable_name.h"
+#include "SkOTTableTypes.h"
+#include "SkOTUtils.h"
+
+extern const uint8_t SK_OT_GlyphData_NoOutline[] = {
+    0x0,0x0, //SkOTTableGlyphData::numberOfContours
+    0x0,0x0, //SkOTTableGlyphData::xMin
+    0x0,0x0, //SkOTTableGlyphData::yMin
+    0x0,0x0, //SkOTTableGlyphData::xMax
+    0x0,0x0, //SkOTTableGlyphData::yMax
+
+    0x0,0x0, //SkOTTableGlyphDataInstructions::length
+};
+
+uint32_t SkOTUtils::CalcTableChecksum(SK_OT_ULONG *data, size_t length) {
+    uint32_t sum = 0;
+    SK_OT_ULONG *dataEnd = data + ((length + 3) & ~3) / sizeof(SK_OT_ULONG);
+    for (; data < dataEnd; ++data) {
+        sum += SkEndian_SwapBE32(*data);
+    }
+    return sum;
+}
+
+SkData* SkOTUtils::RenameFont(SkStream* fontData, const char* fontName, int fontNameLen) {
+
+    // Get the sfnt header.
+    SkSFNTHeader sfntHeader;
+    if (fontData->read(&sfntHeader, sizeof(sfntHeader)) < sizeof(sfntHeader)) {
+        return NULL;
+    }
+
+    // Find the existing 'name' table.
+    int tableIndex;
+    SkSFNTHeader::TableDirectoryEntry tableEntry;
+    int numTables = SkEndian_SwapBE16(sfntHeader.numTables);
+    for (tableIndex = 0; tableIndex < numTables; ++tableIndex) {
+        if (fontData->read(&tableEntry, sizeof(tableEntry)) < sizeof(tableEntry)) {
+            return NULL;
+        }
+        if (SkOTTableName::TAG == tableEntry.tag) {
+            break;
+        }
+    }
+    if (tableIndex == numTables) {
+        return NULL;
+    }
+
+    if (!fontData->rewind()) {
+        return NULL;
+    }
+
+    // The required 'name' record types: Family, Style, Unique, Full and PostScript.
+    const SkOTTableNameRecord::NameID::Predefined::Value namesToCreate[] = {
+        SkOTTableNameRecord::NameID::Predefined::FontFamilyName,
+        SkOTTableNameRecord::NameID::Predefined::FontSubfamilyName,
+        SkOTTableNameRecord::NameID::Predefined::UniqueFontIdentifier,
+        SkOTTableNameRecord::NameID::Predefined::FullFontName,
+        SkOTTableNameRecord::NameID::Predefined::PostscriptName,
+    };
+    const int namesCount = SK_ARRAY_COUNT(namesToCreate);
+
+    // Copy the data, leaving out the old name table.
+    // In theory, we could also remove the DSIG table if it exists.
+    size_t nameTableLogicalSize = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableNameRecord)) + (fontNameLen * sizeof(wchar_t));
+    size_t nameTablePhysicalSize = (nameTableLogicalSize + 3) & ~3; // Rounded up to a multiple of 4.
+
+    size_t oldNameTablePhysicalSize = (SkEndian_SwapBE32(tableEntry.logicalLength) + 3) & ~3; // Rounded up to a multiple of 4.
+    size_t oldNameTableOffset = SkEndian_SwapBE32(tableEntry.offset);
+
+    //originalDataSize is the size of the original data without the name table.
+    size_t originalDataSize = fontData->getLength() - oldNameTablePhysicalSize;
+    size_t newDataSize = originalDataSize + nameTablePhysicalSize;
+
+    SK_OT_BYTE* data = static_cast<SK_OT_BYTE*>(sk_malloc_throw(newDataSize));
+    SkAutoTUnref<SkData> rewrittenFontData(SkData::NewFromMalloc(data, newDataSize));
+
+    if (fontData->read(data, oldNameTableOffset) < oldNameTableOffset) {
+        return NULL;
+    }
+    if (fontData->skip(oldNameTablePhysicalSize) < oldNameTablePhysicalSize) {
+        return NULL;
+    }
+    if (fontData->read(data + oldNameTableOffset, originalDataSize - oldNameTableOffset) < originalDataSize - oldNameTableOffset) {
+        return NULL;
+    }
+
+    //Fix up the offsets of the directory entries after the old 'name' table entry.
+    SkSFNTHeader::TableDirectoryEntry* currentEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader));
+    SkSFNTHeader::TableDirectoryEntry* endEntry = currentEntry + numTables;
+    SkSFNTHeader::TableDirectoryEntry* headTableEntry = NULL;
+    for (; currentEntry < endEntry; ++currentEntry) {
+        uint32_t oldOffset = SkEndian_SwapBE32(currentEntry->offset);
+        if (oldOffset > oldNameTableOffset) {
+            currentEntry->offset = SkEndian_SwapBE32(oldOffset - oldNameTablePhysicalSize);
+        }
+        if (SkOTTableHead::TAG == currentEntry->tag) {
+            headTableEntry = currentEntry;
+        }
+    }
+
+    // Make the table directory entry point to the new 'name' table.
+    SkSFNTHeader::TableDirectoryEntry* nameTableEntry = reinterpret_cast<SkSFNTHeader::TableDirectoryEntry*>(data + sizeof(SkSFNTHeader)) + tableIndex;
+    nameTableEntry->logicalLength = SkEndian_SwapBE32(nameTableLogicalSize);
+    nameTableEntry->offset = SkEndian_SwapBE32(originalDataSize);
+
+    // Write the new 'name' table after the original font data.
+    SkOTTableName* nameTable = reinterpret_cast<SkOTTableName*>(data + originalDataSize);
+    unsigned short stringOffset = sizeof(SkOTTableName) + (namesCount * sizeof(SkOTTableNameRecord));
+    nameTable->format = SkOTTableName::format_0;
+    nameTable->count = SkEndian_SwapBE16(namesCount);
+    nameTable->stringOffset = SkEndian_SwapBE16(stringOffset);
+
+    SkOTTableNameRecord* nameRecords = reinterpret_cast<SkOTTableNameRecord*>(data + originalDataSize + sizeof(SkOTTableName));
+    for (int i = 0; i < namesCount; ++i) {
+        nameRecords[i].platformID.value = SkOTTableNameRecord::PlatformID::Windows;
+        nameRecords[i].encodingID.windows.value = SkOTTableNameRecord::EncodingID::Windows::UnicodeBMPUCS2;
+        nameRecords[i].languageID.windows.value = SkOTTableNameRecord::LanguageID::Windows::English_UnitedStates;
+        nameRecords[i].nameID.predefined.value = namesToCreate[i];
+        nameRecords[i].offset = SkEndian_SwapBE16(0);
+        nameRecords[i].length = SkEndian_SwapBE16(fontNameLen * sizeof(wchar_t));
+    }
+
+    SK_OT_USHORT* nameString = reinterpret_cast<SK_OT_USHORT*>(data + originalDataSize + stringOffset);
+    for (int i = 0; i < fontNameLen; ++i) {
+        nameString[i] = SkEndian_SwapBE16(fontName[i]);
+    }
+
+    unsigned char* logical = data + originalDataSize + nameTableLogicalSize;
+    unsigned char* physical = data + originalDataSize + nameTablePhysicalSize;
+    for (; logical < physical; ++logical) {
+        *logical = 0;
+    }
+
+    // Update the table checksum in the directory entry.
+    nameTableEntry->checksum = SkEndian_SwapBE32(SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(nameTable), nameTableLogicalSize));
+
+    // Update the checksum adjustment in the head table.
+    if (headTableEntry) {
+        size_t headTableOffset = SkEndian_SwapBE32(headTableEntry->offset);
+        if (headTableOffset + sizeof(SkOTTableHead) < originalDataSize) {
+            SkOTTableHead* headTable = reinterpret_cast<SkOTTableHead*>(data + headTableOffset);
+            headTable->checksumAdjustment = SkEndian_SwapBE32(0);
+            uint32_t unadjustedFontChecksum = SkOTUtils::CalcTableChecksum(reinterpret_cast<SK_OT_ULONG*>(data), originalDataSize + nameTablePhysicalSize);
+            headTable->checksumAdjustment = SkEndian_SwapBE32(SkOTTableHead::fontChecksum - unadjustedFontChecksum);
+        }
+    }
+
+    return rewrittenFontData.detach();
+}
diff --git a/src/sfnt/SkOTUtils.h b/src/sfnt/SkOTUtils.h
new file mode 100644
index 0000000..3c5ada2
--- /dev/null
+++ b/src/sfnt/SkOTUtils.h
@@ -0,0 +1,37 @@
+/*
+ * 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 SkOTUtils_DEFINED
+#define SkOTUtils_DEFINED
+
+#include "SkOTTableTypes.h"
+class SkData;
+class SkStream;
+
+struct SkOTUtils {
+    /**
+      *  Calculates the OpenType checksum for data.
+      */
+    static uint32_t CalcTableChecksum(SK_OT_ULONG *data, size_t length);
+
+    /**
+      *  Renames an sfnt font. On failure (invalid data or not an sfnt font)
+      *  returns NULL.
+      *
+      *  Essentially, this removes any existing 'name' table and replaces it
+      *  with a new one in which FontFamilyName, FontSubfamilyName,
+      *  UniqueFontIdentifier, FullFontName, and PostscriptName are fontName.
+      *
+      *  The new 'name' table records will be written with the Windows,
+      *  UnicodeBMPUCS2, and English_UnitedStates settings.
+      *
+      *  fontName and fontNameLen must be specified in terms of ASCII chars.
+      */
+    static SkData* RenameFont(SkStream* fontData, const char* fontName, int fontNameLen);
+};
+
+#endif
diff --git a/src/sfnt/SkPanose.h b/src/sfnt/SkPanose.h
new file mode 100644
index 0000000..873c093
--- /dev/null
+++ b/src/sfnt/SkPanose.h
@@ -0,0 +1,639 @@
+/*
+ * 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 SkPanose_DEFINED
+#define SkPanose_DEFINED
+
+#include "SkOTTableTypes.h"
+#include "SkTypedEnum.h"
+
+#pragma pack(push, 1)
+
+struct SkPanose {
+    //This value changes the meaning of the following 9 bytes.
+    struct FamilyType {
+        SK_TYPED_ENUM(Value, SK_OT_BYTE,
+            ((Any, 0))
+            ((NoFit, 1))
+            ((TextAndDisplay, 2))
+            ((Script, 3))
+            ((Decorative, 4))
+            ((Pictoral, 5))
+            SK_SEQ_END,
+        (value)SK_SEQ_END)
+    } bFamilyType;
+
+    union Data {
+        struct TextAndDisplay {
+            struct SerifStyle {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Cove, 2))
+                    ((ObtuseCove, 3))
+                    ((SquareCove, 4))
+                    ((ObtuseSquareCove, 5))
+                    ((Square, 6))
+                    ((Thin, 7))
+                    ((Bone, 8))
+                    ((Exaggerated, 9))
+                    ((Triangle, 10))
+                    ((NormalSans, 11))
+                    ((ObtuseSans, 12))
+                    ((PerpSans, 13))
+                    ((Flared, 14))
+                    ((Rounded, 15))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bSerifStyle;
+
+            struct Weight {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((VeryLight, 2))
+                    ((Light, 3))
+                    ((Thin, 4))
+                    ((Book, 5))
+                    ((Medium, 6))
+                    ((Demi, 7))
+                    ((Bold, 8))
+                    ((Heavy, 9))
+                    ((Black, 10))
+                    ((ExtraBlack, 11))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bWeight;
+
+            struct Proportion {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((OldStyle, 2))
+                    ((Modern, 3))
+                    ((EvenWidth, 4))
+                    ((Expanded, 5))
+                    ((Condensed, 6))
+                    ((VeryExpanded, 7))
+                    ((VeryCondensed, 8))
+                    ((Monospaced, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bProportion;
+
+            struct Contrast {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((None, 2))
+                    ((VeryLow, 3))
+                    ((Low, 4))
+                    ((MediumLow, 5))
+                    ((Medium, 6))
+                    ((MediumHigh, 7))
+                    ((High, 8))
+                    ((VeryHigh, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bContrast;
+
+#ifdef SK_WIN_PANOSE
+            //This is what Windows (and FontForge and Apple TT spec) define.
+            //The Impact font uses 9.
+            struct StrokeVariation {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((GradualDiagonal, 2))
+                    ((GradualTransitional, 3))
+                    ((GradualVertical, 4))
+                    ((GradualHorizontal, 5))
+                    ((RapidVertical, 6))
+                    ((RapidHorizontal, 7))
+                    ((InstantVertical, 8))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bStrokeVariation;
+#else
+            //Stroke variation description in OT OS/2 ver0,ver1 is incorrect.
+            //This is what HP Panose says.
+            struct StrokeVariation {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((NoVariation, 2))
+                    ((Gradual_Diagonal, 3))
+                    ((Gradual_Transitional, 4))
+                    ((Gradual_Vertical, 5))
+                    ((Gradual_Horizontal, 6))
+                    ((Rapid_Vertical, 7))
+                    ((Rapid_Horizontal, 8))
+                    ((Instant_Vertical, 9))
+                    ((Instant_Horizontal, 10))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bStrokeVariation;
+#endif
+
+            struct ArmStyle {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((StraightArms_Horizontal, 2))
+                    ((StraightArms_Wedge, 3))
+                    ((StraightArms_Vertical, 4))
+                    ((StraightArms_SingleSerif, 5))
+                    ((StraightArms_DoubleSerif, 6))
+                    ((NonStraightArms_Horizontal, 7))
+                    ((NonStraightArms_Wedge, 8))
+                    ((NonStraightArms_Vertical, 9))
+                    ((NonStraightArms_SingleSerif, 10))
+                    ((NonStraightArms_DoubleSerif, 11))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bArmStyle;
+
+            struct Letterform {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Normal_Contact, 2))
+                    ((Normal_Weighted, 3))
+                    ((Normal_Boxed, 4))
+                    ((Normal_Flattened, 5))
+                    ((Normal_Rounded, 6))
+                    ((Normal_OffCenter, 7))
+                    ((Normal_Square, 8))
+                    ((Oblique_Contact, 9))
+                    ((Oblique_Weighted, 10))
+                    ((Oblique_Boxed, 11))
+                    ((Oblique_Flattened, 12))
+                    ((Oblique_Rounded, 13))
+                    ((Oblique_OffCenter, 14))
+                    ((Oblique_Square, 15))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bLetterform;
+
+            struct Midline {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Standard_Trimmed, 2))
+                    ((Standard_Pointed, 3))
+                    ((Standard_Serifed, 4))
+                    ((High_Trimmed, 5))
+                    ((High_Pointed, 6))
+                    ((High_Serifed, 7))
+                    ((Constant_Trimmed, 8))
+                    ((Constant_Pointed, 9))
+                    ((Constant_Serifed, 10))
+                    ((Low_Trimmed, 11))
+                    ((Low_Pointed, 12))
+                    ((Low_Serifed, 13))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bMidline;
+
+            struct XHeight {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Constant_Small, 2))
+                    ((Constant_Standard, 3))
+                    ((Constant_Large, 4))
+                    ((Ducking_Small, 5))
+                    ((Ducking_Standard, 6))
+                    ((Ducking_Large, 7))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bXHeight;
+        } textAndDisplay;
+
+        struct Script {
+            struct ToolKind {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((FlatNib, 2))
+                    ((PressurePoint, 3))
+                    ((Engraved, 4))
+                    ((Ball, 5))
+                    ((Brush, 6))
+                    ((Rough, 7))
+                    ((FeltPen, 8))
+                    ((WildBrush, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bToolKind;
+
+            struct Weight {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((VeryLight, 2))
+                    ((Light, 3))
+                    ((Thin, 4))
+                    ((Book, 5))
+                    ((Medium, 6))
+                    ((Demi, 7))
+                    ((Bold, 8))
+                    ((Heavy, 9))
+                    ((Black, 10))
+                    ((ExtraBlack, 11))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bWeight;
+
+            struct Spacing {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((ProportionalSpaced, 2))
+                    ((Monospaced, 3))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bSpacing;
+
+            struct AspectRatio {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((VeryCondensed, 2))
+                    ((Condensed, 3))
+                    ((Normal, 4))
+                    ((Expanded, 5))
+                    ((VeryExpanded, 6))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bAspectRatio;
+
+            struct Contrast {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((None, 2))
+                    ((VeryLow, 3))
+                    ((Low, 4))
+                    ((MediumLow, 5))
+                    ((Medium, 6))
+                    ((MediumHigh, 7))
+                    ((High, 8))
+                    ((VeryHigh, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bContrast;
+
+            struct Topology {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Roman_Disconnected, 2))
+                    ((Roman_Trailing, 3))
+                    ((Roman_Connected, 4))
+                    ((Cursive_Disconnected, 5))
+                    ((Cursive_Trailing, 6))
+                    ((Cursive_Connected, 7))
+                    ((Blackletter_Disconnected, 8))
+                    ((Blackletter_Trailing, 9))
+                    ((Blackletter_Connected, 10))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bTopology;
+
+            struct Form {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Upright_NoWrapping, 2))
+                    ((Upright_SomeWrapping, 3))
+                    ((Upright_MoreWrapping, 4))
+                    ((Upright_ExtremeWrapping, 5))
+                    ((Oblique_NoWrapping, 6))
+                    ((Oblique_SomeWrapping, 7))
+                    ((Oblique_MoreWrapping, 8))
+                    ((Oblique_ExtremeWrapping, 9))
+                    ((Exaggerated_NoWrapping, 10))
+                    ((Exaggerated_SomeWrapping, 11))
+                    ((Exaggerated_MoreWrapping, 12))
+                    ((Exaggerated_ExtremeWrapping, 13))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bForm;
+
+            struct Finials {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((None_NoLoops, 2))
+                    ((None_ClosedLoops, 3))
+                    ((None_OpenLoops, 4))
+                    ((Sharp_NoLoops, 5))
+                    ((Sharp_ClosedLoops, 6))
+                    ((Sharp_OpenLoops, 7))
+                    ((Tapered_NoLoops, 8))
+                    ((Tapered_ClosedLoops, 9))
+                    ((Tapered_OpenLoops, 10))
+                    ((Round_NoLoops, 11))
+                    ((Round_ClosedLoops, 12))
+                    ((Round_OpenLoops, 13))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bFinials;
+
+            struct XAscent {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((VeryLow, 2))
+                    ((Low, 3))
+                    ((Medium, 4))
+                    ((High, 5))
+                    ((VeryHigh, 6))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bXAscent;
+        } script;
+
+        struct Decorative {
+            struct Class {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Derivative, 2))
+                    ((NonStandard_Topology, 3))
+                    ((NonStandard_Elements, 4))
+                    ((NonStandard_Aspect, 5))
+                    ((Initials, 6))
+                    ((Cartoon, 7))
+                    ((PictureStems, 8))
+                    ((Ornamented, 9))
+                    ((TextAndBackground, 10))
+                    ((Collage, 11))
+                    ((Montage, 12))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bClass;
+
+            struct Weight {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((VeryLight, 2))
+                    ((Light, 3))
+                    ((Thin, 4))
+                    ((Book, 5))
+                    ((Medium, 6))
+                    ((Demi, 7))
+                    ((Bold, 8))
+                    ((Heavy, 9))
+                    ((Black, 10))
+                    ((ExtraBlack, 11))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bWeight;
+
+            struct Aspect {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((SuperCondensed, 2))
+                    ((VeryCondensed, 3))
+                    ((Condensed, 4))
+                    ((Normal, 5))
+                    ((Extended, 6))
+                    ((VeryExtended, 7))
+                    ((SuperExtended, 8))
+                    ((Monospaced, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bAspect;
+
+            struct Contrast {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((None, 2))
+                    ((VeryLow, 3))
+                    ((Low, 4))
+                    ((MediumLow, 5))
+                    ((Medium, 6))
+                    ((MediumHigh, 7))
+                    ((High, 8))
+                    ((VeryHigh, 9))
+                    ((HorizontalLow, 10))
+                    ((HorizontalMedium, 11))
+                    ((HorizontalHigh, 12))
+                    ((Broken, 13))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bContrast;
+
+            struct SerifVariant {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Cove, 2))
+                    ((ObtuseCove, 3))
+                    ((SquareCove, 4))
+                    ((ObtuseSquareCove, 5))
+                    ((Square, 6))
+                    ((Thin, 7))
+                    ((Oval, 8))
+                    ((Exaggerated, 9))
+                    ((Triangle, 10))
+                    ((NormalSans, 11))
+                    ((ObtuseSans, 12))
+                    ((PerpendicularSans, 13))
+                    ((Flared, 14))
+                    ((Rounded, 15))
+                    ((Script, 16))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bSerifVariant;
+
+            struct Treatment {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((None_StandardSolidFill, 2))
+                    ((White_NoFill, 3))
+                    ((PatternedFill, 4))
+                    ((ComplexFill, 5))
+                    ((ShapedFill, 6))
+                    ((DrawnDistressed, 7))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bTreatment;
+
+            struct Lining {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((None, 2))
+                    ((Inline, 3))
+                    ((Outline, 4))
+                    ((Engraved, 5))
+                    ((Shadow, 6))
+                    ((Relief, 7))
+                    ((Backdrop, 8))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bLining;
+
+            struct Topology {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Standard, 2))
+                    ((Square, 3))
+                    ((MultipleSegment, 4))
+                    ((DecoWacoMidlines, 5))
+                    ((UnevenWeighting, 6))
+                    ((DiverseArms, 7))
+                    ((DiverseForms, 8))
+                    ((LombardicForms, 9))
+                    ((UpperCaseInLowerCase, 10))
+                    ((ImpliedTopology, 11))
+                    ((HorseshoeEandA, 12))
+                    ((Cursive, 13))
+                    ((Blackletter, 14))
+                    ((SwashVariance, 15))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bTopology;
+
+            struct RangeOfCharacters {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((ExtendedCollection, 2))
+                    ((Litterals, 3))
+                    ((NoLowerCase, 4))
+                    ((SmallCaps, 5))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bRangeOfCharacters;
+        } decorative;
+
+        struct Pictoral {
+            struct Kind {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((Montages, 2))
+                    ((Pictures, 3))
+                    ((Shapes, 4))
+                    ((Scientific, 5))
+                    ((Music, 6))
+                    ((Expert, 7))
+                    ((Patterns, 8))
+                    ((Boarders, 9))
+                    ((Icons, 10))
+                    ((Logos, 11))
+                    ((IndustrySpecific, 12))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bKind;
+
+            struct Weight {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((NoFit, 1))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bWeight;
+
+            struct Spacing {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((ProportionalSpaced, 2))
+                    ((Monospaced, 3))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bSpacing;
+
+            struct AspectRatioAndContrast {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((NoFit, 1))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bAspectRatioAndContrast;
+
+            struct AspectRatio94 {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((NoWidth, 2))
+                    ((ExceptionallyWide, 3))
+                    ((SuperWide, 4))
+                    ((VeryWide, 5))
+                    ((Wide, 6))
+                    ((Normal, 7))
+                    ((Narrow, 8))
+                    ((VeryNarrow, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bAspectRatio94;
+
+            struct AspectRatio119 {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((NoWidth, 2))
+                    ((ExceptionallyWide, 3))
+                    ((SuperWide, 4))
+                    ((VeryWide, 5))
+                    ((Wide, 6))
+                    ((Normal, 7))
+                    ((Narrow, 8))
+                    ((VeryNarrow, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bAspectRatio119;
+
+             struct AspectRatio157 {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((NoWidth, 2))
+                    ((ExceptionallyWide, 3))
+                    ((SuperWide, 4))
+                    ((VeryWide, 5))
+                    ((Wide, 6))
+                    ((Normal, 7))
+                    ((Narrow, 8))
+                    ((VeryNarrow, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bAspectRatio157;
+
+            struct AspectRatio163 {
+                SK_TYPED_ENUM(Value, SK_OT_BYTE,
+                    ((Any, 0))
+                    ((NoFit, 1))
+                    ((NoWidth, 2))
+                    ((ExceptionallyWide, 3))
+                    ((SuperWide, 4))
+                    ((VeryWide, 5))
+                    ((Wide, 6))
+                    ((Normal, 7))
+                    ((Narrow, 8))
+                    ((VeryNarrow, 9))
+                    SK_SEQ_END,
+                (value)SK_SEQ_END)
+            } bAspectRatio163;
+        } pictoral;
+    } data;
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkPanose) == 10, sizeof_SkPanose_not_10);
+
+#endif
diff --git a/src/sfnt/SkPreprocessorSeq.h b/src/sfnt/SkPreprocessorSeq.h
new file mode 100644
index 0000000..fed2418
--- /dev/null
+++ b/src/sfnt/SkPreprocessorSeq.h
@@ -0,0 +1,826 @@
+/*
+ * 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 SkPreprocessorSeq_DEFINED
+#define SkPreprocessorSeq_DEFINED
+
+#define SK_CONCAT(a, b) SK_CONCAT_I(a, b) // allow for macro expansion
+#define SK_CONCAT_I(a, b) a ## b
+
+//A preprocessor pair is of the form '(a, b)'
+#define SK_PAIR_FIRST(pair) SK_PAIR_FIRST_I pair
+#define SK_PAIR_FIRST_I(a, b) a
+#define SK_PAIR_SECOND(pair) SK_PAIR_SECOND_I pair
+#define SK_PAIR_SECOND_I(a, b) b
+
+//A preprocessor sequence is of the form (a)(b)(c)SK_SEQ_END
+#if 0
+//The following is what we logically want to do.
+//However, MSVC expands macros at the wrong time, causing an error on use of
+//SK_SEQ_IGNORE_SECOND_I because it formally takes two parameters, but we only
+//pass "one" parameter in SK_SEQ_IGNORE_SECOND.
+
+#define SK_SEQ_HEAD(seq) SK_SEQ_IGNORE_SECOND(SK_SEQ_FIRST seq)
+#define SK_SEQ_IGNORE_SECOND(x) SK_SEQ_IGNORE_SECOND_I(x) // expand x
+#define SK_SEQ_IGNORE_SECOND_I(x, _) x
+#define SK_SEQ_FIRST(x) x, SK_NIL
+
+#else
+
+//This is less obvious, but works on GCC, clang, and MSVC.
+#define SK_SEQ_HEAD(seq) SK_SEQ_HEAD_II((SK_SEQ_FIRST seq))
+#define SK_SEQ_HEAD_II(x) SK_SEQ_IGNORE_SECOND_I x
+#define SK_SEQ_IGNORE_SECOND_I(x, _) x
+#define SK_SEQ_FIRST(x) x, SK_NIL
+
+#endif
+
+#define SK_SEQ_TAIL(seq) SK_SEQ_TAIL_I seq
+#define SK_SEQ_TAIL_I(x)
+
+#define SK_SEQ_SIZE(seq) SK_CONCAT(SK_SEQ_SIZE_, SK_SEQ_SIZE_TERMINATOR seq)
+
+#define SK_SEQ_SIZE_TERMINATOR(_) SK_SEQ_SIZE_0
+#define SK_SEQ_SIZE_0(_) SK_SEQ_SIZE_1
+#define SK_SEQ_SIZE_1(_) SK_SEQ_SIZE_2
+#define SK_SEQ_SIZE_2(_) SK_SEQ_SIZE_3
+#define SK_SEQ_SIZE_3(_) SK_SEQ_SIZE_4
+#define SK_SEQ_SIZE_4(_) SK_SEQ_SIZE_5
+#define SK_SEQ_SIZE_5(_) SK_SEQ_SIZE_6
+#define SK_SEQ_SIZE_6(_) SK_SEQ_SIZE_7
+#define SK_SEQ_SIZE_7(_) SK_SEQ_SIZE_8
+#define SK_SEQ_SIZE_8(_) SK_SEQ_SIZE_9
+#define SK_SEQ_SIZE_9(_) SK_SEQ_SIZE_10
+#define SK_SEQ_SIZE_10(_) SK_SEQ_SIZE_11
+#define SK_SEQ_SIZE_11(_) SK_SEQ_SIZE_12
+#define SK_SEQ_SIZE_12(_) SK_SEQ_SIZE_13
+#define SK_SEQ_SIZE_13(_) SK_SEQ_SIZE_14
+#define SK_SEQ_SIZE_14(_) SK_SEQ_SIZE_15
+#define SK_SEQ_SIZE_15(_) SK_SEQ_SIZE_16
+#define SK_SEQ_SIZE_16(_) SK_SEQ_SIZE_17
+#define SK_SEQ_SIZE_17(_) SK_SEQ_SIZE_18
+#define SK_SEQ_SIZE_18(_) SK_SEQ_SIZE_19
+#define SK_SEQ_SIZE_19(_) SK_SEQ_SIZE_20
+#define SK_SEQ_SIZE_20(_) SK_SEQ_SIZE_21
+#define SK_SEQ_SIZE_21(_) SK_SEQ_SIZE_22
+#define SK_SEQ_SIZE_22(_) SK_SEQ_SIZE_23
+#define SK_SEQ_SIZE_23(_) SK_SEQ_SIZE_24
+#define SK_SEQ_SIZE_24(_) SK_SEQ_SIZE_25
+#define SK_SEQ_SIZE_25(_) SK_SEQ_SIZE_26
+#define SK_SEQ_SIZE_26(_) SK_SEQ_SIZE_27
+#define SK_SEQ_SIZE_27(_) SK_SEQ_SIZE_28
+#define SK_SEQ_SIZE_28(_) SK_SEQ_SIZE_29
+#define SK_SEQ_SIZE_29(_) SK_SEQ_SIZE_30
+#define SK_SEQ_SIZE_30(_) SK_SEQ_SIZE_31
+#define SK_SEQ_SIZE_31(_) SK_SEQ_SIZE_32
+#define SK_SEQ_SIZE_32(_) SK_SEQ_SIZE_33
+#define SK_SEQ_SIZE_33(_) SK_SEQ_SIZE_34
+#define SK_SEQ_SIZE_34(_) SK_SEQ_SIZE_35
+#define SK_SEQ_SIZE_35(_) SK_SEQ_SIZE_36
+#define SK_SEQ_SIZE_36(_) SK_SEQ_SIZE_37
+#define SK_SEQ_SIZE_37(_) SK_SEQ_SIZE_38
+#define SK_SEQ_SIZE_38(_) SK_SEQ_SIZE_39
+#define SK_SEQ_SIZE_39(_) SK_SEQ_SIZE_40
+#define SK_SEQ_SIZE_40(_) SK_SEQ_SIZE_41
+#define SK_SEQ_SIZE_41(_) SK_SEQ_SIZE_42
+#define SK_SEQ_SIZE_42(_) SK_SEQ_SIZE_43
+#define SK_SEQ_SIZE_43(_) SK_SEQ_SIZE_44
+#define SK_SEQ_SIZE_44(_) SK_SEQ_SIZE_45
+#define SK_SEQ_SIZE_45(_) SK_SEQ_SIZE_46
+#define SK_SEQ_SIZE_46(_) SK_SEQ_SIZE_47
+#define SK_SEQ_SIZE_47(_) SK_SEQ_SIZE_48
+#define SK_SEQ_SIZE_48(_) SK_SEQ_SIZE_49
+#define SK_SEQ_SIZE_49(_) SK_SEQ_SIZE_50
+#define SK_SEQ_SIZE_50(_) SK_SEQ_SIZE_51
+#define SK_SEQ_SIZE_51(_) SK_SEQ_SIZE_52
+#define SK_SEQ_SIZE_52(_) SK_SEQ_SIZE_53
+#define SK_SEQ_SIZE_53(_) SK_SEQ_SIZE_54
+#define SK_SEQ_SIZE_54(_) SK_SEQ_SIZE_55
+#define SK_SEQ_SIZE_55(_) SK_SEQ_SIZE_56
+#define SK_SEQ_SIZE_56(_) SK_SEQ_SIZE_57
+#define SK_SEQ_SIZE_57(_) SK_SEQ_SIZE_58
+#define SK_SEQ_SIZE_58(_) SK_SEQ_SIZE_59
+#define SK_SEQ_SIZE_59(_) SK_SEQ_SIZE_60
+#define SK_SEQ_SIZE_60(_) SK_SEQ_SIZE_61
+#define SK_SEQ_SIZE_61(_) SK_SEQ_SIZE_62
+#define SK_SEQ_SIZE_62(_) SK_SEQ_SIZE_63
+#define SK_SEQ_SIZE_63(_) SK_SEQ_SIZE_64
+#define SK_SEQ_SIZE_64(_) SK_SEQ_SIZE_65
+#define SK_SEQ_SIZE_65(_) SK_SEQ_SIZE_66
+#define SK_SEQ_SIZE_66(_) SK_SEQ_SIZE_67
+#define SK_SEQ_SIZE_67(_) SK_SEQ_SIZE_68
+#define SK_SEQ_SIZE_68(_) SK_SEQ_SIZE_69
+#define SK_SEQ_SIZE_69(_) SK_SEQ_SIZE_70
+#define SK_SEQ_SIZE_70(_) SK_SEQ_SIZE_71
+#define SK_SEQ_SIZE_71(_) SK_SEQ_SIZE_72
+#define SK_SEQ_SIZE_72(_) SK_SEQ_SIZE_73
+#define SK_SEQ_SIZE_73(_) SK_SEQ_SIZE_74
+#define SK_SEQ_SIZE_74(_) SK_SEQ_SIZE_75
+#define SK_SEQ_SIZE_75(_) SK_SEQ_SIZE_76
+#define SK_SEQ_SIZE_76(_) SK_SEQ_SIZE_77
+#define SK_SEQ_SIZE_77(_) SK_SEQ_SIZE_78
+#define SK_SEQ_SIZE_78(_) SK_SEQ_SIZE_79
+#define SK_SEQ_SIZE_79(_) SK_SEQ_SIZE_80
+#define SK_SEQ_SIZE_80(_) SK_SEQ_SIZE_81
+#define SK_SEQ_SIZE_81(_) SK_SEQ_SIZE_82
+#define SK_SEQ_SIZE_82(_) SK_SEQ_SIZE_83
+#define SK_SEQ_SIZE_83(_) SK_SEQ_SIZE_84
+#define SK_SEQ_SIZE_84(_) SK_SEQ_SIZE_85
+#define SK_SEQ_SIZE_85(_) SK_SEQ_SIZE_86
+#define SK_SEQ_SIZE_86(_) SK_SEQ_SIZE_87
+#define SK_SEQ_SIZE_87(_) SK_SEQ_SIZE_88
+#define SK_SEQ_SIZE_88(_) SK_SEQ_SIZE_89
+#define SK_SEQ_SIZE_89(_) SK_SEQ_SIZE_90
+#define SK_SEQ_SIZE_90(_) SK_SEQ_SIZE_91
+#define SK_SEQ_SIZE_91(_) SK_SEQ_SIZE_92
+#define SK_SEQ_SIZE_92(_) SK_SEQ_SIZE_93
+#define SK_SEQ_SIZE_93(_) SK_SEQ_SIZE_94
+#define SK_SEQ_SIZE_94(_) SK_SEQ_SIZE_95
+#define SK_SEQ_SIZE_95(_) SK_SEQ_SIZE_96
+#define SK_SEQ_SIZE_96(_) SK_SEQ_SIZE_97
+#define SK_SEQ_SIZE_97(_) SK_SEQ_SIZE_98
+#define SK_SEQ_SIZE_98(_) SK_SEQ_SIZE_99
+#define SK_SEQ_SIZE_99(_) SK_SEQ_SIZE_100
+#define SK_SEQ_SIZE_100(_) SK_SEQ_SIZE_101
+#define SK_SEQ_SIZE_101(_) SK_SEQ_SIZE_102
+#define SK_SEQ_SIZE_102(_) SK_SEQ_SIZE_103
+#define SK_SEQ_SIZE_103(_) SK_SEQ_SIZE_104
+#define SK_SEQ_SIZE_104(_) SK_SEQ_SIZE_105
+#define SK_SEQ_SIZE_105(_) SK_SEQ_SIZE_106
+#define SK_SEQ_SIZE_106(_) SK_SEQ_SIZE_107
+#define SK_SEQ_SIZE_107(_) SK_SEQ_SIZE_108
+#define SK_SEQ_SIZE_108(_) SK_SEQ_SIZE_109
+#define SK_SEQ_SIZE_109(_) SK_SEQ_SIZE_110
+#define SK_SEQ_SIZE_110(_) SK_SEQ_SIZE_111
+#define SK_SEQ_SIZE_111(_) SK_SEQ_SIZE_112
+#define SK_SEQ_SIZE_112(_) SK_SEQ_SIZE_113
+#define SK_SEQ_SIZE_113(_) SK_SEQ_SIZE_114
+#define SK_SEQ_SIZE_114(_) SK_SEQ_SIZE_115
+#define SK_SEQ_SIZE_115(_) SK_SEQ_SIZE_116
+#define SK_SEQ_SIZE_116(_) SK_SEQ_SIZE_117
+#define SK_SEQ_SIZE_117(_) SK_SEQ_SIZE_118
+#define SK_SEQ_SIZE_118(_) SK_SEQ_SIZE_119
+#define SK_SEQ_SIZE_119(_) SK_SEQ_SIZE_120
+#define SK_SEQ_SIZE_120(_) SK_SEQ_SIZE_121
+#define SK_SEQ_SIZE_121(_) SK_SEQ_SIZE_122
+#define SK_SEQ_SIZE_122(_) SK_SEQ_SIZE_123
+#define SK_SEQ_SIZE_123(_) SK_SEQ_SIZE_124
+#define SK_SEQ_SIZE_124(_) SK_SEQ_SIZE_125
+#define SK_SEQ_SIZE_125(_) SK_SEQ_SIZE_126
+#define SK_SEQ_SIZE_126(_) SK_SEQ_SIZE_127
+#define SK_SEQ_SIZE_127(_) SK_SEQ_SIZE_128
+#define SK_SEQ_SIZE_128(_) SK_SEQ_SIZE_129
+#define SK_SEQ_SIZE_129(_) SK_SEQ_SIZE_130
+#define SK_SEQ_SIZE_130(_) SK_SEQ_SIZE_131
+#define SK_SEQ_SIZE_131(_) SK_SEQ_SIZE_132
+#define SK_SEQ_SIZE_132(_) SK_SEQ_SIZE_133
+#define SK_SEQ_SIZE_133(_) SK_SEQ_SIZE_134
+#define SK_SEQ_SIZE_134(_) SK_SEQ_SIZE_135
+#define SK_SEQ_SIZE_135(_) SK_SEQ_SIZE_136
+#define SK_SEQ_SIZE_136(_) SK_SEQ_SIZE_137
+#define SK_SEQ_SIZE_137(_) SK_SEQ_SIZE_138
+#define SK_SEQ_SIZE_138(_) SK_SEQ_SIZE_139
+#define SK_SEQ_SIZE_139(_) SK_SEQ_SIZE_140
+#define SK_SEQ_SIZE_140(_) SK_SEQ_SIZE_141
+#define SK_SEQ_SIZE_141(_) SK_SEQ_SIZE_142
+#define SK_SEQ_SIZE_142(_) SK_SEQ_SIZE_143
+#define SK_SEQ_SIZE_143(_) SK_SEQ_SIZE_144
+#define SK_SEQ_SIZE_144(_) SK_SEQ_SIZE_145
+#define SK_SEQ_SIZE_145(_) SK_SEQ_SIZE_146
+#define SK_SEQ_SIZE_146(_) SK_SEQ_SIZE_147
+#define SK_SEQ_SIZE_147(_) SK_SEQ_SIZE_148
+#define SK_SEQ_SIZE_148(_) SK_SEQ_SIZE_149
+#define SK_SEQ_SIZE_149(_) SK_SEQ_SIZE_150
+#define SK_SEQ_SIZE_150(_) SK_SEQ_SIZE_151
+#define SK_SEQ_SIZE_151(_) SK_SEQ_SIZE_152
+#define SK_SEQ_SIZE_152(_) SK_SEQ_SIZE_153
+#define SK_SEQ_SIZE_153(_) SK_SEQ_SIZE_154
+#define SK_SEQ_SIZE_154(_) SK_SEQ_SIZE_155
+#define SK_SEQ_SIZE_155(_) SK_SEQ_SIZE_156
+#define SK_SEQ_SIZE_156(_) SK_SEQ_SIZE_157
+#define SK_SEQ_SIZE_157(_) SK_SEQ_SIZE_158
+#define SK_SEQ_SIZE_158(_) SK_SEQ_SIZE_159
+#define SK_SEQ_SIZE_159(_) SK_SEQ_SIZE_160
+#define SK_SEQ_SIZE_160(_) SK_SEQ_SIZE_161
+#define SK_SEQ_SIZE_161(_) SK_SEQ_SIZE_162
+#define SK_SEQ_SIZE_162(_) SK_SEQ_SIZE_163
+#define SK_SEQ_SIZE_163(_) SK_SEQ_SIZE_164
+#define SK_SEQ_SIZE_164(_) SK_SEQ_SIZE_165
+#define SK_SEQ_SIZE_165(_) SK_SEQ_SIZE_166
+#define SK_SEQ_SIZE_166(_) SK_SEQ_SIZE_167
+#define SK_SEQ_SIZE_167(_) SK_SEQ_SIZE_168
+#define SK_SEQ_SIZE_168(_) SK_SEQ_SIZE_169
+#define SK_SEQ_SIZE_169(_) SK_SEQ_SIZE_170
+#define SK_SEQ_SIZE_170(_) SK_SEQ_SIZE_171
+#define SK_SEQ_SIZE_171(_) SK_SEQ_SIZE_172
+#define SK_SEQ_SIZE_172(_) SK_SEQ_SIZE_173
+#define SK_SEQ_SIZE_173(_) SK_SEQ_SIZE_174
+#define SK_SEQ_SIZE_174(_) SK_SEQ_SIZE_175
+#define SK_SEQ_SIZE_175(_) SK_SEQ_SIZE_176
+#define SK_SEQ_SIZE_176(_) SK_SEQ_SIZE_177
+#define SK_SEQ_SIZE_177(_) SK_SEQ_SIZE_178
+#define SK_SEQ_SIZE_178(_) SK_SEQ_SIZE_179
+#define SK_SEQ_SIZE_179(_) SK_SEQ_SIZE_180
+#define SK_SEQ_SIZE_180(_) SK_SEQ_SIZE_181
+#define SK_SEQ_SIZE_181(_) SK_SEQ_SIZE_182
+#define SK_SEQ_SIZE_182(_) SK_SEQ_SIZE_183
+#define SK_SEQ_SIZE_183(_) SK_SEQ_SIZE_184
+#define SK_SEQ_SIZE_184(_) SK_SEQ_SIZE_185
+#define SK_SEQ_SIZE_185(_) SK_SEQ_SIZE_186
+#define SK_SEQ_SIZE_186(_) SK_SEQ_SIZE_187
+#define SK_SEQ_SIZE_187(_) SK_SEQ_SIZE_188
+#define SK_SEQ_SIZE_188(_) SK_SEQ_SIZE_189
+#define SK_SEQ_SIZE_189(_) SK_SEQ_SIZE_190
+#define SK_SEQ_SIZE_190(_) SK_SEQ_SIZE_191
+#define SK_SEQ_SIZE_191(_) SK_SEQ_SIZE_192
+#define SK_SEQ_SIZE_192(_) SK_SEQ_SIZE_193
+#define SK_SEQ_SIZE_193(_) SK_SEQ_SIZE_194
+#define SK_SEQ_SIZE_194(_) SK_SEQ_SIZE_195
+#define SK_SEQ_SIZE_195(_) SK_SEQ_SIZE_196
+#define SK_SEQ_SIZE_196(_) SK_SEQ_SIZE_197
+#define SK_SEQ_SIZE_197(_) SK_SEQ_SIZE_198
+#define SK_SEQ_SIZE_198(_) SK_SEQ_SIZE_199
+#define SK_SEQ_SIZE_199(_) SK_SEQ_SIZE_200
+#define SK_SEQ_SIZE_200(_) SK_SEQ_SIZE_201
+#define SK_SEQ_SIZE_201(_) SK_SEQ_SIZE_202
+#define SK_SEQ_SIZE_202(_) SK_SEQ_SIZE_203
+#define SK_SEQ_SIZE_203(_) SK_SEQ_SIZE_204
+#define SK_SEQ_SIZE_204(_) SK_SEQ_SIZE_205
+#define SK_SEQ_SIZE_205(_) SK_SEQ_SIZE_206
+#define SK_SEQ_SIZE_206(_) SK_SEQ_SIZE_207
+#define SK_SEQ_SIZE_207(_) SK_SEQ_SIZE_208
+#define SK_SEQ_SIZE_208(_) SK_SEQ_SIZE_209
+#define SK_SEQ_SIZE_209(_) SK_SEQ_SIZE_210
+#define SK_SEQ_SIZE_210(_) SK_SEQ_SIZE_211
+#define SK_SEQ_SIZE_211(_) SK_SEQ_SIZE_212
+#define SK_SEQ_SIZE_212(_) SK_SEQ_SIZE_213
+#define SK_SEQ_SIZE_213(_) SK_SEQ_SIZE_214
+#define SK_SEQ_SIZE_214(_) SK_SEQ_SIZE_215
+#define SK_SEQ_SIZE_215(_) SK_SEQ_SIZE_216
+#define SK_SEQ_SIZE_216(_) SK_SEQ_SIZE_217
+#define SK_SEQ_SIZE_217(_) SK_SEQ_SIZE_218
+#define SK_SEQ_SIZE_218(_) SK_SEQ_SIZE_219
+#define SK_SEQ_SIZE_219(_) SK_SEQ_SIZE_220
+#define SK_SEQ_SIZE_220(_) SK_SEQ_SIZE_221
+#define SK_SEQ_SIZE_221(_) SK_SEQ_SIZE_222
+#define SK_SEQ_SIZE_222(_) SK_SEQ_SIZE_223
+#define SK_SEQ_SIZE_223(_) SK_SEQ_SIZE_224
+#define SK_SEQ_SIZE_224(_) SK_SEQ_SIZE_225
+#define SK_SEQ_SIZE_225(_) SK_SEQ_SIZE_226
+#define SK_SEQ_SIZE_226(_) SK_SEQ_SIZE_227
+#define SK_SEQ_SIZE_227(_) SK_SEQ_SIZE_228
+#define SK_SEQ_SIZE_228(_) SK_SEQ_SIZE_229
+#define SK_SEQ_SIZE_229(_) SK_SEQ_SIZE_230
+#define SK_SEQ_SIZE_230(_) SK_SEQ_SIZE_231
+#define SK_SEQ_SIZE_231(_) SK_SEQ_SIZE_232
+#define SK_SEQ_SIZE_232(_) SK_SEQ_SIZE_233
+#define SK_SEQ_SIZE_233(_) SK_SEQ_SIZE_234
+#define SK_SEQ_SIZE_234(_) SK_SEQ_SIZE_235
+#define SK_SEQ_SIZE_235(_) SK_SEQ_SIZE_236
+#define SK_SEQ_SIZE_236(_) SK_SEQ_SIZE_237
+#define SK_SEQ_SIZE_237(_) SK_SEQ_SIZE_238
+#define SK_SEQ_SIZE_238(_) SK_SEQ_SIZE_239
+#define SK_SEQ_SIZE_239(_) SK_SEQ_SIZE_240
+#define SK_SEQ_SIZE_240(_) SK_SEQ_SIZE_241
+#define SK_SEQ_SIZE_241(_) SK_SEQ_SIZE_242
+#define SK_SEQ_SIZE_242(_) SK_SEQ_SIZE_243
+#define SK_SEQ_SIZE_243(_) SK_SEQ_SIZE_244
+#define SK_SEQ_SIZE_244(_) SK_SEQ_SIZE_245
+#define SK_SEQ_SIZE_245(_) SK_SEQ_SIZE_246
+#define SK_SEQ_SIZE_246(_) SK_SEQ_SIZE_247
+#define SK_SEQ_SIZE_247(_) SK_SEQ_SIZE_248
+#define SK_SEQ_SIZE_248(_) SK_SEQ_SIZE_249
+#define SK_SEQ_SIZE_249(_) SK_SEQ_SIZE_250
+#define SK_SEQ_SIZE_250(_) SK_SEQ_SIZE_251
+#define SK_SEQ_SIZE_251(_) SK_SEQ_SIZE_252
+#define SK_SEQ_SIZE_252(_) SK_SEQ_SIZE_253
+#define SK_SEQ_SIZE_253(_) SK_SEQ_SIZE_254
+#define SK_SEQ_SIZE_254(_) SK_SEQ_SIZE_255
+#define SK_SEQ_SIZE_255(_) SK_SEQ_SIZE_256
+
+
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_0 0
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_1 1
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_2 2
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_3 3
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_4 4
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_5 5
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_6 6
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_7 7
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_8 8
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_9 9
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_10 10
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_11 11
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_12 12
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_13 13
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_14 14
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_15 15
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_16 16
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_17 17
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_18 18
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_19 19
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_20 20
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_21 21
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_22 22
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_23 23
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_24 24
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_25 25
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_26 26
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_27 27
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_28 28
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_29 29
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_30 30
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_31 31
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_32 32
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_33 33
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_34 34
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_35 35
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_36 36
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_37 37
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_38 38
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_39 39
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_40 40
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_41 41
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_42 42
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_43 43
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_44 44
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_45 45
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_46 46
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_47 47
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_48 48
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_49 49
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_50 50
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_51 51
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_52 52
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_53 53
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_54 54
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_55 55
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_56 56
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_57 57
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_58 58
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_59 59
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_60 60
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_61 61
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_62 62
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_63 63
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_64 64
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_65 65
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_66 66
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_67 67
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_68 68
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_69 69
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_70 70
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_71 71
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_72 72
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_73 73
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_74 74
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_75 75
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_76 76
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_77 77
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_78 78
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_79 79
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_80 80
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_81 81
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_82 82
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_83 83
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_84 84
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_85 85
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_86 86
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_87 87
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_88 88
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_89 89
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_90 90
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_91 91
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_92 92
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_93 93
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_94 94
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_95 95
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_96 96
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_97 97
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_98 98
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_99 99
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_100 100
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_101 101
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_102 102
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_103 103
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_104 104
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_105 105
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_106 106
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_107 107
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_108 108
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_109 109
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_110 110
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_111 111
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_112 112
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_113 113
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_114 114
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_115 115
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_116 116
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_117 117
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_118 118
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_119 119
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_120 120
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_121 121
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_122 122
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_123 123
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_124 124
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_125 125
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_126 126
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_127 127
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_128 128
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_129 129
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_130 130
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_131 131
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_132 132
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_133 133
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_134 134
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_135 135
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_136 136
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_137 137
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_138 138
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_139 139
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_140 140
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_141 141
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_142 142
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_143 143
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_144 144
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_145 145
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_146 146
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_147 147
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_148 148
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_149 149
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_150 150
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_151 151
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_152 152
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_153 153
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_154 154
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_155 155
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_156 156
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_157 157
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_158 158
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_159 159
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_160 160
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_161 161
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_162 162
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_163 163
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_164 164
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_165 165
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_166 166
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_167 167
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_168 168
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_169 169
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_170 170
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_171 171
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_172 172
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_173 173
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_174 174
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_175 175
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_176 176
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_177 177
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_178 178
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_179 179
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_180 180
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_181 181
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_182 182
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_183 183
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_184 184
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_185 185
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_186 186
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_187 187
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_188 188
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_189 189
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_190 190
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_191 191
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_192 192
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_193 193
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_194 194
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_195 195
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_196 196
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_197 197
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_198 198
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_199 199
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_200 200
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_201 201
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_202 202
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_203 203
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_204 204
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_205 205
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_206 206
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_207 207
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_208 208
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_209 209
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_210 210
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_211 211
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_212 212
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_213 213
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_214 214
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_215 215
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_216 216
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_217 217
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_218 218
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_219 219
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_220 220
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_221 221
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_222 222
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_223 223
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_224 224
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_225 225
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_226 226
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_227 227
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_228 228
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_229 229
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_230 230
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_231 231
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_232 232
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_233 233
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_234 234
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_235 235
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_236 236
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_237 237
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_238 238
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_239 239
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_240 240
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_241 241
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_242 242
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_243 243
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_244 244
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_245 245
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_246 246
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_247 247
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_248 248
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_249 249
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_250 250
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_251 251
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_252 252
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_253 253
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_254 254
+#define SK_SEQ_SIZE_SK_SEQ_SIZE_255 255
+
+
+#define SK_SEQ_FOREACH(op, data, seq) SK_SEQ_FOREACH_L(op, op, data, seq)
+#define SK_SEQ_FOREACH_L(op, lop, data, seq) SK_CONCAT(SK_SEQ_FOREACH_, SK_SEQ_SIZE(seq)) (op, lop, data, SK_SEQ_HEAD(seq), SK_SEQ_TAIL(seq))
+
+#define SK_SEQ_FOREACH_0(op,lop,d,x,t)
+#define SK_SEQ_FOREACH_1(op,lop,d,x,t) lop(d,x)
+#define SK_SEQ_FOREACH_2(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_1(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_3(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_2(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_4(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_3(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_5(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_4(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_6(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_5(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_7(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_6(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_8(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_7(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_9(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_8(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_10(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_9(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_11(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_10(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_12(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_11(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_13(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_12(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_14(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_13(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_15(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_14(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_16(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_15(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_17(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_16(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_18(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_17(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_19(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_18(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_20(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_19(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_21(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_20(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_22(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_21(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_23(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_22(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_24(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_23(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_25(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_24(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_26(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_25(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_27(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_26(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_28(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_27(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_29(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_28(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_30(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_29(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_31(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_30(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_32(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_31(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_33(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_32(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_34(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_33(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_35(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_34(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_36(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_35(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_37(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_36(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_38(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_37(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_39(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_38(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_40(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_39(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_41(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_40(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_42(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_41(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_43(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_42(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_44(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_43(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_45(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_44(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_46(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_45(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_47(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_46(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_48(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_47(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_49(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_48(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_50(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_49(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_51(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_50(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_52(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_51(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_53(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_52(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_54(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_53(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_55(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_54(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_56(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_55(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_57(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_56(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_58(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_57(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_59(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_58(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_60(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_59(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_61(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_60(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_62(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_61(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_63(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_62(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_64(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_63(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_65(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_64(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_66(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_65(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_67(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_66(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_68(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_67(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_69(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_68(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_70(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_69(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_71(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_70(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_72(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_71(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_73(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_72(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_74(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_73(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_75(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_74(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_76(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_75(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_77(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_76(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_78(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_77(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_79(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_78(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_80(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_79(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_81(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_80(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_82(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_81(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_83(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_82(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_84(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_83(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_85(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_84(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_86(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_85(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_87(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_86(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_88(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_87(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_89(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_88(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_90(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_89(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_91(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_90(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_92(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_91(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_93(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_92(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_94(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_93(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_95(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_94(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_96(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_95(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_97(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_96(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_98(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_97(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_99(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_98(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_100(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_99(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_101(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_100(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_102(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_101(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_103(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_102(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_104(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_103(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_105(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_104(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_106(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_105(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_107(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_106(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_108(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_107(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_109(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_108(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_110(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_109(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_111(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_110(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_112(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_111(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_113(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_112(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_114(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_113(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_115(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_114(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_116(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_115(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_117(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_116(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_118(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_117(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_119(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_118(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_120(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_119(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_121(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_120(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_122(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_121(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_123(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_122(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_124(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_123(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_125(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_124(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_126(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_125(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_127(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_126(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_128(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_127(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_129(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_128(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_130(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_129(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_131(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_130(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_132(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_131(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_133(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_132(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_134(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_133(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_135(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_134(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_136(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_135(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_137(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_136(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_138(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_137(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_139(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_138(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_140(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_139(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_141(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_140(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_142(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_141(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_143(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_142(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_144(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_143(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_145(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_144(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_146(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_145(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_147(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_146(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_148(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_147(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_149(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_148(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_150(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_149(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_151(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_150(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_152(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_151(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_153(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_152(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_154(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_153(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_155(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_154(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_156(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_155(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_157(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_156(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_158(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_157(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_159(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_158(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_160(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_159(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_161(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_160(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_162(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_161(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_163(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_162(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_164(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_163(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_165(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_164(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_166(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_165(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_167(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_166(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_168(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_167(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_169(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_168(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_170(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_169(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_171(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_170(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_172(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_171(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_173(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_172(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_174(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_173(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_175(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_174(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_176(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_175(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_177(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_176(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_178(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_177(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_179(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_178(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_180(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_179(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_181(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_180(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_182(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_181(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_183(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_182(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_184(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_183(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_185(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_184(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_186(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_185(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_187(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_186(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_188(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_187(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_189(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_188(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_190(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_189(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_191(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_190(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_192(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_191(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_193(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_192(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_194(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_193(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_195(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_194(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_196(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_195(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_197(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_196(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_198(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_197(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_199(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_198(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_200(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_199(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_201(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_200(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_202(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_201(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_203(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_202(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_204(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_203(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_205(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_204(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_206(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_205(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_207(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_206(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_208(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_207(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_209(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_208(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_210(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_209(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_211(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_210(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_212(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_211(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_213(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_212(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_214(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_213(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_215(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_214(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_216(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_215(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_217(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_216(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_218(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_217(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_219(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_218(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_220(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_219(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_221(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_220(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_222(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_221(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_223(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_222(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_224(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_223(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_225(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_224(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_226(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_225(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_227(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_226(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_228(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_227(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_229(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_228(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_230(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_229(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_231(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_230(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_232(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_231(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_233(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_232(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_234(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_233(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_235(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_234(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_236(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_235(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_237(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_236(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_238(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_237(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_239(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_238(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_240(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_239(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_241(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_240(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_242(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_241(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_243(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_242(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_244(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_243(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_245(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_244(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_246(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_245(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_247(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_246(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_248(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_247(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_249(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_248(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_250(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_249(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_251(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_250(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_252(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_251(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_253(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_252(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_254(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_253(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+#define SK_SEQ_FOREACH_255(op,lop,d,x,t) op(d,x) SK_SEQ_FOREACH_254(op, lop, d, SK_SEQ_HEAD(t), SK_SEQ_TAIL(t))
+
+#define SK_SEQ_END (SK_NIL)
+
+#endif
diff --git a/src/sfnt/SkSFNTHeader.h b/src/sfnt/SkSFNTHeader.h
new file mode 100644
index 0000000..9071696
--- /dev/null
+++ b/src/sfnt/SkSFNTHeader.h
@@ -0,0 +1,70 @@
+/*
+ * 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 SkSFNTHeader_DEFINED
+#define SkSFNTHeader_DEFINED
+
+#include "SkEndian.h"
+#include "SkOTTableTypes.h"
+
+//All SK_SFNT_ prefixed types should be considered as big endian.
+typedef uint16_t SK_SFNT_USHORT;
+typedef uint32_t SK_SFNT_ULONG;
+
+#pragma pack(push, 1)
+
+struct SkSFNTHeader {
+    SK_SFNT_ULONG fontType;
+    struct fontType_WindowsTrueType {
+        static const SK_OT_CHAR TAG0 = 0;
+        static const SK_OT_CHAR TAG1 = 1;
+        static const SK_OT_CHAR TAG2 = 0;
+        static const SK_OT_CHAR TAG3 = 0;
+        static const SK_OT_ULONG TAG = SkOTTableTAG<fontType_WindowsTrueType>::value;
+    };
+    struct fontType_MacTrueType {
+        static const SK_OT_CHAR TAG0 = 't';
+        static const SK_OT_CHAR TAG1 = 'r';
+        static const SK_OT_CHAR TAG2 = 'u';
+        static const SK_OT_CHAR TAG3 = 'e';
+        static const SK_OT_ULONG TAG = SkOTTableTAG<fontType_MacTrueType>::value;
+    };
+    struct fontType_PostScript {
+        static const SK_OT_CHAR TAG0 = 't';
+        static const SK_OT_CHAR TAG1 = 'y';
+        static const SK_OT_CHAR TAG2 = 'p';
+        static const SK_OT_CHAR TAG3 = '1';
+        static const SK_OT_ULONG TAG = SkOTTableTAG<fontType_PostScript>::value;
+    };
+    struct fontType_OpenTypeCFF {
+        static const SK_OT_CHAR TAG0 = 'O';
+        static const SK_OT_CHAR TAG1 = 'T';
+        static const SK_OT_CHAR TAG2 = 'T';
+        static const SK_OT_CHAR TAG3 = 'O';
+        static const SK_OT_ULONG TAG = SkOTTableTAG<fontType_OpenTypeCFF>::value;
+    };
+
+    SK_SFNT_USHORT numTables;
+    SK_SFNT_USHORT searchRange;
+    SK_SFNT_USHORT entrySelector;
+    SK_SFNT_USHORT rangeShift;
+
+    struct TableDirectoryEntry {
+        SK_SFNT_ULONG tag;
+        SK_SFNT_ULONG checksum;
+        SK_SFNT_ULONG offset; //From beginning of header.
+        SK_SFNT_ULONG logicalLength;
+    }; //tableDirectoryEntries[numTables]
+};
+
+#pragma pack(pop)
+
+
+SK_COMPILE_ASSERT(sizeof(SkSFNTHeader) == 12, sizeof_SkSFNTHeader_not_12);
+SK_COMPILE_ASSERT(sizeof(SkSFNTHeader::TableDirectoryEntry) == 16, sizeof_SkSFNTHeader_TableDirectoryEntry_not_16);
+
+#endif
diff --git a/src/sfnt/SkTypedEnum.h b/src/sfnt/SkTypedEnum.h
new file mode 100644
index 0000000..73d7314
--- /dev/null
+++ b/src/sfnt/SkTypedEnum.h
@@ -0,0 +1,68 @@
+/*
+ * 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 SkTypedEnum_DEFINED
+#define SkTypedEnum_DEFINED
+
+#include "SkPreprocessorSeq.h"
+
+//Compatibility with non-clang compilers.
+#ifndef __has_feature
+    #define __has_feature(x) 0
+#endif
+#ifndef __has_extension
+    #define __has_extension __has_feature
+#endif
+
+//Detect if typed enums are supported.
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+    #define SK_TYPED_ENUMS
+
+#elif defined(__clang__) && __has_extension(cxx_strong_enums)
+    #define SK_TYPED_ENUMS
+
+// Scoped enums are buggy in GCC 4.4.0 through 4.5.1.
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38064
+// __cplusplus should actually be accurate now.
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=1773
+#elif defined(__GNUC__) && (((__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__ >= 40501) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || __cplusplus >= 201103L)
+    #define SK_TYPED_ENUMS
+#endif
+
+//Define what a typed enum looks like.
+#ifdef SK_TYPED_ENUMS
+
+    #define SK_TYPED_ENUM_VALUES(data, elem) \
+        SK_PAIR_FIRST(elem) = SK_PAIR_SECOND(elem),
+
+    #define SK_TYPED_ENUM_IDS(data, elem) \
+        elem,
+
+    #define SK_TYPED_ENUM_IDS_L(data, elem) \
+        elem
+
+    #define SK_TYPED_ENUM(enumName, enumType, enumSeq, idSeq) \
+        enum enumName : enumType { \
+            SK_SEQ_FOREACH(SK_TYPED_ENUM_VALUES, _, enumSeq) \
+        } SK_SEQ_FOREACH_L(SK_TYPED_ENUM_IDS, SK_TYPED_ENUM_IDS_L, _, idSeq);
+
+#else
+
+    #define SK_TYPED_ENUM_VALUES(enumType, elem) \
+        static const enumType SK_PAIR_FIRST(elem) = SK_PAIR_SECOND(elem);
+
+    #define SK_TYPED_ENUM_IDS(enumType, elem) \
+        enumType elem;
+
+    #define SK_TYPED_ENUM(enumName, enumType, enumSeq, idSeq) \
+        typedef enumType enumName; \
+        SK_SEQ_FOREACH(SK_TYPED_ENUM_VALUES, enumType, enumSeq) \
+        SK_SEQ_FOREACH(SK_TYPED_ENUM_IDS, enumType, idSeq)
+
+#endif
+
+#endif
diff --git a/src/svg/SkSVGClipPath.cpp b/src/svg/SkSVGClipPath.cpp
index 0a41764..fa3a799 100644
--- a/src/svg/SkSVGClipPath.cpp
+++ b/src/svg/SkSVGClipPath.cpp
@@ -32,7 +32,7 @@
     const char* refStr = &use->f_xlink_href.c_str()[1];
     SkASSERT(parser.getIDs().find(refStr, &ref));
     SkASSERT(ref);
-    if (ref->getType() == SkSVGType_Rect) 
+    if (ref->getType() == SkSVGType_Rect)
         parser._addAttribute("rectangle", refStr);
     else
         parser._addAttribute("path", refStr);
diff --git a/src/svg/SkSVGElements.cpp b/src/svg/SkSVGElements.cpp
index 0096fc0..a1bd100 100644
--- a/src/svg/SkSVGElements.cpp
+++ b/src/svg/SkSVGElements.cpp
@@ -13,7 +13,7 @@
 SkSVGBase::~SkSVGBase() {
 }
 
-void SkSVGBase::addAttribute(SkSVGParser& parser, int attrIndex, 
+void SkSVGBase::addAttribute(SkSVGParser& parser, int attrIndex,
         const char* attrValue, size_t attrLength) {
     SkString* first = (SkString*) ((char*) this + sizeof(SkSVGElement));
     first += attrIndex;
@@ -82,7 +82,5 @@
 //}
 
 void SkSVGElement::write(SkSVGParser& , SkString& ) {
-    SkASSERT(0); 
+    SkASSERT(0);
 }
-
-
diff --git a/src/svg/SkSVGFilter.h b/src/svg/SkSVGFilter.h
index 5891237..f615bd0 100644
--- a/src/svg/SkSVGFilter.h
+++ b/src/svg/SkSVGFilter.h
@@ -25,4 +25,3 @@
 };
 
 #endif // SkSVGFilter_DEFINEDRITED;
-
diff --git a/src/svg/SkSVGGradient.cpp b/src/svg/SkSVGGradient.cpp
index 27964e7..bbcca18 100644
--- a/src/svg/SkSVGGradient.cpp
+++ b/src/svg/SkSVGGradient.cpp
@@ -84,7 +84,7 @@
     parser.fSuppressPaint = true;
     SkString originalID(f_id);
     f_id.set("mask"); // write out gradient named given name + color (less initial #)
-    f_id.append(baseColor.c_str() + 1); 
+    f_id.append(baseColor.c_str() + 1);
     SkString originalColors;
     for (SkSVGElement** ptr = fChildren.begin(); ptr < fChildren.end(); ptr++) {
         SkSVGStop* colorElement = (SkSVGStop*) *ptr;
@@ -112,4 +112,3 @@
     parser.fSuppressPaint = false;
     parser.fHead = saveHead;
 }
-
diff --git a/src/svg/SkSVGPaintState.cpp b/src/svg/SkSVGPaintState.cpp
index 1a30cb8..5db624d 100644
--- a/src/svg/SkSVGPaintState.cpp
+++ b/src/svg/SkSVGPaintState.cpp
@@ -49,7 +49,7 @@
     return result;
 }
 
-void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex, 
+void SkSVGPaint::addAttribute(SkSVGParser& parser, int attrIndex,
         const char* attrValue, size_t attrLength) {
     SkString* attr = (*this)[attrIndex];
     switch(attrIndex) {
@@ -221,7 +221,7 @@
                 sum.setConcat(matrix, sum);
                 continue;
             }
-            if ( index == kClipPath) 
+            if ( index == kClipPath)
                 *clips.insert(0) = lastAttr;
         }
         walking = walking->fNext;
@@ -267,7 +267,7 @@
 #endif
 }
 
-bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser, 
+bool SkSVGPaint::writeChangedAttributes(SkSVGParser& parser,
         SkSVGPaint& current, bool* changed) {
     SkSVGPaint& lastState = parser.fLastFlush;
     for (int index = kInitial + 1; index < kTerminal; index++) {
@@ -285,7 +285,7 @@
             case kEnableBackground:
                 break;
             case kFill:
-                if (topAttr->equals("none") == false && lastAttr->equals("none") == true) 
+                if (topAttr->equals("none") == false && lastAttr->equals("none") == true)
                     parser._addAttribute("stroke", "false");
                 goto fillStrokeAttrCommon;
             case kFillRule:
@@ -307,7 +307,7 @@
             case kStopOpacity:
                 break;
             case kStroke:
-                if (topAttr->equals("none") == false && lastAttr->equals("none") == true) 
+                if (topAttr->equals("none") == false && lastAttr->equals("none") == true)
                     parser._addAttribute("stroke", "true");
 fillStrokeAttrCommon:
                 if (strncmp(attrValue, "url(", 4) == 0) {
@@ -442,7 +442,7 @@
     }
     return true;
 }
-                
+
 void SkSVGPaint::Push(SkSVGPaint** head, SkSVGPaint* newRecord) {
     newRecord->fNext = *head;
     *head = newRecord;
@@ -452,4 +452,3 @@
     SkSVGPaint* next = (*head)->fNext;
     *head = next;
 }
-
diff --git a/src/svg/SkSVGParser.cpp b/src/svg/SkSVGParser.cpp
index c8712b4..74ea023 100644
--- a/src/svg/SkSVGParser.cpp
+++ b/src/svg/SkSVGParser.cpp
@@ -35,9 +35,9 @@
 
 static int gGeneratedMatrixID = 0;
 
-SkSVGParser::SkSVGParser(SkXMLParserError* errHandler) : 
-	SkXMLParser(errHandler),
-	fHead(&fEmptyPaint), fIDs(256),
+SkSVGParser::SkSVGParser(SkXMLParserError* errHandler) :
+    SkXMLParser(errHandler),
+    fHead(&fEmptyPaint), fIDs(256),
         fXMLWriter(&fStream), fCurrElement(NULL), fInSVG(false), fSuppressPaint(false) {
     fLastTransform.reset();
     fEmptyPaint.f_fill.set("black");
@@ -72,7 +72,7 @@
     size_t result = 0;
     while (result < count) {
         if (strncmp(attributes->fName, attrValue, len) == 0 && strlen(attributes->fName) == len) {
-            SkASSERT(result == (attributes->fOffset - 
+            SkASSERT(result == (attributes->fOffset -
                 (isPaint ? sizeof(SkString) : sizeof(SkSVGElement))) / sizeof(SkString));
             return result;
         }
@@ -199,9 +199,9 @@
     SkSVGTypes type = GetType(name, len);
 //    SkASSERT(type >= 0);
     if (type < 0) {
-		type = SkSVGType_G;
+        type = SkSVGType_G;
 //        return true;
-	}
+    }
     SkSVGElement* parent = fParents.count() > 0 ? fParents.top() : NULL;
     SkSVGElement* element = CreateElement(type, parent);
     bool result = false;
@@ -219,7 +219,7 @@
 bool SkSVGParser::onText(const char text[], int len) {
     if (fInSVG == false)
         return false;
-    SkSVGTypes type = fCurrElement->getType(); 
+    SkSVGTypes type = fCurrElement->getType();
     if (type != SkSVGType_Text && type != SkSVGType_Tspan)
         return false;
     SkSVGText* textElement = (SkSVGText*) fCurrElement;
@@ -310,7 +310,7 @@
         string.append(elems[index], end - elems[index] + 1);
     }
     string.remove(string.size() - 1, 1);
-    string.append(",0,0,1]");    
+    string.append(",0,0,1]");
     _addAttribute("matrix", string);
     _endElement();  // matrix
 }
@@ -434,8 +434,8 @@
 const int kSVGTypeNamesSize = SK_ARRAY_COUNT(gSVGTypeNames);
 
 SkSVGTypes SkSVGParser::GetType(const char match[], size_t len ) {
-    int index = SkStrSearch(&gSVGTypeNames[0].fName, kSVGTypeNamesSize, match, 
+    int index = SkStrSearch(&gSVGTypeNames[0].fName, kSVGTypeNamesSize, match,
         len, sizeof(gSVGTypeNames[0]));
-    return index >= 0 && index < kSVGTypeNamesSize ? gSVGTypeNames[index].fType : 
+    return index >= 0 && index < kSVGTypeNamesSize ? gSVGTypeNames[index].fType :
         (SkSVGTypes) -1;
 }
diff --git a/src/svg/SkSVGPath.cpp b/src/svg/SkSVGPath.cpp
index 92f5b14..ab18a65 100644
--- a/src/svg/SkSVGPath.cpp
+++ b/src/svg/SkSVGPath.cpp
@@ -29,7 +29,7 @@
     }
     if (hasMultiplePaths) {
         SkString& fillRule = parser.getPaintLast(SkSVGPaint::kFillRule);
-        if (fillRule.size() > 0) 
+        if (fillRule.size() > 0)
             parser._addAttribute("fillType", fillRule.equals("evenodd") ? "evenOdd" : "winding");
     }
     SVG_ADD_ATTRIBUTE(d);
diff --git a/src/svg/SkSVGPolygon.cpp b/src/svg/SkSVGPolygon.cpp
index 97cf5e0..4b458db 100644
--- a/src/svg/SkSVGPolygon.cpp
+++ b/src/svg/SkSVGPolygon.cpp
@@ -18,7 +18,7 @@
 
 DEFINE_SVG_INFO(Polygon)
 
-void SkSVGPolygon::addAttribute(SkSVGParser& parser, int attrIndex, 
+void SkSVGPolygon::addAttribute(SkSVGParser& parser, int attrIndex,
         const char* attrValue, size_t attrLength) {
     INHERITED::addAttribute(parser, attrIndex, attrValue, attrLength);
 }
@@ -27,7 +27,7 @@
     parser._startElement("polygon");
     SkSVGElement::translate(parser, defState);
     SVG_ADD_ATTRIBUTE(points);
-    if (f_fillRule.size() > 0) 
+    if (f_fillRule.size() > 0)
         parser._addAttribute("fillType", f_fillRule.equals("evenodd") ? "evenOdd" : "winding");
     parser._endElement();
 }
diff --git a/src/svg/SkSVGPolygon.h b/src/svg/SkSVGPolygon.h
index d353764..b2848d0 100644
--- a/src/svg/SkSVGPolygon.h
+++ b/src/svg/SkSVGPolygon.h
@@ -14,7 +14,7 @@
 
 class SkSVGPolygon : public SkSVGPolyline {
     DECLARE_SVG_INFO(Polygon);
-    virtual void addAttribute(SkSVGParser& , int attrIndex, 
+    virtual void addAttribute(SkSVGParser& , int attrIndex,
         const char* attrValue, size_t attrLength);
 private:
     typedef SkSVGPolyline INHERITED;
diff --git a/src/svg/SkSVGPolyline.cpp b/src/svg/SkSVGPolyline.cpp
index fe83c04..83dad48 100644
--- a/src/svg/SkSVGPolyline.cpp
+++ b/src/svg/SkSVGPolyline.cpp
@@ -24,7 +24,7 @@
 
 DEFINE_SVG_INFO(Polyline)
 
-void SkSVGPolyline::addAttribute(SkSVGParser& , int attrIndex, 
+void SkSVGPolyline::addAttribute(SkSVGParser& , int attrIndex,
         const char* attrValue, size_t attrLength) {
     if (attrIndex != kPoints)
         return;
@@ -37,7 +37,7 @@
     parser._startElement("polyline");
     INHERITED::translate(parser, defState);
     SVG_ADD_ATTRIBUTE(points);
-    if (f_fillRule.size() > 0) 
+    if (f_fillRule.size() > 0)
         parser._addAttribute("fillType", f_fillRule.equals("evenodd") ? "evenOdd" : "winding");
     parser._endElement();
 }
diff --git a/src/svg/SkSVGPolyline.h b/src/svg/SkSVGPolyline.h
index 62dc417..b8d5af4 100644
--- a/src/svg/SkSVGPolyline.h
+++ b/src/svg/SkSVGPolyline.h
@@ -15,7 +15,7 @@
 
 class SkSVGPolyline : public SkSVGElement {
     DECLARE_SVG_INFO(Polyline);
-    virtual void addAttribute(SkSVGParser& , int attrIndex, 
+    virtual void addAttribute(SkSVGParser& , int attrIndex,
         const char* attrValue, size_t attrLength);
 protected:
     SkString f_clipRule;
diff --git a/src/svg/SkSVGSVG.cpp b/src/svg/SkSVGSVG.cpp
index a072d09..fcce62d 100644
--- a/src/svg/SkSVGSVG.cpp
+++ b/src/svg/SkSVGSVG.cpp
@@ -48,8 +48,8 @@
     box.fTop = SkScalarDiv(viewBox[1], height);
     box.fRight = SkScalarDiv(viewBox[2], width);
     box.fBottom = SkScalarDiv(viewBox[3], height);
-    if (box.fLeft == 0 && box.fTop == 0 && 
-        box.fRight == SK_Scalar1 && box.fBottom == SK_Scalar1) 
+    if (box.fLeft == 0 && box.fTop == 0 &&
+        box.fRight == SK_Scalar1 && box.fBottom == SK_Scalar1)
             return;
     parser._startElement("matrix");
     if (box.fLeft != 0) {
@@ -72,5 +72,5 @@
         y.appendScalar(box.fBottom);
         parser._addAttributeLen("scaleY", y.c_str(), y.size());
     }
-    parser._endElement();   
+    parser._endElement();
 }
diff --git a/src/svg/SkSVGSVG.h b/src/svg/SkSVGSVG.h
index 7d1e16a..155f9a9 100644
--- a/src/svg/SkSVGSVG.h
+++ b/src/svg/SkSVGSVG.h
@@ -22,11 +22,11 @@
     SkString f_width;
     SkString f_version;
     SkString f_viewBox;
-	SkString f_x;
+    SkString f_x;
     SkString f_xml_space;
     SkString f_xmlns;
     SkString f_xml_xlink;
-	SkString f_y;
+    SkString f_y;
 
     typedef SkSVGElement INHERITED;
 };
diff --git a/src/svg/SkSVGSymbol.cpp b/src/svg/SkSVGSymbol.cpp
index a988467..ce341e6 100644
--- a/src/svg/SkSVGSymbol.cpp
+++ b/src/svg/SkSVGSymbol.cpp
@@ -18,5 +18,5 @@
 
 void SkSVGSymbol::translate(SkSVGParser& parser, bool defState) {
     INHERITED::translate(parser, defState);
-    // !!! children need to be written into document 
+    // !!! children need to be written into document
 }
diff --git a/src/text/SkTextLayout.cpp b/src/text/SkTextLayout.cpp
index fda4b2f..4e531cf 100644
--- a/src/text/SkTextLayout.cpp
+++ b/src/text/SkTextLayout.cpp
@@ -7,6 +7,8 @@
  */
 #include "SkTextLayout.h"
 
+SK_DEFINE_INST_COUNT(SkTextStyle)
+
 SkTextStyle::SkTextStyle() {
     fPaint.setAntiAlias(true);
 }
@@ -76,4 +78,3 @@
 
 void SkTextLayout::draw(SkCanvas* canvas) {
 }
-
diff --git a/src/utils/SkBase64.cpp b/src/utils/SkBase64.cpp
index a8d4e87..11b647f 100644
--- a/src/utils/SkBase64.cpp
+++ b/src/utils/SkBase64.cpp
@@ -12,7 +12,7 @@
 #define DecodePad -2
 #define EncodePad 64
 
-static const char default_encode[] = 
+static const char default_encode[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     "abcdefghijklmnopqrstuvwxyz"
     "0123456789+/=";
@@ -55,7 +55,7 @@
             signed char decoded = decodeData[srcByte - '+'];
             bytes[byte] = decoded;
             if (decoded < 0) {
-                if (decoded == DecodePad) 
+                if (decoded == DecodePad)
                     goto handlePad;
                 return kBadCharError;
             } else
@@ -89,7 +89,7 @@
             *dst = (unsigned char) one;
         }
         dst++;
-        if (padTwo) 
+        if (padTwo)
             break;
         if (writeDestination)
             *dst = (unsigned char) two;
@@ -105,7 +105,7 @@
     return kNoError;
 }
 
-#if defined _WIN32 && _MSC_VER >= 1300  
+#if defined _WIN32 && _MSC_VER >= 1300
 #pragma warning ( pop )
 #endif
 
@@ -126,7 +126,7 @@
             unsigned b = *src++;
             unsigned c = *src++;
             int      d = c & 0x3F;
-            c = (c >> 6 | b << 2) & 0x3F; 
+            c = (c >> 6 | b << 2) & 0x3F;
             b = (b >> 4 | a << 4) & 0x3F;
             a = a >> 2;
             *dst++ = encode[a];
@@ -183,5 +183,3 @@
     }
 }
 #endif
-
-
diff --git a/src/utils/SkBase64.h b/src/utils/SkBase64.h
index 4f3b323..5bf9006 100644
--- a/src/utils/SkBase64.h
+++ b/src/utils/SkBase64.h
@@ -38,7 +38,7 @@
 
     size_t fLength;
     char* fData;
-    friend class SkImage;
+    friend class SkImageBaseBitmap;
 };
 
 #endif // SkBase64_DEFINED
diff --git a/src/pdf/SkBitSet.cpp b/src/utils/SkBitSet.cpp
similarity index 100%
rename from src/pdf/SkBitSet.cpp
rename to src/utils/SkBitSet.cpp
diff --git a/include/pdf/SkBitSet.h b/src/utils/SkBitSet.h
similarity index 100%
rename from include/pdf/SkBitSet.h
rename to src/utils/SkBitSet.h
diff --git a/src/utils/SkBitmapChecksummer.cpp b/src/utils/SkBitmapChecksummer.cpp
new file mode 100644
index 0000000..bb9fc8d
--- /dev/null
+++ b/src/utils/SkBitmapChecksummer.cpp
@@ -0,0 +1,69 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkBitmapChecksummer.h"
+#include "SkBitmapTransformer.h"
+#include "SkCityHash.h"
+#include "SkEndian.h"
+
+/**
+ * Write an integer value into a bytebuffer in little-endian order.
+ */
+static void write_int_to_buffer(int val, char* buf) {
+    val = SkEndian_SwapLE32(val);
+    for (int byte=0; byte<4; byte++) {
+        *buf++ = (char)(val & 0xff);
+        val = val >> 8;
+    }
+}
+
+/*static*/ uint64_t SkBitmapChecksummer::Compute64Internal(
+        const SkBitmap& bitmap, const SkBitmapTransformer& transformer) {
+    int pixelBufferSize = transformer.bytesNeededTotal();
+    int totalBufferSize = pixelBufferSize + 8; // leave room for x/y dimensions
+
+    SkAutoMalloc bufferManager(totalBufferSize);
+    char *bufferStart = static_cast<char *>(bufferManager.get());
+    char *bufPtr = bufferStart;
+    // start with the x/y dimensions
+    write_int_to_buffer(bitmap.width(), bufPtr);
+    bufPtr += 4;
+    write_int_to_buffer(bitmap.height(), bufPtr);
+    bufPtr += 4;
+
+    // add all the pixel data
+    if (!transformer.copyBitmapToPixelBuffer(bufPtr, pixelBufferSize)) {
+        return 0;
+    }
+    return SkCityHash::Compute64(bufferStart, totalBufferSize);
+}
+
+/*static*/ uint64_t SkBitmapChecksummer::Compute64(const SkBitmap& bitmap) {
+    const SkBitmapTransformer::PixelFormat kPixelFormat =
+        SkBitmapTransformer::kARGB_8888_Premul_PixelFormat;
+
+    // First, try to transform the existing bitmap.
+    const SkBitmapTransformer transformer =
+        SkBitmapTransformer(bitmap, kPixelFormat);
+    if (transformer.isValid(false)) {
+        return Compute64Internal(bitmap, transformer);
+    }
+
+    // Hmm, that didn't work. Maybe if we create a new
+    // kARGB_8888_Config version of the bitmap it will work better?
+    SkBitmap copyBitmap;
+    bitmap.copyTo(&copyBitmap, SkBitmap::kARGB_8888_Config);
+    const SkBitmapTransformer copyTransformer =
+        SkBitmapTransformer(copyBitmap, kPixelFormat);
+    if (copyTransformer.isValid(true)) {
+        return Compute64Internal(copyBitmap, copyTransformer);
+    } else {
+        return 0;
+    }
+}
diff --git a/src/utils/SkBitmapChecksummer.h b/src/utils/SkBitmapChecksummer.h
new file mode 100644
index 0000000..63ac726
--- /dev/null
+++ b/src/utils/SkBitmapChecksummer.h
@@ -0,0 +1,37 @@
+
+/*
+ * 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 SkBitmapChecksummer_DEFINED
+#define SkBitmapChecksummer_DEFINED
+
+#include "SkBitmap.h"
+#include "SkBitmapTransformer.h"
+
+/**
+ * Static class that can generate checksums from SkBitmaps.
+ */
+class SkBitmapChecksummer {
+public:
+    /**
+     * Returns a 64-bit checksum of the pixels in this bitmap.
+     *
+     * If this is unable to compute the checksum for some reason,
+     * it returns 0.
+     *
+     * Note: depending on the bitmap config, we may need to create an
+     * intermediate SkBitmap and copy the pixels over to it... so in some
+     * cases, performance and memory usage can suffer.
+     */
+    static uint64_t Compute64(const SkBitmap& bitmap);
+
+private:
+    static uint64_t Compute64Internal(const SkBitmap& bitmap,
+                                      const SkBitmapTransformer& transformer);
+};
+
+#endif
diff --git a/src/utils/SkBitmapTransformer.cpp b/src/utils/SkBitmapTransformer.cpp
new file mode 100644
index 0000000..c8356d4
--- /dev/null
+++ b/src/utils/SkBitmapTransformer.cpp
@@ -0,0 +1,87 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkBitmapTransformer.h"
+#include "SkColorPriv.h"
+#include "SkTypes.h"
+
+bool SkBitmapTransformer::isValid(bool logReason) const {
+    bool retval = true;
+
+    switch(fPixelFormat) {
+    case kARGB_8888_Premul_PixelFormat:
+        break;
+    default:
+        if (logReason) {
+            SkDEBUGF(("PixelFormat %d not supported\n", fPixelFormat));
+        }
+        retval = false;
+    }
+
+    SkBitmap::Config bitmapConfig = fBitmap.config();
+    switch(bitmapConfig) {
+    case SkBitmap::kARGB_8888_Config:
+        break;
+    default:
+        if (logReason) {
+            SkDEBUGF(("SkBitmap::Config %d not supported\n", bitmapConfig));
+        }
+        retval = false;
+    }
+
+    return retval;
+}
+
+/**
+ * Transform from kARGB_8888_Config to kARGB_8888_Premul_PixelFormat.
+ *
+ * Similar to the various scanline transformers in
+ * src/images/transform_scanline.h .
+ */
+static void transform_scanline(const char* SK_RESTRICT src, int width,
+                               char* SK_RESTRICT dst) {
+    const SkPMColor* SK_RESTRICT srcP = reinterpret_cast<const SkPMColor*>(src);
+    for (int i = 0; i < width; i++) {
+        SkPMColor c = *srcP++;
+        unsigned a = SkGetPackedA32(c);
+        unsigned r = SkGetPackedR32(c);
+        unsigned g = SkGetPackedG32(c);
+        unsigned b = SkGetPackedB32(c);
+        *dst++ = a;
+        *dst++ = r;
+        *dst++ = g;
+        *dst++ = b;
+    }
+}
+
+bool SkBitmapTransformer::copyBitmapToPixelBuffer(void *dstBuffer,
+                                                  size_t dstBufferSize) const {
+    if (!this->isValid(true)) {
+        return false;
+    }
+    size_t bytesNeeded = this->bytesNeededTotal();
+    if (dstBufferSize < bytesNeeded) {
+        SkDEBUGF(("dstBufferSize %d must be >= %d\n", dstBufferSize, bytesNeeded));
+        return false;
+    }
+
+    fBitmap.lockPixels();
+    int width = fBitmap.width();
+    size_t srcRowBytes = fBitmap.rowBytes();
+    size_t dstRowBytes = this->bytesNeededPerRow();
+    const char *srcBytes = const_cast<const char *>(static_cast<char*>(fBitmap.getPixels()));
+    char *dstBytes = static_cast<char *>(dstBuffer);
+    for (int y = 0; y < fBitmap.height(); y++) {
+        transform_scanline(srcBytes, width, dstBytes);
+        srcBytes += srcRowBytes;
+        dstBytes += dstRowBytes;
+    }
+    fBitmap.unlockPixels();
+    return true;
+}
diff --git a/src/utils/SkBitmapTransformer.h b/src/utils/SkBitmapTransformer.h
new file mode 100644
index 0000000..70971ac
--- /dev/null
+++ b/src/utils/SkBitmapTransformer.h
@@ -0,0 +1,104 @@
+
+/*
+ * 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 SkBitmapTransformer_DEFINED
+#define SkBitmapTransformer_DEFINED
+
+#include "SkBitmap.h"
+
+/**
+ * Class that can copy pixel data out of an SkBitmap, transforming it
+ * into the appropriate PixelFormat.
+ *
+ * As noted in https://codereview.appspot.com/6849119/#msg6 and
+ * https://codereview.appspot.com/6900047 , at some point we might want
+ * to make this more general purpose:
+ * - support more PixelFormats
+ * - use existing SkCanvas::Config8888 enum instead of new PixelFormat enum
+ * - add method to copy pixel data for a single row, instead of the whole bitmap
+ * - add methods to copy pixel data INTO an SkBitmap
+ *
+ * That would allow us to replace SkCopyConfig8888ToBitmap() in
+ * src/core/SkConfig8888.h , as well as the transformations used by
+ * src/images/SkImageDecoder_libpng.cpp , with this common code.
+ *
+ * But for now, we want something more narrowly targeted, just
+ * supplying what is needed by SkBitmapChecksummer.
+ */
+class SkBitmapTransformer {
+public:
+    enum PixelFormat {
+        // 32 bits per pixel, ARGB byte order, with the alpha-channel
+        // value premultiplied into the R/G/B channel values.
+        kARGB_8888_Premul_PixelFormat,
+
+        // marks the end of the list
+        kLast_PixelFormat = kARGB_8888_Premul_PixelFormat,
+    };
+
+    /**
+     * Creates an SkBitmapTransformer instance that can transform between
+     * the given bitmap and a pixel buffer with given pixelFormat.
+     *
+     * Call IsValid() before using, to confirm that this particular
+     * bitmap/pixelFormat combination is supported!
+     */
+    SkBitmapTransformer(const SkBitmap& bitmap, PixelFormat pixelFormat) :
+        fBitmap(bitmap), fPixelFormat(pixelFormat) {}
+
+    /**
+     * Returns true iff we can convert between fBitmap and fPixelFormat.
+     * If this returns false, the return values of any other methods will
+     * be meaningless!
+     *
+     * @param logReason whether to log the reason why this combination
+     *                  is unsupported (only applies in debug mode)
+     */
+    bool isValid(bool logReason=false) const;
+
+    /**
+     * Returns the number of bytes needed to store a single row of the
+     * bitmap's pixels if converted to pixelFormat.
+     */
+    size_t bytesNeededPerRow() const {
+        // This is hard-coded for the single supported PixelFormat.
+        return fBitmap.width() * 4;
+    }
+
+    /**
+     * Returns the number of bytes needed to store the entire bitmap
+     * if converted to pixelFormat, ASSUMING that it is written
+     * out as a single contiguous blob of pixels (no leftover bytes
+     * at the end of each row).
+     */
+    size_t bytesNeededTotal() const {
+        return this->bytesNeededPerRow() * fBitmap.height();
+    }
+
+    /**
+     * Writes the entire bitmap into dstBuffer, using the already-specified
+     * pixelFormat. Returns true if successful.
+     *
+     * dstBufferSize is the maximum allowable bytes to write into dstBuffer;
+     * if that is not large enough to hold the entire bitmap, then this
+     * will fail immediately and return false.
+     * We force the caller to pass this in to avoid buffer overruns in
+     * unanticipated cases.
+     *
+     * All pixels for all rows will be written into dstBuffer as a
+     * single contiguous blob (no skipped pixels at the end of each
+     * row).
+     */
+    bool copyBitmapToPixelBuffer (void *dstBuffer, size_t dstBufferSize) const;
+
+private:
+    const SkBitmap& fBitmap;
+    const PixelFormat fPixelFormat;
+};
+
+#endif
diff --git a/src/utils/SkBoundaryPatch.cpp b/src/utils/SkBoundaryPatch.cpp
index 37f59b4..fd1545d 100644
--- a/src/utils/SkBoundaryPatch.cpp
+++ b/src/utils/SkBoundaryPatch.cpp
@@ -7,6 +7,8 @@
  */
 #include "SkBoundaryPatch.h"
 
+SK_DEFINE_INST_COUNT(SkBoundary)
+
 SkBoundaryPatch::SkBoundaryPatch() : fBoundary(NULL) {}
 
 SkBoundaryPatch::~SkBoundaryPatch() {
@@ -48,7 +50,7 @@
 
     const SkScalar invR = SkScalarInvert(SkIntToScalar(rows - 1));
     const SkScalar invC = SkScalarInvert(SkIntToScalar(cols - 1));
-    
+
     for (int y = 0; y < cols; y++) {
         SkScalar yy = y * invC;
         for (int x = 0; x < rows; x++) {
@@ -77,4 +79,3 @@
     SkEvalCubicAt(&fPts[e * 3], t, &loc, NULL, NULL);
     return loc;
 }
-
diff --git a/src/utils/SkCamera.cpp b/src/utils/SkCamera.cpp
index ac6fa0f..a7c0b14 100644
--- a/src/utils/SkCamera.cpp
+++ b/src/utils/SkCamera.cpp
@@ -269,13 +269,13 @@
     fAxis.normalize(&axis);
 
     {
-        SkScalar dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&fZenith, axis);
+        SkScalar dot = SkUnit3D::Dot(*SkTCast<const SkUnit3D*>(&fZenith), axis);
 
         zenith.fX = fZenith.fX - SkUnitScalarMul(dot, axis.fX);
         zenith.fY = fZenith.fY - SkUnitScalarMul(dot, axis.fY);
         zenith.fZ = fZenith.fZ - SkUnitScalarMul(dot, axis.fZ);
 
-        (void)((SkPoint3D*)(void*)&zenith)->normalize(&zenith);
+        SkTCast<SkPoint3D*>(&zenith)->normalize(&zenith);
     }
 
     SkUnit3D::Cross(axis, zenith, &cross);
@@ -313,8 +313,8 @@
     diff.fY = quilt.fOrigin.fY - fLocation.fY;
     diff.fZ = quilt.fOrigin.fZ - fLocation.fZ;
 
-    dot = SkUnit3D::Dot(*(const SkUnit3D*)(const void*)&diff,
-                        *(const SkUnit3D*)(((const SkScalar*)(const void*)&fOrientation) + 6));
+    dot = SkUnit3D::Dot(*SkTCast<const SkUnit3D*>(&diff),
+                        *SkTCast<const SkUnit3D*>(SkTCast<const SkScalar*>(&fOrientation) + 6));
 
     patchPtr = (const SkScalar*)&quilt;
     matrix->set(SkMatrix::kMScaleX, SkScalarDotDiv(3, patchPtr, 1, mapPtr, 1, dot));
@@ -369,7 +369,7 @@
     fCamera.fLocation.set(x * SkFloatToScalar(72.0f), y * SkFloatToScalar(72.0f), lz);
     fCamera.fObserver.set(0, 0, lz);
     fCamera.update();
-    
+
 }
 
 SkScalar Sk3DView::getCameraLocationX() {
@@ -419,8 +419,7 @@
 
 void Sk3DView::applyToCanvas(SkCanvas* canvas) const {
     SkMatrix    matrix;
-    
+
     this->getMatrix(&matrix);
     canvas->concat(matrix);
 }
-
diff --git a/src/utils/SkCityHash.cpp b/src/utils/SkCityHash.cpp
new file mode 100644
index 0000000..a21aa89
--- /dev/null
+++ b/src/utils/SkCityHash.cpp
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Pass any calls through to the CityHash library.
+ * This is the only source file that accesses the CityHash code directly.
+ */
+
+#include "SkCityHash.h"
+#include "SkTypes.h"
+#include "city.h"
+
+uint32_t SkCityHash::Compute32(const char *data, size_t size) {
+    return CityHash32(data, size);
+}
+
+uint64_t SkCityHash::Compute64(const char *data, size_t size) {
+    return CityHash64(data, size);
+}
diff --git a/src/utils/SkCityHash.h b/src/utils/SkCityHash.h
new file mode 100644
index 0000000..c69e0fb
--- /dev/null
+++ b/src/utils/SkCityHash.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Hash functions, using the CityHash algorithm.
+ *
+ * Results are guaranteed to be:
+ *  1. consistent across revisions of the library (for a given set
+ *     of bytes, the checksum generated at one revision of the Skia
+ *     library will match the one generated on any other revision of
+ *     the Skia library)
+ *  2. consistent across platforms (for a given
+ *     set of bytes, the checksum generated on one platform will
+ *     match the one generated on any other platform)
+ */
+
+#ifndef SkCityHash_DEFINED
+#define SkCityHash_DEFINED
+
+#include "SkTypes.h"
+
+class SkCityHash : SkNoncopyable {
+public:
+    /**
+     *  Compute a 32-bit checksum for a given data block.
+     *
+     *  @param data Memory address of the data block to be processed.
+     *  @param size Size of the data block in bytes.
+     *  @return checksum result
+     */
+    static uint32_t Compute32(const char *data, size_t size);
+
+    /**
+     *  Compute a 64-bit checksum for a given data block.
+     *
+     *  @param data Memory address of the data block to be processed.
+     *  @param size Size of the data block in bytes.
+     *  @return checksum result
+     */
+    static uint64_t Compute64(const char *data, size_t size);
+};
+
+#endif
diff --git a/src/utils/SkColorMatrix.cpp b/src/utils/SkColorMatrix.cpp
deleted file mode 100644
index cb77de1..0000000
--- a/src/utils/SkColorMatrix.cpp
+++ /dev/null
@@ -1,161 +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 "SkColorMatrix.h"
-
-#define kRScale     0
-#define kGScale     6
-#define kBScale     12
-#define kAScale     18
-
-void SkColorMatrix::setIdentity() {
-    memset(fMat, 0, sizeof(fMat));
-    fMat[kRScale] = fMat[kGScale] = fMat[kBScale] = fMat[kAScale] = SK_Scalar1;
-}
-
-void SkColorMatrix::setScale(SkScalar rScale, SkScalar gScale, SkScalar bScale,
-                             SkScalar aScale) {
-    memset(fMat, 0, sizeof(fMat));
-    fMat[kRScale] = rScale;
-    fMat[kGScale] = gScale;
-    fMat[kBScale] = bScale;
-    fMat[kAScale] = aScale;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkColorMatrix::setRotate(Axis axis, SkScalar degrees) {
-    SkScalar S, C;
-
-    S = SkScalarSinCos(SkDegreesToRadians(degrees), &C);
-
-    this->setSinCos(axis, S, C);
-}
-
-void SkColorMatrix::setSinCos(Axis axis, SkScalar sine, SkScalar cosine) {
-    SkASSERT((unsigned)axis < 3);
-
-    static const uint8_t gRotateIndex[] = {
-        6, 7, 11, 12,
-        0, 10, 2, 12,
-        0, 1,  5,  6,
-    };
-    const uint8_t* index = gRotateIndex + axis * 4;
-    
-    this->setIdentity();
-    fMat[index[0]] = cosine;
-    fMat[index[1]] = sine;
-    fMat[index[2]] = -sine;
-    fMat[index[3]] = cosine;
-}
-
-void SkColorMatrix::preRotate(Axis axis, SkScalar degrees) {
-    SkColorMatrix tmp;
-    tmp.setRotate(axis, degrees);
-    this->preConcat(tmp);
-}
-
-void SkColorMatrix::postRotate(Axis axis, SkScalar degrees) {
-    SkColorMatrix tmp;
-    tmp.setRotate(axis, degrees);
-    this->postConcat(tmp);
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-void SkColorMatrix::setConcat(const SkColorMatrix& matA,
-                              const SkColorMatrix& matB) {
-    SkScalar    tmp[20];
-    SkScalar*   result = fMat;
-
-    if (&matA == this || &matB == this) {
-        result = tmp;
-    }
-    
-    const SkScalar* a = matA.fMat;
-    const SkScalar* b = matB.fMat;
-
-    int index = 0;
-    for (int j = 0; j < 20; j += 5) {
-        for (int i = 0; i < 4; i++) {
-            result[index++] =   SkScalarMul(a[j + 0], b[i + 0]) + 
-                                SkScalarMul(a[j + 1], b[i + 5]) +
-                                SkScalarMul(a[j + 2], b[i + 10]) +
-                                SkScalarMul(a[j + 3], b[i + 15]);
-        }
-        result[index++] =   SkScalarMul(a[j + 0], b[4]) +
-                            SkScalarMul(a[j + 1], b[9]) +
-                            SkScalarMul(a[j + 2], b[14]) +
-                            SkScalarMul(a[j + 3], b[19]) +
-                            a[j + 4];
-    }
-    
-    if (fMat != result) {
-        memcpy(fMat, result, sizeof(fMat));
-    }
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-static void setrow(SkScalar row[], SkScalar r, SkScalar g, SkScalar b) {
-    row[0] = r;
-    row[1] = g;
-    row[2] = b;
-}
-
-static const SkScalar kHueR = SkFloatToScalar(0.213f);
-static const SkScalar kHueG = SkFloatToScalar(0.715f);
-static const SkScalar kHueB = SkFloatToScalar(0.072f);
-
-void SkColorMatrix::setSaturation(SkScalar sat) {
-    memset(fMat, 0, sizeof(fMat));
-
-    const SkScalar R = SkScalarMul(kHueR, SK_Scalar1 - sat);
-    const SkScalar G = SkScalarMul(kHueG, SK_Scalar1 - sat);
-    const SkScalar B = SkScalarMul(kHueB, SK_Scalar1 - sat);
-
-    setrow(fMat +  0, R + sat, G, B);
-    setrow(fMat +  5, R, G + sat, B);
-    setrow(fMat + 10, R, G, B + sat);
-    fMat[18] = SK_Scalar1;
-}
-
-static const SkScalar kR2Y = SkFloatToScalar(0.299f);
-static const SkScalar kG2Y = SkFloatToScalar(0.587f);
-static const SkScalar kB2Y = SkFloatToScalar(0.114f);
-
-static const SkScalar kR2U = SkFloatToScalar(-0.16874f);
-static const SkScalar kG2U = SkFloatToScalar(-0.33126f);
-static const SkScalar kB2U = SkFloatToScalar(0.5f);
-
-static const SkScalar kR2V = SkFloatToScalar(0.5f);
-static const SkScalar kG2V = SkFloatToScalar(-0.41869f);
-static const SkScalar kB2V = SkFloatToScalar(-0.08131f);
-
-void SkColorMatrix::setRGB2YUV() {
-    memset(fMat, 0, sizeof(fMat));
-    
-    setrow(fMat +  0, kR2Y, kG2Y, kB2Y);
-    setrow(fMat +  5, kR2U, kG2U, kB2U);
-    setrow(fMat + 10, kR2V, kG2V, kB2V);
-    fMat[18] = SK_Scalar1;
-}
-
-static const SkScalar kV2R = SkFloatToScalar(1.402f);
-static const SkScalar kU2G = SkFloatToScalar(-0.34414f);
-static const SkScalar kV2G = SkFloatToScalar(-0.71414f);
-static const SkScalar kU2B = SkFloatToScalar(1.772f);
-
-void SkColorMatrix::setYUV2RGB() {
-    memset(fMat, 0, sizeof(fMat));
-    
-    setrow(fMat +  0, SK_Scalar1, 0, kV2R);
-    setrow(fMat +  5, SK_Scalar1, kU2G, kV2G);
-    setrow(fMat + 10, SK_Scalar1, kU2B, 0);
-    fMat[18] = SK_Scalar1;
-}
-
diff --git a/src/utils/SkCondVar.cpp b/src/utils/SkCondVar.cpp
new file mode 100644
index 0000000..5d001c0
--- /dev/null
+++ b/src/utils/SkCondVar.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "SkCondVar.h"
+
+SkCondVar::SkCondVar() {
+#ifdef SK_USE_POSIX_THREADS
+    pthread_mutex_init(&fMutex, NULL /* default mutex attr */);
+    pthread_cond_init(&fCond, NULL /* default cond attr */);
+#elif defined(SK_BUILD_FOR_WIN32)
+    InitializeCriticalSection(&fCriticalSection);
+    InitializeConditionVariable(&fCondition);
+#endif
+}
+
+SkCondVar::~SkCondVar() {
+#ifdef SK_USE_POSIX_THREADS
+    pthread_mutex_destroy(&fMutex);
+    pthread_cond_destroy(&fCond);
+#elif defined(SK_BUILD_FOR_WIN32)
+    DeleteCriticalSection(&fCriticalSection);
+    // No need to clean up fCondition.
+#endif
+}
+
+void SkCondVar::lock() {
+#ifdef SK_USE_POSIX_THREADS
+    pthread_mutex_lock(&fMutex);
+#elif defined(SK_BUILD_FOR_WIN32)
+    EnterCriticalSection(&fCriticalSection);
+#endif
+}
+
+void SkCondVar::unlock() {
+#ifdef SK_USE_POSIX_THREADS
+    pthread_mutex_unlock(&fMutex);
+#elif defined(SK_BUILD_FOR_WIN32)
+    LeaveCriticalSection(&fCriticalSection);
+#endif
+}
+
+void SkCondVar::wait() {
+#ifdef SK_USE_POSIX_THREADS
+    pthread_cond_wait(&fCond, &fMutex);
+#elif defined(SK_BUILD_FOR_WIN32)
+    SleepConditionVariableCS(&fCondition, &fCriticalSection, INFINITE);
+#endif
+}
+
+void SkCondVar::signal() {
+#ifdef SK_USE_POSIX_THREADS
+    pthread_cond_signal(&fCond);
+#elif defined(SK_BUILD_FOR_WIN32)
+    WakeConditionVariable(&fCondition);
+#endif
+}
+
+void SkCondVar::broadcast() {
+#ifdef SK_USE_POSIX_THREADS
+    pthread_cond_broadcast(&fCond);
+#elif defined(SK_BUILD_FOR_WIN32)
+    WakeAllConditionVariable(&fCondition);
+#endif
+}
diff --git a/src/utils/SkCountdown.cpp b/src/utils/SkCountdown.cpp
new file mode 100644
index 0000000..98b3545
--- /dev/null
+++ b/src/utils/SkCountdown.cpp
@@ -0,0 +1,32 @@
+/*
+ * 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 "SkCountdown.h"
+#include "SkThread.h"
+
+SkCountdown::SkCountdown(int32_t count)
+: fCount(count) {}
+
+void SkCountdown::reset(int32_t count) {
+    fCount = count;
+}
+
+void SkCountdown::run() {
+    if (sk_atomic_dec(&fCount) == 1) {
+        fReady.lock();
+        fReady.signal();
+        fReady.unlock();
+    }
+}
+
+void SkCountdown::wait() {
+    fReady.lock();
+    while (fCount > 0) {
+        fReady.wait();
+    }
+    fReady.unlock();
+}
diff --git a/src/utils/SkCubicInterval.cpp b/src/utils/SkCubicInterval.cpp
index 6c6b0a9..566023a 100644
--- a/src/utils/SkCubicInterval.cpp
+++ b/src/utils/SkCubicInterval.cpp
@@ -59,10 +59,9 @@
 
     // now search for t given unitX
     SkScalar t = find_cubic_t(x1, x2 - 2*x1, x1 - x2 + SK_Scalar1, unitX);
-    
+
     // now evaluate the cubic in Y
     y1 *= 3;
     y2 *= 3;
     return eval_cubic(y1, y2 - 2*y1, y1 - y2 + SK_Scalar1, t);
 }
-
diff --git a/src/utils/SkCullPoints.cpp b/src/utils/SkCullPoints.cpp
index a903075..efb94f4 100644
--- a/src/utils/SkCullPoints.cpp
+++ b/src/utils/SkCullPoints.cpp
@@ -15,7 +15,7 @@
     return v.fX * dy - v.fY * dx < 0;
 #else
     Sk64   tmp0, tmp1;
-    
+
     tmp0.setMul(v.fX, dy);
     tmp1.setMul(dx, v.fY);
     tmp0.sub(tmp1);
@@ -33,7 +33,7 @@
         return false;
     }
 
-    // since the crossprod test is a little expensive, check for easy-in cases first    
+    // since the crossprod test is a little expensive, check for easy-in cases first
     if (r.contains(x0, y0) || r.contains(x1, y1)) {
         return true;
     }
@@ -41,7 +41,7 @@
     // At this point we're not sure, so we do a crossprod test
     SkIPoint           vec;
     const SkIPoint*    rAsQuad = fAsQuad;
-    
+
     vec.set(x1 - x0, y1 - y0);
     bool isNeg = cross_product_is_neg(vec, x0 - rAsQuad[0].fX, y0 - rAsQuad[0].fY);
     for (int i = 1; i < 4; i++) {
@@ -89,14 +89,14 @@
     LineToResult result = kNo_Result;
     int x0 = fPrevPt.fX;
     int y0 = fPrevPt.fY;
-    
+
     // need to upgrade sect_test to chop the result
     // and to correctly return kLineTo_Result when the result is connected
     // to the previous call-out
     if (this->sect_test(x0, y0, x, y)) {
         line[0].set(x0, y0);
         line[1].set(x, y);
-        
+
         if (fPrevResult != kNo_Result && fPrevPt.equals(x0, y0)) {
             result = kLineTo_Result;
         } else {
@@ -133,7 +133,7 @@
 
 void SkCullPointsPath::lineTo(int x, int y) {
     SkIPoint   pts[2];
-    
+
     switch (fCP.lineTo(x, y, pts)) {
     case SkCullPoints::kMoveToLineTo_Result:
         fPath->moveTo(SkIntToScalar(pts[0].fX), SkIntToScalar(pts[0].fY));
@@ -146,3 +146,74 @@
     }
 }
 
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkMatrix.h"
+#include "SkRegion.h"
+
+bool SkHitTestPath(const SkPath& path, SkRect& target, bool hires) {
+    if (target.isEmpty()) {
+        return false;
+    }
+
+    bool isInverse = path.isInverseFillType();
+    if (path.isEmpty()) {
+        return isInverse;
+    }
+
+    SkRect bounds = path.getBounds();
+
+    bool sects = SkRect::Intersects(target, bounds);
+    if (isInverse) {
+        if (!sects) {
+            return true;
+        }
+    } else {
+        if (!sects) {
+            return false;
+        }
+        if (target.contains(bounds)) {
+            return true;
+        }
+    }
+
+    SkPath devPath;
+    const SkPath* pathPtr;
+    SkRect        devTarget;
+
+    if (hires) {
+        const SkScalar coordLimit = SkIntToScalar(16384);
+        const SkRect limit = { 0, 0, coordLimit, coordLimit };
+
+        SkMatrix matrix;
+        matrix.setRectToRect(bounds, limit, SkMatrix::kFill_ScaleToFit);
+
+        path.transform(matrix, &devPath);
+        matrix.mapRect(&devTarget, target);
+
+        pathPtr = &devPath;
+    } else {
+        devTarget = target;
+        pathPtr = &path;
+    }
+
+    SkIRect iTarget;
+    devTarget.round(&iTarget);
+    if (iTarget.isEmpty()) {
+        iTarget.fLeft = SkScalarFloorToInt(devTarget.fLeft);
+        iTarget.fTop = SkScalarFloorToInt(devTarget.fTop);
+        iTarget.fRight = iTarget.fLeft + 1;
+        iTarget.fBottom = iTarget.fTop + 1;
+    }
+
+    SkRegion clip(iTarget);
+    SkRegion rgn;
+    return rgn.setPath(*pathPtr, clip) ^ isInverse;
+}
+
+bool SkHitTestPath(const SkPath& path, SkScalar x, SkScalar y, bool hires) {
+    const SkScalar half = SK_ScalarHalf;
+    const SkScalar one = SK_Scalar1;
+    SkRect r = SkRect::MakeXYWH(x - half, y - half, one, one);
+    return SkHitTestPath(path, r, hires);
+}
diff --git a/src/utils/SkDebugTrace.h b/src/utils/SkDebugTrace.h
index 447418e..5ec3e1c 100644
--- a/src/utils/SkDebugTrace.h
+++ b/src/utils/SkDebugTrace.h
@@ -22,5 +22,3 @@
   SkDebugf("Trace: %s (%s=%s, %s=%s)\n", event, name1, value1, name2, value2)
 
 #endif
-
-
diff --git a/src/utils/SkDeferredCanvas.cpp b/src/utils/SkDeferredCanvas.cpp
index e965050..5119729 100644
--- a/src/utils/SkDeferredCanvas.cpp
+++ b/src/utils/SkDeferredCanvas.cpp
@@ -1,6 +1,6 @@
 
 /*
- * Copyright 2011 Google Inc.
+ * Copyright 2013 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
@@ -8,152 +8,586 @@
 
 #include "SkDeferredCanvas.h"
 
-#include "SkPaint.h"
-#include "SkShader.h"
+#include "SkChunkAlloc.h"
 #include "SkColorFilter.h"
+#include "SkDevice.h"
 #include "SkDrawFilter.h"
+#include "SkGPipe.h"
+#include "SkPaint.h"
+#include "SkPaintPriv.h"
+#include "SkRRect.h"
+#include "SkShader.h"
+
+enum {
+    // Deferred canvas will auto-flush when recording reaches this limit
+    kDefaultMaxRecordingStorageBytes = 64*1024*1024,
+    kDeferredCanvasBitmapSizeThreshold = ~0U, // Disables this feature
+};
+
+enum PlaybackMode {
+    kNormal_PlaybackMode,
+    kSilent_PlaybackMode,
+};
 
 namespace {
-
-bool isPaintOpaque(const SkPaint* paint, 
-                   const SkBitmap* bmpReplacesShader = NULL) {
-    // TODO: SkXfermode should have a virtual isOpaque method, which would
-    // make it possible to test modes that do not have a Coeff representation.
-
-    if (!paint) {
-        return bmpReplacesShader ? bmpReplacesShader->isOpaque() : true;
+bool shouldDrawImmediately(const SkBitmap* bitmap, const SkPaint* paint,
+                           size_t bitmapSizeThreshold) {
+    if (bitmap && ((bitmap->getTexture() && !bitmap->isImmutable()) ||
+        (bitmap->getSize() > bitmapSizeThreshold))) {
+        return true;
     }
-
-    SkXfermode::Coeff srcCoeff, dstCoeff;
-    if (SkXfermode::AsCoeff(paint->getXfermode(), &srcCoeff, &dstCoeff)){
-        switch (dstCoeff) {
-        case SkXfermode::kZero_Coeff:
-            return true;
-        case SkXfermode::kISA_Coeff:
-            if (paint->getAlpha() != 255) {
-                break;
+    if (paint) {
+        SkShader* shader = paint->getShader();
+        // Here we detect the case where the shader is an SkBitmapProcShader
+        // with a gpu texture attached.  Checking this without RTTI
+        // requires making the assumption that only gradient shaders
+        // and SkBitmapProcShader implement asABitmap().  The following
+        // code may need to be revised if that assumption is ever broken.
+        if (shader && !shader->asAGradient(NULL)) {
+            SkBitmap bm;
+            if (shader->asABitmap(&bm, NULL, NULL) &&
+                NULL != bm.getTexture()) {
+                return true;
             }
-            if (bmpReplacesShader) {
-                if (!bmpReplacesShader->isOpaque()) {
-                    break;
-                }
-            } else if (paint->getShader() && !paint->getShader()->isOpaque()) {
-                break;
-            }
-            if (paint->getColorFilter() && 
-                ((paint->getColorFilter()->getFlags() &
-                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
-                break;
-            }
-            return true;
-        case SkXfermode::kSA_Coeff:
-            if (paint->getAlpha() != 0) {
-                break;
-            }
-            if (paint->getColorFilter() && 
-                ((paint->getColorFilter()->getFlags() &
-                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
-                break;
-            }
-            return true;
-        case SkXfermode::kSC_Coeff:
-            if (paint->getColor() != 0) { // all components must be 0
-                break;
-            }
-            if (bmpReplacesShader || paint->getShader()) {
-                break;
-            }
-            if (paint->getColorFilter() && (
-                (paint->getColorFilter()->getFlags() &
-                SkColorFilter::kAlphaUnchanged_Flag) == 0)) {
-                break;
-            }
-            return true;
-        default:
-            break;
         }
     }
     return false;
 }
+}
 
-} // unnamed namespace
+//-----------------------------------------------------------------------------
+// DeferredPipeController
+//-----------------------------------------------------------------------------
+
+class DeferredPipeController : public SkGPipeController {
+public:
+    DeferredPipeController();
+    void setPlaybackCanvas(SkCanvas*);
+    virtual ~DeferredPipeController();
+    virtual void* requestBlock(size_t minRequest, size_t* actual) SK_OVERRIDE;
+    virtual void notifyWritten(size_t bytes) SK_OVERRIDE;
+    void playback(bool silent);
+    bool hasPendingCommands() const { return fAllocator.blockCount() != 0; }
+    size_t storageAllocatedForRecording() const { return fAllocator.totalCapacity(); }
+private:
+    enum {
+        kMinBlockSize = 4096
+    };
+    struct PipeBlock {
+        PipeBlock(void* block, size_t size) { fBlock = block, fSize = size; }
+        void* fBlock;
+        size_t fSize;
+    };
+    void* fBlock;
+    size_t fBytesWritten;
+    SkChunkAlloc fAllocator;
+    SkTDArray<PipeBlock> fBlockList;
+    SkGPipeReader fReader;
+};
+
+DeferredPipeController::DeferredPipeController() :
+    fAllocator(kMinBlockSize) {
+    fBlock = NULL;
+    fBytesWritten = 0;
+}
+
+DeferredPipeController::~DeferredPipeController() {
+    fAllocator.reset();
+}
+
+void DeferredPipeController::setPlaybackCanvas(SkCanvas* canvas) {
+    fReader.setCanvas(canvas);
+}
+
+void* DeferredPipeController::requestBlock(size_t minRequest, size_t *actual) {
+    if (fBlock) {
+        // Save the previous block for later
+        PipeBlock previousBloc(fBlock, fBytesWritten);
+        fBlockList.push(previousBloc);
+    }
+    int32_t blockSize = SkMax32(minRequest, kMinBlockSize);
+    fBlock = fAllocator.allocThrow(blockSize);
+    fBytesWritten = 0;
+    *actual = blockSize;
+    return fBlock;
+}
+
+void DeferredPipeController::notifyWritten(size_t bytes) {
+    fBytesWritten += bytes;
+}
+
+void DeferredPipeController::playback(bool silent) {
+    uint32_t flags = silent ? SkGPipeReader::kSilent_PlaybackFlag : 0;
+    for (int currentBlock = 0; currentBlock < fBlockList.count(); currentBlock++ ) {
+        fReader.playback(fBlockList[currentBlock].fBlock, fBlockList[currentBlock].fSize,
+                         flags);
+    }
+    fBlockList.reset();
+
+    if (fBlock) {
+        fReader.playback(fBlock, fBytesWritten, flags);
+        fBlock = NULL;
+    }
+
+    // Release all allocated blocks
+    fAllocator.reset();
+}
+
+//-----------------------------------------------------------------------------
+// DeferredDevice
+//-----------------------------------------------------------------------------
+class DeferredDevice : public SkDevice {
+public:
+    DeferredDevice(SkDevice* immediateDevice,
+        SkDeferredCanvas::NotificationClient* notificationClient = NULL);
+    ~DeferredDevice();
+
+    void setNotificationClient(SkDeferredCanvas::NotificationClient* notificationClient);
+    SkCanvas* recordingCanvas();
+    SkCanvas* immediateCanvas() const {return fImmediateCanvas;}
+    SkDevice* immediateDevice() const {return fImmediateDevice;}
+    bool isFreshFrame();
+    bool hasPendingCommands();
+    size_t storageAllocatedForRecording() const;
+    size_t freeMemoryIfPossible(size_t bytesToFree);
+    size_t getBitmapSizeThreshold() const;
+    void setBitmapSizeThreshold(size_t sizeThreshold);
+    void flushPendingCommands(PlaybackMode);
+    void skipPendingCommands();
+    void setMaxRecordingStorage(size_t);
+    void recordedDrawCommand();
+
+    virtual uint32_t getDeviceCapabilities() SK_OVERRIDE;
+    virtual int width() const SK_OVERRIDE;
+    virtual int height() const SK_OVERRIDE;
+    virtual SkGpuRenderTarget* accessRenderTarget() SK_OVERRIDE;
+
+    virtual SkDevice* onCreateCompatibleDevice(SkBitmap::Config config,
+                                               int width, int height,
+                                               bool isOpaque,
+                                               Usage usage) SK_OVERRIDE;
+
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+                                SkCanvas::Config8888 config8888) SK_OVERRIDE;
+
+protected:
+    virtual const SkBitmap& onAccessBitmap(SkBitmap*) SK_OVERRIDE;
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                                int x, int y,
+                                SkCanvas::Config8888 config8888) SK_OVERRIDE;
+
+    // The following methods are no-ops on a deferred device
+    virtual bool filterTextFlags(const SkPaint& paint, TextFlags*)
+        SK_OVERRIDE
+        {return false;}
+
+    // None of the following drawing methods should ever get called on the
+    // deferred device
+    virtual void clear(SkColor color)
+        {SkASSERT(0);}
+    virtual void drawPaint(const SkDraw&, const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode,
+                            size_t count, const SkPoint[],
+                            const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawRect(const SkDraw&, const SkRect& r,
+                            const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawPath(const SkDraw&, const SkPath& path,
+                            const SkPaint& paint,
+                            const SkMatrix* prePathMatrix = NULL,
+                            bool pathIsMutable = false)
+        {SkASSERT(0);}
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            const SkIRect* srcRectOrNull,
+                            const SkMatrix& matrix, const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                            int x, int y, const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawText(const SkDraw&, const void* text, size_t len,
+                            SkScalar x, SkScalar y, const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                                const SkScalar pos[], SkScalar constY,
+                                int scalarsPerPos, const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawTextOnPath(const SkDraw&, const void* text,
+                                size_t len, const SkPath& path,
+                                const SkMatrix* matrix,
+                                const SkPaint& paint)
+        {SkASSERT(0);}
+    virtual void drawPosTextOnPath(const SkDraw& draw, const void* text,
+                                    size_t len, const SkPoint pos[],
+                                    const SkPaint& paint,
+                                    const SkPath& path,
+                                    const SkMatrix* matrix)
+        {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)
+        {SkASSERT(0);}
+    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+                            const SkPaint&)
+        {SkASSERT(0);}
+private:
+    virtual void flush();
+
+    void beginRecording();
+
+    DeferredPipeController fPipeController;
+    SkGPipeWriter  fPipeWriter;
+    SkDevice* fImmediateDevice;
+    SkCanvas* fImmediateCanvas;
+    SkCanvas* fRecordingCanvas;
+    SkDeferredCanvas::NotificationClient* fNotificationClient;
+    bool fFreshFrame;
+    size_t fMaxRecordingStorageBytes;
+    size_t fPreviousStorageAllocated;
+    size_t fBitmapSizeThreshold;
+};
+
+DeferredDevice::DeferredDevice(
+    SkDevice* immediateDevice, SkDeferredCanvas::NotificationClient* notificationClient) :
+    SkDevice(SkBitmap::kNo_Config,
+             immediateDevice->width(), immediateDevice->height(),
+             immediateDevice->isOpaque(),
+             immediateDevice->getDeviceProperties())
+    , fRecordingCanvas(NULL)
+    , fFreshFrame(true)
+    , fPreviousStorageAllocated(0)
+    , fBitmapSizeThreshold(kDeferredCanvasBitmapSizeThreshold){
+
+    fMaxRecordingStorageBytes = kDefaultMaxRecordingStorageBytes;
+    fNotificationClient = notificationClient;
+    fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
+    fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
+    fPipeController.setPlaybackCanvas(fImmediateCanvas);
+    this->beginRecording();
+}
+
+DeferredDevice::~DeferredDevice() {
+    this->flushPendingCommands(kSilent_PlaybackMode);
+    SkSafeUnref(fImmediateCanvas);
+}
+
+void DeferredDevice::setMaxRecordingStorage(size_t maxStorage) {
+    fMaxRecordingStorageBytes = maxStorage;
+    this->recordingCanvas(); // Accessing the recording canvas applies the new limit.
+}
+
+void DeferredDevice::beginRecording() {
+    SkASSERT(NULL == fRecordingCanvas);
+    fRecordingCanvas = fPipeWriter.startRecording(&fPipeController, 0,
+        fImmediateDevice->width(), fImmediateDevice->height());
+}
+
+void DeferredDevice::setNotificationClient(
+    SkDeferredCanvas::NotificationClient* notificationClient) {
+    fNotificationClient = notificationClient;
+}
+
+void DeferredDevice::skipPendingCommands() {
+    if (!fRecordingCanvas->isDrawingToLayer() && fPipeController.hasPendingCommands()) {
+        fFreshFrame = true;
+        flushPendingCommands(kSilent_PlaybackMode);
+        if (fNotificationClient) {
+            fNotificationClient->skippedPendingDrawCommands();
+        }
+    }
+}
+
+bool DeferredDevice::isFreshFrame() {
+    bool ret = fFreshFrame;
+    fFreshFrame = false;
+    return ret;
+}
+
+bool DeferredDevice::hasPendingCommands() {
+    return fPipeController.hasPendingCommands();
+}
+
+void DeferredDevice::flushPendingCommands(PlaybackMode playbackMode) {
+    if (!fPipeController.hasPendingCommands()) {
+        return;
+    }
+    if (playbackMode == kNormal_PlaybackMode && fNotificationClient) {
+        fNotificationClient->prepareForDraw();
+    }
+    fPipeWriter.flushRecording(true);
+    fPipeController.playback(kSilent_PlaybackMode == playbackMode);
+    if (playbackMode == kNormal_PlaybackMode && fNotificationClient) {
+        fNotificationClient->flushedDrawCommands();
+    }
+    fPreviousStorageAllocated = storageAllocatedForRecording();
+}
+
+void DeferredDevice::flush() {
+    this->flushPendingCommands(kNormal_PlaybackMode);
+    fImmediateCanvas->flush();
+}
+
+size_t DeferredDevice::freeMemoryIfPossible(size_t bytesToFree) {
+    size_t val = fPipeWriter.freeMemoryIfPossible(bytesToFree);
+    fPreviousStorageAllocated = storageAllocatedForRecording();
+    return val;
+}
+
+size_t DeferredDevice::getBitmapSizeThreshold() const {
+    return fBitmapSizeThreshold;
+}
+
+void DeferredDevice::setBitmapSizeThreshold(size_t sizeThreshold) {
+    fBitmapSizeThreshold = sizeThreshold;
+}
+
+size_t DeferredDevice::storageAllocatedForRecording() const {
+    return (fPipeController.storageAllocatedForRecording()
+            + fPipeWriter.storageAllocatedForRecording());
+}
+
+void DeferredDevice::recordedDrawCommand() {
+    size_t storageAllocated = this->storageAllocatedForRecording();
+
+    if (storageAllocated > fMaxRecordingStorageBytes) {
+        // First, attempt to reduce cache without flushing
+        size_t tryFree = storageAllocated - fMaxRecordingStorageBytes;
+        if (this->freeMemoryIfPossible(tryFree) < tryFree) {
+            // Flush is necessary to free more space.
+            this->flushPendingCommands(kNormal_PlaybackMode);
+            // Free as much as possible to avoid oscillating around fMaxRecordingStorageBytes
+            // which could cause a high flushing frequency.
+            this->freeMemoryIfPossible(~0U);
+        }
+        storageAllocated = this->storageAllocatedForRecording();
+    }
+
+    if (fNotificationClient &&
+        storageAllocated != fPreviousStorageAllocated) {
+        fPreviousStorageAllocated = storageAllocated;
+        fNotificationClient->storageAllocatedForRecordingChanged(storageAllocated);
+    }
+}
+
+SkCanvas* DeferredDevice::recordingCanvas() {
+    return fRecordingCanvas;
+}
+
+uint32_t DeferredDevice::getDeviceCapabilities() {
+    return fImmediateDevice->getDeviceCapabilities();
+}
+
+int DeferredDevice::width() const {
+    return fImmediateDevice->width();
+}
+
+int DeferredDevice::height() const {
+    return fImmediateDevice->height();
+}
+
+SkGpuRenderTarget* DeferredDevice::accessRenderTarget() {
+    this->flushPendingCommands(kNormal_PlaybackMode);
+    return fImmediateDevice->accessRenderTarget();
+}
+
+void DeferredDevice::writePixels(const SkBitmap& bitmap,
+    int x, int y, SkCanvas::Config8888 config8888) {
+
+    if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
+        (y + bitmap.height()) >= height()) {
+        this->skipPendingCommands();
+    }
+
+    if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
+        SkCanvas::kNative_Premul_Config8888 != config8888 &&
+        kPMColorAlias != config8888) {
+        //Special case config: no deferral
+        this->flushPendingCommands(kNormal_PlaybackMode);
+        fImmediateDevice->writePixels(bitmap, x, y, config8888);
+        return;
+    }
+
+    SkPaint paint;
+    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    if (shouldDrawImmediately(&bitmap, NULL, getBitmapSizeThreshold())) {
+        this->flushPendingCommands(kNormal_PlaybackMode);
+        fImmediateCanvas->drawSprite(bitmap, x, y, &paint);
+    } else {
+        this->recordingCanvas()->drawSprite(bitmap, x, y, &paint);
+        this->recordedDrawCommand();
+
+    }
+}
+
+const SkBitmap& DeferredDevice::onAccessBitmap(SkBitmap*) {
+    this->flushPendingCommands(kNormal_PlaybackMode);
+    return fImmediateDevice->accessBitmap(false);
+}
+
+SkDevice* DeferredDevice::onCreateCompatibleDevice(
+    SkBitmap::Config config, int width, int height, bool isOpaque,
+    Usage usage) {
+
+    // Save layer usage not supported, and not required by SkDeferredCanvas.
+    SkASSERT(usage != kSaveLayer_Usage);
+    // Create a compatible non-deferred device.
+    SkAutoTUnref<SkDevice> compatibleDevice
+        (fImmediateDevice->createCompatibleDevice(config, width, height,
+            isOpaque));
+    return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fNotificationClient));
+}
+
+bool DeferredDevice::onReadPixels(
+    const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
+    this->flushPendingCommands(kNormal_PlaybackMode);
+    return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
+                                                   x, y, config8888);
+}
+
+class AutoImmediateDrawIfNeeded {
+public:
+    AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkBitmap* bitmap,
+                              const SkPaint* paint) {
+        this->init(canvas, bitmap, paint);
+    }
+
+    AutoImmediateDrawIfNeeded(SkDeferredCanvas& canvas, const SkPaint* paint) {
+        this->init(canvas, NULL, paint);
+    }
+
+    ~AutoImmediateDrawIfNeeded() {
+        if (fCanvas) {
+            fCanvas->setDeferredDrawing(true);
+        }
+    }
+private:
+    void init(SkDeferredCanvas& canvas, const SkBitmap* bitmap, const SkPaint* paint)
+    {
+        DeferredDevice* device = static_cast<DeferredDevice*>(canvas.getDevice());
+        if (canvas.isDeferredDrawing() && (NULL != device) &&
+            shouldDrawImmediately(bitmap, paint, device->getBitmapSizeThreshold())) {
+            canvas.setDeferredDrawing(false);
+            fCanvas = &canvas;
+        } else {
+            fCanvas = NULL;
+        }
+    }
+
+    SkDeferredCanvas* fCanvas;
+};
 
 SkDeferredCanvas::SkDeferredCanvas() {
-    init();
+    this->init();
 }
 
 SkDeferredCanvas::SkDeferredCanvas(SkDevice* device) {
-    init();
-    setDevice(device);
-}
-
-SkDeferredCanvas::SkDeferredCanvas(SkDevice* device, 
-                                   DeviceContext* deviceContext) {
-    init();
-    setDevice(device);
-    setDeviceContext(deviceContext);
+    this->init();
+    this->setDevice(device);
 }
 
 void SkDeferredCanvas::init() {
     fDeferredDrawing = true; // On by default
 }
 
-void SkDeferredCanvas::validate() const {
-    SkASSERT(getDevice());
+void SkDeferredCanvas::setMaxRecordingStorage(size_t maxStorage) {
+    this->validate();
+    this->getDeferredDevice()->setMaxRecordingStorage(maxStorage);
 }
 
-SkCanvas* SkDeferredCanvas::drawingCanvas() const {
-    validate();
-    return fDeferredDrawing ? getDeferredDevice()->recordingCanvas() :
-        getDeferredDevice()->immediateCanvas();
+size_t SkDeferredCanvas::storageAllocatedForRecording() const {
+    return this->getDeferredDevice()->storageAllocatedForRecording();
 }
 
-void SkDeferredCanvas::flushIfNeeded(const SkBitmap& bitmap) {
-    validate();
+size_t SkDeferredCanvas::freeMemoryIfPossible(size_t bytesToFree) {
+    return this->getDeferredDevice()->freeMemoryIfPossible(bytesToFree);
+}
+
+void SkDeferredCanvas::setBitmapSizeThreshold(size_t sizeThreshold) {
+    DeferredDevice* deferredDevice = this->getDeferredDevice();
+    SkASSERT(deferredDevice);
+    deferredDevice->setBitmapSizeThreshold(sizeThreshold);
+}
+
+void SkDeferredCanvas::recordedDrawCommand() {
     if (fDeferredDrawing) {
-        getDeferredDevice()->flushIfNeeded(bitmap);
+        this->getDeferredDevice()->recordedDrawCommand();
     }
 }
 
-SkDeferredCanvas::DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
-    return static_cast<SkDeferredCanvas::DeferredDevice*>(getDevice());
+void SkDeferredCanvas::validate() const {
+    SkASSERT(this->getDevice());
+}
+
+SkCanvas* SkDeferredCanvas::drawingCanvas() const {
+    this->validate();
+    return fDeferredDrawing ? this->getDeferredDevice()->recordingCanvas() :
+        this->getDeferredDevice()->immediateCanvas();
+}
+
+SkCanvas* SkDeferredCanvas::immediateCanvas() const {
+    this->validate();
+    return this->getDeferredDevice()->immediateCanvas();
+}
+
+DeferredDevice* SkDeferredCanvas::getDeferredDevice() const {
+    return static_cast<DeferredDevice*>(this->getDevice());
 }
 
 void SkDeferredCanvas::setDeferredDrawing(bool val) {
-    validate(); // Must set device before calling this method
-    SkASSERT(drawingCanvas()->getSaveCount() == 1);
+    this->validate(); // Must set device before calling this method
     if (val != fDeferredDrawing) {
         if (fDeferredDrawing) {
             // Going live.
-            getDeferredDevice()->flushPending();
+            this->getDeferredDevice()->flushPendingCommands(kNormal_PlaybackMode);
         }
         fDeferredDrawing = val;
     }
 }
 
+bool SkDeferredCanvas::isDeferredDrawing() const {
+    return fDeferredDrawing;
+}
+
+bool SkDeferredCanvas::isFreshFrame() const {
+    return this->getDeferredDevice()->isFreshFrame();
+}
+
+bool SkDeferredCanvas::hasPendingCommands() const {
+    return this->getDeferredDevice()->hasPendingCommands();
+}
+
+void SkDeferredCanvas::silentFlush() {
+    if (fDeferredDrawing) {
+        this->getDeferredDevice()->flushPendingCommands(kSilent_PlaybackMode);
+    }
+}
+
 SkDeferredCanvas::~SkDeferredCanvas() {
 }
 
 SkDevice* SkDeferredCanvas::setDevice(SkDevice* device) {
-    INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
+    this->INHERITED::setDevice(SkNEW_ARGS(DeferredDevice, (device)))->unref();
     return device;
 }
 
-SkDeferredCanvas::DeviceContext* SkDeferredCanvas::setDeviceContext(
-    DeviceContext* deviceContext) {
+SkDeferredCanvas::NotificationClient* SkDeferredCanvas::setNotificationClient(
+    NotificationClient* notificationClient) {
 
-    DeferredDevice* deferredDevice = getDeferredDevice();
+    DeferredDevice* deferredDevice = this->getDeferredDevice();
     SkASSERT(deferredDevice);
     if (deferredDevice) {
-        deferredDevice->setDeviceContext(deviceContext);
+        deferredDevice->setNotificationClient(notificationClient);
     }
-    return deviceContext;
+    return notificationClient;
 }
 
 bool SkDeferredCanvas::isFullFrame(const SkRect* rect,
                                    const SkPaint* paint) const {
-    SkCanvas* canvas = drawingCanvas();
-    SkISize canvasSize = getDeviceSize();
+    SkCanvas* canvas = this->drawingCanvas();
+    SkISize canvasSize = this->getDeviceSize();
     if (rect) {
         if (!canvas->getTotalMatrix().rectStaysRect()) {
             return false; // conservative
@@ -164,7 +598,7 @@
 
         if (paint) {
             SkPaint::Style paintStyle = paint->getStyle();
-            if (!(paintStyle == SkPaint::kFill_Style || 
+            if (!(paintStyle == SkPaint::kFill_Style ||
                 paintStyle == SkPaint::kStrokeAndFill_Style)) {
                 return false;
             }
@@ -176,170 +610,212 @@
 
         // The following test holds with AA enabled, and is conservative
         // by a 0.5 pixel margin with AA disabled
-        if (transformedRect.fLeft > SkIntToScalar(0) || 
-            transformedRect.fTop > SkIntToScalar(0) || 
+        if (transformedRect.fLeft > SkIntToScalar(0) ||
+            transformedRect.fTop > SkIntToScalar(0) ||
             transformedRect.fRight < SkIntToScalar(canvasSize.fWidth) ||
             transformedRect.fBottom < SkIntToScalar(canvasSize.fHeight)) {
             return false;
         }
     }
 
-    switch (canvas->getClipType()) {
-        case SkCanvas::kRect_ClipType :
-            {
-                SkIRect bounds;
-                canvas->getClipDeviceBounds(&bounds);
-                if (bounds.fLeft > 0 || bounds.fTop > 0 || 
-                    bounds.fRight < canvasSize.fWidth || 
-                    bounds.fBottom < canvasSize.fHeight)
-                    return false;
-            }
-            break;
-        case SkCanvas::kComplex_ClipType :
-            return false; // conservative
-        case SkCanvas::kEmpty_ClipType:
-        default:
-            break;
-    };
-
-    return true;
+    return this->getClipStack()->quickContains(SkRect::MakeXYWH(0, 0,
+        SkIntToScalar(canvasSize.fWidth), SkIntToScalar(canvasSize.fHeight)));
 }
 
 int SkDeferredCanvas::save(SaveFlags flags) {
-    drawingCanvas()->save(flags);
-    return this->INHERITED::save(flags);
+    this->drawingCanvas()->save(flags);
+    int val = this->INHERITED::save(flags);
+    this->recordedDrawCommand();
+
+    return val;
 }
 
 int SkDeferredCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
                                 SaveFlags flags) {
-    drawingCanvas()->saveLayer(bounds, paint, flags);
+    this->drawingCanvas()->saveLayer(bounds, paint, flags);
     int count = this->INHERITED::save(flags);
     this->clipRectBounds(bounds, flags, NULL);
+    this->recordedDrawCommand();
+
     return count;
 }
 
 void SkDeferredCanvas::restore() {
-    drawingCanvas()->restore();
+    this->drawingCanvas()->restore();
     this->INHERITED::restore();
+    this->recordedDrawCommand();
 }
 
 bool SkDeferredCanvas::isDrawingToLayer() const {
-    return drawingCanvas()->isDrawingToLayer();
+    return this->drawingCanvas()->isDrawingToLayer();
 }
 
 bool SkDeferredCanvas::translate(SkScalar dx, SkScalar dy) {
-    drawingCanvas()->translate(dx, dy);
-    return this->INHERITED::translate(dx, dy);
+    this->drawingCanvas()->translate(dx, dy);
+    bool val = this->INHERITED::translate(dx, dy);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::scale(SkScalar sx, SkScalar sy) {
-    drawingCanvas()->scale(sx, sy);
-    return this->INHERITED::scale(sx, sy);
+    this->drawingCanvas()->scale(sx, sy);
+    bool val = this->INHERITED::scale(sx, sy);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::rotate(SkScalar degrees) {
-    drawingCanvas()->rotate(degrees);
-    return this->INHERITED::rotate(degrees);
+    this->drawingCanvas()->rotate(degrees);
+    bool val = this->INHERITED::rotate(degrees);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::skew(SkScalar sx, SkScalar sy) {
-    drawingCanvas()->skew(sx, sy);
-    return this->INHERITED::skew(sx, sy);
+    this->drawingCanvas()->skew(sx, sy);
+    bool val = this->INHERITED::skew(sx, sy);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::concat(const SkMatrix& matrix) {
-    drawingCanvas()->concat(matrix);
-    return this->INHERITED::concat(matrix);
+    this->drawingCanvas()->concat(matrix);
+    bool val = this->INHERITED::concat(matrix);
+    this->recordedDrawCommand();
+    return val;
 }
 
 void SkDeferredCanvas::setMatrix(const SkMatrix& matrix) {
-    drawingCanvas()->setMatrix(matrix);
+    this->drawingCanvas()->setMatrix(matrix);
     this->INHERITED::setMatrix(matrix);
+    this->recordedDrawCommand();
 }
 
 bool SkDeferredCanvas::clipRect(const SkRect& rect,
                                 SkRegion::Op op,
                                 bool doAntiAlias) {
-    drawingCanvas()->clipRect(rect, op, doAntiAlias);
-    return this->INHERITED::clipRect(rect, op, doAntiAlias);
+    this->drawingCanvas()->clipRect(rect, op, doAntiAlias);
+    bool val = this->INHERITED::clipRect(rect, op, doAntiAlias);
+    this->recordedDrawCommand();
+    return val;
+}
+
+bool SkDeferredCanvas::clipRRect(const SkRRect& rrect,
+                                 SkRegion::Op op,
+                                 bool doAntiAlias) {
+    this->drawingCanvas()->clipRRect(rrect, op, doAntiAlias);
+    bool val = this->INHERITED::clipRRect(rrect, op, doAntiAlias);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::clipPath(const SkPath& path,
                                 SkRegion::Op op,
                                 bool doAntiAlias) {
-    drawingCanvas()->clipPath(path, op, doAntiAlias);
-    return this->INHERITED::clipPath(path, op, doAntiAlias);
+    this->drawingCanvas()->clipPath(path, op, doAntiAlias);
+    bool val = this->INHERITED::clipPath(path, op, doAntiAlias);
+    this->recordedDrawCommand();
+    return val;
 }
 
 bool SkDeferredCanvas::clipRegion(const SkRegion& deviceRgn,
                                   SkRegion::Op op) {
-    drawingCanvas()->clipRegion(deviceRgn, op);
-    return this->INHERITED::clipRegion(deviceRgn, op);
+    this->drawingCanvas()->clipRegion(deviceRgn, op);
+    bool val = this->INHERITED::clipRegion(deviceRgn, op);
+    this->recordedDrawCommand();
+    return val;
 }
 
 void SkDeferredCanvas::clear(SkColor color) {
     // purge pending commands
     if (fDeferredDrawing) {
-        getDeferredDevice()->contentsCleared();
+        this->getDeferredDevice()->skipPendingCommands();
     }
 
-    drawingCanvas()->clear(color);
+    this->drawingCanvas()->clear(color);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPaint(const SkPaint& paint) {
-    if (fDeferredDrawing && isFullFrame(NULL, &paint) && 
+    if (fDeferredDrawing && this->isFullFrame(NULL, &paint) &&
         isPaintOpaque(&paint)) {
-        getDeferredDevice()->contentsCleared();
+        this->getDeferredDevice()->skipPendingCommands();
     }
-
-    drawingCanvas()->drawPaint(paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawPaint(paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPoints(PointMode mode, size_t count,
                                   const SkPoint pts[], const SkPaint& paint) {
-    drawingCanvas()->drawPoints(mode, count, pts, paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawPoints(mode, count, pts, paint);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawOval(const SkRect& rect, const SkPaint& paint) {
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawOval(rect, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
-    if (fDeferredDrawing && isFullFrame(&rect, &paint) && 
+    if (fDeferredDrawing && this->isFullFrame(&rect, &paint) &&
         isPaintOpaque(&paint)) {
-        getDeferredDevice()->contentsCleared();
+        this->getDeferredDevice()->skipPendingCommands();
     }
 
-    drawingCanvas()->drawRect(rect, paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawRect(rect, paint);
+    this->recordedDrawCommand();
+}
+
+void SkDeferredCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    if (rrect.isRect()) {
+        this->SkDeferredCanvas::drawRect(rrect.getBounds(), paint);
+    } else if (rrect.isOval()) {
+        this->SkDeferredCanvas::drawOval(rrect.getBounds(), paint);
+    } else {
+        AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+        this->drawingCanvas()->drawRRect(rrect, paint);
+        this->recordedDrawCommand();
+    }
 }
 
 void SkDeferredCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
-    drawingCanvas()->drawPath(path, paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawPath(path, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar left,
                                   SkScalar top, const SkPaint* paint) {
     SkRect bitmapRect = SkRect::MakeXYWH(left, top,
         SkIntToScalar(bitmap.width()), SkIntToScalar(bitmap.height()));
-    if (fDeferredDrawing && 
-        isFullFrame(&bitmapRect, paint) &&
+    if (fDeferredDrawing &&
+        this->isFullFrame(&bitmapRect, paint) &&
         isPaintOpaque(paint, &bitmap)) {
-        getDeferredDevice()->contentsCleared();
+        this->getDeferredDevice()->skipPendingCommands();
     }
 
-    drawingCanvas()->drawBitmap(bitmap, left, top, paint);
-    flushIfNeeded(bitmap);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawBitmap(bitmap, left, top, paint);
+    this->recordedDrawCommand();
 }
 
-void SkDeferredCanvas::drawBitmapRect(const SkBitmap& bitmap, 
-                                      const SkIRect* src,
-                                      const SkRect& dst,
-                                      const SkPaint* paint) {
-    if (fDeferredDrawing && 
-        isFullFrame(&dst, paint) &&
+void SkDeferredCanvas::drawBitmapRectToRect(const SkBitmap& bitmap,
+                                            const SkRect* src,
+                                            const SkRect& dst,
+                                            const SkPaint* paint) {
+    if (fDeferredDrawing &&
+        this->isFullFrame(&dst, paint) &&
         isPaintOpaque(paint, &bitmap)) {
-        getDeferredDevice()->contentsCleared();
+        this->getDeferredDevice()->skipPendingCommands();
     }
 
-    drawingCanvas()->drawBitmapRect(bitmap, src,
-                                    dst, paint);
-    flushIfNeeded(bitmap);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawBitmapRectToRect(bitmap, src, dst, paint);
+    this->recordedDrawCommand();
 }
 
 
@@ -348,8 +824,9 @@
                                         const SkPaint* paint) {
     // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
     // covers canvas entirely and transformed bitmap covers canvas entirely
-    drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
-    flushIfNeeded(bitmap);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawBitmapMatrix(bitmap, m, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawBitmapNine(const SkBitmap& bitmap,
@@ -357,56 +834,63 @@
                                       const SkPaint* paint) {
     // TODO: reset recording canvas if paint+bitmap is opaque and clip rect
     // covers canvas entirely and dst covers canvas entirely
-    drawingCanvas()->drawBitmapNine(bitmap, center,
-                                    dst, paint);
-    flushIfNeeded(bitmap);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawBitmapNine(bitmap, center, dst, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawSprite(const SkBitmap& bitmap, int left, int top,
                                   const SkPaint* paint) {
     SkRect bitmapRect = SkRect::MakeXYWH(
         SkIntToScalar(left),
-        SkIntToScalar(top), 
+        SkIntToScalar(top),
         SkIntToScalar(bitmap.width()),
         SkIntToScalar(bitmap.height()));
-    if (fDeferredDrawing && 
-        isFullFrame(&bitmapRect, paint) &&
+    if (fDeferredDrawing &&
+        this->isFullFrame(&bitmapRect, paint) &&
         isPaintOpaque(paint, &bitmap)) {
-        getDeferredDevice()->contentsCleared();
+        this->getDeferredDevice()->skipPendingCommands();
     }
 
-    drawingCanvas()->drawSprite(bitmap, left, top,
-                                paint);
-    flushIfNeeded(bitmap);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &bitmap, paint);
+    this->drawingCanvas()->drawSprite(bitmap, left, top, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawText(const void* text, size_t byteLength,
                                 SkScalar x, SkScalar y, const SkPaint& paint) {
-    drawingCanvas()->drawText(text, byteLength, x, y, paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawText(text, byteLength, x, y, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPosText(const void* text, size_t byteLength,
                                    const SkPoint pos[], const SkPaint& paint) {
-    drawingCanvas()->drawPosText(text, byteLength, pos, paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawPosText(text, byteLength, pos, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPosTextH(const void* text, size_t byteLength,
                                     const SkScalar xpos[], SkScalar constY,
                                     const SkPaint& paint) {
-    drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawPosTextH(text, byteLength, xpos, constY, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawTextOnPath(const void* text, size_t byteLength,
                                       const SkPath& path,
                                       const SkMatrix* matrix,
                                       const SkPaint& paint) {
-    drawingCanvas()->drawTextOnPath(text, byteLength,
-                                    path, matrix,
-                                    paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawTextOnPath(text, byteLength, path, matrix, paint);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawPicture(SkPicture& picture) {
-    drawingCanvas()->drawPicture(picture);
+    this->drawingCanvas()->drawPicture(picture);
+    this->recordedDrawCommand();
 }
 
 void SkDeferredCanvas::drawVertices(VertexMode vmode, int vertexCount,
@@ -415,181 +899,26 @@
                                     const SkColor colors[], SkXfermode* xmode,
                                     const uint16_t indices[], int indexCount,
                                     const SkPaint& paint) {
-    drawingCanvas()->drawVertices(vmode, vertexCount,
-                                  vertices, texs,
-                                  colors, xmode,
-                                  indices, indexCount,
-                                  paint);
+    AutoImmediateDrawIfNeeded autoDraw(*this, &paint);
+    this->drawingCanvas()->drawVertices(vmode, vertexCount, vertices, texs, colors, xmode,
+                                        indices, indexCount, paint);
+    this->recordedDrawCommand();
 }
 
 SkBounder* SkDeferredCanvas::setBounder(SkBounder* bounder) {
-    drawingCanvas()->setBounder(bounder);
-    return INHERITED::setBounder(bounder);
+    this->drawingCanvas()->setBounder(bounder);
+    this->INHERITED::setBounder(bounder);
+    this->recordedDrawCommand();
+    return bounder;
 }
 
 SkDrawFilter* SkDeferredCanvas::setDrawFilter(SkDrawFilter* filter) {
-    drawingCanvas()->setDrawFilter(filter); 
-    return INHERITED::setDrawFilter(filter);
+    this->drawingCanvas()->setDrawFilter(filter);
+    this->INHERITED::setDrawFilter(filter);
+    this->recordedDrawCommand();
+    return filter;
 }
 
 SkCanvas* SkDeferredCanvas::canvasForDrawIter() {
-    return drawingCanvas();
-}
-
-// SkDeferredCanvas::DeferredDevice
-//------------------------------------
-
-SkDeferredCanvas::DeferredDevice::DeferredDevice(
-    SkDevice* immediateDevice, DeviceContext* deviceContext) :
-    SkDevice(SkBitmap::kNo_Config, immediateDevice->width(),
-             immediateDevice->height(), immediateDevice->isOpaque())
-    , fFreshFrame(true) {
-
-    fDeviceContext = deviceContext;
-    SkSafeRef(fDeviceContext);
-    fImmediateDevice = immediateDevice; // ref counted via fImmediateCanvas
-    fImmediateCanvas = SkNEW_ARGS(SkCanvas, (fImmediateDevice));
-    fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(),
-        fImmediateDevice->height(), 0);
-}
-
-SkDeferredCanvas::DeferredDevice::~DeferredDevice() {
-    SkSafeUnref(fImmediateCanvas);
-    SkSafeUnref(fDeviceContext);
-}
-    
-void SkDeferredCanvas::DeferredDevice::setDeviceContext(
-    DeviceContext* deviceContext) {
-    SkRefCnt_SafeAssign(fDeviceContext, deviceContext);
-}
-
-void SkDeferredCanvas::DeferredDevice::contentsCleared() {
-    if (!fRecordingCanvas->isDrawingToLayer()) {
-        fFreshFrame = true;
-
-        // TODO: find a way to transfer the state stack and layers
-        // to the new recording canvas.  For now, purging only works
-        // with an empty stack.
-        if (fRecordingCanvas->getSaveCount() == 0) {
-
-            // Save state that is trashed by the purge
-            SkDrawFilter* drawFilter = fRecordingCanvas->getDrawFilter();
-            SkSafeRef(drawFilter); // So that it survives the purge
-            SkMatrix matrix = fRecordingCanvas->getTotalMatrix();
-            SkRegion clipRegion = fRecordingCanvas->getTotalClip();
-
-            // beginRecording creates a new recording canvas and discards the
-            // old one, hence purging deferred draw ops.
-            fRecordingCanvas = fPicture.beginRecording(
-                fImmediateDevice->width(),
-                fImmediateDevice->height(), 0);
-
-            // Restore pre-purge state
-            if (!clipRegion.isEmpty()) {
-                fRecordingCanvas->clipRegion(clipRegion, 
-                    SkRegion::kReplace_Op);
-            }
-            if (!matrix.isIdentity()) {
-                fRecordingCanvas->setMatrix(matrix);
-            }
-            if (drawFilter) {
-                fRecordingCanvas->setDrawFilter(drawFilter)->unref();
-            }
-        }
-    }
-}
-
-bool SkDeferredCanvas::DeferredDevice::isFreshFrame() {
-    bool ret = fFreshFrame;
-    fFreshFrame = false;
-    return ret;
-}
-
-void SkDeferredCanvas::DeferredDevice::flushPending() {
-    if (fDeviceContext) {
-        fDeviceContext->prepareForDraw();
-    }
-    fPicture.draw(fImmediateCanvas);
-    fRecordingCanvas = fPicture.beginRecording(fImmediateDevice->width(), 
-        fImmediateDevice->height(), 0);
-}
-
-void SkDeferredCanvas::DeferredDevice::flush() {
-    flushPending();
-    fImmediateCanvas->flush();
-}
-
-void SkDeferredCanvas::DeferredDevice::flushIfNeeded(const SkBitmap& bitmap) {
-    if (bitmap.isImmutable()) {
-        return; // safe to deffer without registering a dependency
-    }
-
-    // For now, drawing a writable bitmap triggers a flush
-    // TODO: implement read-only semantics and auto buffer duplication on write
-    // in SkBitmap/SkPixelRef, which will make deferral possible in this case.
-    flushPending();
-}
-
-uint32_t SkDeferredCanvas::DeferredDevice::getDeviceCapabilities() { 
-    return fImmediateDevice->getDeviceCapabilities();
-}
-
-int SkDeferredCanvas::DeferredDevice::width() const { 
-    return fImmediateDevice->width();
-}
-
-int SkDeferredCanvas::DeferredDevice::height() const {
-    return fImmediateDevice->height(); 
-}
-
-SkGpuRenderTarget* SkDeferredCanvas::DeferredDevice::accessRenderTarget() {
-    flushPending();
-    return fImmediateDevice->accessRenderTarget();
-}
-
-void SkDeferredCanvas::DeferredDevice::writePixels(const SkBitmap& bitmap,
-    int x, int y, SkCanvas::Config8888 config8888) {
-
-    if (x <= 0 && y <= 0 && (x + bitmap.width()) >= width() &&
-        (y + bitmap.height()) >= height()) {
-        contentsCleared();
-    }
-
-    if (SkBitmap::kARGB_8888_Config == bitmap.config() &&
-        SkCanvas::kNative_Premul_Config8888 != config8888 &&
-        kPMColorAlias != config8888) {
-        //Special case config: no deferral
-        flushPending();
-        fImmediateDevice->writePixels(bitmap, x, y, config8888);
-    }
-
-    SkPaint paint;
-    paint.setXfermodeMode(SkXfermode::kSrc_Mode);
-    fRecordingCanvas->drawSprite(bitmap, x, y, &paint);
-    flushIfNeeded(bitmap);
-}
-
-const SkBitmap& SkDeferredCanvas::DeferredDevice::onAccessBitmap(SkBitmap*) {
-    flushPending();
-    return fImmediateDevice->accessBitmap(false);
-}
-
-SkDevice* SkDeferredCanvas::DeferredDevice::onCreateCompatibleDevice(
-    SkBitmap::Config config, int width, int height, bool isOpaque,
-    Usage usage) {
-
-    // Save layer usage not supported, and not required by SkDeferredCanvas.
-    SkASSERT(usage != kSaveLayer_Usage);
-    // Create a compatible non-deferred device.
-    SkDevice* compatibleDevice = 
-        fImmediateDevice->createCompatibleDevice(config, width, height, 
-            isOpaque);
-    return SkNEW_ARGS(DeferredDevice, (compatibleDevice, fDeviceContext));
-}
-
-bool SkDeferredCanvas::DeferredDevice::onReadPixels(
-    const SkBitmap& bitmap, int x, int y, SkCanvas::Config8888 config8888) {
-    flushPending();
-    return fImmediateCanvas->readPixels(const_cast<SkBitmap*>(&bitmap),
-                                                   x, y, config8888);
+    return this->drawingCanvas();
 }
diff --git a/src/utils/SkDumpCanvas.cpp b/src/utils/SkDumpCanvas.cpp
index c9b4a95..46c4141 100644
--- a/src/utils/SkDumpCanvas.cpp
+++ b/src/utils/SkDumpCanvas.cpp
@@ -5,9 +5,13 @@
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "SkDumpCanvas.h"
+
+#ifdef SK_DEVELOPER
 #include "SkPicture.h"
 #include "SkPixelRef.h"
+#include "SkRRect.h"
 #include "SkString.h"
 #include <stdarg.h>
 
@@ -19,21 +23,48 @@
 #include "SkPathEffect.h"
 #include "SkMaskFilter.h"
 
+SK_DEFINE_INST_COUNT(SkDumpCanvas::Dumper)
+
 static void toString(const SkRect& r, SkString* str) {
-    str->printf("[%g,%g %g:%g]",
-                SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
-                SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
+    str->appendf("[%g,%g %g:%g]",
+                 SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
+                 SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
 }
 
 static void toString(const SkIRect& r, SkString* str) {
-    str->printf("[%d,%d %d:%d]", r.fLeft, r.fTop, r.width(), r.height());
+    str->appendf("[%d,%d %d:%d]", r.fLeft, r.fTop, r.width(), r.height());
+}
+
+static void toString(const SkRRect& rrect, SkString* str) {
+    SkRect r = rrect.getBounds();
+    str->appendf("[%g,%g %g:%g]",
+                 SkScalarToFloat(r.fLeft), SkScalarToFloat(r.fTop),
+                 SkScalarToFloat(r.width()), SkScalarToFloat(r.height()));
+    if (rrect.isOval()) {
+        str->append("()");
+    } else if (rrect.isSimple()) {
+        const SkVector& rad = rrect.getSimpleRadii();
+        str->appendf("(%g,%g)", rad.x(), rad.y());
+    } else if (rrect.isComplex()) {
+        SkVector radii[4] = {
+            rrect.radii(SkRRect::kUpperLeft_Corner),
+            rrect.radii(SkRRect::kUpperRight_Corner),
+            rrect.radii(SkRRect::kLowerRight_Corner),
+            rrect.radii(SkRRect::kLowerLeft_Corner),
+        };
+        str->appendf("(%g,%g %g,%g %g,%g %g,%g)",
+                     radii[0].x(), radii[0].y(),
+                     radii[1].x(), radii[1].y(),
+                     radii[2].x(), radii[2].y(),
+                     radii[3].x(), radii[3].y());
+    }
 }
 
 static void dumpVerbs(const SkPath& path, SkString* str) {
     SkPath::Iter iter(path, false);
     SkPoint pts[4];
     for (;;) {
-        switch (iter.next(pts)) {
+        switch (iter.next(pts, false)) {
             case SkPath::kMove_Verb:
                 str->appendf(" M%g,%g", pts[0].fX, pts[0].fY);
                 break;
@@ -59,7 +90,7 @@
 
 static void toString(const SkPath& path, SkString* str) {
     if (path.isEmpty()) {
-        str->set("path:empty");
+        str->append("path:empty");
     } else {
         toString(path.getBounds(), str);
 #if 1
@@ -80,8 +111,8 @@
 }
 
 static void toString(const SkRegion& rgn, SkString* str) {
+    str->append("Region:[");
     toString(rgn.getBounds(), str);
-    str->prepend("Region:[");
     str->append("]");
     if (rgn.isComplex()) {
         str->append(".complex");
@@ -102,59 +133,46 @@
     return gPMNames[pm];
 }
 
-static const char* toString(SkBitmap::Config config) {
-    static const char* gConfigNames[] = {
-        "NONE", "A1", "A8", "INDEX8", "565", "4444", "8888", "RLE"
-    };
-    return gConfigNames[config];
-}
-
-static void toString(const SkBitmap& bm, SkString* str) {
-    str->printf("bitmap:[%d %d] %s", bm.width(), bm.height(),
-                toString(bm.config()));
-
-    SkPixelRef* pr = bm.pixelRef();
-    if (NULL == pr) {
-        // show null or the explicit pixel address (rare)
-        str->appendf(" pixels:%p", bm.getPixels());
-    } else {
-        const char* uri = pr->getURI();
-        if (uri) {
-            str->appendf(" uri:\"%s\"", uri);
-        } else {
-            str->appendf(" pixelref:%p", pr);
-        }
-    }
-}
-
-static void toString(const void* text, size_t len, SkPaint::TextEncoding enc,
+static void toString(const void* text, size_t byteLen, SkPaint::TextEncoding enc,
                      SkString* str) {
+    // FIXME: this code appears to be untested - and probably unused - and probably wrong
     switch (enc) {
         case SkPaint::kUTF8_TextEncoding:
-            str->printf("\"%.*s\"%s", SkMax32(len, 32), text,
-                        len > 32 ? "..." : "");
+            str->appendf("\"%.*s\"%s", SkMax32(byteLen, 32), (const char*) text,
+                        byteLen > 32 ? "..." : "");
             break;
         case SkPaint::kUTF16_TextEncoding:
-            str->printf("\"%.*S\"%s", SkMax32(len, 32), text,
-                        len > 64 ? "..." : "");
+            str->appendf("\"%.*ls\"%s", SkMax32(byteLen, 32), (const wchar_t*) text,
+                        byteLen > 64 ? "..." : "");
+            break;
+        case SkPaint::kUTF32_TextEncoding:
+            str->appendf("\"%.*ls\"%s", SkMax32(byteLen, 32), (const wchar_t*) text,
+                        byteLen > 128 ? "..." : "");
             break;
         case SkPaint::kGlyphID_TextEncoding:
-            str->set("<glyphs>");
+            str->append("<glyphs>");
+            break;
+
+        default:
+            SkASSERT(false);
             break;
     }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-SkDumpCanvas::SkDumpCanvas(Dumper* dumper) : fNestLevel(0) {
+static SkBitmap make_wideopen_bm() {
+    static const int WIDE_OPEN = 16384;
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kNo_Config, WIDE_OPEN, WIDE_OPEN);
+    return bm;
+}
+
+SkDumpCanvas::SkDumpCanvas(Dumper* dumper) : INHERITED(make_wideopen_bm()) {
+    fNestLevel = 0;
     SkSafeRef(dumper);
     fDumper = dumper;
-
-    static const int WIDE_OPEN = 16384;
-    SkBitmap emptyBitmap;
-
-    emptyBitmap.setConfig(SkBitmap::kNo_Config, WIDE_OPEN, WIDE_OPEN);
-    this->setBitmapDevice(emptyBitmap);
 }
 
 SkDumpCanvas::~SkDumpCanvas() {
@@ -185,7 +203,21 @@
 
 int SkDumpCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint,
                              SaveFlags flags) {
-    this->dump(kSave_Verb, paint, "saveLayer(0x%X)", flags);
+    SkString str;
+    str.printf("saveLayer(0x%X)", flags);
+    if (bounds) {
+        str.append(" bounds");
+        toString(*bounds, &str);
+    }
+    if (paint) {
+        if (paint->getAlpha() != 0xFF) {
+            str.appendf(" alpha:0x%02X", paint->getAlpha());
+        }
+        if (paint->getXfermode()) {
+            str.appendf(" xfermode:%p", paint->getXfermode());
+        }
+    }
+    this->dump(kSave_Verb, paint, str.c_str());
     return this->INHERITED::saveLayer(bounds, paint, flags);
 }
 
@@ -219,14 +251,14 @@
 
 bool SkDumpCanvas::concat(const SkMatrix& matrix) {
     SkString str;
-    matrix.toDumpString(&str);
+    matrix.toString(&str);
     this->dump(kMatrix_Verb, NULL, "concat(%s)", str.c_str());
     return this->INHERITED::concat(matrix);
 }
 
 void SkDumpCanvas::setMatrix(const SkMatrix& matrix) {
     SkString str;
-    matrix.toDumpString(&str);
+    matrix.toString(&str);
     this->dump(kMatrix_Verb, NULL, "setMatrix(%s)", str.c_str());
     this->INHERITED::setMatrix(matrix);
 }
@@ -245,6 +277,14 @@
     return this->INHERITED::clipRect(rect, op, doAA);
 }
 
+bool SkDumpCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    SkString str;
+    toString(rrect, &str);
+    this->dump(kClip_Verb, NULL, "clipRRect(%s %s %s)", str.c_str(), toString(op),
+               bool_to_aastring(doAA));
+    return this->INHERITED::clipRRect(rrect, op, doAA);
+}
+
 bool SkDumpCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
     SkString str;
     toString(path, &str);
@@ -273,12 +313,24 @@
                count);
 }
 
+void SkDumpCanvas::drawOval(const SkRect& rect, const SkPaint& paint) {
+    SkString str;
+    toString(rect, &str);
+    this->dump(kDrawOval_Verb, &paint, "drawOval(%s)", str.c_str());
+}
+
 void SkDumpCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
     SkString str;
     toString(rect, &str);
     this->dump(kDrawRect_Verb, &paint, "drawRect(%s)", str.c_str());
 }
 
+void SkDumpCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    SkString str;
+    toString(rrect, &str);
+    this->dump(kDrawRRect_Verb, &paint, "drawRRect(%s)", str.c_str());
+}
+
 void SkDumpCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
     SkString str;
     toString(path, &str);
@@ -288,34 +340,34 @@
 void SkDumpCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar x, SkScalar y,
                                const SkPaint* paint) {
     SkString str;
-    toString(bitmap, &str);
+    bitmap.toString(&str);
     this->dump(kDrawBitmap_Verb, paint, "drawBitmap(%s %g %g)", str.c_str(),
                SkScalarToFloat(x), SkScalarToFloat(y));
 }
 
-void SkDumpCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
-                                   const SkRect& dst, const SkPaint* paint) {
+void SkDumpCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
+                                        const SkRect& dst, const SkPaint* paint) {
     SkString bs, rs;
-    toString(bitmap, &bs);
+    bitmap.toString(&bs);
     toString(dst, &rs);
     // show the src-rect only if its not everything
     if (src && (src->fLeft > 0 || src->fTop > 0 ||
-                src->fRight < bitmap.width() ||
-                src->fBottom < bitmap.height())) {
+                src->fRight < SkIntToScalar(bitmap.width()) ||
+                src->fBottom < SkIntToScalar(bitmap.height()))) {
         SkString ss;
         toString(*src, &ss);
         rs.prependf("%s ", ss.c_str());
     }
 
-    this->dump(kDrawBitmap_Verb, paint, "drawBitmapRect(%s %s)",
+    this->dump(kDrawBitmap_Verb, paint, "drawBitmapRectToRect(%s %s)",
                bs.c_str(), rs.c_str());
 }
 
 void SkDumpCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
                                      const SkPaint* paint) {
     SkString bs, ms;
-    toString(bitmap, &bs);
-    m.toDumpString(&ms);
+    bitmap.toString(&bs);
+    m.toString(&ms);
     this->dump(kDrawBitmap_Verb, paint, "drawBitmapMatrix(%s %s)",
                bs.c_str(), ms.c_str());
 }
@@ -323,7 +375,7 @@
 void SkDumpCanvas::drawSprite(const SkBitmap& bitmap, int x, int y,
                                const SkPaint* paint) {
     SkString str;
-    toString(bitmap, &str);
+    bitmap.toString(&str);
     this->dump(kDrawBitmap_Verb, paint, "drawSprite(%s %d %d)", str.c_str(),
                x, y);
 }
@@ -407,12 +459,7 @@
 static void appendFlattenable(SkString* str, const SkFlattenable* ptr,
                               const char name[]) {
     if (ptr) {
-        SkString info;
-        if (ptr->toDumpString(&info)) {
-            str->appendf(" %s", info.c_str());
-        } else {
-            str->appendf(" %s:%p", name, ptr);
-        }
+        str->appendf(" %s:%p", name, ptr);
     }
 }
 
@@ -422,7 +469,11 @@
     const int level = canvas->getNestLevel() + canvas->getSaveCount() - 1;
     SkASSERT(level >= 0);
     for (int i = 0; i < level; i++) {
+#if 0
         tab.append("\t");
+#else
+        tab.append("    ");   // tabs are often too wide to be useful
+#endif
     }
     msg.printf("%s%s", tab.c_str(), str);
 
@@ -452,4 +503,4 @@
 
 SkDebugfDumper::SkDebugfDumper() : INHERITED(dumpToDebugf, NULL) {}
 
-
+#endif
diff --git a/src/utils/SkFloatUtils.h b/src/utils/SkFloatUtils.h
new file mode 100644
index 0000000..101aac7
--- /dev/null
+++ b/src/utils/SkFloatUtils.h
@@ -0,0 +1,173 @@
+/*
+ * 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 SkFloatUtils_DEFINED
+#define SkFloatUtils_DEFINED
+
+#include "SkTypes.h"
+#include <limits.h>
+#include <float.h>
+
+template <size_t size>
+class SkTypeWithSize {
+public:
+    // Prevents using SkTypeWithSize<N> with non-specialized N.
+    typedef void UInt;
+};
+
+template <>
+class SkTypeWithSize<32> {
+public:
+    typedef uint32_t UInt;
+};
+
+template <>
+class SkTypeWithSize<64> {
+public:
+    typedef uint64_t UInt;
+};
+
+template <typename RawType>
+struct SkNumericLimits {
+    static const int digits = 0;
+};
+
+template <>
+struct SkNumericLimits<double> {
+    static const int digits = DBL_MANT_DIG;
+};
+
+template <>
+struct SkNumericLimits<float> {
+    static const int digits = FLT_MANT_DIG;
+};
+
+//See
+//http://stackoverflow.com/questions/17333/most-effective-way-for-float-and-double-comparison/3423299#3423299
+//http://code.google.com/p/googletest/source/browse/trunk/include/gtest/internal/gtest-internal.h
+//http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
+
+template <typename RawType, unsigned int ULPs>
+class SkFloatingPoint {
+public:
+    /** Bits is a unsigned integer the same size as the floating point number. */
+    typedef typename SkTypeWithSize<sizeof(RawType) * CHAR_BIT>::UInt Bits;
+
+    /** # of bits in a number. */
+    static const size_t kBitCount = CHAR_BIT * sizeof(RawType);
+
+    /** # of fraction bits in a number. */
+    static const size_t kFractionBitCount = SkNumericLimits<RawType>::digits - 1;
+
+    /** # of exponent bits in a number. */
+    static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount;
+
+    /** The mask for the sign bit. */
+    static const Bits kSignBitMask = static_cast<Bits>(1) << (kBitCount - 1);
+
+    /** The mask for the fraction bits. */
+    static const Bits kFractionBitMask =
+        ~static_cast<Bits>(0) >> (kExponentBitCount + 1);
+
+    /** The mask for the exponent bits. */
+    static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask);
+
+    /** How many ULP's (Units in the Last Place) to tolerate when comparing. */
+    static const size_t kMaxUlps = ULPs;
+
+    /**
+     *  Constructs a FloatingPoint from a raw floating-point number.
+     *
+     *  On an Intel CPU, passing a non-normalized NAN (Not a Number)
+     *  around may change its bits, although the new value is guaranteed
+     *  to be also a NAN.  Therefore, don't expect this constructor to
+     *  preserve the bits in x when x is a NAN.
+     */
+    explicit SkFloatingPoint(const RawType& x) { fU.value = x; }
+
+    /** Returns the exponent bits of this number. */
+    Bits exponent_bits() const { return kExponentBitMask & fU.bits; }
+
+    /** Returns the fraction bits of this number. */
+    Bits fraction_bits() const { return kFractionBitMask & fU.bits; }
+
+    /** Returns true iff this is NAN (not a number). */
+    bool is_nan() const {
+        // It's a NAN if both of the folloowing are true:
+        // * the exponent bits are all ones
+        // * the fraction bits are not all zero.
+        return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0);
+    }
+
+    /**
+     *  Returns true iff this number is at most kMaxUlps ULP's away from ths.
+     *  In particular, this function:
+     *   - returns false if either number is (or both are) NAN.
+     *   - treats really large numbers as almost equal to infinity.
+     *   - thinks +0.0 and -0.0 are 0 DLP's apart.
+     */
+    bool AlmostEquals(const SkFloatingPoint& rhs) const {
+        // Any comparison operation involving a NAN must return false.
+        if (is_nan() || rhs.is_nan()) return false;
+
+        const Bits dist = DistanceBetweenSignAndMagnitudeNumbers(fU.bits,
+                                                                 rhs.fU.bits);
+        //SkDEBUGF(("(%f, %f, %d) ", u_.value_, rhs.u_.value_, dist));
+        return dist <= kMaxUlps;
+    }
+
+private:
+    /** The data type used to store the actual floating-point number. */
+    union FloatingPointUnion {
+        /** The raw floating-point number. */
+        RawType value;
+        /** The bits that represent the number. */
+        Bits bits;
+    };
+
+    /**
+     *  Converts an integer from the sign-and-magnitude representation to
+     *  the biased representation. More precisely, let N be 2 to the
+     *  power of (kBitCount - 1), an integer x is represented by the
+     *  unsigned number x + N.
+     *
+     *  For instance,
+     *
+     *    -N + 1 (the most negative number representable using
+     *           sign-and-magnitude) is represented by 1;
+     *    0      is represented by N; and
+     *    N - 1  (the biggest number representable using
+     *           sign-and-magnitude) is represented by 2N - 1.
+     *
+     *  Read http://en.wikipedia.org/wiki/Signed_number_representations
+     *  for more details on signed number representations.
+     */
+    static Bits SignAndMagnitudeToBiased(const Bits &sam) {
+        if (kSignBitMask & sam) {
+            // sam represents a negative number.
+            return ~sam + 1;
+        } else {
+            // sam represents a positive number.
+            return kSignBitMask | sam;
+        }
+    }
+
+    /**
+     *  Given two numbers in the sign-and-magnitude representation,
+     *  returns the distance between them as an unsigned number.
+     */
+    static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1,
+                                                       const Bits &sam2) {
+        const Bits biased1 = SignAndMagnitudeToBiased(sam1);
+        const Bits biased2 = SignAndMagnitudeToBiased(sam2);
+        return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1);
+    }
+
+    FloatingPointUnion fU;
+};
+
+#endif
diff --git a/src/utils/SkInterpolator.cpp b/src/utils/SkInterpolator.cpp
index d43792f..2853b07 100644
--- a/src/utils/SkInterpolator.cpp
+++ b/src/utils/SkInterpolator.cpp
@@ -81,7 +81,7 @@
         if (offsetTime >= endTime) {
             SkScalar fraction = SkScalarFraction(fRepeat);
             offsetTime = fraction == 0 && fRepeat > 0 ? totalTime :
-                SkScalarMulFloor(fraction, totalTime);
+                (SkMSec) SkScalarMulFloor(fraction, totalTime);
             result = kFreezeEnd_Result;
         } else {
             int mirror = fFlags & kMirror;
@@ -165,7 +165,7 @@
 bool SkInterpolator::setKeyFrame(int index, SkMSec time,
                             const SkScalar values[], const SkScalar blend[4]) {
     SkASSERT(values != NULL);
-    
+
     if (blend == NULL) {
         blend = gIdentityBlend;
     }
@@ -237,13 +237,13 @@
                            SkScalar cx, SkScalar cy) {
     // pin to the unit-square, and convert to 2.14
     Dot14 x = pin_and_convert(value);
-    
+
     if (x == 0) return 0;
     if (x == Dot14_ONE) return SK_Scalar1;
-    
+
     Dot14 b = pin_and_convert(bx);
     Dot14 c = pin_and_convert(cx);
-    
+
     // Now compute our coefficients from the control points
     //  t   -> 3b
     //  t^2 -> 3c - 6b
@@ -264,7 +264,7 @@
             t += dt;
         }
     }
-    
+
     // Now we have t, so compute the coeff for Y and evaluate
     b = pin_and_convert(by);
     c = pin_and_convert(cy);
@@ -329,4 +329,3 @@
 }
 
 #endif
-
diff --git a/src/utils/SkJSON.cpp b/src/utils/SkJSON.cpp
index c55d464..9b12208 100644
--- a/src/utils/SkJSON.cpp
+++ b/src/utils/SkJSON.cpp
@@ -56,7 +56,7 @@
         SkASSERT(name);
 
         fNext = NULL;
-        
+
         size_t len = strlen(name);
         // extra 1 for str[0] which stores the type
         char* str = alloc_string(1 + len);
@@ -64,14 +64,14 @@
         // str[1] skips the type, len+1 includes the terminating 0 byte.
         memcpy(&str[1], name, len + 1);
         fName = str;
-        
+
         // fValue is uninitialized
     }
     ~Slot();
-    
+
     Type type() const { return (Type)fName[0]; }
     const char* name() const { return &fName[1]; }
-    
+
     Slot*   fNext;
     char*   fName;    // fName[0] is the type, &fName[1] is the "name"
     union {
@@ -378,7 +378,7 @@
 
 void SkJSON::Object::dumpLevel(int level) const {
     for (Slot* slot = fHead; slot; slot = slot->fNext) {
-        Type t = slot->type();        
+        Type t = slot->type();
         tabForLevel(level + 1);
         SkDebugf("\"%s\" : ", slot->name());
         switch (slot->type()) {
@@ -507,11 +507,11 @@
 };
 
 typedef void* (*DupProc)(const void*);
-             
+
 static void* dup_object(const void* src) {
     return SkNEW_ARGS(SkJSON::Object, (*(SkJSON::Object*)src));
 }
-                      
+
 static void* dup_array(const void* src) {
     return SkNEW_ARGS(SkJSON::Array, (*(SkJSON::Array*)src));
 }
@@ -561,7 +561,7 @@
 SkJSON::Array::Array(const int32_t values[], int count) {
     this->init(kInt, count, values);
 }
-    
+
 SkJSON::Array::Array(const float values[], int count) {
     this->init(kFloat, count, values);
 }
@@ -632,6 +632,3 @@
         prev = dup_string(str);
     }
 }
-
-
-
diff --git a/src/utils/SkLayer.cpp b/src/utils/SkLayer.cpp
index aaca786..a96d921 100644
--- a/src/utils/SkLayer.cpp
+++ b/src/utils/SkLayer.cpp
@@ -15,6 +15,8 @@
     static int gLayerAllocCount;
 #endif
 
+SK_DEFINE_INST_COUNT(SkLayer)
+
 ///////////////////////////////////////////////////////////////////////////////
 
 SkLayer::SkLayer() {
@@ -34,7 +36,7 @@
 #endif
 }
 
-SkLayer::SkLayer(const SkLayer& src) : INHERITED() {
+SkLayer::SkLayer(const SkLayer& src) {
     fParent = NULL;
     m_opacity = src.m_opacity;
     m_size = src.m_size;
@@ -228,4 +230,3 @@
         }
     }
 }
-
diff --git a/src/utils/SkMD5.cpp b/src/utils/SkMD5.cpp
new file mode 100644
index 0000000..725ae55
--- /dev/null
+++ b/src/utils/SkMD5.cpp
@@ -0,0 +1,252 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The following code is based on the description in RFC 1321.
+ * http://www.ietf.org/rfc/rfc1321.txt
+ */
+
+#include "SkTypes.h"
+#include "SkMD5.h"
+#include <string.h>
+
+/** MD5 basic transformation. Transforms state based on block. */
+static void transform(uint32_t state[4], const uint8_t block[64]);
+
+/** Encodes input into output (4 little endian 32 bit values). */
+static void encode(uint8_t output[16], const uint32_t input[4]);
+
+/** Encodes input into output (little endian 64 bit value). */
+static void encode(uint8_t output[8], const uint64_t input);
+
+/** Decodes input (4 little endian 32 bit values) into storage, if required. */
+static const uint32_t* decode(uint32_t storage[16], const uint8_t input[64]);
+
+SkMD5::SkMD5() : byteCount(0) {
+    // These are magic numbers from the specification.
+    this->state[0] = 0x67452301;
+    this->state[1] = 0xefcdab89;
+    this->state[2] = 0x98badcfe;
+    this->state[3] = 0x10325476;
+}
+
+void SkMD5::update(const uint8_t* input, size_t inputLength) {
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int bufferAvailable = 64 - bufferIndex;
+
+    unsigned int inputIndex;
+    if (inputLength >= bufferAvailable) {
+        if (bufferIndex) {
+            memcpy(&this->buffer[bufferIndex], input, bufferAvailable);
+            transform(this->state, this->buffer);
+            inputIndex = bufferAvailable;
+        } else {
+            inputIndex = 0;
+        }
+
+        for (; inputIndex + 63 < inputLength; inputIndex += 64) {
+            transform(this->state, &input[inputIndex]);
+        }
+
+        bufferIndex = 0;
+    } else {
+        inputIndex = 0;
+    }
+
+    memcpy(&this->buffer[bufferIndex], &input[inputIndex], inputLength - inputIndex);
+
+    this->byteCount += inputLength;
+}
+
+void SkMD5::finish(Digest& digest) {
+    // Get the number of bits before padding.
+    uint8_t bits[8];
+    encode(bits, this->byteCount << 3);
+
+    // Pad out to 56 mod 64.
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int paddingLength = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
+    static uint8_t PADDING[64] = {
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    };
+    this->update(PADDING, paddingLength);
+
+    // Append length (length before padding, will cause final update).
+    this->update(bits, 8);
+
+    // Write out digest.
+    encode(digest.data, this->state);
+
+#if defined(SK_MD5_CLEAR_DATA)
+    // Clear state.
+    memset(this, 0, sizeof(*this));
+#endif
+}
+
+struct F { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    //return (x & y) | ((~x) & z);
+    return ((y ^ z) & x) ^ z; //equivelent but faster
+}};
+
+struct G { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    return (x & z) | (y & (~z));
+    //return ((x ^ y) & z) ^ y; //equivelent but slower
+}};
+
+struct H { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    return x ^ y ^ z;
+}};
+
+struct I { uint32_t operator()(uint32_t x, uint32_t y, uint32_t z) {
+    return y ^ (x | (~z));
+}};
+
+/** Rotates x left n bits. */
+static inline uint32_t rotate_left(uint32_t x, uint8_t n) {
+    return (x << n) | (x >> (32 - n));
+}
+
+template <typename T>
+static inline void operation(T operation, uint32_t& a, uint32_t b, uint32_t c, uint32_t d,
+                             uint32_t x, uint8_t s, uint32_t t) {
+    a = b + rotate_left(a + operation(b, c, d) + x + t, s);
+}
+
+static void transform(uint32_t state[4], const uint8_t block[64]) {
+    uint32_t a = state[0], b = state[1], c = state[2], d = state[3];
+
+    uint32_t storage[16];
+    const uint32_t* X = decode(storage, block);
+
+    // Round 1
+    operation(F(), a, b, c, d, X[ 0],  7, 0xd76aa478); // 1
+    operation(F(), d, a, b, c, X[ 1], 12, 0xe8c7b756); // 2
+    operation(F(), c, d, a, b, X[ 2], 17, 0x242070db); // 3
+    operation(F(), b, c, d, a, X[ 3], 22, 0xc1bdceee); // 4
+    operation(F(), a, b, c, d, X[ 4],  7, 0xf57c0faf); // 5
+    operation(F(), d, a, b, c, X[ 5], 12, 0x4787c62a); // 6
+    operation(F(), c, d, a, b, X[ 6], 17, 0xa8304613); // 7
+    operation(F(), b, c, d, a, X[ 7], 22, 0xfd469501); // 8
+    operation(F(), a, b, c, d, X[ 8],  7, 0x698098d8); // 9
+    operation(F(), d, a, b, c, X[ 9], 12, 0x8b44f7af); // 10
+    operation(F(), c, d, a, b, X[10], 17, 0xffff5bb1); // 11
+    operation(F(), b, c, d, a, X[11], 22, 0x895cd7be); // 12
+    operation(F(), a, b, c, d, X[12],  7, 0x6b901122); // 13
+    operation(F(), d, a, b, c, X[13], 12, 0xfd987193); // 14
+    operation(F(), c, d, a, b, X[14], 17, 0xa679438e); // 15
+    operation(F(), b, c, d, a, X[15], 22, 0x49b40821); // 16
+
+    // Round 2
+    operation(G(), a, b, c, d, X[ 1],  5, 0xf61e2562); // 17
+    operation(G(), d, a, b, c, X[ 6],  9, 0xc040b340); // 18
+    operation(G(), c, d, a, b, X[11], 14, 0x265e5a51); // 19
+    operation(G(), b, c, d, a, X[ 0], 20, 0xe9b6c7aa); // 20
+    operation(G(), a, b, c, d, X[ 5],  5, 0xd62f105d); // 21
+    operation(G(), d, a, b, c, X[10],  9,  0x2441453); // 22
+    operation(G(), c, d, a, b, X[15], 14, 0xd8a1e681); // 23
+    operation(G(), b, c, d, a, X[ 4], 20, 0xe7d3fbc8); // 24
+    operation(G(), a, b, c, d, X[ 9],  5, 0x21e1cde6); // 25
+    operation(G(), d, a, b, c, X[14],  9, 0xc33707d6); // 26
+    operation(G(), c, d, a, b, X[ 3], 14, 0xf4d50d87); // 27
+    operation(G(), b, c, d, a, X[ 8], 20, 0x455a14ed); // 28
+    operation(G(), a, b, c, d, X[13],  5, 0xa9e3e905); // 29
+    operation(G(), d, a, b, c, X[ 2],  9, 0xfcefa3f8); // 30
+    operation(G(), c, d, a, b, X[ 7], 14, 0x676f02d9); // 31
+    operation(G(), b, c, d, a, X[12], 20, 0x8d2a4c8a); // 32
+
+    // Round 3
+    operation(H(), a, b, c, d, X[ 5],  4, 0xfffa3942); // 33
+    operation(H(), d, a, b, c, X[ 8], 11, 0x8771f681); // 34
+    operation(H(), c, d, a, b, X[11], 16, 0x6d9d6122); // 35
+    operation(H(), b, c, d, a, X[14], 23, 0xfde5380c); // 36
+    operation(H(), a, b, c, d, X[ 1],  4, 0xa4beea44); // 37
+    operation(H(), d, a, b, c, X[ 4], 11, 0x4bdecfa9); // 38
+    operation(H(), c, d, a, b, X[ 7], 16, 0xf6bb4b60); // 39
+    operation(H(), b, c, d, a, X[10], 23, 0xbebfbc70); // 40
+    operation(H(), a, b, c, d, X[13],  4, 0x289b7ec6); // 41
+    operation(H(), d, a, b, c, X[ 0], 11, 0xeaa127fa); // 42
+    operation(H(), c, d, a, b, X[ 3], 16, 0xd4ef3085); // 43
+    operation(H(), b, c, d, a, X[ 6], 23,  0x4881d05); // 44
+    operation(H(), a, b, c, d, X[ 9],  4, 0xd9d4d039); // 45
+    operation(H(), d, a, b, c, X[12], 11, 0xe6db99e5); // 46
+    operation(H(), c, d, a, b, X[15], 16, 0x1fa27cf8); // 47
+    operation(H(), b, c, d, a, X[ 2], 23, 0xc4ac5665); // 48
+
+    // Round 4
+    operation(I(), a, b, c, d, X[ 0],  6, 0xf4292244); // 49
+    operation(I(), d, a, b, c, X[ 7], 10, 0x432aff97); // 50
+    operation(I(), c, d, a, b, X[14], 15, 0xab9423a7); // 51
+    operation(I(), b, c, d, a, X[ 5], 21, 0xfc93a039); // 52
+    operation(I(), a, b, c, d, X[12],  6, 0x655b59c3); // 53
+    operation(I(), d, a, b, c, X[ 3], 10, 0x8f0ccc92); // 54
+    operation(I(), c, d, a, b, X[10], 15, 0xffeff47d); // 55
+    operation(I(), b, c, d, a, X[ 1], 21, 0x85845dd1); // 56
+    operation(I(), a, b, c, d, X[ 8],  6, 0x6fa87e4f); // 57
+    operation(I(), d, a, b, c, X[15], 10, 0xfe2ce6e0); // 58
+    operation(I(), c, d, a, b, X[ 6], 15, 0xa3014314); // 59
+    operation(I(), b, c, d, a, X[13], 21, 0x4e0811a1); // 60
+    operation(I(), a, b, c, d, X[ 4],  6, 0xf7537e82); // 61
+    operation(I(), d, a, b, c, X[11], 10, 0xbd3af235); // 62
+    operation(I(), c, d, a, b, X[ 2], 15, 0x2ad7d2bb); // 63
+    operation(I(), b, c, d, a, X[ 9], 21, 0xeb86d391); // 64
+
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+
+#if defined(SK_MD5_CLEAR_DATA)
+    // Clear sensitive information.
+    if (X == &storage) {
+        memset(storage, 0, sizeof(storage));
+    }
+#endif
+}
+
+static void encode(uint8_t output[16], const uint32_t input[4]) {
+    for (size_t i = 0, j = 0; i < 4; i++, j += 4) {
+        output[j  ] = (uint8_t) (input[i]        & 0xff);
+        output[j+1] = (uint8_t)((input[i] >>  8) & 0xff);
+        output[j+2] = (uint8_t)((input[i] >> 16) & 0xff);
+        output[j+3] = (uint8_t)((input[i] >> 24) & 0xff);
+    }
+}
+
+static void encode(uint8_t output[8], const uint64_t input) {
+    output[0] = (uint8_t) (input        & 0xff);
+    output[1] = (uint8_t)((input >>  8) & 0xff);
+    output[2] = (uint8_t)((input >> 16) & 0xff);
+    output[3] = (uint8_t)((input >> 24) & 0xff);
+    output[4] = (uint8_t)((input >> 32) & 0xff);
+    output[5] = (uint8_t)((input >> 40) & 0xff);
+    output[6] = (uint8_t)((input >> 48) & 0xff);
+    output[7] = (uint8_t)((input >> 56) & 0xff);
+}
+
+static inline bool is_aligned(const void *pointer, size_t byte_count) {
+    return reinterpret_cast<uintptr_t>(pointer) % byte_count == 0;
+}
+
+static const uint32_t* decode(uint32_t storage[16], const uint8_t input[64]) {
+#if defined(SK_CPU_LENDIAN) && defined(SK_CPU_FAST_UNALIGNED_ACCESS)
+   return reinterpret_cast<const uint32_t*>(input);
+#else
+#if defined(SK_CPU_LENDIAN)
+    if (is_aligned(input, 4)) {
+        return reinterpret_cast<const uint32_t*>(input);
+    }
+#endif
+    for (size_t i = 0, j = 0; j < 64; i++, j += 4) {
+        storage[i] =  ((uint32_t)input[j  ])        |
+                     (((uint32_t)input[j+1]) <<  8) |
+                     (((uint32_t)input[j+2]) << 16) |
+                     (((uint32_t)input[j+3]) << 24);
+    }
+    return storage;
+#endif
+}
diff --git a/src/utils/SkMD5.h b/src/utils/SkMD5.h
new file mode 100644
index 0000000..6fbdb46
--- /dev/null
+++ b/src/utils/SkMD5.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkMD5_DEFINED
+#define SkMD5_DEFINED
+
+#include "SkTypes.h"
+#include "SkEndian.h"
+#include "SkStream.h"
+
+//The following macros can be defined to affect the MD5 code generated.
+//SK_MD5_CLEAR_DATA causes all intermediate state to be overwritten with 0's.
+//SK_CPU_LENDIAN allows 32 bit <=> 8 bit conversions without copies (if alligned).
+//SK_CPU_FAST_UNALIGNED_ACCESS allows 32 bit <=> 8 bit conversions without copies if SK_CPU_LENDIAN.
+
+class SkMD5 : SkWStream {
+public:
+    SkMD5();
+
+    /** Processes input, adding it to the digest.
+     *  Note that this treats the buffer as a series of uint8_t values.
+     */
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE {
+        update(reinterpret_cast<const uint8_t*>(buffer), size);
+        return true;
+    }
+
+    /** Processes input, adding it to the digest. Calling this after finish is undefined. */
+    void update(const uint8_t* input, size_t length);
+
+    struct Digest {
+        uint8_t data[16];
+    };
+
+    /** Computes and returns the digest. */
+    void finish(Digest& digest);
+
+private:
+    // number of bytes, modulo 2^64
+    uint64_t byteCount;
+
+    // state (ABCD)
+    uint32_t state[4];
+
+    // input buffer
+    uint8_t buffer[64];
+};
+
+#endif
diff --git a/src/utils/SkMatrix44.cpp b/src/utils/SkMatrix44.cpp
index f00e399..92c8715 100644
--- a/src/utils/SkMatrix44.cpp
+++ b/src/utils/SkMatrix44.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
@@ -6,32 +5,71 @@
  * found in the LICENSE file.
  */
 
-
-
 #include "SkMatrix44.h"
 
-SkMatrix44::SkMatrix44() {
-    this->setIdentity();
+static inline bool eq4(const SkMScalar* SK_RESTRICT a,
+                      const SkMScalar* SK_RESTRICT b) {
+    return (a[0] == b[0]) & (a[1] == b[1]) & (a[2] == b[2]) & (a[3] == b[3]);
 }
 
-SkMatrix44::SkMatrix44(const SkMatrix44& src) {
-    memcpy(this, &src, sizeof(src));
+bool SkMatrix44::operator==(const SkMatrix44& other) const {
+    if (this == &other) {
+        return true;
+    }
+
+    if (this->isTriviallyIdentity() && other.isTriviallyIdentity()) {
+        return true;
+    }
+
+    const SkMScalar* SK_RESTRICT a = &fMat[0][0];
+    const SkMScalar* SK_RESTRICT b = &other.fMat[0][0];
+
+#if 0
+    for (int i = 0; i < 16; ++i) {
+        if (a[i] != b[i]) {
+            return false;
+        }
+    }
+    return true;
+#else
+    // to reduce branch instructions, we compare 4 at a time.
+    // see bench/Matrix44Bench.cpp for test.
+    if (!eq4(&a[0], &b[0])) {
+        return false;
+    }
+    if (!eq4(&a[4], &b[4])) {
+        return false;
+    }
+    if (!eq4(&a[8], &b[8])) {
+        return false;
+    }
+    return eq4(&a[12], &b[12]);
+#endif
 }
 
-SkMatrix44::SkMatrix44(const SkMatrix44& a, const SkMatrix44& b) {
-    this->setConcat(a, b);
-}
+///////////////////////////////////////////////////////////////////////////////
 
-SkMScalar SkMatrix44::get(int row, int col) const {
-    SkASSERT(row <= 3 && row >= 0);
-    SkASSERT(col <= 3 && col >= 0);
-    return fMat[col][row];
-}
+int SkMatrix44::computeTypeMask() const {
+    unsigned mask = 0;
 
-void SkMatrix44::set(int row, int col, const SkMScalar& value) {
-    SkASSERT(row <= 3 && row >= 0);
-    SkASSERT(col <= 3 && col >= 0);
-    fMat[col][row] = value;
+    if (0 != perspX() || 0 != perspY() || 0 != perspZ() || 1 != fMat[3][3]) {
+        return kTranslate_Mask | kScale_Mask | kAffine_Mask | kPerspective_Mask;
+    }
+
+    if (0 != transX() || 0 != transY() || 0 != transZ()) {
+        mask |= kTranslate_Mask;
+    }
+
+    if (1 != scaleX() || 1 != scaleY() || 1 != scaleZ()) {
+        mask |= kScale_Mask;
+    }
+
+    if (0 != fMat[1][0] || 0 != fMat[0][1] || 0 != fMat[0][2] ||
+        0 != fMat[2][0] || 0 != fMat[1][2] || 0 != fMat[2][1]) {
+            mask |= kAffine_Mask;
+    }
+
+    return mask;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -42,7 +80,7 @@
     for (int i = 0; i < 16; ++i) {
         dst[i] = SkMScalarToFloat(src[i]);
     }
-#else
+#elif defined SK_MSCALAR_IS_FLOAT
     memcpy(dst, src, 16 * sizeof(float));
 #endif
 }
@@ -51,7 +89,7 @@
     const SkMScalar* src = &fMat[0][0];
 #ifdef SK_MSCALAR_IS_DOUBLE
     memcpy(dst, src, 16 * sizeof(double));
-#else
+#elif defined SK_MSCALAR_IS_FLOAT
     for (int i = 0; i < 16; ++i) {
         dst[i] = SkMScalarToDouble(src[i]);
     }
@@ -82,88 +120,183 @@
     }
 }
 
-///////////////////////////////////////////////////////////////////////////////
+void SkMatrix44::setColMajorf(const float src[]) {
+    SkMScalar* dst = &fMat[0][0];
+#ifdef SK_MSCALAR_IS_DOUBLE
+    for (int i = 0; i < 16; ++i) {
+        dst[i] = SkMScalarToFloat(src[i]);
+    }
+#elif defined SK_MSCALAR_IS_FLOAT
+    memcpy(dst, src, 16 * sizeof(float));
+#endif
 
-bool SkMatrix44::isIdentity() const {
-    static const SkMScalar  sIdentityMat[4][4] = {
-        { 1, 0, 0, 0 },
-        { 0, 1, 0, 0 },
-        { 0, 0, 1, 0 },
-        { 0, 0, 0, 1 },
-    };
-    return !memcmp(fMat, sIdentityMat, sizeof(fMat));
+    this->dirtyTypeMask();
+}
+
+void SkMatrix44::setColMajord(const double src[]) {
+    SkMScalar* dst = &fMat[0][0];
+#ifdef SK_MSCALAR_IS_DOUBLE
+    memcpy(dst, src, 16 * sizeof(double));
+#elif defined SK_MSCALAR_IS_FLOAT
+    for (int i = 0; i < 16; ++i) {
+        dst[i] = SkDoubleToMScalar(src[i]);
+    }
+#endif
+
+    this->dirtyTypeMask();
+}
+
+void SkMatrix44::setRowMajorf(const float src[]) {
+    SkMScalar* dst = &fMat[0][0];
+    for (int i = 0; i < 4; ++i) {
+        dst[0] = SkMScalarToFloat(src[0]);
+        dst[4] = SkMScalarToFloat(src[1]);
+        dst[8] = SkMScalarToFloat(src[2]);
+        dst[12] = SkMScalarToFloat(src[3]);
+        src += 4;
+        dst += 1;
+    }
+    this->dirtyTypeMask();
+}
+
+void SkMatrix44::setRowMajord(const double src[]) {
+    SkMScalar* dst = &fMat[0][0];
+    for (int i = 0; i < 4; ++i) {
+        dst[0] = SkDoubleToMScalar(src[0]);
+        dst[4] = SkDoubleToMScalar(src[1]);
+        dst[8] = SkDoubleToMScalar(src[2]);
+        dst[12] = SkDoubleToMScalar(src[3]);
+        src += 4;
+        dst += 1;
+    }
+    this->dirtyTypeMask();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
+const SkMatrix44& SkMatrix44::I() {
+    static const SkMatrix44 gIdentity44(kIdentity_Constructor);
+    return gIdentity44;
+}
+
 void SkMatrix44::setIdentity() {
     sk_bzero(fMat, sizeof(fMat));
     fMat[0][0] = fMat[1][1] = fMat[2][2] = fMat[3][3] = 1;
+    this->setTypeMask(kIdentity_Mask);
 }
 
 void SkMatrix44::set3x3(SkMScalar m00, SkMScalar m01, SkMScalar m02,
                         SkMScalar m10, SkMScalar m11, SkMScalar m12,
                         SkMScalar m20, SkMScalar m21, SkMScalar m22) {
-    sk_bzero(fMat, sizeof(fMat));
     fMat[0][0] = m00; fMat[0][1] = m01; fMat[0][2] = m02; fMat[0][3] = 0;
     fMat[1][0] = m10; fMat[1][1] = m11; fMat[1][2] = m12; fMat[1][3] = 0;
     fMat[2][0] = m20; fMat[2][1] = m21; fMat[2][2] = m22; fMat[2][3] = 0;
     fMat[3][0] = 0;   fMat[3][1] = 0;   fMat[3][2] = 0;   fMat[3][3] = 1;
+    this->dirtyTypeMask();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkMatrix44::setTranslate(SkMScalar tx, SkMScalar ty, SkMScalar tz) {
+void SkMatrix44::setTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
     this->setIdentity();
-    fMat[3][0] = tx;
-    fMat[3][1] = ty;
-    fMat[3][2] = tz;
-    fMat[3][3] = 1;
+
+    if (!dx && !dy && !dz) {
+        return;
+    }
+
+    fMat[3][0] = dx;
+    fMat[3][1] = dy;
+    fMat[3][2] = dz;
+    this->setTypeMask(kTranslate_Mask);
 }
 
 void SkMatrix44::preTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
-    SkMatrix44 mat;
-    mat.setTranslate(dx, dy, dz);
-    this->preConcat(mat);
+    if (!dx && !dy && !dz) {
+        return;
+    }
+
+    const double X = SkMScalarToDouble(dx);
+    const double Y = SkMScalarToDouble(dy);
+    const double Z = SkMScalarToDouble(dz);
+
+    double tmp;
+    for (int i = 0; i < 4; ++i) {
+        tmp = fMat[0][i] * X + fMat[1][i] * Y + fMat[2][i] * Z + fMat[3][i];
+        fMat[3][i] = SkDoubleToMScalar(tmp);
+    }
+    this->dirtyTypeMask();
 }
 
 void SkMatrix44::postTranslate(SkMScalar dx, SkMScalar dy, SkMScalar dz) {
-    fMat[3][0] += dx;
-    fMat[3][1] += dy;
-    fMat[3][2] += dz;
+    if (!dx && !dy && !dz) {
+        return;
+    }
+
+    if (this->getType() & kPerspective_Mask) {
+        for (int i = 0; i < 4; ++i) {
+            fMat[i][0] += fMat[i][3] * dx;
+            fMat[i][1] += fMat[i][3] * dy;
+            fMat[i][2] += fMat[i][3] * dz;
+        }
+    } else {
+        fMat[3][0] += dx;
+        fMat[3][1] += dy;
+        fMat[3][2] += dz;
+        this->dirtyTypeMask();
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkMatrix44::setScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
-    sk_bzero(fMat, sizeof(fMat));
+    this->setIdentity();
+
+    if (1 == sx && 1 == sy && 1 == sz) {
+        return;
+    }
+
     fMat[0][0] = sx;
     fMat[1][1] = sy;
     fMat[2][2] = sz;
-    fMat[3][3] = 1;
+    this->setTypeMask(kScale_Mask);
 }
 
 void SkMatrix44::preScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
-    SkMatrix44 tmp;
-    tmp.setScale(sx, sy, sz);
-    this->preConcat(tmp);
+    if (1 == sx && 1 == sy && 1 == sz) {
+        return;
+    }
+
+    // The implementation matrix * pureScale can be shortcut
+    // by knowing that pureScale components effectively scale
+    // the columns of the original matrix.
+    for (int i = 0; i < 4; i++) {
+        fMat[0][i] *= sx;
+        fMat[1][i] *= sy;
+        fMat[2][i] *= sz;
+    }
+    this->dirtyTypeMask();
 }
 
 void SkMatrix44::postScale(SkMScalar sx, SkMScalar sy, SkMScalar sz) {
+    if (1 == sx && 1 == sy && 1 == sz) {
+        return;
+    }
+
     for (int i = 0; i < 4; i++) {
         fMat[i][0] *= sx;
         fMat[i][1] *= sy;
         fMat[i][2] *= sz;
     }
+    this->dirtyTypeMask();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 void SkMatrix44::setRotateAbout(SkMScalar x, SkMScalar y, SkMScalar z,
                                 SkMScalar radians) {
-    double len2 = x * x + y * y + z * z;
-    if (len2 != 1) {
-        if (len2 == 0) {
+    double len2 = (double)x * x + (double)y * y + (double)z * z;
+    if (1 != len2) {
+        if (0 == len2) {
             this->setIdentity();
             return;
         }
@@ -206,18 +339,55 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+static bool bits_isonly(int value, int mask) {
+    return 0 == (value & ~mask);
+}
+
 void SkMatrix44::setConcat(const SkMatrix44& a, const SkMatrix44& b) {
-    SkMScalar result[4][4];
-    for (int i = 0; i < 4; i++) {
+    const SkMatrix44::TypeMask a_mask = a.getType();
+    const SkMatrix44::TypeMask b_mask = b.getType();
+
+    if (kIdentity_Mask == a_mask) {
+        *this = b;
+        return;
+    }
+    if (kIdentity_Mask == b_mask) {
+        *this = a;
+        return;
+    }
+
+    bool useStorage = (this == &a || this == &b);
+    SkMScalar storage[16];
+    SkMScalar* result = useStorage ? storage : &fMat[0][0];
+
+    // Both matrices are at most scale+translate
+    if (bits_isonly(a_mask | b_mask, kScale_Mask | kTranslate_Mask)) {
+        result[0] = a.fMat[0][0] * b.fMat[0][0];
+        result[1] = result[2] = result[3] = result[4] = 0;
+        result[5] = a.fMat[1][1] * b.fMat[1][1];
+        result[6] = result[7] = result[8] = result[9] = 0;
+        result[10] = a.fMat[2][2] * b.fMat[2][2];
+        result[11] = 0;
+        result[12] = a.fMat[0][0] * b.fMat[3][0] + a.fMat[3][0];
+        result[13] = a.fMat[1][1] * b.fMat[3][1] + a.fMat[3][1];
+        result[14] = a.fMat[2][2] * b.fMat[3][2] + a.fMat[3][2];
+        result[15] = 1;
+    } else {
         for (int j = 0; j < 4; j++) {
-            double value = 0;
-            for (int k = 0; k < 4; k++) {
-                value += SkMScalarToDouble(a.fMat[k][i]) * b.fMat[j][k];
+            for (int i = 0; i < 4; i++) {
+                double value = 0;
+                for (int k = 0; k < 4; k++) {
+                    value += SkMScalarToDouble(a.fMat[k][i]) * b.fMat[j][k];
+                }
+                *result++ = SkDoubleToMScalar(value);
             }
-            result[j][i] = SkDoubleToMScalar(value);
         }
     }
-    memcpy(fMat, result, sizeof(result));
+
+    if (useStorage) {
+        memcpy(fMat, storage, sizeof(storage));
+    }
+    this->dirtyTypeMask();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -239,18 +409,45 @@
     promoting our SkMScalar values to double (if needed).
  */
 double SkMatrix44::determinant() const {
-    return  fMat[0][0] * det3x3(fMat[1][1], fMat[1][2], fMat[1][3],
-                                fMat[2][1], fMat[2][2], fMat[2][3],
-                                fMat[3][1], fMat[3][2], fMat[3][3]) -
-    fMat[1][0] * det3x3(fMat[0][1], fMat[0][2], fMat[0][3],
-                        fMat[2][1], fMat[2][2], fMat[2][3],
-                        fMat[3][1], fMat[3][2], fMat[3][3]) +
-    fMat[2][0] * det3x3(fMat[0][1], fMat[0][2], fMat[0][3],
-                        fMat[1][1], fMat[1][2], fMat[1][3],
-                        fMat[3][1], fMat[3][2], fMat[3][3]) -
-    fMat[3][0] * det3x3(fMat[0][1], fMat[0][2], fMat[0][3],
-                        fMat[1][1], fMat[1][2], fMat[1][3],
-                        fMat[2][1], fMat[2][2], fMat[2][3]);
+    if (this->isIdentity()) {
+        return 1;
+    }
+    if (this->isScaleTranslate()) {
+        return fMat[0][0] * fMat[1][1] * fMat[2][2] * fMat[3][3];
+    }
+
+    double a00 = fMat[0][0];
+    double a01 = fMat[0][1];
+    double a02 = fMat[0][2];
+    double a03 = fMat[0][3];
+    double a10 = fMat[1][0];
+    double a11 = fMat[1][1];
+    double a12 = fMat[1][2];
+    double a13 = fMat[1][3];
+    double a20 = fMat[2][0];
+    double a21 = fMat[2][1];
+    double a22 = fMat[2][2];
+    double a23 = fMat[2][3];
+    double a30 = fMat[3][0];
+    double a31 = fMat[3][1];
+    double a32 = fMat[3][2];
+    double a33 = fMat[3][3];
+
+    double b00 = a00 * a11 - a01 * a10;
+    double b01 = a00 * a12 - a02 * a10;
+    double b02 = a00 * a13 - a03 * a10;
+    double b03 = a01 * a12 - a02 * a11;
+    double b04 = a01 * a13 - a03 * a11;
+    double b05 = a02 * a13 - a03 * a12;
+    double b06 = a20 * a31 - a21 * a30;
+    double b07 = a20 * a32 - a22 * a30;
+    double b08 = a20 * a33 - a23 * a30;
+    double b09 = a21 * a32 - a22 * a31;
+    double b10 = a21 * a33 - a23 * a31;
+    double b11 = a22 * a33 - a23 * a32;
+
+    // Calculate the determinant
+    return b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -266,65 +463,136 @@
 }
 
 bool SkMatrix44::invert(SkMatrix44* inverse) const {
-    double det = this->determinant();
+    if (this->isIdentity()) {
+        if (inverse) {
+            *inverse = *this;
+            return true;
+        }
+    }
+    if (this->isTranslate()) {
+        if (inverse) {
+            inverse->setTranslate(-fMat[3][0], -fMat[3][1], -fMat[3][2]);
+        }
+        return true;
+    }
+    if (this->isScaleTranslate()) {
+        if (0 == fMat[0][0] * fMat[1][1] * fMat[2][2]) {
+            return false;
+        }
+        if (inverse) {
+            sk_bzero(inverse->fMat, sizeof(inverse->fMat));
+
+            inverse->fMat[3][0] = -fMat[3][0] / fMat[0][0];
+            inverse->fMat[3][1] = -fMat[3][1] / fMat[1][1];
+            inverse->fMat[3][2] = -fMat[3][2] / fMat[2][2];
+
+            inverse->fMat[0][0] = 1 / fMat[0][0];
+            inverse->fMat[1][1] = 1 / fMat[1][1];
+            inverse->fMat[2][2] = 1 / fMat[2][2];
+            inverse->fMat[3][3] = 1;
+
+            inverse->setTypeMask(this->getType());
+        }
+        return true;
+    }
+
+    double a00 = fMat[0][0];
+    double a01 = fMat[0][1];
+    double a02 = fMat[0][2];
+    double a03 = fMat[0][3];
+    double a10 = fMat[1][0];
+    double a11 = fMat[1][1];
+    double a12 = fMat[1][2];
+    double a13 = fMat[1][3];
+    double a20 = fMat[2][0];
+    double a21 = fMat[2][1];
+    double a22 = fMat[2][2];
+    double a23 = fMat[2][3];
+    double a30 = fMat[3][0];
+    double a31 = fMat[3][1];
+    double a32 = fMat[3][2];
+    double a33 = fMat[3][3];
+
+    double b00 = a00 * a11 - a01 * a10;
+    double b01 = a00 * a12 - a02 * a10;
+    double b02 = a00 * a13 - a03 * a10;
+    double b03 = a01 * a12 - a02 * a11;
+    double b04 = a01 * a13 - a03 * a11;
+    double b05 = a02 * a13 - a03 * a12;
+    double b06 = a20 * a31 - a21 * a30;
+    double b07 = a20 * a32 - a22 * a30;
+    double b08 = a20 * a33 - a23 * a30;
+    double b09 = a21 * a32 - a22 * a31;
+    double b10 = a21 * a33 - a23 * a31;
+    double b11 = a22 * a33 - a23 * a32;
+
+    // Calculate the determinant
+    double det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
+
     if (dabs(det) < TOO_SMALL_FOR_DETERMINANT) {
         return false;
     }
     if (NULL == inverse) {
         return true;
     }
+    double invdet = 1.0 / det;
 
-    // we explicitly promote to doubles to keep the intermediate values in
-    // higher precision (assuming SkMScalar isn't already a double)
-    double m00 = fMat[0][0];
-    double m01 = fMat[0][1];
-    double m02 = fMat[0][2];
-    double m03 = fMat[0][3];
-    double m10 = fMat[1][0];
-    double m11 = fMat[1][1];
-    double m12 = fMat[1][2];
-    double m13 = fMat[1][3];
-    double m20 = fMat[2][0];
-    double m21 = fMat[2][1];
-    double m22 = fMat[2][2];
-    double m23 = fMat[2][3];
-    double m30 = fMat[3][0];
-    double m31 = fMat[3][1];
-    double m32 = fMat[3][2];
-    double m33 = fMat[3][3];
+    b00 *= invdet;
+    b01 *= invdet;
+    b02 *= invdet;
+    b03 *= invdet;
+    b04 *= invdet;
+    b05 *= invdet;
+    b06 *= invdet;
+    b07 *= invdet;
+    b08 *= invdet;
+    b09 *= invdet;
+    b10 *= invdet;
+    b11 *= invdet;
 
-    double tmp[4][4];
+    inverse->fMat[0][0] = SkDoubleToMScalar(a11 * b11 - a12 * b10 + a13 * b09);
+    inverse->fMat[0][1] = SkDoubleToMScalar(a02 * b10 - a01 * b11 - a03 * b09);
+    inverse->fMat[0][2] = SkDoubleToMScalar(a31 * b05 - a32 * b04 + a33 * b03);
+    inverse->fMat[0][3] = SkDoubleToMScalar(a22 * b04 - a21 * b05 - a23 * b03);
+    inverse->fMat[1][0] = SkDoubleToMScalar(a12 * b08 - a10 * b11 - a13 * b07);
+    inverse->fMat[1][1] = SkDoubleToMScalar(a00 * b11 - a02 * b08 + a03 * b07);
+    inverse->fMat[1][2] = SkDoubleToMScalar(a32 * b02 - a30 * b05 - a33 * b01);
+    inverse->fMat[1][3] = SkDoubleToMScalar(a20 * b05 - a22 * b02 + a23 * b01);
+    inverse->fMat[2][0] = SkDoubleToMScalar(a10 * b10 - a11 * b08 + a13 * b06);
+    inverse->fMat[2][1] = SkDoubleToMScalar(a01 * b08 - a00 * b10 - a03 * b06);
+    inverse->fMat[2][2] = SkDoubleToMScalar(a30 * b04 - a31 * b02 + a33 * b00);
+    inverse->fMat[2][3] = SkDoubleToMScalar(a21 * b02 - a20 * b04 - a23 * b00);
+    inverse->fMat[3][0] = SkDoubleToMScalar(a11 * b07 - a10 * b09 - a12 * b06);
+    inverse->fMat[3][1] = SkDoubleToMScalar(a00 * b09 - a01 * b07 + a02 * b06);
+    inverse->fMat[3][2] = SkDoubleToMScalar(a31 * b01 - a30 * b03 - a32 * b00);
+    inverse->fMat[3][3] = SkDoubleToMScalar(a20 * b03 - a21 * b01 + a22 * b00);
+    inverse->dirtyTypeMask();
 
-    tmp[0][0] = m12*m23*m31 - m13*m22*m31 + m13*m21*m32 - m11*m23*m32 - m12*m21*m33 + m11*m22*m33;
-    tmp[0][1] = m03*m22*m31 - m02*m23*m31 - m03*m21*m32 + m01*m23*m32 + m02*m21*m33 - m01*m22*m33;
-    tmp[0][2] = m02*m13*m31 - m03*m12*m31 + m03*m11*m32 - m01*m13*m32 - m02*m11*m33 + m01*m12*m33;
-    tmp[0][3] = m03*m12*m21 - m02*m13*m21 - m03*m11*m22 + m01*m13*m22 + m02*m11*m23 - m01*m12*m23;
-    tmp[1][0] = m13*m22*m30 - m12*m23*m30 - m13*m20*m32 + m10*m23*m32 + m12*m20*m33 - m10*m22*m33;
-    tmp[1][1] = m02*m23*m30 - m03*m22*m30 + m03*m20*m32 - m00*m23*m32 - m02*m20*m33 + m00*m22*m33;
-    tmp[1][2] = m03*m12*m30 - m02*m13*m30 - m03*m10*m32 + m00*m13*m32 + m02*m10*m33 - m00*m12*m33;
-    tmp[1][3] = m02*m13*m20 - m03*m12*m20 + m03*m10*m22 - m00*m13*m22 - m02*m10*m23 + m00*m12*m23;
-    tmp[2][0] = m11*m23*m30 - m13*m21*m30 + m13*m20*m31 - m10*m23*m31 - m11*m20*m33 + m10*m21*m33;
-    tmp[2][1] = m03*m21*m30 - m01*m23*m30 - m03*m20*m31 + m00*m23*m31 + m01*m20*m33 - m00*m21*m33;
-    tmp[2][2] = m01*m13*m30 - m03*m11*m30 + m03*m10*m31 - m00*m13*m31 - m01*m10*m33 + m00*m11*m33;
-    tmp[2][3] = m03*m11*m20 - m01*m13*m20 - m03*m10*m21 + m00*m13*m21 + m01*m10*m23 - m00*m11*m23;
-    tmp[3][0] = m12*m21*m30 - m11*m22*m30 - m12*m20*m31 + m10*m22*m31 + m11*m20*m32 - m10*m21*m32;
-    tmp[3][1] = m01*m22*m30 - m02*m21*m30 + m02*m20*m31 - m00*m22*m31 - m01*m20*m32 + m00*m21*m32;
-    tmp[3][2] = m02*m11*m30 - m01*m12*m30 - m02*m10*m31 + m00*m12*m31 + m01*m10*m32 - m00*m11*m32;
-    tmp[3][3] = m01*m12*m20 - m02*m11*m20 + m02*m10*m21 - m00*m12*m21 - m01*m10*m22 + m00*m11*m22;
-
-    double invDet = 1.0 / det;
-    for (int i = 0; i < 4; i++) {
-        for (int j = 0; j < 4; j++) {
-            inverse->fMat[i][j] = SkDoubleToMScalar(tmp[i][j] * invDet);
-        }
-    }
+    inverse->dirtyTypeMask();
     return true;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
-void SkMatrix44::map(const SkScalar src[4], SkScalar dst[4]) const {
-    SkScalar result[4];
+void SkMatrix44::transpose() {
+    SkTSwap(fMat[0][1], fMat[1][0]);
+    SkTSwap(fMat[0][2], fMat[2][0]);
+    SkTSwap(fMat[0][3], fMat[3][0]);
+    SkTSwap(fMat[1][2], fMat[2][1]);
+    SkTSwap(fMat[1][3], fMat[3][1]);
+    SkTSwap(fMat[2][3], fMat[3][2]);
+
+    if (!this->isTriviallyIdentity()) {
+        this->dirtyTypeMask();
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkMatrix44::mapScalars(const SkScalar src[4], SkScalar dst[4]) const {
+    SkScalar storage[4];
+    SkScalar* result = (src == dst) ? storage : dst;
+
     for (int i = 0; i < 4; i++) {
         SkMScalar value = 0;
         for (int j = 0; j < 4; j++) {
@@ -332,7 +600,190 @@
         }
         result[i] = SkMScalarToScalar(value);
     }
-    memcpy(dst, result, sizeof(result));
+
+    if (storage == result) {
+        memcpy(dst, storage, sizeof(storage));
+    }
+}
+
+#ifdef SK_MSCALAR_IS_DOUBLE
+
+void SkMatrix44::mapMScalars(const SkMScalar src[4], SkMScalar dst[4]) const {
+    SkMScalar storage[4];
+    SkMScalar* result = (src == dst) ? storage : dst;
+
+    for (int i = 0; i < 4; i++) {
+        SkMScalar value = 0;
+        for (int j = 0; j < 4; j++) {
+            value += fMat[j][i] * src[j];
+        }
+        result[i] = value;
+    }
+
+    if (storage == result) {
+        memcpy(dst, storage, sizeof(storage));
+    }
+}
+
+#endif
+
+typedef void (*Map2Procf)(const SkMScalar mat[][4], const float src2[], int count, float dst4[]);
+typedef void (*Map2Procd)(const SkMScalar mat[][4], const double src2[], int count, double dst4[]);
+
+static void map2_if(const SkMScalar mat[][4], const float* SK_RESTRICT src2,
+                    int count, float* SK_RESTRICT dst4) {
+    for (int i = 0; i < count; ++i) {
+        dst4[0] = src2[0];
+        dst4[1] = src2[1];
+        dst4[2] = 0;
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_id(const SkMScalar mat[][4], const double* SK_RESTRICT src2,
+                    int count, double* SK_RESTRICT dst4) {
+    for (int i = 0; i < count; ++i) {
+        dst4[0] = src2[0];
+        dst4[1] = src2[1];
+        dst4[2] = 0;
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_tf(const SkMScalar mat[][4], const float* SK_RESTRICT src2,
+                    int count, float* SK_RESTRICT dst4) {
+    const float mat30 = SkMScalarToFloat(mat[3][0]);
+    const float mat31 = SkMScalarToFloat(mat[3][1]);
+    const float mat32 = SkMScalarToFloat(mat[3][2]);
+    for (int n = 0; n < count; ++n) {
+        dst4[0] = src2[0] + mat30;
+        dst4[1] = src2[1] + mat31;
+        dst4[2] = mat32;
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_td(const SkMScalar mat[][4], const double* SK_RESTRICT src2,
+                    int count, double* SK_RESTRICT dst4) {
+    for (int n = 0; n < count; ++n) {
+        dst4[0] = src2[0] + mat[3][0];
+        dst4[1] = src2[1] + mat[3][1];
+        dst4[2] = mat[3][2];
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_sf(const SkMScalar mat[][4], const float* SK_RESTRICT src2,
+                    int count, float* SK_RESTRICT dst4) {
+    const float mat32 = SkMScalarToFloat(mat[3][2]);
+    for (int n = 0; n < count; ++n) {
+        dst4[0] = SkMScalarToFloat(mat[0][0] * src2[0] + mat[3][0]);
+        dst4[1] = SkMScalarToFloat(mat[1][1] * src2[1] + mat[3][1]);
+        dst4[2] = mat32;
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_sd(const SkMScalar mat[][4], const double* SK_RESTRICT src2,
+                    int count, double* SK_RESTRICT dst4) {
+    for (int n = 0; n < count; ++n) {
+        dst4[0] = mat[0][0] * src2[0] + mat[3][0];
+        dst4[1] = mat[1][1] * src2[1] + mat[3][1];
+        dst4[2] = mat[3][2];
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_af(const SkMScalar mat[][4], const float* SK_RESTRICT src2,
+                    int count, float* SK_RESTRICT dst4) {
+    double r;
+    for (int n = 0; n < count; ++n) {
+        double sx = src2[0];
+        double sy = src2[1];
+        r = mat[0][0] * sx + mat[1][0] * sy + mat[3][0];
+        dst4[0] = SkMScalarToFloat(r);
+        r = mat[0][1] * sx + mat[1][1] * sy + mat[3][1];
+        dst4[1] = SkMScalarToFloat(r);
+        r = mat[0][2] * sx + mat[1][2] * sy + mat[3][2];
+        dst4[2] = SkMScalarToFloat(r);
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_ad(const SkMScalar mat[][4], const double* SK_RESTRICT src2,
+                    int count, double* SK_RESTRICT dst4) {
+    for (int n = 0; n < count; ++n) {
+        double sx = src2[0];
+        double sy = src2[1];
+        dst4[0] = mat[0][0] * sx + mat[1][0] * sy + mat[3][0];
+        dst4[1] = mat[0][1] * sx + mat[1][1] * sy + mat[3][1];
+        dst4[2] = mat[0][2] * sx + mat[1][2] * sy + mat[3][2];
+        dst4[3] = 1;
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_pf(const SkMScalar mat[][4], const float* SK_RESTRICT src2,
+                    int count, float* SK_RESTRICT dst4) {
+    double r;
+    for (int n = 0; n < count; ++n) {
+        double sx = src2[0];
+        double sy = src2[1];
+        for (int i = 0; i < 4; i++) {
+            r = mat[0][i] * sx + mat[1][i] * sy + mat[3][i];
+            dst4[i] = SkMScalarToFloat(r);
+        }
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+static void map2_pd(const SkMScalar mat[][4], const double* SK_RESTRICT src2,
+                    int count, double* SK_RESTRICT dst4) {
+    for (int n = 0; n < count; ++n) {
+        double sx = src2[0];
+        double sy = src2[1];
+        for (int i = 0; i < 4; i++) {
+            dst4[i] = mat[0][i] * sx + mat[1][i] * sy + mat[3][i];
+        }
+        src2 += 2;
+        dst4 += 4;
+    }
+}
+
+void SkMatrix44::map2(const float src2[], int count, float dst4[]) const {
+    static const Map2Procf gProc[] = {
+        map2_if, map2_tf, map2_sf, map2_sf, map2_af, map2_af, map2_af, map2_af
+    };
+
+    TypeMask mask = this->getType();
+    Map2Procf proc = (mask & kPerspective_Mask) ? map2_pf : gProc[mask];
+    proc(fMat, src2, count, dst4);
+}
+
+void SkMatrix44::map2(const double src2[], int count, double dst4[]) const {
+    static const Map2Procd gProc[] = {
+        map2_id, map2_td, map2_sd, map2_sd, map2_ad, map2_ad, map2_ad, map2_ad
+    };
+
+    TypeMask mask = this->getType();
+    Map2Procd proc = (mask & kPerspective_Mask) ? map2_pd : gProc[mask];
+    proc(fMat, src2, count, dst4);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -357,6 +808,8 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+// TODO: make this support src' perspective elements
+//
 static void initFromMatrix(SkMScalar dst[4][4], const SkMatrix& src) {
     sk_bzero(dst, 16 * sizeof(SkMScalar));
     dst[0][0] = SkScalarToMScalar(src[SkMatrix::kMScaleX]);
@@ -374,9 +827,17 @@
 
 SkMatrix44& SkMatrix44::operator=(const SkMatrix& src) {
     initFromMatrix(fMat, src);
+
+    if (src.isIdentity()) {
+        this->setTypeMask(kIdentity_Mask);
+    } else {
+        this->dirtyTypeMask();
+    }
     return *this;
 }
 
+// TODO: make this support our perspective elements
+//
 SkMatrix44::operator SkMatrix() const {
     SkMatrix dst;
     dst.reset();    // setup our perspective correctly for identity
diff --git a/src/utils/SkMeshUtils.cpp b/src/utils/SkMeshUtils.cpp
index f7af383..3857dc9 100644
--- a/src/utils/SkMeshUtils.cpp
+++ b/src/utils/SkMeshUtils.cpp
@@ -55,11 +55,11 @@
                 *idx++ = index;
                 *idx++ = index + rows + 1;
                 *idx++ = index + 1;
-                
+
                 *idx++ = index + 1;
                 *idx++ = index + rows + 1;
                 *idx++ = index + rows + 2;
-                
+
                 index += 1;
             }
             index += 1;
@@ -89,7 +89,7 @@
                        int rows, int cols, const SkPoint verts[],
                        const SkColor colors[], const SkPaint& paint) {
     SkMeshIndices idx;
-    
+
     if (idx.init(bitmap.width(), bitmap.height(), rows, cols)) {
         SkPaint p(paint);
         p.setShader(SkShader::CreateBitmapShader(bitmap,
@@ -100,4 +100,3 @@
                              idx.indices(), idx.indexCount(), p);
     }
 }
-
diff --git a/src/utils/SkNWayCanvas.cpp b/src/utils/SkNWayCanvas.cpp
index 9f6ebc6..bfc7a7a 100644
--- a/src/utils/SkNWayCanvas.cpp
+++ b/src/utils/SkNWayCanvas.cpp
@@ -7,12 +7,15 @@
  */
 #include "SkNWayCanvas.h"
 
-SkNWayCanvas::SkNWayCanvas(int width, int height) {
+static SkBitmap make_noconfig_bm(int width, int height) {
     SkBitmap bm;
     bm.setConfig(SkBitmap::kNo_Config, width, height);
-    this->setBitmapDevice(bm);
+    return bm;
 }
 
+SkNWayCanvas::SkNWayCanvas(int width, int height)
+        : INHERITED(make_noconfig_bm(width, height)) {}
+
 SkNWayCanvas::~SkNWayCanvas() {
     this->removeAll();
 }
@@ -141,6 +144,14 @@
     return this->INHERITED::clipRect(rect, op, doAA);
 }
 
+bool SkNWayCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->clipRRect(rrect, op, doAA);
+    }
+    return this->INHERITED::clipRRect(rrect, op, doAA);
+}
+
 bool SkNWayCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
     Iter iter(fList);
     while (iter.next()) {
@@ -172,6 +183,13 @@
     }
 }
 
+void SkNWayCanvas::drawOval(const SkRect& rect, const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawOval(rect, paint);
+    }
+}
+
 void SkNWayCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
     Iter iter(fList);
     while (iter.next()) {
@@ -179,6 +197,13 @@
     }
 }
 
+void SkNWayCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    Iter iter(fList);
+    while (iter.next()) {
+        iter->drawRRect(rrect, paint);
+    }
+}
+
 void SkNWayCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
     Iter iter(fList);
     while (iter.next()) {
@@ -194,11 +219,11 @@
     }
 }
 
-void SkNWayCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+void SkNWayCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                                   const SkRect& dst, const SkPaint* paint) {
     Iter iter(fList);
     while (iter.next()) {
-        iter->drawBitmapRect(bitmap, src, dst, paint);
+        iter->drawBitmapRectToRect(bitmap, src, dst, paint);
     }
 }
 
@@ -286,5 +311,3 @@
     }
     return this->INHERITED::setDrawFilter(filter);
 }
-
-
diff --git a/src/utils/SkNinePatch.cpp b/src/utils/SkNinePatch.cpp
index 26ae8eb..9dc8bd2 100644
--- a/src/utils/SkNinePatch.cpp
+++ b/src/utils/SkNinePatch.cpp
@@ -15,11 +15,11 @@
     0, 5, 1,    0, 4, 5,
     1, 6, 2,    1, 5, 6,
     2, 7, 3,    2, 6, 7,
-    
+
     4, 9, 5,    4, 8, 9,
     5, 10, 6,   5, 9, 10,
     6, 11, 7,   6, 10, 11,
-    
+
     8, 13, 9,   8, 12, 13,
     9, 14, 10,  9, 13, 14,
     10, 15, 11, 10, 14, 15
@@ -27,18 +27,18 @@
 
 static int fillIndices(uint16_t indices[], int xCount, int yCount) {
     uint16_t* startIndices = indices;
-    
+
     int n = 0;
     for (int y = 0; y < yCount; y++) {
         for (int x = 0; x < xCount; x++) {
             *indices++ = n;
             *indices++ = n + xCount + 2;
             *indices++ = n + 1;
-            
+
             *indices++ = n;
             *indices++ = n + xCount + 1;
             *indices++ = n + xCount + 2;
-            
+
             n += 1;
         }
         n += 1;
@@ -108,14 +108,14 @@
     if (bounds.isEmpty() || bitmap.width() == 0 || bitmap.height() == 0) {
         return;
     }
-    
+
     // should try a quick-reject test before calling lockPixels
     SkAutoLockPixels alp(bitmap);
     // after the lock, it is valid to check
     if (!bitmap.readyToDraw()) {
         return;
     }
-    
+
     // check for degenerate divs (just an optimization, not required)
     {
         int i;
@@ -129,17 +129,17 @@
             numYDivs -= 1;
         }
     }
-    
+
     Mesh mesh;
-    
+
     const int numXStretch = (numXDivs + 1) >> 1;
     const int numYStretch = (numYDivs + 1) >> 1;
-    
+
     if (numXStretch < 1 && numYStretch < 1) {
         canvas->drawBitmapRect(bitmap, NULL, bounds, paint);
         return;
     }
-    
+
     if (false) {
         int i;
         for (i = 0; i < numXDivs; i++) {
@@ -149,9 +149,9 @@
             SkDebugf("--- ydivs[%d] %d\n", i, yDivs[i]);
         }
     }
-    
+
     SkScalar stretchX = 0, stretchY = 0;
-    
+
     if (numXStretch > 0) {
         int stretchSize = 0;
         for (int i = 1; i < numXDivs; i += 2) {
@@ -163,7 +163,7 @@
         else // reuse stretchX, but keep it negative as a signal
             stretchX = SkScalarDiv(-bounds.width(), fixed);
     }
-    
+
     if (numYStretch > 0) {
         int stretchSize = 0;
         for (int i = 1; i < numYDivs; i += 2) {
@@ -175,7 +175,7 @@
         else // reuse stretchX, but keep it negative as a signal
             stretchY = SkScalarDiv(-bounds.height(), fixed);
     }
-    
+
 #if 0
     SkDebugf("---- drawasamesh [%d %d] -> [%g %g] <%d %d> (%g %g)\n",
              bitmap.width(), bitmap.height(),
@@ -193,12 +193,12 @@
     SkPoint* verts = (SkPoint*)storage.get();
     SkPoint* texs = verts + vCount;
     uint16_t* indices = (uint16_t*)(texs + vCount);
-    
+
     mesh.fVerts = verts;
     mesh.fTexs = texs;
     mesh.fColors = NULL;
     mesh.fIndices = NULL;
-    
+
     // we use <= for YDivs, since the prebuild indices work for 3x2 and 3x1 too
     if (numXDivs == 2 && numYDivs <= 2) {
         mesh.fIndices = g3x3Indices;
@@ -207,7 +207,7 @@
         SkASSERT(n == indexCount);
         mesh.fIndices = indices;
     }
-    
+
     SkScalar vy = bounds.fTop;
     fillRow(verts, texs, vy, 0, bounds, xDivs, numXDivs,
             stretchX, bitmap.width());
@@ -235,7 +235,7 @@
     }
     fillRow(verts, texs, bounds.fBottom, SkIntToScalar(bitmap.height()),
             bounds, xDivs, numXDivs, stretchX, bitmap.width());
-    
+
     SkShader* shader = SkShader::CreateBitmapShader(bitmap,
                                                     SkShader::kClamp_TileMode,
                                                     SkShader::kClamp_TileMode);
@@ -310,7 +310,7 @@
     if (false /* is our canvas backed by a gpu?*/) {
         int32_t xDivs[2];
         int32_t yDivs[2];
-        
+
         xDivs[0] = margins.fLeft;
         xDivs[1] = bitmap.width() - margins.fRight;
         yDivs[0] = margins.fTop;
@@ -326,7 +326,7 @@
                 (margins.fTop + margins.fBottom);
             yDivs[1] = yDivs[0];
         }
-        
+
         SkNinePatch::DrawMesh(canvas, bounds, bitmap,
                               xDivs, 2, yDivs, 2, paint);
     } else {
diff --git a/src/utils/SkNullCanvas.cpp b/src/utils/SkNullCanvas.cpp
new file mode 100644
index 0000000..866a361
--- /dev/null
+++ b/src/utils/SkNullCanvas.cpp
@@ -0,0 +1,18 @@
+/*
+ * 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 "SkNullCanvas.h"
+
+#include "SkCanvas.h"
+#include "SkNWayCanvas.h"
+
+
+SkCanvas* SkCreateNullCanvas() {
+    // An N-Way canvas forwards calls to N canvas's. When N == 0 it's
+    // effectively a null canvas.
+    return SkNEW_ARGS(SkNWayCanvas, (0,0));
+}
diff --git a/src/utils/SkOSFile.cpp b/src/utils/SkOSFile.cpp
index 7c2b024..478a0cc 100644
--- a/src/utils/SkOSFile.cpp
+++ b/src/utils/SkOSFile.cpp
@@ -132,7 +132,7 @@
     return fHandle != (HANDLE)~0 && get_the_file(fHandle, name, dataPtr, getDir);
 }
 
-#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID)
+#elif defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_ANDROID) || defined(SK_BUILD_FOR_IOS)
 
 #if 0
 OSStatus FSPathMakeRef (
@@ -227,4 +227,3 @@
 }
 
 #endif
-
diff --git a/src/utils/SkParse.cpp b/src/utils/SkParse.cpp
index cb265c3..9609ddc 100644
--- a/src/utils/SkParse.cpp
+++ b/src/utils/SkParse.cpp
@@ -62,7 +62,7 @@
     return str;
 }
 
-int SkParse::Count(const char str[]) 
+int SkParse::Count(const char str[])
 {
     char c;
     int count = 0;
@@ -83,7 +83,7 @@
     return count;
 }
 
-int SkParse::Count(const char str[], char separator) 
+int SkParse::Count(const char str[], char separator)
 {
     char c;
     int count = 0;
@@ -234,7 +234,7 @@
 
     if (*str == '.')
     {
-        static const int gFractions[] = { (1 << 24)  / 10, (1 << 24)  / 100, (1 << 24)  / 1000, 
+        static const int gFractions[] = { (1 << 24)  / 10, (1 << 24)  / 100, (1 << 24)  / 1000,
             (1 << 24)  / 10000, (1 << 24)  / 100000 };
         str += 1;
         int d = 0;
@@ -330,7 +330,7 @@
 }
 
 #ifdef SK_SUPPORT_UNITTEST
-void SkParse::UnitTest() 
+void SkParse::UnitTest()
 {
     // !!! additional parse tests go here
     SkParse::TestColor();
diff --git a/src/utils/SkParseColor.cpp b/src/utils/SkParseColor.cpp
index db47aae..becad2c 100644
--- a/src/utils/SkParseColor.cpp
+++ b/src/utils/SkParseColor.cpp
@@ -537,4 +537,3 @@
 //  SkASSERT(result == ((0xFF << 24) | (71 << 16) | (162 << 8) | (253 << 0)));
 }
 #endif
-
diff --git a/src/utils/SkParsePath.cpp b/src/utils/SkParsePath.cpp
index 6030493..8c7a4d0 100644
--- a/src/utils/SkParsePath.cpp
+++ b/src/utils/SkParsePath.cpp
@@ -58,7 +58,7 @@
     return str;
 }
 
-static const char* find_scalar(const char str[], SkScalar* value, 
+static const char* find_scalar(const char str[], SkScalar* value,
                                bool isRelative, SkScalar relative) {
     str = SkParse::FindScalar(str, value);
     if (isRelative) {
@@ -103,7 +103,7 @@
                 op = 'L';
                 c = points[0];
                 break;
-            case 'L': 
+            case 'L':
                 data = find_points(data, points, 1, relative, &c);
                 path.lineTo(points[0]);
                 c = points[0];
@@ -120,10 +120,10 @@
                 path.lineTo(c.fX, y);
                 c.fY = y;
             } break;
-            case 'C': 
+            case 'C':
                 data = find_points(data, points, 3, relative, &c);
                 goto cubicCommon;
-            case 'S': 
+            case 'S':
                 data = find_points(data, &points[1], 2, relative, &c);
                 points[0] = c;
                 if (previousOp == 'C' || previousOp == 'S') {
@@ -191,7 +191,7 @@
 #ifdef SK_SCALAR_IS_FLOAT
     char buffer[64];
 #ifdef SK_BUILD_FOR_WIN32
-	int len = _snprintf(buffer, sizeof(buffer), "%g", value);
+    int len = _snprintf(buffer, sizeof(buffer), "%g", value);
 #else
     int len = snprintf(buffer, sizeof(buffer), "%g", value);
 #endif
@@ -220,7 +220,7 @@
     SkPoint         pts[4];
 
     for (;;) {
-        switch (iter.next(pts)) {
+        switch (iter.next(pts, false)) {
             case SkPath::kMove_Verb:
                 append_scalars(&stream, 'M', &pts[0].fX, 2);
                 break;
@@ -243,4 +243,3 @@
         }
     }
 }
-
diff --git a/src/utils/SkPictureUtils.cpp b/src/utils/SkPictureUtils.cpp
new file mode 100644
index 0000000..de4b440
--- /dev/null
+++ b/src/utils/SkPictureUtils.cpp
@@ -0,0 +1,225 @@
+/*
+ * 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 "SkPictureUtils.h"
+#include "SkCanvas.h"
+#include "SkData.h"
+#include "SkDevice.h"
+#include "SkPixelRef.h"
+#include "SkShader.h"
+#include "SkRRect.h"
+
+class PixelRefSet {
+public:
+    PixelRefSet(SkTDArray<SkPixelRef*>* array) : fArray(array) {}
+
+    // This does a linear search on existing pixelrefs, so if this list gets big
+    // we should use a more complex sorted/hashy thing.
+    //
+    void add(SkPixelRef* pr) {
+        uint32_t genID = pr->getGenerationID();
+        if (fGenID.find(genID) < 0) {
+            *fArray->append() = pr;
+            *fGenID.append() = genID;
+//            SkDebugf("--- adding [%d] %x %d\n", fArray->count() - 1, pr, genID);
+        } else {
+//            SkDebugf("--- already have %x %d\n", pr, genID);
+        }
+    }
+
+private:
+    SkTDArray<SkPixelRef*>* fArray;
+    SkTDArray<uint32_t>     fGenID;
+};
+
+static void not_supported() {
+    SkASSERT(!"this method should never be called");
+}
+
+static void nothing_to_do() {}
+
+/**
+ *  This device will route all bitmaps (primitives and in shaders) to its PRSet.
+ *  It should never actually draw anything, so there need not be any pixels
+ *  behind its device-bitmap.
+ */
+class GatherPixelRefDevice : public SkDevice {
+private:
+    PixelRefSet*  fPRSet;
+
+    void addBitmap(const SkBitmap& bm) {
+        fPRSet->add(bm.pixelRef());
+    }
+
+    void addBitmapFromPaint(const SkPaint& paint) {
+        SkShader* shader = paint.getShader();
+        if (shader) {
+            SkBitmap bm;
+            // Check whether the shader is a gradient in order to short-circuit
+            // call to asABitmap to prevent generation of bitmaps from
+            // gradient shaders, which implement asABitmap.
+            if (SkShader::kNone_GradientType == shader->asAGradient(NULL) &&
+                shader->asABitmap(&bm, NULL, NULL)) {
+                fPRSet->add(bm.pixelRef());
+            }
+        }
+    }
+
+public:
+    GatherPixelRefDevice(const SkBitmap& bm, PixelRefSet* prset) : SkDevice(bm) {
+        fPRSet = prset;
+    }
+
+    virtual void clear(SkColor color) SK_OVERRIDE {
+        nothing_to_do();
+    }
+    virtual void writePixels(const SkBitmap& bitmap, int x, int y,
+                             SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        not_supported();
+    }
+
+    virtual void drawPaint(const SkDraw&, const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawPoints(const SkDraw&, SkCanvas::PointMode mode, size_t count,
+                            const SkPoint[], const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawRect(const SkDraw&, const SkRect&,
+                          const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawOval(const SkDraw&, const SkRect&,
+                          const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawPath(const SkDraw&, const SkPath& path,
+                          const SkPaint& paint, const SkMatrix* prePathMatrix,
+                          bool pathIsMutable) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawBitmap(const SkDraw&, const SkBitmap& bitmap,
+                            const SkIRect* srcRectOrNull,
+                            const SkMatrix&, const SkPaint&) SK_OVERRIDE {
+        this->addBitmap(bitmap);
+    }
+    virtual void drawBitmapRect(const SkDraw&, const SkBitmap& bitmap,
+                                const SkRect* srcOrNull, const SkRect& dst,
+                                const SkPaint&) SK_OVERRIDE {
+        this->addBitmap(bitmap);
+    }
+    virtual void drawSprite(const SkDraw&, const SkBitmap& bitmap,
+                            int x, int y, const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmap(bitmap);
+    }
+    virtual void drawText(const SkDraw&, const void* text, size_t len,
+                          SkScalar x, SkScalar y,
+                          const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawPosText(const SkDraw&, const void* text, size_t len,
+                             const SkScalar pos[], SkScalar constY,
+                             int, const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawTextOnPath(const SkDraw&, const void* text, size_t len,
+                                const SkPath& path, const SkMatrix* matrix,
+                                const SkPaint& paint) SK_OVERRIDE {
+        this->addBitmapFromPaint(paint);
+    }
+    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 {
+        this->addBitmapFromPaint(paint);
+    }
+    virtual void drawDevice(const SkDraw&, SkDevice*, int x, int y,
+                            const SkPaint&) SK_OVERRIDE {
+        nothing_to_do();
+    }
+
+protected:
+    virtual bool onReadPixels(const SkBitmap& bitmap,
+                              int x, int y,
+                              SkCanvas::Config8888 config8888) SK_OVERRIDE {
+        not_supported();
+        return false;
+    }
+};
+
+class NoSaveLayerCanvas : public SkCanvas {
+public:
+    NoSaveLayerCanvas(SkDevice* device) : INHERITED(device) {}
+
+    // turn saveLayer() into save() for speed, should not affect correctness.
+    virtual int saveLayer(const SkRect* bounds, const SkPaint* paint,
+                          SaveFlags flags) SK_OVERRIDE {
+
+        // Like SkPictureRecord, we don't want to create layers, but we do need
+        // to respect the save and (possibly) its rect-clip.
+
+        int count = this->INHERITED::save(flags);
+        if (bounds) {
+            this->INHERITED::clipRectBounds(bounds, flags, NULL);
+        }
+        return count;
+    }
+
+    // disable aa for speed
+    virtual bool clipRect(const SkRect& rect, SkRegion::Op op,
+                          bool doAA) SK_OVERRIDE {
+        return this->INHERITED::clipRect(rect, op, false);
+    }
+
+    // for speed, just respect the bounds, and disable AA. May give us a few
+    // false positives and negatives.
+    virtual bool clipPath(const SkPath& path, SkRegion::Op op,
+                          bool doAA) SK_OVERRIDE {
+        return this->INHERITED::clipRect(path.getBounds(), op, false);
+    }
+    virtual bool clipRRect(const SkRRect& rrect, SkRegion::Op op,
+                           bool doAA) SK_OVERRIDE {
+        return this->INHERITED::clipRect(rrect.getBounds(), op, false);
+    }
+
+private:
+    typedef SkCanvas INHERITED;
+};
+
+SkData* SkPictureUtils::GatherPixelRefs(SkPicture* pict, const SkRect& area) {
+    if (NULL == pict) {
+        return NULL;
+    }
+
+    // this test also handles if either area or pict's width/height are empty
+    if (!SkRect::Intersects(area,
+                            SkRect::MakeWH(SkIntToScalar(pict->width()),
+                                           SkIntToScalar(pict->height())))) {
+        return NULL;
+    }
+
+    SkTDArray<SkPixelRef*> array;
+    PixelRefSet prset(&array);
+
+    SkBitmap emptyBitmap;
+    emptyBitmap.setConfig(SkBitmap::kARGB_8888_Config, pict->width(), pict->height());
+    // note: we do not set any pixels (shouldn't need to)
+
+    GatherPixelRefDevice device(emptyBitmap, &prset);
+    NoSaveLayerCanvas canvas(&device);
+
+    canvas.clipRect(area, SkRegion::kIntersect_Op, false);
+    canvas.drawPicture(*pict);
+
+    SkData* data = NULL;
+    int count = array.count();
+    if (count > 0) {
+        data = SkData::NewFromMalloc(array.detach(), count * sizeof(SkPixelRef*));
+    }
+    return data;
+}
diff --git a/src/utils/SkProxyCanvas.cpp b/src/utils/SkProxyCanvas.cpp
index bc21d52..057f1df 100644
--- a/src/utils/SkProxyCanvas.cpp
+++ b/src/utils/SkProxyCanvas.cpp
@@ -62,6 +62,10 @@
     return fProxy->clipRect(rect, op, doAA);
 }
 
+bool SkProxyCanvas::clipRRect(const SkRRect& rrect, SkRegion::Op op, bool doAA) {
+    return fProxy->clipRRect(rrect, op, doAA);
+}
+
 bool SkProxyCanvas::clipPath(const SkPath& path, SkRegion::Op op, bool doAA) {
     return fProxy->clipPath(path, op, doAA);
 }
@@ -79,10 +83,18 @@
     fProxy->drawPoints(mode, count, pts, paint);
 }
 
+void SkProxyCanvas::drawOval(const SkRect& rect, const SkPaint& paint) {
+    fProxy->drawOval(rect, paint);
+}
+
 void SkProxyCanvas::drawRect(const SkRect& rect, const SkPaint& paint) {
     fProxy->drawRect(rect, paint);
 }
 
+void SkProxyCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) {
+    fProxy->drawRRect(rrect, paint);
+}
+
 void SkProxyCanvas::drawPath(const SkPath& path, const SkPaint& paint) {
     fProxy->drawPath(path, paint);
 }
@@ -92,9 +104,9 @@
     fProxy->drawBitmap(bitmap, x, y, paint);
 }
 
-void SkProxyCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect* src,
+void SkProxyCanvas::drawBitmapRectToRect(const SkBitmap& bitmap, const SkRect* src,
                                    const SkRect& dst, const SkPaint* paint) {
-    fProxy->drawBitmapRect(bitmap, src, dst, paint);
+    fProxy->drawBitmapRectToRect(bitmap, src, dst, paint);
 }
 
 void SkProxyCanvas::drawBitmapMatrix(const SkBitmap& bitmap, const SkMatrix& m,
@@ -153,4 +165,3 @@
 SkDrawFilter* SkProxyCanvas::setDrawFilter(SkDrawFilter* filter) {
     return fProxy->setDrawFilter(filter);
 }
-
diff --git a/src/utils/SkRTConf.cpp b/src/utils/SkRTConf.cpp
new file mode 100644
index 0000000..c701552
--- /dev/null
+++ b/src/utils/SkRTConf.cpp
@@ -0,0 +1,283 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkRTConf.h"
+#include "SkOSFile.h"
+
+SkRTConfRegistry::SkRTConfRegistry(): fConfs(100) {
+
+    SkFILE *fp = sk_fopen(configFileLocation(), kRead_SkFILE_Flag);
+
+    if (!fp) {
+        return;
+    }
+
+    char line[1024];
+
+    while (!sk_feof(fp)) {
+
+        if (!sk_fgets(line, sizeof(line), fp)) {
+            break;
+        }
+
+        char *commentptr = strchr(line, '#');
+        if (commentptr == line) {
+            continue;
+        }
+        if (NULL != commentptr) {
+            *commentptr = '\0';
+        }
+
+        char sep[] = " \t\r\n";
+
+        char *keyptr = strtok(line, sep);
+        if (!keyptr) {
+            continue;
+        }
+
+        char *valptr = strtok(NULL, sep);
+        if (!valptr) {
+            continue;
+        }
+
+        SkString* key = new SkString(keyptr);
+        SkString* val = new SkString(valptr);
+
+        fConfigFileKeys.append(1, &key);
+        fConfigFileValues.append(1, &val);
+    }
+    sk_fclose(fp);
+}
+
+const char *SkRTConfRegistry::configFileLocation() const {
+    return "skia.conf"; // for now -- should probably do something fancier like home directories or whatever.
+}
+
+// dump all known runtime config options to the file with their default values.
+// to trigger this, make a config file of zero size.
+void SkRTConfRegistry::possiblyDumpFile() const {
+    const char *path = configFileLocation();
+    SkFILE *fp = sk_fopen(path, kRead_SkFILE_Flag);
+    if (!fp) {
+        return;
+    }
+    size_t configFileSize = sk_fgetsize(fp);
+    if (configFileSize == 0) {
+        printAll(path);
+    }
+    sk_fclose(fp);
+}
+
+// Run through every provided configuration option and print a warning if the user hasn't
+// declared a correponding configuration object somewhere.
+void SkRTConfRegistry::validate() const {
+    for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
+        if (fConfs.find(fConfigFileKeys[i]->c_str())) {
+            SkDebugf("WARNING: You have config value %s in your configuration file, but I've never heard of that.\n", fConfigFileKeys[i]->c_str());
+        }
+    }
+}
+
+void SkRTConfRegistry::printAll(const char *fname) const {
+    SkWStream *o;
+
+    if (NULL != fname) {
+        o = new SkFILEWStream(fname);
+    } else {
+        o = new SkDebugWStream();
+    }
+
+    ConfMap::Iter iter(fConfs);
+    SkTDArray<SkRTConfBase *> *confArray;
+
+    while (iter.next(&confArray)) {
+        if (confArray->getAt(0)->isDefault()) {
+            o->writeText("# ");
+        }
+        confArray->getAt(0)->print(o);
+        o->newline();
+    }
+
+    delete o;
+}
+
+void SkRTConfRegistry::printNonDefault(const char *fname) const {
+    SkWStream *o;
+
+    if (NULL != fname) {
+        o = new SkFILEWStream(fname);
+    } else {
+        o = new SkDebugWStream();
+    }
+    ConfMap::Iter iter(fConfs);
+    SkTDArray<SkRTConfBase *> *confArray;
+
+    while (iter.next(&confArray)) {
+        if (!confArray->getAt(0)->isDefault()) {
+            confArray->getAt(0)->print(o);
+            o->newline();
+        }
+    }
+
+    delete o;
+}
+
+// register a configuration variable after its value has been set by the parser.
+// we maintain a vector of these things instead of just a single one because the
+// user might set the value after initialization time and we need to have
+// all the pointers lying around, not just one.
+void SkRTConfRegistry::registerConf(SkRTConfBase *conf) {
+    SkTDArray<SkRTConfBase *> *confArray;
+    if (fConfs.find(conf->getName(), &confArray)) {
+        if (!conf->equals(confArray->getAt(0))) {
+            SkDebugf("WARNING: Skia config \"%s\" was registered more than once in incompatible ways.\n", conf->getName());
+        } else {
+            confArray->append(1, &conf);
+        }
+    } else {
+        confArray = new SkTDArray<SkRTConfBase *>;
+        confArray->append(1, &conf);
+        fConfs.set(conf->getName(),confArray);
+    }
+}
+
+template <typename T> T doParse(const char *s, bool *success ) {
+    SkDebugf("WARNING: Invoked non-specialized doParse function...\n");
+    if (success) {
+        *success = false;
+    }
+    return (T) 0;
+}
+
+template<> bool doParse<bool>(const char *s, bool *success) {
+    if (success) {
+        *success = true;
+    }
+    if (!strcmp(s,"1") || !strcmp(s,"true")) {
+        return true;
+    }
+    if (!strcmp(s,"0") || !strcmp(s,"false")) {
+        return false;
+    }
+    if (success) {
+        *success = false;
+    }
+    return false;
+}
+
+template<> const char * doParse<const char *>(const char * s, bool *success) {
+    if (success) {
+        *success = true;
+    }
+    return s;
+}
+
+template<> int doParse<int>(const char * s, bool *success) {
+    return atoi(s);
+}
+
+template<> unsigned int doParse<unsigned int>(const char * s, bool *success) {
+    return (unsigned int) atoi(s);
+}
+
+template<> float doParse<float>(const char * s, bool *success) {
+    return (float) atof(s);
+}
+
+template<> double doParse<double>(const char * s, bool *success) {
+    return atof(s);
+}
+
+static inline void str_replace(char *s, char search, char replace) {
+    for (char *ptr = s ; *ptr ; ptr++) {
+        if (*ptr == search) {
+            *ptr = replace;
+        }
+    }
+}
+
+template<typename T> bool SkRTConfRegistry::parse(const char *name, T* value) {
+    SkString *str = NULL;
+
+    for (int i = 0 ; i < fConfigFileKeys.count() ; i++) {
+        if (fConfigFileKeys[i]->equals(name)) {
+            str = fConfigFileValues[i];
+        }
+    }
+
+    SkString environment_variable("skia.");
+    environment_variable.append(name);
+
+    const char *environment_value = getenv(environment_variable.c_str());
+    if (environment_value) {
+        str->set(environment_value);
+    } else {
+        // apparently my shell doesn't let me have environment variables that
+        // have periods in them, so also let the user substitute underscores.
+        SkString underscore_environment_variable("skia_");
+        char *underscore_name = SkStrDup(name);
+        str_replace(underscore_name,'.','_');
+        underscore_environment_variable.append(underscore_name);
+        sk_free(underscore_name);
+        environment_value = getenv(underscore_environment_variable.c_str());
+        if (environment_value) {
+            str->set(environment_value);
+        }
+    }
+
+    if (!str) {
+        return false;
+    }
+
+    bool success;
+    T new_value = doParse<T>(str->c_str(),&success);
+    if (success) {
+        *value = new_value;
+    } else {
+        SkDebugf("WARNING: Couldn't parse value \'%s\' for variable \'%s\'\n", str->c_str(), name);
+    }
+    return success;
+}
+
+// need to explicitly instantiate the parsing function for every config type we might have...
+
+template bool SkRTConfRegistry::parse(const char *name, bool *value);
+template bool SkRTConfRegistry::parse(const char *name, int *value);
+template bool SkRTConfRegistry::parse(const char *name, unsigned int *value);
+template bool SkRTConfRegistry::parse(const char *name, float *value);
+template bool SkRTConfRegistry::parse(const char *name, double *value);
+template bool SkRTConfRegistry::parse(const char *name, const char **value);
+
+template <typename T> void SkRTConfRegistry::set(const char *name, T value) {
+
+    SkTDArray<SkRTConfBase *> *confArray;
+    if (!fConfs.find(name, &confArray)) {
+        SkDebugf("WARNING: Attempting to set configuration value \"%s\", but I've never heard of that.\n", name);
+        return;
+    }
+
+    for (SkRTConfBase **confBase = confArray->begin(); confBase != confArray->end(); confBase++) {
+        // static_cast here is okay because there's only one kind of child class.
+        SkRTConf<T> *concrete = static_cast<SkRTConf<T> *>(*confBase);
+
+        if (concrete) {
+            concrete->set(value);
+        }
+    }
+}
+
+template void SkRTConfRegistry::set(const char *name, bool value);
+template void SkRTConfRegistry::set(const char *name, int value);
+template void SkRTConfRegistry::set(const char *name, unsigned int value);
+template void SkRTConfRegistry::set(const char *name, float value);
+template void SkRTConfRegistry::set(const char *name, double value);
+template void SkRTConfRegistry::set(const char *name, char * value);
+
+SkRTConfRegistry &skRTConfRegistry() {
+    static SkRTConfRegistry r;
+    return r;
+}
diff --git a/src/utils/SkSHA1.cpp b/src/utils/SkSHA1.cpp
new file mode 100644
index 0000000..1abac06
--- /dev/null
+++ b/src/utils/SkSHA1.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * The following code is based on the description in RFC 3174.
+ * http://www.ietf.org/rfc/rfc3174.txt
+ */
+
+#include "SkTypes.h"
+#include "SkSHA1.h"
+#include <string.h>
+
+/** SHA1 basic transformation. Transforms state based on block. */
+static void transform(uint32_t state[5], const uint8_t block[64]);
+
+/** Encodes input into output (5 big endian 32 bit values). */
+static void encode(uint8_t output[20], const uint32_t input[5]);
+
+/** Encodes input into output (big endian 64 bit value). */
+static void encode(uint8_t output[8], const uint64_t input);
+
+SkSHA1::SkSHA1() : byteCount(0) {
+    // These are magic numbers from the specification. The first four are the same as MD5.
+    this->state[0] = 0x67452301;
+    this->state[1] = 0xefcdab89;
+    this->state[2] = 0x98badcfe;
+    this->state[3] = 0x10325476;
+    this->state[4] = 0xc3d2e1f0;
+}
+
+void SkSHA1::update(const uint8_t* input, size_t inputLength) {
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int bufferAvailable = 64 - bufferIndex;
+
+    unsigned int inputIndex;
+    if (inputLength >= bufferAvailable) {
+        if (bufferIndex) {
+            memcpy(&this->buffer[bufferIndex], input, bufferAvailable);
+            transform(this->state, this->buffer);
+            inputIndex = bufferAvailable;
+        } else {
+            inputIndex = 0;
+        }
+
+        for (; inputIndex + 63 < inputLength; inputIndex += 64) {
+            transform(this->state, &input[inputIndex]);
+        }
+
+        bufferIndex = 0;
+    } else {
+        inputIndex = 0;
+    }
+
+    memcpy(&this->buffer[bufferIndex], &input[inputIndex], inputLength - inputIndex);
+
+    this->byteCount += inputLength;
+}
+
+void SkSHA1::finish(Digest& digest) {
+    // Get the number of bits before padding.
+    uint8_t bits[8];
+    encode(bits, this->byteCount << 3);
+
+    // Pad out to 56 mod 64.
+    unsigned int bufferIndex = (unsigned int)(this->byteCount & 0x3F);
+    unsigned int paddingLength = (bufferIndex < 56) ? (56 - bufferIndex) : (120 - bufferIndex);
+    static uint8_t PADDING[64] = {
+        0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+           0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+    };
+    this->update(PADDING, paddingLength);
+
+    // Append length (length before padding, will cause final update).
+    this->update(bits, 8);
+
+    // Write out digest.
+    encode(digest.data, this->state);
+
+#if defined(SK_SHA1_CLEAR_DATA)
+    // Clear state.
+    memset(this, 0, sizeof(*this));
+#endif
+}
+
+struct F1 { uint32_t operator()(uint32_t B, uint32_t C, uint32_t D) {
+    return (B & C) | ((~B) & D);
+    //return D ^ (B & (C ^ D));
+    //return (B & C) ^ ((~B) & D);
+    //return (B & C) + ((~B) & D);
+    //return _mm_or_ps(_mm_andnot_ps(B, D), _mm_and_ps(B, C)); //SSE2
+    //return vec_sel(D, C, B); //PPC
+}};
+
+struct F2 { uint32_t operator()(uint32_t B, uint32_t C, uint32_t D) {
+    return B ^ C ^ D;
+}};
+
+struct F3 { uint32_t operator()(uint32_t B, uint32_t C, uint32_t D) {
+    return (B & C) | (B & D) | (C & D);
+    //return (B & C) | (D & (B | C));
+    //return (B & C) | (D & (B ^ C));
+    //return (B & C) + (D & (B ^ C));
+    //return (B & C) ^ (B & D) ^ (C & D);
+}};
+
+/** Rotates x left n bits. */
+static inline uint32_t rotate_left(uint32_t x, uint8_t n) {
+    return (x << n) | (x >> (32 - n));
+}
+
+template <typename T>
+static inline void operation(T operation,
+                             uint32_t A, uint32_t& B, uint32_t C, uint32_t D, uint32_t& E,
+                             uint32_t w, uint32_t k) {
+    E += rotate_left(A, 5) + operation(B, C, D) + w + k;
+    B = rotate_left(B, 30);
+}
+
+static void transform(uint32_t state[5], const uint8_t block[64]) {
+    uint32_t A = state[0], B = state[1], C = state[2], D = state[3], E = state[4];
+
+    // Round constants defined in SHA-1.
+    static const uint32_t K[] = {
+        0x5A827999, //sqrt(2) * 2^30
+        0x6ED9EBA1, //sqrt(3) * 2^30
+        0x8F1BBCDC, //sqrt(5) * 2^30
+        0xCA62C1D6, //sqrt(10) * 2^30
+    };
+
+    uint32_t W[80];
+
+    // Initialize the array W.
+    size_t i = 0;
+    for (size_t j = 0; i < 16; ++i, j += 4) {
+        W[i] = (((uint32_t)block[j  ]) << 24) |
+               (((uint32_t)block[j+1]) << 16) |
+               (((uint32_t)block[j+2]) <<  8) |
+               (((uint32_t)block[j+3])      );
+    }
+    for (; i < 80; ++i) {
+       W[i] = rotate_left(W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16], 1);
+       //The following is equivelent and speeds up SSE implementations, but slows non-SSE.
+       //W[i] = rotate_left(W[i-6] ^ W[i-16] ^ W[i-28] ^ W[i-32], 2);
+    }
+
+    // Round 1
+    operation(F1(), A, B, C, D, E, W[ 0], K[0]);
+    operation(F1(), E, A, B, C, D, W[ 1], K[0]);
+    operation(F1(), D, E, A, B, C, W[ 2], K[0]);
+    operation(F1(), C, D, E, A, B, W[ 3], K[0]);
+    operation(F1(), B, C, D, E, A, W[ 4], K[0]);
+    operation(F1(), A, B, C, D, E, W[ 5], K[0]);
+    operation(F1(), E, A, B, C, D, W[ 6], K[0]);
+    operation(F1(), D, E, A, B, C, W[ 7], K[0]);
+    operation(F1(), C, D, E, A, B, W[ 8], K[0]);
+    operation(F1(), B, C, D, E, A, W[ 9], K[0]);
+    operation(F1(), A, B, C, D, E, W[10], K[0]);
+    operation(F1(), E, A, B, C, D, W[11], K[0]);
+    operation(F1(), D, E, A, B, C, W[12], K[0]);
+    operation(F1(), C, D, E, A, B, W[13], K[0]);
+    operation(F1(), B, C, D, E, A, W[14], K[0]);
+    operation(F1(), A, B, C, D, E, W[15], K[0]);
+    operation(F1(), E, A, B, C, D, W[16], K[0]);
+    operation(F1(), D, E, A, B, C, W[17], K[0]);
+    operation(F1(), C, D, E, A, B, W[18], K[0]);
+    operation(F1(), B, C, D, E, A, W[19], K[0]);
+
+    // Round 2
+    operation(F2(), A, B, C, D, E, W[20], K[1]);
+    operation(F2(), E, A, B, C, D, W[21], K[1]);
+    operation(F2(), D, E, A, B, C, W[22], K[1]);
+    operation(F2(), C, D, E, A, B, W[23], K[1]);
+    operation(F2(), B, C, D, E, A, W[24], K[1]);
+    operation(F2(), A, B, C, D, E, W[25], K[1]);
+    operation(F2(), E, A, B, C, D, W[26], K[1]);
+    operation(F2(), D, E, A, B, C, W[27], K[1]);
+    operation(F2(), C, D, E, A, B, W[28], K[1]);
+    operation(F2(), B, C, D, E, A, W[29], K[1]);
+    operation(F2(), A, B, C, D, E, W[30], K[1]);
+    operation(F2(), E, A, B, C, D, W[31], K[1]);
+    operation(F2(), D, E, A, B, C, W[32], K[1]);
+    operation(F2(), C, D, E, A, B, W[33], K[1]);
+    operation(F2(), B, C, D, E, A, W[34], K[1]);
+    operation(F2(), A, B, C, D, E, W[35], K[1]);
+    operation(F2(), E, A, B, C, D, W[36], K[1]);
+    operation(F2(), D, E, A, B, C, W[37], K[1]);
+    operation(F2(), C, D, E, A, B, W[38], K[1]);
+    operation(F2(), B, C, D, E, A, W[39], K[1]);
+
+    // Round 3
+    operation(F3(), A, B, C, D, E, W[40], K[2]);
+    operation(F3(), E, A, B, C, D, W[41], K[2]);
+    operation(F3(), D, E, A, B, C, W[42], K[2]);
+    operation(F3(), C, D, E, A, B, W[43], K[2]);
+    operation(F3(), B, C, D, E, A, W[44], K[2]);
+    operation(F3(), A, B, C, D, E, W[45], K[2]);
+    operation(F3(), E, A, B, C, D, W[46], K[2]);
+    operation(F3(), D, E, A, B, C, W[47], K[2]);
+    operation(F3(), C, D, E, A, B, W[48], K[2]);
+    operation(F3(), B, C, D, E, A, W[49], K[2]);
+    operation(F3(), A, B, C, D, E, W[50], K[2]);
+    operation(F3(), E, A, B, C, D, W[51], K[2]);
+    operation(F3(), D, E, A, B, C, W[52], K[2]);
+    operation(F3(), C, D, E, A, B, W[53], K[2]);
+    operation(F3(), B, C, D, E, A, W[54], K[2]);
+    operation(F3(), A, B, C, D, E, W[55], K[2]);
+    operation(F3(), E, A, B, C, D, W[56], K[2]);
+    operation(F3(), D, E, A, B, C, W[57], K[2]);
+    operation(F3(), C, D, E, A, B, W[58], K[2]);
+    operation(F3(), B, C, D, E, A, W[59], K[2]);
+
+    // Round 4
+    operation(F2(), A, B, C, D, E, W[60], K[3]);
+    operation(F2(), E, A, B, C, D, W[61], K[3]);
+    operation(F2(), D, E, A, B, C, W[62], K[3]);
+    operation(F2(), C, D, E, A, B, W[63], K[3]);
+    operation(F2(), B, C, D, E, A, W[64], K[3]);
+    operation(F2(), A, B, C, D, E, W[65], K[3]);
+    operation(F2(), E, A, B, C, D, W[66], K[3]);
+    operation(F2(), D, E, A, B, C, W[67], K[3]);
+    operation(F2(), C, D, E, A, B, W[68], K[3]);
+    operation(F2(), B, C, D, E, A, W[69], K[3]);
+    operation(F2(), A, B, C, D, E, W[70], K[3]);
+    operation(F2(), E, A, B, C, D, W[71], K[3]);
+    operation(F2(), D, E, A, B, C, W[72], K[3]);
+    operation(F2(), C, D, E, A, B, W[73], K[3]);
+    operation(F2(), B, C, D, E, A, W[74], K[3]);
+    operation(F2(), A, B, C, D, E, W[75], K[3]);
+    operation(F2(), E, A, B, C, D, W[76], K[3]);
+    operation(F2(), D, E, A, B, C, W[77], K[3]);
+    operation(F2(), C, D, E, A, B, W[78], K[3]);
+    operation(F2(), B, C, D, E, A, W[79], K[3]);
+
+    state[0] += A;
+    state[1] += B;
+    state[2] += C;
+    state[3] += D;
+    state[4] += E;
+
+#if defined(SK_SHA1_CLEAR_DATA)
+    // Clear sensitive information.
+    memset(W, 0, sizeof(W));
+#endif
+}
+
+static void encode(uint8_t output[20], const uint32_t input[5]) {
+    for (size_t i = 0, j = 0; i < 5; i++, j += 4) {
+        output[j  ] = (uint8_t)((input[i] >> 24) & 0xff);
+        output[j+1] = (uint8_t)((input[i] >> 16) & 0xff);
+        output[j+2] = (uint8_t)((input[i] >>  8) & 0xff);
+        output[j+3] = (uint8_t)((input[i]      ) & 0xff);
+    }
+}
+
+static void encode(uint8_t output[8], const uint64_t input) {
+    output[0] = (uint8_t)((input >> 56) & 0xff);
+    output[1] = (uint8_t)((input >> 48) & 0xff);
+    output[2] = (uint8_t)((input >> 40) & 0xff);
+    output[3] = (uint8_t)((input >> 32) & 0xff);
+    output[4] = (uint8_t)((input >> 24) & 0xff);
+    output[5] = (uint8_t)((input >> 16) & 0xff);
+    output[6] = (uint8_t)((input >>  8) & 0xff);
+    output[7] = (uint8_t)((input      ) & 0xff);
+}
diff --git a/src/utils/SkSHA1.h b/src/utils/SkSHA1.h
new file mode 100644
index 0000000..10bbc43
--- /dev/null
+++ b/src/utils/SkSHA1.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkSHA1_DEFINED
+#define SkSHA1_DEFINED
+
+#include "SkTypes.h"
+#include "SkEndian.h"
+#include "SkStream.h"
+
+//The following macros can be defined to affect the SHA1 code generated.
+//SK_SHA1_CLEAR_DATA causes all intermediate state to be overwritten with 0's.
+//SK_CPU_BENDIAN allows 32 bit <=> 8 bit conversions without copies (if alligned).
+//SK_CPU_FAST_UNALIGNED_ACCESS allows 32 bit <=> 8 bit conversions without copies if SK_CPU_BENDIAN.
+
+class SkSHA1 : SkWStream {
+public:
+    SkSHA1();
+
+    /** Processes input, adding it to the digest.
+     *  Note that this treats the buffer as a series of uint8_t values.
+     */
+    virtual bool write(const void* buffer, size_t size) SK_OVERRIDE {
+        update(reinterpret_cast<const uint8_t*>(buffer), size);
+        return true;
+    }
+
+    /** Processes input, adding it to the digest. Calling this after finish is undefined. */
+    void update(const uint8_t* input, size_t length);
+
+    struct Digest {
+        uint8_t data[20];
+    };
+
+    /** Computes and returns the digest. */
+    void finish(Digest& digest);
+
+private:
+    // number of bytes, modulo 2^64
+    uint64_t byteCount;
+
+    // state (ABCDE)
+    uint32_t state[5];
+
+    // input buffer
+    uint8_t buffer[64];
+};
+
+#endif
diff --git a/src/utils/SkSfntUtils.cpp b/src/utils/SkSfntUtils.cpp
deleted file mode 100644
index 54f7cc3..0000000
--- a/src/utils/SkSfntUtils.cpp
+++ /dev/null
@@ -1,94 +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 "SkEndian.h"
-#include "SkSfntUtils.h"
-
-static uint16_t parse_be16(const uint8_t*& p) {
-    uint16_t value = (p[0] << 8) | p[1];
-    p += 2;
-    return value;
-}
-
-static uint32_t parse_be32(const uint8_t*& p) {
-    uint32_t value = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-    p += 4;
-    return value;
-}
-
-static Sk64 parse_be64(const uint8_t*& p) {
-    Sk64 value;
-    value.fHi = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
-    value.fLo = (p[4] << 24) | (p[5] << 16) | (p[6] << 8) | p[7];
-    p += 8;
-    return value;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-
-bool SkSfntUtils::ReadTable_head(SkFontID fontID, SkSfntTable_head* head) {
-    static const uint32_t gTag = SkSetFourByteTag('h', 'e', 'a', 'd');
-    static const size_t gSize = 54;
-    
-    uint8_t storage[gSize];
-    size_t size = SkFontHost::GetTableData(fontID, gTag, 0, gSize, storage);
-    if (size != gSize) {
-        return false;
-    }
-    
-    const uint8_t* p = storage;
-    head->fVersion = parse_be32(p);
-    head->fRevision = parse_be32(p);
-    head->fCheckSumAdjustment = parse_be32(p);
-    head->fMagicNumber = parse_be32(p);
-    head->fFlags = parse_be16(p);
-    head->fUnitsPerEm = parse_be16(p);
-    head->fDateCreated = parse_be64(p);
-    head->fDateModified = parse_be64(p);
-    head->fXMin = parse_be16(p);
-    head->fXMin = parse_be16(p);
-    head->fXMin = parse_be16(p);
-    head->fXMin = parse_be16(p);
-    head->fMacStyle = parse_be16(p);
-    head->fLowestPPEM = parse_be16(p);
-    head->fFontDirectionHint = parse_be16(p);
-    head->fIndexToLocFormat = parse_be16(p);
-    head->fGlyphDataFormat = parse_be16(p);
-    SkASSERT(p - storage == (long)size);
-    return true;
-}
-
-bool SkSfntUtils::ReadTable_maxp(SkFontID fontID, SkSfntTable_maxp* maxp) {
-    static const uint32_t gTag = SkSetFourByteTag('m', 'a', 'x', 'p');
-    static const size_t gSize = 32;
-    
-    uint8_t storage[gSize];
-    size_t size = SkFontHost::GetTableData(fontID, gTag, 0, gSize, storage);
-    if (size != gSize) {
-        return false;
-    }
-    
-    const uint8_t* p = storage;
-    maxp->fVersion = parse_be32(p);
-    maxp->fNumGlyphs = parse_be16(p);
-    maxp->fMaxPoints = parse_be16(p);
-    maxp->fMaxContours = parse_be16(p);
-    maxp->fMaxComponentPoints = parse_be16(p);
-    maxp->fMaxComponentContours = parse_be16(p);
-    maxp->fMaxZones = parse_be16(p);
-    maxp->fMaxTwilightPoints = parse_be16(p);
-    maxp->fMaxStorage = parse_be16(p);
-    maxp->fMaxFunctionDefs = parse_be16(p);
-    maxp->fMaxInstructionDefs = parse_be16(p);
-    maxp->fMaxStackElements = parse_be16(p);
-    maxp->fMaxSizeOfInstructions = parse_be16(p);
-    maxp->fMaxComponentElements = parse_be16(p);
-    maxp->fMaxComponentDepth = parse_be16(p);
-    SkASSERT(p - storage == (long)size);
-    return true;
-}
-
diff --git a/src/utils/SkThreadPool.cpp b/src/utils/SkThreadPool.cpp
new file mode 100644
index 0000000..78cb417
--- /dev/null
+++ b/src/utils/SkThreadPool.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "SkThreadPool.h"
+#include "SkRunnable.h"
+#include "SkThreadUtils.h"
+
+SkThreadPool::SkThreadPool(const int count)
+: fDone(false) {
+    // Create count threads, all running SkThreadPool::Loop.
+    for (int i = 0; i < count; i++) {
+        SkThread* thread = SkNEW_ARGS(SkThread, (&SkThreadPool::Loop, this));
+        *fThreads.append() = thread;
+        thread->start();
+    }
+}
+
+SkThreadPool::~SkThreadPool() {
+    fDone = true;
+    fReady.lock();
+    fReady.broadcast();
+    fReady.unlock();
+
+    // Wait for all threads to stop.
+    for (int i = 0; i < fThreads.count(); i++) {
+        fThreads[i]->join();
+        SkDELETE(fThreads[i]);
+    }
+}
+
+/*static*/ void SkThreadPool::Loop(void* arg) {
+    // The SkThreadPool passes itself as arg to each thread as they're created.
+    SkThreadPool* pool = static_cast<SkThreadPool*>(arg);
+
+    while (true) {
+        // We have to be holding the lock to read the queue and to call wait.
+        pool->fReady.lock();
+        while(pool->fQueue.isEmpty()) {
+            // Is it time to die?
+            if (pool->fDone) {
+                pool->fReady.unlock();
+                return;
+            }
+            // wait yields the lock while waiting, but will have it again when awoken.
+            pool->fReady.wait();
+        }
+        // We've got the lock back here, no matter if we ran wait or not.
+
+        // The queue is not empty, so we have something to run.  Claim it.
+        LinkedRunnable* r = pool->fQueue.tail();
+
+        pool->fQueue.remove(r);
+
+        // Having claimed our SkRunnable, we now give up the lock while we run it.
+        // Otherwise, we'd only ever do work on one thread at a time, which rather
+        // defeats the point of this code.
+        pool->fReady.unlock();
+
+        // OK, now really do the work.
+        r->fRunnable->run();
+        SkDELETE(r);
+    }
+
+    SkASSERT(false); // Unreachable.  The only exit happens when pool->fDone.
+}
+
+void SkThreadPool::add(SkRunnable* r) {
+    if (NULL == r) {
+        return;
+    }
+
+    // If we don't have any threads, obligingly just run the thing now.
+    if (fThreads.isEmpty()) {
+        return r->run();
+    }
+
+    // We have some threads.  Queue it up!
+    fReady.lock();
+    LinkedRunnable* linkedRunnable = SkNEW(LinkedRunnable);
+    linkedRunnable->fRunnable = r;
+    fQueue.addToHead(linkedRunnable);
+    fReady.signal();
+    fReady.unlock();
+}
diff --git a/src/utils/SkThreadUtils.h b/src/utils/SkThreadUtils.h
new file mode 100644
index 0000000..8963981
--- /dev/null
+++ b/src/utils/SkThreadUtils.h
@@ -0,0 +1,46 @@
+/*
+ * 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 SkThreadUtils_DEFINED
+#define SkThreadUtils_DEFINED
+
+#include "SkTypes.h"
+
+class SkThread : SkNoncopyable {
+public:
+    typedef void (*entryPointProc)(void*);
+
+    SkThread(entryPointProc entryPoint, void* data = NULL);
+
+    /**
+     * Non-virtual, do not subclass.
+     */
+    ~SkThread();
+
+    /**
+     * Starts the thread. Returns false if the thread could not be started.
+     */
+    bool start();
+
+    /**
+     * Waits for the thread to finish.
+     * If the thread has not started, returns immediately.
+     */
+    void join();
+
+    /**
+     * SkThreads with an affinity for the same processor will attempt to run cache
+     * locally with each other. SkThreads with an affinity for different processors
+     * will attempt to run on different cores. Returns false if the request failed.
+     */
+    bool setProcessorAffinity(unsigned int processor);
+
+private:
+    void* fData;
+};
+
+#endif
diff --git a/src/utils/SkThreadUtils_pthread.cpp b/src/utils/SkThreadUtils_pthread.cpp
new file mode 100644
index 0000000..7dec907
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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 "SkTypes.h"
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_pthread.h"
+
+#include <pthread.h>
+#include <signal.h>
+
+PThreadEvent::PThreadEvent() : fConditionFlag(false) {
+    pthread_cond_init(&fCondition, NULL);
+    pthread_mutex_init(&fConditionMutex, NULL);
+}
+PThreadEvent::~PThreadEvent() {
+    pthread_mutex_destroy(&fConditionMutex);
+    pthread_cond_destroy(&fCondition);
+}
+void PThreadEvent::trigger() {
+    pthread_mutex_lock(&fConditionMutex);
+    fConditionFlag = true;
+    pthread_cond_signal(&fCondition);
+    pthread_mutex_unlock(&fConditionMutex);
+}
+void PThreadEvent::wait() {
+    pthread_mutex_lock(&fConditionMutex);
+    while (!fConditionFlag) {
+        pthread_cond_wait(&fCondition, &fConditionMutex);
+    }
+    pthread_mutex_unlock(&fConditionMutex);
+}
+bool PThreadEvent::isTriggered() {
+    bool currentFlag;
+    pthread_mutex_lock(&fConditionMutex);
+    currentFlag = fConditionFlag;
+    pthread_mutex_unlock(&fConditionMutex);
+    return currentFlag;
+}
+
+SkThread_PThreadData::SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data)
+    : fPThread()
+    , fValidPThread(false)
+    , fParam(data)
+    , fEntryPoint(entryPoint)
+{
+    pthread_attr_init(&fAttr);
+    pthread_attr_setdetachstate(&fAttr, PTHREAD_CREATE_JOINABLE);
+}
+
+SkThread_PThreadData::~SkThread_PThreadData() {
+    pthread_attr_destroy(&fAttr);
+}
+
+static void* thread_start(void* arg) {
+    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(arg);
+    // Wait for start signal
+    pthreadData->fStarted.wait();
+
+    // Call entry point only if thread was not canceled before starting.
+    if (!pthreadData->fCanceled.isTriggered()) {
+        pthreadData->fEntryPoint(pthreadData->fParam);
+    }
+    return NULL;
+}
+
+SkThread::SkThread(entryPointProc entryPoint, void* data) {
+    SkThread_PThreadData* pthreadData = new SkThread_PThreadData(entryPoint, data);
+    fData = pthreadData;
+
+    int ret = pthread_create(&(pthreadData->fPThread),
+                             &(pthreadData->fAttr),
+                             thread_start,
+                             pthreadData);
+
+    pthreadData->fValidPThread = (0 == ret);
+}
+
+SkThread::~SkThread() {
+    if (fData != NULL) {
+        SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+        // If created thread but start was never called, kill the thread.
+        if (pthreadData->fValidPThread && !pthreadData->fStarted.isTriggered()) {
+            pthreadData->fCanceled.trigger();
+            if (this->start()) {
+                this->join();
+            }
+        }
+        delete pthreadData;
+    }
+}
+
+bool SkThread::start() {
+    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+    if (!pthreadData->fValidPThread) {
+        return false;
+    }
+
+    if (pthreadData->fStarted.isTriggered()) {
+        return false;
+    }
+    pthreadData->fStarted.trigger();
+    return true;
+}
+
+void SkThread::join() {
+    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+    if (!pthreadData->fValidPThread || !pthreadData->fStarted.isTriggered()) {
+        return;
+    }
+
+    pthread_join(pthreadData->fPThread, NULL);
+}
diff --git a/src/utils/SkThreadUtils_pthread.h b/src/utils/SkThreadUtils_pthread.h
new file mode 100644
index 0000000..3e10202
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread.h
@@ -0,0 +1,43 @@
+/*
+ * 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 SkThreadUtils_PThreadData_DEFINED
+#define SkThreadUtils_PThreadData_DEFINED
+
+#include "SkThreadUtils.h"
+#include <pthread.h>
+
+class PThreadEvent : SkNoncopyable {
+public:
+    PThreadEvent();
+    ~PThreadEvent();
+    void trigger();
+    void wait();
+    bool isTriggered();
+
+private:
+    pthread_cond_t fCondition;
+    pthread_mutex_t fConditionMutex;
+    bool fConditionFlag;
+};
+
+class SkThread_PThreadData : SkNoncopyable {
+public:
+    SkThread_PThreadData(SkThread::entryPointProc entryPoint, void* data);
+    ~SkThread_PThreadData();
+    pthread_t fPThread;
+    bool fValidPThread;
+    PThreadEvent fStarted;
+    PThreadEvent fCanceled;
+
+    pthread_attr_t fAttr;
+
+    void* fParam;
+    SkThread::entryPointProc fEntryPoint;
+};
+
+#endif
diff --git a/src/utils/SkThreadUtils_pthread_linux.cpp b/src/utils/SkThreadUtils_pthread_linux.cpp
new file mode 100644
index 0000000..4a03cb8
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread_linux.cpp
@@ -0,0 +1,46 @@
+/*
+ * 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 _GNU_SOURCE
+#define _GNU_SOURCE //for pthread_setaffinity_np
+#endif
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_pthread.h"
+
+#include <pthread.h>
+
+static int nth_set_cpu(unsigned int n, cpu_set_t* cpuSet) {
+    n %= CPU_COUNT(cpuSet);
+    for (unsigned int setCpusSeen = 0, currentCpu = 0; true; ++currentCpu) {
+        if (CPU_ISSET(currentCpu, cpuSet)) {
+            ++setCpusSeen;
+            if (setCpusSeen > n) {
+                return currentCpu;
+            }
+        }
+    }
+}
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+    if (!pthreadData->fValidPThread) {
+        return false;
+    }
+
+    cpu_set_t parentCpuset;
+    if (0 != pthread_getaffinity_np(pthread_self(), sizeof(cpu_set_t), &parentCpuset)) {
+        return false;
+    }
+
+    cpu_set_t cpuset;
+    CPU_ZERO(&cpuset);
+    CPU_SET(nth_set_cpu(processor, &parentCpuset), &cpuset);
+    return 0 == pthread_setaffinity_np(pthreadData->fPThread,
+                                       sizeof(cpu_set_t),
+                                       &cpuset);
+}
diff --git a/src/utils/SkThreadUtils_pthread_mach.cpp b/src/utils/SkThreadUtils_pthread_mach.cpp
new file mode 100644
index 0000000..0f6e263
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread_mach.cpp
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_pthread.h"
+
+#include <mach/mach.h>
+#include <mach/thread_policy.h>
+#include <pthread.h>
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+    SkThread_PThreadData* pthreadData = static_cast<SkThread_PThreadData*>(fData);
+    if (!pthreadData->fValidPThread) {
+        return false;
+    }
+
+    mach_port_t tid = pthread_mach_thread_np(pthreadData->fPThread);
+
+    thread_affinity_policy_data_t policy;
+    policy.affinity_tag = processor;
+
+    return 0 == thread_policy_set(tid,
+                                  THREAD_AFFINITY_POLICY,
+                                  (thread_policy_t) &policy,
+                                  THREAD_AFFINITY_POLICY_COUNT);
+}
diff --git a/src/utils/SkThreadUtils_pthread_other.cpp b/src/utils/SkThreadUtils_pthread_other.cpp
new file mode 100644
index 0000000..a3973f1
--- /dev/null
+++ b/src/utils/SkThreadUtils_pthread_other.cpp
@@ -0,0 +1,12 @@
+/*
+ * 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 "SkThreadUtils.h"
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+    return false;
+}
diff --git a/src/utils/SkThreadUtils_win.cpp b/src/utils/SkThreadUtils_win.cpp
new file mode 100644
index 0000000..208ffde
--- /dev/null
+++ b/src/utils/SkThreadUtils_win.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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 "SkTypes.h"
+
+#include "SkThreadUtils.h"
+#include "SkThreadUtils_win.h"
+
+SkThread_WinData::SkThread_WinData(SkThread::entryPointProc entryPoint, void* data)
+    : fHandle(NULL)
+    , fParam(data)
+    , fThreadId(0)
+    , fEntryPoint(entryPoint)
+    , fStarted(false)
+{
+    fCancelEvent = CreateEvent(
+        NULL,  // default security attributes
+        false, //auto reset
+        false, //not signaled
+        NULL); //no name
+}
+
+SkThread_WinData::~SkThread_WinData() {
+    CloseHandle(fCancelEvent);
+}
+
+static DWORD WINAPI thread_start(LPVOID data) {
+    SkThread_WinData* winData = static_cast<SkThread_WinData*>(data);
+
+    //See if this thread was canceled before starting.
+    if (WaitForSingleObject(winData->fCancelEvent, 0) == WAIT_OBJECT_0) {
+        return 0;
+    }
+
+    winData->fEntryPoint(winData->fParam);
+    return 0;
+}
+
+SkThread::SkThread(entryPointProc entryPoint, void* data) {
+    SkThread_WinData* winData = new SkThread_WinData(entryPoint, data);
+    fData = winData;
+
+    if (NULL == winData->fCancelEvent) {
+        return;
+    }
+
+    winData->fHandle = CreateThread(
+        NULL,                   // default security attributes
+        0,                      // use default stack size
+        thread_start,           // thread function name (proxy)
+        winData,                // argument to thread function (proxy args)
+        CREATE_SUSPENDED,       // create suspended so affinity can be set
+        &winData->fThreadId);   // returns the thread identifier
+}
+
+SkThread::~SkThread() {
+    if (fData != NULL) {
+        SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+        // If created thread but start was never called, kill the thread.
+        if (winData->fHandle != NULL && !winData->fStarted) {
+            if (SetEvent(winData->fCancelEvent) != 0) {
+                if (this->start()) {
+                    this->join();
+                }
+            } else {
+                //kill with prejudice
+                TerminateThread(winData->fHandle, -1);
+            }
+        }
+        delete winData;
+    }
+}
+
+bool SkThread::start() {
+    SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+    if (NULL == winData->fHandle) {
+        return false;
+    }
+
+    if (winData->fStarted) {
+        return false;
+    }
+    winData->fStarted = -1 != ResumeThread(winData->fHandle);
+    return winData->fStarted;
+}
+
+void SkThread::join() {
+    SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+    if (NULL == winData->fHandle || !winData->fStarted) {
+        return;
+    }
+
+    WaitForSingleObject(winData->fHandle, INFINITE);
+}
+
+static unsigned int num_bits_set(DWORD_PTR mask) {
+    unsigned int count;
+    for (count = 0; mask; ++count) {
+        mask &= mask - 1;
+    }
+    return count;
+}
+
+static unsigned int nth_set_bit(unsigned int n, DWORD_PTR mask) {
+    n %= num_bits_set(mask);
+    for (unsigned int setBitsSeen = 0, currentBit = 0; true; ++currentBit) {
+        if (mask & (1 << currentBit)) {
+            ++setBitsSeen;
+            if (setBitsSeen > n) {
+                return currentBit;
+            }
+        }
+    }
+}
+
+bool SkThread::setProcessorAffinity(unsigned int processor) {
+    SkThread_WinData* winData = static_cast<SkThread_WinData*>(fData);
+    if (NULL == winData->fHandle) {
+        return false;
+    }
+
+    DWORD_PTR processAffinityMask;
+    DWORD_PTR systemAffinityMask;
+    if (0 == GetProcessAffinityMask(GetCurrentProcess(),
+                                    &processAffinityMask,
+                                    &systemAffinityMask)) {
+        return false;
+    }
+
+    DWORD_PTR threadAffinityMask = 1 << nth_set_bit(processor, processAffinityMask);
+    return 0 != SetThreadAffinityMask(winData->fHandle, threadAffinityMask);
+}
diff --git a/src/utils/SkThreadUtils_win.h b/src/utils/SkThreadUtils_win.h
new file mode 100644
index 0000000..51b32fa
--- /dev/null
+++ b/src/utils/SkThreadUtils_win.h
@@ -0,0 +1,28 @@
+/*
+ * 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 SkThreadUtils_WinData_DEFINED
+#define SkThreadUtils_WinData_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkThreadUtils.h"
+
+class SkThread_WinData : SkNoncopyable {
+public:
+    SkThread_WinData(SkThread::entryPointProc entryPoint, void* data);
+    ~SkThread_WinData();
+    HANDLE fHandle;
+    HANDLE fCancelEvent;
+
+    LPVOID fParam;
+    DWORD fThreadId;
+    SkThread::entryPointProc fEntryPoint;
+    bool fStarted;
+};
+
+#endif
diff --git a/src/utils/SkUnitMappers.cpp b/src/utils/SkUnitMappers.cpp
index 583d091..ceff9ca 100644
--- a/src/utils/SkUnitMappers.cpp
+++ b/src/utils/SkUnitMappers.cpp
@@ -6,6 +6,9 @@
  * found in the LICENSE file.
  */
 #include "SkUnitMappers.h"
+#include "SkFlattenableBuffers.h"
+
+SK_DEFINE_INST_COUNT(SkUnitMapper)
 
 SkDiscreteMapper::SkDiscreteMapper(int segments) {
     if (segments < 2) {
@@ -29,22 +32,14 @@
 
 SkDiscreteMapper::SkDiscreteMapper(SkFlattenableReadBuffer& rb)
         : SkUnitMapper(rb) {
-    fSegments = rb.readU32();
-    fScale = rb.readU32();
+    fSegments = rb.readInt();
+    fScale = rb.read32();
 }
 
-SkFlattenable::Factory SkDiscreteMapper::getFactory() {
-    return Create;
-}
-
-SkFlattenable* SkDiscreteMapper::Create(SkFlattenableReadBuffer& rb) {
-    return SkNEW_ARGS(SkDiscreteMapper, (rb));
-}
-
-void SkDiscreteMapper::flatten(SkFlattenableWriteBuffer& wb) {
+void SkDiscreteMapper::flatten(SkFlattenableWriteBuffer& wb) const {
     this->INHERITED::flatten(wb);
 
-    wb.write32(fSegments);
+    wb.writeInt(fSegments);
     wb.write32(fScale);
 }
 
@@ -64,12 +59,3 @@
 
 SkCosineMapper::SkCosineMapper(SkFlattenableReadBuffer& rb)
     : SkUnitMapper(rb) {}
-
-SkFlattenable::Factory SkCosineMapper::getFactory() {
-    return Create;
-}
-
-SkFlattenable* SkCosineMapper::Create(SkFlattenableReadBuffer& rb) {
-    return SkNEW_ARGS(SkCosineMapper, (rb));
-}
-
diff --git a/src/utils/android/ashmem.c b/src/utils/android/ashmem.c
new file mode 100644
index 0000000..0e1e816
--- /dev/null
+++ b/src/utils/android/ashmem.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2008 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.
+ */
+
+/*
+ * 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 <android/ashmem.h>
+
+#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>
+
+#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);
+}
diff --git a/src/utils/android/ashmem.h b/src/utils/android/ashmem.h
new file mode 100644
index 0000000..2d6c0b5
--- /dev/null
+++ b/src/utils/android/ashmem.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2008 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 _CUTILS_ASHMEM_H
+#define _CUTILS_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);
+
+#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  /* _CUTILS_ASHMEM_H */
diff --git a/src/utils/cityhash/README b/src/utils/cityhash/README
new file mode 100644
index 0000000..d939288
--- /dev/null
+++ b/src/utils/cityhash/README
@@ -0,0 +1,2 @@
+This directory contains files needed to build third_party/externals/cityhash
+(such as the config.h file that would normally be created by autoconf)
diff --git a/src/utils/cityhash/config.h b/src/utils/cityhash/config.h
new file mode 100644
index 0000000..bd36416
--- /dev/null
+++ b/src/utils/cityhash/config.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Converts from Skia build flags to the macro definitions cityhash normally
+ * gets from autoconf.
+ */
+
+#include "SkTypes.h"
+
+#ifdef SK_CPU_BENDIAN
+  #define WORDS_BIGENDIAN 1
+#endif
diff --git a/src/utils/ios/SkFontHost_iOS.mm b/src/utils/ios/SkFontHost_iOS.mm
new file mode 100755
index 0000000..60f111c
--- /dev/null
+++ b/src/utils/ios/SkFontHost_iOS.mm
@@ -0,0 +1,262 @@
+#import <UIKit/UIKit.h>
+
+#include "SkStream_NSData.h"
+#include "SkTypeface.h"
+#include "SkFontHost.h"
+#include "SkThread.h"
+#include "SkTemplates.h"
+
+enum FontDesign {
+    kUnknown_Design,
+    kSans_FontDesign,
+    kSerif_FontDesign,
+
+    kIllegal_FontDesign,    // never use with a real font
+};
+
+// returns kIllegal_FontDesign if not found
+static FontDesign find_design_from_name(const char name[]) {
+    static const struct {
+        const char* fName;
+        FontDesign  fDesign;
+    } gRec[] = {
+        { "sans-serif", kSans_FontDesign },
+        { "serif",      kSerif_FontDesign },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
+        if (!strcasecmp(name, gRec[i].fName)) {
+            return gRec[i].fDesign;
+        }
+    }
+    return kIllegal_FontDesign;
+}
+
+struct FontRes {
+    const char*         fName;
+    SkTypeface::Style   fStyle;
+    FontDesign          fDesign;
+};
+
+static const FontRes gFontRes[] = {
+    { "DroidSans",          SkTypeface::kNormal,    kSans_FontDesign    },
+    { "DroidSans",          SkTypeface::kBold,      kSans_FontDesign    },
+    { "DroidSerif-Regular", SkTypeface::kNormal,    kSerif_FontDesign    },
+    { "DroidSerif-Bold",    SkTypeface::kBold,      kSerif_FontDesign    },
+//    { "PescaderoPro",       SkTypeface::kNormal,    kSerif_FontDesign   },
+//    { "PescaderoPro-Bold",  SkTypeface::kBold,      kSerif_FontDesign   },
+};
+#define FONTRES_COUNT SK_ARRAY_COUNT(gFontRes)
+
+#define DEFAULT_INDEX_REGULAR   1
+#define DEFAULT_INDEX_BOLD      2
+
+///////////////////////////////////////////////////////////////////////////////
+
+class SkTypeface_Stream : public SkTypeface {
+public:
+    SkTypeface_Stream(SkStream* stream, Style style);
+    virtual ~SkTypeface_Stream();
+
+    SkStream* refStream() {
+        fStream->ref();
+        return fStream;
+    }
+
+private:
+    SkStream*   fStream;
+};
+
+static int32_t gUniqueFontID;
+
+SkTypeface_Stream::SkTypeface_Stream(SkStream* stream, Style style)
+: SkTypeface(style, sk_atomic_inc(&gUniqueFontID) + 1) {
+    fStream = stream;
+    fStream->ref();
+}
+
+SkTypeface_Stream::~SkTypeface_Stream() {
+    fStream->unref();
+}
+
+static SkTypeface_Stream* create_from_fontres(const FontRes& res) {
+    SkStream* stream = SkStream_NSData::CreateFromResource(res.fName, "ttf");
+    SkAutoUnref aur(stream);
+
+    return SkNEW_ARGS(SkTypeface_Stream, (stream, res.fStyle));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static int compute_style_distance(SkTypeface::Style a, SkTypeface::Style b) {
+    int dist = 0;
+    int diff = a ^ b;
+    if (diff & SkTypeface::kBold) {
+        dist += 2;
+    }
+    if (diff & SkTypeface::kItalic) {
+        dist += 1;
+    }
+    return dist;
+}
+
+static SkTypeface_Stream* gFonts[FONTRES_COUNT];
+
+static void assure_init_fonts() {
+    static bool gOnce;
+    if (!gOnce) {
+        for (size_t i = 0; i < FONTRES_COUNT; i++) {
+            gFonts[i] = create_from_fontres(gFontRes[i]);
+            gOnce = true;
+        }
+    }
+}
+
+static SkTypeface_Stream* get_default_font(SkTypeface::Style style) {
+    assure_init_fonts();
+
+    if (style & SkTypeface::kBold) {
+        return gFonts[DEFAULT_INDEX_BOLD];
+    } else {
+        return gFonts[DEFAULT_INDEX_REGULAR];
+    }
+}
+
+static SkTypeface_Stream* find_by_id(SkFontID fontID) {
+    assure_init_fonts();
+    
+    for (size_t i = 0; i < FONTRES_COUNT; i++) {
+        if (gFonts[i]->uniqueID() == fontID) {
+            return gFonts[i];
+        }
+    }
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+template <typename T> T* ref_and_return(T* obj) {
+    obj->ref();
+    return obj;
+}
+
+SkTypeface* SkFontHost::CreateTypeface(const SkTypeface* familyFace,
+                                     const char familyName[],
+                                     const void* data, size_t bytelength,
+                                     SkTypeface::Style style) {
+    assure_init_fonts();
+
+    if (familyName) {
+        FontDesign design = find_design_from_name(familyName);
+        if (kIllegal_FontDesign != design) {
+            familyName = "$#@*&%*#$@ never match any name";
+        }
+
+        int bestDistance = 999;
+        int bestIndex = -1;
+        for (size_t i = 0; i < FONTRES_COUNT; i++) {
+            if (design == gFontRes[i].fDesign || !strcmp(gFontRes[i].fName, familyName)) {
+                int dist = compute_style_distance(style, gFontRes[i].fStyle);
+                if (dist < bestDistance) {
+                    bestDistance = dist;
+                    bestIndex = i;
+                }
+            }
+        }
+        if (bestIndex >= 0) {
+            return ref_and_return(gFonts[bestIndex]);
+        }
+    }
+
+    return ref_and_return(get_default_font(style));
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromStream(SkStream* stream) {
+    SkASSERT(!"SkFontHost::CreateTypeface unimplemented");
+    return NULL;
+}
+
+SkTypeface* SkFontHost::CreateTypefaceFromFile(char const*) {
+//    SkASSERT(!"SkFontHost::CreateTypefaceFromFile unimplemented");
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkStream* SkFontHost::OpenStream(uint32_t uniqueID) {
+    SkTypeface_Stream* tf = find_by_id(uniqueID);
+    SkASSERT(tf);
+    return tf->refStream();
+}
+
+size_t SkFontHost::GetFileName(SkFontID fontID, char path[], size_t length,
+                               int32_t* index) {
+    SkDebugf("SkFontHost::GetFileName unimplemented\n");
+    return 0;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void SkFontHost::Serialize(const SkTypeface* face, SkWStream* stream) {
+    SkASSERT(!"SkFontHost::Serialize unimplemented");
+}
+
+SkTypeface* SkFontHost::Deserialize(SkStream* stream) {
+    int style = stream->readU8();
+    int len = stream->readPackedUInt();
+    const char* name = NULL;
+    if (len > 0) {
+        SkString str;
+        str.resize(len);
+        stream->read(str.writable_str(), len);
+        
+        if (str.startsWith("DroidSans")) {
+            name = "sans-serif";
+        } else if (str.startsWith("DroidSerif")) {
+            name = "serif";
+        }
+        SkDebugf("---- deserialize typeface <%s> %d %s\n", str.c_str(), style, name);
+    }
+//    name = NULL; style = 0;
+    return SkFontHost::CreateTypeface(NULL, name, NULL, NULL,
+                                      (SkTypeface::Style)style);
+}
+
+SkFontID SkFontHost::NextLogicalFont(SkFontID currFontID, SkFontID origFontID) {
+    return 0;
+}
+
+#define FONT_CACHE_MEMORY_BUDGET    1 * 1024 * 1024
+
+size_t SkFontHost::ShouldPurgeFontCache(size_t sizeAllocatedSoFar) {
+    if (sizeAllocatedSoFar > FONT_CACHE_MEMORY_BUDGET)
+        return sizeAllocatedSoFar - FONT_CACHE_MEMORY_BUDGET;
+    else
+        return 0;   // nothing to do
+}
+
+///////////////////////////////////////////////////////////////////////////////
+int SkFontHost::ComputeGammaFlag(const SkPaint& paint) {
+    return 0;
+}
+
+void SkFontHost::GetGammaTables(const uint8_t* tables[2]) {
+    tables[0] = NULL;   // black gamma (e.g. exp=1.4)
+    tables[1] = NULL;   // white gamma (e.g. exp= 1/1.4)
+}
+
+// static
+SkAdvancedTypefaceMetrics* SkFontHost::GetAdvancedTypefaceMetrics(
+                                                                  uint32_t fontID,
+                                                                  SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo) {
+    SkASSERT(!"SkFontHost::GetAdvancedTypefaceMetrics unimplemented");
+    return NULL;
+}
+
+void SkFontHost::FilterRec(SkScalerContext::Rec* rec, SkTypeface*) {
+}
+
+SkScalerContext* SkFontHost::CreateScalerContext(const SkDescriptor* desc) {
+    SkASSERT(!"SkFontHost::CreateScalarContext unimplemented");
+    return NULL;
+}
\ No newline at end of file
diff --git a/src/utils/ios/SkImageDecoder_iOS.mm b/src/utils/ios/SkImageDecoder_iOS.mm
new file mode 100755
index 0000000..77d49a5
--- /dev/null
+++ b/src/utils/ios/SkImageDecoder_iOS.mm
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#import <CoreGraphics/CoreGraphics.h>
+#include <CoreGraphics/CGColorSpace.h>
+#import <UIKit/UIKit.h>
+
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkMovie.h"
+#include "SkStream_NSData.h"
+
+class SkImageDecoder_iOS : public SkImageDecoder {
+protected:
+    virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode);
+};
+
+#define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast)
+
+bool SkImageDecoder_iOS::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
+
+    NSData* data = NSData_dataWithStream(stream);
+
+    UIImage* uimage = [UIImage imageWithData:data];
+    
+    const int width = uimage.size.width;
+    const int height = uimage.size.height;
+    bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    if (SkImageDecoder::kDecodeBounds_Mode == mode) {
+        return true;
+    }
+    
+    if (!this->allocPixelRef(bm, NULL)) {
+        return false;
+    }
+    
+    bm->lockPixels();
+    bm->eraseColor(0);
+    
+    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+    CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height,
+                                            8, bm->rowBytes(), cs, BITMAP_INFO);
+    CGContextDrawImage(cg, CGRectMake(0, 0, width, height), uimage.CGImage);
+    CGContextRelease(cg);
+    CGColorSpaceRelease(cs);
+    
+    bm->unlockPixels();
+    return true;
+}
+
+/////////////////////////////////////////////////////////////////////////
+
+SkImageDecoder* SkImageDecoder::Factory(SkStream* stream) {
+    return new SkImageDecoder_iOS;
+}
+
+SkMovie* SkMovie::DecodeStream(SkStream* stream) {
+    return NULL;
+}
+
+SkImageEncoder* SkImageEncoder::Create(Type t) {
+    return NULL;
+}
+
diff --git a/src/utils/ios/SkOSFile_iOS.mm b/src/utils/ios/SkOSFile_iOS.mm
new file mode 100755
index 0000000..a685761
--- /dev/null
+++ b/src/utils/ios/SkOSFile_iOS.mm
@@ -0,0 +1,98 @@
+/*
+ * 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 <Foundation/Foundation.h>
+#include "SkOSFile.h"
+#include "SkString.h"
+
+struct SkFILE {
+    NSData* fData;
+    size_t  fOffset;
+    size_t  fLength;
+};
+
+SkFILE* sk_fopen(const char cpath[], SkFILE_Flags flags) {
+    if (flags & kWrite_SkFILE_Flag) {
+        return NULL;
+    }
+
+    SkString cname, csuffix;
+
+    const char* start = strrchr(cpath, '/');
+    if (NULL == start) {
+        start = cpath;
+    } else {
+        start += 1;
+    }
+    const char* stop = strrchr(cpath, '.');
+    if (NULL == stop) {
+        return NULL;
+    } else {
+        stop += 1;
+    }
+
+    cname.set(start, stop - start - 1);
+    csuffix.set(stop);
+
+    NSBundle* bundle = [NSBundle mainBundle];
+    NSString* name = [NSString stringWithUTF8String:cname.c_str()];
+    NSString* suffix = [NSString stringWithUTF8String:csuffix.c_str()];
+    NSString* path = [bundle pathForResource:name ofType:suffix];
+    NSData* data = [NSData dataWithContentsOfMappedFile:path];
+
+    if (data) {
+        [data retain];
+        SkFILE* rec = new SkFILE;
+        rec->fData = data;
+        rec->fOffset = 0;
+        rec->fLength = [data length];
+        return reinterpret_cast<SkFILE*>(rec);
+    }
+    return NULL;
+}
+
+size_t sk_fgetsize(SkFILE* rec) {
+    SkASSERT(rec);
+    return rec->fLength;
+}
+
+bool sk_frewind(SkFILE* rec) {
+    SkASSERT(rec);
+    rec->fOffset = 0;
+    return true;
+}
+
+size_t sk_fread(void* buffer, size_t byteCount, SkFILE* rec) {
+    if (NULL == buffer) {
+        return rec->fLength;
+    } else {
+        size_t remaining = rec->fLength - rec->fOffset;
+        if (byteCount > remaining) {
+            byteCount = remaining;
+        }
+        memcpy(buffer, (char*)[rec->fData bytes] + rec->fOffset, byteCount);
+        rec->fOffset += byteCount;
+        SkASSERT(rec->fOffset <= rec->fLength);
+        return byteCount;
+    }
+}
+
+size_t sk_fwrite(const void* buffer, size_t byteCount, SkFILE* f) {
+    SkASSERT(!"Not supported yet");
+    return 0;
+}
+
+void sk_fflush(SkFILE* f) {
+    SkASSERT(!"Not supported yet");
+}
+
+void sk_fclose(SkFILE* rec) {
+    SkASSERT(rec);
+    [rec->fData release];
+    delete rec;
+}
+
diff --git a/src/utils/ios/SkStream_NSData.mm b/src/utils/ios/SkStream_NSData.mm
new file mode 100755
index 0000000..ef20f63
--- /dev/null
+++ b/src/utils/ios/SkStream_NSData.mm
@@ -0,0 +1,44 @@
+/*
+ * 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 "SkStream_NSData.h"
+
+NSData* NSData_dataWithStream(SkStream* stream) {
+    size_t length = stream->getLength();
+    void* src = malloc(length);
+    size_t bytes = stream->read(src, length);
+    SkASSERT(bytes == length);
+    return [NSData dataWithBytesNoCopy:src length:length freeWhenDone:YES];
+}
+
+NSData* NSData_dataFromResource(const char cname[], const char csuffix[]) {
+    NSBundle* bundle = [NSBundle mainBundle];
+    NSString* name = [NSString stringWithUTF8String:cname];
+    NSString* suffix = [NSString stringWithUTF8String:csuffix];
+    NSString* path = [bundle pathForResource:name ofType:suffix];
+    return [NSData dataWithContentsOfMappedFile:path];
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+SkStream_NSData::SkStream_NSData(NSData* data) {
+    fNSData = data;
+    [fNSData retain];
+
+    this->setMemory([fNSData bytes], [fNSData length], false);
+}
+
+SkStream_NSData::~SkStream_NSData() {
+    [fNSData release];
+}
+
+SkStream_NSData* SkStream_NSData::CreateFromResource(const char name[],
+                                                     const char suffix[]) {
+    NSData* data = NSData_dataFromResource(name, suffix);
+    return SkNEW_ARGS(SkStream_NSData, (data));
+}
+
diff --git a/src/utils/mac/SkBitmap_Mac.cpp b/src/utils/mac/SkBitmap_Mac.cpp
new file mode 100644
index 0000000..151dc9b
--- /dev/null
+++ b/src/utils/mac/SkBitmap_Mac.cpp
@@ -0,0 +1,149 @@
+/*
+ * 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 "SkBitmap.h"
+#include "SkColorPriv.h"
+#include "SkMath.h"
+
+#if defined(SK_BUILD_FOR_MAC)
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#ifndef __ppc__
+    #define SWAP_16BIT
+#endif
+
+static void convertGL32_to_Mac32(uint32_t dst[], const SkBitmap& bm) {
+    memcpy(dst, bm.getPixels(), bm.getSize());
+    return;
+
+    uint32_t* stop = dst + (bm.getSize() >> 2);
+    const uint8_t* src = (const uint8_t*)bm.getPixels();
+    while (dst < stop) {
+        *dst++ = src[2] << 24 | src[1] << 16 | src[0] << 8 | src[3] << 0;
+        src += sizeof(uint32_t);
+    }
+}
+
+static void convert565_to_32(uint32_t dst[], const SkBitmap& bm) {
+    for (int y = 0; y < bm.height(); y++) {
+        const uint16_t* src = bm.getAddr16(0, y);
+        const uint16_t* stop = src + bm.width();
+        while (src < stop) {
+            unsigned c = *src++;
+            unsigned r = SkPacked16ToR32(c);
+            unsigned g = SkPacked16ToG32(c);
+            unsigned b = SkPacked16ToB32(c);
+
+            *dst++ = (b << 24) | (g << 16) | (r << 8) | 0xFF;
+        }
+    }
+}
+
+static void convert4444_to_555(uint16_t dst[], const uint16_t src[], int count)
+{
+    const uint16_t* stop = src + count;
+
+    while (src < stop)
+    {
+        unsigned c = *src++;
+
+        unsigned r = SkGetPackedR4444(c);
+        unsigned g = SkGetPackedG4444(c);
+        unsigned b = SkGetPackedB4444(c);
+        // convert to 5 bits
+        r = (r << 1) | (r >> 3);
+        g = (g << 1) | (g >> 3);
+        b = (b << 1) | (b >> 3);
+        // build the 555
+        c = (r << 10) | (g << 5) | b;
+
+#ifdef SWAP_16BIT
+        c = (c >> 8) | (c << 8);
+#endif
+        *dst++ = c;
+    }
+}
+
+#include "SkTemplates.h"
+
+static CGImageRef bitmap2imageref(const SkBitmap& bm) {
+    size_t  bitsPerComp;
+    size_t  bitsPerPixel;
+    CGBitmapInfo info;
+    CGColorSpaceRef cs = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
+    CGDataProviderRef data = CGDataProviderCreateWithData(NULL,
+                                                           bm.getPixels(),
+                                                           bm.getSize(),
+                                                           NULL);
+    SkAutoTCallVProc<CGDataProvider, CGDataProviderRelease> acp(data);
+    SkAutoTCallVProc<CGColorSpace, CGColorSpaceRelease> acp2(cs);
+
+    switch (bm.config()) {
+        case SkBitmap::kARGB_8888_Config:
+            bitsPerComp = 8;
+            bitsPerPixel = 32;
+            info = kCGImageAlphaPremultipliedLast;
+            break;
+        case SkBitmap::kARGB_4444_Config:
+            bitsPerComp = 4;
+            bitsPerPixel = 16;
+            info = kCGImageAlphaPremultipliedLast |  kCGBitmapByteOrder16Little;
+            break;
+#if 0   // not supported by quartz !!!
+        case SkBitmap::kRGB_565_Config:
+            bitsPerComp = 5;
+            bitsPerPixel = 16;
+            info = kCGImageAlphaNone | kCGBitmapByteOrder16Little;
+            break;
+#endif
+        default:
+            return NULL;
+    }
+
+    return CGImageCreate(bm.width(), bm.height(), bitsPerComp, bitsPerPixel,
+                         bm.rowBytes(), cs, info, data,
+                         NULL, false, kCGRenderingIntentDefault);
+}
+
+void SkBitmap::drawToPort(WindowRef wind, CGContextRef cg) const {
+    if (fPixels == NULL || fWidth == 0 || fHeight == 0) {
+        return;
+    }
+
+    bool useQD = false;
+    if (NULL == cg) {
+        SetPortWindowPort(wind);
+        QDBeginCGContext(GetWindowPort(wind), &cg);
+        useQD = true;
+    }
+
+    SkBitmap bm;
+    if (this->config() == kRGB_565_Config) {
+        this->copyTo(&bm, kARGB_8888_Config);
+    } else {
+        bm = *this;
+    }
+    bm.lockPixels();
+
+    CGImageRef image = bitmap2imageref(bm);
+    if (image) {
+        CGRect rect;
+        rect.origin.x = rect.origin.y = 0;
+        rect.size.width = bm.width();
+        rect.size.height = bm.height();
+
+        CGContextDrawImage(cg, rect, image);
+        CGImageRelease(image);
+    }
+
+    if (useQD) {
+        QDEndCGContext(GetWindowPort(wind), &cg);
+    }
+}
+
+#endif
diff --git a/src/utils/mac/SkCreateCGImageRef.cpp b/src/utils/mac/SkCreateCGImageRef.cpp
new file mode 100644
index 0000000..e9e1107
--- /dev/null
+++ b/src/utils/mac/SkCreateCGImageRef.cpp
@@ -0,0 +1,240 @@
+
+/*
+ * 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 "SkCGUtils.h"
+#include "SkBitmap.h"
+#include "SkColorPriv.h"
+
+static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
+    delete bitmap;
+}
+
+#define HAS_ARGB_SHIFTS(a, r, g, b) \
+    (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
+    && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
+
+static bool getBitmapInfo(const SkBitmap& bm,
+                          size_t* bitsPerComponent,
+                          CGBitmapInfo* info,
+                          bool* upscaleTo32) {
+    if (upscaleTo32) {
+        *upscaleTo32 = false;
+    }
+
+    switch (bm.config()) {
+        case SkBitmap::kRGB_565_Config:
+            if (upscaleTo32) {
+                *upscaleTo32 = true;
+            }
+            // fall through
+        case SkBitmap::kARGB_8888_Config:
+            *bitsPerComponent = 8;
+#if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 0, 8, 16) \
+|| defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(0, 24, 16, 8)
+            *info = kCGBitmapByteOrder32Big;
+            if (bm.isOpaque()) {
+                *info |= kCGImageAlphaNoneSkipLast;
+            } else {
+                *info |= kCGImageAlphaPremultipliedLast;
+            }
+#elif defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) \
+|| defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
+            // Matches the CGBitmapInfo that Apple recommends for best
+            // performance, used by google chrome.
+            *info = kCGBitmapByteOrder32Little;
+            if (bm.isOpaque()) {
+                *info |= kCGImageAlphaNoneSkipFirst;
+            } else {
+                *info |= kCGImageAlphaPremultipliedFirst;
+            }
+#else
+            // ...add more formats as required...
+#warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \
+This will probably not work.
+            // Legacy behavior. Perhaps turn this into an error at some
+            // point.
+            *info = kCGBitmapByteOrder32Big;
+            if (bm.isOpaque()) {
+                *info |= kCGImageAlphaNoneSkipLast;
+            } else {
+                *info |= kCGImageAlphaPremultipliedLast;
+            }
+#endif
+            break;
+#if 0
+        case SkBitmap::kRGB_565_Config:
+            // doesn't see quite right. Are they thinking 1555?
+            *bitsPerComponent = 5;
+            *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
+            break;
+#endif
+        case SkBitmap::kARGB_4444_Config:
+            *bitsPerComponent = 4;
+            *info = kCGBitmapByteOrder16Little;
+            if (bm.isOpaque()) {
+                *info |= kCGImageAlphaNoneSkipLast;
+            } else {
+                *info |= kCGImageAlphaPremultipliedLast;
+            }
+            break;
+        default:
+            return false;
+    }
+    return true;
+}
+
+static SkBitmap* prepareForImageRef(const SkBitmap& bm,
+                                    size_t* bitsPerComponent,
+                                    CGBitmapInfo* info) {
+    bool upscaleTo32;
+    if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
+        return NULL;
+    }
+
+    SkBitmap* copy;
+    if (upscaleTo32) {
+        copy = new SkBitmap;
+        // here we make a ceep copy of the pixels, since CG won't take our
+        // 565 directly
+        bm.copyTo(copy, SkBitmap::kARGB_8888_Config);
+    } else {
+        copy = new SkBitmap(bm);
+    }
+    return copy;
+}
+
+#undef HAS_ARGB_SHIFTS
+
+CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
+                                            CGColorSpaceRef colorSpace) {
+    size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
+    CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
+
+    SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
+    if (NULL == bitmap) {
+        return NULL;
+    }
+
+    const int w = bitmap->width();
+    const int h = bitmap->height();
+    const size_t s = bitmap->getSize();
+
+    // our provider "owns" the bitmap*, and will take care of deleting it
+    // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
+    // proc, which will in turn unlock the pixels
+    bitmap->lockPixels();
+    CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
+                                                             SkBitmap_ReleaseInfo);
+
+    bool releaseColorSpace = false;
+    if (NULL == colorSpace) {
+        colorSpace = CGColorSpaceCreateDeviceRGB();
+        releaseColorSpace = true;
+    }
+
+    CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
+                                   bitmap->bytesPerPixel() * 8,
+                                   bitmap->rowBytes(), colorSpace, info, dataRef,
+                                   NULL, false, kCGRenderingIntentDefault);
+
+    if (releaseColorSpace) {
+        CGColorSpaceRelease(colorSpace);
+    }
+    CGDataProviderRelease(dataRef);
+    return ref;
+}
+
+void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
+    CGImageRef img = SkCreateCGImageRef(bm);
+
+    if (img) {
+        CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
+
+        CGContextSaveGState(cg);
+        CGContextTranslateCTM(cg, x, r.size.height + y);
+        CGContextScaleCTM(cg, 1, -1);
+
+        CGContextDrawImage(cg, r, img);
+
+        CGContextRestoreGState(cg);
+
+        CGImageRelease(img);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+#include "SkStream.h"
+
+class SkAutoPDFRelease {
+public:
+    SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
+    ~SkAutoPDFRelease() {
+        if (fDoc) {
+            CGPDFDocumentRelease(fDoc);
+        }
+    }
+private:
+    CGPDFDocumentRef fDoc;
+};
+
+static void CGDataProviderReleaseData_FromMalloc(void*, const void* data,
+                                                 size_t size) {
+    sk_free((void*)data);
+}
+
+bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
+    size_t size = stream->getLength();
+    void* ptr = sk_malloc_throw(size);
+    stream->read(ptr, size);
+    CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size,
+                                          CGDataProviderReleaseData_FromMalloc);
+    if (NULL == data) {
+        return false;
+    }
+
+    CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
+    CGDataProviderRelease(data);
+    if (NULL == pdf) {
+        return false;
+    }
+    SkAutoPDFRelease releaseMe(pdf);
+
+    CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
+    if (NULL == page) {
+        return false;
+    }
+
+    CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
+
+    int w = (int)CGRectGetWidth(bounds);
+    int h = (int)CGRectGetHeight(bounds);
+
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    bitmap.allocPixels();
+    bitmap.eraseColor(SK_ColorWHITE);
+
+    size_t bitsPerComponent;
+    CGBitmapInfo info;
+    getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
+
+    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+    CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
+                                             bitsPerComponent, bitmap.rowBytes(),
+                                             cs, info);
+    CGColorSpaceRelease(cs);
+
+    if (ctx) {
+        CGContextDrawPDFPage(ctx, page);
+        CGContextRelease(ctx);
+    }
+
+    output->swap(bitmap);
+    return true;
+}
diff --git a/src/utils/mac/SkStream_mac.cpp b/src/utils/mac/SkStream_mac.cpp
new file mode 100644
index 0000000..afb87fb
--- /dev/null
+++ b/src/utils/mac/SkStream_mac.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 "SkCGUtils.h"
+#include "SkStream.h"
+
+// This is used by CGDataProviderCreateWithData
+
+static void unref_data_proc(void* info, const void* addr, size_t size) {
+    SkASSERT(info);
+    ((SkRefCnt*)info)->unref();
+}
+
+// These are used by CGDataProviderSequentialCallbacks
+
+static size_t get_bytes_proc(void* info, void* buffer, size_t bytes) {
+    SkASSERT(info);
+    return ((SkStream*)info)->read(buffer, bytes);
+}
+
+static off_t skip_forward_proc(void* info, off_t bytes) {
+    return ((SkStream*)info)->skip((size_t) bytes);
+}
+
+static void rewind_proc(void* info) {
+    SkASSERT(info);
+    ((SkStream*)info)->rewind();
+}
+
+static void release_info_proc(void* info) {
+    SkASSERT(info);
+    ((SkStream*)info)->unref();
+}
+
+CGDataProviderRef SkCreateDataProviderFromStream(SkStream* stream) {
+    stream->ref();  // unref will be called when the provider is deleted
+
+    const void* addr = stream->getMemoryBase();
+    if (addr) {
+        // special-case when the stream is just a block of ram
+        return CGDataProviderCreateWithData(stream, addr, stream->getLength(),
+                                            unref_data_proc);
+    }
+
+    CGDataProviderSequentialCallbacks rec;
+    sk_bzero(&rec, sizeof(rec));
+    rec.version = 0;
+    rec.getBytes = get_bytes_proc;
+    rec.skipForward = skip_forward_proc;
+    rec.rewind = rewind_proc;
+    rec.releaseInfo = release_info_proc;
+    return CGDataProviderCreateSequential(stream, &rec);
+}
diff --git a/src/utils/unix/SkOSWindow_Unix.cpp b/src/utils/unix/SkOSWindow_Unix.cpp
deleted file mode 100644
index b4b0f17..0000000
--- a/src/utils/unix/SkOSWindow_Unix.cpp
+++ /dev/null
@@ -1,285 +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 <X11/Xlib.h>
-#include <X11/Xatom.h>
-#include <X11/keysym.h>
-#include <GL/glx.h>
-#include <GL/gl.h>
-#include <GL/glu.h>
-
-#include "SkWindow.h"
-
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkColor.h"
-#include "SkEvent.h"
-#include "SkKey.h"
-#include "SkWindow.h"
-#include "XkeysToSkKeys.h"
-extern "C" {
-    #include "keysym2ucs.h"
-}
-
-const int WIDTH = 500;
-const int HEIGHT = 500;
-
-// Determine which events to listen for.
-const long EVENT_MASK = StructureNotifyMask|ButtonPressMask|ButtonReleaseMask
-        |ExposureMask|PointerMotionMask|KeyPressMask|KeyReleaseMask;
-
-SkOSWindow::SkOSWindow(void* unused) : INHERITED(), fGLAttached(false), fVi(0)
-{
-    fUnixWindow.fDisplay = XOpenDisplay(NULL);
-    Display* dsp = fUnixWindow.fDisplay;
-    if (dsp) {
-        // Attempt to create a window that supports GL
-        GLint att[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER,
-                GLX_STENCIL_SIZE, 8, None };
-        fVi = glXChooseVisual(dsp, DefaultScreen(dsp), att);
-        if (fVi) {
-            Colormap colorMap = XCreateColormap(dsp, RootWindow(dsp, fVi->screen),
-                fVi->visual, AllocNone);
-            XSetWindowAttributes swa;
-            swa.colormap = colorMap;
-            swa.event_mask = EVENT_MASK;
-            fUnixWindow.fWin = XCreateWindow(dsp, RootWindow(dsp, fVi->screen),
-                    0, 0, WIDTH, HEIGHT, 0, fVi->depth,
-                    InputOutput, fVi->visual, CWEventMask | CWColormap, &swa);
-
-        } else {
-            // Create a simple window instead.  We will not be able to
-            // show GL
-            fUnixWindow.fWin = XCreateSimpleWindow(dsp, DefaultRootWindow(dsp),
-                    0, 0, WIDTH, HEIGHT, 0, 0, 0);
-        }
-        mapWindowAndWait();
-        fUnixWindow.fGc = XCreateGC(dsp, fUnixWindow.fWin, 0, NULL);
-    }
-    this->resize(WIDTH, HEIGHT);
-    fUnixWindow.fGLCreated = false;
-}
-
-SkOSWindow::~SkOSWindow()
-{
-    if (fUnixWindow.fDisplay) {
-        if (fGLAttached)
-            glXMakeCurrent(fUnixWindow.fDisplay, None, NULL);
-        XFreeGC(fUnixWindow.fDisplay, fUnixWindow.fGc);
-        if (fUnixWindow.fGLCreated)
-            glXDestroyContext(fUnixWindow.fDisplay, fUnixWindow.fGLContext);
-        XDestroyWindow(fUnixWindow.fDisplay, fUnixWindow.fWin);
-        XCloseDisplay(fUnixWindow.fDisplay);
-        fUnixWindow.fDisplay = 0;
-    }
-}
-
-void SkOSWindow::post_linuxevent()
-{
-    // Put an event in the X queue to fire an SkEvent.
-    if (!fUnixWindow.fDisplay) return;
-    long event_mask = NoEventMask;
-    XClientMessageEvent event;
-    event.type = ClientMessage;
-    Atom myAtom(0);
-    event.message_type = myAtom;
-    event.format = 32;
-    event.data.l[0] = 0;
-    XSendEvent(fUnixWindow.fDisplay, fUnixWindow.fWin, false, 0,
-               (XEvent*) &event);
-    XFlush(fUnixWindow.fDisplay);
-}
-
-void SkOSWindow::loop()
-{
-    Display* dsp = fUnixWindow.fDisplay;
-    XSelectInput(dsp, fUnixWindow.fWin, EVENT_MASK);
-
-    bool loop = true;
-    XEvent evt;
-    while (loop) {
-        XNextEvent(dsp, &evt);
-        switch (evt.type) {
-            case Expose:
-                if (evt.xexpose.count == 0)
-                    this->inval(NULL);
-                break;
-            case ConfigureNotify:
-                this->resize(evt.xconfigure.width, evt.xconfigure.height);
-                break;
-            case ButtonPress:
-                if (evt.xbutton.button == Button1)
-                    this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kDown_State);
-                break;
-            case ButtonRelease:
-                if (evt.xbutton.button == Button1)
-                    this->handleClick(evt.xbutton.x, evt.xbutton.y, SkView::Click::kUp_State);
-                break;
-            case MotionNotify:
-                this->handleClick(evt.xmotion.x, evt.xmotion.y, SkView::Click::kMoved_State);
-                break;
-            case KeyPress:
-            {
-                KeySym keysym = XKeycodeToKeysym(dsp, evt.xkey.keycode, 0);
-                //SkDebugf("pressed key %i!\n\tKeySym:%i\n", evt.xkey.keycode, XKeycodeToKeysym(dsp, evt.xkey.keycode, 0));
-                if (keysym == XK_Escape) {
-                    loop = false;
-                    break;
-                }
-                this->handleKey(XKeyToSkKey(keysym));
-                long uni = keysym2ucs(keysym);
-                if (uni != -1) {
-                    this->handleChar((SkUnichar) uni);
-                }
-                break;
-            }
-            case KeyRelease:
-                //SkDebugf("released key %i\n", evt.xkey.keycode);
-                this->handleKeyUp(XKeyToSkKey(XKeycodeToKeysym(dsp, evt.xkey.keycode, 0)));
-                break;
-            case ClientMessage:
-                if (SkEvent::ProcessEvent()) {
-                    this->post_linuxevent();
-                }
-                break;
-            default:
-                // Do nothing for other events
-                break;
-        }
-    }
-}
-
-void SkOSWindow::mapWindowAndWait()
-{
-    Display* dsp = fUnixWindow.fDisplay;
-    Window win = fUnixWindow.fWin;
-    XMapWindow(dsp, win);
-
-    long eventMask = StructureNotifyMask;
-    XSelectInput(dsp, win, eventMask);
-
-    // Wait until screen is ready.
-    XEvent evt;
-    do {
-        XNextEvent(dsp, &evt);
-    } while(evt.type != MapNotify);
-
-}
-
-bool SkOSWindow::attachGL()
-{
-    if (fGLAttached) return true;
-    Display* dsp = fUnixWindow.fDisplay;
-    if (!dsp || !fVi) return false;
-
-    if (!fUnixWindow.fGLCreated) {
-        fUnixWindow.fGLContext = glXCreateContext(dsp, fVi, NULL, GL_TRUE);
-        fUnixWindow.fGLCreated = true;
-        glXMakeCurrent(dsp, fUnixWindow.fWin, fUnixWindow.fGLContext);
-        glViewport(0, 0, SkScalarRound(this->width()), SkScalarRound(this->height()));
-        glClearColor(0, 0, 0, 0);
-        glClearStencil(0);
-        glStencilMask(0xffffffff);
-        glDisable(GL_SCISSOR_TEST);
-        glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
-    }
-    else
-        glXMakeCurrent(dsp, fUnixWindow.fWin, fUnixWindow.fGLContext);
-    fGLAttached = true;
-
-    return true;
-}
-
-void SkOSWindow::detachGL()
-{
-    if (!fUnixWindow.fDisplay || !fGLAttached) return;
-    fGLAttached = false;
-    // Returns back to normal drawing.
-    glXMakeCurrent(fUnixWindow.fDisplay, None, NULL);
-    // Ensure that we redraw when switching back to raster.
-    this->inval(NULL);
-}
-
-void SkOSWindow::presentGL()
-{
-    if (fUnixWindow.fDisplay && fGLAttached) {
-        glXSwapBuffers(fUnixWindow.fDisplay, fUnixWindow.fWin);
-    }
-}
-
-void SkOSWindow::onSetTitle(const char title[])
-{
-    if (!fUnixWindow.fDisplay) return;
-    XTextProperty textProp;
-    textProp.value = (unsigned char*)title;
-    textProp.format = 8;
-    textProp.nitems = strlen((char*)textProp.value);
-    textProp.encoding = XA_STRING;
-    XSetWMName(fUnixWindow.fDisplay, fUnixWindow.fWin, &textProp);
-}
-
-void SkOSWindow::onHandleInval(const SkIRect&) {
-    (new SkEvent("inval-imageview", this->getSinkID()))->post();
-}
-
-bool SkOSWindow::onEvent(const SkEvent& evt)
-{
-    if (evt.isType("inval-imageview")) {
-        update(NULL);
-        if (!fGLAttached)
-            doPaint();
-        return true;
-    }
-    return INHERITED::onEvent(evt);
-}
-
-static bool convertBitmapToXImage(XImage& image, const SkBitmap& bitmap)
-{
-    sk_bzero(&image, sizeof(image));
-
-    int bitsPerPixel = bitmap.bytesPerPixel() * 8;
-    image.width = bitmap.width();
-    image.height = bitmap.height();
-    image.format = ZPixmap;
-    image.data = (char*) bitmap.getPixels();
-    image.byte_order = LSBFirst;
-    image.bitmap_unit = bitsPerPixel;
-    image.bitmap_bit_order = LSBFirst;
-    image.bitmap_pad = bitsPerPixel;
-    image.depth = 24;
-    image.bytes_per_line = bitmap.rowBytes() - bitmap.width() * bitmap.bytesPerPixel();
-    image.bits_per_pixel = bitsPerPixel;
-    return XInitImage(&image);
-}
-
-void SkOSWindow::doPaint() {
-    if (!fUnixWindow.fDisplay) return;
-    // Draw the bitmap to the screen.
-    const SkBitmap& bitmap = getBitmap();
-    int width = bitmap.width();
-    int height = bitmap.height();
-
-    XImage image;
-    if (!convertBitmapToXImage(image, bitmap)) return;
-
-    XPutImage(fUnixWindow.fDisplay, fUnixWindow.fWin, fUnixWindow.fGc, &image, 0, 0, 0, 0, width, height);
-}
-
-bool SkOSWindow::onHandleChar(SkUnichar)
-{
-    return false;
-}
-
-bool SkOSWindow::onHandleKey(SkKey key)
-{
-    return false;
-}
-
-bool SkOSWindow::onHandleKeyUp(SkKey key)
-{
-    return false;
-}
diff --git a/src/utils/unix/keysym2ucs.c b/src/utils/unix/keysym2ucs.c
deleted file mode 100644
index 520c6a7..0000000
--- a/src/utils/unix/keysym2ucs.c
+++ /dev/null
@@ -1,848 +0,0 @@
-/* $XFree86$
- * This module converts keysym values into the corresponding ISO 10646
- * (UCS, Unicode) values.
- *
- * The array keysymtab[] contains pairs of X11 keysym values for graphical
- * characters and the corresponding Unicode value. The function
- * keysym2ucs() maps a keysym onto a Unicode value using a binary search,
- * therefore keysymtab[] must remain SORTED by keysym value.
- *
- * The keysym -> UTF-8 conversion will hopefully one day be provided
- * by Xlib via XmbLookupString() and should ideally not have to be
- * done in X applications. But we are not there yet.
- *
- * We allow to represent any UCS character in the range U-00000000 to
- * U-00FFFFFF by a keysym value in the range 0x01000000 to 0x01ffffff.
- * This admittedly does not cover the entire 31-bit space of UCS, but
- * it does cover all of the characters up to U-10FFFF, which can be
- * represented by UTF-16, and more, and it is very unlikely that higher
- * UCS codes will ever be assigned by ISO. So to get Unicode character
- * U+ABCD you can directly use keysym 0x0100abcd.
- *
- * NOTE: The comments in the table below contain the actual character
- * encoded in UTF-8, so for viewing and editing best use an editor in
- * UTF-8 mode.
- *
- * Author: Markus G. Kuhn <http://www.cl.cam.ac.uk/~mgk25/>,
- *         University of Cambridge, April 2001
- *
- * Special thanks to Richard Verhoeven <river@win.tue.nl> for preparing
- * an initial draft of the mapping table.
- *
- * This software is in the public domain. Share and enjoy!
- *
- * AUTOMATICALLY GENERATED FILE, DO NOT EDIT !!! (unicode/convmap.pl)
- */
-
-#include "keysym2ucs.h"
-
-struct codepair {
-  unsigned short keysym;
-  unsigned short ucs;
-} keysymtab[] = {
-  { 0x01a1, 0x0104 }, /*                     Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */
-  { 0x01a2, 0x02d8 }, /*                       breve ˘ BREVE */
-  { 0x01a3, 0x0141 }, /*                     Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */
-  { 0x01a5, 0x013d }, /*                      Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */
-  { 0x01a6, 0x015a }, /*                      Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */
-  { 0x01a9, 0x0160 }, /*                      Scaron Š LATIN CAPITAL LETTER S WITH CARON */
-  { 0x01aa, 0x015e }, /*                    Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */
-  { 0x01ab, 0x0164 }, /*                      Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */
-  { 0x01ac, 0x0179 }, /*                      Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */
-  { 0x01ae, 0x017d }, /*                      Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */
-  { 0x01af, 0x017b }, /*                   Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */
-  { 0x01b1, 0x0105 }, /*                     aogonek ą LATIN SMALL LETTER A WITH OGONEK */
-  { 0x01b2, 0x02db }, /*                      ogonek ˛ OGONEK */
-  { 0x01b3, 0x0142 }, /*                     lstroke ł LATIN SMALL LETTER L WITH STROKE */
-  { 0x01b5, 0x013e }, /*                      lcaron ľ LATIN SMALL LETTER L WITH CARON */
-  { 0x01b6, 0x015b }, /*                      sacute ś LATIN SMALL LETTER S WITH ACUTE */
-  { 0x01b7, 0x02c7 }, /*                       caron ˇ CARON */
-  { 0x01b9, 0x0161 }, /*                      scaron š LATIN SMALL LETTER S WITH CARON */
-  { 0x01ba, 0x015f }, /*                    scedilla ş LATIN SMALL LETTER S WITH CEDILLA */
-  { 0x01bb, 0x0165 }, /*                      tcaron ť LATIN SMALL LETTER T WITH CARON */
-  { 0x01bc, 0x017a }, /*                      zacute ź LATIN SMALL LETTER Z WITH ACUTE */
-  { 0x01bd, 0x02dd }, /*                 doubleacute ˝ DOUBLE ACUTE ACCENT */
-  { 0x01be, 0x017e }, /*                      zcaron ž LATIN SMALL LETTER Z WITH CARON */
-  { 0x01bf, 0x017c }, /*                   zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */
-  { 0x01c0, 0x0154 }, /*                      Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */
-  { 0x01c3, 0x0102 }, /*                      Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */
-  { 0x01c5, 0x0139 }, /*                      Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */
-  { 0x01c6, 0x0106 }, /*                      Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */
-  { 0x01c8, 0x010c }, /*                      Ccaron Č LATIN CAPITAL LETTER C WITH CARON */
-  { 0x01ca, 0x0118 }, /*                     Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */
-  { 0x01cc, 0x011a }, /*                      Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */
-  { 0x01cf, 0x010e }, /*                      Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */
-  { 0x01d0, 0x0110 }, /*                     Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */
-  { 0x01d1, 0x0143 }, /*                      Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */
-  { 0x01d2, 0x0147 }, /*                      Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */
-  { 0x01d5, 0x0150 }, /*                Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */
-  { 0x01d8, 0x0158 }, /*                      Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */
-  { 0x01d9, 0x016e }, /*                       Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */
-  { 0x01db, 0x0170 }, /*                Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */
-  { 0x01de, 0x0162 }, /*                    Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */
-  { 0x01e0, 0x0155 }, /*                      racute ŕ LATIN SMALL LETTER R WITH ACUTE */
-  { 0x01e3, 0x0103 }, /*                      abreve ă LATIN SMALL LETTER A WITH BREVE */
-  { 0x01e5, 0x013a }, /*                      lacute ĺ LATIN SMALL LETTER L WITH ACUTE */
-  { 0x01e6, 0x0107 }, /*                      cacute ć LATIN SMALL LETTER C WITH ACUTE */
-  { 0x01e8, 0x010d }, /*                      ccaron č LATIN SMALL LETTER C WITH CARON */
-  { 0x01ea, 0x0119 }, /*                     eogonek ę LATIN SMALL LETTER E WITH OGONEK */
-  { 0x01ec, 0x011b }, /*                      ecaron ě LATIN SMALL LETTER E WITH CARON */
-  { 0x01ef, 0x010f }, /*                      dcaron ď LATIN SMALL LETTER D WITH CARON */
-  { 0x01f0, 0x0111 }, /*                     dstroke đ LATIN SMALL LETTER D WITH STROKE */
-  { 0x01f1, 0x0144 }, /*                      nacute ń LATIN SMALL LETTER N WITH ACUTE */
-  { 0x01f2, 0x0148 }, /*                      ncaron ň LATIN SMALL LETTER N WITH CARON */
-  { 0x01f5, 0x0151 }, /*                odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */
-  { 0x01f8, 0x0159 }, /*                      rcaron ř LATIN SMALL LETTER R WITH CARON */
-  { 0x01f9, 0x016f }, /*                       uring ů LATIN SMALL LETTER U WITH RING ABOVE */
-  { 0x01fb, 0x0171 }, /*                udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */
-  { 0x01fe, 0x0163 }, /*                    tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */
-  { 0x01ff, 0x02d9 }, /*                    abovedot ˙ DOT ABOVE */
-  { 0x02a1, 0x0126 }, /*                     Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */
-  { 0x02a6, 0x0124 }, /*                 Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */
-  { 0x02a9, 0x0130 }, /*                   Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */
-  { 0x02ab, 0x011e }, /*                      Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */
-  { 0x02ac, 0x0134 }, /*                 Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */
-  { 0x02b1, 0x0127 }, /*                     hstroke ħ LATIN SMALL LETTER H WITH STROKE */
-  { 0x02b6, 0x0125 }, /*                 hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */
-  { 0x02b9, 0x0131 }, /*                    idotless ı LATIN SMALL LETTER DOTLESS I */
-  { 0x02bb, 0x011f }, /*                      gbreve ğ LATIN SMALL LETTER G WITH BREVE */
-  { 0x02bc, 0x0135 }, /*                 jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */
-  { 0x02c5, 0x010a }, /*                   Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */
-  { 0x02c6, 0x0108 }, /*                 Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */
-  { 0x02d5, 0x0120 }, /*                   Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */
-  { 0x02d8, 0x011c }, /*                 Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */
-  { 0x02dd, 0x016c }, /*                      Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */
-  { 0x02de, 0x015c }, /*                 Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */
-  { 0x02e5, 0x010b }, /*                   cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */
-  { 0x02e6, 0x0109 }, /*                 ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */
-  { 0x02f5, 0x0121 }, /*                   gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */
-  { 0x02f8, 0x011d }, /*                 gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */
-  { 0x02fd, 0x016d }, /*                      ubreve ŭ LATIN SMALL LETTER U WITH BREVE */
-  { 0x02fe, 0x015d }, /*                 scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */
-  { 0x03a2, 0x0138 }, /*                         kra ĸ LATIN SMALL LETTER KRA */
-  { 0x03a3, 0x0156 }, /*                    Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */
-  { 0x03a5, 0x0128 }, /*                      Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */
-  { 0x03a6, 0x013b }, /*                    Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */
-  { 0x03aa, 0x0112 }, /*                     Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */
-  { 0x03ab, 0x0122 }, /*                    Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */
-  { 0x03ac, 0x0166 }, /*                      Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */
-  { 0x03b3, 0x0157 }, /*                    rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */
-  { 0x03b5, 0x0129 }, /*                      itilde ĩ LATIN SMALL LETTER I WITH TILDE */
-  { 0x03b6, 0x013c }, /*                    lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */
-  { 0x03ba, 0x0113 }, /*                     emacron ē LATIN SMALL LETTER E WITH MACRON */
-  { 0x03bb, 0x0123 }, /*                    gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */
-  { 0x03bc, 0x0167 }, /*                      tslash ŧ LATIN SMALL LETTER T WITH STROKE */
-  { 0x03bd, 0x014a }, /*                         ENG Ŋ LATIN CAPITAL LETTER ENG */
-  { 0x03bf, 0x014b }, /*                         eng ŋ LATIN SMALL LETTER ENG */
-  { 0x03c0, 0x0100 }, /*                     Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */
-  { 0x03c7, 0x012e }, /*                     Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */
-  { 0x03cc, 0x0116 }, /*                   Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */
-  { 0x03cf, 0x012a }, /*                     Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */
-  { 0x03d1, 0x0145 }, /*                    Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */
-  { 0x03d2, 0x014c }, /*                     Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */
-  { 0x03d3, 0x0136 }, /*                    Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */
-  { 0x03d9, 0x0172 }, /*                     Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */
-  { 0x03dd, 0x0168 }, /*                      Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */
-  { 0x03de, 0x016a }, /*                     Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */
-  { 0x03e0, 0x0101 }, /*                     amacron ā LATIN SMALL LETTER A WITH MACRON */
-  { 0x03e7, 0x012f }, /*                     iogonek į LATIN SMALL LETTER I WITH OGONEK */
-  { 0x03ec, 0x0117 }, /*                   eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */
-  { 0x03ef, 0x012b }, /*                     imacron ī LATIN SMALL LETTER I WITH MACRON */
-  { 0x03f1, 0x0146 }, /*                    ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */
-  { 0x03f2, 0x014d }, /*                     omacron ō LATIN SMALL LETTER O WITH MACRON */
-  { 0x03f3, 0x0137 }, /*                    kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */
-  { 0x03f9, 0x0173 }, /*                     uogonek ų LATIN SMALL LETTER U WITH OGONEK */
-  { 0x03fd, 0x0169 }, /*                      utilde ũ LATIN SMALL LETTER U WITH TILDE */
-  { 0x03fe, 0x016b }, /*                     umacron ū LATIN SMALL LETTER U WITH MACRON */
-  { 0x047e, 0x203e }, /*                    overline ‾ OVERLINE */
-  { 0x04a1, 0x3002 }, /*               kana_fullstop 。 IDEOGRAPHIC FULL STOP */
-  { 0x04a2, 0x300c }, /*         kana_openingbracket 「 LEFT CORNER BRACKET */
-  { 0x04a3, 0x300d }, /*         kana_closingbracket 」 RIGHT CORNER BRACKET */
-  { 0x04a4, 0x3001 }, /*                  kana_comma 、 IDEOGRAPHIC COMMA */
-  { 0x04a5, 0x30fb }, /*            kana_conjunctive ・ KATAKANA MIDDLE DOT */
-  { 0x04a6, 0x30f2 }, /*                     kana_WO ヲ KATAKANA LETTER WO */
-  { 0x04a7, 0x30a1 }, /*                      kana_a ァ KATAKANA LETTER SMALL A */
-  { 0x04a8, 0x30a3 }, /*                      kana_i ィ KATAKANA LETTER SMALL I */
-  { 0x04a9, 0x30a5 }, /*                      kana_u ゥ KATAKANA LETTER SMALL U */
-  { 0x04aa, 0x30a7 }, /*                      kana_e ェ KATAKANA LETTER SMALL E */
-  { 0x04ab, 0x30a9 }, /*                      kana_o ォ KATAKANA LETTER SMALL O */
-  { 0x04ac, 0x30e3 }, /*                     kana_ya ャ KATAKANA LETTER SMALL YA */
-  { 0x04ad, 0x30e5 }, /*                     kana_yu ュ KATAKANA LETTER SMALL YU */
-  { 0x04ae, 0x30e7 }, /*                     kana_yo ョ KATAKANA LETTER SMALL YO */
-  { 0x04af, 0x30c3 }, /*                    kana_tsu ッ KATAKANA LETTER SMALL TU */
-  { 0x04b0, 0x30fc }, /*              prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */
-  { 0x04b1, 0x30a2 }, /*                      kana_A ア KATAKANA LETTER A */
-  { 0x04b2, 0x30a4 }, /*                      kana_I イ KATAKANA LETTER I */
-  { 0x04b3, 0x30a6 }, /*                      kana_U ウ KATAKANA LETTER U */
-  { 0x04b4, 0x30a8 }, /*                      kana_E エ KATAKANA LETTER E */
-  { 0x04b5, 0x30aa }, /*                      kana_O オ KATAKANA LETTER O */
-  { 0x04b6, 0x30ab }, /*                     kana_KA カ KATAKANA LETTER KA */
-  { 0x04b7, 0x30ad }, /*                     kana_KI キ KATAKANA LETTER KI */
-  { 0x04b8, 0x30af }, /*                     kana_KU ク KATAKANA LETTER KU */
-  { 0x04b9, 0x30b1 }, /*                     kana_KE ケ KATAKANA LETTER KE */
-  { 0x04ba, 0x30b3 }, /*                     kana_KO コ KATAKANA LETTER KO */
-  { 0x04bb, 0x30b5 }, /*                     kana_SA サ KATAKANA LETTER SA */
-  { 0x04bc, 0x30b7 }, /*                    kana_SHI シ KATAKANA LETTER SI */
-  { 0x04bd, 0x30b9 }, /*                     kana_SU ス KATAKANA LETTER SU */
-  { 0x04be, 0x30bb }, /*                     kana_SE セ KATAKANA LETTER SE */
-  { 0x04bf, 0x30bd }, /*                     kana_SO ソ KATAKANA LETTER SO */
-  { 0x04c0, 0x30bf }, /*                     kana_TA タ KATAKANA LETTER TA */
-  { 0x04c1, 0x30c1 }, /*                    kana_CHI チ KATAKANA LETTER TI */
-  { 0x04c2, 0x30c4 }, /*                    kana_TSU ツ KATAKANA LETTER TU */
-  { 0x04c3, 0x30c6 }, /*                     kana_TE テ KATAKANA LETTER TE */
-  { 0x04c4, 0x30c8 }, /*                     kana_TO ト KATAKANA LETTER TO */
-  { 0x04c5, 0x30ca }, /*                     kana_NA ナ KATAKANA LETTER NA */
-  { 0x04c6, 0x30cb }, /*                     kana_NI ニ KATAKANA LETTER NI */
-  { 0x04c7, 0x30cc }, /*                     kana_NU ヌ KATAKANA LETTER NU */
-  { 0x04c8, 0x30cd }, /*                     kana_NE ネ KATAKANA LETTER NE */
-  { 0x04c9, 0x30ce }, /*                     kana_NO ノ KATAKANA LETTER NO */
-  { 0x04ca, 0x30cf }, /*                     kana_HA ハ KATAKANA LETTER HA */
-  { 0x04cb, 0x30d2 }, /*                     kana_HI ヒ KATAKANA LETTER HI */
-  { 0x04cc, 0x30d5 }, /*                     kana_FU フ KATAKANA LETTER HU */
-  { 0x04cd, 0x30d8 }, /*                     kana_HE ヘ KATAKANA LETTER HE */
-  { 0x04ce, 0x30db }, /*                     kana_HO ホ KATAKANA LETTER HO */
-  { 0x04cf, 0x30de }, /*                     kana_MA マ KATAKANA LETTER MA */
-  { 0x04d0, 0x30df }, /*                     kana_MI ミ KATAKANA LETTER MI */
-  { 0x04d1, 0x30e0 }, /*                     kana_MU ム KATAKANA LETTER MU */
-  { 0x04d2, 0x30e1 }, /*                     kana_ME メ KATAKANA LETTER ME */
-  { 0x04d3, 0x30e2 }, /*                     kana_MO モ KATAKANA LETTER MO */
-  { 0x04d4, 0x30e4 }, /*                     kana_YA ヤ KATAKANA LETTER YA */
-  { 0x04d5, 0x30e6 }, /*                     kana_YU ユ KATAKANA LETTER YU */
-  { 0x04d6, 0x30e8 }, /*                     kana_YO ヨ KATAKANA LETTER YO */
-  { 0x04d7, 0x30e9 }, /*                     kana_RA ラ KATAKANA LETTER RA */
-  { 0x04d8, 0x30ea }, /*                     kana_RI リ KATAKANA LETTER RI */
-  { 0x04d9, 0x30eb }, /*                     kana_RU ル KATAKANA LETTER RU */
-  { 0x04da, 0x30ec }, /*                     kana_RE レ KATAKANA LETTER RE */
-  { 0x04db, 0x30ed }, /*                     kana_RO ロ KATAKANA LETTER RO */
-  { 0x04dc, 0x30ef }, /*                     kana_WA ワ KATAKANA LETTER WA */
-  { 0x04dd, 0x30f3 }, /*                      kana_N ン KATAKANA LETTER N */
-  { 0x04de, 0x309b }, /*                 voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */
-  { 0x04df, 0x309c }, /*             semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */
-  { 0x05ac, 0x060c }, /*                Arabic_comma ، ARABIC COMMA */
-  { 0x05bb, 0x061b }, /*            Arabic_semicolon ؛ ARABIC SEMICOLON */
-  { 0x05bf, 0x061f }, /*        Arabic_question_mark ؟ ARABIC QUESTION MARK */
-  { 0x05c1, 0x0621 }, /*                Arabic_hamza ء ARABIC LETTER HAMZA */
-  { 0x05c2, 0x0622 }, /*          Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */
-  { 0x05c3, 0x0623 }, /*          Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */
-  { 0x05c4, 0x0624 }, /*           Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */
-  { 0x05c5, 0x0625 }, /*       Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */
-  { 0x05c6, 0x0626 }, /*           Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */
-  { 0x05c7, 0x0627 }, /*                 Arabic_alef ا ARABIC LETTER ALEF */
-  { 0x05c8, 0x0628 }, /*                  Arabic_beh ب ARABIC LETTER BEH */
-  { 0x05c9, 0x0629 }, /*           Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */
-  { 0x05ca, 0x062a }, /*                  Arabic_teh ت ARABIC LETTER TEH */
-  { 0x05cb, 0x062b }, /*                 Arabic_theh ث ARABIC LETTER THEH */
-  { 0x05cc, 0x062c }, /*                 Arabic_jeem ج ARABIC LETTER JEEM */
-  { 0x05cd, 0x062d }, /*                  Arabic_hah ح ARABIC LETTER HAH */
-  { 0x05ce, 0x062e }, /*                 Arabic_khah خ ARABIC LETTER KHAH */
-  { 0x05cf, 0x062f }, /*                  Arabic_dal د ARABIC LETTER DAL */
-  { 0x05d0, 0x0630 }, /*                 Arabic_thal ذ ARABIC LETTER THAL */
-  { 0x05d1, 0x0631 }, /*                   Arabic_ra ر ARABIC LETTER REH */
-  { 0x05d2, 0x0632 }, /*                 Arabic_zain ز ARABIC LETTER ZAIN */
-  { 0x05d3, 0x0633 }, /*                 Arabic_seen س ARABIC LETTER SEEN */
-  { 0x05d4, 0x0634 }, /*                Arabic_sheen ش ARABIC LETTER SHEEN */
-  { 0x05d5, 0x0635 }, /*                  Arabic_sad ص ARABIC LETTER SAD */
-  { 0x05d6, 0x0636 }, /*                  Arabic_dad ض ARABIC LETTER DAD */
-  { 0x05d7, 0x0637 }, /*                  Arabic_tah ط ARABIC LETTER TAH */
-  { 0x05d8, 0x0638 }, /*                  Arabic_zah ظ ARABIC LETTER ZAH */
-  { 0x05d9, 0x0639 }, /*                  Arabic_ain ع ARABIC LETTER AIN */
-  { 0x05da, 0x063a }, /*                Arabic_ghain غ ARABIC LETTER GHAIN */
-  { 0x05e0, 0x0640 }, /*              Arabic_tatweel ـ ARABIC TATWEEL */
-  { 0x05e1, 0x0641 }, /*                  Arabic_feh ف ARABIC LETTER FEH */
-  { 0x05e2, 0x0642 }, /*                  Arabic_qaf ق ARABIC LETTER QAF */
-  { 0x05e3, 0x0643 }, /*                  Arabic_kaf ك ARABIC LETTER KAF */
-  { 0x05e4, 0x0644 }, /*                  Arabic_lam ل ARABIC LETTER LAM */
-  { 0x05e5, 0x0645 }, /*                 Arabic_meem م ARABIC LETTER MEEM */
-  { 0x05e6, 0x0646 }, /*                 Arabic_noon ن ARABIC LETTER NOON */
-  { 0x05e7, 0x0647 }, /*                   Arabic_ha ه ARABIC LETTER HEH */
-  { 0x05e8, 0x0648 }, /*                  Arabic_waw و ARABIC LETTER WAW */
-  { 0x05e9, 0x0649 }, /*          Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */
-  { 0x05ea, 0x064a }, /*                  Arabic_yeh ي ARABIC LETTER YEH */
-  { 0x05eb, 0x064b }, /*             Arabic_fathatan ً ARABIC FATHATAN */
-  { 0x05ec, 0x064c }, /*             Arabic_dammatan ٌ ARABIC DAMMATAN */
-  { 0x05ed, 0x064d }, /*             Arabic_kasratan ٍ ARABIC KASRATAN */
-  { 0x05ee, 0x064e }, /*                Arabic_fatha َ ARABIC FATHA */
-  { 0x05ef, 0x064f }, /*                Arabic_damma ُ ARABIC DAMMA */
-  { 0x05f0, 0x0650 }, /*                Arabic_kasra ِ ARABIC KASRA */
-  { 0x05f1, 0x0651 }, /*               Arabic_shadda ّ ARABIC SHADDA */
-  { 0x05f2, 0x0652 }, /*                Arabic_sukun ْ ARABIC SUKUN */
-  { 0x06a1, 0x0452 }, /*                 Serbian_dje ђ CYRILLIC SMALL LETTER DJE */
-  { 0x06a2, 0x0453 }, /*               Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */
-  { 0x06a3, 0x0451 }, /*                 Cyrillic_io ё CYRILLIC SMALL LETTER IO */
-  { 0x06a4, 0x0454 }, /*                Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */
-  { 0x06a5, 0x0455 }, /*               Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */
-  { 0x06a6, 0x0456 }, /*                 Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */
-  { 0x06a7, 0x0457 }, /*                Ukrainian_yi ї CYRILLIC SMALL LETTER YI */
-  { 0x06a8, 0x0458 }, /*                 Cyrillic_je ј CYRILLIC SMALL LETTER JE */
-  { 0x06a9, 0x0459 }, /*                Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */
-  { 0x06aa, 0x045a }, /*                Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */
-  { 0x06ab, 0x045b }, /*                Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */
-  { 0x06ac, 0x045c }, /*               Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */
-  { 0x06ae, 0x045e }, /*         Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */
-  { 0x06af, 0x045f }, /*               Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */
-  { 0x06b0, 0x2116 }, /*                  numerosign № NUMERO SIGN */
-  { 0x06b1, 0x0402 }, /*                 Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */
-  { 0x06b2, 0x0403 }, /*               Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */
-  { 0x06b3, 0x0401 }, /*                 Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */
-  { 0x06b4, 0x0404 }, /*                Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */
-  { 0x06b5, 0x0405 }, /*               Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */
-  { 0x06b6, 0x0406 }, /*                 Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */
-  { 0x06b7, 0x0407 }, /*                Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */
-  { 0x06b8, 0x0408 }, /*                 Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */
-  { 0x06b9, 0x0409 }, /*                Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */
-  { 0x06ba, 0x040a }, /*                Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */
-  { 0x06bb, 0x040b }, /*                Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */
-  { 0x06bc, 0x040c }, /*               Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */
-  { 0x06be, 0x040e }, /*         Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */
-  { 0x06bf, 0x040f }, /*               Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */
-  { 0x06c0, 0x044e }, /*                 Cyrillic_yu ю CYRILLIC SMALL LETTER YU */
-  { 0x06c1, 0x0430 }, /*                  Cyrillic_a а CYRILLIC SMALL LETTER A */
-  { 0x06c2, 0x0431 }, /*                 Cyrillic_be б CYRILLIC SMALL LETTER BE */
-  { 0x06c3, 0x0446 }, /*                Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */
-  { 0x06c4, 0x0434 }, /*                 Cyrillic_de д CYRILLIC SMALL LETTER DE */
-  { 0x06c5, 0x0435 }, /*                 Cyrillic_ie е CYRILLIC SMALL LETTER IE */
-  { 0x06c6, 0x0444 }, /*                 Cyrillic_ef ф CYRILLIC SMALL LETTER EF */
-  { 0x06c7, 0x0433 }, /*                Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */
-  { 0x06c8, 0x0445 }, /*                 Cyrillic_ha х CYRILLIC SMALL LETTER HA */
-  { 0x06c9, 0x0438 }, /*                  Cyrillic_i и CYRILLIC SMALL LETTER I */
-  { 0x06ca, 0x0439 }, /*             Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */
-  { 0x06cb, 0x043a }, /*                 Cyrillic_ka к CYRILLIC SMALL LETTER KA */
-  { 0x06cc, 0x043b }, /*                 Cyrillic_el л CYRILLIC SMALL LETTER EL */
-  { 0x06cd, 0x043c }, /*                 Cyrillic_em м CYRILLIC SMALL LETTER EM */
-  { 0x06ce, 0x043d }, /*                 Cyrillic_en н CYRILLIC SMALL LETTER EN */
-  { 0x06cf, 0x043e }, /*                  Cyrillic_o о CYRILLIC SMALL LETTER O */
-  { 0x06d0, 0x043f }, /*                 Cyrillic_pe п CYRILLIC SMALL LETTER PE */
-  { 0x06d1, 0x044f }, /*                 Cyrillic_ya я CYRILLIC SMALL LETTER YA */
-  { 0x06d2, 0x0440 }, /*                 Cyrillic_er р CYRILLIC SMALL LETTER ER */
-  { 0x06d3, 0x0441 }, /*                 Cyrillic_es с CYRILLIC SMALL LETTER ES */
-  { 0x06d4, 0x0442 }, /*                 Cyrillic_te т CYRILLIC SMALL LETTER TE */
-  { 0x06d5, 0x0443 }, /*                  Cyrillic_u у CYRILLIC SMALL LETTER U */
-  { 0x06d6, 0x0436 }, /*                Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */
-  { 0x06d7, 0x0432 }, /*                 Cyrillic_ve в CYRILLIC SMALL LETTER VE */
-  { 0x06d8, 0x044c }, /*           Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */
-  { 0x06d9, 0x044b }, /*               Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */
-  { 0x06da, 0x0437 }, /*                 Cyrillic_ze з CYRILLIC SMALL LETTER ZE */
-  { 0x06db, 0x0448 }, /*                Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */
-  { 0x06dc, 0x044d }, /*                  Cyrillic_e э CYRILLIC SMALL LETTER E */
-  { 0x06dd, 0x0449 }, /*              Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */
-  { 0x06de, 0x0447 }, /*                Cyrillic_che ч CYRILLIC SMALL LETTER CHE */
-  { 0x06df, 0x044a }, /*           Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */
-  { 0x06e0, 0x042e }, /*                 Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */
-  { 0x06e1, 0x0410 }, /*                  Cyrillic_A А CYRILLIC CAPITAL LETTER A */
-  { 0x06e2, 0x0411 }, /*                 Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */
-  { 0x06e3, 0x0426 }, /*                Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */
-  { 0x06e4, 0x0414 }, /*                 Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */
-  { 0x06e5, 0x0415 }, /*                 Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */
-  { 0x06e6, 0x0424 }, /*                 Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */
-  { 0x06e7, 0x0413 }, /*                Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */
-  { 0x06e8, 0x0425 }, /*                 Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */
-  { 0x06e9, 0x0418 }, /*                  Cyrillic_I И CYRILLIC CAPITAL LETTER I */
-  { 0x06ea, 0x0419 }, /*             Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */
-  { 0x06eb, 0x041a }, /*                 Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */
-  { 0x06ec, 0x041b }, /*                 Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */
-  { 0x06ed, 0x041c }, /*                 Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */
-  { 0x06ee, 0x041d }, /*                 Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */
-  { 0x06ef, 0x041e }, /*                  Cyrillic_O О CYRILLIC CAPITAL LETTER O */
-  { 0x06f0, 0x041f }, /*                 Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */
-  { 0x06f1, 0x042f }, /*                 Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */
-  { 0x06f2, 0x0420 }, /*                 Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */
-  { 0x06f3, 0x0421 }, /*                 Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */
-  { 0x06f4, 0x0422 }, /*                 Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */
-  { 0x06f5, 0x0423 }, /*                  Cyrillic_U У CYRILLIC CAPITAL LETTER U */
-  { 0x06f6, 0x0416 }, /*                Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */
-  { 0x06f7, 0x0412 }, /*                 Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */
-  { 0x06f8, 0x042c }, /*           Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */
-  { 0x06f9, 0x042b }, /*               Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */
-  { 0x06fa, 0x0417 }, /*                 Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */
-  { 0x06fb, 0x0428 }, /*                Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */
-  { 0x06fc, 0x042d }, /*                  Cyrillic_E Э CYRILLIC CAPITAL LETTER E */
-  { 0x06fd, 0x0429 }, /*              Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */
-  { 0x06fe, 0x0427 }, /*                Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */
-  { 0x06ff, 0x042a }, /*           Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */
-  { 0x07a1, 0x0386 }, /*           Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */
-  { 0x07a2, 0x0388 }, /*         Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */
-  { 0x07a3, 0x0389 }, /*             Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */
-  { 0x07a4, 0x038a }, /*            Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */
-  { 0x07a5, 0x03aa }, /*         Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */
-  { 0x07a7, 0x038c }, /*         Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */
-  { 0x07a8, 0x038e }, /*         Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */
-  { 0x07a9, 0x03ab }, /*       Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */
-  { 0x07ab, 0x038f }, /*           Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */
-  { 0x07ae, 0x0385 }, /*        Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */
-  { 0x07af, 0x2015 }, /*              Greek_horizbar ― HORIZONTAL BAR */
-  { 0x07b1, 0x03ac }, /*           Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */
-  { 0x07b2, 0x03ad }, /*         Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */
-  { 0x07b3, 0x03ae }, /*             Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */
-  { 0x07b4, 0x03af }, /*            Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */
-  { 0x07b5, 0x03ca }, /*          Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */
-  { 0x07b6, 0x0390 }, /*    Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */
-  { 0x07b7, 0x03cc }, /*         Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */
-  { 0x07b8, 0x03cd }, /*         Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */
-  { 0x07b9, 0x03cb }, /*       Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */
-  { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */
-  { 0x07bb, 0x03ce }, /*           Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */
-  { 0x07c1, 0x0391 }, /*                 Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */
-  { 0x07c2, 0x0392 }, /*                  Greek_BETA Β GREEK CAPITAL LETTER BETA */
-  { 0x07c3, 0x0393 }, /*                 Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */
-  { 0x07c4, 0x0394 }, /*                 Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */
-  { 0x07c5, 0x0395 }, /*               Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */
-  { 0x07c6, 0x0396 }, /*                  Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */
-  { 0x07c7, 0x0397 }, /*                   Greek_ETA Η GREEK CAPITAL LETTER ETA */
-  { 0x07c8, 0x0398 }, /*                 Greek_THETA Θ GREEK CAPITAL LETTER THETA */
-  { 0x07c9, 0x0399 }, /*                  Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */
-  { 0x07ca, 0x039a }, /*                 Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */
-  { 0x07cb, 0x039b }, /*                Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */
-  { 0x07cc, 0x039c }, /*                    Greek_MU Μ GREEK CAPITAL LETTER MU */
-  { 0x07cd, 0x039d }, /*                    Greek_NU Ν GREEK CAPITAL LETTER NU */
-  { 0x07ce, 0x039e }, /*                    Greek_XI Ξ GREEK CAPITAL LETTER XI */
-  { 0x07cf, 0x039f }, /*               Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */
-  { 0x07d0, 0x03a0 }, /*                    Greek_PI Π GREEK CAPITAL LETTER PI */
-  { 0x07d1, 0x03a1 }, /*                   Greek_RHO Ρ GREEK CAPITAL LETTER RHO */
-  { 0x07d2, 0x03a3 }, /*                 Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */
-  { 0x07d4, 0x03a4 }, /*                   Greek_TAU Τ GREEK CAPITAL LETTER TAU */
-  { 0x07d5, 0x03a5 }, /*               Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */
-  { 0x07d6, 0x03a6 }, /*                   Greek_PHI Φ GREEK CAPITAL LETTER PHI */
-  { 0x07d7, 0x03a7 }, /*                   Greek_CHI Χ GREEK CAPITAL LETTER CHI */
-  { 0x07d8, 0x03a8 }, /*                   Greek_PSI Ψ GREEK CAPITAL LETTER PSI */
-  { 0x07d9, 0x03a9 }, /*                 Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */
-  { 0x07e1, 0x03b1 }, /*                 Greek_alpha α GREEK SMALL LETTER ALPHA */
-  { 0x07e2, 0x03b2 }, /*                  Greek_beta β GREEK SMALL LETTER BETA */
-  { 0x07e3, 0x03b3 }, /*                 Greek_gamma γ GREEK SMALL LETTER GAMMA */
-  { 0x07e4, 0x03b4 }, /*                 Greek_delta δ GREEK SMALL LETTER DELTA */
-  { 0x07e5, 0x03b5 }, /*               Greek_epsilon ε GREEK SMALL LETTER EPSILON */
-  { 0x07e6, 0x03b6 }, /*                  Greek_zeta ζ GREEK SMALL LETTER ZETA */
-  { 0x07e7, 0x03b7 }, /*                   Greek_eta η GREEK SMALL LETTER ETA */
-  { 0x07e8, 0x03b8 }, /*                 Greek_theta θ GREEK SMALL LETTER THETA */
-  { 0x07e9, 0x03b9 }, /*                  Greek_iota ι GREEK SMALL LETTER IOTA */
-  { 0x07ea, 0x03ba }, /*                 Greek_kappa κ GREEK SMALL LETTER KAPPA */
-  { 0x07eb, 0x03bb }, /*                Greek_lambda λ GREEK SMALL LETTER LAMDA */
-  { 0x07ec, 0x03bc }, /*                    Greek_mu μ GREEK SMALL LETTER MU */
-  { 0x07ed, 0x03bd }, /*                    Greek_nu ν GREEK SMALL LETTER NU */
-  { 0x07ee, 0x03be }, /*                    Greek_xi ξ GREEK SMALL LETTER XI */
-  { 0x07ef, 0x03bf }, /*               Greek_omicron ο GREEK SMALL LETTER OMICRON */
-  { 0x07f0, 0x03c0 }, /*                    Greek_pi π GREEK SMALL LETTER PI */
-  { 0x07f1, 0x03c1 }, /*                   Greek_rho ρ GREEK SMALL LETTER RHO */
-  { 0x07f2, 0x03c3 }, /*                 Greek_sigma σ GREEK SMALL LETTER SIGMA */
-  { 0x07f3, 0x03c2 }, /*       Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */
-  { 0x07f4, 0x03c4 }, /*                   Greek_tau τ GREEK SMALL LETTER TAU */
-  { 0x07f5, 0x03c5 }, /*               Greek_upsilon υ GREEK SMALL LETTER UPSILON */
-  { 0x07f6, 0x03c6 }, /*                   Greek_phi φ GREEK SMALL LETTER PHI */
-  { 0x07f7, 0x03c7 }, /*                   Greek_chi χ GREEK SMALL LETTER CHI */
-  { 0x07f8, 0x03c8 }, /*                   Greek_psi ψ GREEK SMALL LETTER PSI */
-  { 0x07f9, 0x03c9 }, /*                 Greek_omega ω GREEK SMALL LETTER OMEGA */
-  { 0x08a1, 0x23b7 }, /*                 leftradical ⎷ ??? */
-  { 0x08a2, 0x250c }, /*              topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
-  { 0x08a3, 0x2500 }, /*              horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */
-  { 0x08a4, 0x2320 }, /*                 topintegral ⌠ TOP HALF INTEGRAL */
-  { 0x08a5, 0x2321 }, /*                 botintegral ⌡ BOTTOM HALF INTEGRAL */
-  { 0x08a6, 0x2502 }, /*               vertconnector │ BOX DRAWINGS LIGHT VERTICAL */
-  { 0x08a7, 0x23a1 }, /*            topleftsqbracket ⎡ ??? */
-  { 0x08a8, 0x23a3 }, /*            botleftsqbracket ⎣ ??? */
-  { 0x08a9, 0x23a4 }, /*           toprightsqbracket ⎤ ??? */
-  { 0x08aa, 0x23a6 }, /*           botrightsqbracket ⎦ ??? */
-  { 0x08ab, 0x239b }, /*               topleftparens ⎛ ??? */
-  { 0x08ac, 0x239d }, /*               botleftparens ⎝ ??? */
-  { 0x08ad, 0x239e }, /*              toprightparens ⎞ ??? */
-  { 0x08ae, 0x23a0 }, /*              botrightparens ⎠ ??? */
-  { 0x08af, 0x23a8 }, /*        leftmiddlecurlybrace ⎨ ??? */
-  { 0x08b0, 0x23ac }, /*       rightmiddlecurlybrace ⎬ ??? */
-/*  0x08b1                          topleftsummation ? ??? */
-/*  0x08b2                          botleftsummation ? ??? */
-/*  0x08b3                 topvertsummationconnector ? ??? */
-/*  0x08b4                 botvertsummationconnector ? ??? */
-/*  0x08b5                         toprightsummation ? ??? */
-/*  0x08b6                         botrightsummation ? ??? */
-/*  0x08b7                      rightmiddlesummation ? ??? */
-  { 0x08bc, 0x2264 }, /*               lessthanequal ≤ LESS-THAN OR EQUAL TO */
-  { 0x08bd, 0x2260 }, /*                    notequal ≠ NOT EQUAL TO */
-  { 0x08be, 0x2265 }, /*            greaterthanequal ≥ GREATER-THAN OR EQUAL TO */
-  { 0x08bf, 0x222b }, /*                    integral ∫ INTEGRAL */
-  { 0x08c0, 0x2234 }, /*                   therefore ∴ THEREFORE */
-  { 0x08c1, 0x221d }, /*                   variation ∝ PROPORTIONAL TO */
-  { 0x08c2, 0x221e }, /*                    infinity ∞ INFINITY */
-  { 0x08c5, 0x2207 }, /*                       nabla ∇ NABLA */
-  { 0x08c8, 0x223c }, /*                 approximate ∼ TILDE OPERATOR */
-  { 0x08c9, 0x2243 }, /*                similarequal ≃ ASYMPTOTICALLY EQUAL TO */
-  { 0x08cd, 0x21d4 }, /*                    ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */
-  { 0x08ce, 0x21d2 }, /*                     implies ⇒ RIGHTWARDS DOUBLE ARROW */
-  { 0x08cf, 0x2261 }, /*                   identical ≡ IDENTICAL TO */
-  { 0x08d6, 0x221a }, /*                     radical √ SQUARE ROOT */
-  { 0x08da, 0x2282 }, /*                  includedin ⊂ SUBSET OF */
-  { 0x08db, 0x2283 }, /*                    includes ⊃ SUPERSET OF */
-  { 0x08dc, 0x2229 }, /*                intersection ∩ INTERSECTION */
-  { 0x08dd, 0x222a }, /*                       union ∪ UNION */
-  { 0x08de, 0x2227 }, /*                  logicaland ∧ LOGICAL AND */
-  { 0x08df, 0x2228 }, /*                   logicalor ∨ LOGICAL OR */
-  { 0x08ef, 0x2202 }, /*           partialderivative ∂ PARTIAL DIFFERENTIAL */
-  { 0x08f6, 0x0192 }, /*                    function ƒ LATIN SMALL LETTER F WITH HOOK */
-  { 0x08fb, 0x2190 }, /*                   leftarrow ← LEFTWARDS ARROW */
-  { 0x08fc, 0x2191 }, /*                     uparrow ↑ UPWARDS ARROW */
-  { 0x08fd, 0x2192 }, /*                  rightarrow → RIGHTWARDS ARROW */
-  { 0x08fe, 0x2193 }, /*                   downarrow ↓ DOWNWARDS ARROW */
-/*  0x09df                                     blank ? ??? */
-  { 0x09e0, 0x25c6 }, /*                soliddiamond ◆ BLACK DIAMOND */
-  { 0x09e1, 0x2592 }, /*                checkerboard ▒ MEDIUM SHADE */
-  { 0x09e2, 0x2409 }, /*                          ht ␉ SYMBOL FOR HORIZONTAL TABULATION */
-  { 0x09e3, 0x240c }, /*                          ff ␌ SYMBOL FOR FORM FEED */
-  { 0x09e4, 0x240d }, /*                          cr ␍ SYMBOL FOR CARRIAGE RETURN */
-  { 0x09e5, 0x240a }, /*                          lf ␊ SYMBOL FOR LINE FEED */
-  { 0x09e8, 0x2424 }, /*                          nl ␤ SYMBOL FOR NEWLINE */
-  { 0x09e9, 0x240b }, /*                          vt ␋ SYMBOL FOR VERTICAL TABULATION */
-  { 0x09ea, 0x2518 }, /*              lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */
-  { 0x09eb, 0x2510 }, /*               uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */
-  { 0x09ec, 0x250c }, /*                upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */
-  { 0x09ed, 0x2514 }, /*               lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */
-  { 0x09ee, 0x253c }, /*               crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */
-  { 0x09ef, 0x23ba }, /*              horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */
-  { 0x09f0, 0x23bb }, /*              horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */
-  { 0x09f1, 0x2500 }, /*              horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */
-  { 0x09f2, 0x23bc }, /*              horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */
-  { 0x09f3, 0x23bd }, /*              horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */
-  { 0x09f4, 0x251c }, /*                       leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */
-  { 0x09f5, 0x2524 }, /*                      rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */
-  { 0x09f6, 0x2534 }, /*                        bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */
-  { 0x09f7, 0x252c }, /*                        topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */
-  { 0x09f8, 0x2502 }, /*                     vertbar │ BOX DRAWINGS LIGHT VERTICAL */
-  { 0x0aa1, 0x2003 }, /*                     emspace   EM SPACE */
-  { 0x0aa2, 0x2002 }, /*                     enspace   EN SPACE */
-  { 0x0aa3, 0x2004 }, /*                    em3space   THREE-PER-EM SPACE */
-  { 0x0aa4, 0x2005 }, /*                    em4space   FOUR-PER-EM SPACE */
-  { 0x0aa5, 0x2007 }, /*                  digitspace   FIGURE SPACE */
-  { 0x0aa6, 0x2008 }, /*                  punctspace   PUNCTUATION SPACE */
-  { 0x0aa7, 0x2009 }, /*                   thinspace   THIN SPACE */
-  { 0x0aa8, 0x200a }, /*                   hairspace   HAIR SPACE */
-  { 0x0aa9, 0x2014 }, /*                      emdash — EM DASH */
-  { 0x0aaa, 0x2013 }, /*                      endash – EN DASH */
-/*  0x0aac                               signifblank ? ??? */
-  { 0x0aae, 0x2026 }, /*                    ellipsis … HORIZONTAL ELLIPSIS */
-  { 0x0aaf, 0x2025 }, /*             doubbaselinedot ‥ TWO DOT LEADER */
-  { 0x0ab0, 0x2153 }, /*                    onethird ⅓ VULGAR FRACTION ONE THIRD */
-  { 0x0ab1, 0x2154 }, /*                   twothirds ⅔ VULGAR FRACTION TWO THIRDS */
-  { 0x0ab2, 0x2155 }, /*                    onefifth ⅕ VULGAR FRACTION ONE FIFTH */
-  { 0x0ab3, 0x2156 }, /*                   twofifths ⅖ VULGAR FRACTION TWO FIFTHS */
-  { 0x0ab4, 0x2157 }, /*                 threefifths ⅗ VULGAR FRACTION THREE FIFTHS */
-  { 0x0ab5, 0x2158 }, /*                  fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */
-  { 0x0ab6, 0x2159 }, /*                    onesixth ⅙ VULGAR FRACTION ONE SIXTH */
-  { 0x0ab7, 0x215a }, /*                  fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */
-  { 0x0ab8, 0x2105 }, /*                      careof ℅ CARE OF */
-  { 0x0abb, 0x2012 }, /*                     figdash ‒ FIGURE DASH */
-  { 0x0abc, 0x2329 }, /*            leftanglebracket ⟨ LEFT-POINTING ANGLE BRACKET */
-/*  0x0abd                              decimalpoint ? ??? */
-  { 0x0abe, 0x232a }, /*           rightanglebracket ⟩ RIGHT-POINTING ANGLE BRACKET */
-/*  0x0abf                                    marker ? ??? */
-  { 0x0ac3, 0x215b }, /*                   oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */
-  { 0x0ac4, 0x215c }, /*                threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */
-  { 0x0ac5, 0x215d }, /*                 fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */
-  { 0x0ac6, 0x215e }, /*                seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */
-  { 0x0ac9, 0x2122 }, /*                   trademark ™ TRADE MARK SIGN */
-  { 0x0aca, 0x2613 }, /*               signaturemark ☓ SALTIRE */
-/*  0x0acb                         trademarkincircle ? ??? */
-  { 0x0acc, 0x25c1 }, /*            leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */
-  { 0x0acd, 0x25b7 }, /*           rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */
-  { 0x0ace, 0x25cb }, /*                emopencircle ○ WHITE CIRCLE */
-  { 0x0acf, 0x25af }, /*             emopenrectangle ▯ WHITE VERTICAL RECTANGLE */
-  { 0x0ad0, 0x2018 }, /*         leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */
-  { 0x0ad1, 0x2019 }, /*        rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */
-  { 0x0ad2, 0x201c }, /*         leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */
-  { 0x0ad3, 0x201d }, /*        rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */
-  { 0x0ad4, 0x211e }, /*                prescription ℞ PRESCRIPTION TAKE */
-  { 0x0ad6, 0x2032 }, /*                     minutes ′ PRIME */
-  { 0x0ad7, 0x2033 }, /*                     seconds ″ DOUBLE PRIME */
-  { 0x0ad9, 0x271d }, /*                  latincross ✝ LATIN CROSS */
-/*  0x0ada                                  hexagram ? ??? */
-  { 0x0adb, 0x25ac }, /*            filledrectbullet ▬ BLACK RECTANGLE */
-  { 0x0adc, 0x25c0 }, /*         filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */
-  { 0x0add, 0x25b6 }, /*        filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */
-  { 0x0ade, 0x25cf }, /*              emfilledcircle ● BLACK CIRCLE */
-  { 0x0adf, 0x25ae }, /*                emfilledrect ▮ BLACK VERTICAL RECTANGLE */
-  { 0x0ae0, 0x25e6 }, /*            enopencircbullet ◦ WHITE BULLET */
-  { 0x0ae1, 0x25ab }, /*          enopensquarebullet ▫ WHITE SMALL SQUARE */
-  { 0x0ae2, 0x25ad }, /*              openrectbullet ▭ WHITE RECTANGLE */
-  { 0x0ae3, 0x25b3 }, /*             opentribulletup △ WHITE UP-POINTING TRIANGLE */
-  { 0x0ae4, 0x25bd }, /*           opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */
-  { 0x0ae5, 0x2606 }, /*                    openstar ☆ WHITE STAR */
-  { 0x0ae6, 0x2022 }, /*          enfilledcircbullet • BULLET */
-  { 0x0ae7, 0x25aa }, /*            enfilledsqbullet ▪ BLACK SMALL SQUARE */
-  { 0x0ae8, 0x25b2 }, /*           filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */
-  { 0x0ae9, 0x25bc }, /*         filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */
-  { 0x0aea, 0x261c }, /*                 leftpointer ☜ WHITE LEFT POINTING INDEX */
-  { 0x0aeb, 0x261e }, /*                rightpointer ☞ WHITE RIGHT POINTING INDEX */
-  { 0x0aec, 0x2663 }, /*                        club ♣ BLACK CLUB SUIT */
-  { 0x0aed, 0x2666 }, /*                     diamond ♦ BLACK DIAMOND SUIT */
-  { 0x0aee, 0x2665 }, /*                       heart ♥ BLACK HEART SUIT */
-  { 0x0af0, 0x2720 }, /*                maltesecross ✠ MALTESE CROSS */
-  { 0x0af1, 0x2020 }, /*                      dagger † DAGGER */
-  { 0x0af2, 0x2021 }, /*                doubledagger ‡ DOUBLE DAGGER */
-  { 0x0af3, 0x2713 }, /*                   checkmark ✓ CHECK MARK */
-  { 0x0af4, 0x2717 }, /*                 ballotcross ✗ BALLOT X */
-  { 0x0af5, 0x266f }, /*                musicalsharp ♯ MUSIC SHARP SIGN */
-  { 0x0af6, 0x266d }, /*                 musicalflat ♭ MUSIC FLAT SIGN */
-  { 0x0af7, 0x2642 }, /*                  malesymbol ♂ MALE SIGN */
-  { 0x0af8, 0x2640 }, /*                femalesymbol ♀ FEMALE SIGN */
-  { 0x0af9, 0x260e }, /*                   telephone ☎ BLACK TELEPHONE */
-  { 0x0afa, 0x2315 }, /*           telephonerecorder ⌕ TELEPHONE RECORDER */
-  { 0x0afb, 0x2117 }, /*         phonographcopyright ℗ SOUND RECORDING COPYRIGHT */
-  { 0x0afc, 0x2038 }, /*                       caret ‸ CARET */
-  { 0x0afd, 0x201a }, /*          singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */
-  { 0x0afe, 0x201e }, /*          doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */
-/*  0x0aff                                    cursor ? ??? */
-  { 0x0ba3, 0x003c }, /*                   leftcaret < LESS-THAN SIGN */
-  { 0x0ba6, 0x003e }, /*                  rightcaret > GREATER-THAN SIGN */
-  { 0x0ba8, 0x2228 }, /*                   downcaret ∨ LOGICAL OR */
-  { 0x0ba9, 0x2227 }, /*                     upcaret ∧ LOGICAL AND */
-  { 0x0bc0, 0x00af }, /*                     overbar ¯ MACRON */
-  { 0x0bc2, 0x22a5 }, /*                    downtack ⊥ UP TACK */
-  { 0x0bc3, 0x2229 }, /*                      upshoe ∩ INTERSECTION */
-  { 0x0bc4, 0x230a }, /*                   downstile ⌊ LEFT FLOOR */
-  { 0x0bc6, 0x005f }, /*                    underbar _ LOW LINE */
-  { 0x0bca, 0x2218 }, /*                         jot ∘ RING OPERATOR */
-  { 0x0bcc, 0x2395 }, /*                        quad ⎕ APL FUNCTIONAL SYMBOL QUAD */
-  { 0x0bce, 0x22a4 }, /*                      uptack ⊤ DOWN TACK */
-  { 0x0bcf, 0x25cb }, /*                      circle ○ WHITE CIRCLE */
-  { 0x0bd3, 0x2308 }, /*                     upstile ⌈ LEFT CEILING */
-  { 0x0bd6, 0x222a }, /*                    downshoe ∪ UNION */
-  { 0x0bd8, 0x2283 }, /*                   rightshoe ⊃ SUPERSET OF */
-  { 0x0bda, 0x2282 }, /*                    leftshoe ⊂ SUBSET OF */
-  { 0x0bdc, 0x22a2 }, /*                    lefttack ⊢ RIGHT TACK */
-  { 0x0bfc, 0x22a3 }, /*                   righttack ⊣ LEFT TACK */
-  { 0x0cdf, 0x2017 }, /*        hebrew_doublelowline ‗ DOUBLE LOW LINE */
-  { 0x0ce0, 0x05d0 }, /*                hebrew_aleph א HEBREW LETTER ALEF */
-  { 0x0ce1, 0x05d1 }, /*                  hebrew_bet ב HEBREW LETTER BET */
-  { 0x0ce2, 0x05d2 }, /*                hebrew_gimel ג HEBREW LETTER GIMEL */
-  { 0x0ce3, 0x05d3 }, /*                hebrew_dalet ד HEBREW LETTER DALET */
-  { 0x0ce4, 0x05d4 }, /*                   hebrew_he ה HEBREW LETTER HE */
-  { 0x0ce5, 0x05d5 }, /*                  hebrew_waw ו HEBREW LETTER VAV */
-  { 0x0ce6, 0x05d6 }, /*                 hebrew_zain ז HEBREW LETTER ZAYIN */
-  { 0x0ce7, 0x05d7 }, /*                 hebrew_chet ח HEBREW LETTER HET */
-  { 0x0ce8, 0x05d8 }, /*                  hebrew_tet ט HEBREW LETTER TET */
-  { 0x0ce9, 0x05d9 }, /*                  hebrew_yod י HEBREW LETTER YOD */
-  { 0x0cea, 0x05da }, /*            hebrew_finalkaph ך HEBREW LETTER FINAL KAF */
-  { 0x0ceb, 0x05db }, /*                 hebrew_kaph כ HEBREW LETTER KAF */
-  { 0x0cec, 0x05dc }, /*                hebrew_lamed ל HEBREW LETTER LAMED */
-  { 0x0ced, 0x05dd }, /*             hebrew_finalmem ם HEBREW LETTER FINAL MEM */
-  { 0x0cee, 0x05de }, /*                  hebrew_mem מ HEBREW LETTER MEM */
-  { 0x0cef, 0x05df }, /*             hebrew_finalnun ן HEBREW LETTER FINAL NUN */
-  { 0x0cf0, 0x05e0 }, /*                  hebrew_nun נ HEBREW LETTER NUN */
-  { 0x0cf1, 0x05e1 }, /*               hebrew_samech ס HEBREW LETTER SAMEKH */
-  { 0x0cf2, 0x05e2 }, /*                 hebrew_ayin ע HEBREW LETTER AYIN */
-  { 0x0cf3, 0x05e3 }, /*              hebrew_finalpe ף HEBREW LETTER FINAL PE */
-  { 0x0cf4, 0x05e4 }, /*                   hebrew_pe פ HEBREW LETTER PE */
-  { 0x0cf5, 0x05e5 }, /*            hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */
-  { 0x0cf6, 0x05e6 }, /*                 hebrew_zade צ HEBREW LETTER TSADI */
-  { 0x0cf7, 0x05e7 }, /*                 hebrew_qoph ק HEBREW LETTER QOF */
-  { 0x0cf8, 0x05e8 }, /*                 hebrew_resh ר HEBREW LETTER RESH */
-  { 0x0cf9, 0x05e9 }, /*                 hebrew_shin ש HEBREW LETTER SHIN */
-  { 0x0cfa, 0x05ea }, /*                  hebrew_taw ת HEBREW LETTER TAV */
-  { 0x0da1, 0x0e01 }, /*                  Thai_kokai ก THAI CHARACTER KO KAI */
-  { 0x0da2, 0x0e02 }, /*                Thai_khokhai ข THAI CHARACTER KHO KHAI */
-  { 0x0da3, 0x0e03 }, /*               Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */
-  { 0x0da4, 0x0e04 }, /*               Thai_khokhwai ค THAI CHARACTER KHO KHWAI */
-  { 0x0da5, 0x0e05 }, /*                Thai_khokhon ฅ THAI CHARACTER KHO KHON */
-  { 0x0da6, 0x0e06 }, /*             Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */
-  { 0x0da7, 0x0e07 }, /*                 Thai_ngongu ง THAI CHARACTER NGO NGU */
-  { 0x0da8, 0x0e08 }, /*                Thai_chochan จ THAI CHARACTER CHO CHAN */
-  { 0x0da9, 0x0e09 }, /*               Thai_choching ฉ THAI CHARACTER CHO CHING */
-  { 0x0daa, 0x0e0a }, /*               Thai_chochang ช THAI CHARACTER CHO CHANG */
-  { 0x0dab, 0x0e0b }, /*                   Thai_soso ซ THAI CHARACTER SO SO */
-  { 0x0dac, 0x0e0c }, /*                Thai_chochoe ฌ THAI CHARACTER CHO CHOE */
-  { 0x0dad, 0x0e0d }, /*                 Thai_yoying ญ THAI CHARACTER YO YING */
-  { 0x0dae, 0x0e0e }, /*                Thai_dochada ฎ THAI CHARACTER DO CHADA */
-  { 0x0daf, 0x0e0f }, /*                Thai_topatak ฏ THAI CHARACTER TO PATAK */
-  { 0x0db0, 0x0e10 }, /*                Thai_thothan ฐ THAI CHARACTER THO THAN */
-  { 0x0db1, 0x0e11 }, /*          Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */
-  { 0x0db2, 0x0e12 }, /*             Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */
-  { 0x0db3, 0x0e13 }, /*                  Thai_nonen ณ THAI CHARACTER NO NEN */
-  { 0x0db4, 0x0e14 }, /*                  Thai_dodek ด THAI CHARACTER DO DEK */
-  { 0x0db5, 0x0e15 }, /*                  Thai_totao ต THAI CHARACTER TO TAO */
-  { 0x0db6, 0x0e16 }, /*               Thai_thothung ถ THAI CHARACTER THO THUNG */
-  { 0x0db7, 0x0e17 }, /*              Thai_thothahan ท THAI CHARACTER THO THAHAN */
-  { 0x0db8, 0x0e18 }, /*               Thai_thothong ธ THAI CHARACTER THO THONG */
-  { 0x0db9, 0x0e19 }, /*                   Thai_nonu น THAI CHARACTER NO NU */
-  { 0x0dba, 0x0e1a }, /*               Thai_bobaimai บ THAI CHARACTER BO BAIMAI */
-  { 0x0dbb, 0x0e1b }, /*                  Thai_popla ป THAI CHARACTER PO PLA */
-  { 0x0dbc, 0x0e1c }, /*               Thai_phophung ผ THAI CHARACTER PHO PHUNG */
-  { 0x0dbd, 0x0e1d }, /*                   Thai_fofa ฝ THAI CHARACTER FO FA */
-  { 0x0dbe, 0x0e1e }, /*                Thai_phophan พ THAI CHARACTER PHO PHAN */
-  { 0x0dbf, 0x0e1f }, /*                  Thai_fofan ฟ THAI CHARACTER FO FAN */
-  { 0x0dc0, 0x0e20 }, /*             Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */
-  { 0x0dc1, 0x0e21 }, /*                   Thai_moma ม THAI CHARACTER MO MA */
-  { 0x0dc2, 0x0e22 }, /*                  Thai_yoyak ย THAI CHARACTER YO YAK */
-  { 0x0dc3, 0x0e23 }, /*                  Thai_rorua ร THAI CHARACTER RO RUA */
-  { 0x0dc4, 0x0e24 }, /*                     Thai_ru ฤ THAI CHARACTER RU */
-  { 0x0dc5, 0x0e25 }, /*                 Thai_loling ล THAI CHARACTER LO LING */
-  { 0x0dc6, 0x0e26 }, /*                     Thai_lu ฦ THAI CHARACTER LU */
-  { 0x0dc7, 0x0e27 }, /*                 Thai_wowaen ว THAI CHARACTER WO WAEN */
-  { 0x0dc8, 0x0e28 }, /*                 Thai_sosala ศ THAI CHARACTER SO SALA */
-  { 0x0dc9, 0x0e29 }, /*                 Thai_sorusi ษ THAI CHARACTER SO RUSI */
-  { 0x0dca, 0x0e2a }, /*                  Thai_sosua ส THAI CHARACTER SO SUA */
-  { 0x0dcb, 0x0e2b }, /*                  Thai_hohip ห THAI CHARACTER HO HIP */
-  { 0x0dcc, 0x0e2c }, /*                Thai_lochula ฬ THAI CHARACTER LO CHULA */
-  { 0x0dcd, 0x0e2d }, /*                   Thai_oang อ THAI CHARACTER O ANG */
-  { 0x0dce, 0x0e2e }, /*               Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */
-  { 0x0dcf, 0x0e2f }, /*              Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */
-  { 0x0dd0, 0x0e30 }, /*                  Thai_saraa ะ THAI CHARACTER SARA A */
-  { 0x0dd1, 0x0e31 }, /*             Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */
-  { 0x0dd2, 0x0e32 }, /*                 Thai_saraaa า THAI CHARACTER SARA AA */
-  { 0x0dd3, 0x0e33 }, /*                 Thai_saraam ำ THAI CHARACTER SARA AM */
-  { 0x0dd4, 0x0e34 }, /*                  Thai_sarai ิ THAI CHARACTER SARA I */
-  { 0x0dd5, 0x0e35 }, /*                 Thai_saraii ี THAI CHARACTER SARA II */
-  { 0x0dd6, 0x0e36 }, /*                 Thai_saraue ึ THAI CHARACTER SARA UE */
-  { 0x0dd7, 0x0e37 }, /*                Thai_sarauee ื THAI CHARACTER SARA UEE */
-  { 0x0dd8, 0x0e38 }, /*                  Thai_sarau ุ THAI CHARACTER SARA U */
-  { 0x0dd9, 0x0e39 }, /*                 Thai_sarauu ู THAI CHARACTER SARA UU */
-  { 0x0dda, 0x0e3a }, /*                Thai_phinthu ฺ THAI CHARACTER PHINTHU */
-/*  0x0dde                    Thai_maihanakat_maitho ? ??? */
-  { 0x0ddf, 0x0e3f }, /*                   Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */
-  { 0x0de0, 0x0e40 }, /*                  Thai_sarae เ THAI CHARACTER SARA E */
-  { 0x0de1, 0x0e41 }, /*                 Thai_saraae แ THAI CHARACTER SARA AE */
-  { 0x0de2, 0x0e42 }, /*                  Thai_sarao โ THAI CHARACTER SARA O */
-  { 0x0de3, 0x0e43 }, /*          Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */
-  { 0x0de4, 0x0e44 }, /*         Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */
-  { 0x0de5, 0x0e45 }, /*            Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */
-  { 0x0de6, 0x0e46 }, /*               Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */
-  { 0x0de7, 0x0e47 }, /*              Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */
-  { 0x0de8, 0x0e48 }, /*                  Thai_maiek ่ THAI CHARACTER MAI EK */
-  { 0x0de9, 0x0e49 }, /*                 Thai_maitho ้ THAI CHARACTER MAI THO */
-  { 0x0dea, 0x0e4a }, /*                 Thai_maitri ๊ THAI CHARACTER MAI TRI */
-  { 0x0deb, 0x0e4b }, /*            Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */
-  { 0x0dec, 0x0e4c }, /*            Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */
-  { 0x0ded, 0x0e4d }, /*               Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */
-  { 0x0df0, 0x0e50 }, /*                 Thai_leksun ๐ THAI DIGIT ZERO */
-  { 0x0df1, 0x0e51 }, /*                Thai_leknung ๑ THAI DIGIT ONE */
-  { 0x0df2, 0x0e52 }, /*                Thai_leksong ๒ THAI DIGIT TWO */
-  { 0x0df3, 0x0e53 }, /*                 Thai_leksam ๓ THAI DIGIT THREE */
-  { 0x0df4, 0x0e54 }, /*                  Thai_leksi ๔ THAI DIGIT FOUR */
-  { 0x0df5, 0x0e55 }, /*                  Thai_lekha ๕ THAI DIGIT FIVE */
-  { 0x0df6, 0x0e56 }, /*                 Thai_lekhok ๖ THAI DIGIT SIX */
-  { 0x0df7, 0x0e57 }, /*                Thai_lekchet ๗ THAI DIGIT SEVEN */
-  { 0x0df8, 0x0e58 }, /*                Thai_lekpaet ๘ THAI DIGIT EIGHT */
-  { 0x0df9, 0x0e59 }, /*                 Thai_lekkao ๙ THAI DIGIT NINE */
-  { 0x0ea1, 0x3131 }, /*               Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */
-  { 0x0ea2, 0x3132 }, /*          Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */
-  { 0x0ea3, 0x3133 }, /*           Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */
-  { 0x0ea4, 0x3134 }, /*                Hangul_Nieun ㄴ HANGUL LETTER NIEUN */
-  { 0x0ea5, 0x3135 }, /*           Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */
-  { 0x0ea6, 0x3136 }, /*           Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */
-  { 0x0ea7, 0x3137 }, /*               Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */
-  { 0x0ea8, 0x3138 }, /*          Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */
-  { 0x0ea9, 0x3139 }, /*                Hangul_Rieul ㄹ HANGUL LETTER RIEUL */
-  { 0x0eaa, 0x313a }, /*          Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */
-  { 0x0eab, 0x313b }, /*           Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */
-  { 0x0eac, 0x313c }, /*           Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */
-  { 0x0ead, 0x313d }, /*            Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */
-  { 0x0eae, 0x313e }, /*           Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */
-  { 0x0eaf, 0x313f }, /*          Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */
-  { 0x0eb0, 0x3140 }, /*           Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */
-  { 0x0eb1, 0x3141 }, /*                Hangul_Mieum ㅁ HANGUL LETTER MIEUM */
-  { 0x0eb2, 0x3142 }, /*                Hangul_Pieub ㅂ HANGUL LETTER PIEUP */
-  { 0x0eb3, 0x3143 }, /*           Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */
-  { 0x0eb4, 0x3144 }, /*            Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */
-  { 0x0eb5, 0x3145 }, /*                 Hangul_Sios ㅅ HANGUL LETTER SIOS */
-  { 0x0eb6, 0x3146 }, /*            Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */
-  { 0x0eb7, 0x3147 }, /*                Hangul_Ieung ㅇ HANGUL LETTER IEUNG */
-  { 0x0eb8, 0x3148 }, /*                Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */
-  { 0x0eb9, 0x3149 }, /*           Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */
-  { 0x0eba, 0x314a }, /*                Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */
-  { 0x0ebb, 0x314b }, /*               Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */
-  { 0x0ebc, 0x314c }, /*                Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */
-  { 0x0ebd, 0x314d }, /*               Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */
-  { 0x0ebe, 0x314e }, /*                Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */
-  { 0x0ebf, 0x314f }, /*                    Hangul_A ㅏ HANGUL LETTER A */
-  { 0x0ec0, 0x3150 }, /*                   Hangul_AE ㅐ HANGUL LETTER AE */
-  { 0x0ec1, 0x3151 }, /*                   Hangul_YA ㅑ HANGUL LETTER YA */
-  { 0x0ec2, 0x3152 }, /*                  Hangul_YAE ㅒ HANGUL LETTER YAE */
-  { 0x0ec3, 0x3153 }, /*                   Hangul_EO ㅓ HANGUL LETTER EO */
-  { 0x0ec4, 0x3154 }, /*                    Hangul_E ㅔ HANGUL LETTER E */
-  { 0x0ec5, 0x3155 }, /*                  Hangul_YEO ㅕ HANGUL LETTER YEO */
-  { 0x0ec6, 0x3156 }, /*                   Hangul_YE ㅖ HANGUL LETTER YE */
-  { 0x0ec7, 0x3157 }, /*                    Hangul_O ㅗ HANGUL LETTER O */
-  { 0x0ec8, 0x3158 }, /*                   Hangul_WA ㅘ HANGUL LETTER WA */
-  { 0x0ec9, 0x3159 }, /*                  Hangul_WAE ㅙ HANGUL LETTER WAE */
-  { 0x0eca, 0x315a }, /*                   Hangul_OE ㅚ HANGUL LETTER OE */
-  { 0x0ecb, 0x315b }, /*                   Hangul_YO ㅛ HANGUL LETTER YO */
-  { 0x0ecc, 0x315c }, /*                    Hangul_U ㅜ HANGUL LETTER U */
-  { 0x0ecd, 0x315d }, /*                  Hangul_WEO ㅝ HANGUL LETTER WEO */
-  { 0x0ece, 0x315e }, /*                   Hangul_WE ㅞ HANGUL LETTER WE */
-  { 0x0ecf, 0x315f }, /*                   Hangul_WI ㅟ HANGUL LETTER WI */
-  { 0x0ed0, 0x3160 }, /*                   Hangul_YU ㅠ HANGUL LETTER YU */
-  { 0x0ed1, 0x3161 }, /*                   Hangul_EU ㅡ HANGUL LETTER EU */
-  { 0x0ed2, 0x3162 }, /*                   Hangul_YI ㅢ HANGUL LETTER YI */
-  { 0x0ed3, 0x3163 }, /*                    Hangul_I ㅣ HANGUL LETTER I */
-  { 0x0ed4, 0x11a8 }, /*             Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */
-  { 0x0ed5, 0x11a9 }, /*        Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */
-  { 0x0ed6, 0x11aa }, /*         Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */
-  { 0x0ed7, 0x11ab }, /*              Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */
-  { 0x0ed8, 0x11ac }, /*         Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */
-  { 0x0ed9, 0x11ad }, /*         Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */
-  { 0x0eda, 0x11ae }, /*             Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */
-  { 0x0edb, 0x11af }, /*              Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */
-  { 0x0edc, 0x11b0 }, /*        Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */
-  { 0x0edd, 0x11b1 }, /*         Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */
-  { 0x0ede, 0x11b2 }, /*         Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */
-  { 0x0edf, 0x11b3 }, /*          Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */
-  { 0x0ee0, 0x11b4 }, /*         Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */
-  { 0x0ee1, 0x11b5 }, /*        Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */
-  { 0x0ee2, 0x11b6 }, /*         Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */
-  { 0x0ee3, 0x11b7 }, /*              Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */
-  { 0x0ee4, 0x11b8 }, /*              Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */
-  { 0x0ee5, 0x11b9 }, /*          Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */
-  { 0x0ee6, 0x11ba }, /*               Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */
-  { 0x0ee7, 0x11bb }, /*          Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */
-  { 0x0ee8, 0x11bc }, /*              Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */
-  { 0x0ee9, 0x11bd }, /*              Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */
-  { 0x0eea, 0x11be }, /*              Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */
-  { 0x0eeb, 0x11bf }, /*             Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */
-  { 0x0eec, 0x11c0 }, /*              Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */
-  { 0x0eed, 0x11c1 }, /*             Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */
-  { 0x0eee, 0x11c2 }, /*              Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */
-  { 0x0eef, 0x316d }, /*     Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */
-  { 0x0ef0, 0x3171 }, /*    Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */
-  { 0x0ef1, 0x3178 }, /*    Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */
-  { 0x0ef2, 0x317f }, /*              Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */
-  { 0x0ef3, 0x3181 }, /*    Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */
-  { 0x0ef4, 0x3184 }, /*   Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */
-  { 0x0ef5, 0x3186 }, /*          Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */
-  { 0x0ef6, 0x318d }, /*                Hangul_AraeA ㆍ HANGUL LETTER ARAEA */
-  { 0x0ef7, 0x318e }, /*               Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */
-  { 0x0ef8, 0x11eb }, /*            Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */
-  { 0x0ef9, 0x11f0 }, /*  Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */
-  { 0x0efa, 0x11f9 }, /*        Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */
-  { 0x0eff, 0x20a9 }, /*                  Korean_Won ₩ WON SIGN */
-  { 0x13a4, 0x20ac }, /*                        Euro € EURO SIGN */
-  { 0x13bc, 0x0152 }, /*                          OE ΠLATIN CAPITAL LIGATURE OE */
-  { 0x13bd, 0x0153 }, /*                          oe œ LATIN SMALL LIGATURE OE */
-  { 0x13be, 0x0178 }, /*                  Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */
-  { 0x20ac, 0x20ac }, /*                    EuroSign € EURO SIGN */
-};
-
-long keysym2ucs(KeySym keysym)
-{
-    int min = 0;
-    int max = sizeof(keysymtab) / sizeof(struct codepair) - 1;
-    int mid;
-
-    /* first check for Latin-1 characters (1:1 mapping) */
-    if ((keysym >= 0x0020 && keysym <= 0x007e) ||
-        (keysym >= 0x00a0 && keysym <= 0x00ff))
-        return keysym;
-
-    /* also check for directly encoded 24-bit UCS characters */
-    if ((keysym & 0xff000000) == 0x01000000)
-	return keysym & 0x00ffffff;
-
-    /* binary search in table */
-    while (max >= min) {
-	mid = (min + max) / 2;
-	if (keysymtab[mid].keysym < keysym)
-	    min = mid + 1;
-	else if (keysymtab[mid].keysym > keysym)
-	    max = mid - 1;
-	else {
-	    /* found it */
-	    return keysymtab[mid].ucs;
-	}
-    }
-
-    /* no matching Unicode value found */
-    return -1;
-}
diff --git a/src/utils/win/SkAutoCoInitialize.cpp b/src/utils/win/SkAutoCoInitialize.cpp
new file mode 100644
index 0000000..dd6e936
--- /dev/null
+++ b/src/utils/win/SkAutoCoInitialize.cpp
@@ -0,0 +1,29 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <ole2.h>
+#include "SkAutoCoInitialize.h"
+
+SkAutoCoInitialize::SkAutoCoInitialize() :
+    fHR(
+        CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)
+    )
+{ }
+
+SkAutoCoInitialize::~SkAutoCoInitialize() {
+    if (SUCCEEDED(this->fHR)) {
+        CoUninitialize();
+    }
+}
+
+bool SkAutoCoInitialize::succeeded() {
+    return SUCCEEDED(this->fHR) || RPC_E_CHANGED_MODE == this->fHR;
+}
diff --git a/src/utils/win/SkDWriteFontFileStream.cpp b/src/utils/win/SkDWriteFontFileStream.cpp
new file mode 100644
index 0000000..1569158
--- /dev/null
+++ b/src/utils/win/SkDWriteFontFileStream.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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 "SkTypes.h"
+#include "SkDWriteFontFileStream.h"
+#include "SkHRESULT.h"
+
+#include <dwrite.h>
+#include <limits>
+
+///////////////////////////////////////////////////////////////////////////////
+//  SkIDWriteFontFileStream
+
+SkDWriteFontFileStream::SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream)
+    : fFontFileStream(fontFileStream)
+    , fPos(0)
+    , fLockedMemory(NULL)
+    , fFragmentLock(NULL) {
+    fontFileStream->AddRef();
+}
+
+SkDWriteFontFileStream::~SkDWriteFontFileStream() {
+    if (fFragmentLock) {
+        fFontFileStream->ReleaseFileFragment(fFragmentLock);
+    }
+}
+
+const void* SkDWriteFontFileStream::getMemoryBase() {
+    if (fLockedMemory) {
+        return fLockedMemory;
+    }
+
+    UINT64 fileSize;
+    HRNM(fFontFileStream->GetFileSize(&fileSize), "Could not get file size");
+    HRNM(fFontFileStream->ReadFileFragment(&fLockedMemory, 0, fileSize, &fFragmentLock),
+         "Could not lock file fragment.");
+    return fLockedMemory;
+}
+
+bool SkDWriteFontFileStream::rewind() {
+    fPos = 0;
+    return true;
+}
+
+size_t SkDWriteFontFileStream::read(void* buffer, size_t size) {
+    HRESULT hr = S_OK;
+
+    if (NULL == buffer) {
+        UINT64 realFileSize = 0;
+        hr = fFontFileStream->GetFileSize(&realFileSize);
+        if (realFileSize > (std::numeric_limits<size_t>::max)()) {
+            return 0;
+        }
+        size_t fileSize = static_cast<size_t>(realFileSize);
+        if (size == 0) {
+            return fileSize;
+        } else {
+            if (fPos + size > fileSize) {
+                size_t skipped = fileSize - fPos;
+                fPos = fileSize;
+                return skipped;
+            } else {
+                fPos += size;
+                return size;
+            }
+        }
+    }
+
+    const void* start;
+    void* fragmentLock;
+    hr = fFontFileStream->ReadFileFragment(&start, fPos, size, &fragmentLock);
+    if (SUCCEEDED(hr)) {
+        memcpy(buffer, start, size);
+        fFontFileStream->ReleaseFileFragment(fragmentLock);
+        fPos += size;
+        return size;
+    }
+
+    //The read may have failed because we asked for too much data.
+    UINT64 realFileSize = 0;
+    hr = fFontFileStream->GetFileSize(&realFileSize);
+    if (realFileSize > (std::numeric_limits<size_t>::max)()) {
+        return 0;
+    }
+    size_t fileSize = static_cast<size_t>(realFileSize);
+    if (fPos + size > fileSize) {
+        size_t read = fileSize - fPos;
+        hr = fFontFileStream->ReadFileFragment(&start, fPos, read, &fragmentLock);
+        if (SUCCEEDED(hr)) {
+            memcpy(buffer, start, read);
+            fFontFileStream->ReleaseFileFragment(fragmentLock);
+            fPos = fileSize;
+            return read;
+        }
+        return 0;
+    } else {
+        //This means we were within bounds, but failed for some other reason.
+        return 0;
+    }
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+//  SkIDWriteFontFileStreamWrapper
+
+HRESULT SkDWriteFontFileStreamWrapper::Create(SkStream* stream, SkDWriteFontFileStreamWrapper** streamFontFileStream) {
+    *streamFontFileStream = new SkDWriteFontFileStreamWrapper(stream);
+    if (NULL == streamFontFileStream) {
+        return E_OUTOFMEMORY;
+    }
+    return S_OK;
+}
+
+SkDWriteFontFileStreamWrapper::SkDWriteFontFileStreamWrapper(SkStream* stream)
+    : fRefCount(1), fStream(stream) {
+    stream->ref();
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::QueryInterface(REFIID iid, void** ppvObject) {
+    if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileStream)) {
+        *ppvObject = this;
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::AddRef() {
+    return InterlockedIncrement(&fRefCount);
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::Release() {
+    ULONG newCount = InterlockedDecrement(&fRefCount);
+    if (0 == newCount) {
+        delete this;
+    }
+    return newCount;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReadFileFragment(
+    void const** fragmentStart,
+    UINT64 fileOffset,
+    UINT64 fragmentSize,
+    void** fragmentContext)
+{
+    // The loader is responsible for doing a bounds check.
+    UINT64 fileSize;
+    this->GetFileSize(&fileSize);
+    if (fileOffset > fileSize || fragmentSize > fileSize - fileOffset) {
+        *fragmentStart = NULL;
+        *fragmentContext = NULL;
+        return E_FAIL;
+    }
+
+    if (fileOffset + fragmentSize > (std::numeric_limits<size_t>::max)()) {
+        return E_FAIL;
+    }
+
+    const void* data = fStream->getMemoryBase();
+    if (NULL != data) {
+        *fragmentStart = static_cast<BYTE const*>(data) + static_cast<size_t>(fileOffset);
+        *fragmentContext = NULL;
+
+    } else {
+        //May be called from multiple threads.
+        SkAutoMutexAcquire ama(fStreamMutex);
+
+        *fragmentStart = NULL;
+        *fragmentContext = NULL;
+
+        if (!fStream->rewind()) {
+            return E_FAIL;
+        }
+        if (fStream->skip(static_cast<size_t>(fileOffset)) != fileOffset) {
+            return E_FAIL;
+        }
+        SkAutoTDeleteArray<uint8_t> streamData(new uint8_t[static_cast<size_t>(fragmentSize)]);
+        if (fStream->read(streamData.get(), static_cast<size_t>(fragmentSize)) != fragmentSize) {
+            return E_FAIL;
+        }
+
+        *fragmentStart = streamData.get();
+        *fragmentContext = streamData.detach();
+    }
+    return S_OK;
+}
+
+void STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::ReleaseFileFragment(void* fragmentContext) {
+    if (NULL == fragmentContext) {
+        return;
+    }
+    delete [] fragmentContext;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetFileSize(UINT64* fileSize) {
+    *fileSize = fStream->getLength();
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE SkDWriteFontFileStreamWrapper::GetLastWriteTime(UINT64* lastWriteTime) {
+    // The concept of last write time does not apply to this loader.
+    *lastWriteTime = 0;
+    return E_NOTIMPL;
+}
diff --git a/src/utils/win/SkDWriteFontFileStream.h b/src/utils/win/SkDWriteFontFileStream.h
new file mode 100644
index 0000000..b214858
--- /dev/null
+++ b/src/utils/win/SkDWriteFontFileStream.h
@@ -0,0 +1,69 @@
+/*
+ * 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 SkDWriteFontFileStream_DEFINED
+#define SkDWriteFontFileStream_DEFINED
+
+#include "SkTypes.h"
+
+#include "SkStream.h"
+#include "SkTScopedComPtr.h"
+
+#include <dwrite.h>
+
+/**
+ *  An SkStream backed by an IDWriteFontFileStream.
+ *  This allows Skia code to read an IDWriteFontFileStream.
+ */
+class SkDWriteFontFileStream : public SkStream {
+public:
+    explicit SkDWriteFontFileStream(IDWriteFontFileStream* fontFileStream);
+    virtual ~SkDWriteFontFileStream();
+
+    virtual bool rewind() SK_OVERRIDE;
+    virtual size_t read(void* buffer, size_t size) SK_OVERRIDE;
+    virtual const void* getMemoryBase() SK_OVERRIDE;
+
+private:
+    SkTScopedComPtr<IDWriteFontFileStream> fFontFileStream;
+    size_t fPos;
+    const void* fLockedMemory;
+    void* fFragmentLock;
+};
+
+/**
+ *  An IDWriteFontFileStream backed by an SkStream.
+ *  This allows DirectWrite to read an SkStream.
+ */
+class SkDWriteFontFileStreamWrapper : public IDWriteFontFileStream {
+public:
+    // IUnknown methods
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject);
+    virtual ULONG STDMETHODCALLTYPE AddRef();
+    virtual ULONG STDMETHODCALLTYPE Release();
+
+    // IDWriteFontFileStream methods
+    virtual HRESULT STDMETHODCALLTYPE ReadFileFragment(
+        void const** fragmentStart,
+        UINT64 fileOffset,
+        UINT64 fragmentSize,
+        void** fragmentContext);
+
+    virtual void STDMETHODCALLTYPE ReleaseFileFragment(void* fragmentContext);
+    virtual HRESULT STDMETHODCALLTYPE GetFileSize(UINT64* fileSize);
+    virtual HRESULT STDMETHODCALLTYPE GetLastWriteTime(UINT64* lastWriteTime);
+
+    static HRESULT Create(SkStream* stream, SkDWriteFontFileStreamWrapper** streamFontFileStream);
+
+private:
+    explicit SkDWriteFontFileStreamWrapper(SkStream* stream);
+
+    ULONG fRefCount;
+    SkAutoTUnref<SkStream> fStream;
+    SkMutex fStreamMutex;
+};
+#endif
diff --git a/src/utils/win/SkDWriteGeometrySink.cpp b/src/utils/win/SkDWriteGeometrySink.cpp
new file mode 100644
index 0000000..5455e66
--- /dev/null
+++ b/src/utils/win/SkDWriteGeometrySink.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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 "SkTypes.h"
+
+#include "SkDWriteGeometrySink.h"
+#include "SkFloatUtils.h"
+#include "SkPath.h"
+
+#include <dwrite.h>
+#include <d2d1.h>
+
+SkDWriteGeometrySink::SkDWriteGeometrySink(SkPath* path) : fRefCount(1), fPath(path) { }
+
+SkDWriteGeometrySink::~SkDWriteGeometrySink() { }
+
+HRESULT STDMETHODCALLTYPE SkDWriteGeometrySink::QueryInterface(REFIID iid, void **object) {
+    if (NULL == object) {
+        return E_INVALIDARG;
+    }
+    if (iid == __uuidof(IUnknown) || iid == __uuidof(IDWriteGeometrySink)) {
+        *object = static_cast<IDWriteGeometrySink*>(this);
+        this->AddRef();
+        return S_OK;
+    } else {
+        *object = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::AddRef(void) {
+    return static_cast<ULONG>(InterlockedIncrement(&fRefCount));
+}
+
+ULONG STDMETHODCALLTYPE SkDWriteGeometrySink::Release(void) {
+    ULONG res = static_cast<ULONG>(InterlockedDecrement(&fRefCount));
+    if (0 == res) {
+        delete this;
+    }
+    return res;
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::SetFillMode(D2D1_FILL_MODE fillMode) {
+    switch (fillMode) {
+    case D2D1_FILL_MODE_ALTERNATE:
+        fPath->setFillType(SkPath::kEvenOdd_FillType);
+        break;
+    case D2D1_FILL_MODE_WINDING:
+        fPath->setFillType(SkPath::kWinding_FillType);
+        break;
+    default:
+        SkASSERT(!"Unknown D2D1_FILL_MODE.");
+        break;
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) {
+    if (vertexFlags == D2D1_PATH_SEGMENT_NONE || vertexFlags == D2D1_PATH_SEGMENT_FORCE_ROUND_LINE_JOIN) {
+        SkASSERT(!"Invalid D2D1_PATH_SEGMENT value.");
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) {
+    fPath->moveTo(SkFloatToScalar(startPoint.x), SkFloatToScalar(startPoint.y));
+    if (figureBegin == D2D1_FIGURE_BEGIN_HOLLOW) {
+        SkASSERT(!"Invalid D2D1_FIGURE_BEGIN value.");
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::AddLines(const D2D1_POINT_2F *points, UINT pointsCount) {
+    for (const D2D1_POINT_2F *end = &points[pointsCount]; points < end; ++points) {
+        fPath->lineTo(SkFloatToScalar(points->x), SkFloatToScalar(points->y));
+    }
+}
+
+static bool approximately_equal(float a, float b) {
+    const SkFloatingPoint<float, 10> lhs(a), rhs(b);
+    return lhs.AlmostEquals(rhs);
+}
+
+typedef struct {
+    float x;
+    float y;
+} Cubic[4], Quadratic[3];
+
+static bool check_quadratic(const Cubic& cubic, Quadratic& reduction) {
+    float dx10 = cubic[1].x - cubic[0].x;
+    float dx23 = cubic[2].x - cubic[3].x;
+    float midX = cubic[0].x + dx10 * 3 / 2;
+    //NOTE: !approximately_equal(midX - cubic[3].x, dx23 * 3 / 2)
+    //does not work as subnormals get in between the left side and 0.
+    if (!approximately_equal(midX, (dx23 * 3 / 2) + cubic[3].x)) {
+        return false;
+    }
+    float dy10 = cubic[1].y - cubic[0].y;
+    float dy23 = cubic[2].y - cubic[3].y;
+    float midY = cubic[0].y + dy10 * 3 / 2;
+    if (!approximately_equal(midY, (dy23 * 3 / 2) + cubic[3].y)) {
+        return false;
+    }
+    reduction[0] = cubic[0];
+    reduction[1].x = midX;
+    reduction[1].y = midY;
+    reduction[2] = cubic[3];
+    return true;
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) {
+    SkPoint lastPt;
+    fPath->getLastPt(&lastPt);
+    D2D1_POINT_2F prevPt = { SkScalarToFloat(lastPt.fX), SkScalarToFloat(lastPt.fY) };
+
+    for (const D2D1_BEZIER_SEGMENT *end = &beziers[beziersCount]; beziers < end; ++beziers) {
+        Cubic cubic = { { prevPt.x, prevPt.y },
+                        { beziers->point1.x, beziers->point1.y },
+                        { beziers->point2.x, beziers->point2.y },
+                        { beziers->point3.x, beziers->point3.y }, };
+        Quadratic quadratic;
+        if (check_quadratic(cubic, quadratic)) {
+            fPath->quadTo(SkFloatToScalar(quadratic[1].x), SkFloatToScalar(quadratic[1].y),
+                          SkFloatToScalar(quadratic[2].x), SkFloatToScalar(quadratic[2].y));
+        } else {
+            fPath->cubicTo(SkFloatToScalar(beziers->point1.x), SkFloatToScalar(beziers->point1.y),
+                           SkFloatToScalar(beziers->point2.x), SkFloatToScalar(beziers->point2.y),
+                           SkFloatToScalar(beziers->point3.x), SkFloatToScalar(beziers->point3.y));
+        }
+        prevPt = beziers->point3;
+    }
+}
+
+void STDMETHODCALLTYPE SkDWriteGeometrySink::EndFigure(D2D1_FIGURE_END figureEnd) {
+    fPath->close();
+}
+
+HRESULT SkDWriteGeometrySink::Close() {
+    return S_OK;
+}
+
+HRESULT SkDWriteGeometrySink::Create(SkPath* path, IDWriteGeometrySink** geometryToPath) {
+    *geometryToPath = new SkDWriteGeometrySink(path);
+    return S_OK;
+}
diff --git a/src/utils/win/SkDWriteGeometrySink.h b/src/utils/win/SkDWriteGeometrySink.h
new file mode 100644
index 0000000..99a3262
--- /dev/null
+++ b/src/utils/win/SkDWriteGeometrySink.h
@@ -0,0 +1,46 @@
+/*
+ * 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 SkDWriteToPath_DEFINED
+#define SkDWriteToPath_DEFINED
+
+#include "SkTypes.h"
+
+class SkPath;
+
+#include <dwrite.h>
+#include <d2d1.h>
+
+class SkDWriteGeometrySink : public IDWriteGeometrySink {
+private:
+    LONG fRefCount;
+    SkPath* fPath;
+
+    SkDWriteGeometrySink(const SkDWriteGeometrySink&);
+    SkDWriteGeometrySink& operator=(const SkDWriteGeometrySink&);
+
+protected:
+    explicit SkDWriteGeometrySink(SkPath* path);
+    virtual ~SkDWriteGeometrySink();
+
+public:
+    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **object) SK_OVERRIDE;
+    virtual ULONG STDMETHODCALLTYPE AddRef(void) SK_OVERRIDE;
+    virtual ULONG STDMETHODCALLTYPE Release(void) SK_OVERRIDE;
+
+    virtual void STDMETHODCALLTYPE SetFillMode(D2D1_FILL_MODE fillMode) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE SetSegmentFlags(D2D1_PATH_SEGMENT vertexFlags) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE BeginFigure(D2D1_POINT_2F startPoint, D2D1_FIGURE_BEGIN figureBegin) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE AddLines(const D2D1_POINT_2F *points, UINT pointsCount) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE AddBeziers(const D2D1_BEZIER_SEGMENT *beziers, UINT beziersCount) SK_OVERRIDE;
+    virtual void STDMETHODCALLTYPE EndFigure(D2D1_FIGURE_END figureEnd) SK_OVERRIDE;
+    virtual HRESULT STDMETHODCALLTYPE Close() SK_OVERRIDE;
+
+    static HRESULT Create(SkPath* path, IDWriteGeometrySink** geometryToPath);
+};
+
+#endif
diff --git a/src/utils/win/SkHRESULT.cpp b/src/utils/win/SkHRESULT.cpp
new file mode 100644
index 0000000..8b6b79f
--- /dev/null
+++ b/src/utils/win/SkHRESULT.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "SkTypes.h"
+
+#include "SKHRESULT.h"
+
+void SkTraceHR(const char* file, unsigned long line,
+               HRESULT hr, const char* msg) {
+    SkDEBUGCODE(if (NULL != msg) SkDEBUGF(("%s\n", msg)));
+    SkDEBUGF(("%s(%lu) : error 0x%x: ", file, line, hr));
+
+    LPSTR errorText = NULL;
+    FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                   FORMAT_MESSAGE_FROM_SYSTEM |
+                   FORMAT_MESSAGE_IGNORE_INSERTS,
+                   NULL,
+                   hr,
+                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                   (LPSTR) &errorText,
+                   0,
+                   NULL
+    );
+
+    if (NULL == errorText) {
+        SkDEBUGF(("<unknown>\n"));
+    } else {
+        SkDEBUGF((errorText));
+        LocalFree(errorText);
+        errorText = NULL;
+    }
+}
diff --git a/src/utils/win/SkIStream.cpp b/src/utils/win/SkIStream.cpp
new file mode 100644
index 0000000..1d00611
--- /dev/null
+++ b/src/utils/win/SkIStream.cpp
@@ -0,0 +1,270 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+
+#define WIN32_LEAN_AND_MEAN
+#include <Windows.h>
+#include <ole2.h>
+#include "SkIStream.h"
+#include "SkStream.h"
+
+/**
+ * SkBaseIStream
+ */
+SkBaseIStream::SkBaseIStream() : _refcount(1) { }
+SkBaseIStream::~SkBaseIStream() { }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::QueryInterface(REFIID iid
+                                                      , void ** ppvObject)
+{
+    if (NULL == ppvObject) {
+        return E_INVALIDARG;
+    }
+    if (iid == __uuidof(IUnknown)
+        || iid == __uuidof(IStream)
+        || iid == __uuidof(ISequentialStream))
+    {
+        *ppvObject = static_cast<IStream*>(this);
+        AddRef();
+        return S_OK;
+    } else {
+        *ppvObject = NULL;
+        return E_NOINTERFACE;
+    }
+}
+
+ULONG STDMETHODCALLTYPE SkBaseIStream::AddRef(void) {
+    return (ULONG)InterlockedIncrement(&_refcount);
+}
+
+ULONG STDMETHODCALLTYPE SkBaseIStream::Release(void) {
+    ULONG res = (ULONG) InterlockedDecrement(&_refcount);
+    if (0 == res) {
+        delete this;
+    }
+    return res;
+}
+
+// ISequentialStream Interface
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Read(void* pv
+                                            , ULONG cb
+                                            , ULONG* pcbRead)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Write(void const* pv
+                                             , ULONG cb
+                                             , ULONG* pcbWritten)
+{ return E_NOTIMPL; }
+
+// IStream Interface
+HRESULT STDMETHODCALLTYPE SkBaseIStream::SetSize(ULARGE_INTEGER)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::CopyTo(IStream*
+                                              , ULARGE_INTEGER
+                                              , ULARGE_INTEGER*
+                                              , ULARGE_INTEGER*)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Commit(DWORD)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Revert(void)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::LockRegion(ULARGE_INTEGER
+                                                  , ULARGE_INTEGER
+                                                  , DWORD)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::UnlockRegion(ULARGE_INTEGER
+                                                    , ULARGE_INTEGER
+                                                    , DWORD)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Clone(IStream **)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Seek(LARGE_INTEGER liDistanceToMove
+                                            , DWORD dwOrigin
+                                            , ULARGE_INTEGER* lpNewFilePointer)
+{ return E_NOTIMPL; }
+
+HRESULT STDMETHODCALLTYPE SkBaseIStream::Stat(STATSTG* pStatstg
+                                            , DWORD grfStatFlag)
+{ return E_NOTIMPL; }
+
+
+/**
+ * SkIStream
+ */
+SkIStream::SkIStream(SkStream* stream, bool unrefOnRelease)
+    : SkBaseIStream()
+    , fSkStream(stream)
+    , fUnrefOnRelease(unrefOnRelease)
+    , fLocation()
+{
+    this->fSkStream->rewind();
+}
+
+SkIStream::~SkIStream() {
+    if (NULL != this->fSkStream && fUnrefOnRelease) {
+        this->fSkStream->unref();
+    }
+}
+
+HRESULT SkIStream::CreateFromSkStream(SkStream* stream
+                                    , bool unrefOnRelease
+                                    , IStream ** ppStream)
+{
+    *ppStream = new SkIStream(stream, unrefOnRelease);
+    return S_OK;
+}
+
+// ISequentialStream Interface
+HRESULT STDMETHODCALLTYPE SkIStream::Read(void* pv, ULONG cb, ULONG* pcbRead) {
+    *pcbRead = this->fSkStream->read(pv, cb);
+    this->fLocation.QuadPart += *pcbRead;
+    return (*pcbRead == cb) ? S_OK : S_FALSE;
+}
+
+HRESULT STDMETHODCALLTYPE SkIStream::Write(void const* pv
+                                         , ULONG cb
+                                         , ULONG* pcbWritten)
+{
+    return STG_E_CANTSAVE;
+}
+
+// IStream Interface
+HRESULT STDMETHODCALLTYPE SkIStream::Seek(LARGE_INTEGER liDistanceToMove
+                                        , DWORD dwOrigin
+                                        , ULARGE_INTEGER* lpNewFilePointer)
+{
+    HRESULT hr = S_OK;
+
+    switch(dwOrigin) {
+    case STREAM_SEEK_SET: {
+        if (!this->fSkStream->rewind()) {
+            hr = E_FAIL;
+        } else {
+            size_t skipped = this->fSkStream->skip(
+                static_cast<size_t>(liDistanceToMove.QuadPart)
+            );
+            this->fLocation.QuadPart = skipped;
+            if (skipped != liDistanceToMove.QuadPart) {
+                hr = E_FAIL;
+            }
+        }
+        break;
+    }
+    case STREAM_SEEK_CUR: {
+        size_t skipped = this->fSkStream->skip(
+            static_cast<size_t>(liDistanceToMove.QuadPart)
+        );
+        this->fLocation.QuadPart += skipped;
+        if (skipped != liDistanceToMove.QuadPart) {
+            hr = E_FAIL;
+        }
+        break;
+    }
+    case STREAM_SEEK_END: {
+        if (!this->fSkStream->rewind()) {
+            hr = E_FAIL;
+        } else {
+            LONGLONG skip = this->fSkStream->getLength()
+                          + liDistanceToMove.QuadPart;
+            size_t skipped = this->fSkStream->skip(static_cast<size_t>(skip));
+            this->fLocation.QuadPart = skipped;
+            if (skipped != skip) {
+                hr = E_FAIL;
+            }
+        }
+        break;
+    }
+    default:
+        hr = STG_E_INVALIDFUNCTION;
+        break;
+    }
+
+    if (NULL != lpNewFilePointer) {
+        lpNewFilePointer->QuadPart = this->fLocation.QuadPart;
+    }
+    return hr;
+}
+
+HRESULT STDMETHODCALLTYPE SkIStream::Stat(STATSTG* pStatstg
+                                        , DWORD grfStatFlag)
+{
+    if (0 == (grfStatFlag & STATFLAG_NONAME)) {
+        return STG_E_INVALIDFLAG;
+    }
+    pStatstg->pwcsName = NULL;
+    pStatstg->cbSize.QuadPart = this->fSkStream->getLength();
+    pStatstg->clsid = CLSID_NULL;
+    pStatstg->type = STGTY_STREAM;
+    pStatstg->grfMode = STGM_READ;
+    return S_OK;
+}
+
+
+/**
+ * SkIWStream
+ */
+SkWIStream::SkWIStream(SkWStream* stream)
+    : SkBaseIStream()
+    , fSkWStream(stream)
+{ }
+
+SkWIStream::~SkWIStream() {
+    if (NULL != this->fSkWStream) {
+        this->fSkWStream->flush();
+    }
+}
+
+HRESULT SkWIStream::CreateFromSkWStream(SkWStream* stream
+                                      , IStream ** ppStream)
+{
+    *ppStream = new SkWIStream(stream);
+    return S_OK;
+}
+
+// ISequentialStream Interface
+HRESULT STDMETHODCALLTYPE SkWIStream::Write(void const* pv
+                                          , ULONG cb
+                                          , ULONG* pcbWritten)
+{
+    HRESULT hr = S_OK;
+    bool wrote = this->fSkWStream->write(pv, cb);
+    if (wrote) {
+        *pcbWritten = cb;
+    } else {
+        *pcbWritten = 0;
+        hr = S_FALSE;
+    }
+    return hr;
+}
+
+// IStream Interface
+HRESULT STDMETHODCALLTYPE SkWIStream::Commit(DWORD) {
+    this->fSkWStream->flush();
+    return S_OK;
+}
+
+HRESULT STDMETHODCALLTYPE SkWIStream::Stat(STATSTG* pStatstg
+                                         , DWORD grfStatFlag)
+{
+    if (0 == (grfStatFlag & STATFLAG_NONAME)) {
+        return STG_E_INVALIDFLAG;
+    }
+    pStatstg->pwcsName = NULL;
+    pStatstg->cbSize.QuadPart = 0;
+    pStatstg->clsid = CLSID_NULL;
+    pStatstg->type = STGTY_STREAM;
+    pStatstg->grfMode = STGM_WRITE;
+    return S_OK;
+}
diff --git a/src/utils/win/SkWGL_win.cpp b/src/utils/win/SkWGL_win.cpp
new file mode 100644
index 0000000..48b270d
--- /dev/null
+++ b/src/utils/win/SkWGL_win.cpp
@@ -0,0 +1,265 @@
+
+/*
+ * 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 "SkWGL.h"
+
+#include "SkTDArray.h"
+#include "SkTSearch.h"
+
+bool SkWGLExtensions::hasExtension(HDC dc, const char* ext) const {
+    if (NULL == this->fGetExtensionsString) {
+        return false;
+    }
+    if (!strcmp("WGL_ARB_extensions_string", ext)) {
+        return true;
+    }
+    const char* extensionString = this->getExtensionsString(dc);
+    int extLength = strlen(ext);
+
+    while (true) {
+        int n = strcspn(extensionString, " ");
+        if (n == extLength && 0 == strncmp(ext, extensionString, n)) {
+            return true;
+        }
+        if (0 == extensionString[n]) {
+            return false;
+        }
+        extensionString += n+1;
+    }
+
+    return false;
+}
+
+const char* SkWGLExtensions::getExtensionsString(HDC hdc) const {
+    return fGetExtensionsString(hdc);
+}
+
+BOOL SkWGLExtensions::choosePixelFormat(HDC hdc,
+                                        const int* piAttribIList,
+                                        const FLOAT* pfAttribFList,
+                                        UINT nMaxFormats,
+                                        int* piFormats,
+                                        UINT* nNumFormats) const {
+    return fChoosePixelFormat(hdc, piAttribIList, pfAttribFList,
+                              nMaxFormats, piFormats, nNumFormats);
+}
+
+BOOL SkWGLExtensions::getPixelFormatAttribiv(HDC hdc,
+                                             int iPixelFormat,
+                                             int iLayerPlane,
+                                             UINT nAttributes,
+                                             const int *piAttributes,
+                                             int *piValues) const {
+    return fGetPixelFormatAttribiv(hdc, iPixelFormat, iLayerPlane,
+                                   nAttributes, piAttributes, piValues);
+}
+
+BOOL SkWGLExtensions::getPixelFormatAttribfv(HDC hdc,
+                                             int iPixelFormat,
+                                             int iLayerPlane,
+                                             UINT nAttributes,
+                                             const int *piAttributes,
+                                             float *pfValues) const {
+    return fGetPixelFormatAttribfv(hdc, iPixelFormat, iLayerPlane,
+                                   nAttributes, piAttributes, pfValues);
+}
+HGLRC SkWGLExtensions::createContextAttribs(HDC hDC,
+                                            HGLRC hShareContext,
+                                            const int *attribList) const {
+    return fCreateContextAttribs(hDC, hShareContext, attribList);
+}
+
+namespace {
+
+struct PixelFormat {
+    int fFormat;
+    int fCoverageSamples;
+    int fColorSamples;
+    int fChoosePixelFormatRank;
+};
+
+int compare_pf(const PixelFormat* a, const PixelFormat* b) {
+    if (a->fCoverageSamples < b->fCoverageSamples) {
+        return -1;
+    } else if (b->fCoverageSamples < a->fCoverageSamples) {
+        return 1;
+    } else if (a->fColorSamples < b->fColorSamples) {
+        return -1;
+    } else if (b->fColorSamples < a->fColorSamples) {
+        return 1;
+    } else if (a->fChoosePixelFormatRank < b->fChoosePixelFormatRank) {
+        return -1;
+    } else if (b->fChoosePixelFormatRank < a->fChoosePixelFormatRank) {
+        return 1;
+    }
+    return 0;
+}
+}
+
+int SkWGLExtensions::selectFormat(const int formats[],
+                                  int formatCount,
+                                  HDC dc,
+                                  int desiredSampleCount) {
+    PixelFormat desiredFormat = {
+        0,
+        desiredSampleCount,
+        0,
+        0,
+    };
+    SkTDArray<PixelFormat> rankedFormats;
+    rankedFormats.setCount(formatCount);
+    bool supportsCoverage = this->hasExtension(dc,
+                                               "WGL_NV_multisample_coverage");
+    for (int i = 0; i < formatCount; ++i) {
+        static const int queryAttrs[] = {
+            SK_WGL_COVERAGE_SAMPLES,
+            // Keep COLOR_SAMPLES at the end so it can be skipped
+            SK_WGL_COLOR_SAMPLES,
+        };
+        int answers[2];
+        int queryAttrCnt = supportsCoverage ?
+                                    SK_ARRAY_COUNT(queryAttrs) :
+                                    SK_ARRAY_COUNT(queryAttrs) - 1;
+        this->getPixelFormatAttribiv(dc,
+                                     formats[i],
+                                     0,
+                                     queryAttrCnt,
+                                     queryAttrs,
+                                     answers);
+        rankedFormats[i].fFormat =  formats[i];
+        rankedFormats[i].fCoverageSamples = answers[0];
+        rankedFormats[i].fColorSamples = answers[supportsCoverage ? 1 : 0];
+        rankedFormats[i].fChoosePixelFormatRank = i;
+    }
+    qsort(rankedFormats.begin(),
+            rankedFormats.count(),
+            sizeof(PixelFormat),
+            SkCastForQSort(compare_pf));
+    int idx = SkTSearch<PixelFormat>(rankedFormats.begin(),
+                                     rankedFormats.count(),
+                                     desiredFormat,
+                                     sizeof(PixelFormat),
+                                     compare_pf);
+    if (idx < 0) {
+        idx = ~idx;
+    }
+    return rankedFormats[idx].fFormat;
+}
+
+
+namespace {
+
+#if defined(UNICODE)
+    #define STR_LIT(X) L## #X
+#else
+    #define STR_LIT(X) #X
+#endif
+
+#define DUMMY_CLASS STR_LIT("DummyClass")
+
+HWND create_dummy_window() {
+    HMODULE module = GetModuleHandle(NULL);
+    HWND dummy;
+    RECT windowRect;
+    windowRect.left = 0;
+    windowRect.right = 8;
+    windowRect.top = 0;
+    windowRect.bottom = 8;
+
+    WNDCLASS wc;
+
+    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+    wc.lpfnWndProc = (WNDPROC) DefWindowProc;
+    wc.cbClsExtra = 0;
+    wc.cbWndExtra = 0;
+    wc.hInstance = module;
+    wc.hIcon = LoadIcon(NULL, IDI_WINLOGO);
+    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wc.hbrBackground = NULL;
+    wc.lpszMenuName = NULL;
+    wc.lpszClassName = DUMMY_CLASS;
+
+    if(!RegisterClass(&wc)) {
+        return 0;
+    }
+
+    DWORD style, exStyle;
+    exStyle = WS_EX_CLIENTEDGE;
+    style = WS_SYSMENU;
+
+    AdjustWindowRectEx(&windowRect, style, false, exStyle);
+    if(!(dummy = CreateWindowEx(exStyle,
+                                DUMMY_CLASS,
+                                STR_LIT("DummyWindow"),
+                                WS_CLIPSIBLINGS | WS_CLIPCHILDREN | style,
+                                0, 0,
+                                windowRect.right-windowRect.left,
+                                windowRect.bottom-windowRect.top,
+                                NULL, NULL,
+                                module,
+                                NULL))) {
+        UnregisterClass(DUMMY_CLASS, module);
+        return NULL;
+    }
+    ShowWindow(dummy, SW_HIDE);
+
+    return dummy;
+}
+
+void destroy_dummy_window(HWND dummy) {
+    DestroyWindow(dummy);
+    HMODULE module = GetModuleHandle(NULL);
+    UnregisterClass(DUMMY_CLASS, module);
+}
+}
+
+#define GET_PROC(NAME, SUFFIX) f##NAME = \
+                     (##NAME##Proc) wglGetProcAddress("wgl" #NAME #SUFFIX)
+
+SkWGLExtensions::SkWGLExtensions()
+    : fGetExtensionsString(NULL)
+    , fChoosePixelFormat(NULL)
+    , fGetPixelFormatAttribfv(NULL)
+    , fGetPixelFormatAttribiv(NULL)
+    , fCreateContextAttribs(NULL) {
+    HDC prevDC = wglGetCurrentDC();
+    HGLRC prevGLRC = wglGetCurrentContext();
+
+    PIXELFORMATDESCRIPTOR dummyPFD;
+
+    ZeroMemory(&dummyPFD, sizeof(dummyPFD));
+    dummyPFD.nSize = sizeof(dummyPFD);
+    dummyPFD.nVersion = 1;
+    dummyPFD.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
+    dummyPFD.iPixelType = PFD_TYPE_RGBA;
+    dummyPFD.cColorBits  = 32;
+    dummyPFD.cDepthBits  = 0;
+    dummyPFD.cStencilBits = 8;
+    dummyPFD.iLayerType = PFD_MAIN_PLANE;
+    HWND dummyWND = create_dummy_window();
+    if (dummyWND) {
+        HDC dummyDC = GetDC(dummyWND);
+        int dummyFormat = ChoosePixelFormat(dummyDC, &dummyPFD);
+        SetPixelFormat(dummyDC, dummyFormat, &dummyPFD);
+        HGLRC dummyGLRC = wglCreateContext(dummyDC);
+        SkASSERT(dummyGLRC);
+        wglMakeCurrent(dummyDC, dummyGLRC);
+
+        GET_PROC(GetExtensionsString, ARB);
+        GET_PROC(ChoosePixelFormat, ARB);
+        GET_PROC(GetPixelFormatAttribiv, ARB);
+        GET_PROC(GetPixelFormatAttribfv, ARB);
+        GET_PROC(CreateContextAttribs, ARB);
+
+        wglMakeCurrent(dummyDC, NULL);
+        wglDeleteContext(dummyGLRC);
+        destroy_dummy_window(dummyWND);
+    }
+
+    wglMakeCurrent(prevDC, prevGLRC);
+}
diff --git a/src/views/SkBGViewArtist.cpp b/src/views/SkBGViewArtist.cpp
index d9a45b8..c10fe3e 100644
--- a/src/views/SkBGViewArtist.cpp
+++ b/src/views/SkBGViewArtist.cpp
@@ -11,7 +11,7 @@
 
 SkBGViewArtist::SkBGViewArtist(SkColor c)
 {
-	fPaint.setColor(c);
+    fPaint.setColor(c);
 }
 
 SkBGViewArtist::~SkBGViewArtist()
@@ -20,12 +20,11 @@
 
 void SkBGViewArtist::onDraw(SkView*, SkCanvas* canvas)
 {
-	// only works for views that are clipped their bounds.
-	canvas->drawPaint(fPaint);
+    // only works for views that are clipped their bounds.
+    canvas->drawPaint(fPaint);
 }
 
 void SkBGViewArtist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	SkPaint_Inflate(&fPaint, dom, node);
+    SkPaint_Inflate(&fPaint, dom, node);
 }
-
diff --git a/src/views/SkBorderView.cpp b/src/views/SkBorderView.cpp
deleted file mode 100644
index cc1c08b..0000000
--- a/src/views/SkBorderView.cpp
+++ /dev/null
@@ -1,96 +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 "SkBorderView.h"
-#include "SkAnimator.h"
-#include "SkWidgetViews.h"
-#include "SkSystemEventTypes.h"
-#include "SkTime.h"
-#include "SkStackViewLayout.h"
-
-SkBorderView::SkBorderView() : fLeft(SkIntToScalar(0)),
-                               fRight(SkIntToScalar(0)),
-                               fTop(SkIntToScalar(0)),
-                               fBottom(SkIntToScalar(0))
-{
-	fAnim.setHostEventSink(this);
-	init_skin_anim(kBorder_SkinEnum, &fAnim);
-}
-
-SkBorderView::~SkBorderView()
-{
-	
-}
-
-void SkBorderView::setSkin(const char skin[])
-{
-	init_skin_anim(skin, &fAnim);
-}
-
-/* virtual */ void SkBorderView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-}
-
-/*virtual*/ void SkBorderView::onSizeChange()
-{
-	this->INHERITED::onSizeChange();
-	SkEvent evt("user");
-	evt.setString("id", "setDim");
-	evt.setScalar("dimX", this->width());
-	evt.setScalar("dimY", this->height());
-	fAnim.doUserEvent(evt);
-}
-
-/*virtual*/ void SkBorderView::onDraw(SkCanvas* canvas)
-{
-	SkPaint						paint;		
-	SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
-	
-	if (diff == SkAnimator::kDifferent)
-		this->inval(NULL);
-	else if (diff == SkAnimator::kPartiallyDifferent)
-	{
-		SkRect	bounds;
-		fAnim.getInvalBounds(&bounds);
-		this->inval(&bounds);
-	}
-}
-
-/*virtual*/ bool SkBorderView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Inval))
-	{
-		this->inval(NULL);
-		return true;
-	}
-	if (evt.isType("recommendDim"))
-	{
-		evt.findScalar("leftMargin", &fLeft);
-		evt.findScalar("rightMargin", &fRight);
-		evt.findScalar("topMargin", &fTop);
-		evt.findScalar("bottomMargin", &fBottom);
-	
-		//setup_views.cpp uses SkView::Layout instead of SkStackViewLayout
-		//but that gives me an error
-		SkStackViewLayout* layout;
-		fMargin.set(fLeft, fTop, fRight, fBottom);
-		if (this->getLayout())
-		{
-			layout = (SkStackViewLayout*)this->getLayout();
-			layout->setMargin(fMargin);
-		}
-		else
-		{
-			layout = new SkStackViewLayout;
-			layout->setMargin(fMargin);
-			this->setLayout(layout)->unref();
-		}
-		this->invokeLayout();
-	}
-	return this->INHERITED::onEvent(evt);
-}
diff --git a/src/views/SkEvent.cpp b/src/views/SkEvent.cpp
index 0149215..52a0c4d 100644
--- a/src/views/SkEvent.cpp
+++ b/src/views/SkEvent.cpp
@@ -59,9 +59,9 @@
     return strlen(buffer);
 }
 
-void SkEvent::getType(SkString* str) const 
-{ 
-    if (str) 
+void SkEvent::getType(SkString* str) const
+{
+    if (str)
     {
         if ((size_t) fType & 1) // not a pointer
         {
@@ -74,13 +74,13 @@
     }
 }
 
-bool SkEvent::isType(const SkString& str) const 
+bool SkEvent::isType(const SkString& str) const
 {
-    return this->isType(str.c_str(), str.size()); 
+    return this->isType(str.c_str(), str.size());
 }
 
-bool SkEvent::isType(const char type[], size_t typeLen) const 
-{ 
+bool SkEvent::isType(const char type[], size_t typeLen) const
+{
     if (typeLen == 0)
         typeLen = strlen(type);
     if ((size_t) fType & 1) {   // not a pointer
@@ -88,7 +88,7 @@
         size_t len = makeCharArray(chars, (size_t) fType);
         return len == typeLen && strncmp(chars, type, typeLen) == 0;
     }
-    return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0; 
+    return strncmp(fType, type, typeLen) == 0 && fType[typeLen] == 0;
 }
 
 void SkEvent::setType(const char type[], size_t typeLen)
@@ -183,7 +183,7 @@
     {
         if (title)
             SkDebugf("%s ", title);
-            
+
         SkString    etype;
         this->getType(&etype);
         SkDebugf("event<%s> fast32=%d", etype.c_str(), this->getFast32());
@@ -193,7 +193,7 @@
         SkMetaData::Type    mtype;
         int                 count;
         const char*         name;
-        
+
         while ((name = iter.next(&mtype, &count)) != NULL)
         {
             SkASSERT(count > 0);
@@ -296,7 +296,7 @@
         delete this;
         return;
     }
-    
+
     if (delay) {
         this->postTime(SkTime::GetMSecs() + delay);
         return;
@@ -307,7 +307,7 @@
     globals.fEventMutex.acquire();
     bool wasEmpty = SkEvent::Enqueue(this);
     globals.fEventMutex.release();
-    
+
     // call outside of us holding the mutex
     if (wasEmpty) {
         SkEvent::SignalNonEmptyQueue();
@@ -321,11 +321,11 @@
     }
 
     SkEvent_Globals& globals = getGlobals();
-    
+
     globals.fEventMutex.acquire();
     SkMSec queueDelay = SkEvent::EnqueueTime(this, time);
     globals.fEventMutex.release();
-    
+
     // call outside of us holding the mutex
     if ((int32_t)queueDelay != ~0) {
         SkEvent::SignalQueueTimer(queueDelay);
@@ -473,7 +473,7 @@
 int SkEvent::CountEventsOnQueue() {
     SkEvent_Globals& globals = getGlobals();
     globals.fEventMutex.acquire();
-    
+
     int count = 0;
     const SkEvent* evt = globals.fEventQHead;
     while (evt) {
@@ -506,4 +506,3 @@
         evt = next;
     }
 }
-
diff --git a/src/views/SkEventSink.cpp b/src/views/SkEventSink.cpp
index 20d8cdf..b6a3a6e 100644
--- a/src/views/SkEventSink.cpp
+++ b/src/views/SkEventSink.cpp
@@ -14,6 +14,8 @@
 #include "SkThread.h"
 #include "SkTime.h"
 
+SK_DEFINE_INST_COUNT(SkEventSink)
+
 class SkEventSink_Globals {
 public:
     SkEventSink_Globals() {
@@ -161,7 +163,7 @@
     this->addTagList(next);
 }
 
-void SkEventSink::copyListeners(const SkEventSink& sink) 
+void SkEventSink::copyListeners(const SkEventSink& sink)
 {
     SkListenersTagList* sinkList = (SkListenersTagList*)sink.findTagList(kListeners_SkTagList);
     if (sinkList == NULL)
@@ -224,7 +226,7 @@
     if (proc) {
         return proc(evt) ? kHandled_EventResult : kNotHandled_EventResult;
     }
-        
+
     SkEventSink* sink = SkEventSink::FindSink(evt.getTargetID());
     if (sink) {
         return sink->doEvent(evt) ? kHandled_EventResult : kNotHandled_EventResult;
diff --git a/src/views/SkImageView.cpp b/src/views/SkImageView.cpp
deleted file mode 100644
index 8924dd3..0000000
--- a/src/views/SkImageView.cpp
+++ /dev/null
@@ -1,303 +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 "SkImageView.h"
-#include "SkAnimator.h"
-#include "SkBitmap.h"
-#include "SkCanvas.h"
-#include "SkImageDecoder.h"
-#include "SkMatrix.h"
-#include "SkSystemEventTypes.h"
-#include "SkTime.h"
-
-SkImageView::SkImageView()
-{
-	fMatrix		= NULL;
-	fScaleType	= kMatrix_ScaleType;
-
-	fData.fAnim	= NULL;		// handles initializing the other union values
-	fDataIsAnim	= true;
-	
-	fUriIsValid	= false;	// an empty string is not valid
-}
-
-SkImageView::~SkImageView()
-{
-	if (fMatrix)
-		sk_free(fMatrix);
-		
-	this->freeData();
-}
-
-void SkImageView::getUri(SkString* uri) const
-{
-	if (uri)
-		*uri = fUri;
-}
-
-void SkImageView::setUri(const char uri[])
-{
-	if (!fUri.equals(uri))
-	{
-		fUri.set(uri);
-		this->onUriChange();
-	}
-}
-
-void SkImageView::setUri(const SkString& uri)
-{
-	if (fUri != uri)
-	{
-		fUri = uri;
-		this->onUriChange();
-	}
-}
-
-void SkImageView::setScaleType(ScaleType st)
-{
-	SkASSERT((unsigned)st <= kFitEnd_ScaleType);
-
-	if ((ScaleType)fScaleType != st)
-	{
-		fScaleType = SkToU8(st);
-		if (fUriIsValid)
-			this->inval(NULL);
-	}
-}
-
-bool SkImageView::getImageMatrix(SkMatrix* matrix) const
-{
-	if (fMatrix)
-	{
-		SkASSERT(!fMatrix->isIdentity());
-		if (matrix)
-			*matrix = *fMatrix;
-		return true;
-	}
-	else
-	{
-		if (matrix)
-			matrix->reset();
-		return false;
-	}
-}
-
-void SkImageView::setImageMatrix(const SkMatrix* matrix)
-{
-	bool changed = false;
-
-	if (matrix && !matrix->isIdentity())
-	{
-		if (fMatrix == NULL)
-			fMatrix = (SkMatrix*)sk_malloc_throw(sizeof(SkMatrix));
-		*fMatrix = *matrix;
-		changed = true;
-	}
-	else	// set us to identity
-	{
-		if (fMatrix)
-		{
-			SkASSERT(!fMatrix->isIdentity());
-			sk_free(fMatrix);
-			fMatrix = NULL;
-			changed = true;
-		}
-	}
-
-	// only redraw if we changed our matrix and we're not in scaleToFit mode
-	if (changed && this->getScaleType() == kMatrix_ScaleType && fUriIsValid)
-		this->inval(NULL);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-bool SkImageView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Inval))
-	{
-		if (fUriIsValid)
-			this->inval(NULL);
-		return true;
-	}
-	return this->INHERITED::onEvent(evt);
-}
-
-static inline SkMatrix::ScaleToFit scaleTypeToScaleToFit(SkImageView::ScaleType st)
-{
-	SkASSERT(st != SkImageView::kMatrix_ScaleType);
-	SkASSERT((unsigned)st <= SkImageView::kFitEnd_ScaleType);
-
-	SkASSERT(SkImageView::kFitXY_ScaleType - 1 == SkMatrix::kFill_ScaleToFit);
-	SkASSERT(SkImageView::kFitStart_ScaleType - 1 == SkMatrix::kStart_ScaleToFit);
-	SkASSERT(SkImageView::kFitCenter_ScaleType - 1 == SkMatrix::kCenter_ScaleToFit);
-	SkASSERT(SkImageView::kFitEnd_ScaleType - 1 == SkMatrix::kEnd_ScaleToFit);
-	
-	return (SkMatrix::ScaleToFit)(st - 1);
-}
-
-void SkImageView::onDraw(SkCanvas* canvas)
-{
-	SkRect	src;
-	if (!this->getDataBounds(&src))
-	{
-		SkDEBUGCODE(canvas->drawColor(SK_ColorRED);)
-		return;		// nothing to draw
-	}
-		
-	SkAutoCanvasRestore	restore(canvas, true);
-	SkMatrix			matrix;
-	
-	if (this->getScaleType() == kMatrix_ScaleType)
-		(void)this->getImageMatrix(&matrix);
-	else
-	{
-		SkRect	dst;		
-		dst.set(0, 0, this->width(), this->height());
-		matrix.setRectToRect(src, dst, scaleTypeToScaleToFit(this->getScaleType()));
-	}
-	canvas->concat(matrix);
-
-	SkPaint	paint;
-	
-	paint.setAntiAlias(true);
-
-	if (fDataIsAnim)
-	{
-		SkMSec	now = SkTime::GetMSecs();
-		
-		SkAnimator::DifferenceType diff = fData.fAnim->draw(canvas, &paint, now);
-		
-SkDEBUGF(("SkImageView : now = %X[%12.3f], diff = %d\n", now, now/1000., diff));
-
-		if (diff == SkAnimator::kDifferent)
-			this->inval(NULL);
-		else if (diff == SkAnimator::kPartiallyDifferent)
-		{
-			SkRect	bounds;
-			fData.fAnim->getInvalBounds(&bounds);
-			matrix.mapRect(&bounds);	// get the bounds into view coordinates
-			this->inval(&bounds);
-		}
-	}
-	else
-		canvas->drawBitmap(*fData.fBitmap, 0, 0, &paint);
-}
-
-void SkImageView::onInflate(const SkDOM& dom, const SkDOMNode* node)
-{
-	this->INHERITED::onInflate(dom, node);
-	
-	const char* src = dom.findAttr(node, "src");
-	if (src)
-		this->setUri(src);
-
-	int	index = dom.findList(node, "scaleType", "matrix,fitXY,fitStart,fitCenter,fitEnd");
-	if (index >= 0)
-		this->setScaleType((ScaleType)index);
-		
-	// need inflate syntax/reader for matrix
-}
-
-/////////////////////////////////////////////////////////////////////////////////////
-
-void SkImageView::onUriChange()
-{
-	if (this->freeData())
-		this->inval(NULL);
-	fUriIsValid = true;		// give ensureUriIsLoaded() a shot at the new uri
-}
-
-bool SkImageView::freeData()
-{
-	if (fData.fAnim)	// test is valid for all union values
-	{
-		if (fDataIsAnim)
-			delete fData.fAnim;
-		else
-			delete fData.fBitmap;
-
-		fData.fAnim = NULL;	// valid for all union values
-		return true;
-	}
-	return false;
-}
-
-bool SkImageView::getDataBounds(SkRect* bounds)
-{
-	SkASSERT(bounds);
-
-	if (this->ensureUriIsLoaded())
-	{
-		SkScalar width, height;
-
-		if (fDataIsAnim)
-		{			
-			if (SkScalarIsNaN(width = fData.fAnim->getScalar("dimensions", "x")) ||
-				SkScalarIsNaN(height = fData.fAnim->getScalar("dimensions", "y")))
-			{
-				// cons up fake bounds
-				width = this->width();
-				height = this->height();
-			}
-		}
-		else
-		{
-			width = SkIntToScalar(fData.fBitmap->width());
-			height = SkIntToScalar(fData.fBitmap->height());
-		}
-		bounds->set(0, 0, width, height);
-		return true;
-	}
-	return false;
-}
-
-bool SkImageView::ensureUriIsLoaded()
-{
-	if (fData.fAnim)	// test is valid for all union values
-	{
-		SkASSERT(fUriIsValid);
-		return true;
-	}
-	if (!fUriIsValid)
-		return false;
-
-	// try to load the url
-	if (fUri.endsWith(".xml"))	// assume it is screenplay
-	{
-		SkAnimator* anim = new SkAnimator;
-		
-		if (!anim->decodeURI(fUri.c_str()))
-		{
-			delete anim;
-			fUriIsValid = false;
-			return false;
-		}
-		anim->setHostEventSink(this);
-
-		fData.fAnim = anim;
-		fDataIsAnim = true;
-	}
-	else	// assume it is an image format
-	{
-    #if 0
-		SkBitmap* bitmap = new SkBitmap;
-
-		if (!SkImageDecoder::DecodeURL(fUri.c_str(), bitmap))
-		{
-			delete bitmap;
-			fUriIsValid = false;
-			return false;
-		}
-		fData.fBitmap = bitmap;
-		fDataIsAnim = false;
-    #else
-        return false;
-    #endif
-	}
-	return true;
-}
-
diff --git a/src/views/SkListView.cpp b/src/views/SkListView.cpp
deleted file mode 100644
index 20747a2..0000000
--- a/src/views/SkListView.cpp
+++ /dev/null
@@ -1,902 +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 "SkWidget.h"
-#include "SkCanvas.h"
-#include "SkEvent.h"
-#include "SkKey.h"
-#include "SkParsePaint.h"
-#include "SkSystemEventTypes.h"
-
-#if 0
-
-SkEvent* SkListSource::getEvent(int index)
-{
-	return NULL;
-}
-
-#include "SkOSFile.h"
-
-class SkDirListSource : public SkListSource {
-public:
-	SkDirListSource(const char path[], const char suffix[], const char target[])
-		: fPath(path), fSuffix(suffix), fTarget(target)
-	{
-		fCount = -1;
-	}
-	virtual int	countRows()
-	{
-		if (fCount < 0)
-		{
-			fCount = 0;
-			fIter.reset(fPath.c_str(), fSuffix.c_str());
-			while (fIter.next(NULL))
-				fCount += 1;
-			fIter.reset(fPath.c_str(), fSuffix.c_str());
-			fIndex = 0;
-		}
-		return fCount;
-	}
-	virtual void getRow(int index, SkString* left, SkString* right)
-	{
-		(void)this->countRows();
-		SkASSERT((unsigned)index < (unsigned)fCount);
-
-		if (fIndex > index)
-		{
-			fIter.reset(fPath.c_str(), fSuffix.c_str());
-			fIndex = 0;
-		}
-
-		while (fIndex < index)
-		{
-			fIter.next(NULL);
-			fIndex += 1;
-		}
-
-		if (fIter.next(left))
-		{
-			if (left)
-				left->remove(left->size() - fSuffix.size(), fSuffix.size());
-		}
-		else
-		{
-			if (left)
-				left->reset();
-		}
-		if (right)	// only set to ">" if we know we're on a sub-directory
-			right->reset();
-
-		fIndex += 1;
-	}
-	virtual SkEvent* getEvent(int index)
-	{
-		SkASSERT((unsigned)index < (unsigned)fCount);
-
-		SkEvent*	evt = new SkEvent();
-		SkString	label;
-
-		this->getRow(index, &label, NULL);
-		evt->setString("name", label.c_str());
-
-		int c = fPath.c_str()[fPath.size() - 1];
-		if (c != '/' && c != '\\')
-			label.prepend("/");
-		label.prepend(fPath);
-		label.append(fSuffix);
-		evt->setString("path", label.c_str());
-		evt->setS32("index", index);
-		evt->setS32("duration", 22);
-		evt->setType(fTarget);
-		return evt;
-	}
-
-private:
-	SkString		fPath, fSuffix;
-	SkString		fTarget;
-	SkOSFile::Iter	fIter;
-	int				fCount;
-	int				fIndex;
-};
-
-SkListSource* SkListSource::CreateFromDir(const char path[], const char suffix[], const char target[])
-{
-	return new SkDirListSource(path, suffix, target);
-}
-
-//////////////////////////////////////////////////////////////////
-
-class SkDOMListSource : public SkListSource {
-public:
-	enum Type {
-		kUnknown_Type,
-		kDir_Type,
-		kToggle_Type
-	};
-	struct ItemRec {
-		SkString	fLabel;
-		SkString	fTail, fAltTail;
-		SkString	fTarget;
-		Type		fType;
-	};
-
-	SkDOMListSource(const SkDOM& dom, const SkDOM::Node* node) : fDirTail(">")
-	{
-		const SkDOM::Node* child = dom.getFirstChild(node, "item");
-		int	count = 0;
-
-		while (child)
-		{
-			count += 1;
-			child = dom.getNextSibling(child, "item");
-		}
-
-		fCount = count;
-		fList = NULL;
-		if (count)
-		{
-			ItemRec* rec = fList = new ItemRec[count];
-
-			child = dom.getFirstChild(node, "item");
-			while (child)
-			{
-				rec->fLabel.set(dom.findAttr(child, "label"));
-				rec->fTail.set(dom.findAttr(child, "tail"));
-				rec->fAltTail.set(dom.findAttr(child, "alt-tail"));
-				rec->fTarget.set(dom.findAttr(child, "target"));
-				rec->fType = kUnknown_Type;
-
-				int	index = dom.findList(child, "type", "dir,toggle");
-				if (index >= 0)
-					rec->fType = (Type)(index + 1);
-
-				child = dom.getNextSibling(child, "item");
-				rec += 1;
-			}
-		}
-	}
-	virtual ~SkDOMListSource()
-	{
-		delete[] fList;
-	}
-	virtual int	countRows()
-	{
-		return fCount;
-	}
-	virtual void getRow(int index, SkString* left, SkString* right)
-	{
-		SkASSERT((unsigned)index < (unsigned)fCount);
-
-		if (left)
-			*left = fList[index].fLabel;
-		if (right)
-			*right = fList[index].fType == kDir_Type ? fDirTail : fList[index].fTail;
-	}
-	virtual SkEvent* getEvent(int index)
-	{
-		SkASSERT((unsigned)index < (unsigned)fCount);
-
-		if (fList[index].fType == kDir_Type)
-		{
-			SkEvent* evt = new SkEvent();
-			evt->setType(fList[index].fTarget);
-			evt->setFast32(index);
-			return evt;
-		}
-		if (fList[index].fType == kToggle_Type)
-			fList[index].fTail.swap(fList[index].fAltTail);
-
-		return NULL;
-	}
-
-private:
-	int			fCount;
-	ItemRec*	fList;
-	SkString	fDirTail;
-};
-
-SkListSource* SkListSource::CreateFromDOM(const SkDOM& dom, const SkDOM::Node* node)
-{
-	return new SkDOMListSource(dom, node);
-}
-
-//////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////
-
-SkListView::SkListView(U32 flags) : SkWidgetView(flags)
-{
-	fSource = NULL;
-	fScrollIndex = 0;
-	fCurrIndex = -1;
-	fRowHeight = SkIntToScalar(16);
-	fVisibleRowCount = 0;
-	fStrCache = NULL;
-
-	fPaint[kBG_Attr].setColor(0);
-	fPaint[kNormalText_Attr].setTextSize(SkIntToScalar(14));
-	fPaint[kHiliteText_Attr].setTextSize(SkIntToScalar(14));
-	fPaint[kHiliteText_Attr].setColor(SK_ColorWHITE);
-	fPaint[kHiliteCell_Attr].setColor(SK_ColorBLUE);
-}
-
-SkListView::~SkListView()
-{
-	delete[] fStrCache;
-	fSource->safeUnref();
-}
-
-void SkListView::setRowHeight(SkScalar height)
-{
-	SkASSERT(height >= 0);
-
-	if (fRowHeight != height)
-	{
-		fRowHeight = height;
-		this->inval(NULL);
-		this->onSizeChange();
-	}
-}
-
-void SkListView::setSelection(int index)
-{
-	if (fCurrIndex != index)
-	{
-		this->invalSelection();
-		fCurrIndex = index;
-		this->invalSelection();
-		this->ensureSelectionIsVisible();
-
-		{
-			SkEvent	evt;
-			evt.setType("listview-selection");
-			evt.setFast32(index);
-			this->sendEventToParents(evt);
-		}
-	}
-}
-
-void SkListView::moveSelectionUp()
-{
-	if (fSource)
-	{
-		int	index = fCurrIndex;
-		if (index < 0)	// no selection
-			index = fSource->countRows() - 1;
-		else
-			index = SkMax32(index - 1, 0);
-		this->setSelection(index);
-	}
-}
-
-void SkListView::moveSelectionDown()
-{
-	if (fSource)
-	{
-		int	index = fCurrIndex;
-		if (index < 0)	// no selection
-			index = 0;
-		else
-			index = SkMin32(index + 1, fSource->countRows() - 1);
-		this->setSelection(index);
-	}
-}
-
-void SkListView::invalSelection()
-{
-	SkRect	r;
-	if (this->getRowRect(fCurrIndex, &r))
-		this->inval(&r);
-}
-
-void SkListView::ensureSelectionIsVisible()
-{
-	if (fSource == NULL)
-		return;
-
-	if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
-	{
-		int index = this->logicalToVisualIndex(fCurrIndex);
-
-		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
-		{
-			if (index < 0)	// too high
-				fScrollIndex = fCurrIndex;
-			else
-				fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
-			SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
-
-			this->dirtyStrCache();
-			this->inval(NULL);
-		}
-	}
-}
-
-bool SkListView::getRowRect(int index, SkRect* r) const
-{
-	SkASSERT(r);
-	index = this->logicalToVisualIndex(index);
-	if (index >= 0)
-	{
-		SkScalar top = index * fRowHeight;
-
-		if (top < this->height())
-		{
-			if (r)
-				r->set(0, top, this->width(), top + fRowHeight);
-			return true;
-		}
-	}
-	return false;
-}
-
-SkPaint& SkListView::paint(Attr attr)
-{
-	SkASSERT((unsigned)attr < kAttrCount);
-	return fPaint[attr];
-}
-
-SkListSource* SkListView::setListSource(SkListSource* src)
-{
-	if (fSource != src)
-	{
-		SkRefCnt_SafeAssign(fSource, src);
-		this->dirtyStrCache();
-		this->ensureSelectionIsVisible();
-		this->inval(NULL);
-	}
-	return src;
-}
-
-void SkListView::onDraw(SkCanvas* canvas)
-{
-	this->INHERITED::onDraw(canvas);
-
-	canvas->drawPaint(fPaint[kBG_Attr]);
-
-	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
-	if (visibleCount == 0)
-		return;
-
-	this->ensureStrCache(visibleCount);
-	int currIndex = this->logicalToVisualIndex(fCurrIndex);
-
-	if ((unsigned)currIndex < (unsigned)visibleCount)
-	{
-		SkAutoCanvasRestore	restore(canvas, true);
-		SkRect	r;
-
-		canvas->translate(0, currIndex * fRowHeight);
-		(void)this->getRowRect(fScrollIndex, &r);
-		canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
-	}
-
-	SkPaint*	p;
-	SkScalar	y, x = SkIntToScalar(6);
-	SkScalar	rite = this->width() - x;
-
-	{
-		SkScalar ascent, descent;
-		fPaint[kNormalText_Attr].measureText(0, NULL, &ascent, &descent);
-		y = SkScalarHalf(fRowHeight - descent + ascent) - ascent;
-	}
-
-	for (int i = 0; i < visibleCount; i++)
-	{
-		if (i == currIndex)
-			p = &fPaint[kHiliteText_Attr];
-		else
-			p = &fPaint[kNormalText_Attr];
-
-		p->setTextAlign(SkPaint::kLeft_Align);
-		canvas->drawText(fStrCache[i].c_str(), fStrCache[i].size(), x, y, *p);
-		p->setTextAlign(SkPaint::kRight_Align);
-		canvas->drawText(fStrCache[i + visibleCount].c_str(), fStrCache[i + visibleCount].size(), rite, y, *p);
-		canvas->translate(0, fRowHeight);
-	}
-}
-
-void SkListView::onSizeChange()
-{
-	SkScalar count = SkScalarDiv(this->height(), fRowHeight);
-	int		 n = SkScalarFloor(count);
-
-	// only want to show rows that are mostly visible
-	if (n == 0 || count - SkIntToScalar(n) > SK_Scalar1*75/100)
-		n += 1;
-
-	if (fVisibleRowCount != n)
-	{
-		fVisibleRowCount = n;
-		this->ensureSelectionIsVisible();
-		this->dirtyStrCache();
-	}
-}
-
-void SkListView::dirtyStrCache()
-{
-	if (fStrCache)
-	{
-		delete[] fStrCache;
-		fStrCache = NULL;
-	}
-}
-
-void SkListView::ensureStrCache(int count)
-{
-	if (fStrCache == NULL)
-	{
-		fStrCache = new SkString[count << 1];
-
-		if (fSource)
-			for (int i = 0; i < count; i++)
-				fSource->getRow(i + fScrollIndex, &fStrCache[i], &fStrCache[i + count]);
-	}
-}
-
-bool SkListView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Key))
-	{
-		switch (evt.getFast32()) {
-		case kUp_SkKey:
-			this->moveSelectionUp();
-			return true;
-		case kDown_SkKey:
-			this->moveSelectionDown();
-			return true;
-		case kRight_SkKey:
-		case kOK_SkKey:
-			if (fSource && fCurrIndex >= 0)
-			{
-				SkEvent* evt = fSource->getEvent(fCurrIndex);
-				if (evt)
-				{
-					SkView* view = this->sendEventToParents(*evt);
-					delete evt;
-					return view != NULL;
-				}
-				else	// hack to make toggle work
-				{
-					this->dirtyStrCache();
-					this->inval(NULL);
-				}
-			}
-			break;
-		}
-	}
-	return this->INHERITED::onEvent(evt);
-}
-
-void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-
-	SkScalar			x;
-	const SkDOM::Node*	child;
-
-	if (dom.findScalar(node, "row-height", &x))
-		this->setRowHeight(x);
-
-	if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL)
-		SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
-
-	// look for a listsource
-	{
-		SkListSource* src = NULL;
-
-		if ((child = dom.getFirstChild(node, "file-listsource")) != NULL)
-		{
-			const char* path = dom.findAttr(child, "path");
-			if (path)
-				src = SkListSource::CreateFromDir(	path,
-													dom.findAttr(child, "filter"),
-													dom.findAttr(child, "target"));
-		}
-		else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL)
-		{
-			src = SkListSource::CreateFromDOM(dom, child);
-		}
-
-		if (src)
-		{
-			this->setListSource(src)->unref();
-			this->setSelection(0);
-		}
-	}
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////////////////
-
-#include "SkImageDecoder.h"
-#include "SkShader.h"
-
-class SkScrollBarView : public SkView {
-public:
-	SkScrollBarView(const char bg[], const char fg[])
-	{
-		fBGRef = SkBitmapRef::Decode(bg, true);
-		fFGRef = SkBitmapRef::Decode(fg, true);
-
-		if (fBGRef)
-			this->setWidth(SkIntToScalar(fBGRef->bitmap().width()));
-	}
-	~SkScrollBarView()
-	{
-		delete fBGRef;
-		delete fFGRef;
-	}
-protected:
-	virtual void onDraw(SkCanvas* canvas)
-	{
-		if (fBGRef == NULL) return;
-
-		SkPaint	paint;
-
-		SkShader* shader = SkShader::CreateBitmapShader(fBGRef->bitmap(), false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
-		paint.setShader(shader)->unref();
-
-		canvas->drawPaint(paint);
-	}
-private:
-	SkBitmapRef*	fBGRef, *fFGRef;
-};
-
-SkGridView::SkGridView(U32 flags) : SkWidgetView(flags)
-{
-	fSource = NULL;
-	fCurrIndex = -1;
-	fVisibleCount.set(0, 0);
-
-	fPaint[kBG_Attr].setColor(SK_ColorWHITE);
-	fPaint[kHiliteCell_Attr].setColor(SK_ColorYELLOW);
-	fPaint[kHiliteCell_Attr].setStyle(SkPaint::kStroke_Style);
-	fPaint[kHiliteCell_Attr].setAntiAliasOn(true);
-	fPaint[kHiliteCell_Attr].setStrokeWidth(SK_Scalar1*3);
-
-	fScrollBar = new SkScrollBarView("icons/scrollbarGrey.jpg", "icons/scrollbarBlue.jpg");
-	this->attachChildToFront(fScrollBar)->unref();
-	fScrollBar->setVisibleP(true);
-}
-
-SkGridView::~SkGridView()
-{
-	fSource->safeUnref();
-}
-
-void SkGridView::getCellSize(SkPoint* size) const
-{
-	if (size)
-		*size = fCellSize;
-}
-
-void SkGridView::setCellSize(SkScalar x, SkScalar y)
-{
-	SkASSERT(x >= 0 && y >= 0);
-
-	if (!fCellSize.equals(x, y))
-	{
-		fCellSize.set(x, y);
-		this->inval(NULL);
-	}
-}
-
-void SkGridView::setSelection(int index)
-{
-	if (fCurrIndex != index)
-	{
-		this->invalSelection();
-		fCurrIndex = index;
-		this->invalSelection();
-		this->ensureSelectionIsVisible();
-
-		// this generates the click
-		{
-			SkEvent	evt;
-			evt.setType("listview-selection");
-			evt.setFast32(index);
-			this->sendEventToParents(evt);
-		}
-	}
-}
-
-void SkGridView::moveSelectionUp()
-{
-	if (fSource)
-	{
-		int	index = fCurrIndex;
-		if (index < 0)	// no selection
-			index = fSource->countRows() - 1;
-		else
-			index = SkMax32(index - 1, 0);
-		this->setSelection(index);
-	}
-}
-
-void SkGridView::moveSelectionDown()
-{
-	if (fSource)
-	{
-		int	index = fCurrIndex;
-		if (index < 0)	// no selection
-			index = 0;
-		else
-			index = SkMin32(index + 1, fSource->countRows() - 1);
-		this->setSelection(index);
-	}
-}
-
-void SkGridView::invalSelection()
-{
-	SkRect	r;
-	if (this->getCellRect(fCurrIndex, &r))
-	{
-		SkScalar inset = 0;
-		if (fPaint[kHiliteCell_Attr].getStyle() != SkPaint::kFill_Style)
-			inset += fPaint[kHiliteCell_Attr].getStrokeWidth() / 2;
-		if (fPaint[kHiliteCell_Attr].isAntiAliasOn())
-			inset += SK_Scalar1;
-		r.inset(-inset, -inset);
-		this->inval(&r);
-	}
-}
-
-void SkGridView::ensureSelectionIsVisible()
-{
-	if (fSource == NULL)
-		return;
-#if 0
-	if ((unsigned)fCurrIndex < (unsigned)fSource->countRows())
-	{
-		int index = this->logicalToVisualIndex(fCurrIndex);
-
-		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
-		{
-			if (index < 0)	// too high
-				fScrollIndex = fCurrIndex;
-			else
-				fScrollIndex = fCurrIndex - fVisibleRowCount + 1;
-			SkASSERT((unsigned)fScrollIndex < (unsigned)fSource->countRows());
-
-			this->dirtyStrCache();
-			this->inval(NULL);
-		}
-	}
-#endif
-}
-
-bool SkGridView::getCellRect(int index, SkRect* r) const
-{
-	if (fVisibleCount.fY == 0)
-		return false;
-
-	index = this->logicalToVisualIndex(index);
-	if (index >= 0)
-	{
-		SkRect	bounds;
-		int row = index / fVisibleCount.fY;
-		int col = index % fVisibleCount.fY;
-
-		bounds.set(0, 0, fCellSize.fX, fCellSize.fY);
-		bounds.offset(col * (fCellSize.fX + SkIntToScalar(col > 0)),
-					  row * (fCellSize.fY + SkIntToScalar(row > 0)));
-
-		if (bounds.fTop < this->height())
-		{
-			if (r)
-				*r = bounds;
-			return true;
-		}
-	}
-	return false;
-}
-
-SkPaint& SkGridView::paint(Attr attr)
-{
-	SkASSERT((unsigned)attr < kAttrCount);
-	return fPaint[attr];
-}
-
-SkListSource* SkGridView::setListSource(SkListSource* src)
-{
-	if (fSource != src)
-	{
-		SkRefCnt_SafeAssign(fSource, src);
-	//	this->dirtyStrCache();
-		this->ensureSelectionIsVisible();
-		this->inval(NULL);
-	}
-	return src;
-}
-
-#include "SkShader.h"
-
-static void copybits(SkCanvas* canvas, const SkBitmap& bm, const SkRect& dst, const SkPaint& paint)
-{
-	SkRect		src;
-	SkMatrix	matrix;
-
-	src.set(0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height()));
-	if (matrix.setRectToRect(src, dst))
-	{
-		SkPaint	  p(paint);
-		SkShader* shader = SkShader::CreateBitmapShader(bm, false, SkPaint::kNo_FilterType, SkShader::kClamp_TileMode);
-		p.setShader(shader)->unref();
-
-		shader->setLocalMatrix(matrix);
-		canvas->drawRect(dst, p);
-	}
-}
-
-#include "SkImageDecoder.h"
-
-void SkGridView::onDraw(SkCanvas* canvas)
-{
-	this->INHERITED::onDraw(canvas);
-
-	canvas->drawPaint(fPaint[kBG_Attr]);
-
-	if (fSource == NULL)
-		return;
-
-#if 0
-	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRows() - fScrollIndex);
-	if (visibleCount == 0)
-		return;
-
-	this->ensureStrCache(visibleCount);
-	int currIndex = this->logicalToVisualIndex(fCurrIndex);
-#endif
-
-	SkPaint	p;
-	for (int i = 0; i < fSource->countRows(); i++)
-	{
-		bool	 forced = false;
-		SkEvent* evt = fSource->getEvent(i);
-		SkASSERT(evt);
-		SkString path(evt->findString("path"));
-		delete evt;
-
-		SkBitmapRef* bmr = SkBitmapRef::Decode(path.c_str(), false);
-		if (bmr == NULL)
-		{
-			bmr = SkBitmapRef::Decode(path.c_str(), true);
-			if (bmr)
-				forced = true;
-		}
-
-		if (bmr)
-		{
-			SkAutoTDelete<SkBitmapRef>	autoRef(bmr);
-			SkRect	r;
-			if (!this->getCellRect(i, &r))
-				break;
-			copybits(canvas, bmr->bitmap(), r, p);
-		}
-		// only draw one forced bitmap at a time
-		if (forced)
-		{
-			this->inval(NULL);	// could inval only the remaining visible cells...
-			break;
-		}
-	}
-
-	// draw the hilite
-	{
-		SkRect	r;
-		if (fCurrIndex >= 0 && this->getCellRect(fCurrIndex, &r))
-			canvas->drawRect(r, fPaint[kHiliteCell_Attr]);
-	}
-}
-
-static int check_count(int n, SkScalar s)
-{
-	// only want to show cells that are mostly visible
-	if (n == 0 || s - SkIntToScalar(n) > SK_Scalar1*75/100)
-		n += 1;
-	return n;
-}
-
-void SkGridView::onSizeChange()
-{
-	fScrollBar->setHeight(this->height());
-	fScrollBar->setLoc(this->locX() + this->width() - fScrollBar->width(), 0);
-
-	if (fCellSize.equals(0, 0))
-	{
-		fVisibleCount.set(0, 0);
-		return;
-	}
-
-	SkScalar rows = SkScalarDiv(this->height(), fCellSize.fY);
-	SkScalar cols = SkScalarDiv(this->width(), fCellSize.fX);
-	int		 y = SkScalarFloor(rows);
-	int		 x = SkScalarFloor(cols);
-
-	y = check_count(y, rows);
-	x = check_count(x, cols);
-
-	if (!fVisibleCount.equals(x, y))
-	{
-		fVisibleCount.set(x, y);
-		this->ensureSelectionIsVisible();
-	//	this->dirtyStrCache();
-	}
-}
-
-bool SkGridView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Key))
-	{
-		switch (evt.getFast32()) {
-		case kUp_SkKey:
-			this->moveSelectionUp();
-			return true;
-		case kDown_SkKey:
-			this->moveSelectionDown();
-			return true;
-		case kRight_SkKey:
-		case kOK_SkKey:
-			if (fSource && fCurrIndex >= 0)
-			{
-				SkEvent* evt = fSource->getEvent(fCurrIndex);
-				if (evt)
-				{
-					// augment the event with our local rect
-					(void)this->getCellRect(fCurrIndex, (SkRect*)evt->setScalars("local-rect", 4, NULL));
-
-					SkView* view = this->sendEventToParents(*evt);
-					delete evt;
-					return view != NULL;
-				}
-			}
-			break;
-		}
-	}
-	return this->INHERITED::onEvent(evt);
-}
-
-void SkGridView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-
-	SkScalar			x[2];
-	const SkDOM::Node*	child;
-
-	if (dom.findScalars(node, "cell-size", x, 2))
-		this->setCellSize(x[0], x[1]);
-
-	if ((child = dom.getFirstChild(node, "hilite-paint")) != NULL)
-		SkPaint_Inflate(&this->paint(kHiliteCell_Attr), dom, child);
-
-	// look for a listsource
-	{
-		SkListSource* src = NULL;
-
-		if ((child = dom.getFirstChild(node, "file-listsource")) != NULL)
-		{
-			const char* path = dom.findAttr(child, "path");
-			if (path)
-				src = SkListSource::CreateFromDir(	path,
-													dom.findAttr(child, "filter"),
-													dom.findAttr(child, "target"));
-		}
-		else if ((child = dom.getFirstChild(node, "xml-listsource")) != NULL)
-		{
-			src = SkListSource::CreateFromDOM(dom, child);
-		}
-
-		if (src)
-		{
-			this->setListSource(src)->unref();
-			this->setSelection(0);
-		}
-	}
-	this->onSizeChange();
-}
-
-#endif
diff --git a/src/views/SkListWidget.cpp b/src/views/SkListWidget.cpp
deleted file mode 100644
index 4d95e0f..0000000
--- a/src/views/SkListWidget.cpp
+++ /dev/null
@@ -1,630 +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 "SkWidgetViews.h"
-
-#include "SkAnimator.h"
-#include "SkScrollBarView.h"
-
-extern void init_skin_anim(const char name[], SkAnimator*);
-
-struct SkListView::BindingRec {
-	SkString	fSlotName;
-	int			fFieldIndex;
-};
-
-SkListView::SkListView()
-{
-	fSource = NULL;				// our list-source
-	fScrollBar = NULL;
-	fAnims = NULL;				// array of animators[fVisibleRowCount]
-	fBindings = NULL;			// our fields->slot array
-	fBindingCount = 0;			// number of entries in fSlots array
-	fScrollIndex = 0;			// number of cells to skip before first visible cell
-	fCurrIndex = -1;			// index of "selected" cell
-	fVisibleRowCount = 0;		// number of cells that can fit in our bounds
-	fAnimContentDirty = true;	// true if fAnims[] have their correct content
-	fAnimFocusDirty = true;
-
-	fHeights[kNormal_Height] = SkIntToScalar(16);
-	fHeights[kSelected_Height] = SkIntToScalar(16);
-	
-	this->setFlags(this->getFlags() | kFocusable_Mask);
-}
-
-SkListView::~SkListView()
-{
-	SkSafeUnref(fScrollBar);
-	SkSafeUnref(fSource);
-	delete[] fAnims;
-	delete[] fBindings;
-}
-
-void SkListView::setHasScrollBar(bool hasSB)
-{
-	if (hasSB != this->hasScrollBar())
-	{
-		if (hasSB)
-		{
-			SkASSERT(fScrollBar == NULL);
-			fScrollBar = (SkScrollBarView*)SkWidgetFactory(kScroll_WidgetEnum);
-			fScrollBar->setVisibleP(true);
-			this->attachChildToFront(fScrollBar);
-			fScrollBar->setHeight(this->height());	// assume it auto-sets its width
-		//	fScrollBar->setLoc(this->getContentWidth(), 0);
-			fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
-		}
-		else
-		{
-			SkASSERT(fScrollBar);
-			fScrollBar->detachFromParent();
-			fScrollBar->unref();
-			fScrollBar = NULL;
-		}
-		this->dirtyCache(kAnimContent_DirtyFlag);
-	}
-}
-
-void SkListView::setSelection(int index)
-{
-	if (fCurrIndex != index)
-	{
-		fAnimFocusDirty = true;
-		this->inval(NULL);
-
-		this->invalSelection();
-		fCurrIndex = index;
-		this->invalSelection();
-		this->ensureSelectionIsVisible();
-	}
-}
-
-bool SkListView::moveSelectionUp()
-{
-	if (fSource)
-	{
-		int	index = fCurrIndex;
-		if (index < 0)	// no selection
-			index = fSource->countRecords() - 1;
-		else
-			index = SkMax32(index - 1, 0);
-		
-		if (fCurrIndex != index)
-		{
-			this->setSelection(index);
-			return true;
-		}
-	}
-	return false;
-}
-
-bool SkListView::moveSelectionDown()
-{
-	if (fSource)
-	{
-		int	index = fCurrIndex;
-		if (index < 0)	// no selection
-			index = 0;
-		else
-			index = SkMin32(index + 1, fSource->countRecords() - 1);
-		
-		if (fCurrIndex != index)
-		{
-			this->setSelection(index);
-			return true;
-		}
-	}
-	return false;
-}
-
-void SkListView::invalSelection()
-{
-	SkRect	r;
-	if (this->getRowRect(fCurrIndex, &r))
-		this->inval(&r);
-}
-
-void SkListView::ensureSelectionIsVisible()
-{
-	if (fSource && (unsigned)fCurrIndex < (unsigned)fSource->countRecords())
-	{
-		int index = this->logicalToVisualIndex(fCurrIndex);
-
-		if ((unsigned)index >= (unsigned)fVisibleRowCount)	// need to scroll
-		{
-			int newIndex;
-			
-			if (index < 0)	// too high
-				newIndex = fCurrIndex;
-			else
-				newIndex = fCurrIndex - fVisibleRowCount + 1;
-			SkASSERT((unsigned)newIndex < (unsigned)fSource->countRecords());
-			this->inval(NULL);
-			
-			if (fScrollIndex != newIndex)
-			{
-				fScrollIndex = newIndex;
-				if (fScrollBar)
-					fScrollBar->setStart(newIndex);
-				this->dirtyCache(kAnimContent_DirtyFlag);
-			}
-		}
-	}
-}
-
-SkScalar SkListView::getContentWidth() const
-{
-	SkScalar width = this->width();
-	
-	if (fScrollBar)
-	{
-		width -= fScrollBar->width();
-		if (width < 0)
-			width = 0;
-	}
-	return width;
-}
-
-bool SkListView::getRowRect(int index, SkRect* r) const
-{
-	SkASSERT(r);
-
-	index = this->logicalToVisualIndex(index);
-	if (index >= 0)
-	{
-		int	selection = this->logicalToVisualIndex(fCurrIndex);
-		
-		SkScalar height = fHeights[index == selection ? kSelected_Height : kNormal_Height];
-		SkScalar top = index * fHeights[kNormal_Height];
-
-		if (index > selection && selection >= 0)
-			top += fHeights[kSelected_Height] - fHeights[kNormal_Height];	
-
-		if (top < this->height())
-		{
-			if (r)
-				r->set(0, top, this->getContentWidth(), top + height);
-			return true;
-		}
-	}
-	return false;
-}
-
-SkListSource* SkListView::setListSource(SkListSource* src)
-{
-	if (fSource != src)
-	{
-		SkRefCnt_SafeAssign(fSource, src);
-		this->ensureSelectionIsVisible();
-		this->inval(NULL);
-		
-		if (fScrollBar)
-			fScrollBar->setTotal(fSource->countRecords());
-	}
-	return src;
-}
-
-void SkListView::dirtyCache(unsigned dirtyFlags)
-{
-	if (dirtyFlags & kAnimCount_DirtyFlag)
-	{
-		delete fAnims;
-		fAnims = NULL;
-		fAnimContentDirty = true;
-		fAnimFocusDirty = true;
-	}
-	if (dirtyFlags & kAnimContent_DirtyFlag)
-	{
-		if (!fAnimContentDirty)
-		{
-			this->inval(NULL);
-			fAnimContentDirty = true;
-		}
-		fAnimFocusDirty = true;
-	}
-}
-
-bool SkListView::ensureCache()
-{
-	if (fSkinName.size() == 0)
-		return false;
-
-	if (fAnims == NULL)
-	{
-		int n = SkMax32(1, fVisibleRowCount);
-
-		SkASSERT(fAnimContentDirty);
-		fAnims = new SkAnimator[n];
-		for (int i = 0; i < n; i++)
-		{
-			fAnims[i].setHostEventSink(this);
-			init_skin_anim(fSkinName.c_str(), &fAnims[i]);
-		}
-		
-		fHeights[kNormal_Height] = fAnims[0].getScalar("idleHeight", "value");
-		fHeights[kSelected_Height] = fAnims[0].getScalar("focusedHeight", "value");
-
-		fAnimFocusDirty = true;
-	}
-
-	if (fAnimContentDirty && fSource)
-	{
-		fAnimContentDirty = false;
-
-		SkString	str;
-		SkEvent		evt("user");
-		evt.setString("id", "setFields");
-		evt.setS32("rowCount", fVisibleRowCount);
-		
-		SkEvent	dimEvt("user");
-		dimEvt.setString("id", "setDim");
-		dimEvt.setScalar("dimX", this->getContentWidth());
-		dimEvt.setScalar("dimY", this->height());
-
-		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
-		{
-			evt.setS32("relativeIndex", i - fScrollIndex);
-			for (int j = 0; j < fBindingCount; j++)
-			{
-				fSource->getRecord(i, fBindings[j].fFieldIndex, &str);
-//SkDEBUGF(("getRecord(%d,%d,%s) slot(%s)\n", i, fBindings[j].fFieldIndex, str.c_str(), fBindings[j].fSlotName.c_str()));
-				evt.setString(fBindings[j].fSlotName.c_str(), str.c_str());
-			}
-			(void)fAnims[i % fVisibleRowCount].doUserEvent(evt);
-			(void)fAnims[i % fVisibleRowCount].doUserEvent(dimEvt);
-		}
-		fAnimFocusDirty = true;
-	}
-
-	if (fAnimFocusDirty)
-	{
-//SkDEBUGF(("service fAnimFocusDirty\n"));
-		fAnimFocusDirty = false;
-
-		SkEvent		focusEvt("user");
-		focusEvt.setString("id", "setFocus");
-
-		for (int i = fScrollIndex; i < fScrollIndex + fVisibleRowCount; i++)
-		{
-			focusEvt.setS32("FOCUS", i == fCurrIndex);
-			(void)fAnims[i % fVisibleRowCount].doUserEvent(focusEvt);
-		}
-	}
-
-	return true;
-}
-
-void SkListView::ensureVisibleRowCount()
-{
-	SkScalar	height = this->height();
-	int			n = 0;
-	
-	if (height > 0)
-	{
-		n = 1;
-		height -= fHeights[kSelected_Height];
-		if (height > 0)
-		{
-			SkScalar count = SkScalarDiv(height, fHeights[kNormal_Height]);
-			n += SkScalarFloor(count);
-			if (count - SkIntToScalar(n) > SK_Scalar1*3/4)
-				n += 1;
-				
-		//	SkDebugf("count %g, n %d\n", count/65536., n);
-		}
-	}
-
-	if (fVisibleRowCount != n)
-	{
-		if (fScrollBar)
-			fScrollBar->setShown(n);
-
-		fVisibleRowCount = n;
-		this->ensureSelectionIsVisible();
-		this->dirtyCache(kAnimCount_DirtyFlag | kAnimContent_DirtyFlag);
-	}
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-#include "SkSystemEventTypes.h"
-#include "SkTime.h"
-
-void SkListView::onSizeChange()
-{
-	this->INHERITED::onSizeChange();
-
-	if (fScrollBar)
-		fScrollBar->setLoc(this->width()-SkIntToScalar(10), 0);
-
-	this->ensureVisibleRowCount();
-}
-
-void SkListView::onDraw(SkCanvas* canvas)
-{
-	this->INHERITED::onDraw(canvas);
-
-	this->ensureVisibleRowCount();
-
-	int	visibleCount = SkMin32(fVisibleRowCount, fSource->countRecords() - fScrollIndex);
-	if (visibleCount == 0 || !this->ensureCache())
-		return;
-
-//SkDebugf("visibleCount %d scrollIndex %d currIndex %d\n", visibleCount, fScrollIndex, fCurrIndex);
-
-	SkAutoCanvasRestore	ar(canvas, true);
-	SkMSec				now = SkTime::GetMSecs();
-	SkRect				bounds;
-
-	bounds.fLeft	= 0;
-	bounds.fRight	= this->getContentWidth();
-	bounds.fBottom	= 0;
-	// assign bounds.fTop inside the loop
-
-	// hack to reveal our bounds for debugging
-	if (this->hasFocus())
-		canvas->drawARGB(0x11, 0, 0, 0xFF);
-	else
-		canvas->drawARGB(0x11, 0x88, 0x88, 0x88);
-
-	for (int i = fScrollIndex; i < fScrollIndex + visibleCount; i++)
-	{
-		SkPaint	 paint;
-		SkScalar height = fHeights[i == fCurrIndex ? kSelected_Height : kNormal_Height];
-
-		bounds.fTop = bounds.fBottom;
-		bounds.fBottom += height;
-		
-		canvas->save();
-		if (fAnims[i % fVisibleRowCount].draw(canvas, &paint, now) != SkAnimator::kNotDifferent)
-			this->inval(&bounds);
-		canvas->restore();
-
-		canvas->translate(0, height);
-	}
-}
-
-bool SkListView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Key))
-	{
-		switch (evt.getFast32()) {
-		case kUp_SkKey:
-			return this->moveSelectionUp();
-		case kDown_SkKey:
-			return this->moveSelectionDown();
-		case kRight_SkKey:
-		case kOK_SkKey:
-			this->postWidgetEvent();
-			return true;
-		default:
-			break;
-		}
-	}
-	return this->INHERITED::onEvent(evt);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-static const char gListViewEventSlot[] = "sk-listview-slot-name";
-
-/*virtual*/ bool SkListView::onPrepareWidgetEvent(SkEvent* evt)
-{
-	if (fSource && fCurrIndex >= 0 && this->INHERITED::onPrepareWidgetEvent(evt) &&
-		fSource->prepareWidgetEvent(evt, fCurrIndex))
-	{
-		evt->setS32(gListViewEventSlot, fCurrIndex);
-		return true;
-	}
-	return false;
-}
-
-int SkListView::GetWidgetEventListIndex(const SkEvent& evt)
-{
-	int32_t	index;
-
-	return evt.findS32(gListViewEventSlot, &index) ? index : -1;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////
-
-void SkListView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-	
-	{
-		bool hasScrollBar;
-		if (dom.findBool(node, "scrollBar", &hasScrollBar))
-			this->setHasScrollBar(hasScrollBar);
-	}
-
-	const SkDOM::Node*	child;
-
-	if ((child = dom.getFirstChild(node, "bindings")) != NULL)
-	{
-		delete[] fBindings;
-		fBindings = NULL;
-		fBindingCount = 0;
-
-		SkListSource* listSrc = SkListSource::Factory(dom.findAttr(child, "data-fields"));
-		SkASSERT(listSrc);
-		fSkinName.set(dom.findAttr(child, "skin-slots"));
-		SkASSERT(fSkinName.size());
-
-		this->setListSource(listSrc)->unref();
-			
-		int count = dom.countChildren(child, "bind");
-		if (count > 0)
-		{
-			fBindings = new BindingRec[count];
-			count = 0;	// reuse this to count up to the number of valid bindings
-
-			child = dom.getFirstChild(child, "bind");
-			SkASSERT(child);
-			do {
-				const char* fieldName = dom.findAttr(child, "field");
-				const char* slotName = dom.findAttr(child, "slot");
-				if (fieldName && slotName)
-				{
-					fBindings[count].fFieldIndex = listSrc->findFieldIndex(fieldName);
-					if (fBindings[count].fFieldIndex >= 0)
-						fBindings[count++].fSlotName.set(slotName);
-				}
-			} while ((child = dom.getNextSibling(child, "bind")) != NULL);
-
-			fBindingCount = SkToU16(count);
-			if (count == 0)
-			{
-				SkDEBUGF(("SkListView::onInflate: no valid <bind> elements in <listsource>\n"));
-				delete[] fBindings;
-			}
-		}
-		this->dirtyCache(kAnimCount_DirtyFlag);
-		this->setSelection(0);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////////////////////
-
-class SkXMLListSource : public SkListSource {
-public:
-	SkXMLListSource(const char doc[], size_t len);
-	virtual ~SkXMLListSource()
-	{
-		delete[] fFields;
-		delete[] fRecords;
-	}
-
-	virtual int countFields() { return fFieldCount; }
-	virtual void getFieldName(int index, SkString* field)
-	{
-		SkASSERT((unsigned)index < (unsigned)fFieldCount);
-		if (field)
-			*field = fFields[index];
-	}
-	virtual int findFieldIndex(const char field[])
-	{
-		for (int i = 0; i < fFieldCount; i++)
-			if (fFields[i].equals(field))
-				return i;
-		return -1;
-	}
-
-	virtual int	countRecords() { return fRecordCount; }
-	virtual void getRecord(int rowIndex, int fieldIndex, SkString* data)
-	{
-		SkASSERT((unsigned)rowIndex < (unsigned)fRecordCount);
-		SkASSERT((unsigned)fieldIndex < (unsigned)fFieldCount);
-		if (data)
-			*data = fRecords[rowIndex * fFieldCount + fieldIndex];
-	}
-
-	virtual bool prepareWidgetEvent(SkEvent* evt, int rowIndex)
-	{
-		// hack, for testing right now. Need the xml to tell us what to jam in and where
-		SkString	data;
-		
-		this->getRecord(rowIndex, 0, &data);
-		evt->setString("xml-listsource", data.c_str());
-		return true;
-	}
-	
-private:
-	SkString*	fFields;	// [fFieldCount]
-	SkString*	fRecords;	// [fRecordCount][fFieldCount]
-	int			fFieldCount, fRecordCount;
-};
-
-#include "SkDOM.h"
-
-SkXMLListSource::SkXMLListSource(const char doc[], size_t len)
-{
-	fFieldCount = fRecordCount = 0;
-	fFields = fRecords = NULL;
-
-	SkDOM	dom;
-
-	const SkDOM::Node* node = dom.build(doc, len);
-	SkASSERT(node);
-	const SkDOM::Node*	child;	
-
-	child = dom.getFirstChild(node, "fields");
-	if (child)
-	{
-		fFieldCount = dom.countChildren(child, "field");
-		fFields = new SkString[fFieldCount];
-
-		int n = 0;
-		child = dom.getFirstChild(child, "field");
-		while (child)
-		{
-			fFields[n].set(dom.findAttr(child, "name"));
-			child = dom.getNextSibling(child, "field");
-			n += 1;
-		}
-		SkASSERT(n == fFieldCount);
-	}
-	
-	child = dom.getFirstChild(node, "records");
-	if (child)
-	{
-		fRecordCount = dom.countChildren(child, "record");
-		fRecords = new SkString[fRecordCount * fFieldCount];
-
-		int n = 0;
-		child = dom.getFirstChild(child, "record");
-		while (child)
-		{
-			for (int i = 0; i < fFieldCount; i++)
-				fRecords[n * fFieldCount + i].set(dom.findAttr(child, fFields[i].c_str()));
-			child = dom.getNextSibling(child, "record");
-			n += 1;
-		}
-		SkASSERT(n == fRecordCount);
-	}
-}
-
-/////////////////////////////////////////////////////////////////////////////////////////////
-
-SkListSource* SkListSource::Factory(const char name[])
-{
-	static const char gDoc[] =
-		"<db name='contacts.db'>"
-			"<fields>"
-				"<field name='name'/>"
-				"<field name='work-num'/>"
-				"<field name='home-num'/>"
-				"<field name='type'/>"
-			"</fields>"
-			"<records>"
-				"<record name='Andy McFadden' work-num='919 357-1234' home-num='919 123-4567' type='0'/>"
-				"<record name='Brian Swetland' work-num='919 123-1234' home-num='929 123-4567' type='1' />"
-				"<record name='Chris Desalvo' work-num='919 345-1234' home-num='949 123-4567' type='1' />"
-				"<record name='Chris White' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
-				"<record name='Dan Bornstein' work-num='919 357-1234' home-num='919 123-4567' type='0' />"
-				"<record name='Don Cung' work-num='919 123-1234' home-num='929 123-4567' type='2' />"
-				"<record name='Eric Fischer' work-num='919 345-1234' home-num='949 123-4567' type='2' />"
-				"<record name='Ficus Kirkpatric' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
-				"<record name='Jack Veenstra' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
-				"<record name='Jeff Yaksick' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
-				"<record name='Joe Onorato' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
-				"<record name='Mathias Agopian' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
-				"<record name='Mike Fleming' work-num='919 234-1234' home-num='939 123-4567' type='2' />"
-				"<record name='Nick Sears' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
-				"<record name='Rich Miner' work-num='919 234-1234' home-num='939 123-4567' type='1' />"
-				"<record name='Tracey Cole' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
-				"<record name='Wei Huang' work-num='919 234-1234' home-num='939 123-4567' type='0' />"
-			"</records>"
-		"</db>";
-		
-//SkDebugf("doc size %d\n", sizeof(gDoc)-1);
-	return new SkXMLListSource(gDoc, sizeof(gDoc) - 1);
-}
-
-
-
diff --git a/src/views/SkOSMenu.cpp b/src/views/SkOSMenu.cpp
index ed37541..3de0a9e 100644
--- a/src/views/SkOSMenu.cpp
+++ b/src/views/SkOSMenu.cpp
@@ -11,7 +11,7 @@
 static int gOSMenuCmd = 7000;
 
 SkOSMenu::SkOSMenu(const char title[]) {
-	fTitle.set(title);
+    fTitle.set(title);
 }
 
 SkOSMenu::~SkOSMenu() {
@@ -46,7 +46,7 @@
     }
 }
 
-bool SkOSMenu::handleKeyEquivalent(SkUnichar key) {    
+bool SkOSMenu::handleKeyEquivalent(SkUnichar key) {
     int value = 0, size = 0;
     bool state;
     SkOSMenu::TriState tristate;
@@ -89,7 +89,7 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-SkOSMenu::Item::Item(const char label[], SkOSMenu::Type type, 
+SkOSMenu::Item::Item(const char label[], SkOSMenu::Type type,
                      const char slotName[], SkEvent* evt) {
     fLabel.set(label);
     fSlotName.set(slotName);
@@ -133,7 +133,7 @@
 static const char* gList_Items_Str = "SkOSMenuList_Items";
 static const char* gList_ItemCount_S32 = "SkOSMenuList_ItemCount";
 
-int SkOSMenu::appendItem(const char label[], Type type, const char slotName[], 
+int SkOSMenu::appendItem(const char label[], Type type, const char slotName[],
                          SkEvent* evt) {
     SkOSMenu::Item* item = new Item(label, type, slotName, evt);
     fItems.append(1, &item);
@@ -147,7 +147,7 @@
     return appendItem(label, SkOSMenu::kAction_Type, "", evt);
 }
 
-int SkOSMenu::appendList(const char label[], const char slotName[], 
+int SkOSMenu::appendList(const char label[], const char slotName[],
                          SkEventSinkID target, int index, const char option[], ...) {
     SkEvent* evt = new SkEvent(gMenuEventType, target);
     va_list args;
@@ -168,8 +168,8 @@
     return appendItem(label, SkOSMenu::kList_Type, slotName, evt);
 }
 
-int SkOSMenu::appendSlider(const char label[], const char slotName[], 
-                           SkEventSinkID target, SkScalar min, SkScalar max, 
+int SkOSMenu::appendSlider(const char label[], const char slotName[],
+                           SkEventSinkID target, SkScalar min, SkScalar max,
                            SkScalar defaultValue) {
     SkEvent* evt = new SkEvent(gMenuEventType, target);
     evt->setScalar(gSlider_Min_Scalar, min);
@@ -178,7 +178,7 @@
     return appendItem(label, SkOSMenu::kSlider_Type, slotName, evt);
 }
 
-int SkOSMenu::appendSwitch(const char label[], const char slotName[], 
+int SkOSMenu::appendSwitch(const char label[], const char slotName[],
                            SkEventSinkID target, bool defaultState) {
     SkEvent* evt = new SkEvent(gMenuEventType, target);
     evt->setBool(slotName, defaultState);
@@ -192,7 +192,7 @@
     return appendItem(label, SkOSMenu::kTriState_Type, slotName, evt);
 }
 
-int SkOSMenu::appendTextField(const char label[], const char slotName[], 
+int SkOSMenu::appendTextField(const char label[], const char slotName[],
                               SkEventSinkID target, const char placeholder[]) {
     SkEvent* evt = new SkEvent(gMenuEventType, target);
     evt->setString(slotName, placeholder);
@@ -234,7 +234,7 @@
 }
 
 bool SkOSMenu::FindListIndex(const SkEvent& evt, const char slotName[], int* value) {
-    return evt.isType(gMenuEventType) && evt.findS32(slotName, value); 
+    return evt.isType(gMenuEventType) && evt.findS32(slotName, value);
 }
 
 bool SkOSMenu::FindSliderValue(const SkEvent& evt, const char slotName[], SkScalar* value) {
diff --git a/src/views/SkParsePaint.cpp b/src/views/SkParsePaint.cpp
index 4839439..344da0b 100644
--- a/src/views/SkParsePaint.cpp
+++ b/src/views/SkParsePaint.cpp
@@ -13,98 +13,97 @@
 
 static SkShader* inflate_shader(const SkDOM& dom, const SkDOM::Node* node)
 {
-	if ((node = dom.getFirstChild(node, "shader")) == NULL)
-		return NULL;
+    if ((node = dom.getFirstChild(node, "shader")) == NULL)
+        return NULL;
 
-	const char* str;
+    const char* str;
 
-	if (dom.hasAttr(node, "type", "linear-gradient"))
-	{
-		SkColor		colors[2];
-		SkPoint		pts[2];
+    if (dom.hasAttr(node, "type", "linear-gradient"))
+    {
+        SkColor        colors[2];
+        SkPoint        pts[2];
 
-		colors[0] = colors[1] = SK_ColorBLACK;	// need to initialized the alpha to opaque, since FindColor doesn't set it
-		if ((str = dom.findAttr(node, "c0")) != NULL &&
-			SkParse::FindColor(str, &colors[0]) &&
-			(str = dom.findAttr(node, "c1")) != NULL &&
-			SkParse::FindColor(str, &colors[1]) &&
-			dom.findScalars(node, "p0", &pts[0].fX, 2) &&
-			dom.findScalars(node, "p1", &pts[1].fX, 2))
-		{
-			SkShader::TileMode	mode = SkShader::kClamp_TileMode;
-			int					index;
+        colors[0] = colors[1] = SK_ColorBLACK;    // need to initialized the alpha to opaque, since FindColor doesn't set it
+        if ((str = dom.findAttr(node, "c0")) != NULL &&
+            SkParse::FindColor(str, &colors[0]) &&
+            (str = dom.findAttr(node, "c1")) != NULL &&
+            SkParse::FindColor(str, &colors[1]) &&
+            dom.findScalars(node, "p0", &pts[0].fX, 2) &&
+            dom.findScalars(node, "p1", &pts[1].fX, 2))
+        {
+            SkShader::TileMode    mode = SkShader::kClamp_TileMode;
+            int                    index;
 
-			if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
-				mode = (SkShader::TileMode)index;
-			return SkGradientShader::CreateLinear(pts, colors, NULL, 2, mode);
-		}
-	}
-	else if (dom.hasAttr(node, "type", "bitmap"))
-	{
-		if ((str = dom.findAttr(node, "src")) == NULL)
-			return NULL;
+            if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+                mode = (SkShader::TileMode)index;
+            return SkGradientShader::CreateLinear(pts, colors, NULL, 2, mode);
+        }
+    }
+    else if (dom.hasAttr(node, "type", "bitmap"))
+    {
+        if ((str = dom.findAttr(node, "src")) == NULL)
+            return NULL;
 
-		SkBitmap	bm;
+        SkBitmap    bm;
 
-		if (SkImageDecoder::DecodeFile(str, &bm))
-		{
-			SkShader::TileMode	mode = SkShader::kRepeat_TileMode;
-			int					index;
+        if (SkImageDecoder::DecodeFile(str, &bm))
+        {
+            SkShader::TileMode    mode = SkShader::kRepeat_TileMode;
+            int                    index;
 
-			if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
-				mode = (SkShader::TileMode)index;
+            if ((index = dom.findList(node, "tile-mode", "clamp,repeat,mirror")) >= 0)
+                mode = (SkShader::TileMode)index;
 
-			return SkShader::CreateBitmapShader(bm, mode, mode);
-		}
-	}
-	return NULL;
+            return SkShader::CreateBitmapShader(bm, mode, mode);
+        }
+    }
+    return NULL;
 }
 
 void SkPaint_Inflate(SkPaint* paint, const SkDOM& dom, const SkDOM::Node* node)
 {
-	SkASSERT(paint);
-	SkASSERT(&dom);
-	SkASSERT(node);
+    SkASSERT(paint);
+    SkASSERT(&dom);
+    SkASSERT(node);
 
-	SkScalar x;
+    SkScalar x;
 
-	if (dom.findScalar(node, "stroke-width", &x))
-		paint->setStrokeWidth(x);
-	if (dom.findScalar(node, "text-size", &x))
-		paint->setTextSize(x);
-	
-	bool	b;
+    if (dom.findScalar(node, "stroke-width", &x))
+        paint->setStrokeWidth(x);
+    if (dom.findScalar(node, "text-size", &x))
+        paint->setTextSize(x);
 
-	SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b));
+    bool    b;
 
-	if (dom.findBool(node, "is-stroke", &b))
-		paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
-	if (dom.findBool(node, "is-antialias", &b))
-		paint->setAntiAlias(b);
-	if (dom.findBool(node, "is-lineartext", &b))
-		paint->setLinearText(b);
+    SkASSERT("legacy: use is-stroke" && !dom.findBool(node, "is-frame", &b));
 
-	const char* str = dom.findAttr(node, "color");
-	if (str)
-	{
-		SkColor	c = paint->getColor();
-		if (SkParse::FindColor(str, &c))
-			paint->setColor(c);
-	}
+    if (dom.findBool(node, "is-stroke", &b))
+        paint->setStyle(b ? SkPaint::kStroke_Style : SkPaint::kFill_Style);
+    if (dom.findBool(node, "is-antialias", &b))
+        paint->setAntiAlias(b);
+    if (dom.findBool(node, "is-lineartext", &b))
+        paint->setLinearText(b);
 
-	// do this AFTER parsing for the color
-	if (dom.findScalar(node, "opacity", &x))
-	{
-		x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1));
-		paint->setAlpha(SkScalarRound(x * 255));
-	}
+    const char* str = dom.findAttr(node, "color");
+    if (str)
+    {
+        SkColor    c = paint->getColor();
+        if (SkParse::FindColor(str, &c))
+            paint->setColor(c);
+    }
 
-	int	index = dom.findList(node, "text-anchor", "left,center,right");
-	if (index >= 0)
-		paint->setTextAlign((SkPaint::Align)index);
+    // do this AFTER parsing for the color
+    if (dom.findScalar(node, "opacity", &x))
+    {
+        x = SkMaxScalar(0, SkMinScalar(x, SK_Scalar1));
+        paint->setAlpha(SkScalarRound(x * 255));
+    }
 
-	SkShader* shader = inflate_shader(dom, node);
-	if (shader)
-		paint->setShader(shader)->unref();
+    int    index = dom.findList(node, "text-anchor", "left,center,right");
+    if (index >= 0)
+        paint->setTextAlign((SkPaint::Align)index);
+
+    SkShader* shader = inflate_shader(dom, node);
+    if (shader)
+        paint->setShader(shader)->unref();
 }
-
diff --git a/src/views/SkProgressBarView.cpp b/src/views/SkProgressBarView.cpp
deleted file mode 100644
index ce26ac4..0000000
--- a/src/views/SkProgressBarView.cpp
+++ /dev/null
@@ -1,109 +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 "SkProgressBarView.h"
-#include "SkAnimator.h"
-#include "SkWidgetViews.h"
-#include "SkTime.h"
-#include "SkSystemEventTypes.h"
-
-SkProgressBarView::SkProgressBarView()
-{
-	init_skin_anim(kProgress_SkinEnum, &fAnim);
-	fAnim.setHostEventSink(this);
-	fProgress = 0;
-	fMax = 100;
-	
-}
-
-void SkProgressBarView::changeProgress(int diff)
-{
-	int newProg = fProgress + diff;
-	if (newProg > 0 && newProg < fMax)
-		this->setProgress(newProg);
-	//otherwise i'll just leave it as it is
-	//this implies that if a new max and progress are set, max must be set first
-}
-
-/*virtual*/ void SkProgressBarView::onDraw(SkCanvas* canvas)
-{
-	SkPaint						paint;		
-	SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
-	
-	if (diff == SkAnimator::kDifferent)
-		this->inval(NULL);
-	else if (diff == SkAnimator::kPartiallyDifferent)
-	{
-		SkRect	bounds;
-		fAnim.getInvalBounds(&bounds);
-		this->inval(&bounds);
-	}
-}
-	
-/*virtual*/ bool SkProgressBarView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Inval))
-	{
-		this->inval(NULL);
-		return true;
-	}
-	if (evt.isType("recommendDim"))
-	{
-		SkScalar	height;
-		
-		if (evt.findScalar("y", &height))
-			this->setHeight(height);
-		return true;
-	}
-	return this->INHERITED::onEvent(evt);
-}
-
-/*virtual*/ void SkProgressBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-	int32_t temp;
-	if (dom.findS32(node, "max", &temp))
-		this->setMax(temp);
-	if (dom.findS32(node, "progress", &temp))
-		this->setProgress(temp);
-}
-
-/*virtual*/ void SkProgressBarView::onSizeChange()
-{
-	this->INHERITED::onSizeChange();
-	SkEvent evt("user");
-	evt.setString("id", "setDim");
-	evt.setScalar("dimX", this->width());
-	evt.setScalar("dimY", this->height());
-	fAnim.doUserEvent(evt);
-}
-
-void SkProgressBarView::reset()
-{
-	fProgress = 0;
-	SkEvent e("user");
-	e.setString("id", "reset");
-	fAnim.doUserEvent(e);
-}
-
-void SkProgressBarView::setMax(int max)
-{
-	fMax = max;
-	SkEvent e("user");
-	e.setString("id", "setMax");
-	e.setS32("newMax", max);
-	fAnim.doUserEvent(e);
-}
-
-void SkProgressBarView::setProgress(int progress)
-{
-	fProgress = progress;
-	SkEvent e("user");
-	e.setString("id", "setProgress");
-	e.setS32("newProgress", progress);
-	fAnim.doUserEvent(e);
-}
diff --git a/src/views/SkProgressView.cpp b/src/views/SkProgressView.cpp
index d82b48e..03e28eb 100644
--- a/src/views/SkProgressView.cpp
+++ b/src/views/SkProgressView.cpp
@@ -14,120 +14,119 @@
 
 SkProgressView::SkProgressView(uint32_t flags) : SkView(flags), fOnShader(NULL), fOffShader(NULL)
 {
-	fValue = 0;
-	fMax = 0;
-	fInterp = NULL;
-	fDoInterp = false;
+    fValue = 0;
+    fMax = 0;
+    fInterp = NULL;
+    fDoInterp = false;
 }
 
 SkProgressView::~SkProgressView()
 {
-	delete fInterp;
-	SkSafeUnref(fOnShader);
-	SkSafeUnref(fOffShader);
+    delete fInterp;
+    SkSafeUnref(fOnShader);
+    SkSafeUnref(fOffShader);
 }
 
 void SkProgressView::setMax(U16CPU max)
 {
-	if (fMax != max)
-	{
-		fMax = SkToU16(max);
-		if (fValue > 0)
-			this->inval(NULL);
-	}
+    if (fMax != max)
+    {
+        fMax = SkToU16(max);
+        if (fValue > 0)
+            this->inval(NULL);
+    }
 }
 
 void SkProgressView::setValue(U16CPU value)
 {
-	if (fValue != value)
-	{
-		if (fDoInterp)
-		{
-			if (fInterp)
-				delete fInterp;
-			fInterp = new SkInterpolator(1, 2);
-			SkScalar x = (SkScalar)(fValue << 8);
-			fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0);
-			x = (SkScalar)(value << 8);
-			fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x);
-		}
-		fValue = SkToU16(value);
-		this->inval(NULL);
-	}
+    if (fValue != value)
+    {
+        if (fDoInterp)
+        {
+            if (fInterp)
+                delete fInterp;
+            fInterp = new SkInterpolator(1, 2);
+            SkScalar x = (SkScalar)(fValue << 8);
+            fInterp->setKeyFrame(0, SkTime::GetMSecs(), &x, 0);
+            x = (SkScalar)(value << 8);
+            fInterp->setKeyFrame(1, SkTime::GetMSecs() + 333, &x);
+        }
+        fValue = SkToU16(value);
+        this->inval(NULL);
+    }
 }
 
 void SkProgressView::onDraw(SkCanvas* canvas)
 {
-	if (fMax == 0)
-		return;
+    if (fMax == 0)
+        return;
 
-	SkFixed	percent;
+    SkFixed    percent;
 
-	if (fInterp)
-	{
-		SkScalar x;
-		if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result)
-		{
-			delete fInterp;
-			fInterp = NULL;
-		}
-		percent = (SkFixed)x;	// now its 16.8
-		percent = SkMax32(0, SkMin32(percent, fMax << 8));	// now its pinned
-		percent = SkFixedDiv(percent, fMax << 8);	// now its 0.16
-		this->inval(NULL);
-	}
-	else
-	{
-		U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
-		percent = SkFixedDiv(value, fMax);
-	}
+    if (fInterp)
+    {
+        SkScalar x;
+        if (fInterp->timeToValues(SkTime::GetMSecs(), &x) == SkInterpolator::kFreezeEnd_Result)
+        {
+            delete fInterp;
+            fInterp = NULL;
+        }
+        percent = (SkFixed)x;    // now its 16.8
+        percent = SkMax32(0, SkMin32(percent, fMax << 8));    // now its pinned
+        percent = SkFixedDiv(percent, fMax << 8);    // now its 0.16
+        this->inval(NULL);
+    }
+    else
+    {
+        U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
+        percent = SkFixedDiv(value, fMax);
+    }
 
 
-	SkRect	r;
-	SkPaint	p;
+    SkRect    r;
+    SkPaint    p;
 
-	r.set(0, 0, this->width(), this->height());
-	p.setAntiAlias(true);
-	
-	r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
-	p.setStyle(SkPaint::kFill_Style);
+    r.set(0, 0, this->width(), this->height());
+    p.setAntiAlias(true);
 
-	p.setColor(SK_ColorDKGRAY);
-	p.setShader(fOnShader);
-	canvas->drawRect(r, p);
+    r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
+    p.setStyle(SkPaint::kFill_Style);
 
-	p.setColor(SK_ColorWHITE);
-	p.setShader(fOffShader);
-	r.fLeft = r.fRight;
-	r.fRight = this->width() - SK_Scalar1;
-	if (r.width() > 0)
-		canvas->drawRect(r, p);
+    p.setColor(SK_ColorDKGRAY);
+    p.setShader(fOnShader);
+    canvas->drawRect(r, p);
+
+    p.setColor(SK_ColorWHITE);
+    p.setShader(fOffShader);
+    r.fLeft = r.fRight;
+    r.fRight = this->width() - SK_Scalar1;
+    if (r.width() > 0)
+        canvas->drawRect(r, p);
 }
 
 #include "SkImageDecoder.h"
 
 static SkShader* inflate_shader(const char file[])
 {
-	SkBitmap	bm;
+    SkBitmap    bm;
 
-	return SkImageDecoder::DecodeFile(file, &bm) ?
-			SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) :
-			NULL;
+    return SkImageDecoder::DecodeFile(file, &bm) ?
+            SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode) :
+            NULL;
 }
 
 void SkProgressView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->INHERITED::onInflate(dom, node);
+    this->INHERITED::onInflate(dom, node);
 
-	const char* s;
+    const char* s;
 
-	SkASSERT(fOnShader == NULL);
-	SkASSERT(fOffShader == NULL);
+    SkASSERT(fOnShader == NULL);
+    SkASSERT(fOffShader == NULL);
 
-	if ((s = dom.findAttr(node, "src-on")) != NULL)
-		fOnShader = inflate_shader(s);
-	if ((s = dom.findAttr(node, "src-off")) != NULL)
-		fOffShader = inflate_shader(s);
-	(void)dom.findBool(node, "do-interp", &fDoInterp);
+    if ((s = dom.findAttr(node, "src-on")) != NULL)
+        fOnShader = inflate_shader(s);
+    if ((s = dom.findAttr(node, "src-off")) != NULL)
+        fOffShader = inflate_shader(s);
+    (void)dom.findBool(node, "do-interp", &fDoInterp);
 }
-
diff --git a/src/views/SkScrollBarView.cpp b/src/views/SkScrollBarView.cpp
deleted file mode 100644
index 98288f5..0000000
--- a/src/views/SkScrollBarView.cpp
+++ /dev/null
@@ -1,146 +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 "SkScrollBarView.h"
-#include "SkAnimator.h"
-#include "SkWidgetViews.h"
-#include "SkSystemEventTypes.h"
-#include "SkTime.h"
-
-//see SkProgressBarView.cpp
-//#include "SkWidgetViews.cpp"
-
-SkScrollBarView::SkScrollBarView()
-{
-	fAnim.setHostEventSink(this);
-	init_skin_anim(kScroll_SkinEnum, &fAnim);
-
-	fTotalLength = 0;
-	fStartPoint = 0;
-	fShownLength = 0;
-
-	this->adjust();
-}
-
-void SkScrollBarView::setStart(unsigned start)
-{
-	if ((int)start < 0)
-		start = 0;
-	
-	if (fStartPoint != start)
-	{
-		fStartPoint = start;
-		this->adjust();
-	}
-}
-
-void SkScrollBarView::setShown(unsigned shown)
-{
-	if ((int)shown < 0)
-		shown = 0;
-
-	if (fShownLength != shown)
-	{
-		fShownLength = shown;
-		this->adjust();
-	}
-}
-
-void SkScrollBarView::setTotal(unsigned total)
-{
-	if ((int)total < 0)
-		total = 0;
-
-	if (fTotalLength != total)
-	{
-		fTotalLength = total;
-		this->adjust();
-	}
-}
-
-/* virtual */ void SkScrollBarView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-	
-	int32_t value;
-	if (dom.findS32(node, "total", &value))
-		this->setTotal(value);
-	if (dom.findS32(node, "shown", &value))
-		this->setShown(value);
-}
-
-/*virtual*/ void SkScrollBarView::onSizeChange()
-{
-	this->INHERITED::onSizeChange();
-	SkEvent evt("user");
-	evt.setString("id", "setDim");
-	evt.setScalar("dimX", this->width());
-	evt.setScalar("dimY", this->height());
-	fAnim.doUserEvent(evt);
-}
-
-/*virtual*/ void SkScrollBarView::onDraw(SkCanvas* canvas)
-{
-	SkPaint						paint;		
-	SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
-	
-	if (diff == SkAnimator::kDifferent)
-		this->inval(NULL);
-	else if (diff == SkAnimator::kPartiallyDifferent)
-	{
-		SkRect	bounds;
-		fAnim.getInvalBounds(&bounds);
-		this->inval(&bounds);
-	}
-}
-
-/*virtual*/ bool SkScrollBarView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Inval))
-	{
-		this->inval(NULL);
-		return true;
-	}
-	if (evt.isType("recommendDim"))
-	{
-		SkScalar	width;
-		
-		if (evt.findScalar("x", &width))
-			this->setWidth(width);
-		return true;
-	}
-
-	return this->INHERITED::onEvent(evt);
-}
-
-void SkScrollBarView::adjust()
-{
-	int total = fTotalLength;
-	int start = fStartPoint;
-	int shown = fShownLength;
-	int hideBar = 0;
-	
-	if (total <= 0 || shown <= 0 || shown >= total)	// no bar to show
-	{
-		total = 1;		// avoid divide-by-zero. should be done by skin/script
-		hideBar = 1;	// signal we don't want a thumb
-	}
-	else
-	{
-		if (start + shown > total)
-			start = total - shown;
-	}
-	
-	SkEvent e("user");
-	e.setString("id", "adjustScrollBar");
-	e.setScalar("_totalLength", SkIntToScalar(total));
-	e.setScalar("_startPoint", SkIntToScalar(start));
-	e.setScalar("_shownLength", SkIntToScalar(shown));
-//	e.setS32("hideBar", hideBar);
-	fAnim.doUserEvent(e);
-}
-
diff --git a/src/views/SkStackViewLayout.cpp b/src/views/SkStackViewLayout.cpp
index bf6f363..9a3a352 100644
--- a/src/views/SkStackViewLayout.cpp
+++ b/src/views/SkStackViewLayout.cpp
@@ -9,51 +9,51 @@
 
 SkStackViewLayout::SkStackViewLayout()
 {
-	fMargin.set(0, 0, 0, 0);
-	fSpacer	= 0;
-	fOrient	= kHorizontal_Orient;
-	fPack	= kStart_Pack;
-	fAlign	= kStart_Align;
-	fRound	= false;
+    fMargin.set(0, 0, 0, 0);
+    fSpacer    = 0;
+    fOrient    = kHorizontal_Orient;
+    fPack    = kStart_Pack;
+    fAlign    = kStart_Align;
+    fRound    = false;
 }
 
 void SkStackViewLayout::setOrient(Orient ori)
 {
-	SkASSERT((unsigned)ori < kOrientCount);
-	fOrient = SkToU8(ori);
+    SkASSERT((unsigned)ori < kOrientCount);
+    fOrient = SkToU8(ori);
 }
 
 void SkStackViewLayout::getMargin(SkRect* margin) const
 {
-	if (margin)
-		*margin = fMargin;
+    if (margin)
+        *margin = fMargin;
 }
 
 void SkStackViewLayout::setMargin(const SkRect& margin)
 {
-	fMargin = margin;
+    fMargin = margin;
 }
 
 void SkStackViewLayout::setSpacer(SkScalar spacer)
 {
-	fSpacer = spacer;
+    fSpacer = spacer;
 }
 
 void SkStackViewLayout::setPack(Pack pack)
 {
-	SkASSERT((unsigned)pack < kPackCount);
-	fPack = SkToU8(pack);
+    SkASSERT((unsigned)pack < kPackCount);
+    fPack = SkToU8(pack);
 }
 
 void SkStackViewLayout::setAlign(Align align)
 {
-	SkASSERT((unsigned)align < kAlignCount);
-	fAlign = SkToU8(align);
+    SkASSERT((unsigned)align < kAlignCount);
+    fAlign = SkToU8(align);
 }
 
 void SkStackViewLayout::setRound(bool r)
 {
-	fRound = SkToU8(r);
+    fRound = SkToU8(r);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -68,170 +68,170 @@
 static SkScalar right_align_proc(SkScalar childLimit, SkScalar parentLimit) { return parentLimit - childLimit; }
 static SkScalar fill_align_proc(SkScalar childLimit, SkScalar parentLimit) { return 0; }
 
-/*	Measure the main-dimension for all the children. If a child is marked flex in that direction
-	ignore its current value but increment the counter for flexChildren
+/*    Measure the main-dimension for all the children. If a child is marked flex in that direction
+    ignore its current value but increment the counter for flexChildren
 */
 static SkScalar compute_children_limit(SkView* parent, GetSizeProc sizeProc, int* count,
-									   uint32_t flexMask, int* flexCount)
+                                       uint32_t flexMask, int* flexCount)
 {
-	SkView::B2FIter	iter(parent);
-	SkView*			child;
-	SkScalar		limit = 0;
-	int				n = 0, flex = 0;
+    SkView::B2FIter    iter(parent);
+    SkView*            child;
+    SkScalar        limit = 0;
+    int                n = 0, flex = 0;
 
-	while ((child = iter.next()) != NULL)
-	{
-		n += 1;
-		if (child->getFlags() & flexMask)
-			flex += 1;
-		else
-			limit += (child->*sizeProc)();
-	}
-	if (count)
-		*count = n;
-	if (flexCount)
-		*flexCount = flex;
-	return limit;
+    while ((child = iter.next()) != NULL)
+    {
+        n += 1;
+        if (child->getFlags() & flexMask)
+            flex += 1;
+        else
+            limit += (child->*sizeProc)();
+    }
+    if (count)
+        *count = n;
+    if (flexCount)
+        *flexCount = flex;
+    return limit;
 }
 
 void SkStackViewLayout::onLayoutChildren(SkView* parent)
 {
-	static AlignProc gAlignProcs[] = {
-		left_align_proc,
-		center_align_proc,
-		right_align_proc,
-		fill_align_proc
-	};
+    static AlignProc gAlignProcs[] = {
+        left_align_proc,
+        center_align_proc,
+        right_align_proc,
+        fill_align_proc
+    };
 
-	SkScalar			startM, endM, crossStartM, crossLimit;
-	GetSizeProc			mainGetSizeP, crossGetSizeP;
-	SetLocProc			mainLocP, crossLocP;
-	SetSizeProc			mainSetSizeP, crossSetSizeP;
-	SkView::Flag_Mask	flexMask;
+    SkScalar            startM, endM, crossStartM, crossLimit;
+    GetSizeProc            mainGetSizeP, crossGetSizeP;
+    SetLocProc            mainLocP, crossLocP;
+    SetSizeProc            mainSetSizeP, crossSetSizeP;
+    SkView::Flag_Mask    flexMask;
 
-	if (fOrient == kHorizontal_Orient)
-	{
-		startM		= fMargin.fLeft;
-		endM		= fMargin.fRight;
-		crossStartM	= fMargin.fTop;
-		crossLimit	= -fMargin.fTop - fMargin.fBottom;
+    if (fOrient == kHorizontal_Orient)
+    {
+        startM        = fMargin.fLeft;
+        endM        = fMargin.fRight;
+        crossStartM    = fMargin.fTop;
+        crossLimit    = -fMargin.fTop - fMargin.fBottom;
 
-		mainGetSizeP	= &SkView::width;
-		crossGetSizeP	= &SkView::height;
-		mainLocP	= &SkView::setLocX;
-		crossLocP	= &SkView::setLocY;
+        mainGetSizeP    = &SkView::width;
+        crossGetSizeP    = &SkView::height;
+        mainLocP    = &SkView::setLocX;
+        crossLocP    = &SkView::setLocY;
 
-		mainSetSizeP  = &SkView::setWidth;
-		crossSetSizeP = &SkView::setHeight;
+        mainSetSizeP  = &SkView::setWidth;
+        crossSetSizeP = &SkView::setHeight;
 
-		flexMask	= SkView::kFlexH_Mask;
-	}
-	else
-	{
-		startM		= fMargin.fTop;
-		endM		= fMargin.fBottom;
-		crossStartM	= fMargin.fLeft;
-		crossLimit	= -fMargin.fLeft - fMargin.fRight;
+        flexMask    = SkView::kFlexH_Mask;
+    }
+    else
+    {
+        startM        = fMargin.fTop;
+        endM        = fMargin.fBottom;
+        crossStartM    = fMargin.fLeft;
+        crossLimit    = -fMargin.fLeft - fMargin.fRight;
 
-		mainGetSizeP	= &SkView::height;
-		crossGetSizeP	= &SkView::width;
-		mainLocP	= &SkView::setLocY;
-		crossLocP	= &SkView::setLocX;
+        mainGetSizeP    = &SkView::height;
+        crossGetSizeP    = &SkView::width;
+        mainLocP    = &SkView::setLocY;
+        crossLocP    = &SkView::setLocX;
 
-		mainSetSizeP  = &SkView::setHeight;
-		crossSetSizeP = &SkView::setWidth;
+        mainSetSizeP  = &SkView::setHeight;
+        crossSetSizeP = &SkView::setWidth;
 
-		flexMask	= SkView::kFlexV_Mask;
-	}
-	crossLimit += (parent->*crossGetSizeP)();
-	if (fAlign != kStretch_Align)
-		crossSetSizeP = NULL;
+        flexMask    = SkView::kFlexV_Mask;
+    }
+    crossLimit += (parent->*crossGetSizeP)();
+    if (fAlign != kStretch_Align)
+        crossSetSizeP = NULL;
 
-	int			childCount, flexCount;
-	SkScalar	childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
+    int            childCount, flexCount;
+    SkScalar    childLimit = compute_children_limit(parent, mainGetSizeP, &childCount, flexMask, &flexCount);
 
-	if (childCount == 0)
-		return;
+    if (childCount == 0)
+        return;
 
-	childLimit += (childCount - 1) * fSpacer;
+    childLimit += (childCount - 1) * fSpacer;
 
-	SkScalar		parentLimit = (parent->*mainGetSizeP)() - startM - endM;
-	SkScalar		pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
-	SkScalar		flexAmount = 0;
-	SkView::B2FIter	iter(parent);
-	SkView*			child;
+    SkScalar        parentLimit = (parent->*mainGetSizeP)() - startM - endM;
+    SkScalar        pos = startM + gAlignProcs[fPack](childLimit, parentLimit);
+    SkScalar        flexAmount = 0;
+    SkView::B2FIter    iter(parent);
+    SkView*            child;
 
-	if (flexCount > 0 && parentLimit > childLimit)
-		flexAmount = (parentLimit - childLimit) / flexCount;
+    if (flexCount > 0 && parentLimit > childLimit)
+        flexAmount = (parentLimit - childLimit) / flexCount;
 
-	while ((child = iter.next()) != NULL)
-	{
-		if (fRound)
-			pos = SkIntToScalar(SkScalarRound(pos));
-		(child->*mainLocP)(pos);
-		SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
-		if (fRound)
-			crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
-		(child->*crossLocP)(crossLoc);
+    while ((child = iter.next()) != NULL)
+    {
+        if (fRound)
+            pos = SkIntToScalar(SkScalarRound(pos));
+        (child->*mainLocP)(pos);
+        SkScalar crossLoc = crossStartM + gAlignProcs[fAlign]((child->*crossGetSizeP)(), crossLimit);
+        if (fRound)
+            crossLoc = SkIntToScalar(SkScalarRound(crossLoc));
+        (child->*crossLocP)(crossLoc);
 
-		if (crossSetSizeP)
-			(child->*crossSetSizeP)(crossLimit);
-		if (child->getFlags() & flexMask)
-			(child->*mainSetSizeP)(flexAmount);
-		pos += (child->*mainGetSizeP)() + fSpacer;
-	}
+        if (crossSetSizeP)
+            (child->*crossSetSizeP)(crossLimit);
+        if (child->getFlags() & flexMask)
+            (child->*mainSetSizeP)(flexAmount);
+        pos += (child->*mainGetSizeP)() + fSpacer;
+    }
 }
 
 //////////////////////////////////////////////////////////////////////////////////////
 
 #ifdef SK_DEBUG
-	static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
-	{
-		const char* value = dom.findAttr(node, attr);
-		if (value)
-			SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
-	}
+    static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+    {
+        const char* value = dom.findAttr(node, attr);
+        if (value)
+            SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+    }
 #else
-	#define assert_no_attr(dom, node, attr)
+    #define assert_no_attr(dom, node, attr)
 #endif
 
 void SkStackViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	int			index;
-	SkScalar	value[4];
+    int            index;
+    SkScalar    value[4];
 
-	if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
-		this->setOrient((Orient)index);
-	else {
-		assert_no_attr(dom, node, "orient");
+    if ((index = dom.findList(node, "orient", "horizontal,vertical")) >= 0)
+        this->setOrient((Orient)index);
+    else {
+        assert_no_attr(dom, node, "orient");
         }
 
-	if (dom.findScalars(node, "margin", value, 4))
-	{
-		SkRect	margin;
-		margin.set(value[0], value[1], value[2], value[3]);
-		this->setMargin(margin);
-	}
-	else {
-		assert_no_attr(dom, node, "margin");
+    if (dom.findScalars(node, "margin", value, 4))
+    {
+        SkRect    margin;
+        margin.set(value[0], value[1], value[2], value[3]);
+        this->setMargin(margin);
+    }
+    else {
+        assert_no_attr(dom, node, "margin");
         }
 
-	if (dom.findScalar(node, "spacer", value))
-		this->setSpacer(value[0]);
-	else {
-		assert_no_attr(dom, node, "spacer");
+    if (dom.findScalar(node, "spacer", value))
+        this->setSpacer(value[0]);
+    else {
+        assert_no_attr(dom, node, "spacer");
         }
 
-	if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
-		this->setPack((Pack)index);
-	else {
-		assert_no_attr(dom, node, "pack");
+    if ((index = dom.findList(node, "pack", "start,center,end")) >= 0)
+        this->setPack((Pack)index);
+    else {
+        assert_no_attr(dom, node, "pack");
         }
 
-	if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
-		this->setAlign((Align)index);
-	else {
-		assert_no_attr(dom, node, "align");
+    if ((index = dom.findList(node, "align", "start,center,end,stretch")) >= 0)
+        this->setAlign((Align)index);
+    else {
+        assert_no_attr(dom, node, "align");
         }
 }
 
@@ -239,36 +239,35 @@
 
 SkFillViewLayout::SkFillViewLayout()
 {
-	fMargin.setEmpty();
+    fMargin.setEmpty();
 }
 
 void SkFillViewLayout::getMargin(SkRect* r) const
 {
-	if (r)
-		*r = fMargin;
+    if (r)
+        *r = fMargin;
 }
 
 void SkFillViewLayout::setMargin(const SkRect& margin)
 {
-	fMargin = margin;
+    fMargin = margin;
 }
 
 void SkFillViewLayout::onLayoutChildren(SkView* parent)
 {
-	SkView::B2FIter	iter(parent);
-	SkView*			child;
+    SkView::B2FIter    iter(parent);
+    SkView*            child;
 
-	while ((child = iter.next()) != NULL)
-	{
-		child->setLoc(fMargin.fLeft, fMargin.fTop);
-		child->setSize(	parent->width() - fMargin.fRight - fMargin.fLeft,
-						parent->height() - fMargin.fBottom - fMargin.fTop);
-	}
+    while ((child = iter.next()) != NULL)
+    {
+        child->setLoc(fMargin.fLeft, fMargin.fTop);
+        child->setSize(    parent->width() - fMargin.fRight - fMargin.fLeft,
+                        parent->height() - fMargin.fBottom - fMargin.fTop);
+    }
 }
 
 void SkFillViewLayout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->INHERITED::onInflate(dom, node);
-	(void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
+    this->INHERITED::onInflate(dom, node);
+    (void)dom.findScalars(node, "margin", (SkScalar*)&fMargin, 4);
 }
-
diff --git a/src/views/SkStaticTextView.cpp b/src/views/SkStaticTextView.cpp
deleted file mode 100644
index 2fd04a7..0000000
--- a/src/views/SkStaticTextView.cpp
+++ /dev/null
@@ -1,184 +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 "SkWidgetViews.h"
-#include "SkTextBox.h"
-
-#ifdef SK_DEBUG
-static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
-{
-    const char* value = dom.findAttr(node, attr);
-    if (value)
-        SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
-}
-#else
-    #define assert_no_attr(dom, node, attr)
-#endif
-
-SkStaticTextView::SkStaticTextView()
-{
-	fMargin.set(0, 0);
-	fMode = kFixedSize_Mode;
-	fSpacingAlign = SkTextBox::kStart_SpacingAlign;
-	
-//	init_skin_paint(kStaticText_SkinEnum, &fPaint);
-}
-
-SkStaticTextView::~SkStaticTextView()
-{
-}
-
-void SkStaticTextView::computeSize()
-{
-	if (fMode == kAutoWidth_Mode)
-	{
-		SkScalar width = fPaint.measureText(fText.c_str(), fText.size());
-		this->setWidth(width + fMargin.fX * 2);
-	}
-	else if (fMode == kAutoHeight_Mode)
-	{
-		SkScalar width = this->width() - fMargin.fX * 2;
-		int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
-
-		this->setHeight(lines * fPaint.getFontSpacing() + fMargin.fY * 2);
-	}
-}
-
-void SkStaticTextView::setMode(Mode mode)
-{
-	SkASSERT((unsigned)mode < kModeCount);
-
-	if (fMode != mode)
-	{
-		fMode = SkToU8(mode);
-		this->computeSize();
-	}
-}
-
-void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
-{
-	fSpacingAlign = SkToU8(align);
-	this->inval(NULL);
-}
-
-void SkStaticTextView::getMargin(SkPoint* margin) const
-{
-	if (margin)
-		*margin = fMargin;
-}
-
-void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
-{
-	if (fMargin.fX != dx || fMargin.fY != dy)
-	{
-		fMargin.set(dx, dy);
-		this->computeSize();
-		this->inval(NULL);
-	}
-}
-
-size_t SkStaticTextView::getText(SkString* text) const
-{
-	if (text)
-		*text = fText;
-	return fText.size();
-}
-
-size_t SkStaticTextView::getText(char text[]) const
-{
-	if (text)
-		memcpy(text, fText.c_str(), fText.size());
-	return fText.size();
-}
-
-void SkStaticTextView::setText(const SkString& text)
-{
-	this->setText(text.c_str(), text.size());
-}
-
-void SkStaticTextView::setText(const char text[])
-{
-	if (text == NULL)
-		text = "";
-	this->setText(text, strlen(text));
-}
-
-void SkStaticTextView::setText(const char text[], size_t len)
-{
-	if (!fText.equals(text, len))
-	{
-		fText.set(text, len);
-		this->computeSize();
-		this->inval(NULL);
-	}
-}
-
-void SkStaticTextView::getPaint(SkPaint* paint) const
-{
-	if (paint)
-		*paint = fPaint;
-}
-
-void SkStaticTextView::setPaint(const SkPaint& paint)
-{
-	if (fPaint != paint)
-	{
-		fPaint = paint;
-		this->computeSize();
-		this->inval(NULL);
-	}
-}
-
-void SkStaticTextView::onDraw(SkCanvas* canvas)
-{
-	this->INHERITED::onDraw(canvas);
-
-	if (fText.isEmpty())
-		return;
-
-	SkTextBox	box;
-
-	box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
-	box.setSpacingAlign(this->getSpacingAlign());
-	box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
-	box.draw(canvas, fText.c_str(), fText.size(), fPaint);
-}
-
-void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-#if 0
-	this->INHERITED::onInflate(dom, node);
-
-	int	index;
-	if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
-		this->setMode((Mode)index);
-	else
-		assert_no_attr(dom, node, "mode");
-
-	if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
-		this->setSpacingAlign((SkTextBox::SpacingAlign)index);
-	else
-		assert_no_attr(dom, node, "spacing-align");
-
-	SkScalar s[2];
-	if (dom.findScalars(node, "margin", s, 2))
-		this->setMargin(s[0], s[1]);
-	else
-		assert_no_attr(dom, node, "margin");
-
-	const char* text = dom.findAttr(node, "text");
-	if (text)
-		this->setText(text);
-
-	if ((node = dom.getFirstChild(node, "paint")) != NULL &&
-		(node = dom.getFirstChild(node, "screenplay")) != NULL)
-	{
-		inflate_paint(dom, node, &fPaint);
-	}
-#endif
-}
-
diff --git a/src/views/SkTagList.cpp b/src/views/SkTagList.cpp
index e1b1662..8ede870 100644
--- a/src/views/SkTagList.cpp
+++ b/src/views/SkTagList.cpp
@@ -60,4 +60,3 @@
         rec = next;
     }
 }
-
diff --git a/src/views/SkTextBox.cpp b/src/views/SkTextBox.cpp
index 6a88c6c..b5dab09 100644
--- a/src/views/SkTextBox.cpp
+++ b/src/views/SkTextBox.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright 2006 The Android Open Source Project
  *
@@ -6,62 +5,93 @@
  * found in the LICENSE file.
  */
 
-
 #include "SkTextBox.h"
-#include "../core/SkGlyphCache.h"
 #include "SkUtils.h"
-#include "SkAutoKern.h"
 
 static inline int is_ws(int c)
 {
     return !((c - 1) >> 5);
 }
 
-static size_t linebreak(const char text[], const char stop[], const SkPaint& paint, SkScalar margin)
+static size_t linebreak(const char text[], const char stop[],
+                        const SkPaint& paint, SkScalar margin,
+                        size_t* trailing = NULL)
 {
+    size_t lengthBreak = paint.breakText(text, stop - text, margin);
+
+    //Check for white space or line breakers before the lengthBreak
     const char* start = text;
-
-    SkAutoGlyphCache    ac(paint, NULL);
-    SkGlyphCache*       cache = ac.getCache();
-    SkFixed             w = 0;
-    SkFixed             limit = SkScalarToFixed(margin);
-    SkAutoKern          autokern;
-
     const char* word_start = text;
-    int         prevWS = true;
+    int prevWS = true;
+    if (trailing) {
+        *trailing = 0;
+    }
 
-    while (text < stop)
-    {
+    while (text < stop) {
         const char* prevText = text;
-        SkUnichar   uni = SkUTF8_NextUnichar(&text);
-        int         currWS = is_ws(uni);
-        const SkGlyph&  glyph = cache->getUnicharMetrics(uni);
+        SkUnichar uni = SkUTF8_NextUnichar(&text);
+        int currWS = is_ws(uni);
 
-        if (!currWS && prevWS)
+        if (!currWS && prevWS) {
             word_start = prevText;
+        }
         prevWS = currWS;
 
-        w += autokern.adjust(glyph) + glyph.fAdvanceX;
-        if (w > limit)
-        {
-            if (currWS) // eat the rest of the whitespace
-            {
-                while (text < stop && is_ws(SkUTF8_ToUnichar(text)))
+        if (text > start + lengthBreak) {
+            if (currWS) {
+                // eat the rest of the whitespace
+                while (text < stop && is_ws(SkUTF8_ToUnichar(text))) {
                     text += SkUTF8_CountUTF8Bytes(text);
-            }
-            else    // backup until a whitespace (or 1 char)
-            {
-                if (word_start == start)
-                {
-                    if (prevText > start)
-                        text = prevText;
                 }
-                else
+                if (trailing) {
+                    *trailing = text - prevText;
+                }
+            } else {
+                // backup until a whitespace (or 1 char)
+                if (word_start == start) {
+                    if (prevText > start) {
+                        text = prevText;
+                    }
+                } else {
                     text = word_start;
+                }
             }
             break;
         }
+
+        if ('\n' == uni) {
+            size_t ret = text - start;
+            size_t lineBreakSize = 1;
+            if (text < stop) {
+                uni = SkUTF8_NextUnichar(&text);
+                if ('\r' == uni) {
+                    ret = text - start;
+                    ++lineBreakSize;
+                }
+            }
+            if (trailing) {
+                *trailing = lineBreakSize;
+            }
+            return ret;
+        }
+
+        if ('\r' == uni) {
+            size_t ret = text - start;
+            size_t lineBreakSize = 1;
+            if (text < stop) {
+                uni = SkUTF8_NextUnichar(&text);
+                if ('\n' == uni) {
+                    ret = text - start;
+                    ++lineBreakSize;
+                }
+            }
+            if (trailing) {
+                *trailing = lineBreakSize;
+            }
+            return ret;
+        }
     }
+
     return text - start;
 }
 
@@ -194,16 +224,17 @@
 
     for (;;)
     {
-        len = linebreak(text, textStop, paint, marginWidth);
+        size_t trailing;
+        len = linebreak(text, textStop, paint, marginWidth, &trailing);
         if (y + metrics.fDescent + metrics.fLeading > 0)
-            canvas->drawText(text, len, x, y, paint);
+            canvas->drawText(text, len - trailing, x, y, paint);
         text += len;
         if (text >= textStop)
             break;
         y += scaledSpacing;
         if (y + metrics.fAscent >= height)
             break;
-    } 
+    }
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -226,4 +257,3 @@
     SkScalar spacing = SkScalarMul(fPaint->getTextSize(), fSpacingMul) + fSpacingAdd;
     return this->countLines() * spacing;
 }
-
diff --git a/src/views/SkTouchGesture.cpp b/src/views/SkTouchGesture.cpp
index 31adc74..3becccd 100644
--- a/src/views/SkTouchGesture.cpp
+++ b/src/views/SkTouchGesture.cpp
@@ -14,9 +14,9 @@
 
 #define DISCRETIZE_TRANSLATE_TO_AVOID_FLICKER   true
 
-static const float MAX_FLING_SPEED = 1500;
+static const SkScalar MAX_FLING_SPEED = SkIntToScalar(1500);
 
-static float pin_max_fling(float speed) {
+static SkScalar pin_max_fling(SkScalar speed) {
     if (speed > MAX_FLING_SPEED) {
         speed = MAX_FLING_SPEED;
     }
@@ -50,7 +50,7 @@
 
 void SkFlingState::reset(float sx, float sy) {
     fActive = true;
-    fDirection.set(sx, sy);
+    fDirection.set(SkFloatToScalar(sx), SkFloatToScalar(sy));
     fSpeed0 = SkPoint::Normalize(&fDirection);
     fSpeed0 = pin_max_fling(fSpeed0);
     fTime0 = getseconds();
@@ -82,7 +82,7 @@
         tx = (float)sk_float_round2int(tx);
         ty = (float)sk_float_round2int(ty);
     }
-    matrix->setTranslate(tx, ty);
+    matrix->setTranslate(SkFloatToScalar(tx), SkFloatToScalar(ty));
 //    printf("---- evaluate (%g %g)\n", tx, ty);
 
     return true;
@@ -181,8 +181,8 @@
     return -1;
 }
 
-static float center(float pos0, float pos1) {
-    return (pos0 + pos1) * 0.5f;
+static SkScalar center(float pos0, float pos1) {
+    return SkFloatToScalar((pos0 + pos1) * 0.5f);
 }
 
 static const float MAX_ZOOM_SCALE = 4;
@@ -190,8 +190,8 @@
 
 float SkTouchGesture::limitTotalZoom(float scale) const {
     // this query works 'cause we know that we're square-scale w/ no skew/rotation
-    const float curr = fGlobalM[0];
-    
+    const float curr = SkScalarToFloat(fGlobalM[0]);
+
     if (scale > 1 && curr * scale > MAX_ZOOM_SCALE) {
         scale = MAX_ZOOM_SCALE / curr;
     } else if (scale < 1 && curr * scale < MIN_ZOOM_SCALE) {
@@ -239,7 +239,7 @@
             SkASSERT(kZoom_State == fState);
             const Rec& rec0 = fTouches[0];
             const Rec& rec1 = fTouches[1];
-            
+
             float scale = this->computePinch(rec0, rec1);
             scale = this->limitTotalZoom(scale);
 
@@ -325,5 +325,3 @@
     fLastUpP.set(x, y);
     return found;
 }
-
-
diff --git a/src/views/SkView.cpp b/src/views/SkView.cpp
index fc1ddfb..0c6c0de 100644
--- a/src/views/SkView.cpp
+++ b/src/views/SkView.cpp
@@ -8,52 +8,55 @@
 #include "SkView.h"
 #include "SkCanvas.h"
 
+SK_DEFINE_INST_COUNT(SkView::Artist)
+SK_DEFINE_INST_COUNT(SkView::Layout)
+
 ////////////////////////////////////////////////////////////////////////
 
 SkView::SkView(uint32_t flags) : fFlags(SkToU8(flags))
 {
-	fWidth = fHeight = 0;
-	fLoc.set(0, 0);
-	fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
+    fWidth = fHeight = 0;
+    fLoc.set(0, 0);
+    fParent = fFirstChild = fNextSibling = fPrevSibling = NULL;
     fMatrix.setIdentity();
-	fContainsFocus = 0;
+    fContainsFocus = 0;
 }
 
 SkView::~SkView()
 {
-	this->detachAllChildren();
+    this->detachAllChildren();
 }
 
 void SkView::setFlags(uint32_t flags)
 {
-	SkASSERT((flags & ~kAllFlagMasks) == 0);
+    SkASSERT((flags & ~kAllFlagMasks) == 0);
 
-	uint32_t diff = fFlags ^ flags;
+    uint32_t diff = fFlags ^ flags;
 
-	if (diff & kVisible_Mask)
-		this->inval(NULL);
+    if (diff & kVisible_Mask)
+        this->inval(NULL);
 
-	fFlags = SkToU8(flags);
+    fFlags = SkToU8(flags);
 
-	if (diff & kVisible_Mask)
-	{
-		this->inval(NULL);
-	}
+    if (diff & kVisible_Mask)
+    {
+        this->inval(NULL);
+    }
 }
 
 void SkView::setVisibleP(bool pred)
 {
-	this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
+    this->setFlags(SkSetClearShift(fFlags, pred, kVisible_Shift));
 }
 
 void SkView::setEnabledP(bool pred)
 {
-	this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
+    this->setFlags(SkSetClearShift(fFlags, pred, kEnabled_Shift));
 }
 
 void SkView::setFocusableP(bool pred)
 {
-	this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
+    this->setFlags(SkSetClearShift(fFlags, pred, kFocusable_Shift));
 }
 
 void SkView::setClipToBounds(bool pred) {
@@ -62,37 +65,37 @@
 
 void SkView::setSize(SkScalar width, SkScalar height)
 {
-	width = SkMaxScalar(0, width);
-	height = SkMaxScalar(0, height);
+    width = SkMaxScalar(0, width);
+    height = SkMaxScalar(0, height);
 
-	if (fWidth != width || fHeight != height)
-	{
-		this->inval(NULL);
-		fWidth = width;
-		fHeight = height;
-		this->inval(NULL);
-		this->onSizeChange();
-		this->invokeLayout();
-	}
+    if (fWidth != width || fHeight != height)
+    {
+        this->inval(NULL);
+        fWidth = width;
+        fHeight = height;
+        this->inval(NULL);
+        this->onSizeChange();
+        this->invokeLayout();
+    }
 }
 
 void SkView::setLoc(SkScalar x, SkScalar y)
 {
-	if (fLoc.fX != x || fLoc.fY != y)
-	{
-		this->inval(NULL);
-		fLoc.set(x, y);
+    if (fLoc.fX != x || fLoc.fY != y)
+    {
         this->inval(NULL);
-	}
+        fLoc.set(x, y);
+        this->inval(NULL);
+    }
 }
 
 void SkView::offset(SkScalar dx, SkScalar dy)
 {
-	if (dx || dy)
-		this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
+    if (dx || dy)
+        this->setLoc(fLoc.fX + dx, fLoc.fY + dy);
 }
 
-void SkView::setLocalMatrix(const SkMatrix& matrix) 
+void SkView::setLocalMatrix(const SkMatrix& matrix)
 {
     this->inval(NULL);
     fMatrix = matrix;
@@ -101,53 +104,53 @@
 
 void SkView::draw(SkCanvas* canvas)
 {
-	if (fWidth && fHeight && this->isVisible())
-	{
-		SkRect	r;
-		r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
-		if (this->isClipToBounds() &&
-            canvas->quickReject(r, SkCanvas::kBW_EdgeType)) {
+    if (fWidth && fHeight && this->isVisible())
+    {
+        SkRect    r;
+        r.set(fLoc.fX, fLoc.fY, fLoc.fX + fWidth, fLoc.fY + fHeight);
+        if (this->isClipToBounds() &&
+            canvas->quickReject(r)) {
                 return;
         }
 
-		SkAutoCanvasRestore	as(canvas, true);
+        SkAutoCanvasRestore    as(canvas, true);
 
         if (this->isClipToBounds()) {
             canvas->clipRect(r);
         }
-        
-        canvas->translate(fLoc.fX, fLoc.fY);		
+
+        canvas->translate(fLoc.fX, fLoc.fY);
         canvas->concat(fMatrix);
-        
+
         if (fParent) {
             fParent->beforeChild(this, canvas);
         }
 
         int sc = canvas->save();
-		this->onDraw(canvas);
+        this->onDraw(canvas);
         canvas->restoreToCount(sc);
 
         if (fParent) {
             fParent->afterChild(this, canvas);
         }
-        
-		B2FIter	iter(this);
-		SkView*	child;
+
+        B2FIter    iter(this);
+        SkView*    child;
 
         SkCanvas* childCanvas = this->beforeChildren(canvas);
 
-		while ((child = iter.next()) != NULL)
-			child->draw(childCanvas);
-        
+        while ((child = iter.next()) != NULL)
+            child->draw(childCanvas);
+
         this->afterChildren(canvas);
-	}
+    }
 }
 
 void SkView::inval(SkRect* rect) {
-	SkView*	view = this;
+    SkView*    view = this;
     SkRect storage;
 
-	for (;;) {
+    for (;;) {
         if (!view->isVisible()) {
             return;
         }
@@ -164,7 +167,7 @@
             return;
         }
 
-		SkView* parent = view->fParent;
+        SkView* parent = view->fParent;
         if (parent == NULL) {
             return;
         }
@@ -173,139 +176,139 @@
             rect->offset(view->fLoc.fX, view->fLoc.fY);
         }
         view = parent;
-	}
+    }
 }
 
 ////////////////////////////////////////////////////////////////////////////
 
 bool SkView::setFocusView(SkView* fv)
 {
-	SkView* view = this;
-	
-	do {
-		if (view->onSetFocusView(fv))
-			return true;
-	} while ((view = view->fParent) != NULL);
-	return false;
+    SkView* view = this;
+
+    do {
+        if (view->onSetFocusView(fv))
+            return true;
+    } while ((view = view->fParent) != NULL);
+    return false;
 }
 
 SkView* SkView::getFocusView() const
 {
-	SkView*			focus = NULL;
-	const SkView*	view = this;
-	do {
-		if (view->onGetFocusView(&focus))
-			break;
-	} while ((view = view->fParent) != NULL);
-	return focus;
+    SkView*            focus = NULL;
+    const SkView*    view = this;
+    do {
+        if (view->onGetFocusView(&focus))
+            break;
+    } while ((view = view->fParent) != NULL);
+    return focus;
 }
 
 bool SkView::hasFocus() const
 {
-	return this == this->getFocusView();
+    return this == this->getFocusView();
 }
 
 bool SkView::acceptFocus()
 {
-	return this->isFocusable() && this->setFocusView(this);
+    return this->isFocusable() && this->setFocusView(this);
 }
 
 /*
-	Try to give focus to this view, or its children
+    Try to give focus to this view, or its children
 */
 SkView* SkView::acceptFocus(FocusDirection dir)
 {
-	if (dir == kNext_FocusDirection)
-	{
-		if (this->acceptFocus())
-			return this;
+    if (dir == kNext_FocusDirection)
+    {
+        if (this->acceptFocus())
+            return this;
 
-		B2FIter	iter(this);
-		SkView*	child, *focus;
-		while ((child = iter.next()) != NULL)
-			if ((focus = child->acceptFocus(dir)) != NULL)
-				return focus;
-	}
-	else // prev
-	{
-		F2BIter	iter(this);
-		SkView*	child, *focus;
-		while ((child = iter.next()) != NULL)
-			if ((focus = child->acceptFocus(dir)) != NULL)
-				return focus;
+        B2FIter    iter(this);
+        SkView*    child, *focus;
+        while ((child = iter.next()) != NULL)
+            if ((focus = child->acceptFocus(dir)) != NULL)
+                return focus;
+    }
+    else // prev
+    {
+        F2BIter    iter(this);
+        SkView*    child, *focus;
+        while ((child = iter.next()) != NULL)
+            if ((focus = child->acceptFocus(dir)) != NULL)
+                return focus;
 
-		if (this->acceptFocus())
-			return this;
-	}
+        if (this->acceptFocus())
+            return this;
+    }
 
-	return NULL;
+    return NULL;
 }
 
 SkView* SkView::moveFocus(FocusDirection dir)
 {
-	SkView* focus = this->getFocusView();
+    SkView* focus = this->getFocusView();
 
-	if (focus == NULL)
-	{	// start with the root
-		focus = this;
-		while (focus->fParent)
-			focus = focus->fParent;
-	}
+    if (focus == NULL)
+    {    // start with the root
+        focus = this;
+        while (focus->fParent)
+            focus = focus->fParent;
+    }
 
-	SkView*	child, *parent;
+    SkView*    child, *parent;
 
-	if (dir == kNext_FocusDirection)
-	{
-		parent = focus;
-		child = focus->fFirstChild;
-		if (child)
-			goto FIRST_CHILD;
-		else
-			goto NEXT_SIB;
+    if (dir == kNext_FocusDirection)
+    {
+        parent = focus;
+        child = focus->fFirstChild;
+        if (child)
+            goto FIRST_CHILD;
+        else
+            goto NEXT_SIB;
 
-		do {
-			while (child != parent->fFirstChild)
-			{
-	FIRST_CHILD:
-				if ((focus = child->acceptFocus(dir)) != NULL)
-					return focus;
-				child = child->fNextSibling;
-			}
-	NEXT_SIB:
-			child = parent->fNextSibling;
-			parent = parent->fParent;
-		} while (parent != NULL);
-	}
-	else	// prevfocus
-	{
-		parent = focus->fParent;
-		if (parent == NULL)	// we're the root
-			return focus->acceptFocus(dir);
-		else
-		{
-			child = focus;
-			while (parent)
-			{
-				while (child != parent->fFirstChild)
-				{
-					child = child->fPrevSibling;
-					if ((focus = child->acceptFocus(dir)) != NULL)
-						return focus;
-				}
-				if (parent->acceptFocus())
-					return parent;
+        do {
+            while (child != parent->fFirstChild)
+            {
+    FIRST_CHILD:
+                if ((focus = child->acceptFocus(dir)) != NULL)
+                    return focus;
+                child = child->fNextSibling;
+            }
+    NEXT_SIB:
+            child = parent->fNextSibling;
+            parent = parent->fParent;
+        } while (parent != NULL);
+    }
+    else    // prevfocus
+    {
+        parent = focus->fParent;
+        if (parent == NULL)    // we're the root
+            return focus->acceptFocus(dir);
+        else
+        {
+            child = focus;
+            while (parent)
+            {
+                while (child != parent->fFirstChild)
+                {
+                    child = child->fPrevSibling;
+                    if ((focus = child->acceptFocus(dir)) != NULL)
+                        return focus;
+                }
+                if (parent->acceptFocus())
+                    return parent;
 
-				child = parent;
-				parent = parent->fParent;
-			}
-		}
-	}
-	return NULL;
+                child = parent;
+                parent = parent->fParent;
+            }
+        }
+    }
+    return NULL;
 }
 
 void SkView::onFocusChange(bool gainFocusP)
 {
-	this->inval(NULL);
+    this->inval(NULL);
 }
 
 ////////////////////////////////////////////////////////////////////////////
@@ -321,178 +324,195 @@
 
 SkView::Click::~Click()
 {
-	this->resetType();
+    this->resetType();
 }
 
 void SkView::Click::resetType()
 {
-	if (fWeOwnTheType)
-	{
-		sk_free(fType);
-		fWeOwnTheType = false;
-	}
-	fType = NULL;
+    if (fWeOwnTheType)
+    {
+        sk_free(fType);
+        fWeOwnTheType = false;
+    }
+    fType = NULL;
 }
 
 bool SkView::Click::isType(const char type[]) const
 {
-	const char* t = fType;
+    const char* t = fType;
 
-	if (type == t)
-		return true;
+    if (type == t)
+        return true;
 
-	if (type == NULL)
-		type = "";
-	if (t == NULL)
-		t = "";
-	return !strcmp(t, type);
+    if (type == NULL)
+        type = "";
+    if (t == NULL)
+        t = "";
+    return !strcmp(t, type);
 }
 
 void SkView::Click::setType(const char type[])
 {
-	this->resetType();
-	fType = (char*)type;
+    this->resetType();
+    fType = (char*)type;
 }
 
 void SkView::Click::copyType(const char type[])
 {
-	if (fType != type)
-	{
-		this->resetType();
-		if (type)
-		{
-			size_t	len = strlen(type) + 1;
-			fType = (char*)sk_malloc_throw(len);
-			memcpy(fType, type, len);
-			fWeOwnTheType = true;
-		}
-	}
+    if (fType != type)
+    {
+        this->resetType();
+        if (type)
+        {
+            size_t    len = strlen(type) + 1;
+            fType = (char*)sk_malloc_throw(len);
+            memcpy(fType, type, len);
+            fWeOwnTheType = true;
+        }
+    }
 }
 
-SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y)
-{
-	if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
-		return NULL;
+SkView::Click* SkView::findClickHandler(SkScalar x, SkScalar y, unsigned modi) {
+    if (x < 0 || y < 0 || x >= fWidth || y >= fHeight) {
+        return NULL;
     }
 
-    if (this->onSendClickToChildren(x, y)) {
-        F2BIter	iter(this);
-        SkView*	child;
-        
+    if (this->onSendClickToChildren(x, y, modi)) {
+        F2BIter    iter(this);
+        SkView*    child;
+
         while ((child = iter.next()) != NULL)
         {
             SkPoint p;
-            child->globalToLocal(x, y, &p);
-            
-            Click* click = child->findClickHandler(p.fX, p.fY);
-            
+            if (!child->globalToLocal(x, y, &p)) {
+                continue;
+            }
+
+            Click* click = child->findClickHandler(p.fX, p.fY, modi);
+
             if (click) {
                 return click;
             }
         }
     }
-	return this->onFindClickHandler(x, y);
+
+    return this->onFindClickHandler(x, y, modi);
 }
 
-void SkView::DoClickDown(Click* click, int x, int y)
+void SkView::DoClickDown(Click* click, int x, int y, unsigned modi)
 {
-	SkASSERT(click);
+    SkASSERT(click);
 
-	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
-	if (target == NULL)
-		return;
+    SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+    if (NULL == target) {
+        return;
+    }
 
-	click->fIOrig.set(x, y);
-	click->fICurr = click->fIPrev = click->fIOrig;
+    click->fIOrig.set(x, y);
+    click->fICurr = click->fIPrev = click->fIOrig;
 
-	click->fOrig.iset(x, y);
-	target->globalToLocal(&click->fOrig);
-	click->fPrev = click->fCurr = click->fOrig;
+    click->fOrig.iset(x, y);
+    if (!target->globalToLocal(&click->fOrig)) {
+        // no history to let us recover from this failure
+        return;
+    }
+    click->fPrev = click->fCurr = click->fOrig;
 
-	click->fState = Click::kDown_State;
-	target->onClick(click);
+    click->fState = Click::kDown_State;
+    click->fModifierKeys = modi;
+    target->onClick(click);
 }
 
-void SkView::DoClickMoved(Click* click, int x, int y)
+void SkView::DoClickMoved(Click* click, int x, int y, unsigned modi)
 {
-	SkASSERT(click);
+    SkASSERT(click);
 
-	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
-	if (target == NULL)
-		return;
+    SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+    if (NULL == target) {
+        return;
+    }
 
-	click->fIPrev = click->fICurr;
-	click->fICurr.set(x, y);
+    click->fIPrev = click->fICurr;
+    click->fICurr.set(x, y);
 
-	click->fPrev = click->fCurr;
-	click->fCurr.iset(x, y);
-	target->globalToLocal(&click->fCurr);
+    click->fPrev = click->fCurr;
+    click->fCurr.iset(x, y);
+    if (!target->globalToLocal(&click->fCurr)) {
+        // on failure pretend the mouse didn't move
+        click->fCurr = click->fPrev;
+    }
 
-	click->fState = Click::kMoved_State;
-	target->onClick(click);
+    click->fState = Click::kMoved_State;
+    click->fModifierKeys = modi;
+    target->onClick(click);
 }
 
-void SkView::DoClickUp(Click* click, int x, int y)
+void SkView::DoClickUp(Click* click, int x, int y, unsigned modi)
 {
-	SkASSERT(click);
+    SkASSERT(click);
 
-	SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
-	if (target == NULL)
-		return;
+    SkView* target = (SkView*)SkEventSink::FindSink(click->fTargetID);
+    if (NULL == target) {
+        return;
+    }
 
-	click->fIPrev = click->fICurr;
-	click->fICurr.set(x, y);
+    click->fIPrev = click->fICurr;
+    click->fICurr.set(x, y);
 
-	click->fPrev = click->fCurr;
-	click->fCurr.iset(x, y);
-	target->globalToLocal(&click->fCurr);
+    click->fPrev = click->fCurr;
+    click->fCurr.iset(x, y);
+    if (!target->globalToLocal(&click->fCurr)) {
+        // on failure pretend the mouse didn't move
+        click->fCurr = click->fPrev;
+    }
 
-	click->fState = Click::kUp_State;
-	target->onClick(click);
+    click->fState = Click::kUp_State;
+    click->fModifierKeys = modi;
+    target->onClick(click);
 }
 
 //////////////////////////////////////////////////////////////////////
 
 void SkView::invokeLayout() {
-	SkView::Layout* layout = this->getLayout();
+    SkView::Layout* layout = this->getLayout();
 
-	if (layout) {
-		layout->layoutChildren(this);
+    if (layout) {
+        layout->layoutChildren(this);
     }
 }
 
 void SkView::onDraw(SkCanvas* canvas) {
-	Artist* artist = this->getArtist();
+    Artist* artist = this->getArtist();
 
-	if (artist) {
-		artist->draw(this, canvas);
+    if (artist) {
+        artist->draw(this, canvas);
     }
 }
 
 void SkView::onSizeChange() {}
 
-bool SkView::onSendClickToChildren(SkScalar x, SkScalar y) {
+bool SkView::onSendClickToChildren(SkScalar x, SkScalar y, unsigned modi) {
     return true;
 }
 
-SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y) {
-	return NULL;
+SkView::Click* SkView::onFindClickHandler(SkScalar x, SkScalar y, unsigned modi) {
+    return NULL;
 }
 
 bool SkView::onClick(Click*) {
-	return false;
+    return false;
 }
 
 bool SkView::handleInval(const SkRect*) {
-	return false;
+    return false;
 }
 
 //////////////////////////////////////////////////////////////////////
 
 void SkView::getLocalBounds(SkRect* bounds) const
 {
-	if (bounds)
-		bounds->set(0, 0, fWidth, fHeight);
+    if (bounds)
+        bounds->set(0, 0, fWidth, fHeight);
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -500,110 +520,110 @@
 
 void SkView::detachFromParent_NoLayout()
 {
-	if (fParent == NULL)
-		return;
+    if (fParent == NULL)
+        return;
 
-	if (fContainsFocus)
-		(void)this->setFocusView(NULL);
+    if (fContainsFocus)
+        (void)this->setFocusView(NULL);
 
-	this->inval(NULL);
+    this->inval(NULL);
 
-	SkView*	next = NULL;
+    SkView*    next = NULL;
 
-	if (fNextSibling != this)	// do we have any siblings
-	{
-		fNextSibling->fPrevSibling = fPrevSibling;
-		fPrevSibling->fNextSibling = fNextSibling;
-		next = fNextSibling;
-	}
+    if (fNextSibling != this)    // do we have any siblings
+    {
+        fNextSibling->fPrevSibling = fPrevSibling;
+        fPrevSibling->fNextSibling = fNextSibling;
+        next = fNextSibling;
+    }
 
-	if (fParent->fFirstChild == this)
-		fParent->fFirstChild = next;
+    if (fParent->fFirstChild == this)
+        fParent->fFirstChild = next;
 
-	fParent = fNextSibling = fPrevSibling = NULL;
+    fParent = fNextSibling = fPrevSibling = NULL;
 
-	this->unref();
+    this->unref();
 }
 
 void SkView::detachFromParent()
 {
-	SkView* parent = fParent;
+    SkView* parent = fParent;
 
-	if (parent)
-	{
-		this->detachFromParent_NoLayout();
-		parent->invokeLayout();
-	}
+    if (parent)
+    {
+        this->detachFromParent_NoLayout();
+        parent->invokeLayout();
+    }
 }
 
 SkView* SkView::attachChildToBack(SkView* child)
 {
-	SkASSERT(child != this);
+    SkASSERT(child != this);
 
-	if (child == NULL || fFirstChild == child)
-		goto DONE;
+    if (child == NULL || fFirstChild == child)
+        goto DONE;
 
-	child->ref();
-	child->detachFromParent_NoLayout();
+    child->ref();
+    child->detachFromParent_NoLayout();
 
-	if (fFirstChild == NULL)
-	{
-		child->fNextSibling = child;
-		child->fPrevSibling = child;
-	}
-	else
-	{
-		child->fNextSibling = fFirstChild;
-		child->fPrevSibling = fFirstChild->fPrevSibling;
-		fFirstChild->fPrevSibling->fNextSibling = child;
-		fFirstChild->fPrevSibling = child;
-	}
+    if (fFirstChild == NULL)
+    {
+        child->fNextSibling = child;
+        child->fPrevSibling = child;
+    }
+    else
+    {
+        child->fNextSibling = fFirstChild;
+        child->fPrevSibling = fFirstChild->fPrevSibling;
+        fFirstChild->fPrevSibling->fNextSibling = child;
+        fFirstChild->fPrevSibling = child;
+    }
 
-	fFirstChild = child;
-	child->fParent = this;
-	child->inval(NULL);
+    fFirstChild = child;
+    child->fParent = this;
+    child->inval(NULL);
 
-	this->invokeLayout();
+    this->invokeLayout();
 DONE:
-	return child;
+    return child;
 }
 
 SkView* SkView::attachChildToFront(SkView* child)
 {
-	SkASSERT(child != this);
+    SkASSERT(child != this);
 
-	if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
-		goto DONE;
+    if (child == NULL || (fFirstChild && fFirstChild->fPrevSibling == child))
+        goto DONE;
 
-	child->ref();
-	child->detachFromParent_NoLayout();
+    child->ref();
+    child->detachFromParent_NoLayout();
 
-	if (fFirstChild == NULL)
-	{
-		fFirstChild = child;
-		child->fNextSibling = child;
-		child->fPrevSibling = child;
-	}
-	else
-	{
-		child->fNextSibling = fFirstChild;
-		child->fPrevSibling = fFirstChild->fPrevSibling;
-		fFirstChild->fPrevSibling->fNextSibling = child;
-		fFirstChild->fPrevSibling = child;
-	}
+    if (fFirstChild == NULL)
+    {
+        fFirstChild = child;
+        child->fNextSibling = child;
+        child->fPrevSibling = child;
+    }
+    else
+    {
+        child->fNextSibling = fFirstChild;
+        child->fPrevSibling = fFirstChild->fPrevSibling;
+        fFirstChild->fPrevSibling->fNextSibling = child;
+        fFirstChild->fPrevSibling = child;
+    }
 
-	child->fParent = this;
-	child->inval(NULL);
+    child->fParent = this;
+    child->inval(NULL);
 
-	this->invokeLayout();
+    this->invokeLayout();
 DONE:
-	return child;
+    return child;
 }
 
 void SkView::detachAllChildren()
 {
-	while (fFirstChild)
-		fFirstChild->detachFromParent_NoLayout();
+    while (fFirstChild)
+        fFirstChild->detachFromParent_NoLayout();
 }
 
 void SkView::localToGlobal(SkMatrix* matrix) const
@@ -619,101 +639,105 @@
         }
     }
 }
-void SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
+bool SkView::globalToLocal(SkScalar x, SkScalar y, SkPoint* local) const
 {
-	SkASSERT(this);
-	if (local)
-	{
+    SkASSERT(this);
+
+    if (NULL != local) {
         SkMatrix m;
         this->localToGlobal(&m);
+        if (!m.invert(&m)) {
+            return false;
+        }
         SkPoint p;
-        m.invert(&m);
         m.mapXY(x, y, &p);
-		local->set(p.fX, p.fY);
-	}
+        local->set(p.fX, p.fY);
+    }
+
+    return true;
 }
 
 //////////////////////////////////////////////////////////////////
 
-/*	Even if the subclass overrides onInflate, they should always be
-	sure to call the inherited method, so that we get called.
+/*    Even if the subclass overrides onInflate, they should always be
+    sure to call the inherited method, so that we get called.
 */
 void SkView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	SkScalar x, y;
+    SkScalar x, y;
 
-	x = this->locX();
-	y = this->locY();
-	(void)dom.findScalar(node, "x", &x);
-	(void)dom.findScalar(node, "y", &y);
-	this->setLoc(x, y);
+    x = this->locX();
+    y = this->locY();
+    (void)dom.findScalar(node, "x", &x);
+    (void)dom.findScalar(node, "y", &y);
+    this->setLoc(x, y);
 
-	x = this->width();
-	y = this->height();
-	(void)dom.findScalar(node, "width", &x);
-	(void)dom.findScalar(node, "height", &y);
-	this->setSize(x, y);
+    x = this->width();
+    y = this->height();
+    (void)dom.findScalar(node, "width", &x);
+    (void)dom.findScalar(node, "height", &y);
+    this->setSize(x, y);
 
-	// inflate the flags
+    // inflate the flags
 
-	static const char* gFlagNames[] = {
-		"visible", "enabled", "focusable", "flexH", "flexV"
-	};
-	SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
+    static const char* gFlagNames[] = {
+        "visible", "enabled", "focusable", "flexH", "flexV"
+    };
+    SkASSERT(SK_ARRAY_COUNT(gFlagNames) == kFlagShiftCount);
 
-	bool     b;
-	uint32_t flags = this->getFlags();
-	for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
-		if (dom.findBool(node, gFlagNames[i], &b))
-			flags = SkSetClearShift(flags, b, i);
-	this->setFlags(flags);
+    bool     b;
+    uint32_t flags = this->getFlags();
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gFlagNames); i++)
+        if (dom.findBool(node, gFlagNames[i], &b))
+            flags = SkSetClearShift(flags, b, i);
+    this->setFlags(flags);
 }
 
 void SkView::inflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->onInflate(dom, node);
+    this->onInflate(dom, node);
 }
 
 void SkView::onPostInflate(const SkTDict<SkView*>&)
 {
-	// override in subclass as needed
+    // override in subclass as needed
 }
 
 void SkView::postInflate(const SkTDict<SkView*>& dict)
 {
-	this->onPostInflate(dict);
+    this->onPostInflate(dict);
 
-	B2FIter	iter(this);
-	SkView*	child;
-	while ((child = iter.next()) != NULL)
-		child->postInflate(dict);
+    B2FIter    iter(this);
+    SkView*    child;
+    while ((child = iter.next()) != NULL)
+        child->postInflate(dict);
 }
 
 //////////////////////////////////////////////////////////////////
 
 SkView* SkView::sendEventToParents(const SkEvent& evt)
 {
-	SkView* parent = fParent;
-    
-	while (parent)
-	{
-		if (parent->doEvent(evt))
-			return parent;
-		parent = parent->fParent;
-	}
-	return NULL;
+    SkView* parent = fParent;
+
+    while (parent)
+    {
+        if (parent->doEvent(evt))
+            return parent;
+        parent = parent->fParent;
+    }
+    return NULL;
 }
 
 SkView* SkView::sendQueryToParents(SkEvent* evt) {
-	SkView* parent = fParent;
-    
-	while (parent) {
-		if (parent->doQuery(evt)) {
-			return parent;
+    SkView* parent = fParent;
+
+    while (parent) {
+        if (parent->doQuery(evt)) {
+            return parent;
         }
-		parent = parent->fParent;
-	}
-	return NULL;
+        parent = parent->fParent;
+    }
+    return NULL;
 }
 
 //////////////////////////////////////////////////////////////////
@@ -721,42 +745,42 @@
 
 SkView::F2BIter::F2BIter(const SkView* parent)
 {
-	fFirstChild = parent ? parent->fFirstChild : NULL;
-	fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
+    fFirstChild = parent ? parent->fFirstChild : NULL;
+    fChild = fFirstChild ? fFirstChild->fPrevSibling : NULL;
 }
 
-SkView*	SkView::F2BIter::next()
+SkView*    SkView::F2BIter::next()
 {
-	SkView* curr = fChild;
+    SkView* curr = fChild;
 
-	if (fChild)
-	{
-		if (fChild == fFirstChild)
-			fChild = NULL;
-		else
-			fChild = fChild->fPrevSibling;
-	}
-	return curr;
+    if (fChild)
+    {
+        if (fChild == fFirstChild)
+            fChild = NULL;
+        else
+            fChild = fChild->fPrevSibling;
+    }
+    return curr;
 }
 
 SkView::B2FIter::B2FIter(const SkView* parent)
 {
-	fFirstChild = parent ? parent->fFirstChild : NULL;
-	fChild = fFirstChild;
+    fFirstChild = parent ? parent->fFirstChild : NULL;
+    fChild = fFirstChild;
 }
 
-SkView*	SkView::B2FIter::next()
+SkView*    SkView::B2FIter::next()
 {
-	SkView* curr = fChild;
+    SkView* curr = fChild;
 
-	if (fChild)
-	{
-		SkView* next = fChild->fNextSibling;
-		if (next == fFirstChild)
-			next = NULL;
-		fChild = next;
-	}
-	return curr;
+    if (fChild)
+    {
+        SkView* next = fChild->fNextSibling;
+        if (next == fFirstChild)
+            next = NULL;
+        fChild = next;
+    }
+    return curr;
 }
 
 //////////////////////////////////////////////////////////////////
@@ -766,58 +790,58 @@
 
 static inline void show_if_nonzero(const char name[], SkScalar value)
 {
-	if (value)
-		SkDebugf("%s=\"%g\"", name, value/65536.);
+    if (value)
+        SkDebugf("%s=\"%g\"", name, value/65536.);
 }
 
 static void tab(int level)
 {
-	for (int i = 0; i < level; i++)
-		SkDebugf("    ");
+    for (int i = 0; i < level; i++)
+        SkDebugf("    ");
 }
 
 static void dumpview(const SkView* view, int level, bool recurse)
 {
-	tab(level);
+    tab(level);
 
-	SkDebugf("<view");
-	show_if_nonzero(" x", view->locX());
-	show_if_nonzero(" y", view->locY());
-	show_if_nonzero(" width", view->width());
-	show_if_nonzero(" height", view->height());
+    SkDebugf("<view");
+    show_if_nonzero(" x", view->locX());
+    show_if_nonzero(" y", view->locY());
+    show_if_nonzero(" width", view->width());
+    show_if_nonzero(" height", view->height());
 
-	if (recurse)
-	{
-		SkView::B2FIter	iter(view);
-		SkView*			child;
-		bool			noChildren = true;
+    if (recurse)
+    {
+        SkView::B2FIter    iter(view);
+        SkView*            child;
+        bool            noChildren = true;
 
-		while ((child = iter.next()) != NULL)
-		{
-			if (noChildren)
-				SkDebugf(">\n");
-			noChildren = false;
-			dumpview(child, level + 1, true);
-		}
+        while ((child = iter.next()) != NULL)
+        {
+            if (noChildren)
+                SkDebugf(">\n");
+            noChildren = false;
+            dumpview(child, level + 1, true);
+        }
 
-		if (!noChildren)
-		{
-			tab(level);
-			SkDebugf("</view>\n");
-		}
-		else
-			goto ONELINER;
-	}
-	else
-	{
-	ONELINER:
-		SkDebugf(" />\n");
-	}
+        if (!noChildren)
+        {
+            tab(level);
+            SkDebugf("</view>\n");
+        }
+        else
+            goto ONELINER;
+    }
+    else
+    {
+    ONELINER:
+        SkDebugf(" />\n");
+    }
 }
 
 void SkView::dump(bool recurse) const
 {
-	dumpview(this, 0, recurse);
+    dumpview(this, 0, recurse);
 }
 
 #endif
diff --git a/src/views/SkViewInflate.cpp b/src/views/SkViewInflate.cpp
index 184e540..59cc481 100644
--- a/src/views/SkViewInflate.cpp
+++ b/src/views/SkViewInflate.cpp
@@ -19,128 +19,127 @@
 
 void SkViewInflate::rInflate(const SkDOM& dom, const SkDOM::Node* node, SkView* parent)
 {
-	const char* str = dom.findAttr(node, "id");
-	if (str)
-		fIDs.set(str, parent);
+    const char* str = dom.findAttr(node, "id");
+    if (str)
+        fIDs.set(str, parent);
 
-	const SkDOM::Node* child = dom.getFirstChild(node);
-	while (child)
-	{
-		SkView* view = this->createView(dom, child);
-		if (view)
-		{
-			this->rInflate(dom, child, view);
-			parent->attachChildToFront(view)->unref();
-		}
-		else
-		{
-			const char* name = dom.getName(child);
-			const char* target;
+    const SkDOM::Node* child = dom.getFirstChild(node);
+    while (child)
+    {
+        SkView* view = this->createView(dom, child);
+        if (view)
+        {
+            this->rInflate(dom, child, view);
+            parent->attachChildToFront(view)->unref();
+        }
+        else
+        {
+            const char* name = dom.getName(child);
+            const char* target;
 
-			if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != NULL)
-				this->addIDStr(&fListenTo, parent, target);
+            if (!strcmp(name, "listenTo") && (target = dom.findAttr(child, "target")) != NULL)
+                this->addIDStr(&fListenTo, parent, target);
 
-			if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL)
-				this->addIDStr(&fBroadcastTo, parent, target);
-		}
-		child = dom.getNextSibling(child);
-	}
+            if (!strcmp(name, "broadcastTo") && (target = dom.findAttr(child, "target")) != NULL)
+                this->addIDStr(&fBroadcastTo, parent, target);
+        }
+        child = dom.getNextSibling(child);
+    }
 
-	parent->setVisibleP(true);
-	this->inflateView(parent, dom, node);
+    parent->setVisibleP(true);
+    this->inflateView(parent, dom, node);
 }
 
 void SkViewInflate::inflateView(SkView* view, const SkDOM& dom, const SkDOM::Node* node)
 {
-	// called after all of view's children have been instantiated.
-	// this may be overridden by a subclass, to load in layout or other helpers
-	// they should call through to us (INHERITED) before or after their patch
-	view->inflate(dom, node);
+    // called after all of view's children have been instantiated.
+    // this may be overridden by a subclass, to load in layout or other helpers
+    // they should call through to us (INHERITED) before or after their patch
+    view->inflate(dom, node);
 }
 
 SkView* SkViewInflate::inflate(const SkDOM& dom, const SkDOM::Node* node, SkView* root)
 {
-	fIDs.reset();
+    fIDs.reset();
 
-	if (root == NULL)
-	{
-		root = this->createView(dom, node);
-		if (root == NULL)
-		{
-			printf("createView returned NULL on <%s>\n", dom.getName(node));
-			return NULL;
-		}
-	}
-	this->rInflate(dom, node, root);
+    if (root == NULL)
+    {
+        root = this->createView(dom, node);
+        if (root == NULL)
+        {
+            printf("createView returned NULL on <%s>\n", dom.getName(node));
+            return NULL;
+        }
+    }
+    this->rInflate(dom, node, root);
 
-	// resolve listeners and broadcasters
-	{
-		SkView*			target;
-		const IDStr*	iter = fListenTo.begin();
-		const IDStr*	stop = fListenTo.end();
-		for (; iter < stop; iter++)
-		{
-			if (fIDs.find(iter->fStr, &target))
-				target->addListenerID(iter->fView->getSinkID());
-		}
+    // resolve listeners and broadcasters
+    {
+        SkView*            target;
+        const IDStr*    iter = fListenTo.begin();
+        const IDStr*    stop = fListenTo.end();
+        for (; iter < stop; iter++)
+        {
+            if (fIDs.find(iter->fStr, &target))
+                target->addListenerID(iter->fView->getSinkID());
+        }
 
-		iter = fBroadcastTo.begin();
-		stop = fBroadcastTo.end();
-		for (; iter < stop; iter++)
-		{
-			if (fIDs.find(iter->fStr, &target))
-				iter->fView->addListenerID(target->getSinkID());
-		}
-	}
+        iter = fBroadcastTo.begin();
+        stop = fBroadcastTo.end();
+        for (; iter < stop; iter++)
+        {
+            if (fIDs.find(iter->fStr, &target))
+                iter->fView->addListenerID(target->getSinkID());
+        }
+    }
 
-	// now that the tree is built, give everyone a shot at the ID dict
-	root->postInflate(fIDs);
-	return root;
+    // now that the tree is built, give everyone a shot at the ID dict
+    root->postInflate(fIDs);
+    return root;
 }
 
 SkView* SkViewInflate::inflate(const char xml[], size_t len, SkView* root)
 {
-	SkDOM				dom;
-	const SkDOM::Node*	node = dom.build(xml, len);
+    SkDOM                dom;
+    const SkDOM::Node*    node = dom.build(xml, len);
 
-	return node ? this->inflate(dom, node, root) : NULL;
+    return node ? this->inflate(dom, node, root) : NULL;
 }
 
 SkView* SkViewInflate::findViewByID(const char id[]) const
 {
-	SkASSERT(id);
-	SkView* view;
-	return fIDs.find(id, &view) ? view : NULL;
+    SkASSERT(id);
+    SkView* view;
+    return fIDs.find(id, &view) ? view : NULL;
 }
 
 SkView* SkViewInflate::createView(const SkDOM& dom, const SkDOM::Node* node)
 {
-	if (!strcmp(dom.getName(node), "view"))
-		return new SkView;
-	return NULL;
+    if (!strcmp(dom.getName(node), "view"))
+        return new SkView;
+    return NULL;
 }
 
 void SkViewInflate::addIDStr(SkTDArray<IDStr>* list, SkView* view, const char* str)
 {
-	size_t len = strlen(str) + 1;
-	IDStr* pair = list->append();
-	pair->fView = view;
-	pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType);
-	memcpy(pair->fStr, str, len);
+    size_t len = strlen(str) + 1;
+    IDStr* pair = list->append();
+    pair->fView = view;
+    pair->fStr = (char*)fStrings.alloc(len, SkChunkAlloc::kThrow_AllocFailType);
+    memcpy(pair->fStr, str, len);
 }
 
 #ifdef SK_DEBUG
 void SkViewInflate::dump() const
 {
-	const IDStr* iter = fListenTo.begin();
-	const IDStr* stop = fListenTo.end();
-	for (; iter < stop; iter++)
-		SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr);
+    const IDStr* iter = fListenTo.begin();
+    const IDStr* stop = fListenTo.end();
+    for (; iter < stop; iter++)
+        SkDebugf("inflate: listenTo(\"%s\")\n", iter->fStr);
 
-	iter = fBroadcastTo.begin();
-	stop = fBroadcastTo.end();
-	for (; iter < stop; iter++)
-		SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
+    iter = fBroadcastTo.begin();
+    stop = fBroadcastTo.end();
+    for (; iter < stop; iter++)
+        SkDebugf("inflate: broadcastFrom(\"%s\")\n", iter->fStr);
 }
 #endif
-
diff --git a/src/views/SkViewPriv.cpp b/src/views/SkViewPriv.cpp
index ad15f3f..0348fd7 100644
--- a/src/views/SkViewPriv.cpp
+++ b/src/views/SkViewPriv.cpp
@@ -11,94 +11,93 @@
 
 void SkView::Artist::draw(SkView* view, SkCanvas* canvas)
 {
-	SkASSERT(view && canvas);
-	this->onDraw(view, canvas);
+    SkASSERT(view && canvas);
+    this->onDraw(view, canvas);
 }
 
 void SkView::Artist::inflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	SkASSERT(&dom && node);
-	this->onInflate(dom, node);
+    SkASSERT(&dom && node);
+    this->onInflate(dom, node);
 }
 
 void SkView::Artist::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	// subclass should override this as needed
+    // subclass should override this as needed
 }
 
 SkView::Artist* SkView::getArtist() const
 {
-	Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
-	SkASSERT(rec == NULL || rec->fArtist != NULL);
+    Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+    SkASSERT(rec == NULL || rec->fArtist != NULL);
 
-	return rec ? rec->fArtist : NULL;
+    return rec ? rec->fArtist : NULL;
 }
 
 SkView::Artist* SkView::setArtist(Artist* obj)
 {
-	if (obj == NULL)
-	{
-		this->removeTagList(kViewArtist_SkTagList);
-	}
-	else	// add/replace
-	{
-		Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
+    if (obj == NULL)
+    {
+        this->removeTagList(kViewArtist_SkTagList);
+    }
+    else    // add/replace
+    {
+        Artist_SkTagList* rec = (Artist_SkTagList*)this->findTagList(kViewArtist_SkTagList);
 
-		if (rec)
-			SkRefCnt_SafeAssign(rec->fArtist, obj);
-		else
-			this->addTagList(new Artist_SkTagList(obj));
-	}
-	return obj;
+        if (rec)
+            SkRefCnt_SafeAssign(rec->fArtist, obj);
+        else
+            this->addTagList(new Artist_SkTagList(obj));
+    }
+    return obj;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 void SkView::Layout::layoutChildren(SkView* parent)
 {
-	SkASSERT(parent);
-	if (parent->width() > 0 && parent->height() > 0)
-		this->onLayoutChildren(parent);
+    SkASSERT(parent);
+    if (parent->width() > 0 && parent->height() > 0)
+        this->onLayoutChildren(parent);
 }
 
 void SkView::Layout::inflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	SkASSERT(&dom && node);
-	this->onInflate(dom, node);
+    SkASSERT(&dom && node);
+    this->onInflate(dom, node);
 }
 
 void SkView::Layout::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	// subclass should override this as needed
+    // subclass should override this as needed
 }
 
 SkView::Layout* SkView::getLayout() const
 {
-	Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
-	SkASSERT(rec == NULL || rec->fLayout != NULL);
+    Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+    SkASSERT(rec == NULL || rec->fLayout != NULL);
 
-	return rec ? rec->fLayout : NULL;
+    return rec ? rec->fLayout : NULL;
 }
 
 SkView::Layout* SkView::setLayout(Layout* obj, bool invokeLayoutNow)
 {
-	if (obj == NULL)
-	{
-		this->removeTagList(kViewLayout_SkTagList);
-	}
-	else	// add/replace
-	{
-		Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
+    if (obj == NULL)
+    {
+        this->removeTagList(kViewLayout_SkTagList);
+    }
+    else    // add/replace
+    {
+        Layout_SkTagList* rec = (Layout_SkTagList*)this->findTagList(kViewLayout_SkTagList);
 
-		if (rec)
-			SkRefCnt_SafeAssign(rec->fLayout, obj);
-		else
-			this->addTagList(new Layout_SkTagList(obj));
-	}
-	
-	if (invokeLayoutNow)
-		this->invokeLayout();
+        if (rec)
+            SkRefCnt_SafeAssign(rec->fLayout, obj);
+        else
+            this->addTagList(new Layout_SkTagList(obj));
+    }
 
-	return obj;
+    if (invokeLayoutNow)
+        this->invokeLayout();
+
+    return obj;
 }
-
diff --git a/src/views/SkViewPriv.h b/src/views/SkViewPriv.h
index d8cf966..487e379 100644
--- a/src/views/SkViewPriv.h
+++ b/src/views/SkViewPriv.h
@@ -12,34 +12,33 @@
 #include "SkTagList.h"
 
 struct Layout_SkTagList : SkTagList {
-	SkView::Layout*	fLayout;
+    SkView::Layout*    fLayout;
 
-	Layout_SkTagList(SkView::Layout* layout)
-		: SkTagList(kViewLayout_SkTagList), fLayout(layout)
-	{
-		SkASSERT(layout);
-		layout->ref();
-	}
-	virtual ~Layout_SkTagList()
-	{
-		fLayout->unref();
-	}
+    Layout_SkTagList(SkView::Layout* layout)
+        : SkTagList(kViewLayout_SkTagList), fLayout(layout)
+    {
+        SkASSERT(layout);
+        layout->ref();
+    }
+    virtual ~Layout_SkTagList()
+    {
+        fLayout->unref();
+    }
 };
 
 struct Artist_SkTagList : SkTagList {
-	SkView::Artist*	fArtist;
+    SkView::Artist*    fArtist;
 
-	Artist_SkTagList(SkView::Artist* artist)
-		: SkTagList(kViewArtist_SkTagList), fArtist(artist)
-	{
-		SkASSERT(artist);
-		artist->ref();
-	}
-	virtual ~Artist_SkTagList()
-	{
-		fArtist->unref();
-	}
+    Artist_SkTagList(SkView::Artist* artist)
+        : SkTagList(kViewArtist_SkTagList), fArtist(artist)
+    {
+        SkASSERT(artist);
+        artist->ref();
+    }
+    virtual ~Artist_SkTagList()
+    {
+        fArtist->unref();
+    }
 };
 
 #endif
-
diff --git a/src/views/SkWidget.cpp b/src/views/SkWidget.cpp
deleted file mode 100644
index 104429d..0000000
--- a/src/views/SkWidget.cpp
+++ /dev/null
@@ -1,330 +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 "SkWidget.h"
-#include "SkCanvas.h"
-#include "SkInterpolator.h"
-#include "SkTime.h"
-#include "SkParsePaint.h"
-
-#if 0
-SkWidgetView::SkWidgetView(U32 flags) : SkView(flags)
-{
-}
-
-SkWidgetView::~SkWidgetView()
-{
-}
-
-const char* SkWidgetView::GetEventType()
-{
-	return "SkWidgetView";
-}
-
-/////////////////////////////////////////////////////////////////////////////
-/////////////////////////////////////////////////////////////////////////////
-
-class SkTextView::Interp {
-public:
-	Interp(const SkString& old, SkMSec now, SkMSec dur, AnimaDir dir) : fOldText(old), fInterp(1, 2)
-	{
-		SkScalar x = 0;
-		fInterp.setKeyFrame(0, now, &x, 0);
-		x = SK_Scalar1;
-		if (dir == kBackward_AnimDir)
-			x = -x;
-		fInterp.setKeyFrame(1, now + dur, &x);
-	}
-	bool draw(SkCanvas* canvas, const SkString& newText, SkScalar x, SkScalar y, SkPaint& paint)
-	{
-		SkScalar scale;
-
-		if (fInterp.timeToValues(SkTime::GetMSecs(), &scale) == SkInterpolator::kFreezeEnd_Result)
-		{
-			canvas->drawText(newText.c_str(), newText.size(), x, y, paint);
-			return false;
-		}
-		else
-		{
-			U8 alpha = paint.getAlpha();
-			SkScalar above, below;
-			(void)paint.measureText(NULL, 0, &above, &below);
-			SkScalar height = below - above;
-			SkScalar dy = SkScalarMul(height, scale);
-			if (scale < 0)
-				height = -height;
-
-			// draw the old
-			paint.setAlpha((U8)SkScalarMul(alpha, SK_Scalar1 - SkScalarAbs(scale)));
-			canvas->drawText(fOldText.c_str(), fOldText.size(), x, y - dy, paint);
-			// draw the new
-			paint.setAlpha((U8)SkScalarMul(alpha, SkScalarAbs(scale)));
-			canvas->drawText(newText.c_str(), newText.size(), x, y + height - dy, paint);
-			// restore the paint
-			paint.setAlpha(alpha);
-			return true;
-		}
-	}
-
-private:
-	SkString		fOldText;
-	SkInterpolator	fInterp;
-};
-
-SkTextView::SkTextView(U32 flags) : SkView(flags), fInterp(NULL), fDoInterp(false)
-{
-	fMargin.set(0, 0);
-}
-
-SkTextView::~SkTextView()
-{
-	delete fInterp;
-}
-
-void SkTextView::getText(SkString* str) const
-{
-	if (str)
-		str->set(fText);
-}
-
-void SkTextView::setText(const char text[], AnimaDir dir)
-{
-	if (!fText.equals(text))
-	{
-		SkString tmp(text);
-		this->privSetText(tmp, dir);
-	}
-}
-
-void SkTextView::setText(const char text[], size_t len, AnimaDir dir)
-{
-	if (!fText.equals(text))
-	{
-		SkString tmp(text, len);
-		this->privSetText(tmp, dir);
-	}
-}
-
-void SkTextView::setText(const SkString& src, AnimaDir dir)
-{
-	if (fText != src)
-		this->privSetText(src, dir);
-}
-
-void SkTextView::privSetText(const SkString& src, AnimaDir dir)
-{
-	SkASSERT(fText != src);
-
-	if (fDoInterp)
-	{
-		if (fInterp)
-			delete fInterp;
-		fInterp = new Interp(fText, SkTime::GetMSecs(), 500, dir);
-	}
-	fText = src;
-	this->inval(NULL);
-}
-
-/////////////////////////////////////////////////////////////////
-
-void SkTextView::getMargin(SkPoint* margin) const
-{
-	if (margin)
-		*margin = fMargin;
-}
-
-void SkTextView::setMargin(const SkPoint& margin)
-{
-	if (fMargin != margin)
-	{
-		fMargin = margin;
-		this->inval(NULL);
-	}
-}
-
-void SkTextView::onDraw(SkCanvas* canvas)
-{
-	this->INHERITED::onDraw(canvas);
-
-	if (fText.size() == 0)
-		return;
-
-	SkPaint::Align	align = fPaint.getTextAlign();
-	SkScalar		x, y;
-
-	switch (align) {
-	case SkPaint::kLeft_Align:
-		x = fMargin.fX;
-		break;
-	case SkPaint::kCenter_Align:
-		x = SkScalarHalf(this->width());
-		break;
-	default:
-		SkASSERT(align == SkPaint::kRight_Align);
-		x = this->width() - fMargin.fX;
-		break;
-	}
-
-	fPaint.measureText(NULL, 0, &y, NULL);
-	y = fMargin.fY - y;
-
-	if (fInterp)
-	{
-		if (fInterp->draw(canvas, fText, x, y, fPaint))
-			this->inval(NULL);
-		else
-		{
-			delete fInterp;
-			fInterp = NULL;
-		}
-	}
-	else
-		canvas->drawText(fText.c_str(), fText.size(), x, y, fPaint);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////
-
-void SkTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-
-	const char* text = dom.findAttr(node, "text");
-	if (text)
-		this->setText(text);
-
-	SkPoint	margin;
-	if (dom.findScalars(node, "margin", (SkScalar*)&margin, 2))
-		this->setMargin(margin);
-	(void)dom.findBool(node, "do-interp", &fDoInterp);
-
-	SkPaint_Inflate(&fPaint, dom, node);
-}
-
-//////////////////////////////////////////////////////////////////////////////////////
-
-SkSliderView::SkSliderView(U32 flags) : SkWidgetView(flags)
-{
-	fValue = 0;
-	fMax = 0;
-}
-
-static U16 actual_value(U16CPU value, U16CPU max)
-{
-	return SkToU16(SkMax32(0, SkMin32(value, max)));
-}
-
-void SkSliderView::setMax(U16CPU max)
-{
-	if (fMax != max)
-	{
-		fMax = SkToU16(max);
-		if (fValue > 0)
-			this->inval(NULL);
-	}
-}
-
-void SkSliderView::setValue(U16CPU value)
-{
-	if (fValue != value)
-	{
-		U16 prev = actual_value(fValue, fMax);
-		U16 next = actual_value(value, fMax);
-
-		fValue = SkToU16(value);
-		if (prev != next)
-		{
-			this->inval(NULL);
-
-			if (this->hasListeners())
-			{
-				SkEvent	evt;
-				
-				evt.setType(SkWidgetView::GetEventType());
-				evt.setFast32(this->getSinkID());
-				evt.setS32("sliderValue", next);
-				this->postToListeners(evt);
-			}
-		}
-	}
-}
-
-#include "SkGradientShader.h"
-
-static void setgrad(SkPaint* paint, const SkRect& r)
-{
-	SkPoint	pts[2];
-	SkColor	colors[2];
-
-#if 0
-	pts[0].set(r.fLeft, r.fTop);
-	pts[1].set(r.fLeft + r.height(), r.fBottom);
-#else
-	pts[0].set(r.fRight, r.fBottom);
-	pts[1].set(r.fRight - r.height(), r.fTop);
-#endif
-	colors[0] = SK_ColorBLUE;
-	colors[1] = SK_ColorWHITE;
-
-	paint->setShader(SkGradientShader::CreateLinear(pts, colors, NULL, 2, SkShader::kMirror_TileMode))->unref();
-}
-
-void SkSliderView::onDraw(SkCanvas* canvas)
-{
-	this->INHERITED::onDraw(canvas);
-
-	U16CPU value = SkMax32(0, SkMin32(fValue, fMax));
-
-	SkRect	r;
-	SkPaint	p;
-
-	r.set(0, 0, this->width(), this->height());
-
-	p.setAntiAliasOn(true);
-	p.setStyle(SkPaint::kStroke_Style);
-	p.setStrokeWidth(SK_Scalar1);
-	r.inset(SK_Scalar1/2, SK_Scalar1/2);
-	canvas->drawRect(r, p);
-
-	if (fMax)
-	{
-		SkFixed percent = SkFixedDiv(value, fMax);
-		
-		r.inset(SK_Scalar1/2, SK_Scalar1/2);
-		r.fRight = r.fLeft + SkScalarMul(r.width(), SkFixedToScalar(percent));
-		p.setStyle(SkPaint::kFill_Style);
-		setgrad(&p, r);
-		canvas->drawRect(r, p);
-	}
-
-#if 0
-	r.set(0, 0, this->width(), this->height());
-	r.inset(SK_Scalar1, SK_Scalar1);
-	r.inset(r.width()/2, 0);
-	p.setColor(SK_ColorBLACK);
-	canvas->drawLine(*(SkPoint*)&r.fLeft, *(SkPoint*)&r.fRight, p);
-#endif
-}
-
-SkView::Click* SkSliderView::onFindClickHandler(SkScalar x, SkScalar y)
-{
-	return new Click(this);
-}
-
-bool SkSliderView::onClick(Click* click)
-{
-	if (fMax)
-	{
-		SkScalar percent = SkScalarDiv(click->fCurr.fX + SK_Scalar1, this->width() - SK_Scalar1*2);
-		percent = SkMaxScalar(0, SkMinScalar(percent, SK_Scalar1));
-		this->setValue(SkScalarRound(percent * fMax));
-		return true;
-	}
-	return false;
-}
-
-#endif
-
diff --git a/src/views/SkWidgetViews.cpp b/src/views/SkWidgetViews.cpp
deleted file mode 100644
index 2803d93..0000000
--- a/src/views/SkWidgetViews.cpp
+++ /dev/null
@@ -1,412 +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 "SkWidgetViews.h"
-#include "SkAnimator.h"
-#include "SkCanvas.h"
-#include "SkPaint.h"
-#include "SkStream.h"
-#include "SkSystemEventTypes.h"
-
-#ifdef SK_DEBUG
-	static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
-	{
-		const char* value = dom.findAttr(node, attr);
-		if (value)
-			SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
-	}
-#else
-	#define assert_no_attr(dom, node, attr)
-#endif
-/*
-I have moved this to SkWidgetViews.h
-enum SkinEnum {
-	kButton_SkinEnum,
-	kProgress_SkinEnum,
-	kScroll_SkinEnum,
-	kStaticText_SkinEnum,
-	
-	kSkinEnumCount
-};
-*/
-
-const char* get_skin_enum_path(SkinEnum se)
-{
-	SkASSERT((unsigned)se < kSkinEnumCount);
-
-	static const char* gSkinPaths[] = {
-            "common/default/default/skins/border3.xml",
-            "common/default/default/skins/button.xml",
-            "common/default/default/skins/progressBar.xml",
-            "common/default/default/skins/scrollBar.xml",
-            "common/default/default/skins/statictextpaint.xml"
-	};
-
-	return gSkinPaths[se];
-}
-
-void init_skin_anim(const char path[], SkAnimator* anim)
-{
-	SkASSERT(path && anim);
-
-	SkFILEStream	stream(path);
-
-	if (!stream.isValid())
-	{
-		SkDEBUGF(("init_skin_anim: loading skin failed <%s>\n", path));
-		sk_throw();
-	}
-
-	if (!anim->decodeStream(&stream))
-	{
-		SkDEBUGF(("init_skin_anim: decoding skin failed <%s>\n", path));
-		sk_throw();
-	}
-}
-
-void init_skin_anim(SkinEnum se, SkAnimator* anim)
-{
-	init_skin_anim(get_skin_enum_path(se), anim);
-}
-
-void init_skin_paint(SkinEnum se, SkPaint* paint)
-{
-	SkASSERT(paint);
-
-	SkAnimator	anim;
-	SkCanvas	canvas;
-	
-	init_skin_anim(se, &anim);
-	anim.draw(&canvas, paint, 0);
-}
-
-void inflate_paint(const SkDOM& dom, const SkDOM::Node* node, SkPaint* paint)
-{
-	SkASSERT(paint);
-
-	SkAnimator	anim;
-	SkCanvas	canvas;
-	
-	if (!anim.decodeDOM(dom, node))
-	{
-		SkDEBUGF(("inflate_paint: decoding dom failed\n"));
-		SkDEBUGCODE(dom.dump(node);)
-		sk_throw();
-	}	
-	anim.draw(&canvas, paint, 0);
-}
-
-////////////////////////////////////////////////////////////////////////////////////////
-
-SkWidgetView::SkWidgetView() : SkView(SkView::kFocusable_Mask | SkView::kEnabled_Mask)
-{
-}
-
-const char* SkWidgetView::getLabel() const
-{
-	return fLabel.c_str();
-}
-	
-void SkWidgetView::getLabel(SkString* label) const
-{
-	if (label)
-		*label = fLabel;
-}
-
-void SkWidgetView::setLabel(const char label[])
-{
-	this->setLabel(label, label ? strlen(label) : 0);
-}
-
-void SkWidgetView::setLabel(const char label[], size_t len)
-{
-	if ((label == NULL && fLabel.size() != 0) || !fLabel.equals(label, len))
-	{
-		SkString	tmp(label, len);
-
-		this->onLabelChange(fLabel.c_str(), tmp.c_str());
-		fLabel.swap(tmp);
-	}
-}
-
-void SkWidgetView::setLabel(const SkString& label)
-{
-	if (fLabel != label)
-	{
-		this->onLabelChange(fLabel.c_str(), label.c_str());
-		fLabel = label;
-	}
-}
-
-bool SkWidgetView::postWidgetEvent()
-{
-	if (!fEvent.isType(""))
-	{
-		SkEvent	evt(fEvent);	// make a copy since onPrepareWidgetEvent may edit the event
-
-		if (this->onPrepareWidgetEvent(&evt))
-		{
-			SkDEBUGCODE(evt.dump("SkWidgetView::postWidgetEvent");)
-
-			this->postToListeners(evt);	// wonder if this should return true if there are > 0 listeners...
-			return true;
-		}
-	}
-	return false;
-}
-
-/*virtual*/ void SkWidgetView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-
-	const char* label = dom.findAttr(node, "label");
-	if (label)
-		this->setLabel(label);
-		
-	if ((node = dom.getFirstChild(node, "event")) != NULL)
-		fEvent.inflate(dom, node);
-}
-
-/*virtual*/ void SkWidgetView::onLabelChange(const char oldLabel[], const char newLabel[])
-{
-	this->inval(NULL);
-}
-
-static const char gWidgetEventSinkIDSlotName[] = "sk-widget-sinkid-slot";
-
-/*virtual*/ bool SkWidgetView::onPrepareWidgetEvent(SkEvent* evt)
-{
-	evt->setS32(gWidgetEventSinkIDSlotName, this->getSinkID());
-	return true;
-}
-
-SkEventSinkID SkWidgetView::GetWidgetEventSinkID(const SkEvent& evt)
-{
-	int32_t	sinkID;
-	
-	return evt.findS32(gWidgetEventSinkIDSlotName, &sinkID) ? (SkEventSinkID)sinkID : 0;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-/*virtual*/ bool SkButtonView::onEvent(const SkEvent& evt)
-{
-	if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
-	{
-		this->postWidgetEvent();
-		return true;
-	}
-	return this->INHERITED::onEvent(evt);
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-SkCheckButtonView::SkCheckButtonView() : fCheckState(kOff_CheckState)
-{
-}
-
-void SkCheckButtonView::setCheckState(CheckState state)
-{
-	SkASSERT((unsigned)state <= kUnknown_CheckState);
-	
-	if (fCheckState != state)
-	{
-		this->onCheckStateChange(this->getCheckState(), state);
-		fCheckState = SkToU8(state);
-	}
-}
-	
-/*virtual*/ void SkCheckButtonView::onCheckStateChange(CheckState oldState, CheckState newState)
-{
-	this->inval(NULL);
-}
-
-/*virtual*/ void SkCheckButtonView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
-{
-	this->INHERITED::onInflate(dom, node);
-	
-	int index = dom.findList(node, "check-state", "off,on,unknown");
-	if (index >= 0)
-		this->setCheckState((CheckState)index);
-}
-
-static const char gCheckStateSlotName[] = "sk-checkbutton-check-slot";
-
-/*virtual*/ bool SkCheckButtonView::onPrepareWidgetEvent(SkEvent* evt)
-{
-	// could check if we're "disabled", and return false...
-
-	evt->setS32(gCheckStateSlotName, this->getCheckState());
-	return true;
-}
-
-bool SkCheckButtonView::GetWidgetEventCheckState(const SkEvent& evt, CheckState* state)
-{
-	int32_t	state32;
-	
-	if (evt.findS32(gCheckStateSlotName, &state32))
-	{
-		if (state)
-			*state = (CheckState)state32;
-		return true;
-	}
-	return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////////////////////////
-///////////////////////////////////////////////////////////////////////////////////////////////////
-
-#include "SkTime.h"
-#include <stdio.h>
-
-class SkAnimButtonView : public SkButtonView {
-public:
-	SkAnimButtonView()
-	{
-		fAnim.setHostEventSink(this);
-		init_skin_anim(kButton_SkinEnum, &fAnim);
-	}
-
-protected:
-	virtual void onLabelChange(const char oldLabel[], const char newLabel[])
-	{
-		this->INHERITED::onLabelChange(oldLabel, newLabel);
-
-		SkEvent evt("user");
-		evt.setString("id", "setLabel");
-		evt.setString("LABEL", newLabel);
-		fAnim.doUserEvent(evt);
-	}
-	
-	virtual void onFocusChange(bool gainFocus)
-	{
-		this->INHERITED::onFocusChange(gainFocus);
-
-		SkEvent evt("user");
-		evt.setString("id", "setFocus");
-		evt.setS32("FOCUS", gainFocus);
-		fAnim.doUserEvent(evt);
-	}
-
-	virtual void onSizeChange()
-	{
-		this->INHERITED::onSizeChange();
-
-		SkEvent evt("user");
-		evt.setString("id", "setDim");
-		evt.setScalar("dimX", this->width());
-		evt.setScalar("dimY", this->height());
-		fAnim.doUserEvent(evt);
-	}
-
-	virtual void onDraw(SkCanvas* canvas)
-	{
-		SkPaint						paint;		
-		SkAnimator::DifferenceType	diff = fAnim.draw(canvas, &paint, SkTime::GetMSecs());
-		
-		if (diff == SkAnimator::kDifferent)
-			this->inval(NULL);
-		else if (diff == SkAnimator::kPartiallyDifferent)
-		{
-			SkRect	bounds;
-			fAnim.getInvalBounds(&bounds);
-			this->inval(&bounds);
-		}
-	}
-	
-	virtual bool onEvent(const SkEvent& evt)
-	{
-		if (evt.isType(SK_EventType_Inval))
-		{
-			this->inval(NULL);
-			return true;
-		}
-		if (evt.isType("recommendDim"))
-		{
-			SkScalar	height;
-			
-			if (evt.findScalar("y", &height))
-				this->setHeight(height);
-			return true;
-		}
-		return this->INHERITED::onEvent(evt);
-	}
-	
-	virtual bool onPrepareWidgetEvent(SkEvent* evt)
-	{
-		if (this->INHERITED::onPrepareWidgetEvent(evt))
-		{
-			SkEvent	e("user");
-			e.setString("id", "handlePress");
-			(void)fAnim.doUserEvent(e);
-			return true;
-		}
-		return false;
-	}
-
-private:
-	SkAnimator	fAnim;
-	
-	typedef SkButtonView INHERITED;
-};
-
-////////////////////////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////////////////////////
-
-SkView* SkWidgetFactory(const char name[])
-{
-	if (name == NULL)
-		return NULL;
-	
-	// must be in the same order as the SkSkinWidgetEnum is declared
-	static const char* gNames[] = {
-		"sk-border",
-		"sk-button",
-		"sk-image",
-		"sk-list",
-		"sk-progress",
-		"sk-scroll",
-		"sk-text"
-		
-	};
-
-	for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); i++)
-		if (!strcmp(gNames[i], name))
-			return SkWidgetFactory((SkWidgetEnum)i);
-
-	return NULL;
-}
-
-#include "SkImageView.h"
-#include "SkProgressBarView.h"
-#include "SkScrollBarView.h"
-#include "SkBorderView.h"
-
-SkView* SkWidgetFactory(SkWidgetEnum sw)
-{
-	switch (sw) {
-	case kBorder_WidgetEnum:
-		return new SkBorderView;
-	case kButton_WidgetEnum:
-		return new SkAnimButtonView;
-	case kImage_WidgetEnum:
-		return new SkImageView;
-	case kList_WidgetEnum:
-		return new SkListView;
-	case kProgress_WidgetEnum:
-		return new SkProgressBarView;
-	case kScroll_WidgetEnum:
-		return new SkScrollBarView;
-	case kText_WidgetEnum:
-		return new SkStaticTextView;
-	default:
-		SkDEBUGFAIL("unknown enum passed to SkWidgetFactory");
-		break;
-	}
-	return NULL;
-}
diff --git a/src/views/SkWidgets.cpp b/src/views/SkWidgets.cpp
index 69d755b..202bf52 100644
--- a/src/views/SkWidgets.cpp
+++ b/src/views/SkWidgets.cpp
@@ -15,14 +15,14 @@
 #if 0
 
 #ifdef SK_DEBUG
-	static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
-	{
-		const char* value = dom.findAttr(node, attr);
-		if (value)
-			SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
-	}
+    static void assert_no_attr(const SkDOM& dom, const SkDOM::Node* node, const char attr[])
+    {
+        const char* value = dom.findAttr(node, attr);
+        if (value)
+            SkDebugf("unknown attribute %s=\"%s\"\n", attr, value);
+    }
 #else
-	#define assert_no_attr(dom, node, attr)
+    #define assert_no_attr(dom, node, attr)
 #endif
 
 #include "SkAnimator.h"
@@ -31,56 +31,56 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 enum SkinType {
-	kPushButton_SkinType,
-	kStaticText_SkinType,
+    kPushButton_SkinType,
+    kStaticText_SkinType,
 
-	kSkinTypeCount
+    kSkinTypeCount
 };
 
 struct SkinSuite {
-	SkinSuite();
-	~SkinSuite()
-	{
-		for (int i = 0; i < kSkinTypeCount; i++)
-			delete fAnimators[i];
-	}
+    SkinSuite();
+    ~SkinSuite()
+    {
+        for (int i = 0; i < kSkinTypeCount; i++)
+            delete fAnimators[i];
+    }
 
-	SkAnimator*	get(SkinType);
+    SkAnimator*    get(SkinType);
 
 private:
-	SkAnimator*	fAnimators[kSkinTypeCount];
+    SkAnimator*    fAnimators[kSkinTypeCount];
 };
 
 SkinSuite::SkinSuite()
 {
-	static const char kSkinPath[] = "skins/";
+    static const char kSkinPath[] = "skins/";
 
-	static const char* gSkinNames[] = {
-		"pushbutton_skin.xml",
-		"statictext_skin.xml"
-	};
+    static const char* gSkinNames[] = {
+        "pushbutton_skin.xml",
+        "statictext_skin.xml"
+    };
 
-	for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++)
-	{
-		size_t		len = strlen(gSkinNames[i]);
-		SkString	path(sizeof(kSkinPath) - 1 + len);
+    for (unsigned i = 0; i < SK_ARRAY_COUNT(gSkinNames); i++)
+    {
+        size_t        len = strlen(gSkinNames[i]);
+        SkString    path(sizeof(kSkinPath) - 1 + len);
 
-		memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1);
-		memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len);
+        memcpy(path.writable_str(), kSkinPath, sizeof(kSkinPath) - 1);
+        memcpy(path.writable_str() + sizeof(kSkinPath) - 1, gSkinNames[i], len);
 
-		fAnimators[i] = new SkAnimator;
-		if (!fAnimators[i]->decodeURI(path.c_str()))
-		{
-			delete fAnimators[i];
-			fAnimators[i] = NULL;
-		}
-	}
+        fAnimators[i] = new SkAnimator;
+        if (!fAnimators[i]->decodeURI(path.c_str()))
+        {
+            delete fAnimators[i];
+            fAnimators[i] = NULL;
+        }
+    }
 }
 
 SkAnimator* SkinSuite::get(SkinType st)
 {
-	SkASSERT((unsigned)st < kSkinTypeCount);
-	return fAnimators[st];
+    SkASSERT((unsigned)st < kSkinTypeCount);
+    return fAnimators[st];
 }
 
 static SkinSuite* gSkinSuite;
@@ -88,11 +88,11 @@
 static SkAnimator* get_skin_animator(SkinType st)
 {
 #if 0
-	if (gSkinSuite == NULL)
-		gSkinSuite = new SkinSuite;
-	return gSkinSuite->get(st);
+    if (gSkinSuite == NULL)
+        gSkinSuite = new SkinSuite;
+    return gSkinSuite->get(st);
 #else
-	return NULL;
+    return NULL;
 #endif
 }
 
@@ -104,134 +104,134 @@
 
 void SkWidget::Term()
 {
-	delete gSkinSuite;
+    delete gSkinSuite;
 }
 
 void SkWidget::onEnabledChange()
 {
-	this->inval(NULL);
+    this->inval(NULL);
 }
 
 void SkWidget::postWidgetEvent()
 {
-	if (!fEvent.isType("") && this->hasListeners())
-	{
-		this->prepareWidgetEvent(&fEvent);
-		this->postToListeners(fEvent);
-	}
+    if (!fEvent.isType("") && this->hasListeners())
+    {
+        this->prepareWidgetEvent(&fEvent);
+        this->postToListeners(fEvent);
+    }
 }
 
 void SkWidget::prepareWidgetEvent(SkEvent*)
 {
-	// override in subclass to add any additional fields before posting
+    // override in subclass to add any additional fields before posting
 }
 
 void SkWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->INHERITED::onInflate(dom, node);
+    this->INHERITED::onInflate(dom, node);
 
-	if ((node = dom.getFirstChild(node, "event")) != NULL)
-		fEvent.inflate(dom, node);
+    if ((node = dom.getFirstChild(node, "event")) != NULL)
+        fEvent.inflate(dom, node);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 size_t SkHasLabelWidget::getLabel(SkString* str) const
 {
-	if (str)
-		*str = fLabel;
-	return fLabel.size();
+    if (str)
+        *str = fLabel;
+    return fLabel.size();
 }
 
 size_t SkHasLabelWidget::getLabel(char buffer[]) const
 {
-	if (buffer)
-		memcpy(buffer, fLabel.c_str(), fLabel.size());
-	return fLabel.size();
+    if (buffer)
+        memcpy(buffer, fLabel.c_str(), fLabel.size());
+    return fLabel.size();
 }
 
 void SkHasLabelWidget::setLabel(const SkString& str)
 {
-	this->setLabel(str.c_str(), str.size());
+    this->setLabel(str.c_str(), str.size());
 }
 
 void SkHasLabelWidget::setLabel(const char label[])
 {
-	this->setLabel(label, strlen(label));
+    this->setLabel(label, strlen(label));
 }
 
 void SkHasLabelWidget::setLabel(const char label[], size_t len)
 {
-	if (!fLabel.equals(label, len))
-	{
-		fLabel.set(label, len);
-		this->onLabelChange();
-	}
+    if (!fLabel.equals(label, len))
+    {
+        fLabel.set(label, len);
+        this->onLabelChange();
+    }
 }
 
 void SkHasLabelWidget::onLabelChange()
 {
-	// override in subclass
+    // override in subclass
 }
 
 void SkHasLabelWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->INHERITED::onInflate(dom, node);
+    this->INHERITED::onInflate(dom, node);
 
-	const char* text = dom.findAttr(node, "label");
-	if (text)
-		this->setLabel(text);
+    const char* text = dom.findAttr(node, "label");
+    if (text)
+        this->setLabel(text);
 }
 
 /////////////////////////////////////////////////////////////////////////////////////
 
 void SkButtonWidget::setButtonState(State state)
 {
-	if (fState != state)
-	{
-		fState = state;
-		this->onButtonStateChange();
-	}
+    if (fState != state)
+    {
+        fState = state;
+        this->onButtonStateChange();
+    }
 }
 
 void SkButtonWidget::onButtonStateChange()
 {
-	this->inval(NULL);
+    this->inval(NULL);
 }
 
 void SkButtonWidget::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->INHERITED::onInflate(dom, node);
+    this->INHERITED::onInflate(dom, node);
 
-	int	index;
-	if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0)
-		this->setButtonState((State)index);
+    int    index;
+    if ((index = dom.findList(node, "buttonState", "off,on,unknown")) >= 0)
+        this->setButtonState((State)index);
 }
 
 /////////////////////////////////////////////////////////////////////////////////////
 
 bool SkPushButtonWidget::onEvent(const SkEvent& evt)
 {
-	if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
-	{
-		this->postWidgetEvent();
-		return true;
-	}
-	return this->INHERITED::onEvent(evt);
+    if (evt.isType(SK_EventType_Key) && evt.getFast32() == kOK_SkKey)
+    {
+        this->postWidgetEvent();
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
 }
 
 static const char* computeAnimatorState(int enabled, int focused, SkButtonWidget::State state)
 {
-	if (!enabled)
-		return "disabled";
-	if (state == SkButtonWidget::kOn_State)
-	{
-		SkASSERT(focused);
-		return "enabled-pressed";
-	}
-	if (focused)
-		return "enabled-focused";
-	return "enabled";
+    if (!enabled)
+        return "disabled";
+    if (state == SkButtonWidget::kOn_State)
+    {
+        SkASSERT(focused);
+        return "enabled-pressed";
+    }
+    if (focused)
+        return "enabled-focused";
+    return "enabled";
 }
 
 #include "SkBlurMaskFilter.h"
@@ -239,106 +239,106 @@
 
 static void create_emboss(SkPaint* paint, SkScalar radius, bool focus, bool pressed)
 {
-	SkEmbossMaskFilter::Light	light;
+    SkEmbossMaskFilter::Light    light;
 
-	light.fDirection[0] = SK_Scalar1/2;
-	light.fDirection[1] = SK_Scalar1/2;
-	light.fDirection[2] = SK_Scalar1/3;
-	light.fAmbient		= 0x48;
-	light.fSpecular		= 0x80;
+    light.fDirection[0] = SK_Scalar1/2;
+    light.fDirection[1] = SK_Scalar1/2;
+    light.fDirection[2] = SK_Scalar1/3;
+    light.fAmbient        = 0x48;
+    light.fSpecular        = 0x80;
 
-	if (pressed)
-	{
-		light.fDirection[0] = -light.fDirection[0];
-		light.fDirection[1] = -light.fDirection[1];
-	}
-	if (focus)
-		light.fDirection[2] += SK_Scalar1/4;
+    if (pressed)
+    {
+        light.fDirection[0] = -light.fDirection[0];
+        light.fDirection[1] = -light.fDirection[1];
+    }
+    if (focus)
+        light.fDirection[2] += SK_Scalar1/4;
 
-	paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref();
+    paint->setMaskFilter(new SkEmbossMaskFilter(light, radius))->unref();
 }
 
 void SkPushButtonWidget::onDraw(SkCanvas* canvas)
 {
-	this->INHERITED::onDraw(canvas);
+    this->INHERITED::onDraw(canvas);
 
-	SkString label;
-	this->getLabel(&label);
+    SkString label;
+    this->getLabel(&label);
 
-	SkAnimator* anim = get_skin_animator(kPushButton_SkinType);
+    SkAnimator* anim = get_skin_animator(kPushButton_SkinType);
 
-	if (anim)
-	{
-		SkEvent	evt("user");
+    if (anim)
+    {
+        SkEvent    evt("user");
 
-		evt.setString("id", "prime");
-		evt.setScalar("prime-width", this->width());
-		evt.setScalar("prime-height", this->height());
-		evt.setString("prime-text", label);
-		evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState()));
+        evt.setString("id", "prime");
+        evt.setScalar("prime-width", this->width());
+        evt.setScalar("prime-height", this->height());
+        evt.setString("prime-text", label);
+        evt.setString("prime-state", computeAnimatorState(this->isEnabled(), this->hasFocus(), this->getButtonState()));
 
-		(void)anim->doUserEvent(evt);
-		SkPaint paint;
-		anim->draw(canvas, &paint, SkTime::GetMSecs());
-	}
-	else
-	{
-		SkRect	r;
-		SkPaint	p;
+        (void)anim->doUserEvent(evt);
+        SkPaint paint;
+        anim->draw(canvas, &paint, SkTime::GetMSecs());
+    }
+    else
+    {
+        SkRect    r;
+        SkPaint    p;
 
-		r.set(0, 0, this->width(), this->height());
-		p.setAntiAliasOn(true);
-		p.setColor(SK_ColorBLUE);
-		create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State);
-		canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p);
-		p.setMaskFilter(NULL);
+        r.set(0, 0, this->width(), this->height());
+        p.setAntiAliasOn(true);
+        p.setColor(SK_ColorBLUE);
+        create_emboss(&p, SkIntToScalar(12)/5, this->hasFocus(), this->getButtonState() == kOn_State);
+        canvas->drawRoundRect(r, SkScalarHalf(this->height()), SkScalarHalf(this->height()), p);
+        p.setMaskFilter(NULL);
 
-		p.setTextAlign(SkPaint::kCenter_Align);
+        p.setTextAlign(SkPaint::kCenter_Align);
 
-		SkTextBox	box;
-		box.setMode(SkTextBox::kOneLine_Mode);
-		box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign);
-		box.setBox(0, 0, this->width(), this->height());
+        SkTextBox    box;
+        box.setMode(SkTextBox::kOneLine_Mode);
+        box.setSpacingAlign(SkTextBox::kCenter_SpacingAlign);
+        box.setBox(0, 0, this->width(), this->height());
 
-//		if (this->getButtonState() == kOn_State)
-//			p.setColor(SK_ColorRED);
-//		else
-			p.setColor(SK_ColorWHITE);
+//        if (this->getButtonState() == kOn_State)
+//            p.setColor(SK_ColorRED);
+//        else
+            p.setColor(SK_ColorWHITE);
 
-		box.draw(canvas, label.c_str(), label.size(), p);
-	}
+        box.draw(canvas, label.c_str(), label.size(), p);
+    }
 }
 
 SkView::Click* SkPushButtonWidget::onFindClickHandler(SkScalar x, SkScalar y)
 {
-	this->acceptFocus();
-	return new Click(this);
+    this->acceptFocus();
+    return new Click(this);
 }
 
 bool SkPushButtonWidget::onClick(Click* click)
 {
-	SkRect	r;
-	State	state = kOff_State;
+    SkRect    r;
+    State    state = kOff_State;
 
-	this->getLocalBounds(&r);
-	if (r.contains(click->fCurr))
-	{
-		if (click->fState == Click::kUp_State)
-			this->postWidgetEvent();
-		else
-			state = kOn_State;
-	}
-	this->setButtonState(state);
-	return true;
+    this->getLocalBounds(&r);
+    if (r.contains(click->fCurr))
+    {
+        if (click->fState == Click::kUp_State)
+            this->postWidgetEvent();
+        else
+            state = kOn_State;
+    }
+    this->setButtonState(state);
+    return true;
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 
 SkStaticTextView::SkStaticTextView(U32 flags) : SkView(flags)
 {
-	fMargin.set(0, 0);
-	fMode = kFixedSize_Mode;
-	fSpacingAlign = SkTextBox::kStart_SpacingAlign;
+    fMargin.set(0, 0);
+    fMode = kFixedSize_Mode;
+    fSpacingAlign = SkTextBox::kStart_SpacingAlign;
 }
 
 SkStaticTextView::~SkStaticTextView()
@@ -347,148 +347,148 @@
 
 void SkStaticTextView::computeSize()
 {
-	if (fMode == kAutoWidth_Mode)
-	{
-		SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), NULL, NULL);
-		this->setWidth(width + fMargin.fX * 2);
-	}
-	else if (fMode == kAutoHeight_Mode)
-	{
-		SkScalar width = this->width() - fMargin.fX * 2;
-		int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
+    if (fMode == kAutoWidth_Mode)
+    {
+        SkScalar width = fPaint.measureText(fText.c_str(), fText.size(), NULL, NULL);
+        this->setWidth(width + fMargin.fX * 2);
+    }
+    else if (fMode == kAutoHeight_Mode)
+    {
+        SkScalar width = this->width() - fMargin.fX * 2;
+        int lines = width > 0 ? SkTextLineBreaker::CountLines(fText.c_str(), fText.size(), fPaint, width) : 0;
 
-		SkScalar	before, after;
-		(void)fPaint.measureText(0, NULL, &before, &after);
+        SkScalar    before, after;
+        (void)fPaint.measureText(0, NULL, &before, &after);
 
-		this->setHeight(lines * (after - before) + fMargin.fY * 2);
-	}
+        this->setHeight(lines * (after - before) + fMargin.fY * 2);
+    }
 }
 
 void SkStaticTextView::setMode(Mode mode)
 {
-	SkASSERT((unsigned)mode < kModeCount);
+    SkASSERT((unsigned)mode < kModeCount);
 
-	if (fMode != mode)
-	{
-		fMode = SkToU8(mode);
-		this->computeSize();
-	}
+    if (fMode != mode)
+    {
+        fMode = SkToU8(mode);
+        this->computeSize();
+    }
 }
 
 void SkStaticTextView::setSpacingAlign(SkTextBox::SpacingAlign align)
 {
-	fSpacingAlign = SkToU8(align);
-	this->inval(NULL);
+    fSpacingAlign = SkToU8(align);
+    this->inval(NULL);
 }
 
 void SkStaticTextView::getMargin(SkPoint* margin) const
 {
-	if (margin)
-		*margin = fMargin;
+    if (margin)
+        *margin = fMargin;
 }
 
 void SkStaticTextView::setMargin(SkScalar dx, SkScalar dy)
 {
-	if (fMargin.fX != dx || fMargin.fY != dy)
-	{
-		fMargin.set(dx, dy);
-		this->computeSize();
-		this->inval(NULL);
-	}
+    if (fMargin.fX != dx || fMargin.fY != dy)
+    {
+        fMargin.set(dx, dy);
+        this->computeSize();
+        this->inval(NULL);
+    }
 }
 
 size_t SkStaticTextView::getText(SkString* text) const
 {
-	if (text)
-		*text = fText;
-	return fText.size();
+    if (text)
+        *text = fText;
+    return fText.size();
 }
 
 size_t SkStaticTextView::getText(char text[]) const
 {
-	if (text)
-		memcpy(text, fText.c_str(), fText.size());
-	return fText.size();
+    if (text)
+        memcpy(text, fText.c_str(), fText.size());
+    return fText.size();
 }
 
 void SkStaticTextView::setText(const SkString& text)
 {
-	this->setText(text.c_str(), text.size());
+    this->setText(text.c_str(), text.size());
 }
 
 void SkStaticTextView::setText(const char text[])
 {
-	this->setText(text, strlen(text));
+    this->setText(text, strlen(text));
 }
 
 void SkStaticTextView::setText(const char text[], size_t len)
 {
-	if (!fText.equals(text, len))
-	{
-		fText.set(text, len);
-		this->computeSize();
-		this->inval(NULL);
-	}
+    if (!fText.equals(text, len))
+    {
+        fText.set(text, len);
+        this->computeSize();
+        this->inval(NULL);
+    }
 }
 
 void SkStaticTextView::getPaint(SkPaint* paint) const
 {
-	if (paint)
-		*paint = fPaint;
+    if (paint)
+        *paint = fPaint;
 }
 
 void SkStaticTextView::setPaint(const SkPaint& paint)
 {
-	if (fPaint != paint)
-	{
-		fPaint = paint;
-		this->computeSize();
-		this->inval(NULL);
-	}
+    if (fPaint != paint)
+    {
+        fPaint = paint;
+        this->computeSize();
+        this->inval(NULL);
+    }
 }
 
 void SkStaticTextView::onDraw(SkCanvas* canvas)
 {
-	this->INHERITED::onDraw(canvas);
+    this->INHERITED::onDraw(canvas);
 
-	if (fText.isEmpty())
-		return;
+    if (fText.isEmpty())
+        return;
 
-	SkTextBox	box;
+    SkTextBox    box;
 
-	box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
-	box.setSpacingAlign(this->getSpacingAlign());
-	box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
-	box.draw(canvas, fText.c_str(), fText.size(), fPaint);
+    box.setMode(fMode == kAutoWidth_Mode ? SkTextBox::kOneLine_Mode : SkTextBox::kLineBreak_Mode);
+    box.setSpacingAlign(this->getSpacingAlign());
+    box.setBox(fMargin.fX, fMargin.fY, this->width() - fMargin.fX, this->height() - fMargin.fY);
+    box.draw(canvas, fText.c_str(), fText.size(), fPaint);
 }
 
 void SkStaticTextView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->INHERITED::onInflate(dom, node);
+    this->INHERITED::onInflate(dom, node);
 
-	int	index;
-	if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
-		this->setMode((Mode)index);
-	else
-		assert_no_attr(dom, node, "mode");
+    int    index;
+    if ((index = dom.findList(node, "mode", "fixed,auto-width,auto-height")) >= 0)
+        this->setMode((Mode)index);
+    else
+        assert_no_attr(dom, node, "mode");
 
-	if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
-		this->setSpacingAlign((SkTextBox::SpacingAlign)index);
-	else
-		assert_no_attr(dom, node, "mode");
+    if ((index = dom.findList(node, "spacing-align", "start,center,end")) >= 0)
+        this->setSpacingAlign((SkTextBox::SpacingAlign)index);
+    else
+        assert_no_attr(dom, node, "mode");
 
-	SkScalar s[2];
-	if (dom.findScalars(node, "margin", s, 2))
-		this->setMargin(s[0], s[1]);
-	else
-		assert_no_attr(dom, node, "margin");
+    SkScalar s[2];
+    if (dom.findScalars(node, "margin", s, 2))
+        this->setMargin(s[0], s[1]);
+    else
+        assert_no_attr(dom, node, "margin");
 
-	const char* text = dom.findAttr(node, "text");
-	if (text)
-		this->setText(text);
+    const char* text = dom.findAttr(node, "text");
+    if (text)
+        this->setText(text);
 
-	if ((node = dom.getFirstChild(node, "paint")) != NULL)
-		SkPaint_Inflate(&fPaint, dom, node);
+    if ((node = dom.getFirstChild(node, "paint")) != NULL)
+        SkPaint_Inflate(&fPaint, dom, node);
 }
 
 /////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -505,57 +505,56 @@
 
 bool SkBitmapView::getBitmap(SkBitmap* bitmap) const
 {
-	if (bitmap)
-		*bitmap = fBitmap;
-	return fBitmap.getConfig() != SkBitmap::kNo_Config;
+    if (bitmap)
+        *bitmap = fBitmap;
+    return fBitmap.getConfig() != SkBitmap::kNo_Config;
 }
 
 void SkBitmapView::setBitmap(const SkBitmap* bitmap, bool viewOwnsPixels)
 {
-	if (bitmap)
-	{
-		fBitmap = *bitmap;
-		fBitmap.setOwnsPixels(viewOwnsPixels);
-	}
+    if (bitmap)
+    {
+        fBitmap = *bitmap;
+        fBitmap.setOwnsPixels(viewOwnsPixels);
+    }
 }
 
 bool SkBitmapView::loadBitmapFromFile(const char path[])
 {
-	SkBitmap	bitmap;
+    SkBitmap    bitmap;
 
-	if (SkImageDecoder::DecodeFile(path, &bitmap))
-	{
-		this->setBitmap(&bitmap, true);
-		bitmap.setOwnsPixels(false);
-		return true;
-	}
-	return false;
+    if (SkImageDecoder::DecodeFile(path, &bitmap))
+    {
+        this->setBitmap(&bitmap, true);
+        bitmap.setOwnsPixels(false);
+        return true;
+    }
+    return false;
 }
 
 void SkBitmapView::onDraw(SkCanvas* canvas)
 {
-	if (fBitmap.getConfig() != SkBitmap::kNo_Config &&
-		fBitmap.width() && fBitmap.height())
-	{
-		SkAutoCanvasRestore	restore(canvas, true);
-		SkPaint				p;
+    if (fBitmap.getConfig() != SkBitmap::kNo_Config &&
+        fBitmap.width() && fBitmap.height())
+    {
+        SkAutoCanvasRestore    restore(canvas, true);
+        SkPaint                p;
 
-		p.setFilterType(SkPaint::kBilinear_FilterType);
-		canvas->scale(	this->width() / fBitmap.width(),
-						this->height() / fBitmap.height(),
-						0, 0);
-		canvas->drawBitmap(fBitmap, 0, 0, p);
-	}
+        p.setFilterType(SkPaint::kBilinear_FilterType);
+        canvas->scale(    this->width() / fBitmap.width(),
+                        this->height() / fBitmap.height(),
+                        0, 0);
+        canvas->drawBitmap(fBitmap, 0, 0, p);
+    }
 }
 
 void SkBitmapView::onInflate(const SkDOM& dom, const SkDOM::Node* node)
 {
-	this->INHERITED::onInflate(dom, node);
+    this->INHERITED::onInflate(dom, node);
 
-	const char* src = dom.findAttr(node, "src");
-	if (src)
-		(void)this->loadBitmapFromFile(src);
+    const char* src = dom.findAttr(node, "src");
+    if (src)
+        (void)this->loadBitmapFromFile(src);
 }
 
 #endif
-
diff --git a/src/views/SkWindow.cpp b/src/views/SkWindow.cpp
index c952a61..7331976 100644
--- a/src/views/SkWindow.cpp
+++ b/src/views/SkWindow.cpp
@@ -19,31 +19,31 @@
 #include "SkBounder.h"
 class test_bounder : public SkBounder {
 public:
-	test_bounder(const SkBitmap& bm) : fCanvas(bm) {}
+    test_bounder(const SkBitmap& bm) : fCanvas(bm) {}
 protected:
-	virtual bool onIRect(const SkIRect& r)
-	{
-		SkRect	rr;
+    virtual bool onIRect(const SkIRect& r)
+    {
+        SkRect    rr;
 
-		rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop),
-				SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom));
+        rr.set(SkIntToScalar(r.fLeft), SkIntToScalar(r.fTop),
+                SkIntToScalar(r.fRight), SkIntToScalar(r.fBottom));
 
-		SkPaint	p;
+        SkPaint    p;
 
-		p.setStyle(SkPaint::kStroke_Style);
-		p.setColor(SK_ColorYELLOW);
+        p.setStyle(SkPaint::kStroke_Style);
+        p.setColor(SK_ColorYELLOW);
 
 #if 0
-		rr.inset(SK_ScalarHalf, SK_ScalarHalf);
+        rr.inset(SK_ScalarHalf, SK_ScalarHalf);
 #else
-		rr.inset(-SK_ScalarHalf, -SK_ScalarHalf);
+        rr.inset(-SK_ScalarHalf, -SK_ScalarHalf);
 #endif
 
-		fCanvas.drawRect(rr, p);
-		return true;
-	}
+        fCanvas.drawRect(rr, p);
+        return true;
+    }
 private:
-	SkCanvas	fCanvas;
+    SkCanvas    fCanvas;
 };
 
 SkWindow::SkWindow() : fFocusView(NULL)
@@ -66,6 +66,10 @@
     fMenus.deleteAll();
 }
 
+SkCanvas* SkWindow::createCanvas() {
+    return new SkCanvas(this->getBitmap());
+}
+
 void SkWindow::setMatrix(const SkMatrix& matrix) {
     if (fMatrix != matrix) {
         fMatrix = matrix;
@@ -87,39 +91,39 @@
 
 void SkWindow::setConfig(SkBitmap::Config config)
 {
-	this->resize(fBitmap.width(), fBitmap.height(), config);
+    this->resize(fBitmap.width(), fBitmap.height(), config);
 }
 
 void SkWindow::resize(int width, int height, SkBitmap::Config config)
 {
-	if (config == SkBitmap::kNo_Config)
-		config = fConfig;
+    if (config == SkBitmap::kNo_Config)
+        config = fConfig;
 
-	if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig)
-	{
-		fConfig = config;
-		fBitmap.setConfig(config, width, height);
-		fBitmap.allocPixels();
+    if (width != fBitmap.width() || height != fBitmap.height() || config != fConfig)
+    {
+        fConfig = config;
+        fBitmap.setConfig(config, width, height);
+        fBitmap.allocPixels();
         fBitmap.setIsOpaque(true);
 
-		this->setSize(SkIntToScalar(width), SkIntToScalar(height));
-		this->inval(NULL);
-	}
+        this->setSize(SkIntToScalar(width), SkIntToScalar(height));
+        this->inval(NULL);
+    }
 }
 
 void SkWindow::eraseARGB(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
 {
-	fBitmap.eraseARGB(a, r, g, b);
+    fBitmap.eraseARGB(a, r, g, b);
 }
 
 void SkWindow::eraseRGB(U8CPU r, U8CPU g, U8CPU b)
 {
-	fBitmap.eraseRGB(r, g, b);
+    fBitmap.eraseRGB(r, g, b);
 }
 
 bool SkWindow::handleInval(const SkRect* localR)
 {
-	SkIRect	ir;
+    SkIRect    ir;
 
     if (localR) {
         SkRect devR;
@@ -131,13 +135,13 @@
         devR.round(&ir);
     } else {
         ir.set(0, 0,
-			   SkScalarRound(this->width()),
-			   SkScalarRound(this->height()));
+               SkScalarRound(this->width()),
+               SkScalarRound(this->height()));
     }
-	fDirtyRgn.op(ir, SkRegion::kUnion_Op);
+    fDirtyRgn.op(ir, SkRegion::kUnion_Op);
 
-	this->onHandleInval(ir);
-	return true;
+    this->onHandleInval(ir);
+    return true;
 }
 
 void SkWindow::forceInvalAll() {
@@ -147,139 +151,134 @@
 }
 
 #if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
-	#include <windows.h>
-	#include <gx.h>
-	extern GXDisplayProperties gDisplayProps;
+    #include <windows.h>
+    #include <gx.h>
+    extern GXDisplayProperties gDisplayProps;
 #endif
 
 #ifdef SK_SIMULATE_FAILED_MALLOC
 extern bool gEnableControlledThrow;
 #endif
 
-bool SkWindow::update(SkIRect* updateArea, SkCanvas* canvas)
+bool SkWindow::update(SkIRect* updateArea)
 {
-	if (!fDirtyRgn.isEmpty())
-	{
-		SkBitmap bm = this->getBitmap();
+    if (!fDirtyRgn.isEmpty())
+    {
+        SkBitmap bm = this->getBitmap();
 
 #if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
-		char* buffer = (char*)GXBeginDraw();
-		SkASSERT(buffer);
+        char* buffer = (char*)GXBeginDraw();
+        SkASSERT(buffer);
 
-		RECT	rect;
-		GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
-		buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
+        RECT    rect;
+        GetWindowRect((HWND)((SkOSWindow*)this)->getHWND(), &rect);
+        buffer += rect.top * gDisplayProps.cbyPitch + rect.left * gDisplayProps.cbxPitch;
 
-		bm.setPixels(buffer);
+        bm.setPixels(buffer);
 #endif
 
-		SkCanvas	rasterCanvas;
+        SkAutoTUnref<SkCanvas> canvas(this->createCanvas());
 
-        if (NULL == canvas) {
-            canvas = &rasterCanvas;
-        }
-        canvas->setBitmapDevice(bm);
-
-		canvas->clipRegion(fDirtyRgn);
-		if (updateArea)
-			*updateArea = fDirtyRgn.getBounds();
+        canvas->clipRegion(fDirtyRgn);
+        if (updateArea)
+            *updateArea = fDirtyRgn.getBounds();
 
         SkAutoCanvasRestore acr(canvas, true);
         canvas->concat(fMatrix);
 
-		// empty this now, so we can correctly record any inval calls that
-		// might be made during the draw call.
-		fDirtyRgn.setEmpty();
+        // empty this now, so we can correctly record any inval calls that
+        // might be made during the draw call.
+        fDirtyRgn.setEmpty();
 
 #ifdef TEST_BOUNDER
-		test_bounder	b(bm);
-		canvas->setBounder(&b);
+        test_bounder    b(bm);
+        canvas->setBounder(&b);
 #endif
 #ifdef SK_SIMULATE_FAILED_MALLOC
-		gEnableControlledThrow = true;
+        gEnableControlledThrow = true;
 #endif
 #ifdef SK_BUILD_FOR_WIN32
-		//try {
-			this->draw(canvas);
-		//}
-		//catch (...) {
-		//}
+        //try {
+            this->draw(canvas);
+        //}
+        //catch (...) {
+        //}
 #else
-		this->draw(canvas);
+        this->draw(canvas);
 #endif
 #ifdef SK_SIMULATE_FAILED_MALLOC
-		gEnableControlledThrow = false;
+        gEnableControlledThrow = false;
 #endif
 #ifdef TEST_BOUNDER
-		canvas->setBounder(NULL);
+        canvas->setBounder(NULL);
 #endif
 
 #if defined(SK_BUILD_FOR_WINCE) && defined(USE_GX_SCREEN)
-		GXEndDraw();
+        GXEndDraw();
 #endif
 
-		return true;
-	}
-	return false;
+        return true;
+    }
+    return false;
 }
 
 bool SkWindow::handleChar(SkUnichar uni)
 {
-	if (this->onHandleChar(uni))
-		return true;
+    if (this->onHandleChar(uni))
+        return true;
 
-	SkView* focus = this->getFocusView();
-	if (focus == NULL)
-		focus = this;
+    SkView* focus = this->getFocusView();
+    if (focus == NULL)
+        focus = this;
 
-	SkEvent evt(SK_EventType_Unichar);
-	evt.setFast32(uni);
-	return focus->doEvent(evt);
+    SkEvent evt(SK_EventType_Unichar);
+    evt.setFast32(uni);
+    return focus->doEvent(evt);
 }
 
 bool SkWindow::handleKey(SkKey key)
 {
-	if (key == kNONE_SkKey)
-		return false;
+    if (key == kNONE_SkKey)
+        return false;
 
-	if (this->onHandleKey(key))
-		return true;
+    if (this->onHandleKey(key))
+        return true;
 
-	// send an event to the focus-view
-	{
-		SkView* focus = this->getFocusView();
-		if (focus == NULL)
-			focus = this;
+    // send an event to the focus-view
+    {
+        SkView* focus = this->getFocusView();
+        if (focus == NULL)
+            focus = this;
 
-		SkEvent evt(SK_EventType_Key);
-		evt.setFast32(key);
-		if (focus->doEvent(evt))
-			return true;
-	}
+        SkEvent evt(SK_EventType_Key);
+        evt.setFast32(key);
+        if (focus->doEvent(evt))
+            return true;
+    }
 
-	if (key == kUp_SkKey || key == kDown_SkKey)
-	{
-		if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL)
-			this->onSetFocusView(NULL);
-		return true;
-	}
-	return false;
+    if (key == kUp_SkKey || key == kDown_SkKey)
+    {
+        if (this->moveFocus(key == kUp_SkKey ? kPrev_FocusDirection : kNext_FocusDirection) == NULL)
+            this->onSetFocusView(NULL);
+        return true;
+    }
+    return false;
 }
 
 bool SkWindow::handleKeyUp(SkKey key)
 {
     if (key == kNONE_SkKey)
         return false;
-        
+
     if (this->onHandleKeyUp(key))
         return true;
-    
+
     //send an event to the focus-view
     {
         SkView* focus = this->getFocusView();
         if (focus == NULL)
             focus = this;
-            
+
         //should this one be the same?
         SkEvent evt(SK_EventType_KeyUp);
         evt.setFast32(key);
@@ -290,8 +289,8 @@
 }
 
 void SkWindow::addMenu(SkOSMenu* menu) {
-	*fMenus.append() = menu;
-	this->onAddMenu(menu);
+    *fMenus.append() = menu;
+    this->onAddMenu(menu);
 }
 
 void SkWindow::setTitle(const char title[]) {
@@ -306,36 +305,36 @@
 
 bool SkWindow::onEvent(const SkEvent& evt)
 {
-	if (evt.isType(SK_EventDelayInval))
-	{
-		SkRegion::Iterator	iter(fDirtyRgn);
+    if (evt.isType(SK_EventDelayInval))
+    {
+        SkRegion::Iterator    iter(fDirtyRgn);
 
-		for (; !iter.done(); iter.next())
-			this->onHandleInval(iter.rect());
-		fWaitingOnInval = false;
-		return true;
-	}
-	return this->INHERITED::onEvent(evt);
+        for (; !iter.done(); iter.next())
+            this->onHandleInval(iter.rect());
+        fWaitingOnInval = false;
+        return true;
+    }
+    return this->INHERITED::onEvent(evt);
 }
 
 bool SkWindow::onGetFocusView(SkView** focus) const
 {
-	if (focus)
-		*focus = fFocusView;
-	return true;
+    if (focus)
+        *focus = fFocusView;
+    return true;
 }
 
 bool SkWindow::onSetFocusView(SkView* focus)
 {
-	if (fFocusView != focus)
-	{
-		if (fFocusView)
-			fFocusView->onFocusChange(false);
-		fFocusView = focus;
-		if (focus)
-			focus->onFocusChange(true);
-	}
-	return true;
+    if (fFocusView != focus)
+    {
+        if (fFocusView)
+            fFocusView->onFocusChange(false);
+        fFocusView = focus;
+        if (focus)
+            focus->onFocusChange(true);
+    }
+    return true;
 }
 
 //////////////////////////////////////////////////////////////////////
@@ -346,12 +345,12 @@
 
 bool SkWindow::onHandleChar(SkUnichar)
 {
-	return false;
+    return false;
 }
 
 bool SkWindow::onHandleKey(SkKey key)
 {
-	return false;
+    return false;
 }
 
 bool SkWindow::onHandleKeyUp(SkKey key)
@@ -359,13 +358,14 @@
     return false;
 }
 
-bool SkWindow::handleClick(int x, int y, Click::State state, void *owner) {
-    return this->onDispatchClick(x, y, state, owner);
+bool SkWindow::handleClick(int x, int y, Click::State state, void *owner,
+                           unsigned modifierKeys) {
+    return this->onDispatchClick(x, y, state, owner, modifierKeys);
 }
 
 bool SkWindow::onDispatchClick(int x, int y, Click::State state,
-        void* owner) {
-	bool handled = false;
+                               void* owner, unsigned modifierKeys) {
+    bool handled = false;
 
     // First, attempt to find an existing click with this owner.
     int index = -1;
@@ -376,32 +376,32 @@
         }
     }
 
-	switch (state) {
+    switch (state) {
         case Click::kDown_State: {
             if (index != -1) {
                 delete fClicks[index];
                 fClicks.remove(index);
             }
             Click* click = this->findClickHandler(SkIntToScalar(x),
-                    SkIntToScalar(y));
+                                                  SkIntToScalar(y), modifierKeys);
 
             if (click) {
                 click->fOwner = owner;
                 *fClicks.append() = click;
-                SkView::DoClickDown(click, x, y);
+                SkView::DoClickDown(click, x, y, modifierKeys);
                 handled = true;
             }
             break;
         }
         case Click::kMoved_State:
             if (index != -1) {
-                SkView::DoClickMoved(fClicks[index], x, y);
+                SkView::DoClickMoved(fClicks[index], x, y, modifierKeys);
                 handled = true;
             }
             break;
         case Click::kUp_State:
             if (index != -1) {
-                SkView::DoClickUp(fClicks[index], x, y);
+                SkView::DoClickUp(fClicks[index], x, y, modifierKeys);
                 delete fClicks[index];
                 fClicks.remove(index);
                 handled = true;
@@ -410,7 +410,6 @@
         default:
             // Do nothing
             break;
-	}
-	return handled;
+    }
+    return handled;
 }
-
diff --git a/src/xml/SkBML_XMLParser.cpp b/src/xml/SkBML_XMLParser.cpp
index dbdfe4b..c0a5af5 100644
--- a/src/xml/SkBML_XMLParser.cpp
+++ b/src/xml/SkBML_XMLParser.cpp
@@ -15,7 +15,7 @@
 static uint8_t rbyte(SkStream& s)
 {
     uint8_t b;
-    size_t size = s.read(&b, 1);
+    SkDEBUGCODE(size_t size = ) s.read(&b, 1);
     SkASSERT(size == 1);
     return b;
 }
@@ -179,6 +179,3 @@
     SkXMLParserWriter writer(&output);
     Read(s, writer);
 }
-
-
-
diff --git a/src/xml/SkDOM.cpp b/src/xml/SkDOM.cpp
index 474c291..9854608 100644
--- a/src/xml/SkDOM.cpp
+++ b/src/xml/SkDOM.cpp
@@ -19,10 +19,10 @@
 
     if (this->startElement(elemName))
         return false;
-    
+
     SkDOM::AttrIter iter(dom, node);
     const char*     name, *value;
-    
+
     while ((name = iter.next(&value)) != NULL)
         if (this->addAttribute(name, value))
             return false;
@@ -32,7 +32,7 @@
             if (!this->parse(dom, node))
                 return false;
         } while ((node = dom.getNextSibling(node)) != NULL);
-    
+
     return !this->endElement(elemName);
 }
 
@@ -312,7 +312,7 @@
     const char* elem = dom.getName(node);
 
     parser->startElement(elem);
-    
+
     SkDOM::AttrIter iter(dom, node);
     const char*     name;
     const char*     value;
@@ -467,7 +467,7 @@
 void SkDOM::UnitTest()
 {
 #ifdef SK_SUPPORT_UNITTEST
-    static const char gDoc[] = 
+    static const char gDoc[] =
         "<root a='1' b='2'>"
             "<elem1 c='3' />"
             "<elem2 d='4' />"
@@ -501,4 +501,3 @@
 }
 
 #endif
-
diff --git a/src/xml/SkJS.cpp b/src/xml/SkJS.cpp
index f2e7a83..8167c9c 100644
--- a/src/xml/SkJS.cpp
+++ b/src/xml/SkJS.cpp
@@ -226,4 +226,3 @@
     SkASSERT(success);
 }
 #endifASSERT(success);
-
diff --git a/src/xml/SkJSDisplayable.cpp b/src/xml/SkJSDisplayable.cpp
index 0e14fde..5027797 100644
--- a/src/xml/SkJSDisplayable.cpp
+++ b/src/xml/SkJSDisplayable.cpp
@@ -83,7 +83,7 @@
 }
 
 
-JSFunctionSpec SkJSDisplayable_methods[] = 
+JSFunctionSpec SkJSDisplayable_methods[] =
 {
     { "draw", SkJSDisplayable::Draw, 1, 0, 0 },
     { 0 }
@@ -219,7 +219,7 @@
                                  jsval *vp)
 {
     if (JSVAL_IS_INT(id) == 0)
-        return JS_TRUE; 
+        return JS_TRUE;
     SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
     SkDisplayable* displayable = p->fDisplayable;
     SkDisplayTypes displayableType = displayable->getType();
@@ -295,7 +295,7 @@
 
 JSBool SkJSDisplayable::SetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) {
     if (JSVAL_IS_INT(id) == 0)
-        return JS_TRUE; 
+        return JS_TRUE;
     SkJSDisplayable *p = (SkJSDisplayable *) JS_GetPrivate(cx, obj);
     SkDisplayable* displayable = p->fDisplayable;
     SkDisplayTypes displayableType = displayable->getType();
diff --git a/src/xml/SkXMLParser.cpp b/src/xml/SkXMLParser.cpp
index 17329be..63929a9 100644
--- a/src/xml/SkXMLParser.cpp
+++ b/src/xml/SkXMLParser.cpp
@@ -74,7 +74,7 @@
     return this->onEndElement(elem);
 }
 
-bool SkXMLParser::text(const char text[], int len) 
+bool SkXMLParser::text(const char text[], int len)
 {
     return this->onText(text, len);
 }
diff --git a/src/xml/SkXMLPullParser.cpp b/src/xml/SkXMLPullParser.cpp
index 03fed42..ed04228 100644
--- a/src/xml/SkXMLPullParser.cpp
+++ b/src/xml/SkXMLPullParser.cpp
@@ -26,7 +26,7 @@
 {
     fCurr.fEventType = ERROR;
     fDepth = 0;
-    
+
     this->setStream(stream);
 }
 
@@ -65,12 +65,12 @@
     case END_TAG:
         fDepth -= 1;
         // fall through
-    default:        
+    default:
         reset(&fCurr);
         fCurr.fEventType = this->onNextToken();
         break;
     }
-    
+
     switch (fCurr.fEventType) {
     case START_TAG:
         fDepth += 1;
@@ -125,15 +125,14 @@
 void SkXMLPullParser::getAttributeInfo(int index, AttrInfo* info)
 {
     SkASSERT((unsigned)index < (unsigned)fCurr.fAttrInfoCount);
-    
+
     if (info)
         *info = fCurr.fAttrInfos[index];
 }
-    
+
 bool SkXMLPullParser::onEntityReplacement(const char name[],
                                           SkString* replacement)
 {
     // TODO: std 5 entities here
     return false;
 }
-
diff --git a/src/xml/SkXMLWriter.cpp b/src/xml/SkXMLWriter.cpp
index 935745d..2ff47ea 100644
--- a/src/xml/SkXMLWriter.cpp
+++ b/src/xml/SkXMLWriter.cpp
@@ -69,7 +69,7 @@
     return firstChild;
 }
 
-SkXMLWriter::Elem* SkXMLWriter::getEnd() 
+SkXMLWriter::Elem* SkXMLWriter::getEnd()
 {
     Elem* elem;
     fElems.pop(&elem);
@@ -331,4 +331,3 @@
 }
 
 #endif
-
diff --git a/tests/AAClipTest.cpp b/tests/AAClipTest.cpp
index 4f3f759..1b060a7 100644
--- a/tests/AAClipTest.cpp
+++ b/tests/AAClipTest.cpp
@@ -272,10 +272,12 @@
             }
             REPORTER_ASSERT(reporter, nonEmptyAA == nonEmptyBW);
             REPORTER_ASSERT(reporter, clip2.getBounds() == rgn2.getBounds());
-            
+
             SkMask maskBW, maskAA;
             copyToMask(rgn2, &maskBW);
             clip2.copyToMask(&maskAA);
+            SkAutoMaskFreeImage freeBW(maskBW.fImage);
+            SkAutoMaskFreeImage freeAA(maskAA.fImage);
             REPORTER_ASSERT(reporter, maskBW == maskAA);
         }
     }
@@ -305,24 +307,89 @@
     for (int i = 0; i < 2; ++i) {
         SkAAClip clip;
         clip.setPath(path, NULL, 1 == i);
-        
+
         SkMask mask;
         clip.copyToMask(&mask);
-        
+        SkAutoMaskFreeImage freeM(mask.fImage);
+
         REPORTER_ASSERT(reporter, expected == mask);
     }
 }
 
+#include "SkRasterClip.h"
+
+static void copyToMask(const SkRasterClip& rc, SkMask* mask) {
+    if (rc.isAA()) {
+        rc.aaRgn().copyToMask(mask);
+    } else {
+        copyToMask(rc.bwRgn(), mask);
+    }
+}
+
+static bool operator==(const SkRasterClip& a, const SkRasterClip& b) {
+    if (a.isEmpty()) {
+        return b.isEmpty();
+    }
+    if (b.isEmpty()) {
+        return false;
+    }
+
+    SkMask ma, mb;
+    copyToMask(a, &ma);
+    copyToMask(b, &mb);
+    SkAutoMaskFreeImage aCleanUp(ma.fImage);
+    SkAutoMaskFreeImage bCleanUp(mb.fImage);
+
+    return ma == mb;
+}
+
+static void did_dx_affect(skiatest::Reporter* reporter, const SkScalar dx[],
+                          size_t count, bool changed) {
+    SkIRect ir = { 0, 0, 10, 10 };
+
+    for (size_t i = 0; i < count; ++i) {
+        SkRect r;
+        r.set(ir);
+
+        SkRasterClip rc0(ir);
+        SkRasterClip rc1(ir);
+        SkRasterClip rc2(ir);
+
+        rc0.op(r, SkRegion::kIntersect_Op, false);
+        r.offset(dx[i], 0);
+        rc1.op(r, SkRegion::kIntersect_Op, true);
+        r.offset(-2*dx[i], 0);
+        rc2.op(r, SkRegion::kIntersect_Op, true);
+
+        REPORTER_ASSERT(reporter, changed != (rc0 == rc1));
+        REPORTER_ASSERT(reporter, changed != (rc0 == rc2));
+    }
+}
+
+static void test_nearly_integral(skiatest::Reporter* reporter) {
+    // All of these should generate equivalent rasterclips
+
+    static const SkScalar gSafeX[] = {
+        0, SK_Scalar1/1000, SK_Scalar1/100, SK_Scalar1/10,
+    };
+    did_dx_affect(reporter, gSafeX, SK_ARRAY_COUNT(gSafeX), false);
+
+    static const SkScalar gUnsafeX[] = {
+        SK_Scalar1/4, SK_Scalar1/3,
+    };
+    did_dx_affect(reporter, gUnsafeX, SK_ARRAY_COUNT(gUnsafeX), true);
+}
+
 static void test_regressions(skiatest::Reporter* reporter) {
     // these should not assert in the debug build
     // bug was introduced in rev. 3209
     {
         SkAAClip clip;
         SkRect r;
-        r.fLeft = SkFloatToScalar(129.892181);
-        r.fTop = SkFloatToScalar(10.3999996);
-        r.fRight = SkFloatToScalar(130.892181); 
-        r.fBottom = SkFloatToScalar(20.3999996);
+        r.fLeft = SkFloatToScalar(129.892181f);
+        r.fTop = SkFloatToScalar(10.3999996f);
+        r.fRight = SkFloatToScalar(130.892181f);
+        r.fBottom = SkFloatToScalar(20.3999996f);
         clip.setRect(r, true);
     }
 }
@@ -334,6 +401,7 @@
     test_rgn(reporter);
     test_path_with_hole(reporter);
     test_regressions(reporter);
+    test_nearly_integral(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/Android.mk b/tests/Android.mk
index 8487699..f4a10c8 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -4,12 +4,17 @@
 
 LOCAL_SRC_FILES:= \
   AAClipTest.cpp \
+  AtomicTest.cpp \
   BitmapCopyTest.cpp \
+  BitmapFactoryTest.cpp \
   BitmapGetColorTest.cpp \
+  BitmapHeapTest.cpp \
+  BitmapTransformerTest.cpp \
+  BitSetTest.cpp \
   BlitRowTest.cpp \
   BlurTest.cpp \
-  CanvasTest.cpp \
   ClampRangeTest.cpp \
+  ClipCacheTest.cpp \
   ClipCubicTest.cpp \
   ClipStackTest.cpp \
   ClipperTest.cpp \
@@ -19,18 +24,28 @@
   DeferredCanvasTest.cpp \
   DequeTest.cpp \
   DrawBitmapRectTest.cpp \
+  DrawPathTest.cpp \
   DrawTextTest.cpp \
   EmptyPathTest.cpp \
   FillPathTest.cpp \
+  FlatDataTest.cpp \
   FlateTest.cpp \
+  FontHostStreamTest.cpp \
   FontHostTest.cpp \
   GeometryTest.cpp \
   GLInterfaceValidation.cpp \
   GLProgramsTest.cpp \
+  GpuBitmapCopyTest.cpp \
+  GrContextFactoryTest.cpp \
+  GradientTest.cpp \
+  GrMemoryPoolTest.cpp \
+  HashCacheTest.cpp \
   InfRectTest.cpp \
+  LListTest.cpp \
+  MD5Test.cpp \
   MathTest.cpp \
-  MatrixTest.cpp \
   Matrix44Test.cpp \
+  MatrixTest.cpp \
   MemsetTest.cpp \
   MetaDataTest.cpp \
   PackBitsTest.cpp \
@@ -39,13 +54,20 @@
   PathCoverageTest.cpp \
   PathMeasureTest.cpp \
   PathTest.cpp \
+  PictureTest.cpp \
+  PipeTest.cpp \
   PointTest.cpp \
   PremulAlphaRoundTripTest.cpp \
   QuickRejectTest.cpp \
   Reader32Test.cpp \
   ReadPixelsTest.cpp \
+  ReadWriteAlphaTest.cpp \
+  RefCntTest.cpp \
   RefDictTest.cpp \
   RegionTest.cpp \
+  RoundRectTest.cpp \
+  RTreeTest.cpp \
+  SHA1Test.cpp \
   ScalarTest.cpp \
   ShaderOpacityTest.cpp \
   Sk64Test.cpp \
@@ -54,9 +76,11 @@
   SrcOverTest.cpp \
   StreamTest.cpp \
   StringTest.cpp \
+  StrokeTest.cpp \
   Test.cpp \
-  Test.h \
   TestSize.cpp \
+  TileGridTest.cpp \
+  TLSTest.cpp \
   UnicodeTest.cpp \
   UtilsTest.cpp \
   WArrayTest.cpp \
@@ -64,10 +88,17 @@
   Writer32Test.cpp \
   XfermodeTest.cpp
 
+# Needed for PipeTest
+LOCAL_SRC_FILES += \
+  ../src/pipe/utils/SamplePipeControllers.cpp
+
 # TODO: tests that currently are causing build problems
 #LOCAL_SRC_FILES += \
-#  BitSetTest.cpp \
+#  AnnotationTest.cpp \
+#  CanvasTest.cpp \
+#  ChecksumTest.cpp \
 #  PDFPrimitivesTest.cpp \
+#  PictureUtilsTest.cpp \
 #  ToUnicode.cpp
 
 LOCAL_MODULE:= skia_test
@@ -79,14 +110,16 @@
    external/skia/include/effects \
    external/skia/include/gpu \
    external/skia/include/images \
-   external/skia/include/pdf \
+   external/skia/include/pipe \
    external/skia/include/ports \
    external/skia/include/utils \
    external/skia/src/core \
-   external/skia/src/gpu
+   external/skia/src/effects \
+   external/skia/src/gpu \
+   external/skia/src/pipe/utils \
+   external/skia/src/utils
 
 LOCAL_SHARED_LIBRARIES := libcutils libskia libGLESv2 libEGL
-LOCAL_STATIC_LIBRARIES := libskiagpu
 
 LOCAL_MODULE_TAGS := eng tests
 
diff --git a/tests/AnnotationTest.cpp b/tests/AnnotationTest.cpp
new file mode 100644
index 0000000..586525a
--- /dev/null
+++ b/tests/AnnotationTest.cpp
@@ -0,0 +1,82 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkAnnotation.h"
+#include "SkData.h"
+#include "SkCanvas.h"
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+
+static void test_nodraw(skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorTRANSPARENT);
+
+    SkCanvas canvas(bm);
+    SkRect r = SkRect::MakeWH(SkIntToScalar(10), SkIntToScalar(10));
+
+    SkAutoDataUnref data(SkData::NewWithCString("http://www.gooogle.com"));
+
+    REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0));
+    SkAnnotateRectWithURL(&canvas, r, data.get());
+    REPORTER_ASSERT(reporter, 0 == *bm.getAddr32(0, 0));
+}
+
+struct testCase {
+    SkPDFDocument::Flags flags;
+    bool expectAnnotations;
+};
+
+static void test_pdf_link_annotations(skiatest::Reporter* reporter) {
+    SkISize size = SkISize::Make(612, 792);
+    SkMatrix initialTransform;
+    initialTransform.reset();
+    SkPDFDevice device(size, size, initialTransform);
+    SkCanvas canvas(&device);
+
+    SkRect r = SkRect::MakeXYWH(SkIntToScalar(72), SkIntToScalar(72),
+                                SkIntToScalar(288), SkIntToScalar(72));
+    SkAutoDataUnref data(SkData::NewWithCString("http://www.gooogle.com"));
+    SkAnnotateRectWithURL(&canvas, r, data.get());
+
+    testCase tests[] = {{(SkPDFDocument::Flags)0, true},
+                        {SkPDFDocument::kNoLinks_Flags, false}};
+    for (size_t testNum = 0; testNum < SK_ARRAY_COUNT(tests); testNum++) {
+        SkPDFDocument doc(tests[testNum].flags);
+        doc.appendPage(&device);
+        SkDynamicMemoryWStream outStream;
+        doc.emitPDF(&outStream);
+        SkAutoDataUnref out(outStream.copyToData());
+        const char* rawOutput = (const char*)out->data();
+
+        bool found = false;
+        for (size_t i = 0; i < out->size() - 8; i++) {
+            if (rawOutput[i + 0] == '/' &&
+                rawOutput[i + 1] == 'A' &&
+                rawOutput[i + 2] == 'n' &&
+                rawOutput[i + 3] == 'n' &&
+                rawOutput[i + 4] == 'o' &&
+                rawOutput[i + 5] == 't' &&
+                rawOutput[i + 6] == 's' &&
+                rawOutput[i + 7] == ' ') {
+                found = true;
+                break;
+            }
+        }
+        REPORTER_ASSERT(reporter, found == tests[testNum].expectAnnotations);
+    }
+}
+
+static void TestAnnotation(skiatest::Reporter* reporter) {
+    test_nodraw(reporter);
+    test_pdf_link_annotations(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Annotation", AnnotationClass, TestAnnotation)
diff --git a/tests/AtomicTest.cpp b/tests/AtomicTest.cpp
new file mode 100644
index 0000000..a9ab8d2
--- /dev/null
+++ b/tests/AtomicTest.cpp
@@ -0,0 +1,60 @@
+/*
+ * 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 "SkThread.h"
+#include "SkThreadUtils.h"
+#include "SkTypes.h"
+#include "Test.h"
+
+struct AddInfo {
+    int32_t valueToAdd;
+    int timesToAdd;
+    unsigned int processorAffinity;
+};
+
+static int32_t base = 0;
+
+static AddInfo gAdds[] = {
+    { 3, 100, 23 },
+    { 2, 200, 2 },
+    { 7, 150, 17 },
+};
+
+static void addABunchOfTimes(void* data) {
+    AddInfo* addInfo = static_cast<AddInfo*>(data);
+    for (int i = 0; i < addInfo->timesToAdd; i++) {
+        sk_atomic_add(&base, addInfo->valueToAdd);
+    }
+}
+
+static void test_atomicAddTests(skiatest::Reporter* reporter) {
+    int32_t total = base;
+    SkThread* threads[SK_ARRAY_COUNT(gAdds)];
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) {
+        total += gAdds[i].valueToAdd * gAdds[i].timesToAdd;
+    }
+    // Start the threads
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) {
+        threads[i] = new SkThread(addABunchOfTimes, &gAdds[i]);
+        threads[i]->setProcessorAffinity(gAdds[i].processorAffinity);
+        threads[i]->start();
+    }
+
+    // Now end the threads
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gAdds); i++) {
+        threads[i]->join();
+        delete threads[i];
+    }
+    REPORTER_ASSERT(reporter, total == base);
+    // Ensure that the returned value from sk_atomic_add is correct.
+    int32_t valueToModify = 3;
+    const int32_t originalValue = valueToModify;
+    REPORTER_ASSERT(reporter, originalValue == sk_atomic_add(&valueToModify, 7));
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("AtomicAdd", AtomicAddTestClass, test_atomicAddTests)
diff --git a/tests/BitmapCopyTest.cpp b/tests/BitmapCopyTest.cpp
index d5fd7df..6cfb24b 100644
--- a/tests/BitmapCopyTest.cpp
+++ b/tests/BitmapCopyTest.cpp
@@ -80,7 +80,7 @@
     }
 }
 
-SkColorTable* init_ctable() {
+static SkColorTable* init_ctable() {
     static const SkColor colors[] = {
         SK_ColorBLACK, SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE, SK_ColorWHITE
     };
@@ -102,7 +102,7 @@
 
 // Utility function to read the value of a given pixel in bm. All
 // values converted to uint32_t for simplification of comparisons.
-uint32_t getPixel(int x, int y, const SkBitmap& bm) {
+static uint32_t getPixel(int x, int y, const SkBitmap& bm) {
     uint32_t val = 0;
     uint16_t val16;
     uint8_t val8, shift;
@@ -137,7 +137,7 @@
 // Utility function to set value of any pixel in bm.
 // bm.getConfig() specifies what format 'val' must be
 // converted to, but at present uint32_t can handle all formats.
-void setPixel(int x, int y, uint32_t val, SkBitmap& bm) {
+static void setPixel(int x, int y, uint32_t val, SkBitmap& bm) {
     uint16_t val16;
     uint8_t val8, shift;
     SkAutoLockPixels lock(bm);
@@ -174,7 +174,7 @@
 
 // Utility to return string containing name of each format, to
 // simplify diagnostic output.
-const char* getSkConfigName(const SkBitmap& bm) {
+static const char* getSkConfigName(const SkBitmap& bm) {
     switch (bm.getConfig()) {
         case SkBitmap::kNo_Config: return "SkBitmap::kNo_Config";
         case SkBitmap::kA1_Config: return "SkBitmap::kA1_Config";
@@ -211,17 +211,18 @@
 // A function to verify that two bitmaps contain the same pixel values
 // at all coordinates indicated by coords. Simplifies verification of
 // copied bitmaps.
-void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2,
+static void reportCopyVerification(const SkBitmap& bm1, const SkBitmap& bm2,
                             Coordinates& coords,
                             const char* msg,
                             skiatest::Reporter* reporter){
     bool success = true;
 
     // Confirm all pixels in the list match.
-    for (int i = 0; i < coords.length; ++i)
+    for (int i = 0; i < coords.length; ++i) {
         success = success &&
                   (getPixel(coords[i]->fX, coords[i]->fY, bm1) ==
                    getPixel(coords[i]->fX, coords[i]->fY, bm2));
+    }
 
     if (!success) {
         SkString str;
@@ -232,7 +233,7 @@
 }
 
 // Writes unique pixel values at locations specified by coords.
-void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) {
+static void writeCoordPixels(SkBitmap& bm, const Coordinates& coords) {
     for (int i = 0; i < coords.length; ++i)
         setPixel(coords[i]->fX, coords[i]->fY, i, bm);
 }
@@ -305,6 +306,9 @@
                     REPORTER_ASSERT(reporter, srcP != dstP);
                     REPORTER_ASSERT(reporter, !memcmp(srcP, dstP,
                                                       src.getSize()));
+                    REPORTER_ASSERT(reporter, src.getGenerationID() == dst.getGenerationID());
+                } else {
+                    REPORTER_ASSERT(reporter, src.getGenerationID() != dst.getGenerationID());
                 }
                 // test extractSubset
                 {
@@ -312,11 +316,14 @@
                     SkBitmap subset;
                     SkIRect r;
                     r.set(1, 1, 2, 2);
+                    bitmap.setIsOpaque(true);
                     bitmap.setIsVolatile(true);
                     if (bitmap.extractSubset(&subset, r)) {
                         REPORTER_ASSERT(reporter, subset.width() == 1);
                         REPORTER_ASSERT(reporter, subset.height() == 1);
                         REPORTER_ASSERT(reporter,
+                                        subset.isOpaque() == bitmap.isOpaque());
+                        REPORTER_ASSERT(reporter,
                                         subset.isVolatile() == true);
 
                         SkBitmap copy;
@@ -333,9 +340,12 @@
                         REPORTER_ASSERT(reporter,
                                     (copy.getColorTable() != NULL) == hasCT);
                     }
+                    bitmap.setIsOpaque(false);
                     bitmap.setIsVolatile(false);
                     if (bitmap.extractSubset(&subset, r)) {
                         REPORTER_ASSERT(reporter,
+                                        subset.isOpaque() == bitmap.isOpaque());
+                        REPORTER_ASSERT(reporter,
                                         subset.isVolatile() == false);
                     }
                 }
@@ -474,7 +484,8 @@
                     // raw buffer pointer.
                     const uint32_t bufSize = subH *
                         SkBitmap::ComputeRowBytes(src.getConfig(), subW) * 2;
-                    uint8_t* buf = new uint8_t[bufSize];
+                    SkAutoMalloc autoBuf (bufSize);
+                    uint8_t* buf = static_cast<uint8_t*>(autoBuf.get());
 
                     SkBitmap bufBm; // Attach buf to this bitmap.
                     bool successExpected;
@@ -593,7 +604,6 @@
                         subset.copyPixelsFrom(buf, 1, subset.rowBytes()) ==
                             false);
 
-                    delete [] buf;
 #endif
                 }
             }
diff --git a/tests/BitmapFactoryTest.cpp b/tests/BitmapFactoryTest.cpp
new file mode 100644
index 0000000..b24fd25
--- /dev/null
+++ b/tests/BitmapFactoryTest.cpp
@@ -0,0 +1,76 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkBitmapFactory.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkData.h"
+#include "SkImageEncoder.h"
+#include "SkPaint.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "Test.h"
+
+static SkBitmap* create_bitmap() {
+    SkBitmap* bm = SkNEW(SkBitmap);
+    const int W = 100, H = 100;
+    bm->setConfig(SkBitmap::kARGB_8888_Config, W, H);
+    bm->allocPixels();
+    bm->eraseColor(SK_ColorBLACK);
+    SkCanvas canvas(*bm);
+    SkPaint paint;
+    paint.setColor(SK_ColorBLUE);
+    canvas.drawRectCoords(0, 0, SkIntToScalar(W/2), SkIntToScalar(H/2), paint);
+    return bm;
+}
+
+static SkData* create_data_from_bitmap(const SkBitmap& bm) {
+    SkDynamicMemoryWStream stream;
+    if (SkImageEncoder::EncodeStream(&stream, bm, SkImageEncoder::kPNG_Type, 100)) {
+        return stream.copyToData();
+    }
+    return NULL;
+}
+
+static void assert_bounds_equal(skiatest::Reporter* reporter, const SkBitmap& bm1,
+                                const SkBitmap& bm2) {
+    REPORTER_ASSERT(reporter, bm1.width() == bm2.width());
+    REPORTER_ASSERT(reporter, bm1.height() == bm2.height());
+}
+
+static void TestBitmapFactory(skiatest::Reporter* reporter) {
+    SkAutoTDelete<SkBitmap> bitmap(create_bitmap());
+    SkASSERT(bitmap.get() != NULL);
+
+    SkAutoDataUnref encodedBitmap(create_data_from_bitmap(*bitmap.get()));
+    if (encodedBitmap.get() == NULL) {
+        // Encoding failed.
+        return;
+    }
+
+    SkBitmap bitmapFromFactory;
+    bool success = SkBitmapFactory::DecodeBitmap(&bitmapFromFactory, encodedBitmap);
+    // This assumes that if the encoder worked, the decoder should also work, so the above call
+    // should not fail.
+    REPORTER_ASSERT(reporter, success);
+    assert_bounds_equal(reporter, *bitmap.get(), bitmapFromFactory);
+    REPORTER_ASSERT(reporter, bitmapFromFactory.pixelRef() != NULL);
+
+    // When only requesting that the bounds be decoded, the bounds should be set properly while
+    // the pixels should be empty.
+    SkBitmap boundedBitmap;
+    success = SkBitmapFactory::DecodeBitmap(&boundedBitmap, encodedBitmap,
+                                            SkBitmapFactory::kDecodeBoundsOnly_Constraint);
+    REPORTER_ASSERT(reporter, success);
+    assert_bounds_equal(reporter, *bitmap.get(), boundedBitmap);
+    REPORTER_ASSERT(reporter, boundedBitmap.pixelRef() == NULL);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BitmapFactory", TestBitmapFactoryClass, TestBitmapFactory)
diff --git a/tests/BitmapHeapTest.cpp b/tests/BitmapHeapTest.cpp
new file mode 100644
index 0000000..34a2984
--- /dev/null
+++ b/tests/BitmapHeapTest.cpp
@@ -0,0 +1,96 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkBitmapHeap.h"
+#include "SkColor.h"
+#include "SkFlattenable.h"
+#include "SkOrderedWriteBuffer.h"
+#include "SkPictureFlat.h"
+#include "SkRefCnt.h"
+#include "SkShader.h"
+#include "Test.h"
+
+class FlatDictionary : public SkFlatDictionary<SkShader> {
+
+public:
+    FlatDictionary(SkFlatController* controller)
+    : SkFlatDictionary<SkShader>(controller) {
+        fFlattenProc = &flattenFlattenableProc;
+        // No need for an unflattenProc
+    }
+    static void flattenFlattenableProc(SkOrderedWriteBuffer& buffer, const void* obj) {
+        buffer.writeFlattenable((SkFlattenable*)obj);
+    }
+};
+
+class SkBitmapHeapTester {
+
+public:
+    static int32_t GetRefCount(const SkBitmapHeapEntry* entry) {
+        return entry->fRefCount;
+    }
+};
+
+static void TestBitmapHeap(skiatest::Reporter* reporter) {
+    // Create a bitmap shader.
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorRED);
+    uint32_t* pixel = bm.getAddr32(1,0);
+    *pixel = SK_ColorBLUE;
+
+    SkShader* bitmapShader = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+                                                          SkShader::kRepeat_TileMode);
+    SkAutoTUnref<SkShader> aur(bitmapShader);
+
+    // Flatten, storing it in the bitmap heap.
+    SkBitmapHeap heap(1, 1);
+    SkChunkFlatController controller(1024);
+    controller.setBitmapStorage(&heap);
+    FlatDictionary dictionary(&controller);
+
+    // Dictionary and heap start off empty.
+    REPORTER_ASSERT(reporter, heap.count() == 0);
+    REPORTER_ASSERT(reporter, dictionary.count() == 0);
+
+    heap.deferAddingOwners();
+    int index = dictionary.find(*bitmapShader);
+    heap.endAddingOwnersDeferral(true);
+
+    // The dictionary and heap should now each have one entry.
+    REPORTER_ASSERT(reporter, 1 == index);
+    REPORTER_ASSERT(reporter, heap.count() == 1);
+    REPORTER_ASSERT(reporter, dictionary.count() == 1);
+
+    // The bitmap entry's refcount should be 1, then 0 after release.
+    SkBitmapHeapEntry* entry = heap.getEntry(0);
+    REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(entry) == 1);
+
+    entry->releaseRef();
+    REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(entry) == 0);
+
+    // Now clear out the heap, after which it should be empty.
+    heap.freeMemoryIfPossible(~0U);
+    REPORTER_ASSERT(reporter, heap.count() == 0);
+
+    // Now attempt to flatten the shader again.
+    heap.deferAddingOwners();
+    index = dictionary.find(*bitmapShader);
+    heap.endAddingOwnersDeferral(false);
+
+    // The dictionary should report the same index since the new entry is identical.
+    // The bitmap heap should contain the bitmap, but with no references.
+    REPORTER_ASSERT(reporter, 1 == index);
+    REPORTER_ASSERT(reporter, heap.count() == 1);
+    REPORTER_ASSERT(reporter, SkBitmapHeapTester::GetRefCount(heap.getEntry(0)) == 0);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("BitmapHeap", TestBitmapHeapClass, TestBitmapHeap)
diff --git a/tests/BitmapTransformerTest.cpp b/tests/BitmapTransformerTest.cpp
new file mode 100644
index 0000000..d9ce696
--- /dev/null
+++ b/tests/BitmapTransformerTest.cpp
@@ -0,0 +1,97 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+/**
+ * Tests for SkBitmapTransformer.h and SkBitmapTransformer.cpp
+ */
+
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkBitmapTransformer.h"
+
+namespace skiatest {
+    class BitmapTransformerTestClass : public Test {
+    public:
+        static Test* Factory(void*) {return SkNEW(BitmapTransformerTestClass); }
+    protected:
+        virtual void onGetName(SkString* name) { name->set("BitmapTransformer"); }
+        virtual void onRun(Reporter* reporter) {
+            this->fReporter = reporter;
+            RunTest();
+        }
+    private:
+        void RunTest() {
+            SkBitmap bitmap;
+            SkBitmap::Config supportedConfig = SkBitmap::kARGB_8888_Config;
+            SkBitmap::Config unsupportedConfig = SkBitmap::kARGB_4444_Config;
+            SkBitmapTransformer::PixelFormat supportedPixelFormat =
+                SkBitmapTransformer::kARGB_8888_Premul_PixelFormat;
+            const int kWidth = 55;
+            const int kHeight = 333;
+
+            // Transformations that we know are unsupported:
+            {
+                bitmap.setConfig(unsupportedConfig, kWidth, kHeight);
+                SkBitmapTransformer transformer = SkBitmapTransformer(bitmap, supportedPixelFormat);
+                REPORTER_ASSERT(fReporter, !transformer.isValid());
+            }
+
+            // Valid transformations:
+            {
+                // Bytes we expect to get:
+                const int kWidth = 3;
+                const int kHeight = 5;
+                const unsigned char comparisonBuffer[] = {
+                    // kHeight rows, each with kWidth pixels, premultiplied ARGB for each pixel
+                    0xff,0xff,0x00,0x00, 0xff,0xff,0x00,0x00, 0xff,0xff,0x00,0x00, // red
+                    0xff,0x00,0xff,0x00, 0xff,0x00,0xff,0x00, 0xff,0x00,0xff,0x00, // green
+                    0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue
+                    0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue
+                    0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, 0xff,0x00,0x00,0xff, // blue
+                };
+
+                // A bitmap that should generate the above bytes:
+                bitmap.setConfig(supportedConfig, kWidth, kHeight);
+                REPORTER_ASSERT(fReporter, bitmap.allocPixels());
+                bitmap.setIsOpaque(true);
+                bitmap.eraseColor(SK_ColorBLUE);
+                bitmap.lockPixels();
+                // Change rows [0,1] from blue to [red,green].
+                SkColor oldColor = SK_ColorBLUE;
+                SkColor newColors[] = {SK_ColorRED, SK_ColorGREEN};
+                for (int y = 0; y <= 1; y++) {
+                    for (int x = 0; x < kWidth; x++) {
+                        REPORTER_ASSERT(fReporter, bitmap.getColor(x, y) == oldColor);
+                        SkPMColor* pixel = static_cast<SkPMColor *>(bitmap.getAddr(x, y));
+                        *pixel = SkPreMultiplyColor(newColors[y]);
+                        REPORTER_ASSERT(fReporter, bitmap.getColor(x, y) == newColors[y]);
+                    }
+                }
+                bitmap.unlockPixels();
+
+                // Transform the bitmap and confirm we got the expected results.
+                SkBitmapTransformer transformer = SkBitmapTransformer(bitmap, supportedPixelFormat);
+                REPORTER_ASSERT(fReporter, transformer.isValid());
+                REPORTER_ASSERT(fReporter, transformer.bytesNeededPerRow() == kWidth * 4);
+                REPORTER_ASSERT(fReporter, transformer.bytesNeededTotal() == kWidth * kHeight * 4);
+                int bufferSize = transformer.bytesNeededTotal();
+                SkAutoMalloc pixelBufferManager(bufferSize);
+                char *pixelBuffer = static_cast<char *>(pixelBufferManager.get());
+                REPORTER_ASSERT(fReporter,
+                                transformer.copyBitmapToPixelBuffer(pixelBuffer, bufferSize));
+                REPORTER_ASSERT(fReporter, bufferSize == sizeof(comparisonBuffer));
+                REPORTER_ASSERT(fReporter, memcmp(pixelBuffer, comparisonBuffer, bufferSize) == 0);
+            }
+
+        }
+
+        Reporter* fReporter;
+    };
+
+    static TestRegistry gReg(BitmapTransformerTestClass::Factory);
+}
diff --git a/tests/BlitRowTest.cpp b/tests/BlitRowTest.cpp
index b37b47e..fca4588 100644
--- a/tests/BlitRowTest.cpp
+++ b/tests/BlitRowTest.cpp
@@ -136,7 +136,7 @@
         SkBitmap dstBM;
         dstBM.setConfig(gDstConfig[i], W, 1);
         dstBM.allocPixels();
-        
+
         SkCanvas canvas(dstBM);
         for (size_t j = 0; j < SK_ARRAY_COUNT(gSrcRec); j++) {
             srcBM.eraseColor(gSrcRec[j].fSrc);
@@ -178,7 +178,7 @@
         SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
                                                    SkShader::kClamp_TileMode);
         paint->setShader(s)->unref();
-        
+
     }
 
     void draw(SkCanvas* canvas, SkPaint* paint) {
@@ -199,7 +199,7 @@
 static void test_diagonal(skiatest::Reporter* reporter) {
     static const int W = 64;
     static const int H = W;
-    
+
     static const SkBitmap::Config gDstConfig[] = {
         SkBitmap::kARGB_8888_Config,
         SkBitmap::kRGB_565_Config,
@@ -210,7 +210,7 @@
     static const SkColor gDstBG[] = { 0, 0xFFFFFFFF };
 
     SkPaint paint;
-    
+
     SkBitmap srcBM;
     srcBM.setConfig(SkBitmap::kARGB_8888_Config, W, H);
     srcBM.allocPixels();
@@ -226,7 +226,7 @@
         dstBM1.setConfig(gDstConfig[i], W, H);
         dstBM0.allocPixels();
         dstBM1.allocPixels();
-        
+
         SkCanvas canvas0(dstBM0);
         SkCanvas canvas1(dstBM1);
         SkColor bgColor;
@@ -236,7 +236,7 @@
 
             for (int c = 0; c <= 0xFF; c++) {
                 srcBM.eraseARGB(0xFF, c, c, c);
-                
+
                 for (int k = 0; k < 4; k++) {
                     bool dither = (k & 1) != 0;
                     uint8_t alpha = (k & 2) ? 0x80 : 0xFF;
diff --git a/tests/CanvasTest.cpp b/tests/CanvasTest.cpp
index 76573ed..8c64d58 100644
--- a/tests/CanvasTest.cpp
+++ b/tests/CanvasTest.cpp
@@ -18,7 +18,7 @@
  *      The general pattern for creating a new test step is to write a test
  *      function of the form:
  *
- *          static void MyTestStepFunction(SkCanvas* canvas, 
+ *          static void MyTestStepFunction(SkCanvas* canvas,
  *                                         skiatest::Reporter* reporter,
  *                                         CanvasTestStep* testStep)
  *          {
@@ -50,6 +50,8 @@
 #include "SkDevice.h"
 #include "SkMatrix.h"
 #include "SkNWayCanvas.h"
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkPicture.h"
@@ -62,10 +64,38 @@
 #include "SkTDArray.h"
 #include "Test.h"
 
+class Canvas2CanvasClipVisitor : public SkCanvas::ClipVisitor {
+public:
+    Canvas2CanvasClipVisitor(SkCanvas* target) : fTarget(target) {}
+
+    virtual void clipRect(const SkRect& r, SkRegion::Op op, bool aa) {
+        fTarget->clipRect(r, op, aa);
+    }
+    virtual void clipPath(const SkPath& p, SkRegion::Op op, bool aa) {
+        fTarget->clipPath(p, op, aa);
+    }
+
+private:
+    SkCanvas* fTarget;
+};
+
+static void test_clipVisitor(skiatest::Reporter* reporter, SkCanvas* canvas) {
+    SkISize size = canvas->getDeviceSize();
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, size.width(), size.height());
+    SkCanvas c(bm);
+
+    Canvas2CanvasClipVisitor visitor(&c);
+    canvas->replayClips(&visitor);
+
+    REPORTER_ASSERT(reporter, c.getTotalClip() == canvas->getTotalClip());
+}
+
 static const int kWidth = 2;
 static const int kHeight = 2;
 // Maximum stream length for picture serialization
-static const size_t kMaxPictureBufferSize = 1024; 
+static const size_t kMaxPictureBufferSize = 1024;
 
 // 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
@@ -73,29 +103,33 @@
 // functions that are called multiple times in different contexts (test
 // cases and test steps).
 static const char* const kDefaultAssertMessageFormat = "%s";
-static const char* const kCanvasDrawAssertMessageFormat = 
+static const char* const kCanvasDrawAssertMessageFormat =
     "Drawing test step %s with SkCanvas";
-static const char* const kPictureDrawAssertMessageFormat = 
+static const char* const kPictureDrawAssertMessageFormat =
     "Drawing test step %s with SkPicture";
-static const char* const kPictureSecondDrawAssertMessageFormat = 
+static const char* const kPictureSecondDrawAssertMessageFormat =
     "Duplicate draw of test step %s with SkPicture";
-static const char* const kPictureReDrawAssertMessageFormat = 
+static const char* const kPictureReDrawAssertMessageFormat =
     "Playing back test step %s from an SkPicture to another SkPicture";
-static const char* const kDeferredDrawAssertMessageFormat = 
+static const char* const kDeferredDrawAssertMessageFormat =
     "Drawing test step %s with SkDeferredCanvas";
-static const char* const kProxyDrawAssertMessageFormat = 
+static const char* const kProxyDrawAssertMessageFormat =
     "Drawing test step %s with SkProxyCanvas";
-static const char* const kNWayDrawAssertMessageFormat = 
+static const char* const kNWayDrawAssertMessageFormat =
     "Drawing test step %s with SkNWayCanvas";
-static const char* const kRoundTripAssertMessageFormat = 
+static const char* const kRoundTripAssertMessageFormat =
     "test step %s, SkPicture consistency after round trip";
-static const char* const kPictureRecoringAssertMessageFormat = 
+static const char* const kPictureRecoringAssertMessageFormat =
     "test step %s, SkPicture state consistency after recording";
-static const char* const kPicturePlaybackAssertMessageFormat = 
+static const char* const kPicturePlaybackAssertMessageFormat =
     "test step %s, SkPicture state consistency in playback canvas";
-static const char* const kDeferredPreFlushAssertMessageFormat = 
+static const char* const kDeferredPreFlushAssertMessageFormat =
     "test step %s, SkDeferredCanvas state consistency before flush";
-static const char* const kDeferredPostFlushAssertMessageFormat = 
+static const char* const kDeferredPostFlushPlaybackAssertMessageFormat =
+    "test step %s, SkDeferredCanvas playback canvas state consistency after flush";
+static const char* const kDeferredPostSilentFlushPlaybackAssertMessageFormat =
+    "test step %s, SkDeferredCanvas playback canvas state consistency after silent flush";
+static const char* const kDeferredPostFlushAssertMessageFormat =
     "test step %s, SkDeferredCanvas state consistency after flush";
 static const char* const kPictureResourceReuseMessageFormat =
     "test step %s, SkPicture duplicate flattened object test";
@@ -109,6 +143,8 @@
     "test step %s, SkNWayCanvas indirect canvas 1 state consistency";
 static const char* const kNWayIndirect2StateAssertMessageFormat =
     "test step %s, SkNWayCanvas indirect canvas 2 state consistency";
+static const char* const kPdfAssertMessageFormat =
+    "PDF sanity check failed %s";
 
 static void createBitmap(SkBitmap* bm, SkBitmap::Config config, SkColor color) {
     bm->setConfig(config, kWidth, kHeight);
@@ -124,9 +160,10 @@
 
 class CanvasTestStep {
 public:
-    CanvasTestStep() {
+    CanvasTestStep(bool fEnablePdfTesting = true) {
         *testStepArray().append() = this;
         fAssertMessageFormat = kDefaultAssertMessageFormat;
+        this->fEnablePdfTesting = fEnablePdfTesting;
     }
     virtual ~CanvasTestStep() { }
 
@@ -142,15 +179,18 @@
         fAssertMessageFormat = format;
     }
 
+    bool enablePdfTesting() { return fEnablePdfTesting; }
+
 private:
     SkString fAssertMessage;
     const char* fAssertMessageFormat;
+    bool fEnablePdfTesting;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
 // Constants used by test steps
 
-const SkRect kTestRect = 
+const SkRect kTestRect =
     SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
                      SkIntToScalar(2), SkIntToScalar(1));
 static SkMatrix testMatrix() {
@@ -190,7 +230,20 @@
 }
 SkBitmap kTestBitmap; // cannot be created during static init
 SkString kTestText("Hello World");
-SkPoint kTestPoint = SkPoint::Make(SkIntToScalar(0), SkIntToScalar(1));
+SkPoint kTestPoints2[] = {
+  { SkIntToScalar(0), SkIntToScalar(1) },
+  { SkIntToScalar(1), SkIntToScalar(1) },
+  { SkIntToScalar(2), SkIntToScalar(1) },
+  { SkIntToScalar(3), SkIntToScalar(1) },
+  { SkIntToScalar(4), SkIntToScalar(1) },
+  { SkIntToScalar(5), SkIntToScalar(1) },
+  { SkIntToScalar(6), SkIntToScalar(1) },
+  { SkIntToScalar(7), SkIntToScalar(1) },
+  { SkIntToScalar(8), SkIntToScalar(1) },
+  { SkIntToScalar(9), SkIntToScalar(1) },
+  { SkIntToScalar(10), SkIntToScalar(1) },
+};
+
 
 ///////////////////////////////////////////////////////////////////////////////
 // Macros for defining test steps
@@ -205,6 +258,17 @@
 };                                                                      \
 static NAME##_TestStep NAME##_TestStepInstance;
 
+#define TEST_STEP_NO_PDF(NAME, FUNCTION)                                       \
+class NAME##_TestStep : public CanvasTestStep{                          \
+public:                                                                 \
+    NAME##_TestStep() : CanvasTestStep(false) {}                        \
+    virtual void draw(SkCanvas* canvas, skiatest::Reporter* reporter) { \
+        FUNCTION (canvas, reporter, this);                              \
+    }                                                                   \
+    virtual const char* name() const {return #NAME ;}                   \
+};                                                                      \
+static NAME##_TestStep NAME##_TestStepInstance;
+
 #define SIMPLE_TEST_STEP(NAME, CALL)                              \
 static void NAME##TestStep(SkCanvas* canvas, skiatest::Reporter*, \
     CanvasTestStep*) {                                            \
@@ -222,15 +286,9 @@
 
 
 ///////////////////////////////////////////////////////////////////////////////
-// Basic test steps for most virtual methods in SkCanvas that draw or affect 
+// Basic test steps for most virtual methods in SkCanvas that draw or affect
 // the state of the canvas.
 
-SIMPLE_TEST_STEP(SaveMatrix, save(SkCanvas::kMatrix_SaveFlag));
-SIMPLE_TEST_STEP(SaveClip, save(SkCanvas::kClip_SaveFlag));
-SIMPLE_TEST_STEP(SaveMatrixClip, save(SkCanvas::kMatrixClip_SaveFlag));
-SIMPLE_TEST_STEP(SaveLayer, saveLayer(NULL, NULL));
-SIMPLE_TEST_STEP(BoundedSaveLayer, saveLayer(&kTestRect, NULL));
-SIMPLE_TEST_STEP(PaintSaveLayer, saveLayer(NULL, &kTestPaint));
 SIMPLE_TEST_STEP_WITH_ASSERT(Translate,
     translate(SkIntToScalar(1), SkIntToScalar(2)));
 SIMPLE_TEST_STEP_WITH_ASSERT(Scale,
@@ -240,9 +298,9 @@
     skew(SkIntToScalar(1), SkIntToScalar(2)));
 SIMPLE_TEST_STEP_WITH_ASSERT(Concat, concat(kTestMatrix));
 SIMPLE_TEST_STEP(SetMatrix, setMatrix(kTestMatrix));
-SIMPLE_TEST_STEP_WITH_ASSERT(ClipRect, clipRect(kTestRect));
-SIMPLE_TEST_STEP_WITH_ASSERT(ClipPath, clipPath(kTestPath));
-SIMPLE_TEST_STEP_WITH_ASSERT(ClipRegion,
+SIMPLE_TEST_STEP(ClipRect, clipRect(kTestRect));
+SIMPLE_TEST_STEP(ClipPath, clipPath(kTestPath));
+SIMPLE_TEST_STEP(ClipRegion,
     clipRegion(kTestRegion, SkRegion::kReplace_Op));
 SIMPLE_TEST_STEP(Clear, clear(kTestColor));
 SIMPLE_TEST_STEP(DrawPaint, drawPaint(kTestPaint));
@@ -275,18 +333,138 @@
 SIMPLE_TEST_STEP(DrawText, drawText(kTestText.c_str(), kTestText.size(),
     0, 1, kTestPaint));
 SIMPLE_TEST_STEP(DrawPosText, drawPosText(kTestText.c_str(),
-    kTestText.size(), &kTestPoint, kTestPaint));
+    kTestText.size(), kTestPoints2, kTestPaint));
 SIMPLE_TEST_STEP(DrawTextOnPath, drawTextOnPath(kTestText.c_str(),
     kTestText.size(), kTestPath, NULL, kTestPaint));
 SIMPLE_TEST_STEP(DrawTextOnPathMatrix, drawTextOnPath(kTestText.c_str(),
     kTestText.size(), kTestPath, &kTestMatrix, kTestPaint));
-SIMPLE_TEST_STEP(SetExternalMatrix, setExternalMatrix(&kTestMatrix));
 SIMPLE_TEST_STEP(DrawData, drawData(kTestText.c_str(), kTestText.size()));
 
 ///////////////////////////////////////////////////////////////////////////////
 // Complex test steps
 
-static void DrawVerticesShaderTestStep(SkCanvas* canvas, 
+// Save/restore calls cannot be in isolated simple test steps because the test
+// cases that use SkPicture require that save and restore calls be balanced.
+static void SaveMatrixStep(SkCanvas* canvas,
+                           skiatest::Reporter* reporter,
+                           CanvasTestStep* testStep) {
+    int saveCount = canvas->getSaveCount();
+    canvas->save(SkCanvas::kMatrix_SaveFlag);
+    canvas->clipRegion(kTestRegion);
+    canvas->translate(SkIntToScalar(1), SkIntToScalar(2));
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalMatrix().isIdentity(),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalClip() == kTestRegion,
+        testStep->assertMessage());
+}
+TEST_STEP(SaveMatrix, SaveMatrixStep);
+
+static void SaveClipStep(SkCanvas* canvas,
+                         skiatest::Reporter* reporter,
+                         CanvasTestStep* testStep) {
+    int saveCount = canvas->getSaveCount();
+    canvas->save(SkCanvas::kClip_SaveFlag);
+    canvas->translate(SkIntToScalar(1), SkIntToScalar(2));
+    canvas->clipRegion(kTestRegion);
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, !canvas->getTotalMatrix().isIdentity(),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalClip() != kTestRegion,
+        testStep->assertMessage());
+}
+TEST_STEP(SaveClip, SaveClipStep);
+
+static void SaveMatrixClipStep(SkCanvas* canvas,
+                               skiatest::Reporter* reporter,
+                               CanvasTestStep* testStep) {
+    int saveCount = canvas->getSaveCount();
+    canvas->save(SkCanvas::kMatrixClip_SaveFlag);
+    canvas->translate(SkIntToScalar(1), SkIntToScalar(2));
+    canvas->clipRegion(kTestRegion);
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalMatrix().isIdentity(),
+        testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getTotalClip() != kTestRegion,
+        testStep->assertMessage());
+}
+TEST_STEP(SaveMatrixClip, SaveMatrixClipStep);
+
+static void SaveLayerStep(SkCanvas* canvas,
+                          skiatest::Reporter* reporter,
+                          CanvasTestStep* testStep) {
+    int saveCount = canvas->getSaveCount();
+    canvas->saveLayer(NULL, NULL);
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
+        testStep->assertMessage());
+}
+TEST_STEP(SaveLayer, SaveLayerStep);
+
+static void BoundedSaveLayerStep(SkCanvas* canvas,
+                          skiatest::Reporter* reporter,
+                          CanvasTestStep* testStep) {
+    int saveCount = canvas->getSaveCount();
+    canvas->saveLayer(&kTestRect, NULL);
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
+        testStep->assertMessage());
+}
+TEST_STEP(BoundedSaveLayer, BoundedSaveLayerStep);
+
+static void PaintSaveLayerStep(SkCanvas* canvas,
+                          skiatest::Reporter* reporter,
+                          CanvasTestStep* testStep) {
+    int saveCount = canvas->getSaveCount();
+    canvas->saveLayer(NULL, &kTestPaint);
+    canvas->restore();
+    REPORTER_ASSERT_MESSAGE(reporter, canvas->getSaveCount() == saveCount,
+        testStep->assertMessage());
+}
+TEST_STEP(PaintSaveLayer, PaintSaveLayerStep);
+
+static void TwoClipOpsStep(SkCanvas* canvas,
+                           skiatest::Reporter* reporter,
+                           CanvasTestStep* testStep) {
+    // This test exercises a functionality in SkPicture that leads to the
+    // recording of restore offset placeholders.  This test will trigger an
+    // assertion at playback time if the placeholders are not properly
+    // filled when the recording ends.
+    canvas->clipRect(kTestRect);
+    canvas->clipRegion(kTestRegion);
+}
+TEST_STEP(TwoClipOps, TwoClipOpsStep);
+
+// exercise fix for http://code.google.com/p/skia/issues/detail?id=560
+// ('SkPathStroker::lineTo() fails for line with length SK_ScalarNearlyZero')
+static void DrawNearlyZeroLengthPathTestStep(SkCanvas* canvas,
+                                             skiatest::Reporter* reporter,
+                                             CanvasTestStep* testStep) {
+    SkPaint paint;
+    paint.setStrokeWidth(SkIntToScalar(1));
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    SkPath path;
+    SkPoint pt1 = { 0, 0 };
+    SkPoint pt2 = { 0, SK_ScalarNearlyZero };
+    SkPoint pt3 = { SkIntToScalar(1), 0 };
+    SkPoint pt4 = { SkIntToScalar(1), SK_ScalarNearlyZero/2 };
+    path.moveTo(pt1);
+    path.lineTo(pt2);
+    path.lineTo(pt3);
+    path.lineTo(pt4);
+
+    canvas->drawPath(path, paint);
+}
+TEST_STEP(DrawNearlyZeroLengthPath, DrawNearlyZeroLengthPathTestStep);
+
+static void DrawVerticesShaderTestStep(SkCanvas* canvas,
                                        skiatest::Reporter* reporter,
                                        CanvasTestStep* testStep) {
     SkPoint pts[4];
@@ -301,9 +479,10 @@
     canvas->drawVertices(SkCanvas::kTriangleFan_VertexMode, 4, pts, pts,
                          NULL, NULL, NULL, 0, paint);
 }
-TEST_STEP(DrawVerticesShader, DrawVerticesShaderTestStep);
+// NYI: issue 240.
+TEST_STEP_NO_PDF(DrawVerticesShader, DrawVerticesShaderTestStep);
 
-static void DrawPictureTestStep(SkCanvas* canvas, 
+static void DrawPictureTestStep(SkCanvas* canvas,
                                 skiatest::Reporter* reporter,
                                 CanvasTestStep* testStep) {
     SkPicture* testPicture = SkNEW_ARGS(SkPicture, ());
@@ -316,21 +495,20 @@
 }
 TEST_STEP(DrawPicture, DrawPictureTestStep);
 
-static void SaveRestoreTestStep(SkCanvas* canvas, 
+static void SaveRestoreTestStep(SkCanvas* canvas,
                                 skiatest::Reporter* reporter,
                                 CanvasTestStep* testStep) {
-    REPORTER_ASSERT_MESSAGE(reporter, 1 == canvas->getSaveCount(), 
-        testStep->assertMessage());
-    size_t n = canvas->save();
-    REPORTER_ASSERT_MESSAGE(reporter, 1 == n, testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, 2 == canvas->getSaveCount(),
+    int baseSaveCount = canvas->getSaveCount();
+    int n = canvas->save();
+    REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount == n, testStep->assertMessage());
+    REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 1 == canvas->getSaveCount(),
         testStep->assertMessage());
     canvas->save();
     canvas->save();
-    REPORTER_ASSERT_MESSAGE(reporter, 4 == canvas->getSaveCount(),
+    REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 3 == canvas->getSaveCount(),
         testStep->assertMessage());
-    canvas->restoreToCount(2);
-    REPORTER_ASSERT_MESSAGE(reporter, 2 == canvas->getSaveCount(),
+    canvas->restoreToCount(baseSaveCount + 1);
+    REPORTER_ASSERT_MESSAGE(reporter, baseSaveCount + 1 == canvas->getSaveCount(),
         testStep->assertMessage());
 
     // should this pin to 1, or be a no-op, or crash?
@@ -340,7 +518,7 @@
 }
 TEST_STEP(SaveRestore, SaveRestoreTestStep);
 
-static void DrawLayerTestStep(SkCanvas* canvas, 
+static void DrawLayerTestStep(SkCanvas* canvas,
                               skiatest::Reporter* reporter,
                               CanvasTestStep* testStep) {
     REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
@@ -348,7 +526,8 @@
     canvas->save();
     REPORTER_ASSERT_MESSAGE(reporter, !canvas->isDrawingToLayer(),
         testStep->assertMessage());
-    
+    canvas->restore();
+
     const SkRect* bounds = NULL;    // null means include entire bounds
     const SkPaint* paint = NULL;
 
@@ -373,8 +552,51 @@
 }
 TEST_STEP(DrawLayer, DrawLayerTestStep);
 
+static void NestedSaveRestoreWithSolidPaintTestStep(SkCanvas* canvas,
+                                      skiatest::Reporter* reporter,
+                                      CanvasTestStep* testStep) {
+    // This test step challenges the TestDeferredCanvasStateConsistency
+    // test cases because the opaque paint can trigger an optimization
+    // that discards previously recorded commands. The challenge is to maintain
+    // correct clip and matrix stack state.
+    canvas->resetMatrix();
+    canvas->rotate(SkIntToScalar(30));
+    canvas->save();
+    canvas->translate(SkIntToScalar(2), SkIntToScalar(1));
+    canvas->save();
+    canvas->scale(SkIntToScalar(3), SkIntToScalar(3));
+    SkPaint paint;
+    paint.setColor(0xFFFFFFFF);
+    canvas->drawPaint(paint);
+    canvas->restore();
+    canvas->restore();
+}
+TEST_STEP(NestedSaveRestoreWithSolidPaint, \
+    NestedSaveRestoreWithSolidPaintTestStep);
+
+static void NestedSaveRestoreWithFlushTestStep(SkCanvas* canvas,
+                                      skiatest::Reporter* reporter,
+                                      CanvasTestStep* testStep) {
+    // This test step challenges the TestDeferredCanvasStateConsistency
+    // test case because the canvas flush on a deferred canvas will
+    // reset the recording session. The challenge is to maintain correct
+    // clip and matrix stack state on the playback canvas.
+    canvas->resetMatrix();
+    canvas->rotate(SkIntToScalar(30));
+    canvas->save();
+    canvas->translate(SkIntToScalar(2), SkIntToScalar(1));
+    canvas->save();
+    canvas->scale(SkIntToScalar(3), SkIntToScalar(3));
+    canvas->drawRect(kTestRect,kTestPaint);
+    canvas->flush();
+    canvas->restore();
+    canvas->restore();
+}
+TEST_STEP(NestedSaveRestoreWithFlush, \
+    NestedSaveRestoreWithFlushTestStep);
+
 static void AssertCanvasStatesEqual(skiatest::Reporter* reporter,
-                                    const SkCanvas* canvas1, 
+                                    const SkCanvas* canvas1,
                                     const SkCanvas* canvas2,
                                     CanvasTestStep* testStep) {
     REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDeviceSize() ==
@@ -383,19 +605,14 @@
         canvas2->getSaveCount(), testStep->assertMessage());
     REPORTER_ASSERT_MESSAGE(reporter, canvas1->isDrawingToLayer() ==
         canvas2->isDrawingToLayer(), testStep->assertMessage());
+
     SkRect bounds1, bounds2;
     REPORTER_ASSERT_MESSAGE(reporter,
-        canvas1->getClipBounds(&bounds1, SkCanvas::kAA_EdgeType) ==
-        canvas2->getClipBounds(&bounds2, SkCanvas::kAA_EdgeType),
+        canvas1->getClipBounds(&bounds1) == canvas2->getClipBounds(&bounds2),
         testStep->assertMessage());
     REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2,
-        testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter,
-        canvas1->getClipBounds(&bounds1, SkCanvas::kBW_EdgeType) ==
-        canvas2->getClipBounds(&bounds2, SkCanvas::kBW_EdgeType),
-        testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, bounds1 == bounds2,
-        testStep->assertMessage());
+                            testStep->assertMessage());
+
     REPORTER_ASSERT_MESSAGE(reporter, canvas1->getDrawFilter() ==
         canvas2->getDrawFilter(), testStep->assertMessage());
     SkIRect deviceBounds1, deviceBounds2;
@@ -413,11 +630,9 @@
         canvas2->getClipType(), testStep->assertMessage());
     REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalClip() ==
         canvas2->getTotalClip(), testStep->assertMessage());
-    REPORTER_ASSERT_MESSAGE(reporter, canvas1->getTotalClipStack() ==
-        canvas2->getTotalClipStack(), testStep->assertMessage());
 
     // The following test code is commented out because the test fails when
-    // the canvas is an SkPictureRecord or SkDeferredCanvas 
+    // the canvas is an SkPictureRecord or SkDeferredCanvas
     // Issue: http://code.google.com/p/skia/issues/detail?id=498
     // Also, creating a LayerIter on an SkProxyCanvas crashes
     // Issue: http://code.google.com/p/skia/issues/detail?id=499
@@ -449,6 +664,10 @@
 // the privates members of SkPictureRecord
 class SkPictureTester {
 private:
+    static int EQ(const SkFlatData* a, const SkFlatData* b) {
+        return *a == *b;
+    }
+
     static void AssertFlattenedObjectsEqual(
         SkPictureRecord* referenceRecord,
         SkPictureRecord* testRecord,
@@ -456,20 +675,14 @@
         CanvasTestStep* testStep) {
 
         REPORTER_ASSERT_MESSAGE(reporter,
-            referenceRecord->fBitmaps.count() ==
-            testRecord->fBitmaps.count(), testStep->assertMessage());
-        for (int i = 0; i < referenceRecord->fBitmaps.count(); ++i) {
-            REPORTER_ASSERT_MESSAGE(reporter,
-                SkFlatData::Compare(referenceRecord->fBitmaps[i],
-                testRecord->fBitmaps[i]) == 0, testStep->assertMessage());
-        }
+            referenceRecord->fBitmapHeap->count() ==
+            testRecord->fBitmapHeap->count(), testStep->assertMessage());
         REPORTER_ASSERT_MESSAGE(reporter,
             referenceRecord->fMatrices.count() ==
             testRecord->fMatrices.count(), testStep->assertMessage());
         for (int i = 0; i < referenceRecord->fMatrices.count(); ++i) {
             REPORTER_ASSERT_MESSAGE(reporter,
-                SkFlatData::Compare(referenceRecord->fMatrices[i],
-                testRecord->fMatrices[i]) == 0,
+                EQ(referenceRecord->fMatrices[i], testRecord->fMatrices[i]),
                 testStep->assertMessage());
         }
         REPORTER_ASSERT_MESSAGE(reporter,
@@ -477,16 +690,16 @@
             testRecord->fPaints.count(), testStep->assertMessage());
         for (int i = 0; i < referenceRecord->fPaints.count(); ++i) {
             REPORTER_ASSERT_MESSAGE(reporter,
-                SkFlatData::Compare(referenceRecord->fPaints[i],
-                testRecord->fPaints[i]) == 0, testStep->assertMessage());
+                EQ(referenceRecord->fPaints[i], testRecord->fPaints[i]),
+                                    testStep->assertMessage());
         }
         REPORTER_ASSERT_MESSAGE(reporter,
             referenceRecord->fRegions.count() ==
             testRecord->fRegions.count(), testStep->assertMessage());
         for (int i = 0; i < referenceRecord->fRegions.count(); ++i) {
             REPORTER_ASSERT_MESSAGE(reporter,
-                SkFlatData::Compare(referenceRecord->fRegions[i],
-                testRecord->fRegions[i]) == 0, testStep->assertMessage());
+                EQ(referenceRecord->fRegions[i], testRecord->fRegions[i]),
+                                    testStep->assertMessage());
         }
         REPORTER_ASSERT_MESSAGE(reporter,
             !referenceRecord->fPathHeap ==
@@ -507,79 +720,24 @@
             }
         }
         */
-    
+
     }
 
 public:
 
-    static void TestPictureSerializationRoundTrip(skiatest::Reporter* reporter, 
-                                                  CanvasTestStep* testStep) {
-        testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
-        SkPicture referencePicture;
-        testStep->draw(referencePicture.beginRecording(kWidth, kHeight),
-            reporter);
-        SkPicture initialPicture;
-        testStep->draw(initialPicture.beginRecording(kWidth, kHeight),
-            reporter);
-        testStep->setAssertMessageFormat(kPictureReDrawAssertMessageFormat);
-        SkPicture roundTripPicture;
-        initialPicture.draw(roundTripPicture.beginRecording(kWidth, kHeight));
-
-        SkPictureRecord* referenceRecord = static_cast<SkPictureRecord*>(
-            referencePicture.getRecordingCanvas());
-        SkPictureRecord* roundTripRecord = static_cast<SkPictureRecord*>(
-            roundTripPicture.getRecordingCanvas());
-
-        testStep->setAssertMessageFormat(kPictureReDrawAssertMessageFormat);
-
-        // Verify that deserialization-serialization round trip conserves all
-        // data by comparing referenceRecord to roundTripRecord
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fBitmapIndex ==
-            roundTripRecord->fBitmapIndex, testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fMatrixIndex ==
-            roundTripRecord->fMatrixIndex, testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fPaintIndex ==
-            roundTripRecord->fPaintIndex, testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fRegionIndex ==
-            roundTripRecord->fRegionIndex, testStep->assertMessage());
-        char referenceBuffer[kMaxPictureBufferSize];
-        SkMemoryWStream referenceStream(referenceBuffer,
-            kMaxPictureBufferSize);
-        referenceRecord->fWriter.writeToStream(&referenceStream);
-        char roundTripBuffer[kMaxPictureBufferSize];
-        SkMemoryWStream roundTripStream(roundTripBuffer,
-            kMaxPictureBufferSize);
-        roundTripRecord->fWriter.writeToStream(&roundTripStream);
-        REPORTER_ASSERT_MESSAGE(reporter,
-            roundTripStream.bytesWritten() == referenceStream.bytesWritten(),
-            testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, 0 == memcmp(referenceBuffer,
-            roundTripBuffer, roundTripStream.bytesWritten()),
-            testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter, referenceRecord->fRecordFlags ==
-            roundTripRecord->fRecordFlags, testStep->assertMessage());
-        REPORTER_ASSERT_MESSAGE(reporter,
-            referenceRecord->fRestoreOffsetStack ==
-            roundTripRecord->fRestoreOffsetStack,
-            testStep->assertMessage());
-        AssertFlattenedObjectsEqual(referenceRecord, roundTripRecord,
-            reporter, testStep);
-        AssertCanvasStatesEqual(reporter, referenceRecord, roundTripRecord,
-            testStep);
-    }
-
-    static void TestPictureFlattenedObjectReuse(skiatest::Reporter* reporter, 
-                                         CanvasTestStep* testStep) {
+    static void TestPictureFlattenedObjectReuse(skiatest::Reporter* reporter,
+                                                CanvasTestStep* testStep,
+                                                uint32_t recordFlags) {
         // Verify that when a test step is executed twice, no extra resources
         // are flattened during the second execution
         testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
         SkPicture referencePicture;
         SkCanvas* referenceCanvas = referencePicture.beginRecording(kWidth,
-            kHeight);
+            kHeight, recordFlags);
         testStep->draw(referenceCanvas, reporter);
         SkPicture testPicture;
         SkCanvas* testCanvas = testPicture.beginRecording(kWidth,
-            kHeight);
+            kHeight, recordFlags);
         testStep->draw(testCanvas, reporter);
         testStep->setAssertMessageFormat(kPictureSecondDrawAssertMessageFormat);
         testStep->draw(testCanvas, reporter);
@@ -594,66 +752,65 @@
     }
 };
 
-static void TestPictureStateConsistency(skiatest::Reporter* reporter, 
-                                        CanvasTestStep* testStep,
-                                        const SkCanvas& referenceCanvas) {
-    // Verify that the recording canvas's state is consistent
-    // with that of a regular canvas
-    SkPicture testPicture;
-    SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight);
-    testStep->setAssertMessageFormat(kPictureDrawAssertMessageFormat);
-    testStep->draw(pictureCanvas, reporter);
-    testStep->setAssertMessageFormat(kPictureRecoringAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, pictureCanvas, &referenceCanvas,
-        testStep);
-
-    SkBitmap playbackStore;
-    createBitmap(&playbackStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
-    SkDevice playbackDevice(playbackStore);
-    SkCanvas playbackCanvas(&playbackDevice);
-    testPicture.draw(&playbackCanvas);
-    testStep->setAssertMessageFormat(kPicturePlaybackAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &playbackCanvas, &referenceCanvas,
-        testStep);
-
-    // The following test code is commented out because SkPicture is not
-    // currently expected to preserve state when restarting recording.
-    /*
-    SkCanvas* pictureCanvas = testPicture.beginRecording(kWidth, kHeight);
-    testStep->setAssertMessageFormat(kPictureResumeAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, pictureCanvas, &referenceCanvas,
-        testStep);
-    */
+static void TestPdfDevice(skiatest::Reporter* reporter,
+                          CanvasTestStep* testStep) {
+    SkISize pageSize = SkISize::Make(kWidth, kHeight);
+    SkPDFDevice device(pageSize, pageSize, SkMatrix::I());
+    SkCanvas canvas(&device);
+    testStep->setAssertMessageFormat(kPdfAssertMessageFormat);
+    testStep->draw(&canvas, reporter);
+    SkPDFDocument doc;
+    doc.appendPage(&device);
+    SkDynamicMemoryWStream stream;
+    doc.emitPDF(&stream);
 }
 
-static void TestDeferredCanvasStateConsistency(
-    skiatest::Reporter* reporter,
-    CanvasTestStep* testStep,
-    const SkCanvas& referenceCanvas) {
+// The following class groups static functions that need to access
+// the privates members of SkDeferredCanvas
+class SkDeferredCanvasTester {
+public:
+    static void TestDeferredCanvasStateConsistency(
+        skiatest::Reporter* reporter,
+        CanvasTestStep* testStep,
+        const SkCanvas& referenceCanvas, bool silent) {
 
-    SkBitmap deferredStore;
-    createBitmap(&deferredStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
-    SkDevice deferredDevice(deferredStore);
-    SkDeferredCanvas deferredCanvas(&deferredDevice);
-    testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat);
-    testStep->draw(&deferredCanvas, reporter);
-    testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
-        testStep);
+        SkBitmap deferredStore;
+        createBitmap(&deferredStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
+        SkDevice deferredDevice(deferredStore);
+        SkDeferredCanvas deferredCanvas(&deferredDevice);
+        testStep->setAssertMessageFormat(kDeferredDrawAssertMessageFormat);
+        testStep->draw(&deferredCanvas, reporter);
+        testStep->setAssertMessageFormat(kDeferredPreFlushAssertMessageFormat);
+        AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
+            testStep);
 
-    // Verified that deferred canvas state is not affected by flushing
-    // pending draw operations
+        if (silent) {
+            deferredCanvas.silentFlush();
+        } else {
+            deferredCanvas.flush();
+        }
 
-    // The following test code is commented out because it currently fails.
-    // Issue: http://code.google.com/p/skia/issues/detail?id=496
-    /*
-    deferredCanvas.flush();
-    testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat);
-    AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
-        testStep);
-    */
-}
+        testStep->setAssertMessageFormat(
+            silent ? kDeferredPostSilentFlushPlaybackAssertMessageFormat :
+            kDeferredPostFlushPlaybackAssertMessageFormat);
+        AssertCanvasStatesEqual(reporter,
+            deferredCanvas.immediateCanvas(),
+            &referenceCanvas, testStep);
 
+        // Verified that deferred canvas state is not affected by flushing
+        // pending draw operations
+
+        // The following test code is commented out because it currently fails.
+        // Issue: http://code.google.com/p/skia/issues/detail?id=496
+        /*
+        testStep->setAssertMessageFormat(kDeferredPostFlushAssertMessageFormat);
+        AssertCanvasStatesEqual(reporter, &deferredCanvas, &referenceCanvas,
+            testStep);
+        */
+    }
+};
+
+// unused
 static void TestProxyCanvasStateConsistency(
     skiatest::Reporter* reporter,
     CanvasTestStep* testStep,
@@ -676,6 +833,7 @@
         testStep);
 }
 
+// unused
 static void TestNWayCanvasStateConsistency(
     skiatest::Reporter* reporter,
     CanvasTestStep* testStep,
@@ -717,7 +875,7 @@
  * that the all canvas derivatives report the same state as an SkCanvas
  * after having executed the test step.
  */
-static void TestOverrideStateConsistency(skiatest::Reporter* reporter, 
+static void TestOverrideStateConsistency(skiatest::Reporter* reporter,
                                          CanvasTestStep* testStep) {
     SkBitmap referenceStore;
     createBitmap(&referenceStore, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
@@ -726,21 +884,30 @@
     testStep->setAssertMessageFormat(kCanvasDrawAssertMessageFormat);
     testStep->draw(&referenceCanvas, reporter);
 
-    TestPictureStateConsistency(reporter, testStep, referenceCanvas);
-    TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas);
+    SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, false);
 
-    // The following test code is commented out because SkProxyCanvas is
+    SkDeferredCanvasTester::TestDeferredCanvasStateConsistency(reporter, testStep, referenceCanvas, true);
+
+    // The following test code is disabled because SkProxyCanvas is
     // missing a lot of virtual overrides on get* methods, which are used
     // to verify canvas state.
     // Issue: http://code.google.com/p/skia/issues/detail?id=500
-    
-    //TestProxyCanvasStateConsistency(reporter, testStep, referenceCanvas);
 
-    // The following test code is commented out because SkNWayCanvas does not
+    if (false) { // avoid bit rot, suppress warning
+        TestProxyCanvasStateConsistency(reporter, testStep, referenceCanvas);
+    }
+
+    // The following test code is disabled because SkNWayCanvas does not
     // report correct clipping and device bounds information
     // Issue: http://code.google.com/p/skia/issues/detail?id=501
-    
-    //TestNWayCanvasStateConsistency(reporter, testStep, referenceCanvas);
+
+    if (false) { // avoid bit rot, suppress warning
+        TestNWayCanvasStateConsistency(reporter, testStep, referenceCanvas);
+    }
+
+    if (false) { // avoid bit rot, suppress warning
+        test_clipVisitor(reporter, &referenceCanvas);
+    }
 }
 
 static void TestCanvas(skiatest::Reporter* reporter) {
@@ -750,11 +917,15 @@
 
     for (int testStep = 0; testStep < testStepArray().count(); testStep++) {
         TestOverrideStateConsistency(reporter, testStepArray()[testStep]);
-        SkPictureTester::TestPictureSerializationRoundTrip(reporter, 
-            testStepArray()[testStep]);
         SkPictureTester::TestPictureFlattenedObjectReuse(reporter,
-            testStepArray()[testStep]);
+            testStepArray()[testStep], 0);
+        if (testStepArray()[testStep]->enablePdfTesting()) {
+            TestPdfDevice(reporter, testStepArray()[testStep]);
+        }
     }
+
+    // Explicitly call reset(), so we don't leak the pixels (since kTestBitmap is a global)
+    kTestBitmap.reset();
 }
 
 #include "TestClassDef.h"
diff --git a/tests/ChecksumTest.cpp b/tests/ChecksumTest.cpp
new file mode 100644
index 0000000..0319490
--- /dev/null
+++ b/tests/ChecksumTest.cpp
@@ -0,0 +1,185 @@
+
+/*
+ * 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 "Test.h"
+
+#include "SkBitmap.h"
+#include "SkBitmapChecksummer.h"
+#include "SkChecksum.h"
+#include "SkCityHash.h"
+#include "SkColor.h"
+
+// Word size that is large enough to hold results of any checksum type.
+typedef uint64_t checksum_result;
+
+namespace skiatest {
+    class ChecksumTestClass : public Test {
+    public:
+        static Test* Factory(void*) {return SkNEW(ChecksumTestClass); }
+    protected:
+        virtual void onGetName(SkString* name) { name->set("Checksum"); }
+        virtual void onRun(Reporter* reporter) {
+            this->fReporter = reporter;
+            RunTest();
+        }
+    private:
+        enum Algorithm {
+            kSkChecksum,
+            kSkCityHash32,
+            kSkCityHash64
+        };
+
+        // Call Compute(data, size) on the appropriate checksum algorithm,
+        // depending on this->fWhichAlgorithm.
+        checksum_result ComputeChecksum(const char *data, size_t size) {
+            switch(fWhichAlgorithm) {
+            case kSkChecksum:
+                REPORTER_ASSERT_MESSAGE(fReporter,
+                                        reinterpret_cast<uintptr_t>(data) % 4 == 0,
+                                        "test data pointer is not 32-bit aligned");
+                REPORTER_ASSERT_MESSAGE(fReporter, SkIsAlign4(size),
+                                        "test data size is not 32-bit aligned");
+                return SkChecksum::Compute(reinterpret_cast<const uint32_t *>(data), size);
+            case kSkCityHash32:
+                return SkCityHash::Compute32(data, size);
+            case kSkCityHash64:
+                return SkCityHash::Compute64(data, size);
+            default:
+                SkString message("fWhichAlgorithm has unknown value ");
+                message.appendf("%d", fWhichAlgorithm);
+                fReporter->reportFailed(message);
+            }
+            // we never get here
+            return 0;
+        }
+
+        // Confirm that the checksum algorithm (specified by fWhichAlgorithm)
+        // generates the same results if called twice over the same data.
+        void TestChecksumSelfConsistency(size_t buf_size) {
+            SkAutoMalloc storage(buf_size);
+            char* ptr = reinterpret_cast<char *>(storage.get());
+
+            REPORTER_ASSERT(fReporter,
+                            GetTestDataChecksum(8, 0) ==
+                            GetTestDataChecksum(8, 0));
+            REPORTER_ASSERT(fReporter,
+                            GetTestDataChecksum(8, 0) !=
+                            GetTestDataChecksum(8, 1));
+
+            sk_bzero(ptr, buf_size);
+            checksum_result prev = 0;
+
+            // assert that as we change values (from 0 to non-zero) in
+            // our buffer, we get a different value
+            for (size_t i = 0; i < buf_size; ++i) {
+                ptr[i] = (i & 0x7f) + 1; // need some non-zero value here
+
+                // Try checksums of different-sized chunks, but always
+                // 32-bit aligned and big enough to contain all the
+                // nonzero bytes.  (Remaining bytes will still be zero
+                // from the initial sk_bzero() call.)
+                size_t checksum_size = (((i/4)+1)*4);
+                REPORTER_ASSERT(fReporter, checksum_size <= buf_size);
+
+                checksum_result curr = ComputeChecksum(ptr, checksum_size);
+                REPORTER_ASSERT(fReporter, prev != curr);
+                checksum_result again = ComputeChecksum(ptr, checksum_size);
+                REPORTER_ASSERT(fReporter, again == curr);
+                prev = curr;
+            }
+        }
+
+        // Return the checksum of a buffer of bytes 'len' long.
+        // The pattern of values within the buffer will be consistent
+        // for every call, based on 'seed'.
+        checksum_result GetTestDataChecksum(size_t len, char seed=0) {
+            SkAutoMalloc storage(len);
+            char* start = reinterpret_cast<char *>(storage.get());
+            char* ptr = start;
+            for (size_t i = 0; i < len; ++i) {
+                *ptr++ = ((seed+i) & 0x7f);
+            }
+            checksum_result result = ComputeChecksum(start, len);
+            return result;
+        }
+
+        // Fill in bitmap with test data.
+        void CreateTestBitmap(SkBitmap &bitmap, SkBitmap::Config config, int width, int height,
+                              SkColor color) {
+            bitmap.setConfig(config, width, height);
+            REPORTER_ASSERT(fReporter, bitmap.allocPixels());
+            bitmap.setIsOpaque(true);
+            bitmap.eraseColor(color);
+        }
+
+        void RunTest() {
+            // Test self-consistency of checksum algorithms.
+            fWhichAlgorithm = kSkChecksum;
+            TestChecksumSelfConsistency(128);
+            fWhichAlgorithm = kSkCityHash32;
+            TestChecksumSelfConsistency(128);
+            fWhichAlgorithm = kSkCityHash64;
+            TestChecksumSelfConsistency(128);
+
+            // Test checksum results that should be consistent across
+            // versions and platforms.
+            fWhichAlgorithm = kSkChecksum;
+            REPORTER_ASSERT(fReporter, ComputeChecksum(NULL, 0) == 0);
+            fWhichAlgorithm = kSkCityHash32;
+            REPORTER_ASSERT(fReporter, ComputeChecksum(NULL, 0) == 0xdc56d17a);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(4)   == 0x616e1132);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(8)   == 0xeb0fd2d6);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(128) == 0x5321e430);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(132) == 0x924a10e4);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(256) == 0xd4de9dc9);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(260) == 0xecf0325d);
+            fWhichAlgorithm = kSkCityHash64;
+            REPORTER_ASSERT(fReporter, ComputeChecksum(NULL, 0) == 0x9ae16a3b2f90404fULL);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(4)   == 0x82bffd898958e540ULL);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(8)   == 0xad5a13e1e8e93b98ULL);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(128) == 0x10b153630af1f395ULL);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(132) == 0x7db71dc4adcc6647ULL);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(256) == 0xeee763519b91b010ULL);
+            REPORTER_ASSERT(fReporter, GetTestDataChecksum(260) == 0x2fe19e0b2239bc23ULL);
+
+            // TODO: note the weakness exposed by these collisions...
+            // We need to improve the SkChecksum algorithm.
+            // We would prefer that these asserts FAIL!
+            // Filed as https://code.google.com/p/skia/issues/detail?id=981
+            // ('SkChecksum algorithm allows for way too many collisions')
+            fWhichAlgorithm = kSkChecksum;
+            REPORTER_ASSERT(fReporter,
+                GetTestDataChecksum(128) == GetTestDataChecksum(256));
+            REPORTER_ASSERT(fReporter,
+                GetTestDataChecksum(132) == GetTestDataChecksum(260));
+
+            // Test SkBitmapChecksummer
+            SkBitmap bitmap;
+            // initial test case
+            CreateTestBitmap(bitmap, SkBitmap::kARGB_8888_Config, 333, 555, SK_ColorBLUE);
+            REPORTER_ASSERT(fReporter,
+                            SkBitmapChecksummer::Compute64(bitmap) == 0x18f9df68b1b02f38ULL);
+            // same pixel data but different dimensions should yield a different checksum
+            CreateTestBitmap(bitmap, SkBitmap::kARGB_8888_Config, 555, 333, SK_ColorBLUE);
+            REPORTER_ASSERT(fReporter,
+                            SkBitmapChecksummer::Compute64(bitmap) == 0x6b0298183f786c8eULL);
+            // same dimensions but different color should yield a different checksum
+            CreateTestBitmap(bitmap, SkBitmap::kARGB_8888_Config, 555, 333, SK_ColorGREEN);
+            REPORTER_ASSERT(fReporter,
+                            SkBitmapChecksummer::Compute64(bitmap) == 0xc6b4b3f6fadaaf37ULL);
+            // same pixel colors in a different config should yield the same checksum
+            CreateTestBitmap(bitmap, SkBitmap::kARGB_4444_Config, 555, 333, SK_ColorGREEN);
+            REPORTER_ASSERT(fReporter,
+                            SkBitmapChecksummer::Compute64(bitmap) == 0xc6b4b3f6fadaaf37ULL);
+        }
+
+        Reporter* fReporter;
+        Algorithm fWhichAlgorithm;
+    };
+
+    static TestRegistry gReg(ChecksumTestClass::Factory);
+}
diff --git a/tests/ClampRangeTest.cpp b/tests/ClampRangeTest.cpp
index 226d030..fa3804e 100644
--- a/tests/ClampRangeTest.cpp
+++ b/tests/ClampRangeTest.cpp
@@ -6,7 +6,7 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
-#include "SkClampRange.h"
+#include "gradients/SkClampRange.h"
 #include "SkRandom.h"
 
 static skiatest::Reporter* gReporter;
@@ -96,7 +96,7 @@
     test_range(0xFFFF, 0, 20);
     test_range(-ff(2), 0, 20);
     test_range( ff(2), 0, 20);
-    
+
     test_range(-10, 1, 20);
     test_range(10, -1, 20);
     test_range(-10, 3, 20);
@@ -108,7 +108,7 @@
     test_range(ff(1)/2, ff(-16384), 100);
 
     SkRandom rand;
-    
+
     // test non-overflow cases
     for (int i = 0; i < 1000000; i++) {
         SkFixed fx = rand.nextS() >> 1;
@@ -117,7 +117,7 @@
         SkFixed dx = (sx - fx) / count;
         test_range(fx, dx, count);
     }
-    
+
     // test overflow cases
     for (int i = 0; i < 100000; i++) {
         SkFixed fx = rand.nextS();
diff --git a/tests/ClipCacheTest.cpp b/tests/ClipCacheTest.cpp
new file mode 100644
index 0000000..c80ba63
--- /dev/null
+++ b/tests/ClipCacheTest.cpp
@@ -0,0 +1,229 @@
+/*
+ * 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 "Test.h"
+// This is a GR test
+#if SK_SUPPORT_GPU
+#include "SkGpuDevice.h"
+#include "../../src/gpu/GrClipMaskManager.h"
+
+static const int X_SIZE = 12;
+static const int Y_SIZE = 12;
+
+////////////////////////////////////////////////////////////////////////////////
+// note: this is unused
+static GrTexture* createTexture(GrContext* context) {
+    unsigned char textureData[X_SIZE][Y_SIZE][4];
+
+    memset(textureData, 0, 4* X_SIZE * Y_SIZE);
+
+    GrTextureDesc desc;
+
+    // let Skia know we will be using this texture as a render target
+    desc.fFlags     = kRenderTarget_GrTextureFlagBit;
+    desc.fConfig    = kSkia8888_PM_GrPixelConfig;
+    desc.fWidth     = X_SIZE;
+    desc.fHeight    = Y_SIZE;
+
+    // We are initializing the texture with zeros here
+    GrTexture* texture = context->createUncachedTexture(desc, textureData, 0);
+    if (!texture) {
+        return NULL;
+    }
+
+    return texture;
+}
+
+// Ensure that the 'getConservativeBounds' calls are returning bounds clamped
+// to the render target
+static void test_clip_bounds(skiatest::Reporter* reporter, GrContext* context) {
+
+    static const int kXSize = 100;
+    static const int kYSize = 100;
+
+    GrTextureDesc desc;
+    desc.fFlags     = kRenderTarget_GrTextureFlagBit;
+    desc.fConfig    = kAlpha_8_GrPixelConfig;
+    desc.fWidth     = kXSize;
+    desc.fHeight    = kYSize;
+
+    GrTexture* texture = context->createUncachedTexture(desc, NULL, 0);
+    if (!texture) {
+        return;
+    }
+
+    GrAutoUnref au(texture);
+
+    SkIRect intScreen = SkIRect::MakeWH(kXSize, kYSize);
+    SkRect screen = SkRect::MakeWH(SkIntToScalar(kXSize),
+                                   SkIntToScalar(kYSize));
+    SkRect clipRect(screen);
+    clipRect.outset(10, 10);
+
+    // create a clip stack that will (trivially) reduce to a single rect that
+    // is larger than the screen
+    SkClipStack stack;
+    stack.clipDevRect(clipRect, SkRegion::kReplace_Op, false);
+
+    bool isIntersectionOfRects = true;
+    SkRect devStackBounds;
+
+    stack.getConservativeBounds(0, 0, kXSize, kYSize,
+                                &devStackBounds,
+                                &isIntersectionOfRects);
+
+    // make sure that the SkClipStack is behaving itself
+    REPORTER_ASSERT(reporter, screen == devStackBounds);
+    REPORTER_ASSERT(reporter, isIntersectionOfRects);
+
+    // wrap the SkClipStack in a GrClipData
+    GrClipData clipData;
+    clipData.fClipStack = &stack;
+
+    SkIRect devGrClipDataBound;
+    clipData.getConservativeBounds(texture,
+                                   &devGrClipDataBound,
+                                   &isIntersectionOfRects);
+
+    // make sure that GrClipData is behaving itself
+    REPORTER_ASSERT(reporter, intScreen == devGrClipDataBound);
+    REPORTER_ASSERT(reporter, isIntersectionOfRects);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// verify that the top state of the stack matches the passed in state
+static void check_state(skiatest::Reporter* reporter,
+                        const GrClipMaskCache& cache,
+                        const SkClipStack& clip,
+                        GrTexture* mask,
+                        const GrIRect& bound) {
+    SkClipStack cacheClip;
+    REPORTER_ASSERT(reporter, clip.getTopmostGenID() == cache.getLastClipGenID());
+
+    REPORTER_ASSERT(reporter, mask == cache.getLastMask());
+
+    GrIRect cacheBound;
+    cache.getLastBound(&cacheBound);
+    REPORTER_ASSERT(reporter, bound == cacheBound);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// basic test of the cache's base functionality:
+//  push, pop, set, canReuse & getters
+static void test_cache(skiatest::Reporter* reporter, GrContext* context) {
+
+    if (false) { // avoid bit rot, suppress warning
+        createTexture(context);
+    }
+    GrClipMaskCache cache;
+
+    cache.setContext(context);
+
+    SkClipStack emptyClip;
+    emptyClip.reset();
+
+    GrIRect emptyBound;
+    emptyBound.setEmpty();
+
+    // check initial state
+    check_state(reporter, cache, emptyClip, NULL, emptyBound);
+
+    // set the current state
+    GrIRect bound1;
+    bound1.set(0, 0, 100, 100);
+
+    SkClipStack clip1(bound1);
+
+    GrTextureDesc desc;
+    desc.fFlags = kRenderTarget_GrTextureFlagBit;
+    desc.fWidth = X_SIZE;
+    desc.fHeight = Y_SIZE;
+    desc.fConfig = kSkia8888_PM_GrPixelConfig;
+
+    cache.acquireMask(clip1.getTopmostGenID(), desc, bound1);
+
+    GrTexture* texture1 = cache.getLastMask();
+    REPORTER_ASSERT(reporter, texture1);
+    if (NULL == texture1) {
+        return;
+    }
+
+    // check that the set took
+    check_state(reporter, cache, clip1, texture1, bound1);
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+
+    // push the state
+    cache.push();
+
+    // verify that the pushed state is initially empty
+    check_state(reporter, cache, emptyClip, NULL, emptyBound);
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+
+    // modify the new state
+    GrIRect bound2;
+    bound2.set(-10, -10, 10, 10);
+
+    SkClipStack clip2(bound2);
+
+    cache.acquireMask(clip2.getTopmostGenID(), desc, bound2);
+
+    GrTexture* texture2 = cache.getLastMask();
+    REPORTER_ASSERT(reporter, texture2);
+    if (NULL == texture2) {
+        return;
+    }
+
+    // check that the changes took
+    check_state(reporter, cache, clip2, texture2, bound2);
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
+
+    // check to make sure canReuse works
+    REPORTER_ASSERT(reporter, cache.canReuse(clip2.getTopmostGenID(), bound2));
+    REPORTER_ASSERT(reporter, !cache.canReuse(clip1.getTopmostGenID(), bound1));
+
+    // pop the state
+    cache.pop();
+
+    // verify that the old state is restored
+    check_state(reporter, cache, clip1, texture1, bound1);
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
+
+    // manually clear the state
+    cache.reset();
+
+    // verify it is now empty
+    check_state(reporter, cache, emptyClip, NULL, emptyBound);
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
+
+    // pop again - so there is no state
+    cache.pop();
+
+#if !defined(SK_DEBUG)
+    // verify that the getters don't crash
+    // only do in release since it generates asserts in debug
+    check_state(reporter, cache, emptyClip, NULL, emptyBound);
+#endif
+    REPORTER_ASSERT(reporter, texture1->getRefCnt());
+    REPORTER_ASSERT(reporter, texture2->getRefCnt());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+static void TestClipCache(skiatest::Reporter* reporter, GrContext* context) {
+
+    test_cache(reporter, context);
+    test_clip_bounds(reporter, context);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+#include "TestClassDef.h"
+DEFINE_GPUTESTCLASS("ClipCache", ClipCacheTestClass, TestClipCache)
+
+#endif
diff --git a/tests/ClipCubicTest.cpp b/tests/ClipCubicTest.cpp
index 491d0e5..e980781 100644
--- a/tests/ClipCubicTest.cpp
+++ b/tests/ClipCubicTest.cpp
@@ -20,8 +20,8 @@
     bm.setConfig(SkBitmap::kARGB_8888_Config, 64919, 1);
     bm.allocPixels();
     SkCanvas canvas(bm);
-    canvas.clear(0);
-    
+    canvas.clear(SK_ColorTRANSPARENT);
+
     SkPath path;
     path.moveTo(0, 0); path.lineTo(1, 0); path.lineTo(33, 1);
     SkPaint paint;
@@ -81,7 +81,7 @@
     SkPoint clipped[4], shouldbe[4];
     SkIRect clipRect;
     bool success;
-    const float tol = SkFloatToScalar(1e-4);
+    const float tol = SkFloatToScalar(1e-4f);
 
     // Test no clip, with plenty of room.
     clipRect.set(-2, -2, 6, 14);
@@ -119,9 +119,9 @@
     success = clipper.clipCubic(crv, clipped);
     REPORTER_ASSERT(reporter, success == true);
     REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
-        0.5126125216, 1,
-        1.841195941,  4.337081432,
-        1.297019958,  10.19801331,
+        0.5126125216f, 1,
+        1.841195941f,  4.337081432f,
+        1.297019958f,  10.19801331f,
         4,            12,
         shouldbe), tol));
 
@@ -131,9 +131,9 @@
     success = clipper.clipCubic(crv, clipped);
     REPORTER_ASSERT(reporter, success == true);
     REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
-        00.8412352204, 2,
-        1.767683744,   5.400758266,
-        1.55052948,    10.36701965,
+        00.8412352204f, 2,
+        1.767683744f,   5.400758266f,
+        1.55052948f,    10.36701965f,
         4,             12,
         shouldbe), tol));
 
@@ -144,9 +144,9 @@
     REPORTER_ASSERT(reporter, success == true);
     REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
         0,           0,
-        1.742904663, 2.614356995,
-        1.207521796, 8.266430855,
-        3.026495695, 11,
+        1.742904663f, 2.614356995f,
+        1.207521796f, 8.266430855f,
+        3.026495695f, 11,
         shouldbe), tol));
 
     // Test clip at 10.
@@ -156,9 +156,9 @@
     REPORTER_ASSERT(reporter, success == true);
     REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
         0,           0,
-        1.551193237, 2.326789856,
-        1.297736168, 7.059780121,
-        2.505550385, 10,
+        1.551193237f, 2.326789856f,
+        1.297736168f, 7.059780121f,
+        2.505550385f, 10,
         shouldbe), tol));
 
     test_giantClip();
diff --git a/tests/ClipStackTest.cpp b/tests/ClipStackTest.cpp
index 40738df..4709d22 100644
--- a/tests/ClipStackTest.cpp
+++ b/tests/ClipStackTest.cpp
@@ -6,16 +6,26 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
+#if SK_SUPPORT_GPU
+    #include "GrReducedClip.h"
+#endif
 #include "SkClipStack.h"
 #include "SkPath.h"
+#include "SkRandom.h"
 #include "SkRect.h"
+#include "SkRegion.h"
+
 
 static void test_assign_and_comparison(skiatest::Reporter* reporter) {
     SkClipStack s;
     bool doAA = false;
 
+    REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
+
     // Build up a clip stack with a path, an empty clip, and a rect.
     s.save();
+    REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
+
     SkPath p;
     p.moveTo(5, 6);
     p.lineTo(7, 8);
@@ -24,12 +34,16 @@
     s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
 
     s.save();
+    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
+
     SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
     s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
     r = SkRect::MakeLTRB(10, 11, 12, 13);
     s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
 
     s.save();
+    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
+
     r = SkRect::MakeLTRB(14, 15, 16, 17);
     s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
 
@@ -39,47 +53,69 @@
 
     // Test that different save levels triggers not equal.
     s.restore();
+    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
     REPORTER_ASSERT(reporter, s != copy);
 
     // Test that an equal, but not copied version is equal.
     s.save();
+    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
+
     r = SkRect::MakeLTRB(14, 15, 16, 17);
     s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
     REPORTER_ASSERT(reporter, s == copy);
 
     // Test that a different op on one level triggers not equal.
     s.restore();
+    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
     s.save();
+    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
+
     r = SkRect::MakeLTRB(14, 15, 16, 17);
     s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
     REPORTER_ASSERT(reporter, s != copy);
 
     // Test that different state (clip type) triggers not equal.
+    // NO LONGER VALID: if a path contains only a rect, we turn
+    // it into a bare rect for performance reasons (working
+    // around Chromium/JavaScript bad pattern).
+/*
     s.restore();
     s.save();
     SkPath rp;
     rp.addRect(r);
     s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
     REPORTER_ASSERT(reporter, s != copy);
+*/
 
     // Test that different rects triggers not equal.
     s.restore();
+    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
     s.save();
+    REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
+
     r = SkRect::MakeLTRB(24, 25, 26, 27);
     s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
     REPORTER_ASSERT(reporter, s != copy);
 
     // Sanity check
     s.restore();
+    REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
+
     copy.restore();
+    REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
     REPORTER_ASSERT(reporter, s == copy);
     s.restore();
+    REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
     copy.restore();
+    REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
     REPORTER_ASSERT(reporter, s == copy);
 
     // Test that different paths triggers not equal.
     s.restore();
+    REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
     s.save();
+    REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
+
     p.addRect(r);
     s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
     REPORTER_ASSERT(reporter, s != copy);
@@ -87,9 +123,7 @@
 
 static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
                          int count) {
-    REPORTER_ASSERT(reporter, count == stack.getSaveCount());
-
-    SkClipStack::B2FIter iter(stack);
+    SkClipStack::B2TIter iter(stack);
     int counter = 0;
     while (iter.next()) {
         counter += 1;
@@ -97,9 +131,790 @@
     REPORTER_ASSERT(reporter, count == counter);
 }
 
+// Exercise the SkClipStack's bottom to top and bidirectional iterators
+// (including the skipToTopmost functionality)
+static void test_iterators(skiatest::Reporter* reporter) {
+    SkClipStack stack;
+
+    static const SkRect gRects[] = {
+        { 0,   0,  40,  40 },
+        { 60,  0, 100,  40 },
+        { 0,  60,  40, 100 },
+        { 60, 60, 100, 100 }
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
+        // the union op will prevent these from being fused together
+        stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
+    }
+
+    assert_count(reporter, stack, 4);
+
+    // bottom to top iteration
+    {
+        const SkClipStack::Element* element = NULL;
+
+        SkClipStack::B2TIter iter(stack);
+        int i;
+
+        for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
+            REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
+            REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
+        }
+
+        SkASSERT(i == 4);
+    }
+
+    // top to bottom iteration
+    {
+        const SkClipStack::Element* element = NULL;
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
+        int i;
+
+        for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
+            REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
+            REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
+        }
+
+        SkASSERT(i == -1);
+    }
+
+    // skipToTopmost
+    {
+        const SkClipStack::Element* element = NULL;
+
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+
+        element = iter.skipToTopmost(SkRegion::kUnion_Op);
+        REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
+        REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
+    }
+}
+
+// Exercise the SkClipStack's getConservativeBounds computation
+static void test_bounds(skiatest::Reporter* reporter, bool useRects) {
+
+    static const int gNumCases = 20;
+    static const SkRect gAnswerRectsBW[gNumCases] = {
+        // A op B
+        { 40, 40, 50, 50 },
+        { 10, 10, 50, 50 },
+        { 10, 10, 80, 80 },
+        { 10, 10, 80, 80 },
+        { 40, 40, 80, 80 },
+
+        // invA op B
+        { 40, 40, 80, 80 },
+        { 0, 0, 100, 100 },
+        { 0, 0, 100, 100 },
+        { 0, 0, 100, 100 },
+        { 40, 40, 50, 50 },
+
+        // A op invB
+        { 10, 10, 50, 50 },
+        { 40, 40, 50, 50 },
+        { 0, 0, 100, 100 },
+        { 0, 0, 100, 100 },
+        { 0, 0, 100, 100 },
+
+        // invA op invB
+        { 0, 0, 100, 100 },
+        { 40, 40, 80, 80 },
+        { 0, 0, 100, 100 },
+        { 10, 10, 80, 80 },
+        { 10, 10, 50, 50 },
+    };
+
+    static const SkRegion::Op gOps[] = {
+        SkRegion::kIntersect_Op,
+        SkRegion::kDifference_Op,
+        SkRegion::kUnion_Op,
+        SkRegion::kXOR_Op,
+        SkRegion::kReverseDifference_Op
+    };
+
+    SkRect rectA, rectB;
+
+    rectA.iset(10, 10, 50, 50);
+    rectB.iset(40, 40, 80, 80);
+
+    SkPath clipA, clipB;
+
+    clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
+    clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
+
+    SkClipStack stack;
+    SkRect devClipBound;
+    bool isIntersectionOfRects = false;
+
+    int testCase = 0;
+    int numBitTests = useRects ? 1 : 4;
+    for (int invBits = 0; invBits < numBitTests; ++invBits) {
+        for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
+
+            stack.save();
+            bool doInvA = SkToBool(invBits & 1);
+            bool doInvB = SkToBool(invBits & 2);
+
+            clipA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
+                                       SkPath::kEvenOdd_FillType);
+            clipB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
+                                       SkPath::kEvenOdd_FillType);
+
+            if (useRects) {
+                stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
+                stack.clipDevRect(rectB, gOps[op], false);
+            } else {
+                stack.clipDevPath(clipA, SkRegion::kIntersect_Op, false);
+                stack.clipDevPath(clipB, gOps[op], false);
+            }
+
+            REPORTER_ASSERT(reporter, !stack.isWideOpen());
+
+            stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
+                                        &isIntersectionOfRects);
+
+            if (useRects) {
+                REPORTER_ASSERT(reporter, isIntersectionOfRects ==
+                        (gOps[op] == SkRegion::kIntersect_Op));
+            } else {
+                REPORTER_ASSERT(reporter, !isIntersectionOfRects);
+            }
+
+            SkASSERT(testCase < gNumCases);
+            REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
+            ++testCase;
+
+            stack.restore();
+        }
+    }
+}
+
+// Test out 'isWideOpen' entry point
+static void test_isWideOpen(skiatest::Reporter* reporter) {
+
+    SkRect rectA, rectB;
+
+    rectA.iset(10, 10, 40, 40);
+    rectB.iset(50, 50, 80, 80);
+
+    // Stack should initially be wide open
+    {
+        SkClipStack stack;
+
+        REPORTER_ASSERT(reporter, stack.isWideOpen());
+    }
+
+    // Test out case where the user specifies a union that includes everything
+    {
+        SkClipStack stack;
+
+        SkPath clipA, clipB;
+
+        clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
+        clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
+
+        clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
+        clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
+
+        stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
+        stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
+
+        REPORTER_ASSERT(reporter, stack.isWideOpen());
+    }
+
+    // Test out union w/ a wide open clip
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
+
+        REPORTER_ASSERT(reporter, stack.isWideOpen());
+    }
+
+    // Test out empty difference from a wide open clip
+    {
+        SkClipStack stack;
+
+        SkRect emptyRect;
+        emptyRect.setEmpty();
+
+        stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
+
+        REPORTER_ASSERT(reporter, stack.isWideOpen());
+    }
+
+    // Test out return to wide open
+    {
+        SkClipStack stack;
+
+        stack.save();
+
+        stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
+
+        REPORTER_ASSERT(reporter, !stack.isWideOpen());
+
+        stack.restore();
+
+        REPORTER_ASSERT(reporter, stack.isWideOpen());
+    }
+}
+
+static int count(const SkClipStack& stack) {
+
+    SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
+
+    const SkClipStack::Element* element = NULL;
+    int count = 0;
+
+    for (element = iter.prev(); element; element = iter.prev(), ++count) {
+        ;
+    }
+
+    return count;
+}
+
+static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
+    // non-intersecting rectangles
+    SkRect rect  = SkRect::MakeLTRB(0, 0, 10, 10);
+
+    SkPath path;
+    path.addRect(rect);
+    path.toggleInverseFillType();
+    SkClipStack stack;
+    stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+
+    SkRect bounds;
+    SkClipStack::BoundsType boundsType;
+    stack.getBounds(&bounds, &boundsType);
+    REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
+    REPORTER_ASSERT(reporter, bounds == rect);
+}
+
+// Test out SkClipStack's merging of rect clips. In particular exercise
+// merging of aa vs. bw rects.
+static void test_rect_merging(skiatest::Reporter* reporter) {
+
+    SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50);
+    SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
+
+    SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
+    SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60);
+
+    SkRect bound;
+    SkClipStack::BoundsType type;
+    bool isIntersectionOfRects;
+
+    // all bw overlapping - should merge
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
+
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
+
+        REPORTER_ASSERT(reporter, 1 == count(stack));
+
+        stack.getBounds(&bound, &type, &isIntersectionOfRects);
+
+        REPORTER_ASSERT(reporter, isIntersectionOfRects);
+    }
+
+    // all aa overlapping - should merge
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
+
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
+
+        REPORTER_ASSERT(reporter, 1 == count(stack));
+
+        stack.getBounds(&bound, &type, &isIntersectionOfRects);
+
+        REPORTER_ASSERT(reporter, isIntersectionOfRects);
+    }
+
+    // mixed overlapping - should _not_ merge
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
+
+        stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
+
+        REPORTER_ASSERT(reporter, 2 == count(stack));
+
+        stack.getBounds(&bound, &type, &isIntersectionOfRects);
+
+        REPORTER_ASSERT(reporter, !isIntersectionOfRects);
+    }
+
+    // mixed nested (bw inside aa) - should merge
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
+
+        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
+
+        REPORTER_ASSERT(reporter, 1 == count(stack));
+
+        stack.getBounds(&bound, &type, &isIntersectionOfRects);
+
+        REPORTER_ASSERT(reporter, isIntersectionOfRects);
+    }
+
+    // mixed nested (aa inside bw) - should merge
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
+
+        stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
+
+        REPORTER_ASSERT(reporter, 1 == count(stack));
+
+        stack.getBounds(&bound, &type, &isIntersectionOfRects);
+
+        REPORTER_ASSERT(reporter, isIntersectionOfRects);
+    }
+
+    // reverse nested (aa inside bw) - should _not_ merge
+    {
+        SkClipStack stack;
+
+        stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
+
+        stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
+
+        REPORTER_ASSERT(reporter, 2 == count(stack));
+
+        stack.getBounds(&bound, &type, &isIntersectionOfRects);
+
+        REPORTER_ASSERT(reporter, !isIntersectionOfRects);
+    }
+}
+
+static void test_quickContains(skiatest::Reporter* reporter) {
+    SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
+    SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
+    SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
+    SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
+    SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
+
+    SkPath insideCircle;
+    insideCircle.addCircle(25, 25, 5);
+    SkPath intersectingCircle;
+    intersectingCircle.addCircle(25, 40, 10);
+    SkPath outsideCircle;
+    outsideCircle.addCircle(25, 25, 50);
+    SkPath nonIntersectingCircle;
+    nonIntersectingCircle.addCircle(100, 100, 5);
+
+    {
+        SkClipStack stack;
+        stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
+        // return false because quickContains currently does not care for kDifference_Op
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    // Replace Op tests
+    {
+        SkClipStack stack;
+        stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
+        REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+        stack.save(); // To prevent in-place substitution by replace OP
+        stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
+        REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+        stack.restore();
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
+        stack.save(); // To prevent in-place substitution by replace OP
+        stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+        stack.restore();
+    }
+
+    // Verify proper traversal of multi-element clip
+    {
+        SkClipStack stack;
+        stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+        // Use a path for second clip to prevent in-place intersection
+        stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    // Intersect Op tests with rectangles
+    {
+        SkClipStack stack;
+        stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    // Intersect Op tests with circle paths
+    {
+        SkClipStack stack;
+        stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    // Intersect Op tests with inverse filled rectangles
+    {
+        SkClipStack stack;
+        SkPath path;
+        path.addRect(outsideRect);
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        SkPath path;
+        path.addRect(insideRect);
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        SkPath path;
+        path.addRect(intersectingRect);
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        SkPath path;
+        path.addRect(nonIntersectingRect);
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+    }
+
+    // Intersect Op tests with inverse filled circles
+    {
+        SkClipStack stack;
+        SkPath path = outsideCircle;
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        SkPath path = insideCircle;
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        SkPath path = intersectingCircle;
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
+    }
+
+    {
+        SkClipStack stack;
+        SkPath path = nonIntersectingCircle;
+        path.toggleInverseFillType();
+        stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
+        REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+#if SK_SUPPORT_GPU
+// Functions that add a shape to the clip stack. The shape is computed from a rectangle.
+// AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
+// stack. A fractional edge repeated in different elements may be rasterized fewer times using the
+// reduced stack.
+typedef void (*AddElementFunc) (const SkRect& rect,
+                                bool invert,
+                                SkRegion::Op op,
+                                SkClipStack* stack);
+
+static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
+    SkPath path;
+    SkScalar rx = rect.width() / 10;
+    SkScalar ry = rect.height() / 20;
+    path.addRoundRect(rect, rx, ry);
+    if (invert) {
+        path.setFillType(SkPath::kInverseWinding_FillType);
+    }
+    stack->clipDevPath(path, op, false);
+};
+
+static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
+    if (invert) {
+        SkPath path;
+        path.addRect(rect);
+        path.setFillType(SkPath::kInverseWinding_FillType);
+        stack->clipDevPath(path, op, false);
+    } else {
+        stack->clipDevRect(rect, op, false);
+    }
+};
+
+static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
+    SkPath path;
+    path.addOval(rect);
+    if (invert) {
+        path.setFillType(SkPath::kInverseWinding_FillType);
+    }
+    stack->clipDevPath(path, op, false);
+};
+
+static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
+    switch (element.getType()) {
+        case SkClipStack::Element::kRect_Type:
+            stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
+            break;
+        case SkClipStack::Element::kPath_Type:
+            stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
+            break;
+        case SkClipStack::Element::kEmpty_Type:
+            SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
+            stack->clipEmpty();
+            break;
+    }
+}
+
+static void add_elem_to_region(const SkClipStack::Element& element,
+                               const SkIRect& bounds,
+                               SkRegion* region) {
+    SkRegion elemRegion;
+    SkRegion boundsRgn(bounds);
+
+    switch (element.getType()) {
+        case SkClipStack::Element::kRect_Type: {
+            SkPath path;
+            path.addRect(element.getRect());
+            elemRegion.setPath(path, boundsRgn);
+            break;
+        }
+        case SkClipStack::Element::kPath_Type:
+            elemRegion.setPath(element.getPath(), boundsRgn);
+            break;
+        case SkClipStack::Element::kEmpty_Type:
+            //
+            region->setEmpty();
+            return;
+    }
+    region->op(elemRegion, element.getOp());
+}
+
+// This can assist with debugging the clip stack reduction code when the test below fails.
+static inline void print_clip(const SkClipStack::Element& element) {
+    static const char* kOpStrs[] = {
+        "DF",
+        "IS",
+        "UN",
+        "XR",
+        "RD",
+        "RP",
+    };
+    if (SkClipStack::Element::kEmpty_Type != element.getType()) {
+        const SkRect& bounds = element.getBounds();
+        bool isRect = SkClipStack::Element::kRect_Type == element.getType();
+        SkDebugf("%s %s %s [%f %f] x [%f %f]\n",
+                 kOpStrs[element.getOp()],
+                 (isRect ? "R" : "P"),
+                 (element.isInverseFilled() ? "I" : " "),
+                 bounds.fLeft, bounds.fRight, bounds.fTop, bounds.fBottom);
+    } else {
+        SkDebugf("EM\n");
+    }
+}
+
+static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
+    // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
+    // they are equal.
+
+    // All the clip elements will be contained within these bounds.
+    static const SkRect kBounds = SkRect::MakeWH(100, 100);
+
+    enum {
+        kNumTests = 200,
+        kMinElemsPerTest = 1,
+        kMaxElemsPerTest = 50,
+    };
+
+    // min/max size of a clip element as a fraction of kBounds.
+    static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
+    static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
+
+    static const SkRegion::Op kOps[] = {
+        SkRegion::kDifference_Op,
+        SkRegion::kIntersect_Op,
+        SkRegion::kUnion_Op,
+        SkRegion::kXOR_Op,
+        SkRegion::kReverseDifference_Op,
+        SkRegion::kReplace_Op,
+    };
+
+    // Replace operations short-circuit the optimizer. We want to make sure that we test this code
+    // path a little bit but we don't want it to prevent us from testing many longer traversals in
+    // the optimizer.
+    static const int kReplaceDiv = 4 * kMaxElemsPerTest;
+
+    // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
+    static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
+
+    static const AddElementFunc kElementFuncs[] = {
+        add_rect,
+        add_round_rect,
+        add_oval,
+    };
+
+    SkRandom r;
+
+    for (int i = 0; i < kNumTests; ++i) {
+        // Randomly generate a clip stack.
+        SkClipStack stack;
+        int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
+        for (int e = 0; e < numElems; ++e) {
+            SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
+            if (op == SkRegion::kReplace_Op) {
+                if (r.nextU() % kReplaceDiv) {
+                    --e;
+                    continue;
+                }
+            }
+
+            // saves can change the clip stack behavior when an element is added.
+            bool doSave = r.nextBool();
+
+            SkSize size = SkSize::Make(
+                SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
+                SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
+
+            SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
+                          SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
+
+            SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
+
+            bool invert = r.nextBiasedBool(kFractionInverted);
+            kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
+            if (doSave) {
+                stack.save();
+            }
+        }
+
+        SkRect inflatedBounds = kBounds;
+        inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
+        SkIRect inflatedIBounds;
+        inflatedBounds.roundOut(&inflatedIBounds);
+
+        typedef GrReducedClip::ElementList ElementList;
+        // Get the reduced version of the stack.
+        ElementList reducedClips;
+
+        GrReducedClip::InitialState initial;
+        SkIRect tBounds;
+        SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL;
+        GrReducedClip::ReduceClipStack(stack,
+                                       inflatedIBounds,
+                                       &reducedClips,
+                                       &initial,
+                                       tightBounds);
+
+        // Build a new clip stack based on the reduced clip elements
+        SkClipStack reducedStack;
+        if (GrReducedClip::kAllOut_InitialState == initial) {
+            // whether the result is bounded or not, the whole plane should start outside the clip.
+            reducedStack.clipEmpty();
+        }
+        for (ElementList::Iter iter = reducedClips.headIter(); NULL != iter.get(); iter.next()) {
+            add_elem_to_stack(*iter.get(), &reducedStack);
+        }
+
+        // GrReducedClipStack assumes that the final result is clipped to the returned bounds
+        if (NULL != tightBounds) {
+            reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
+        }
+
+        // convert both the original stack and reduced stack to SkRegions and see if they're equal
+        SkRegion region;
+        SkRegion reducedRegion;
+
+        region.setRect(inflatedIBounds);
+        const SkClipStack::Element* element;
+        SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
+        while ((element = iter.next())) {
+            add_elem_to_region(*element, inflatedIBounds, &region);
+        }
+
+        reducedRegion.setRect(inflatedIBounds);
+        iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
+        while ((element = iter.next())) {
+            add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
+        }
+
+        REPORTER_ASSERT(reporter, region == reducedRegion);
+    }
+}
+
+#endif
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
 static void TestClipStack(skiatest::Reporter* reporter) {
     SkClipStack stack;
 
+    REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
     assert_count(reporter, stack, 0);
 
     static const SkIRect gRects[] = {
@@ -113,23 +928,33 @@
     }
 
     // all of the above rects should have been intersected, leaving only 1 rect
-    SkClipStack::B2FIter iter(stack);
-    const SkClipStack::B2FIter::Clip* clip = iter.next();
+    SkClipStack::B2TIter iter(stack);
+    const SkClipStack::Element* element = iter.next();
     SkRect answer;
     answer.iset(25, 25, 75, 75);
 
-    REPORTER_ASSERT(reporter, clip);
-    REPORTER_ASSERT(reporter, clip->fRect);
-    REPORTER_ASSERT(reporter, !clip->fPath);
-    REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == clip->fOp);
-    REPORTER_ASSERT(reporter, *clip->fRect == answer);
+    REPORTER_ASSERT(reporter, NULL != element);
+    REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
+    REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
+    REPORTER_ASSERT(reporter, element->getRect() == answer);
     // now check that we only had one in our iterator
     REPORTER_ASSERT(reporter, !iter.next());
 
     stack.reset();
+    REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
     assert_count(reporter, stack, 0);
 
     test_assign_and_comparison(reporter);
+    test_iterators(reporter);
+    test_bounds(reporter, true);        // once with rects
+    test_bounds(reporter, false);       // once with paths
+    test_isWideOpen(reporter);
+    test_rect_merging(reporter);
+    test_rect_inverse_fill(reporter);
+    test_quickContains(reporter);
+#if SK_SUPPORT_GPU
+    test_reduced_clip_stack(reporter);
+#endif
 }
 
 #include "TestClassDef.h"
diff --git a/tests/ClipperTest.cpp b/tests/ClipperTest.cpp
index 7bb2254..b914374 100644
--- a/tests/ClipperTest.cpp
+++ b/tests/ClipperTest.cpp
@@ -10,14 +10,52 @@
 #include "SkLineClipper.h"
 #include "SkEdgeClipper.h"
 
+#include "SkCanvas.h"
+static void test_hairclipping(skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 4, 4);
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorWHITE);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkCanvas canvas(bm);
+    canvas.clipRect(SkRect::MakeWH(SkIntToScalar(4), SkIntToScalar(2)));
+    canvas.drawLine(SkFloatToScalar(1.5f), SkFloatToScalar(1.5f),
+                    SkFloatToScalar(3.5f), SkFloatToScalar(3.5f), paint);
+
+    /**
+     *  We had a bug where we misinterpreted the bottom of the clip, and
+     *  would draw another pixel (to the right in this case) on the same
+     *  last scanline. i.e. we would draw to [2,1], even though this hairline
+     *  should just draw to [1,1], [2,2], [3,3] modulo the clip.
+     *
+     *  The result of this entire draw should be that we only draw to [1,1]
+     *
+     *  Fixed in rev. 3366
+     */
+    for (int y = 0; y < 4; ++y) {
+        for (int x = 0; x < 4; ++x) {
+            bool nonWhite = (1 == y) && (1 == x);
+            SkPMColor c = *bm.getAddr32(x, y);
+            if (nonWhite) {
+                REPORTER_ASSERT(reporter, 0xFFFFFFFF != c);
+            } else {
+                REPORTER_ASSERT(reporter, 0xFFFFFFFF == c);
+            }
+        }
+    }
+}
+
 static void test_edgeclipper(skiatest::Reporter* reporter) {
     SkEdgeClipper clipper;
-    
+
     const SkPoint pts[] = {
-        { SkFloatToScalar(3.0995476e+010),  SkFloatToScalar(42.929779) },
-        { SkFloatToScalar(-3.0995163e+010), SkFloatToScalar(51.050385) },
-        { SkFloatToScalar(-3.0995157e+010), SkFloatToScalar(51.050392) },
-        { SkFloatToScalar(-3.0995134e+010), SkFloatToScalar(51.050400) },
+        { SkFloatToScalar(3.0995476e+010f),  SkFloatToScalar(42.929779f) },
+        { SkFloatToScalar(-3.0995163e+010f), SkFloatToScalar(51.050385f) },
+        { SkFloatToScalar(-3.0995157e+010f), SkFloatToScalar(51.050392f) },
+        { SkFloatToScalar(-3.0995134e+010f), SkFloatToScalar(51.050400f) },
     };
 
     const SkRect clip = { 0, 0, SkIntToScalar(300), SkIntToScalar(200) };
@@ -59,7 +97,7 @@
         }
         REPORTER_ASSERT(reporter, !valid);
     }
-    
+
     static const SkPoint gFull[] = {
         // diagonals, chords
         { L, T }, { R, B },
@@ -83,7 +121,7 @@
         }
         REPORTER_ASSERT(reporter, valid && !memcmp(&gFull[i], dst, sizeof(dst)));
     }
-    
+
     static const SkPoint gPartial[] = {
         { L - 10, CY }, { CX, CY }, { L, CY }, { CX, CY },
         { CX, T - 10 }, { CX, CY }, { CX, T }, { CX, CY },
@@ -103,12 +141,13 @@
         REPORTER_ASSERT(reporter, valid &&
                                   !memcmp(&gPartial[i+2], dst, sizeof(dst)));
     }
-    
+
 }
 
-void TestClipper(skiatest::Reporter* reporter) {
+static void TestClipper(skiatest::Reporter* reporter) {
     test_intersectline(reporter);
     test_edgeclipper(reporter);
+    test_hairclipping(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/ColorFilterTest.cpp b/tests/ColorFilterTest.cpp
index f98deb0..4016f21 100644
--- a/tests/ColorFilterTest.cpp
+++ b/tests/ColorFilterTest.cpp
@@ -10,23 +10,20 @@
 #include "SkColorFilter.h"
 #include "SkRandom.h"
 #include "SkXfermode.h"
+#include "SkOrderedReadBuffer.h"
+#include "SkOrderedWriteBuffer.h"
 
 static SkFlattenable* reincarnate_flattenable(SkFlattenable* obj) {
-    SkFlattenable::Factory fact = obj->getFactory();
-    if (NULL == fact) {
-        return NULL;
-    }
-
-    SkFlattenableWriteBuffer wb(1024);
-    obj->flatten(wb);
+    SkOrderedWriteBuffer wb(1024);
+    wb.writeFlattenable(obj);
 
     size_t size = wb.size();
     SkAutoSMalloc<1024> storage(size);
     // make a copy into storage
-    wb.flatten(storage.get());
+    wb.writeToMemory(storage.get());
 
-    SkFlattenableReadBuffer rb(storage.get(), size);
-    return fact(rb);
+    SkOrderedReadBuffer rb(storage.get(), size);
+    return rb.readFlattenable();
 }
 
 template <typename T> T* reincarnate(T* obj) {
@@ -75,13 +72,13 @@
             if (m != expectedMode) {
                 expectedMode = SkXfermode::kSrc_Mode;
             }
-        } 
+        }
 
 //        SkDebugf("--- got [%d %x] expected [%d %x]\n", m, c, expectedMode, expectedColor);
 
         REPORTER_ASSERT(reporter, c == expectedColor);
         REPORTER_ASSERT(reporter, m == expectedMode);
-        
+
         {
             SkColorFilter* cf2 = reincarnate(cf);
             SkAutoUnref aur2(cf2);
diff --git a/tests/ColorTest.cpp b/tests/ColorTest.cpp
index 83e2e3f..8985dbd 100644
--- a/tests/ColorTest.cpp
+++ b/tests/ColorTest.cpp
@@ -8,11 +8,117 @@
 #include "Test.h"
 #include "SkColor.h"
 #include "SkColorPriv.h"
-#include "SkMath.h"
+#include "SkMathPriv.h"
 #include "SkRandom.h"
 #include "SkUnPreMultiply.h"
 
-static void test_premul(skiatest::Reporter* reporter) {
+#define GetPackedR16As32(packed)    (SkGetPackedR16(dc) << (8 - SK_R16_BITS))
+#define GetPackedG16As32(packed)    (SkGetPackedG16(dc) << (8 - SK_G16_BITS))
+#define GetPackedB16As32(packed)    (SkGetPackedB16(dc) << (8 - SK_B16_BITS))
+
+static inline bool S32A_D565_Blend_0(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+    unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
+    unsigned dr = SkMulS16(SkPacked32ToR16(sc), alpha) + SkMulS16(SkGetPackedR16(dc), dst_scale);
+    unsigned dg = SkMulS16(SkPacked32ToG16(sc), alpha) + SkMulS16(SkGetPackedG16(dc), dst_scale);
+
+    unsigned rr = SkDiv255Round(dr);
+    unsigned rg = SkDiv255Round(dg);
+
+    if (rr <= 31 && rg <= 63) {
+        return true;
+    }
+    return false;
+}
+
+static inline bool S32A_D565_Blend_01(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+    unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
+    unsigned dr = SkMulS16(SkGetPackedR32(sc), alpha) + SkMulS16(SkGetPackedR16(dc) << 3, dst_scale);
+    unsigned dg = SkMulS16(SkGetPackedG32(sc), alpha) + SkMulS16(SkGetPackedG16(dc) << 2, dst_scale);
+
+    unsigned rr = SkDiv255Round(dr) >> 3;
+    unsigned rg = SkDiv255Round(dg) >> 2;
+
+    if (rr <= 31 && rg <= 63) {
+        return true;
+    }
+    return false;
+}
+
+static inline bool S32A_D565_Blend_02(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+    unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
+    unsigned dr = SkMulS16(SkGetPackedR32(sc), alpha) + SkMulS16(GetPackedR16As32(dc), dst_scale);
+    unsigned dg = SkMulS16(SkGetPackedG32(sc), alpha) + SkMulS16(GetPackedG16As32(dc), dst_scale);
+    unsigned db = SkMulS16(SkGetPackedB32(sc), alpha) + SkMulS16(GetPackedB16As32(dc), dst_scale);
+    int rc = SkPack888ToRGB16(SkDiv255Round(dr),
+                              SkDiv255Round(dg),
+                              SkDiv255Round(db));
+
+    unsigned rr = SkGetPackedR16(rc);
+    unsigned rg = SkGetPackedG16(rc);
+
+    if (rr <= 31 && rg <= 63) {
+        return true;
+    }
+    return false;
+}
+
+static inline bool S32A_D565_Blend_1(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+    unsigned dst_scale = 255 - SkMulDiv255Round(SkGetPackedA32(sc), alpha);
+    unsigned dr = (SkMulS16(SkGetPackedR32(sc), alpha) >> 3) + SkMulS16(SkGetPackedR16(dc), dst_scale);
+    unsigned dg = (SkMulS16(SkGetPackedG32(sc), alpha) >> 2) + SkMulS16(SkGetPackedG16(dc), dst_scale);
+
+    unsigned rr = SkDiv255Round(dr);
+    unsigned rg = SkDiv255Round(dg);
+
+    if (rr <= 31 && rg <= 63) {
+        return true;
+    }
+    return false;
+}
+
+static inline int SkDiv65025Round(int x) {
+    return (x + 65025/2) / 65025;
+//    return x / 65025;
+}
+static inline bool S32A_D565_Blend_2(SkPMColor sc, uint16_t dc, U8CPU alpha) {
+    unsigned dst_scale = 255*255 - SkGetPackedA32(sc) * alpha;
+    alpha *= 255;
+    unsigned dr = (SkGetPackedR32(sc) >> 3) * alpha + SkGetPackedR16(dc) * dst_scale;
+    unsigned dg = (SkGetPackedG32(sc) >> 2) * alpha + SkGetPackedG16(dc) * dst_scale;
+
+    unsigned rr = SkDiv65025Round(dr);
+    unsigned rg = SkDiv65025Round(dg);
+
+    if (rr <= 31 && rg <= 63) {
+        return true;
+    }
+    return false;
+}
+
+static inline void test_565blend(skiatest::Reporter* reporter) {
+    int total_failures = 0;
+    for (int global_alpha = 0; global_alpha <= 255; ++global_alpha) {
+        int failures = 0;
+        int total = 0;
+        for (int src_a = 0; src_a <= 255; ++src_a) {
+            for (int src_c = 0; src_c <= src_a; ++src_c) {
+                SkPMColor sc = SkPackARGB32(src_a, src_c, src_c, src_c);
+                for (int dst_r = 0; dst_r <= 31; ++dst_r) {
+                    for (int dst_g = 0; dst_g <= 63; ++dst_g) {
+                        uint16_t dc = SkPackRGB16(dst_r, dst_g, dst_r);
+                        failures += !S32A_D565_Blend_0(sc, dc, global_alpha);
+                        total += 1;
+                    }
+                }
+            }
+        }
+        SkDebugf("global_alpha=%d failures=%d total=%d %g\n", global_alpha, failures, total, failures * 100.0 / total);
+        total_failures += failures;
+    }
+    SkDebugf("total failures %d\n", total_failures);
+}
+
+static inline void test_premul(skiatest::Reporter* reporter) {
     for (int a = 0; a <= 255; a++) {
         for (int x = 0; x <= 255; x++) {
             SkColor c0 = SkColorSetARGB(a, x, x, x);
@@ -56,7 +162,7 @@
 }
 */
 
-static void test_fast_interp(skiatest::Reporter* reporter) {
+static inline void test_fast_interp(skiatest::Reporter* reporter) {
     SkRandom r;
 
     U8CPU a0 = 0;
@@ -76,6 +182,7 @@
     test_premul(reporter);
     //test_interp(reporter);
     test_fast_interp(reporter);
+//    test_565blend(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/DataRefTest.cpp b/tests/DataRefTest.cpp
index 6a42485..8c002c8 100644
--- a/tests/DataRefTest.cpp
+++ b/tests/DataRefTest.cpp
@@ -7,6 +7,85 @@
  */
 #include "Test.h"
 #include "SkData.h"
+#include "SkDataSet.h"
+#include "SkStream.h"
+
+template <typename T> class SkTUnref {
+public:
+    SkTUnref(T* ref) : fRef(ref) {}
+    ~SkTUnref() { fRef->unref(); }
+
+    operator T*() { return fRef; }
+    operator const T*() { return fRef; }
+
+private:
+    T*  fRef;
+};
+
+static void unrefAll(const SkDataSet::Pair pairs[], int count) {
+    for (int i = 0; i < count; ++i) {
+        pairs[i].fValue->unref();
+    }
+}
+
+// asserts that inner is a subset of outer
+static void test_dataset_subset(skiatest::Reporter* reporter,
+                                const SkDataSet& outer, const SkDataSet& inner) {
+    SkDataSet::Iter iter(inner);
+    for (; !iter.done(); iter.next()) {
+        SkData* outerData = outer.find(iter.key());
+        REPORTER_ASSERT(reporter, outerData);
+        REPORTER_ASSERT(reporter, outerData->equals(iter.value()));
+    }
+}
+
+static void test_datasets_equal(skiatest::Reporter* reporter,
+                                const SkDataSet& ds0, const SkDataSet& ds1) {
+    REPORTER_ASSERT(reporter, ds0.count() == ds1.count());
+
+    test_dataset_subset(reporter, ds0, ds1);
+    test_dataset_subset(reporter, ds1, ds0);
+}
+
+static void test_dataset(skiatest::Reporter* reporter, const SkDataSet& ds,
+                         int count) {
+    REPORTER_ASSERT(reporter, ds.count() == count);
+
+    SkDataSet::Iter iter(ds);
+    int index = 0;
+    for (; !iter.done(); iter.next()) {
+//        const char* name = iter.key();
+//        SkData* data = iter.value();
+//        SkDebugf("[%d] %s:%s\n", index, name, (const char*)data->bytes());
+        index += 1;
+    }
+    REPORTER_ASSERT(reporter, index == count);
+
+    SkDynamicMemoryWStream ostream;
+    ds.writeToStream(&ostream);
+    SkMemoryStream istream;
+    istream.setData(ostream.copyToData())->unref();
+    SkDataSet copy(&istream);
+
+    test_datasets_equal(reporter, ds, copy);
+}
+
+static void test_dataset(skiatest::Reporter* reporter) {
+    SkDataSet set0(NULL, 0);
+    SkDataSet set1("hello", SkTUnref<SkData>(SkData::NewWithCString("world")));
+
+    const SkDataSet::Pair pairs[] = {
+        { "one", SkData::NewWithCString("1") },
+        { "two", SkData::NewWithCString("2") },
+        { "three", SkData::NewWithCString("3") },
+    };
+    SkDataSet set3(pairs, 3);
+    unrefAll(pairs, 3);
+
+    test_dataset(reporter, set0, 0);
+    test_dataset(reporter, set1, 1);
+    test_dataset(reporter, set3, 3);
+}
 
 static void* gGlobal;
 
@@ -26,26 +105,35 @@
     REPORTER_ASSERT(reporter, !memcmp(ref->data(), data, len));
 }
 
-void TestDataRef(skiatest::Reporter* reporter) {
+static void test_cstring(skiatest::Reporter* reporter) {
+    const char str[] = "Hello world";
+    size_t     len = strlen(str);
+
+    SkAutoTUnref<SkData> r0(SkData::NewWithCopy(str, len + 1));
+    SkAutoTUnref<SkData> r1(SkData::NewWithCString(str));
+
+    REPORTER_ASSERT(reporter, r0->equals(r1));
+
+    SkAutoTUnref<SkData> r2(SkData::NewWithCString(NULL));
+    REPORTER_ASSERT(reporter, 1 == r2->size());
+    REPORTER_ASSERT(reporter, 0 == *r2->bytes());
+}
+
+static void TestData(skiatest::Reporter* reporter) {
     const char* str = "We the people, in order to form a more perfect union.";
     const int N = 10;
 
-    SkData* r0 = SkData::NewEmpty();
-    SkData* r1 = SkData::NewWithCopy(str, strlen(str));
-    SkData* r2 = SkData::NewWithProc(new int[N], N*sizeof(int),
-                                           delete_int_proc, gGlobal);
-    SkData* r3 = SkData::NewSubset(r1, 7, 6);
+    SkAutoTUnref<SkData> r0(SkData::NewEmpty());
+    SkAutoTUnref<SkData> r1(SkData::NewWithCopy(str, strlen(str)));
+    SkAutoTUnref<SkData> r2(SkData::NewWithProc(new int[N], N*sizeof(int),
+                                           delete_int_proc, gGlobal));
+    SkAutoTUnref<SkData> r3(SkData::NewSubset(r1, 7, 6));
 
-    SkAutoUnref aur0(r0);
-    SkAutoUnref aur1(r1);
-    SkAutoUnref aur2(r2);
-    SkAutoUnref aur3(r3);
-    
     assert_len(reporter, r0, 0);
     assert_len(reporter, r1, strlen(str));
     assert_len(reporter, r2, N * sizeof(int));
     assert_len(reporter, r3, 6);
-    
+
     assert_data(reporter, r1, str, strlen(str));
     assert_data(reporter, r3, "people", 6);
 
@@ -55,7 +143,10 @@
     tmp = SkData::NewSubset(r1, 0, 0);
     assert_len(reporter, tmp, 0);
     tmp->unref();
+
+    test_cstring(reporter);
+    test_dataset(reporter);
 }
 
 #include "TestClassDef.h"
-DEFINE_TESTCLASS("DataRef", DataRefTestClass, TestDataRef)
+DEFINE_TESTCLASS("Data", DataTestClass, TestData)
diff --git a/tests/DeferredCanvasTest.cpp b/tests/DeferredCanvasTest.cpp
index 17adb52..8a834be 100644
--- a/tests/DeferredCanvasTest.cpp
+++ b/tests/DeferredCanvasTest.cpp
@@ -7,10 +7,11 @@
  */
 #include "Test.h"
 #include "SkBitmap.h"
+#include "SkBitmapProcShader.h"
 #include "SkDeferredCanvas.h"
+#include "SkDevice.h"
 #include "SkShader.h"
 
-
 static const int gWidth = 2;
 static const int gHeight = 2;
 
@@ -64,25 +65,25 @@
     SkDeferredCanvas canvas(&device);
 
     // verify that frame is intially fresh
-    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+    REPORTER_ASSERT(reporter, canvas.isFreshFrame());
     // no clearing op since last call to isFreshFrame -> not fresh
-    REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+    REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
 
     // Verify that clear triggers a fresh frame
     canvas.clear(0x00000000);
-    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+    REPORTER_ASSERT(reporter, canvas.isFreshFrame());
 
     // Verify that clear with saved state triggers a fresh frame
     canvas.save(SkCanvas::kMatrixClip_SaveFlag);
     canvas.clear(0x00000000);
     canvas.restore();
-    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+    REPORTER_ASSERT(reporter, canvas.isFreshFrame());
 
     // Verify that clear within a layer does NOT trigger a fresh frame
     canvas.saveLayer(NULL, NULL, SkCanvas::kARGB_ClipLayer_SaveFlag);
     canvas.clear(0x00000000);
     canvas.restore();
-    REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+    REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
 
     // Verify that a clear with clipping triggers a fresh frame
     // (clear is not affected by clipping)
@@ -90,7 +91,7 @@
     canvas.clipRect(partialRect, SkRegion::kIntersect_Op, false);
     canvas.clear(0x00000000);
     canvas.restore();
-    REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());    
+    REPORTER_ASSERT(reporter, canvas.isFreshFrame());
 
     // Verify that full frame rects with different forms of opaque paint
     // trigger frames to be marked as fresh
@@ -99,7 +100,15 @@
         paint.setStyle( SkPaint::kFill_Style );
         paint.setAlpha( 255 );
         canvas.drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());
+        REPORTER_ASSERT(reporter, canvas.isFreshFrame());
+    }
+    {
+        SkPaint paint;
+        paint.setStyle( SkPaint::kFill_Style );
+        paint.setAlpha( 255 );
+        paint.setXfermodeMode(SkXfermode::kSrcIn_Mode);
+        canvas.drawRect(fullRect, paint);
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
     }
     {
         SkPaint paint;
@@ -107,11 +116,11 @@
         SkBitmap bmp;
         create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
         bmp.setIsOpaque(true);
-        SkShader* shader = SkShader::CreateBitmapShader(bmp, 
+        SkShader* shader = SkShader::CreateBitmapShader(bmp,
             SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
         paint.setShader(shader)->unref();
         canvas.drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, canvas.getDeferredDevice()->isFreshFrame());        
+        REPORTER_ASSERT(reporter, canvas.isFreshFrame());
     }
 
     // Verify that full frame rects with different forms of non-opaque paint
@@ -121,7 +130,7 @@
         paint.setStyle( SkPaint::kFill_Style );
         paint.setAlpha( 254 );
         canvas.drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
     }
     {
         SkPaint paint;
@@ -129,11 +138,11 @@
         SkBitmap bmp;
         create(&bmp, SkBitmap::kARGB_8888_Config, 0xFFFFFFFF);
         bmp.setIsOpaque(false);
-        SkShader* shader = SkShader::CreateBitmapShader(bmp, 
+        SkShader* shader = SkShader::CreateBitmapShader(bmp,
             SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
         paint.setShader(shader)->unref();
         canvas.drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());        
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
     }
 
     // Verify that incomplete coverage does not trigger a fresh frame
@@ -142,7 +151,7 @@
         paint.setStyle(SkPaint::kFill_Style);
         paint.setAlpha(255);
         canvas.drawRect(partialRect, paint);
-        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
     }
 
     // Verify that incomplete coverage due to clipping does not trigger a fresh
@@ -154,7 +163,20 @@
         paint.setStyle(SkPaint::kFill_Style);
         paint.setAlpha(255);
         canvas.drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+        canvas.restore();
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
+    }
+    {
+        canvas.save(SkCanvas::kMatrixClip_SaveFlag);
+        SkPaint paint;
+        paint.setStyle( SkPaint::kFill_Style );
+        paint.setAlpha( 255 );
+        SkPath path;
+        path.addCircle(SkIntToScalar(0), SkIntToScalar(0), SkIntToScalar(2));
+        canvas.clipPath(path, SkRegion::kIntersect_Op, false);
+        canvas.drawRect(fullRect, paint);
+        canvas.restore();
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
     }
 
     // Verify that stroked rect does not trigger a fresh frame
@@ -163,9 +185,9 @@
         paint.setStyle( SkPaint::kStroke_Style );
         paint.setAlpha( 255 );
         canvas.drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+        REPORTER_ASSERT(reporter, !canvas.isFreshFrame());
     }
-    
+
     // Verify kSrcMode triggers a fresh frame even with transparent color
     {
         SkPaint paint;
@@ -173,7 +195,253 @@
         paint.setAlpha( 100 );
         paint.setXfermodeMode(SkXfermode::kSrc_Mode);
         canvas.drawRect(fullRect, paint);
-        REPORTER_ASSERT(reporter, !canvas.getDeferredDevice()->isFreshFrame());
+        REPORTER_ASSERT(reporter, canvas.isFreshFrame());
+    }
+}
+
+class MockDevice : public SkDevice {
+public:
+    MockDevice(const SkBitmap& bm) : SkDevice(bm) {
+        fDrawBitmapCallCount = 0;
+    }
+    virtual void drawBitmap(const SkDraw&, const SkBitmap&,
+                            const SkIRect*,
+                            const SkMatrix&, const SkPaint&) {
+        fDrawBitmapCallCount++;
+    }
+
+    int fDrawBitmapCallCount;
+};
+
+// Verifies that the deferred canvas triggers a flush when its memory
+// limit is exceeded
+static void TestDeferredCanvasMemoryLimit(skiatest::Reporter* reporter) {
+    SkBitmap store;
+    store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    store.allocPixels();
+    MockDevice mockDevice(store);
+    SkDeferredCanvas canvas(&mockDevice);
+    canvas.setMaxRecordingStorage(160000);
+
+    SkBitmap sourceImage;
+    // 100 by 100 image, takes 40,000 bytes in memory
+    sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    sourceImage.allocPixels();
+
+    for (int i = 0; i < 5; i++) {
+        sourceImage.notifyPixelsChanged(); // to force re-serialization
+        canvas.drawBitmap(sourceImage, 0, 0, NULL);
+    }
+
+    REPORTER_ASSERT(reporter, mockDevice.fDrawBitmapCallCount == 4);
+}
+
+class NotificationCounter : public SkDeferredCanvas::NotificationClient {
+public:
+    NotificationCounter() {
+        fPrepareForDrawCount = fStorageAllocatedChangedCount =
+            fFlushedDrawCommandsCount = fSkippedPendingDrawCommandsCount = 0;
+    }
+
+    virtual void prepareForDraw() SK_OVERRIDE {
+        fPrepareForDrawCount++;
+    }
+    virtual void storageAllocatedForRecordingChanged(size_t size) SK_OVERRIDE {
+        fStorageAllocatedChangedCount++;
+    }
+    virtual void flushedDrawCommands() SK_OVERRIDE {
+        fFlushedDrawCommandsCount++;
+    }
+    virtual void skippedPendingDrawCommands() SK_OVERRIDE {
+        fSkippedPendingDrawCommandsCount++;
+    }
+
+    int fPrepareForDrawCount;
+    int fStorageAllocatedChangedCount;
+    int fFlushedDrawCommandsCount;
+    int fSkippedPendingDrawCommandsCount;
+};
+
+static void TestDeferredCanvasBitmapCaching(skiatest::Reporter* reporter) {
+    SkBitmap store;
+    store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    store.allocPixels();
+    SkDevice device(store);
+    NotificationCounter notificationCounter;
+    SkDeferredCanvas canvas(&device);
+    canvas.setNotificationClient(&notificationCounter);
+
+    const int imageCount = 2;
+    SkBitmap sourceImages[imageCount];
+    for (int i = 0; i < imageCount; i++)
+    {
+        sourceImages[i].setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+        sourceImages[i].allocPixels();
+    }
+
+    size_t bitmapSize = sourceImages[0].getSize();
+
+    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fStorageAllocatedChangedCount);
+    // stored bitmap + drawBitmap command
+    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > bitmapSize);
+
+    // verify that nothing can be freed at this point
+    REPORTER_ASSERT(reporter, 0 == canvas.freeMemoryIfPossible(~0U));
+
+    // verify that flush leaves image in cache
+    REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
+    REPORTER_ASSERT(reporter, 0 == notificationCounter.fPrepareForDrawCount);
+    canvas.flush();
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fPrepareForDrawCount);
+    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() >= bitmapSize);
+
+    // verify that after a flush, cached image can be freed
+    REPORTER_ASSERT(reporter, canvas.freeMemoryIfPossible(~0U) >= bitmapSize);
+
+    // Verify that caching works for avoiding multiple copies of the same bitmap
+    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
+    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fStorageAllocatedChangedCount);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
+    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < 2 * bitmapSize);
+
+    // Verify partial eviction based on bytesToFree
+    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
+    canvas.flush();
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
+    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2 * bitmapSize);
+    size_t bytesFreed = canvas.freeMemoryIfPossible(1);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
+    REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
+    REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
+
+    // Verifiy that partial purge works, image zero is in cache but not reffed by
+    // a pending draw, while image 1 is locked-in.
+    canvas.freeMemoryIfPossible(~0U);
+    REPORTER_ASSERT(reporter, 2 == notificationCounter.fFlushedDrawCommandsCount);
+    canvas.drawBitmap(sourceImages[0], 0, 0, NULL);
+    canvas.flush();
+    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
+    bytesFreed = canvas.freeMemoryIfPossible(~0U);
+    // only one bitmap should have been freed.
+    REPORTER_ASSERT(reporter,  bytesFreed >= bitmapSize);
+    REPORTER_ASSERT(reporter,  bytesFreed < 2*bitmapSize);
+    // Clear for next test
+    canvas.flush();
+    canvas.freeMemoryIfPossible(~0U);
+    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() < bitmapSize);
+
+    // Verify the image cache is sensitive to genID bumps
+    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
+    sourceImages[1].notifyPixelsChanged();
+    canvas.drawBitmap(sourceImages[1], 0, 0, NULL);
+    REPORTER_ASSERT(reporter, canvas.storageAllocatedForRecording() > 2*bitmapSize);
+
+    // Verify that nothing in this test caused commands to be skipped
+    REPORTER_ASSERT(reporter, 0 == notificationCounter.fSkippedPendingDrawCommandsCount);
+}
+
+static void TestDeferredCanvasSkip(skiatest::Reporter* reporter) {
+    SkBitmap store;
+    store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    store.allocPixels();
+    SkDevice device(store);
+    NotificationCounter notificationCounter;
+    SkDeferredCanvas canvas(&device);
+    canvas.setNotificationClient(&notificationCounter);
+    canvas.clear(0x0);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
+    REPORTER_ASSERT(reporter, 0 == notificationCounter.fFlushedDrawCommandsCount);
+    canvas.flush();
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fSkippedPendingDrawCommandsCount);
+    REPORTER_ASSERT(reporter, 1 == notificationCounter.fFlushedDrawCommandsCount);
+
+}
+
+static void TestDeferredCanvasBitmapShaderNoLeak(skiatest::Reporter* reporter) {
+    // This is a regression test for crbug.com/155875
+    // This test covers a code path that inserts bitmaps into the bitmap heap through the
+    // flattening of SkBitmapProcShaders. The refcount in the bitmap heap is maintained through
+    // the flattening and unflattening of the shader.
+    SkBitmap store;
+    store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    store.allocPixels();
+    SkDevice device(store);
+    SkDeferredCanvas canvas(&device);
+    // test will fail if nbIterations is not in sync with
+    // BITMAPS_TO_KEEP in SkGPipeWrite.cpp
+    const int nbIterations = 5;
+    size_t bytesAllocated = 0;
+    for(int pass = 0; pass < 2; ++pass) {
+        for(int i = 0; i < nbIterations; ++i) {
+            SkPaint paint;
+            SkBitmap paintPattern;
+            paintPattern.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
+            paintPattern.allocPixels();
+            paint.setShader(SkNEW_ARGS(SkBitmapProcShader,
+                (paintPattern, SkShader::kClamp_TileMode, SkShader::kClamp_TileMode)))->unref();
+            canvas.drawPaint(paint);
+            canvas.flush();
+
+            // In the first pass, memory allocation should be monotonically increasing as
+            // the bitmap heap slots fill up.  In the second pass memory allocation should be
+            // stable as bitmap heap slots get recycled.
+            size_t newBytesAllocated = canvas.storageAllocatedForRecording();
+            if (pass == 0) {
+                REPORTER_ASSERT(reporter, newBytesAllocated > bytesAllocated);
+                bytesAllocated = newBytesAllocated;
+            } else {
+                REPORTER_ASSERT(reporter, newBytesAllocated == bytesAllocated);
+            }
+        }
+    }
+    // All cached resources should be evictable since last canvas call was flush()
+    canvas.freeMemoryIfPossible(~0U);
+    REPORTER_ASSERT(reporter, 0 == canvas.storageAllocatedForRecording());
+}
+
+static void TestDeferredCanvasBitmapSizeThreshold(skiatest::Reporter* reporter) {
+    SkBitmap store;
+    store.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    store.allocPixels();
+
+    SkBitmap sourceImage;
+    // 100 by 100 image, takes 40,000 bytes in memory
+    sourceImage.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    sourceImage.allocPixels();
+
+    // 1 under : should not store the image
+    {
+        SkDevice device(store);
+        SkDeferredCanvas canvas(&device);
+        canvas.setBitmapSizeThreshold(39999);
+        canvas.drawBitmap(sourceImage, 0, 0, NULL);
+        size_t newBytesAllocated = canvas.storageAllocatedForRecording();
+        REPORTER_ASSERT(reporter, newBytesAllocated == 0);
+    }
+
+    // exact value : should store the image
+    {
+        SkDevice device(store);
+        SkDeferredCanvas canvas(&device);
+        canvas.setBitmapSizeThreshold(40000);
+        canvas.drawBitmap(sourceImage, 0, 0, NULL);
+        size_t newBytesAllocated = canvas.storageAllocatedForRecording();
+        REPORTER_ASSERT(reporter, newBytesAllocated > 0);
+    }
+
+    // 1 over : should still store the image
+    {
+        SkDevice device(store);
+        SkDeferredCanvas canvas(&device);
+        canvas.setBitmapSizeThreshold(40001);
+        canvas.drawBitmap(sourceImage, 0, 0, NULL);
+        size_t newBytesAllocated = canvas.storageAllocatedForRecording();
+        REPORTER_ASSERT(reporter, newBytesAllocated > 0);
     }
 }
 
@@ -181,6 +449,11 @@
     TestDeferredCanvasBitmapAccess(reporter);
     TestDeferredCanvasFlush(reporter);
     TestDeferredCanvasFreshFrame(reporter);
+    TestDeferredCanvasMemoryLimit(reporter);
+    TestDeferredCanvasBitmapCaching(reporter);
+    TestDeferredCanvasSkip(reporter);
+    TestDeferredCanvasBitmapShaderNoLeak(reporter);
+    TestDeferredCanvasBitmapSizeThreshold(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/DequeTest.cpp b/tests/DequeTest.cpp
index 4e1c1b7..62e8e4d 100644
--- a/tests/DequeTest.cpp
+++ b/tests/DequeTest.cpp
@@ -29,21 +29,76 @@
     }
 }
 
-static void assert_f2biter(skiatest::Reporter* reporter, const SkDeque& deq,
-                           int max, int min) {
-    SkDeque::F2BIter iter(deq);
+static void assert_iter(skiatest::Reporter* reporter, const SkDeque& deq,
+                        int max, int min) {
+    // test forward iteration
+    SkDeque::Iter iter(deq, SkDeque::Iter::kFront_IterStart);
     void* ptr;
 
     int value = max;
-    while ((ptr = iter.next()) != NULL) {
+    while (NULL != (ptr = iter.next())) {
         REPORTER_ASSERT(reporter, value == *(int*)ptr);
         value -= 1;
     }
     REPORTER_ASSERT(reporter, value+1 == min);
+
+    // test reverse iteration
+    iter.reset(deq, SkDeque::Iter::kBack_IterStart);
+
+    value = min;
+    while (NULL != (ptr = iter.prev())) {
+        REPORTER_ASSERT(reporter, value == *(int*)ptr);
+        value += 1;
+    }
+    REPORTER_ASSERT(reporter, value-1 == max);
+
+    // test mixed iteration
+    iter.reset(deq, SkDeque::Iter::kFront_IterStart);
+
+    value = max;
+    // forward iteration half-way
+    for (int i = 0; i < deq.count()/2 && NULL != (ptr = iter.next()); i++) {
+        REPORTER_ASSERT(reporter, value == *(int*)ptr);
+        value -= 1;
+    }
+    // then back down w/ reverse iteration
+    while (NULL != (ptr = iter.prev())) {
+        REPORTER_ASSERT(reporter, value == *(int*)ptr);
+        value += 1;
+    }
+    REPORTER_ASSERT(reporter, value-1 == max);
 }
 
-static void TestDeque(skiatest::Reporter* reporter) {
-    SkDeque deq(sizeof(int));
+// This helper is intended to only give the unit test access to SkDeque's
+// private numBlocksAllocated method
+class DequeUnitTestHelper {
+public:
+    int fNumBlocksAllocated;
+
+    DequeUnitTestHelper(const SkDeque& deq) {
+        fNumBlocksAllocated = deq.numBlocksAllocated();
+    }
+};
+
+static void assert_blocks(skiatest::Reporter* reporter,
+                          const SkDeque& deq,
+                          int allocCount) {
+    DequeUnitTestHelper helper(deq);
+
+    if (0 == deq.count()) {
+        REPORTER_ASSERT(reporter, 1 == helper.fNumBlocksAllocated);
+    } else {
+        int expected = (deq.count() + allocCount - 1) / allocCount;
+        // A block isn't freed in the deque when it first becomes empty so
+        // sometimes an extra block lingers around
+        REPORTER_ASSERT(reporter,
+            expected == helper.fNumBlocksAllocated ||
+            expected+1 == helper.fNumBlocksAllocated);
+    }
+}
+
+static void TestSub(skiatest::Reporter* reporter, int allocCount) {
+    SkDeque deq(sizeof(int), allocCount);
     int i;
 
     // test pushing on the front
@@ -53,18 +108,21 @@
         *(int*)deq.push_front() = i;
     }
     assert_count(reporter, deq, 10);
-    assert_f2biter(reporter, deq, 10, 1);
+    assert_iter(reporter, deq, 10, 1);
+    assert_blocks(reporter, deq, allocCount);
 
     for (i = 0; i < 5; i++) {
         deq.pop_front();
     }
     assert_count(reporter, deq, 5);
-    assert_f2biter(reporter, deq, 5, 1);
+    assert_iter(reporter, deq, 5, 1);
+    assert_blocks(reporter, deq, allocCount);
 
     for (i = 0; i < 5; i++) {
         deq.pop_front();
     }
     assert_count(reporter, deq, 0);
+    assert_blocks(reporter, deq, allocCount);
 
     // now test pushing on the back
 
@@ -72,20 +130,23 @@
         *(int*)deq.push_back() = i;
     }
     assert_count(reporter, deq, 10);
-    assert_f2biter(reporter, deq, 10, 1);
+    assert_iter(reporter, deq, 10, 1);
+    assert_blocks(reporter, deq, allocCount);
 
     for (i = 0; i < 5; i++) {
         deq.pop_back();
     }
     assert_count(reporter, deq, 5);
-    assert_f2biter(reporter, deq, 10, 6);
+    assert_iter(reporter, deq, 10, 6);
+    assert_blocks(reporter, deq, allocCount);
 
     for (i = 0; i < 5; i++) {
         deq.pop_back();
     }
     assert_count(reporter, deq, 0);
+    assert_blocks(reporter, deq, allocCount);
 
-    // now tests pushing/poping on both ends
+    // now test pushing/popping on both ends
 
     *(int*)deq.push_front() = 5;
     *(int*)deq.push_back() = 4;
@@ -96,8 +157,15 @@
     *(int*)deq.push_front() = 8;
     *(int*)deq.push_back() = 1;
     assert_count(reporter, deq, 8);
-    assert_f2biter(reporter, deq, 8, 1);
+    assert_iter(reporter, deq, 8, 1);
+    assert_blocks(reporter, deq, allocCount);
 }
 
+static void TestDeque(skiatest::Reporter* reporter) {
+    // test it once with the default allocation count
+    TestSub(reporter, 1);
+    // test it again with a generous allocation count
+    TestSub(reporter, 10);
+}
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Deque", TestDequeClass, TestDeque)
diff --git a/tests/DrawBitmapRectTest.cpp b/tests/DrawBitmapRectTest.cpp
index 843feb5..a56396c 100644
--- a/tests/DrawBitmapRectTest.cpp
+++ b/tests/DrawBitmapRectTest.cpp
@@ -8,6 +8,221 @@
 #include "Test.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkShader.h"
+#include "SkRandom.h"
+#include "SkMatrixUtils.h"
+
+static void rand_matrix(SkMatrix* mat, SkRandom& rand, unsigned mask) {
+    mat->setIdentity();
+    if (mask & SkMatrix::kTranslate_Mask) {
+        mat->postTranslate(rand.nextSScalar1(), rand.nextSScalar1());
+    }
+    if (mask & SkMatrix::kScale_Mask) {
+        mat->postScale(rand.nextSScalar1(), rand.nextSScalar1());
+    }
+    if (mask & SkMatrix::kAffine_Mask) {
+        mat->postRotate(rand.nextSScalar1() * 360);
+    }
+    if (mask & SkMatrix::kPerspective_Mask) {
+        mat->setPerspX(rand.nextSScalar1());
+        mat->setPerspY(rand.nextSScalar1());
+    }
+}
+
+static void rand_size(SkISize* size, SkRandom& rand) {
+    size->set(rand.nextU() & 0xFFFF, rand.nextU() & 0xFFFF);
+}
+
+static bool treat_as_sprite(const SkMatrix& mat, const SkISize& size,
+                            unsigned bits) {
+    return SkTreatAsSprite(mat, size.width(), size.height(), bits);
+}
+
+static void test_treatAsSprite(skiatest::Reporter* reporter) {
+    const unsigned bilerBits = kSkSubPixelBitsForBilerp;
+
+    SkMatrix mat;
+    SkISize  size;
+    SkRandom rand;
+
+    // assert: translate-only no-filter can always be treated as sprite
+    for (int i = 0; i < 1000; ++i) {
+        rand_matrix(&mat, rand, SkMatrix::kTranslate_Mask);
+        for (int j = 0; j < 1000; ++j) {
+            rand_size(&size, rand);
+            REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, 0));
+        }
+    }
+
+    // assert: rotate/perspect is never treated as sprite
+    for (int i = 0; i < 1000; ++i) {
+        rand_matrix(&mat, rand, SkMatrix::kAffine_Mask | SkMatrix::kPerspective_Mask);
+        for (int j = 0; j < 1000; ++j) {
+            rand_size(&size, rand);
+            REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, 0));
+            REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+        }
+    }
+
+    size.set(500, 600);
+
+    const SkScalar tooMuchSubpixel = SkFloatToScalar(100.1f);
+    mat.setTranslate(tooMuchSubpixel, 0);
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+    mat.setTranslate(0, tooMuchSubpixel);
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar tinySubPixel = SkFloatToScalar(100.02f);
+    mat.setTranslate(tinySubPixel, 0);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+    mat.setTranslate(0, tinySubPixel);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar twoThirds = SK_Scalar1 * 2 / 3;
+    const SkScalar bigScale = SkScalarDiv(size.width() + twoThirds, size.width());
+    mat.setScale(bigScale, bigScale);
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, false));
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar oneThird = SK_Scalar1 / 3;
+    const SkScalar smallScale = SkScalarDiv(size.width() + oneThird, size.width());
+    mat.setScale(smallScale, smallScale);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false));
+    REPORTER_ASSERT(reporter, !treat_as_sprite(mat, size, bilerBits));
+
+    const SkScalar oneFortyth = SK_Scalar1 / 40;
+    const SkScalar tinyScale = SkScalarDiv(size.width() + oneFortyth, size.width());
+    mat.setScale(tinyScale, tinyScale);
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, false));
+    REPORTER_ASSERT(reporter, treat_as_sprite(mat, size, bilerBits));
+}
+
+static void assert_ifDrawnTo(skiatest::Reporter* reporter,
+                             const SkBitmap& bm, bool shouldBeDrawn) {
+    for (int y = 0; y < bm.height(); ++y) {
+        for (int x = 0; x < bm.width(); ++x) {
+            if (shouldBeDrawn) {
+                if (0 == *bm.getAddr32(x, y)) {
+                    REPORTER_ASSERT(reporter, false);
+                    return;
+                }
+            } else {
+                // should not be drawn
+                if (*bm.getAddr32(x, y)) {
+                    REPORTER_ASSERT(reporter, false);
+                    return;
+                }
+            }
+        }
+    }
+}
+
+static void test_wacky_bitmapshader(skiatest::Reporter* reporter,
+                                    int width, int height, bool shouldBeDrawn) {
+    SkBitmap dev;
+    dev.setConfig(SkBitmap::kARGB_8888_Config, 0x56F, 0x4f6);
+    dev.allocPixels();
+    dev.eraseColor(SK_ColorTRANSPARENT);  // necessary, so we know if we draw to it
+
+    SkMatrix matrix;
+
+    SkCanvas c(dev);
+    matrix.setAll(SkFloatToScalar(-119.34097f),
+                  SkFloatToScalar(-43.436558f),
+                  SkFloatToScalar(93489.945f),
+                  SkFloatToScalar(43.436558f),
+                  SkFloatToScalar(-119.34097f),
+                  SkFloatToScalar(123.98426f),
+                  0, 0, SK_Scalar1);
+    c.concat(matrix);
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorRED);
+
+    SkShader* s = SkShader::CreateBitmapShader(bm, SkShader::kRepeat_TileMode,
+                                               SkShader::kRepeat_TileMode);
+    matrix.setAll(SkFloatToScalar(0.0078740157f),
+                  0,
+                  SkIntToScalar(249),
+                  0,
+                  SkFloatToScalar(0.0078740157f),
+                  SkIntToScalar(239),
+                  0, 0, SK_Scalar1);
+    s->setLocalMatrix(matrix);
+
+    SkPaint paint;
+    paint.setShader(s)->unref();
+
+    SkRect r = SkRect::MakeXYWH(681, 239, 695, 253);
+    c.drawRect(r, paint);
+
+    assert_ifDrawnTo(reporter, dev, shouldBeDrawn);
+}
+
+/*
+ *  Original bug was asserting that the matrix-proc had generated a (Y) value
+ *  that was out of range. This led (in the release build) to the sampler-proc
+ *  reading memory out-of-bounds of the original bitmap.
+ *
+ *  We were numerically overflowing our 16bit coordinates that we communicate
+ *  between these two procs. The fixes was in two parts:
+ *
+ *  1. Just don't draw bitmaps larger than 64K-1 in width or height, since we
+ *     can't represent those coordinates in our transport format (yet).
+ *  2. Perform an unsigned shift during the calculation, so we don't get
+ *     sign-extension bleed when packing the two values (X,Y) into our 32bit
+ *     slot.
+ *
+ *  This tests exercises the original setup, plus 3 more to ensure that we can,
+ *  in fact, handle bitmaps at 64K-1 (assuming we don't exceed the total
+ *  memory allocation limit).
+ */
+static void test_giantrepeat_crbug118018(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    static const struct {
+        int fWidth;
+        int fHeight;
+        bool fExpectedToDraw;
+    } gTests[] = {
+        { 0x1b294, 0x7f,  false },   // crbug 118018 (width exceeds 64K)
+        { 0xFFFF, 0x7f,    true },   // should draw, test max width
+        { 0x7f, 0xFFFF,    true },   // should draw, test max height
+        { 0xFFFF, 0xFFFF, false },   // allocation fails (too much RAM)
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); ++i) {
+        test_wacky_bitmapshader(reporter,
+                                gTests[i].fWidth, gTests[i].fHeight,
+                                gTests[i].fExpectedToDraw);
+    }
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void test_nan_antihair(skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 20, 20);
+    bm.allocPixels();
+
+    SkCanvas canvas(bm);
+
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(10, SK_ScalarNaN);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setStyle(SkPaint::kStroke_Style);
+
+    // before our fix to SkScan_Antihair.cpp to check for integral NaN (0x800...)
+    // this would trigger an assert/crash.
+    //
+    // see rev. 3558
+    canvas.drawPath(path, paint);
+}
 
 static bool check_for_all_zeros(const SkBitmap& bm) {
     SkAutoLockPixels alp(bm);
@@ -48,6 +263,11 @@
 
     // ensure that we draw nothing if srcR does not intersect the bitmap
     REPORTER_ASSERT(reporter, check_for_all_zeros(dst));
+
+    test_nan_antihair(reporter);
+    test_giantrepeat_crbug118018(reporter);
+
+    test_treatAsSprite(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/DrawPathTest.cpp b/tests/DrawPathTest.cpp
index ae0068b..3a238db 100644
--- a/tests/DrawPathTest.cpp
+++ b/tests/DrawPathTest.cpp
@@ -8,6 +8,7 @@
 #include "Test.h"
 #include "SkBitmap.h"
 #include "SkCanvas.h"
+#include "SkDashPathEffect.h"
 
 static SkCanvas* create(SkBitmap::Config config, int w, int h, int rb,
                         void* addr = NULL) {
@@ -21,25 +22,260 @@
     return new SkCanvas(bm);
 }
 
+static SkCanvas* new_canvas(int w, int h) {
+    return create(SkBitmap::kARGB_8888_Config, w, h, 0, NULL);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void moveToH(SkPath* path, const uint32_t raw[]) {
+    const float* fptr = (const float*)raw;
+    path->moveTo(fptr[0], fptr[1]);
+}
+
+static void cubicToH(SkPath* path, const uint32_t raw[]) {
+    const float* fptr = (const float*)raw;
+    path->cubicTo(fptr[0], fptr[1], fptr[2], fptr[3], fptr[4], fptr[5]);
+}
+
+// This used to assert, because we performed a cast (int)(pt[0].fX * scale) to
+// arrive at an int (SkFDot6) rather than calling sk_float_round2int. The assert
+// was that the initial line-segment produced by the cubic was not monotonically
+// going down (i.e. the initial DY was negative). By rounding the floats, we get
+// the more proper result.
+//
+// http://code.google.com/p/chromium/issues/detail?id=131181
+//
+
+// we're not calling this test anymore; is that for a reason?
+
+static void test_crbug131181(skiatest::Reporter*) {
+    /*
+     fX = 18.8943768,
+     fY = 129.121277
+     }, {
+     fX = 18.8937435,
+     fY = 129.121689
+     }, {
+     fX = 18.8950119,
+     fY = 129.120422
+     }, {
+     fX = 18.5030727,
+     fY = 129.13121
+     */
+    uint32_t data[] = {
+        0x419727af, 0x43011f0c, 0x41972663, 0x43011f27,
+        0x419728fc, 0x43011ed4, 0x4194064b, 0x43012197
+    };
+
+    SkPath path;
+    moveToH(&path, &data[0]);
+    cubicToH(&path, &data[2]);
+
+    SkAutoTUnref<SkCanvas> canvas(new_canvas(640, 480));
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas->drawPath(path, paint);
+}
+
+// This used to assert in debug builds (and crash writing bad memory in release)
+// because we overflowed an intermediate value (B coefficient) setting up our
+// stepper for the quadratic. Now we bias that value by 1/2 so we don't overflow
+static void test_crbug_140803(skiatest::Reporter* reporter) {
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 2700, 30*1024);
+    bm.allocPixels();
+    SkCanvas canvas(bm);
+
+    SkPath path;
+    path.moveTo(2762, 20);
+    path.quadTo(11, 21702, 10, 21706);
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    canvas.drawPath(path, paint);
+}
+
+// Need to exercise drawing an inverse-path whose bounds intersect the clip,
+// but whose edges do not (since its a quad which draws only in the bottom half
+// of its bounds).
+// In the debug build, we used to assert in this case, until it was fixed.
+//
+static void test_inversepathwithclip(skiatest::Reporter* reporter) {
+    SkPath path;
+
+    path.moveTo(0, SkIntToScalar(20));
+    path.quadTo(SkIntToScalar(10), SkIntToScalar(10),
+                SkIntToScalar(20), SkIntToScalar(20));
+    path.toggleInverseFillType();
+
+    SkPaint paint;
+
+    SkAutoTUnref<SkCanvas> canvas(new_canvas(640, 480));
+    canvas.get()->save();
+    canvas.get()->clipRect(SkRect::MakeWH(SkIntToScalar(19), SkIntToScalar(11)));
+
+    paint.setAntiAlias(false);
+    canvas.get()->drawPath(path, paint);
+    paint.setAntiAlias(true);
+    canvas.get()->drawPath(path, paint);
+
+    canvas.get()->restore();
+
+    // Now do the test again, with the path flipped, so we only draw in the
+    // top half of our bounds, and have the clip intersect our bounds at the
+    // bottom.
+    path.reset();   // preserves our filltype
+    path.moveTo(0, SkIntToScalar(10));
+    path.quadTo(SkIntToScalar(10), SkIntToScalar(20),
+                SkIntToScalar(20), SkIntToScalar(10));
+    canvas.get()->clipRect(SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(19),
+                                            SkIntToScalar(19), SkIntToScalar(11)));
+
+    paint.setAntiAlias(false);
+    canvas.get()->drawPath(path, paint);
+    paint.setAntiAlias(true);
+    canvas.get()->drawPath(path, paint);
+}
+
+static void test_bug533(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    /*
+        http://code.google.com/p/skia/issues/detail?id=533
+        This particular test/bug only applies to the float case, where the
+        coordinates are very large.
+     */
+    SkPath path;
+    path.moveTo(64, 3);
+    path.quadTo(-329936, -100000000, 1153, 330003);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkAutoTUnref<SkCanvas> canvas(new_canvas(640, 480));
+    canvas.get()->drawPath(path, paint);
+#endif
+}
+
+static void test_crbug_140642(skiatest::Reporter* reporter) {
+    /*
+     *  We used to see this construct, and due to rounding as we accumulated
+     *  our length, the loop where we apply the phase would run off the end of
+     *  the array, since it relied on just -= each interval value, which did not
+     *  behave as "expected". Now the code explicitly checks for walking off the
+     *  end of that array.
+
+     *  A different (better) fix might be to rewrite dashing to do all of its
+     *  length/phase/measure math using double, but this may need to be
+     *  coordinated with SkPathMeasure, to be consistent between the two.
+
+     <path stroke="mintcream" stroke-dasharray="27734 35660 2157846850 247"
+           stroke-dashoffset="-248.135982067">
+     */
+
+#ifdef SK_SCALAR_IS_FLOAT
+    const SkScalar vals[] = { 27734, 35660, 2157846850.0f, 247 };
+    SkDashPathEffect dontAssert(vals, 4, -248.135982067f);
+#endif
+}
+
+static void test_crbug_124652(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    /*
+        http://code.google.com/p/chromium/issues/detail?id=124652
+        This particular test/bug only applies to the float case, where
+        large values can "swamp" small ones.
+     */
+    SkScalar intervals[2] = {837099584, 33450};
+    SkAutoTUnref<SkDashPathEffect> dash(
+        new SkDashPathEffect(intervals, 2, -10, false));
+#endif
+}
+
+static void test_bigcubic(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    SkPath path;
+    path.moveTo(64, 3);
+    path.cubicTo(-329936, -100000000, -329936, 100000000, 1153, 330003);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkAutoTUnref<SkCanvas> canvas(new_canvas(640, 480));
+    canvas.get()->drawPath(path, paint);
+#endif
+}
+
 // we used to assert if the bounds of the device (clip) was larger than 32K
 // even when the path itself was smaller. We just draw and hope in the debug
 // version to not assert.
 static void test_giantaa(skiatest::Reporter* reporter) {
     const int W = 400;
     const int H = 400;
-    SkCanvas* canvas = create(SkBitmap::kARGB_8888_Config, 33000, 10, 0, NULL);
-    canvas->clear(0);
-    
+    SkAutoTUnref<SkCanvas> canvas(new_canvas(33000, 10));
+    canvas.get()->clear(SK_ColorTRANSPARENT);
+
     SkPaint paint;
     paint.setAntiAlias(true);
     SkPath path;
     path.addOval(SkRect::MakeXYWH(-10, -10, 20 + W, 20 + H));
-    canvas->drawPath(path, paint);
-    canvas->unref();
+    canvas.get()->drawPath(path, paint);
+}
+
+// Extremely large path_length/dash_length ratios may cause infinite looping
+// in SkDashPathEffect::filterPath() due to single precision rounding.
+// The test is quite expensive, but it should get much faster after the fix
+// for http://crbug.com/165432 goes in.
+static void test_infinite_dash(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(5000000, 0);
+
+    SkScalar intervals[] = { 0.2f, 0.2f };
+    SkDashPathEffect dash(intervals, 2, 0);
+
+    SkPath filteredPath;
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setPathEffect(&dash);
+
+    paint.getFillPath(path, &filteredPath);
+    // If we reach this, we passed.
+    REPORTER_ASSERT(reporter, true);
+}
+
+// http://crbug.com/165432
+// Limit extreme dash path effects to avoid exhausting the system memory.
+static void test_crbug_165432(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.moveTo(0, 0);
+    path.lineTo(10000000, 0);
+
+    SkScalar intervals[] = { 0.5f, 0.5f };
+    SkDashPathEffect dash(intervals, 2, 0);
+
+    SkPaint paint;
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setPathEffect(&dash);
+
+    SkPath filteredPath;
+    SkStrokeRec rec(paint);
+    REPORTER_ASSERT(reporter, !dash.filterPath(&filteredPath, path, &rec, NULL));
+    REPORTER_ASSERT(reporter, filteredPath.isEmpty());
 }
 
 static void TestDrawPath(skiatest::Reporter* reporter) {
     test_giantaa(reporter);
+    test_bug533(reporter);
+    test_bigcubic(reporter);
+    test_crbug_124652(reporter);
+    test_crbug_140642(reporter);
+    test_crbug_140803(reporter);
+    test_inversepathwithclip(reporter);
+    // why?
+    if (false) test_crbug131181(reporter);
+    test_infinite_dash(reporter);
+    test_crbug_165432(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/DrawTextTest.cpp b/tests/DrawTextTest.cpp
index aefe2f9..4f3eb82 100644
--- a/tests/DrawTextTest.cpp
+++ b/tests/DrawTextTest.cpp
@@ -1,115 +1,115 @@
-/*

- * 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 "SkTypes.h"

-

-#include "Test.h"

-#include "SkBitmap.h"

-#include "SkCanvas.h"

-#include "SkColor.h"

-#include "SkPaint.h"

-#include "SkPoint.h"

-#include "SkRect.h"

-

-///////////////////////////////////////////////////////////////////////////////

-

-static const SkColor bgColor = SK_ColorWHITE;

-

-static void create(SkBitmap* bm, SkIRect bound, SkBitmap::Config config) {

-    bm->setConfig(config, bound.width(), bound.height());

-    bm->allocPixels();

-}

-

-static void drawBG(SkCanvas* canvas) {

-    canvas->drawColor(bgColor);

-}

-

-/** Assumes that the ref draw was completely inside ref canvas --

-    implies that everything outside is "bgColor".

-    Checks that all overlap is the same and that all non-overlap on the

-    ref is "bgColor".

- */

-static bool compare(const SkBitmap& ref, const SkIRect& iref,

-                    const SkBitmap& test, const SkIRect& itest)

-{

-    const int xOff = itest.fLeft - iref.fLeft;

-    const int yOff = itest.fTop - iref.fTop;

-

-    SkAutoLockPixels alpRef(ref);

-    SkAutoLockPixels alpTest(test);

-

-    for (int y = 0; y < test.height(); ++y) {

-        for (int x = 0; x < test.width(); ++x) {

-            SkColor testColor = test.getColor(x, y);

-            int refX = x + xOff;

-            int refY = y + yOff;

-            SkColor refColor;

-            if (refX >= 0 && refX < ref.width() &&

-                refY >= 0 && refY < ref.height())

-            {

-                refColor = ref.getColor(refX, refY);

-            } else {

-                refColor = bgColor;

-            }

-            if (refColor != testColor) {

-                return false;

-            }

-        }

-    }

-    return true;

-}

-

-static void test_drawText(skiatest::Reporter* reporter) {

-

-    SkPaint paint;

-    paint.setColor(SK_ColorGRAY);

-    paint.setTextSize(SkIntToScalar(20));

-    

-    SkIRect drawTextRect = SkIRect::MakeWH(64, 64);

-    SkBitmap drawTextBitmap;

-    create(&drawTextBitmap, drawTextRect, SkBitmap::kARGB_8888_Config);

-    SkCanvas drawTextCanvas(drawTextBitmap);

-

-    SkIRect drawPosTextRect = SkIRect::MakeWH(64, 64);

-    SkBitmap drawPosTextBitmap;

-    create(&drawPosTextBitmap, drawPosTextRect, SkBitmap::kARGB_8888_Config);

-    SkCanvas drawPosTextCanvas(drawPosTextBitmap);

-

-    for (float offsetY = 0.0f; offsetY < 1.0f; offsetY += (1.0f / 16.0f)) {

-        for (float offsetX = 0.0f; offsetX < 1.0f; offsetX += (1.0f / 16.0f)) {

-            SkPoint point = SkPoint::Make(SkFloatToScalar(25.0f + offsetX),

-                                          SkFloatToScalar(25.0f + offsetY));

-

-            for (int align = 0; align < SkPaint::kAlignCount; ++align) {

-                paint.setTextAlign(static_cast<SkPaint::Align>(align));

-

-                for (unsigned int flags = 0; flags < (1 << 3); ++flags) {

-                    static const unsigned int antiAliasFlag = 1;

-                    static const unsigned int subpixelFlag = 1 << 1;

-                    static const unsigned int lcdFlag = 1 << 2;

-

-                    paint.setAntiAlias(SkToBool(flags & antiAliasFlag));

-                    paint.setSubpixelText(SkToBool(flags & subpixelFlag));

-                    paint.setLCDRenderText(SkToBool(flags & lcdFlag));

-

-                    // Test: drawText and drawPosText draw the same.

-                    drawBG(&drawTextCanvas);

-                    drawTextCanvas.drawText("A", 1, point.fX, point.fY, paint);

-

-                    drawBG(&drawPosTextCanvas);

-                    drawPosTextCanvas.drawPosText("A", 1, &point, paint);

-

-                    REPORTER_ASSERT(reporter,

-                        compare(drawTextBitmap, drawTextRect,

-                                drawPosTextBitmap, drawPosTextRect));

-                }

-            }

-        }

-    }

-}

-

-#include "TestClassDef.h"

-DEFINE_TESTCLASS("DrawText_DrawPosText", DrawTextTestClass, test_drawText)

+/*
+ * 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 "SkTypes.h"
+
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkColor bgColor = SK_ColorWHITE;
+
+static void create(SkBitmap* bm, SkIRect bound, SkBitmap::Config config) {
+    bm->setConfig(config, bound.width(), bound.height());
+    bm->allocPixels();
+}
+
+static void drawBG(SkCanvas* canvas) {
+    canvas->drawColor(bgColor);
+}
+
+/** Assumes that the ref draw was completely inside ref canvas --
+    implies that everything outside is "bgColor".
+    Checks that all overlap is the same and that all non-overlap on the
+    ref is "bgColor".
+ */
+static bool compare(const SkBitmap& ref, const SkIRect& iref,
+                    const SkBitmap& test, const SkIRect& itest)
+{
+    const int xOff = itest.fLeft - iref.fLeft;
+    const int yOff = itest.fTop - iref.fTop;
+
+    SkAutoLockPixels alpRef(ref);
+    SkAutoLockPixels alpTest(test);
+
+    for (int y = 0; y < test.height(); ++y) {
+        for (int x = 0; x < test.width(); ++x) {
+            SkColor testColor = test.getColor(x, y);
+            int refX = x + xOff;
+            int refY = y + yOff;
+            SkColor refColor;
+            if (refX >= 0 && refX < ref.width() &&
+                refY >= 0 && refY < ref.height())
+            {
+                refColor = ref.getColor(refX, refY);
+            } else {
+                refColor = bgColor;
+            }
+            if (refColor != testColor) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static void test_drawText(skiatest::Reporter* reporter) {
+
+    SkPaint paint;
+    paint.setColor(SK_ColorGRAY);
+    paint.setTextSize(SkIntToScalar(20));
+
+    SkIRect drawTextRect = SkIRect::MakeWH(64, 64);
+    SkBitmap drawTextBitmap;
+    create(&drawTextBitmap, drawTextRect, SkBitmap::kARGB_8888_Config);
+    SkCanvas drawTextCanvas(drawTextBitmap);
+
+    SkIRect drawPosTextRect = SkIRect::MakeWH(64, 64);
+    SkBitmap drawPosTextBitmap;
+    create(&drawPosTextBitmap, drawPosTextRect, SkBitmap::kARGB_8888_Config);
+    SkCanvas drawPosTextCanvas(drawPosTextBitmap);
+
+    for (float offsetY = 0.0f; offsetY < 1.0f; offsetY += (1.0f / 16.0f)) {
+        for (float offsetX = 0.0f; offsetX < 1.0f; offsetX += (1.0f / 16.0f)) {
+            SkPoint point = SkPoint::Make(SkFloatToScalar(25.0f + offsetX),
+                                          SkFloatToScalar(25.0f + offsetY));
+
+            for (int align = 0; align < SkPaint::kAlignCount; ++align) {
+                paint.setTextAlign(static_cast<SkPaint::Align>(align));
+
+                for (unsigned int flags = 0; flags < (1 << 3); ++flags) {
+                    static const unsigned int antiAliasFlag = 1;
+                    static const unsigned int subpixelFlag = 1 << 1;
+                    static const unsigned int lcdFlag = 1 << 2;
+
+                    paint.setAntiAlias(SkToBool(flags & antiAliasFlag));
+                    paint.setSubpixelText(SkToBool(flags & subpixelFlag));
+                    paint.setLCDRenderText(SkToBool(flags & lcdFlag));
+
+                    // Test: drawText and drawPosText draw the same.
+                    drawBG(&drawTextCanvas);
+                    drawTextCanvas.drawText("A", 1, point.fX, point.fY, paint);
+
+                    drawBG(&drawPosTextCanvas);
+                    drawPosTextCanvas.drawPosText("A", 1, &point, paint);
+
+                    REPORTER_ASSERT(reporter,
+                        compare(drawTextBitmap, drawTextRect,
+                                drawPosTextBitmap, drawPosTextRect));
+                }
+            }
+        }
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("DrawText_DrawPosText", DrawTextTestClass, test_drawText)
diff --git a/tests/EmptyPathTest.cpp b/tests/EmptyPathTest.cpp
index b126076..be7d529 100644
--- a/tests/EmptyPathTest.cpp
+++ b/tests/EmptyPathTest.cpp
@@ -28,7 +28,7 @@
     // explicitly specify a trim rowbytes, so we have no padding on each row
     bm.setConfig(SkBitmap::kARGB_8888_Config, DIMENSION, DIMENSION, DIMENSION*4);
     bm.allocPixels();
-    bm.eraseColor(0);
+    bm.eraseColor(SK_ColorTRANSPARENT);
 
     SkCanvas canvas(bm);
     SkPaint p(paint);
@@ -39,7 +39,7 @@
     size_t count = DIMENSION * DIMENSION;
     const SkPMColor* ptr = bm.getAddr32(0, 0);
 
-    SkPMColor andValue = ~0;
+    SkPMColor andValue = ~0U;
     SkPMColor orValue = 0;
     for (size_t i = 0; i < count; ++i) {
         SkPMColor c = ptr[i];
diff --git a/tests/FillPathTest.cpp b/tests/FillPathTest.cpp
index 8271851..0ddf4e2 100644
--- a/tests/FillPathTest.cpp
+++ b/tests/FillPathTest.cpp
@@ -28,7 +28,7 @@
 }
 
 // http://code.google.com/p/skia/issues/detail?id=87
-// Lines which is not clipped by boundary based clipping, 
+// Lines which is not clipped by boundary based clipping,
 // but skipped after tessellation, should be cleared by the blitter.
 static void TestFillPathInverse(skiatest::Reporter* reporter) {
   FakeBlitter blitter;
@@ -38,8 +38,9 @@
   int width  = 200;
   int expected_lines = 5;
   clip.set(0, height - expected_lines, width, height);
-  path.moveTo(0.0, 0.0);
-  path.quadTo(width/2, height, width, 0.0);
+  path.moveTo(0.0f, 0.0f);
+  path.quadTo(SkIntToScalar(width/2), SkIntToScalar(height),
+              SkIntToScalar(width), 0.0f);
   path.close();
   path.setFillType(SkPath::kInverseWinding_FillType);
   SkScan::FillPath(path, clip, &blitter);
diff --git a/tests/FlatDataTest.cpp b/tests/FlatDataTest.cpp
new file mode 100644
index 0000000..10d297a
--- /dev/null
+++ b/tests/FlatDataTest.cpp
@@ -0,0 +1,91 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkColorFilter.h"
+#include "SkGradientShader.h"
+#include "SkPaint.h"
+#include "SkPictureFlat.h"
+#include "SkShader.h"
+#include "SkXfermode.h"
+#include "Test.h"
+
+static void flattenFlattenableProc(SkOrderedWriteBuffer& buffer,
+                                   const void* obj) {
+    buffer.writeFlattenable((SkFlattenable*)obj);
+}
+
+class Controller : public SkChunkFlatController {
+public:
+    Controller() : INHERITED(1024) {
+        this->INHERITED::setNamedFactorySet(SkNEW(SkNamedFactorySet))->unref();
+    }
+private:
+    typedef SkChunkFlatController INHERITED;
+};
+
+/**
+ * Verify that two SkFlatData objects that created from the same object are
+ * identical when using an SkNamedFactorySet.
+ * @param reporter Object to report failures.
+ * @param obj Flattenable object to be flattened.
+ * @param flattenProc Function that flattens objects with the same type as obj.
+ */
+static void testCreate(skiatest::Reporter* reporter, const void* obj,
+                       void (*flattenProc)(SkOrderedWriteBuffer&, const void*)) {
+    Controller controller;
+    // No need to delete data because that will be taken care of by the
+    // controller.
+    SkFlatData* data1 = SkFlatData::Create(&controller, obj, 0, flattenProc);
+    data1->setSentinelInCache();
+    SkFlatData* data2 = SkFlatData::Create(&controller, obj, 1, flattenProc);
+    data2->setSentinelAsCandidate();
+    REPORTER_ASSERT(reporter, SkFlatData::Compare(data1, data2) == 0);
+}
+
+static void Tests(skiatest::Reporter* reporter) {
+    // Test flattening SkShader
+    SkPoint points[2];
+    points[0].set(0, 0);
+    points[1].set(SkIntToScalar(20), SkIntToScalar(20));
+    SkColor colors[2];
+    colors[0] = SK_ColorRED;
+    colors[1] = SK_ColorBLUE;
+    SkShader* shader = SkGradientShader::CreateLinear(points, colors, NULL,
+                                            2, SkShader::kRepeat_TileMode);
+    SkAutoUnref aur(shader);
+    testCreate(reporter, shader, flattenFlattenableProc);
+
+    // Test SkBitmap
+    {
+        SkBitmap bm;
+        bm.setConfig(SkBitmap::kARGB_8888_Config, 50, 50);
+        bm.allocPixels();
+        SkCanvas canvas(bm);
+        SkPaint paint;
+        paint.setShader(shader);
+        canvas.drawPaint(paint);
+        testCreate(reporter, &bm, &SkFlattenObjectProc<SkBitmap>);
+    }
+
+    // Test SkColorFilter
+    SkColorFilter* cf = SkColorFilter::CreateLightingFilter(SK_ColorBLUE,
+                                                            SK_ColorRED);
+    SkAutoUnref aurcf(cf);
+    testCreate(reporter, cf, &flattenFlattenableProc);
+
+    // Test SkXfermode
+    SkXfermode* xfer = SkXfermode::Create(SkXfermode::kDstOver_Mode);
+    SkAutoUnref aurxf(xfer);
+    testCreate(reporter, xfer, &flattenFlattenableProc);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("FlatData", FlatDataClass, Tests)
diff --git a/tests/FlateTest.cpp b/tests/FlateTest.cpp
index 8697df9..879973f 100644
--- a/tests/FlateTest.cpp
+++ b/tests/FlateTest.cpp
@@ -72,14 +72,16 @@
     inputSize = testStream->getLength();
     if (inputSize == 0)
         inputSize = testStream->read(NULL, SkZeroSizeMemStream::kGetSizeKey);
-    REPORTER_ASSERT(reporter, data1.size() == inputSize);
+    REPORTER_ASSERT(reporter, data1->size() == inputSize);
     REPORTER_ASSERT(reporter, memcmp(testStream->getMemoryBase(),
-                                     data1.data(), data1.size()) == 0);
+                                     data1->data(),
+                                     data1->size()) == 0);
 
     // Check that the uncompressed data matches the source data.
     SkAutoDataUnref data2(uncompressed.copyToData());
     REPORTER_ASSERT(reporter, testData.getLength() == uncompressed.getOffset());
-    REPORTER_ASSERT(reporter, memcmp(testData.getMemoryBase(), data2.data(),
+    REPORTER_ASSERT(reporter, memcmp(testData.getMemoryBase(),
+                                     data2->data(),
                                      testData.getLength()) == 0);
 }
 
diff --git a/tests/FontHostStreamTest.cpp b/tests/FontHostStreamTest.cpp
new file mode 100644
index 0000000..bbf1a03
--- /dev/null
+++ b/tests/FontHostStreamTest.cpp
@@ -0,0 +1,109 @@
+/*
+ * 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 "SkTypes.h"
+
+#include "Test.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkFontHost.h"
+#include "SkGraphics.h"
+#include "SkPaint.h"
+#include "SkPoint.h"
+#include "SkRect.h"
+#include "SkTypeface.h"
+
+///////////////////////////////////////////////////////////////////////////////
+
+static const SkColor bgColor = SK_ColorWHITE;
+
+static void create(SkBitmap* bm, SkIRect bound, SkBitmap::Config config) {
+    bm->setConfig(config, bound.width(), bound.height());
+    bm->allocPixels();
+}
+
+static void drawBG(SkCanvas* canvas) {
+    canvas->drawColor(bgColor);
+}
+
+/** Assumes that the ref draw was completely inside ref canvas --
+    implies that everything outside is "bgColor".
+    Checks that all overlap is the same and that all non-overlap on the
+    ref is "bgColor".
+ */
+static bool compare(const SkBitmap& ref, const SkIRect& iref,
+                    const SkBitmap& test, const SkIRect& itest)
+{
+    const int xOff = itest.fLeft - iref.fLeft;
+    const int yOff = itest.fTop - iref.fTop;
+
+    SkAutoLockPixels alpRef(ref);
+    SkAutoLockPixels alpTest(test);
+
+    for (int y = 0; y < test.height(); ++y) {
+        for (int x = 0; x < test.width(); ++x) {
+            SkColor testColor = test.getColor(x, y);
+            int refX = x + xOff;
+            int refY = y + yOff;
+            SkColor refColor;
+            if (refX >= 0 && refX < ref.width() &&
+                refY >= 0 && refY < ref.height())
+            {
+                refColor = ref.getColor(refX, refY);
+            } else {
+                refColor = bgColor;
+            }
+            if (refColor != testColor) {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+static void test_fontHostStream(skiatest::Reporter* reporter) {
+
+    {
+        SkPaint paint;
+        paint.setColor(SK_ColorGRAY);
+        paint.setTextSize(SkIntToScalar(30));
+
+        paint.setTypeface(SkTypeface::CreateFromName("Georgia", SkTypeface::kNormal))->unref();
+
+        SkIRect origRect = SkIRect::MakeWH(64, 64);
+        SkBitmap origBitmap;
+        create(&origBitmap, origRect, SkBitmap::kARGB_8888_Config);
+        SkCanvas origCanvas(origBitmap);
+
+        SkIRect streamRect = SkIRect::MakeWH(64, 64);
+        SkBitmap streamBitmap;
+        create(&streamBitmap, streamRect, SkBitmap::kARGB_8888_Config);
+        SkCanvas streamCanvas(streamBitmap);
+
+        SkPoint point = SkPoint::Make(24, 32);
+
+        // Test: origTypeface and streamTypeface from orig data draw the same
+        drawBG(&origCanvas);
+        origCanvas.drawText("A", 1, point.fX, point.fY, paint);
+
+        SkTypeface* origTypeface = paint.getTypeface();
+        const SkFontID typefaceID = SkTypeface::UniqueID(origTypeface);
+        SkStream* fontData = SkFontHost::OpenStream(typefaceID);
+        SkTypeface* streamTypeface = SkTypeface::CreateFromStream(fontData);
+        SkSafeUnref(paint.setTypeface(streamTypeface));
+        drawBG(&streamCanvas);
+        streamCanvas.drawPosText("A", 1, &point, paint);
+
+        REPORTER_ASSERT(reporter,
+                        compare(origBitmap, origRect, streamBitmap, streamRect));
+    }
+    //Make sure the typeface is deleted and removed.
+    SkGraphics::PurgeFontCache();
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("FontHost::CreateTypefaceFromStream", FontHostStreamTestClass, test_fontHostStream)
diff --git a/tests/FontHostTest.cpp b/tests/FontHostTest.cpp
index 8ab7ad3..4cd7812 100644
--- a/tests/FontHostTest.cpp
+++ b/tests/FontHostTest.cpp
@@ -6,8 +6,9 @@
  */
 
 #include "Test.h"
+#include "SkPaint.h"
 #include "SkTypeface.h"
-#include "SkFontHost.h"
+#include "SkEndian.h"
 
 //#define DUMP_TABLES
 
@@ -24,19 +25,37 @@
     {   kFontTableTag_maxp,         32 },
 };
 
-static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) {
-    SkFontID fontID = face->uniqueID();
+static void test_unitsPerEm(skiatest::Reporter* reporter, SkTypeface* face) {
+    int upem = face->getUnitsPerEm();
+    if (0 == upem) return;
 
-    int count = SkFontHost::CountTables(fontID);
+    size_t size = face->getTableSize(kFontTableTag_head);
+    if (size) {
+        SkAutoMalloc storage(size);
+        char* ptr = (char*)storage.get();
+        face->getTableData(kFontTableTag_head, 0, size, ptr);
+        // unitsPerEm is at offset 18 into the 'head' table.
+        int upem2 = SkEndian_SwapBE16(*(uint16_t*)&ptr[18]);
+        REPORTER_ASSERT(reporter, upem2 == upem);
+    }
+}
+
+static void test_tables(skiatest::Reporter* reporter, SkTypeface* face) {
+    if (false) { // avoid bit rot, suppress warning
+        SkFontID fontID = face->uniqueID();
+        REPORTER_ASSERT(reporter, fontID);
+    }
+
+    int count = face->countTables();
 
     SkAutoTMalloc<SkFontTableTag> storage(count);
     SkFontTableTag* tags = storage.get();
 
-    int count2 = SkFontHost::GetTableTags(fontID, tags);
+    int count2 = face->getTableTags(tags);
     REPORTER_ASSERT(reporter, count2 == count);
 
     for (int i = 0; i < count; ++i) {
-        size_t size = SkFontHost::GetTableSize(fontID, tags[i]);
+        size_t size = face->getTableSize(tags[i]);
         REPORTER_ASSERT(reporter, size > 0);
 
 #ifdef DUMP_TABLES
@@ -54,12 +73,11 @@
                 REPORTER_ASSERT(reporter, gKnownTableSizes[j].fSize == size);
             }
         }
-        
+
         // do we get the same size from GetTableData and GetTableSize
         {
             SkAutoMalloc data(size);
-            size_t size2 = SkFontHost::GetTableData(fontID, tags[i], 0, size,
-                                                    data.get());
+            size_t size2 = face->getTableData(tags[i], 0, size, data.get());
             REPORTER_ASSERT(reporter, size2 == size);
         }
     }
@@ -69,7 +87,7 @@
     static const char* const gNames[] = {
         NULL,   // default font
         "Arial", "Times", "Times New Roman", "Helvetica", "Courier",
-        "Courier New",
+        "Courier New", "Terminal", "MS Sans Serif",
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gNames); ++i) {
@@ -80,13 +98,86 @@
             SkDebugf("%s\n", gNames[i]);
 #endif
             test_tables(reporter, face);
+            test_unitsPerEm(reporter, face);
             face->unref();
         }
     }
 }
 
+/*
+ * Verifies that the advance values returned by generateAdvance and
+ * generateMetrics match.
+ */
+static void test_advances(skiatest::Reporter* reporter) {
+    static const char* const faces[] = {
+        NULL,   // default font
+        "Arial", "Times", "Times New Roman", "Helvetica", "Courier",
+        "Courier New", "Verdana", "monospace",
+    };
+
+    static const struct {
+        SkPaint::Hinting    hinting;
+        unsigned            flags;
+    } settings[] = {
+        { SkPaint::kNo_Hinting,     0                               },
+        { SkPaint::kNo_Hinting,     SkPaint::kLinearText_Flag       },
+        { SkPaint::kNo_Hinting,     SkPaint::kSubpixelText_Flag     },
+        { SkPaint::kSlight_Hinting, 0                               },
+        { SkPaint::kSlight_Hinting, SkPaint::kLinearText_Flag       },
+        { SkPaint::kSlight_Hinting, SkPaint::kSubpixelText_Flag     },
+        { SkPaint::kNormal_Hinting, 0                               },
+        { SkPaint::kNormal_Hinting, SkPaint::kLinearText_Flag       },
+        { SkPaint::kNormal_Hinting, SkPaint::kSubpixelText_Flag     },
+    };
+
+    static const struct {
+        SkScalar    fScaleX;
+        SkScalar    fSkewX;
+    } gScaleRec[] = {
+        { SK_Scalar1, 0 },
+        { SK_Scalar1/2, 0 },
+        // these two exercise obliquing (skew)
+        { SK_Scalar1, -SK_Scalar1/4 },
+        { SK_Scalar1/2, -SK_Scalar1/4 },
+    };
+
+    SkPaint paint;
+    char txt[] = "long.text.with.lots.of.dots.";
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(faces); i++) {
+        SkTypeface* face = SkTypeface::CreateFromName(faces[i], SkTypeface::kNormal);
+        paint.setTypeface(face);
+
+        for (size_t j = 0; j  < SK_ARRAY_COUNT(settings); j++) {
+            paint.setHinting(settings[j].hinting);
+            paint.setLinearText((settings[j].flags & SkPaint::kLinearText_Flag) != 0);
+            paint.setSubpixelText((settings[j].flags & SkPaint::kSubpixelText_Flag) != 0);
+
+            for (size_t k = 0; k < SK_ARRAY_COUNT(gScaleRec); ++k) {
+                paint.setTextScaleX(gScaleRec[k].fScaleX);
+                paint.setTextSkewX(gScaleRec[k].fSkewX);
+
+                SkRect bounds;
+
+                // For no hinting and light hinting this should take the
+                // optimized generateAdvance path.
+                SkScalar width1 = paint.measureText(txt, strlen(txt));
+
+                // Requesting the bounds forces a generateMetrics call.
+                SkScalar width2 = paint.measureText(txt, strlen(txt), &bounds);
+
+                // SkDebugf("Font: %s, generateAdvance: %f, generateMetrics: %f\n",
+                //    faces[i], SkScalarToFloat(width1), SkScalarToFloat(width2));
+
+                REPORTER_ASSERT(reporter, width1 == width2);
+            }
+        }
+    }
+}
+
 static void TestFontHost(skiatest::Reporter* reporter) {
     test_tables(reporter);
+    test_advances(reporter);
 }
 
 // need tests for SkStrSearch
diff --git a/tests/GLInterfaceValidation.cpp b/tests/GLInterfaceValidation.cpp
index 5cee0e4..c49de3e 100755
--- a/tests/GLInterfaceValidation.cpp
+++ b/tests/GLInterfaceValidation.cpp
@@ -6,9 +6,19 @@
  * found in the LICENSE file.
  */
 
+
+
 #include "Test.h"
+// This is a GPU-backend specific test
+#if SK_SUPPORT_GPU
+
+#if SK_ANGLE
+#include "gl/SkANGLEGLContext.h"
+#endif
 #include "gl/SkNativeGLContext.h"
+#if SK_MESA
 #include "gl/SkMesaGLContext.h"
+#endif
 
 static void GLInterfaceValidationTest(skiatest::Reporter* reporter) {
     typedef const GrGLInterface* (*interfaceFactory)();
@@ -16,10 +26,14 @@
        interfaceFactory fFactory;
        const char* fName;
     } interfaceFactories[] = {
+#if SK_ANGLE
+        {GrGLCreateANGLEInterface, "ANGLE"},
+#endif
         {GrGLCreateNativeInterface, "Native"},
 #if SK_MESA
         {GrGLCreateMesaInterface, "Mesa"},
 #endif
+        {GrGLCreateDebugInterface, "Debug"},
         {GrGLCreateNullInterface, "Null"},
     };
 
@@ -68,3 +82,4 @@
                  GLInterfaceValidationTestClass,
                  GLInterfaceValidationTest)
 
+#endif
diff --git a/tests/GLProgramsTest.cpp b/tests/GLProgramsTest.cpp
index 583b802..3106377 100644
--- a/tests/GLProgramsTest.cpp
+++ b/tests/GLProgramsTest.cpp
@@ -6,16 +6,175 @@
  * found in the LICENSE file.
  */
 
-#include "Test.h"
-#include "GrContext.h"
-#include "gl/GrGpuGLShaders.h"
+// This is a GPU-backend specific test. It relies on static intializers to work
 
-static void GLProgramsTest(skiatest::Reporter* reporter, GrContext* context) {
-    GrGpuGLShaders* shadersGpu = (GrGpuGLShaders*) context->getGpu();
-    REPORTER_ASSERT(reporter, shadersGpu->programUnitTest());
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU && SK_ALLOW_STATIC_GLOBAL_INITIALIZERS
+
+#include "gl/GrGpuGL.h"
+#include "GrBackendEffectFactory.h"
+#include "effects/GrConfigConversionEffect.h"
+
+#include "SkRandom.h"
+#include "Test.h"
+
+namespace {
+
+// SkRandoms nextU() values have patterns in the low bits
+// So using nextU() % array_count might never take some values.
+int random_int(SkRandom* r, int count) {
+    return (int)(r->nextF() * count);
 }
 
+bool random_bool(SkRandom* r) {
+    return r->nextF() > .5f;
+}
+
+const GrEffectRef* create_random_effect(SkRandom* random,
+                                        GrContext* context,
+                                        GrTexture* dummyTextures[]) {
+
+    SkRandom sk_random;
+    sk_random.setSeed(random->nextU());
+    GrEffectRef* effect = GrEffectTestFactory::CreateStage(&sk_random, context, dummyTextures);
+    GrAssert(effect);
+    return effect;
+}
+}
+
+bool GrGpuGL::programUnitTest() {
+
+    GrTextureDesc dummyDesc;
+    dummyDesc.fConfig = kSkia8888_PM_GrPixelConfig;
+    dummyDesc.fWidth = 34;
+    dummyDesc.fHeight = 18;
+    SkAutoTUnref<GrTexture> dummyTexture1(this->createTexture(dummyDesc, NULL, 0));
+    dummyDesc.fConfig = kAlpha_8_GrPixelConfig;
+    dummyDesc.fWidth = 16;
+    dummyDesc.fHeight = 22;
+    SkAutoTUnref<GrTexture> dummyTexture2(this->createTexture(dummyDesc, NULL, 0));
+
+    static const int NUM_TESTS = 512;
+
+    SkRandom random;
+    for (int t = 0; t < NUM_TESTS; ++t) {
+
+#if 0
+        GrPrintf("\nTest Program %d\n-------------\n", t);
+        static const int stop = -1;
+        if (t == stop) {
+            int breakpointhere = 9;
+        }
+#endif
+
+        ProgramDesc pdesc;
+        pdesc.fVertexLayout = 0;
+        pdesc.fEmitsPointSize = random.nextF() > .5f;
+        pdesc.fColorInput = random_int(&random, ProgramDesc::kColorInputCnt);
+        pdesc.fCoverageInput = random_int(&random, ProgramDesc::kColorInputCnt);
+
+        pdesc.fColorFilterXfermode = random_int(&random, SkXfermode::kCoeffModesCnt);
+
+        pdesc.fFirstCoverageStage = random_int(&random, GrDrawState::kNumStages);
+
+        pdesc.fVertexLayout |= random_bool(&random) ?
+                                    GrDrawState::kCoverage_VertexLayoutBit :
+                                    0;
+
+#if GR_GL_EXPERIMENTAL_GS
+        pdesc.fExperimentalGS = this->getCaps().geometryShaderSupport() &&
+                                random_bool(&random);
+#endif
+
+        bool edgeAA = random_bool(&random);
+        if (edgeAA) {
+            pdesc.fVertexLayout |= GrDrawState::kEdge_VertexLayoutBit;
+            if (this->getCaps().shaderDerivativeSupport()) {
+                pdesc.fVertexEdgeType = (GrDrawState::VertexEdgeType) random_int(&random, GrDrawState::kVertexEdgeTypeCnt);
+                pdesc.fDiscardIfOutsideEdge = random.nextBool();
+            } else {
+                pdesc.fVertexEdgeType = GrDrawState::kHairLine_EdgeType;
+                pdesc.fDiscardIfOutsideEdge = false;
+            }
+        } else {
+        }
+
+        if (this->getCaps().dualSourceBlendingSupport()) {
+            pdesc.fDualSrcOutput = random_int(&random, ProgramDesc::kDualSrcOutputCnt);
+        } else {
+            pdesc.fDualSrcOutput = ProgramDesc::kNone_DualSrcOutput;
+        }
+
+        GrEffectStage stages[GrDrawState::kNumStages];
+
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            // enable the stage?
+            if (random_bool(&random)) {
+                // use separate tex coords?
+                if (random_bool(&random)) {
+                    int t = random_int(&random, GrDrawState::kMaxTexCoords);
+                    pdesc.fVertexLayout |= GrDrawState::StageTexCoordVertexLayoutBit(s, t);
+                }
+                // use text-formatted verts?
+                if (random_bool(&random)) {
+                    pdesc.fVertexLayout |= GrDrawState::kTextFormat_VertexLayoutBit;
+                }
+
+                GrTexture* dummyTextures[] = {dummyTexture1.get(), dummyTexture2.get()};
+                SkAutoTUnref<const GrEffectRef> effect(create_random_effect(&random,
+                                                                            getContext(),
+                                                                            dummyTextures));
+                stages[s].setEffect(effect.get());
+                if (NULL != stages[s].getEffect()) {
+                    pdesc.fEffectKeys[s] =
+                        (*stages[s].getEffect())->getFactory().glEffectKey(stages[s],
+                                                                           this->glCaps());
+                }
+            }
+        }
+        const GrEffectStage* stagePtrs[GrDrawState::kNumStages];
+        for (int s = 0; s < GrDrawState::kNumStages; ++s) {
+            stagePtrs[s] = &stages[s];
+        }
+        SkAutoTUnref<GrGLProgram> program(GrGLProgram::Create(this->glContextInfo(),
+                                                              pdesc,
+                                                              stagePtrs));
+        if (NULL == program.get()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static void GLProgramsTest(skiatest::Reporter* reporter, GrContext* context) {
+    GrGpuGL* shadersGpu = static_cast<GrGpuGL*>(context->getGpu());
+    REPORTER_ASSERT(reporter, shadersGpu->programUnitTest());
+}
 
 #include "TestClassDef.h"
 DEFINE_GPUTESTCLASS("GLPrograms", GLProgramsTestClass, GLProgramsTest)
 
+// This is evil evil evil. The linker may throw away whole translation units as dead code if it
+// thinks none of the functions are called. It will do this even if there are static initializers
+// in the unit that could pass pointers to functions from the unit out to other translation units!
+// We force some of the effects that would otherwise be discarded to link here.
+
+#include "SkLightingImageFilter.h"
+#include "SkMagnifierImageFilter.h"
+#include "SkColorMatrixFilter.h"
+
+void forceLinking();
+
+void forceLinking() {
+    SkLightingImageFilter::CreateDistantLitDiffuse(SkPoint3(0,0,0), 0, 0, 0);
+    SkMagnifierImageFilter mag(SkRect::MakeWH(SK_Scalar1, SK_Scalar1), SK_Scalar1);
+    GrConfigConversionEffect::Create(NULL,
+                                     false,
+                                     GrConfigConversionEffect::kNone_PMConversion,
+                                     SkMatrix::I());
+    SkScalar matrix[20];
+    SkColorMatrixFilter cmf(matrix);
+}
+
+#endif
diff --git a/tests/GeometryTest.cpp b/tests/GeometryTest.cpp
index 9a0f78f..69d980a 100644
--- a/tests/GeometryTest.cpp
+++ b/tests/GeometryTest.cpp
@@ -15,7 +15,7 @@
 static void testChopCubic(skiatest::Reporter* reporter) {
     /*
         Inspired by this test, which used to assert that the tValues had dups
-     
+
         <path stroke="#202020" d="M0,0 C0,0 1,1 2190,5130 C2190,5070 2220,5010 2205,4980" />
      */
     const SkPoint src[] = {
@@ -28,6 +28,9 @@
     SkScalar tValues[3];
     // make sure we don't assert internally
     int count = SkChopCubicAtMaxCurvature(src, dst, tValues);
+    if (false) { // avoid bit rot, suppress warning
+        REPORTER_ASSERT(reporter, count);
+    }
 }
 
 
@@ -54,7 +57,7 @@
     for (int i = 0; i < 4; ++i) {
         REPORTER_ASSERT(reporter, nearly_equal(cubic[i], dst[i]));
     }
-    
+
     testChopCubic(reporter);
 }
 
diff --git a/tests/GpuBitmapCopyTest.cpp b/tests/GpuBitmapCopyTest.cpp
new file mode 100644
index 0000000..63e3cca
--- /dev/null
+++ b/tests/GpuBitmapCopyTest.cpp
@@ -0,0 +1,207 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#if SK_SUPPORT_GPU
+
+#include "GrContext.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkColor.h"
+#include "SkGpuDevice.h"
+#include "SkPaint.h"
+#include "SkPixelRef.h"
+#include "SkRect.h"
+#include "Test.h"
+
+static const char* boolStr(bool value) {
+    return value ? "true" : "false";
+}
+
+// these are in the same order as the SkBitmap::Config enum
+static const char* gConfigName[] = {
+    "None", "4444", "8888"
+};
+
+struct Pair {
+    SkBitmap::Config    fConfig;
+    const char*         fValid;
+};
+
+extern bool getUpperLeftFromOffset(const SkBitmap& bm, int* x, int* y);
+extern size_t getSubOffset(const SkBitmap& bm, int x, int y);
+
+/**
+ *  Tests that getUpperLeftFromOffset and getSubOffset agree with each other.
+ */
+static void TestSubsetHelpers(skiatest::Reporter* reporter, const SkBitmap& bitmap){
+    int x, y;
+    bool upperLeft = getUpperLeftFromOffset(bitmap, &x, &y);
+    REPORTER_ASSERT(reporter, upperLeft);
+    REPORTER_ASSERT(reporter, getSubOffset(bitmap, x, y) == bitmap.pixelRefOffset());
+}
+
+/**
+ *  Check to ensure that copying a GPU-backed SkBitmap behaved as expected.
+ *  @param reporter Used to report failures.
+ *  @param desiredConfig Config being copied to. If the copy succeeded, dst must have this Config.
+ *  @param success True if the copy succeeded.
+ *  @param src A GPU-backed SkBitmap that had copyTo or deepCopyTo called on it.
+ *  @param dst SkBitmap that was copied to.
+ *  @param deepCopy True if deepCopyTo was used; false if copyTo was used.
+ *  @param subset Portion of src's SkPixelRef that src represents. dst should represent the same
+ *      portion after the copy. Pass NULL for all pixels.
+ */
+static void TestIndividualCopy(skiatest::Reporter* reporter, const SkBitmap::Config desiredConfig,
+                               const bool success, const SkBitmap& src, const SkBitmap& dst,
+                               const bool deepCopy = true, const SkIRect* subset = NULL) {
+    if (success) {
+        REPORTER_ASSERT(reporter, src.width() == dst.width());
+        REPORTER_ASSERT(reporter, src.height() == dst.height());
+        REPORTER_ASSERT(reporter, dst.config() == desiredConfig);
+        if (src.config() == dst.config()) {
+            // FIXME: When calling copyTo (so deepCopy is false here), sometimes we copy the pixels
+            // exactly, in which case the IDs should be the same, but sometimes we do a bitmap draw,
+            // in which case the IDs should not be the same. Is there any way to determine which is
+            // the case at this point?
+            if (deepCopy) {
+                REPORTER_ASSERT(reporter, src.getGenerationID() == dst.getGenerationID());
+            }
+            REPORTER_ASSERT(reporter, src.pixelRef() != NULL && dst.pixelRef() != NULL);
+
+            // Do read backs and make sure that the two are the same.
+            SkBitmap srcReadBack, dstReadBack;
+            {
+                SkASSERT(src.getTexture() != NULL);
+                bool readBack = src.pixelRef()->readPixels(&srcReadBack, subset);
+                REPORTER_ASSERT(reporter, readBack);
+            }
+            if (dst.getTexture() != NULL) {
+                bool readBack = dst.pixelRef()->readPixels(&dstReadBack, subset);
+                REPORTER_ASSERT(reporter, readBack);
+            } else {
+                // If dst is not a texture, do a copy instead, to the same config as srcReadBack.
+                bool copy = dst.copyTo(&dstReadBack, srcReadBack.config());
+                REPORTER_ASSERT(reporter, copy);
+            }
+
+            SkAutoLockPixels srcLock(srcReadBack);
+            SkAutoLockPixels dstLock(dstReadBack);
+            REPORTER_ASSERT(reporter, srcReadBack.readyToDraw() && dstReadBack.readyToDraw());
+
+            const char* srcP = static_cast<const char*>(srcReadBack.getAddr(0, 0));
+            const char* dstP = static_cast<const char*>(dstReadBack.getAddr(0, 0));
+            REPORTER_ASSERT(reporter, srcP != dstP);
+
+            REPORTER_ASSERT(reporter, !memcmp(srcP, dstP, srcReadBack.getSize()));
+        } else {
+            REPORTER_ASSERT(reporter, src.getGenerationID() != dst.getGenerationID());
+        }
+
+        // If the copy used a subset, test the pixel offset calculation functions.
+        if (subset != NULL) {
+            TestSubsetHelpers(reporter, dst);
+        }
+    } else {
+        // dst should be unchanged from its initial state
+        REPORTER_ASSERT(reporter, dst.config() == SkBitmap::kNo_Config);
+        REPORTER_ASSERT(reporter, dst.width() == 0);
+        REPORTER_ASSERT(reporter, dst.height() == 0);
+    }
+
+}
+
+// Stripped down version of TestBitmapCopy that checks basic fields (width, height, config, genID)
+// to ensure that they were copied properly.
+static void TestGpuBitmapCopy(skiatest::Reporter* reporter, GrContext* grContext) {
+#ifdef SK_BUILD_FOR_ANDROID // https://code.google.com/p/skia/issues/detail?id=753
+    return;
+#endif
+    if (NULL == grContext) {
+        return;
+    }
+    static const Pair gPairs[] = {
+        { SkBitmap::kNo_Config,         "000"  },
+        { SkBitmap::kARGB_4444_Config,  "011"  },
+        { SkBitmap::kARGB_8888_Config,  "011"  },
+    };
+
+    const int W = 20;
+    const int H = 33;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gPairs); i++) {
+        SkBitmap src, dst;
+
+        SkGpuDevice* device = SkNEW_ARGS(SkGpuDevice, (grContext, gPairs[i].fConfig, W, H));
+        SkAutoUnref aur(device);
+        src = device->accessBitmap(false);
+        device->clear(SK_ColorWHITE);
+
+        // Draw something different to the same portion of the bitmap that we will extract as a
+        // subset, so that comparing the pixels of the subset will be meaningful.
+        SkIRect subsetRect = SkIRect::MakeLTRB(W/2, H/2, W, H);
+        SkCanvas drawingCanvas(device);
+        SkPaint paint;
+        paint.setColor(SK_ColorRED);
+        drawingCanvas.drawRect(SkRect::MakeFromIRect(subsetRect), paint);
+
+        // Extract a subset. If this succeeds we will test copying the subset.
+        SkBitmap subset;
+        const bool extracted = src.extractSubset(&subset, subsetRect);
+        if (extracted) {
+            TestSubsetHelpers(reporter, subset);
+        }
+
+        for (size_t j = 0; j < SK_ARRAY_COUNT(gPairs); j++) {
+            dst.reset();
+            bool success = src.deepCopyTo(&dst, gPairs[j].fConfig);
+            bool expected = gPairs[i].fValid[j] != '0';
+            if (success != expected) {
+                SkString str;
+                str.printf("SkBitmap::deepCopyTo from %s to %s. expected %s returned %s",
+                           gConfigName[i], gConfigName[j], boolStr(expected),
+                           boolStr(success));
+                reporter->reportFailed(str);
+            }
+
+            bool canSucceed = src.canCopyTo(gPairs[j].fConfig);
+            if (success != canSucceed) {
+                SkString str;
+                str.printf("SkBitmap::deepCopyTo from %s to %s returned %s,"
+                           "but canCopyTo returned %s",
+                           gConfigName[i], gConfigName[j], boolStr(success),
+                           boolStr(canSucceed));
+                reporter->reportFailed(str);
+            }
+
+            TestIndividualCopy(reporter, gPairs[j].fConfig, success, src, dst);
+
+            // Test copying the subset bitmap, using both copyTo and deepCopyTo.
+            if (extracted) {
+                SkBitmap subsetCopy;
+                success = subset.copyTo(&subsetCopy, gPairs[j].fConfig);
+                REPORTER_ASSERT(reporter, success == expected);
+                REPORTER_ASSERT(reporter, success == canSucceed);
+                TestIndividualCopy(reporter, gPairs[j].fConfig, success, subset, subsetCopy, false,
+                                   &subsetRect);
+
+                // Reset the bitmap so that a failed copyTo will leave it in the expected state.
+                subsetCopy.reset();
+                success = subset.deepCopyTo(&subsetCopy, gPairs[j].fConfig);
+                REPORTER_ASSERT(reporter, success == expected);
+                REPORTER_ASSERT(reporter, success == canSucceed);
+                TestIndividualCopy(reporter, gPairs[j].fConfig, success, subset, subsetCopy, true,
+                                   &subsetRect);
+            }
+        } // for (size_t j = ...
+    } // for (size_t i = ...
+}
+
+#include "TestClassDef.h"
+DEFINE_GPUTESTCLASS("GpuBitmapCopy", TestGpuBitmapCopyClass, TestGpuBitmapCopy)
+
+#endif
diff --git a/tests/GrContextFactoryTest.cpp b/tests/GrContextFactoryTest.cpp
new file mode 100644
index 0000000..80f1418
--- /dev/null
+++ b/tests/GrContextFactoryTest.cpp
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+
+// This is a GPU-backend specific test
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+
+static void test_context_factory(skiatest::Reporter* reporter) {
+    GrContextFactory contextFactory;
+
+    // Before we ask for a context, we expect the GL context to not be there.
+    REPORTER_ASSERT(reporter,
+                    NULL == contextFactory.getGLContext(GrContextFactory::kNative_GLContextType));
+
+    // After we ask for a context, we expect that the GL context to be there.
+    contextFactory.get(GrContextFactory::kNative_GLContextType);
+    REPORTER_ASSERT(reporter,
+                    contextFactory.getGLContext(GrContextFactory::kNative_GLContextType) != NULL);
+
+    // If we did not ask for a context with the particular GL context, we would
+    // expect the particular GL context to not be there.
+    REPORTER_ASSERT(reporter,
+                    NULL == contextFactory.getGLContext(GrContextFactory::kNull_GLContextType));
+}
+
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("GrContextFactory", GrContextFactoryClass, test_context_factory);
+
+#endif
diff --git a/tests/GrMemoryPoolTest.cpp b/tests/GrMemoryPoolTest.cpp
new file mode 100644
index 0000000..8660ea2
--- /dev/null
+++ b/tests/GrMemoryPoolTest.cpp
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "Test.h"
+// This is a GPU-backend specific test
+#if SK_SUPPORT_GPU
+#include "GrMemoryPool.h"
+#include "SkRandom.h"
+#include "SkTDArray.h"
+#include "SkTScopedPtr.h"
+#include "SkInstCnt.h"
+
+namespace {
+// A is the top of an inheritance tree of classes that overload op new and
+// and delete to use a GrMemoryPool. The objects have values of different types
+// that can be set and checked.
+class A {
+public:
+    A() {};
+    virtual void setValues(int v) {
+        fChar = static_cast<char>(v);
+    }
+    virtual bool checkValues(int v) {
+        return fChar == static_cast<char>(v);
+    }
+    virtual ~A() {};
+
+    void* operator new(size_t size) {
+        if (!gPool.get()) {
+            return ::operator new(size);
+        } else {
+            return gPool->allocate(size);
+        }
+    }
+
+    void operator delete(void* p) {
+        if (!gPool.get()) {
+            ::operator delete(p);
+        } else {
+            return gPool->release(p);
+        }
+    }
+
+    SK_DECLARE_INST_COUNT_ROOT(A);
+
+    static A* Create(SkRandom* r);
+
+    static void SetAllocator(size_t preallocSize, size_t minAllocSize) {
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(0 == GetInstanceCount());
+#endif
+        GrMemoryPool* pool = new GrMemoryPool(preallocSize, minAllocSize);
+        gPool.reset(pool);
+    }
+
+    static void ResetAllocator() {
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(0 == GetInstanceCount());
+#endif
+        gPool.reset(NULL);
+    }
+
+private:
+    static SkTScopedPtr<GrMemoryPool> gPool;
+    char fChar;
+};
+SK_DEFINE_INST_COUNT(A);
+SkTScopedPtr<GrMemoryPool> A::gPool;
+
+class B : public A {
+public:
+    B() {};
+    virtual void setValues(int v) {
+        fDouble = static_cast<double>(v);
+        this->INHERITED::setValues(v);
+    }
+    virtual bool checkValues(int v) {
+        return fDouble == static_cast<double>(v) &&
+               this->INHERITED::checkValues(v);
+    }
+    virtual ~B() {};
+
+private:
+    double fDouble;
+
+    typedef A INHERITED;
+};
+
+class C : public A {
+public:
+    C() {};
+    virtual void setValues(int v) {
+        fInt64 = static_cast<int64_t>(v);
+        this->INHERITED::setValues(v);
+    }
+    virtual bool checkValues(int v) {
+        return fInt64 == static_cast<int64_t>(v) &&
+               this->INHERITED::checkValues(v);
+    }
+    virtual ~C() {};
+
+private:
+    int64_t fInt64;
+
+    typedef A INHERITED;
+};
+
+// D derives from C and owns a dynamically created B
+class D : public C {
+public:
+    D() {
+        fB = new B();
+    }
+    virtual void setValues(int v) {
+        fVoidStar = reinterpret_cast<void*>(v);
+        this->INHERITED::setValues(v);
+        fB->setValues(v);
+    }
+    virtual bool checkValues(int v) {
+        return fVoidStar == reinterpret_cast<void*>(v) &&
+               fB->checkValues(v) &&
+               this->INHERITED::checkValues(v);
+    }
+    virtual ~D() {
+        delete fB;
+    }
+private:
+    void*   fVoidStar;
+    B*      fB;
+
+    typedef C INHERITED;
+};
+
+class E : public A {
+public:
+    E() {}
+    virtual void setValues(int v) {
+        for (size_t i = 0; i < SK_ARRAY_COUNT(fIntArray); ++i) {
+            fIntArray[i] = v;
+        }
+        this->INHERITED::setValues(v);
+    }
+    virtual bool checkValues(int v) {
+        bool ok = true;
+        for (size_t i = 0; ok && i < SK_ARRAY_COUNT(fIntArray); ++i) {
+            if (fIntArray[i] != v) {
+                ok = false;
+            }
+        }
+        return ok && this->INHERITED::checkValues(v);
+    }
+    virtual ~E() {}
+private:
+    int   fIntArray[20];
+
+    typedef A INHERITED;
+};
+
+A* A::Create(SkRandom* r) {
+    switch (r->nextRangeU(0, 4)) {
+        case 0:
+            return new A;
+        case 1:
+            return new B;
+        case 2:
+            return new C;
+        case 3:
+            return new D;
+        case 4:
+            return new E;
+        default:
+            // suppress warning
+            return NULL;
+    }
+}
+}
+struct Rec {
+    A* fInstance;
+    int fValue;
+};
+
+static void test_memory_pool(skiatest::Reporter* reporter) {
+    // prealloc and min alloc sizes for the pool
+    static const size_t gSizes[][2] = {
+        {0, 0},
+        {10 * sizeof(A), 20 * sizeof(A)},
+        {100 * sizeof(A), 100 * sizeof(A)},
+        {500 * sizeof(A), 500 * sizeof(A)},
+        {10000 * sizeof(A), 0},
+        {1, 100 * sizeof(A)},
+    };
+    // different percentages of creation vs deletion
+    static const float gCreateFraction[] = {1.f, .95f, 0.75f, .5f};
+    // number of create/destroys per test
+    static const int kNumIters = 20000;
+    // check that all the values stored in A objects are correct after this
+    // number of iterations
+    static const int kCheckPeriod = 500;
+
+    SkRandom r;
+    for (size_t s = 0; s < SK_ARRAY_COUNT(gSizes); ++s) {
+        A::SetAllocator(gSizes[s][0], gSizes[s][1]);
+        for (size_t c = 0; c < SK_ARRAY_COUNT(gCreateFraction); ++c) {
+            SkTDArray<Rec> instanceRecs;
+            for (int i = 0; i < kNumIters; ++i) {
+                float createOrDestroy = r.nextUScalar1();
+                if (createOrDestroy < gCreateFraction[c] ||
+                    0 == instanceRecs.count()) {
+                    Rec* rec = instanceRecs.append();
+                    rec->fInstance = A::Create(&r);
+                    rec->fValue = static_cast<int>(r.nextU());
+                    rec->fInstance->setValues(rec->fValue);
+                } else {
+                    int d = r.nextRangeU(0, instanceRecs.count() - 1);
+                    Rec& rec = instanceRecs[d];
+                    REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
+                    delete rec.fInstance;
+                    instanceRecs.removeShuffle(d);
+                }
+                if (0 == i % kCheckPeriod) {
+                    for (int r = 0; r < instanceRecs.count(); ++r) {
+                        Rec& rec = instanceRecs[r];
+                        REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
+                    }
+                }
+            }
+            for (int i = 0; i < instanceRecs.count(); ++i) {
+                Rec& rec = instanceRecs[i];
+                REPORTER_ASSERT(reporter, rec.fInstance->checkValues(rec.fValue));
+                delete rec.fInstance;
+            }
+#if SK_ENABLE_INST_COUNT
+            REPORTER_ASSERT(reporter, !A::GetInstanceCount());
+#endif
+        }
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("GrMemoryPool", GrMemoryPoolClass, test_memory_pool)
+
+#endif
diff --git a/tests/GradientTest.cpp b/tests/GradientTest.cpp
new file mode 100644
index 0000000..e60c9ed
--- /dev/null
+++ b/tests/GradientTest.cpp
@@ -0,0 +1,199 @@
+
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkDevice.h"
+#include "SkTemplates.h"
+#include "SkShader.h"
+#include "SkColorShader.h"
+#include "SkEmptyShader.h"
+#include "SkGradientShader.h"
+
+struct GradRec {
+    int             fColorCount;
+    const SkColor*  fColors;
+    const SkScalar* fPos;
+    const SkPoint*  fPoint;   // 2
+    const SkScalar* fRadius; // 2
+    SkShader::TileMode fTileMode;
+
+    void gradCheck(skiatest::Reporter* reporter, SkShader* shader,
+                   SkShader::GradientInfo* info,
+                   SkShader::GradientType gt) const {
+        SkAutoTMalloc<SkColor> colorStorage(fColorCount);
+        SkAutoTMalloc<SkScalar> posStorage(fColorCount);
+
+        info->fColorCount = fColorCount;
+        info->fColors = colorStorage;
+        info->fColorOffsets = posStorage.get();
+        REPORTER_ASSERT(reporter, shader->asAGradient(info) == gt);
+
+        REPORTER_ASSERT(reporter, info->fColorCount == fColorCount);
+        REPORTER_ASSERT(reporter,
+                        !memcmp(info->fColors, fColors, fColorCount * sizeof(SkColor)));
+        REPORTER_ASSERT(reporter,
+                        !memcmp(info->fColorOffsets, fPos, fColorCount * sizeof(SkScalar)));
+        REPORTER_ASSERT(reporter, fTileMode == info->fTileMode);
+    }
+};
+
+
+static void none_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
+    SkAutoTUnref<SkShader> s(new SkEmptyShader);
+    REPORTER_ASSERT(reporter, SkShader::kNone_GradientType == s->asAGradient(NULL));
+}
+
+static void color_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
+    SkAutoTUnref<SkShader> s(new SkColorShader(rec.fColors[0]));
+    REPORTER_ASSERT(reporter, SkShader::kColor_GradientType == s->asAGradient(NULL));
+
+    SkShader::GradientInfo info;
+    info.fColorCount = 0;
+    s->asAGradient(&info);
+    REPORTER_ASSERT(reporter, 1 == info.fColorCount);
+}
+
+static void linear_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
+    SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(rec.fPoint,
+                                                            rec.fColors,
+                                                            rec.fPos,
+                                                            rec.fColorCount,
+                                                            rec.fTileMode));
+
+    SkShader::GradientInfo info;
+    rec.gradCheck(reporter, s, &info, SkShader::kLinear_GradientType);
+    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint)));
+}
+
+static void radial_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
+    SkAutoTUnref<SkShader> s(SkGradientShader::CreateRadial(rec.fPoint[0],
+                                                            rec.fRadius[0],
+                                                            rec.fColors,
+                                                            rec.fPos,
+                                                            rec.fColorCount,
+                                                            rec.fTileMode));
+
+    SkShader::GradientInfo info;
+    rec.gradCheck(reporter, s, &info, SkShader::kRadial_GradientType);
+    REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]);
+    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,
+                                                           rec.fColors,
+                                                           rec.fPos,
+                                                           rec.fColorCount));
+
+    SkShader::GradientInfo info;
+    rec.gradCheck(reporter, s, &info, SkShader::kSweep_GradientType);
+    REPORTER_ASSERT(reporter, info.fPoint[0] == rec.fPoint[0]);
+}
+
+static void conical_gradproc(skiatest::Reporter* reporter, const GradRec& rec) {
+    SkAutoTUnref<SkShader> s(SkGradientShader::CreateTwoPointConical(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::kConical_GradientType);
+    REPORTER_ASSERT(reporter, !memcmp(info.fPoint, rec.fPoint, 2 * sizeof(SkPoint)));
+    REPORTER_ASSERT(reporter, !memcmp(info.fRadius, rec.fRadius, 2 * sizeof(SkScalar)));
+}
+
+// Ensure that repeated color gradients behave like drawing a single color
+static void TestConstantGradient(skiatest::Reporter* reporter) {
+    const SkPoint pts[] = {
+        { 0, 0 },
+        { SkIntToScalar(10), 0 }
+    };
+    SkColor colors[] = { SK_ColorBLUE, SK_ColorBLUE };
+    const SkScalar pos[] = { 0, SK_Scalar1 };
+    SkAutoTUnref<SkShader> s(SkGradientShader::CreateLinear(pts,
+                                                            colors,
+                                                            pos,
+                                                            2,
+                                                            SkShader::kClamp_TileMode));
+    SkBitmap outBitmap;
+    outBitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 1);
+    outBitmap.allocPixels();
+    SkPaint paint;
+    paint.setShader(s.get());
+    SkDevice device(outBitmap);
+    SkCanvas canvas(&device);
+    canvas.drawPaint(paint);
+    SkAutoLockPixels alp(outBitmap);
+    for (int i = 0; i < 10; i++) {
+        // The following is commented out because it currently fails
+        // Related bug: https://code.google.com/p/skia/issues/detail?id=1098
+
+        // REPORTER_ASSERT(reporter, SK_ColorBLUE == outBitmap.getColor(i, 0));
+    }
+}
+
+typedef void (*GradProc)(skiatest::Reporter* reporter, const GradRec&);
+
+static void TestGradientShaders(skiatest::Reporter* reporter) {
+    static const SkColor gColors[] = { SK_ColorRED, SK_ColorGREEN, SK_ColorBLUE };
+    static const SkScalar gPos[] = { 0, SK_ScalarHalf, SK_Scalar1 };
+    static const SkPoint gPts[] = {
+        { 0, 0 },
+        { SkIntToScalar(10), SkIntToScalar(20) }
+    };
+    static const SkScalar gRad[] = { SkIntToScalar(1), SkIntToScalar(2) };
+
+    GradRec rec;
+    rec.fColorCount = SK_ARRAY_COUNT(gColors);
+    rec.fColors = gColors;
+    rec.fPos = gPos;
+    rec.fPoint = gPts;
+    rec.fRadius = gRad;
+    rec.fTileMode = SkShader::kClamp_TileMode;
+
+    static const GradProc gProcs[] = {
+        none_gradproc,
+        color_gradproc,
+        linear_gradproc,
+        radial_gradproc,
+        radial2_gradproc,
+        sweep_gradproc,
+        conical_gradproc,
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gProcs); ++i) {
+        gProcs[i](reporter, rec);
+    }
+}
+
+static void TestGradients(skiatest::Reporter* reporter) {
+    TestGradientShaders(reporter);
+    TestConstantGradient(reporter);
+}
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Gradients", TestGradientsClass, TestGradients)
diff --git a/tests/HashCacheTest.cpp b/tests/HashCacheTest.cpp
new file mode 100644
index 0000000..f26dd8a
--- /dev/null
+++ b/tests/HashCacheTest.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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 "Test.h"
+
+// This is a GR test
+#if SK_SUPPORT_GPU
+#include "GrTHashCache.h"
+
+struct HashElement {
+    int     fKey;
+    int     fValue;
+};
+
+class GrFindPositivesFunctor {
+public:
+    // only return elements with positive values
+    bool operator()(const HashElement* elem) const {
+        return elem->fValue > 0;
+    }
+};
+
+class GrFindNegativesFunctor {
+public:
+    // only return elements with negative values
+    bool operator()(const HashElement* elem) const {
+        return elem->fValue < 0;
+    }
+};
+
+class HashKey {
+public:
+    HashKey(int key) : fKey(key) {}
+
+    uint32_t getHash() const { return fKey; }
+
+    static bool LT(const HashElement& entry, const HashKey& key) {
+        return entry.fKey < key.fKey;
+    }
+    static bool EQ(const HashElement& entry, const HashKey& key) {
+        return entry.fKey == key.fKey;
+    }
+
+#if GR_DEBUG
+    static uint32_t GetHash(const HashElement& entry) {
+        return entry.fKey;
+    }
+    static bool LT(const HashElement& a, const HashElement& b) {
+        return a.fKey < b.fKey;
+    }
+    static bool EQ(const HashElement& a, const HashElement& b) {
+        return a.fKey == b.fKey;
+    }
+#endif
+
+protected:
+    int fKey;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+static void TestHashCache(skiatest::Reporter* reporter, GrContext* context) {
+
+    GrTHashTable<HashElement, HashKey, 4> cache;
+
+    HashElement negHashElements[10] = {
+        { 0,  0 },
+        { 1, -1 },
+        { 2, -2 },
+        { 3, -3 },
+        { 4, -4 },
+        { 5, -5 },
+        { 6, -6 },
+        { 7, -7 },
+        { 8, -8 },
+        { 9, -9 }
+    };
+    HashElement posHashElements[10] = {
+        { 0, 0 },
+        { 1, 1 },
+        { 2, 2 },
+        { 3, 3 },
+        { 4, 4 },
+        { 5, 5 },
+        { 6, 6 },
+        { 7, 7 },
+        { 8, 8 },
+        { 9, 9 }
+    };
+
+    // add i: -i pairs
+    for (int i = 0; i < 10; ++i) {
+        cache.insert(HashKey(i), &negHashElements[i]);
+    }
+
+    REPORTER_ASSERT(reporter, 10 == cache.count());
+
+    // look for all i's and assert we found the -i's
+    for (int i = 0; i < 10; ++i) {
+        HashElement* found = cache.find(i);
+        REPORTER_ASSERT(reporter, NULL != found && -i == found->fValue);
+    }
+
+    // look for something not in the cache
+    {
+        HashElement* found = cache.find(10);
+        REPORTER_ASSERT(reporter, NULL == found);
+    }
+
+    // add i:i duplicates (so each i will have a positive & negative entry)
+    for (int i = 0; i < 10; ++i) {
+        cache.insert(i, &posHashElements[i]);
+    }
+
+    REPORTER_ASSERT(reporter, 20 == cache.count());
+
+    // test out the find functor to find all the positive values
+    {
+        GrFindPositivesFunctor findPos;
+
+        HashElement* found = cache.find(0, findPos);
+        REPORTER_ASSERT(reporter, NULL == found);
+
+        for (int i = 1; i < 10; ++i) {
+            found = cache.find(i, findPos);
+
+            REPORTER_ASSERT(reporter, NULL != found && found->fValue > 0);
+        }
+    }
+
+    // make sure finding the positives wasn't a fluke - find the negatives
+    {
+        GrFindNegativesFunctor findNeg;
+
+        HashElement* found = cache.find(0, findNeg);
+        REPORTER_ASSERT(reporter, NULL == found);
+
+        for (int i = 1; i < 10; ++i) {
+            found = cache.find(i, findNeg);
+
+            REPORTER_ASSERT(reporter, NULL != found && found->fValue < 0);
+        }
+    }
+
+    // remove the 0:0 entries
+    {
+        cache.remove(0, &negHashElements[0]);
+        cache.remove(0, &posHashElements[0]);
+        REPORTER_ASSERT(reporter, 18 == cache.count());
+
+        HashElement* found = cache.find(0);
+        REPORTER_ASSERT(reporter, NULL == found);
+    }
+
+    // remove all
+    {
+        cache.removeAll();
+        REPORTER_ASSERT(reporter, 0 == cache.count());
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+#include "TestClassDef.h"
+DEFINE_GPUTESTCLASS("HashCache", HashCacheTestClass, TestHashCache)
+
+#endif
diff --git a/tests/InfRectTest.cpp b/tests/InfRectTest.cpp
index 12356d9..4d957dc 100644
--- a/tests/InfRectTest.cpp
+++ b/tests/InfRectTest.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
+#include "SkRandom.h"
 #include "SkRect.h"
 
 #ifdef SK_SCALAR_IS_FLOAT
@@ -14,6 +15,39 @@
 }
 #endif
 
+struct RectCenter {
+    SkIRect  fRect;
+    SkIPoint fCenter;
+};
+
+static void test_center(skiatest::Reporter* reporter) {
+    static const RectCenter gData[] = {
+        { { 0, 0, 0, 0 }, { 0, 0 } },
+        { { 0, 0, 1, 1 }, { 0, 0 } },
+        { { -1, -1, 0, 0 }, { -1, -1 } },
+        { { 0, 0, 10, 7 }, { 5, 3 } },
+        { { 0, 0, 11, 6 }, { 5, 3 } },
+    };
+    for (size_t index = 0; index < SK_ARRAY_COUNT(gData); ++index) {
+        REPORTER_ASSERT(reporter,
+                        gData[index].fRect.centerX() == gData[index].fCenter.x());
+        REPORTER_ASSERT(reporter,
+                        gData[index].fRect.centerY() == gData[index].fCenter.y());
+    }
+
+    SkRandom rand;
+    for (int i = 0; i < 10000; ++i) {
+        SkIRect r;
+
+        r.set(rand.nextS() >> 2, rand.nextS() >> 2,
+              rand.nextS() >> 2, rand.nextS() >> 2);
+        int cx = r.centerX();
+        int cy = r.centerY();
+        REPORTER_ASSERT(reporter, ((r.left() + r.right()) >> 1) == cx);
+        REPORTER_ASSERT(reporter, ((r.top() + r.bottom()) >> 1) == cy);
+    }
+}
+
 static void check_invalid(skiatest::Reporter* reporter,
                           SkScalar l, SkScalar t, SkScalar r, SkScalar b) {
     SkRect rect;
@@ -25,24 +59,30 @@
 // as one of its coordinates.
 static void TestInfRect(skiatest::Reporter* reporter) {
 #ifdef SK_SCALAR_IS_FLOAT
-    float invalid = 1 / make_zero();    // infinity
+    float inf = 1 / make_zero();    // infinity
+    float nan = inf * 0;
+    SkASSERT(!(nan == nan));
 #else
-    SkFixed invalid = SK_FixedNaN;
+    SkFixed inf = SK_FixedNaN;
+    SkFixed nan = SK_FixedNaN;
 #endif
     SkScalar small = SkIntToScalar(10);
     SkScalar big = SkIntToScalar(100);
 
+    REPORTER_ASSERT(reporter, SkRect::MakeEmpty().isFinite());
+
     SkRect rect = SkRect::MakeXYWH(small, small, big, big);
     REPORTER_ASSERT(reporter, rect.isFinite());
 
-    check_invalid(reporter, small, small, big, invalid);
-    check_invalid(reporter, small, small, invalid, big);
-    check_invalid(reporter, small, invalid, big, big);
-    check_invalid(reporter, invalid, small, big, big);
-    check_invalid(reporter, small, small, big, -invalid);
-    check_invalid(reporter, small, small, -invalid, big);
-    check_invalid(reporter, small, -invalid, big, big);
-    check_invalid(reporter, -invalid, small, big, big);
+    const SkScalar invalid[] = { nan, inf, -inf };
+    for (size_t i = 0; i < SK_ARRAY_COUNT(invalid); ++i) {
+        check_invalid(reporter, small, small, big, invalid[i]);
+        check_invalid(reporter, small, small, invalid[i], big);
+        check_invalid(reporter, small, invalid[i], big, big);
+        check_invalid(reporter, invalid[i], small, big, big);
+    }
+
+    test_center(reporter);
 }
 
 // need tests for SkStrSearch
diff --git a/tests/LListTest.cpp b/tests/LListTest.cpp
new file mode 100644
index 0000000..ab36ef5
--- /dev/null
+++ b/tests/LListTest.cpp
@@ -0,0 +1,321 @@
+/*
+ * 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 "Test.h"
+#include "SkRandom.h"
+#include "SkTInternalLList.h"
+#include "SkTLList.h"
+
+class ListElement {
+public:
+    ListElement(int id) : fID(id) {
+    }
+    bool operator== (const ListElement& other) { return fID == other.fID; }
+
+#if SK_ENABLE_INST_COUNT
+    // Make the instance count available publicly.
+    static int InstanceCount() { return GetInstanceCount(); }
+#endif
+
+    int fID;
+
+private:
+    SK_DECLARE_INST_COUNT_ROOT(ListElement);
+    SK_DECLARE_INTERNAL_LLIST_INTERFACE(ListElement);
+};
+
+SK_DEFINE_INST_COUNT(ListElement);
+
+static void check_list(const SkTInternalLList<ListElement>& list,
+                       skiatest::Reporter* reporter,
+                       bool empty,
+                       int numElements,
+                       bool in0, bool in1, bool in2, bool in3,
+                       ListElement elements[4]) {
+
+    REPORTER_ASSERT(reporter, empty == list.isEmpty());
+#if SK_DEBUG
+    list.validate();
+    REPORTER_ASSERT(reporter, numElements == list.countEntries());
+    REPORTER_ASSERT(reporter, in0 == list.isInList(&elements[0]));
+    REPORTER_ASSERT(reporter, in1 == list.isInList(&elements[1]));
+    REPORTER_ASSERT(reporter, in2 == list.isInList(&elements[2]));
+    REPORTER_ASSERT(reporter, in3 == list.isInList(&elements[3]));
+#endif
+}
+
+static void TestTInternalLList(skiatest::Reporter* reporter) {
+    SkTInternalLList<ListElement> list;
+    ListElement elements[4] = {
+        ListElement(0),
+        ListElement(1),
+        ListElement(2),
+        ListElement(3),
+    };
+
+    // list should be empty to start with
+    check_list(list, reporter, true, 0, false, false, false, false, elements);
+
+    list.addToHead(&elements[0]);
+
+    check_list(list, reporter, false, 1, true, false, false, false, elements);
+
+    list.addToHead(&elements[1]);
+    list.addToHead(&elements[2]);
+    list.addToHead(&elements[3]);
+
+    check_list(list, reporter, false, 4, true, true, true, true, elements);
+
+    // test out iterators
+    typedef SkTInternalLList<ListElement>::Iter Iter;
+    Iter iter;
+
+    ListElement* cur = iter.init(list, Iter::kHead_IterStart);
+    for (int i = 0; NULL != cur; ++i, cur = iter.next()) {
+        REPORTER_ASSERT(reporter, cur->fID == 3-i);
+    }
+
+    cur = iter.init(list, Iter::kTail_IterStart);
+    for (int i = 0; NULL != cur; ++i, cur = iter.prev()) {
+        REPORTER_ASSERT(reporter, cur->fID == i);
+    }
+
+    // remove middle, frontmost then backmost
+    list.remove(&elements[1]);
+    list.remove(&elements[3]);
+    list.remove(&elements[0]);
+
+    check_list(list, reporter, false, 1, false, false, true, false, elements);
+
+    // remove last element
+    list.remove(&elements[2]);
+
+    // list should be empty again
+    check_list(list, reporter, true, 0, false, false, false, false, elements);
+
+    // test out methods that add to the middle of the list.
+    list.addAfter(&elements[1], NULL);
+    check_list(list, reporter, false, 1, false, true, false, false, elements);
+
+    list.remove(&elements[1]);
+
+    list.addBefore(&elements[1], NULL);
+    check_list(list, reporter, false, 1, false, true, false, false, elements);
+
+    list.addBefore(&elements[0], &elements[1]);
+    check_list(list, reporter, false, 2, true, true, false, false, elements);
+
+    list.addAfter(&elements[3], &elements[1]);
+    check_list(list, reporter, false, 3, true, true, false, true, elements);
+
+    list.addBefore(&elements[2], &elements[3]);
+    check_list(list, reporter, false, 4, true, true, true, true, elements);
+
+    cur = iter.init(list, Iter::kHead_IterStart);
+    for (int i = 0; NULL != cur; ++i, cur = iter.next()) {
+        REPORTER_ASSERT(reporter, cur->fID == i);
+    }
+}
+
+static void TestTLList(skiatest::Reporter* reporter) {
+    typedef SkTLList<ListElement> ElList;
+    typedef ElList::Iter Iter;
+    SkRandom random;
+
+    for (int i = 1; i <= 16; i *= 2) {
+
+        ElList list1(i);
+        ElList list2(i);
+        Iter iter1;
+        Iter iter2;
+        Iter iter3;
+        Iter iter4;
+
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(0 == ListElement::InstanceCount());
+#endif
+
+        REPORTER_ASSERT(reporter, list1.isEmpty());
+        REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kHead_IterStart));
+        REPORTER_ASSERT(reporter, NULL == iter1.init(list1, Iter::kTail_IterStart));
+        // Try popping an empty list
+        list1.popHead();
+        list1.popTail();
+        REPORTER_ASSERT(reporter, list1.isEmpty());
+        REPORTER_ASSERT(reporter, list1 == list2);
+
+        // Create two identical lists, one by appending to head and the other to the tail.
+        list1.addToHead(ListElement(1));
+        list2.addToTail(ListElement(1));
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(2 == ListElement::InstanceCount());
+#endif
+        iter1.init(list1, Iter::kHead_IterStart);
+        iter2.init(list1, Iter::kTail_IterStart);
+        REPORTER_ASSERT(reporter, iter1.get()->fID == iter2.get()->fID);
+        iter3.init(list2, Iter::kHead_IterStart);
+        iter4.init(list2, Iter::kTail_IterStart);
+        REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
+        REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
+        REPORTER_ASSERT(reporter, list1 == list2);
+
+        list2.reset();
+
+        // use both before/after in-place construction on an empty list
+        SkNEW_INSERT_IN_LLIST_BEFORE(&list2, list2.headIter(), ListElement, (1));
+        REPORTER_ASSERT(reporter, list2 == list1);
+        list2.reset();
+
+        SkNEW_INSERT_IN_LLIST_AFTER(&list2, list2.tailIter(), ListElement, (1));
+        REPORTER_ASSERT(reporter, list2 == list1);
+
+        // add an element to the second list, check that iters are still valid
+        iter3.init(list2, Iter::kHead_IterStart);
+        iter4.init(list2, Iter::kTail_IterStart);
+        list2.addToHead(ListElement(2));
+
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(3 == ListElement::InstanceCount());
+#endif
+
+        REPORTER_ASSERT(reporter, iter3.get()->fID == iter1.get()->fID);
+        REPORTER_ASSERT(reporter, iter4.get()->fID == iter1.get()->fID);
+        REPORTER_ASSERT(reporter, 1 == Iter(list2, Iter::kTail_IterStart).get()->fID);
+        REPORTER_ASSERT(reporter, 2 == Iter(list2, Iter::kHead_IterStart).get()->fID);
+        REPORTER_ASSERT(reporter, list1 != list2);
+        list1.addToHead(ListElement(2));
+        REPORTER_ASSERT(reporter, list1 == list2);
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(4 == ListElement::InstanceCount());
+#endif
+        REPORTER_ASSERT(reporter, !list1.isEmpty());
+
+        list1.reset();
+        list2.reset();
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(0 == ListElement::InstanceCount());
+#endif
+        REPORTER_ASSERT(reporter, list1.isEmpty() && list2.isEmpty());
+
+        // randomly perform insertions and deletions on a list and perform tests
+        int count = 0;
+        for (int j = 0; j < 100; ++j) {
+            if (list1.isEmpty() || random.nextBiasedBool(3  * SK_Scalar1 / 4)) {
+                int id = j;
+                // Choose one of three ways to insert a new element: at the head, at the tail,
+                // before a random element, after a random element
+                int numValidMethods = 0 == count ? 2 : 4;
+                int insertionMethod = random.nextULessThan(numValidMethods);
+                switch (insertionMethod) {
+                    case 0:
+                        list1.addToHead(ListElement(id));
+                        break;
+                    case 1:
+                        list1.addToTail(ListElement(id));
+                        break;
+                    case 2: // fallthru to share code that picks random element.
+                    case 3: {
+                        int n = random.nextULessThan(list1.count());
+                        Iter iter = list1.headIter();
+                        // remember the elements before/after the insertion point.
+                        while (n--) {
+                            iter.next();
+                        }
+                        Iter prev(iter);
+                        Iter next(iter);
+                        next.next();
+                        prev.prev();
+
+                        SkASSERT(NULL != iter.get());
+                        // insert either before or after the iterator, then check that the
+                        // surrounding sequence is correct.
+                        if (2 == insertionMethod) {
+                            SkNEW_INSERT_IN_LLIST_BEFORE(&list1, iter, ListElement, (id));
+                            Iter newItem(iter);
+                            newItem.prev();
+                            REPORTER_ASSERT(reporter, newItem.get()->fID == id);
+
+                            if (NULL != next.get()) {
+                                REPORTER_ASSERT(reporter, next.prev()->fID == iter.get()->fID);
+                            }
+                            if (NULL != prev.get()) {
+                                REPORTER_ASSERT(reporter, prev.next()->fID == id);
+                            }
+                        } else {
+                            SkNEW_INSERT_IN_LLIST_AFTER(&list1, iter, ListElement, (id));
+                            Iter newItem(iter);
+                            newItem.next();
+                            REPORTER_ASSERT(reporter, newItem.get()->fID == id);
+
+                            if (NULL != next.get()) {
+                                REPORTER_ASSERT(reporter, next.prev()->fID == id);
+                            }
+                            if (NULL != prev.get()) {
+                                REPORTER_ASSERT(reporter, prev.next()->fID == iter.get()->fID);
+                            }
+                        }
+                    }
+                }
+                ++count;
+            } else {
+                // walk to a random place either forward or backwards and remove.
+                int n = random.nextULessThan(list1.count());
+                Iter::IterStart start;
+                ListElement* (Iter::*incrFunc)();
+
+                if (random.nextBool()) {
+                    start = Iter::kHead_IterStart;
+                    incrFunc = &Iter::next;
+                } else {
+                    start = Iter::kTail_IterStart;
+                    incrFunc = &Iter::prev;
+                }
+
+                // find the element
+                Iter iter(list1, start);
+                while (n--) {
+                    REPORTER_ASSERT(reporter, NULL != iter.get());
+                    (iter.*incrFunc)();
+                }
+                REPORTER_ASSERT(reporter, NULL != iter.get());
+
+                // remember the prev and next elements from the element to be removed
+                Iter prev = iter;
+                Iter next = iter;
+                prev.prev();
+                next.next();
+                list1.remove(iter.get());
+
+                // make sure the remembered next/prev iters still work
+                Iter pn = prev; pn.next();
+                Iter np = next; np.prev();
+                // pn should match next unless the target node was the head, in which case prev
+                // walked off the list.
+                REPORTER_ASSERT(reporter, pn.get() == next.get() || NULL == prev.get());
+                // Similarly, np should match prev unless next originally walked off the tail.
+                REPORTER_ASSERT(reporter, np.get() == prev.get() || NULL == next.get());
+                --count;
+            }
+            REPORTER_ASSERT(reporter, count == list1.count());
+#if SK_ENABLE_INST_COUNT
+            SkASSERT(count == ListElement::InstanceCount());
+#endif
+        }
+        list1.reset();
+#if SK_ENABLE_INST_COUNT
+        SkASSERT(0 == ListElement::InstanceCount());
+#endif
+    }
+}
+
+static void test_llists(skiatest::Reporter* reporter) {
+    TestTInternalLList(reporter);
+    TestTLList(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("LList", TestLListClass, test_llists)
diff --git a/tests/MD5Test.cpp b/tests/MD5Test.cpp
new file mode 100644
index 0000000..9b9e756
--- /dev/null
+++ b/tests/MD5Test.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkMD5.h"
+
+static bool digests_equal(const SkMD5::Digest& expectedDigest, const SkMD5::Digest& computedDigest) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(expectedDigest.data); ++i) {
+        if (expectedDigest.data[i] != computedDigest.data[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static void md5_test(const char* string, const SkMD5::Digest& expectedDigest, skiatest::Reporter* reporter) {
+    size_t len = strlen(string);
+
+    // All at once
+    {
+        SkMD5 context;
+        context.update(reinterpret_cast<const uint8_t*>(string), len);
+        SkMD5::Digest digest;
+        context.finish(digest);
+
+        REPORTER_ASSERT(reporter, digests_equal(expectedDigest, digest));
+    }
+
+    // One byte at a time.
+    {
+        SkMD5 context;
+        const uint8_t* data = reinterpret_cast<const uint8_t*>(string);
+        const uint8_t* end = reinterpret_cast<const uint8_t*>(string + len);
+        for (; data < end; ++data) {
+            context.update(data, 1);
+        }
+        SkMD5::Digest digest;
+        context.finish(digest);
+
+        REPORTER_ASSERT(reporter, digests_equal(expectedDigest, digest));
+    }
+}
+
+static struct MD5Test {
+    const char* message;
+    SkMD5::Digest digest;
+} md5_tests[] = {
+    // Reference tests from RFC1321 Section A.5 ( http://www.ietf.org/rfc/rfc1321.txt )
+    { "", {{ 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e }} },
+    { "a", {{ 0x0c, 0xc1, 0x75, 0xb9, 0xc0, 0xf1, 0xb6, 0xa8, 0x31, 0xc3, 0x99, 0xe2, 0x69, 0x77, 0x26, 0x61 }} },
+    { "abc", {{ 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 }} },
+    { "message digest", {{ 0xf9, 0x6b, 0x69, 0x7d, 0x7c, 0xb7, 0x93, 0x8d, 0x52, 0x5a, 0x2f, 0x31, 0xaa, 0xf1, 0x61, 0xd0 }} },
+    { "abcdefghijklmnopqrstuvwxyz", {{ 0xc3, 0xfc, 0xd3, 0xd7, 0x61, 0x92, 0xe4, 0x00, 0x7d, 0xfb, 0x49, 0x6c, 0xca, 0x67, 0xe1, 0x3b }} },
+    { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", {{ 0xd1, 0x74, 0xab, 0x98, 0xd2, 0x77, 0xd9, 0xf5, 0xa5, 0x61, 0x1c, 0x2c, 0x9f, 0x41, 0x9d, 0x9f }} },
+    { "12345678901234567890123456789012345678901234567890123456789012345678901234567890", {{ 0x57, 0xed, 0xf4, 0xa2, 0x2b, 0xe3, 0xc9, 0x55, 0xac, 0x49, 0xda, 0x2e, 0x21, 0x07, 0xb6, 0x7a }} },
+};
+
+static void TestMD5(skiatest::Reporter* reporter) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(md5_tests); ++i) {
+        md5_test(md5_tests[i].message, md5_tests[i].digest, reporter);
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("MD5", MD5TestClass, TestMD5)
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
index fef93cd..7fc53a9 100644
--- a/tests/MathTest.cpp
+++ b/tests/MathTest.cpp
@@ -6,12 +6,48 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
+#include "SkFloatBits.h"
 #include "SkFloatingPoint.h"
-#include "SkMath.h"
+#include "SkMathPriv.h"
 #include "SkPoint.h"
 #include "SkRandom.h"
 #include "SkColorPriv.h"
 
+static float sk_fsel(float pred, float result_ge, float result_lt) {
+    return pred >= 0 ? result_ge : result_lt;
+}
+
+static float fast_floor(float x) {
+//    float big = sk_fsel(x, 0x1.0p+23, -0x1.0p+23);
+    float big = sk_fsel(x, (float)(1 << 23), -(float)(1 << 23));
+    return (float)(x + big) - big;
+}
+
+static float std_floor(float x) {
+    return sk_float_floor(x);
+}
+
+static void test_floor_value(skiatest::Reporter* reporter, float value) {
+    float fast = fast_floor(value);
+    float std = std_floor(value);
+    REPORTER_ASSERT(reporter, std == fast);
+//    SkDebugf("value[%1.9f] std[%g] fast[%g] equal[%d]\n",
+//             value, std, fast, std == fast);
+}
+
+static void test_floor(skiatest::Reporter* reporter) {
+    static const float gVals[] = {
+        0, 1, 1.1f, 1.01f, 1.001f, 1.0001f, 1.00001f, 1.000001f, 1.0000001f
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gVals); ++i) {
+        test_floor_value(reporter, gVals[i]);
+//        test_floor_value(reporter, -gVals[i]);
+    }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 static float float_blend(int src, int dst, float unit) {
     return dst + (src - dst) * unit;
 }
@@ -38,9 +74,20 @@
     return dst + ((src - dst) * a31 >> 5);
 }
 
+// suppress unused code warning
+static int (*blend_functions[])(int, int, int) = {
+    blend31,
+    blend31_slow,
+    blend31_round,
+    blend31_old
+};
+
 static void test_blend31() {
     int failed = 0;
     int death = 0;
+    if (false) { // avoid bit rot, suppress warning
+        failed = (*blend_functions[0])(0,0,0);
+    }
     for (int src = 0; src <= 255; src++) {
         for (int dst = 0; dst <= 255; dst++) {
             for (int a = 0; a <= 31; a++) {
@@ -117,7 +164,6 @@
 
 static void check_length(skiatest::Reporter* reporter,
                          const SkPoint& p, SkScalar targetLen) {
-#ifdef SK_CAN_USE_FLOAT
     float x = SkScalarToFloat(p.fX);
     float y = SkScalarToFloat(p.fY);
     float len = sk_float_sqrt(x*x + y*y);
@@ -125,11 +171,8 @@
     len /= SkScalarToFloat(targetLen);
 
     REPORTER_ASSERT(reporter, len > 0.999f && len < 1.001f);
-#endif
 }
 
-#if defined(SK_CAN_USE_FLOAT)
-
 static float nextFloat(SkRandom& rand) {
     SkFloatIntUnion data;
     data.fSignBitInt = rand.nextU();
@@ -141,14 +184,14 @@
  */
 static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) {
     if (!(x == x)) {    // NAN
-        return si == SK_MaxS32 || si == SK_MinS32;
+        return ((int32_t)si) == SK_MaxS32 || ((int32_t)si) == SK_MinS32;
     }
     // for out of range, C is undefined, but skia always should return NaN32
     if (x > SK_MaxS32) {
-        return si == SK_MaxS32;
+        return ((int32_t)si) == SK_MaxS32;
     }
     if (x < -SK_MaxS32) {
-        return si == SK_MinS32;
+        return ((int32_t)si) == SK_MinS32;
     }
     return si == ni;
 }
@@ -157,7 +200,8 @@
                                float x, uint32_t ni, uint32_t si) {
     if (!equal_float_native_skia(x, ni, si)) {
         SkString desc;
-        desc.printf("%s float %g bits %x native %x skia %x\n", op, x, ni, si);
+        uint32_t xi = SkFloat2Bits(x);
+        desc.printf("%s float %g bits %x native %x skia %x\n", op, x, xi, ni, si);
         reporter->reportFailed(desc);
     }
 }
@@ -245,8 +289,8 @@
 static void unittest_isfinite(skiatest::Reporter* reporter) {
 #ifdef SK_SCALAR_IS_FLOAT
     float nan = sk_float_asin(2);
-    float inf = 1.0 / make_zero();
-    float big = 3.40282e+038;
+    float inf = 1.0f / make_zero();
+    float big = 3.40282e+038f;
 
     REPORTER_ASSERT(reporter, !SkScalarIsNaN(inf));
     REPORTER_ASSERT(reporter, !SkScalarIsNaN(-inf));
@@ -261,17 +305,14 @@
     REPORTER_ASSERT(reporter, !SkScalarIsNaN(big));
     REPORTER_ASSERT(reporter, !SkScalarIsNaN(-big));
     REPORTER_ASSERT(reporter, !SkScalarIsNaN(0));
-    
+
     REPORTER_ASSERT(reporter, !SkScalarIsFinite(nan));
     REPORTER_ASSERT(reporter,  SkScalarIsFinite(big));
     REPORTER_ASSERT(reporter,  SkScalarIsFinite(-big));
     REPORTER_ASSERT(reporter,  SkScalarIsFinite(0));
 }
 
-#endif
-
 static void test_muldiv255(skiatest::Reporter* reporter) {
-#ifdef SK_CAN_USE_FLOAT
     for (int a = 0; a <= 255; a++) {
         for (int b = 0; b <= 255; b++) {
             int ab = a * b;
@@ -290,7 +331,6 @@
             REPORTER_ASSERT(reporter, iround <= b);
         }
     }
-#endif
 }
 
 static void test_muldiv255ceiling(skiatest::Reporter* reporter) {
@@ -322,12 +362,10 @@
     for (size_t i = 0; i < SK_ARRAY_COUNT(gTriples); i += 3) {
         REPORTER_ASSERT(reporter,
                         SkCopySign32(gTriples[i], gTriples[i+1]) == gTriples[i+2]);
-#ifdef SK_CAN_USE_FLOAT
         float x = (float)gTriples[i];
         float y = (float)gTriples[i+1];
         float expected = (float)gTriples[i+2];
         REPORTER_ASSERT(reporter, sk_float_copysign(x, y) == expected);
-#endif
     }
 
     SkRandom rand;
@@ -416,9 +454,15 @@
     for (i = 0; i < 10000; i++) {
         SkPoint p;
 
-        p.setLength(rand.nextS(), rand.nextS(), SK_Scalar1);
+        // These random values are being treated as 32-bit-patterns, not as
+        // ints; calling SkIntToScalar() here produces crashes.
+        p.setLength((SkScalar) rand.nextS(),
+                    (SkScalar) rand.nextS(),
+                    SK_Scalar1);
         check_length(reporter, p, SK_Scalar1);
-        p.setLength(rand.nextS() >> 13, rand.nextS() >> 13, SK_Scalar1);
+        p.setLength((SkScalar) (rand.nextS() >> 13),
+                    (SkScalar) (rand.nextS() >> 13),
+                    SK_Scalar1);
         check_length(reporter, p, SK_Scalar1);
     }
 
@@ -429,10 +473,8 @@
         REPORTER_ASSERT(reporter, result == 1);
     }
 
-#ifdef SK_CAN_USE_FLOAT
     unittest_fastfloat(reporter);
     unittest_isfinite(reporter);
-#endif
 
 #ifdef SkLONGLONG
     for (i = 0; i < 10000; i++) {
@@ -475,7 +517,6 @@
         r2 = SkFixedSquare(numer);
         REPORTER_ASSERT(reporter, result == r2);
 
-#ifdef SK_CAN_USE_FLOAT
         if (numer >= 0 && denom >= 0) {
             SkFixed mean = SkFixedMean(numer, denom);
             float prod = SkFixedToFloat(numer) * SkFixedToFloat(denom);
@@ -495,11 +536,9 @@
             int diff = SkAbs32(mod - SkFloatToFixed(m));
             REPORTER_ASSERT(reporter, (diff >> 7) == 0);
         }
-#endif
     }
 #endif
 
-#ifdef SK_CAN_USE_FLOAT
     for (i = 0; i < 10000; i++) {
         SkFract x = rand.nextU() >> 1;
         double xx = (double)x / SK_Fract1;
@@ -519,9 +558,8 @@
         check = (int32_t)sqrt(xx);
         REPORTER_ASSERT(reporter, xr == check || xr == check-1);
     }
-#endif
 
-#if !defined(SK_SCALAR_IS_FLOAT) && defined(SK_CAN_USE_FLOAT)
+#if !defined(SK_SCALAR_IS_FLOAT)
     {
         SkFixed s, c;
         s = SkFixedSinCos(0, &c);
@@ -553,8 +591,10 @@
     test_blend(reporter);
 #endif
 
+    if (false) test_floor(reporter);
+
     // disable for now
-//    test_blend31();
+    if (false) test_blend31();  // avoid bit rot, suppress warning
 }
 
 #include "TestClassDef.h"
diff --git a/tests/Matrix44Test.cpp b/tests/Matrix44Test.cpp
index 485b38b..269e359 100644
--- a/tests/Matrix44Test.cpp
+++ b/tests/Matrix44Test.cpp
@@ -1,13 +1,21 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "Test.h"
 #include "SkMatrix44.h"
 
+static bool nearly_equal_double(double a, double b) {
+    const double tolerance = 1e-7;
+    double diff = a - b;
+    if (diff < 0)
+        diff = -diff;
+    return diff <= tolerance;
+}
+
 static bool nearly_equal_scalar(SkMScalar a, SkMScalar b) {
     // Note that we get more compounded error for multiple operations when
     // SK_SCALAR_IS_FIXED.
@@ -17,7 +25,7 @@
     const SkScalar tolerance = SK_Scalar1 / 1024;
 #endif
 
-    return SkScalarAbs(a - b) <= tolerance;
+    return SkTAbs<SkMScalar>(a - b) <= tolerance;
 }
 
 template <typename T> void assert16(skiatest::Reporter* reporter, const T data[],
@@ -64,44 +72,316 @@
     return nearly_equal(m, identity);
 }
 
+///////////////////////////////////////////////////////////////////////////////
+static bool bits_isonly(int value, int mask) {
+    return 0 == (value & ~mask);
+}
+
+static void test_constructor(skiatest::Reporter* reporter) {
+    // Allocate a matrix on the heap
+    SkMatrix44* placeholderMatrix = new SkMatrix44();
+    for (int row = 0; row < 4; ++row) {
+        for (int col = 0; col < 4; ++col) {
+            placeholderMatrix->setDouble(row, col, row * col);
+        }
+    }
+
+    // Use placement-new syntax to trigger the constructor on top of the heap
+    // address we already initialized. This allows us to check that the
+    // constructor did avoid initializing the matrix contents.
+    SkMatrix44* testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kUninitialized_Constructor);
+    REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
+    REPORTER_ASSERT(reporter, !testMatrix->isIdentity());
+    for (int row = 0; row < 4; ++row) {
+        for (int col = 0; col < 4; ++col) {
+            REPORTER_ASSERT(reporter, nearly_equal_double(row * col, testMatrix->getDouble(row, col)));
+        }
+    }
+
+    // Verify that kIdentity_Constructor really does initialize to an identity matrix.
+    testMatrix = 0;
+    testMatrix = new(placeholderMatrix) SkMatrix44(SkMatrix44::kIdentity_Constructor);
+    REPORTER_ASSERT(reporter, testMatrix == placeholderMatrix);
+    REPORTER_ASSERT(reporter, testMatrix->isIdentity());
+    REPORTER_ASSERT(reporter, *testMatrix == SkMatrix44::I());
+}
+
+static void test_translate(skiatest::Reporter* reporter) {
+    SkMatrix44 mat, inverse;
+
+    mat.setTranslate(0, 0, 0);
+    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
+    mat.setTranslate(1, 2, 3);
+    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kTranslate_Mask));
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
+    REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kTranslate_Mask));
+
+    SkMatrix44 a, b, c;
+    a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
+    b.setTranslate(10, 11, 12);
+
+    c.setConcat(a, b);
+    mat = a;
+    mat.preTranslate(10, 11, 12);
+    REPORTER_ASSERT(reporter, mat == c);
+
+    c.setConcat(b, a);
+    mat = a;
+    mat.postTranslate(10, 11, 12);
+    REPORTER_ASSERT(reporter, mat == c);
+}
+
+static void test_scale(skiatest::Reporter* reporter) {
+    SkMatrix44 mat, inverse;
+
+    mat.setScale(1, 1, 1);
+    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kIdentity_Mask));
+    mat.setScale(1, 2, 3);
+    REPORTER_ASSERT(reporter, bits_isonly(mat.getType(), SkMatrix44::kScale_Mask));
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
+    REPORTER_ASSERT(reporter, bits_isonly(inverse.getType(), SkMatrix44::kScale_Mask));
+
+    SkMatrix44 a, b, c;
+    a.set3x3(1, 2, 3, 4, 5, 6, 7, 8, 9);
+    b.setScale(10, 11, 12);
+
+    c.setConcat(a, b);
+    mat = a;
+    mat.preScale(10, 11, 12);
+    REPORTER_ASSERT(reporter, mat == c);
+
+    c.setConcat(b, a);
+    mat = a;
+    mat.postScale(10, 11, 12);
+    REPORTER_ASSERT(reporter, mat == c);
+}
+
+static void make_i(SkMatrix44* mat) { mat->setIdentity(); }
+static void make_t(SkMatrix44* mat) { mat->setTranslate(1, 2, 3); }
+static void make_s(SkMatrix44* mat) { mat->setScale(1, 2, 3); }
+static void make_st(SkMatrix44* mat) {
+    mat->setScale(1, 2, 3);
+    mat->postTranslate(1, 2, 3);
+}
+static void make_a(SkMatrix44* mat) {
+    mat->setRotateDegreesAbout(1, 2, 3, 45);
+}
+static void make_p(SkMatrix44* mat) {
+    SkMScalar data[] = {
+        1, 2, 3, 4, 5, 6, 7, 8,
+        1, 2, 3, 4, 5, 6, 7, 8,
+    };
+    mat->setRowMajor(data);
+}
+
+typedef void (*Make44Proc)(SkMatrix44*);
+
+static const Make44Proc gMakeProcs[] = {
+    make_i, make_t, make_s, make_st, make_a, make_p
+};
+
+static void test_map2(skiatest::Reporter* reporter, const SkMatrix44& mat) {
+    SkMScalar src2[] = { 1, 2 };
+    SkMScalar src4[] = { src2[0], src2[1], 0, 1 };
+    SkMScalar dstA[4], dstB[4];
+
+    for (int i = 0; i < 4; ++i) {
+        dstA[i] = 123456789;
+        dstB[i] = 987654321;
+    }
+
+    mat.map2(src2, 1, dstA);
+    mat.mapMScalars(src4, dstB);
+
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter, dstA[i] == dstB[i]);
+    }
+}
+
+static void test_map2(skiatest::Reporter* reporter) {
+    SkMatrix44 mat;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gMakeProcs); ++i) {
+        gMakeProcs[i](&mat);
+        test_map2(reporter, mat);
+    }
+}
+
+static void test_gettype(skiatest::Reporter* reporter) {
+    SkMatrix44 matrix;
+
+    REPORTER_ASSERT(reporter, matrix.isIdentity());
+    REPORTER_ASSERT(reporter, SkMatrix44::kIdentity_Mask == matrix.getType());
+
+    int expectedMask;
+
+    matrix.set(1, 1, 0);
+    expectedMask = SkMatrix44::kScale_Mask;
+    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
+
+    matrix.set(0, 3, 1);    // translate-x
+    expectedMask |= SkMatrix44::kTranslate_Mask;
+    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
+
+    matrix.set(2, 0, 1);
+    expectedMask |= SkMatrix44::kAffine_Mask;
+    REPORTER_ASSERT(reporter, matrix.getType() == expectedMask);
+
+    matrix.set(3, 2, 1);
+    REPORTER_ASSERT(reporter, matrix.getType() & SkMatrix44::kPerspective_Mask);
+}
+
 static void test_common_angles(skiatest::Reporter* reporter) {
     SkMatrix44 rot;
     // Test precision of rotation in common cases
     int common_angles[] = { 0, 90, -90, 180, -180, 270, -270, 360, -360 };
     for (int i = 0; i < 9; ++i) {
-        rot.setRotateDegreesAbout(0, 0, -1, common_angles[i]);
+        rot.setRotateDegreesAbout(0, 0, -1, SkIntToScalar(common_angles[i]));
 
         SkMatrix rot3x3 = rot;
         REPORTER_ASSERT(reporter, rot3x3.rectStaysRect());
     }
 }
 
-void TestMatrix44(skiatest::Reporter* reporter) {
-#ifdef SK_SCALAR_IS_FLOAT
+static void test_concat(skiatest::Reporter* reporter) {
+    int i;
+    SkMatrix44 a, b, c, d;
+
+    a.setTranslate(10, 10, 10);
+    b.setScale(2, 2, 2);
+
+    SkScalar src[8] = {
+        0, 0, 0, 1,
+        1, 1, 1, 1
+    };
+    SkScalar dst[8];
+
+    c.setConcat(a, b);
+
+    d = a;
+    d.preConcat(b);
+    REPORTER_ASSERT(reporter, d == c);
+
+    c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
+    for (i = 0; i < 3; ++i) {
+        REPORTER_ASSERT(reporter, 10 == dst[i]);
+        REPORTER_ASSERT(reporter, 12 == dst[i + 4]);
+    }
+
+    c.setConcat(b, a);
+
+    d = a;
+    d.postConcat(b);
+    REPORTER_ASSERT(reporter, d == c);
+
+    c.mapScalars(src, dst); c.mapScalars(src + 4, dst + 4);
+    for (i = 0; i < 3; ++i) {
+        REPORTER_ASSERT(reporter, 20 == dst[i]);
+        REPORTER_ASSERT(reporter, 22 == dst[i + 4]);
+    }
+}
+
+static void test_determinant(skiatest::Reporter* reporter) {
+    SkMatrix44 a;
+    REPORTER_ASSERT(reporter, nearly_equal_double(1, a.determinant()));
+    a.set(1, 1, 2);
+    REPORTER_ASSERT(reporter, nearly_equal_double(2, a.determinant()));
+    SkMatrix44 b;
+    REPORTER_ASSERT(reporter, a.invert(&b));
+    REPORTER_ASSERT(reporter, nearly_equal_double(0.5, b.determinant()));
+    SkMatrix44 c = b = a;
+    c.set(0, 1, 4);
+    b.set(1, 0, 4);
+    REPORTER_ASSERT(reporter,
+                    nearly_equal_double(a.determinant(),
+                                        b.determinant()));
+    SkMatrix44 d = a;
+    d.set(0, 0, 8);
+    REPORTER_ASSERT(reporter, nearly_equal_double(16, d.determinant()));
+
+    SkMatrix44 e = a;
+    e.postConcat(d);
+    REPORTER_ASSERT(reporter, nearly_equal_double(32, e.determinant()));
+    e.set(0, 0, 0);
+    REPORTER_ASSERT(reporter, nearly_equal_double(0, e.determinant()));
+}
+
+static void test_transpose(skiatest::Reporter* reporter) {
+    SkMatrix44 a;
+    SkMatrix44 b;
+
+    int i = 0;
+    for (int row = 0; row < 4; ++row) {
+        for (int col = 0; col < 4; ++col) {
+            a.setDouble(row, col, i);
+            b.setDouble(col, row, i++);
+        }
+    }
+
+    a.transpose();
+    REPORTER_ASSERT(reporter, nearly_equal(a, b));
+}
+
+static void test_get_set_double(skiatest::Reporter* reporter) {
+    SkMatrix44 a;
+    for (int row = 0; row < 4; ++row) {
+        for (int col = 0; col < 4; ++col) {
+            a.setDouble(row, col, 3.141592653589793);
+            REPORTER_ASSERT(reporter,
+                            nearly_equal_double(3.141592653589793,
+                                                a.getDouble(row, col)));
+            a.setDouble(row, col, 0);
+            REPORTER_ASSERT(reporter,
+                            nearly_equal_double(0, a.getDouble(row, col)));
+        }
+    }
+}
+
+static void test_set_row_col_major(skiatest::Reporter* reporter) {
+    SkMatrix44 a, b, c, d;
+    for (int row = 0; row < 4; ++row) {
+        for (int col = 0; col < 4; ++col) {
+            a.setDouble(row, col, row * 4 + col);
+        }
+    }
+
+    double bufferd[16];
+    float bufferf[16];
+    a.asColMajord(bufferd);
+    b.setColMajord(bufferd);
+    REPORTER_ASSERT(reporter, nearly_equal(a, b));
+    b.setRowMajord(bufferd);
+    b.transpose();
+    REPORTER_ASSERT(reporter, nearly_equal(a, b));
+    a.asColMajorf(bufferf);
+    b.setColMajorf(bufferf);
+    REPORTER_ASSERT(reporter, nearly_equal(a, b));
+    b.setRowMajorf(bufferf);
+    b.transpose();
+    REPORTER_ASSERT(reporter, nearly_equal(a, b));
+}
+
+static void TestMatrix44(skiatest::Reporter* reporter) {
     SkMatrix44 mat, inverse, iden1, iden2, rot;
 
     mat.reset();
-    mat.setTranslate(SK_Scalar1, SK_Scalar1, SK_Scalar1);
+    mat.setTranslate(1, 1, 1);
     mat.invert(&inverse);
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
 
-    mat.setScale(SkIntToScalar(2), SkIntToScalar(2), SkIntToScalar(2));
+    mat.setScale(2, 2, 2);
     mat.invert(&inverse);
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
 
-    mat.setScale(SK_Scalar1/2, SK_Scalar1/2, SK_Scalar1/2);
+    mat.setScale(SK_MScalar1/2, SK_MScalar1/2, SK_MScalar1/2);
     mat.invert(&inverse);
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
 
-    mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20));
-    rot.setRotateDegreesAbout(
-        SkIntToScalar(0),
-        SkIntToScalar(0),
-        SkIntToScalar(-1),
-        SkIntToScalar(90));
+    mat.setScale(3, 3, 3);
+    rot.setRotateDegreesAbout(0, 0, -1, 90);
     mat.postConcat(rot);
     REPORTER_ASSERT(reporter, mat.invert(NULL));
     mat.invert(&inverse);
@@ -115,7 +395,7 @@
         mat.setTranslate(2, 3, 4);
         float dataf[16];
         double datad[16];
-        
+
         mat.asColMajorf(dataf);
         assert16<float>(reporter, dataf,
                  1, 0, 0, 0,
@@ -139,10 +419,21 @@
                         0, 0, 0, 1);
     }
 
-#if 0   // working on making this pass
-    test_common_angles(reporter);
-#endif
-#endif
+    test_concat(reporter);
+
+    if (false) { // avoid bit rot, suppress warning (working on making this pass)
+        test_common_angles(reporter);
+    }
+
+    test_constructor(reporter);
+    test_gettype(reporter);
+    test_determinant(reporter);
+    test_transpose(reporter);
+    test_get_set_double(reporter);
+    test_set_row_col_major(reporter);
+    test_translate(reporter);
+    test_scale(reporter);
+    test_map2(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
index c9a696c..d585f75 100644
--- a/tests/MatrixTest.cpp
+++ b/tests/MatrixTest.cpp
@@ -32,32 +32,103 @@
     return true;
 }
 
+static bool are_equal(skiatest::Reporter* reporter,
+                      const SkMatrix& a,
+                      const SkMatrix& b) {
+    bool equal = a == b;
+    bool cheapEqual = a.cheapEqualTo(b);
+    if (equal != cheapEqual) {
+#ifdef SK_SCALAR_IS_FLOAT
+        if (equal) {
+            bool foundZeroSignDiff = false;
+            for (int i = 0; i < 9; ++i) {
+                float aVal = a.get(i);
+                float bVal = b.get(i);
+                int aValI = *SkTCast<int*>(&aVal);
+                int bValI = *SkTCast<int*>(&bVal);
+                if (0 == aVal && 0 == bVal && aValI != bValI) {
+                    foundZeroSignDiff = true;
+                } else {
+                    REPORTER_ASSERT(reporter, aVal == bVal && aValI == aValI);
+                }
+            }
+            REPORTER_ASSERT(reporter, foundZeroSignDiff);
+        } else {
+            bool foundNaN = false;
+            for (int i = 0; i < 9; ++i) {
+                float aVal = a.get(i);
+                float bVal = b.get(i);
+                int aValI = *SkTCast<int*>(&aVal);
+                int bValI = *SkTCast<int*>(&bVal);
+                if (sk_float_isnan(aVal) && aValI == bValI) {
+                    foundNaN = true;
+                } else {
+                    REPORTER_ASSERT(reporter, aVal == bVal && aValI == bValI);
+                }
+            }
+            REPORTER_ASSERT(reporter, foundNaN);
+        }
+#else
+        REPORTER_ASSERT(reporter, false);
+#endif
+    }
+    return equal;
+}
+
 static bool is_identity(const SkMatrix& m) {
     SkMatrix identity;
     identity.reset();
     return nearly_equal(m, identity);
 }
 
+static void test_matrix_recttorect(skiatest::Reporter* reporter) {
+    SkRect src, dst;
+    SkMatrix matrix;
+
+    src.set(0, 0, SK_Scalar1*10, SK_Scalar1*10);
+    dst = src;
+    matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
+    REPORTER_ASSERT(reporter, SkMatrix::kIdentity_Mask == matrix.getType());
+    REPORTER_ASSERT(reporter, matrix.rectStaysRect());
+
+    dst.offset(SK_Scalar1, SK_Scalar1);
+    matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
+    REPORTER_ASSERT(reporter, SkMatrix::kTranslate_Mask == matrix.getType());
+    REPORTER_ASSERT(reporter, matrix.rectStaysRect());
+
+    dst.fRight += SK_Scalar1;
+    matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
+    REPORTER_ASSERT(reporter,
+                    (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask) == matrix.getType());
+    REPORTER_ASSERT(reporter, matrix.rectStaysRect());
+
+    dst = src;
+    dst.fRight = src.fRight * 2;
+    matrix.setRectToRect(src, dst, SkMatrix::kFill_ScaleToFit);
+    REPORTER_ASSERT(reporter, SkMatrix::kScale_Mask == matrix.getType());
+    REPORTER_ASSERT(reporter, matrix.rectStaysRect());
+}
+
 static void test_flatten(skiatest::Reporter* reporter, const SkMatrix& m) {
     // add 100 in case we have a bug, I don't want to kill my stack in the test
     char buffer[SkMatrix::kMaxFlattenSize + 100];
-    uint32_t size1 = m.flatten(NULL);
-    uint32_t size2 = m.flatten(buffer);
+    uint32_t size1 = m.writeToMemory(NULL);
+    uint32_t size2 = m.writeToMemory(buffer);
     REPORTER_ASSERT(reporter, size1 == size2);
     REPORTER_ASSERT(reporter, size1 <= SkMatrix::kMaxFlattenSize);
-    
+
     SkMatrix m2;
-    uint32_t size3 = m2.unflatten(buffer);
-    REPORTER_ASSERT(reporter, size1 == size2);
-    REPORTER_ASSERT(reporter, m == m2);
-    
+    uint32_t size3 = m2.readFromMemory(buffer);
+    REPORTER_ASSERT(reporter, size1 == size3);
+    REPORTER_ASSERT(reporter, are_equal(reporter, m, m2));
+
     char buffer2[SkMatrix::kMaxFlattenSize + 100];
-    size3 = m2.flatten(buffer2);
-    REPORTER_ASSERT(reporter, size1 == size2);
+    size3 = m2.writeToMemory(buffer2);
+    REPORTER_ASSERT(reporter, size1 == size3);
     REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
 }
 
-void test_matrix_max_stretch(skiatest::Reporter* reporter) {
+static void test_matrix_max_stretch(skiatest::Reporter* reporter) {
     SkMatrix identity;
     identity.reset();
     REPORTER_ASSERT(reporter, SK_Scalar1 == identity.getMaxStretch());
@@ -106,7 +177,7 @@
             mat.postConcat(mats[x]);
         }
         SkScalar stretch = mat.getMaxStretch();
-        
+
         if ((stretch < 0) != mat.hasPerspective()) {
             stretch = mat.getMaxStretch();
         }
@@ -145,23 +216,152 @@
     }
 }
 
-void TestMatrix(skiatest::Reporter* reporter) {
+static void test_matrix_is_similarity(skiatest::Reporter* reporter) {
+    SkMatrix mat;
+
+    // identity
+    mat.setIdentity();
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // translation only
+    mat.reset();
+    mat.setTranslate(SkIntToScalar(100), SkIntToScalar(100));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // scale with same size
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(15));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // scale with one negative
+    mat.reset();
+    mat.setScale(SkIntToScalar(-15), SkIntToScalar(15));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // scale with different size
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(20));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // scale with same size at a pivot point
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(15),
+                 SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // scale with different size at a pivot point
+    mat.reset();
+    mat.setScale(SkIntToScalar(15), SkIntToScalar(20),
+                 SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // skew with same size
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // skew with different size
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // skew with same size at a pivot point
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(15),
+                SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // skew with different size at a pivot point
+    mat.reset();
+    mat.setSkew(SkIntToScalar(15), SkIntToScalar(20),
+                SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // perspective x
+    mat.reset();
+    mat.setPerspX(SkScalarToPersp(SK_Scalar1 / 2));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // perspective y
+    mat.reset();
+    mat.setPerspY(SkScalarToPersp(SK_Scalar1 / 2));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+#ifdef SK_SCALAR_IS_FLOAT
+    /* We bypass the following tests for SK_SCALAR_IS_FIXED build.
+     * The long discussion can be found in this issue:
+     *     http://codereview.appspot.com/5999050/
+     * In short, we haven't found a perfect way to fix the precision
+     * issue, i.e. the way we use tolerance in isSimilarityTransformation
+     * is incorrect. The situation becomes worse in fixed build, so
+     * we disabled rotation related tests for fixed build.
+     */
+
+    // rotate
+    for (int angle = 0; angle < 360; ++angle) {
+        mat.reset();
+        mat.setRotate(SkIntToScalar(angle));
+        REPORTER_ASSERT(reporter, mat.isSimilarity());
+    }
+
+    // see if there are any accumulated precision issues
+    mat.reset();
+    for (int i = 1; i < 360; i++) {
+        mat.postRotate(SkIntToScalar(1));
+    }
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // rotate + translate
+    mat.reset();
+    mat.setRotate(SkIntToScalar(30));
+    mat.postTranslate(SkIntToScalar(10), SkIntToScalar(20));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // rotate + uniform scale
+    mat.reset();
+    mat.setRotate(SkIntToScalar(30));
+    mat.postScale(SkIntToScalar(2), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+
+    // rotate + non-uniform scale
+    mat.reset();
+    mat.setRotate(SkIntToScalar(30));
+    mat.postScale(SkIntToScalar(3), SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+#endif
+
+    // all zero
+    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, 0);
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // all zero except perspective
+    mat.setAll(0, 0, 0, 0, 0, 0, 0, 0, SK_Scalar1);
+    REPORTER_ASSERT(reporter, !mat.isSimilarity());
+
+    // scales zero, only skews
+    mat.setAll(0, SK_Scalar1, 0,
+               SK_Scalar1, 0, 0,
+               0, 0, SkMatrix::I()[8]);
+    REPORTER_ASSERT(reporter, mat.isSimilarity());
+}
+
+static void TestMatrix(skiatest::Reporter* reporter) {
     SkMatrix    mat, inverse, iden1, iden2;
 
     mat.reset();
     mat.setTranslate(SK_Scalar1, SK_Scalar1);
-    mat.invert(&inverse);
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
 
-    mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
-    mat.invert(&inverse);
+    mat.setScale(SkIntToScalar(2), SkIntToScalar(4));
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
     test_flatten(reporter, mat);
 
-    mat.setScale(SK_Scalar1/2, SK_Scalar1/2);
-    mat.invert(&inverse);
+    mat.setScale(SK_Scalar1/2, SkIntToScalar(2));
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
     test_flatten(reporter, mat);
@@ -169,7 +369,7 @@
     mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
     mat.postRotate(SkIntToScalar(25));
     REPORTER_ASSERT(reporter, mat.invert(NULL));
-    mat.invert(&inverse);
+    REPORTER_ASSERT(reporter, mat.invert(&inverse));
     iden1.setConcat(mat, inverse);
     REPORTER_ASSERT(reporter, is_identity(iden1));
     iden2.setConcat(inverse, mat);
@@ -177,6 +377,13 @@
     test_flatten(reporter, mat);
     test_flatten(reporter, iden2);
 
+    mat.setScale(0, SK_Scalar1);
+    REPORTER_ASSERT(reporter, !mat.invert(NULL));
+    REPORTER_ASSERT(reporter, !mat.invert(&inverse));
+    mat.setScale(SK_Scalar1, 0);
+    REPORTER_ASSERT(reporter, !mat.invert(NULL));
+    REPORTER_ASSERT(reporter, !mat.invert(&inverse));
+
     // rectStaysRect test
     {
         static const struct {
@@ -237,7 +444,27 @@
     mat.set(SkMatrix::kMPersp1, SkScalarToPersp(SK_Scalar1 / 2));
     REPORTER_ASSERT(reporter, !mat.asAffine(affine));
 
+    SkMatrix mat2;
+    mat2.reset();
+    mat.reset();
+    SkScalar zero = 0;
+    mat.set(SkMatrix::kMSkewX, -zero);
+    REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
+
+    mat2.reset();
+    mat.reset();
+    mat.set(SkMatrix::kMSkewX, SK_ScalarNaN);
+    mat2.set(SkMatrix::kMSkewX, SK_ScalarNaN);
+    // fixed pt doesn't have the property that NaN does not equal itself.
+#ifdef SK_SCALAR_IS_FIXED
+    REPORTER_ASSERT(reporter, are_equal(reporter, mat, mat2));
+#else
+    REPORTER_ASSERT(reporter, !are_equal(reporter, mat, mat2));
+#endif
+
     test_matrix_max_stretch(reporter);
+    test_matrix_is_similarity(reporter);
+    test_matrix_recttorect(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/MemsetTest.cpp b/tests/MemsetTest.cpp
index 9c1fc92..ad1a21c 100644
--- a/tests/MemsetTest.cpp
+++ b/tests/MemsetTest.cpp
@@ -1,13 +1,39 @@
-
 /*
  * Copyright 2011 Google Inc.
  *
  * Use of this source code is governed by a BSD-style license that can be
  * found in the LICENSE file.
  */
+
 #include "Test.h"
+#include "SkChunkAlloc.h"
 #include "SkUtils.h"
 
+static void test_chunkalloc(skiatest::Reporter* reporter) {
+    size_t min = 256;
+    SkChunkAlloc alloc(min);
+
+    REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
+    REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
+    REPORTER_ASSERT(reporter, !alloc.contains(NULL));
+    REPORTER_ASSERT(reporter, !alloc.contains(reporter));
+
+    alloc.reset();
+    REPORTER_ASSERT(reporter, 0 == alloc.totalCapacity());
+    REPORTER_ASSERT(reporter, 0 == alloc.blockCount());
+
+    size_t size = min >> 1;
+    void* ptr = alloc.allocThrow(size);
+    REPORTER_ASSERT(reporter, alloc.totalCapacity() >= size);
+    REPORTER_ASSERT(reporter, alloc.blockCount() > 0);
+    REPORTER_ASSERT(reporter, alloc.contains(ptr));
+
+    alloc.reset();
+    REPORTER_ASSERT(reporter, !alloc.contains(ptr));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
 static void set_zero(void* dst, size_t bytes) {
     char* ptr = (char*)dst;
     for (size_t i = 0; i < bytes; ++i) {
@@ -45,14 +71,14 @@
 
 static void test_16(skiatest::Reporter* reporter) {
     uint16_t buffer[TOTAL];
-    
+
     for (int count = 0; count < MAX_COUNT; ++count) {
         for (int alignment = 0; alignment < MAX_ALIGNMENT; ++alignment) {
             set_zero(buffer, sizeof(buffer));
-            
+
             uint16_t* base = &buffer[PAD + alignment];
             sk_memset16(base, VALUE16, count);
-            
+
             compare16(buffer,       0,       PAD + alignment);
             compare16(base,         VALUE16, count);
             compare16(base + count, 0,       TOTAL - count - PAD - alignment);
@@ -62,14 +88,14 @@
 
 static void test_32(skiatest::Reporter* reporter) {
     uint32_t buffer[TOTAL];
-    
+
     for (int count = 0; count < MAX_COUNT; ++count) {
         for (int alignment = 0; alignment < MAX_ALIGNMENT; ++alignment) {
             set_zero(buffer, sizeof(buffer));
-            
+
             uint32_t* base = &buffer[PAD + alignment];
             sk_memset32(base, VALUE32, count);
-            
+
             compare32(buffer,       0,       PAD + alignment);
             compare32(base,         VALUE32, count);
             compare32(base + count, 0,       TOTAL - count - PAD - alignment);
@@ -85,6 +111,8 @@
 static void TestMemset(skiatest::Reporter* reporter) {
     test_16(reporter);
     test_32(reporter);
+
+    test_chunkalloc(reporter);
 };
 
 #include "TestClassDef.h"
diff --git a/tests/MetaDataTest.cpp b/tests/MetaDataTest.cpp
index 1cc3cca..4390652 100644
--- a/tests/MetaDataTest.cpp
+++ b/tests/MetaDataTest.cpp
@@ -36,7 +36,7 @@
 
 static void TestMetaData(skiatest::Reporter* reporter) {
     SkMetaData  m1;
-    
+
     REPORTER_ASSERT(reporter, !m1.findS32("int"));
     REPORTER_ASSERT(reporter, !m1.findScalar("scalar"));
     REPORTER_ASSERT(reporter, !m1.findString("hello"));
@@ -45,28 +45,28 @@
     REPORTER_ASSERT(reporter, !m1.removeString("hello"));
     REPORTER_ASSERT(reporter, !m1.removeString("true"));
     REPORTER_ASSERT(reporter, !m1.removeString("false"));
-    
+
     m1.setS32("int", 12345);
     m1.setScalar("scalar", SK_Scalar1 * 42);
     m1.setString("hello", "world");
     m1.setPtr("ptr", &m1);
     m1.setBool("true", true);
     m1.setBool("false", false);
-    
+
     int32_t     n;
     SkScalar    s;
-    
+
     m1.setScalar("scalar", SK_Scalar1/2);
-    
+
     REPORTER_ASSERT(reporter, m1.findS32("int", &n) && n == 12345);
     REPORTER_ASSERT(reporter, m1.findScalar("scalar", &s) && s == SK_Scalar1/2);
     REPORTER_ASSERT(reporter, !strcmp(m1.findString("hello"), "world"));
     REPORTER_ASSERT(reporter, m1.hasBool("true", true));
     REPORTER_ASSERT(reporter, m1.hasBool("false", false));
-    
+
     SkMetaData::Iter iter(m1);
     const char* name;
-    
+
     static const struct {
         const char*         fName;
         SkMetaData::Type    fType;
@@ -79,7 +79,7 @@
         { "true",   SkMetaData::kBool_Type,     1 },
         { "false",  SkMetaData::kBool_Type,     1 }
     };
-    
+
     int                 loop = 0;
     int count;
     SkMetaData::Type    t;
@@ -99,13 +99,13 @@
         loop += 1;
     }
     REPORTER_ASSERT(reporter, loop == SK_ARRAY_COUNT(gElems));
-    
+
     REPORTER_ASSERT(reporter, m1.removeS32("int"));
     REPORTER_ASSERT(reporter, m1.removeScalar("scalar"));
     REPORTER_ASSERT(reporter, m1.removeString("hello"));
     REPORTER_ASSERT(reporter, m1.removeBool("true"));
     REPORTER_ASSERT(reporter, m1.removeBool("false"));
-    
+
     REPORTER_ASSERT(reporter, !m1.findS32("int"));
     REPORTER_ASSERT(reporter, !m1.findScalar("scalar"));
     REPORTER_ASSERT(reporter, !m1.findString("hello"));
diff --git a/tests/PDFPrimitivesTest.cpp b/tests/PDFPrimitivesTest.cpp
index 82686ef..6e7d616 100644
--- a/tests/PDFPrimitivesTest.cpp
+++ b/tests/PDFPrimitivesTest.cpp
@@ -7,12 +7,12 @@
  */
 
 
-#include <string>
-
 #include "Test.h"
+#include "SkCanvas.h"
 #include "SkData.h"
 #include "SkFlate.h"
 #include "SkPDFCatalog.h"
+#include "SkPDFDevice.h"
 #include "SkPDFStream.h"
 #include "SkPDFTypes.h"
 #include "SkScalar.h"
@@ -40,10 +40,10 @@
 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()) {
+    if (offset + len > data->size()) {
         return false;
     }
-    return memcmp(data.bytes() + offset, buffer, len) == 0;
+    return memcmp(data->bytes() + offset, buffer, len) == 0;
 }
 
 static void CheckObjectOutput(skiatest::Reporter* reporter, SkPDFObject* obj,
@@ -51,7 +51,7 @@
                               bool indirect, bool compression) {
     SkPDFDocument::Flags docFlags = (SkPDFDocument::Flags) 0;
     if (!compression) {
-        docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flag);
+        docFlags = SkTBitOr(docFlags, SkPDFDocument::kNoCompression_Flags);
     }
     SkPDFCatalog catalog(docFlags);
     size_t directSize = obj->getOutputSize(&catalog, false);
@@ -89,18 +89,16 @@
 
 static void SimpleCheckObjectOutput(skiatest::Reporter* reporter,
                                     SkPDFObject* obj,
-                                    const std::string& expectedResult) {
-    CheckObjectOutput(reporter, obj, expectedResult.c_str(),
-                      expectedResult.length(), true, false);
+                                    const char* expectedResult) {
+    CheckObjectOutput(reporter, obj, expectedResult,
+                      strlen(expectedResult), true, false);
 }
 
 static void TestPDFStream(skiatest::Reporter* reporter) {
     char streamBytes[] = "Test\nFoo\tBar";
-    SkRefPtr<SkMemoryStream> streamData = new SkMemoryStream(
-        streamBytes, strlen(streamBytes), true);
-    streamData->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData.get());
-    stream->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkMemoryStream> streamData(new SkMemoryStream(
+        streamBytes, strlen(streamBytes), true));
+    SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData.get()));
     SimpleCheckObjectOutput(
         reporter, stream.get(),
         "<</Length 12\n>> stream\nTest\nFoo\tBar\nendstream");
@@ -116,8 +114,7 @@
                               "with an uncompressed string.";
         SkAutoDataUnref streamData2(SkData::NewWithCopy(streamBytes2,
                                                         strlen(streamBytes2)));
-        SkRefPtr<SkPDFStream> stream = new SkPDFStream(streamData2.get());
-        stream->unref();  // SkRefPtr and new both took a reference.
+        SkAutoTUnref<SkPDFStream> stream(new SkPDFStream(streamData2.get()));
 
         SkDynamicMemoryWStream compressedByteStream;
         SkFlate::Deflate(streamData2.get(), &compressedByteStream);
@@ -130,31 +127,29 @@
         expectedResult1.writeText("\nendstream");
         SkAutoDataUnref expectedResultData1(expectedResult1.copyToData());
         CheckObjectOutput(reporter, stream.get(),
-                          (const char*) expectedResultData1.data(),
-                          expectedResultData1.size(), true, false);
+                          (const char*) expectedResultData1->data(),
+                          expectedResultData1->size(), true, false);
 
         // Then again with compression.
         SkDynamicMemoryWStream expectedResult2;
         expectedResult2.writeText("<</Filter /FlateDecode\n/Length 116\n"
                                  ">> stream\n");
-        expectedResult2.write(compressedData.data(), compressedData.size());
+        expectedResult2.write(compressedData->data(), compressedData->size());
         expectedResult2.writeText("\nendstream");
         SkAutoDataUnref expectedResultData2(expectedResult2.copyToData());
         CheckObjectOutput(reporter, stream.get(),
-                          (const char*) expectedResultData2.data(),
-                          expectedResultData2.size(), true, true);
+                          (const char*) expectedResultData2->data(),
+                          expectedResultData2->size(), true, true);
     }
 }
 
 static void TestCatalog(skiatest::Reporter* reporter) {
     SkPDFCatalog catalog((SkPDFDocument::Flags)0);
-    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
-    int1->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
-    int2->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int3 = new SkPDFInt(3);
-    int3->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int1Again(int1.get());
+    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
+    SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
+    SkAutoTUnref<SkPDFInt> int3(new SkPDFInt(3));
+    int1.get()->ref();
+    SkAutoTUnref<SkPDFInt> int1Again(int1.get());
 
     catalog.addObject(int1.get(), false);
     catalog.addObject(int2.get(), false);
@@ -175,12 +170,9 @@
 }
 
 static void TestObjectRef(skiatest::Reporter* reporter) {
-    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
-    int1->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int2 = new SkPDFInt(2);
-    int2->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFObjRef> int2ref = new SkPDFObjRef(int2.get());
-    int2ref->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
+    SkAutoTUnref<SkPDFInt> int2(new SkPDFInt(2));
+    SkAutoTUnref<SkPDFObjRef> int2ref(new SkPDFObjRef(int2.get()));
 
     SkPDFCatalog catalog((SkPDFDocument::Flags)0);
     catalog.addObject(int1.get(), false);
@@ -197,16 +189,11 @@
 }
 
 static void TestSubstitute(skiatest::Reporter* reporter) {
-    SkRefPtr<SkPDFTestDict> proxy = new SkPDFTestDict();
-    proxy->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFTestDict> stub = new SkPDFTestDict();
-    stub->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int33 = new SkPDFInt(33);
-    int33->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFDict> stubResource = new SkPDFDict();
-    stubResource->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFInt> int44 = new SkPDFInt(44);
-    int44->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFTestDict> proxy(new SkPDFTestDict());
+    SkAutoTUnref<SkPDFTestDict> stub(new SkPDFTestDict());
+    SkAutoTUnref<SkPDFInt> int33(new SkPDFInt(33));
+    SkAutoTUnref<SkPDFDict> stubResource(new SkPDFDict());
+    SkAutoTUnref<SkPDFInt> int44(new SkPDFInt(44));
 
     stub->insert("Value", int33.get());
     stubResource->insert("InnerValue", int44.get());
@@ -232,76 +219,97 @@
                                             buffer.getOffset()));
 }
 
+// This test used to assert without the fix submitted for
+// http://code.google.com/p/skia/issues/detail?id=1083.
+// SKP files might have invalid glyph ids. This test ensures they are ignored,
+// and there is no assert on input data in Debug mode.
+static void test_issue1083(skiatest::Reporter* reporter) {
+    SkISize pageSize = SkISize::Make(100, 100);
+    SkPDFDevice* dev = new SkPDFDevice(pageSize, pageSize, SkMatrix::I());
+
+    SkCanvas c(dev);
+    SkPaint paint;
+    paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
+    uint16_t glyphID = 65000;
+    c.drawText(&glyphID, 2, 0, 0, paint);
+
+    SkPDFDocument doc;
+    doc.appendPage(dev);
+
+    SkDynamicMemoryWStream stream;
+    doc.emitPDF(&stream);
+}
+
 static void TestPDFPrimitives(skiatest::Reporter* reporter) {
-    SkRefPtr<SkPDFInt> int42 = new SkPDFInt(42);
-    int42->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int42(new SkPDFInt(42));
     SimpleCheckObjectOutput(reporter, int42.get(), "42");
 
-    SkRefPtr<SkPDFScalar> realHalf = new SkPDFScalar(SK_ScalarHalf);
-    realHalf->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> realHalf(new SkPDFScalar(SK_ScalarHalf));
     SimpleCheckObjectOutput(reporter, realHalf.get(), "0.5");
 
 #if defined(SK_SCALAR_IS_FLOAT)
-    SkRefPtr<SkPDFScalar> bigScalar = new SkPDFScalar(110999.75);
-    bigScalar->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> bigScalar(new SkPDFScalar(110999.75f));
 #if !defined(SK_ALLOW_LARGE_PDF_SCALARS)
     SimpleCheckObjectOutput(reporter, bigScalar.get(), "111000");
 #else
     SimpleCheckObjectOutput(reporter, bigScalar.get(), "110999.75");
 
-    SkRefPtr<SkPDFScalar> biggerScalar = new SkPDFScalar(50000000.1);
-    biggerScalar->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> biggerScalar(new SkPDFScalar(50000000.1));
     SimpleCheckObjectOutput(reporter, biggerScalar.get(), "50000000");
 
-    SkRefPtr<SkPDFScalar> smallestScalar = new SkPDFScalar(1.0/65536);
-    smallestScalar->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFScalar> smallestScalar(new SkPDFScalar(1.0/65536));
     SimpleCheckObjectOutput(reporter, smallestScalar.get(), "0.00001526");
 #endif
 #endif
 
-    SkRefPtr<SkPDFString> stringSimple = new SkPDFString("test ) string ( foo");
-    stringSimple->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFString> stringSimple(
+        new SkPDFString("test ) string ( foo"));
     SimpleCheckObjectOutput(reporter, stringSimple.get(),
                             "(test \\) string \\( foo)");
-    SkRefPtr<SkPDFString> stringComplex =
-        new SkPDFString("\ttest ) string ( foo");
-    stringComplex->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFString> stringComplex(
+        new SkPDFString("\ttest ) string ( foo"));
     SimpleCheckObjectOutput(reporter, stringComplex.get(),
                             "<0974657374202920737472696E67202820666F6F>");
 
-    SkRefPtr<SkPDFName> name = new SkPDFName("Test name\twith#tab");
-    name->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> name(new SkPDFName("Test name\twith#tab"));
     const char expectedResult[] = "/Test#20name#09with#23tab";
     CheckObjectOutput(reporter, name.get(), expectedResult,
                       strlen(expectedResult), false, false);
 
-    SkRefPtr<SkPDFArray> array = new SkPDFArray;
-    array->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> escapedName(new SkPDFName("A#/%()<>[]{}B"));
+    const char escapedNameExpected[] = "/A#23#2F#25#28#29#3C#3E#5B#5D#7B#7DB";
+    CheckObjectOutput(reporter, escapedName.get(), escapedNameExpected,
+                      strlen(escapedNameExpected), false, false);
+
+    // Test that we correctly handle characters with the high-bit set.
+    const unsigned char highBitCString[] = {0xDE, 0xAD, 'b', 'e', 0xEF, 0};
+    SkAutoTUnref<SkPDFName> highBitName(
+        new SkPDFName((const char*)highBitCString));
+    const char highBitExpectedResult[] = "/#DE#ADbe#EF";
+    CheckObjectOutput(reporter, highBitName.get(), highBitExpectedResult,
+                      strlen(highBitExpectedResult), false, false);
+
+    SkAutoTUnref<SkPDFArray> array(new SkPDFArray);
     SimpleCheckObjectOutput(reporter, array.get(), "[]");
     array->append(int42.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[42]");
     array->append(realHalf.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5]");
-    SkRefPtr<SkPDFInt> int0 = new SkPDFInt(0);
-    int0->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int0(new SkPDFInt(0));
     array->append(int0.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[42 0.5 0]");
-    SkRefPtr<SkPDFInt> int1 = new SkPDFInt(1);
-    int1->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFInt> int1(new SkPDFInt(1));
     array->setAt(0, int1.get());
     SimpleCheckObjectOutput(reporter, array.get(), "[1 0.5 0]");
 
-    SkRefPtr<SkPDFDict> dict = new SkPDFDict;
-    dict->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFDict> dict(new SkPDFDict);
     SimpleCheckObjectOutput(reporter, dict.get(), "<<>>");
-    SkRefPtr<SkPDFName> n1 = new SkPDFName("n1");
-    n1->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> n1(new SkPDFName("n1"));
     dict->insert(n1.get(), int42.get());
     SimpleCheckObjectOutput(reporter, dict.get(), "<</n1 42\n>>");
-    SkRefPtr<SkPDFName> n2 = new SkPDFName("n2");
-    n2->unref();  // SkRefPtr and new both took a reference.
-    SkRefPtr<SkPDFName> n3 = new SkPDFName("n3");
-    n3->unref();  // SkRefPtr and new both took a reference.
+    SkAutoTUnref<SkPDFName> n2(new SkPDFName("n2"));
+    SkAutoTUnref<SkPDFName> n3(new SkPDFName("n3"));
     dict->insert(n2.get(), realHalf.get());
     dict->insert(n3.get(), array.get());
     SimpleCheckObjectOutput(reporter, dict.get(),
@@ -314,6 +322,8 @@
     TestObjectRef(reporter);
 
     TestSubstitute(reporter);
+
+    test_issue1083(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/PaintTest.cpp b/tests/PaintTest.cpp
index 6350eb5..2a84e4c 100644
--- a/tests/PaintTest.cpp
+++ b/tests/PaintTest.cpp
@@ -69,15 +69,15 @@
     SkPath path, stroke;
     SkPaint paint;
 
-    path.moveTo(SkFloatToFixed(460.2881309415525f),
-                SkFloatToFixed(303.250847066498));
-    path.cubicTo(SkFloatToFixed(463.36378422175284),
-                 SkFloatToFixed(302.1169735073363),
-                 SkFloatToFixed(456.32239330810046),
-                 SkFloatToFixed(304.720354932878),
-                 SkFloatToFixed(453.15255460013304),
-                 SkFloatToFixed(305.788586869862));
-    
+    path.moveTo(SkFloatToScalar(460.2881309415525f),
+                SkFloatToScalar(303.250847066498f));
+    path.cubicTo(SkFloatToScalar(463.36378422175284f),
+                 SkFloatToScalar(302.1169735073363f),
+                 SkFloatToScalar(456.32239330810046f),
+                 SkFloatToScalar(304.720354932878f),
+                 SkFloatToScalar(453.15255460013304f),
+                 SkFloatToScalar(305.788586869862f));
+
     SkRect fillR, strokeR;
     fillR = path.getBounds();
 
diff --git a/tests/ParsePathTest.cpp b/tests/ParsePathTest.cpp
index 831cd8e..c911860 100644
--- a/tests/ParsePathTest.cpp
+++ b/tests/ParsePathTest.cpp
@@ -27,18 +27,18 @@
 #endif
 }
 
+static struct {
+    const char* fStr;
+    const SkRect fBounds;
+} gRec[] = {
+    { "", { 0, 0, 0, 0 } },
+    { "M0,0L10,10", { 0, 0, SkIntToScalar(10), SkIntToScalar(10) } },
+    { "M-5.5,-0.5 Q 0 0 6,6.50",
+        { SkFloatToScalar(-5.5f), SkFloatToScalar(-0.5f),
+          SkFloatToScalar(6), SkFloatToScalar(6.5f) } }
+};
+
 static void TestParsePath(skiatest::Reporter* reporter) {
-    static const struct {
-        const char* fStr;
-        SkRect      fBounds;
-    } gRec[] = {
-        { "", { 0, 0, 0, 0 } },
-        { "M0,0L10,10", { 0, 0, SkIntToScalar(10), SkIntToScalar(10) } },
-        { "M-5.5,-0.5 Q 0 0 6,6.50",
-            { SkFloatToScalar(-5.5f), SkFloatToScalar(-0.5f),
-              SkFloatToScalar(6), SkFloatToScalar(6.5f) } }
-    };
-    
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); i++) {
         SkPath  path;
         bool success = SkParsePath::FromSVGString(gRec[i].fStr, &path);
@@ -49,15 +49,15 @@
 
         test_to_from(reporter, path);
     }
-    
+
     SkRect r;
-    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5));
+    r.set(0, 0, SkFloatToScalar(10), SkFloatToScalar(10.5f));
     SkPath p;
     p.addRect(r);
     test_to_from(reporter, p);
     p.addOval(r);
     test_to_from(reporter, p);
-    p.addRoundRect(r, SkFloatToScalar(4), SkFloatToScalar(4.5));
+    p.addRoundRect(r, SkFloatToScalar(4), SkFloatToScalar(4.5f));
     test_to_from(reporter, p);
 }
 
diff --git a/tests/PathCoverageTest.cpp b/tests/PathCoverageTest.cpp
index 91de178..87ec908 100644
--- a/tests/PathCoverageTest.cpp
+++ b/tests/PathCoverageTest.cpp
@@ -60,28 +60,28 @@
     if (d < tol) {
        return 1;
     } else {
-       int temp = SkScalarCeil(SkScalarSqrt(SkScalarDiv(d, tol)));
-       uint32_t count = SkMinScalar(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
+       int temp = SkScalarCeilToInt(SkScalarSqrt(SkScalarDiv(d, tol)));
+       uint32_t count = SkMin32(SkNextPow2(temp), MAX_POINTS_PER_CURVE);
        return count;
     }
 }
 
-uint32_t quadraticPointCount_EE(const SkPoint points[], SkScalar tol) {
+static uint32_t quadraticPointCount_EE(const SkPoint points[], SkScalar tol) {
     int distance = estimate_distance(points);
     return estimate_pointCount(distance);
 }
 
-uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
+static uint32_t quadraticPointCount_EC(const SkPoint points[], SkScalar tol) {
     int distance = estimate_distance(points);
     return compute_pointCount(SkIntToScalar(distance), tol);
 }
 
-uint32_t quadraticPointCount_CE(const SkPoint points[], SkScalar tol) {
+static uint32_t quadraticPointCount_CE(const SkPoint points[], SkScalar tol) {
     SkScalar distance = compute_distance(points);
     return estimate_pointCount(SkScalarRound(distance));
 }
 
-uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
+static uint32_t quadraticPointCount_CC(const SkPoint points[], SkScalar tol) {
     SkScalar distance = compute_distance(points);
     return compute_pointCount(distance, tol);
 }
@@ -123,6 +123,13 @@
             quadraticPointCount_CC(path, SkIntToScalar(1));
         uint32_t estimatedCount =
             quadraticPointCount_EE(path, SkIntToScalar(1));
+
+        if (false) { // avoid bit rot, suppress warning
+            computedCount =
+                    quadraticPointCount_EC(path, SkIntToScalar(1));
+            estimatedCount =
+                    quadraticPointCount_CE(path, SkIntToScalar(1));
+        }
         // Allow estimated to be high by a factor of two, but no less than
         // the computed value.
         bool isAccurate = (estimatedCount >= computedCount) &&
diff --git a/tests/PathMeasureTest.cpp b/tests/PathMeasureTest.cpp
index 2ff9f3a..e8477c6 100644
--- a/tests/PathMeasureTest.cpp
+++ b/tests/PathMeasureTest.cpp
@@ -8,6 +8,77 @@
 #include "Test.h"
 #include "SkPathMeasure.h"
 
+static void test_small_segment3(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    SkPath path;
+    const SkPoint pts[] = {
+        { 0, 0 },
+        { 100000000000.0f, 100000000000.0f }, { 0, 0 }, { 10, 10 },
+        { 10, 10 }, { 0, 0 }, { 10, 10 }
+    };
+
+    path.moveTo(pts[0]);
+    for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 3) {
+        path.cubicTo(pts[i], pts[i + 1], pts[i + 2]);
+    }
+
+    SkPathMeasure meas(path, false);
+    meas.getLength();
+#endif
+}
+
+static void test_small_segment2(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    SkPath path;
+    const SkPoint pts[] = {
+        { 0, 0 },
+        { 100000000000.0f, 100000000000.0f }, { 0, 0 },
+        { 10, 10 }, { 0, 0 },
+    };
+
+    path.moveTo(pts[0]);
+    for (size_t i = 1; i < SK_ARRAY_COUNT(pts); i += 2) {
+        path.quadTo(pts[i], pts[i + 1]);
+    }
+    SkPathMeasure meas(path, false);
+    meas.getLength();
+#endif
+}
+
+static void test_small_segment(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    SkPath path;
+    const SkPoint pts[] = {
+        { 100000, 100000},
+        // big jump between these points, makes a big segment
+        { SkFloatToScalar(1.0005f), SkFloatToScalar(0.9999f) },
+        // tiny (non-zero) jump between these points
+        { SK_Scalar1, SK_Scalar1 },
+    };
+
+    path.moveTo(pts[0]);
+    for (size_t i = 1; i < SK_ARRAY_COUNT(pts); ++i) {
+        path.lineTo(pts[i]);
+    }
+    SkPathMeasure meas(path, false);
+
+    /*  this would assert (before a fix) because we added a segment with
+        the same length as the prev segment, due to the follow (bad) pattern
+
+        d = distance(pts[0], pts[1]);
+        distance += d;
+        seg->fDistance = distance;
+
+        SkASSERT(d > 0);    // TRUE
+        SkASSERT(seg->fDistance > prevSeg->fDistance);  // FALSE
+
+        This 2nd assert failes because (distance += d) didn't affect distance
+        because distance >>> d.
+     */
+    meas.getLength();
+#endif
+}
+
 static void TestPathMeasure(skiatest::Reporter* reporter) {
     SkPath  path;
 
@@ -33,17 +104,6 @@
     length = meas.getLength();
 //    SkDebugf("circle arc-length = %g\n", length);
 
-    for (int i = 0; i < 8; i++) {
-        SkScalar    d = length * i / 8;
-        SkPoint     p;
-        SkVector    v;
-        meas.getPosTan(d, &p, &v);
-#if 0
-        SkDebugf("circle arc-length=%g, pos[%g %g] tan[%g %g]\n",
-                 d, p.fX, p.fY, v.fX, v.fY);
-#endif
-    }
-
     // Test the behavior following a close not followed by a move.
     path.reset();
     path.lineTo(SK_Scalar1, 0);
@@ -61,7 +121,9 @@
     SkVector tangent;
     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fX, -SK_ScalarHalf, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fX,
+                            -SK_ScalarHalf,
+                            SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter, position.fY == 0);
     REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
     REPORTER_ASSERT(reporter, tangent.fY == 0);
@@ -84,22 +146,28 @@
     REPORTER_ASSERT(reporter, length == SK_Scalar1 * 6);
     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fX, SK_ScalarHalf, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fX,
+                            SK_ScalarHalf,
+                            SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter, position.fY == 0);
     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
     REPORTER_ASSERT(reporter, tangent.fY == 0);
-    REPORTER_ASSERT(reporter, meas.getPosTan(SK_Scalar1 * 2.5f, &position, &tangent));
+    REPORTER_ASSERT(reporter, meas.getPosTan(SkFloatToScalar(2.5f), &position, &tangent));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fX, SK_Scalar1, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fX, SK_Scalar1, SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fY, SK_Scalar1 * 1.5f));
+        SkScalarNearlyEqual(position.fY, SkFloatToScalar(1.5f)));
     REPORTER_ASSERT(reporter, tangent.fX == 0);
     REPORTER_ASSERT(reporter, tangent.fY == SK_Scalar1);
-    REPORTER_ASSERT(reporter, meas.getPosTan(SK_Scalar1 * 4.5f, &position, &tangent));
+    REPORTER_ASSERT(reporter, meas.getPosTan(SkFloatToScalar(4.5f), &position, &tangent));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fX, SK_Scalar1 * 2.5f, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fX,
+                            SkFloatToScalar(2.5f),
+                            SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fY, SK_Scalar1 * 2.0f, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fY,
+                            SkFloatToScalar(2.0f),
+                            SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
     REPORTER_ASSERT(reporter, tangent.fY == 0);
 
@@ -114,7 +182,9 @@
     REPORTER_ASSERT(reporter, length == SK_Scalar1);
     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fX, SK_ScalarHalf, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fX,
+                            SK_ScalarHalf,
+                            SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter, position.fY == 0);
     REPORTER_ASSERT(reporter, tangent.fX == SK_Scalar1);
     REPORTER_ASSERT(reporter, tangent.fY == 0);
@@ -123,11 +193,19 @@
     REPORTER_ASSERT(reporter, length == SK_Scalar1);
     REPORTER_ASSERT(reporter, meas.getPosTan(SK_ScalarHalf, &position, &tangent));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fX, SK_Scalar1 * 1.5f, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fX,
+                            SkFloatToScalar(1.5f),
+                            SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter,
-        SkScalarNearlyEqual(position.fY, SK_Scalar1 * 2.0f, SK_Scalar1 * 0.0001));
+        SkScalarNearlyEqual(position.fY,
+                            SkFloatToScalar(2.0f),
+                            SkFloatToScalar(0.0001f)));
     REPORTER_ASSERT(reporter, tangent.fX == -SK_Scalar1);
     REPORTER_ASSERT(reporter, tangent.fY == 0);
+
+    test_small_segment(reporter);
+    test_small_segment2(reporter);
+    test_small_segment3(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index 868ce31..d4442cb 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -6,28 +6,509 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
+#include "SkCanvas.h"
 #include "SkPaint.h"
 #include "SkPath.h"
 #include "SkParse.h"
 #include "SkParsePath.h"
+#include "SkPathEffect.h"
 #include "SkRandom.h"
 #include "SkReader32.h"
 #include "SkSize.h"
 #include "SkWriter32.h"
+#include "SkSurface.h"
 
-/**
- * cheapIsDirection can take a shortcut when a path is marked convex.
- * This function ensures that we always test cheapIsDirection when the path
- * is flagged with unknown convexity status.
- */
-static void check_direction(SkPath* path,
-                            SkPath::Direction expectedDir,
-                            skiatest::Reporter* reporter) {
-    if (SkPath::kConvex_Convexity == path->getConvexity()) {
-        REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
-        path->setConvexity(SkPath::kUnknown_Convexity);
+#if defined(WIN32)
+    #define SUPPRESS_VISIBILITY_WARNING
+#else
+    #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
+#endif
+
+static SkSurface* new_surface(int w, int h) {
+    SkImage::Info info = {
+        w, h, SkImage::kPMColor_ColorType, SkImage::kPremul_AlphaType
+    };
+    return SkSurface::NewRaster(info);
+}
+
+static void build_path_170666(SkPath& path) {
+    path.moveTo(17.9459f, 21.6344f);
+    path.lineTo(139.545f, -47.8105f);
+    path.lineTo(139.545f, -47.8105f);
+    path.lineTo(131.07f, -47.3888f);
+    path.lineTo(131.07f, -47.3888f);
+    path.lineTo(122.586f, -46.9532f);
+    path.lineTo(122.586f, -46.9532f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(18085.1f, 31390.5f);
+    path.lineTo(18085.1f, 31390.5f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(18076.6f, 31390.9f);
+    path.lineTo(17955, 31460.3f);
+    path.lineTo(17955, 31460.3f);
+    path.lineTo(17963.5f, 31459.9f);
+    path.lineTo(17963.5f, 31459.9f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(17.9551f, 21.6205f);
+    path.lineTo(17.9551f, 21.6205f);
+    path.lineTo(9.47091f, 22.0561f);
+    path.lineTo(9.47091f, 22.0561f);
+    path.lineTo(17.9459f, 21.6344f);
+    path.lineTo(17.9459f, 21.6344f);
+    path.close();path.moveTo(0.995934f, 22.4779f);
+    path.lineTo(0.986725f, 22.4918f);
+    path.lineTo(0.986725f, 22.4918f);
+    path.lineTo(17955, 31460.4f);
+    path.lineTo(17955, 31460.4f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(17971.9f, 31459.5f);
+    path.lineTo(18093.6f, 31390.1f);
+    path.lineTo(18093.6f, 31390.1f);
+    path.lineTo(18093.6f, 31390);
+    path.lineTo(18093.6f, 31390);
+    path.lineTo(139.555f, -47.8244f);
+    path.lineTo(139.555f, -47.8244f);
+    path.lineTo(122.595f, -46.9671f);
+    path.lineTo(122.595f, -46.9671f);
+    path.lineTo(0.995934f, 22.4779f);
+    path.lineTo(0.995934f, 22.4779f);
+    path.close();
+    path.moveTo(5.43941f, 25.5223f);
+    path.lineTo(798267, -28871.1f);
+    path.lineTo(798267, -28871.1f);
+    path.lineTo(3.12512e+06f, -113102);
+    path.lineTo(3.12512e+06f, -113102);
+    path.cubicTo(5.16324e+06f, -186882, 8.15247e+06f, -295092, 1.1957e+07f, -432813);
+    path.cubicTo(1.95659e+07f, -708257, 3.04359e+07f, -1.10175e+06f, 4.34798e+07f, -1.57394e+06f);
+    path.cubicTo(6.95677e+07f, -2.51831e+06f, 1.04352e+08f, -3.77748e+06f, 1.39135e+08f, -5.03666e+06f);
+    path.cubicTo(1.73919e+08f, -6.29583e+06f, 2.08703e+08f, -7.555e+06f, 2.34791e+08f, -8.49938e+06f);
+    path.cubicTo(2.47835e+08f, -8.97157e+06f, 2.58705e+08f, -9.36506e+06f, 2.66314e+08f, -9.6405e+06f);
+    path.cubicTo(2.70118e+08f, -9.77823e+06f, 2.73108e+08f, -9.88644e+06f, 2.75146e+08f, -9.96022e+06f);
+    path.cubicTo(2.76165e+08f, -9.99711e+06f, 2.76946e+08f, -1.00254e+07f, 2.77473e+08f, -1.00444e+07f);
+    path.lineTo(2.78271e+08f, -1.00733e+07f);
+    path.lineTo(2.78271e+08f, -1.00733e+07f);
+    path.cubicTo(2.78271e+08f, -1.00733e+07f, 2.08703e+08f, -7.555e+06f, 135.238f, 23.3517f);
+    path.cubicTo(131.191f, 23.4981f, 125.995f, 23.7976f, 123.631f, 24.0206f);
+    path.cubicTo(121.267f, 24.2436f, 122.631f, 24.3056f, 126.677f, 24.1591f);
+    path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
+    path.lineTo(2.77473e+08f, -1.00444e+07f);
+    path.lineTo(2.77473e+08f, -1.00444e+07f);
+    path.cubicTo(2.76946e+08f, -1.00254e+07f, 2.76165e+08f, -9.99711e+06f, 2.75146e+08f, -9.96022e+06f);
+    path.cubicTo(2.73108e+08f, -9.88644e+06f, 2.70118e+08f, -9.77823e+06f, 2.66314e+08f, -9.6405e+06f);
+    path.cubicTo(2.58705e+08f, -9.36506e+06f, 2.47835e+08f, -8.97157e+06f, 2.34791e+08f, -8.49938e+06f);
+    path.cubicTo(2.08703e+08f, -7.555e+06f, 1.73919e+08f, -6.29583e+06f, 1.39135e+08f, -5.03666e+06f);
+    path.cubicTo(1.04352e+08f, -3.77749e+06f, 6.95677e+07f, -2.51831e+06f, 4.34798e+07f, -1.57394e+06f);
+    path.cubicTo(3.04359e+07f, -1.10175e+06f, 1.95659e+07f, -708258, 1.1957e+07f, -432814);
+    path.cubicTo(8.15248e+06f, -295092, 5.16324e+06f, -186883, 3.12513e+06f, -113103);
+    path.lineTo(798284, -28872);
+    path.lineTo(798284, -28872);
+    path.lineTo(22.4044f, 24.6677f);
+    path.lineTo(22.4044f, 24.6677f);
+    path.cubicTo(22.5186f, 24.5432f, 18.8134f, 24.6337f, 14.1287f, 24.8697f);
+    path.cubicTo(9.4439f, 25.1057f, 5.55359f, 25.3978f, 5.43941f, 25.5223f);
+    path.close();
+}
+
+static void build_path_simple_170666(SkPath& path) {
+    path.moveTo(126.677f, 24.1591f);
+    path.cubicTo(2.08703e+08f, -7.555e+06f, 2.78271e+08f, -1.00733e+07f, 2.78271e+08f, -1.00733e+07f);
+}
+
+// This used to assert in the SK_DEBUG build, as the clip step would fail with
+// too-few interations in our cubic-line intersection code. That code now runs
+// 24 interations (instead of 16).
+static void test_crbug_170666(skiatest::Reporter* reporter) {
+    SkPath path;
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkAutoTUnref<SkSurface> surface(new_surface(1000, 1000));
+
+    build_path_simple_170666(path);
+    surface->getCanvas()->drawPath(path, paint);
+
+    build_path_170666(path);
+    surface->getCanvas()->drawPath(path, paint);
+}
+
+// Make sure we stay non-finite once we get there (unless we reset or rewind).
+static void test_addrect_isfinite(skiatest::Reporter* reporter) {
+    SkPath path;
+
+    path.addRect(SkRect::MakeWH(50, 100));
+    REPORTER_ASSERT(reporter, path.isFinite());
+
+    path.moveTo(0, 0);
+    path.lineTo(SK_ScalarInfinity, 42);
+    REPORTER_ASSERT(reporter, !path.isFinite());
+
+    path.addRect(SkRect::MakeWH(50, 100));
+    REPORTER_ASSERT(reporter, !path.isFinite());
+
+    path.reset();
+    REPORTER_ASSERT(reporter, path.isFinite());
+
+    path.addRect(SkRect::MakeWH(50, 100));
+    REPORTER_ASSERT(reporter, path.isFinite());
+}
+
+static void build_big_path(SkPath* path, bool reducedCase) {
+    if (reducedCase) {
+        path->moveTo(577330, 1971.72f);
+        path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
+    } else {
+        path->moveTo(60.1631f, 7.70567f);
+        path->quadTo(60.1631f, 7.70567f, 0.99474f, 0.901199f);
+        path->lineTo(577379, 1977.77f);
+        path->quadTo(577364, 1979.57f, 577325, 1980.26f);
+        path->quadTo(577286, 1980.95f, 577245, 1980.13f);
+        path->quadTo(577205, 1979.3f, 577187, 1977.45f);
+        path->quadTo(577168, 1975.6f, 577183, 1973.8f);
+        path->quadTo(577198, 1972, 577238, 1971.31f);
+        path->quadTo(577277, 1970.62f, 577317, 1971.45f);
+        path->quadTo(577330, 1971.72f, 577341, 1972.11f);
+        path->cubicTo(10.7082f, -116.596f, 262.057f, 45.6468f, 294.694f, 1.96237f);
+        path->moveTo(306.718f, -32.912f);
+        path->cubicTo(30.531f, 10.0005f, 1502.47f, 13.2804f, 84.3088f, 9.99601f);
     }
-    REPORTER_ASSERT(reporter, path->cheapIsDirection(expectedDir));
+}
+
+static void test_clipped_cubic(skiatest::Reporter* reporter) {
+    SkAutoTUnref<SkSurface> surface(new_surface(640, 480));
+
+    // This path used to assert, because our cubic-chopping code incorrectly
+    // moved control points after the chop. This test should be run in SK_DEBUG
+    // mode to ensure that we no long assert.
+    SkPath path;
+    for (int doReducedCase = 0; doReducedCase <= 1; ++doReducedCase) {
+        build_big_path(&path, SkToBool(doReducedCase));
+
+        SkPaint paint;
+        for (int doAA = 0; doAA <= 1; ++doAA) {
+            paint.setAntiAlias(SkToBool(doAA));
+            surface->getCanvas()->drawPath(path, paint);
+        }
+    }
+}
+
+// Inspired by http://ie.microsoft.com/testdrive/Performance/Chalkboard/
+// which triggered an assert, from a tricky cubic. This test replicates that
+// example, so we can ensure that we handle it (in SkEdge.cpp), and don't
+// assert in the SK_DEBUG build.
+static void test_tricky_cubic(skiatest::Reporter* reporter) {
+    const SkPoint pts[] = {
+        { SkDoubleToScalar(18.8943768),    SkDoubleToScalar(129.121277) },
+        { SkDoubleToScalar(18.8937435),    SkDoubleToScalar(129.121689) },
+        { SkDoubleToScalar(18.8950119),    SkDoubleToScalar(129.120422) },
+        { SkDoubleToScalar(18.5030727),    SkDoubleToScalar(129.13121)  },
+    };
+
+    SkPath path;
+    path.moveTo(pts[0]);
+    path.cubicTo(pts[1], pts[2], pts[3]);
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+
+    SkSurface* surface = new_surface(19, 130);
+    surface->getCanvas()->drawPath(path, paint);
+    surface->unref();
+}
+
+// Inspired by http://code.google.com/p/chromium/issues/detail?id=141651
+//
+static void test_isfinite_after_transform(skiatest::Reporter* reporter) {
+    SkPath path;
+    path.quadTo(157, 366, 286, 208);
+    path.arcTo(37, 442, 315, 163, 957494590897113.0f);
+
+    SkMatrix matrix;
+    matrix.setScale(1000*1000, 1000*1000);
+
+    // Be sure that path::transform correctly updates isFinite and the bounds
+    // if the transformation overflows. The previous bug was that isFinite was
+    // set to true in this case, but the bounds were not set to empty (which
+    // they should be).
+    while (path.isFinite()) {
+        REPORTER_ASSERT(reporter, path.getBounds().isFinite());
+        REPORTER_ASSERT(reporter, !path.getBounds().isEmpty());
+        path.transform(matrix);
+    }
+    REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
+
+    matrix.setTranslate(SK_Scalar1, SK_Scalar1);
+    path.transform(matrix);
+    // we need to still be non-finite
+    REPORTER_ASSERT(reporter, !path.isFinite());
+    REPORTER_ASSERT(reporter, path.getBounds().isEmpty());
+}
+
+static void add_corner_arc(SkPath* path, const SkRect& rect,
+                           SkScalar xIn, SkScalar yIn,
+                           int startAngle)
+{
+
+    SkScalar rx = SkMinScalar(rect.width(), xIn);
+    SkScalar ry = SkMinScalar(rect.height(), yIn);
+
+    SkRect arcRect;
+    arcRect.set(-rx, -ry, rx, ry);
+    switch (startAngle) {
+    case 0:
+        arcRect.offset(rect.fRight - arcRect.fRight, rect.fBottom - arcRect.fBottom);
+        break;
+    case 90:
+        arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fBottom - arcRect.fBottom);
+        break;
+    case 180:
+        arcRect.offset(rect.fLeft - arcRect.fLeft, rect.fTop - arcRect.fTop);
+        break;
+    case 270:
+        arcRect.offset(rect.fRight - arcRect.fRight, rect.fTop - arcRect.fTop);
+        break;
+    default:
+        break;
+    }
+
+    path->arcTo(arcRect, SkIntToScalar(startAngle), SkIntToScalar(90), false);
+}
+
+static void make_arb_round_rect(SkPath* path, const SkRect& r,
+                                SkScalar xCorner, SkScalar yCorner) {
+    // we are lazy here and use the same x & y for each corner
+    add_corner_arc(path, r, xCorner, yCorner, 270);
+    add_corner_arc(path, r, xCorner, yCorner, 0);
+    add_corner_arc(path, r, xCorner, yCorner, 90);
+    add_corner_arc(path, r, xCorner, yCorner, 180);
+    path->close();
+}
+
+// Chrome creates its own round rects with each corner possibly being different.
+// Performance will suffer if they are not convex.
+// Note: PathBench::ArbRoundRectBench performs almost exactly
+// the same test (but with drawing)
+static void test_arb_round_rect_is_convex(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    SkRect r;
+
+    for (int i = 0; i < 5000; ++i) {
+
+        SkScalar size = rand.nextUScalar1() * 30;
+        if (size < SK_Scalar1) {
+            continue;
+        }
+        r.fLeft = rand.nextUScalar1() * 300;
+        r.fTop =  rand.nextUScalar1() * 300;
+        r.fRight =  r.fLeft + 2 * size;
+        r.fBottom = r.fTop + 2 * size;
+
+        SkPath temp;
+
+        make_arb_round_rect(&temp, r, r.width() / 10, r.height() / 15);
+
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
+        REPORTER_ASSERT(reporter, temp.isConvex());
+#endif
+    }
+}
+
+// Chrome will sometimes create a 0 radius round rect. The degenerate
+// quads prevent the path from being converted to a rect
+// Note: PathBench::ArbRoundRectBench performs almost exactly
+// the same test (but with drawing)
+static void test_arb_zero_rad_round_rect_is_rect(skiatest::Reporter* reporter) {
+    SkRandom rand;
+    SkRect r;
+
+    for (int i = 0; i < 5000; ++i) {
+
+        SkScalar size = rand.nextUScalar1() * 30;
+        if (size < SK_Scalar1) {
+            continue;
+        }
+        r.fLeft = rand.nextUScalar1() * 300;
+        r.fTop =  rand.nextUScalar1() * 300;
+        r.fRight =  r.fLeft + 2 * size;
+        r.fBottom = r.fTop + 2 * size;
+
+        SkPath temp;
+
+        make_arb_round_rect(&temp, r, 0, 0);
+
+#ifndef SK_IGNORE_CONVEX_QUAD_OPT
+        SkRect result;
+        REPORTER_ASSERT(reporter, temp.isRect(&result));
+        REPORTER_ASSERT(reporter, r == result);
+#endif
+    }
+}
+
+static void test_rect_isfinite(skiatest::Reporter* reporter) {
+    const SkScalar inf = SK_ScalarInfinity;
+    const SkScalar nan = SK_ScalarNaN;
+
+    SkRect r;
+    r.setEmpty();
+    REPORTER_ASSERT(reporter, r.isFinite());
+    r.set(0, 0, inf, -inf);
+    REPORTER_ASSERT(reporter, !r.isFinite());
+    r.set(0, 0, nan, 0);
+    REPORTER_ASSERT(reporter, !r.isFinite());
+
+    SkPoint pts[] = {
+        { 0, 0 },
+        { SK_Scalar1, 0 },
+        { 0, SK_Scalar1 },
+    };
+
+    bool isFine = r.setBoundsCheck(pts, 3);
+    REPORTER_ASSERT(reporter, isFine);
+    REPORTER_ASSERT(reporter, !r.isEmpty());
+
+    pts[1].set(inf, 0);
+    isFine = r.setBoundsCheck(pts, 3);
+    REPORTER_ASSERT(reporter, !isFine);
+    REPORTER_ASSERT(reporter, r.isEmpty());
+
+    pts[1].set(nan, 0);
+    isFine = r.setBoundsCheck(pts, 3);
+    REPORTER_ASSERT(reporter, !isFine);
+    REPORTER_ASSERT(reporter, r.isEmpty());
+}
+
+static void test_path_isfinite(skiatest::Reporter* reporter) {
+    const SkScalar inf = SK_ScalarInfinity;
+    const SkScalar negInf = SK_ScalarNegativeInfinity;
+    const SkScalar nan = SK_ScalarNaN;
+
+    SkPath path;
+    REPORTER_ASSERT(reporter, path.isFinite());
+
+    path.reset();
+    REPORTER_ASSERT(reporter, path.isFinite());
+
+    path.reset();
+    path.moveTo(SK_Scalar1, 0);
+    REPORTER_ASSERT(reporter, path.isFinite());
+
+    path.reset();
+    path.moveTo(inf, negInf);
+    REPORTER_ASSERT(reporter, !path.isFinite());
+
+    path.reset();
+    path.moveTo(nan, 0);
+    REPORTER_ASSERT(reporter, !path.isFinite());
+}
+
+static void test_isfinite(skiatest::Reporter* reporter) {
+    test_rect_isfinite(reporter);
+    test_path_isfinite(reporter);
+}
+
+// assert that we always
+//  start with a moveTo
+//  only have 1 moveTo
+//  only have Lines after that
+//  end with a single close
+//  only have (at most) 1 close
+//
+static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
+                      const SkPoint srcPts[], int count, bool expectClose) {
+    SkPath::RawIter iter(path);
+    SkPoint         pts[4];
+
+    bool firstTime = true;
+    bool foundClose = false;
+    for (;;) {
+        switch (iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                REPORTER_ASSERT(reporter, firstTime);
+                REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
+                srcPts++;
+                firstTime = false;
+                break;
+            case SkPath::kLine_Verb:
+                REPORTER_ASSERT(reporter, !firstTime);
+                REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
+                srcPts++;
+                break;
+            case SkPath::kQuad_Verb:
+                REPORTER_ASSERT(reporter, !"unexpected quad verb");
+                break;
+            case SkPath::kCubic_Verb:
+                REPORTER_ASSERT(reporter, !"unexpected cubic verb");
+                break;
+            case SkPath::kClose_Verb:
+                REPORTER_ASSERT(reporter, !firstTime);
+                REPORTER_ASSERT(reporter, !foundClose);
+                REPORTER_ASSERT(reporter, expectClose);
+                foundClose = true;
+                break;
+            case SkPath::kDone_Verb:
+                goto DONE;
+        }
+    }
+DONE:
+    REPORTER_ASSERT(reporter, foundClose == expectClose);
+}
+
+static void test_addPoly(skiatest::Reporter* reporter) {
+    SkPoint pts[32];
+    SkRandom rand;
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
+        pts[i].fX = rand.nextSScalar1();
+        pts[i].fY = rand.nextSScalar1();
+    }
+
+    for (int doClose = 0; doClose <= 1; ++doClose) {
+        for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
+            SkPath path;
+            path.addPoly(pts, count, SkToBool(doClose));
+            test_poly(reporter, path, pts, count, SkToBool(doClose));
+        }
+    }
+}
+
+static void test_strokerec(skiatest::Reporter* reporter) {
+    SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
+    REPORTER_ASSERT(reporter, rec.isFillStyle());
+
+    rec.setHairlineStyle();
+    REPORTER_ASSERT(reporter, rec.isHairlineStyle());
+
+    rec.setStrokeStyle(SK_Scalar1, false);
+    REPORTER_ASSERT(reporter, SkStrokeRec::kStroke_Style == rec.getStyle());
+
+    rec.setStrokeStyle(SK_Scalar1, true);
+    REPORTER_ASSERT(reporter, SkStrokeRec::kStrokeAndFill_Style == rec.getStyle());
+
+    rec.setStrokeStyle(0, false);
+    REPORTER_ASSERT(reporter, SkStrokeRec::kHairline_Style == rec.getStyle());
+
+    rec.setStrokeStyle(0, true);
+    REPORTER_ASSERT(reporter, SkStrokeRec::kFill_Style == rec.getStyle());
+}
+
+// Set this for paths that don't have a consistent direction such as a bowtie.
+// (cheapComputeDirection is not expected to catch these.)
+static const SkPath::Direction kDontCheckDir = static_cast<SkPath::Direction>(-1);
+
+static void check_direction(skiatest::Reporter* reporter, const SkPath& path,
+                            SkPath::Direction expected) {
+    if (expected == kDontCheckDir) {
+        return;
+    }
+    SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
+
+    SkPath::Direction dir;
+    if (copy.cheapComputeDirection(&dir)) {
+        REPORTER_ASSERT(reporter, dir == expected);
+    } else {
+        REPORTER_ASSERT(reporter, SkPath::kUnknown_Direction == expected);
+    }
 }
 
 static void test_direction(skiatest::Reporter* reporter) {
@@ -36,6 +517,7 @@
     REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
     REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCW_Direction));
     REPORTER_ASSERT(reporter, !path.cheapIsDirection(SkPath::kCCW_Direction));
+    REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kUnknown_Direction));
 
     static const char* gDegen[] = {
         "M 10 10",
@@ -51,29 +533,37 @@
         REPORTER_ASSERT(reporter, valid);
         REPORTER_ASSERT(reporter, !path.cheapComputeDirection(NULL));
     }
-    
+
     static const char* gCW[] = {
         "M 10 10 L 10 10 Q 20 10 20 20",
         "M 10 10 C 20 10 20 20 20 20",
         "M 20 10 Q 20 20 30 20 L 10 20", // test double-back at y-max
+        // rect with top two corners replaced by cubics with identical middle
+        // control points
+        "M 10 10 C 10 0 10 0 20 0 L 40 0 C 50 0 50 0 50 10",
+        "M 20 10 L 0 10 Q 10 10 20 0",  // left, degenerate serif
     };
     for (i = 0; i < SK_ARRAY_COUNT(gCW); ++i) {
         path.reset();
         bool valid = SkParsePath::FromSVGString(gCW[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        check_direction(&path, SkPath::kCW_Direction, reporter);
+        check_direction(reporter, path, SkPath::kCW_Direction);
     }
-    
+
     static const char* gCCW[] = {
         "M 10 10 L 10 10 Q 20 10 20 -20",
         "M 10 10 C 20 10 20 -20 20 -20",
         "M 20 10 Q 20 20 10 20 L 30 20", // test double-back at y-max
+        // rect with top two corners replaced by cubics with identical middle
+        // control points
+        "M 50 10 C 50 0 50 0 40 0 L 20 0 C 10 0 10 0 10 10",
+        "M 10 10 L 30 10 Q 20 10 10 0",  // right, degenerate serif
     };
     for (i = 0; i < SK_ARRAY_COUNT(gCCW); ++i) {
         path.reset();
         bool valid = SkParsePath::FromSVGString(gCCW[i], &path);
         REPORTER_ASSERT(reporter, valid);
-        check_direction(&path, SkPath::kCCW_Direction, reporter);
+        check_direction(reporter, path, SkPath::kCCW_Direction);
     }
 
     // Test two donuts, each wound a different direction. Only the outer contour
@@ -81,12 +571,12 @@
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCCW_Direction);
-    check_direction(&path, SkPath::kCW_Direction, reporter);
+    check_direction(reporter, path, SkPath::kCW_Direction);
 
     path.reset();
     path.addCircle(0, 0, SkIntToScalar(1), SkPath::kCW_Direction);
     path.addCircle(0, 0, SkIntToScalar(2), SkPath::kCCW_Direction);
-    check_direction(&path, SkPath::kCCW_Direction, reporter);
+    check_direction(reporter, path, SkPath::kCCW_Direction);
 
 #ifdef SK_SCALAR_IS_FLOAT
     // triangle with one point really far from the origin.
@@ -95,7 +585,7 @@
     path.moveTo(SkFloatToScalar(SkBits2Float(0x501c7652)), SkFloatToScalar(SkBits2Float(0x501c7652)));
     path.lineTo(110 * SK_Scalar1, -10 * SK_Scalar1);
     path.lineTo(-10 * SK_Scalar1, 60 * SK_Scalar1);
-    check_direction(&path, SkPath::kCCW_Direction, reporter);
+    check_direction(reporter, path, SkPath::kCCW_Direction);
 #endif
 }
 
@@ -128,11 +618,11 @@
     SkPath path;
     path.moveTo(pts[0]);
     path.cubicTo(pts[1], pts[2], pts[3]);
-    
+
     SkPaint paint;
     paint.setStyle(SkPaint::kStroke_Style);
     paint.setStrokeWidth(SK_Scalar1 * 2);
-    
+
     SkPath fill;
     paint.getFillPath(path, &fill);
 }
@@ -147,22 +637,22 @@
         { 372.0f,   92.0f },
         { 372.0f,   92.0f },
     };
-    
+
     stroke_cubic(p0);
-    
+
     SkPoint p1[] = {
         { 372.0f,       92.0f },
         { 372.0007f,    92.000755f },
         { 371.99927f,   92.003922f },
         { 371.99826f,   92.003899f },
     };
-    
+
     stroke_cubic(p1);
 }
 
 static void check_close(skiatest::Reporter* reporter, const SkPath& path) {
     for (int i = 0; i < 2; ++i) {
-        SkPath::Iter iter(path, (bool)i);
+        SkPath::Iter iter(path, SkToBool(i));
         SkPoint mv;
         SkPoint pts[4];
         SkPath::Verb v;
@@ -217,7 +707,7 @@
     check_close(reporter, quad);
 
     SkPath cubic;
-    quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1, 
+    quad.cubicTo(SK_Scalar1, SK_Scalar1, 10 * SK_Scalar1,
                  10*SK_Scalar1, 20 * SK_Scalar1, 20*SK_Scalar1);
     check_close(reporter, cubic);
     cubic.close();
@@ -258,7 +748,8 @@
 
 static void check_convexity(skiatest::Reporter* reporter, const SkPath& path,
                             SkPath::Convexity expected) {
-    SkPath::Convexity c = SkPath::ComputeConvexity(path);
+    SkPath copy(path); // we make a copy so that we don't cache the result on the passed in path.
+    SkPath::Convexity c = copy.getConvexity();
     REPORTER_ASSERT(reporter, c == expected);
 }
 
@@ -267,27 +758,31 @@
     pt.moveTo(0, 0);
     pt.close();
     check_convexity(reporter, pt, SkPath::kConvex_Convexity);
-    
+    check_direction(reporter, pt, SkPath::kUnknown_Direction);
+
     SkPath line;
     line.moveTo(12*SK_Scalar1, 20*SK_Scalar1);
     line.lineTo(-12*SK_Scalar1, -20*SK_Scalar1);
     line.close();
-    check_convexity(reporter, pt, SkPath::kConvex_Convexity);
-    
+    check_convexity(reporter, line, SkPath::kConvex_Convexity);
+    check_direction(reporter, line, SkPath::kUnknown_Direction);
+
     SkPath triLeft;
     triLeft.moveTo(0, 0);
     triLeft.lineTo(SK_Scalar1, 0);
     triLeft.lineTo(SK_Scalar1, SK_Scalar1);
     triLeft.close();
     check_convexity(reporter, triLeft, SkPath::kConvex_Convexity);
-    
+    check_direction(reporter, triLeft, SkPath::kCW_Direction);
+
     SkPath triRight;
     triRight.moveTo(0, 0);
     triRight.lineTo(-SK_Scalar1, 0);
     triRight.lineTo(SK_Scalar1, SK_Scalar1);
     triRight.close();
     check_convexity(reporter, triRight, SkPath::kConvex_Convexity);
-    
+    check_direction(reporter, triRight, SkPath::kCCW_Direction);
+
     SkPath square;
     square.moveTo(0, 0);
     square.lineTo(SK_Scalar1, 0);
@@ -295,7 +790,8 @@
     square.lineTo(0, SK_Scalar1);
     square.close();
     check_convexity(reporter, square, SkPath::kConvex_Convexity);
-    
+    check_direction(reporter, square, SkPath::kCW_Direction);
+
     SkPath redundantSquare;
     redundantSquare.moveTo(0, 0);
     redundantSquare.lineTo(0, 0);
@@ -311,7 +807,8 @@
     redundantSquare.lineTo(0, SK_Scalar1);
     redundantSquare.close();
     check_convexity(reporter, redundantSquare, SkPath::kConvex_Convexity);
-    
+    check_direction(reporter, redundantSquare, SkPath::kCW_Direction);
+
     SkPath bowTie;
     bowTie.moveTo(0, 0);
     bowTie.lineTo(0, 0);
@@ -327,7 +824,8 @@
     bowTie.lineTo(0, SK_Scalar1);
     bowTie.close();
     check_convexity(reporter, bowTie, SkPath::kConcave_Convexity);
-    
+    check_direction(reporter, bowTie, kDontCheckDir);
+
     SkPath spiral;
     spiral.moveTo(0, 0);
     spiral.lineTo(100*SK_Scalar1, 0);
@@ -338,7 +836,8 @@
     spiral.lineTo(50*SK_Scalar1, 75*SK_Scalar1);
     spiral.close();
     check_convexity(reporter, spiral, SkPath::kConcave_Convexity);
-    
+    check_direction(reporter, spiral, kDontCheckDir);
+
     SkPath dent;
     dent.moveTo(0, 0);
     dent.lineTo(100*SK_Scalar1, 100*SK_Scalar1);
@@ -347,6 +846,7 @@
     dent.lineTo(-200*SK_Scalar1, 100*SK_Scalar1);
     dent.close();
     check_convexity(reporter, dent, SkPath::kConcave_Convexity);
+    check_direction(reporter, dent, SkPath::kCW_Direction);
 }
 
 static void check_convex_bounds(skiatest::Reporter* reporter, const SkPath& p,
@@ -384,47 +884,249 @@
 }
 
 static void test_convexity(skiatest::Reporter* reporter) {
-    static const SkPath::Convexity C = SkPath::kConcave_Convexity;
-    static const SkPath::Convexity V = SkPath::kConvex_Convexity;
-
     SkPath path;
 
-    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+    check_convexity(reporter, path, SkPath::kConvex_Convexity);
     path.addCircle(0, 0, SkIntToScalar(10));
-    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+    check_convexity(reporter, path, SkPath::kConvex_Convexity);
     path.addCircle(0, 0, SkIntToScalar(10));   // 2nd circle
-    REPORTER_ASSERT(reporter, C == SkPath::ComputeConvexity(path));
+    check_convexity(reporter, path, SkPath::kConcave_Convexity);
+
     path.reset();
     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCCW_Direction);
-    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+    check_convexity(reporter, path, SkPath::kConvex_Convexity);
     REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCCW_Direction));
+
     path.reset();
     path.addRect(0, 0, SkIntToScalar(10), SkIntToScalar(10), SkPath::kCW_Direction);
-    REPORTER_ASSERT(reporter, V == SkPath::ComputeConvexity(path));
+    check_convexity(reporter, path, SkPath::kConvex_Convexity);
     REPORTER_ASSERT(reporter, path.cheapIsDirection(SkPath::kCW_Direction));
-    
+
     static const struct {
         const char*         fPathStr;
         SkPath::Convexity   fExpectedConvexity;
+        SkPath::Direction   fExpectedDirection;
     } gRec[] = {
-        { "", SkPath::kConvex_Convexity },
-        { "0 0", SkPath::kConvex_Convexity },
-        { "0 0 10 10", SkPath::kConvex_Convexity },
-        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity },
-        { "0 0 10 10 10 20", SkPath::kConvex_Convexity },
-        { "0 0 10 10 10 0", SkPath::kConvex_Convexity },
-        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity },
-        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity },
+        { "", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
+        { "0 0", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
+        { "0 0 10 10", SkPath::kConvex_Convexity, SkPath::kUnknown_Direction },
+        { "0 0 10 10 20 20 0 0 10 10", SkPath::kConcave_Convexity, SkPath::kUnknown_Direction },
+        { "0 0 10 10 10 20", SkPath::kConvex_Convexity, SkPath::kCW_Direction },
+        { "0 0 10 10 10 0", SkPath::kConvex_Convexity, SkPath::kCCW_Direction },
+        { "0 0 10 10 10 0 0 10", SkPath::kConcave_Convexity, kDontCheckDir },
+        { "0 0 10 0 0 10 -10 -10", SkPath::kConcave_Convexity, SkPath::kCW_Direction },
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
         SkPath path;
         setFromString(&path, gRec[i].fPathStr);
-        SkPath::Convexity c = SkPath::ComputeConvexity(path);
-        REPORTER_ASSERT(reporter, c == gRec[i].fExpectedConvexity);
+        check_convexity(reporter, path, gRec[i].fExpectedConvexity);
+        check_direction(reporter, path, gRec[i].fExpectedDirection);
     }
 }
 
+static void test_isLine(skiatest::Reporter* reporter) {
+    SkPath path;
+    SkPoint pts[2];
+    const SkScalar value = SkIntToScalar(5);
+
+    REPORTER_ASSERT(reporter, !path.isLine(NULL));
+
+    // set some non-zero values
+    pts[0].set(value, value);
+    pts[1].set(value, value);
+    REPORTER_ASSERT(reporter, !path.isLine(pts));
+    // check that pts was untouched
+    REPORTER_ASSERT(reporter, pts[0].equals(value, value));
+    REPORTER_ASSERT(reporter, pts[1].equals(value, value));
+
+    const SkScalar moveX = SkIntToScalar(1);
+    const SkScalar moveY = SkIntToScalar(2);
+    SkASSERT(value != moveX && value != moveY);
+
+    path.moveTo(moveX, moveY);
+    REPORTER_ASSERT(reporter, !path.isLine(NULL));
+    REPORTER_ASSERT(reporter, !path.isLine(pts));
+    // check that pts was untouched
+    REPORTER_ASSERT(reporter, pts[0].equals(value, value));
+    REPORTER_ASSERT(reporter, pts[1].equals(value, value));
+
+    const SkScalar lineX = SkIntToScalar(2);
+    const SkScalar lineY = SkIntToScalar(2);
+    SkASSERT(value != lineX && value != lineY);
+
+    path.lineTo(lineX, lineY);
+    REPORTER_ASSERT(reporter, path.isLine(NULL));
+
+    REPORTER_ASSERT(reporter, !pts[0].equals(moveX, moveY));
+    REPORTER_ASSERT(reporter, !pts[1].equals(lineX, lineY));
+    REPORTER_ASSERT(reporter, path.isLine(pts));
+    REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
+    REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
+
+    path.lineTo(0, 0);  // too many points/verbs
+    REPORTER_ASSERT(reporter, !path.isLine(NULL));
+    REPORTER_ASSERT(reporter, !path.isLine(pts));
+    REPORTER_ASSERT(reporter, pts[0].equals(moveX, moveY));
+    REPORTER_ASSERT(reporter, pts[1].equals(lineX, lineY));
+}
+
+static void test_conservativelyContains(skiatest::Reporter* reporter) {
+    SkPath path;
+
+    // kBaseRect is used to construct most our test paths: a rect, a circle, and a round-rect.
+    static const SkRect kBaseRect = SkRect::MakeWH(SkIntToScalar(100), SkIntToScalar(100));
+
+    // A circle that bounds kBaseRect (with a significant amount of slop)
+    SkScalar circleR = SkMaxScalar(kBaseRect.width(), kBaseRect.height());
+    circleR = SkScalarMul(circleR, SkFloatToScalar(1.75f)) / 2;
+    static const SkPoint kCircleC = {kBaseRect.centerX(), kBaseRect.centerY()};
+
+    // round-rect radii
+    static const SkScalar kRRRadii[] = {SkIntToScalar(5), SkIntToScalar(3)};
+
+    static const struct SUPPRESS_VISIBILITY_WARNING {
+        SkRect fQueryRect;
+        bool   fInRect;
+        bool   fInCircle;
+        bool   fInRR;
+    } kQueries[] = {
+        {kBaseRect, true, true, false},
+
+        // rect well inside of kBaseRect
+        {SkRect::MakeLTRB(kBaseRect.fLeft + SkFloatToScalar(0.25f)*kBaseRect.width(),
+                          kBaseRect.fTop + SkFloatToScalar(0.25f)*kBaseRect.height(),
+                          kBaseRect.fRight - SkFloatToScalar(0.25f)*kBaseRect.width(),
+                          kBaseRect.fBottom - SkFloatToScalar(0.25f)*kBaseRect.height()),
+                          true, true, true},
+
+        // rects with edges off by one from kBaseRect's edges
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
+                          kBaseRect.width(), kBaseRect.height() + 1),
+         false, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
+                          kBaseRect.width() + 1, kBaseRect.height()),
+         false, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop,
+                          kBaseRect.width() + 1, kBaseRect.height() + 1),
+         false, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
+                          kBaseRect.width(), kBaseRect.height()),
+         false, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
+                          kBaseRect.width(), kBaseRect.height()),
+         false, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fLeft - 1, kBaseRect.fTop,
+                          kBaseRect.width() + 2, kBaseRect.height()),
+         false, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop - 1,
+                          kBaseRect.width() + 2, kBaseRect.height()),
+         false, true, false},
+
+        // zero-w/h rects at each corner of kBaseRect
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fTop, 0, 0), true, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fTop, 0, 0), true, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.fBottom, 0, 0), true, true, false},
+        {SkRect::MakeXYWH(kBaseRect.fRight, kBaseRect.fBottom, 0, 0), true, true, false},
+
+        // far away rect
+        {SkRect::MakeXYWH(10 * kBaseRect.fRight, 10 * kBaseRect.fBottom,
+                          SkIntToScalar(10), SkIntToScalar(10)),
+         false, false, false},
+
+        // very large rect containing kBaseRect
+        {SkRect::MakeXYWH(kBaseRect.fLeft - 5 * kBaseRect.width(),
+                          kBaseRect.fTop - 5 * kBaseRect.height(),
+                          11 * kBaseRect.width(), 11 * kBaseRect.height()),
+         false, false, false},
+
+        // skinny rect that spans same y-range as kBaseRect
+        {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
+                          SkIntToScalar(1), kBaseRect.height()),
+         true, true, true},
+
+        // short rect that spans same x-range as kBaseRect
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(), kBaseRect.width(), SkScalar(1)),
+         true, true, true},
+
+        // skinny rect that spans slightly larger y-range than kBaseRect
+        {SkRect::MakeXYWH(kBaseRect.centerX(), kBaseRect.fTop,
+                          SkIntToScalar(1), kBaseRect.height() + 1),
+         false, true, false},
+
+        // short rect that spans slightly larger x-range than kBaseRect
+        {SkRect::MakeXYWH(kBaseRect.fLeft, kBaseRect.centerY(),
+                          kBaseRect.width() + 1, SkScalar(1)),
+         false, true, false},
+    };
+
+    for (int inv = 0; inv < 4; ++inv) {
+        for (size_t q = 0; q < SK_ARRAY_COUNT(kQueries); ++q) {
+            SkRect qRect = kQueries[q].fQueryRect;
+            if (inv & 0x1) {
+                SkTSwap(qRect.fLeft, qRect.fRight);
+            }
+            if (inv & 0x2) {
+                SkTSwap(qRect.fTop, qRect.fBottom);
+            }
+            for (int d = 0; d < 2; ++d) {
+                SkPath::Direction dir = d ? SkPath::kCCW_Direction : SkPath::kCW_Direction;
+                path.reset();
+                path.addRect(kBaseRect, dir);
+                REPORTER_ASSERT(reporter, kQueries[q].fInRect ==
+                                          path.conservativelyContainsRect(qRect));
+
+                path.reset();
+                path.addCircle(kCircleC.fX, kCircleC.fY, circleR, dir);
+                REPORTER_ASSERT(reporter, kQueries[q].fInCircle ==
+                                          path.conservativelyContainsRect(qRect));
+
+                path.reset();
+                path.addRoundRect(kBaseRect, kRRRadii[0], kRRRadii[1], dir);
+                REPORTER_ASSERT(reporter, kQueries[q].fInRR ==
+                                          path.conservativelyContainsRect(qRect));
+            }
+            // Slightly non-convex shape, shouldn't contain any rects.
+            path.reset();
+            path.moveTo(0, 0);
+            path.lineTo(SkIntToScalar(50), SkFloatToScalar(0.05f));
+            path.lineTo(SkIntToScalar(100), 0);
+            path.lineTo(SkIntToScalar(100), SkIntToScalar(100));
+            path.lineTo(0, SkIntToScalar(100));
+            path.close();
+            REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(qRect));
+        }
+    }
+
+    // make sure a minimal convex shape works, a right tri with edges along pos x and y axes.
+    path.reset();
+    path.moveTo(0, 0);
+    path.lineTo(SkIntToScalar(100), 0);
+    path.lineTo(0, SkIntToScalar(100));
+
+    // inside, on along top edge
+    REPORTER_ASSERT(reporter, path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(50), 0,
+                                                                               SkIntToScalar(10),
+                                                                               SkIntToScalar(10))));
+    // above
+    REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(
+        SkRect::MakeXYWH(SkIntToScalar(50),
+                         SkIntToScalar(-10),
+                         SkIntToScalar(10),
+                         SkIntToScalar(10))));
+    // to the left
+    REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(-10),
+                                                                                SkIntToScalar(5),
+                                                                                SkIntToScalar(5),
+                                                                                SkIntToScalar(5))));
+
+    // outside the diagonal edge
+    REPORTER_ASSERT(reporter, !path.conservativelyContainsRect(SkRect::MakeXYWH(SkIntToScalar(10),
+                                                                                SkIntToScalar(200),
+                                                                                SkIntToScalar(20),
+                                                                                SkIntToScalar(5))));
+}
+
 // Simple isRect test is inline TestPath, below.
 // test_isRect provides more extensive testing.
 static void test_isRect(skiatest::Reporter* reporter) {
@@ -445,7 +1147,8 @@
     SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
     SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
     SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
-    
+    SkPoint rf[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}};
+
     // failing tests
     SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
     SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
@@ -455,7 +1158,10 @@
     SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
     SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
     SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
-    
+    SkPoint f9[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 0}, {2, 0}}; // overlaps
+    SkPoint fa[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, -1}, {1, -1}}; // non colinear gap
+    SkPoint fb[] = {{1, 0}, {8, 0}, {8, 8}, {0, 8}, {0, 1}}; // falls short
+
     // failing, no close
     SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
     SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
@@ -463,18 +1169,18 @@
     size_t testLen[] = {
         sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
         sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
-        sizeof(rd), sizeof(re),
+        sizeof(rd), sizeof(re), sizeof(rf),
         sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
-        sizeof(f7), sizeof(f8),
-        sizeof(c1), sizeof(c2) 
+        sizeof(f7), sizeof(f8), sizeof(f9), sizeof(fa), sizeof(fb),
+        sizeof(c1), sizeof(c2)
     };
     SkPoint* tests[] = {
-        r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
-        f1, f2, f3, f4, f5, f6, f7, f8,
-        c1, c2 
+        r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re, rf,
+        f1, f2, f3, f4, f5, f6, f7, f8, f9, fa, fb,
+        c1, c2
     };
-    SkPoint* lastPass = re;
-    SkPoint* lastClose = f8;
+    SkPoint* lastPass = rf;
+    SkPoint* lastClose = fb;
     bool fail = false;
     bool close = true;
     const size_t testCount = sizeof(tests) / sizeof(tests[0]);
@@ -489,6 +1195,34 @@
             path.close();
         }
         REPORTER_ASSERT(reporter, fail ^ path.isRect(0));
+        REPORTER_ASSERT(reporter, fail ^ path.isRect(NULL, NULL));
+
+        if (!fail) {
+            SkRect computed, expected;
+            expected.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
+            REPORTER_ASSERT(reporter, path.isRect(&computed));
+            REPORTER_ASSERT(reporter, expected == computed);
+
+            bool isClosed;
+            SkPath::Direction direction, cheapDirection;
+            REPORTER_ASSERT(reporter, path.cheapComputeDirection(&cheapDirection));
+            REPORTER_ASSERT(reporter, path.isRect(&isClosed, &direction));
+            REPORTER_ASSERT(reporter, isClosed == close);
+            REPORTER_ASSERT(reporter, direction == cheapDirection);
+        } else {
+            SkRect computed;
+            computed.set(123, 456, 789, 1011);
+            REPORTER_ASSERT(reporter, !path.isRect(&computed));
+            REPORTER_ASSERT(reporter, computed.fLeft == 123 && computed.fTop == 456);
+            REPORTER_ASSERT(reporter, computed.fRight == 789 && computed.fBottom == 1011);
+
+            bool isClosed = (bool) -1;
+            SkPath::Direction direction = (SkPath::Direction) -1;
+            REPORTER_ASSERT(reporter, !path.isRect(&isClosed, &direction));
+            REPORTER_ASSERT(reporter, isClosed == (bool) -1);
+            REPORTER_ASSERT(reporter, direction == (SkPath::Direction) -1);
+        }
+
         if (tests[testIndex] == lastPass) {
             fail = true;
         }
@@ -496,7 +1230,7 @@
             close = false;
         }
     }
-    
+
     // fail, close then line
     SkPath path1;
     path1.moveTo(r1[0].fX, r1[0].fY);
@@ -506,7 +1240,7 @@
     path1.close();
     path1.lineTo(1, 0);
     REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
-    
+
     // fail, move in the middle
     path1.reset();
     path1.moveTo(r1[0].fX, r1[0].fY);
@@ -527,7 +1261,7 @@
     }
     path1.close();
     REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
-    
+
     // fail, quad
     path1.reset();
     path1.moveTo(r1[0].fX, r1[0].fY);
@@ -539,7 +1273,7 @@
     }
     path1.close();
     REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
-    
+
     // fail, cubic
     path1.reset();
     path1.moveTo(r1[0].fX, r1[0].fY);
@@ -553,6 +1287,220 @@
     REPORTER_ASSERT(reporter, fail ^ path1.isRect(0));
 }
 
+static void test_isNestedRects(skiatest::Reporter* reporter) {
+    // passing tests (all moveTo / lineTo...
+    SkPoint r1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}};
+    SkPoint r2[] = {{1, 0}, {1, 1}, {0, 1}, {0, 0}};
+    SkPoint r3[] = {{1, 1}, {0, 1}, {0, 0}, {1, 0}};
+    SkPoint r4[] = {{0, 1}, {0, 0}, {1, 0}, {1, 1}};
+    SkPoint r5[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}};
+    SkPoint r6[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
+    SkPoint r7[] = {{1, 1}, {1, 0}, {0, 0}, {0, 1}};
+    SkPoint r8[] = {{1, 0}, {0, 0}, {0, 1}, {1, 1}};
+    SkPoint r9[] = {{0, 1}, {1, 1}, {1, 0}, {0, 0}};
+    SkPoint ra[] = {{0, 0}, {0, .5f}, {0, 1}, {.5f, 1}, {1, 1}, {1, .5f},
+        {1, 0}, {.5f, 0}};
+    SkPoint rb[] = {{0, 0}, {.5f, 0}, {1, 0}, {1, .5f}, {1, 1}, {.5f, 1},
+        {0, 1}, {0, .5f}};
+    SkPoint rc[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}};
+    SkPoint rd[] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}};
+    SkPoint re[] = {{0, 0}, {1, 0}, {1, 0}, {1, 1}, {0, 1}};
+
+    // failing tests
+    SkPoint f1[] = {{0, 0}, {1, 0}, {1, 1}}; // too few points
+    SkPoint f2[] = {{0, 0}, {1, 1}, {0, 1}, {1, 0}}; // diagonal
+    SkPoint f3[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 0}, {1, 0}}; // wraps
+    SkPoint f4[] = {{0, 0}, {1, 0}, {0, 0}, {1, 0}, {1, 1}, {0, 1}}; // backs up
+    SkPoint f5[] = {{0, 0}, {1, 0}, {1, 1}, {2, 0}}; // end overshoots
+    SkPoint f6[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}, {0, 2}}; // end overshoots
+    SkPoint f7[] = {{0, 0}, {1, 0}, {1, 1}, {0, 2}}; // end overshoots
+    SkPoint f8[] = {{0, 0}, {1, 0}, {1, 1}, {1, 0}}; // 'L'
+
+    // failing, no close
+    SkPoint c1[] = {{0, 0}, {1, 0}, {1, 1}, {0, 1}}; // close doesn't match
+    SkPoint c2[] = {{0, 0}, {1, 0}, {1, 2}, {0, 2}, {0, 1}}; // ditto
+
+    size_t testLen[] = {
+        sizeof(r1), sizeof(r2), sizeof(r3), sizeof(r4), sizeof(r5), sizeof(r6),
+        sizeof(r7), sizeof(r8), sizeof(r9), sizeof(ra), sizeof(rb), sizeof(rc),
+        sizeof(rd), sizeof(re),
+        sizeof(f1), sizeof(f2), sizeof(f3), sizeof(f4), sizeof(f5), sizeof(f6),
+        sizeof(f7), sizeof(f8),
+        sizeof(c1), sizeof(c2)
+    };
+    SkPoint* tests[] = {
+        r1, r2, r3, r4, r5, r6, r7, r8, r9, ra, rb, rc, rd, re,
+        f1, f2, f3, f4, f5, f6, f7, f8,
+        c1, c2
+    };
+    const SkPoint* lastPass = re;
+    const SkPoint* lastClose = f8;
+    const size_t testCount = sizeof(tests) / sizeof(tests[0]);
+    size_t index;
+    for (int rectFirst = 0; rectFirst <= 1; ++rectFirst) {
+        bool fail = false;
+        bool close = true;
+        for (size_t testIndex = 0; testIndex < testCount; ++testIndex) {
+            SkPath path;
+            if (rectFirst) {
+                path.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+            }
+            path.moveTo(tests[testIndex][0].fX, tests[testIndex][0].fY);
+            for (index = 1; index < testLen[testIndex] / sizeof(SkPoint); ++index) {
+                path.lineTo(tests[testIndex][index].fX, tests[testIndex][index].fY);
+            }
+            if (close) {
+                path.close();
+            }
+            if (!rectFirst) {
+                path.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+            }
+            REPORTER_ASSERT(reporter, fail ^ path.isNestedRects(0));
+            if (!fail) {
+                SkRect expected[2], computed[2];
+                SkRect testBounds;
+                testBounds.set(tests[testIndex], testLen[testIndex] / sizeof(SkPoint));
+                expected[0] = SkRect::MakeLTRB(-1, -1, 2, 2);
+                expected[1] = testBounds;
+                REPORTER_ASSERT(reporter, path.isNestedRects(computed));
+                REPORTER_ASSERT(reporter, expected[0] == computed[0]);
+                REPORTER_ASSERT(reporter, expected[1] == computed[1]);
+            }
+            if (tests[testIndex] == lastPass) {
+                fail = true;
+            }
+            if (tests[testIndex] == lastClose) {
+                close = false;
+            }
+        }
+
+        // fail, close then line
+        SkPath path1;
+        if (rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+        }
+        path1.moveTo(r1[0].fX, r1[0].fY);
+        for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
+            path1.lineTo(r1[index].fX, r1[index].fY);
+        }
+        path1.close();
+        path1.lineTo(1, 0);
+        if (!rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+        }
+        REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
+
+        // fail, move in the middle
+        path1.reset();
+        if (rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+        }
+        path1.moveTo(r1[0].fX, r1[0].fY);
+        for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
+            if (index == 2) {
+                path1.moveTo(1, .5f);
+            }
+            path1.lineTo(r1[index].fX, r1[index].fY);
+        }
+        path1.close();
+        if (!rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+        }
+        REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
+
+        // fail, move on the edge
+        path1.reset();
+        if (rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+        }
+        for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
+            path1.moveTo(r1[index - 1].fX, r1[index - 1].fY);
+            path1.lineTo(r1[index].fX, r1[index].fY);
+        }
+        path1.close();
+        if (!rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+        }
+        REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
+
+        // fail, quad
+        path1.reset();
+        if (rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+        }
+        path1.moveTo(r1[0].fX, r1[0].fY);
+        for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
+            if (index == 2) {
+                path1.quadTo(1, .5f, 1, .5f);
+            }
+            path1.lineTo(r1[index].fX, r1[index].fY);
+        }
+        path1.close();
+        if (!rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+        }
+        REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
+
+        // fail, cubic
+        path1.reset();
+        if (rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCW_Direction);
+        }
+        path1.moveTo(r1[0].fX, r1[0].fY);
+        for (index = 1; index < testLen[0] / sizeof(SkPoint); ++index) {
+            if (index == 2) {
+                path1.cubicTo(1, .5f, 1, .5f, 1, .5f);
+            }
+            path1.lineTo(r1[index].fX, r1[index].fY);
+        }
+        path1.close();
+        if (!rectFirst) {
+            path1.addRect(-1, -1, 2, 2, SkPath::kCCW_Direction);
+        }
+        REPORTER_ASSERT(reporter, fail ^ path1.isNestedRects(0));
+
+        // 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, fail ^ path1.isNestedRects(0));
+    }
+
+    // pass, stroke rect
+    SkPath src, dst;
+    src.addRect(1, 1, 7, 7, SkPath::kCW_Direction);
+    SkPaint strokePaint;
+    strokePaint.setStyle(SkPaint::kStroke_Style);
+    strokePaint.setStrokeWidth(2);
+    strokePaint.getFillPath(src, &dst);
+    REPORTER_ASSERT(reporter, dst.isNestedRects(0));
+}
+
+static void write_and_read_back(skiatest::Reporter* reporter,
+                                const SkPath& p) {
+    SkWriter32 writer(100);
+    writer.writePath(p);
+    size_t size = writer.size();
+    SkAutoMalloc storage(size);
+    writer.flatten(storage.get());
+    SkReader32 reader(storage.get(), size);
+
+    SkPath readBack;
+    REPORTER_ASSERT(reporter, readBack != p);
+    reader.readPath(&readBack);
+    REPORTER_ASSERT(reporter, readBack == p);
+
+    REPORTER_ASSERT(reporter, readBack.getConvexityOrUnknown() ==
+                              p.getConvexityOrUnknown());
+
+    REPORTER_ASSERT(reporter, readBack.isOval(NULL) == p.isOval(NULL));
+
+    const SkRect& origBounds = p.getBounds();
+    const SkRect& readBackBounds = readBack.getBounds();
+
+    REPORTER_ASSERT(reporter, origBounds == readBackBounds);
+}
+
 static void test_flattening(skiatest::Reporter* reporter) {
     SkPath p;
 
@@ -567,22 +1515,38 @@
     p.quadTo(pts[2], pts[3]);
     p.cubicTo(pts[4], pts[5], pts[6]);
 
-    SkWriter32 writer(100);
-    p.flatten(writer);
-    size_t size = writer.size();
-    SkAutoMalloc storage(size);
-    writer.flatten(storage.get());
-    SkReader32 reader(storage.get(), size);
+    write_and_read_back(reporter, p);
 
-    SkPath p1;
-    REPORTER_ASSERT(reporter, p1 != p);
-    p1.unflatten(reader);
-    REPORTER_ASSERT(reporter, p1 == p);
+    // create a buffer that should be much larger than the path so we don't
+    // kill our stack if writer goes too far.
+    char buffer[1024];
+    uint32_t size1 = p.writeToMemory(NULL);
+    uint32_t size2 = p.writeToMemory(buffer);
+    REPORTER_ASSERT(reporter, size1 == size2);
+
+    SkPath p2;
+    uint32_t size3 = p2.readFromMemory(buffer);
+    REPORTER_ASSERT(reporter, size1 == size3);
+    REPORTER_ASSERT(reporter, p == p2);
+
+    char buffer2[1024];
+    size3 = p2.writeToMemory(buffer2);
+    REPORTER_ASSERT(reporter, size1 == size3);
+    REPORTER_ASSERT(reporter, memcmp(buffer, buffer2, size1) == 0);
+
+    // test persistence of the oval flag & convexity
+    {
+        SkPath oval;
+        SkRect rect = SkRect::MakeWH(10, 10);
+        oval.addOval(rect);
+
+        write_and_read_back(reporter, oval);
+    }
 }
 
 static void test_transform(skiatest::Reporter* reporter) {
     SkPath p, p1;
-    
+
     static const SkPoint pts[] = {
         { 0, 0 },
         { SkIntToScalar(10), SkIntToScalar(10) },
@@ -593,12 +1557,12 @@
     p.lineTo(pts[1]);
     p.quadTo(pts[2], pts[3]);
     p.cubicTo(pts[4], pts[5], pts[6]);
-    
+
     SkMatrix matrix;
     matrix.reset();
     p.transform(matrix, &p1);
     REPORTER_ASSERT(reporter, p == p1);
-    
+
     matrix.setScale(SK_Scalar1 * 2, SK_Scalar1 * 3);
     p.transform(matrix, &p1);
     SkPoint pts1[7];
@@ -612,139 +1576,73 @@
 
 static void test_zero_length_paths(skiatest::Reporter* reporter) {
     SkPath  p;
-    SkPoint pt;
-    SkRect  bounds;
+    uint8_t verbs[32];
 
-    // Lone moveTo case
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 1 == p.countPoints());
-    p.getLastPt(&pt);
-    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
-    REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
-    bounds.set(0, 0, 0, 0);
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
+    struct SUPPRESS_VISIBILITY_WARNING zeroPathTestData {
+        const char* testPath;
+        const size_t numResultPts;
+        const SkRect resultBound;
+        const SkPath::Verb* resultVerbs;
+        const size_t numResultVerbs;
+    };
 
-    // MoveTo-MoveTo case
-    p.moveTo(SK_Scalar1*2, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 2 == p.countPoints());
-    p.getLastPt(&pt);
-    REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1*2);
-    REPORTER_ASSERT(reporter, pt.fY == SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
-    REPORTER_ASSERT(reporter, bounds == p.getBounds()); 
+    static const SkPath::Verb resultVerbs1[] = { SkPath::kMove_Verb };
+    static const SkPath::Verb resultVerbs2[] = { SkPath::kMove_Verb, SkPath::kMove_Verb };
+    static const SkPath::Verb resultVerbs3[] = { SkPath::kMove_Verb, SkPath::kClose_Verb };
+    static const SkPath::Verb resultVerbs4[] = { SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb };
+    static const SkPath::Verb resultVerbs5[] = { SkPath::kMove_Verb, SkPath::kLine_Verb };
+    static const SkPath::Verb resultVerbs6[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb };
+    static const SkPath::Verb resultVerbs7[] = { SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb };
+    static const SkPath::Verb resultVerbs8[] = {
+        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb
+    };
+    static const SkPath::Verb resultVerbs9[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb };
+    static const SkPath::Verb resultVerbs10[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb };
+    static const SkPath::Verb resultVerbs11[] = { SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb };
+    static const SkPath::Verb resultVerbs12[] = {
+        SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kQuad_Verb, SkPath::kClose_Verb
+    };
+    static const SkPath::Verb resultVerbs13[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb };
+    static const SkPath::Verb resultVerbs14[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb };
+    static const SkPath::Verb resultVerbs15[] = { SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb };
+    static const SkPath::Verb resultVerbs16[] = {
+        SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kCubic_Verb, SkPath::kClose_Verb
+    };
+    static const struct zeroPathTestData gZeroLengthTests[] = {
+        { "M 1 1", 1, {0, 0, 0, 0}, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "M 1 1 M 2 1", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
+        { "M 1 1 z", 1, {0, 0, 0, 0}, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
+        { "M 1 1 z M 2 1 z", 2, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
+        { "M 1 1 L 1 1", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) },
+        { "M 1 1 L 1 1 M 2 1 L 2 1", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs6, SK_ARRAY_COUNT(resultVerbs6) },
+        { "M 1 1 L 1 1 z", 2, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs7, SK_ARRAY_COUNT(resultVerbs7) },
+        { "M 1 1 L 1 1 z M 2 1 L 2 1 z", 4, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs8, SK_ARRAY_COUNT(resultVerbs8) },
+        { "M 1 1 Q 1 1 1 1", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs9, SK_ARRAY_COUNT(resultVerbs9) },
+        { "M 1 1 Q 1 1 1 1 M 2 1 Q 2 1 2 1", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs10, SK_ARRAY_COUNT(resultVerbs10) },
+        { "M 1 1 Q 1 1 1 1 z", 3, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs11, SK_ARRAY_COUNT(resultVerbs11) },
+        { "M 1 1 Q 1 1 1 1 z M 2 1 Q 2 1 2 1 z", 6, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs12, SK_ARRAY_COUNT(resultVerbs12) },
+        { "M 1 1 C 1 1 1 1 1 1", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs13, SK_ARRAY_COUNT(resultVerbs13) },
+        { "M 1 1 C 1 1 1 1 1 1 M 2 1 C 2 1 2 1 2 1", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs14,
+            SK_ARRAY_COUNT(resultVerbs14)
+        },
+        { "M 1 1 C 1 1 1 1 1 1 z", 4, {SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1}, resultVerbs15, SK_ARRAY_COUNT(resultVerbs15) },
+        { "M 1 1 C 1 1 1 1 1 1 z M 2 1 C 2 1 2 1 2 1 z", 8, {SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1}, resultVerbs16,
+            SK_ARRAY_COUNT(resultVerbs16)
+        }
+    };
 
-    // moveTo-close case
-    p.reset();
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    p.close();
-    bounds.set(0, 0, 0, 0);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 1 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-close-moveTo-close case
-    p.moveTo(SK_Scalar1*2, SK_Scalar1);
-    p.close();
-    bounds.set(SK_Scalar1, SK_Scalar1, 2*SK_Scalar1, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 2 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-line case
-    p.reset();
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    p.lineTo(SK_Scalar1, SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 2 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-lineTo-moveTo-lineTo case
-    p.moveTo(SK_Scalar1*2, SK_Scalar1);
-    p.lineTo(SK_Scalar1*2, SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 4 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-line-close case
-    p.reset();
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    p.lineTo(SK_Scalar1, SK_Scalar1);
-    p.close();
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 2 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-line-close-moveTo-line-close case
-    p.moveTo(SK_Scalar1*2, SK_Scalar1);
-    p.lineTo(SK_Scalar1*2, SK_Scalar1);
-    p.close();
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 4 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-quadTo case
-    p.reset();
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 3 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-quadTo-close case
-    p.close();
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 3 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-quadTo-moveTo-quadTo case
-    p.reset();
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    p.quadTo(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    p.moveTo(SK_Scalar1*2, SK_Scalar1);
-    p.quadTo(SK_Scalar1*2, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 6 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-cubicTo case
-    p.reset();
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    p.cubicTo(SK_Scalar1, SK_Scalar1,
-              SK_Scalar1, SK_Scalar1,
-              SK_Scalar1, SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 4 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-quadTo-close case
-    p.close();
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 4 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
-
-    // moveTo-quadTo-moveTo-quadTo case
-    p.reset();
-    p.moveTo(SK_Scalar1, SK_Scalar1);
-    p.cubicTo(SK_Scalar1, SK_Scalar1,
-              SK_Scalar1, SK_Scalar1,
-              SK_Scalar1, SK_Scalar1);
-    p.moveTo(SK_Scalar1*2, SK_Scalar1);
-    p.cubicTo(SK_Scalar1*2, SK_Scalar1,
-              SK_Scalar1*2, SK_Scalar1,
-              SK_Scalar1*2, SK_Scalar1);
-    bounds.set(SK_Scalar1, SK_Scalar1, SK_Scalar1*2, SK_Scalar1);
-    REPORTER_ASSERT(reporter, !p.isEmpty());
-    REPORTER_ASSERT(reporter, 8 == p.countPoints());
-    REPORTER_ASSERT(reporter, bounds == p.getBounds());
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gZeroLengthTests); ++i) {
+        p.reset();
+        bool valid = SkParsePath::FromSVGString(gZeroLengthTests[i].testPath, &p);
+        REPORTER_ASSERT(reporter, valid);
+        REPORTER_ASSERT(reporter, !p.isEmpty());
+        REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultPts == (size_t)p.countPoints());
+        REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultBound == p.getBounds());
+        REPORTER_ASSERT(reporter, gZeroLengthTests[i].numResultVerbs == (size_t)p.getVerbs(verbs, SK_ARRAY_COUNT(verbs)));
+        for (size_t j = 0; j < gZeroLengthTests[i].numResultVerbs; ++j) {
+            REPORTER_ASSERT(reporter, gZeroLengthTests[i].resultVerbs[j] == verbs[j]);
+        }
+    }
 }
 
 struct SegmentInfo {
@@ -755,85 +1653,127 @@
 #define kCurveSegmentMask   (SkPath::kQuad_SegmentMask | SkPath::kCubic_SegmentMask)
 
 static void test_segment_masks(skiatest::Reporter* reporter) {
-    SkPath p;
+    SkPath p, p2;
+
     p.moveTo(0, 0);
     p.quadTo(100, 100, 200, 200);
     REPORTER_ASSERT(reporter, SkPath::kQuad_SegmentMask == p.getSegmentMasks());
     REPORTER_ASSERT(reporter, !p.isEmpty());
+    p2 = p;
+    REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
     p.cubicTo(100, 100, 200, 200, 300, 300);
     REPORTER_ASSERT(reporter, kCurveSegmentMask == p.getSegmentMasks());
     REPORTER_ASSERT(reporter, !p.isEmpty());
+    p2 = p;
+    REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
+
     p.reset();
     p.moveTo(0, 0);
     p.cubicTo(100, 100, 200, 200, 300, 300);
     REPORTER_ASSERT(reporter, SkPath::kCubic_SegmentMask == p.getSegmentMasks());
+    p2 = p;
+    REPORTER_ASSERT(reporter, p2.getSegmentMasks() == p.getSegmentMasks());
+
     REPORTER_ASSERT(reporter, !p.isEmpty());
 }
 
 static void test_iter(skiatest::Reporter* reporter) {
-    SkPath p;
+    SkPath  p;
     SkPoint pts[4];
 
     // Test an iterator with no path
     SkPath::Iter noPathIter;
     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+
     // Test that setting an empty path works
     noPathIter.setPath(p, false);
     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
+
     // Test that close path makes no difference for an empty path
     noPathIter.setPath(p, true);
     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
-    
+
     // Test an iterator with an initial empty path
     SkPath::Iter iter(p, false);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
 
     // Test that close path makes no difference
-    SkPath::Iter forceCloseIter(p, true);
-    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
-
-    // Test that a move-only path produces nothing when iterated.
-    p.moveTo(SK_Scalar1, 0);
-    iter.setPath(p, false);
+    iter.setPath(p, true);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
 
-    // No matter how many moves we add, we should still get nothing back.
-    p.moveTo(SK_Scalar1*2, 0);
-    p.moveTo(SK_Scalar1*3, 0);
-    p.moveTo(SK_Scalar1*4, 0);
-    p.moveTo(SK_Scalar1*5, 0);
-    iter.setPath(p, false);
-    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
 
-    // Nor should force closing
-    forceCloseIter.setPath(p, true);
-    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+    struct iterTestData {
+        const char* testPath;
+        const bool forceClose;
+        const bool consumeDegenerates;
+        const size_t* numResultPtsPerVerb;
+        const SkPoint* resultPts;
+        const SkPath::Verb* resultVerbs;
+        const size_t numResultVerbs;
+    };
 
-    // Initial closes should be ignored
-    p.reset();
-    p.close();
-    iter.setPath(p, false);
-    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
-    // Even if force closed
-    forceCloseIter.setPath(p, true);
-    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+    static const SkPath::Verb resultVerbs1[] = { SkPath::kDone_Verb };
+    static const SkPath::Verb resultVerbs2[] = {
+        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kDone_Verb
+    };
+    static const SkPath::Verb resultVerbs3[] = {
+        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
+    };
+    static const SkPath::Verb resultVerbs4[] = {
+        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
+    };
+    static const SkPath::Verb resultVerbs5[] = {
+        SkPath::kMove_Verb, SkPath::kLine_Verb, SkPath::kClose_Verb, SkPath::kMove_Verb, SkPath::kClose_Verb, SkPath::kDone_Verb
+    };
+    static const size_t resultPtsSizes1[] = { 0 };
+    static const size_t resultPtsSizes2[] = { 1, 2, 2, 0 };
+    static const size_t resultPtsSizes3[] = { 1, 2, 2, 2, 1, 0 };
+    static const size_t resultPtsSizes4[] = { 1, 2, 1, 1, 0 };
+    static const size_t resultPtsSizes5[] = { 1, 2, 1, 1, 1, 0 };
+    static const SkPoint* resultPts1 = 0;
+    static const SkPoint resultPts2[] = {
+        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 }
+    };
+    static const SkPoint resultPts3[] = {
+        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, SK_Scalar1 }, { SK_Scalar1, SK_Scalar1 }, { 0, SK_Scalar1 },
+        { 0, SK_Scalar1 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }
+    };
+    static const SkPoint resultPts4[] = {
+        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
+    };
+    static const SkPoint resultPts5[] = {
+        { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { SK_Scalar1, 0 }, { 0, 0 }, { 0, 0 }
+    };
+    static const struct iterTestData gIterTests[] = {
+        { "M 1 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "M 1 0 M 2 0 M 3 0 M 4 0 M 5 0", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "M 1 0 M 1 0 M 3 0 M 4 0 M 5 0", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "z M 1 0 z z M 2 0 z M 3 0 M 4 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "M 1 0 L 1 1 L 0 1 M 0 0 z", false, true, resultPtsSizes2, resultPts2, resultVerbs2, SK_ARRAY_COUNT(resultVerbs2) },
+        { "M 1 0 L 1 1 L 0 1 M 0 0 z", true, true, resultPtsSizes3, resultPts3, resultVerbs3, SK_ARRAY_COUNT(resultVerbs3) },
+        { "M 1 0 L 1 0 M 0 0 z", false, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "M 1 0 L 1 0 M 0 0 z", true, true, resultPtsSizes1, resultPts1, resultVerbs1, SK_ARRAY_COUNT(resultVerbs1) },
+        { "M 1 0 L 1 0 M 0 0 z", false, false, resultPtsSizes4, resultPts4, resultVerbs4, SK_ARRAY_COUNT(resultVerbs4) },
+        { "M 1 0 L 1 0 M 0 0 z", true, false, resultPtsSizes5, resultPts5, resultVerbs5, SK_ARRAY_COUNT(resultVerbs5) }
+    };
 
-    // Move/close sequences should also be ignored
-    p.reset();
-    p.close();
-    p.moveTo(SK_Scalar1, 0);
-    p.close();
-    p.close();
-    p.moveTo(SK_Scalar1*2, 0);
-    p.close();
-    p.moveTo(SK_Scalar1*3, 0);
-    p.moveTo(SK_Scalar1*4, 0);
-    p.close();
-    iter.setPath(p, false);
-    REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
-    // Even if force closed
-    forceCloseIter.setPath(p, true);
-    REPORTER_ASSERT(reporter, forceCloseIter.next(pts) == SkPath::kDone_Verb);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gIterTests); ++i) {
+        p.reset();
+        bool valid = SkParsePath::FromSVGString(gIterTests[i].testPath, &p);
+        REPORTER_ASSERT(reporter, valid);
+        iter.setPath(p, gIterTests[i].forceClose);
+        int j = 0, l = 0;
+        do {
+            REPORTER_ASSERT(reporter, iter.next(pts, gIterTests[i].consumeDegenerates) == gIterTests[i].resultVerbs[j]);
+            for (int k = 0; k < (int)gIterTests[i].numResultPtsPerVerb[j]; ++k) {
+                REPORTER_ASSERT(reporter, pts[k] == gIterTests[i].resultPts[l++]);
+            }
+        } while (gIterTests[i].resultVerbs[j++] != SkPath::kDone_Verb);
+        REPORTER_ASSERT(reporter, j == (int)gIterTests[i].numResultVerbs);
+    }
 
     // The GM degeneratesegments.cpp test is more extensive
 }
@@ -848,7 +1788,7 @@
     // Test that setting an empty path works
     noPathIter.setPath(p);
     REPORTER_ASSERT(reporter, noPathIter.next(pts) == SkPath::kDone_Verb);
-    
+
     // Test an iterator with an initial empty path
     SkPath::RawIter iter(p);
     REPORTER_ASSERT(reporter, iter.next(pts) == SkPath::kDone_Verb);
@@ -943,7 +1883,6 @@
             do {
                 nextVerb = static_cast<SkPath::Verb>((rand.nextU() >> 16) % SkPath::kDone_Verb);
             } while (lastWasClose && nextVerb == SkPath::kClose_Verb);
-            int numRequiredPts;
             switch (nextVerb) {
                 case SkPath::kMove_Verb:
                     expectedPts[numPoints] = randomPts[(rand.nextU() >> 16) % 25];
@@ -999,7 +1938,7 @@
             }
             expectedVerbs[numIterVerbs++] = nextVerb;
         }
-        
+
         iter.setPath(p);
         numVerbs = numIterVerbs;
         numIterVerbs = 0;
@@ -1054,16 +1993,268 @@
     }
 }
 
-void TestPath(skiatest::Reporter* reporter);
-void TestPath(skiatest::Reporter* reporter) {
-    {
-        SkSize size;
-        size.fWidth = 3.4f;
-        size.width();
-        size = SkSize::Make(3,4);
-        SkISize isize = SkISize::Make(3,4);
+static void check_for_circle(skiatest::Reporter* reporter,
+                             const SkPath& path,
+                             bool expectedCircle,
+                             SkPath::Direction expectedDir) {
+    SkRect rect;
+    REPORTER_ASSERT(reporter, path.isOval(&rect) == expectedCircle);
+    REPORTER_ASSERT(reporter, path.cheapIsDirection(expectedDir));
+
+    if (expectedCircle) {
+        REPORTER_ASSERT(reporter, rect.height() == rect.width());
+    }
+}
+
+static void test_circle_skew(skiatest::Reporter* reporter,
+                             const SkPath& path,
+                             SkPath::Direction dir) {
+    SkPath tmp;
+
+    SkMatrix m;
+    m.setSkew(SkIntToScalar(3), SkIntToScalar(5));
+    path.transform(m, &tmp);
+    // this matrix reverses the direction.
+    if (SkPath::kCCW_Direction == dir) {
+        dir = SkPath::kCW_Direction;
+    } else {
+        SkASSERT(SkPath::kCW_Direction == dir);
+        dir = SkPath::kCCW_Direction;
+    }
+    check_for_circle(reporter, tmp, false, dir);
+}
+
+static void test_circle_translate(skiatest::Reporter* reporter,
+                                  const SkPath& path,
+                                  SkPath::Direction dir) {
+    SkPath tmp;
+
+    // translate at small offset
+    SkMatrix m;
+    m.setTranslate(SkIntToScalar(15), SkIntToScalar(15));
+    path.transform(m, &tmp);
+    check_for_circle(reporter, tmp, true, dir);
+
+    tmp.reset();
+    m.reset();
+
+    // translate at a relatively big offset
+    m.setTranslate(SkIntToScalar(1000), SkIntToScalar(1000));
+    path.transform(m, &tmp);
+    check_for_circle(reporter, tmp, true, dir);
+}
+
+static void test_circle_rotate(skiatest::Reporter* reporter,
+                               const SkPath& path,
+                               SkPath::Direction dir) {
+    for (int angle = 0; angle < 360; ++angle) {
+        SkPath tmp;
+        SkMatrix m;
+        m.setRotate(SkIntToScalar(angle));
+        path.transform(m, &tmp);
+
+        // TODO: a rotated circle whose rotated angle is not a multiple of 90
+        // degrees is not an oval anymore, this can be improved.  we made this
+        // for the simplicity of our implementation.
+        if (angle % 90 == 0) {
+            check_for_circle(reporter, tmp, true, dir);
+        } else {
+            check_for_circle(reporter, tmp, false, dir);
+        }
+    }
+}
+
+static void test_circle_mirror_x(skiatest::Reporter* reporter,
+                                 const SkPath& path,
+                                 SkPath::Direction dir) {
+    SkPath tmp;
+    SkMatrix m;
+    m.reset();
+    m.setScaleX(-SK_Scalar1);
+    path.transform(m, &tmp);
+
+    if (SkPath::kCW_Direction == dir) {
+        dir = SkPath::kCCW_Direction;
+    } else {
+        SkASSERT(SkPath::kCCW_Direction == dir);
+        dir = SkPath::kCW_Direction;
     }
 
+    check_for_circle(reporter, tmp, true, dir);
+}
+
+static void test_circle_mirror_y(skiatest::Reporter* reporter,
+                                 const SkPath& path,
+                                 SkPath::Direction dir) {
+    SkPath tmp;
+    SkMatrix m;
+    m.reset();
+    m.setScaleY(-SK_Scalar1);
+    path.transform(m, &tmp);
+
+    if (SkPath::kCW_Direction == dir) {
+        dir = SkPath::kCCW_Direction;
+    } else {
+        SkASSERT(SkPath::kCCW_Direction == dir);
+        dir = SkPath::kCW_Direction;
+    }
+
+    check_for_circle(reporter, tmp, true, dir);
+}
+
+static void test_circle_mirror_xy(skiatest::Reporter* reporter,
+                                 const SkPath& path,
+                                 SkPath::Direction dir) {
+    SkPath tmp;
+    SkMatrix m;
+    m.reset();
+    m.setScaleX(-SK_Scalar1);
+    m.setScaleY(-SK_Scalar1);
+    path.transform(m, &tmp);
+
+    check_for_circle(reporter, tmp, true, dir);
+}
+
+static void test_circle_with_direction(skiatest::Reporter* reporter,
+                                       SkPath::Direction dir) {
+    SkPath path;
+
+    // circle at origin
+    path.addCircle(0, 0, SkIntToScalar(20), dir);
+    check_for_circle(reporter, path, true, dir);
+    test_circle_rotate(reporter, path, dir);
+    test_circle_translate(reporter, path, dir);
+    test_circle_skew(reporter, path, dir);
+
+    // circle at an offset at (10, 10)
+    path.reset();
+    path.addCircle(SkIntToScalar(10), SkIntToScalar(10),
+                   SkIntToScalar(20), dir);
+    check_for_circle(reporter, path, true, dir);
+    test_circle_rotate(reporter, path, dir);
+    test_circle_translate(reporter, path, dir);
+    test_circle_skew(reporter, path, dir);
+    test_circle_mirror_x(reporter, path, dir);
+    test_circle_mirror_y(reporter, path, dir);
+    test_circle_mirror_xy(reporter, path, dir);
+}
+
+static void test_circle_with_add_paths(skiatest::Reporter* reporter) {
+    SkPath path;
+    SkPath circle;
+    SkPath rect;
+    SkPath empty;
+
+    static const SkPath::Direction kCircleDir = SkPath::kCW_Direction;
+    static const SkPath::Direction kCircleDirOpposite = SkPath::kCCW_Direction;
+
+    circle.addCircle(0, 0, SkIntToScalar(10), kCircleDir);
+    rect.addRect(SkIntToScalar(5), SkIntToScalar(5),
+                 SkIntToScalar(20), SkIntToScalar(20), SkPath::kCW_Direction);
+
+    SkMatrix translate;
+    translate.setTranslate(SkIntToScalar(12), SkIntToScalar(12));
+
+    // For simplicity, all the path concatenation related operations
+    // would mark it non-circle, though in theory it's still a circle.
+
+    // empty + circle (translate)
+    path = empty;
+    path.addPath(circle, translate);
+    check_for_circle(reporter, path, false, kCircleDir);
+
+    // circle + empty (translate)
+    path = circle;
+    path.addPath(empty, translate);
+    check_for_circle(reporter, path, false, kCircleDir);
+
+    // test reverseAddPath
+    path = circle;
+    path.reverseAddPath(rect);
+    check_for_circle(reporter, path, false, kCircleDirOpposite);
+}
+
+static void test_circle(skiatest::Reporter* reporter) {
+    test_circle_with_direction(reporter, SkPath::kCW_Direction);
+    test_circle_with_direction(reporter, SkPath::kCCW_Direction);
+
+    // multiple addCircle()
+    SkPath path;
+    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
+    path.addCircle(0, 0, SkIntToScalar(20), SkPath::kCW_Direction);
+    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+
+    // some extra lineTo() would make isOval() fail
+    path.reset();
+    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
+    path.lineTo(0, 0);
+    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+
+    // not back to the original point
+    path.reset();
+    path.addCircle(0, 0, SkIntToScalar(10), SkPath::kCW_Direction);
+    path.setLastPt(SkIntToScalar(5), SkIntToScalar(5));
+    check_for_circle(reporter, path, false, SkPath::kCW_Direction);
+
+    test_circle_with_add_paths(reporter);
+}
+
+static void test_oval(skiatest::Reporter* reporter) {
+    SkRect rect;
+    SkMatrix m;
+    SkPath path;
+
+    rect = SkRect::MakeWH(SkIntToScalar(30), SkIntToScalar(50));
+    path.addOval(rect);
+
+    REPORTER_ASSERT(reporter, path.isOval(NULL));
+
+    m.setRotate(SkIntToScalar(90));
+    SkPath tmp;
+    path.transform(m, &tmp);
+    // an oval rotated 90 degrees is still an oval.
+    REPORTER_ASSERT(reporter, tmp.isOval(NULL));
+
+    m.reset();
+    m.setRotate(SkIntToScalar(30));
+    tmp.reset();
+    path.transform(m, &tmp);
+    // an oval rotated 30 degrees is not an oval anymore.
+    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+
+    // since empty path being transformed.
+    path.reset();
+    tmp.reset();
+    m.reset();
+    path.transform(m, &tmp);
+    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+
+    // empty path is not an oval
+    tmp.reset();
+    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+
+    // only has moveTo()s
+    tmp.reset();
+    tmp.moveTo(0, 0);
+    tmp.moveTo(SkIntToScalar(10), SkIntToScalar(10));
+    REPORTER_ASSERT(reporter, !tmp.isOval(NULL));
+
+    // mimic WebKit's calling convention,
+    // call moveTo() first and then call addOval()
+    path.reset();
+    path.moveTo(0, 0);
+    path.addOval(rect);
+    REPORTER_ASSERT(reporter, path.isOval(NULL));
+
+    // copy path
+    path.reset();
+    tmp.reset();
+    tmp.addOval(rect);
+    path = tmp;
+    REPORTER_ASSERT(reporter, path.isOval(NULL));
+}
+
+static void TestPath(skiatest::Reporter* reporter) {
     SkTSize<SkScalar>::Make(3,4);
 
     SkPath  p, p2;
@@ -1071,6 +2262,7 @@
 
     REPORTER_ASSERT(reporter, p.isEmpty());
     REPORTER_ASSERT(reporter, 0 == p.countPoints());
+    REPORTER_ASSERT(reporter, 0 == p.countVerbs());
     REPORTER_ASSERT(reporter, 0 == p.getSegmentMasks());
     REPORTER_ASSERT(reporter, p.isConvex());
     REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
@@ -1106,11 +2298,21 @@
     REPORTER_ASSERT(reporter, p != p2);
     REPORTER_ASSERT(reporter, !(p == p2));
 
-    // does getPoints return the right result
-    REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
+    // do getPoints and getVerbs return the right result
+    REPORTER_ASSERT(reporter, p.getPoints(NULL, 0) == 4);
+    REPORTER_ASSERT(reporter, p.getVerbs(NULL, 0) == 5);
     SkPoint pts[4];
     int count = p.getPoints(pts, 4);
     REPORTER_ASSERT(reporter, count == 4);
+    uint8_t verbs[6];
+    verbs[5] = 0xff;
+    p.getVerbs(verbs, 5);
+    REPORTER_ASSERT(reporter, SkPath::kMove_Verb == verbs[0]);
+    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[1]);
+    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[2]);
+    REPORTER_ASSERT(reporter, SkPath::kLine_Verb == verbs[3]);
+    REPORTER_ASSERT(reporter, SkPath::kClose_Verb == verbs[4]);
+    REPORTER_ASSERT(reporter, 0xff == verbs[5]);
     bounds2.set(pts, 4);
     REPORTER_ASSERT(reporter, bounds == bounds2);
 
@@ -1127,12 +2329,15 @@
     bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
     p.addRect(bounds);
     REPORTER_ASSERT(reporter, !p.isRect(NULL));
-    test_isRect(reporter);
 
+    test_isLine(reporter);
+    test_isRect(reporter);
+    test_isNestedRects(reporter);
     test_zero_length_paths(reporter);
     test_direction(reporter);
     test_convexity(reporter);
     test_convexity2(reporter);
+    test_conservativelyContains(reporter);
     test_close(reporter);
     test_segment_masks(reporter);
     test_flattening(reporter);
@@ -1140,6 +2345,18 @@
     test_bounds(reporter);
     test_iter(reporter);
     test_raw_iter(reporter);
+    test_circle(reporter);
+    test_oval(reporter);
+    test_strokerec(reporter);
+    test_addPoly(reporter);
+    test_isfinite(reporter);
+    test_isfinite_after_transform(reporter);
+    test_tricky_cubic(reporter);
+    test_arb_round_rect_is_convex(reporter);
+    test_arb_zero_rad_round_rect_is_rect(reporter);
+    test_addrect_isfinite(reporter);
+    test_clipped_cubic(reporter);
+    test_crbug_170666(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/PictureTest.cpp b/tests/PictureTest.cpp
new file mode 100644
index 0000000..f6a18c1
--- /dev/null
+++ b/tests/PictureTest.cpp
@@ -0,0 +1,434 @@
+/*
+ * 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 "Test.h"
+#include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkData.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkRandom.h"
+#include "SkRRect.h"
+#include "SkShader.h"
+#include "SkStream.h"
+
+#include "SkPictureUtils.h"
+
+static void make_bm(SkBitmap* bm, int w, int h, SkColor color, bool immutable) {
+    bm->setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    bm->allocPixels();
+    bm->eraseColor(color);
+    if (immutable) {
+        bm->setImmutable();
+    }
+}
+
+typedef void (*DrawBitmapProc)(SkCanvas*, const SkBitmap&, const SkPoint&);
+
+static void drawbitmap_proc(SkCanvas* canvas, const SkBitmap& bm,
+                            const SkPoint& pos) {
+    canvas->drawBitmap(bm, pos.fX, pos.fY, NULL);
+}
+
+static void drawbitmaprect_proc(SkCanvas* canvas, const SkBitmap& bm,
+                                const SkPoint& pos) {
+    SkRect r = {
+        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
+    };
+    r.offset(pos.fX, pos.fY);
+    canvas->drawBitmapRectToRect(bm, NULL, r, NULL);
+}
+
+static void drawshader_proc(SkCanvas* canvas, const SkBitmap& bm,
+                            const SkPoint& pos) {
+    SkRect r = {
+        0, 0, SkIntToScalar(bm.width()), SkIntToScalar(bm.height())
+    };
+    r.offset(pos.fX, pos.fY);
+
+    SkShader* s = SkShader::CreateBitmapShader(bm,
+                                               SkShader::kClamp_TileMode,
+                                               SkShader::kClamp_TileMode);
+    SkPaint paint;
+    paint.setShader(s)->unref();
+    canvas->drawRect(r, paint);
+    canvas->drawOval(r, paint);
+    SkRRect rr;
+    rr.setRectXY(r, 10, 10);
+    canvas->drawRRect(rr, paint);
+}
+
+// Return a picture with the bitmaps drawn at the specified positions.
+static SkPicture* record_bitmaps(const SkBitmap bm[], const SkPoint pos[],
+                                 int count, DrawBitmapProc proc) {
+    SkPicture* pic = new SkPicture;
+    SkCanvas* canvas = pic->beginRecording(1000, 1000);
+    for (int i = 0; i < count; ++i) {
+        proc(canvas, bm[i], pos[i]);
+    }
+    pic->endRecording();
+    return pic;
+}
+
+static void rand_rect(SkRect* rect, SkRandom& rand, SkScalar W, SkScalar H) {
+    rect->fLeft   = rand.nextRangeScalar(-W, 2*W);
+    rect->fTop    = rand.nextRangeScalar(-H, 2*H);
+    rect->fRight  = rect->fLeft + rand.nextRangeScalar(0, W);
+    rect->fBottom = rect->fTop + rand.nextRangeScalar(0, H);
+
+    // we integralize rect to make our tests more predictable, since Gather is
+    // a little sloppy.
+    SkIRect ir;
+    rect->round(&ir);
+    rect->set(ir);
+}
+
+// Allocate result to be large enough to hold subset, and then draw the picture
+// into it, offsetting by subset's top/left corner.
+static void draw(SkPicture* pic, const SkRect& subset, SkBitmap* result) {
+    SkIRect ir;
+    subset.roundOut(&ir);
+    int w = ir.width();
+    int h = ir.height();
+    make_bm(result, w, h, 0, false);
+
+    SkCanvas canvas(*result);
+    canvas.translate(-SkIntToScalar(ir.left()), -SkIntToScalar(ir.top()));
+    canvas.drawPicture(*pic);
+}
+
+template <typename T> int find_index(const T* array, T elem, int count) {
+    for (int i = 0; i < count; ++i) {
+        if (array[i] == elem) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+// Return true if 'ref' is found in array[]
+static bool find(SkPixelRef const * const * array, SkPixelRef const * ref, int count) {
+    return find_index<const SkPixelRef*>(array, ref, count) >= 0;
+}
+
+// Look at each pixel in bm, and if its color appears in colors[], find the
+// corresponding value in refs[] and append that ref into array, skipping
+// duplicates of the same value.
+static void gather_from_colors(const SkBitmap& bm, SkPixelRef* const refs[],
+                               int count, SkTDArray<SkPixelRef*>* array) {
+    // Since we only want to return unique values in array, when we scan we just
+    // set a bit for each index'd color found. In practice we only have a few
+    // distinct colors, so we just use an int's bits as our array. Hence the
+    // assert that count <= number-of-bits-in-our-int.
+    SkASSERT((unsigned)count <= 32);
+    uint32_t bitarray = 0;
+
+    SkAutoLockPixels alp(bm);
+
+    for (int y = 0; y < bm.height(); ++y) {
+        for (int x = 0; x < bm.width(); ++x) {
+            SkPMColor pmc = *bm.getAddr32(x, y);
+            // the only good case where the color is not found would be if
+            // the color is transparent, meaning no bitmap was drawn in that
+            // pixel.
+            if (pmc) {
+                uint32_t index = SkGetPackedR32(pmc);
+                SkASSERT(SkGetPackedG32(pmc) == index);
+                SkASSERT(SkGetPackedB32(pmc) == index);
+                SkASSERT(static_cast<int>(index) < count);
+                bitarray |= 1 << index;
+            }
+        }
+    }
+
+    for (int i = 0; i < count; ++i) {
+        if (bitarray & (1 << i)) {
+            *array->append() = refs[i];
+        }
+    }
+}
+
+static void test_gatherpixelrefs(skiatest::Reporter* reporter) {
+    const int IW = 8;
+    const int IH = IW;
+    const SkScalar W = SkIntToScalar(IW);
+    const SkScalar H = W;
+
+    static const int N = 4;
+    SkBitmap bm[N];
+    SkPixelRef* refs[N];
+
+    const SkPoint pos[] = {
+        { 0, 0 }, { W, 0 }, { 0, H }, { W, H }
+    };
+
+    // Our convention is that the color components contain the index of their
+    // corresponding bitmap/pixelref
+    for (int i = 0; i < N; ++i) {
+        make_bm(&bm[i], IW, IH, SkColorSetARGB(0xFF, i, i, i), true);
+        refs[i] = bm[i].pixelRef();
+    }
+
+    static const DrawBitmapProc procs[] = {
+        drawbitmap_proc, drawbitmaprect_proc, drawshader_proc
+    };
+
+    SkRandom rand;
+    for (size_t k = 0; k < SK_ARRAY_COUNT(procs); ++k) {
+        SkAutoTUnref<SkPicture> pic(record_bitmaps(bm, pos, N, procs[k]));
+
+        // quick check for a small piece of each quadrant, which should just
+        // contain 1 bitmap.
+        for (size_t  i = 0; i < SK_ARRAY_COUNT(pos); ++i) {
+            SkRect r;
+            r.set(2, 2, W - 2, H - 2);
+            r.offset(pos[i].fX, pos[i].fY);
+            SkAutoDataUnref data(SkPictureUtils::GatherPixelRefs(pic, r));
+            REPORTER_ASSERT(reporter, data);
+            int count = data->size() / sizeof(SkPixelRef*);
+            REPORTER_ASSERT(reporter, 1 == count);
+            REPORTER_ASSERT(reporter, *(SkPixelRef**)data->data() == refs[i]);
+        }
+
+        // Test a bunch of random (mostly) rects, and compare the gather results
+        // with a deduced list of refs by looking at the colors drawn.
+        for (int j = 0; j < 100; ++j) {
+            SkRect r;
+            rand_rect(&r, rand, 2*W, 2*H);
+
+            SkBitmap result;
+            draw(pic, r, &result);
+            SkTDArray<SkPixelRef*> array;
+
+            SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
+            size_t dataSize = data ? data->size() : 0;
+            int gatherCount = dataSize / sizeof(SkPixelRef*);
+            SkASSERT(gatherCount * sizeof(SkPixelRef*) == dataSize);
+            SkPixelRef** gatherRefs = data ? (SkPixelRef**)(data->data()) : NULL;
+            SkAutoDataUnref adu(data);
+
+            gather_from_colors(result, refs, N, &array);
+
+            /*
+             *  GatherPixelRefs is conservative, so it can return more bitmaps
+             *  that we actually can see (usually because of conservative bounds
+             *  inflation for antialiasing). Thus our check here is only that
+             *  Gather didn't miss any that we actually saw. Even that isn't
+             *  a strict requirement on Gather, which is meant to be quick and
+             *  only mostly-correct, but at the moment this test should work.
+             */
+            for (int i = 0; i < array.count(); ++i) {
+                bool found = find(gatherRefs, array[i], gatherCount);
+                REPORTER_ASSERT(reporter, found);
+#if 0
+                // enable this block of code to debug failures, as it will rerun
+                // the case that failed.
+                if (!found) {
+                    SkData* data = SkPictureUtils::GatherPixelRefs(pic, r);
+                    size_t dataSize = data ? data->size() : 0;
+                }
+#endif
+            }
+        }
+    }
+}
+
+#ifdef SK_DEBUG
+// Ensure that deleting SkPicturePlayback does not assert. Asserts only fire in debug mode, so only
+// run in debug mode.
+static void test_deleting_empty_playback() {
+    SkPicture picture;
+    // Creates an SkPictureRecord
+    picture.beginRecording(0, 0);
+    // Turns that into an SkPicturePlayback
+    picture.endRecording();
+    // Deletes the old SkPicturePlayback, and creates a new SkPictureRecord
+    picture.beginRecording(0, 0);
+}
+
+// Ensure that serializing an empty picture does not assert. Likewise only runs in debug mode.
+static void test_serializing_empty_picture() {
+    SkPicture picture;
+    picture.beginRecording(0, 0);
+    picture.endRecording();
+    SkDynamicMemoryWStream stream;
+    picture.serialize(&stream);
+}
+#endif
+
+static void rand_op(SkCanvas* canvas, SkRandom& rand) {
+    SkPaint paint;
+    SkRect rect = SkRect::MakeWH(50, 50);
+
+    SkScalar unit = rand.nextUScalar1();
+    if (unit <= 0.3) {
+//        SkDebugf("save\n");
+        canvas->save();
+    } else if (unit <= 0.6) {
+//        SkDebugf("restore\n");
+        canvas->restore();
+    } else if (unit <= 0.9) {
+//        SkDebugf("clip\n");
+        canvas->clipRect(rect);
+    } else {
+//        SkDebugf("draw\n");
+        canvas->drawPaint(paint);
+    }
+}
+
+static void test_peephole(skiatest::Reporter* reporter) {
+    SkRandom rand;
+
+    for (int j = 0; j < 100; j++) {
+        SkRandom rand2(rand.getSeed()); // remember the seed
+
+        SkPicture picture;
+        SkCanvas* canvas = picture.beginRecording(100, 100);
+
+        for (int i = 0; i < 1000; ++i) {
+            rand_op(canvas, rand);
+        }
+        picture.endRecording();
+    }
+
+    {
+        SkPicture picture;
+        SkCanvas* canvas = picture.beginRecording(100, 100);
+        SkRect rect = SkRect::MakeWH(50, 50);
+
+        for (int i = 0; i < 100; ++i) {
+            canvas->save();
+        }
+        while (canvas->getSaveCount() > 1) {
+            canvas->clipRect(rect);
+            canvas->restore();
+        }
+        picture.endRecording();
+    }
+}
+
+#ifndef SK_DEBUG
+// Only test this is in release mode. We deliberately crash in debug mode, since a valid caller
+// should never do this.
+static void test_bad_bitmap() {
+    // This bitmap has a width and height but no pixels. As a result, attempting to record it will
+    // fail.
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 100, 100);
+    SkPicture picture;
+    SkCanvas* recordingCanvas = picture.beginRecording(100, 100);
+    recordingCanvas->drawBitmap(bm, 0, 0);
+    picture.endRecording();
+
+    SkCanvas canvas;
+    canvas.drawPicture(picture);
+}
+#endif
+
+#include "SkData.h"
+#include "SkImageRef_GlobalPool.h"
+// Class to test SkPixelRef::onRefEncodedData, since there are currently no implementations in skia.
+class SkDataImageRef : public SkImageRef_GlobalPool {
+
+public:
+    SkDataImageRef(SkMemoryStream* stream)
+        : SkImageRef_GlobalPool(stream, SkBitmap::kNo_Config) {
+        SkASSERT(stream != NULL);
+        fData = stream->copyToData();
+        this->setImmutable();
+    }
+
+    ~SkDataImageRef() {
+        fData->unref();
+    }
+
+    virtual SkData* onRefEncodedData() SK_OVERRIDE {
+        fData->ref();
+        return fData;
+    }
+
+private:
+    SkData* fData;
+};
+
+#include "SkImageEncoder.h"
+
+static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
+    return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
+}
+
+static SkData* serialized_picture_from_bitmap(const SkBitmap& bitmap) {
+    SkPicture picture;
+    SkCanvas* canvas = picture.beginRecording(bitmap.width(), bitmap.height());
+    canvas->drawBitmap(bitmap, 0, 0);
+    SkDynamicMemoryWStream wStream;
+    picture.serialize(&wStream, &PNGEncodeBitmapToStream);
+    return wStream.copyToData();
+}
+
+static void test_bitmap_with_encoded_data(skiatest::Reporter* reporter) {
+    // Create a bitmap that will be encoded.
+    SkBitmap original;
+    make_bm(&original, 100, 100, SK_ColorBLUE, true);
+    SkDynamicMemoryWStream wStream;
+    if (!SkImageEncoder::EncodeStream(&wStream, original, SkImageEncoder::kPNG_Type, 100)) {
+        return;
+    }
+    SkAutoDataUnref data(wStream.copyToData());
+    SkMemoryStream memStream;
+    memStream.setData(data);
+
+    // Use the encoded bitmap as the data for an image ref.
+    SkBitmap bm;
+    SkAutoTUnref<SkDataImageRef> imageRef(SkNEW_ARGS(SkDataImageRef, (&memStream)));
+    imageRef->getInfo(&bm);
+    bm.setPixelRef(imageRef);
+
+    // Write both bitmaps to pictures, and ensure that the resulting data streams are the same.
+    // Flattening original will follow the old path of performing an encode, while flattening bm
+    // will use the already encoded data.
+    SkAutoDataUnref picture1(serialized_picture_from_bitmap(original));
+    SkAutoDataUnref picture2(serialized_picture_from_bitmap(bm));
+    REPORTER_ASSERT(reporter, picture1->equals(picture2));
+}
+
+static void test_clone_empty(skiatest::Reporter* reporter) {
+    // This is a regression test for crbug.com/172062
+    // Before the fix, we used to crash accessing a null pointer when we
+    // had a picture with no paints. This test passes by not crashing.
+    {
+        SkPicture picture;
+        picture.beginRecording(1, 1);
+        picture.endRecording();
+        SkPicture* destPicture = picture.clone();
+        REPORTER_ASSERT(reporter, NULL != destPicture);
+        destPicture->unref();
+    }
+    {
+        // Test without call to endRecording
+        SkPicture picture;
+        picture.beginRecording(1, 1);
+        SkPicture* destPicture = picture.clone();
+        REPORTER_ASSERT(reporter, NULL != destPicture);
+        destPicture->unref();
+    }
+}
+
+static void TestPicture(skiatest::Reporter* reporter) {
+#ifdef SK_DEBUG
+    test_deleting_empty_playback();
+    test_serializing_empty_picture();
+#else
+    test_bad_bitmap();
+#endif
+    test_peephole(reporter);
+    test_gatherpixelrefs(reporter);
+    test_bitmap_with_encoded_data(reporter);
+    test_clone_empty(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Pictures", PictureTestClass, TestPicture)
diff --git a/tests/PictureUtilsTest.cpp b/tests/PictureUtilsTest.cpp
new file mode 100644
index 0000000..bb01bc9
--- /dev/null
+++ b/tests/PictureUtilsTest.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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 "Test.h"
+#include "picture_utils.h"
+#include "SkString.h"
+
+static void test_filepath_creation(skiatest::Reporter* reporter) {
+    SkString result;
+    SkString filename("test");
+    SkString dir("test/path");
+    sk_tools::make_filepath(&result, dir, filename);
+    REPORTER_ASSERT(reporter, result.equals("test/path/test"));
+}
+
+static void test_get_basename(skiatest::Reporter* reporter) {
+    SkString result;
+    SkString path("/path/basename");
+    sk_tools::get_basename(&result, path);
+    REPORTER_ASSERT(reporter, result.equals("basename"));
+
+    result.reset();
+    path.set("/path/dir/");
+    sk_tools::get_basename(&result, path);
+    REPORTER_ASSERT(reporter, result.equals("dir"));
+
+    result.reset();
+    path.set("path");
+    sk_tools::get_basename(&result, path);
+    REPORTER_ASSERT(reporter, result.equals("path"));
+
+#if defined(SK_BUILD_FOR_WIN)
+    result.reset();
+    path.set("path\\winbasename");
+    sk_tools::get_basename(&result, path);
+    REPORTER_ASSERT(reporter, result.equals("winbasename"));
+
+    result.reset();
+    path.set("path\\windir\\");
+    sk_tools::get_basename(&result, path);
+    REPORTER_ASSERT(reporter, result.equals("windir"));
+#endif
+}
+
+static void TestPictureUtils(skiatest::Reporter* reporter) {
+    test_filepath_creation(reporter);
+    test_get_basename(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("PictureUtils", PictureUtilsTestClass, TestPictureUtils)
diff --git a/tests/PipeTest.cpp b/tests/PipeTest.cpp
new file mode 100644
index 0000000..acf288a
--- /dev/null
+++ b/tests/PipeTest.cpp
@@ -0,0 +1,61 @@
+
+/*
+ * 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 "SamplePipeControllers.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkGPipe.h"
+#include "SkPaint.h"
+#include "SkShader.h"
+#include "Test.h"
+
+// Ensures that the pipe gracefully handles drawing an invalid bitmap.
+static void testDrawingBadBitmap(SkCanvas* pipeCanvas) {
+    SkBitmap badBitmap;
+    badBitmap.setConfig(SkBitmap::kNo_Config, 5, 5);
+    pipeCanvas->drawBitmap(badBitmap, 0, 0);
+}
+
+// Ensure that pipe gracefully handles attempting to draw after endRecording is called on the
+// SkGPipeWriter.
+static void testDrawingAfterEndRecording(SkCanvas* canvas) {
+    PipeController pc(canvas);
+    SkGPipeWriter writer;
+    SkCanvas* pipeCanvas = writer.startRecording(&pc, SkGPipeWriter::kCrossProcess_Flag);
+    writer.endRecording();
+
+    SkBitmap bm;
+    bm.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
+    bm.allocPixels();
+    bm.eraseColor(SK_ColorTRANSPARENT);
+
+    SkShader* shader = SkShader::CreateBitmapShader(bm, SkShader::kClamp_TileMode,
+                                                    SkShader::kClamp_TileMode);
+    SkPaint paint;
+    paint.setShader(shader)->unref();
+    pipeCanvas->drawPaint(paint);
+
+    pipeCanvas->drawBitmap(bm, 0, 0);
+}
+
+static void test_pipeTests(skiatest::Reporter*) {
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, 64, 64);
+    SkCanvas canvas(bitmap);
+
+    PipeController pipeController(&canvas);
+    SkGPipeWriter writer;
+    SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
+    testDrawingBadBitmap(pipeCanvas);
+    writer.endRecording();
+
+    testDrawingAfterEndRecording(&canvas);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("PipeTest", PipeTestClass, test_pipeTests)
diff --git a/tests/PointTest.cpp b/tests/PointTest.cpp
index 876a272..de0ae84 100644
--- a/tests/PointTest.cpp
+++ b/tests/PointTest.cpp
@@ -18,7 +18,9 @@
     point.set(x, y);
     SkScalar s1 = point.length();
     SkScalar s2 = SkPoint::Length(x, y);
-    REPORTER_ASSERT(reporter, s1 == s2);
+    //The following should be exactly the same, but need not be.
+    //See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, s2));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(s1, expectedLength));
 }
 
@@ -30,16 +32,16 @@
     SkScalar oldLength = point.length();
     SkScalar returned = SkPoint::Normalize(&point);
     SkScalar newLength = point.length();
-    REPORTER_ASSERT(reporter, returned == oldLength);
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(returned, oldLength));
     REPORTER_ASSERT(reporter, SkScalarNearlyEqual(newLength, SK_Scalar1));
 }
 
-void PointTest(skiatest::Reporter* reporter) {
+static void PointTest(skiatest::Reporter* reporter) {
     test_length(reporter, SkIntToScalar(3), SkIntToScalar(4), SkIntToScalar(5));
-    test_length(reporter, SkFloatToScalar(0.6), SkFloatToScalar(0.8),
+    test_length(reporter, SkFloatToScalar(0.6f), SkFloatToScalar(0.8f),
                 SK_Scalar1);
     test_Normalize(reporter, SkIntToScalar(3), SkIntToScalar(4));
-    test_Normalize(reporter, SkFloatToScalar(0.6), SkFloatToScalar(0.8));
+    test_Normalize(reporter, SkFloatToScalar(0.6f), SkFloatToScalar(0.8f));
 }
 
 #include "TestClassDef.h"
diff --git a/tests/PremulAlphaRoundTripTest.cpp b/tests/PremulAlphaRoundTripTest.cpp
index c4ec6ab..8321c94 100644
--- a/tests/PremulAlphaRoundTripTest.cpp
+++ b/tests/PremulAlphaRoundTripTest.cpp
@@ -9,7 +9,11 @@
 #include "Test.h"
 #include "SkCanvas.h"
 #include "SkConfig8888.h"
+#include "SkDevice.h"
+
+#if SK_SUPPORT_GPU
 #include "SkGpuDevice.h"
+#endif
 
 
 namespace {
@@ -44,23 +48,25 @@
 
 void PremulAlphaRoundTripTest(skiatest::Reporter* reporter,
                               GrContext* context) {
-    SkCanvas canvas;
+    SkAutoTUnref<SkDevice> device;
     for (int dtype = 0; dtype < 2; ++dtype) {
         if (0 == dtype) {
-            canvas.setDevice(new SkDevice(SkBitmap::kARGB_8888_Config,
+            device.reset(new SkDevice(SkBitmap::kARGB_8888_Config,
                                           256,
                                           256,
-                                          false))->unref();
+                                          false));
         } else {
-#if SK_SCALAR_IS_FIXED
+#if !SK_SUPPORT_GPU || defined(SK_SCALAR_IS_FIXED)
             // GPU device known not to work in the fixed pt build.
             continue;
-#endif
-            canvas.setDevice(new SkGpuDevice(context,
+#else
+            device.reset(new SkGpuDevice(context,
                                              SkBitmap::kARGB_8888_Config,
                                              256,
-                                             256))->unref();
+                                             256));
+#endif
         }
+        SkCanvas canvas(device);
 
         SkBitmap readBmp1;
         readBmp1.setConfig(SkBitmap::kARGB_8888_Config, 256, 256);
@@ -90,10 +96,11 @@
                 reinterpret_cast<uint32_t*>(readBmp1.getPixels());
             uint32_t* pixels2 =
                 reinterpret_cast<uint32_t*>(readBmp2.getPixels());
-            for (int y = 0; y < 256; ++y) {
-                for (int x = 0; x < 256; ++x) {
+            bool success = true;
+            for (int y = 0; y < 256 && success; ++y) {
+                for (int x = 0; x < 256 && success; ++x) {
                     int i = y * 256 + x;
-                    REPORTER_ASSERT(reporter, pixels1[i] == pixels2[i]);
+                    REPORTER_ASSERT(reporter, success = pixels1[i] == pixels2[i]);
                 }
             }
         }
@@ -103,4 +110,3 @@
 
 #include "TestClassDef.h"
 DEFINE_GPUTESTCLASS("PremulAlphaRoundTripTest", PremulAlphaRoundTripTestClass, PremulAlphaRoundTripTest)
-
diff --git a/tests/QuickRejectTest.cpp b/tests/QuickRejectTest.cpp
index 8cde327..9d87bff 100644
--- a/tests/QuickRejectTest.cpp
+++ b/tests/QuickRejectTest.cpp
@@ -29,9 +29,13 @@
         return false;
     }
 
-    virtual Factory getFactory() SK_OVERRIDE {
-        return NULL;
+#ifdef SK_DEVELOPER
+    virtual void toString(SkString* str) const SK_OVERRIDE {
+        str->append("TestLooper:");
     }
+#endif
+
+    SK_DECLARE_UNFLATTENABLE_OBJECT()
 };
 
 static void test_drawBitmap(skiatest::Reporter* reporter) {
@@ -43,7 +47,7 @@
     SkBitmap dst;
     dst.setConfig(SkBitmap::kARGB_8888_Config, 10, 10);
     dst.allocPixels();
-    dst.eraseColor(0);
+    dst.eraseColor(SK_ColorTRANSPARENT);
 
     SkCanvas canvas(dst);
     SkPaint  paint;
@@ -56,7 +60,7 @@
     REPORTER_ASSERT(reporter, 0xFFFFFFFF == *dst.getAddr32(5, 5));
 
     // reverify we are clear again
-    dst.eraseColor(0);
+    dst.eraseColor(SK_ColorTRANSPARENT);
     REPORTER_ASSERT(reporter, 0 == *dst.getAddr32(5, 5));
 
     // if the bitmap is clipped out, we don't draw it
diff --git a/tests/RTreeTest.cpp b/tests/RTreeTest.cpp
new file mode 100644
index 0000000..6962c89
--- /dev/null
+++ b/tests/RTreeTest.cpp
@@ -0,0 +1,145 @@
+
+/*
+ * 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 "Test.h"
+#include "SkRandom.h"
+#include "SkRTree.h"
+#include "SkTSort.h"
+
+static const size_t MIN_CHILDREN = 6;
+static const size_t MAX_CHILDREN = 11;
+
+static const int NUM_RECTS = 200;
+static const size_t NUM_ITERATIONS = 100;
+static const size_t NUM_QUERIES = 50;
+
+struct DataRect {
+    SkIRect rect;
+    void* data;
+};
+
+static SkIRect random_rect(SkRandom& rand) {
+    SkIRect rect = {0,0,0,0};
+    while (rect.isEmpty()) {
+        rect.fLeft   = rand.nextS() % 1000;
+        rect.fRight  = rand.nextS() % 1000;
+        rect.fTop    = rand.nextS() % 1000;
+        rect.fBottom = rand.nextS() % 1000;
+        rect.sort();
+    }
+    return rect;
+}
+
+static void random_data_rects(SkRandom& rand, DataRect out[], int n) {
+    for (int i = 0; i < n; ++i) {
+        out[i].rect = random_rect(rand);
+        out[i].data = reinterpret_cast<void*>(i);
+    }
+}
+
+static bool verify_query(SkIRect query, DataRect rects[],
+                         SkTDArray<void*>& found) {
+    SkTDArray<void*> expected;
+    // manually intersect with every rectangle
+    for (int i = 0; i < NUM_RECTS; ++i) {
+        if (SkIRect::IntersectsNoEmptyCheck(query, rects[i].rect)) {
+            expected.push(rects[i].data);
+        }
+    }
+
+    if (expected.count() != found.count()) {
+        return false;
+    }
+
+    if (0 == expected.count()) {
+        return true;
+    }
+
+    // Just cast to long since sorting by the value of the void*'s was being problematic...
+    SkTQSort(reinterpret_cast<long*>(expected.begin()),
+             reinterpret_cast<long*>(expected.end() - 1));
+    SkTQSort(reinterpret_cast<long*>(found.begin()),
+             reinterpret_cast<long*>(found.end() - 1));
+    return found == expected;
+}
+
+static void runQueries(skiatest::Reporter* reporter, SkRandom& rand, DataRect rects[],
+                       SkRTree& tree) {
+    for (size_t i = 0; i < NUM_QUERIES; ++i) {
+        SkTDArray<void*> hits;
+        SkIRect query = random_rect(rand);
+        tree.search(query, &hits);
+        REPORTER_ASSERT(reporter, verify_query(query, rects, hits));
+    }
+}
+
+static void TestRTree(skiatest::Reporter* reporter) {
+    DataRect rects[NUM_RECTS];
+    SkRandom rand;
+    SkRTree* rtree = SkRTree::Create(MIN_CHILDREN, MAX_CHILDREN);
+    SkAutoUnref au(rtree);
+    REPORTER_ASSERT(reporter, NULL != rtree);
+
+    int expectedDepthMin = -1;
+    int expectedDepthMax = -1;
+
+    int tmp = NUM_RECTS;
+    while (tmp > 0) {
+        tmp -= static_cast<int>(pow(static_cast<double>(MAX_CHILDREN),
+                                static_cast<double>(expectedDepthMin + 1)));
+        ++expectedDepthMin;
+    }
+
+    tmp = NUM_RECTS;
+    while (tmp > 0) {
+        tmp -= static_cast<int>(pow(static_cast<double>(MIN_CHILDREN),
+                                static_cast<double>(expectedDepthMax + 1)));
+        ++expectedDepthMax;
+    }
+
+    for (size_t i = 0; i < NUM_ITERATIONS; ++i) {
+        random_data_rects(rand, rects, NUM_RECTS);
+
+        // First try bulk-loaded inserts
+        for (int i = 0; i < NUM_RECTS; ++i) {
+            rtree->insert(rects[i].data, rects[i].rect, true);
+        }
+        rtree->flushDeferredInserts();
+        runQueries(reporter, rand, rects, *rtree);
+        REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount());
+        REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() &&
+                                  expectedDepthMax >= rtree->getDepth());
+        rtree->clear();
+        REPORTER_ASSERT(reporter, 0 == rtree->getCount());
+
+        // Then try immediate inserts
+        for (int i = 0; i < NUM_RECTS; ++i) {
+            rtree->insert(rects[i].data, rects[i].rect);
+        }
+        runQueries(reporter, rand, rects, *rtree);
+        REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount());
+        REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() &&
+                                  expectedDepthMax >= rtree->getDepth());
+        rtree->clear();
+        REPORTER_ASSERT(reporter, 0 == rtree->getCount());
+
+        // And for good measure try immediate inserts, but in reversed order
+        for (int i = NUM_RECTS - 1; i >= 0; --i) {
+            rtree->insert(rects[i].data, rects[i].rect);
+        }
+        runQueries(reporter, rand, rects, *rtree);
+        REPORTER_ASSERT(reporter, NUM_RECTS == rtree->getCount());
+        REPORTER_ASSERT(reporter, expectedDepthMin <= rtree->getDepth() &&
+                                  expectedDepthMax >= rtree->getDepth());
+        rtree->clear();
+        REPORTER_ASSERT(reporter, 0 == rtree->getCount());
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("RTree", RTreeTestClass, TestRTree)
diff --git a/tests/ReadPixelsTest.cpp b/tests/ReadPixelsTest.cpp
index 4e0fcc6..a33e819 100644
--- a/tests/ReadPixelsTest.cpp
+++ b/tests/ReadPixelsTest.cpp
@@ -8,13 +8,18 @@
 
 #include "Test.h"
 #include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkMathPriv.h"
 #include "SkRegion.h"
+#if SK_SUPPORT_GPU
 #include "SkGpuDevice.h"
+#endif
 
 
 static const int DEV_W = 100, DEV_H = 100;
 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
-static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, 
+static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
                                                 DEV_H * SK_Scalar1);
 
 namespace {
@@ -46,7 +51,7 @@
     }
     return SkPremultiplyARGBInline(a, r, g, b);
 }
-    
+
 SkPMColor getBitmapColor(int x, int y, int w, int h) {
     int n = y * w + x;
 
@@ -88,6 +93,9 @@
             g = static_cast<U8CPU>(c[1]);
             b = static_cast<U8CPU>(c[2]);
             break;
+        default:
+            SkDEBUGFAIL("Unexpected Config8888");
+            return 0;
     }
     if (*premul) {
         r = SkMulDiv255Ceiling(r, a);
@@ -101,7 +109,7 @@
     static SkBitmap bmp;
     if (bmp.isNull()) {
         bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
-        bool alloc = bmp.allocPixels();
+        SkDEBUGCODE(bool alloc =) bmp.allocPixels();
         SkASSERT(alloc);
         SkAutoLockPixels alp(bmp);
         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
@@ -120,7 +128,7 @@
     canvas->drawBitmap(bmp, 0, 0, &paint);
     canvas->restore();
 }
-    
+
 void fillBitmap(SkBitmap* bitmap) {
     SkASSERT(bitmap->lockPixelsAreWritable());
     SkAutoLockPixels alp(*bitmap);
@@ -167,7 +175,7 @@
     SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
     SkASSERT(!bitmap.isNull());
     SkASSERT(checkCanvasPixels || checkBitmapPixels);
-    
+
     int bw = bitmap.width();
     int bh = bitmap.height();
 
@@ -176,14 +184,13 @@
     if (!clippedSrcRect.intersect(srcRect)) {
         clippedSrcRect.setEmpty();
     }
-    bool failed = false;
     SkAutoLockPixels alp(bitmap);
     intptr_t pixels = reinterpret_cast<intptr_t>(bitmap.getPixels());
     for (int by = 0; by < bh; ++by) {
         for (int bx = 0; bx < bw; ++bx) {
             int devx = bx + srcRect.fLeft;
             int devy = by + srcRect.fTop;
-            
+
             uint32_t pixel = *reinterpret_cast<SkPMColor*>(pixels + by * bitmap.rowBytes() + bx * bitmap.bytesPerPixel());
 
             if (clippedSrcRect.contains(devx, devy)) {
@@ -194,27 +201,27 @@
                     bool check;
                     REPORTER_ASSERT(reporter, check = checkPixel(pmPixel, canvasPixel, didPremul));
                     if (!check) {
-                        failed = true;
+                        return false;
                     }
                 }
             } else if (checkBitmapPixels) {
                 REPORTER_ASSERT(reporter, getBitmapColor(bx, by, bw, bh) == pixel);
                 if (getBitmapColor(bx, by, bw, bh) != pixel) {
-                    failed = true;
+                    return false;
                 }
             }
         }
     }
-    return !failed;
+    return true;
 }
 
 enum BitmapInit {
     kFirstBitmapInit = 0,
-    
+
     kNoPixels_BitmapInit = kFirstBitmapInit,
     kTight_BitmapInit,
     kRowBytes_BitmapInit,
-    
+
     kBitmapInitCnt
 };
 
@@ -248,8 +255,6 @@
 }
 
 void ReadPixelsTest(skiatest::Reporter* reporter, GrContext* context) {
-    SkCanvas canvas;
-    
     const SkIRect testRects[] = {
         // entire thing
         DEV_RECT,
@@ -298,22 +303,24 @@
     };
 
     for (int dtype = 0; dtype < 2; ++dtype) {
-
+        SkAutoTUnref<SkDevice> device;
         if (0 == dtype) {
-            canvas.setDevice(new SkDevice(SkBitmap::kARGB_8888_Config,
+            device.reset(new SkDevice(SkBitmap::kARGB_8888_Config,
                                           DEV_W,
                                           DEV_H,
-                                          false))->unref();
+                                          false));
         } else {
-#if SK_SCALAR_IS_FIXED
-            // GPU device known not to work in the fixed pt build.
+// GPU device known not to work in the fixed pt build.
+#if defined(SK_SCALAR_IS_FIXED) || !SK_SUPPORT_GPU
             continue;
-#endif
-            canvas.setDevice(new SkGpuDevice(context,
+#else
+            device.reset(new SkGpuDevice(context,
                                              SkBitmap::kARGB_8888_Config,
                                              DEV_W,
-                                             DEV_H))->unref();
+                                             DEV_H));
+#endif
         }
+        SkCanvas canvas(device);
         fillCanvas(&canvas);
 
         static const SkCanvas::Config8888 gReadConfigs[] = {
@@ -347,16 +354,19 @@
                     if (startsWithPixels) {
                         fillBitmap(&bmp);
                     }
-
+                    uint32_t idBefore = canvas.getDevice()->accessBitmap(false).getGenerationID();
                     bool success =
                         canvas.readPixels(&bmp, srcRect.fLeft,
                                           srcRect.fTop, config8888);
+                    uint32_t idAfter = canvas.getDevice()->accessBitmap(false).getGenerationID();
 
                     // we expect to succeed when the read isn't fully clipped
                     // out.
                     bool expectSuccess = SkIRect::Intersects(srcRect, DEV_RECT);
                     // determine whether we expected the read to succeed.
                     REPORTER_ASSERT(reporter, success == expectSuccess);
+                    // read pixels should never change the gen id
+                    REPORTER_ASSERT(reporter, idBefore == idAfter);
 
                     if (success || startsWithPixels) {
                         checkRead(reporter, bmp, srcRect.fLeft, srcRect.fTop,
@@ -388,4 +398,3 @@
 
 #include "TestClassDef.h"
 DEFINE_GPUTESTCLASS("ReadPixels", ReadPixelsTestClass, ReadPixelsTest)
-
diff --git a/tests/ReadWriteAlphaTest.cpp b/tests/ReadWriteAlphaTest.cpp
new file mode 100644
index 0000000..eea78ce
--- /dev/null
+++ b/tests/ReadWriteAlphaTest.cpp
@@ -0,0 +1,112 @@
+
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+// This test is specific to the GPU backend.
+#if SK_SUPPORT_GPU
+
+#include "Test.h"
+#include "SkGpuDevice.h"
+
+static const int X_SIZE = 12;
+static const int Y_SIZE = 12;
+
+static void ReadWriteAlphaTest(skiatest::Reporter* reporter, GrContext* context) {
+
+#if SK_SCALAR_IS_FIXED
+    // GPU device known not to work in the fixed pt build.
+    return;
+#endif
+
+    unsigned char textureData[X_SIZE][Y_SIZE];
+
+    memset(textureData, 0, X_SIZE * Y_SIZE);
+
+    GrTextureDesc desc;
+
+    // let Skia know we will be using this texture as a render target
+    desc.fFlags     = kRenderTarget_GrTextureFlagBit;
+    // it is a single channel texture
+    desc.fConfig    = kAlpha_8_GrPixelConfig;
+    desc.fWidth     = X_SIZE;
+    desc.fHeight    = Y_SIZE;
+
+    // We are initializing the texture with zeros here
+    GrTexture* texture = context->createUncachedTexture(desc, textureData, 0);
+    if (!texture) {
+        return;
+    }
+
+    GrAutoUnref au(texture);
+
+    // create a distinctive texture
+    for (int y = 0; y < Y_SIZE; ++y) {
+        for (int x = 0; x < X_SIZE; ++x) {
+            textureData[x][y] = x*Y_SIZE+y;
+        }
+    }
+
+    // upload the texture
+    texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                         textureData, 0);
+
+    unsigned char readback[X_SIZE][Y_SIZE];
+
+    // clear readback to something non-zero so we can detect readback failures
+    memset(readback, 0x1, X_SIZE * Y_SIZE);
+
+    // read the texture back
+    texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                        readback, 0);
+
+    // make sure the original & read back versions match
+    bool match = true;
+
+    for (int y = 0; y < Y_SIZE; ++y) {
+        for (int x = 0; x < X_SIZE; ++x) {
+            if (textureData[x][y] != readback[x][y]) {
+                match = false;
+            }
+        }
+    }
+
+    REPORTER_ASSERT(reporter, match);
+
+    // Now try writing on the single channel texture
+    SkAutoTUnref<SkDevice> device(new SkGpuDevice(context, texture->asRenderTarget()));
+    SkCanvas canvas(device);
+
+    SkPaint paint;
+
+    const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10);
+
+    paint.setColor(SK_ColorWHITE);
+
+    canvas.drawRect(rect, paint);
+
+    texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
+                        readback, 0);
+
+    match = true;
+
+    for (int y = 0; y < Y_SIZE; ++y) {
+        for (int x = 0; x < X_SIZE; ++x) {
+            if (0xFF != readback[x][y]) {
+                match = false;
+            }
+        }
+    }
+
+    REPORTER_ASSERT(reporter, match);
+}
+
+#ifndef SK_BUILD_FOR_ANDROID
+#include "TestClassDef.h"
+DEFINE_GPUTESTCLASS("ReadWriteAlpha", ReadWriteAlphaTestClass, ReadWriteAlphaTest)
+
+#endif
+#endif
diff --git a/tests/Reader32Test.cpp b/tests/Reader32Test.cpp
index 13f2fc4..d81287b 100644
--- a/tests/Reader32Test.cpp
+++ b/tests/Reader32Test.cpp
@@ -85,4 +85,3 @@
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Reader32", Reader32Class, Tests)
-
diff --git a/tests/RefCntTest.cpp b/tests/RefCntTest.cpp
new file mode 100644
index 0000000..ed771e7
--- /dev/null
+++ b/tests/RefCntTest.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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 "SkTypes.h"
+#include "Test.h"
+
+#include "SkRefCnt.h"
+#include "SkThreadUtils.h"
+#include "SkWeakRefCnt.h"
+#include "SkTRefArray.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 test_refarray(skiatest::Reporter* reporter) {
+    REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter);
+
+    const int N = 10;
+    SkTRefArray<InstCounterClass>* array = SkTRefArray<InstCounterClass>::Create(N);
+
+    REPORTER_ASSERT(reporter, 1 == array->getRefCnt());
+    REPORTER_ASSERT(reporter, N == array->count());
+
+    REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter);
+    array->unref();
+    REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter);
+
+    // Now test the copy factory
+
+    int i;
+    InstCounterClass* src = new InstCounterClass[N];
+    REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter);
+    for (i = 0; i < N; ++i) {
+        REPORTER_ASSERT(reporter, i == src[i].fCount);
+    }
+
+    array = SkTRefArray<InstCounterClass>::Create(src, N);
+    REPORTER_ASSERT(reporter, 1 == array->getRefCnt());
+    REPORTER_ASSERT(reporter, N == array->count());
+
+    REPORTER_ASSERT(reporter, 2*N == InstCounterClass::gInstCounter);
+    for (i = 0; i < N; ++i) {
+        REPORTER_ASSERT(reporter, i == (*array)[i].fCount);
+    }
+
+    delete[] src;
+    REPORTER_ASSERT(reporter, N == InstCounterClass::gInstCounter);
+
+    for (i = 0; i < N; ++i) {
+        REPORTER_ASSERT(reporter, i == (*array)[i].fCount);
+    }
+    array->unref();
+    REPORTER_ASSERT(reporter, 0 == InstCounterClass::gInstCounter);
+}
+
+static void bounce_ref(void* data) {
+    SkRefCnt* ref = static_cast<SkRefCnt*>(data);
+    for (int i = 0; i < 100000; ++i) {
+        ref->ref();
+        ref->unref();
+    }
+}
+
+static void test_refCnt(skiatest::Reporter* reporter) {
+    SkRefCnt* ref = new SkRefCnt();
+
+    SkThread thing1(bounce_ref, ref);
+    SkThread thing2(bounce_ref, ref);
+
+    thing1.setProcessorAffinity(0);
+    thing2.setProcessorAffinity(23);
+
+    SkASSERT(thing1.start());
+    SkASSERT(thing2.start());
+
+    thing1.join();
+    thing2.join();
+
+    REPORTER_ASSERT(reporter, ref->getRefCnt() == 1);
+    ref->unref();
+}
+
+static void bounce_weak_ref(void* data) {
+    SkWeakRefCnt* ref = static_cast<SkWeakRefCnt*>(data);
+    for (int i = 0; i < 100000; ++i) {
+        if (ref->try_ref()) {
+            ref->unref();
+        }
+    }
+}
+
+static void bounce_weak_weak_ref(void* data) {
+    SkWeakRefCnt* ref = static_cast<SkWeakRefCnt*>(data);
+    for (int i = 0; i < 100000; ++i) {
+        ref->weak_ref();
+        ref->weak_unref();
+    }
+}
+
+static void test_weakRefCnt(skiatest::Reporter* reporter) {
+    SkWeakRefCnt* ref = new SkWeakRefCnt();
+
+    SkThread thing1(bounce_ref, ref);
+    SkThread thing2(bounce_ref, ref);
+    SkThread thing3(bounce_weak_ref, ref);
+    SkThread thing4(bounce_weak_weak_ref, ref);
+
+    thing1.setProcessorAffinity(0);
+    thing2.setProcessorAffinity(23);
+    thing3.setProcessorAffinity(2);
+    thing4.setProcessorAffinity(17);
+
+    SkASSERT(thing1.start());
+    SkASSERT(thing2.start());
+    SkASSERT(thing3.start());
+    SkASSERT(thing4.start());
+
+    thing1.join();
+    thing2.join();
+    thing3.join();
+    thing4.join();
+
+    REPORTER_ASSERT(reporter, ref->getRefCnt() == 1);
+    REPORTER_ASSERT(reporter, ref->getWeakCnt() == 1);
+    ref->unref();
+}
+
+static void test_refCntTests(skiatest::Reporter* reporter) {
+    test_refCnt(reporter);
+    test_weakRefCnt(reporter);
+    test_refarray(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("RefCnt", RefCntTestClass, test_refCntTests)
diff --git a/tests/RefDictTest.cpp b/tests/RefDictTest.cpp
index 83d1aef..38a990a 100644
--- a/tests/RefDictTest.cpp
+++ b/tests/RefDictTest.cpp
@@ -9,8 +9,14 @@
 #include "SkRefDict.h"
 
 class TestRC : public SkRefCnt {
+public:
+    SK_DECLARE_INST_COUNT(TestRC)
+private:
+    typedef SkRefCnt INHERITED;
 };
 
+SK_DEFINE_INST_COUNT(TestRC)
+
 static void TestRefDict(skiatest::Reporter* reporter) {
     TestRC    data0, data1;
     SkRefDict dict;
diff --git a/tests/RegionTest.cpp b/tests/RegionTest.cpp
index a1bf699..5d3946e 100644
--- a/tests/RegionTest.cpp
+++ b/tests/RegionTest.cpp
@@ -9,6 +9,190 @@
 #include "SkRegion.h"
 #include "SkRandom.h"
 
+static void Union(SkRegion* rgn, const SkIRect& rect) {
+    rgn->op(rect, SkRegion::kUnion_Op);
+}
+
+#define TEST_NO_INTERSECT(rgn, rect)    REPORTER_ASSERT(reporter, !rgn.intersects(rect))
+#define TEST_INTERSECT(rgn, rect)       REPORTER_ASSERT(reporter, rgn.intersects(rect))
+#define TEST_NO_CONTAINS(rgn, rect)     REPORTER_ASSERT(reporter, !rgn.contains(rect))
+
+// inspired by http://code.google.com/p/skia/issues/detail?id=958
+//
+static void test_fromchrome(skiatest::Reporter* reporter) {
+    SkRegion r;
+    Union(&r, SkIRect::MakeXYWH(0, 0, 1, 1));
+    TEST_NO_INTERSECT(r, SkIRect::MakeXYWH(0, 0, 0, 0));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 0, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, 0, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, -1, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, -1, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, -1, 3, 3));
+
+    Union(&r, SkIRect::MakeXYWH(0, 0, 3, 3));
+    Union(&r, SkIRect::MakeXYWH(10, 0, 3, 3));
+    Union(&r, SkIRect::MakeXYWH(0, 10, 13, 3));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, -1, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(2, -1, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(2, 2, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(-1, 2, 2, 2));
+
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(9, -1, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(12, -1, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(12, 2, 2, 2));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(9, 2, 2, 2));
+
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, -1, 13, 5));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(1, -1, 11, 5));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(2, -1, 9, 5));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(2, -1, 8, 5));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(3, -1, 8, 5));
+
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 1, 13, 1));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(1, 1, 11, 1));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(2, 1, 9, 1));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(2, 1, 8, 1));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(3, 1, 8, 1));
+
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 0, 13, 13));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 1, 13, 11));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 2, 13, 9));
+    TEST_INTERSECT(r, SkIRect::MakeXYWH(0, 2, 13, 8));
+
+
+    // These test SkRegion::contains(Rect) and SkRegion::contains(Region)
+
+    SkRegion container;
+    Union(&container, SkIRect::MakeXYWH(0, 0, 40, 20));
+    Union(&container, SkIRect::MakeXYWH(30, 20, 10, 20));
+    TEST_NO_CONTAINS(container, SkIRect::MakeXYWH(0, 0, 10, 39));
+    TEST_NO_CONTAINS(container, SkIRect::MakeXYWH(29, 0, 10, 39));
+
+    {
+        SkRegion rgn;
+        Union(&rgn, SkIRect::MakeXYWH(0, 0, 10, 10));
+        Union(&rgn, SkIRect::MakeLTRB(5, 10, 20, 20));
+        TEST_INTERSECT(rgn, SkIRect::MakeXYWH(15, 0, 5, 11));
+    }
+}
+
+static void test_empties(skiatest::Reporter* reporter) {
+    SkRegion valid(SkIRect::MakeWH(10, 10));
+    SkRegion empty, empty2;
+
+    REPORTER_ASSERT(reporter, empty.isEmpty());
+    REPORTER_ASSERT(reporter, !valid.isEmpty());
+
+    // test intersects
+    REPORTER_ASSERT(reporter, !empty.intersects(empty2));
+    REPORTER_ASSERT(reporter, !valid.intersects(empty));
+
+    // test contains
+    REPORTER_ASSERT(reporter, !empty.contains(empty2));
+    REPORTER_ASSERT(reporter, !valid.contains(empty));
+    REPORTER_ASSERT(reporter, !empty.contains(valid));
+}
+
+enum {
+    W = 256,
+    H = 256
+};
+
+static SkIRect randRect(SkRandom& rand) {
+    int x = rand.nextU() % W;
+    int y = rand.nextU() % H;
+    int w = rand.nextU() % W;
+    int h = rand.nextU() % H;
+    return SkIRect::MakeXYWH(x, y, w >> 1, h >> 1);
+}
+
+static void randRgn(SkRandom& rand, SkRegion* rgn, int n) {
+    rgn->setEmpty();
+    for (int i = 0; i < n; ++i) {
+        rgn->op(randRect(rand), SkRegion::kUnion_Op);
+    }
+}
+
+static bool slow_contains(const SkRegion& outer, const SkRegion& inner) {
+    SkRegion tmp;
+    tmp.op(outer, inner, SkRegion::kUnion_Op);
+    return outer == tmp;
+}
+
+static bool slow_contains(const SkRegion& outer, const SkIRect& r) {
+    SkRegion tmp;
+    tmp.op(outer, SkRegion(r), SkRegion::kUnion_Op);
+    return outer == tmp;
+}
+
+static bool slow_intersects(const SkRegion& outer, const SkRegion& inner) {
+    SkRegion tmp;
+    return tmp.op(outer, inner, SkRegion::kIntersect_Op);
+}
+
+static void test_contains_iter(skiatest::Reporter* reporter, const SkRegion& rgn) {
+    SkRegion::Iterator iter(rgn);
+    while (!iter.done()) {
+        SkIRect r = iter.rect();
+        REPORTER_ASSERT(reporter, rgn.contains(r));
+        r.inset(-1, -1);
+        REPORTER_ASSERT(reporter, !rgn.contains(r));
+        iter.next();
+    }
+}
+
+static void contains_proc(skiatest::Reporter* reporter,
+                          const SkRegion& a, const SkRegion& b) {
+    // test rgn
+    bool c0 = a.contains(b);
+    bool c1 = slow_contains(a, b);
+    REPORTER_ASSERT(reporter, c0 == c1);
+
+    // test rect
+    SkIRect r = a.getBounds();
+    r.inset(r.width()/4, r.height()/4);
+    c0 = a.contains(r);
+    c1 = slow_contains(a, r);
+    REPORTER_ASSERT(reporter, c0 == c1);
+
+    test_contains_iter(reporter, a);
+    test_contains_iter(reporter, b);
+}
+
+static void test_intersects_iter(skiatest::Reporter* reporter, const SkRegion& rgn) {
+    SkRegion::Iterator iter(rgn);
+    while (!iter.done()) {
+        SkIRect r = iter.rect();
+        REPORTER_ASSERT(reporter, rgn.intersects(r));
+        r.inset(-1, -1);
+        REPORTER_ASSERT(reporter, rgn.intersects(r));
+        iter.next();
+    }
+}
+
+static void intersects_proc(skiatest::Reporter* reporter,
+                          const SkRegion& a, const SkRegion& b) {
+    bool c0 = a.intersects(b);
+    bool c1 = slow_intersects(a, b);
+    REPORTER_ASSERT(reporter, c0 == c1);
+
+    test_intersects_iter(reporter, a);
+    test_intersects_iter(reporter, b);
+}
+
+static void test_proc(skiatest::Reporter* reporter,
+                      void (*proc)(skiatest::Reporter*,
+                                   const SkRegion& a, const SkRegion&)) {
+    SkRandom rand;
+    for (int i = 0; i < 10000; ++i) {
+        SkRegion outer;
+        randRgn(rand, &outer, 8);
+        SkRegion inner;
+        randRgn(rand, &inner, 2);
+        proc(reporter, outer, inner);
+    }
+}
+
 static void rand_rect(SkIRect* rect, SkRandom& rand) {
     int bits = 6;
     int shift = 32 - bits;
@@ -44,7 +228,7 @@
         { 2, 2, 3, 3 },
     };
     REPORTER_ASSERT(reporter, test_rects(r2, SK_ARRAY_COUNT(r2)));
-    
+
     const SkIRect rects[] = {
         { 0, 0, 1, 2 },
         { 2, 1, 3, 3 },
@@ -64,6 +248,11 @@
         }
         REPORTER_ASSERT(reporter, test_rects(rect, N));
     }
+
+    test_proc(reporter, contains_proc);
+    test_proc(reporter, intersects_proc);
+    test_empties(reporter);
+    test_fromchrome(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/RoundRectTest.cpp b/tests/RoundRectTest.cpp
new file mode 100644
index 0000000..a8387d5
--- /dev/null
+++ b/tests/RoundRectTest.cpp
@@ -0,0 +1,330 @@
+/*
+ * 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 "Test.h"
+#include "SkRRect.h"
+
+static const SkScalar kWidth = 100.0f;
+static const SkScalar kHeight = 100.0f;
+
+static void test_inset(skiatest::Reporter* reporter) {
+    SkRRect rr, rr2;
+    SkRect r = { 0, 0, 100, 100 };
+
+    rr.setRect(r);
+    rr.inset(-20, -20, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isRect());
+
+    rr.inset(20, 20, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isRect());
+
+    rr.inset(r.width()/2, r.height()/2, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isEmpty());
+
+    rr.setRectXY(r, 20, 20);
+    rr.inset(19, 19, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isSimple());
+    rr.inset(20, 20, &rr2);
+    REPORTER_ASSERT(reporter, rr2.isRect());
+}
+
+// Test out the basic API entry points
+static void test_round_rect_basic(skiatest::Reporter* reporter) {
+    // Test out initialization methods
+    SkPoint zeroPt = { 0, 0 };
+    SkRRect empty;
+
+    empty.setEmpty();
+
+    REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
+    REPORTER_ASSERT(reporter, empty.rect().isEmpty());
+
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter, zeroPt == empty.radii((SkRRect::Corner) i));
+    }
+
+    //----
+    SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+
+    SkRRect rr1;
+    rr1.setRect(rect);
+
+    REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
+    REPORTER_ASSERT(reporter, rr1.rect() == rect);
+
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter, zeroPt == rr1.radii((SkRRect::Corner) i));
+    }
+
+    //----
+    SkPoint halfPoint = { SkScalarHalf(kWidth), SkScalarHalf(kHeight) };
+    SkRRect rr2;
+    rr2.setOval(rect);
+
+    REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr2.type());
+    REPORTER_ASSERT(reporter, rr2.rect() == rect);
+
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter,
+                        rr2.radii((SkRRect::Corner) i).equalsWithinTolerance(halfPoint));
+    }
+
+    //----
+    SkPoint p = { 5, 5 };
+    SkRRect rr3;
+    rr3.setRectXY(rect, p.fX, p.fY);
+
+    REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr3.type());
+    REPORTER_ASSERT(reporter, rr3.rect() == rect);
+
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter, p == rr3.radii((SkRRect::Corner) i));
+    }
+
+    //----
+    SkPoint radii[4] = { { 5, 5 }, { 5, 5 }, { 5, 5 }, { 5, 5 } };
+
+    SkRRect rr4;
+    rr4.setRectRadii(rect, radii);
+
+    REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr4.type());
+    REPORTER_ASSERT(reporter, rr4.rect() == rect);
+
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter, radii[i] == rr4.radii((SkRRect::Corner) i));
+    }
+
+    //----
+    SkPoint radii2[4] = { { 0, 0 }, { 0, 0 }, { 50, 50 }, { 20, 50 } };
+
+    SkRRect rr5;
+    rr5.setRectRadii(rect, radii2);
+
+    REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr5.type());
+    REPORTER_ASSERT(reporter, rr5.rect() == rect);
+
+    for (int i = 0; i < 4; ++i) {
+        REPORTER_ASSERT(reporter, radii2[i] == rr5.radii((SkRRect::Corner) i));
+    }
+
+    // Test out == & !=
+    REPORTER_ASSERT(reporter, empty != rr3);
+    REPORTER_ASSERT(reporter, rr3 == rr4);
+    REPORTER_ASSERT(reporter, rr4 != rr5);
+}
+
+// Test out the cases when the RR degenerates to a rect
+static void test_round_rect_rects(skiatest::Reporter* reporter) {
+    SkRect r;
+    static const SkPoint pts[] = {
+        // Upper Left
+        { -SK_Scalar1, -SK_Scalar1 },               // out
+        { SK_Scalar1, SK_Scalar1 },                 // in
+        // Upper Right
+        { SkIntToScalar(101), -SK_Scalar1},         // out
+        { SkIntToScalar(99), SK_Scalar1 },          // in
+        // Lower Right
+        { SkIntToScalar(101), SkIntToScalar(101) }, // out
+        { SkIntToScalar(99), SkIntToScalar(99) },   // in
+        // Lower Left
+        { -SK_Scalar1, SkIntToScalar(101) },        // out
+        { SK_Scalar1, SkIntToScalar(99) },          // in
+        // Middle
+        { SkIntToScalar(50), SkIntToScalar(50) }    // in
+    };
+    static const bool isIn[] = { false, true, false, true, false, true, false, true, true };
+
+    SkASSERT(SK_ARRAY_COUNT(pts) == SK_ARRAY_COUNT(isIn));
+
+    //----
+    SkRRect empty;
+
+    empty.setEmpty();
+
+    REPORTER_ASSERT(reporter, SkRRect::kEmpty_Type == empty.type());
+    r = empty.rect();
+    REPORTER_ASSERT(reporter, 0 == r.fLeft && 0 == r.fTop && 0 == r.fRight && 0 == r.fBottom);
+
+    //----
+    SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+    SkRRect rr1;
+    rr1.setRectXY(rect, 0, 0);
+
+    REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr1.type());
+    r = rr1.rect();
+    REPORTER_ASSERT(reporter, rect == r);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
+        REPORTER_ASSERT(reporter, isIn[i] == rr1.contains(pts[i].fX, pts[i].fY));
+    }
+
+    //----
+    SkPoint radii[4] = { { 0, 0 }, { 0, 0 }, { 0, 0 }, { 0, 0 } };
+
+    SkRRect rr2;
+    rr2.setRectRadii(rect, radii);
+
+    REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
+    r = rr2.rect();
+    REPORTER_ASSERT(reporter, rect == r);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
+        REPORTER_ASSERT(reporter, isIn[i] == rr2.contains(pts[i].fX, pts[i].fY));
+    }
+
+    //----
+    SkPoint radii2[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
+
+    SkRRect rr3;
+    rr3.setRectRadii(rect, radii2);
+    REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr3.type());
+}
+
+// Test out the cases when the RR degenerates to an oval
+static void test_round_rect_ovals(skiatest::Reporter* reporter) {
+    static const SkScalar kEps = 0.1f;
+    static const SkScalar kWidthTol = SkScalarHalf(kWidth) * (SK_Scalar1 - SK_ScalarRoot2Over2);
+    static const SkScalar kHeightTol = SkScalarHalf(kHeight) * (SK_Scalar1 - SK_ScalarRoot2Over2);
+    static const SkPoint pts[] = {
+        // Upper Left
+        { kWidthTol - kEps, kHeightTol - kEps },       // out
+        { kWidthTol + kEps, kHeightTol + kEps },       // in
+        // Upper Right
+        { kWidth + kEps - kWidthTol, kHeightTol - kEps },     // out
+        { kWidth - kEps - kWidthTol, kHeightTol + kEps },      // in
+        // Lower Right
+        { kWidth + kEps - kWidthTol, kHeight + kEps - kHeightTol },   // out
+        { kWidth - kEps - kWidthTol, kHeight - kEps - kHeightTol },   // in
+        // Lower Left
+        { kWidthTol - kEps, kHeight + kEps - kHeightTol },     //out
+        { kWidthTol + kEps, kHeight - kEps - kHeightTol },     // in
+        // Middle
+        { SkIntToScalar(50), SkIntToScalar(50) } // in
+    };
+    static const bool isIn[] = { false, true, false, true, false, true, false, true, true };
+
+    SkASSERT(SK_ARRAY_COUNT(pts) == SK_ARRAY_COUNT(isIn));
+
+    //----
+    SkRect oval;
+    SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+    SkRRect rr1;
+    rr1.setRectXY(rect, SkScalarHalf(kWidth), SkScalarHalf(kHeight));
+
+    REPORTER_ASSERT(reporter, SkRRect::kOval_Type == rr1.type());
+    oval = rr1.rect();
+    REPORTER_ASSERT(reporter, oval == rect);
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
+        REPORTER_ASSERT(reporter, isIn[i] == rr1.contains(pts[i].fX, pts[i].fY));
+    }
+}
+
+// Test out the non-degenerate RR cases
+static void test_round_rect_general(skiatest::Reporter* reporter) {
+    static const SkScalar kEps = 0.1f;
+    static const SkScalar kDist20 = 20 * (SK_Scalar1 - SK_ScalarRoot2Over2);
+    static const SkPoint pts[] = {
+        // Upper Left
+        { kDist20 - kEps, kDist20 - kEps },       // out
+        { kDist20 + kEps, kDist20 + kEps },       // in
+        // Upper Right
+        { kWidth + kEps - kDist20, kDist20 - kEps },     // out
+        { kWidth - kEps - kDist20, kDist20 + kEps },      // in
+        // Lower Right
+        { kWidth + kEps - kDist20, kHeight + kEps - kDist20 },   // out
+        { kWidth - kEps - kDist20, kHeight - kEps - kDist20 },   // in
+        // Lower Left
+        { kDist20 - kEps, kHeight + kEps - kDist20 },     //out
+        { kDist20 + kEps, kHeight - kEps - kDist20 },     // in
+        // Middle
+        { SkIntToScalar(50), SkIntToScalar(50) } // in
+    };
+    static const bool isIn[] = { false, true, false, true, false, true, false, true, true };
+
+    SkASSERT(SK_ARRAY_COUNT(pts) == SK_ARRAY_COUNT(isIn));
+
+    //----
+    SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+    SkRRect rr1;
+    rr1.setRectXY(rect, 20, 20);
+
+    REPORTER_ASSERT(reporter, SkRRect::kSimple_Type == rr1.type());
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
+        REPORTER_ASSERT(reporter, isIn[i] == rr1.contains(pts[i].fX, pts[i].fY));
+    }
+
+    //----
+    static const SkScalar kDist50 = 50*(SK_Scalar1 - SK_ScalarRoot2Over2);
+    static const SkPoint pts2[] = {
+        // Upper Left
+        { -SK_Scalar1, -SK_Scalar1 },           // out
+        { SK_Scalar1, SK_Scalar1 },             // in
+        // Upper Right
+        { kWidth + kEps - kDist20, kDist20 - kEps },     // out
+        { kWidth - kEps - kDist20, kDist20 + kEps },     // in
+        // Lower Right
+        { kWidth + kEps - kDist50, kHeight + kEps - kDist50 },   // out
+        { kWidth - kEps - kDist50, kHeight - kEps - kDist50 },   // in
+        // Lower Left
+        { kDist20 - kEps, kHeight + kEps - kDist50 },     // out
+        { kDist20 + kEps, kHeight - kEps - kDist50 },     // in
+        // Middle
+        { SkIntToScalar(50), SkIntToScalar(50) }  // in
+    };
+
+    SkASSERT(SK_ARRAY_COUNT(pts2) == SK_ARRAY_COUNT(isIn));
+
+    SkPoint radii[4] = { { 0, 0 }, { 20, 20 }, { 50, 50 }, { 20, 50 } };
+
+    SkRRect rr2;
+    rr2.setRectRadii(rect, radii);
+
+    REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr2.type());
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
+        REPORTER_ASSERT(reporter, isIn[i] == rr2.contains(pts2[i].fX, pts2[i].fY));
+    }
+}
+
+// Test out questionable-parameter handling
+static void test_round_rect_iffy_parameters(skiatest::Reporter* reporter) {
+
+    // When the radii exceed the base rect they are proportionally scaled down
+    // to fit
+    SkRect rect = SkRect::MakeLTRB(0, 0, kWidth, kHeight);
+    SkPoint radii[4] = { { 50, 100 }, { 100, 50 }, { 50, 100 }, { 100, 50 } };
+
+    SkRRect rr1;
+    rr1.setRectRadii(rect, radii);
+
+    REPORTER_ASSERT(reporter, SkRRect::kComplex_Type == rr1.type());
+
+    const SkPoint& p = rr1.radii(SkRRect::kUpperLeft_Corner);
+
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fX, 33.33333f));
+    REPORTER_ASSERT(reporter, SkScalarNearlyEqual(p.fY, 66.66666f));
+
+    // Negative radii should be capped at zero
+    SkRRect rr2;
+    rr2.setRectXY(rect, -10, -20);
+
+    REPORTER_ASSERT(reporter, SkRRect::kRect_Type == rr2.type());
+
+    const SkPoint& p2 = rr2.radii(SkRRect::kUpperLeft_Corner);
+
+    REPORTER_ASSERT(reporter, 0.0f == p2.fX);
+    REPORTER_ASSERT(reporter, 0.0f == p2.fY);
+}
+
+static void TestRoundRect(skiatest::Reporter* reporter) {
+    test_round_rect_basic(reporter);
+    test_round_rect_rects(reporter);
+    test_round_rect_ovals(reporter);
+    test_round_rect_general(reporter);
+    test_round_rect_iffy_parameters(reporter);
+    test_inset(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("RoundRect", TestRoundRectClass, TestRoundRect)
diff --git a/tests/SHA1Test.cpp b/tests/SHA1Test.cpp
new file mode 100644
index 0000000..dde828c
--- /dev/null
+++ b/tests/SHA1Test.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include "Test.h"
+#include "SkSHA1.h"
+
+static bool digests_equal(const SkSHA1::Digest& expectedDigest, const SkSHA1::Digest& computedDigest) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(expectedDigest.data); ++i) {
+        if (expectedDigest.data[i] != computedDigest.data[i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static struct SHA1Test {
+    const char* message;
+    const unsigned long int repeatCount;
+    const SkSHA1::Digest digest;
+} sha1_tests[] = {
+    // Reference tests from RFC3174 Section 7.3 ( http://www.ietf.org/rfc/rfc3174.txt )
+    { "abc", 1, {{ 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }} },
+    { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 1, {{ 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }} },
+    { "a", 1000000, {{ 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F }} },
+    { "0123456701234567012345670123456701234567012345670123456701234567", 10, {{ 0xDE, 0xA3, 0x56, 0xA2, 0xCD, 0xDD, 0x90, 0xC7, 0xA7, 0xEC, 0xED, 0xC5, 0xEB, 0xB5, 0x63, 0x93, 0x4F, 0x46, 0x04, 0x52 }} },
+
+    // Reference tests from running GNU sha1sum on test input
+    { "The quick brown fox jumps over the lazy dog", 1, {{ 0x2f, 0xd4, 0xe1, 0xc6, 0x7a, 0x2d, 0x28, 0xfc, 0xed, 0x84, 0x9e, 0xe1, 0xbb, 0x76, 0xe7, 0x39, 0x1b, 0x93, 0xeb, 0x12 }} },
+    { "", 1, {{ 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 }} },
+};
+
+static void sha1_test(const SHA1Test& test, skiatest::Reporter* reporter) {
+    size_t len = strlen(test.message);
+
+    SkSHA1 context;
+    for (unsigned long int i = 0; i < test.repeatCount; ++i) {
+        context.update(reinterpret_cast<const uint8_t*>(test.message), len);
+    }
+    SkSHA1::Digest digest;
+    context.finish(digest);
+
+    REPORTER_ASSERT(reporter, digests_equal(test.digest, digest));
+}
+
+static void TestSHA1(skiatest::Reporter* reporter) {
+    for (size_t i = 0; i < SK_ARRAY_COUNT(sha1_tests); ++i) {
+        sha1_test(sha1_tests[i], reporter);
+    }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("SHA1", SHA1TestClass, TestSHA1)
diff --git a/tests/ScalarTest.cpp b/tests/ScalarTest.cpp
index d2c05ab..8a16645 100644
--- a/tests/ScalarTest.cpp
+++ b/tests/ScalarTest.cpp
@@ -10,8 +10,58 @@
 #include "SkMath.h"
 #include "SkPoint.h"
 #include "SkRandom.h"
+#include "SkRect.h"
 
-#ifdef SK_CAN_USE_FLOAT
+struct PointSet {
+    const SkPoint* fPts;
+    size_t         fCount;
+    bool           fIsFinite;
+};
+
+static void test_isRectFinite(skiatest::Reporter* reporter) {
+#ifdef SK_SCALAR_IS_FLOAT
+    static const SkPoint gF0[] = {
+        { 0, 0 }, { 1, 1 }
+    };
+    static const SkPoint gF1[] = {
+        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }
+    };
+
+    static const SkPoint gI0[] = {
+        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarNaN, 3 }, { 2, 3 },
+    };
+    static const SkPoint gI1[] = {
+        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarNaN }, { 2, 3 },
+    };
+    static const SkPoint gI2[] = {
+        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { SK_ScalarInfinity, 3 }, { 2, 3 },
+    };
+    static const SkPoint gI3[] = {
+        { 0, 0 }, { 1, 1 }, { 99.234f, -42342 }, { 3, SK_ScalarInfinity }, { 2, 3 },
+    };
+
+    static const struct {
+        const SkPoint* fPts;
+        size_t         fCount;
+        bool           fIsFinite;
+    } gSets[] = {
+        { gF0, SK_ARRAY_COUNT(gF0), true },
+        { gF1, SK_ARRAY_COUNT(gF1), true },
+
+        { gI0, SK_ARRAY_COUNT(gI0), false },
+        { gI1, SK_ARRAY_COUNT(gI1), false },
+        { gI2, SK_ARRAY_COUNT(gI2), false },
+        { gI3, SK_ARRAY_COUNT(gI3), false },
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(gSets); ++i) {
+        SkRect r;
+        r.set(gSets[i].fPts, gSets[i].fCount);
+        bool rectIsFinite = !r.isEmpty();
+        REPORTER_ASSERT(reporter, gSets[i].fIsFinite == rectIsFinite);
+    }
+#endif
+}
 
 static bool isFinite_int(float x) {
     uint32_t bits = SkFloat2Bits(x);    // need unsigned for our shifts
@@ -20,7 +70,7 @@
 }
 
 static bool isFinite_float(float x) {
-    return sk_float_isfinite(x);
+    return SkToBool(sk_float_isfinite(x));
 }
 
 static bool isFinite_mulzero(float x) {
@@ -42,8 +92,6 @@
 // return true if both floats are finite
 typedef bool (*IsFiniteProc2)(float, float, IsFiniteProc1);
 
-#endif
-
 enum FloatClass {
     kFinite,
     kInfinite,
@@ -58,13 +106,19 @@
     REPORTER_ASSERT(reporter, !!sk_float_isnan(value) == (fc == kNaN));
 }
 
+#if defined _WIN32
+#pragma warning ( push )
+// we are intentionally causing an overflow here
+//      (warning C4756: overflow in constant arithmetic)
+#pragma warning ( disable : 4756 )
+#endif
+
 static void test_isfinite(skiatest::Reporter* reporter) {
-#ifdef SK_CAN_USE_FLOAT
     struct Rec {
         float   fValue;
         bool    fIsFinite;
     };
-    
+
     float max = 3.402823466e+38f;
     float inf = max * max;
     float nan = inf * 0;
@@ -78,16 +132,16 @@
     test_floatclass(reporter, -nan, kNaN);
 
     const Rec data[] = {
-        {   0,          true    },
-        {   1,          true    },
-        {  -1,          true    },
-        {  max * 0.75,  true    },
-        {  max,         true    },
-        {  -max * 0.75, true    },
-        {  -max,        true    },
-        {  inf,         false   },
-        { -inf,         false   },
-        {  nan,         false   },
+        {   0,           true    },
+        {   1,           true    },
+        {  -1,           true    },
+        {  max * 0.75f,  true    },
+        {  max,          true    },
+        {  -max * 0.75f, true    },
+        {  -max,         true    },
+        {  inf,          false   },
+        { -inf,          false   },
+        {  nan,          false   },
     };
 
     const IsFiniteProc1 gProc1[] = {
@@ -116,7 +170,7 @@
             const Rec& rec1 = data[j];
             for (size_t k = 0; k < SK_ARRAY_COUNT(gProc1); ++k) {
                 IsFiniteProc1 proc1 = gProc1[k];
-                
+
                 for (size_t m = 0; m < SK_ARRAY_COUNT(gProc2); ++m) {
                     bool finite = gProc2[m](rec0.fValue, rec1.fValue, proc1);
                     bool finite2 = rec0.fIsFinite && rec1.fIsFinite;
@@ -125,13 +179,17 @@
             }
         }
     }
-#endif
+
+    test_isRectFinite(reporter);
 }
 
+#if defined _WIN32
+#pragma warning ( pop )
+#endif
+
 static void TestScalar(skiatest::Reporter* reporter) {
     test_isfinite(reporter);
 }
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Scalar", TestScalarClass, TestScalar)
-
diff --git a/tests/ShaderOpacityTest.cpp b/tests/ShaderOpacityTest.cpp
index a8177ee..0af25d1 100644
--- a/tests/ShaderOpacityTest.cpp
+++ b/tests/ShaderOpacityTest.cpp
@@ -13,9 +13,9 @@
 static void test_bitmap(skiatest::Reporter* reporter) {
     SkBitmap bmp;
     bmp.setConfig(SkBitmap::kARGB_8888_Config, 2, 2);
-    
+
     // test 1: bitmap without pixel data
-    SkShader* shader = SkShader::CreateBitmapShader(bmp, 
+    SkShader* shader = SkShader::CreateBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, !shader->isOpaque());
@@ -25,7 +25,7 @@
     bmp.allocPixels();
 
     // test 2: not opaque by default
-    shader = SkShader::CreateBitmapShader(bmp, 
+    shader = SkShader::CreateBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, !shader->isOpaque());
@@ -33,7 +33,7 @@
 
     // test 3: explicitly opaque
     bmp.setIsOpaque(true);
-    shader = SkShader::CreateBitmapShader(bmp, 
+    shader = SkShader::CreateBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, shader->isOpaque());
@@ -41,7 +41,7 @@
 
     // test 4: explicitly not opaque
     bmp.setIsOpaque(false);
-    shader = SkShader::CreateBitmapShader(bmp, 
+    shader = SkShader::CreateBitmapShader(bmp,
         SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
     REPORTER_ASSERT(reporter, shader);
     REPORTER_ASSERT(reporter, !shader->isOpaque());
@@ -102,7 +102,7 @@
     SkColorShader colorShader3(SkColorSetARGB(0x7F,0,0,0));
     REPORTER_ASSERT(reporter, !colorShader3.isOpaque());
 
-    // with inherrited color, shader must declare itself as opaque, 
+    // with inherrited color, shader must declare itself as opaque,
     // since lack of opacity will depend solely on the paint
     SkColorShader colorShader4;
     REPORTER_ASSERT(reporter, colorShader4.isOpaque());
diff --git a/tests/Sk64Test.cpp b/tests/Sk64Test.cpp
index 64eef75..50b7ec7 100644
--- a/tests/Sk64Test.cpp
+++ b/tests/Sk64Test.cpp
@@ -167,7 +167,6 @@
 
         REPORTER_ASSERT(reporter, check == w);
 
-#ifdef SK_CAN_USE_FLOAT
         wide.setMul(rand.nextS(), rand.nextS());
         wide.abs();
         denom = wide.getSqrt();
@@ -196,7 +195,6 @@
                      i, dnumer, ddenom, ddiv, dfixdiv, fixdiv);
         }
         REPORTER_ASSERT(reporter, SkAbs32(diff) <= 1);
-#endif
     }
 #endif
 }
diff --git a/tests/SortTest.cpp b/tests/SortTest.cpp
index 9b4642f..28c6e68 100644
--- a/tests/SortTest.cpp
+++ b/tests/SortTest.cpp
@@ -7,11 +7,10 @@
  */
 #include "Test.h"
 #include "SkRandom.h"
-#include "SkTSearch.h"
 #include "SkTSort.h"
 
 extern "C" {
-    int compare_int(const void* a, const void* b) {
+    static int compare_int(const void* a, const void* b) {
         return *(const int*)a - *(const int*)b;
     }
 }
@@ -23,31 +22,41 @@
 }
 
 static void check_sort(skiatest::Reporter* reporter, const char label[],
-                       const int array[], int n) {
-    for (int j = 1; j < n; j++) {
-        if (array[j-1] > array[j]) {
+                       const int array[], const int reference[], int n) {
+    for (int j = 0; j < n; ++j) {
+        if (array[j] != reference[j]) {
             SkString str;
-           str.printf("%sSort [%d] failed %d %d", label, n,
-                      array[j-1], array[j]);
+            str.printf("%sSort [%d] failed %d %d", label, n, array[j], reference[j]);
             reporter->reportFailed(str);
         }
     }
 }
 
 static void TestSort(skiatest::Reporter* reporter) {
-    int         array[500];
+    /** An array of random numbers to be sorted. */
+    int randomArray[500];
+    /** The reference sort of the random numbers. */
+    int sortedArray[SK_ARRAY_COUNT(randomArray)];
+    /** The random numbers are copied into this array, sorted by an SkSort,
+        then this array is compared against the reference sort. */
+    int workingArray[SK_ARRAY_COUNT(randomArray)];
     SkRandom    rand;
 
     for (int i = 0; i < 10000; i++) {
-        int count = rand.nextRangeU(1, SK_ARRAY_COUNT(array));
+        int count = rand.nextRangeU(1, SK_ARRAY_COUNT(randomArray));
+        rand_array(rand, randomArray, count);
 
-        rand_array(rand, array, count);
-        SkQSort(array, count, sizeof(int), compare_int);
-        check_sort(reporter, "Quick", array, count);
+        // Use qsort as the reference sort.
+        memcpy(sortedArray, randomArray, sizeof(randomArray));
+        qsort(sortedArray, count, sizeof(sortedArray[0]), compare_int);
 
-        rand_array(rand, array, count);
-        SkTHeapSort<int>(array, count);
-        check_sort(reporter, "Heap", array, count);
+        memcpy(workingArray, randomArray, sizeof(randomArray));
+        SkTHeapSort<int>(workingArray, count);
+        check_sort(reporter, "Heap", workingArray, sortedArray, count);
+
+        memcpy(workingArray, randomArray, sizeof(randomArray));
+        SkTQSort<int>(workingArray, workingArray + count - 1);
+        check_sort(reporter, "Quick", workingArray, sortedArray, count);
     }
 }
 
diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp
index 85b1b2e..ed51aa7 100644
--- a/tests/StreamTest.cpp
+++ b/tests/StreamTest.cpp
@@ -108,18 +108,18 @@
         0xFFFFFD, 0xFFFFFE, 0xFFFFFF, 0x1000000, 0x1000001,
         0x7FFFFFFE, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF
     };
-    
-    
+
+
     size_t i;
     char buffer[sizeof(sizes) * 4];
-    
+
     SkMemoryWStream wstream(buffer, sizeof(buffer));
     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
         bool success = wstream.writePackedUInt(sizes[i]);
         REPORTER_ASSERT(reporter, success);
     }
     wstream.flush();
-    
+
     SkMemoryStream rstream(buffer, sizeof(buffer));
     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
         size_t n = rstream.readPackedUInt();
@@ -130,10 +130,29 @@
     }
 }
 
+// Test that setting an SkMemoryStream to a NULL data does not result in a crash when calling
+// methods that access fData.
+static void TestDereferencingData(SkMemoryStream* memStream) {
+    memStream->read(NULL, 0);
+    memStream->getMemoryBase();
+    SkAutoDataUnref data(memStream->copyToData());
+}
+
+static void TestNullData() {
+    SkData* nullData = NULL;
+    SkMemoryStream memStream(nullData);
+    TestDereferencingData(&memStream);
+
+    memStream.setData(nullData);
+    TestDereferencingData(&memStream);
+
+}
+
 static void TestStreams(skiatest::Reporter* reporter) {
     TestRStream(reporter);
     TestWStream(reporter);
     TestPackedUInt(reporter);
+    TestNullData();
 }
 
 #include "TestClassDef.h"
diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp
index 52a038d..2c5026c 100644
--- a/tests/StringTest.cpp
+++ b/tests/StringTest.cpp
@@ -28,7 +28,7 @@
         va_end(args);                               \
     } while (0)
 
-void printfAnalog(char* buffer, int size, const char format[], ...) {
+static void printfAnalog(char* buffer, int size, const char format[], ...) {
     ARGS_TO_BUFFER(format, buffer, size);
 }
 
@@ -56,6 +56,25 @@
     REPORTER_ASSERT(reporter, a.equals("hello"));
     REPORTER_ASSERT(reporter, !a.equals("help"));
 
+    REPORTER_ASSERT(reporter,  a.startsWith("hell"));
+    REPORTER_ASSERT(reporter,  a.startsWith('h'));
+    REPORTER_ASSERT(reporter, !a.startsWith( "ell"));
+    REPORTER_ASSERT(reporter, !a.startsWith( 'e'));
+    REPORTER_ASSERT(reporter,  a.startsWith(""));
+    REPORTER_ASSERT(reporter,  a.endsWith("llo"));
+    REPORTER_ASSERT(reporter,  a.endsWith('o'));
+    REPORTER_ASSERT(reporter, !a.endsWith("ll" ));
+    REPORTER_ASSERT(reporter, !a.endsWith('l'));
+    REPORTER_ASSERT(reporter,  a.endsWith(""));
+    REPORTER_ASSERT(reporter,  a.contains("he"));
+    REPORTER_ASSERT(reporter,  a.contains("ll"));
+    REPORTER_ASSERT(reporter,  a.contains("lo"));
+    REPORTER_ASSERT(reporter,  a.contains("hello"));
+    REPORTER_ASSERT(reporter, !a.contains("hellohello"));
+    REPORTER_ASSERT(reporter,  a.contains(""));
+    REPORTER_ASSERT(reporter,  a.contains('e'));
+    REPORTER_ASSERT(reporter, !a.contains('z'));
+
     SkString    e(a);
     SkString    f("hello");
     SkString    g("helloz", 5);
@@ -135,7 +154,7 @@
     REPORTER_ASSERT(reporter, buffer[18] == ' ');
     REPORTER_ASSERT(reporter, buffer[19] == 0);
     REPORTER_ASSERT(reporter, buffer[20] == 'a');
-    
+
 }
 
 #include "TestClassDef.h"
diff --git a/tests/StrokeTest.cpp b/tests/StrokeTest.cpp
new file mode 100644
index 0000000..9336bed
--- /dev/null
+++ b/tests/StrokeTest.cpp
@@ -0,0 +1,63 @@
+/*
+ * 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 "Test.h"
+#include "SkPaint.h"
+#include "SkPath.h"
+#include "SkRect.h"
+#include "SkStroke.h"
+
+static bool equal(const SkRect& a, const SkRect& b) {
+    return  SkScalarNearlyEqual(a.left(), b.left()) &&
+            SkScalarNearlyEqual(a.top(), b.top()) &&
+            SkScalarNearlyEqual(a.right(), b.right()) &&
+            SkScalarNearlyEqual(a.bottom(), b.bottom());
+}
+
+static void test_strokerect(skiatest::Reporter* reporter) {
+    const SkScalar width = SkIntToScalar(10);
+    SkPaint paint;
+
+    paint.setStyle(SkPaint::kStroke_Style);
+    paint.setStrokeWidth(width);
+
+    SkRect r = { 0, 0, SkIntToScalar(200), SkIntToScalar(100) };
+
+    SkRect outer(r);
+    outer.outset(width/2, width/2);
+
+    static const SkPaint::Join joins[] = {
+        SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join
+    };
+
+    for (size_t i = 0; i < SK_ARRAY_COUNT(joins); ++i) {
+        paint.setStrokeJoin(joins[i]);
+
+        SkPath path, fillPath;
+        path.addRect(r);
+        paint.getFillPath(path, &fillPath);
+
+        REPORTER_ASSERT(reporter, equal(outer, fillPath.getBounds()));
+
+        bool isMiter = SkPaint::kMiter_Join == joins[i];
+        SkRect nested[2];
+        REPORTER_ASSERT(reporter, fillPath.isNestedRects(nested) == isMiter);
+        if (isMiter) {
+            SkRect inner(r);
+            inner.inset(width/2, width/2);
+            REPORTER_ASSERT(reporter, equal(nested[0], outer));
+            REPORTER_ASSERT(reporter, equal(nested[1], inner));
+        }
+    }
+}
+
+static void TestStroke(skiatest::Reporter* reporter) {
+    test_strokerect(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Stroke", TestStrokeClass, TestStroke)
diff --git a/tests/TLSTest.cpp b/tests/TLSTest.cpp
new file mode 100644
index 0000000..38eb322
--- /dev/null
+++ b/tests/TLSTest.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "Test.h"
+#include "SkGraphics.h"
+#include "SkPaint.h"
+#include "SkTLS.h"
+#include "SkThreadUtils.h"
+
+static void thread_main(void*) {
+    SkGraphics::SetTLSFontCacheLimit(1 * 1024 * 1024);
+
+    const char text[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+    size_t len = strlen(text);
+
+    SkPaint paint;
+
+    for (int j = 0; j < 10; ++j) {
+        for (int i = 9; i <= 48; ++i) {
+            paint.setTextSize(SkIntToScalar(i));
+            paint.setAntiAlias(false);
+            paint.measureText(text, len);
+            paint.setAntiAlias(true);
+            paint.measureText(text, len);
+        }
+    }
+}
+
+static void test_threads(SkThread::entryPointProc proc) {
+    SkThread* threads[8];
+    int N = SK_ARRAY_COUNT(threads);
+    int i;
+
+    for (i = 0; i < N; ++i) {
+        threads[i] = new SkThread(proc);
+    }
+
+    for (i = 0; i < N; ++i) {
+        threads[i]->start();
+    }
+
+    for (i = 0; i < N; ++i) {
+        threads[i]->join();
+    }
+
+    for (i = 0; i < N; ++i) {
+        delete threads[i];
+    }
+}
+
+static int32_t gCounter;
+
+static void* FakeCreateTLS() {
+    sk_atomic_inc(&gCounter);
+    return NULL;
+}
+
+static void FakeDeleteTLS(void* unused) {
+    sk_atomic_dec(&gCounter);
+}
+
+static void testTLSDestructor(void* unused) {
+    SkTLS::Get(FakeCreateTLS, FakeDeleteTLS);
+}
+
+static void TestTLS(skiatest::Reporter* reporter) {
+    // TODO: Disabled for now to work around
+    // http://code.google.com/p/skia/issues/detail?id=619
+    // ('flaky segfault in TLS test on Shuttle_Ubuntu12 buildbots')
+    if( false ) test_threads(&thread_main);
+
+    // Test to ensure that at thread destruction, TLS destructors
+    // have been called.
+    test_threads(&testTLSDestructor);
+    REPORTER_ASSERT(reporter, 0 == gCounter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("TLS", TLSClass, TestTLS)
diff --git a/tests/Test.cpp b/tests/Test.cpp
index 62df731..de5e793 100644
--- a/tests/Test.cpp
+++ b/tests/Test.cpp
@@ -7,9 +7,16 @@
  */
 #include "Test.h"
 
+#include "SkTLazy.h"
+
+#if SK_SUPPORT_GPU
 #include "GrContext.h"
 #include "gl/SkNativeGLContext.h"
-#include "SkTLazy.h"
+#else
+class GrContext;
+#endif
+
+SK_DEFINE_INST_COUNT(skiatest::Reporter)
 
 using namespace skiatest;
 
@@ -76,22 +83,34 @@
 
 ///////////////////////////////////////////////////////////////////////////////
 
+#if SK_SUPPORT_GPU
+    static SkAutoTUnref<SkNativeGLContext> gGLContext;
+    static SkAutoTUnref<GrContext> gGrContext;
+#endif
+
+void GpuTest::DestroyContext() {
+#if SK_SUPPORT_GPU
+    // preserve this order, we want gGrContext destroyed before gGLContext
+    gGrContext.reset(NULL);
+    gGLContext.reset(NULL);
+#endif
+}
+
 
 GrContext* GpuTest::GetContext() {
-    // preserve this order, we want gGrContext destroyed after gEGLContext
-    static SkTLazy<SkNativeGLContext> gGLContext;
-    static SkAutoTUnref<GrContext> gGrContext;
-
+#if SK_SUPPORT_GPU
     if (NULL == gGrContext.get()) {
-        gGLContext.init();
+        gGLContext.reset(new SkNativeGLContext());
         if (gGLContext.get()->init(800, 600)) {
-            GrPlatform3DContext ctx = reinterpret_cast<GrPlatform3DContext>(gGLContext.get()->gl());
-            gGrContext.reset(GrContext::Create(kOpenGL_Shaders_GrEngine, ctx));
+            GrBackendContext ctx = reinterpret_cast<GrBackendContext>(gGLContext.get()->gl());
+            gGrContext.reset(GrContext::Create(kOpenGL_GrBackend, ctx));
         }
     }
     if (gGLContext.get()) {
         gGLContext.get()->makeCurrent();
     }
     return gGrContext.get();
+#else
+    return NULL;
+#endif
 }
-
diff --git a/tests/Test.h b/tests/Test.h
index 4ca1971..2cbd00b 100644
--- a/tests/Test.h
+++ b/tests/Test.h
@@ -21,6 +21,7 @@
 
     class Reporter : public SkRefCnt {
     public:
+        SK_DECLARE_INST_COUNT(Reporter)
         Reporter();
 
         enum Result {
@@ -58,11 +59,11 @@
         void reportFailed(const SkString& desc) {
             this->report(desc.c_str(), kFailed);
         }
-        
+
         bool getCurrSuccess() const {
             return fCurrTestSuccess;
         }
-        
+
     protected:
         virtual void onStart(Test*) {}
         virtual void onReport(const char desc[], Result) {}
@@ -102,10 +103,11 @@
         GpuTest() : Test() {
             fContext = GetContext();
         }
+        static GrContext* GetContext();
+        static void DestroyContext();
     protected:
         GrContext* fContext;
     private:
-        static GrContext* GetContext();
     };
 
     typedef SkTRegistry<Test*, void*> TestRegistry;
diff --git a/tests/TestSize.cpp b/tests/TestSize.cpp
index 97a6668..6a9a887 100644
--- a/tests/TestSize.cpp
+++ b/tests/TestSize.cpp
@@ -10,7 +10,7 @@
 
 static void TestISize(skiatest::Reporter* reporter) {
     SkISize  a, b;
-    
+
     a.set(0, 0);
     REPORTER_ASSERT(reporter, a.isEmpty());
     a.set(5, -5);
@@ -19,7 +19,7 @@
     REPORTER_ASSERT(reporter, a.isEmpty());
     b.set(5, 0);
     REPORTER_ASSERT(reporter, a == b);
-    
+
     a.set(3, 5);
     REPORTER_ASSERT(reporter, !a.isEmpty());
     b = a;
@@ -32,13 +32,13 @@
 
 static void TestSize(skiatest::Reporter* reporter) {
     TestISize(reporter);
-    
+
     SkSize a, b;
     int ix = 5;
     int iy = 3;
     SkScalar x = SkIntToScalar(ix);
     SkScalar y = SkIntToScalar(iy);
-    
+
     a.set(0, 0);
     REPORTER_ASSERT(reporter, a.isEmpty());
     a.set(x, -x);
@@ -47,7 +47,7 @@
     REPORTER_ASSERT(reporter, a.isEmpty());
     b.set(x, 0);
     REPORTER_ASSERT(reporter, a == b);
-    
+
     a.set(y, x);
     REPORTER_ASSERT(reporter, !a.isEmpty());
     b = a;
@@ -56,7 +56,7 @@
     REPORTER_ASSERT(reporter, !(a != b));
     REPORTER_ASSERT(reporter,
                     a.fWidth == b.fWidth && a.fHeight == b.fHeight);
-    
+
     SkISize ia;
     ia.set(ix, iy);
     a.set(x, y);
diff --git a/tests/TileGridTest.cpp b/tests/TileGridTest.cpp
new file mode 100644
index 0000000..1946b9f
--- /dev/null
+++ b/tests/TileGridTest.cpp
@@ -0,0 +1,135 @@
+
+/*
+ * 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 "Test.h"
+#include "SkTileGrid.h"
+#include "SkTileGridPicture.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+
+enum Tile {
+    kTopLeft_Tile = 0x1,
+    kTopRight_Tile = 0x2,
+    kBottomLeft_Tile = 0x4,
+    kBottomRight_Tile = 0x8,
+
+    kAll_Tile = kTopLeft_Tile | kTopRight_Tile | kBottomLeft_Tile | kBottomRight_Tile,
+};
+
+namespace {
+class MockCanvas : public SkCanvas {
+public:
+    MockCanvas(SkDevice* device) : SkCanvas(device)
+    {}
+
+    virtual void drawRect(const SkRect& rect, const SkPaint& paint)
+    {
+        // This capture occurs before quick reject.
+        fRects.push(rect);
+    }
+
+    SkTDArray<SkRect> fRects;
+};
+}
+
+class TileGridTest {
+public:
+    static void verifyTileHits(skiatest::Reporter* reporter, SkIRect rect, uint32_t tileMask) {
+        SkTileGrid grid(10, 10, 2, 2, NULL);
+        grid.insert(NULL, rect, false);
+        REPORTER_ASSERT(reporter, grid.tile(0,0).count() ==
+            ((tileMask & kTopLeft_Tile)? 1 : 0));
+        REPORTER_ASSERT(reporter, grid.tile(1,0).count() ==
+            ((tileMask & kTopRight_Tile)? 1 : 0));
+        REPORTER_ASSERT(reporter, grid.tile(0,1).count() ==
+            ((tileMask & kBottomLeft_Tile)? 1 : 0));
+        REPORTER_ASSERT(reporter, grid.tile(1,1).count() ==
+            ((tileMask & kBottomRight_Tile)? 1 : 0));
+    }
+
+    static void TestUnalignedQuery(skiatest::Reporter* reporter) {
+        // Use SkTileGridPicture to generate a SkTileGrid with a helper
+        SkTileGridPicture picture(10, 10, 20, 20);
+        SkRect rect1 = SkRect::MakeXYWH(SkIntToScalar(0), SkIntToScalar(0),
+            SkIntToScalar(8), SkIntToScalar(8));
+        SkRect rect2 = SkRect::MakeXYWH(SkIntToScalar(11), SkIntToScalar(11),
+            SkIntToScalar(1), SkIntToScalar(1));
+        SkCanvas* canvas = picture.beginRecording(20, 20, SkPicture::kOptimizeForClippedPlayback_RecordingFlag);
+        SkPaint paint;
+        canvas->drawRect(rect1, paint);
+        canvas->drawRect(rect2, paint);
+        picture.endRecording();
+
+        SkBitmap store;
+        store.setConfig(SkBitmap::kARGB_8888_Config, 1, 1);
+        store.allocPixels();
+
+        // Test parts of top-left tile
+        {
+            SkDevice device(store);
+            MockCanvas mockCanvas(&device);
+            picture.draw(&mockCanvas);
+            REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
+            REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
+        }
+        {
+            SkDevice device(store);
+            MockCanvas mockCanvas(&device);
+            mockCanvas.translate(SkFloatToScalar(-7.99f), SkFloatToScalar(-7.99f));
+            picture.draw(&mockCanvas);
+            REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
+            REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
+        }
+        // Corner overlap
+        {
+            SkDevice device(store);
+            MockCanvas mockCanvas(&device);
+            mockCanvas.translate(SkFloatToScalar(-9.5f), SkFloatToScalar(-9.5f));
+            picture.draw(&mockCanvas);
+            REPORTER_ASSERT(reporter, 2 == mockCanvas.fRects.count());
+            REPORTER_ASSERT(reporter, rect1 == mockCanvas.fRects[0]);
+            REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[1]);
+        }
+        // Intersect bottom right tile, but does not overlap rect 2
+        {
+            SkDevice device(store);
+            MockCanvas mockCanvas(&device);
+            mockCanvas.translate(SkFloatToScalar(-16.0f), SkFloatToScalar(-16.0f));
+            picture.draw(&mockCanvas);
+            REPORTER_ASSERT(reporter, 1 == mockCanvas.fRects.count());
+            REPORTER_ASSERT(reporter, rect2 == mockCanvas.fRects[0]);
+        }
+    }
+
+    static void Test(skiatest::Reporter* reporter) {
+        // Out of bounds
+        verifyTileHits(reporter, SkIRect::MakeXYWH(30, 0, 1, 1),  0);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(0, 30, 1, 1),  0);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(-10, 0, 1, 1),  0);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(0, -10, 1, 1),  0);
+
+        // Dilation for AA consideration
+        verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 8, 8),  kTopLeft_Tile);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(0, 0, 9, 9),  kAll_Tile);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(10, 10, 1, 1),  kAll_Tile);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(11, 11, 1, 1),  kBottomRight_Tile);
+
+        // BBoxes that overlap tiles
+        verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 1),  kTopLeft_Tile | kTopRight_Tile);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 1, 10),  kTopLeft_Tile |
+                       kBottomLeft_Tile);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(5, 5, 10, 10),  kAll_Tile);
+        verifyTileHits(reporter, SkIRect::MakeXYWH(-10, -10, 40, 40),  kAll_Tile);
+
+        TestUnalignedQuery(reporter);
+    }
+};
+
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("TileGrid", TileGridTestClass, TileGridTest::Test)
diff --git a/tests/ToUnicode.cpp b/tests/ToUnicode.cpp
index c0a945a..ea9e258 100644
--- a/tests/ToUnicode.cpp
+++ b/tests/ToUnicode.cpp
@@ -7,8 +7,6 @@
  */
 
 
-#include <string>
-
 #include "Test.h"
 #include "SkData.h"
 #include "SkPDFTypes.h"
@@ -18,13 +16,13 @@
 static bool stream_equals(const SkDynamicMemoryWStream& stream, size_t offset,
                           const char* buffer, size_t len) {
     SkAutoDataUnref data(stream.copyToData());
-    if (offset + len > data.size()) {
+    if (offset + len > data->size()) {
         return false;
     }
     if (len != strlen(buffer)) {
         return false;
     }
-    return memcmp(data.bytes() + offset, buffer, len) == 0;
+    return memcmp(data->bytes() + offset, buffer, len) == 0;
 }
 
 void append_cmap_sections(const SkTDArray<SkUnichar>& glyphToUnicode,
diff --git a/tests/TriangulationTest.cpp b/tests/TriangulationTest.cpp
deleted file mode 100644
index 8d692c7..0000000
--- a/tests/TriangulationTest.cpp
+++ /dev/null
@@ -1,299 +0,0 @@
-
-/*
- * Copyright 2011 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-#include "Test.h"
-#include "../../src/core/SkConcaveToTriangles.h"
-#include "SkGeometry.h"
-
-static int GetIndexFromPoint(const SkPoint &pt,
-                             int numPts, const SkPoint *pts) {
-    for (int i = 0; i < numPts; ++i)
-        if (pt.fX == pts[i].fX && pt.fY == pts[i].fY)
-            return i;
-    return -1;
-}
-
-
-bool gPrintTriangles = false;   // Can we set this on the command line?
-
-static void PrintTriangles(const SkTDArray<SkPoint> &triangles,
-                           int numPts, const SkPoint *pts,
-                           skiatest::Reporter* reporter) {
-    if (gPrintTriangles) {
-        SkPoint *p = triangles.begin();
-        int n = triangles.count();
-        REPORTER_ASSERT(reporter, n % 3 == 0);
-        n /= 3;
-        printf("%d Triangles:\n{\n", n);
-        for (; n-- != 0; p += 3)
-            printf("    { {%.7g, %.7g}, {%.7g, %.7g}, {%.7g, %.7g} },  "
-                   "// { %2d, %2d, %2d }\n",
-                p[0].fX, p[0].fY,
-                p[1].fX, p[1].fY,
-                p[2].fX, p[2].fY,
-                GetIndexFromPoint(p[0], numPts, pts),
-                GetIndexFromPoint(p[1], numPts, pts),
-                GetIndexFromPoint(p[2], numPts, pts));
-        printf("}\n");
-    }
-}
-
-
-static bool CompareTriangleList(int numTriangles,
-                                const float refTriangles[][3][2],
-                                const SkTDArray<SkPoint> &triangles) {
-    if (triangles.count() != numTriangles * 3) {
-        printf("Expected %d triangles, not %d\n",
-               numTriangles, triangles.count() / 3);
-        return false;
-    }
-    int numErrors = 0;
-    for (int i = 0; i < numTriangles; ++i) {
-        const float *r = &refTriangles[i][0][0];
-        const SkScalar *t = &triangles[i * 3].fX;
-        bool equalTriangle = true;
-        for (int j = 6; j-- != 0; r++, t++)
-            if (SkFloatToScalar(*r) != *t)
-                equalTriangle = false;
-        if (equalTriangle == false) {
-            ++numErrors;
-            printf("Triangle %d differs\n", i);
-        }
-    }
-    if (numErrors > 0)
-        printf("%d triangles differ\n", numErrors);
-    return numErrors == 0;
-}
-
-
-#ifndef LEFT_HANDED_POLYGONS
-static const SkPoint star[] = {
-    // Outer contour is clockwise if Y is down, counterclockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 70), SkFloatToScalar(120)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 40)  },
-    // Inner contour is counterclockwise if Y is down, clockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  }
-};
-static const SkPoint plus[] = {
-    { SkFloatToScalar( 70), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 70), SkFloatToScalar(110)  },
-    { SkFloatToScalar( 50), SkFloatToScalar(110)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 10)  }
-};
-static const SkPoint zipper[] = {
-    { SkFloatToScalar( 10), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 20), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 20), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 10)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 50)  },
-    { SkFloatToScalar(110), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(140), SkFloatToScalar( 10)  },
-    { SkFloatToScalar(140), SkFloatToScalar( 60)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 60)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 20)  },
-    { SkFloatToScalar(120), SkFloatToScalar( 20)  },
-    { SkFloatToScalar(120), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 60)  }
-};
-#else  // LEFT_HANDED_POLYGONS
-static const SkPoint star[] = {
-    // Outer contour is counterclockwise if Y is down, clockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 40)  },
-    { SkFloatToScalar( 30), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 40), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 10), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 50), SkFloatToScalar( 80)  },
-    { SkFloatToScalar( 70), SkFloatToScalar(120)  },
-    { SkFloatToScalar( 90), SkFloatToScalar( 80)  },
-    { SkFloatToScalar(130), SkFloatToScalar( 80)  },
-    { SkFloatToScalar(100), SkFloatToScalar( 50)  },
-    // Inner contour is clockwise if Y is down, counterclockwise if Y is up.
-    { SkFloatToScalar(110), SkFloatToScalar( 20)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 80), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 70)  },
-    { SkFloatToScalar( 60), SkFloatToScalar( 60)  },
-    { SkFloatToScalar( 70), SkFloatToScalar( 50)  }
-};
-#endif  // LEFT_HANDED_POLYGONS
-#define kNumStarVertices 10
-#define kNumStarHoleVertices (sizeof(star) / sizeof(star[0]))
-
-
-// Star test
-static void TestStarTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { { 30,  20}, { 70,  40}, { 40,  50} },  // { 8, 9, 7 }
-        { {100,  50}, { 10,  80}, { 40,  50} },  // { 1, 6, 7 }
-        { {100,  50}, { 40,  50}, { 70,  40} },  // { 1, 7, 9 }
-        { {100,  50}, { 70,  40}, {110,  20} },  // { 1, 9, 0 }
-        { { 90,  80}, { 70, 120}, { 50,  80} },  // { 3, 4, 5 }
-        { {130,  80}, { 90,  80}, { 50,  80} },  // { 2, 3, 5 } degen
-        { {130,  80}, { 50,  80}, { 10,  80} },  // { 2, 5, 6 } degen
-        { {130,  80}, { 10,  80}, {100,  50} }   // { 2, 6, 1 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    bool success = SkConcaveToTriangles(kNumStarVertices, star, &triangles);
-    PrintTriangles(triangles, kNumStarVertices, star, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Star", success ? reporter->kPassed
-                                                 : reporter->kFailed);
-}
-
-
-// Star with hole test
-static void TestStarHoleTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { {100,  50}, { 80,  60}, { 70,  50} },  // {  1, 15, 16 }
-        { {100,  50}, { 70,  50}, {110,  20} },  // {  1, 16,  0 }
-        { { 30,  20}, { 70,  40}, { 40,  50} },  // {  8,  9,  7 }
-        { { 60,  70}, { 80,  70}, { 10,  80} },  // { 13, 14,  6 }
-        { { 60,  60}, { 60,  70}, { 10,  80} },  // { 12, 13,  6 }
-        { { 70,  50}, { 60,  60}, { 10,  80} },  // { 11, 12,  6 }
-        { { 70,  50}, { 10,  80}, { 40,  50} },  // { 11,  6,  7 }
-        { { 70,  50}, { 40,  50}, { 70,  40} },  // { 11,  7,  9 }
-        { { 70,  50}, { 70,  40}, {110,  20} },  // { 11,  9, 10 }
-        { { 90,  80}, { 70, 120}, { 50,  80} },  // {  3,  4,  5 }
-        { {130,  80}, { 90,  80}, { 50,  80} },  // {  2,  3,  5 } degen
-        { {130,  80}, { 50,  80}, { 10,  80} },  // {  2,  5,  6 } degen
-        { {130,  80}, { 10,  80}, { 80,  70} },  // {  2,  6, 14 }
-        { {130,  80}, { 80,  70}, { 80,  60} },  // {  2, 14, 15 }
-        { {130,  80}, { 80,  60}, {100,  50} }   // {  2, 15,  1 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    bool success = SkConcaveToTriangles(kNumStarHoleVertices, star, &triangles);
-    PrintTriangles(triangles, kNumStarHoleVertices, star, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Star With Hole", success ? reporter->kPassed
-                                                           : reporter->kFailed);
-}
-
-
-// Plus test
-static void TestPlusTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { { 50,  10}, { 70,  10}, { 50, 50} },  // { 11,  0, 10 }
-        { { 70,  50}, {110,  50}, { 10, 70} },  // {  1,  2,  8 }
-        { { 70,  50}, { 10,  70}, { 10, 50} },  // {  1,  8,  9 }
-        { { 70,  50}, { 10,  50}, { 50, 50} },  // {  1,  9, 10 }
-        { { 70,  50}, { 50,  50}, { 70, 10} },  // {  1, 10,  0 }
-        { { 70,  70}, { 50, 110}, { 50, 70} },  // {  4,  6,  7 }
-        { {110,  70}, { 70,  70}, { 50, 70} },  // {  3,  4,  7 }
-        { {110,  70}, { 50,  70}, { 10, 70} },  // {  3,  7,  8 }
-        { {110,  70}, { 10,  70}, {110, 50} },  // {  3,  8,  2 }
-        { { 70, 110}, { 50, 110}, { 70, 70} },  // {  5,  6,  4 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    const size_t numVertices = sizeof(plus) / sizeof(SkPoint);
-    bool success = SkConcaveToTriangles(numVertices, plus, &triangles);
-    PrintTriangles(triangles, numVertices, plus, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Plus", success ? reporter->kPassed
-                                                 : reporter->kFailed);
-}
-
-
-// Zipper test
-static void TestZipperTriangulation(skiatest::Reporter* reporter) {
-    static const float refTriangles[][3][2] = {
-        { { 10, 10}, { 20, 10}, { 20, 50} },  // {  1,  2,  3 }
-        { { 20, 50}, { 30, 50}, { 10, 60} },  // {  3,  4,  0 }
-        { { 10, 10}, { 20, 50}, { 10, 60} },  // {  1,  3,  0 }
-        { { 30, 10}, { 60, 10}, { 40, 20} },  // {  5,  6, 26 }
-        { { 30, 10}, { 40, 20}, { 30, 50} },  // {  5, 26,  4 }
-        { { 40, 60}, { 10, 60}, { 30, 50} },  // { 27,  0,  4 }
-        { { 40, 60}, { 30, 50}, { 40, 20} },  // { 27,  4, 26 }
-        { { 60, 50}, { 70, 50}, { 50, 60} },  // {  7,  8, 24 }
-        { { 50, 20}, { 60, 50}, { 50, 60} },  // { 25,  7, 24 }
-        { { 50, 20}, { 40, 20}, { 60, 10} },  // { 25, 26,  6 }
-        { { 60, 50}, { 50, 20}, { 60, 10} },  // {  7, 25,  6 }
-        { { 70, 10}, {100, 10}, { 80, 20} },  // {  9, 10, 22 }
-        { { 70, 10}, { 80, 20}, { 70, 50} },  // {  9, 22,  8 }
-        { { 80, 60}, { 50, 60}, { 70, 50} },  // { 23, 24,  8 }
-        { { 80, 60}, { 70, 50}, { 80, 20} },  // { 23,  8, 22 }
-        { {100, 50}, {110, 50}, { 90, 60} },  // { 11, 12, 20 }
-        { { 90, 20}, {100, 50}, { 90, 60} },  // { 21, 11, 20 }
-        { { 90, 20}, { 80, 20}, {100, 10} },  // { 21, 22, 10 }
-        { {100, 50}, { 90, 20}, {100, 10} },  // { 11, 21, 10 }
-        { {110, 10}, {140, 10}, {120, 20} },  // { 13, 14, 18 }
-        { {110, 10}, {120, 20}, {110, 50} },  // { 13, 18, 12 }
-        { {120, 60}, { 90, 60}, {110, 50} },  // { 19, 20, 12 }
-        { {120, 60}, {110, 50}, {120, 20} },  // { 19, 12, 18 }
-        { {140, 60}, {130, 60}, {130, 20} },  // { 15, 16, 17 }
-        { {130, 20}, {120, 20}, {140, 10} },  // { 17, 18, 14 }
-        { {140, 60}, {130, 20}, {140, 10} },  // { 15, 17, 14 }
-    };
-    const size_t numRefTriangles = sizeof(refTriangles)
-                                 / (6 * sizeof(refTriangles[0][0][0]));
-    SkTDArray<SkPoint> triangles;
-    const size_t numVertices = sizeof(zipper) / sizeof(SkPoint);
-    bool success = SkConcaveToTriangles(numVertices, zipper, &triangles);
-    PrintTriangles(triangles, numVertices, zipper, reporter);
-    success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
-            && success;
-    reporter->report("Triangulate Zipper", success ? reporter->kPassed
-                                                   : reporter->kFailed);
-}
-
-
-static void TestTriangulation(skiatest::Reporter* reporter) {
-    TestStarTriangulation(reporter);
-    TestStarHoleTriangulation(reporter);
-    TestPlusTriangulation(reporter);
-    TestZipperTriangulation(reporter);
-}
-
-#include "TestClassDef.h"
-DEFINE_TESTCLASS("Triangulation", TriangulationTestClass, TestTriangulation)
diff --git a/tests/UnicodeTest.cpp b/tests/UnicodeTest.cpp
index 602ff81..ec9a8bc 100644
--- a/tests/UnicodeTest.cpp
+++ b/tests/UnicodeTest.cpp
@@ -6,6 +6,7 @@
  * found in the LICENSE file.
  */
 #include "Test.h"
+#include "SkPaint.h"
 #include "SkUtils.h"
 
 // Unicode Variation Selector ranges: inclusive
@@ -37,8 +38,48 @@
     }
 }
 
+// Simple test to ensure that when we call textToGlyphs, we get the same
+// result (for the same text) when using UTF8, UTF16, UTF32.
+// TODO: make the text more complex (i.e. incorporate chars>7bits)
+static void test_textencodings(skiatest::Reporter* reporter) {
+    const char text8[] = "ABCDEFGabcdefg0123456789";
+    uint16_t text16[sizeof(text8)];
+    int32_t  text32[sizeof(text8)];
+    size_t len8 = strlen(text8);
+    size_t len16 = len8 * 2;
+    size_t len32 = len8 * 4;
+
+    // expand our 8bit chars to 16 and 32
+    for (size_t i = 0; i < len8; ++i) {
+        text32[i] = text16[i] = text8[i];
+    }
+
+    uint16_t glyphs8[sizeof(text8)];
+    uint16_t glyphs16[sizeof(text8)];
+    uint16_t glyphs32[sizeof(text8)];
+
+    SkPaint paint;
+
+    paint.setTextEncoding(SkPaint::kUTF8_TextEncoding);
+    int count8  = paint.textToGlyphs(text8,  len8,  glyphs8);
+
+    paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+    int count16 = paint.textToGlyphs(text16, len16, glyphs16);
+
+    paint.setTextEncoding(SkPaint::kUTF32_TextEncoding);
+    int count32 = paint.textToGlyphs(text32, len32, glyphs32);
+
+    REPORTER_ASSERT(reporter, (int)len8 == count8);
+    REPORTER_ASSERT(reporter, (int)len8 == count16);
+    REPORTER_ASSERT(reporter, (int)len8 == count32);
+
+    REPORTER_ASSERT(reporter, !memcmp(glyphs8, glyphs16, count8 * sizeof(uint16_t)));
+    REPORTER_ASSERT(reporter, !memcmp(glyphs8, glyphs32, count8 * sizeof(uint16_t)));
+}
+
 static void TestUnicode(skiatest::Reporter* reporter) {
     test_uvs(reporter);
+    test_textencodings(reporter);
 }
 
 #include "TestClassDef.h"
diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp
index f1b1ea9..7f27f7e 100644
--- a/tests/UtilsTest.cpp
+++ b/tests/UtilsTest.cpp
@@ -14,41 +14,18 @@
 
 class RefClass : public SkRefCnt {
 public:
+    SK_DECLARE_INST_COUNT(RefClass)
+
     RefClass(int n) : fN(n) {}
     int get() const { return fN; }
 
 private:
     int fN;
+
+    typedef SkRefCnt INHERITED;
 };
 
-static void test_refptr(skiatest::Reporter* reporter) {
-    RefClass* r0 = new RefClass(0);
-
-    SkRefPtr<RefClass> rc0;
-    REPORTER_ASSERT(reporter, rc0.get() == NULL);
-    REPORTER_ASSERT(reporter, !rc0);
-
-    SkRefPtr<RefClass> rc1;
-    REPORTER_ASSERT(reporter, rc0 == rc1);
-    REPORTER_ASSERT(reporter, rc0.get() != r0);
-
-    rc0 = r0;
-    REPORTER_ASSERT(reporter, rc0);
-    REPORTER_ASSERT(reporter, rc0 != rc1);
-    REPORTER_ASSERT(reporter, rc0.get() == r0);
-
-    rc1 = rc0;
-    REPORTER_ASSERT(reporter, rc1);
-    REPORTER_ASSERT(reporter, rc0 == rc1);
-    REPORTER_ASSERT(reporter, rc0.get() == r0);
-
-    rc0 = NULL;
-    REPORTER_ASSERT(reporter, rc0.get() == NULL);
-    REPORTER_ASSERT(reporter, !rc0);
-    REPORTER_ASSERT(reporter, rc0 != rc1);
-
-    r0->unref();
-}
+SK_DEFINE_INST_COUNT(RefClass)
 
 static void test_autounref(skiatest::Reporter* reporter) {
     RefClass obj(0);
@@ -172,7 +149,6 @@
 
     test_utf16(reporter);
     test_search(reporter);
-    test_refptr(reporter);
     test_autounref(reporter);
 }
 
diff --git a/tests/WArrayTest.cpp b/tests/WArrayTest.cpp
index daab543..4a5b879 100644
--- a/tests/WArrayTest.cpp
+++ b/tests/WArrayTest.cpp
@@ -8,7 +8,7 @@
 #include "Test.h"
 
 // Include the implementation so we can make an appropriate template instance.
-#include "SkAdvancedTypefaceMetrics.cpp"
+#include "SkAdvancedTypefaceMetrics.h"
 
 using namespace skia_advanced_typeface_metrics_utils;
 
@@ -102,8 +102,7 @@
 
 }
 
-static SkString stringify_advance_data(
-        SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* data) {
+static SkString stringify_advance_data(SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>* data) {
     SkString result;
     bool leadingSpace = false;
     while (data != NULL) {
@@ -114,8 +113,7 @@
       }
       switch(data->fType) {
         case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRun:
-          result.appendf("%d %d %d", data->fStartId, data->fEndId,
-              data->fAdvance[0]);
+          result.appendf("%d %d %d", data->fStartId, data->fEndId, data->fAdvance[0]);
           break;
         case SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t>::kRange:
           result.appendf("%d[", data->fStartId);
@@ -139,10 +137,8 @@
 class TestWData {
   public:
     TestWData(skiatest::Reporter* reporter,
-              const int16_t advances[],
-              int advanceLen,
-              const uint32_t subset[],
-              int subsetLen,
+              const int16_t advances[], int advanceLen,
+              const uint32_t subset[], int subsetLen,
               const char* expected)
             : fAdvances(advances)
             , fAdvancesLen(advanceLen)
@@ -159,7 +155,8 @@
     const int fSubsetLen;
     const char* fExpected;
 
-    static bool getAdvance(TestWData* testCase, int gId, int16_t* advance) {
+    static bool getAdvance(void* tc, int gId, int16_t* advance) {
+        TestWData* testCase = (TestWData*)tc;
         if (gId >= 0 && gId < testCase->fAdvancesLen) {
             *advance = testCase->fAdvances[gId];
             return true;
@@ -169,13 +166,11 @@
 
     bool RunTest() {
         SkTScopedPtr<SkAdvancedTypefaceMetrics::AdvanceMetric<int16_t> > result;
-        result.reset(getAdvanceData(this, fAdvancesLen, fSubset, fSubsetLen,
-                                    getAdvance));
+        result.reset(getAdvanceData((void*)this, fAdvancesLen, fSubset, fSubsetLen, getAdvance));
 
         SkString stringResult = stringify_advance_data(result.get());
         if (!stringResult.equals(fExpected)) {
-            printf("Expected: %s\n  Result: %s\n", fExpected,
-                   stringResult.c_str());
+            printf("Expected: %s\n  Result: %s\n", fExpected, stringResult.c_str());
             return false;
         }
         return true;
diff --git a/tests/WritePixelsTest.cpp b/tests/WritePixelsTest.cpp
index 403ab84..715d8f7 100644
--- a/tests/WritePixelsTest.cpp
+++ b/tests/WritePixelsTest.cpp
@@ -8,12 +8,19 @@
 
 #include "Test.h"
 #include "SkCanvas.h"
+#include "SkColorPriv.h"
+#include "SkDevice.h"
+#include "SkMathPriv.h"
 #include "SkRegion.h"
+#if SK_SUPPORT_GPU
 #include "SkGpuDevice.h"
+#else
+class GrContext;
+#endif
 
 static const int DEV_W = 100, DEV_H = 100;
 static const SkIRect DEV_RECT = SkIRect::MakeWH(DEV_W, DEV_H);
-static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1, 
+static const SkRect DEV_RECT_S = SkRect::MakeWH(DEV_W * SK_Scalar1,
                                                 DEV_H * SK_Scalar1);
 static const U8CPU DEV_PAD = 0xee;
 
@@ -129,7 +136,7 @@
     static SkBitmap bmp;
     if (bmp.isNull()) {
         bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
-        bool alloc = bmp.allocPixels();
+        SkDEBUGCODE(bool alloc = ) bmp.allocPixels();
         SkASSERT(alloc);
         SkAutoLockPixels alp(bmp);
         intptr_t pixels = reinterpret_cast<intptr_t>(bmp.getPixels());
@@ -182,7 +189,8 @@
             b = static_cast<U8CPU>(c[2]);
             break;
         default:
-            GrCrash("Unexpected Config8888");
+            SkDEBUGFAIL("Unexpected Config8888");
+            return 0;
     }
     if (*premul) {
         r = SkMulDiv255Ceiling(r, a);
@@ -233,7 +241,6 @@
     intptr_t canvasPixels = reinterpret_cast<intptr_t>(devBmp.getPixels());
     size_t canvasRowBytes = devBmp.rowBytes();
     SkIRect writeRect = SkIRect::MakeXYWH(writeX, writeY, bitmap.width(), bitmap.height());
-    bool success = true;
     for (int cy = 0; cy < DEV_H; ++cy) {
         const SkPMColor* canvasRow = reinterpret_cast<const SkPMColor*>(canvasPixels);
         for (int cx = 0; cx < DEV_W; ++cx) {
@@ -247,14 +254,14 @@
                 bool check;
                 REPORTER_ASSERT(reporter, check = checkPixel(bmpPMColor, canvasPixel, mul));
                 if (!check) {
-                    success = false;
+                    return false;
                 }
             } else {
                 bool check;
                 SkPMColor testColor = getCanvasColor(cx, cy);
                 REPORTER_ASSERT(reporter, check = (canvasPixel == testColor));
                 if (!check) {
-                    success = false;
+                    return false;
                 }
             }
         }
@@ -264,19 +271,21 @@
                 bool check;
                 REPORTER_ASSERT(reporter, check = (pad[px] == static_cast<char>(DEV_PAD)));
                 if (!check) {
-                    success = false;
+                    return false;
                 }
             }
         }
         canvasPixels += canvasRowBytes;
     }
 
-    return success;
+    return true;
 }
 
 enum DevType {
     kRaster_DevType,
+#if SK_SUPPORT_GPU
     kGpu_DevType,
+#endif
 };
 
 struct CanvasConfig {
@@ -287,34 +296,34 @@
 static const CanvasConfig gCanvasConfigs[] = {
     {kRaster_DevType, true},
     {kRaster_DevType, false},
-#ifdef SK_SCALAR_IS_FLOAT
+#if SK_SUPPORT_GPU && defined(SK_SCALAR_IS_FLOAT)
     {kGpu_DevType, true}, // row bytes has no meaning on gpu devices
 #endif
 };
 
-bool setupCanvas(SkCanvas* canvas, const CanvasConfig& c, GrContext* grCtx) {
+SkDevice* createDevice(const CanvasConfig& c, GrContext* grCtx) {
     switch (c.fDevType) {
         case kRaster_DevType: {
             SkBitmap bmp;
             size_t rowBytes = c.fTightRowBytes ? 0 : 4 * DEV_W + 100;
             bmp.setConfig(SkBitmap::kARGB_8888_Config, DEV_W, DEV_H, rowBytes);
             if (!bmp.allocPixels()) {
-                return false;
+                sk_throw();
+                return NULL;
             }
             // if rowBytes isn't tight then set the padding to a known value
             if (rowBytes) {
                 SkAutoLockPixels alp(bmp);
                 memset(bmp.getPixels(), DEV_PAD, bmp.getSafeSize());
             }
-            canvas->setDevice(new SkDevice(bmp))->unref();
-            } break;
+            return new SkDevice(bmp);
+        }
+#if SK_SUPPORT_GPU
         case kGpu_DevType:
-            canvas->setDevice(new SkGpuDevice(grCtx,
-                                              SkBitmap::kARGB_8888_Config,
-                                              DEV_W, DEV_H))->unref();
-            break;
+            return new SkGpuDevice(grCtx, SkBitmap::kARGB_8888_Config, DEV_W, DEV_H);
+#endif
     }
-    return true;
+    return NULL;
 }
 
 bool setupBitmap(SkBitmap* bitmap,
@@ -339,7 +348,7 @@
 
 void WritePixelsTest(skiatest::Reporter* reporter, GrContext* context) {
     SkCanvas canvas;
-    
+
     const SkIRect testRects[] = {
         // entire thing
         DEV_RECT,
@@ -388,9 +397,10 @@
     };
 
     for (size_t i = 0; i < SK_ARRAY_COUNT(gCanvasConfigs); ++i) {
-        REPORTER_ASSERT(reporter, setupCanvas(&canvas, gCanvasConfigs[i], context));
+        SkAutoTUnref<SkDevice> device(createDevice(gCanvasConfigs[i], context));
+        SkCanvas canvas(device);
 
-        static const SkCanvas::Config8888 gReadConfigs[] = {
+        static const SkCanvas::Config8888 gSrcConfigs[] = {
             SkCanvas::kNative_Premul_Config8888,
             SkCanvas::kNative_Unpremul_Config8888,
             SkCanvas::kBGRA_Premul_Config8888,
@@ -401,13 +411,22 @@
         for (size_t r = 0; r < SK_ARRAY_COUNT(testRects); ++r) {
             const SkIRect& rect = testRects[r];
             for (int tightBmp = 0; tightBmp < 2; ++tightBmp) {
-                for (size_t c = 0; c < SK_ARRAY_COUNT(gReadConfigs); ++c) {
+                for (size_t c = 0; c < SK_ARRAY_COUNT(gSrcConfigs); ++c) {
                     fillCanvas(&canvas);
-                    SkCanvas::Config8888 config8888 = gReadConfigs[c];
+                    SkCanvas::Config8888 config8888 = gSrcConfigs[c];
                     SkBitmap bmp;
                     REPORTER_ASSERT(reporter, setupBitmap(&bmp, config8888, rect.width(), rect.height(), SkToBool(tightBmp)));
+                    uint32_t idBefore = canvas.getDevice()->accessBitmap(false).getGenerationID();
                     canvas.writePixels(bmp, rect.fLeft, rect.fTop, config8888);
+                    uint32_t idAfter = canvas.getDevice()->accessBitmap(false).getGenerationID();
                     REPORTER_ASSERT(reporter, checkWrite(reporter, &canvas, bmp, rect.fLeft, rect.fTop, config8888));
+
+                    // we should change the genID iff pixels were actually written.
+                    SkIRect canvasRect = SkIRect::MakeSize(canvas.getDeviceSize());
+                    SkIRect writeRect = SkIRect::MakeXYWH(rect.fLeft, rect.fTop,
+                                                          bmp.width(), bmp.height());
+                    bool intersects = SkIRect::Intersects(canvasRect, writeRect) ;
+                    REPORTER_ASSERT(reporter, intersects == (idBefore != idAfter));
                 }
             }
         }
@@ -415,6 +434,7 @@
 }
 }
 
+#ifndef SK_BUILD_FOR_ANDROID
 #include "TestClassDef.h"
 DEFINE_GPUTESTCLASS("WritePixels", WritePixelsTestClass, WritePixelsTest)
-
+#endif
diff --git a/tests/Writer32Test.cpp b/tests/Writer32Test.cpp
index a9a07ad..33777a1 100644
--- a/tests/Writer32Test.cpp
+++ b/tests/Writer32Test.cpp
@@ -8,10 +8,79 @@
 
 
 
+#include "SkRandom.h"
 #include "SkReader32.h"
 #include "SkWriter32.h"
 #include "Test.h"
 
+static void check_contents(skiatest::Reporter* reporter, const SkWriter32& writer,
+                           const void* expected, size_t size) {
+    SkAutoSMalloc<256> storage(size);
+    REPORTER_ASSERT(reporter, writer.bytesWritten() == size);
+    writer.flatten(storage.get());
+    REPORTER_ASSERT(reporter, !memcmp(storage.get(), expected, size));
+}
+
+static void test_rewind(skiatest::Reporter* reporter) {
+    SkSWriter32<32> writer(32);
+    int32_t array[3] = { 1, 2, 4 };
+
+    REPORTER_ASSERT(reporter, 0 == writer.bytesWritten());
+    for (size_t i = 0; i < SK_ARRAY_COUNT(array); ++i) {
+        writer.writeInt(array[i]);
+    }
+    check_contents(reporter, writer, array, sizeof(array));
+
+    writer.rewindToOffset(2*sizeof(int32_t));
+    REPORTER_ASSERT(reporter, sizeof(array) - 4 == writer.bytesWritten());
+    writer.writeInt(3);
+    REPORTER_ASSERT(reporter, sizeof(array) == writer.bytesWritten());
+    array[2] = 3;
+    check_contents(reporter, writer, array, sizeof(array));
+
+    // test rewinding past allocated chunks. This used to crash because we
+    // didn't truncate our link-list after freeing trailing blocks
+    {
+        SkWriter32 writer(64);
+        for (int i = 0; i < 100; ++i) {
+            writer.writeInt(i);
+        }
+        REPORTER_ASSERT(reporter, 100*4 == writer.bytesWritten());
+        for (int j = 100*4; j >= 0; j -= 16) {
+            writer.rewindToOffset(j);
+        }
+        REPORTER_ASSERT(reporter, writer.bytesWritten() < 16);
+    }
+}
+
+static void test_ptr(skiatest::Reporter* reporter) {
+    SkSWriter32<32> writer(32);
+
+    void* p0 = reporter;
+    void* p1 = &writer;
+
+    // try writing ptrs where at least one of them may be at a non-multiple of
+    // 8 boundary, to confirm this works on 64bit machines.
+
+    writer.writePtr(p0);
+    writer.write8(0x33);
+    writer.writePtr(p1);
+    writer.write8(0x66);
+
+    size_t size = writer.size();
+    REPORTER_ASSERT(reporter, 2 * sizeof(void*) + 2 * sizeof(int32_t));
+
+    char buffer[32];
+    SkASSERT(sizeof(buffer) >= size);
+    writer.flatten(buffer);
+
+    SkReader32 reader(buffer, size);
+    REPORTER_ASSERT(reporter, reader.readPtr() == p0);
+    REPORTER_ASSERT(reporter, reader.readInt() == 0x33);
+    REPORTER_ASSERT(reporter, reader.readPtr() == p1);
+    REPORTER_ASSERT(reporter, reader.readInt() == 0x66);
+}
+
 static void test1(skiatest::Reporter* reporter, SkWriter32* writer) {
     const uint32_t data[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
     for (size_t i = 0; i < SK_ARRAY_COUNT(data); ++i) {
@@ -49,35 +118,114 @@
         REPORTER_ASSERT(reporter, i == len);
         REPORTER_ASSERT(reporter, strlen(str) == len);
         REPORTER_ASSERT(reporter, !memcmp(str, gStr, len));
+        // Ensure that the align4 of the string is padded with zeroes.
+        size_t alignedSize = SkAlign4(len + 1);
+        for (size_t j = len; j < alignedSize; j++) {
+            REPORTER_ASSERT(reporter, 0 == str[j]);
+        }
     }
     REPORTER_ASSERT(reporter, reader.eof());
 }
 
+static void testWritePad(skiatest::Reporter* reporter, SkWriter32* writer) {
+    // Create some random data to write.
+    const size_t dataSize = 10<<2;
+    SkASSERT(SkIsAlign4(dataSize));
+
+    SkAutoMalloc originalData(dataSize);
+    {
+        SkRandom rand(0);
+        uint32_t* ptr = static_cast<uint32_t*>(originalData.get());
+        uint32_t* stop = ptr + (dataSize>>2);
+        while (ptr < stop) {
+            *ptr++ = rand.nextU();
+        }
+
+        // Write  the random data to the writer at different lengths for
+        // different alignments.
+        for (size_t len = 0; len < dataSize; len++) {
+            writer->writePad(originalData.get(), len);
+        }
+    }
+
+    uint32_t totalBytes = writer->size();
+
+    SkAutoMalloc readStorage(totalBytes);
+    writer->flatten(readStorage.get());
+
+    SkReader32 reader;
+    reader.setMemory(readStorage.get(), totalBytes);
+
+    for (size_t len = 0; len < dataSize; len++) {
+        const char* readPtr = static_cast<const char*>(reader.skip(len));
+        // Ensure that the data read is the same as what was written.
+        REPORTER_ASSERT(reporter, memcmp(readPtr, originalData.get(), len) == 0);
+        // Ensure that the rest is padded with zeroes.
+        const char* stop = readPtr + SkAlign4(len);
+        readPtr += len;
+        while (readPtr < stop) {
+            REPORTER_ASSERT(reporter, *readPtr++ == 0);
+        }
+    }
+}
+
 static void Tests(skiatest::Reporter* reporter) {
     // dynamic allocator
     {
         SkWriter32 writer(256 * 4);
-        REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
         test1(reporter, &writer);
-        
+
         writer.reset();
         test2(reporter, &writer);
+
+        writer.reset();
+        testWritePad(reporter, &writer);
     }
-    
-    // single-block
+
+    // storage-block
     {
         SkWriter32 writer(0);
         uint32_t storage[256];
-        REPORTER_ASSERT(reporter, NULL == writer.getSingleBlock());
         writer.reset(storage, sizeof(storage));
-        REPORTER_ASSERT(reporter, (void*)storage == writer.getSingleBlock());
         test1(reporter, &writer);
 
         writer.reset(storage, sizeof(storage));
         test2(reporter, &writer);
+
+        writer.reset(storage, sizeof(storage));
+        testWritePad(reporter, &writer);
+
+        // try overflowing the storage-block
+        uint32_t smallStorage[8];
+        writer.reset(smallStorage, sizeof(smallStorage));
+        test2(reporter, &writer);
     }
+
+    // small storage
+    {
+        SkSWriter32<8 * sizeof(intptr_t)> writer(100);
+        test1(reporter, &writer);
+        writer.reset(); // should just rewind our storage
+        test2(reporter, &writer);
+
+        writer.reset();
+        testWritePad(reporter, &writer);
+    }
+
+    // large storage
+    {
+        SkSWriter32<1024 * sizeof(intptr_t)> writer(100);
+        test1(reporter, &writer);
+        writer.reset(); // should just rewind our storage
+        test2(reporter, &writer);
+
+        writer.reset();
+        testWritePad(reporter, &writer);
+    }
+
+    test_ptr(reporter);
+    test_rewind(reporter);
 }
 
 #include "TestClassDef.h"
 DEFINE_TESTCLASS("Writer32", Writer32Class, Tests)
-
diff --git a/tests/XfermodeTest.cpp b/tests/XfermodeTest.cpp
index 966da51..0b7012d 100644
--- a/tests/XfermodeTest.cpp
+++ b/tests/XfermodeTest.cpp
@@ -9,7 +9,7 @@
 #include "SkColor.h"
 #include "SkXfermode.h"
 
-SkPMColor bogusXfermodeProc(SkPMColor src, SkPMColor dst) {
+static SkPMColor bogusXfermodeProc(SkPMColor src, SkPMColor dst) {
     return 42;
 }
 
@@ -33,7 +33,7 @@
             REPORTER_ASSERT(reporter, reportedMode == mode);
             xfer->unref();
         } else {
-            REPORTER_ASSERT(reporter, SkXfermode::kSrcOver_Mode == mode); 
+            REPORTER_ASSERT(reporter, SkXfermode::kSrcOver_Mode == mode);
         }
     }
 
@@ -52,7 +52,7 @@
 
     for (int i = 0; i <= SkXfermode::kLastMode; ++i) {
         SkXfermode::Mode mode = (SkXfermode::Mode)i;
-        
+
         SkXfermode* xfer = SkXfermode::Create(mode);
         REPORTER_ASSERT(reporter, SkXfermode::IsMode(xfer, mode));
         SkSafeUnref(xfer);
diff --git a/tests/skia_test.cpp b/tests/skia_test.cpp
index b740590..8a1feae 100644
--- a/tests/skia_test.cpp
+++ b/tests/skia_test.cpp
@@ -8,6 +8,10 @@
 #include "SkGraphics.h"
 #include "Test.h"
 
+#if SK_SUPPORT_GPU
+#include "GrContext.h"
+#endif
+
 using namespace skiatest;
 
 // need to explicitly declare this, or we get some weird infinite loop llist
@@ -56,7 +60,7 @@
 
 class DebugfReporter : public Reporter {
 public:
-    DebugfReporter(bool androidMode) : fAndroidMode(androidMode) {}
+    DebugfReporter() : fIndex(0), fTotal(0) {}
 
     void setIndexOfTotal(int index, int total) {
         fIndex = index;
@@ -64,56 +68,32 @@
     }
 protected:
     virtual void onStart(Test* test) {
-        this->dumpState(test, kStarting_State);
+        SkDebugf("[%d/%d] %s...\n", fIndex+1, fTotal, test->getName());
     }
     virtual void onReport(const char desc[], Reporter::Result result) {
-        if (!fAndroidMode) {
-            SkDebugf("\t%s: %s\n", result2string(result), desc);
-        }
+        SkDebugf("\t%s: %s\n", result2string(result), desc);
     }
     virtual void onEnd(Test* test) {
-        this->dumpState(test, this->getCurrSuccess() ?
-                        kSucceeded_State : kFailed_State);
-    }
-private:
-    enum State {
-        kStarting_State = 1,
-        kSucceeded_State = 0,
-        kFailed_State = -2
-    };
-
-    void dumpState(Test* test, State state) {
-        if (fAndroidMode) {
-            SkDebugf("INSTRUMENTATION_STATUS: test=%s\n", test->getName());
-            SkDebugf("INSTRUMENTATION_STATUS: class=com.skia\n");
-            SkDebugf("INSTRUMENTATION_STATUS: current=%d\n", fIndex+1);
-            SkDebugf("INSTRUMENTATION_STATUS: numtests=%d\n", fTotal);
-            SkDebugf("INSTRUMENTATION_STATUS_CODE: %d\n", state);
-        } else {
-            if (kStarting_State == state) {
-                SkDebugf("[%d/%d] %s...\n", fIndex+1, fTotal, test->getName());
-            } else if (kFailed_State == state) {
-                SkDebugf("---- FAILED\n");
-            }
+        if (!this->getCurrSuccess()) {
+            SkDebugf("---- FAILED\n");
         }
     }
-
+private:
     int fIndex, fTotal;
-    bool fAndroidMode;
 };
 
-int main (int argc, char * const argv[]) {
-    SkAutoGraphics ag;
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+#if SK_ENABLE_INST_COUNT
+    gPrintInstCount = true;
+#endif
+    SkGraphics::Init();
 
-    bool androidMode = false;
     const char* matchStr = NULL;
 
     char* const* stop = argv + argc;
     for (++argv; argv < stop; ++argv) {
-        if (strcmp(*argv, "-android") == 0) {
-            androidMode = true;
-        
-        } else if (strcmp(*argv, "--match") == 0) {
+        if (strcmp(*argv, "--match") == 0) {
             ++argv;
             if (argv < stop && **argv) {
                 matchStr = *argv;
@@ -136,12 +116,10 @@
 #else
         header.append(" SK_SCALAR_IS_FLOAT");
 #endif
-        if (!androidMode) {
-            SkDebugf("%s\n", header.c_str());
-        }
+        SkDebugf("%s\n", header.c_str());
     }
 
-    DebugfReporter reporter(androidMode);
+    DebugfReporter reporter;
     Iter iter(&reporter);
     Test* test;
 
@@ -162,9 +140,27 @@
         index += 1;
     }
 
-    if (!androidMode) {
-        SkDebugf("Finished %d tests, %d failures, %d skipped.\n",
-                 count, failCount, skipCount);
-    }
+    SkDebugf("Finished %d tests, %d failures, %d skipped.\n",
+             count, failCount, skipCount);
+
+#if SK_SUPPORT_GPU
+
+#if GR_CACHE_STATS
+    GrContext *gr = GpuTest::GetContext();
+
+    gr->printCacheStats();
+#endif
+
+#endif
+
+    SkGraphics::Term();
+    GpuTest::DestroyContext();
+
     return (failCount == 0) ? 0 : 1;
 }
+
+#if !defined(SK_BUILD_FOR_IOS) && !defined(SK_BUILD_FOR_NACL)
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tests/valgrind.supp b/tests/valgrind.supp
new file mode 100644
index 0000000..4539c94
--- /dev/null
+++ b/tests/valgrind.supp
@@ -0,0 +1,16 @@
+# Pass this file to Valgrind with "--suppressions=tests/valgrind.supp"
+# to avoid reporting errors inside the driver.
+
+{
+    nVidiaDriverUninitializedJump001
+    Memcheck:Cond
+    obj:/usr/lib/nvidia-current/libnvidia-glcore.so.280.13
+}
+{
+   nVidiaDriverLeak001
+   Memcheck:Leak
+   fun:malloc
+   obj:/usr/lib/nvidia-current/libGL.so.280.13
+}
+
+
diff --git a/third_party/glu/LICENSE.txt b/third_party/glu/LICENSE.txt
deleted file mode 100644
index e58ae38..0000000
--- a/third_party/glu/LICENSE.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) 
-
-Copyright (C) [dates of first publication] Silicon Graphics, Inc. All
-Rights Reserved.
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-"Software"), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice including the dates of first publication
-and either this permission notice or a reference to HYPERLINK
-"http://oss.sgi.com/projects/FreeB/"http://oss.sgi.com/projects/FreeB/
-shall be included in all copies or substantial portions of the
-Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL SILICON GRAPHICS, INC. BE LIABLE
-FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
-CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-
-Except as contained in this notice, the name of Silicon Graphics,
-Inc. shall not be used in advertising or otherwise to promote the
-sale, use or other dealings in this Software without prior written
-authorization from Silicon Graphics, Inc.
diff --git a/third_party/glu/README.skia b/third_party/glu/README.skia
deleted file mode 100644
index 0b6b624..0000000
--- a/third_party/glu/README.skia
+++ /dev/null
@@ -1,41 +0,0 @@
-This is a nearly verbatim copy of the GLU tessellator source code from
-SGI's OpenGL Sample Implementation at
-http://oss.sgi.com/projects/ogl-sample/ . Per
-http://oss.sgi.com/projects/FreeB/ , the code is covered under the SGI
-Free Software License B, version 2.0, a copy of which is in
-LICENSE.txt in this directory.
-
-The following changes were made in order to incorporate this code:
-
-  - The addition of a simplified gluos.h to eliminate operating system
-    dependencies.  The entry points to the tessellator were prefixed with
-    Sk_ to avoid symbol collisions with any host OS version of GLU via
-    #defines in gluos.h.
-
-  - The removal of inclusion of GL/glu.h and replacement with an
-    include of sk_glu.h.
-
-  - In tess.c, the obsolete entry points gluBeginPolygon,
-    gluNextContour and gluEndPolygon in tess.c were #if 0'd out.
-    Default branches were added to the switch statements in GotoState.
-
-  - In memalloc.h, the include of malloc.h was changed to an include
-    of stdlib.h.
-
-  - In normal.c, an unused variable "w" was removed from
-    __gl_projectPolygon. #if guards were placed around the definition
-    of the unused Normalize function.
-
-  - In priorityq-heap.c, an #include of <limits.h> was added.
-
-  - In sweep.c, IsWindingInside() was given a return value to silence a
-    warning-as-error in release builds.
-
-  - In sweep.c, DoneEdgeDict()'s fixedEdges was wrapped in #indef NDEBUG, to
-    silence a warning-as-error in release builds.
-
-  - In priorityq.c, render.c, and others:  the construct "if(1)...else" was
-  replaced with "do{...}while(1)" to silence a warning-as-error in Mac builds.
-
-  - rename all __gl_ functions to Sk__gl_ to avoid conflicting with other
-    static linkers of this library.
diff --git a/third_party/glu/gluos.h b/third_party/glu/gluos.h
deleted file mode 100644
index e94c679..0000000
--- a/third_party/glu/gluos.h
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2010, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef GLUOS_H_
-#define GLUOS_H_
-
-// This header provides the minimal definitions needed to compile the
-// GLU tessellator sources.
-#define GLAPIENTRY
-
-typedef unsigned char GLboolean;
-typedef double GLdouble;
-typedef unsigned int GLenum;
-typedef float GLfloat;
-typedef void GLvoid;
-
-#define gluNewTess Sk_gluNewTess
-#define gluDeleteTess Sk_gluDeleteTess
-#define gluTessProperty Sk_gluTessProperty
-#define gluGetTessProperty Sk_gluGetTessProperty
-#define gluTessNormal Sk_gluTessNormal
-#define gluTessCallback Sk_gluTessCallback
-#define gluTessVertex Sk_gluTessVertex
-#define gluTessBeginPolygon Sk_gluTessBeginPolygon
-#define gluTessBeginContour Sk_gluTessBeginContour
-#define gluTessEndContour Sk_gluTessEndContour
-#define gluTessEndPolygon Sk_gluTessEndPolygon
-
-#define __gl_noBeginData Sk__gl_noBeginData
-#define __gl_noEdgeFlagData Sk__gl_noEdgeFlagData
-#define __gl_noVertexData Sk__gl_noVertexData
-#define __gl_noEndData Sk__gl_noEndData
-#define __gl_noErrorData Sk__gl_noErrorData
-#define __gl_noCombineData Sk__gl_noCombineData
-#define __gl_computeInterior Sk__gl_computeInterior
-#define __gl_dictListDelete Sk__gl_dictListDelete
-#define __gl_dictListDeleteDict Sk__gl_dictListDeleteDict
-#define __gl_dictListInsertBefore Sk__gl_dictListInsertBefore
-#define __gl_dictListNewDict Sk__gl_dictListNewDict
-#define __gl_dictListSearch Sk__gl_dictListSearch
-#define __gl_edgeEval Sk__gl_edgeEval
-#define __gl_edgeIntersect Sk__gl_edgeIntersect
-#define __gl_edgeSign Sk__gl_edgeSign
-#define __gl_memInit Sk__gl_memInit
-#define __gl_meshAddEdgeVertex Sk__gl_meshAddEdgeVertex
-#ifndef NDEBUG
-#define __gl_meshCheckMesh Sk__gl_meshCheckMesh
-#endif
-#define __gl_meshConnect Sk__gl_meshConnect
-#define __gl_meshDelete Sk__gl_meshDelete
-#define __gl_meshDeleteMesh Sk__gl_meshDeleteMesh
-#define __gl_meshDiscardExterior Sk__gl_meshDiscardExterior
-#define __gl_meshMakeEdge Sk__gl_meshMakeEdge
-#define __gl_meshNewMesh Sk__gl_meshNewMesh
-#define __gl_meshSetWindingNumber Sk__gl_meshSetWindingNumber
-#define __gl_meshSplice Sk__gl_meshSplice
-#define __gl_meshSplitEdge Sk__gl_meshSplitEdge
-#define __gl_meshTessellateInterior Sk__gl_meshTessellateInterior
-#define __gl_meshTessellateMonoRegion Sk__gl_meshTessellateMonoRegion
-#define __gl_meshUnion Sk__gl_meshUnion
-#define __gl_meshZapFace Sk__gl_meshZapFace
-#define __gl_pqHeapDelete Sk__gl_pqHeapDelete
-#define __gl_pqHeapDeletePriorityQ Sk__gl_pqHeapDeletePriorityQ
-#define __gl_pqHeapExtractMin Sk__gl_pqHeapExtractMin
-#define __gl_pqHeapInit Sk__gl_pqHeapInit
-#define __gl_pqHeapInsert Sk__gl_pqHeapInsert
-#define __gl_pqHeapNewPriorityQ Sk__gl_pqHeapNewPriorityQ
-#define __gl_pqSortDelete Sk__gl_pqSortDelete
-#define __gl_pqSortDeletePriorityQ Sk__gl_pqSortDeletePriorityQ
-#define __gl_pqSortExtractMin Sk__gl_pqSortExtractMin
-#define __gl_pqSortInit Sk__gl_pqSortInit
-#define __gl_pqSortInsert Sk__gl_pqSortInsert
-#define __gl_pqSortIsEmpty Sk__gl_pqSortIsEmpty
-#define __gl_pqSortMinimum Sk__gl_pqSortMinimum
-#define __gl_pqSortNewPriorityQ Sk__gl_pqSortNewPriorityQ
-#define __gl_projectPolygon Sk__gl_projectPolygon
-#define __gl_renderBoundary Sk__gl_renderBoundary
-#define __gl_renderCache Sk__gl_renderCache
-#define __gl_renderMesh Sk__gl_renderMesh
-#define __gl_transEval Sk__gl_transEval
-#define __gl_transSign Sk__gl_transSign
-#define __gl_vertCCW Sk__gl_vertCCW
-#define __gl_vertLeq Sk__gl_vertLeq
-
-
-#undef MIN
-#undef MAX
-
-#endif  // GLUOS_H_
diff --git a/third_party/glu/libtess/GNUmakefile b/third_party/glu/libtess/GNUmakefile
deleted file mode 100644
index 0ae859f..0000000
--- a/third_party/glu/libtess/GNUmakefile
+++ /dev/null
@@ -1,110 +0,0 @@
-#!gmake
-#
-# License Applicability. Except to the extent portions of this file are
-# made subject to an alternative license as permitted in the SGI Free
-# Software License B, Version 1.1 (the "License"), the contents of this
-# file are subject only to the provisions of the License. You may not use
-# this file except in compliance with the License. You may obtain a copy
-# of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-# Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-#
-# http://oss.sgi.com/projects/FreeB
-#
-# Note that, as provided in the License, the Software is distributed on an
-# "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-# DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-# CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-# PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-#
-# Original Code. The Original Code is: OpenGL Sample Implementation,
-# Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-# Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-# Copyright in any portions created by third parties is as indicated
-# elsewhere herein. All Rights Reserved.
-#
-# Additional Notice Provisions: The application programming interfaces
-# established by SGI in conjunction with the Original Code are The
-# OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-# April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-# 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-# Window System(R) (Version 1.3), released October 19, 1998. This software
-# was created using the OpenGL(R) version 1.2.1 Sample Implementation
-# published by SGI, but has not been independently verified as being
-# compliant with the OpenGL(R) version 1.2.1 Specification.
-#
-# $Date$ $Revision$
-# $Header: //depot/main/gfx/lib/glu/libtess/GNUmakefile#9 $
-
-OBJECT_STYLE = N32
-
-include $(ROOT)/usr/include/make/commondefs
-
-TARGET = libtess.a
-
-TARGETS = $(TARGET)
-
-LCINCS = -I../include
-
-# Compilation flags:
-#
-# -DNDEBUG is for the production code; it removes all assertion checks
-#    (note that <assert.h> looks at this symbol).
-#
-# -DNO_MALLOPT uses regular malloc instead of the mallopt() version.
-#   ***** Unless you use this flag, you must use "-lmalloc" to link
-#   ***** your application!
-#
-# -DMEMORY_DEBUG turns on the M_DEBUG option of mallopt; this can
-#    increase the running time a LOT.
-#
-# -DGLU_TESS_API_FLOAT compiles a single-precision version of the library.
-#
-# -float prevents automatic promotion to double precision; this will produce
-#    faster code when compiled with -DGLU_TESS_API_FLOAT.
-#
-# -DNO_BRANCH_CONDITIONS uses & and | instead of && and || on a couple
-#    of heavily-used tests (VertEq and VertLeq); some compilers can generate
-#    better code with these (use special instructions to avoid branching).
-#
-# -DFOR_TRITE_TEST_PROGRAM is *only* for use with the test program called
-#    "trite".  It uses some variables which are defined by the test program,
-#    so you won't be able to link it with anything else.
-
-HFILES = \
-	dict.h \
-	dict-list.h \
-	geom.h \
-	memalloc.h \
-	mesh.h \
-	normal.h \
-	priorityq-heap.h \
-	priorityq-heap.c \
-	priorityq-sort.h \
-	priorityq.h \
-	render.h \
-	sweep.h \
-	tess.h \
-	tessmono.h \
-	$(NULL)
-
-CFILES = \
-	dict.c \
-	geom.c \
-	memalloc.c \
-	mesh.c \
-	normal.c \
-	priorityq.c \
-	render.c \
-	sweep.c \
-	tess.c \
-	tessmono.c \
-	$(NULL)
-
-default libs libs_install install: $(TARGET)
-
-headers headers_install apps:
-
-$(TARGET): $(OBJECTS)
-	$(AR) crl $@ $(OBJECTS);
-
-include $(COMMONRULES)
diff --git a/third_party/glu/libtess/Imakefile b/third_party/glu/libtess/Imakefile
deleted file mode 100644
index fb99ce2..0000000
--- a/third_party/glu/libtess/Imakefile
+++ /dev/null
@@ -1,61 +0,0 @@
-XCOMM License Applicability. Except to the extent portions of this file are
-XCOMM made subject to an alternative license as permitted in the SGI Free
-XCOMM Software License B, Version 1.1 (the "License"), the contents of this
-XCOMM file are subject only to the provisions of the License. You may not use
-XCOMM this file except in compliance with the License. You may obtain a copy
-XCOMM of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-XCOMM Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-XCOMM 
-XCOMM http://oss.sgi.com/projects/FreeB
-XCOMM 
-XCOMM Note that, as provided in the License, the Software is distributed on an
-XCOMM "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-XCOMM DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-XCOMM CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-XCOMM PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-XCOMM 
-XCOMM Original Code. The Original Code is: OpenGL Sample Implementation,
-XCOMM Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-XCOMM Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-XCOMM Copyright in any portions created by third parties is as indicated
-XCOMM elsewhere herein. All Rights Reserved.
-XCOMM 
-XCOMM Additional Notice Provisions: The application programming interfaces
-XCOMM established by SGI in conjunction with the Original Code are The
-XCOMM OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-XCOMM April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-XCOMM 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-XCOMM Window System(R) (Version 1.3), released October 19, 1998. This software
-XCOMM was created using the OpenGL(R) version 1.2.1 Sample Implementation
-XCOMM published by SGI, but has not been independently verified as being
-XCOMM compliant with the OpenGL(R) version 1.2.1 Specification.
-XCOMM
-
-#include <Library.tmpl>
-
-OBJS = \
-	dict.o \
-	geom.o \
-	memalloc.o \
-	mesh.o \
-	normal.o \
-	priorityq.o \
-	render.o \
-	sweep.o \
-	tess.o \
-	tessmono.o
-
-INCLUDES = \
-	-I../include \
-	-I$(TOP)/include \
-	-I$(TOP)/include/GL
-
-DEFINES = \
-	-DNDEBUG
-
-NormalLibraryObjectRule()
-
-NormalLibraryTarget(tess, $(OBJS))
-
-DependTarget()
-CleanTarget()
diff --git a/third_party/glu/libtess/README b/third_party/glu/libtess/README
deleted file mode 100644
index 59f4ff2..0000000
--- a/third_party/glu/libtess/README
+++ /dev/null
@@ -1,447 +0,0 @@
-/*
-** $Header: /cvs/projects/ogl-sample/main/gfx/lib/glu/libtess/README,v 1.1 2000/04/26 05:53:59 ljp Exp $
-*/
-
-General Polygon Tesselation
----------------------------
-
-  This note describes a tesselator for polygons consisting of one or
-  more closed contours.  It is backward-compatible with the current
-  OpenGL Utilities tesselator, and is intended to replace it.  Here is
-  a summary of the major differences:
-
-   - input contours can be intersecting, self-intersecting, or degenerate.
-  
-   - supports a choice of several winding rules for determining which parts
-     of the polygon are on the "interior".  This makes it possible to do
-     CSG operations on polygons.
-  
-   - boundary extraction: instead of tesselating the polygon, returns a
-     set of closed contours which separate the interior from the exterior.
-  
-   - returns the output as a small number of triangle fans and strips,
-     rather than a list of independent triangles (when possible).
-  
-   - output is available as an explicit mesh (a quad-edge structure),
-     in addition to the normal callback interface.
-  
-   - the algorithm used is extremely robust.
-
-
-The interface
--------------
-
-  The tesselator state is maintained in a "tesselator object".
-  These are allocated and destroyed using
-
-     GLUtesselator *gluNewTess( void );
-     void gluDeleteTess( GLUtesselator *tess );
-
-  Several tesselator objects may be used simultaneously.
-
-  Inputs
-  ------
-  
-  The input contours are specified with the following routines:
-
-     void gluTessBeginPolygon( GLUtesselator *tess );
-     void gluTessBeginContour( GLUtesselator *tess );
-     void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data );
-     void gluTessEndContour( GLUtesselator *tess );
-     void gluTessEndPolygon( GLUtesselator *tess );
-
-  Within each BeginPolygon/EndPolygon pair, there can be zero or more
-  calls to BeginContour/EndContour.  Within each contour, there are zero
-  or more calls to gluTessVertex().  The vertices specify a closed
-  contour (the last vertex of each contour is automatically linked to
-  the first).
-
-  "coords" give the coordinates of the vertex in 3-space.  For useful
-  results, all vertices should lie in some plane, since the vertices
-  are projected onto a plane before tesselation.  "data" is a pointer
-  to a user-defined vertex structure, which typically contains other
-  information such as color, texture coordinates, normal, etc.  It is
-  used to refer to the vertex during rendering.
-
-  The library can be compiled in single- or double-precision; the type
-  GLUcoord represents either "float" or "double" accordingly.  The GLU
-  version will be available in double-precision only.  Compile with
-  GLU_TESS_API_FLOAT defined to get the single-precision version.
-
-  When EndPolygon is called, the tesselation algorithm determines
-  which regions are interior to the given contours, according to one
-  of several "winding rules" described below.  The interior regions
-  are then tesselated, and the output is provided as callbacks.
-
-
-  Rendering Callbacks
-  -------------------
-
-  Callbacks are specified by the client using
-
-     void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)());
-
-  If "fn" is NULL, any previously defined callback is discarded.
-  
-  The callbacks used to provide output are:	/* which == */
-
-     void begin( GLenum type );			/* GLU_TESS_BEGIN */
-     void edgeFlag( GLboolean flag );		/* GLU_TESS_EDGE_FLAG */
-     void vertex( void *data );			/* GLU_TESS_VERTEX */
-     void end( void );				/* GLU_TESS_END */
-
-  Any of the callbacks may be left undefined; if so, the corresponding
-  information will not be supplied during rendering.
-
-  The "begin" callback indicates the start of a primitive; type is one
-  of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the
-  notes on "boundary extraction" below).
-  
-  It is followed by any number of "vertex" callbacks, which supply the
-  vertices in the same order as expected by the corresponding glBegin()
-  call.  After the last vertex of a given primitive, there is a callback
-  to "end".
-
-  If the "edgeFlag" callback is provided, no triangle fans or strips
-  will be used.  When edgeFlag is called, if "flag" is GL_TRUE then each
-  vertex which follows begins an edge which lies on the polygon boundary
-  (ie. an edge which separates an interior region from an exterior one).
-  If "flag" is GL_FALSE, each vertex which follows begins an edge which lies
-  in the polygon interior.  "edgeFlag" will be called before the first
-  call to "vertex".
-
-  Other Callbacks
-  ---------------
-
-   void mesh( GLUmesh *mesh );			/* GLU_TESS_MESH */
-
-   - Returns an explicit mesh, represented using the quad-edge structure
-     (Guibas/Stolfi '85).  Other implementations of this interface might
-     use a different mesh structure, so this is available only only as an
-     SGI extension.  When the mesh is no longer needed, it should be freed
-     using
-
-	void gluDeleteMesh( GLUmesh *mesh );
-
-     There is a brief description of this data structure in the include
-     file "mesh.h".  For the full details, see L. Guibas and J. Stolfi,
-     Primitives for the manipulation of general subdivisions and the
-     computation of Voronoi diagrams, ACM Transactions on Graphics,
-     4(2):74-123, April 1985.  For an introduction, see the course notes
-     for CS348a, "Mathematical Foundations of Computer Graphics",
-     available at the Stanford bookstore (and taught during the fall
-     quarter).
-
-   void error( GLenum errno );			/* GLU_TESS_ERROR */
-
-   - errno is one of	GLU_TESS_MISSING_BEGIN_POLYGON,
-			GLU_TESS_MISSING_END_POLYGON,
-			GLU_TESS_MISSING_BEGIN_CONTOUR,
-			GLU_TESS_MISSING_END_CONTOUR,
-			GLU_TESS_COORD_TOO_LARGE,
-			GLU_TESS_NEED_COMBINE_CALLBACK
-
-     The first four are obvious.  The interface recovers from these
-     errors by inserting the missing call(s).
-  
-     GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded
-     the predefined constant GLU_TESS_MAX_COORD in absolute value, and
-     that the value has been clamped.  (Coordinate values must be small
-     enough so that two can be multiplied together without overflow.)
-
-     GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an
-     intersection between two edges in the input data, and the "combine"
-     callback (below) was not provided.  No output will be generated.
-
-
-   void combine( GLUcoord coords[3], void *data[4],	/* GLU_TESS_COMBINE */
-		 GLUcoord weight[4], void **outData );
-
-   - When the algorithm detects an intersection, or wishes to merge
-     features, it needs to create a new vertex.  The vertex is defined
-     as a linear combination of up to 4 existing vertices, referenced
-     by data[0..3].  The coefficients of the linear combination are
-     given by weight[0..3]; these weights always sum to 1.0.  All vertex
-     pointers are valid even when some of the weights are zero.
-     "coords" gives the location of the new vertex.
-
-     The user must allocate another vertex, interpolate parameters
-     using "data" and "weights", and return the new vertex pointer in
-     "outData".  This handle is supplied during rendering callbacks.
-     For example, if the polygon lies in an arbitrary plane in 3-space,
-     and we associate a color with each vertex, the combine callback might
-     look like this:
-    
-     void myCombine( GLUcoord coords[3], VERTEX *d[4],
-                     GLUcoord w[4], VERTEX **dataOut )
-     {
-        VERTEX *new = new_vertex();
-       
-        new->x = coords[0];
-        new->y = coords[1];
-        new->z = coords[2];
-        new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r;
-        new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g;
-        new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b;
-        new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a;
-        *dataOut = new;
-     }
-
-     If the algorithm detects an intersection, then the "combine" callback
-     must be defined, and must write a non-NULL pointer into "dataOut".
-     Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no
-     output is generated.  This is the only error that can occur during
-     tesselation and rendering.
-
-
-  Control over Tesselation
-  ------------------------
-  
-   void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value );
-
-   Properties defined:
-
-    - GLU_TESS_WINDING_RULE.  Possible values:
-
-	  GLU_TESS_WINDING_ODD
-	  GLU_TESS_WINDING_NONZERO
-	  GLU_TESS_WINDING_POSITIVE
-	  GLU_TESS_WINDING_NEGATIVE
-	  GLU_TESS_WINDING_ABS_GEQ_TWO
-
-      The input contours parition the plane into regions.  A winding
-      rule determines which of these regions are inside the polygon.
-      
-      For a single contour C, the winding number of a point x is simply
-      the signed number of revolutions we make around x as we travel
-      once around C (where CCW is positive).  When there are several
-      contours, the individual winding numbers are summed.  This
-      procedure associates a signed integer value with each point x in
-      the plane.  Note that the winding number is the same for all
-      points in a single region.
-
-      The winding rule classifies a region as "inside" if its winding
-      number belongs to the chosen category (odd, nonzero, positive,
-      negative, or absolute value of at least two).  The current GLU
-      tesselator implements the "odd" rule.  The "nonzero" rule is another
-      common way to define the interior.  The other three rules are
-      useful for polygon CSG operations (see below).
-
-    - GLU_TESS_BOUNDARY_ONLY.  Values: TRUE (non-zero) or FALSE (zero).
-
-      If TRUE, returns a set of closed contours which separate the
-      polygon interior and exterior (rather than a tesselation).
-      Exterior contours are oriented CCW with respect to the normal,
-      interior contours are oriented CW.  The GLU_TESS_BEGIN callback
-      uses the type GL_LINE_LOOP for each contour.
-      
-    - GLU_TESS_TOLERANCE.  Value: a real number between 0.0 and 1.0.
-
-      This specifies a tolerance for merging features to reduce the size
-      of the output.  For example, two vertices which are very close to
-      each other might be replaced by a single vertex.  The tolerance
-      is multiplied by the largest coordinate magnitude of any input vertex;
-      this specifies the maximum distance that any feature can move as the
-      result of a single merge operation.  If a single feature takes part
-      in several merge operations, the total distance moved could be larger.
-
-      Feature merging is completely optional; the tolerance is only a hint.
-      The implementation is free to merge in some cases and not in others,
-      or to never merge features at all.  The default tolerance is zero.
-      
-      The current implementation merges vertices only if they are exactly
-      coincident, regardless of the current tolerance.  A vertex is
-      spliced into an edge only if the implementation is unable to
-      distinguish which side of the edge the vertex lies on.
-      Two edges are merged only when both endpoints are identical.
-
-
-   void gluTessNormal( GLUtesselator *tess,
-		      GLUcoord x, GLUcoord y, GLUcoord z )
-
-    - Lets the user supply the polygon normal, if known.  All input data
-      is projected into a plane perpendicular to the normal before
-      tesselation.  All output triangles are oriented CCW with
-      respect to the normal (CW orientation can be obtained by
-      reversing the sign of the supplied normal).  For example, if
-      you know that all polygons lie in the x-y plane, call
-      "gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons.
-      
-    - If the supplied normal is (0,0,0) (the default value), the
-      normal is determined as follows.  The direction of the normal,
-      up to its sign, is found by fitting a plane to the vertices,
-      without regard to how the vertices are connected.  It is
-      expected that the input data lies approximately in plane;
-      otherwise projection perpendicular to the computed normal may
-      substantially change the geometry.  The sign of the normal is
-      chosen so that the sum of the signed areas of all input contours
-      is non-negative (where a CCW contour has positive area).
-    
-    - The supplied normal persists until it is changed by another
-      call to gluTessNormal.
-
-
-  Backward compatibility with the GLU tesselator
-  ----------------------------------------------
-
-  The preferred interface is the one described above.  The following
-  routines are obsolete, and are provided only for backward compatibility:
-
-    typedef GLUtesselator GLUtriangulatorObj;	/* obsolete name */
-
-    void gluBeginPolygon( GLUtesselator *tess );
-    void gluNextContour( GLUtesselator *tess, GLenum type );
-    void gluEndPolygon( GLUtesselator *tess );
-  
-  "type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or
-  GLU_UNKNOWN.  It is ignored by the current GLU tesselator.
-  
-  GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined
-  as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END,
-  GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG.
-
-
-Polygon CSG operations
-----------------------
-
-  The features of the tesselator make it easy to find the union, difference,
-  or intersection of several polygons.
-
-  First, assume that each polygon is defined so that the winding number
-  is 0 for each exterior region, and 1 for each interior region.  Under
-  this model, CCW contours define the outer boundary of the polygon, and
-  CW contours define holes.  Contours may be nested, but a nested
-  contour must be oriented oppositely from the contour that contains it.
-
-  If the original polygons do not satisfy this description, they can be
-  converted to this form by first running the tesselator with the
-  GLU_TESS_BOUNDARY_ONLY property turned on.  This returns a list of
-  contours satisfying the restriction above.  By allocating two
-  tesselator objects, the callbacks from one tesselator can be fed
-  directly to the input of another.
-
-  Given two or more polygons of the form above, CSG operations can be
-  implemented as follows:
-
-  Union
-     Draw all the input contours as a single polygon.  The winding number
-     of each resulting region is the number of original polygons
-     which cover it.  The union can be extracted using the
-     GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules.
-     Note that with the nonzero rule, we would get the same result if
-     all contour orientations were reversed.
-
-  Intersection (two polygons at a time only)
-     Draw a single polygon using the contours from both input polygons.
-     Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO.  (Since this
-     winding rule looks at the absolute value, reversing all contour
-     orientations does not change the result.)
-
-  Difference
-  
-     Suppose we want to compute A \ (B union C union D).  Draw a single
-     polygon consisting of the unmodified contours from A, followed by
-     the contours of B,C,D with the vertex order reversed (this changes
-     the winding number of the interior regions to -1).  To extract the
-     result, use the GLU_TESS_WINDING_POSITIVE rule.
-   
-     If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an
-     alternative to reversing the vertex order is to reverse the sign of
-     the supplied normal.  For example in the x-y plane, call
-     gluTessNormal( tess, 0.0, 0.0, -1.0 ).
- 
-
-Performance
------------
-
-  The tesselator is not intended for immediate-mode rendering; when
-  possible the output should be cached in a user structure or display
-  list.  General polygon tesselation is an inherently difficult problem,
-  especially given the goal of extreme robustness.
-
-  The implementation makes an effort to output a small number of fans
-  and strips; this should improve the rendering performance when the
-  output is used in a display list.
-
-  Single-contour input polygons are first tested to see whether they can
-  be rendered as a triangle fan with respect to the first vertex (to
-  avoid running the full decomposition algorithm on convex polygons).
-  Non-convex polygons may be rendered by this "fast path" as well, if
-  the algorithm gets lucky in its choice of a starting vertex.
-
-  For best performance follow these guidelines:
-
-   - supply the polygon normal, if available, using gluTessNormal().
-     This represents about 10% of the computation time.  For example,
-     if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1).
-
-   - render many polygons using the same tesselator object, rather than
-     allocating a new tesselator for each one.  (In a multi-threaded,
-     multi-processor environment you may get better performance using
-     several tesselators.)
-
-
-Comparison with the GLU tesselator
-----------------------------------
-
-  On polygons which make it through the "fast path", the tesselator is
-  3 to 5 times faster than the GLU tesselator.
-
-  On polygons which don't make it through the fast path (but which don't
-  have self-intersections or degeneracies), it is about 2 times slower.
-
-  On polygons with self-intersections or degeneraces, there is nothing
-  to compare against.
-
-  The new tesselator generates many more fans and strips, reducing the
-  number of vertices that need to be sent to the hardware.
-
-  Key to the statistics:
-
-	vert		number of input vertices on all contours
-	cntr		number of input contours
-	tri		number of triangles in all output primitives
-	strip		number of triangle strips
-	fan		number of triangle fans
-	ind		number of independent triangles
-	ms		number of milliseconds for tesselation
-			(on a 150MHz R4400 Indy)
-
-  Convex polygon examples:
-
-New:     3 vert,   1 cntr,     1 tri,   0 strip,   0 fan,     1 ind,  0.0459 ms
-Old:     3 vert,   1 cntr,     1 tri,   0 strip,   0 fan,     1 ind,   0.149 ms
-New:     4 vert,   1 cntr,     2 tri,   0 strip,   1 fan,     0 ind,  0.0459 ms
-Old:     4 vert,   1 cntr,     2 tri,   0 strip,   0 fan,     2 ind,   0.161 ms
-New:    36 vert,   1 cntr,    34 tri,   0 strip,   1 fan,     0 ind,   0.153 ms
-Old:    36 vert,   1 cntr,    34 tri,   0 strip,   0 fan,    34 ind,   0.621 ms
-
-  Concave single-contour polygons:
-
-New:     5 vert,   1 cntr,     3 tri,   0 strip,   1 fan,     0 ind,   0.052 ms
-Old:     5 vert,   1 cntr,     3 tri,   0 strip,   0 fan,     3 ind,   0.252 ms
-New:    19 vert,   1 cntr,    17 tri,   2 strip,   2 fan,     1 ind,   0.911 ms
-Old:    19 vert,   1 cntr,    17 tri,   0 strip,   0 fan,    17 ind,   0.529 ms
-New:   151 vert,   1 cntr,   149 tri,  13 strip,  18 fan,     3 ind,    6.82 ms
-Old:   151 vert,   1 cntr,   149 tri,   0 strip,   3 fan,   143 ind,     2.7 ms
-New:   574 vert,   1 cntr,   572 tri,  59 strip,  54 fan,    11 ind,    26.6 ms
-Old:   574 vert,   1 cntr,   572 tri,   0 strip,  31 fan,   499 ind,    12.4 ms
-
-  Multiple contours, but no intersections:
-
-New:     7 vert,   2 cntr,     7 tri,   1 strip,   0 fan,     0 ind,   0.527 ms
-Old:     7 vert,   2 cntr,     7 tri,   0 strip,   0 fan,     7 ind,   0.274 ms
-New:    81 vert,   6 cntr,    89 tri,   9 strip,   7 fan,     6 ind,    3.88 ms
-Old:    81 vert,   6 cntr,    89 tri,   0 strip,  13 fan,    61 ind,     2.2 ms
-New:   391 vert,  19 cntr,   413 tri,  37 strip,  32 fan,    26 ind,    20.2 ms
-Old:   391 vert,  19 cntr,   413 tri,   0 strip,  25 fan,   363 ind,    8.68 ms
-
-  Self-intersecting and degenerate examples:
-
-Bowtie:  4 vert,   1 cntr,     2 tri,   0 strip,   0 fan,     2 ind,   0.483 ms
-Star:    5 vert,   1 cntr,     5 tri,   0 strip,   0 fan,     5 ind,    0.91 ms
-Random: 24 vert,   7 cntr,    46 tri,   2 strip,  12 fan,     7 ind,    5.32 ms
-Font:  333 vert,   2 cntr,   331 tri,  32 strip,  16 fan,     3 ind,    14.1 ms
-:      167 vert,  35 cntr,   254 tri,   8 strip,  56 fan,    52 ind,    46.3 ms
-:       78 vert,   1 cntr,  2675 tri, 148 strip, 207 fan,   180 ind,     243 ms
-:    12480 vert,   2 cntr, 12478 tri, 736 strip,1275 fan,     5 ind,    1010 ms
diff --git a/third_party/glu/libtess/alg-outline b/third_party/glu/libtess/alg-outline
deleted file mode 100644
index 91ed7f3..0000000
--- a/third_party/glu/libtess/alg-outline
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
-** $Header: /cvs/projects/ogl-sample/main/gfx/lib/glu/libtess/alg-outline,v 1.1 2000/04/26 05:53:59 ljp Exp $
-*/
-
-This is only a very brief overview.  There is quite a bit of
-additional documentation in the source code itself.
-
-
-Goals of robust tesselation
----------------------------
-
-The tesselation algorithm is fundamentally a 2D algorithm.  We
-initially project all data into a plane; our goal is to robustly
-tesselate the projected data.  The same topological tesselation is
-then applied to the input data.
-
-Topologically, the output should always be a tesselation.  If the
-input is even slightly non-planar, then some triangles will
-necessarily be back-facing when viewed from some angles, but the goal
-is to minimize this effect.
-
-The algorithm needs some capability of cleaning up the input data as
-well as the numerical errors in its own calculations.  One way to do
-this is to specify a tolerance as defined above, and clean up the
-input and output during the line sweep process.  At the very least,
-the algorithm must handle coincident vertices, vertices incident to an
-edge, and coincident edges.
-
-
-Phases of the algorithm
------------------------
-
-1. Find the polygon normal N.
-2. Project the vertex data onto a plane.  It does not need to be
-   perpendicular to the normal, eg. we can project onto the plane
-   perpendicular to the coordinate axis whose dot product with N
-   is largest.
-3. Using a line-sweep algorithm, partition the plane into x-monotone
-   regions.  Any vertical line intersects an x-monotone region in
-   at most one interval.
-4. Triangulate the x-monotone regions.
-5. Group the triangles into strips and fans.
-
-
-Finding the normal vector
--------------------------
-
-A common way to find a polygon normal is to compute the signed area
-when the polygon is projected along the three coordinate axes.  We
-can't do this, since contours can have zero area without being
-degenerate (eg. a bowtie).
-
-We fit a plane to the vertex data, ignoring how they are connected
-into contours.  Ideally this would be a least-squares fit; however for
-our purpose the accuracy of the normal is not important.  Instead we
-find three vertices which are widely separated, and compute the normal
-to the triangle they form.  The vertices are chosen so that the
-triangle has an area at least 1/sqrt(3) times the largest area of any
-triangle formed using the input vertices.  
-
-The contours do affect the orientation of the normal; after computing
-the normal, we check that the sum of the signed contour areas is
-non-negative, and reverse the normal if necessary.
-
-
-Projecting the vertices
------------------------
-
-We project the vertices onto a plane perpendicular to one of the three
-coordinate axes.  This helps numerical accuracy by removing a
-transformation step between the original input data and the data
-processed by the algorithm.  The projection also compresses the input
-data; the 2D distance between vertices after projection may be smaller
-than the original 2D distance.  However by choosing the coordinate
-axis whose dot product with the normal is greatest, the compression
-factor is at most 1/sqrt(3).
-
-Even though the *accuracy* of the normal is not that important (since
-we are projecting perpendicular to a coordinate axis anyway), the
-*robustness* of the computation is important.  For example, if there
-are many vertices which lie almost along a line, and one vertex V
-which is well-separated from the line, then our normal computation
-should involve V otherwise the results will be garbage.
-
-The advantage of projecting perpendicular to the polygon normal is
-that computed intersection points will be as close as possible to
-their ideal locations.  To get this behavior, define TRUE_PROJECT.
-
-
-The Line Sweep
---------------
-
-There are three data structures: the mesh, the event queue, and the
-edge dictionary.
-
-The mesh is a "quad-edge" data structure which records the topology of
-the current decomposition; for details see the include file "mesh.h".
-
-The event queue simply holds all vertices (both original and computed
-ones), organized so that we can quickly extract the vertex with the
-minimum x-coord (and among those, the one with the minimum y-coord).
-
-The edge dictionary describes the current intersection of the sweep
-line with the regions of the polygon.  This is just an ordering of the
-edges which intersect the sweep line, sorted by their current order of
-intersection.  For each pair of edges, we store some information about
-the monotone region between them -- these are call "active regions"
-(since they are crossed by the current sweep line).
-
-The basic algorithm is to sweep from left to right, processing each
-vertex.  The processed portion of the mesh (left of the sweep line) is
-a planar decomposition.  As we cross each vertex, we update the mesh
-and the edge dictionary, then we check any newly adjacent pairs of
-edges to see if they intersect.
-
-A vertex can have any number of edges.  Vertices with many edges can
-be created as vertices are merged and intersection points are
-computed.  For unprocessed vertices (right of the sweep line), these
-edges are in no particular order around the vertex; for processed
-vertices, the topological ordering should match the geometric ordering.
-
-The vertex processing happens in two phases: first we process are the
-left-going edges (all these edges are currently in the edge
-dictionary).  This involves:
-
- - deleting the left-going edges from the dictionary;
- - relinking the mesh if necessary, so that the order of these edges around
-   the event vertex matches the order in the dictionary;
- - marking any terminated regions (regions which lie between two left-going
-   edges) as either "inside" or "outside" according to their winding number.
-
-When there are no left-going edges, and the event vertex is in an
-"interior" region, we need to add an edge (to split the region into
-monotone pieces).  To do this we simply join the event vertex to the
-rightmost left endpoint of the upper or lower edge of the containing
-region.
-
-Then we process the right-going edges.  This involves:
-
- - inserting the edges in the edge dictionary;
- - computing the winding number of any newly created active regions.
-   We can compute this incrementally using the winding of each edge
-   that we cross as we walk through the dictionary.
- - relinking the mesh if necessary, so that the order of these edges around
-   the event vertex matches the order in the dictionary;
- - checking any newly adjacent edges for intersection and/or merging.
-
-If there are no right-going edges, again we need to add one to split
-the containing region into monotone pieces.  In our case it is most
-convenient to add an edge to the leftmost right endpoint of either
-containing edge; however we may need to change this later (see the
-code for details).
-
-
-Invariants
-----------
-
-These are the most important invariants maintained during the sweep.
-We define a function VertLeq(v1,v2) which defines the order in which
-vertices cross the sweep line, and a function EdgeLeq(e1,e2; loc)
-which says whether e1 is below e2 at the sweep event location "loc".
-This function is defined only at sweep event locations which lie
-between the rightmost left endpoint of {e1,e2}, and the leftmost right
-endpoint of {e1,e2}.
-
-Invariants for the Edge Dictionary.
-
- - Each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
-   at any valid location of the sweep event.
- - If EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
-   share a common endpoint.
- - For each e in the dictionary, e->Dst has been processed but not e->Org.
- - Each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)
-   where "event" is the current sweep line event.
- - No edge e has zero length.
- - No two edges have identical left and right endpoints.
- 
-Invariants for the Mesh (the processed portion).
-
- - The portion of the mesh left of the sweep line is a planar graph,
-   ie. there is *some* way to embed it in the plane.
- - No processed edge has zero length.
- - No two processed vertices have identical coordinates.
- - Each "inside" region is monotone, ie. can be broken into two chains
-   of monotonically increasing vertices according to VertLeq(v1,v2)
-   - a non-invariant: these chains may intersect (slightly) due to
-     numerical errors, but this does not affect the algorithm's operation.
-
-Invariants for the Sweep.
-
- - If a vertex has any left-going edges, then these must be in the edge
-   dictionary at the time the vertex is processed.
- - If an edge is marked "fixUpperEdge" (it is a temporary edge introduced
-   by ConnectRightVertex), then it is the only right-going edge from
-   its associated vertex.  (This says that these edges exist only
-   when it is necessary.)
-
-
-Robustness
-----------
-
-The key to the robustness of the algorithm is maintaining the
-invariants above, especially the correct ordering of the edge
-dictionary.  We achieve this by:
-
-  1. Writing the numerical computations for maximum precision rather
-     than maximum speed.
-     
-  2. Making no assumptions at all about the results of the edge
-     intersection calculations -- for sufficiently degenerate inputs,
-     the computed location is not much better than a random number.
-     
-  3. When numerical errors violate the invariants, restore them
-     by making *topological* changes when necessary (ie. relinking
-     the mesh structure).
-     
-     
-Triangulation and Grouping
---------------------------
-
-We finish the line sweep before doing any triangulation.  This is
-because even after a monotone region is complete, there can be further
-changes to its vertex data because of further vertex merging.
-
-After triangulating all monotone regions, we want to group the
-triangles into fans and strips.  We do this using a greedy approach.
-The triangulation itself is not optimized to reduce the number of
-primitives; we just try to get a reasonable decomposition of the
-computed triangulation.
diff --git a/third_party/glu/libtess/dict-list.h b/third_party/glu/libtess/dict-list.h
deleted file mode 100644
index 9c67c49..0000000
--- a/third_party/glu/libtess/dict-list.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/dict-list.h#5 $
-*/
-
-#ifndef __dict_list_h_
-#define __dict_list_h_
-
-/* Use #define's so that another heap implementation can use this one */
-
-#define DictKey		DictListKey
-#define Dict		DictList
-#define DictNode	DictListNode
-
-#define dictNewDict(frame,leq)		__gl_dictListNewDict(frame,leq)
-#define dictDeleteDict(dict)		__gl_dictListDeleteDict(dict)
-
-#define dictSearch(dict,key)		__gl_dictListSearch(dict,key)
-#define dictInsert(dict,key)		__gl_dictListInsert(dict,key)
-#define dictInsertBefore(dict,node,key)	__gl_dictListInsertBefore(dict,node,key)
-#define dictDelete(dict,node)		__gl_dictListDelete(dict,node)
-
-#define dictKey(n)			__gl_dictListKey(n)
-#define dictSucc(n)			__gl_dictListSucc(n)
-#define dictPred(n)			__gl_dictListPred(n)
-#define dictMin(d)			__gl_dictListMin(d)
-#define dictMax(d)			__gl_dictListMax(d)
-
-
-
-typedef void *DictKey;
-typedef struct Dict Dict;
-typedef struct DictNode DictNode;
-
-Dict		*dictNewDict(
-			void *frame,
-			int (*leq)(void *frame, DictKey key1, DictKey key2) );
-			
-void		dictDeleteDict( Dict *dict );
-
-/* Search returns the node with the smallest key greater than or equal
- * to the given key.  If there is no such key, returns a node whose
- * key is NULL.  Similarly, Succ(Max(d)) has a NULL key, etc.
- */
-DictNode	*dictSearch( Dict *dict, DictKey key );
-DictNode	*dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
-void		dictDelete( Dict *dict, DictNode *node );
-
-#define		__gl_dictListKey(n)	((n)->key)
-#define		__gl_dictListSucc(n)	((n)->next)
-#define		__gl_dictListPred(n)	((n)->prev)
-#define		__gl_dictListMin(d)	((d)->head.next)
-#define		__gl_dictListMax(d)	((d)->head.prev)
-#define	       __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
-
-
-/*** Private data structures ***/
-
-struct DictNode {
-  DictKey	key;
-  DictNode	*next;
-  DictNode	*prev;
-};
-
-struct Dict {
-  DictNode	head;
-  void		*frame;
-  int		(*leq)(void *frame, DictKey key1, DictKey key2);
-};
-
-#endif
diff --git a/third_party/glu/libtess/dict.c b/third_party/glu/libtess/dict.c
deleted file mode 100644
index e149fd5..0000000
--- a/third_party/glu/libtess/dict.c
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/dict.c#5 $
-*/
-
-#include <stddef.h>
-#include "dict-list.h"
-#include "memalloc.h"
-
-/* really __gl_dictListNewDict */
-Dict *dictNewDict( void *frame,
-		   int (*leq)(void *frame, DictKey key1, DictKey key2) )
-{
-  Dict *dict = (Dict *) memAlloc( sizeof( Dict ));
-  DictNode *head;
-
-  if (dict == NULL) return NULL;
-
-  head = &dict->head;
-
-  head->key = NULL;
-  head->next = head;
-  head->prev = head;
-
-  dict->frame = frame;
-  dict->leq = leq;
-
-  return dict;
-}
-
-/* really __gl_dictListDeleteDict */
-void dictDeleteDict( Dict *dict )
-{
-  DictNode *node;
-
-  for( node = dict->head.next; node != &dict->head; node = node->next ) {
-    memFree( node );
-  }
-  memFree( dict );
-}
-
-/* really __gl_dictListInsertBefore */
-DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key )
-{
-  DictNode *newNode;
-
-  do {
-    node = node->prev;
-  } while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key));
-
-  newNode = (DictNode *) memAlloc( sizeof( DictNode ));
-  if (newNode == NULL) return NULL;
-
-  newNode->key = key;
-  newNode->next = node->next;
-  node->next->prev = newNode;
-  newNode->prev = node;
-  node->next = newNode;
-
-  return newNode;
-}
-
-/* really __gl_dictListDelete */
-void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/
-{
-  node->next->prev = node->prev;
-  node->prev->next = node->next;
-  memFree( node );
-}
-
-/* really __gl_dictListSearch */
-DictNode *dictSearch( Dict *dict, DictKey key )
-{
-  DictNode *node = &dict->head;
-
-  do {
-    node = node->next;
-  } while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key));
-
-  return node;
-}
diff --git a/third_party/glu/libtess/dict.h b/third_party/glu/libtess/dict.h
deleted file mode 100644
index fee0769..0000000
--- a/third_party/glu/libtess/dict.h
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/dict.h#5 $
-*/
-
-#ifndef __dict_list_h_
-#define __dict_list_h_
-
-#include <sk_glu.h>
-
-/* Use #define's so that another heap implementation can use this one */
-
-#define DictKey		DictListKey
-#define Dict		DictList
-#define DictNode	DictListNode
-
-#define dictNewDict(frame,leq)		__gl_dictListNewDict(frame,leq)
-#define dictDeleteDict(dict)		__gl_dictListDeleteDict(dict)
-
-#define dictSearch(dict,key)		__gl_dictListSearch(dict,key)
-#define dictInsert(dict,key)		__gl_dictListInsert(dict,key)
-#define dictInsertBefore(dict,node,key)	__gl_dictListInsertBefore(dict,node,key)
-#define dictDelete(dict,node)		__gl_dictListDelete(dict,node)
-
-#define dictKey(n)			__gl_dictListKey(n)
-#define dictSucc(n)			__gl_dictListSucc(n)
-#define dictPred(n)			__gl_dictListPred(n)
-#define dictMin(d)			__gl_dictListMin(d)
-#define dictMax(d)			__gl_dictListMax(d)
-
-
-
-typedef void *DictKey;
-typedef struct Dict Dict;
-typedef struct DictNode DictNode;
-
-Dict		*dictNewDict(
-			void *frame,
-			int (*leq)(void *frame, DictKey key1, DictKey key2) );
-			
-void		dictDeleteDict( Dict *dict );
-
-/* Search returns the node with the smallest key greater than or equal
- * to the given key.  If there is no such key, returns a node whose
- * key is NULL.  Similarly, Succ(Max(d)) has a NULL key, etc.
- */
-DictNode	*dictSearch( Dict *dict, DictKey key );
-DictNode	*dictInsertBefore( Dict *dict, DictNode *node, DictKey key );
-void		dictDelete( Dict *dict, DictNode *node );
-
-#define		__gl_dictListKey(n)	((n)->key)
-#define		__gl_dictListSucc(n)	((n)->next)
-#define		__gl_dictListPred(n)	((n)->prev)
-#define		__gl_dictListMin(d)	((d)->head.next)
-#define		__gl_dictListMax(d)	((d)->head.prev)
-#define	       __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k)))
-
-
-/*** Private data structures ***/
-
-struct DictNode {
-  DictKey	key;
-  DictNode	*next;
-  DictNode	*prev;
-};
-
-struct Dict {
-  DictNode	head;
-  void		*frame;
-  int		(*leq)(void *frame, DictKey key1, DictKey key2);
-};
-
-#endif
diff --git a/third_party/glu/libtess/geom.c b/third_party/glu/libtess/geom.c
deleted file mode 100644
index 461d8b4..0000000
--- a/third_party/glu/libtess/geom.c
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/geom.c#5 $
-*/
-
-#include "gluos.h"
-#include <assert.h>
-#include "mesh.h"
-#include "geom.h"
-
-int __gl_vertLeq( GLUvertex *u, GLUvertex *v )
-{
-  /* Returns TRUE if u is lexicographically <= v. */
-
-  return VertLeq( u, v );
-}
-
-GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
-{
-  /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w),
-   * evaluates the t-coord of the edge uw at the s-coord of the vertex v.
-   * Returns v->t - (uw)(v->s), ie. the signed distance from uw to v.
-   * If uw is vertical (and thus passes thru v), the result is zero.
-   *
-   * The calculation is extremely accurate and stable, even when v
-   * is very close to u or w.  In particular if we set v->t = 0 and
-   * let r be the negated result (this evaluates (uw)(v->s)), then
-   * r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t).
-   */
-  GLdouble gapL, gapR;
-
-  assert( VertLeq( u, v ) && VertLeq( v, w ));
-  
-  gapL = v->s - u->s;
-  gapR = w->s - v->s;
-
-  if( gapL + gapR > 0 ) {
-    if( gapL < gapR ) {
-      return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR));
-    } else {
-      return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR));
-    }
-  }
-  /* vertical line */
-  return 0;
-}
-
-GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
-{
-  /* Returns a number whose sign matches EdgeEval(u,v,w) but which
-   * is cheaper to evaluate.  Returns > 0, == 0 , or < 0
-   * as v is above, on, or below the edge uw.
-   */
-  GLdouble gapL, gapR;
-
-  assert( VertLeq( u, v ) && VertLeq( v, w ));
-  
-  gapL = v->s - u->s;
-  gapR = w->s - v->s;
-
-  if( gapL + gapR > 0 ) {
-    return (v->t - w->t) * gapL + (v->t - u->t) * gapR;
-  }
-  /* vertical line */
-  return 0;
-}
-
-
-/***********************************************************************
- * Define versions of EdgeSign, EdgeEval with s and t transposed.
- */
-
-GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w )
-{
-  /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w),
-   * evaluates the t-coord of the edge uw at the s-coord of the vertex v.
-   * Returns v->s - (uw)(v->t), ie. the signed distance from uw to v.
-   * If uw is vertical (and thus passes thru v), the result is zero.
-   *
-   * The calculation is extremely accurate and stable, even when v
-   * is very close to u or w.  In particular if we set v->s = 0 and
-   * let r be the negated result (this evaluates (uw)(v->t)), then
-   * r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s).
-   */
-  GLdouble gapL, gapR;
-
-  assert( TransLeq( u, v ) && TransLeq( v, w ));
-  
-  gapL = v->t - u->t;
-  gapR = w->t - v->t;
-
-  if( gapL + gapR > 0 ) {
-    if( gapL < gapR ) {
-      return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR));
-    } else {
-      return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR));
-    }
-  }
-  /* vertical line */
-  return 0;
-}
-
-GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w )
-{
-  /* Returns a number whose sign matches TransEval(u,v,w) but which
-   * is cheaper to evaluate.  Returns > 0, == 0 , or < 0
-   * as v is above, on, or below the edge uw.
-   */
-  GLdouble gapL, gapR;
-
-  assert( TransLeq( u, v ) && TransLeq( v, w ));
-  
-  gapL = v->t - u->t;
-  gapR = w->t - v->t;
-
-  if( gapL + gapR > 0 ) {
-    return (v->s - w->s) * gapL + (v->s - u->s) * gapR;
-  }
-  /* vertical line */
-  return 0;
-}
-
-
-int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w )
-{
-  /* For almost-degenerate situations, the results are not reliable.
-   * Unless the floating-point arithmetic can be performed without
-   * rounding errors, *any* implementation will give incorrect results
-   * on some degenerate inputs, so the client must have some way to
-   * handle this situation.
-   */
-  return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0;
-}
-
-/* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b),
- * or (x+y)/2 if a==b==0.  It requires that a,b >= 0, and enforces
- * this in the rare case that one argument is slightly negative.
- * The implementation is extremely stable numerically.
- * In particular it guarantees that the result r satisfies
- * MIN(x,y) <= r <= MAX(x,y), and the results are very accurate
- * even when a and b differ greatly in magnitude.
- */
-#define RealInterpolate(a,x,b,y)			\
-  (a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b,		\
-  ((a <= b) ? ((b == 0) ? ((x+y) / 2)			\
-                        : (x + (y-x) * (a/(a+b))))	\
-            : (y + (x-y) * (b/(a+b)))))
-
-#ifndef FOR_TRITE_TEST_PROGRAM
-#define Interpolate(a,x,b,y)	RealInterpolate(a,x,b,y)
-#else
-
-/* Claim: the ONLY property the sweep algorithm relies on is that
- * MIN(x,y) <= r <= MAX(x,y).  This is a nasty way to test that.
- */
-#include <stdlib.h>
-extern int RandomInterpolate;
-
-GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y)
-{
-printf("*********************%d\n",RandomInterpolate);
-  if( RandomInterpolate ) {
-    a = 1.2 * drand48() - 0.1;
-    a = (a < 0) ? 0 : ((a > 1) ? 1 : a);
-    b = 1.0 - a;
-  }
-  return RealInterpolate(a,x,b,y);
-}
-
-#endif
-
-#define Swap(a,b)	do { GLUvertex *t = a; a = b; b = t; } while(0)
-
-void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
-			 GLUvertex *o2, GLUvertex *d2,
-			 GLUvertex *v )
-/* Given edges (o1,d1) and (o2,d2), compute their point of intersection.
- * The computed point is guaranteed to lie in the intersection of the
- * bounding rectangles defined by each edge.
- */
-{
-  GLdouble z1, z2;
-
-  /* This is certainly not the most efficient way to find the intersection
-   * of two line segments, but it is very numerically stable.
-   *
-   * Strategy: find the two middle vertices in the VertLeq ordering,
-   * and interpolate the intersection s-value from these.  Then repeat
-   * using the TransLeq ordering to find the intersection t-value.
-   */
-
-  if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); }
-  if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); }
-  if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
-
-  if( ! VertLeq( o2, d1 )) {
-    /* Technically, no intersection -- do our best */
-    v->s = (o2->s + d1->s) / 2;
-  } else if( VertLeq( d1, d2 )) {
-    /* Interpolate between o2 and d1 */
-    z1 = EdgeEval( o1, o2, d1 );
-    z2 = EdgeEval( o2, d1, d2 );
-    if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
-    v->s = Interpolate( z1, o2->s, z2, d1->s );
-  } else {
-    /* Interpolate between o2 and d2 */
-    z1 = EdgeSign( o1, o2, d1 );
-    z2 = -EdgeSign( o1, d2, d1 );
-    if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
-    v->s = Interpolate( z1, o2->s, z2, d2->s );
-  }
-
-  /* Now repeat the process for t */
-
-  if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); }
-  if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); }
-  if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); }
-
-  if( ! TransLeq( o2, d1 )) {
-    /* Technically, no intersection -- do our best */
-    v->t = (o2->t + d1->t) / 2;
-  } else if( TransLeq( d1, d2 )) {
-    /* Interpolate between o2 and d1 */
-    z1 = TransEval( o1, o2, d1 );
-    z2 = TransEval( o2, d1, d2 );
-    if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
-    v->t = Interpolate( z1, o2->t, z2, d1->t );
-  } else {
-    /* Interpolate between o2 and d2 */
-    z1 = TransSign( o1, o2, d1 );
-    z2 = -TransSign( o1, d2, d1 );
-    if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; }
-    v->t = Interpolate( z1, o2->t, z2, d2->t );
-  }
-}
diff --git a/third_party/glu/libtess/geom.h b/third_party/glu/libtess/geom.h
deleted file mode 100644
index b4d0c66..0000000
--- a/third_party/glu/libtess/geom.h
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/geom.h#5 $
-*/
-
-#ifndef __geom_h_
-#define __geom_h_
-
-#include "mesh.h"
-
-#ifdef NO_BRANCH_CONDITIONS
-/* MIPS architecture has special instructions to evaluate boolean
- * conditions -- more efficient than branching, IF you can get the
- * compiler to generate the right instructions (SGI compiler doesn't)
- */
-#define VertEq(u,v)	(((u)->s == (v)->s) & ((u)->t == (v)->t))
-#define VertLeq(u,v)	(((u)->s < (v)->s) | \
-                         ((u)->s == (v)->s & (u)->t <= (v)->t))
-#else
-#define VertEq(u,v)	((u)->s == (v)->s && (u)->t == (v)->t)
-#define VertLeq(u,v)	(((u)->s < (v)->s) || \
-                         ((u)->s == (v)->s && (u)->t <= (v)->t))
-#endif
-
-#define EdgeEval(u,v,w)	__gl_edgeEval(u,v,w)
-#define EdgeSign(u,v,w)	__gl_edgeSign(u,v,w)
-
-/* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */
-
-#define TransLeq(u,v)	(((u)->t < (v)->t) || \
-                         ((u)->t == (v)->t && (u)->s <= (v)->s))
-#define TransEval(u,v,w)	__gl_transEval(u,v,w)
-#define TransSign(u,v,w)	__gl_transSign(u,v,w)
-
-
-#define EdgeGoesLeft(e)		VertLeq( (e)->Dst, (e)->Org )
-#define EdgeGoesRight(e)	VertLeq( (e)->Org, (e)->Dst )
-
-#define ABS(x)	((x) < 0 ? -(x) : (x))
-#define VertL1dist(u,v)	(ABS(u->s - v->s) + ABS(u->t - v->t))
-
-#define VertCCW(u,v,w)	__gl_vertCCW(u,v,w)
-
-int		__gl_vertLeq( GLUvertex *u, GLUvertex *v );
-GLdouble	__gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
-GLdouble	__gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
-GLdouble	__gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w );
-GLdouble	__gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w );
-int		__gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w );
-void		__gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1,
-				    GLUvertex *o2, GLUvertex *d2,
-				    GLUvertex *v );
-
-#endif
diff --git a/third_party/glu/libtess/memalloc.c b/third_party/glu/libtess/memalloc.c
deleted file mode 100644
index bd5b4ac..0000000
--- a/third_party/glu/libtess/memalloc.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/memalloc.c#5 $
-*/
-
-#include "memalloc.h"
-#include "string.h"
-
-int __gl_memInit( size_t maxFast )
-{
-#ifndef NO_MALLOPT
-/*  mallopt( M_MXFAST, maxFast );*/
-#ifdef MEMORY_DEBUG
-  mallopt( M_DEBUG, 1 );
-#endif
-#endif
-   return 1;
-}
-
-#ifdef MEMORY_DEBUG
-void *__gl_memAlloc( size_t n )
-{
-  return memset( malloc( n ), 0xa5, n );
-}
-#endif
-
diff --git a/third_party/glu/libtess/memalloc.h b/third_party/glu/libtess/memalloc.h
deleted file mode 100644
index 7133ada..0000000
--- a/third_party/glu/libtess/memalloc.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/memalloc.h#5 $
-*/
-
-#ifndef __memalloc_simple_h_
-#define __memalloc_simple_h_
-
-#include <sk_glu.h>
-#include <stdlib.h>
-
-#define memRealloc	realloc
-#define memFree		free
-
-#define memInit		__gl_memInit
-/*extern void		__gl_memInit( size_t );*/
-extern int		__gl_memInit( size_t );
-
-#ifndef MEMORY_DEBUG
-#define memAlloc	malloc
-#else
-#define memAlloc	__gl_memAlloc
-extern void *		__gl_memAlloc( size_t );
-#endif
-
-#endif
diff --git a/third_party/glu/libtess/mesh.c b/third_party/glu/libtess/mesh.c
deleted file mode 100644
index 7305fab..0000000
--- a/third_party/glu/libtess/mesh.c
+++ /dev/null
@@ -1,796 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/mesh.c#6 $
-*/
-
-#include "gluos.h"
-#include <stddef.h>
-#include <assert.h>
-#include "mesh.h"
-#include "memalloc.h"
-
-#define TRUE 1
-#define FALSE 0
-
-static GLUvertex *allocVertex()
-{
-   return (GLUvertex *)memAlloc( sizeof( GLUvertex ));
-}
-
-static GLUface *allocFace()
-{
-   return (GLUface *)memAlloc( sizeof( GLUface ));
-}
-
-/************************ Utility Routines ************************/
-
-/* Allocate and free half-edges in pairs for efficiency.
- * The *only* place that should use this fact is allocation/free.
- */
-typedef struct { GLUhalfEdge e, eSym; } EdgePair;
-
-/* MakeEdge creates a new pair of half-edges which form their own loop.
- * No vertex or face structures are allocated, but these must be assigned
- * before the current edge operation is completed.
- */
-static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext )
-{
-  GLUhalfEdge *e;
-  GLUhalfEdge *eSym;
-  GLUhalfEdge *ePrev;
-  EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair ));
-  if (pair == NULL) return NULL;
-
-  e = &pair->e;
-  eSym = &pair->eSym;
-
-  /* Make sure eNext points to the first edge of the edge pair */
-  if( eNext->Sym < eNext ) { eNext = eNext->Sym; }
-
-  /* Insert in circular doubly-linked list before eNext.
-   * Note that the prev pointer is stored in Sym->next.
-   */
-  ePrev = eNext->Sym->next;
-  eSym->next = ePrev;
-  ePrev->Sym->next = e;
-  e->next = eNext;
-  eNext->Sym->next = eSym;
-
-  e->Sym = eSym;
-  e->Onext = e;
-  e->Lnext = eSym;
-  e->Org = NULL;
-  e->Lface = NULL;
-  e->winding = 0;
-  e->activeRegion = NULL;
-
-  eSym->Sym = e;
-  eSym->Onext = eSym;
-  eSym->Lnext = e;
-  eSym->Org = NULL;
-  eSym->Lface = NULL;
-  eSym->winding = 0;
-  eSym->activeRegion = NULL;
-
-  return e;
-}
-
-/* Splice( a, b ) is best described by the Guibas/Stolfi paper or the
- * CS348a notes (see mesh.h).  Basically it modifies the mesh so that
- * a->Onext and b->Onext are exchanged.  This can have various effects
- * depending on whether a and b belong to different face or vertex rings.
- * For more explanation see __gl_meshSplice() below.
- */
-static void Splice( GLUhalfEdge *a, GLUhalfEdge *b )
-{
-  GLUhalfEdge *aOnext = a->Onext;
-  GLUhalfEdge *bOnext = b->Onext;
-
-  aOnext->Sym->Lnext = b;
-  bOnext->Sym->Lnext = a;
-  a->Onext = bOnext;
-  b->Onext = aOnext;
-}
-
-/* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the
- * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives
- * a place to insert the new vertex in the global vertex list.  We insert
- * the new vertex *before* vNext so that algorithms which walk the vertex
- * list will not see the newly created vertices.
- */
-static void MakeVertex( GLUvertex *newVertex, 
-			GLUhalfEdge *eOrig, GLUvertex *vNext )
-{
-  GLUhalfEdge *e;
-  GLUvertex *vPrev;
-  GLUvertex *vNew = newVertex;
-
-  assert(vNew != NULL);
-
-  /* insert in circular doubly-linked list before vNext */
-  vPrev = vNext->prev;
-  vNew->prev = vPrev;
-  vPrev->next = vNew;
-  vNew->next = vNext;
-  vNext->prev = vNew;
-
-  vNew->anEdge = eOrig;
-  vNew->data = NULL;
-  /* leave coords, s, t undefined */
-
-  /* fix other edges on this vertex loop */
-  e = eOrig;
-  do {
-    e->Org = vNew;
-    e = e->Onext;
-  } while( e != eOrig );
-}
-
-/* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left
- * face of all edges in the face loop to which eOrig belongs.  "fNext" gives
- * a place to insert the new face in the global face list.  We insert
- * the new face *before* fNext so that algorithms which walk the face
- * list will not see the newly created faces.
- */
-static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext )
-{
-  GLUhalfEdge *e;
-  GLUface *fPrev;
-  GLUface *fNew = newFace;
-
-  assert(fNew != NULL); 
-
-  /* insert in circular doubly-linked list before fNext */
-  fPrev = fNext->prev;
-  fNew->prev = fPrev;
-  fPrev->next = fNew;
-  fNew->next = fNext;
-  fNext->prev = fNew;
-
-  fNew->anEdge = eOrig;
-  fNew->data = NULL;
-  fNew->trail = NULL;
-  fNew->marked = FALSE;
-
-  /* The new face is marked "inside" if the old one was.  This is a
-   * convenience for the common case where a face has been split in two.
-   */
-  fNew->inside = fNext->inside;
-
-  /* fix other edges on this face loop */
-  e = eOrig;
-  do {
-    e->Lface = fNew;
-    e = e->Lnext;
-  } while( e != eOrig );
-}
-
-/* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym),
- * and removes from the global edge list.
- */
-static void KillEdge( GLUhalfEdge *eDel )
-{
-  GLUhalfEdge *ePrev, *eNext;
-
-  /* Half-edges are allocated in pairs, see EdgePair above */
-  if( eDel->Sym < eDel ) { eDel = eDel->Sym; }
-
-  /* delete from circular doubly-linked list */
-  eNext = eDel->next;
-  ePrev = eDel->Sym->next;
-  eNext->Sym->next = ePrev;
-  ePrev->Sym->next = eNext;
-
-  memFree( eDel );
-}
-
-
-/* KillVertex( vDel ) destroys a vertex and removes it from the global
- * vertex list.  It updates the vertex loop to point to a given new vertex.
- */
-static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg )
-{
-  GLUhalfEdge *e, *eStart = vDel->anEdge;
-  GLUvertex *vPrev, *vNext;
-
-  /* change the origin of all affected edges */
-  e = eStart;
-  do {
-    e->Org = newOrg;
-    e = e->Onext;
-  } while( e != eStart );
-
-  /* delete from circular doubly-linked list */
-  vPrev = vDel->prev;
-  vNext = vDel->next;
-  vNext->prev = vPrev;
-  vPrev->next = vNext;
-
-  memFree( vDel );
-}
-
-/* KillFace( fDel ) destroys a face and removes it from the global face
- * list.  It updates the face loop to point to a given new face.
- */
-static void KillFace( GLUface *fDel, GLUface *newLface )
-{
-  GLUhalfEdge *e, *eStart = fDel->anEdge;
-  GLUface *fPrev, *fNext;
-
-  /* change the left face of all affected edges */
-  e = eStart;
-  do {
-    e->Lface = newLface;
-    e = e->Lnext;
-  } while( e != eStart );
-
-  /* delete from circular doubly-linked list */
-  fPrev = fDel->prev;
-  fNext = fDel->next;
-  fNext->prev = fPrev;
-  fPrev->next = fNext;
-
-  memFree( fDel );
-}
-
-
-/****************** Basic Edge Operations **********************/
-
-/* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face).
- * The loop consists of the two new half-edges.
- */
-GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh )
-{
-  GLUvertex *newVertex1= allocVertex();
-  GLUvertex *newVertex2= allocVertex();
-  GLUface *newFace= allocFace();
-  GLUhalfEdge *e;
-
-  /* if any one is null then all get freed */
-  if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) {
-     if (newVertex1 != NULL) memFree(newVertex1);
-     if (newVertex2 != NULL) memFree(newVertex2);
-     if (newFace != NULL) memFree(newFace);     
-     return NULL;
-  } 
-
-  e = MakeEdge( &mesh->eHead );
-  if (e == NULL) return NULL;
-
-  MakeVertex( newVertex1, e, &mesh->vHead );
-  MakeVertex( newVertex2, e->Sym, &mesh->vHead );
-  MakeFace( newFace, e, &mesh->fHead );
-  return e;
-}
-  
-
-/* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
- * mesh connectivity and topology.  It changes the mesh so that
- *	eOrg->Onext <- OLD( eDst->Onext )
- *	eDst->Onext <- OLD( eOrg->Onext )
- * where OLD(...) means the value before the meshSplice operation.
- *
- * This can have two effects on the vertex structure:
- *  - if eOrg->Org != eDst->Org, the two vertices are merged together
- *  - if eOrg->Org == eDst->Org, the origin is split into two vertices
- * In both cases, eDst->Org is changed and eOrg->Org is untouched.
- *
- * Similarly (and independently) for the face structure,
- *  - if eOrg->Lface == eDst->Lface, one loop is split into two
- *  - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
- * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
- *
- * Some special cases:
- * If eDst == eOrg, the operation has no effect.
- * If eDst == eOrg->Lnext, the new face will have a single edge.
- * If eDst == eOrg->Lprev, the old face will have a single edge.
- * If eDst == eOrg->Onext, the new vertex will have a single edge.
- * If eDst == eOrg->Oprev, the old vertex will have a single edge.
- */
-int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
-{
-  int joiningLoops = FALSE;
-  int joiningVertices = FALSE;
-
-  if( eOrg == eDst ) return 1;
-
-  if( eDst->Org != eOrg->Org ) {
-    /* We are merging two disjoint vertices -- destroy eDst->Org */
-    joiningVertices = TRUE;
-    KillVertex( eDst->Org, eOrg->Org );
-  }
-  if( eDst->Lface != eOrg->Lface ) {
-    /* We are connecting two disjoint loops -- destroy eDst->Lface */
-    joiningLoops = TRUE;
-    KillFace( eDst->Lface, eOrg->Lface );
-  }
-
-  /* Change the edge structure */
-  Splice( eDst, eOrg );
-
-  if( ! joiningVertices ) {
-    GLUvertex *newVertex= allocVertex();
-    if (newVertex == NULL) return 0;
-
-    /* We split one vertex into two -- the new vertex is eDst->Org.
-     * Make sure the old vertex points to a valid half-edge.
-     */
-    MakeVertex( newVertex, eDst, eOrg->Org );
-    eOrg->Org->anEdge = eOrg;
-  }
-  if( ! joiningLoops ) {
-    GLUface *newFace= allocFace();  
-    if (newFace == NULL) return 0;
-
-    /* We split one loop into two -- the new loop is eDst->Lface.
-     * Make sure the old face points to a valid half-edge.
-     */
-    MakeFace( newFace, eDst, eOrg->Lface );
-    eOrg->Lface->anEdge = eOrg;
-  }
-
-  return 1;
-}
-
-
-/* __gl_meshDelete( eDel ) removes the edge eDel.  There are several cases:
- * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
- * eDel->Lface is deleted.  Otherwise, we are splitting one loop into two;
- * the newly created loop will contain eDel->Dst.  If the deletion of eDel
- * would create isolated vertices, those are deleted as well.
- *
- * This function could be implemented as two calls to __gl_meshSplice
- * plus a few calls to memFree, but this would allocate and delete
- * unnecessary vertices and faces.
- */
-int __gl_meshDelete( GLUhalfEdge *eDel )
-{
-  GLUhalfEdge *eDelSym = eDel->Sym;
-  int joiningLoops = FALSE;
-
-  /* First step: disconnect the origin vertex eDel->Org.  We make all
-   * changes to get a consistent mesh in this "intermediate" state.
-   */
-  if( eDel->Lface != eDel->Rface ) {
-    /* We are joining two loops into one -- remove the left face */
-    joiningLoops = TRUE;
-    KillFace( eDel->Lface, eDel->Rface );
-  }
-
-  if( eDel->Onext == eDel ) {
-    KillVertex( eDel->Org, NULL );
-  } else {
-    /* Make sure that eDel->Org and eDel->Rface point to valid half-edges */
-    eDel->Rface->anEdge = eDel->Oprev;
-    eDel->Org->anEdge = eDel->Onext;
-
-    Splice( eDel, eDel->Oprev );
-    if( ! joiningLoops ) {
-      GLUface *newFace= allocFace();
-      if (newFace == NULL) return 0; 
-
-      /* We are splitting one loop into two -- create a new loop for eDel. */
-      MakeFace( newFace, eDel, eDel->Lface );
-    }
-  }
-
-  /* Claim: the mesh is now in a consistent state, except that eDel->Org
-   * may have been deleted.  Now we disconnect eDel->Dst.
-   */
-  if( eDelSym->Onext == eDelSym ) {
-    KillVertex( eDelSym->Org, NULL );
-    KillFace( eDelSym->Lface, NULL );
-  } else {
-    /* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */
-    eDel->Lface->anEdge = eDelSym->Oprev;
-    eDelSym->Org->anEdge = eDelSym->Onext;
-    Splice( eDelSym, eDelSym->Oprev );
-  }
-
-  /* Any isolated vertices or faces have already been freed. */
-  KillEdge( eDel );
-
-  return 1;
-}
-
-
-/******************** Other Edge Operations **********************/
-
-/* All these routines can be implemented with the basic edge
- * operations above.  They are provided for convenience and efficiency.
- */
-
-
-/* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
- * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
- * eOrg and eNew will have the same left face.
- */
-GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg )
-{
-  GLUhalfEdge *eNewSym;
-  GLUhalfEdge *eNew = MakeEdge( eOrg );
-  if (eNew == NULL) return NULL;
-
-  eNewSym = eNew->Sym;
-
-  /* Connect the new edge appropriately */
-  Splice( eNew, eOrg->Lnext );
-
-  /* Set the vertex and face information */
-  eNew->Org = eOrg->Dst;
-  {
-    GLUvertex *newVertex= allocVertex();
-    if (newVertex == NULL) return NULL;
-
-    MakeVertex( newVertex, eNewSym, eNew->Org );
-  }
-  eNew->Lface = eNewSym->Lface = eOrg->Lface;
-
-  return eNew;
-}
-
-
-/* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
- * such that eNew == eOrg->Lnext.  The new vertex is eOrg->Dst == eNew->Org.
- * eOrg and eNew will have the same left face.
- */
-GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg )
-{
-  GLUhalfEdge *eNew;
-  GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg );
-  if (tempHalfEdge == NULL) return NULL;
-
-  eNew = tempHalfEdge->Sym;
-
-  /* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */
-  Splice( eOrg->Sym, eOrg->Sym->Oprev );
-  Splice( eOrg->Sym, eNew );
-
-  /* Set the vertex and face information */
-  eOrg->Dst = eNew->Org;
-  eNew->Dst->anEdge = eNew->Sym;	/* may have pointed to eOrg->Sym */
-  eNew->Rface = eOrg->Rface;
-  eNew->winding = eOrg->winding;	/* copy old winding information */
-  eNew->Sym->winding = eOrg->Sym->winding;
-
-  return eNew;
-}
-
-
-/* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
- * to eDst->Org, and returns the corresponding half-edge eNew.
- * If eOrg->Lface == eDst->Lface, this splits one loop into two,
- * and the newly created loop is eNew->Lface.  Otherwise, two disjoint
- * loops are merged into one, and the loop eDst->Lface is destroyed.
- *
- * If (eOrg == eDst), the new face will have only two edges.
- * If (eOrg->Lnext == eDst), the old face is reduced to a single edge.
- * If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges.
- */
-GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst )
-{
-  GLUhalfEdge *eNewSym;
-  int joiningLoops = FALSE;  
-  GLUhalfEdge *eNew = MakeEdge( eOrg );
-  if (eNew == NULL) return NULL;
-
-  eNewSym = eNew->Sym;
-
-  if( eDst->Lface != eOrg->Lface ) {
-    /* We are connecting two disjoint loops -- destroy eDst->Lface */
-    joiningLoops = TRUE;
-    KillFace( eDst->Lface, eOrg->Lface );
-  }
-
-  /* Connect the new edge appropriately */
-  Splice( eNew, eOrg->Lnext );
-  Splice( eNewSym, eDst );
-
-  /* Set the vertex and face information */
-  eNew->Org = eOrg->Dst;
-  eNewSym->Org = eDst->Org;
-  eNew->Lface = eNewSym->Lface = eOrg->Lface;
-
-  /* Make sure the old face points to a valid half-edge */
-  eOrg->Lface->anEdge = eNewSym;
-
-  if( ! joiningLoops ) {
-    GLUface *newFace= allocFace();
-    if (newFace == NULL) return NULL;
-
-    /* We split one loop into two -- the new loop is eNew->Lface */
-    MakeFace( newFace, eNew, eOrg->Lface );
-  }
-  return eNew;
-}
-
-
-/******************** Other Operations **********************/
-
-/* __gl_meshZapFace( fZap ) destroys a face and removes it from the
- * global face list.  All edges of fZap will have a NULL pointer as their
- * left face.  Any edges which also have a NULL pointer as their right face
- * are deleted entirely (along with any isolated vertices this produces).
- * An entire mesh can be deleted by zapping its faces, one at a time,
- * in any order.  Zapped faces cannot be used in further mesh operations!
- */
-void __gl_meshZapFace( GLUface *fZap )
-{
-  GLUhalfEdge *eStart = fZap->anEdge;
-  GLUhalfEdge *e, *eNext, *eSym;
-  GLUface *fPrev, *fNext;
-
-  /* walk around face, deleting edges whose right face is also NULL */
-  eNext = eStart->Lnext;
-  do {
-    e = eNext;
-    eNext = e->Lnext;
-
-    e->Lface = NULL;
-    if( e->Rface == NULL ) {
-      /* delete the edge -- see __gl_MeshDelete above */
-
-      if( e->Onext == e ) {
-	KillVertex( e->Org, NULL );
-      } else {
-	/* Make sure that e->Org points to a valid half-edge */
-	e->Org->anEdge = e->Onext;
-	Splice( e, e->Oprev );
-      }
-      eSym = e->Sym;
-      if( eSym->Onext == eSym ) {
-	KillVertex( eSym->Org, NULL );
-      } else {
-	/* Make sure that eSym->Org points to a valid half-edge */
-	eSym->Org->anEdge = eSym->Onext;
-	Splice( eSym, eSym->Oprev );
-      }
-      KillEdge( e );
-    }
-  } while( e != eStart );
-
-  /* delete from circular doubly-linked list */
-  fPrev = fZap->prev;
-  fNext = fZap->next;
-  fNext->prev = fPrev;
-  fPrev->next = fNext;
-
-  memFree( fZap );
-}
-
-
-/* __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
- * and no loops (what we usually call a "face").
- */
-GLUmesh *__gl_meshNewMesh( void )
-{
-  GLUvertex *v;
-  GLUface *f;
-  GLUhalfEdge *e;
-  GLUhalfEdge *eSym;
-  GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh ));
-  if (mesh == NULL) {
-     return NULL;
-  }
-  
-  v = &mesh->vHead;
-  f = &mesh->fHead;
-  e = &mesh->eHead;
-  eSym = &mesh->eHeadSym;
-
-  v->next = v->prev = v;
-  v->anEdge = NULL;
-  v->data = NULL;
-
-  f->next = f->prev = f;
-  f->anEdge = NULL;
-  f->data = NULL;
-  f->trail = NULL;
-  f->marked = FALSE;
-  f->inside = FALSE;
-
-  e->next = e;
-  e->Sym = eSym;
-  e->Onext = NULL;
-  e->Lnext = NULL;
-  e->Org = NULL;
-  e->Lface = NULL;
-  e->winding = 0;
-  e->activeRegion = NULL;
-
-  eSym->next = eSym;
-  eSym->Sym = e;
-  eSym->Onext = NULL;
-  eSym->Lnext = NULL;
-  eSym->Org = NULL;
-  eSym->Lface = NULL;
-  eSym->winding = 0;
-  eSym->activeRegion = NULL;
-
-  return mesh;
-}
-
-
-/* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
- * both meshes, and returns the new mesh (the old meshes are destroyed).
- */
-GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 )
-{
-  GLUface *f1 = &mesh1->fHead;
-  GLUvertex *v1 = &mesh1->vHead;
-  GLUhalfEdge *e1 = &mesh1->eHead;
-  GLUface *f2 = &mesh2->fHead;
-  GLUvertex *v2 = &mesh2->vHead;
-  GLUhalfEdge *e2 = &mesh2->eHead;
-
-  /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */
-  if( f2->next != f2 ) {
-    f1->prev->next = f2->next;
-    f2->next->prev = f1->prev;
-    f2->prev->next = f1;
-    f1->prev = f2->prev;
-  }
-
-  if( v2->next != v2 ) {
-    v1->prev->next = v2->next;
-    v2->next->prev = v1->prev;
-    v2->prev->next = v1;
-    v1->prev = v2->prev;
-  }
-
-  if( e2->next != e2 ) {
-    e1->Sym->next->Sym->next = e2->next;
-    e2->next->Sym->next = e1->Sym->next;
-    e2->Sym->next->Sym->next = e1;
-    e1->Sym->next = e2->Sym->next;
-  }
-
-  memFree( mesh2 );
-  return mesh1;
-}
-
-
-#ifdef DELETE_BY_ZAPPING
-
-/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
- */
-void __gl_meshDeleteMesh( GLUmesh *mesh )
-{
-  GLUface *fHead = &mesh->fHead;
-
-  while( fHead->next != fHead ) {
-    __gl_meshZapFace( fHead->next );
-  }
-  assert( mesh->vHead.next == &mesh->vHead );
-
-  memFree( mesh );
-}
-
-#else
-
-/* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
- */
-void __gl_meshDeleteMesh( GLUmesh *mesh )
-{
-  GLUface *f, *fNext;
-  GLUvertex *v, *vNext;
-  GLUhalfEdge *e, *eNext;
-
-  for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
-    fNext = f->next;
-    memFree( f );
-  }
-
-  for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) {
-    vNext = v->next;
-    memFree( v );
-  }
-
-  for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
-    /* One call frees both e and e->Sym (see EdgePair above) */
-    eNext = e->next;
-    memFree( e );
-  }
-
-  memFree( mesh );
-}
-
-#endif
-
-#ifndef NDEBUG
-
-/* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
- */
-void __gl_meshCheckMesh( GLUmesh *mesh )
-{
-  GLUface *fHead = &mesh->fHead;
-  GLUvertex *vHead = &mesh->vHead;
-  GLUhalfEdge *eHead = &mesh->eHead;
-  GLUface *f, *fPrev;
-  GLUvertex *v, *vPrev;
-  GLUhalfEdge *e, *ePrev;
-
-  fPrev = fHead;
-  for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) {
-    assert( f->prev == fPrev );
-    e = f->anEdge;
-    do {
-      assert( e->Sym != e );
-      assert( e->Sym->Sym == e );
-      assert( e->Lnext->Onext->Sym == e );
-      assert( e->Onext->Sym->Lnext == e );
-      assert( e->Lface == f );
-      e = e->Lnext;
-    } while( e != f->anEdge );
-  }
-  assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL );
-
-  vPrev = vHead;
-  for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) {
-    assert( v->prev == vPrev );
-    e = v->anEdge;
-    do {
-      assert( e->Sym != e );
-      assert( e->Sym->Sym == e );
-      assert( e->Lnext->Onext->Sym == e );
-      assert( e->Onext->Sym->Lnext == e );
-      assert( e->Org == v );
-      e = e->Onext;
-    } while( e != v->anEdge );
-  }
-  assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL );
-
-  ePrev = eHead;
-  for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) {
-    assert( e->Sym->next == ePrev->Sym );
-    assert( e->Sym != e );
-    assert( e->Sym->Sym == e );
-    assert( e->Org != NULL );
-    assert( e->Dst != NULL );
-    assert( e->Lnext->Onext->Sym == e );
-    assert( e->Onext->Sym->Lnext == e );
-  }
-  assert( e->Sym->next == ePrev->Sym
-       && e->Sym == &mesh->eHeadSym
-       && e->Sym->Sym == e
-       && e->Org == NULL && e->Dst == NULL
-       && e->Lface == NULL && e->Rface == NULL );
-}
-
-#endif
diff --git a/third_party/glu/libtess/mesh.h b/third_party/glu/libtess/mesh.h
deleted file mode 100644
index da149d7..0000000
--- a/third_party/glu/libtess/mesh.h
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/mesh.h#5 $
-*/
-
-#ifndef __mesh_h_
-#define __mesh_h_
-
-#include <sk_glu.h>
-
-typedef struct GLUmesh GLUmesh; 
-
-typedef struct GLUvertex GLUvertex;
-typedef struct GLUface GLUface;
-typedef struct GLUhalfEdge GLUhalfEdge;
-
-typedef struct ActiveRegion ActiveRegion;	/* Internal data */
-
-/* The mesh structure is similar in spirit, notation, and operations
- * to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives
- * for the manipulation of general subdivisions and the computation of
- * Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985).
- * For a simplified description, see the course notes for CS348a,
- * "Mathematical Foundations of Computer Graphics", available at the
- * Stanford bookstore (and taught during the fall quarter).
- * The implementation also borrows a tiny subset of the graph-based approach
- * use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction
- * to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988).
- *
- * The fundamental data structure is the "half-edge".  Two half-edges
- * go together to make an edge, but they point in opposite directions.
- * Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym),
- * its origin vertex (Org), the face on its left side (Lface), and the
- * adjacent half-edges in the CCW direction around the origin vertex
- * (Onext) and around the left face (Lnext).  There is also a "next"
- * pointer for the global edge list (see below).
- *
- * The notation used for mesh navigation:
- *	Sym   = the mate of a half-edge (same edge, but opposite direction)
- *	Onext = edge CCW around origin vertex (keep same origin)
- *	Dnext = edge CCW around destination vertex (keep same dest)
- *	Lnext = edge CCW around left face (dest becomes new origin)
- *	Rnext = edge CCW around right face (origin becomes new dest)
- *
- * "prev" means to substitute CW for CCW in the definitions above.
- *
- * The mesh keeps global lists of all vertices, faces, and edges,
- * stored as doubly-linked circular lists with a dummy header node.
- * The mesh stores pointers to these dummy headers (vHead, fHead, eHead).
- *
- * The circular edge list is special; since half-edges always occur
- * in pairs (e and e->Sym), each half-edge stores a pointer in only
- * one direction.  Starting at eHead and following the e->next pointers
- * will visit each *edge* once (ie. e or e->Sym, but not both).
- * e->Sym stores a pointer in the opposite direction, thus it is
- * always true that e->Sym->next->Sym->next == e.
- *
- * Each vertex has a pointer to next and previous vertices in the
- * circular list, and a pointer to a half-edge with this vertex as
- * the origin (NULL if this is the dummy header).  There is also a
- * field "data" for client data.
- *
- * Each face has a pointer to the next and previous faces in the
- * circular list, and a pointer to a half-edge with this face as
- * the left face (NULL if this is the dummy header).  There is also
- * a field "data" for client data.
- *
- * Note that what we call a "face" is really a loop; faces may consist
- * of more than one loop (ie. not simply connected), but there is no
- * record of this in the data structure.  The mesh may consist of
- * several disconnected regions, so it may not be possible to visit
- * the entire mesh by starting at a half-edge and traversing the edge
- * structure.
- *
- * The mesh does NOT support isolated vertices; a vertex is deleted along
- * with its last edge.  Similarly when two faces are merged, one of the
- * faces is deleted (see __gl_meshDelete below).  For mesh operations,
- * all face (loop) and vertex pointers must not be NULL.  However, once
- * mesh manipulation is finished, __gl_MeshZapFace can be used to delete
- * faces of the mesh, one at a time.  All external faces can be "zapped"
- * before the mesh is returned to the client; then a NULL face indicates
- * a region which is not part of the output polygon.
- */
-
-struct GLUvertex {
-  GLUvertex	*next;		/* next vertex (never NULL) */
-  GLUvertex	*prev;		/* previous vertex (never NULL) */
-  GLUhalfEdge	*anEdge;	/* a half-edge with this origin */
-  void		*data;		/* client's data */
-
-  /* Internal data (keep hidden) */
-  GLdouble	coords[3];	/* vertex location in 3D */
-  GLdouble	s, t;		/* projection onto the sweep plane */
-  long		pqHandle;	/* to allow deletion from priority queue */
-};
-
-struct GLUface {
-  GLUface	*next;		/* next face (never NULL) */
-  GLUface	*prev;		/* previous face (never NULL) */
-  GLUhalfEdge	*anEdge;	/* a half edge with this left face */
-  void		*data;		/* room for client's data */
-
-  /* Internal data (keep hidden) */
-  GLUface	*trail;		/* "stack" for conversion to strips */
-  GLboolean	marked;		/* flag for conversion to strips */
-  GLboolean	inside;		/* this face is in the polygon interior */
-};
-
-struct GLUhalfEdge {
-  GLUhalfEdge	*next;		/* doubly-linked list (prev==Sym->next) */
-  GLUhalfEdge	*Sym;		/* same edge, opposite direction */
-  GLUhalfEdge	*Onext;		/* next edge CCW around origin */
-  GLUhalfEdge	*Lnext;		/* next edge CCW around left face */
-  GLUvertex	*Org;		/* origin vertex (Overtex too long) */
-  GLUface	*Lface;		/* left face */
-
-  /* Internal data (keep hidden) */
-  ActiveRegion	*activeRegion;	/* a region with this upper edge (sweep.c) */
-  int		winding;	/* change in winding number when crossing
-                                   from the right face to the left face */
-};
-
-#define	Rface	Sym->Lface
-#define Dst	Sym->Org
-
-#define Oprev	Sym->Lnext
-#define Lprev   Onext->Sym
-#define Dprev	Lnext->Sym
-#define Rprev	Sym->Onext
-#define Dnext	Rprev->Sym	/* 3 pointers */
-#define Rnext	Oprev->Sym	/* 3 pointers */
-
-
-struct GLUmesh {
-  GLUvertex	vHead;		/* dummy header for vertex list */
-  GLUface	fHead;		/* dummy header for face list */
-  GLUhalfEdge	eHead;		/* dummy header for edge list */
-  GLUhalfEdge	eHeadSym;	/* and its symmetric counterpart */
-};
-
-/* The mesh operations below have three motivations: completeness,
- * convenience, and efficiency.  The basic mesh operations are MakeEdge,
- * Splice, and Delete.  All the other edge operations can be implemented
- * in terms of these.  The other operations are provided for convenience
- * and/or efficiency.
- *
- * When a face is split or a vertex is added, they are inserted into the
- * global list *before* the existing vertex or face (ie. e->Org or e->Lface).
- * This makes it easier to process all vertices or faces in the global lists
- * without worrying about processing the same data twice.  As a convenience,
- * when a face is split, the "inside" flag is copied from the old face.
- * Other internal data (v->data, v->activeRegion, f->data, f->marked,
- * f->trail, e->winding) is set to zero.
- *
- * ********************** Basic Edge Operations **************************
- *
- * __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop.
- * The loop (face) consists of the two new half-edges.
- *
- * __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the
- * mesh connectivity and topology.  It changes the mesh so that
- *	eOrg->Onext <- OLD( eDst->Onext )
- *	eDst->Onext <- OLD( eOrg->Onext )
- * where OLD(...) means the value before the meshSplice operation.
- *
- * This can have two effects on the vertex structure:
- *  - if eOrg->Org != eDst->Org, the two vertices are merged together
- *  - if eOrg->Org == eDst->Org, the origin is split into two vertices
- * In both cases, eDst->Org is changed and eOrg->Org is untouched.
- *
- * Similarly (and independently) for the face structure,
- *  - if eOrg->Lface == eDst->Lface, one loop is split into two
- *  - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one
- * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected.
- *
- * __gl_meshDelete( eDel ) removes the edge eDel.  There are several cases:
- * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop
- * eDel->Lface is deleted.  Otherwise, we are splitting one loop into two;
- * the newly created loop will contain eDel->Dst.  If the deletion of eDel
- * would create isolated vertices, those are deleted as well.
- *
- * ********************** Other Edge Operations **************************
- *
- * __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that
- * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex.
- * eOrg and eNew will have the same left face.
- *
- * __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew,
- * such that eNew == eOrg->Lnext.  The new vertex is eOrg->Dst == eNew->Org.
- * eOrg and eNew will have the same left face.
- *
- * __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst
- * to eDst->Org, and returns the corresponding half-edge eNew.
- * If eOrg->Lface == eDst->Lface, this splits one loop into two,
- * and the newly created loop is eNew->Lface.  Otherwise, two disjoint
- * loops are merged into one, and the loop eDst->Lface is destroyed.
- *
- * ************************ Other Operations *****************************
- *
- * __gl_meshNewMesh() creates a new mesh with no edges, no vertices,
- * and no loops (what we usually call a "face").
- *
- * __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in
- * both meshes, and returns the new mesh (the old meshes are destroyed).
- *
- * __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh.
- *
- * __gl_meshZapFace( fZap ) destroys a face and removes it from the
- * global face list.  All edges of fZap will have a NULL pointer as their
- * left face.  Any edges which also have a NULL pointer as their right face
- * are deleted entirely (along with any isolated vertices this produces).
- * An entire mesh can be deleted by zapping its faces, one at a time,
- * in any order.  Zapped faces cannot be used in further mesh operations!
- *
- * __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency.
- */
-
-GLUhalfEdge	*__gl_meshMakeEdge( GLUmesh *mesh );
-int		__gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
-int		__gl_meshDelete( GLUhalfEdge *eDel );
-
-GLUhalfEdge	*__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg );
-GLUhalfEdge	*__gl_meshSplitEdge( GLUhalfEdge *eOrg );
-GLUhalfEdge	*__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst );
-
-GLUmesh		*__gl_meshNewMesh( void );
-GLUmesh		*__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 );
-void		__gl_meshDeleteMesh( GLUmesh *mesh );
-void		__gl_meshZapFace( GLUface *fZap );
-
-#ifdef NDEBUG
-#define		__gl_meshCheckMesh( mesh )
-#else
-void		__gl_meshCheckMesh( GLUmesh *mesh );
-#endif
-
-#endif
diff --git a/third_party/glu/libtess/normal.c b/third_party/glu/libtess/normal.c
deleted file mode 100644
index 8fb22f1..0000000
--- a/third_party/glu/libtess/normal.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/normal.c#5 $
-*/
-
-#include "gluos.h"
-#include "mesh.h"
-#include "tess.h"
-#include "normal.h"
-#include <math.h>
-#include <assert.h>
-
-#define TRUE 1
-#define FALSE 0
-
-#define Dot(u,v)	(u[0]*v[0] + u[1]*v[1] + u[2]*v[2])
-
-#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
-static void Normalize( GLdouble v[3] )
-{
-  GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2];
-
-  assert( len > 0 );
-  len = sqrt( len );
-  v[0] /= len;
-  v[1] /= len;
-  v[2] /= len;
-}
-#endif
-
-#define ABS(x)	((x) < 0 ? -(x) : (x))
-
-static int LongAxis( GLdouble v[3] )
-{
-  int i = 0;
-
-  if( ABS(v[1]) > ABS(v[0]) ) { i = 1; }
-  if( ABS(v[2]) > ABS(v[i]) ) { i = 2; }
-  return i;
-}
-
-static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] )
-{
-  GLUvertex *v, *v1, *v2;
-  GLdouble c, tLen2, maxLen2;
-  GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3];
-  GLUvertex *maxVert[3], *minVert[3];
-  GLUvertex *vHead = &tess->mesh->vHead;
-  int i;
-
-  maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD;
-  minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD;
-
-  for( v = vHead->next; v != vHead; v = v->next ) {
-    for( i = 0; i < 3; ++i ) {
-      c = v->coords[i];
-      if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; }
-      if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; }
-    }
-  }
-
-  /* Find two vertices separated by at least 1/sqrt(3) of the maximum
-   * distance between any two vertices
-   */
-  i = 0;
-  if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; }
-  if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; }
-  if( minVal[i] >= maxVal[i] ) {
-    /* All vertices are the same -- normal doesn't matter */
-    norm[0] = 0; norm[1] = 0; norm[2] = 1;
-    return;
-  }
-
-  /* Look for a third vertex which forms the triangle with maximum area
-   * (Length of normal == twice the triangle area)
-   */
-  maxLen2 = 0;
-  v1 = minVert[i];
-  v2 = maxVert[i];
-  d1[0] = v1->coords[0] - v2->coords[0];
-  d1[1] = v1->coords[1] - v2->coords[1];
-  d1[2] = v1->coords[2] - v2->coords[2];
-  for( v = vHead->next; v != vHead; v = v->next ) {
-    d2[0] = v->coords[0] - v2->coords[0];
-    d2[1] = v->coords[1] - v2->coords[1];
-    d2[2] = v->coords[2] - v2->coords[2];
-    tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1];
-    tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2];
-    tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0];
-    tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2];
-    if( tLen2 > maxLen2 ) {
-      maxLen2 = tLen2;
-      norm[0] = tNorm[0];
-      norm[1] = tNorm[1];
-      norm[2] = tNorm[2];
-    }
-  }
-
-  if( maxLen2 <= 0 ) {
-    /* All points lie on a single line -- any decent normal will do */
-    norm[0] = norm[1] = norm[2] = 0;
-    norm[LongAxis(d1)] = 1;
-  }
-}
-  
-
-static void CheckOrientation( GLUtesselator *tess )
-{
-  GLdouble area;
-  GLUface *f, *fHead = &tess->mesh->fHead;
-  GLUvertex *v, *vHead = &tess->mesh->vHead;
-  GLUhalfEdge *e;
-
-  /* When we compute the normal automatically, we choose the orientation
-   * so that the the sum of the signed areas of all contours is non-negative.
-   */
-  area = 0;
-  for( f = fHead->next; f != fHead; f = f->next ) {
-    e = f->anEdge;
-    if( e->winding <= 0 ) continue;
-    do {
-      area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t);
-      e = e->Lnext;
-    } while( e != f->anEdge );
-  }
-  if( area < 0 ) {
-    /* Reverse the orientation by flipping all the t-coordinates */
-    for( v = vHead->next; v != vHead; v = v->next ) {
-      v->t = - v->t;
-    }
-    tess->tUnit[0] = - tess->tUnit[0];
-    tess->tUnit[1] = - tess->tUnit[1];
-    tess->tUnit[2] = - tess->tUnit[2];
-  }
-}
-
-#ifdef FOR_TRITE_TEST_PROGRAM
-#include <stdlib.h>
-extern int RandomSweep;
-#define S_UNIT_X	(RandomSweep ? (2*drand48()-1) : 1.0)
-#define S_UNIT_Y	(RandomSweep ? (2*drand48()-1) : 0.0)
-#else
-#if defined(SLANTED_SWEEP) 
-/* The "feature merging" is not intended to be complete.  There are
- * special cases where edges are nearly parallel to the sweep line
- * which are not implemented.  The algorithm should still behave
- * robustly (ie. produce a reasonable tesselation) in the presence
- * of such edges, however it may miss features which could have been
- * merged.  We could minimize this effect by choosing the sweep line
- * direction to be something unusual (ie. not parallel to one of the
- * coordinate axes).
- */
-#define S_UNIT_X	0.50941539564955385	/* Pre-normalized */
-#define S_UNIT_Y	0.86052074622010633
-#else
-#define S_UNIT_X	1.0
-#define S_UNIT_Y	0.0
-#endif
-#endif
-
-/* Determine the polygon normal and project vertices onto the plane
- * of the polygon.
- */
-void __gl_projectPolygon( GLUtesselator *tess )
-{
-  GLUvertex *v, *vHead = &tess->mesh->vHead;
-  GLdouble norm[3];
-  GLdouble *sUnit, *tUnit;
-  int i, computedNormal = FALSE;
-
-  norm[0] = tess->normal[0];
-  norm[1] = tess->normal[1];
-  norm[2] = tess->normal[2];
-  if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
-    ComputeNormal( tess, norm );
-    computedNormal = TRUE;
-  }
-  sUnit = tess->sUnit;
-  tUnit = tess->tUnit;
-  i = LongAxis( norm );
-
-#if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT)
-  /* Choose the initial sUnit vector to be approximately perpendicular
-   * to the normal.
-   */
-  Normalize( norm );
-
-  sUnit[i] = 0;
-  sUnit[(i+1)%3] = S_UNIT_X;
-  sUnit[(i+2)%3] = S_UNIT_Y;
-
-  /* Now make it exactly perpendicular */
-  w = Dot( sUnit, norm );
-  sUnit[0] -= w * norm[0];
-  sUnit[1] -= w * norm[1];
-  sUnit[2] -= w * norm[2];
-  Normalize( sUnit );
-
-  /* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */
-  tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1];
-  tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2];
-  tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0];
-  Normalize( tUnit );
-#else
-  /* Project perpendicular to a coordinate axis -- better numerically */
-  sUnit[i] = 0;
-  sUnit[(i+1)%3] = S_UNIT_X;
-  sUnit[(i+2)%3] = S_UNIT_Y;
-  
-  tUnit[i] = 0;
-  tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y;
-  tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X;
-#endif
-
-  /* Project the vertices onto the sweep plane */
-  for( v = vHead->next; v != vHead; v = v->next ) {
-    v->s = Dot( v->coords, sUnit );
-    v->t = Dot( v->coords, tUnit );
-  }
-  if( computedNormal ) {
-    CheckOrientation( tess );
-  }
-}
diff --git a/third_party/glu/libtess/normal.h b/third_party/glu/libtess/normal.h
deleted file mode 100644
index 5a68747..0000000
--- a/third_party/glu/libtess/normal.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/normal.h#5 $
-*/
-
-#ifndef __normal_h_
-#define __normal_h_
-
-#include "tess.h"
-
-/* __gl_projectPolygon( tess ) determines the polygon normal
- * and project vertices onto the plane of the polygon.
- */
-void __gl_projectPolygon( GLUtesselator *tess );
-
-#endif
diff --git a/third_party/glu/libtess/priorityq-heap.c b/third_party/glu/libtess/priorityq-heap.c
deleted file mode 100644
index 163bde6..0000000
--- a/third_party/glu/libtess/priorityq-heap.c
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/priorityq-heap.c#5 $
-*/
-
-#include <stddef.h>
-#include <assert.h>
-#include <limits.h>
-#include "priorityq-heap.h"
-#include "memalloc.h"
-
-#define INIT_SIZE	32
-
-#define TRUE 1
-#define FALSE 0
-
-#ifdef FOR_TRITE_TEST_PROGRAM
-#define LEQ(x,y)	(*pq->leq)(x,y)
-#else
-/* Violates modularity, but a little faster */
-#include "geom.h"
-#define LEQ(x,y)	VertLeq((GLUvertex *)x, (GLUvertex *)y)
-#endif
-
-/* really __gl_pqHeapNewPriorityQ */
-PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
-{
-  PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
-  if (pq == NULL) return NULL;
-
-  pq->size = 0;
-  pq->max = INIT_SIZE;
-  pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) );
-  if (pq->nodes == NULL) {
-     memFree(pq);
-     return NULL;
-  }
-
-  pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) );
-  if (pq->handles == NULL) {
-     memFree(pq->nodes);
-     memFree(pq);
-     return NULL;
-  }
-
-  pq->initialized = FALSE;
-  pq->freeList = 0;
-  pq->leq = leq;
-
-  pq->nodes[1].handle = 1;	/* so that Minimum() returns NULL */
-  pq->handles[1].key = NULL;
-  return pq;
-}
-
-/* really __gl_pqHeapDeletePriorityQ */
-void pqDeletePriorityQ( PriorityQ *pq )
-{
-  memFree( pq->handles );
-  memFree( pq->nodes );
-  memFree( pq );
-}
-
-
-static void FloatDown( PriorityQ *pq, long curr )
-{
-  PQnode *n = pq->nodes;
-  PQhandleElem *h = pq->handles;
-  PQhandle hCurr, hChild;
-  long child;
-
-  hCurr = n[curr].handle;
-  for( ;; ) {
-    child = curr << 1;
-    if( child < pq->size && LEQ( h[n[child+1].handle].key,
-				 h[n[child].handle].key )) {
-      ++child;
-    }
-
-    assert(child <= pq->max);
-
-    hChild = n[child].handle;
-    if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) {
-      n[curr].handle = hCurr;
-      h[hCurr].node = curr;
-      break;
-    }
-    n[curr].handle = hChild;
-    h[hChild].node = curr;
-    curr = child;
-  }
-}
-
-
-static void FloatUp( PriorityQ *pq, long curr )
-{
-  PQnode *n = pq->nodes;
-  PQhandleElem *h = pq->handles;
-  PQhandle hCurr, hParent;
-  long parent;
-
-  hCurr = n[curr].handle;
-  for( ;; ) {
-    parent = curr >> 1;
-    hParent = n[parent].handle;
-    if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) {
-      n[curr].handle = hCurr;
-      h[hCurr].node = curr;
-      break;
-    }
-    n[curr].handle = hParent;
-    h[hParent].node = curr;
-    curr = parent;
-  }
-}
-
-/* really __gl_pqHeapInit */
-void pqInit( PriorityQ *pq )
-{
-  long i;
-
-  /* This method of building a heap is O(n), rather than O(n lg n). */
-
-  for( i = pq->size; i >= 1; --i ) {
-    FloatDown( pq, i );
-  }
-  pq->initialized = TRUE;
-}
-
-/* really __gl_pqHeapInsert */
-/* returns LONG_MAX iff out of memory */
-PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
-{
-  long curr;
-  PQhandle free;
-
-  curr = ++ pq->size;
-  if( (curr*2) > pq->max ) {
-    PQnode *saveNodes= pq->nodes;
-    PQhandleElem *saveHandles= pq->handles;
-
-    /* If the heap overflows, double its size. */
-    pq->max <<= 1;
-    pq->nodes = (PQnode *)memRealloc( pq->nodes, 
-				     (size_t) 
-				     ((pq->max + 1) * sizeof( pq->nodes[0] )));
-    if (pq->nodes == NULL) {
-       pq->nodes = saveNodes;	/* restore ptr to free upon return */
-       return LONG_MAX;
-    }
-    pq->handles = (PQhandleElem *)memRealloc( pq->handles,
-			                     (size_t)
-			                      ((pq->max + 1) * 
-					       sizeof( pq->handles[0] )));
-    if (pq->handles == NULL) {
-       pq->handles = saveHandles; /* restore ptr to free upon return */
-       return LONG_MAX;
-    }
-  }
-
-  if( pq->freeList == 0 ) {
-    free = curr;
-  } else {
-    free = pq->freeList;
-    pq->freeList = pq->handles[free].node;
-  }
-
-  pq->nodes[curr].handle = free;
-  pq->handles[free].node = curr;
-  pq->handles[free].key = keyNew;
-
-  if( pq->initialized ) {
-    FloatUp( pq, curr );
-  }
-  assert(free != LONG_MAX);
-  return free;
-}
-
-/* really __gl_pqHeapExtractMin */
-PQkey pqExtractMin( PriorityQ *pq )
-{
-  PQnode *n = pq->nodes;
-  PQhandleElem *h = pq->handles;
-  PQhandle hMin = n[1].handle;
-  PQkey min = h[hMin].key;
-
-  if( pq->size > 0 ) {
-    n[1].handle = n[pq->size].handle;
-    h[n[1].handle].node = 1;
-
-    h[hMin].key = NULL;
-    h[hMin].node = pq->freeList;
-    pq->freeList = hMin;
-
-    if( -- pq->size > 0 ) {
-      FloatDown( pq, 1 );
-    }
-  }
-  return min;
-}
-
-/* really __gl_pqHeapDelete */
-void pqDelete( PriorityQ *pq, PQhandle hCurr )
-{
-  PQnode *n = pq->nodes;
-  PQhandleElem *h = pq->handles;
-  long curr;
-
-  assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL );
-
-  curr = h[hCurr].node;
-  n[curr].handle = n[pq->size].handle;
-  h[n[curr].handle].node = curr;
-
-  if( curr <= -- pq->size ) {
-    if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) {
-      FloatDown( pq, curr );
-    } else {
-      FloatUp( pq, curr );
-    }
-  }
-  h[hCurr].key = NULL;
-  h[hCurr].node = pq->freeList;
-  pq->freeList = hCurr;
-}
diff --git a/third_party/glu/libtess/priorityq-heap.h b/third_party/glu/libtess/priorityq-heap.h
deleted file mode 100644
index 02e4434..0000000
--- a/third_party/glu/libtess/priorityq-heap.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/priorityq-heap.h#5 $
-*/
-
-#ifndef __priorityq_heap_h_
-#define __priorityq_heap_h_
-
-/* Use #define's so that another heap implementation can use this one */
-
-#define PQkey			PQHeapKey
-#define PQhandle		PQHeapHandle
-#define PriorityQ		PriorityQHeap
-
-#define pqNewPriorityQ(leq)	__gl_pqHeapNewPriorityQ(leq)
-#define pqDeletePriorityQ(pq)	__gl_pqHeapDeletePriorityQ(pq)
-
-/* The basic operations are insertion of a new key (pqInsert),
- * and examination/extraction of a key whose value is minimum
- * (pqMinimum/pqExtractMin).  Deletion is also allowed (pqDelete);
- * for this purpose pqInsert returns a "handle" which is supplied
- * as the argument.
- *
- * An initial heap may be created efficiently by calling pqInsert
- * repeatedly, then calling pqInit.  In any case pqInit must be called
- * before any operations other than pqInsert are used.
- *
- * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
- * This may also be tested with pqIsEmpty.
- */
-#define pqInit(pq)		__gl_pqHeapInit(pq)
-#define pqInsert(pq,key)	__gl_pqHeapInsert(pq,key)
-#define pqMinimum(pq)		__gl_pqHeapMinimum(pq)
-#define pqExtractMin(pq)	__gl_pqHeapExtractMin(pq)
-#define pqDelete(pq,handle)	__gl_pqHeapDelete(pq,handle)
-#define pqIsEmpty(pq)		__gl_pqHeapIsEmpty(pq)
-
-
-/* Since we support deletion the data structure is a little more
- * complicated than an ordinary heap.  "nodes" is the heap itself;
- * active nodes are stored in the range 1..pq->size.  When the
- * heap exceeds its allocated size (pq->max), its size doubles.
- * The children of node i are nodes 2i and 2i+1.
- *
- * Each node stores an index into an array "handles".  Each handle
- * stores a key, plus a pointer back to the node which currently
- * represents that key (ie. nodes[handles[i].node].handle == i).
- */
-
-typedef void *PQkey;
-typedef long PQhandle;
-typedef struct PriorityQ PriorityQ;
-
-typedef struct { PQhandle handle; } PQnode;
-typedef struct { PQkey key; PQhandle node; } PQhandleElem;
-
-struct PriorityQ {
-  PQnode	*nodes;
-  PQhandleElem	*handles;
-  long		size, max;
-  PQhandle	freeList;
-  int		initialized;
-  int		(*leq)(PQkey key1, PQkey key2);
-};
-  
-PriorityQ	*pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
-void		pqDeletePriorityQ( PriorityQ *pq );
-
-void		pqInit( PriorityQ *pq );
-PQhandle	pqInsert( PriorityQ *pq, PQkey key );
-PQkey		pqExtractMin( PriorityQ *pq );
-void		pqDelete( PriorityQ *pq, PQhandle handle );
-
-
-#define __gl_pqHeapMinimum(pq)	((pq)->handles[(pq)->nodes[1].handle].key)
-#define __gl_pqHeapIsEmpty(pq)	((pq)->size == 0)
-
-#endif
diff --git a/third_party/glu/libtess/priorityq-sort.h b/third_party/glu/libtess/priorityq-sort.h
deleted file mode 100644
index 12c2f0d..0000000
--- a/third_party/glu/libtess/priorityq-sort.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/priorityq-sort.h#5 $
-*/
-
-#ifndef __priorityq_sort_h_
-#define __priorityq_sort_h_
-
-#include "priorityq-heap.h"
-
-#undef PQkey
-#undef PQhandle
-#undef PriorityQ
-#undef pqNewPriorityQ
-#undef pqDeletePriorityQ
-#undef pqInit
-#undef pqInsert
-#undef pqMinimum
-#undef pqExtractMin
-#undef pqDelete
-#undef pqIsEmpty
-
-/* Use #define's so that another heap implementation can use this one */
-
-#define PQkey			PQSortKey
-#define PQhandle		PQSortHandle
-#define PriorityQ		PriorityQSort
-
-#define pqNewPriorityQ(leq)	__gl_pqSortNewPriorityQ(leq)
-#define pqDeletePriorityQ(pq)	__gl_pqSortDeletePriorityQ(pq)
-
-/* The basic operations are insertion of a new key (pqInsert),
- * and examination/extraction of a key whose value is minimum
- * (pqMinimum/pqExtractMin).  Deletion is also allowed (pqDelete);
- * for this purpose pqInsert returns a "handle" which is supplied
- * as the argument.
- *
- * An initial heap may be created efficiently by calling pqInsert
- * repeatedly, then calling pqInit.  In any case pqInit must be called
- * before any operations other than pqInsert are used.
- *
- * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
- * This may also be tested with pqIsEmpty.
- */
-#define pqInit(pq)		__gl_pqSortInit(pq)
-#define pqInsert(pq,key)	__gl_pqSortInsert(pq,key)
-#define pqMinimum(pq)		__gl_pqSortMinimum(pq)
-#define pqExtractMin(pq)	__gl_pqSortExtractMin(pq)
-#define pqDelete(pq,handle)	__gl_pqSortDelete(pq,handle)
-#define pqIsEmpty(pq)		__gl_pqSortIsEmpty(pq)
-
-
-/* Since we support deletion the data structure is a little more
- * complicated than an ordinary heap.  "nodes" is the heap itself;
- * active nodes are stored in the range 1..pq->size.  When the
- * heap exceeds its allocated size (pq->max), its size doubles.
- * The children of node i are nodes 2i and 2i+1.
- *
- * Each node stores an index into an array "handles".  Each handle
- * stores a key, plus a pointer back to the node which currently
- * represents that key (ie. nodes[handles[i].node].handle == i).
- */
-
-typedef PQHeapKey PQkey;
-typedef PQHeapHandle PQhandle;
-typedef struct PriorityQ PriorityQ;
-
-struct PriorityQ {
-  PriorityQHeap	*heap;
-  PQkey		*keys;
-  PQkey		**order;
-  PQhandle	size, max;
-  int		initialized;
-  int		(*leq)(PQkey key1, PQkey key2);
-};
-  
-PriorityQ	*pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
-void		pqDeletePriorityQ( PriorityQ *pq );
-
-int		pqInit( PriorityQ *pq );
-PQhandle	pqInsert( PriorityQ *pq, PQkey key );
-PQkey		pqExtractMin( PriorityQ *pq );
-void		pqDelete( PriorityQ *pq, PQhandle handle );
-
-PQkey		pqMinimum( PriorityQ *pq );
-int		pqIsEmpty( PriorityQ *pq );
-
-#endif
diff --git a/third_party/glu/libtess/priorityq.c b/third_party/glu/libtess/priorityq.c
deleted file mode 100644
index 4b9e1ae..0000000
--- a/third_party/glu/libtess/priorityq.c
+++ /dev/null
@@ -1,267 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/priorityq.c#5 $
-*/
-
-#include "gluos.h"
-#include <stddef.h>
-#include <assert.h>
-#include <limits.h>		/* LONG_MAX */
-#include "memalloc.h"
-
-/* Include all the code for the regular heap-based queue here. */
-
-#include "priorityq-heap.c"
-
-/* Now redefine all the function names to map to their "Sort" versions. */
-
-#include "priorityq-sort.h"
-
-/* really __gl_pqSortNewPriorityQ */
-PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) )
-{
-  PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ ));
-  if (pq == NULL) return NULL;
-
-  pq->heap = __gl_pqHeapNewPriorityQ( leq );
-  if (pq->heap == NULL) {
-     memFree(pq);
-     return NULL;
-  }
-
-  pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) );
-  if (pq->keys == NULL) {
-     __gl_pqHeapDeletePriorityQ(pq->heap);
-     memFree(pq);
-     return NULL;
-  }
-
-  pq->size = 0;
-  pq->max = INIT_SIZE;
-  pq->initialized = FALSE;
-  pq->leq = leq;
-  return pq;
-}
-
-/* really __gl_pqSortDeletePriorityQ */
-void pqDeletePriorityQ( PriorityQ *pq )
-{
-  assert(pq != NULL); 
-  if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap );
-  if (pq->order != NULL) memFree( pq->order );
-  if (pq->keys != NULL) memFree( pq->keys );
-  memFree( pq );
-}
-
-
-#define LT(x,y)		(! LEQ(y,x))
-#define GT(x,y)		(! LEQ(x,y))
-#define Swap(a,b)	do{PQkey *tmp = *a; *a = *b; *b = tmp;}while(0)
-
-/* really __gl_pqSortInit */
-int pqInit( PriorityQ *pq )
-{
-  PQkey **p, **r, **i, **j, *piv;
-  struct { PQkey **p, **r; } Stack[50], *top = Stack;
-  unsigned long seed = 2016473283;
-
-  /* Create an array of indirect pointers to the keys, so that we
-   * the handles we have returned are still valid.
-   */
-/*
-  pq->order = (PQHeapKey **)memAlloc( (size_t)
-                                  (pq->size * sizeof(pq->order[0])) );
-*/
-  pq->order = (PQHeapKey **)memAlloc( (size_t)
-                                  ((pq->size+1) * sizeof(pq->order[0])) );
-/* the previous line is a patch to compensate for the fact that IBM */
-/* machines return a null on a malloc of zero bytes (unlike SGI),   */
-/* so we have to put in this defense to guard against a memory      */
-/* fault four lines down. from fossum@austin.ibm.com.               */
-  if (pq->order == NULL) return 0;
-
-  p = pq->order;
-  r = p + pq->size - 1;
-  for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) {
-    *i = piv;
-  }
-
-  /* Sort the indirect pointers in descending order,
-   * using randomized Quicksort
-   */
-  top->p = p; top->r = r; ++top;
-  while( --top >= Stack ) {
-    p = top->p;
-    r = top->r;
-    while( r > p + 10 ) {
-      seed = seed * 1539415821 + 1;
-      i = p + seed % (r - p + 1);
-      piv = *i;
-      *i = *p;
-      *p = piv;
-      i = p - 1;
-      j = r + 1;
-      do {
-	do { ++i; } while( GT( **i, *piv ));
-	do { --j; } while( LT( **j, *piv ));
-	Swap( i, j );
-      } while( i < j );
-      Swap( i, j );	/* Undo last swap */
-      if( i - p < r - j ) {
-	top->p = j+1; top->r = r; ++top;
-	r = i-1;
-      } else {
-	top->p = p; top->r = i-1; ++top;
-	p = j+1;
-      }
-    }
-    /* Insertion sort small lists */
-    for( i = p+1; i <= r; ++i ) {
-      piv = *i;
-      for( j = i; j > p && LT( **(j-1), *piv ); --j ) {
-	*j = *(j-1);
-      }
-      *j = piv;
-    }
-  }
-  pq->max = pq->size;
-  pq->initialized = TRUE;
-  __gl_pqHeapInit( pq->heap );	/* always succeeds */
-
-#ifndef NDEBUG
-  p = pq->order;
-  r = p + pq->size - 1;
-  for( i = p; i < r; ++i ) {
-    assert( LEQ( **(i+1), **i ));
-  }
-#endif
-
-  return 1;
-}
-
-/* really __gl_pqSortInsert */
-/* returns LONG_MAX iff out of memory */ 
-PQhandle pqInsert( PriorityQ *pq, PQkey keyNew )
-{
-  long curr;
-
-  if( pq->initialized ) {
-    return __gl_pqHeapInsert( pq->heap, keyNew );
-  }
-  curr = pq->size;
-  if( ++ pq->size >= pq->max ) {
-    PQkey *saveKey= pq->keys;
-
-    /* If the heap overflows, double its size. */
-    pq->max <<= 1;
-    pq->keys = (PQHeapKey *)memRealloc( pq->keys, 
-	 	                        (size_t)
-	                                 (pq->max * sizeof( pq->keys[0] )));
-    if (pq->keys == NULL) {	
-       pq->keys = saveKey;	/* restore ptr to free upon return */
-       return LONG_MAX;
-    }
-  }
-  assert(curr != LONG_MAX);	
-  pq->keys[curr] = keyNew;
-
-  /* Negative handles index the sorted array. */
-  return -(curr+1);
-}
-
-/* really __gl_pqSortExtractMin */
-PQkey pqExtractMin( PriorityQ *pq )
-{
-  PQkey sortMin, heapMin;
-
-  if( pq->size == 0 ) {
-    return __gl_pqHeapExtractMin( pq->heap );
-  }
-  sortMin = *(pq->order[pq->size-1]);
-  if( ! __gl_pqHeapIsEmpty( pq->heap )) {
-    heapMin = __gl_pqHeapMinimum( pq->heap );
-    if( LEQ( heapMin, sortMin )) {
-      return __gl_pqHeapExtractMin( pq->heap );
-    }
-  }
-  do {
-    -- pq->size;
-  } while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL );
-  return sortMin;
-}
-
-/* really __gl_pqSortMinimum */
-PQkey pqMinimum( PriorityQ *pq )
-{
-  PQkey sortMin, heapMin;
-
-  if( pq->size == 0 ) {
-    return __gl_pqHeapMinimum( pq->heap );
-  }
-  sortMin = *(pq->order[pq->size-1]);
-  if( ! __gl_pqHeapIsEmpty( pq->heap )) {
-    heapMin = __gl_pqHeapMinimum( pq->heap );
-    if( LEQ( heapMin, sortMin )) {
-      return heapMin;
-    }
-  }
-  return sortMin;
-}
-
-/* really __gl_pqSortIsEmpty */
-int pqIsEmpty( PriorityQ *pq )
-{
-  return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap );
-}
-
-/* really __gl_pqSortDelete */
-void pqDelete( PriorityQ *pq, PQhandle curr )
-{
-  if( curr >= 0 ) {
-    __gl_pqHeapDelete( pq->heap, curr );
-    return;
-  }
-  curr = -(curr+1);
-  assert( curr < pq->max && pq->keys[curr] != NULL );
-
-  pq->keys[curr] = NULL;
-  while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) {
-    -- pq->size;
-  }
-}
diff --git a/third_party/glu/libtess/priorityq.h b/third_party/glu/libtess/priorityq.h
deleted file mode 100644
index 3ae6b0c..0000000
--- a/third_party/glu/libtess/priorityq.h
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/priorityq.h#5 $
-*/
-
-#ifndef __priorityq_sort_h_
-#define __priorityq_sort_h_
-
-#include "priorityq-heap.h"
-
-#undef PQkey
-#undef PQhandle
-#undef PriorityQ
-#undef pqNewPriorityQ
-#undef pqDeletePriorityQ
-#undef pqInit
-#undef pqInsert
-#undef pqMinimum
-#undef pqExtractMin
-#undef pqDelete
-#undef pqIsEmpty
-
-/* Use #define's so that another heap implementation can use this one */
-
-#define PQkey			PQSortKey
-#define PQhandle		PQSortHandle
-#define PriorityQ		PriorityQSort
-
-#define pqNewPriorityQ(leq)	__gl_pqSortNewPriorityQ(leq)
-#define pqDeletePriorityQ(pq)	__gl_pqSortDeletePriorityQ(pq)
-
-/* The basic operations are insertion of a new key (pqInsert),
- * and examination/extraction of a key whose value is minimum
- * (pqMinimum/pqExtractMin).  Deletion is also allowed (pqDelete);
- * for this purpose pqInsert returns a "handle" which is supplied
- * as the argument.
- *
- * An initial heap may be created efficiently by calling pqInsert
- * repeatedly, then calling pqInit.  In any case pqInit must be called
- * before any operations other than pqInsert are used.
- *
- * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key.
- * This may also be tested with pqIsEmpty.
- */
-#define pqInit(pq)		__gl_pqSortInit(pq)
-#define pqInsert(pq,key)	__gl_pqSortInsert(pq,key)
-#define pqMinimum(pq)		__gl_pqSortMinimum(pq)
-#define pqExtractMin(pq)	__gl_pqSortExtractMin(pq)
-#define pqDelete(pq,handle)	__gl_pqSortDelete(pq,handle)
-#define pqIsEmpty(pq)		__gl_pqSortIsEmpty(pq)
-
-
-/* Since we support deletion the data structure is a little more
- * complicated than an ordinary heap.  "nodes" is the heap itself;
- * active nodes are stored in the range 1..pq->size.  When the
- * heap exceeds its allocated size (pq->max), its size doubles.
- * The children of node i are nodes 2i and 2i+1.
- *
- * Each node stores an index into an array "handles".  Each handle
- * stores a key, plus a pointer back to the node which currently
- * represents that key (ie. nodes[handles[i].node].handle == i).
- */
-
-typedef PQHeapKey PQkey;
-typedef PQHeapHandle PQhandle;
-typedef struct PriorityQ PriorityQ;
-
-struct PriorityQ {
-  PriorityQHeap	*heap;
-  PQkey		*keys;
-  PQkey		**order;
-  PQhandle	size, max;
-  int		initialized;
-  int		(*leq)(PQkey key1, PQkey key2);
-};
-  
-PriorityQ	*pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) );
-void		pqDeletePriorityQ( PriorityQ *pq );
-
-int		pqInit( PriorityQ *pq );
-PQhandle	pqInsert( PriorityQ *pq, PQkey key );
-PQkey		pqExtractMin( PriorityQ *pq );
-void		pqDelete( PriorityQ *pq, PQhandle handle );
-
-PQkey		pqMinimum( PriorityQ *pq );
-int		pqIsEmpty( PriorityQ *pq );
-
-#endif
diff --git a/third_party/glu/libtess/render.c b/third_party/glu/libtess/render.c
deleted file mode 100644
index aff1298..0000000
--- a/third_party/glu/libtess/render.c
+++ /dev/null
@@ -1,505 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/render.c#5 $
-*/
-
-#include "gluos.h"
-#include <assert.h>
-#include <stddef.h>
-#include "mesh.h"
-#include "tess.h"
-#include "render.h"
-
-#define TRUE 1
-#define FALSE 0
-
-/* This structure remembers the information we need about a primitive
- * to be able to render it later, once we have determined which
- * primitive is able to use the most triangles.
- */
-struct FaceCount {
-  long		size;		/* number of triangles used */
-  GLUhalfEdge	*eStart;	/* edge where this primitive starts */
-  void		(*render)(GLUtesselator *, GLUhalfEdge *, long);
-                                /* routine to render this primitive */
-};
-
-static struct FaceCount MaximumFan( GLUhalfEdge *eOrig );
-static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig );
-
-static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
-static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size );
-static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart,
-			    long size );
-
-static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig );
-static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head );
-
-
-
-/************************ Strips and Fans decomposition ******************/
-
-/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
- * fans, strips, and separate triangles.  A substantial effort is made
- * to use as few rendering primitives as possible (ie. to make the fans
- * and strips as large as possible).
- *
- * The rendering output is provided as callbacks (see the api).
- */
-void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh )
-{
-  GLUface *f;
-
-  /* Make a list of separate triangles so we can render them all at once */
-  tess->lonelyTriList = NULL;
-
-  for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
-    f->marked = FALSE;
-  }
-  for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
-
-    /* We examine all faces in an arbitrary order.  Whenever we find
-     * an unprocessed face F, we output a group of faces including F
-     * whose size is maximum.
-     */
-    if( f->inside && ! f->marked ) {
-      RenderMaximumFaceGroup( tess, f );
-      assert( f->marked );
-    }
-  }
-  if( tess->lonelyTriList != NULL ) {
-    RenderLonelyTriangles( tess, tess->lonelyTriList );
-    tess->lonelyTriList = NULL;
-  }
-}
-
-
-static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig )
-{
-  /* We want to find the largest triangle fan or strip of unmarked faces
-   * which includes the given face fOrig.  There are 3 possible fans
-   * passing through fOrig (one centered at each vertex), and 3 possible
-   * strips (one for each CCW permutation of the vertices).  Our strategy
-   * is to try all of these, and take the primitive which uses the most
-   * triangles (a greedy approach).
-   */
-  GLUhalfEdge *e = fOrig->anEdge;
-  struct FaceCount max, newFace;
-
-  max.size = 1;
-  max.eStart = e;
-  max.render = &RenderTriangle;
-
-  if( ! tess->flagBoundary ) {
-    newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; }
-    newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
-    newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
-
-    newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; }
-    newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; }
-    newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; }
-  }
-  (*(max.render))( tess, max.eStart, max.size );
-}
-
-
-/* Macros which keep track of faces we have marked temporarily, and allow
- * us to backtrack when necessary.  With triangle fans, this is not
- * really necessary, since the only awkward case is a loop of triangles
- * around a single origin vertex.  However with strips the situation is
- * more complicated, and we need a general tracking method like the
- * one here.
- */
-#define Marked(f)	(! (f)->inside || (f)->marked)
-
-#define AddToTrail(f,t)	((f)->trail = (t), (t) = (f), (f)->marked = TRUE)
-
-#define FreeTrail(t)	do { \
-			  while( (t) != NULL ) { \
-			    (t)->marked = FALSE; t = (t)->trail; \
-			  } \
-			} while(0) /* absorb trailing semicolon */
-
-
-
-static struct FaceCount MaximumFan( GLUhalfEdge *eOrig )
-{
-  /* eOrig->Lface is the face we want to render.  We want to find the size
-   * of a maximal fan around eOrig->Org.  To do this we just walk around
-   * the origin vertex as far as possible in both directions.
-   */
-  struct FaceCount newFace = { 0, NULL, &RenderFan };
-  GLUface *trail = NULL;
-  GLUhalfEdge *e;
-
-  for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) {
-    AddToTrail( e->Lface, trail );
-    ++newFace.size;
-  }
-  for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) {
-    AddToTrail( e->Rface, trail );
-    ++newFace.size;
-  }
-  newFace.eStart = e;
-  /*LINTED*/
-  FreeTrail( trail );
-  return newFace;
-}
-
-
-#define IsEven(n)	(((n) & 1) == 0)
-
-static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig )
-{
-  /* Here we are looking for a maximal strip that contains the vertices
-   * eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the
-   * reverse, such that all triangles are oriented CCW).
-   *
-   * Again we walk forward and backward as far as possible.  However for
-   * strips there is a twist: to get CCW orientations, there must be
-   * an *even* number of triangles in the strip on one side of eOrig.
-   * We walk the strip starting on a side with an even number of triangles;
-   * if both side have an odd number, we are forced to shorten one side.
-   */
-  struct FaceCount newFace = { 0, NULL, &RenderStrip };
-  long headSize = 0, tailSize = 0;
-  GLUface *trail = NULL;
-  GLUhalfEdge *e, *eTail, *eHead;
-
-  for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) {
-    AddToTrail( e->Lface, trail );
-    ++tailSize;
-    e = e->Dprev;
-    if( Marked( e->Lface )) break;
-    AddToTrail( e->Lface, trail );
-  }
-  eTail = e;
-
-  for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) {
-    AddToTrail( e->Rface, trail );
-    ++headSize;
-    e = e->Oprev;
-    if( Marked( e->Rface )) break;
-    AddToTrail( e->Rface, trail );
-  }
-  eHead = e;
-
-  newFace.size = tailSize + headSize;
-  if( IsEven( tailSize )) {
-    newFace.eStart = eTail->Sym;
-  } else if( IsEven( headSize )) {
-    newFace.eStart = eHead;
-  } else {
-    /* Both sides have odd length, we must shorten one of them.  In fact,
-     * we must start from eHead to guarantee inclusion of eOrig->Lface.
-     */
-    --newFace.size;
-    newFace.eStart = eHead->Onext;
-  }
-  /*LINTED*/
-  FreeTrail( trail );
-  return newFace;
-}
-
-
-static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size )
-{
-  /* Just add the triangle to a triangle list, so we can render all
-   * the separate triangles at once.
-   */
-  assert( size == 1 );
-  AddToTrail( e->Lface, tess->lonelyTriList );
-}
-
-
-static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f )
-{
-  /* Now we render all the separate triangles which could not be
-   * grouped into a triangle fan or strip.
-   */
-  GLUhalfEdge *e;
-  int newState;
-  int edgeState = -1;	/* force edge state output for first vertex */
-
-  CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES );
-
-  for( ; f != NULL; f = f->trail ) {
-    /* Loop once for each edge (there will always be 3 edges) */
-
-    e = f->anEdge;
-    do {
-      if( tess->flagBoundary ) {
-	/* Set the "edge state" to TRUE just before we output the
-	 * first vertex of each edge on the polygon boundary.
-	 */
-	newState = ! e->Rface->inside;
-	if( edgeState != newState ) {
-	  edgeState = newState;
-          CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState );
-	}
-      }
-      CALL_VERTEX_OR_VERTEX_DATA( e->Org->data );
-
-      e = e->Lnext;
-    } while( e != f->anEdge );
-  }
-  CALL_END_OR_END_DATA();
-}
-
-
-static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size )
-{
-  /* Render as many CCW triangles as possible in a fan starting from
-   * edge "e".  The fan *should* contain exactly "size" triangles
-   * (otherwise we've goofed up somewhere).
-   */
-  CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN ); 
-  CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
-  CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
-
-  while( ! Marked( e->Lface )) {
-    e->Lface->marked = TRUE;
-    --size;
-    e = e->Onext;
-    CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
-  }
-
-  assert( size == 0 );
-  CALL_END_OR_END_DATA();
-}
-
-
-static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size )
-{
-  /* Render as many CCW triangles as possible in a strip starting from
-   * edge "e".  The strip *should* contain exactly "size" triangles
-   * (otherwise we've goofed up somewhere).
-   */
-  CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP );
-  CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
-  CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
-
-  while( ! Marked( e->Lface )) {
-    e->Lface->marked = TRUE;
-    --size;
-    e = e->Dprev;
-    CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
-    if( Marked( e->Lface )) break;
-
-    e->Lface->marked = TRUE;
-    --size;
-    e = e->Onext;
-    CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); 
-  }
-
-  assert( size == 0 );
-  CALL_END_OR_END_DATA();
-}
-
-
-/************************ Boundary contour decomposition ******************/
-
-/* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one
- * contour for each face marked "inside".  The rendering output is
- * provided as callbacks (see the api).
- */
-void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh )
-{
-  GLUface *f;
-  GLUhalfEdge *e;
-
-  for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) {
-    if( f->inside ) {
-      CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP );
-      e = f->anEdge;
-      do {
-        CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); 
-	e = e->Lnext;
-      } while( e != f->anEdge );
-      CALL_END_OR_END_DATA();
-    }
-  }
-}
-
-
-/************************ Quick-and-dirty decomposition ******************/
-
-#define SIGN_INCONSISTENT 2
-
-static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check )
-/*
- * If check==FALSE, we compute the polygon normal and place it in norm[].
- * If check==TRUE, we check that each triangle in the fan from v0 has a
- * consistent orientation with respect to norm[].  If triangles are
- * consistently oriented CCW, return 1; if CW, return -1; if all triangles
- * are degenerate return 0; otherwise (no consistent orientation) return
- * SIGN_INCONSISTENT.
- */
-{
-  CachedVertex *v0 = tess->cache;
-  CachedVertex *vn = v0 + tess->cacheCount;
-  CachedVertex *vc;
-  GLdouble dot, xc, yc, zc, xp, yp, zp, n[3];
-  int sign = 0;
-
-  /* Find the polygon normal.  It is important to get a reasonable
-   * normal even when the polygon is self-intersecting (eg. a bowtie).
-   * Otherwise, the computed normal could be very tiny, but perpendicular
-   * to the true plane of the polygon due to numerical noise.  Then all
-   * the triangles would appear to be degenerate and we would incorrectly
-   * decompose the polygon as a fan (or simply not render it at all).
-   *
-   * We use a sum-of-triangles normal algorithm rather than the more
-   * efficient sum-of-trapezoids method (used in CheckOrientation()
-   * in normal.c).  This lets us explicitly reverse the signed area
-   * of some triangles to get a reasonable normal in the self-intersecting
-   * case.
-   */
-  if( ! check ) {
-    norm[0] = norm[1] = norm[2] = 0.0;
-  }
-
-  vc = v0 + 1;
-  xc = vc->coords[0] - v0->coords[0];
-  yc = vc->coords[1] - v0->coords[1];
-  zc = vc->coords[2] - v0->coords[2];
-  while( ++vc < vn ) {
-    xp = xc; yp = yc; zp = zc;
-    xc = vc->coords[0] - v0->coords[0];
-    yc = vc->coords[1] - v0->coords[1];
-    zc = vc->coords[2] - v0->coords[2];
-
-    /* Compute (vp - v0) cross (vc - v0) */
-    n[0] = yp*zc - zp*yc;
-    n[1] = zp*xc - xp*zc;
-    n[2] = xp*yc - yp*xc;
-
-    dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2];
-    if( ! check ) {
-      /* Reverse the contribution of back-facing triangles to get
-       * a reasonable normal for self-intersecting polygons (see above)
-       */
-      if( dot >= 0 ) {
-	norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2];
-      } else {
-	norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2];
-      }
-    } else if( dot != 0 ) {
-      /* Check the new orientation for consistency with previous triangles */
-      if( dot > 0 ) {
-	if( sign < 0 ) return SIGN_INCONSISTENT;
-	sign = 1;
-      } else {
-	if( sign > 0 ) return SIGN_INCONSISTENT;
-	sign = -1;
-      }
-    }
-  }
-  return sign;
-}
-
-/* __gl_renderCache( tess ) takes a single contour and tries to render it
- * as a triangle fan.  This handles convex polygons, as well as some
- * non-convex polygons if we get lucky.
- *
- * Returns TRUE if the polygon was successfully rendered.  The rendering
- * output is provided as callbacks (see the api).
- */
-GLboolean __gl_renderCache( GLUtesselator *tess )
-{
-  CachedVertex *v0 = tess->cache;
-  CachedVertex *vn = v0 + tess->cacheCount;
-  CachedVertex *vc;
-  GLdouble norm[3];
-  int sign;
-
-  if( tess->cacheCount < 3 ) {
-    /* Degenerate contour -- no output */
-    return TRUE;
-  }
-
-  norm[0] = tess->normal[0];
-  norm[1] = tess->normal[1];
-  norm[2] = tess->normal[2];
-  if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) {
-    ComputeNormal( tess, norm, FALSE );
-  }
-
-  sign = ComputeNormal( tess, norm, TRUE );
-  if( sign == SIGN_INCONSISTENT ) {
-    /* Fan triangles did not have a consistent orientation */
-    return FALSE;
-  }
-  if( sign == 0 ) {
-    /* All triangles were degenerate */
-    return TRUE;
-  }
-
-  /* Make sure we do the right thing for each winding rule */
-  switch( tess->windingRule ) {
-  case GLU_TESS_WINDING_ODD:
-  case GLU_TESS_WINDING_NONZERO:
-    break;
-  case GLU_TESS_WINDING_POSITIVE:
-    if( sign < 0 ) return TRUE;
-    break;
-  case GLU_TESS_WINDING_NEGATIVE:
-    if( sign > 0 ) return TRUE;
-    break;
-  case GLU_TESS_WINDING_ABS_GEQ_TWO:
-    return TRUE;
-  }
-
-  CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP
-			  : (tess->cacheCount > 3) ? GL_TRIANGLE_FAN
-			  : GL_TRIANGLES );
-
-  CALL_VERTEX_OR_VERTEX_DATA( v0->data ); 
-  if( sign > 0 ) {
-    for( vc = v0+1; vc < vn; ++vc ) {
-      CALL_VERTEX_OR_VERTEX_DATA( vc->data ); 
-    }
-  } else {
-    for( vc = vn-1; vc > v0; --vc ) {
-      CALL_VERTEX_OR_VERTEX_DATA( vc->data ); 
-    }
-  }
-  CALL_END_OR_END_DATA();
-  return TRUE;
-}
diff --git a/third_party/glu/libtess/render.h b/third_party/glu/libtess/render.h
deleted file mode 100644
index 598423e..0000000
--- a/third_party/glu/libtess/render.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/render.h#5 $
-*/
-
-#ifndef __render_h_
-#define __render_h_
-
-#include "mesh.h"
-
-/* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle
- * fans, strips, and separate triangles.  A substantial effort is made
- * to use as few rendering primitives as possible (ie. to make the fans
- * and strips as large as possible).
- *
- * The rendering output is provided as callbacks (see the api).
- */
-void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh );
-void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh );
-
-GLboolean __gl_renderCache( GLUtesselator *tess );
-
-#endif
diff --git a/third_party/glu/libtess/sweep.c b/third_party/glu/libtess/sweep.c
deleted file mode 100644
index 6fe8a47..0000000
--- a/third_party/glu/libtess/sweep.c
+++ /dev/null
@@ -1,1362 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/sweep.c#5 $
-*/
-
-#include "gluos.h"
-#include <assert.h>
-#include <stddef.h>
-#include <setjmp.h>		/* longjmp */
-#include <limits.h>		/* LONG_MAX */
-
-#include "mesh.h"
-#include "geom.h"
-#include "tess.h"
-#include "dict.h"
-#include "priorityq.h"
-#include "memalloc.h"
-#include "sweep.h"
-
-#define TRUE 1
-#define FALSE 0
-
-#ifdef FOR_TRITE_TEST_PROGRAM
-extern void DebugEvent( GLUtesselator *tess );
-#else
-#define DebugEvent( tess )
-#endif
-
-/*
- * Invariants for the Edge Dictionary.
- * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2)
- *   at any valid location of the sweep event
- * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2
- *   share a common endpoint
- * - for each e, e->Dst has been processed, but not e->Org
- * - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org)
- *   where "event" is the current sweep line event.
- * - no edge e has zero length
- *
- * Invariants for the Mesh (the processed portion).
- * - the portion of the mesh left of the sweep line is a planar graph,
- *   ie. there is *some* way to embed it in the plane
- * - no processed edge has zero length
- * - no two processed vertices have identical coordinates
- * - each "inside" region is monotone, ie. can be broken into two chains
- *   of monotonically increasing vertices according to VertLeq(v1,v2)
- *   - a non-invariant: these chains may intersect (very slightly)
- *
- * Invariants for the Sweep.
- * - if none of the edges incident to the event vertex have an activeRegion
- *   (ie. none of these edges are in the edge dictionary), then the vertex
- *   has only right-going edges.
- * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced
- *   by ConnectRightVertex), then it is the only right-going edge from
- *   its associated vertex.  (This says that these edges exist only
- *   when it is necessary.)
- */
-
-#define MAX(x,y)	((x) >= (y) ? (x) : (y))
-#define MIN(x,y)	((x) <= (y) ? (x) : (y))
-
-/* When we merge two edges into one, we need to compute the combined
- * winding of the new edge.
- */
-#define AddWinding(eDst,eSrc)	(eDst->winding += eSrc->winding, \
-				 eDst->Sym->winding += eSrc->Sym->winding)
-
-static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent );
-static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp );
-static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp );
-
-static int EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1,
-		    ActiveRegion *reg2 )
-/*
- * Both edges must be directed from right to left (this is the canonical
- * direction for the upper edge of each region).
- *
- * The strategy is to evaluate a "t" value for each edge at the
- * current sweep line position, given by tess->event.  The calculations
- * are designed to be very stable, but of course they are not perfect.
- *
- * Special case: if both edge destinations are at the sweep event,
- * we sort the edges by slope (they would otherwise compare equally).
- */
-{
-  GLUvertex *event = tess->event;
-  GLUhalfEdge *e1, *e2;
-  GLdouble t1, t2;
-
-  e1 = reg1->eUp;
-  e2 = reg2->eUp;
-
-  if( e1->Dst == event ) {
-    if( e2->Dst == event ) {
-      /* Two edges right of the sweep line which meet at the sweep event.
-       * Sort them by slope.
-       */
-      if( VertLeq( e1->Org, e2->Org )) {
-	return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0;
-      }
-      return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0;
-    }
-    return EdgeSign( e2->Dst, event, e2->Org ) <= 0;
-  }
-  if( e2->Dst == event ) {
-    return EdgeSign( e1->Dst, event, e1->Org ) >= 0;
-  }
-
-  /* General case - compute signed distance *from* e1, e2 to event */
-  t1 = EdgeEval( e1->Dst, event, e1->Org );
-  t2 = EdgeEval( e2->Dst, event, e2->Org );
-  return (t1 >= t2);
-}
-
-
-static void DeleteRegion( GLUtesselator *tess, ActiveRegion *reg )
-{
-  if( reg->fixUpperEdge ) {
-    /* It was created with zero winding number, so it better be
-     * deleted with zero winding number (ie. it better not get merged
-     * with a real edge).
-     */
-    assert( reg->eUp->winding == 0 );
-  }
-  reg->eUp->activeRegion = NULL;
-  dictDelete( tess->dict, reg->nodeUp ); /* __gl_dictListDelete */
-  memFree( reg );
-}
-
-
-static int FixUpperEdge( ActiveRegion *reg, GLUhalfEdge *newEdge )
-/*
- * Replace an upper edge which needs fixing (see ConnectRightVertex).
- */
-{
-  assert( reg->fixUpperEdge );
-  if ( !__gl_meshDelete( reg->eUp ) ) return 0;
-  reg->fixUpperEdge = FALSE;
-  reg->eUp = newEdge;
-  newEdge->activeRegion = reg;
-
-  return 1; 
-}
-
-static ActiveRegion *TopLeftRegion( ActiveRegion *reg )
-{
-  GLUvertex *org = reg->eUp->Org;
-  GLUhalfEdge *e;
-
-  /* Find the region above the uppermost edge with the same origin */
-  do {
-    reg = RegionAbove( reg );
-  } while( reg->eUp->Org == org );
-
-  /* If the edge above was a temporary edge introduced by ConnectRightVertex,
-   * now is the time to fix it.
-   */
-  if( reg->fixUpperEdge ) {
-    e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext );
-    if (e == NULL) return NULL;
-    if ( !FixUpperEdge( reg, e ) ) return NULL;
-    reg = RegionAbove( reg );
-  }
-  return reg;
-}
-
-static ActiveRegion *TopRightRegion( ActiveRegion *reg )
-{
-  GLUvertex *dst = reg->eUp->Dst;
-
-  /* Find the region above the uppermost edge with the same destination */
-  do {
-    reg = RegionAbove( reg );
-  } while( reg->eUp->Dst == dst );
-  return reg;
-}
-
-static ActiveRegion *AddRegionBelow( GLUtesselator *tess,
-				     ActiveRegion *regAbove,
-				     GLUhalfEdge *eNewUp )
-/*
- * Add a new active region to the sweep line, *somewhere* below "regAbove"
- * (according to where the new edge belongs in the sweep-line dictionary).
- * The upper edge of the new region will be "eNewUp".
- * Winding number and "inside" flag are not updated.
- */
-{
-  ActiveRegion *regNew = (ActiveRegion *)memAlloc( sizeof( ActiveRegion ));
-  if (regNew == NULL) longjmp(tess->env,1);
-
-  regNew->eUp = eNewUp;
-  /* __gl_dictListInsertBefore */ 
-  regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew );
-  if (regNew->nodeUp == NULL) longjmp(tess->env,1);
-  regNew->fixUpperEdge = FALSE;
-  regNew->sentinel = FALSE;
-  regNew->dirty = FALSE;
-
-  eNewUp->activeRegion = regNew;
-  return regNew;
-}
-
-static GLboolean IsWindingInside( GLUtesselator *tess, int n )
-{
-  switch( tess->windingRule ) {
-  case GLU_TESS_WINDING_ODD:
-    return (n & 1);
-  case GLU_TESS_WINDING_NONZERO:
-    return (n != 0);
-  case GLU_TESS_WINDING_POSITIVE:
-    return (n > 0);
-  case GLU_TESS_WINDING_NEGATIVE:
-    return (n < 0);
-  case GLU_TESS_WINDING_ABS_GEQ_TWO:
-    return (n >= 2) || (n <= -2);
-  }
-  /*LINTED*/
-  assert( FALSE );
-  /*NOTREACHED*/
-  return 0;
-}
-
-
-static void ComputeWinding( GLUtesselator *tess, ActiveRegion *reg )
-{
-  reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding;
-  reg->inside = IsWindingInside( tess, reg->windingNumber );
-}
-
-
-static void FinishRegion( GLUtesselator *tess, ActiveRegion *reg )
-/*
- * Delete a region from the sweep line.  This happens when the upper
- * and lower chains of a region meet (at a vertex on the sweep line).
- * The "inside" flag is copied to the appropriate mesh face (we could
- * not do this before -- since the structure of the mesh is always
- * changing, this face may not have even existed until now).
- */
-{
-  GLUhalfEdge *e = reg->eUp;
-  GLUface *f = e->Lface;
-
-  f->inside = reg->inside;
-  f->anEdge = e;   /* optimization for __gl_meshTessellateMonoRegion() */
-  DeleteRegion( tess, reg );
-}
-
-
-static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess,
-	       ActiveRegion *regFirst, ActiveRegion *regLast )
-/*
- * We are given a vertex with one or more left-going edges.  All affected
- * edges should be in the edge dictionary.  Starting at regFirst->eUp,
- * we walk down deleting all regions where both edges have the same
- * origin vOrg.  At the same time we copy the "inside" flag from the
- * active region to the face, since at this point each face will belong
- * to at most one region (this was not necessarily true until this point
- * in the sweep).  The walk stops at the region above regLast; if regLast
- * is NULL we walk as far as possible.  At the same time we relink the
- * mesh if necessary, so that the ordering of edges around vOrg is the
- * same as in the dictionary.
- */
-{
-  ActiveRegion *reg, *regPrev;
-  GLUhalfEdge *e, *ePrev;
-
-  regPrev = regFirst;
-  ePrev = regFirst->eUp;
-  while( regPrev != regLast ) {
-    regPrev->fixUpperEdge = FALSE;	/* placement was OK */
-    reg = RegionBelow( regPrev );
-    e = reg->eUp;
-    if( e->Org != ePrev->Org ) {
-      if( ! reg->fixUpperEdge ) {
-	/* Remove the last left-going edge.  Even though there are no further
-	 * edges in the dictionary with this origin, there may be further
-	 * such edges in the mesh (if we are adding left edges to a vertex
-	 * that has already been processed).  Thus it is important to call
-	 * FinishRegion rather than just DeleteRegion.
-	 */
-	FinishRegion( tess, regPrev );
-	break;
-      }
-      /* If the edge below was a temporary edge introduced by
-       * ConnectRightVertex, now is the time to fix it.
-       */
-      e = __gl_meshConnect( ePrev->Lprev, e->Sym );
-      if (e == NULL) longjmp(tess->env,1);
-      if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1);
-    }
-
-    /* Relink edges so that ePrev->Onext == e */
-    if( ePrev->Onext != e ) {
-      if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1);
-      if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1);
-    }
-    FinishRegion( tess, regPrev );	/* may change reg->eUp */
-    ePrev = reg->eUp;
-    regPrev = reg;
-  }
-  return ePrev;
-}
-
-
-static void AddRightEdges( GLUtesselator *tess, ActiveRegion *regUp,
-       GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft,
-       GLboolean cleanUp )
-/*
- * Purpose: insert right-going edges into the edge dictionary, and update
- * winding numbers and mesh connectivity appropriately.  All right-going
- * edges share a common origin vOrg.  Edges are inserted CCW starting at
- * eFirst; the last edge inserted is eLast->Oprev.  If vOrg has any
- * left-going edges already processed, then eTopLeft must be the edge
- * such that an imaginary upward vertical segment from vOrg would be
- * contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft
- * should be NULL.
- */
-{
-  ActiveRegion *reg, *regPrev;
-  GLUhalfEdge *e, *ePrev;
-  int firstTime = TRUE;
-
-  /* Insert the new right-going edges in the dictionary */
-  e = eFirst;
-  do {
-    assert( VertLeq( e->Org, e->Dst ));
-    AddRegionBelow( tess, regUp, e->Sym );
-    e = e->Onext;
-  } while ( e != eLast );
-
-  /* Walk *all* right-going edges from e->Org, in the dictionary order,
-   * updating the winding numbers of each region, and re-linking the mesh
-   * edges to match the dictionary ordering (if necessary).
-   */
-  if( eTopLeft == NULL ) {
-    eTopLeft = RegionBelow( regUp )->eUp->Rprev;
-  }
-  regPrev = regUp;
-  ePrev = eTopLeft;
-  for( ;; ) {
-    reg = RegionBelow( regPrev );
-    e = reg->eUp->Sym;
-    if( e->Org != ePrev->Org ) break;
-
-    if( e->Onext != ePrev ) {
-      /* Unlink e from its current position, and relink below ePrev */
-      if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1);
-      if ( !__gl_meshSplice( ePrev->Oprev, e ) ) longjmp(tess->env,1);
-    }
-    /* Compute the winding number and "inside" flag for the new regions */
-    reg->windingNumber = regPrev->windingNumber - e->winding;
-    reg->inside = IsWindingInside( tess, reg->windingNumber );
-
-    /* Check for two outgoing edges with same slope -- process these
-     * before any intersection tests (see example in __gl_computeInterior).
-     */
-    regPrev->dirty = TRUE;
-    if( ! firstTime && CheckForRightSplice( tess, regPrev )) {
-      AddWinding( e, ePrev );
-      DeleteRegion( tess, regPrev );
-      if ( !__gl_meshDelete( ePrev ) ) longjmp(tess->env,1);
-    }
-    firstTime = FALSE;
-    regPrev = reg;
-    ePrev = e;
-  }
-  regPrev->dirty = TRUE;
-  assert( regPrev->windingNumber - e->winding == reg->windingNumber );
-
-  if( cleanUp ) {
-    /* Check for intersections between newly adjacent edges. */
-    WalkDirtyRegions( tess, regPrev );
-  }
-}
-
-
-static void CallCombine( GLUtesselator *tess, GLUvertex *isect,
-			 void *data[4], GLfloat weights[4], int needed )
-{
-  GLdouble coords[3];
-
-  /* Copy coord data in case the callback changes it. */
-  coords[0] = isect->coords[0];
-  coords[1] = isect->coords[1];
-  coords[2] = isect->coords[2];
-
-  isect->data = NULL;
-  CALL_COMBINE_OR_COMBINE_DATA( coords, data, weights, &isect->data );
-  if( isect->data == NULL ) {
-    if( ! needed ) {
-      isect->data = data[0];
-    } else if( ! tess->fatalError ) {
-      /* The only way fatal error is when two edges are found to intersect,
-       * but the user has not provided the callback necessary to handle
-       * generated intersection points.
-       */
-      CALL_ERROR_OR_ERROR_DATA( GLU_TESS_NEED_COMBINE_CALLBACK );
-      tess->fatalError = TRUE;
-    }
-  }
-}
-
-static void SpliceMergeVertices( GLUtesselator *tess, GLUhalfEdge *e1,
-				 GLUhalfEdge *e2 )
-/*
- * Two vertices with idential coordinates are combined into one.
- * e1->Org is kept, while e2->Org is discarded.
- */
-{
-  void *data[4] = { NULL, NULL, NULL, NULL };
-  GLfloat weights[4] = { 0.5, 0.5, 0.0, 0.0 };
-
-  data[0] = e1->Org->data;
-  data[1] = e2->Org->data;
-  CallCombine( tess, e1->Org, data, weights, FALSE );
-  if ( !__gl_meshSplice( e1, e2 ) ) longjmp(tess->env,1); 
-}
-
-static void VertexWeights( GLUvertex *isect, GLUvertex *org, GLUvertex *dst,
-                           GLfloat *weights )
-/*
- * Find some weights which describe how the intersection vertex is
- * a linear combination of "org" and "dest".  Each of the two edges
- * which generated "isect" is allocated 50% of the weight; each edge
- * splits the weight between its org and dst according to the
- * relative distance to "isect".
- */
-{
-  GLdouble t1 = VertL1dist( org, isect );
-  GLdouble t2 = VertL1dist( dst, isect );
-
-  weights[0] = 0.5 * t2 / (t1 + t2);
-  weights[1] = 0.5 * t1 / (t1 + t2);
-  isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0];
-  isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1];
-  isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2];
-}
-
-
-static void GetIntersectData( GLUtesselator *tess, GLUvertex *isect,
-       GLUvertex *orgUp, GLUvertex *dstUp,
-       GLUvertex *orgLo, GLUvertex *dstLo )
-/*
- * We've computed a new intersection point, now we need a "data" pointer
- * from the user so that we can refer to this new vertex in the
- * rendering callbacks.
- */
-{
-  void *data[4];
-  GLfloat weights[4];
-
-  data[0] = orgUp->data;
-  data[1] = dstUp->data;
-  data[2] = orgLo->data;
-  data[3] = dstLo->data;
-
-  isect->coords[0] = isect->coords[1] = isect->coords[2] = 0;
-  VertexWeights( isect, orgUp, dstUp, &weights[0] );
-  VertexWeights( isect, orgLo, dstLo, &weights[2] );
-
-  CallCombine( tess, isect, data, weights, TRUE );
-}
-
-static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp )
-/*
- * Check the upper and lower edge of "regUp", to make sure that the
- * eUp->Org is above eLo, or eLo->Org is below eUp (depending on which
- * origin is leftmost).
- *
- * The main purpose is to splice right-going edges with the same
- * dest vertex and nearly identical slopes (ie. we can't distinguish
- * the slopes numerically).  However the splicing can also help us
- * to recover from numerical errors.  For example, suppose at one
- * point we checked eUp and eLo, and decided that eUp->Org is barely
- * above eLo.  Then later, we split eLo into two edges (eg. from
- * a splice operation like this one).  This can change the result of
- * our test so that now eUp->Org is incident to eLo, or barely below it.
- * We must correct this condition to maintain the dictionary invariants.
- *
- * One possibility is to check these edges for intersection again
- * (ie. CheckForIntersect).  This is what we do if possible.  However
- * CheckForIntersect requires that tess->event lies between eUp and eLo,
- * so that it has something to fall back on when the intersection
- * calculation gives us an unusable answer.  So, for those cases where
- * we can't check for intersection, this routine fixes the problem
- * by just splicing the offending vertex into the other edge.
- * This is a guaranteed solution, no matter how degenerate things get.
- * Basically this is a combinatorial solution to a numerical problem.
- */
-{
-  ActiveRegion *regLo = RegionBelow(regUp);
-  GLUhalfEdge *eUp = regUp->eUp;
-  GLUhalfEdge *eLo = regLo->eUp;
-
-  if( VertLeq( eUp->Org, eLo->Org )) {
-    if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE;
-
-    /* eUp->Org appears to be below eLo */
-    if( ! VertEq( eUp->Org, eLo->Org )) {
-      /* Splice eUp->Org into eLo */
-      if ( __gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
-      if ( !__gl_meshSplice( eUp, eLo->Oprev ) ) longjmp(tess->env,1);
-      regUp->dirty = regLo->dirty = TRUE;
-
-    } else if( eUp->Org != eLo->Org ) {
-      /* merge the two vertices, discarding eUp->Org */
-      pqDelete( tess->pq, eUp->Org->pqHandle ); /* __gl_pqSortDelete */
-      SpliceMergeVertices( tess, eLo->Oprev, eUp );
-    }
-  } else {
-    if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE;
-
-    /* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */
-    RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
-    if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
-    if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1);
-  }
-  return TRUE;
-}
-
-static int CheckForLeftSplice( GLUtesselator *tess, ActiveRegion *regUp )
-/*
- * Check the upper and lower edge of "regUp", to make sure that the
- * eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which
- * destination is rightmost).
- *
- * Theoretically, this should always be true.  However, splitting an edge
- * into two pieces can change the results of previous tests.  For example,
- * suppose at one point we checked eUp and eLo, and decided that eUp->Dst
- * is barely above eLo.  Then later, we split eLo into two edges (eg. from
- * a splice operation like this one).  This can change the result of
- * the test so that now eUp->Dst is incident to eLo, or barely below it.
- * We must correct this condition to maintain the dictionary invariants
- * (otherwise new edges might get inserted in the wrong place in the
- * dictionary, and bad stuff will happen).
- *
- * We fix the problem by just splicing the offending vertex into the
- * other edge.
- */
-{
-  ActiveRegion *regLo = RegionBelow(regUp);
-  GLUhalfEdge *eUp = regUp->eUp;
-  GLUhalfEdge *eLo = regLo->eUp;
-  GLUhalfEdge *e;
-
-  assert( ! VertEq( eUp->Dst, eLo->Dst ));
-
-  if( VertLeq( eUp->Dst, eLo->Dst )) {
-    if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE;
-
-    /* eLo->Dst is above eUp, so splice eLo->Dst into eUp */
-    RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
-    e = __gl_meshSplitEdge( eUp );
-    if (e == NULL) longjmp(tess->env,1);
-    if ( !__gl_meshSplice( eLo->Sym, e ) ) longjmp(tess->env,1);
-    e->Lface->inside = regUp->inside;
-  } else {
-    if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE;
-
-    /* eUp->Dst is below eLo, so splice eUp->Dst into eLo */
-    regUp->dirty = regLo->dirty = TRUE;
-    e = __gl_meshSplitEdge( eLo );
-    if (e == NULL) longjmp(tess->env,1);    
-    if ( !__gl_meshSplice( eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1);
-    e->Rface->inside = regUp->inside;
-  }
-  return TRUE;
-}
-
-
-static int CheckForIntersect( GLUtesselator *tess, ActiveRegion *regUp )
-/*
- * Check the upper and lower edges of the given region to see if
- * they intersect.  If so, create the intersection and add it
- * to the data structures.
- *
- * Returns TRUE if adding the new intersection resulted in a recursive
- * call to AddRightEdges(); in this case all "dirty" regions have been
- * checked for intersections, and possibly regUp has been deleted.
- */
-{
-  ActiveRegion *regLo = RegionBelow(regUp);
-  GLUhalfEdge *eUp = regUp->eUp;
-  GLUhalfEdge *eLo = regLo->eUp;
-  GLUvertex *orgUp = eUp->Org;
-  GLUvertex *orgLo = eLo->Org;
-  GLUvertex *dstUp = eUp->Dst;
-  GLUvertex *dstLo = eLo->Dst;
-  GLdouble tMinUp, tMaxLo;
-  GLUvertex isect, *orgMin;
-  GLUhalfEdge *e;
-
-  assert( ! VertEq( dstLo, dstUp ));
-  assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 );
-  assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 );
-  assert( orgUp != tess->event && orgLo != tess->event );
-  assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge );
-
-  if( orgUp == orgLo ) return FALSE;	/* right endpoints are the same */
-
-  tMinUp = MIN( orgUp->t, dstUp->t );
-  tMaxLo = MAX( orgLo->t, dstLo->t );
-  if( tMinUp > tMaxLo ) return FALSE;	/* t ranges do not overlap */
-
-  if( VertLeq( orgUp, orgLo )) {
-    if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE;
-  } else {
-    if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE;
-  }
-
-  /* At this point the edges intersect, at least marginally */
-  DebugEvent( tess );
-
-  __gl_edgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect );
-  /* The following properties are guaranteed: */
-  assert( MIN( orgUp->t, dstUp->t ) <= isect.t );
-  assert( isect.t <= MAX( orgLo->t, dstLo->t ));
-  assert( MIN( dstLo->s, dstUp->s ) <= isect.s );
-  assert( isect.s <= MAX( orgLo->s, orgUp->s ));
-
-  if( VertLeq( &isect, tess->event )) {
-    /* The intersection point lies slightly to the left of the sweep line,
-     * so move it until it''s slightly to the right of the sweep line.
-     * (If we had perfect numerical precision, this would never happen
-     * in the first place).  The easiest and safest thing to do is
-     * replace the intersection by tess->event.
-     */
-    isect.s = tess->event->s;
-    isect.t = tess->event->t;
-  }
-  /* Similarly, if the computed intersection lies to the right of the
-   * rightmost origin (which should rarely happen), it can cause
-   * unbelievable inefficiency on sufficiently degenerate inputs.
-   * (If you have the test program, try running test54.d with the
-   * "X zoom" option turned on).
-   */
-  orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo;
-  if( VertLeq( orgMin, &isect )) {
-    isect.s = orgMin->s;
-    isect.t = orgMin->t;
-  }
-
-  if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) {
-    /* Easy case -- intersection at one of the right endpoints */
-    (void) CheckForRightSplice( tess, regUp );
-    return FALSE;
-  }
-
-  if(    (! VertEq( dstUp, tess->event )
-	  && EdgeSign( dstUp, tess->event, &isect ) >= 0)
-      || (! VertEq( dstLo, tess->event )
-	  && EdgeSign( dstLo, tess->event, &isect ) <= 0 ))
-  {
-    /* Very unusual -- the new upper or lower edge would pass on the
-     * wrong side of the sweep event, or through it.  This can happen
-     * due to very small numerical errors in the intersection calculation.
-     */
-    if( dstLo == tess->event ) {
-      /* Splice dstLo into eUp, and process the new region(s) */
-      if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
-      if ( !__gl_meshSplice( eLo->Sym, eUp ) ) longjmp(tess->env,1);
-      regUp = TopLeftRegion( regUp );
-      if (regUp == NULL) longjmp(tess->env,1);
-      eUp = RegionBelow(regUp)->eUp;
-      FinishLeftRegions( tess, RegionBelow(regUp), regLo );
-      AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE );
-      return TRUE;
-    }
-    if( dstUp == tess->event ) {
-      /* Splice dstUp into eLo, and process the new region(s) */
-      if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
-      if ( !__gl_meshSplice( eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1); 
-      regLo = regUp;
-      regUp = TopRightRegion( regUp );
-      e = RegionBelow(regUp)->eUp->Rprev;
-      regLo->eUp = eLo->Oprev;
-      eLo = FinishLeftRegions( tess, regLo, NULL );
-      AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE );
-      return TRUE;
-    }
-    /* Special case: called from ConnectRightVertex.  If either
-     * edge passes on the wrong side of tess->event, split it
-     * (and wait for ConnectRightVertex to splice it appropriately).
-     */
-    if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) {
-      RegionAbove(regUp)->dirty = regUp->dirty = TRUE;
-      if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
-      eUp->Org->s = tess->event->s;
-      eUp->Org->t = tess->event->t;
-    }
-    if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) {
-      regUp->dirty = regLo->dirty = TRUE;
-      if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
-      eLo->Org->s = tess->event->s;
-      eLo->Org->t = tess->event->t;
-    }
-    /* leave the rest for ConnectRightVertex */
-    return FALSE;
-  }
-
-  /* General case -- split both edges, splice into new vertex.
-   * When we do the splice operation, the order of the arguments is
-   * arbitrary as far as correctness goes.  However, when the operation
-   * creates a new face, the work done is proportional to the size of
-   * the new face.  We expect the faces in the processed part of
-   * the mesh (ie. eUp->Lface) to be smaller than the faces in the
-   * unprocessed original contours (which will be eLo->Oprev->Lface).
-   */
-  if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1);
-  if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1);
-  if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1);
-  eUp->Org->s = isect.s;
-  eUp->Org->t = isect.t;
-  eUp->Org->pqHandle = pqInsert( tess->pq, eUp->Org ); /* __gl_pqSortInsert */
-  if (eUp->Org->pqHandle == LONG_MAX) {
-     pqDeletePriorityQ(tess->pq);	/* __gl_pqSortDeletePriorityQ */
-     tess->pq = NULL;
-     longjmp(tess->env,1);
-  }
-  GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo );
-  RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE;
-  return FALSE;
-}
-
-static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp )
-/*
- * When the upper or lower edge of any region changes, the region is
- * marked "dirty".  This routine walks through all the dirty regions
- * and makes sure that the dictionary invariants are satisfied
- * (see the comments at the beginning of this file).  Of course
- * new dirty regions can be created as we make changes to restore
- * the invariants.
- */
-{
-  ActiveRegion *regLo = RegionBelow(regUp);
-  GLUhalfEdge *eUp, *eLo;
-
-  for( ;; ) {
-    /* Find the lowest dirty region (we walk from the bottom up). */
-    while( regLo->dirty ) {
-      regUp = regLo;
-      regLo = RegionBelow(regLo);
-    }
-    if( ! regUp->dirty ) {
-      regLo = regUp;
-      regUp = RegionAbove( regUp );
-      if( regUp == NULL || ! regUp->dirty ) {
-	/* We've walked all the dirty regions */
-	return;
-      }
-    }
-    regUp->dirty = FALSE;
-    eUp = regUp->eUp;
-    eLo = regLo->eUp;
-
-    if( eUp->Dst != eLo->Dst ) {
-      /* Check that the edge ordering is obeyed at the Dst vertices. */
-      if( CheckForLeftSplice( tess, regUp )) {
-
-	/* If the upper or lower edge was marked fixUpperEdge, then
-	 * we no longer need it (since these edges are needed only for
-	 * vertices which otherwise have no right-going edges).
-	 */
-	if( regLo->fixUpperEdge ) {
-	  DeleteRegion( tess, regLo );
-	  if ( !__gl_meshDelete( eLo ) ) longjmp(tess->env,1);
-	  regLo = RegionBelow( regUp );
-	  eLo = regLo->eUp;
-	} else if( regUp->fixUpperEdge ) {
-	  DeleteRegion( tess, regUp );
-	  if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1);
-	  regUp = RegionAbove( regLo );
-	  eUp = regUp->eUp;
-	}
-      }
-    }
-    if( eUp->Org != eLo->Org ) {
-      if(    eUp->Dst != eLo->Dst
-	  && ! regUp->fixUpperEdge && ! regLo->fixUpperEdge
-          && (eUp->Dst == tess->event || eLo->Dst == tess->event) )
-      {
-	/* When all else fails in CheckForIntersect(), it uses tess->event
-	 * as the intersection location.  To make this possible, it requires
-	 * that tess->event lie between the upper and lower edges, and also
-	 * that neither of these is marked fixUpperEdge (since in the worst
-	 * case it might splice one of these edges into tess->event, and
-	 * violate the invariant that fixable edges are the only right-going
-	 * edge from their associated vertex).
-         */
-	if( CheckForIntersect( tess, regUp )) {
-	  /* WalkDirtyRegions() was called recursively; we're done */
-	  return;
-	}
-      } else {
-	/* Even though we can't use CheckForIntersect(), the Org vertices
-	 * may violate the dictionary edge ordering.  Check and correct this.
-	 */
-	(void) CheckForRightSplice( tess, regUp );
-      }
-    }
-    if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) {
-      /* A degenerate loop consisting of only two edges -- delete it. */
-      AddWinding( eLo, eUp );
-      DeleteRegion( tess, regUp );
-      if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1);
-      regUp = RegionAbove( regLo );
-    }
-  }
-}
-
-
-static void ConnectRightVertex( GLUtesselator *tess, ActiveRegion *regUp,
-			        GLUhalfEdge *eBottomLeft )
-/*
- * Purpose: connect a "right" vertex vEvent (one where all edges go left)
- * to the unprocessed portion of the mesh.  Since there are no right-going
- * edges, two regions (one above vEvent and one below) are being merged
- * into one.  "regUp" is the upper of these two regions.
- *
- * There are two reasons for doing this (adding a right-going edge):
- *  - if the two regions being merged are "inside", we must add an edge
- *    to keep them separated (the combined region would not be monotone).
- *  - in any case, we must leave some record of vEvent in the dictionary,
- *    so that we can merge vEvent with features that we have not seen yet.
- *    For example, maybe there is a vertical edge which passes just to
- *    the right of vEvent; we would like to splice vEvent into this edge.
- *
- * However, we don't want to connect vEvent to just any vertex.  We don''t
- * want the new edge to cross any other edges; otherwise we will create
- * intersection vertices even when the input data had no self-intersections.
- * (This is a bad thing; if the user's input data has no intersections,
- * we don't want to generate any false intersections ourselves.)
- *
- * Our eventual goal is to connect vEvent to the leftmost unprocessed
- * vertex of the combined region (the union of regUp and regLo).
- * But because of unseen vertices with all right-going edges, and also
- * new vertices which may be created by edge intersections, we don''t
- * know where that leftmost unprocessed vertex is.  In the meantime, we
- * connect vEvent to the closest vertex of either chain, and mark the region
- * as "fixUpperEdge".  This flag says to delete and reconnect this edge
- * to the next processed vertex on the boundary of the combined region.
- * Quite possibly the vertex we connected to will turn out to be the
- * closest one, in which case we won''t need to make any changes.
- */
-{
-  GLUhalfEdge *eNew;
-  GLUhalfEdge *eTopLeft = eBottomLeft->Onext;
-  ActiveRegion *regLo = RegionBelow(regUp);
-  GLUhalfEdge *eUp = regUp->eUp;
-  GLUhalfEdge *eLo = regLo->eUp;
-  int degenerate = FALSE;
-
-  if( eUp->Dst != eLo->Dst ) {
-    (void) CheckForIntersect( tess, regUp );
-  }
-
-  /* Possible new degeneracies: upper or lower edge of regUp may pass
-   * through vEvent, or may coincide with new intersection vertex
-   */
-  if( VertEq( eUp->Org, tess->event )) {
-    if ( !__gl_meshSplice( eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1);
-    regUp = TopLeftRegion( regUp );
-    if (regUp == NULL) longjmp(tess->env,1);
-    eTopLeft = RegionBelow( regUp )->eUp;
-    FinishLeftRegions( tess, RegionBelow(regUp), regLo );
-    degenerate = TRUE;
-  }
-  if( VertEq( eLo->Org, tess->event )) {
-    if ( !__gl_meshSplice( eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1);
-    eBottomLeft = FinishLeftRegions( tess, regLo, NULL );
-    degenerate = TRUE;
-  }
-  if( degenerate ) {
-    AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
-    return;
-  }
-
-  /* Non-degenerate situation -- need to add a temporary, fixable edge.
-   * Connect to the closer of eLo->Org, eUp->Org.
-   */
-  if( VertLeq( eLo->Org, eUp->Org )) {
-    eNew = eLo->Oprev;
-  } else {
-    eNew = eUp;
-  }
-  eNew = __gl_meshConnect( eBottomLeft->Lprev, eNew );
-  if (eNew == NULL) longjmp(tess->env,1);
-
-  /* Prevent cleanup, otherwise eNew might disappear before we've even
-   * had a chance to mark it as a temporary edge.
-   */
-  AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE );
-  eNew->Sym->activeRegion->fixUpperEdge = TRUE;
-  WalkDirtyRegions( tess, regUp );
-}
-
-/* Because vertices at exactly the same location are merged together
- * before we process the sweep event, some degenerate cases can't occur.
- * However if someone eventually makes the modifications required to
- * merge features which are close together, the cases below marked
- * TOLERANCE_NONZERO will be useful.  They were debugged before the
- * code to merge identical vertices in the main loop was added.
- */
-#define TOLERANCE_NONZERO	FALSE
-
-static void ConnectLeftDegenerate( GLUtesselator *tess,
-				   ActiveRegion *regUp, GLUvertex *vEvent )
-/*
- * The event vertex lies exacty on an already-processed edge or vertex.
- * Adding the new vertex involves splicing it into the already-processed
- * part of the mesh.
- */
-{
-  GLUhalfEdge *e, *eTopLeft, *eTopRight, *eLast;
-  ActiveRegion *reg;
-
-  e = regUp->eUp;
-  if( VertEq( e->Org, vEvent )) {
-    /* e->Org is an unprocessed vertex - just combine them, and wait
-     * for e->Org to be pulled from the queue
-     */
-    assert( TOLERANCE_NONZERO );
-    SpliceMergeVertices( tess, e, vEvent->anEdge );
-    return;
-  }
-  
-  if( ! VertEq( e->Dst, vEvent )) {
-    /* General case -- splice vEvent into edge e which passes through it */
-    if (__gl_meshSplitEdge( e->Sym ) == NULL) longjmp(tess->env,1);
-    if( regUp->fixUpperEdge ) {
-      /* This edge was fixable -- delete unused portion of original edge */
-      if ( !__gl_meshDelete( e->Onext ) ) longjmp(tess->env,1);
-      regUp->fixUpperEdge = FALSE;
-    }
-    if ( !__gl_meshSplice( vEvent->anEdge, e ) ) longjmp(tess->env,1);
-    SweepEvent( tess, vEvent );	/* recurse */
-    return;
-  }
-
-  /* vEvent coincides with e->Dst, which has already been processed.
-   * Splice in the additional right-going edges.
-   */
-  assert( TOLERANCE_NONZERO );
-  regUp = TopRightRegion( regUp );
-  reg = RegionBelow( regUp );
-  eTopRight = reg->eUp->Sym;
-  eTopLeft = eLast = eTopRight->Onext;
-  if( reg->fixUpperEdge ) {
-    /* Here e->Dst has only a single fixable edge going right.
-     * We can delete it since now we have some real right-going edges.
-     */
-    assert( eTopLeft != eTopRight );   /* there are some left edges too */
-    DeleteRegion( tess, reg );
-    if ( !__gl_meshDelete( eTopRight ) ) longjmp(tess->env,1);
-    eTopRight = eTopLeft->Oprev;
-  }
-  if ( !__gl_meshSplice( vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1);
-  if( ! EdgeGoesLeft( eTopLeft )) {
-    /* e->Dst had no left-going edges -- indicate this to AddRightEdges() */
-    eTopLeft = NULL;
-  }
-  AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE );
-}
-
-
-static void ConnectLeftVertex( GLUtesselator *tess, GLUvertex *vEvent )
-/*
- * Purpose: connect a "left" vertex (one where both edges go right)
- * to the processed portion of the mesh.  Let R be the active region
- * containing vEvent, and let U and L be the upper and lower edge
- * chains of R.  There are two possibilities:
- *
- * - the normal case: split R into two regions, by connecting vEvent to
- *   the rightmost vertex of U or L lying to the left of the sweep line
- *
- * - the degenerate case: if vEvent is close enough to U or L, we
- *   merge vEvent into that edge chain.  The subcases are:
- *	- merging with the rightmost vertex of U or L
- *	- merging with the active edge of U or L
- *	- merging with an already-processed portion of U or L
- */
-{
-  ActiveRegion *regUp, *regLo, *reg;
-  GLUhalfEdge *eUp, *eLo, *eNew;
-  ActiveRegion tmp;
-
-  /* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */
-
-  /* Get a pointer to the active region containing vEvent */
-  tmp.eUp = vEvent->anEdge->Sym;
-  /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */
-  regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp ));
-  regLo = RegionBelow( regUp );
-  eUp = regUp->eUp;
-  eLo = regLo->eUp;
-
-  /* Try merging with U or L first */
-  if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) {
-    ConnectLeftDegenerate( tess, regUp, vEvent );
-    return;
-  }
-
-  /* Connect vEvent to rightmost processed vertex of either chain.
-   * e->Dst is the vertex that we will connect to vEvent.
-   */
-  reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo;
-
-  if( regUp->inside || reg->fixUpperEdge) {
-    if( reg == regUp ) {
-      eNew = __gl_meshConnect( vEvent->anEdge->Sym, eUp->Lnext );
-      if (eNew == NULL) longjmp(tess->env,1);
-    } else {
-      GLUhalfEdge *tempHalfEdge= __gl_meshConnect( eLo->Dnext, vEvent->anEdge);
-      if (tempHalfEdge == NULL) longjmp(tess->env,1);
-
-      eNew = tempHalfEdge->Sym;
-    }
-    if( reg->fixUpperEdge ) {
-      if ( !FixUpperEdge( reg, eNew ) ) longjmp(tess->env,1);
-    } else {
-      ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew ));
-    }
-    SweepEvent( tess, vEvent );
-  } else {
-    /* The new vertex is in a region which does not belong to the polygon.
-     * We don''t need to connect this vertex to the rest of the mesh.
-     */
-    AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE );
-  }
-}
-
-
-static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent )
-/*
- * Does everything necessary when the sweep line crosses a vertex.
- * Updates the mesh and the edge dictionary.
- */
-{
-  ActiveRegion *regUp, *reg;
-  GLUhalfEdge *e, *eTopLeft, *eBottomLeft;
-
-  tess->event = vEvent;		/* for access in EdgeLeq() */
-  DebugEvent( tess );
-  
-  /* Check if this vertex is the right endpoint of an edge that is
-   * already in the dictionary.  In this case we don't need to waste
-   * time searching for the location to insert new edges.
-   */
-  e = vEvent->anEdge;
-  while( e->activeRegion == NULL ) {
-    e = e->Onext;
-    if( e == vEvent->anEdge ) {
-      /* All edges go right -- not incident to any processed edges */
-      ConnectLeftVertex( tess, vEvent );
-      return;
-    }
-  }
-
-  /* Processing consists of two phases: first we "finish" all the
-   * active regions where both the upper and lower edges terminate
-   * at vEvent (ie. vEvent is closing off these regions).
-   * We mark these faces "inside" or "outside" the polygon according
-   * to their winding number, and delete the edges from the dictionary.
-   * This takes care of all the left-going edges from vEvent.
-   */
-  regUp = TopLeftRegion( e->activeRegion );
-  if (regUp == NULL) longjmp(tess->env,1);
-  reg = RegionBelow( regUp );
-  eTopLeft = reg->eUp;
-  eBottomLeft = FinishLeftRegions( tess, reg, NULL );
-
-  /* Next we process all the right-going edges from vEvent.  This
-   * involves adding the edges to the dictionary, and creating the
-   * associated "active regions" which record information about the
-   * regions between adjacent dictionary edges.
-   */
-  if( eBottomLeft->Onext == eTopLeft ) {
-    /* No right-going edges -- add a temporary "fixable" edge */
-    ConnectRightVertex( tess, regUp, eBottomLeft );
-  } else {
-    AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE );
-  }
-}
-
-
-/* Make the sentinel coordinates big enough that they will never be
- * merged with real input features.  (Even with the largest possible
- * input contour and the maximum tolerance of 1.0, no merging will be
- * done with coordinates larger than 3 * GLU_TESS_MAX_COORD).
- */
-#define SENTINEL_COORD	(4 * GLU_TESS_MAX_COORD)
-
-static void AddSentinel( GLUtesselator *tess, GLdouble t )
-/*
- * We add two sentinel edges above and below all other edges,
- * to avoid special cases at the top and bottom.
- */
-{
-  GLUhalfEdge *e;
-  ActiveRegion *reg = (ActiveRegion *)memAlloc( sizeof( ActiveRegion ));
-  if (reg == NULL) longjmp(tess->env,1);
-
-  e = __gl_meshMakeEdge( tess->mesh );
-  if (e == NULL) longjmp(tess->env,1);
-
-  e->Org->s = SENTINEL_COORD;
-  e->Org->t = t;
-  e->Dst->s = -SENTINEL_COORD;
-  e->Dst->t = t;
-  tess->event = e->Dst;		/* initialize it */
-
-  reg->eUp = e;
-  reg->windingNumber = 0;
-  reg->inside = FALSE;
-  reg->fixUpperEdge = FALSE;
-  reg->sentinel = TRUE;
-  reg->dirty = FALSE;
-  reg->nodeUp = dictInsert( tess->dict, reg ); /* __gl_dictListInsertBefore */
-  if (reg->nodeUp == NULL) longjmp(tess->env,1);
-}
-
-
-static void InitEdgeDict( GLUtesselator *tess )
-/*
- * We maintain an ordering of edge intersections with the sweep line.
- * This order is maintained in a dynamic dictionary.
- */
-{
-  /* __gl_dictListNewDict */
-  tess->dict = dictNewDict( tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq );
-  if (tess->dict == NULL) longjmp(tess->env,1);
-
-  AddSentinel( tess, -SENTINEL_COORD );
-  AddSentinel( tess, SENTINEL_COORD );
-}
-
-
-static void DoneEdgeDict( GLUtesselator *tess )
-{
-  ActiveRegion *reg;
-#ifndef NDEBUG
-  int fixedEdges = 0;
-#endif
-
-  /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */
-  while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) {
-    /*
-     * At the end of all processing, the dictionary should contain
-     * only the two sentinel edges, plus at most one "fixable" edge
-     * created by ConnectRightVertex().
-     */
-    if( ! reg->sentinel ) {
-      assert( reg->fixUpperEdge );
-      assert( ++fixedEdges == 1 );
-    }
-    assert( reg->windingNumber == 0 );
-    DeleteRegion( tess, reg );
-/*    __gl_meshDelete( reg->eUp );*/
-  }
-  dictDeleteDict( tess->dict );	/* __gl_dictListDeleteDict */
-}
-
-
-static void RemoveDegenerateEdges( GLUtesselator *tess )
-/*
- * Remove zero-length edges, and contours with fewer than 3 vertices.
- */
-{
-  GLUhalfEdge *e, *eNext, *eLnext;
-  GLUhalfEdge *eHead = &tess->mesh->eHead;
-
-  /*LINTED*/
-  for( e = eHead->next; e != eHead; e = eNext ) {
-    eNext = e->next;
-    eLnext = e->Lnext;
-    
-    if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) {
-      /* Zero-length edge, contour has at least 3 edges */
-      
-      SpliceMergeVertices( tess, eLnext, e );	/* deletes e->Org */
-      if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); /* e is a self-loop */
-      e = eLnext;
-      eLnext = e->Lnext;
-    }
-    if( eLnext->Lnext == e ) {
-      /* Degenerate contour (one or two edges) */
-      
-      if( eLnext != e ) {
-	if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; }
-	if ( !__gl_meshDelete( eLnext ) ) longjmp(tess->env,1);
-      }
-      if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; }
-      if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1);
-    }
-  }
-}
-
-static int InitPriorityQ( GLUtesselator *tess )
-/*
- * Insert all vertices into the priority queue which determines the
- * order in which vertices cross the sweep line.
- */
-{
-  PriorityQ *pq;
-  GLUvertex *v, *vHead;
-
-  /* __gl_pqSortNewPriorityQ */
-  pq = tess->pq = pqNewPriorityQ( (int (*)(PQkey, PQkey)) __gl_vertLeq );
-  if (pq == NULL) return 0;
-
-  vHead = &tess->mesh->vHead;
-  for( v = vHead->next; v != vHead; v = v->next ) {
-    v->pqHandle = pqInsert( pq, v ); /* __gl_pqSortInsert */
-    if (v->pqHandle == LONG_MAX) break;
-  }
-  if (v != vHead || !pqInit( pq ) ) { /* __gl_pqSortInit */
-    pqDeletePriorityQ(tess->pq);	/* __gl_pqSortDeletePriorityQ */
-    tess->pq = NULL;
-    return 0;
-  }
-
-  return 1;
-}
-
-
-static void DonePriorityQ( GLUtesselator *tess )
-{
-  pqDeletePriorityQ( tess->pq ); /* __gl_pqSortDeletePriorityQ */
-}
-
-
-static int RemoveDegenerateFaces( GLUmesh *mesh )
-/*
- * Delete any degenerate faces with only two edges.  WalkDirtyRegions()
- * will catch almost all of these, but it won't catch degenerate faces
- * produced by splice operations on already-processed edges.
- * The two places this can happen are in FinishLeftRegions(), when
- * we splice in a "temporary" edge produced by ConnectRightVertex(),
- * and in CheckForLeftSplice(), where we splice already-processed
- * edges to ensure that our dictionary invariants are not violated
- * by numerical errors.
- *
- * In both these cases it is *very* dangerous to delete the offending
- * edge at the time, since one of the routines further up the stack
- * will sometimes be keeping a pointer to that edge.
- */
-{
-  GLUface *f, *fNext;
-  GLUhalfEdge *e;
-
-  /*LINTED*/
-  for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) {
-    fNext = f->next;
-    e = f->anEdge;
-    assert( e->Lnext != e );
-
-    if( e->Lnext->Lnext == e ) {
-      /* A face with only two edges */
-      AddWinding( e->Onext, e );
-      if ( !__gl_meshDelete( e ) ) return 0;
-    }
-  }
-  return 1;
-}
-
-int __gl_computeInterior( GLUtesselator *tess )
-/*
- * __gl_computeInterior( tess ) computes the planar arrangement specified
- * by the given contours, and further subdivides this arrangement
- * into regions.  Each region is marked "inside" if it belongs
- * to the polygon, according to the rule given by tess->windingRule.
- * Each interior region is guaranteed be monotone.
- */
-{
-  GLUvertex *v, *vNext;
-
-  tess->fatalError = FALSE;
-
-  /* Each vertex defines an event for our sweep line.  Start by inserting
-   * all the vertices in a priority queue.  Events are processed in
-   * lexicographic order, ie.
-   *
-   *	e1 < e2  iff  e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y)
-   */
-  RemoveDegenerateEdges( tess );
-  if ( !InitPriorityQ( tess ) ) return 0; /* if error */
-  InitEdgeDict( tess );
-
-  /* __gl_pqSortExtractMin */
-  while( (v = (GLUvertex *)pqExtractMin( tess->pq )) != NULL ) {
-    for( ;; ) {
-      vNext = (GLUvertex *)pqMinimum( tess->pq ); /* __gl_pqSortMinimum */
-      if( vNext == NULL || ! VertEq( vNext, v )) break;
-      
-      /* Merge together all vertices at exactly the same location.
-       * This is more efficient than processing them one at a time,
-       * simplifies the code (see ConnectLeftDegenerate), and is also
-       * important for correct handling of certain degenerate cases.
-       * For example, suppose there are two identical edges A and B
-       * that belong to different contours (so without this code they would
-       * be processed by separate sweep events).  Suppose another edge C
-       * crosses A and B from above.  When A is processed, we split it
-       * at its intersection point with C.  However this also splits C,
-       * so when we insert B we may compute a slightly different
-       * intersection point.  This might leave two edges with a small
-       * gap between them.  This kind of error is especially obvious
-       * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY).
-       */
-      vNext = (GLUvertex *)pqExtractMin( tess->pq ); /* __gl_pqSortExtractMin*/
-      SpliceMergeVertices( tess, v->anEdge, vNext->anEdge );
-    }
-    SweepEvent( tess, v );
-  }
-
-  /* Set tess->event for debugging purposes */
-  /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ 
-  tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org;
-  DebugEvent( tess );
-  DoneEdgeDict( tess );
-  DonePriorityQ( tess );
-
-  if ( !RemoveDegenerateFaces( tess->mesh ) ) return 0;
-  __gl_meshCheckMesh( tess->mesh );
-
-  return 1;
-}
diff --git a/third_party/glu/libtess/sweep.h b/third_party/glu/libtess/sweep.h
deleted file mode 100644
index 75d3e64..0000000
--- a/third_party/glu/libtess/sweep.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/sweep.h#5 $
-*/
-
-#ifndef __sweep_h_
-#define __sweep_h_
-
-#include "mesh.h"
-
-/* __gl_computeInterior( tess ) computes the planar arrangement specified
- * by the given contours, and further subdivides this arrangement
- * into regions.  Each region is marked "inside" if it belongs
- * to the polygon, according to the rule given by tess->windingRule.
- * Each interior region is guaranteed be monotone.
- */
-int __gl_computeInterior( GLUtesselator *tess );
-
-
-/* The following is here *only* for access by debugging routines */
-
-#include "dict.h"
-
-/* For each pair of adjacent edges crossing the sweep line, there is
- * an ActiveRegion to represent the region between them.  The active
- * regions are kept in sorted order in a dynamic dictionary.  As the
- * sweep line crosses each vertex, we update the affected regions.
- */
-
-struct ActiveRegion {
-  GLUhalfEdge	*eUp;		/* upper edge, directed right to left */
-  DictNode	*nodeUp;	/* dictionary node corresponding to eUp */
-  int		windingNumber;	/* used to determine which regions are
-                                 * inside the polygon */
-  GLboolean	inside;		/* is this region inside the polygon? */
-  GLboolean	sentinel;	/* marks fake edges at t = +/-infinity */
-  GLboolean	dirty;		/* marks regions where the upper or lower
-                                 * edge has changed, but we haven't checked
-                                 * whether they intersect yet */
-  GLboolean	fixUpperEdge;	/* marks temporary edges introduced when
-                                 * we process a "right vertex" (one without
-                                 * any edges leaving to the right) */
-};
-
-#define RegionBelow(r)	((ActiveRegion *) dictKey(dictPred((r)->nodeUp)))
-#define RegionAbove(r)	((ActiveRegion *) dictKey(dictSucc((r)->nodeUp)))
-
-#endif
diff --git a/third_party/glu/libtess/tess.c b/third_party/glu/libtess/tess.c
deleted file mode 100644
index c59a178..0000000
--- a/third_party/glu/libtess/tess.c
+++ /dev/null
@@ -1,638 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/tess.c#7 $
-*/
-
-#include "gluos.h"
-#include <stddef.h>
-#include <assert.h>
-#include <setjmp.h>
-#include "memalloc.h"
-#include "tess.h"
-#include "mesh.h"
-#include "normal.h"
-#include "sweep.h"
-#include "tessmono.h"
-#include "render.h"
-
-#define GLU_TESS_DEFAULT_TOLERANCE 0.0
-#define GLU_TESS_MESH		100112	/* void (*)(GLUmesh *mesh)	    */
-
-#define TRUE 1
-#define FALSE 0
-
-/*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {}
-/*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {}
-/*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {}
-/*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {}
-/*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {}
-/*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4],
-                                    GLfloat weight[4], void **dataOut ) {}
-/*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {}
-
-
-/*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type,
-					     void *polygonData ) {}
-/*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, 
-				       void *polygonData ) {}
-/*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data,
-					      void *polygonData ) {}
-/*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {}
-/*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum,
-					     void *polygonData ) {}
-/*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3],
-					       void *data[4],
-					       GLfloat weight[4],
-					       void **outData,
-					       void *polygonData ) {}
-
-/* Half-edges are allocated in pairs (see mesh.c) */
-typedef struct { GLUhalfEdge e, eSym; } EdgePair;
-
-#define MAX(a,b)	((a) > (b) ? (a) : (b))
-#define MAX_FAST_ALLOC	(MAX(sizeof(EdgePair), \
-			 MAX(sizeof(GLUvertex),sizeof(GLUface))))
-
-
-GLUtesselator * GLAPIENTRY
-gluNewTess( void )
-{
-  GLUtesselator *tess;
-
-  /* Only initialize fields which can be changed by the api.  Other fields
-   * are initialized where they are used.
-   */
-
-  if (memInit( MAX_FAST_ALLOC ) == 0) {
-     return 0;			/* out of memory */
-  }
-  tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator ));
-  if (tess == NULL) {
-     return 0;			/* out of memory */
-  }
-
-  tess->state = T_DORMANT;
-
-  tess->normal[0] = 0;
-  tess->normal[1] = 0;
-  tess->normal[2] = 0;
-
-  tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE;
-  tess->windingRule = GLU_TESS_WINDING_ODD;
-  tess->flagBoundary = FALSE;
-  tess->boundaryOnly = FALSE;
-
-  tess->callBegin = &noBegin;
-  tess->callEdgeFlag = &noEdgeFlag;
-  tess->callVertex = &noVertex;
-  tess->callEnd = &noEnd;
-
-  tess->callError = &noError;
-  tess->callCombine = &noCombine;
-  tess->callMesh = &noMesh;
-
-  tess->callBeginData= &__gl_noBeginData;
-  tess->callEdgeFlagData= &__gl_noEdgeFlagData;
-  tess->callVertexData= &__gl_noVertexData;
-  tess->callEndData= &__gl_noEndData;
-  tess->callErrorData= &__gl_noErrorData;
-  tess->callCombineData= &__gl_noCombineData;
-
-  tess->polygonData= NULL;
-
-  return tess;
-}
-
-static void MakeDormant( GLUtesselator *tess )
-{
-  /* Return the tessellator to its original dormant state. */
-
-  if( tess->mesh != NULL ) {
-    __gl_meshDeleteMesh( tess->mesh );
-  }
-  tess->state = T_DORMANT;
-  tess->lastEdge = NULL;
-  tess->mesh = NULL;
-}
-
-#define RequireState( tess, s )   if( tess->state != s ) GotoState(tess,s)
-
-static void GotoState( GLUtesselator *tess, enum TessState newState )
-{
-  while( tess->state != newState ) {
-    /* We change the current state one level at a time, to get to
-     * the desired state.
-     */
-    if( tess->state < newState ) {
-      switch( tess->state ) {
-      case T_DORMANT:
-	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON );
-	gluTessBeginPolygon( tess, NULL );
-	break;
-      case T_IN_POLYGON:
-	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR );
-	gluTessBeginContour( tess );
-	break;
-      default:
-        assert(0);
-        break;
-      }
-    } else {
-      switch( tess->state ) {
-      case T_IN_CONTOUR:
-	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR );
-	gluTessEndContour( tess );
-	break;
-      case T_IN_POLYGON:
-	CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON );
-	/* gluTessEndPolygon( tess ) is too much work! */
-	MakeDormant( tess );
-	break;
-      default:
-        assert(0);
-        break;
-      }
-    }
-  }
-}
-
-
-void GLAPIENTRY
-gluDeleteTess( GLUtesselator *tess )
-{
-  RequireState( tess, T_DORMANT );
-  memFree( tess );
-}
-
-
-void GLAPIENTRY
-gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value )
-{
-  GLenum windingRule;
-
-  switch( which ) {
-  case GLU_TESS_TOLERANCE:
-    if( value < 0.0 || value > 1.0 ) break;
-    tess->relTolerance = value;
-    return;
-
-  case GLU_TESS_WINDING_RULE:
-    windingRule = (GLenum) value;
-    if( windingRule != value ) break;	/* not an integer */
-
-    switch( windingRule ) {
-    case GLU_TESS_WINDING_ODD:
-    case GLU_TESS_WINDING_NONZERO:
-    case GLU_TESS_WINDING_POSITIVE:
-    case GLU_TESS_WINDING_NEGATIVE:
-    case GLU_TESS_WINDING_ABS_GEQ_TWO:
-      tess->windingRule = windingRule;
-      return;
-    default:
-      break;
-    }
-
-  case GLU_TESS_BOUNDARY_ONLY:
-    tess->boundaryOnly = (value != 0);
-    return;
-
-  default:
-    CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
-    return;
-  }
-  CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE );
-}
-
-/* Returns tessellator property */
-void GLAPIENTRY
-gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value )
-{
-   switch (which) {
-   case GLU_TESS_TOLERANCE:
-      /* tolerance should be in range [0..1] */
-      assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0);
-      *value= tess->relTolerance;
-      break;    
-   case GLU_TESS_WINDING_RULE:
-      assert(tess->windingRule == GLU_TESS_WINDING_ODD ||
-	     tess->windingRule == GLU_TESS_WINDING_NONZERO ||
-	     tess->windingRule == GLU_TESS_WINDING_POSITIVE ||
-	     tess->windingRule == GLU_TESS_WINDING_NEGATIVE ||
-	     tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO);
-      *value= tess->windingRule;
-      break;
-   case GLU_TESS_BOUNDARY_ONLY:
-      assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE);
-      *value= tess->boundaryOnly;
-      break;
-   default:
-      *value= 0.0;
-      CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
-      break;
-   }
-} /* gluGetTessProperty() */
-
-void GLAPIENTRY
-gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z )
-{
-  tess->normal[0] = x;
-  tess->normal[1] = y;
-  tess->normal[2] = z;
-}
-
-void GLAPIENTRY
-gluTessCallback( GLUtesselator *tess, GLenum which, void (GLAPIENTRY *fn)())
-{
-  switch( which ) {
-  case GLU_TESS_BEGIN:
-    tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn;
-    return;
-  case GLU_TESS_BEGIN_DATA:
-    tess->callBeginData = (fn == NULL) ?
-	&__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
-    return;
-  case GLU_TESS_EDGE_FLAG:
-    tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag :
-					(void (GLAPIENTRY *)(GLboolean)) fn;
-    /* If the client wants boundary edges to be flagged,
-     * we render everything as separate triangles (no strips or fans).
-     */
-    tess->flagBoundary = (fn != NULL);
-    return;
-  case GLU_TESS_EDGE_FLAG_DATA:
-    tess->callEdgeFlagData= (fn == NULL) ?
-	&__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn; 
-    /* If the client wants boundary edges to be flagged,
-     * we render everything as separate triangles (no strips or fans).
-     */
-    tess->flagBoundary = (fn != NULL);
-    return;
-  case GLU_TESS_VERTEX:
-    tess->callVertex = (fn == NULL) ? &noVertex :
-				      (void (GLAPIENTRY *)(void *)) fn;
-    return;
-  case GLU_TESS_VERTEX_DATA:
-    tess->callVertexData = (fn == NULL) ?
-	&__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn;
-    return;
-  case GLU_TESS_END:
-    tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn;
-    return;
-  case GLU_TESS_END_DATA:
-    tess->callEndData = (fn == NULL) ? &__gl_noEndData : 
-                                       (void (GLAPIENTRY *)(void *)) fn;
-    return;
-  case GLU_TESS_ERROR:
-    tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn;
-    return;
-  case GLU_TESS_ERROR_DATA:
-    tess->callErrorData = (fn == NULL) ?
-	&__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn;
-    return;
-  case GLU_TESS_COMBINE:
-    tess->callCombine = (fn == NULL) ? &noCombine :
-	(void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn;
-    return;
-  case GLU_TESS_COMBINE_DATA:
-    tess->callCombineData = (fn == NULL) ? &__gl_noCombineData :
-                                           (void (GLAPIENTRY *)(GLdouble [3],
-						     void *[4], 
-						     GLfloat [4], 
-						     void **,
-						     void *)) fn;
-    return;
-  case GLU_TESS_MESH:
-    tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn;
-    return;
-  default:
-    CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM );
-    return;
-  }
-}
-
-static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
-{
-  GLUhalfEdge *e;
-
-  e = tess->lastEdge;
-  if( e == NULL ) {
-    /* Make a self-loop (one vertex, one edge). */
-
-    e = __gl_meshMakeEdge( tess->mesh );
-    if (e == NULL) return 0;
-    if ( !__gl_meshSplice( e, e->Sym ) ) return 0;
-  } else {
-    /* Create a new vertex and edge which immediately follow e
-     * in the ordering around the left face.
-     */
-    if (__gl_meshSplitEdge( e ) == NULL) return 0;
-    e = e->Lnext;
-  }
-
-  /* The new vertex is now e->Org. */
-  e->Org->data = data;
-  e->Org->coords[0] = coords[0];
-  e->Org->coords[1] = coords[1];
-  e->Org->coords[2] = coords[2];
-  
-  /* The winding of an edge says how the winding number changes as we
-   * cross from the edge''s right face to its left face.  We add the
-   * vertices in such an order that a CCW contour will add +1 to
-   * the winding number of the region inside the contour.
-   */
-  e->winding = 1;
-  e->Sym->winding = -1;
-
-  tess->lastEdge = e;
-
-  return 1;
-}
-
-
-static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
-{
-  CachedVertex *v = &tess->cache[tess->cacheCount];
-
-  v->data = data;
-  v->coords[0] = coords[0];
-  v->coords[1] = coords[1];
-  v->coords[2] = coords[2];
-  ++tess->cacheCount;
-}
-
-
-static int EmptyCache( GLUtesselator *tess )
-{
-  CachedVertex *v = tess->cache;
-  CachedVertex *vLast;
-
-  tess->mesh = __gl_meshNewMesh();
-  if (tess->mesh == NULL) return 0;
-
-  for( vLast = v + tess->cacheCount; v < vLast; ++v ) {
-    if ( !AddVertex( tess, v->coords, v->data ) ) return 0;
-  }
-  tess->cacheCount = 0;
-  tess->emptyCache = FALSE;
-
-  return 1;
-}
-
-
-void GLAPIENTRY
-gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data )
-{
-  int i, tooLarge = FALSE;
-  GLdouble x, clamped[3];
-
-  RequireState( tess, T_IN_CONTOUR );
-
-  if( tess->emptyCache ) {
-    if ( !EmptyCache( tess ) ) {
-       CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
-       return;
-    }
-    tess->lastEdge = NULL;
-  }
-  for( i = 0; i < 3; ++i ) {
-    x = coords[i];
-    if( x < - GLU_TESS_MAX_COORD ) {
-      x = - GLU_TESS_MAX_COORD;
-      tooLarge = TRUE;
-    }
-    if( x > GLU_TESS_MAX_COORD ) {
-      x = GLU_TESS_MAX_COORD;
-      tooLarge = TRUE;
-    }
-    clamped[i] = x;
-  }
-  if( tooLarge ) {
-    CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE );
-  }
-
-  if( tess->mesh == NULL ) {
-    if( tess->cacheCount < TESS_MAX_CACHE ) {
-      CacheVertex( tess, clamped, data );
-      return;
-    }
-    if ( !EmptyCache( tess ) ) {
-       CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
-       return;
-    }
-  }
-  if ( !AddVertex( tess, clamped, data ) ) {
-       CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
-  }
-}
-
-
-void GLAPIENTRY
-gluTessBeginPolygon( GLUtesselator *tess, void *data )
-{
-  RequireState( tess, T_DORMANT );
-
-  tess->state = T_IN_POLYGON;
-  tess->cacheCount = 0;
-  tess->emptyCache = FALSE;
-  tess->mesh = NULL;
-
-  tess->polygonData= data;
-}
-
-
-void GLAPIENTRY
-gluTessBeginContour( GLUtesselator *tess )
-{
-  RequireState( tess, T_IN_POLYGON );
-
-  tess->state = T_IN_CONTOUR;
-  tess->lastEdge = NULL;
-  if( tess->cacheCount > 0 ) {
-    /* Just set a flag so we don't get confused by empty contours
-     * -- these can be generated accidentally with the obsolete
-     * NextContour() interface.
-     */
-    tess->emptyCache = TRUE;
-  }
-}
-
-
-void GLAPIENTRY
-gluTessEndContour( GLUtesselator *tess )
-{
-  RequireState( tess, T_IN_CONTOUR );
-  tess->state = T_IN_POLYGON;
-}
-
-void GLAPIENTRY
-gluTessEndPolygon( GLUtesselator *tess )
-{
-  GLUmesh *mesh;
-
-  if (setjmp(tess->env) != 0) {	
-     /* come back here if out of memory */
-     CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY );
-     return;
-  }
-
-  RequireState( tess, T_IN_POLYGON );
-  tess->state = T_DORMANT;
-
-  if( tess->mesh == NULL ) {
-    if( ! tess->flagBoundary && tess->callMesh == &noMesh ) {
-
-      /* Try some special code to make the easy cases go quickly
-       * (eg. convex polygons).  This code does NOT handle multiple contours,
-       * intersections, edge flags, and of course it does not generate
-       * an explicit mesh either.
-       */
-      if( __gl_renderCache( tess )) {
-	tess->polygonData= NULL; 
-	return;
-      }
-    }
-    if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/
-  }
-
-  /* Determine the polygon normal and project vertices onto the plane
-   * of the polygon.
-   */
-  __gl_projectPolygon( tess );
-
-  /* __gl_computeInterior( tess ) computes the planar arrangement specified
-   * by the given contours, and further subdivides this arrangement
-   * into regions.  Each region is marked "inside" if it belongs
-   * to the polygon, according to the rule given by tess->windingRule.
-   * Each interior region is guaranteed be monotone.
-   */
-  if ( !__gl_computeInterior( tess ) ) {
-     longjmp(tess->env,1);	/* could've used a label */
-  }
-
-  mesh = tess->mesh;
-  if( ! tess->fatalError ) {
-    int rc = 1;
-
-    /* If the user wants only the boundary contours, we throw away all edges
-     * except those which separate the interior from the exterior.
-     * Otherwise we tessellate all the regions marked "inside".
-     */
-    if( tess->boundaryOnly ) {
-      rc = __gl_meshSetWindingNumber( mesh, 1, TRUE );
-    } else {
-      rc = __gl_meshTessellateInterior( mesh ); 
-    }
-    if (rc == 0) longjmp(tess->env,1);	/* could've used a label */
-
-    __gl_meshCheckMesh( mesh );
-
-    if( tess->callBegin != &noBegin || tess->callEnd != &noEnd
-       || tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag 
-       || tess->callBeginData != &__gl_noBeginData 
-       || tess->callEndData != &__gl_noEndData
-       || tess->callVertexData != &__gl_noVertexData
-       || tess->callEdgeFlagData != &__gl_noEdgeFlagData )
-    {
-      if( tess->boundaryOnly ) {
-	__gl_renderBoundary( tess, mesh );  /* output boundary contours */
-      } else {
-	__gl_renderMesh( tess, mesh );	   /* output strips and fans */
-      }
-    }
-    if( tess->callMesh != &noMesh ) {
-
-      /* Throw away the exterior faces, so that all faces are interior.
-       * This way the user doesn't have to check the "inside" flag,
-       * and we don't need to even reveal its existence.  It also leaves
-       * the freedom for an implementation to not generate the exterior
-       * faces in the first place.
-       */
-      __gl_meshDiscardExterior( mesh );
-      (*tess->callMesh)( mesh );		/* user wants the mesh itself */
-      tess->mesh = NULL;
-      tess->polygonData= NULL;
-      return;
-    }
-  }
-  __gl_meshDeleteMesh( mesh );
-  tess->polygonData= NULL;
-  tess->mesh = NULL;
-}
-
-
-/*XXXblythe unused function*/
-#if 0
-void GLAPIENTRY
-gluDeleteMesh( GLUmesh *mesh )
-{
-  __gl_meshDeleteMesh( mesh );
-}
-#endif
-
-
-
-/*******************************************************/
-
-/* Obsolete calls -- for backward compatibility */
-
-#if 0
-void GLAPIENTRY
-gluBeginPolygon( GLUtesselator *tess )
-{
-  gluTessBeginPolygon( tess, NULL );
-  gluTessBeginContour( tess );
-}
-
-
-/*ARGSUSED*/
-void GLAPIENTRY
-gluNextContour( GLUtesselator *tess, GLenum type )
-{
-  gluTessEndContour( tess );
-  gluTessBeginContour( tess );
-}
-
-
-void GLAPIENTRY
-gluEndPolygon( GLUtesselator *tess )
-{
-  gluTessEndContour( tess );
-  gluTessEndPolygon( tess );
-}
-#endif
diff --git a/third_party/glu/libtess/tess.h b/third_party/glu/libtess/tess.h
deleted file mode 100644
index d8d3d21..0000000
--- a/third_party/glu/libtess/tess.h
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/tess.h#6 $
-*/
-
-#ifndef __tess_h_
-#define __tess_h_
-
-#include <sk_glu.h>
-#include <setjmp.h>
-#include "mesh.h"
-#include "dict.h"
-#include "priorityq.h"
-
-/* The begin/end calls must be properly nested.  We keep track of
- * the current state to enforce the ordering.
- */
-enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR };
-
-/* We cache vertex data for single-contour polygons so that we can
- * try a quick-and-dirty decomposition first.
- */
-#define TESS_MAX_CACHE	100
-
-typedef struct CachedVertex {
-  GLdouble	coords[3];
-  void		*data;
-} CachedVertex;
-
-struct GLUtesselator {
-
-  /*** state needed for collecting the input data ***/
-
-  enum TessState state;		/* what begin/end calls have we seen? */
-
-  GLUhalfEdge	*lastEdge;	/* lastEdge->Org is the most recent vertex */
-  GLUmesh	*mesh;		/* stores the input contours, and eventually
-                                   the tessellation itself */
-
-  void		(GLAPIENTRY *callError)( GLenum errnum );
-
-  /*** state needed for projecting onto the sweep plane ***/
-
-  GLdouble	normal[3];	/* user-specified normal (if provided) */
-  GLdouble	sUnit[3];	/* unit vector in s-direction (debugging) */
-  GLdouble	tUnit[3];	/* unit vector in t-direction (debugging) */
-
-  /*** state needed for the line sweep ***/
-
-  GLdouble	relTolerance;	/* tolerance for merging features */
-  GLenum	windingRule;	/* rule for determining polygon interior */
-  GLboolean	fatalError;	/* fatal error: needed combine callback */
-
-  Dict		*dict;		/* edge dictionary for sweep line */
-  PriorityQ	*pq;		/* priority queue of vertex events */
-  GLUvertex	*event;		/* current sweep event being processed */
-
-  void		(GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4],
-			        GLfloat weight[4], void **outData );
-
-  /*** state needed for rendering callbacks (see render.c) ***/
-
-  GLboolean	flagBoundary;	/* mark boundary edges (use EdgeFlag) */
-  GLboolean	boundaryOnly;	/* Extract contours, not triangles */
-  GLUface	*lonelyTriList;
-    /* list of triangles which could not be rendered as strips or fans */
-
-  void		(GLAPIENTRY *callBegin)( GLenum type );
-  void		(GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge );
-  void		(GLAPIENTRY *callVertex)( void *data );
-  void		(GLAPIENTRY *callEnd)( void );
-  void		(GLAPIENTRY *callMesh)( GLUmesh *mesh );
-
-
-  /*** state needed to cache single-contour polygons for renderCache() */
-
-  GLboolean	emptyCache;		/* empty cache on next vertex() call */
-  int		cacheCount;		/* number of cached vertices */
-  CachedVertex	cache[TESS_MAX_CACHE];	/* the vertex data */
-
-  /*** rendering callbacks that also pass polygon data  ***/ 
-  void		(GLAPIENTRY *callBeginData)( GLenum type, void *polygonData );
-  void		(GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge, 
-				     void *polygonData );
-  void		(GLAPIENTRY *callVertexData)( void *data, void *polygonData );
-  void		(GLAPIENTRY *callEndData)( void *polygonData );
-  void		(GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData );
-  void		(GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4],
-				    GLfloat weight[4], void **outData,
-				    void *polygonData );
-
-  jmp_buf env;			/* place to jump to when memAllocs fail */
-
-  void *polygonData;		/* client data for current polygon */
-};
-
-void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData );
-void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData );
-void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData );
-void GLAPIENTRY __gl_noEndData( void *polygonData );
-void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData );
-void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4],
-			 GLfloat weight[4], void **outData,
-			 void *polygonData );
-
-#define CALL_BEGIN_OR_BEGIN_DATA(a) \
-   if (tess->callBeginData != &__gl_noBeginData) \
-      (*tess->callBeginData)((a),tess->polygonData); \
-   else (*tess->callBegin)((a));
-
-#define CALL_VERTEX_OR_VERTEX_DATA(a) \
-   if (tess->callVertexData != &__gl_noVertexData) \
-      (*tess->callVertexData)((a),tess->polygonData); \
-   else (*tess->callVertex)((a));
-
-#define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \
-   if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \
-      (*tess->callEdgeFlagData)((a),tess->polygonData); \
-   else (*tess->callEdgeFlag)((a));
-
-#define CALL_END_OR_END_DATA() \
-   if (tess->callEndData != &__gl_noEndData) \
-      (*tess->callEndData)(tess->polygonData); \
-   else (*tess->callEnd)();
-
-#define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \
-   if (tess->callCombineData != &__gl_noCombineData) \
-      (*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \
-   else (*tess->callCombine)((a),(b),(c),(d));
-
-#define CALL_ERROR_OR_ERROR_DATA(a) \
-   if (tess->callErrorData != &__gl_noErrorData) \
-      (*tess->callErrorData)((a),tess->polygonData); \
-   else (*tess->callError)((a));
-
-#endif
diff --git a/third_party/glu/libtess/tessmono.c b/third_party/glu/libtess/tessmono.c
deleted file mode 100644
index 2e2cc79..0000000
--- a/third_party/glu/libtess/tessmono.c
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/tessmono.c#5 $
-*/
-
-#include "gluos.h"
-#include <stdlib.h>
-#include "geom.h"
-#include "mesh.h"
-#include "tessmono.h"
-#include <assert.h>
-
-#define AddWinding(eDst,eSrc)	(eDst->winding += eSrc->winding, \
-				 eDst->Sym->winding += eSrc->Sym->winding)
-
-/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region
- * (what else would it do??)  The region must consist of a single
- * loop of half-edges (see mesh.h) oriented CCW.  "Monotone" in this
- * case means that any vertical line intersects the interior of the
- * region in a single interval.  
- *
- * Tessellation consists of adding interior edges (actually pairs of
- * half-edges), to split the region into non-overlapping triangles.
- *
- * The basic idea is explained in Preparata and Shamos (which I don''t
- * have handy right now), although their implementation is more
- * complicated than this one.  The are two edge chains, an upper chain
- * and a lower chain.  We process all vertices from both chains in order,
- * from right to left.
- *
- * The algorithm ensures that the following invariant holds after each
- * vertex is processed: the untessellated region consists of two
- * chains, where one chain (say the upper) is a single edge, and
- * the other chain is concave.  The left vertex of the single edge
- * is always to the left of all vertices in the concave chain.
- *
- * Each step consists of adding the rightmost unprocessed vertex to one
- * of the two chains, and forming a fan of triangles from the rightmost
- * of two chain endpoints.  Determining whether we can add each triangle
- * to the fan is a simple orientation test.  By making the fan as large
- * as possible, we restore the invariant (check it yourself).
- */
-int __gl_meshTessellateMonoRegion( GLUface *face )
-{
-  GLUhalfEdge *up, *lo;
-
-  /* All edges are oriented CCW around the boundary of the region.
-   * First, find the half-edge whose origin vertex is rightmost.
-   * Since the sweep goes from left to right, face->anEdge should
-   * be close to the edge we want.
-   */
-  up = face->anEdge;
-  assert( up->Lnext != up && up->Lnext->Lnext != up );
-
-  for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev )
-    ;
-  for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext )
-    ;
-  lo = up->Lprev;
-
-  while( up->Lnext != lo ) {
-    if( VertLeq( up->Dst, lo->Org )) {
-      /* up->Dst is on the left.  It is safe to form triangles from lo->Org.
-       * The EdgeGoesLeft test guarantees progress even when some triangles
-       * are CW, given that the upper and lower chains are truly monotone.
-       */
-      while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext )
-	     || EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) {
-	GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo );
-	if (tempHalfEdge == NULL) return 0;
-	lo = tempHalfEdge->Sym;
-      }
-      lo = lo->Lprev;
-    } else {
-      /* lo->Org is on the left.  We can make CCW triangles from up->Dst. */
-      while( lo->Lnext != up && (EdgeGoesRight( up->Lprev )
-	     || EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) {
-	GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev );
-	if (tempHalfEdge == NULL) return 0;
-	up = tempHalfEdge->Sym;
-      }
-      up = up->Lnext;
-    }
-  }
-
-  /* Now lo->Org == up->Dst == the leftmost vertex.  The remaining region
-   * can be tessellated in a fan from this leftmost vertex.
-   */
-  assert( lo->Lnext != up );
-  while( lo->Lnext->Lnext != up ) {
-    GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo );
-    if (tempHalfEdge == NULL) return 0;
-    lo = tempHalfEdge->Sym;
-  }
-
-  return 1;
-}
-
-
-/* __gl_meshTessellateInterior( mesh ) tessellates each region of
- * the mesh which is marked "inside" the polygon.  Each such region
- * must be monotone.
- */
-int __gl_meshTessellateInterior( GLUmesh *mesh )
-{
-  GLUface *f, *next;
-
-  /*LINTED*/
-  for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
-    /* Make sure we don''t try to tessellate the new triangles. */
-    next = f->next;
-    if( f->inside ) {
-      if ( !__gl_meshTessellateMonoRegion( f ) ) return 0;
-    }
-  }
-
-  return 1;
-}
-
-
-/* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces
- * which are not marked "inside" the polygon.  Since further mesh operations
- * on NULL faces are not allowed, the main purpose is to clean up the
- * mesh so that exterior loops are not represented in the data structure.
- */
-void __gl_meshDiscardExterior( GLUmesh *mesh )
-{
-  GLUface *f, *next;
-
-  /*LINTED*/
-  for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) {
-    /* Since f will be destroyed, save its next pointer. */
-    next = f->next;
-    if( ! f->inside ) {
-      __gl_meshZapFace( f );
-    }
-  }
-}
-
-#define MARKED_FOR_DELETION	0x7fffffff
-
-/* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the
- * winding numbers on all edges so that regions marked "inside" the
- * polygon have a winding number of "value", and regions outside
- * have a winding number of 0.
- *
- * If keepOnlyBoundary is TRUE, it also deletes all edges which do not
- * separate an interior region from an exterior one.
- */
-int __gl_meshSetWindingNumber( GLUmesh *mesh, int value,
-			        GLboolean keepOnlyBoundary )
-{
-  GLUhalfEdge *e, *eNext;
-
-  for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) {
-    eNext = e->next;
-    if( e->Rface->inside != e->Lface->inside ) {
-
-      /* This is a boundary edge (one side is interior, one is exterior). */
-      e->winding = (e->Lface->inside) ? value : -value;
-    } else {
-
-      /* Both regions are interior, or both are exterior. */
-      if( ! keepOnlyBoundary ) {
-	e->winding = 0;
-      } else {
-	if ( !__gl_meshDelete( e ) ) return 0;
-      }
-    }
-  }
-  return 1;
-}
diff --git a/third_party/glu/libtess/tessmono.h b/third_party/glu/libtess/tessmono.h
deleted file mode 100644
index 6a71ee8..0000000
--- a/third_party/glu/libtess/tessmono.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
-** License Applicability. Except to the extent portions of this file are
-** made subject to an alternative license as permitted in the SGI Free
-** Software License B, Version 1.1 (the "License"), the contents of this
-** file are subject only to the provisions of the License. You may not use
-** this file except in compliance with the License. You may obtain a copy
-** of the License at Silicon Graphics, Inc., attn: Legal Services, 1600
-** Amphitheatre Parkway, Mountain View, CA 94043-1351, or at:
-** 
-** http://oss.sgi.com/projects/FreeB
-** 
-** Note that, as provided in the License, the Software is distributed on an
-** "AS IS" basis, with ALL EXPRESS AND IMPLIED WARRANTIES AND CONDITIONS
-** DISCLAIMED, INCLUDING, WITHOUT LIMITATION, ANY IMPLIED WARRANTIES AND
-** CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY, FITNESS FOR A
-** PARTICULAR PURPOSE, AND NON-INFRINGEMENT.
-** 
-** Original Code. The Original Code is: OpenGL Sample Implementation,
-** Version 1.2.1, released January 26, 2000, developed by Silicon Graphics,
-** Inc. The Original Code is Copyright (c) 1991-2000 Silicon Graphics, Inc.
-** Copyright in any portions created by third parties is as indicated
-** elsewhere herein. All Rights Reserved.
-** 
-** Additional Notice Provisions: The application programming interfaces
-** established by SGI in conjunction with the Original Code are The
-** OpenGL(R) Graphics System: A Specification (Version 1.2.1), released
-** April 1, 1999; The OpenGL(R) Graphics System Utility Library (Version
-** 1.3), released November 4, 1998; and OpenGL(R) Graphics with the X
-** Window System(R) (Version 1.3), released October 19, 1998. This software
-** was created using the OpenGL(R) version 1.2.1 Sample Implementation
-** published by SGI, but has not been independently verified as being
-** compliant with the OpenGL(R) version 1.2.1 Specification.
-**
-*/
-/*
-** Author: Eric Veach, July 1994.
-**
-** $Date$ $Revision$
-** $Header: //depot/main/gfx/lib/glu/libtess/tessmono.h#5 $
-*/
-
-#ifndef __tessmono_h_
-#define __tessmono_h_
-
-/* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region
- * (what else would it do??)  The region must consist of a single
- * loop of half-edges (see mesh.h) oriented CCW.  "Monotone" in this
- * case means that any vertical line intersects the interior of the
- * region in a single interval.  
- *
- * Tessellation consists of adding interior edges (actually pairs of
- * half-edges), to split the region into non-overlapping triangles.
- *
- * __gl_meshTessellateInterior( mesh ) tessellates each region of
- * the mesh which is marked "inside" the polygon.  Each such region
- * must be monotone.
- *
- * __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces
- * which are not marked "inside" the polygon.  Since further mesh operations
- * on NULL faces are not allowed, the main purpose is to clean up the
- * mesh so that exterior loops are not represented in the data structure.
- *
- * __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the
- * winding numbers on all edges so that regions marked "inside" the
- * polygon have a winding number of "value", and regions outside
- * have a winding number of 0.
- *
- * If keepOnlyBoundary is TRUE, it also deletes all edges which do not
- * separate an interior region from an exterior one.
- */
-
-int __gl_meshTessellateMonoRegion( GLUface *face );
-int __gl_meshTessellateInterior( GLUmesh *mesh );
-void __gl_meshDiscardExterior( GLUmesh *mesh );
-int __gl_meshSetWindingNumber( GLUmesh *mesh, int value,
-			        GLboolean keepOnlyBoundary );
-
-#endif
diff --git a/third_party/glu/sk_glu.h b/third_party/glu/sk_glu.h
deleted file mode 100644
index 7b2b261..0000000
--- a/third_party/glu/sk_glu.h
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * Copyright 2010, Google Inc.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are
- * met:
- *
- *     * Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- *     * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer
- * in the documentation and/or other materials provided with the
- * distribution.
- *     * Neither the name of Google Inc. nor the names of its
- * contributors may be used to endorse or promote products derived from
- * this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-// This header is a skeleton substitute for GL/glu.h which contains
-// only the definitions, constants and function declarations necessary
-// to compile the GLU tessellator.
-
-#ifndef SK_GLU_H_
-#define SK_GLU_H_
-
-#include "gluos.h"
-
-/* Primitives */
-#define GL_LINE_LOOP                            0x0002
-#define GL_TRIANGLES                            0x0004
-#define GL_TRIANGLE_STRIP                       0x0005
-#define GL_TRIANGLE_FAN                         0x0006
-
-/* ErrorCode */
-#define GLU_INVALID_ENUM                     100900
-#define GLU_INVALID_VALUE                    100901
-#define GLU_OUT_OF_MEMORY                    100902
-
-/* TessCallback */
-#define GLU_TESS_BEGIN                       100100
-#define GLU_BEGIN                            100100
-#define GLU_TESS_VERTEX                      100101
-#define GLU_VERTEX                           100101
-#define GLU_TESS_END                         100102
-#define GLU_END                              100102
-#define GLU_TESS_ERROR                       100103
-#define GLU_TESS_EDGE_FLAG                   100104
-#define GLU_EDGE_FLAG                        100104
-#define GLU_TESS_COMBINE                     100105
-#define GLU_TESS_BEGIN_DATA                  100106
-#define GLU_TESS_VERTEX_DATA                 100107
-#define GLU_TESS_END_DATA                    100108
-#define GLU_TESS_ERROR_DATA                  100109
-#define GLU_TESS_EDGE_FLAG_DATA              100110
-#define GLU_TESS_COMBINE_DATA                100111
-
-/* TessContour */
-#define GLU_CW                               100120
-#define GLU_CCW                              100121
-#define GLU_INTERIOR                         100122
-#define GLU_EXTERIOR                         100123
-#define GLU_UNKNOWN                          100124
-
-/* TessProperty */
-#define GLU_TESS_WINDING_RULE                100140
-#define GLU_TESS_BOUNDARY_ONLY               100141
-#define GLU_TESS_TOLERANCE                   100142
-
-/* TessError */
-#define GLU_TESS_ERROR1                      100151
-#define GLU_TESS_ERROR2                      100152
-#define GLU_TESS_ERROR3                      100153
-#define GLU_TESS_ERROR4                      100154
-#define GLU_TESS_ERROR5                      100155
-#define GLU_TESS_ERROR6                      100156
-#define GLU_TESS_ERROR7                      100157
-#define GLU_TESS_ERROR8                      100158
-#define GLU_TESS_MISSING_BEGIN_POLYGON       100151
-#define GLU_TESS_MISSING_BEGIN_CONTOUR       100152
-#define GLU_TESS_MISSING_END_POLYGON         100153
-#define GLU_TESS_MISSING_END_CONTOUR         100154
-#define GLU_TESS_COORD_TOO_LARGE             100155
-#define GLU_TESS_NEED_COMBINE_CALLBACK       100156
-
-/* TessWinding */
-#define GLU_TESS_WINDING_ODD                 100130
-#define GLU_TESS_WINDING_NONZERO             100131
-#define GLU_TESS_WINDING_POSITIVE            100132
-#define GLU_TESS_WINDING_NEGATIVE            100133
-#define GLU_TESS_WINDING_ABS_GEQ_TWO         100134
-
-#define GLU_TESS_MAX_COORD 1.0e150
-
-typedef struct GLUtesselator GLUtesselator;
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-extern GLUtesselator * GLAPIENTRY Sk_gluNewTess(void);
-extern void GLAPIENTRY Sk_gluDeleteTess(GLUtesselator *tess);
-extern void GLAPIENTRY Sk_gluTessProperty(GLUtesselator *tess,
-                                                GLenum which,
-                                                GLdouble value);
-extern void GLAPIENTRY Sk_gluGetTessProperty(GLUtesselator *tess,
-                                                   GLenum which,
-                                                   GLdouble *value);
-extern void GLAPIENTRY Sk_gluTessNormal(GLUtesselator *tess,
-                                              GLdouble x,
-                                              GLdouble y,
-                                              GLdouble z);
-extern void GLAPIENTRY Sk_gluTessCallback(GLUtesselator *tess,
-                                                GLenum which,
-                                                void (GLAPIENTRY *fn)());
-extern void GLAPIENTRY Sk_gluTessVertex(GLUtesselator *tess,
-                                              GLdouble coords[3],
-                                              void *data);
-extern void GLAPIENTRY Sk_gluTessBeginPolygon(GLUtesselator *tess,
-                                                    void *data);
-extern void GLAPIENTRY Sk_gluTessBeginContour(GLUtesselator *tess);
-extern void GLAPIENTRY Sk_gluTessEndContour(GLUtesselator *tess);
-extern void GLAPIENTRY Sk_gluTessEndPolygon(GLUtesselator *tess);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif  // SK_GLU_H_
diff --git a/tools/CopyTilesRenderer.cpp b/tools/CopyTilesRenderer.cpp
new file mode 100644
index 0000000..d3f266b
--- /dev/null
+++ b/tools/CopyTilesRenderer.cpp
@@ -0,0 +1,90 @@
+/*
+ * 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 "picture_utils.h"
+#include "CopyTilesRenderer.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkImageEncoder.h"
+#include "SkPicture.h"
+#include "SkPixelRef.h"
+#include "SkRect.h"
+#include "SkString.h"
+
+namespace sk_tools {
+    CopyTilesRenderer::CopyTilesRenderer(int x, int y)
+    : fXTilesPerLargeTile(x)
+    , fYTilesPerLargeTile(y) {
+    }
+    void CopyTilesRenderer::init(SkPicture* pict) {
+        SkASSERT(pict != NULL);
+        // Only work with absolute widths (as opposed to percentages).
+        SkASSERT(this->getTileWidth() != 0 && this->getTileHeight() != 0);
+        fPicture = pict;
+        fPicture->ref();
+        this->buildBBoxHierarchy();
+        // In order to avoid allocating a large canvas (particularly important for GPU), create one
+        // canvas that is a multiple of the tile size, and draw portions of the picture.
+        fLargeTileWidth = fXTilesPerLargeTile * this->getTileWidth();
+        fLargeTileHeight = fYTilesPerLargeTile * this->getTileHeight();
+        fCanvas.reset(this->INHERITED::setupCanvas(fLargeTileWidth, fLargeTileHeight));
+    }
+
+    bool CopyTilesRenderer::render(const SkString* path, SkBitmap** out) {
+        int i = 0;
+        bool success = true;
+        SkBitmap dst;
+        for (int x = 0; x < this->getViewWidth(); x += fLargeTileWidth) {
+            for (int y = 0; y < this->getViewHeight(); y += fLargeTileHeight) {
+                SkAutoCanvasRestore autoRestore(fCanvas, true);
+                // Translate so that we draw the correct portion of the picture.
+                // Perform a postTranslate so that the scaleFactor does not interfere with the
+                // positioning.
+                SkMatrix mat(fCanvas->getTotalMatrix());
+                mat.postTranslate(SkIntToScalar(-x), SkIntToScalar(-y));
+                fCanvas->setMatrix(mat);
+                // Draw the picture
+                fCanvas->drawPicture(*fPicture);
+                // Now extract the picture into tiles
+                const SkBitmap& baseBitmap = fCanvas->getDevice()->accessBitmap(false);
+                SkIRect subset;
+                for (int tileY = 0; tileY < fLargeTileHeight; tileY += this->getTileHeight()) {
+                    for (int tileX = 0; tileX < fLargeTileWidth; tileX += this->getTileWidth()) {
+                        subset.set(tileX, tileY, tileX + this->getTileWidth(),
+                                   tileY + this->getTileHeight());
+                        SkDEBUGCODE(bool extracted =)
+                        baseBitmap.extractSubset(&dst, subset);
+                        SkASSERT(extracted);
+                        if (path != NULL) {
+                            // Similar to writeAppendNumber in PictureRenderer.cpp, but just encodes
+                            // a bitmap directly.
+                            SkString pathWithNumber(*path);
+                            pathWithNumber.appendf("%i.png", i++);
+                            SkBitmap copy;
+#if SK_SUPPORT_GPU
+                            if (isUsingGpuDevice()) {
+                                dst.pixelRef()->readPixels(&copy, &subset);
+                            } else {
+#endif
+                                dst.copyTo(&copy, dst.config());
+#if SK_SUPPORT_GPU
+                            }
+#endif
+                            success &= SkImageEncoder::EncodeFile(pathWithNumber.c_str(), copy,
+                                                                  SkImageEncoder::kPNG_Type, 100);
+                        }
+                    }
+                }
+            }
+        }
+        return success;
+    }
+
+    SkString CopyTilesRenderer::getConfigNameInternal() {
+        return SkString("copy_tiles");
+    }
+}
diff --git a/tools/CopyTilesRenderer.h b/tools/CopyTilesRenderer.h
new file mode 100644
index 0000000..5546604
--- /dev/null
+++ b/tools/CopyTilesRenderer.h
@@ -0,0 +1,46 @@
+/*
+ * 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 CopyTilesRenderer_DEFINED
+#define CopyTilesRenderer_DEFINED
+
+#include "PictureRenderer.h"
+#include "SkTypes.h"
+
+class SkPicture;
+class SkString;
+
+namespace sk_tools {
+    /**
+     *  PictureRenderer that draws the picture and then extracts it into tiles. For large pictures,
+     *  it will divide the picture into large tiles and draw the picture once for each large tile.
+     */
+    class CopyTilesRenderer : public TiledPictureRenderer {
+
+    public:
+        CopyTilesRenderer(int x, int y);
+        virtual void init(SkPicture* pict) SK_OVERRIDE;
+
+        /**
+         *  Similar to TiledPictureRenderer, this will draw a PNG for each tile. However, the
+         *  numbering (and actual tiles) will be different.
+         */
+        virtual bool render(const SkString* path, SkBitmap** out) SK_OVERRIDE;
+
+    private:
+        int fXTilesPerLargeTile;
+        int fYTilesPerLargeTile;
+
+        int fLargeTileWidth;
+        int fLargeTileHeight;
+
+        virtual SkString getConfigNameInternal() SK_OVERRIDE;
+
+        typedef TiledPictureRenderer INHERITED;
+    };
+} // sk_tools
+#endif // CopyTilesRenderer_DEFINED
diff --git a/tools/PdfRenderer.cpp b/tools/PdfRenderer.cpp
new file mode 100644
index 0000000..9a4bd38
--- /dev/null
+++ b/tools/PdfRenderer.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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 "PdfRenderer.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkPDFDevice.h"
+#include "SkPDFDocument.h"
+
+namespace sk_tools {
+
+void PdfRenderer::init(SkPicture* pict) {
+    SkASSERT(NULL == fPicture);
+    SkASSERT(NULL == fCanvas.get());
+    if (fPicture != NULL || NULL != fCanvas.get()) {
+        return;
+    }
+
+    SkASSERT(pict != NULL);
+    if (NULL == pict) {
+        return;
+    }
+
+    fPicture = pict;
+    fCanvas.reset(this->setupCanvas());
+}
+
+SkCanvas* PdfRenderer::setupCanvas() {
+    return this->setupCanvas(fPicture->width(), fPicture->height());
+}
+
+SkCanvas* PdfRenderer::setupCanvas(int width, int height) {
+    SkISize pageSize = SkISize::Make(width, height);
+    fPDFDevice = SkNEW_ARGS(SkPDFDevice, (pageSize, pageSize, SkMatrix::I()));
+    return SkNEW_ARGS(SkCanvas, (fPDFDevice));
+}
+
+void PdfRenderer::end() {
+    fPicture = NULL;
+    fCanvas.reset(NULL);
+    if (fPDFDevice) {
+        SkDELETE(fPDFDevice);
+        fPDFDevice = NULL;
+    }
+}
+
+void PdfRenderer::write(SkWStream* stream) const {
+    SkPDFDocument doc;
+    doc.appendPage(fPDFDevice);
+    doc.emitPDF(stream);
+}
+
+void SimplePdfRenderer::render() {
+    SkASSERT(fCanvas.get() != NULL);
+    SkASSERT(fPicture != NULL);
+    if (NULL == fCanvas.get() || NULL == fPicture) {
+        return;
+    }
+
+    fCanvas->drawPicture(*fPicture);
+    fCanvas->flush();
+}
+
+}
diff --git a/tools/PdfRenderer.h b/tools/PdfRenderer.h
new file mode 100644
index 0000000..3524a9d
--- /dev/null
+++ b/tools/PdfRenderer.h
@@ -0,0 +1,67 @@
+/*
+ * 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 PdfRenderer_DEFINED
+#define PdfRenderer_DEFINED
+
+//
+// PdfRender takes a SkPicture and writes it to a PDF file.
+// An SkPicture can be built manually, or read from an SKP file.
+//
+
+#include "SkMath.h"
+#include "SkPicture.h"
+#include "SkTypes.h"
+#include "SkTDArray.h"
+#include "SkRefCnt.h"
+#include "SkString.h"
+
+class SkBitmap;
+class SkCanvas;
+class SkGLContext;
+class SkPDFDevice;
+
+namespace sk_tools {
+
+class PdfRenderer : public SkRefCnt {
+public:
+    virtual void init(SkPicture* pict);
+    virtual void setup() {}
+    virtual void render() = 0;
+    virtual void end();
+
+    PdfRenderer()
+        : fPicture(NULL)
+        , fPDFDevice(NULL)
+        {}
+
+    void write(SkWStream* stream) const;
+
+protected:
+    SkCanvas* setupCanvas();
+    SkCanvas* setupCanvas(int width, int height);
+
+    SkAutoTUnref<SkCanvas> fCanvas;
+    SkPicture* fPicture;
+    SkPDFDevice* fPDFDevice;
+
+
+private:
+    typedef SkRefCnt INHERITED;
+};
+
+class SimplePdfRenderer : public PdfRenderer {
+public:
+    virtual void render() SK_OVERRIDE;
+
+private:
+    typedef PdfRenderer INHERITED;
+};
+
+}
+
+#endif  // PdfRenderer_DEFINED
diff --git a/tools/PictureBenchmark.cpp b/tools/PictureBenchmark.cpp
new file mode 100644
index 0000000..f1be2aa
--- /dev/null
+++ b/tools/PictureBenchmark.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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 "SkBenchLogger.h"
+#include "BenchTimer.h"
+#include "PictureBenchmark.h"
+#include "SkCanvas.h"
+#include "SkPicture.h"
+#include "SkString.h"
+#include "picture_utils.h"
+#include "TimerData.h"
+
+namespace sk_tools {
+
+PictureBenchmark::PictureBenchmark()
+: fRepeats(1)
+, fLogger(NULL)
+, fRenderer(NULL)
+, fLogPerIter(false)
+, fPrintMin(false)
+, fShowWallTime(false)
+, fShowTruncatedWallTime(false)
+, fShowCpuTime(true)
+, fShowTruncatedCpuTime(false)
+, fShowGpuTime(false)
+, fTimeIndividualTiles(false)
+{}
+
+PictureBenchmark::~PictureBenchmark() {
+    SkSafeUnref(fRenderer);
+}
+
+BenchTimer* PictureBenchmark::setupTimer() {
+#if SK_SUPPORT_GPU
+    if (fRenderer != NULL && fRenderer->isUsingGpuDevice()) {
+        return SkNEW_ARGS(BenchTimer, (fRenderer->getGLContext()));
+    }
+#endif
+    return SkNEW_ARGS(BenchTimer, (NULL));
+}
+
+void PictureBenchmark::logProgress(const char msg[]) {
+    if (fLogger != NULL) {
+        fLogger->logProgress(msg);
+    }
+}
+
+PictureRenderer* PictureBenchmark::setRenderer(sk_tools::PictureRenderer* renderer) {
+    SkRefCnt_SafeAssign(fRenderer, renderer);
+    return renderer;
+}
+
+void PictureBenchmark::run(SkPicture* pict) {
+    SkASSERT(pict);
+    if (NULL == pict) {
+        return;
+    }
+
+    SkASSERT(fRenderer != NULL);
+    if (NULL == fRenderer) {
+        return;
+    }
+
+    fRenderer->init(pict);
+
+    // We throw this away to remove first time effects (such as paging in this program)
+    fRenderer->setup();
+    fRenderer->render(NULL);
+    fRenderer->resetState(true);
+
+    bool usingGpu = false;
+#if SK_SUPPORT_GPU
+    usingGpu = fRenderer->isUsingGpuDevice();
+#endif
+
+    if (fTimeIndividualTiles) {
+        TiledPictureRenderer* tiledRenderer = fRenderer->getTiledRenderer();
+        SkASSERT(tiledRenderer);
+        if (NULL == tiledRenderer) {
+            return;
+        }
+        int xTiles, yTiles;
+        if (!tiledRenderer->tileDimensions(xTiles, yTiles)) {
+            return;
+        }
+
+        // Insert a newline so that each tile is reported on its own line (separate from the line
+        // that describes the skp being run).
+        this->logProgress("\n");
+
+        int x, y;
+        while (tiledRenderer->nextTile(x, y)) {
+            // There are two timers, which will behave slightly differently:
+            // 1) longRunningTimer, along with perTileTimerData, will time how long it takes to draw
+            // one tile fRepeats times, and take the average. As such, it will not respect the
+            // logPerIter or printMin options, since it does not know the time per iteration. It
+            // will also be unable to call flush() for each tile.
+            // The goal of this timer is to make up for a system timer that is not precise enough to
+            // measure the small amount of time it takes to draw one tile once.
+            //
+            // 2) perTileTimer, along with perTileTimerData, will record each run separately, and
+            // then take the average. As such, it supports logPerIter and printMin options.
+            SkAutoTDelete<BenchTimer> longRunningTimer(this->setupTimer());
+            TimerData longRunningTimerData(tiledRenderer->getPerIterTimeFormat(),
+                                           tiledRenderer->getNormalTimeFormat());
+            SkAutoTDelete<BenchTimer> perTileTimer(this->setupTimer());
+            TimerData perTileTimerData(tiledRenderer->getPerIterTimeFormat(),
+                                       tiledRenderer->getNormalTimeFormat());
+            longRunningTimer->start();
+            for (int i = 0; i < fRepeats; ++i) {
+                perTileTimer->start();
+                tiledRenderer->drawCurrentTile();
+                perTileTimer->truncatedEnd();
+                tiledRenderer->resetState(false);
+                perTileTimer->end();
+                perTileTimerData.appendTimes(perTileTimer.get(), fRepeats - 1 == i);
+            }
+            longRunningTimer->truncatedEnd();
+            tiledRenderer->resetState(true);
+            longRunningTimer->end();
+            longRunningTimerData.appendTimes(longRunningTimer.get(), true);
+
+            SkString configName = tiledRenderer->getConfigName();
+            configName.appendf(": tile [%i,%i] out of [%i,%i]", x, y, xTiles, yTiles);
+            SkString result = perTileTimerData.getResult(fLogPerIter, fPrintMin, fRepeats,
+                                                         configName.c_str(), fShowWallTime,
+                                                         fShowTruncatedWallTime, fShowCpuTime,
+                                                         fShowTruncatedCpuTime,
+                                                         usingGpu && fShowGpuTime);
+            result.append("\n");
+            this->logProgress(result.c_str());
+
+            configName.append(" <averaged>");
+            SkString longRunningResult = longRunningTimerData.getResult(false, false, fRepeats,
+                    configName.c_str(), fShowWallTime, fShowTruncatedWallTime,
+                    fShowCpuTime, fShowTruncatedCpuTime, usingGpu && fShowGpuTime);
+            longRunningResult.append("\n");
+            this->logProgress(longRunningResult.c_str());
+        }
+    } else {
+        SkAutoTDelete<BenchTimer> timer(this->setupTimer());
+        TimerData timerData(fRenderer->getPerIterTimeFormat(), fRenderer->getNormalTimeFormat());
+        for (int i = 0; i < fRepeats; ++i) {
+            fRenderer->setup();
+
+            timer->start();
+            fRenderer->render(NULL);
+            timer->truncatedEnd();
+
+            // Finishes gl context
+            fRenderer->resetState(true);
+            timer->end();
+
+            timerData.appendTimes(timer.get(), fRepeats - 1 == i);
+        }
+
+        SkString configName = fRenderer->getConfigName();
+        SkString result = timerData.getResult(fLogPerIter, fPrintMin, fRepeats,
+                                              configName.c_str(), fShowWallTime,
+                                              fShowTruncatedWallTime, fShowCpuTime,
+                                              fShowTruncatedCpuTime, usingGpu && fShowGpuTime);
+        result.append("\n");
+        this->logProgress(result.c_str());
+    }
+
+    fRenderer->end();
+}
+
+}
diff --git a/tools/PictureBenchmark.h b/tools/PictureBenchmark.h
new file mode 100644
index 0000000..af81f69
--- /dev/null
+++ b/tools/PictureBenchmark.h
@@ -0,0 +1,88 @@
+/*
+ * 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 PictureBenchmark_DEFINED
+#define PictureBenchmark_DEFINED
+
+#include "SkTypes.h"
+#include "PictureRenderer.h"
+
+class BenchTimer;
+class SkBenchLogger;
+class SkPicture;
+class SkString;
+
+namespace sk_tools {
+
+class PictureBenchmark {
+public:
+    PictureBenchmark();
+
+    ~PictureBenchmark();
+
+    /**
+     * Draw the provided SkPicture fRepeats times while collecting timing data, and log the output
+     * via fLogger.
+     */
+    void run(SkPicture* pict);
+
+    void setRepeats(int repeats) {
+        fRepeats = repeats;
+    }
+
+    /**
+     * If true, tells run to log separate timing data for each individual tile. Each tile will be
+     * drawn fRepeats times. Requires the PictureRenderer set by setRenderer to be a
+     * TiledPictureRenderer.
+     */
+    void setTimeIndividualTiles(bool indiv) { fTimeIndividualTiles = true; }
+
+    bool timeIndividualTiles() { return fTimeIndividualTiles; }
+
+    PictureRenderer* setRenderer(PictureRenderer*);
+
+    void setDeviceType(PictureRenderer::SkDeviceTypes deviceType) {
+        if (fRenderer != NULL) {
+            fRenderer->setDeviceType(deviceType);
+        }
+    }
+
+    void setLogPerIter(bool log) { fLogPerIter = log; }
+
+    void setPrintMin(bool min) { fPrintMin = min; }
+
+    void setTimersToShow(bool wall, bool truncatedWall, bool cpu, bool truncatedCpu, bool gpu) {
+        fShowWallTime = wall;
+        fShowTruncatedWallTime = truncatedWall;
+        fShowCpuTime = cpu;
+        fShowTruncatedCpuTime = truncatedCpu;
+        fShowGpuTime = gpu;
+    }
+
+    void setLogger(SkBenchLogger* logger) { fLogger = logger; }
+
+private:
+    int              fRepeats;
+    SkBenchLogger*   fLogger;
+    PictureRenderer* fRenderer;
+    bool             fLogPerIter;
+    bool             fPrintMin;
+    bool             fShowWallTime;
+    bool             fShowTruncatedWallTime;
+    bool             fShowCpuTime;
+    bool             fShowTruncatedCpuTime;
+    bool             fShowGpuTime;
+    bool             fTimeIndividualTiles;
+
+    void logProgress(const char msg[]);
+
+    BenchTimer* setupTimer();
+};
+
+}
+
+#endif  // PictureBenchmark_DEFINED
diff --git a/tools/PictureRenderer.cpp b/tools/PictureRenderer.cpp
new file mode 100644
index 0000000..cb2c3a4
--- /dev/null
+++ b/tools/PictureRenderer.cpp
@@ -0,0 +1,836 @@
+/*
+ * 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 "PictureRenderer.h"
+#include "picture_utils.h"
+#include "SamplePipeControllers.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGPipe.h"
+#if SK_SUPPORT_GPU
+#include "SkGpuDevice.h"
+#endif
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkMaskFilter.h"
+#include "SkMatrix.h"
+#include "SkPicture.h"
+#include "SkRTree.h"
+#include "SkScalar.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTemplates.h"
+#include "SkTileGridPicture.h"
+#include "SkTDArray.h"
+#include "SkThreadUtils.h"
+#include "SkTypes.h"
+#include "SkData.h"
+#include "SkPictureUtils.h"
+
+namespace sk_tools {
+
+enum {
+    kDefaultTileWidth = 256,
+    kDefaultTileHeight = 256
+};
+
+void PictureRenderer::init(SkPicture* pict) {
+    SkASSERT(NULL == fPicture);
+    SkASSERT(NULL == fCanvas.get());
+    if (fPicture != NULL || NULL != fCanvas.get()) {
+        return;
+    }
+
+    SkASSERT(pict != NULL);
+    if (NULL == pict) {
+        return;
+    }
+
+    fPicture = pict;
+    fPicture->ref();
+    fCanvas.reset(this->setupCanvas());
+}
+
+class FlagsDrawFilter : public SkDrawFilter {
+public:
+    FlagsDrawFilter(PictureRenderer::DrawFilterFlags* flags) :
+        fFlags(flags) {}
+
+    virtual bool filter(SkPaint* paint, Type t) {
+        paint->setFlags(paint->getFlags() & ~fFlags[t] & SkPaint::kAllFlags);
+        if (PictureRenderer::kBlur_DrawFilterFlag & fFlags[t]) {
+            SkMaskFilter* maskFilter = paint->getMaskFilter();
+            SkMaskFilter::BlurInfo blurInfo;
+            if (maskFilter && maskFilter->asABlur(&blurInfo)) {
+                paint->setMaskFilter(NULL);
+            }
+        }
+        if (PictureRenderer::kHinting_DrawFilterFlag & fFlags[t]) {
+            paint->setHinting(SkPaint::kNo_Hinting);
+        } else if (PictureRenderer::kSlightHinting_DrawFilterFlag & fFlags[t]) {
+            paint->setHinting(SkPaint::kSlight_Hinting);
+        }
+        return true;
+    }
+
+private:
+    PictureRenderer::DrawFilterFlags* fFlags;
+};
+
+static void setUpFilter(SkCanvas* canvas, PictureRenderer::DrawFilterFlags* drawFilters) {
+    if (drawFilters && !canvas->getDrawFilter()) {
+        canvas->setDrawFilter(SkNEW_ARGS(FlagsDrawFilter, (drawFilters)))->unref();
+        if (drawFilters[0] & PictureRenderer::kAAClip_DrawFilterFlag) {
+            canvas->setAllowSoftClip(false);
+        }
+    }
+}
+
+SkCanvas* PictureRenderer::setupCanvas() {
+    const int width = this->getViewWidth();
+    const int height = this->getViewHeight();
+    return this->setupCanvas(width, height);
+}
+
+SkCanvas* PictureRenderer::setupCanvas(int width, int height) {
+    SkCanvas* canvas;
+    switch(fDeviceType) {
+        case kBitmap_DeviceType: {
+            SkBitmap bitmap;
+            sk_tools::setup_bitmap(&bitmap, width, height);
+            canvas = SkNEW_ARGS(SkCanvas, (bitmap));
+        }
+        break;
+#if SK_SUPPORT_GPU
+        case kGPU_DeviceType: {
+            SkAutoTUnref<SkGpuDevice> device(SkNEW_ARGS(SkGpuDevice,
+                                                    (fGrContext, SkBitmap::kARGB_8888_Config,
+                                                    width, height)));
+            canvas = SkNEW_ARGS(SkCanvas, (device.get()));
+        }
+        break;
+#endif
+        default:
+            SkASSERT(0);
+            return NULL;
+    }
+    setUpFilter(canvas, fDrawFilters);
+    this->scaleToScaleFactor(canvas);
+    return canvas;
+}
+
+void PictureRenderer::scaleToScaleFactor(SkCanvas* canvas) {
+    SkASSERT(canvas != NULL);
+    if (fScaleFactor != SK_Scalar1) {
+        canvas->scale(fScaleFactor, fScaleFactor);
+    }
+}
+
+void PictureRenderer::end() {
+    this->resetState(true);
+    SkSafeUnref(fPicture);
+    fPicture = NULL;
+    fCanvas.reset(NULL);
+}
+
+int PictureRenderer::getViewWidth() {
+    SkASSERT(fPicture != NULL);
+    int width = fPicture->width();
+    if (fViewport.width() > 0) {
+        width = SkMin32(width, fViewport.width());
+    }
+    return width;
+}
+
+int PictureRenderer::getViewHeight() {
+    SkASSERT(fPicture != NULL);
+    int height = fPicture->height();
+    if (fViewport.height() > 0) {
+        height = SkMin32(height, fViewport.height());
+    }
+    return height;
+}
+
+/** Converts fPicture to a picture that uses a BBoxHierarchy.
+ *  PictureRenderer subclasses that are used to test picture playback
+ *  should call this method during init.
+ */
+void PictureRenderer::buildBBoxHierarchy() {
+    SkASSERT(NULL != fPicture);
+    if (kNone_BBoxHierarchyType != fBBoxHierarchyType && NULL != fPicture) {
+        SkPicture* newPicture = this->createPicture();
+        SkCanvas* recorder = newPicture->beginRecording(fPicture->width(), fPicture->height(),
+                                                        this->recordFlags());
+        fPicture->draw(recorder);
+        newPicture->endRecording();
+        fPicture->unref();
+        fPicture = newPicture;
+    }
+}
+
+void PictureRenderer::resetState(bool callFinish) {
+#if SK_SUPPORT_GPU
+    if (this->isUsingGpuDevice()) {
+        SkGLContext* glContext = fGrContextFactory.getGLContext(
+            GrContextFactory::kNative_GLContextType);
+
+        SkASSERT(glContext != NULL);
+        if (NULL == glContext) {
+            return;
+        }
+
+        fGrContext->flush();
+        if (callFinish) {
+            SK_GL(*glContext, Finish());
+        }
+    }
+#endif
+}
+
+uint32_t PictureRenderer::recordFlags() {
+    return ((kNone_BBoxHierarchyType == fBBoxHierarchyType) ? 0 :
+        SkPicture::kOptimizeForClippedPlayback_RecordingFlag) |
+        SkPicture::kUsePathBoundsForClip_RecordingFlag;
+}
+
+/**
+ * Write the canvas to the specified path.
+ * @param canvas Must be non-null. Canvas to be written to a file.
+ * @param path Path for the file to be written. Should have no extension; write() will append
+ *             an appropriate one. Passed in by value so it can be modified.
+ * @return bool True if the Canvas is written to a file.
+ */
+static bool write(SkCanvas* canvas, SkString path) {
+    SkASSERT(canvas != NULL);
+    if (NULL == canvas) {
+        return false;
+    }
+
+    SkBitmap bitmap;
+    SkISize size = canvas->getDeviceSize();
+    sk_tools::setup_bitmap(&bitmap, size.width(), size.height());
+
+    canvas->readPixels(&bitmap, 0, 0);
+    sk_tools::force_all_opaque(bitmap);
+
+    // Since path is passed in by value, it is okay to modify it.
+    path.append(".png");
+    return SkImageEncoder::EncodeFile(path.c_str(), bitmap, SkImageEncoder::kPNG_Type, 100);
+}
+
+/**
+ * If path is non NULL, append number to it, and call write(SkCanvas*, SkString) to write the
+ * provided canvas to a file. Returns true if path is NULL or if write() succeeds.
+ */
+static bool writeAppendNumber(SkCanvas* canvas, const SkString* path, int number) {
+    if (NULL == path) {
+        return true;
+    }
+    SkString pathWithNumber(*path);
+    pathWithNumber.appendf("%i", number);
+    return write(canvas, pathWithNumber);
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+SkCanvas* RecordPictureRenderer::setupCanvas(int width, int height) {
+    // defer the canvas setup until the render step
+    return NULL;
+}
+
+static bool PNGEncodeBitmapToStream(SkWStream* wStream, const SkBitmap& bm) {
+    return SkImageEncoder::EncodeStream(wStream, bm, SkImageEncoder::kPNG_Type, 100);
+}
+
+bool RecordPictureRenderer::render(const SkString* path, SkBitmap** out) {
+    SkAutoTUnref<SkPicture> replayer(this->createPicture());
+    SkCanvas* recorder = replayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
+                                                  this->recordFlags());
+    this->scaleToScaleFactor(recorder);
+    fPicture->draw(recorder);
+    replayer->endRecording();
+    if (path != NULL) {
+        // Record the new picture as a new SKP with PNG encoded bitmaps.
+        SkString skpPath(*path);
+        // ".skp" was removed from 'path' before being passed in here.
+        skpPath.append(".skp");
+        SkFILEWStream stream(skpPath.c_str());
+        replayer->serialize(&stream, &PNGEncodeBitmapToStream);
+        return true;
+    }
+    return false;
+}
+
+SkString RecordPictureRenderer::getConfigNameInternal() {
+    return SkString("record");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+bool PipePictureRenderer::render(const SkString* path, SkBitmap** out) {
+    SkASSERT(fCanvas.get() != NULL);
+    SkASSERT(fPicture != NULL);
+    if (NULL == fCanvas.get() || NULL == fPicture) {
+        return false;
+    }
+
+    PipeController pipeController(fCanvas.get());
+    SkGPipeWriter writer;
+    SkCanvas* pipeCanvas = writer.startRecording(&pipeController);
+    pipeCanvas->drawPicture(*fPicture);
+    writer.endRecording();
+    fCanvas->flush();
+    if (NULL != path) {
+        return write(fCanvas, *path);
+    }
+    if (NULL != out) {
+        *out = SkNEW(SkBitmap);
+        setup_bitmap(*out, fPicture->width(), fPicture->height());
+        fCanvas->readPixels(*out, 0, 0);
+    }
+    return true;
+}
+
+SkString PipePictureRenderer::getConfigNameInternal() {
+    return SkString("pipe");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void SimplePictureRenderer::init(SkPicture* picture) {
+    INHERITED::init(picture);
+    this->buildBBoxHierarchy();
+}
+
+bool SimplePictureRenderer::render(const SkString* path, SkBitmap** out) {
+    SkASSERT(fCanvas.get() != NULL);
+    SkASSERT(fPicture != NULL);
+    if (NULL == fCanvas.get() || NULL == fPicture) {
+        return false;
+    }
+
+    fCanvas->drawPicture(*fPicture);
+    fCanvas->flush();
+    if (NULL != path) {
+        return write(fCanvas, *path);
+    }
+
+    if (NULL != out) {
+        *out = SkNEW(SkBitmap);
+        setup_bitmap(*out, fPicture->width(), fPicture->height());
+        fCanvas->readPixels(*out, 0, 0);
+    }
+
+    return true;
+}
+
+SkString SimplePictureRenderer::getConfigNameInternal() {
+    return SkString("simple");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+TiledPictureRenderer::TiledPictureRenderer()
+    : fTileWidth(kDefaultTileWidth)
+    , fTileHeight(kDefaultTileHeight)
+    , fTileWidthPercentage(0.0)
+    , fTileHeightPercentage(0.0)
+    , fTileMinPowerOf2Width(0)
+    , fCurrentTileOffset(-1)
+    , fTilesX(0)
+    , fTilesY(0) { }
+
+void TiledPictureRenderer::init(SkPicture* pict) {
+    SkASSERT(pict != NULL);
+    SkASSERT(0 == fTileRects.count());
+    if (NULL == pict || fTileRects.count() != 0) {
+        return;
+    }
+
+    // Do not call INHERITED::init(), which would create a (potentially large) canvas which is not
+    // used by bench_pictures.
+    fPicture = pict;
+    fPicture->ref();
+    this->buildBBoxHierarchy();
+
+    if (fTileWidthPercentage > 0) {
+        fTileWidth = sk_float_ceil2int(float(fTileWidthPercentage * fPicture->width() / 100));
+    }
+    if (fTileHeightPercentage > 0) {
+        fTileHeight = sk_float_ceil2int(float(fTileHeightPercentage * fPicture->height() / 100));
+    }
+
+    if (fTileMinPowerOf2Width > 0) {
+        this->setupPowerOf2Tiles();
+    } else {
+        this->setupTiles();
+    }
+    fCanvas.reset(this->setupCanvas(fTileWidth, fTileHeight));
+    // Initialize to -1 so that the first call to nextTile will set this up to draw tile 0 on the
+    // first call to drawCurrentTile.
+    fCurrentTileOffset = -1;
+}
+
+void TiledPictureRenderer::end() {
+    fTileRects.reset();
+    this->INHERITED::end();
+}
+
+void TiledPictureRenderer::setupTiles() {
+    // Only use enough tiles to cover the viewport
+    const int width = this->getViewWidth();
+    const int height = this->getViewHeight();
+
+    fTilesX = fTilesY = 0;
+    for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
+        fTilesY++;
+        for (int tile_x_start = 0; tile_x_start < width; tile_x_start += fTileWidth) {
+            if (0 == tile_y_start) {
+                // Only count tiles in the X direction on the first pass.
+                fTilesX++;
+            }
+            *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
+                                                    SkIntToScalar(tile_y_start),
+                                                    SkIntToScalar(fTileWidth),
+                                                    SkIntToScalar(fTileHeight));
+        }
+    }
+}
+
+bool TiledPictureRenderer::tileDimensions(int &x, int &y) {
+    if (fTileRects.count() == 0 || NULL == fPicture) {
+        return false;
+    }
+    x = fTilesX;
+    y = fTilesY;
+    return true;
+}
+
+// The goal of the powers of two tiles is to minimize the amount of wasted tile
+// space in the width-wise direction and then minimize the number of tiles. The
+// constraints are that every tile must have a pixel width that is a power of
+// two and also be of some minimal width (that is also a power of two).
+//
+// This is solved by first taking our picture size and rounding it up to the
+// multiple of the minimal width. The binary representation of this rounded
+// value gives us the tiles we need: a bit of value one means we need a tile of
+// that size.
+void TiledPictureRenderer::setupPowerOf2Tiles() {
+    // Only use enough tiles to cover the viewport
+    const int width = this->getViewWidth();
+    const int height = this->getViewHeight();
+
+    int rounded_value = width;
+    if (width % fTileMinPowerOf2Width != 0) {
+        rounded_value = width - (width % fTileMinPowerOf2Width) + fTileMinPowerOf2Width;
+    }
+
+    int num_bits = SkScalarCeilToInt(SkScalarLog2(SkIntToScalar(width)));
+    int largest_possible_tile_size = 1 << num_bits;
+
+    fTilesX = fTilesY = 0;
+    // The tile height is constant for a particular picture.
+    for (int tile_y_start = 0; tile_y_start < height; tile_y_start += fTileHeight) {
+        fTilesY++;
+        int tile_x_start = 0;
+        int current_width = largest_possible_tile_size;
+        // Set fTileWidth to be the width of the widest tile, so that each canvas is large enough
+        // to draw each tile.
+        fTileWidth = current_width;
+
+        while (current_width >= fTileMinPowerOf2Width) {
+            // It is very important this is a bitwise AND.
+            if (current_width & rounded_value) {
+                if (0 == tile_y_start) {
+                    // Only count tiles in the X direction on the first pass.
+                    fTilesX++;
+                }
+                *fTileRects.append() = SkRect::MakeXYWH(SkIntToScalar(tile_x_start),
+                                                        SkIntToScalar(tile_y_start),
+                                                        SkIntToScalar(current_width),
+                                                        SkIntToScalar(fTileHeight));
+                tile_x_start += current_width;
+            }
+
+            current_width >>= 1;
+        }
+    }
+}
+
+/**
+ * Draw the specified playback to the canvas translated to rectangle provided, so that this mini
+ * canvas represents the rectangle's portion of the overall picture.
+ * Saves and restores so that the initial clip and matrix return to their state before this function
+ * is called.
+ */
+template<class T>
+static void DrawTileToCanvas(SkCanvas* canvas, const SkRect& tileRect, T* playback) {
+    int saveCount = canvas->save();
+    // Translate so that we draw the correct portion of the picture.
+    // Perform a postTranslate so that the scaleFactor does not interfere with the positioning.
+    SkMatrix mat(canvas->getTotalMatrix());
+    mat.postTranslate(-tileRect.fLeft, -tileRect.fTop);
+    canvas->setMatrix(mat);
+    playback->draw(canvas);
+    canvas->restoreToCount(saveCount);
+    canvas->flush();
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+static void bitmapCopySubset(const SkBitmap& src, SkBitmap* dst, int xDst,
+                             int yDst) {
+    for (int y = 0; y <src.height() && y + yDst < dst->height() ; y++) {
+        for (int x = 0; x < src.width() && x + xDst < dst->width() ; x++) {
+            *dst->getAddr32(xDst + x, yDst + y) = *src.getAddr32(x, y);
+        }
+    }
+}
+
+bool TiledPictureRenderer::nextTile(int &i, int &j) {
+    if (++fCurrentTileOffset < fTileRects.count()) {
+        i = fCurrentTileOffset % fTilesX;
+        j = fCurrentTileOffset / fTilesX;
+        return true;
+    }
+    return false;
+}
+
+void TiledPictureRenderer::drawCurrentTile() {
+    SkASSERT(fCurrentTileOffset >= 0 && fCurrentTileOffset < fTileRects.count());
+    DrawTileToCanvas(fCanvas, fTileRects[fCurrentTileOffset], fPicture);
+}
+
+bool TiledPictureRenderer::render(const SkString* path, SkBitmap** out) {
+    SkASSERT(fPicture != NULL);
+    if (NULL == fPicture) {
+        return false;
+    }
+
+    SkBitmap bitmap;
+    if (out){
+        *out = SkNEW(SkBitmap);
+        setup_bitmap(*out, fPicture->width(), fPicture->height());
+        setup_bitmap(&bitmap, fTileWidth, fTileHeight);
+    }
+    bool success = true;
+    for (int i = 0; i < fTileRects.count(); ++i) {
+        DrawTileToCanvas(fCanvas, fTileRects[i], fPicture);
+        if (NULL != path) {
+            success &= writeAppendNumber(fCanvas, path, i);
+        }
+        if (NULL != out) {
+            if (fCanvas->readPixels(&bitmap, 0, 0)) {
+                bitmapCopySubset(bitmap, *out, SkScalarFloorToInt(fTileRects[i].left()),
+                                 SkScalarFloorToInt(fTileRects[i].top()));
+            } else {
+                success = false;
+            }
+        }
+    }
+    return success;
+}
+
+SkCanvas* TiledPictureRenderer::setupCanvas(int width, int height) {
+    SkCanvas* canvas = this->INHERITED::setupCanvas(width, height);
+    SkASSERT(fPicture != NULL);
+    // Clip the tile to an area that is completely inside both the SkPicture and the viewport. This
+    // is mostly important for tiles on the right and bottom edges as they may go over this area and
+    // the picture may have some commands that draw outside of this area and so should not actually
+    // be written.
+    // Uses a clipRegion so that it will be unaffected by the scale factor, which may have been set
+    // by INHERITED::setupCanvas.
+    SkRegion clipRegion;
+    clipRegion.setRect(0, 0, this->getViewWidth(), this->getViewHeight());
+    canvas->clipRegion(clipRegion);
+    return canvas;
+}
+
+SkString TiledPictureRenderer::getConfigNameInternal() {
+    SkString name;
+    if (fTileMinPowerOf2Width > 0) {
+        name.append("pow2tile_");
+        name.appendf("%i", fTileMinPowerOf2Width);
+    } else {
+        name.append("tile_");
+        if (fTileWidthPercentage > 0) {
+            name.appendf("%.f%%", fTileWidthPercentage);
+        } else {
+            name.appendf("%i", fTileWidth);
+        }
+    }
+    name.append("x");
+    if (fTileHeightPercentage > 0) {
+        name.appendf("%.f%%", fTileHeightPercentage);
+    } else {
+        name.appendf("%i", fTileHeight);
+    }
+    return name;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+// Holds all of the information needed to draw a set of tiles.
+class CloneData : public SkRunnable {
+
+public:
+    CloneData(SkPicture* clone, SkCanvas* canvas, SkTDArray<SkRect>& rects, int start, int end,
+              SkRunnable* done)
+        : fClone(clone)
+        , fCanvas(canvas)
+        , fPath(NULL)
+        , fRects(rects)
+        , fStart(start)
+        , fEnd(end)
+        , fSuccess(NULL)
+        , fDone(done) {
+        SkASSERT(fDone != NULL);
+    }
+
+    virtual void run() SK_OVERRIDE {
+        SkGraphics::SetTLSFontCacheLimit(1024 * 1024);
+
+        SkBitmap bitmap;
+        if (fBitmap != NULL) {
+            // All tiles are the same size.
+            setup_bitmap(&bitmap, SkScalarFloorToInt(fRects[0].width()), SkScalarFloorToInt(fRects[0].height()));
+        }
+
+        for (int i = fStart; i < fEnd; i++) {
+            DrawTileToCanvas(fCanvas, fRects[i], fClone);
+            if (fPath != NULL && !writeAppendNumber(fCanvas, fPath, i)
+                && fSuccess != NULL) {
+                *fSuccess = false;
+                // If one tile fails to write to a file, do not continue drawing the rest.
+                break;
+            }
+            if (fBitmap != NULL) {
+                if (fCanvas->readPixels(&bitmap, 0, 0)) {
+                    SkAutoLockPixels alp(*fBitmap);
+                    bitmapCopySubset(bitmap, fBitmap, SkScalarFloorToInt(fRects[i].left()),
+                                     SkScalarFloorToInt(fRects[i].top()));
+                } else {
+                    *fSuccess = false;
+                    // If one tile fails to read pixels, do not continue drawing the rest.
+                    break;
+                }
+            }
+        }
+        fDone->run();
+    }
+
+    void setPathAndSuccess(const SkString* path, bool* success) {
+        fPath = path;
+        fSuccess = success;
+    }
+
+    void setBitmap(SkBitmap* bitmap) {
+        fBitmap = bitmap;
+    }
+
+private:
+    // All pointers unowned.
+    SkPicture*         fClone;      // Picture to draw from. Each CloneData has a unique one which
+                                    // is threadsafe.
+    SkCanvas*          fCanvas;     // Canvas to draw to. Reused for each tile.
+    const SkString*    fPath;       // If non-null, path to write the result to as a PNG.
+    SkTDArray<SkRect>& fRects;      // All tiles of the picture.
+    const int          fStart;      // Range of tiles drawn by this thread.
+    const int          fEnd;
+    bool*              fSuccess;    // Only meaningful if path is non-null. Shared by all threads,
+                                    // and only set to false upon failure to write to a PNG.
+    SkRunnable*        fDone;
+    SkBitmap*          fBitmap;
+};
+
+MultiCorePictureRenderer::MultiCorePictureRenderer(int threadCount)
+: fNumThreads(threadCount)
+, fThreadPool(threadCount)
+, fCountdown(threadCount) {
+    // Only need to create fNumThreads - 1 clones, since one thread will use the base
+    // picture.
+    fPictureClones = SkNEW_ARRAY(SkPicture, fNumThreads - 1);
+    fCloneData = SkNEW_ARRAY(CloneData*, fNumThreads);
+}
+
+void MultiCorePictureRenderer::init(SkPicture *pict) {
+    // Set fPicture and the tiles.
+    this->INHERITED::init(pict);
+    for (int i = 0; i < fNumThreads; ++i) {
+        *fCanvasPool.append() = this->setupCanvas(this->getTileWidth(), this->getTileHeight());
+    }
+    // Only need to create fNumThreads - 1 clones, since one thread will use the base picture.
+    fPicture->clone(fPictureClones, fNumThreads - 1);
+    // Populate each thread with the appropriate data.
+    // Group the tiles into nearly equal size chunks, rounding up so we're sure to cover them all.
+    const int chunkSize = (fTileRects.count() + fNumThreads - 1) / fNumThreads;
+
+    for (int i = 0; i < fNumThreads; i++) {
+        SkPicture* pic;
+        if (i == fNumThreads-1) {
+            // The last set will use the original SkPicture.
+            pic = fPicture;
+        } else {
+            pic = &fPictureClones[i];
+        }
+        const int start = i * chunkSize;
+        const int end = SkMin32(start + chunkSize, fTileRects.count());
+        fCloneData[i] = SkNEW_ARGS(CloneData,
+                                   (pic, fCanvasPool[i], fTileRects, start, end, &fCountdown));
+    }
+}
+
+bool MultiCorePictureRenderer::render(const SkString *path, SkBitmap** out) {
+    bool success = true;
+    if (path != NULL) {
+        for (int i = 0; i < fNumThreads-1; i++) {
+            fCloneData[i]->setPathAndSuccess(path, &success);
+        }
+    }
+
+    if (NULL != out) {
+        *out = SkNEW(SkBitmap);
+        setup_bitmap(*out, fPicture->width(), fPicture->height());
+        for (int i = 0; i < fNumThreads; i++) {
+            fCloneData[i]->setBitmap(*out);
+        }
+    } else {
+        for (int i = 0; i < fNumThreads; i++) {
+            fCloneData[i]->setBitmap(NULL);
+        }
+    }
+
+    fCountdown.reset(fNumThreads);
+    for (int i = 0; i < fNumThreads; i++) {
+        fThreadPool.add(fCloneData[i]);
+    }
+    fCountdown.wait();
+
+    return success;
+}
+
+void MultiCorePictureRenderer::end() {
+    for (int i = 0; i < fNumThreads - 1; i++) {
+        SkDELETE(fCloneData[i]);
+        fCloneData[i] = NULL;
+    }
+
+    fCanvasPool.unrefAll();
+
+    this->INHERITED::end();
+}
+
+MultiCorePictureRenderer::~MultiCorePictureRenderer() {
+    // Each individual CloneData was deleted in end.
+    SkDELETE_ARRAY(fCloneData);
+    SkDELETE_ARRAY(fPictureClones);
+}
+
+SkString MultiCorePictureRenderer::getConfigNameInternal() {
+    SkString name = this->INHERITED::getConfigNameInternal();
+    name.appendf("_multi_%i_threads", fNumThreads);
+    return name;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void PlaybackCreationRenderer::setup() {
+    fReplayer.reset(this->createPicture());
+    SkCanvas* recorder = fReplayer->beginRecording(this->getViewWidth(), this->getViewHeight(),
+                                                   this->recordFlags());
+    this->scaleToScaleFactor(recorder);
+    fPicture->draw(recorder);
+}
+
+bool PlaybackCreationRenderer::render(const SkString*, SkBitmap** out) {
+    fReplayer->endRecording();
+    // Since this class does not actually render, return false.
+    return false;
+}
+
+SkString PlaybackCreationRenderer::getConfigNameInternal() {
+    return SkString("playback_creation");
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// SkPicture variants for each BBoxHierarchy type
+
+class RTreePicture : public SkPicture {
+public:
+    virtual SkBBoxHierarchy* createBBoxHierarchy() const SK_OVERRIDE{
+        static const int kRTreeMinChildren = 6;
+        static const int kRTreeMaxChildren = 11;
+        SkScalar aspectRatio = SkScalarDiv(SkIntToScalar(fWidth),
+                                           SkIntToScalar(fHeight));
+        return SkRTree::Create(kRTreeMinChildren, kRTreeMaxChildren,
+                               aspectRatio);
+    }
+};
+
+SkPicture* PictureRenderer::createPicture() {
+    switch (fBBoxHierarchyType) {
+        case kNone_BBoxHierarchyType:
+            return SkNEW(SkPicture);
+        case kRTree_BBoxHierarchyType:
+            return SkNEW(RTreePicture);
+        case kTileGrid_BBoxHierarchyType:
+            return SkNEW_ARGS(SkTileGridPicture, (fGridWidth, fGridHeight, fPicture->width(),
+                fPicture->height()));
+    }
+    SkASSERT(0); // invalid bbhType
+    return NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class GatherRenderer : public PictureRenderer {
+public:
+    virtual bool render(const SkString* path, SkBitmap** out = NULL)
+            SK_OVERRIDE {
+        SkRect bounds = SkRect::MakeWH(SkIntToScalar(fPicture->width()),
+                                       SkIntToScalar(fPicture->height()));
+        SkData* data = SkPictureUtils::GatherPixelRefs(fPicture, bounds);
+        SkSafeUnref(data);
+
+        return NULL == path;    // we don't have anything to write
+    }
+
+private:
+    virtual SkString getConfigNameInternal() SK_OVERRIDE {
+        return SkString("gather_pixelrefs");
+    }
+};
+
+PictureRenderer* CreateGatherPixelRefsRenderer() {
+    return SkNEW(GatherRenderer);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+class PictureCloneRenderer : public PictureRenderer {
+public:
+    virtual bool render(const SkString* path, SkBitmap** out = NULL)
+            SK_OVERRIDE {
+        for (int i = 0; i < 100; ++i) {
+            SkPicture* clone = fPicture->clone();
+            SkSafeUnref(clone);
+        }
+
+        return NULL == path;    // we don't have anything to write
+    }
+
+private:
+    virtual SkString getConfigNameInternal() SK_OVERRIDE {
+        return SkString("picture_clone");
+    }
+};
+
+PictureRenderer* CreatePictureCloneRenderer() {
+    return SkNEW(PictureCloneRenderer);
+}
+
+} // namespace sk_tools
diff --git a/tools/PictureRenderer.h b/tools/PictureRenderer.h
new file mode 100644
index 0000000..df38faa
--- /dev/null
+++ b/tools/PictureRenderer.h
@@ -0,0 +1,462 @@
+/*
+ * 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 PictureRenderer_DEFINED
+#define PictureRenderer_DEFINED
+
+#include "SkCountdown.h"
+#include "SkDrawFilter.h"
+#include "SkMath.h"
+#include "SkPaint.h"
+#include "SkPicture.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "SkRunnable.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+#include "SkThreadPool.h"
+#include "SkTypes.h"
+
+#if SK_SUPPORT_GPU
+#include "GrContextFactory.h"
+#include "GrContext.h"
+#endif
+
+class SkBitmap;
+class SkCanvas;
+class SkGLContext;
+class SkThread;
+
+namespace sk_tools {
+
+class TiledPictureRenderer;
+
+class PictureRenderer : public SkRefCnt {
+
+public:
+    enum SkDeviceTypes {
+        kBitmap_DeviceType,
+#if SK_SUPPORT_GPU
+        kGPU_DeviceType
+#endif
+    };
+
+    enum BBoxHierarchyType {
+        kNone_BBoxHierarchyType = 0,
+        kRTree_BBoxHierarchyType,
+        kTileGrid_BBoxHierarchyType,
+    };
+
+    // this uses SkPaint::Flags as a base and adds additional flags
+    enum DrawFilterFlags {
+        kNone_DrawFilterFlag = 0,
+        kBlur_DrawFilterFlag = 0x4000, // toggles between blur and no blur
+        kHinting_DrawFilterFlag = 0x8000, // toggles between no hinting and normal hinting
+        kSlightHinting_DrawFilterFlag = 0x10000, // toggles between slight and normal hinting
+        kAAClip_DrawFilterFlag = 0x20000, // toggles between soft and hard clip
+    };
+
+    SK_COMPILE_ASSERT(!(kBlur_DrawFilterFlag & SkPaint::kAllFlags), blur_flag_must_be_greater);
+    SK_COMPILE_ASSERT(!(kHinting_DrawFilterFlag & SkPaint::kAllFlags),
+            hinting_flag_must_be_greater);
+    SK_COMPILE_ASSERT(!(kSlightHinting_DrawFilterFlag & SkPaint::kAllFlags),
+            slight_hinting_flag_must_be_greater);
+
+    /**
+     * Called with each new SkPicture to render.
+     */
+    virtual void init(SkPicture* pict);
+
+    /**
+     *  Set the viewport so that only the portion listed gets drawn.
+     */
+    void setViewport(SkISize size) { fViewport = size; }
+
+    /**
+     *  Set the scale factor at which draw the picture.
+     */
+    void setScaleFactor(SkScalar scale) { fScaleFactor = scale; }
+
+    /**
+     * Perform any setup that should done prior to each iteration of render() which should not be
+     * timed.
+     */
+    virtual void setup() {}
+
+    /**
+     * Perform work that is to be timed. Typically this is rendering, but is also used for recording
+     * and preparing picture for playback by the subclasses which do those.
+     * If path is non-null, subclass implementations should call write().
+     * @param path If non-null, also write the output to the file specified by path. path should
+     *             have no extension; it will be added by write().
+     * @return bool True if rendering succeeded and, if path is non-null, the output was
+     *             successfully written to a file.
+     */
+    virtual bool render(const SkString* path, SkBitmap** out = NULL) = 0;
+
+    /**
+     * Called once finished with a particular SkPicture, before calling init again, and before
+     * being done with this Renderer.
+     */
+    virtual void end();
+
+    /**
+     * If this PictureRenderer is actually a TiledPictureRender, return a pointer to this as a
+     * TiledPictureRender so its methods can be called.
+     */
+    virtual TiledPictureRenderer* getTiledRenderer() { return NULL; }
+
+    /**
+     * Resets the GPU's state. Does nothing if the backing is raster. For a GPU renderer, calls
+     * flush, and calls finish if callFinish is true.
+     * @param callFinish Whether to call finish.
+     */
+    void resetState(bool callFinish);
+
+    void setDeviceType(SkDeviceTypes deviceType) {
+        fDeviceType = deviceType;
+    }
+
+    void setDrawFilters(DrawFilterFlags const * const filters, const SkString& configName) {
+        memcpy(fDrawFilters, filters, sizeof(fDrawFilters));
+        fDrawFiltersConfig = configName;
+    }
+
+    void setBBoxHierarchyType(BBoxHierarchyType bbhType) {
+        fBBoxHierarchyType = bbhType;
+    }
+
+    void setGridSize(int width, int height) {
+        fGridWidth = width;
+        fGridHeight = height;
+    }
+
+    bool isUsingBitmapDevice() {
+        return kBitmap_DeviceType == fDeviceType;
+    }
+
+    virtual SkString getPerIterTimeFormat() { return SkString("%.2f"); }
+
+    virtual SkString getNormalTimeFormat() { return SkString("%6.2f"); }
+
+    /**
+     * Reports the configuration of this PictureRenderer.
+     */
+    SkString getConfigName() {
+        SkString config = this->getConfigNameInternal();
+        if (!fViewport.isEmpty()) {
+            config.appendf("_viewport_%ix%i", fViewport.width(), fViewport.height());
+        }
+        if (kRTree_BBoxHierarchyType == fBBoxHierarchyType) {
+            config.append("_rtree");
+        } else if (kTileGrid_BBoxHierarchyType == fBBoxHierarchyType) {
+            config.append("_grid");
+        }
+#if SK_SUPPORT_GPU
+        if (this->isUsingGpuDevice()) {
+            config.append("_gpu");
+        }
+#endif
+        config.append(fDrawFiltersConfig.c_str());
+        return config;
+    }
+
+#if SK_SUPPORT_GPU
+    bool isUsingGpuDevice() {
+        return kGPU_DeviceType == fDeviceType;
+    }
+
+    SkGLContext* getGLContext() {
+        if (this->isUsingGpuDevice()) {
+            return fGrContextFactory.getGLContext(GrContextFactory::kNative_GLContextType);
+        } else {
+            return NULL;
+        }
+    }
+
+    GrContext* getGrContext() {
+        return fGrContext;
+    }
+#endif
+
+    PictureRenderer()
+        : fPicture(NULL)
+        , fDeviceType(kBitmap_DeviceType)
+        , fBBoxHierarchyType(kNone_BBoxHierarchyType)
+        , fGridWidth(0)
+        , fGridHeight(0)
+#if SK_SUPPORT_GPU
+        , fGrContext(fGrContextFactory.get(GrContextFactory::kNative_GLContextType))
+#endif
+        , fScaleFactor(SK_Scalar1)
+        {
+            sk_bzero(fDrawFilters, sizeof(fDrawFilters));
+            fViewport.set(0, 0);
+        }
+
+protected:
+    SkAutoTUnref<SkCanvas> fCanvas;
+    SkPicture*             fPicture;
+    SkDeviceTypes          fDeviceType;
+    BBoxHierarchyType      fBBoxHierarchyType;
+    DrawFilterFlags        fDrawFilters[SkDrawFilter::kTypeCount];
+    SkString               fDrawFiltersConfig;
+    int                    fGridWidth, fGridHeight; // used when fBBoxHierarchyType is TileGrid
+
+#if SK_SUPPORT_GPU
+    GrContextFactory fGrContextFactory;
+    GrContext* fGrContext;
+#endif
+
+    void buildBBoxHierarchy();
+
+    /**
+     * Return the total width that should be drawn. If the viewport width has been set greater than
+     * 0, this will be the minimum of the current SkPicture's width and the viewport's width.
+     */
+    int getViewWidth();
+
+    /**
+     * Return the total height that should be drawn. If the viewport height has been set greater
+     * than 0, this will be the minimum of the current SkPicture's height and the viewport's height.
+     */
+    int getViewHeight();
+
+    /**
+     * Scales the provided canvas to the scale factor set by setScaleFactor.
+     */
+    void scaleToScaleFactor(SkCanvas*);
+
+    SkPicture* createPicture();
+    uint32_t recordFlags();
+    SkCanvas* setupCanvas();
+    virtual SkCanvas* setupCanvas(int width, int height);
+
+private:
+    SkISize                fViewport;
+    SkScalar               fScaleFactor;
+
+    virtual SkString getConfigNameInternal() = 0;
+
+    typedef SkRefCnt INHERITED;
+};
+
+/**
+ * This class does not do any rendering, but its render function executes recording, which we want
+ * to time.
+ */
+class RecordPictureRenderer : public PictureRenderer {
+    virtual bool render(const SkString*, SkBitmap** out = NULL) SK_OVERRIDE;
+
+    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
+
+    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
+
+protected:
+    virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
+
+private:
+    virtual SkString getConfigNameInternal() SK_OVERRIDE;
+};
+
+class PipePictureRenderer : public PictureRenderer {
+public:
+    virtual bool render(const SkString*, SkBitmap** out = NULL) SK_OVERRIDE;
+
+private:
+    virtual SkString getConfigNameInternal() SK_OVERRIDE;
+
+    typedef PictureRenderer INHERITED;
+};
+
+class SimplePictureRenderer : public PictureRenderer {
+public:
+    virtual void init(SkPicture* pict) SK_OVERRIDE;
+
+    virtual bool render(const SkString*, SkBitmap** out = NULL) SK_OVERRIDE;
+
+private:
+    virtual SkString getConfigNameInternal() SK_OVERRIDE;
+
+    typedef PictureRenderer INHERITED;
+};
+
+class TiledPictureRenderer : public PictureRenderer {
+public:
+    TiledPictureRenderer();
+
+    virtual void init(SkPicture* pict) SK_OVERRIDE;
+
+    /**
+     * Renders to tiles, rather than a single canvas. If a path is provided, a separate file is
+     * created for each tile, named "path0.png", "path1.png", etc.
+     * Multithreaded mode currently does not support writing to a file.
+     */
+    virtual bool render(const SkString* path, SkBitmap** out = NULL) SK_OVERRIDE;
+
+    virtual void end() SK_OVERRIDE;
+
+    void setTileWidth(int width) {
+        fTileWidth = width;
+    }
+
+    int getTileWidth() const {
+        return fTileWidth;
+    }
+
+    void setTileHeight(int height) {
+        fTileHeight = height;
+    }
+
+    int getTileHeight() const {
+        return fTileHeight;
+    }
+
+    void setTileWidthPercentage(double percentage) {
+        fTileWidthPercentage = percentage;
+    }
+
+    double getTileWidthPercentage() const {
+        return fTileWidthPercentage;
+    }
+
+    void setTileHeightPercentage(double percentage) {
+        fTileHeightPercentage = percentage;
+    }
+
+    double getTileHeightPercentage() const {
+        return fTileHeightPercentage;
+    }
+
+    void setTileMinPowerOf2Width(int width) {
+        SkASSERT(SkIsPow2(width) && width > 0);
+        if (!SkIsPow2(width) || width <= 0) {
+            return;
+        }
+
+        fTileMinPowerOf2Width = width;
+    }
+
+    int getTileMinPowerOf2Width() const {
+        return fTileMinPowerOf2Width;
+    }
+
+    virtual TiledPictureRenderer* getTiledRenderer() SK_OVERRIDE { return this; }
+
+    /**
+     * Report the number of tiles in the x and y directions. Must not be called before init.
+     * @param x Output parameter identifying the number of tiles in the x direction.
+     * @param y Output parameter identifying the number of tiles in the y direction.
+     * @return True if the tiles have been set up, and x and y are meaningful. If false, x and y are
+     *         unmodified.
+     */
+    bool tileDimensions(int& x, int&y);
+
+    /**
+     * Move to the next tile and return its indices. Must be called before calling drawCurrentTile
+     * for the first time.
+     * @param i Output parameter identifying the column of the next tile to be drawn on the next
+     *          call to drawNextTile.
+     * @param j Output parameter identifying the row  of the next tile to be drawn on the next call
+     *          to drawNextTile.
+     * @param True if the tiles have been created and the next tile to be drawn by drawCurrentTile
+     *        is within the range of tiles. If false, i and j are unmodified.
+     */
+    bool nextTile(int& i, int& j);
+
+    /**
+     * Render one tile. This will draw the same tile each time it is called until nextTile is
+     * called. The tile rendered will depend on how many calls have been made to nextTile.
+     * It is an error to call this without first calling nextTile, or if nextTile returns false.
+     */
+    void drawCurrentTile();
+
+protected:
+    SkTDArray<SkRect> fTileRects;
+
+    virtual SkCanvas* setupCanvas(int width, int height) SK_OVERRIDE;
+    virtual SkString getConfigNameInternal() SK_OVERRIDE;
+
+private:
+    int    fTileWidth;
+    int    fTileHeight;
+    double fTileWidthPercentage;
+    double fTileHeightPercentage;
+    int    fTileMinPowerOf2Width;
+
+    // These variables are only used for timing individual tiles.
+    // Next tile to draw in fTileRects.
+    int    fCurrentTileOffset;
+    // Number of tiles in the x direction.
+    int    fTilesX;
+    // Number of tiles in the y direction.
+    int    fTilesY;
+
+    void setupTiles();
+    void setupPowerOf2Tiles();
+
+    typedef PictureRenderer INHERITED;
+};
+
+class CloneData;
+
+class MultiCorePictureRenderer : public TiledPictureRenderer {
+public:
+    explicit MultiCorePictureRenderer(int threadCount);
+
+    ~MultiCorePictureRenderer();
+
+    virtual void init(SkPicture* pict) SK_OVERRIDE;
+
+    /**
+     * Behaves like TiledPictureRenderer::render(), only using multiple threads.
+     */
+    virtual bool render(const SkString* path, SkBitmap** out = NULL) SK_OVERRIDE;
+
+    virtual void end() SK_OVERRIDE;
+
+private:
+    virtual SkString getConfigNameInternal() SK_OVERRIDE;
+
+    const int            fNumThreads;
+    SkTDArray<SkCanvas*> fCanvasPool;
+    SkThreadPool         fThreadPool;
+    SkPicture*           fPictureClones;
+    CloneData**          fCloneData;
+    SkCountdown          fCountdown;
+
+    typedef TiledPictureRenderer INHERITED;
+};
+
+/**
+ * This class does not do any rendering, but its render function executes turning an SkPictureRecord
+ * into an SkPicturePlayback, which we want to time.
+ */
+class PlaybackCreationRenderer : public PictureRenderer {
+public:
+    virtual void setup() SK_OVERRIDE;
+
+    virtual bool render(const SkString*, SkBitmap** out = NULL) SK_OVERRIDE;
+
+    virtual SkString getPerIterTimeFormat() SK_OVERRIDE { return SkString("%.4f"); }
+
+    virtual SkString getNormalTimeFormat() SK_OVERRIDE { return SkString("%6.4f"); }
+
+private:
+    SkAutoTUnref<SkPicture> fReplayer;
+
+    virtual SkString getConfigNameInternal() SK_OVERRIDE;
+
+    typedef PictureRenderer INHERITED;
+};
+
+extern PictureRenderer* CreateGatherPixelRefsRenderer();
+extern PictureRenderer* CreatePictureCloneRenderer();
+
+}
+
+#endif  // PictureRenderer_DEFINED
diff --git a/tools/__init__.py b/tools/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tools/__init__.py
diff --git a/tools/bench_pictures.cfg b/tools/bench_pictures.cfg
new file mode 100644
index 0000000..661af52
--- /dev/null
+++ b/tools/bench_pictures.cfg
@@ -0,0 +1,116 @@
+# 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.
+
+
+"""
+This file defines the configurations in which bench_pictures should be run
+on various platforms. The buildbots read these configurations from the
+bench_pictures_cfg dictionary. Everything else in this file exists to help in
+constructing that dictionary.
+
+This code is executed directly on the buildbot so that convenient things like
+variables and loops can be used to avoid unnecessary verbosity. With great power
+comes great responsibility; don't put any nasty code here. To reiterate, code in
+this file will be directly executed on the build slaves.
+"""
+
+
+import os
+import sys
+
+
+if 'import_path' in globals():
+  sys.path.append(import_path)
+
+
+from bench_pictures_cfg_helper import *
+
+
+# Default tile sizes
+DEFAULT_TILE_X = '256'
+DEFAULT_TILE_Y = '256'
+
+
+# Configs to run on most bots
+default_configs = [
+  # Basic CPU and GPU configs
+  TiledBitmapConfig(DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  TiledGPUConfig(DEFAULT_TILE_X, DEFAULT_TILE_Y),
+
+  # CopyTiles
+  CopyTilesConfig(DEFAULT_TILE_X, DEFAULT_TILE_Y),
+
+  # Record
+  RecordConfig(),
+
+  # Multi-threaded
+  MultiThreadTileConfig(2, DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  MultiThreadTileConfig(3, DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  MultiThreadTileConfig(4, DEFAULT_TILE_X, DEFAULT_TILE_Y),
+
+  # Different tile sizes
+  TiledBitmapConfig(512, 512),
+  TiledBitmapConfig(1024, 256),
+  TiledBitmapConfig(1024, 64),
+
+  # Different bounding box heirarchies, for different modes.
+  RecordRTreeConfig(),
+  PlaybackCreationRTreeConfig(),
+  TileRTreeConfig(            DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  RecordGridConfig(           DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  PlaybackCreationGridConfig( DEFAULT_TILE_X, DEFAULT_TILE_Y),
+  TileGridConfig(             DEFAULT_TILE_X, DEFAULT_TILE_Y),
+]
+
+
+def AndroidConfigList(tile_size, scale, cores, viewport, do_gpu=True):
+  tile_x = tile_size[0]
+  tile_y = tile_size[1]
+
+  viewport_x = viewport[0]
+  viewport_y = viewport[1]
+
+  configs = [
+    # Record
+    RecordConfig(     scale=str(scale)),
+    RecordRTreeConfig(scale=str(scale)),
+    RecordGridConfig( tile_x, tile_y, scale=str(scale)),
+
+    # Tiled playback
+    TiledBitmapConfig(tile_x, tile_y, scale=str(scale)),
+    TileRTreeConfig(  tile_x, tile_y, scale=str(scale)),
+    TileGridConfig(   tile_x, tile_y, scale=str(scale)),
+
+    # Viewport playback
+    ViewportBitmapConfig(viewport_x, viewport_y, scale=str(scale)),
+    ViewportRTreeConfig( viewport_x, viewport_y, scale=str(scale)),
+  ]
+
+  if do_gpu:
+    configs.append(TiledGPUConfig(tile_x, tile_y, scale=str(scale)))
+    configs.append(ViewportGPUConfig(viewport_x, viewport_y, scale=str(scale)))
+
+  # Multicore
+  for num_cores in cores:
+    configs.append(MultiThreadTileConfig(num_cores, tile_x, tile_y,
+                                         scale=str(scale)))
+
+  return configs
+
+
+# This dictionary defines the sets of configs for all platforms. Each config is
+# a dictionary of key/value pairs directly corresponding to the command-line
+# flags passed to bench_pictures.
+bench_pictures_cfg = {
+  'debug': [TiledBitmapConfig(DEFAULT_TILE_X, DEFAULT_TILE_Y)],
+  'default': default_configs,
+  'no_gpu': [cfg for cfg in default_configs if cfg['device'] != 'gpu'],
+  'nexus_s':      AndroidConfigList((256, 256), 0.4897, [],  (480,  800),
+                                    do_gpu=False),
+  'xoom':         AndroidConfigList((256, 256), 1.2244, [],  (1200, 800)),
+  'galaxy_nexus': AndroidConfigList((256, 256), 0.8163, [],  (800,  1280)),
+  'nexus_4':      AndroidConfigList((256, 256), 0.7836, [],  (768,  1280)),
+  'nexus_7':      AndroidConfigList((256, 256), 1.3061, [2], (1280, 800)),
+  'nexus_10':     AndroidConfigList((512, 512), 2.6122, [],  (2560, 1600)),
+}
\ No newline at end of file
diff --git a/tools/bench_pictures_cfg_helper.py b/tools/bench_pictures_cfg_helper.py
new file mode 100644
index 0000000..65d63a4
--- /dev/null
+++ b/tools/bench_pictures_cfg_helper.py
@@ -0,0 +1,105 @@
+# 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.
+
+
+""" Helper functions to be used in bench_pictures.cfg. """
+
+
+def Config(**kwargs):
+  config = {}
+  for key in kwargs:
+    config[key] = kwargs[key]
+  return config
+
+
+def TileArgs(tile_x, tile_y):
+  return {'mode': ['tile', str(tile_x), str(tile_y)],
+          # TODO(borenet): Turn this back on once the parser is working.
+          #'timeIndividualTiles': True
+          }
+
+
+def BitmapConfig(**kwargs):
+  return Config(device='bitmap', **kwargs)
+
+
+def GPUConfig(**kwargs):
+  return Config(device='gpu', **kwargs)
+
+
+def TiledBitmapConfig(tile_x, tile_y, **kwargs):
+  return BitmapConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
+
+
+def TiledGPUConfig(tile_x, tile_y, **kwargs):
+  return GPUConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
+
+
+def ViewportBitmapConfig(viewport_x, viewport_y, **kwargs):
+  return BitmapConfig(viewport=[str(viewport_x), str(viewport_y)], **kwargs)
+
+
+def ViewportGPUConfig(viewport_x, viewport_y, **kwargs):
+  return GPUConfig(viewport=[str(viewport_x), str(viewport_y)], **kwargs)
+
+
+def ViewportRTreeConfig(viewport_x, viewport_y, **kwargs):
+  return RTreeConfig(mode='simple', viewport=[str(viewport_x), str(viewport_y)],
+                     **kwargs)
+
+
+def ViewportGridConfig(viewport_x, viewport_y, **kwargs):
+  return GridConfig(viewport_x, viewport_y, mode='simple',
+                    viewport=[str(viewport_x), str(viewport_y)], **kwargs)
+
+
+def CopyTilesConfig(tile_x, tile_y, **kwargs):
+  return BitmapConfig(mode=['copyTile', str(tile_x), str(tile_y)], **kwargs)
+
+
+def RecordConfig(**kwargs):
+  return BitmapConfig(mode='record', **kwargs)
+
+
+def PlaybackCreationConfig(**kwargs):
+  return BitmapConfig(mode='playbackCreation', **kwargs)
+
+
+def MultiThreadTileConfig(threads, tile_x, tile_y, **kwargs):
+  return TiledBitmapConfig(multi=str(threads), tile_x=tile_x, tile_y=tile_y,
+                           **kwargs)
+
+
+def RTreeConfig(**kwargs):
+  return BitmapConfig(bbh='rtree', **kwargs)
+
+
+def GridConfig(tile_x, tile_y, mode, **kwargs):
+  return BitmapConfig(mode=mode, bbh=['grid', str(tile_x), str(tile_y)],
+                      **kwargs)
+
+
+def RecordRTreeConfig(**kwargs):
+  return RTreeConfig(mode='record', **kwargs)
+
+
+def PlaybackCreationRTreeConfig(**kwargs):
+  return RTreeConfig(mode='playbackCreation', **kwargs)
+
+
+def TileRTreeConfig(tile_x, tile_y, **kwargs):
+  return RTreeConfig(**dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
+
+
+def RecordGridConfig(tile_x, tile_y, **kwargs):
+  return GridConfig(tile_x=tile_x, tile_y=tile_y, mode='record', **kwargs)
+
+
+def PlaybackCreationGridConfig(tile_x, tile_y, **kwargs):
+  return GridConfig(tile_x, tile_y, mode='playbackCreation')
+
+
+def TileGridConfig(tile_x, tile_y, **kwargs):
+  return GridConfig(tile_x, tile_y,
+                    **dict(TileArgs(tile_x, tile_y).items() + kwargs.items()))
\ No newline at end of file
diff --git a/tools/bench_pictures_main.cpp b/tools/bench_pictures_main.cpp
new file mode 100644
index 0000000..b485aff
--- /dev/null
+++ b/tools/bench_pictures_main.cpp
@@ -0,0 +1,806 @@
+/*
+ * 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 "BenchTimer.h"
+#include "CopyTilesRenderer.h"
+#include "PictureBenchmark.h"
+#include "SkBenchLogger.h"
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkMath.h"
+#include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkTArray.h"
+#include "picture_utils.h"
+
+const int DEFAULT_REPEATS = 1;
+
+static char const * const gFilterTypes[] = {
+    "paint",
+    "point",
+    "line",
+    "bitmap",
+    "rect",
+    "oval",
+    "path",
+    "text",
+    "all",
+};
+
+static const size_t kFilterTypesCount = sizeof(gFilterTypes) / sizeof(gFilterTypes[0]);
+
+static char const * const gFilterFlags[] = {
+    "antiAlias",
+    "filterBitmap",
+    "dither",
+    "underlineText",
+    "strikeThruText",
+    "fakeBoldText",
+    "linearText",
+    "subpixelText",
+    "devKernText",
+    "LCDRenderText",
+    "embeddedBitmapText",
+    "autoHinting",
+    "verticalText",
+    "genA8FromLCD",
+    "blur",
+    "hinting",
+    "slightHinting",
+    "AAClip",
+};
+
+static const size_t kFilterFlagsCount = sizeof(gFilterFlags) / sizeof(gFilterFlags[0]);
+
+static SkString filtersName(sk_tools::PictureRenderer::DrawFilterFlags* drawFilters) {
+    int all = drawFilters[0];
+    size_t tIndex;
+    for (tIndex = 1; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
+        all &= drawFilters[tIndex];
+    }
+    SkString result;
+    for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
+        SkString types;
+        if (all & (1 << fIndex)) {
+            types = gFilterTypes[SkDrawFilter::kTypeCount];
+        } else {
+            for (tIndex = 0; tIndex < SkDrawFilter::kTypeCount; ++tIndex) {
+                if (drawFilters[tIndex] & (1 << fIndex)) {
+                    types += gFilterTypes[tIndex];
+                }
+            }
+        }
+        if (!types.size()) {
+            continue;
+        }
+        result += "_";
+        result += types;
+        result += ".";
+        result += gFilterFlags[fIndex];
+    }
+    return result;
+}
+
+static SkString filterTypesUsage() {
+    SkString result;
+    for (size_t index = 0; index < kFilterTypesCount; ++index) {
+        result += gFilterTypes[index];
+        if (index < kFilterTypesCount - 1) {
+            result += " | ";
+        }
+    }
+    return result;
+}
+
+static SkString filterFlagsUsage() {
+    SkString result;
+    size_t len = 0;
+    for (size_t index = 0; index < kFilterFlagsCount; ++index) {
+        result += gFilterFlags[index];
+        if (result.size() - len >= 72) {
+            result += "\n           ";
+            len = result.size();
+        }
+        if (index < kFilterFlagsCount - 1) {
+            result += " | ";
+        }
+    }
+    return result;
+}
+
+static void usage(const char* argv0) {
+    SkDebugf("SkPicture benchmarking tool\n");
+    SkDebugf("\n"
+"Usage: \n"
+"     %s <inputDir>...\n"
+"     [--logFile filename][--timers [wcgWC]*][--logPerIter 1|0][--min]\n"
+"     [--repeat][--timeIndividualTiles] \n"
+"     [--mode pow2tile minWidth height | record | simple\n"
+"             | tile width height | playbackCreation]\n"
+"     [--pipe]\n"
+"     [--bbh bbhType]\n"
+"     [--multi numThreads]\n"
+"     [--viewport width height][--scale sf]\n"
+"     [--device bitmap"
+#if SK_SUPPORT_GPU
+" | gpu"
+#endif
+"]\n"
+"     [--filter [%s]:\n            [%s]]\n"
+, argv0, filterTypesUsage().c_str(), filterFlagsUsage().c_str());
+    SkDebugf("\n");
+    SkDebugf(
+"     inputDir:  A list of directories and files to use as input. Files are\n"
+"                expected to have the .skp extension.\n\n"
+"     --logFile filename : destination for writing log output, in addition to stdout.\n");
+    SkDebugf("     --logPerIter 1|0 : "
+             "Log each repeat timer instead of mean, default is disabled.\n");
+    SkDebugf("     --min : Print the minimum times (instead of average).\n");
+    SkDebugf("     --timers [wcgWC]* : "
+             "Display wall, cpu, gpu, truncated wall or truncated cpu time for each picture.\n");
+    SkDebugf("     --timeIndividualTiles : Report times for drawing individual tiles, rather than\n"
+"                                          times for drawing the whole page.\n"
+"                                          Requires --mode tile\n");
+    SkDebugf(
+"     --mode pow2tile minWidth height | copyTile width height | record | simple\n"
+"            | tile width height | playbackCreation:\n"
+"            Run in the corresponding mode.\n"
+"            Default is simple.\n");
+    SkDebugf(
+"                     pow2tile minWidth height, Creates tiles with widths\n"
+"                                                 that are all a power of two\n"
+"                                                 such that they minimize the\n"
+"                                                 amount of wasted tile space.\n"
+"                                                 minWidth is the minimum width\n"
+"                                                 of these tiles and must be a\n"
+"                                                 power of two. Simple\n"
+"                                                 rendering using these tiles\n"
+"                                                 is benchmarked.\n");
+    SkDebugf(
+"                     record, Benchmark picture to picture recording.\n");
+    SkDebugf(
+"                     simple, Benchmark a simple rendering.\n");
+    SkDebugf(
+"                     tile width height, Benchmark simple rendering using\n"
+"                                            tiles with the given dimensions.\n"
+"                     copyTile width height, Draw the picture, then copy it into tiles.\n"
+"                                                Does not support percentages.\n"
+"                                                If the picture is large enough, breaks it into\n"
+"                                                larger tiles (and draws the picture once per\n"
+"                                                larger tile) to avoid creating a large canvas.\n"
+"                                                Add --tiles x y to specify the number of tiles\n"
+"                                                per larger tile in the x and y direction.\n"
+             );
+    SkDebugf(
+"                     playbackCreation, Benchmark creation of the SkPicturePlayback.\n");
+    SkDebugf("\n");
+    SkDebugf(
+"     --multi numThreads : Set the number of threads for multi threaded drawing. Must be greater\n"
+"                          than 1. Only works with tiled rendering.\n"
+"     --viewport width height : Set the viewport.\n"
+"     --scale sf : Scale drawing by sf.\n"
+"     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
+    SkDebugf(
+"     --bbh bbhType [width height]: Set the bounding box hierarchy type to\n"
+"                     be used. Accepted values are: none, rtree, grid. Default\n"
+"                     value is none. Not compatible with --pipe. With value\n"
+"                     'grid', width and height must be specified. 'grid' can\n"
+"                     only be used with modes tile, record, and\n"
+"                     playbackCreation.");
+    SkDebugf(
+"     --device bitmap"
+#if SK_SUPPORT_GPU
+" | gpu"
+#endif
+": Use the corresponding device. Default is bitmap.\n");
+    SkDebugf(
+"                     bitmap, Render to a bitmap.\n");
+#if SK_SUPPORT_GPU
+    SkDebugf(
+"                     gpu, Render to the GPU.\n");
+#endif
+    SkDebugf("\n");
+    SkDebugf(
+"     --repeat:  "
+"Set the number of times to repeat each test."
+" Default is %i.\n", DEFAULT_REPEATS);
+    SkDebugf(
+"     --filter type:flag : Enable canvas filtering to disable a paint flag,\n"
+"                     use no blur or low quality blur, or use no hinting or\n"
+"                     slight hinting. For all flags except AAClip, specify the\n"
+"                     type of primitive to effect, or choose all. for AAClip\n"
+"                     alone, the filter affects all clips independent of type.\n");
+}
+
+SkBenchLogger gLogger;
+
+static bool run_single_benchmark(const SkString& inputPath,
+                                 sk_tools::PictureBenchmark& benchmark) {
+    SkFILEStream inputStream;
+
+    inputStream.setPath(inputPath.c_str());
+    if (!inputStream.isValid()) {
+        SkString err;
+        err.printf("Could not open file %s\n", inputPath.c_str());
+        gLogger.logError(err);
+        return false;
+    }
+
+    bool success = false;
+    SkPicture picture(&inputStream, &success, &SkImageDecoder::DecodeStream);
+    if (!success) {
+        SkString err;
+        err.printf("Could not read an SkPicture from %s\n", inputPath.c_str());
+        gLogger.logError(err);
+        return false;
+    }
+
+    SkString filename;
+    sk_tools::get_basename(&filename, inputPath);
+
+    SkString result;
+    result.printf("running bench [%i %i] %s ", picture.width(),
+                  picture.height(), filename.c_str());
+    gLogger.logProgress(result);
+
+    benchmark.run(&picture);
+    return true;
+}
+
+#define PRINT_USAGE_AND_EXIT \
+    do {                     \
+        usage(argv0);        \
+        exit(-1);            \
+    } while (0)
+
+static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
+                              sk_tools::PictureBenchmark* benchmark) {
+    const char* argv0 = argv[0];
+    char* const* stop = argv + argc;
+
+    int repeats = DEFAULT_REPEATS;
+    sk_tools::PictureRenderer::SkDeviceTypes deviceType =
+        sk_tools::PictureRenderer::kBitmap_DeviceType;
+
+    SkAutoTUnref<sk_tools::PictureRenderer> renderer(NULL);
+
+    // Create a string to show our current settings.
+    // TODO: Make it prettier. Currently it just repeats the command line.
+    SkString commandLine("bench_pictures:");
+    for (int i = 1; i < argc; i++) {
+        commandLine.appendf(" %s", *(argv+i));
+    }
+    commandLine.append("\n");
+
+    bool usePipe = false;
+    int numThreads = 1;
+    bool useTiles = false;
+    const char* widthString = NULL;
+    const char* heightString = NULL;
+    int gridWidth = 0;
+    int gridHeight = 0;
+    bool isPowerOf2Mode = false;
+    bool isCopyMode = false;
+    const char* xTilesString = NULL;
+    const char* yTilesString = NULL;
+    const char* mode = NULL;
+    bool gridSupported = false;
+    sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
+        sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
+    sk_tools::PictureRenderer::DrawFilterFlags drawFilters[SkDrawFilter::kTypeCount];
+    sk_bzero(drawFilters, sizeof(drawFilters));
+    SkISize viewport;
+    viewport.setEmpty();
+    SkScalar scaleFactor = SK_Scalar1;
+    for (++argv; argv < stop; ++argv) {
+        if (0 == strcmp(*argv, "--repeat")) {
+            ++argv;
+            if (argv < stop) {
+                repeats = atoi(*argv);
+                if (repeats < 1) {
+                    gLogger.logError("--repeat must be given a value > 0\n");
+                    PRINT_USAGE_AND_EXIT;
+                }
+            } else {
+                gLogger.logError("Missing arg for --repeat\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--pipe")) {
+            usePipe = true;
+        } else if (0 == strcmp(*argv, "--logFile")) {
+            argv++;
+            if (argv < stop) {
+                if (!gLogger.SetLogFile(*argv)) {
+                    SkString str;
+                    str.printf("Could not open %s for writing.", *argv);
+                    gLogger.logError(str);
+                    usage(argv0);
+                    // TODO(borenet): We're disabling this for now, due to
+                    // write-protected Android devices.  The very short-term
+                    // solution is to ignore the fact that we have no log file.
+                    //exit(-1);
+                }
+            } else {
+                gLogger.logError("Missing arg for --logFile\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--multi")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing arg for --multi\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            numThreads = atoi(*argv);
+            if (numThreads < 2) {
+                gLogger.logError("Number of threads must be at least 2.\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--bbh")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing value for --bbh\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            if (0 == strcmp(*argv, "none")) {
+                bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
+            } else if (0 == strcmp(*argv, "rtree")) {
+                bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
+            } else if (0 == strcmp(*argv, "grid")) {
+                bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
+                ++argv;
+                if (argv >= stop) {
+                    gLogger.logError("Missing width for --bbh grid\n");
+                    PRINT_USAGE_AND_EXIT;
+                }
+                gridWidth = atoi(*argv);
+                ++argv;
+                if (argv >= stop) {
+                    gLogger.logError("Missing height for --bbh grid\n");
+                    PRINT_USAGE_AND_EXIT;
+                }
+                gridHeight = atoi(*argv);
+            } else {
+                SkString err;
+                err.printf("%s is not a valid value for --bbhType\n", *argv);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+
+        } else if (0 == strcmp(*argv, "--mode")) {
+            if (renderer.get() != NULL) {
+                SkDebugf("Cannot combine modes.\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing mode for --mode\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+
+            if (0 == strcmp(*argv, "record")) {
+                renderer.reset(SkNEW(sk_tools::RecordPictureRenderer));
+                gridSupported = true;
+            } else if (0 == strcmp(*argv, "clone")) {
+                renderer.reset(sk_tools::CreatePictureCloneRenderer());
+            } else if (0 == strcmp(*argv, "simple")) {
+                renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
+            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
+                       || 0 == strcmp(*argv, "copyTile")) {
+                useTiles = true;
+                mode = *argv;
+
+                if (0 == strcmp(*argv, "pow2tile")) {
+                    isPowerOf2Mode = true;
+                } else if (0 == strcmp(*argv, "copyTile")) {
+                    isCopyMode = true;
+                } else {
+                    gridSupported = true;
+                }
+
+                ++argv;
+                if (argv >= stop) {
+                    SkString err;
+                    err.printf("Missing width for --mode %s\n", mode);
+                    gLogger.logError(err);
+                    PRINT_USAGE_AND_EXIT;
+                }
+
+                widthString = *argv;
+                ++argv;
+                if (argv >= stop) {
+                    SkString err;
+                    err.appendf("Missing height for --mode %s\n", mode);
+                    gLogger.logError(err);
+                    PRINT_USAGE_AND_EXIT;
+                }
+                heightString = *argv;
+            } else if (0 == strcmp(*argv, "playbackCreation")) {
+                renderer.reset(SkNEW(sk_tools::PlaybackCreationRenderer));
+                gridSupported = true;
+            } else if (0 == strcmp(*argv, "gatherPixelRefs")) {
+                renderer.reset(sk_tools::CreateGatherPixelRefsRenderer());
+            } else {
+                SkString err;
+                err.printf("%s is not a valid mode for --mode\n", *argv);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--viewport")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing width for --viewport\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            viewport.fWidth = atoi(*argv);
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing height for --viewport\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            viewport.fHeight = atoi(*argv);
+        } else if (0 == strcmp(*argv, "--scale")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing scaleFactor for --scale\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            scaleFactor = SkDoubleToScalar(atof(*argv));
+        } else if (0 == strcmp(*argv, "--tiles")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing x for --tiles\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            xTilesString = *argv;
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing y for --tiles\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            yTilesString = *argv;
+        }  else if (0 == strcmp(*argv, "--device")) {
+            ++argv;
+            if (argv >= stop) {
+                gLogger.logError("Missing mode for --device\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+
+            if (0 == strcmp(*argv, "bitmap")) {
+                deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
+            }
+#if SK_SUPPORT_GPU
+            else if (0 == strcmp(*argv, "gpu")) {
+                deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
+            }
+#endif
+            else {
+                SkString err;
+                err.printf("%s is not a valid mode for --device\n", *argv);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--timers")) {
+            ++argv;
+            if (argv < stop) {
+                bool timerWall = false;
+                bool truncatedTimerWall = false;
+                bool timerCpu = false;
+                bool truncatedTimerCpu = false;
+                bool timerGpu = false;
+                for (char* t = *argv; *t; ++t) {
+                    switch (*t) {
+                        case 'w':
+                            timerWall = true;
+                            break;
+                        case 'c':
+                            timerCpu = true;
+                            break;
+                        case 'W':
+                            truncatedTimerWall = true;
+                            break;
+                        case 'C':
+                            truncatedTimerCpu = true;
+                            break;
+                        case 'g':
+                            timerGpu = true;
+                            break;
+                        default: {
+                            break;
+                        }
+                    }
+                }
+                benchmark->setTimersToShow(timerWall, truncatedTimerWall, timerCpu,
+                                           truncatedTimerCpu, timerGpu);
+            } else {
+                gLogger.logError("Missing arg for --timers\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--timeIndividualTiles")) {
+            benchmark->setTimeIndividualTiles(true);
+        } else if (0 == strcmp(*argv, "--min")) {
+            benchmark->setPrintMin(true);
+        } else if (0 == strcmp(*argv, "--logPerIter")) {
+            ++argv;
+            if (argv < stop) {
+                bool log = atoi(*argv) != 0;
+                benchmark->setLogPerIter(log);
+            } else {
+                gLogger.logError("Missing arg for --logPerIter\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--filter")) {
+            ++argv;
+            if (argv < stop) {
+                const char* colon = strchr(*argv, ':');
+                if (colon) {
+                    int type = -1;
+                    size_t typeLen = colon - *argv;
+                    for (size_t tIndex = 0; tIndex < kFilterTypesCount; ++tIndex) {
+                        if (typeLen == strlen(gFilterTypes[tIndex])
+                                && !strncmp(*argv, gFilterTypes[tIndex], typeLen)) {
+                            type = tIndex;
+                            break;
+                        }
+                    }
+                    if (type < 0) {
+                        SkString err;
+                        err.printf("Unknown type for --filter %s\n", *argv);
+                        gLogger.logError(err);
+                        PRINT_USAGE_AND_EXIT;
+                    }
+                    int flag = -1;
+                    size_t flagLen = strlen(*argv) - typeLen - 1;
+                    for (size_t fIndex = 0; fIndex < kFilterFlagsCount; ++fIndex) {
+                        if (flagLen == strlen(gFilterFlags[fIndex])
+                                && !strncmp(colon + 1, gFilterFlags[fIndex], flagLen)) {
+                            flag = 1 << fIndex;
+                            break;
+                        }
+                    }
+                    if (flag < 0) {
+                        SkString err;
+                        err.printf("Unknown flag for --filter %s\n", *argv);
+                        gLogger.logError(err);
+                        PRINT_USAGE_AND_EXIT;
+                    }
+                    for (int index = 0; index < SkDrawFilter::kTypeCount; ++index) {
+                        if (type != SkDrawFilter::kTypeCount && index != type) {
+                            continue;
+                        }
+                        drawFilters[index] = (sk_tools::PictureRenderer::DrawFilterFlags)
+                                (drawFilters[index] | flag);
+                    }
+                } else {
+                    SkString err;
+                    err.printf("Unknown arg for --filter %s : missing colon\n", *argv);
+                    gLogger.logError(err);
+                    PRINT_USAGE_AND_EXIT;
+                }
+            } else {
+                gLogger.logError("Missing arg for --filter\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (0 == strcmp(*argv, "--help") || 0 == strcmp(*argv, "-h")) {
+            PRINT_USAGE_AND_EXIT;
+        } else {
+            inputs->push_back(SkString(*argv));
+        }
+    }
+
+    if (numThreads > 1 && !useTiles) {
+        gLogger.logError("Multithreaded drawing requires tiled rendering.\n");
+        PRINT_USAGE_AND_EXIT;
+    }
+
+    if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
+        gLogger.logError("--pipe and --bbh cannot be used together\n");
+        PRINT_USAGE_AND_EXIT;
+    }
+
+    if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType &&
+        !gridSupported) {
+        gLogger.logError("'--bbh grid' is not compatible with specified --mode.\n");
+        PRINT_USAGE_AND_EXIT;
+    }
+
+    if (useTiles) {
+        SkASSERT(NULL == renderer);
+        sk_tools::TiledPictureRenderer* tiledRenderer;
+        if (isCopyMode) {
+            int x, y;
+            if (xTilesString != NULL) {
+                SkASSERT(yTilesString != NULL);
+                x = atoi(xTilesString);
+                y = atoi(yTilesString);
+                if (x <= 0 || y <= 0) {
+                    gLogger.logError("--tiles must be given values > 0\n");
+                    PRINT_USAGE_AND_EXIT;
+                }
+            } else {
+                x = y = 4;
+            }
+            tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
+            if (benchmark->timeIndividualTiles()) {
+                gLogger.logError("timeIndividualTiles is not compatible with copyTile\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else if (numThreads > 1) {
+            tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
+        } else {
+            tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
+        }
+        if (isPowerOf2Mode) {
+            int minWidth = atoi(widthString);
+            if (!SkIsPow2(minWidth) || minWidth < 0) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("-mode %s must be given a width"
+                         " value that is a power of two\n", mode);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+            tiledRenderer->setTileMinPowerOf2Width(minWidth);
+        } else if (sk_tools::is_percentage(widthString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                gLogger.logError(err.c_str());
+                PRINT_USAGE_AND_EXIT;
+            }
+            tiledRenderer->setTileWidthPercentage(atof(widthString));
+            if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
+                tiledRenderer->unref();
+                SkString err;
+                err.appendf("--mode %s must be given a width percentage > 0\n", mode);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else {
+            tiledRenderer->setTileWidth(atoi(widthString));
+            if (!(tiledRenderer->getTileWidth() > 0)) {
+                tiledRenderer->unref();
+                SkString err;
+                err.appendf("--mode %s must be given a width > 0\n", mode);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+        }
+
+        if (sk_tools::is_percentage(heightString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                gLogger.logError(err.c_str());
+                PRINT_USAGE_AND_EXIT;
+            }
+            tiledRenderer->setTileHeightPercentage(atof(heightString));
+            if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
+                tiledRenderer->unref();
+                SkString err;
+                err.appendf("--mode %s must be given a height percentage > 0\n", mode);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+        } else {
+            tiledRenderer->setTileHeight(atoi(heightString));
+            if (!(tiledRenderer->getTileHeight() > 0)) {
+                tiledRenderer->unref();
+                SkString err;
+                err.appendf("--mode %s must be given a height > 0\n", mode);
+                gLogger.logError(err);
+                PRINT_USAGE_AND_EXIT;
+            }
+        }
+        if (numThreads > 1) {
+#if SK_SUPPORT_GPU
+            if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
+                tiledRenderer->unref();
+                gLogger.logError("GPU not compatible with multithreaded tiling.\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+#endif
+        }
+        renderer.reset(tiledRenderer);
+        if (usePipe) {
+            gLogger.logError("Pipe rendering is currently not compatible with tiling.\n"
+                     "Turning off pipe.\n");
+        }
+    } else {
+        if (benchmark->timeIndividualTiles()) {
+            gLogger.logError("timeIndividualTiles requires tiled rendering.\n");
+            PRINT_USAGE_AND_EXIT;
+        }
+        if (usePipe) {
+            if (renderer.get() != NULL) {
+                gLogger.logError("Pipe is incompatible with other modes.\n");
+                PRINT_USAGE_AND_EXIT;
+            }
+            renderer.reset(SkNEW(sk_tools::PipePictureRenderer));
+        }
+    }
+    if (inputs->count() < 1) {
+        PRINT_USAGE_AND_EXIT;
+    }
+
+    if (NULL == renderer) {
+        renderer.reset(SkNEW(sk_tools::SimplePictureRenderer));
+    }
+
+    renderer->setBBoxHierarchyType(bbhType);
+    renderer->setDrawFilters(drawFilters, filtersName(drawFilters));
+    renderer->setGridSize(gridWidth, gridHeight);
+    renderer->setViewport(viewport);
+    renderer->setScaleFactor(scaleFactor);
+    benchmark->setRenderer(renderer);
+    benchmark->setRepeats(repeats);
+    benchmark->setDeviceType(deviceType);
+    benchmark->setLogger(&gLogger);
+    // Report current settings:
+    gLogger.logProgress(commandLine);
+}
+
+static int process_input(const SkString& input,
+                         sk_tools::PictureBenchmark& benchmark) {
+    SkOSFile::Iter iter(input.c_str(), "skp");
+    SkString inputFilename;
+    int failures = 0;
+    if (iter.next(&inputFilename)) {
+        do {
+            SkString inputPath;
+            sk_tools::make_filepath(&inputPath, input, inputFilename);
+            if (!run_single_benchmark(inputPath, benchmark)) {
+                ++failures;
+            }
+        } while(iter.next(&inputFilename));
+    } else if (SkStrEndsWith(input.c_str(), ".skp")) {
+        if (!run_single_benchmark(input, benchmark)) {
+            ++failures;
+        }
+    } else {
+        SkString warning;
+        warning.printf("Warning: skipping %s\n", input.c_str());
+        gLogger.logError(warning);
+    }
+    return failures;
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+#if SK_ENABLE_INST_COUNT
+    gPrintInstCount = true;
+#endif
+    SkAutoGraphics ag;
+
+    SkTArray<SkString> inputs;
+    sk_tools::PictureBenchmark benchmark;
+
+    parse_commandline(argc, argv, &inputs, &benchmark);
+
+    int failures = 0;
+    for (int i = 0; i < inputs.count(); ++i) {
+        failures += process_input(inputs[i], benchmark);
+    }
+
+    if (failures != 0) {
+        SkString err;
+        err.printf("Failed to run %i benchmarks.\n", failures);
+        gLogger.logError(err);
+        return 1;
+    }
+    return 0;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/build-tot-chromium.sh b/tools/build-tot-chromium.sh
new file mode 100755
index 0000000..9592462
--- /dev/null
+++ b/tools/build-tot-chromium.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+#
+# Author: Ravi Mistry
+#
+# Script to checkout and build a fresh copy of Chromium from head that uses a
+# writable, tip-of-tree Skia rather than the read-only, revision-locked Skia
+# specified in http://src.chromium.org/viewvc/chrome/trunk/src/DEPS
+#
+# Sample Usage:
+#   tools/build-tot-chromium.sh ~/chromiumtrunk
+
+if [[ $# -ne 1 ]] ; then
+  echo "usage: $0 chromium_location"
+  exit 1
+fi
+CHROMIUM_LOCATION=$1
+
+echo -e "\n\n========Deleting $CHROMIUM_LOCATION========\n\n"
+rm -rf $CHROMIUM_LOCATION
+
+mkdir $CHROMIUM_LOCATION
+cd $CHROMIUM_LOCATION
+gclient config https://src.chromium.org/chrome/trunk/src
+echo '
+solutions = [
+  { "name"        : "src",
+    "url"         : "https://src.chromium.org/chrome/trunk/src",
+    "deps_file"   : "DEPS",
+    "managed"     : True,
+    "custom_deps" : {
+      "src/third_party/skia": "https://skia.googlecode.com/svn/trunk",
+      "src/third_party/skia/gyp": None,
+      "src/third_party/skia/src": None,
+      "src/third_party/skia/include": None,
+    },
+    "safesync_url": "",
+  },
+]
+' > .gclient
+
+echo -e "\n\n========Starting gclient sync========\n\n"
+START_TIME=$SECONDS
+gclient sync
+END_TIME=$SECONDS
+echo -e "\n\n========gclient sync took $((END_TIME - START_TIME)) seconds========\n\n"
+
+cd src
+rm -rf out/Debug out/Release
+GYP_GENERATORS='ninja' ./build/gyp_chromium
+
+echo -e "\n\n========Starting ninja build========\n\n"
+START_TIME=$SECONDS
+ninja -C out/Release chrome
+END_TIME=$SECONDS
+echo -e "\n\n========ninja build took $((END_TIME - START_TIME)) seconds========\n\n"
+
+SVN_VERSION=`svnversion .`
+echo -e "\n\n========The Chromium & Skia versions are $SVN_VERSION========\n\n"
+
diff --git a/tools/compare_baselines.py b/tools/compare_baselines.py
new file mode 100644
index 0000000..7268eb7
--- /dev/null
+++ b/tools/compare_baselines.py
@@ -0,0 +1,209 @@
+'''
+Compares the gm results within the local checkout against those already
+committed to the Skia repository.
+
+Launch with --help to see more information.
+
+
+Copyright 2012 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+
+# common Python modules
+import fnmatch
+import optparse
+import os
+import shutil
+import tempfile
+
+# modules declared within this same directory
+import svn
+
+USAGE_STRING = 'Usage: %s [options]'
+HOWTO_STRING = '''
+To update the checked-in baselines across all platforms, follow these steps:
+
+cd .../trunk
+svn update
+svn stat   # and make sure there are no files awaiting svn commit
+make tools BUILDTYPE=Release
+python tools/download_baselines.py
+python tools/compare_baselines.py
+# view compare_baselines output in a browser and make sure it's reasonable
+# upload CL for review
+# validate that the diffs look right in the review tool
+# commit CL
+
+Note that the above instructions will only *update* already-checked-in
+baseline images; if you want to check in new baseline images (ones that the
+bots have been generating but we don't have a golden master for yet), you need
+to use download_baselines.py's --add-new-files option.
+'''
+HELP_STRING = '''
+
+Compares the gm results within the local checkout against those already
+committed to the Skia repository. Relies on skdiff to do the low-level
+comparison.
+
+''' + HOWTO_STRING
+
+TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
+
+OPTION_GM_BASEDIR = '--gm-basedir'
+DEFAULT_GM_BASEDIR = os.path.join(TRUNK_PATH, os.pardir, 'gm-expected')
+OPTION_PATH_TO_SKDIFF = '--path-to-skdiff'
+# default PATH_TO_SKDIFF is determined at runtime
+OPTION_SVN_GM_URL = '--svn-gm-url'
+DEFAULT_SVN_GM_URL = 'http://skia.googlecode.com/svn/gm-expected'
+
+def CopyAllFilesAddingPrefix(source_dir, dest_dir, prefix):
+    """Copy all files from source_dir into dest_dir, adding prefix to the name
+    of each one as we copy it.
+    prefixes.
+
+    @param source_dir
+    @param dest_dir where to save the copied files
+    @param prefix prefix to add to each filename when we make the copy
+    """
+    all_filenames = os.listdir(source_dir)
+    for filename in all_filenames:
+        source_path = os.path.join(source_dir, filename)
+        if os.path.isdir(source_path):
+            print 'skipping %s because it is a directory, not a file' % filename
+            continue
+        dest_path = os.path.join(dest_dir, '%s%s' % (prefix, filename))
+        shutil.copyfile(source_path, dest_path)
+
+def Flatten(source_dir, dest_dir, subdirectory_pattern):
+    """Copy all files from matching subdirectories under source_dir into
+    dest_dir, flattened into a single directory using subdirectory names as
+    prefixes.
+
+    @param source_dir
+    @param dest_dir where to save the copied files
+    @param subdirectory_pattern only copy files from subdirectories that match
+           this Unix-style filename pattern (e.g., 'base-*')
+    """
+    all_filenames = os.listdir(source_dir)
+    matching_filenames = fnmatch.filter(all_filenames, subdirectory_pattern)
+    for filename in matching_filenames:
+        source_path = os.path.join(source_dir, filename)
+        if not os.path.isdir(source_path):
+            print 'skipping %s because it is a file, not a directory' % filename
+            continue
+        print 'flattening directory %s' % source_path
+        CopyAllFilesAddingPrefix(source_dir=source_path, dest_dir=dest_dir,
+                                 prefix='%s_' % filename)
+
+def RunCommand(command):
+    """Run a command, raising an exception if it fails.
+
+    @param command the command as a single string
+    """
+    print 'running command [%s]...' % command
+    retval = os.system(command)
+    #if retval is not 0:
+    #    raise Exception('command [%s] failed' % command)
+
+def FindPathToSkDiff(user_set_path=None):
+    """Return path to an existing skdiff binary, or raise an exception if we
+    cannot find one.
+
+    @param user_set_path if None, the user did not specify a path, so look in
+           some likely places; otherwise, only check at this path
+    """
+    if user_set_path is not None:
+        if os.path.isfile(user_set_path):
+            return user_set_path
+        raise Exception('unable to find skdiff at user-set path %s' %
+                        user_set_path)
+    trunk_path = os.path.join(os.path.dirname(__file__), os.pardir)
+    possible_paths = [os.path.join(trunk_path, 'out', 'Release', 'skdiff'),
+                      os.path.join(trunk_path, 'out', 'Debug', 'skdiff')]
+    for try_path in possible_paths:
+        if os.path.isfile(try_path):
+            return try_path
+    raise Exception('cannot find skdiff in paths %s; maybe you need to '
+                    'specify the %s option or build skdiff?' % (
+                        possible_paths, OPTION_PATH_TO_SKDIFF))
+
+def CompareBaselines(gm_basedir, path_to_skdiff, svn_gm_url):
+    """Compare the gm results within gm_basedir against those already
+    committed to the Skia repository.
+
+    @param gm_basedir
+    @param path_to_skdiff
+    @param svn_gm_url base URL of Subversion repository where we store the
+           expected GM results
+    """
+    # Validate parameters, filling in default values if necessary and possible.
+    if not os.path.isdir(gm_basedir):
+        raise Exception('cannot find gm_basedir at %s; maybe you need to '
+                        'specify the %s option?' % (
+                            gm_basedir, OPTION_GM_BASEDIR))
+    path_to_skdiff = FindPathToSkDiff(path_to_skdiff)
+
+    tempdir_base = tempfile.mkdtemp()
+
+    # Download all checked-in baseline images to a temp directory
+    checkedin_dir = os.path.join(tempdir_base, 'checkedin')
+    os.mkdir(checkedin_dir)
+    svn.Svn(checkedin_dir).Checkout(svn_gm_url, '.')
+
+    # Flatten those checked-in baseline images into checkedin_flattened_dir
+    checkedin_flattened_dir = os.path.join(tempdir_base, 'checkedin_flattened')
+    os.mkdir(checkedin_flattened_dir)
+    Flatten(source_dir=checkedin_dir, dest_dir=checkedin_flattened_dir,
+            subdirectory_pattern='base-*')
+
+    # Flatten the local baseline images into local_flattened_dir
+    local_flattened_dir = os.path.join(tempdir_base, 'local_flattened')
+    os.mkdir(local_flattened_dir)
+    Flatten(source_dir=gm_basedir, dest_dir=local_flattened_dir,
+            subdirectory_pattern='base-*')
+
+    # Run skdiff to compare checkedin_flattened_dir against local_flattened_dir
+    diff_dir = os.path.join(tempdir_base, 'diffs')
+    os.mkdir(diff_dir)
+    RunCommand('%s %s %s %s' % (path_to_skdiff, checkedin_flattened_dir,
+                                local_flattened_dir, diff_dir))
+    print '\nskdiff results are ready in file://%s/index.html' % diff_dir
+    # TODO(epoger): delete tempdir_base tree to clean up after ourselves (but
+    # not before the user gets a chance to examine the results), and/or
+    # allow user to specify a different directory to write into?
+
+def RaiseUsageException():
+    raise Exception('%s\nRun with --help for more detail.' % (
+        USAGE_STRING % __file__))
+
+def Main(options, args):
+    """Allow other scripts to call this script with fake command-line args.
+    """
+    num_args = len(args)
+    if num_args != 0:
+        RaiseUsageException()
+    CompareBaselines(gm_basedir=options.gm_basedir,
+                     path_to_skdiff=options.path_to_skdiff,
+                     svn_gm_url=options.svn_gm_url)
+
+if __name__ == '__main__':
+    parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
+    parser.add_option(OPTION_GM_BASEDIR,
+                      action='store', type='string', default=DEFAULT_GM_BASEDIR,
+                      help='path to root of locally stored baseline images '
+                      'to compare against those checked into the svn repo; '
+                      'defaults to "%s"' % DEFAULT_GM_BASEDIR)
+    parser.add_option(OPTION_PATH_TO_SKDIFF,
+                      action='store', type='string', default=None,
+                      help='path to already-built skdiff tool; if not set, '
+                      'will search for it in typical directories near this '
+                      'script')
+    parser.add_option(OPTION_SVN_GM_URL,
+                      action='store', type='string', default=DEFAULT_SVN_GM_URL,
+                      help='URL of SVN repository within which we store the '
+                      'expected GM baseline images; defaults to "%s"' %
+                      DEFAULT_SVN_GM_URL)
+    (options, args) = parser.parse_args()
+    Main(options, args)
diff --git a/tools/copyright/fileparser.py b/tools/copyright/fileparser.py
new file mode 100644
index 0000000..a4051e2
--- /dev/null
+++ b/tools/copyright/fileparser.py
@@ -0,0 +1,92 @@
+'''
+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 datetime
+import re
+
+def CreateParser(filepath):
+    """Returns a Parser as appropriate for the file at this filepath.
+    """
+    if (filepath.endswith('.cpp') or
+        filepath.endswith('.h') or
+        filepath.endswith('.c')):
+        return CParser()
+    else:
+        return None
+
+
+class Parser(object):
+    """Base class for all language-specific parsers.
+    """
+
+    def __init__(self):
+        self._copyright_pattern = re.compile('copyright', re.IGNORECASE)
+        self._attribute_pattern = re.compile(
+            'copyright.*\D(\d{4})\W*(\w.*[\w.])', re.IGNORECASE)
+
+    def FindCopyrightBlock(self, comment_blocks):
+        """Given a list of comment block strings, return the one that seems
+        like the most likely copyright block.
+
+        Returns None if comment_blocks was empty, or if we couldn't find
+        a comment block that contains copyright info."""
+        if not comment_blocks:
+            return None
+        for block in comment_blocks:
+            if self._copyright_pattern.search(block):
+                return block
+
+    def GetCopyrightBlockAttributes(self, comment_block):
+        """Given a comment block, return a tuple of attributes: (year, holder).
+
+        If comment_block is None, or none of the attributes are found,
+        this will return (None, None)."""
+        if not comment_block:
+            return (None, None)
+        matches = self._attribute_pattern.findall(comment_block)
+        if not matches:
+            return (None, None)
+        first_match = matches[0]
+        return (first_match[0], first_match[1])
+
+
+class CParser(Parser):
+    """Parser that knows how to parse C/C++ files.
+    """
+
+    DEFAULT_YEAR = datetime.date.today().year
+    DEFAULT_HOLDER = 'Google Inc.'
+    COPYRIGHT_BLOCK_FORMAT = '''
+/*
+ * Copyright %s %s
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+'''
+
+    def __init__(self):
+        super(CParser, self).__init__()
+        self._comment_pattern = re.compile('/\*.*?\*/', re.DOTALL)
+
+    def FindAllCommentBlocks(self, file_contents):
+        """Returns a list of all comment blocks within these file contents.
+        """
+        return self._comment_pattern.findall(file_contents)
+
+    def CreateCopyrightBlock(self, year, holder):
+        """Returns a copyright block suitable for this language, with the
+        given attributes.
+
+        @param year year in which to hold copyright (defaults to DEFAULT_YEAR)
+        @param holder holder of copyright (defaults to DEFAULT_HOLDER)
+        """
+        if not year:
+            year = self.DEFAULT_YEAR
+        if not holder:
+            holder = self.DEFAULT_HOLDER
+        return self.COPYRIGHT_BLOCK_FORMAT % (year, holder)
diff --git a/tools/copyright/main.py b/tools/copyright/main.py
new file mode 100644
index 0000000..24969a7
--- /dev/null
+++ b/tools/copyright/main.py
@@ -0,0 +1,107 @@
+'''
+Copyright 2011 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+
+'''
+Updates all copyright headers within our code:
+- For files that already have a copyright header, the header is modified
+  while keeping the year and holder intact.
+- For files that don't have a copyright header, we add one with the current
+  year and default holder.
+
+@author: epoger@google.com
+'''
+
+import os
+import sys
+
+import fileparser
+
+# Only modify copyright stanzas if the copyright holder is one of these.
+ALLOWED_COPYRIGHT_HOLDERS = [
+    'Google Inc.',
+    'Skia',
+    'The Android Open Source Project',
+]
+
+def Main(root_directory):
+    """Run everything.
+
+    @param root_directory root directory within which to modify all files
+    """
+    filepaths = GetAllFilepaths(root_directory)
+    for filepath in filepaths:
+        parser = fileparser.CreateParser(filepath)
+        if not parser:
+            ReportWarning('cannot find a parser for file %s, skipping...' %
+                          filepath)
+            continue
+        old_file_contents = ReadFileIntoString(filepath)
+        comment_blocks = parser.FindAllCommentBlocks(old_file_contents)
+        if not comment_blocks:
+            ReportWarning('cannot find any comment blocks in file %s' %
+                          filepath)
+        old_copyright_block = parser.FindCopyrightBlock(comment_blocks)
+        if not old_copyright_block:
+            ReportWarning('cannot find copyright block in file %s' % filepath)
+        (year, holder) = parser.GetCopyrightBlockAttributes(old_copyright_block)
+        if holder and not ConfirmAllowedCopyrightHolder(holder):
+            ReportWarning(
+                'unrecognized copyright holder "%s" in file %s, skipping...' % (
+                    holder, filepath))
+            continue
+        new_copyright_block = parser.CreateCopyrightBlock(year, holder)
+        if old_copyright_block:
+            new_file_contents = old_file_contents.replace(
+                old_copyright_block, new_copyright_block, 1)
+        else:
+            new_file_contents = new_copyright_block + old_file_contents
+        WriteStringToFile(new_file_contents, filepath)
+
+def GetAllFilepaths(root_directory):
+    """Return a list of all files (absolute path for each one) within a tree.
+
+    @param root_directory root directory within which to find all files
+    """
+    path_list = []
+    for dirpath, dirnames, filenames in os.walk(root_directory):
+        for filename in filenames:
+            path_list.append(os.path.abspath(os.path.join(dirpath, filename)))
+    return path_list
+
+def ReportWarning(text):
+    """Report a warning, but continue.
+    """
+    print 'warning: %s' % text
+
+def ReportError(text):
+    """Report an error and raise an exception.
+    """
+    raise IOError(text)
+
+def ReadFileIntoString(filepath):
+    """Returns the full contents of this file as a string.
+    """
+    with open(filepath, 'r') as file_handle:
+        contents = file_handle.read()
+    return contents
+
+def WriteStringToFile(string, filepath):
+    """Writes this string out to filepath, replacing the file if it already
+    exists.
+    """
+    with open(filepath, 'w') as file_handle:
+        file_handle.write(string)
+
+def ConfirmAllowedCopyrightHolder(holder):
+    """Returns True if this is one of our allowed copyright holders.
+
+    @param holder copyright holder as a string
+    """
+    return holder in ALLOWED_COPYRIGHT_HOLDERS
+
+
+Main(sys.argv[1])
diff --git a/tools/download_baselines.py b/tools/download_baselines.py
new file mode 100644
index 0000000..3615b5d
--- /dev/null
+++ b/tools/download_baselines.py
@@ -0,0 +1,235 @@
+'''
+Downloads the actual gm results most recently generated by the Skia buildbots,
+and adds any new ones to SVN control.
+
+Launch with --help to see more information.
+
+
+Copyright 2011 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+
+# common Python modules
+import fnmatch
+import optparse
+import os
+import re
+import shutil
+import sys
+import tempfile
+
+# modules declared within this same directory
+import compare_baselines
+import svn
+
+USAGE_STRING = 'Usage: %s [options] [baseline_subdir]...'
+HELP_STRING = '''
+
+Downloads the actual gm results most recently generated by the Skia buildbots,
+and adds any new ones to SVN control.
+
+If no baseline_subdir is given, then this tool will download the most-recently
+generated actual gm results for ALL platforms.
+
+''' + compare_baselines.HOWTO_STRING
+
+# Base URL of SVN repository where buildbots store actual gm image results.
+GM_ACTUAL_URL = 'http://skia-autogen.googlecode.com/svn/gm-actual'
+
+# GM baseline image URL in regular Skia SVN repository
+GM_BASELINE_URL = 'https://skia.googlecode.com/svn/gm-expected'
+
+GM_EXPECTED_DIR = 'gm-expected'
+
+OPTION_ADD_NEW_FILES = '--add-new-files'
+OPTION_BUILDER_SUFFIX = '--builder-suffix'
+DEFAULT_BUILDER_SUFFIX = '32'
+OPTION_IGNORE_LOCAL_MODS = '--ignore-local-mods'
+
+def GetLatestResultsSvnUrl(svn, baseline_subdir, builder_suffix):
+    """Return SVN URL from which we can check out the MOST RECENTLY generated images for this
+    baseline type.
+
+    @param svn an Svn object we can use to call ListSubdirs()
+    @param baseline_subdir indicates which platform we want images for
+    @param builder_suffix if multiple builders uploaded actual GM images for this baseline type,
+           choose the one whose builder_name matches this suffix
+    """
+    root_url = '%s/%s' % (GM_ACTUAL_URL, baseline_subdir)
+    subdirs = sorted(svn.ListSubdirs(root_url))
+    num_subdirs = len(subdirs)
+    print('Within actual-results root URL %s, found these %d subdirs (presumably builder_names): %s'
+          % (root_url, num_subdirs, subdirs))
+
+    selected_subdir = None
+    if num_subdirs == 0:
+        print 'Found no builder_name subdirs, so reading actual images from the root_url itself.'
+        return root_url
+    elif num_subdirs == 1:
+        selected_subdir = subdirs[0]
+        print 'Found exactly one subdir in actual-results root_url: %s' % selected_subdir
+    else:
+        for possible_subdir in subdirs:
+            if possible_subdir.endswith(builder_suffix):
+                selected_subdir = possible_subdir
+                print 'Selected the first subdir ending in "%s": %s' % (
+                    builder_suffix, selected_subdir)
+                break
+
+    if selected_subdir:
+        return '%s/%s/%s' % (root_url, selected_subdir, baseline_subdir)
+    else:
+        raise Exception('none of these subdirs of %s ended in "%s": %s' % (
+            root_url, builder_suffix, subdirs))
+
+def GetBaselineSvnUrl(baseline_subdir):
+    """Return SVN URL from which we can check out the baseline images for this
+    baseline type.
+
+    @param baseline_subdir indicates which platform we want baselines for
+    """
+    return '%s/%s' % (GM_BASELINE_URL, baseline_subdir)
+
+def CopyMatchingFiles(source_dir, dest_dir, filename_pattern, only_copy_updates=False):
+    """Copy all files from source_dir that match filename_pattern, and save them (with their
+    original filenames) in dest_dir.
+
+    @param source_dir
+    @param dest_dir where to save the copied files
+    @param filename_pattern only copy files that match this Unix-style filename
+           pattern (e.g., '*.jpg')
+    @param only_copy_updates if True, only copy files that are already present in dest_dir
+    """
+    all_filenames = os.listdir(source_dir)
+    matching_filenames = fnmatch.filter(all_filenames, filename_pattern)
+    for filename in matching_filenames:
+        source_path = os.path.join(source_dir, filename)
+        dest_path = os.path.join(dest_dir, filename)
+        if only_copy_updates and not os.path.isfile(dest_path):
+            continue
+        shutil.copyfile(source_path, dest_path)
+
+def DownloadBaselinesForOnePlatform(baseline_subdir):
+    """Download most recently generated baseline images for a single platform,
+    and add any new ones to SVN control.
+
+    @param baseline_subdir
+    """
+    # Create repo_to_modify to handle the SVN repository we will add files to.
+    gm_dir = os.path.join(os.pardir, GM_EXPECTED_DIR) # Shouldn't assume we're in trunk...
+    try:
+        os.makedirs(gm_dir)
+    except:
+        pass
+    repo_to_modify = svn.Svn(gm_dir)
+    repo_to_modify.Checkout(GetBaselineSvnUrl(baseline_subdir), baseline_subdir)
+
+    # If there are any locally modified files in that directory, exit
+    # (so that we don't risk overwriting the user's previous work).
+    new_and_modified_files = repo_to_modify.GetNewAndModifiedFiles()
+    if not options.ignore_local_mods:
+        if new_and_modified_files:
+            raise Exception('Exiting because there are already new and/or '
+                            'modified files in %s.  To continue in spite of '
+                            'that, run with %s option.' % (
+                                baseline_subdir, OPTION_IGNORE_LOCAL_MODS))
+
+    # Download actual gm images into a separate repo in a temporary directory.
+    actual_dir = tempfile.mkdtemp()
+    actual_repo = svn.Svn(actual_dir)
+    print 'Using %s as a temp dir' % actual_dir
+    actual_url = GetLatestResultsSvnUrl(svn=actual_repo, baseline_subdir=baseline_subdir,
+                                        builder_suffix=options.builder_suffix)
+    print 'Reading actual buildbot GM results from %s' % actual_url
+    actual_repo.Checkout(actual_url, '.')
+
+    # Copy any of those files we are interested in into repo_to_modify,
+    # and then delete the temporary directory.
+    CopyMatchingFiles(source_dir=actual_dir,
+                      dest_dir=os.path.join(gm_dir, baseline_subdir),
+                      filename_pattern='*.png',
+                      only_copy_updates=(not options.add_new_files))
+    shutil.rmtree(actual_dir)
+    actual_repo = None
+
+    # Add any new files to SVN control (if we are running with add_new_files).
+    if options.add_new_files:
+        new_files = repo_to_modify.GetNewFiles()
+        if new_files:
+            repo_to_modify.AddFiles(sorted(new_files))
+
+    # Set the mimetype property on any new/modified image files in
+    # baseline_subdir.  (We used to set the mimetype property on *all* image
+    # files in the directory, even those whose content wasn't changing,
+    # but that caused confusion.  See
+    # http://code.google.com/p/skia/issues/detail?id=618 .)
+    modified_files = repo_to_modify.GetNewAndModifiedFiles()
+    repo_to_modify.SetProperty(sorted(fnmatch.filter(modified_files, '*.png')),
+                               svn.PROPERTY_MIMETYPE, 'image/png')
+    repo_to_modify.SetProperty(sorted(fnmatch.filter(modified_files, '*.pdf')),
+                               svn.PROPERTY_MIMETYPE, 'application/pdf')
+
+def RaiseUsageException():
+    raise Exception('%s\nRun with --help for more detail.' % (
+        USAGE_STRING % __file__))
+
+def Main(options, args):
+    """Allow other scripts to call this script with fake command-line args.
+    """
+    # If no platforms are specified, do 'em all.
+    num_args = len(args)
+    if num_args == 0:
+        # TODO(epoger): automate the default set of platforms. We want to ensure
+        # that the user gets all of the platforms that the bots are running,
+        # not just whatever subdirectories he happens to have checked out...
+        # See http://code.google.com/p/skia/issues/detail?id=678
+        # Now that we have added Svn.ListSubdirs(), we should be able to do this
+        # pretty easily...
+        #
+        # For now, I generate this list using these Unix commands:
+        # svn ls http://skia.googlecode.com/svn/gm-expected | grep ^base | sort >/tmp/baselines
+        # svn ls http://skia-autogen.googlecode.com/svn/gm-actual | grep ^base | sort >/tmp/actual
+        # comm -1 -2 /tmp/baselines /tmp/actual
+        args = [
+            'base-android-galaxy-nexus',
+            'base-android-nexus-7',
+            'base-android-nexus-s',
+            'base-android-xoom',
+            'base-macmini',
+            'base-macmini-lion-float',
+            'base-shuttle-win7-intel-float',
+            'base-shuttle_ubuntu12_ati5770',
+            'base-shuttle-win7-intel-angle',
+            'base-shuttle-win7-intel-directwrite',
+            ]
+
+    # Trim all subdir names.
+    baseline_subdirs = []
+    for arg in args:
+        baseline_subdirs.append(arg.rstrip(os.sep))
+
+    # Process the subdirs, one at a time.
+    for baseline_subdir in baseline_subdirs:
+        DownloadBaselinesForOnePlatform(baseline_subdir=baseline_subdir)
+
+if __name__ == '__main__':
+    parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
+    parser.add_option(OPTION_ADD_NEW_FILES,
+                      action='store_true', default=False,
+                      help='in addition to downloading new versions of '
+                      'existing baselines, also download baselines that are '
+                      'not under SVN control yet')
+    parser.add_option(OPTION_BUILDER_SUFFIX,
+                      action='store', type='string', default=DEFAULT_BUILDER_SUFFIX,
+                      help='if multiple builders have uploaded actual GM images '
+                      'for this platform, download the images uploaded by the '
+                      'builder whose name ends in this suffix; defaults to '
+                      '"%s".' % DEFAULT_BUILDER_SUFFIX)
+    parser.add_option(OPTION_IGNORE_LOCAL_MODS,
+                      action='store_true', default=False,
+                      help='allow tool to run even if there are already '
+                      'local modifications in the baseline_subdir')
+    (options, args) = parser.parse_args()
+    Main(options, args)
diff --git a/tools/doxygen_footer.txt b/tools/doxygen_footer.txt
new file mode 100644
index 0000000..51cee38
--- /dev/null
+++ b/tools/doxygen_footer.txt
@@ -0,0 +1,9 @@
+<!--
+  This is the static footer that Doxygen appends to every page.
+  It is not properly formed html; it must end with unbalanced /body
+  and /html tags.
+-->
+<hr class="footer"/>
+<iframe src="../iframe_footer.html" width="100%" frameborder=0></iframe>
+</body>
+</html>
diff --git a/tools/filtermain.cpp b/tools/filtermain.cpp
new file mode 100644
index 0000000..3715b3e
--- /dev/null
+++ b/tools/filtermain.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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 "SkDebugCanvas.h"
+#include "SkDevice.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkPicturePlayback.h"
+#include "SkPictureRecord.h"
+#include "SkStream.h"
+#include "picture_utils.h"
+#include "path_utils.h"
+
+static void usage() {
+    SkDebugf("Usage: filter -i inFile [-o outFile] [--input-dir path] [--output-dir path]\n");
+    SkDebugf("                        [-h|--help]\n\n");
+    SkDebugf("    -i inFile  : file to file.\n");
+    SkDebugf("    -o outFile : result of filtering.\n");
+    SkDebugf("    --input-dir : process all files in dir with .skp extension.\n");
+    SkDebugf("    --output-dir : results of filtering the input dir.\n");
+    SkDebugf("    -h|--help  : Show this help message.\n");
+}
+
+// Is the supplied paint simply a color?
+static bool is_simple(const SkPaint& p) {
+    return NULL == p.getPathEffect() &&
+           NULL == p.getShader() &&
+           NULL == p.getXfermode() &&
+           NULL == p.getMaskFilter() &&
+           NULL == p.getColorFilter() &&
+           NULL == p.getRasterizer() &&
+           NULL == p.getLooper() &&
+           NULL == p.getImageFilter();
+}
+
+static int filter_picture(const SkString& inFile, const SkString& outFile) {
+    SkPicture* inPicture = NULL;
+
+    SkFILEStream inStream(inFile.c_str());
+    if (inStream.isValid()) {
+        inPicture = SkNEW_ARGS(SkPicture, (&inStream, NULL, &SkImageDecoder::DecodeStream));
+    }
+
+    if (NULL == inPicture) {
+        SkDebugf("Could not read file %s\n", inFile.c_str());
+        return -1;
+    }
+
+    SkDebugCanvas debugCanvas(inPicture->width(), inPicture->height());
+    debugCanvas.setBounds(inPicture->width(), inPicture->height());
+    inPicture->draw(&debugCanvas);
+
+    const SkTDArray<SkDrawCommand*>& commands = debugCanvas.getDrawCommands();
+
+    for (int i = 0; i < commands.count(); ++i) {
+        // Check for:
+        //    SAVE_LAYER
+        //      DRAW_BITMAP_RECT_TO_RECT
+        //    RESTORE
+        // where the saveLayer's color can be moved into the drawBitmapRect
+        if (SAVE_LAYER == commands[i]->getType() && commands.count() > i+2) {
+            if (DRAW_BITMAP_RECT_TO_RECT == commands[i+1]->getType() &&
+                RESTORE == commands[i+2]->getType()) {
+                SaveLayer* sl = (SaveLayer*) commands[i];
+                DrawBitmapRect* dbmr = (DrawBitmapRect*) commands[i+1];
+
+                const SkPaint* p0 = sl->paint();
+                SkPaint* p1 = dbmr->paint();
+
+                if (NULL == p0) {
+                    commands[i]->setVisible(false);
+                    commands[i+2]->setVisible(false);
+                } else if (NULL == p1) {
+                    commands[i]->setVisible(false);
+                    dbmr->setPaint(*p0);
+                    commands[i+2]->setVisible(false);
+                } else if (is_simple(*p0) &&
+                           (SkColorGetR(p0->getColor()) == SkColorGetR(p1->getColor())) &&
+                           (SkColorGetG(p0->getColor()) == SkColorGetG(p1->getColor())) &&
+                           (SkColorGetB(p0->getColor()) == SkColorGetB(p1->getColor()))) {
+                    commands[i]->setVisible(false);
+                    SkColor newColor = SkColorSetA(p1->getColor(),
+                                                   SkColorGetA(p0->getColor()));
+                    p1->setColor(newColor);
+                    commands[i+2]->setVisible(false);
+                }
+            }
+        }
+    }
+
+    if (!outFile.isEmpty()) {
+        SkPicture outPicture;
+
+        SkCanvas* canvas = outPicture.beginRecording(inPicture->width(), inPicture->height());
+        debugCanvas.draw(canvas);
+        outPicture.endRecording();
+
+        SkFILEWStream outStream(outFile.c_str());
+
+        outPicture.serialize(&outStream);
+    }
+
+    return 0;
+}
+
+// This function is not marked as 'static' so it can be referenced externally
+// in the iOS build.
+int tool_main(int argc, char** argv); // suppress a warning on mac
+
+int tool_main(int argc, char** argv) {
+    SkGraphics::Init();
+
+    if (argc < 3) {
+        usage();
+        return -1;
+    }
+
+    SkString inFile, outFile, inDir, outDir;
+
+    char* const* stop = argv + argc;
+    for (++argv; argv < stop; ++argv) {
+        if (strcmp(*argv, "-i") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                inFile.set(*argv);
+            } else {
+                SkDebugf("missing arg for -i\n");
+                usage();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--input-dir") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                inDir.set(*argv);
+            } else {
+                SkDebugf("missing arg for --input-dir\n");
+                usage();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--output-dir") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                outDir.set(*argv);
+            } else {
+                SkDebugf("missing arg for --output-dir\n");
+                usage();
+                return -1;
+            }
+        } else if (strcmp(*argv, "-o") == 0) {
+            argv++;
+            if (argv < stop && **argv) {
+                outFile.set(*argv);
+            } else {
+                SkDebugf("missing arg for -o\n");
+                usage();
+                return -1;
+            }
+        } else if (strcmp(*argv, "--help") == 0 || strcmp(*argv, "-h") == 0) {
+            usage();
+            return 0;
+        } else {
+            SkDebugf("unknown arg %s\n", *argv);
+            usage();
+            return -1;
+        }
+    }
+
+    SkOSFile::Iter iter(inDir.c_str(), "skp");
+
+    SkString inputFilename, outputFilename;
+    if (iter.next(&inputFilename)) {
+
+        do {
+            sk_tools::make_filepath(&inFile, inDir, inputFilename);
+            if (!outDir.isEmpty()) {
+                sk_tools::make_filepath(&outFile, outDir, inputFilename);
+            }
+            SkDebugf("Executing %s\n", inputFilename.c_str());
+            filter_picture(inFile, outFile);
+        } while(iter.next(&inputFilename));
+
+    } else if (!inFile.isEmpty()) {
+        filter_picture(inFile, outFile);
+    } else {
+        usage();
+        return -1;
+    }
+
+    SkGraphics::Term();
+    return 0;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/git-skia-verify b/tools/git-skia-verify
new file mode 100644
index 0000000..c9a6c9d
--- /dev/null
+++ b/tools/git-skia-verify
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# Copyright 2012 Intel Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+#
+# This script builds and runs GM in current workspace with another Skia
+# revision user specifies, and then compares their results. This script is
+# useful when developers want to know whether their changes would cause any
+# regression.
+#
+# As the name of this script tells, it only works for git repository. :)
+#
+# Usage:
+#   Put this script into where your PATH can find it.
+#   And then invoke:
+#       $ git skia-verify [sha1-to-compare-default-is-HEAD^]
+#   It would delete {before,after,diff} directory under the current directory,
+#   so be warned!
+#   After it's done, check out diff/index.html for the possible differences.
+
+
+function say() {
+    # set color to yellow
+    tput setaf 3
+    echo $1
+    tput sgr0
+}
+
+function warn() {
+    # set color to red
+    tput setaf 1
+    echo $1
+    tput sgr0
+}
+
+REVISION="HEAD^"
+
+if [[ $# -eq 1 ]];
+then
+    REVISION="$1"
+fi
+
+tput clear
+
+say "Checking sanity..."
+git diff --exit-code > /dev/null
+if [[ $? -ne 0 ]];
+then
+    warn "You have uncommitted changes!"
+    exit 1
+fi
+git diff --cached --exit-code > /dev/null
+if [[ $? -ne 0 ]];
+then
+    warn "You have uncommitted changes!"
+    exit 1
+fi
+
+say "Preparing Directories..."
+rm -rf {before,after,diff}
+mkdir {before,after,diff}
+
+PREVIOUS_BRANCH=`git branch --no-color | grep "^*" | awk '{ print $2}'`
+
+say "Running GM for current revision..."
+./gyp_skia
+make BUILDTYPE=Release -j10
+if [[ $? -ne 0 ]];
+then
+    warn "Failed to compile!"
+    exit 1
+fi
+./out/Release/gm -w after
+
+say "Running GM for revision $REVISION..."
+# we run the test in a detached branch
+git checkout --detach "$REVISION"
+./gyp_skia
+make BUILDTYPE=Release -j10
+if [[ $? -ne 0 ]];
+then
+    warn "Failed to compile!"
+    say "Back to original revision..."
+    git checkout "$PREVIOUS_BRANCH"
+    exit 1
+fi
+./out/Release/gm -w before
+
+say "Back to original revision..."
+git checkout "$PREVIOUS_BRANCH"
+
+say "Comparing..."
+./out/Release/skdiff before after diff
diff --git a/tools/merge_static_libs.py b/tools/merge_static_libs.py
new file mode 100755
index 0000000..842be18
--- /dev/null
+++ b/tools/merge_static_libs.py
@@ -0,0 +1,66 @@
+#!/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.
+
+import os
+import shutil
+import subprocess
+import sys
+import tempfile
+
+def _Usage():
+  print 'Usage: merge_static_libs OUTPUT_LIB INPUT_LIB [INPUT_LIB]*'
+  sys.exit(1)
+
+def MergeLibs(in_libs, out_lib):
+  """ Merges multiple static libraries into one.
+  
+  in_libs: list of paths to static libraries to be merged
+  out_lib: path to the static library which will be created from in_libs
+  """
+  if os.name == 'posix':
+    tempdir = tempfile.mkdtemp()
+    abs_in_libs = []
+    for in_lib in in_libs:
+      abs_in_libs.append(os.path.abspath(in_lib))
+    curdir = os.getcwd()
+    os.chdir(tempdir)
+    objects = []
+    ar = os.environ.get('AR', 'ar')
+    for in_lib in abs_in_libs:
+      proc = subprocess.Popen([ar, '-t', in_lib], stdout=subprocess.PIPE)
+      proc.wait()
+      obj_str = proc.communicate()[0]
+      current_objects = obj_str.rstrip().split('\n')
+      proc = subprocess.Popen([ar, '-x', in_lib], stdout=subprocess.PIPE,
+                              stderr=subprocess.STDOUT)
+      proc.wait()
+      if proc.poll() == 0:
+        # The static library is non-thin, and we extracted objects
+        for object in current_objects:
+          objects.append(os.path.abspath(object))
+      elif 'thin archive' in proc.communicate()[0]:
+        # The static library is thin, so it contains the paths to its objects
+        for object in current_objects:
+          objects.append(object)
+      else:
+        raise Exception('Failed to extract objects from %s.' % in_lib)
+    os.chdir(curdir)
+    if not subprocess.call([ar, '-crs', out_lib] + objects) == 0:
+      raise Exception('Failed to add object files to %s' % out_lib)
+    shutil.rmtree(tempdir)
+  elif os.name == 'nt':
+    subprocess.call(['lib', '/OUT:%s' % out_lib] + in_libs)
+  else:
+    raise Exception('Error: Your platform is not supported')
+
+def Main():
+  if len(sys.argv) < 3:
+    _Usage()
+  out_lib = sys.argv[1]
+  in_libs = sys.argv[2:]
+  MergeLibs(in_libs, out_lib)
+
+if '__main__' == __name__:
+  sys.exit(Main())
diff --git a/tools/path_utils.cpp b/tools/path_utils.cpp
new file mode 100644
index 0000000..b1dad34
--- /dev/null
+++ b/tools/path_utils.cpp
@@ -0,0 +1,134 @@
+/*
+ * 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 "path_utils.h"
+#include "SkPath.h"
+#include "SkStream.h"
+
+namespace sk_tools {
+    static int gCurPathID = 0;
+
+    void dump_path_prefix(SkFILEWStream* pathStream) {
+        if (NULL == pathStream) {
+            return;
+        }
+
+        pathStream->writeText("#include \"SkScalar.h\"\n");
+        pathStream->writeText("#include \"SkPoint.h\"\n");
+        pathStream->writeText("#include \"SkBitmap.h\"\n");
+        pathStream->writeText("#include \"SkDevice.h\"\n");
+        pathStream->writeText("#include \"SkString.h\"\n");
+        pathStream->writeText("#include \"SkImageEncoder.h\"\n");
+    }
+
+    void dump_path(SkFILEWStream* pathStream, const SkPath& path) {
+        if (NULL == pathStream) {
+            return;
+        }
+
+        static const int kMaxPts = 200;
+        static const int kMaxVerbs = 200;
+
+        int numPts = path.countPoints();
+        int numVerbs = path.countVerbs();
+
+        SkASSERT(numPts <= kMaxPts);
+        SkASSERT(numVerbs <= kMaxVerbs);
+
+        SkPoint pts[kMaxPts];
+        uint8_t verbs[kMaxVerbs];
+
+        path.getPoints(pts, kMaxPts);
+        path.getVerbs(verbs, kMaxVerbs);
+
+        const char* gStrs[] = {
+            "kMove_Verb",
+            "kLine_Verb",
+            "kQuad_Verb",
+            "kCubic_Verb",
+            "kClose_Verb",
+            "kDone_Verb"
+        };
+
+        pathStream->writeText("static const int numPts");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText(" = ");
+        pathStream->writeDecAsText(numPts);
+        pathStream->writeText(";\n");
+
+        pathStream->writeText("SkPoint pts");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText("[] = {\n");
+
+        for (int i = 0; i < numPts; ++i) {
+            SkString temp;
+
+            pathStream->writeText("      { ");
+            temp.appendScalar(pts[i].fX);
+            temp.append("f, ");
+            temp.appendScalar(pts[i].fY);
+            temp.append("f },\n");
+            pathStream->writeText(temp.c_str());
+        }
+        pathStream->writeText("};\n");
+
+        pathStream->writeText("static const int numVerbs");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText(" = ");
+        pathStream->writeDecAsText(numVerbs);
+        pathStream->writeText(";\n");
+
+        pathStream->writeText("uint8_t verbs");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText("[] = {\n");
+
+        for (int i = 0; i < numVerbs; ++i) {
+            pathStream->writeText("\tSkPath::");
+            pathStream->writeText(gStrs[verbs[i]]);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+
+        gCurPathID++;
+    }
+
+    void dump_path_suffix(SkFILEWStream* pathStream) {
+        if (NULL == pathStream) {
+            return;
+        }
+
+        pathStream->writeText("int numPaths = ");
+        pathStream->writeDecAsText(gCurPathID);
+        pathStream->writeText(";\n");
+
+        pathStream->writeText("int sizes[] = {\n");
+        for (int i = 0; i < gCurPathID; ++i) {
+            pathStream->writeText("\t numPts");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(", numVerbs");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+
+        pathStream->writeText("const SkPoint* points[] = {\n");
+        for (int i = 0; i < gCurPathID; ++i) {
+            pathStream->writeText("\tpts");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+
+        pathStream->writeText("const uint8_t* verbs[] = {\n");
+        for (int i = 0; i < gCurPathID; ++i) {
+            pathStream->writeText("\t(const uint8_t*)verbs");
+            pathStream->writeDecAsText(i);
+            pathStream->writeText(",\n");
+        }
+        pathStream->writeText("};\n");
+    }
+}
diff --git a/tools/path_utils.h b/tools/path_utils.h
new file mode 100644
index 0000000..d05ad16
--- /dev/null
+++ b/tools/path_utils.h
@@ -0,0 +1,47 @@
+/*
+ * 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 path_utils_DEFINED
+#define path_utils_DEFINED
+
+class SkFILEWStream;
+class SkPath;
+
+namespace sk_tools {
+    // These utilities help write paths to a .cpp file in a compileable form.
+    // To use call them in the order:
+    //      dump_path_prefix - once per program invocation
+    //      dump_path - once for each path of interest
+    //      dump_path_suffix - once per program invocation
+    //
+    // The output system relies on a global current path ID and assumes that
+    // only one set of aggregation arrays will be written per program
+    // invocation. These utilities are not thread safe.
+
+    // Write of the headers needed to compile the resulting .cpp file
+    void dump_path_prefix(SkFILEWStream* pathStream);
+
+    // Write out a single path in the form:
+    //      static const int numPts# = ...;
+    //      SkPoint pts#[] = { ... };
+    //      static const int numVerbs# = ...;
+    //      uint8_t verbs#[] = { ... };
+    // Where # is a globally unique identifier
+    void dump_path(SkFILEWStream* pathStream, const SkPath& path);
+
+    // Write out structures to aggregate info about the written paths:
+    //      int numPaths = ...;
+    //      int sizes[] = {
+    //          numPts#, numVerbs#,
+    //          ...
+    //      };
+    //      const SkPoint* points[] = { pts#, ... };
+    //      const uint8_t* verbs[] = { verbs#, ... };
+    void dump_path_suffix(SkFILEWStream* pathStream);
+}
+
+#endif
diff --git a/tools/picture_utils.cpp b/tools/picture_utils.cpp
new file mode 100644
index 0000000..7a5e156
--- /dev/null
+++ b/tools/picture_utils.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 "picture_utils.h"
+#include "SkColorPriv.h"
+#include "SkBitmap.h"
+#include "SkPicture.h"
+#include "SkString.h"
+#include "SkStream.h"
+
+namespace sk_tools {
+    void force_all_opaque(const SkBitmap& bitmap) {
+        SkASSERT(NULL == bitmap.getTexture());
+        SkASSERT(SkBitmap::kARGB_8888_Config == bitmap.config());
+        if (NULL != bitmap.getTexture() || SkBitmap::kARGB_8888_Config == bitmap.config()) {
+            return;
+        }
+
+        SkAutoLockPixels lock(bitmap);
+        for (int y = 0; y < bitmap.height(); y++) {
+            for (int x = 0; x < bitmap.width(); x++) {
+                *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
+            }
+        }
+    }
+
+    void make_filepath(SkString* path, const SkString& dir, const SkString& name) {
+        size_t len = dir.size();
+        path->set(dir);
+        if (0 < len  && '/' != dir[len - 1]) {
+            path->append("/");
+        }
+        path->append(name);
+    }
+
+    namespace {
+        bool is_path_seperator(const char chr) {
+#if defined(SK_BUILD_FOR_WIN)
+            return chr == '\\' || chr == '/';
+#else
+            return chr == '/';
+#endif
+        }
+    }
+
+    void get_basename(SkString* basename, const SkString& path) {
+        if (path.size() == 0) {
+            basename->reset();
+            return;
+        }
+
+        size_t end = path.size() - 1;
+
+        // Paths pointing to directories often have a trailing slash,
+        // we remove it so the name is not empty
+        if (is_path_seperator(path[end])) {
+            if (end == 0) {
+                basename->reset();
+                return;
+            }
+
+            end -= 1;
+        }
+
+        size_t i = end;
+        do {
+            --i;
+            if (is_path_seperator(path[i])) {
+                  const char* basenameStart = path.c_str() + i + 1;
+                  size_t basenameLength = end - i;
+                  basename->set(basenameStart, basenameLength);
+                  return;
+            }
+        } while (i > 0);
+
+        basename->set(path.c_str(), end + 1);
+    }
+
+    bool is_percentage(const char* const string) {
+        SkString skString(string);
+        return skString.endsWith("%");
+    }
+
+    void setup_bitmap(SkBitmap* bitmap, int width, int height) {
+        bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+        bitmap->allocPixels();
+        bitmap->eraseColor(SK_ColorTRANSPARENT);
+    }
+}
diff --git a/tools/picture_utils.h b/tools/picture_utils.h
new file mode 100644
index 0000000..4c2f6e5
--- /dev/null
+++ b/tools/picture_utils.h
@@ -0,0 +1,47 @@
+/*
+ * 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 picture_utils_DEFINED
+#define picture_utils_DEFINED
+
+#include "SkTypes.h"
+
+class SkBitmap;
+class SkFILEStream;
+class SkPicture;
+class SkString;
+
+namespace sk_tools {
+    // since PNG insists on unpremultiplying our alpha, we take no precision
+    // chances and force all pixels to be 100% opaque, otherwise on compare we
+    // may not get a perfect match.
+    //
+    // This expects a bitmap with a config type of 8888 and for the pixels to
+    // not be on the GPU.
+    void force_all_opaque(const SkBitmap& bitmap);
+
+    // Creates a posix style filepath by concatenating name onto dir with a
+    // forward slash into path.
+    void make_filepath(SkString* path, const SkString&, const SkString& name);
+
+    // Returns the last part of the path (file name or leaf directory name)
+    //
+    // This basically just looks for a foward slash or backslash (windows
+    // only).
+    void get_basename(SkString* basename, const SkString& path);
+
+    // Returns true if the string ends with %
+    bool is_percentage(const char* const string);
+
+    // Prepares the bitmap so that it can be written.
+    //
+    // Specifically, it configures the bitmap, allocates pixels and then
+    // erases the pixels to transparent black.
+    void setup_bitmap(SkBitmap* bitmap, int width, int height);
+}
+
+#endif  // picture_utils_DEFINED
diff --git a/tools/pinspect.cpp b/tools/pinspect.cpp
new file mode 100644
index 0000000..b5a6727
--- /dev/null
+++ b/tools/pinspect.cpp
@@ -0,0 +1,82 @@
+/*
+ * 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 "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkDumpCanvas.h"
+
+static SkPicture* inspect(const char path[]) {
+    SkFILEStream stream(path);
+    if (!stream.isValid()) {
+        printf("-- Can't open '%s'\n", path);
+        return NULL;
+    }
+
+    printf("Opening '%s'...\n", path);
+
+    {
+        int32_t header[3];
+        if (stream.read(header, sizeof(header)) != sizeof(header)) {
+            printf("-- Failed to read header (12 bytes)\n");
+            return NULL;
+        }
+        printf("version:%d width:%d height:%d\n", header[0], header[1], header[2]);
+    }
+
+    stream.rewind();
+    SkPicture* pic = SkNEW_ARGS(SkPicture, (&stream));
+    printf("picture size:[%d %d]\n", pic->width(), pic->height());
+    return pic;
+}
+
+static void dumpOps(SkPicture* pic) {
+#ifdef SK_DEVELOPER
+    SkDebugfDumper dumper;
+    SkDumpCanvas canvas(&dumper);
+    canvas.drawPicture(*pic);
+#else
+    printf("SK_DEVELOPER mode not enabled\n");
+#endif
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+    if (argc < 2) {
+        printf("Usage: pinspect [--dump-ops] filename [filename ...]\n");
+        return 1;
+    }
+
+    bool doDumpOps = false;
+
+    int index = 1;
+    if (!strcmp(argv[index], "--dump-ops")) {
+        index += 1;
+        doDumpOps = true;
+    }
+
+    for (; index < argc; ++index) {
+        SkAutoTUnref<SkPicture> pic(inspect(argv[index]));
+        if (doDumpOps) {
+            dumpOps(pic);
+        }
+        if (index < argc - 1) {
+            printf("\n");
+        }
+    }
+    return 0;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/rebaseline.py b/tools/rebaseline.py
new file mode 100755
index 0000000..21964d4
--- /dev/null
+++ b/tools/rebaseline.py
@@ -0,0 +1,77 @@
+#!/usr/bin/python
+
+'''
+Copyright 2012 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+
+'''
+Rebaselines the given GM tests, on all bots and all configurations.
+Must be run from the gm-expected directory.  If run from a git or SVN
+checkout, the files will be added to the staging area for commit.
+'''
+
+import os, subprocess, sys, tempfile
+
+pairs = [ 
+   ['base-shuttle-win7-intel-float',
+    'Skia_Shuttle_Win7_Intel_Float_Release_32'],
+   ['base-shuttle-win7-intel-angle',
+    'Skia_Shuttle_Win7_Intel_Float_ANGLE_Release_32'],
+   ['base-shuttle-win7-intel-directwrite',
+    'Skia_Shuttle_Win7_Intel_Float_DirectWrite_Release_32'],
+   ['base-shuttle_ubuntu12_ati5770',
+    'Skia_Shuttle_Ubuntu12_ATI5770_Float_Release_64'],
+   ['base-macmini',
+    'Skia_Mac_Float_Release_32'],
+   ['base-macmini-lion-float',
+    'Skia_MacMiniLion_Float_Release_32'],
+   ['base-android-galaxy-nexus',
+    'Skia_GalaxyNexus_4-1_Float_Release_32'],
+   ['base-android-nexus-7',
+    'Skia_Nexus7_4-1_Float_Release_32'],
+   ['base-android-nexus-s',
+    'Skia_NexusS_4-1_Float_Release_32'],
+   ['base-android-xoom',
+    'Skia_Xoom_4-1_Float_Release_32'],
+]
+
+if len(sys.argv) < 2:
+    print 'Usage:  ' + os.path.basename(sys.argv[0]) + ' <testname> '
+    '[ <testname> ... ]'
+    exit(1)
+
+is_svn_checkout = os.path.exists('.svn') or os.path.exists(os.path.join('..', '.svn') )
+is_git_checkout = os.path.exists('.git') or os.path.exists(os.path.join('..', '.git'))
+
+for testname in sys.argv[1:]:
+    for pair in pairs:
+        if (pair[0] == 'base-shuttle-win7-intel-angle'):
+            testtypes = [ 'angle' ]
+        else:
+            testtypes = [ '565', '8888', 'gpu', 'pdf', 'mesa' ]
+        print pair[0] + ':'
+        for testtype in testtypes:
+            infilename = testname + '_' + testtype + '.png'
+            print infilename
+
+            url = 'http://skia-autogen.googlecode.com/svn/gm-actual/' + pair[0] + '/' + pair[1] + '/' + pair[0] + '/' + infilename
+            cmd = [ 'curl', '--fail', '--silent', url ]
+            temp = tempfile.NamedTemporaryFile()
+            ret = subprocess.call(cmd, stdout=temp)
+            if ret != 0:
+                print 'Couldn\'t fetch ' + url
+                continue
+            outfilename = os.path.join(pair[0], infilename);
+            cmd = [ 'cp', temp.name, outfilename ]
+            subprocess.call(cmd);
+            if is_svn_checkout:
+                cmd = [ 'svn', 'add', '--quiet', outfilename ]
+                subprocess.call(cmd)
+                cmd = [ 'svn', 'propset', '--quiet', 'svn:mime-type', 'image/png', outfilename ];
+                subprocess.call(cmd)
+            elif is_git_checkout:
+                cmd = [ 'git', 'add', outfilename ]
+                subprocess.call(cmd)
diff --git a/tools/render_pdfs_main.cpp b/tools/render_pdfs_main.cpp
new file mode 100644
index 0000000..3aac682
--- /dev/null
+++ b/tools/render_pdfs_main.cpp
@@ -0,0 +1,238 @@
+/*
+ * 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 "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGraphics.h"
+#include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkTArray.h"
+#include "PdfRenderer.h"
+#include "picture_utils.h"
+
+/**
+ * render_pdfs
+ *
+ * Given list of directories and files to use as input, expects to find .skp
+ * files and it will convert them to .pdf files writing them in the output
+ * directory.
+ *
+ * Returns zero exit code if all .skp files were converted successfully,
+ * otherwise returns error code 1.
+ */
+
+static const char PDF_FILE_EXTENSION[] = "pdf";
+static const char SKP_FILE_EXTENSION[] = "skp";
+
+static void usage(const char* argv0) {
+    SkDebugf("SKP to PDF rendering tool\n");
+    SkDebugf("\n"
+"Usage: \n"
+"     %s <input>... -w <outputDir> \n"
+, argv0);
+    SkDebugf("\n\n");
+    SkDebugf(
+"     input:     A list of directories and files to use as input. Files are\n"
+"                expected to have the .skp extension.\n\n");
+    SkDebugf(
+"     outputDir: directory to write the rendered pdfs.\n\n");
+    SkDebugf("\n");
+}
+
+/** Replaces the extension of a file.
+ * @param path File name whose extension will be changed.
+ * @param old_extension The old extension.
+ * @param new_extension The new extension.
+ * @returns false if the file did not has the expected extension.
+ *  if false is returned, contents of path are undefined.
+ */
+static bool replace_filename_extension(SkString* path,
+                                       const char old_extension[],
+                                       const char new_extension[]) {
+    if (path->endsWith(old_extension)) {
+        path->remove(path->size() - strlen(old_extension),
+                     strlen(old_extension));
+        if (!path->endsWith(".")) {
+            return false;
+        }
+        path->append(new_extension);
+        return true;
+    }
+    return false;
+}
+
+/** Builds the output filename. path = dir/name, and it replaces expected
+ * .skp extension with .pdf extention.
+ * @param path Output filename.
+ * @param name The name of the file.
+ * @returns false if the file did not has the expected extension.
+ *  if false is returned, contents of path are undefined.
+ */
+static bool make_output_filepath(SkString* path, const SkString& dir,
+                                 const SkString& name) {
+    sk_tools::make_filepath(path, dir, name);
+    return replace_filename_extension(path,
+                                      SKP_FILE_EXTENSION,
+                                      PDF_FILE_EXTENSION);
+}
+
+/** Write the output of pdf renderer to a file.
+ * @param outputDir Output dir.
+ * @param inputFilename The skp file that was read.
+ * @param renderer The object responsible to write the pdf file.
+ */
+static bool write_output(const SkString& outputDir,
+                         const SkString& inputFilename,
+                         const sk_tools::PdfRenderer& renderer) {
+    if (outputDir.isEmpty()) {
+        SkDynamicMemoryWStream stream;
+        renderer.write(&stream);
+        return true;
+    }
+
+    SkString outputPath;
+    if (!make_output_filepath(&outputPath, outputDir, inputFilename)) {
+        return false;
+    }
+
+    SkFILEWStream stream(outputPath.c_str());
+    if (!stream.isValid()) {
+        SkDebugf("Could not write to file %s\n", outputPath.c_str());
+        return false;
+    }
+    renderer.write(&stream);
+
+    return true;
+}
+
+/** Reads an skp file, renders it to pdf and writes the output to a pdf file
+ * @param inputPath The skp file to be read.
+ * @param outputDir Output dir.
+ * @param renderer The object responsible to render the skp object into pdf.
+ */
+static bool render_pdf(const SkString& inputPath, const SkString& outputDir,
+                       sk_tools::PdfRenderer& renderer) {
+    SkString inputFilename;
+    sk_tools::get_basename(&inputFilename, inputPath);
+
+    SkFILEStream inputStream;
+    inputStream.setPath(inputPath.c_str());
+    if (!inputStream.isValid()) {
+        SkDebugf("Could not open file %s\n", inputPath.c_str());
+        return false;
+    }
+
+    bool success = false;
+    SkAutoTUnref<SkPicture>
+        picture(SkNEW_ARGS(SkPicture, (&inputStream, &success)));
+
+    if (!success) {
+        SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
+        return false;
+    }
+
+    SkDebugf("exporting... [%i %i] %s\n", picture->width(), picture->height(),
+             inputPath.c_str());
+
+    renderer.init(picture);
+
+    renderer.render();
+
+    success = write_output(outputDir, inputFilename, renderer);
+
+    renderer.end();
+    return success;
+}
+
+/** For each file in the directory or for the file passed in input, call
+ * render_pdf.
+ * @param input A directory or an skp file.
+ * @param outputDir Output dir.
+ * @param renderer The object responsible to render the skp object into pdf.
+ */
+static int process_input(const SkString& input, const SkString& outputDir,
+                         sk_tools::PdfRenderer& renderer) {
+    int failures = 0;
+    if (sk_isdir(input.c_str())) {
+        SkOSFile::Iter iter(input.c_str(), SKP_FILE_EXTENSION);
+        SkString inputFilename;
+        while (iter.next(&inputFilename)) {
+            SkString inputPath;
+            sk_tools::make_filepath(&inputPath, input, inputFilename);
+            if (!render_pdf(inputPath, outputDir, renderer)) {
+                ++failures;
+            }
+        }
+    } else {
+        SkString inputPath(input);
+        if (!render_pdf(inputPath, outputDir, renderer)) {
+            ++failures;
+        }
+    }
+    return failures;
+}
+
+static void parse_commandline(int argc, char* const argv[],
+                              SkTArray<SkString>* inputs,
+                              SkString* outputDir) {
+    const char* argv0 = argv[0];
+    char* const* stop = argv + argc;
+
+    for (++argv; argv < stop; ++argv) {
+        if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
+            usage(argv0);
+            exit(-1);
+        } else if (0 == strcmp(*argv, "-w")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing outputDir for -w\n");
+                usage(argv0);
+                exit(-1);
+            }
+            *outputDir = SkString(*argv);
+        } else {
+            inputs->push_back(SkString(*argv));
+        }
+    }
+
+    if (inputs->count() < 1) {
+        usage(argv0);
+        exit(-1);
+    }
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+
+    SkAutoGraphics ag;
+    SkTArray<SkString> inputs;
+
+    SkAutoTUnref<sk_tools::PdfRenderer>
+        renderer(SkNEW(sk_tools::SimplePdfRenderer));
+    SkASSERT(renderer.get());
+
+    SkString outputDir;
+    parse_commandline(argc, argv, &inputs, &outputDir);
+
+    int failures = 0;
+    for (int i = 0; i < inputs.count(); i ++) {
+        failures += process_input(inputs[i], outputDir, *renderer);
+    }
+
+    if (failures != 0) {
+        SkDebugf("Failed to render %i PDFs.\n", failures);
+        return 1;
+    }
+    return 0;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/render_pictures_main.cpp b/tools/render_pictures_main.cpp
new file mode 100644
index 0000000..a459bf2
--- /dev/null
+++ b/tools/render_pictures_main.cpp
@@ -0,0 +1,763 @@
+/*
+ * 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 "CopyTilesRenderer.h"
+#include "SkBitmap.h"
+#include "SkCanvas.h"
+#include "SkDevice.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkMath.h"
+#include "SkOSFile.h"
+#include "SkPicture.h"
+#include "SkStream.h"
+#include "SkString.h"
+#include "SkTArray.h"
+#include "PictureRenderer.h"
+#include "picture_utils.h"
+
+static void usage(const char* argv0) {
+    SkDebugf("SkPicture rendering tool\n");
+    SkDebugf("\n"
+"Usage: \n"
+"     %s <input>... \n"
+"     [-w <outputDir>]\n"
+"     [--mode pow2tile minWidth height | copyTile width height | simple\n"
+"         | tile width height]\n"
+"     [--pipe]\n"
+"     [--bbh bbhType]\n"
+"     [--multi count]\n"
+"     [--validate [--maxComponentDiff n]]\n"
+"     [--writeWholeImage]\n"
+"     [--clone n]\n"
+"     [--viewport width height][--scale sf]\n"
+"     [--device bitmap"
+#if SK_SUPPORT_GPU
+" | gpu"
+#endif
+"]"
+, argv0);
+    SkDebugf("\n\n");
+    SkDebugf(
+"     input:     A list of directories and files to use as input. Files are\n"
+"                expected to have the .skp extension.\n\n");
+    SkDebugf(
+"     outputDir: directory to write the rendered images.\n\n");
+    SkDebugf(
+"     --mode pow2tile minWidth height | copyTile width height | simple\n"
+"          | tile width height | rerecord: Run in the corresponding mode.\n"
+"                                     Default is simple.\n");
+    SkDebugf(
+"                     pow2tile minWidth height, Creates tiles with widths\n"
+"                                                  that are all a power of two\n"
+"                                                  such that they minimize the\n"
+"                                                  amount of wasted tile space.\n"
+"                                                  minWidth is the minimum width\n"
+"                                                  of these tiles and must be a\n"
+"                                                  power of two. A simple render\n"
+"                                                  is done with these tiles.\n");
+    SkDebugf(
+"                     simple, Render using the default rendering method.\n"
+"                     rerecord, Record the picture as a new skp, with the bitmaps PNG encoded.\n"
+             );
+    SkDebugf(
+"                     tile width height, Do a simple render using tiles\n"
+"                                              with the given dimensions.\n"
+"                     copyTile width height, Draw the picture, then copy it into tiles.\n"
+"                                                Does not support percentages.\n"
+"                                                If the picture is large enough, breaks it into\n"
+"                                                larger tiles (and draws the picture once per\n"
+"                                                larger tile) to avoid creating a large canvas.\n"
+"                                                Add --tiles x y to specify the number of tiles\n"
+"                                                per larger tile in the x and y direction.\n"
+             );
+    SkDebugf("\n");
+    SkDebugf(
+"     --multi count : Set the number of threads for multi threaded drawing. Must be greater\n"
+"                     than 1. Only works with tiled rendering.\n"
+"     --viewport width height : Set the viewport.\n"
+"     --scale sf : Scale drawing by sf.\n"
+"     --pipe: Benchmark SkGPipe rendering. Currently incompatible with \"mode\".\n");
+    SkDebugf(
+"     --validate: Verify that the rendered image contains the same pixels as "
+"the picture rendered in simple mode.\n"
+"     --maxComponentDiff: maximum diff on a component. Default is 256, "
+"which means we report but we do not generate an error.\n"
+"     --writeWholeImage: In tile mode, write the entire rendered image to a "
+"file, instead of an image for each tile.\n");
+    SkDebugf(
+"     --clone n: Clone the picture n times before rendering.\n");
+    SkDebugf(
+"     --bbh bbhType [width height]: Set the bounding box hierarchy type to\n"
+"                     be used. Accepted values are: none, rtree, grid. Default\n"
+"                     value is none. Not compatible with --pipe. With value\n"
+"                     'grid', width and height must be specified. 'grid' can\n"
+"                     only be used with modes tile, record, and\n"
+"                     playbackCreation.");
+    SkDebugf(
+"     --device bitmap"
+#if SK_SUPPORT_GPU
+" | gpu"
+#endif
+": Use the corresponding device. Default is bitmap.\n");
+    SkDebugf(
+"                     bitmap, Render to a bitmap.\n");
+#if SK_SUPPORT_GPU
+    SkDebugf(
+"                     gpu, Render to the GPU.\n");
+#endif
+}
+
+static void make_output_filepath(SkString* path, const SkString& dir,
+                                 const SkString& name) {
+    sk_tools::make_filepath(path, dir, name);
+    // Remove ".skp"
+    path->remove(path->size() - 4, 4);
+}
+
+static bool render_picture(const SkString& inputPath, const SkString* outputDir,
+                           sk_tools::PictureRenderer& renderer,
+                           SkBitmap** out,
+                           int clones) {
+    SkString inputFilename;
+    sk_tools::get_basename(&inputFilename, inputPath);
+
+    SkFILEStream inputStream;
+    inputStream.setPath(inputPath.c_str());
+    if (!inputStream.isValid()) {
+        SkDebugf("Could not open file %s\n", inputPath.c_str());
+        return false;
+    }
+
+    bool success = false;
+    SkPicture* picture = SkNEW_ARGS(SkPicture,
+            (&inputStream, &success, &SkImageDecoder::DecodeStream));
+    if (!success) {
+        SkDebugf("Could not read an SkPicture from %s\n", inputPath.c_str());
+        return false;
+    }
+
+    for (int i = 0; i < clones; ++i) {
+        SkPicture* clone = picture->clone();
+        SkDELETE(picture);
+        picture = clone;
+    }
+
+    SkDebugf("drawing... [%i %i] %s\n", picture->width(), picture->height(),
+             inputPath.c_str());
+
+    renderer.init(picture);
+    renderer.setup();
+
+    SkString* outputPath = NULL;
+    if (NULL != outputDir) {
+        outputPath = SkNEW(SkString);
+        make_output_filepath(outputPath, *outputDir, inputFilename);
+    }
+
+    success = renderer.render(outputPath, out);
+    if (outputPath) {
+        if (!success) {
+            SkDebugf("Could not write to file %s\n", outputPath->c_str());
+        }
+        SkDELETE(outputPath);
+    }
+
+    renderer.end();
+
+    SkDELETE(picture);
+    return success;
+}
+
+static inline int getByte(uint32_t value, int index) {
+    SkASSERT(0 <= index && index < 4);
+    return (value >> (index * 8)) & 0xFF;
+}
+
+static int MaxByteDiff(uint32_t v1, uint32_t v2) {
+    return SkMax32(SkMax32(abs(getByte(v1, 0) - getByte(v2, 0)), abs(getByte(v1, 1) - getByte(v2, 1))),
+                   SkMax32(abs(getByte(v1, 2) - getByte(v2, 2)), abs(getByte(v1, 3) - getByte(v2, 3))));
+}
+
+static bool render_picture(const SkString& inputPath, const SkString* outputDir,
+                           sk_tools::PictureRenderer& renderer,
+                           bool validate, int maxComponentDiff,
+                           bool writeWholeImage,
+                           int clones) {
+    int diffs[256] = {0};
+    SkBitmap* bitmap = NULL;
+    bool success = render_picture(inputPath,
+        writeWholeImage ? NULL : outputDir,
+        renderer,
+        validate || writeWholeImage ? &bitmap : NULL, clones);
+
+    if (!success || ((validate || writeWholeImage) && bitmap == NULL)) {
+        SkDebugf("Failed to draw the picture.\n");
+        SkDELETE(bitmap);
+        return false;
+    }
+
+    if (validate) {
+        SkBitmap* referenceBitmap = NULL;
+        sk_tools::SimplePictureRenderer referenceRenderer;
+        success = render_picture(inputPath, NULL, referenceRenderer,
+                                 &referenceBitmap, 0);
+
+        if (!success || !referenceBitmap) {
+            SkDebugf("Failed to draw the reference picture.\n");
+            SkDELETE(bitmap);
+            SkDELETE(referenceBitmap);
+            return false;
+        }
+
+        if (success && (bitmap->width() != referenceBitmap->width())) {
+            SkDebugf("Expected image width: %i, actual image width %i.\n",
+                     referenceBitmap->width(), bitmap->width());
+            SkDELETE(bitmap);
+            SkDELETE(referenceBitmap);
+            return false;
+        }
+        if (success && (bitmap->height() != referenceBitmap->height())) {
+            SkDebugf("Expected image height: %i, actual image height %i",
+                     referenceBitmap->height(), bitmap->height());
+            SkDELETE(bitmap);
+            SkDELETE(referenceBitmap);
+            return false;
+        }
+
+        for (int y = 0; success && y < bitmap->height(); y++) {
+            for (int x = 0; success && x < bitmap->width(); x++) {
+                int diff = MaxByteDiff(*referenceBitmap->getAddr32(x, y),
+                                       *bitmap->getAddr32(x, y));
+                SkASSERT(diff >= 0 && diff <= 255);
+                diffs[diff]++;
+
+                if (diff > maxComponentDiff) {
+                    SkDebugf("Expected pixel at (%i %i) exceedds maximum "
+                                 "component diff of %i: 0x%x, actual 0x%x\n",
+                             x, y, maxComponentDiff,
+                             *referenceBitmap->getAddr32(x, y),
+                             *bitmap->getAddr32(x, y));
+                    SkDELETE(bitmap);
+                    SkDELETE(referenceBitmap);
+                    return false;
+                }
+            }
+        }
+        SkDELETE(referenceBitmap);
+
+        for (int i = 1; i <= 255; ++i) {
+            if(diffs[i] > 0) {
+                SkDebugf("Number of pixels with max diff of %i is %i\n", i, diffs[i]);
+            }
+        }
+    }
+
+    if (writeWholeImage) {
+        sk_tools::force_all_opaque(*bitmap);
+        if (NULL != outputDir && writeWholeImage) {
+            SkString inputFilename;
+            sk_tools::get_basename(&inputFilename, inputPath);
+            SkString outputPath;
+            make_output_filepath(&outputPath, *outputDir, inputFilename);
+            outputPath.append(".png");
+            if (!SkImageEncoder::EncodeFile(outputPath.c_str(), *bitmap,
+                                            SkImageEncoder::kPNG_Type, 100)) {
+                SkDebugf("Failed to draw the picture.\n");
+                success = false;
+            }
+        }
+    }
+    SkDELETE(bitmap);
+
+    return success;
+}
+
+
+static int process_input(const SkString& input, const SkString* outputDir,
+                         sk_tools::PictureRenderer& renderer,
+                         bool validate, int maxComponentDiff,
+                         bool writeWholeImage, int clones) {
+    SkOSFile::Iter iter(input.c_str(), "skp");
+    SkString inputFilename;
+    int failures = 0;
+    SkDebugf("process_input, %s\n", input.c_str());
+    if (iter.next(&inputFilename)) {
+        do {
+            SkString inputPath;
+            sk_tools::make_filepath(&inputPath, input, inputFilename);
+            if (!render_picture(inputPath, outputDir, renderer,
+                                validate, maxComponentDiff,
+                                writeWholeImage, clones)) {
+                ++failures;
+            }
+        } while(iter.next(&inputFilename));
+    } else if (SkStrEndsWith(input.c_str(), ".skp")) {
+        SkString inputPath(input);
+        if (!render_picture(inputPath, outputDir, renderer,
+                            validate, maxComponentDiff,
+                            writeWholeImage, clones)) {
+            ++failures;
+        }
+    } else {
+        SkString warning;
+        warning.printf("Warning: skipping %s\n", input.c_str());
+        SkDebugf(warning.c_str());
+    }
+    return failures;
+}
+
+static void parse_commandline(int argc, char* const argv[], SkTArray<SkString>* inputs,
+                              sk_tools::PictureRenderer*& renderer, SkString*& outputDir,
+                              bool* validate, int* maxComponentDiff,
+                              bool* writeWholeImage,
+                              int* clones){
+    const char* argv0 = argv[0];
+    char* const* stop = argv + argc;
+
+    sk_tools::PictureRenderer::SkDeviceTypes deviceType =
+        sk_tools::PictureRenderer::kBitmap_DeviceType;
+
+    bool usePipe = false;
+    int numThreads = 1;
+    bool useTiles = false;
+    const char* widthString = NULL;
+    const char* heightString = NULL;
+    int gridWidth = 0;
+    int gridHeight = 0;
+    bool isPowerOf2Mode = false;
+    bool isCopyMode = false;
+    const char* xTilesString = NULL;
+    const char* yTilesString = NULL;
+    const char* mode = NULL;
+    bool gridSupported = false;
+    sk_tools::PictureRenderer::BBoxHierarchyType bbhType =
+        sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
+    *validate = false;
+    *maxComponentDiff = 256;
+    *writeWholeImage = false;
+    *clones = 0;
+    SkISize viewport;
+    viewport.setEmpty();
+    SkScalar scaleFactor = SK_Scalar1;
+
+    for (++argv; argv < stop; ++argv) {
+        if (0 == strcmp(*argv, "--mode")) {
+            if (renderer != NULL) {
+                renderer->unref();
+                SkDebugf("Cannot combine modes.\n");
+                usage(argv0);
+                exit(-1);
+            }
+
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing mode for --mode\n");
+                usage(argv0);
+                exit(-1);
+            }
+
+            if (0 == strcmp(*argv, "simple")) {
+                renderer = SkNEW(sk_tools::SimplePictureRenderer);
+            } else if ((0 == strcmp(*argv, "tile")) || (0 == strcmp(*argv, "pow2tile"))
+                       || 0 == strcmp(*argv, "copyTile")) {
+                useTiles = true;
+                mode = *argv;
+
+                if (0 == strcmp(*argv, "pow2tile")) {
+                    isPowerOf2Mode = true;
+                } else if (0 == strcmp(*argv, "copyTile")) {
+                    isCopyMode = true;
+                } else {
+                    gridSupported = true;
+                }
+
+                ++argv;
+                if (argv >= stop) {
+                    SkDebugf("Missing width for --mode %s\n", mode);
+                    usage(argv0);
+                    exit(-1);
+                }
+
+                widthString = *argv;
+                ++argv;
+                if (argv >= stop) {
+                    SkDebugf("Missing height for --mode %s\n", mode);
+                    usage(argv0);
+                    exit(-1);
+                }
+                heightString = *argv;
+            } else if (0 == strcmp(*argv, "rerecord")) {
+                renderer = SkNEW(sk_tools::RecordPictureRenderer);
+            } else {
+                SkDebugf("%s is not a valid mode for --mode\n", *argv);
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--bbh")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing value for --bbh\n");
+                usage(argv0);
+                exit(-1);
+            }
+            if (0 == strcmp(*argv, "none")) {
+                bbhType = sk_tools::PictureRenderer::kNone_BBoxHierarchyType;
+            } else if (0 == strcmp(*argv, "rtree")) {
+                bbhType = sk_tools::PictureRenderer::kRTree_BBoxHierarchyType;
+            } else if (0 == strcmp(*argv, "grid")) {
+                bbhType = sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType;
+                ++argv;
+                if (argv >= stop) {
+                    SkDebugf("Missing width for --bbh grid\n");
+                    usage(argv0);
+                    exit(-1);
+                }
+                gridWidth = atoi(*argv);
+                ++argv;
+                if (argv >= stop) {
+                    SkDebugf("Missing height for --bbh grid\n");
+                    usage(argv0);
+                    exit(-1);
+                }
+                gridHeight = atoi(*argv);
+            } else {
+                SkDebugf("%s is not a valid value for --bbhType\n", *argv);
+                usage(argv0);
+                exit(-1);;
+            }
+        } else if (0 == strcmp(*argv, "--viewport")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing width for --viewport\n");
+                usage(argv0);
+                exit(-1);
+            }
+            viewport.fWidth = atoi(*argv);
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing height for --viewport\n");
+                usage(argv0);
+                exit(-1);
+            }
+            viewport.fHeight = atoi(*argv);
+        } else if (0 == strcmp(*argv, "--scale")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing scaleFactor for --scale\n");
+                usage(argv0);
+                exit(-1);
+            }
+            scaleFactor = SkDoubleToScalar(atof(*argv));
+        } else if (0 == strcmp(*argv, "--tiles")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing x for --tiles\n");
+                usage(argv0);
+                exit(-1);
+            }
+            xTilesString = *argv;
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing y for --tiles\n");
+                usage(argv0);
+                exit(-1);
+            }
+            yTilesString = *argv;
+        } else if (0 == strcmp(*argv, "--pipe")) {
+            usePipe = true;
+        } else if (0 == strcmp(*argv, "--multi")) {
+            ++argv;
+            if (argv >= stop) {
+                SkSafeUnref(renderer);
+                SkDebugf("Missing arg for --multi\n");
+                usage(argv0);
+                exit(-1);
+            }
+            numThreads = atoi(*argv);
+            if (numThreads < 2) {
+                SkSafeUnref(renderer);
+                SkDebugf("Number of threads must be at least 2.\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--clone")) {
+            ++argv;
+            if (argv >= stop) {
+                SkSafeUnref(renderer);
+                SkDebugf("Missing arg for --clone\n");
+                usage(argv0);
+                exit(-1);
+            }
+            *clones = atoi(*argv);
+            if (*clones < 0) {
+                SkSafeUnref(renderer);
+                SkDebugf("Number of clones must be at least 0.\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--device")) {
+            ++argv;
+            if (argv >= stop) {
+                SkSafeUnref(renderer);
+                SkDebugf("Missing mode for --device\n");
+                usage(argv0);
+                exit(-1);
+            }
+
+            if (0 == strcmp(*argv, "bitmap")) {
+                deviceType = sk_tools::PictureRenderer::kBitmap_DeviceType;
+            }
+#if SK_SUPPORT_GPU
+            else if (0 == strcmp(*argv, "gpu")) {
+                deviceType = sk_tools::PictureRenderer::kGPU_DeviceType;
+            }
+#endif
+            else {
+                SkSafeUnref(renderer);
+                SkDebugf("%s is not a valid mode for --device\n", *argv);
+                usage(argv0);
+                exit(-1);
+            }
+
+        } else if ((0 == strcmp(*argv, "-h")) || (0 == strcmp(*argv, "--help"))) {
+            SkSafeUnref(renderer);
+            usage(argv0);
+            exit(-1);
+        } else if (0 == strcmp(*argv, "-w")) {
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing output directory for -w\n");
+                usage(argv0);
+                exit(-1);
+            }
+            outputDir = SkNEW_ARGS(SkString, (*argv));
+        } else if (0 == strcmp(*argv, "--validate")) {
+            *validate = true;
+        } else if (0 == strcmp(*argv, "--maxComponentDiff")) {
+            if (!*validate) {
+                SkDebugf("--maxComponentDiff must be used only with --validate\n");
+                usage(argv0);
+                exit(-1);
+            }
+            ++argv;
+            if (argv >= stop) {
+                SkDebugf("Missing arg for --maxComponentDiff\n");
+                usage(argv0);
+                exit(-1);
+            }
+            *maxComponentDiff = atoi(*argv);
+            if (*maxComponentDiff < 0 || *maxComponentDiff > 256) {
+                SkSafeUnref(renderer);
+                SkDebugf("maxComponentDiff: 0 - 256.\n");
+                usage(argv0);
+                exit(-1);
+            }
+        } else if (0 == strcmp(*argv, "--writeWholeImage")) {
+            *writeWholeImage = true;
+        } else {
+            inputs->push_back(SkString(*argv));
+        }
+    }
+
+    if (numThreads > 1 && !useTiles) {
+        SkSafeUnref(renderer);
+        SkDebugf("Multithreaded drawing requires tiled rendering.\n");
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (usePipe && sk_tools::PictureRenderer::kNone_BBoxHierarchyType != bbhType) {
+        SkDebugf("--pipe and --bbh cannot be used together\n");
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (sk_tools::PictureRenderer::kTileGrid_BBoxHierarchyType == bbhType &&
+        !gridSupported) {
+        SkDebugf("'--bbh grid' is not compatible with specified --mode.\n");
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (useTiles) {
+        SkASSERT(NULL == renderer);
+        sk_tools::TiledPictureRenderer* tiledRenderer;
+        if (isCopyMode) {
+            int x, y;
+            if (xTilesString != NULL) {
+                SkASSERT(yTilesString != NULL);
+                x = atoi(xTilesString);
+                y = atoi(yTilesString);
+                if (x <= 0 || y <= 0) {
+                    SkDebugf("--tiles must be given values > 0\n");
+                    usage(argv0);
+                    exit(-1);
+                }
+            } else {
+                x = y = 4;
+            }
+            tiledRenderer = SkNEW_ARGS(sk_tools::CopyTilesRenderer, (x, y));
+        } else if (numThreads > 1) {
+            tiledRenderer = SkNEW_ARGS(sk_tools::MultiCorePictureRenderer, (numThreads));
+        } else {
+            tiledRenderer = SkNEW(sk_tools::TiledPictureRenderer);
+        }
+        if (isPowerOf2Mode) {
+            int minWidth = atoi(widthString);
+            if (!SkIsPow2(minWidth) || minWidth < 0) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("-mode %s must be given a width"
+                           " value that is a power of two\n", mode);
+                SkDebugf(err.c_str());
+                usage(argv0);
+                exit(-1);
+            }
+            tiledRenderer->setTileMinPowerOf2Width(minWidth);
+        } else if (sk_tools::is_percentage(widthString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                SkDebugf(err.c_str());
+                usage(argv0);
+                exit(-1);
+            }
+            tiledRenderer->setTileWidthPercentage(atof(widthString));
+            if (!(tiledRenderer->getTileWidthPercentage() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode %s must be given a width percentage > 0\n", mode);
+                usage(argv0);
+                exit(-1);
+            }
+        } else {
+            tiledRenderer->setTileWidth(atoi(widthString));
+            if (!(tiledRenderer->getTileWidth() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode %s must be given a width > 0\n", mode);
+                usage(argv0);
+                exit(-1);
+            }
+        }
+
+        if (sk_tools::is_percentage(heightString)) {
+            if (isCopyMode) {
+                tiledRenderer->unref();
+                SkString err;
+                err.printf("--mode %s does not support percentages.\n", mode);
+                SkDebugf(err.c_str());
+                usage(argv0);
+                exit(-1);
+            }
+            tiledRenderer->setTileHeightPercentage(atof(heightString));
+            if (!(tiledRenderer->getTileHeightPercentage() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode %s must be given a height percentage > 0\n", mode);
+                usage(argv0);
+                exit(-1);
+            }
+        } else {
+            tiledRenderer->setTileHeight(atoi(heightString));
+            if (!(tiledRenderer->getTileHeight() > 0)) {
+                tiledRenderer->unref();
+                SkDebugf("--mode %s must be given a height > 0\n", mode);
+                usage(argv0);
+                exit(-1);
+            }
+        }
+        if (numThreads > 1) {
+#if SK_SUPPORT_GPU
+            if (sk_tools::PictureRenderer::kGPU_DeviceType == deviceType) {
+                tiledRenderer->unref();
+                SkDebugf("GPU not compatible with multithreaded tiling.\n");
+                usage(argv0);
+                exit(-1);
+            }
+#endif
+        }
+        renderer = tiledRenderer;
+        if (usePipe) {
+            SkDebugf("Pipe rendering is currently not compatible with tiling.\n"
+                     "Turning off pipe.\n");
+        }
+    } else if (usePipe) {
+        if (renderer != NULL) {
+            renderer->unref();
+            SkDebugf("Pipe is incompatible with other modes.\n");
+            usage(argv0);
+            exit(-1);
+        }
+        renderer = SkNEW(sk_tools::PipePictureRenderer);
+    }
+
+    if (inputs->empty()) {
+        SkSafeUnref(renderer);
+        if (NULL != outputDir) {
+            SkDELETE(outputDir);
+        }
+        usage(argv0);
+        exit(-1);
+    }
+
+    if (NULL == renderer) {
+        renderer = SkNEW(sk_tools::SimplePictureRenderer);
+    }
+
+    renderer->setBBoxHierarchyType(bbhType);
+    renderer->setGridSize(gridWidth, gridHeight);
+    renderer->setViewport(viewport);
+    renderer->setScaleFactor(scaleFactor);
+    renderer->setDeviceType(deviceType);
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+    SkAutoGraphics ag;
+    SkTArray<SkString> inputs;
+    sk_tools::PictureRenderer* renderer = NULL;
+    SkString* outputDir = NULL;
+    bool validate = false;
+    int maxComponentDiff = 256;
+    bool writeWholeImage = false;
+    int clones = 0;
+    parse_commandline(argc, argv, &inputs, renderer, outputDir,
+                      &validate, &maxComponentDiff, &writeWholeImage, &clones);
+    SkASSERT(renderer);
+
+    int failures = 0;
+    for (int i = 0; i < inputs.count(); i ++) {
+        failures += process_input(inputs[i], outputDir, *renderer,
+                                  validate, maxComponentDiff,
+                                  writeWholeImage, clones);
+    }
+    if (failures != 0) {
+        SkDebugf("Failed to render %i pictures.\n", failures);
+        return 1;
+    }
+#if SK_SUPPORT_GPU
+#if GR_CACHE_STATS
+    if (renderer->isUsingGpuDevice()) {
+        GrContext* ctx = renderer->getGrContext();
+
+        ctx->printCacheStats();
+    }
+#endif
+#endif
+    if (NULL != outputDir) {
+        SkDELETE(outputDir);
+    }
+    SkDELETE(renderer);
+    return 0;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/roll_autogen.sh b/tools/roll_autogen.sh
new file mode 100755
index 0000000..2567a13
--- /dev/null
+++ b/tools/roll_autogen.sh
@@ -0,0 +1,64 @@
+#/bin/bash
+# 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.
+
+# roll_autogen.sh: Helper script for removing old revisions from an svn
+# repository.  Unfortunately, the only way to discard old revisions is to clone
+# the repository locally, use svnadmin to dump a range of commits from the local
+# copy, re-import them into a brand-new repository, "reset" the original repo,
+# and then import the commits from the new repository into the original.  This
+# script automates all of that except for resetting the original repository.
+
+REPO=${REPO:-"https://skia-autogen.googlecode.com"}
+REVS_TO_KEEP=${REVS_TO_KEEP:-50}
+REPO_SVN="${REPO}/svn"
+CLONE_DIR="local_clone_dir"
+LOCAL_CLONE="$(pwd)/${CLONE_DIR}"
+
+echo "Creating local repository in ${LOCAL_CLONE}"
+svnadmin create ${LOCAL_CLONE}
+pushd ${LOCAL_CLONE}/hooks > /dev/null
+echo "#!/bin/sh" > pre-revprop-change
+chmod 755 pre-revprop-change 
+popd > /dev/null
+
+# Determine the latest revision.  Note that any revisions committed while we
+# were syncing will be lost forever!
+END=`svn info ${REPO_SVN} | grep Revision | cut -c11-`
+START=$((END-REVS_TO_KEEP))
+DUMPFILE="skia-autogen_r${START}-${END}.dump"
+
+echo "Cloning ${REPO_SVN} into ${LOCAL_CLONE}..."
+svnsync init file://${LOCAL_CLONE} ${REPO_SVN}
+svnsync --non-interactive sync file://${LOCAL_CLONE}
+
+echo "Dumping revisions ${START} to ${END} to ${DUMPFILE}."
+svnadmin dump --revision ${START}:${END} ${LOCAL_CLONE} > ${DUMPFILE}
+
+echo "Removing temporary local clone."
+rm -rf ${LOCAL_CLONE}
+
+echo "Re-creating local clone from ${DUMPFILE}."
+svnadmin create ${LOCAL_CLONE}
+svnadmin load ${LOCAL_CLONE} < ${DUMPFILE}
+
+echo "Deleting ${DUMPFILE}"
+rm ${DUMPFILE}
+
+echo "Now you need to reset the remote repository. Typically, a link to do this"
+echo "can be found at (${REPO}/adminSource).
+echo "Please do so and press any key to continue."
+read -n 1 -s
+
+echo "Syncing ${LOCAL_CLONE} to ${REPO_SVN}."
+svnsync init ${REPO_SVN} file://${LOCAL_CLONE}
+svnsync sync ${REPO_SVN}
+
+echo "Removing temporary local clone."
+rm -rf ${LOCAL_CLONE}
+
+echo "Removing local checkout."
+rm -rf ${CHECKOUT_DIR}
+
+echo "Finished!"
diff --git a/tools/sanitize_source_files.py b/tools/sanitize_source_files.py
new file mode 100755
index 0000000..c7edaa0
--- /dev/null
+++ b/tools/sanitize_source_files.py
@@ -0,0 +1,152 @@
+#!/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.
+
+"""Module that sanitizes source files with specified modifiers."""
+
+
+import commands
+import os
+import sys
+
+
+_FILE_EXTENSIONS_TO_SANITIZE = ['cpp', 'h', 'c', 'gyp', 'gypi']
+
+_SUBDIRS_TO_IGNORE = ['.svn', 'third_party']
+
+
+def SanitizeFilesWithModifiers(directory, file_modifiers, line_modifiers):
+  """Sanitizes source files with the specified file and line modifiers.
+
+  Args:
+    directory: string - The directory which will be recursively traversed to
+        find source files to apply modifiers to.
+    file_modifiers: list - file-modification methods which should be applied to
+        the complete file content (Eg: EOFOneAndOnlyOneNewlineAdder).
+    line_modifiers: list - line-modification methods which should be applied to
+        lines in a file (Eg: TabReplacer).
+  """
+  for item in os.listdir(directory):
+
+    full_item_path = os.path.join(directory, item)
+
+    if os.path.isfile(full_item_path):  # Item is a file.
+
+      # Only sanitize files with extensions we care about.
+      if (len(full_item_path.split('.')) > 1 and
+          full_item_path.split('.')[-1] in _FILE_EXTENSIONS_TO_SANITIZE):
+        f = file(full_item_path)
+        try:
+          lines = f.readlines()
+        finally:
+          f.close()
+
+        new_lines = []  # Collect changed lines here.
+        line_number = 0  # Keeps track of line numbers in the source file.
+        write_to_file = False  # File is written to only if this flag is set.
+
+        # Run the line modifiers for each line in this file.
+        for line in lines:
+          original_line = line
+          line_number += 1
+
+          for modifier in line_modifiers:
+            line = modifier(line, full_item_path, line_number)
+            if original_line != line:
+              write_to_file = True
+          new_lines.append(line)
+
+        # Run the file modifiers.
+        old_content = ''.join(lines)
+        new_content = ''.join(new_lines)
+        for modifier in file_modifiers:
+          new_content = modifier(new_content, full_item_path)
+        if new_content != old_content:
+          write_to_file = True
+
+        # Write modifications to the file.
+        if write_to_file:
+          f = file(full_item_path, 'w')
+          try:
+            f.write(new_content)
+          finally:
+            f.close()
+          print 'Made changes to %s' % full_item_path
+
+    elif item not in _SUBDIRS_TO_IGNORE:
+      # Item is a directory recursively call the method.
+      SanitizeFilesWithModifiers(full_item_path, file_modifiers, line_modifiers)
+
+
+############## Line Modification methods ##############
+
+
+def TrailingWhitespaceRemover(line, file_path, line_number):
+  """Strips out trailing whitespaces from the specified line."""
+  stripped_line = line.rstrip() + '\n'
+  if line != stripped_line:
+    print 'Removing trailing whitespace in %s:%s' % (file_path, line_number)
+  return stripped_line
+
+
+def CrlfReplacer(line, file_path, line_number):
+  """Replaces CRLF with LF."""
+  if '\r\n' in line:
+    print 'Replacing CRLF with LF in %s:%s' % (file_path, line_number)
+  return line.replace('\r\n', '\n')
+
+
+def TabReplacer(line, file_path, line_number):
+  """Replaces Tabs with 4 whitespaces."""
+  if '\t' in line:
+    print 'Replacing Tab with whitespace in %s:%s' % (file_path, line_number)
+  return line.replace('\t', '    ')
+
+
+############## File Modification methods ##############
+
+
+def CopywriteChecker(file_content, unused_file_path):
+  """Ensures that the copywrite information is correct."""
+  # TODO(rmistry): Figure out the legal implications of changing old copyright
+  # headers.
+  return file_content
+
+
+def EOFOneAndOnlyOneNewlineAdder(file_content, file_path):
+  """Adds one and only one LF at the end of the file."""
+  if file_content and (file_content[-1] != '\n' or file_content[-2:-1] == '\n'):
+    file_content = file_content.rstrip()
+    file_content += '\n'
+    print 'Added exactly one newline to %s' % file_path
+  return file_content
+
+
+def SvnEOLChecker(file_content, file_path):
+  """Sets svn:eol-style property to LF."""
+  output = commands.getoutput(
+      'svn propget svn:eol-style %s' % file_path)
+  if output != 'LF':
+    print 'Setting svn:eol-style property to LF in %s' % file_path
+    os.system('svn ps svn:eol-style LF %s' % file_path)
+  return file_content
+
+
+#######################################################
+
+
+if '__main__' == __name__:
+  sys.exit(SanitizeFilesWithModifiers(
+      os.getcwd(),
+      file_modifiers=[
+          CopywriteChecker,
+          EOFOneAndOnlyOneNewlineAdder,
+          SvnEOLChecker,
+      ],
+      line_modifiers=[
+          CrlfReplacer,
+          TabReplacer,
+          TrailingWhitespaceRemover,
+      ],
+  ))
diff --git a/tools/skdiff.cpp b/tools/skdiff.cpp
new file mode 100644
index 0000000..ae6d72c
--- /dev/null
+++ b/tools/skdiff.cpp
@@ -0,0 +1,228 @@
+/*
+ * 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 "skdiff.h"
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkTypes.h"
+
+/*static*/ char const * const DiffRecord::ResultNames[DiffRecord::kResultCount] = {
+    "EqualBits",
+    "EqualPixels",
+    "DifferentPixels",
+    "DifferentSizes",
+    "CouldNotCompare",
+    "Unknown",
+};
+
+DiffRecord::Result DiffRecord::getResultByName(const char *name) {
+    for (int result = 0; result < DiffRecord::kResultCount; ++result) {
+        if (0 == strcmp(DiffRecord::ResultNames[result], name)) {
+            return static_cast<DiffRecord::Result>(result);
+        }
+    }
+    return DiffRecord::kResultCount;
+}
+
+static char const * const ResultDescriptions[DiffRecord::kResultCount] = {
+    "contain exactly the same bits",
+    "contain the same pixel values, but not the same bits",
+    "have identical dimensions but some differing pixels",
+    "have differing dimensions",
+    "could not be compared",
+    "not compared yet",
+};
+
+const char* DiffRecord::getResultDescription(DiffRecord::Result result) {
+    return ResultDescriptions[result];
+}
+
+/*static*/ char const * const DiffResource::StatusNames[DiffResource::kStatusCount] = {
+    "Decoded",
+    "CouldNotDecode",
+
+    "Read",
+    "CouldNotRead",
+
+    "Exists",
+    "DoesNotExist",
+
+    "Specified",
+    "Unspecified",
+
+    "Unknown",
+};
+
+DiffResource::Status DiffResource::getStatusByName(const char *name) {
+    for (int status = 0; status < DiffResource::kStatusCount; ++status) {
+        if (0 == strcmp(DiffResource::StatusNames[status], name)) {
+            return static_cast<DiffResource::Status>(status);
+        }
+    }
+    return DiffResource::kStatusCount;
+}
+
+static char const * const StatusDescriptions[DiffResource::kStatusCount] = {
+    "decoded",
+    "could not be decoded",
+
+    "read",
+    "could not be read",
+
+    "found",
+    "not found",
+
+    "specified",
+    "unspecified",
+
+    "unknown",
+};
+
+const char* DiffResource::getStatusDescription(DiffResource::Status status) {
+    return StatusDescriptions[status];
+}
+
+bool DiffResource::isStatusFailed(DiffResource::Status status) {
+    return DiffResource::kCouldNotDecode_Status == status ||
+           DiffResource::kCouldNotRead_Status == status ||
+           DiffResource::kDoesNotExist_Status == status ||
+           DiffResource::kUnspecified_Status == status ||
+           DiffResource::kUnknown_Status == status;
+}
+
+bool DiffResource::getMatchingStatuses(char* selector, bool statuses[kStatusCount]) {
+    if (!strcmp(selector, "any")) {
+        for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
+            statuses[statusIndex] = true;
+        }
+        return true;
+    }
+
+    for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
+        statuses[statusIndex] = false;
+    }
+
+    static const char kDelimiterChar = ',';
+    bool understood = true;
+    while (true) {
+        char* delimiterPtr = strchr(selector, kDelimiterChar);
+
+        if (delimiterPtr) {
+            *delimiterPtr = '\0';
+        }
+
+        if (!strcmp(selector, "failed")) {
+            for (int statusIndex = 0; statusIndex < kStatusCount; ++statusIndex) {
+                Status status = static_cast<Status>(statusIndex);
+                statuses[statusIndex] |= isStatusFailed(status);
+            }
+        } else {
+            Status status = getStatusByName(selector);
+            if (status == kStatusCount) {
+                understood = false;
+            } else {
+                statuses[status] = true;
+            }
+        }
+
+        if (!delimiterPtr) {
+            break;
+        }
+
+        *delimiterPtr = kDelimiterChar;
+        selector = delimiterPtr + 1;
+    }
+    return understood;
+}
+
+static inline bool colors_match_thresholded(SkPMColor c0, SkPMColor c1, const int threshold) {
+    int da = SkGetPackedA32(c0) - SkGetPackedA32(c1);
+    int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
+    int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
+    int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
+
+    return ((SkAbs32(da) <= threshold) &&
+            (SkAbs32(dr) <= threshold) &&
+            (SkAbs32(dg) <= threshold) &&
+            (SkAbs32(db) <= threshold));
+}
+
+const SkPMColor PMCOLOR_WHITE = SkPreMultiplyColor(SK_ColorWHITE);
+const SkPMColor PMCOLOR_BLACK = SkPreMultiplyColor(SK_ColorBLACK);
+
+void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold) {
+    const int w = dr->fComparison.fBitmap.width();
+    const int h = dr->fComparison.fBitmap.height();
+    if (w != dr->fBase.fBitmap.width() || h != dr->fBase.fBitmap.height()) {
+        dr->fResult = DiffRecord::kDifferentSizes_Result;
+        return;
+    }
+
+    SkAutoLockPixels alpDiff(dr->fDifference.fBitmap);
+    SkAutoLockPixels alpWhite(dr->fWhite.fBitmap);
+    int mismatchedPixels = 0;
+    int totalMismatchA = 0;
+    int totalMismatchR = 0;
+    int totalMismatchG = 0;
+    int totalMismatchB = 0;
+
+    // Accumulate fractionally different pixels, then divide out
+    // # of pixels at the end.
+    dr->fWeightedFraction = 0;
+    for (int y = 0; y < h; y++) {
+        for (int x = 0; x < w; x++) {
+            SkPMColor c0 = *dr->fBase.fBitmap.getAddr32(x, y);
+            SkPMColor c1 = *dr->fComparison.fBitmap.getAddr32(x, y);
+            SkPMColor outputDifference = diffFunction(c0, c1);
+            uint32_t thisA = SkAbs32(SkGetPackedA32(c0) - SkGetPackedA32(c1));
+            uint32_t thisR = SkAbs32(SkGetPackedR32(c0) - SkGetPackedR32(c1));
+            uint32_t thisG = SkAbs32(SkGetPackedG32(c0) - SkGetPackedG32(c1));
+            uint32_t thisB = SkAbs32(SkGetPackedB32(c0) - SkGetPackedB32(c1));
+            totalMismatchA += thisA;
+            totalMismatchR += thisR;
+            totalMismatchG += thisG;
+            totalMismatchB += thisB;
+            // In HSV, value is defined as max RGB component.
+            int value = MAX3(thisR, thisG, thisB);
+            dr->fWeightedFraction += ((float) value) / 255;
+            if (thisA > dr->fMaxMismatchA) {
+                dr->fMaxMismatchA = thisA;
+            }
+            if (thisR > dr->fMaxMismatchR) {
+                dr->fMaxMismatchR = thisR;
+            }
+            if (thisG > dr->fMaxMismatchG) {
+                dr->fMaxMismatchG = thisG;
+            }
+            if (thisB > dr->fMaxMismatchB) {
+                dr->fMaxMismatchB = thisB;
+            }
+            if (!colors_match_thresholded(c0, c1, colorThreshold)) {
+                mismatchedPixels++;
+                *dr->fDifference.fBitmap.getAddr32(x, y) = outputDifference;
+                *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_WHITE;
+            } else {
+                *dr->fDifference.fBitmap.getAddr32(x, y) = 0;
+                *dr->fWhite.fBitmap.getAddr32(x, y) = PMCOLOR_BLACK;
+            }
+        }
+    }
+    if (0 == mismatchedPixels) {
+        dr->fResult = DiffRecord::kEqualPixels_Result;
+        return;
+    }
+    dr->fResult = DiffRecord::kDifferentPixels_Result;
+    int pixelCount = w * h;
+    dr->fFractionDifference = ((float) mismatchedPixels) / pixelCount;
+    dr->fWeightedFraction /= pixelCount;
+    dr->fTotalMismatchA = totalMismatchA;
+    dr->fAverageMismatchA = ((float) totalMismatchA) / pixelCount;
+    dr->fAverageMismatchR = ((float) totalMismatchR) / pixelCount;
+    dr->fAverageMismatchG = ((float) totalMismatchG) / pixelCount;
+    dr->fAverageMismatchB = ((float) totalMismatchB) / pixelCount;
+}
diff --git a/tools/skdiff.h b/tools/skdiff.h
new file mode 100644
index 0000000..6abaf6c
--- /dev/null
+++ b/tools/skdiff.h
@@ -0,0 +1,272 @@
+/*
+ * 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 skdiff_DEFINED
+#define skdiff_DEFINED
+
+#include "SkBitmap.h"
+#include "SkColor.h"
+#include "SkColorPriv.h"
+#include "SkString.h"
+#include "SkTDArray.h"
+
+#if SK_BUILD_FOR_WIN32
+    #define PATH_DIV_STR "\\"
+    #define PATH_DIV_CHAR '\\'
+#else
+    #define PATH_DIV_STR "/"
+    #define PATH_DIV_CHAR '/'
+#endif
+
+#define MAX2(a,b) (((b) < (a)) ? (a) : (b))
+#define MAX3(a,b,c) (((b) < (a)) ? MAX2((a), (c)) : MAX2((b), (c)))
+
+
+struct DiffResource {
+    enum Status {
+        /** The resource was specified, exists, read, and decoded. */
+        kDecoded_Status,
+        /** The resource was specified, exists, read, but could not be decoded. */
+        kCouldNotDecode_Status,
+
+        /** The resource was specified, exists, and read. */
+        kRead_Status,
+        /** The resource was specified, exists, but could not be read. */
+        kCouldNotRead_Status,
+
+        /** The resource was specified and exists. */
+        kExists_Status,
+        /** The resource was specified, but does not exist. */
+        kDoesNotExist_Status,
+
+        /** The resource was specified. */
+        kSpecified_Status,
+        /** The resource was not specified. */
+        kUnspecified_Status,
+
+        /** Nothing is yet known about the resource. */
+        kUnknown_Status,
+
+        /** NOT A VALID VALUE -- used to set up arrays and to represent an unknown value. */
+        kStatusCount
+    };
+    static char const * const StatusNames[DiffResource::kStatusCount];
+
+    /** Returns the Status with this name.
+     *  If there is no Status with this name, returns kStatusCount.
+     */
+    static Status getStatusByName(const char *name);
+
+    /** Returns a text description of the given Status type. */
+    static const char *getStatusDescription(Status status);
+
+    /** Returns true if the Status indicates some kind of failure. */
+    static bool isStatusFailed(Status status);
+
+    /** Sets statuses[i] if it is implied by selector, unsets it if not.
+     *  Selector may be a comma delimited list of status names, "any", or "failed".
+     *  Returns true if the selector was entirely understood, false otherwise.
+     */
+    static bool getMatchingStatuses(char* selector, bool statuses[kStatusCount]);
+
+    DiffResource() : fFilename(), fFullPath(), fBitmap(), fStatus(kUnknown_Status) { };
+
+    /** If isEmpty() indicates no filename available. */
+    SkString fFilename;
+    /** If isEmpty() indicates no path available. */
+    SkString fFullPath;
+    /** If empty() indicates the bitmap could not be created. */
+    SkBitmap fBitmap;
+    Status fStatus;
+};
+
+struct DiffRecord {
+
+    // Result of comparison for each pair of files.
+    // Listed from "better" to "worse", for sorting of results.
+    enum Result {
+        kEqualBits_Result,
+        kEqualPixels_Result,
+        kDifferentPixels_Result,
+        kDifferentSizes_Result,
+        kCouldNotCompare_Result,
+        kUnknown_Result,
+
+        kResultCount  // NOT A VALID VALUE--used to set up arrays. Must be last.
+    };
+    static char const * const ResultNames[DiffRecord::kResultCount];
+
+    /** Returns the Result with this name.
+     *  If there is no Result with this name, returns kResultCount.
+     */
+    static Result getResultByName(const char *name);
+
+    /** Returns a text description of the given Result type. */
+    static const char *getResultDescription(Result result);
+
+    DiffRecord()
+        : fBase()
+        , fComparison()
+        , fDifference()
+        , fWhite()
+        , fFractionDifference(0)
+        , fWeightedFraction(0)
+        , fAverageMismatchA(0)
+        , fAverageMismatchR(0)
+        , fAverageMismatchG(0)
+        , fAverageMismatchB(0)
+        , fTotalMismatchA(0)
+        , fMaxMismatchA(0)
+        , fMaxMismatchR(0)
+        , fMaxMismatchG(0)
+        , fMaxMismatchB(0)
+        , fResult(kUnknown_Result) {
+    };
+
+    DiffResource fBase;
+    DiffResource fComparison;
+    DiffResource fDifference;
+    DiffResource fWhite;
+
+    /// Arbitrary floating-point metric to be used to sort images from most
+    /// to least different from baseline; values of 0 will be omitted from the
+    /// summary webpage.
+    float fFractionDifference;
+    float fWeightedFraction;
+
+    float fAverageMismatchA;
+    float fAverageMismatchR;
+    float fAverageMismatchG;
+    float fAverageMismatchB;
+
+    uint32_t fTotalMismatchA;
+
+    uint32_t fMaxMismatchA;
+    uint32_t fMaxMismatchR;
+    uint32_t fMaxMismatchG;
+    uint32_t fMaxMismatchB;
+
+    /// Which category of diff result.
+    Result fResult;
+};
+
+typedef SkTDArray<DiffRecord*> RecordArray;
+
+/// A wrapper for any sortProc (comparison routine) which applies a first-order
+/// sort beforehand, and a tiebreaker if the sortProc returns 0.
+template<typename T> static int compare(const void* untyped_lhs, const void* untyped_rhs) {
+    const DiffRecord* lhs = *reinterpret_cast<DiffRecord* const *>(untyped_lhs);
+    const DiffRecord* rhs = *reinterpret_cast<DiffRecord* const *>(untyped_rhs);
+
+    // First-order sort... these comparisons should be applied before comparing
+    // pixel values, no matter what.
+    if (lhs->fResult != rhs->fResult) {
+        return (lhs->fResult < rhs->fResult) ? 1 : -1;
+    }
+
+    // Passed first-order sort, so call the pixel comparison routine.
+    int result = T::comparePixels(lhs, rhs);
+    if (result != 0) {
+        return result;
+    }
+
+    // Tiebreaker... if we got to this point, we don't really care
+    // which order they are sorted in, but let's at least be consistent.
+    return strcmp(lhs->fBase.fFilename.c_str(), rhs->fBase.fFilename.c_str());
+}
+
+/// Comparison routine for qsort; sorts by fFractionDifference
+/// from largest to smallest.
+class CompareDiffMetrics {
+public:
+    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
+        if (lhs->fFractionDifference < rhs->fFractionDifference) {
+          return 1;
+        }
+        if (rhs->fFractionDifference < lhs->fFractionDifference) {
+          return -1;
+        }
+        return 0;
+    }
+};
+
+class CompareDiffWeighted {
+public:
+    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
+        if (lhs->fWeightedFraction < rhs->fWeightedFraction) {
+            return 1;
+        }
+        if (lhs->fWeightedFraction > rhs->fWeightedFraction) {
+            return -1;
+        }
+        return 0;
+    }
+};
+
+/// Comparison routine for qsort;  sorts by max(fAverageMismatch{RGB})
+/// from largest to smallest.
+class CompareDiffMeanMismatches {
+public:
+    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
+        float leftValue = MAX3(lhs->fAverageMismatchR,
+                               lhs->fAverageMismatchG,
+                               lhs->fAverageMismatchB);
+        float rightValue = MAX3(rhs->fAverageMismatchR,
+                                rhs->fAverageMismatchG,
+                                rhs->fAverageMismatchB);
+        if (leftValue < rightValue) {
+            return 1;
+        }
+        if (rightValue < leftValue) {
+            return -1;
+        }
+        return 0;
+    }
+};
+
+/// Comparison routine for qsort;  sorts by max(fMaxMismatch{RGB})
+/// from largest to smallest.
+class CompareDiffMaxMismatches {
+public:
+    static int comparePixels(const DiffRecord* lhs, const DiffRecord* rhs) {
+        uint32_t leftValue = MAX3(lhs->fMaxMismatchR,
+                                  lhs->fMaxMismatchG,
+                                  lhs->fMaxMismatchB);
+        uint32_t rightValue = MAX3(rhs->fMaxMismatchR,
+                                   rhs->fMaxMismatchG,
+                                   rhs->fMaxMismatchB);
+        if (leftValue < rightValue) {
+            return 1;
+        }
+        if (rightValue < leftValue) {
+            return -1;
+        }
+
+        return CompareDiffMeanMismatches::comparePixels(lhs, rhs);
+    }
+};
+
+
+/// Parameterized routine to compute the color of a pixel in a difference image.
+typedef SkPMColor (*DiffMetricProc)(SkPMColor, SkPMColor);
+
+// from gm
+static inline SkPMColor compute_diff_pmcolor(SkPMColor c0, SkPMColor c1) {
+    int dr = SkGetPackedR32(c0) - SkGetPackedR32(c1);
+    int dg = SkGetPackedG32(c0) - SkGetPackedG32(c1);
+    int db = SkGetPackedB32(c0) - SkGetPackedB32(c1);
+
+    return SkPackARGB32(0xFF, SkAbs32(dr), SkAbs32(dg), SkAbs32(db));
+}
+
+/** When finished, dr->fResult should have some value other than kUnknown_Result.
+ *  Expects dr->fWhite.fBitmap and dr->fDifference.fBitmap to have the same bounds as
+ *  dr->fBase.fBitmap and have a valid pixelref.
+ */
+void compute_diff(DiffRecord* dr, DiffMetricProc diffFunction, const int colorThreshold);
+
+#endif
diff --git a/tools/skdiff_html.cpp b/tools/skdiff_html.cpp
new file mode 100644
index 0000000..6f3c3b0
--- /dev/null
+++ b/tools/skdiff_html.cpp
@@ -0,0 +1,313 @@
+/*
+ * 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 "skdiff.h"
+#include "skdiff_html.h"
+#include "SkStream.h"
+#include "SkTime.h"
+
+/// Make layout more consistent by scaling image to 240 height, 360 width,
+/// or natural size, whichever is smallest.
+static int compute_image_height(int height, int width) {
+    int retval = 240;
+    if (height < retval) {
+        retval = height;
+    }
+    float scale = (float) retval / height;
+    if (width * scale > 360) {
+        scale = (float) 360 / width;
+        retval = static_cast<int>(height * scale);
+    }
+    return retval;
+}
+
+static void print_table_header(SkFILEWStream* stream,
+                               const int matchCount,
+                               const int colorThreshold,
+                               const RecordArray& differences,
+                               const SkString &baseDir,
+                               const SkString &comparisonDir,
+                               bool doOutputDate = false) {
+    stream->writeText("<table>\n");
+    stream->writeText("<tr><th>");
+    stream->writeText("select image</th>\n<th>");
+    if (doOutputDate) {
+        SkTime::DateTime dt;
+        SkTime::GetDateTime(&dt);
+        stream->writeText("SkDiff run at ");
+        stream->writeDecAsText(dt.fHour);
+        stream->writeText(":");
+        if (dt.fMinute < 10) {
+            stream->writeText("0");
+        }
+        stream->writeDecAsText(dt.fMinute);
+        stream->writeText(":");
+        if (dt.fSecond < 10) {
+            stream->writeText("0");
+        }
+        stream->writeDecAsText(dt.fSecond);
+        stream->writeText("<br>");
+    }
+    stream->writeDecAsText(matchCount);
+    stream->writeText(" of ");
+    stream->writeDecAsText(differences.count());
+    stream->writeText(" diffs matched ");
+    if (colorThreshold == 0) {
+        stream->writeText("exactly");
+    } else {
+        stream->writeText("within ");
+        stream->writeDecAsText(colorThreshold);
+        stream->writeText(" color units per component");
+    }
+    stream->writeText(".<br>");
+    stream->writeText("</th>\n<th>");
+    stream->writeText("every different pixel shown in white");
+    stream->writeText("</th>\n<th>");
+    stream->writeText("color difference at each pixel");
+    stream->writeText("</th>\n<th>baseDir: ");
+    stream->writeText(baseDir.c_str());
+    stream->writeText("</th>\n<th>comparisonDir: ");
+    stream->writeText(comparisonDir.c_str());
+    stream->writeText("</th>\n");
+    stream->writeText("</tr>\n");
+}
+
+static void print_pixel_count(SkFILEWStream* stream, const DiffRecord& diff) {
+    stream->writeText("<br>(");
+    stream->writeDecAsText(static_cast<int>(diff.fFractionDifference *
+                                            diff.fBase.fBitmap.width() *
+                                            diff.fBase.fBitmap.height()));
+    stream->writeText(" pixels)");
+/*
+    stream->writeDecAsText(diff.fWeightedFraction *
+                           diff.fBaseWidth *
+                           diff.fBaseHeight);
+    stream->writeText(" weighted pixels)");
+*/
+}
+
+static void print_checkbox_cell(SkFILEWStream* stream, const DiffRecord& diff) {
+    stream->writeText("<td><input type=\"checkbox\" name=\"");
+    stream->writeText(diff.fBase.fFilename.c_str());
+    stream->writeText("\" checked=\"yes\"></td>");
+}
+
+static void print_label_cell(SkFILEWStream* stream, const DiffRecord& diff) {
+    char metricBuf [20];
+
+    stream->writeText("<td><b>");
+    stream->writeText(diff.fBase.fFilename.c_str());
+    stream->writeText("</b><br>");
+    switch (diff.fResult) {
+      case DiffRecord::kEqualBits_Result:
+        SkDEBUGFAIL("should not encounter DiffRecord with kEqualBits here");
+        return;
+      case DiffRecord::kEqualPixels_Result:
+        SkDEBUGFAIL("should not encounter DiffRecord with kEqualPixels here");
+        return;
+      case DiffRecord::kDifferentSizes_Result:
+        stream->writeText("Image sizes differ</td>");
+        return;
+      case DiffRecord::kDifferentPixels_Result:
+        sprintf(metricBuf, "%.4f%%", 100 * diff.fFractionDifference);
+        stream->writeText(metricBuf);
+        stream->writeText(" of pixels differ");
+        stream->writeText("\n  (");
+        sprintf(metricBuf, "%.4f%%", 100 * diff.fWeightedFraction);
+        stream->writeText(metricBuf);
+        stream->writeText(" weighted)");
+        // Write the actual number of pixels that differ if it's < 1%
+        if (diff.fFractionDifference < 0.01) {
+            print_pixel_count(stream, diff);
+        }
+        stream->writeText("<br>");
+        if (SkScalarRoundToInt(diff.fAverageMismatchA) > 0) {
+          stream->writeText("<br>Average alpha channel mismatch ");
+          stream->writeDecAsText(SkScalarRoundToInt(diff.fAverageMismatchA));
+        }
+
+        stream->writeText("<br>Max alpha channel mismatch ");
+        stream->writeDecAsText(SkScalarRoundToInt(diff.fMaxMismatchA));
+
+        stream->writeText("<br>Total alpha channel mismatch ");
+        stream->writeDecAsText(static_cast<int>(diff.fTotalMismatchA));
+
+        stream->writeText("<br>");
+        stream->writeText("<br>Average color mismatch ");
+        stream->writeDecAsText(SkScalarRoundToInt(MAX3(diff.fAverageMismatchR,
+                                                       diff.fAverageMismatchG,
+                                                       diff.fAverageMismatchB)));
+        stream->writeText("<br>Max color mismatch ");
+        stream->writeDecAsText(MAX3(diff.fMaxMismatchR,
+                                    diff.fMaxMismatchG,
+                                    diff.fMaxMismatchB));
+        stream->writeText("</td>");
+        break;
+      case DiffRecord::kCouldNotCompare_Result:
+        stream->writeText("Could not compare.<br>base: ");
+        stream->writeText(DiffResource::getStatusDescription(diff.fBase.fStatus));
+        stream->writeText("<br>comparison: ");
+        stream->writeText(DiffResource::getStatusDescription(diff.fComparison.fStatus));
+        stream->writeText("</td>");
+        return;
+      default:
+        SkDEBUGFAIL("encountered DiffRecord with unknown result type");
+        return;
+    }
+}
+
+static void print_image_cell(SkFILEWStream* stream, const SkString& path, int height) {
+    stream->writeText("<td><a href=\"");
+    stream->writeText(path.c_str());
+    stream->writeText("\"><img src=\"");
+    stream->writeText(path.c_str());
+    stream->writeText("\" height=\"");
+    stream->writeDecAsText(height);
+    stream->writeText("px\"></a></td>");
+}
+
+static void print_link_cell(SkFILEWStream* stream, const SkString& path, const char* text) {
+    stream->writeText("<td><a href=\"");
+    stream->writeText(path.c_str());
+    stream->writeText("\">");
+    stream->writeText(text);
+    stream->writeText("</a></td>");
+}
+
+static void print_diff_resource_cell(SkFILEWStream* stream, DiffResource& resource,
+                                     const SkString& relativePath, bool local) {
+    if (resource.fBitmap.empty()) {
+        if (DiffResource::kCouldNotDecode_Status == resource.fStatus) {
+            if (local && !resource.fFilename.isEmpty()) {
+                print_link_cell(stream, resource.fFilename, "N/A");
+                return;
+            }
+            if (!resource.fFullPath.isEmpty()) {
+                if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
+                    resource.fFullPath.prepend(relativePath);
+                }
+                print_link_cell(stream, resource.fFullPath, "N/A");
+                return;
+            }
+        }
+        stream->writeText("<td>N/A</td>");
+        return;
+    }
+
+    int height = compute_image_height(resource.fBitmap.height(), resource.fBitmap.width());
+    if (local) {
+        print_image_cell(stream, resource.fFilename, height);
+        return;
+    }
+    if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
+        resource.fFullPath.prepend(relativePath);
+    }
+    print_image_cell(stream, resource.fFullPath, height);
+}
+
+static void print_diff_row(SkFILEWStream* stream, DiffRecord& diff, const SkString& relativePath) {
+    stream->writeText("<tr>\n");
+    print_checkbox_cell(stream, diff);
+    print_label_cell(stream, diff);
+    print_diff_resource_cell(stream, diff.fWhite, relativePath, true);
+    print_diff_resource_cell(stream, diff.fDifference, relativePath, true);
+    print_diff_resource_cell(stream, diff.fBase, relativePath, false);
+    print_diff_resource_cell(stream, diff.fComparison, relativePath, false);
+    stream->writeText("</tr>\n");
+    stream->flush();
+}
+
+void print_diff_page(const int matchCount,
+                     const int colorThreshold,
+                     const RecordArray& differences,
+                     const SkString& baseDir,
+                     const SkString& comparisonDir,
+                     const SkString& outputDir) {
+
+    SkASSERT(!baseDir.isEmpty());
+    SkASSERT(!comparisonDir.isEmpty());
+    SkASSERT(!outputDir.isEmpty());
+
+    SkString outputPath(outputDir);
+    outputPath.append("index.html");
+    //SkFILEWStream outputStream ("index.html");
+    SkFILEWStream outputStream(outputPath.c_str());
+
+    // Need to convert paths from relative-to-cwd to relative-to-outputDir
+    // FIXME this doesn't work if there are '..' inside the outputDir
+
+    bool isPathAbsolute = false;
+    // On Windows or Linux, a path starting with PATH_DIV_CHAR is absolute.
+    if (outputDir.size() > 0 && PATH_DIV_CHAR == outputDir[0]) {
+        isPathAbsolute = true;
+    }
+#ifdef SK_BUILD_FOR_WIN32
+    // On Windows, absolute paths can also start with "x:", where x is any
+    // drive letter.
+    if (outputDir.size() > 1 && ':' == outputDir[1]) {
+        isPathAbsolute = true;
+    }
+#endif
+
+    SkString relativePath;
+    if (!isPathAbsolute) {
+        unsigned int ui;
+        for (ui = 0; ui < outputDir.size(); ui++) {
+            if (outputDir[ui] == PATH_DIV_CHAR) {
+                relativePath.append(".." PATH_DIV_STR);
+            }
+        }
+    }
+
+    outputStream.writeText(
+        "<html>\n<head>\n"
+        "<script src=\"https://ajax.googleapis.com/ajax/"
+        "libs/jquery/1.7.2/jquery.min.js\"></script>\n"
+        "<script type=\"text/javascript\">\n"
+        "function generateCheckedList() {\n"
+        "var boxes = $(\":checkbox:checked\");\n"
+        "var fileCmdLineString = '';\n"
+        "var fileMultiLineString = '';\n"
+        "for (var i = 0; i < boxes.length; i++) {\n"
+        "fileMultiLineString += boxes[i].name + '<br>';\n"
+        "fileCmdLineString += boxes[i].name + '&nbsp;';\n"
+        "}\n"
+        "$(\"#checkedList\").html(fileCmdLineString + "
+        "'<br><br>' + fileMultiLineString);\n"
+        "}\n"
+        "</script>\n</head>\n<body>\n");
+    print_table_header(&outputStream, matchCount, colorThreshold, differences,
+                       baseDir, comparisonDir);
+    int i;
+    for (i = 0; i < differences.count(); i++) {
+        DiffRecord* diff = differences[i];
+
+        switch (diff->fResult) {
+          // Cases in which there is no diff to report.
+          case DiffRecord::kEqualBits_Result:
+          case DiffRecord::kEqualPixels_Result:
+            continue;
+          // Cases in which we want a detailed pixel diff.
+          case DiffRecord::kDifferentPixels_Result:
+          case DiffRecord::kDifferentSizes_Result:
+          case DiffRecord::kCouldNotCompare_Result:
+            print_diff_row(&outputStream, *diff, relativePath);
+            continue;
+          default:
+            SkDEBUGFAIL("encountered DiffRecord with unknown result type");
+            continue;
+        }
+    }
+    outputStream.writeText(
+        "</table>\n"
+        "<input type=\"button\" "
+        "onclick=\"generateCheckedList()\" "
+        "value=\"Create Rebaseline List\">\n"
+        "<div id=\"checkedList\"></div>\n"
+        "</body>\n</html>\n");
+    outputStream.flush();
+}
diff --git a/tools/skdiff_html.h b/tools/skdiff_html.h
new file mode 100644
index 0000000..eefbebf
--- /dev/null
+++ b/tools/skdiff_html.h
@@ -0,0 +1,21 @@
+/*
+ * 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 skdiff_html_DEFINED
+#define skdiff_html_DEFINED
+
+#include "skdiff.h"
+class SkString;
+
+void print_diff_page(const int matchCount,
+                     const int colorThreshold,
+                     const RecordArray& differences,
+                     const SkString& baseDir,
+                     const SkString& comparisonDir,
+                     const SkString& outputDir);
+
+#endif
diff --git a/tools/skdiff_image.cpp b/tools/skdiff_image.cpp
new file mode 100644
index 0000000..1487587
--- /dev/null
+++ b/tools/skdiff_image.cpp
@@ -0,0 +1,375 @@
+/*
+ * 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 "skdiff.h"
+#include "skdiff_utils.h"
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkOSFile.h"
+#include "SkTDArray.h"
+#include "SkTemplates.h"
+#include "SkTypes.h"
+
+/// If outputDir.isEmpty(), don't write out diff files.
+static void create_diff_images (DiffMetricProc dmp,
+                                const int colorThreshold,
+                                const SkString& baseFile,
+                                const SkString& comparisonFile,
+                                const SkString& outputDir,
+                                const SkString& outputFilename,
+                                DiffRecord* drp) {
+    SkASSERT(!baseFile.isEmpty());
+    SkASSERT(!comparisonFile.isEmpty());
+
+    drp->fBase.fFilename = baseFile;
+    drp->fBase.fFullPath = baseFile;
+    drp->fBase.fStatus = DiffResource::kSpecified_Status;
+
+    drp->fComparison.fFilename = comparisonFile;
+    drp->fComparison.fFullPath = comparisonFile;
+    drp->fComparison.fStatus = DiffResource::kSpecified_Status;
+
+    SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
+    if (NULL != baseFileBits) {
+        drp->fBase.fStatus = DiffResource::kRead_Status;
+    }
+    SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
+    if (NULL != comparisonFileBits) {
+        drp->fComparison.fStatus = DiffResource::kRead_Status;
+    }
+    if (NULL == baseFileBits || NULL == comparisonFileBits) {
+        if (NULL == baseFileBits) {
+            drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
+        }
+        if (NULL == comparisonFileBits) {
+            drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
+        }
+        drp->fResult = DiffRecord::kCouldNotCompare_Result;
+        return;
+    }
+
+    if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
+        drp->fResult = DiffRecord::kEqualBits_Result;
+        return;
+    }
+
+    get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode);
+    get_bitmap(comparisonFileBits, drp->fComparison, SkImageDecoder::kDecodePixels_Mode);
+    if (DiffResource::kDecoded_Status != drp->fBase.fStatus ||
+        DiffResource::kDecoded_Status != drp->fComparison.fStatus)
+    {
+        drp->fResult = DiffRecord::kCouldNotCompare_Result;
+        return;
+    }
+
+    create_and_write_diff_image(drp, dmp, colorThreshold, outputDir, outputFilename);
+    //TODO: copy fBase.fFilename and fComparison.fFilename to outputDir
+    //      svn and git often present tmp files to diff tools which are promptly deleted
+
+    //TODO: serialize drp to outputDir
+    //      write a tool to deserialize them and call print_diff_page
+
+    SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
+}
+
+static void usage (char * argv0) {
+    SkDebugf("Skia image diff tool\n");
+    SkDebugf("\n"
+"Usage: \n"
+"    %s <baseFile> <comparisonFile>\n" , argv0);
+    SkDebugf(
+"\nArguments:"
+"\n    --failonresult <result>: After comparing all file pairs, exit with nonzero"
+"\n                             return code (number of file pairs yielding this"
+"\n                             result) if any file pairs yielded this result."
+"\n                             This flag may be repeated, in which case the"
+"\n                             return code will be the number of fail pairs"
+"\n                             yielding ANY of these results."
+"\n    --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
+"\n                             code if any file pairs yeilded this status."
+"\n    --help: display this info"
+"\n    --listfilenames: list all filenames for each result type in stdout"
+"\n    --nodiffs: don't write out image diffs, just generate report on stdout"
+"\n    --outputdir: directory to write difference images"
+"\n    --threshold <n>: only report differences > n (per color channel) [default 0]"
+"\n    -u: ignored. Recognized for compatibility with svn diff."
+"\n    -L: first occurrence label for base, second occurrence label for comparison."
+"\n        Labels must be of the form \"<filename>(\t<specifier>)?\"."
+"\n        The base <filename> will be used to create files in outputdir."
+"\n"
+"\n    baseFile: baseline image file."
+"\n    comparisonFile: comparison image file"
+"\n"
+"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
+"\n");
+}
+
+const int kNoError = 0;
+const int kGenericError = -1;
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+    DiffMetricProc diffProc = compute_diff_pmcolor;
+
+    // Maximum error tolerated in any one color channel in any one pixel before
+    // a difference is reported.
+    int colorThreshold = 0;
+    SkString baseFile;
+    SkString baseLabel;
+    SkString comparisonFile;
+    SkString comparisonLabel;
+    SkString outputDir;
+
+    bool listFilenames = false;
+
+    bool failOnResultType[DiffRecord::kResultCount];
+    for (int i = 0; i < DiffRecord::kResultCount; i++) {
+        failOnResultType[i] = false;
+    }
+
+    bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
+    for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+        for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+            failOnStatusType[base][comparison] = false;
+        }
+    }
+
+    int i;
+    int numUnflaggedArguments = 0;
+    int numLabelArguments = 0;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "--failonresult")) {
+            if (argc == ++i) {
+                SkDebugf("failonresult expects one argument.\n");
+                continue;
+            }
+            DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
+            if (type != DiffRecord::kResultCount) {
+                failOnResultType[type] = true;
+            } else {
+                SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
+            }
+            continue;
+        }
+        if (!strcmp(argv[i], "--failonstatus")) {
+            if (argc == ++i) {
+                SkDebugf("failonstatus missing base status.\n");
+                continue;
+            }
+            bool baseStatuses[DiffResource::kStatusCount];
+            if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
+                SkDebugf("unrecognized base status <%s>\n", argv[i]);
+            }
+
+            if (argc == ++i) {
+                SkDebugf("failonstatus missing comparison status.\n");
+                continue;
+            }
+            bool comparisonStatuses[DiffResource::kStatusCount];
+            if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
+                SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
+            }
+
+            for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+                for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+                    failOnStatusType[base][comparison] |=
+                        baseStatuses[base] && comparisonStatuses[comparison];
+                }
+            }
+            continue;
+        }
+        if (!strcmp(argv[i], "--help")) {
+            usage(argv[0]);
+            return kNoError;
+        }
+        if (!strcmp(argv[i], "--listfilenames")) {
+            listFilenames = true;
+            continue;
+        }
+        if (!strcmp(argv[i], "--outputdir")) {
+            if (argc == ++i) {
+                SkDebugf("outputdir expects one argument.\n");
+                continue;
+            }
+            outputDir.set(argv[i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "--threshold")) {
+            colorThreshold = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "-u")) {
+            //we don't produce unified diffs, ignore parameter to work with svn diff
+            continue;
+        }
+        if (!strcmp(argv[i], "-L")) {
+            if (argc == ++i) {
+                SkDebugf("label expects one argument.\n");
+                continue;
+            }
+            switch (numLabelArguments++) {
+                case 0:
+                    baseLabel.set(argv[i]);
+                    continue;
+                case 1:
+                    comparisonLabel.set(argv[i]);
+                    continue;
+                default:
+                    SkDebugf("extra label argument <%s>\n", argv[i]);
+                    usage(argv[0]);
+                    return kGenericError;
+            }
+            continue;
+        }
+        if (argv[i][0] != '-') {
+            switch (numUnflaggedArguments++) {
+                case 0:
+                    baseFile.set(argv[i]);
+                    continue;
+                case 1:
+                    comparisonFile.set(argv[i]);
+                    continue;
+                default:
+                    SkDebugf("extra unflagged argument <%s>\n", argv[i]);
+                    usage(argv[0]);
+                    return kGenericError;
+            }
+        }
+
+        SkDebugf("Unrecognized argument <%s>\n", argv[i]);
+        usage(argv[0]);
+        return kGenericError;
+    }
+
+    if (numUnflaggedArguments != 2) {
+        usage(argv[0]);
+        return kGenericError;
+    }
+
+    if (listFilenames) {
+        printf("Base file is [%s]\n", baseFile.c_str());
+    }
+
+    if (listFilenames) {
+        printf("Comparison file is [%s]\n", comparisonFile.c_str());
+    }
+
+    if (outputDir.isEmpty()) {
+        if (listFilenames) {
+            printf("Not writing any diffs. No output dir specified.\n");
+        }
+    } else {
+        if (!outputDir.endsWith(PATH_DIV_STR)) {
+            outputDir.append(PATH_DIV_STR);
+        }
+        if (listFilenames) {
+            printf("Writing diffs. Output dir is [%s]\n", outputDir.c_str());
+        }
+    }
+
+    // Some obscure documentation about diff/patch labels:
+    //
+    // Posix says the format is: <filename><tab><date>
+    //     It also states that if a filename contains <tab> or <newline>
+    //     the result is implementation defined
+    //
+    // Svn diff --diff-cmd provides labels of the form: <filename><tab><revision>
+    //
+    // Git diff --ext-diff does not supply arguments compatible with diff.
+    //     However, it does provide the filename directly.
+    //     skimagediff_git.sh: skimagediff %2 %5 -L "%1\t(%3)" -L "%1\t(%6)"
+    //
+    // Git difftool sets $LOCAL, $REMOTE, $MERGED, and $BASE instead of command line parameters.
+    //     difftool.<>.cmd: skimagediff $LOCAL $REMOTE -L "$MERGED\t(local)" -L "$MERGED\t(remote)"
+    //
+    // Diff will write any specified label verbatim. Without a specified label diff will write
+    //     <filename><tab><date>
+    //     However, diff will encode the filename as a cstring if the filename contains
+    //         Any of <space> or <double quote>
+    //         A char less than 32
+    //         Any escapable character \a, \b, \t, \n, \v, \f, \r, \\
+    //
+    // Patch decodes:
+    //     If first <non-white-space> is <double quote>, parse filename from cstring.
+    //     If there is a <tab> after the first <non-white-space>, filename is
+    //         [first <non-white-space>, the next run of <white-space> with an embedded <tab>).
+    //     Otherwise the filename is [first <non-space>, the next <white-space>).
+    //
+    // The filename /dev/null means the file does not exist (used in adds and deletes).
+
+    // Considering the above, skimagediff will consider the contents of a -L parameter as
+    //     <filename>(\t<specifier>)?
+    SkString outputFile;
+
+    if (baseLabel.isEmpty()) {
+        baseLabel.set(baseFile);
+        outputFile = baseLabel;
+    } else {
+        const char* baseLabelCstr = baseLabel.c_str();
+        const char* tab = strchr(baseLabelCstr, '\t');
+        if (NULL == tab) {
+            outputFile = baseLabel;
+        } else {
+            outputFile.set(baseLabelCstr, tab - baseLabelCstr);
+        }
+    }
+    if (comparisonLabel.isEmpty()) {
+        comparisonLabel.set(comparisonFile);
+    }
+    printf("Base:       %s\n", baseLabel.c_str());
+    printf("Comparison: %s\n", comparisonLabel.c_str());
+
+    DiffRecord dr;
+    create_diff_images(diffProc, colorThreshold, baseFile, comparisonFile, outputDir, outputFile,
+                       &dr);
+
+    if (DiffResource::isStatusFailed(dr.fBase.fStatus)) {
+        printf("Base %s.\n", DiffResource::getStatusDescription(dr.fBase.fStatus));
+    }
+    if (DiffResource::isStatusFailed(dr.fComparison.fStatus)) {
+        printf("Comparison %s.\n", DiffResource::getStatusDescription(dr.fComparison.fStatus));
+    }
+    printf("Base and Comparison %s.\n", DiffRecord::getResultDescription(dr.fResult));
+
+    if (DiffRecord::kDifferentPixels_Result == dr.fResult) {
+        printf("%.4f%% of pixels differ", 100 * dr.fFractionDifference);
+        printf(" (%.4f%%  weighted)", 100 * dr.fWeightedFraction);
+        if (dr.fFractionDifference < 0.01) {
+            printf(" %d pixels", static_cast<int>(dr.fFractionDifference *
+                                                  dr.fBase.fBitmap.width() *
+                                                  dr.fBase.fBitmap.height()));
+        }
+
+        printf("\nAverage color mismatch: ");
+        printf("%d", static_cast<int>(MAX3(dr.fAverageMismatchR,
+                                           dr.fAverageMismatchG,
+                                           dr.fAverageMismatchB)));
+        printf("\nMax color mismatch: ");
+        printf("%d", MAX3(dr.fMaxMismatchR,
+                          dr.fMaxMismatchG,
+                          dr.fMaxMismatchB));
+        printf("\n");
+    }
+    printf("\n");
+
+    int num_failing_results = 0;
+    if (failOnResultType[dr.fResult]) {
+        ++num_failing_results;
+    }
+    if (failOnStatusType[dr.fBase.fStatus][dr.fComparison.fStatus]) {
+        ++num_failing_results;
+    }
+
+    return num_failing_results;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/skdiff_main.cpp b/tools/skdiff_main.cpp
new file mode 100644
index 0000000..a9a1968
--- /dev/null
+++ b/tools/skdiff_main.cpp
@@ -0,0 +1,773 @@
+/*
+ * 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 "skdiff.h"
+#include "skdiff_html.h"
+#include "skdiff_utils.h"
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkOSFile.h"
+#include "SkStream.h"
+#include "SkTDArray.h"
+#include "SkTemplates.h"
+#include "SkTSearch.h"
+#include "SkTypes.h"
+
+/**
+ * skdiff
+ *
+ * Given three directory names, expects to find identically-named files in
+ * each of the first two; the first are treated as a set of baseline,
+ * the second a set of variant images, and a diff image is written into the
+ * third directory for each pair.
+ * Creates an index.html in the current third directory to compare each
+ * pair that does not match exactly.
+ * Recursively descends directories, unless run with --norecurse.
+ *
+ * Returns zero exit code if all images match across baseDir and comparisonDir.
+ */
+
+typedef SkTDArray<SkString*> StringArray;
+typedef StringArray FileArray;
+
+struct DiffSummary {
+    DiffSummary ()
+        : fNumMatches(0)
+        , fNumMismatches(0)
+        , fMaxMismatchV(0)
+        , fMaxMismatchPercent(0) { };
+
+    ~DiffSummary() {
+        for (int i = 0; i < DiffRecord::kResultCount; ++i) {
+            fResultsOfType[i].deleteAll();
+        }
+        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+                fStatusOfType[base][comparison].deleteAll();
+            }
+        }
+    }
+
+    uint32_t fNumMatches;
+    uint32_t fNumMismatches;
+    uint32_t fMaxMismatchV;
+    float fMaxMismatchPercent;
+
+    FileArray fResultsOfType[DiffRecord::kResultCount];
+    FileArray fStatusOfType[DiffResource::kStatusCount][DiffResource::kStatusCount];
+
+    void printContents(const FileArray& fileArray,
+                       const char* baseStatus, const char* comparisonStatus,
+                       bool listFilenames) {
+        int n = fileArray.count();
+        printf("%d file pairs %s in baseDir and %s in comparisonDir",
+                n,            baseStatus,       comparisonStatus);
+        if (listFilenames) {
+            printf(": ");
+            for (int i = 0; i < n; ++i) {
+                printf("%s ", fileArray[i]->c_str());
+            }
+        }
+        printf("\n");
+    }
+
+    void printStatus(bool listFilenames,
+                     bool failOnStatusType[DiffResource::kStatusCount]
+                                          [DiffResource::kStatusCount]) {
+        typedef DiffResource::Status Status;
+
+        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+            Status baseStatus = static_cast<Status>(base);
+            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+                Status comparisonStatus = static_cast<Status>(comparison);
+                const FileArray& fileArray = fStatusOfType[base][comparison];
+                if (fileArray.count() > 0) {
+                    if (failOnStatusType[base][comparison]) {
+                        printf("   [*] ");
+                    } else {
+                        printf("   [_] ");
+                    }
+                    printContents(fileArray,
+                                  DiffResource::getStatusDescription(baseStatus),
+                                  DiffResource::getStatusDescription(comparisonStatus),
+                                  listFilenames);
+                }
+            }
+        }
+    }
+
+    // Print a line about the contents of this FileArray to stdout.
+    void printContents(const FileArray& fileArray, const char* headerText, bool listFilenames) {
+        int n = fileArray.count();
+        printf("%d file pairs %s", n, headerText);
+        if (listFilenames) {
+            printf(": ");
+            for (int i = 0; i < n; ++i) {
+                printf("%s ", fileArray[i]->c_str());
+            }
+        }
+        printf("\n");
+    }
+
+    void print(bool listFilenames, bool failOnResultType[DiffRecord::kResultCount],
+               bool failOnStatusType[DiffResource::kStatusCount]
+                                    [DiffResource::kStatusCount]) {
+        printf("\ncompared %d file pairs:\n", fNumMatches + fNumMismatches);
+        for (int resultInt = 0; resultInt < DiffRecord::kResultCount; ++resultInt) {
+            DiffRecord::Result result = static_cast<DiffRecord::Result>(resultInt);
+            if (failOnResultType[result]) {
+                printf("[*] ");
+            } else {
+                printf("[_] ");
+            }
+            printContents(fResultsOfType[result], DiffRecord::getResultDescription(result),
+                          listFilenames);
+            if (DiffRecord::kCouldNotCompare_Result == result) {
+                printStatus(listFilenames, failOnStatusType);
+            }
+        }
+        printf("(results marked with [*] will cause nonzero return value)\n");
+        printf("\nnumber of mismatching file pairs: %d\n", fNumMismatches);
+        if (fNumMismatches > 0) {
+            printf("Maximum pixel intensity mismatch %d\n", fMaxMismatchV);
+            printf("Largest area mismatch was %.2f%% of pixels\n",fMaxMismatchPercent);
+        }
+    }
+
+    void add (DiffRecord* drp) {
+        uint32_t mismatchValue;
+
+        if (drp->fBase.fFilename.equals(drp->fComparison.fFilename)) {
+            fResultsOfType[drp->fResult].push(new SkString(drp->fBase.fFilename));
+        } else {
+            SkString* blame = new SkString("(");
+            blame->append(drp->fBase.fFilename);
+            blame->append(", ");
+            blame->append(drp->fComparison.fFilename);
+            blame->append(")");
+            fResultsOfType[drp->fResult].push(blame);
+        }
+        switch (drp->fResult) {
+          case DiffRecord::kEqualBits_Result:
+            fNumMatches++;
+            break;
+          case DiffRecord::kEqualPixels_Result:
+            fNumMatches++;
+            break;
+          case DiffRecord::kDifferentSizes_Result:
+            fNumMismatches++;
+            break;
+          case DiffRecord::kDifferentPixels_Result:
+            fNumMismatches++;
+            if (drp->fFractionDifference * 100 > fMaxMismatchPercent) {
+                fMaxMismatchPercent = drp->fFractionDifference * 100;
+            }
+            mismatchValue = MAX3(drp->fMaxMismatchR, drp->fMaxMismatchG,
+                                 drp->fMaxMismatchB);
+            if (mismatchValue > fMaxMismatchV) {
+                fMaxMismatchV = mismatchValue;
+            }
+            break;
+          case DiffRecord::kCouldNotCompare_Result:
+            fNumMismatches++;
+            fStatusOfType[drp->fBase.fStatus][drp->fComparison.fStatus].push(
+                    new SkString(drp->fBase.fFilename));
+            break;
+          case DiffRecord::kUnknown_Result:
+            SkDEBUGFAIL("adding uncategorized DiffRecord");
+            break;
+          default:
+            SkDEBUGFAIL("adding DiffRecord with unhandled fResult value");
+            break;
+        }
+    }
+};
+
+/// Returns true if string contains any of these substrings.
+static bool string_contains_any_of(const SkString& string,
+                                   const StringArray& substrings) {
+    for (int i = 0; i < substrings.count(); i++) {
+        if (string.contains(substrings[i]->c_str())) {
+            return true;
+        }
+    }
+    return false;
+}
+
+/// Internal (potentially recursive) implementation of get_file_list.
+static void get_file_list_subdir(const SkString& rootDir, const SkString& subDir,
+                                 const StringArray& matchSubstrings,
+                                 const StringArray& nomatchSubstrings,
+                                 bool recurseIntoSubdirs, FileArray *files) {
+    bool isSubDirEmpty = subDir.isEmpty();
+    SkString dir(rootDir);
+    if (!isSubDirEmpty) {
+        dir.append(PATH_DIV_STR);
+        dir.append(subDir);
+    }
+
+    // Iterate over files (not directories) within dir.
+    SkOSFile::Iter fileIterator(dir.c_str());
+    SkString fileName;
+    while (fileIterator.next(&fileName, false)) {
+        if (fileName.startsWith(".")) {
+            continue;
+        }
+        SkString pathRelativeToRootDir(subDir);
+        if (!isSubDirEmpty) {
+            pathRelativeToRootDir.append(PATH_DIV_STR);
+        }
+        pathRelativeToRootDir.append(fileName);
+        if (string_contains_any_of(pathRelativeToRootDir, matchSubstrings) &&
+            !string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
+            files->push(new SkString(pathRelativeToRootDir));
+        }
+    }
+
+    // Recurse into any non-ignored subdirectories.
+    if (recurseIntoSubdirs) {
+        SkOSFile::Iter dirIterator(dir.c_str());
+        SkString dirName;
+        while (dirIterator.next(&dirName, true)) {
+            if (dirName.startsWith(".")) {
+                continue;
+            }
+            SkString pathRelativeToRootDir(subDir);
+            if (!isSubDirEmpty) {
+                pathRelativeToRootDir.append(PATH_DIV_STR);
+            }
+            pathRelativeToRootDir.append(dirName);
+            if (!string_contains_any_of(pathRelativeToRootDir, nomatchSubstrings)) {
+                get_file_list_subdir(rootDir, pathRelativeToRootDir,
+                                     matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
+                                     files);
+            }
+        }
+    }
+}
+
+/// Iterate over dir and get all files whose filename:
+///  - matches any of the substrings in matchSubstrings, but...
+///  - DOES NOT match any of the substrings in nomatchSubstrings
+///  - DOES NOT start with a dot (.)
+/// Adds the matching files to the list in *files.
+static void get_file_list(const SkString& dir,
+                          const StringArray& matchSubstrings,
+                          const StringArray& nomatchSubstrings,
+                          bool recurseIntoSubdirs, FileArray *files) {
+    get_file_list_subdir(dir, SkString(""),
+                         matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
+                         files);
+}
+
+static void release_file_list(FileArray *files) {
+    files->deleteAll();
+}
+
+/// Comparison routines for qsort, sort by file names.
+static int compare_file_name_metrics(SkString **lhs, SkString **rhs) {
+    return strcmp((*lhs)->c_str(), (*rhs)->c_str());
+}
+
+class AutoReleasePixels {
+public:
+    AutoReleasePixels(DiffRecord* drp)
+    : fDrp(drp) {
+        SkASSERT(drp != NULL);
+    }
+    ~AutoReleasePixels() {
+        fDrp->fBase.fBitmap.setPixelRef(NULL);
+        fDrp->fComparison.fBitmap.setPixelRef(NULL);
+        fDrp->fDifference.fBitmap.setPixelRef(NULL);
+        fDrp->fWhite.fBitmap.setPixelRef(NULL);
+    }
+
+private:
+    DiffRecord* fDrp;
+};
+
+static void get_bounds(DiffResource& resource, const char* name) {
+    if (resource.fBitmap.empty() && !DiffResource::isStatusFailed(resource.fStatus)) {
+        SkAutoDataUnref fileBits(read_file(resource.fFullPath.c_str()));
+        if (NULL == fileBits) {
+            SkDebugf("WARNING: couldn't read %s file <%s>\n", name, resource.fFullPath.c_str());
+            resource.fStatus = DiffResource::kCouldNotRead_Status;
+        } else {
+            get_bitmap(fileBits, resource, SkImageDecoder::kDecodeBounds_Mode);
+        }
+    }
+}
+
+static void get_bounds(DiffRecord& drp) {
+    get_bounds(drp.fBase, "base");
+    get_bounds(drp.fComparison, "comparison");
+}
+
+/// Creates difference images, returns the number that have a 0 metric.
+/// If outputDir.isEmpty(), don't write out diff files.
+static void create_diff_images (DiffMetricProc dmp,
+                                const int colorThreshold,
+                                RecordArray* differences,
+                                const SkString& baseDir,
+                                const SkString& comparisonDir,
+                                const SkString& outputDir,
+                                const StringArray& matchSubstrings,
+                                const StringArray& nomatchSubstrings,
+                                bool recurseIntoSubdirs,
+                                bool getBounds,
+                                DiffSummary* summary) {
+    SkASSERT(!baseDir.isEmpty());
+    SkASSERT(!comparisonDir.isEmpty());
+
+    FileArray baseFiles;
+    FileArray comparisonFiles;
+
+    get_file_list(baseDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, &baseFiles);
+    get_file_list(comparisonDir, matchSubstrings, nomatchSubstrings, recurseIntoSubdirs,
+                  &comparisonFiles);
+
+    if (!baseFiles.isEmpty()) {
+        qsort(baseFiles.begin(), baseFiles.count(), sizeof(SkString*),
+              SkCastForQSort(compare_file_name_metrics));
+    }
+    if (!comparisonFiles.isEmpty()) {
+        qsort(comparisonFiles.begin(), comparisonFiles.count(),
+              sizeof(SkString*), SkCastForQSort(compare_file_name_metrics));
+    }
+
+    int i = 0;
+    int j = 0;
+
+    while (i < baseFiles.count() &&
+           j < comparisonFiles.count()) {
+
+        SkString basePath(baseDir);
+        SkString comparisonPath(comparisonDir);
+
+        DiffRecord *drp = new DiffRecord;
+        int v = strcmp(baseFiles[i]->c_str(), comparisonFiles[j]->c_str());
+
+        if (v < 0) {
+            // in baseDir, but not in comparisonDir
+            drp->fResult = DiffRecord::kCouldNotCompare_Result;
+
+            basePath.append(*baseFiles[i]);
+            comparisonPath.append(*baseFiles[i]);
+
+            drp->fBase.fFilename = *baseFiles[i];
+            drp->fBase.fFullPath = basePath;
+            drp->fBase.fStatus = DiffResource::kExists_Status;
+
+            drp->fComparison.fFilename = *baseFiles[i];
+            drp->fComparison.fFullPath = comparisonPath;
+            drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
+
+            ++i;
+        } else if (v > 0) {
+            // in comparisonDir, but not in baseDir
+            drp->fResult = DiffRecord::kCouldNotCompare_Result;
+
+            basePath.append(*comparisonFiles[j]);
+            comparisonPath.append(*comparisonFiles[j]);
+
+            drp->fBase.fFilename = *comparisonFiles[j];
+            drp->fBase.fFullPath = basePath;
+            drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
+
+            drp->fComparison.fFilename = *comparisonFiles[j];
+            drp->fComparison.fFullPath = comparisonPath;
+            drp->fComparison.fStatus = DiffResource::kExists_Status;
+
+            ++j;
+        } else {
+            // Found the same filename in both baseDir and comparisonDir.
+            SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
+
+            basePath.append(*baseFiles[i]);
+            comparisonPath.append(*comparisonFiles[j]);
+
+            drp->fBase.fFilename = *baseFiles[i];
+            drp->fBase.fFullPath = basePath;
+            drp->fBase.fStatus = DiffResource::kExists_Status;
+
+            drp->fComparison.fFilename = *comparisonFiles[j];
+            drp->fComparison.fFullPath = comparisonPath;
+            drp->fComparison.fStatus = DiffResource::kExists_Status;
+
+            SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str()));
+            if (NULL != baseFileBits) {
+                drp->fBase.fStatus = DiffResource::kRead_Status;
+            }
+            SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str()));
+            if (NULL != comparisonFileBits) {
+                drp->fComparison.fStatus = DiffResource::kRead_Status;
+            }
+            if (NULL == baseFileBits || NULL == comparisonFileBits) {
+                if (NULL == baseFileBits) {
+                    drp->fBase.fStatus = DiffResource::kCouldNotRead_Status;
+                }
+                if (NULL == comparisonFileBits) {
+                    drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status;
+                }
+                drp->fResult = DiffRecord::kCouldNotCompare_Result;
+
+            } else if (are_buffers_equal(baseFileBits, comparisonFileBits)) {
+                drp->fResult = DiffRecord::kEqualBits_Result;
+
+            } else {
+                AutoReleasePixels arp(drp);
+                get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode);
+                get_bitmap(comparisonFileBits, drp->fComparison,
+                           SkImageDecoder::kDecodePixels_Mode);
+                if (DiffResource::kDecoded_Status == drp->fBase.fStatus &&
+                    DiffResource::kDecoded_Status == drp->fComparison.fStatus) {
+                    create_and_write_diff_image(drp, dmp, colorThreshold,
+                                                outputDir, drp->fBase.fFilename);
+                } else {
+                    drp->fResult = DiffRecord::kCouldNotCompare_Result;
+                }
+            }
+
+            ++i;
+            ++j;
+        }
+
+        if (getBounds) {
+            get_bounds(*drp);
+        }
+        SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
+        differences->push(drp);
+        summary->add(drp);
+    }
+
+    for (; i < baseFiles.count(); ++i) {
+        // files only in baseDir
+        DiffRecord *drp = new DiffRecord();
+        drp->fBase.fFilename = *baseFiles[i];
+        drp->fBase.fFullPath = baseDir;
+        drp->fBase.fFullPath.append(drp->fBase.fFilename);
+        drp->fBase.fStatus = DiffResource::kExists_Status;
+
+        drp->fComparison.fFilename = *baseFiles[i];
+        drp->fComparison.fFullPath = comparisonDir;
+        drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
+        drp->fComparison.fStatus = DiffResource::kDoesNotExist_Status;
+
+        drp->fResult = DiffRecord::kCouldNotCompare_Result;
+        if (getBounds) {
+            get_bounds(*drp);
+        }
+        differences->push(drp);
+        summary->add(drp);
+    }
+
+    for (; j < comparisonFiles.count(); ++j) {
+        // files only in comparisonDir
+        DiffRecord *drp = new DiffRecord();
+        drp->fBase.fFilename = *comparisonFiles[j];
+        drp->fBase.fFullPath = baseDir;
+        drp->fBase.fFullPath.append(drp->fBase.fFilename);
+        drp->fBase.fStatus = DiffResource::kDoesNotExist_Status;
+
+        drp->fComparison.fFilename = *comparisonFiles[j];
+        drp->fComparison.fFullPath = comparisonDir;
+        drp->fComparison.fFullPath.append(drp->fComparison.fFilename);
+        drp->fComparison.fStatus = DiffResource::kExists_Status;
+
+        drp->fResult = DiffRecord::kCouldNotCompare_Result;
+        if (getBounds) {
+            get_bounds(*drp);
+        }
+        differences->push(drp);
+        summary->add(drp);
+    }
+
+    release_file_list(&baseFiles);
+    release_file_list(&comparisonFiles);
+}
+
+static void usage (char * argv0) {
+    SkDebugf("Skia baseline image diff tool\n");
+    SkDebugf("\n"
+"Usage: \n"
+"    %s <baseDir> <comparisonDir> [outputDir] \n", argv0);
+    SkDebugf(
+"\nArguments:"
+"\n    --failonresult <result>: After comparing all file pairs, exit with nonzero"
+"\n                             return code (number of file pairs yielding this"
+"\n                             result) if any file pairs yielded this result."
+"\n                             This flag may be repeated, in which case the"
+"\n                             return code will be the number of fail pairs"
+"\n                             yielding ANY of these results."
+"\n    --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return"
+"\n                             code if any file pairs yielded this status."
+"\n    --help: display this info"
+"\n    --listfilenames: list all filenames for each result type in stdout"
+"\n    --match <substring>: compare files whose filenames contain this substring;"
+"\n                         if unspecified, compare ALL files."
+"\n                         this flag may be repeated."
+"\n    --nodiffs: don't write out image diffs or index.html, just generate"
+"\n               report on stdout"
+"\n    --nomatch <substring>: regardless of --match, DO NOT compare files whose"
+"\n                           filenames contain this substring."
+"\n                           this flag may be repeated."
+"\n    --noprintdirs: do not print the directories used."
+"\n    --norecurse: do not recurse into subdirectories."
+"\n    --sortbymaxmismatch: sort by worst color channel mismatch;"
+"\n                         break ties with -sortbymismatch"
+"\n    --sortbymismatch: sort by average color channel mismatch"
+"\n    --threshold <n>: only report differences > n (per color channel) [default 0]"
+"\n    --weighted: sort by # pixels different weighted by color difference"
+"\n"
+"\n    baseDir: directory to read baseline images from."
+"\n    comparisonDir: directory to read comparison images from"
+"\n    outputDir: directory to write difference images and index.html to;"
+"\n               defaults to comparisonDir"
+"\n"
+"\nIf no sort is specified, it will sort by fraction of pixels mismatching."
+"\n");
+}
+
+const int kNoError = 0;
+const int kGenericError = -1;
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+    DiffMetricProc diffProc = compute_diff_pmcolor;
+    int (*sortProc)(const void*, const void*) = compare<CompareDiffMetrics>;
+
+    // Maximum error tolerated in any one color channel in any one pixel before
+    // a difference is reported.
+    int colorThreshold = 0;
+    SkString baseDir;
+    SkString comparisonDir;
+    SkString outputDir;
+
+    StringArray matchSubstrings;
+    StringArray nomatchSubstrings;
+
+    bool generateDiffs = true;
+    bool listFilenames = false;
+    bool printDirNames = true;
+    bool recurseIntoSubdirs = true;
+
+    RecordArray differences;
+    DiffSummary summary;
+
+    bool failOnResultType[DiffRecord::kResultCount];
+    for (int i = 0; i < DiffRecord::kResultCount; i++) {
+        failOnResultType[i] = false;
+    }
+
+    bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount];
+    for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+        for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+            failOnStatusType[base][comparison] = false;
+        }
+    }
+
+    int i;
+    int numUnflaggedArguments = 0;
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "--failonresult")) {
+            if (argc == ++i) {
+                SkDebugf("failonresult expects one argument.\n");
+                continue;
+            }
+            DiffRecord::Result type = DiffRecord::getResultByName(argv[i]);
+            if (type != DiffRecord::kResultCount) {
+                failOnResultType[type] = true;
+            } else {
+                SkDebugf("ignoring unrecognized result <%s>\n", argv[i]);
+            }
+            continue;
+        }
+        if (!strcmp(argv[i], "--failonstatus")) {
+            if (argc == ++i) {
+                SkDebugf("failonstatus missing base status.\n");
+                continue;
+            }
+            bool baseStatuses[DiffResource::kStatusCount];
+            if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) {
+                SkDebugf("unrecognized base status <%s>\n", argv[i]);
+            }
+
+            if (argc == ++i) {
+                SkDebugf("failonstatus missing comparison status.\n");
+                continue;
+            }
+            bool comparisonStatuses[DiffResource::kStatusCount];
+            if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) {
+                SkDebugf("unrecognized comarison status <%s>\n", argv[i]);
+            }
+
+            for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+                for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+                    failOnStatusType[base][comparison] |=
+                        baseStatuses[base] && comparisonStatuses[comparison];
+                }
+            }
+            continue;
+        }
+        if (!strcmp(argv[i], "--help")) {
+            usage(argv[0]);
+            return kNoError;
+        }
+        if (!strcmp(argv[i], "--listfilenames")) {
+            listFilenames = true;
+            continue;
+        }
+        if (!strcmp(argv[i], "--match")) {
+            matchSubstrings.push(new SkString(argv[++i]));
+            continue;
+        }
+        if (!strcmp(argv[i], "--nodiffs")) {
+            generateDiffs = false;
+            continue;
+        }
+        if (!strcmp(argv[i], "--nomatch")) {
+            nomatchSubstrings.push(new SkString(argv[++i]));
+            continue;
+        }
+        if (!strcmp(argv[i], "--noprintdirs")) {
+            printDirNames = false;
+            continue;
+        }
+        if (!strcmp(argv[i], "--norecurse")) {
+            recurseIntoSubdirs = false;
+            continue;
+        }
+        if (!strcmp(argv[i], "--sortbymaxmismatch")) {
+            sortProc = compare<CompareDiffMaxMismatches>;
+            continue;
+        }
+        if (!strcmp(argv[i], "--sortbymismatch")) {
+            sortProc = compare<CompareDiffMeanMismatches>;
+            continue;
+        }
+        if (!strcmp(argv[i], "--threshold")) {
+            colorThreshold = atoi(argv[++i]);
+            continue;
+        }
+        if (!strcmp(argv[i], "--weighted")) {
+            sortProc = compare<CompareDiffWeighted>;
+            continue;
+        }
+        if (argv[i][0] != '-') {
+            switch (numUnflaggedArguments++) {
+                case 0:
+                    baseDir.set(argv[i]);
+                    continue;
+                case 1:
+                    comparisonDir.set(argv[i]);
+                    continue;
+                case 2:
+                    outputDir.set(argv[i]);
+                    continue;
+                default:
+                    SkDebugf("extra unflagged argument <%s>\n", argv[i]);
+                    usage(argv[0]);
+                    return kGenericError;
+            }
+        }
+
+        SkDebugf("Unrecognized argument <%s>\n", argv[i]);
+        usage(argv[0]);
+        return kGenericError;
+    }
+
+    if (numUnflaggedArguments == 2) {
+        outputDir = comparisonDir;
+    } else if (numUnflaggedArguments != 3) {
+        usage(argv[0]);
+        return kGenericError;
+    }
+
+    if (!baseDir.endsWith(PATH_DIV_STR)) {
+        baseDir.append(PATH_DIV_STR);
+    }
+    if (printDirNames) {
+        printf("baseDir is [%s]\n", baseDir.c_str());
+    }
+
+    if (!comparisonDir.endsWith(PATH_DIV_STR)) {
+        comparisonDir.append(PATH_DIV_STR);
+    }
+    if (printDirNames) {
+        printf("comparisonDir is [%s]\n", comparisonDir.c_str());
+    }
+
+    if (!outputDir.endsWith(PATH_DIV_STR)) {
+        outputDir.append(PATH_DIV_STR);
+    }
+    if (generateDiffs) {
+        if (printDirNames) {
+            printf("writing diffs to outputDir is [%s]\n", outputDir.c_str());
+        }
+    } else {
+        if (printDirNames) {
+            printf("not writing any diffs to outputDir [%s]\n", outputDir.c_str());
+        }
+        outputDir.set("");
+    }
+
+    // If no matchSubstrings were specified, match ALL strings
+    // (except for whatever nomatchSubstrings were specified, if any).
+    if (matchSubstrings.isEmpty()) {
+        matchSubstrings.push(new SkString(""));
+    }
+
+    create_diff_images(diffProc, colorThreshold, &differences,
+                       baseDir, comparisonDir, outputDir,
+                       matchSubstrings, nomatchSubstrings, recurseIntoSubdirs, generateDiffs,
+                       &summary);
+    summary.print(listFilenames, failOnResultType, failOnStatusType);
+
+    if (differences.count()) {
+        qsort(differences.begin(), differences.count(),
+              sizeof(DiffRecord*), sortProc);
+    }
+
+    if (generateDiffs) {
+        print_diff_page(summary.fNumMatches, colorThreshold, differences,
+                        baseDir, comparisonDir, outputDir);
+    }
+
+    for (i = 0; i < differences.count(); i++) {
+        delete differences[i];
+    }
+    matchSubstrings.deleteAll();
+    nomatchSubstrings.deleteAll();
+
+    int num_failing_results = 0;
+    for (int i = 0; i < DiffRecord::kResultCount; i++) {
+        if (failOnResultType[i]) {
+            num_failing_results += summary.fResultsOfType[i].count();
+        }
+    }
+    if (!failOnResultType[DiffRecord::kCouldNotCompare_Result]) {
+        for (int base = 0; base < DiffResource::kStatusCount; ++base) {
+            for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) {
+                if (failOnStatusType[base][comparison]) {
+                    num_failing_results += summary.fStatusOfType[base][comparison].count();
+                }
+            }
+        }
+    }
+
+    // On Linux (and maybe other platforms too), any results outside of the
+    // range [0...255] are wrapped (mod 256).  Do the conversion ourselves, to
+    // make sure that we only return 0 when there were no failures.
+    return (num_failing_results > 255) ? 255 : num_failing_results;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/skdiff_utils.cpp b/tools/skdiff_utils.cpp
new file mode 100644
index 0000000..0eb405a
--- /dev/null
+++ b/tools/skdiff_utils.cpp
@@ -0,0 +1,186 @@
+/*
+ * 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 "skdiff.h"
+#include "skdiff_utils.h"
+#include "SkBitmap.h"
+#include "SkData.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "SkTypes.h"
+
+bool are_buffers_equal(SkData* skdata1, SkData* skdata2) {
+    if ((NULL == skdata1) || (NULL == skdata2)) {
+        return false;
+    }
+    if (skdata1->size() != skdata2->size()) {
+        return false;
+    }
+    return (0 == memcmp(skdata1->data(), skdata2->data(), skdata1->size()));
+}
+
+SkData* read_file(const char* file_path) {
+    SkFILEStream fileStream(file_path);
+    if (!fileStream.isValid()) {
+        SkDebugf("WARNING: could not open file <%s> for reading\n", file_path);
+        return NULL;
+    }
+    size_t bytesInFile = fileStream.getLength();
+    size_t bytesLeftToRead = bytesInFile;
+
+    void* bufferStart = sk_malloc_throw(bytesInFile);
+    char* bufferPointer = (char*)bufferStart;
+    while (bytesLeftToRead > 0) {
+        size_t bytesReadThisTime = fileStream.read(bufferPointer, bytesLeftToRead);
+        if (0 == bytesReadThisTime) {
+            SkDebugf("WARNING: error reading from <%s>\n", file_path);
+            sk_free(bufferStart);
+            return NULL;
+        }
+        bytesLeftToRead -= bytesReadThisTime;
+        bufferPointer += bytesReadThisTime;
+    }
+    return SkData::NewFromMalloc(bufferStart, bytesInFile);
+}
+
+bool get_bitmap(SkData* fileBits, DiffResource& resource, SkImageDecoder::Mode mode) {
+    SkMemoryStream stream(fileBits->data(), fileBits->size());
+
+    SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+    if (NULL == codec) {
+        SkDebugf("ERROR: no codec found for <%s>\n", resource.fFullPath.c_str());
+        resource.fStatus = DiffResource::kCouldNotDecode_Status;
+        return false;
+    }
+
+    // In debug, the DLL will automatically be unloaded when this is deleted,
+    // but that shouldn't be a problem in release mode.
+    SkAutoTDelete<SkImageDecoder> ad(codec);
+
+    stream.rewind();
+    if (!codec->decode(&stream, &resource.fBitmap, SkBitmap::kARGB_8888_Config, mode)) {
+        SkDebugf("ERROR: codec failed for basePath <%s>\n", resource.fFullPath.c_str());
+        resource.fStatus = DiffResource::kCouldNotDecode_Status;
+        return false;
+    }
+
+    resource.fStatus = DiffResource::kDecoded_Status;
+    return true;
+}
+
+/** Thanks to PNG, we need to force all pixels 100% opaque. */
+static void force_all_opaque(const SkBitmap& bitmap) {
+   SkAutoLockPixels lock(bitmap);
+   for (int y = 0; y < bitmap.height(); y++) {
+       for (int x = 0; x < bitmap.width(); x++) {
+           *bitmap.getAddr32(x, y) |= (SK_A32_MASK << SK_A32_SHIFT);
+       }
+   }
+}
+
+bool write_bitmap(const SkString& path, const SkBitmap& bitmap) {
+    SkBitmap copy;
+    bitmap.copyTo(&copy, SkBitmap::kARGB_8888_Config);
+    force_all_opaque(copy);
+    return SkImageEncoder::EncodeFile(path.c_str(), copy,
+                                      SkImageEncoder::kPNG_Type, 100);
+}
+
+/// Return a copy of the "input" string, within which we have replaced all instances
+/// of oldSubstring with newSubstring.
+///
+/// TODO: If we like this, we should move it into the core SkString implementation,
+/// adding more checks and ample test cases, and paying more attention to efficiency.
+static SkString replace_all(const SkString &input,
+                            const char oldSubstring[], const char newSubstring[]) {
+    SkString output;
+    const char *input_cstr = input.c_str();
+    const char *first_char = input_cstr;
+    const char *match_char;
+    int oldSubstringLen = strlen(oldSubstring);
+    while (NULL != (match_char = strstr(first_char, oldSubstring))) {
+        output.append(first_char, (match_char - first_char));
+        output.append(newSubstring);
+        first_char = match_char + oldSubstringLen;
+    }
+    output.append(first_char);
+    return output;
+}
+
+static SkString filename_to_derived_filename(const SkString& filename, const char *suffix) {
+    SkString diffName (filename);
+    const char* cstring = diffName.c_str();
+    int dotOffset = strrchr(cstring, '.') - cstring;
+    diffName.remove(dotOffset, diffName.size() - dotOffset);
+    diffName.append(suffix);
+
+    // In case we recursed into subdirectories, replace slashes with something else
+    // so the diffs will all be written into a single flat directory.
+    diffName = replace_all(diffName, PATH_DIV_STR, "_");
+    return diffName;
+}
+
+SkString filename_to_diff_filename(const SkString& filename) {
+    return filename_to_derived_filename(filename, "-diff.png");
+}
+
+SkString filename_to_white_filename(const SkString& filename) {
+    return filename_to_derived_filename(filename, "-white.png");
+}
+
+void create_and_write_diff_image(DiffRecord* drp,
+                                 DiffMetricProc dmp,
+                                 const int colorThreshold,
+                                 const SkString& outputDir,
+                                 const SkString& filename) {
+    const int w = drp->fBase.fBitmap.width();
+    const int h = drp->fBase.fBitmap.height();
+
+    if (w != drp->fComparison.fBitmap.width() || h != drp->fComparison.fBitmap.height()) {
+        drp->fResult = DiffRecord::kDifferentSizes_Result;
+    } else {
+        drp->fDifference.fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+        drp->fDifference.fBitmap.allocPixels();
+
+        drp->fWhite.fBitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+        drp->fWhite.fBitmap.allocPixels();
+
+        SkASSERT(DiffRecord::kUnknown_Result == drp->fResult);
+        compute_diff(drp, dmp, colorThreshold);
+        SkASSERT(DiffRecord::kUnknown_Result != drp->fResult);
+    }
+
+    if (outputDir.isEmpty()) {
+        drp->fDifference.fStatus = DiffResource::kUnspecified_Status;
+        drp->fWhite.fStatus = DiffResource::kUnspecified_Status;
+
+    } else {
+        drp->fDifference.fFilename = filename_to_diff_filename(filename);
+        drp->fDifference.fFullPath = outputDir;
+        drp->fDifference.fFullPath.append(drp->fDifference.fFilename);
+        drp->fDifference.fStatus = DiffResource::kSpecified_Status;
+
+        drp->fWhite.fFilename = filename_to_white_filename(filename);
+        drp->fWhite.fFullPath = outputDir;
+        drp->fWhite.fFullPath.append(drp->fWhite.fFilename);
+        drp->fWhite.fStatus = DiffResource::kSpecified_Status;
+
+        if (DiffRecord::kDifferentPixels_Result == drp->fResult) {
+            if (write_bitmap(drp->fDifference.fFullPath, drp->fDifference.fBitmap)) {
+                drp->fDifference.fStatus = DiffResource::kExists_Status;
+            } else {
+                drp->fDifference.fStatus = DiffResource::kDoesNotExist_Status;
+            }
+            if (write_bitmap(drp->fWhite.fFullPath, drp->fWhite.fBitmap)) {
+                drp->fWhite.fStatus = DiffResource::kExists_Status;
+            } else {
+                drp->fWhite.fStatus = DiffResource::kDoesNotExist_Status;
+            }
+        }
+    }
+}
diff --git a/tools/skdiff_utils.h b/tools/skdiff_utils.h
new file mode 100644
index 0000000..00ebf89
--- /dev/null
+++ b/tools/skdiff_utils.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2012 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef skdiff_utils_DEFINED
+#define skdiff_utils_DEFINED
+
+#include "skdiff.h"
+#include "SkImageDecoder.h"
+
+class SkBitmap;
+class SkData;
+class SkString;
+
+/** Returns true if the two buffers passed in are both non-NULL,
+ *  have the same length, and contain exactly the same byte values.
+ */
+bool are_buffers_equal(SkData* skdata1, SkData* skdata2);
+
+/** Reads the file at the given path and returns its complete contents as an
+ *  SkData object (or returns NULL on error).
+ */
+SkData* read_file(const char* file_path);
+
+/** Decodes the fileBits into the resource.fBitmap. Returns false on failure. */
+bool get_bitmap(SkData* fileBits, DiffResource& resource, SkImageDecoder::Mode mode);
+
+/** Writes the bitmap as a PNG to the path specified. */
+bool write_bitmap(const SkString& path, const SkBitmap& bitmap);
+
+/** Given an image filename, returns the name of the file containing
+ *  the associated difference image.
+ */
+SkString filename_to_diff_filename(const SkString& filename);
+
+/** Given an image filename, returns the name of the file containing
+ *  the "white" difference image.
+ */
+SkString filename_to_white_filename(const SkString& filename);
+
+/** Calls compute_diff and handles the difference and white diff resources.
+ *  If !outputDir.isEmpty(), writes out difference and white images.
+ */
+void create_and_write_diff_image(DiffRecord* drp,
+                                 DiffMetricProc dmp,
+                                 const int colorThreshold,
+                                 const SkString& outputDir,
+                                 const SkString& filename);
+
+#endif
diff --git a/tools/skhello.cpp b/tools/skhello.cpp
new file mode 100644
index 0000000..465806f
--- /dev/null
+++ b/tools/skhello.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkCanvas.h"
+#include "SkGraphics.h"
+#include "SkImageEncoder.h"
+#include "SkString.h"
+
+static void show_help() {
+    SkDebugf("usage: skhello [-o out-dir] [-t 'hello']\n  default output: skhello.png\n");
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+    SkAutoGraphics ag;
+    SkString path("skhello.png");
+    SkString text("Hello");
+
+    for (int i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "--help")) {
+            show_help();
+            return 0;
+        }
+        if (!strcmp(argv[i], "-o")) {
+            if (i == argc-1) {
+                SkDebugf("ERROR: -o needs a following filename\n");
+                return -1;
+            }
+            path.set(argv[i+1]);
+            i += 1; // skip the out dir name
+        } else if (!strcmp(argv[i], "-t")) {
+            if (i == argc-1) {
+                SkDebugf("ERROR: -t needs a following string\n");
+                return -1;
+            }
+            text.set(argv[i+1]);
+            i += 1; // skip the text string
+        }
+    }
+
+    SkPaint paint;
+    paint.setAntiAlias(true);
+    paint.setTextSize(SkIntToScalar(30));
+    SkScalar width = paint.measureText(text.c_str(), text.size());
+    SkScalar spacing = paint.getFontSpacing();
+
+    int w = SkScalarRound(width) + 30;
+    int h = SkScalarRound(spacing) + 30;
+    SkBitmap bitmap;
+    bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
+    bitmap.allocPixels();
+
+    SkCanvas canvas(bitmap);
+    canvas.drawColor(SK_ColorWHITE);
+
+    paint.setTextAlign(SkPaint::kCenter_Align);
+    canvas.drawText(text.c_str(), text.size(),
+                    SkIntToScalar(w)/2, SkIntToScalar(h)*2/3,
+                    paint);
+
+    bool success = SkImageEncoder::EncodeFile(path.c_str(), bitmap,
+                               SkImageEncoder::kPNG_Type, 100);
+    if (!success) {
+        SkDebugf("--- failed to write %s\n", path.c_str());
+    }
+    return !success;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/skimage_main.cpp b/tools/skimage_main.cpp
new file mode 100644
index 0000000..53cc6b3
--- /dev/null
+++ b/tools/skimage_main.cpp
@@ -0,0 +1,110 @@
+
+/*
+ * 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 "SkBitmap.h"
+#include "SkGraphics.h"
+#include "SkImageDecoder.h"
+#include "SkImageEncoder.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+
+static bool decodeFile(SkBitmap* bitmap, const char srcPath[]) {
+    SkFILEStream stream(srcPath);
+    if (!stream.isValid()) {
+        SkDebugf("ERROR: bad filename <%s>\n", srcPath);
+        return false;
+    }
+
+    SkImageDecoder* codec = SkImageDecoder::Factory(&stream);
+    if (NULL == codec) {
+        SkDebugf("ERROR: no codec found for <%s>\n", srcPath);
+        return false;
+    }
+
+    SkAutoTDelete<SkImageDecoder> ad(codec);
+
+    stream.rewind();
+    if (!codec->decode(&stream, bitmap, SkBitmap::kARGB_8888_Config,
+                       SkImageDecoder::kDecodePixels_Mode)) {
+        SkDebugf("ERROR: codec failed for <%s>\n", srcPath);
+        return false;
+    }
+    return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+static void show_help() {
+    SkDebugf("usage: skiamge [-o out-dir] inputfiles...\n");
+}
+
+static void make_outname(SkString* dst, const char outDir[], const char src[]) {
+    dst->set(outDir);
+    const char* start = strrchr(src, '/');
+    if (start) {
+        start += 1; // skip the actual last '/'
+    } else {
+        start = src;
+    }
+    dst->append(start);
+    dst->append(".png");
+}
+
+int tool_main(int argc, char** argv);
+int tool_main(int argc, char** argv) {
+    SkAutoGraphics ag;
+    int i, outDirIndex = 0;
+    SkString outDir;
+
+    for (i = 1; i < argc; i++) {
+        if (!strcmp(argv[i], "-help")) {
+            show_help();
+            return 0;
+        }
+        if (!strcmp(argv[i], "-o")) {
+            if (i == argc-1) {
+                SkDebugf("ERROR: -o needs a following filename\n");
+                return -1;
+            }
+            outDirIndex = i;
+            outDir.set(argv[i+1]);
+            if (outDir.c_str()[outDir.size() - 1] != '/') {
+                outDir.append("/");
+            }
+            i += 1; // skip the out dir name
+        }
+    }
+
+    for (i = 1; i < argc; i++) {
+        if (i == outDirIndex) {
+            i += 1; // skip this and the next entry
+            continue;
+        }
+
+        SkBitmap bitmap;
+        if (decodeFile(&bitmap, argv[i])) {
+            if (outDirIndex) {
+                SkString outPath;
+                make_outname(&outPath, outDir.c_str(), argv[i]);
+                SkDebugf("  writing %s\n", outPath.c_str());
+                SkImageEncoder::EncodeFile(outPath.c_str(), bitmap,
+                                           SkImageEncoder::kPNG_Type, 100);
+            } else {
+                SkDebugf("  decoded %s [%d %d]\n", argv[i], bitmap.width(),
+                         bitmap.height());
+            }
+        }
+    }
+
+    return 0;
+}
+
+#if !defined SK_BUILD_FOR_IOS
+int main(int argc, char * const argv[]) {
+    return tool_main(argc, (char**) argv);
+}
+#endif
diff --git a/tools/submit_try b/tools/submit_try
new file mode 100755
index 0000000..0a4cf2d
--- /dev/null
+++ b/tools/submit_try
@@ -0,0 +1,266 @@
+#!/usr/bin/python
+
+# Copyright (c) 2013 The Chromium Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+
+"""
+submit_try: Submit a try request.
+
+This is a thin wrapper around the try request utilities in depot_tools which
+adds some validation and supports both git and svn.
+"""
+
+
+import httplib
+import json
+import os
+import subprocess
+import sys
+
+
+# Alias which can be used to run a try on every builder.
+ALL_BUILDERS = 'all'
+
+# Contact information for the build master.
+# TODO(borenet): Share this information from a single location. Filed bug:
+# http://code.google.com/p/skia/issues/detail?id=1081
+SKIA_BUILD_MASTER_HOST = '70.32.156.51'
+SKIA_BUILD_MASTER_PORT = '10117'
+
+# All try builders have this suffix.
+TRYBOT_SUFFIX = '_Trybot'
+
+# Location of the codereview.settings file in the Skia repo.
+SKIA_URL = 'skia.googlecode.com'
+CODEREVIEW_SETTINGS = '/svn/codereview.settings'
+
+# String for matching the svn url of the try server inside codereview.settings.
+TRYSERVER_SVN_URL = 'TRYSERVER_SVN_URL: '
+
+# Strings used for matching svn config properties.
+URL_STR = 'URL: '
+REPO_ROOT_STR = 'Repository Root: '
+
+
+def FindDepotTools():
+  """ Find depot_tools on the local machine and return its location. """
+  which_cmd = 'where' if os.name == 'nt' else 'which'
+  cmd = [which_cmd, 'gcl']
+  proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+  if proc.wait() != 0:
+    raise Exception('Couldn\'t find depot_tools in PATH!')
+  gcl = proc.communicate()[0].split('\n')[0].rstrip()
+  depot_tools_dir = os.path.dirname(gcl)
+  return depot_tools_dir
+
+
+def GetCheckoutRoot(is_svn=True):
+  """ Determine where the local checkout is rooted.
+
+  is_svn: boolean; whether we're in an SVN checkout. If False, assume we're in
+      a git checkout.
+  """
+  if is_svn:
+    svn_cmd = 'svn.bat' if os.name == 'nt' else 'svn'
+    cmd = [svn_cmd, 'info']
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT)
+    if proc.wait() != 0:
+      raise Exception('Couldn\'t find checkout root!')
+    output = proc.communicate()[0].split('\n')
+    url = None
+    repo_root = None
+    for line in output:
+      if line.startswith(REPO_ROOT_STR):
+        repo_root = line[len(REPO_ROOT_STR):].rstrip()
+      elif line.startswith(URL_STR):
+        url = line[len(URL_STR):].rstrip()
+    if not url or not repo_root:
+      raise Exception('Couldn\'t find checkout root!')
+    if url == repo_root:
+      return 'svn'
+    return url[len(repo_root)+1:]
+  else:
+    cmd = ['git', 'rev-parse', '--show-toplevel']
+    proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT)
+    if proc.wait() != 0:
+      raise Exception('Couldn\'t find checkout root!')
+    return os.path.basename(proc.communicate()[0])
+
+
+def GetTryRepo():
+  """ Determine the TRYSERVER_SVN_URL from the codereview.settings file in the
+  Skia repo. """
+  connection = httplib.HTTPConnection(SKIA_URL)
+  connection.request('GET', CODEREVIEW_SETTINGS)
+  content = connection.getresponse().read()
+  for line in content.split('\n'):
+    if line.startswith(TRYSERVER_SVN_URL):
+      return line[len(TRYSERVER_SVN_URL):].rstrip()
+  raise Exception('Couldn\'t determine the TRYSERVER_SVN_URL. Make sure it is '
+                  'defined in the %s file.' % CODEREVIEW_SETTINGS)
+
+
+def RetrieveTrybotList():
+  """ Retrieve the list of known trybots from the build master, stripping
+  TRYBOT_SUFFIX from the name. """
+  trybots = []
+  connection = httplib.HTTPConnection(SKIA_BUILD_MASTER_HOST,  
+                                      SKIA_BUILD_MASTER_PORT)  
+  connection.request('GET', '/json/builders')  
+  response = connection.getresponse()  
+  builders = json.load(response)
+
+  for builder in builders:
+    if builder.endswith(TRYBOT_SUFFIX):
+      trybots.append(builder[:-len(TRYBOT_SUFFIX)])
+  return trybots
+
+
+def ValidateArgs(argv, trybots, is_svn=True):
+  """ Parse and validate command-line arguments. If the arguments are valid,
+  returns a tuple of (<changelist name>, <list of trybots>).
+
+  trybots: A list of the known try builders.
+  """
+
+  class CollectedArgs(object):
+    def __init__(self, bots, changelist, revision):
+      self._bots = bots
+      self._changelist = changelist
+      self._revision = revision
+
+    @property
+    def bots(self):
+      for bot in self._bots:
+        yield bot
+
+    @property
+    def changelist(self):
+      return self._changelist
+
+    @property
+    def revision(self):
+      return self._revision
+
+  usage = (
+"""submit_try: Submit a try request.
+submit_try %s--bot <buildername> [<buildername> ...]
+
+--bot               Builder on which to run the try. Required.
+-h, --help          Show this message.
+-r <revision#>      Revision from which to run the try.
+-l, --list_bots     List the available try builders and exit.
+""" % ('<changelist> ' if is_svn else ''))
+
+  def Error(msg=None):
+    if msg:
+      print msg
+    print usage
+    sys.exit(1)
+
+  using_bots = None
+  changelist = None
+  revision = None
+
+  while argv:
+    arg = argv.pop(0)
+    if arg == '-h' or arg == '--help':
+      Error()
+    elif arg == '-l' or arg == '--list_bots':
+      print 'submit_try: Available builders:\n  %s' % '\n  '.join(trybots)
+      sys.exit(0)
+    elif arg == '--bot':
+      if using_bots:
+        Error('--bot specified multiple times.')
+      if len(argv) < 1:
+        Error('You must specify a builder with "--bot".')
+      using_bots = []
+      while argv and not argv[0].startswith('-'):
+        bot = argv.pop(0)
+        if bot == ALL_BUILDERS:
+          if using_bots:
+            Error('Cannot specify "all" with additional builder names.')
+          using_bots = trybots
+          break
+        else:
+          if not bot in trybots:
+            Error('Unrecognized builder: %s' % bot)
+          using_bots.append(bot)
+    elif arg == '-r':
+      if len(argv) < 1:
+        Error('You must specify a revision with "-r".')
+      revision = argv.pop(0)
+    else:
+      if changelist or not is_svn:
+        Error('Unknown argument: %s' % arg)
+      changelist = arg
+  if is_svn and not changelist:
+    Error('You must specify a changelist name.')
+  if not using_bots:
+    Error('You must specify one or more builders using --bot.')
+  return CollectedArgs(bots=using_bots, changelist=changelist,
+                       revision=revision)
+
+
+def SubmitTryRequest(args, is_svn=True):
+  """ Submits a try request for the given changelist on the given list of
+  trybots.
+
+  args: Object whose properties are derived from command-line arguments. If
+      is_svn is True, it should contain:
+      - changelist: string; the name of the changelist to try.
+      - bot: list of strings; the names of the try builders to run.
+      - revision: optional, int; the revision number from which to run the try.
+      If is_svn is False, it should contain:
+      - bot: list of strings; the names of the try builders to run.
+      - revision: optional, int; the revision number from which to run the try. 
+  is_svn: boolean; are we in an SVN repo?
+  """
+  botlist = ','.join(['%s%s' % (bot, TRYBOT_SUFFIX) for bot in args.bots])
+  if is_svn:
+    gcl_cmd = 'gcl.bat' if os.name == 'nt' else 'gcl'
+    try_args = [gcl_cmd, 'try', args.changelist,
+                '--root', GetCheckoutRoot(is_svn),
+                '--bot', botlist]
+    if args.revision:
+      try_args.extend(['-r', args.revision])
+    print ' '.join(try_args)
+    proc = subprocess.Popen(try_args, stdout=subprocess.PIPE,
+                            stderr=subprocess.STDOUT)
+    if proc.wait() != 0:
+      raise Exception('Failed to submit try request: %s' % (
+          proc.communicate()[0]))
+    print proc.communicate()[0]
+  else:
+    # First, find depot_tools. This is needed to import trychange.
+    sys.path.append(FindDepotTools())
+    import trychange
+    try_args = ['--use_svn',
+                '--svn_repo', GetTryRepo(),
+                '--root', GetCheckoutRoot(is_svn),
+                '--bot', botlist]
+    if args.revision:
+      try_args.extend(['-r', args.revision])
+    trychange.TryChange(try_args, None, False)
+
+
+def main():
+  # Retrieve the list of active try builders from the build master.
+  trybots = RetrieveTrybotList()
+
+  # Determine if we're in an SVN checkout.
+  is_svn = os.path.isdir('.svn')
+
+  # Parse and validate the command-line arguments.
+  args = ValidateArgs(sys.argv[1:], trybots=trybots, is_svn=is_svn)
+
+  # Submit the try request.
+  SubmitTryRequest(args, is_svn=is_svn)
+
+
+if __name__ == '__main__':
+  sys.exit(main())
\ No newline at end of file
diff --git a/tools/submit_try.bat b/tools/submit_try.bat
new file mode 100644
index 0000000..d1d5392
--- /dev/null
+++ b/tools/submit_try.bat
@@ -0,0 +1 @@
+python tools\submit_try %*
\ No newline at end of file
diff --git a/tools/svn.py b/tools/svn.py
new file mode 100644
index 0000000..9bac8e1
--- /dev/null
+++ b/tools/svn.py
@@ -0,0 +1,141 @@
+'''
+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 fnmatch
+import os
+import re
+import subprocess
+
+PROPERTY_MIMETYPE = 'svn:mime-type'
+
+# Status types for GetFilesWithStatus()
+STATUS_ADDED                 = 0x01
+STATUS_DELETED               = 0x02
+STATUS_MODIFIED              = 0x04
+STATUS_NOT_UNDER_SVN_CONTROL = 0x08
+
+class Svn:
+
+    def __init__(self, directory):
+        """Set up to manipulate SVN control within the given directory.
+
+        @param directory
+        """
+        self._directory = directory
+
+    def _RunCommand(self, args):
+        """Run a command (from self._directory) and return stdout as a single
+        string.
+
+        @param args a list of arguments
+        """
+        print 'RunCommand: %s' % args
+        proc = subprocess.Popen(args, cwd=self._directory,
+                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        if proc.returncode is not 0:
+            raise Exception('command "%s" failed in dir "%s": %s' %
+                            (args, self._directory, stderr))
+        return stdout
+
+    def Checkout(self, url, path):
+        """Check out a working copy from a repository.
+        Returns stdout as a single string.
+
+        @param url URL from which to check out the working copy
+        @param path path (within self._directory) where the local copy will be
+        written
+        """
+        return self._RunCommand(['svn', 'checkout', url, path])
+
+    def ListSubdirs(self, url):
+        """Returns a list of all subdirectories (not files) within a given SVN
+        url.
+
+        @param url remote directory to list subdirectories of
+        """
+        subdirs = []
+        filenames = self._RunCommand(['svn', 'ls', url]).split('\n')
+        for filename in filenames:
+            if filename.endswith('/'):
+                subdirs.append(filename.strip('/'))
+        return subdirs
+
+    def GetNewFiles(self):
+        """Return a list of files which are in this directory but NOT under
+        SVN control.
+        """
+        return self.GetFilesWithStatus(STATUS_NOT_UNDER_SVN_CONTROL)
+
+    def GetNewAndModifiedFiles(self):
+        """Return a list of files in this dir which are newly added or modified,
+        including those that are not (yet) under SVN control.
+        """
+        return self.GetFilesWithStatus(
+            STATUS_ADDED | STATUS_MODIFIED | STATUS_NOT_UNDER_SVN_CONTROL)
+
+    def GetFilesWithStatus(self, status):
+        """Return a list of files in this dir with the given SVN status.
+
+        @param status bitfield combining one or more STATUS_xxx values
+        """
+        status_types_string = ''
+        if status & STATUS_ADDED:
+            status_types_string += 'A'
+        if status & STATUS_DELETED:
+            status_types_string += 'D'
+        if status & STATUS_MODIFIED:
+            status_types_string += 'M'
+        if status & STATUS_NOT_UNDER_SVN_CONTROL:
+            status_types_string += '\?'
+        status_regex_string = '^[%s].....\s+(.+)$' % status_types_string
+        stdout = self._RunCommand(['svn', 'status'])
+        status_regex = re.compile(status_regex_string, re.MULTILINE)
+        files = status_regex.findall(stdout)
+        return files
+
+    def AddFiles(self, filenames):
+        """Adds these files to SVN control.
+
+        @param filenames files to add to SVN control
+        """
+        self._RunCommand(['svn', 'add'] + filenames)
+
+    def SetProperty(self, filenames, property_name, property_value):
+        """Sets a svn property for these files.
+
+        @param filenames files to set property on
+        @param property_name property_name to set for each file
+        @param property_value what to set the property_name to
+        """
+        if filenames:
+            self._RunCommand(
+                ['svn', 'propset', property_name, property_value] + filenames)
+
+    def SetPropertyByFilenamePattern(self, filename_pattern,
+                                     property_name, property_value):
+        """Sets a svn property for all files matching filename_pattern.
+
+        @param filename_pattern set the property for all files whose names match
+               this Unix-style filename pattern (e.g., '*.jpg')
+        @param property_name property_name to set for each file
+        @param property_value what to set the property_name to
+        """
+        all_files = os.listdir(self._directory)
+        matching_files = sorted(fnmatch.filter(all_files, filename_pattern))
+        self.SetProperty(matching_files, property_name, property_value)
+
+    def ExportBaseVersionOfFile(self, file_within_repo, dest_path):
+        """Retrieves a copy of the base version (what you would get if you ran
+        'svn revert') of a file within the repository.
+
+        @param file_within_repo path to the file within the repo whose base
+               version you wish to obtain
+        @param dest_path destination to which to write the base content
+        """
+        self._RunCommand(['svn', 'export', '--revision', 'BASE',
+                          file_within_repo, dest_path])
diff --git a/tools/svndiff.py b/tools/svndiff.py
new file mode 100644
index 0000000..d8beb8d
--- /dev/null
+++ b/tools/svndiff.py
@@ -0,0 +1,141 @@
+'''
+Generates a visual diff of all pending changes in the local SVN checkout.
+
+Launch with --help to see more information.
+
+
+Copyright 2012 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+
+# common Python modules
+import optparse
+import os
+import re
+import shutil
+import tempfile
+
+# modules declared within this same directory
+import svn
+
+USAGE_STRING = 'Usage: %s [options]'
+HELP_STRING = '''
+
+Generates a visual diff of all pending changes in the local SVN checkout.
+
+This includes a list of all files that have been added, deleted, or modified
+(as far as SVN knows about).  For any image modifications, pixel diffs will
+be generated.
+
+'''
+
+TRUNK_PATH = os.path.join(os.path.dirname(__file__), os.pardir)
+
+OPTION_DEST_DIR = '--dest-dir'
+# default DEST_DIR is determined at runtime
+OPTION_PATH_TO_SKDIFF = '--path-to-skdiff'
+# default PATH_TO_SKDIFF is determined at runtime
+
+def RunCommand(command):
+    """Run a command, raising an exception if it fails.
+
+    @param command the command as a single string
+    """
+    print 'running command [%s]...' % command
+    retval = os.system(command)
+    if retval is not 0:
+        raise Exception('command [%s] failed' % command)
+
+def FindPathToSkDiff(user_set_path=None):
+    """Return path to an existing skdiff binary, or raise an exception if we
+    cannot find one.
+
+    @param user_set_path if None, the user did not specify a path, so look in
+           some likely places; otherwise, only check at this path
+    """
+    if user_set_path is not None:
+        if os.path.isfile(user_set_path):
+            return user_set_path
+        raise Exception('unable to find skdiff at user-set path %s' %
+                        user_set_path)
+    trunk_path = os.path.join(os.path.dirname(__file__), os.pardir)
+    possible_paths = [os.path.join(trunk_path, 'out', 'Release', 'skdiff'),
+                      os.path.join(trunk_path, 'out', 'Debug', 'skdiff')]
+    for try_path in possible_paths:
+        if os.path.isfile(try_path):
+            return try_path
+    raise Exception('cannot find skdiff in paths %s; maybe you need to '
+                    'specify the %s option or build skdiff?' % (
+                        possible_paths, OPTION_PATH_TO_SKDIFF))
+
+def SvnDiff(path_to_skdiff, dest_dir):
+    """Generates a visual diff of all pending changes in the local SVN checkout.
+
+    @param path_to_skdiff
+    @param dest_dir existing directory within which to write results
+    """
+    # Validate parameters, filling in default values if necessary and possible.
+    path_to_skdiff = FindPathToSkDiff(path_to_skdiff)
+    if not dest_dir:
+        dest_dir = tempfile.mkdtemp()
+
+    # Prepare temporary directories.
+    modified_flattened_dir = os.path.join(dest_dir, 'modified_flattened')
+    original_flattened_dir = os.path.join(dest_dir, 'original_flattened')
+    diff_dir = os.path.join(dest_dir, 'diffs')
+    for dir in [modified_flattened_dir, original_flattened_dir, diff_dir] :
+        shutil.rmtree(dir, ignore_errors=True)
+        os.mkdir(dir)
+
+    # Get a list of all locally modified (including added/deleted) files,
+    # descending subdirectories.
+    svn_repo = svn.Svn('.')
+    modified_file_paths = svn_repo.GetFilesWithStatus(
+        svn.STATUS_ADDED | svn.STATUS_DELETED | svn.STATUS_MODIFIED)
+
+    # For each modified file:
+    # 1. copy its current contents into modified_flattened_dir
+    # 2. copy its original contents into original_flattened_dir
+    for modified_file_path in modified_file_paths:
+        dest_filename = re.sub(os.sep, '__', modified_file_path)
+        # If the file had STATUS_DELETED, it won't exist anymore...
+        if os.path.isfile(modified_file_path):
+            shutil.copyfile(modified_file_path,
+                            os.path.join(modified_flattened_dir, dest_filename))
+        svn_repo.ExportBaseVersionOfFile(
+            modified_file_path,
+            os.path.join(original_flattened_dir, dest_filename))
+
+    # Run skdiff: compare original_flattened_dir against modified_flattened_dir
+    RunCommand('%s %s %s %s' % (path_to_skdiff, original_flattened_dir,
+                                modified_flattened_dir, diff_dir))
+    print '\nskdiff results are ready in file://%s/index.html' % diff_dir
+
+def RaiseUsageException():
+    raise Exception('%s\nRun with --help for more detail.' % (
+        USAGE_STRING % __file__))
+
+def Main(options, args):
+    """Allow other scripts to call this script with fake command-line args.
+    """
+    num_args = len(args)
+    if num_args != 0:
+        RaiseUsageException()
+    SvnDiff(path_to_skdiff=options.path_to_skdiff, dest_dir=options.dest_dir)
+
+if __name__ == '__main__':
+    parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
+    parser.add_option(OPTION_DEST_DIR,
+                      action='store', type='string', default=None,
+                      help='existing directory within which to write results; '
+                      'if not set, will create a temporary directory which '
+                      'will remain in place after this script completes')
+    parser.add_option(OPTION_PATH_TO_SKDIFF,
+                      action='store', type='string', default=None,
+                      help='path to already-built skdiff tool; if not set, '
+                      'will search for it in typical directories near this '
+                      'script')
+    (options, args) = parser.parse_args()
+    Main(options, args)
diff --git a/tools/test_pdfs.py b/tools/test_pdfs.py
new file mode 100644
index 0000000..ac3eab9
--- /dev/null
+++ b/tools/test_pdfs.py
@@ -0,0 +1,60 @@
+'''
+Compares the rendererings of serialized SkPictures to expected images.
+
+Launch with --help to see more information.
+
+
+Copyright 2012 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+# common Python modules
+import os
+import optparse
+import sys
+import shutil
+import tempfile
+import test_rendering
+
+USAGE_STRING = 'Usage: %s input... expectedDir'
+HELP_STRING = '''
+
+Takes input SkPicture files and renders them as PDF files, and then compares
+those resulting PDF files against PDF files found in expectedDir.
+
+Each instance of "input" can be either a file (name must end in .skp), or a
+directory (in which case this script will process all .skp files within the
+directory).
+'''
+
+
+def Main(args):
+    """Allow other scripts to call this script with fake command-line args.
+
+    @param The commandline argument list
+    """
+    parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
+    parser.add_option('--render_dir', dest='render_dir',
+                      help = ('specify the location to output the rendered '
+                      'files. Default is a temp directory.'))
+    parser.add_option('--diff_dir', dest='diff_dir',
+                      help = ('specify the location to output the diff files. '
+                      'Default is a temp directory.'))
+
+    options, arguments = parser.parse_args(args)
+
+    if (len(arguments) < 3):
+        print("Expected at least one input and one ouput folder.")
+        parser.print_help()
+        sys.exit(-1)
+
+    inputs = arguments[1:-1]
+    expected_dir = arguments[-1]
+
+    test_rendering.TestRenderSkps(inputs, expected_dir, options.render_dir,
+                                  options.diff_dir, 'render_pdfs', '')
+
+if __name__ == '__main__':
+    Main(sys.argv)
+
diff --git a/tools/test_pictures.py b/tools/test_pictures.py
new file mode 100644
index 0000000..b2397c4
--- /dev/null
+++ b/tools/test_pictures.py
@@ -0,0 +1,97 @@
+'''
+Compares the rendererings of serialized SkPictures to expected images.
+
+Launch with --help to see more information.
+
+
+Copyright 2012 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+# common Python modules
+import os
+import optparse
+import sys
+import shutil
+import tempfile
+
+# modules declared within this same directory
+import test_rendering
+
+USAGE_STRING = 'Usage: %s input... expectedDir'
+HELP_STRING = '''
+
+Takes input SkPicture files and renders them as PNG files, and then compares
+those resulting PNG files against PNG files found in expectedDir.
+
+Each instance of "input" can be either a file (name must end in .skp), or a
+directory (in which case this script will process all .skp files within the
+directory).
+'''
+
+def ModeParse(option, opt_str, value, parser):
+    """Parses the --mode option of the commandline.
+
+    The --mode option will either take in three parameters (if tile or
+    pow2tile) or a single parameter (otherwise).
+    """
+    result = [value]
+    if value == "tile":
+          if (len(parser.rargs) < 2):
+              raise optparse.OptionValueError(("--mode tile mising width"
+                                               " and/or height parameters"))
+          result.extend(parser.rargs[:2])
+          del parser.rargs[:2]
+    elif value == "pow2tile":
+          if (len(parser.rargs) < 2):
+              raise optparse.OptionValueError(("--mode pow2tile mising minWidth"
+                                               " and/or height parameters"))
+          result.extend(parser.rargs[:2])
+          del parser.rargs[:2]
+
+    setattr(parser.values, option.dest, result)
+
+
+def Main(args):
+    """Allow other scripts to call this script with fake command-line args.
+
+    @param The commandline argument list
+    """
+    parser = optparse.OptionParser(USAGE_STRING % '%prog' + HELP_STRING)
+    parser.add_option('--render_dir', dest='render_dir',
+                    help = ("specify the location to output the rendered files."
+                              " Default is a temp directory."))
+    parser.add_option('--diff_dir', dest='diff_dir',
+                    help = ("specify the location to output the diff files."
+                              " Default is a temp directory."))
+    parser.add_option('--mode', dest='mode', type='string',
+                      action="callback", callback=ModeParse,
+                      help = ("specify how rendering is to be done."))
+    parser.add_option('--device', dest='device',
+                      help = ("specify the device to render to."))
+
+    options, arguments = parser.parse_args(args)
+
+    if (len(arguments) < 3):
+        print("Expected at least one input and one ouput folder.")
+        parser.print_help()
+        sys.exit(-1)
+
+    inputs = arguments[1:-1]
+    expected_dir = arguments[-1]
+
+    extra_args = ''
+
+    if (options.mode is not None):
+        extra_args += ' --mode %s' % ' '.join(options.mode)
+
+    if (options.device is not None):
+        extra_args += ' --device %s' % options.device
+
+    test_rendering.TestRenderSkps(inputs, expected_dir, options.render_dir,
+                                  options.diff_dir, 'render_pictures',
+                                  extra_args)
+
+if __name__ == '__main__':
+    Main(sys.argv)
diff --git a/tools/test_rendering.py b/tools/test_rendering.py
new file mode 100644
index 0000000..35e6d94
--- /dev/null
+++ b/tools/test_rendering.py
@@ -0,0 +1,118 @@
+'''
+Compares the rendererings of serialized SkPictures to expected result.
+
+Copyright 2012 Google Inc.
+
+Use of this source code is governed by a BSD-style license that can be
+found in the LICENSE file.
+'''
+# common Python modules
+import os
+import optparse
+import sys
+import shutil
+import tempfile
+
+USAGE_STRING = 'Usage: %s input... expectedDir render_app [reander_app_args]'
+HELP_STRING = '''
+
+Compares the renderings of serialized SkPicture files and directories specified
+by input with the files in expectedDir. Note, files in directoriers are
+expected to end with .skp.
+'''
+
+def RunCommand(command):
+    """Run a command.
+
+    @param command the command as a single string
+    """
+    print 'running command [%s]...' % command
+    os.system(command)
+
+
+def FindPathToProgram(program):
+    """Return path to an existing program binary, or raise an exception if we
+    cannot find one.
+
+    @param program the name of the program that is being looked for
+    """
+    trunk_path = os.path.abspath(os.path.join(os.path.dirname(__file__),
+                                              os.pardir))
+    possible_paths = [os.path.join(trunk_path, 'out', 'Release', program),
+                      os.path.join(trunk_path, 'out', 'Debug', program),
+                      os.path.join(trunk_path, 'out', 'Release',
+                                   program + ".exe"),
+                      os.path.join(trunk_path, 'out', 'Debug',
+                                   program + ".exe")]
+    for try_path in possible_paths:
+        if os.path.isfile(try_path):
+            return try_path
+    raise Exception('cannot find %s in paths %s; maybe you need to '
+                    'build %s?' % (program, possible_paths, program))
+
+
+def RenderSkps(inputs, render_dir, render_app, args):
+    """Renders the serialized SkPictures.
+
+    Uses the render_pictures program to do the rendering.
+
+    @param inputs the location(s) to read the serlialized SkPictures
+    @param render_dir the location to write out the rendered images
+    """
+    renderer_path = FindPathToProgram(render_app)
+    inputs_as_string = " ".join(inputs)
+    command = '%s %s %s' % (renderer_path, inputs_as_string, render_dir)
+
+    command += args
+
+    RunCommand(command)
+
+
+def DiffRenderings(expected_dir, comparison_dir, diff_dir):
+    """Diffs the rendered SkPicture files with the baseline files.
+
+    Uses the skdiff program to do the diffing.
+
+    @param expected_dir the location of the baseline images.
+    @param comparison_dir the location of the images to comapre with the
+           baseline
+    @param diff_dir the location to write out the diff results
+    """
+    skdiff_path = FindPathToProgram('skdiff')
+    RunCommand('%s %s %s %s %s' %
+               (skdiff_path, expected_dir, comparison_dir, diff_dir,
+                '--noprintdirs'))
+
+
+def Cleanup(render_dir_option, diff_dir_option, render_dir, diff_dir):
+    """Deletes any temporary folders and files created.
+
+    @param foo_option The OptionParser parsed render_dir or diff_dir vars.
+            If these variables are not passed by user we ended up creating
+            temporary directories (render_dir, diff_dir) which we will remove.
+    @param render_dir the directory where the rendered images were written
+    @param diff_dir the directory where the diff results were written
+    """
+    if (not render_dir_option):
+        if (os.path.isdir(render_dir)):
+            shutil.rmtree(render_dir)
+    if (not diff_dir_option):
+        if (os.path.isdir(diff_dir)):
+            shutil.rmtree(diff_dir)
+
+def TestRenderSkps(inputs, expected_dir, render_dir_option, diff_dir_option,
+                   render_app, render_args):
+    if (render_dir_option):
+        render_dir = render_dir_option
+    else:
+        render_dir = tempfile.mkdtemp()
+
+    if (diff_dir_option):
+        diff_dir = diff_dir_option
+    else:
+        diff_dir = tempfile.mkdtemp()
+    try:    
+        RenderSkps(inputs, render_dir, render_app, render_args)
+        DiffRenderings(expected_dir, render_dir, diff_dir)
+    finally:
+        Cleanup(render_dir_option, diff_dir_option, render_dir, diff_dir)
diff --git a/tools/tests/bench_pictures_cfg_test.py b/tools/tests/bench_pictures_cfg_test.py
new file mode 100644
index 0000000..9b0e0ac
--- /dev/null
+++ b/tools/tests/bench_pictures_cfg_test.py
@@ -0,0 +1,46 @@
+# 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.
+
+
+"""
+Verify that the bench_pictures.cfg file is sane.
+"""
+
+
+import os
+import sys
+
+
+def ThrowIfNotAString(obj):
+  """ Raise a TypeError if obj is not a string. """
+  if str(obj) != obj:
+    raise TypeError('%s is not a string!' % str(obj))
+
+
+def Main(argv):
+  """ Verify that the bench_pictures.cfg file is sane.
+
+  - Exec the file to ensure that it uses correct Python syntax.
+  - Make sure that every element is a string, because the buildbot scripts will
+      fail to execute if this is not the case.
+
+  This test does not verify that the well-formed configs are actually valid.
+  """
+  vars = {'import_path': 'tools'}
+  execfile(os.path.join('tools', 'bench_pictures.cfg'), vars)
+  bench_pictures_cfg = vars['bench_pictures_cfg']
+
+  for config_name, config_list in bench_pictures_cfg.iteritems():
+    ThrowIfNotAString(config_name)  
+    for config in config_list:
+      for key, value in config.iteritems():
+        ThrowIfNotAString(key)
+        if type(value).__name__ == 'list':
+          for item in value:
+            ThrowIfNotAString(item)
+        elif not value is True:
+          ThrowIfNotAString(value)
+
+if __name__ == '__main__':
+  sys.exit(Main(sys.argv))
\ No newline at end of file
diff --git a/tools/tests/rebaseline.sh b/tools/tests/rebaseline.sh
new file mode 100755
index 0000000..eee8db8
--- /dev/null
+++ b/tools/tests/rebaseline.sh
@@ -0,0 +1,73 @@
+#!/bin/bash
+
+# Rebaseline the skdiff/*/output-expected/ subdirectories used by the skdiff
+# self-tests.
+# Use with caution: are you sure the new results are actually correct?
+#
+# YOU MUST RE-RUN THIS UNTIL THE SELF-TESTS SUCCEED!
+#
+# TODO: currently, this must be run on Linux to generate baselines that match
+# the ones on the housekeeper bot (which runs on Linux... see
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1417/steps/RunGmSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
+
+function replace_expected_with_actual {
+  # Delete all the expected output files
+  EXPECTED_FILES=$(find skdiff/*/output-expected -type f | grep -v /\.svn/)
+  for EXPECTED_FILE in $EXPECTED_FILES; do
+    rm $EXPECTED_FILE
+  done
+
+  # Copy all the actual output files into the "expected" directories,
+  # creating new subdirs as we go.
+  ACTUAL_FILES=$(find skdiff/*/output-actual -type f | grep -v /\.svn/)
+  for ACTUAL_FILE in $ACTUAL_FILES; do
+    EXPECTED_FILE=${ACTUAL_FILE//actual/expected}
+    mkdir -p $(dirname $EXPECTED_FILE)
+    cp $ACTUAL_FILE $EXPECTED_FILE
+  done
+}
+
+function svn_add_new_files {
+  # Delete all the "actual" directories, so we can svn-add any new "expected"
+  # directories without adding the "actual" ones.
+  rm -rf skdiff/*/output-actual
+  FILES=$(svn stat skdiff/* | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+  FILES=$(svn stat skdiff/*/output-expected | grep ^\? | awk '{print $2}')
+  for FILE in $FILES; do
+    svn add $FILE
+  done
+}
+
+function svn_delete_old_files {
+  FILES=$(svn stat skdiff/*/output-expected | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+  FILES=$(svn stat skdiff/* | grep ^\! | awk '{print $2}')
+  for FILE in $FILES; do
+    svn rm $FILE
+  done
+}
+
+
+# cd into the gm self-test dir
+cd $(dirname $0)
+
+./run.sh
+SELFTEST_RESULT=$?
+echo
+if [ "$SELFTEST_RESULT" != "0" ]; then
+  replace_expected_with_actual
+  echo "Self-tests still failing, you should probably run this again..."
+else
+  svn_add_new_files
+  svn_delete_old_files
+  echo "Self-tests succeeded this time, you should be done!"
+fi
+exit $SELFTEST_RESULT
+
diff --git a/tools/tests/run.sh b/tools/tests/run.sh
new file mode 100755
index 0000000..7731805
--- /dev/null
+++ b/tools/tests/run.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+# Tests for our tools.
+#
+# TODO: for now, it only tests skdiff
+#
+# TODO: currently, this only passes on Linux (which is the platform that
+# the housekeeper bot runs on, e.g.
+# http://70.32.156.51:10117/builders/Skia_PerCommit_House_Keeping/builds/1415/steps/RunToolSelfTests/logs/stdio )
+# See https://code.google.com/p/skia/issues/detail?id=677
+# ('make tools/tests/run.sh work cross-platform')
+# Ideally, these tests should pass on all development platforms...
+# otherwise, how can developers be expected to test them before committing a
+# change?
+
+# cd into .../trunk so all the paths will work
+cd $(dirname $0)/../..
+
+# TODO: make it look in Release and/or Debug
+SKDIFF_BINARY=out/Debug/skdiff
+
+# Compare contents of all files within directories $1 and $2,
+# EXCEPT for any dotfiles.
+# If there are any differences, a description is written to stdout and
+# we exit with a nonzero return value.
+# Otherwise, we write nothing to stdout and return.
+function compare_directories {
+  if [ $# != 2 ]; then
+    echo "compare_directories requires exactly 2 parameters, got $#"
+    exit 1
+  fi
+  diff --exclude=.* $1 $2
+  if [ $? != 0 ]; then
+    echo "failed in: compare_directories $1 $2"
+    exit 1
+  fi
+}
+
+# Run skdiff with arguments in $1 (plus implicit final argument causing skdiff
+# to write its output, if any, to directory $2/output-actual).
+# Then compare its results against those in $2/output-expected.
+function skdiff_test {
+  if [ $# != 2 ]; then
+    echo "skdiff_test requires exactly 2 parameters, got $#"
+    exit 1
+  fi
+  SKDIFF_ARGS="$1"
+  ACTUAL_OUTPUT_DIR="$2/output-actual"
+  EXPECTED_OUTPUT_DIR="$2/output-expected"
+
+  rm -rf $ACTUAL_OUTPUT_DIR
+  mkdir -p $ACTUAL_OUTPUT_DIR
+  COMMAND="$SKDIFF_BINARY $SKDIFF_ARGS $ACTUAL_OUTPUT_DIR"
+  echo "$COMMAND" >$ACTUAL_OUTPUT_DIR/command_line
+  $COMMAND &>$ACTUAL_OUTPUT_DIR/stdout
+  echo $? >$ACTUAL_OUTPUT_DIR/return_value
+
+  compare_directories $EXPECTED_OUTPUT_DIR $ACTUAL_OUTPUT_DIR
+}
+
+SKDIFF_TESTDIR=tools/tests/skdiff
+
+# Run skdiff over a variety of file pair types: identical bits, identical pixels, missing from
+# baseDir, etc.
+skdiff_test "$SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TESTDIR/test1"
+
+# Run skdiff over the same set of files, but with arguments as used by our buildbots:
+# - return the number of mismatching file pairs (but ignore any files missing from either
+#   baseDir or comparisonDir)
+# - list filenames with each result type to stdout
+# - don't generate HTML output files
+skdiff_test "--failonresult DifferentPixels --failonresult DifferentSizes --failonresult Unknown --failonstatus CouldNotDecode,CouldNotRead any --failonstatus any CouldNotDecode,CouldNotRead --listfilenames --nodiffs $SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TESTDIR/test2"
+
+# Run skdiff over just the files that have identical bits.
+skdiff_test "--nodiffs --match identical-bits $SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TESTDIR/identical-bits"
+
+# Run skdiff over just the files that have identical bits or identical pixels.
+skdiff_test "--nodiffs --match identical-bits --match identical-pixels $SKDIFF_TESTDIR/baseDir $SKDIFF_TESTDIR/comparisonDir" "$SKDIFF_TESTDIR/identical-bits-or-pixels"
+
+echo "All tests passed."
diff --git a/tools/tests/skdiff/baseDir/different-bits/different-bits-identical-pixels.png b/tools/tests/skdiff/baseDir/different-bits/different-bits-identical-pixels.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/different-bits/different-bits-identical-pixels.png
Binary files differ
diff --git a/tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz b/tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz
new file mode 100644
index 0000000..5862bde
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz
@@ -0,0 +1 @@
+hhqwekjlkji
diff --git a/tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png b/tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png
Binary files differ
diff --git a/tools/tests/skdiff/baseDir/different-bits/slightly-different-sizes.png b/tools/tests/skdiff/baseDir/different-bits/slightly-different-sizes.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/different-bits/slightly-different-sizes.png
Binary files differ
diff --git a/tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png b/tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png
Binary files differ
diff --git a/tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png b/tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png
Binary files differ
diff --git a/tools/tests/skdiff/baseDir/identical-bits/identical-bits-unknown-format.xyz b/tools/tests/skdiff/baseDir/identical-bits/identical-bits-unknown-format.xyz
new file mode 100644
index 0000000..319d3a9
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/identical-bits/identical-bits-unknown-format.xyz
@@ -0,0 +1 @@
+ioiojgwwerrgkjoiiuhunkbmujois
diff --git a/tools/tests/skdiff/baseDir/identical-bits/identical-bits.png b/tools/tests/skdiff/baseDir/identical-bits/identical-bits.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/identical-bits/identical-bits.png
Binary files differ
diff --git a/tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png b/tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png
Binary files differ
diff --git a/tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz b/tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz
new file mode 100644
index 0000000..06271f1
--- /dev/null
+++ b/tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz
@@ -0,0 +1 @@
+ppouow
diff --git a/tools/tests/skdiff/comparisonDir/different-bits/different-bits-identical-pixels.png b/tools/tests/skdiff/comparisonDir/different-bits/different-bits-identical-pixels.png
new file mode 100644
index 0000000..a35db84
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/different-bits/different-bits-identical-pixels.png
Binary files differ
diff --git a/tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz b/tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz
new file mode 100644
index 0000000..6da08df
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz
@@ -0,0 +1 @@
+awiowejroiwjeoijowimc
diff --git a/tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png b/tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png
new file mode 100644
index 0000000..3d59d90
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png
Binary files differ
diff --git a/tools/tests/skdiff/comparisonDir/different-bits/slightly-different-sizes.png b/tools/tests/skdiff/comparisonDir/different-bits/slightly-different-sizes.png
new file mode 100644
index 0000000..7c9dd07
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/different-bits/slightly-different-sizes.png
Binary files differ
diff --git a/tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png b/tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png
new file mode 100644
index 0000000..5cbdeba
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png
Binary files differ
diff --git a/tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png b/tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png
new file mode 100644
index 0000000..92797b4
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png
Binary files differ
diff --git a/tools/tests/skdiff/comparisonDir/identical-bits/identical-bits-unknown-format.xyz b/tools/tests/skdiff/comparisonDir/identical-bits/identical-bits-unknown-format.xyz
new file mode 100644
index 0000000..319d3a9
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/identical-bits/identical-bits-unknown-format.xyz
@@ -0,0 +1 @@
+ioiojgwwerrgkjoiiuhunkbmujois
diff --git a/tools/tests/skdiff/comparisonDir/identical-bits/identical-bits.png b/tools/tests/skdiff/comparisonDir/identical-bits/identical-bits.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/identical-bits/identical-bits.png
Binary files differ
diff --git a/tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png b/tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png
new file mode 100644
index 0000000..e141b27
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png
Binary files differ
diff --git a/tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz b/tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz
new file mode 100644
index 0000000..001e06a
--- /dev/null
+++ b/tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz
@@ -0,0 +1 @@
+bblksdffff
diff --git a/tools/tests/skdiff/identical-bits-or-pixels/output-expected/command_line b/tools/tests/skdiff/identical-bits-or-pixels/output-expected/command_line
new file mode 100644
index 0000000..8a20667
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits-or-pixels/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/skdiff --nodiffs --match identical-bits --match identical-pixels tools/tests/skdiff/baseDir tools/tests/skdiff/comparisonDir tools/tests/skdiff/identical-bits-or-pixels/output-actual
diff --git a/tools/tests/skdiff/identical-bits-or-pixels/output-expected/return_value b/tools/tests/skdiff/identical-bits-or-pixels/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits-or-pixels/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/tools/tests/skdiff/identical-bits-or-pixels/output-expected/stdout b/tools/tests/skdiff/identical-bits-or-pixels/output-expected/stdout
new file mode 100644
index 0000000..a0800cf
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits-or-pixels/output-expected/stdout
@@ -0,0 +1,14 @@
+baseDir is [tools/tests/skdiff/baseDir/]
+comparisonDir is [tools/tests/skdiff/comparisonDir/]
+not writing any diffs to outputDir [tools/tests/skdiff/identical-bits-or-pixels/output-actual/]
+
+compared 3 file pairs:
+[_] 2 file pairs contain exactly the same bits
+[_] 1 file pairs contain the same pixel values, but not the same bits
+[_] 0 file pairs have identical dimensions but some differing pixels
+[_] 0 file pairs have differing dimensions
+[_] 0 file pairs could not be compared
+[_] 0 file pairs not compared yet
+(results marked with [*] will cause nonzero return value)
+
+number of mismatching file pairs: 0
diff --git a/tools/tests/skdiff/identical-bits/output-expected/command_line b/tools/tests/skdiff/identical-bits/output-expected/command_line
new file mode 100644
index 0000000..49f9ad5
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/skdiff --nodiffs --match identical-bits tools/tests/skdiff/baseDir tools/tests/skdiff/comparisonDir tools/tests/skdiff/identical-bits/output-actual
diff --git a/tools/tests/skdiff/identical-bits/output-expected/return_value b/tools/tests/skdiff/identical-bits/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/tools/tests/skdiff/identical-bits/output-expected/stdout b/tools/tests/skdiff/identical-bits/output-expected/stdout
new file mode 100644
index 0000000..d05c1cc
--- /dev/null
+++ b/tools/tests/skdiff/identical-bits/output-expected/stdout
@@ -0,0 +1,14 @@
+baseDir is [tools/tests/skdiff/baseDir/]
+comparisonDir is [tools/tests/skdiff/comparisonDir/]
+not writing any diffs to outputDir [tools/tests/skdiff/identical-bits/output-actual/]
+
+compared 2 file pairs:
+[_] 2 file pairs contain exactly the same bits
+[_] 0 file pairs contain the same pixel values, but not the same bits
+[_] 0 file pairs have identical dimensions but some differing pixels
+[_] 0 file pairs have differing dimensions
+[_] 0 file pairs could not be compared
+[_] 0 file pairs not compared yet
+(results marked with [*] will cause nonzero return value)
+
+number of mismatching file pairs: 0
diff --git a/tools/tests/skdiff/test1/output-expected/command_line b/tools/tests/skdiff/test1/output-expected/command_line
new file mode 100644
index 0000000..7ed9128
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/skdiff tools/tests/skdiff/baseDir tools/tests/skdiff/comparisonDir tools/tests/skdiff/test1/output-actual
diff --git a/tools/tests/skdiff/test1/output-expected/different-bits_slightly-different-pixels-same-size-diff.png b/tools/tests/skdiff/test1/output-expected/different-bits_slightly-different-pixels-same-size-diff.png
new file mode 100644
index 0000000..479a4e5
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/different-bits_slightly-different-pixels-same-size-diff.png
Binary files differ
diff --git a/tools/tests/skdiff/test1/output-expected/different-bits_slightly-different-pixels-same-size-white.png b/tools/tests/skdiff/test1/output-expected/different-bits_slightly-different-pixels-same-size-white.png
new file mode 100644
index 0000000..738406e
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/different-bits_slightly-different-pixels-same-size-white.png
Binary files differ
diff --git a/tools/tests/skdiff/test1/output-expected/different-bits_very-different-pixels-same-size-diff.png b/tools/tests/skdiff/test1/output-expected/different-bits_very-different-pixels-same-size-diff.png
new file mode 100644
index 0000000..dbd7063
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/different-bits_very-different-pixels-same-size-diff.png
Binary files differ
diff --git a/tools/tests/skdiff/test1/output-expected/different-bits_very-different-pixels-same-size-white.png b/tools/tests/skdiff/test1/output-expected/different-bits_very-different-pixels-same-size-white.png
new file mode 100644
index 0000000..10d8c55
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/different-bits_very-different-pixels-same-size-white.png
Binary files differ
diff --git a/tools/tests/skdiff/test1/output-expected/index.html b/tools/tests/skdiff/test1/output-expected/index.html
new file mode 100644
index 0000000..4bf8eee
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/index.html
@@ -0,0 +1,50 @@
+<html>
+<head>
+<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+<script type="text/javascript">
+function generateCheckedList() {
+var boxes = $(":checkbox:checked");
+var fileCmdLineString = '';
+var fileMultiLineString = '';
+for (var i = 0; i < boxes.length; i++) {
+fileMultiLineString += boxes[i].name + '<br>';
+fileCmdLineString += boxes[i].name + '&nbsp;';
+}
+$("#checkedList").html(fileCmdLineString + '<br><br>' + fileMultiLineString);
+}
+</script>
+</head>
+<body>
+<table>
+<tr><th>select image</th>
+<th>3 of 12 diffs matched exactly.<br></th>
+<th>every different pixel shown in white</th>
+<th>color difference at each pixel</th>
+<th>baseDir: tools/tests/skdiff/baseDir/</th>
+<th>comparisonDir: tools/tests/skdiff/comparisonDir/</th>
+</tr>
+<tr>
+<td><input type="checkbox" name="different-bits/different-bits-unknown-format.xyz" checked="yes"></td><td><b>different-bits/different-bits-unknown-format.xyz</b><br>Could not compare.<br>base: could not be decoded<br>comparison: could not be decoded</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz">N/A</a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz">N/A</a></td></tr>
+<tr>
+<td><input type="checkbox" name="missing-files/missing-from-baseDir.png" checked="yes"></td><td><b>missing-files/missing-from-baseDir.png</b><br>Could not compare.<br>base: not found<br>comparison: decoded</td><td>N/A</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.png" height="240px"></a></td></tr>
+<tr>
+<td><input type="checkbox" name="missing-files/missing-from-baseDir.xyz" checked="yes"></td><td><b>missing-files/missing-from-baseDir.xyz</b><br>Could not compare.<br>base: not found<br>comparison: could not be decoded</td><td>N/A</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz">N/A</a></td></tr>
+<tr>
+<td><input type="checkbox" name="missing-files/missing-from-comparisonDir.png" checked="yes"></td><td><b>missing-files/missing-from-comparisonDir.png</b><br>Could not compare.<br>base: decoded<br>comparison: not found</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png"><img src="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.png" height="240px"></a></td><td>N/A</td></tr>
+<tr>
+<td><input type="checkbox" name="missing-files/missing-from-comparisonDir.xyz" checked="yes"></td><td><b>missing-files/missing-from-comparisonDir.xyz</b><br>Could not compare.<br>base: could not be decoded<br>comparison: not found</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz">N/A</a></td><td>N/A</td></tr>
+<tr>
+<td><input type="checkbox" name="different-bits/slightly-different-sizes.png" checked="yes"></td><td><b>different-bits/slightly-different-sizes.png</b><br>Image sizes differ</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-sizes.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-sizes.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-sizes.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-sizes.png" height="240px"></a></td></tr>
+<tr>
+<td><input type="checkbox" name="different-bits/very-different-sizes.png" checked="yes"></td><td><b>different-bits/very-different-sizes.png</b><br>Image sizes differ</td><td>N/A</td><td>N/A</td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-sizes.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-sizes.png" height="128px"></a></td></tr>
+<tr>
+<td><input type="checkbox" name="different-bits/very-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/very-different-pixels-same-size.png</b><br>97.9926% of pixels differ
+  (42.8911% weighted)<br><br>Max alpha channel mismatch 0<br>Total alpha channel mismatch 0<br><br>Average color mismatch 89<br>Max color mismatch 239</td><td><a href="different-bits_very-different-pixels-same-size-white.png"><img src="different-bits_very-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_very-different-pixels-same-size-diff.png"><img src="different-bits_very-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/very-different-pixels-same-size.png" height="240px"></a></td></tr>
+<tr>
+<td><input type="checkbox" name="different-bits/slightly-different-pixels-same-size.png" checked="yes"></td><td><b>different-bits/slightly-different-pixels-same-size.png</b><br>0.6630% of pixels differ
+  (0.1904% weighted)<br>(2164 pixels)<br><br>Max alpha channel mismatch 0<br>Total alpha channel mismatch 0<br><br>Average color mismatch 0<br>Max color mismatch 213</td><td><a href="different-bits_slightly-different-pixels-same-size-white.png"><img src="different-bits_slightly-different-pixels-same-size-white.png" height="240px"></a></td><td><a href="different-bits_slightly-different-pixels-same-size-diff.png"><img src="different-bits_slightly-different-pixels-same-size-diff.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/baseDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td><td><a href="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png"><img src="../../../../../tools/tests/skdiff/comparisonDir/different-bits/slightly-different-pixels-same-size.png" height="240px"></a></td></tr>
+</table>
+<input type="button" onclick="generateCheckedList()" value="Create Rebaseline List">
+<div id="checkedList"></div>
+</body>
+</html>
diff --git a/tools/tests/skdiff/test1/output-expected/return_value b/tools/tests/skdiff/test1/output-expected/return_value
new file mode 100644
index 0000000..573541a
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/return_value
@@ -0,0 +1 @@
+0
diff --git a/tools/tests/skdiff/test1/output-expected/stdout b/tools/tests/skdiff/test1/output-expected/stdout
new file mode 100644
index 0000000..2b8b2d4
--- /dev/null
+++ b/tools/tests/skdiff/test1/output-expected/stdout
@@ -0,0 +1,27 @@
+ERROR: no codec found for <tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz>
+ERROR: no codec found for <tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz>
+ERROR: no codec found for <tools/tests/skdiff/baseDir/identical-bits/identical-bits-unknown-format.xyz>
+ERROR: no codec found for <tools/tests/skdiff/comparisonDir/identical-bits/identical-bits-unknown-format.xyz>
+ERROR: no codec found for <tools/tests/skdiff/comparisonDir/missing-files/missing-from-baseDir.xyz>
+ERROR: no codec found for <tools/tests/skdiff/baseDir/missing-files/missing-from-comparisonDir.xyz>
+baseDir is [tools/tests/skdiff/baseDir/]
+comparisonDir is [tools/tests/skdiff/comparisonDir/]
+writing diffs to outputDir is [tools/tests/skdiff/test1/output-actual/]
+
+compared 12 file pairs:
+[_] 2 file pairs contain exactly the same bits
+[_] 1 file pairs contain the same pixel values, but not the same bits
+[_] 2 file pairs have identical dimensions but some differing pixels
+[_] 2 file pairs have differing dimensions
+[_] 5 file pairs could not be compared
+   [_] 1 file pairs decoded in baseDir and not found in comparisonDir
+   [_] 1 file pairs could not be decoded in baseDir and could not be decoded in comparisonDir
+   [_] 1 file pairs could not be decoded in baseDir and not found in comparisonDir
+   [_] 1 file pairs not found in baseDir and decoded in comparisonDir
+   [_] 1 file pairs not found in baseDir and could not be decoded in comparisonDir
+[_] 0 file pairs not compared yet
+(results marked with [*] will cause nonzero return value)
+
+number of mismatching file pairs: 9
+Maximum pixel intensity mismatch 239
+Largest area mismatch was 97.99% of pixels
diff --git a/tools/tests/skdiff/test2/output-expected/command_line b/tools/tests/skdiff/test2/output-expected/command_line
new file mode 100644
index 0000000..2d8cc57
--- /dev/null
+++ b/tools/tests/skdiff/test2/output-expected/command_line
@@ -0,0 +1 @@
+out/Debug/skdiff --failonresult DifferentPixels --failonresult DifferentSizes --failonresult Unknown --failonstatus CouldNotDecode,CouldNotRead any --failonstatus any CouldNotDecode,CouldNotRead --listfilenames --nodiffs tools/tests/skdiff/baseDir tools/tests/skdiff/comparisonDir tools/tests/skdiff/test2/output-actual
diff --git a/tools/tests/skdiff/test2/output-expected/return_value b/tools/tests/skdiff/test2/output-expected/return_value
new file mode 100644
index 0000000..7ed6ff8
--- /dev/null
+++ b/tools/tests/skdiff/test2/output-expected/return_value
@@ -0,0 +1 @@
+5
diff --git a/tools/tests/skdiff/test2/output-expected/stdout b/tools/tests/skdiff/test2/output-expected/stdout
new file mode 100644
index 0000000..6aa33b8
--- /dev/null
+++ b/tools/tests/skdiff/test2/output-expected/stdout
@@ -0,0 +1,21 @@
+ERROR: no codec found for <tools/tests/skdiff/baseDir/different-bits/different-bits-unknown-format.xyz>
+ERROR: no codec found for <tools/tests/skdiff/comparisonDir/different-bits/different-bits-unknown-format.xyz>
+baseDir is [tools/tests/skdiff/baseDir/]
+comparisonDir is [tools/tests/skdiff/comparisonDir/]
+not writing any diffs to outputDir [tools/tests/skdiff/test2/output-actual/]
+
+compared 12 file pairs:
+[_] 2 file pairs contain exactly the same bits: identical-bits/identical-bits-unknown-format.xyz identical-bits/identical-bits.png 
+[_] 1 file pairs contain the same pixel values, but not the same bits: different-bits/different-bits-identical-pixels.png 
+[*] 2 file pairs have identical dimensions but some differing pixels: different-bits/slightly-different-pixels-same-size.png different-bits/very-different-pixels-same-size.png 
+[*] 2 file pairs have differing dimensions: different-bits/slightly-different-sizes.png different-bits/very-different-sizes.png 
+[_] 5 file pairs could not be compared: different-bits/different-bits-unknown-format.xyz missing-files/missing-from-baseDir.png missing-files/missing-from-baseDir.xyz missing-files/missing-from-comparisonDir.png missing-files/missing-from-comparisonDir.xyz 
+   [*] 1 file pairs could not be decoded in baseDir and could not be decoded in comparisonDir: different-bits/different-bits-unknown-format.xyz 
+   [_] 2 file pairs found in baseDir and not found in comparisonDir: missing-files/missing-from-comparisonDir.png missing-files/missing-from-comparisonDir.xyz 
+   [_] 2 file pairs not found in baseDir and found in comparisonDir: missing-files/missing-from-baseDir.png missing-files/missing-from-baseDir.xyz 
+[*] 0 file pairs not compared yet: 
+(results marked with [*] will cause nonzero return value)
+
+number of mismatching file pairs: 9
+Maximum pixel intensity mismatch 239
+Largest area mismatch was 97.99% of pixels
diff --git a/tools/update-doxygen.sh b/tools/update-doxygen.sh
new file mode 100755
index 0000000..5351659
--- /dev/null
+++ b/tools/update-doxygen.sh
@@ -0,0 +1,88 @@
+#!/bin/bash
+#
+# Runs doxygen and stores its results in the skia-autogen repo, so that they
+# can be browsed at http://skia-autogen.googlecode.com/svn/docs/html/index.html
+#
+# The DOXYGEN_TEMPDIR env variable is the working directory within which we will
+# check out the code, generate documentation, and store the doxygen log
+# (by default, /tmp/skia-doxygen). The DOXYGEN_COMMIT env variable determines
+# whether docs should be commited (true by default).
+#
+# Sample Usage:
+#  export DOXYGEN_TEMPDIR=/tmp/doxygen
+#  export DOXYGEN_COMMIT=false
+#  bash update-doxygen.sh
+
+# Prepare a temporary dir and check out Skia trunk and docs.
+cd
+DOXYGEN_TEMPDIR=${DOXYGEN_TEMPDIR:-/tmp/skia-doxygen}
+DOXYGEN_COMMIT=${DOXYGEN_COMMIT:-true}
+
+mkdir -p $DOXYGEN_TEMPDIR
+cd $DOXYGEN_TEMPDIR
+
+if [ -d "trunk" ]; then
+  svn update --accept theirs-full trunk
+else
+  svn checkout http://skia.googlecode.com/svn/trunk  # read-only
+fi
+if [ -d "docs" ]; then
+  svn update --accept theirs-full docs
+else
+  svn checkout https://skia-autogen.googlecode.com/svn/docs  # writeable
+if [ ! -f "docs/static_footer.txt" ]; then
+  cp trunk/tools/doxygen_footer.txt docs/static_footer.txt
+fi
+fi
+
+# Run Doxygen.
+cd trunk
+doxygen Doxyfile
+ret_code=$?
+if [ $ret_code != 0 ]; then
+  echo "Error while executing Doxygen command"
+  exit $ret_code
+fi
+
+cd ../docs
+
+# Add any newly created files to Subversion.
+NEWFILES=$(svn status | grep ^\? | awk '{print $2}')
+if [ -n "$NEWFILES" ]; then
+  svn add $NEWFILES
+fi
+
+# We haven't updated the timestamp footer yet... if there are no changes
+# yet, just exit. (We'll wait until there are any actual doc changes before
+# updating the timestamp and committing changes to the repository.)
+MODFILES=$(svn status | grep ^[AM])
+if [ -z "$MODFILES" ]; then
+  echo "No documentation updates, exiting early."
+  exit 0
+fi
+
+# Update the timestamp footer.
+cat >iframe_footer.html <<EOF
+<html><body>
+<address style="text-align: right;"><small>
+Generated on $(date) for skia by
+<a href="http://www.doxygen.org/index.html">doxygen</a>
+$(doxygen --version) </small></address>
+</body></html>
+EOF
+
+# Make sure that all files have the correct mimetype.
+find . -name '*.html' -exec svn propset svn:mime-type text/html '{}' \;
+find . -name '*.css'  -exec svn propset svn:mime-type text/css '{}' \;
+find . -name '*.js'   -exec svn propset svn:mime-type text/javascript '{}' \;
+find . -name '*.gif'  -exec svn propset svn:mime-type image/gif '{}' \;
+find . -name '*.png'  -exec svn propset svn:mime-type image/png '{}' \;
+
+# Output files with documentation updates.
+echo -e "\n\nThe following are the documentation updates:"
+echo $MODFILES
+
+if $DOXYGEN_COMMIT ; then
+  # Commit the updated docs to the subversion repo.
+  svn commit --message 'commit doxygen-generated documentation'
+fi